Compare commits

...

11 Commits

Author SHA1 Message Date
GitHub Actions e621aa8f8c chore(release): v0.3.0 2023-07-21 03:59:08 +00:00
Aaron 488d7885ca feat: Windows support (#50) 2023-07-20 22:54:30 -05:00
Aaron Klinker 2c70246af5 fix: Add WebWorker lib to generated tsconfig 2023-07-20 22:34:16 -05:00
Aaron Klinker a82a66ec37 Fix installation docs syntax colors 2023-07-20 22:31:39 -05:00
Aaron Klinker 19c0948d95 feat!: Change default publicDir to <rootDir>/public
BREAKING CHANGE: `config.publicDir`'s default changed from `<srcDir>/public` to `rootDir/<public>` to align with other frameworks and standards
2023-07-20 15:47:38 -05:00
Aaron Klinker 04e5400a46 Fix awkward sentences in the docs 2023-07-20 14:13:52 -05:00
Aaron Klinker 33c1c171db Consistent capitalization on docs homepage 2023-07-20 14:02:35 -05:00
Aaron Klinker 9cc464f48b ci: Improve checks against demo/ extension 2023-07-20 13:56:22 -05:00
Aaron Klinker 58a84ec253 feat!: Add type safety to browser.runtime.getURL
Entrypoint outputs and files in the public directory are typed properly.
Chunks and other generated files are not typed since they aren't often accessed at runtime.

BREAKING CHANGE: `browser` is now imported from `wxt/browser` instead of `webextension-polyfill`
2023-07-20 13:55:35 -05:00
Aaron Klinker 609223566c types: Allow any string for the __BROWSER__ global 2023-07-20 13:14:16 -05:00
Aaron Klinker 0aebb67b73 docs: Update entrypoint directory links 2023-07-20 12:14:45 -05:00
36 changed files with 243 additions and 93 deletions
+2 -2
View File
@@ -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: |
+10 -3
View File
@@ -9,7 +9,11 @@ on:
jobs:
wxt:
name: WXT
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v3
@@ -29,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
@@ -43,6 +48,8 @@ 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
@@ -50,7 +57,7 @@ jobs:
project-templates:
name: Project Templates
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3
+36
View File
@@ -1,5 +1,41 @@
# 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)
+1
View File
@@ -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

+5
View File
@@ -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');
});
+5 -5
View File
@@ -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` &rarr; `action.default_popup`
- `content.ts` &rarr; `content_scripts.*.js`
- `content.ts` &rarr; `content_scripts.0.js.0`
- `background.ts` &rarr; `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.
:::
+17 -8
View File
@@ -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.
+1 -1
View File
@@ -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`
+2
View File
@@ -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:
+2 -2
View File
@@ -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.'
---
+24 -17
View File
@@ -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']
}
"
`);
});
});
+2 -1
View File
@@ -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`);
}
+6 -3
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.2.5",
"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 @@
]
}
}
}
}
+7
View File
@@ -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: {
+11
View File
@@ -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;
@@ -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);
@@ -443,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(', '),
);
});
});
+4 -5
View File
@@ -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);
+35 -26
View File
@@ -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))}"]
}`,
);
}
+2 -2
View File
@@ -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;
/**
+2 -5
View File
@@ -7,11 +7,8 @@ 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/*'],
};
+4 -2
View File
@@ -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) {
+6 -3
View File
@@ -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)),
);
}
+1 -1
View File
@@ -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');
+1 -1
View File
@@ -12,7 +12,7 @@ export function getGlobals(
{
name: '__BROWSER__',
value: config.browser,
type: `"chromium" | "firefox"`,
type: `string`,
},
{
name: '__IS_CHROME__',
+5 -2
View File
@@ -6,6 +6,7 @@ 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`.
@@ -27,11 +28,12 @@ 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();
@@ -54,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);
},
});
+17
View File
@@ -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);
}
+17
View File
@@ -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);
}
+3 -2
View File
@@ -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);
+2 -1
View File
@@ -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) {
+5
View File
@@ -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,6 +58,7 @@ 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]);
+1
View File
@@ -9,6 +9,7 @@
/* Type Checking */
"strict": true,
"lib": ["DOM", "WebWorker", "ESNext"],
/* Linting */
"noUnusedLocals": true,