Skip to content

Commit c9c5d72

Browse files
authored
chore: Refactor order of config resolution (#578)
1 parent 1556b9a commit c9c5d72

File tree

11 files changed

+135
-113
lines changed

11 files changed

+135
-113
lines changed

Diff for: src/core/builders/vite/index.ts

+6-15
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ import type * as vite from 'vite';
22
import {
33
BuildStepOutput,
44
Entrypoint,
5-
InlineConfig,
65
ResolvedConfig,
7-
UserConfig,
86
VirtualEntrypointType,
97
WxtBuilder,
108
WxtBuilderServer,
9+
WxtDevServer,
1110
} from '~/types';
1211
import * as wxtPlugins from './plugins';
1312
import {
@@ -16,24 +15,16 @@ import {
1615
} from '~/core/utils/entrypoints';
1716

1817
export async function createViteBuilder(
19-
inlineConfig: InlineConfig,
20-
userConfig: UserConfig,
21-
wxtConfig: Omit<ResolvedConfig, 'builder'>,
18+
wxtConfig: ResolvedConfig,
19+
server?: WxtDevServer,
2220
): Promise<WxtBuilder> {
2321
const vite = await import('vite');
2422

2523
/**
2624
* Returns the base vite config shared by all builds based on the inline and user config.
2725
*/
2826
const getBaseConfig = async () => {
29-
const resolvedInlineConfig =
30-
(await inlineConfig.vite?.(wxtConfig.env)) ?? {};
31-
const resolvedUserConfig = (await userConfig.vite?.(wxtConfig.env)) ?? {};
32-
33-
const config: vite.InlineConfig = vite.mergeConfig(
34-
resolvedUserConfig,
35-
resolvedInlineConfig,
36-
);
27+
const config: vite.InlineConfig = await wxtConfig.vite(wxtConfig.env);
3728

3829
config.root = wxtConfig.root;
3930
config.configFile = false;
@@ -55,13 +46,13 @@ export async function createViteBuilder(
5546
config.plugins ??= [];
5647
config.plugins.push(
5748
wxtPlugins.download(wxtConfig),
58-
wxtPlugins.devHtmlPrerender(wxtConfig),
49+
wxtPlugins.devHtmlPrerender(wxtConfig, server),
5950
wxtPlugins.unimport(wxtConfig),
6051
wxtPlugins.virtualEntrypoint('background', wxtConfig),
6152
wxtPlugins.virtualEntrypoint('content-script-isolated-world', wxtConfig),
6253
wxtPlugins.virtualEntrypoint('content-script-main-world', wxtConfig),
6354
wxtPlugins.virtualEntrypoint('unlisted-script', wxtConfig),
64-
wxtPlugins.devServerGlobals(wxtConfig),
55+
wxtPlugins.devServerGlobals(wxtConfig, server),
6556
wxtPlugins.tsconfigPaths(wxtConfig),
6657
wxtPlugins.noopBackground(),
6758
wxtPlugins.globals(wxtConfig),

Diff for: src/core/builders/vite/plugins/devHtmlPrerender.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ let reactRefreshPreamble = '';
1313
*/
1414
export function devHtmlPrerender(
1515
config: Omit<ResolvedConfig, 'builder'>,
16+
server: WxtDevServer | undefined,
1617
): vite.PluginOption {
1718
const htmlReloadId = '@wxt/reload-html';
1819
const resolvedHtmlReloadId = resolve(
@@ -38,7 +39,6 @@ export function devHtmlPrerender(
3839
// Convert scripts like src="./main.tsx" -> src="http://localhost:3000/entrypoints/popup/main.tsx"
3940
// before the paths are replaced with their bundled path
4041
transform(code, id) {
41-
const server = config.server;
4242
if (
4343
config.command !== 'serve' ||
4444
server == null ||
@@ -68,7 +68,6 @@ export function devHtmlPrerender(
6868

6969
// Pass the HTML through the dev server to add dev-mode specific code
7070
async transformIndexHtml(html, ctx) {
71-
const server = config.server;
7271
if (config.command !== 'serve' || server == null) return;
7372

7473
const originalUrl = `${server.origin}${ctx.path}`;

Diff for: src/core/builders/vite/plugins/devServerGlobals.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
import { Plugin } from 'vite';
2-
import { ResolvedConfig } from '~/types';
2+
import { ResolvedConfig, WxtDevServer } from '~/types';
33

44
/**
55
* Defines global constants about the dev server. Helps scripts connect to the server's web socket.
66
*/
77
export function devServerGlobals(
88
config: Omit<ResolvedConfig, 'builder'>,
9+
server: WxtDevServer | undefined,
910
): Plugin {
1011
return {
1112
name: 'wxt:dev-server-globals',
1213
config() {
13-
if (config.server == null || config.command == 'build') return;
14+
if (server == null || config.command == 'build') return;
1415

1516
return {
1617
define: {
1718
__DEV_SERVER_PROTOCOL__: JSON.stringify('ws:'),
18-
__DEV_SERVER_HOSTNAME__: JSON.stringify(config.server.hostname),
19-
__DEV_SERVER_PORT__: JSON.stringify(config.server.port),
19+
__DEV_SERVER_HOSTNAME__: JSON.stringify(server.hostname),
20+
__DEV_SERVER_PORT__: JSON.stringify(server.port),
2021
},
2122
};
2223
},

Diff for: src/core/create-server.ts

+58-57
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,64 @@ import { mapWxtOptionsToRegisteredContentScript } from './utils/content-scripts'
4040
export async function createServer(
4141
inlineConfig?: InlineConfig,
4242
): Promise<WxtDevServer> {
43-
const port = await getPort();
44-
const hostname = 'localhost';
45-
const origin = `http://${hostname}:${port}`;
46-
const serverInfo: ServerInfo = {
47-
port,
48-
hostname,
49-
origin,
50-
};
43+
await registerWxt('serve', inlineConfig, async (_config) => {
44+
const port = await getPort();
45+
const hostname = 'localhost';
46+
const serverInfo: ServerInfo = {
47+
port,
48+
hostname,
49+
origin: `http://${hostname}:${port}`,
50+
};
51+
52+
// Server instance must be created first so its reference can be added to the internal config used
53+
// to pre-render entrypoints
54+
const server: WxtDevServer = {
55+
...serverInfo,
56+
get watcher() {
57+
return builderServer.watcher;
58+
},
59+
get ws() {
60+
return builderServer.ws;
61+
},
62+
currentOutput: undefined,
63+
async start() {
64+
await builderServer.listen();
65+
wxt.logger.success(`Started dev server @ ${serverInfo.origin}`);
66+
await buildAndOpenBrowser();
67+
},
68+
async stop() {
69+
await runner.closeBrowser();
70+
await builderServer.close();
71+
},
72+
async restart() {
73+
await closeAndRecreateRunner();
74+
await buildAndOpenBrowser();
75+
},
76+
transformHtml(url, html, originalUrl) {
77+
return builderServer.transformHtml(url, html, originalUrl);
78+
},
79+
reloadContentScript(payload) {
80+
server.ws.send('wxt:reload-content-script', payload);
81+
},
82+
reloadPage(path) {
83+
server.ws.send('wxt:reload-page', path);
84+
},
85+
reloadExtension() {
86+
server.ws.send('wxt:reload-extension');
87+
},
88+
async restartBrowser() {
89+
await closeAndRecreateRunner();
90+
await runner.openBrowser();
91+
},
92+
};
93+
return server;
94+
});
95+
96+
const server = wxt.server!;
97+
let [runner, builderServer] = await Promise.all([
98+
createExtensionRunner(),
99+
wxt.builder.createServer(server),
100+
]);
51101

52102
const buildAndOpenBrowser = async () => {
53103
// Build after starting the dev server so it can be used to transform HTML files
@@ -74,55 +124,6 @@ export async function createServer(
74124
runner = await createExtensionRunner();
75125
};
76126

77-
// Server instance must be created first so its reference can be added to the internal config used
78-
// to pre-render entrypoints
79-
const server: WxtDevServer = {
80-
...serverInfo,
81-
get watcher() {
82-
return builderServer.watcher;
83-
},
84-
get ws() {
85-
return builderServer.ws;
86-
},
87-
currentOutput: undefined,
88-
async start() {
89-
await builderServer.listen();
90-
wxt.logger.success(`Started dev server @ ${serverInfo.origin}`);
91-
await buildAndOpenBrowser();
92-
},
93-
async stop() {
94-
await runner.closeBrowser();
95-
await builderServer.close();
96-
},
97-
async restart() {
98-
await closeAndRecreateRunner();
99-
await buildAndOpenBrowser();
100-
},
101-
transformHtml(url, html, originalUrl) {
102-
return builderServer.transformHtml(url, html, originalUrl);
103-
},
104-
reloadContentScript(payload) {
105-
server.ws.send('wxt:reload-content-script', payload);
106-
},
107-
reloadPage(path) {
108-
server.ws.send('wxt:reload-page', path);
109-
},
110-
reloadExtension() {
111-
server.ws.send('wxt:reload-extension');
112-
},
113-
async restartBrowser() {
114-
await closeAndRecreateRunner();
115-
await runner.openBrowser();
116-
},
117-
};
118-
119-
await registerWxt('serve', inlineConfig, server);
120-
121-
let [runner, builderServer] = await Promise.all([
122-
createExtensionRunner(),
123-
wxt.config.builder.createServer(server),
124-
]);
125-
126127
// Register content scripts for the first time after the background starts up since they're not
127128
// listed in the manifest
128129
server.ws.on('wxt:background-initialized', () => {

Diff for: src/core/utils/building/build-entrypoints.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export async function buildEntrypoints(
1818
spinner.text =
1919
pc.dim(`[${i + 1}/${groups.length}]`) + ` ${groupNameColored}`;
2020
try {
21-
steps.push(await wxt.config.builder.build(group));
21+
steps.push(await wxt.builder.build(group));
2222
} catch (err) {
2323
spinner.stop().clear();
2424
wxt.logger.error(err);

Diff for: src/core/utils/building/internal-build.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export async function internalBuild(): Promise<BuildOutput> {
3737
const target = `${wxt.config.browser}-mv${wxt.config.manifestVersion}`;
3838
wxt.logger.info(
3939
`${verb} ${pc.cyan(target)} for ${pc.cyan(wxt.config.mode)} with ${pc.green(
40-
`${wxt.config.builder.name} ${wxt.config.builder.version}`,
40+
`${wxt.builder.name} ${wxt.builder.version}`,
4141
)}`,
4242
);
4343
const startTime = Date.now();

Diff for: src/core/utils/building/resolve-config.ts

+27-21
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@ import {
77
UserManifestFn,
88
UserManifest,
99
ExtensionRunnerConfig,
10-
WxtDevServer,
1110
WxtResolvedUnimportOptions,
1211
Logger,
1312
WxtCommand,
1413
} from '~/types';
1514
import path from 'node:path';
1615
import { createFsCache } from '~/core/utils/cache';
1716
import consola, { LogLevels } from 'consola';
18-
import { createViteBuilder } from '~/core/builders/vite';
1917
import defu from 'defu';
2018
import { NullablyRequired } from '../types';
2119
import { isModuleInstalled } from '../package';
@@ -32,7 +30,6 @@ import { normalizePath } from '../paths';
3230
export async function resolveConfig(
3331
inlineConfig: InlineConfig,
3432
command: WxtCommand,
35-
server?: WxtDevServer,
3633
): Promise<ResolvedConfig> {
3734
// Load user config
3835

@@ -54,7 +51,7 @@ export async function resolveConfig(
5451

5552
// Merge it into the inline config
5653

57-
const mergedConfig = mergeInlineConfig(inlineConfig, userConfig);
54+
const mergedConfig = await mergeInlineConfig(inlineConfig, userConfig);
5855

5956
// Apply defaults to make internal config.
6057

@@ -113,7 +110,7 @@ export async function resolveConfig(
113110
}).map(([key, value]) => [key, path.resolve(root, value)]),
114111
);
115112

116-
const finalConfig: Omit<ResolvedConfig, 'builder'> = {
113+
return {
117114
browser,
118115
command,
119116
debug,
@@ -143,22 +140,11 @@ export async function resolveConfig(
143140
experimental: defu(mergedConfig.experimental, {
144141
includeBrowserPolyfill: true,
145142
}),
146-
server,
147143
dev: {
148144
reloadCommand,
149145
},
150146
hooks: mergedConfig.hooks ?? {},
151-
};
152-
153-
const builder = await createViteBuilder(
154-
inlineConfig,
155-
userConfig,
156-
finalConfig,
157-
);
158-
159-
return {
160-
...finalConfig,
161-
builder,
147+
vite: mergedConfig.vite ?? (() => ({})),
162148
};
163149
}
164150

@@ -174,10 +160,10 @@ async function resolveManifestConfig(
174160
/**
175161
* Merge the inline config and user config. Inline config is given priority. Defaults are not applied here.
176162
*/
177-
function mergeInlineConfig(
163+
async function mergeInlineConfig(
178164
inlineConfig: InlineConfig,
179165
userConfig: UserConfig,
180-
): InlineConfig {
166+
): Promise<InlineConfig> {
181167
// Merge imports option
182168
const imports: InlineConfig['imports'] =
183169
inlineConfig.imports === false || userConfig.imports === false
@@ -199,14 +185,16 @@ function mergeInlineConfig(
199185
inlineConfig.transformManifest?.(manifest);
200186
};
201187

188+
// Builders
189+
const builderConfig = await mergeBuilderConfig(inlineConfig, userConfig);
190+
202191
return {
203192
...defu(inlineConfig, userConfig),
204193
// Custom merge values
205194
transformManifest,
206195
imports,
207196
manifest,
208-
// Vite builder handles merging vite config internally
209-
vite: undefined,
197+
...builderConfig,
210198
};
211199
}
212200

@@ -337,3 +325,21 @@ const COMMAND_MODES: Record<WxtCommand, string> = {
337325
build: 'production',
338326
serve: 'development',
339327
};
328+
329+
export async function mergeBuilderConfig(
330+
inlineConfig: InlineConfig,
331+
userConfig: UserConfig,
332+
): Promise<Pick<InlineConfig, 'vite'>> {
333+
const vite = await import('vite').catch(() => void 0);
334+
if (vite) {
335+
return {
336+
vite: async (env) => {
337+
const resolvedInlineConfig = (await inlineConfig.vite?.(env)) ?? {};
338+
const resolvedUserConfig = (await userConfig.vite?.(env)) ?? {};
339+
return vite.mergeConfig(resolvedUserConfig, resolvedInlineConfig);
340+
},
341+
};
342+
}
343+
344+
throw Error('Builder not found. Make sure vite is installed.');
345+
}

Diff for: src/core/utils/manifest.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -455,8 +455,8 @@ function discoverIcons(
455455
}
456456

457457
function addDevModeCsp(manifest: Manifest.WebExtensionManifest): void {
458-
const permission = `http://${wxt.config.server?.hostname ?? ''}/*`;
459-
const allowedCsp = wxt.config.server?.origin ?? 'http://localhost:*';
458+
const permission = `http://${wxt.server?.hostname ?? ''}/*`;
459+
const allowedCsp = wxt.server?.origin ?? 'http://localhost:*';
460460

461461
if (manifest.manifest_version === 3) {
462462
addHostPermission(manifest, permission);
@@ -473,7 +473,7 @@ function addDevModeCsp(manifest: Manifest.WebExtensionManifest): void {
473473
"script-src 'self'; object-src 'self';", // default CSP for MV2
474474
);
475475

476-
if (wxt.config.server) csp.add('script-src', allowedCsp);
476+
if (wxt.server) csp.add('script-src', allowedCsp);
477477

478478
if (manifest.manifest_version === 3) {
479479
manifest.content_security_policy ??= {};

0 commit comments

Comments
 (0)