ESLint added experimental support for typescript config files since [9.9.0](https://eslint.org/blog/2024/08/eslint-v9.9.0-released/#experimental-typescript-configuration-files), and as of [9.18.0](https://eslint.org/blog/2025/01/eslint-v9.18.0-released/#stable-typescript-configuration-file-support) that support is stable. This PR add ts/mts/cts to the list of known eslint config files, and adds the same extensions to config file generators ## Current Behavior When using the eslint executor with a ts file, returns error "When using the new Flat Config with ESLint, all configs must be named eslint.config.js or eslint.config.cjs and .eslintrc files may not be used. See https://eslint.org/docs/latest/use/configure/configuration-files" When using the eslint plugin, the inferred task is not created for projects that do not have a non-ts eslint config. ### Workarounds - Compiling ts rules/configs in a project. Introduces other issues - Using jiti or comparable - For plugin users, having a fake eslint.config.js at the root allows the inferred task to be created. ESLint will still use the ts config. - Cache targets are wrong - Complications in non-monorepo workspaces ## Expected Behavior When using the eslint executor with a ts file, no error is thrown. When using the eslint plugin with a ts file, the inferred task is created. ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> No issues, but addresses [this discussion](https://github.com/nrwl/nx/discussions/29710#discussion-7856165) --------- Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com>
184 lines
4.7 KiB
TypeScript
184 lines
4.7 KiB
TypeScript
import 'nx/src/internal-testing-utils/mock-project-graph';
|
|
|
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
|
import {
|
|
Tree,
|
|
readProjectConfiguration,
|
|
readJson,
|
|
updateJson,
|
|
joinPathFragments,
|
|
writeJson,
|
|
} from '@nx/devkit';
|
|
|
|
import type { Linter as ESLint } from 'eslint';
|
|
|
|
import generator from './generator';
|
|
import pluginGenerator from '../plugin/plugin';
|
|
import generatorGenerator from '../generator/generator';
|
|
import executorGenerator from '../executor/executor';
|
|
|
|
import { PackageJson } from 'nx/src/utils/package-json';
|
|
|
|
describe('lint-checks generator', () => {
|
|
let tree: Tree;
|
|
|
|
beforeEach(async () => {
|
|
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
await pluginGenerator(tree, {
|
|
directory: 'plugin',
|
|
importPath: '@acme/plugin',
|
|
compiler: 'tsc',
|
|
linter: 'eslint',
|
|
skipFormat: false,
|
|
skipTsConfig: false,
|
|
skipLintChecks: true, // we manually call it s.t. we can update config files first
|
|
unitTestRunner: 'jest',
|
|
});
|
|
await generatorGenerator(tree, {
|
|
name: 'my-generator',
|
|
path: 'plugin/src/generators/my-generator',
|
|
unitTestRunner: 'jest',
|
|
skipLintChecks: true,
|
|
});
|
|
await executorGenerator(tree, {
|
|
name: 'my-executor',
|
|
path: 'plugin/src/executors/my-executor',
|
|
unitTestRunner: 'jest',
|
|
includeHasher: false,
|
|
skipLintChecks: true,
|
|
});
|
|
});
|
|
|
|
it('should update configuration files for default plugin', async () => {
|
|
await generator(tree, { projectName: 'plugin' });
|
|
|
|
const projectConfig = readProjectConfiguration(tree, 'plugin');
|
|
const eslintConfig: ESLint.Config = readJson(
|
|
tree,
|
|
`${projectConfig.root}/.eslintrc.json`
|
|
);
|
|
|
|
expect(eslintConfig.overrides).toContainEqual(
|
|
expect.objectContaining({
|
|
files: expect.arrayContaining([
|
|
'./executors.json',
|
|
'./package.json',
|
|
'./generators.json',
|
|
]),
|
|
rules: {
|
|
'@nx/nx-plugin-checks': 'error',
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should not duplicate configuration', async () => {
|
|
await generator(tree, { projectName: 'plugin' });
|
|
await generator(tree, { projectName: 'plugin' });
|
|
const projectConfig = readProjectConfiguration(tree, 'plugin');
|
|
const eslintConfig: ESLint.Config = readJson(
|
|
tree,
|
|
`${projectConfig.root}/.eslintrc.json`
|
|
);
|
|
|
|
expect(
|
|
eslintConfig.overrides.find((x) => '@nx/nx-plugin-checks' in x.rules)
|
|
).toMatchInlineSnapshot(`
|
|
{
|
|
"files": [
|
|
"./package.json",
|
|
"./generators.json",
|
|
"./executors.json",
|
|
],
|
|
"parser": "jsonc-eslint-parser",
|
|
"rules": {
|
|
"@nx/nx-plugin-checks": "error",
|
|
},
|
|
}
|
|
`);
|
|
});
|
|
|
|
it('should update configuration files for angular-style plugin', async () => {
|
|
const startingProjectConfig = readProjectConfiguration(tree, 'plugin');
|
|
updateJson(
|
|
tree,
|
|
joinPathFragments(startingProjectConfig.root, 'package.json'),
|
|
(json: PackageJson) => {
|
|
json.schematics = './collection.json';
|
|
delete json.generators;
|
|
json.builders = './builders.json';
|
|
delete json.executors;
|
|
json['ng-update'] = './migrations.json';
|
|
return json;
|
|
}
|
|
);
|
|
writeJson(
|
|
tree,
|
|
joinPathFragments(startingProjectConfig.root, 'migrations.json'),
|
|
{}
|
|
);
|
|
await generator(tree, { projectName: 'plugin' });
|
|
const projectConfig = readProjectConfiguration(tree, 'plugin');
|
|
const eslintConfig: ESLint.Config = readJson(
|
|
tree,
|
|
`${projectConfig.root}/.eslintrc.json`
|
|
);
|
|
|
|
expect(eslintConfig.overrides).toMatchInlineSnapshot(`
|
|
[
|
|
{
|
|
"files": [
|
|
"*.ts",
|
|
"*.tsx",
|
|
"*.js",
|
|
"*.jsx",
|
|
],
|
|
"rules": {},
|
|
},
|
|
{
|
|
"files": [
|
|
"*.ts",
|
|
"*.tsx",
|
|
],
|
|
"rules": {},
|
|
},
|
|
{
|
|
"files": [
|
|
"*.js",
|
|
"*.jsx",
|
|
],
|
|
"rules": {},
|
|
},
|
|
{
|
|
"files": [
|
|
"*.json",
|
|
],
|
|
"parser": "jsonc-eslint-parser",
|
|
"rules": {
|
|
"@nx/dependency-checks": [
|
|
"error",
|
|
{
|
|
"ignoredFiles": [
|
|
"{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}",
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
"files": [
|
|
"./package.json",
|
|
"./collection.json",
|
|
"./builders.json",
|
|
"./migrations.json",
|
|
],
|
|
"parser": "jsonc-eslint-parser",
|
|
"rules": {
|
|
"@nx/nx-plugin-checks": "error",
|
|
},
|
|
},
|
|
]
|
|
`);
|
|
});
|
|
});
|