Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3eb505dbde | |||
| fbe502fddf | |||
| c143a9978d | |||
| e3555b663d | |||
| df934d252c | |||
| 447d011b8f | |||
| abe1263d27 | |||
| 143b5ac81f | |||
| 87fbb1f4c3 | |||
| 62f11bf605 | |||
| 3ead54b58e | |||
| 580f4f3cfe | |||
| da0accebe8 | |||
| eb65d0b42a | |||
| c2784135b0 | |||
| 61d54bd1f9 | |||
| 0bd94fce3b | |||
| 2a97c06443 | |||
| 8213aa776a | |||
| 026f78254d | |||
| 0ebb013ba6 | |||
| c5f78d0c8d | |||
| 8771676e69 | |||
| aebbbb0685 | |||
| 68183b648e | |||
| 45809c0198 | |||
| 13163c9ba1 | |||
| aedbba3f3d | |||
| 763dbd5ee8 |
@@ -38,6 +38,7 @@ const chromeExtensionIds = [
|
||||
'cfkdcideecefncbglkhneoflfnmhoicc', // mindful - stay focused on your goals
|
||||
'lnhejcpclabmbgpiiomjbhalblnnbffg', // 1Proompt
|
||||
'fonflmjnjbkigocpoommgmhljdpljain', // NiceTab - https://github.com/web-dahuyou/NiceTab
|
||||
'fcffekbnfcfdemeekijbbmgmkognnmkd', // Draftly for LinkedIn
|
||||
];
|
||||
|
||||
const { data, err, isLoading } = useListExtensionDetails(chromeExtensionIds);
|
||||
|
||||
@@ -56,7 +56,8 @@ When `permissions`/`host_permissions` change during an update, depending on what
|
||||
You can test if your permission changes will result in a disabled extension:
|
||||
|
||||
- Chromium: Use [Google's Extension Update Testing tool](https://github.com/GoogleChromeLabs/extension-update-testing-tool)
|
||||
- Firefox/Safari: Everyone breaks something in production eventually... 🫡 Good luck soldier
|
||||
- Firefox: See their [Test Permission Requests](https://extensionworkshop.com/documentation/develop/test-permission-requests/) page
|
||||
- Safari: Everyone breaks something in production eventually... 🫡 Good luck soldier
|
||||
|
||||
## Update Event
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ Usually, this means each entrypoint should be a directory with it's own files in
|
||||
|
||||
## Configuring Routers
|
||||
|
||||
Lots of frameworks come with routers for building a multi-page app using the URL's path. Chrome extensions don't don't work like this. Since HTML files are static, `chrome-extension://{id}/popup.html`, there's no way to change the entire path for routing.
|
||||
Lots of frameworks come with routers for building a multi-page app using the URL's path. Chrome extensions don't work like this. Since HTML files are static, `chrome-extension://{id}/popup.html`, there's no way to change the entire path for routing.
|
||||
|
||||
Instead, you need to configure the router to run in "hash" mode, where the routing information is apart of the URL's hash, not the path (ie: `popup.html#/` and `popup.html#/account/settings`).
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ pnpm i wxt@latest
|
||||
|
||||
If there was a major version change, follow the steps below to fix breaking changes.
|
||||
|
||||
## v0.18.0 → v0.19.0
|
||||
## v0.18.5 → v0.19.0
|
||||
|
||||
### `vite-node` Entrypoint Loader
|
||||
|
||||
@@ -86,6 +86,27 @@ WXT no longer ships with Common JS support. If you're using CJS, here's your mig
|
||||
|
||||
Vite also provides steps for migrating to ESM. Check them out for more details: https://vitejs.dev/guide/migration#deprecate-cjs-node-api
|
||||
|
||||
## v0.18.0 → v0.18.5
|
||||
|
||||
> When this version was released, it was not considered a breaking change... but it should have been.
|
||||
|
||||
### New `modules/` Directory
|
||||
|
||||
WXT now recognizes the `modules/` directory as a folder containing [WXT modules](/guide/go-further/reusable-modules).
|
||||
|
||||
If you already have `<srcDir>/modules` or `<srcDir>/Modules` directory, `wxt prepare` and other commands will fail.
|
||||
|
||||
You have two options:
|
||||
|
||||
1. [Recommended] Keep your files where they are and tell WXT to look in a different folder:
|
||||
```ts
|
||||
// wxt.config.ts
|
||||
export default defineConfig({
|
||||
modulesDir: 'wxt-modules', // defaults to "modules"
|
||||
});
|
||||
```
|
||||
2. Rename your `modules` directory to something else.
|
||||
|
||||
## v0.17.0 → v0.18.0
|
||||
|
||||
### Automatic MV3 `host_permissions` to MV2 `permissions`
|
||||
|
||||
+3
-3
@@ -17,14 +17,14 @@
|
||||
"docs:preview": "pnpm -s docs:gen && vitepress preview docs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/buildc": "^1.0.11",
|
||||
"@aklinker1/check": "^1.3.1",
|
||||
"@aklinker1/buildc": "^1.1.1",
|
||||
"@aklinker1/check": "^1.4.5",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@vitest/coverage-v8": "^2.0.4",
|
||||
"changelogen": "^0.5.5",
|
||||
"consola": "^3.2.3",
|
||||
"dependency-graph": "^1.0.0",
|
||||
"execa": "^9.3.0",
|
||||
"execa": "^9.3.1",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fs-extra": "^11.2.0",
|
||||
"hasha": "^6.0.0",
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
"wxt": ">=0.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "^1.3.1",
|
||||
"@aklinker1/check": "^1.4.5",
|
||||
"oxlint": "^0.9.1",
|
||||
"publint": "^0.2.9",
|
||||
"typescript": "^5.5.4",
|
||||
"unbuild": "^2.0.0",
|
||||
@@ -54,6 +55,6 @@
|
||||
"dependencies": {
|
||||
"defu": "^6.1.4",
|
||||
"fs-extra": "^11.2.0",
|
||||
"sharp": "^0.33.4"
|
||||
"sharp": "^0.33.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,9 +38,10 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "^1.3.1",
|
||||
"@aklinker1/check": "^1.4.5",
|
||||
"@types/chrome": "^0.0.268",
|
||||
"@types/node": "^20.14.2",
|
||||
"oxlint": "^0.9.1",
|
||||
"publint": "^0.2.8",
|
||||
"typescript": "^5.4.5",
|
||||
"unbuild": "^2.0.0",
|
||||
|
||||
@@ -14,7 +14,7 @@ export function applyChromeMessagePlaceholders(message: ChromeMessage): string {
|
||||
export function getSubstitionCount(message: string): number {
|
||||
return (
|
||||
1 +
|
||||
new Array(MAX_SUBSTITUTIONS).findLastIndex((_, i) =>
|
||||
Array.from({ length: MAX_SUBSTITUTIONS }).findLastIndex((_, i) =>
|
||||
message.match(new RegExp(`(?<!\\$)\\$${i + 1}`)),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"@vitejs/plugin-react": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "^1.3.1",
|
||||
"@aklinker1/check": "^1.4.5",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"publint": "^0.2.9",
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"vite-plugin-solid": "^2.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "^1.3.1",
|
||||
"@aklinker1/check": "^1.4.5",
|
||||
"publint": "^0.2.9",
|
||||
"solid-js": "^1.8.19",
|
||||
"typescript": "^5.5.4",
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "^1.3.1",
|
||||
"@aklinker1/check": "^1.4.5",
|
||||
"publint": "^0.2.9",
|
||||
"typescript": "^5.5.4",
|
||||
"unbuild": "^2.0.0",
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"@vitejs/plugin-vue": "^5.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "^1.3.1",
|
||||
"@aklinker1/check": "^1.4.5",
|
||||
"publint": "^0.2.9",
|
||||
"typescript": "^5.5.4",
|
||||
"unbuild": "^2.0.0",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
src/core/utils/building/__tests__/test-entrypoints
|
||||
@@ -1,5 +1,83 @@
|
||||
# Changelog
|
||||
|
||||
## v0.19.9
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.8...wxt-v0.19.9)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- **modules:** Add `wxt.hook` alias for `wxt.hooks.hook` ([c5f78d0](https://github.com/wxt-dev/wxt/commit/c5f78d0))
|
||||
- Use `@types/chrome` for config manifest type ([#969](https://github.com/wxt-dev/wxt/pull/969))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Allow adding multiple hyphens in an entrypoint name ([#949](https://github.com/wxt-dev/wxt/pull/949))
|
||||
- Duplicate `BuildOutput.publicAssets` ([#951](https://github.com/wxt-dev/wxt/pull/951))
|
||||
- Properly overload `import.meta.env` with WXT's own environment globals ([#966](https://github.com/wxt-dev/wxt/pull/966))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Add docs around `importEntrypoint` to relevant functions ([143b5ac](https://github.com/wxt-dev/wxt/commit/143b5ac))
|
||||
|
||||
### 🌊 Types
|
||||
|
||||
- **modules:** Use `NestedHooks` instead of `Partial` for hooks object ([0ebb013](https://github.com/wxt-dev/wxt/commit/0ebb013))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- Fix type error ([#946](https://github.com/wxt-dev/wxt/pull/946))
|
||||
- Add `oxlint` for linting ([#947](https://github.com/wxt-dev/wxt/pull/947))
|
||||
- **deps:** Bump ora from 8.0.1 to 8.1.0 ([#961](https://github.com/wxt-dev/wxt/pull/961))
|
||||
- **deps:** Bump unimport from 3.9.1 to 3.11.1 ([#960](https://github.com/wxt-dev/wxt/pull/960))
|
||||
- **deps:** Bump execa from 9.3.0 to 9.3.1 ([#957](https://github.com/wxt-dev/wxt/pull/957))
|
||||
- Cleanup leftover E2E test artifacts ([#968](https://github.com/wxt-dev/wxt/pull/968))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- 1natsu ([@1natsu172](http://github.com/1natsu172))
|
||||
|
||||
## v0.19.8
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.7...wxt-v0.19.8)
|
||||
|
||||
### 🔥 Performance
|
||||
|
||||
- Ignore output directories for all `vite.watcher` ([#924](https://github.com/wxt-dev/wxt/pull/924))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Fallback to GitHub API for listing templates when ungh is down ([#944](https://github.com/wxt-dev/wxt/pull/944))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- **types:** Cleanup types in wxt/browser/chrome ([#932](https://github.com/wxt-dev/wxt/pull/932))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- 1natsu
|
||||
|
||||
## v0.19.7
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.6...wxt-v0.19.7)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- **testing:** Run WXT modules when setting up test environment ([#926](https://github.com/wxt-dev/wxt/pull/926))
|
||||
- **modules:** Add `addAlias` helper ([#928](https://github.com/wxt-dev/wxt/pull/928))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- **testing:** Stub `chrome` and `browser` globals with `fakeBrowser` automatically ([#925](https://github.com/wxt-dev/wxt/pull/925))
|
||||
- Ensure TSConfig paths start with `../` or `./` so they are valid ([#927](https://github.com/wxt-dev/wxt/pull/927))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Fix module name for `wxt/browser/chrome` ([751706d](https://github.com/wxt-dev/wxt/commit/751706d))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- Remove warning log for missing public directory ([5f2e1c3](https://github.com/wxt-dev/wxt/commit/5f2e1c3))
|
||||
|
||||
## v0.19.6
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/wxt-v0.19.5...wxt-v0.19.6)
|
||||
@@ -2390,4 +2468,4 @@ Initial release of WXT. Full support for production builds and initial toolkit f
|
||||
### 🤖 CI
|
||||
|
||||
- Create validation workflow ([#12](https://github.com/wxt-dev/wxt/pull/12))
|
||||
- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13))
|
||||
- Create release workflow ([#13](https://github.com/wxt-dev/wxt/pull/13))
|
||||
@@ -362,7 +362,7 @@ describe('Output Directory Structure', () => {
|
||||
.toMatchInlineSnapshot(`
|
||||
".output/chrome-mv3/background.js
|
||||
----------------------------------------
|
||||
var _background = function() {
|
||||
var background = function() {
|
||||
"use strict";
|
||||
var _a, _b;
|
||||
function defineBackground(arg) {
|
||||
@@ -372,13 +372,13 @@ describe('Output Directory Structure', () => {
|
||||
function logHello(name) {
|
||||
console.log(\`Hello \${name}!\`);
|
||||
}
|
||||
_background;
|
||||
background;
|
||||
const definition = defineBackground({
|
||||
main() {
|
||||
logHello("background");
|
||||
}
|
||||
});
|
||||
_background;
|
||||
background;
|
||||
function initPlugins() {
|
||||
}
|
||||
// @ts-expect-error
|
||||
@@ -411,7 +411,7 @@ describe('Output Directory Structure', () => {
|
||||
const result$1 = result;
|
||||
return result$1;
|
||||
}();
|
||||
_background;
|
||||
background;
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -13,7 +13,6 @@ describe('TypeScript Project', () => {
|
||||
".wxt/types/globals.d.ts
|
||||
----------------------------------------
|
||||
// Generated by wxt
|
||||
export {}
|
||||
interface ImportMetaEnv {
|
||||
readonly MANIFEST_VERSION: 2 | 3;
|
||||
readonly BROWSER: string;
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('User Config', () => {
|
||||
);
|
||||
|
||||
await project.build({
|
||||
// @ts-expect-error: Specifically setting an invalid field for the test - it should show up in the snapshot
|
||||
// Specifically setting an invalid field for the test - it should show up in the snapshot
|
||||
manifest: ({ manifestVersion, command }) => ({
|
||||
example_customization: [String(manifestVersion), command],
|
||||
}),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.19.6",
|
||||
"version": "0.19.9",
|
||||
"description": "Next gen framework for developing web extensions",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -81,6 +81,7 @@
|
||||
"prepack": "pnpm build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/chrome": "^0.0.269",
|
||||
"@aklinker1/rollup-plugin-visualizer": "5.12.0",
|
||||
"@types/webextension-polyfill": "^0.10.7",
|
||||
"@webext-core/fake-browser": "^1.3.1",
|
||||
@@ -95,7 +96,7 @@
|
||||
"defu": "^6.1.4",
|
||||
"dequal": "^2.0.3",
|
||||
"esbuild": "^0.23.0",
|
||||
"execa": "^9.3.0",
|
||||
"execa": "^9.3.1",
|
||||
"fast-glob": "^3.3.2",
|
||||
"filesize": "^10.1.4",
|
||||
"fs-extra": "^11.2.0",
|
||||
@@ -114,20 +115,20 @@
|
||||
"nypm": "^0.3.9",
|
||||
"ohash": "^1.1.3",
|
||||
"open": "^10.1.0",
|
||||
"ora": "^8.0.1",
|
||||
"ora": "^8.1.0",
|
||||
"picocolors": "^1.0.1",
|
||||
"prompts": "^2.4.2",
|
||||
"publish-browser-extension": "^2.1.3",
|
||||
"unimport": "^3.9.1",
|
||||
"scule": "^1.3.0",
|
||||
"unimport": "^3.11.1",
|
||||
"vite": "^5.3.5",
|
||||
"vite-node": "^2.0.4",
|
||||
"web-ext-run": "^0.2.1",
|
||||
"webextension-polyfill": "^0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aklinker1/check": "^1.3.1",
|
||||
"@aklinker1/check": "^1.4.5",
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@types/chrome": "^0.0.269",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/lodash.merge": "^4.6.9",
|
||||
"@types/natural-compare": "^1.4.3",
|
||||
@@ -137,6 +138,7 @@
|
||||
"extract-zip": "^2.0.1",
|
||||
"happy-dom": "^14.12.3",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"oxlint": "^0.9.1",
|
||||
"p-map": "^7.0.2",
|
||||
"publint": "^0.2.9",
|
||||
"tsx": "4.15.7",
|
||||
@@ -145,9 +147,6 @@
|
||||
"vitest": "^2.0.4",
|
||||
"vitest-plugin-random-seed": "^1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/chrome": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/chrome": {
|
||||
"optional": true
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
/// <reference types="chrome" />
|
||||
/**
|
||||
* @module wxt/browser/chrome
|
||||
*/
|
||||
import type { WxtRuntime, WxtI18n } from './index';
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL
|
||||
*
|
||||
@@ -11,11 +6,11 @@ import type { WxtRuntime, WxtI18n } from './index';
|
||||
*
|
||||
* @module wxt/browser/chrome
|
||||
*/
|
||||
import type { WxtRuntime, WxtI18n } from './index';
|
||||
|
||||
export type Chrome = typeof chrome;
|
||||
export type WxtBrowser = Omit<Chrome, 'runtime' | 'i18n'> & {
|
||||
runtime: WxtRuntime & Omit<Chrome['runtime'], 'getURL'>;
|
||||
i18n: WxtI18n & Omit<Chrome['i18n'], 'getMessage'>;
|
||||
export type WxtBrowser = Omit<typeof chrome, 'runtime' | 'i18n'> & {
|
||||
runtime: WxtRuntime & Omit<(typeof chrome)['runtime'], 'getURL'>;
|
||||
i18n: WxtI18n & Omit<(typeof chrome)['i18n'], 'getMessage'>;
|
||||
};
|
||||
|
||||
export const browser: WxtBrowser =
|
||||
|
||||
@@ -59,6 +59,11 @@ export async function createViteBuilder(
|
||||
config.build.sourcemap = 'inline';
|
||||
}
|
||||
|
||||
config.server ??= {};
|
||||
config.server.watch = {
|
||||
ignored: [`${wxtConfig.outBaseDir}/**`, `${wxtConfig.wxtDir}/**`],
|
||||
};
|
||||
|
||||
config.plugins ??= [];
|
||||
config.plugins.push(
|
||||
wxtPlugins.download(wxtConfig),
|
||||
@@ -287,9 +292,6 @@ export async function createViteBuilder(
|
||||
strictPort: true,
|
||||
host: info.hostname,
|
||||
origin: info.origin,
|
||||
watch: {
|
||||
ignored: [`${wxtConfig.outBaseDir}/**`, `${wxtConfig.wxtDir}/**`],
|
||||
},
|
||||
},
|
||||
};
|
||||
const baseConfig = await getBaseConfig();
|
||||
|
||||
@@ -211,7 +211,7 @@ function createFileReloader(server: WxtDevServer) {
|
||||
wxt.logger.success(`Reloaded: ${getFilenameList(rebuiltNames)}`);
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
} catch {
|
||||
// Catch build errors instead of crashing. Don't log error either, builder should have already logged it
|
||||
}
|
||||
});
|
||||
|
||||
@@ -99,53 +99,74 @@ interface Template {
|
||||
}
|
||||
|
||||
async function listTemplates(): Promise<Template[]> {
|
||||
try {
|
||||
const res = await fetch('https://ungh.cc/repos/wxt-dev/wxt/files/main');
|
||||
if (res.status >= 300)
|
||||
throw Error(`Request failed with status ${res.status} ${res.statusText}`);
|
||||
const templates = await listTemplatesUngh().catch((err) => {
|
||||
consola.debug('Failed to load templates via ungh:', err);
|
||||
return listTemplatesGithub();
|
||||
});
|
||||
return templates.sort((l, r) => {
|
||||
const lWeight = TEMPLATE_SORT_WEIGHT[l.name] ?? Number.MAX_SAFE_INTEGER;
|
||||
const rWeight = TEMPLATE_SORT_WEIGHT[r.name] ?? Number.MAX_SAFE_INTEGER;
|
||||
const diff = lWeight - rWeight;
|
||||
if (diff !== 0) return diff;
|
||||
return l.name.localeCompare(r.name);
|
||||
});
|
||||
}
|
||||
|
||||
const data = (await res.json()) as {
|
||||
meta: {
|
||||
sha: string;
|
||||
};
|
||||
files: Array<{
|
||||
path: string;
|
||||
mode: string;
|
||||
sha: string;
|
||||
size: number;
|
||||
}>;
|
||||
async function listTemplatesUngh(): Promise<Template[]> {
|
||||
const res = await fetch('https://ungh.cc/repos/wxt-dev/wxt/files/main');
|
||||
if (res.status !== 200)
|
||||
throw Error(
|
||||
`Request failed with status ${res.status} ${res.statusText}: ${await res.text()}`,
|
||||
);
|
||||
|
||||
const data = (await res.json()) as {
|
||||
meta: {
|
||||
sha: string;
|
||||
};
|
||||
return data.files
|
||||
.map((item) => item.path.match(/templates\/(.+)\/package\.json/)?.[1])
|
||||
.filter((name) => name != null)
|
||||
.map((name) => ({ name: name!, path: `templates/${name}` }))
|
||||
.sort((l, r) => {
|
||||
const lWeight = TEMPLATE_SORT_WEIGHT[l.name] ?? Number.MAX_SAFE_INTEGER;
|
||||
const rWeight = TEMPLATE_SORT_WEIGHT[r.name] ?? Number.MAX_SAFE_INTEGER;
|
||||
const diff = lWeight - rWeight;
|
||||
if (diff !== 0) return diff;
|
||||
return l.name.localeCompare(r.name);
|
||||
});
|
||||
} catch (err) {
|
||||
consola.error(err);
|
||||
throw Error(`Failed to load templates`);
|
||||
}
|
||||
files: Array<{
|
||||
path: string;
|
||||
mode: string;
|
||||
sha: string;
|
||||
size: number;
|
||||
}>;
|
||||
};
|
||||
return data.files
|
||||
.map((item) => item.path.match(/templates\/(.+)\/package\.json/)?.[1])
|
||||
.filter((name) => name != null)
|
||||
.map((name) => ({ name: name!, path: `templates/${name}` }));
|
||||
}
|
||||
|
||||
async function listTemplatesGithub(): Promise<Template[]> {
|
||||
const res = await fetch(
|
||||
`https://api.github.com/repos/${REPO}/contents/templates`,
|
||||
{ headers: { Accept: 'application/vnd.github+json' } },
|
||||
);
|
||||
if (res.status !== 200)
|
||||
throw Error(
|
||||
`Request failed with status ${res.status} ${res.statusText}: ${await res.text()}`,
|
||||
);
|
||||
|
||||
// Schema is Example4 of https://docs.github.com/en/rest/repos/contents?apiVersion=2022-11-28#get-repository-content
|
||||
return (await res.json()) as Array<{
|
||||
name: string;
|
||||
path: string;
|
||||
sha: string;
|
||||
size: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
async function cloneProject({
|
||||
directory,
|
||||
template,
|
||||
packageManager,
|
||||
}: {
|
||||
directory: string;
|
||||
template: Template;
|
||||
packageManager: string;
|
||||
}) {
|
||||
const { default: ora } = await import('ora');
|
||||
const spinner = ora('Downloading template').start();
|
||||
try {
|
||||
// 1. Clone repo
|
||||
await downloadTemplate(`gh:wxt-dev/wxt/${template.path}`, {
|
||||
await downloadTemplate(`gh:${REPO}/${template.path}`, {
|
||||
dir: directory,
|
||||
force: true,
|
||||
});
|
||||
@@ -180,3 +201,5 @@ const TEMPLATE_SORT_WEIGHT: Record<string, number> = {
|
||||
vue: 1,
|
||||
react: 2,
|
||||
};
|
||||
|
||||
const REPO = 'wxt-dev/wxt';
|
||||
|
||||
@@ -21,9 +21,10 @@ describe('String utils', () => {
|
||||
|
||||
describe('safeVarName', () => {
|
||||
it.each([
|
||||
['Hello world!', '_hello_world'],
|
||||
['Hello world!', 'helloWorld'],
|
||||
['123', '_123'],
|
||||
['abc-123', '_abc_123'],
|
||||
['abc-123', 'abc123'],
|
||||
['abc-123-xyz', 'abc123Xyz'],
|
||||
['', '_'],
|
||||
[' ', '_'],
|
||||
['_', '_'],
|
||||
|
||||
@@ -201,7 +201,6 @@ async function getGlobalsDeclarationEntry(): Promise<WxtDirFileEntry> {
|
||||
path: 'types/globals.d.ts',
|
||||
text: [
|
||||
'// Generated by wxt',
|
||||
'export {}',
|
||||
'interface ImportMetaEnv {',
|
||||
...globals.map((global) => ` readonly ${global.name}: ${global.type};`),
|
||||
'}',
|
||||
|
||||
@@ -47,14 +47,16 @@ export async function rebuild(
|
||||
const newOutput = await buildEntrypoints(entrypointGroups, spinner);
|
||||
const mergedOutput: Omit<BuildOutput, 'manifest'> = {
|
||||
steps: [...existingOutput.steps, ...newOutput.steps],
|
||||
publicAssets: [...existingOutput.publicAssets, ...newOutput.publicAssets],
|
||||
// Do not merge existing because all publicAssets copied everytime
|
||||
publicAssets: newOutput.publicAssets,
|
||||
};
|
||||
|
||||
const { manifest: newManifest, warnings: manifestWarnings } =
|
||||
await generateManifest(allEntrypoints, mergedOutput);
|
||||
|
||||
const finalOutput: BuildOutput = {
|
||||
manifest: newManifest,
|
||||
...newOutput,
|
||||
...mergedOutput,
|
||||
};
|
||||
|
||||
// Write manifest
|
||||
@@ -64,14 +66,7 @@ export async function rebuild(
|
||||
spinner.clear().stop();
|
||||
|
||||
return {
|
||||
output: {
|
||||
manifest: newManifest,
|
||||
steps: [...existingOutput.steps, ...finalOutput.steps],
|
||||
publicAssets: [
|
||||
...existingOutput.publicAssets,
|
||||
...finalOutput.publicAssets,
|
||||
],
|
||||
},
|
||||
output: finalOutput,
|
||||
manifest: newManifest,
|
||||
warnings: manifestWarnings,
|
||||
};
|
||||
|
||||
@@ -86,7 +86,7 @@ export async function resolveConfig(
|
||||
if (await isDirMissing(entrypointsDir)) {
|
||||
logMissingDir(logger, 'Entrypoints', entrypointsDir);
|
||||
}
|
||||
const filterEntrypoints = !!mergedConfig.filterEntrypoints?.length
|
||||
const filterEntrypoints = mergedConfig.filterEntrypoints?.length
|
||||
? new Set(mergedConfig.filterEntrypoints)
|
||||
: undefined;
|
||||
const publicDir = path.resolve(srcDir, mergedConfig.publicDir ?? 'public');
|
||||
|
||||
@@ -14,7 +14,7 @@ export function getEntrypointName(
|
||||
): string {
|
||||
const relativePath = path.relative(entrypointsDir, inputPath);
|
||||
// Grab the string up to the first . or / or \\
|
||||
const name = relativePath.split(/[\.\/\\]/, 2)[0];
|
||||
const name = relativePath.split(/[./\\]/, 2)[0];
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export function applyGlobals(globals: EnvGlobals): void {
|
||||
try {
|
||||
// @ts-expect-error: Untyped key on globalThis
|
||||
globalThis[key] = envValue;
|
||||
} catch (err) {
|
||||
} catch {
|
||||
// ignore any globals that can't be set
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,7 +5,8 @@ export async function getEslintVersion(): Promise<string[]> {
|
||||
);
|
||||
const { ESLint } = require('eslint');
|
||||
return ESLint.version?.split('.') ?? [];
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Return an empty version when there's an error importing ESLint
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,14 +272,14 @@ function addEntrypoints(
|
||||
options.browser_style = popup.options.browserStyle;
|
||||
if (manifest.manifest_version === 3) {
|
||||
manifest.action = {
|
||||
...(manifest.action ?? {}),
|
||||
...manifest.action,
|
||||
...options,
|
||||
default_popup,
|
||||
};
|
||||
} else {
|
||||
const key = popup.options.mv2Key ?? 'browser_action';
|
||||
manifest[key] = {
|
||||
...(manifest[key] ?? {}),
|
||||
...manifest[key],
|
||||
...options,
|
||||
default_popup,
|
||||
};
|
||||
@@ -434,8 +434,8 @@ function discoverIcons(
|
||||
/^icon@([0-9]+)w\.png$/, // icon@16w.png
|
||||
/^icon@([0-9]+)h\.png$/, // icon@16h.png
|
||||
/^icon@([0-9]+)\.png$/, // icon@16.png
|
||||
/^icons?[\/\\]([0-9]+)\.png$/, // icon/16.png | icons/16.png
|
||||
/^icons?[\/\\]([0-9]+)x[0-9]+\.png$/, // icon/16x16.png | icons/16x16.png
|
||||
/^icons?[/\\]([0-9]+)\.png$/, // icon/16.png | icons/16.png
|
||||
/^icons?[/\\]([0-9]+)x[0-9]+\.png$/, // icon/16x16.png | icons/16x16.png
|
||||
];
|
||||
// #endregion snippet
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export async function getPackageJson(): Promise<
|
||||
} catch (err) {
|
||||
wxt.logger.debug(
|
||||
`Failed to read package.json at: ${file}. Returning undefined.`,
|
||||
err,
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { camelCase } from 'scule';
|
||||
|
||||
export function kebabCaseAlphanumeric(str: string): string {
|
||||
return str
|
||||
.toLowerCase()
|
||||
@@ -9,8 +11,17 @@ export function kebabCaseAlphanumeric(str: string): string {
|
||||
* Return a safe variable name for a given string.
|
||||
*/
|
||||
export function safeVarName(str: string): string {
|
||||
// _ prefix to ensure it doesn't start with a number
|
||||
return '_' + kebabCaseAlphanumeric(str.trim()).replace('-', '_');
|
||||
const name = camelCase(kebabCaseAlphanumeric(str));
|
||||
if (name.match(/^[a-z]/)) return name;
|
||||
// _ prefix to ensure it doesn't start with a number or other invalid symbol
|
||||
return '_' + name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string to a valid filename (NOT path), stripping out invalid characters.
|
||||
*/
|
||||
export function safeFilename(str: string): string {
|
||||
return kebabCaseAlphanumeric(str);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -311,6 +311,7 @@ export const fakeResolvedConfig = fakeObjectCreator<ResolvedConfig>(() => {
|
||||
|
||||
export const fakeWxt = fakeObjectCreator<Wxt>(() => ({
|
||||
config: fakeResolvedConfig(),
|
||||
hook: vi.fn(),
|
||||
hooks: mock(),
|
||||
logger: mock(),
|
||||
reloadConfig: vi.fn(),
|
||||
|
||||
@@ -162,9 +162,7 @@ function getSimpleAstJson(ast: any): any {
|
||||
} else if (typeof ast === 'object') {
|
||||
return Object.fromEntries(
|
||||
Object.entries(ast)
|
||||
.filter(
|
||||
([key, value]) => key !== 'loc' && key !== 'start' && key !== 'end',
|
||||
)
|
||||
.filter(([key]) => key !== 'loc' && key !== 'start' && key !== 'end')
|
||||
.map(([key, value]) => [key, getSimpleAstJson(value)]),
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -40,6 +40,7 @@ export async function registerWxt(
|
||||
wxt = {
|
||||
config,
|
||||
hooks,
|
||||
hook: hooks.hook.bind(hooks),
|
||||
get logger() {
|
||||
return config.logger;
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { InlineConfig } from '../types';
|
||||
import path from 'node:path';
|
||||
import fs from 'fs-extra';
|
||||
import { kebabCaseAlphanumeric } from './utils/strings';
|
||||
import { safeFilename } from './utils/strings';
|
||||
import { getPackageJson } from './utils/package';
|
||||
import { minimatch } from 'minimatch';
|
||||
import { formatDuration } from './utils/time';
|
||||
@@ -27,9 +27,7 @@ export async function zip(config?: InlineConfig): Promise<string[]> {
|
||||
|
||||
const projectName =
|
||||
wxt.config.zip.name ??
|
||||
kebabCaseAlphanumeric(
|
||||
(await getPackageJson())?.name || path.dirname(process.cwd()),
|
||||
);
|
||||
safeFilename((await getPackageJson())?.name || path.dirname(process.cwd()));
|
||||
const applyTemplate = (template: string): string =>
|
||||
template
|
||||
.replaceAll('{{name}}', projectName)
|
||||
|
||||
@@ -34,17 +34,21 @@ export function defineWxtModule<TOptions extends WxtModuleOptions>(
|
||||
* the output directory. This will speed up project builds since it just has to
|
||||
* copy some files instead of bundling them.
|
||||
*
|
||||
* To extract entrypoint options from a JS/TS file, use
|
||||
* `wxt.builder.importEntrypoint` (see example).
|
||||
*
|
||||
* @param wxt The wxt instance provided by the module's setup function.
|
||||
* @param entrypoint The entrypoint to be bundled along with the extension.
|
||||
*
|
||||
* @example
|
||||
* export default defineWxtModule((wxt, options) => {
|
||||
* export default defineWxtModule(async (wxt, options) => {
|
||||
* const entrypointPath = "/path/to/my-entrypoint.ts";
|
||||
* addEntrypoint(wxt, {
|
||||
* type: "unlisted-page",
|
||||
* name: "changelog",
|
||||
* inputPath: "wxt-module-changelog/index.html"
|
||||
* type: "content-script",
|
||||
* name: "some-name",
|
||||
* inputPath: entrypointPath,
|
||||
* outputDir: wxt.config.outputDir,
|
||||
* options: {},
|
||||
* options: await wxt.builder.importEntrypoint(entrypointPath),
|
||||
* });
|
||||
* });
|
||||
*/
|
||||
|
||||
@@ -832,7 +832,8 @@ export type ResolvedPerBrowserOptions<T, TOmitted extends keyof T = never> = {
|
||||
*/
|
||||
export type UserManifest = Partial<
|
||||
Omit<
|
||||
Manifest.WebExtensionManifest,
|
||||
chrome.runtime.ManifestV3,
|
||||
| 'action'
|
||||
| 'background'
|
||||
| 'chrome_url_overrides'
|
||||
| 'devtools_page'
|
||||
@@ -841,7 +842,34 @@ export type UserManifest = Partial<
|
||||
| 'options_ui'
|
||||
| 'sandbox'
|
||||
>
|
||||
>;
|
||||
> & {
|
||||
// Add any Browser-specific or MV2 properties that WXT supports here
|
||||
action?: chrome.runtime.ManifestV3['action'] & {
|
||||
browser_style?: boolean;
|
||||
};
|
||||
browser_action?: chrome.runtime.ManifestV2['browser_action'] & {
|
||||
browser_style?: boolean;
|
||||
};
|
||||
page_action?: chrome.runtime.ManifestV2['page_action'] & {
|
||||
browser_style?: boolean;
|
||||
};
|
||||
browser_specific_settings?: {
|
||||
gecko?: {
|
||||
id?: string;
|
||||
strict_min_version?: string;
|
||||
strict_max_version?: string;
|
||||
update_url?: string;
|
||||
};
|
||||
gecko_android?: {
|
||||
strict_min_version?: string;
|
||||
strict_max_version?: string;
|
||||
};
|
||||
safari?: {
|
||||
strict_min_version?: string;
|
||||
strict_max_version?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type UserManifestFn = (
|
||||
env: ConfigEnv,
|
||||
@@ -1107,6 +1135,9 @@ export interface WxtHooks {
|
||||
) => HookResult;
|
||||
/**
|
||||
* Called once all entrypoints have been loaded from the `entrypointsDir`.
|
||||
* Use `wxt.builder.importEntrypoint` to load entrypoint options from the
|
||||
* file, or manually define them.
|
||||
*
|
||||
* @param wxt The configured WXT object
|
||||
* @param entrypoints The list of entrypoints to be built
|
||||
*/
|
||||
@@ -1129,6 +1160,10 @@ export interface WxtHooks {
|
||||
export interface Wxt {
|
||||
config: ResolvedConfig;
|
||||
hooks: Hookable<WxtHooks>;
|
||||
/**
|
||||
* Alias for `wxt.hooks.hook(...)`.
|
||||
*/
|
||||
hook: Hookable<WxtHooks>['hook'];
|
||||
/**
|
||||
* Alias for config.logger
|
||||
*/
|
||||
@@ -1375,7 +1410,7 @@ export interface WxtModule<TOptions extends WxtModuleOptions> {
|
||||
* Alternative to adding hooks in setup function with `wxt.hooks`. Hooks are
|
||||
* added before the `setup` function is called.
|
||||
*/
|
||||
hooks?: Partial<WxtHooks>;
|
||||
hooks?: NestedHooks<WxtHooks>;
|
||||
/**
|
||||
* A custom function that can be used to setup hooks and call module-specific
|
||||
* APIs.
|
||||
|
||||
@@ -6,12 +6,12 @@ export default defineConfig({
|
||||
test: {
|
||||
mockReset: true,
|
||||
restoreMocks: true,
|
||||
setupFiles: ['vitest.setup.ts'],
|
||||
testTimeout: 120e3,
|
||||
coverage: {
|
||||
include: ['src/**'],
|
||||
exclude: ['**/dist', '**/__tests__', 'src/utils/testing'],
|
||||
},
|
||||
globalSetup: ['./vitest.globalSetup.ts'],
|
||||
},
|
||||
server: {
|
||||
watch: {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { exists, rm } from 'fs-extra';
|
||||
|
||||
let setupHappened = false;
|
||||
|
||||
export async function setup() {
|
||||
if (setupHappened) {
|
||||
throw new Error('setup called twice');
|
||||
}
|
||||
|
||||
setupHappened = true;
|
||||
|
||||
// @ts-expect-error
|
||||
globalThis.__ENTRYPOINT__ = 'test';
|
||||
|
||||
const e2eDistPath = './e2e/dist/';
|
||||
if (await exists(e2eDistPath)) {
|
||||
await rm(e2eDistPath, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
// @ts-expect-error
|
||||
globalThis.__ENTRYPOINT__ = 'test';
|
||||
Generated
+329
-210
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user