extension 4.1.19 → 4.1.20
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 +16 -15
- package/bin/extension.cjs +2 -0
- package/dist/browsers.cjs +66 -26
- package/dist/cli.cjs +194 -127
- package/dist/extension/browsers/browsers-lib/constants.d.ts +0 -2
- package/dist/extension/browsers/browsers-lib/messages.d.ts +1 -1
- package/dist/extension/browsers/browsers-lib/resolve-profile.d.ts +0 -35
- package/dist/extension/browsers/browsers-types.d.ts +0 -89
- package/dist/extension/browsers/index.d.ts +0 -28
- package/dist/extension/browsers/run-chromium/chromium-launch/extension-output-path.d.ts +0 -6
- package/dist/extension/browsers/run-chromium/chromium-launch/index.d.ts +0 -9
- package/dist/extension/browsers/run-chromium/chromium-types.d.ts +0 -11
- package/dist/extension/browsers/run-safari/safari-launch/index.d.ts +1 -6
- package/dist/extension/browsers/run-safari/safari-packager.d.ts +0 -15
- package/dist/extension/browsers/run-safari/safari-types.d.ts +0 -1
- package/dist/extension/commands/act.d.ts +0 -6
- package/dist/extension/commands/doctor.d.ts +0 -7
- package/dist/extension/commands/publish.d.ts +0 -6
- package/dist/extension/helpers/cli-explicit.d.ts +0 -15
- package/dist/extension/helpers/node-version-guard.d.ts +4 -1
- package/dist/extension/helpers/template-catalog.d.ts +1 -1
- package/dist/extension/helpers/template-corpus.generated.d.ts +1 -1
- package/dist/extension/helpers/vendors.d.ts +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -6,12 +6,10 @@
|
|
|
6
6
|
[action-url]: https://github.com/extension-js/extension.js/actions
|
|
7
7
|
[discord-image]: https://img.shields.io/discord/1253608412890271755?label=Discord&logo=discord&style=flat&color=0971fe
|
|
8
8
|
[discord-url]: https://discord.gg/v9h2RgeTSN
|
|
9
|
-
[
|
|
10
|
-
[
|
|
11
|
-
[scorecard-image]: https://img.shields.io/ossf-scorecard/github.com/extension-js/extension.js?label=OpenSSF%20Scorecard&style=flat&color=0971fe
|
|
12
|
-
[scorecard-url]: https://scorecard.dev/viewer/?uri=github.com/extension-js/extension.js
|
|
9
|
+
[bestpractices-image]: https://img.shields.io/cii/level/14662?label=OpenSSF%20Best%20Practices&style=flat&color=0971fe
|
|
10
|
+
[bestpractices-url]: https://www.bestpractices.dev/projects/14662
|
|
13
11
|
|
|
14
|
-
# Extension.js [![Version][npm-version-image]][npm-version-url] [![Stars][stars-image]][stars-url] [![CI][action-image]][action-url] [![Discord][discord-image]][discord-url] [![OpenSSF
|
|
12
|
+
# Extension.js [![Version][npm-version-image]][npm-version-url] [![Stars][stars-image]][stars-url] [![CI][action-image]][action-url] [![Discord][discord-image]][discord-url] [![OpenSSF Best Practices][bestpractices-image]][bestpractices-url]
|
|
15
13
|
|
|
16
14
|
> The cross-browser extension framework. Build for Chrome, Edge, Firefox, and Safari with no build config required.
|
|
17
15
|
|
|
@@ -25,7 +23,9 @@ npm run dev
|
|
|
25
23
|
|
|
26
24
|
Works with `npm`, `pnpm`, `yarn`, `bun`, and `deno`.
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
The CLI itself runs on Node.js 22.12+ and Deno 2.5+. Bun installs and launches it, and it runs on Node.js from there.
|
|
27
|
+
|
|
28
|
+
[Documentation](https://extension.js.org/docs) · [Templates](https://templates.extension.dev/?utm_source=readme&utm_campaign=nav-templates) · [Showcase](https://extension.js.org/showcase) · [Blog](https://extension.js.org/blog)
|
|
29
29
|
|
|
30
30
|
## Why Extension.js
|
|
31
31
|
|
|
@@ -181,6 +181,16 @@ npx extension@latest dev https://github.com/GoogleChrome/chrome-extensions-sampl
|
|
|
181
181
|
|
|
182
182
|
</div>
|
|
183
183
|
|
|
184
|
+
Each of these installs Extension.js, launches it, and runs its scripts. Which runtime the CLI then executes on is a separate question.
|
|
185
|
+
|
|
186
|
+
## Runtimes
|
|
187
|
+
|
|
188
|
+
| Runtime | Status | Notes |
|
|
189
|
+
| :-- | :-- | :-- |
|
|
190
|
+
| Node.js | ✅ 22.12 and newer | The default. `npx`, `pnpm dlx`, `yarn dlx` and `bunx` all land here through the published bin |
|
|
191
|
+
| Deno | ✅ 2.5 and newer | `deno run -A npm:extension@latest`. Deno 2.8.0 is refused because it cannot load `node:querystring`, fixed in 2.8.1 |
|
|
192
|
+
| Bun | ❌ Not a runtime for the CLI | Bun installs and launches Extension.js, which then runs on Node.js. `bunx --bun` and `bun run --bun` are refused |
|
|
193
|
+
|
|
184
194
|
## Community
|
|
185
195
|
|
|
186
196
|
- Star the repo if Extension.js helped you ship faster
|
|
@@ -193,15 +203,6 @@ npx extension@latest dev https://github.com/GoogleChrome/chrome-extensions-sampl
|
|
|
193
203
|
Sponsors help the project ship faster releases, better developer experience, and long-term reliability for extension teams.
|
|
194
204
|
|
|
195
205
|
<div align="center">
|
|
196
|
-
<p>
|
|
197
|
-
<a href="https://www.testmuai.com/?utm_medium=sponsor&utm_source=extensionjs" target="_blank" rel="noopener noreferrer">
|
|
198
|
-
<picture>
|
|
199
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://extension.js.org/images/sponsors/testmuai_dark.svg" />
|
|
200
|
-
<source media="(prefers-color-scheme: light)" srcset="https://extension.js.org/images/sponsors/testmuai.svg" />
|
|
201
|
-
<img src="https://extension.js.org/images/sponsors/testmuai.svg" width="220" alt="TestMu AI" />
|
|
202
|
-
</picture>
|
|
203
|
-
</a>
|
|
204
|
-
</p>
|
|
205
206
|
<p>
|
|
206
207
|
<a href="https://extension.dev/?utm_medium=sponsor&utm_source=extensionjs" target="_blank" rel="noopener noreferrer">
|
|
207
208
|
<picture>
|
package/bin/extension.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
// ██████╗██╗ ██╗
|
|
3
4
|
// ██╔════╝██║ ██║
|
|
4
5
|
// ██║ ██║ ██║
|
|
@@ -38,6 +39,7 @@ if (fs.existsSync(distCjsEntry)) {
|
|
|
38
39
|
console.error(
|
|
39
40
|
'[Extension.js] CLI not built. Run "pnpm --filter extension compile".'
|
|
40
41
|
)
|
|
42
|
+
|
|
41
43
|
process.exit(1)
|
|
42
44
|
}
|
|
43
45
|
} else {
|
package/dist/browsers.cjs
CHANGED
|
@@ -740,16 +740,19 @@ var __webpack_modules__ = {
|
|
|
740
740
|
const tail = outputTail.trim().length ? `\n${pintor__rspack_import_8_default().gray('── last output ──')}\n${outputTail}` : `\n${pintor__rspack_import_8_default().gray('(no output captured)')}`;
|
|
741
741
|
return `${getLoggingPrefix('error')} Safari packaging tool ${pintor__rspack_import_8_default().underline(tool)} failed (${pintor__rspack_import_8_default().red(code)}).${tail}`;
|
|
742
742
|
}
|
|
743
|
+
const SAFARI_KEPT_ON_PURPOSE = {
|
|
744
|
+
world: 'Safari has honored the MAIN world since Safari 18, so the key stays'
|
|
745
|
+
};
|
|
743
746
|
function safariConverterWarnings(warnings) {
|
|
744
|
-
|
|
747
|
+
const kept = warnings.map((line)=>line.trim()).filter((line)=>line in SAFARI_KEPT_ON_PURPOSE);
|
|
748
|
+
const keptLines = kept.map((line)=>` ${pintor__rspack_import_8_default().yellow(line)} ${pintor__rspack_import_8_default().gray(SAFARI_KEPT_ON_PURPOSE[line])}`).join('\n');
|
|
749
|
+
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}` : '');
|
|
745
750
|
}
|
|
746
751
|
function safariDefaultBundleIdNote(bundleId) {
|
|
747
752
|
return `${getLoggingPrefix('warn')} The bundle id ${pintor__rspack_import_8_default().gray(bundleId)} is generated from the app name.\nEvery project built from the same source shares this id, and the first team to register it takes it.\nSet your own with ${pintor__rspack_import_8_default().blue('--bundle-id')} before your first submission.\nAfter a submission, a new id is a new extension with none of your users.`;
|
|
748
753
|
}
|
|
749
|
-
function safariOpenHint(appPath, appName
|
|
750
|
-
|
|
751
|
-
if (signed) return launch + `Then turn on ${pintor__rspack_import_8_default().yellow(appName)} in Safari ▸ Settings ▸ Extensions.`;
|
|
752
|
-
return launch + `Then enable ${pintor__rspack_import_8_default().yellow(appName)} via Safari ▸ Develop ▸ ` + `${pintor__rspack_import_8_default().yellow('Allow Unsigned Extensions')} and Safari ▸ Settings ▸ Extensions.`;
|
|
754
|
+
function safariOpenHint(appPath, appName) {
|
|
755
|
+
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.`;
|
|
753
756
|
}
|
|
754
757
|
function safariDryRunNotBuilding() {
|
|
755
758
|
return `${getLoggingPrefix('info')} Dry run: skip the Safari app build.`;
|
|
@@ -761,8 +764,9 @@ var __webpack_modules__ = {
|
|
|
761
764
|
return `${getLoggingPrefix('info')} xcodebuild: ${pintor__rspack_import_8_default().gray(cmd)}.`;
|
|
762
765
|
}
|
|
763
766
|
function safariNextSteps(appName, signed) {
|
|
764
|
-
|
|
765
|
-
|
|
767
|
+
const steps = `${getLoggingPrefix('info')} One-time setup to load ${pintor__rspack_import_8_default().yellow(appName)} in Safari:\n ${pintor__rspack_import_8_default().gray('1.')} Safari ▸ Settings ▸ Extensions ▸ turn on ${pintor__rspack_import_8_default().yellow(appName)}\n`;
|
|
768
|
+
if (signed) return steps + ` ${pintor__rspack_import_8_default().gray('→')} Signed with your team id, so the identity is stable for distribution.`;
|
|
769
|
+
return steps + ` ${pintor__rspack_import_8_default().gray('→')} Ad-hoc signed, so it stays enabled across restarts.`;
|
|
766
770
|
}
|
|
767
771
|
function safariRegistered(appName) {
|
|
768
772
|
return `${getLoggingPrefix('success')} Safari recognizes ${pintor__rspack_import_8_default().yellow(appName)}, finish enabling it with the steps above.`;
|
|
@@ -771,7 +775,7 @@ var __webpack_modules__ = {
|
|
|
771
775
|
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.`;
|
|
772
776
|
}
|
|
773
777
|
function safariRebuilt(appName) {
|
|
774
|
-
return `${getLoggingPrefix('success')} Rebuilt ${pintor__rspack_import_8_default().yellow(appName)}
|
|
778
|
+
return `${getLoggingPrefix('success')} Rebuilt ${pintor__rspack_import_8_default().yellow(appName)}.`;
|
|
775
779
|
}
|
|
776
780
|
function safariProjectStale() {
|
|
777
781
|
return `${getLoggingPrefix('info')} Regenerating the Xcode project, manifest.json or identity options changed since it was generated.`;
|
|
@@ -5936,7 +5940,24 @@ var __webpack_modules__ = {
|
|
|
5936
5940
|
});
|
|
5937
5941
|
}
|
|
5938
5942
|
function converterWarnings(output) {
|
|
5939
|
-
|
|
5943
|
+
const lines = output.split(/\r?\n/);
|
|
5944
|
+
const collected = [];
|
|
5945
|
+
let inWarning = false;
|
|
5946
|
+
for (const line of lines){
|
|
5947
|
+
const trimmed = line.trim();
|
|
5948
|
+
if (/warning/i.test(line)) {
|
|
5949
|
+
inWarning = true;
|
|
5950
|
+
if (trimmed.length > 0) collected.push(trimmed);
|
|
5951
|
+
continue;
|
|
5952
|
+
}
|
|
5953
|
+
const isContinuation = inWarning && /^\s/.test(line) && trimmed.length > 0;
|
|
5954
|
+
if (isContinuation) {
|
|
5955
|
+
collected.push(trimmed);
|
|
5956
|
+
continue;
|
|
5957
|
+
}
|
|
5958
|
+
inWarning = false;
|
|
5959
|
+
}
|
|
5960
|
+
return collected;
|
|
5940
5961
|
}
|
|
5941
5962
|
async function confirmRegisteredWithSafari(bundleIdentifier) {
|
|
5942
5963
|
const needle = `${bundleIdentifier}.Extension`;
|
|
@@ -6067,7 +6088,7 @@ var __webpack_modules__ = {
|
|
|
6067
6088
|
if (preservedKeys.length > 0) logger.info?.(messages.oB(preservedKeys));
|
|
6068
6089
|
(0, safari_config.x7)(config);
|
|
6069
6090
|
logger.info?.(messages.l_(config.projectLocation));
|
|
6070
|
-
} else logger.info?.(messages.oM());
|
|
6091
|
+
} else if ('full' === mode) logger.info?.(messages.oM());
|
|
6071
6092
|
if ('full' === mode) logger.info?.(messages.jR((0, safari_config.aD)(config)));
|
|
6072
6093
|
const built = await runTool('xcodebuild', xcodebuildArgs);
|
|
6073
6094
|
if (!built.ok) {
|
|
@@ -6082,7 +6103,7 @@ var __webpack_modules__ = {
|
|
|
6082
6103
|
}
|
|
6083
6104
|
logger.info?.(messages.Rl(appPath));
|
|
6084
6105
|
if (!config.open) {
|
|
6085
|
-
logger.info?.(messages.aO(appPath, config.appName
|
|
6106
|
+
logger.info?.(messages.aO(appPath, config.appName));
|
|
6086
6107
|
if (host.announceDevReady) await announceSafariDevSession(host, config, appPath);
|
|
6087
6108
|
return describePackage(config);
|
|
6088
6109
|
}
|
|
@@ -6227,36 +6248,55 @@ var __webpack_modules__ = {
|
|
|
6227
6248
|
function manifestFingerprintPath(config) {
|
|
6228
6249
|
return node_path__rspack_import_1.join(config.projectLocation, '.manifest-fingerprint');
|
|
6229
6250
|
}
|
|
6230
|
-
function
|
|
6231
|
-
if (null == obj) return JSON.stringify(obj);
|
|
6232
|
-
if ('object' != typeof obj) return JSON.stringify(obj);
|
|
6233
|
-
if (Array.isArray(obj)) return `[${obj.map(stableStringify).join(',')}]`;
|
|
6234
|
-
const keys = Object.keys(obj).sort();
|
|
6235
|
-
return `{${keys.map((k)=>`${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(',')}}`;
|
|
6236
|
-
}
|
|
6237
|
-
function readManifestRaw(extensionDir) {
|
|
6251
|
+
function topLevelEntries(extensionDir) {
|
|
6238
6252
|
try {
|
|
6239
|
-
return node_fs__rspack_import_0.
|
|
6253
|
+
return node_fs__rspack_import_0.readdirSync(extensionDir).sort();
|
|
6240
6254
|
} catch {
|
|
6241
|
-
return
|
|
6255
|
+
return [];
|
|
6242
6256
|
}
|
|
6243
6257
|
}
|
|
6244
|
-
function
|
|
6258
|
+
function iconsFingerprint(extensionDir) {
|
|
6245
6259
|
try {
|
|
6246
|
-
|
|
6260
|
+
const raw = node_fs__rspack_import_0.readFileSync(node_path__rspack_import_1.join(extensionDir, 'manifest.json'), 'utf8');
|
|
6261
|
+
const icons = JSON.parse(raw).icons;
|
|
6262
|
+
if (!icons || 'object' != typeof icons) return '';
|
|
6263
|
+
return Object.keys(icons).sort().map((size)=>`${size}:${String(icons[size])}`).join(',');
|
|
6247
6264
|
} catch {
|
|
6248
|
-
return
|
|
6265
|
+
return '';
|
|
6266
|
+
}
|
|
6267
|
+
}
|
|
6268
|
+
function asRecord(value) {
|
|
6269
|
+
return value && 'object' == typeof value && !Array.isArray(value) ? value : {};
|
|
6270
|
+
}
|
|
6271
|
+
function judgedManifestSurface(extensionDir) {
|
|
6272
|
+
const manifest = readManifest(extensionDir);
|
|
6273
|
+
const parts = [
|
|
6274
|
+
Object.keys(manifest).sort().join(',')
|
|
6275
|
+
];
|
|
6276
|
+
for (const listName of [
|
|
6277
|
+
'permissions',
|
|
6278
|
+
'optional_permissions'
|
|
6279
|
+
]){
|
|
6280
|
+
const list = manifest[listName];
|
|
6281
|
+
const names = Array.isArray(list) ? list.filter((entry)=>'string' == typeof entry).sort() : [];
|
|
6282
|
+
parts.push(`${listName}:${names.join(',')}`);
|
|
6249
6283
|
}
|
|
6284
|
+
const contentScripts = manifest.content_scripts;
|
|
6285
|
+
if (Array.isArray(contentScripts)) parts.push(contentScripts.map((entry)=>Object.keys(asRecord(entry)).sort().join('+')).join(';'));
|
|
6286
|
+
parts.push(Object.keys(asRecord(manifest.options_ui)).sort().join(','));
|
|
6287
|
+
return parts.join('|');
|
|
6250
6288
|
}
|
|
6251
6289
|
function composeProjectFingerprint(config) {
|
|
6252
6290
|
return JSON.stringify({
|
|
6253
|
-
v:
|
|
6291
|
+
v: 4,
|
|
6254
6292
|
identity: {
|
|
6255
6293
|
appName: config.appName,
|
|
6256
6294
|
bundleId: config.bundleIdentifier,
|
|
6257
6295
|
macOsOnly: config.macOsOnly
|
|
6258
6296
|
},
|
|
6259
|
-
|
|
6297
|
+
entries: topLevelEntries(config.extensionDir),
|
|
6298
|
+
icons: iconsFingerprint(config.extensionDir),
|
|
6299
|
+
judged: judgedManifestSurface(config.extensionDir)
|
|
6260
6300
|
});
|
|
6261
6301
|
}
|
|
6262
6302
|
function saveManifestFingerprint(config) {
|