extension 4.0.15 → 4.0.16
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 -6
- package/dist/49.cjs +142 -12
- package/dist/browsers.cjs +190 -23
- package/dist/cli.cjs +190 -23
- package/dist/extension/browsers/browsers-lib/banner.d.ts +1 -0
- package/dist/extension/browsers/browsers-lib/messages.d.ts +2 -0
- package/dist/extension/browsers/browsers-lib/ready-stamp.d.ts +1 -0
- package/dist/extension/browsers/browsers-lib/shared-utils.d.ts +1 -0
- package/dist/extension/browsers/browsers-types.d.ts +6 -0
- package/dist/extension/browsers/index.d.ts +6 -1
- package/dist/extension/browsers/run-chromium/cdp/cdp-extension-controller/ensure.d.ts +11 -0
- package/dist/extension/browsers/run-chromium/cdp/cdp-extension-controller/index.d.ts +5 -0
- package/dist/extension/browsers/run-chromium/chromium-launch/index.d.ts +6 -1
- package/dist/extension/browsers/run-chromium/chromium-types.d.ts +2 -0
- package/dist/extension/browsers/run-firefox/firefox-launch/index.d.ts +2 -0
- package/dist/extension/browsers/run-firefox/firefox-types.d.ts +4 -1
- package/dist/extension/browsers/run-firefox/rdp/rdp-extension-controller/index.d.ts +1 -0
- package/dist/extension/browsers/run-firefox/rdp/remote-firefox/addons-install.d.ts +7 -0
- package/dist/extension/browsers/run-firefox/rdp/remote-firefox/index.d.ts +2 -0
- package/package.json +7 -7
- package/dist/extension/browsers/browsers-lib/html-merge.d.ts +0 -1
package/README.md
CHANGED
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
[action-url]: https://github.com/extension-js/extension.js/actions
|
|
9
9
|
[discord-image]: https://img.shields.io/discord/1253608412890271755?label=Discord&logo=discord&style=flat&color=0971fe
|
|
10
10
|
[discord-url]: https://discord.gg/v9h2RgeTSN
|
|
11
|
-
[snyk-image]: https://snyk.io/test/github/extension-js/extension/badge.svg?color=0971fe
|
|
12
|
-
[snyk-url]: https://snyk.io/test/github/extension-js/extension
|
|
11
|
+
[snyk-image]: https://snyk.io/test/github/extension-js/extension.js/badge.svg?color=0971fe
|
|
12
|
+
[snyk-url]: https://snyk.io/test/github/extension-js/extension.js
|
|
13
13
|
|
|
14
14
|
# Extension.js [![Version][npm-version-image]][npm-version-url] [![Downloads][npm-downloads-image]][npm-downloads-url] [![Stars][stars-image]][stars-url] [![CI][action-image]][action-url] [![Discord][discord-image]][discord-url]
|
|
15
15
|
|
|
16
|
-
> The cross-browser extension framework. Build for Chrome, Edge, and
|
|
16
|
+
> The cross-browser extension framework. Build for Chrome, Edge, Firefox, and Safari with no build config required.
|
|
17
17
|
|
|
18
18
|
<img alt="Logo" align="right" src="https://avatars.githubusercontent.com/u/172809806" width="14.1%" />
|
|
19
19
|
|
|
@@ -32,7 +32,7 @@ Works with `npm`, `pnpm`, `yarn`, `bun`, and `deno`.
|
|
|
32
32
|
Browser extensions ship with the worst dev experience in modern web. Manifest V3 fragmentation, browser-specific quirks, no hot reload for content scripts, and a separate build pipeline for every target. Extension.js fixes that.
|
|
33
33
|
|
|
34
34
|
- **Hot Module Replacement** for background, content, popup, and options scripts, including React, Vue, Svelte, and Preact components
|
|
35
|
-
- **Manifest V3 by default**, with automatic adapters for Chrome, Edge, and
|
|
35
|
+
- **Manifest V3 by default**, with automatic adapters for Chrome, Edge, Firefox, and Safari targets
|
|
36
36
|
- **One CLI** for Chrome, Edge, Firefox, and any Chromium or Gecko binary
|
|
37
37
|
- **Zero config**, no webpack, no rollup, no plugins to maintain
|
|
38
38
|
- **First-class** TypeScript, React, Vue, Svelte, and Preact support
|
|
@@ -72,7 +72,7 @@ If you have used [Plasmo](https://www.plasmo.com), [WXT](https://wxt.dev), or [C
|
|
|
72
72
|
|
|
73
73
|
Use these flags with `extension dev`, `extension start`, or `extension preview`:
|
|
74
74
|
|
|
75
|
-
- Select a browser: `--browser <chrome | edge | firefox>`
|
|
75
|
+
- Select a browser: `--browser <chrome | edge | firefox | safari>`
|
|
76
76
|
- Custom Chromium binary: `--chromium-binary <path-to-binary>`
|
|
77
77
|
- Custom Gecko (Firefox) binary: `--gecko-binary <path-to-binary>`
|
|
78
78
|
|
|
@@ -94,7 +94,7 @@ npx extension@latest dev --gecko-binary "/Applications/Firefox.app/Contents/MacO
|
|
|
94
94
|
|
|
95
95
|
| <img alt="Chrome" src="https://media.extension.land/logos/browsers/chrome.svg" width="70"> | <img alt="Edge" src="https://media.extension.land/logos/browsers/edge.svg" width="70"> | <img alt="Firefox" src="https://media.extension.land/logos/browsers/firefox.svg" width="70"> | <img alt="Safari" src="https://media.extension.land/logos/browsers/safari.svg" width="70"> | <img alt="Chromium" src="https://media.extension.land/logos/browsers/chromium.svg" width="70"> | <img alt="Gecko" src="https://media.extension.land/logos/browsers/firefox.svg" width="70"> |
|
|
96
96
|
| :-: | :-: | :-: | :-: | :-: | :-: |
|
|
97
|
-
| Google Chrome<br>✅ Supported | Microsoft Edge<br>✅ Supported | Mozilla Firefox<br>✅ Supported | Apple Safari<br
|
|
97
|
+
| Google Chrome<br>✅ Supported | Microsoft Edge<br>✅ Supported | Mozilla Firefox<br>✅ Supported | Apple Safari<br>✅ Supported | Chromium-based<br>✅ Supported | Gecko-based<br>✅ Supported |
|
|
98
98
|
|
|
99
99
|
</div>
|
|
100
100
|
|
|
@@ -165,6 +165,16 @@ npx extension@latest dev https://github.com/GoogleChrome/chrome-extensions-sampl
|
|
|
165
165
|
|
|
166
166
|
[Watch demo](https://github.com/cezaraugusto/extension/assets/4672033/ee221a94-6ec7-4e04-8553-8812288927f1)
|
|
167
167
|
|
|
168
|
+
## Package managers
|
|
169
|
+
|
|
170
|
+
<div align="center">
|
|
171
|
+
|
|
172
|
+
| <img alt="npm" src="https://media.extension.land/logos/package-managers/npm.svg" width="70"> | <img alt="pnpm" src="https://media.extension.land/logos/package-managers/pnpm.svg" width="70"> | <img alt="Yarn" src="https://media.extension.land/logos/package-managers/yarn.svg" width="70"> | <img alt="Bun" src="https://media.extension.land/logos/package-managers/bun.svg" width="70"> | <picture><source media="(prefers-color-scheme: dark)" srcset="https://media.extension.land/logos/package-managers/deno-dark.svg"><img alt="Deno" src="https://media.extension.land/logos/package-managers/deno.svg" width="70"></picture> |
|
|
173
|
+
| :-: | :-: | :-: | :-: | :-: |
|
|
174
|
+
| npm<br>✅ Supported | pnpm<br>✅ Supported | Yarn<br>✅ Supported | Bun<br>✅ Supported | Deno<br>✅ Supported |
|
|
175
|
+
|
|
176
|
+
</div>
|
|
177
|
+
|
|
168
178
|
## Community
|
|
169
179
|
|
|
170
180
|
- Star the repo if Extension.js helped you ship faster
|
package/dist/49.cjs
CHANGED
|
@@ -18,20 +18,74 @@ exports.modules = {
|
|
|
18
18
|
} catch {}
|
|
19
19
|
return removed;
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
function declaresBackgroundContext(outPath) {
|
|
22
22
|
try {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
const id = String(response?.extensionId || '');
|
|
30
|
-
return id || null;
|
|
23
|
+
const manifest = JSON.parse(node_fs__rspack_import_0.readFileSync(node_path__rspack_import_1.join(outPath, 'manifest.json'), 'utf-8'));
|
|
24
|
+
const background = manifest?.background;
|
|
25
|
+
if (!background || 'object' != typeof background) return false;
|
|
26
|
+
return Boolean(background.service_worker || Array.isArray(background.scripts) && background.scripts.length || background.page);
|
|
31
27
|
} catch {
|
|
32
|
-
return
|
|
28
|
+
return false;
|
|
33
29
|
}
|
|
34
30
|
}
|
|
31
|
+
function parseCdpError(error) {
|
|
32
|
+
const raw = String(error?.message || error || '').trim();
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
const message = String(parsed?.message || '').trim();
|
|
36
|
+
if (message) return {
|
|
37
|
+
code: 'number' == typeof parsed.code ? parsed.code : void 0,
|
|
38
|
+
message
|
|
39
|
+
};
|
|
40
|
+
} catch {}
|
|
41
|
+
return {
|
|
42
|
+
message: raw
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const PROTOCOL_ERROR_CODES = new Set([
|
|
46
|
+
-32601,
|
|
47
|
+
-32602
|
|
48
|
+
]);
|
|
49
|
+
async function callLoadUnpacked(cdp, params) {
|
|
50
|
+
try {
|
|
51
|
+
const response = await cdp.sendCommand('Extensions.loadUnpacked', params);
|
|
52
|
+
const id = String(response?.id || response?.extensionId || '');
|
|
53
|
+
return {
|
|
54
|
+
ok: true,
|
|
55
|
+
id
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
...parseCdpError(error)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function loadUnpacked(cdp, outPath) {
|
|
65
|
+
let result = await callLoadUnpacked(cdp, {
|
|
66
|
+
path: outPath
|
|
67
|
+
});
|
|
68
|
+
if (!result.ok && -32602 === result.code) result = await callLoadUnpacked(cdp, {
|
|
69
|
+
extensionPath: outPath
|
|
70
|
+
});
|
|
71
|
+
if (result.ok) return result.id ? {
|
|
72
|
+
status: 'loaded',
|
|
73
|
+
extensionId: result.id
|
|
74
|
+
} : {
|
|
75
|
+
status: 'unknown'
|
|
76
|
+
};
|
|
77
|
+
if (PROTOCOL_ERROR_CODES.has(result.code) || !result.message) return {
|
|
78
|
+
status: 'unknown'
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
status: 'refused',
|
|
82
|
+
reason: result.message
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
async function loadUnpackedIfNeeded(cdp, outPath) {
|
|
86
|
+
const outcome = await loadUnpacked(cdp, outPath);
|
|
87
|
+
return 'loaded' === outcome.status ? outcome.extensionId : null;
|
|
88
|
+
}
|
|
35
89
|
function readManifestInfo(outPath) {
|
|
36
90
|
try {
|
|
37
91
|
const manifestPath = node_path__rspack_import_1.join(outPath, 'manifest.json');
|
|
@@ -45,8 +99,10 @@ exports.modules = {
|
|
|
45
99
|
}
|
|
46
100
|
}
|
|
47
101
|
__webpack_require__.d(__webpack_exports__, {
|
|
48
|
-
|
|
102
|
+
MB: ()=>readManifestInfo,
|
|
103
|
+
iF: ()=>loadUnpacked,
|
|
49
104
|
loadUnpackedIfNeeded: ()=>loadUnpackedIfNeeded,
|
|
105
|
+
tg: ()=>declaresBackgroundContext,
|
|
50
106
|
uninstallStaleUnpackedLoads: ()=>uninstallStaleUnpackedLoads
|
|
51
107
|
});
|
|
52
108
|
},
|
|
@@ -133,6 +189,7 @@ exports.modules = {
|
|
|
133
189
|
var external_node_path_ = __webpack_require__("node:path");
|
|
134
190
|
var banner = __webpack_require__("./browsers/browsers-lib/banner.ts");
|
|
135
191
|
var messages = __webpack_require__("./browsers/browsers-lib/messages.ts");
|
|
192
|
+
var ready_stamp = __webpack_require__("./browsers/browsers-lib/ready-stamp.ts");
|
|
136
193
|
var shared_utils = __webpack_require__("./browsers/browsers-lib/shared-utils.ts");
|
|
137
194
|
var external_ws_ = __webpack_require__("ws");
|
|
138
195
|
var external_ws_default = /*#__PURE__*/ __webpack_require__.n(external_ws_);
|
|
@@ -737,6 +794,50 @@ exports.modules = {
|
|
|
737
794
|
url
|
|
738
795
|
});
|
|
739
796
|
}
|
|
797
|
+
async verifyGuestLoaded() {
|
|
798
|
+
if (!this.cdp) return {
|
|
799
|
+
status: 'unknown'
|
|
800
|
+
};
|
|
801
|
+
if (!external_node_fs_.existsSync(this.outPath)) return {
|
|
802
|
+
status: 'unknown'
|
|
803
|
+
};
|
|
804
|
+
const expectedId = (0, banner.vy)(this.outPath);
|
|
805
|
+
if (expectedId && (0, ensure.tg)(this.outPath)) {
|
|
806
|
+
const present = await this.waitForExtensionTarget(expectedId, 12, 200);
|
|
807
|
+
if (present) {
|
|
808
|
+
if (!this.extensionId) this.extensionId = expectedId;
|
|
809
|
+
return {
|
|
810
|
+
status: 'loaded',
|
|
811
|
+
extensionId: expectedId
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
let outcome;
|
|
816
|
+
try {
|
|
817
|
+
outcome = await (0, ensure.iF)(this.cdp, this.outPath);
|
|
818
|
+
} catch {
|
|
819
|
+
return {
|
|
820
|
+
status: 'unknown'
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
if ('loaded' === outcome.status && !this.extensionId) this.extensionId = outcome.extensionId;
|
|
824
|
+
if ('refused' === outcome.status) this.loadRefusalReason = outcome.reason;
|
|
825
|
+
return outcome;
|
|
826
|
+
}
|
|
827
|
+
getLoadRefusalReason() {
|
|
828
|
+
return this.loadRefusalReason;
|
|
829
|
+
}
|
|
830
|
+
async waitForExtensionTarget(extensionId, maxRetries, backoffMs) {
|
|
831
|
+
const prefix = `chrome-extension://${extensionId}/`;
|
|
832
|
+
for(let attempt = 0; attempt <= maxRetries; attempt++){
|
|
833
|
+
try {
|
|
834
|
+
const targets = await this.cdp?.getTargets();
|
|
835
|
+
if ((targets || []).some((t)=>String(t?.url || '').startsWith(prefix))) return true;
|
|
836
|
+
} catch {}
|
|
837
|
+
if (attempt < maxRetries) await new Promise((r)=>setTimeout(r, backoffMs));
|
|
838
|
+
}
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
740
841
|
async ensureLoaded() {
|
|
741
842
|
if (!this.cdp) throw new Error('CDP not connected');
|
|
742
843
|
const exists = external_node_fs_.existsSync(this.outPath);
|
|
@@ -803,7 +904,7 @@ exports.modules = {
|
|
|
803
904
|
version = info?.extensionInfo?.version;
|
|
804
905
|
} catch (error) {
|
|
805
906
|
try {
|
|
806
|
-
const manifest = (0, ensure.
|
|
907
|
+
const manifest = (0, ensure.MB)(this.outPath);
|
|
807
908
|
name = String(manifest?.name || '') || void 0;
|
|
808
909
|
version = String(manifest?.version || '') || void 0;
|
|
809
910
|
} catch (e2) {
|
|
@@ -947,6 +1048,7 @@ exports.modules = {
|
|
|
947
1048
|
cdp_extension_controller_define_property(this, "logSink", void 0);
|
|
948
1049
|
cdp_extension_controller_define_property(this, "cdp", null);
|
|
949
1050
|
cdp_extension_controller_define_property(this, "extensionId", null);
|
|
1051
|
+
cdp_extension_controller_define_property(this, "loadRefusalReason", null);
|
|
950
1052
|
this.outPath = args.outPath;
|
|
951
1053
|
this.cdpPort = args.cdpPort;
|
|
952
1054
|
this.profilePath = args.profilePath;
|
|
@@ -1005,6 +1107,34 @@ exports.modules = {
|
|
|
1005
1107
|
}
|
|
1006
1108
|
}
|
|
1007
1109
|
} catch {}
|
|
1110
|
+
const loadOutcome = await cdpExtensionController.verifyGuestLoaded?.() ?? {
|
|
1111
|
+
status: 'unknown'
|
|
1112
|
+
};
|
|
1113
|
+
if ('refused' === loadOutcome.status) {
|
|
1114
|
+
const refusedPath = extensionOutputPath || selectedExtensionPaths[0] || '';
|
|
1115
|
+
console.error(messages.M6(refusedPath, loadOutcome.reason));
|
|
1116
|
+
plugin.logSink?.({
|
|
1117
|
+
level: 'error',
|
|
1118
|
+
text: `extension_load_refused: ${refusedPath}${loadOutcome.reason ? ` - ${loadOutcome.reason}` : ''}`,
|
|
1119
|
+
source: 'browser'
|
|
1120
|
+
});
|
|
1121
|
+
(0, ready_stamp.sf)(extensionOutputPath, loadOutcome.reason);
|
|
1122
|
+
plugin.extensionLoadRefused = loadOutcome.reason;
|
|
1123
|
+
plugin.cdpController = cdpExtensionController;
|
|
1124
|
+
plugin.printBannerOnRecovery = async ()=>{
|
|
1125
|
+
await (0, banner.ai)({
|
|
1126
|
+
outPath: extensionOutputPath,
|
|
1127
|
+
browser: plugin.browser,
|
|
1128
|
+
hostPort: {
|
|
1129
|
+
host: '127.0.0.1',
|
|
1130
|
+
port: chromeRemoteDebugPort
|
|
1131
|
+
},
|
|
1132
|
+
getInfo: async ()=>cdpExtensionController.getInfoBestEffort(),
|
|
1133
|
+
browserVersionLine: plugin.browserVersionLine
|
|
1134
|
+
});
|
|
1135
|
+
};
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1008
1138
|
const mode = compilation?.options?.mode || 'development';
|
|
1009
1139
|
let earlyBannerPrinted = false;
|
|
1010
1140
|
if ('development' === mode) try {
|