Skip to content

Commit f7f03e2

Browse files
authored
fix: Import entrypoint improvements (#1207)
1 parent 1dc7c34 commit f7f03e2

File tree

5 files changed

+324
-275
lines changed

5 files changed

+324
-275
lines changed

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

+86-35
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import { importEntrypointFile } from '../../utils/building';
2424
import { ViteNodeServer } from 'vite-node/server';
2525
import { ViteNodeRunner } from 'vite-node/client';
2626
import { installSourcemapsSupport } from 'vite-node/source-map';
27+
import { createExtensionEnvironment } from '../../utils/environments';
28+
import { relative } from 'node:path';
2729

2830
export async function createViteBuilder(
2931
wxtConfig: ResolvedConfig,
@@ -220,55 +222,104 @@ export async function createViteBuilder(
220222
};
221223
};
222224

225+
const createViteNodeImporter = async (paths: string[]) => {
226+
const baseConfig = await getBaseConfig({
227+
excludeAnalysisPlugin: true,
228+
});
229+
// Disable dep optimization, as recommended by vite-node's README
230+
baseConfig.optimizeDeps ??= {};
231+
baseConfig.optimizeDeps.noDiscovery = true;
232+
baseConfig.optimizeDeps.include = [];
233+
const envConfig: vite.InlineConfig = {
234+
plugins: paths.map((path) =>
235+
wxtPlugins.removeEntrypointMainFunction(wxtConfig, path),
236+
),
237+
};
238+
const config = vite.mergeConfig(baseConfig, envConfig);
239+
const server = await vite.createServer(config);
240+
await server.pluginContainer.buildStart({});
241+
const node = new ViteNodeServer(
242+
// @ts-ignore: Some weird type error...
243+
server,
244+
);
245+
installSourcemapsSupport({
246+
getSourceMap: (source) => node.getSourceMap(source),
247+
});
248+
const runner = new ViteNodeRunner({
249+
root: server.config.root,
250+
base: server.config.base,
251+
// when having the server and runner in a different context,
252+
// you will need to handle the communication between them
253+
// and pass to this function
254+
fetchModule(id) {
255+
return node.fetchModule(id);
256+
},
257+
resolveId(id, importer) {
258+
return node.resolveId(id, importer);
259+
},
260+
});
261+
return { runner, server };
262+
};
263+
264+
const requireDefaultExport = (path: string, mod: any) => {
265+
const relativePath = relative(wxtConfig.root, path);
266+
if (mod?.default == null) {
267+
const defineFn = relativePath.includes('.content')
268+
? 'defineContentScript'
269+
: relativePath.includes('background')
270+
? 'defineBackground'
271+
: 'defineUnlistedScript';
272+
273+
throw Error(
274+
`${relativePath}: Default export not found, did you forget to call "export default ${defineFn}(...)"?`,
275+
);
276+
}
277+
};
278+
223279
return {
224280
name: 'Vite',
225281
version: vite.version,
226282
async importEntrypoint(path) {
283+
const env = createExtensionEnvironment();
227284
switch (wxtConfig.entrypointLoader) {
228285
default:
229286
case 'jiti': {
230-
return await importEntrypointFile(path);
287+
return await env.run(() => importEntrypointFile(path));
231288
}
232289
case 'vite-node': {
233-
const baseConfig = await getBaseConfig({
234-
excludeAnalysisPlugin: true,
235-
});
236-
// Disable dep optimization, as recommended by vite-node's README
237-
baseConfig.optimizeDeps ??= {};
238-
baseConfig.optimizeDeps.noDiscovery = true;
239-
baseConfig.optimizeDeps.include = [];
240-
const envConfig: vite.InlineConfig = {
241-
plugins: [wxtPlugins.removeEntrypointMainFunction(wxtConfig, path)],
242-
};
243-
const config = vite.mergeConfig(baseConfig, envConfig);
244-
const server = await vite.createServer(config);
245-
await server.pluginContainer.buildStart({});
246-
const node = new ViteNodeServer(
247-
// @ts-ignore: Some weird type error...
248-
server,
249-
);
250-
installSourcemapsSupport({
251-
getSourceMap: (source) => node.getSourceMap(source),
252-
});
253-
const runner = new ViteNodeRunner({
254-
root: server.config.root,
255-
base: server.config.base,
256-
// when having the server and runner in a different context,
257-
// you will need to handle the communication between them
258-
// and pass to this function
259-
fetchModule(id) {
260-
return node.fetchModule(id);
261-
},
262-
resolveId(id, importer) {
263-
return node.resolveId(id, importer);
264-
},
265-
});
266-
const res = await runner.executeFile(path);
290+
const { runner, server } = await createViteNodeImporter([path]);
291+
const res = await env.run(() => runner.executeFile(path));
267292
await server.close();
293+
requireDefaultExport(path, res);
268294
return res.default;
269295
}
270296
}
271297
},
298+
async importEntrypoints(paths) {
299+
const env = createExtensionEnvironment();
300+
switch (wxtConfig.entrypointLoader) {
301+
default:
302+
case 'jiti': {
303+
return await env.run(() =>
304+
Promise.all(paths.map(importEntrypointFile)),
305+
);
306+
}
307+
case 'vite-node': {
308+
const { runner, server } = await createViteNodeImporter(paths);
309+
const res = await env.run(() =>
310+
Promise.all(
311+
paths.map(async (path) => {
312+
const mod = await runner.executeFile(path);
313+
requireDefaultExport(path, mod);
314+
return mod.default;
315+
}),
316+
),
317+
);
318+
await server.close();
319+
return res;
320+
}
321+
}
322+
},
272323
async build(group) {
273324
let entryConfig;
274325
if (Array.isArray(group)) entryConfig = getMultiPageConfig(group);

Diff for: packages/wxt/src/core/utils/building/__tests__/find-entrypoints.test.ts

+27-30
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ describe('findEntrypoints', () => {
3636
outDir: resolve('.output'),
3737
command: 'build',
3838
});
39-
let importEntrypointMock: Mock;
39+
let importEntrypointsMock: Mock<typeof wxt.builder.importEntrypoints>;
4040

4141
beforeEach(() => {
4242
setFakeWxt({ config });
43-
importEntrypointMock = vi.mocked(wxt.builder.importEntrypoint);
43+
importEntrypointsMock = vi.mocked(wxt.builder.importEntrypoints);
44+
importEntrypointsMock.mockResolvedValue([]);
4445
});
4546

4647
it.each<[string, string, PopupEntrypoint]>([
@@ -210,13 +211,13 @@ describe('findEntrypoints', () => {
210211
matches: ['<all_urls>'],
211212
};
212213
globMock.mockResolvedValueOnce([path]);
213-
importEntrypointMock.mockResolvedValue(options);
214+
importEntrypointsMock.mockResolvedValue([options]);
214215

215216
const entrypoints = await findEntrypoints();
216217

217218
expect(entrypoints).toHaveLength(1);
218219
expect(entrypoints[0]).toEqual({ ...expected, options });
219-
expect(importEntrypointMock).toBeCalledWith(expected.inputPath);
220+
expect(importEntrypointsMock).toBeCalledWith([expected.inputPath]);
220221
},
221222
);
222223

@@ -244,17 +245,17 @@ describe('findEntrypoints', () => {
244245
])(
245246
'should find and load background entrypoint config from %s',
246247
async (path, expected) => {
247-
const options: BackgroundEntrypointOptions = {
248+
const options = {
248249
type: 'module',
249-
};
250+
} satisfies BackgroundEntrypointOptions;
250251
globMock.mockResolvedValueOnce([path]);
251-
importEntrypointMock.mockResolvedValue(options);
252+
importEntrypointsMock.mockResolvedValue([options]);
252253

253254
const entrypoints = await findEntrypoints();
254255

255256
expect(entrypoints).toHaveLength(1);
256257
expect(entrypoints[0]).toEqual({ ...expected, options });
257-
expect(importEntrypointMock).toBeCalledWith(expected.inputPath);
258+
expect(importEntrypointsMock).toBeCalledWith([expected.inputPath]);
258259
},
259260
);
260261

@@ -339,11 +340,11 @@ describe('findEntrypoints', () => {
339340
},
340341
builder: wxt.builder,
341342
});
342-
const options: BackgroundEntrypointOptions = {
343+
const options = {
343344
type: 'module',
344-
};
345+
} satisfies BackgroundEntrypointOptions;
345346
globMock.mockResolvedValueOnce(['background.ts']);
346-
importEntrypointMock.mockResolvedValue(options);
347+
importEntrypointsMock.mockResolvedValue([options]);
347348

348349
const entrypoints = await findEntrypoints();
349350

@@ -357,11 +358,11 @@ describe('findEntrypoints', () => {
357358
},
358359
builder: wxt.builder,
359360
});
360-
const options: BackgroundEntrypointOptions = {
361+
const options = {
361362
type: 'module',
362-
};
363+
} satisfies BackgroundEntrypointOptions;
363364
globMock.mockResolvedValueOnce(['background.ts']);
364-
importEntrypointMock.mockResolvedValue(options);
365+
importEntrypointsMock.mockResolvedValue([options]);
365366

366367
const entrypoints = await findEntrypoints();
367368

@@ -410,15 +411,15 @@ describe('findEntrypoints', () => {
410411
outputDir: config.outDir,
411412
skipped: false,
412413
};
413-
const options: BaseEntrypointOptions = {};
414+
const options = {} satisfies BaseEntrypointOptions;
414415
globMock.mockResolvedValueOnce([path]);
415-
importEntrypointMock.mockResolvedValue(options);
416+
importEntrypointsMock.mockResolvedValue([options]);
416417

417418
const entrypoints = await findEntrypoints();
418419

419420
expect(entrypoints).toHaveLength(1);
420421
expect(entrypoints[0]).toEqual({ ...expected, options });
421-
expect(importEntrypointMock).toBeCalledWith(expected.inputPath);
422+
expect(importEntrypointsMock).toBeCalledWith([expected.inputPath]);
422423
},
423424
);
424425

@@ -703,9 +704,9 @@ describe('findEntrypoints', () => {
703704
describe('include option', () => {
704705
it("should mark the background as skipped when include doesn't contain the target browser", async () => {
705706
globMock.mockResolvedValueOnce(['background.ts']);
706-
importEntrypointMock.mockResolvedValue({
707-
include: ['not' + config.browser],
708-
});
707+
importEntrypointsMock.mockResolvedValue([
708+
{ include: ['not' + config.browser] },
709+
]);
709710

710711
const entrypoints = await findEntrypoints();
711712

@@ -719,9 +720,9 @@ describe('findEntrypoints', () => {
719720

720721
it("should mark content scripts as skipped when include doesn't contain the target browser", async () => {
721722
globMock.mockResolvedValueOnce(['example.content.ts']);
722-
importEntrypointMock.mockResolvedValue({
723-
include: ['not' + config.browser],
724-
});
723+
importEntrypointsMock.mockResolvedValue([
724+
{ include: ['not' + config.browser] },
725+
]);
725726

726727
const entrypoints = await findEntrypoints();
727728

@@ -803,9 +804,7 @@ describe('findEntrypoints', () => {
803804
describe('exclude option', () => {
804805
it('should mark the background as skipped when exclude contains the target browser', async () => {
805806
globMock.mockResolvedValueOnce(['background.ts']);
806-
importEntrypointMock.mockResolvedValue({
807-
exclude: [config.browser],
808-
});
807+
importEntrypointsMock.mockResolvedValue([{ exclude: [config.browser] }]);
809808

810809
const entrypoints = await findEntrypoints();
811810

@@ -819,9 +818,7 @@ describe('findEntrypoints', () => {
819818

820819
it('should mark content scripts as skipped when exclude contains the target browser', async () => {
821820
globMock.mockResolvedValueOnce(['example.content.ts']);
822-
importEntrypointMock.mockResolvedValue({
823-
exclude: [config.browser],
824-
});
821+
importEntrypointsMock.mockResolvedValue([{ exclude: [config.browser] }]);
825822

826823
const entrypoints = await findEntrypoints();
827824

@@ -914,7 +911,7 @@ describe('findEntrypoints', () => {
914911
builder: wxt.builder,
915912
});
916913

917-
importEntrypointMock.mockResolvedValue({});
914+
importEntrypointsMock.mockResolvedValue([{}]);
918915

919916
const entrypoints = await findEntrypoints();
920917

0 commit comments

Comments
 (0)