@gjsify/cli 0.51.1 → 0.53.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 +539 -493
- package/lib/actions/build.d.ts +22 -0
- package/lib/actions/build.js +37 -9
- package/lib/bundler-pick.d.ts +37 -1
- package/lib/bundler-pick.js +108 -17
- package/lib/cli-app.js +7 -2
- package/lib/commands/affected-classify.d.ts +7 -1
- package/lib/commands/affected-classify.js +66 -9
- package/lib/commands/affected-script-refs.d.ts +20 -0
- package/lib/commands/affected-script-refs.js +218 -0
- package/lib/commands/affected.js +24 -2
- package/lib/commands/env.d.ts +31 -0
- package/lib/commands/env.js +114 -0
- package/lib/commands/exec.d.ts +10 -0
- package/lib/commands/exec.js +208 -0
- package/lib/commands/flatpak/diff.js +8 -2
- package/lib/commands/flatpak/init.js +4 -1
- package/lib/commands/flatpak/sources.js +4 -3
- package/lib/commands/flatpak/sync-flathub.d.ts +2 -0
- package/lib/commands/flatpak/sync-flathub.js +149 -7
- package/lib/commands/flatpak/utils.d.ts +10 -0
- package/lib/commands/flatpak/utils.js +10 -0
- package/lib/commands/foreach.js +5 -97
- package/lib/commands/format.js +26 -6
- package/lib/commands/index.d.ts +4 -0
- package/lib/commands/index.js +4 -0
- package/lib/commands/install.d.ts +2 -2
- package/lib/commands/install.js +81 -5
- package/lib/commands/link.d.ts +13 -0
- package/lib/commands/link.js +243 -0
- package/lib/commands/onboard.js +18 -2
- package/lib/commands/publish.d.ts +34 -0
- package/lib/commands/publish.js +153 -8
- package/lib/commands/self-update.js +2 -1
- package/lib/commands/ship.js +19 -1
- package/lib/commands/test.d.ts +18 -9
- package/lib/commands/test.js +56 -11
- package/lib/commands/tsc.js +4 -4
- package/lib/commands/upgrade.d.ts +15 -0
- package/lib/commands/upgrade.js +76 -2
- package/lib/commands/webext/index.d.ts +2 -0
- package/lib/commands/webext/index.js +15 -0
- package/lib/commands/webext/subcommands.d.ts +16 -0
- package/lib/commands/webext/subcommands.js +204 -0
- package/lib/types/config-data.d.ts +2 -2
- package/lib/utils/bin-shim.d.ts +8 -2
- package/lib/utils/bin-shim.js +6 -5
- package/lib/utils/bundle-inputs.d.ts +84 -0
- package/lib/utils/bundle-inputs.js +268 -0
- package/lib/utils/check-system-deps.d.ts +5 -19
- package/lib/utils/check-system-deps.js +31 -94
- package/lib/utils/dev-link.d.ts +225 -0
- package/lib/utils/dev-link.js +684 -0
- package/lib/utils/exec-bin.d.ts +137 -0
- package/lib/utils/exec-bin.js +321 -0
- package/lib/utils/gi-runtime-paths.js +3 -2
- package/lib/utils/gitignore.d.ts +15 -0
- package/lib/utils/gitignore.js +178 -0
- package/lib/utils/gjsify-shim.d.ts +47 -0
- package/lib/utils/gjsify-shim.js +97 -8
- package/lib/utils/hang-watchdog.d.ts +95 -0
- package/lib/utils/hang-watchdog.js +194 -0
- package/lib/utils/install-backend-native.d.ts +33 -1
- package/lib/utils/install-backend-native.js +184 -20
- package/lib/utils/install-extraneous.d.ts +90 -0
- package/lib/utils/install-extraneous.js +120 -0
- package/lib/utils/install-lock.js +4 -25
- package/lib/utils/install-tarball-cache.d.ts +37 -9
- package/lib/utils/install-tarball-cache.js +59 -14
- package/lib/utils/native-skip-scan.d.ts +20 -0
- package/lib/utils/native-skip-scan.js +343 -0
- package/lib/utils/node-script.js +1 -1
- package/lib/utils/normalize-bundler-options.js +11 -0
- package/lib/utils/oxc-resolve.js +42 -3
- package/lib/utils/process-table.d.ts +22 -0
- package/lib/utils/process-table.js +188 -0
- package/lib/utils/prune-prefix.js +1 -1
- package/lib/utils/publish-pins.d.ts +38 -0
- package/lib/utils/publish-pins.js +47 -0
- package/lib/utils/publish-readback.d.ts +103 -17
- package/lib/utils/publish-readback.js +326 -47
- package/lib/utils/run-gjs.d.ts +15 -1
- package/lib/utils/run-gjs.js +98 -13
- package/lib/utils/run-node.d.ts +2 -0
- package/lib/utils/run-node.js +2 -1
- package/lib/utils/runtimes.js +14 -13
- package/lib/utils/ship/gzip.js +1 -1
- package/lib/utils/ship/icns.d.ts +14 -0
- package/lib/utils/ship/icns.js +83 -0
- package/lib/utils/ship/ico.d.ts +25 -0
- package/lib/utils/ship/ico.js +120 -0
- package/lib/utils/ship/icons.d.ts +197 -0
- package/lib/utils/ship/icons.js +399 -0
- package/lib/utils/ship/launcher.js +16 -1
- package/lib/utils/ship/layout.d.ts +32 -0
- package/lib/utils/ship/layout.js +112 -38
- package/lib/utils/ship/msi.d.ts +1 -1
- package/lib/utils/ship/msi.js +30 -5
- package/lib/utils/ship/pe-launcher.d.ts +18 -0
- package/lib/utils/ship/pe-launcher.js +125 -4
- package/lib/utils/ship/plan.d.ts +1 -34
- package/lib/utils/ship/plan.js +7 -56
- package/lib/utils/ship/plist.d.ts +20 -10
- package/lib/utils/ship/plist.js +29 -12
- package/lib/utils/ship/settings.js +3 -2
- package/lib/utils/spawn.d.ts +13 -0
- package/lib/utils/spawn.js +10 -5
- package/lib/utils/watch-loop.d.ts +15 -1
- package/lib/utils/watch-loop.js +9 -5
- package/lib/utils/webext/build.d.ts +42 -0
- package/lib/utils/webext/build.js +120 -0
- package/lib/utils/webext/config.d.ts +72 -0
- package/lib/utils/webext/config.js +234 -0
- package/lib/utils/webext/icons.d.ts +24 -0
- package/lib/utils/webext/icons.js +83 -0
- package/lib/utils/webext/launch.d.ts +32 -0
- package/lib/utils/webext/launch.js +77 -0
- package/lib/utils/webext/manifest.d.ts +53 -0
- package/lib/utils/webext/manifest.js +219 -0
- package/lib/utils/webext/pages.d.ts +26 -0
- package/lib/utils/webext/pages.js +61 -0
- package/lib/utils/webext/targets.d.ts +13 -0
- package/lib/utils/webext/targets.js +36 -0
- package/lib/utils/win32-command.d.ts +17 -0
- package/lib/utils/win32-command.js +21 -6
- package/package.json +20 -20
package/lib/commands/publish.js
CHANGED
|
@@ -48,7 +48,8 @@ import { buildPublishHeaders, escapePackageName } from '../utils/publish-headers
|
|
|
48
48
|
import { packWorkspace } from './pack.js';
|
|
49
49
|
import { getNpmTrustedToken, hasGithubOidcEnv, OidcExchangeError, OidcUnavailableError } from '../utils/npm-oidc.js';
|
|
50
50
|
import { diagnose404, is404DiagnosticCandidate } from '../utils/publish-diagnose.js';
|
|
51
|
-
import { DEFAULT_VERIFY_BUDGET_MS, formatUnconfirmedPublish, verifyPublishedVersion, } from '../utils/publish-readback.js';
|
|
51
|
+
import { DEFAULT_VERIFY_BUDGET_MS, formatUnconfirmedPublish, probePackageName, verifyPublishedVersion, } from '../utils/publish-readback.js';
|
|
52
|
+
import { collectWorkspacePins, REQUIRED_BLOCK } from '../utils/publish-pins.js';
|
|
52
53
|
import { loadNpmrc } from '../utils/load-npmrc.js';
|
|
53
54
|
import { OtpProvider, withOtpRetry, isOtpChallenge } from '../utils/npm-otp.js';
|
|
54
55
|
import { promptLine } from '../utils/prompt.js';
|
|
@@ -81,7 +82,7 @@ export const publishCommand = {
|
|
|
81
82
|
default: false,
|
|
82
83
|
})
|
|
83
84
|
.option('verify-timeout', {
|
|
84
|
-
description: 'Seconds to keep asking the registry for the just-published version before giving up. npm answers 2xx to a write it has only ACCEPTED: in the v0.46.0 release 19 of 199 packages were recorded 56-252s after their 2xx and one was never recorded at all, so a shorter window turns normal queueing into a false red. `0` disables the read-back — for a registry with no packument read path.',
|
|
85
|
+
description: 'Seconds to keep asking the registry for the just-published version before giving up. npm answers 2xx to a write it has only ACCEPTED: in the v0.46.0 release 19 of 199 packages were recorded 56-252s after their 2xx and one was never recorded at all, so a shorter window turns normal queueing into a false red. `0` disables the read-back — for a registry with no packument read path; the success line then reads `UNVERIFIED` and says so, because a publish nothing checked must never look like one that was checked.',
|
|
85
86
|
type: 'number',
|
|
86
87
|
default: DEFAULT_VERIFY_BUDGET_MS / 1000,
|
|
87
88
|
})
|
|
@@ -305,6 +306,55 @@ export async function publishWorkspace(input) {
|
|
|
305
306
|
// Base headers (npm-command: publish routing header etc.). The OTP is added
|
|
306
307
|
// per-PUT by `doPut` so `withOtpRetry` can retry with different codes.
|
|
307
308
|
const headers = buildPublishHeaders(url, { npmrc });
|
|
309
|
+
// 5. Do not PUT a tarball whose required intra-repo pins point at a name
|
|
310
|
+
// this registry does not have (#1713).
|
|
311
|
+
//
|
|
312
|
+
// The question is PRESENCE OF THE NAME, never a version, and that is what
|
|
313
|
+
// makes it safe to gate on: a version is eventually consistent for
|
|
314
|
+
// minutes after its own 2xx, so a version gate would manufacture a red on
|
|
315
|
+
// a publish that worked. A name does not move once its first version
|
|
316
|
+
// lands. `unknown` — a 5xx, a timeout, an auth wall — is not `absent` and
|
|
317
|
+
// never blocks.
|
|
318
|
+
const assumePresent = new Set(input.assumePresent ?? []);
|
|
319
|
+
const pins = collectWorkspacePins(pkg, rewrittenPkg).filter((pin) => !assumePresent.has(pin.name));
|
|
320
|
+
if (pins.length > 0) {
|
|
321
|
+
const probed = await Promise.all(pins.map(async (pin) => ({
|
|
322
|
+
pin,
|
|
323
|
+
presence: await probePackageName({
|
|
324
|
+
registry,
|
|
325
|
+
name: pin.name,
|
|
326
|
+
authorization: headers['authorization'],
|
|
327
|
+
}),
|
|
328
|
+
})));
|
|
329
|
+
const missing = [];
|
|
330
|
+
for (const { pin, presence } of probed) {
|
|
331
|
+
if (presence.state === 'present')
|
|
332
|
+
continue;
|
|
333
|
+
const where = `${pin.block}.${pin.name}@${pin.spec}`;
|
|
334
|
+
if (presence.state === 'unknown') {
|
|
335
|
+
console.error(`gjsify publish: could not establish whether ${pin.name} exists (${presence.detail}) — not blocking on ${where}`);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (pin.block !== REQUIRED_BLOCK) {
|
|
339
|
+
// npm and pnpm skip an unresolvable optional edge in silence;
|
|
340
|
+
// Yarn refuses the whole install. Loud, not fatal.
|
|
341
|
+
console.error(`gjsify publish: ${where} names a package ${registry} does not have — npm installs around it, Yarn (\`YN0035\`) does not`);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
missing.push(pin);
|
|
345
|
+
}
|
|
346
|
+
if (missing.length > 0) {
|
|
347
|
+
return {
|
|
348
|
+
ok: tolerateUntrustedNew,
|
|
349
|
+
action: 'blocked-missing-dependency',
|
|
350
|
+
name: packed.name,
|
|
351
|
+
version: packed.version,
|
|
352
|
+
registry,
|
|
353
|
+
tolerated: tolerateUntrustedNew,
|
|
354
|
+
missing,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
}
|
|
308
358
|
// Trusted Publishing (OIDC) path — `--trusted` forces it, `undefined`
|
|
309
359
|
// auto-detects from the GitHub OIDC env. Unused by `gjsify onboard` (local
|
|
310
360
|
// token auth), preserved verbatim for `gjsify publish` in CI.
|
|
@@ -414,7 +464,7 @@ export async function publishWorkspace(input) {
|
|
|
414
464
|
//
|
|
415
465
|
// NOT CONFIRMED YET — the other 19. Here the read-back waits out
|
|
416
466
|
// npm's own write queue, and those residual lags SUM TO 2554 s: a
|
|
417
|
-
// sweep that waited them all out
|
|
467
|
+
// sweep that waited them all out under the fatal default would have
|
|
418
468
|
// added ~43 min to that 33 min job. That single number decides the
|
|
419
469
|
// shape below.
|
|
420
470
|
//
|
|
@@ -438,7 +488,7 @@ export async function publishWorkspace(input) {
|
|
|
438
488
|
//
|
|
439
489
|
// Hence BOTH, split along that cost. The read-back runs per package
|
|
440
490
|
// everywhere and is FATAL by default — that is what turns the
|
|
441
|
-
// incident's green single-package job red, for up to
|
|
491
|
+
// incident's green single-package job red, for up to 600 s of
|
|
442
492
|
// patience on a job that otherwise ran 59 s. The
|
|
443
493
|
// 199-package sweep passes `--verify-defer --verify-timeout 5`: it
|
|
444
494
|
// still CONFIRMS nine packages in ten and still names the suspect
|
|
@@ -458,7 +508,7 @@ export async function publishWorkspace(input) {
|
|
|
458
508
|
// decoration: it is the only client-side measurement of when a
|
|
459
509
|
// version became RESOLVABLE, as against `time[version]`, which is
|
|
460
510
|
// when the registry recorded the write. The two are assumed equal by
|
|
461
|
-
// the 4.2-minute figure and the
|
|
511
|
+
// the 4.2-minute figure and the default window, and nothing had ever
|
|
462
512
|
// measured the gap. The pre-registered comparison — and what each
|
|
463
513
|
// outcome would change — is in #1509's description; this line, under
|
|
464
514
|
// GJSIFY_PUBLISH_DEBUG (which release.yml sets), is its input. Note
|
|
@@ -534,6 +584,7 @@ export async function publishWorkspace(input) {
|
|
|
534
584
|
name: packed.name,
|
|
535
585
|
version: packed.version,
|
|
536
586
|
status: res.status,
|
|
587
|
+
registry: registryClean,
|
|
537
588
|
readback,
|
|
538
589
|
};
|
|
539
590
|
}
|
|
@@ -568,9 +619,68 @@ export async function publishWorkspace(input) {
|
|
|
568
619
|
* re-checks the same set afterwards. It is policy, so it lives here and not in
|
|
569
620
|
* {@link publishWorkspace}, which stays a pure fact-finder.
|
|
570
621
|
*/
|
|
622
|
+
/**
|
|
623
|
+
* What the success line SAYS it established — the half of the read-back that
|
|
624
|
+
* was missing.
|
|
625
|
+
*
|
|
626
|
+
* AN INSTRUMENT STATES WHAT IT MEASURED, and `+ name@version` states nothing.
|
|
627
|
+
* It is the identical string from a CLI that polled the registry and read a
|
|
628
|
+
* tarball back, from one run with `--verify-timeout 0`, and from any
|
|
629
|
+
* `@gjsify/cli` older than v0.47.0, which had no read-back at all. That is not a
|
|
630
|
+
* hypothetical: ts-for-gir's v5.1.0 release (run 34899018849) printed twelve
|
|
631
|
+
* `+ <name>@5.1.0` lines with `GJSIFY_PUBLISH_DEBUG=1` set and not one
|
|
632
|
+
* `response:` or read-back line among them, because the job's
|
|
633
|
+
* `PATH="$WS_PATH/node_modules/.bin:$PATH"` put that workspace's pinned
|
|
634
|
+
* `@gjsify/cli@^0.44.0` ahead of the 0.51.1 it had just bootstrapped. Three of
|
|
635
|
+
* the twelve were not on the registry. The marker could not have told anyone,
|
|
636
|
+
* and nobody could have known to look.
|
|
637
|
+
*
|
|
638
|
+
* So every terminal success marker now carries its verification clause, and an
|
|
639
|
+
* unverified one says UNVERIFIED in a word a log sweep can grep for. A bare
|
|
640
|
+
* `+ name@version` with no clause is now itself the signature of a CLI too old
|
|
641
|
+
* to check.
|
|
642
|
+
*/
|
|
643
|
+
function describeVerification(readback, registry) {
|
|
644
|
+
if (!readback?.confirmed) {
|
|
645
|
+
return {
|
|
646
|
+
verified: false,
|
|
647
|
+
clause: '(UNVERIFIED — read-back disabled by --verify-timeout 0)',
|
|
648
|
+
json: null,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
const seconds = (readback.elapsedMs / 1000).toFixed(1);
|
|
652
|
+
return {
|
|
653
|
+
verified: true,
|
|
654
|
+
clause: `(verified on ${registry} — ${readback.attempts} probe(s), ${seconds}s)`,
|
|
655
|
+
json: {
|
|
656
|
+
registry,
|
|
657
|
+
verdict: readback.verdict,
|
|
658
|
+
probes: readback.attempts,
|
|
659
|
+
elapsedMs: readback.elapsedMs,
|
|
660
|
+
tarball: readback.last.state === 'present' ? readback.last.tarball : null,
|
|
661
|
+
},
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* An unverified SUCCESS gets an Actions annotation too.
|
|
666
|
+
*
|
|
667
|
+
* `--verify-timeout 0` is a legitimate escape hatch for a registry with no
|
|
668
|
+
* packument read path, and it restores exactly the behaviour every incident in
|
|
669
|
+
* this file came out of. A disabled verifier that announces itself is a
|
|
670
|
+
* decision; one that stays quiet is the original defect wearing a hat — and the
|
|
671
|
+
* channel is the one `--verify-defer` already uses, beside the job rather than
|
|
672
|
+
* in a log nobody re-reads. stderr, because `--json` owns stdout.
|
|
673
|
+
*/
|
|
674
|
+
function warnUnverified(name, version, registry) {
|
|
675
|
+
if (!process.env.GITHUB_ACTIONS)
|
|
676
|
+
return;
|
|
677
|
+
process.stderr.write(`::warning title=Publish unverified::${name}@${version} was published with the read-back disabled ` +
|
|
678
|
+
`(--verify-timeout 0) — nothing confirmed that ${registry} serves it\n`);
|
|
679
|
+
}
|
|
571
680
|
function reportPublishOutcome(outcome, asJson, deferUnconfirmed = false) {
|
|
572
681
|
switch (outcome.action) {
|
|
573
682
|
case 'published': {
|
|
683
|
+
const verification = describeVerification(outcome.readback, outcome.registry);
|
|
574
684
|
const out = {
|
|
575
685
|
ok: true,
|
|
576
686
|
name: outcome.name,
|
|
@@ -580,11 +690,15 @@ function reportPublishOutcome(outcome, asJson, deferUnconfirmed = false) {
|
|
|
580
690
|
integrity: outcome.integrity,
|
|
581
691
|
tag: outcome.tag,
|
|
582
692
|
registry: outcome.registry,
|
|
693
|
+
verified: verification.verified,
|
|
694
|
+
verification: verification.json,
|
|
583
695
|
};
|
|
584
696
|
if (asJson)
|
|
585
697
|
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
|
|
586
698
|
else
|
|
587
|
-
process.stdout.write(`+ ${outcome.name}@${outcome.version}\n`);
|
|
699
|
+
process.stdout.write(`+ ${outcome.name}@${outcome.version} ${verification.clause}\n`);
|
|
700
|
+
if (!verification.verified)
|
|
701
|
+
warnUnverified(outcome.name, outcome.version, outcome.registry);
|
|
588
702
|
return;
|
|
589
703
|
}
|
|
590
704
|
case 'publish-unconfirmed': {
|
|
@@ -602,6 +716,9 @@ function reportPublishOutcome(outcome, asJson, deferUnconfirmed = false) {
|
|
|
602
716
|
elapsedMs: outcome.readback.elapsedMs,
|
|
603
717
|
readbackUrl: outcome.readback.url,
|
|
604
718
|
readbackState: outcome.readback.last.state,
|
|
719
|
+
verdict: outcome.readback.verdict,
|
|
720
|
+
verdictDetail: outcome.readback.verdictDetail,
|
|
721
|
+
recordedAt: outcome.readback.recordedAt ?? null,
|
|
605
722
|
deferred: deferUnconfirmed,
|
|
606
723
|
}, null, 2)}\n`);
|
|
607
724
|
}
|
|
@@ -619,24 +736,31 @@ function reportPublishOutcome(outcome, asJson, deferUnconfirmed = false) {
|
|
|
619
736
|
const claimed = outcome.claim === 'already-published' ? 'was reported already published' : 'was accepted';
|
|
620
737
|
process.stderr.write(`::warning title=Publish unconfirmed::${outcome.name}@${outcome.version} ${claimed} ` +
|
|
621
738
|
`(HTTP ${outcome.putStatus}) but did not resolve on ${outcome.registry} within ` +
|
|
622
|
-
`${(outcome.readback.elapsedMs / 1000).toFixed(1)}s —
|
|
739
|
+
`${(outcome.readback.elapsedMs / 1000).toFixed(1)}s [${outcome.readback.verdict}] — ` +
|
|
740
|
+
'the release-closure job must confirm it\n');
|
|
623
741
|
}
|
|
624
742
|
return;
|
|
625
743
|
}
|
|
626
744
|
return process.exit(1);
|
|
627
745
|
}
|
|
628
746
|
case 'republish-tolerated': {
|
|
747
|
+
const verification = describeVerification(outcome.readback, outcome.registry);
|
|
629
748
|
const out = {
|
|
630
749
|
ok: true,
|
|
631
750
|
action: 'republish-tolerated',
|
|
632
751
|
name: outcome.name,
|
|
633
752
|
version: outcome.version,
|
|
634
753
|
status: outcome.status,
|
|
754
|
+
registry: outcome.registry,
|
|
755
|
+
verified: verification.verified,
|
|
756
|
+
verification: verification.json,
|
|
635
757
|
};
|
|
636
758
|
if (asJson)
|
|
637
759
|
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
|
|
638
760
|
else
|
|
639
|
-
process.stdout.write(`= ${outcome.name}@${outcome.version} (already published, tolerated)\n`);
|
|
761
|
+
process.stdout.write(`= ${outcome.name}@${outcome.version} (already published, tolerated) ${verification.clause}\n`);
|
|
762
|
+
if (!verification.verified)
|
|
763
|
+
warnUnverified(outcome.name, outcome.version, outcome.registry);
|
|
640
764
|
return;
|
|
641
765
|
}
|
|
642
766
|
case 'skipped-untrusted-new': {
|
|
@@ -654,6 +778,27 @@ function reportPublishOutcome(outcome, asJson, deferUnconfirmed = false) {
|
|
|
654
778
|
}
|
|
655
779
|
return;
|
|
656
780
|
}
|
|
781
|
+
case 'blocked-missing-dependency': {
|
|
782
|
+
const edges = outcome.missing.map((m) => `${m.block}.${m.name}@${m.spec}`).join(', ');
|
|
783
|
+
if (asJson) {
|
|
784
|
+
process.stdout.write(`${JSON.stringify({
|
|
785
|
+
ok: outcome.ok,
|
|
786
|
+
action: 'blocked-missing-dependency',
|
|
787
|
+
name: outcome.name,
|
|
788
|
+
version: outcome.version,
|
|
789
|
+
registry: outcome.registry,
|
|
790
|
+
tolerated: outcome.tolerated,
|
|
791
|
+
missing: outcome.missing,
|
|
792
|
+
}, null, 2)}\n`);
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
process.stdout.write(`~ ${outcome.name}@${outcome.version} (NOT published — ${outcome.missing.length} required pin(s) name a package ${outcome.registry} does not have: ${edges})\n`);
|
|
796
|
+
}
|
|
797
|
+
console.error(`gjsify publish: every consumer install of ${outcome.name} would fail with \`npm error 404\`, on every package manager — a required edge is not skippable. Bootstrap the target first (docs/publishing.md § New \`@gjsify/*\` package), then re-run. See #1713.`);
|
|
798
|
+
if (!outcome.tolerated)
|
|
799
|
+
return process.exit(1);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
657
802
|
case 'otp-required': {
|
|
658
803
|
console.error(`gjsify publish: npm requires a 2FA one-time code — re-run with \`--otp <code>\``);
|
|
659
804
|
return process.exit(1);
|
|
@@ -27,6 +27,7 @@ import { fetchPackument } from '@gjsify/npm-registry';
|
|
|
27
27
|
import { installPackages, makeProgressReporter } from '../utils/install-backend.js';
|
|
28
28
|
import { classifyInstall } from '../utils/install-provenance.js';
|
|
29
29
|
import { defaultGlobalLayout, globalLayoutIsDefault, hasBundlerEngineInstalled, installGjsEnginePackages, linkGlobalBins, resolveBinOnPath, } from '../utils/install-global.js';
|
|
30
|
+
import { pathWithoutSelfShim } from '../utils/gjsify-shim.js';
|
|
30
31
|
import { resolveHostPlatform } from '../utils/platform-check.js';
|
|
31
32
|
import { pruneAfterInstall } from '../utils/prune-prefix.js';
|
|
32
33
|
const PACKAGE_NAME = '@gjsify/cli';
|
|
@@ -210,7 +211,7 @@ export const selfUpdateCommand = {
|
|
|
210
211
|
const verdict = verifyPathResolution({
|
|
211
212
|
binName: BIN_NAME,
|
|
212
213
|
binDir: layout.binDir,
|
|
213
|
-
resolvedBin: resolveBinOnPath(BIN_NAME),
|
|
214
|
+
resolvedBin: resolveBinOnPath(BIN_NAME, { pathValue: pathWithoutSelfShim() }),
|
|
214
215
|
runningVersion: currentVersion,
|
|
215
216
|
targetVersion: target,
|
|
216
217
|
});
|
package/lib/commands/ship.js
CHANGED
|
@@ -35,6 +35,7 @@ import { discoverPayload } from '../utils/ship/discover.js';
|
|
|
35
35
|
import { appDirFor, appDirPayload, appImageHostRequirements, appImageRuntimeNotice, buildAppImage, findPinnedRuntime, stampAppDirTimes, } from '../utils/ship/appimage.js';
|
|
36
36
|
import { buildDmgImage, dmgVolumeDir, dmgVolumeName } from '../utils/ship/dmg.js';
|
|
37
37
|
import { buildFlatpakBundle } from '../utils/ship/flatpak.js';
|
|
38
|
+
import { iconSizes, resolveAppIcon } from '../utils/ship/icons.js';
|
|
38
39
|
import { localizeMetadata } from '../utils/ship/localize-metadata.js';
|
|
39
40
|
import { assertHostCanFinish, assertKindCanPack, assertToolsInstalled, canCarryTicket, configuredFormats, defaultFormatIds, formatIdsFor, resolveFormats, windowsProgramDirName, FORMAT_IDS, FORMATS, } from '../utils/ship/formats.js';
|
|
40
41
|
import { assertLayoutSupportsArch, hostLayout, layoutForOs, place, placeStage, resolveLayout, LAYOUT_NAMES, } from '../utils/ship/layout.js';
|
|
@@ -433,7 +434,24 @@ async function assemble(args) {
|
|
|
433
434
|
workDir: join(outRoot, 'schemas'),
|
|
434
435
|
})),
|
|
435
436
|
];
|
|
436
|
-
|
|
437
|
+
// THE APPLICATION ICON, rendered once for the sizes this layout's writers
|
|
438
|
+
// embed — six for the `.exe`'s resource directory, seven for the `.icns` —
|
|
439
|
+
// and nothing for a CLI or for Linux, where the theme carries the SVG the
|
|
440
|
+
// project declared. `resolveAppIcon` REFUSES an app that has no source for a
|
|
441
|
+
// size rather than staging without it: the released artifacts this closes
|
|
442
|
+
// shipped for a year with the generic icon on two platforms, and the only
|
|
443
|
+
// reason was that nothing said so (`utils/ship/icons.ts`).
|
|
444
|
+
const icon = settings.kind === 'app' && layout.icon !== undefined
|
|
445
|
+
? await resolveAppIcon({
|
|
446
|
+
iconFiles: settings.iconFiles,
|
|
447
|
+
appId: settings.appId,
|
|
448
|
+
sizes: layout.icon.sizes,
|
|
449
|
+
target: layout.icon.writes,
|
|
450
|
+
})
|
|
451
|
+
: undefined;
|
|
452
|
+
if (icon !== undefined)
|
|
453
|
+
console.log(`${LOG} icon: ${iconSizes(icon).join('/')} px, ${icon.source}`);
|
|
454
|
+
const staged = placeStage(layout, { ...settings, icon }, planned, runtime.files);
|
|
437
455
|
writeStage(stageDir, staged);
|
|
438
456
|
console.log(`${LOG} staged ${staged.length} file(s) for ${layout.name} in ${relative(projectDir, stageDir)}/`);
|
|
439
457
|
if (args.verbose)
|
package/lib/commands/test.d.ts
CHANGED
|
@@ -10,16 +10,25 @@ interface TestOptions {
|
|
|
10
10
|
}
|
|
11
11
|
export declare const testCommand: Command<unknown, TestOptions>;
|
|
12
12
|
/**
|
|
13
|
-
* True when `outfile` is at least as new as every
|
|
14
|
-
*
|
|
13
|
+
* True when `outfile` is at least as new as every input of the build that
|
|
14
|
+
* wrote it.
|
|
15
15
|
*
|
|
16
|
-
* The set is
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
16
|
+
* The set is the union of two answers, because neither covers the question
|
|
17
|
+
* alone: {@link packageBuildInputs} — what may this PACKAGE's build read, the
|
|
18
|
+
* same walk the build cache hashes — and the manifest the build itself wrote
|
|
19
|
+
* beside `outfile` ({@link readBundleInputs}), what this build DID read. The
|
|
20
|
+
* second is what reaches a workspace package through a `node_modules` symlink
|
|
21
|
+
* into a sibling directory: the package walk is blind to it by construction,
|
|
22
|
+
* so an edit there left the bundle "fresh" and the run reported on code that
|
|
23
|
+
* was no longer on disk — a stale green, worse than a slow build. The first is
|
|
24
|
+
* what the walk alone never managed: it was `dirname(entry)`, `tests/` for any
|
|
25
|
+
* package whose suite lives there, so `src/**` was never seen and `gjsify test`
|
|
26
|
+
* reran the previous build and reported on it, in both directions inside one
|
|
27
|
+
* dependency bump (#1651).
|
|
28
|
+
*
|
|
29
|
+
* A missing manifest is NOT an answer: it is the pre-recorder shape, and the
|
|
30
|
+
* package walk then decides alone — never weaker than before, and the next
|
|
31
|
+
* build writes one.
|
|
23
32
|
*
|
|
24
33
|
* `outdir` is subtracted as this command's own output; `entry` is added
|
|
25
34
|
* because `--entry ../shared/test.mts` may point outside the package.
|
package/lib/commands/test.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// exit codes.
|
|
8
8
|
import { existsSync, statSync } from 'node:fs';
|
|
9
9
|
import { join, resolve, relative, sep } from 'node:path';
|
|
10
|
+
import { newestBundleInputMtimeMs, readBundleInputs, createBundleInputsRecorder } from '../utils/bundle-inputs.js';
|
|
10
11
|
import { newestDepSignalMtimeMs, newestInputMtimeMs, packageBuildInputs } from '../utils/package-inputs.js';
|
|
11
12
|
import { isRuntimeAvailable, RUNTIMES } from '../utils/runtimes.js';
|
|
12
13
|
import { nodeBinary } from '../utils/run-node.js';
|
|
@@ -180,13 +181,39 @@ async function buildTestBundle(entry, outfile, runtime, verbose) {
|
|
|
180
181
|
// bundler.output.file from the surrounding project don't redirect the
|
|
181
182
|
// bundle elsewhere.
|
|
182
183
|
configData.library = { ...configData.library };
|
|
184
|
+
// Record what this build READ, so the next `isFresh` can judge against the
|
|
185
|
+
// bundle's real dependencies rather than this package's tree. A recorder
|
|
186
|
+
// rides the same plugin chain a user's `bundler.plugins` entry does, and
|
|
187
|
+
// the auto-globals pre-build pass therefore feeds it too — a superset of
|
|
188
|
+
// the final graph, which errs towards a rebuild and never towards a stale
|
|
189
|
+
// green. Appended last so it observes whatever the earlier hooks loaded.
|
|
190
|
+
const recorder = createBundleInputsRecorder(outfile);
|
|
183
191
|
configData.bundler = {
|
|
184
192
|
...configData.bundler,
|
|
185
193
|
input: [entry],
|
|
186
194
|
output: { ...configData.bundler?.output, file: outfile },
|
|
195
|
+
plugins: [...(configData.bundler?.plugins ?? []), recorder.plugin],
|
|
187
196
|
};
|
|
188
197
|
const action = new BuildAction(configData);
|
|
189
|
-
|
|
198
|
+
// `onWatchFiles` is the half the transform observer cannot reach: a file a
|
|
199
|
+
// plugin read itself and declared (a stylesheet's `@import` chain). The
|
|
200
|
+
// recorder takes the union — npm reports its module set too, the native
|
|
201
|
+
// engine reports nothing but the declarations, and each engine alone would
|
|
202
|
+
// leave a class of input unrecorded.
|
|
203
|
+
await action.start({
|
|
204
|
+
app: runtime,
|
|
205
|
+
library: false,
|
|
206
|
+
onWatchFiles: (watch) => recorder.addWatchFiles(watch),
|
|
207
|
+
});
|
|
208
|
+
// Written AFTER the build resolves, and that ordering is the whole of what
|
|
209
|
+
// a failure leaves behind: a build that fails BEFORE writing an artifact
|
|
210
|
+
// (parse, resolve, a plugin hook) throws out of `start()` and never reaches
|
|
211
|
+
// this line, so the previous manifest still describes the previous bundle.
|
|
212
|
+
// A failure in a POST-WRITE gate is the other case and is not ours to
|
|
213
|
+
// paper over: `actions/build.ts` has already replaced the bundle, the
|
|
214
|
+
// manifest is rewritten beside it, and both then describe the same build —
|
|
215
|
+
// the next run re-runs the failing bundle rather than the one before it.
|
|
216
|
+
recorder.write();
|
|
190
217
|
}
|
|
191
218
|
/** Run a single test bundle and reject on non-zero exit. */
|
|
192
219
|
async function runTestBundle(outfile, runtime) {
|
|
@@ -206,16 +233,25 @@ async function runTestBundle(outfile, runtime) {
|
|
|
206
233
|
throw new Error(`node exited with ${describeExit(result)}`);
|
|
207
234
|
}
|
|
208
235
|
/**
|
|
209
|
-
* True when `outfile` is at least as new as every
|
|
210
|
-
*
|
|
236
|
+
* True when `outfile` is at least as new as every input of the build that
|
|
237
|
+
* wrote it.
|
|
238
|
+
*
|
|
239
|
+
* The set is the union of two answers, because neither covers the question
|
|
240
|
+
* alone: {@link packageBuildInputs} — what may this PACKAGE's build read, the
|
|
241
|
+
* same walk the build cache hashes — and the manifest the build itself wrote
|
|
242
|
+
* beside `outfile` ({@link readBundleInputs}), what this build DID read. The
|
|
243
|
+
* second is what reaches a workspace package through a `node_modules` symlink
|
|
244
|
+
* into a sibling directory: the package walk is blind to it by construction,
|
|
245
|
+
* so an edit there left the bundle "fresh" and the run reported on code that
|
|
246
|
+
* was no longer on disk — a stale green, worse than a slow build. The first is
|
|
247
|
+
* what the walk alone never managed: it was `dirname(entry)`, `tests/` for any
|
|
248
|
+
* package whose suite lives there, so `src/**` was never seen and `gjsify test`
|
|
249
|
+
* reran the previous build and reported on it, in both directions inside one
|
|
250
|
+
* dependency bump (#1651).
|
|
211
251
|
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* the bundle "fresh" and `gjsify test` reran the previous build and reported
|
|
216
|
-
* on it. That went both ways inside one dependency bump — a green run
|
|
217
|
-
* measuring a version that was no longer installed, then a red one after the
|
|
218
|
-
* source had already been repaired (#1651).
|
|
252
|
+
* A missing manifest is NOT an answer: it is the pre-recorder shape, and the
|
|
253
|
+
* package walk then decides alone — never weaker than before, and the next
|
|
254
|
+
* build writes one.
|
|
219
255
|
*
|
|
220
256
|
* `outdir` is subtracted as this command's own output; `entry` is added
|
|
221
257
|
* because `--entry ../shared/test.mts` may point outside the package.
|
|
@@ -229,7 +265,16 @@ export function isFresh(outfile, entry, cwd, outdir) {
|
|
|
229
265
|
const outdirRel = relative(cwd, outdir).split(sep).join('/');
|
|
230
266
|
if (outdirRel !== '' && !outdirRel.startsWith('../'))
|
|
231
267
|
extraOutputs.push(outdirRel);
|
|
232
|
-
const
|
|
268
|
+
const bundleInputs = readBundleInputs(outfile);
|
|
269
|
+
// A build whose engine could report NO watch list never entered a file
|
|
270
|
+
// only a PLUGIN read — a stylesheet's `@import` chain, an inlined
|
|
271
|
+
// `readFileSync` — into the set below, and no mtime comparison can
|
|
272
|
+
// recover it. Judging such a bundle fresh IS the stale green, so the
|
|
273
|
+
// answer is no: rebuild, on every run, and say why. `bundler-pick.ts`
|
|
274
|
+
// prints the skew once; the manifest carries it across processes.
|
|
275
|
+
if (bundleInputs !== null && !bundleInputs.watchList)
|
|
276
|
+
return false;
|
|
277
|
+
const newest = Math.max(newestInputMtimeMs(packageBuildInputs(cwd, { extraOutputs })), bundleInputs === null ? 0 : newestBundleInputMtimeMs(bundleInputs.inputs), statSync(entry).mtimeMs,
|
|
233
278
|
// A dependency bump changes no file in the package — and that is
|
|
234
279
|
// the shape #1651 was found in. `node_modules` is far too large
|
|
235
280
|
// to stat per run, so the lockfile stands in for it.
|
package/lib/commands/tsc.js
CHANGED
|
@@ -128,10 +128,10 @@ export const tscCommand = {
|
|
|
128
128
|
// Run upstream `typescript` under Node — the Node fallback.
|
|
129
129
|
//
|
|
130
130
|
// `nodeBinary()`, NEVER a bare `process.execPath`: under the GJS bundle
|
|
131
|
-
// `process.execPath` is the GJS interpreter (
|
|
132
|
-
// `
|
|
133
|
-
// TypeScript's CommonJS CLI to
|
|
134
|
-
// reference — or, where execPath resolves to a gjsify launcher instead,
|
|
131
|
+
// `process.execPath` is the GJS interpreter (`hostExecPath()`:
|
|
132
|
+
// `/proc/self/exe` on Linux, the PATH lookup of `gjs` on macOS), so
|
|
133
|
+
// `spawn(process.execPath, [tsc.js])` hands TypeScript's CommonJS CLI to
|
|
134
|
+
// GJS, which dies on the first `module` reference — or, where execPath resolves to a gjsify launcher instead,
|
|
135
135
|
// re-executes the CLI with the tsc entry as an unknown argument. Both
|
|
136
136
|
// were reached from a COLD tree: `@gjsify/tsc`'s `dist/tsc.gjs.mjs` is a
|
|
137
137
|
// build output, and `build:infra` builds `@gjsify/create-app` (its first
|
|
@@ -29,6 +29,21 @@ export declare const upgradeCommand: Command<unknown, UpgradeOptions>;
|
|
|
29
29
|
* Returns the number of offending declarations so the caller can exit once for both arms.
|
|
30
30
|
*/
|
|
31
31
|
export declare function reportInexactRanges(groups: readonly DependencyGroup[]): number;
|
|
32
|
+
/**
|
|
33
|
+
* Check that every lockfile resolution satisfies the ranges declared for it.
|
|
34
|
+
*
|
|
35
|
+
* `gjsify upgrade --check` compares declared ranges against each other across
|
|
36
|
+
* workspaces. It does NOT compare the resolution against any of them, so a
|
|
37
|
+
* lockfile entry that violates every declaration in the repo passes silently.
|
|
38
|
+
*
|
|
39
|
+
* This check reads the lockfile, resolves each entry's version, and verifies
|
|
40
|
+
* it satisfies at least one declared range for that dependency.
|
|
41
|
+
*
|
|
42
|
+
* @param groups dependency groups with declared ranges
|
|
43
|
+
* @param cwd working directory
|
|
44
|
+
* @returns number of violations
|
|
45
|
+
*/
|
|
46
|
+
export declare function checkLockfileRangeViolations(groups: readonly DependencyGroup[], cwd: string): number;
|
|
32
47
|
/**
|
|
33
48
|
* The range a dependency will be WRITTEN at.
|
|
34
49
|
*
|
package/lib/commands/upgrade.js
CHANGED
|
@@ -36,7 +36,7 @@ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
|
36
36
|
import { join, resolve } from 'node:path';
|
|
37
37
|
import { homedir } from 'node:os';
|
|
38
38
|
import { createInterface } from 'node:readline/promises';
|
|
39
|
-
import { parse } from '@gjsify/semver';
|
|
39
|
+
import { parse, satisfies } from '@gjsify/semver';
|
|
40
40
|
import { DEFAULT_REGISTRY, fetchPackument, parseNpmrc } from '@gjsify/npm-registry';
|
|
41
41
|
import { discoverWorkspaces, filterWorkspaces } from '@gjsify/workspace';
|
|
42
42
|
import { findWorkspaceRoot } from '../utils/workspace-root.js';
|
|
@@ -325,7 +325,81 @@ export function reportInexactRanges(groups) {
|
|
|
325
325
|
}
|
|
326
326
|
return total;
|
|
327
327
|
}
|
|
328
|
-
|
|
328
|
+
/**
|
|
329
|
+
* Check that every lockfile resolution satisfies the ranges declared for it.
|
|
330
|
+
*
|
|
331
|
+
* `gjsify upgrade --check` compares declared ranges against each other across
|
|
332
|
+
* workspaces. It does NOT compare the resolution against any of them, so a
|
|
333
|
+
* lockfile entry that violates every declaration in the repo passes silently.
|
|
334
|
+
*
|
|
335
|
+
* This check reads the lockfile, resolves each entry's version, and verifies
|
|
336
|
+
* it satisfies at least one declared range for that dependency.
|
|
337
|
+
*
|
|
338
|
+
* @param groups dependency groups with declared ranges
|
|
339
|
+
* @param cwd working directory
|
|
340
|
+
* @returns number of violations
|
|
341
|
+
*/
|
|
342
|
+
export function checkLockfileRangeViolations(groups, cwd) {
|
|
343
|
+
const lockPath = join(cwd, 'gjsify-lock.json');
|
|
344
|
+
if (!existsSync(lockPath))
|
|
345
|
+
return 0;
|
|
346
|
+
let lock;
|
|
347
|
+
try {
|
|
348
|
+
lock = JSON.parse(readFileSync(lockPath, 'utf-8'));
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
if (!lock.packages)
|
|
354
|
+
return 0;
|
|
355
|
+
// Build a map: dep name -> set of declared ranges
|
|
356
|
+
const declaredRanges = new Map();
|
|
357
|
+
for (const g of groups) {
|
|
358
|
+
const ranges = declaredRanges.get(g.name) ?? new Set();
|
|
359
|
+
for (const occ of g.occurrences) {
|
|
360
|
+
ranges.add(occ.currentRange);
|
|
361
|
+
}
|
|
362
|
+
declaredRanges.set(g.name, ranges);
|
|
363
|
+
}
|
|
364
|
+
let violations = 0;
|
|
365
|
+
for (const [key, entry] of Object.entries(lock.packages)) {
|
|
366
|
+
if (!entry.version)
|
|
367
|
+
continue;
|
|
368
|
+
// Only check direct dependencies — the single-segment "node_modules/<name>" form.
|
|
369
|
+
// A deep transitive resolution (e.g. "node_modules/a/node_modules/b") is pinned by
|
|
370
|
+
// its parent, not by the root's declared range, and must not be checked against it.
|
|
371
|
+
const parts = key.split('node_modules/');
|
|
372
|
+
if (parts.length !== 2)
|
|
373
|
+
continue;
|
|
374
|
+
const depName = parts[1];
|
|
375
|
+
const ranges = declaredRanges.get(depName);
|
|
376
|
+
if (!ranges || ranges.size === 0)
|
|
377
|
+
continue;
|
|
378
|
+
// Check if the resolved version satisfies at least one declared range
|
|
379
|
+
let satisfied = false;
|
|
380
|
+
for (const range of ranges) {
|
|
381
|
+
try {
|
|
382
|
+
if (entry.version && satisfies(entry.version, range)) {
|
|
383
|
+
satisfied = true;
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
// Invalid range, skip
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (!satisfied) {
|
|
392
|
+
console.error(` ${depName}: lockfile has ${entry.version}, but declared ranges are ${[...ranges].join(', ')}`);
|
|
393
|
+
violations++;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (violations > 0) {
|
|
397
|
+
console.error(`gjsify upgrade --check: FAIL. ${violations} lockfile entr${violations === 1 ? 'y' : 'ies'} violate declared ranges:\n`);
|
|
398
|
+
}
|
|
399
|
+
return violations;
|
|
400
|
+
}
|
|
401
|
+
function runCheckMode(groups, exact = false, cwd = process.cwd()) {
|
|
402
|
+
const lockfileViolations = checkLockfileRangeViolations(groups, cwd);
|
|
329
403
|
const inexact = exact ? reportInexactRanges(groups) : 0;
|
|
330
404
|
const inconsistencies = findInconsistencies(groups);
|
|
331
405
|
if (inconsistencies.length === 0 && inexact > 0) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// `gjsify webext` — yargs subcommand-group dispatcher (ADR 0077).
|
|
2
|
+
//
|
|
3
|
+
// Wires {build, zip, dev}; each is a self-contained `Command<>` in `subcommands.ts`,
|
|
4
|
+
// the same composition `flatpak/index.ts` uses.
|
|
5
|
+
import { webextBuildCommand, webextDevCommand, webextZipCommand } from './subcommands.js';
|
|
6
|
+
export const webextCommand = {
|
|
7
|
+
command: 'webext <subcommand>',
|
|
8
|
+
description: 'Browser extensions: build, zip and develop one WebExtension for Chrome, Edge, Firefox and Safari.',
|
|
9
|
+
builder: (yargs) => yargs
|
|
10
|
+
.command(webextBuildCommand.command, webextBuildCommand.description, webextBuildCommand.builder, webextBuildCommand.handler)
|
|
11
|
+
.command(webextZipCommand.command, webextZipCommand.description, webextZipCommand.builder, webextZipCommand.handler)
|
|
12
|
+
.command(webextDevCommand.command, webextDevCommand.description, webextDevCommand.builder, webextDevCommand.handler)
|
|
13
|
+
.demandCommand(1)
|
|
14
|
+
.strict(),
|
|
15
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Command } from '../../types/index.js';
|
|
2
|
+
interface WebextArgs {
|
|
3
|
+
target?: string[];
|
|
4
|
+
outDir?: string;
|
|
5
|
+
define: string[];
|
|
6
|
+
mode?: string;
|
|
7
|
+
launch: boolean;
|
|
8
|
+
browserBinary?: string;
|
|
9
|
+
profile?: string;
|
|
10
|
+
headless: boolean;
|
|
11
|
+
debounce: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const webextBuildCommand: Command<unknown, WebextArgs>;
|
|
14
|
+
export declare const webextZipCommand: Command<unknown, WebextArgs>;
|
|
15
|
+
export declare const webextDevCommand: Command<unknown, WebextArgs>;
|
|
16
|
+
export {};
|