@gjsify/rolldown-plugin-gjsify 0.51.1 → 0.53.0

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.
Files changed (44) hide show
  1. package/README.md +11 -0
  2. package/lib/app/browser.js +28 -0
  3. package/lib/app/gjs.js +14 -2
  4. package/lib/app/nativescript.js +15 -3
  5. package/lib/app/node.d.ts +1 -1
  6. package/lib/app/node.js +26 -10
  7. package/lib/index.d.ts +4 -2
  8. package/lib/index.js +4 -2
  9. package/lib/plugins/console-assign.d.ts +13 -0
  10. package/lib/plugins/console-assign.js +125 -0
  11. package/lib/plugins/css-as-string.js +126 -44
  12. package/lib/plugins/gi-renderer.d.ts +1 -1
  13. package/lib/plugins/gi-renderer.js +1 -1
  14. package/lib/plugins/gi-runtime-paths.js +5 -2
  15. package/lib/plugins/napi-node-addon.d.ts +48 -4
  16. package/lib/plugins/napi-node-addon.js +261 -85
  17. package/lib/plugins/node-native-external.d.ts +20 -0
  18. package/lib/plugins/node-native-external.js +145 -0
  19. package/lib/plugins/platform-resolve.d.ts +95 -5
  20. package/lib/plugins/platform-resolve.js +132 -11
  21. package/lib/plugins/rewrite-node-modules-paths.d.ts +18 -1
  22. package/lib/plugins/rewrite-node-modules-paths.js +181 -25
  23. package/lib/plugins/unresolved-workspace-import.js +89 -68
  24. package/lib/shims/addon-resolve.d.ts +19 -0
  25. package/lib/shims/addon-resolve.js +193 -0
  26. package/lib/utils/addon-platform.d.ts +23 -0
  27. package/lib/utils/addon-platform.js +50 -0
  28. package/lib/utils/auto-globals.d.ts +6 -0
  29. package/lib/utils/auto-globals.js +92 -15
  30. package/lib/utils/declare-build-input.d.ts +13 -0
  31. package/lib/utils/declare-build-input.js +37 -0
  32. package/lib/utils/detect-free-globals.d.ts +3 -0
  33. package/lib/utils/detect-free-globals.js +1 -1
  34. package/lib/utils/entry-wrapper.js +6 -1
  35. package/lib/utils/index.d.ts +1 -0
  36. package/lib/utils/index.js +1 -0
  37. package/lib/utils/inline-static-reads.d.ts +15 -1
  38. package/lib/utils/inline-static-reads.js +16 -8
  39. package/lib/utils/runtime.js +1 -1
  40. package/lib/utils/scan-globals.d.ts +2 -2
  41. package/lib/utils/scan-globals.js +20 -14
  42. package/lib/utils/zip-path.d.ts +9 -0
  43. package/lib/utils/zip-path.js +12 -0
  44. package/package.json +13 -9
@@ -66,6 +66,7 @@ import { tsPlugin } from 'acorn-typescript';
66
66
  import { dirname, join, resolve, basename, relative, extname, posix, win32 } from 'node:path';
67
67
  import { fileURLToPath, pathToFileURL } from 'node:url';
68
68
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
69
+ import { hasZipSegment } from './zip-path.js';
69
70
  /**
70
71
  * Run the inliner on a source string. Returns the rewritten source (or the
71
72
  * original string when no inlining applied) and the count of edits applied.
@@ -101,7 +102,7 @@ const TS_PARSER = acorn.Parser.extend(tsPlugin());
101
102
  * `inlined: 0`, while the identical expression in a `.js` file returned
102
103
  * `inlined: 1`.
103
104
  */
