feat(core): Add ESM support for Eslint config file (#29613)
This pull request includes changes to migrate ESLint configuration files
from CommonJS (`.cjs`) to ECMAScript modules (`.mjs`) as the default.
### ESLint Configuration Generation Changes
The changes also ensure consistent generated eslint configs based on the
base eslint config.
- If the workspace root has an `eslint.config.cjs` or `eslint.config.js`
with `module.exports`. When you create a library or application it will
generate an accompanying config at path
`{projectRoot}/eslint.config.cjs` of the same format.
- If the workspace root has an `eslint.config.mjs` or
`eslint.config.mjs` with `export default`. When you create a library or
application it will generate an accompanying config at path
`{projectRoot}/eslint.config.mjs`.
- If no eslint config is found at the workspace root one will be created
`eslint.config.mjs`
This commit is contained in:
parent
a468d72c7f
commit
dec21662b6
@ -39,7 +39,7 @@ describe('Move Angular Project', () => {
|
|||||||
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.app.json`);
|
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.app.json`);
|
||||||
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.json`);
|
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.json`);
|
||||||
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.spec.json`);
|
expect(moveOutput).toContain(`CREATE ${newPath}/tsconfig.spec.json`);
|
||||||
expect(moveOutput).toContain(`CREATE ${newPath}/eslint.config.cjs`);
|
expect(moveOutput).toContain(`CREATE ${newPath}/eslint.config.mjs`);
|
||||||
expect(moveOutput).toContain(`CREATE ${newPath}/public/favicon.ico`);
|
expect(moveOutput).toContain(`CREATE ${newPath}/public/favicon.ico`);
|
||||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/index.html`);
|
expect(moveOutput).toContain(`CREATE ${newPath}/src/index.html`);
|
||||||
expect(moveOutput).toContain(`CREATE ${newPath}/src/main.ts`);
|
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 () => {
|
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
|
// disable the prefer-standalone rule for app1 which is not standalone
|
||||||
let app1EslintConfig = readFile(`${app1}/eslint.config.cjs`);
|
let app1EslintConfig = readFile(`${app1}/eslint.config.mjs`);
|
||||||
app1EslintConfig = app1EslintConfig.replace(
|
app1EslintConfig = app1EslintConfig.replace(
|
||||||
`'@angular-eslint/directive-selector': [`,
|
`'@angular-eslint/directive-selector': [`,
|
||||||
`'@angular-eslint/prefer-standalone': 'off',
|
`'@angular-eslint/prefer-standalone': 'off',
|
||||||
'@angular-eslint/directive-selector': [`
|
'@angular-eslint/directive-selector': [`
|
||||||
);
|
);
|
||||||
updateFile(`${app1}/eslint.config.cjs`, app1EslintConfig);
|
updateFile(`${app1}/eslint.config.mjs`, app1EslintConfig);
|
||||||
|
|
||||||
// check apps and lib pass linting for initial generated code
|
// check apps and lib pass linting for initial generated code
|
||||||
runCLI(`run-many --target lint --projects=${app1},${lib1} --parallel`);
|
runCLI(`run-many --target lint --projects=${app1},${lib1} --parallel`);
|
||||||
|
|||||||
@ -150,10 +150,10 @@ describe('Linter (legacy)', () => {
|
|||||||
env: { NX_ADD_PLUGINS: 'false' },
|
env: { NX_ADD_PLUGINS: 'false' },
|
||||||
});
|
});
|
||||||
checkFilesExist(
|
checkFilesExist(
|
||||||
'eslint.config.cjs',
|
'eslint.config.mjs',
|
||||||
`apps/${myapp}/eslint.config.cjs`,
|
`apps/${myapp}/eslint.config.mjs`,
|
||||||
`libs/${mylib}/eslint.config.cjs`,
|
`libs/${mylib}/eslint.config.mjs`,
|
||||||
`libs/${mylib2}/eslint.config.cjs`
|
`libs/${mylib2}/eslint.config.mjs`
|
||||||
);
|
);
|
||||||
checkFilesDoNotExist(
|
checkFilesDoNotExist(
|
||||||
'.eslintrc.json',
|
'.eslintrc.json',
|
||||||
@ -164,12 +164,12 @@ describe('Linter (legacy)', () => {
|
|||||||
|
|
||||||
// move eslint.config one step up
|
// move eslint.config one step up
|
||||||
// to test the absence of the flat eslint config in the project root folder
|
// to test the absence of the flat eslint config in the project root folder
|
||||||
renameFile(`libs/${mylib2}/eslint.config.cjs`, `libs/eslint.config.cjs`);
|
renameFile(`libs/${mylib2}/eslint.config.mjs`, `libs/eslint.config.mjs`);
|
||||||
updateFile(
|
updateFile(
|
||||||
`libs/eslint.config.cjs`,
|
`libs/eslint.config.mjs`,
|
||||||
readFile(`libs/eslint.config.cjs`).replace(
|
readFile(`libs/eslint.config.mjs`).replace(
|
||||||
`../../eslint.config.cjs`,
|
`../../eslint.config.mjs`,
|
||||||
`../eslint.config.cjs`
|
`../eslint.config.mjs`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -202,9 +202,9 @@ describe('Linter (legacy)', () => {
|
|||||||
env: { NX_ADD_PLUGINS: 'false' },
|
env: { NX_ADD_PLUGINS: 'false' },
|
||||||
});
|
});
|
||||||
checkFilesExist(
|
checkFilesExist(
|
||||||
'eslint.config.cjs',
|
'eslint.config.mjs',
|
||||||
`${mylib}/eslint.config.cjs`,
|
`${mylib}/eslint.config.mjs`,
|
||||||
'eslint.base.config.cjs'
|
'eslint.base.config.mjs'
|
||||||
);
|
);
|
||||||
checkFilesDoNotExist(
|
checkFilesDoNotExist(
|
||||||
'.eslintrc.json',
|
'.eslintrc.json',
|
||||||
|
|||||||
@ -615,8 +615,8 @@ describe('Linter', () => {
|
|||||||
runCLI(`generate @nx/js:lib ${jsLib} --linter eslint`);
|
runCLI(`generate @nx/js:lib ${jsLib} --linter eslint`);
|
||||||
|
|
||||||
checkFilesExist(
|
checkFilesExist(
|
||||||
`${reactLib}/eslint.config.cjs`,
|
`${reactLib}/eslint.config.mjs`,
|
||||||
`${jsLib}/eslint.config.cjs`
|
`${jsLib}/eslint.config.mjs`
|
||||||
);
|
);
|
||||||
checkFilesDoNotExist(
|
checkFilesDoNotExist(
|
||||||
`${reactLib}/.eslintrc.json`,
|
`${reactLib}/.eslintrc.json`,
|
||||||
|
|||||||
@ -26,7 +26,7 @@ exports[`Extra Nx Misc Tests task graph inputs should correctly expand dependent
|
|||||||
],
|
],
|
||||||
"lib-base-123": [
|
"lib-base-123": [
|
||||||
"libs/lib-base-123/README.md",
|
"libs/lib-base-123/README.md",
|
||||||
"libs/lib-base-123/eslint.config.cjs",
|
"libs/lib-base-123/eslint.config.mjs",
|
||||||
"libs/lib-base-123/jest.config.ts",
|
"libs/lib-base-123/jest.config.ts",
|
||||||
"libs/lib-base-123/package.json",
|
"libs/lib-base-123/package.json",
|
||||||
"libs/lib-base-123/project.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": [
|
"lib-dependent-123": [
|
||||||
"libs/lib-dependent-123/README.md",
|
"libs/lib-dependent-123/README.md",
|
||||||
"libs/lib-dependent-123/eslint.config.cjs",
|
"libs/lib-dependent-123/eslint.config.mjs",
|
||||||
"libs/lib-dependent-123/jest.config.ts",
|
"libs/lib-dependent-123/jest.config.ts",
|
||||||
"libs/lib-dependent-123/package.json",
|
"libs/lib-dependent-123/package.json",
|
||||||
"libs/lib-dependent-123/project.json",
|
"libs/lib-dependent-123/project.json",
|
||||||
|
|||||||
@ -94,6 +94,7 @@ exports[`workspace move to nx layout should create nx.json 1`] = `
|
|||||||
"!{projectRoot}/karma.conf.js",
|
"!{projectRoot}/karma.conf.js",
|
||||||
"!{projectRoot}/.eslintrc.json",
|
"!{projectRoot}/.eslintrc.json",
|
||||||
"!{projectRoot}/eslint.config.cjs",
|
"!{projectRoot}/eslint.config.cjs",
|
||||||
|
"!{projectRoot}/eslint.config.mjs",
|
||||||
],
|
],
|
||||||
"sharedGlobals": [],
|
"sharedGlobals": [],
|
||||||
},
|
},
|
||||||
@ -104,7 +105,7 @@ exports[`workspace move to nx layout should create nx.json 1`] = `
|
|||||||
"default",
|
"default",
|
||||||
"{workspaceRoot}/.eslintrc.json",
|
"{workspaceRoot}/.eslintrc.json",
|
||||||
"{workspaceRoot}/.eslintignore",
|
"{workspaceRoot}/.eslintignore",
|
||||||
"{workspaceRoot}/eslint.config.cjs",
|
"{workspaceRoot}/eslint.config.mjs",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should add env configuration 1`] = `
|
exports[`convert-to-flat-config generator CJS should add env configuration 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||||
@ -61,7 +61,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should add global and env configuration 1`] = `
|
exports[`convert-to-flat-config generator CJS should add global and env configuration 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||||
@ -126,7 +126,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should add global configuration 1`] = `
|
exports[`convert-to-flat-config generator CJS should add global configuration 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||||
@ -186,7 +186,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should add global eslintignores 1`] = `
|
exports[`convert-to-flat-config generator CJS should add global eslintignores 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||||
@ -248,7 +248,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should add parser 1`] = `
|
exports[`convert-to-flat-config generator CJS should add parser 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||||
@ -309,7 +309,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should add plugins 1`] = `
|
exports[`convert-to-flat-config generator CJS should add plugins 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const eslintPluginImport = require('eslint-plugin-import');
|
const eslintPluginImport = require('eslint-plugin-import');
|
||||||
@ -378,7 +378,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should add settings 1`] = `
|
exports[`convert-to-flat-config generator CJS should add settings 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||||
@ -442,7 +442,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should convert json successfully 1`] = `
|
exports[`convert-to-flat-config generator CJS should convert json successfully 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||||
@ -501,7 +501,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should convert json successfully 2`] = `
|
exports[`convert-to-flat-config generator CJS should convert json successfully 2`] = `
|
||||||
"const baseConfig = require('../../eslint.config.cjs');
|
"const baseConfig = require('../../eslint.config.cjs');
|
||||||
|
|
||||||
module.exports = [
|
module.exports = [
|
||||||
@ -528,7 +528,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should convert yaml successfully 1`] = `
|
exports[`convert-to-flat-config generator CJS should convert yaml successfully 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||||
@ -587,7 +587,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should convert yaml successfully 2`] = `
|
exports[`convert-to-flat-config generator CJS should convert yaml successfully 2`] = `
|
||||||
"const baseConfig = require('../../eslint.config.cjs');
|
"const baseConfig = require('../../eslint.config.cjs');
|
||||||
|
|
||||||
module.exports = [
|
module.exports = [
|
||||||
@ -614,7 +614,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should convert yml successfully 1`] = `
|
exports[`convert-to-flat-config generator CJS should convert yml successfully 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const nxEslintPlugin = require('@nx/eslint-plugin');
|
const nxEslintPlugin = require('@nx/eslint-plugin');
|
||||||
@ -673,7 +673,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should convert yml successfully 2`] = `
|
exports[`convert-to-flat-config generator CJS should convert yml successfully 2`] = `
|
||||||
"const baseConfig = require('../../eslint.config.cjs');
|
"const baseConfig = require('../../eslint.config.cjs');
|
||||||
|
|
||||||
module.exports = [
|
module.exports = [
|
||||||
@ -700,7 +700,7 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`convert-to-flat-config generator should handle custom eslintignores 1`] = `
|
exports[`convert-to-flat-config generator CJS should handle custom eslintignores 1`] = `
|
||||||
"const baseConfig = require('../../eslint.config.cjs');
|
"const baseConfig = require('../../eslint.config.cjs');
|
||||||
|
|
||||||
module.exports = [
|
module.exports = [
|
||||||
@ -732,3 +732,756 @@ module.exports = [
|
|||||||
];
|
];
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should add env configuration 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should add global and env configuration 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: { ...globals.browser, myCustomGlobal: 'readonly' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should add global configuration 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{ languageOptions: { globals: { myCustomGlobal: 'readonly' } } },
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should add global eslintignores 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
ignores: ['ignore/me'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should add parser 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
import typescriptEslintParser from '@typescript-eslint/parser';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{ languageOptions: { parser: typescriptEslintParser } },
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should add plugins 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import eslintPluginImport from 'eslint-plugin-import';
|
||||||
|
import eslintPluginSingleName from 'eslint-plugin-single-name';
|
||||||
|
import scopeEslintPluginWithName from '@scope/eslint-plugin-with-name';
|
||||||
|
import justScopeEslintPlugin from '@just-scope/eslint-plugin';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
'eslint-plugin-import': eslintPluginImport,
|
||||||
|
'single-name': eslintPluginSingleName,
|
||||||
|
'@scope/with-name': scopeEslintPluginWithName,
|
||||||
|
'@just-scope': justScopeEslintPlugin,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should add settings 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{
|
||||||
|
settings: {
|
||||||
|
sharedData: 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should convert json successfully 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should convert json successfully 2`] = `
|
||||||
|
"import baseConfig from '../../eslint.config.mjs';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should convert yaml successfully 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should convert yaml successfully 2`] = `
|
||||||
|
"import baseConfig from '../../eslint.config.mjs';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should convert yml successfully 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should convert yml successfully 2`] = `
|
||||||
|
"import baseConfig from '../../eslint.config.mjs';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`convert-to-flat-config generator MJS should handle custom eslintignores 1`] = `
|
||||||
|
"import baseConfig from '../../eslint.config.mjs';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['ignore/me'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['ignore/me/as/well'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ describe('convertEslintJsonToFlatConfig', () => {
|
|||||||
tree = createTreeWithEmptyWorkspace();
|
tree = createTreeWithEmptyWorkspace();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ESM', () => {
|
||||||
it('should convert root configs', async () => {
|
it('should convert root configs', async () => {
|
||||||
tree.write(
|
tree.write(
|
||||||
'.eslintrc.json',
|
'.eslintrc.json',
|
||||||
@ -68,7 +69,321 @@ describe('convertEslintJsonToFlatConfig', () => {
|
|||||||
tree,
|
tree,
|
||||||
'',
|
'',
|
||||||
readJson(tree, '.eslintrc.json'),
|
readJson(tree, '.eslintrc.json'),
|
||||||
['.eslintignore']
|
['.eslintignore'],
|
||||||
|
'mjs'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(content).toMatchInlineSnapshot(`
|
||||||
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
|
import { dirname } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import js from "@eslint/js";
|
||||||
|
import nxEslintPlugin from "@nx/eslint-plugin";
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"**/dist"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ plugins: { "@nx": nxEslintPlugin } },
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
"**/*.js",
|
||||||
|
"**/*.jsx"
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
"@nx/enforce-module-boundaries": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: "*",
|
||||||
|
onlyDependOnLibsWithTags: [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...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
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
...compat.config({
|
||||||
|
env: {
|
||||||
|
jest: true
|
||||||
|
}
|
||||||
|
}).map(config => ({
|
||||||
|
...config,
|
||||||
|
files: [
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.spec.tsx",
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.spec.jsx"
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
...config.rules
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"src/ignore/to/keep.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"something/else"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert project configs', async () => {
|
||||||
|
tree.write(
|
||||||
|
'mylib/.eslintrc.json',
|
||||||
|
JSON.stringify({
|
||||||
|
extends: [
|
||||||
|
'plugin:@nx/react-typescript',
|
||||||
|
'next',
|
||||||
|
'next/core-web-vitals',
|
||||||
|
'../../.eslintrc.json',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['!**/*', '.next/**/*'],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@next/next/no-html-link-for-pages': [
|
||||||
|
'error',
|
||||||
|
'apps/test-next/pages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx'],
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.js', '*.jsx'],
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.json'],
|
||||||
|
parser: 'jsonc-eslint-parser',
|
||||||
|
rules: {
|
||||||
|
'@nx/dependency-checks': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'@next/next/no-html-link-for-pages': 'off',
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
tree.write('mylib/.eslintignore', 'node_modules\nsomething/else');
|
||||||
|
|
||||||
|
const { content } = convertEslintJsonToFlatConfig(
|
||||||
|
tree,
|
||||||
|
'mylib',
|
||||||
|
readJson(tree, 'mylib/.eslintrc.json'),
|
||||||
|
['mylib/.eslintignore'],
|
||||||
|
'mjs'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(content).toMatchInlineSnapshot(`
|
||||||
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
|
import { dirname } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import js from "@eslint/js";
|
||||||
|
import baseConfig from "../../eslint.config.mjs";
|
||||||
|
import globals from "globals";
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"**/dist"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
...baseConfig,
|
||||||
|
...compat.extends("plugin:@nx/react-typescript", "next", "next/core-web-vitals"),
|
||||||
|
{ languageOptions: { globals: { ...globals.jest } } },
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
"@next/next/no-html-link-for-pages": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
"**/*.js",
|
||||||
|
"**/*.jsx"
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
"@next/next/no-html-link-for-pages": [
|
||||||
|
"error",
|
||||||
|
"apps/test-next/pages"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx"
|
||||||
|
],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/*.js",
|
||||||
|
"**/*.jsx"
|
||||||
|
],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/*.json"
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
"@nx/dependency-checks": "error"
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
parser: await import("jsonc-eslint-parser")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
".next/**/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"something/else"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('CJS', () => {
|
||||||
|
it('should convert root configs', async () => {
|
||||||
|
tree.write(
|
||||||
|
'.eslintrc.json',
|
||||||
|
JSON.stringify({
|
||||||
|
root: true,
|
||||||
|
ignorePatterns: ['**/*', 'src/ignore/to/keep.ts'],
|
||||||
|
plugins: ['@nx'],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx'],
|
||||||
|
extends: ['plugin:@nx/typescript'],
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.js', '*.jsx'],
|
||||||
|
extends: ['plugin:@nx/javascript'],
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'**/*.spec.ts',
|
||||||
|
'**/*.spec.tsx',
|
||||||
|
'**/*.spec.js',
|
||||||
|
'**/*.spec.jsx',
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
tree.write('.eslintignore', 'node_modules\nsomething/else');
|
||||||
|
|
||||||
|
const { content } = convertEslintJsonToFlatConfig(
|
||||||
|
tree,
|
||||||
|
'',
|
||||||
|
readJson(tree, '.eslintrc.json'),
|
||||||
|
['.eslintignore'],
|
||||||
|
'cjs'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(content).toMatchInlineSnapshot(`
|
expect(content).toMatchInlineSnapshot(`
|
||||||
@ -228,7 +543,8 @@ describe('convertEslintJsonToFlatConfig', () => {
|
|||||||
tree,
|
tree,
|
||||||
'mylib',
|
'mylib',
|
||||||
readJson(tree, 'mylib/.eslintrc.json'),
|
readJson(tree, 'mylib/.eslintrc.json'),
|
||||||
['mylib/.eslintignore']
|
['mylib/.eslintignore'],
|
||||||
|
'cjs'
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(content).toMatchInlineSnapshot(`
|
expect(content).toMatchInlineSnapshot(`
|
||||||
@ -312,3 +628,4 @@ describe('convertEslintJsonToFlatConfig', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -21,7 +21,8 @@ export function convertEslintJsonToFlatConfig(
|
|||||||
tree: Tree,
|
tree: Tree,
|
||||||
root: string,
|
root: string,
|
||||||
config: ESLint.ConfigData,
|
config: ESLint.ConfigData,
|
||||||
ignorePaths: string[]
|
ignorePaths: string[],
|
||||||
|
format: 'cjs' | 'mjs'
|
||||||
): { content: string; addESLintRC: boolean; addESLintJS: boolean } {
|
): { content: string; addESLintRC: boolean; addESLintJS: boolean } {
|
||||||
const importsMap = new Map<string, string>();
|
const importsMap = new Map<string, string>();
|
||||||
const exportElements: ts.Expression[] = [];
|
const exportElements: ts.Expression[] = [];
|
||||||
@ -38,7 +39,12 @@ export function convertEslintJsonToFlatConfig(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (config.extends) {
|
if (config.extends) {
|
||||||
const extendsResult = addExtends(importsMap, exportElements, config);
|
const extendsResult = addExtends(
|
||||||
|
importsMap,
|
||||||
|
exportElements,
|
||||||
|
config,
|
||||||
|
format
|
||||||
|
);
|
||||||
isFlatCompatNeeded = extendsResult.isFlatCompatNeeded;
|
isFlatCompatNeeded = extendsResult.isFlatCompatNeeded;
|
||||||
isESLintJSNeeded = extendsResult.isESLintJSNeeded;
|
isESLintJSNeeded = extendsResult.isESLintJSNeeded;
|
||||||
}
|
}
|
||||||
@ -156,7 +162,7 @@ export function convertEslintJsonToFlatConfig(
|
|||||||
) {
|
) {
|
||||||
isFlatCompatNeeded = true;
|
isFlatCompatNeeded = true;
|
||||||
}
|
}
|
||||||
exportElements.push(generateFlatOverride(override));
|
exportElements.push(generateFlatOverride(override, format));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +195,7 @@ export function convertEslintJsonToFlatConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create the node list and print it to new file
|
// create the node list and print it to new file
|
||||||
const nodeList = createNodeList(importsMap, exportElements);
|
const nodeList = createNodeList(importsMap, exportElements, format);
|
||||||
let content = stringifyNodeList(nodeList);
|
let content = stringifyNodeList(nodeList);
|
||||||
if (isFlatCompatNeeded) {
|
if (isFlatCompatNeeded) {
|
||||||
content = addFlatCompatToFlatConfig(content);
|
content = addFlatCompatToFlatConfig(content);
|
||||||
@ -206,7 +212,8 @@ export function convertEslintJsonToFlatConfig(
|
|||||||
function addExtends(
|
function addExtends(
|
||||||
importsMap: Map<string, string | string[]>,
|
importsMap: Map<string, string | string[]>,
|
||||||
configBlocks: ts.Expression[],
|
configBlocks: ts.Expression[],
|
||||||
config: ESLint.ConfigData
|
config: ESLint.ConfigData,
|
||||||
|
format: 'mjs' | 'cjs'
|
||||||
): { isFlatCompatNeeded: boolean; isESLintJSNeeded: boolean } {
|
): { isFlatCompatNeeded: boolean; isESLintJSNeeded: boolean } {
|
||||||
let isFlatCompatNeeded = false;
|
let isFlatCompatNeeded = false;
|
||||||
let isESLintJSNeeded = false;
|
let isESLintJSNeeded = false;
|
||||||
@ -225,7 +232,7 @@ function addExtends(
|
|||||||
configBlocks.push(generateSpreadElement(localName));
|
configBlocks.push(generateSpreadElement(localName));
|
||||||
const newImport = imp.replace(
|
const newImport = imp.replace(
|
||||||
/^(.*)\.eslintrc(.base)?\.json$/,
|
/^(.*)\.eslintrc(.base)?\.json$/,
|
||||||
'$1eslint$2.config.cjs'
|
`$1eslint$2.config.${format}`
|
||||||
);
|
);
|
||||||
importsMap.set(newImport, localName);
|
importsMap.set(newImport, localName);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import { dump } from '@zkochan/js-yaml';
|
|||||||
|
|
||||||
describe('convert-to-flat-config generator', () => {
|
describe('convert-to-flat-config generator', () => {
|
||||||
let tree: Tree;
|
let tree: Tree;
|
||||||
const options: ConvertToFlatConfigGeneratorSchema = { skipFormat: false };
|
|
||||||
|
|
||||||
// TODO(@meeroslav): add plugin in these tests
|
// TODO(@meeroslav): add plugin in these tests
|
||||||
|
|
||||||
@ -47,6 +46,11 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('CJS', () => {
|
||||||
|
const options: ConvertToFlatConfigGeneratorSchema = {
|
||||||
|
skipFormat: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
|
};
|
||||||
it('should update dependencies', async () => {
|
it('should update dependencies', async () => {
|
||||||
await lintProjectGenerator(tree, {
|
await lintProjectGenerator(tree, {
|
||||||
skipFormat: false,
|
skipFormat: false,
|
||||||
@ -79,6 +83,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
await convertToFlatConfigGenerator(tree, options);
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
@ -105,6 +110,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
eslintFilePatterns: ['**/*.ts'],
|
eslintFilePatterns: ['**/*.ts'],
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
const yamlContent = dump(readJson(tree, 'libs/test-lib/.eslintrc.json'));
|
const yamlContent = dump(readJson(tree, 'libs/test-lib/.eslintrc.json'));
|
||||||
tree.delete('libs/test-lib/.eslintrc.json');
|
tree.delete('libs/test-lib/.eslintrc.json');
|
||||||
@ -135,6 +141,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
eslintFilePatterns: ['**/*.ts'],
|
eslintFilePatterns: ['**/*.ts'],
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
const yamlContent = dump(readJson(tree, 'libs/test-lib/.eslintrc.json'));
|
const yamlContent = dump(readJson(tree, 'libs/test-lib/.eslintrc.json'));
|
||||||
tree.delete('libs/test-lib/.eslintrc.json');
|
tree.delete('libs/test-lib/.eslintrc.json');
|
||||||
@ -164,6 +171,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, '.eslintrc.json', (json) => {
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
json.extends = ['plugin:storybook/recommended'];
|
json.extends = ['plugin:storybook/recommended'];
|
||||||
@ -284,6 +292,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
tree.write('another-folder/.myeslintignore', 'ignore/me');
|
tree.write('another-folder/.myeslintignore', 'ignore/me');
|
||||||
updateJson(tree, 'libs/test-lib/project.json', (json) => {
|
updateJson(tree, 'libs/test-lib/project.json', (json) => {
|
||||||
@ -313,6 +322,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, '.eslintrc.json', (json) => {
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
json.settings = {
|
json.settings = {
|
||||||
@ -331,6 +341,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, '.eslintrc.json', (json) => {
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
json.env = {
|
json.env = {
|
||||||
@ -350,6 +361,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, '.eslintrc.json', (json) => {
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
json.globals = {
|
json.globals = {
|
||||||
@ -368,6 +380,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, '.eslintrc.json', (json) => {
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
json.globals = {
|
json.globals = {
|
||||||
@ -389,6 +402,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, '.eslintrc.json', (json) => {
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
json.plugins = [
|
json.plugins = [
|
||||||
@ -410,6 +424,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, '.eslintrc.json', (json) => {
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
json.parser = '@typescript-eslint/parser';
|
json.parser = '@typescript-eslint/parser';
|
||||||
@ -426,6 +441,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, '.eslintrc.json', (json) => {
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
json.noInlineConfig = true;
|
json.noInlineConfig = true;
|
||||||
@ -504,6 +520,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, 'nx.json', (json: NxJsonConfiguration) => {
|
updateJson(tree, 'nx.json', (json: NxJsonConfiguration) => {
|
||||||
delete json.targetDefaults;
|
delete json.targetDefaults;
|
||||||
@ -532,6 +549,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, 'nx.json', (json: NxJsonConfiguration) => {
|
updateJson(tree, 'nx.json', (json: NxJsonConfiguration) => {
|
||||||
delete json.targetDefaults;
|
delete json.targetDefaults;
|
||||||
@ -572,6 +590,7 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
|
|
||||||
project: 'dx-assets-ui',
|
project: 'dx-assets-ui',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
updateJson(tree, 'apps/dx-assets-ui/.eslintrc.json', () => {
|
updateJson(tree, 'apps/dx-assets-ui/.eslintrc.json', () => {
|
||||||
return {
|
return {
|
||||||
@ -637,3 +656,621 @@ describe('convert-to-flat-config generator', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('MJS', () => {
|
||||||
|
const options: ConvertToFlatConfigGeneratorSchema = {
|
||||||
|
skipFormat: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should update dependencies', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.read('package.json', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"{
|
||||||
|
"name": "@proj/source",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^2.1.1",
|
||||||
|
"@nx/eslint": "0.0.1",
|
||||||
|
"@nx/eslint-plugin": "0.0.1",
|
||||||
|
"eslint": "^9.8.0",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"typescript-eslint": "^8.19.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert json successfully', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.exists('eslint.config.mjs')).toBeTruthy();
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(tree.exists('libs/test-lib/eslint.config.mjs')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('libs/test-lib/eslint.config.mjs', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
// check nx.json changes
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.targetDefaults.lint.inputs).toContain(
|
||||||
|
'{workspaceRoot}/eslint.config.mjs'
|
||||||
|
);
|
||||||
|
expect(nxJson.namedInputs.production).toContain(
|
||||||
|
'!{projectRoot}/eslint.config.mjs'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert yaml successfully', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
eslintFilePatterns: ['**/*.ts'],
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
const yamlContent = dump(readJson(tree, 'libs/test-lib/.eslintrc.json'));
|
||||||
|
tree.delete('libs/test-lib/.eslintrc.json');
|
||||||
|
tree.write('libs/test-lib/.eslintrc.yaml', yamlContent);
|
||||||
|
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.exists('eslint.config.mjs')).toBeTruthy();
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(tree.exists('libs/test-lib/eslint.config.mjs')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('libs/test-lib/eslint.config.mjs', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
// check nx.json changes
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.targetDefaults.lint.inputs).toContain(
|
||||||
|
'{workspaceRoot}/eslint.config.mjs'
|
||||||
|
);
|
||||||
|
expect(nxJson.namedInputs.production).toContain(
|
||||||
|
'!{projectRoot}/eslint.config.mjs'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert yml successfully', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
eslintFilePatterns: ['**/*.ts'],
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
const yamlContent = dump(readJson(tree, 'libs/test-lib/.eslintrc.json'));
|
||||||
|
tree.delete('libs/test-lib/.eslintrc.json');
|
||||||
|
tree.write('libs/test-lib/.eslintrc.yml', yamlContent);
|
||||||
|
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.exists('eslint.config.mjs')).toBeTruthy();
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(tree.exists('libs/test-lib/eslint.config.mjs')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('libs/test-lib/eslint.config.mjs', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
// check nx.json changes
|
||||||
|
const nxJson = readJson(tree, 'nx.json');
|
||||||
|
expect(nxJson.targetDefaults.lint.inputs).toContain(
|
||||||
|
'{workspaceRoot}/eslint.config.mjs'
|
||||||
|
);
|
||||||
|
expect(nxJson.namedInputs.production).toContain(
|
||||||
|
'!{projectRoot}/eslint.config.mjs'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add plugin extends', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
|
json.extends = ['plugin:storybook/recommended'];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
...compat.extends('plugin:storybook/recommended'),
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
expect(tree.read('libs/test-lib/eslint.config.mjs', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import baseConfig from '../../eslint.config.mjs';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
expect(
|
||||||
|
readJson(tree, 'package.json').devDependencies['@eslint/eslintrc']
|
||||||
|
).toEqual(eslintrcVersion);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add global eslintignores', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
});
|
||||||
|
tree.write('.eslintignore', 'ignore/me');
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
const config = tree.read('eslint.config.mjs', 'utf-8');
|
||||||
|
expect(config).toContain('ignore/me');
|
||||||
|
expect(config).toMatchSnapshot();
|
||||||
|
expect(tree.exists('.eslintignore')).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle custom eslintignores', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
tree.write('another-folder/.myeslintignore', 'ignore/me');
|
||||||
|
updateJson(tree, 'libs/test-lib/project.json', (json) => {
|
||||||
|
json.targets.lint.options = json.targets.lint.options || {};
|
||||||
|
json.targets.lint.options.ignorePath = 'another-folder/.myeslintignore';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
tree.write('libs/test-lib/.eslintignore', 'ignore/me/as/well');
|
||||||
|
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tree.read('libs/test-lib/eslint.config.mjs', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(tree.exists('another-folder/.myeslintignore')).toBeFalsy();
|
||||||
|
expect(tree.exists('libs/test-lib/.eslintignore')).toBeFalsy();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
readJson(tree, 'libs/test-lib/project.json').targets.lint.options
|
||||||
|
.ignorePath
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add settings', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
|
json.settings = {
|
||||||
|
sharedData: 'Hello',
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add env configuration', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
|
json.env = {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add global configuration', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
|
json.globals = {
|
||||||
|
myCustomGlobal: 'readonly',
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add global and env configuration', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
|
json.globals = {
|
||||||
|
myCustomGlobal: 'readonly',
|
||||||
|
};
|
||||||
|
json.env = {
|
||||||
|
browser: true,
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add plugins', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
|
json.plugins = [
|
||||||
|
'eslint-plugin-import',
|
||||||
|
'single-name',
|
||||||
|
'@scope/with-name',
|
||||||
|
'@just-scope',
|
||||||
|
];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add parser', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
|
json.parser = '@typescript-eslint/parser';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add linter options', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, '.eslintrc.json', (json) => {
|
||||||
|
json.noInlineConfig = true;
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nxEslintPlugin from '@nx/eslint-plugin';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{
|
||||||
|
linterOptions: {
|
||||||
|
noInlineConfig: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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 convert project if target is defined via plugin as string', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, 'nx.json', (json: NxJsonConfiguration) => {
|
||||||
|
delete json.targetDefaults;
|
||||||
|
json.plugins = ['@nx/eslint/plugin'];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
updateJson(
|
||||||
|
tree,
|
||||||
|
'libs/test-lib/project.json',
|
||||||
|
(json: ProjectConfiguration) => {
|
||||||
|
delete json.targets.lint;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tree.exists('eslint.config.mjs')).toBeFalsy();
|
||||||
|
expect(tree.exists('libs/test-lib/eslint.config.mjs')).toBeFalsy();
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
expect(tree.exists('eslint.config.mjs')).toBeTruthy();
|
||||||
|
expect(tree.exists('libs/test-lib/eslint.config.mjs')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert project if target is defined via plugin as object', async () => {
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, 'nx.json', (json: NxJsonConfiguration) => {
|
||||||
|
delete json.targetDefaults;
|
||||||
|
json.plugins = [
|
||||||
|
{
|
||||||
|
plugin: '@nx/eslint/plugin',
|
||||||
|
options: {
|
||||||
|
targetName: 'lint',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
updateJson(
|
||||||
|
tree,
|
||||||
|
'libs/test-lib/project.json',
|
||||||
|
(json: ProjectConfiguration) => {
|
||||||
|
delete json.targets.lint;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tree.exists('eslint.config.mjs')).toBeFalsy();
|
||||||
|
expect(tree.exists('libs/test-lib/eslint.config.mjs')).toBeFalsy();
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
expect(tree.exists('eslint.config.mjs')).toBeTruthy();
|
||||||
|
expect(tree.exists('libs/test-lib/eslint.config.mjs')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle parser options even if parser is extended', async () => {
|
||||||
|
addProjectConfiguration(tree, 'dx-assets-ui', {
|
||||||
|
root: 'apps/dx-assets-ui',
|
||||||
|
targets: {},
|
||||||
|
});
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
skipFormat: false,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
|
||||||
|
project: 'dx-assets-ui',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
updateJson(tree, 'apps/dx-assets-ui/.eslintrc.json', () => {
|
||||||
|
return {
|
||||||
|
extends: ['../../.eslintrc.json'],
|
||||||
|
ignorePatterns: ['!**/*', '__fixtures__/**/*'],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||||
|
parserOptions: {
|
||||||
|
project: ['apps/dx-assets-ui/tsconfig.*?.json'],
|
||||||
|
},
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx'],
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.js', '*.jsx'],
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
await convertToFlatConfigGenerator(tree, options);
|
||||||
|
expect(tree.exists('apps/dx-assets-ui/eslint.config.mjs')).toBeTruthy();
|
||||||
|
expect(tree.exists('eslint.config.mjs')).toBeTruthy();
|
||||||
|
expect(tree.read('apps/dx-assets-ui/eslint.config.mjs', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import baseConfig from '../../eslint.config.mjs';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
project: ['apps/dx-assets-ui/tsconfig.*?.json'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['__fixtures__/**/*'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -40,10 +40,12 @@ export async function convertToFlatConfigGenerator(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options.eslintConfigFormat ??= 'mjs';
|
||||||
|
|
||||||
const eslintIgnoreFiles = new Set<string>(['.eslintignore']);
|
const eslintIgnoreFiles = new Set<string>(['.eslintignore']);
|
||||||
|
|
||||||
// convert root eslint config to eslint.config.cjs
|
// convert root eslint config to eslint.config.cjs or eslint.base.config.mjs based on eslintConfigFormat
|
||||||
convertRootToFlatConfig(tree, eslintFile);
|
convertRootToFlatConfig(tree, eslintFile, options.eslintConfigFormat);
|
||||||
|
|
||||||
// convert project eslint files to eslint.config.cjs
|
// convert project eslint files to eslint.config.cjs
|
||||||
const projects = getProjects(tree);
|
const projects = getProjects(tree);
|
||||||
@ -53,7 +55,8 @@ export async function convertToFlatConfigGenerator(
|
|||||||
project,
|
project,
|
||||||
projectConfig,
|
projectConfig,
|
||||||
readNxJson(tree),
|
readNxJson(tree),
|
||||||
eslintIgnoreFiles
|
eslintIgnoreFiles,
|
||||||
|
options.eslintConfigFormat
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +66,7 @@ export async function convertToFlatConfigGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// replace references in nx.json
|
// replace references in nx.json
|
||||||
updateNxJsonConfig(tree);
|
updateNxJsonConfig(tree, options.eslintConfigFormat);
|
||||||
// install missing packages
|
// install missing packages
|
||||||
|
|
||||||
if (!options.skipFormat) {
|
if (!options.skipFormat) {
|
||||||
@ -75,15 +78,26 @@ export async function convertToFlatConfigGenerator(
|
|||||||
|
|
||||||
export default convertToFlatConfigGenerator;
|
export default convertToFlatConfigGenerator;
|
||||||
|
|
||||||
function convertRootToFlatConfig(tree: Tree, eslintFile: string) {
|
function convertRootToFlatConfig(
|
||||||
|
tree: Tree,
|
||||||
|
eslintFile: string,
|
||||||
|
format: 'cjs' | 'mjs'
|
||||||
|
) {
|
||||||
if (/\.base\.(js|json|yml|yaml)$/.test(eslintFile)) {
|
if (/\.base\.(js|json|yml|yaml)$/.test(eslintFile)) {
|
||||||
convertConfigToFlatConfig(tree, '', eslintFile, 'eslint.base.config.cjs');
|
convertConfigToFlatConfig(
|
||||||
|
tree,
|
||||||
|
'',
|
||||||
|
eslintFile,
|
||||||
|
`eslint.base.config.${format}`,
|
||||||
|
format
|
||||||
|
);
|
||||||
}
|
}
|
||||||
convertConfigToFlatConfig(
|
convertConfigToFlatConfig(
|
||||||
tree,
|
tree,
|
||||||
'',
|
'',
|
||||||
eslintFile.replace('.base.', '.'),
|
eslintFile.replace('.base.', '.'),
|
||||||
'eslint.config.cjs'
|
`eslint.config.${format}`,
|
||||||
|
format
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +106,8 @@ function convertProjectToFlatConfig(
|
|||||||
project: string,
|
project: string,
|
||||||
projectConfig: ProjectConfiguration,
|
projectConfig: ProjectConfiguration,
|
||||||
nxJson: NxJsonConfiguration,
|
nxJson: NxJsonConfiguration,
|
||||||
eslintIgnoreFiles: Set<string>
|
eslintIgnoreFiles: Set<string>,
|
||||||
|
format: 'cjs' | 'mjs'
|
||||||
) {
|
) {
|
||||||
const eslintFile = findEslintFile(tree, projectConfig.root);
|
const eslintFile = findEslintFile(tree, projectConfig.root);
|
||||||
if (eslintFile && !eslintFile.endsWith('.js')) {
|
if (eslintFile && !eslintFile.endsWith('.js')) {
|
||||||
@ -132,7 +147,8 @@ function convertProjectToFlatConfig(
|
|||||||
tree,
|
tree,
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
eslintFile,
|
eslintFile,
|
||||||
'eslint.config.cjs',
|
`eslint.config.${format}`,
|
||||||
|
format,
|
||||||
ignorePath
|
ignorePath
|
||||||
);
|
);
|
||||||
eslintIgnoreFiles.add(`${projectConfig.root}/.eslintignore`);
|
eslintIgnoreFiles.add(`${projectConfig.root}/.eslintignore`);
|
||||||
@ -146,22 +162,22 @@ function convertProjectToFlatConfig(
|
|||||||
|
|
||||||
// update names of eslint files in nx.json
|
// update names of eslint files in nx.json
|
||||||
// and remove eslintignore
|
// and remove eslintignore
|
||||||
function updateNxJsonConfig(tree: Tree) {
|
function updateNxJsonConfig(tree: Tree, format: 'cjs' | 'mjs') {
|
||||||
if (tree.exists('nx.json')) {
|
if (tree.exists('nx.json')) {
|
||||||
updateJson(tree, 'nx.json', (json: NxJsonConfiguration) => {
|
updateJson(tree, 'nx.json', (json: NxJsonConfiguration) => {
|
||||||
if (json.targetDefaults?.lint?.inputs) {
|
if (json.targetDefaults?.lint?.inputs) {
|
||||||
const inputSet = new Set(json.targetDefaults.lint.inputs);
|
const inputSet = new Set(json.targetDefaults.lint.inputs);
|
||||||
inputSet.add('{workspaceRoot}/eslint.config.cjs');
|
inputSet.add(`{workspaceRoot}/eslint.config.${format}`);
|
||||||
json.targetDefaults.lint.inputs = Array.from(inputSet);
|
json.targetDefaults.lint.inputs = Array.from(inputSet);
|
||||||
}
|
}
|
||||||
if (json.targetDefaults?.['@nx/eslint:lint']?.inputs) {
|
if (json.targetDefaults?.['@nx/eslint:lint']?.inputs) {
|
||||||
const inputSet = new Set(json.targetDefaults['@nx/eslint:lint'].inputs);
|
const inputSet = new Set(json.targetDefaults['@nx/eslint:lint'].inputs);
|
||||||
inputSet.add('{workspaceRoot}/eslint.config.cjs');
|
inputSet.add(`{workspaceRoot}/eslint.config.${format}`);
|
||||||
json.targetDefaults['@nx/eslint:lint'].inputs = Array.from(inputSet);
|
json.targetDefaults['@nx/eslint:lint'].inputs = Array.from(inputSet);
|
||||||
}
|
}
|
||||||
if (json.namedInputs?.production) {
|
if (json.namedInputs?.production) {
|
||||||
const inputSet = new Set(json.namedInputs.production);
|
const inputSet = new Set(json.namedInputs.production);
|
||||||
inputSet.add('!{projectRoot}/eslint.config.cjs');
|
inputSet.add(`!{projectRoot}/eslint.config.${format}`);
|
||||||
json.namedInputs.production = Array.from(inputSet);
|
json.namedInputs.production = Array.from(inputSet);
|
||||||
}
|
}
|
||||||
return json;
|
return json;
|
||||||
@ -174,6 +190,7 @@ function convertConfigToFlatConfig(
|
|||||||
root: string,
|
root: string,
|
||||||
source: string,
|
source: string,
|
||||||
target: string,
|
target: string,
|
||||||
|
format: 'cjs' | 'mjs',
|
||||||
ignorePath?: string
|
ignorePath?: string
|
||||||
) {
|
) {
|
||||||
const ignorePaths = ignorePath
|
const ignorePaths = ignorePath
|
||||||
@ -186,7 +203,8 @@ function convertConfigToFlatConfig(
|
|||||||
tree,
|
tree,
|
||||||
root,
|
root,
|
||||||
config,
|
config,
|
||||||
ignorePaths
|
ignorePaths,
|
||||||
|
format
|
||||||
);
|
);
|
||||||
return processConvertedConfig(tree, root, source, target, conversionResult);
|
return processConvertedConfig(tree, root, source, target, conversionResult);
|
||||||
}
|
}
|
||||||
@ -201,7 +219,8 @@ function convertConfigToFlatConfig(
|
|||||||
tree,
|
tree,
|
||||||
root,
|
root,
|
||||||
config,
|
config,
|
||||||
ignorePaths
|
ignorePaths,
|
||||||
|
format
|
||||||
);
|
);
|
||||||
return processConvertedConfig(tree, root, source, target, conversionResult);
|
return processConvertedConfig(tree, root, source, target, conversionResult);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
export interface ConvertToFlatConfigGeneratorSchema {
|
export interface ConvertToFlatConfigGeneratorSchema {
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
|
// Internal option
|
||||||
|
eslintConfigFormat?: 'mjs' | 'cjs';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,6 +66,7 @@ function postTargetTransformer(
|
|||||||
'{workspaceRoot}/.eslintrc.json',
|
'{workspaceRoot}/.eslintrc.json',
|
||||||
'{workspaceRoot}/.eslintignore',
|
'{workspaceRoot}/.eslintignore',
|
||||||
'{workspaceRoot}/eslint.config.cjs',
|
'{workspaceRoot}/eslint.config.cjs',
|
||||||
|
'{workspaceRoot}/eslint.config.mjs',
|
||||||
].includes(input)
|
].includes(input)
|
||||||
);
|
);
|
||||||
if (inputs.length === 0) {
|
if (inputs.length === 0) {
|
||||||
|
|||||||
@ -92,9 +92,10 @@ export const getGlobalEsLintConfiguration = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getGlobalFlatEslintConfiguration = (
|
export const getGlobalFlatEslintConfiguration = (
|
||||||
|
format: 'cjs' | 'mjs',
|
||||||
rootProject?: boolean
|
rootProject?: boolean
|
||||||
): string => {
|
): string => {
|
||||||
const nodeList = createNodeList(new Map(), []);
|
const nodeList = createNodeList(new Map(), [], format);
|
||||||
let content = stringifyNodeList(nodeList);
|
let content = stringifyNodeList(nodeList);
|
||||||
content = addImportToFlatConfig(content, 'nx', '@nx/eslint-plugin');
|
content = addImportToFlatConfig(content, 'nx', '@nx/eslint-plugin');
|
||||||
|
|
||||||
@ -114,15 +115,19 @@ export const getGlobalFlatEslintConfiguration = (
|
|||||||
|
|
||||||
content = addBlockToFlatConfigExport(
|
content = addBlockToFlatConfigExport(
|
||||||
content,
|
content,
|
||||||
generateFlatOverride({
|
generateFlatOverride(
|
||||||
|
{
|
||||||
ignores: ['**/dist'],
|
ignores: ['**/dist'],
|
||||||
})
|
},
|
||||||
|
format
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!rootProject) {
|
if (!rootProject) {
|
||||||
content = addBlockToFlatConfigExport(
|
content = addBlockToFlatConfigExport(
|
||||||
content,
|
content,
|
||||||
generateFlatOverride({
|
generateFlatOverride(
|
||||||
|
{
|
||||||
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||||
rules: {
|
rules: {
|
||||||
'@nx/enforce-module-boundaries': [
|
'@nx/enforce-module-boundaries': [
|
||||||
@ -140,13 +145,16 @@ export const getGlobalFlatEslintConfiguration = (
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as Linter.RulesRecord,
|
} as Linter.RulesRecord,
|
||||||
})
|
},
|
||||||
|
format
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
content = addBlockToFlatConfigExport(
|
content = addBlockToFlatConfigExport(
|
||||||
content,
|
content,
|
||||||
generateFlatOverride({
|
generateFlatOverride(
|
||||||
|
{
|
||||||
files: [
|
files: [
|
||||||
'**/*.ts',
|
'**/*.ts',
|
||||||
'**/*.tsx',
|
'**/*.tsx',
|
||||||
@ -156,7 +164,9 @@ export const getGlobalFlatEslintConfiguration = (
|
|||||||
'**/*.mjs',
|
'**/*.mjs',
|
||||||
],
|
],
|
||||||
rules: {},
|
rules: {},
|
||||||
})
|
},
|
||||||
|
format
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
|
|||||||
@ -9,8 +9,12 @@ import {
|
|||||||
updateJson,
|
updateJson,
|
||||||
writeJson,
|
writeJson,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { dirname } from 'path';
|
import { dirname, extname } from 'path';
|
||||||
import { findEslintFile, isEslintConfigSupported } from '../utils/eslint-file';
|
import {
|
||||||
|
determineEslintConfigFormat,
|
||||||
|
findEslintFile,
|
||||||
|
isEslintConfigSupported,
|
||||||
|
} from '../utils/eslint-file';
|
||||||
import {
|
import {
|
||||||
getGlobalEsLintConfiguration,
|
getGlobalEsLintConfiguration,
|
||||||
getGlobalFlatEslintConfiguration,
|
getGlobalFlatEslintConfiguration,
|
||||||
@ -32,10 +36,24 @@ export function migrateConfigToMonorepoStyle(
|
|||||||
projects: ProjectConfiguration[],
|
projects: ProjectConfiguration[],
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
unitTestRunner: string,
|
unitTestRunner: string,
|
||||||
|
eslintConfigFormat: 'mjs' | 'cjs',
|
||||||
keepExistingVersions?: boolean
|
keepExistingVersions?: boolean
|
||||||
): GeneratorCallback {
|
): GeneratorCallback {
|
||||||
const rootEslintConfig = findEslintFile(tree);
|
const rootEslintConfig = findEslintFile(tree);
|
||||||
let skipCleanup = false;
|
let skipCleanup = false;
|
||||||
|
|
||||||
|
if (rootEslintConfig) {
|
||||||
|
// We do not want to mix the formats
|
||||||
|
const fileExtension = extname(rootEslintConfig);
|
||||||
|
if (fileExtension === '.mjs' || fileExtension === '.cjs') {
|
||||||
|
eslintConfigFormat = fileExtension.slice(1) as 'mjs' | 'cjs';
|
||||||
|
} else {
|
||||||
|
eslintConfigFormat = determineEslintConfigFormat(
|
||||||
|
tree.read(rootEslintConfig, 'utf-8')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
rootEslintConfig?.match(/\.base\./) &&
|
rootEslintConfig?.match(/\.base\./) &&
|
||||||
!projects.some((p) => p.root === '.')
|
!projects.some((p) => p.root === '.')
|
||||||
@ -57,10 +75,10 @@ export function migrateConfigToMonorepoStyle(
|
|||||||
keepExistingVersions
|
keepExistingVersions
|
||||||
);
|
);
|
||||||
tree.write(
|
tree.write(
|
||||||
tree.exists('eslint.config.cjs')
|
tree.exists(`eslint.config.${eslintConfigFormat}`)
|
||||||
? 'eslint.base.config.cjs'
|
? `eslint.base.config.${eslintConfigFormat}`
|
||||||
: 'eslint.config.cjs',
|
: `eslint.config.${eslintConfigFormat}`,
|
||||||
getGlobalFlatEslintConfiguration()
|
getGlobalFlatEslintConfiguration(eslintConfigFormat)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const eslintFile = findEslintFile(tree, '.');
|
const eslintFile = findEslintFile(tree, '.');
|
||||||
@ -134,7 +152,9 @@ function migrateEslintFile(projectEslintPath: string, tree: Tree) {
|
|||||||
let config = tree.read(projectEslintPath, 'utf-8');
|
let config = tree.read(projectEslintPath, 'utf-8');
|
||||||
// remove @nx plugin
|
// remove @nx plugin
|
||||||
config = removePlugin(config, '@nx', '@nx/eslint-plugin-nx');
|
config = removePlugin(config, '@nx', '@nx/eslint-plugin-nx');
|
||||||
// extend eslint.base.config.cjs
|
|
||||||
|
// if base config is cjs, we will need to import it using async import
|
||||||
|
|
||||||
config = addImportToFlatConfig(
|
config = addImportToFlatConfig(
|
||||||
config,
|
config,
|
||||||
'baseConfig',
|
'baseConfig',
|
||||||
|
|||||||
@ -102,8 +102,13 @@ describe('@nx/eslint:init', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('(legacy)', () => {
|
describe('(legacy)', () => {
|
||||||
|
describe('CJS', () => {
|
||||||
it('should add the root eslint config to the lint targetDefaults for lint', async () => {
|
it('should add the root eslint config to the lint targetDefaults for lint', async () => {
|
||||||
await lintInitGenerator(tree, { ...options, addPlugin: false });
|
await lintInitGenerator(tree, {
|
||||||
|
...options,
|
||||||
|
addPlugin: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
readJson(tree, 'nx.json').targetDefaults['@nx/eslint:lint']
|
readJson(tree, 'nx.json').targetDefaults['@nx/eslint:lint']
|
||||||
@ -125,7 +130,11 @@ describe('@nx/eslint:init', () => {
|
|||||||
return json;
|
return json;
|
||||||
});
|
});
|
||||||
|
|
||||||
await lintInitGenerator(tree, { ...options, addPlugin: false });
|
await lintInitGenerator(tree, {
|
||||||
|
...options,
|
||||||
|
addPlugin: false,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
readJson<NxJsonConfiguration>(tree, 'nx.json').targetDefaults[
|
readJson<NxJsonConfiguration>(tree, 'nx.json').targetDefaults[
|
||||||
@ -142,4 +151,55 @@ describe('@nx/eslint:init', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('MJS', () => {
|
||||||
|
it('should add the root eslint config to the lint targetDefaults for lint', async () => {
|
||||||
|
await lintInitGenerator(tree, {
|
||||||
|
...options,
|
||||||
|
addPlugin: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
readJson(tree, 'nx.json').targetDefaults['@nx/eslint:lint']
|
||||||
|
).toEqual({
|
||||||
|
cache: true,
|
||||||
|
inputs: [
|
||||||
|
'default',
|
||||||
|
'{workspaceRoot}/.eslintrc.json',
|
||||||
|
'{workspaceRoot}/.eslintignore',
|
||||||
|
'{workspaceRoot}/eslint.config.mjs',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup lint target defaults', async () => {
|
||||||
|
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
|
||||||
|
json.namedInputs ??= {};
|
||||||
|
json.namedInputs.production = ['default'];
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
await lintInitGenerator(tree, {
|
||||||
|
...options,
|
||||||
|
addPlugin: false,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
readJson<NxJsonConfiguration>(tree, 'nx.json').targetDefaults[
|
||||||
|
'@nx/eslint:lint'
|
||||||
|
]
|
||||||
|
).toEqual({
|
||||||
|
cache: true,
|
||||||
|
inputs: [
|
||||||
|
'default',
|
||||||
|
'{workspaceRoot}/.eslintrc.json',
|
||||||
|
'{workspaceRoot}/.eslintignore',
|
||||||
|
'{workspaceRoot}/eslint.config.mjs',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,31 +11,37 @@ import {
|
|||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { addPlugin } from '@nx/devkit/src/utils/add-plugin';
|
import { addPlugin } from '@nx/devkit/src/utils/add-plugin';
|
||||||
import { eslintVersion, nxVersion } from '../../utils/versions';
|
import { eslintVersion, nxVersion } from '../../utils/versions';
|
||||||
import { findEslintFile } from '../utils/eslint-file';
|
import {
|
||||||
|
determineEslintConfigFormat,
|
||||||
|
findEslintFile,
|
||||||
|
} from '../utils/eslint-file';
|
||||||
import { createNodesV2 } from '../../plugins/plugin';
|
import { createNodesV2 } from '../../plugins/plugin';
|
||||||
import { hasEslintPlugin } from '../utils/plugin';
|
import { hasEslintPlugin } from '../utils/plugin';
|
||||||
|
import { extname } from 'path';
|
||||||
|
|
||||||
export interface LinterInitOptions {
|
export interface LinterInitOptions {
|
||||||
skipPackageJson?: boolean;
|
skipPackageJson?: boolean;
|
||||||
keepExistingVersions?: boolean;
|
keepExistingVersions?: boolean;
|
||||||
updatePackageScripts?: boolean;
|
updatePackageScripts?: boolean;
|
||||||
addPlugin?: boolean;
|
addPlugin?: boolean;
|
||||||
|
// Internal option
|
||||||
|
eslintConfigFormat?: 'mjs' | 'cjs';
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateProductionFileset(tree: Tree) {
|
function updateProductionFileset(tree: Tree, format: 'mjs' | 'cjs' = 'mjs') {
|
||||||
const nxJson = readNxJson(tree);
|
const nxJson = readNxJson(tree);
|
||||||
|
|
||||||
const productionFileSet = nxJson.namedInputs?.production;
|
const productionFileSet = nxJson.namedInputs?.production;
|
||||||
if (productionFileSet) {
|
if (productionFileSet) {
|
||||||
productionFileSet.push('!{projectRoot}/.eslintrc.json');
|
productionFileSet.push('!{projectRoot}/.eslintrc.json');
|
||||||
productionFileSet.push('!{projectRoot}/eslint.config.cjs');
|
productionFileSet.push(`!{projectRoot}/eslint.config.${format}`);
|
||||||
// Dedupe and set
|
// Dedupe and set
|
||||||
nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
|
nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
|
||||||
}
|
}
|
||||||
updateNxJson(tree, nxJson);
|
updateNxJson(tree, nxJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTargetDefaults(tree: Tree) {
|
function addTargetDefaults(tree: Tree, format: 'mjs' | 'cjs') {
|
||||||
const nxJson = readNxJson(tree);
|
const nxJson = readNxJson(tree);
|
||||||
|
|
||||||
nxJson.targetDefaults ??= {};
|
nxJson.targetDefaults ??= {};
|
||||||
@ -45,7 +51,7 @@ function addTargetDefaults(tree: Tree) {
|
|||||||
'default',
|
'default',
|
||||||
`{workspaceRoot}/.eslintrc.json`,
|
`{workspaceRoot}/.eslintrc.json`,
|
||||||
`{workspaceRoot}/.eslintignore`,
|
`{workspaceRoot}/.eslintignore`,
|
||||||
`{workspaceRoot}/eslint.config.cjs`,
|
`{workspaceRoot}/eslint.config.${format}`,
|
||||||
];
|
];
|
||||||
updateNxJson(tree, nxJson);
|
updateNxJson(tree, nxJson);
|
||||||
}
|
}
|
||||||
@ -74,9 +80,21 @@ export async function initEsLint(
|
|||||||
process.env.NX_ADD_PLUGINS !== 'false' &&
|
process.env.NX_ADD_PLUGINS !== 'false' &&
|
||||||
nxJson.useInferencePlugins !== false;
|
nxJson.useInferencePlugins !== false;
|
||||||
options.addPlugin ??= addPluginDefault;
|
options.addPlugin ??= addPluginDefault;
|
||||||
|
options.eslintConfigFormat ??= 'mjs';
|
||||||
const hasPlugin = hasEslintPlugin(tree);
|
const hasPlugin = hasEslintPlugin(tree);
|
||||||
const rootEslintFile = findEslintFile(tree);
|
const rootEslintFile = findEslintFile(tree);
|
||||||
|
|
||||||
|
if (rootEslintFile) {
|
||||||
|
const fileExtension = extname(rootEslintFile);
|
||||||
|
if (fileExtension === '.mjs' || fileExtension === '.cjs') {
|
||||||
|
options.eslintConfigFormat = fileExtension.slice(1) as 'mjs' | 'cjs';
|
||||||
|
} else {
|
||||||
|
options.eslintConfigFormat = determineEslintConfigFormat(
|
||||||
|
tree.read(rootEslintFile, 'utf-8')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const graph = await createProjectGraphAsync();
|
const graph = await createProjectGraphAsync();
|
||||||
|
|
||||||
const lintTargetNames = [
|
const lintTargetNames = [
|
||||||
@ -107,7 +125,7 @@ export async function initEsLint(
|
|||||||
return () => {};
|
return () => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProductionFileset(tree);
|
updateProductionFileset(tree, options.eslintConfigFormat);
|
||||||
|
|
||||||
updateVsCodeRecommendedExtensions(tree);
|
updateVsCodeRecommendedExtensions(tree);
|
||||||
|
|
||||||
@ -123,7 +141,7 @@ export async function initEsLint(
|
|||||||
options.updatePackageScripts
|
options.updatePackageScripts
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
addTargetDefaults(tree);
|
addTargetDefaults(tree, options.eslintConfigFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
|
|||||||
@ -42,7 +42,237 @@ describe('@nx/eslint:lint-project', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate a flat eslint base config', async () => {
|
describe('Eslint base config named eslint.base.config', () => {
|
||||||
|
it('should generate a flat eslint config format based on base config (JS with CJS export)', async () => {
|
||||||
|
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
||||||
|
|
||||||
|
// CJS config
|
||||||
|
tree.write('eslint.base.config.js', 'module.exports = {};');
|
||||||
|
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
skipFormat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('libs/test-lib/eslint.config.cjs', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"const baseConfig = require("../../eslint.base.config.js");
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
...baseConfig
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = originalEslintUseFlatConfigVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a flat eslint config format based on base config (JS with MJS export)', async () => {
|
||||||
|
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
||||||
|
|
||||||
|
// MJS config
|
||||||
|
tree.write('eslint.base.config.js', 'export default {};');
|
||||||
|
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
skipFormat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('libs/test-lib/eslint.config.mjs', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import baseConfig from "../../eslint.base.config.js";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = originalEslintUseFlatConfigVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a flat eslint config format based on base config (mjs)', async () => {
|
||||||
|
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
||||||
|
|
||||||
|
// MJS config
|
||||||
|
tree.write('eslint.base.config.mjs', 'export default {};');
|
||||||
|
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
skipFormat: true,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('libs/test-lib/eslint.config.mjs', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import baseConfig from "../../eslint.base.config.mjs";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = originalEslintUseFlatConfigVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a flat eslint config format based on base config CJS', async () => {
|
||||||
|
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
||||||
|
|
||||||
|
// CJS config
|
||||||
|
tree.write('eslint.base.config.cjs', 'module.exports = {};');
|
||||||
|
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
skipFormat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('libs/test-lib/eslint.config.cjs', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"const baseConfig = require("../../eslint.base.config.cjs");
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
...baseConfig
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = originalEslintUseFlatConfigVal;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Eslint base config named eslint.config', () => {
|
||||||
|
it('should generate a flat eslint config format based on base config (JS with CJS export)', async () => {
|
||||||
|
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
||||||
|
|
||||||
|
// CJS config
|
||||||
|
tree.write('eslint.config.js', 'module.exports = {};');
|
||||||
|
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
skipFormat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('libs/test-lib/eslint.config.cjs', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"const baseConfig = require("../../eslint.config.js");
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
...baseConfig
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = originalEslintUseFlatConfigVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a flat eslint config format based on base config (JS with MJS export)', async () => {
|
||||||
|
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
||||||
|
|
||||||
|
// MJS config
|
||||||
|
tree.write('eslint.config.js', 'export default {};');
|
||||||
|
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
skipFormat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('libs/test-lib/eslint.config.mjs', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import baseConfig from "../../eslint.config.js";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = originalEslintUseFlatConfigVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a flat eslint config format based on base config (mjs)', async () => {
|
||||||
|
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
||||||
|
|
||||||
|
// MJS config
|
||||||
|
tree.write('eslint.config.mjs', 'export default {};');
|
||||||
|
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
skipFormat: true,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('libs/test-lib/eslint.config.mjs', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = originalEslintUseFlatConfigVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a flat eslint config format based on base config CJS', async () => {
|
||||||
|
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
||||||
|
|
||||||
|
// CJS config
|
||||||
|
tree.write('eslint.config.cjs', 'module.exports = {};');
|
||||||
|
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
skipFormat: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('libs/test-lib/eslint.config.cjs', 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"const baseConfig = require("../../eslint.config.cjs");
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
...baseConfig
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = originalEslintUseFlatConfigVal;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a flat eslint base config ESM', async () => {
|
||||||
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
||||||
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
||||||
await lintProjectGenerator(tree, {
|
await lintProjectGenerator(tree, {
|
||||||
@ -51,6 +281,76 @@ describe('@nx/eslint:lint-project', () => {
|
|||||||
project: 'test-lib',
|
project: 'test-lib',
|
||||||
setParserOptionsProject: false,
|
setParserOptionsProject: false,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
|
eslintConfigFormat: 'mjs',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"import nx from "@nx/eslint-plugin";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...nx.configs["flat/base"],
|
||||||
|
...nx.configs["flat/typescript"],
|
||||||
|
...nx.configs["flat/javascript"],
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"**/dist"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
"**/*.js",
|
||||||
|
"**/*.jsx"
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
"@nx/enforce-module-boundaries": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [
|
||||||
|
"^.*/eslint(\\\\.base)?\\\\.config\\\\.[cm]?js$"
|
||||||
|
],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: "*",
|
||||||
|
onlyDependOnLibsWithTags: [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
"**/*.js",
|
||||||
|
"**/*.jsx",
|
||||||
|
"**/*.cjs",
|
||||||
|
"**/*.mjs"
|
||||||
|
],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = originalEslintUseFlatConfigVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a flat eslint base config CJS', async () => {
|
||||||
|
const originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG;
|
||||||
|
process.env.ESLINT_USE_FLAT_CONFIG = 'true';
|
||||||
|
await lintProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
project: 'test-lib',
|
||||||
|
setParserOptionsProject: false,
|
||||||
|
skipFormat: true,
|
||||||
|
eslintConfigFormat: 'cjs',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchInlineSnapshot(`
|
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
|||||||
@ -17,8 +17,11 @@ import {
|
|||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
|
|
||||||
import { Linter as LinterEnum, LinterType } from '../utils/linter';
|
import { Linter as LinterEnum, LinterType } from '../utils/linter';
|
||||||
import { findEslintFile } from '../utils/eslint-file';
|
import {
|
||||||
import { join } from 'path';
|
determineEslintConfigFormat,
|
||||||
|
findEslintFile,
|
||||||
|
} from '../utils/eslint-file';
|
||||||
|
import { extname, join } from 'path';
|
||||||
import { lintInitGenerator } from '../init/init';
|
import { lintInitGenerator } from '../init/init';
|
||||||
import type { Linter } from 'eslint';
|
import type { Linter } from 'eslint';
|
||||||
import { migrateConfigToMonorepoStyle } from '../init/init-migration';
|
import { migrateConfigToMonorepoStyle } from '../init/init-migration';
|
||||||
@ -32,7 +35,7 @@ import {
|
|||||||
} from '../utils/flat-config/ast-utils';
|
} from '../utils/flat-config/ast-utils';
|
||||||
import {
|
import {
|
||||||
baseEsLintConfigFile,
|
baseEsLintConfigFile,
|
||||||
baseEsLintFlatConfigFile,
|
BASE_ESLINT_CONFIG_FILENAMES,
|
||||||
} from '../../utils/config-file';
|
} from '../../utils/config-file';
|
||||||
import { hasEslintPlugin } from '../utils/plugin';
|
import { hasEslintPlugin } from '../utils/plugin';
|
||||||
import { setupRootEsLint } from './setup-root-eslint';
|
import { setupRootEsLint } from './setup-root-eslint';
|
||||||
@ -49,6 +52,7 @@ interface LintProjectOptions {
|
|||||||
rootProject?: boolean;
|
rootProject?: boolean;
|
||||||
keepExistingVersions?: boolean;
|
keepExistingVersions?: boolean;
|
||||||
addPlugin?: boolean;
|
addPlugin?: boolean;
|
||||||
|
eslintConfigFormat?: 'mjs' | 'cjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -66,6 +70,7 @@ export async function lintProjectGeneratorInternal(
|
|||||||
options: LintProjectOptions
|
options: LintProjectOptions
|
||||||
) {
|
) {
|
||||||
const nxJson = readNxJson(tree);
|
const nxJson = readNxJson(tree);
|
||||||
|
options.eslintConfigFormat ??= 'mjs';
|
||||||
const addPluginDefault =
|
const addPluginDefault =
|
||||||
process.env.NX_ADD_PLUGINS !== 'false' &&
|
process.env.NX_ADD_PLUGINS !== 'false' &&
|
||||||
nxJson.useInferencePlugins !== false;
|
nxJson.useInferencePlugins !== false;
|
||||||
@ -74,12 +79,14 @@ export async function lintProjectGeneratorInternal(
|
|||||||
const initTask = await lintInitGenerator(tree, {
|
const initTask = await lintInitGenerator(tree, {
|
||||||
skipPackageJson: options.skipPackageJson,
|
skipPackageJson: options.skipPackageJson,
|
||||||
addPlugin: options.addPlugin,
|
addPlugin: options.addPlugin,
|
||||||
|
eslintConfigFormat: options.eslintConfigFormat,
|
||||||
});
|
});
|
||||||
tasks.push(initTask);
|
tasks.push(initTask);
|
||||||
const rootEsLintTask = setupRootEsLint(tree, {
|
const rootEsLintTask = setupRootEsLint(tree, {
|
||||||
unitTestRunner: options.unitTestRunner,
|
unitTestRunner: options.unitTestRunner,
|
||||||
skipPackageJson: options.skipPackageJson,
|
skipPackageJson: options.skipPackageJson,
|
||||||
rootProject: options.rootProject,
|
rootProject: options.rootProject,
|
||||||
|
eslintConfigFormat: options.eslintConfigFormat,
|
||||||
});
|
});
|
||||||
tasks.push(rootEsLintTask);
|
tasks.push(rootEsLintTask);
|
||||||
const projectConfig = readProjectConfiguration(tree, options.project);
|
const projectConfig = readProjectConfiguration(tree, options.project);
|
||||||
@ -146,6 +153,7 @@ export async function lintProjectGeneratorInternal(
|
|||||||
filteredProjects,
|
filteredProjects,
|
||||||
tree,
|
tree,
|
||||||
options.unitTestRunner,
|
options.unitTestRunner,
|
||||||
|
options.eslintConfigFormat,
|
||||||
options.keepExistingVersions
|
options.keepExistingVersions
|
||||||
);
|
);
|
||||||
tasks.push(migrateTask);
|
tasks.push(migrateTask);
|
||||||
@ -199,6 +207,22 @@ function createEsLintConfiguration(
|
|||||||
const pathToRootConfig = extendedRootConfig
|
const pathToRootConfig = extendedRootConfig
|
||||||
? `${offsetFromRoot(projectConfig.root)}${extendedRootConfig}`
|
? `${offsetFromRoot(projectConfig.root)}${extendedRootConfig}`
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
if (extendedRootConfig) {
|
||||||
|
// We do not want to mix the formats
|
||||||
|
// if the base file extension is `.mjs` we should use `mjs` for the new file
|
||||||
|
// or if base the file extension is `.cjs` then the format should be `cjs`
|
||||||
|
|
||||||
|
const fileExtension = extname(extendedRootConfig);
|
||||||
|
if (fileExtension === '.mjs' || fileExtension === '.cjs') {
|
||||||
|
options.eslintConfigFormat = fileExtension.slice(1) as 'mjs' | 'cjs';
|
||||||
|
} else {
|
||||||
|
options.eslintConfigFormat = determineEslintConfigFormat(
|
||||||
|
tree.read(extendedRootConfig, 'utf-8')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const addDependencyChecks =
|
const addDependencyChecks =
|
||||||
options.addPackageJsonDependencyChecks ||
|
options.addPackageJsonDependencyChecks ||
|
||||||
isBuildableLibraryProject(projectConfig);
|
isBuildableLibraryProject(projectConfig);
|
||||||
@ -269,11 +293,18 @@ function createEsLintConfiguration(
|
|||||||
nodes.push(generateSpreadElement('baseConfig'));
|
nodes.push(generateSpreadElement('baseConfig'));
|
||||||
}
|
}
|
||||||
overrides.forEach((override) => {
|
overrides.forEach((override) => {
|
||||||
nodes.push(generateFlatOverride(override));
|
nodes.push(generateFlatOverride(override, options.eslintConfigFormat));
|
||||||
});
|
});
|
||||||
const nodeList = createNodeList(importMap, nodes);
|
const nodeList = createNodeList(
|
||||||
|
importMap,
|
||||||
|
nodes,
|
||||||
|
options.eslintConfigFormat
|
||||||
|
);
|
||||||
const content = stringifyNodeList(nodeList);
|
const content = stringifyNodeList(nodeList);
|
||||||
tree.write(join(projectConfig.root, `eslint.config.cjs`), content);
|
tree.write(
|
||||||
|
join(projectConfig.root, `eslint.config.${options.eslintConfigFormat}`),
|
||||||
|
content
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
writeJson(tree, join(projectConfig.root, `.eslintrc.json`), {
|
writeJson(tree, join(projectConfig.root, `.eslintrc.json`), {
|
||||||
extends: extendedRootConfig ? [pathToRootConfig] : undefined,
|
extends: extendedRootConfig ? [pathToRootConfig] : undefined,
|
||||||
@ -313,8 +344,9 @@ function isBuildableLibraryProject(
|
|||||||
function isMigrationToMonorepoNeeded(tree: Tree, graph: ProjectGraph): boolean {
|
function isMigrationToMonorepoNeeded(tree: Tree, graph: ProjectGraph): boolean {
|
||||||
// the base config is already created, migration has been done
|
// the base config is already created, migration has been done
|
||||||
if (
|
if (
|
||||||
tree.exists(baseEsLintConfigFile) ||
|
[baseEsLintConfigFile, ...BASE_ESLINT_CONFIG_FILENAMES].some((f) =>
|
||||||
tree.exists(baseEsLintFlatConfigFile)
|
tree.exists(f)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export type SetupRootEsLintOptions = {
|
|||||||
unitTestRunner?: string;
|
unitTestRunner?: string;
|
||||||
skipPackageJson?: boolean;
|
skipPackageJson?: boolean;
|
||||||
rootProject?: boolean;
|
rootProject?: boolean;
|
||||||
|
eslintConfigFormat?: 'mjs' | 'cjs';
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setupRootEsLint(
|
export function setupRootEsLint(
|
||||||
@ -32,6 +33,8 @@ export function setupRootEsLint(
|
|||||||
if (rootEslintFile) {
|
if (rootEslintFile) {
|
||||||
return () => {};
|
return () => {};
|
||||||
}
|
}
|
||||||
|
options.eslintConfigFormat ??= 'mjs';
|
||||||
|
|
||||||
if (!useFlatConfig(tree)) {
|
if (!useFlatConfig(tree)) {
|
||||||
return setUpLegacyRootEslintRc(tree, options);
|
return setUpLegacyRootEslintRc(tree, options);
|
||||||
}
|
}
|
||||||
@ -71,8 +74,11 @@ function setUpLegacyRootEslintRc(tree: Tree, options: SetupRootEsLintOptions) {
|
|||||||
|
|
||||||
function setUpRootFlatConfig(tree: Tree, options: SetupRootEsLintOptions) {
|
function setUpRootFlatConfig(tree: Tree, options: SetupRootEsLintOptions) {
|
||||||
tree.write(
|
tree.write(
|
||||||
'eslint.config.cjs',
|
`eslint.config.${options.eslintConfigFormat}`,
|
||||||
getGlobalFlatEslintConfiguration(options.rootProject)
|
getGlobalFlatEslintConfiguration(
|
||||||
|
options.eslintConfigFormat,
|
||||||
|
options.rootProject
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return !options.skipPackageJson
|
return !options.skipPackageJson
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { readJson, type Tree } from '@nx/devkit';
|
|||||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
import * as devkitInternals from 'nx/src/devkit-internals';
|
import * as devkitInternals from 'nx/src/devkit-internals';
|
||||||
import {
|
import {
|
||||||
|
BASE_ESLINT_CONFIG_FILENAMES,
|
||||||
ESLINT_CONFIG_FILENAMES,
|
ESLINT_CONFIG_FILENAMES,
|
||||||
baseEsLintConfigFile,
|
|
||||||
} from '../../utils/config-file';
|
} from '../../utils/config-file';
|
||||||
import {
|
import {
|
||||||
addExtendsToLintConfig,
|
addExtendsToLintConfig,
|
||||||
@ -32,12 +32,11 @@ describe('@nx/eslint:lint-file', () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
test.each(ESLINT_CONFIG_FILENAMES)(
|
test.each(BASE_ESLINT_CONFIG_FILENAMES)(
|
||||||
'should return base file instead %p when calling findEslintFile',
|
'should return base file %p when calling findEslintFile',
|
||||||
(eslintFileName) => {
|
(eslintFileName) => {
|
||||||
tree.write(baseEsLintConfigFile, '{}');
|
|
||||||
tree.write(eslintFileName, '{}');
|
tree.write(eslintFileName, '{}');
|
||||||
expect(findEslintFile(tree)).toBe(baseEsLintConfigFile);
|
expect(findEslintFile(tree)).toBe(eslintFileName);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -12,9 +12,8 @@ import type { Linter } from 'eslint';
|
|||||||
import { gte } from 'semver';
|
import { gte } from 'semver';
|
||||||
import {
|
import {
|
||||||
baseEsLintConfigFile,
|
baseEsLintConfigFile,
|
||||||
baseEsLintFlatConfigFile,
|
|
||||||
ESLINT_CONFIG_FILENAMES,
|
ESLINT_CONFIG_FILENAMES,
|
||||||
legacyBaseEsLintFlatConfigFile,
|
BASE_ESLINT_CONFIG_FILENAMES,
|
||||||
} from '../../utils/config-file';
|
} from '../../utils/config-file';
|
||||||
import {
|
import {
|
||||||
eslintFlatConfigFilenames,
|
eslintFlatConfigFilenames,
|
||||||
@ -45,17 +44,15 @@ export function findEslintFile(
|
|||||||
tree: Tree,
|
tree: Tree,
|
||||||
projectRoot?: string
|
projectRoot?: string
|
||||||
): string | null {
|
): string | null {
|
||||||
if (projectRoot === undefined && tree.exists(baseEsLintConfigFile)) {
|
if (projectRoot === undefined) {
|
||||||
return baseEsLintConfigFile;
|
for (const file of [
|
||||||
|
baseEsLintConfigFile,
|
||||||
|
...BASE_ESLINT_CONFIG_FILENAMES,
|
||||||
|
]) {
|
||||||
|
if (tree.exists(file)) {
|
||||||
|
return file;
|
||||||
}
|
}
|
||||||
if (projectRoot === undefined && tree.exists(baseEsLintFlatConfigFile)) {
|
|
||||||
return baseEsLintFlatConfigFile;
|
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
projectRoot === undefined &&
|
|
||||||
tree.exists(legacyBaseEsLintFlatConfigFile)
|
|
||||||
) {
|
|
||||||
return legacyBaseEsLintFlatConfigFile;
|
|
||||||
}
|
}
|
||||||
projectRoot ??= '';
|
projectRoot ??= '';
|
||||||
for (const file of ESLINT_CONFIG_FILENAMES) {
|
for (const file of ESLINT_CONFIG_FILENAMES) {
|
||||||
@ -75,7 +72,8 @@ export function isEslintConfigSupported(tree: Tree, projectRoot = ''): boolean {
|
|||||||
return (
|
return (
|
||||||
eslintFile.endsWith('.json') ||
|
eslintFile.endsWith('.json') ||
|
||||||
eslintFile.endsWith('.config.js') ||
|
eslintFile.endsWith('.config.js') ||
|
||||||
eslintFile.endsWith('.config.cjs')
|
eslintFile.endsWith('.config.cjs') ||
|
||||||
|
eslintFile.endsWith('.config.mjs')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,6 +146,19 @@ function replaceFlatConfigPaths(
|
|||||||
`require('${newPath}')` +
|
`require('${newPath}')` +
|
||||||
newConfig.slice(match.index + match[0].length);
|
newConfig.slice(match.index + match[0].length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle import statements
|
||||||
|
const importRegex = RegExp(/import\s+.*?\s+from\s+['"](.*)['"]/g);
|
||||||
|
while ((match = importRegex.exec(newConfig)) !== null) {
|
||||||
|
const oldPath = match[1];
|
||||||
|
const newPath = offsetFilePath(sourceRoot, oldPath, offset, tree);
|
||||||
|
|
||||||
|
// Replace the old path with the updated path
|
||||||
|
newConfig =
|
||||||
|
newConfig.slice(0, match.index + match[0].indexOf(oldPath)) +
|
||||||
|
newPath +
|
||||||
|
newConfig.slice(match.index + match[0].indexOf(oldPath) + oldPath.length);
|
||||||
|
}
|
||||||
// replace projects
|
// replace projects
|
||||||
const projectRegex = RegExp(/project:\s?\[?['"](.*)['"]\]?/g);
|
const projectRegex = RegExp(/project:\s?\[?['"](.*)['"]\]?/g);
|
||||||
while ((match = projectRegex.exec(newConfig)) !== null) {
|
while ((match = projectRegex.exec(newConfig)) !== null) {
|
||||||
@ -184,6 +195,22 @@ function offsetFilePath(
|
|||||||
return joinPathFragments(offset, projectRoot, pathToFile);
|
return joinPathFragments(offset, projectRoot, pathToFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function determineEslintConfigFormat(content: string): 'mjs' | 'cjs' {
|
||||||
|
const sourceFile = ts.createSourceFile(
|
||||||
|
'',
|
||||||
|
content,
|
||||||
|
ts.ScriptTarget.Latest,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if there's an `export default` in the AST
|
||||||
|
const hasExportDefault = sourceFile.statements.some(
|
||||||
|
(statement) => ts.isExportAssignment(statement) && !statement.isExportEquals
|
||||||
|
);
|
||||||
|
|
||||||
|
return hasExportDefault ? 'mjs' : 'cjs';
|
||||||
|
}
|
||||||
|
|
||||||
export function addOverrideToLintConfig(
|
export function addOverrideToLintConfig(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
root: string,
|
root: string,
|
||||||
@ -197,7 +224,12 @@ export function addOverrideToLintConfig(
|
|||||||
if (useFlatConfig(tree)) {
|
if (useFlatConfig(tree)) {
|
||||||
let fileName: string;
|
let fileName: string;
|
||||||
if (isBase) {
|
if (isBase) {
|
||||||
fileName = joinPathFragments(root, baseEsLintFlatConfigFile);
|
for (const file of BASE_ESLINT_CONFIG_FILENAMES) {
|
||||||
|
if (tree.exists(joinPathFragments(root, file))) {
|
||||||
|
fileName = joinPathFragments(root, file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const f of eslintFlatConfigFilenames) {
|
for (const f of eslintFlatConfigFilenames) {
|
||||||
if (tree.exists(joinPathFragments(root, f))) {
|
if (tree.exists(joinPathFragments(root, f))) {
|
||||||
@ -207,8 +239,10 @@ export function addOverrideToLintConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const flatOverride = generateFlatOverride(override);
|
|
||||||
let content = tree.read(fileName, 'utf8');
|
let content = tree.read(fileName, 'utf8');
|
||||||
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
|
|
||||||
|
const flatOverride = generateFlatOverride(override, format);
|
||||||
// Check if the provided override using legacy eslintrc properties or plugins, if so we need to add compat
|
// Check if the provided override using legacy eslintrc properties or plugins, if so we need to add compat
|
||||||
if (overrideNeedsCompat(override)) {
|
if (overrideNeedsCompat(override)) {
|
||||||
content = addFlatCompatToFlatConfig(content);
|
content = addFlatCompatToFlatConfig(content);
|
||||||
@ -306,7 +340,12 @@ export function lintConfigHasOverride(
|
|||||||
checkBaseConfig &&
|
checkBaseConfig &&
|
||||||
findEslintFile(tree, root).includes('.base');
|
findEslintFile(tree, root).includes('.base');
|
||||||
if (isBase) {
|
if (isBase) {
|
||||||
fileName = joinPathFragments(root, baseEsLintFlatConfigFile);
|
for (const file of BASE_ESLINT_CONFIG_FILENAMES) {
|
||||||
|
if (tree.exists(joinPathFragments(root, file))) {
|
||||||
|
fileName = joinPathFragments(root, file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (useFlatConfig(tree)) {
|
if (useFlatConfig(tree)) {
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
@ -343,13 +382,14 @@ export function replaceOverridesInLintConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let content = tree.read(fileName, 'utf8');
|
let content = tree.read(fileName, 'utf8');
|
||||||
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
// Check if any of the provided overrides using legacy eslintrc properties or plugins, if so we need to add compat
|
// Check if any of the provided overrides using legacy eslintrc properties or plugins, if so we need to add compat
|
||||||
if (overrides.some(overrideNeedsCompat)) {
|
if (overrides.some(overrideNeedsCompat)) {
|
||||||
content = addFlatCompatToFlatConfig(content);
|
content = addFlatCompatToFlatConfig(content);
|
||||||
}
|
}
|
||||||
content = removeOverridesFromLintConfig(content);
|
content = removeOverridesFromLintConfig(content);
|
||||||
overrides.forEach((override) => {
|
overrides.forEach((override) => {
|
||||||
const flatOverride = generateFlatOverride(override);
|
const flatOverride = generateFlatOverride(override, format);
|
||||||
content = addBlockToFlatConfigExport(content, flatOverride);
|
content = addBlockToFlatConfigExport(content, flatOverride);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -381,6 +421,14 @@ export function addExtendsToLintConfig(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check the file extension to determine the format of the config if it is .js we look for the export
|
||||||
|
const eslintConfigFormat = fileName.endsWith('.mjs')
|
||||||
|
? 'mjs'
|
||||||
|
: fileName.endsWith('.cjs')
|
||||||
|
? 'cjs'
|
||||||
|
: tree.read(fileName, 'utf-8').includes('module.exports')
|
||||||
|
? 'cjs'
|
||||||
|
: 'mjs';
|
||||||
|
|
||||||
let shouldImportEslintCompat = false;
|
let shouldImportEslintCompat = false;
|
||||||
// assume eslint version is 9 if not found, as it's what we'd be generating by default
|
// assume eslint version is 9 if not found, as it's what we'd be generating by default
|
||||||
|
|||||||
@ -30,7 +30,7 @@ describe('ast-utils', () => {
|
|||||||
it('should create appropriate ASTs for a flat config entries based on the provided legacy eslintrc JSON override data', () => {
|
it('should create appropriate ASTs for a flat config entries based on the provided legacy eslintrc JSON override data', () => {
|
||||||
// It's easier to review the stringified result of the AST than the AST itself
|
// It's easier to review the stringified result of the AST than the AST itself
|
||||||
const getOutput = (input: any) => {
|
const getOutput = (input: any) => {
|
||||||
const ast = generateFlatOverride(input);
|
const ast = generateFlatOverride(input, 'mjs');
|
||||||
return printTsNode(ast);
|
return printTsNode(ast);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -87,13 +87,13 @@ describe('ast-utils', () => {
|
|||||||
|
|
||||||
expect(
|
expect(
|
||||||
getOutput({
|
getOutput({
|
||||||
// It should not only nest the parser in languageOptions, but also wrap it in a require call because parsers are passed by reference in flat config
|
// It should not only nest the parser in languageOptions, but also wrap it in an import call because parsers are passed by reference in flat config
|
||||||
parser: 'jsonc-eslint-parser',
|
parser: 'jsonc-eslint-parser',
|
||||||
})
|
})
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"{
|
"{
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parser: require("jsonc-eslint-parser")
|
parser: await import("jsonc-eslint-parser")
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`);
|
`);
|
||||||
@ -188,8 +188,8 @@ describe('ast-utils', () => {
|
|||||||
|
|
||||||
describe('addBlockToFlatConfigExport', () => {
|
describe('addBlockToFlatConfigExport', () => {
|
||||||
it('should inject block to the end of the file', () => {
|
it('should inject block to the end of the file', () => {
|
||||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
const content = `import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -210,8 +210,9 @@ describe('ast-utils', () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const baseConfig = require("../../eslint.config.cjs");
|
"import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -228,14 +229,15 @@ describe('ast-utils', () => {
|
|||||||
rules: {
|
rules: {
|
||||||
"@nx/do-something-with-svg": "error"
|
"@nx/do-something-with-svg": "error"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
];"
|
];
|
||||||
|
"
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should inject spread to the beginning of the file', () => {
|
it('should inject spread to the beginning of the file', () => {
|
||||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
const content = `import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -252,10 +254,10 @@ describe('ast-utils', () => {
|
|||||||
{ insertAtTheEnd: false }
|
{ insertAtTheEnd: false }
|
||||||
);
|
);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const baseConfig = require("../../eslint.config.cjs");
|
"import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
|
||||||
...config,
|
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...config,
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -264,16 +266,17 @@ describe('ast-utils', () => {
|
|||||||
],
|
],
|
||||||
rules: {}
|
rules: {}
|
||||||
},
|
},
|
||||||
{ ignores: ["my-lib/.cache/**/*"] },
|
{ ignores: ["my-lib/.cache/**/*"] }
|
||||||
];"
|
];
|
||||||
|
"
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('addImportToFlatConfig', () => {
|
describe('addImportToFlatConfig', () => {
|
||||||
it('should inject import if not found', () => {
|
it('should inject import if not found', () => {
|
||||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
const content = `import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -286,13 +289,13 @@ describe('ast-utils', () => {
|
|||||||
];`;
|
];`;
|
||||||
const result = addImportToFlatConfig(
|
const result = addImportToFlatConfig(
|
||||||
content,
|
content,
|
||||||
'varName',
|
['varName'],
|
||||||
'@myorg/awesome-config'
|
'@myorg/awesome-config'
|
||||||
);
|
);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const varName = require("@myorg/awesome-config");
|
"import { varName } from "@myorg/awesome-config";
|
||||||
const baseConfig = require("../../eslint.config.cjs");
|
import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -307,9 +310,9 @@ describe('ast-utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update import if already found', () => {
|
it('should update import if already found', () => {
|
||||||
const content = `const { varName } = require("@myorg/awesome-config");
|
const content = `import { varName } from "@myorg/awesome-config";
|
||||||
const baseConfig = require("../../eslint.config.cjs");
|
import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -326,9 +329,9 @@ describe('ast-utils', () => {
|
|||||||
'@myorg/awesome-config'
|
'@myorg/awesome-config'
|
||||||
);
|
);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const { varName, otherName, someName } = require("@myorg/awesome-config");
|
"import { varName, otherName, someName } from "@myorg/awesome-config";
|
||||||
const baseConfig = require("../../eslint.config.cjs");
|
import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -343,9 +346,10 @@ describe('ast-utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not inject import if already exists', () => {
|
it('should not inject import if already exists', () => {
|
||||||
const content = `const { varName, otherName } = require("@myorg/awesome-config");
|
const content = `import { varName, otherName } from "@myorg/awesome-config";
|
||||||
const baseConfig = require("../../eslint.config.cjs");
|
import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -365,9 +369,10 @@ describe('ast-utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not update import if already exists', () => {
|
it('should not update import if already exists', () => {
|
||||||
const content = `const varName = require("@myorg/awesome-config");
|
const content = `import { varName } from "@myorg/awesome-config";
|
||||||
const baseConfig = require("../../eslint.config.cjs");
|
import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -380,7 +385,7 @@ describe('ast-utils', () => {
|
|||||||
];`;
|
];`;
|
||||||
const result = addImportToFlatConfig(
|
const result = addImportToFlatConfig(
|
||||||
content,
|
content,
|
||||||
'varName',
|
['varName'],
|
||||||
'@myorg/awesome-config'
|
'@myorg/awesome-config'
|
||||||
);
|
);
|
||||||
expect(result).toEqual(content);
|
expect(result).toEqual(content);
|
||||||
@ -390,10 +395,11 @@ describe('ast-utils', () => {
|
|||||||
describe('removeImportFromFlatConfig', () => {
|
describe('removeImportFromFlatConfig', () => {
|
||||||
it('should remove existing import from config if the var name matches', () => {
|
it('should remove existing import from config if the var name matches', () => {
|
||||||
const content = stripIndents`
|
const content = stripIndents`
|
||||||
const nx = require("@nx/eslint-plugin");
|
import nx from "@nx/eslint-plugin";
|
||||||
const thisShouldRemain = require("@nx/eslint-plugin");
|
import thisShouldRemain from "@nx/eslint-plugin";
|
||||||
const playwright = require('eslint-plugin-playwright');
|
import playwright from 'eslint-plugin-playwright';
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
playwright.configs['flat/recommended'],
|
playwright.configs['flat/recommended'],
|
||||||
];
|
];
|
||||||
`;
|
`;
|
||||||
@ -404,9 +410,10 @@ describe('ast-utils', () => {
|
|||||||
);
|
);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"
|
"
|
||||||
const thisShouldRemain = require("@nx/eslint-plugin");
|
import thisShouldRemain from "@nx/eslint-plugin";
|
||||||
const playwright = require('eslint-plugin-playwright');
|
import playwright from 'eslint-plugin-playwright';
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
playwright.configs['flat/recommended'],
|
playwright.configs['flat/recommended'],
|
||||||
];"
|
];"
|
||||||
`);
|
`);
|
||||||
@ -415,8 +422,8 @@ describe('ast-utils', () => {
|
|||||||
|
|
||||||
describe('addCompatToFlatConfig', () => {
|
describe('addCompatToFlatConfig', () => {
|
||||||
it('should add compat to config', () => {
|
it('should add compat to config', () => {
|
||||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
const content = `import baseConfig from "../../eslint.config.mjs";
|
||||||
module.exports = [
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -429,15 +436,18 @@ describe('ast-utils', () => {
|
|||||||
];`;
|
];`;
|
||||||
const result = addFlatCompatToFlatConfig(content);
|
const result = addFlatCompatToFlatConfig(content);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const js = require("@eslint/js");
|
import { dirname } from "path";
|
||||||
const baseConfig = require("../../eslint.config.cjs");
|
import { fileURLToPath } from "url";
|
||||||
|
import js from "@eslint/js";
|
||||||
|
import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -452,9 +462,10 @@ describe('ast-utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add only partially compat to config if parts exist', () => {
|
it('should add only partially compat to config if parts exist', () => {
|
||||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
const content = `import baseConfig from "../../eslint.config.mjs";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -467,15 +478,19 @@ describe('ast-utils', () => {
|
|||||||
];`;
|
];`;
|
||||||
const result = addFlatCompatToFlatConfig(content);
|
const result = addFlatCompatToFlatConfig(content);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const baseConfig = require("../../eslint.config.cjs");
|
import { dirname } from "path";
|
||||||
const js = require("@eslint/js");
|
import { fileURLToPath } from "url";
|
||||||
|
import baseConfig from "../../eslint.config.mjs";
|
||||||
|
import js from "@eslint/js";
|
||||||
|
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -490,16 +505,18 @@ describe('ast-utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not add compat to config if exist', () => {
|
it('should not add compat to config if exist', () => {
|
||||||
const content = `const FlatCompat = require("@eslint/eslintrc");
|
const content = `import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const baseConfig = require("../../eslint.config.cjs");
|
import baseConfig from "../../eslint.config.cjs";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -517,16 +534,17 @@ describe('ast-utils', () => {
|
|||||||
|
|
||||||
describe('removeOverridesFromLintConfig', () => {
|
describe('removeOverridesFromLintConfig', () => {
|
||||||
it('should remove all rules from config', () => {
|
it('should remove all rules from config', () => {
|
||||||
const content = `const FlatCompat = require("@eslint/eslintrc");
|
const content = `import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const baseConfig = require("../../eslint.config.cjs");
|
import js from "@eslint/js";
|
||||||
const js = require("@eslint/js");
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
@ -557,16 +575,17 @@ describe('ast-utils', () => {
|
|||||||
];`;
|
];`;
|
||||||
const result = removeOverridesFromLintConfig(content);
|
const result = removeOverridesFromLintConfig(content);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const FlatCompat = require("@eslint/eslintrc");
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const baseConfig = require("../../eslint.config.cjs");
|
import js from "@eslint/js";
|
||||||
const js = require("@eslint/js");
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
{ ignores: ["my-lib/.cache/**/*"] },
|
{ ignores: ["my-lib/.cache/**/*"] },
|
||||||
];"
|
];"
|
||||||
@ -574,9 +593,9 @@ describe('ast-utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should remove all rules from starting with first', () => {
|
it('should remove all rules from starting with first', () => {
|
||||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
const content = `import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
"my-lib/**/*.ts",
|
"my-lib/**/*.ts",
|
||||||
@ -605,9 +624,9 @@ describe('ast-utils', () => {
|
|||||||
];`;
|
];`;
|
||||||
const result = removeOverridesFromLintConfig(content);
|
const result = removeOverridesFromLintConfig(content);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const baseConfig = require("../../eslint.config.cjs");
|
"import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
];"
|
];"
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
@ -615,9 +634,9 @@ describe('ast-utils', () => {
|
|||||||
|
|
||||||
describe('replaceOverride', () => {
|
describe('replaceOverride', () => {
|
||||||
it('should find and replace rules in override', () => {
|
it('should find and replace rules in override', () => {
|
||||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
const content = `import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
"my-lib/**/*.ts",
|
"my-lib/**/*.ts",
|
||||||
@ -657,9 +676,9 @@ module.exports = [
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const baseConfig = require("../../eslint.config.cjs");
|
"import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
"my-lib/**/*.ts",
|
"my-lib/**/*.ts",
|
||||||
@ -692,9 +711,9 @@ module.exports = [
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should append rules in override', () => {
|
it('should append rules in override', () => {
|
||||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
const content = `import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
{
|
{
|
||||||
files: [
|
files: [
|
||||||
"my-lib/**/*.ts",
|
"my-lib/**/*.ts",
|
||||||
@ -728,9 +747,9 @@ module.exports = [
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const baseConfig = require("../../eslint.config.cjs");
|
"import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
"my-lib/**/*.ts",
|
"my-lib/**/*.ts",
|
||||||
@ -755,9 +774,9 @@ module.exports = [
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should work for compat overrides', () => {
|
it('should work for compat overrides', () => {
|
||||||
const content = `const baseConfig = require("../../eslint.config.cjs");
|
const content = `import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
...compat.config({ extends: ["plugin:@nx/typescript"] }).map(config => ({
|
...compat.config({ extends: ["plugin:@nx/typescript"] }).map(config => ({
|
||||||
...config,
|
...config,
|
||||||
files: [
|
files: [
|
||||||
@ -783,9 +802,9 @@ module.exports = [
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const baseConfig = require("../../eslint.config.cjs");
|
"import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
...compat.config({ extends: ["plugin:@nx/typescript"] }).map(config => ({
|
...compat.config({ extends: ["plugin:@nx/typescript"] }).map(config => ({
|
||||||
...config,
|
...config,
|
||||||
"files": [
|
"files": [
|
||||||
@ -804,14 +823,17 @@ module.exports = [
|
|||||||
|
|
||||||
describe('removePlugin', () => {
|
describe('removePlugin', () => {
|
||||||
it('should remove plugins from config', () => {
|
it('should remove plugins from config', () => {
|
||||||
const content = `const { FlatCompat } = require("@eslint/eslintrc");
|
const content = `import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const nxEslintPlugin = require("@nx/eslint-plugin");
|
import nxEslintPlugin from "@nx/eslint-plugin";
|
||||||
const js = require("@eslint/js");
|
import js = from ("@eslint/js");
|
||||||
|
import { fileURLToPath} from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url));
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
export default [
|
||||||
{ plugins: { "@nx": nxEslintPlugin } },
|
{ plugins: { "@nx": nxEslintPlugin } },
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
@ -819,13 +841,16 @@ module.exports = [
|
|||||||
|
|
||||||
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const js = require("@eslint/js");
|
import js = from ("@eslint/js");
|
||||||
|
import { fileURLToPath} from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url));
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
export default [
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
];"
|
];"
|
||||||
@ -833,15 +858,19 @@ module.exports = [
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should remove single plugin from config', () => {
|
it('should remove single plugin from config', () => {
|
||||||
const content = `const { FlatCompat } = require("@eslint/eslintrc");
|
const content = `import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const nxEslintPlugin = require("@nx/eslint-plugin");
|
import nxEslintPlugin from "@nx/eslint-plugin";
|
||||||
const otherPlugin = require("other/eslint-plugin");
|
import otherPlugin from "other/eslint-plugin";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ plugins: { "@nx": nxEslintPlugin, "@other": otherPlugin } },
|
{ plugins: { "@nx": nxEslintPlugin, "@other": otherPlugin } },
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
@ -849,14 +878,18 @@ module.exports = [
|
|||||||
|
|
||||||
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const otherPlugin = require("other/eslint-plugin");
|
import otherPlugin from "other/eslint-plugin";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ plugins: { "@other": otherPlugin } },
|
{ plugins: { "@other": otherPlugin } },
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
@ -865,14 +898,18 @@ module.exports = [
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should leave other properties in config', () => {
|
it('should leave other properties in config', () => {
|
||||||
const content = `const { FlatCompat } = require("@eslint/eslintrc");
|
const content = `import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const nxEslintPlugin = require("@nx/eslint-plugin");
|
import nxEslintPlugin from "@nx/eslint-plugin";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ plugins: { "@nx": nxEslintPlugin }, rules: {} },
|
{ plugins: { "@nx": nxEslintPlugin }, rules: {} },
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
@ -880,13 +917,17 @@ module.exports = [
|
|||||||
|
|
||||||
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ rules: {} },
|
{ rules: {} },
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
@ -895,14 +936,18 @@ module.exports = [
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should remove single plugin from config array', () => {
|
it('should remove single plugin from config array', () => {
|
||||||
const content = `const { FlatCompat } = require("@eslint/eslintrc");
|
const content = `import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const nxEslintPlugin = require("@nx/eslint-plugin");
|
import nxEslintPlugin from "@nx/eslint-plugin";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ plugins: ["@nx", "something-else"] },
|
{ plugins: ["@nx", "something-else"] },
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
@ -910,13 +955,17 @@ module.exports = [
|
|||||||
|
|
||||||
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ plugins:["something-else"] },
|
{ plugins:["something-else"] },
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
@ -925,14 +974,18 @@ module.exports = [
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should leave other fields in the object', () => {
|
it('should leave other fields in the object', () => {
|
||||||
const content = `const { FlatCompat } = require("@eslint/eslintrc");
|
const content = `import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const nxEslintPlugin = require("@nx/eslint-plugin");
|
import nxEslintPlugin from "@nx/eslint-plugin";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ plugins: ["@nx"], rules: { } },
|
{ plugins: ["@nx"], rules: { } },
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
@ -940,13 +993,17 @@ module.exports = [
|
|||||||
|
|
||||||
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ rules: { } },
|
{ rules: { } },
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
@ -955,14 +1012,19 @@ module.exports = [
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should remove entire plugin when array with single element', () => {
|
it('should remove entire plugin when array with single element', () => {
|
||||||
const content = `const { FlatCompat } = require("@eslint/eslintrc");
|
const content = `import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const nxEslintPlugin = require("@nx/eslint-plugin");
|
import nxEslintPlugin from "@nx/eslint-plugin";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ plugins: ["@nx"] },
|
{ plugins: ["@nx"] },
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
@ -970,13 +1032,18 @@ module.exports = [
|
|||||||
|
|
||||||
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
const result = removePlugin(content, '@nx', '@nx/eslint-plugin');
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ ignores: ["src/ignore/to/keep.ts"] },
|
{ ignores: ["src/ignore/to/keep.ts"] },
|
||||||
{ ignores: ["something/else"] }
|
{ ignores: ["something/else"] }
|
||||||
];"
|
];"
|
||||||
@ -986,14 +1053,18 @@ module.exports = [
|
|||||||
|
|
||||||
describe('removeCompatExtends', () => {
|
describe('removeCompatExtends', () => {
|
||||||
it('should remove compat extends from config', () => {
|
it('should remove compat extends from config', () => {
|
||||||
const content = `const { FlatCompat } = require("@eslint/eslintrc");
|
const content = `import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const nxEslintPlugin = require("@nx/eslint-plugin");
|
import nxEslintPlugin from "@nx/eslint-plugin";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from "path";
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ plugins: { "@nx": nxEslintPlugin } },
|
{ plugins: { "@nx": nxEslintPlugin } },
|
||||||
...compat.config({ extends: ["plugin:@nx/typescript"] }).map(config => ({
|
...compat.config({ extends: ["plugin:@nx/typescript"] }).map(config => ({
|
||||||
...config,
|
...config,
|
||||||
@ -1013,14 +1084,18 @@ module.exports = [
|
|||||||
'plugin:@nx/javascript',
|
'plugin:@nx/javascript',
|
||||||
]);
|
]);
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const { FlatCompat } = require("@eslint/eslintrc");
|
"import { FlatCompat } from "@eslint/eslintrc";
|
||||||
const nxEslintPlugin = require("@nx/eslint-plugin");
|
import nxEslintPlugin from "@nx/eslint-plugin";
|
||||||
const js = require("@eslint/js");
|
import js from "@eslint/js";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { dirname } from "path";
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
recommendedConfig: js.configs.recommended,
|
recommendedConfig: js.configs.recommended,
|
||||||
});
|
});
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
{ plugins: { "@nx": nxEslintPlugin } },
|
{ plugins: { "@nx": nxEslintPlugin } },
|
||||||
{
|
{
|
||||||
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||||
@ -1039,9 +1114,10 @@ module.exports = [
|
|||||||
describe('removePredefinedConfigs', () => {
|
describe('removePredefinedConfigs', () => {
|
||||||
it('should remove config objects and import', () => {
|
it('should remove config objects and import', () => {
|
||||||
const content = stripIndents`
|
const content = stripIndents`
|
||||||
const nx = require("@nx/eslint-plugin");
|
import nx from "@nx/eslint-plugin";
|
||||||
const playwright = require('eslint-plugin-playwright');
|
import playwright from 'eslint-plugin-playwright';
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
...nx.config['flat/base'],
|
...nx.config['flat/base'],
|
||||||
...nx.config['flat/typescript'],
|
...nx.config['flat/typescript'],
|
||||||
...nx.config['flat/javascript'],
|
...nx.config['flat/javascript'],
|
||||||
@ -1058,8 +1134,9 @@ module.exports = [
|
|||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"
|
"
|
||||||
const playwright = require('eslint-plugin-playwright');
|
import playwright from 'eslint-plugin-playwright';
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
playwright.configs['flat/recommended'],
|
playwright.configs['flat/recommended'],
|
||||||
];"
|
];"
|
||||||
`);
|
`);
|
||||||
@ -1067,9 +1144,10 @@ module.exports = [
|
|||||||
|
|
||||||
it('should keep configs that are not in the list', () => {
|
it('should keep configs that are not in the list', () => {
|
||||||
const content = stripIndents`
|
const content = stripIndents`
|
||||||
const nx = require("@nx/eslint-plugin");
|
import nx from "@nx/eslint-plugin";
|
||||||
const playwright = require('eslint-plugin-playwright');
|
import playwright from 'eslint-plugin-playwright';
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
...nx.config['flat/base'],
|
...nx.config['flat/base'],
|
||||||
...nx.config['flat/typescript'],
|
...nx.config['flat/typescript'],
|
||||||
...nx.config['flat/javascript'],
|
...nx.config['flat/javascript'],
|
||||||
@ -1086,9 +1164,10 @@ module.exports = [
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
"const nx = require("@nx/eslint-plugin");
|
"import nx from "@nx/eslint-plugin";
|
||||||
const playwright = require('eslint-plugin-playwright');
|
import playwright from 'eslint-plugin-playwright';
|
||||||
module.exports = [
|
|
||||||
|
export default [
|
||||||
...nx.config['flat/react'],
|
...nx.config['flat/react'],
|
||||||
playwright.configs['flat/recommended'],
|
playwright.configs['flat/recommended'],
|
||||||
];"
|
];"
|
||||||
|
|||||||
@ -26,7 +26,10 @@ export function removeOverridesFromLintConfig(content: string): string {
|
|||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
);
|
);
|
||||||
|
|
||||||
const exportsArray = findAllBlocks(source);
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
|
|
||||||
|
const exportsArray =
|
||||||
|
format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
|
||||||
if (!exportsArray) {
|
if (!exportsArray) {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
@ -47,7 +50,19 @@ export function removeOverridesFromLintConfig(content: string): string {
|
|||||||
return applyChangesToString(content, changes);
|
return applyChangesToString(content, changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
function findAllBlocks(source: ts.SourceFile): ts.NodeArray<ts.Node> {
|
// TODO Change name
|
||||||
|
function findExportDefault(source: ts.SourceFile): ts.NodeArray<ts.Node> {
|
||||||
|
return ts.forEachChild(source, function analyze(node) {
|
||||||
|
if (
|
||||||
|
ts.isExportAssignment(node) &&
|
||||||
|
ts.isArrayLiteralExpression(node.expression)
|
||||||
|
) {
|
||||||
|
return node.expression.elements;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function findModuleExports(source: ts.SourceFile): ts.NodeArray<ts.Node> {
|
||||||
return ts.forEachChild(source, function analyze(node) {
|
return ts.forEachChild(source, function analyze(node) {
|
||||||
if (
|
if (
|
||||||
ts.isExpressionStatement(node) &&
|
ts.isExpressionStatement(node) &&
|
||||||
@ -86,7 +101,9 @@ export function hasOverride(
|
|||||||
true,
|
true,
|
||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
);
|
);
|
||||||
const exportsArray = findAllBlocks(source);
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
|
const exportsArray =
|
||||||
|
format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
|
||||||
if (!exportsArray) {
|
if (!exportsArray) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -120,6 +137,7 @@ function parseTextToJson(text: string): any {
|
|||||||
.replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": ')
|
.replace(/\s([a-zA-Z0-9_]+)\s*:/g, ' "$1": ')
|
||||||
// stringify any require calls to avoid JSON parsing errors, turn them into just the string value being required
|
// stringify any require calls to avoid JSON parsing errors, turn them into just the string value being required
|
||||||
.replace(/require\(['"]([^'"]+)['"]\)/g, '"$1"')
|
.replace(/require\(['"]([^'"]+)['"]\)/g, '"$1"')
|
||||||
|
.replace(/\(?await import\(['"]([^'"]+)['"]\)\)?/g, '"$1"')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +159,9 @@ export function replaceOverride(
|
|||||||
true,
|
true,
|
||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
);
|
);
|
||||||
const exportsArray = findAllBlocks(source);
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
|
const exportsArray =
|
||||||
|
format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
|
||||||
if (!exportsArray) {
|
if (!exportsArray) {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
@ -174,20 +194,24 @@ export function replaceOverride(
|
|||||||
let updatedData = update(data);
|
let updatedData = update(data);
|
||||||
if (updatedData) {
|
if (updatedData) {
|
||||||
updatedData = mapFilePaths(updatedData);
|
updatedData = mapFilePaths(updatedData);
|
||||||
|
|
||||||
|
const parserReplacement =
|
||||||
|
format === 'mjs'
|
||||||
|
? (parser: string) => `(await import('${parser}'))`
|
||||||
|
: (parser: string) => `require('${parser}')`;
|
||||||
|
|
||||||
changes.push({
|
changes.push({
|
||||||
type: ChangeType.Insert,
|
type: ChangeType.Insert,
|
||||||
index: start,
|
index: start,
|
||||||
// NOTE: Indentation added to format without formatting tools like Prettier.
|
|
||||||
text:
|
text:
|
||||||
' ' +
|
' ' +
|
||||||
JSON.stringify(updatedData, null, 2)
|
JSON.stringify(updatedData, null, 2)
|
||||||
// restore any parser require calls that were stripped during JSON parsing
|
.replace(
|
||||||
.replace(/"parser": "([^"]+)"/g, (_, parser) => {
|
/"parser": "([^"]+)"/g,
|
||||||
return `"parser": require('${parser}')`;
|
(_, parser) => `"parser": ${parserReplacement(parser)}`
|
||||||
})
|
)
|
||||||
.slice(2, -2) // remove curly braces and start/end line breaks since we are injecting just properties
|
.slice(2, -2) // Remove curly braces and start/end line breaks
|
||||||
// Append indentation so file is formatted without Prettier
|
.replaceAll(/\n/g, '\n '), // Maintain indentation
|
||||||
.replaceAll(/\n/g, '\n '),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,7 +222,12 @@ export function replaceOverride(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adding require statement to the top of the file
|
* Adding import statement to the top of the file
|
||||||
|
* The imports are added based on a few rules:
|
||||||
|
* 1. If it's a default import and matches the variable, return content unchanged.
|
||||||
|
* 2. If it's a named import and the variables are not part of the import object, add them.
|
||||||
|
* 3. If no existing import and variable is a string, add a default import.
|
||||||
|
* 4. If no existing import and variable is an array, add it as an object import.
|
||||||
*/
|
*/
|
||||||
export function addImportToFlatConfig(
|
export function addImportToFlatConfig(
|
||||||
content: string,
|
content: string,
|
||||||
@ -214,6 +243,159 @@ export function addImportToFlatConfig(
|
|||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
|
|
||||||
|
if (format === 'mjs') {
|
||||||
|
return addESMImportToFlatConfig(source, printer, content, variable, imp);
|
||||||
|
}
|
||||||
|
return addCJSImportToFlatConfig(source, printer, content, variable, imp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addESMImportToFlatConfig(
|
||||||
|
source: ts.SourceFile,
|
||||||
|
printer: ts.Printer,
|
||||||
|
content: string,
|
||||||
|
variable: string | string[],
|
||||||
|
imp: string
|
||||||
|
): string {
|
||||||
|
let existingImport: ts.ImportDeclaration | undefined;
|
||||||
|
|
||||||
|
ts.forEachChild(source, (node) => {
|
||||||
|
if (
|
||||||
|
ts.isImportDeclaration(node) &&
|
||||||
|
ts.isStringLiteral(node.moduleSpecifier) &&
|
||||||
|
node.moduleSpecifier.text === imp
|
||||||
|
) {
|
||||||
|
existingImport = node;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rule 1:
|
||||||
|
if (
|
||||||
|
existingImport &&
|
||||||
|
typeof variable === 'string' &&
|
||||||
|
existingImport.importClause?.name?.getText() === variable
|
||||||
|
) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule 2:
|
||||||
|
if (
|
||||||
|
existingImport &&
|
||||||
|
existingImport.importClause?.namedBindings &&
|
||||||
|
Array.isArray(variable)
|
||||||
|
) {
|
||||||
|
const namedImports = existingImport.importClause
|
||||||
|
.namedBindings as ts.NamedImports;
|
||||||
|
const existingElements = namedImports.elements;
|
||||||
|
|
||||||
|
// Filter out variables that are already imported
|
||||||
|
const newVariables = variable.filter(
|
||||||
|
(v) => !existingElements.some((e) => e.name.getText() === v)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newVariables.length === 0) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newImportSpecifiers = newVariables.map((v) =>
|
||||||
|
ts.factory.createImportSpecifier(
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
ts.factory.createIdentifier(v)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const lastElement = existingElements[existingElements.length - 1];
|
||||||
|
const insertIndex = lastElement
|
||||||
|
? lastElement.getEnd()
|
||||||
|
: namedImports.getEnd();
|
||||||
|
|
||||||
|
const insertText = printer.printList(
|
||||||
|
ts.ListFormat.NamedImportsOrExportsElements,
|
||||||
|
ts.factory.createNodeArray(newImportSpecifiers),
|
||||||
|
source
|
||||||
|
);
|
||||||
|
|
||||||
|
return applyChangesToString(content, [
|
||||||
|
{
|
||||||
|
type: ChangeType.Insert,
|
||||||
|
index: insertIndex,
|
||||||
|
text: `, ${insertText}`,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule 3:
|
||||||
|
if (!existingImport && typeof variable === 'string') {
|
||||||
|
const defaultImport = ts.factory.createImportDeclaration(
|
||||||
|
undefined,
|
||||||
|
ts.factory.createImportClause(
|
||||||
|
false,
|
||||||
|
ts.factory.createIdentifier(variable),
|
||||||
|
undefined
|
||||||
|
),
|
||||||
|
ts.factory.createStringLiteral(imp)
|
||||||
|
);
|
||||||
|
|
||||||
|
const insert = printer.printNode(
|
||||||
|
ts.EmitHint.Unspecified,
|
||||||
|
defaultImport,
|
||||||
|
source
|
||||||
|
);
|
||||||
|
|
||||||
|
return applyChangesToString(content, [
|
||||||
|
{
|
||||||
|
type: ChangeType.Insert,
|
||||||
|
index: 0,
|
||||||
|
text: `${insert}\n`,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule 4:
|
||||||
|
if (!existingImport && Array.isArray(variable)) {
|
||||||
|
const objectImport = ts.factory.createImportDeclaration(
|
||||||
|
undefined,
|
||||||
|
ts.factory.createImportClause(
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
ts.factory.createNamedImports(
|
||||||
|
variable.map((v) =>
|
||||||
|
ts.factory.createImportSpecifier(
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
ts.factory.createIdentifier(v)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
ts.factory.createStringLiteral(imp)
|
||||||
|
);
|
||||||
|
|
||||||
|
const insert = printer.printNode(
|
||||||
|
ts.EmitHint.Unspecified,
|
||||||
|
objectImport,
|
||||||
|
source
|
||||||
|
);
|
||||||
|
|
||||||
|
return applyChangesToString(content, [
|
||||||
|
{
|
||||||
|
type: ChangeType.Insert,
|
||||||
|
index: 0,
|
||||||
|
text: `${insert}\n`,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCJSImportToFlatConfig(
|
||||||
|
source: ts.SourceFile,
|
||||||
|
printer: ts.Printer,
|
||||||
|
content: string,
|
||||||
|
variable: string | string[],
|
||||||
|
imp: string
|
||||||
|
): string {
|
||||||
const foundBindingVars: ts.NodeArray<ts.BindingElement> = ts.forEachChild(
|
const foundBindingVars: ts.NodeArray<ts.BindingElement> = ts.forEachChild(
|
||||||
source,
|
source,
|
||||||
function analyze(node) {
|
function analyze(node) {
|
||||||
@ -322,6 +504,22 @@ export function addImportToFlatConfig(
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function existsAsNamedOrDefaultImport(
|
||||||
|
node: ts.ImportDeclaration,
|
||||||
|
variable: string | string[]
|
||||||
|
) {
|
||||||
|
const isNamed =
|
||||||
|
node.importClause.namedBindings &&
|
||||||
|
ts.isNamedImports(node.importClause.namedBindings);
|
||||||
|
if (Array.isArray(variable)) {
|
||||||
|
return isNamed || variable.includes(node.importClause?.name?.getText());
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
(node.importClause.namedBindings &&
|
||||||
|
ts.isNamedImports(node.importClause.namedBindings)) ||
|
||||||
|
node.importClause?.name?.getText() === variable
|
||||||
|
);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Remove an import from flat config
|
* Remove an import from flat config
|
||||||
*/
|
*/
|
||||||
@ -338,8 +536,49 @@ export function removeImportFromFlatConfig(
|
|||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
|
if (format === 'mjs') {
|
||||||
|
return removeImportFromFlatConfigESM(source, content, variable, imp);
|
||||||
|
} else {
|
||||||
|
return removeImportFromFlatConfigCJS(source, content, variable, imp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeImportFromFlatConfigESM(
|
||||||
|
source: ts.SourceFile,
|
||||||
|
content: string,
|
||||||
|
variable: string,
|
||||||
|
imp: string
|
||||||
|
): string {
|
||||||
const changes: StringChange[] = [];
|
const changes: StringChange[] = [];
|
||||||
|
|
||||||
|
ts.forEachChild(source, (node) => {
|
||||||
|
// we can only combine object binding patterns
|
||||||
|
if (
|
||||||
|
ts.isImportDeclaration(node) &&
|
||||||
|
ts.isStringLiteral(node.moduleSpecifier) &&
|
||||||
|
node.moduleSpecifier.text === imp &&
|
||||||
|
node.importClause &&
|
||||||
|
existsAsNamedOrDefaultImport(node, variable)
|
||||||
|
) {
|
||||||
|
changes.push({
|
||||||
|
type: ChangeType.Delete,
|
||||||
|
start: node.pos,
|
||||||
|
length: node.end - node.pos,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return applyChangesToString(content, changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeImportFromFlatConfigCJS(
|
||||||
|
source: ts.SourceFile,
|
||||||
|
content: string,
|
||||||
|
variable: string,
|
||||||
|
imp: string
|
||||||
|
): string {
|
||||||
|
const changes: StringChange[] = [];
|
||||||
ts.forEachChild(source, (node) => {
|
ts.forEachChild(source, (node) => {
|
||||||
// we can only combine object binding patterns
|
// we can only combine object binding patterns
|
||||||
if (
|
if (
|
||||||
@ -367,7 +606,7 @@ export function removeImportFromFlatConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injects new ts.expression to the end of the module.exports array.
|
* Injects new ts.expression to the end of the module.exports or export default array.
|
||||||
*/
|
*/
|
||||||
export function addBlockToFlatConfigExport(
|
export function addBlockToFlatConfigExport(
|
||||||
content: string,
|
content: string,
|
||||||
@ -385,6 +624,79 @@ export function addBlockToFlatConfigExport(
|
|||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
|
|
||||||
|
// find the export default array statement
|
||||||
|
if (format === 'mjs') {
|
||||||
|
return addBlockToFlatConfigExportESM(
|
||||||
|
content,
|
||||||
|
config,
|
||||||
|
source,
|
||||||
|
printer,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return addBlockToFlatConfigExportCJS(
|
||||||
|
content,
|
||||||
|
config,
|
||||||
|
source,
|
||||||
|
printer,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addBlockToFlatConfigExportESM(
|
||||||
|
content: string,
|
||||||
|
config: ts.Expression | ts.SpreadElement,
|
||||||
|
source: ts.SourceFile,
|
||||||
|
printer: ts.Printer,
|
||||||
|
options: { insertAtTheEnd?: boolean; checkBaseConfig?: boolean } = {
|
||||||
|
insertAtTheEnd: true,
|
||||||
|
}
|
||||||
|
): string {
|
||||||
|
const exportDefaultStatement = source.statements.find(
|
||||||
|
(statement): statement is ts.ExportAssignment =>
|
||||||
|
ts.isExportAssignment(statement) &&
|
||||||
|
ts.isArrayLiteralExpression(statement.expression)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!exportDefaultStatement) return content;
|
||||||
|
|
||||||
|
const exportArrayLiteral =
|
||||||
|
exportDefaultStatement.expression as ts.ArrayLiteralExpression;
|
||||||
|
|
||||||
|
const updatedArrayElements = options.insertAtTheEnd
|
||||||
|
? [...exportArrayLiteral.elements, config]
|
||||||
|
: [config, ...exportArrayLiteral.elements];
|
||||||
|
|
||||||
|
const updatedExportDefault = ts.factory.createExportAssignment(
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
ts.factory.createArrayLiteralExpression(updatedArrayElements, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
// update the existing export default array
|
||||||
|
const updatedStatements = source.statements.map((statement) =>
|
||||||
|
statement === exportDefaultStatement ? updatedExportDefault : statement
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedSource = ts.factory.updateSourceFile(source, updatedStatements);
|
||||||
|
|
||||||
|
return printer
|
||||||
|
.printFile(updatedSource)
|
||||||
|
.replace(/export default/, '\nexport default');
|
||||||
|
}
|
||||||
|
|
||||||
|
function addBlockToFlatConfigExportCJS(
|
||||||
|
content: string,
|
||||||
|
config: ts.Expression | ts.SpreadElement,
|
||||||
|
source: ts.SourceFile,
|
||||||
|
printer: ts.Printer,
|
||||||
|
options: { insertAtTheEnd?: boolean; checkBaseConfig?: boolean } = {
|
||||||
|
insertAtTheEnd: true,
|
||||||
|
}
|
||||||
|
): string {
|
||||||
const exportsArray = ts.forEachChild(source, function analyze(node) {
|
const exportsArray = ts.forEachChild(source, function analyze(node) {
|
||||||
if (
|
if (
|
||||||
ts.isExpressionStatement(node) &&
|
ts.isExpressionStatement(node) &&
|
||||||
@ -443,7 +755,31 @@ export function removePlugin(
|
|||||||
true,
|
true,
|
||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
);
|
);
|
||||||
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
const changes: StringChange[] = [];
|
const changes: StringChange[] = [];
|
||||||
|
if (format === 'mjs') {
|
||||||
|
ts.forEachChild(source, function analyze(node) {
|
||||||
|
if (
|
||||||
|
ts.isImportDeclaration(node) &&
|
||||||
|
ts.isStringLiteral(node.moduleSpecifier) &&
|
||||||
|
node.moduleSpecifier.text === pluginImport
|
||||||
|
) {
|
||||||
|
const importClause = node.importClause;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(importClause && importClause.name) ||
|
||||||
|
(importClause.namedBindings &&
|
||||||
|
ts.isNamedImports(importClause.namedBindings))
|
||||||
|
) {
|
||||||
|
changes.push({
|
||||||
|
type: ChangeType.Delete,
|
||||||
|
start: node.pos,
|
||||||
|
length: node.end - node.pos,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
ts.forEachChild(source, function analyze(node) {
|
ts.forEachChild(source, function analyze(node) {
|
||||||
if (
|
if (
|
||||||
ts.isVariableStatement(node) &&
|
ts.isVariableStatement(node) &&
|
||||||
@ -463,14 +799,13 @@ export function removePlugin(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
ts.forEachChild(source, function analyze(node) {
|
ts.forEachChild(source, function analyze(node) {
|
||||||
if (
|
if (
|
||||||
ts.isExpressionStatement(node) &&
|
ts.isExportAssignment(node) &&
|
||||||
ts.isBinaryExpression(node.expression) &&
|
ts.isArrayLiteralExpression(node.expression)
|
||||||
node.expression.left.getText() === 'module.exports' &&
|
|
||||||
ts.isArrayLiteralExpression(node.expression.right)
|
|
||||||
) {
|
) {
|
||||||
const blockElements = node.expression.right.elements;
|
const blockElements = node.expression.elements;
|
||||||
blockElements.forEach((element) => {
|
blockElements.forEach((element) => {
|
||||||
if (ts.isObjectLiteralExpression(element)) {
|
if (ts.isObjectLiteralExpression(element)) {
|
||||||
const pluginsElem = element.properties.find(
|
const pluginsElem = element.properties.find(
|
||||||
@ -583,7 +918,15 @@ export function removeCompatExtends(
|
|||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
);
|
);
|
||||||
const changes: StringChange[] = [];
|
const changes: StringChange[] = [];
|
||||||
findAllBlocks(source)?.forEach((node) => {
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
|
const exportsArray =
|
||||||
|
format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
|
||||||
|
|
||||||
|
if (!exportsArray) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportsArray.forEach((node) => {
|
||||||
if (
|
if (
|
||||||
ts.isSpreadElement(node) &&
|
ts.isSpreadElement(node) &&
|
||||||
ts.isCallExpression(node.expression) &&
|
ts.isCallExpression(node.expression) &&
|
||||||
@ -644,9 +987,16 @@ export function removePredefinedConfigs(
|
|||||||
true,
|
true,
|
||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
);
|
);
|
||||||
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
const changes: StringChange[] = [];
|
const changes: StringChange[] = [];
|
||||||
let removeImport = true;
|
let removeImport = true;
|
||||||
findAllBlocks(source)?.forEach((node) => {
|
const exportsArray =
|
||||||
|
format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
|
||||||
|
if (!exportsArray) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportsArray.forEach((node) => {
|
||||||
if (
|
if (
|
||||||
ts.isSpreadElement(node) &&
|
ts.isSpreadElement(node) &&
|
||||||
ts.isElementAccessExpression(node.expression) &&
|
ts.isElementAccessExpression(node.expression) &&
|
||||||
@ -709,14 +1059,23 @@ export function addPluginsToExportsBlock(
|
|||||||
* Adds compat if missing to flat config
|
* Adds compat if missing to flat config
|
||||||
*/
|
*/
|
||||||
export function addFlatCompatToFlatConfig(content: string) {
|
export function addFlatCompatToFlatConfig(content: string) {
|
||||||
let result = content;
|
const result = addImportToFlatConfig(content, 'js', '@eslint/js');
|
||||||
result = addImportToFlatConfig(result, 'js', '@eslint/js');
|
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||||
if (result.includes('const compat = new FlatCompat')) {
|
if (result.includes('const compat = new FlatCompat')) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
result = addImportToFlatConfig(result, ['FlatCompat'], '@eslint/eslintrc');
|
|
||||||
const index = result.indexOf('module.exports');
|
if (format === 'mjs') {
|
||||||
return applyChangesToString(result, [
|
return addFlatCompatToFlatConfigESM(result);
|
||||||
|
} else {
|
||||||
|
return addFlatCompatToFlatConfigCJS(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addFlatCompatToFlatConfigCJS(content: string) {
|
||||||
|
content = addImportToFlatConfig(content, ['FlatCompat'], '@eslint/eslintrc');
|
||||||
|
const index = content.indexOf('module.exports');
|
||||||
|
return applyChangesToString(content, [
|
||||||
{
|
{
|
||||||
type: ChangeType.Insert,
|
type: ChangeType.Insert,
|
||||||
index: index - 1,
|
index: index - 1,
|
||||||
@ -729,6 +1088,32 @@ const compat = new FlatCompat({
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
function addFlatCompatToFlatConfigESM(content: string) {
|
||||||
|
const importsToAdd = [
|
||||||
|
{ variable: 'js', module: '@eslint/js' },
|
||||||
|
{ variable: ['fileURLToPath'], module: 'url' },
|
||||||
|
{ variable: ['dirname'], module: 'path' },
|
||||||
|
{ variable: ['FlatCompat'], module: '@eslint/eslintrc' },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { variable, module } of importsToAdd) {
|
||||||
|
content = addImportToFlatConfig(content, variable, module);
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = content.indexOf('export default');
|
||||||
|
return applyChangesToString(content, [
|
||||||
|
{
|
||||||
|
type: ChangeType.Insert,
|
||||||
|
index: index - 1,
|
||||||
|
text: `
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});\n
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate node list representing the imports and the exports blocks
|
* Generate node list representing the imports and the exports blocks
|
||||||
@ -736,17 +1121,26 @@ const compat = new FlatCompat({
|
|||||||
*/
|
*/
|
||||||
export function createNodeList(
|
export function createNodeList(
|
||||||
importsMap: Map<string, string>,
|
importsMap: Map<string, string>,
|
||||||
exportElements: ts.Expression[]
|
exportElements: ts.Expression[],
|
||||||
|
format: 'mjs' | 'cjs'
|
||||||
): ts.NodeArray<
|
): ts.NodeArray<
|
||||||
ts.VariableStatement | ts.Identifier | ts.ExpressionStatement | ts.SourceFile
|
ts.VariableStatement | ts.Identifier | ts.ExpressionStatement | ts.SourceFile
|
||||||
> {
|
> {
|
||||||
const importsList = [];
|
const importsList = [];
|
||||||
|
|
||||||
// generateRequire(varName, imp, ts.factory);
|
|
||||||
Array.from(importsMap.entries()).forEach(([imp, varName]) => {
|
Array.from(importsMap.entries()).forEach(([imp, varName]) => {
|
||||||
|
if (format === 'mjs') {
|
||||||
|
importsList.push(generateESMImport(varName, imp));
|
||||||
|
} else {
|
||||||
importsList.push(generateRequire(varName, imp));
|
importsList.push(generateRequire(varName, imp));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const exports =
|
||||||
|
format === 'mjs'
|
||||||
|
? generateESMExport(exportElements)
|
||||||
|
: generateCJSExport(exportElements);
|
||||||
|
|
||||||
return ts.factory.createNodeArray([
|
return ts.factory.createNodeArray([
|
||||||
// add plugin imports
|
// add plugin imports
|
||||||
...importsList,
|
...importsList,
|
||||||
@ -757,19 +1151,31 @@ export function createNodeList(
|
|||||||
false,
|
false,
|
||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
),
|
),
|
||||||
// creates:
|
exports,
|
||||||
// module.exports = [ ... ];
|
]);
|
||||||
ts.factory.createExpressionStatement(
|
}
|
||||||
|
|
||||||
|
function generateESMExport(elements: ts.Expression[]): ts.ExportAssignment {
|
||||||
|
// creates: export default = [...]
|
||||||
|
return ts.factory.createExportAssignment(
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
ts.factory.createArrayLiteralExpression(elements, true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCJSExport(elements: ts.Expression[]): ts.ExpressionStatement {
|
||||||
|
// creates: module.exports = [...]
|
||||||
|
return ts.factory.createExpressionStatement(
|
||||||
ts.factory.createBinaryExpression(
|
ts.factory.createBinaryExpression(
|
||||||
ts.factory.createPropertyAccessExpression(
|
ts.factory.createPropertyAccessExpression(
|
||||||
ts.factory.createIdentifier('module'),
|
ts.factory.createIdentifier('module'),
|
||||||
ts.factory.createIdentifier('exports')
|
ts.factory.createIdentifier('exports')
|
||||||
),
|
),
|
||||||
ts.factory.createToken(ts.SyntaxKind.EqualsToken),
|
ts.factory.createToken(ts.SyntaxKind.EqualsToken),
|
||||||
ts.factory.createArrayLiteralExpression(exportElements, true)
|
ts.factory.createArrayLiteralExpression(elements, true)
|
||||||
)
|
)
|
||||||
),
|
);
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateSpreadElement(name: string): ts.SpreadElement {
|
export function generateSpreadElement(name: string): ts.SpreadElement {
|
||||||
@ -831,17 +1237,20 @@ export function stringifyNodeList(
|
|||||||
true,
|
true,
|
||||||
ts.ScriptKind.JS
|
ts.ScriptKind.JS
|
||||||
);
|
);
|
||||||
return (
|
const result = printer
|
||||||
printer
|
|
||||||
.printList(ts.ListFormat.MultiLine, nodes, resultFile)
|
.printList(ts.ListFormat.MultiLine, nodes, resultFile)
|
||||||
// add new line before compat initialization
|
// add new line before compat initialization
|
||||||
.replace(
|
.replace(
|
||||||
/const compat = new FlatCompat/,
|
/const compat = new FlatCompat/,
|
||||||
'\nconst compat = new FlatCompat'
|
'\nconst compat = new FlatCompat'
|
||||||
)
|
|
||||||
// add new line before module.exports = ...
|
|
||||||
.replace(/module\.exports/, '\nmodule.exports')
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (result.includes('export default')) {
|
||||||
|
return result // add new line before export default = ...
|
||||||
|
.replace(/export default/, '\nexport default');
|
||||||
|
} else {
|
||||||
|
return result.replace(/module.exports/, '\nmodule.exports');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -871,6 +1280,50 @@ export function generateRequire(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Top level imports
|
||||||
|
export function generateESMImport(
|
||||||
|
variableName: string | ts.ObjectBindingPattern,
|
||||||
|
imp: string
|
||||||
|
): ts.ImportDeclaration {
|
||||||
|
let importClause;
|
||||||
|
|
||||||
|
if (typeof variableName === 'string') {
|
||||||
|
// For single variable import e.g import foo from 'module';
|
||||||
|
importClause = ts.factory.createImportClause(
|
||||||
|
false,
|
||||||
|
ts.factory.createIdentifier(variableName),
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// For object binding pattern import e.g import { a, b, c } from 'module';
|
||||||
|
importClause = ts.factory.createImportClause(
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
ts.factory.createNamedImports(
|
||||||
|
variableName.elements.map((element) => {
|
||||||
|
const propertyName = element.propertyName
|
||||||
|
? ts.isIdentifier(element.propertyName)
|
||||||
|
? element.propertyName
|
||||||
|
: ts.factory.createIdentifier(element.propertyName.getText())
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return ts.factory.createImportSpecifier(
|
||||||
|
false,
|
||||||
|
propertyName,
|
||||||
|
element.name as ts.Identifier
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.factory.createImportDeclaration(
|
||||||
|
undefined,
|
||||||
|
importClause,
|
||||||
|
ts.factory.createStringLiteral(imp)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FROM: https://github.com/eslint/rewrite/blob/e2a7ec809db20e638abbad250d105ddbde88a8d5/packages/migrate-config/src/migrate-config.js#L222
|
* FROM: https://github.com/eslint/rewrite/blob/e2a7ec809db20e638abbad250d105ddbde88a8d5/packages/migrate-config/src/migrate-config.js#L222
|
||||||
*
|
*
|
||||||
@ -904,7 +1357,8 @@ export function overrideNeedsCompat(
|
|||||||
export function generateFlatOverride(
|
export function generateFlatOverride(
|
||||||
_override: Partial<Linter.ConfigOverride<Linter.RulesRecord>> & {
|
_override: Partial<Linter.ConfigOverride<Linter.RulesRecord>> & {
|
||||||
ignores?: Linter.FlatConfig['ignores'];
|
ignores?: Linter.FlatConfig['ignores'];
|
||||||
}
|
},
|
||||||
|
format: 'mjs' | 'cjs'
|
||||||
): ts.ObjectLiteralExpression | ts.SpreadElement {
|
): ts.ObjectLiteralExpression | ts.SpreadElement {
|
||||||
const override = mapFilePaths(_override);
|
const override = mapFilePaths(_override);
|
||||||
|
|
||||||
@ -981,21 +1435,10 @@ export function generateFlatOverride(
|
|||||||
}
|
}
|
||||||
return propertyAssignment;
|
return propertyAssignment;
|
||||||
} else {
|
} else {
|
||||||
// Change parser to require statement.
|
// Change parser to import statement.
|
||||||
return ts.factory.createPropertyAssignment(
|
return format === 'mjs'
|
||||||
'parser',
|
? generateESMParserImport(override)
|
||||||
ts.factory.createCallExpression(
|
: generateCJSParserImport(override);
|
||||||
ts.factory.createIdentifier('require'),
|
|
||||||
undefined,
|
|
||||||
[
|
|
||||||
ts.factory.createStringLiteral(
|
|
||||||
override['languageOptions']?.['parserOptions']?.parser ??
|
|
||||||
override['languageOptions']?.parser ??
|
|
||||||
override.parser
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -1103,6 +1546,50 @@ export function generateFlatOverride(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateESMParserImport(
|
||||||
|
override: Partial<Linter.ConfigOverride<Linter.RulesRecord>> & {
|
||||||
|
ignores?: Linter.FlatConfig['ignores'];
|
||||||
|
}
|
||||||
|
): ts.PropertyAssignment {
|
||||||
|
return ts.factory.createPropertyAssignment(
|
||||||
|
'parser',
|
||||||
|
ts.factory.createAwaitExpression(
|
||||||
|
ts.factory.createCallExpression(
|
||||||
|
ts.factory.createIdentifier('import'),
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
ts.factory.createStringLiteral(
|
||||||
|
override['languageOptions']?.['parserOptions']?.parser ??
|
||||||
|
override['languageOptions']?.parser ??
|
||||||
|
override.parser
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCJSParserImport(
|
||||||
|
override: Partial<Linter.ConfigOverride<Linter.RulesRecord>> & {
|
||||||
|
ignores?: Linter.FlatConfig['ignores'];
|
||||||
|
}
|
||||||
|
): ts.PropertyAssignment {
|
||||||
|
return ts.factory.createPropertyAssignment(
|
||||||
|
'parser',
|
||||||
|
ts.factory.createCallExpression(
|
||||||
|
ts.factory.createIdentifier('require'),
|
||||||
|
undefined,
|
||||||
|
[
|
||||||
|
ts.factory.createStringLiteral(
|
||||||
|
override['languageOptions']?.['parserOptions']?.parser ??
|
||||||
|
override['languageOptions']?.parser ??
|
||||||
|
override.parser
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function generateFlatPredefinedConfig(
|
export function generateFlatPredefinedConfig(
|
||||||
predefinedConfigName: string,
|
predefinedConfigName: string,
|
||||||
moduleName = 'nx',
|
moduleName = 'nx',
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import { globWithWorkspaceContext } from 'nx/src/utils/workspace-context';
|
|||||||
import { gte } from 'semver';
|
import { gte } from 'semver';
|
||||||
import {
|
import {
|
||||||
baseEsLintConfigFile,
|
baseEsLintConfigFile,
|
||||||
baseEsLintFlatConfigFile,
|
BASE_ESLINT_CONFIG_FILENAMES,
|
||||||
ESLINT_CONFIG_FILENAMES,
|
ESLINT_CONFIG_FILENAMES,
|
||||||
isFlatConfig,
|
isFlatConfig,
|
||||||
} from '../utils/config-file';
|
} from '../utils/config-file';
|
||||||
@ -405,7 +405,7 @@ function getProjectUsingESLintConfig(
|
|||||||
): CreateNodesResult['projects'][string] | null {
|
): CreateNodesResult['projects'][string] | null {
|
||||||
const rootEslintConfig = [
|
const rootEslintConfig = [
|
||||||
baseEsLintConfigFile,
|
baseEsLintConfigFile,
|
||||||
baseEsLintFlatConfigFile,
|
...BASE_ESLINT_CONFIG_FILENAMES,
|
||||||
...ESLINT_CONFIG_FILENAMES,
|
...ESLINT_CONFIG_FILENAMES,
|
||||||
].find((f) => existsSync(join(context.workspaceRoot, f)));
|
].find((f) => existsSync(join(context.workspaceRoot, f)));
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import { existsSync, statSync } from 'fs';
|
import { existsSync, statSync } from 'fs';
|
||||||
import { basename, dirname, join, resolve } from 'path';
|
import { basename, dirname, join, resolve } from 'path';
|
||||||
import { eslintFlatConfigFilenames } from './flat-config';
|
import {
|
||||||
|
baseEslintConfigFilenames,
|
||||||
|
eslintFlatConfigFilenames,
|
||||||
|
} from './flat-config';
|
||||||
|
|
||||||
export const ESLINT_FLAT_CONFIG_FILENAMES = eslintFlatConfigFilenames;
|
export const ESLINT_FLAT_CONFIG_FILENAMES = eslintFlatConfigFilenames;
|
||||||
|
|
||||||
@ -18,8 +21,10 @@ export const ESLINT_CONFIG_FILENAMES = [
|
|||||||
...ESLINT_FLAT_CONFIG_FILENAMES,
|
...ESLINT_FLAT_CONFIG_FILENAMES,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const BASE_ESLINT_CONFIG_FILENAMES = baseEslintConfigFilenames;
|
||||||
|
|
||||||
export const baseEsLintConfigFile = '.eslintrc.base.json';
|
export const baseEsLintConfigFile = '.eslintrc.base.json';
|
||||||
export const baseEsLintFlatConfigFile = 'eslint.base.config.cjs';
|
export const baseEsLintFlatConfigFile = 'eslint.base.config.mjs';
|
||||||
// Make sure we can handle previous file extension as well for migrations or custom generators.
|
// Make sure we can handle previous file extension as well for migrations or custom generators.
|
||||||
export const legacyBaseEsLintFlatConfigFile = 'eslint.base.config.js';
|
export const legacyBaseEsLintFlatConfigFile = 'eslint.base.config.js';
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,17 @@
|
|||||||
import { Tree } from '@nx/devkit';
|
import { Tree } from '@nx/devkit';
|
||||||
import { gte } from 'semver';
|
import { gte } from 'semver';
|
||||||
|
|
||||||
// todo: add support for eslint.config.mjs,
|
|
||||||
export const eslintFlatConfigFilenames = [
|
export const eslintFlatConfigFilenames = [
|
||||||
'eslint.config.cjs',
|
'eslint.config.cjs',
|
||||||
'eslint.config.js',
|
'eslint.config.js',
|
||||||
|
'eslint.config.mjs',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const baseEslintConfigFilenames = [
|
||||||
|
'eslint.base.js',
|
||||||
|
'eslint.base.config.cjs',
|
||||||
|
'eslint.base.config.js',
|
||||||
|
'eslint.base.config.mjs',
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getRootESLintFlatConfigFilename(tree: Tree): string {
|
export function getRootESLintFlatConfigFilename(tree: Tree): string {
|
||||||
|
|||||||
@ -623,7 +623,41 @@ describe('app', () => {
|
|||||||
|
|
||||||
describe('--linter', () => {
|
describe('--linter', () => {
|
||||||
describe('default (eslint)', () => {
|
describe('default (eslint)', () => {
|
||||||
it('should add flat config as needed', async () => {
|
it('should add flat config as needed MJS', async () => {
|
||||||
|
tree.write('eslint.config.mjs', 'export default {};');
|
||||||
|
const name = uniq();
|
||||||
|
|
||||||
|
await applicationGenerator(tree, {
|
||||||
|
directory: name,
|
||||||
|
style: 'css',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read(`${name}/eslint.config.mjs`, 'utf-8'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nx from '@nx/eslint-plugin';
|
||||||
|
import baseConfig from '../eslint.config.mjs';
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...compat.extends('next', 'next/core-web-vitals'),
|
||||||
|
...baseConfig,
|
||||||
|
...nx.configs['flat/react-typescript'],
|
||||||
|
{
|
||||||
|
ignores: ['.next/**/*'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add flat config as needed CJS', async () => {
|
||||||
tree.write('eslint.config.cjs', '');
|
tree.write('eslint.config.cjs', '');
|
||||||
const name = uniq();
|
const name = uniq();
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,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`] = `
|
exports[`app generated files content - as-provided - my-app general application should configure eslint correctly (flat config CJS) 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const baseConfig = require('../eslint.config.cjs');
|
const baseConfig = require('../eslint.config.cjs');
|
||||||
@ -66,6 +66,40 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`app generated files content - as-provided - my-app general application should configure eslint correctly (flat config ESM) 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import baseConfig from '../eslint.config.mjs';
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
...compat.extends('@nuxt/eslint-config'),
|
||||||
|
{
|
||||||
|
files: ['**/*.vue'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
parser: await import('@typescript-eslint/parser'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['.nuxt/**', '.output/**', 'node_modules'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`app generated files content - as-provided - my-app general application should configure nuxt correctly 1`] = `
|
exports[`app generated files content - as-provided - my-app general application should configure nuxt correctly 1`] = `
|
||||||
"import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
"import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||||
import { defineNuxtConfig } from 'nuxt/config';
|
import { defineNuxtConfig } from 'nuxt/config';
|
||||||
@ -393,7 +427,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`] = `
|
exports[`app generated files content - as-provided - myApp general application should configure eslint correctly (flat config CJS) 1`] = `
|
||||||
"const { FlatCompat } = require('@eslint/eslintrc');
|
"const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
const js = require('@eslint/js');
|
const js = require('@eslint/js');
|
||||||
const baseConfig = require('../eslint.config.cjs');
|
const baseConfig = require('../eslint.config.cjs');
|
||||||
@ -426,6 +460,40 @@ module.exports = [
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`app generated files content - as-provided - myApp general application should configure eslint correctly (flat config ESM) 1`] = `
|
||||||
|
"import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import baseConfig from '../eslint.config.mjs';
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
...compat.extends('@nuxt/eslint-config'),
|
||||||
|
{
|
||||||
|
files: ['**/*.vue'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
parser: await import('@typescript-eslint/parser'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['.nuxt/**', '.output/**', 'node_modules'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`app generated files content - as-provided - myApp general application should configure nuxt correctly 1`] = `
|
exports[`app generated files content - as-provided - myApp general application should configure nuxt correctly 1`] = `
|
||||||
"import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
"import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||||
import { defineNuxtConfig } from 'nuxt/config';
|
import { defineNuxtConfig } from 'nuxt/config';
|
||||||
|
|||||||
@ -65,8 +65,21 @@ describe('app', () => {
|
|||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should configure eslint correctly (flat config)', async () => {
|
it('should configure eslint correctly (flat config ESM)', async () => {
|
||||||
tree.write('eslint.config.cjs', '');
|
tree.write('eslint.config.mjs', 'export default {};');
|
||||||
|
|
||||||
|
await applicationGenerator(tree, {
|
||||||
|
directory: name,
|
||||||
|
unitTestRunner: 'vitest',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tree.read(`${name}/eslint.config.mjs`, 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should configure eslint correctly (flat config CJS)', async () => {
|
||||||
|
tree.write('eslint.config.cjs', 'module.exports = {};');
|
||||||
|
|
||||||
await applicationGenerator(tree, {
|
await applicationGenerator(tree, {
|
||||||
directory: name,
|
directory: name,
|
||||||
|
|||||||
@ -277,7 +277,7 @@ exports[`library should ignore test files in tsconfig.lib.json 1`] = `
|
|||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`library should support eslint flat config 1`] = `
|
exports[`library should support eslint flat config CJS 1`] = `
|
||||||
"const vue = require('eslint-plugin-vue');
|
"const vue = require('eslint-plugin-vue');
|
||||||
const baseConfig = require('../eslint.config.cjs');
|
const baseConfig = require('../eslint.config.cjs');
|
||||||
|
|
||||||
@ -301,3 +301,28 @@ module.exports = [
|
|||||||
];
|
];
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`library should support eslint flat config ESM 1`] = `
|
||||||
|
"import vue from 'eslint-plugin-vue';
|
||||||
|
import baseConfig from '../eslint.config.mjs';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...baseConfig,
|
||||||
|
...vue.configs['flat/recommended'],
|
||||||
|
{
|
||||||
|
files: ['**/*.vue'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
parser: await import('@typescript-eslint/parser'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue'],
|
||||||
|
rules: {
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|||||||
@ -149,7 +149,7 @@ describe('library', () => {
|
|||||||
expect(eslintJson).toMatchSnapshot();
|
expect(eslintJson).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support eslint flat config', async () => {
|
it('should support eslint flat config CJS', async () => {
|
||||||
tree.write(
|
tree.write(
|
||||||
'eslint.config.cjs',
|
'eslint.config.cjs',
|
||||||
`const { FlatCompat } = require('@eslint/eslintrc');
|
`const { FlatCompat } = require('@eslint/eslintrc');
|
||||||
@ -217,6 +217,76 @@ module.exports = [
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support eslint flat config ESM', async () => {
|
||||||
|
tree.write(
|
||||||
|
'eslint.config.mjs',
|
||||||
|
`import { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import nx from '@nx/eslint-plugin';
|
||||||
|
import baseConfig from '../eslint.config.mjs';
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname(fileURLToPath(import.meta.url)),
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{ plugins: { '@nx': nxEslintPlugin } },
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
rules: {
|
||||||
|
'@nx/enforce-module-boundaries': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
enforceBuildableLibDependency: true,
|
||||||
|
allow: [],
|
||||||
|
depConstraints: [
|
||||||
|
{
|
||||||
|
sourceTag: '*',
|
||||||
|
onlyDependOnLibsWithTags: ['*'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...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,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
...compat.config({ env: { jest: true } }).map((config) => ({
|
||||||
|
...config,
|
||||||
|
files: ['**/*.spec.ts', '**/*.spec.tsx', '**/*.spec.js', '**/*.spec.jsx'],
|
||||||
|
rules: {
|
||||||
|
...config.rules,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
]`
|
||||||
|
);
|
||||||
|
|
||||||
|
await libraryGenerator(tree, defaultSchema);
|
||||||
|
|
||||||
|
const eslintJson = tree.read('my-lib/eslint.config.mjs', 'utf-8');
|
||||||
|
expect(eslintJson).toMatchSnapshot();
|
||||||
|
// assert **/*.vue was added to override in base eslint config
|
||||||
|
const eslintBaseJson = tree.read('eslint.config.mjs', 'utf-8');
|
||||||
|
expect(eslintBaseJson).toContain(
|
||||||
|
`files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue'],`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
describe('nested', () => {
|
describe('nested', () => {
|
||||||
it('should update tags and implicitDependencies', async () => {
|
it('should update tags and implicitDependencies', async () => {
|
||||||
await libraryGenerator(tree, {
|
await libraryGenerator(tree, {
|
||||||
|
|||||||
@ -16,9 +16,11 @@ export function updateEslintConfig(
|
|||||||
!tree.exists('.eslintrc.json') &&
|
!tree.exists('.eslintrc.json') &&
|
||||||
!tree.exists('eslint.config.js') &&
|
!tree.exists('eslint.config.js') &&
|
||||||
!tree.exists('eslint.config.cjs') &&
|
!tree.exists('eslint.config.cjs') &&
|
||||||
|
!tree.exists('eslint.config.mjs') &&
|
||||||
!tree.exists('.eslintrc.base.json') &&
|
!tree.exists('.eslintrc.base.json') &&
|
||||||
!tree.exists('eslint.base.config.js') &&
|
!tree.exists('eslint.base.config.js') &&
|
||||||
!tree.exists('eslint.base.config.cjs')
|
!tree.exists('eslint.base.config.cjs') &&
|
||||||
|
!tree.exists('eslint.base.config.mjs')
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user