@gjsify/rolldown-plugin-gjsify 0.4.37 → 0.4.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,15 +28,36 @@
28
28
  // bundle's own URL and `__dirname`/`__filename` derive from it.
29
29
  //
30
30
  // 4. CJS (`__dirname`/`__filename` only, no `import.meta.url`)
31
- // → declare them from the file's absolute build path. NOTE: still
32
- // build-location-coupled and shares the "breaks when published" class of
33
- // bug case (1) fixes; injecting an ESM import into a CJS module is unsafe,
34
- // so a CJS-safe runtime-resolve is tracked as a follow-up. ESM deps (the
35
- // common data-reader shape, e.g. typedoc) take path (1).
31
+ // → case 4a (runtime-resolve) when `runtimeResolve` is on (ESM output +
32
+ // bundle-URL banner present) AND the file is a genuinely resolvable
33
+ // installed package (`isRuntimeResolvable`). Declares `__dirname`/
34
+ // `__filename` from the RUNTIME module-resolve shim the same
35
+ // location-independent strategy as case (1), so the bundle works after
36
+ // being published/relocated. This is what lets `@gjsify/tsc` find its
37
+ // `lib.*.d.ts` files relative to the INSTALLED bundle (via
38
+ // `getExecutingFilePath()` → `__filename`) instead of the build
39
+ // machine's `node_modules` path. The shim functions are pulled in with a
40
+ // CJS `require(...)`, NOT an ESM `import` header: Rolldown classifies a
41
+ // node_modules file by its own heuristics, and a genuine CommonJS file
42
+ // (a `.cjs`, or a `.js` with `module.exports` like `typescript/lib/
43
+ // _tsc.js`) is parsed as a CJS script — a prepended ESM `import` there
44
+ // throws `Cannot use import statement outside a module`. `require(...)`
45
+ // is the native CJS idiom Rolldown resolves + links to the bundled shim.
46
+ // → case 4b (legacy baked absolute path) otherwise: when `runtimeResolve`
47
+ // is OFF (the rare `--format iife|cjs` build that can't host the
48
+ // bundle-URL banner) OR the file is NOT a resolvable installed package
49
+ // (a relative-imported module under a `node_modules`-named dir that the
50
+ // package manager — Yarn PnP especially — does not resolve). Runtime
51
+ // resolution of such a file would collapse `__filename` to the bundle's
52
+ // own location, so the build-time absolute path is the correct value for
53
+ // an in-place run. Build-location-coupled — shares the "breaks when
54
+ // published" class of bug, but a non-installed file can't be published
55
+ // as a package anyway.
36
56
  //
37
57
  // Hosting: a Rolldown `transform(code, id)` hook with `order: 'post'` — runs
38
58
  // after deepkit/blueprint/css pre-transforms but still during module loading,
39
59
  // before chunking.
60
+ import { createRequire } from 'node:module';
40
61
  import { dirname, join, relative, resolve } from 'node:path';
41
62
  import { inlineStaticReads } from '../utils/inline-static-reads.js';
42
63
  export const REWRITE_FILTER = /\.(m?js|cjs|[cm]?tsx?)$/;
@@ -89,6 +110,58 @@ export function extractPackageSpec(path) {
89
110
  const idx = path.lastIndexOf(marker);
90
111
  return idx < 0 ? path : path.slice(idx + marker.length);
91
112
  }
