@gjsify/cli 0.26.1 → 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/dist/cli.gjs.mjs +184 -182
- package/lib/cli-app.js +3 -1
- package/lib/commands/affected.js +1 -1
- package/lib/commands/build.js +21 -14
- package/lib/commands/check.js +40 -4
- package/lib/commands/clear.d.ts +8 -0
- package/lib/commands/clear.js +90 -0
- package/lib/commands/copy.d.ts +8 -0
- package/lib/commands/copy.js +87 -0
- package/lib/commands/dlx.js +8 -1
- package/lib/commands/index.d.ts +2 -0
- package/lib/commands/index.js +2 -0
- package/lib/commands/install.d.ts +4 -0
- package/lib/commands/install.js +164 -15
- package/lib/commands/onboard.js +17 -14
- package/lib/commands/publish.js +18 -4
- package/lib/commands/storybook.js +5 -1
- package/lib/commands/test.js +29 -2
- package/lib/commands/uninstall.js +44 -2
- package/lib/commands/upgrade.js +1 -4
- package/lib/templates/install.mjs.tmpl +110 -19
- package/lib/utils/bin-shim.d.ts +73 -0
- package/lib/utils/bin-shim.js +97 -2
- package/lib/utils/build-cache.d.ts +7 -27
- package/lib/utils/build-cache.js +27 -2
- package/lib/utils/check-system-deps.d.ts +15 -1
- package/lib/utils/check-system-deps.js +126 -2
- package/lib/utils/clear-targets.d.ts +30 -0
- package/lib/utils/clear-targets.js +114 -0
- package/lib/utils/copy-targets.d.ts +41 -0
- package/lib/utils/copy-targets.js +145 -0
- package/lib/utils/detect-native-packages.d.ts +160 -5
- package/lib/utils/detect-native-packages.js +326 -50
- package/lib/utils/dir-link.d.ts +34 -6
- package/lib/utils/dir-link.js +23 -9
- package/lib/utils/install-backend-native.d.ts +109 -1
- package/lib/utils/install-backend-native.js +622 -59
- package/lib/utils/install-backend.js +17 -15
- package/lib/utils/install-global.js +6 -34
- package/lib/utils/install-lock.js +1 -1
- package/lib/utils/install-packument-cache.d.ts +36 -17
- package/lib/utils/install-packument-cache.js +59 -31
- package/lib/utils/oxc-resolve.d.ts +27 -1
- package/lib/utils/oxc-resolve.js +53 -26
- package/lib/utils/pkg-json.d.ts +24 -0
- package/lib/utils/pkg-json.js +27 -1
- package/lib/utils/platform-check.d.ts +215 -0
- package/lib/utils/platform-check.js +377 -0
- package/lib/utils/resolve-npm-package.js +9 -2
- package/lib/utils/run-gjs.d.ts +1 -1
- package/lib/utils/run-gjs.js +19 -3
- package/lib/utils/spawn.js +50 -2
- package/lib/utils/win32-command.d.ts +54 -0
- package/lib/utils/win32-command.js +140 -0
- package/package.json +19 -20
package/lib/cli-app.js
CHANGED
|
@@ -19,7 +19,7 @@ import { readFileSync } from 'node:fs';
|
|
|
19
19
|
import { dirname, join } from 'node:path';
|
|
20
20
|
import { fileURLToPath } from 'node:url';
|
|
21
21
|
import yargs from 'yargs';
|
|
22
|
-
import { buildCommand as build, testCommand as test, runCommand as run, infoCommand as info, systemCheckCommand as systemCheck, checkCommand as check, showcaseCommand as showcase, createCommand as create, gresourceCommand as gresource, gettextCommand as gettext, gsettingsCommand as gsettings, flatpakCommand as flatpak, dlxCommand as dlx, installCommand as install, foreachCommand as foreach, workspaceCommand as workspace, packCommand as pack, publishCommand as publish, whoamiCommand as whoami, loginCommand as login, logoutCommand as logout, trustCommand as trust, onboardCommand as onboard, selfUpdateCommand as selfUpdate, generateInstallerCommand as generateInstaller, uninstallCommand as uninstall, formatCommand as format, lintCommand as lint, fixCommand as fix, upgradeCommand as upgrade, barrelsCommand as barrels, tscCommand as tsc, affectedCommand as affected, storybookCommand as storybook, debugCommand as debug, browseCommand as browse, } from './commands/index.js';
|
|
22
|
+
import { buildCommand as build, clearCommand as clear, copyCommand as copy, testCommand as test, runCommand as run, infoCommand as info, systemCheckCommand as systemCheck, checkCommand as check, showcaseCommand as showcase, createCommand as create, gresourceCommand as gresource, gettextCommand as gettext, gsettingsCommand as gsettings, flatpakCommand as flatpak, dlxCommand as dlx, installCommand as install, foreachCommand as foreach, workspaceCommand as workspace, packCommand as pack, publishCommand as publish, whoamiCommand as whoami, loginCommand as login, logoutCommand as logout, trustCommand as trust, onboardCommand as onboard, selfUpdateCommand as selfUpdate, generateInstallerCommand as generateInstaller, uninstallCommand as uninstall, formatCommand as format, lintCommand as lint, fixCommand as fix, upgradeCommand as upgrade, barrelsCommand as barrels, tscCommand as tsc, affectedCommand as affected, storybookCommand as storybook, debugCommand as debug, browseCommand as browse, } from './commands/index.js';
|
|
23
23
|
import { APP_NAME } from './constants.js';
|
|
24
24
|
import { isBun, isDeno, isNode, gjsSystemVersion } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
25
25
|
import { ensureGjsifyShimOnPath } from './utils/gjsify-shim.js';
|
|
@@ -89,6 +89,8 @@ export async function runCli(argv) {
|
|
|
89
89
|
.command(create.command, create.description, create.builder, create.handler)
|
|
90
90
|
.command(install.command, install.description, install.builder, install.handler)
|
|
91
91
|
.command(build.command, build.description, build.builder, build.handler)
|
|
92
|
+
.command(clear.command, clear.description, clear.builder, clear.handler)
|
|
93
|
+
.command(copy.command, copy.description, copy.builder, copy.handler)
|
|
92
94
|
.command(test.command, test.description, test.builder, test.handler)
|
|
93
95
|
.command(run.command, run.description, run.builder, run.handler)
|
|
94
96
|
.command(dlx.command, dlx.description, dlx.builder, dlx.handler)
|
package/lib/commands/affected.js
CHANGED
|
@@ -199,7 +199,7 @@ const OTHER_WORKFLOW_INPUTS = [
|
|
|
199
199
|
// Each sibling workflow's own definition. `main.yml` is deliberately
|
|
200
200
|
// absent — it is a GLOBAL_TRIGGER, since a job-shape change there is
|
|
201
201
|
// invisible until the workflow re-runs.
|
|
202
|
-
/^\.github\/workflows\/(deploy-docs|commitlint|release|release-cut|audit-runtimes|prebuilds|node-gi|napi|cli-cross-platform|build-ci-image)\.yml$/,
|
|
202
|
+
/^\.github\/workflows\/(deploy-docs|commitlint|release|release-cut|audit-runtimes|prebuilds|node-gi|napi|cli-cross-platform|build-ci-image|cancel-pr-runs)\.yml$/,
|
|
203
203
|
// `prebuilds.yml`'s toolchain (#838): the QEMU-emulated build script and
|
|
204
204
|
// the `changed-packages` classifier that decides which native packages a
|
|
205
205
|
// prebuild run rebuilds. `prebuilds.yml` lists `.github/prebuild-toolchain/**`
|
package/lib/commands/build.js
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
import { Config } from '../config.js';
|
|
2
2
|
import { BuildAction } from '../actions/build.js';
|
|
3
|
+
// NO `normalize: true` ON THE GLOB-BEARING OPTIONS — `entry-points` and
|
|
4
|
+
// `exclude` are PATTERNS, not paths.
|
|
5
|
+
//
|
|
6
|
+
// yargs' `normalize` runs `path.normalize()` over every value, which on win32
|
|
7
|
+
// rewrites separators: `src/**/*.{ts,js}` becomes `src\**\*.{ts,js}`. Those
|
|
8
|
+
// patterns go to fast-glob, whose own source says why that must not happen
|
|
9
|
+
// ("we cannot use the standard `path.normalize` method, because on Windows
|
|
10
|
+
// platform it will use of backslashes") — and the failure is not a clean
|
|
11
|
+
// no-match. With no `/` left, fast-glob finds no static base, walks from `.`,
|
|
12
|
+
// follows the workspace's relative `node_modules/@gjsify/*` symlinks with
|
|
13
|
+
// unbounded depth and no cycle detection, and dies of heap exhaustion after
|
|
14
|
+
// tens of minutes without writing one output file. That is issue #914's
|
|
15
|
+
// "`--library` is pathologically slow" report, in full: every `build:gjsify`
|
|
16
|
+
// script in the tree passes its entry points as globs. See
|
|
17
|
+
// `rolldown-plugin-gjsify/src/utils/entry-points.ts` for the second layer.
|
|
18
|
+
//
|
|
19
|
+
// The flag had also been copied onto twelve options that are not paths at all
|
|
20
|
+
// (booleans, `choices` enums, comma-separated identifier lists). Normalizing
|
|
21
|
+
// those is meaningless, and it is what made the one place it is harmful look
|
|
22
|
+
// like house style. `outfile`/`outdir` keep it: they ARE single paths, and
|
|
23
|
+
// nothing globs them.
|
|
3
24
|
export const buildCommand = {
|
|
4
25
|
command: 'build [entryPoints..]',
|
|
5
26
|
description: 'Build and bundle your Gjs project',
|
|
@@ -9,7 +30,6 @@ export const buildCommand = {
|
|
|
9
30
|
description: 'The entry points you want to bundle. Defaults to bundler.input from package.json#gjsify or .gjsifyrc.js, falling back to src/index.ts when neither is set.',
|
|
10
31
|
array: true,
|
|
11
32
|
type: 'string',
|
|
12
|
-
normalize: true,
|
|
13
33
|
// No yargs `default` here on purpose. A yargs default value
|
|
14
34
|
// is indistinguishable from "user passed the flag" in the
|
|
15
35
|
// parsed args (cliArgs.entryPoints?.length is truthy either
|
|
@@ -29,20 +49,17 @@ export const buildCommand = {
|
|
|
29
49
|
description: 'An array of glob patterns to exclude entry-points and aliases',
|
|
30
50
|
array: true,
|
|
31
51
|
type: 'string',
|
|
32
|
-
normalize: true,
|
|
33
52
|
default: [],
|
|
34
53
|
})
|
|
35
54
|
.option('verbose', {
|
|
36
55
|
description: 'Switch on the verbose mode',
|
|
37
56
|
type: 'boolean',
|
|
38
|
-
normalize: true,
|
|
39
57
|
default: false,
|
|
40
58
|
})
|
|
41
59
|
.option('app', {
|
|
42
60
|
description: 'Build target for an application. Defaults to the target of the HOST runtime running the CLI: gjs when run under gjs, node when run under node/bun/deno (both consume the --app node bundle). Set `gjsify.app` in package.json#gjsify to override the host default.',
|
|
43
61
|
type: 'string',
|
|
44
62
|
choices: ['gjs', 'node', 'browser', 'nativescript'],
|
|
45
|
-
normalize: true,
|
|
46
63
|
// No yargs `default: 'gjs'` — a yargs default is
|
|
47
64
|
// indistinguishable from a user-set value and would clobber
|
|
48
65
|
// `package.json#gjsify.app` in config.ts (same footgun as
|
|
@@ -55,18 +72,15 @@ export const buildCommand = {
|
|
|
55
72
|
description: 'Override the default output format',
|
|
56
73
|
type: 'string',
|
|
57
74
|
choices: ['iife', 'esm', 'cjs'],
|
|
58
|
-
normalize: true,
|
|
59
75
|
})
|
|
60
76
|
.option('minify', {
|
|
61
77
|
description: 'Minify the bundled output. Defaults to true; use --no-minify to emit pretty-printed code (e.g. for debugging or readable bundle review).',
|
|
62
78
|
type: 'boolean',
|
|
63
|
-
normalize: true,
|
|
64
79
|
defaultDescription: 'true',
|
|
65
80
|
})
|
|
66
81
|
.option('library', {
|
|
67
82
|
description: 'Use this if you want to build a library for Gjsify',
|
|
68
83
|
type: 'boolean',
|
|
69
|
-
normalize: true,
|
|
70
84
|
default: false,
|
|
71
85
|
})
|
|
72
86
|
.option('outfile', {
|
|
@@ -85,26 +99,22 @@ export const buildCommand = {
|
|
|
85
99
|
alias: 'r',
|
|
86
100
|
description: "Enables TypeScript types on runtime using Deepkit's type compiler",
|
|
87
101
|
type: 'boolean',
|
|
88
|
-
normalize: true,
|
|
89
102
|
default: false,
|
|
90
103
|
})
|
|
91
104
|
.option('log-level', {
|
|
92
105
|
description: 'The log level can be changed to prevent esbuild from printing warning and/or error messages to the terminal',
|
|
93
106
|
type: 'string',
|
|
94
107
|
choices: ['silent', 'error', 'warning', 'info', 'debug', 'verbose'],
|
|
95
|
-
normalize: true,
|
|
96
108
|
default: 'warning',
|
|
97
109
|
})
|
|
98
110
|
.option('console-shim', {
|
|
99
111
|
description: 'Inject a console shim into GJS builds for clean output without the GLib prefix and with working ANSI colors. Use --no-console-shim to disable. Only applies to GJS app builds.',
|
|
100
112
|
type: 'boolean',
|
|
101
|
-
normalize: true,
|
|
102
113
|
default: true,
|
|
103
114
|
})
|
|
104
115
|
.option('globals', {
|
|
105
116
|
description: "Comma-separated list of global identifiers, 'auto' (default) to detect automatically from the bundled output, or 'none' to disable. The 'auto' token may be combined with explicit identifiers/groups (e.g. 'auto,dom') for cases where the detector cannot statically see a global because it's accessed via indirection. Each identifier is mapped to the corresponding `@gjsify/<pkg>/register` module and injected into the bundle. See the CLI Reference docs for the full list of known identifiers. Only applies to GJS app builds.",
|
|
106
117
|
type: 'string',
|
|
107
|
-
normalize: true,
|
|
108
118
|
// No yargs `default: 'auto'` — a yargs default is
|
|
109
119
|
// indistinguishable from a user-set value and would always
|
|
110
120
|
// clobber `package.json#gjsify.globals` / `.gjsifyrc.*` in
|
|
@@ -115,7 +125,6 @@ export const buildCommand = {
|
|
|
115
125
|
.option('shebang', {
|
|
116
126
|
description: 'Prepend a target-appropriate shebang to the output and mark it executable (chmod 755): `#!/usr/bin/env -S gjs -m` for --app gjs, `#!/usr/bin/env node` for --app node. Applies to GJS and Node app builds with a single --outfile. Default: false (use --shebang to enable, or set `shebang: true` in `.gjsifyrc.js`).',
|
|
117
127
|
type: 'boolean',
|
|
118
|
-
normalize: true,
|
|
119
128
|
})
|
|
120
129
|
.option('external', {
|
|
121
130
|
description: 'Module names that should NOT be bundled — EXACT package names only, matched literally on both bundler engines (no globs). Repeat the flag or pass a comma-separated list (e.g. --external typedoc,prettier). Appended to the per-app built-in externals; register subpaths (`<pkg>/register[/…]`) are always force-inlined for --app gjs regardless of this flag.',
|
|
@@ -146,13 +155,11 @@ export const buildCommand = {
|
|
|
146
155
|
.option('exclude-globals', {
|
|
147
156
|
description: 'Comma-separated global identifiers to remove from auto-detection results. Use for false positives from dead browser-compat code whose polyfills require unavailable native libraries (e.g. --exclude-globals fetch,XMLHttpRequest).',
|
|
148
157
|
type: 'string',
|
|
149
|
-
normalize: true,
|
|
150
158
|
})
|
|
151
159
|
.option('watch', {
|
|
152
160
|
alias: 'w',
|
|
153
161
|
description: 'Watch source files and rebuild on change. Logs each rebuild with duration; clean SIGINT shutdown. Only valid with --app gjs|node|browser (rejected with --library). Requires the npm `rolldown` engine — run under Node, not the GJS-bundled CLI.',
|
|
154
162
|
type: 'boolean',
|
|
155
|
-
normalize: true,
|
|
156
163
|
default: false,
|
|
157
164
|
});
|
|
158
165
|
},
|
package/lib/commands/check.js
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
// — if both `system-check`-style flags (`--json`) and `check`-style scripts
|
|
15
15
|
// are reachable here, the `--json` flag routes through this command first
|
|
16
16
|
// (since the typescript-check binding is positional `[paths..]`).
|
|
17
|
-
import { spawnSync } from 'node:child_process';
|
|
18
17
|
import { prefixLines } from '../utils/prefixed-output.js';
|
|
19
18
|
import { spawnToCompletion } from '../utils/spawn.js';
|
|
20
19
|
import { readPackageJson } from '../utils/pkg-json.js';
|
|
@@ -22,6 +21,7 @@ import { join } from 'node:path';
|
|
|
22
21
|
import { cpus } from 'node:os';
|
|
23
22
|
import { discoverWorkspaces, filterWorkspaces } from '@gjsify/workspace';
|
|
24
23
|
import { findWorkspaceRoot } from '../utils/workspace-root.js';
|
|
24
|
+
import { detectPackageManager } from './workspace.js';
|
|
25
25
|
/**
|
|
26
26
|
* Run a single workspace's `check` script. Returns the exit code; non-zero
|
|
27
27
|
* indicates failure. Output is forwarded to the parent's stdout/stderr,
|
|
@@ -33,8 +33,25 @@ async function runCheck(ws, prefix) {
|
|
|
33
33
|
// GJS, so the STREAMING async path is safe here (and required: the
|
|
34
34
|
// blocking one would serialize the parallel workers). See utils/spawn.ts.
|
|
35
35
|
const flushers = [];
|
|
36
|
+
// The RUNNER is detected, never hardcoded — `foreach` and `workspace` have
|
|
37
|
+
// always done this and this site did not, which made `gjsify check` the one
|
|
38
|
+
// orchestrator that still required Node. Under GJS `detectPackageManager()`
|
|
39
|
+
// returns `gjsify` and the script goes through the shim on PATH.
|
|
40
|
+
//
|
|
41
|
+
// It failed in the worst possible shape: `spawnToCompletion` maps the
|
|
42
|
+
// `spawn npm ENOENT` into the rejection the `catch` below turns into a bare
|
|
43
|
+
// `exit 1`, so a Node-less host reported `✗ <pkg> (exit 1)` for a check
|
|
44
|
+
// script that never ran — measured on postmarketOS/aarch64 against a
|
|
45
|
+
// package whose `check` was literally `echo`.
|
|
46
|
+
const runner = detectPackageManager();
|
|
47
|
+
// `--if-present` is an npm/yarn flag with no `gjsify run` equivalent, so for
|
|
48
|
+
// the GJS runner the absence test moves here rather than being dropped.
|
|
49
|
+
if (runner === 'gjsify' && readPackageJson(join(ws.location, 'package.json'))?.scripts?.check === undefined) {
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
const argv = runner === 'gjsify' ? ['run', 'check'] : ['run', 'check', '--if-present'];
|
|
36
53
|
try {
|
|
37
|
-
const { code } = await spawnToCompletion(
|
|
54
|
+
const { code } = await spawnToCompletion(runner, argv, {
|
|
38
55
|
completion: 'exit',
|
|
39
56
|
cwd: ws.location,
|
|
40
57
|
stdio: prefix === null ? 'inherit' : 'pipe',
|
|
@@ -111,11 +128,30 @@ export const checkCommand = {
|
|
|
111
128
|
if (args.verbose) {
|
|
112
129
|
console.log(`[check] cwd=${cwd} → npm run check`);
|
|
113
130
|
}
|
|
114
|
-
|
|
131
|
+
// Through `spawnToCompletion`, like workspace mode above and
|
|
132
|
+
// like every other spawn in the CLI. This site kept a raw
|
|
133
|
+
// `spawnSync('npm', …)` when the six wrappers were consolidated
|
|
134
|
+
// (see utils/spawn.ts), and that made it the last place where
|
|
135
|
+
// `npm` was handed to `spawn` as a bare name — ENOENT on
|
|
136
|
+
// Windows, where npm is a `.cmd` shim `CreateProcess` never
|
|
137
|
+
// finds. Reading only `r.status` then turned that into
|
|
138
|
+
// `process.exit(1)` with NO diagnostic at all: `gjsify check`
|
|
139
|
+
// inside any package failed silently on win32. Measured there
|
|
140
|
+
// before the change; `--verbose` printed the command line and
|
|
141
|
+
// nothing else.
|
|
142
|
+
// Runner detected, not hardcoded — same reason as workspace mode
|
|
143
|
+
// above. This branch already established that the package HAS a
|
|
144
|
+
// check script, so there is no `--if-present` to translate.
|
|
145
|
+
const { code } = await spawnToCompletion(detectPackageManager(), ['run', 'check'], {
|
|
146
|
+
cwd,
|
|
147
|
+
// The handler exits the process on every path below, so the
|
|
148
|
+
// streaming path is safe under GJS too.
|
|
149
|
+
completion: 'exit',
|
|
150
|
+
});
|
|
115
151
|
// `return` — a bare `process.exit()` is deferred under GJS (no
|
|
116
152
|
// atexit), so execution fell through into workspace mode and
|
|
117
153
|
// re-checked the whole monorepo after the single-package run.
|
|
118
|
-
return process.exit(
|
|
154
|
+
return process.exit(code ?? 1);
|
|
119
155
|
}
|
|
120
156
|
// Fall through to workspace mode when the local package has no
|
|
121
157
|
// check script (cd'd into a non-package dir under the root).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// `gjsify clear <paths…>` — portable recursive delete for package `clear` scripts.
|
|
2
|
+
//
|
|
3
|
+
// WHY THIS EXISTS
|
|
4
|
+
//
|
|
5
|
+
// Every `clear` script in this workspace was `rm -rf <dirs…> || exit 0`. npm runs
|
|
6
|
+
// package scripts through `cmd.exe` on Windows, which has neither `rm` nor that
|
|
7
|
+
// syntax, so `gjsify foreach clear` — and with it the documented full gate
|
|
8
|
+
// (`gjsify install --immutable && gjsify run clear && gjsify run build …`) —
|
|
9
|
+
// could not run there at all. 224 package.json files carried that line, so the
|
|
10
|
+
// fix is one portable implementation, not 224 hand-written platform branches
|
|
11
|
+
// (issue #914, finding 3).
|
|
12
|
+
//
|
|
13
|
+
// `force: true` already ignores a missing path, which is all the `|| exit 0` tail
|
|
14
|
+
// ever did — so the rewritten scripts drop it and stop swallowing REAL failures.
|
|
15
|
+
// A permission error used to exit 0 and read as a successful clean.
|
|
16
|
+
//
|
|
17
|
+
// THE BOOTSTRAP QUESTION, ANSWERED DELIBERATELY
|
|
18
|
+
//
|
|
19
|
+
// A `clear` script that calls `gjsify` makes cleaning depend on a built CLI —
|
|
20
|
+
// the same circularity `gjsify run chmod` had in the CLI's own build (removed in
|
|
21
|
+
// ce47b6a). It is acceptable HERE, and for a stronger reason than "you cannot
|
|
22
|
+
// clear a tree you never built": for the single-command case there is no second
|
|
23
|
+
// process at all. `gjsify run clear` recognises a script that is one
|
|
24
|
+
// `gjsify <subcommand>` and dispatches it IN-PROCESS (see `cli-app.ts`), so the
|
|
25
|
+
// nested-gjs cost the chmod case paid does not arise. `gjsify foreach clear`
|
|
26
|
+
// still spawns one child per package, exactly as it did for `rm -rf`, and the
|
|
27
|
+
// root script already passes `-p`.
|
|
28
|
+
//
|
|
29
|
+
// What it does mean: `clear` is a NEW command, so it does not exist in the
|
|
30
|
+
// committed `dist/cli.gjs.mjs` until that bundle is rebuilt. On a checkout
|
|
31
|
+
// carrying a stale bundle, `gjsify run clear` fails with "Unknown command" rather
|
|
32
|
+
// than silently clearing nothing — which is the honest failure of the two.
|
|
33
|
+
import { rm, stat } from 'node:fs/promises';
|
|
34
|
+
import { readdirSync } from 'node:fs';
|
|
35
|
+
import { relative } from 'node:path';
|
|
36
|
+
import { resolveClearTargets } from '../utils/clear-targets.js';
|
|
37
|
+
export const clearCommand = {
|
|
38
|
+
command: 'clear [paths..]',
|
|
39
|
+
description: 'Delete build output recursively. Portable replacement for `rm -rf` in package `clear` scripts.',
|
|
40
|
+
// No `normalize: true` anywhere below — see the comment block in
|
|
41
|
+
// `commands/build.ts`. It is meaningless for a path `fs.rm` resolves itself,
|
|
42
|
+
// and on win32 it is what would backslash a wildcard segment.
|
|
43
|
+
builder: (yargs) => {
|
|
44
|
+
return yargs
|
|
45
|
+
.positional('paths', {
|
|
46
|
+
description: 'Files and directories to delete, relative to the current package. A missing path is not an error. `*` and `?` are honoured in the last segment.',
|
|
47
|
+
type: 'string',
|
|
48
|
+
array: true,
|
|
49
|
+
})
|
|
50
|
+
.option('dry-run', {
|
|
51
|
+
description: 'Print what would be deleted and exit without touching anything.',
|
|
52
|
+
type: 'boolean',
|
|
53
|
+
default: false,
|
|
54
|
+
})
|
|
55
|
+
.option('verbose', {
|
|
56
|
+
alias: 'v',
|
|
57
|
+
description: 'Print each path as it is deleted.',
|
|
58
|
+
type: 'boolean',
|
|
59
|
+
default: false,
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
handler: async (args) => {
|
|
63
|
+
const cwd = process.cwd();
|
|
64
|
+
const paths = args.paths ?? [];
|
|
65
|
+
if (paths.length === 0) {
|
|
66
|
+
throw new Error('gjsify clear: no paths given. Usage: gjsify clear <paths…>');
|
|
67
|
+
}
|
|
68
|
+
// Resolution validates every target and throws on anything outside the
|
|
69
|
+
// package — before a single delete runs.
|
|
70
|
+
const targets = resolveClearTargets(paths, { cwd, readdir: readdirSync });
|
|
71
|
+
for (const target of targets) {
|
|
72
|
+
const shown = relative(cwd, target);
|
|
73
|
+
if (args.dryRun) {
|
|
74
|
+
const present = await stat(target).then(() => true, () => false);
|
|
75
|
+
console.log(`gjsify clear: would remove ${shown}${present ? '' : ' (absent)'}`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
// `maxRetries` is for Windows: a file another process still holds
|
|
79
|
+
// open fails there with EBUSY/EPERM instead of being unlinked while
|
|
80
|
+
// open as on POSIX — and clearing `@gjsify/cli` itself deletes the
|
|
81
|
+
// `lib/`+`dist/` the running process was loaded from. Both runtimes
|
|
82
|
+
// read a module fully and close it, so this covers a transient lock
|
|
83
|
+
// (a scanner, an editor), not a genuinely held handle; if it does
|
|
84
|
+
// fail, the error names the path instead of exiting 0.
|
|
85
|
+
await rm(target, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
86
|
+
if (args.verbose)
|
|
87
|
+
console.log(`gjsify clear: removed ${shown}`);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// `gjsify copy <sources…> <dest>` — portable file staging for package scripts.
|
|
2
|
+
//
|
|
3
|
+
// WHY THIS EXISTS
|
|
4
|
+
//
|
|
5
|
+
// The sibling of `gjsify clear`, and the second half of the same finding. npm
|
|
6
|
+
// runs package scripts through `cmd.exe` on Windows, which has no `cp`, no
|
|
7
|
+
// `mkdir -p` and expands no glob. `clear` closed the 224 `rm -rf` scripts
|
|
8
|
+
// (issue #914); what remained were 34 scripts that stage static assets with
|
|
9
|
+
// `mkdir -p X && cp -r …` — `build:assets` (24), `build:public` (5), and one
|
|
10
|
+
// each of `build:views`, `sync:theme`, `prebuild:test`, `prebuild:test:fixtures`.
|
|
11
|
+
//
|
|
12
|
+
// They were not merely a hygiene item. `showcases/node/express-webserver` is the
|
|
13
|
+
// one showcase that runs on Windows at all — its `--app node` bundle needs no
|
|
14
|
+
// gjs — and `build:public` is what puts `index.html` and `style.css` next to it.
|
|
15
|
+
// Without this command the showcase builds and then serves 404s for its own
|
|
16
|
+
// frontend, which is a worse failure than not building: the server starts.
|
|
17
|
+
//
|
|
18
|
+
// WHAT IT IS NOT
|
|
19
|
+
//
|
|
20
|
+
// Not a `cp` clone. Two deliberate differences, both because a build step is
|
|
21
|
+
// re-run:
|
|
22
|
+
//
|
|
23
|
+
// - `mkdir -p` is implied. Every one of the replaced scripts paired the two,
|
|
24
|
+
// and a copy that fails because the output directory does not exist yet is
|
|
25
|
+
// not a distinction worth preserving.
|
|
26
|
+
// - directory-vs-path is decided by the ARGUMENTS, never by what is on disk —
|
|
27
|
+
// see `isDirectoryDestination`. `cp -r src/assets dist/res` means two
|
|
28
|
+
// different things on the first and second run; this does not.
|
|
29
|
+
//
|
|
30
|
+
// Overwriting is unconditional (`force: true`), which is what the `-f` in most
|
|
31
|
+
// of the replaced scripts asked for and what the rest wanted without saying so.
|
|
32
|
+
import { cp, mkdir } from 'node:fs/promises';
|
|
33
|
+
import { readdirSync } from 'node:fs';
|
|
34
|
+
import { dirname, relative } from 'node:path';
|
|
35
|
+
import { planCopy } from '../utils/copy-targets.js';
|
|
36
|
+
export const copyCommand = {
|
|
37
|
+
command: 'copy [paths..]',
|
|
38
|
+
description: 'Copy files and directories into the build output. Portable replacement for `mkdir -p` + `cp -r` in package scripts.',
|
|
39
|
+
// No `normalize: true` — see the comment block in `commands/build.ts`. It is
|
|
40
|
+
// meaningless for a path resolved here anyway, and on win32 it is what would
|
|
41
|
+
// backslash a wildcard segment before it can be matched.
|
|
42
|
+
builder: (yargs) => {
|
|
43
|
+
return yargs
|
|
44
|
+
.positional('paths', {
|
|
45
|
+
description: 'One or more sources followed by the destination. The destination is a DIRECTORY when it ends with "/", when several sources are given, or when a source carries a wildcard; otherwise it is the exact target path. Missing parent directories are created. `*` and `?` are honoured in the last segment of a source.',
|
|
46
|
+
type: 'string',
|
|
47
|
+
array: true,
|
|
48
|
+
})
|
|
49
|
+
.option('dry-run', {
|
|
50
|
+
description: 'Print what would be copied and exit without touching anything.',
|
|
51
|
+
type: 'boolean',
|
|
52
|
+
default: false,
|
|
53
|
+
})
|
|
54
|
+
.option('verbose', {
|
|
55
|
+
alias: 'v',
|
|
56
|
+
description: 'Print each path as it is copied.',
|
|
57
|
+
type: 'boolean',
|
|
58
|
+
default: false,
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
handler: async (args) => {
|
|
62
|
+
const cwd = process.cwd();
|
|
63
|
+
const paths = args.paths ?? [];
|
|
64
|
+
if (paths.length < 2) {
|
|
65
|
+
throw new Error('gjsify copy: needs at least one source and a destination. Usage: gjsify copy <sources…> <dest>');
|
|
66
|
+
}
|
|
67
|
+
// Planning validates every argument — and every destination against the
|
|
68
|
+
// package boundary — before a single byte is written.
|
|
69
|
+
const ops = planCopy(paths, { cwd, readdir: readdirSync });
|
|
70
|
+
const show = (p) => {
|
|
71
|
+
const rel = relative(cwd, p);
|
|
72
|
+
// A source may legitimately live in a sibling package; showing it as
|
|
73
|
+
// `../../../packages/…` is more useful than an absolute path there.
|
|
74
|
+
return rel === '' ? '.' : rel;
|
|
75
|
+
};
|
|
76
|
+
for (const { from, to } of ops) {
|
|
77
|
+
if (args.dryRun) {
|
|
78
|
+
console.log(`gjsify copy: would copy ${show(from)} -> ${show(to)}`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
await mkdir(dirname(to), { recursive: true });
|
|
82
|
+
await cp(from, to, { recursive: true, force: true });
|
|
83
|
+
if (args.verbose)
|
|
84
|
+
console.log(`gjsify copy: ${show(from)} -> ${show(to)}`);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
package/lib/commands/dlx.js
CHANGED
|
@@ -89,7 +89,14 @@ export const dlxCommand = {
|
|
|
89
89
|
const { binName, extraArgs } = splitBinAndArgs(pkgDir, args.binOrArg, extraArgsCombined);
|
|
90
90
|
const entry = resolveGjsEntry(pkgDir, binName);
|
|
91
91
|
if (entry.fromFallback) {
|
|
92
|
-
|
|
92
|
+
// Name the key that is actually missing. The condition is
|
|
93
|
+
// `resolveGjsEntry` finding no `gjsify.main` (or no matching
|
|
94
|
+
// `gjsify.bin`), but the text said "no `gjsify` field" — and two
|
|
95
|
+
// showcases DO carry a `gjsify` object (tier + example) without a
|
|
96
|
+
// `main`, so the warning sent the reader looking for a field that
|
|
97
|
+
// was right there. Measured running `gjsify showcase
|
|
98
|
+
// webrtc-loopback` on a Node-less device.
|
|
99
|
+
console.warn(`[gjsify dlx] package "${cachedPkgName ?? parsed.kind}" declares no \`gjsify.main\` — falling back to package.json#main. Add \`gjsify.main\` to silence.`);
|
|
93
100
|
}
|
|
94
101
|
// Terminal call — exit on success, or the GJS main loop parks this
|
|
95
102
|
// process forever (see RunGjsBundleOptions.exitOnSuccess).
|
package/lib/commands/index.d.ts
CHANGED
package/lib/commands/index.js
CHANGED
|
@@ -12,6 +12,10 @@ interface InstallOptions {
|
|
|
12
12
|
progress?: boolean;
|
|
13
13
|
backend?: 'native' | 'npm';
|
|
14
14
|
timeout: number;
|
|
15
|
+
os?: string;
|
|
16
|
+
cpu?: string;
|
|
17
|
+
libc?: string;
|
|
18
|
+
force?: boolean;
|
|
15
19
|
}
|
|
16
20
|
export declare const installCommand: Command<unknown, InstallOptions>;
|
|
17
21
|
export declare function buildBinShim(wsLocation: string, nodeTarget?: string, gjsTarget?: string, nativePrebuildDirs?: string[], platform?: string, scanRoot?: string): string;
|