Files
wekan/models/lib/cardMoveModifier.js
Lauri Ojansivu 8d1fae4f7d Convert client-reachable models/lib helpers from CommonJS to ES modules (1/2).
The rspack client build treats a `module.exports` file imported into the
client bundle as an ES module and throws
"ES Modules may not assign module.exports" at evaluation time, aborting
the whole client module chain (No such template: swimlane / notifications
/ boardButtons, blank board, missing top bar). This is not an rspack
version regression (the lockfile has pinned ~1.7.x since v8.40); it is
that WeKan added CommonJS helpers to the client bundle.

Convert the 42 client-reachable pure-logic models/lib helpers from
`module.exports = { ... }` to `export { ... }`, and calendarFilter's
internal require('./swimlaneFilter') to an import with a .js extension
(node ESM needs it; rspack/reify tolerate it). Server-only helpers that
use node built-ins (fs/path/child_process) stay CommonJS. The plain-Node
.cjs unit tests for these helpers now use `await import(...js)` (wrapped
in an async IIFE); a source-guard in importVirtualMembers was updated
from module.exports to export. Verified: the full tests/*.test.cjs suite
has the SAME 7 pre-existing failures as before this change and no new
ones.
2026-07-23 18:55:30 +03:00

35 lines
1.6 KiB
JavaScript

'use strict';
// Pure helper that computes the $set modifier for Card.move(): only the fields
// that ACTUALLY change relative to the card's current position. Extracted from
// models/cards.js so it is unit-testable without Meteor.
//
// #6430 (card flicker ~1s on drag on large boards): Card.move() used to write
// boardId/swimlaneId/listId unconditionally, even when the card stayed on the
// same board/swimlane (the common "drag to another list" case). That meant every
// drag:
// - re-ran the Cards.after.update hook that boardId-re-syncs the card's
// checklists and checklistItems via multi updates (server load + reactive
// re-publish of those collections),
// - ran the cross-board consistency before-hook and the denyCrossBoardMove
// deny-rule DB lookup, and
// - invalidated more reactive dependents than necessary.
// Writing only the changed fields skips all of that for same-board moves, and an
// empty result means a true no-op (card dropped back in place) that the caller
// can skip entirely.
function computeCardMoveModifier(card, target = {}) {
const { boardId, swimlaneId, listId, sort } = target;
const fields = {};
if (boardId !== undefined && boardId !== card.boardId) fields.boardId = boardId;
if (swimlaneId !== undefined && swimlaneId !== card.swimlaneId) {
fields.swimlaneId = swimlaneId;
}
if (listId !== undefined && listId !== card.listId) fields.listId = listId;
// sort === null means "keep current" (Card.move's default), so it is never a change.
if (sort !== undefined && sort !== null && sort !== card.sort) fields.sort = sort;
return fields;
}
export { computeCardMoveModifier };