113
+ /**
114
+ * The bare package name of a node_modules file spec (scoped names kept whole):
115
+ * "typedoc/dist/lib/app.js" → "typedoc"
116
+ * "@scope/name/sub/file.js" → "@scope/name"
117
+ */
118
+ function packageNameOf(spec) {
119
+ const parts = spec.split('/');
120
+ const segments = spec.startsWith('@') ? 2 : 1;
121
+ return parts.slice(0, segments).join('/');
122
+ }
123
+ /**
124
+ * Whether the runtime module-resolve shim can actually resolve this file's
125
+ * package at runtime — i.e. it is a genuinely *installed*, resolvable package
126
+ * rather than a file that merely lives under a `node_modules`-named directory.
127
+ *
128
+ * The CJS runtime-resolve (case 4a) resolves `__dirname`/`__filename` by
129
+ * resolving the dep's package root from the bundle's location. That only works
130
+ * for a real installed package (`typescript`, `@gjsify/tsc`, a normal npm dep).
131
+ * A file reached via a RELATIVE import into a `node_modules`-named folder that
132
+ * is NOT a resolvable package (e.g. a test fixture at
133
+ * `fixtures/node_modules/fake-cjs/index.cjs`, or any dir not in the
134
+ * package manager's resolution graph — Yarn PnP especially rejects these)
135
+ * would resolve to the bundle's own location at runtime → wrong `__filename`.
136
+ * For those we fall back to the legacy baked-absolute-path rewrite (case 4b),
137
+ * which is build-location-coupled but correct for an in-place run.
138
+ *
139
+ * Probed at BUILD time with `createRequire(<file>).resolve("<pkg>/package.json")`
140
+ * anchored at the file itself — succeeds iff the package is installed and
141
+ * reachable (honours node_modules walking AND Yarn PnP, since the build runs
142
+ * under the consumer's loader). Best-effort: any throw → treat as not
143
+ * resolvable (fall back to the absolute rewrite, never crash the build).
144
+ */
145
+ function isRuntimeResolvable(path) {
146
+ const pkg = packageNameOf(extractPackageSpec(path));
147
+ if (!pkg)
148
+ return false;
149
+ try {
150
+ createRequire(path).resolve(`${pkg}/package.json`);
151
+ return true;
152
+ }
153
+ catch {
154
+ try {
155
+ // Some strict `exports` maps block `<pkg>/package.json` — fall back
156
+ // to resolving the package's main entry, which is always exported.
157
+ createRequire(path).resolve(pkg);
158
+ return true;
159
+ }
160
+ catch {
161
+ return false;
162
+ }
163
+ }
164
+ }
92
165
  /** Whether the file needs a `var __dirname`/`__filename` declaration injected. */
