Skip to content

Commit

Permalink
fix: Import entrypoint improvements (#1207)
Browse files Browse the repository at this point in the history
  • Loading branch information
aklinker1 authored Dec 8, 2024
1 parent 1dc7c34 commit f7f03e2
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 275 deletions.
121 changes: 86 additions & 35 deletions packages/wxt/src/core/builders/vite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { importEntrypointFile } from '../../utils/building';
import { ViteNodeServer } from 'vite-node/server';
import { ViteNodeRunner } from 'vite-node/client';
import { installSourcemapsSupport } from 'vite-node/source-map';
import { createExtensionEnvironment } from '../../utils/environments';
import { relative } from 'node:path';

export async function createViteBuilder(
wxtConfig: ResolvedConfig,
Expand Down Expand Up @@ -220,55 +222,104 @@ export async function createViteBuilder(
};
};

const createViteNodeImporter = async (paths: string[]) => {
const baseConfig = await getBaseConfig({
excludeAnalysisPlugin: true,
});
// Disable dep optimization, as recommended by vite-node's README
baseConfig.optimizeDeps ??= {};
baseConfig.optimizeDeps.noDiscovery = true;
baseConfig.optimizeDeps.include = [];
const envConfig: vite.InlineConfig = {
plugins: paths.map((path) =>
wxtPlugins.removeEntrypointMainFunction(wxtConfig, path),
),
};
const config = vite.mergeConfig(baseConfig, envConfig);
const server = await vite.createServer(config);
await server.pluginContainer.buildStart({});
const node = new ViteNodeServer(
// @ts-ignore: Some weird type error...
server,
);
installSourcemapsSupport({
getSourceMap: (source) => node.getSourceMap(source),
});
const runner = new ViteNodeRunner({
root: server.config.root,
base: server.config.base,
// when having the server and runner in a different context,
// you will need to handle the communication between them
// and pass to this function
fetchModule(id) {
return node.fetchModule(id);
},
resolveId(id, importer) {
return node.resolveId(id, importer);
},
});
return { runner, server };
};

const requireDefaultExport = (path: string, mod: any) => {
const relativePath = relative(wxtConfig.root, path);
if (mod?.default == null) {
const defineFn = relativePath.includes('.content')
? 'defineContentScript'
: relativePath.includes('background')
? 'defineBackground'
: 'defineUnlistedScript';

throw Error(
`${relativePath}: Default export not found, did you forget to call "export default ${defineFn}(...)"?`,
);
}
};

return {
name: 'Vite',
version: vite.version,
async importEntrypoint(path) {
const env = createExtensionEnvironment();
switch (wxtConfig.entrypointLoader) {
default:
case 'jiti': {
return await importEntrypointFile(path);
return await env.run(() => importEntrypointFile(path));
}
case 'vite-node': {
const baseConfig = await getBaseConfig({
excludeAnalysisPlugin: true,
});
// Disable dep optimization, as recommended by vite-node's README
baseConfig.optimizeDeps ??= {};
baseConfig.optimizeDeps.noDiscovery = true;
baseConfig.optimizeDeps.include = [];
const envConfig: vite.InlineConfig = {
plugins: [wxtPlugins.removeEntrypointMainFunction(wxtConfig, path)],
};
const config = vite.mergeConfig(baseConfig, envConfig);
const server = await vite.createServer(config);
await server.pluginContainer.buildStart({});
const node = new ViteNodeServer(
// @ts-ignore: Some weird type error...
server,
);
installSourcemapsSupport({
getSourceMap: (source) => node.getSourceMap(source),
});
const runner = new ViteNodeRunner({
root: server.config.root,
base: server.config.base,
// when having the server and runner in a different context,
// you will need to handle the communication between them
// and pass to this function
fetchModule(id) {
return node.fetchModule(id);
},
resolveId(id, importer) {
return node.resolveId(id, importer);
},
});
const res = await runner.executeFile(path);
const { runner, server } = await createViteNodeImporter([path]);
const res = await env.run(() => runner.executeFile(path));
await server.close();
requireDefaultExport(path, res);
return res.default;
}
}
},
async importEntrypoints(paths) {
const env = createExtensionEnvironment();
switch (wxtConfig.entrypointLoader) {
default:
case 'jiti': {
return await env.run(() =>
Promise.all(paths.map(importEntrypointFile)),
);
}
case 'vite-node': {
const { runner, server } = await createViteNodeImporter(paths);
const res = await env.run(() =>
Promise.all(
paths.map(async (path) => {
const mod = await runner.executeFile(path);
requireDefaultExport(path, mod);
return mod.default;
}),
),
);
await server.close();
return res;
}
}
},
async build(group) {
let entryConfig;
if (Array.isArray(group)) entryConfig = getMultiPageConfig(group);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ describe('findEntrypoints', () => {
outDir: resolve('.output'),
command: 'build',
});
let importEntrypointMock: Mock;
let importEntrypointsMock: Mock<typeof wxt.builder.importEntrypoints>;

beforeEach(() => {
setFakeWxt({ config });
importEntrypointMock = vi.mocked(wxt.builder.importEntrypoint);
importEntrypointsMock = vi.mocked(wxt.builder.importEntrypoints);
importEntrypointsMock.mockResolvedValue([]);
});

it.each<[string, string, PopupEntrypoint]>([
Expand Down Expand Up @@ -210,13 +211,13 @@ describe('findEntrypoints', () => {
matches: ['<all_urls>'],
};
globMock.mockResolvedValueOnce([path]);
importEntrypointMock.mockResolvedValue(options);
importEntrypointsMock.mockResolvedValue([options]);

const entrypoints = await findEntrypoints();

expect(entrypoints).toHaveLength(1);
expect(entrypoints[0]).toEqual({ ...expected, options });
expect(importEntrypointMock).toBeCalledWith(expected.inputPath);
expect(importEntrypointsMock).toBeCalledWith([expected.inputPath]);
},
);

Expand Down Expand Up @@ -244,17 +245,17 @@ describe('findEntrypoints', () => {
])(
'should find and load background entrypoint config from %s',
async (path, expected) => {
const options: BackgroundEntrypointOptions = {
const options = {
type: 'module',
};
} satisfies BackgroundEntrypointOptions;
globMock.mockResolvedValueOnce([path]);
importEntrypointMock.mockResolvedValue(options);
importEntrypointsMock.mockResolvedValue([options]);

const entrypoints = await findEntrypoints();

expect(entrypoints).toHaveLength(1);
expect(entrypoints[0]).toEqual({ ...expected, options });
expect(importEntrypointMock).toBeCalledWith(expected.inputPath);
expect(importEntrypointsMock).toBeCalledWith([expected.inputPath]);
},
);

Expand Down Expand Up @@ -339,11 +340,11 @@ describe('findEntrypoints', () => {
},
builder: wxt.builder,
});
const options: BackgroundEntrypointOptions = {
const options = {
type: 'module',
};
} satisfies BackgroundEntrypointOptions;
globMock.mockResolvedValueOnce(['background.ts']);
importEntrypointMock.mockResolvedValue(options);
importEntrypointsMock.mockResolvedValue([options]);

const entrypoints = await findEntrypoints();

Expand All @@ -357,11 +358,11 @@ describe('findEntrypoints', () => {
},
builder: wxt.builder,
});
const options: BackgroundEntrypointOptions = {
const options = {
type: 'module',
};
} satisfies BackgroundEntrypointOptions;
globMock.mockResolvedValueOnce(['background.ts']);
importEntrypointMock.mockResolvedValue(options);
importEntrypointsMock.mockResolvedValue([options]);

const entrypoints = await findEntrypoints();

Expand Down Expand Up @@ -410,15 +411,15 @@ describe('findEntrypoints', () => {
outputDir: config.outDir,
skipped: false,
};
const options: BaseEntrypointOptions = {};
const options = {} satisfies BaseEntrypointOptions;
globMock.mockResolvedValueOnce([path]);
importEntrypointMock.mockResolvedValue(options);
importEntrypointsMock.mockResolvedValue([options]);

const entrypoints = await findEntrypoints();

expect(entrypoints).toHaveLength(1);
expect(entrypoints[0]).toEqual({ ...expected, options });
expect(importEntrypointMock).toBeCalledWith(expected.inputPath);
expect(importEntrypointsMock).toBeCalledWith([expected.inputPath]);
},
);

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

const entrypoints = await findEntrypoints();

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

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

const entrypoints = await findEntrypoints();

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

const entrypoints = await findEntrypoints();

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

it('should mark content scripts as skipped when exclude contains the target browser', async () => {
globMock.mockResolvedValueOnce(['example.content.ts']);
importEntrypointMock.mockResolvedValue({
exclude: [config.browser],
});
importEntrypointsMock.mockResolvedValue([{ exclude: [config.browser] }]);

const entrypoints = await findEntrypoints();

Expand Down Expand Up @@ -914,7 +911,7 @@ describe('findEntrypoints', () => {
builder: wxt.builder,
});

importEntrypointMock.mockResolvedValue({});
importEntrypointsMock.mockResolvedValue([{}]);

const entrypoints = await findEntrypoints();

Expand Down
Loading

0 comments on commit f7f03e2

Please sign in to comment.