From 2068710489fd4a5e139b39a6ca90d8cf6a796fa5 Mon Sep 17 00:00:00 2001 From: PatrykKuniczak Date: Fri, 24 Apr 2026 12:07:18 +0200 Subject: [PATCH 1/4] feat: Add support for generating ESLint 10 config files --- packages/wxt/src/builtin-modules/unimport.ts | 6 +++--- packages/wxt/src/types.ts | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/wxt/src/builtin-modules/unimport.ts b/packages/wxt/src/builtin-modules/unimport.ts index a0b72bc58..b058fac37 100644 --- a/packages/wxt/src/builtin-modules/unimport.ts +++ b/packages/wxt/src/builtin-modules/unimport.ts @@ -103,7 +103,7 @@ async function getImportsModuleEntry( async function getEslintConfigEntry( unimport: Unimport, - version: 8 | 9, + version: 8 | 9 | 10, options: WxtResolvedUnimportOptions, ): Promise { const globals = (await unimport.getImports()) @@ -116,7 +116,7 @@ async function getEslintConfigEntry( }, {}); if (version <= 8) return getEslint8ConfigEntry(options, globals); - else return getEslint9ConfigEntry(options, globals); + else return getEslint9PlusConfigEntry(options, globals); } export function getEslint8ConfigEntry( @@ -129,7 +129,7 @@ export function getEslint8ConfigEntry( }; } -export function getEslint9ConfigEntry( +export function getEslint9PlusConfigEntry( options: WxtResolvedUnimportOptions, globals: Record, ): WxtDirFileEntry { diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 46561536a..fe4e9eebf 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -1,5 +1,5 @@ import type * as vite from 'vite'; -import { UnimportOptions, Import } from 'unimport'; +import { Import, UnimportOptions } from 'unimport'; import { LogLevel } from 'consola'; import type { ContentScriptContext } from './utils/content-script-context'; import type { PluginVisualizerOptions } from '@aklinker1/rollup-plugin-visualizer'; @@ -1585,10 +1585,11 @@ export interface Eslintrc { * - `true`: Same as `8`. * - `8`: Generate a config file compatible with ESLint 8. * - `9`: Generate a config file compatible with ESLint 9. + * - `10`: Generate a config file compatible with ESLint 10. * * @default 'auto' */ - enabled?: false | true | 'auto' | 8 | 9; + enabled?: boolean | 'auto' | 8 | 9 | 10; /** * File path to save the generated eslint config. * From 27dcfe1cd79f2b73d9bb696bc16ae35b49e3fd4c Mon Sep 17 00:00:00 2001 From: PatrykKuniczak Date: Fri, 24 Apr 2026 12:49:30 +0200 Subject: [PATCH 2/4] feat: Create EslintSupportedVersions type --- packages/wxt/src/types.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index fe4e9eebf..a76614abb 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -1574,6 +1574,8 @@ export type EslintGlobalsPropValue = | 'writable' | 'writeable'; +type EslintSupportedVersions = 8 | 9 | 10; + export interface Eslintrc { /** * When true, generates a file that can be used by ESLint to know which @@ -1589,7 +1591,7 @@ export interface Eslintrc { * * @default 'auto' */ - enabled?: boolean | 'auto' | 8 | 9 | 10; + enabled?: boolean | 'auto' | EslintSupportedVersions; /** * File path to save the generated eslint config. * @@ -1605,7 +1607,7 @@ export interface Eslintrc { export interface ResolvedEslintrc { /** False if disabled, otherwise the major version of ESLint installed */ - enabled: false | 8 | 9; + enabled: false | EslintSupportedVersions; /** Absolute path */ filePath: string; globalsPropValue: EslintGlobalsPropValue; From 9346411b01e8a52e18fe6eee98de848d55fcd307 Mon Sep 17 00:00:00 2001 From: PatrykKuniczak Date: Tue, 28 Apr 2026 16:16:06 +0200 Subject: [PATCH 3/4] fix: change `EslintSupportedVersions` to `old` and `flat` const and fix references across project --- packages/wxt/e2e/tests/auto-imports.test.ts | 21 +++++++++--------- packages/wxt/src/builtin-modules/unimport.ts | 9 ++++---- packages/wxt/src/core/resolve-config.ts | 20 +++++++++-------- .../src/core/utils/testing/fake-objects.ts | 22 +++++++++---------- packages/wxt/src/types.ts | 11 +++++----- 5 files changed, 43 insertions(+), 40 deletions(-) diff --git a/packages/wxt/e2e/tests/auto-imports.test.ts b/packages/wxt/e2e/tests/auto-imports.test.ts index f1c305b54..5ee3c16f0 100644 --- a/packages/wxt/e2e/tests/auto-imports.test.ts +++ b/packages/wxt/e2e/tests/auto-imports.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest'; import { TestProject } from '../utils'; +import { EslintSupportedVersions } from '../../src'; describe('Auto Imports', () => { describe('imports: { ... }', () => { @@ -217,7 +218,7 @@ describe('Auto Imports', () => { }); describe('eslintrc', () => { - it('"enabled: true" should output a JSON config file compatible with ESlint 8', async () => { + it('"enabled: true" should output a JSON config file compatible with ESlint <= 8', async () => { const project = new TestProject(); project.addFile('entrypoints/popup.html', ``); @@ -234,14 +235,14 @@ describe('Auto Imports', () => { ).toMatchSnapshot(); }); - it('"enabled: 8" should output a JSON config file compatible with ESlint 8', async () => { + it('"enabled: "old" should output a JSON config file compatible with ESlint <= 8', async () => { const project = new TestProject(); project.addFile('entrypoints/popup.html', ``); await project.prepare({ imports: { eslintrc: { - enabled: 8, + enabled: 'old', }, }, }); @@ -251,14 +252,14 @@ describe('Auto Imports', () => { ).toMatchSnapshot(); }); - it('"enabled: 9" should output a flat config file compatible with ESlint 9', async () => { + it('"enabled: "flat" should output a flat config file compatible with ESlint >= 9', async () => { const project = new TestProject(); project.addFile('entrypoints/popup.html', ``); await project.prepare({ imports: { eslintrc: { - enabled: 9, + enabled: 'flat', }, }, }); @@ -308,7 +309,7 @@ describe('Auto Imports', () => { describe('Actual linting results', () => { async function runEslint( project: TestProject, - version: boolean | 'auto' | 8 | 9, + version: EslintSupportedVersions, ) { project.addFile( 'entrypoints/background.js', @@ -340,7 +341,7 @@ describe('Auto Imports', () => { `, ); - await expect(runEslint(project, 9)).rejects.toMatchObject({ + await expect(runEslint(project, 'flat')).rejects.toMatchObject({ exitCode: 1, stdout: expect.stringContaining( "'defineBackground' is not defined", @@ -367,7 +368,7 @@ describe('Auto Imports', () => { ]; `, ); - const res = await runEslint(project, 9); + const res = await runEslint(project, 'flat'); expect(res).toBeDefined(); }); @@ -389,7 +390,7 @@ describe('Auto Imports', () => { }), ); - await expect(runEslint(project, 8)).rejects.toMatchObject({ + await expect(runEslint(project, 'old')).rejects.toMatchObject({ exitCode: 1, stdout: expect.stringContaining( "'defineBackground' is not defined", @@ -414,7 +415,7 @@ describe('Auto Imports', () => { ], }), ); - const res = await runEslint(project, 8); + const res = await runEslint(project, 'old'); expect(res).toBeDefined(); }); diff --git a/packages/wxt/src/builtin-modules/unimport.ts b/packages/wxt/src/builtin-modules/unimport.ts index b058fac37..a75edc76c 100644 --- a/packages/wxt/src/builtin-modules/unimport.ts +++ b/packages/wxt/src/builtin-modules/unimport.ts @@ -1,12 +1,13 @@ import { addViteConfig, defineWxtModule } from '../modules'; -import type { +import { EslintGlobalsPropValue, + EslintSupportedVersions, Wxt, WxtDirFileEntry, WxtModule, WxtResolvedUnimportOptions, } from '../types'; -import { type Unimport, createUnimport, toExports } from 'unimport'; +import { createUnimport, toExports, type Unimport } from 'unimport'; import UnimportPlugin from 'unimport/unplugin'; export default defineWxtModule({ @@ -103,7 +104,7 @@ async function getImportsModuleEntry( async function getEslintConfigEntry( unimport: Unimport, - version: 8 | 9 | 10, + version: EslintSupportedVersions, options: WxtResolvedUnimportOptions, ): Promise { const globals = (await unimport.getImports()) @@ -115,7 +116,7 @@ async function getEslintConfigEntry( return globals; }, {}); - if (version <= 8) return getEslint8ConfigEntry(options, globals); + if (version == 'old') return getEslint8ConfigEntry(options, globals); else return getEslint9PlusConfigEntry(options, globals); } diff --git a/packages/wxt/src/core/resolve-config.ts b/packages/wxt/src/core/resolve-config.ts index 9e7903a76..1070a5f4d 100644 --- a/packages/wxt/src/core/resolve-config.ts +++ b/packages/wxt/src/core/resolve-config.ts @@ -1,19 +1,19 @@ import { loadConfig } from 'c12'; import { resolve as esmResolve } from 'import-meta-resolve'; import { + ConfigEnv, InlineConfig, + Logger, ResolvedConfig, + ResolvedEslintrc, UserConfig, - ConfigEnv, - UserManifestFn, UserManifest, + UserManifestFn, WebExtConfig, - WxtResolvedUnimportOptions, - Logger, WxtCommand, WxtModule, WxtModuleWithMetadata, - ResolvedEslintrc, + WxtResolvedUnimportOptions, } from '../types'; import path from 'node:path'; import { createFsCache } from './utils/cache'; @@ -516,13 +516,13 @@ async function getUnimportEslintOptions( const version = await getEslintVersion(); let major = parseInt(version[0]); if (isNaN(major)) enabled = false; - if (major <= 8) enabled = 8; - else if (major >= 9) enabled = 9; + if (major <= 8) enabled = 'old'; + else if (major >= 9) enabled = 'flat'; // NaN else enabled = false; break; case true: - enabled = 8; + enabled = 'old'; break; default: enabled = inlineEnabled; @@ -532,7 +532,9 @@ async function getUnimportEslintOptions( enabled, filePath: path.resolve( wxtDir, - enabled === 9 ? 'eslint-auto-imports.mjs' : 'eslintrc-auto-import.json', + enabled === 'flat' + ? 'eslint-auto-imports.mjs' + : 'eslintrc-auto-import.json', ), globalsPropValue: true, }; diff --git a/packages/wxt/src/core/utils/testing/fake-objects.ts b/packages/wxt/src/core/utils/testing/fake-objects.ts index 9a97e6660..1332196f9 100644 --- a/packages/wxt/src/core/utils/testing/fake-objects.ts +++ b/packages/wxt/src/core/utils/testing/fake-objects.ts @@ -3,23 +3,23 @@ import { resolve } from 'path'; import { faker } from '@faker-js/faker'; import merge from 'lodash.merge'; import { - FsCache, - ResolvedConfig, - WxtDevServer, BackgroundEntrypoint, + BaseEntrypoint, + BuildOutput, + BuildStepOutput, ContentScriptEntrypoint, + FsCache, GenericEntrypoint, OptionsEntrypoint, - PopupEntrypoint, - OutputChunk, OutputAsset, - BuildOutput, - BuildStepOutput, - UserManifest, - Wxt, + OutputChunk, + PopupEntrypoint, + ResolvedConfig, SidepanelEntrypoint, - BaseEntrypoint, UnlistedScriptEntrypoint, + UserManifest, + Wxt, + WxtDevServer, } from '../../../types'; import { mock } from 'vitest-mock-extended'; import { vi } from 'vitest'; @@ -243,7 +243,7 @@ export const fakeResolvedConfig = fakeObjectCreator(() => { imports: { disabled: faker.datatype.boolean(), eslintrc: { - enabled: faker.helpers.arrayElement([false, 8, 9]), + enabled: faker.helpers.arrayElement([false, 'old', 'flat']), filePath: fakeFile(), globalsPropValue: faker.helpers.arrayElement([ true, diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 677d0f8c6..593537be7 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -1578,7 +1578,7 @@ export type EslintGlobalsPropValue = | 'writable' | 'writeable'; -type EslintSupportedVersions = 8 | 9 | 10; +export type EslintSupportedVersions = 'old' | 'flat'; export interface Eslintrc { /** @@ -1589,9 +1589,8 @@ export interface Eslintrc { * - `'auto'`: Check if eslint is installed, and if it is, generate a compatible * config file. * - `true`: Same as `8`. - * - `8`: Generate a config file compatible with ESLint 8. - * - `9`: Generate a config file compatible with ESLint 9. - * - `10`: Generate a config file compatible with ESLint 10. + * - `old`: Generate a config file compatible with ESLint <e; 8. + * - `flat`: Generate a config file compatible with ESLint >e; 9. * * @default 'auto' */ @@ -1601,8 +1600,8 @@ export interface Eslintrc { * * Default depends on version of ESLint used: * - * - 9 and above: './.wxt/eslint-auto-imports.mjs' - * - 8 and below: './.wxt/eslintrc-auto-import.json' + * - >e; 9: './.wxt/eslint-auto-imports.mjs' + * - <e; 8: './.wxt/eslintrc-auto-import.json' */ filePath?: string; /** @default true */ From 466965c3c6dbd057856c38440ed3552a2700467b Mon Sep 17 00:00:00 2001 From: PatrykKuniczak Date: Tue, 28 Apr 2026 16:36:01 +0200 Subject: [PATCH 4/4] fix: build new auto-imports.test.ts.snap --- .../__snapshots__/auto-imports.test.ts.snap | 228 ++++++++++-------- 1 file changed, 121 insertions(+), 107 deletions(-) diff --git a/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap b/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap index 868663acb..247103811 100644 --- a/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap +++ b/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap @@ -1,62 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Auto Imports > eslintrc > "enabled: 8" should output a JSON config file compatible with ESlint 8 1`] = ` -".wxt/eslintrc-auto-import.json ----------------------------------------- -{ - "globals": { - "AutoMount": true, - "AutoMountOptions": true, - "Browser": true, - "ContentScriptAnchoredOptions": true, - "ContentScriptAppendMode": true, - "ContentScriptContext": true, - "ContentScriptInlinePositioningOptions": true, - "ContentScriptModalPositioningOptions": true, - "ContentScriptOverlayAlignment": true, - "ContentScriptOverlayPositioningOptions": true, - "ContentScriptPositioningOptions": true, - "ContentScriptUi": true, - "ContentScriptUiOptions": true, - "IframeContentScriptUi": true, - "IframeContentScriptUiOptions": true, - "InjectScriptOptions": true, - "IntegratedContentScriptUi": true, - "IntegratedContentScriptUiOptions": true, - "InvalidMatchPattern": true, - "MatchPattern": true, - "MigrationError": true, - "ScriptPublicPath": true, - "ShadowRootContentScriptUi": true, - "ShadowRootContentScriptUiOptions": true, - "StopAutoMount": true, - "StorageArea": true, - "StorageAreaChanges": true, - "StorageItemKey": true, - "WxtAppConfig": true, - "WxtStorage": true, - "WxtStorageItem": true, - "WxtWindowEventMap": true, - "browser": true, - "createIframeUi": true, - "createIntegratedUi": true, - "createShadowRootUi": true, - "defineAppConfig": true, - "defineBackground": true, - "defineContentScript": true, - "defineUnlistedScript": true, - "defineWxtPlugin": true, - "fakeBrowser": true, - "getAppConfig": true, - "injectScript": true, - "storage": true, - "useAppConfig": true - } -} -" -`; - -exports[`Auto Imports > eslintrc > "enabled: 9" should output a flat config file compatible with ESlint 9 1`] = ` +exports[`Auto Imports > eslintrc > "enabled: "flat" should output a flat config file compatible with ESlint >= 9 1`] = ` ".wxt/eslint-auto-imports.mjs ---------------------------------------- const globals = { @@ -119,7 +63,7 @@ export default { " `; -exports[`Auto Imports > eslintrc > "enabled: true" should output a JSON config file compatible with ESlint 8 1`] = ` +exports[`Auto Imports > eslintrc > "enabled: "old" should output a JSON config file compatible with ESlint <= 8 1`] = ` ".wxt/eslintrc-auto-import.json ---------------------------------------- { @@ -175,58 +119,128 @@ exports[`Auto Imports > eslintrc > "enabled: true" should output a JSON config f " `; +exports[`Auto Imports > eslintrc > "enabled: true" should output a JSON config file compatible with ESlint <= 8 1`] = ` +".wxt/eslintrc-auto-import.json +---------------------------------------- +const globals = { + "AutoMount": true, + "AutoMountOptions": true, + "Browser": true, + "ContentScriptAnchoredOptions": true, + "ContentScriptAppendMode": true, + "ContentScriptContext": true, + "ContentScriptInlinePositioningOptions": true, + "ContentScriptModalPositioningOptions": true, + "ContentScriptOverlayAlignment": true, + "ContentScriptOverlayPositioningOptions": true, + "ContentScriptPositioningOptions": true, + "ContentScriptUi": true, + "ContentScriptUiOptions": true, + "IframeContentScriptUi": true, + "IframeContentScriptUiOptions": true, + "InjectScriptOptions": true, + "IntegratedContentScriptUi": true, + "IntegratedContentScriptUiOptions": true, + "InvalidMatchPattern": true, + "MatchPattern": true, + "MigrationError": true, + "ScriptPublicPath": true, + "ShadowRootContentScriptUi": true, + "ShadowRootContentScriptUiOptions": true, + "StopAutoMount": true, + "StorageArea": true, + "StorageAreaChanges": true, + "StorageItemKey": true, + "WxtAppConfig": true, + "WxtStorage": true, + "WxtStorageItem": true, + "WxtWindowEventMap": true, + "browser": true, + "createIframeUi": true, + "createIntegratedUi": true, + "createShadowRootUi": true, + "defineAppConfig": true, + "defineBackground": true, + "defineContentScript": true, + "defineUnlistedScript": true, + "defineWxtPlugin": true, + "fakeBrowser": true, + "getAppConfig": true, + "injectScript": true, + "storage": true, + "useAppConfig": true +} + +export default { + name: "wxt/auto-imports", + languageOptions: { + globals, + /** @type {import('eslint').Linter.SourceType} */ + sourceType: "module", + }, +}; +" +`; + exports[`Auto Imports > eslintrc > should allow customizing the output 1`] = ` "example.json ---------------------------------------- -{ - "globals": { - "AutoMount": "readonly", - "AutoMountOptions": "readonly", - "Browser": "readonly", - "ContentScriptAnchoredOptions": "readonly", - "ContentScriptAppendMode": "readonly", - "ContentScriptContext": "readonly", - "ContentScriptInlinePositioningOptions": "readonly", - "ContentScriptModalPositioningOptions": "readonly", - "ContentScriptOverlayAlignment": "readonly", - "ContentScriptOverlayPositioningOptions": "readonly", - "ContentScriptPositioningOptions": "readonly", - "ContentScriptUi": "readonly", - "ContentScriptUiOptions": "readonly", - "IframeContentScriptUi": "readonly", - "IframeContentScriptUiOptions": "readonly", - "InjectScriptOptions": "readonly", - "IntegratedContentScriptUi": "readonly", - "IntegratedContentScriptUiOptions": "readonly", - "InvalidMatchPattern": "readonly", - "MatchPattern": "readonly", - "MigrationError": "readonly", - "ScriptPublicPath": "readonly", - "ShadowRootContentScriptUi": "readonly", - "ShadowRootContentScriptUiOptions": "readonly", - "StopAutoMount": "readonly", - "StorageArea": "readonly", - "StorageAreaChanges": "readonly", - "StorageItemKey": "readonly", - "WxtAppConfig": "readonly", - "WxtStorage": "readonly", - "WxtStorageItem": "readonly", - "WxtWindowEventMap": "readonly", - "browser": "readonly", - "createIframeUi": "readonly", - "createIntegratedUi": "readonly", - "createShadowRootUi": "readonly", - "defineAppConfig": "readonly", - "defineBackground": "readonly", - "defineContentScript": "readonly", - "defineUnlistedScript": "readonly", - "defineWxtPlugin": "readonly", - "fakeBrowser": "readonly", - "getAppConfig": "readonly", - "injectScript": "readonly", - "storage": "readonly", - "useAppConfig": "readonly" - } +const globals = { + "AutoMount": "readonly", + "AutoMountOptions": "readonly", + "Browser": "readonly", + "ContentScriptAnchoredOptions": "readonly", + "ContentScriptAppendMode": "readonly", + "ContentScriptContext": "readonly", + "ContentScriptInlinePositioningOptions": "readonly", + "ContentScriptModalPositioningOptions": "readonly", + "ContentScriptOverlayAlignment": "readonly", + "ContentScriptOverlayPositioningOptions": "readonly", + "ContentScriptPositioningOptions": "readonly", + "ContentScriptUi": "readonly", + "ContentScriptUiOptions": "readonly", + "IframeContentScriptUi": "readonly", + "IframeContentScriptUiOptions": "readonly", + "InjectScriptOptions": "readonly", + "IntegratedContentScriptUi": "readonly", + "IntegratedContentScriptUiOptions": "readonly", + "InvalidMatchPattern": "readonly", + "MatchPattern": "readonly", + "MigrationError": "readonly", + "ScriptPublicPath": "readonly", + "ShadowRootContentScriptUi": "readonly", + "ShadowRootContentScriptUiOptions": "readonly", + "StopAutoMount": "readonly", + "StorageArea": "readonly", + "StorageAreaChanges": "readonly", + "StorageItemKey": "readonly", + "WxtAppConfig": "readonly", + "WxtStorage": "readonly", + "WxtStorageItem": "readonly", + "WxtWindowEventMap": "readonly", + "browser": "readonly", + "createIframeUi": "readonly", + "createIntegratedUi": "readonly", + "createShadowRootUi": "readonly", + "defineAppConfig": "readonly", + "defineBackground": "readonly", + "defineContentScript": "readonly", + "defineUnlistedScript": "readonly", + "defineWxtPlugin": "readonly", + "fakeBrowser": "readonly", + "getAppConfig": "readonly", + "injectScript": "readonly", + "storage": "readonly", + "useAppConfig": "readonly" } + +export default { + name: "wxt/auto-imports", + languageOptions: { + globals, + /** @type {import('eslint').Linter.SourceType} */ + sourceType: "module", + }, +}; " `;