Compare commits

...

10 Commits

Author SHA1 Message Date
GitHub Actions d8905d23a4 chore(release): v0.16.11 2024-02-21 14:17:40 +00:00
Aaron 5d2d124e5a fix: Output main JS file for HTML entrypoints to chunks directory (#473) 2024-02-21 08:13:43 -06:00
Aaron Klinker 4fda203def chore(e2e): Remove log 2024-02-21 08:13:21 -06:00
Aaron Klinker 905bfc7a38 ci: Upgrade pnpm/action-setup to v2 2024-02-20 14:35:53 -06:00
Aaron Klinker 7c6973f0e6 ci: Fix codecov warning in release workflow 2024-02-20 14:35:08 -06:00
GitHub Actions 969e39c541 chore(release): v0.16.10 2024-02-20 20:33:12 +00:00
Aaron b256939cc4 fix: Respect configFile option (#472) 2024-02-20 14:30:21 -06:00
Aaron c46137644b feat: Customize when content scripts are registered, in the manifest or at runtime (#471) 2024-02-20 14:18:53 -06:00
Aaron 1bd7ef56c4 fix: Don't assume react when importing JSX entrypoints during build (#470) 2024-02-20 13:53:00 -06:00
Aaron Klinker c258b99cec Update changelog 2024-02-20 08:26:02 -06:00
17 changed files with 222 additions and 41 deletions
+1 -1
View File
@@ -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
+1
View File
@@ -5,6 +5,7 @@ on:
jobs:
validate:
uses: './.github/workflows/validate.yml'
secrets: inherit
publish:
runs-on: ubuntu-22.04
+1 -1
View File
@@ -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
View File
@@ -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
+3
View File
@@ -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
},
-1
View File
@@ -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);
+25
View File
@@ -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
View File
@@ -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",
+13 -5
View File
@@ -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 -4
View File
@@ -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');
+65
View File
@@ -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', () => {
+5 -2
View File
@@ -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())
+8 -1
View File
@@ -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,
+9
View File
@@ -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
View File
@@ -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(
+12
View File
@@ -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