@gjsify/cli 0.4.35 → 0.4.36
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 +85 -75
- package/lib/bundler-pick.js +43 -4
- package/lib/commands/affected.d.ts +10 -0
- package/lib/commands/affected.js +303 -0
- package/lib/commands/build.js +1 -1
- package/lib/commands/dlx.js +8 -1
- package/lib/commands/index.d.ts +3 -0
- package/lib/commands/index.js +3 -0
- package/lib/commands/install.d.ts +3 -0
- package/lib/commands/install.js +324 -77
- package/lib/commands/publish.js +36 -35
- package/lib/commands/tsc.d.ts +6 -0
- package/lib/commands/tsc.js +109 -0
- package/lib/commands/whoami.d.ts +7 -0
- package/lib/commands/whoami.js +118 -0
- package/lib/commands/workspace.d.ts +4 -0
- package/lib/commands/workspace.js +159 -32
- package/lib/index.js +4 -1
- package/lib/utils/install-backend-native.js +58 -15
- package/lib/utils/install-backend.d.ts +19 -0
- package/lib/utils/install-backend.js +1 -0
- package/lib/utils/install-progress.d.ts +26 -0
- package/lib/utils/install-progress.js +109 -0
- package/lib/utils/install-tarball-cache.d.ts +23 -0
- package/lib/utils/install-tarball-cache.js +140 -0
- package/lib/utils/load-npmrc.d.ts +14 -0
- package/lib/utils/load-npmrc.js +61 -0
- package/lib/utils/publish-diagnose.d.ts +38 -0
- package/lib/utils/publish-diagnose.js +99 -0
- package/lib/utils/resolve-npm-package.d.ts +21 -0
- package/lib/utils/resolve-npm-package.js +121 -0
- package/package.json +29 -18
package/lib/bundler-pick.js
CHANGED
|
@@ -26,6 +26,7 @@ import { promises as fs } from 'node:fs';
|
|
|
26
26
|
import * as path from 'node:path';
|
|
27
27
|
import { createRequire } from 'node:module';
|
|
28
28
|
import { pathToFileURL } from 'node:url';
|
|
29
|
+
import { resolveNpmPackage } from './utils/resolve-npm-package.js';
|
|
29
30
|
// npm `rolldown` is a Rust crate with platform-specific prebuilds; loading
|
|
30
31
|
// it eagerly at module init pulls musl-detection code that does
|
|
31
32
|
// `require('node:fs')` synchronously — fine on Node, but fatal under GJS
|
|
@@ -36,9 +37,41 @@ async function loadNpmRolldown() {
|
|
|
36
37
|
// Indirect specifier so Rolldown's static-analysis doesn't try to
|
|
37
38
|
// bundle the npm crate into a GJS target build.
|
|
38
39
|
const specifier = 'rolldown';
|
|
39
|
-
const
|
|
40
|
+
const target = resolveImportTargetForGjs(specifier);
|
|
41
|
+
const mod = (await import(/* @vite-ignore */ target));
|
|
40
42
|
return mod.rolldown;
|
|
41
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Convert a bare npm specifier into something dynamic `import(...)` can
|
|
46
|
+
* load regardless of host runtime:
|
|
47
|
+
*
|
|
48
|
+
* - Node has a native node_modules resolver — return the specifier
|
|
49
|
+
* unchanged.
|
|
50
|
+
* - GJS's native ESM loader has no node_modules walker, so we resolve
|
|
51
|
+
* the specifier through multiple `createRequire` anchors (cwd,
|
|
52
|
+
* workspace root, bundle URL, parent-dir walk, `GJSIFY_NODE_PATH`)
|
|
53
|
+
* and dynamic-import the resulting `file://` URL.
|
|
54
|
+
*
|
|
55
|
+
* Falls back to the bare specifier when every anchor misses so the
|
|
56
|
+
* host runtime's loader surfaces its native error path instead of a
|
|
57
|
+
* silent synth from this helper.
|
|
58
|
+
*
|
|
59
|
+
* The bundle-URL anchor (`import.meta.url`) is critical for the case
|
|
60
|
+
* where the install lives next to the bundle but the user invokes
|
|
61
|
+
* `gjs -m <install>/dist/cli.gjs.mjs build …` from a completely
|
|
62
|
+
* unrelated cwd — without it, the createRequire walk anchored at the
|
|
63
|
+
* cwd's `node_modules` chain misses, and we'd throw `Module not found:
|
|
64
|
+
* rolldown` even though the package is present under the install dir.
|
|
65
|
+
*/
|
|
66
|
+
function resolveImportTargetForGjs(specifier) {
|
|
67
|
+
const isGjs = typeof globalThis.imports?.gi !== 'undefined';
|
|
68
|
+
if (!isGjs)
|
|
69
|
+
return specifier;
|
|
70
|
+
const resolved = resolveNpmPackage(specifier, { bundleUrl: import.meta.url });
|
|
71
|
+
if (resolved)
|
|
72
|
+
return pathToFileURL(resolved).href;
|
|
73
|
+
return specifier;
|
|
74
|
+
}
|
|
42
75
|
/**
|
|
43
76
|
* In-memory bundle used by `--globals auto` for AST-driven detection.
|
|
44
77
|
* Mirrors the shape of `AnalysisBundler` in
|
|
@@ -99,7 +132,8 @@ export async function runWatch(finalOpts) {
|
|
|
99
132
|
'under Node (`node lib/index.js build … --watch`) or set `GJSIFY_BUNDLER=npm`.');
|
|
100
133
|
}
|
|
101
134
|
const specifier = 'rolldown';
|
|
102
|
-
const
|
|
135
|
+
const target = resolveImportTargetForGjs(specifier);
|
|
136
|
+
const mod = (await import(/* @vite-ignore */ target));
|
|
103
137
|
const output = finalOpts.output ?? {};
|
|
104
138
|
return mod.watch({ ...finalOpts, output });
|
|
105
139
|
}
|
|
@@ -170,8 +204,13 @@ async function tryLoadNative() {
|
|
|
170
204
|
const specifier = '@gjsify/rolldown-native';
|
|
171
205
|
let target = specifier;
|
|
172
206
|
if (isGjs) {
|
|
173
|
-
|
|
174
|
-
|
|
207
|
+
// Same multi-anchor resolution as `loadNpmRolldown` —
|
|
208
|
+
// when the bundle is invoked from a cwd outside the
|
|
209
|
+
// install dir, anchoring solely at `import.meta.url`
|
|
210
|
+
// misses node_modules layouts where the user's cwd
|
|
211
|
+
// or the workspace root carries the package instead.
|
|
212
|
+
const resolved = resolveNpmPackage(specifier, { bundleUrl: import.meta.url }) ??
|
|
213
|
+
createRequire(import.meta.url).resolve(specifier);
|
|
175
214
|
target = pathToFileURL(resolved).href;
|
|
176
215
|
}
|
|
177
216
|
const mod = (await import(/* @vite-ignore */ target));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Command } from '../types/index.js';
|
|
2
|
+
interface AffectedOptions {
|
|
3
|
+
base?: string;
|
|
4
|
+
head?: string;
|
|
5
|
+
format: 'text' | 'json' | 'globs' | 'github-actions';
|
|
6
|
+
'changed-from-stdin': boolean;
|
|
7
|
+
cwd?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const affectedCommand: Command<unknown, AffectedOptions>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// `gjsify affected --base <sha> [--head <ref>] [--format=...]`
|
|
2
|
+
//
|
|
3
|
+
// Diffs the working tree against `<base>` and emits the set of workspaces
|
|
4
|
+
// that are AFFECTED by the change set — i.e. seeds plus everything that
|
|
5
|
+
// transitively depends on them. CI uses the output as the `--include`
|
|
6
|
+
// filter for `gjsify foreach test`, so a typical single-package PR ends
|
|
7
|
+
// up running the touched workspace + its downstream consumers instead of
|
|
8
|
+
// the whole monorepo.
|
|
9
|
+
//
|
|
10
|
+
// Classifier table (first-match wins) handles the cases that aren't a
|
|
11
|
+
// straightforward "file lives in workspace X":
|
|
12
|
+
//
|
|
13
|
+
// 1. GLOBAL_TRIGGERS — change touches infra the classifier itself
|
|
14
|
+
// depends on (`@gjsify/workspace`, `@gjsify/cli`, the bundler
|
|
15
|
+
// plugins, the lockfile, root tsconfig / package.json, this very
|
|
16
|
+
// workflow file). Emits `global=true` → CI must run the full
|
|
17
|
+
// suite. We can't trust the closure when the algorithm that
|
|
18
|
+
// computes it just changed.
|
|
19
|
+
//
|
|
20
|
+
// 2. IGNORE — pure-docs / website / refs/ submodule / unrelated
|
|
21
|
+
// workflow files. Discard; do not contribute to seeds.
|
|
22
|
+
//
|
|
23
|
+
// 3. TEST_ONLY — every changed file under ONE workspace is a spec
|
|
24
|
+
// file, e2e fixture, or integration test. Seed = that workspace
|
|
25
|
+
// but SKIP the closure expansion (downstream consumers don't care
|
|
26
|
+
// about test code changes).
|
|
27
|
+
//
|
|
28
|
+
// 4. CODE (default) — `workspacesForChangedFiles` maps file → ws,
|
|
29
|
+
// then `affectedClosure` walks reverse-dep edges.
|
|
30
|
+
//
|
|
31
|
+
// Integration tests are gated separately: they run when any workspace
|
|
32
|
+
// in the closure appears as a `dependencies` entry of any
|
|
33
|
+
// `@gjsify/integration-*` workspace, OR on a `globalTrigger`. Same for
|
|
34
|
+
// e2e: any infra change OR explicit `tests/e2e/**` touch turns the e2e
|
|
35
|
+
// gate on.
|
|
36
|
+
//
|
|
37
|
+
// Output formats:
|
|
38
|
+
//
|
|
39
|
+
// --format=text (default) human-readable summary
|
|
40
|
+
// --format=json { global, workspaces[], runIntegration, runE2E, skipAll, reason }
|
|
41
|
+
// --format=globs one `@gjsify/<name>` per line
|
|
42
|
+
// --format=github-actions $GITHUB_OUTPUT key=value lines
|
|
43
|
+
//
|
|
44
|
+
// `--changed-from-stdin` skips `git diff` entirely and reads a newline-
|
|
45
|
+
// separated list of paths from stdin. Useful for local debugging and
|
|
46
|
+
// for the spec suite.
|
|
47
|
+
import { spawnSync } from 'node:child_process';
|
|
48
|
+
import { readFileSync } from 'node:fs';
|
|
49
|
+
import { discoverWorkspaces, buildDependencyGraph, buildReverseDependencyGraph, affectedClosure, workspacesForChangedFiles, } from '@gjsify/workspace';
|
|
50
|
+
import { findWorkspaceRoot } from '../utils/workspace-root.js';
|
|
51
|
+
export const affectedCommand = {
|
|
52
|
+
command: 'affected',
|
|
53
|
+
description: 'Classify changed files against the workspace tree and print the set of workspaces affected (seeds + transitive dependents). Designed for CI to gate `gjsify foreach test --include …` so unrelated workspaces are not re-tested on every PR.',
|
|
54
|
+
builder: (yargs) => yargs
|
|
55
|
+
.option('base', {
|
|
56
|
+
description: 'Diff base. Default: `origin/main`. Resolved via `git rev-parse`. On a PR set this to `${{ github.event.pull_request.base.sha }}`.',
|
|
57
|
+
type: 'string',
|
|
58
|
+
})
|
|
59
|
+
.option('head', {
|
|
60
|
+
description: 'Diff head. Default: `HEAD`.',
|
|
61
|
+
type: 'string',
|
|
62
|
+
default: 'HEAD',
|
|
63
|
+
})
|
|
64
|
+
.option('format', {
|
|
65
|
+
description: 'Output shape.',
|
|
66
|
+
choices: ['text', 'json', 'globs', 'github-actions'],
|
|
67
|
+
default: 'text',
|
|
68
|
+
})
|
|
69
|
+
.option('changed-from-stdin', {
|
|
70
|
+
description: 'Skip `git diff`. Read a newline-separated list of repo-relative paths from stdin instead. Lets callers — tests, ad-hoc scripts — control the input exactly.',
|
|
71
|
+
type: 'boolean',
|
|
72
|
+
default: false,
|
|
73
|
+
})
|
|
74
|
+
.option('cwd', {
|
|
75
|
+
description: 'Workspace root. Default: discovered from `process.cwd()`.',
|
|
76
|
+
type: 'string',
|
|
77
|
+
}),
|
|
78
|
+
handler: async (args) => {
|
|
79
|
+
const rootDir = args.cwd ?? findWorkspaceRoot(process.cwd()) ?? process.cwd();
|
|
80
|
+
const workspaces = discoverWorkspaces(rootDir, { includeRoot: true });
|
|
81
|
+
const changedFiles = args['changed-from-stdin']
|
|
82
|
+
? readStdinLines()
|
|
83
|
+
: runGitDiff(rootDir, args.base ?? 'origin/main', args.head ?? 'HEAD');
|
|
84
|
+
const result = classifyAndExpand(workspaces, rootDir, changedFiles);
|
|
85
|
+
emit(args.format, result);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
/** Patterns that force a full run. First-match wins; order is intentional. */
|
|
89
|
+
const GLOBAL_TRIGGERS = [
|
|
90
|
+
// The classifier itself + everything in its plumbing.
|
|
91
|
+
/^packages\/infra\/workspace\//,
|
|
92
|
+
/^packages\/infra\/cli\//,
|
|
93
|
+
/^packages\/infra\/rolldown-plugin-gjsify\//,
|
|
94
|
+
/^packages\/infra\/resolve-npm\//,
|
|
95
|
+
// Cross-cutting dep + lockfile + root config.
|
|
96
|
+
/^gjsify-lock\.json$/,
|
|
97
|
+
/^package\.json$/,
|
|
98
|
+
/^tsconfig[^/]*\.json$/,
|
|
99
|
+
// The workflow file itself — a job-shape change is invisible until
|
|
100
|
+
// the workflow re-runs, so a path-filtered job can't safely apply
|
|
101
|
+
// the new shape to an in-flight PR.
|
|
102
|
+
/^\.github\/workflows\/main\.yml$/,
|
|
103
|
+
/^scripts\/audit-runtimes\.mjs$/,
|
|
104
|
+
];
|
|
105
|
+
/** Patterns that contribute no seed and don't force a full run. */
|
|
106
|
+
const IGNORE = [
|
|
107
|
+
/\.md$/i,
|
|
108
|
+
/^refs\//,
|
|
109
|
+
/^website\//,
|
|
110
|
+
/^docs\//,
|
|
111
|
+
/^\.github\/workflows\/(deploy-docs|commitlint|release|audit-runtimes|prebuilds)\.yml$/,
|
|
112
|
+
/^\.githooks\//,
|
|
113
|
+
/^LICENSE/,
|
|
114
|
+
/^\.gitignore$/,
|
|
115
|
+
/^\.gjsify-[^/]*\.md$/,
|
|
116
|
+
/^STATUS\.md$/,
|
|
117
|
+
/^CHANGELOG\.md$/,
|
|
118
|
+
/^AGENTS\.md$/,
|
|
119
|
+
/^CLAUDE\.md$/,
|
|
120
|
+
/^README\.md$/,
|
|
121
|
+
];
|
|
122
|
+
/** Patterns that suggest a test-only change. */
|
|
123
|
+
const TEST_PATHS = [/\.spec\.[mc]?[tj]sx?$/, /^tests\/(e2e|integration)\//];
|
|
124
|
+
function classifyAndExpand(workspaces, rootDir, changedFiles) {
|
|
125
|
+
const files = changedFiles.map((f) => f.replace(/\\/g, '/')).filter((f) => f.length > 0);
|
|
126
|
+
if (files.length === 0) {
|
|
127
|
+
return {
|
|
128
|
+
global: false,
|
|
129
|
+
reason: 'empty-diff',
|
|
130
|
+
workspaces: [],
|
|
131
|
+
runE2E: false,
|
|
132
|
+
runIntegration: false,
|
|
133
|
+
skipAll: true,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
// Global triggers short-circuit immediately.
|
|
137
|
+
for (const f of files) {
|
|
138
|
+
for (const re of GLOBAL_TRIGGERS) {
|
|
139
|
+
if (re.test(f)) {
|
|
140
|
+
return {
|
|
141
|
+
global: true,
|
|
142
|
+
reason: `global-trigger ${re.source} matched ${f}`,
|
|
143
|
+
workspaces: workspaces.map((w) => w.name),
|
|
144
|
+
runE2E: true,
|
|
145
|
+
runIntegration: true,
|
|
146
|
+
skipAll: false,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Drop ignored files; collect the remainder.
|
|
152
|
+
const remaining = [];
|
|
153
|
+
for (const f of files) {
|
|
154
|
+
if (IGNORE.some((re) => re.test(f)))
|
|
155
|
+
continue;
|
|
156
|
+
remaining.push(f);
|
|
157
|
+
}
|
|
158
|
+
if (remaining.length === 0) {
|
|
159
|
+
return {
|
|
160
|
+
global: false,
|
|
161
|
+
reason: 'ignored-only',
|
|
162
|
+
workspaces: [],
|
|
163
|
+
runE2E: false,
|
|
164
|
+
runIntegration: false,
|
|
165
|
+
skipAll: true,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// Map files → workspaces. Files outside any workspace stay in `unmatched`.
|
|
169
|
+
const { matched, unmatched } = workspacesForChangedFiles(workspaces, rootDir, remaining);
|
|
170
|
+
// Unmatched-but-not-ignored files are suspicious enough to fall back to
|
|
171
|
+
// the conservative "full run" path. Examples: a new top-level dotfile,
|
|
172
|
+
// a script in `scripts/` we haven't carved out, a refs/-adjacent file.
|
|
173
|
+
if (unmatched.length > 0) {
|
|
174
|
+
return {
|
|
175
|
+
global: true,
|
|
176
|
+
reason: `unmatched files (${unmatched.length}): ${unmatched.slice(0, 3).join(', ')}${unmatched.length > 3 ? '…' : ''}`,
|
|
177
|
+
workspaces: workspaces.map((w) => w.name),
|
|
178
|
+
runE2E: true,
|
|
179
|
+
runIntegration: true,
|
|
180
|
+
skipAll: false,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
// TEST_ONLY shortcut: every remaining file is a spec / e2e / integration
|
|
184
|
+
// path, all under ONE workspace. Skip the closure expansion — test code
|
|
185
|
+
// has no downstream consumers.
|
|
186
|
+
const testOnly = remaining.every((f) => TEST_PATHS.some((re) => re.test(f)));
|
|
187
|
+
if (testOnly && matched.size === 1) {
|
|
188
|
+
const only = [...matched][0];
|
|
189
|
+
// E2E or integration test-only changes still need their own job.
|
|
190
|
+
const touchedE2E = remaining.some((f) => f.startsWith('tests/e2e/'));
|
|
191
|
+
const touchedIntegration = remaining.some((f) => f.startsWith('tests/integration/'));
|
|
192
|
+
return {
|
|
193
|
+
global: false,
|
|
194
|
+
reason: `test-only (${remaining.length} file(s) in ${only})`,
|
|
195
|
+
workspaces: [only],
|
|
196
|
+
runE2E: touchedE2E,
|
|
197
|
+
runIntegration: touchedIntegration || isIntegrationWorkspace(only),
|
|
198
|
+
skipAll: false,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// Default: closure walk.
|
|
202
|
+
const reverse = buildReverseDependencyGraph(workspaces, { includeDev: true });
|
|
203
|
+
const closure = affectedClosure(reverse, [...matched]);
|
|
204
|
+
// Integration suites whose forward deps overlap with the closure also
|
|
205
|
+
// need to run. We walk the forward graph and pull any integration ws
|
|
206
|
+
// that depends on something inside the closure.
|
|
207
|
+
const forward = buildDependencyGraph(workspaces, { includeDev: true });
|
|
208
|
+
let runIntegration = false;
|
|
209
|
+
for (const [from, deps] of forward.edges) {
|
|
210
|
+
if (!isIntegrationWorkspace(from))
|
|
211
|
+
continue;
|
|
212
|
+
for (const dep of deps) {
|
|
213
|
+
if (closure.has(dep)) {
|
|
214
|
+
closure.add(from);
|
|
215
|
+
runIntegration = true;
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const runE2E = remaining.some((f) => f.startsWith('tests/e2e/'));
|
|
221
|
+
return {
|
|
222
|
+
global: false,
|
|
223
|
+
reason: `closure (${closure.size} ws from ${matched.size} seed(s))`,
|
|
224
|
+
workspaces: [...closure].sort(),
|
|
225
|
+
runE2E,
|
|
226
|
+
runIntegration,
|
|
227
|
+
skipAll: false,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function isIntegrationWorkspace(name) {
|
|
231
|
+
return name.startsWith('@gjsify/integration-');
|
|
232
|
+
}
|
|
233
|
+
// ─── git diff + stdin ──────────────────────────────────────────────────────
|
|
234
|
+
function runGitDiff(cwd, base, head) {
|
|
235
|
+
// `git diff --name-only base...head` lists changed paths on `head`
|
|
236
|
+
// relative to the merge-base. That matches what GitHub PR diffs show
|
|
237
|
+
// and survives stacked PRs without picking up commits from base.
|
|
238
|
+
const r = spawnSync('git', ['diff', '--name-only', `${base}...${head}`], {
|
|
239
|
+
cwd,
|
|
240
|
+
encoding: 'utf8',
|
|
241
|
+
});
|
|
242
|
+
if (r.status !== 0) {
|
|
243
|
+
// Surface a clear error — caller (CI) will fall back to full run
|
|
244
|
+
// via `continue-on-error: true` on the classify step.
|
|
245
|
+
process.stderr.write(`gjsify affected: git diff failed (${r.status}): ${r.stderr.trim()}\n`);
|
|
246
|
+
process.exit(2);
|
|
247
|
+
}
|
|
248
|
+
return r.stdout.split('\n').filter(Boolean);
|
|
249
|
+
}
|
|
250
|
+
function readStdinLines() {
|
|
251
|
+
const data = readFileSync(0, 'utf8');
|
|
252
|
+
return data.split('\n').map((s) => s.trim()).filter(Boolean);
|
|
253
|
+
}
|
|
254
|
+
// ─── Output ────────────────────────────────────────────────────────────────
|
|
255
|
+
function emit(format, r) {
|
|
256
|
+
if (format === 'json') {
|
|
257
|
+
process.stdout.write(JSON.stringify(r) + '\n');
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (format === 'globs') {
|
|
261
|
+
for (const name of r.workspaces)
|
|
262
|
+
process.stdout.write(`${name}\n`);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (format === 'github-actions') {
|
|
266
|
+
const includeArgs = r.global
|
|
267
|
+
? ''
|
|
268
|
+
: r.workspaces.map((n) => `--include '${escSingleQuote(n)}'`).join(' ');
|
|
269
|
+
const out = process.env.GITHUB_OUTPUT;
|
|
270
|
+
const lines = [
|
|
271
|
+
`skip-all=${r.skipAll}`,
|
|
272
|
+
`global=${r.global}`,
|
|
273
|
+
`include-args=${includeArgs}`,
|
|
274
|
+
`run-integration=${r.runIntegration}`,
|
|
275
|
+
`run-e2e=${r.runE2E}`,
|
|
276
|
+
`reason=${r.reason}`,
|
|
277
|
+
];
|
|
278
|
+
if (out) {
|
|
279
|
+
// GitHub Actions: append to $GITHUB_OUTPUT.
|
|
280
|
+
const { appendFileSync } = require('node:fs');
|
|
281
|
+
for (const l of lines)
|
|
282
|
+
appendFileSync(out, `${l}\n`);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
for (const l of lines)
|
|
286
|
+
process.stdout.write(`${l}\n`);
|
|
287
|
+
}
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
// text (default)
|
|
291
|
+
process.stdout.write(`affected:\n`);
|
|
292
|
+
process.stdout.write(` reason: ${r.reason}\n`);
|
|
293
|
+
process.stdout.write(` global: ${r.global}\n`);
|
|
294
|
+
process.stdout.write(` skip-all: ${r.skipAll}\n`);
|
|
295
|
+
process.stdout.write(` run-integration: ${r.runIntegration}\n`);
|
|
296
|
+
process.stdout.write(` run-e2e: ${r.runE2E}\n`);
|
|
297
|
+
process.stdout.write(` workspaces (${r.workspaces.length}):\n`);
|
|
298
|
+
for (const name of r.workspaces)
|
|
299
|
+
process.stdout.write(` - ${name}\n`);
|
|
300
|
+
}
|
|
301
|
+
function escSingleQuote(s) {
|
|
302
|
+
return s.replace(/'/g, `'\\''`);
|
|
303
|
+
}
|
package/lib/commands/build.js
CHANGED
|
@@ -41,7 +41,7 @@ export const buildCommand = {
|
|
|
41
41
|
.option('app', {
|
|
42
42
|
description: 'Use this if you want to build an application, the platform node is usually only used for tests',
|
|
43
43
|
type: 'string',
|
|
44
|
-
choices: ['gjs', 'node', 'browser'],
|
|
44
|
+
choices: ['gjs', 'node', 'browser', 'nativescript'],
|
|
45
45
|
normalize: true,
|
|
46
46
|
default: 'gjs',
|
|
47
47
|
})
|
package/lib/commands/dlx.js
CHANGED
|
@@ -14,7 +14,7 @@ import { runGjsBundle } from '../utils/run-gjs.js';
|
|
|
14
14
|
import { parseSpec } from '../utils/parse-spec.js';
|
|
15
15
|
import { resolveGjsEntry } from '../utils/resolve-gjs-entry.js';
|
|
16
16
|
import { cacheDirFor, createCacheKey, getValidCachedPkg, makePrepareDir, resolveInstalledPkgDir, symlinkSwap, } from '../utils/dlx-cache.js';
|
|
17
|
-
import { installPackages } from '../utils/install-backend.js';
|
|
17
|
+
import { installPackages, makeProgressReporter } from '../utils/install-backend.js';
|
|
18
18
|
export const dlxCommand = {
|
|
19
19
|
command: 'dlx <spec> [binOrArg] [extraArgs..]',
|
|
20
20
|
description: 'Run the GJS bundle of an npm-published package without installing it locally.',
|
|
@@ -108,6 +108,12 @@ async function ensurePkgDir(parsed, opts) {
|
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
110
|
const prepareDir = makePrepareDir(cacheDir);
|
|
111
|
+
// First-run dlx (cache miss) downloads & extracts the package + its tree.
|
|
112
|
+
// For `npx @gjsify/cli showcase excalibur-jelly-jumper` and similar first-
|
|
113
|
+
// contact entry points the user otherwise sees no feedback for 10+ seconds
|
|
114
|
+
// (cold packument fetch + tarball extract + prebuild detection); the
|
|
115
|
+
// progress reporter renders a live bar via stderr.
|
|
116
|
+
const progress = makeProgressReporter({ enabled: !opts.verbose });
|
|
111
117
|
await installPackages({
|
|
112
118
|
prefix: prepareDir,
|
|
113
119
|
specs: [parsed.spec],
|
|
@@ -118,6 +124,7 @@ async function ensurePkgDir(parsed, opts) {
|
|
|
118
124
|
// and lets `--frozen` short-circuit the resolver entirely.
|
|
119
125
|
lockfile: true,
|
|
120
126
|
frozen: opts.frozen,
|
|
127
|
+
progress,
|
|
121
128
|
});
|
|
122
129
|
const liveTarget = symlinkSwap(cacheDir, prepareDir);
|
|
123
130
|
return {
|
package/lib/commands/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * from './foreach.js';
|
|
|
16
16
|
export * from './workspace.js';
|
|
17
17
|
export * from './pack.js';
|
|
18
18
|
export * from './publish.js';
|
|
19
|
+
export * from './whoami.js';
|
|
19
20
|
export * from './self-update.js';
|
|
20
21
|
export * from './generate-installer.js';
|
|
21
22
|
export * from './uninstall.js';
|
|
@@ -24,3 +25,5 @@ export * from './lint.js';
|
|
|
24
25
|
export * from './fix.js';
|
|
25
26
|
export * from './upgrade.js';
|
|
26
27
|
export * from './barrels.js';
|
|
28
|
+
export * from './tsc.js';
|
|
29
|
+
export * from './affected.js';
|
package/lib/commands/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export * from './foreach.js';
|
|
|
16
16
|
export * from './workspace.js';
|
|
17
17
|
export * from './pack.js';
|
|
18
18
|
export * from './publish.js';
|
|
19
|
+
export * from './whoami.js';
|
|
19
20
|
export * from './self-update.js';
|
|
20
21
|
export * from './generate-installer.js';
|
|
21
22
|
export * from './uninstall.js';
|
|
@@ -24,3 +25,5 @@ export * from './lint.js';
|
|
|
24
25
|
export * from './fix.js';
|
|
25
26
|
export * from './upgrade.js';
|
|
26
27
|
export * from './barrels.js';
|
|
28
|
+
export * from './tsc.js';
|
|
29
|
+
export * from './affected.js';
|
|
@@ -7,7 +7,10 @@ interface InstallOptions {
|
|
|
7
7
|
'save-optional'?: boolean;
|
|
8
8
|
immutable?: boolean;
|
|
9
9
|
verbose: boolean;
|
|
10
|
+
quiet?: boolean;
|
|
11
|
+
progress?: boolean;
|
|
10
12
|
backend?: 'native' | 'npm';
|
|
13
|
+
timeout: number;
|
|
11
14
|
}
|
|
12
15
|
export declare const installCommand: Command<unknown, InstallOptions>;
|
|
13
16
|
export {};
|