@gjsify/rolldown-plugin-gjsify 0.26.0 → 0.27.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.
package/lib/globals.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { resolveGlobalsList, writeRegisterInjectFile, isRegisterPathResolvable, filterResolvableRegisterPaths } from './utils/scan-globals.js';
1
+ export { resolveGlobalsList, writeRegisterInjectFile, isRegisterPathResolvable, filterResolvableRegisterPaths, } from './utils/scan-globals.js';
2
2
  export { detectFreeGlobals, detectGjsAmbientGlobals, detectNodeGiModuleImports, GJS_AMBIENT_GLOBALS, } from './utils/detect-free-globals.js';
3
3
  export { detectAutoGlobals, detectNodeGiGlobals, describeGiBackedInjection } from './utils/auto-globals.js';
4
4
  export type { AutoGlobalsResult, DetectAutoGlobalsOptions, AnalysisOptions } from './utils/auto-globals.js';
package/lib/globals.js CHANGED
@@ -4,6 +4,6 @@
4
4
  // (or auto-detect via the iterative multi-pass build) and write the inject
5
5
  // stub that the plugin picks up via its `autoGlobalsInject` option. See the
6
6
  // "Tree-shakeable Globals" section in AGENTS.md for the architecture.
7
- export { resolveGlobalsList, writeRegisterInjectFile, isRegisterPathResolvable, filterResolvableRegisterPaths } from './utils/scan-globals.js';
7
+ export { resolveGlobalsList, writeRegisterInjectFile, isRegisterPathResolvable, filterResolvableRegisterPaths, } from './utils/scan-globals.js';
8
8
  export { detectFreeGlobals, detectGjsAmbientGlobals, detectNodeGiModuleImports, GJS_AMBIENT_GLOBALS, } from './utils/detect-free-globals.js';
9
9
  export { detectAutoGlobals, detectNodeGiGlobals, describeGiBackedInjection } from './utils/auto-globals.js';
package/lib/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export * from './types/index.js';
2
2
  export * from './utils/index.js';
3
3
  export * from './app/index.js';
4
4
  export * from './library/index.js';
5
- export { REWRITE_FILTER, getBundleDirFromOutput, rewriteContents, shouldRewrite, nodeModulesPathRewritePlugin, } from './plugins/rewrite-node-modules-paths.js';
5
+ export { REWRITE_FILTER, extractPackageSpec, getBundleDirFromOutput, rewriteContents, shouldRewrite, nodeModulesPathRewritePlugin, } from './plugins/rewrite-node-modules-paths.js';
6
6
  export type { NodeModulesPathRewriteOptions, RewriteResult } from './plugins/rewrite-node-modules-paths.js';
7
7
  export { processStubPlugin, GJS_PROCESS_STUB, composeBanner } from './plugins/process-stub.js';
8
8
  export type { ProcessStubPluginOptions } from './plugins/process-stub.js';
@@ -18,7 +18,7 @@ export { externalsPlugin } from './plugins/externals.js';
18
18
  export type { ExternalsPredicate, ExternalsPluginOptions } from './plugins/externals.js';
19
19
  export { unresolvedWorkspaceImportPlugin, classifyImport, isWorkspaceSpecifier, formatUnresolvedWorkspaceImport, buildReverseAliasIndex, UnresolvedWorkspaceImportError, } from './plugins/unresolved-workspace-import.js';
20
20
  export type { WorkspaceImportGuardOptions, WorkspaceImportGuardTarget, ImportVerdict, ClassifyImportInput, UnresolvedWorkspaceImportDetails, } from './plugins/unresolved-workspace-import.js';
