Skip to content

feat(typescript-eslint): support basePath in tseslint.config() #11357

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions packages/typescript-eslint/src/config-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ export type InfiniteDepthConfigWithExtends =
| InfiniteDepthConfigWithExtends[];

export interface ConfigWithExtends extends TSESLint.FlatConfig.Config {
/**
* The base path for files and ignores.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a @since eslint v9.x.y?

*
* Since ESLint 9.30.0
*/
basePath?: string;

/**
* Allows you to "extend" a set of configs similar to `extends` from the
* classic configs.
Expand Down Expand Up @@ -120,6 +127,7 @@ function configImpl(...configs: unknown[]): ConfigArray {
extends?: unknown;
files?: unknown;
ignores?: unknown;
basePath?: unknown;
};

if (extendsArr == null) {
Expand Down Expand Up @@ -163,11 +171,20 @@ 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 (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}.`,
);
Expand All @@ -181,6 +198,7 @@ function configImpl(...configs: unknown[]): ConfigArray {
files?: unknown;
ignores?: unknown;
};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Praise] 😄 this was bugging me, now that you mention it...

const resolvedConfigName = [name, extension.name]
.filter(Boolean)
.join('__');
Expand All @@ -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 } : {}),
});
}
Expand All @@ -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),
);
}
42 changes: 42 additions & 0 deletions packages/typescript-eslint/tests/config-helper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,4 +374,46 @@ 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.",
);
});
});