feat(eslint): rename eslint.config.js to eslint.config.cjs to resolve them as CommonJS (#29334)
This PR updates our generators to use `eslint.config.cjs` instead of `eslint.config.js` so that Node resolution will treat it as CommonJS. This solves an issue where having `"type": "module"` in `package.json` will result in an error when Node tries to resolve the config file as ESM. Also allows us to clean up out Remix generators to not have to rename to `eslint.config.cjs` to solve the same issue. <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
a675bd2a06
commit
b9c0e3db5f
@ -69,7 +69,7 @@ Sometimes we intentionally want to add or remove a dependency from our `package.
|
||||
"checkObsoleteDependencies": true, // toggle to disable
|
||||
"checkVersionMismatches": true, // toggle to disable
|
||||
"ignoredDependencies": ["lodash"], // these libs will be omitted from checks
|
||||
"ignoredFiles": ["webpack.config.js", "eslint.config.js"], // list of files that should be skipped for check
|
||||
"ignoredFiles": ["webpack.config.js", "eslint.config.cjs"], // list of files that should be skipped for check
|
||||
"includeTransitiveDependencies": true, // collect dependencies transitively from children
|
||||
"useLocalPathsForWorkspaceDependencies": true // toggle to disable
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"examplesFile": "Linter can be configured in multiple ways. The basic way is to provide only `lintFilePatterns`, which is a mandatory property. This tells us where to look for files to lint.\n\n`project.json`:\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"]\n }\n}\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Fixing linter issues\" %}\n\nLinter provides an automated way of fixing known issues. To ensure that those changes are properly cached, we need to add an `outputs` property to the `lint` target. Omitting the `outputs` property would produce an invalid cache record. Both of these properties are set by default when scaffolding a new project.\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"outputs\": [\"{options.outputFile}\"],\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"]\n }\n}\n```\n\nWith these settings, we can run the command with a `--fix` flag:\n\n```bash\nnx run frontend:lint --fix\n```\n\nWe can also set this flag via project configuration to always fix files when running lint:\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"outputs\": [\"{options.outputFile}\"],\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"],\n \"fix\": true\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"Custom output format\" %}\n\nESLint executor uses the `stylish` output format by default. You can change this by specifying the `format` property:\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"outputs\": [\"{options.outputFile}\"],\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"],\n \"format\": \"compact\"\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"Silence warnings\" %}\n\nMigrated or legacy projects tend to have an overwhelming amount of lint errors. We might want to change those temporarily to be warnings so they don't block the development. But they would still clutter the report. We can run the command with `--quiet` to hide warning (errors would still break the lint):\n\n```bash\nnx run frontend:lint --quiet\n```\n\nWe can also set this via project configuration as a default option.\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"outputs\": [\"{options.outputFile}\"],\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"],\n \"quiet\": true\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"Flat Config file\" %}\n\n`ESLint` provides several ways of specifying the configuration. The default one is using `.eslintrc.json` but you can override it by setting the `eslintConfig` flag. The new `Flat Config` is now also supported:\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"outputs\": [\"{options.outputFile}\"],\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"],\n \"eslintConfig\": \"eslint.config.js\"\n }\n}\n```\n\n**Note:** In contrast to other configuration formats, the `Flat Config` requires that all configuration files are converted to `eslint.config.js`. Built-in migrations and generators support only `.eslintrc.json` at the moment.\n\n{% /tab %}\n{% /tabs %}\n\n---\n",
|
||||
"examplesFile": "Linter can be configured in multiple ways. The basic way is to provide only `lintFilePatterns`, which is a mandatory property. This tells us where to look for files to lint.\n\n`project.json`:\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"]\n }\n}\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Fixing linter issues\" %}\n\nLinter provides an automated way of fixing known issues. To ensure that those changes are properly cached, we need to add an `outputs` property to the `lint` target. Omitting the `outputs` property would produce an invalid cache record. Both of these properties are set by default when scaffolding a new project.\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"outputs\": [\"{options.outputFile}\"],\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"]\n }\n}\n```\n\nWith these settings, we can run the command with a `--fix` flag:\n\n```bash\nnx run frontend:lint --fix\n```\n\nWe can also set this flag via project configuration to always fix files when running lint:\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"outputs\": [\"{options.outputFile}\"],\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"],\n \"fix\": true\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"Custom output format\" %}\n\nESLint executor uses the `stylish` output format by default. You can change this by specifying the `format` property:\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"outputs\": [\"{options.outputFile}\"],\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"],\n \"format\": \"compact\"\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"Silence warnings\" %}\n\nMigrated or legacy projects tend to have an overwhelming amount of lint errors. We might want to change those temporarily to be warnings so they don't block the development. But they would still clutter the report. We can run the command with `--quiet` to hide warning (errors would still break the lint):\n\n```bash\nnx run frontend:lint --quiet\n```\n\nWe can also set this via project configuration as a default option.\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"outputs\": [\"{options.outputFile}\"],\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"],\n \"quiet\": true\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"Flat Config file\" %}\n\n`ESLint` provides several ways of specifying the configuration. The default one is using `.eslintrc.json` but you can override it by setting the `eslintConfig` flag. The new `Flat Config` is now also supported:\n\n```json\n\"lint\": {\n \"executor\": \"@nx/eslint:lint\",\n \"outputs\": [\"{options.outputFile}\"],\n \"options\": {\n \"lintFilePatterns\": [\"apps/frontend/**/*.ts\"],\n \"eslintConfig\": \"eslint.config.cjs\"\n }\n}\n```\n\n**Note:** In contrast to other configuration formats, the `Flat Config` requires that all configuration files are converted to `eslint.config.cjs`. Built-in migrations and generators support only `.eslintrc.json` at the moment.\n\n{% /tab %}\n{% /tabs %}\n\n---\n",
|
||||
"presets": []
|
||||
},
|
||||
"hasher": "./src/executors/lint/hasher",
|
||||
|
||||
@ -69,7 +69,7 @@ Sometimes we intentionally want to add or remove a dependency from our `package.
|
||||
"checkObsoleteDependencies": true, // toggle to disable
|
||||
"checkVersionMismatches": true, // toggle to disable
|
||||
"ignoredDependencies": ["lodash"], // these libs will be omitted from checks
|
||||
"ignoredFiles": ["webpack.config.js", "eslint.config.js"], // list of files that should be skipped for check
|
||||
"ignoredFiles": ["webpack.config.js", "eslint.config.cjs"], // list of files that should be skipped for check
|
||||
"includeTransitiveDependencies": true, // collect dependencies transitively from children
|
||||
"useLocalPathsForWorkspaceDependencies": true // toggle to disable
|
||||
}
|
||||
|
||||
@ -10,12 +10,12 @@ See below a direct comparison between `JSON`, `JS` and `Flat` config:
|
||||
{% tabs %}
|
||||
{% tab label="Flat" %}
|
||||
|
||||
```js {% fileName="eslint.config.js" %}
|
||||
```js {% fileName="eslint.config.cjs" %}
|
||||
// the older versions were magically interpreting all the imports
|
||||
// in flat config we do it explicitly
|
||||
const nxPlugin = require('@nx/eslint-plugin');
|
||||
const js = require('@eslint/js');
|
||||
const baseConfig = require('./eslint.base.config.js');
|
||||
const baseConfig = require('./eslint.base.config.cjs');
|
||||
const globals = require('globals');
|
||||
const jsoncParser = require('jsonc-eslint-parser');
|
||||
const tsParser = require('@typescript-eslint/parser');
|
||||
|
||||
@ -39,7 +39,7 @@ describe('Move Angular Project', () => {
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.app.json`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.json`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.spec.json`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/eslint.config.js`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/eslint.config.cjs`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/public/favicon.ico`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/index.html`);
|
||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/main.ts`);
|
||||
|
||||
@ -164,13 +164,13 @@ describe('Angular Projects', () => {
|
||||
|
||||
it('should lint correctly with eslint and handle external HTML files and inline templates', async () => {
|
||||
// disable the prefer-standalone rule for app1 which is not standalone
|
||||
let app1EslintConfig = readFile(`${app1}/eslint.config.js`);
|
||||
let app1EslintConfig = readFile(`${app1}/eslint.config.cjs`);
|
||||
app1EslintConfig = app1EslintConfig.replace(
|
||||
`'@angular-eslint/directive-selector': [`,
|
||||
`'@angular-eslint/prefer-standalone': 'off',
|
||||
'@angular-eslint/directive-selector': [`
|
||||
);
|
||||
updateFile(`${app1}/eslint.config.js`, app1EslintConfig);
|
||||
updateFile(`${app1}/eslint.config.cjs`, app1EslintConfig);
|
||||
|
||||
// check apps and lib pass linting for initial generated code
|
||||
runCLI(`run-many --target lint --projects=${app1},${lib1} --parallel`);
|
||||
|
||||
@ -150,10 +150,10 @@ describe('Linter (legacy)', () => {
|
||||
env: { NX_ADD_PLUGINS: 'false' },
|
||||
});
|
||||
checkFilesExist(
|
||||
'eslint.config.js',
|
||||
`apps/${myapp}/eslint.config.js`,
|
||||
`libs/${mylib}/eslint.config.js`,
|
||||
`libs/${mylib2}/eslint.config.js`
|
||||
'eslint.config.cjs',
|
||||
`apps/${myapp}/eslint.config.cjs`,
|
||||
`libs/${mylib}/eslint.config.cjs`,
|
||||
`libs/${mylib2}/eslint.config.cjs`
|
||||
);
|
||||
checkFilesDoNotExist(
|
||||
'.eslintrc.json',
|
||||
@ -164,12 +164,12 @@ describe('Linter (legacy)', () => {
|
||||
|
||||
// move eslint.config one step up
|
||||
// to test the absence of the flat eslint config in the project root folder
|
||||
renameFile(`libs/${mylib2}/eslint.config.js`, `libs/eslint.config.js`);
|
||||
renameFile(`libs/${mylib2}/eslint.config.cjs`, `libs/eslint.config.cjs`);
|
||||
updateFile(
|
||||
`libs/eslint.config.js`,
|
||||
readFile(`libs/eslint.config.js`).replace(
|
||||
`../../eslint.config.js`,
|
||||
`../eslint.config.js`
|
||||
`libs/eslint.config.cjs`,
|
||||
readFile(`libs/eslint.config.cjs`).replace(
|
||||
`../../eslint.config.cjs`,
|
||||
`../eslint.config.cjs`
|
||||
)
|
||||
);
|
||||
|
||||
@ -202,9 +202,9 @@ describe('Linter (legacy)', () => {
|
||||
env: { NX_ADD_PLUGINS: 'false' },
|
||||
});
|
||||
checkFilesExist(
|
||||
'eslint.config.js',
|
||||
`${mylib}/eslint.config.js`,
|
||||
'eslint.base.config.js'
|
||||
'eslint.config.cjs',
|
||||
`${mylib}/eslint.config.cjs`,
|
||||
'eslint.base.config.cjs'
|
||||
);
|
||||
checkFilesDoNotExist(
|
||||
'.eslintrc.json',
|
||||
|
||||
@ -615,8 +615,8 @@ describe('Linter', () => {
|
||||
runCLI(`generate @nx/js:lib ${jsLib} --linter eslint`);
|
||||
|
||||
checkFilesExist(
|
||||
`${reactLib}/eslint.config.js`,
|
||||
`${jsLib}/eslint.config.js`
|
||||
`${reactLib}/eslint.config.cjs`,
|
||||
`${jsLib}/eslint.config.cjs`
|
||||
);
|
||||
checkFilesDoNotExist(
|
||||
`${reactLib}/.eslintrc.json`,
|
||||
|
||||
@ -178,16 +178,6 @@ describe('packaging libs', () => {
|
||||
`libs/${swcEsmLib}/src/index.ts`,
|
||||
`export * from './lib/${swcEsmLib}.js';`
|
||||
);
|
||||
// We also need to update the eslint config file extensions to be explicitly commonjs
|
||||
// TODO: re-evaluate this once we support ESM eslint configs
|
||||
renameFile(
|
||||
`libs/${tscEsmLib}/eslint.config.js`,
|
||||
`libs/${tscEsmLib}/eslint.config.cjs`
|
||||
);
|
||||
renameFile(
|
||||
`libs/${swcEsmLib}/eslint.config.js`,
|
||||
`libs/${swcEsmLib}/eslint.config.cjs`
|
||||
);
|
||||
|
||||
// Add additional entry points for `exports` field
|
||||
updateJson(join('libs', tscLib, 'project.json'), (json) => {
|
||||
|
||||
@ -26,7 +26,7 @@ exports[`Extra Nx Misc Tests task graph inputs should correctly expand dependent
|
||||
],
|
||||
"lib-base-123": [
|
||||
"libs/lib-base-123/README.md",
|
||||
"libs/lib-base-123/eslint.config.js",
|
||||
"libs/lib-base-123/eslint.config.cjs",
|
||||
"libs/lib-base-123/jest.config.ts",
|
||||
"libs/lib-base-123/package.json",
|
||||
"libs/lib-base-123/project.json",
|
||||
@ -39,7 +39,7 @@ exports[`Extra Nx Misc Tests task graph inputs should correctly expand dependent
|
||||
],
|
||||
"lib-dependent-123": [
|
||||
"libs/lib-dependent-123/README.md",
|
||||
"libs/lib-dependent-123/eslint.config.js",
|
||||
"libs/lib-dependent-123/eslint.config.cjs",
|
||||
"libs/lib-dependent-123/jest.config.ts",
|
||||
"libs/lib-dependent-123/package.json",
|
||||
"libs/lib-dependent-123/project.json",
|
||||
|
||||
@ -1204,14 +1204,14 @@ describe('lib', () => {
|
||||
describe('--linter', () => {
|
||||
describe('eslint', () => {
|
||||
it('should add valid eslint JSON configuration which extends from Nx presets (flat config)', async () => {
|
||||
tree.write('eslint.config.js', '');
|
||||
tree.write('eslint.config.cjs', '');
|
||||
|
||||
await runLibraryGeneratorWithOpts({ linter: Linter.EsLint });
|
||||
|
||||
const eslintConfig = tree.read('my-lib/eslint.config.js', 'utf-8');
|
||||
const eslintConfig = tree.read('my-lib/eslint.config.cjs', 'utf-8');
|
||||
expect(eslintConfig).toMatchInlineSnapshot(`
|
||||
"const nx = require("@nx/eslint-plugin");
|
||||
const baseConfig = require("../eslint.config.js");
|
||||
const baseConfig = require("../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
|
||||
@ -93,7 +93,7 @@ exports[`workspace move to nx layout should create nx.json 1`] = `
|
||||
"!{projectRoot}/**/*.spec.[jt]s",
|
||||
"!{projectRoot}/karma.conf.js",
|
||||
"!{projectRoot}/.eslintrc.json",
|
||||
"!{projectRoot}/eslint.config.js",
|
||||
"!{projectRoot}/eslint.config.cjs",
|
||||
],
|
||||
"sharedGlobals": [],
|
||||
},
|
||||
@ -104,7 +104,7 @@ exports[`workspace move to nx layout should create nx.json 1`] = `
|
||||
"default",
|
||||
"{workspaceRoot}/.eslintrc.json",
|
||||
"{workspaceRoot}/.eslintignore",
|
||||
"{workspaceRoot}/eslint.config.js",
|
||||
"{workspaceRoot}/eslint.config.cjs",
|
||||
],
|
||||
},
|
||||
"build": {
|
||||
@ -129,7 +129,7 @@ exports[`workspace move to nx layout should create nx.json 1`] = `
|
||||
"inputs": [
|
||||
"default",
|
||||
"{workspaceRoot}/.eslintrc.json",
|
||||
"{workspaceRoot}/eslint.config.js",
|
||||
"{workspaceRoot}/eslint.config.cjs",
|
||||
],
|
||||
},
|
||||
"test": {
|
||||
|
||||
@ -64,7 +64,10 @@ export function createNxJson(
|
||||
]
|
||||
: []),
|
||||
...(targets.lint
|
||||
? ['!{projectRoot}/.eslintrc.json', '!{projectRoot}/eslint.config.js']
|
||||
? [
|
||||
'!{projectRoot}/.eslintrc.json',
|
||||
'!{projectRoot}/eslint.config.cjs',
|
||||
]
|
||||
: []),
|
||||
].filter(Boolean),
|
||||
},
|
||||
@ -85,7 +88,7 @@ export function createNxJson(
|
||||
inputs: [
|
||||
'default',
|
||||
'{workspaceRoot}/.eslintrc.json',
|
||||
'{workspaceRoot}/eslint.config.js',
|
||||
'{workspaceRoot}/eslint.config.cjs',
|
||||
],
|
||||
cache: true,
|
||||
}
|
||||
|
||||
@ -16,5 +16,5 @@
|
||||
<%_ if (jsx) { _%>"<%= offsetFromProjectRoot %>**/*.cy.jsx",<%_ } _%>
|
||||
"<%= offsetFromProjectRoot %>**/*.d.ts"
|
||||
],
|
||||
"exclude": ["out-tsc", "test-output"<% if (linter === 'eslint') { %>, "eslint.config.js"<% } %>]
|
||||
"exclude": ["out-tsc", "test-output"<% if (linter === 'eslint') { %>, "eslint.config.js", "eslint.config.cjs", "eslint.config.mjs"<% } %>]
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ const isPrettierAvailable =
|
||||
*/
|
||||
export default tseslint.config(
|
||||
{
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
extends: [eslint.configs.recommended, ...tseslint.configs.recommended],
|
||||
},
|
||||
{
|
||||
@ -40,7 +40,7 @@ export default tseslint.config(
|
||||
plugins: { '@typescript-eslint': tseslint.plugin },
|
||||
},
|
||||
{
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
'@typescript-eslint/explicit-member-accessibility': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
|
||||
@ -15,7 +15,7 @@ const isPrettierAvailable =
|
||||
*/
|
||||
export default tseslint.config(
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
extends: [eslint.configs.recommended, ...tseslint.configs.recommended],
|
||||
},
|
||||
{
|
||||
@ -30,7 +30,7 @@ export default tseslint.config(
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', , '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
'@typescript-eslint/explicit-member-accessibility': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
|
||||
@ -96,12 +96,12 @@ We can also set this via project configuration as a default option.
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/frontend/**/*.ts"],
|
||||
"eslintConfig": "eslint.config.js"
|
||||
"eslintConfig": "eslint.config.cjs"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** In contrast to other configuration formats, the `Flat Config` requires that all configuration files are converted to `eslint.config.js`. Built-in migrations and generators support only `.eslintrc.json` at the moment.
|
||||
**Note:** In contrast to other configuration formats, the `Flat Config` requires that all configuration files are converted to `eslint.config.cjs`. Built-in migrations and generators support only `.eslintrc.json` at the moment.
|
||||
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
@ -24,6 +24,11 @@
|
||||
"version": "20.2.0-beta.5",
|
||||
"description": "Update TypeScript ESLint packages to v8.13.0 if they are already on v8",
|
||||
"implementation": "./src/migrations/update-20-2-0/update-typescript-eslint-v8-13-0"
|
||||
},
|
||||
"add-file-extensions-to-overrides": {
|
||||
"version": "20.3.0-beta.1",
|
||||
"description": "Update ESLint flat config to include .cjs, .mjs, .cts, and .mts files in overrides (if needed)",
|
||||
"implementation": "./src/migrations/update-20-3-0/add-file-extensions-to-overrides"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
|
||||
@ -436,7 +436,7 @@ Please see https://nx.dev/recipes/tips-n-tricks/eslint for full guidance on how
|
||||
|
||||
it('should intercept the error from `@typescript-eslint` regarding missing parserServices and provide a more detailed user-facing message logging the found flat config', async () => {
|
||||
setupMocks();
|
||||
tempFs.createFileSync('apps/proj/eslint.config.js', '');
|
||||
tempFs.createFileSync('apps/proj/eslint.config.cjs', '');
|
||||
tempFs.createFileSync('apps/proj/src/some-file.ts', '');
|
||||
|
||||
mockLintFiles.mockImplementation(() => {
|
||||
@ -456,7 +456,7 @@ Occurred while linting ${mockContext.root}/apps/proj/src/some-file.ts`
|
||||
);
|
||||
expect(console.error).toHaveBeenCalledWith(
|
||||
`
|
||||
Error: You have attempted to use the lint rule "@typescript-eslint/await-thenable" which requires the full TypeScript type-checker to be available, but you do not have "parserOptions.project" configured to point at your project tsconfig.json files in the relevant TypeScript file "overrides" block of your ESLint config "apps/proj/eslint.config.js"
|
||||
Error: You have attempted to use the lint rule "@typescript-eslint/await-thenable" which requires the full TypeScript type-checker to be available, but you do not have "parserOptions.project" configured to point at your project tsconfig.json files in the relevant TypeScript file "overrides" block of your ESLint config "apps/proj/eslint.config.cjs"
|
||||
Occurred while linting ${mockContext.root}/apps/proj/src/some-file.ts
|
||||
|
||||
Please see https://nx.dev/recipes/tips-n-tricks/eslint for full guidance on how to resolve this issue.
|
||||
@ -466,7 +466,7 @@ Please see https://nx.dev/recipes/tips-n-tricks/eslint for full guidance on how
|
||||
|
||||
it('should intercept the error from `@typescript-eslint` regarding missing parserServices and provide a more detailed user-facing message logging the found flat config at the workspace root', async () => {
|
||||
setupMocks();
|
||||
tempFs.createFileSync('eslint.config.js', '');
|
||||
tempFs.createFileSync('eslint.config.cjs', '');
|
||||
tempFs.createFileSync('apps/proj/src/some-file.ts', '');
|
||||
|
||||
mockLintFiles.mockImplementation(() => {
|
||||
@ -486,7 +486,7 @@ Occurred while linting ${mockContext.root}/apps/proj/src/some-file.ts`
|
||||
);
|
||||
expect(console.error).toHaveBeenCalledWith(
|
||||
`
|
||||
Error: You have attempted to use the lint rule "@typescript-eslint/await-thenable" which requires the full TypeScript type-checker to be available, but you do not have "parserOptions.project" configured to point at your project tsconfig.json files in the relevant TypeScript file "overrides" block of your ESLint config "eslint.config.js"
|
||||
Error: You have attempted to use the lint rule "@typescript-eslint/await-thenable" which requires the full TypeScript type-checker to be available, but you do not have "parserOptions.project" configured to point at your project tsconfig.json files in the relevant TypeScript file "overrides" block of your ESLint config "eslint.config.cjs"
|
||||
Occurred while linting ${mockContext.root}/apps/proj/src/some-file.ts
|
||||
|
||||
Please see https://nx.dev/recipes/tips-n-tricks/eslint for full guidance on how to resolve this issue.
|
||||
@ -906,15 +906,15 @@ Please see https://nx.dev/recipes/tips-n-tricks/eslint for full guidance on how
|
||||
expect(result).toEqual({ success: true });
|
||||
});
|
||||
|
||||
it('should pass path to eslint.config.js to resolveAndInstantiateESLint if it is unspecified and we are using flag configuration', async () => {
|
||||
it('should pass path to eslint.config.cjs to resolveAndInstantiateESLint if it is unspecified and we are using flag configuration', async () => {
|
||||
setupMocks();
|
||||
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
|
||||
await lintExecutor(createValidRunBuilderOptions(), mockContext);
|
||||
expect(mockResolveAndInstantiateESLint).toHaveBeenCalledWith(
|
||||
`${mockContext.root}/apps/proj/eslint.config.js`,
|
||||
`${mockContext.root}/apps/proj/eslint.config.cjs`,
|
||||
{
|
||||
lintFilePatterns: [],
|
||||
eslintConfig: 'apps/proj/eslint.config.js',
|
||||
eslintConfig: 'apps/proj/eslint.config.cjs',
|
||||
fix: true,
|
||||
cache: true,
|
||||
cacheLocation: 'cacheLocation1/proj',
|
||||
|
||||
@ -162,7 +162,7 @@ describe('eslint-utils', () => {
|
||||
});
|
||||
|
||||
describe('ESLint Flat Config', () => {
|
||||
it('should throw if a non eslint.config.js or eslint.config.cjs file is used with ESLint Flat Config', async () => {
|
||||
it('should throw if a non eslint.config.cjs or eslint.config.cjs file is used with ESLint Flat Config', async () => {
|
||||
await expect(
|
||||
resolveAndInstantiateESLint('./.eslintrc.json', {} as any, true)
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
|
||||
@ -12,6 +12,9 @@ const compat = new FlatCompat({
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
|
||||
{
|
||||
@ -38,7 +41,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -49,7 +52,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -70,6 +73,9 @@ const compat = new FlatCompat({
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{
|
||||
languageOptions: {
|
||||
@ -100,7 +106,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -111,7 +117,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -131,6 +137,9 @@ const compat = new FlatCompat({
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{ languageOptions: { globals: { myCustomGlobal: 'readonly' } } },
|
||||
{
|
||||
@ -157,7 +166,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -168,7 +177,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -188,6 +197,9 @@ const compat = new FlatCompat({
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
@ -213,7 +225,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -224,7 +236,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -248,6 +260,9 @@ const compat = new FlatCompat({
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{ languageOptions: { parser: typescriptEslintParser } },
|
||||
{
|
||||
@ -274,7 +289,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -285,7 +300,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -308,6 +323,9 @@ const compat = new FlatCompat({
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{
|
||||
plugins: {
|
||||
'eslint-plugin-import': eslintPluginImport,
|
||||
@ -340,7 +358,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -351,7 +369,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -371,6 +389,9 @@ const compat = new FlatCompat({
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{
|
||||
settings: {
|
||||
@ -401,7 +422,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -412,7 +433,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -432,6 +453,9 @@ const compat = new FlatCompat({
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
@ -457,7 +481,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -468,7 +492,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -478,9 +502,12 @@ module.exports = [
|
||||
`;
|
||||
|
||||
exports[`convert-to-flat-config generator should convert json successfully 2`] = `
|
||||
"const baseConfig = require('../../eslint.config.js');
|
||||
"const baseConfig = require('../../eslint.config.cjs');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
...baseConfig,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
@ -512,6 +539,9 @@ const compat = new FlatCompat({
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
@ -537,7 +567,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -548,7 +578,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -558,9 +588,12 @@ module.exports = [
|
||||
`;
|
||||
|
||||
exports[`convert-to-flat-config generator should convert yaml successfully 2`] = `
|
||||
"const baseConfig = require('../../eslint.config.js');
|
||||
"const baseConfig = require('../../eslint.config.cjs');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
...baseConfig,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
@ -592,6 +625,9 @@ const compat = new FlatCompat({
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
@ -617,7 +653,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -628,7 +664,7 @@ module.exports = [
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -638,9 +674,12 @@ module.exports = [
|
||||
`;
|
||||
|
||||
exports[`convert-to-flat-config generator should convert yml successfully 2`] = `
|
||||
"const baseConfig = require('../../eslint.config.js');
|
||||
"const baseConfig = require('../../eslint.config.cjs');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
...baseConfig,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
@ -662,9 +701,12 @@ module.exports = [
|
||||
`;
|
||||
|
||||
exports[`convert-to-flat-config generator should handle custom eslintignores 1`] = `
|
||||
"const baseConfig = require('../../eslint.config.js');
|
||||
"const baseConfig = require('../../eslint.config.cjs');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
...baseConfig,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
|
||||
@ -40,6 +40,12 @@ describe('convertEslintJsonToFlatConfig', () => {
|
||||
extends: ['plugin:@nx/typescript'],
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['*.js', '*.jsx'],
|
||||
extends: ['plugin:@nx/javascript'],
|
||||
rules: {},
|
||||
},
|
||||
|
||||
{
|
||||
files: [
|
||||
'**/*.spec.ts',
|
||||
@ -76,6 +82,11 @@ describe('convertEslintJsonToFlatConfig', () => {
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: [
|
||||
"**/dist"
|
||||
]
|
||||
},
|
||||
{ plugins: { "@nx": nxEslintPlugin } },
|
||||
{
|
||||
files: [
|
||||
@ -110,7 +121,25 @@ describe('convertEslintJsonToFlatConfig', () => {
|
||||
...config,
|
||||
files: [
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
"**/*.tsx",
|
||||
"**/*.cts",
|
||||
"**/*.mts"
|
||||
],
|
||||
rules: {
|
||||
...config.rules
|
||||
}
|
||||
})),
|
||||
...compat.config({
|
||||
extends: [
|
||||
"plugin:@nx/javascript"
|
||||
]
|
||||
}).map(config => ({
|
||||
...config,
|
||||
files: [
|
||||
"**/*.js",
|
||||
"**/*.jsx",
|
||||
"**/*.cjs",
|
||||
"**/*.mjs"
|
||||
],
|
||||
rules: {
|
||||
...config.rules
|
||||
@ -205,7 +234,7 @@ describe('convertEslintJsonToFlatConfig', () => {
|
||||
expect(content).toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
||||
const js = require("@eslint/js");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
const globals = require("globals");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
@ -214,6 +243,11 @@ describe('convertEslintJsonToFlatConfig', () => {
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: [
|
||||
"**/dist"
|
||||
]
|
||||
},
|
||||
...baseConfig,
|
||||
...compat.extends("plugin:@nx/react-typescript", "next", "next/core-web-vitals"),
|
||||
{ languageOptions: { globals: { ...globals.jest } } },
|
||||
|
||||
@ -30,6 +30,13 @@ export function convertEslintJsonToFlatConfig(
|
||||
let combinedConfig: ts.PropertyAssignment[] = [];
|
||||
let languageOptions: ts.PropertyAssignment[] = [];
|
||||
|
||||
// exclude dist and eslint config from being linted, which matches the default for new workspaces
|
||||
exportElements.push(
|
||||
generateAst({
|
||||
ignores: ['**/dist'],
|
||||
})
|
||||
);
|
||||
|
||||
if (config.extends) {
|
||||
const extendsResult = addExtends(importsMap, exportElements, config);
|
||||
isFlatCompatNeeded = extendsResult.isFlatCompatNeeded;
|
||||
@ -218,7 +225,7 @@ function addExtends(
|
||||
configBlocks.push(generateSpreadElement(localName));
|
||||
const newImport = imp.replace(
|
||||
/^(.*)\.eslintrc(.base)?\.json$/,
|
||||
'$1eslint$2.config.js'
|
||||
'$1eslint$2.config.cjs'
|
||||
);
|
||||
importsMap.set(newImport, localName);
|
||||
} else {
|
||||
|
||||
@ -82,19 +82,19 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.exists('eslint.config.js')).toBeTruthy();
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.js')).toBeTruthy();
|
||||
expect(tree.exists('eslint.config.cjs')).toBeTruthy();
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.cjs')).toBeTruthy();
|
||||
expect(
|
||||
tree.read('libs/test-lib/eslint.config.js', 'utf-8')
|
||||
tree.read('libs/test-lib/eslint.config.cjs', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
// check nx.json changes
|
||||
const nxJson = readJson(tree, 'nx.json');
|
||||
expect(nxJson.targetDefaults.lint.inputs).toContain(
|
||||
'{workspaceRoot}/eslint.config.js'
|
||||
'{workspaceRoot}/eslint.config.cjs'
|
||||
);
|
||||
expect(nxJson.namedInputs.production).toContain(
|
||||
'!{projectRoot}/eslint.config.js'
|
||||
'!{projectRoot}/eslint.config.cjs'
|
||||
);
|
||||
});
|
||||
|
||||
@ -112,19 +112,19 @@ describe('convert-to-flat-config generator', () => {
|
||||
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.exists('eslint.config.js')).toBeTruthy();
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.js')).toBeTruthy();
|
||||
expect(tree.exists('eslint.config.cjs')).toBeTruthy();
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.cjs')).toBeTruthy();
|
||||
expect(
|
||||
tree.read('libs/test-lib/eslint.config.js', 'utf-8')
|
||||
tree.read('libs/test-lib/eslint.config.cjs', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
// check nx.json changes
|
||||
const nxJson = readJson(tree, 'nx.json');
|
||||
expect(nxJson.targetDefaults.lint.inputs).toContain(
|
||||
'{workspaceRoot}/eslint.config.js'
|
||||
'{workspaceRoot}/eslint.config.cjs'
|
||||
);
|
||||
expect(nxJson.namedInputs.production).toContain(
|
||||
'!{projectRoot}/eslint.config.js'
|
||||
'!{projectRoot}/eslint.config.cjs'
|
||||
);
|
||||
});
|
||||
|
||||
@ -142,19 +142,19 @@ describe('convert-to-flat-config generator', () => {
|
||||
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.exists('eslint.config.js')).toBeTruthy();
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.js')).toBeTruthy();
|
||||
expect(tree.exists('eslint.config.cjs')).toBeTruthy();
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.cjs')).toBeTruthy();
|
||||
expect(
|
||||
tree.read('libs/test-lib/eslint.config.js', 'utf-8')
|
||||
tree.read('libs/test-lib/eslint.config.cjs', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
// check nx.json changes
|
||||
const nxJson = readJson(tree, 'nx.json');
|
||||
expect(nxJson.targetDefaults.lint.inputs).toContain(
|
||||
'{workspaceRoot}/eslint.config.js'
|
||||
'{workspaceRoot}/eslint.config.cjs'
|
||||
);
|
||||
expect(nxJson.namedInputs.production).toContain(
|
||||
'!{projectRoot}/eslint.config.js'
|
||||
'!{projectRoot}/eslint.config.cjs'
|
||||
);
|
||||
});
|
||||
|
||||
@ -171,7 +171,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchInlineSnapshot(`
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||
@ -182,6 +182,9 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
...compat.extends('plugin:storybook/recommended'),
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{
|
||||
@ -208,7 +211,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -219,7 +222,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -227,11 +230,14 @@ describe('convert-to-flat-config generator', () => {
|
||||
];
|
||||
"
|
||||
`);
|
||||
expect(tree.read('libs/test-lib/eslint.config.js', 'utf-8'))
|
||||
expect(tree.read('libs/test-lib/eslint.config.cjs', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const baseConfig = require('../../eslint.config.js');
|
||||
"const baseConfig = require('../../eslint.config.cjs');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
...baseConfig,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
@ -266,7 +272,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
tree.write('.eslintignore', 'ignore/me');
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
const config = tree.read('eslint.config.js', 'utf-8');
|
||||
const config = tree.read('eslint.config.cjs', 'utf-8');
|
||||
expect(config).toContain('ignore/me');
|
||||
expect(config).toMatchSnapshot();
|
||||
expect(tree.exists('.eslintignore')).toBeFalsy();
|
||||
@ -290,7 +296,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(
|
||||
tree.read('libs/test-lib/eslint.config.js', 'utf-8')
|
||||
tree.read('libs/test-lib/eslint.config.cjs', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.exists('another-folder/.myeslintignore')).toBeFalsy();
|
||||
expect(tree.exists('libs/test-lib/.eslintignore')).toBeFalsy();
|
||||
@ -316,7 +322,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add env configuration', async () => {
|
||||
@ -335,7 +341,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add global configuration', async () => {
|
||||
@ -353,7 +359,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add global and env configuration', async () => {
|
||||
@ -374,7 +380,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add plugins', async () => {
|
||||
@ -395,7 +401,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add parser', async () => {
|
||||
@ -411,7 +417,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchSnapshot();
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add linter options', async () => {
|
||||
@ -427,7 +433,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchInlineSnapshot(`
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||
@ -438,6 +444,9 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
{ plugins: { '@nx': nxEslintPlugin } },
|
||||
{
|
||||
linterOptions: {
|
||||
@ -468,7 +477,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -479,7 +488,7 @@ describe('convert-to-flat-config generator', () => {
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
@ -510,11 +519,11 @@ describe('convert-to-flat-config generator', () => {
|
||||
}
|
||||
);
|
||||
|
||||
expect(tree.exists('eslint.config.js')).toBeFalsy();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.js')).toBeFalsy();
|
||||
expect(tree.exists('eslint.config.cjs')).toBeFalsy();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.cjs')).toBeFalsy();
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
expect(tree.exists('eslint.config.js')).toBeTruthy();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.js')).toBeTruthy();
|
||||
expect(tree.exists('eslint.config.cjs')).toBeTruthy();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.cjs')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should convert project if target is defined via plugin as object', async () => {
|
||||
@ -545,11 +554,11 @@ describe('convert-to-flat-config generator', () => {
|
||||
}
|
||||
);
|
||||
|
||||
expect(tree.exists('eslint.config.js')).toBeFalsy();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.js')).toBeFalsy();
|
||||
expect(tree.exists('eslint.config.cjs')).toBeFalsy();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.cjs')).toBeFalsy();
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
expect(tree.exists('eslint.config.js')).toBeTruthy();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.js')).toBeTruthy();
|
||||
expect(tree.exists('eslint.config.cjs')).toBeTruthy();
|
||||
expect(tree.exists('libs/test-lib/eslint.config.cjs')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should handle parser options even if parser is extended', async () => {
|
||||
@ -589,13 +598,16 @@ describe('convert-to-flat-config generator', () => {
|
||||
});
|
||||
|
||||
await convertToFlatConfigGenerator(tree, options);
|
||||
expect(tree.exists('apps/dx-assets-ui/eslint.config.js')).toBeTruthy();
|
||||
expect(tree.exists('eslint.config.js')).toBeTruthy();
|
||||
expect(tree.read('apps/dx-assets-ui/eslint.config.js', 'utf-8'))
|
||||
expect(tree.exists('apps/dx-assets-ui/eslint.config.cjs')).toBeTruthy();
|
||||
expect(tree.exists('eslint.config.cjs')).toBeTruthy();
|
||||
expect(tree.read('apps/dx-assets-ui/eslint.config.cjs', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const baseConfig = require('../../eslint.config.js');
|
||||
"const baseConfig = require('../../eslint.config.cjs');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
},
|
||||
...baseConfig,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
|
||||
@ -42,10 +42,10 @@ export async function convertToFlatConfigGenerator(
|
||||
|
||||
const eslintIgnoreFiles = new Set<string>(['.eslintignore']);
|
||||
|
||||
// convert root eslint config to eslint.config.js
|
||||
// convert root eslint config to eslint.config.cjs
|
||||
convertRootToFlatConfig(tree, eslintFile);
|
||||
|
||||
// convert project eslint files to eslint.config.js
|
||||
// convert project eslint files to eslint.config.cjs
|
||||
const projects = getProjects(tree);
|
||||
for (const [project, projectConfig] of projects) {
|
||||
convertProjectToFlatConfig(
|
||||
@ -77,13 +77,13 @@ export default convertToFlatConfigGenerator;
|
||||
|
||||
function convertRootToFlatConfig(tree: Tree, eslintFile: string) {
|
||||
if (/\.base\.(js|json|yml|yaml)$/.test(eslintFile)) {
|
||||
convertConfigToFlatConfig(tree, '', eslintFile, 'eslint.base.config.js');
|
||||
convertConfigToFlatConfig(tree, '', eslintFile, 'eslint.base.config.cjs');
|
||||
}
|
||||
convertConfigToFlatConfig(
|
||||
tree,
|
||||
'',
|
||||
eslintFile.replace('.base.', '.'),
|
||||
'eslint.config.js'
|
||||
'eslint.config.cjs'
|
||||
);
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ function convertProjectToFlatConfig(
|
||||
tree,
|
||||
projectConfig.root,
|
||||
eslintFile,
|
||||
'eslint.config.js',
|
||||
'eslint.config.cjs',
|
||||
ignorePath
|
||||
);
|
||||
eslintIgnoreFiles.add(`${projectConfig.root}/.eslintignore`);
|
||||
@ -151,17 +151,17 @@ function updateNxJsonConfig(tree: Tree) {
|
||||
updateJson(tree, 'nx.json', (json: NxJsonConfiguration) => {
|
||||
if (json.targetDefaults?.lint?.inputs) {
|
||||
const inputSet = new Set(json.targetDefaults.lint.inputs);
|
||||
inputSet.add('{workspaceRoot}/eslint.config.js');
|
||||
inputSet.add('{workspaceRoot}/eslint.config.cjs');
|
||||
json.targetDefaults.lint.inputs = Array.from(inputSet);
|
||||
}
|
||||
if (json.targetDefaults?.['@nx/eslint:lint']?.inputs) {
|
||||
const inputSet = new Set(json.targetDefaults['@nx/eslint:lint'].inputs);
|
||||
inputSet.add('{workspaceRoot}/eslint.config.js');
|
||||
inputSet.add('{workspaceRoot}/eslint.config.cjs');
|
||||
json.targetDefaults['@nx/eslint:lint'].inputs = Array.from(inputSet);
|
||||
}
|
||||
if (json.namedInputs?.production) {
|
||||
const inputSet = new Set(json.namedInputs.production);
|
||||
inputSet.add('!{projectRoot}/eslint.config.js');
|
||||
inputSet.add('!{projectRoot}/eslint.config.cjs');
|
||||
json.namedInputs.production = Array.from(inputSet);
|
||||
}
|
||||
return json;
|
||||
|
||||
@ -573,7 +573,7 @@ describe('Eslint - Convert Executors To Plugin', () => {
|
||||
'default',
|
||||
'{projectRoot}/.eslintrc.json',
|
||||
'{projectRoot}/.eslintignore',
|
||||
'{projectRoot}/eslint.config.js',
|
||||
'{projectRoot}/eslint.config.cjs',
|
||||
],
|
||||
};
|
||||
updateNxJson(tree, nxJson);
|
||||
@ -589,7 +589,7 @@ describe('Eslint - Convert Executors To Plugin', () => {
|
||||
'default',
|
||||
'{projectRoot}/.eslintrc.json',
|
||||
'{projectRoot}/.eslintignore',
|
||||
'{projectRoot}/eslint.config.js',
|
||||
'{projectRoot}/eslint.config.cjs',
|
||||
{ externalDependencies: ['eslint'] },
|
||||
]);
|
||||
});
|
||||
@ -604,7 +604,7 @@ describe('Eslint - Convert Executors To Plugin', () => {
|
||||
'default',
|
||||
'{projectRoot}/.eslintrc.json',
|
||||
'{projectRoot}/.eslintignore',
|
||||
'{projectRoot}/eslint.config.js',
|
||||
'{projectRoot}/eslint.config.cjs',
|
||||
{ externalDependencies: ['eslint-plugin-react'] },
|
||||
],
|
||||
};
|
||||
@ -621,7 +621,7 @@ describe('Eslint - Convert Executors To Plugin', () => {
|
||||
'default',
|
||||
'{projectRoot}/.eslintrc.json',
|
||||
'{projectRoot}/.eslintignore',
|
||||
'{projectRoot}/eslint.config.js',
|
||||
'{projectRoot}/eslint.config.cjs',
|
||||
{ externalDependencies: ['eslint-plugin-react', 'eslint'] },
|
||||
]);
|
||||
});
|
||||
@ -636,7 +636,7 @@ describe('Eslint - Convert Executors To Plugin', () => {
|
||||
'default',
|
||||
'{projectRoot}/.eslintrc.json',
|
||||
'{projectRoot}/.eslintignore',
|
||||
'{projectRoot}/eslint.config.js',
|
||||
'{projectRoot}/eslint.config.cjs',
|
||||
{ externalDependencies: ['eslint', 'eslint-plugin-react'] },
|
||||
],
|
||||
};
|
||||
@ -653,7 +653,7 @@ describe('Eslint - Convert Executors To Plugin', () => {
|
||||
'default',
|
||||
'{projectRoot}/.eslintrc.json',
|
||||
'{projectRoot}/.eslintignore',
|
||||
'{projectRoot}/eslint.config.js',
|
||||
'{projectRoot}/eslint.config.cjs',
|
||||
{ externalDependencies: ['eslint', 'eslint-plugin-react'] },
|
||||
]);
|
||||
});
|
||||
|
||||
@ -65,7 +65,7 @@ function postTargetTransformer(
|
||||
'default',
|
||||
'{workspaceRoot}/.eslintrc.json',
|
||||
'{workspaceRoot}/.eslintignore',
|
||||
'{workspaceRoot}/eslint.config.js',
|
||||
'{workspaceRoot}/eslint.config.cjs',
|
||||
].includes(input)
|
||||
);
|
||||
if (inputs.length === 0) {
|
||||
|
||||
@ -114,7 +114,9 @@ export const getGlobalFlatEslintConfiguration = (
|
||||
|
||||
content = addBlockToFlatConfigExport(
|
||||
content,
|
||||
generateFlatOverride({ ignores: ['**/dist'] })
|
||||
generateFlatOverride({
|
||||
ignores: ['**/dist'],
|
||||
})
|
||||
);
|
||||
|
||||
if (!rootProject) {
|
||||
@ -145,7 +147,14 @@ export const getGlobalFlatEslintConfiguration = (
|
||||
content = addBlockToFlatConfigExport(
|
||||
content,
|
||||
generateFlatOverride({
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
files: [
|
||||
'**/*.ts',
|
||||
'**/*.tsx',
|
||||
'**/*.js',
|
||||
'**/*.jsx',
|
||||
'**/*.cjs',
|
||||
'**/*.mjs',
|
||||
],
|
||||
rules: {},
|
||||
})
|
||||
);
|
||||
|
||||
@ -57,9 +57,9 @@ export function migrateConfigToMonorepoStyle(
|
||||
keepExistingVersions
|
||||
);
|
||||
tree.write(
|
||||
tree.exists('eslint.config.js')
|
||||
? 'eslint.base.config.js'
|
||||
: 'eslint.config.js',
|
||||
tree.exists('eslint.config.cjs')
|
||||
? 'eslint.base.config.cjs'
|
||||
: 'eslint.config.cjs',
|
||||
getGlobalFlatEslintConfiguration()
|
||||
);
|
||||
} else {
|
||||
@ -134,7 +134,7 @@ function migrateEslintFile(projectEslintPath: string, tree: Tree) {
|
||||
let config = tree.read(projectEslintPath, 'utf-8');
|
||||
// remove @nx plugin
|
||||
config = removePlugin(config, '@nx', '@nx/eslint-plugin-nx');
|
||||
// extend eslint.base.config.js
|
||||
// extend eslint.base.config.cjs
|
||||
config = addImportToFlatConfig(
|
||||
config,
|
||||
'baseConfig',
|
||||
|
||||
@ -113,7 +113,7 @@ describe('@nx/eslint:init', () => {
|
||||
'default',
|
||||
'{workspaceRoot}/.eslintrc.json',
|
||||
'{workspaceRoot}/.eslintignore',
|
||||
'{workspaceRoot}/eslint.config.js',
|
||||
'{workspaceRoot}/eslint.config.cjs',
|
||||
],
|
||||
});
|
||||
});
|
||||
@ -137,7 +137,7 @@ describe('@nx/eslint:init', () => {
|
||||
'default',
|
||||
'{workspaceRoot}/.eslintrc.json',
|
||||
'{workspaceRoot}/.eslintignore',
|
||||
'{workspaceRoot}/eslint.config.js',
|
||||
'{workspaceRoot}/eslint.config.cjs',
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
@ -28,7 +28,7 @@ function updateProductionFileset(tree: Tree) {
|
||||
const productionFileSet = nxJson.namedInputs?.production;
|
||||
if (productionFileSet) {
|
||||
productionFileSet.push('!{projectRoot}/.eslintrc.json');
|
||||
productionFileSet.push('!{projectRoot}/eslint.config.js');
|
||||
productionFileSet.push('!{projectRoot}/eslint.config.cjs');
|
||||
// Dedupe and set
|
||||
nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
|
||||
}
|
||||
@ -45,7 +45,7 @@ function addTargetDefaults(tree: Tree) {
|
||||
'default',
|
||||
`{workspaceRoot}/.eslintrc.json`,
|
||||
`{workspaceRoot}/.eslintignore`,
|
||||
`{workspaceRoot}/eslint.config.js`,
|
||||
`{workspaceRoot}/eslint.config.cjs`,
|
||||
];
|
||||
updateNxJson(tree, nxJson);
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ describe('@nx/eslint:lint-project', () => {
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
expect(tree.read('eslint.config.js', 'utf-8')).toMatchInlineSnapshot(`
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"const nx = require("@nx/eslint-plugin");
|
||||
|
||||
module.exports = [
|
||||
@ -97,7 +97,9 @@ describe('@nx/eslint:lint-project', () => {
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.js",
|
||||
"**/*.jsx"
|
||||
"**/*.jsx",
|
||||
"**/*.cjs",
|
||||
"**/*.mjs"
|
||||
],
|
||||
// Override or add rules here
|
||||
rules: {}
|
||||
|
||||
@ -273,8 +273,7 @@ function createEsLintConfiguration(
|
||||
});
|
||||
const nodeList = createNodeList(importMap, nodes);
|
||||
const content = stringifyNodeList(nodeList);
|
||||
const ext = extendedRootConfig?.endsWith('.cjs') ? '.cjs' : '.js';
|
||||
tree.write(join(projectConfig.root, `eslint.config${ext}`), content);
|
||||
tree.write(join(projectConfig.root, `eslint.config.cjs`), content);
|
||||
} else {
|
||||
writeJson(tree, join(projectConfig.root, `.eslintrc.json`), {
|
||||
extends: extendedRootConfig ? [pathToRootConfig] : undefined,
|
||||
|
||||
@ -71,7 +71,7 @@ function setUpLegacyRootEslintRc(tree: Tree, options: SetupRootEsLintOptions) {
|
||||
|
||||
function setUpRootFlatConfig(tree: Tree, options: SetupRootEsLintOptions) {
|
||||
tree.write(
|
||||
'eslint.config.js',
|
||||
'eslint.config.cjs',
|
||||
getGlobalFlatEslintConfiguration(options.rootProject)
|
||||
);
|
||||
|
||||
|
||||
@ -122,10 +122,10 @@ describe('@nx/eslint:lint-file', () => {
|
||||
});
|
||||
|
||||
it('should add extends to flat config', () => {
|
||||
tree.write('eslint.config.js', 'module.exports = {};');
|
||||
tree.write('eslint.config.cjs', 'module.exports = {};');
|
||||
tree.write(
|
||||
'apps/demo/eslint.config.js',
|
||||
`const baseConfig = require("../../eslint.config.js");
|
||||
'apps/demo/eslint.config.cjs',
|
||||
`const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
@ -143,11 +143,11 @@ module.exports = [
|
||||
|
||||
addExtendsToLintConfig(tree, 'apps/demo', 'plugin:playwright/recommend');
|
||||
|
||||
expect(tree.read('apps/demo/eslint.config.js', 'utf-8'))
|
||||
expect(tree.read('apps/demo/eslint.config.cjs', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
||||
const js = require("@eslint/js");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
@ -177,10 +177,10 @@ module.exports = [
|
||||
packageJson: { name: 'eslint', version: '9.0.0' },
|
||||
path: '',
|
||||
});
|
||||
tree.write('eslint.config.js', 'module.exports = {};');
|
||||
tree.write('eslint.config.cjs', 'module.exports = {};');
|
||||
tree.write(
|
||||
'apps/demo/eslint.config.js',
|
||||
`const baseConfig = require("../../eslint.config.js");
|
||||
'apps/demo/eslint.config.cjs',
|
||||
`const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
@ -201,12 +201,12 @@ module.exports = [
|
||||
needCompatFixup: true,
|
||||
});
|
||||
|
||||
expect(tree.read('apps/demo/eslint.config.js', 'utf-8'))
|
||||
expect(tree.read('apps/demo/eslint.config.cjs', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
||||
const js = require("@eslint/js");
|
||||
const { fixupConfigRules } = require("@eslint/compat");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
@ -236,10 +236,10 @@ module.exports = [
|
||||
packageJson: { name: 'eslint', version: '9.0.0' },
|
||||
path: '',
|
||||
});
|
||||
tree.write('eslint.config.js', 'module.exports = {};');
|
||||
tree.write('eslint.config.cjs', 'module.exports = {};');
|
||||
tree.write(
|
||||
'apps/demo/eslint.config.js',
|
||||
`const baseConfig = require("../../eslint.config.js");
|
||||
'apps/demo/eslint.config.cjs',
|
||||
`const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
@ -264,12 +264,12 @@ module.exports = [
|
||||
{ name: 'incompatible-plugin3', needCompatFixup: true },
|
||||
]);
|
||||
|
||||
expect(tree.read('apps/demo/eslint.config.js', 'utf-8'))
|
||||
expect(tree.read('apps/demo/eslint.config.cjs', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
||||
const js = require("@eslint/js");
|
||||
const { fixupConfigRules } = require("@eslint/compat");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
@ -307,10 +307,10 @@ module.exports = [
|
||||
packageJson: { name: 'eslint', version: '8.0.0' },
|
||||
path: '',
|
||||
});
|
||||
tree.write('eslint.config.js', 'module.exports = {};');
|
||||
tree.write('eslint.config.cjs', 'module.exports = {};');
|
||||
tree.write(
|
||||
'apps/demo/eslint.config.js',
|
||||
`const baseConfig = require("../../eslint.config.js");
|
||||
'apps/demo/eslint.config.cjs',
|
||||
`const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
@ -331,11 +331,11 @@ module.exports = [
|
||||
needCompatFixup: true,
|
||||
});
|
||||
|
||||
expect(tree.read('apps/demo/eslint.config.js', 'utf-8'))
|
||||
expect(tree.read('apps/demo/eslint.config.cjs', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
||||
const js = require("@eslint/js");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
@ -362,10 +362,10 @@ module.exports = [
|
||||
|
||||
describe('replaceOverridesInLintConfig', () => {
|
||||
it('should replace overrides when using flat config', () => {
|
||||
tree.write('eslint.config.js', 'module.exports = {};');
|
||||
tree.write('eslint.config.cjs', 'module.exports = {};');
|
||||
tree.write(
|
||||
'apps/demo/eslint.config.js',
|
||||
`const baseConfig = require("../../eslint.config.js");
|
||||
'apps/demo/eslint.config.cjs',
|
||||
`const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
@ -428,11 +428,11 @@ module.exports = [
|
||||
},
|
||||
]);
|
||||
|
||||
expect(tree.read('apps/demo/eslint.config.js', 'utf-8'))
|
||||
expect(tree.read('apps/demo/eslint.config.cjs', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
||||
const js = require("@eslint/js");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
|
||||
@ -14,9 +14,10 @@ import {
|
||||
baseEsLintConfigFile,
|
||||
baseEsLintFlatConfigFile,
|
||||
ESLINT_CONFIG_FILENAMES,
|
||||
legacyBaseEsLintFlatConfigFile,
|
||||
} from '../../utils/config-file';
|
||||
import {
|
||||
getRootESLintFlatConfigFilename,
|
||||
eslintFlatConfigFilenames,
|
||||
useFlatConfig,
|
||||
} from '../../utils/flat-config';
|
||||
import { getInstalledEslintVersion } from '../../utils/version-utils';
|
||||
@ -50,6 +51,12 @@ export function findEslintFile(
|
||||
if (projectRoot === undefined && tree.exists(baseEsLintFlatConfigFile)) {
|
||||
return baseEsLintFlatConfigFile;
|
||||
}
|
||||
if (
|
||||
projectRoot === undefined &&
|
||||
tree.exists(legacyBaseEsLintFlatConfigFile)
|
||||
) {
|
||||
return legacyBaseEsLintFlatConfigFile;
|
||||
}
|
||||
projectRoot ??= '';
|
||||
for (const file of ESLINT_CONFIG_FILENAMES) {
|
||||
if (tree.exists(joinPathFragments(projectRoot, file))) {
|
||||
@ -188,10 +195,18 @@ export function addOverrideToLintConfig(
|
||||
const isBase =
|
||||
options.checkBaseConfig && findEslintFile(tree, root).includes('.base');
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(
|
||||
root,
|
||||
isBase ? baseEsLintFlatConfigFile : getRootESLintFlatConfigFilename(tree)
|
||||
);
|
||||
let fileName: string;
|
||||
if (isBase) {
|
||||
fileName = joinPathFragments(root, baseEsLintFlatConfigFile);
|
||||
} else {
|
||||
for (const f of eslintFlatConfigFilenames) {
|
||||
if (tree.exists(joinPathFragments(root, f))) {
|
||||
fileName = joinPathFragments(root, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const flatOverride = generateFlatOverride(override);
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
// Check if the provided override using legacy eslintrc properties or plugins, if so we need to add compat
|
||||
@ -235,7 +250,15 @@ export function updateOverrideInLintConfig(
|
||||
}
|
||||
|
||||
if (useFlatConfig(tree)) {
|
||||
fileName ??= joinPathFragments(root, getRootESLintFlatConfigFilename(tree));
|
||||
if (!fileName) {
|
||||
for (const f of eslintFlatConfigFilenames) {
|
||||
if (tree.exists(joinPathFragments(root, f))) {
|
||||
fileName = joinPathFragments(root, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
content = replaceOverride(content, root, lookup, update);
|
||||
tree.write(fileName, content);
|
||||
@ -282,11 +305,18 @@ export function lintConfigHasOverride(
|
||||
!fileName &&
|
||||
checkBaseConfig &&
|
||||
findEslintFile(tree, root).includes('.base');
|
||||
if (isBase) {
|
||||
fileName = joinPathFragments(root, baseEsLintFlatConfigFile);
|
||||
}
|
||||
if (useFlatConfig(tree)) {
|
||||
fileName ??= joinPathFragments(
|
||||
root,
|
||||
isBase ? baseEsLintFlatConfigFile : getRootESLintFlatConfigFilename(tree)
|
||||
);
|
||||
if (!fileName) {
|
||||
for (const f of eslintFlatConfigFilenames) {
|
||||
if (tree.exists(joinPathFragments(root, f))) {
|
||||
fileName = joinPathFragments(root, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const content = tree.read(fileName, 'utf8');
|
||||
return hasOverride(content, lookup);
|
||||
} else {
|
||||
@ -305,10 +335,13 @@ export function replaceOverridesInLintConfig(
|
||||
overrides: Linter.ConfigOverride<Linter.RulesRecord>[]
|
||||
) {
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(
|
||||
root,
|
||||
getRootESLintFlatConfigFilename(tree)
|
||||
);
|
||||
let fileName: string;
|
||||
for (const f of eslintFlatConfigFilenames) {
|
||||
if (tree.exists(joinPathFragments(root, f))) {
|
||||
fileName = joinPathFragments(root, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
// Check if any of the provided overrides using legacy eslintrc properties or plugins, if so we need to add compat
|
||||
if (overrides.some(overrideNeedsCompat)) {
|
||||
@ -341,10 +374,14 @@ export function addExtendsToLintConfig(
|
||||
): GeneratorCallback {
|
||||
if (useFlatConfig(tree)) {
|
||||
const pluginExtends: ts.SpreadElement[] = [];
|
||||
const fileName = joinPathFragments(
|
||||
root,
|
||||
getRootESLintFlatConfigFilename(tree)
|
||||
);
|
||||
let fileName: string;
|
||||
for (const f of eslintFlatConfigFilenames) {
|
||||
if (tree.exists(joinPathFragments(root, f))) {
|
||||
fileName = joinPathFragments(root, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let shouldImportEslintCompat = false;
|
||||
// assume eslint version is 9 if not found, as it's what we'd be generating by default
|
||||
const eslintVersion =
|
||||
@ -445,10 +482,13 @@ export function addPredefinedConfigToFlatLintConfig(
|
||||
if (!useFlatConfig(tree))
|
||||
throw new Error('Predefined configs can only be used with flat configs');
|
||||
|
||||
const fileName = joinPathFragments(
|
||||
root,
|
||||
getRootESLintFlatConfigFilename(tree)
|
||||
);
|
||||
let fileName: string;
|
||||
for (const f of eslintFlatConfigFilenames) {
|
||||
if (tree.exists(joinPathFragments(root, f))) {
|
||||
fileName = joinPathFragments(root, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
content = addImportToFlatConfig(content, moduleName, moduleImportPath);
|
||||
@ -468,10 +508,14 @@ export function addPluginsToLintConfig(
|
||||
) {
|
||||
const plugins = Array.isArray(plugin) ? plugin : [plugin];
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(
|
||||
root,
|
||||
getRootESLintFlatConfigFilename(tree)
|
||||
);
|
||||
let fileName: string;
|
||||
for (const f of eslintFlatConfigFilenames) {
|
||||
if (tree.exists(joinPathFragments(root, f))) {
|
||||
fileName = joinPathFragments(root, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
const mappedPlugins: { name: string; varName: string; imp: string }[] = [];
|
||||
plugins.forEach((name) => {
|
||||
@ -499,10 +543,14 @@ export function addIgnoresToLintConfig(
|
||||
ignorePatterns: string[]
|
||||
) {
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(
|
||||
root,
|
||||
getRootESLintFlatConfigFilename(tree)
|
||||
);
|
||||
let fileName: string;
|
||||
for (const f of eslintFlatConfigFilenames) {
|
||||
if (tree.exists(joinPathFragments(root, f))) {
|
||||
fileName = joinPathFragments(root, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const block = generateAst<ts.ObjectLiteralExpression>({
|
||||
ignores: ignorePatterns.map((path) => mapFilePath(path)),
|
||||
});
|
||||
|
||||
@ -124,6 +124,12 @@ describe('ast-utils', () => {
|
||||
]
|
||||
}).map(config => ({
|
||||
...config,
|
||||
files: [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.cts",
|
||||
"**/*.mts"
|
||||
],
|
||||
rules: {
|
||||
...config.rules
|
||||
}
|
||||
@ -182,7 +188,7 @@ describe('ast-utils', () => {
|
||||
|
||||
describe('addBlockToFlatConfigExport', () => {
|
||||
it('should inject block to the end of the file', () => {
|
||||
const content = `const baseConfig = require("../../eslint.config.js");
|
||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
@ -204,7 +210,7 @@ describe('ast-utils', () => {
|
||||
})
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const baseConfig = require("../../eslint.config.js");
|
||||
"const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
@ -228,7 +234,7 @@ describe('ast-utils', () => {
|
||||
});
|
||||
|
||||
it('should inject spread to the beginning of the file', () => {
|
||||
const content = `const baseConfig = require("../../eslint.config.js");
|
||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
@ -246,7 +252,7 @@ describe('ast-utils', () => {
|
||||
{ insertAtTheEnd: false }
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const baseConfig = require("../../eslint.config.js");
|
||||
"const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...config,
|
||||
|
||||
@ -266,7 +272,7 @@ describe('ast-utils', () => {
|
||||
|
||||
describe('addImportToFlatConfig', () => {
|
||||
it('should inject import if not found', () => {
|
||||
const content = `const baseConfig = require("../../eslint.config.js");
|
||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
@ -285,7 +291,7 @@ describe('ast-utils', () => {
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const varName = require("@myorg/awesome-config");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
@ -302,7 +308,7 @@ describe('ast-utils', () => {
|
||||
|
||||
it('should update import if already found', () => {
|
||||
const content = `const { varName } = require("@myorg/awesome-config");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
@ -321,7 +327,7 @@ describe('ast-utils', () => {
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const { varName, otherName, someName } = require("@myorg/awesome-config");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
@ -338,7 +344,7 @@ describe('ast-utils', () => {
|
||||
|
||||
it('should not inject import if already exists', () => {
|
||||
const content = `const { varName, otherName } = require("@myorg/awesome-config");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
@ -360,7 +366,7 @@ describe('ast-utils', () => {
|
||||
|
||||
it('should not update import if already exists', () => {
|
||||
const content = `const varName = require("@myorg/awesome-config");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
@ -409,7 +415,7 @@ describe('ast-utils', () => {
|
||||
|
||||
describe('addCompatToFlatConfig', () => {
|
||||
it('should add compat to config', () => {
|
||||
const content = `const baseConfig = require("../../eslint.config.js");
|
||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
{
|
||||
@ -425,7 +431,7 @@ describe('ast-utils', () => {
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
||||
const js = require("@eslint/js");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
@ -446,7 +452,7 @@ describe('ast-utils', () => {
|
||||
});
|
||||
|
||||
it('should add only partially compat to config if parts exist', () => {
|
||||
const content = `const baseConfig = require("../../eslint.config.js");
|
||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
||||
const js = require("@eslint/js");
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
@ -462,7 +468,7 @@ describe('ast-utils', () => {
|
||||
const result = addFlatCompatToFlatConfig(content);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
const js = require("@eslint/js");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
@ -485,7 +491,7 @@ describe('ast-utils', () => {
|
||||
|
||||
it('should not add compat to config if exist', () => {
|
||||
const content = `const FlatCompat = require("@eslint/eslintrc");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
const js = require("@eslint/js");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
@ -512,7 +518,7 @@ describe('ast-utils', () => {
|
||||
describe('removeOverridesFromLintConfig', () => {
|
||||
it('should remove all rules from config', () => {
|
||||
const content = `const FlatCompat = require("@eslint/eslintrc");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
const js = require("@eslint/js");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
@ -552,7 +558,7 @@ describe('ast-utils', () => {
|
||||
const result = removeOverridesFromLintConfig(content);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const FlatCompat = require("@eslint/eslintrc");
|
||||
const baseConfig = require("../../eslint.config.js");
|
||||
const baseConfig = require("../../eslint.config.cjs");
|
||||
const js = require("@eslint/js");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
@ -568,7 +574,7 @@ describe('ast-utils', () => {
|
||||
});
|
||||
|
||||
it('should remove all rules from starting with first', () => {
|
||||
const content = `const baseConfig = require("../../eslint.config.js");
|
||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
@ -599,7 +605,7 @@ describe('ast-utils', () => {
|
||||
];`;
|
||||
const result = removeOverridesFromLintConfig(content);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const baseConfig = require("../../eslint.config.js");
|
||||
"const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
];"
|
||||
@ -609,7 +615,7 @@ describe('ast-utils', () => {
|
||||
|
||||
describe('replaceOverride', () => {
|
||||
it('should find and replace rules in override', () => {
|
||||
const content = `const baseConfig = require("../../eslint.config.js");
|
||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
@ -651,7 +657,7 @@ module.exports = [
|
||||
})
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const baseConfig = require("../../eslint.config.js");
|
||||
"const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
@ -686,7 +692,7 @@ module.exports = [
|
||||
});
|
||||
|
||||
it('should append rules in override', () => {
|
||||
const content = `const baseConfig = require("../../eslint.config.js");
|
||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
@ -722,7 +728,7 @@ module.exports = [
|
||||
})
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const baseConfig = require("../../eslint.config.js");
|
||||
"const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
@ -749,7 +755,7 @@ module.exports = [
|
||||
});
|
||||
|
||||
it('should work for compat overrides', () => {
|
||||
const content = `const baseConfig = require("../../eslint.config.js");
|
||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
...compat.config({ extends: ["plugin:@nx/typescript"] }).map(config => ({
|
||||
@ -777,7 +783,7 @@ module.exports = [
|
||||
})
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const baseConfig = require("../../eslint.config.js");
|
||||
"const baseConfig = require("../../eslint.config.cjs");
|
||||
|
||||
module.exports = [
|
||||
...compat.config({ extends: ["plugin:@nx/typescript"] }).map(config => ({
|
||||
|
||||
@ -1002,12 +1002,40 @@ export function generateFlatOverride(
|
||||
}
|
||||
|
||||
// At this point we are applying the flat config compat tooling to the override
|
||||
const { excludedFiles, parser, parserOptions, rules, files, ...rest } =
|
||||
let { excludedFiles, parser, parserOptions, rules, files, ...rest } =
|
||||
override;
|
||||
|
||||
const objectLiteralElements: ts.ObjectLiteralElementLike[] = [
|
||||
ts.factory.createSpreadAssignment(ts.factory.createIdentifier('config')),
|
||||
];
|
||||
|
||||
// If converting the JS rule, then we need to match ESLint default and also include .cjs and .mjs files.
|
||||
if (
|
||||
(Array.isArray(rest.extends) &&
|
||||
rest.extends.includes('plugin:@nx/javascript')) ||
|
||||
rest.extends === 'plugin:@nx/javascript'
|
||||
) {
|
||||
const newFiles = new Set(files);
|
||||
newFiles.add('**/*.js');
|
||||
newFiles.add('**/*.jsx');
|
||||
newFiles.add('**/*.cjs');
|
||||
newFiles.add('**/*.mjs');
|
||||
files = Array.from(newFiles);
|
||||
}
|
||||
// If converting the TS rule, then we need to match ESLint default and also include .cts and .mts files.
|
||||
if (
|
||||
(Array.isArray(rest.extends) &&
|
||||
rest.extends.includes('plugin:@nx/typescript')) ||
|
||||
rest.extends === 'plugin:@nx/typescript'
|
||||
) {
|
||||
const newFiles = new Set(files);
|
||||
newFiles.add('**/*.ts');
|
||||
newFiles.add('**/*.tsx');
|
||||
newFiles.add('**/*.cts');
|
||||
newFiles.add('**/*.mts');
|
||||
files = Array.from(newFiles);
|
||||
}
|
||||
|
||||
addTSObjectProperty(objectLiteralElements, 'files', files);
|
||||
addTSObjectProperty(objectLiteralElements, 'excludedFiles', excludedFiles);
|
||||
|
||||
|
||||
@ -0,0 +1,187 @@
|
||||
import { type Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import migration from './add-file-extensions-to-overrides';
|
||||
|
||||
describe('add-file-extensions-to-overrides', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
});
|
||||
|
||||
it('should add .cjs, .mjs, .cts, .mts file extensions to overrides converted using convert-to-flat-config', async () => {
|
||||
tree.write(
|
||||
'eslint.config.js',
|
||||
`const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@nx/typescript'],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
})),
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@nx/javascript'],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
})),
|
||||
];`
|
||||
);
|
||||
|
||||
await migration(tree);
|
||||
|
||||
const updated = tree.read('eslint.config.js', 'utf-8');
|
||||
expect(updated).toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@nx/typescript'],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
})),
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@nx/javascript'],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
})),
|
||||
];"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle duplicates', async () => {
|
||||
tree.write(
|
||||
'eslint.config.js',
|
||||
`const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@nx/javascript'],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.mjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
})),
|
||||
];`
|
||||
);
|
||||
|
||||
await migration(tree);
|
||||
|
||||
const updated = tree.read('eslint.config.js', 'utf-8');
|
||||
expect(updated).toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@nx/javascript'],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
})),
|
||||
];"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not update if plugin:@nx/javascript and plugin:@nx/typescript are not used', async () => {
|
||||
const original = `const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@acme/foo'],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
})),
|
||||
...compat
|
||||
.config({
|
||||
extends: ['plugin:@acme/bar'],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
rules: {
|
||||
...config.rules,
|
||||
},
|
||||
})),
|
||||
];`;
|
||||
tree.write('eslint.config.js', original);
|
||||
|
||||
await migration(tree);
|
||||
|
||||
const updated = tree.read('eslint.config.js', 'utf-8');
|
||||
expect(updated).toEqual(original);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,97 @@
|
||||
import { type Tree } from '@nx/devkit';
|
||||
import * as ts from 'typescript';
|
||||
import { findNodes, replaceChange } from '@nx/js';
|
||||
|
||||
export default async function (tree: Tree): Promise<void> {
|
||||
let rootConfig: string;
|
||||
|
||||
// NOTE: we don't support generating ESM base config currently so they are not handled.
|
||||
for (const candidate of ['eslint.config.js', 'eslint.config.cjs']) {
|
||||
if (tree.exists(candidate)) {
|
||||
rootConfig = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rootConfig) return;
|
||||
|
||||
updateOverrideFileExtensions(
|
||||
tree,
|
||||
rootConfig,
|
||||
'plugin:@nx/typescript',
|
||||
[`'**/*.ts'`, `'**/*.tsx'`],
|
||||
[`'**/*.cts'`, `'**/*.mts'`]
|
||||
);
|
||||
|
||||
updateOverrideFileExtensions(
|
||||
tree,
|
||||
rootConfig,
|
||||
'plugin:@nx/javascript',
|
||||
[`'**/*.js'`, `'**/*.jsx'`],
|
||||
[`'**/*.cjs'`, `'**/*.mjs'`]
|
||||
);
|
||||
}
|
||||
|
||||
function updateOverrideFileExtensions(
|
||||
tree: Tree,
|
||||
configFile: string,
|
||||
plugin: string,
|
||||
matchingExts: string[],
|
||||
newExts: string[]
|
||||
): void {
|
||||
const content = tree.read(configFile, 'utf-8');
|
||||
const source = ts.createSourceFile(
|
||||
'',
|
||||
content,
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
ts.ScriptKind.JS
|
||||
);
|
||||
let compatNode: ts.SpreadElement;
|
||||
|
||||
const spreadElementNodes = findNodes(
|
||||
source,
|
||||
ts.SyntaxKind.SpreadElement
|
||||
) as ts.SpreadElement[];
|
||||
for (const a of spreadElementNodes) {
|
||||
const assignmentNodes = findNodes(
|
||||
a,
|
||||
ts.SyntaxKind.PropertyAssignment
|
||||
) as ts.PropertyAssignment[];
|
||||
if (assignmentNodes.length === 0) continue;
|
||||
for (const b of assignmentNodes) {
|
||||
if (
|
||||
b.name.getText() === 'extends' &&
|
||||
b.initializer.getText().includes(plugin)
|
||||
) {
|
||||
compatNode = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compatNode) {
|
||||
const arrayNodes = findNodes(
|
||||
compatNode,
|
||||
ts.SyntaxKind.ArrayLiteralExpression
|
||||
) as ts.ArrayLiteralExpression[];
|
||||
for (const a of arrayNodes) {
|
||||
if (
|
||||
matchingExts.every((ext) => a.elements.some((e) => e.getText() === ext))
|
||||
) {
|
||||
const exts = new Set(a.elements.map((e) => e.getText()));
|
||||
for (const ext of newExts) {
|
||||
exts.add(ext);
|
||||
}
|
||||
replaceChange(
|
||||
tree,
|
||||
source,
|
||||
configFile,
|
||||
a.getStart(a.getSourceFile()),
|
||||
`[${Array.from(exts).join(', ')}]`,
|
||||
a.getText()
|
||||
).getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,13 +73,13 @@ describe('@nx/eslint/plugin', () => {
|
||||
|
||||
// TODO(leo): dynamic import of the flat config fails with jest:
|
||||
// "TypeError: A dynamic import callback was invoked without --experimental-vm-modules"
|
||||
// mocking the "eslint.config.js" file import is not working, figure out if there's a way
|
||||
// mocking the "eslint.config.cjs" file import is not working, figure out if there's a way
|
||||
it.skip('should not create a node for a root level eslint config when accompanied by a project.json, if no src directory is present', async () => {
|
||||
createFiles({
|
||||
'eslint.config.js': `module.exports = {};`,
|
||||
'eslint.config.cjs': `module.exports = {};`,
|
||||
'project.json': `{}`,
|
||||
});
|
||||
// NOTE: It should set ESLINT_USE_FLAT_CONFIG to true because of the use of eslint.config.js
|
||||
// NOTE: It should set ESLINT_USE_FLAT_CONFIG to true because of the use of eslint.config.cjs
|
||||
expect(
|
||||
await invokeCreateNodesOnMatchingFiles(context, { targetName: 'lint' })
|
||||
).toMatchInlineSnapshot(`
|
||||
|
||||
@ -19,7 +19,9 @@ export const ESLINT_CONFIG_FILENAMES = [
|
||||
];
|
||||
|
||||
export const baseEsLintConfigFile = '.eslintrc.base.json';
|
||||
export const baseEsLintFlatConfigFile = 'eslint.base.config.js';
|
||||
export const baseEsLintFlatConfigFile = 'eslint.base.config.cjs';
|
||||
// Make sure we can handle previous file extension as well for migrations or custom generators.
|
||||
export const legacyBaseEsLintFlatConfigFile = 'eslint.base.config.js';
|
||||
|
||||
export function isFlatConfig(configFilePath: string): boolean {
|
||||
const configFileName = basename(configFilePath);
|
||||
|
||||
@ -3,8 +3,8 @@ import { gte } from 'semver';
|
||||
|
||||
// todo: add support for eslint.config.mjs,
|
||||
export const eslintFlatConfigFilenames = [
|
||||
'eslint.config.js',
|
||||
'eslint.config.cjs',
|
||||
'eslint.config.js',
|
||||
];
|
||||
|
||||
export function getRootESLintFlatConfigFilename(tree: Tree): string {
|
||||
|
||||
@ -624,7 +624,7 @@ describe('app', () => {
|
||||
describe('--linter', () => {
|
||||
describe('default (eslint)', () => {
|
||||
it('should add flat config as needed', async () => {
|
||||
tree.write('eslint.config.js', '');
|
||||
tree.write('eslint.config.cjs', '');
|
||||
const name = uniq();
|
||||
|
||||
await applicationGenerator(tree, {
|
||||
@ -632,12 +632,12 @@ describe('app', () => {
|
||||
style: 'css',
|
||||
});
|
||||
|
||||
expect(tree.read(`${name}/eslint.config.js`, 'utf-8'))
|
||||
expect(tree.read(`${name}/eslint.config.cjs`, 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../eslint.config.js');
|
||||
const baseConfig = require('../eslint.config.cjs');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
|
||||
@ -96,16 +96,16 @@ describe('updateEslint', () => {
|
||||
});
|
||||
|
||||
it('should update the flat config', async () => {
|
||||
tree.write('eslint.config.js', `module.exports = []`);
|
||||
tree.write('eslint.config.cjs', `module.exports = []`);
|
||||
|
||||
await addLinting(tree, schema);
|
||||
|
||||
expect(tree.read(`${schema.appProjectRoot}/eslint.config.js`, 'utf-8'))
|
||||
expect(tree.read(`${schema.appProjectRoot}/eslint.config.cjs`, 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
||||
const js = require("@eslint/js");
|
||||
const nx = require("@nx/eslint-plugin");
|
||||
const baseConfig = require("../eslint.config.js");
|
||||
const baseConfig = require("../eslint.config.cjs");
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
|
||||
@ -35,7 +35,7 @@ exports[`app generated files content - as-provided - my-app general application
|
||||
exports[`app generated files content - as-provided - my-app general application should configure eslint correctly (flat config) 1`] = `
|
||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const baseConfig = require('../eslint.config.js');
|
||||
const baseConfig = require('../eslint.config.cjs');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
@ -394,7 +394,7 @@ exports[`app generated files content - as-provided - myApp general application s
|
||||
exports[`app generated files content - as-provided - myApp general application should configure eslint correctly (flat config) 1`] = `
|
||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const js = require('@eslint/js');
|
||||
const baseConfig = require('../eslint.config.js');
|
||||
const baseConfig = require('../eslint.config.cjs');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
|
||||
@ -66,7 +66,7 @@ describe('app', () => {
|
||||
});
|
||||
|
||||
it('should configure eslint correctly (flat config)', async () => {
|
||||
tree.write('eslint.config.js', '');
|
||||
tree.write('eslint.config.cjs', '');
|
||||
|
||||
await applicationGenerator(tree, {
|
||||
directory: name,
|
||||
@ -74,7 +74,7 @@ describe('app', () => {
|
||||
});
|
||||
|
||||
expect(
|
||||
tree.read(`${name}/eslint.config.js`, 'utf-8')
|
||||
tree.read(`${name}/eslint.config.cjs`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
||||
@ -91,7 +91,7 @@ function createNxJson(
|
||||
].filter(Boolean)
|
||||
: []),
|
||||
...(eslintProjectConfigFile
|
||||
? ['!{projectRoot}/.eslintrc.json', '!{projectRoot}/eslint.config.js']
|
||||
? ['!{projectRoot}/.eslintrc.json', '!{projectRoot}/eslint.config.cjs']
|
||||
: []),
|
||||
].filter(Boolean),
|
||||
};
|
||||
@ -124,8 +124,8 @@ function createNxJson(
|
||||
if (fileExists(join(repoRoot, '.eslintrc.json'))) {
|
||||
inputs.push('{workspaceRoot}/.eslintrc.json');
|
||||
}
|
||||
if (fileExists(join(repoRoot, 'eslint.config.js'))) {
|
||||
inputs.push('{workspaceRoot}/eslint.config.js');
|
||||
if (fileExists(join(repoRoot, 'eslint.config.cjs'))) {
|
||||
inputs.push('{workspaceRoot}/eslint.config.cjs');
|
||||
}
|
||||
nxJson.targetDefaults.lint = {
|
||||
...nxJson.targetDefaults.lint,
|
||||
@ -230,7 +230,9 @@ function projectHasEslintConfig(
|
||||
): boolean {
|
||||
return (
|
||||
fileExists(join(project.root, '.eslintrc.json')) ||
|
||||
fileExists(join(project.root, 'eslint.config.js'))
|
||||
fileExists(join(project.root, 'eslint.config.js')) ||
|
||||
fileExists(join(project.root, 'eslint.config.mjs')) ||
|
||||
fileExists(join(project.root, 'eslint.config.cjs'))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -10923,7 +10923,7 @@ describe('createNxReleaseConfig()', () => {
|
||||
'group-1': {
|
||||
projects: 'nx',
|
||||
versionPlans: {
|
||||
ignorePatternsForPlanCheck: ['**/eslint.config.js'],
|
||||
ignorePatternsForPlanCheck: ['**/eslint.config.cjs'],
|
||||
},
|
||||
},
|
||||
'group-2': {
|
||||
@ -11076,7 +11076,7 @@ describe('createNxReleaseConfig()', () => {
|
||||
},
|
||||
"versionPlans": {
|
||||
"ignorePatternsForPlanCheck": [
|
||||
"**/eslint.config.js",
|
||||
"**/eslint.config.cjs",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@ -309,52 +309,6 @@ export default {...nxPreset};
|
||||
|
||||
tasks.push(await addE2E(tree, options));
|
||||
|
||||
// If the project package.json uses type module, and the project uses flat eslint config, we need to make sure the eslint config uses an explicit .cjs extension
|
||||
// TODO: This could be re-evaluated once we support ESM in eslint configs
|
||||
if (
|
||||
tree.exists(joinPathFragments(options.projectRoot, 'package.json')) &&
|
||||
tree.exists(joinPathFragments(options.projectRoot, 'eslint.config.js'))
|
||||
) {
|
||||
const pkgJson = readJson(
|
||||
tree,
|
||||
joinPathFragments(options.projectRoot, 'package.json')
|
||||
);
|
||||
if (pkgJson.type === 'module') {
|
||||
tree.rename(
|
||||
joinPathFragments(options.projectRoot, 'eslint.config.js'),
|
||||
joinPathFragments(options.projectRoot, 'eslint.config.cjs')
|
||||
);
|
||||
visitNotIgnoredFiles(tree, options.projectRoot, (file) => {
|
||||
if (file.endsWith('eslint.config.js')) {
|
||||
// Replace any extends on the eslint config to use the .cjs extension
|
||||
const content = tree.read(file).toString();
|
||||
if (content.includes('eslint.config')) {
|
||||
tree.write(
|
||||
file,
|
||||
content
|
||||
.replace(/eslint\.config'/g, `eslint.config.cjs'`)
|
||||
.replace(/eslint\.config"/g, `eslint.config.cjs"`)
|
||||
.replace(/eslint\.config\.js/g, `eslint.config.cjs`)
|
||||
);
|
||||
}
|
||||
|
||||
// If there is no sibling package.json with type commonjs, we need to rename the .js files to .cjs
|
||||
const siblingPackageJsonPath = joinPathFragments(
|
||||
dirname(file),
|
||||
'package.json'
|
||||
);
|
||||
if (tree.exists(siblingPackageJsonPath)) {
|
||||
const siblingPkgJson = readJson(tree, siblingPackageJsonPath);
|
||||
if (siblingPkgJson.type === 'module') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
tree.rename(file, file.replace('.js', '.cjs'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addViteTempFilesToGitIgnore(tree);
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
|
||||
@ -279,7 +279,7 @@ exports[`library should ignore test files in tsconfig.lib.json 1`] = `
|
||||
|
||||
exports[`library should support eslint flat config 1`] = `
|
||||
"const vue = require('eslint-plugin-vue');
|
||||
const baseConfig = require('../eslint.config.js');
|
||||
const baseConfig = require('../eslint.config.cjs');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
|
||||
@ -151,7 +151,7 @@ describe('library', () => {
|
||||
|
||||
it('should support eslint flat config', async () => {
|
||||
tree.write(
|
||||
'eslint.config.js',
|
||||
'eslint.config.cjs',
|
||||
`const { FlatCompat } = require('@eslint/eslintrc');
|
||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||
const js = require('@eslint/js');
|
||||
@ -208,10 +208,10 @@ module.exports = [
|
||||
|
||||
await libraryGenerator(tree, defaultSchema);
|
||||
|
||||
const eslintJson = tree.read('my-lib/eslint.config.js', 'utf-8');
|
||||
const eslintJson = tree.read('my-lib/eslint.config.cjs', 'utf-8');
|
||||
expect(eslintJson).toMatchSnapshot();
|
||||
// assert **/*.vue was added to override in base eslint config
|
||||
const eslintBaseJson = tree.read('eslint.config.js', 'utf-8');
|
||||
const eslintBaseJson = tree.read('eslint.config.cjs', 'utf-8');
|
||||
expect(eslintBaseJson).toContain(
|
||||
`files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue'],`
|
||||
);
|
||||
|
||||
@ -140,7 +140,7 @@ exports[`moveProjectConfiguration should rename the project correctly except for
|
||||
"default",
|
||||
"{workspaceRoot}/.eslintrc.json",
|
||||
"{workspaceRoot}/.eslintignore",
|
||||
"{workspaceRoot}/eslint.config.js",
|
||||
"{workspaceRoot}/eslint.config.cjs",
|
||||
],
|
||||
"options": {},
|
||||
"outputs": [
|
||||
|
||||
@ -199,7 +199,7 @@ describe('moveProjectConfiguration', () => {
|
||||
'default',
|
||||
'{workspaceRoot}/.eslintrc.json',
|
||||
'{workspaceRoot}/.eslintignore',
|
||||
'{workspaceRoot}/eslint.config.js',
|
||||
'{workspaceRoot}/eslint.config.cjs',
|
||||
],
|
||||
executor: '@nx/eslint:lint',
|
||||
outputs: ['{options.outputFile}'],
|
||||
|
||||
@ -22,6 +22,7 @@ export function moveProjectFiles(
|
||||
'.babelrc',
|
||||
'.eslintrc.json',
|
||||
'eslint.config.js',
|
||||
'eslint.config.cjs',
|
||||
/^jest\.config\.((app|lib)\.)?[jt]s$/,
|
||||
'vite.config.ts',
|
||||
/^webpack.*\.js$/,
|
||||
|
||||
@ -243,7 +243,7 @@ describe('updateEslint (flat config)', () => {
|
||||
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.delete('.eslintrc.json');
|
||||
tree.write('eslint.config.js', `module.exports = [];`);
|
||||
tree.write('eslint.config.cjs', `module.exports = [];`);
|
||||
});
|
||||
|
||||
it('should handle config not existing', async () => {
|
||||
@ -267,16 +267,16 @@ describe('updateEslint (flat config)', () => {
|
||||
convertToFlat(tree, 'my-lib');
|
||||
// This step is usually handled elsewhere
|
||||
tree.rename(
|
||||
'my-lib/eslint.config.js',
|
||||
'shared/my-destination/eslint.config.js'
|
||||
'my-lib/eslint.config.cjs',
|
||||
'shared/my-destination/eslint.config.cjs'
|
||||
);
|
||||
const projectConfig = readProjectConfiguration(tree, 'my-lib');
|
||||
|
||||
updateEslintConfig(tree, schema, projectConfig);
|
||||
|
||||
expect(
|
||||
tree.read('shared/my-destination/eslint.config.js', 'utf-8')
|
||||
).toEqual(expect.stringContaining(`require('../../eslint.config.js')`));
|
||||
tree.read('shared/my-destination/eslint.config.cjs', 'utf-8')
|
||||
).toEqual(expect.stringContaining(`require('../../eslint.config.cjs')`));
|
||||
});
|
||||
|
||||
it('should update config extends path when project is moved from subdirectory', async () => {
|
||||
@ -287,7 +287,7 @@ describe('updateEslint (flat config)', () => {
|
||||
});
|
||||
convertToFlat(tree, 'api/test');
|
||||
// This step is usually handled elsewhere
|
||||
tree.rename('api/test/eslint.config.js', 'test/eslint.config.js');
|
||||
tree.rename('api/test/eslint.config.cjs', 'test/eslint.config.cjs');
|
||||
|
||||
const projectConfig = readProjectConfiguration(tree, 'api-test');
|
||||
|
||||
@ -302,8 +302,8 @@ describe('updateEslint (flat config)', () => {
|
||||
|
||||
updateEslintConfig(tree, newSchema, projectConfig);
|
||||
|
||||
expect(tree.read('test/eslint.config.js', 'utf-8')).toEqual(
|
||||
expect.stringContaining(`require('../eslint.config.js')`)
|
||||
expect(tree.read('test/eslint.config.cjs', 'utf-8')).toEqual(
|
||||
expect.stringContaining(`require('../eslint.config.cjs')`)
|
||||
);
|
||||
});
|
||||
|
||||
@ -316,15 +316,15 @@ describe('updateEslint (flat config)', () => {
|
||||
convertToFlat(tree, 'my-lib', { hasParser: true });
|
||||
// This step is usually handled elsewhere
|
||||
tree.rename(
|
||||
'my-lib/eslint.config.js',
|
||||
'shared/my-destination/eslint.config.js'
|
||||
'my-lib/eslint.config.cjs',
|
||||
'shared/my-destination/eslint.config.cjs'
|
||||
);
|
||||
const projectConfig = readProjectConfiguration(tree, 'my-lib');
|
||||
|
||||
updateEslintConfig(tree, schema, projectConfig);
|
||||
|
||||
expect(
|
||||
tree.read('shared/my-destination/eslint.config.js', 'utf-8')
|
||||
tree.read('shared/my-destination/eslint.config.cjs', 'utf-8')
|
||||
).toEqual(
|
||||
expect.stringContaining(
|
||||
`project: ["shared/my-destination/tsconfig.*?.json"]`
|
||||
@ -346,15 +346,15 @@ describe('updateEslint (flat config)', () => {
|
||||
});
|
||||
// This step is usually handled elsewhere
|
||||
tree.rename(
|
||||
'my-lib/eslint.config.js',
|
||||
'shared/my-destination/eslint.config.js'
|
||||
'my-lib/eslint.config.cjs',
|
||||
'shared/my-destination/eslint.config.cjs'
|
||||
);
|
||||
const projectConfig = readProjectConfiguration(tree, 'my-lib');
|
||||
|
||||
updateEslintConfig(tree, schema, projectConfig);
|
||||
|
||||
expect(
|
||||
tree.read('shared/my-destination/eslint.config.js', 'utf-8')
|
||||
tree.read('shared/my-destination/eslint.config.cjs', 'utf-8')
|
||||
).toEqual(
|
||||
expect.stringContaining(
|
||||
`project: ["shared/my-destination/tsconfig.*?.json", "shared/my-destination/${storybookProject}"]`
|
||||
@ -372,15 +372,15 @@ describe('updateEslint (flat config)', () => {
|
||||
convertToFlat(tree, 'my-lib', { hasParser: true, isString: true });
|
||||
// This step is usually handled elsewhere
|
||||
tree.rename(
|
||||
'my-lib/eslint.config.js',
|
||||
'shared/my-destination/eslint.config.js'
|
||||
'my-lib/eslint.config.cjs',
|
||||
'shared/my-destination/eslint.config.cjs'
|
||||
);
|
||||
const projectConfig = readProjectConfiguration(tree, 'my-lib');
|
||||
|
||||
updateEslintConfig(tree, schema, projectConfig);
|
||||
|
||||
expect(
|
||||
tree.read('shared/my-destination/eslint.config.js', 'utf-8')
|
||||
tree.read('shared/my-destination/eslint.config.cjs', 'utf-8')
|
||||
).toEqual(
|
||||
expect.stringContaining(
|
||||
`project: "shared/my-destination/tsconfig.*?.json"`
|
||||
@ -417,9 +417,9 @@ function convertToFlat(
|
||||
}
|
||||
|
||||
tree.write(
|
||||
joinPathFragments(path, 'eslint.config.js'),
|
||||
joinPathFragments(path, 'eslint.config.cjs'),
|
||||
`const { FlatCompat } = require("@eslint/eslintrc");
|
||||
const baseConfig = require("${offset}eslint.config.js");
|
||||
const baseConfig = require("${offset}eslint.config.cjs");
|
||||
const js = require("@eslint/js");
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
|
||||
@ -15,8 +15,10 @@ export function updateEslintConfig(
|
||||
if (
|
||||
!tree.exists('.eslintrc.json') &&
|
||||
!tree.exists('eslint.config.js') &&
|
||||
!tree.exists('eslint.config.cjs') &&
|
||||
!tree.exists('.eslintrc.base.json') &&
|
||||
!tree.exists('eslint.base.config.js')
|
||||
!tree.exists('eslint.base.config.js') &&
|
||||
!tree.exists('eslint.base.config.cjs')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -51,7 +51,11 @@ export function updateFilesForRootProjects(
|
||||
if (!allowedExt.includes(ext)) {
|
||||
continue;
|
||||
}
|
||||
if (file === '.eslintrc.json' || file === 'eslint.config.js') {
|
||||
if (
|
||||
file === '.eslintrc.json' ||
|
||||
file === 'eslint.config.js' ||
|
||||
file === 'eslint.config.cjs'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -104,7 +108,11 @@ export function updateFilesForNonRootProjects(
|
||||
if (!allowedExt.includes(ext)) {
|
||||
continue;
|
||||
}
|
||||
if (file === '.eslintrc.json' || file === 'eslint.config.js') {
|
||||
if (
|
||||
file === '.eslintrc.json' ||
|
||||
file === 'eslint.config.cjs' ||
|
||||
file === 'eslint.config.js'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user