Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8905d23a4 | |||
| 5d2d124e5a | |||
| 4fda203def | |||
| 905bfc7a38 | |||
| 7c6973f0e6 | |||
| 969e39c541 | |||
| b256939cc4 | |||
| c46137644b | |||
| 1bd7ef56c4 | |||
| c258b99cec |
@@ -4,7 +4,7 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
- name: Setup NodeJS
|
||||
|
||||
@@ -5,6 +5,7 @@ on:
|
||||
jobs:
|
||||
validate:
|
||||
uses: './.github/workflows/validate.yml'
|
||||
secrets: inherit
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
- uses: actions/setup-node@v4
|
||||
|
||||
+33
-3
@@ -1,5 +1,35 @@
|
||||
# Changelog
|
||||
|
||||
## v0.16.11
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.16.10...v0.16.11)
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Output main JS file for HTML entrypoints to chunks directory ([#473](https://github.com/wxt-dev/wxt/pull/473))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- **e2e:** Remove log ([4fda203](https://github.com/wxt-dev/wxt/commit/4fda203))
|
||||
|
||||
### 🤖 CI
|
||||
|
||||
- Fix codecov warning in release workflow ([7c6973f](https://github.com/wxt-dev/wxt/commit/7c6973f))
|
||||
- Upgrade `pnpm/action-setup` to v2 ([905bfc7](https://github.com/wxt-dev/wxt/commit/905bfc7))
|
||||
|
||||
## v0.16.10
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.16.9...v0.16.10)
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- Customize when content scripts are registered, in the manifest or at runtime ([#471](https://github.com/wxt-dev/wxt/pull/471))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Don't assume react when importing JSX entrypoints during build ([#470](https://github.com/wxt-dev/wxt/pull/470))
|
||||
- Respect `configFile` option ([#472](https://github.com/wxt-dev/wxt/pull/472))
|
||||
|
||||
## v0.16.9
|
||||
|
||||
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.16.8...v0.16.9)
|
||||
@@ -22,7 +52,7 @@
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Okou
|
||||
- Okou ([@ookkoouu](https://github.com/ookkoouu))
|
||||
|
||||
## v0.16.8
|
||||
|
||||
@@ -83,8 +113,8 @@
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Lionelhorn
|
||||
- Okou
|
||||
- Lionelhorn ([@Lionelhorn](https://github.com/Lionelhorn))
|
||||
- Okou ([@ookkoouu](https://github.com/ookkoouu))
|
||||
|
||||
## v0.16.5
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@ export default defineContentScript({
|
||||
// Configure how CSS is injected onto the page
|
||||
cssInjectionMode: undefined | "manifest" | "manual" | "ui",
|
||||
|
||||
// Configure how/when content script will be registered
|
||||
registration: undefined | "manifest" | "runtime",
|
||||
|
||||
main(ctx) {
|
||||
// Executed when content script is loaded
|
||||
},
|
||||
|
||||
@@ -84,7 +84,6 @@ describe('Analysis', () => {
|
||||
keepArtifacts: true,
|
||||
},
|
||||
});
|
||||
console.log(project.root);
|
||||
|
||||
expect(await project.fileExists('stats/bundle.html')).toBe(true);
|
||||
expect(await project.fileExists('stats/bundle-0.json')).toBe(true);
|
||||
|
||||
@@ -107,4 +107,29 @@ describe('User Config', () => {
|
||||
});
|
||||
expect(withoutPolyfill).not.toBe(withPolyfill);
|
||||
});
|
||||
|
||||
it('should respect changing config files', async () => {
|
||||
const project = new TestProject();
|
||||
project.addFile(
|
||||
'src/entrypoints/background.ts',
|
||||
`export default defineBackground(
|
||||
() => console.log('Hello background'),
|
||||
);`,
|
||||
);
|
||||
project.addFile(
|
||||
'test.config.ts',
|
||||
`import { defineConfig } from 'wxt';
|
||||
|
||||
export default defineConfig({
|
||||
outDir: ".custom-output",
|
||||
srcDir: "src",
|
||||
});`,
|
||||
);
|
||||
|
||||
await project.build({ configFile: 'test.config.ts' });
|
||||
|
||||
expect(
|
||||
await project.fileExists('.custom-output/chrome-mv3/background.js'),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "wxt",
|
||||
"type": "module",
|
||||
"version": "0.16.9",
|
||||
"version": "0.16.11",
|
||||
"description": "Next gen framework for developing web extensions",
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
|
||||
@@ -10,7 +10,10 @@ import {
|
||||
WxtBuilderServer,
|
||||
} from '~/types';
|
||||
import * as wxtPlugins from './plugins';
|
||||
import { getEntrypointBundlePath } from '~/core/utils/entrypoints';
|
||||
import {
|
||||
getEntrypointBundlePath,
|
||||
isHtmlEntrypoint,
|
||||
} from '~/core/utils/entrypoints';
|
||||
|
||||
export async function createViteBuilder(
|
||||
inlineConfig: InlineConfig,
|
||||
@@ -133,6 +136,9 @@ export async function createViteBuilder(
|
||||
* Return the basic config for building multiple entrypoints in [multi-page mode](https://vitejs.dev/guide/build.html#multi-page-app).
|
||||
*/
|
||||
const getMultiPageConfig = (entrypoints: Entrypoint[]): vite.InlineConfig => {
|
||||
const htmlEntrypoints = new Set(
|
||||
entrypoints.filter(isHtmlEntrypoint).map((e) => e.name),
|
||||
);
|
||||
return {
|
||||
mode: wxtConfig.mode,
|
||||
plugins: [
|
||||
@@ -148,10 +154,12 @@ export async function createViteBuilder(
|
||||
output: {
|
||||
// Include a hash to prevent conflicts
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
// Place JS entrypoints in main directory without a hash. (popup.html & popup.js are
|
||||
// next to each other). The unique entrypoint name requirement prevents conflicts with
|
||||
// scripts of the same name
|
||||
entryFileNames: '[name].js',
|
||||
entryFileNames: ({ name }) => {
|
||||
// HTML main JS files go in the chunks folder
|
||||
if (htmlEntrypoints.has(name)) return 'chunks/[name]-[hash].js';
|
||||
// Scripts are output in the root folder
|
||||
return '[name].js';
|
||||
},
|
||||
// We can't control the "name", so we need a hash to prevent conflicts
|
||||
assetFileNames: 'assets/[name]-[hash].[ext]',
|
||||
},
|
||||
|
||||
@@ -5,7 +5,10 @@ import {
|
||||
ServerInfo,
|
||||
WxtDevServer,
|
||||
} from '~/types';
|
||||
import { getEntrypointBundlePath } from '~/core/utils/entrypoints';
|
||||
import {
|
||||
getEntrypointBundlePath,
|
||||
isHtmlEntrypoint,
|
||||
} from '~/core/utils/entrypoints';
|
||||
import {
|
||||
getContentScriptCssFiles,
|
||||
getContentScriptsCssMap,
|
||||
@@ -248,9 +251,7 @@ function reloadHtmlPages(
|
||||
server: WxtDevServer,
|
||||
): { reloadedNames: string[] } {
|
||||
// groups might contain other files like background/content scripts, and we only care about the HTMl pages
|
||||
const htmlEntries = groups
|
||||
.flat()
|
||||
.filter((entry) => entry.inputPath.endsWith('.html'));
|
||||
const htmlEntries = groups.flat().filter(isHtmlEntrypoint);
|
||||
|
||||
htmlEntries.forEach((entry) => {
|
||||
const path = getEntrypointBundlePath(entry, wxt.config.outDir, '.html');
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
fakeArray,
|
||||
fakeBackgroundEntrypoint,
|
||||
fakeBuildOutput,
|
||||
fakeContentScriptEntrypoint,
|
||||
fakeEntrypoint,
|
||||
fakeManifestCommand,
|
||||
fakeOptionsEntrypoint,
|
||||
@@ -757,6 +758,70 @@ describe('Manifest Utils', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('registration', () => {
|
||||
it('should throw an error when registration=runtime for MV2', async () => {
|
||||
const cs: ContentScriptEntrypoint = fakeContentScriptEntrypoint({
|
||||
options: {
|
||||
registration: 'runtime',
|
||||
},
|
||||
});
|
||||
|
||||
const entrypoints = [cs];
|
||||
const buildOutput: Omit<BuildOutput, 'manifest'> = {
|
||||
publicAssets: [],
|
||||
steps: [{ entrypoints: cs, chunks: [] }],
|
||||
};
|
||||
setFakeWxt({
|
||||
config: {
|
||||
manifestVersion: 2,
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
generateManifest(entrypoints, buildOutput),
|
||||
).rejects.toThrowError();
|
||||
});
|
||||
|
||||
it('should add host_permissions instead of content_scripts when registration=runtime', async () => {
|
||||
const cs: ContentScriptEntrypoint = {
|
||||
type: 'content-script',
|
||||
name: 'one',
|
||||
inputPath: 'entrypoints/one.content.ts',
|
||||
outputDir: contentScriptOutDir,
|
||||
options: {
|
||||
matches: ['*://google.com/*'],
|
||||
registration: 'runtime',
|
||||
},
|
||||
skipped: false,
|
||||
};
|
||||
const styles: OutputAsset = {
|
||||
type: 'asset',
|
||||
fileName: 'content-scripts/one.css',
|
||||
};
|
||||
|
||||
const entrypoints = [cs];
|
||||
const buildOutput: Omit<BuildOutput, 'manifest'> = {
|
||||
publicAssets: [],
|
||||
steps: [{ entrypoints: cs, chunks: [styles] }],
|
||||
};
|
||||
setFakeWxt({
|
||||
config: {
|
||||
manifestVersion: 3,
|
||||
outDir,
|
||||
command: 'build',
|
||||
},
|
||||
});
|
||||
|
||||
const { manifest: actual } = await generateManifest(
|
||||
entrypoints,
|
||||
buildOutput,
|
||||
);
|
||||
|
||||
expect(actual.content_scripts).toEqual([]);
|
||||
expect(actual.host_permissions).toEqual(['*://google.com/*']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sidepanel', () => {
|
||||
|
||||
@@ -6,7 +6,10 @@ import {
|
||||
} from '~/types';
|
||||
import fs from 'fs-extra';
|
||||
import { relative, resolve } from 'path';
|
||||
import { getEntrypointBundlePath } from '~/core/utils/entrypoints';
|
||||
import {
|
||||
getEntrypointBundlePath,
|
||||
isHtmlEntrypoint,
|
||||
} from '~/core/utils/entrypoints';
|
||||
import { getEntrypointGlobals, getGlobals } from '~/core/utils/globals';
|
||||
import { normalizePath } from '~/core/utils/paths';
|
||||
import path from 'node:path';
|
||||
@@ -82,7 +85,7 @@ async function writePathsDeclarationFile(
|
||||
getEntrypointBundlePath(
|
||||
entry,
|
||||
wxt.config.outDir,
|
||||
entry.inputPath.endsWith('.html') ? '.html' : '.js',
|
||||
isHtmlEntrypoint(entry) ? '.html' : '.js',
|
||||
),
|
||||
)
|
||||
.concat(await getPublicFiles())
|
||||
|
||||
@@ -105,6 +105,13 @@ function getEsbuildOptions(opts: JitiTransformOptions): TransformOptions {
|
||||
return {
|
||||
format: 'cjs',
|
||||
loader: isJsx ? 'tsx' : 'ts',
|
||||
jsx: isJsx ? 'automatic' : undefined,
|
||||
...(isJsx
|
||||
? {
|
||||
// `h` and `Fragment` are undefined, but that's OK because JSX is never evaluated while
|
||||
// grabbing the entrypoint's options.
|
||||
jsxFactory: 'h',
|
||||
jsxFragment: 'Fragment',
|
||||
}
|
||||
: undefined),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ export async function resolveConfig(
|
||||
let userConfigMetadata: ResolvedConfig['userConfigMetadata'] | undefined;
|
||||
if (inlineConfig.configFile !== false) {
|
||||
const { config: loadedConfig, ...metadata } = await loadConfig<UserConfig>({
|
||||
configFile: inlineConfig.configFile,
|
||||
name: 'wxt',
|
||||
cwd: inlineConfig.root ?? process.cwd(),
|
||||
rcFile: false,
|
||||
|
||||
@@ -70,3 +70,12 @@ export function resolvePerBrowserOptions<
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the entrypoint is an HTML entrypoint.
|
||||
*
|
||||
* Naively just checking the file extension of the input path.
|
||||
*/
|
||||
export function isHtmlEntrypoint(entrypoint: Entrypoint): boolean {
|
||||
return entrypoint.inputPath.endsWith('.html');
|
||||
}
|
||||
|
||||
+39
-22
@@ -355,37 +355,54 @@ function addEntrypoints(
|
||||
// Don't add content scripts to the manifest in dev mode for MV3 - they're managed and reloaded
|
||||
// at runtime
|
||||
if (wxt.config.command === 'serve' && wxt.config.manifestVersion === 3) {
|
||||
const hostPermissions = new Set<string>(manifest.host_permissions ?? []);
|
||||
contentScripts.forEach((script) => {
|
||||
script.options.matches.forEach((matchPattern) => {
|
||||
hostPermissions.add(matchPattern);
|
||||
addHostPermission(manifest, matchPattern);
|
||||
});
|
||||
});
|
||||
hostPermissions.forEach((permission) =>
|
||||
addHostPermission(manifest, permission),
|
||||
);
|
||||
} else {
|
||||
const hashToEntrypointsMap = contentScripts.reduce((map, script) => {
|
||||
const hash = hashContentScriptOptions(script.options);
|
||||
if (map.has(hash)) map.get(hash)?.push(script);
|
||||
else map.set(hash, [script]);
|
||||
return map;
|
||||
}, new Map<string, ContentScriptEntrypoint[]>());
|
||||
|
||||
const newContentScripts = Array.from(hashToEntrypointsMap.entries()).map(
|
||||
([, scripts]) =>
|
||||
mapWxtOptionsToContentScript(
|
||||
scripts[0].options,
|
||||
scripts.map((entry) =>
|
||||
getEntrypointBundlePath(entry, wxt.config.outDir, '.js'),
|
||||
),
|
||||
getContentScriptCssFiles(scripts, cssMap),
|
||||
// Manifest scripts
|
||||
const hashToEntrypointsMap = contentScripts
|
||||
.filter((cs) => cs.options.registration !== 'runtime')
|
||||
.reduce((map, script) => {
|
||||
const hash = hashContentScriptOptions(script.options);
|
||||
if (map.has(hash)) map.get(hash)?.push(script);
|
||||
else map.set(hash, [script]);
|
||||
return map;
|
||||
}, new Map<string, ContentScriptEntrypoint[]>());
|
||||
const manifestContentScripts = Array.from(
|
||||
hashToEntrypointsMap.values(),
|
||||
).map((scripts) =>
|
||||
mapWxtOptionsToContentScript(
|
||||
scripts[0].options,
|
||||
scripts.map((entry) =>
|
||||
getEntrypointBundlePath(entry, wxt.config.outDir, '.js'),
|
||||
),
|
||||
getContentScriptCssFiles(scripts, cssMap),
|
||||
),
|
||||
);
|
||||
if (newContentScripts.length >= 0) {
|
||||
if (manifestContentScripts.length >= 0) {
|
||||
manifest.content_scripts ??= [];
|
||||
manifest.content_scripts.push(...newContentScripts);
|
||||
manifest.content_scripts.push(...manifestContentScripts);
|
||||
}
|
||||
|
||||
// Runtime content scripts
|
||||
const runtimeContentScripts = contentScripts.filter(
|
||||
(cs) => cs.options.registration === 'runtime',
|
||||
);
|
||||
if (
|
||||
runtimeContentScripts.length > 0 &&
|
||||
wxt.config.manifestVersion === 2
|
||||
) {
|
||||
throw Error(
|
||||
'Cannot use `registration: "runtime"` with MV2 content scripts, it is a MV3-only feature.',
|
||||
);
|
||||
}
|
||||
runtimeContentScripts.forEach((script) => {
|
||||
script.options.matches.forEach((matchPattern) => {
|
||||
addHostPermission(manifest, matchPattern);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const contentScriptCssResources = getContentScriptCssWebAccessibleResources(
|
||||
|
||||
@@ -507,6 +507,18 @@ export interface BaseContentScriptEntrypointOptions
|
||||
* @default "manifest"
|
||||
*/
|
||||
cssInjectionMode?: PerBrowserOption<'manifest' | 'manual' | 'ui'>;
|
||||
/**
|
||||
* Specify how the content script is registered.
|
||||
*
|
||||
* - `"manifest"`: The content script will be added to the `content_scripts` entry in the
|
||||
* manifest. This is the normal and most well known way of registering a content script.
|
||||
* - `"runtime"`: The content script's `matches` is added to `host_permissions` and you are
|
||||
* responsible for using the scripting API to register the content script dynamically at
|
||||
* runtime.
|
||||
*
|
||||
* @default "manifest"
|
||||
*/
|
||||
registration?: PerBrowserOption<'manifest' | 'runtime'>;
|
||||
}
|
||||
|
||||
export interface MainWorldContentScriptEntrypointOptions
|
||||
|
||||
Reference in New Issue
Block a user