Nicholas Cunningham dec21662b6
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`
2025-01-17 13:39:45 -05:00

206 lines
5.4 KiB
TypeScript

import 'nx/src/internal-testing-utils/mock-project-graph';
import {
NxJsonConfiguration,
readJson,
Tree,
updateJson,
writeJson,
} from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { LinterInitOptions, lintInitGenerator } from './init';
import { setWorkspaceRoot } from 'nx/src/utils/workspace-root';
describe('@nx/eslint:init', () => {
let tree: Tree;
let options: LinterInitOptions;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
setWorkspaceRoot(tree.root);
options = {
addPlugin: true,
};
});
it('should not generate the global eslint config if it already exist', async () => {
tree.write('.eslintrc.js', '{}');
await lintInitGenerator(tree, options);
expect(tree.exists('.eslintrc.json')).toBe(false);
});
it('should setup @nx/eslint/plugin', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs ??= {};
json.namedInputs.production = ['default'];
return json;
});
await lintInitGenerator(tree, options);
expect(
readJson<NxJsonConfiguration>(tree, 'nx.json').targetDefaults[
'@nx/eslint:lint'
]
).toBeUndefined();
expect(readJson<NxJsonConfiguration>(tree, 'nx.json').plugins)
.toMatchInlineSnapshot(`
[
{
"options": {
"targetName": "lint",
},
"plugin": "@nx/eslint/plugin",
},
]
`);
});
it('should add @nx/eslint/plugin', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs ??= {};
json.namedInputs.production = ['default'];
return json;
});
await lintInitGenerator(tree, options);
expect(readJson<NxJsonConfiguration>(tree, 'nx.json').plugins)
.toMatchInlineSnapshot(`
[
{
"options": {
"targetName": "lint",
},
"plugin": "@nx/eslint/plugin",
},
]
`);
});
it('should add the eslint extension to the recommended property', async () => {
writeJson(tree, '.vscode/extensions.json', {
recommendations: [
'nrwl.angular-console',
'angular.ng-template',
'esbenp.prettier-vscode',
],
});
await lintInitGenerator(tree, options);
expect(readJson(tree, '.vscode/extensions.json')).toMatchInlineSnapshot(`
{
"recommendations": [
"nrwl.angular-console",
"angular.ng-template",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
],
}
`);
});
describe('(legacy)', () => {
describe('CJS', () => {
it('should add the root eslint config to the lint targetDefaults for lint', async () => {
await lintInitGenerator(tree, {
...options,
addPlugin: false,
eslintConfigFormat: 'cjs',
});
expect(
readJson(tree, 'nx.json').targetDefaults['@nx/eslint:lint']
).toEqual({
cache: true,
inputs: [
'default',
'{workspaceRoot}/.eslintrc.json',
'{workspaceRoot}/.eslintignore',
'{workspaceRoot}/eslint.config.cjs',
],
});
});
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: 'cjs',
});
expect(
readJson<NxJsonConfiguration>(tree, 'nx.json').targetDefaults[
'@nx/eslint:lint'
]
).toEqual({
cache: true,
inputs: [
'default',
'{workspaceRoot}/.eslintrc.json',
'{workspaceRoot}/.eslintignore',
'{workspaceRoot}/eslint.config.cjs',
],
});
});
});
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',
],
});
});
});
});
});