93
166
  function needsDirnameDecl(src, flags) {
94
167
  return flags.hasDirname && !DIRNAME_DECL_RE.test(src);
@@ -157,8 +230,46 @@ function rewriteZipResident(src, path, flags) {
157
230
  return { code: withPreamble(src, preamble), moduleType: moduleTypeForPath(path) };
158
231
  }
159
232
  /**
160
- * Case 4 — CJS (no `import.meta.url`). Declare `__dirname`/`__filename` from the
161
- * absolute build path. Build-location-coupled see file header note.
233
+ * Case 4a — CJS, runtime-resolve. Declare `__dirname`/`__filename` from the
234
+ * module-resolve shim so they point at the file's location relative to the
235
+ * INSTALLED bundle, not the build machine. Location-independent — the fix for
236
+ * the "works in the workspace, crashes once published" class of bug for CJS
237
+ * deps (e.g. `@gjsify/tsc`'s `typescript/lib/_tsc.js` resolving its sibling
238
+ * `lib.*.d.ts` files). Mirrors `rewriteOnDiskEsm` minus the `import.meta.url`
239
+ * rewrite (a CJS file has none).
240
+ *
241
+ * Unlike the ESM case we pull the shim functions in via a CJS `require(...)`,
242
+ * NOT an ESM `import { … } from …` header. Rolldown classifies a `node_modules`
243
+ * file by its own heuristics (extension + `module.exports`/`require` content),
244
+ * and a genuine CommonJS file — a `.cjs`, or a `.js` with `module.exports` like
245
+ * `typescript/lib/_tsc.js` — is parsed as a CJS script. Prepending an ESM
246
+ * `import` statement there makes Rolldown's parser throw
247
+ * `[PARSE_ERROR] Cannot use import statement outside a module`. `require(...)`
248
+ * is the native CJS idiom Rolldown resolves + links to the bundled shim, so the
249
+ * preamble parses in either classification.
250
+ */
251
+ function rewriteCjsRuntime(src, path, flags) {
252
+ const spec = JSON.stringify(extractPackageSpec(path));
253
+ const used = [];
254
+ const preamble = [];
255
+ if (needsDirnameDecl(src, flags)) {
256
+ preamble.push(`var __dirname = __gjsifyModuleDir(${spec});`);
257
+ used.push('__gjsifyModuleDir');
258
+ }
259
+ if (needsFilenameDecl(src, flags)) {
260
+ preamble.push(`var __filename = __gjsifyModuleFile(${spec});`);
261
+ used.push('__gjsifyModuleFile');
262
+ }
263
+ // Nothing to declare (both tokens already locally bound) — leave untouched.
264
+ if (used.length === 0)
265
+ return { code: src, moduleType: moduleTypeForPath(path) };
266
+ const header = `var { ${used.join(', ')} } = require(${JSON.stringify(MODULE_RESOLVE_SHIM)});`;
267
+ return { code: withPreamble(src, preamble, header), moduleType: moduleTypeForPath(path) };
268
+ }
269
+ /**
270
+ * Case 4b — CJS, legacy. Declare `__dirname`/`__filename` from the absolute
271
+ * build path. Build-location-coupled — see file header note. Used only for the
272
+ * non-ESM output formats that can't host the bundle-URL anchor banner.
162
273
  */
163
274
  function rewriteCjsAbsolute(src, path, flags) {
164
275
  const preamble = [];
@@ -202,7 +313,15 @@ export function rewriteContents(args, srcInput, bundleDir, runtimeResolve) {
202
313
  ? rewriteOnDiskEsm(src, args.path, flags)
203
314
  : rewriteOnDiskEsmLegacy(src, args.path, bundleDir, flags);
204
315
  }
205
- return rewriteCjsAbsolute(src, args.path, flags);
316
+ // CJS (no `import.meta.url`). Runtime-resolve only when the output can host
317
+ // the bundle-URL banner (ESM output) AND the file is a genuinely resolvable
318
+ // installed package — otherwise the shim would resolve `__filename` to the
319
+ // bundle's own location at runtime. A file under a `node_modules`-named dir
320
+ // that is not actually installed (a relative-imported fixture, a non-PnP
321
+ // path) keeps the legacy baked-absolute-path rewrite.
322
+ return runtimeResolve && isRuntimeResolvable(args.path)
323
+ ? rewriteCjsRuntime(src, args.path, flags)
324
+ : rewriteCjsAbsolute(src, args.path, flags);
206
325
  }
207
326
  /**
208
327
  * Build a Rolldown plugin that runs the path rewriter as a `transform(code, id)`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/rolldown-plugin-gjsify",
3
- "version": "0.4.37",
3
+ "version": "0.4.38",
4
4
  "description": "Rolldown / Rollup / Vite plugin orchestrator for GJS, Node, and Browser targets",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -48,11 +48,11 @@
48
48
  ],
49
49
  "license": "MIT",
50
50
  "dependencies": {
51
- "@gjsify/console": "^0.4.37",
52
- "@gjsify/resolve-npm": "^0.4.37",
53
- "@gjsify/rolldown-plugin-deepkit": "^0.4.37",
54
- "@gjsify/rolldown-plugin-pnp": "^0.4.37",
55
- "@gjsify/vite-plugin-blueprint": "^0.4.37",
51
+ "@gjsify/console": "^0.4.38",
52
+ "@gjsify/resolve-npm": "^0.4.38",
53
+ "@gjsify/rolldown-plugin-deepkit": "^0.4.38",
54
+ "@gjsify/rolldown-plugin-pnp": "^0.4.38",
55
+ "@gjsify/vite-plugin-blueprint": "^0.4.38",
56
56
  "@rollup/pluginutils": "^5.4.0",
57
57
  "acorn": "^8.16.0",
58
58
  "acorn-walk": "^8.3.5",
@@ -60,7 +60,7 @@
60
60
  "lightningcss": "^1.32.0"
61
61
  },
62
62
  "peerDependencies": {
63
- "@gjsify/lightningcss-native": "^0.4.37",
63
+ "@gjsify/lightningcss-native": "^0.4.38",
64
64
  "rolldown": "^1.0.3"
65
65
  },
66
66
  "peerDependenciesMeta": {
@@ -74,6 +74,6 @@
74
74
  "devDependencies": {
75
75
  "@types/node": "^25.9.1",
76
76
  "rolldown": "^1.0.3",
77
- "typescript": "^5.9.3"
77
+ "typescript": "^6.0.3"
78
78
  }
79
79
  }