104
- function parseSource(src, sourceFilePath) {
105
+ export function parseSource(src, sourceFilePath) {
105
106
  const shared = {
106
107
  ecmaVersion: 'latest',
107
108
  sourceType: 'module',
@@ -142,7 +143,7 @@ function forEachCallExpression(root, visit) {
142
143
  }
143
144
  }
144
145
  }
145
- export function inlineStaticReads(src, sourceFilePath) {
146
+ export function inlineStaticReads(src, sourceFilePath, declare) {
146
147
  if (!src.includes('readFileSync') && !src.includes('readdirSync') && !src.includes('existsSync')) {
147
148
  return { contents: src, inlined: 0 };
148
149
  }
@@ -160,6 +161,7 @@ export function inlineStaticReads(src, sourceFilePath) {
160
161
  const ctx = {
161
162
  sourceUrl: pathToFileURL(sourceFilePath).href,
162
163
  resourceRoot: resourceRootFor(sourceFilePath),
164
+ declare,
163
165
  };
164
166
  const edits = [];
165
167
  forEachCallExpression(ast, (node) => {
@@ -246,6 +248,7 @@ function tryInlineCall(node, ctx, _src) {
246
248
  if (path && existsSync(path) && isDirectorySafe(path)) {
247
249
  try {
248
250
  const names = readdirSync(path);
251
+ ctx.declare?.(path);
249
252
  return {
250
253
  start: node.start,
251
254
  end: node.end,
@@ -260,11 +263,11 @@ function tryInlineCall(node, ctx, _src) {
260
263
  if (calleeName === 'existsSync') {
261
264
  const path = evalPathExpr(node.arguments[0], ctx);
262
265
  if (path !== undefined) {
263
- return {
264
- start: node.start,
265
- end: node.end,
266
- replacement: existsSync(path) ? 'true' : 'false',
267
- };
266
+ // The ANSWER is in the bundle, so the file's existence is an input
267
+ // of the output — a file that appears changes the emitted literal.
268
+ const present = existsSync(path);
269
+ ctx.declare?.(path);
270
+ return { start: node.start, end: node.end, replacement: present ? 'true' : 'false' };
268
271
  }
269
272
  }
270
273
  // `createRequire(<URL>)` from `node:module` returns a CJS-style require.
@@ -288,8 +291,11 @@ function tryInlineCall(node, ctx, _src) {
288
291
  // - the path contains a `.zip/` segment (Yarn PnP virtual zip,
289
292
  // where Node's PnP hooks make `existsSync` return true at build
290
293
  // time but the path doesn't exist under GJS at runtime).
291
- const isZip = path !== undefined && path.includes('.zip/');
294
+ const isZip = path !== undefined && hasZipSegment(path);
292
295
  if (path !== undefined && (isZip || !existsSync(path))) {
296
+ // The stub-vs-real choice reads the file's existence too, so the
297
+ // same declaration applies to the branch that replaces the call.
298
+ ctx.declare?.(path);
293
299
  return {
294
300
  start: node.start,
295
301
  end: node.end,
@@ -330,6 +336,7 @@ function tryInlineReadFile(node, ctx, forceTextEncoding) {
330
336
  try {
331
337
  if (encoding) {
332
338
  const text = readFileSync(path, encoding);
339
+ ctx.declare?.(path);
333
340
  return jsStringLiteral(text);
334
341
  }
335
342
  else {
@@ -337,6 +344,7 @@ function tryInlineReadFile(node, ctx, forceTextEncoding) {
337
344
  // Buffer-vs-Uint8Array semantic difference is mostly irrelevant in
338
345
  // bundled GJS code (Buffer is polyfilled on top of Uint8Array).
339
346
  const bytes = readFileSync(path);
347
+ ctx.declare?.(path);
340
348
  return `new Uint8Array([${Array.from(bytes).join(',')}])`;
341
349
  }
342
350
  }
@@ -11,7 +11,7 @@
11
11
  // Kept deliberately pure — no `gi://` / `@girs/*` imports, no side effects —
12
12
  // so it stays loadable on every host and so consumers can import it via the
13
13
  // `@gjsify/rolldown-plugin-gjsify/runtime` subpath without pulling the rest of
14
- // the plugin (which transitively loads blueprint-compiler, deepkit, etc.).
14
+ // the plugin (which transitively loads the Blueprint parser, deepkit, etc.).
15
15
  /**
16
16
  * `true` when running under Bun.
17
17
  *
@@ -34,7 +34,7 @@ export declare function resolveGlobalsList(globalsArg: string): Set<string>;
34
34
  * Returns false — never throws — so a missing package causes a graceful
35
35
  * skip rather than a build crash.
36
36
  */
37
- export declare function isRegisterPathResolvable(registerPath: string, fromDir: string): boolean;
37
+ export declare function isRegisterPathResolvable(registerPath: string, fromDir: string | readonly string[]): boolean;
38
38
  /**
39
39
  * Filter `registerPaths` to only those whose base package is resolvable
40
40
  * from `fromDir`. Emits a `console.warn` for each skipped path —
@@ -45,7 +45,7 @@ export declare function isRegisterPathResolvable(registerPath: string, fromDir:
45
45
  * dep, and that polyfill is not a direct/transitive dep, the warn makes
46
46
  * the gap visible so the developer can add the dep explicitly.
47
47
  */
48
- export declare function filterResolvableRegisterPaths(registerPaths: Set<string>, fromDir: string): Set<string>;
48
+ export declare function filterResolvableRegisterPaths(registerPaths: Set<string>, fromDir: string | readonly string[]): Set<string>;
49
49
  /** Options for {@link writeRegisterInjectFile}. */
50
50
  export interface WriteRegisterInjectOptions {
51
51
  /**
@@ -126,20 +126,26 @@ export function isRegisterPathResolvable(registerPath, fromDir) {
126
126
  const canonical = resolveRegisterAlias(registerPath);
127
127
  const pkgName = packageNameFromRegisterPath(canonical);
128
128
  const parts = pkgName.split('/');
129
- // Walk up the directory tree mirroring Node's module-resolution
130
- // algorithm: check <dir>/node_modules/<pkg>/package.json at each level.
131
- let dir = fromDir;
132
- const root = dir.slice(0, dir.indexOf('/') + 1) || '/';
133
- while (true) {
134
- const candidate = join(dir, 'node_modules', ...parts, 'package.json');
135
- if (existsSync(candidate))
136
- return true;
137
- if (dir === root)
138
- break;
139
- const parent = join(dir, '..');
140
- if (parent === dir)
141
- break;
142
- dir = parent;
129
+ // Several roots when the build may take a `@gjsify/*` from beside the running CLI
130
+ // (`toolchainAnchor`): the resolver would rescue the import, so this gate must not
131
+ // drop it first — it did, and `gjsify exec` rebuilt a consumer's bin with every
132
+ // global skipped because the PROJECT installs no `@gjsify/*` at all.
133
+ for (const start of typeof fromDir === 'string' ? [fromDir] : fromDir) {
134
+ // Walk up the directory tree mirroring Node's module-resolution
135
+ // algorithm: check <dir>/node_modules/<pkg>/package.json at each level.
136
+ let dir = start;
137
+ const root = dir.slice(0, dir.indexOf('/') + 1) || '/';
138
+ while (true) {
139
+ const candidate = join(dir, 'node_modules', ...parts, 'package.json');
140
+ if (existsSync(candidate))
141
+ return true;
142
+ if (dir === root)
143
+ break;
144
+ const parent = join(dir, '..');
145
+ if (parent === dir)
146
+ break;
147
+ dir = parent;
148
+ }
143
149
  }
144
150
  return false;
145
151
  }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Does `path` run THROUGH a `.zip` archive (Yarn PnP's `.yarn/cache/<pkg>.zip/...`)?
3
+ *
4
+ * The separator is the host's as well as `/`: `relative()` and rolldown's module ids answer
5
+ * with `\` on win32, where a `.zip/`-only test never matched and a zip-resident module was
6
+ * sent down the on-disk path. `\` counts only there, because on POSIX it is a filename
7
+ * character. `separator` defaults to the host's; a parameter so both hosts are testable on one.
8
+ */
9
+ export declare function hasZipSegment(path: string, separator?: string): boolean;
@@ -0,0 +1,12 @@
1
+ import { sep } from 'node:path';
2
+ /**
3
+ * Does `path` run THROUGH a `.zip` archive (Yarn PnP's `.yarn/cache/<pkg>.zip/...`)?
4
+ *
5
+ * The separator is the host's as well as `/`: `relative()` and rolldown's module ids answer
6
+ * with `\` on win32, where a `.zip/`-only test never matched and a zip-resident module was
7
+ * sent down the on-disk path. `\` counts only there, because on POSIX it is a filename
8
+ * character. `separator` defaults to the host's; a parameter so both hosts are testable on one.
9
+ */
10
+ export function hasZipSegment(path, separator = sep) {
11
+ return path.includes('.zip/') || (separator === '\\' && path.includes('.zip\\'));
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/rolldown-plugin-gjsify",
3
- "version": "0.51.1",
3
+ "version": "0.53.0",
4
4
  "description": "Rolldown / Rollup / Vite plugin orchestrator for GJS, Node, and Browser targets",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -25,6 +25,9 @@
25
25
  "./shims/module-resolve": {
26
26
  "default": "./lib/shims/module-resolve.js"
27
27
  },
28
+ "./shims/addon-resolve": {
29
+ "default": "./lib/shims/addon-resolve.js"
30
+ },
28
31
  "./shims/unicorn-magic": {
29
32
  "default": "./lib/shims/unicorn-magic.js"
30
33
  }
@@ -60,11 +63,12 @@
60
63
  ],
61
64
  "license": "MIT",
62
65
  "dependencies": {
63
- "@gjsify/console": "^0.51.1",
64
- "@gjsify/resolve-npm": "^0.51.1",
65
- "@gjsify/rolldown-plugin-deepkit": "^0.51.1",
66
- "@gjsify/rolldown-plugin-pnp": "^0.51.1",
67
- "@gjsify/vite-plugin-blueprint": "^0.51.1",
66
+ "@gjsify/console": "^0.53.0",
67
+ "@gjsify/resolve-npm": "^0.53.0",
68
+ "@gjsify/rolldown-plugin-deepkit": "^0.53.0",
69
+ "@gjsify/rolldown-plugin-pnp": "^0.53.0",
70
+ "@gjsify/utils": "^0.53.0",
71
+ "@gjsify/vite-plugin-blueprint": "^0.53.0",
68
72
  "@rollup/pluginutils": "^5.4.0",
69
73
  "acorn": "^8.17.0",
70
74
  "acorn-typescript": "^1.4.13",
@@ -74,7 +78,7 @@
74
78
  "sass": "^1.101.0"
75
79
  },
76
80
  "peerDependencies": {
77
- "@gjsify/lightningcss-native": "^0.51.1",
81
+ "@gjsify/lightningcss-native": "^0.53.0",
78
82
  "rolldown": "^1.1.4"
79
83
  },
80
84
  "peerDependenciesMeta": {
@@ -86,8 +90,8 @@
86
90
  }
87
91
  },
88
92
  "devDependencies": {
89
- "@gjsify/cli": "^0.51.1",
90
- "@gjsify/unit": "^0.51.1",
93
+ "@gjsify/cli": "^0.53.0",
94
+ "@gjsify/unit": "^0.53.0",
91
95
  "@types/node": "^25.9.2",
92
96
  "rolldown": "^1.1.4",
93
97
  "typescript": "^6.0.3"