extension 4.1.20 → 4.1.21
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/README.md +18 -16
- package/dist/84.cjs +2 -1
- package/dist/browsers.cjs +114 -70
- package/dist/cli.cjs +264 -115
- package/dist/extension/browsers/__spec__/safari-fake-tools.d.ts +27 -0
- package/dist/extension/browsers/browsers-lib/messages.d.ts +1 -0
- package/dist/extension/browsers/browsers-lib/write-json-atomic.d.ts +1 -0
- package/dist/extension/browsers/index.d.ts +1 -1
- package/dist/extension/browsers/run-safari/safari-launch/index.d.ts +1 -1
- package/dist/extension/browsers/run-safari/safari-launch/tools.d.ts +17 -0
- package/dist/extension/browsers/run-safari/safari-packager.d.ts +2 -1
- package/dist/extension/browsers/run-safari/safari-types.d.ts +2 -0
- package/dist/extension/config-types.d.ts +5 -2
- package/dist/extension/helpers/messages.d.ts +7 -0
- package/dist/extension/helpers/node-version-guard.d.ts +3 -1
- package/dist/extension/helpers/template-catalog.d.ts +1 -1
- package/dist/extension/helpers/template-corpus.generated.d.ts +1 -1
- package/package.json +4 -6
package/dist/cli.cjs
CHANGED
|
@@ -750,7 +750,7 @@ var __webpack_modules__ = {
|
|
|
750
750
|
return `${getLoggingPrefix('warn')} safari-web-extension-converter reported ${pintor__rspack_import_8_default().yellow(String(warnings.length))} ${1 === warnings.length ? 'warning' : 'warnings'}, some manifest keys/APIs may not be supported by Safari:\n` + warnings.map((line)=>` ${pintor__rspack_import_8_default().gray('•')} ${line}`).join('\n') + (kept.length ? `\nExtension.js kept ${1 === kept.length ? 'one of these keys' : 'some of these keys'} on purpose:\n${keptLines}` : '');
|
|
751
751
|
}
|
|
752
752
|
function safariDefaultBundleIdNote(bundleId) {
|
|
753
|
-
return `${getLoggingPrefix('
|
|
753
|
+
return `${getLoggingPrefix('info')} Bundle id ${pintor__rspack_import_8_default().gray(bundleId)} (generated, set ${pintor__rspack_import_8_default().blue('--bundle-id')} to own it).`;
|
|
754
754
|
}
|
|
755
755
|
function safariOpenHint(appPath, appName) {
|
|
756
756
|
return `${getLoggingPrefix('info')} Launch it once to register with Safari: ${pintor__rspack_import_8_default().blue('open')} ${pintor__rspack_import_8_default().underline(`"${appPath}"`)}\nThen turn on ${pintor__rspack_import_8_default().yellow(appName)} in Safari ▸ Settings ▸ Extensions.`;
|
|
@@ -772,6 +772,9 @@ var __webpack_modules__ = {
|
|
|
772
772
|
function safariRegistered(appName) {
|
|
773
773
|
return `${getLoggingPrefix('success')} Safari recognizes ${pintor__rspack_import_8_default().yellow(appName)}, finish enabling it with the steps above.`;
|
|
774
774
|
}
|
|
775
|
+
function safariPidUnresolved(appName) {
|
|
776
|
+
return `${getLoggingPrefix('warn')} Opened ${pintor__rspack_import_8_default().yellow(appName)} but could not find its process id.\nThe session's ready.json names the app and the extension without a ${pintor__rspack_import_8_default().gray('browserPid')}.`;
|
|
777
|
+
}
|
|
775
778
|
function safariNotYetRegistered(appName) {
|
|
776
779
|
return `${getLoggingPrefix('info')} Safari hasn't picked up ${pintor__rspack_import_8_default().yellow(appName)} yet.\nOpen the app once, then check Safari ▸ Settings ▸ Extensions.`;
|
|
777
780
|
}
|
|
@@ -1033,6 +1036,7 @@ var __webpack_modules__ = {
|
|
|
1033
1036
|
jR: ()=>safariBuilding,
|
|
1034
1037
|
jc: ()=>safariProjectStale,
|
|
1035
1038
|
jk: ()=>requireGeckoBinaryForGeckoBased,
|
|
1039
|
+
jn: ()=>safariPidUnresolved,
|
|
1036
1040
|
kK: ()=>preferringSystemBrowserOverSnapshot,
|
|
1037
1041
|
l9: ()=>parsingPacketError,
|
|
1038
1042
|
lO: ()=>firefoxDryRunArgs,
|
|
@@ -1405,6 +1409,7 @@ var __webpack_modules__ = {
|
|
|
1405
1409
|
"./browsers/browsers-lib/ready-stamp.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
1406
1410
|
var node_fs__rspack_import_0 = __webpack_require__("node:fs");
|
|
1407
1411
|
var node_path__rspack_import_1 = __webpack_require__("node:path");
|
|
1412
|
+
var _write_json_atomic__rspack_import_2 = __webpack_require__("./browsers/browsers-lib/write-json-atomic.ts");
|
|
1408
1413
|
function readyPathFor(extensionOutputPath) {
|
|
1409
1414
|
return node_path__rspack_import_1.join(node_path__rspack_import_1.dirname(extensionOutputPath), 'extension-js', node_path__rspack_import_1.basename(extensionOutputPath), 'ready.json');
|
|
1410
1415
|
}
|
|
@@ -1416,7 +1421,7 @@ var __webpack_modules__ = {
|
|
|
1416
1421
|
const ready = JSON.parse(node_fs__rspack_import_0.readFileSync(readyPath, 'utf-8'));
|
|
1417
1422
|
if (ready.rdpPort === rdpPort) return;
|
|
1418
1423
|
ready.rdpPort = rdpPort;
|
|
1419
|
-
|
|
1424
|
+
(0, _write_json_atomic__rspack_import_2.Z)(readyPath, ready);
|
|
1420
1425
|
} catch {}
|
|
1421
1426
|
}
|
|
1422
1427
|
function stampReadyBrowserLaunch(extensionOutputPath, details) {
|
|
@@ -1434,7 +1439,7 @@ var __webpack_modules__ = {
|
|
|
1434
1439
|
if (binary) ready.binary = binary;
|
|
1435
1440
|
const provenance = String(details?.binaryProvenance || '').trim();
|
|
1436
1441
|
if (provenance) ready.binaryProvenance = provenance;
|
|
1437
|
-
|
|
1442
|
+
(0, _write_json_atomic__rspack_import_2.Z)(readyPath, ready);
|
|
1438
1443
|
} catch {}
|
|
1439
1444
|
}
|
|
1440
1445
|
function stampReadyExtensionId(extensionOutputPath, extensionId) {
|
|
@@ -1446,7 +1451,7 @@ var __webpack_modules__ = {
|
|
|
1446
1451
|
const ready = JSON.parse(node_fs__rspack_import_0.readFileSync(readyPath, 'utf-8'));
|
|
1447
1452
|
if (ready.extensionId === id) return;
|
|
1448
1453
|
ready.extensionId = id;
|
|
1449
|
-
|
|
1454
|
+
(0, _write_json_atomic__rspack_import_2.Z)(readyPath, ready);
|
|
1450
1455
|
} catch {}
|
|
1451
1456
|
}
|
|
1452
1457
|
function stampReadyExtensionLoadRefused(extensionOutputPath, reason) {
|
|
@@ -1461,7 +1466,7 @@ var __webpack_modules__ = {
|
|
|
1461
1466
|
ready.message = `${browserLabel.charAt(0).toUpperCase() + browserLabel.slice(1)} refused to load the extension at ${extensionOutputPath}${reason ? `: ${reason}` : ''}`;
|
|
1462
1467
|
ready.extensionLoadRefusedAt = new Date().toISOString();
|
|
1463
1468
|
if (reason) ready.extensionLoadRefusedReason = reason;
|
|
1464
|
-
|
|
1469
|
+
(0, _write_json_atomic__rspack_import_2.Z)(readyPath, ready);
|
|
1465
1470
|
} catch {}
|
|
1466
1471
|
}
|
|
1467
1472
|
function stampReadyProfileLocked(extensionOutputPath, details) {
|
|
@@ -1475,7 +1480,7 @@ var __webpack_modules__ = {
|
|
|
1475
1480
|
ready.message = String(details?.message || '').trim() || 'the browser profile is already in use by another session';
|
|
1476
1481
|
ready.profileLockedAt = new Date().toISOString();
|
|
1477
1482
|
if (details?.owner) ready.profileLockOwner = details.owner;
|
|
1478
|
-
|
|
1483
|
+
(0, _write_json_atomic__rspack_import_2.Z)(readyPath, ready);
|
|
1479
1484
|
} catch {}
|
|
1480
1485
|
}
|
|
1481
1486
|
function stampReadyBrowserExited(extensionOutputPath, code) {
|
|
@@ -1491,7 +1496,7 @@ var __webpack_modules__ = {
|
|
|
1491
1496
|
ready.code = 'browser_exited';
|
|
1492
1497
|
ready.message = `the ${ready.browser || 'browser'} process exited (code ${code ?? 'unknown'}); nothing is running`;
|
|
1493
1498
|
}
|
|
1494
|
-
|
|
1499
|
+
(0, _write_json_atomic__rspack_import_2.Z)(readyPath, ready);
|
|
1495
1500
|
} catch {}
|
|
1496
1501
|
}
|
|
1497
1502
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -1955,6 +1960,27 @@ var __webpack_modules__ = {
|
|
|
1955
1960
|
yO: ()=>probeChromiumBinaryVersion
|
|
1956
1961
|
});
|
|
1957
1962
|
},
|
|
1963
|
+
"./browsers/browsers-lib/write-json-atomic.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
1964
|
+
var node_fs__rspack_import_0 = __webpack_require__("node:fs");
|
|
1965
|
+
function writeJsonAtomic(filePath, value) {
|
|
1966
|
+
const tmpPath = `${filePath}.tmp-${process.pid}`;
|
|
1967
|
+
try {
|
|
1968
|
+
node_fs__rspack_import_0.writeFileSync(tmpPath, JSON.stringify(value, null, 2), 'utf-8');
|
|
1969
|
+
node_fs__rspack_import_0.renameSync(tmpPath, filePath);
|
|
1970
|
+
return true;
|
|
1971
|
+
} catch {
|
|
1972
|
+
try {
|
|
1973
|
+
node_fs__rspack_import_0.rmSync(tmpPath, {
|
|
1974
|
+
force: true
|
|
1975
|
+
});
|
|
1976
|
+
} catch {}
|
|
1977
|
+
return false;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
1981
|
+
Z: ()=>writeJsonAtomic
|
|
1982
|
+
});
|
|
1983
|
+
},
|
|
1958
1984
|
"./browsers/browsers-lib/wsl-support.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
1959
1985
|
__webpack_require__.d(__webpack_exports__, {
|
|
1960
1986
|
qc: ()=>external_wsl_support_namespaceObject.hasGuiDisplay,
|
|
@@ -5807,7 +5833,6 @@ var __webpack_modules__ = {
|
|
|
5807
5833
|
QW: ()=>safariBuildPreflight,
|
|
5808
5834
|
iF: ()=>safariPreflightError
|
|
5809
5835
|
});
|
|
5810
|
-
var external_node_child_process_ = __webpack_require__("node:child_process");
|
|
5811
5836
|
var external_node_fs_ = __webpack_require__("node:fs");
|
|
5812
5837
|
var messaging = __webpack_require__("./helpers/messaging.ts");
|
|
5813
5838
|
var banner = __webpack_require__("./browsers/browsers-lib/banner.ts");
|
|
@@ -5821,6 +5846,7 @@ var __webpack_modules__ = {
|
|
|
5821
5846
|
(0, messaging._w)(messages.Aj(xcodebuildCmd));
|
|
5822
5847
|
}
|
|
5823
5848
|
var safari_config = __webpack_require__("./browsers/run-safari/safari-launch/safari-config.ts");
|
|
5849
|
+
var external_node_child_process_ = __webpack_require__("node:child_process");
|
|
5824
5850
|
function isMacOS() {
|
|
5825
5851
|
return 'darwin' === process.platform;
|
|
5826
5852
|
}
|
|
@@ -5878,20 +5904,6 @@ var __webpack_modules__ = {
|
|
|
5878
5904
|
ok: Boolean(converter && xcodebuild)
|
|
5879
5905
|
};
|
|
5880
5906
|
}
|
|
5881
|
-
function fallbackLogger() {
|
|
5882
|
-
return {
|
|
5883
|
-
info: (...a)=>(0, messaging._w)(...a),
|
|
5884
|
-
warn: (...a)=>(0, messaging.mR)(...a),
|
|
5885
|
-
error: (...a)=>(0, messaging.Gg)(...a),
|
|
5886
|
-
debug: (...a)=>console?.debug?.(...a)
|
|
5887
|
-
};
|
|
5888
|
-
}
|
|
5889
|
-
function isTestEnv() {
|
|
5890
|
-
return Boolean(process.env.VITEST || process.env.VITEST_WORKER_ID);
|
|
5891
|
-
}
|
|
5892
|
-
function delay(ms) {
|
|
5893
|
-
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
5894
|
-
}
|
|
5895
5907
|
const TOOL_TAIL_LINES = 50;
|
|
5896
5908
|
const TOOL_TAIL_BYTES = 8192;
|
|
5897
5909
|
function toolOutputTail(output) {
|
|
@@ -5930,6 +5942,60 @@ var __webpack_modules__ = {
|
|
|
5930
5942
|
}));
|
|
5931
5943
|
});
|
|
5932
5944
|
}
|
|
5945
|
+
async function resolvePidForBundle(bundleId) {
|
|
5946
|
+
for(let attempt = 0; attempt < 10; attempt += 1){
|
|
5947
|
+
const pid = await new Promise((resolve)=>{
|
|
5948
|
+
const child = (0, external_node_child_process_.spawn)("osascript", [
|
|
5949
|
+
'-e',
|
|
5950
|
+
`tell application "System Events" to get unix id of first process whose bundle identifier is "${bundleId}"`
|
|
5951
|
+
]);
|
|
5952
|
+
let out = '';
|
|
5953
|
+
child.stdout.on('data', (d)=>out += String(d));
|
|
5954
|
+
child.on('error', ()=>resolve(null));
|
|
5955
|
+
child.on('close', ()=>{
|
|
5956
|
+
const n = Number(String(out).trim());
|
|
5957
|
+
resolve(Number.isFinite(n) && n > 0 ? n : null);
|
|
5958
|
+
});
|
|
5959
|
+
});
|
|
5960
|
+
if (pid) return pid;
|
|
5961
|
+
await new Promise((r)=>setTimeout(r, 500));
|
|
5962
|
+
}
|
|
5963
|
+
return null;
|
|
5964
|
+
}
|
|
5965
|
+
function createSafariTools() {
|
|
5966
|
+
return {
|
|
5967
|
+
detectToolchain: ()=>detectSafariToolchain(),
|
|
5968
|
+
runConverter: (args)=>runTool('xcrun', args),
|
|
5969
|
+
runXcodebuild: (args)=>runTool('xcodebuild', args),
|
|
5970
|
+
openApp: (target)=>runTool('open', [
|
|
5971
|
+
target
|
|
5972
|
+
]),
|
|
5973
|
+
openSafari: (binary)=>runTool('open', [
|
|
5974
|
+
'-a',
|
|
5975
|
+
binary
|
|
5976
|
+
]),
|
|
5977
|
+
resolvePid: (bundleId)=>resolvePidForBundle(bundleId),
|
|
5978
|
+
pluginkitList: async ()=>{
|
|
5979
|
+
const { ok, output } = await runTool('pluginkit', [
|
|
5980
|
+
'-m'
|
|
5981
|
+
], {
|
|
5982
|
+
quiet: true
|
|
5983
|
+
});
|
|
5984
|
+
return ok ? output : '';
|
|
5985
|
+
}
|
|
5986
|
+
};
|
|
5987
|
+
}
|
|
5988
|
+
function fallbackLogger() {
|
|
5989
|
+
return {
|
|
5990
|
+
info: (...a)=>(0, messaging._w)(...a),
|
|
5991
|
+
warn: (...a)=>(0, messaging.mR)(...a),
|
|
5992
|
+
error: (...a)=>(0, messaging.Gg)(...a),
|
|
5993
|
+
debug: (...a)=>console?.debug?.(...a)
|
|
5994
|
+
};
|
|
5995
|
+
}
|
|
5996
|
+
function delay(ms) {
|
|
5997
|
+
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
5998
|
+
}
|
|
5933
5999
|
function converterWarnings(output) {
|
|
5934
6000
|
const lines = output.split(/\r?\n/);
|
|
5935
6001
|
const collected = [];
|
|
@@ -5950,15 +6016,11 @@ var __webpack_modules__ = {
|
|
|
5950
6016
|
}
|
|
5951
6017
|
return collected;
|
|
5952
6018
|
}
|
|
5953
|
-
async function confirmRegisteredWithSafari(bundleIdentifier) {
|
|
6019
|
+
async function confirmRegisteredWithSafari(tools, bundleIdentifier) {
|
|
5954
6020
|
const needle = `${bundleIdentifier}.Extension`;
|
|
5955
6021
|
for(let attempt = 0; attempt < 6; attempt += 1){
|
|
5956
|
-
const
|
|
5957
|
-
|
|
5958
|
-
], {
|
|
5959
|
-
quiet: true
|
|
5960
|
-
});
|
|
5961
|
-
if (ok && output.includes(needle)) return true;
|
|
6022
|
+
const listing = await tools.pluginkitList();
|
|
6023
|
+
if (listing.includes(needle)) return true;
|
|
5962
6024
|
await delay(800);
|
|
5963
6025
|
}
|
|
5964
6026
|
return false;
|
|
@@ -5973,6 +6035,10 @@ var __webpack_modules__ = {
|
|
|
5973
6035
|
macOsOnly: config.macOsOnly
|
|
5974
6036
|
};
|
|
5975
6037
|
}
|
|
6038
|
+
function completePackage(config, logger, mode) {
|
|
6039
|
+
if ('full' === mode && config.bundleIdDerived) logger.info?.(messages.$q(config.bundleIdentifier));
|
|
6040
|
+
return describePackage(config);
|
|
6041
|
+
}
|
|
5976
6042
|
function safariPreflightError() {
|
|
5977
6043
|
const tc = detectSafariToolchain();
|
|
5978
6044
|
if (!tc.platformOk) return messages.G3(process.platform);
|
|
@@ -5996,26 +6062,6 @@ var __webpack_modules__ = {
|
|
|
5996
6062
|
severity: 'ok'
|
|
5997
6063
|
};
|
|
5998
6064
|
}
|
|
5999
|
-
async function resolvePidForBundle(bundleId) {
|
|
6000
|
-
for(let attempt = 0; attempt < 10; attempt += 1){
|
|
6001
|
-
const pid = await new Promise((resolve)=>{
|
|
6002
|
-
const child = (0, external_node_child_process_.spawn)("osascript", [
|
|
6003
|
-
'-e',
|
|
6004
|
-
`tell application "System Events" to get unix id of first process whose bundle identifier is "${bundleId}"`
|
|
6005
|
-
]);
|
|
6006
|
-
let out = '';
|
|
6007
|
-
child.stdout.on('data', (d)=>out += String(d));
|
|
6008
|
-
child.on('error', ()=>resolve(null));
|
|
6009
|
-
child.on('close', ()=>{
|
|
6010
|
-
const n = Number(String(out).trim());
|
|
6011
|
-
resolve(Number.isFinite(n) && n > 0 ? n : null);
|
|
6012
|
-
});
|
|
6013
|
-
});
|
|
6014
|
-
if (pid) return pid;
|
|
6015
|
-
await new Promise((r)=>setTimeout(r, 500));
|
|
6016
|
-
}
|
|
6017
|
-
return null;
|
|
6018
|
-
}
|
|
6019
6065
|
async function announceSafariDevSession(host, config, appPath) {
|
|
6020
6066
|
try {
|
|
6021
6067
|
await (0, banner.ai)({
|
|
@@ -6031,16 +6077,16 @@ var __webpack_modules__ = {
|
|
|
6031
6077
|
} catch {}
|
|
6032
6078
|
}
|
|
6033
6079
|
async function runSafariPipeline(compilation, host, logger, mode) {
|
|
6080
|
+
const tools = host.tools || createSafariTools();
|
|
6034
6081
|
const config = (0, safari_config.F7)(compilation, host);
|
|
6035
6082
|
const converterArgs = (0, safari_config.w7)(config);
|
|
6036
6083
|
const xcodebuildArgs = (0, safari_config.vx)(config);
|
|
6037
6084
|
const isSigned = Boolean(config.developmentTeam);
|
|
6038
|
-
if (
|
|
6039
|
-
if (host.dryRun || isTestEnv()) {
|
|
6085
|
+
if (host.dryRun) {
|
|
6040
6086
|
logSafariDryRun(`xcrun ${converterArgs.join(' ')}`, `xcodebuild ${xcodebuildArgs.join(' ')}`);
|
|
6041
|
-
return
|
|
6087
|
+
return completePackage(config, logger, mode);
|
|
6042
6088
|
}
|
|
6043
|
-
const toolchain =
|
|
6089
|
+
const toolchain = tools.detectToolchain();
|
|
6044
6090
|
if (!toolchain.platformOk) {
|
|
6045
6091
|
logger.warn?.(messages.G3(process.platform));
|
|
6046
6092
|
return describePackage(config);
|
|
@@ -6064,7 +6110,7 @@ var __webpack_modules__ = {
|
|
|
6064
6110
|
}
|
|
6065
6111
|
const { saved, restore } = (0, safari_config._D)(config);
|
|
6066
6112
|
logger.info?.(messages.uK(config.extensionDir));
|
|
6067
|
-
const converted = await
|
|
6113
|
+
const converted = await tools.runConverter(converterArgs);
|
|
6068
6114
|
if (!converted.ok) {
|
|
6069
6115
|
const tail = toolOutputTail(converted.output);
|
|
6070
6116
|
logger.error?.(messages.A('safari-web-extension-converter', converted.code, tail));
|
|
@@ -6081,7 +6127,7 @@ var __webpack_modules__ = {
|
|
|
6081
6127
|
logger.info?.(messages.l_(config.projectLocation));
|
|
6082
6128
|
} else if ('full' === mode) logger.info?.(messages.oM());
|
|
6083
6129
|
if ('full' === mode) logger.info?.(messages.jR((0, safari_config.aD)(config)));
|
|
6084
|
-
const built = await
|
|
6130
|
+
const built = await tools.runXcodebuild(xcodebuildArgs);
|
|
6085
6131
|
if (!built.ok) {
|
|
6086
6132
|
const tail = toolOutputTail(built.output);
|
|
6087
6133
|
logger.error?.(messages.A('xcodebuild', built.code, tail));
|
|
@@ -6096,30 +6142,26 @@ var __webpack_modules__ = {
|
|
|
6096
6142
|
if (!config.open) {
|
|
6097
6143
|
logger.info?.(messages.aO(appPath, config.appName));
|
|
6098
6144
|
if (host.announceDevReady) await announceSafariDevSession(host, config, appPath);
|
|
6099
|
-
return
|
|
6145
|
+
return completePackage(config, logger, mode);
|
|
6100
6146
|
}
|
|
6101
6147
|
const target = external_node_fs_.existsSync(appPath) ? appPath : (0, safari_config.Gi)(config);
|
|
6102
6148
|
logger.info?.(messages.fw(target));
|
|
6103
|
-
await
|
|
6104
|
-
|
|
6105
|
-
]);
|
|
6106
|
-
if (config.safariBinary) await runTool('open', [
|
|
6107
|
-
'-a',
|
|
6108
|
-
config.safariBinary
|
|
6109
|
-
]);
|
|
6149
|
+
await tools.openApp(target);
|
|
6150
|
+
if (config.safariBinary) await tools.openSafari(config.safariBinary);
|
|
6110
6151
|
const raisedBundleId = config.safariBinary ? 'com.apple.Safari' : config.bundleIdentifier;
|
|
6111
|
-
const browserPid = await
|
|
6112
|
-
|
|
6113
|
-
browserPid,
|
|
6152
|
+
const browserPid = await tools.resolvePid(raisedBundleId);
|
|
6153
|
+
(0, ready_stamp.M)(config.extensionDir, {
|
|
6154
|
+
browserPid: browserPid || void 0,
|
|
6114
6155
|
binary: config.safariBinary || appPath,
|
|
6115
6156
|
binaryProvenance: config.safariBinary ? 'pinned' : 'system',
|
|
6116
6157
|
extensionId: `${config.bundleIdentifier}.Extension`
|
|
6117
6158
|
});
|
|
6159
|
+
if (!browserPid) logger.warn?.(messages.jn(config.appName));
|
|
6118
6160
|
logger.info?.(messages.fO(config.appName, isSigned));
|
|
6119
|
-
if (await confirmRegisteredWithSafari(config.bundleIdentifier)) logger.info?.(messages.fd(config.appName));
|
|
6161
|
+
if (await confirmRegisteredWithSafari(tools, config.bundleIdentifier)) logger.info?.(messages.fd(config.appName));
|
|
6120
6162
|
else logger.info?.(messages.qY(config.appName));
|
|
6121
6163
|
if (host.announceDevReady) await announceSafariDevSession(host, config, appPath);
|
|
6122
|
-
return
|
|
6164
|
+
return completePackage(config, logger, mode);
|
|
6123
6165
|
}
|
|
6124
6166
|
async function packageSafariExtension(host, outputPath, logger, mode = 'full') {
|
|
6125
6167
|
const compilation = {
|
|
@@ -6239,9 +6281,10 @@ var __webpack_modules__ = {
|
|
|
6239
6281
|
function manifestFingerprintPath(config) {
|
|
6240
6282
|
return node_path__rspack_import_1.join(config.projectLocation, '.manifest-fingerprint');
|
|
6241
6283
|
}
|
|
6284
|
+
const HOT_UPDATE_DIR = 'hot';
|
|
6242
6285
|
function topLevelEntries(extensionDir) {
|
|
6243
6286
|
try {
|
|
6244
|
-
return node_fs__rspack_import_0.readdirSync(extensionDir).sort();
|
|
6287
|
+
return node_fs__rspack_import_0.readdirSync(extensionDir).filter((entry)=>entry !== HOT_UPDATE_DIR).sort();
|
|
6245
6288
|
} catch {
|
|
6246
6289
|
return [];
|
|
6247
6290
|
}
|
|
@@ -6370,7 +6413,7 @@ var __webpack_modules__ = {
|
|
|
6370
6413
|
return Object.fromEntries(Object.entries(input).filter(([, value])=>void 0 !== value));
|
|
6371
6414
|
}
|
|
6372
6415
|
function createSafariPackager(options = {}) {
|
|
6373
|
-
const { browser = 'safari', noOpen = true, dryRun = false, logger, ...identity } = options;
|
|
6416
|
+
const { browser = 'safari', noOpen = true, dryRun = false, logger, tools, ...identity } = options;
|
|
6374
6417
|
return async (distPath, mode = 'full', overrides)=>await (0, _safari_launch__rspack_import_0.Ah)({
|
|
6375
6418
|
extension: [
|
|
6376
6419
|
distPath
|
|
@@ -6378,6 +6421,7 @@ var __webpack_modules__ = {
|
|
|
6378
6421
|
browser,
|
|
6379
6422
|
noOpen,
|
|
6380
6423
|
dryRun,
|
|
6424
|
+
tools,
|
|
6381
6425
|
...withoutUndefined(identity),
|
|
6382
6426
|
...withoutUndefined(overrides)
|
|
6383
6427
|
}, distPath, logger, mode);
|
|
@@ -6705,6 +6749,8 @@ var __webpack_modules__ = {
|
|
|
6705
6749
|
const MIN_NODE_MINOR = 12;
|
|
6706
6750
|
const MIN_DENO_MAJOR = 2;
|
|
6707
6751
|
const MIN_DENO_MINOR = 5;
|
|
6752
|
+
const MIN_BUN_MAJOR = 1;
|
|
6753
|
+
const MIN_BUN_MINOR = 2;
|
|
6708
6754
|
const BLOCKED_DENO_VERSIONS = [
|
|
6709
6755
|
'2.8.0'
|
|
6710
6756
|
];
|
|
@@ -6721,6 +6767,9 @@ var __webpack_modules__ = {
|
|
|
6721
6767
|
if (BLOCKED_DENO_VERSIONS.includes(version)) return false;
|
|
6722
6768
|
return meetsFloor(version, MIN_DENO_MAJOR, MIN_DENO_MINOR);
|
|
6723
6769
|
}
|
|
6770
|
+
function isSupportedBunVersion(version) {
|
|
6771
|
+
return meetsFloor(version, MIN_BUN_MAJOR, MIN_BUN_MINOR);
|
|
6772
|
+
}
|
|
6724
6773
|
function detectBunVersion(versions = process.versions) {
|
|
6725
6774
|
const bunVersion = versions.bun;
|
|
6726
6775
|
return 'string' == typeof bunVersion && bunVersion.length > 0 ? bunVersion : void 0;
|
|
@@ -6733,8 +6782,10 @@ var __webpack_modules__ = {
|
|
|
6733
6782
|
if (BLOCKED_DENO_VERSIONS.includes(version)) return `[Extension.js] Deno ${version} cannot load node:querystring inside the bundler, so every build fails on it. Deno fixed that in 2.8.1. Run deno upgrade to build the extension on Deno.`;
|
|
6734
6783
|
return `[Extension.js] Requires Deno >= ${MIN_DENO_MAJOR}.${MIN_DENO_MINOR} (you are on ${version}). Run deno upgrade, or run the extension CLI on Node.js >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR} instead.`;
|
|
6735
6784
|
}
|
|
6736
|
-
function
|
|
6737
|
-
|
|
6785
|
+
function unsupportedBunVersionMessage(version) {
|
|
6786
|
+
return `[Extension.js] Requires Bun >= ${MIN_BUN_MAJOR}.${MIN_BUN_MINOR} (you are on ${version}). Run bun upgrade, or run the extension CLI on Node.js >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR} instead.`;
|
|
6787
|
+
}
|
|
6788
|
+
function unsupportedNodeVersionMessage(version) {
|
|
6738
6789
|
return `[Extension.js] Requires Node.js >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR} (you are on ${version}). Upgrade Node.js to run the extension CLI.`;
|
|
6739
6790
|
}
|
|
6740
6791
|
function enforceSupportedNodeVersion(version = process.versions.node, bunVersion = detectBunVersion(), denoVersion = detectDenoVersion()) {
|
|
@@ -6744,8 +6795,14 @@ var __webpack_modules__ = {
|
|
|
6744
6795
|
process.exit(1);
|
|
6745
6796
|
return;
|
|
6746
6797
|
}
|
|
6747
|
-
if (
|
|
6748
|
-
|
|
6798
|
+
if (bunVersion) {
|
|
6799
|
+
if (isSupportedBunVersion(bunVersion)) return;
|
|
6800
|
+
console.error(unsupportedBunVersionMessage(bunVersion));
|
|
6801
|
+
process.exit(1);
|
|
6802
|
+
return;
|
|
6803
|
+
}
|
|
6804
|
+
if (isSupportedNodeVersion(version)) return;
|
|
6805
|
+
console.error(unsupportedNodeVersionMessage(version));
|
|
6749
6806
|
process.exit(1);
|
|
6750
6807
|
}
|
|
6751
6808
|
enforceSupportedNodeVersion();
|
|
@@ -7141,7 +7198,7 @@ var __webpack_modules__ = {
|
|
|
7141
7198
|
return Math.min(Math.max(n, min), max);
|
|
7142
7199
|
}
|
|
7143
7200
|
const TEMPLATE_CORPUS_REPO = 'extension-js/examples';
|
|
7144
|
-
const TEMPLATE_CORPUS_REF = '
|
|
7201
|
+
const TEMPLATE_CORPUS_REF = '06d33e35894516a460409dca06d1614e291e6252';
|
|
7145
7202
|
const TEMPLATE_CORPUS_SLUGS = [
|
|
7146
7203
|
'action',
|
|
7147
7204
|
'action-locales',
|
|
@@ -8254,7 +8311,10 @@ Docker / Devcontainers / Codespaces
|
|
|
8254
8311
|
- Use ${messages_code('--host 0.0.0.0')} to bind the dev server on all interfaces so HMR is reachable from the host.
|
|
8255
8312
|
- Use ${messages_code('--no-browser')} inside the container and load the extension manually from ${messages_code('dist/<browser>/')} in your host browser.
|
|
8256
8313
|
- Chromium sandbox flags (${messages_code('--no-sandbox')}) are added automatically when Docker, Podman, devcontainers, or Codespaces are detected.
|
|
8257
|
-
- File watching uses
|
|
8314
|
+
- File watching uses native file events by default, which keeps rebuilds fast.
|
|
8315
|
+
- Set ${messages_code(messages_arg('EXTENSION_WATCH_POLL=true'))} to poll instead when native events do not fire,
|
|
8316
|
+
as in a container, a virtual machine, or a network or bind mount.
|
|
8317
|
+
Tune it with ${messages_code(messages_arg('EXTENSION_WATCH_POLL_INTERVAL'))} (default 1000 ms).
|
|
8258
8318
|
- Example: ${messages_code('extension dev ./my-ext --host 0.0.0.0 --no-browser --port 8080')}
|
|
8259
8319
|
|
|
8260
8320
|
Flatpak Firefox
|
|
@@ -8427,7 +8487,9 @@ Cross-browser compatibility
|
|
|
8427
8487
|
],
|
|
8428
8488
|
notes: [
|
|
8429
8489
|
'Use --no-browser inside the container and load dist/<browser>/ in the host browser',
|
|
8430
|
-
'File watching uses
|
|
8490
|
+
'File watching uses native file events by default',
|
|
8491
|
+
'Set EXTENSION_WATCH_POLL=true to poll instead when native events do not fire, as in a container, a virtual machine, or a network or bind mount',
|
|
8492
|
+
'EXTENSION_WATCH_POLL_INTERVAL sets the poll interval in ms (default 1000)',
|
|
8431
8493
|
'--no-sandbox is added automatically when a container environment is detected'
|
|
8432
8494
|
]
|
|
8433
8495
|
}
|
|
@@ -8458,12 +8520,45 @@ Cross-browser compatibility
|
|
|
8458
8520
|
}
|
|
8459
8521
|
const OPEN_SURFACE_NOUN = {
|
|
8460
8522
|
popup: 'popup',
|
|
8523
|
+
options: 'options page',
|
|
8461
8524
|
sidebar: 'side panel',
|
|
8462
8525
|
action: 'action popup'
|
|
8463
8526
|
};
|
|
8464
8527
|
function openSurfaceNoun(surface) {
|
|
8465
8528
|
return OPEN_SURFACE_NOUN[surface] || surface;
|
|
8466
8529
|
}
|
|
8530
|
+
function openSurfaceAction(surface) {
|
|
8531
|
+
return 'command' === surface ? 'trigger the command' : `open the ${openSurfaceNoun(surface)}`;
|
|
8532
|
+
}
|
|
8533
|
+
function asSentence(text) {
|
|
8534
|
+
const trimmed = text.trim();
|
|
8535
|
+
const capitalized = trimmed.charAt(0).toUpperCase() + trimmed.slice(1);
|
|
8536
|
+
return capitalized.endsWith('.') ? capitalized : `${capitalized}.`;
|
|
8537
|
+
}
|
|
8538
|
+
function listenerCount(count, event) {
|
|
8539
|
+
return `${count} ${event} ${1 === count ? 'listener' : 'listeners'}`;
|
|
8540
|
+
}
|
|
8541
|
+
function openedSurface(surface) {
|
|
8542
|
+
return `${getLoggingPrefix('success')} Opened the ${openSurfaceNoun(surface)}.`;
|
|
8543
|
+
}
|
|
8544
|
+
function replayedActionClick(listeners) {
|
|
8545
|
+
return `${getLoggingPrefix('success')} Replayed ${listenerCount(listeners, 'onClicked')} without a user gesture.`;
|
|
8546
|
+
}
|
|
8547
|
+
function replayedCommand(listeners, command) {
|
|
8548
|
+
const name = command ? ` for ${messages_arg(command)}` : '';
|
|
8549
|
+
return `${getLoggingPrefix('success')} Replayed ${listenerCount(listeners, 'onCommand')}${name} without a user gesture.`;
|
|
8550
|
+
}
|
|
8551
|
+
function openSurfaceWarning(warning) {
|
|
8552
|
+
return `${getLoggingPrefix('warn')} ${asSentence(warning)}`;
|
|
8553
|
+
}
|
|
8554
|
+
function openSurfaceFailed(surface, reason, engine, remedy) {
|
|
8555
|
+
const engineNote = engine ? messages_arg(` (engine ${engine})`) : '';
|
|
8556
|
+
return [
|
|
8557
|
+
`${getLoggingPrefix('error')} Can't ${openSurfaceAction(surface)}.`,
|
|
8558
|
+
`${messaging.Sc.label('REASON')} ${messaging.Sc.val(reason)}${engineNote}`,
|
|
8559
|
+
remedy
|
|
8560
|
+
].filter(Boolean).join('\n');
|
|
8561
|
+
}
|
|
8467
8562
|
function openSurfaceGestureStep(surface) {
|
|
8468
8563
|
return `Click the extension's toolbar entry, or its row in the extensions menu, to open the ${openSurfaceNoun(surface)}.`;
|
|
8469
8564
|
}
|
|
@@ -8473,6 +8568,12 @@ Cross-browser compatibility
|
|
|
8473
8568
|
function openSurfaceNeedsGesture(surface) {
|
|
8474
8569
|
return `${getLoggingPrefix('error')} Chromium opens the ${openSurfaceNoun(surface)} only in response to a user gesture, so ${messages_code(`extension open ${surface}`)} can't open it from the command line.\n` + openSurfaceGestureStep(surface);
|
|
8475
8570
|
}
|
|
8571
|
+
function doctorHeader(browser, passes, total) {
|
|
8572
|
+
return `doctor (${browser}), ${passes}/${total} checks passed`;
|
|
8573
|
+
}
|
|
8574
|
+
function doctorRemedy(check, remediation) {
|
|
8575
|
+
return `\n${check}: ${remediation}`;
|
|
8576
|
+
}
|
|
8476
8577
|
function sessionOnPort(browser, port) {
|
|
8477
8578
|
return port ? `for ${browser} on port ${port}` : `for ${browser}`;
|
|
8478
8579
|
}
|
|
@@ -8735,11 +8836,20 @@ Cross-browser compatibility
|
|
|
8735
8836
|
if ('json' === format) return void console.log(JSON.stringify(event, null, 2));
|
|
8736
8837
|
console.log(formatPrettyLogLine(event));
|
|
8737
8838
|
}
|
|
8839
|
+
function levelToken(level) {
|
|
8840
|
+
const word = level.toUpperCase();
|
|
8841
|
+
if ('error' === level) return external_pintor_default().red(word);
|
|
8842
|
+
if ('warn' === level) return external_pintor_default().brightYellow(word);
|
|
8843
|
+
if ('info' === level) return external_pintor_default().gray(word);
|
|
8844
|
+
if ('debug' === level) return external_pintor_default().dim(external_pintor_default().gray(word));
|
|
8845
|
+
return word;
|
|
8846
|
+
}
|
|
8738
8847
|
function formatPrettyLogLine(event) {
|
|
8739
8848
|
const parts = Array.isArray(event.messageParts) ? event.messageParts.map((p)=>'string' == typeof p ? p : JSON.stringify(p)).join(' ') : '';
|
|
8740
8849
|
const code = event.code ? ` ${event.code}` : '';
|
|
8741
8850
|
const remediation = event.remediation ? `\n ↳ ${event.remediation}` : '';
|
|
8742
|
-
|
|
8851
|
+
const level = String(event.level || 'log').toLowerCase();
|
|
8852
|
+
return `[${event.seq ?? '-'}] ${levelToken(level)} ${external_pintor_default().dim(`(${event.context})`)}${code} ${parts}${remediation}`;
|
|
8743
8853
|
}
|
|
8744
8854
|
function writeFrame(frame) {
|
|
8745
8855
|
try {
|
|
@@ -8822,7 +8932,7 @@ Cross-browser compatibility
|
|
|
8822
8932
|
if (matches(event)) printEvent(event, format);
|
|
8823
8933
|
},
|
|
8824
8934
|
onGap: (gap)=>{
|
|
8825
|
-
console.error(`… ${gap.dropped} event(s) dropped (${gap.reason}), stream is behind`);
|
|
8935
|
+
console.error(external_pintor_default().dim(external_pintor_default().gray(`… ${gap.dropped} event(s) dropped (${gap.reason}), stream is behind`)));
|
|
8826
8936
|
},
|
|
8827
8937
|
onClose: (close)=>{
|
|
8828
8938
|
if (!refusals.has(close.code) && sessionStillNamed()) return;
|
|
@@ -8994,11 +9104,33 @@ Cross-browser compatibility
|
|
|
8994
9104
|
hint: openSurfaceGestureStep(surface)
|
|
8995
9105
|
};
|
|
8996
9106
|
}
|
|
8997
|
-
function
|
|
9107
|
+
function openResultLines(value) {
|
|
9108
|
+
if (!value || 'object' != typeof value) return;
|
|
9109
|
+
const reply = value;
|
|
9110
|
+
if ('string' == typeof reply.opened) return [
|
|
9111
|
+
openedSurface(reply.opened)
|
|
9112
|
+
];
|
|
9113
|
+
if ('popup' === reply.triggered) return [
|
|
9114
|
+
openedSurface('action')
|
|
9115
|
+
];
|
|
9116
|
+
if ('number' != typeof reply.listeners) return;
|
|
9117
|
+
const lines = [];
|
|
9118
|
+
if ('onClicked' === reply.triggered) lines.push(replayedActionClick(reply.listeners));
|
|
9119
|
+
else {
|
|
9120
|
+
if ('command' !== reply.triggered) return;
|
|
9121
|
+
lines.push(replayedCommand(reply.listeners, 'string' == typeof reply.command ? reply.command : void 0));
|
|
9122
|
+
}
|
|
9123
|
+
if ('string' == typeof reply.warning && reply.warning) lines.push(openSurfaceWarning(reply.warning));
|
|
9124
|
+
return lines;
|
|
9125
|
+
}
|
|
9126
|
+
function printResult(result, output, command, pretty) {
|
|
8998
9127
|
if ('json' === normalizeOutputFormat(output)) return void console.log(JSON.stringify(buildActEnvelope(command, result)));
|
|
8999
9128
|
if (result.ok) {
|
|
9000
9129
|
const value = result.value;
|
|
9001
|
-
|
|
9130
|
+
const lines = pretty?.value(value) ?? [
|
|
9131
|
+
'string' == typeof value ? value : JSON.stringify(value, null, 2)
|
|
9132
|
+
];
|
|
9133
|
+
for (const line of lines)console.log(line);
|
|
9002
9134
|
const consoleRecords = result.console;
|
|
9003
9135
|
if (Array.isArray(consoleRecords)) {
|
|
9004
9136
|
if (0 === consoleRecords.length) console.log('(no console lines captured)');
|
|
@@ -9007,6 +9139,11 @@ Cross-browser compatibility
|
|
|
9007
9139
|
if (result.truncated) console.error('… result truncated (byte cap)');
|
|
9008
9140
|
return;
|
|
9009
9141
|
}
|
|
9142
|
+
if (pretty) {
|
|
9143
|
+
const { error } = buildActEnvelope(command, result);
|
|
9144
|
+
console.error(pretty.failure(error));
|
|
9145
|
+
return;
|
|
9146
|
+
}
|
|
9010
9147
|
const err = result.error || {
|
|
9011
9148
|
name: 'Error',
|
|
9012
9149
|
message: 'command failed'
|
|
@@ -9080,7 +9217,7 @@ Cross-browser compatibility
|
|
|
9080
9217
|
if (result?.ok && input.augment) try {
|
|
9081
9218
|
Object.assign(result, input.augment(projectPath, browser, result));
|
|
9082
9219
|
} catch {}
|
|
9083
|
-
printResult(result, input.opts.output, input.command);
|
|
9220
|
+
printResult(result, input.opts.output, input.command, input.pretty);
|
|
9084
9221
|
await exitAfterDrain(result.ok ? 0 : 1);
|
|
9085
9222
|
}
|
|
9086
9223
|
function targetFrom(opts, fallback = 'background') {
|
|
@@ -9225,7 +9362,11 @@ Cross-browser compatibility
|
|
|
9225
9362
|
},
|
|
9226
9363
|
args,
|
|
9227
9364
|
opts,
|
|
9228
|
-
preflight: (bridge, projectPath, browser)=>gestureRefusal(surface, bridge, projectPath, browser)
|
|
9365
|
+
preflight: (bridge, projectPath, browser)=>gestureRefusal(surface, bridge, projectPath, browser),
|
|
9366
|
+
pretty: {
|
|
9367
|
+
value: openResultLines,
|
|
9368
|
+
failure: (error)=>openSurfaceFailed(surface, error.message, error.engine, error.hint)
|
|
9369
|
+
}
|
|
9229
9370
|
});
|
|
9230
9371
|
});
|
|
9231
9372
|
}
|
|
@@ -9264,7 +9405,7 @@ Cross-browser compatibility
|
|
|
9264
9405
|
return values.length > 0 ? values : void 0;
|
|
9265
9406
|
}
|
|
9266
9407
|
function registerBuildCommand(program) {
|
|
9267
|
-
program.command('build').arguments('[project-name]').usage('[path-to-remote-extension] [options]').description(commandDescriptions.build).option(`--browser <${BROWSER_TARGETS_HELP}>`, 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds a Safari app via Xcode (macOS only)').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`', parseOptionalBoolean).option('--no-polyfill', 'disable the cross-browser polyfill').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`', parseOptionalBoolean).option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`', parseOptionalBoolean).option('--zip-filename <string>', 'specify the name of the ZIP file. Defaults to the extension name and version').option('--silent [boolean]', 'suppress the build summary output. Defaults to `false`', parseOptionalBoolean).option('--addon-lint [boolean]', 'check Firefox builds against addons.mozilla.org rules with addons-linter when it is installed. Findings print as warnings. Defaults to `true`', parseOptionalBoolean).option('--no-addon-lint', 'skip the addons.mozilla.org lint of Firefox builds').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--mode <development|production|none>', 'bundler mode override (also sets NODE_ENV). Defaults to `production`').option('--open [boolean]', 'open the built Safari app after packaging (safari targets only). Defaults to `false`', parseOptionalBoolean).option('--app-name <name>', 'override the Safari app name (safari targets only). Defaults to the manifest `name`').option('--bundle-id <reverse.dns>', 'set a user-owned Safari bundle identifier (safari targets only). Defaults to a generated dev.extensionjs.* id').option('--development-team <id>', 'sign the Safari app with an Apple Developer team id (safari targets only). Without it the build is ad-hoc signed, which Safari loads locally with no per-launch toggle. Use a team id for a stable identity you can distribute').option('--macos-only [boolean]', 'generate a macOS-only Safari Xcode project (safari targets only). Pass `false` for a universal macOS + iOS project. Defaults to `true`', parseOptionalBoolean).option('--force-regenerate', 'regenerate the Safari Xcode project even when up to date (safari targets only)').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).action(async (pathOrRemoteUrl, { browser: cliBrowser, ...buildOptions })=>{
|
|
9408
|
+
program.command('build').arguments('[project-name]').usage('[path-to-remote-extension] [options]').description(commandDescriptions.build).option(`--browser <${BROWSER_TARGETS_HELP}>`, 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds a Safari app via Xcode (macOS only)').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`', parseOptionalBoolean).option('--no-polyfill', 'disable the cross-browser polyfill').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`', parseOptionalBoolean).option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`', parseOptionalBoolean).option('--zip-filename <string>', 'specify the name of the ZIP file. Defaults to the extension name and version').option('--silent [boolean]', 'suppress the build summary output. Defaults to `false`', parseOptionalBoolean).option('--addon-lint [boolean]', 'check Firefox builds against addons.mozilla.org rules with addons-linter when it is installed. Findings print as warnings. Defaults to `true`', parseOptionalBoolean).option('--no-addon-lint', 'skip the addons.mozilla.org lint of Firefox builds').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--mode <development|production|none>', 'bundler mode override (also sets NODE_ENV). Defaults to `production`').option('--open [boolean]', 'open the built Safari app after packaging (safari targets only). Defaults to `false`', parseOptionalBoolean).option('--app-name <name>', 'override the Safari app name (safari targets only). Defaults to the manifest `name`').option('--bundle-id <reverse.dns>', 'set a user-owned Safari bundle identifier (safari targets only). Defaults to a generated dev.extensionjs.* id shared by every build of the same source, so set your own before the first submission').option('--development-team <id>', 'sign the Safari app with an Apple Developer team id (safari targets only). Without it the build is ad-hoc signed, which Safari loads locally with no per-launch toggle. Use a team id for a stable identity you can distribute').option('--macos-only [boolean]', 'generate a macOS-only Safari Xcode project (safari targets only). Pass `false` for a universal macOS + iOS project. Defaults to `true`', parseOptionalBoolean).option('--force-regenerate', 'regenerate the Safari Xcode project even when up to date (safari targets only)').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).action(async (pathOrRemoteUrl, { browser: cliBrowser, ...buildOptions })=>{
|
|
9268
9409
|
const browser = cliBrowser ?? await resolveConfigBrowser(pathOrRemoteUrl || process.cwd(), 'build') ?? 'chromium';
|
|
9269
9410
|
if (buildOptions.debug || buildOptions.author || buildOptions.authorMode) {
|
|
9270
9411
|
process.env.EXTENSION_DEBUG = '1';
|
|
@@ -9826,37 +9967,45 @@ Cross-browser compatibility
|
|
|
9826
9967
|
async function waitForReadyContract(options) {
|
|
9827
9968
|
const readyPath = sessionReadyPath(options.bridge, options.projectPath, options.browser);
|
|
9828
9969
|
const start = Date.now();
|
|
9970
|
+
let lastReadFailure = '';
|
|
9829
9971
|
while(Date.now() - start < options.timeoutMs){
|
|
9830
|
-
if (external_node_fs_.existsSync(readyPath))
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9972
|
+
if (external_node_fs_.existsSync(readyPath)) {
|
|
9973
|
+
let payload = null;
|
|
9974
|
+
try {
|
|
9975
|
+
payload = JSON.parse(external_node_fs_.readFileSync(readyPath, 'utf8'));
|
|
9976
|
+
lastReadFailure = '';
|
|
9977
|
+
} catch (error) {
|
|
9978
|
+
payload = null;
|
|
9979
|
+
lastReadFailure = error instanceof Error ? error.message : String(error);
|
|
9837
9980
|
}
|
|
9838
|
-
if (
|
|
9839
|
-
|
|
9981
|
+
if (payload) {
|
|
9982
|
+
const isLive = isProcessLikelyAlive(payload.pid);
|
|
9983
|
+
const isFresh = isFreshContractPayload(payload);
|
|
9984
|
+
if (payload.command !== options.command) {
|
|
9840
9985
|
await new Promise((resolve)=>setTimeout(resolve, 250));
|
|
9841
9986
|
continue;
|
|
9842
9987
|
}
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9988
|
+
if (!isLive) {
|
|
9989
|
+
if ('start' !== options.command || !isFresh) {
|
|
9990
|
+
await new Promise((resolve)=>setTimeout(resolve, 250));
|
|
9991
|
+
continue;
|
|
9992
|
+
}
|
|
9993
|
+
}
|
|
9994
|
+
if ('ready' === payload.status) return payload;
|
|
9995
|
+
if ('error' === payload.status) {
|
|
9996
|
+
if (!isLive && 'start' !== options.command) {
|
|
9997
|
+
await new Promise((resolve)=>setTimeout(resolve, 250));
|
|
9998
|
+
continue;
|
|
9999
|
+
}
|
|
10000
|
+
const detail = payload.message || payload.errors?.[0] || 'unknown error';
|
|
10001
|
+
throw new WaitModeError(String(detail), messaging.Lp.E_INTERNAL);
|
|
9849
10002
|
}
|
|
9850
|
-
const detail = payload.message || payload.errors?.[0] || 'unknown error';
|
|
9851
|
-
throw new WaitModeError(String(detail), messaging.Lp.E_INTERNAL);
|
|
9852
10003
|
}
|
|
9853
|
-
} catch (error) {
|
|
9854
|
-
if (error instanceof Error) throw error;
|
|
9855
|
-
throw new Error(String(error));
|
|
9856
10004
|
}
|
|
9857
10005
|
await new Promise((resolve)=>setTimeout(resolve, 250));
|
|
9858
10006
|
}
|
|
9859
|
-
|
|
10007
|
+
const timeoutDetail = lastReadFailure ? `. The last read of the file failed to parse as JSON: ${lastReadFailure}` : '';
|
|
10008
|
+
throw new WaitModeError(`Timed out waiting for ready contract at ${readyPath} (${options.timeoutMs} ms)${timeoutDetail}`, messaging.Lp.E_READY_TIMEOUT);
|
|
9860
10009
|
}
|
|
9861
10010
|
async function runWaitMode(options) {
|
|
9862
10011
|
if (isHttpUrl(options.pathOrRemoteUrl)) throw new WaitModeError('--wait requires a local project path (remote URLs are not supported)', messaging.Lp.E_ARGS);
|
|
@@ -9900,7 +10049,7 @@ Cross-browser compatibility
|
|
|
9900
10049
|
return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 8080;
|
|
9901
10050
|
}
|
|
9902
10051
|
function registerDevCommand(program) {
|
|
9903
|
-
program.command('dev').arguments('[project-path|remote-url]').usage('[project-path|remote-url] [options]').description(commandDescriptions.dev).addHelpText('after', "\nAdditional options:\n --no-browser stop the browser launch, the dev server still starts\n --no-reload emit a dev-mode dist without the content-script reload runtime, tabs need a manual reload to see changes\n --wait wait for ready contract and exit, pair with --output json for machine output\n" + BROWSER_LAUNCH_HELP_FOOTER).option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option(`-b, --browser <${BROWSER_TARGETS_HELP}>`, 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds and opens a Safari app via Xcode (macOS only)').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').addOption(geckoBinaryOption()).addOption(firefoxBinaryAliasOption()).option('--safari-binary <path-to-binary>', 'specify the Safari binary to open after packaging (safari targets only)').option('--app-name <name>', 'override the Safari app name (safari targets only). Defaults to the manifest `name`').option('--bundle-id <reverse.dns>', 'set a user-owned Safari bundle identifier (safari targets only). Defaults to a generated dev.extensionjs.* id').option('--development-team <id>', 'sign the Safari app with an Apple Developer team id (safari targets only). Without it the build is ad-hoc signed, which Safari loads locally with no per-launch toggle. Use a team id for a stable identity you can distribute').option('--macos-only [boolean]', 'generate a macOS-only Safari Xcode project (safari targets only). Pass `false` for a universal macOS + iOS project. Defaults to `true`', parseOptionalBoolean).option('--force-regenerate', 'regenerate the Safari Xcode project even when up to date (safari targets only)').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`', parseOptionalBoolean).option('--no-polyfill', 'disable the cross-browser polyfill').option('--no-open', NO_OPEN_FLAG_DESCRIPTION).option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--host <host>', 'specify the host to bind the dev server to. Use 0.0.0.0 for Docker/devcontainers. Defaults to `127.0.0.1`').option('--public-host <host>', 'connectable host the browser (HMR + reload bridge) dials when it differs from the bind host (e.g. a remote/devcontainer). Defaults to the bind host, or 127.0.0.1 when bound to 0.0.0.0').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json|ndjson>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--wait [boolean]', 'wait for dist/extension-js/<browser>/ready.json and exit', parseOptionalBoolean).option('--wait-timeout <ms>', 'timeout in milliseconds when using --wait (default: 60000)').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--wait-format <pretty|json>').hideHelp()).addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).option('--allow-control', 'enable the agent-bridge control channel for bounded act (storage/reload/open): see `extension reload|storage|open`').option('--allow-eval', 'additionally enable `extension eval` (implies --allow-control, runs arbitrary code in a context, writes a 0600 session token)').option('--parent-pid <pid>', 'exit when the given process dies. For wrappers that spawn `extension dev`, so a leaked dev server can never outlive its owner').action(async (pathOrRemoteUrl, options, command)=>{
|
|
10052
|
+
program.command('dev').arguments('[project-path|remote-url]').usage('[project-path|remote-url] [options]').description(commandDescriptions.dev).addHelpText('after', "\nAdditional options:\n --no-browser stop the browser launch, the dev server still starts\n --no-reload emit a dev-mode dist without the content-script reload runtime, tabs need a manual reload to see changes\n --wait wait for ready contract and exit, pair with --output json for machine output\n" + BROWSER_LAUNCH_HELP_FOOTER).option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option(`-b, --browser <${BROWSER_TARGETS_HELP}>`, 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds and opens a Safari app via Xcode (macOS only)').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').addOption(geckoBinaryOption()).addOption(firefoxBinaryAliasOption()).option('--safari-binary <path-to-binary>', 'specify the Safari binary to open after packaging (safari targets only)').option('--app-name <name>', 'override the Safari app name (safari targets only). Defaults to the manifest `name`').option('--bundle-id <reverse.dns>', 'set a user-owned Safari bundle identifier (safari targets only). Defaults to a generated dev.extensionjs.* id shared by every build of the same source, so set your own before the first submission').option('--development-team <id>', 'sign the Safari app with an Apple Developer team id (safari targets only). Without it the build is ad-hoc signed, which Safari loads locally with no per-launch toggle. Use a team id for a stable identity you can distribute').option('--macos-only [boolean]', 'generate a macOS-only Safari Xcode project (safari targets only). Pass `false` for a universal macOS + iOS project. Defaults to `true`', parseOptionalBoolean).option('--force-regenerate', 'regenerate the Safari Xcode project even when up to date (safari targets only)').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`', parseOptionalBoolean).option('--no-polyfill', 'disable the cross-browser polyfill').option('--no-open', NO_OPEN_FLAG_DESCRIPTION).option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--host <host>', 'specify the host to bind the dev server to. Use 0.0.0.0 for Docker/devcontainers. Defaults to `127.0.0.1`').option('--public-host <host>', 'connectable host the browser (HMR + reload bridge) dials when it differs from the bind host (e.g. a remote/devcontainer). Defaults to the bind host, or 127.0.0.1 when bound to 0.0.0.0').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json|ndjson>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--wait [boolean]', 'wait for dist/extension-js/<browser>/ready.json and exit', parseOptionalBoolean).option('--wait-timeout <ms>', 'timeout in milliseconds when using --wait (default: 60000)').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--wait-format <pretty|json>').hideHelp()).addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).option('--allow-control', 'enable the agent-bridge control channel for bounded act (storage/reload/open): see `extension reload|storage|open`').option('--allow-eval', 'additionally enable `extension eval` (implies --allow-control, runs arbitrary code in a context, writes a 0600 session token)').option('--parent-pid <pid>', 'exit when the given process dies. For wrappers that spawn `extension dev`, so a leaked dev server can never outlive its owner').action(async (pathOrRemoteUrl, options, command)=>{
|
|
9904
10053
|
const { browser: cliBrowser, ...devOptions } = options;
|
|
9905
10054
|
const browser = cliBrowser ?? await resolveConfigBrowser(pathOrRemoteUrl || process.cwd(), 'dev') ?? 'chromium';
|
|
9906
10055
|
if (devOptions.debug || devOptions.author || devOptions.authorMode) {
|
|
@@ -10356,19 +10505,19 @@ Cross-browser compatibility
|
|
|
10356
10505
|
});
|
|
10357
10506
|
return results;
|
|
10358
10507
|
}
|
|
10508
|
+
function checkGlyph(status) {
|
|
10509
|
+
if ('pass' === status) return external_pintor_default().green('✓');
|
|
10510
|
+
if ('fail' === status) return external_pintor_default().red('✗');
|
|
10511
|
+
if ('warn' === status) return external_pintor_default().brightYellow('!');
|
|
10512
|
+
return external_pintor_default().gray('–');
|
|
10513
|
+
}
|
|
10359
10514
|
function printPretty(results, browser) {
|
|
10360
10515
|
const passes = results.filter((r)=>'pass' === r.status).length;
|
|
10361
|
-
|
|
10362
|
-
pass: '✓',
|
|
10363
|
-
fail: '✗',
|
|
10364
|
-
warn: '!',
|
|
10365
|
-
skip: '–'
|
|
10366
|
-
};
|
|
10367
|
-
console.log(`doctor (${browser}), ${passes}/${results.length} checks passed`);
|
|
10516
|
+
console.log(doctorHeader(browser, passes, results.length));
|
|
10368
10517
|
const width = Math.max(...results.map((r)=>r.check.length));
|
|
10369
|
-
for (const r of results)console.log(` ${
|
|
10518
|
+
for (const r of results)console.log(` ${checkGlyph(r.status)} ${r.check.padEnd(width)} ${r.detail}`);
|
|
10370
10519
|
const advisory = results.find((r)=>'fail' === r.status) ?? results.find((r)=>'warn' === r.status);
|
|
10371
|
-
if (advisory?.remediation) console.log(
|
|
10520
|
+
if (advisory?.remediation) console.log(doctorRemedy(advisory.check, advisory.remediation));
|
|
10372
10521
|
}
|
|
10373
10522
|
const CHECK_CODES = {
|
|
10374
10523
|
'ready-contract': messaging.Lp.E_SESSION_NOT_FOUND,
|