@gjsify/cli 0.64.8 → 0.64.10
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/dist/cli.gjs.mjs +56 -56
- package/lib/commands/ship.js +36 -3
- package/lib/types/config-data.d.ts +25 -0
- package/lib/utils/ship/layout.d.ts +10 -0
- package/lib/utils/ship/layout.js +12 -0
- package/lib/utils/ship/node-modules.d.ts +39 -0
- package/lib/utils/ship/node-modules.js +192 -0
- package/package.json +20 -20
package/lib/commands/ship.js
CHANGED
|
@@ -35,6 +35,7 @@ import { describeExit, spawnToCompletion } from '../utils/spawn.js';
|
|
|
35
35
|
import { buildDeb } from '../utils/ship/deb.js';
|
|
36
36
|
import { deriveDepends, warnAboutGjsFloor, warnAboutNodeFloor } from '../utils/ship/depends.js';
|
|
37
37
|
import { isGtkRuntimeTarget, stageAppRuntime } from '../utils/ship/app-runtime.js';
|
|
38
|
+
import { resolveNodeModulesClosure, stageNodeModules } from '../utils/ship/node-modules.js';
|
|
38
39
|
import { acquireGtkRuntime } from '../utils/ship/gtk-runtime-acquire.js';
|
|
39
40
|
import { discoverPayload } from '../utils/ship/discover.js';
|
|
40
41
|
import { appDirFor, appDirPayload, appImageHostRequirements, appImageRuntimeNotice, buildAppImage, findPinnedRuntime, stampAppDirTimes, } from '../utils/ship/appimage.js';
|
|
@@ -44,7 +45,7 @@ import { buildFlatpakBundle } from '../utils/ship/flatpak.js';
|
|
|
44
45
|
import { iconSizes, resolveAppIcon } from '../utils/ship/icons.js';
|
|
45
46
|
import { localizeMetadata } from '../utils/ship/localize-metadata.js';
|
|
46
47
|
import { assertHostCanFinish, assertKindCanPack, assertToolsInstalled, canCarryTicket, configuredFormats, defaultFormatIds, formatIdsFor, resolveFormats, windowsProgramDirName, FORMAT_IDS, FORMATS, } from '../utils/ship/formats.js';
|
|
47
|
-
import { assertLayoutSupportsArch, hostLayout, layoutForOs, place, placeStage, resolveLayout,
|
|
48
|
+
import { assertLayoutSupportsArch, hostLayout, layoutForOs, place, placeStage, resolveLayout, payloadArchExemptions, LAYOUT_NAMES, } from '../utils/ship/layout.js';
|
|
48
49
|
import { isNodeRuntimeTarget, resolveNodeRuntime } from '../utils/ship/node-runtime.js';
|
|
49
50
|
import { isUnder, SHARE } from '../utils/ship/share-dirs.js';
|
|
50
51
|
import { compileSchemasForPayload, compileSchemasForStage } from '../utils/ship/schemas.js';
|
|
@@ -416,6 +417,7 @@ async function assemble(args) {
|
|
|
416
417
|
// carrying a second copy inside the payload would be a private interpreter no
|
|
417
418
|
// distribution update ever patches.
|
|
418
419
|
const runtime = await resolveCarriedRuntime(layout, settings, projectDir);
|
|
420
|
+
carryNodeModules(runtime, layout, settings, projectDir, ship.nodeModules, ship.nodeModulesExclude);
|
|
419
421
|
const stageInputs = {
|
|
420
422
|
bundleFiles: discovered.bundleFiles,
|
|
421
423
|
launcher: renderLauncher(settings, basename(settings.bundlePath), layout, runtime.launcher),
|
|
@@ -510,7 +512,7 @@ async function assemble(args) {
|
|
|
510
512
|
// stub unconditionally (it used to skip arm64 entirely), so an arm64 stage
|
|
511
513
|
// legitimately carries one x64 PE beside its arm64 payload.
|
|
512
514
|
assertPayloadMatchesArch(readStage([stageDir], staged), settings.arch, {
|
|
513
|
-
exempt: layout
|
|
515
|
+
exempt: payloadArchExemptions(layout.os, settings),
|
|
514
516
|
});
|
|
515
517
|
// What the map carried into a layout that has no install step for it.
|
|
516
518
|
//
|
|
@@ -802,6 +804,35 @@ async function resolveCarriedRuntime(layout, settings, projectDir) {
|
|
|
802
804
|
...(acquired.kind === 'failed' ? { gtkFetchFailure: acquired.reason } : {}),
|
|
803
805
|
});
|
|
804
806
|
}
|
|
807
|
+
/**
|
|
808
|
+
* `gjsify.ship.nodeModules`: the npm packages an `--app node` bundle imports and cannot contain,
|
|
809
|
+
* staged beside the bundle with what they need at runtime (`utils/ship/node-modules.ts`).
|
|
810
|
+
*
|
|
811
|
+
* Only for a node payload: a GJS bundle inlines or rewrites them, so one declaration can serve a
|
|
812
|
+
* project that ships GJS on Linux and Node on macOS and Windows. Mutates `runtime` because the
|
|
813
|
+
* files take the same route as the carried interpreter — already stage-relative, past the planner.
|
|
814
|
+
*/
|
|
815
|
+
function carryNodeModules(runtime, layout, settings, projectDir, declared, exclude) {
|
|
816
|
+
if (declared === undefined || declared.length === 0 || settings.app !== 'node')
|
|
817
|
+
return;
|
|
818
|
+
const closure = resolveNodeModulesClosure(declared, projectDir, {
|
|
819
|
+
target: { os: layout.os, cpu: settings.arch },
|
|
820
|
+
exclude,
|
|
821
|
+
});
|
|
822
|
+
runtime.files.push(...stageNodeModules(closure, layout.dirs(settings).bundle));
|
|
823
|
+
runtime.found.push(`node_modules: ${closure.packages.length} package(s) for ${declared.join(', ')}`);
|
|
824
|
+
if (closure.excluded.length > 0) {
|
|
825
|
+
console.log(`${LOG} node_modules: left out by gjsify.ship.nodeModulesExclude: ${closure.excluded.join(', ')}`);
|
|
826
|
+
}
|
|
827
|
+
if (closure.skippedForTarget.length > 0) {
|
|
828
|
+
console.log(`${LOG} node_modules: left out, installed but built for another platform than ${layout.os}-${settings.arch}: ` +
|
|
829
|
+
closure.skippedForTarget.join(', '));
|
|
830
|
+
}
|
|
831
|
+
if (closure.skippedOptional.length > 0) {
|
|
832
|
+
console.log(`${LOG} node_modules: ${closure.skippedOptional.length} optional dependenc(ies) not installed, ` +
|
|
833
|
+
`so not carried (other platforms' builds): ${closure.skippedOptional.join(', ')}`);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
805
836
|
/** How long a fetched runtime stays in the dlx cache — the same week `gjsify showcase` uses. */
|
|
806
837
|
const DLX_CACHE_MAX_AGE_MIN = 60 * 24 * 7;
|
|
807
838
|
/**
|
|
@@ -979,7 +1010,9 @@ async function packOne(input) {
|
|
|
979
1010
|
// asserting afterwards would compare against a label that no longer names an
|
|
980
1011
|
// architecture, and the mismatch that matters is exactly the one on a payload
|
|
981
1012
|
// that IS architecture-specific.
|
|
982
|
-
assertPayloadMatchesArch(assembled, input.arch
|
|
1013
|
+
assertPayloadMatchesArch(assembled, input.arch, {
|
|
1014
|
+
exempt: payloadArchExemptions(format.layoutOs, settings),
|
|
1015
|
+
});
|
|
983
1016
|
// The second half of the same idea, on the other label this artifact carries:
|
|
984
1017
|
// something declares which interpreter runs the app, and until now nothing
|
|
985
1018
|
// compared it to the launcher that will. They CAN disagree — a stage assembled
|
|
@@ -606,6 +606,31 @@ export interface ConfigDataShip extends AppMetadata {
|
|
|
606
606
|
* bundle seal and `dlopen` both refuse. A target with no key stages nothing.
|
|
607
607
|
*/
|
|
608
608
|
bundledTypelibs?: string[] | ShipBundledTypelibsOptions;
|
|
609
|
+
/**
|
|
610
|
+
* npm packages the `--app node` bundle imports but cannot contain, by NAME — e.g.
|
|
611
|
+
* `["node-llama-cpp"]`.
|
|
612
|
+
*
|
|
613
|
+
* An `--app node` build keeps every Node-API addon package external (an addon finds its
|
|
614
|
+
* `.node` relative to its own files), so the bundle `import`s it by name at runtime. A `.app`
|
|
615
|
+
* or program directory has no `npm install`; without this entry the artifact fails with
|
|
616
|
+
* `ERR_MODULE_NOT_FOUND` before the program starts. Each name is staged into the bundle's
|
|
617
|
+
* `node_modules/` with its `dependencies` and `optionalDependencies`, resolved from the
|
|
618
|
+
* project the way Node would, at the paths they are installed at (two versions of one package
|
|
619
|
+
* stay nested). An optional dependency that is not installed is skipped, which is how a
|
|
620
|
+
* per-platform addon package is chosen: `gjsify install --os <os> --cpu <cpu>` before `ship`.
|
|
621
|
+
* A required one that is missing is an error.
|
|
622
|
+
*
|
|
623
|
+
* Ignored for a GJS payload, so one declaration serves a project that ships GJS on Linux and
|
|
624
|
+
* Node on macOS and Windows. `@gjsify/node-gi` is not listed here; `ship` carries it itself.
|
|
625
|
+
*/
|
|
626
|
+
nodeModules?: string[];
|
|
627
|
+
/**
|
|
628
|
+
* Packages {@link ConfigDataShip.nodeModules} leaves out wherever they turn up in the closure —
|
|
629
|
+
* for an optional dependency the project does not want to ship, e.g. the CUDA and Vulkan
|
|
630
|
+
* builds of an inference addon (`@node-llama-cpp/win-x64-cuda`, hundreds of MB) when the
|
|
631
|
+
* program only runs on the CPU. Names a declared package itself are not affected.
|
|
632
|
+
*/
|
|
633
|
+
nodeModulesExclude?: string[];
|
|
609
634
|
/**
|
|
610
635
|
* Additions for ONE package format — `deb` or `rpm` — on top of everything above.
|
|
611
636
|
*
|
|
@@ -330,6 +330,16 @@ export declare function launcherPath(layout: Layout, identity: LayoutIdentity):
|
|
|
330
330
|
* is not there, at exit 0, with a window that never appears.
|
|
331
331
|
*/
|
|
332
332
|
export declare function windowsGuiLauncherPath(identity: LayoutIdentity): string;
|
|
333
|
+
/**
|
|
334
|
+
* The payload paths exempt from the architecture check for one OS (`assertPayloadMatchesArch`).
|
|
335
|
+
*
|
|
336
|
+
* On windows that is the GUI-launcher stub, x86-64 machine code on every windows arch by design.
|
|
337
|
+
* ONE function for the two places that read the stage back — the assemble phase and the pack
|
|
338
|
+
* phase — because the pack phase once had its own call without the exemption: an arm64 stage
|
|
339
|
+
* assembled fine and every package refused it with "stamped.exe is built for x64". `undefined`
|
|
340
|
+
* for an OS with no such file.
|
|
341
|
+
*/
|
|
342
|
+
export declare function payloadArchExemptions(os: HostOs, identity: LayoutIdentity): ReadonlySet<string> | undefined;
|
|
333
343
|
/**
|
|
334
344
|
* What the windows GUI launcher calls the file it writes under `%TEMP%`.
|
|
335
345
|
*
|
package/lib/utils/ship/layout.js
CHANGED
|
@@ -552,6 +552,18 @@ export function windowsGuiLauncherPath(identity) {
|
|
|
552
552
|
}
|
|
553
553
|
return posix.join(layout.dirs(identity).launcher, `${identity.binaryName}.exe`);
|
|
554
554
|
}
|
|
555
|
+
/**
|
|
556
|
+
* The payload paths exempt from the architecture check for one OS (`assertPayloadMatchesArch`).
|
|
557
|
+
*
|
|
558
|
+
* On windows that is the GUI-launcher stub, x86-64 machine code on every windows arch by design.
|
|
559
|
+
* ONE function for the two places that read the stage back — the assemble phase and the pack
|
|
560
|
+
* phase — because the pack phase once had its own call without the exemption: an arm64 stage
|
|
561
|
+
* assembled fine and every package refused it with "stamped.exe is built for x64". `undefined`
|
|
562
|
+
* for an OS with no such file.
|
|
563
|
+
*/
|
|
564
|
+
export function payloadArchExemptions(os, identity) {
|
|
565
|
+
return os === 'win32' ? new Set([windowsGuiLauncherPath(identity)]) : undefined;
|
|
566
|
+
}
|
|
555
567
|
/**
|
|
556
568
|
* What the windows GUI launcher calls the file it writes under `%TEMP%`.
|
|
557
569
|
*
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { StagedFile } from './types.js';
|
|
2
|
+
/** One package of the closure, ready to stage. */
|
|
3
|
+
export interface NodeModulePackage {
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
/** Where the files are read from — the installed directory. */
|
|
7
|
+
dir: string;
|
|
8
|
+
/** POSIX path below the staged `node_modules/`: `yargs`, `a/node_modules/b`, `@scope/pkg`. */
|
|
9
|
+
dest: string;
|
|
10
|
+
}
|
|
11
|
+
/** The platform the artifact is for, spelled like npm's `os` / `cpu` fields (`darwin`, `win32`; `arm64`, `x64`). */
|
|
12
|
+
export interface NodeModulesTarget {
|
|
13
|
+
os: string;
|
|
14
|
+
cpu: string;
|
|
15
|
+
}
|
|
16
|
+
export interface NodeModulesOptions {
|
|
17
|
+
/** Platform filter, see {@link NodeModulesTarget}. Without one nothing is filtered by `os` / `cpu`. */
|
|
18
|
+
target?: NodeModulesTarget;
|
|
19
|
+
/** Package names left out wherever they appear below a declared one — `gjsify.ship.nodeModulesExclude`. */
|
|
20
|
+
exclude?: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
export interface NodeModulesClosure {
|
|
23
|
+
packages: NodeModulePackage[];
|
|
24
|
+
/** Optional dependencies that are not installed — expected for other platforms, listed for the log. */
|
|
25
|
+
skippedOptional: string[];
|
|
26
|
+
/** Dependencies installed here but built for another platform, named by their `os` / `cpu` fields. */
|
|
27
|
+
skippedForTarget: string[];
|
|
28
|
+
/** Dependencies the project asked to leave out, and that were reached. */
|
|
29
|
+
excluded: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve `names` and everything they need at runtime, from `cwd`.
|
|
33
|
+
*
|
|
34
|
+
* Throws on a name that is not installed, a required dependency that is not, and two different
|
|
35
|
+
* installed copies that would land on one staged path.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveNodeModulesClosure(names: readonly string[], cwd: string, options?: NodeModulesOptions): NodeModulesClosure;
|
|
38
|
+
/** The closure as stage-relative files, under `<bundleDir>/node_modules/`. */
|
|
39
|
+
export declare function stageNodeModules(closure: NodeModulesClosure, bundleDir: string): StagedFile[];
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// npm packages an `--app node` bundle imports but cannot contain: found, then staged as a TREE.
|
|
3
|
+
//
|
|
4
|
+
// WHY THIS EXISTS. `rolldown-plugin-gjsify`'s `nodeNativeExternalPlugin` keeps every Node-API
|
|
5
|
+
// addon package EXTERNAL in an `--app node` bundle, because an addon finds its `.node` relative
|
|
6
|
+
// to its own files and a bundle moves them ("an `--app node` bundle of a native dependency needs
|
|
7
|
+
// `node_modules` at runtime, like any `npm install`-ed program"). A shipped `.app` or program
|
|
8
|
+
// directory has no `npm install`. Until this module the only package `ship` carried was
|
|
9
|
+
// `@gjsify/node-gi`, by name — so a project with one more native dependency produced an artifact
|
|
10
|
+
// that fails at its first `import` on every machine but the author's. Measured on a real consumer
|
|
11
|
+
// (Stamped, `node-llama-cpp`): the released macOS `.app` exited with
|
|
12
|
+
// `ERR_MODULE_NOT_FOUND: Cannot find package 'node-llama-cpp'` before opening a window, and no
|
|
13
|
+
// CI step noticed because nothing ever started the artifact.
|
|
14
|
+
//
|
|
15
|
+
// THE DECLARATION. `gjsify.ship.nodeModules` lists package NAMES, nothing else. The names are the
|
|
16
|
+
// entry points the bundle imports; this module adds everything they need at runtime.
|
|
17
|
+
//
|
|
18
|
+
// THE CLOSURE is node's own resolution rule applied to the installed tree: for each package,
|
|
19
|
+
// `dependencies` and `optionalDependencies`, each found by walking `node_modules` upward from the
|
|
20
|
+
// package that asks for it. A missing optional dependency is skipped silently — that is the
|
|
21
|
+
// per-platform filter (`@node-llama-cpp/mac-arm64-metal` is optional; `gjsify install --os
|
|
22
|
+
// darwin --cpu arm64` is what puts the right one on disk; one that IS on disk but whose `os` /
|
|
23
|
+
// `cpu` fields exclude the artifact's target is left out too, since installs accumulate) — and a missing required one is an
|
|
24
|
+
// error naming the chain, because a package without its dependency is the very artifact this
|
|
25
|
+
// exists to prevent.
|
|
26
|
+
//
|
|
27
|
+
// THE LAYOUT IS THE INSTALLED ONE. Each package lands at the path it was FOUND at, relative to
|
|
28
|
+
// the first `node_modules` in that path: `node_modules/a/node_modules/b` stays nested, which is
|
|
29
|
+
// how two versions of `yargs` coexist. Flattening would be a guess about which one wins.
|
|
30
|
+
//
|
|
31
|
+
// NOT INCLUDED: `@gjsify/node-gi`, which `app-runtime.ts` stages as a trimmed tree of its own, and
|
|
32
|
+
// any nested `node_modules` that is not reached through a declared dependency (an npm install
|
|
33
|
+
// leaves dev-only trees there).
|
|
34
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from 'node:fs';
|
|
35
|
+
import { dirname, join, posix, relative, sep } from 'node:path';
|
|
36
|
+
import { isExecutableAsset } from './plan.js';
|
|
37
|
+
/** The package this module leaves to `app-runtime.ts`. */
|
|
38
|
+
const LEFT_TO_APP_RUNTIME = '@gjsify/node-gi';
|
|
39
|
+
function readManifest(dir) {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8'));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** `<from>/node_modules/<name>`, then each ancestor's — node's lookup, on the file system alone. */
|
|
48
|
+
function findInstalled(name, from) {
|
|
49
|
+
let dir = from;
|
|
50
|
+
for (;;) {
|
|
51
|
+
const candidate = join(dir, 'node_modules', ...name.split('/'));
|
|
52
|
+
if (existsSync(join(candidate, 'package.json')))
|
|
53
|
+
return candidate;
|
|
54
|
+
const parent = dirname(dir);
|
|
55
|
+
if (parent === dir)
|
|
56
|
+
return null;
|
|
57
|
+
dir = parent;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** npm's rule for `os` / `cpu`: a `!x` entry blocks `x`; otherwise, if any plain entry exists, `value` must be one. */
|
|
61
|
+
function allows(field, value) {
|
|
62
|
+
const list = typeof field === 'string' ? [field] : (field ?? []);
|
|
63
|
+
if (list.includes(`!${value}`))
|
|
64
|
+
return false;
|
|
65
|
+
const positive = list.filter((entry) => !entry.startsWith('!'));
|
|
66
|
+
return positive.length === 0 || positive.includes(value);
|
|
67
|
+
}
|
|
68
|
+
/** The path below the first `node_modules` segment, POSIX-separated, or `null` when there is none. */
|
|
69
|
+
function destinationOf(found) {
|
|
70
|
+
const parts = found.split(sep);
|
|
71
|
+
const at = parts.indexOf('node_modules');
|
|
72
|
+
return at === -1 || at === parts.length - 1 ? null : parts.slice(at + 1).join('/');
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolve `names` and everything they need at runtime, from `cwd`.
|
|
76
|
+
*
|
|
77
|
+
* Throws on a name that is not installed, a required dependency that is not, and two different
|
|
78
|
+
* installed copies that would land on one staged path.
|
|
79
|
+
*/
|
|
80
|
+
export function resolveNodeModulesClosure(names, cwd, options = {}) {
|
|
81
|
+
const { target, exclude = [] } = options;
|
|
82
|
+
const packages = new Map();
|
|
83
|
+
const skippedOptional = [];
|
|
84
|
+
const skippedForTarget = [];
|
|
85
|
+
const excluded = [];
|
|
86
|
+
const visit = (name, from, via, optional) => {
|
|
87
|
+
if (name === LEFT_TO_APP_RUNTIME)
|
|
88
|
+
return;
|
|
89
|
+
if (via.length > 0 && exclude.includes(name)) {
|
|
90
|
+
if (!excluded.includes(name))
|
|
91
|
+
excluded.push(name);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const found = findInstalled(name, from);
|
|
95
|
+
if (found === null) {
|
|
96
|
+
if (optional) {
|
|
97
|
+
if (!skippedOptional.includes(name))
|
|
98
|
+
skippedOptional.push(name);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
throw new Error(`gjsify ship: \`${name}\` is not installed${via.length > 0 ? ` (needed by ${via.join(' > ')})` : ''}. ` +
|
|
102
|
+
(via.length === 0
|
|
103
|
+
? '`gjsify.ship.nodeModules` names it, so the artifact must carry it — install it, or remove the entry.'
|
|
104
|
+
: 'A package without its required dependency is an artifact that fails at its first import.'));
|
|
105
|
+
}
|
|
106
|
+
const dest = destinationOf(found);
|
|
107
|
+
if (dest === null) {
|
|
108
|
+
throw new Error(`gjsify ship: ${found} is not inside a node_modules directory, so it has no place to be staged.`);
|
|
109
|
+
}
|
|
110
|
+
const dir = realpathSync(found);
|
|
111
|
+
const manifest = readManifest(dir);
|
|
112
|
+
if (manifest === null)
|
|
113
|
+
throw new Error(`gjsify ship: ${join(dir, 'package.json')} is unreadable.`);
|
|
114
|
+
// A dependency built for another platform may well be on disk: `gjsify install --os` adds to a
|
|
115
|
+
// tree, it does not clear it. Carrying it would put a foreign binary in the artifact.
|
|
116
|
+
if (via.length > 0 &&
|
|
117
|
+
target !== undefined &&
|
|
118
|
+
!(allows(manifest.os, target.os) && allows(manifest.cpu, target.cpu))) {
|
|
119
|
+
if (!skippedForTarget.includes(name))
|
|
120
|
+
skippedForTarget.push(name);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const existing = packages.get(dest);
|
|
124
|
+
if (existing !== undefined) {
|
|
125
|
+
if (existing.dir !== dir) {
|
|
126
|
+
throw new Error(`gjsify ship: two installed copies of \`${name}\` would both be staged at node_modules/${dest} ` +
|
|
127
|
+
`(${existing.dir} and ${dir}).`);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
packages.set(dest, { name, version: manifest.version ?? '0.0.0', dir, dest });
|
|
132
|
+
const chain = [...via, name];
|
|
133
|
+
for (const dep of Object.keys(manifest.dependencies ?? {}))
|
|
134
|
+
visit(dep, found, chain, false);
|
|
135
|
+
for (const dep of Object.keys(manifest.optionalDependencies ?? {}))
|
|
136
|
+
visit(dep, found, chain, true);
|
|
137
|
+
};
|
|
138
|
+
for (const name of names)
|
|
139
|
+
visit(name, cwd, [], false);
|
|
140
|
+
return {
|
|
141
|
+
packages: [...packages.values()].sort((a, b) => (a.dest < b.dest ? -1 : 1)),
|
|
142
|
+
skippedOptional: skippedOptional.sort(),
|
|
143
|
+
skippedForTarget: skippedForTarget.sort(),
|
|
144
|
+
excluded: excluded.sort(),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Every file of one package, package-relative, without the `node_modules` directories inside it —
|
|
149
|
+
* those are separate packages of the closure, or not wanted at all.
|
|
150
|
+
*/
|
|
151
|
+
function listPackageFiles(root) {
|
|
152
|
+
const out = [];
|
|
153
|
+
const walk = (current) => {
|
|
154
|
+
for (const entry of readdirSync(current, { withFileTypes: true }).sort((a, b) => (a.name < b.name ? -1 : 1))) {
|
|
155
|
+
const full = join(current, entry.name);
|
|
156
|
+
if (entry.isDirectory()) {
|
|
157
|
+
if (entry.name !== 'node_modules')
|
|
158
|
+
walk(full);
|
|
159
|
+
}
|
|
160
|
+
else if (entry.isSymbolicLink()) {
|
|
161
|
+
throw new Error(`gjsify ship: ${full} is a symlink. The payload has to be self-contained — a package that ` +
|
|
162
|
+
'links inside itself needs the link replaced by the file.');
|
|
163
|
+
}
|
|
164
|
+
else if (entry.isFile()) {
|
|
165
|
+
out.push(posix.join(...relative(root, full).split(sep)));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
walk(root);
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
/** 0755 for what the source marks executable or is a shared library, 0644 for the rest. */
|
|
173
|
+
function modeOf(rel, abs) {
|
|
174
|
+
if (isExecutableAsset(rel))
|
|
175
|
+
return 0o755;
|
|
176
|
+
return (statSync(abs).mode & 0o111) !== 0 ? 0o755 : 0o644;
|
|
177
|
+
}
|
|
178
|
+
/** The closure as stage-relative files, under `<bundleDir>/node_modules/`. */
|
|
179
|
+
export function stageNodeModules(closure, bundleDir) {
|
|
180
|
+
const files = [];
|
|
181
|
+
for (const pkg of closure.packages) {
|
|
182
|
+
for (const rel of listPackageFiles(pkg.dir)) {
|
|
183
|
+
const abs = join(pkg.dir, ...rel.split('/'));
|
|
184
|
+
files.push({
|
|
185
|
+
path: posix.join(bundleDir, 'node_modules', pkg.dest, rel),
|
|
186
|
+
mode: modeOf(rel, abs),
|
|
187
|
+
source: { kind: 'file', path: abs },
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return files;
|
|
192
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.64.
|
|
3
|
+
"version": "0.64.10",
|
|
4
4
|
"description": "CLI for Gjsify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -142,22 +142,22 @@
|
|
|
142
142
|
"cli"
|
|
143
143
|
],
|
|
144
144
|
"dependencies": {
|
|
145
|
-
"@gjsify/blueprint": "^0.64.
|
|
146
|
-
"@gjsify/buffer": "^0.64.
|
|
147
|
-
"@gjsify/create-app": "^0.64.
|
|
148
|
-
"@gjsify/node-globals": "^0.64.
|
|
149
|
-
"@gjsify/node-polyfills": "^0.64.
|
|
150
|
-
"@gjsify/npm-registry": "^0.64.
|
|
151
|
-
"@gjsify/resolve-npm": "^0.64.
|
|
152
|
-
"@gjsify/rolldown-plugin-gjsify": "^0.64.
|
|
153
|
-
"@gjsify/vite-plugin-blueprint": "^0.64.
|
|
154
|
-
"@gjsify/rolldown-plugin-pnp": "^0.64.
|
|
155
|
-
"@gjsify/semver": "^0.64.
|
|
156
|
-
"@gjsify/tar": "^0.64.
|
|
157
|
-
"@gjsify/tsc": "^0.64.
|
|
158
|
-
"@gjsify/utils": "^0.64.
|
|
159
|
-
"@gjsify/web-polyfills": "^0.64.
|
|
160
|
-
"@gjsify/workspace": "^0.64.
|
|
145
|
+
"@gjsify/blueprint": "^0.64.10",
|
|
146
|
+
"@gjsify/buffer": "^0.64.10",
|
|
147
|
+
"@gjsify/create-app": "^0.64.10",
|
|
148
|
+
"@gjsify/node-globals": "^0.64.10",
|
|
149
|
+
"@gjsify/node-polyfills": "^0.64.10",
|
|
150
|
+
"@gjsify/npm-registry": "^0.64.10",
|
|
151
|
+
"@gjsify/resolve-npm": "^0.64.10",
|
|
152
|
+
"@gjsify/rolldown-plugin-gjsify": "^0.64.10",
|
|
153
|
+
"@gjsify/vite-plugin-blueprint": "^0.64.10",
|
|
154
|
+
"@gjsify/rolldown-plugin-pnp": "^0.64.10",
|
|
155
|
+
"@gjsify/semver": "^0.64.10",
|
|
156
|
+
"@gjsify/tar": "^0.64.10",
|
|
157
|
+
"@gjsify/tsc": "^0.64.10",
|
|
158
|
+
"@gjsify/utils": "^0.64.10",
|
|
159
|
+
"@gjsify/web-polyfills": "^0.64.10",
|
|
160
|
+
"@gjsify/workspace": "^0.64.10",
|
|
161
161
|
"acorn": "^8.17.0",
|
|
162
162
|
"cosmiconfig": "^9.0.2",
|
|
163
163
|
"get-tsconfig": "^4.14.0",
|
|
@@ -166,13 +166,13 @@
|
|
|
166
166
|
"yargs": "^18.0.0"
|
|
167
167
|
},
|
|
168
168
|
"devDependencies": {
|
|
169
|
-
"@gjsify/unit": "^0.64.
|
|
169
|
+
"@gjsify/unit": "^0.64.10",
|
|
170
170
|
"@types/yargs": "^17.0.35",
|
|
171
171
|
"typescript": "^6.0.3"
|
|
172
172
|
},
|
|
173
173
|
"peerDependencies": {
|
|
174
|
-
"@gjsify/oxfmt-native": "^0.64.
|
|
175
|
-
"@gjsify/rolldown-native": "^0.64.
|
|
174
|
+
"@gjsify/oxfmt-native": "^0.64.10",
|
|
175
|
+
"@gjsify/rolldown-native": "^0.64.10"
|
|
176
176
|
},
|
|
177
177
|
"peerDependenciesMeta": {
|
|
178
178
|
"@gjsify/oxfmt-native": {
|