From 65dfded68295e773a4661322f90d8cd2d9ec4fdb Mon Sep 17 00:00:00 2001 From: Aaron Date: Sat, 19 Oct 2024 05:44:54 -0500 Subject: [PATCH] fix!: Remove deprecated `jiti` entrypoint loader (#1087) --- .../essentials/config/entrypoint-loaders.md | 60 --------- .../wxt/e2e/tests/manifest-content.test.ts | 47 +++---- packages/wxt/package.json | 1 - packages/wxt/src/core/builders/vite/index.ts | 53 +++----- packages/wxt/src/core/resolve-config.ts | 1 - .../__tests__/import-entrypoint.test.ts | 52 ------- .../core/utils/building/import-entrypoint.ts | 127 ------------------ packages/wxt/src/core/utils/building/index.ts | 1 - packages/wxt/src/core/utils/strings.ts | 11 -- .../src/core/utils/testing/fake-objects.ts | 1 - packages/wxt/src/types.ts | 15 --- pnpm-lock.yaml | 3 - 12 files changed, 39 insertions(+), 333 deletions(-) delete mode 100644 packages/wxt/src/core/utils/building/__tests__/import-entrypoint.test.ts delete mode 100644 packages/wxt/src/core/utils/building/import-entrypoint.ts diff --git a/docs/guide/essentials/config/entrypoint-loaders.md b/docs/guide/essentials/config/entrypoint-loaders.md index a61708b4..c33c870a 100644 --- a/docs/guide/essentials/config/entrypoint-loaders.md +++ b/docs/guide/essentials/config/entrypoint-loaders.md @@ -17,63 +17,3 @@ If you're running into errors while importing entrypoints, run `wxt prepare --de ::: Once the environment has been polyfilled and your code pre-processed, it's up the entrypoint loader to import your code, extracting the options from the default export. - -There are two options for loading your entrypoints: - -1. `vite-node` - default as of `v0.19.0` -2. `jiti` (**DEPRECATED, will be removed in `v0.20.0`**) - Default before `v0.19.0` - -## vite-node - -Since 0.19.0, WXT uses `vite-node`, the same tool that powers Vitest and Nuxt, to import your entrypoint files. It re-uses the same vite config used when building your extension, making it the most stable entrypoint loader. - -## jiti - -To enable `jiti`: - -```ts -export default defineConfig({ - entrypointLoader: 'jiti', -}); -``` - -This is the original method WXT used to import TS files. However, because it doesn't support vite plugins like `vite-node`, it does one additional pre-processing step: It removes **_ALL_** imports from your code. - -That means you cannot use imported variables outside the `main` function in JS entrypoints, like for content script `matches` or other options: - -```ts -// entrypoints/content.ts -import { GOOGLE_MATCHES } from '~/utils/match-patterns'; - -export default defineContentScript({ - matches: GOOGLE_MATCHES, - main() { - // ... - }, -}); -``` - -``` -$ wxt build -wxt build - -WXT 0.14.1 -ℹ Building chrome-mv3 for production with Vite 5.0.5 -✖ Command failed after 360 ms - -[8:55:54 AM] ERROR entrypoints/content.ts: Cannot use imported variable "GOOGLE_MATCHES" before main function. -``` - -Usually, this error occurs when you try to extract options into a shared file or when running code outside the `main` function. To fix the example from above, use literal values when defining an entrypoint instead of importing them: - -```ts -import { GOOGLE_MATCHES } from '~/utils/match-patterns'; // [!code --] - -export default defineContentScript({ - matches: GOOGLE_MATCHES, // [!code --] - matches: ['*//*.google.com/*'], // [!code ++] - main() { - // ... - }, -}); -``` diff --git a/packages/wxt/e2e/tests/manifest-content.test.ts b/packages/wxt/e2e/tests/manifest-content.test.ts index fcbd97ce..b9b68f35 100644 --- a/packages/wxt/e2e/tests/manifest-content.test.ts +++ b/packages/wxt/e2e/tests/manifest-content.test.ts @@ -1,36 +1,33 @@ import { describe, it, expect } from 'vitest'; import { TestProject } from '../utils'; -describe.each(['vite-node', 'jiti'] as const)( - 'Manifest Content (Vite runtime? %s)', - (entrypointImporter) => { - it.each([ - { browser: undefined, outDir: 'chrome-mv3', expected: undefined }, - { browser: 'chrome', outDir: 'chrome-mv3', expected: undefined }, - { browser: 'firefox', outDir: 'firefox-mv2', expected: true }, - { browser: 'safari', outDir: 'safari-mv2', expected: false }, - ])( - 'should respect the per-browser entrypoint option with %j', - async ({ browser, expected, outDir }) => { - const project = new TestProject(); +describe('Manifest Content', () => { + it.each([ + { browser: undefined, outDir: 'chrome-mv3', expected: undefined }, + { browser: 'chrome', outDir: 'chrome-mv3', expected: undefined }, + { browser: 'firefox', outDir: 'firefox-mv2', expected: true }, + { browser: 'safari', outDir: 'safari-mv2', expected: false }, + ])( + 'should respect the per-browser entrypoint option with %j', + async ({ browser, expected, outDir }) => { + const project = new TestProject(); - project.addFile( - 'entrypoints/background.ts', - `export default defineBackground({ + project.addFile( + 'entrypoints/background.ts', + `export default defineBackground({ persistent: { firefox: true, safari: false, }, main: () => {}, })`, - ); - await project.build({ browser, experimental: { entrypointImporter } }); + ); + await project.build({ browser }); - const safariManifest = await project.getOutputManifest( - `.output/${outDir}/manifest.json`, - ); - expect(safariManifest.background.persistent).toBe(expected); - }, - ); - }, -); + const safariManifest = await project.getOutputManifest( + `.output/${outDir}/manifest.json`, + ); + expect(safariManifest.background.persistent).toBe(expected); + }, + ); +}); diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 696ac38e..dc9c264a 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -100,7 +100,6 @@ "giget": "^1.2.3", "hookable": "^5.5.3", "is-wsl": "^3.1.0", - "jiti": "^1.21.6", "json5": "^2.2.3", "jszip": "^3.10.1", "linkedom": "^0.18.5", diff --git a/packages/wxt/src/core/builders/vite/index.ts b/packages/wxt/src/core/builders/vite/index.ts index fa1eaaa1..13e9ddca 100644 --- a/packages/wxt/src/core/builders/vite/index.ts +++ b/packages/wxt/src/core/builders/vite/index.ts @@ -20,7 +20,6 @@ import { import { Hookable } from 'hookable'; import { toArray } from '../../utils/arrays'; import { safeVarName } from '../../utils/strings'; -import { importEntrypointFile } from '../../utils/building'; import { ViteNodeServer } from 'vite-node/server'; import { ViteNodeRunner } from 'vite-node/client'; import { installSourcemapsSupport } from 'vite-node/source-map'; @@ -280,44 +279,26 @@ export async function createViteBuilder( version: vite.version, async importEntrypoint(path) { const env = createExtensionEnvironment(); - switch (wxtConfig.entrypointLoader) { - default: - case 'jiti': { - return await env.run(() => importEntrypointFile(path)); - } - case 'vite-node': { - const { runner, server } = await createViteNodeImporter([path]); - const res = await env.run(() => runner.executeFile(path)); - await server.close(); - requireDefaultExport(path, res); - return res.default; - } - } + 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; - } - } + 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; diff --git a/packages/wxt/src/core/resolve-config.ts b/packages/wxt/src/core/resolve-config.ts index 946f6aaf..7b4499f1 100644 --- a/packages/wxt/src/core/resolve-config.ts +++ b/packages/wxt/src/core/resolve-config.ts @@ -201,7 +201,6 @@ export async function resolveConfig( analysis: resolveAnalysisConfig(root, mergedConfig), userConfigMetadata: userConfigMetadata ?? {}, alias, - entrypointLoader: mergedConfig.entrypointLoader ?? 'vite-node', experimental: defu(mergedConfig.experimental, {}), dev: { server: devServerConfig, diff --git a/packages/wxt/src/core/utils/building/__tests__/import-entrypoint.test.ts b/packages/wxt/src/core/utils/building/__tests__/import-entrypoint.test.ts deleted file mode 100644 index 2f757199..00000000 --- a/packages/wxt/src/core/utils/building/__tests__/import-entrypoint.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; -import { importEntrypointFile } from '../../../utils/building'; -import { resolve } from 'node:path'; -import { setFakeWxt } from '../../../utils/testing/fake-objects'; - -const entrypointPath = (filename: string) => - resolve(__dirname, 'test-entrypoints', filename); - -describe('importEntrypointFile', () => { - beforeEach(() => { - setFakeWxt({ - config: { - imports: false, - debug: false, - // Run inside the demo folder so that wxt is in the node_modules - // WXT must also be built for these tests to pass - root: 'demo', - }, - }); - }); - - it.each([ - ['background.ts', { main: expect.any(Function) }], - ['content.ts', { main: expect.any(Function), matches: [''] }], - ['unlisted.ts', { main: expect.any(Function) }], - ['react.tsx', { main: expect.any(Function) }], - ['with-named.ts', { main: expect.any(Function) }], - ])( - 'should return the default export of test-entrypoints/%s', - async (file, expected) => { - const actual = await importEntrypointFile(entrypointPath(file)); - - expect(actual).toEqual(expected); - }, - ); - - it('should return undefined when there is no default export', async () => { - const actual = await importEntrypointFile( - entrypointPath('no-default-export.ts'), - ); - - expect(actual).toBeUndefined(); - }); - - it('should throw a custom error message when an imported variable is used before main', async () => { - await expect(() => - importEntrypointFile(entrypointPath('imported-option.ts')), - ).rejects.toThrowError( - `imported-option.ts: Cannot use imported variable "faker" outside the main function.`, - ); - }); -}); diff --git a/packages/wxt/src/core/utils/building/import-entrypoint.ts b/packages/wxt/src/core/utils/building/import-entrypoint.ts deleted file mode 100644 index d2a029f4..00000000 --- a/packages/wxt/src/core/utils/building/import-entrypoint.ts +++ /dev/null @@ -1,127 +0,0 @@ -import createJITI, { TransformOptions as JitiTransformOptions } from 'jiti'; -import { createUnimport } from 'unimport'; -import fs from 'fs-extra'; -import { relative, resolve } from 'node:path'; -import { removeProjectImportStatements } from '../../utils/strings'; -import { normalizePath } from '../../utils/paths'; -import { TransformOptions, transformSync } from 'esbuild'; -import { fileURLToPath } from 'node:url'; -import { wxt } from '../../wxt'; - -/** - * Get the value from the default export of a `path`. - * - * It works by: - * - * 1. Reading the file text - * 2. Stripping all imports from it via regex - * 3. Auto-import only the client helper functions - * - * This prevents resolving imports of imports, speeding things up and preventing "xxx is not - * defined" errors. - * - * Downside is that code cannot be executed outside of the main fucntion for the entrypoint, - * otherwise you will see "xxx is not defined" errors for any imports used outside of main function. - */ -export async function importEntrypointFile(path: string): Promise { - wxt.logger.debug('Loading file metadata:', path); - // JITI & Babel uses normalized paths. - const normalPath = normalizePath(path); - - const unimport = createUnimport({ - ...wxt.config.imports, - // Only allow specific imports, not all from the project - dirs: [], - }); - await unimport.init(); - - const text = await fs.readFile(path, 'utf-8'); - const textNoImports = removeProjectImportStatements(text); - const { code } = await unimport.injectImports(textNoImports); - wxt.logger.debug( - ['Text:', text, 'No imports:', textNoImports, 'Code:', code].join('\n'), - ); - - const jiti = createJITI( - typeof __filename !== 'undefined' - ? __filename - : fileURLToPath(import.meta.url), - { - cache: false, - debug: wxt.config.debug, - esmResolve: true, - alias: { - 'webextension-polyfill': resolve( - wxt.config.wxtModuleDir, - 'dist/virtual/mock-browser.mjs', - ), - // TODO: Resolve this virtual module to some file with - // `export default {}` instead of this hack of using another file with - // a default export. - 'virtual:app-config': resolve( - wxt.config.wxtModuleDir, - 'dist/virtual/mock-browser.mjs', - ), - }, - // Continue using node to load TS files even if `bun run --bun` is detected. Jiti does not - // respect the custom transform function when using it's native bun option. - experimentalBun: false, - // List of extensions to transform with esbuild - extensions: [ - '.ts', - '.cts', - '.mts', - '.tsx', - '.js', - '.cjs', - '.mjs', - '.jsx', - ], - transform(opts) { - const isEntrypoint = opts.filename === normalPath; - return transformSync( - // Use modified source code for entrypoints - isEntrypoint ? code : opts.source, - getEsbuildOptions(opts), - ); - }, - }, - ); - - try { - const res = await jiti(path); - return res.default; - } catch (err) { - const filePath = relative(wxt.config.root, path); - if (err instanceof ReferenceError) { - // "XXX is not defined" - usually due to WXT removing imports - const variableName = err.message.replace(' is not defined', ''); - throw Error( - `${filePath}: Cannot use imported variable "${variableName}" outside the main function. See https://wxt.dev/guide/go-further/entrypoint-side-effects.html`, - { cause: err }, - ); - } else { - wxt.logger.error(err); - throw Error(`Failed to load entrypoint: ${filePath}`, { cause: err }); - } - } -} - -function getEsbuildOptions(opts: JitiTransformOptions): TransformOptions { - const isJsx = opts.filename?.endsWith('x'); - return { - format: 'cjs', - loader: isJsx ? 'tsx' : 'ts', - define: { - 'import.meta.env.ENTRYPOINT': '"build"', - }, - ...(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), - }; -} diff --git a/packages/wxt/src/core/utils/building/index.ts b/packages/wxt/src/core/utils/building/index.ts index 72d74acd..de8fb0bb 100644 --- a/packages/wxt/src/core/utils/building/index.ts +++ b/packages/wxt/src/core/utils/building/index.ts @@ -2,6 +2,5 @@ export * from './build-entrypoints'; export * from './detect-dev-changes'; export * from './find-entrypoints'; export * from './group-entrypoints'; -export * from './import-entrypoint'; export * from './internal-build'; export * from './rebuild'; diff --git a/packages/wxt/src/core/utils/strings.ts b/packages/wxt/src/core/utils/strings.ts index b48b7995..bb3f264c 100644 --- a/packages/wxt/src/core/utils/strings.ts +++ b/packages/wxt/src/core/utils/strings.ts @@ -34,14 +34,3 @@ export function removeImportStatements(text: string): string { '', ); } - -/** - * Removes imports, ensuring that some of WXT's client imports are present, so that entrypoints can be parsed if auto-imports are disabled. - */ -export function removeProjectImportStatements(text: string): string { - const noImports = removeImportStatements(text); - - return `import { defineUnlistedScript, defineContentScript, defineBackground } from 'wxt/sandbox'; - -${noImports}`; -} diff --git a/packages/wxt/src/core/utils/testing/fake-objects.ts b/packages/wxt/src/core/utils/testing/fake-objects.ts index 81ab34a6..deffe862 100644 --- a/packages/wxt/src/core/utils/testing/fake-objects.ts +++ b/packages/wxt/src/core/utils/testing/fake-objects.ts @@ -296,7 +296,6 @@ export const fakeResolvedConfig = fakeObjectCreator(() => { transformManifest: () => {}, userConfigMetadata: {}, alias: {}, - entrypointLoader: 'vite-node', experimental: {}, dev: { reloadCommand: 'Alt+R', diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 07aa4b47..8fdaf841 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -326,20 +326,6 @@ export interface InlineConfig { * } */ alias?: Record; - /** - * @deprecated Will be removed in v0.20.0, please migrate to using `vite-node`, the new default. - * - * Method used to import entrypoint files during the build process to extract their options. - * - * - `"vite-node"` (default as of 0.19.0): Uses `vite-node` to import the entrypoints. Automatically includes vite config based on your wxt.config.ts file - * - `"jiti"`: Simplest and fastest, but doesn't allow using any imported variables outside the entrypoint's main function - * - * @see {@link https://wxt.dev/guide/go-further/entrypoint-importers.html|Entrypoint Importers} - * - * @default "vite-node" - * @since 0.19.0 - */ - entrypointLoader?: 'vite-node' | 'jiti'; /** * Experimental settings - use with caution. */ @@ -1368,7 +1354,6 @@ export interface ResolvedConfig { * Import aliases to absolute paths. */ alias: Record; - entrypointLoader: 'vite-node' | 'jiti'; experimental: {}; dev: { /** Only defined during dev command */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1e8fc08..86901c2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -399,9 +399,6 @@ importers: is-wsl: specifier: ^3.1.0 version: 3.1.0 - jiti: - specifier: ^1.21.6 - version: 1.21.6 json5: specifier: ^2.2.3 version: 2.2.3