Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 145038c6a2 | |||
| 6e7184d98d | |||
| ee49837804 | |||
| 67f972eab8 | |||
| b29c3c6fdd | |||
| ef6001e0c0 | |||
| 636aa48e95 | |||
| ec66b6172c | |||
| 1edd94e8f4 | |||
| 2efc193b45 | |||
| 2e51e7349d | |||
| 28733df5f9 | |||
| 0110a2bd4e | |||
| afe838a348 | |||
| 0255028ce2 | |||
| 249aa5eccc | |||
| b7c078f4e4 |
@@ -47,4 +47,4 @@ jobs:
|
||||
pnpm publish
|
||||
|
||||
- name: GitHub Release
|
||||
run: pnpx changelogen@latest gh release
|
||||
run: pnpx changelogen@latest gh release --token ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.output
|
||||
coverage
|
||||
dist
|
||||
e2e/project
|
||||
|
||||
+62
-2
@@ -1,5 +1,67 @@
|
||||
# Changelog
|
||||
|
||||
## v0.1.2
|
||||
|
||||
[compare changes](https://github.com/aklinker1/wxt/compare/v0.1.1...v0.1.2)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- Accept a function for `config.manifest` ([ee49837](https://github.com/aklinker1/wxt/commit/ee49837))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Add missing types for `webextension-polyfill` and the `manifest` option ([636aa48](https://github.com/aklinker1/wxt/commit/636aa48))
|
||||
- Only add imports to JS files ([b29c3c6](https://github.com/aklinker1/wxt/commit/b29c3c6))
|
||||
- Generate valid type for `EntrypointPath` when there are no entrypoints ([6e7184d](https://github.com/aklinker1/wxt/commit/6e7184d))
|
||||
|
||||
### 🌊 Types
|
||||
|
||||
- Change `config.vite` to `UserConfig` ([ef6001e](https://github.com/aklinker1/wxt/commit/ef6001e))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron Klinker
|
||||
|
||||
## v0.1.1
|
||||
|
||||
[compare changes](https://github.com/aklinker1/wxt/compare/v0.1.0...v0.1.1)
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Allow dashes in entrypoint names ([2e51e73](https://github.com/aklinker1/wxt/commit/2e51e73))
|
||||
- Unable to read entrypoint options ([#28](https://github.com/aklinker1/wxt/pull/28))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron Klinker
|
||||
|
||||
## v0.1.0
|
||||
|
||||
Initial release of WXT. Full support for production builds and initial toolkit for development:
|
||||
|
||||
- HMR support when HTML page dependencies change
|
||||
- Reload extension when background changes
|
||||
- Reload HTML pages when saving them directly
|
||||
- Re-register and reload tabs when content scripts change
|
||||
|
||||
[compare changes](https://github.com/aklinker1/wxt/compare/v0.0.2...v0.1.0)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- Content scripts reloading ([#25](https://github.com/aklinker1/wxt/pull/25))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Update feature list ([0255028](https://github.com/aklinker1/wxt/commit/0255028))
|
||||
|
||||
### 🤖 CI
|
||||
|
||||
- Create github release ([b7c078f](https://github.com/aklinker1/wxt/commit/b7c078f))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron Klinker
|
||||
|
||||
## v0.0.2
|
||||
|
||||
[compare changes](https://github.com/aklinker1/wxt/compare/v0.0.1...v0.0.2)
|
||||
@@ -25,7 +87,6 @@
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- Update changelog ([a9af4d3](https://github.com/aklinker1/wxt/commit/a9af4d3))
|
||||
- Refactor build output type ([#19](https://github.com/aklinker1/wxt/pull/19))
|
||||
- Refactor build outputs to support transpiled templates ([a78aada](https://github.com/aklinker1/wxt/commit/a78aada))
|
||||
- Rename `templates` to `virtual-modules` ([#24](https://github.com/aklinker1/wxt/pull/24))
|
||||
@@ -34,7 +95,6 @@
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron Klinker
|
||||
- Aaron
|
||||
|
||||
## v0.0.1
|
||||
|
||||
|
||||
@@ -4,31 +4,22 @@
|
||||
|
||||

|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> [](https://github.com/aklinker1/wxt/milestone/1)
|
||||
>
|
||||
> WXT is not ready for use yet. Production builds work, but dev mode is incomplete.
|
||||
>
|
||||
> See the [Initial Release Candidate](https://github.com/aklinker1/wxt/milestone/1) milestone for more details.
|
||||
|
||||
## Features
|
||||
|
||||
- 🌐 Supports all browsers
|
||||
- ✅ Supports both MV2 and MV3
|
||||
- 📂 Directory based entrypoints
|
||||
- ⚡ Dev mode with HMR & auto-reload
|
||||
- 🚔 TypeScript
|
||||
- 🦾 Auto-imports
|
||||
- ⚡ Dev mode with HMR
|
||||
- ⬇️ Download and bundle remote URL imports
|
||||
- 🎨 Frontend framework agnostic: works with Vue, React, Svelte, etc
|
||||
|
||||
### Todo
|
||||
|
||||
- 🤖 Automated publishing
|
||||
- 🎨 Frontend framework agnostic: works with Vue, React, Svelte, etc
|
||||
- 🖍️ Quickly bootstrap a new project
|
||||
- 📏 Bundle analysis
|
||||
- ⚡ Dev mode with HMR and **_auto-reload_**
|
||||
- 🤖 Automated publishing
|
||||
|
||||
## Get Started
|
||||
|
||||
|
||||
@@ -69,4 +69,28 @@ describe('Output Directory Structure', () => {
|
||||
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"*://*/*\\"],\\"css\\":[\\"assets/one.css\\",\\"assets/two.css\\"],\\"js\\":[\\"content-scripts/one.js\\",\\"content-scripts/two.js\\"]}]}"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should allow inputs with invalid JS variable names, like dashes', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile(
|
||||
'entrypoints/overlay-one.content.ts',
|
||||
`export default defineContentScript({
|
||||
matches: ["*://*/*"],
|
||||
main: () => {},
|
||||
})`,
|
||||
);
|
||||
|
||||
await project.build();
|
||||
|
||||
expect(await project.serializeOutput()).toMatchInlineSnapshot(`
|
||||
".output/chrome-mv3/content-scripts/overlay-one.js
|
||||
----------------------------------------
|
||||
(function(){\\"use strict\\";function i(n){return n}const o={matches:[\\"*://*/*\\"],main:()=>{}};function t(n,...e){if(typeof e[0]==\\"string\\"){const c=e.shift();n(\`[wxt] \${c}\`,...e)}else n(\\"[wxt]\\",...e)}var r={debug:(...n)=>t(console.debug,...n),log:(...n)=>t(console.log,...n),warn:(...n)=>t(console.warn,...n),error:(...n)=>t(console.error,...n)};(async()=>{try{await o.main()}catch(n){r.error(\\"The content script crashed on startup!\\",n)}})()})();
|
||||
|
||||
================================================================================
|
||||
.output/chrome-mv3/manifest.json
|
||||
----------------------------------------
|
||||
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"*://*/*\\"],\\"js\\":[\\"content-scripts/overlay-one.js\\"]}]}"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,4 +55,27 @@ describe('User Config', () => {
|
||||
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"background\\":{\\"service_worker\\":\\"background.js\\"}}"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should accept a function for a manifest', async () => {
|
||||
const project = new TestProject();
|
||||
|
||||
await project.build({
|
||||
// @ts-expect-error: Specifically setting an invalid field for the test - it should show up in the snapshot
|
||||
manifest: ({ mode, browser, manifestVersion, command }) => ({
|
||||
example_customization: [
|
||||
mode,
|
||||
browser,
|
||||
String(manifestVersion),
|
||||
command,
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const output = await project.serializeOutput();
|
||||
expect(output).toMatchInlineSnapshot(`
|
||||
".output/chrome-mv3/manifest.json
|
||||
----------------------------------------
|
||||
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"example_customization\\":[\\"production\\",\\"chrome\\",\\"3\\",\\"build\\"]}"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
+6
-3
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.0.2",
|
||||
"version": "0.1.2",
|
||||
"description": "Next gen framework for developing web extensions",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -52,6 +52,9 @@
|
||||
"prepublish": "pnpm -s build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/webextension-polyfill": "^0.10.0",
|
||||
"@webext-core/fake-browser": "^1.2.2",
|
||||
"@webext-core/match-patterns": "^1.0.1",
|
||||
"async-mutex": "^0.4.0",
|
||||
"c12": "^1.4.2",
|
||||
"cac": "^6.7.14",
|
||||
@@ -75,7 +78,6 @@
|
||||
"@types/lodash.merge": "^4.6.7",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/picomatch": "^2.3.0",
|
||||
"@types/webextension-polyfill": "^0.10.0",
|
||||
"@vitest/coverage-v8": "^0.32.2",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
@@ -86,7 +88,8 @@
|
||||
"tsup": "^7.0.0",
|
||||
"tsx": "^3.12.7",
|
||||
"typescript": "^5.1.3",
|
||||
"vitest": "^0.32.2",
|
||||
"vitest": "^0.32.4",
|
||||
"vitest-mock-extended": "^1.1.4",
|
||||
"webextension-polyfill": "^0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
Generated
+189
-188
@@ -10,6 +10,8 @@ importers:
|
||||
'@types/picomatch': ^2.3.0
|
||||
'@types/webextension-polyfill': ^0.10.0
|
||||
'@vitest/coverage-v8': ^0.32.2
|
||||
'@webext-core/fake-browser': ^1.2.2
|
||||
'@webext-core/match-patterns': ^1.0.1
|
||||
async-mutex: ^0.4.0
|
||||
c12: ^1.4.2
|
||||
cac: ^6.7.14
|
||||
@@ -33,10 +35,14 @@ importers:
|
||||
typescript: ^5.1.3
|
||||
unimport: ^3.0.8
|
||||
vite: ^4.3.9
|
||||
vitest: ^0.32.2
|
||||
vitest: ^0.32.4
|
||||
vitest-mock-extended: ^1.1.4
|
||||
web-ext: ^7.6.2
|
||||
webextension-polyfill: ^0.10.0
|
||||
dependencies:
|
||||
'@types/webextension-polyfill': 0.10.0
|
||||
'@webext-core/fake-browser': 1.2.2
|
||||
'@webext-core/match-patterns': 1.0.1
|
||||
async-mutex: 0.4.0
|
||||
c12: 1.4.2
|
||||
cac: 6.7.14
|
||||
@@ -59,8 +65,7 @@ importers:
|
||||
'@types/lodash.merge': 4.6.7
|
||||
'@types/node': 20.3.1
|
||||
'@types/picomatch': 2.3.0
|
||||
'@types/webextension-polyfill': 0.10.0
|
||||
'@vitest/coverage-v8': 0.32.2_vitest@0.32.2
|
||||
'@vitest/coverage-v8': 0.32.2_vitest@0.32.4
|
||||
lodash.merge: 4.6.2
|
||||
npm-run-all: 4.1.5
|
||||
ora: 6.3.1
|
||||
@@ -70,7 +75,8 @@ importers:
|
||||
tsup: 7.0.0_typescript@5.1.3
|
||||
tsx: 3.12.7
|
||||
typescript: 5.1.3
|
||||
vitest: 0.32.2
|
||||
vitest: 0.32.4
|
||||
vitest-mock-extended: 1.1.4_ekuvgbe6yxmpeqet3xraeb3oaa
|
||||
|
||||
demo:
|
||||
specifiers:
|
||||
@@ -218,10 +224,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/android-arm/0.18.5:
|
||||
/@esbuild/android-arm/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+8GXQzuASxGg/rb47Z5zJe3vjOfL7RRce/DILuk6kbB/8HO0p3CPo72CbR349P2K8YP1h5NvNqU+2GDRbNJylw==,
|
||||
integrity: sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [arm]
|
||||
@@ -241,10 +247,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/android-arm64/0.18.5:
|
||||
/@esbuild/android-arm64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-410IPUj7ZOxZ2dwK0B7o7Nibu7YEyaLBvYOfYBpuA1TpY0fOkDM5r4bwn+hT8Uma06DBI4RnYNN09fn55PYInQ==,
|
||||
integrity: sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [arm64]
|
||||
@@ -264,10 +270,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/android-x64/0.18.5:
|
||||
/@esbuild/android-x64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+fdfceCYwcz9OReheSWYOGaAAt03n0BnG5/UW9tyGyo15PjSOF14ylxfjvz+0atDx0S/RxyezMsH/mbnWhnC8w==,
|
||||
integrity: sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [x64]
|
||||
@@ -287,10 +293,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/darwin-arm64/0.18.5:
|
||||
/@esbuild/darwin-arm64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-L7noeTaus5xEtgd5J7u/lGrZfSiYkvZb0gOD7rvKTuuWbdGM4bunz5DUFsWBbEIlloslpOO5PDy4Hnd6mZT20A==,
|
||||
integrity: sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [arm64]
|
||||
@@ -310,10 +316,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/darwin-x64/0.18.5:
|
||||
/@esbuild/darwin-x64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-eA39B8SxbxRdSSILD4AsePzvJiVao6ZaYrcTOJqg89jnnMEGR/EAh+ehV7E4GOx4WXQoWeJRP1P9JQSzIrROeg==,
|
||||
integrity: sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [x64]
|
||||
@@ -333,10 +339,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/freebsd-arm64/0.18.5:
|
||||
/@esbuild/freebsd-arm64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Eg1UnkTZHfsphgcy1Wj/McNModSO/F+kqtWqvtvEZc9BAgvdwxAt11BESgBczU+Gti0G2dLvHs0Sfb3gavwhGg==,
|
||||
integrity: sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [arm64]
|
||||
@@ -356,10 +362,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/freebsd-x64/0.18.5:
|
||||
/@esbuild/freebsd-x64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-GNTMSJ55gl7Tf5VUqVRkMJhRGzH6vI9vFBfZCj4Zjm7RgfXCWxLnTyjMgZZKT8pOzW40KD2KlrGbqwnnJWyGWw==,
|
||||
integrity: sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [x64]
|
||||
@@ -379,10 +385,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-arm/0.18.5:
|
||||
/@esbuild/linux-arm/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6R+vEIyfEvp+gOWKSc+m6hdnhWKQYzicqONQYiDGT6qepc6OGsLEZcyFwoz6BvFx5j233CBWMcJ69eXFrwXw9A==,
|
||||
integrity: sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [arm]
|
||||
@@ -402,10 +408,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-arm64/0.18.5:
|
||||
/@esbuild/linux-arm64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-r08LmhqyPRj6FtuNPBTu8BliKh6h+oNEhMkWmmR/aWs4DWjDOivyDfLGznPdgtSThL23fk1QgSBUEbuCIzjA2A==,
|
||||
integrity: sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [arm64]
|
||||
@@ -425,10 +431,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-ia32/0.18.5:
|
||||
/@esbuild/linux-ia32/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ph6M9iEMc6BHgv2XuIE8qeQrQCH+2l116c8L9ysmmXYwpNXa3E7JNIu/O7hI0I9qDvh1P19AGbIh+/y0GAZijA==,
|
||||
integrity: sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [ia32]
|
||||
@@ -448,10 +454,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-loong64/0.18.5:
|
||||
/@esbuild/linux-loong64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-s6Nup5FMQ8R8OKJG2rSxtV40s8LRdfC73XGHGaFlGiC+2SeCyq4dl3MMfLdzLowYzyDjfc4GRrXWUNMX3kNxYA==,
|
||||
integrity: sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [loong64]
|
||||
@@ -471,10 +477,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-mips64el/0.18.5:
|
||||
/@esbuild/linux-mips64el/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-DxW4nNDIGbivZxnJD01C5PlwKPpin8YgSwWtToCy4w4lNigT7Iaf5A+wcPT2laibdgbcgPKpPOXUg6RFGTt8xA==,
|
||||
integrity: sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [mips64el]
|
||||
@@ -494,10 +500,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-ppc64/0.18.5:
|
||||
/@esbuild/linux-ppc64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-BksOs2uYTafS+u75QiN4RoLbEMNjE192adJCBalncI3E2PWyR2i1kEs9rEghHK7pw0SD0uWgV9otRmV7G5b2lQ==,
|
||||
integrity: sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [ppc64]
|
||||
@@ -517,10 +523,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-riscv64/0.18.5:
|
||||
/@esbuild/linux-riscv64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-mGv8BOJXsV7bZyjyMdeDs55CDXZ5vrY3oKa58DNRz2vPn54dREyj4BhhyWuqSuzSURJhFg7pM/1fI2vnAHGkHw==,
|
||||
integrity: sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [riscv64]
|
||||
@@ -540,10 +546,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-s390x/0.18.5:
|
||||
/@esbuild/linux-s390x/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-m4uIYyrl5znGnNHgiM/Zsw6I9Se513NqdTxeUxZ66/VDWbuUp8ACe1KOSpwF4NNxfYy6Q3W8beZsIdF4F85q8Q==,
|
||||
integrity: sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [s390x]
|
||||
@@ -563,10 +569,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-x64/0.18.5:
|
||||
/@esbuild/linux-x64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-R1C7X30YjXmOZYOzx4dJ/QvRNfrkK/sDCFfcGNhlHFX6B/iodJdk81h7EhnKVUQy+3BaARxF7udd91iSSzMlbQ==,
|
||||
integrity: sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [x64]
|
||||
@@ -586,10 +592,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/netbsd-x64/0.18.5:
|
||||
/@esbuild/netbsd-x64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-MABnKzjMcXjO0NEYyexOhqjcrgM6dE8BXnm+lctm2x2aPpYg5iL0Ew3aABSTZyp9dS3Z4VzFu5PPoOYEw8akTQ==,
|
||||
integrity: sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [x64]
|
||||
@@ -609,10 +615,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/openbsd-x64/0.18.5:
|
||||
/@esbuild/openbsd-x64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-aU7R0tLIUMaQuAgBjKrq02Z98rcY9Pxk76hynSqcGeld2C/ro1uBbS2i9rh7vdwBAY0rG08Og4wnDnlx5rU+fQ==,
|
||||
integrity: sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [x64]
|
||||
@@ -632,10 +638,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/sunos-x64/0.18.5:
|
||||
/@esbuild/sunos-x64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ngm3fVv2VxufI8zH/Phk0mYkgvFjFGnS+l7uxxd20mmeLTNI/8OXDJpNqTUbvzJh3tqhI/Gof0N2+5xJbqEaxA==,
|
||||
integrity: sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [x64]
|
||||
@@ -655,10 +661,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-arm64/0.18.5:
|
||||
/@esbuild/win32-arm64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-XqpS89+MGLzR8YtQQkBYsLCfAv1ySflMb+FEH99rOp6kOPv/ORO+ujEB5ICDBZZbvYqB75uFrNELo1BVEQbS3g==,
|
||||
integrity: sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [arm64]
|
||||
@@ -678,10 +684,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-ia32/0.18.5:
|
||||
/@esbuild/win32-ia32/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-V3xj/nb9uie0I4mn1f8nPZSgHldtNJrqTKYjTyMPMBnHbMYF5Loz8ZHsp7+La8kI6NxIF1ClQ9XBV+G3RtSkww==,
|
||||
integrity: sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [ia32]
|
||||
@@ -701,10 +707,10 @@ packages:
|
||||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-x64/0.18.5:
|
||||
/@esbuild/win32-x64/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-gMxWvQeTQWDpa8ExPP41al+Ho7HyK24h7y41JdGKqE24KzXXQPxESUtrCoIES+HwF+OGq2smtibU9UvZ8WH3JQ==,
|
||||
integrity: sha512-rQI4cjLHd2hGsM1LqgDI7oOCYbQ6IBOVsX9ejuRMSze0GqXUG2ekwiKkiBU1pRGSeCqFFHxTrcEydB2Hyoz9CA==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
cpu: [x64]
|
||||
@@ -815,6 +821,16 @@ packages:
|
||||
engines: { node: '>=8' }
|
||||
dev: true
|
||||
|
||||
/@jest/schemas/29.6.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==,
|
||||
}
|
||||
engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 }
|
||||
dependencies:
|
||||
'@sinclair/typebox': 0.27.8
|
||||
dev: true
|
||||
|
||||
/@jridgewell/gen-mapping/0.3.3:
|
||||
resolution:
|
||||
{
|
||||
@@ -947,6 +963,13 @@ packages:
|
||||
picomatch: 2.3.1
|
||||
dev: false
|
||||
|
||||
/@sinclair/typebox/0.27.8:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==,
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@sindresorhus/is/5.4.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -1061,7 +1084,6 @@ packages:
|
||||
{
|
||||
integrity: sha512-If4EcaHzYTqcbNMp/FdReVdRmLL/Te42ivnJII551bYjhX19bWem5m14FERCqdJA732OloGuxCRvLBvcMGsn4A==,
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@types/yauzl/2.10.0:
|
||||
resolution:
|
||||
@@ -1072,7 +1094,7 @@ packages:
|
||||
'@types/node': 20.3.1
|
||||
dev: false
|
||||
|
||||
/@vitest/coverage-v8/0.32.2_vitest@0.32.2:
|
||||
/@vitest/coverage-v8/0.32.2_vitest@0.32.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-/+V3nB3fyeuuSeKxCfi6XmWjDIxpky7AWSkGVfaMjAk7di8igBwRsThLjultwIZdTDH1RAxpjmCXEfSqsMFZOA==,
|
||||
@@ -1091,65 +1113,89 @@ packages:
|
||||
std-env: 3.3.3
|
||||
test-exclude: 6.0.0
|
||||
v8-to-istanbul: 9.1.0
|
||||
vitest: 0.32.2
|
||||
vitest: 0.32.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@vitest/expect/0.32.2:
|
||||
/@vitest/expect/0.32.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6q5yzweLnyEv5Zz1fqK5u5E83LU+gOMVBDuxBl2d2Jfx1BAp5M+rZgc5mlyqdnxquyoiOXpXmFNkcGcfFnFH3Q==,
|
||||
integrity: sha512-m7EPUqmGIwIeoU763N+ivkFjTzbaBn0n9evsTOcde03ugy2avPs3kZbYmw3DkcH1j5mxhMhdamJkLQ6dM1bk/A==,
|
||||
}
|
||||
dependencies:
|
||||
'@vitest/spy': 0.32.2
|
||||
'@vitest/utils': 0.32.2
|
||||
'@vitest/spy': 0.32.4
|
||||
'@vitest/utils': 0.32.4
|
||||
chai: 4.3.7
|
||||
dev: true
|
||||
|
||||
/@vitest/runner/0.32.2:
|
||||
/@vitest/runner/0.32.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-06vEL0C1pomOEktGoLjzZw+1Fb+7RBRhmw/06WkDrd1akkT9i12su0ku+R/0QM69dfkIL/rAIDTG+CSuQVDcKw==,
|
||||
integrity: sha512-cHOVCkiRazobgdKLnczmz2oaKK9GJOw6ZyRcaPdssO1ej+wzHVIkWiCiNacb3TTYPdzMddYkCgMjZ4r8C0JFCw==,
|
||||
}
|
||||
dependencies:
|
||||
'@vitest/utils': 0.32.2
|
||||
concordance: 5.0.4
|
||||
'@vitest/utils': 0.32.4
|
||||
p-limit: 4.0.0
|
||||
pathe: 1.1.1
|
||||
dev: true
|
||||
|
||||
/@vitest/snapshot/0.32.2:
|
||||
/@vitest/snapshot/0.32.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-JwhpeH/PPc7GJX38vEfCy9LtRzf9F4er7i4OsAJyV7sjPwjj+AIR8cUgpMTWK4S3TiamzopcTyLsZDMuldoi5A==,
|
||||
integrity: sha512-IRpyqn9t14uqsFlVI2d7DFMImGMs1Q9218of40bdQQgMePwVdmix33yMNnebXcTzDU5eiV3eUsoxxH5v0x/IQA==,
|
||||
}
|
||||
dependencies:
|
||||
magic-string: 0.30.0
|
||||
pathe: 1.1.1
|
||||
pretty-format: 27.5.1
|
||||
pretty-format: 29.6.0
|
||||
dev: true
|
||||
|
||||
/@vitest/spy/0.32.2:
|
||||
/@vitest/spy/0.32.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Q/ZNILJ4ca/VzQbRM8ur3Si5Sardsh1HofatG9wsJY1RfEaw0XKP8IVax2lI1qnrk9YPuG9LA2LkZ0EI/3d4ug==,
|
||||
integrity: sha512-oA7rCOqVOOpE6rEoXuCOADX7Lla1LIa4hljI2MSccbpec54q+oifhziZIJXxlE/CvI2E+ElhBHzVu0VEvJGQKQ==,
|
||||
}
|
||||
dependencies:
|
||||
tinyspy: 2.1.1
|
||||
dev: true
|
||||
|
||||
/@vitest/utils/0.32.2:
|
||||
/@vitest/utils/0.32.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-lnJ0T5i03j0IJaeW73hxe2AuVnZ/y1BhhCOuIcl9LIzXnbpXJT9Lrt6brwKHXLOiA7MZ6N5hSJjt0xE1dGNCzQ==,
|
||||
integrity: sha512-Gwnl8dhd1uJ+HXrYyV0eRqfmk9ek1ASE/LWfTCuWMw+d07ogHqp4hEAV28NiecimK6UY9DpSEPh+pXBA5gtTBg==,
|
||||
}
|
||||
dependencies:
|
||||
diff-sequences: 29.4.3
|
||||
loupe: 2.3.6
|
||||
pretty-format: 27.5.1
|
||||
pretty-format: 29.6.0
|
||||
dev: true
|
||||
|
||||
/@webcomponents/webcomponentsjs/2.8.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-loGD63sacRzOzSJgQnB9ZAhaQGkN7wl2Zuw7tsphI5Isa0irijrRo6EnJii/GgjGefIFO8AIO7UivzRhFaEk9w==,
|
||||
}
|
||||
dev: false
|
||||
|
||||
/@webext-core/fake-browser/1.2.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-3w0lU0gAYODoMklGQfBuGwIxR+lakYK8FONRFUg8aEdpHUMMzbf5jPwRaTrnsgqupE0JAoBwk8vF6san/BtDeQ==,
|
||||
}
|
||||
dependencies:
|
||||
lodash.merge: 4.6.2
|
||||
dev: false
|
||||
|
||||
/@webext-core/match-patterns/1.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-huti7vdxWQ10XlkmEO/nC7gVmzAcxNRj+Kdp+HrnRzh2PY9pv5BLrjL8QXzN5EdJjxP154yr7/Q7+Z6xe0TZDA==,
|
||||
}
|
||||
dependencies:
|
||||
'@webcomponents/webcomponentsjs': 2.8.0
|
||||
dev: false
|
||||
|
||||
/abort-controller/3.0.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -1330,6 +1376,7 @@ packages:
|
||||
integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==,
|
||||
}
|
||||
engines: { node: '>=8' }
|
||||
dev: false
|
||||
|
||||
/ansi-regex/6.0.1:
|
||||
resolution:
|
||||
@@ -1576,13 +1623,6 @@ packages:
|
||||
}
|
||||
dev: false
|
||||
|
||||
/blueimp-md5/2.19.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==,
|
||||
}
|
||||
dev: true
|
||||
|
||||
/boolbase/1.0.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -1663,7 +1703,7 @@ packages:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
|
||||
/bundle-require/4.0.1_esbuild@0.18.5:
|
||||
/bundle-require/4.0.1_esbuild@0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==,
|
||||
@@ -1672,7 +1712,7 @@ packages:
|
||||
peerDependencies:
|
||||
esbuild: '>=0.17'
|
||||
dependencies:
|
||||
esbuild: 0.18.5
|
||||
esbuild: 0.18.11
|
||||
load-tsconfig: 0.2.5
|
||||
dev: true
|
||||
|
||||
@@ -2072,23 +2112,6 @@ packages:
|
||||
typedarray: 0.0.6
|
||||
dev: false
|
||||
|
||||
/concordance/5.0.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==,
|
||||
}
|
||||
engines: { node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14' }
|
||||
dependencies:
|
||||
date-time: 3.1.0
|
||||
esutils: 2.0.3
|
||||
fast-diff: 1.3.0
|
||||
js-string-escape: 1.0.1
|
||||
lodash: 4.17.21
|
||||
md5-hex: 3.0.1
|
||||
semver: 7.5.2
|
||||
well-known-symbols: 2.0.0
|
||||
dev: true
|
||||
|
||||
/config-chain/1.1.13:
|
||||
resolution:
|
||||
{
|
||||
@@ -2230,16 +2253,6 @@ packages:
|
||||
engines: { node: '>= 12' }
|
||||
dev: false
|
||||
|
||||
/date-time/3.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==,
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
dependencies:
|
||||
time-zone: 1.0.0
|
||||
dev: true
|
||||
|
||||
/debounce/1.2.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -2667,37 +2680,37 @@ packages:
|
||||
'@esbuild/win32-ia32': 0.17.19
|
||||
'@esbuild/win32-x64': 0.17.19
|
||||
|
||||
/esbuild/0.18.5:
|
||||
/esbuild/0.18.11:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ztF1Z53Mc8ijEo1ZWFduHZXIqRWufo76JHm1ikvhGjIzO1mj84LdKXSGmRzahfgvWSwky48MkT+o5yUIkQtDPA==,
|
||||
integrity: sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA==,
|
||||
}
|
||||
engines: { node: '>=12' }
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@esbuild/android-arm': 0.18.5
|
||||
'@esbuild/android-arm64': 0.18.5
|
||||
'@esbuild/android-x64': 0.18.5
|
||||
'@esbuild/darwin-arm64': 0.18.5
|
||||
'@esbuild/darwin-x64': 0.18.5
|
||||
'@esbuild/freebsd-arm64': 0.18.5
|
||||
'@esbuild/freebsd-x64': 0.18.5
|
||||
'@esbuild/linux-arm': 0.18.5
|
||||
'@esbuild/linux-arm64': 0.18.5
|
||||
'@esbuild/linux-ia32': 0.18.5
|
||||
'@esbuild/linux-loong64': 0.18.5
|
||||
'@esbuild/linux-mips64el': 0.18.5
|
||||
'@esbuild/linux-ppc64': 0.18.5
|
||||
'@esbuild/linux-riscv64': 0.18.5
|
||||
'@esbuild/linux-s390x': 0.18.5
|
||||
'@esbuild/linux-x64': 0.18.5
|
||||
'@esbuild/netbsd-x64': 0.18.5
|
||||
'@esbuild/openbsd-x64': 0.18.5
|
||||
'@esbuild/sunos-x64': 0.18.5
|
||||
'@esbuild/win32-arm64': 0.18.5
|
||||
'@esbuild/win32-ia32': 0.18.5
|
||||
'@esbuild/win32-x64': 0.18.5
|
||||
'@esbuild/android-arm': 0.18.11
|
||||
'@esbuild/android-arm64': 0.18.11
|
||||
'@esbuild/android-x64': 0.18.11
|
||||
'@esbuild/darwin-arm64': 0.18.11
|
||||
'@esbuild/darwin-x64': 0.18.11
|
||||
'@esbuild/freebsd-arm64': 0.18.11
|
||||
'@esbuild/freebsd-x64': 0.18.11
|
||||
'@esbuild/linux-arm': 0.18.11
|
||||
'@esbuild/linux-arm64': 0.18.11
|
||||
'@esbuild/linux-ia32': 0.18.11
|
||||
'@esbuild/linux-loong64': 0.18.11
|
||||
'@esbuild/linux-mips64el': 0.18.11
|
||||
'@esbuild/linux-ppc64': 0.18.11
|
||||
'@esbuild/linux-riscv64': 0.18.11
|
||||
'@esbuild/linux-s390x': 0.18.11
|
||||
'@esbuild/linux-x64': 0.18.11
|
||||
'@esbuild/netbsd-x64': 0.18.11
|
||||
'@esbuild/openbsd-x64': 0.18.11
|
||||
'@esbuild/sunos-x64': 0.18.11
|
||||
'@esbuild/win32-arm64': 0.18.11
|
||||
'@esbuild/win32-ia32': 0.18.11
|
||||
'@esbuild/win32-x64': 0.18.11
|
||||
dev: true
|
||||
|
||||
/escalade/3.1.1:
|
||||
@@ -2903,6 +2916,7 @@ packages:
|
||||
integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==,
|
||||
}
|
||||
engines: { node: '>=0.10.0' }
|
||||
dev: false
|
||||
|
||||
/event-target-shim/5.0.1:
|
||||
resolution:
|
||||
@@ -2977,13 +2991,6 @@ packages:
|
||||
}
|
||||
dev: false
|
||||
|
||||
/fast-diff/1.3.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==,
|
||||
}
|
||||
dev: true
|
||||
|
||||
/fast-glob/3.2.12:
|
||||
resolution:
|
||||
{
|
||||
@@ -4287,14 +4294,6 @@ packages:
|
||||
}
|
||||
dev: false
|
||||
|
||||
/js-string-escape/1.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==,
|
||||
}
|
||||
engines: { node: '>= 0.8' }
|
||||
dev: true
|
||||
|
||||
/js-tokens/4.0.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -4632,6 +4631,7 @@ packages:
|
||||
{
|
||||
integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==,
|
||||
}
|
||||
dev: false
|
||||
|
||||
/log-symbols/5.1.0:
|
||||
resolution:
|
||||
@@ -4669,6 +4669,7 @@ packages:
|
||||
engines: { node: '>=10' }
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/lru-cache/9.1.2:
|
||||
resolution:
|
||||
@@ -4721,16 +4722,6 @@ packages:
|
||||
}
|
||||
dev: false
|
||||
|
||||
/md5-hex/3.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==,
|
||||
}
|
||||
engines: { node: '>=8' }
|
||||
dependencies:
|
||||
blueimp-md5: 2.19.0
|
||||
dev: true
|
||||
|
||||
/mem/5.1.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -5684,16 +5675,16 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/pretty-format/27.5.1:
|
||||
/pretty-format/29.6.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==,
|
||||
integrity: sha512-XH+D4n7Ey0iSR6PdAnBs99cWMZdGsdKrR33iUHQNr79w1szKTCIZDVdXuccAsHVwDBp0XeWPfNEoaxP9EZgRmQ==,
|
||||
}
|
||||
engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 }
|
||||
engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 }
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
'@jest/schemas': 29.6.0
|
||||
ansi-styles: 5.2.0
|
||||
react-is: 17.0.2
|
||||
react-is: 18.2.0
|
||||
dev: true
|
||||
|
||||
/pretty-quick/3.1.3_prettier@2.8.8:
|
||||
@@ -5849,10 +5840,10 @@ packages:
|
||||
flat: 5.0.2
|
||||
dev: false
|
||||
|
||||
/react-is/17.0.2:
|
||||
/react-is/18.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==,
|
||||
integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==,
|
||||
}
|
||||
dev: true
|
||||
|
||||
@@ -6239,6 +6230,7 @@ packages:
|
||||
hasBin: true
|
||||
dependencies:
|
||||
lru-cache: 6.0.0
|
||||
dev: false
|
||||
|
||||
/setimmediate/1.0.5:
|
||||
resolution:
|
||||
@@ -6823,14 +6815,6 @@ packages:
|
||||
}
|
||||
dev: false
|
||||
|
||||
/time-zone/1.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==,
|
||||
}
|
||||
engines: { node: '>=4' }
|
||||
dev: true
|
||||
|
||||
/tinybench/2.5.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -6909,6 +6893,17 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/ts-essentials/9.3.2_typescript@5.1.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-JxKJzuWqH1MmH4ZFHtJzGEhkfN3QvVR3C3w+4BIoWeoY68UVVoA2Np/Bca9z0IPSErVCWhv439aT0We4Dks8kQ==,
|
||||
}
|
||||
peerDependencies:
|
||||
typescript: '>=4.1.0'
|
||||
dependencies:
|
||||
typescript: 5.1.3
|
||||
dev: true
|
||||
|
||||
/ts-interface-checker/0.1.13:
|
||||
resolution:
|
||||
{
|
||||
@@ -6942,11 +6937,11 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
bundle-require: 4.0.1_esbuild@0.18.5
|
||||
bundle-require: 4.0.1_esbuild@0.18.11
|
||||
cac: 6.7.14
|
||||
chokidar: 3.5.3
|
||||
debug: 4.3.4
|
||||
esbuild: 0.18.5
|
||||
esbuild: 0.18.11
|
||||
execa: 5.1.1
|
||||
globby: 11.1.0
|
||||
joycon: 3.1.1
|
||||
@@ -7250,10 +7245,10 @@ packages:
|
||||
extsprintf: 1.3.0
|
||||
dev: false
|
||||
|
||||
/vite-node/0.32.2_@types+node@20.3.1:
|
||||
/vite-node/0.32.4_@types+node@20.3.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-dTQ1DCLwl2aEseov7cfQ+kDMNJpM1ebpyMMMwWzBvLbis8Nla/6c9WQcqpPssTwS6Rp/+U6KwlIj8Eapw4bLdA==,
|
||||
integrity: sha512-L2gIw+dCxO0LK14QnUMoqSYpa9XRGnTTTDjW2h19Mr+GR0EFj4vx52W41gFXfMLqpA00eK4ZjOVYo1Xk//LFEw==,
|
||||
}
|
||||
engines: { node: '>=v14.18.0' }
|
||||
hasBin: true
|
||||
@@ -7309,10 +7304,24 @@ packages:
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
|
||||
/vitest/0.32.2:
|
||||
/vitest-mock-extended/1.1.4_ekuvgbe6yxmpeqet3xraeb3oaa:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-hU8GNNuQfwuQmqTLfiKcqEhZY72Zxb7nnN07koCUNmntNxbKQnVbeIS6sqUgR3eXSlbOpit8+/gr1KpqoMgWCQ==,
|
||||
integrity: sha512-MKPdepFjk0CE7Ocb5txntHK/sloHO9s0Kk9dr630uL03shRSHMkZtSs5CBPsbJ8K/CvYLv0GHUmMt2zWks2eXg==,
|
||||
}
|
||||
peerDependencies:
|
||||
typescript: 3.x || 4.x || 5.x
|
||||
vitest: '>=0.31.1'
|
||||
dependencies:
|
||||
ts-essentials: 9.3.2_typescript@5.1.3
|
||||
typescript: 5.1.3
|
||||
vitest: 0.32.4
|
||||
dev: true
|
||||
|
||||
/vitest/0.32.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-3czFm8RnrsWwIzVDu/Ca48Y/M+qh3vOnF16czJm98Q/AN1y3B6PBsyV8Re91Ty5s7txKNjEhpgtGPcfdbh2MZg==,
|
||||
}
|
||||
engines: { node: '>=v14.18.0' }
|
||||
hasBin: true
|
||||
@@ -7346,16 +7355,15 @@ packages:
|
||||
'@types/chai': 4.3.5
|
||||
'@types/chai-subset': 1.3.3
|
||||
'@types/node': 20.3.1
|
||||
'@vitest/expect': 0.32.2
|
||||
'@vitest/runner': 0.32.2
|
||||
'@vitest/snapshot': 0.32.2
|
||||
'@vitest/spy': 0.32.2
|
||||
'@vitest/utils': 0.32.2
|
||||
'@vitest/expect': 0.32.4
|
||||
'@vitest/runner': 0.32.4
|
||||
'@vitest/snapshot': 0.32.4
|
||||
'@vitest/spy': 0.32.4
|
||||
'@vitest/utils': 0.32.4
|
||||
acorn: 8.9.0
|
||||
acorn-walk: 8.2.0
|
||||
cac: 6.7.14
|
||||
chai: 4.3.7
|
||||
concordance: 5.0.4
|
||||
debug: 4.3.4
|
||||
local-pkg: 0.4.3
|
||||
magic-string: 0.30.0
|
||||
@@ -7366,7 +7374,7 @@ packages:
|
||||
tinybench: 2.5.0
|
||||
tinypool: 0.5.0
|
||||
vite: 4.3.9_@types+node@20.3.1
|
||||
vite-node: 0.32.2_@types+node@20.3.1
|
||||
vite-node: 0.32.4_@types+node@20.3.1
|
||||
why-is-node-running: 2.2.2
|
||||
transitivePeerDependencies:
|
||||
- less
|
||||
@@ -7482,14 +7490,6 @@ packages:
|
||||
}
|
||||
dev: false
|
||||
|
||||
/well-known-symbols/2.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==,
|
||||
}
|
||||
engines: { node: '>=6' }
|
||||
dev: true
|
||||
|
||||
/whatwg-url/7.1.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -7702,6 +7702,7 @@ packages:
|
||||
{
|
||||
integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==,
|
||||
}
|
||||
dev: false
|
||||
|
||||
/yaml/2.3.1:
|
||||
resolution:
|
||||
|
||||
@@ -57,6 +57,13 @@ await Promise.all([
|
||||
sourcemap: true,
|
||||
silent: true,
|
||||
}),
|
||||
tsup.build({
|
||||
entry: {
|
||||
'virtual-modules/fake-browser': `src/client/virtual-modules/fake-browser.ts`,
|
||||
},
|
||||
format: ['esm', 'cjs'],
|
||||
silent: true,
|
||||
}),
|
||||
]).catch((err) => {
|
||||
spinner.fail();
|
||||
console.error(err);
|
||||
|
||||
@@ -23,7 +23,7 @@ export const dev = defineCommand<
|
||||
};
|
||||
|
||||
const server = await wxt.createServer(cliConfig);
|
||||
await server.listen(server.port);
|
||||
await server.start();
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import browser, { Manifest } from 'webextension-polyfill';
|
||||
import { logger } from './logger';
|
||||
import { MatchPattern } from '@webext-core/match-patterns';
|
||||
|
||||
export function reloadContentScript(contentScript: Manifest.ContentScript) {
|
||||
const manifest = browser.runtime.getManifest();
|
||||
if (manifest.manifest_version == 2) {
|
||||
void reloadContentScriptMv2(contentScript);
|
||||
} else {
|
||||
void reloadContentScriptMv3(contentScript);
|
||||
}
|
||||
}
|
||||
|
||||
export async function reloadContentScriptMv3(
|
||||
contentScript: Manifest.ContentScript,
|
||||
) {
|
||||
const id = `wxt:${contentScript.js![0]}`;
|
||||
logger.log('Reloading content script:', contentScript);
|
||||
const registered = await browser.scripting.getRegisteredContentScripts();
|
||||
logger.debug('Existing scripts:', registered);
|
||||
|
||||
const existing = registered.find((cs) => cs.id === id);
|
||||
|
||||
if (existing) {
|
||||
logger.debug('Updating content script', existing);
|
||||
await browser.scripting.updateContentScripts([{ ...contentScript, id }]);
|
||||
} else {
|
||||
logger.debug('Registering new content script...');
|
||||
await browser.scripting.registerContentScripts([{ ...contentScript, id }]);
|
||||
}
|
||||
|
||||
const allTabs = await browser.tabs.query({});
|
||||
const matchPatterns = contentScript.matches.map(
|
||||
(match) => new MatchPattern(match),
|
||||
);
|
||||
const matchingTabs = allTabs.filter((tab) => {
|
||||
const url = tab.url;
|
||||
if (!url) return false;
|
||||
return !!matchPatterns.find((pattern) => pattern.includes(url));
|
||||
});
|
||||
await Promise.all(matchingTabs.map((tab) => browser.tabs.reload(tab.id)));
|
||||
}
|
||||
|
||||
export async function reloadContentScriptMv2(
|
||||
contentScript: Manifest.ContentScript,
|
||||
) {
|
||||
throw Error('TODO: reloadContentScriptMv2');
|
||||
}
|
||||
@@ -3,15 +3,26 @@ import { setupWebSocket } from '../utils/setupWebSocket';
|
||||
import { logger } from '../utils/logger';
|
||||
import browser from 'webextension-polyfill';
|
||||
import { keepServiceWorkerAlive } from '../utils/keepServiceWorkerAlive';
|
||||
import { reloadContentScript } from '../utils/reloadContentScript';
|
||||
|
||||
if (__COMMAND__ === 'serve') {
|
||||
try {
|
||||
setupWebSocket((message) => {
|
||||
const ws = setupWebSocket((message) => {
|
||||
if (message.event === 'wxt:reload-extension') browser.runtime.reload();
|
||||
if (message.event === 'wxt:reload-content-script' && message.data != null)
|
||||
reloadContentScript(message.data);
|
||||
});
|
||||
|
||||
// Web Socket will disconnect if the service worker is killed
|
||||
keepServiceWorkerAlive();
|
||||
if (__MANIFEST_VERSION__ === 3) {
|
||||
// Tell the server the background script is loaded and ready to go
|
||||
ws.addEventListener('open', () => {
|
||||
const msg = { type: 'custom', event: 'wxt:background-initialized' };
|
||||
ws.send(JSON.stringify(msg));
|
||||
});
|
||||
|
||||
// Web Socket will disconnect if the service worker is killed
|
||||
keepServiceWorkerAlive();
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Failed to setup web socket connection with dev server', err);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { fakeBrowser as browser } from '@webext-core/fake-browser';
|
||||
|
||||
export default browser;
|
||||
@@ -20,3 +20,4 @@ declare const __COMMAND__: 'build' | 'serve';
|
||||
declare const __DEV_SERVER_PROTOCOL__: string;
|
||||
declare const __DEV_SERVER_HOSTNAME__: string;
|
||||
declare const __DEV_SERVER_PORT__: string;
|
||||
declare const __MANIFEST_VERSION__: 2 | 3;
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import { Manifest } from 'webextension-polyfill';
|
||||
import { BuildOutput } from './types';
|
||||
import { buildEntrypoints } from './build/buildEntrypoints';
|
||||
import { findEntrypoints } from './build/findEntrypoints';
|
||||
import { generateTypesDir } from './build/generateTypesDir';
|
||||
import { InternalConfig, EntrypointGroup } from './types';
|
||||
import { generateMainfest, writeManifest } from './utils/manifest';
|
||||
import pc from 'picocolors';
|
||||
import * as vite from 'vite';
|
||||
import fs from 'fs-extra';
|
||||
import { groupEntrypoints } from './utils/groupEntrypoints';
|
||||
import { formatDuration } from './utils/formatDuration';
|
||||
import { printBuildSummary } from './log/printBuildSummary';
|
||||
|
||||
/**
|
||||
* Builds the extension based on an internal config.
|
||||
*
|
||||
* This function:
|
||||
* 1. Cleans the output directory
|
||||
* 2. Executes the rebuild function with a blank previous output so everything is built (see
|
||||
* `rebuild` for more details)
|
||||
* 3. Prints the summary
|
||||
*/
|
||||
export async function buildInternal(
|
||||
config: InternalConfig,
|
||||
): Promise<BuildOutput> {
|
||||
const verb = config.command === 'serve' ? 'Pre-rendering' : 'Building';
|
||||
const target = `${config.browser}-mv${config.manifestVersion}`;
|
||||
config.logger.info(
|
||||
`${verb} ${pc.cyan(target)} for ${pc.cyan(config.mode)} with ${pc.green(
|
||||
`Vite ${vite.version}`,
|
||||
)}`,
|
||||
);
|
||||
const startTime = Date.now();
|
||||
|
||||
// Cleanup
|
||||
await fs.rm(config.outDir, { recursive: true, force: true });
|
||||
await fs.ensureDir(config.outDir);
|
||||
|
||||
const entrypoints = await findEntrypoints(config);
|
||||
const groups = groupEntrypoints(entrypoints);
|
||||
const { output } = await rebuild(config, groups);
|
||||
|
||||
// Post-build
|
||||
config.logger.success(
|
||||
`Built extension in ${formatDuration(Date.now() - startTime)}`,
|
||||
);
|
||||
await printBuildSummary(output, config);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a configuration, list of entrypoints, and an existing, partial output, build the
|
||||
* entrypoints and merge the new output with the existing output.
|
||||
*
|
||||
* This function will:
|
||||
* 1. Generate the .wxt directory's types
|
||||
* 2. Build the `entrypointGroups` (and copies public files)
|
||||
* 3. Generate the latest manifest for all entrypoints
|
||||
* 4. Write the new manifest to the file system
|
||||
*/
|
||||
export async function rebuild(
|
||||
config: InternalConfig,
|
||||
entrypointGroups: EntrypointGroup[],
|
||||
existingOutput: Omit<BuildOutput, 'manifest'> = {
|
||||
steps: [],
|
||||
publicAssets: [],
|
||||
},
|
||||
): Promise<{ output: BuildOutput; manifest: Manifest.WebExtensionManifest }> {
|
||||
// Update types directory with new files and types
|
||||
const allEntrypoints = await findEntrypoints(config);
|
||||
await generateTypesDir(allEntrypoints, config);
|
||||
|
||||
// Build and merge the outputs
|
||||
const newOutput = await buildEntrypoints(entrypointGroups, config);
|
||||
const mergedOutput: Omit<BuildOutput, 'manifest'> = {
|
||||
steps: [...existingOutput.steps, ...newOutput.steps],
|
||||
publicAssets: [...existingOutput.publicAssets, ...newOutput.publicAssets],
|
||||
};
|
||||
|
||||
const newManifest = await generateMainfest(
|
||||
allEntrypoints,
|
||||
mergedOutput,
|
||||
config,
|
||||
);
|
||||
const finalOutput: BuildOutput = {
|
||||
manifest: newManifest,
|
||||
...newOutput,
|
||||
};
|
||||
|
||||
// Write manifest
|
||||
await writeManifest(newManifest, finalOutput, config);
|
||||
|
||||
return {
|
||||
output: {
|
||||
manifest: newManifest,
|
||||
steps: [...existingOutput.steps, ...finalOutput.steps],
|
||||
publicAssets: [
|
||||
...existingOutput.publicAssets,
|
||||
...finalOutput.publicAssets,
|
||||
],
|
||||
},
|
||||
manifest: newManifest,
|
||||
};
|
||||
}
|
||||
@@ -7,10 +7,11 @@ import {
|
||||
PopupEntrypoint,
|
||||
} from '../../types';
|
||||
import { resolve } from 'path';
|
||||
import { FindEntrypointsConfig, findEntrypoints } from '../findEntrypoints';
|
||||
import { findEntrypoints } from '../findEntrypoints';
|
||||
import fs from 'fs-extra';
|
||||
import { importTsFile } from '../../utils/importTsFile';
|
||||
import glob from 'fast-glob';
|
||||
import { fakeInternalConfig } from '../../../testing/fake-objects';
|
||||
|
||||
vi.mock('../../utils/importTsFile');
|
||||
const importTsFileMock = vi.mocked(importTsFile);
|
||||
@@ -24,24 +25,11 @@ const readFileMock = vi.mocked(
|
||||
);
|
||||
|
||||
describe('findEntrypoints', () => {
|
||||
const config: FindEntrypointsConfig = {
|
||||
const config = fakeInternalConfig({
|
||||
root: '/',
|
||||
entrypointsDir: resolve('/src/entrypoints'),
|
||||
outDir: resolve('.output'),
|
||||
logger: {
|
||||
debug: vi.fn(),
|
||||
error: vi.fn(),
|
||||
fatal: (...args) => {
|
||||
throw Error('logger.fatal called with: ' + JSON.stringify(args));
|
||||
},
|
||||
info: vi.fn(),
|
||||
log: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
success: vi.fn(),
|
||||
},
|
||||
command: 'build',
|
||||
mode: 'production',
|
||||
};
|
||||
});
|
||||
|
||||
it.each<[string, string, PopupEntrypoint]>([
|
||||
[
|
||||
@@ -189,7 +177,7 @@ describe('findEntrypoints', () => {
|
||||
|
||||
expect(entrypoints).toHaveLength(1);
|
||||
expect(entrypoints[0]).toEqual({ ...expected, options });
|
||||
expect(importTsFileMock).toBeCalledWith(config.root, expected.inputPath);
|
||||
expect(importTsFileMock).toBeCalledWith(expected.inputPath, config);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -216,7 +204,7 @@ describe('findEntrypoints', () => {
|
||||
|
||||
expect(entrypoints).toHaveLength(1);
|
||||
expect(entrypoints[0]).toEqual({ ...expected, options });
|
||||
expect(importTsFileMock).toBeCalledWith(config.root, expected.inputPath);
|
||||
expect(importTsFileMock).toBeCalledWith(expected.inputPath, config);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -45,12 +45,12 @@ async function buildSingleEntrypoint(
|
||||
? `virtual:wxt-${entrypoint.type}?${entrypoint.inputPath}`
|
||||
: entrypoint.inputPath;
|
||||
|
||||
const libMode: vite.InlineConfig = {
|
||||
const libMode: vite.UserConfig = {
|
||||
build: {
|
||||
lib: {
|
||||
entry,
|
||||
formats: ['iife'],
|
||||
name: entrypoint.name,
|
||||
name: '_',
|
||||
fileName: entrypoint.name,
|
||||
},
|
||||
rollupOptions: {
|
||||
@@ -89,7 +89,7 @@ async function buildMultipleEntrypoints(
|
||||
entrypoints: Entrypoint[],
|
||||
config: InternalConfig,
|
||||
): Promise<BuildStepOutput> {
|
||||
const multiPage: vite.InlineConfig = {
|
||||
const multiPage: vite.UserConfig = {
|
||||
plugins: [plugins.multipageMove(entrypoints, config)],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
@@ -112,7 +112,7 @@ async function buildMultipleEntrypoints(
|
||||
const entryConfig = vite.mergeConfig(
|
||||
multiPage,
|
||||
config.vite,
|
||||
) as vite.InlineConfig;
|
||||
) as vite.UserConfig;
|
||||
|
||||
const result = await vite.build(entryConfig);
|
||||
return {
|
||||
|
||||
@@ -21,7 +21,7 @@ import { getEntrypointName } from '../utils/entrypoints';
|
||||
* Return entrypoints and their configuration by looking through the
|
||||
*/
|
||||
export async function findEntrypoints(
|
||||
config: FindEntrypointsConfig,
|
||||
config: InternalConfig,
|
||||
): Promise<Entrypoint[]> {
|
||||
const relativePaths = await glob('**/*', {
|
||||
cwd: config.entrypointsDir,
|
||||
@@ -103,7 +103,7 @@ export async function findEntrypoints(
|
||||
* @param content String contents of the file at the path.
|
||||
*/
|
||||
async function getPopupEntrypoint(
|
||||
config: FindEntrypointsConfig,
|
||||
config: InternalConfig,
|
||||
path: string,
|
||||
): Promise<PopupEntrypoint> {
|
||||
const options: PopupEntrypoint['options'] = {};
|
||||
@@ -150,7 +150,7 @@ async function getPopupEntrypoint(
|
||||
* @param content String contents of the file at the path.
|
||||
*/
|
||||
async function getOptionsEntrypoint(
|
||||
config: FindEntrypointsConfig,
|
||||
config: InternalConfig,
|
||||
path: string,
|
||||
): Promise<OptionsEntrypoint> {
|
||||
const options: OptionsEntrypoint['options'] = {};
|
||||
@@ -192,11 +192,11 @@ async function getOptionsEntrypoint(
|
||||
* @param path Absolute path to the background's TS file.
|
||||
*/
|
||||
async function getBackgroundEntrypoint(
|
||||
config: FindEntrypointsConfig,
|
||||
config: InternalConfig,
|
||||
path: string,
|
||||
): Promise<BackgroundEntrypoint> {
|
||||
const { main: _, ...options } =
|
||||
await importTsFile<BackgroundScriptDefintition>(config.root, path);
|
||||
await importTsFile<BackgroundScriptDefintition>(path, config);
|
||||
if (options == null) {
|
||||
throw Error('Background script does not have a default export');
|
||||
}
|
||||
@@ -213,13 +213,13 @@ async function getBackgroundEntrypoint(
|
||||
* @param path Absolute path to the content script's TS file.
|
||||
*/
|
||||
async function getContentScriptEntrypoint(
|
||||
config: FindEntrypointsConfig,
|
||||
config: InternalConfig,
|
||||
name: string,
|
||||
path: string,
|
||||
): Promise<ContentScriptEntrypoint> {
|
||||
const { main: _, ...options } = await importTsFile<ContentScriptDefinition>(
|
||||
config.root,
|
||||
path,
|
||||
config,
|
||||
);
|
||||
if (options == null) {
|
||||
throw Error(`Content script ${name} does not have a default export`);
|
||||
@@ -274,8 +274,3 @@ const PATH_GLOB_TO_TYPE_MAP: Record<string, Entrypoint['type'] | 'ignored'> = {
|
||||
// Don't warn about any files in subdirectories, like CSS or JS entrypoints for HTML files
|
||||
'*/*': 'ignored',
|
||||
};
|
||||
|
||||
export type FindEntrypointsConfig = Pick<
|
||||
InternalConfig,
|
||||
'root' | 'entrypointsDir' | 'outDir' | 'logger' | 'mode' | 'command'
|
||||
>;
|
||||
|
||||
@@ -48,22 +48,23 @@ async function writePathsDeclarationFile(
|
||||
config: InternalConfig,
|
||||
): Promise<string> {
|
||||
const filePath = resolve(config.typesDir, 'paths.d.ts');
|
||||
const unions = entrypoints
|
||||
.map((entry) => {
|
||||
const path = getEntrypointBundlePath(
|
||||
entry,
|
||||
config.outDir,
|
||||
entry.inputPath.endsWith('.html') ? '.html' : '.js',
|
||||
);
|
||||
return ` | "/${path}"`;
|
||||
})
|
||||
.sort();
|
||||
|
||||
await fs.writeFile(
|
||||
filePath,
|
||||
[
|
||||
'// Generated by wxt',
|
||||
'type EntrypointPath =',
|
||||
...entrypoints
|
||||
.map((entry) => {
|
||||
const path = getEntrypointBundlePath(
|
||||
entry,
|
||||
config.outDir,
|
||||
entry.inputPath.endsWith('.html') ? '.html' : '.js',
|
||||
);
|
||||
return ` | "/${path}"`;
|
||||
})
|
||||
.sort(),
|
||||
...(unions.length === 0 ? [' never'] : unions),
|
||||
].join('\n') + '\n',
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import {
|
||||
BuildStepOutput,
|
||||
EntrypointGroup,
|
||||
InternalConfig,
|
||||
WxtDevServer,
|
||||
} from './types';
|
||||
import * as vite from 'vite';
|
||||
import { findOpenPort } from './utils/findOpenPort';
|
||||
import { Manifest } from 'webextension-polyfill';
|
||||
import { getEntrypointBundlePath } from './utils/entrypoints';
|
||||
import { getContentScriptCssFiles } from './utils/manifest';
|
||||
import { createWebExtRunner } from './runners/createWebExtRunner';
|
||||
import { buildInternal } from './build';
|
||||
|
||||
export async function getServerInfo(): Promise<ServerInfo> {
|
||||
const port = await findOpenPort(3000, 3010);
|
||||
const hostname = 'localhost';
|
||||
const origin = `http://${hostname}:${port}`;
|
||||
const serverConfig: vite.InlineConfig = {
|
||||
server: {
|
||||
origin,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
port,
|
||||
hostname,
|
||||
origin,
|
||||
viteServerConfig: serverConfig,
|
||||
};
|
||||
}
|
||||
|
||||
export async function setupServer(
|
||||
serverInfo: ServerInfo,
|
||||
config: InternalConfig,
|
||||
): Promise<WxtDevServer> {
|
||||
const runner = createWebExtRunner();
|
||||
|
||||
const viteServer = await vite.createServer(
|
||||
vite.mergeConfig(serverInfo, config.vite),
|
||||
);
|
||||
|
||||
const start = async () => {
|
||||
await viteServer.listen(server.port);
|
||||
config.logger.success(`Started dev server @ ${serverInfo.origin}`);
|
||||
|
||||
server.currentOutput = await buildInternal(config);
|
||||
config.logger.info('Opening browser...');
|
||||
await runner.openBrowser(config);
|
||||
config.logger.success('Opened!');
|
||||
};
|
||||
|
||||
const reloadExtension = () => {
|
||||
viteServer.ws.send('wxt:reload-extension');
|
||||
};
|
||||
const reloadPage = (path: string) => {
|
||||
// Can't use Vite's built-in "full-reload" event because it doesn't like our paths, it expects
|
||||
// paths ending in "/index.html"
|
||||
viteServer.ws.send('wxt:reload-page', path);
|
||||
};
|
||||
const reloadContentScript = (contentScript: Manifest.ContentScript) => {
|
||||
viteServer.ws.send('wxt:reload-content-script', contentScript);
|
||||
};
|
||||
|
||||
const server: WxtDevServer = {
|
||||
...viteServer,
|
||||
start,
|
||||
currentOutput: {
|
||||
manifest: {
|
||||
manifest_version: 3,
|
||||
name: '',
|
||||
version: '',
|
||||
},
|
||||
publicAssets: [],
|
||||
steps: [],
|
||||
},
|
||||
port: serverInfo.port,
|
||||
hostname: serverInfo.hostname,
|
||||
origin: serverInfo.origin,
|
||||
reloadExtension,
|
||||
reloadPage,
|
||||
reloadContentScript,
|
||||
};
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* From the server, tell the client to reload content scripts from the provided build step outputs.
|
||||
*/
|
||||
export function reloadContentScripts(
|
||||
steps: BuildStepOutput[],
|
||||
config: InternalConfig,
|
||||
server: WxtDevServer,
|
||||
) {
|
||||
if (config.manifestVersion === 3) {
|
||||
steps.forEach((step) => {
|
||||
const entry = step.entrypoints;
|
||||
if (Array.isArray(entry) || entry.type !== 'content-script') return;
|
||||
|
||||
const js = [getEntrypointBundlePath(entry, config.outDir, '.js')];
|
||||
const css = getContentScriptCssFiles([entry], server.currentOutput);
|
||||
|
||||
server.reloadContentScript({
|
||||
js,
|
||||
css,
|
||||
...entry.options,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
server.reloadExtension();
|
||||
}
|
||||
}
|
||||
|
||||
export function reloadHtmlPages(
|
||||
groups: EntrypointGroup[],
|
||||
server: WxtDevServer,
|
||||
config: InternalConfig,
|
||||
) {
|
||||
groups.flat().forEach((entry) => {
|
||||
const path = getEntrypointBundlePath(entry, config.outDir, '.html');
|
||||
server.reloadPage(path);
|
||||
});
|
||||
}
|
||||
|
||||
interface ServerInfo {
|
||||
port: number;
|
||||
hostname: string;
|
||||
origin: string;
|
||||
viteServerConfig: vite.InlineConfig;
|
||||
}
|
||||
@@ -19,8 +19,8 @@ export interface InlineConfig {
|
||||
browser?: TargetBrowser;
|
||||
manifestVersion?: TargetManifestVersion;
|
||||
logger?: Logger;
|
||||
vite?: Omit<vite.InlineConfig, 'root' | 'configFile' | 'mode'>;
|
||||
manifest?: UserManifest;
|
||||
vite?: Omit<vite.UserConfig, 'root' | 'configFile' | 'mode'>;
|
||||
manifest?: UserManifest | Promise<UserManifest> | UserManifestFn;
|
||||
server?: WxtDevServer;
|
||||
runner?: ExtensionRunnerConfig;
|
||||
}
|
||||
@@ -54,6 +54,14 @@ export interface WxtDevServer extends vite.ViteDevServer {
|
||||
* Ex: `"http://localhost:3000"`
|
||||
*/
|
||||
origin: string;
|
||||
/**
|
||||
* Stores the current build output of the server.
|
||||
*/
|
||||
currentOutput: BuildOutput;
|
||||
/**
|
||||
* Start the server on the first open port.
|
||||
*/
|
||||
start(): Promise<void>;
|
||||
/**
|
||||
* Tell the extension to reload by running `browser.runtime.reload`.
|
||||
*/
|
||||
@@ -70,6 +78,12 @@ export interface WxtDevServer extends vite.ViteDevServer {
|
||||
* server.reloadPage("sandbox.html")
|
||||
*/
|
||||
reloadPage: (path: string) => void;
|
||||
/**
|
||||
* Tell the extension to restart a content script.
|
||||
*
|
||||
* @param contentScript The manifest definition for a content script
|
||||
*/
|
||||
reloadContentScript: (contentScript: Manifest.ContentScript) => void;
|
||||
}
|
||||
|
||||
export type TargetBrowser = 'chrome' | 'firefox' | 'safari' | 'edge' | 'opera';
|
||||
@@ -125,7 +139,6 @@ export interface GenericEntrypoint extends BaseEntrypoint {
|
||||
| 'sidepanel'
|
||||
| 'devtools'
|
||||
| 'unlisted-page'
|
||||
| 'unlisted-page'
|
||||
| 'unlisted-script';
|
||||
}
|
||||
|
||||
@@ -213,6 +226,23 @@ export type UserManifest = Omit<
|
||||
| 'version_name'
|
||||
>;
|
||||
|
||||
export type UserManifestFn = (
|
||||
env: ConfigEnv,
|
||||
) => UserManifest | Promise<UserManifest>;
|
||||
|
||||
export interface ConfigEnv {
|
||||
mode: string;
|
||||
command: 'build' | 'serve';
|
||||
/**
|
||||
* Browser passed in from the CLI
|
||||
*/
|
||||
browser: TargetBrowser;
|
||||
/**
|
||||
* Manifest version passed in from the CLI
|
||||
*/
|
||||
manifestVersion: 2 | 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure how the browser starts up.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { every } from '../arrays';
|
||||
|
||||
describe('Array Utils', () => {
|
||||
describe('every', () => {
|
||||
it('should return true when the array is empty', () => {
|
||||
expect(every([], () => false)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when all item predicate's return true", () => {
|
||||
expect(every([1, 1, 1], (item) => item === 1)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false when a single item predicate's return false", () => {
|
||||
expect(every([1, 2, 1], (item) => item === 1)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,10 @@ import {
|
||||
fakeBackgroundEntrypoint,
|
||||
fakeContentScriptEntrypoint,
|
||||
fakeFile,
|
||||
fakeGenericEntrypoint,
|
||||
fakeManifest,
|
||||
fakeOptionsEntrypoint,
|
||||
fakePopupEntrypoint,
|
||||
fakeRollupOutputAsset,
|
||||
fakeRollupOutputChunk,
|
||||
} from '../../../testing/fake-objects';
|
||||
@@ -129,4 +132,120 @@ describe('Detect Dev Changes', () => {
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTML Pages', () => {
|
||||
it('should rebuild then reload only the effected pages', async () => {
|
||||
const changedPath = '/root/page1/index.html';
|
||||
const htmlPage1 = fakePopupEntrypoint({
|
||||
inputPath: changedPath,
|
||||
});
|
||||
const htmlPage2 = fakeOptionsEntrypoint({
|
||||
inputPath: '/root/page2.html',
|
||||
});
|
||||
const htmlPage3 = fakeGenericEntrypoint({
|
||||
type: 'sandbox',
|
||||
inputPath: '/root/page3.html',
|
||||
});
|
||||
|
||||
const step1: BuildStepOutput = {
|
||||
entrypoints: [htmlPage1, htmlPage2],
|
||||
chunks: [
|
||||
fakeRollupOutputChunk({
|
||||
moduleIds: [fakeFile(), changedPath],
|
||||
}),
|
||||
],
|
||||
};
|
||||
const step2: BuildStepOutput = {
|
||||
entrypoints: [htmlPage3],
|
||||
chunks: [
|
||||
fakeRollupOutputChunk({
|
||||
moduleIds: [fakeFile(), fakeFile(), fakeFile()],
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
const currentOutput: BuildOutput = {
|
||||
manifest: fakeManifest(),
|
||||
publicAssets: [],
|
||||
steps: [step1, step2],
|
||||
};
|
||||
const expected: DevModeChange = {
|
||||
type: 'html-reload',
|
||||
cachedOutput: {
|
||||
...currentOutput,
|
||||
steps: [step2],
|
||||
},
|
||||
rebuildGroups: [[htmlPage1, htmlPage2]],
|
||||
};
|
||||
|
||||
const actual = detectDevChanges(
|
||||
[['unknown', changedPath]],
|
||||
currentOutput,
|
||||
);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Content Scripts', () => {
|
||||
it('should rebuild then reload only the effected content scripts', async () => {
|
||||
const changedPath = '/root/utils/shared.ts';
|
||||
const script1 = fakeContentScriptEntrypoint({
|
||||
inputPath: '/root/overlay1.content/index.ts',
|
||||
});
|
||||
const script2 = fakeContentScriptEntrypoint({
|
||||
inputPath: '/root/overlay2.ts',
|
||||
});
|
||||
const script3 = fakeContentScriptEntrypoint({
|
||||
inputPath: '/root/overlay3.content/index.ts',
|
||||
});
|
||||
|
||||
const step1: BuildStepOutput = {
|
||||
entrypoints: script1,
|
||||
chunks: [
|
||||
fakeRollupOutputChunk({
|
||||
moduleIds: [fakeFile(), changedPath],
|
||||
}),
|
||||
],
|
||||
};
|
||||
const step2: BuildStepOutput = {
|
||||
entrypoints: script2,
|
||||
chunks: [
|
||||
fakeRollupOutputChunk({
|
||||
moduleIds: [fakeFile(), fakeFile(), fakeFile()],
|
||||
}),
|
||||
],
|
||||
};
|
||||
const step3: BuildStepOutput = {
|
||||
entrypoints: script3,
|
||||
chunks: [
|
||||
fakeRollupOutputChunk({
|
||||
moduleIds: [changedPath, fakeFile(), fakeFile()],
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
const currentOutput: BuildOutput = {
|
||||
manifest: fakeManifest(),
|
||||
publicAssets: [],
|
||||
steps: [step1, step2, step3],
|
||||
};
|
||||
const expected: DevModeChange = {
|
||||
type: 'content-script-reload',
|
||||
cachedOutput: {
|
||||
...currentOutput,
|
||||
steps: [step2],
|
||||
},
|
||||
changedSteps: [step1, step3],
|
||||
rebuildGroups: [script1, script3],
|
||||
};
|
||||
|
||||
const actual = detectDevChanges(
|
||||
[['unknown', changedPath]],
|
||||
currentOutput,
|
||||
);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Checks if `predicate` returns truthy for all elements of the array.
|
||||
*/
|
||||
export function every<T>(
|
||||
array: T[],
|
||||
predicate: (item: T, index: number) => boolean,
|
||||
): boolean {
|
||||
for (let i = 0; i < array.length; i++)
|
||||
if (!predicate(array[i], i)) return false;
|
||||
return true;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BuildOutput, BuildStepOutput, EntrypointGroup } from '../types';
|
||||
import * as vite from 'vite';
|
||||
import { every } from './arrays';
|
||||
|
||||
/**
|
||||
* Compare the changed files vs the build output and determine what kind of reload needs to happen:
|
||||
@@ -60,9 +61,9 @@ export function detectDevChanges(
|
||||
}
|
||||
}
|
||||
|
||||
const isOnlyHtmlChanges = !changedFiles.find(
|
||||
([_, file]) => !file.endsWith('.html'),
|
||||
);
|
||||
const isOnlyHtmlChanges =
|
||||
changedFiles.length > 0 &&
|
||||
every(changedFiles, ([_, file]) => file.endsWith('.html'));
|
||||
if (isOnlyHtmlChanges) {
|
||||
return {
|
||||
type: 'html-reload',
|
||||
@@ -71,6 +72,21 @@ export function detectDevChanges(
|
||||
};
|
||||
}
|
||||
|
||||
const isOnlyContentScripts =
|
||||
changedOutput.steps.length > 0 &&
|
||||
every(
|
||||
changedOutput.steps.flatMap((step) => step.entrypoints),
|
||||
(entry) => entry.type === 'content-script',
|
||||
);
|
||||
if (isOnlyContentScripts) {
|
||||
return {
|
||||
type: 'content-script-reload',
|
||||
cachedOutput: unchangedOutput,
|
||||
changedSteps: changedOutput.steps,
|
||||
rebuildGroups: changedOutput.steps.map((step) => step.entrypoints),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'extension-reload',
|
||||
cachedOutput: unchangedOutput,
|
||||
@@ -114,9 +130,12 @@ function findEffectedSteps(
|
||||
* Contains information about what files changed, what needs rebuilt, and the type of reload that is
|
||||
* required.
|
||||
*/
|
||||
export type DevModeChange = NoChange | HtmlReload | ExtensionReload;
|
||||
export type DevModeChange =
|
||||
| NoChange
|
||||
| HtmlReload
|
||||
| ExtensionReload
|
||||
| ContentScriptReload;
|
||||
// | BrowserRestart
|
||||
// | ContentScriptReload
|
||||
|
||||
interface NoChange {
|
||||
type: 'no-change';
|
||||
@@ -145,9 +164,10 @@ interface ExtensionReload extends RebuildChange {
|
||||
// type: 'browser-restart';
|
||||
// }
|
||||
|
||||
// interface ContentScriptReload extends RebuildChange {
|
||||
// type: 'content-script-reload';
|
||||
// }
|
||||
interface ContentScriptReload extends RebuildChange {
|
||||
type: 'content-script-reload';
|
||||
changedSteps: BuildStepOutput[];
|
||||
}
|
||||
|
||||
/**
|
||||
* When figuring out what needs reloaded, this stores the step that was changed, or the public
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
InlineConfig,
|
||||
InternalConfig,
|
||||
UserConfig,
|
||||
UserManifest,
|
||||
} from '../types';
|
||||
import path, { resolve } from 'node:path';
|
||||
import * as vite from 'vite';
|
||||
import { consola } from 'consola';
|
||||
import { importTsFile } from './importTsFile';
|
||||
import * as plugins from '../vite-plugins';
|
||||
import { createFsCache } from './createFsCache';
|
||||
import { getGlobals } from './globals';
|
||||
@@ -32,6 +32,15 @@ export async function getInternalConfig(
|
||||
const outDir = path.resolve(outBaseDir, `${browser}-mv${manifestVersion}`);
|
||||
const logger = config.logger ?? consola;
|
||||
|
||||
const manifest: UserManifest = await (typeof config.manifest === 'function'
|
||||
? config.manifest({
|
||||
browser,
|
||||
command,
|
||||
manifestVersion,
|
||||
mode,
|
||||
})
|
||||
: config.manifest ?? {});
|
||||
|
||||
const baseConfig: InternalConfigNoUserDirs = {
|
||||
root,
|
||||
outDir,
|
||||
@@ -43,7 +52,7 @@ export async function getInternalConfig(
|
||||
command,
|
||||
logger,
|
||||
vite: config.vite ?? {},
|
||||
manifest: config.manifest ?? {},
|
||||
manifest,
|
||||
imports: config.imports ?? {},
|
||||
runnerConfig: await loadConfig<ExtensionRunnerConfig>({
|
||||
name: 'web-ext',
|
||||
@@ -59,10 +68,12 @@ export async function getInternalConfig(
|
||||
mode,
|
||||
};
|
||||
if (config.configFile !== false) {
|
||||
userConfig = await importTsFile<UserConfig>(
|
||||
root,
|
||||
path.resolve(root, config.configFile ?? 'wxt.config.ts'),
|
||||
);
|
||||
const loaded = await loadConfig<UserConfig>({
|
||||
name: 'wxt',
|
||||
cwd: root,
|
||||
rcFile: false,
|
||||
});
|
||||
userConfig = loaded.config ?? {};
|
||||
}
|
||||
|
||||
// Merge inline and user configs
|
||||
|
||||
@@ -1,44 +1,67 @@
|
||||
import { consola } from 'consola';
|
||||
import createJITI from 'jiti';
|
||||
import transform from 'jiti/dist/babel';
|
||||
import { InternalConfig } from '../types';
|
||||
import { createUnimport } from 'unimport';
|
||||
import fs from 'fs-extra';
|
||||
import { resolve } from 'path';
|
||||
import { scanExports } from 'unimport';
|
||||
import transform from 'jiti/dist/babel';
|
||||
import { getUnimportOptions } from './auto-imports';
|
||||
|
||||
export async function importTsFile<T>(root: string, path: string): Promise<T> {
|
||||
const clientImports = await scanExports(
|
||||
resolve(root, 'node_modules/wxt/dist/client.js'),
|
||||
/**
|
||||
* Get the value from the default export of a `path`.
|
||||
*
|
||||
* It works by:
|
||||
*
|
||||
* 1. Reading the file text
|
||||
* 2. Stripping all imports from it via regex
|
||||
* 3. Auto-import only the client helper functions
|
||||
*
|
||||
* This prevents resolving imports of imports, speeding things up and preventing "xxx is not
|
||||
* defined" errors.
|
||||
*
|
||||
* Downside is that code cannot be executed outside of the main fucntion for the entrypoint,
|
||||
* otherwise you will see "xxx is not defined" errors for any imports used outside of main function.
|
||||
*/
|
||||
export async function importTsFile<T>(
|
||||
path: string,
|
||||
config: InternalConfig,
|
||||
): Promise<T> {
|
||||
config.logger.debug('Loading file metadata:', path);
|
||||
|
||||
const unimport = createUnimport({
|
||||
...getUnimportOptions(config),
|
||||
// Only allow specific imports, not all from the project
|
||||
imports: [{ name: '*', as: 'browser', from: 'webextension-polyfill' }],
|
||||
dirs: [],
|
||||
});
|
||||
await unimport.init();
|
||||
|
||||
const text = await fs.readFile(path, 'utf-8');
|
||||
const textNoImports = text.replace(/import.*[\n;]/gm, '');
|
||||
const { code } = await unimport.injectImports(textNoImports);
|
||||
config.logger.debug(
|
||||
['Text:', text, 'No imports:', textNoImports, 'Code:', code].join('\n'),
|
||||
);
|
||||
|
||||
const jiti = createJITI(__filename, {
|
||||
cache: false,
|
||||
esmResolve: true,
|
||||
interopDefault: true,
|
||||
|
||||
alias: {
|
||||
'webextension-polyfill': resolve(
|
||||
config.root,
|
||||
'node_modules/wxt/dist/virtual-modules/fake-browser.js',
|
||||
),
|
||||
},
|
||||
transform(opts) {
|
||||
// Remove CSS imports from the source code - Jiti can't handle them.
|
||||
opts.source = opts.source.replace(/^import ['"].*\.css['"];?$/gm, '');
|
||||
opts.source = opts.source.replace(
|
||||
/^import\s+.*\s+from ['"]webextension-polyfill['"];?$/gm,
|
||||
'',
|
||||
);
|
||||
|
||||
// Append any wxt/client functions so babel doesn't complain about undefined variables
|
||||
if (opts.filename === path) {
|
||||
// TODO: Only append import if it isn't already imported
|
||||
const imports =
|
||||
clientImports
|
||||
.map((i) => `import { ${i.name} } from "${i.from}";`)
|
||||
.join('\n') + '\n';
|
||||
opts.source = imports + opts.source;
|
||||
}
|
||||
|
||||
// Call the default babel transformer with our modified source code
|
||||
return transform(opts);
|
||||
if (opts.filename === path) return transform({ ...opts, source: code });
|
||||
else return transform(opts);
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
return await jiti(path);
|
||||
} catch (err) {
|
||||
consola.error(`Failed to import file: ${path}`);
|
||||
config.logger.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
+42
-17
@@ -65,6 +65,7 @@ export async function generateMainfest(
|
||||
addEntrypoints(manifest, entrypoints, buildOutput, config);
|
||||
|
||||
if (config.command === 'serve') addDevModeCsp(manifest, config);
|
||||
if (config.command === 'serve') addDevModePermissions(manifest, config);
|
||||
|
||||
return manifest;
|
||||
}
|
||||
@@ -270,24 +271,23 @@ function addEntrypoints(
|
||||
}
|
||||
|
||||
if (contentScripts?.length) {
|
||||
if (config.command === 'serve') {
|
||||
const permissionsKey =
|
||||
config.manifestVersion === 2 ? 'permissions' : 'host_permissions';
|
||||
const hostPermissions = new Set<string>(manifest[permissionsKey] ?? []);
|
||||
// Don't add content scripts to the manifest in dev mode for MV3 - they're managed and reloaded
|
||||
// at runtime
|
||||
if (config.command === 'serve' && config.manifestVersion === 3) {
|
||||
const hostPermissions = new Set<string>(manifest.host_permissions ?? []);
|
||||
contentScripts.forEach((script) => {
|
||||
script.options.matches.forEach((matchPattern) => {
|
||||
hostPermissions.add(matchPattern);
|
||||
});
|
||||
});
|
||||
manifest[permissionsKey] = Array.from(hostPermissions).sort();
|
||||
hostPermissions.forEach((permission) =>
|
||||
addHostPermission(manifest, permission),
|
||||
);
|
||||
} else {
|
||||
const hashToEntrypointsMap = contentScripts.reduce((map, script) => {
|
||||
const hash = JSON.stringify(script.options);
|
||||
if (!map.has(hash)) {
|
||||
map.set(hash, [script]);
|
||||
} else {
|
||||
map.get(hash)?.push(script);
|
||||
}
|
||||
if (map.has(hash)) map.get(hash)?.push(script);
|
||||
else map.set(hash, [script]);
|
||||
return map;
|
||||
}, new Map<string, ContentScriptEntrypoint[]>());
|
||||
|
||||
@@ -316,13 +316,9 @@ function addDevModeCsp(
|
||||
const allowedCsp = config.server?.origin ?? 'http://localhost:*';
|
||||
|
||||
if (manifest.manifest_version === 3) {
|
||||
manifest.host_permissions ??= [];
|
||||
if (!manifest.host_permissions.includes(permission))
|
||||
manifest.host_permissions.push(permission);
|
||||
addHostPermission(manifest, permission);
|
||||
} else {
|
||||
manifest.permissions ??= [];
|
||||
if (!manifest.permissions.includes(permission))
|
||||
manifest.permissions.push(permission);
|
||||
addPermission(manifest, permission);
|
||||
}
|
||||
|
||||
const csp = new ContentSecurityPolicy(
|
||||
@@ -345,11 +341,22 @@ function addDevModeCsp(
|
||||
}
|
||||
}
|
||||
|
||||
function addDevModePermissions(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
config: InternalConfig,
|
||||
) {
|
||||
// For reloading the page
|
||||
addPermission(manifest, 'tabs');
|
||||
|
||||
// For registering content scripts
|
||||
if (config.manifestVersion === 3) addPermission(manifest, 'scripting');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bundle paths to CSS files associated with a list of content scripts, or undefined if
|
||||
* there is no associated CSS.
|
||||
*/
|
||||
function getContentScriptCssFiles(
|
||||
export function getContentScriptCssFiles(
|
||||
contentScripts: ContentScriptEntrypoint[],
|
||||
buildOutput: Omit<BuildOutput, 'manifest'>,
|
||||
): string[] | undefined {
|
||||
@@ -368,3 +375,21 @@ function getContentScriptCssFiles(
|
||||
if (css.length > 0) return css;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function addPermission(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
permission: string,
|
||||
): void {
|
||||
manifest.permissions ??= [];
|
||||
if (manifest.permissions.includes(permission)) return;
|
||||
manifest.permissions.push(permission);
|
||||
}
|
||||
|
||||
function addHostPermission(
|
||||
manifest: Manifest.WebExtensionManifest,
|
||||
hostPermission: string,
|
||||
): void {
|
||||
manifest.host_permissions ??= [];
|
||||
if (manifest.host_permissions.includes(hostPermission)) return;
|
||||
manifest.host_permissions.push(hostPermission);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,16 @@ import { createUnimport } from 'unimport';
|
||||
import { InternalConfig } from '../types';
|
||||
import { getUnimportOptions } from '../utils/auto-imports';
|
||||
import { Plugin } from 'vite';
|
||||
import { extname } from 'path';
|
||||
|
||||
const ENABLED_EXTENSIONS: Record<string, boolean | undefined> = {
|
||||
'.js': true,
|
||||
'.jsx': true,
|
||||
'.ts': true,
|
||||
'.tsx': true,
|
||||
'.vue': true,
|
||||
'.svelte': true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject any global imports defined by unimport
|
||||
@@ -16,7 +26,8 @@ export function unimport(config: InternalConfig): Plugin {
|
||||
await unimport.scanImportsFromDir(undefined, { cwd: config.srcDir });
|
||||
},
|
||||
async transform(code, id) {
|
||||
return unimport.injectImports(code, id);
|
||||
const ext = extname(id);
|
||||
if (ENABLED_EXTENSIONS[ext]) return unimport.injectImports(code, id);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
+35
-145
@@ -1,32 +1,19 @@
|
||||
import {
|
||||
BuildOutput,
|
||||
WxtDevServer,
|
||||
InlineConfig,
|
||||
InternalConfig,
|
||||
EntrypointGroup,
|
||||
} from './core/types';
|
||||
import { BuildOutput, WxtDevServer, InlineConfig } from './core/types';
|
||||
import { getInternalConfig } from './core/utils/getInternalConfig';
|
||||
import { findEntrypoints } from './core/build/findEntrypoints';
|
||||
import { buildEntrypoints } from './core/build/buildEntrypoints';
|
||||
import { generateMainfest, writeManifest } from './core/utils/manifest';
|
||||
import { printBuildSummary } from './core/log/printBuildSummary';
|
||||
import fs from 'fs-extra';
|
||||
import { generateTypesDir } from './core/build/generateTypesDir';
|
||||
import pc from 'picocolors';
|
||||
import * as vite from 'vite';
|
||||
import { findOpenPort } from './core/utils/findOpenPort';
|
||||
import { formatDuration } from './core/utils/formatDuration';
|
||||
import { createWebExtRunner } from './core/runners/createWebExtRunner';
|
||||
import { groupEntrypoints } from './core/utils/groupEntrypoints';
|
||||
import { Manifest } from 'webextension-polyfill';
|
||||
import { detectDevChanges } from './core/utils/detectDevChanges';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import { consola } from 'consola';
|
||||
import { relative } from 'node:path';
|
||||
import { getEntrypointOutputFile } from './core/utils/entrypoints';
|
||||
import { buildInternal, rebuild } from './core/build';
|
||||
import {
|
||||
getEntrypointBundlePath,
|
||||
getEntrypointOutputFile,
|
||||
} from './core/utils/entrypoints';
|
||||
getServerInfo,
|
||||
reloadContentScripts,
|
||||
reloadHtmlPages,
|
||||
setupServer,
|
||||
} from './core/server';
|
||||
|
||||
export { version } from '../package.json';
|
||||
export * from './core/types/external';
|
||||
@@ -44,33 +31,35 @@ export async function build(config: InlineConfig): Promise<BuildOutput> {
|
||||
export async function createServer(
|
||||
config?: InlineConfig,
|
||||
): Promise<WxtDevServer> {
|
||||
const port = await findOpenPort(3000, 3010);
|
||||
const hostname = 'localhost';
|
||||
const origin = `http://${hostname}:${port}`;
|
||||
const serverConfig: vite.InlineConfig = {
|
||||
server: {
|
||||
origin,
|
||||
},
|
||||
const serverInfo = await getServerInfo();
|
||||
|
||||
const getLatestInternalConfig = () => {
|
||||
const viteConfig: vite.InlineConfig = vite.mergeConfig(
|
||||
serverInfo.viteServerConfig,
|
||||
config?.vite ?? {},
|
||||
);
|
||||
return getInternalConfig({ ...config, vite: viteConfig }, 'serve');
|
||||
};
|
||||
let internalConfig = await getInternalConfig(
|
||||
vite.mergeConfig(serverConfig, config ?? {}),
|
||||
'serve',
|
||||
);
|
||||
const runner = createWebExtRunner();
|
||||
|
||||
let internalConfig = await getLatestInternalConfig();
|
||||
const server = await setupServer(serverInfo, internalConfig);
|
||||
internalConfig.server = server;
|
||||
|
||||
let hasBuiltOnce = false;
|
||||
let currentOutput: BuildOutput | undefined;
|
||||
const fileChangedMutex = new Mutex();
|
||||
const changeQueue: Array<[string, string]> = [];
|
||||
|
||||
const viteServer = await vite.createServer(internalConfig.vite);
|
||||
viteServer.watcher.on('all', async (event, path, stats) => {
|
||||
if (!hasBuiltOnce || path.startsWith(internalConfig.outBaseDir)) return;
|
||||
server.ws.on('wxt:background-initialized', () => {
|
||||
// Register content scripts for the first time since they're not listed in the manifest
|
||||
reloadContentScripts(server.currentOutput.steps, internalConfig, server);
|
||||
});
|
||||
|
||||
server.watcher.on('all', async (event, path, _stats) => {
|
||||
if (path.startsWith(internalConfig.outBaseDir)) return;
|
||||
changeQueue.push([event, path]);
|
||||
|
||||
await fileChangedMutex.runExclusive(async () => {
|
||||
const fileChanges = changeQueue.splice(0, changeQueue.length);
|
||||
const changes = detectDevChanges(fileChanges, currentOutput);
|
||||
const changes = detectDevChanges(fileChanges, server.currentOutput);
|
||||
|
||||
if (changes.type === 'no-change') return;
|
||||
|
||||
@@ -90,10 +79,7 @@ export async function createServer(
|
||||
.join(pc.dim(', '));
|
||||
|
||||
// Get latest config and Rebuild groups with changes
|
||||
internalConfig = await getInternalConfig(
|
||||
vite.mergeConfig(serverConfig, config ?? {}),
|
||||
'serve',
|
||||
);
|
||||
internalConfig = await getLatestInternalConfig();
|
||||
internalConfig.server = server;
|
||||
const { output: newOutput } = await rebuild(
|
||||
internalConfig,
|
||||
@@ -101,119 +87,23 @@ export async function createServer(
|
||||
changes.rebuildGroups,
|
||||
changes.cachedOutput,
|
||||
);
|
||||
currentOutput = newOutput;
|
||||
server.currentOutput = newOutput;
|
||||
|
||||
// Perform reloads
|
||||
switch (changes.type) {
|
||||
case 'extension-reload':
|
||||
server.reloadExtension();
|
||||
consola.success(`Reloaded extension: ${rebuiltNames}`);
|
||||
break;
|
||||
case 'html-reload':
|
||||
changes.rebuildGroups.flat().forEach((entry) => {
|
||||
const path = getEntrypointBundlePath(
|
||||
entry,
|
||||
internalConfig.outDir,
|
||||
'.html',
|
||||
);
|
||||
server.reloadPage(path);
|
||||
});
|
||||
consola.success(`Reloaded pages: ${rebuiltNames}`);
|
||||
reloadHtmlPages(changes.rebuildGroups, server, internalConfig);
|
||||
break;
|
||||
case 'content-script-reload':
|
||||
reloadContentScripts(changes.changedSteps, internalConfig, server);
|
||||
break;
|
||||
}
|
||||
consola.success(`Reloaded: ${rebuiltNames}`);
|
||||
});
|
||||
});
|
||||
const server: WxtDevServer = {
|
||||
...viteServer,
|
||||
async listen(port, isRestart) {
|
||||
const res = await viteServer.listen(port, isRestart);
|
||||
|
||||
if (!isRestart) {
|
||||
internalConfig.logger.success(`Started dev server @ ${origin}`);
|
||||
|
||||
internalConfig.logger.info('Opening browser...');
|
||||
await runner.openBrowser(internalConfig);
|
||||
internalConfig.logger.success('Opened!');
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
port,
|
||||
hostname,
|
||||
origin,
|
||||
reloadExtension: () => {
|
||||
server.ws.send('wxt:reload-extension');
|
||||
},
|
||||
reloadPage: (path) => {
|
||||
// Can't use Vite's built-in "full-reload" event because it doesn't like our paths, it expects
|
||||
// paths ending in "/index.html"
|
||||
server.ws.send('wxt:reload-page', path);
|
||||
},
|
||||
};
|
||||
internalConfig.logger.info('Created dev server');
|
||||
|
||||
internalConfig.server = server;
|
||||
currentOutput = await buildInternal(internalConfig);
|
||||
hasBuiltOnce = true;
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
async function buildInternal(config: InternalConfig): Promise<BuildOutput> {
|
||||
const verb = config.command === 'serve' ? 'Pre-rendering' : 'Building';
|
||||
const target = `${config.browser}-mv${config.manifestVersion}`;
|
||||
config.logger.info(
|
||||
`${verb} ${pc.cyan(target)} for ${pc.cyan(config.mode)} with ${pc.green(
|
||||
`Vite ${vite.version}`,
|
||||
)}`,
|
||||
);
|
||||
const startTime = Date.now();
|
||||
|
||||
// Cleanup
|
||||
await fs.rm(config.outDir, { recursive: true, force: true });
|
||||
await fs.ensureDir(config.outDir);
|
||||
|
||||
const entrypoints = await findEntrypoints(config);
|
||||
const groups = groupEntrypoints(entrypoints);
|
||||
const { output } = await rebuild(config, groups);
|
||||
|
||||
// Post-build
|
||||
config.logger.success(
|
||||
`Built extension in ${formatDuration(Date.now() - startTime)}`,
|
||||
);
|
||||
await printBuildSummary(output, config);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
export async function rebuild(
|
||||
config: InternalConfig,
|
||||
entrypointGroups: EntrypointGroup[],
|
||||
existingOutput: Omit<BuildOutput, 'manifest'> = {
|
||||
steps: [],
|
||||
publicAssets: [],
|
||||
},
|
||||
): Promise<{ output: BuildOutput; manifest: Manifest.WebExtensionManifest }> {
|
||||
// Build
|
||||
const allEntrypoints = await findEntrypoints(config);
|
||||
await generateTypesDir(allEntrypoints, config);
|
||||
const buildOutput = await buildEntrypoints(entrypointGroups, config);
|
||||
|
||||
const manifest = await generateMainfest(allEntrypoints, buildOutput, config);
|
||||
const output: BuildOutput = {
|
||||
manifest,
|
||||
...buildOutput,
|
||||
};
|
||||
|
||||
// Write manifest
|
||||
await writeManifest(manifest, output, config);
|
||||
|
||||
return {
|
||||
output: {
|
||||
manifest,
|
||||
steps: [...existingOutput.steps, ...output.steps],
|
||||
publicAssets: [...existingOutput.publicAssets, ...output.publicAssets],
|
||||
},
|
||||
manifest,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,11 +6,15 @@ import {
|
||||
BackgroundEntrypoint,
|
||||
ContentScriptEntrypoint,
|
||||
GenericEntrypoint,
|
||||
OptionsEntrypoint,
|
||||
PopupEntrypoint,
|
||||
} from '..';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import merge from 'lodash.merge';
|
||||
import { Rollup } from 'vite';
|
||||
import { Manifest } from 'webextension-polyfill';
|
||||
import { FsCache, InternalConfig, WxtDevServer } from '../core/types';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
|
||||
faker.seed(__TEST_SEED__);
|
||||
|
||||
@@ -75,6 +79,63 @@ export const fakeUnlistedScriptEntrypoint =
|
||||
outputDir: fakeDir('.output'),
|
||||
}));
|
||||
|
||||
export const fakeOptionsEntrypoint = fakeObjectCreator<OptionsEntrypoint>(
|
||||
() => ({
|
||||
type: 'options',
|
||||
inputPath: fakeFile('src'),
|
||||
name: faker.string.alpha(),
|
||||
outputDir: fakeDir('.output'),
|
||||
options: {
|
||||
browserStyle: faker.helpers.arrayElement([true, false, undefined]),
|
||||
chromeStyle: faker.helpers.arrayElement([true, false, undefined]),
|
||||
openInTab: faker.helpers.arrayElement([true, false, undefined]),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
export const fakePopupEntrypoint = fakeObjectCreator<PopupEntrypoint>(() => ({
|
||||
type: 'popup',
|
||||
inputPath: fakeFile('src'),
|
||||
name: faker.string.alpha(),
|
||||
outputDir: fakeDir('.output'),
|
||||
options: {
|
||||
defaultTitle: faker.helpers.arrayElement([
|
||||
faker.person.fullName(),
|
||||
undefined,
|
||||
]),
|
||||
defaultIcon: faker.helpers.arrayElement([
|
||||
{
|
||||
'16': 'icon/16.png',
|
||||
'24': 'icon/24.png',
|
||||
'64': 'icon/64.png',
|
||||
},
|
||||
]),
|
||||
mv2Key: faker.helpers.arrayElement([
|
||||
'browser_action',
|
||||
'page_action',
|
||||
undefined,
|
||||
]),
|
||||
},
|
||||
}));
|
||||
|
||||
export const fakeGenericEntrypoint = fakeObjectCreator<GenericEntrypoint>(
|
||||
() => ({
|
||||
type: faker.helpers.arrayElement([
|
||||
'sandbox',
|
||||
'bookmarks',
|
||||
'history',
|
||||
'newtab',
|
||||
'sidepanel',
|
||||
'devtools',
|
||||
'unlisted-page',
|
||||
'unlisted-script',
|
||||
]),
|
||||
inputPath: fakeFile('src'),
|
||||
name: faker.string.alpha(),
|
||||
outputDir: fakeDir('.output'),
|
||||
}),
|
||||
);
|
||||
|
||||
export const fakeRollupOutputChunk = fakeObjectCreator<Rollup.OutputChunk>(
|
||||
() => ({
|
||||
type: 'chunk',
|
||||
@@ -133,3 +194,28 @@ export function fakeArray<T>(createItem: () => T, count = 3): T[] {
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
export const fakeInternalConfig = fakeObjectCreator<InternalConfig>(() => ({
|
||||
browser: faker.helpers.arrayElement(['chrome', 'firefox']),
|
||||
command: faker.helpers.arrayElement(['build', 'serve']),
|
||||
entrypointsDir: fakeDir(),
|
||||
fsCache: mock<FsCache>(),
|
||||
imports: {},
|
||||
logger: mock(),
|
||||
manifest: fakeManifest(),
|
||||
manifestVersion: faker.helpers.arrayElement([2, 3]),
|
||||
mode: faker.helpers.arrayElement(['development', 'production']),
|
||||
outBaseDir: fakeDir(),
|
||||
outDir: fakeDir(),
|
||||
publicDir: fakeDir(),
|
||||
root: fakeDir(),
|
||||
runnerConfig: {
|
||||
config: {},
|
||||
},
|
||||
srcDir: fakeDir(),
|
||||
storeIds: {},
|
||||
typesDir: fakeDir(),
|
||||
vite: {},
|
||||
wxtDir: fakeDir(),
|
||||
server: mock<WxtDevServer>(),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user