@gjsify/cli 0.63.0 → 0.63.1-next.235
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 +30 -23
- package/lib/cli-app.js +2 -1
- package/lib/commands/index.d.ts +1 -0
- package/lib/commands/index.js +1 -0
- package/lib/commands/self-update.d.ts +28 -1
- package/lib/commands/self-update.js +53 -5
- package/lib/commands/site.d.ts +15 -0
- package/lib/commands/site.js +153 -0
- package/lib/commands/upgrade.d.ts +23 -1
- package/lib/commands/upgrade.js +53 -2
- package/package.json +22 -21
package/lib/cli-app.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import yargs from 'yargs';
|
|
19
19
|
import { classifyCliFailure } from './cli-fail.js';
|
|
20
20
|
import { cliVersion } from './utils/publish-headers.js';
|
|
21
|
-
import { buildCommand as build, clearCommand as clear, copyCommand as copy, envCommand as env, execCommand as exec, 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, shipCommand as ship, dlxCommand as dlx, installCommand as install, linkCommand as link, unlinkCommand as unlink, 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, pruneCommand as prune, 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, devCommand as dev, debugCommand as debug, devtoolsCommand as devtools, browseCommand as browse, webextCommand as webext, blueprintCommand as blueprint, } from './commands/index.js';
|
|
21
|
+
import { buildCommand as build, clearCommand as clear, copyCommand as copy, envCommand as env, execCommand as exec, 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, shipCommand as ship, dlxCommand as dlx, installCommand as install, linkCommand as link, unlinkCommand as unlink, 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, pruneCommand as prune, 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, siteCommand as site, devCommand as dev, debugCommand as debug, devtoolsCommand as devtools, browseCommand as browse, webextCommand as webext, blueprintCommand as blueprint, } from './commands/index.js';
|
|
22
22
|
import { APP_NAME } from './constants.js';
|
|
23
23
|
import { isBun, isDeno, isNode, gjsSystemVersion } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
24
24
|
import { ensureGjsifyShimOnPath } from './utils/gjsify-shim.js';
|
|
@@ -133,6 +133,7 @@ export async function runCli(argv) {
|
|
|
133
133
|
.command(tsc.command, tsc.description, tsc.builder, tsc.handler)
|
|
134
134
|
.command(affected.command, affected.description, affected.builder, affected.handler)
|
|
135
135
|
.command(storybook.command, storybook.description, storybook.builder, storybook.handler)
|
|
136
|
+
.command(site.command, site.description, site.builder, site.handler)
|
|
136
137
|
.command(dev.command, dev.description, dev.builder, dev.handler)
|
|
137
138
|
.command(debug.command, debug.description, debug.builder, debug.handler)
|
|
138
139
|
.command(devtools.command, devtools.description, devtools.builder, devtools.handler)
|
package/lib/commands/index.d.ts
CHANGED
package/lib/commands/index.js
CHANGED
|
@@ -2,7 +2,8 @@ import type { Command } from '../types/index.js';
|
|
|
2
2
|
interface SelfUpdateOptions {
|
|
3
3
|
check?: boolean;
|
|
4
4
|
force?: boolean;
|
|
5
|
-
tag
|
|
5
|
+
/** `undefined` is "no explicit --tag", which `nextChannelNotice` must tell apart. */
|
|
6
|
+
tag?: string;
|
|
6
7
|
skipDeps?: boolean;
|
|
7
8
|
prune?: boolean;
|
|
8
9
|
}
|
|
@@ -34,4 +35,30 @@ export declare function verifyPathResolution(opts: {
|
|
|
34
35
|
targetVersion: string;
|
|
35
36
|
platform?: NodeJS.Platform;
|
|
36
37
|
}): PathVerdict;
|
|
38
|
+
/**
|
|
39
|
+
* Is `version` a `next`-channel prerelease — the shape `publish-next` publishes?
|
|
40
|
+
*
|
|
41
|
+
* The channel's scheme is `<x.y.z>-next.<run number>` (ADR 0111), so the test is the
|
|
42
|
+
* prerelease identifier `next`, never a substring: `1.2.3-nextgen.1` is somebody else's
|
|
43
|
+
* preid and `1.2.3-beta.1+next` carries it in build metadata, and neither is this channel.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isNextChannelVersion(version: string | null | undefined): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* The one line a bare `self-update` prints when it is about to move the user OFF the
|
|
48
|
+
* `next` channel — or `null` when it is not.
|
|
49
|
+
*
|
|
50
|
+
* `--tag` is and stays the only mechanism; there is no remembered channel and no
|
|
51
|
+
* `--channel` flag, because a second place to record "which channel am I on" is a second
|
|
52
|
+
* thing that can disagree with the installed version. So the notice is purely derived:
|
|
53
|
+
* running a `next` build plus no explicit `--tag` means the implicit `latest` is a channel
|
|
54
|
+
* change the user did not ask for in words. An explicit `--tag` — even `--tag latest` — is
|
|
55
|
+
* the user saying it, and gets no notice.
|
|
56
|
+
*
|
|
57
|
+
* Pure, so the interesting case is testable without a global install on the channel.
|
|
58
|
+
*/
|
|
59
|
+
export declare function nextChannelNotice(opts: {
|
|
60
|
+
currentVersion: string | null | undefined;
|
|
61
|
+
/** `undefined` means no `--tag` was passed; any string is an explicit choice. */
|
|
62
|
+
requestedTag: string | undefined;
|
|
63
|
+
}): string | null;
|
|
37
64
|
export {};
|
|
@@ -24,6 +24,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
24
24
|
import { dirname, join, posix, resolve, win32 } from 'node:path';
|
|
25
25
|
import { fileURLToPath } from 'node:url';
|
|
26
26
|
import { fetchPackument } from '@gjsify/npm-registry';
|
|
27
|
+
import { parse } from '@gjsify/semver';
|
|
27
28
|
import { installPackages, makeProgressReporter } from '../utils/install-backend.js';
|
|
28
29
|
import { classifyInstall } from '../utils/install-provenance.js';
|
|
29
30
|
import { defaultGlobalLayout, globalLayoutIsDefault, hasBundlerEngineInstalled, installGjsEnginePackages, linkGlobalBins, resolveBinOnPath, } from '../utils/install-global.js';
|
|
@@ -31,6 +32,8 @@ import { pathWithoutSelfShim } from '../utils/gjsify-shim.js';
|
|
|
31
32
|
import { resolveHostPlatform } from '../utils/platform-check.js';
|
|
32
33
|
import { pruneAfterInstall } from '../utils/prune-prefix.js';
|
|
33
34
|
const PACKAGE_NAME = '@gjsify/cli';
|
|
35
|
+
/** What `self-update` installs when no `--tag` is given. Applied in the handler, not by yargs. */
|
|
36
|
+
const DEFAULT_TAG = 'latest';
|
|
34
37
|
export const selfUpdateCommand = {
|
|
35
38
|
command: 'self-update',
|
|
36
39
|
description: `Update the installed ${PACKAGE_NAME} to the latest release (or pinned --tag).`,
|
|
@@ -48,7 +51,10 @@ export const selfUpdateCommand = {
|
|
|
48
51
|
.option('tag', {
|
|
49
52
|
description: 'npm dist-tag or pinned version to install (e.g. `latest`, `next`, `0.5.0`).',
|
|
50
53
|
type: 'string',
|
|
51
|
-
default
|
|
54
|
+
// No yargs `default` on purpose. The effective default is still
|
|
55
|
+
// DEFAULT_TAG, applied in the handler — but a default here collapses
|
|
56
|
+
// "no --tag" and "--tag latest" into one value, and the next-channel
|
|
57
|
+
// notice is about exactly that distinction.
|
|
52
58
|
})
|
|
53
59
|
.option('prune', {
|
|
54
60
|
description: 'After updating, remove packages an earlier install left behind that this host cannot use (foreign os/cpu/libc). Use --no-prune to disable.',
|
|
@@ -93,7 +99,15 @@ export const selfUpdateCommand = {
|
|
|
93
99
|
`If you installed via \`npm install -g\`, remove that and use:\n` +
|
|
94
100
|
` curl -fsSL https://github.com/gjsify/gjsify/releases/latest/download/install.mjs -o /tmp/g.mjs && gjs -m /tmp/g.mjs && rm /tmp/g.mjs`);
|
|
95
101
|
}
|
|
96
|
-
|
|
102
|
+
const tag = args.tag ?? DEFAULT_TAG;
|
|
103
|
+
// A bare `self-update` on a `next` build is a CHANNEL CHANGE, and silence
|
|
104
|
+
// about it is how a user ends up reporting "my prerelease fix disappeared".
|
|
105
|
+
// One line, then proceed — the default is still `latest`, and `--tag next`
|
|
106
|
+
// stays the only mechanism for staying on the channel (ADR 0111).
|
|
107
|
+
const notice = nextChannelNotice({ currentVersion, requestedTag: args.tag });
|
|
108
|
+
if (notice)
|
|
109
|
+
console.log(notice);
|
|
110
|
+
console.log(`Fetching dist-tags for ${PACKAGE_NAME}@${tag} ...`);
|
|
97
111
|
let packument;
|
|
98
112
|
try {
|
|
99
113
|
packument = await fetchPackument(PACKAGE_NAME);
|
|
@@ -103,13 +117,13 @@ export const selfUpdateCommand = {
|
|
|
103
117
|
console.error(`Failed to fetch packument: ${msg}`);
|
|
104
118
|
return process.exit(1);
|
|
105
119
|
}
|
|
106
|
-
const target = resolveTag(packument,
|
|
120
|
+
const target = resolveTag(packument, tag);
|
|
107
121
|
if (!target) {
|
|
108
|
-
console.error(`Unknown dist-tag '${
|
|
122
|
+
console.error(`Unknown dist-tag '${tag}' on ${PACKAGE_NAME}. ` +
|
|
109
123
|
`Known tags: ${Object.keys(packument['dist-tags'] ?? {}).join(', ') || '(none)'}`);
|
|
110
124
|
return process.exit(1);
|
|
111
125
|
}
|
|
112
|
-
console.log(`Latest matching --tag ${
|
|
126
|
+
console.log(`Latest matching --tag ${tag}: v${target}`);
|
|
113
127
|
// A version match is NOT sufficient to declare "up to date": a global
|
|
114
128
|
// GJS install can be on the right `@gjsify/cli` version yet be MISSING
|
|
115
129
|
// the GJS bundler engine (`@gjsify/rolldown-native`) — it's an optional
|
|
@@ -275,6 +289,40 @@ export function verifyPathResolution(opts) {
|
|
|
275
289
|
` this run — open a new shell and check \`${opts.binName} --version\`.`,
|
|
276
290
|
};
|
|
277
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Is `version` a `next`-channel prerelease — the shape `publish-next` publishes?
|
|
294
|
+
*
|
|
295
|
+
* The channel's scheme is `<x.y.z>-next.<run number>` (ADR 0111), so the test is the
|
|
296
|
+
* prerelease identifier `next`, never a substring: `1.2.3-nextgen.1` is somebody else's
|
|
297
|
+
* preid and `1.2.3-beta.1+next` carries it in build metadata, and neither is this channel.
|
|
298
|
+
*/
|
|
299
|
+
export function isNextChannelVersion(version) {
|
|
300
|
+
if (!version)
|
|
301
|
+
return false;
|
|
302
|
+
const parsed = parse(version);
|
|
303
|
+
return (parsed?.prerelease ?? []).some((id) => id === 'next');
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* The one line a bare `self-update` prints when it is about to move the user OFF the
|
|
307
|
+
* `next` channel — or `null` when it is not.
|
|
308
|
+
*
|
|
309
|
+
* `--tag` is and stays the only mechanism; there is no remembered channel and no
|
|
310
|
+
* `--channel` flag, because a second place to record "which channel am I on" is a second
|
|
311
|
+
* thing that can disagree with the installed version. So the notice is purely derived:
|
|
312
|
+
* running a `next` build plus no explicit `--tag` means the implicit `latest` is a channel
|
|
313
|
+
* change the user did not ask for in words. An explicit `--tag` — even `--tag latest` — is
|
|
314
|
+
* the user saying it, and gets no notice.
|
|
315
|
+
*
|
|
316
|
+
* Pure, so the interesting case is testable without a global install on the channel.
|
|
317
|
+
*/
|
|
318
|
+
export function nextChannelNotice(opts) {
|
|
319
|
+
if (opts.requestedTag !== undefined)
|
|
320
|
+
return null;
|
|
321
|
+
if (!isNextChannelVersion(opts.currentVersion))
|
|
322
|
+
return null;
|
|
323
|
+
return (`Note: v${opts.currentVersion} is a \`next\` prerelease; updating without --tag moves you to ` +
|
|
324
|
+
`\`${DEFAULT_TAG}\`. Use \`--tag next\` to stay on the next channel.`);
|
|
325
|
+
}
|
|
278
326
|
/**
|
|
279
327
|
* Resolve the CLI's own `package.json#version`. Walks up from
|
|
280
328
|
* `import.meta.url` (the bundle file under GJS, or `lib/index.js` under
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Command } from '../types/index.js';
|
|
2
|
+
interface SiteCliOptions {
|
|
3
|
+
operation: string;
|
|
4
|
+
root?: string;
|
|
5
|
+
out?: string;
|
|
6
|
+
offline: boolean;
|
|
7
|
+
cache: boolean;
|
|
8
|
+
cacheDir?: string;
|
|
9
|
+
check: boolean;
|
|
10
|
+
package: boolean;
|
|
11
|
+
runtime?: string;
|
|
12
|
+
globals?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const siteCommand: Command<unknown, SiteCliOptions>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// `gjsify site` — render the static landing page of the CURRENT project from its
|
|
2
|
+
// AppStream metainfo, or sync the page catalogues. The work lives in
|
|
3
|
+
// `@gjsify/site`; this is the entry point into it.
|
|
4
|
+
//
|
|
5
|
+
// Why a subcommand and not a `gjsify-site` bin: a bin has to name an
|
|
6
|
+
// interpreter in its shebang, and that would pick the runtime FOR the user. The
|
|
7
|
+
// CLI already runs on whichever runtime it was installed with — gjs after the
|
|
8
|
+
// Linux install script, Node after `npm install -g @gjsify/cli` on
|
|
9
|
+
// Windows/macOS, bun or deno when installed with those — so taking the entry
|
|
10
|
+
// point through the CLI makes the site build inherit that choice instead of
|
|
11
|
+
// overriding it. `@gjsify/site` uses GdkPixbuf for its image work, which is
|
|
12
|
+
// reachable on all four (natively under gjs, through the `@gjsify/node-gi`
|
|
13
|
+
// reverse bridge otherwise).
|
|
14
|
+
//
|
|
15
|
+
// Dispatch follows `gjsify storybook`: a generated entry importing
|
|
16
|
+
// `@gjsify/site` from the CONSUMER's node_modules, bundled `--app gjs|node` and
|
|
17
|
+
// run. That keeps `@gjsify/site` (tier 2) out of this tier-1 package's
|
|
18
|
+
// dependencies, and it is what lowers the `gi://GdkPixbuf` import to the right
|
|
19
|
+
// thing for the runtime in use.
|
|
20
|
+
//
|
|
21
|
+
// The resolved install directory is passed to the child as `--package-dir`. A
|
|
22
|
+
// bundle's `import.meta.url` is the OUTFILE, so the library cannot find its own
|
|
23
|
+
// `po/` and `lib/client/` on its own — only the caller still knows.
|
|
24
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
25
|
+
import { dirname, join, resolve } from 'node:path';
|
|
26
|
+
import { hostRuntime } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
27
|
+
/** The `@gjsify/site` install reachable from `cwd`, walking the node_modules chain upwards. */
|
|
28
|
+
function resolveSitePackage(cwd) {
|
|
29
|
+
let dir = resolve(cwd);
|
|
30
|
+
for (;;) {
|
|
31
|
+
const candidate = join(dir, 'node_modules', '@gjsify', 'site');
|
|
32
|
+
if (existsSync(join(candidate, 'package.json')))
|
|
33
|
+
return candidate;
|
|
34
|
+
const parent = dirname(dir);
|
|
35
|
+
if (parent === dir)
|
|
36
|
+
return undefined;
|
|
37
|
+
dir = parent;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The generated entry. Arguments arrive as ARGV rather than baked in, so the cached bundle serves
|
|
42
|
+
* every invocation — flags change far more often than this does.
|
|
43
|
+
*
|
|
44
|
+
* The exit is spelled per runtime on purpose. Under GJS `process.exit` does not halt and loses its
|
|
45
|
+
* code (the `gjsify storybook` measurement), so the gjs entry uses `system.exit`; both read the
|
|
46
|
+
* user arguments from where their own runtime puts them.
|
|
47
|
+
*/
|
|
48
|
+
function generateEntry(app) {
|
|
49
|
+
const header = `// Auto-generated by \`gjsify site\` — do not edit by hand.\nimport { runSiteCli } from '@gjsify/site';\n`;
|
|
50
|
+
return app === 'gjs'
|
|
51
|
+
? `${header}import system from 'system';\n\nsystem.exit(await runSiteCli(system.programArgs));\n`
|
|
52
|
+
: `${header}\nprocess.exit(await runSiteCli(process.argv.slice(2)));\n`;
|
|
53
|
+
}
|
|
54
|
+
export const siteCommand = {
|
|
55
|
+
command: 'site <operation>',
|
|
56
|
+
description: "Render this project's static landing page from its AppStream metainfo (downloads, screenshots, " +
|
|
57
|
+
'release notes, support links), or sync the page catalogues.',
|
|
58
|
+
builder: (yargs) => yargs
|
|
59
|
+
.positional('operation', {
|
|
60
|
+
description: 'build — render the pages; i18n — update the page catalogues',
|
|
61
|
+
type: 'string',
|
|
62
|
+
choices: ['build', 'i18n'],
|
|
63
|
+
demandOption: true,
|
|
64
|
+
})
|
|
65
|
+
.option('root', {
|
|
66
|
+
description: 'Repository to read from (default: the working directory)',
|
|
67
|
+
type: 'string',
|
|
68
|
+
})
|
|
69
|
+
.option('out', { description: 'Output directory, emptied first (default: <root>/dist)', type: 'string' })
|
|
70
|
+
.option('offline', { description: 'Skip every network call', type: 'boolean', default: false })
|
|
71
|
+
.option('cache', {
|
|
72
|
+
description: 'Use the one-hour HTTP response cache (--no-cache to ignore it)',
|
|
73
|
+
type: 'boolean',
|
|
74
|
+
default: true,
|
|
75
|
+
})
|
|
76
|
+
.option('cache-dir', {
|
|
77
|
+
description: 'Where the response cache and the image renditions live',
|
|
78
|
+
defaultDescription: '<root>/node_modules/.cache/gjsify-site',
|
|
79
|
+
type: 'string',
|
|
80
|
+
})
|
|
81
|
+
.option('check', {
|
|
82
|
+
description: 'i18n: report what is out of date instead of writing it',
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
default: false,
|
|
85
|
+
})
|
|
86
|
+
.option('package', {
|
|
87
|
+
description: "i18n: sync @gjsify/site's own catalogues instead of this project's",
|
|
88
|
+
type: 'boolean',
|
|
89
|
+
default: false,
|
|
90
|
+
})
|
|
91
|
+
.option('runtime', {
|
|
92
|
+
description: 'Runtime to build for and run on: gjs | node | bun | deno (default: the host runtime). ' +
|
|
93
|
+
'node/bun/deno build the same `--app node` bundle and require @gjsify/node-gi in the project.',
|
|
94
|
+
type: 'string',
|
|
95
|
+
choices: ['gjs', 'node', 'bun', 'deno'],
|
|
96
|
+
defaultDescription: 'host runtime (gjs on gjs, else node/bun/deno)',
|
|
97
|
+
})
|
|
98
|
+
.option('globals', { description: 'Value for `gjsify build --globals` (default: auto)', type: 'string' }),
|
|
99
|
+
handler: async (args) => {
|
|
100
|
+
const cwd = process.cwd();
|
|
101
|
+
const packageDir = resolveSitePackage(cwd);
|
|
102
|
+
if (!packageDir) {
|
|
103
|
+
console.error('gjsify site: @gjsify/site is not installed in this project.\n\n' +
|
|
104
|
+
' The generator ships as its own package so a project only pays for it when it\n' +
|
|
105
|
+
' publishes a page. Add it, then run this again:\n\n' +
|
|
106
|
+
' npm install --save-dev @gjsify/site');
|
|
107
|
+
return process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
if (!existsSync(join(packageDir, 'lib', 'client', 'site.js'))) {
|
|
110
|
+
console.error(`gjsify site: ${packageDir} has no built client bundle (lib/client/site.js).\n` +
|
|
111
|
+
' In a source checkout, build the package first: `gjsify workspace site build`.');
|
|
112
|
+
return process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
const runtime = (args.runtime ?? hostRuntime());
|
|
115
|
+
const app = runtime === 'gjs' ? 'gjs' : 'node';
|
|
116
|
+
const cacheDir = join(cwd, 'node_modules', '.cache', 'gjsify-site');
|
|
117
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
118
|
+
const entryPath = join(cacheDir, `entry.${app}.ts`);
|
|
119
|
+
const outPath = join(cacheDir, `site.${app}.mjs`);
|
|
120
|
+
writeFileSync(entryPath, generateEntry(app));
|
|
121
|
+
const { runCli } = await import('../cli-app.js');
|
|
122
|
+
await runCli(['build', entryPath, '--app', app, '--globals', args.globals ?? 'auto', '--outfile', outPath]);
|
|
123
|
+
// RESOLVED values, not the raw argv: the child parses its own flags and
|
|
124
|
+
// cannot know where this CLI found the package.
|
|
125
|
+
const forwarded = [args.operation, '--package-dir', packageDir];
|
|
126
|
+
if (args.root)
|
|
127
|
+
forwarded.push('--root', resolve(cwd, args.root));
|
|
128
|
+
if (args.out)
|
|
129
|
+
forwarded.push('--out', resolve(cwd, args.out));
|
|
130
|
+
if (args.offline)
|
|
131
|
+
forwarded.push('--offline');
|
|
132
|
+
if (!args.cache)
|
|
133
|
+
forwarded.push('--no-cache');
|
|
134
|
+
else if (args.cacheDir)
|
|
135
|
+
forwarded.push('--cache-dir', resolve(cwd, args.cacheDir));
|
|
136
|
+
if (args.check)
|
|
137
|
+
forwarded.push('--check');
|
|
138
|
+
if (args.package)
|
|
139
|
+
forwarded.push('--package');
|
|
140
|
+
if (runtime === 'gjs') {
|
|
141
|
+
const { runGjsBundle } = await import('../utils/run-gjs.js');
|
|
142
|
+
await runGjsBundle(outPath, forwarded, { completion: 'exit', exitOnSuccess: true });
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (runtime === 'node') {
|
|
146
|
+
const { runNodeBundle } = await import('../utils/run-node.js');
|
|
147
|
+
await runNodeBundle(outPath, forwarded, { completion: 'exit', exitOnSuccess: true });
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const { runRuntimeBundle } = await import('../utils/run-node.js');
|
|
151
|
+
await runRuntimeBundle(runtime, outPath, forwarded, { completion: 'exit', exitOnSuccess: true });
|
|
152
|
+
},
|
|
153
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Workspace } from '@gjsify/workspace';
|
|
2
|
+
import { type DepDeclaration, type DependencyGroup } from '../utils/dep-aggregation.js';
|
|
2
3
|
import type { Command } from '../types/index.js';
|
|
3
4
|
interface UpgradeOptions {
|
|
4
5
|
latest?: boolean;
|
|
@@ -16,6 +17,27 @@ interface UpgradeOptions {
|
|
|
16
17
|
yes?: boolean;
|
|
17
18
|
}
|
|
18
19
|
export declare const upgradeCommand: Command<unknown, UpgradeOptions>;
|
|
20
|
+
export declare function collectFromWorkspace(workspace: Workspace, depFilters: string[]): DepDeclaration[];
|
|
21
|
+
/**
|
|
22
|
+
* Is this spec a dist-tag (`next`, `latest`, `beta`) rather than a version range?
|
|
23
|
+
*
|
|
24
|
+
* npm's own rule is "whatever `semver.validRange` rejects is a tag", and that is NOT
|
|
25
|
+
* usable here: `@gjsify/semver`'s `validRange` reads any unparseable leading token as an
|
|
26
|
+
* x-range, so it answers `>=0.0.0` for `latest` — the most common dist-tag of all — while
|
|
27
|
+
* correctly answering `null` for `next`. A detector that disagreed with itself across tag
|
|
28
|
+
* names is worse than none, so the test is lexical and total instead.
|
|
29
|
+
*
|
|
30
|
+
* A semver range begins with a letter in exactly two ways: an x-range (`x`, `X`, and the
|
|
31
|
+
* `*`/empty spellings beside them) and a `v`-prefixed version (`v1.2.3`, `v1.x`). Every
|
|
32
|
+
* other letter-initial spec is a tag — which is also the npm rule for what a tag may be
|
|
33
|
+
* NAMED, since a tag that parses as a version is refused at `npm dist-tag add`.
|
|
34
|
+
*
|
|
35
|
+
* Deliberately NOT fixing `validRange` instead: its leniency is what makes `gjsify
|
|
36
|
+
* install` accept `1`, `1.2` and `x` from real-world manifests, and tightening the shared
|
|
37
|
+
* parser to serve one caller's discrimination question would be a resolution change to
|
|
38
|
+
* every install in the repository.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isDistTagSpec(raw: string): boolean;
|
|
19
41
|
/**
|
|
20
42
|
* `--exact` half of the CI gate: does every matched dep carry a bare version?
|
|
21
43
|
*
|
package/lib/commands/upgrade.js
CHANGED
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
//
|
|
32
32
|
// Workspace-protocol ranges (`workspace:*`, `workspace:^`, …) are always
|
|
33
33
|
// skipped — those are internal links and `gjsify install` resolves them
|
|
34
|
-
// locally.
|
|
34
|
+
// locally. So are DIST-TAG specs (`"@gjsify/cli": "next"`, the `next`-channel
|
|
35
|
+
// opt-in of ADR 0111): a tag carries no version to compare, and `--align`
|
|
36
|
+
// used to overwrite it with one. See `isDistTagSpec`.
|
|
35
37
|
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
36
38
|
import { join, resolve } from 'node:path';
|
|
37
39
|
import { homedir } from 'node:os';
|
|
@@ -242,7 +244,7 @@ function collectAllDeclarations(workspaces, depFilters) {
|
|
|
242
244
|
}
|
|
243
245
|
return out;
|
|
244
246
|
}
|
|
245
|
-
function collectFromWorkspace(workspace, depFilters) {
|
|
247
|
+
export function collectFromWorkspace(workspace, depFilters) {
|
|
246
248
|
const out = [];
|
|
247
249
|
const pkg = workspace.manifest;
|
|
248
250
|
for (const field of DEP_FIELDS) {
|
|
@@ -254,6 +256,21 @@ function collectFromWorkspace(workspace, depFilters) {
|
|
|
254
256
|
continue;
|
|
255
257
|
if (depFilters.length && !depFilters.some((f) => name.toLowerCase().includes(f)))
|
|
256
258
|
continue;
|
|
259
|
+
// A DIST-TAG is not a range and has no version to compare. Skipped for the
|
|
260
|
+
// same reason as the protocols below — `gjsify install` resolves it, and
|
|
261
|
+
// nothing here can say anything true about it.
|
|
262
|
+
//
|
|
263
|
+
// THE DEFECT THIS CLOSES. `"@gjsify/cli": "next"` is how a consumer app opts
|
|
264
|
+
// into the `next` channel (ADR 0111): one spec, resolved through the
|
|
265
|
+
// packument's `dist-tags` on every install. Without this skip `splitRange`
|
|
266
|
+
// returned `{prefix: '', version: null}`, so the declaration joined its dep's
|
|
267
|
+
// group as the literal range `next`: `--check` reported the dep inconsistent
|
|
268
|
+
// against every sibling declaring a real range, `checkLockfileRangeViolations`
|
|
269
|
+
// reported the resolved prerelease as violating `next`, and `--align` —
|
|
270
|
+
// the repair that failure message names — REWROTE the spec to the group's
|
|
271
|
+
// highest declared version, silently destroying the opt-in it was pointed at.
|
|
272
|
+
if (isDistTagSpec(raw))
|
|
273
|
+
continue;
|
|
257
274
|
// Skip workspace-protocol + file: + link: + git: + http(s): specs.
|
|
258
275
|
if (raw.startsWith('workspace:') ||
|
|
259
276
|
raw.startsWith('file:') ||
|
|
@@ -280,6 +297,40 @@ function collectFromWorkspace(workspace, depFilters) {
|
|
|
280
297
|
}
|
|
281
298
|
return out;
|
|
282
299
|
}
|
|
300
|
+
/** The three spellings of an x-range: alphabetic (or empty) and still a RANGE, not a tag. */
|
|
301
|
+
const X_RANGE_SPELLINGS = new Set(['x', 'X', '*', '']);
|
|
302
|
+
/**
|
|
303
|
+
* Is this spec a dist-tag (`next`, `latest`, `beta`) rather than a version range?
|
|
304
|
+
*
|
|
305
|
+
* npm's own rule is "whatever `semver.validRange` rejects is a tag", and that is NOT
|
|
306
|
+
* usable here: `@gjsify/semver`'s `validRange` reads any unparseable leading token as an
|
|
307
|
+
* x-range, so it answers `>=0.0.0` for `latest` — the most common dist-tag of all — while
|
|
308
|
+
* correctly answering `null` for `next`. A detector that disagreed with itself across tag
|
|
309
|
+
* names is worse than none, so the test is lexical and total instead.
|
|
310
|
+
*
|
|
311
|
+
* A semver range begins with a letter in exactly two ways: an x-range (`x`, `X`, and the
|
|
312
|
+
* `*`/empty spellings beside them) and a `v`-prefixed version (`v1.2.3`, `v1.x`). Every
|
|
313
|
+
* other letter-initial spec is a tag — which is also the npm rule for what a tag may be
|
|
314
|
+
* NAMED, since a tag that parses as a version is refused at `npm dist-tag add`.
|
|
315
|
+
*
|
|
316
|
+
* Deliberately NOT fixing `validRange` instead: its leniency is what makes `gjsify
|
|
317
|
+
* install` accept `1`, `1.2` and `x` from real-world manifests, and tightening the shared
|
|
318
|
+
* parser to serve one caller's discrimination question would be a resolution change to
|
|
319
|
+
* every install in the repository.
|
|
320
|
+
*/
|
|
321
|
+
export function isDistTagSpec(raw) {
|
|
322
|
+
const spec = raw.trim();
|
|
323
|
+
if (X_RANGE_SPELLINGS.has(spec))
|
|
324
|
+
return false;
|
|
325
|
+
if (!/^[A-Za-z]/.test(spec))
|
|
326
|
+
return false;
|
|
327
|
+
if (/^v\d/.test(spec) || /^v[xX*]/.test(spec))
|
|
328
|
+
return false;
|
|
329
|
+
// npm tag names are URL path segments: no whitespace, no range operators, nothing the
|
|
330
|
+
// packument lookup could not carry. A letter-initial spec that is none of the above
|
|
331
|
+
// and still fails this is malformed either way, and is left to the registry to reject.
|
|
332
|
+
return /^[A-Za-z][\w.-]*$/.test(spec);
|
|
333
|
+
}
|
|
283
334
|
function splitRange(range) {
|
|
284
335
|
const m = range.match(/^(\^|~|>=|<=|>|<|=)?\s*([0-9].*)$/);
|
|
285
336
|
if (!m)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.63.
|
|
3
|
+
"version": "0.63.1-next.235",
|
|
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": "
|
|
146
|
-
"@gjsify/buffer": "
|
|
147
|
-
"@gjsify/create-app": "
|
|
148
|
-
"@gjsify/node-globals": "
|
|
149
|
-
"@gjsify/node-polyfills": "
|
|
150
|
-
"@gjsify/npm-registry": "
|
|
151
|
-
"@gjsify/resolve-npm": "
|
|
152
|
-
"@gjsify/rolldown-plugin-gjsify": "
|
|
153
|
-
"@gjsify/vite-plugin-blueprint": "
|
|
154
|
-
"@gjsify/rolldown-plugin-pnp": "
|
|
155
|
-
"@gjsify/semver": "
|
|
156
|
-
"@gjsify/tar": "
|
|
157
|
-
"@gjsify/tsc": "
|
|
158
|
-
"@gjsify/utils": "
|
|
159
|
-
"@gjsify/web-polyfills": "
|
|
160
|
-
"@gjsify/workspace": "
|
|
145
|
+
"@gjsify/blueprint": "0.63.1-next.235",
|
|
146
|
+
"@gjsify/buffer": "0.63.1-next.235",
|
|
147
|
+
"@gjsify/create-app": "0.63.1-next.235",
|
|
148
|
+
"@gjsify/node-globals": "0.63.1-next.235",
|
|
149
|
+
"@gjsify/node-polyfills": "0.63.1-next.235",
|
|
150
|
+
"@gjsify/npm-registry": "0.63.1-next.235",
|
|
151
|
+
"@gjsify/resolve-npm": "0.63.1-next.235",
|
|
152
|
+
"@gjsify/rolldown-plugin-gjsify": "0.63.1-next.235",
|
|
153
|
+
"@gjsify/vite-plugin-blueprint": "0.63.1-next.235",
|
|
154
|
+
"@gjsify/rolldown-plugin-pnp": "0.63.1-next.235",
|
|
155
|
+
"@gjsify/semver": "0.63.1-next.235",
|
|
156
|
+
"@gjsify/tar": "0.63.1-next.235",
|
|
157
|
+
"@gjsify/tsc": "0.63.1-next.235",
|
|
158
|
+
"@gjsify/utils": "0.63.1-next.235",
|
|
159
|
+
"@gjsify/web-polyfills": "0.63.1-next.235",
|
|
160
|
+
"@gjsify/workspace": "0.63.1-next.235",
|
|
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": "
|
|
169
|
+
"@gjsify/unit": "0.63.1-next.235",
|
|
170
170
|
"@types/yargs": "^17.0.35",
|
|
171
171
|
"typescript": "^6.0.3"
|
|
172
172
|
},
|
|
173
173
|
"peerDependencies": {
|
|
174
|
-
"@gjsify/oxfmt-native": "
|
|
175
|
-
"@gjsify/rolldown-native": "
|
|
174
|
+
"@gjsify/oxfmt-native": "0.63.1-next.235",
|
|
175
|
+
"@gjsify/rolldown-native": "0.63.1-next.235"
|
|
176
176
|
},
|
|
177
177
|
"peerDependenciesMeta": {
|
|
178
178
|
"@gjsify/oxfmt-native": {
|
|
@@ -191,5 +191,6 @@
|
|
|
191
191
|
"bugs": {
|
|
192
192
|
"url": "https://github.com/gjsify/gjsify/issues"
|
|
193
193
|
},
|
|
194
|
-
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/infra/cli#readme"
|
|
194
|
+
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/infra/cli#readme",
|
|
195
|
+
"gitHead": "9d2cca56170a0215b3e0792e98421ed1f83e7f72"
|
|
195
196
|
}
|