@gjsify/rolldown-plugin-gjsify 0.26.1 → 0.27.1
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 +1 -1
- package/lib/globals.js +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.js +2 -2
- package/lib/plugins/napi-node-addon.d.ts +41 -0
- package/lib/plugins/napi-node-addon.js +57 -2
- package/lib/plugins/rewrite-node-modules-paths.d.ts +23 -1
- package/lib/plugins/rewrite-node-modules-paths.js +26 -3
- package/lib/utils/auto-globals.js +9 -7
- package/lib/utils/entry-points.d.ts +1 -1
- package/lib/utils/entry-points.js +63 -7
- package/lib/utils/index.d.ts +1 -1
- package/lib/utils/index.js +1 -1
- package/lib/utils/inline-static-reads.d.ts +30 -0
- package/lib/utils/inline-static-reads.js +34 -2
- package/lib/utils/scan-globals.js +1 -1
- package/package.json +9 -9
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, isGjsifyNativeBridge, 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, isGjsifyNativeBridge, 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';
|
|
@@ -67,8 +67,39 @@ export interface AddonPackageJson {
|
|
|
67
67
|
exports?: unknown;
|
|
68
68
|
browser?: string;
|
|
69
69
|
napi?: unknown;
|
|
70
|
+
gjsify?: unknown;
|
|
70
71
|
optionalDependencies?: Record<string, string>;
|
|
71
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Is this manifest a gjsify NATIVE BRIDGE whose prebuilds live in per-target
|
|
75
|
+
* sibling packages (ADR 0017) — `@gjsify/webgl` + `@gjsify/webgl-darwin-x64`, …?
|
|
76
|
+
*
|
|
77
|
+
* Such a bridge ships a GObject-Introspection typelib plus a `.so`/`.dylib`/`.dll`
|
|
78
|
+
* loaded through `gi://`, NEVER a Node-API `.node` addon, so it must never be
|
|
79
|
+
* routed through `loadAddon()`.
|
|
80
|
+
*
|
|
81
|
+
* It has to be excluded EXPLICITLY because the two conventions are
|
|
82
|
+
* indistinguishable by name: ADR 0017 spells a target `<os>-<arch>` and names the
|
|
83
|
+
* package `<self>-<os>-<arch>` in `optionalDependencies` — which is exactly
|
|
84
|
+
* napi-rs' `<self>-<triple>` sibling scheme whenever the target happens to be a
|
|
85
|
+
* name napi-rs also uses. On Linux the two vocabularies differ by the libc token
|
|
86
|
+
* (`linux-x64` vs napi-rs' `linux-x64-gnu`), so `hostNapiRsTriple()` never matched
|
|
87
|
+
* and nothing fired; on macOS napi-rs' triple IS `darwin-x64` / `darwin-arm64`,
|
|
88
|
+
* so every split bridge looked like a napi-rs package to
|
|
89
|
+
* {@link isNapiRsPackageJson} and {@link resolveNapiRsEntryAddon} then probed
|
|
90
|
+
* `ctx.resolve('@gjsify/webgl-darwin-x64')` — an artifact-only package with no JS
|
|
91
|
+
* entry at all. That probe does not return null: the `unresolved-workspace-import`
|
|
92
|
+
* guard (correctly) treats an unresolvable bare `@gjsify/*` as FATAL, so
|
|
93
|
+
* `gjsify build --app gjs` of any consumer of any split bridge died on darwin with
|
|
94
|
+
* "cannot resolve the workspace package" — invisible to every Linux CI job.
|
|
95
|
+
*
|
|
96
|
+
* `gjsify.platforms` is the right discriminator: it is the OS-axis declaration
|
|
97
|
+
* every native bridge carries (§ Runtime & platform model) and no napi-rs package
|
|
98
|
+
* has any reason to declare it.
|
|
99
|
+
*
|
|
100
|
+
* Exported for the unit tests.
|
|
101
|
+
*/
|
|
102
|
+
export declare function isGjsifyNativeBridge(pkg: AddonPackageJson): boolean;
|
|
72
103
|
/**
|
|
73
104
|
* A package.json describes a napi-rs generated-loader package when EITHER
|
|
74
105
|
* signal holds — both are package.json-level (not source sniffing):
|
|
@@ -108,6 +139,16 @@ export declare function detectNapiRsEntry(entryFile: string): {
|
|
|
108
139
|
* package's binaries. Exported for the unit tests.
|
|
109
140
|
*/
|
|
110
141
|
export declare function isNapiRsSibling(pkg: AddonPackageJson, dep: string): boolean;
|
|
142
|
+
/**
|
|
143
|
+
* The napi-rs short platform triple for the CURRENT host (`linux-x64-gnu`,
|
|
144
|
+
* `darwin-arm64`, `win32-x64-msvc`, …) — the tail napi-rs stamps into a sibling
|
|
145
|
+
* package name (`@node-rs/argon2-linux-x64-gnu`) and a local binary
|
|
146
|
+
* (`argon2.linux-x64-gnu.node`). Selects BOTH the sibling package and the
|
|
147
|
+
* local-file fallback — see `resolveNapiRsEntryAddon` for why the sibling path
|
|
148
|
+
* cannot just take whatever resolves. Returns null for a host napi-rs doesn't
|
|
149
|
+
* name, and the caller then widens rather than guessing.
|
|
150
|
+
*/
|
|
151
|
+
export declare function hostNapiRsTriple(): string | null;
|
|
111
152
|
/**
|
|
112
153
|
* Transparent `.node`-addon loader for `gjsify build --app gjs`.
|
|
113
154
|
*
|
|
@@ -384,6 +384,42 @@ export function classifySpecifier(source) {
|
|
|
384
384
|
return { kind: 'napi-rs-candidate' };
|
|
385
385
|
return null;
|
|
386
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Is this manifest a gjsify NATIVE BRIDGE whose prebuilds live in per-target
|
|
389
|
+
* sibling packages (ADR 0017) — `@gjsify/webgl` + `@gjsify/webgl-darwin-x64`, …?
|
|
390
|
+
*
|
|
391
|
+
* Such a bridge ships a GObject-Introspection typelib plus a `.so`/`.dylib`/`.dll`
|
|
392
|
+
* loaded through `gi://`, NEVER a Node-API `.node` addon, so it must never be
|
|
393
|
+
* routed through `loadAddon()`.
|
|
394
|
+
*
|
|
395
|
+
* It has to be excluded EXPLICITLY because the two conventions are
|
|
396
|
+
* indistinguishable by name: ADR 0017 spells a target `<os>-<arch>` and names the
|
|
397
|
+
* package `<self>-<os>-<arch>` in `optionalDependencies` — which is exactly
|
|
398
|
+
* napi-rs' `<self>-<triple>` sibling scheme whenever the target happens to be a
|
|
399
|
+
* name napi-rs also uses. On Linux the two vocabularies differ by the libc token
|
|
400
|
+
* (`linux-x64` vs napi-rs' `linux-x64-gnu`), so `hostNapiRsTriple()` never matched
|
|
401
|
+
* and nothing fired; on macOS napi-rs' triple IS `darwin-x64` / `darwin-arm64`,
|
|
402
|
+
* so every split bridge looked like a napi-rs package to
|
|
403
|
+
* {@link isNapiRsPackageJson} and {@link resolveNapiRsEntryAddon} then probed
|
|
404
|
+
* `ctx.resolve('@gjsify/webgl-darwin-x64')` — an artifact-only package with no JS
|
|
405
|
+
* entry at all. That probe does not return null: the `unresolved-workspace-import`
|
|
406
|
+
* guard (correctly) treats an unresolvable bare `@gjsify/*` as FATAL, so
|
|
407
|
+
* `gjsify build --app gjs` of any consumer of any split bridge died on darwin with
|
|
408
|
+
* "cannot resolve the workspace package" — invisible to every Linux CI job.
|
|
409
|
+
*
|
|
410
|
+
* `gjsify.platforms` is the right discriminator: it is the OS-axis declaration
|
|
411
|
+
* every native bridge carries (§ Runtime & platform model) and no napi-rs package
|
|
412
|
+
* has any reason to declare it.
|
|
413
|
+
*
|
|
414
|
+
* Exported for the unit tests.
|
|
415
|
+
*/
|
|
416
|
+
export function isGjsifyNativeBridge(pkg) {
|
|
417
|
+
const gjsify = pkg?.gjsify;
|
|
418
|
+
if (gjsify === null || typeof gjsify !== 'object')
|
|
419
|
+
return false;
|
|
420
|
+
const { platforms, prebuilds } = gjsify;
|
|
421
|
+
return Array.isArray(platforms) || typeof prebuilds === 'string';
|
|
422
|
+
}
|
|
387
423
|
/**
|
|
388
424
|
* A package.json describes a napi-rs generated-loader package when EITHER
|
|
389
425
|
* signal holds — both are package.json-level (not source sniffing):
|
|
@@ -576,6 +612,10 @@ function napiSiblingPrefixes(pkg) {
|
|
|
576
612
|
export function isNapiRsSibling(pkg, dep) {
|
|
577
613
|
if (!NAPI_RS_TRIPLE_RE.test(dep))
|
|
578
614
|
return false;
|
|
615
|
+
// A gjsify ADR-0017 platform package wears the same name — see
|
|
616
|
+
// {@link isGjsifyNativeBridge} for the darwin-only build break that caused.
|
|
617
|
+
if (isGjsifyNativeBridge(pkg))
|
|
618
|
+
return false;
|
|
579
619
|
return napiSiblingPrefixes(pkg).some((prefix) => dep.startsWith(`${prefix}-`));
|
|
580
620
|
}
|
|
581
621
|
/**
|
|
@@ -587,7 +627,7 @@ export function isNapiRsSibling(pkg, dep) {
|
|
|
587
627
|
* cannot just take whatever resolves. Returns null for a host napi-rs doesn't
|
|
588
628
|
* name, and the caller then widens rather than guessing.
|
|
589
629
|
*/
|
|
590
|
-
function hostNapiRsTriple() {
|
|
630
|
+
export function hostNapiRsTriple() {
|
|
591
631
|
const platform = process.platform;
|
|
592
632
|
const arch = process.arch;
|
|
593
633
|
const archTok = {
|
|
@@ -650,7 +690,22 @@ async function resolveNapiRsEntryAddon(ctx, pkgRoot, pkg, importer) {
|
|
|
650
690
|
// loaders do, so this matches the binary Node would have loaded.
|
|
651
691
|
const ordered = triple === null ? siblings : siblings.filter((dep) => dep.endsWith(`-${triple}`));
|
|
652
692
|
for (const dep of ordered) {
|
|
653
|
-
|
|
693
|
+
// This resolve is a QUESTION ("is a host-triple binary installed?"), but it
|
|
694
|
+
// can THROW rather than answer: `skipSelf` skips only THIS plugin, so the
|
|
695
|
+
// `unresolved-workspace-import` guard still runs at `order:'post'` and
|
|
696
|
+
// (correctly, for a real import) makes an unresolvable bare `@gjsify/*`
|
|
697
|
+
// FATAL. A misread sibling name then kills the whole build instead of
|
|
698
|
+
// declining — measured on darwin, where every ADR-0017 platform package
|
|
699
|
+
// matched this path. The name check above is the fix; this keeps the class
|
|
700
|
+
// from ever being fatal again, and says which specifier was asked about.
|
|
701
|
+
let resolved = null;
|
|
702
|
+
try {
|
|
703
|
+
resolved = await ctx.resolve(dep, importer, { skipSelf: true });
|
|
704
|
+
}
|
|
705
|
+
catch (err) {
|
|
706
|
+
warnSafe(ctx, `[gjsify-napi] probing the platform sibling "${dep}" of "${pkg.name ?? pkgRoot}" failed; not rewriting its entry (${err instanceof Error ? err.message.split('\n')[0] : String(err)})`);
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
654
709
|
if (!resolved)
|
|
655
710
|
continue;
|
|
656
711
|
const abs = rawAddonPath(resolved.id);
|
|
@@ -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 =
|
|
111
|
-
return idx < 0 ?
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
}
|
package/lib/utils/index.d.ts
CHANGED
|
@@ -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';
|
package/lib/utils/index.js
CHANGED
|
@@ -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
|
|
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.
|
|
3
|
+
"version": "0.27.1",
|
|
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,9 +33,9 @@
|
|
|
33
33
|
"lib"
|
|
34
34
|
],
|
|
35
35
|
"scripts": {
|
|
36
|
-
"clear": "
|
|
36
|
+
"clear": "gjsify clear lib tsconfig.tsbuildinfo",
|
|
37
37
|
"check": "gjsify tsc --noEmit",
|
|
38
|
-
"build": "tsc"
|
|
38
|
+
"build": "gjsify tsc"
|
|
39
39
|
},
|
|
40
40
|
"repository": {
|
|
41
41
|
"type": "git",
|
|
@@ -55,11 +55,11 @@
|
|
|
55
55
|
],
|
|
56
56
|
"license": "MIT",
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@gjsify/console": "^0.
|
|
59
|
-
"@gjsify/resolve-npm": "^0.
|
|
60
|
-
"@gjsify/rolldown-plugin-deepkit": "^0.
|
|
61
|
-
"@gjsify/rolldown-plugin-pnp": "^0.
|
|
62
|
-
"@gjsify/vite-plugin-blueprint": "^0.
|
|
58
|
+
"@gjsify/console": "^0.27.1",
|
|
59
|
+
"@gjsify/resolve-npm": "^0.27.1",
|
|
60
|
+
"@gjsify/rolldown-plugin-deepkit": "^0.27.1",
|
|
61
|
+
"@gjsify/rolldown-plugin-pnp": "^0.27.1",
|
|
62
|
+
"@gjsify/vite-plugin-blueprint": "^0.27.1",
|
|
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.
|
|
71
|
+
"@gjsify/lightningcss-native": "^0.27.1",
|
|
72
72
|
"rolldown": "^1.1.4"
|
|
73
73
|
},
|
|
74
74
|
"peerDependenciesMeta": {
|