Compare commits

..

9 Commits

Author SHA1 Message Date
GitHub Actions 3c723d2c92 chore(release): v0.7.0 2023-09-30 17:45:03 +00:00
Aaron Klinker 1eb35c7207 Reduce codecov requirement to 80% 2023-09-30 12:38:14 -05:00
Aaron 725ecf7c64 fix: Reorder typescript paths to give priority to @ and ~ over @@ and ~~ (#142) 2023-09-30 12:33:33 -05:00
Aaron 3847092df4 feat: Content script cssInjectionMode (#141) 2023-09-30 12:32:00 -05:00
Aaron 19b11c236f fix!: Output content script CSS to content-scripts/<name>.css (#140)
BREAKING CHANGE: Content script CSS used to be output to `assets/<name>.css`, but is now `content-scripts/<name>.css` to match the docs.
2023-09-30 12:20:41 -05:00
Aaron Klinker 4b2012c489 fix: Validate transformed manifest correctly 2023-09-30 10:32:57 -05:00
Aaron Klinker 0591050f31 chore: Store user config metadata in memory
This will be used in #16
2023-09-30 10:30:25 -05:00
GitHub Actions b15dc7fc11 chore(release): v0.6.6 2023-09-30 15:05:16 +00:00
Aaron 89d15babc2 feat: Disable opening browser automatically during dev mode (#136) 2023-09-30 08:45:24 -05:00
24 changed files with 394 additions and 118 deletions
+6
View File
@@ -0,0 +1,6 @@
coverage:
status:
project:
default:
target: 80%
threshold: 1%
+30
View File
@@ -1,5 +1,35 @@
# Changelog
## v0.7.0
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.6.6...v0.7.0)
### 🚀 Enhancements
- Content script `cssInjectionMode` ([#141](https://github.com/wxt-dev/wxt/pull/141))
### 🩹 Fixes
- Validate transformed manifest correctly ([4b2012c](https://github.com/wxt-dev/wxt/commit/4b2012c))
- ⚠️ Output content script CSS to `content-scripts/<name>.css` ([#140](https://github.com/wxt-dev/wxt/pull/140))
- Reorder typescript paths to give priority to `@` and `~` over `@@` and `~~` ([#142](https://github.com/wxt-dev/wxt/pull/142))
### 🏡 Chore
- Store user config metadata in memory ([0591050](https://github.com/wxt-dev/wxt/commit/0591050))
#### ⚠️ Breaking Changes
- ⚠️ Output content script CSS to `content-scripts/<name>.css` ([#140](https://github.com/wxt-dev/wxt/pull/140))
## v0.6.6
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.6.5...v0.6.6)
### 🚀 Enhancements
- Disable opening browser automatically during dev mode ([#136](https://github.com/wxt-dev/wxt/pull/136))
## v0.6.5
[compare changes](https://github.com/wxt-dev/wxt/compare/v0.6.4...v0.6.5)
+3
View File
@@ -34,6 +34,9 @@ export default defineContentScript({
include: undefined | string[],
exclude: undefined | string[],
// Configure how CSS is injected onto the page
cssInjectionMode: undefined | "manifest" | "manual",
main(ctx) {
// Executed when content script is loaded
},
+162 -77
View File
@@ -193,117 +193,202 @@ describe('Manifest Content', () => {
});
});
it('should group content scripts and styles together based on their matches and run_at', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/one.content/index.ts',
`import "./style.css";
describe('content_scripts', () => {
it('should group content scripts and styles together based on their matches and run_at', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/one.content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["*://google.com/*"],
main: () => {},
})`,
);
project.addFile('entrypoints/one.content/style.css', `body { color: red }`);
project.addFile(
'entrypoints/two.content/index.ts',
`import "./style.css";
);
project.addFile(
'entrypoints/one.content/style.css',
`body { color: red }`,
);
project.addFile(
'entrypoints/two.content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["*://google.com/*"],
runAt: "document_end",
main: () => {},
})`,
);
project.addFile(
'entrypoints/two.content/style.css',
`body { color: green }`,
);
project.addFile(
'entrypoints/three.content/index.ts',
`import "./style.css";
);
project.addFile(
'entrypoints/two.content/style.css',
`body { color: green }`,
);
project.addFile(
'entrypoints/three.content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["*://google.com/*"],
runAt: "document_end",
main: () => {},
})`,
);
project.addFile(
'entrypoints/three.content/style.css',
`body { color: blue }`,
);
project.addFile(
'entrypoints/four.content/index.ts',
`import "./style.css";
);
project.addFile(
'entrypoints/three.content/style.css',
`body { color: blue }`,
);
project.addFile(
'entrypoints/four.content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["*://duckduckgo.com/*"],
runAt: "document_end",
main: () => {},
})`,
);
project.addFile(
'entrypoints/four.content/style.css',
`body { color: yellow }`,
);
);
project.addFile(
'entrypoints/four.content/style.css',
`body { color: yellow }`,
);
await project.build();
await project.build();
const manifest = await project.getOutputManifest();
const manifest = await project.getOutputManifest();
expect(manifest.content_scripts).toContainEqual({
matches: ['*://duckduckgo.com/*'],
run_at: 'document_end',
css: ['assets/four.css'],
js: ['content-scripts/four.js'],
expect(manifest.content_scripts).toContainEqual({
matches: ['*://duckduckgo.com/*'],
run_at: 'document_end',
css: ['content-scripts/four.css'],
js: ['content-scripts/four.js'],
});
expect(manifest.content_scripts).toContainEqual({
matches: ['*://google.com/*'],
run_at: 'document_end',
css: ['content-scripts/three.css', 'content-scripts/two.css'],
js: ['content-scripts/three.js', 'content-scripts/two.js'],
});
expect(manifest.content_scripts).toContainEqual({
matches: ['*://google.com/*'],
css: ['content-scripts/one.css'],
js: ['content-scripts/one.js'],
});
});
expect(manifest.content_scripts).toContainEqual({
matches: ['*://google.com/*'],
run_at: 'document_end',
css: ['assets/three.css', 'assets/two.css'],
js: ['content-scripts/three.js', 'content-scripts/two.js'],
});
expect(manifest.content_scripts).toContainEqual({
matches: ['*://google.com/*'],
css: ['assets/one.css'],
js: ['content-scripts/one.js'],
});
});
it('should add to any content scripts declared in wxt.config.ts', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/one.content/index.ts',
`export default defineContentScript({
it('should add to any content scripts declared in wxt.config.ts', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/one.content/index.ts',
`export default defineContentScript({
matches: ["*://google.com/*"],
main: () => {},
})`,
);
project.addFile(
'entrypoints/two.content/style.css',
`body {
);
project.addFile(
'entrypoints/two.content/style.css',
`body {
background-color: red;
}`,
);
project.setConfigFileConfig({
manifest: {
content_scripts: [
{
css: ['content-scripts/two.css'],
matches: ['*://*.google.com/*'],
},
],
},
);
project.setConfigFileConfig({
manifest: {
content_scripts: [
{
css: ['content-scripts/two.css'],
matches: ['*://*.google.com/*'],
},
],
},
});
await project.build();
const manifest = await project.getOutputManifest();
expect(manifest.content_scripts).toContainEqual({
css: ['content-scripts/two.css'],
matches: ['*://*.google.com/*'],
});
expect(manifest.content_scripts).toContainEqual({
matches: ['*://google.com/*'],
js: ['content-scripts/one.js'],
});
});
await project.build();
it('should add a CSS entry when cssInjectionMode is undefined', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/content/style.css',
'body { background-color: red; }',
);
project.addFile(
'entrypoints/content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["https://*.google.com/*"],
const manifest = await project.getOutputManifest();
main() {},
});`,
);
await project.build();
expect(manifest.content_scripts).toContainEqual({
css: ['content-scripts/two.css'],
matches: ['*://*.google.com/*'],
expect(await project.serializeFile('.output/chrome-mv3/manifest.json'))
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"https://*.google.com/*\\"],\\"css\\":[\\"content-scripts/content.css\\"],\\"js\\":[\\"content-scripts/content.js\\"]}]}"
`);
});
expect(manifest.content_scripts).toContainEqual({
matches: ['*://google.com/*'],
js: ['content-scripts/one.js'],
it('should add a CSS entry when cssInjectionMode is "manifest"', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/content/style.css',
'body { background-color: red; }',
);
project.addFile(
'entrypoints/content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["https://*.google.com/*"],
cssInjectionMode: "manifest",
main() {},
});`,
);
await project.build();
expect(await project.serializeFile('.output/chrome-mv3/manifest.json'))
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"https://*.google.com/*\\"],\\"css\\":[\\"content-scripts/content.css\\"],\\"js\\":[\\"content-scripts/content.js\\"]}]}"
`);
});
it('should not add an entry for CSS when cssInjectionMode is "manual"', async () => {
const project = new TestProject();
project.addFile(
'entrypoints/content/style.css',
'body { background-color: red; }',
);
project.addFile(
'entrypoints/content/index.ts',
`import "./style.css";
export default defineContentScript({
matches: ["https://*.google.com/*"],
cssInjectionMode: "manual",
main() {},
});`,
);
await project.build();
expect(await project.serializeFile('.output/chrome-mv3/manifest.json'))
.toMatchInlineSnapshot(`
".output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"https://*.google.com/*\\"],\\"js\\":[\\"content-scripts/content.js\\"]}]}"
`);
});
});
+7 -7
View File
@@ -49,19 +49,19 @@ describe('Output Directory Structure', () => {
'.output/chrome-mv3/content-scripts/two.js',
]),
).toMatchInlineSnapshot(`
".output/chrome-mv3/assets/one.css
".output/chrome-mv3/content-scripts/one.css
----------------------------------------
body{color:#00f}
================================================================================
.output/chrome-mv3/assets/two.css
----------------------------------------
body{color:red}
================================================================================
.output/chrome-mv3/content-scripts/one.js
----------------------------------------
<contents-ignored>
================================================================================
.output/chrome-mv3/content-scripts/two.css
----------------------------------------
body{color:red}
================================================================================
.output/chrome-mv3/content-scripts/two.js
----------------------------------------
@@ -69,7 +69,7 @@ describe('Output Directory Structure', () => {
================================================================================
.output/chrome-mv3/manifest.json
----------------------------------------
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"*://*/*\\"],\\"css\\":[\\"assets/one.css\\",\\"assets/two.css\\"],\\"js\\":[\\"content-scripts/one.js\\",\\"content-scripts/two.js\\"]}]}"
{\\"manifest_version\\":3,\\"name\\":\\"E2E Extension\\",\\"description\\":\\"Example description\\",\\"version\\":\\"0.0.0\\",\\"version_name\\":\\"0.0.0-test\\",\\"content_scripts\\":[{\\"matches\\":[\\"*://*/*\\"],\\"css\\":[\\"content-scripts/one.css\\",\\"content-scripts/two.css\\"],\\"js\\":[\\"content-scripts/one.js\\",\\"content-scripts/two.js\\"]}]}"
`);
});
+10 -10
View File
@@ -255,14 +255,14 @@ describe('TypeScript Project', () => {
\\"lib\\": [\\"DOM\\", \\"WebWorker\\"],
\\"skipLibCheck\\": true,
\\"paths\\": {
\\"@@\\": [\\"..\\"],
\\"@@/*\\": [\\"../*\\"],
\\"~~\\": [\\"..\\"],
\\"~~/*\\": [\\"../*\\"],
\\"@\\": [\\"..\\"],
\\"@/*\\": [\\"../*\\"],
\\"~\\": [\\"..\\"],
\\"~/*\\": [\\"../*\\"]
\\"~/*\\": [\\"../*\\"],
\\"@@\\": [\\"..\\"],
\\"@@/*\\": [\\"../*\\"],
\\"~~\\": [\\"..\\"],
\\"~~/*\\": [\\"../*\\"]
}
},
\\"include\\": [
@@ -299,14 +299,14 @@ describe('TypeScript Project', () => {
\\"lib\\": [\\"DOM\\", \\"WebWorker\\"],
\\"skipLibCheck\\": true,
\\"paths\\": {
\\"@@\\": [\\"..\\"],
\\"@@/*\\": [\\"../*\\"],
\\"~~\\": [\\"..\\"],
\\"~~/*\\": [\\"../*\\"],
\\"@\\": [\\"../src\\"],
\\"@/*\\": [\\"../src/*\\"],
\\"~\\": [\\"../src\\"],
\\"~/*\\": [\\"../src/*\\"]
\\"~/*\\": [\\"../src/*\\"],
\\"@@\\": [\\"..\\"],
\\"@@/*\\": [\\"../*\\"],
\\"~~\\": [\\"..\\"],
\\"~~/*\\": [\\"../*\\"]
}
},
\\"include\\": [
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.6.5",
"version": "0.7.0",
"description": "Next gen framework for developing web extensions",
"engines": {
"node": ">=18.16.0",
+9 -4
View File
@@ -84,10 +84,15 @@ async function buildSingleEntrypoint(
config.outDir,
'.js',
),
// Output content script CSS to assets/ with a hash to prevent conflicts. Defaults to
// "[name].[ext]" in lib mode, which usually results in "style.css". That means multiple
// content scripts with styles would overwrite each other if it weren't changed below.
assetFileNames: `assets/${entrypoint.name}.[ext]`,
// Output content script CSS to `content-scripts/`, but all other scripts are written to
// `assets/`.
assetFileNames: ({ name }) => {
if (entrypoint.type === 'content-script' && name?.endsWith('css')) {
return `content-scripts/${entrypoint.name}.[ext]`;
} else {
return `assets/${entrypoint.name}.[ext]`;
}
},
},
},
},
+5 -5
View File
@@ -213,14 +213,14 @@ async function writeTsConfigFile(
"lib": ["DOM", "WebWorker"],
"skipLibCheck": true,
"paths": {
"@@": ["${rootPath}"],
"@@/*": ["${rootPath}/*"],
"~~": ["${rootPath}"],
"~~/*": ["${rootPath}/*"],
"@": ["${srcPath}"],
"@/*": ["${srcPath}/*"],
"~": ["${srcPath}"],
"~/*": ["${srcPath}/*"]
"~/*": ["${srcPath}/*"],
"@@": ["${rootPath}"],
"@@/*": ["${rootPath}/*"],
"~~": ["${rootPath}"],
"~~/*": ["${rootPath}/*"]
}
},
"include": [
+79
View File
@@ -0,0 +1,79 @@
import { describe, expect, it, vi } from 'vitest';
import { createExtensionRunner } from '..';
import { fakeInternalConfig } from '../../../testing/fake-objects';
import { mock } from 'vitest-mock-extended';
import { createSafariRunner } from '../safari';
import { ExtensionRunner } from '../extension-runner';
import { createWslRunner } from '../wsl';
import { createManualRunner } from '../manual';
import { isWsl } from '../../utils/wsl';
import { createWebExtRunner } from '../web-ext';
vi.mock('../../utils/wsl');
const isWslMock = vi.mocked(isWsl);
vi.mock('../safari');
const createSafariRunnerMock = vi.mocked(createSafariRunner);
vi.mock('../wsl');
const createWslRunnerMock = vi.mocked(createWslRunner);
vi.mock('../manual');
const createManualRunnerMock = vi.mocked(createManualRunner);
vi.mock('../web-ext');
const createWebExtRunnerMock = vi.mocked(createWebExtRunner);
describe('createExtensionRunner', () => {
it('should return a Safari runner when browser is "safari"', async () => {
const config = fakeInternalConfig({
browser: 'safari',
});
const safariRunner = mock<ExtensionRunner>();
createSafariRunnerMock.mockReturnValue(safariRunner);
await expect(createExtensionRunner(config)).resolves.toBe(safariRunner);
});
it('should return a WSL runner when `is-wsl` is true', async () => {
isWslMock.mockResolvedValueOnce(true);
const config = fakeInternalConfig({
browser: 'chrome',
});
const wslRunner = mock<ExtensionRunner>();
createWslRunnerMock.mockReturnValue(wslRunner);
await expect(createExtensionRunner(config)).resolves.toBe(wslRunner);
});
it('should return a manual runner when `runner.disabled` is true', async () => {
isWslMock.mockResolvedValueOnce(false);
const config = fakeInternalConfig({
browser: 'chrome',
runnerConfig: {
config: {
disabled: true,
},
},
});
const manualRunner = mock<ExtensionRunner>();
createManualRunnerMock.mockReturnValue(manualRunner);
await expect(createExtensionRunner(config)).resolves.toBe(manualRunner);
});
it('should return a web-ext runner otherwise', async () => {
const config = fakeInternalConfig({
browser: 'chrome',
runnerConfig: {
config: {
disabled: undefined,
},
},
});
const manualRunner = mock<ExtensionRunner>();
createWebExtRunnerMock.mockReturnValue(manualRunner);
await expect(createExtensionRunner(config)).resolves.toBe(manualRunner);
});
});
+4 -2
View File
@@ -3,14 +3,16 @@ import { ExtensionRunner } from './extension-runner';
import { createWslRunner } from './wsl';
import { createWebExtRunner } from './web-ext';
import { createSafariRunner } from './safari';
import { createManualRunner } from './manual';
import { isWsl } from '../utils/wsl';
export async function createExtensionRunner(
config: InternalConfig,
): Promise<ExtensionRunner> {
if (config.browser === 'safari') return createSafariRunner();
const { default: isWsl } = await import('is-wsl'); // ESM only, requires dynamic import
if (isWsl) return createWslRunner();
if (await isWsl()) return createWslRunner();
if (config.runnerConfig.config?.disabled) return createManualRunner();
return createWebExtRunner();
}
+21
View File
@@ -0,0 +1,21 @@
import { ExtensionRunner } from './extension-runner';
import { relative } from 'node:path';
/**
* The manual runner tells the user to load the unpacked extension manually.
*/
export function createManualRunner(): ExtensionRunner {
return {
async openBrowser(config) {
config.logger.info(
`Load "${relative(
process.cwd(),
config.outDir,
)}" as an unpacked extension manually`,
);
},
async closeBrowser() {
// noop
},
};
}
+6 -3
View File
@@ -101,10 +101,13 @@ export function reloadContentScripts(
const js = [getEntrypointBundlePath(entry, config.outDir, '.js')];
const css = getContentScriptCssFiles([entry], server.currentOutput);
const { include: _1, exclude: _2, ...options } = entry.options;
server.reloadContentScript({
...options,
allFrames: entry.options.allFrames,
excludeMatches: entry.options.excludeMatches,
matches: entry.options.matches,
runAt: entry.options.runAt,
// @ts-expect-error: Chrome accepts this, not typed in webextension-polyfill (https://developer.chrome.com/docs/extensions/reference/scripting/#type-RegisteredContentScript)
world: entry.options.world,
js,
css,
});
+22
View File
@@ -177,6 +177,8 @@ export interface InlineConfig {
/**
* Explicitly include bundle analysis when running `wxt build`. This can be overridden by the
* command line `--analysis` option.
*
* @default false
*/
enabled?: boolean;
/**
@@ -184,6 +186,8 @@ export interface InlineConfig {
* bundle will be visualized. See
* [`rollup-plugin-visualizer`](https://github.com/btd/rollup-plugin-visualizer#how-to-use-generated-files)
* for more details.
*
* @default "treemap"
*/
template?: PluginVisualizerOptions['template'];
};
@@ -402,6 +406,18 @@ export interface ContentScriptDefinition extends ExcludableEntrypoint {
* @default "ISOLATED"
*/
world?: 'ISOLATED' | 'MAIN';
/**
* Customize how imported/generated styles are injected with the content script. Regardless of the
* mode selected, CSS will always be built and included in the output directory.
*
* - `"manifest"` - Include the CSS in the manifest, under the content script's `css` array.
* - `"manual"` - Exclude the CSS from the manifest. You are responsible for manually loading it
* onto the page. Use `browser.runtime.getURL("content-scripts/<name>.css")` to get the file's
* URL
*
* @default "manifest"
*/
cssInjectionMode?: 'manifest' | 'manual';
/**
* Main function executed when the content script is loaded.
*/
@@ -474,6 +490,12 @@ export interface ConfigEnv {
* Configure how the browser starts up.
*/
export interface ExtensionRunnerConfig {
/**
* Whether or not to open the browser with the extension installed in dev mode.
*
* @default false
*/
disabled?: boolean;
/**
* @see https://extensionworkshop.com/documentation/develop/web-ext-command-reference/#browser-console
*/
+2
View File
@@ -8,6 +8,7 @@ import {
UserManifest,
ExtensionRunnerConfig,
ConfigEnv,
UserConfig,
} from './external';
import { UnimportOptions } from 'unimport';
import { ResolvedConfig } from 'c12';
@@ -48,6 +49,7 @@ export interface InternalConfig {
enabled: boolean;
template: NonNullable<PluginVisualizerOptions['template']>;
};
userConfigMetadata: Omit<ResolvedConfig<UserConfig>, 'config'>;
}
export type EntrypointGroup = Entrypoint | Entrypoint[];
+7 -3
View File
@@ -30,13 +30,15 @@ export async function getInternalConfig(
// Load user config
let userConfig: UserConfig = {};
let userConfigMetadata: InternalConfig['userConfigMetadata'] | undefined;
if (inlineConfig.configFile !== false) {
const loaded = await loadConfig<UserConfig>({
const { config: loadedConfig, ...metadata } = await loadConfig<UserConfig>({
name: 'wxt',
cwd: inlineConfig.root ?? process.cwd(),
rcFile: false,
});
userConfig = loaded.config ?? {};
userConfig = loadedConfig ?? {};
userConfigMetadata = metadata;
}
// Merge it into the inline config
@@ -75,7 +77,8 @@ export async function getInternalConfig(
cwd: root,
globalRc: true,
rcFile: '.webextrc',
overrides: mergedConfig.runner,
overrides: inlineConfig.runner,
defaults: userConfig.runner,
});
const finalConfig: InternalConfig = {
@@ -108,6 +111,7 @@ export async function getInternalConfig(
enabled: mergedConfig.analysis?.enabled ?? false,
template: mergedConfig.analysis?.template ?? 'treemap',
},
userConfigMetadata: userConfigMetadata ?? {},
};
finalConfig.vite = (env) =>
+7 -6
View File
@@ -78,19 +78,19 @@ export async function generateMainfest(
if (config.command === 'serve') addDevModeCsp(manifest, config);
if (config.command === 'serve') addDevModePermissions(manifest, config);
// TODO: transform manifest here.
const finalManifest = produce(manifest, config.transformManifest);
if (manifest.name == null)
if (finalManifest.name == null)
throw Error(
"Manifest 'name' is missing. Either:\n1. Set the name in your <rootDir>/package.json\n2. Set a name via the manifest option in your wxt.config.ts",
);
if (manifest.version == null) {
if (finalManifest.version == null) {
throw Error(
"Manifest 'version' is missing. Either:\n1. Add a version in your <rootDir>/package.json\n2. Pass the version via the manifest option in your wxt.config.ts",
);
}
return produce(manifest, config.transformManifest);
return finalManifest;
}
/**
@@ -416,9 +416,10 @@ export function getContentScriptCssFiles(
const allChunks = buildOutput.steps.flatMap((step) => step.chunks);
contentScripts.forEach((script) => {
// TODO: optimize and remove loop with a map
if (script.options.cssInjectionMode === 'manual') return;
const relatedCss = allChunks.find(
(chunk) => chunk.fileName === `assets/${script.name}.css`,
(chunk) => chunk.fileName === `content-scripts/${script.name}.css`,
);
if (relatedCss) css.push(relatedCss.fileName);
});
+7
View File
@@ -0,0 +1,7 @@
/**
* Returns true when running on WSL or WSL2.
*/
export async function isWsl(): Promise<boolean> {
const { default: isWsl } = await import('is-wsl'); // ESM only, requires dynamic import
return isWsl;
}
+1
View File
@@ -240,5 +240,6 @@ export const fakeInternalConfig = fakeObjectCreator<InternalConfig>(() => {
name: faker.person.firstName().toLowerCase(),
},
transformManifest: () => {},
userConfigMetadata: {},
};
});
+1
View File
@@ -11,6 +11,7 @@ node_modules
.output
stats.html
.wxt
web-ext.config.ts
# Editor directories and files
.vscode/*
+1
View File
@@ -11,6 +11,7 @@ node_modules
.output
stats.html
.wxt
web-ext.config.ts
# Editor directories and files
.vscode/*
+1
View File
@@ -11,6 +11,7 @@ node_modules
.output
stats.html
.wxt
web-ext.config.ts
# Editor directories and files
.vscode/*
+1
View File
@@ -11,6 +11,7 @@ node_modules
.output
stats.html
.wxt
web-ext.config.ts
# Editor directories and files
.vscode/*
+1
View File
@@ -11,6 +11,7 @@ node_modules
.output
stats.html
.wxt
web-ext.config.ts
# Editor directories and files
.vscode/*