21
- export { napiNodeAddonPlugin, resolveAddonPath, nearestPackageRoot, classifySpecifier, directNodeShim, nodeGypBuildShim, bindingsShim, napiRsShim, ADDON_FILTER_RE, isNapiRsPackageJson, isNapiRsSibling, detectNapiRsEntry, AddonNotBuiltError, } from './plugins/napi-node-addon.js';
21
+ export { napiNodeAddonPlugin, resolveAddonPath, nearestPackageRoot, classifySpecifier, directNodeShim, nodeGypBuildShim, bindingsShim, napiRsShim, ADDON_FILTER_RE, isNapiRsPackageJson, isNapiRsSibling, detectNapiRsEntry, hostNapiRsTriple, AddonNotBuiltError, } from './plugins/napi-node-addon.js';
22
22
  export type { NapiNodeAddonPluginOptions, AddonPackageJson } from './plugins/napi-node-addon.js';
23
23
  export { platformResolvePlugin, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
24
24
  export type { PlatformResolvePluginOptions, NativescriptPlatform } from './plugins/platform-resolve.js';
package/lib/index.js CHANGED
@@ -3,7 +3,7 @@ export * from './types/index.js';
3
3
  export * from './utils/index.js';
4
4
  export * from './app/index.js';
5
5
  export * from './library/index.js';
6
- export { REWRITE_FILTER, getBundleDirFromOutput, rewriteContents, shouldRewrite, nodeModulesPathRewritePlugin, } from './plugins/rewrite-node-modules-paths.js';
6
+ export { REWRITE_FILTER, extractPackageSpec, getBundleDirFromOutput, rewriteContents, shouldRewrite, nodeModulesPathRewritePlugin, } from './plugins/rewrite-node-modules-paths.js';
7
7
  export { processStubPlugin, GJS_PROCESS_STUB, composeBanner } from './plugins/process-stub.js';
8
8
  export { cssAsStringPlugin } from './plugins/css-as-string.js';
9
9
  export { textLoaderPlugin } from './plugins/text-loader.js';
@@ -12,7 +12,7 @@ export { aliasPlugin } from './plugins/alias.js';
12
12
  export { gjsImportsEmptyPlugin } from './plugins/gjs-imports-empty.js';
13
13
  export { externalsPlugin } from './plugins/externals.js';
14
14
  export { unresolvedWorkspaceImportPlugin, classifyImport, isWorkspaceSpecifier, formatUnresolvedWorkspaceImport, buildReverseAliasIndex, UnresolvedWorkspaceImportError, } from './plugins/unresolved-workspace-import.js';
15
- export { napiNodeAddonPlugin, resolveAddonPath, nearestPackageRoot, classifySpecifier, directNodeShim, nodeGypBuildShim, bindingsShim, napiRsShim, ADDON_FILTER_RE, isNapiRsPackageJson, isNapiRsSibling, detectNapiRsEntry, AddonNotBuiltError, } from './plugins/napi-node-addon.js';
15
+ export { napiNodeAddonPlugin, resolveAddonPath, nearestPackageRoot, classifySpecifier, directNodeShim, nodeGypBuildShim, bindingsShim, napiRsShim, ADDON_FILTER_RE, isNapiRsPackageJson, isNapiRsSibling, detectNapiRsEntry, hostNapiRsTriple, AddonNotBuiltError, } from './plugins/napi-node-addon.js';
16
16
  export { platformResolvePlugin, detectNativescriptPlatform, nativescriptPlatformDefines, } from './plugins/platform-resolve.js';
17
17
  export * from './plugin.js';
18
18
  import { gjsifyPlugin } from './plugin.js';
@@ -108,6 +108,16 @@ export declare function detectNapiRsEntry(entryFile: string): {
108
108
  * package's binaries. Exported for the unit tests.
109
109
  */
110
110
  export declare function isNapiRsSibling(pkg: AddonPackageJson, dep: string): boolean;
111
+ /**
112
+ * The napi-rs short platform triple for the CURRENT host (`linux-x64-gnu`,
113
+ * `darwin-arm64`, `win32-x64-msvc`, …) — the tail napi-rs stamps into a sibling
114
+ * package name (`@node-rs/argon2-linux-x64-gnu`) and a local binary
115
+ * (`argon2.linux-x64-gnu.node`). Selects BOTH the sibling package and the
116
+ * local-file fallback — see `resolveNapiRsEntryAddon` for why the sibling path
117
+ * cannot just take whatever resolves. Returns null for a host napi-rs doesn't
118
+ * name, and the caller then widens rather than guessing.
119
+ */
120
+ export declare function hostNapiRsTriple(): string | null;
111
121
  /**
112
122
  * Transparent `.node`-addon loader for `gjsify build --app gjs`.
113
123
  *
@@ -587,7 +587,7 @@ export function isNapiRsSibling(pkg, dep) {
587
587
  * cannot just take whatever resolves. Returns null for a host napi-rs doesn't
588
588
  * name, and the caller then widens rather than guessing.
589
589
  */
590
- function hostNapiRsTriple() {
590
+ export function hostNapiRsTriple() {
591
591
  const platform = process.platform;
592
592
  const arch = process.arch;
593
593
  const archTok = {
@@ -19,8 +19,30 @@ export declare function getBundleDirFromOutput(opts: {
19
19
  * ".../node_modules/typedoc/dist/lib/app.js" → "typedoc/dist/lib/app.js"
20
20
  * ".../node_modules/@scope/name/sub.js" → "@scope/name/sub.js"
21
21
  * ".../node_modules/a/node_modules/b/file.js" → "b/file.js"
22
+ *
23
+ * The result is a module SPECIFIER, not a path, so it is always `/`-separated.
24
+ *
25
+ * On win32 the incoming path is `C:\…\node_modules\typescript\lib\_tsc.js` and
26
+ * the `node_modules/` marker never matched, so this returned the ABSOLUTE
27
+ * BUILD-MACHINE PATH unchanged — and `shouldRewrite` above tests
28
+ * `includes('node_modules')` without a separator, so the rewriter still fired
29
+ * and baked that path into the bundle as the runtime resolve spec. Measured on
30
+ * win32 x64 building `@gjsify/tsc`:
31
+ *
32
+ * linux: To(`typescript/lib/_tsc.js`) → retargets
33
+ * win32: To(`C:\\src\\werkstatt\\gjsify\\gjsify\\node_mod…`) → unshippable
34
+ *
35
+ * `@gjsify/tsc`'s `scripts/build-bundle.mjs` caught it ("expected runtime
36
+ * path-rewrite spec … not found in bundle — has the node-modules path rewriter
37
+ * changed?") and refused to ship, which is why this surfaced as a build failure
38
+ * rather than as a bundle that resolves nothing on someone else's machine.
39
+ *
40
+ * The conversion is win32-ONLY: on POSIX a backslash is a legitimate FILENAME
41
+ * character, so rewriting it there would corrupt a real path. `platform` is
42
+ * injected so both branches are unit-testable from either host — the same shape
43
+ * `utils/entry-points.ts` and `cli/src/utils/win32-command.ts` use.
22
44
  */
23
- export declare function extractPackageSpec(path: string): string;
45
+ export declare function extractPackageSpec(path: string, platform?: string): string;
24
46
  export interface RewriteResult {
25
47
  code: string;
26
48
  moduleType?: 'ts' | 'js';
@@ -104,11 +104,34 @@ function moduleTypeForPath(path) {
104
104
  * ".../node_modules/typedoc/dist/lib/app.js" → "typedoc/dist/lib/app.js"
105
105
  * ".../node_modules/@scope/name/sub.js" → "@scope/name/sub.js"
106
106
  * ".../node_modules/a/node_modules/b/file.js" → "b/file.js"
107
+ *
108
+ * The result is a module SPECIFIER, not a path, so it is always `/`-separated.
109
+ *
110
+ * On win32 the incoming path is `C:\…\node_modules\typescript\lib\_tsc.js` and
111
+ * the `node_modules/` marker never matched, so this returned the ABSOLUTE
112
+ * BUILD-MACHINE PATH unchanged — and `shouldRewrite` above tests
113
+ * `includes('node_modules')` without a separator, so the rewriter still fired
114
+ * and baked that path into the bundle as the runtime resolve spec. Measured on
115
+ * win32 x64 building `@gjsify/tsc`:
116
+ *
117
+ * linux: To(`typescript/lib/_tsc.js`) → retargets
118
+ * win32: To(`C:\\src\\werkstatt\\gjsify\\gjsify\\node_mod…`) → unshippable
119
+ *
120
+ * `@gjsify/tsc`'s `scripts/build-bundle.mjs` caught it ("expected runtime
121
+ * path-rewrite spec … not found in bundle — has the node-modules path rewriter
122
+ * changed?") and refused to ship, which is why this surfaced as a build failure
123
+ * rather than as a bundle that resolves nothing on someone else's machine.
124
+ *
125
+ * The conversion is win32-ONLY: on POSIX a backslash is a legitimate FILENAME
126
+ * character, so rewriting it there would corrupt a real path. `platform` is
127
+ * injected so both branches are unit-testable from either host — the same shape
128
+ * `utils/entry-points.ts` and `cli/src/utils/win32-command.ts` use.
107
129
  */
108
- export function extractPackageSpec(path) {
130
+ export function extractPackageSpec(path, platform = process.platform) {
131
+ const normalized = platform === 'win32' ? path.replaceAll('\\', '/') : path;
109
132
  const marker = 'node_modules/';
110
- const idx = path.lastIndexOf(marker);
111
- return idx < 0 ? path : path.slice(idx + marker.length);
133
+ const idx = normalized.lastIndexOf(marker);
134
+ return idx < 0 ? normalized : normalized.slice(idx + marker.length);
112
135
  }
113
136
  /**
114
137
  * The bare package name of a node_modules file spec (scoped names kept whole):
@@ -231,9 +231,7 @@ export async function detectAutoGlobals(analysisOptions, pluginOptions, gjsifyPl
231
231
  let detected = new Set();
232
232
  let currentInject = undefined;
233
233
  if (extraRegisterPaths.size > 0) {
234
- const resolvableExtra = cwd
235
- ? filterResolvableRegisterPaths(extraRegisterPaths, cwd)
236
- : extraRegisterPaths;
234
+ const resolvableExtra = cwd ? filterResolvableRegisterPaths(extraRegisterPaths, cwd) : extraRegisterPaths;
237
235
  currentInject = (await writeRegisterInjectFile(resolvableExtra, cwd)) ?? undefined;
238
236
  }
239
237
  // Caller-provided plugins (e.g. PnP relay) survive every pass; the
@@ -317,7 +315,13 @@ export async function detectAutoGlobals(analysisOptions, pluginOptions, gjsifyPl
317
315
  catch (e) {
318
316
  if (globalThis.process?.env
319
317
  ?.GJSIFY_DEBUG_AUTO_GLOBALS) {
320
- const path = `/tmp/gjsify-auto-globals-failed-chunk-${i}.mjs`;
318
+ // `tmpdir()`, not a literal `/tmp` — on Windows that wrote
319
+ // to `<drive>:\tmp\`, and the `writeFileSync` is inside a
320
+ // `catch`, so the dump silently did not appear where the
321
+ // log line below says it did.
322
+ const os = await import('node:os');
323
+ const nodePath = await import('node:path');
324
+ const path = nodePath.join(os.tmpdir(), `gjsify-auto-globals-failed-chunk-${i}.mjs`);
321
325
  try {
322
326
  // Dynamic `import()` on purpose: this is a debug-only
323
327
  // branch and the module graph should not carry
@@ -368,9 +372,7 @@ export async function detectAutoGlobals(analysisOptions, pluginOptions, gjsifyPl
368
372
  // Union with the previously-injected set keeps growth monotonic
369
373
  // (an over-approximated superset must never shrink, or the subset
370
374
  // convergence check could oscillate).
371
- const expanded = options.disableClosureExpansion
372
- ? newDetected
373
- : expandWithClosure(newDetected, excludeSet);
375
+ const expanded = options.disableClosureExpansion ? newDetected : expandWithClosure(newDetected, excludeSet);
374
376
  if (verbose && expanded.size > newDetected.size) {
375
377
  console.debug(`[gjsify] --globals auto: closure map expanded ${newDetected.size} → ${expanded.size} global(s)`);
376
378
  }
@@ -1,2 +1,2 @@
1
1
  export type EntryPoints = string | string[] | Record<string, string>;
2
- export declare const globToEntryPoints: (_entryPoints: EntryPoints | undefined, ignore?: string[]) => Promise<EntryPoints | undefined>;
2
+ export declare const globToEntryPoints: (_entryPoints: EntryPoints | undefined, ignore?: string[], platform?: string) => Promise<EntryPoints | undefined>;
@@ -36,18 +36,74 @@
36
36
  // `packages/infra/cli/src/entry-points.spec.ts` and
37
37
  // `tests/e2e/deterministic-library-build/`.
38
38
  import fastGlob from 'fast-glob';
39
+ /** Never expandable to an entry point — the type-only exclusion from the header. */
39
40
  const DEFAULT_IGNORE = ['**/*.d.ts'];
41
+ /**
42
+ * BLAST-RADIUS cap, added to a pattern that does not itself name `node_modules`.
43
+ *
44
+ * `ignore` prunes fast-glob's DIRECTORY traversal, not merely its results, so it
45
+ * bounds the walk: on the mis-formed pattern in {@link toGlobSeparators} it turns
46
+ * heap death into 32 ms. That matters beyond the one bug — the walk it stops runs
47
+ * into the workspace's RELATIVE `node_modules/@gjsify/*` symlinks, which cycle
48
+ * back into `packages/`.
49
+ *
50
+ * Conditional, and NOT a blanket default, because a package may legitimately
51
+ * bundle an entry that lives in there: `@gjsify/tsc`'s whole build is
52
+ * `gjsify build node_modules/typescript/lib/_tsc.js --app gjs`, and an
53
+ * unconditional ignore emptied its input and broke `build:infra` (caught by the
54
+ * closure build, not by a unit test — which is why the rule is stated as
55
+ * narrowly as it can be). The rule in one sentence: a WILDCARD never descends
56
+ * into `node_modules`, while a path that names it explicitly is honoured.
57
+ */
58
+ const NODE_MODULES_IGNORE = '**/node_modules/**';
59
+ function ignoreFor(pattern, ignore) {
60
+ return pattern.includes('node_modules') ? [...ignore] : [...ignore, NODE_MODULES_IGNORE];
61
+ }
62
+ /**
63
+ * Make a pattern safe to hand to fast-glob on Windows.
64
+ *
65
+ * fast-glob patterns are POSIX-only: `\` is its ESCAPE character, never a path
66
+ * separator. Its own source says so where it declines to use `path.normalize`
67
+ * (`fast-glob/out/utils/path.js`):
68
+ *
69
+ * > Because of this, we cannot use the standard `path.normalize` method,
70
+ * > because on Windows platform it will use of backslashes.
71
+ *
72
+ * A backslashed pattern does NOT fail cleanly, which is what made this
73
+ * expensive. `src\**\*.{ts,js}` contains no `/`, so fast-glob's
74
+ * `generateTasks` finds no static prefix and walks from `.` instead of from
75
+ * `src` — and its walker follows symlinks with unbounded depth and no cycle
76
+ * detection. In a workspace whose `node_modules/@gjsify/*` are RELATIVE
77
+ * symlinks back into `packages/`, that walk never terminates: RSS climbs until
78
+ * the heap dies, nothing ever matches, and no output file is written. Measured
79
+ * on win32 (issue #914): `@gjsify/utils` at 845 MB / 413 s CPU after 5 min with
80
+ * zero outputs, `@gjsify/rolldown-native` `FATAL ERROR: Ineffective
81
+ * mark-compacts near heap limit` after 33 min — reproduced verbatim on Linux by
82
+ * feeding the backslashed pattern in directly.
83
+ *
84
+ * The conversion is win32-ONLY because on POSIX a backslash is a legitimate
85
+ * escape (`src/\*.ts` = a file literally named `*.ts`); rewriting it there
86
+ * would silently change what the pattern means. `platform` is injected so both
87
+ * branches are unit-testable from either host — the shape
88
+ * `utils/win32-command.ts` uses.
89
+ */
90
+ function toGlobSeparators(pattern, platform) {
91
+ return platform === 'win32' ? pattern.replaceAll('\\', '/') : pattern;
92
+ }
40
93
  /** Expand ONE pattern to its lexicographically sorted match list. */
41
- async function expandPattern(pattern, ignore) {
42
- const matched = await fastGlob([pattern], { ignore });
94
+ async function expandPattern(pattern, ignore, platform) {
95
+ const normalized = toGlobSeparators(pattern, platform);
96
+ const matched = await fastGlob([normalized], { ignore: ignoreFor(normalized, ignore) });
43
97
  return matched.sort();
44
98
  }
45
- export const globToEntryPoints = async (_entryPoints, ignore = []) => {
99
+ export const globToEntryPoints = async (_entryPoints, ignore = [], platform = process.platform) => {
46
100
  if (_entryPoints === undefined)
47
101
  return undefined;
48
- const fullIgnore = [...DEFAULT_IGNORE, ...ignore];
102
+ // The ignore list is fed to the same matcher, so it needs the same
103
+ // treatment — a backslashed `--exclude` silently excludes nothing.
104
+ const fullIgnore = [...DEFAULT_IGNORE, ...ignore.map((p) => toGlobSeparators(p, platform))];
49
105
  if (typeof _entryPoints === 'string') {
50
- return await expandPattern(_entryPoints, fullIgnore);
106
+ return await expandPattern(_entryPoints, fullIgnore, platform);
51
107
  }
52
108
  if (Array.isArray(_entryPoints)) {
53
109
  // Per-pattern expansion (not one combined fast-glob call): the
@@ -58,7 +114,7 @@ export const globToEntryPoints = async (_entryPoints, ignore = []) => {
58
114
  const out = [];
59
115
  const seen = new Set();
60
116
  for (const pattern of _entryPoints) {
61
- for (const matched of await expandPattern(pattern, fullIgnore)) {
117
+ for (const matched of await expandPattern(pattern, fullIgnore, platform)) {
62
118
  if (!seen.has(matched)) {
63
119
  seen.add(matched);
64
120
  out.push(matched);
@@ -70,7 +126,7 @@ export const globToEntryPoints = async (_entryPoints, ignore = []) => {
70
126
  const entryPoints = {};
71
127
  for (const input in _entryPoints) {
72
128
  const output = _entryPoints[input];
73
- for (const matched of await expandPattern(input, fullIgnore)) {
129
+ for (const matched of await expandPattern(input, fullIgnore, platform)) {
74
130
  entryPoints[matched] = output;
75
131
  }
76
132
  }
@@ -5,5 +5,5 @@ export * from './runtime.js';
5
5
  export * from './merge.js';
6
6
  export { detectFreeGlobals } from './detect-free-globals.js';
7
7
  export { resolveGlobalsList, writeRegisterInjectFile } from './scan-globals.js';
8
- export { inlineStaticReads } from './inline-static-reads.js';
8
+ export { inlineStaticReads, isAbsoluteFsPath } from './inline-static-reads.js';
9
9
  export { GJSIFY_VIRTUAL_PREFIX, isGjsifyVirtualModuleId } from './virtual-module-id.js';
@@ -5,5 +5,5 @@ export * from './runtime.js';
5
5
  export * from './merge.js';
6
6
  export { detectFreeGlobals } from './detect-free-globals.js';
7
7
  export { resolveGlobalsList, writeRegisterInjectFile } from './scan-globals.js';
8
- export { inlineStaticReads } from './inline-static-reads.js';
8
+ export { inlineStaticReads, isAbsoluteFsPath } from './inline-static-reads.js';
9
9
  export { GJSIFY_VIRTUAL_PREFIX, isGjsifyVirtualModuleId } from './virtual-module-id.js';
@@ -9,3 +9,33 @@ export declare function inlineStaticReads(src: string, sourceFilePath: string):
9
9
  contents: string;
10
10
  inlined: number;
11
11
  };
12
+ /**
13
+ * Is `v` an absolute filesystem path on `platform`?
14
+ *
15
+ * This is the last gate before a resolved expression is read from disk, and it
16
+ * used to be `v.startsWith('/')`. That is the right test for exactly one
17
+ * platform. On Windows every path this evaluator produces is `C:\…` — both
18
+ * documented compositions land there:
19
+ *
20
+ * readFileSync(fileURLToPath(new URL('./x.txt', import.meta.url)), 'utf8')
21
+ * readdirSync(path.join(__dirname))
22
+ *
23
+ * so both returned `undefined` and the call was left un-inlined. Silently: no
24
+ * warning, no error, just a bundle that keeps a runtime `readFileSync` against
25
+ * a build-site path and throws ENOENT the moment it is moved — which is the one
26
+ * failure this whole module exists to prevent (see the header). It also means
27
+ * the same source could bundle to DIFFERENT bytes on Windows than on Linux.
28
+ *
29
+ * Measured on win32 x64: the four `new URL(…)` patterns inline there correctly
30
+ * (the URL branch above never went through this gate); only the two that reduce
31
+ * to a path STRING were affected. No call site in this repo composes them
32
+ * inline today — every one goes through an intermediate variable, which the
33
+ * evaluator cannot follow on any platform — so no committed bundle is known to
34
+ * differ. This closes the gap before it opens.
35
+ *
36
+ * `platform` is injected and the branches use `path.win32`/`path.posix`
37
+ * explicitly, so both are exercised from either host — the same shape
38
+ * `extractPackageSpec` and `utils/entry-points.ts` use. On POSIX
39
+ * `posix.isAbsolute` IS `startsWith('/')`, so behaviour there is unchanged.
40
+ */
41
+ export declare function isAbsoluteFsPath(v: string, platform?: string): boolean;
@@ -42,7 +42,7 @@
42
42
  // `import.meta.url` rewriter still applies as a fallback.
43
43
  import * as acorn from 'acorn';
44
44
  import * as walk from 'acorn-walk';
45
- import { dirname, join, resolve, basename, relative, extname } from 'node:path';
45
+ import { dirname, join, resolve, basename, relative, extname, posix, win32 } from 'node:path';
46
46
  import { fileURLToPath, pathToFileURL } from 'node:url';
47
47
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
48
48
  /**
@@ -287,10 +287,42 @@ function evalPathExpr(node, ctx) {
287
287
  return undefined;
288
288
  if (v.startsWith('file://'))
289
289
  return fileURLToPath(v);
290
- if (v.startsWith('/'))
290
+ if (isAbsoluteFsPath(v))
291
291
  return v;
292
292
  return undefined;
293
293
  }
294
+ /**
295
+ * Is `v` an absolute filesystem path on `platform`?
296
+ *
297
+ * This is the last gate before a resolved expression is read from disk, and it
298
+ * used to be `v.startsWith('/')`. That is the right test for exactly one
299
+ * platform. On Windows every path this evaluator produces is `C:\…` — both
300
+ * documented compositions land there:
301
+ *
302
+ * readFileSync(fileURLToPath(new URL('./x.txt', import.meta.url)), 'utf8')
303
+ * readdirSync(path.join(__dirname))
304
+ *
305
+ * so both returned `undefined` and the call was left un-inlined. Silently: no
306
+ * warning, no error, just a bundle that keeps a runtime `readFileSync` against
307
+ * a build-site path and throws ENOENT the moment it is moved — which is the one
308
+ * failure this whole module exists to prevent (see the header). It also means
309
+ * the same source could bundle to DIFFERENT bytes on Windows than on Linux.
310
+ *
311
+ * Measured on win32 x64: the four `new URL(…)` patterns inline there correctly
312
+ * (the URL branch above never went through this gate); only the two that reduce
313
+ * to a path STRING were affected. No call site in this repo composes them
314
+ * inline today — every one goes through an intermediate variable, which the
315
+ * evaluator cannot follow on any platform — so no committed bundle is known to
316
+ * differ. This closes the gap before it opens.
317
+ *
318
+ * `platform` is injected and the branches use `path.win32`/`path.posix`
319
+ * explicitly, so both are exercised from either host — the same shape
320
+ * `extractPackageSpec` and `utils/entry-points.ts` use. On POSIX
321
+ * `posix.isAbsolute` IS `startsWith('/')`, so behaviour there is unchanged.
322
+ */
323
+ export function isAbsoluteFsPath(v, platform = process.platform) {
324
+ return (platform === 'win32' ? win32 : posix).isAbsolute(v);
325
+ }
294
326
  function evalExpr(node, ctx) {
295
327
  if (!node)
296
328
  return undefined;
@@ -14,7 +14,7 @@ import { writeFile, mkdir } from 'node:fs/promises';
14
14
  import { existsSync } from 'node:fs';
15
15
  import { createHash } from 'node:crypto';
16
16
  import { join } from 'node:path';
17
- import { GJS_GLOBALS_MAP, GJS_GLOBALS_GROUPS, REVERSE_BRIDGE_NATIVE_OVERRIDE_IDENTS } from '@gjsify/resolve-npm/globals-map';
17
+ import { GJS_GLOBALS_MAP, GJS_GLOBALS_GROUPS, REVERSE_BRIDGE_NATIVE_OVERRIDE_IDENTS, } from '@gjsify/resolve-npm/globals-map';
18
18
  import { ALIASES_WEB_FOR_GJS } from '@gjsify/resolve-npm';
19
19
  const GLOBALS_MAP = GJS_GLOBALS_MAP;
20
20
  const GLOBALS_GROUPS = GJS_GLOBALS_GROUPS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/rolldown-plugin-gjsify",
3
- "version": "0.26.0",
3
+ "version": "0.27.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",
@@ -33,7 +33,7 @@
33
33
  "lib"
34
34
  ],
35
35
  "scripts": {
36
- "clear": "rm -rf lib tsconfig.tsbuildinfo || exit 0",
36
+ "clear": "gjsify clear lib tsconfig.tsbuildinfo",
37
37
  "check": "gjsify tsc --noEmit",
38
38
  "build": "tsc"
39
39
  },
@@ -55,11 +55,11 @@
55
55
  ],
56
56
  "license": "MIT",
57
57
  "dependencies": {
58
- "@gjsify/console": "^0.26.0",
59
- "@gjsify/resolve-npm": "^0.26.0",
60
- "@gjsify/rolldown-plugin-deepkit": "^0.26.0",
61
- "@gjsify/rolldown-plugin-pnp": "^0.26.0",
62
- "@gjsify/vite-plugin-blueprint": "^0.26.0",
58
+ "@gjsify/console": "^0.27.0",
59
+ "@gjsify/resolve-npm": "^0.27.0",
60
+ "@gjsify/rolldown-plugin-deepkit": "^0.27.0",
61
+ "@gjsify/rolldown-plugin-pnp": "^0.27.0",
62
+ "@gjsify/vite-plugin-blueprint": "^0.27.0",
63
63
  "@rollup/pluginutils": "^5.4.0",
64
64
  "acorn": "^8.17.0",
65
65
  "acorn-walk": "^8.3.5",
@@ -68,7 +68,7 @@
68
68
  "sass": "^1.101.0"
69
69
  },
70
70
  "peerDependencies": {
71
- "@gjsify/lightningcss-native": "^0.26.0",
71
+ "@gjsify/lightningcss-native": "^0.27.0",
72
72
  "rolldown": "^1.1.4"
73
73
  },
74
74
  "peerDependenciesMeta": {