@gjsify/cli 0.4.28 → 0.4.30
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 +132 -132
- package/lib/actions/barrels-generate.js +1 -5
- package/lib/actions/build.d.ts +3 -3
- package/lib/actions/build.js +56 -64
- package/lib/bundler-pick.d.ts +3 -3
- package/lib/bundler-pick.js +5 -6
- package/lib/commands/build.d.ts +1 -1
- package/lib/commands/build.js +37 -31
- package/lib/commands/check.js +3 -3
- package/lib/commands/create.d.ts +1 -1
- package/lib/commands/dlx.d.ts +1 -1
- package/lib/commands/fix.js +33 -23
- package/lib/commands/flatpak/build.js +6 -2
- package/lib/commands/flatpak/check.js +9 -3
- package/lib/commands/flatpak/ci.js +1 -2
- package/lib/commands/flatpak/deps.js +1 -2
- package/lib/commands/flatpak/diff.js +2 -6
- package/lib/commands/flatpak/init.js +19 -19
- package/lib/commands/flatpak/release.js +2 -2
- package/lib/commands/flatpak/scaffold.js +3 -11
- package/lib/commands/flatpak/sync-flathub.js +5 -9
- package/lib/commands/flatpak/utils.js +1 -6
- package/lib/commands/foreach.d.ts +1 -1
- package/lib/commands/foreach.js +5 -14
- package/lib/commands/format.js +54 -41
- package/lib/commands/generate-installer.d.ts +1 -1
- package/lib/commands/gettext.d.ts +1 -1
- package/lib/commands/gettext.js +8 -15
- package/lib/commands/gresource.d.ts +1 -1
- package/lib/commands/gresource.js +8 -13
- package/lib/commands/gsettings.d.ts +1 -1
- package/lib/commands/gsettings.js +7 -8
- package/lib/commands/info.d.ts +1 -1
- package/lib/commands/install.d.ts +1 -1
- package/lib/commands/install.js +45 -13
- package/lib/commands/lint.d.ts +1 -1
- package/lib/commands/lint.js +22 -22
- package/lib/commands/pack.d.ts +1 -1
- package/lib/commands/pack.js +29 -17
- package/lib/commands/publish.d.ts +1 -1
- package/lib/commands/publish.js +17 -18
- package/lib/commands/run.d.ts +1 -1
- package/lib/commands/run.js +2 -6
- package/lib/commands/self-update.d.ts +1 -1
- package/lib/commands/self-update.js +1 -3
- package/lib/commands/showcase.d.ts +1 -1
- package/lib/commands/showcase.js +1 -1
- package/lib/commands/system-check.d.ts +1 -1
- package/lib/commands/system-check.js +8 -11
- package/lib/commands/test.js +12 -8
- package/lib/commands/uninstall.d.ts +1 -1
- package/lib/commands/uninstall.js +1 -3
- package/lib/commands/upgrade.d.ts +1 -1
- package/lib/commands/upgrade.js +109 -120
- package/lib/commands/workspace.d.ts +1 -1
- package/lib/commands/workspace.js +1 -3
- package/lib/config.js +18 -13
- package/lib/index.js +3 -1
- package/lib/templates/install.mjs.tmpl +20 -14
- package/lib/templates/oxfmtrc.tmpl +54 -0
- package/lib/templates/oxlintrc.json.tmpl +35 -0
- package/lib/types/command.d.ts +1 -1
- package/lib/types/config-data.d.ts +23 -13
- package/lib/types/cosmiconfig-result.d.ts +1 -1
- package/lib/utils/check-system-deps.js +10 -4
- package/lib/utils/detect-native-packages.js +1 -1
- package/lib/utils/dlx-cache.js +2 -7
- package/lib/utils/install-backend-native.d.ts +2 -2
- package/lib/utils/install-backend-native.js +112 -58
- package/lib/utils/install-backend.js +2 -1
- package/lib/utils/install-global.js +1 -3
- package/lib/utils/normalize-bundler-options.js +52 -17
- package/lib/utils/oxc-resolve.d.ts +63 -0
- package/lib/utils/oxc-resolve.js +264 -0
- package/lib/utils/pkg-json-edit.js +1 -6
- package/lib/utils/run-gjs.js +1 -4
- package/lib/utils/run-lifecycle-script.js +3 -7
- package/lib/utils/workspace-root.js +3 -1
- package/package.json +17 -17
- package/lib/templates/biome.json.tmpl +0 -79
- package/lib/utils/biome-resolve.d.ts +0 -47
- package/lib/utils/biome-resolve.js +0 -204
package/lib/commands/gettext.js
CHANGED
|
@@ -28,13 +28,7 @@ async function fileExists(path) {
|
|
|
28
28
|
async function compileBulkXml(opts) {
|
|
29
29
|
const outputFile = join(opts.outDir, opts.filename);
|
|
30
30
|
await ensureDir(opts.outDir);
|
|
31
|
-
const args = [
|
|
32
|
-
`--output-file=${outputFile}`,
|
|
33
|
-
'--xml',
|
|
34
|
-
`--template=${opts.template}`,
|
|
35
|
-
'-d',
|
|
36
|
-
opts.poDir,
|
|
37
|
-
];
|
|
31
|
+
const args = [`--output-file=${outputFile}`, '--xml', `--template=${opts.template}`, '-d', opts.poDir];
|
|
38
32
|
if (opts.verbose) {
|
|
39
33
|
console.log(`[gjsify gettext] msgfmt ${args.join(' ')}`);
|
|
40
34
|
}
|
|
@@ -55,9 +49,7 @@ async function compilePerLanguage(opts) {
|
|
|
55
49
|
}
|
|
56
50
|
for (const lang of languages) {
|
|
57
51
|
const poFile = join(opts.poDir, `${lang}.po`);
|
|
58
|
-
const langDir = opts.format === 'mo'
|
|
59
|
-
? join(opts.outDir, lang, 'LC_MESSAGES')
|
|
60
|
-
: join(opts.outDir, lang);
|
|
52
|
+
const langDir = opts.format === 'mo' ? join(opts.outDir, lang, 'LC_MESSAGES') : join(opts.outDir, lang);
|
|
61
53
|
await ensureDir(langDir);
|
|
62
54
|
const outputFile = join(langDir, opts.filename);
|
|
63
55
|
// msgfmt produces the binary .mo format by default — there is no
|
|
@@ -187,15 +179,16 @@ export const gettextCommand = {
|
|
|
187
179
|
}
|
|
188
180
|
}
|
|
189
181
|
catch (err) {
|
|
190
|
-
|
|
182
|
+
const e = err;
|
|
183
|
+
if (e?.code === 'ENOENT') {
|
|
191
184
|
console.error('[gjsify gettext] msgfmt not found. Install it via your distro (package: gettext).');
|
|
192
185
|
}
|
|
193
186
|
else {
|
|
194
|
-
if (
|
|
195
|
-
process.stderr.write(
|
|
196
|
-
console.error(`[gjsify gettext] msgfmt failed${
|
|
187
|
+
if (e?.stderr)
|
|
188
|
+
process.stderr.write(e.stderr);
|
|
189
|
+
console.error(`[gjsify gettext] msgfmt failed${e?.code !== undefined ? ` (exit ${e.code})` : ''}`);
|
|
197
190
|
}
|
|
198
|
-
process.exitCode = typeof
|
|
191
|
+
process.exitCode = typeof e?.code === 'number' ? e.code : 1;
|
|
199
192
|
}
|
|
200
193
|
},
|
|
201
194
|
};
|
|
@@ -43,14 +43,8 @@ export const gresourceCommand = {
|
|
|
43
43
|
handler: async (args) => {
|
|
44
44
|
const xmlPath = resolve(args.xml);
|
|
45
45
|
const target = args.target ? resolve(args.target) : defaultTargetFor(xmlPath);
|
|
46
|
-
const sourcedir = args.sourcedir
|
|
47
|
-
|
|
48
|
-
: dirname(xmlPath);
|
|
49
|
-
const cmdArgs = [
|
|
50
|
-
`--sourcedir=${sourcedir}`,
|
|
51
|
-
`--target=${target}`,
|
|
52
|
-
xmlPath,
|
|
53
|
-
];
|
|
46
|
+
const sourcedir = args.sourcedir ? resolve(args.sourcedir) : dirname(xmlPath);
|
|
47
|
+
const cmdArgs = [`--sourcedir=${sourcedir}`, `--target=${target}`, xmlPath];
|
|
54
48
|
if (args.verbose) {
|
|
55
49
|
console.log(`[gjsify gresource] glib-compile-resources ${cmdArgs.join(' ')}`);
|
|
56
50
|
}
|
|
@@ -68,15 +62,16 @@ export const gresourceCommand = {
|
|
|
68
62
|
}
|
|
69
63
|
}
|
|
70
64
|
catch (err) {
|
|
71
|
-
|
|
65
|
+
const e = err;
|
|
66
|
+
if (e?.code === 'ENOENT') {
|
|
72
67
|
console.error('[gjsify gresource] glib-compile-resources not found. Install it via your distro (package: glib2-devel / libglib2.0-dev).');
|
|
73
68
|
}
|
|
74
69
|
else {
|
|
75
|
-
if (
|
|
76
|
-
process.stderr.write(
|
|
77
|
-
console.error(`[gjsify gresource] glib-compile-resources failed${
|
|
70
|
+
if (e?.stderr)
|
|
71
|
+
process.stderr.write(e.stderr);
|
|
72
|
+
console.error(`[gjsify gresource] glib-compile-resources failed${e?.code !== undefined ? ` (exit ${e.code})` : ''}`);
|
|
78
73
|
}
|
|
79
|
-
process.exitCode = typeof
|
|
74
|
+
process.exitCode = typeof e?.code === 'number' ? e.code : 1;
|
|
80
75
|
}
|
|
81
76
|
},
|
|
82
77
|
};
|
|
@@ -33,9 +33,7 @@ export const gsettingsCommand = {
|
|
|
33
33
|
},
|
|
34
34
|
handler: async (args) => {
|
|
35
35
|
const schemadir = resolve(args.schemadir);
|
|
36
|
-
const targetdir = args.targetdir
|
|
37
|
-
? resolve(args.targetdir)
|
|
38
|
-
: schemadir;
|
|
36
|
+
const targetdir = args.targetdir ? resolve(args.targetdir) : schemadir;
|
|
39
37
|
const cmdArgs = [];
|
|
40
38
|
if (args.strict)
|
|
41
39
|
cmdArgs.push('--strict');
|
|
@@ -58,15 +56,16 @@ export const gsettingsCommand = {
|
|
|
58
56
|
}
|
|
59
57
|
}
|
|
60
58
|
catch (err) {
|
|
61
|
-
|
|
59
|
+
const e = err;
|
|
60
|
+
if (e?.code === 'ENOENT') {
|
|
62
61
|
console.error('[gjsify gsettings] glib-compile-schemas not found. Install it via your distro (package: glib2-devel / libglib2.0-dev).');
|
|
63
62
|
}
|
|
64
63
|
else {
|
|
65
|
-
if (
|
|
66
|
-
process.stderr.write(
|
|
67
|
-
console.error(`[gjsify gsettings] glib-compile-schemas failed${
|
|
64
|
+
if (e?.stderr)
|
|
65
|
+
process.stderr.write(e.stderr);
|
|
66
|
+
console.error(`[gjsify gsettings] glib-compile-schemas failed${e?.code !== undefined ? ` (exit ${e.code})` : ''}`);
|
|
68
67
|
}
|
|
69
|
-
process.exitCode = typeof
|
|
68
|
+
process.exitCode = typeof e?.code === 'number' ? e.code : 1;
|
|
70
69
|
}
|
|
71
70
|
},
|
|
72
71
|
};
|
package/lib/commands/info.d.ts
CHANGED
package/lib/commands/install.js
CHANGED
|
@@ -22,10 +22,10 @@ import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, rmSync, syml
|
|
|
22
22
|
import { dirname, join, relative } from 'node:path';
|
|
23
23
|
import { spawn } from 'node:child_process';
|
|
24
24
|
import { discoverWorkspaces } from '@gjsify/workspace';
|
|
25
|
-
import { buildInstallCommand, detectPackageManager, runMinimalChecks
|
|
25
|
+
import { buildInstallCommand, detectPackageManager, runMinimalChecks } from '../utils/check-system-deps.js';
|
|
26
26
|
import { detectNativePackages } from '../utils/detect-native-packages.js';
|
|
27
27
|
import { installPackages } from '../utils/install-backend.js';
|
|
28
|
-
import { binDirOnPath, defaultGlobalLayout, linkGlobalBins, specToPackageName
|
|
28
|
+
import { binDirOnPath, defaultGlobalLayout, linkGlobalBins, specToPackageName } from '../utils/install-global.js';
|
|
29
29
|
import { addDependencyEntry, defaultRangeFromVersion, parseSpec, projectSpecsFromPackageJson, readPackageJson, writePackageJson, } from '../utils/pkg-json-edit.js';
|
|
30
30
|
export const installCommand = {
|
|
31
31
|
command: 'install [packages..]',
|
|
@@ -265,14 +265,39 @@ async function workspaceInstall(cwd, args) {
|
|
|
265
265
|
for (const [depName, spec] of Object.entries(block)) {
|
|
266
266
|
if (typeof spec !== 'string')
|
|
267
267
|
continue;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
// A dependency whose NAME matches a local workspace is always
|
|
269
|
+
// satisfied by that workspace — never by a same-named package on
|
|
270
|
+
// npm. This holds regardless of the spec form: an explicit
|
|
271
|
+
// `workspace:` protocol, a plain semver range (`^1.2.3`), or even
|
|
272
|
+
// a dist-tag. Yarn/npm resolve a workspace-named dep to the local
|
|
273
|
+
// package first; we MUST do the same. Routing such a dep into the
|
|
274
|
+
// native fetch/extract queue is the data-loss bug this guards
|
|
275
|
+
// against — `extractOne` would `rmSync` + drop the published
|
|
276
|
+
// tarball over the workspace's OWN source tree (the published
|
|
277
|
+
// tarball ships only `files`, so `src/**` gets wiped). Symlink
|
|
278
|
+
// it instead, exactly like a `workspace:` ref.
|
|
279
|
+
const localWorkspace = byName.get(depName);
|
|
280
|
+
if (localWorkspace) {
|
|
281
|
+
// Only an EXPLICIT out-of-workspace protocol (link:/file:/
|
|
282
|
+
// portal:/git+/http(s):) opts out of the local workspace. Any
|
|
283
|
+
// other shape — `workspace:` or a plain semver range/dist-tag
|
|
284
|
+
// (`^1.2.3`, `*`, `latest`) — resolves to the local package.
|
|
285
|
+
const explicitOverride = /^(link|file|portal|git\+|https?):/.test(spec);
|
|
286
|
+
if (!explicitOverride) {
|
|
287
|
+
symlinks.push({
|
|
288
|
+
fromWorkspaceName: ws.name,
|
|
289
|
+
depName,
|
|
290
|
+
targetLocation: localWorkspace.location,
|
|
291
|
+
});
|
|
292
|
+
continue;
|
|
273
293
|
}
|
|
274
|
-
|
|
275
|
-
|
|
294
|
+
// explicit override → fall through to the existing handling.
|
|
295
|
+
}
|
|
296
|
+
if (spec.startsWith('workspace:')) {
|
|
297
|
+
// `workspace:` against a name that is NOT a discovered
|
|
298
|
+
// workspace is a hard error (typo / missing package).
|
|
299
|
+
throw new Error(`gjsify install: ${ws.name} declares "${depName}: ${spec}" but ` +
|
|
300
|
+
`no workspace with that name exists`);
|
|
276
301
|
}
|
|
277
302
|
if (/^(link|file|portal|git\+|https?):/.test(spec))
|
|
278
303
|
continue;
|
|
@@ -320,7 +345,8 @@ async function workspaceInstall(cwd, args) {
|
|
|
320
345
|
try {
|
|
321
346
|
rmSync(linkPath, { recursive: true, force: true });
|
|
322
347
|
}
|
|
323
|
-
catch {
|
|
348
|
+
catch {
|
|
349
|
+
/* unexpected — Gio failure on a path we just lstat'd to
|
|
324
350
|
decide we wanted to remove. The subsequent symlinkSync
|
|
325
351
|
will surface the real reason if there is one. */
|
|
326
352
|
}
|
|
@@ -367,7 +393,9 @@ async function workspaceInstall(cwd, args) {
|
|
|
367
393
|
lstatSync(linkPath);
|
|
368
394
|
existsHere = true;
|
|
369
395
|
}
|
|
370
|
-
catch {
|
|
396
|
+
catch {
|
|
397
|
+
/* ENOENT */
|
|
398
|
+
}
|
|
371
399
|
if (existsHere)
|
|
372
400
|
continue;
|
|
373
401
|
mkdirSync(dirname(linkPath), { recursive: true });
|
|
@@ -419,8 +447,12 @@ async function workspaceInstall(cwd, args) {
|
|
|
419
447
|
try {
|
|
420
448
|
rmSync(linkPath, { force: true });
|
|
421
449
|
}
|
|
422
|
-
catch {
|
|
423
|
-
|
|
450
|
+
catch {
|
|
451
|
+
/* fine */
|
|
452
|
+
}
|
|
453
|
+
writeFileSync(linkPath, buildBinShim(ws.location, nodeTarget, gjsTarget, nativePrebuildDirs), {
|
|
454
|
+
mode: 0o755,
|
|
455
|
+
});
|
|
424
456
|
chmodSync(linkPath, 0o755);
|
|
425
457
|
wsBinsCreated++;
|
|
426
458
|
}
|
package/lib/commands/lint.d.ts
CHANGED
package/lib/commands/lint.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
// `gjsify lint` — wraps
|
|
1
|
+
// `gjsify lint` — wraps oxlint.
|
|
2
2
|
//
|
|
3
|
-
// Sibling of `gjsify format`. Spawns
|
|
4
|
-
// (
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// Sibling of `gjsify format`. Spawns oxlint via its Node launcher
|
|
4
|
+
// (`node_modules/oxlint/bin/oxlint` → `dist/cli.js`) — NOT a bare binary —
|
|
5
|
+
// because oxlint's JS-plugin host (used by the internal
|
|
6
|
+
// `oxlint-plugin-gjsify` rule) lives in the JS launcher. Default behaviour:
|
|
7
|
+
// report-only. Pass `--fix` for oxlint's safe-fix mode, or use `gjsify fix`
|
|
8
|
+
// for the combined oxfmt + oxlint --fix surface.
|
|
7
9
|
import { resolve } from 'node:path';
|
|
8
|
-
import {
|
|
10
|
+
import { OxcNotFoundError, findOxlintConfig, printOxcNotFound, runOxlint } from '../utils/oxc-resolve.js';
|
|
9
11
|
export const lintCommand = {
|
|
10
12
|
command: 'lint [paths..]',
|
|
11
|
-
description: 'Run
|
|
13
|
+
description: 'Run oxlint diagnostics (spawned via its Node launcher to support JS plugins).',
|
|
12
14
|
builder: (yargs) => {
|
|
13
15
|
return yargs
|
|
14
16
|
.positional('paths', {
|
|
@@ -16,41 +18,39 @@ export const lintCommand = {
|
|
|
16
18
|
type: 'string',
|
|
17
19
|
array: true,
|
|
18
20
|
})
|
|
19
|
-
.option('
|
|
21
|
+
.option('fix', {
|
|
20
22
|
description: 'Apply safe lint fixes in place.',
|
|
21
23
|
type: 'boolean',
|
|
22
24
|
default: false,
|
|
23
25
|
})
|
|
24
26
|
.option('config-path', {
|
|
25
|
-
description: 'Path to
|
|
27
|
+
description: 'Path to an .oxlintrc.json. Default: walks up from cwd to find one.',
|
|
26
28
|
type: 'string',
|
|
27
29
|
normalize: true,
|
|
28
30
|
})
|
|
29
31
|
.option('verbose', {
|
|
30
|
-
description: 'Echo the resolved
|
|
32
|
+
description: 'Echo the resolved oxlint launcher + args before spawning.',
|
|
31
33
|
type: 'boolean',
|
|
32
34
|
default: false,
|
|
33
35
|
});
|
|
34
36
|
},
|
|
35
37
|
handler: async (args) => {
|
|
36
38
|
const cwd = process.cwd();
|
|
37
|
-
const paths = args.paths?.length
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
biomeArgs.push('--write');
|
|
43
|
-
const configPath = args.configPath ?? findBiomeConfig(cwd) ?? undefined;
|
|
39
|
+
const paths = args.paths?.length ? args.paths : ['.'];
|
|
40
|
+
const oxlintArgs = [];
|
|
41
|
+
if (args.fix)
|
|
42
|
+
oxlintArgs.push('--fix');
|
|
43
|
+
const configPath = args.configPath ?? findOxlintConfig(cwd) ?? undefined;
|
|
44
44
|
if (configPath)
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
oxlintArgs.push('--config', resolve(configPath));
|
|
46
|
+
oxlintArgs.push(...paths);
|
|
47
47
|
try {
|
|
48
|
-
const code = await
|
|
48
|
+
const code = await runOxlint(oxlintArgs, { cwd, verbose: args.verbose });
|
|
49
49
|
process.exitCode = code;
|
|
50
50
|
}
|
|
51
51
|
catch (err) {
|
|
52
|
-
if (err instanceof
|
|
53
|
-
|
|
52
|
+
if (err instanceof OxcNotFoundError) {
|
|
53
|
+
printOxcNotFound(err);
|
|
54
54
|
process.exitCode = 1;
|
|
55
55
|
return;
|
|
56
56
|
}
|
package/lib/commands/pack.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ interface PackResult {
|
|
|
23
23
|
/** Absolute path of the written .tgz, or null on --dry-run. */
|
|
24
24
|
absolutePath: string | null;
|
|
25
25
|
}
|
|
26
|
-
export declare const packCommand: Command<
|
|
26
|
+
export declare const packCommand: Command<unknown, PackOptions>;
|
|
27
27
|
export interface PackWorkspaceOptions {
|
|
28
28
|
/** Directory to write the .tgz into. Defaults to the workspace itself. */
|
|
29
29
|
destination?: string;
|
package/lib/commands/pack.js
CHANGED
|
@@ -113,15 +113,11 @@ export async function packWorkspace(wsDir, opts = {}) {
|
|
|
113
113
|
// mutated it (e.g. a `prepack` that injects build metadata into
|
|
114
114
|
// package.json fields). Rare but legal — npm pack does the same.
|
|
115
115
|
const sourceAfterScripts = readFileSync(pkgPath, 'utf-8');
|
|
116
|
-
const pkgAfterScripts = sourceAfterScripts === originalSource
|
|
117
|
-
? pkg
|
|
118
|
-
: JSON.parse(sourceAfterScripts);
|
|
116
|
+
const pkgAfterScripts = sourceAfterScripts === originalSource ? pkg : JSON.parse(sourceAfterScripts);
|
|
119
117
|
// Rewrite workspace:^/~/* deps to resolved npm version ranges, mirroring
|
|
120
118
|
// yarn's auto-rewrite at publish time. Done in-memory only — the source
|
|
121
119
|
// package.json on disk is never mutated by `gjsify pack`.
|
|
122
|
-
const rewrittenPkg = opts.skipWorkspaceRewrite
|
|
123
|
-
? pkgAfterScripts
|
|
124
|
-
: rewriteWorkspaceDeps(pkgAfterScripts, wsDir);
|
|
120
|
+
const rewrittenPkg = opts.skipWorkspaceRewrite ? pkgAfterScripts : rewriteWorkspaceDeps(pkgAfterScripts, wsDir);
|
|
125
121
|
const rewrittenSource = JSON.stringify(rewrittenPkg, null, indentOf(sourceAfterScripts)) + '\n';
|
|
126
122
|
// Collect files according to the package.json `files` field (or npm's
|
|
127
123
|
// default set). The package.json itself is always included with the
|
|
@@ -148,9 +144,7 @@ export async function packWorkspace(wsDir, opts = {}) {
|
|
|
148
144
|
const tarBytes = createTarball(entries);
|
|
149
145
|
const gzipBytes = await gzip(tarBytes);
|
|
150
146
|
// npm filename: scope replaced with leading dash. "@gjsify/foo" → "gjsify-foo".
|
|
151
|
-
const filenameBase = name.startsWith('@')
|
|
152
|
-
? name.slice(1).replace('/', '-')
|
|
153
|
-
: name;
|
|
147
|
+
const filenameBase = name.startsWith('@') ? name.slice(1).replace('/', '-') : name;
|
|
154
148
|
const filename = `${filenameBase}-${version}.tgz`;
|
|
155
149
|
const sha1 = createHash('sha1').update(gzipBytes).digest('hex');
|
|
156
150
|
const sha512 = createHash('sha512').update(gzipBytes).digest('base64');
|
|
@@ -188,7 +182,9 @@ export async function packWorkspace(wsDir, opts = {}) {
|
|
|
188
182
|
*/
|
|
189
183
|
function collectFiles(wsDir, pkg) {
|
|
190
184
|
const always = forceIncluded(pkg);
|
|
191
|
-
const filesField = Array.isArray(pkg.files)
|
|
185
|
+
const filesField = Array.isArray(pkg.files)
|
|
186
|
+
? pkg.files.filter((f) => typeof f === 'string')
|
|
187
|
+
: null;
|
|
192
188
|
let candidates;
|
|
193
189
|
if (filesField) {
|
|
194
190
|
candidates = expandFilesPatterns(wsDir, filesField);
|
|
@@ -208,12 +204,25 @@ function collectFiles(wsDir, pkg) {
|
|
|
208
204
|
}
|
|
209
205
|
return [...out].sort();
|
|
210
206
|
}
|
|
211
|
-
const ALWAYS_INCLUDED_BASENAMES = new Set(['package.json', 'README', 'README.md', 'LICENSE', 'LICENSE.md', 'NOTICE', 'NOTICE.md']);
|
|
212
207
|
const NEVER_INCLUDED_BASENAMES = new Set([
|
|
213
|
-
'.git',
|
|
214
|
-
'
|
|
215
|
-
'
|
|
216
|
-
'.
|
|
208
|
+
'.git',
|
|
209
|
+
'.svn',
|
|
210
|
+
'.hg',
|
|
211
|
+
'.gitignore',
|
|
212
|
+
'.gitattributes',
|
|
213
|
+
'.npmrc',
|
|
214
|
+
'CVS',
|
|
215
|
+
'.DS_Store',
|
|
216
|
+
'node_modules',
|
|
217
|
+
'.npmignore',
|
|
218
|
+
'package-lock.json',
|
|
219
|
+
'gjsify-lock.json',
|
|
220
|
+
'yarn.lock',
|
|
221
|
+
'yarn-error.log',
|
|
222
|
+
'.yarn',
|
|
223
|
+
'.pnp.cjs',
|
|
224
|
+
'.pnp.loader.mjs',
|
|
225
|
+
'tsconfig.tsbuildinfo',
|
|
217
226
|
]);
|
|
218
227
|
function forceIncluded(pkg) {
|
|
219
228
|
const out = new Set();
|
|
@@ -292,7 +301,7 @@ function loadIgnore(wsDir) {
|
|
|
292
301
|
const npmIgnorePath = join(wsDir, '.npmignore');
|
|
293
302
|
const gitIgnorePath = join(wsDir, '.gitignore');
|
|
294
303
|
const patterns = [];
|
|
295
|
-
const sourcePath = existsSync(npmIgnorePath) ? npmIgnorePath :
|
|
304
|
+
const sourcePath = existsSync(npmIgnorePath) ? npmIgnorePath : existsSync(gitIgnorePath) ? gitIgnorePath : null;
|
|
296
305
|
if (sourcePath) {
|
|
297
306
|
const lines = readFileSync(sourcePath, 'utf-8').split('\n');
|
|
298
307
|
for (const raw of lines) {
|
|
@@ -319,7 +328,10 @@ function globToRegex(glob) {
|
|
|
319
328
|
// Escape regex metachars except *,?,/
|
|
320
329
|
pat = pat.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
321
330
|
// ** → .* * → [^/]* ? → [^/]
|
|
322
|
-
pat = pat
|
|
331
|
+
pat = pat
|
|
332
|
+
.replace(/\*\*/g, '__DOUBLESTAR__')
|
|
333
|
+
.replace(/\*/g, '[^/]*')
|
|
334
|
+
.replace(/__DOUBLESTAR__/g, '.*');
|
|
323
335
|
pat = pat.replace(/\?/g, '[^/]');
|
|
324
336
|
return new RegExp(`^${pat}($|/)`);
|
|
325
337
|
}
|
package/lib/commands/publish.js
CHANGED
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
import { existsSync, readFileSync } from 'node:fs';
|
|
37
37
|
import { homedir } from 'node:os';
|
|
38
38
|
import { join, resolve } from 'node:path';
|
|
39
|
-
import { DEFAULT_REGISTRY, parseNpmrc, registryFor, buildHeaders
|
|
39
|
+
import { DEFAULT_REGISTRY, parseNpmrc, registryFor, buildHeaders } from '@gjsify/npm-registry';
|
|
40
40
|
import { packWorkspace } from './pack.js';
|
|
41
|
-
import { getNpmTrustedToken, hasGithubOidcEnv, OidcExchangeError, OidcUnavailableError
|
|
41
|
+
import { getNpmTrustedToken, hasGithubOidcEnv, OidcExchangeError, OidcUnavailableError } from '../utils/npm-oidc.js';
|
|
42
42
|
export const publishCommand = {
|
|
43
43
|
command: 'publish [path]',
|
|
44
44
|
description: 'Pack + upload the workspace at <path> (default: cwd) to its npm registry. Drop-in for `npm publish` with workspace:^ rewrite handled automatically.',
|
|
@@ -68,7 +68,7 @@ export const publishCommand = {
|
|
|
68
68
|
default: false,
|
|
69
69
|
})
|
|
70
70
|
.option('provenance', {
|
|
71
|
-
description:
|
|
71
|
+
description: "Pass-through flag — recorded in the payload but no signing happens (gjsify doesn't ship a sigstore signer yet).",
|
|
72
72
|
type: 'boolean',
|
|
73
73
|
default: false,
|
|
74
74
|
})
|
|
@@ -227,9 +227,7 @@ export const publishCommand = {
|
|
|
227
227
|
// libnpmpublish/lib/publish.js). The full scoped filename is what
|
|
228
228
|
// `npm pack` writes to disk, but the registry stores tarballs at
|
|
229
229
|
// the unscoped path.
|
|
230
|
-
const unscopedName = packed.name.includes('/')
|
|
231
|
-
? packed.name.slice(packed.name.indexOf('/') + 1)
|
|
232
|
-
: packed.name;
|
|
230
|
+
const unscopedName = packed.name.includes('/') ? packed.name.slice(packed.name.indexOf('/') + 1) : packed.name;
|
|
233
231
|
const wireFilename = `${unscopedName}-${packed.version}.tgz`;
|
|
234
232
|
const tarballUrl = `${registryClean}/${packed.name}/-/${wireFilename}`;
|
|
235
233
|
// 4. Build payload + PUT
|
|
@@ -255,8 +253,7 @@ export const publishCommand = {
|
|
|
255
253
|
// OIDC is used iff GitHub OIDC env vars are present AND no
|
|
256
254
|
// `NODE_AUTH_TOKEN` is set. With `NODE_AUTH_TOKEN` set the user has
|
|
257
255
|
// explicitly opted into token auth, so we don't shadow their choice.
|
|
258
|
-
const wantTrusted = trustedFlag === true ||
|
|
259
|
-
(trustedFlag === undefined && hasGithubOidcEnv() && !process.env.NODE_AUTH_TOKEN);
|
|
256
|
+
const wantTrusted = trustedFlag === true || (trustedFlag === undefined && hasGithubOidcEnv() && !process.env.NODE_AUTH_TOKEN);
|
|
260
257
|
let authMode = 'token';
|
|
261
258
|
if (wantTrusted) {
|
|
262
259
|
try {
|
|
@@ -281,9 +278,7 @@ export const publishCommand = {
|
|
|
281
278
|
// Trusted Publisher bootstrap"). Skip such a package when
|
|
282
279
|
// --tolerate-untrusted-new is set so one un-bootstrapped
|
|
283
280
|
// package doesn't break the entire serialized publish loop.
|
|
284
|
-
const isUntrustedNewPackage = err instanceof OidcExchangeError &&
|
|
285
|
-
err.status === 404 &&
|
|
286
|
-
/package not found/i.test(err.body);
|
|
281
|
+
const isUntrustedNewPackage = err instanceof OidcExchangeError && err.status === 404 && /package not found/i.test(err.body);
|
|
287
282
|
if (isUntrustedNewPackage && tolerateUntrustedNew) {
|
|
288
283
|
const headerMsg = `${packed.name}@${packed.version} (skipped — no Trusted Publisher on npm, see AGENTS.md "New @gjsify/* package: first-publish + Trusted Publisher bootstrap")`;
|
|
289
284
|
if (args.json) {
|
|
@@ -343,8 +338,7 @@ export const publishCommand = {
|
|
|
343
338
|
if (!otp && res.status === 401) {
|
|
344
339
|
const wwwAuth = res.headers.get('www-authenticate') ?? '';
|
|
345
340
|
const body401 = await res.text().catch(() => '');
|
|
346
|
-
const needsOtp = wwwAuth.toLowerCase().split(/,\s*/).includes('otp') ||
|
|
347
|
-
/one-time pass/i.test(body401);
|
|
341
|
+
const needsOtp = wwwAuth.toLowerCase().split(/,\s*/).includes('otp') || /one-time pass/i.test(body401);
|
|
348
342
|
if (needsOtp) {
|
|
349
343
|
// Interactive path: if stdin is a TTY, prompt and retry once.
|
|
350
344
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
@@ -394,8 +388,7 @@ export const publishCommand = {
|
|
|
394
388
|
// Both are intentionally tolerated under --tolerate-republish so
|
|
395
389
|
// that re-running a release workflow after a partial failure does
|
|
396
390
|
// not error on the already-published packages.
|
|
397
|
-
const isRepublishConflict = res.status === 409 ||
|
|
398
|
-
(res.status === 403 && /previously published/i.test(text));
|
|
391
|
+
const isRepublishConflict = res.status === 409 || (res.status === 403 && /previously published/i.test(text));
|
|
399
392
|
if (isRepublishConflict && tolerate) {
|
|
400
393
|
const out = {
|
|
401
394
|
ok: true,
|
|
@@ -433,11 +426,15 @@ async function packWorkspaceToBytes(wsDir) {
|
|
|
433
426
|
try {
|
|
434
427
|
(await import('node:fs')).rmSync(res.absolutePath);
|
|
435
428
|
}
|
|
436
|
-
catch {
|
|
429
|
+
catch {
|
|
430
|
+
/* best effort */
|
|
431
|
+
}
|
|
437
432
|
try {
|
|
438
433
|
(await import('node:fs')).rmdirSync(tmp);
|
|
439
434
|
}
|
|
440
|
-
catch {
|
|
435
|
+
catch {
|
|
436
|
+
/* best effort */
|
|
437
|
+
}
|
|
441
438
|
return bytes;
|
|
442
439
|
}
|
|
443
440
|
async function loadRewrittenManifest(wsDir, pkg) {
|
|
@@ -490,7 +487,9 @@ async function loadNpmrc(cwd) {
|
|
|
490
487
|
// The auth-token npmrc from actions/setup-node ships
|
|
491
488
|
// `_authToken=${NODE_AUTH_TOKEN}` as a literal placeholder; the env var
|
|
492
489
|
// is set on the publish step.
|
|
493
|
-
const merged = sources
|
|
490
|
+
const merged = sources
|
|
491
|
+
.join('\n')
|
|
492
|
+
.replace(/\$\{([A-Z_][A-Z0-9_]*)\}/gi, (_, name) => process.env[name] ?? '');
|
|
494
493
|
return parseNpmrc(merged);
|
|
495
494
|
}
|
|
496
495
|
function buildPublishPayload(opts) {
|
package/lib/commands/run.d.ts
CHANGED
package/lib/commands/run.js
CHANGED
|
@@ -108,9 +108,7 @@ async function runScript(script, extraArgs) {
|
|
|
108
108
|
// (stdout is always a pipe there, but the GHA log viewer renders ANSI
|
|
109
109
|
// fine). Respect user overrides: FORCE_COLOR=0 or NO_COLOR keeps
|
|
110
110
|
// colors off.
|
|
111
|
-
const colorEnv = process.env.FORCE_COLOR !== undefined || process.env.NO_COLOR !== undefined
|
|
112
|
-
? {}
|
|
113
|
-
: { FORCE_COLOR: '1' };
|
|
111
|
+
const colorEnv = process.env.FORCE_COLOR !== undefined || process.env.NO_COLOR !== undefined ? {} : { FORCE_COLOR: '1' };
|
|
114
112
|
const env = {
|
|
115
113
|
...process.env,
|
|
116
114
|
...colorEnv,
|
|
@@ -119,9 +117,7 @@ async function runScript(script, extraArgs) {
|
|
|
119
117
|
npm_package_name: pkg.name ?? '',
|
|
120
118
|
npm_package_version: pkg.version ?? '',
|
|
121
119
|
};
|
|
122
|
-
const fullCmd = extraArgs.length > 0
|
|
123
|
-
? `${literal} ${extraArgs.map(shellEscape).join(' ')}`
|
|
124
|
-
: literal;
|
|
120
|
+
const fullCmd = extraArgs.length > 0 ? `${literal} ${extraArgs.map(shellEscape).join(' ')}` : literal;
|
|
125
121
|
// ensureMainLoop() (called inside spawn) keeps GJS alive after the
|
|
126
122
|
// child exits — without an explicit process.exit() the success path
|
|
127
123
|
// would park the loop forever. The error path already exits.
|
|
@@ -76,9 +76,7 @@ export const selfUpdateCommand = {
|
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
if (args.check) {
|
|
79
|
-
console.log(currentVersion
|
|
80
|
-
? `Update available: v${currentVersion} → v${target}`
|
|
81
|
-
: `Install required: → v${target}`);
|
|
79
|
+
console.log(currentVersion ? `Update available: v${currentVersion} → v${target}` : `Install required: → v${target}`);
|
|
82
80
|
process.exit(1);
|
|
83
81
|
return;
|
|
84
82
|
}
|
package/lib/commands/showcase.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { discoverShowcases, findShowcase } from '../utils/discover-showcases.js';
|
|
2
|
-
import { runMinimalChecks, detectPackageManager, buildInstallCommand
|
|
2
|
+
import { runMinimalChecks, detectPackageManager, buildInstallCommand } from '../utils/check-system-deps.js';
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { readFileSync } from 'node:fs';
|