Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e621aa8f8c | |||
| 488d7885ca | |||
| 2c70246af5 | |||
| a82a66ec37 | |||
| 19c0948d95 | |||
| 04e5400a46 | |||
| 33c1c171db | |||
| 9cc464f48b | |||
| 58a84ec253 | |||
| 609223566c | |||
| 0aebb67b73 | |||
| 5fa5fd01cb | |||
| de16423e02 | |||
| aa4c0449e9 | |||
| 9d00eb2466 | |||
| 9ac756fb43 | |||
| f195aa429c | |||
| ca20a210ea | |||
| 547fee0e0e | |||
| 54b18cc66e | |||
| d8c190365a | |||
| b625f41919 |
@@ -7,7 +7,7 @@ jobs:
|
||||
uses: './.github/workflows/validate.yml'
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- validate
|
||||
steps:
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
run: |
|
||||
pnpm install --ignore-scripts
|
||||
pnpm build
|
||||
pnpm install
|
||||
pnpm install --ignore-scripts
|
||||
|
||||
- name: Bump and Tag
|
||||
run: |
|
||||
|
||||
@@ -7,8 +7,13 @@ on:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-20.04
|
||||
wxt:
|
||||
name: WXT
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -28,7 +33,8 @@ jobs:
|
||||
run: |
|
||||
pnpm install --ignore-scripts
|
||||
pnpm build
|
||||
pnpm install
|
||||
pnpm install --ignore-scripts
|
||||
cd demo && pnpm wxt prepare
|
||||
|
||||
- name: Formatting
|
||||
run: pnpm format:check
|
||||
@@ -42,7 +48,55 @@ jobs:
|
||||
pnpm build:all:chrome-mv3
|
||||
pnpm build:all:firefox-mv2
|
||||
pnpm build:all:firefox-mv3
|
||||
pnpm compile
|
||||
pnpm zip
|
||||
working-directory: demo
|
||||
|
||||
- name: Tests
|
||||
run: pnpm test:coverage
|
||||
run: pnpm test:coverage --reporter=default --reporter=hanging-process
|
||||
|
||||
project-templates:
|
||||
name: Project Templates
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Validate Vanilla
|
||||
working-directory: templates/vanilla
|
||||
run: |
|
||||
npm i
|
||||
npm run build
|
||||
npm run compile
|
||||
|
||||
- name: Validate Vue
|
||||
working-directory: templates/vue
|
||||
run: |
|
||||
npm i
|
||||
npm run build
|
||||
npm run compile
|
||||
|
||||
- name: Validate React
|
||||
working-directory: templates/react
|
||||
run: |
|
||||
npm i
|
||||
npm run build
|
||||
npm run compile
|
||||
|
||||
- name: Validate Svelte
|
||||
working-directory: templates/svelte
|
||||
run: |
|
||||
npm i
|
||||
npm run build
|
||||
npm run check
|
||||
|
||||
@@ -1,5 +1,67 @@
|
||||
# Changelog
|
||||
|
||||
## v0.3.0
|
||||
|
||||
[compare changes](https://github.com/aklinker1/wxt/compare/v0.2.5...v0.3.0)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- ⚠️ Add type safety to `browser.runtime.getURL` ([58a84ec](https://github.com/aklinker1/wxt/commit/58a84ec))
|
||||
- ⚠️ Change default `publicDir` to `<rootDir>/public` ([19c0948](https://github.com/aklinker1/wxt/commit/19c0948))
|
||||
- Windows support ([#50](https://github.com/aklinker1/wxt/pull/50))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Add `WebWorker` lib to generated tsconfig ([2c70246](https://github.com/aklinker1/wxt/commit/2c70246))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- Update entrypoint directory links ([0aebb67](https://github.com/aklinker1/wxt/commit/0aebb67))
|
||||
|
||||
### 🌊 Types
|
||||
|
||||
- Allow any string for the __BROWSER__ global ([6092235](https://github.com/aklinker1/wxt/commit/6092235))
|
||||
|
||||
### 🤖 CI
|
||||
|
||||
- Improve checks against `demo/` extension ([9cc464f](https://github.com/aklinker1/wxt/commit/9cc464f))
|
||||
|
||||
#### ⚠️ Breaking Changes
|
||||
|
||||
- ⚠️ Add type safety to `browser.runtime.getURL` ([58a84ec](https://github.com/aklinker1/wxt/commit/58a84ec))
|
||||
- ⚠️ Change default `publicDir` to `<rootDir>/public` ([19c0948](https://github.com/aklinker1/wxt/commit/19c0948))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron
|
||||
- Aaron Klinker
|
||||
|
||||
## v0.2.5
|
||||
|
||||
[compare changes](https://github.com/aklinker1/wxt/compare/v0.2.4...v0.2.5)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- Auto-import from subdirectories ([547fee0](https://github.com/aklinker1/wxt/commit/547fee0))
|
||||
- Include background script in dev mode if user doesn't define one ([ca20a21](https://github.com/aklinker1/wxt/commit/ca20a21))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Don't crash when generating types in dev mode ([d8c1903](https://github.com/aklinker1/wxt/commit/d8c1903))
|
||||
- Properly load entrypoints that reference `import.meta` ([54b18cc](https://github.com/aklinker1/wxt/commit/54b18cc))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- Update templates to wxt@0.2 ([9d00eb2](https://github.com/aklinker1/wxt/commit/9d00eb2))
|
||||
|
||||
### 🤖 CI
|
||||
|
||||
- Validate project templates ([9ac756f](https://github.com/aklinker1/wxt/commit/9ac756f))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron Klinker
|
||||
|
||||
## v0.2.4
|
||||
|
||||
[compare changes](https://github.com/aklinker1/wxt/compare/v0.2.3...v0.2.4)
|
||||
@@ -24,7 +86,6 @@
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron
|
||||
- Aaron Klinker
|
||||
|
||||
## v0.2.2
|
||||
@@ -158,7 +219,6 @@
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Aaron
|
||||
- Aaron Klinker
|
||||
|
||||
## v0.1.2
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"build:all:firefox-mv3": "wxt build -b firefox --mv3",
|
||||
"build:all:firefox-mv2": "wxt build -b firefox",
|
||||
"zip": "pnpm -w build && wxt zip",
|
||||
"compile": "pnpm -w build && tsc --noEmit",
|
||||
"postinstall": "pnpm -w build && wxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 698 B After Width: | Height: | Size: 698 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
@@ -7,4 +7,9 @@ export default defineBackground(() => {
|
||||
firefox: __IS_FIREFOX__,
|
||||
manifestVersion: __MANIFEST_VERSION__,
|
||||
});
|
||||
|
||||
// @ts-expect-error: should only accept entrypoints or public assets
|
||||
browser.runtime.getURL('/');
|
||||
browser.runtime.getURL('/background.js');
|
||||
browser.runtime.getURL('/icon/128.png');
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Defining Entrypoints
|
||||
|
||||
Entrypoints are any HTML, JS, or CSS file that needs to be bundled and included with the extension.
|
||||
An entrypoints is any HTML, JS, or CSS file that needs to be bundled and included with the extension.
|
||||
|
||||
They may or may not be listed in the extension's `manifest.json`.
|
||||
|
||||
@@ -46,15 +46,15 @@ would result in the following `manifest.json`:
|
||||
If a file uses a [special name recognized by WXT](/get-started/entrypoints.md), it will be added to the manifest. In this case:
|
||||
|
||||
- `popup.html` → `action.default_popup`
|
||||
- `content.ts` → `content_scripts.*.js`
|
||||
- `content.ts` → `content_scripts.0.js.0`
|
||||
- `background.ts` → `background.service_worker`
|
||||
|
||||
But not all entrypoints are added to the `manifest.json`. If they have a name that is not recognized by WXT, they are still built and included in the extension, but they are unlisted and do not show up in the manifest.
|
||||
But not all entrypoints are added to the `manifest.json`. If they have a name that is not recognized by WXT, they are still built and included in the extension, but they are considered "unlisted" and are not apart of the manifest.
|
||||
|
||||
In this case, `injected.ts` gets bundled to `<outdir>/injected.js` and is accessible via `browser.runtime.getURL("/injected.js")`.
|
||||
|
||||
:::info
|
||||
See [`/entrypoints` folder](/get-started/entrypoints.md) documentation for a full list of recognized entrypoint filenames.
|
||||
See [`/entrypoints` folder](/guide/background.md) documentation for a full list of recognized entrypoint filenames.
|
||||
:::
|
||||
|
||||
## Entrypoint Options
|
||||
@@ -85,5 +85,5 @@ export default defineContentScript({
|
||||
```
|
||||
|
||||
:::info
|
||||
For a full list of entrypoints and each of their options, see the [`/entrypoints` folder](/get-started/entrypoints.md) documentation.
|
||||
For a full list of entrypoints and each of their options, see the [`/entrypoints` folder](/guide/background.md) documentation.
|
||||
:::
|
||||
|
||||
@@ -13,11 +13,11 @@ See [From Scratch](#from-scratch) or reference one of the templates below.
|
||||
:::code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpx wxt@latest init <project-name>
|
||||
pnpx wxt@latest init
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npx wxt@latest init <project-name>
|
||||
npx wxt@latest init
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -40,19 +40,22 @@ Create a new NPM project:
|
||||
:::code-group
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm init <project-name>
|
||||
cd <project-name>
|
||||
mkdir project-name
|
||||
cd project-name
|
||||
pnpm init
|
||||
echo 'shamefully-hoist=true' >> .npmrc
|
||||
```
|
||||
|
||||
```sh [npm]
|
||||
npm init <project-name>
|
||||
cd <project-name>
|
||||
mkdir project-name
|
||||
cd project-name
|
||||
npm init
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn init <project-name>
|
||||
cd <project-name>
|
||||
mkdir project-name
|
||||
cd project-name
|
||||
yarn init
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -100,6 +103,12 @@ Finally, add scripts to your `package.json`:
|
||||
|
||||
> You can skip `*:firefox` scripts if you don't want to support Firefox
|
||||
|
||||
## 🚧 WSL Support
|
||||
|
||||
**_WXT does not support [Windows Subsystem for Linux](https://learn.microsoft.com/en-us/windows/wsl/) yet_**. See [Issue #55](https://github.com/aklinker1/wxt/issues/55) to track progress.
|
||||
|
||||
In the meantime, you can use `cmd` instead.
|
||||
|
||||
## Development
|
||||
|
||||
Once you've installed WXT, you can start the development server using the `dev` script.
|
||||
|
||||
@@ -10,7 +10,7 @@ To setup your test environment for auto-imports, see [Testing](/get-started/test
|
||||
|
||||
Some WXT APIs can be used without importing them:
|
||||
|
||||
- [`browser`](/config.md#browser) from `webextension-polyfill`
|
||||
- [`browser`](/config.md#browser) from `wxt/browser`, a small wrapper around `webextension-polyfill`
|
||||
- [`defineContentScript`](/config.md#defiencontentscript) from `wxt/client`
|
||||
- [`defineBackground`](/config.md#definebackgroundscript) from `wxt/client`
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ export default defineContentScript({
|
||||
|
||||
> All manifest options default to `undefined`.
|
||||
|
||||
When defining multiple content scripts, content script entrypoints that have the same set of options will be merged into a single `content_script` item in the manifest.
|
||||
|
||||
## CSS
|
||||
|
||||
To include CSS with your content script, import the CSS file at the top of your entrypoint:
|
||||
|
||||
@@ -38,12 +38,12 @@ features:
|
||||
title: Bundle Remote Code
|
||||
details: Downloads and bundles remote code imported from URLs.
|
||||
- icon: 🎨
|
||||
title: Frontend framework agnostic
|
||||
title: Frontend Framework Agnostic
|
||||
details: Works with any front-end framework with a Vite plugin.
|
||||
- icon: 🤖
|
||||
title: Automated Publishing
|
||||
details: 'TODO: Automatically zip, upload, and release extensions.'
|
||||
- icon: 📏
|
||||
title: Bundle analysis
|
||||
title: Bundle Analysis
|
||||
details: 'TODO: Tools for analyizing the final extension bundle.'
|
||||
---
|
||||
|
||||
@@ -21,10 +21,17 @@ describe('Auto Imports', () => {
|
||||
".wxt/types/paths.d.ts
|
||||
----------------------------------------
|
||||
// Generated by wxt
|
||||
type EntrypointPath =
|
||||
| \\"/background.js\\"
|
||||
| \\"/content-scripts/overlay.js\\"
|
||||
| \\"/popup.html\\"
|
||||
import \\"wxt/browser\\";
|
||||
|
||||
declare module \\"wxt/browser\\" {
|
||||
type PublicPath =
|
||||
| \\"/background.js\\"
|
||||
| \\"/content-scripts/overlay.js\\"
|
||||
| \\"/popup.html\\"
|
||||
export interface ProjectRuntime extends Runtime.Static {
|
||||
getURL(path: PublicPath): string;
|
||||
}
|
||||
}
|
||||
"
|
||||
`);
|
||||
});
|
||||
@@ -37,18 +44,18 @@ describe('Auto Imports', () => {
|
||||
|
||||
expect(await project.serializeFile('.wxt/types/imports.d.ts'))
|
||||
.toMatchInlineSnapshot(`
|
||||
".wxt/types/imports.d.ts
|
||||
----------------------------------------
|
||||
// Generated by wxt
|
||||
export {}
|
||||
declare global {
|
||||
const browser: typeof import('webextension-polyfill')
|
||||
const defineBackground: typeof import('wxt/client')['defineBackground']
|
||||
const defineConfig: typeof import('wxt')['defineConfig']
|
||||
const defineContentScript: typeof import('wxt/client')['defineContentScript']
|
||||
const mountContentScriptUi: typeof import('wxt/client')['mountContentScriptUi']
|
||||
}
|
||||
"
|
||||
`);
|
||||
".wxt/types/imports.d.ts
|
||||
----------------------------------------
|
||||
// Generated by wxt
|
||||
export {}
|
||||
declare global {
|
||||
const browser: typeof import('wxt/browser')['browser']
|
||||
const defineBackground: typeof import('wxt/client')['defineBackground']
|
||||
const defineConfig: typeof import('wxt')['defineConfig']
|
||||
const defineContentScript: typeof import('wxt/client')['defineContentScript']
|
||||
const mountContentScriptUi: typeof import('wxt/client')['mountContentScriptUi']
|
||||
}
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import fs from 'fs-extra';
|
||||
import glob from 'fast-glob';
|
||||
import { execSync } from 'child_process';
|
||||
import { InlineConfig, UserConfig, build } from '../src';
|
||||
import { normalizePath } from '../src/core/utils/paths';
|
||||
|
||||
export class TestProject {
|
||||
files: Array<[string, string]> = [];
|
||||
@@ -113,7 +114,7 @@ export class TestProject {
|
||||
async serializeFile(path: string): Promise<string> {
|
||||
const absolutePath = resolve(this.root, path);
|
||||
return [
|
||||
relative(this.root, absolutePath),
|
||||
normalizePath(relative(this.root, absolutePath)),
|
||||
await fs.readFile(absolutePath),
|
||||
].join(`\n${''.padEnd(40, '-')}\n`);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.2.4",
|
||||
"version": "0.3.0",
|
||||
"description": "Next gen framework for developing web extensions",
|
||||
"engines": {
|
||||
"node": ">=18.16.0",
|
||||
@@ -39,9 +39,12 @@
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./client": {
|
||||
"require": "./dist/client.cjs",
|
||||
"import": "./dist/client.js",
|
||||
"types": "./dist/client.d.ts"
|
||||
},
|
||||
"./browser": {
|
||||
"import": "./dist/browser.js",
|
||||
"types": "./dist/browser.d.ts"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
@@ -122,4 +125,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,13 @@ await Promise.all([
|
||||
dts: true,
|
||||
silent: true,
|
||||
}),
|
||||
tsup.build({
|
||||
entry: { browser: 'src/client/browser.ts' },
|
||||
format: ['esm'],
|
||||
sourcemap: 'inline',
|
||||
dts: true,
|
||||
silent: true,
|
||||
}),
|
||||
...virtualEntrypoints.map((entryName) =>
|
||||
tsup.build({
|
||||
entry: {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import originalBrowser, { Browser, Runtime } from 'webextension-polyfill';
|
||||
|
||||
export interface AugmentedBrowser extends Browser {
|
||||
runtime: ProjectRuntime;
|
||||
}
|
||||
|
||||
export interface ProjectRuntime extends Runtime.Static {
|
||||
// Overriden per-project
|
||||
}
|
||||
|
||||
export const browser: AugmentedBrowser = originalBrowser;
|
||||
@@ -39,7 +39,7 @@ export async function buildInternal(
|
||||
|
||||
const entrypoints = await findEntrypoints(config);
|
||||
const groups = groupEntrypoints(entrypoints);
|
||||
const { output } = await rebuild(config, groups);
|
||||
const { output } = await rebuild(config, groups, undefined);
|
||||
|
||||
// Post-build
|
||||
config.logger.success(
|
||||
@@ -70,7 +70,11 @@ export async function rebuild(
|
||||
): Promise<{ output: BuildOutput; manifest: Manifest.WebExtensionManifest }> {
|
||||
// Update types directory with new files and types
|
||||
const allEntrypoints = await findEntrypoints(config);
|
||||
await generateTypesDir(allEntrypoints, config);
|
||||
await generateTypesDir(allEntrypoints, config).catch((err) => {
|
||||
config.logger.warn('Failed to update .wxt directory:', err);
|
||||
// Throw the error if doing a regular build, don't for dev mode.
|
||||
if (config.command === 'build') throw err;
|
||||
});
|
||||
|
||||
// Build and merge the outputs
|
||||
const newOutput = await buildEntrypoints(entrypointGroups, config);
|
||||
|
||||
@@ -12,6 +12,7 @@ import fs from 'fs-extra';
|
||||
import { importTsFile } from '../../utils/importTsFile';
|
||||
import glob from 'fast-glob';
|
||||
import { fakeInternalConfig } from '../../../testing/fake-objects';
|
||||
import { unnormalizePath } from '../../utils/paths';
|
||||
|
||||
vi.mock('../../utils/importTsFile');
|
||||
const importTsFileMock = vi.mocked(importTsFile);
|
||||
@@ -29,6 +30,7 @@ describe('findEntrypoints', () => {
|
||||
root: '/',
|
||||
entrypointsDir: resolve('/src/entrypoints'),
|
||||
outDir: resolve('.output'),
|
||||
command: 'build',
|
||||
});
|
||||
|
||||
it.each<[string, string, PopupEntrypoint]>([
|
||||
@@ -210,10 +212,10 @@ describe('findEntrypoints', () => {
|
||||
},
|
||||
],
|
||||
])(
|
||||
'should find and load content script entrypoint config from %s',
|
||||
'should find and load background entrypoint config from %s',
|
||||
async (path, expected) => {
|
||||
const options: ContentScriptEntrypoint['options'] = {
|
||||
matches: ['<all_urls>'],
|
||||
const options: BackgroundEntrypoint['options'] = {
|
||||
type: 'module',
|
||||
};
|
||||
globMock.mockResolvedValueOnce([path]);
|
||||
importTsFileMock.mockResolvedValue(options);
|
||||
@@ -226,6 +228,24 @@ describe('findEntrypoints', () => {
|
||||
},
|
||||
);
|
||||
|
||||
it("should include a virtual background script so dev reloading works when there isn't a background entrypoint defined by the user", async () => {
|
||||
globMock.mockResolvedValueOnce([]);
|
||||
|
||||
const entrypoints = await findEntrypoints({
|
||||
...config,
|
||||
command: 'serve',
|
||||
});
|
||||
|
||||
expect(entrypoints).toHaveLength(1);
|
||||
expect(entrypoints[0]).toEqual({
|
||||
type: 'background',
|
||||
inputPath: 'virtual:user-background',
|
||||
name: 'background',
|
||||
options: {},
|
||||
outputDir: config.outDir,
|
||||
});
|
||||
});
|
||||
|
||||
it.each<[string, GenericEntrypoint]>([
|
||||
// Sandbox
|
||||
[
|
||||
@@ -424,9 +444,14 @@ describe('findEntrypoints', () => {
|
||||
|
||||
it('should not allow multiple entrypoints with the same name', async () => {
|
||||
globMock.mockResolvedValueOnce(['popup.html', 'popup/index.html']);
|
||||
const expectedPaths = [
|
||||
'src/entrypoints/popup.html',
|
||||
'src/entrypoints/popup/index.html',
|
||||
].map(unnormalizePath);
|
||||
|
||||
await expect(() => findEntrypoints(config)).rejects.toThrowError(
|
||||
'Multiple entrypoints with the name "popup" detected, but only one is allowed: src/entrypoints/popup.html, src/entrypoints/popup/index.html',
|
||||
'Multiple entrypoints with the name "popup" detected, but only one is allowed: ' +
|
||||
expectedPaths.join(', '),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
import * as plugins from '../vite-plugins';
|
||||
import { removeEmptyDirs } from '../utils/removeEmptyDirs';
|
||||
import { getEntrypointBundlePath } from '../utils/entrypoints';
|
||||
import glob from 'fast-glob';
|
||||
import fs from 'fs-extra';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { getPublicFiles } from '../utils/public';
|
||||
|
||||
export async function buildEntrypoints(
|
||||
groups: EntrypointGroup[],
|
||||
@@ -132,11 +132,10 @@ function getBuildOutputChunks(
|
||||
async function copyPublicDirectory(
|
||||
config: InternalConfig,
|
||||
): Promise<BuildOutput['publicAssets']> {
|
||||
const files = await getPublicFiles(config);
|
||||
if (files.length === 0) return [];
|
||||
|
||||
const publicAssets: BuildOutput['publicAssets'] = [];
|
||||
if (!(await fs.exists(config.publicDir))) return publicAssets;
|
||||
|
||||
const files = await glob('**/*', { cwd: config.publicDir });
|
||||
|
||||
for (const file of files) {
|
||||
const srcPath = resolve(config.publicDir, file);
|
||||
const outPath = resolve(config.outDir, file);
|
||||
|
||||
@@ -16,9 +16,10 @@ import JSON5 from 'json5';
|
||||
import { importTsFile } from '../utils/importTsFile';
|
||||
import glob from 'fast-glob';
|
||||
import { getEntrypointName } from '../utils/entrypoints';
|
||||
import { VIRTUAL_NOOP_BACKGROUND_MODULE_ID } from '../vite-plugins/noopBackground';
|
||||
|
||||
/**
|
||||
* Return entrypoints and their configuration by looking through the
|
||||
* Return entrypoints and their configuration by looking through the project's files.
|
||||
*/
|
||||
export async function findEntrypoints(
|
||||
config: InternalConfig,
|
||||
@@ -33,6 +34,7 @@ export async function findEntrypoints(
|
||||
const existingNames: Record<string, Entrypoint | undefined> = {};
|
||||
|
||||
const entrypoints: Entrypoint[] = [];
|
||||
let hasBackground = false;
|
||||
await Promise.all(
|
||||
relativePaths.map(async (relativePath) => {
|
||||
const path = resolve(config.entrypointsDir, relativePath);
|
||||
@@ -63,6 +65,7 @@ export async function findEntrypoints(
|
||||
break;
|
||||
case 'background':
|
||||
entrypoint = await getBackgroundEntrypoint(config, path);
|
||||
hasBackground = true;
|
||||
break;
|
||||
case 'content-script':
|
||||
entrypoint = await getContentScriptEntrypoint(
|
||||
@@ -95,6 +98,11 @@ export async function findEntrypoints(
|
||||
existingNames[entrypoint.name] = entrypoint;
|
||||
}),
|
||||
);
|
||||
if (config.command === 'serve' && !hasBackground) {
|
||||
entrypoints.push(
|
||||
await getBackgroundEntrypoint(config, VIRTUAL_NOOP_BACKGROUND_MODULE_ID),
|
||||
);
|
||||
}
|
||||
return entrypoints;
|
||||
}
|
||||
|
||||
@@ -195,10 +203,17 @@ async function getBackgroundEntrypoint(
|
||||
config: InternalConfig,
|
||||
path: string,
|
||||
): Promise<BackgroundEntrypoint> {
|
||||
const { main: _, ...options } =
|
||||
await importTsFile<BackgroundScriptDefintition>(path, config);
|
||||
if (options == null) {
|
||||
throw Error('Background script does not have a default export');
|
||||
let options: Omit<BackgroundScriptDefintition, 'main'> = {};
|
||||
if (path !== VIRTUAL_NOOP_BACKGROUND_MODULE_ID) {
|
||||
const defaultExport = await importTsFile<BackgroundScriptDefintition>(
|
||||
path,
|
||||
config,
|
||||
);
|
||||
if (defaultExport == null) {
|
||||
throw Error('Background script does not have a default export');
|
||||
}
|
||||
const { main: _, ...moduleOptions } = defaultExport;
|
||||
options = moduleOptions;
|
||||
}
|
||||
return {
|
||||
type: 'background',
|
||||
@@ -257,6 +272,7 @@ const PATH_GLOB_TO_TYPE_MAP: Record<string, Entrypoint['type'] | 'ignored'> = {
|
||||
'devtools/index.html': 'devtools',
|
||||
|
||||
'background.ts': 'background',
|
||||
[VIRTUAL_NOOP_BACKGROUND_MODULE_ID]: 'background',
|
||||
|
||||
'content.ts?(x)': 'content-script',
|
||||
'content/index.ts?(x)': 'content-script',
|
||||
|
||||
@@ -5,6 +5,8 @@ import { relative, resolve } from 'path';
|
||||
import { getEntrypointBundlePath } from '../utils/entrypoints';
|
||||
import { getUnimportOptions } from '../utils/auto-imports';
|
||||
import { getGlobals } from '../utils/globals';
|
||||
import { getPublicFiles } from '../utils/public';
|
||||
import { normalizePath } from '../utils/paths';
|
||||
|
||||
/**
|
||||
* Generate and write all the files inside the `InternalConfig.typesDir` directory.
|
||||
@@ -49,23 +51,34 @@ async function writePathsDeclarationFile(
|
||||
): Promise<string> {
|
||||
const filePath = resolve(config.typesDir, 'paths.d.ts');
|
||||
const unions = entrypoints
|
||||
.map((entry) => {
|
||||
const path = getEntrypointBundlePath(
|
||||
.map((entry) =>
|
||||
getEntrypointBundlePath(
|
||||
entry,
|
||||
config.outDir,
|
||||
entry.inputPath.endsWith('.html') ? '.html' : '.js',
|
||||
);
|
||||
return ` | "/${path}"`;
|
||||
})
|
||||
.sort();
|
||||
),
|
||||
)
|
||||
.concat(await getPublicFiles(config))
|
||||
.map(normalizePath)
|
||||
.map((path) => ` | "/${path}"`)
|
||||
.sort()
|
||||
.join('\n');
|
||||
|
||||
const template = `// Generated by wxt
|
||||
import "wxt/browser";
|
||||
|
||||
declare module "wxt/browser" {
|
||||
type PublicPath =
|
||||
{{ union }}
|
||||
export interface ProjectRuntime extends Runtime.Static {
|
||||
getURL(path: PublicPath): string;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
await fs.writeFile(
|
||||
filePath,
|
||||
[
|
||||
'// Generated by wxt',
|
||||
'type EntrypointPath =',
|
||||
...(unions.length === 0 ? [' never'] : unions),
|
||||
].join('\n') + '\n',
|
||||
template.replace('{{ union }}', unions || ' | never'),
|
||||
);
|
||||
|
||||
return filePath;
|
||||
@@ -102,7 +115,8 @@ async function writeMainDeclarationFile(
|
||||
'// Generated by wxt',
|
||||
`/// <reference types="vite/client" />`,
|
||||
...references.map(
|
||||
(ref) => `/// <reference types="./${relative(dir, ref)}" />`,
|
||||
(ref) =>
|
||||
`/// <reference types="./${normalizePath(relative(dir, ref))}" />`,
|
||||
),
|
||||
].join('\n') + '\n',
|
||||
);
|
||||
@@ -125,31 +139,26 @@ async function writeTsConfigFile(
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true,
|
||||
|
||||
/* Completeness */
|
||||
"lib": ["DOM", "WebWorker"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Aliases */
|
||||
"baseUrl": "${relative(dir, config.root)}",
|
||||
"baseUrl": "${normalizePath(relative(dir, config.root))}",
|
||||
"paths": {
|
||||
"@@": ["."],
|
||||
"@@/*": ["./*"],
|
||||
"~~": ["."],
|
||||
"~~/*": ["./*"],
|
||||
"@": ["${relative(config.root, config.srcDir)}"],
|
||||
"@/*": ["${relative(config.root, config.srcDir)}/*"],
|
||||
"~": ["${relative(config.root, config.srcDir)}"],
|
||||
"~/*": ["${relative(config.root, config.srcDir)}/*"]
|
||||
"@": ["${normalizePath(relative(config.root, config.srcDir))}"],
|
||||
"@/*": ["${normalizePath(relative(config.root, config.srcDir))}/*"],
|
||||
"~": ["${normalizePath(relative(config.root, config.srcDir))}"],
|
||||
"~/*": ["${normalizePath(relative(config.root, config.srcDir))}/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"${relative(dir, config.root)}/**/*",
|
||||
"./${relative(dir, mainReference)}"
|
||||
"${normalizePath(relative(dir, config.root))}/**/*",
|
||||
"./${normalizePath(relative(dir, mainReference))}"
|
||||
],
|
||||
"exclude": ["${relative(dir, config.outBaseDir)}"]
|
||||
"exclude": ["${normalizePath(relative(dir, config.outBaseDir))}"]
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ export interface InlineConfig {
|
||||
* directory.
|
||||
*
|
||||
* @default
|
||||
* "<root>"
|
||||
* "<rootDir>"
|
||||
*/
|
||||
srcDir?: string;
|
||||
/**
|
||||
* Directory containing files that will be copied to the output directory as-is.
|
||||
*
|
||||
* @default
|
||||
* "<srcDir>/publicDir"
|
||||
* "<rootDir>/publicDir"
|
||||
*/
|
||||
publicDir?: string;
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { kebabCaseAlphanumeric } from '../strings';
|
||||
import { kebabCaseAlphanumeric, removeImportStatements } from '../strings';
|
||||
|
||||
describe('String utils', () => {
|
||||
describe('kebabCaseAlphanumeric', () => {
|
||||
@@ -14,4 +14,32 @@ describe('String utils', () => {
|
||||
expect(kebabCaseAlphanumeric(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeImportStatements', () => {
|
||||
it('should remove all import formats', () => {
|
||||
const imports = `
|
||||
import { registerGithubService, createGithubApi } from "@/utils/github";
|
||||
import {
|
||||
registerGithubService,
|
||||
createGithubApi
|
||||
} from "@/utils/github";
|
||||
import{ registerGithubService, createGithubApi }from "@/utils/github";
|
||||
import GitHub from "@/utils/github";
|
||||
import "@/utils/github";
|
||||
import '@/utils/github';
|
||||
import"@/utils/github"
|
||||
import'@/utils/github';
|
||||
`;
|
||||
expect(removeImportStatements(imports).trim()).toEqual('');
|
||||
});
|
||||
|
||||
it('should not remove import.meta or inline import statements', () => {
|
||||
const imports = `
|
||||
import.meta.env.DEV
|
||||
const a = await import("example");
|
||||
import("example");
|
||||
`;
|
||||
expect(removeImportStatements(imports)).toEqual(imports);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,13 +7,10 @@ export function getUnimportOptions(
|
||||
): Partial<UnimportOptions> {
|
||||
const defaultOptions: Partial<UnimportOptions> = {
|
||||
debugLog: config.logger.debug,
|
||||
imports: [
|
||||
{ name: '*', as: 'browser', from: 'webextension-polyfill' },
|
||||
{ name: 'defineConfig', from: 'wxt' },
|
||||
],
|
||||
presets: [{ package: 'wxt/client' }],
|
||||
imports: [{ name: 'defineConfig', from: 'wxt' }],
|
||||
presets: [{ package: 'wxt/client' }, { package: 'wxt/browser' }],
|
||||
warn: config.logger.warn,
|
||||
dirs: ['components', 'composables', 'hooks', 'utils'],
|
||||
dirs: ['./components/*', './composables/*', './hooks/*', './utils/*'],
|
||||
};
|
||||
|
||||
return mergeConfig(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { BuildOutput, BuildStepOutput, EntrypointGroup } from '../types';
|
||||
import * as vite from 'vite';
|
||||
import { every } from './arrays';
|
||||
import { normalizePath } from './paths';
|
||||
|
||||
/**
|
||||
* Compare the changed files vs the build output and determine what kind of reload needs to happen:
|
||||
@@ -102,15 +103,16 @@ function findEffectedSteps(
|
||||
currentOutput: BuildOutput,
|
||||
): DetectedChange[] {
|
||||
const changes: DetectedChange[] = [];
|
||||
const changedPath = changedFile[1];
|
||||
const changedPath = normalizePath(changedFile[1]);
|
||||
|
||||
const isChunkEffected = (
|
||||
chunk: vite.Rollup.OutputChunk | vite.Rollup.OutputAsset,
|
||||
): boolean =>
|
||||
// If it's an HTML file with the same path, is is effected because HTML files need to be pre-rendered
|
||||
// TODO: use bundle path to support `<name>/index.html`?
|
||||
// fileName is normalized, relative bundle path
|
||||
(chunk.type === 'asset' && changedPath.endsWith(chunk.fileName)) ||
|
||||
// If it's a chunk that depends on the changed file, it is effected
|
||||
// moduleIds are absolute, normalized paths
|
||||
(chunk.type === 'chunk' && chunk.moduleIds.includes(changedPath));
|
||||
|
||||
for (const step of currentOutput.steps) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Entrypoint } from '../types';
|
||||
import path, { relative, resolve } from 'node:path';
|
||||
import { normalizePath } from './paths';
|
||||
|
||||
export function getEntrypointName(
|
||||
entrypointsDir: string,
|
||||
@@ -7,8 +8,8 @@ export function getEntrypointName(
|
||||
// type: Entrypoint['type'],
|
||||
): string {
|
||||
const relativePath = path.relative(entrypointsDir, inputPath);
|
||||
// Grab the string up to the first . or /
|
||||
const name = relativePath.split(/[\.\/]/, 2)[0];
|
||||
// Grab the string up to the first . or / or \\
|
||||
const name = relativePath.split(/[\.\/\\]/, 2)[0];
|
||||
|
||||
return name;
|
||||
}
|
||||
@@ -29,5 +30,7 @@ export function getEntrypointBundlePath(
|
||||
outDir: string,
|
||||
ext: string,
|
||||
): string {
|
||||
return relative(outDir, getEntrypointOutputFile(entrypoint, ext));
|
||||
return normalizePath(
|
||||
relative(outDir, getEntrypointOutputFile(entrypoint, ext)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ export async function getInternalConfig(
|
||||
srcDir,
|
||||
userConfig.entrypointsDir ?? 'entrypoints',
|
||||
);
|
||||
const publicDir = resolve(srcDir, userConfig.publicDir ?? 'public');
|
||||
const publicDir = resolve(root, userConfig.publicDir ?? 'public');
|
||||
const wxtDir = resolve(srcDir, '.wxt');
|
||||
const typesDir = resolve(wxtDir, 'types');
|
||||
|
||||
@@ -142,6 +142,7 @@ export async function getInternalConfig(
|
||||
);
|
||||
finalConfig.vite.plugins.push(plugins.devServerGlobals(finalConfig));
|
||||
finalConfig.vite.plugins.push(plugins.tsconfigPaths(finalConfig));
|
||||
finalConfig.vite.plugins.push(plugins.noopBackground());
|
||||
|
||||
finalConfig.vite.define ??= {};
|
||||
getGlobals(finalConfig).forEach((global) => {
|
||||
|
||||
@@ -12,7 +12,7 @@ export function getGlobals(
|
||||
{
|
||||
name: '__BROWSER__',
|
||||
value: config.browser,
|
||||
type: `"chromium" | "firefox"`,
|
||||
type: `string`,
|
||||
},
|
||||
{
|
||||
name: '__IS_CHROME__',
|
||||
|
||||
@@ -5,6 +5,8 @@ import fs from 'fs-extra';
|
||||
import { resolve } from 'path';
|
||||
import transform from 'jiti/dist/babel';
|
||||
import { getUnimportOptions } from './auto-imports';
|
||||
import { removeImportStatements } from './strings';
|
||||
import { normalizePath } from './paths';
|
||||
|
||||
/**
|
||||
* Get the value from the default export of a `path`.
|
||||
@@ -26,17 +28,18 @@ export async function importTsFile<T>(
|
||||
config: InternalConfig,
|
||||
): Promise<T> {
|
||||
config.logger.debug('Loading file metadata:', path);
|
||||
// JITI & Babel uses normalized paths.
|
||||
const normalPath = normalizePath(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 textNoImports = removeImportStatements(text);
|
||||
const { code } = await unimport.injectImports(textNoImports);
|
||||
config.logger.debug(
|
||||
['Text:', text, 'No imports:', textNoImports, 'Code:', code].join('\n'),
|
||||
@@ -53,7 +56,8 @@ export async function importTsFile<T>(
|
||||
),
|
||||
},
|
||||
transform(opts) {
|
||||
if (opts.filename === path) return transform({ ...opts, source: code });
|
||||
if (opts.filename === normalPath)
|
||||
return transform({ ...opts, source: code });
|
||||
else return transform(opts);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import nodePath from 'node:path';
|
||||
import * as vite from 'vite';
|
||||
|
||||
/**
|
||||
* Converts system paths to normalized bundler path. On windows and unix, this returns paths with /
|
||||
* instead of \.
|
||||
*/
|
||||
export function normalizePath(path: string): string {
|
||||
return vite.normalizePath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a normalized path, convert it to the system path style. On Windows, switch to \, otherwise use /.
|
||||
*/
|
||||
export function unnormalizePath(path: string): string {
|
||||
return nodePath.normalize(path);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { InternalConfig } from '../types';
|
||||
import fs from 'fs-extra';
|
||||
import glob from 'fast-glob';
|
||||
import { unnormalizePath } from './paths';
|
||||
|
||||
/**
|
||||
* Get all the files in the project's public directory. Returned paths are relative to the
|
||||
* `config.publicDir`.
|
||||
*/
|
||||
export async function getPublicFiles(
|
||||
config: InternalConfig,
|
||||
): Promise<string[]> {
|
||||
if (!(await fs.exists(config.publicDir))) return [];
|
||||
|
||||
const files = await glob('**/*', { cwd: config.publicDir });
|
||||
return files.map(unnormalizePath);
|
||||
}
|
||||
@@ -4,3 +4,14 @@ export function kebabCaseAlphanumeric(str: string): string {
|
||||
.replace(/[^a-z0-9-\s]/g, '') // Remove all non-alphanumeric, non-hyphen characters
|
||||
.replace(/\s+/g, '-'); // Replace spaces with hyphens
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes import statements from the top of a file. Keeps import.meta and inline, async `import()`
|
||||
* calls.
|
||||
*/
|
||||
export function removeImportStatements(text: string): string {
|
||||
return text.replace(
|
||||
/(import\s?[{\w][\s\S]*?from\s?["'][\s\S]*?["'];?|import\s?["'][\s\S]*?["'];?)/gm,
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,3 +5,4 @@ export * from './multipageMove';
|
||||
export * from './unimport';
|
||||
export * from './virtualEntrypoint';
|
||||
export * from './tsconfigPaths';
|
||||
export * from './noopBackground';
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Entrypoint, InternalConfig } from '../types';
|
||||
import { dirname, extname, resolve } from 'node:path';
|
||||
import { getEntrypointBundlePath } from '../utils/entrypoints';
|
||||
import fs, { ensureDir } from 'fs-extra';
|
||||
import { normalizePath } from '../utils/paths';
|
||||
|
||||
/**
|
||||
* Ensures the HTML files output by a multipage build are in the correct location. This does two
|
||||
@@ -23,11 +24,11 @@ export function multipageMove(
|
||||
name: 'wxt:multipage-move',
|
||||
async writeBundle(_, bundle) {
|
||||
for (const oldBundlePath in bundle) {
|
||||
// oldBundlePath = 'entrypoints/popup.html' or 'entrypoints/o ptions/index.html'
|
||||
// oldBundlePath = 'entrypoints/popup.html' or 'entrypoints/options/index.html'
|
||||
|
||||
// Find a matching entrypoint - oldBundlePath is the same as end end of the input path.
|
||||
const entrypoint = entrypoints.find(
|
||||
(entry) => !!entry.inputPath.endsWith(oldBundlePath),
|
||||
(entry) => !!normalizePath(entry.inputPath).endsWith(oldBundlePath),
|
||||
);
|
||||
if (entrypoint == null) {
|
||||
config.logger.debug('No entrypoint found for', oldBundlePath);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Plugin } from 'vite';
|
||||
|
||||
/**
|
||||
* In dev mode, if there's not a background script listed, we need to add one.
|
||||
*
|
||||
* This define's a virtual module that is basically just a noop.
|
||||
*/
|
||||
export function noopBackground(): Plugin {
|
||||
const virtualModuleId = VIRTUAL_NOOP_BACKGROUND_MODULE_ID;
|
||||
const resolvedVirtualModuleId = '\0' + virtualModuleId;
|
||||
return {
|
||||
name: 'wxt:noop-background',
|
||||
resolveId(id) {
|
||||
if (id === virtualModuleId) return resolvedVirtualModuleId;
|
||||
},
|
||||
load(id) {
|
||||
if (id === resolvedVirtualModuleId) {
|
||||
return `import { defineBackground } from 'wxt/client';\nexport default defineBackground(() => void 0)`;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const VIRTUAL_NOOP_BACKGROUND_MODULE_ID = 'virtual:user-background';
|
||||
@@ -2,6 +2,7 @@ import { Plugin } from 'vite';
|
||||
import { Entrypoint, InternalConfig } from '../types';
|
||||
import fs from 'fs-extra';
|
||||
import { resolve } from 'path';
|
||||
import { normalizePath } from '../utils/paths';
|
||||
|
||||
/**
|
||||
* Wraps a user's entrypoint with a vitual version with additional logic.
|
||||
@@ -21,7 +22,7 @@ export function virtualEntrypoin(
|
||||
const index = id.indexOf(virtualId);
|
||||
if (index === -1) return;
|
||||
|
||||
const inputPath = id.substring(index + virtualId.length);
|
||||
const inputPath = normalizePath(id.substring(index + virtualId.length));
|
||||
return resolvedVirtualId + inputPath;
|
||||
},
|
||||
async load(id) {
|
||||
|
||||
@@ -28,6 +28,10 @@ export async function build(config: InlineConfig): Promise<BuildOutput> {
|
||||
return await buildInternal(internalConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dev server, pre-builds all the files that need to exist to load the extension, and open
|
||||
* the browser with the extension installed.
|
||||
*/
|
||||
export async function createServer(
|
||||
config?: InlineConfig,
|
||||
): Promise<WxtDevServer> {
|
||||
@@ -54,13 +58,15 @@ export async function createServer(
|
||||
});
|
||||
|
||||
server.watcher.on('all', async (event, path, _stats) => {
|
||||
// Here, "path" is a non-normalized path (ie: C:\\users\\... instead of C:/users/...)
|
||||
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, server.currentOutput);
|
||||
if (fileChanges.length === 0) return;
|
||||
|
||||
const changes = detectDevChanges(fileChanges, server.currentOutput);
|
||||
if (changes.type === 'no-change') return;
|
||||
|
||||
// Log the entrypoints that were effected
|
||||
|
||||
@@ -21,6 +21,6 @@
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@vitejs/plugin-react": "^4.0.3",
|
||||
"typescript": "^5.1.6",
|
||||
"wxt": "^0.1.3"
|
||||
"wxt": "^0.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,6 @@
|
||||
"svelte-check": "^3.4.6",
|
||||
"tslib": "^2.6.0",
|
||||
"typescript": "^5.1.6",
|
||||
"wxt": "^0.1.5"
|
||||
"wxt": "^0.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.1.6",
|
||||
"wxt": "^0.1.3"
|
||||
"wxt": "^0.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"typescript": "^5.1.6",
|
||||
"vue-tsc": "^1.8.4",
|
||||
"wxt": "^0.1.3"
|
||||
"wxt": "^0.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true,
|
||||
"lib": ["DOM", "WebWorker", "ESNext"],
|
||||
|
||||
/* Linting */
|
||||
"noUnusedLocals": true,
|
||||
|
||||