diff --git a/packages/typescript-eslint/src/config-helper.ts b/packages/typescript-eslint/src/config-helper.ts index ce5d4ea806ed..51456475734f 100644 --- a/packages/typescript-eslint/src/config-helper.ts +++ b/packages/typescript-eslint/src/config-helper.ts @@ -120,6 +120,7 @@ function configImpl(...configs: unknown[]): ConfigArray { extends?: unknown; files?: unknown; ignores?: unknown; + basePath?: unknown; }; if (extendsArr == null) { @@ -163,11 +164,27 @@ function configImpl(...configs: unknown[]): ConfigArray { } if (extension == null || typeof extension !== 'object') { nonObjectExtensions.push(extensionIndex); + continue; + } + + // https://github.com/eslint/rewrite/blob/82d07fd0e8e06780b552a41f8bcbe2a4f8741d42/packages/config-helpers/src/define-config.js#L448-L450 + if ('basePath' in extension) { + throw new TypeError( + `tseslint.config(): Config at index ${configIndex}${nameErrorPhrase} has an 'extends' array that contains a config with a 'basePath' property at index ${extensionIndex}.` + + ` 'basePath' in 'extends' is not allowed.`, + ); + } + + if ('extends' in extension) { + throw new TypeError( + `tseslint.config(): Config at index ${configIndex}${nameErrorPhrase} has an 'extends' array that contains a config with an 'extends' property at index ${extensionIndex}.` + + ` Nested 'extends' is not allowed.`, + ); } } if (nonObjectExtensions.length > 0) { const extensionIndices = nonObjectExtensions.join(', '); - throw new Error( + throw new TypeError( `tseslint.config(): Config at index ${configIndex}${nameErrorPhrase} contains non-object` + ` extensions at the following indices: ${extensionIndices}.`, ); @@ -181,6 +198,7 @@ function configImpl(...configs: unknown[]): ConfigArray { files?: unknown; ignores?: unknown; }; + const resolvedConfigName = [name, extension.name] .filter(Boolean) .join('__'); @@ -195,6 +213,7 @@ function configImpl(...configs: unknown[]): ConfigArray { ...extension, ...(config.files ? { files: config.files } : {}), ...(config.ignores ? { ignores: config.ignores } : {}), + ...(config.basePath ? { basePath: config.basePath } : {}), ...(resolvedConfigName !== '' ? { name: resolvedConfigName } : {}), }); } @@ -218,5 +237,7 @@ function configImpl(...configs: unknown[]): ConfigArray { * the return value can still be true. */ function isPossiblyGlobalIgnores(config: object): boolean { - return Object.keys(config).every(key => ['name', 'ignores'].includes(key)); + return Object.keys(config).every(key => + ['name', 'ignores', 'basePath'].includes(key), + ); } diff --git a/packages/typescript-eslint/tests/config-helper.test.ts b/packages/typescript-eslint/tests/config-helper.test.ts index 748f67626266..8b4a151a32cf 100644 --- a/packages/typescript-eslint/tests/config-helper.test.ts +++ b/packages/typescript-eslint/tests/config-helper.test.ts @@ -374,4 +374,61 @@ describe('config helper', () => { "tseslint.config(): Config at index 0 has a 'name' property that is not a string.", ); }); + + it('basePath works with unextended config', () => { + expect( + tseslint.config({ + basePath: 'base/path', + rules: { rule1: 'error' }, + }), + ).toStrictEqual([ + { + basePath: 'base/path', + rules: { rule1: 'error' }, + }, + ]); + }); + + it('basePath works with extended config', () => { + expect( + tseslint.config({ + basePath: 'base/path', + extends: [{ rules: { rule1: 'error' } }, { rules: { rule2: 'error' } }], + }), + ).toStrictEqual([ + { + basePath: 'base/path', + rules: { rule1: 'error' }, + }, + { + basePath: 'base/path', + rules: { rule2: 'error' }, + }, + ]); + }); + + it('basePath cannot be used in an extension', () => { + expect(() => { + tseslint.config({ + extends: [{ basePath: 'base/path', rules: { rule1: 'error' } }], + }); + }).toThrow( + "tseslint.config(): Config at index 0 (anonymous) has an 'extends' array that contains a config with a 'basePath' property at index 0. 'basePath' in 'extends' is not allowed.", + ); + }); + + it('should error when trying to use nested extends', () => { + expect(() => { + tseslint.config({ + extends: [ + { + extends: [ + { rules: { rule1: 'error' } }, + { rules: { rule2: 'error' } }, + ], + }, + ], + }); + }).toThrow(); + }); }); diff --git a/packages/utils/src/ts-eslint/Config.ts b/packages/utils/src/ts-eslint/Config.ts index 8dc9b80c9e28..c9eaee06d71c 100644 --- a/packages/utils/src/ts-eslint/Config.ts +++ b/packages/utils/src/ts-eslint/Config.ts @@ -255,6 +255,15 @@ export namespace FlatConfig { // it's not a json schema so it's nowhere near as nice to read and convert... // https://github.com/eslint/eslint/blob/v8.45.0/lib/config/flat-config-schema.js export interface Config { + /** + * The base path for files and ignores. + * + * Note that this is not permitted inside an `extends` array. + * + * Since ESLint 9.30.0 + */ + basePath?: string; + /** * An array of glob patterns indicating the files that the configuration object should apply to. * If not specified, the configuration object applies to all files matched by any other configuration object. diff --git a/packages/utils/tests/ts-eslint/ESLint.test.ts b/packages/utils/tests/ts-eslint/ESLint.test.ts index cce64f5a743e..1abf895cfafa 100644 --- a/packages/utils/tests/ts-eslint/ESLint.test.ts +++ b/packages/utils/tests/ts-eslint/ESLint.test.ts @@ -1,5 +1,7 @@ import { FlatESLint, LegacyESLint } from 'eslint/use-at-your-own-risk'; +import type { FlatConfig } from '../../src/ts-eslint'; + import * as ESLint from '../../src/ts-eslint/ESLint'; describe('ESLint', () => { @@ -12,6 +14,7 @@ describe('ESLint', () => { }); expect(eslint).toBeInstanceOf(FlatESLint); }); + it('legacy', () => { // eslint-disable-next-line @typescript-eslint/no-deprecated const eslint = new ESLint.LegacyESLint({ @@ -27,5 +30,11 @@ describe('ESLint', () => { }); expect(eslint).toBeInstanceOf(LegacyESLint); }); + + it('config type permits basePath (type test)', () => { + const __config: FlatConfig.Config = { + basePath: 'some/path', + }; + }); }); });
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: