meteor test: postinstall patch renames yargs esm.mjs __dirname (fixes Mocha boot crash)

.meteorignore does not apply to node_modules under Meteor 3 / rspack (verified: the
rebuilt Mocha bundle still contained node_modules/yargs), so revert that no-op rule and
fix the actual crash instead.

Root cause (pre-existing, unrelated to app code): the whole yargs package gets pulled
into the server bundle as a transitive devDependency, and its ESM shim
node_modules/yargs/lib/platform-shims/esm.mjs declares a top-level
'const __dirname = fileURLToPath(import.meta.url)'. Meteor wraps that .mjs in a CommonJS
module function that already provides __dirname, so it collides —
'SyntaxError: Identifier __dirname has already been declared' — crashing the server
bundle at boot, which is why Mocha reported 0 tests.

Add scripts/patch-yargs-dirname.cjs, run from a package.json postinstall, which renames
yargs' local __dirname (and its .substring/.lastIndexOf uses) to __dirnameYargs so it can
no longer collide with the wrapper's. Idempotent and best-effort (never fails install);
yargs is not used at runtime here so the rename is inert for the app.

Thanks to xet7.
This commit is contained in:
Lauri Ojansivu committed 2026-07-13 14:14:32 +03:00
1 parent 298ffcfd3b
commit 6b409c5dad
3 files changed
+41 -11

No files matched your search

-10
View File
@@ -7,13 +7,3 @@ npm-packages/
# package.json). A naked "tests/" would match a dir named tests at any depth and
# would hide those suites, making `meteor test` report "0 passing".
/tests/
# node_modules/i18next-sprintf-postprocessor ships a stray rollup.config.js (a build
# config) that does require('yargs'). Since a devDependency (mongodb) pulled yargs 18
# into node_modules, Meteor's bundler now follows that require and bundles yargs'
# lib/platform-shims/esm.mjs, whose top-level `const __dirname = fileURLToPath(...)`
# collides with the __dirname that Meteor's CommonJS module wrapper already provides,
# crashing `meteor test` at boot with "Identifier '__dirname' has already been declared".
# That build config is not part of the package's runtime entry (index.js -> dist/commonjs),
# so exclude it from the bundle.
/node_modules/i18next-sprintf-postprocessor/rollup.config.js
+2 -1
View File
@@ -33,7 +33,8 @@
"test:playwright:ui": "cd tests/playwright && npx playwright test --ui",
"test:playwright:all": "cd tests/playwright && WEKAN_PLAYWRIGHT_ALL=1 npx playwright test --project=chromium --project=firefox --project=webkit",
"test:playwright:report": "cd tests/playwright && npx playwright show-report",
"playwright:install": "cd tests/playwright && npx playwright install --with-deps"
"playwright:install": "cd tests/playwright && npx playwright install --with-deps",
"postinstall": "node scripts/patch-yargs-dirname.cjs"
},
"devDependencies": {
"@meteorjs/rspack": "^2.0.1",
+39
View File
@@ -0,0 +1,39 @@
'use strict';
// Workaround for a Meteor 3 / rspack bundler incompatibility with yargs.
//
// yargs' ESM shim node_modules/yargs/lib/platform-shims/esm.mjs starts with
// const __dirname = fileURLToPath(import.meta.url);
// When Meteor bundles this .mjs it wraps it in a CommonJS module function whose
// signature already declares __dirname, so the file's own `const __dirname` collides:
// SyntaxError: Identifier '__dirname' has already been declared
// which crashes the whole server bundle at boot (notably `meteor test` / Mocha).
//
// yargs is only ever reachable from build configs here (nothing in WeKan uses it at
// runtime); it merely got installed as a transitive devDependency. Rather than fight the
// bundler, rename yargs' local __dirname so it can no longer collide. Idempotent and
// best-effort: it never fails an install.
//
// Runs from package.json "postinstall". Safe to run repeatedly.
const fs = require('fs');
const path = require('path');
const FILE = path.join('node_modules', 'yargs', 'lib', 'platform-shims', 'esm.mjs');
try {
if (!fs.existsSync(FILE)) process.exit(0);
let src = fs.readFileSync(FILE, 'utf8');
if (!/const __dirname\b/.test(src)) process.exit(0); // already patched / different version
const patched = src
.replace(/const __dirname\b/g, 'const __dirnameYargs')
.replace(/__dirname\.substring/g, '__dirnameYargs.substring')
.replace(/__dirname\.lastIndexOf/g, '__dirnameYargs.lastIndexOf');
if (patched !== src) {
fs.writeFileSync(FILE, patched);
console.log('patch-yargs-dirname: renamed yargs esm.mjs __dirname to avoid Meteor bundler collision');
}
} catch (e) {
// Never break `npm install` over this best-effort workaround.
console.warn('patch-yargs-dirname: skipped -', (e && e.message) || e);
}