Project Init

This commit is contained in:
Muluhabt
2026-05-29 15:23:46 +03:00
commit 2fbc557aac
67387 changed files with 6063341 additions and 0 deletions

22
node_modules/@nx/eslint/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2017-2026 Narwhal Technologies Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

66
node_modules/@nx/eslint/README.md generated vendored Normal file
View File

@@ -0,0 +1,66 @@
<p style="text-align: center;">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-dark.svg">
<img alt="Nx - Smart Monorepos · Fast Builds" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
</picture>
</p>
<div style="text-align: center;">
[![CircleCI](https://circleci.com/gh/nrwl/nx.svg?style=svg)](https://circleci.com/gh/nrwl/nx)
[![License](https://img.shields.io/npm/l/@nx/workspace.svg?style=flat-square)]()
[![NPM Version](https://badge.fury.io/js/nx.svg)](https://www.npmjs.com/package/nx)
[![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)]()
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![Join the chat at https://gitter.im/nrwl-nx/community](https://badges.gitter.im/nrwl-nx/community.svg)](https://gitter.im/nrwl-nx/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Join us on the Official Nx Discord Server](https://img.shields.io/discord/1143497901675401286?label=discord)](https://go.nx.dev/community)
</div>
<hr>
# Nx: Smart Monorepos · Fast Builds
Get to green PRs in half the time. Nx optimizes your builds, scales your CI, and fixes failed PRs. Built for developers and AI agents.
## Getting Started
### Creating an Nx Workspace
**Using `npx`**
```bash
npx create-nx-workspace
```
**Using `npm init`**
```bash
npm init nx-workspace
```
**Using `yarn create`**
```bash
yarn create nx-workspace
```
### Adding Nx to an Existing Repository
Run:
```bash
npx nx@latest init
```
## Documentation & Resources
- [Nx.Dev: Documentation, Guides, Tutorials](https://nx.dev)
- [Intro to Nx](https://nx.dev/getting-started/intro)
- [Official Nx YouTube Channel](https://www.youtube.com/@NxDevtools)
- [Blog Posts About Nx](https://nx.dev/blog)
<p style="text-align: center;"><a href="https://nx.dev/#learning-materials" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-courses-and-videos.svg"
width="100%" alt="Nx - Smart Monorepos · Fast Builds"></a></p>

9
node_modules/@nx/eslint/executors.json generated vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"executors": {
"lint": {
"implementation": "./src/executors/lint/lint.impl",
"schema": "./src/executors/lint/schema.json",
"description": "Run ESLint on a project."
}
}
}

33
node_modules/@nx/eslint/generators.json generated vendored Normal file
View File

@@ -0,0 +1,33 @@
{
"name": "nx/eslint",
"version": "0.1",
"generators": {
"init": {
"factory": "./src/generators/init/init#initEsLint",
"schema": "./src/generators/init/schema.json",
"description": "Set up the ESLint plugin.",
"hidden": true
},
"workspace-rules-project": {
"factory": "./src/generators/workspace-rules-project/workspace-rules-project#lintWorkspaceRulesProjectGenerator",
"schema": "./src/generators/workspace-rules-project/schema.json",
"description": "Create the Workspace Lint Rules Project.",
"hidden": true
},
"workspace-rule": {
"factory": "./src/generators/workspace-rule/workspace-rule#lintWorkspaceRuleGenerator",
"schema": "./src/generators/workspace-rule/schema.json",
"description": "Create a new Workspace ESLint rule."
},
"convert-to-flat-config": {
"factory": "./src/generators/convert-to-flat-config/generator",
"schema": "./src/generators/convert-to-flat-config/schema.json",
"description": "Convert an Nx workspace's ESLint configs to use Flat Config."
},
"convert-to-inferred": {
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
"schema": "./src/generators/convert-to-inferred/schema.json",
"description": "Convert existing ESLint project(s) using `@nx/eslint:lint` executor to use `@nx/eslint/plugin`."
}
}
}

4
node_modules/@nx/eslint/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export { lintProjectGenerator } from './src/generators/lint-project/lint-project';
export { lintInitGenerator } from './src/generators/init/init';
export { Linter, LinterType } from './src/generators/utils/linter';
//# sourceMappingURL=index.d.ts.map

1
node_modules/@nx/eslint/index.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../packages/eslint/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC"}

9
node_modules/@nx/eslint/index.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Linter = exports.lintInitGenerator = exports.lintProjectGenerator = void 0;
var lint_project_1 = require("./src/generators/lint-project/lint-project");
Object.defineProperty(exports, "lintProjectGenerator", { enumerable: true, get: function () { return lint_project_1.lintProjectGenerator; } });
var init_1 = require("./src/generators/init/init");
Object.defineProperty(exports, "lintInitGenerator", { enumerable: true, get: function () { return init_1.lintInitGenerator; } });
var linter_1 = require("./src/generators/utils/linter");
Object.defineProperty(exports, "Linter", { enumerable: true, get: function () { return linter_1.Linter; } });

221
node_modules/@nx/eslint/migrations.json generated vendored Normal file
View File

@@ -0,0 +1,221 @@
{
"generators": {
"update-typescript-eslint-v8.13.0": {
"version": "20.2.0-beta.5",
"description": "Update TypeScript ESLint packages to v8.13.0 if they are already on v8",
"implementation": "./src/migrations/update-20-2-0/update-typescript-eslint-v8-13-0"
},
"add-file-extensions-to-overrides": {
"version": "20.3.0-beta.1",
"description": "Update ESLint flat config to include .cjs, .mjs, .cts, and .mts files in overrides (if needed)",
"implementation": "./src/migrations/update-20-3-0/add-file-extensions-to-overrides"
},
"update-executor-lint-inputs": {
"version": "22.7.0-beta.12",
"description": "Add missing inputs to @nx/eslint:lint executor target defaults",
"implementation": "./src/migrations/update-21-6-0/update-executor-lint-inputs"
}
},
"packageJsonUpdates": {
"20.4.0-typescript-eslint": {
"version": "20.4.0-beta.1",
"requires": {
"typescript-eslint": ">8.0.0 <8.19.0"
},
"packages": {
"typescript-eslint": {
"version": "^8.19.0"
},
"@typescript-eslint/eslint-plugin": {
"version": "^8.19.0"
},
"@typescript-eslint/parser": {
"version": "^8.19.0"
},
"@typescript-eslint/utils": {
"version": "^8.19.0"
},
"@typescript-eslint/rule-tester": {
"version": "^8.19.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/scope-manager": {
"version": "^8.19.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/typescript-estree": {
"version": "^8.19.0",
"alwaysAddToPackageJson": false
}
}
},
"20.4.0-@typescript-eslint": {
"version": "20.4.0-beta.1",
"requires": {
"@typescript-eslint/eslint-plugin": ">8.0.0 <8.19.0"
},
"packages": {
"typescript-eslint": {
"version": "^8.19.0"
},
"@typescript-eslint/eslint-plugin": {
"version": "^8.19.0"
},
"@typescript-eslint/parser": {
"version": "^8.19.0"
},
"@typescript-eslint/utils": {
"version": "^8.19.0"
},
"@typescript-eslint/rule-tester": {
"version": "^8.19.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/scope-manager": {
"version": "^8.19.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/typescript-estree": {
"version": "^8.19.0",
"alwaysAddToPackageJson": false
}
}
},
"20.7.0": {
"version": "20.7.0-beta.4",
"packages": {
"eslint-config-prettier": {
"version": "^10.0.0"
}
}
},
"21.2.0-typescript-eslint": {
"version": "21.2.0-beta.0",
"requires": {
"typescript-eslint": ">8.0.0 <8.29.0"
},
"packages": {
"typescript-eslint": {
"version": "^8.29.0"
},
"@typescript-eslint/eslint-plugin": {
"version": "^8.29.0"
},
"@typescript-eslint/parser": {
"version": "^8.29.0"
},
"@typescript-eslint/utils": {
"version": "^8.29.0"
},
"@typescript-eslint/rule-tester": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/scope-manager": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/typescript-estree": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
}
}
},
"21.2.0-@typescript-eslint": {
"version": "21.2.0-beta.0",
"requires": {
"@typescript-eslint/eslint-plugin": ">8.0.0 <8.29.0"
},
"packages": {
"typescript-eslint": {
"version": "^8.29.0"
},
"@typescript-eslint/eslint-plugin": {
"version": "^8.29.0"
},
"@typescript-eslint/parser": {
"version": "^8.29.0"
},
"@typescript-eslint/utils": {
"version": "^8.29.0"
},
"@typescript-eslint/rule-tester": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/scope-manager": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/typescript-estree": {
"version": "^8.29.0",
"alwaysAddToPackageJson": false
}
}
},
"21.5.0-typescript-eslint": {
"version": "21.5.0-beta.2",
"requires": {
"typescript-eslint": ">8.0.0 <8.40.0"
},
"packages": {
"typescript-eslint": {
"version": "^8.40.0"
},
"@typescript-eslint/eslint-plugin": {
"version": "^8.40.0"
},
"@typescript-eslint/parser": {
"version": "^8.40.0"
},
"@typescript-eslint/utils": {
"version": "^8.40.0"
},
"@typescript-eslint/rule-tester": {
"version": "^8.40.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/scope-manager": {
"version": "^8.40.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/typescript-estree": {
"version": "^8.40.0",
"alwaysAddToPackageJson": false
}
}
},
"21.5.0-@typescript-eslint": {
"version": "21.5.0-beta.2",
"requires": {
"@typescript-eslint/eslint-plugin": ">8.0.0 <8.40.0"
},
"packages": {
"typescript-eslint": {
"version": "^8.40.0"
},
"@typescript-eslint/eslint-plugin": {
"version": "^8.40.0"
},
"@typescript-eslint/parser": {
"version": "^8.40.0"
},
"@typescript-eslint/utils": {
"version": "^8.40.0"
},
"@typescript-eslint/rule-tester": {
"version": "^8.40.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/scope-manager": {
"version": "^8.40.0",
"alwaysAddToPackageJson": false
},
"@typescript-eslint/typescript-estree": {
"version": "^8.40.0",
"alwaysAddToPackageJson": false
}
}
}
}
}

60
node_modules/@nx/eslint/package.json generated vendored Normal file
View File

@@ -0,0 +1,60 @@
{
"name": "@nx/eslint",
"version": "22.7.5",
"private": false,
"description": "The ESLint plugin for Nx contains executors, generators and utilities used for linting JavaScript/TypeScript projects within an Nx workspace.",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
"directory": "packages/eslint"
},
"keywords": [
"Monorepo",
"Web",
"Lint",
"ESLint",
"CLI",
"Testing"
],
"main": "./index.js",
"type": "commonjs",
"types": "./index.d.ts",
"author": "Victor Savkin",
"license": "MIT",
"bugs": {
"url": "https://github.com/nrwl/nx/issues"
},
"homepage": "https://nx.dev",
"ng-update": {
"requirements": {},
"migrations": "./migrations.json"
},
"generators": "./generators.json",
"executors": "./executors.json",
"peerDependencies": {
"@nx/jest": "22.7.5",
"@zkochan/js-yaml": "0.0.7",
"eslint": "^8.0.0 || ^9.0.0 || ^10.0.0"
},
"dependencies": {
"@nx/devkit": "22.7.5",
"@nx/js": "22.7.5",
"semver": "^7.6.3",
"tslib": "^2.3.0",
"typescript": "~5.9.2"
},
"devDependencies": {
"nx": "22.7.5"
},
"peerDependenciesMeta": {
"@nx/jest": {
"optional": true
},
"@zkochan/js-yaml": {
"optional": true
}
},
"publishConfig": {
"access": "public"
}
}

2
node_modules/@nx/eslint/plugin.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export { createNodes, createNodesV2, EslintPluginOptions, } from './src/plugins/plugin';
//# sourceMappingURL=plugin.d.ts.map

1
node_modules/@nx/eslint/plugin.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../packages/eslint/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,aAAa,EACb,mBAAmB,GACpB,MAAM,sBAAsB,CAAC"}

6
node_modules/@nx/eslint/plugin.js generated vendored Normal file
View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNodesV2 = exports.createNodes = void 0;
var plugin_1 = require("./src/plugins/plugin");
Object.defineProperty(exports, "createNodes", { enumerable: true, get: function () { return plugin_1.createNodes; } });
Object.defineProperty(exports, "createNodesV2", { enumerable: true, get: function () { return plugin_1.createNodesV2; } });

View File

@@ -0,0 +1,6 @@
import { type ExecutorContext } from '@nx/devkit';
import type { Schema } from './schema';
export default function run(options: Schema, context: ExecutorContext): Promise<{
success: boolean;
}>;
//# sourceMappingURL=lint.impl.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"lint.impl.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/executors/lint/lint.impl.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAM7E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,wBAA8B,GAAG,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAwN/B"}

193
node_modules/@nx/eslint/src/executors/lint/lint.impl.js generated vendored Normal file
View File

@@ -0,0 +1,193 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = run;
const devkit_1 = require("@nx/devkit");
const fs_1 = require("fs");
const utils_1 = require("nx/src/tasks-runner/utils");
const path_1 = require("path");
const config_file_1 = require("../../utils/config-file");
const eslint_utils_1 = require("./utility/eslint-utils");
async function run(options, context) {
// hasTypeAwareRules is deprecated and no longer used, delete it so it's not passed to ESLint
delete options.hasTypeAwareRules;
const systemRoot = context.root;
// eslint resolves files relative to the current working directory.
// We want these paths to always be resolved relative to the workspace
// root to be able to run the lint executor from any subfolder.
process.chdir(systemRoot);
const projectName = context.projectName || '<???>';
const projectRoot = context.projectsConfigurations.projects[context.projectName].root;
const printInfo = options.format && !options.silent;
if (printInfo) {
console.info(`\nLinting ${JSON.stringify(projectName)}...`);
}
options.cacheLocation = options.cacheLocation
? (0, devkit_1.joinPathFragments)(options.cacheLocation, projectName)
: undefined;
const { printConfig, errorOnUnmatchedPattern, ...normalizedOptions } = options;
// locate the flat config file if it exists starting from the project root
const flatConfigFilePath = (0, config_file_1.findFlatConfigFile)(projectRoot, context.root);
const hasFlatConfig = flatConfigFilePath !== null;
// while standard eslint uses by default closest config to the file, if otherwise not specified,
// the flat config would be resolved starting from the cwd, which we changed to the workspace root
// so we explicitly set the config path to the flat config file path we previously found
if (hasFlatConfig && !normalizedOptions.eslintConfig) {
normalizedOptions.eslintConfig = (0, path_1.relative)(systemRoot, flatConfigFilePath);
}
/**
* We want users to have the option of not specifying the config path, and let
* eslint automatically resolve the `.eslintrc.json` files in each folder.
*/
let eslintConfigPath = normalizedOptions.eslintConfig
? (0, path_1.resolve)(systemRoot, normalizedOptions.eslintConfig)
: undefined;
const { eslint, ESLint } = await (0, eslint_utils_1.resolveAndInstantiateESLint)(eslintConfigPath, normalizedOptions, hasFlatConfig);
const version = ESLint.version?.split('.');
if (!version ||
version.length < 2 ||
Number(version[0]) < 7 ||
(Number(version[0]) === 7 && Number(version[1]) < 6)) {
throw new Error('ESLint must be version 7.6 or higher.');
}
if (printConfig) {
try {
const fileConfig = await eslint.calculateConfigForFile(printConfig);
console.log(JSON.stringify(fileConfig, null, ' '));
return {
success: true,
};
}
catch (err) {
console.error(err);
return {
success: false,
};
}
}
let lintResults = [];
const normalizedLintFilePatterns = normalizedOptions.lintFilePatterns.map((pattern) => {
return (0, utils_1.interpolate)(pattern, {
workspaceRoot: '',
projectRoot,
projectName: context.projectName,
});
});
try {
lintResults = await eslint.lintFiles(normalizedLintFilePatterns);
}
catch (err) {
if (err.message.includes('You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser')) {
const ruleName = err.message.match(/rule '([^']+)':/)?.[1];
const reportedFile = err.message.match(/Occurred while linting (.+)$/)?.[1];
let eslintConfigPathForError = `for the project "${projectName}"`;
if (eslintConfigPath) {
eslintConfigPathForError = `"${path_1.posix.relative(context.root, eslintConfigPath)}"`;
}
else {
const configPathForfile = hasFlatConfig
? (0, config_file_1.findFlatConfigFile)(projectRoot, context.root)
: (0, config_file_1.findOldConfigFile)(reportedFile ?? projectRoot, context.root);
if (configPathForfile) {
eslintConfigPathForError = `"${path_1.posix.relative(context.root, configPathForfile)}"`;
}
}
console.error(`
Error: You have attempted to use ${ruleName ? `the lint rule "${ruleName}"` : 'a lint rule'} which requires the full TypeScript type-checker to be available, but you do not have "parserOptions.project" configured to point at your project tsconfig.json files in the relevant TypeScript file "overrides" block of your ESLint config ${eslintConfigPathForError}
${reportedFile ? `Occurred while linting ${reportedFile}` : ''}
Please see https://nx.dev/recipes/tips-n-tricks/eslint for full guidance on how to resolve this issue.
`);
return {
success: false,
};
}
// log unexpected errors
const lines = (err.message ? err.message : err.toString()).split('\n');
const bodyLines = lines.slice(1);
if (err.stack) {
bodyLines.push(...err.stack.split('\n'));
}
devkit_1.output.error({
title: lines[0],
bodyLines,
});
return {
success: false,
};
}
if (lintResults.length === 0 && errorOnUnmatchedPattern) {
const ignoredPatterns = (await Promise.all(normalizedLintFilePatterns.map(async (pattern) => (await eslint.isPathIgnored(pattern)) ? pattern : null)))
.filter((pattern) => !!pattern)
.map((pattern) => `- '${pattern}'`);
if (ignoredPatterns.length) {
const ignoreSection = hasFlatConfig
? `'ignores' configuration`
: `'.eslintignore' file`;
throw new Error(`All files matching the following patterns are ignored:\n${ignoredPatterns.join('\n')}\n\nPlease check your ${ignoreSection}.`);
}
throw new Error('Invalid lint configuration. Nothing to lint. Please check your lint target pattern(s).');
}
// output fixes to disk, if applicable based on the options
await ESLint.outputFixes(lintResults);
// if quiet, only show errors
if (normalizedOptions.quiet) {
console.debug('Quiet mode enabled - filtering out warnings\n');
lintResults = ESLint.getErrorResults(lintResults);
}
const formatter = await eslint.loadFormatter(normalizedOptions.format);
const formattedResults = await formatter.format(lintResults);
if (normalizedOptions.outputFile) {
const pathToOutputFile = (0, devkit_1.joinPathFragments)(context.root, normalizedOptions.outputFile);
(0, fs_1.mkdirSync)((0, path_1.dirname)(pathToOutputFile), { recursive: true });
(0, fs_1.writeFileSync)(pathToOutputFile, formattedResults);
}
else {
console.info(formattedResults);
}
const totals = getTotals(lintResults);
if (printInfo) {
outputPrintInfo(totals);
}
if (options.maxWarnings >= 0 && totals.warnings > options.maxWarnings) {
console.info(`ESLint found too many warnings (maximum: ${options.maxWarnings}).`);
}
return {
success: normalizedOptions.force ||
(totals.errors === 0 &&
(normalizedOptions.maxWarnings === -1 ||
totals.warnings <= normalizedOptions.maxWarnings)),
};
}
function getTotals(lintResults) {
let errors = 0;
let warnings = 0;
let fixableErrors = 0;
let fixableWarnings = 0;
for (const result of lintResults) {
errors += result.errorCount || 0;
warnings += result.warningCount || 0;
fixableErrors += result.fixableErrorCount || 0;
fixableWarnings += result.fixableWarningCount || 0;
}
return {
errors,
warnings,
fixableErrors,
fixableWarnings,
};
}
function pluralizedOutput(word, count) {
return `${count} ${word}${count === 1 ? '' : 's'}`;
}
function outputPrintInfo({ errors, warnings, fixableErrors, fixableWarnings, }) {
const total = warnings + errors;
const totalFixable = fixableErrors + fixableWarnings;
if (total <= 0) {
console.info('\u2714 All files pass linting\n');
return;
}
console.info(`\u2716 ${pluralizedOutput('problem', total)} (${pluralizedOutput('error', errors)}, ${pluralizedOutput('warning', warnings)})\n`);
if (totalFixable <= 0)
return;
console.info(` ${pluralizedOutput('error', fixableErrors)} and ${pluralizedOutput('warning', fixableWarnings)} are potentially fixable with the \`--fix\` option.\n`);
}

45
node_modules/@nx/eslint/src/executors/lint/schema.d.ts generated vendored Normal file
View File

@@ -0,0 +1,45 @@
import type { JsonObject } from '@angular-devkit/core';
import type { Linter } from 'eslint';
export interface Schema extends JsonObject {
eslintConfig: string | null;
lintFilePatterns: string[];
format: Formatter;
force: boolean;
silent: boolean;
fix: boolean;
cache: boolean;
noEslintrc: boolean;
outputFile: string | null;
cacheLocation: string | null;
maxWarnings: number;
quiet: boolean;
ignorePath: string | null;
/** @deprecated No longer has any effect. Will be removed in Nx v23. */
hasTypeAwareRules?: boolean;
cacheStrategy: 'content' | 'metadata' | null;
rulesdir: string[];
resolvePluginsRelativeTo: string | null;
reportUnusedDisableDirectives: Linter.StringSeverity | null;
printConfig?: string | null;
errorOnUnmatchedPattern?: boolean;
suppressAll?: boolean;
suppressRule?: string[];
suppressionsLocation?: string;
}
type Formatter =
| 'stylish'
| 'compact'
| 'codeframe'
| 'unix'
| 'visualstudio'
| 'table'
| 'checkstyle'
| 'html'
| 'jslint-xml'
| 'json'
| 'json-with-metadata'
| 'junit'
| 'tap';

167
node_modules/@nx/eslint/src/executors/lint/schema.json generated vendored Normal file
View File

@@ -0,0 +1,167 @@
{
"version": 2,
"outputCapture": "direct-nodejs",
"$schema": "https://json-schema.org/schema",
"title": "ESLint Lint Target",
"description": "ESLint Lint Target.",
"cli": "nx",
"type": "object",
"properties": {
"eslintConfig": {
"type": "string",
"description": "The name of the ESLint configuration file.",
"x-completion-type": "file",
"x-completion-glob": ".eslintrc?(.json)",
"x-priority": "important"
},
"lintFilePatterns": {
"type": "array",
"description": "One or more files/dirs/globs to pass directly to ESLint's `lintFiles()` method.",
"default": ["{projectRoot}"],
"items": {
"type": "string"
}
},
"format": {
"type": "string",
"description": "ESLint Output formatter (https://eslint.org/docs/user-guide/formatters).",
"default": "stylish",
"anyOf": [
{
"enum": [
"stylish",
"compact",
"codeframe",
"unix",
"visualstudio",
"table",
"checkstyle",
"html",
"jslint-xml",
"json",
"json-with-metadata",
"junit",
"tap"
]
},
{
"minLength": 1
}
],
"x-priority": "important"
},
"force": {
"type": "boolean",
"description": "Succeeds even if there was linting errors.",
"default": false
},
"silent": {
"type": "boolean",
"description": "Hide output text.",
"default": false
},
"fix": {
"type": "boolean",
"description": "Fixes linting errors (may overwrite linted files).",
"default": false,
"x-priority": "important"
},
"cache": {
"type": "boolean",
"description": "Only check changed files.",
"default": false
},
"cacheLocation": {
"type": "string",
"description": "Path to the cache file or directory.",
"x-completion-type": "directory",
"x-completion-glob": "tsconfig.*.json"
},
"outputFile": {
"type": "string",
"description": "File to write report to.",
"x-completion-type": "file"
},
"maxWarnings": {
"type": "number",
"description": "Number of warnings to trigger nonzero exit code - default: `-1`.",
"default": -1
},
"quiet": {
"type": "boolean",
"description": "Report errors only - default: `false`.",
"default": false,
"x-priority": "important"
},
"ignorePath": {
"type": "string",
"description": "The path of the `.eslintignore` file. Not supported for Flat Config.",
"x-completion-type": "file",
"x-completion-glob": ".eslintignore"
},
"noEslintrc": {
"type": "boolean",
"description": "The equivalent of the `--no-eslintrc` flag on the ESLint CLI, it is `false` by default.",
"default": false
},
"hasTypeAwareRules": {
"type": "boolean",
"description": "Deprecated. No longer has any effect.",
"x-deprecated": "No longer has any effect. Will be removed in Nx v23."
},
"cacheStrategy": {
"type": "string",
"description": "Strategy to use for detecting changed files in the cache.",
"default": "metadata",
"enum": ["metadata", "content"]
},
"rulesdir": {
"type": "array",
"description": "The equivalent of the `--rulesdir` flag on the ESLint CLI.",
"default": [],
"items": {
"type": "string",
"x-completion-type": "directory"
}
},
"resolvePluginsRelativeTo": {
"type": "string",
"description": "The equivalent of the `--resolve-plugins-relative-to` flag on the ESLint CLI. Not supported for Flat Config.",
"x-completion-type": "directory"
},
"reportUnusedDisableDirectives": {
"type": "string",
"enum": ["off", "warn", "error"],
"description": "The equivalent of the `--report-unused-disable-directives` flag on the ESLint CLI."
},
"printConfig": {
"type": "string",
"description": "The equivalent of the `--print-config` flag on the ESLint CLI.",
"x-completion-type": "file"
},
"errorOnUnmatchedPattern": {
"type": "boolean",
"description": "When set to false, equivalent of the `--no-error-on-unmatched-pattern` flag on the ESLint CLI.",
"default": true
},
"suppressAll": {
"type": "boolean",
"description": "Suppress all existing violations. This is equivalent to the `--suppress-all` flag on the ESLint CLI. Requires ESLint v9.24.0 or higher.",
"default": false
},
"suppressRule": {
"type": "array",
"description": "Suppress violations for specific rules. This is equivalent to the `--suppress-rule` flag on the ESLint CLI. Requires ESLint v9.24.0 or higher.",
"default": [],
"items": {
"type": "string"
}
},
"suppressionsLocation": {
"type": "string",
"description": "Specify the location of the suppressions file. This is equivalent to the `--suppressions-location` flag on the ESLint CLI. Defaults to 'eslint-suppressions.json' in the project root. Requires ESLint v9.24.0 or higher.",
"x-completion-type": "file"
}
},
"examplesFile": "../../../docs/eslint-examples.md"
}

View File

@@ -0,0 +1,7 @@
import type { ESLint } from 'eslint';
import type { Schema } from '../schema';
export declare function resolveAndInstantiateESLint(eslintConfigPath: string | undefined, options: Schema, useFlatConfig?: boolean): Promise<{
ESLint: typeof import("eslint").ESLint;
eslint: ESLint;
}>;
//# sourceMappingURL=eslint-utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"eslint-utils.d.ts","sourceRoot":"","sources":["../../../../../../../packages/eslint/src/executors/lint/utility/eslint-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC,wBAAsB,2BAA2B,CAC/C,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,OAAO,EAAE,MAAM,EACf,aAAa,UAAQ;;;GA6HtB"}

View File

@@ -0,0 +1,104 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveAndInstantiateESLint = resolveAndInstantiateESLint;
const semver_1 = require("semver");
const config_file_1 = require("../../../utils/config-file");
const resolve_eslint_class_1 = require("../../../utils/resolve-eslint-class");
async function resolveAndInstantiateESLint(eslintConfigPath, options, useFlatConfig = false) {
if (useFlatConfig && eslintConfigPath && !(0, config_file_1.isFlatConfig)(eslintConfigPath)) {
throw new Error('When using the new Flat Config with ESLint, all configs must be named eslint.config.js or eslint.config.cjs and .eslintrc files may not be used. See https://eslint.org/docs/latest/use/configure/configuration-files');
}
const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)({
useFlatConfigOverrideVal: useFlatConfig,
});
// Use the broader legacy options shape so the v8 (legacy-only) fields assigned
// below type-check. Flat-only fields (ruleFilter, suppress*) are intersected
// in; they're ignored at runtime by legacy ESLint and are version-gated below.
const eslintOptions = {
overrideConfigFile: eslintConfigPath,
fix: !!options.fix &&
(options.quiet ? (message) => message.severity === 2 : true),
cache: !!options.cache,
cacheLocation: options.cacheLocation || undefined,
cacheStrategy: options.cacheStrategy || undefined,
/**
* Default is `true` and if not overridden the eslint.lintFiles() method will throw an error
* when no target files are found.
*
* We don't want ESLint to throw an error if a user has only just created
* a project and therefore doesn't necessarily have matching files, for example.
*
* Also, the angular generator creates a lint pattern for `html` files, but there may
* not be any html files in the project, so keeping it true would break linting every time
*/
errorOnUnmatchedPattern: false,
};
if (useFlatConfig) {
if (typeof options.useEslintrc !== 'undefined') {
throw new Error('For Flat Config, the `useEslintrc` option is not applicable. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
}
if (options.resolvePluginsRelativeTo !== undefined) {
throw new Error('For Flat Config, ESLint removed `resolvePluginsRelativeTo` and so it is not supported as an option. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
}
if (options.ignorePath !== undefined) {
throw new Error('For Flat Config, ESLint removed `ignorePath` and so it is not supported as an option. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
}
if (options.reportUnusedDisableDirectives) {
throw new Error('For Flat Config, ESLint removed `reportedUnusedDisableDirectives` and so it is not supported as an option. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
}
}
else {
eslintOptions.rulePaths = options.rulesdir || [];
eslintOptions.resolvePluginsRelativeTo =
options.resolvePluginsRelativeTo || undefined;
eslintOptions.ignorePath = options.ignorePath || undefined;
/**
* If "noEslintrc" is set to `true` (and therefore here "useEslintrc" will be `false`), then ESLint will not
* merge the provided config with others it finds automatically.
*/
eslintOptions.useEslintrc = !options.noEslintrc;
eslintOptions.reportUnusedDisableDirectives =
options.reportUnusedDisableDirectives || undefined;
}
// pass --quiet to eslint 9+ directly: filter only errors
if (options.quiet && (0, semver_1.gte)(ESLint.version, '9.0.0')) {
eslintOptions.ruleFilter = (rule) => rule.severity === 2;
}
// Handle bulk suppression options (ESLint v9.24.0+)
try {
if (ESLint.version && (0, semver_1.gte)(ESLint.version, '9.24.0')) {
if (options.suppressAll) {
eslintOptions.suppressAll = true;
}
if (options.suppressRule && options.suppressRule.length > 0) {
eslintOptions.suppressRule = options.suppressRule;
}
if (options.suppressionsLocation) {
eslintOptions.suppressionsLocation = options.suppressionsLocation;
}
}
else if (options.suppressAll ||
(options.suppressRule && options.suppressRule.length > 0) ||
options.suppressionsLocation) {
throw new Error('Bulk suppression options (suppressAll, suppressRule, suppressionsLocation) require ESLint v9.24.0 or higher. Current version: ' +
(ESLint.version || 'unknown'));
}
}
catch (error) {
// If version checking fails (e.g., in tests), skip suppression options
if (options.suppressAll ||
(options.suppressRule && options.suppressRule.length > 0) ||
options.suppressionsLocation) {
// In test environment, just skip the suppression options
console.warn('Bulk suppression options skipped due to version check failure');
}
}
// Runtime ESLint class may be the flat or legacy implementation; the built
// options object is compatible with either at runtime, but the two Options
// shapes diverge in v9 types so cast at the boundary.
const eslint = new ESLint(eslintOptions);
return {
ESLint,
eslint,
};
}

View File

@@ -0,0 +1,13 @@
import { Tree } from '@nx/devkit';
import { ESLint } from 'eslint';
export declare function renameLegacyEslintrcFile(path: string, format: 'mjs' | 'cjs'): string;
/**
* Converts an ESLint JSON config to a flat config.
* Deletes the original file along with .eslintignore if it exists.
*/
export declare function convertEslintJsonToFlatConfig(tree: Tree, root: string, config: ESLint.ConfigData, ignorePaths: string[], format: 'cjs' | 'mjs'): {
content: string;
addESLintRC: boolean;
addESLintJS: boolean;
};
//# sourceMappingURL=json-converter.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"json-converter.d.ts","sourceRoot":"","sources":["../../../../../../../packages/eslint/src/generators/convert-to-flat-config/converters/json-converter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAS,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAmBhC,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,KAAK,GAAG,KAAK,GACpB,MAAM,CAOR;AA0DD;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,UAAU,EACzB,WAAW,EAAE,MAAM,EAAE,EACrB,MAAM,EAAE,KAAK,GAAG,KAAK,GACpB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CA6PjE"}

View File

@@ -0,0 +1,320 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.renameLegacyEslintrcFile = renameLegacyEslintrcFile;
exports.convertEslintJsonToFlatConfig = convertEslintJsonToFlatConfig;
const tslib_1 = require("tslib");
const devkit_1 = require("@nx/devkit");
const ts = tslib_1.__importStar(require("typescript"));
const ast_utils_1 = require("../../utils/flat-config/ast-utils");
const eslint_file_1 = require("../../utils/eslint-file");
const path_utils_1 = require("../../utils/flat-config/path-utils");
// Rewrites legacy `.eslintrc[.base][.json]` / `.eslintignore` filenames to their flat-config
// counterparts. Used for `extends` local paths, rule option values that embed these filenames,
// and nx.json / project.json input globs that referenced the deleted files. Accepts
// extensionless `.eslintrc` since ESLint treats that as JSON by convention.
function renameLegacyEslintrcFile(path, format) {
return path
.replace(/(^|.*?)\.eslintrc(\.base)?(\.json)?$/, `$1eslint$2.config.${format}`)
.replace(/(^|.*?)\.eslintignore$/, `$1eslint.config.${format}`);
}
// In flat config, `@nx/workspace/<rule>` is parsed as plugin `@nx/workspace`, rule `<rule>`.
// The `@nx` plugin already exposes workspace rules under both `workspace/<rule>` and `workspace-<rule>` keys.
// Rewriting to `@nx/workspace-<rule>` makes ESLint resolve them via the already-registered `@nx` plugin.
function renameLegacyWorkspaceRules(rules) {
const renamed = {};
for (const [key, value] of Object.entries(rules)) {
const newKey = key.startsWith('@nx/workspace/')
? '@nx/workspace-' + key.slice('@nx/workspace/'.length)
: key;
renamed[newKey] = value;
}
return renamed;
}
// Rewrites references to the legacy `.eslintrc[.base].json` / `.eslintignore` that may appear
// inside rule option values (e.g. `@nx/dependency-checks`'s `ignoredFiles`) to point at the
// generated flat-config files instead. Without this, rule options keep pointing at files that
// no longer exist after the conversion.
function rewriteStaleEslintrcRefs(value, format) {
if (typeof value === 'string') {
return renameLegacyEslintrcFile(value, format);
}
if (Array.isArray(value)) {
const mapped = value.map((v) => rewriteStaleEslintrcRefs(v, format));
// Rewriting may collapse distinct strings (e.g. `.eslintrc.json` and
// `.eslintrc.base.json`) into identical entries; dedupe string arrays.
if (mapped.every((v) => typeof v === 'string')) {
return Array.from(new Set(mapped));
}
return mapped;
}
if (value && typeof value === 'object') {
const out = {};
for (const [k, v] of Object.entries(value)) {
out[k] = rewriteStaleEslintrcRefs(v, format);
}
return out;
}
return value;
}
function preprocessRules(rules, format) {
return rewriteStaleEslintrcRefs(renameLegacyWorkspaceRules(rules), format);
}
/**
* Converts an ESLint JSON config to a flat config.
* Deletes the original file along with .eslintignore if it exists.
*/
function convertEslintJsonToFlatConfig(tree, root, config, ignorePaths, format) {
const importsMap = new Map();
const exportElements = [];
let isFlatCompatNeeded = false;
let isESLintJSNeeded = false;
let combinedConfig = [];
let languageOptions = [];
if (config.rules) {
config.rules = preprocessRules(config.rules, format);
}
if (config.overrides) {
config.overrides = config.overrides.map((override) => override.rules
? {
...override,
rules: preprocessRules(override.rules, format),
}
: override);
}
if (config.extends) {
const extendsResult = addExtends(importsMap, exportElements, config, format);
isFlatCompatNeeded = extendsResult.isFlatCompatNeeded;
isESLintJSNeeded = extendsResult.isESLintJSNeeded;
}
if (config.plugins) {
addPlugins(importsMap, exportElements, config);
}
if (config.parser) {
const imp = config.parser;
const parserName = (0, devkit_1.names)(imp).propertyName;
importsMap.set(imp, parserName);
languageOptions.push(ts.factory.createPropertyAssignment('parser', ts.factory.createIdentifier(parserName)));
}
if (config.parserOptions) {
languageOptions.push(ts.factory.createPropertyAssignment('parserOptions', (0, ast_utils_1.generateAst)(config.parserOptions)));
}
if (config.globals || config.env) {
if (config.env) {
importsMap.set('globals', 'globals');
}
languageOptions.push(ts.factory.createPropertyAssignment('globals', ts.factory.createObjectLiteralExpression([
...Object.keys(config.env || {}).map((env) => ts.factory.createSpreadAssignment(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('globals'), ts.factory.createIdentifier(env)))),
...Object.keys(config.globals || {}).map((key) => ts.factory.createPropertyAssignment(key, (0, ast_utils_1.generateAst)(config.globals[key]))),
])));
}
if (config.settings) {
combinedConfig.push(ts.factory.createPropertyAssignment('settings', (0, ast_utils_1.generateAst)(config.settings)));
}
if (config.noInlineConfig !== undefined ||
config.reportUnusedDisableDirectives !== undefined) {
combinedConfig.push(ts.factory.createPropertyAssignment('linterOptions', (0, ast_utils_1.generateAst)({
noInlineConfig: config.noInlineConfig,
reportUnusedDisableDirectives: config.reportUnusedDisableDirectives,
})));
}
if (languageOptions.length > 0) {
combinedConfig.push(ts.factory.createPropertyAssignment('languageOptions', ts.factory.createObjectLiteralExpression(languageOptions, languageOptions.length > 1)));
}
if (combinedConfig.length > 0) {
exportElements.push(ts.factory.createObjectLiteralExpression(combinedConfig, combinedConfig.length > 1));
}
if (config.rules) {
exportElements.push((0, ast_utils_1.generateAst)({ rules: config.rules }));
}
if (config.overrides) {
config.overrides.forEach((override) => {
if (override.extends) {
const extendsArr = Array.isArray(override.extends)
? override.extends
: [override.extends];
const mapped = extendsArr.map((e) => ({
original: e,
flatConfig: mapNxPluginToFlatConfig(e),
}));
const nxExtends = mapped.filter((m) => m.flatConfig);
const nonNxExtends = mapped
.filter((m) => !m.flatConfig)
.map((m) => m.original);
if (nxExtends.length > 0) {
const nxVar = importsMap.get('@nx/eslint-plugin') ?? 'nx';
importsMap.set('@nx/eslint-plugin', nxVar);
nxExtends.forEach((ext) => {
exportElements.push((0, ast_utils_1.generateFlatPredefinedConfig)(ext.flatConfig, nxVar, true));
});
// Build remaining override without Nx extends
const remainingOverride = { ...override };
if (nonNxExtends.length > 0) {
remainingOverride.extends = nonNxExtends;
}
else {
delete remainingOverride.extends;
}
// Emit remaining override if it has content beyond files and empty rules
const { files: _files, rules: remainingRules, ...remainingRest } = remainingOverride;
const hasNonEmptyRules = remainingRules && Object.keys(remainingRules).length > 0;
if (Object.keys(remainingRest).length > 0 || hasNonEmptyRules) {
if (remainingOverride.env ||
remainingOverride.extends ||
remainingOverride.plugins) {
isFlatCompatNeeded = true;
}
exportElements.push((0, ast_utils_1.generateFlatOverride)(remainingOverride, format, importsMap));
}
return;
}
}
if (override.env || override.extends || override.plugins) {
isFlatCompatNeeded = true;
}
exportElements.push((0, ast_utils_1.generateFlatOverride)(override, format, importsMap));
});
}
if (config.ignorePatterns) {
const patterns = (Array.isArray(config.ignorePatterns)
? config.ignorePatterns
: [config.ignorePatterns]).filter((pattern) =>
// Drop patterns that are meaningless in flat config. `'**/*'` and
// `'!**/*'` were eslintrc cascading toggles; `node_modules` is already
// ignored by default. Real negations like `['dist/**', '!dist/keep.js']`
// are preserved — flat config still supports un-ignoring within a
// broader ignores block.
!['**/*', '!**/*', 'node_modules'].includes(pattern));
if (patterns.length > 0) {
exportElements.push((0, ast_utils_1.generateAst)({
ignores: patterns.map((path) => (0, path_utils_1.mapFilePath)(path)),
}));
}
}
for (const ignorePath of ignorePaths) {
if (tree.exists(ignorePath)) {
const patterns = tree
.read(ignorePath, 'utf-8')
.split(/\r\n|\r|\n/)
.filter((line) => line.length > 0 && line !== 'node_modules')
.map((path) => (0, path_utils_1.mapFilePath)(path));
if (patterns.length > 0) {
exportElements.push((0, ast_utils_1.generateAst)({ ignores: patterns }));
}
}
}
// create the node list and print it to new file
const nodeList = (0, ast_utils_1.createNodeList)(importsMap, exportElements, format);
let content = (0, ast_utils_1.stringifyNodeList)(nodeList);
if (isFlatCompatNeeded) {
content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
}
return {
content,
addESLintRC: isFlatCompatNeeded,
addESLintJS: isESLintJSNeeded,
};
}
// add parsed extends to export blocks and add import statements
function addExtends(importsMap, configBlocks, config, format) {
let isFlatCompatNeeded = false;
let isESLintJSNeeded = false;
const extendsConfig = Array.isArray(config.extends)
? config.extends
: [config.extends];
const eslintrcConfigs = [];
// add base extends
extendsConfig
.filter((imp) => imp.match(/^\.?(\.\/)/))
.forEach((imp, index) => {
if (imp.match(/\.eslintrc(\.base)?(\.json)?$/)) {
const localName = index ? `baseConfig${index}` : 'baseConfig';
configBlocks.push((0, ast_utils_1.generateSpreadElement)(localName));
importsMap.set(renameLegacyEslintrcFile(imp, format), localName);
}
else {
eslintrcConfigs.push(imp);
}
});
// add plugin extends
const pluginExtends = extendsConfig.filter((imp) => !imp.match(/^\.?(\.\/)/));
if (pluginExtends.length) {
const eslintPluginExtends = pluginExtends.filter((imp) => imp.startsWith('eslint:'));
pluginExtends.forEach((imp) => {
if (imp.startsWith('eslint:')) {
return;
}
const nxFlatConfig = mapNxPluginToFlatConfig(imp);
if (nxFlatConfig) {
const nxVar = importsMap.get('@nx/eslint-plugin') ?? 'nx';
importsMap.set('@nx/eslint-plugin', nxVar);
configBlocks.push((0, ast_utils_1.generateFlatPredefinedConfig)(nxFlatConfig, nxVar, true));
}
else {
eslintrcConfigs.push(imp);
}
});
if (eslintPluginExtends.length) {
isESLintJSNeeded = true;
importsMap.set('@eslint/js', 'js');
eslintPluginExtends.forEach((plugin) => {
configBlocks.push(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('js'), ts.factory.createIdentifier('configs')), ts.factory.createIdentifier(plugin.slice(7)) // strip 'eslint:' prefix
));
});
}
}
if (eslintrcConfigs.length) {
isFlatCompatNeeded = true;
isESLintJSNeeded = true;
configBlocks.push((0, ast_utils_1.generatePluginExtendsElement)(eslintrcConfigs));
}
return { isFlatCompatNeeded, isESLintJSNeeded };
}
function addPlugins(importsMap, configBlocks, config) {
// Replace @nx plugin with flat/base predefined config to match fresh generation.
// flat/base registers the @nx plugin and ignores .nx directory.
// This runs before overrides are processed, so we set the import name here
// for Nx extends that may appear in overrides later.
if (config.plugins.includes('@nx')) {
importsMap.set('@nx/eslint-plugin', 'nx');
configBlocks.push((0, ast_utils_1.generateFlatPredefinedConfig)('flat/base', 'nx', true));
}
const remainingPlugins = config.plugins.filter((name) => name !== '@nx');
if (remainingPlugins.length === 0) {
return;
}
const mappedPlugins = [];
remainingPlugins.forEach((name) => {
const imp = (0, eslint_file_1.getPluginImport)(name);
const varName = importsMap.get(imp) ?? (0, devkit_1.names)(imp).propertyName;
mappedPlugins.push({ name, varName, imp });
});
mappedPlugins.forEach(({ varName, imp }) => {
importsMap.set(imp, varName);
});
const pluginsAst = ts.factory.createObjectLiteralExpression([
ts.factory.createPropertyAssignment('plugins', ts.factory.createObjectLiteralExpression(mappedPlugins.map(({ name, varName }) => {
return ts.factory.createPropertyAssignment(ts.factory.createStringLiteral(name), ts.factory.createIdentifier(varName));
}), mappedPlugins.length > 1)),
...(config.processor
? [
ts.factory.createPropertyAssignment('processor', ts.factory.createStringLiteral(config.processor)),
]
: []),
], false);
configBlocks.push(pluginsAst);
}
const nxPluginToFlatConfigMap = {
'plugin:@nx/typescript': 'flat/typescript',
'plugin:@nx/javascript': 'flat/javascript',
'plugin:@nx/react': 'flat/react',
'plugin:@nx/react-base': 'flat/react-base',
'plugin:@nx/react-typescript': 'flat/react-typescript',
'plugin:@nx/react-jsx': 'flat/react-jsx',
'plugin:@nx/angular': 'flat/angular',
'plugin:@nx/angular-template': 'flat/angular-template',
'plugin:@nrwl/nx/typescript': 'flat/typescript',
'plugin:@nrwl/nx/javascript': 'flat/javascript',
};
function mapNxPluginToFlatConfig(pluginExtend) {
return nxPluginToFlatConfigMap[pluginExtend];
}

View File

@@ -0,0 +1,5 @@
import { GeneratorCallback, Tree } from '@nx/devkit';
import { ConvertToFlatConfigGeneratorSchema } from './schema';
export declare function convertToFlatConfigGenerator(tree: Tree, options: ConvertToFlatConfigGeneratorSchema): Promise<void | GeneratorCallback>;
export default convertToFlatConfigGenerator;
//# sourceMappingURL=generator.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/convert-to-flat-config/generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,iBAAiB,EASjB,IAAI,EAGL,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kCAAkC,EAAE,MAAM,UAAU,CAAC;AAiB9D,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,kCAAkC,GAC1C,OAAO,CAAC,IAAI,GAAG,iBAAiB,CAAC,CA8CnC;AAED,eAAe,4BAA4B,CAAC"}

View File

@@ -0,0 +1,236 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertToFlatConfigGenerator = convertToFlatConfigGenerator;
const devkit_1 = require("@nx/devkit");
const eslint_file_1 = require("../utils/eslint-file");
const plugin_1 = require("../utils/plugin");
const path_1 = require("path");
const versions_1 = require("../../utils/versions");
const json_converter_1 = require("./converters/json-converter");
async function convertToFlatConfigGenerator(tree, options) {
const eslintFile = (0, eslint_file_1.findEslintFile)(tree);
if (!eslintFile) {
throw new Error('Could not find root eslint file');
}
if (eslintFile.endsWith('.js')) {
throw new Error('Only json and yaml eslint config files are supported for conversion');
}
options.eslintConfigFormat ??= 'mjs';
const eslintIgnoreFiles = new Set(['.eslintignore']);
// convert root eslint config to eslint.config.cjs or eslint.base.config.mjs based on eslintConfigFormat
convertRootToFlatConfig(tree, eslintFile, options.eslintConfigFormat);
// convert project eslint files to eslint.config.cjs
const projects = (0, devkit_1.getProjects)(tree);
for (const [project, projectConfig] of projects) {
convertProjectToFlatConfig(tree, project, projectConfig, (0, devkit_1.readNxJson)(tree), eslintIgnoreFiles, options.eslintConfigFormat);
}
// delete all .eslintignore files
for (const ignoreFile of eslintIgnoreFiles) {
tree.delete(ignoreFile);
}
// replace references in nx.json and project.json files
updateNxJsonConfig(tree, options.eslintConfigFormat);
updateProjectConfigsInputs(tree, options.eslintConfigFormat);
// install missing packages
if (!options.skipFormat) {
await (0, devkit_1.formatFiles)(tree);
}
return () => (0, devkit_1.installPackagesTask)(tree);
}
exports.default = convertToFlatConfigGenerator;
function convertRootToFlatConfig(tree, eslintFile, format) {
if (/\.base\.(js|json|yml|yaml)$/.test(eslintFile)) {
convertConfigToFlatConfig(tree, '', eslintFile, `eslint.base.config.${format}`, format);
}
convertConfigToFlatConfig(tree, '', eslintFile.replace('.base.', '.'), `eslint.config.${format}`, format);
}
const ESLINT_LINT_EXECUTOR = '@nx/eslint:lint';
function isEslintTarget(target) {
return (target.executor === ESLINT_LINT_EXECUTOR ||
target.command?.includes('eslint'));
}
function convertProjectToFlatConfig(tree, project, projectConfig, nxJson, eslintIgnoreFiles, format) {
const eslintFile = (0, eslint_file_1.findEslintFile)(tree, projectConfig.root);
if (!eslintFile || eslintFile.endsWith('.js')) {
return;
}
// Clean up obsolete target options and detect explicit ESLint targets
let ignorePath;
const eslintTargets = projectConfig.targets
? Object.keys(projectConfig.targets).filter((t) => isEslintTarget(projectConfig.targets[t]))
: [];
for (const target of eslintTargets) {
if (projectConfig.targets[target].options?.eslintConfig) {
delete projectConfig.targets[target].options.eslintConfig;
}
if (projectConfig.targets[target].options?.ignorePath) {
ignorePath = projectConfig.targets[target].options.ignorePath;
delete projectConfig.targets[target].options.ignorePath;
}
}
if (eslintTargets.length > 0) {
(0, devkit_1.updateProjectConfiguration)(tree, project, projectConfig);
}
const hasEslintTargetDefaults = projectConfig.targets &&
Object.keys(nxJson.targetDefaults || {}).some((t) => (t === ESLINT_LINT_EXECUTOR ||
isEslintTarget(nxJson.targetDefaults[t])) &&
projectConfig.targets[t]);
if (eslintTargets.length === 0 &&
!hasEslintTargetDefaults &&
!(0, plugin_1.hasEslintPlugin)(tree)) {
devkit_1.logger.warn(`Skipping "${project}": found ${eslintFile} but no ESLint lint target detected. Convert manually if needed.`);
return;
}
convertConfigToFlatConfig(tree, projectConfig.root, eslintFile, `eslint.config.${format}`, format, ignorePath);
eslintIgnoreFiles.add(`${projectConfig.root}/.eslintignore`);
if (ignorePath) {
eslintIgnoreFiles.add(ignorePath);
}
}
// Rewrites input entries that reference legacy `.eslintrc[.base].json` / `.eslintignore`
// files to their flat-config counterparts, then dedupes so the rewrite doesn't produce
// duplicates of entries that already pointed at the flat config. Leaves non-string /
// non-fileset inputs (runtime/env/dependentTasksOutputFiles/etc.) untouched.
function rewriteLegacyInputs(inputs, format) {
const seenStrings = new Set();
const result = [];
for (const entry of inputs) {
if (typeof entry === 'string') {
const rewritten = (0, json_converter_1.renameLegacyEslintrcFile)(entry, format);
if (seenStrings.has(rewritten))
continue;
seenStrings.add(rewritten);
result.push(rewritten);
}
else if ('fileset' in entry) {
const rewritten = (0, json_converter_1.renameLegacyEslintrcFile)(entry.fileset, format);
// Preserve the original reference when nothing changed so downstream identity
// checks (e.g. `inputsEqual`) don't see a spurious mutation.
result.push(rewritten === entry.fileset ? entry : { ...entry, fileset: rewritten });
}
else {
result.push(entry);
}
}
return result;
}
// Adds `value` to `inputs` (after rewriting) when the rewritten set doesn't already contain it.
function ensureInputPresent(inputs, value, format) {
const rewritten = rewriteLegacyInputs(inputs, format);
if (!rewritten.some((entry) => entry === value)) {
rewritten.push(value);
}
return rewritten;
}
// Updates nx.json: rewrites stale eslintrc/eslintignore references across all targetDefaults
// inputs and namedInputs, and ensures lint targets include the new flat config file as an input
// (and `production` excludes it).
function updateNxJsonConfig(tree, format) {
if (!tree.exists('nx.json')) {
return;
}
(0, devkit_1.updateJson)(tree, 'nx.json', (json) => {
if (json.targetDefaults) {
for (const [name, target] of Object.entries(json.targetDefaults)) {
if (!target.inputs)
continue;
const isLintTarget = name === 'lint' || name === ESLINT_LINT_EXECUTOR;
target.inputs = isLintTarget
? ensureInputPresent(target.inputs, `{workspaceRoot}/eslint.config.${format}`, format)
: rewriteLegacyInputs(target.inputs, format);
}
}
if (json.namedInputs) {
for (const [name, inputs] of Object.entries(json.namedInputs)) {
json.namedInputs[name] =
name === 'production'
? ensureInputPresent(inputs, `!{projectRoot}/eslint.config.${format}`, format)
: rewriteLegacyInputs(inputs, format);
}
}
return json;
});
}
// Walks every project's `targets.*.inputs` and `namedInputs.*`, rewriting stale references.
function updateProjectConfigsInputs(tree, format) {
for (const [project, projectConfig] of (0, devkit_1.getProjects)(tree)) {
let changed = false;
if (projectConfig.targets) {
for (const target of Object.values(projectConfig.targets)) {
if (!target.inputs)
continue;
const rewritten = rewriteLegacyInputs(target.inputs, format);
if (!inputsEqual(target.inputs, rewritten)) {
target.inputs = rewritten;
changed = true;
}
}
}
if (projectConfig.namedInputs) {
for (const [name, inputs] of Object.entries(projectConfig.namedInputs)) {
const rewritten = rewriteLegacyInputs(inputs, format);
if (!inputsEqual(inputs, rewritten)) {
projectConfig.namedInputs[name] = rewritten;
changed = true;
}
}
}
if (changed) {
(0, devkit_1.updateProjectConfiguration)(tree, project, projectConfig);
}
}
}
function inputsEqual(a, b) {
return a.length === b.length && a.every((entry, i) => entry === b[i]);
}
function convertConfigToFlatConfig(tree, root, source, target, format, ignorePath) {
const ignorePaths = ignorePath
? [ignorePath, `${root}/.eslintignore`]
: [`${root}/.eslintignore`];
// `.eslintrc` (no extension) is JSON by convention.
if (source.endsWith('.json') || (0, path_1.basename)(source) === '.eslintrc') {
const config = (0, devkit_1.readJson)(tree, `${root}/${source}`);
const conversionResult = (0, json_converter_1.convertEslintJsonToFlatConfig)(tree, root, config, ignorePaths, format);
return processConvertedConfig(tree, root, source, target, conversionResult);
}
if (source.endsWith('.yaml') || source.endsWith('.yml')) {
const originalContent = tree.read(`${root}/${source}`, 'utf-8');
const { load } = require('@zkochan/js-yaml');
const config = load(originalContent, {
json: true,
filename: source,
});
const conversionResult = (0, json_converter_1.convertEslintJsonToFlatConfig)(tree, root, config, ignorePaths, format);
return processConvertedConfig(tree, root, source, target, conversionResult);
}
}
function processConvertedConfig(tree, root, source, target, { content, addESLintRC, addESLintJS, }) {
// remove original config file
tree.delete((0, path_1.join)(root, source));
// save new
tree.write((0, path_1.join)(root, target), content);
// These dependencies are required for flat configs that are generated by subsequent app/lib generators.
const devDependencies = {
eslint: versions_1.eslint9__eslintVersion,
'eslint-config-prettier': versions_1.eslintConfigPrettierVersion,
'typescript-eslint': versions_1.eslint9__typescriptESLintVersion,
'@typescript-eslint/eslint-plugin': versions_1.eslint9__typescriptESLintVersion,
'@typescript-eslint/parser': versions_1.eslint9__typescriptESLintVersion,
};
if ((0, devkit_1.getDependencyVersionFromPackageJson)(tree, '@typescript-eslint/utils')) {
devDependencies['@typescript-eslint/utils'] =
versions_1.eslint9__typescriptESLintVersion;
}
if ((0, devkit_1.getDependencyVersionFromPackageJson)(tree, '@typescript-eslint/type-utils')) {
devDependencies['@typescript-eslint/type-utils'] =
versions_1.eslint9__typescriptESLintVersion;
}
// add missing packages
if (addESLintRC) {
devDependencies['@eslint/eslintrc'] = versions_1.eslintrcVersion;
}
if (addESLintJS) {
devDependencies['@eslint/js'] = versions_1.eslintVersion;
}
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, devDependencies);
}

View File

@@ -0,0 +1,5 @@
export interface ConvertToFlatConfigGeneratorSchema {
skipFormat?: boolean;
// Internal option
eslintConfigFormat?: 'mjs' | 'cjs';
}

View File

@@ -0,0 +1,24 @@
{
"$schema": "https://json-schema.org/schema",
"$id": "ConvertToFlatConfig",
"cli": "nx",
"description": "Convert an Nx workspace's ESLint configs to use Flat Config.",
"type": "object",
"properties": {
"skipFormat": {
"type": "boolean",
"description": "Skip formatting files.",
"default": false,
"x-priority": "internal"
},
"eslintConfigFormat": {
"type": "string",
"description": "The format of the generated ESLint flat config files.",
"enum": ["mjs", "cjs"],
"default": "mjs",
"x-priority": "internal"
}
},
"additionalProperties": false,
"required": []
}

View File

@@ -0,0 +1,8 @@
import { type Tree } from '@nx/devkit';
interface Schema {
project?: string;
skipFormat?: boolean;
}
export declare function convertToInferred(tree: Tree, options: Schema): Promise<void>;
export default convertToInferred;
//# sourceMappingURL=convert-to-inferred.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"convert-to-inferred.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/convert-to-inferred/convert-to-inferred.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,IAAI,EACV,MAAM,YAAY,CAAC;AAYpB,UAAU,MAAM;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,iBA4BlE;AAyJD,eAAe,iBAAiB,CAAC"}

View File

@@ -0,0 +1,153 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertToInferred = convertToInferred;
const devkit_1 = require("@nx/devkit");
const executor_to_plugin_migrator_1 = require("@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator");
const plugin_migration_utils_1 = require("@nx/devkit/src/generators/plugin-migrations/plugin-migration-utils");
const posix_1 = require("node:path/posix");
const utils_1 = require("nx/src/tasks-runner/utils");
const plugin_1 = require("../../plugins/plugin");
const config_file_1 = require("../../utils/config-file");
const target_options_map_1 = require("./lib/target-options-map");
async function convertToInferred(tree, options) {
const projectGraph = await (0, devkit_1.createProjectGraphAsync)();
const migratedProjects = await (0, executor_to_plugin_migrator_1.migrateProjectExecutorsToPlugin)(tree, projectGraph, '@nx/eslint/plugin', plugin_1.createNodesV2, { targetName: 'lint' }, [
{
executors: ['@nx/eslint:lint', '@nrwl/linter:eslint'],
postTargetTransformer,
targetPluginOptionMapper: (targetName) => ({ targetName }),
skipTargetFilter,
},
], options.project);
if (migratedProjects.size === 0) {
throw new executor_to_plugin_migrator_1.NoTargetsToMigrateError();
}
if (!options.skipFormat) {
await (0, devkit_1.formatFiles)(tree);
}
}
function postTargetTransformer(target, tree, projectDetails, inferredTargetConfiguration) {
if (target.inputs) {
const normalizeInput = (input) => {
return input
.replace('{workspaceRoot}', '')
.replace('{projectRoot}', projectDetails.root)
.replace('{projectName}', projectDetails.projectName)
.replace(/^\//, '');
};
const inputs = target.inputs.filter((input) => {
if (typeof input === 'string') {
// if the input is a string, check if it is inferred by the plugin
// if it is, filter it out
return !inferredTargetConfiguration.inputs.some((inferredInput) => typeof inferredInput === 'string' &&
normalizeInput(inferredInput) === normalizeInput(input));
}
else if ('externalDependencies' in input) {
// if the input is an object with an externalDependencies property,
// check if all the external dependencies are inferred by the plugin
// if they are, filter it out
return !input.externalDependencies.every((externalDependency) => inferredTargetConfiguration.inputs.some((inferredInput) => typeof inferredInput === 'object' &&
'externalDependencies' in inferredInput &&
inferredInput.externalDependencies.includes(externalDependency)));
}
});
if (inputs.length === 0) {
delete target.inputs;
}
}
if (target.options) {
handlePropertiesInOptions(target.options, projectDetails, target);
}
if (target.configurations) {
for (const configurationName in target.configurations) {
const configuration = target.configurations[configurationName];
handlePropertiesInOptions(configuration, projectDetails, target);
}
if (Object.keys(target.configurations).length !== 0) {
for (const configuration in target.configurations) {
if (Object.keys(target.configurations[configuration]).length === 0) {
delete target.configurations[configuration];
}
}
if (Object.keys(target.configurations).length === 0) {
delete target.configurations;
}
}
}
if (target.outputs) {
(0, plugin_migration_utils_1.processTargetOutputs)(target, [], inferredTargetConfiguration, {
projectName: projectDetails.projectName,
projectRoot: projectDetails.root,
});
}
return target;
}
function handlePropertiesInOptions(options, projectDetails, target) {
// inferred targets are only identified after known files that ESLint would
// pick up, so we can remove the eslintConfig option
delete options.eslintConfig;
if ('force' in options) {
delete options.force;
}
if ('silent' in options) {
delete options.silent;
}
if ('hasTypeAwareRules' in options) {
delete options.hasTypeAwareRules;
}
if ('errorOnUnmatchedPattern' in options) {
if (!options.errorOnUnmatchedPattern) {
options['no-error-on-unmatched-pattern'] = true;
}
delete options.errorOnUnmatchedPattern;
}
if ('outputFile' in options) {
target.outputs ??= [];
target.outputs.push(options.outputFile);
}
for (const key in target_options_map_1.targetOptionsToCliMap) {
if (options[key]) {
const prevValue = options[key];
delete options[key];
options[target_options_map_1.targetOptionsToCliMap[key]] = prevValue;
}
}
if ('lintFilePatterns' in options) {
const normalizedLintFilePatterns = options.lintFilePatterns.map((pattern) => {
const interpolatedPattern = (0, utils_1.interpolate)(pattern, {
workspaceRoot: '',
projectRoot: projectDetails.root,
projectName: projectDetails.projectName,
});
if (interpolatedPattern === projectDetails.root) {
return '.';
}
return interpolatedPattern.replace(new RegExp(`^(?:\./)?${projectDetails.root}/`), '');
});
options.args = normalizedLintFilePatterns
// the @nx/eslint/plugin automatically infers these, so we don't need to pass them in
.filter((p) => projectDetails.root === '.'
? !['.', 'src', './src', 'lib', './lib'].includes(p)
: p !== '.');
if (options.args.length === 0) {
delete options.args;
}
delete options.lintFilePatterns;
}
}
exports.default = convertToInferred;
function skipTargetFilter(targetOptions, project) {
if (targetOptions.eslintConfig) {
// check that the eslintConfig option is a default config file known by ESLint
if (!config_file_1.ESLINT_CONFIG_FILENAMES.includes((0, posix_1.basename)(targetOptions.eslintConfig))) {
return `The "eslintConfig" option value (${targetOptions.eslintConfig}) is not a default config file known by ESLint.`;
}
// check that it is at the project root or in a parent directory
const eslintConfigPath = (0, posix_1.relative)(project.root, targetOptions.eslintConfig);
if ((0, posix_1.dirname)(eslintConfigPath) !== '.' &&
!eslintConfigPath.startsWith('../')) {
return `The "eslintConfig" option value (${targetOptions.eslintConfig}) must point to a file in the project root or a parent directory.`;
}
}
return false;
}

View File

@@ -0,0 +1,17 @@
export declare const targetOptionsToCliMap: {
fix: string;
format: string;
cache: string;
cacheLocation: string;
cacheStrategy: string;
noEslintrc: string;
outputFile: string;
maxWarnings: string;
quiet: string;
ignorePath: string;
rulesdir: string;
resolvePluginsRelativeTo: string;
reportUnusedDisableDirectives: string;
printConfig: string;
};
//# sourceMappingURL=target-options-map.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"target-options-map.d.ts","sourceRoot":"","sources":["../../../../../../../packages/eslint/src/generators/convert-to-inferred/lib/target-options-map.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;CAejC,CAAC"}

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.targetOptionsToCliMap = void 0;
exports.targetOptionsToCliMap = {
fix: 'fix',
format: 'format',
cache: 'cache',
cacheLocation: 'cache-location',
cacheStrategy: 'cache-strategy',
noEslintrc: 'no-eslintrc',
outputFile: 'output-file',
maxWarnings: 'max-warnings',
quiet: 'quiet',
ignorePath: 'ignore-path',
rulesdir: 'rulesdir',
resolvePluginsRelativeTo: 'resolve-plugins-relative-to',
reportUnusedDisableDirectives: 'report-unused-disable-directives',
printConfig: 'print-config',
};

View File

@@ -0,0 +1,19 @@
{
"$schema": "https://json-schema.org/schema",
"$id": "NxEslintConvertToInferred",
"description": "Convert existing Eslint project(s) using `@nx/eslint:lint` executor to use `@nx/eslint/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
"title": "Convert Eslint project from executor to plugin",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "The project to convert from using the `@nx/eslint:lint` executor to use `@nx/eslint/plugin`.",
"x-priority": "important"
},
"skipFormat": {
"type": "boolean",
"description": "Whether to format files at the end of the migration.",
"default": false
}
}
}

View File

@@ -0,0 +1,30 @@
import { Linter } from 'eslint';
/**
* This configuration is intended to apply to all TypeScript source files.
* See the eslint-plugin package for what is in the referenced shareable config.
*/
export declare const typeScriptOverride: {
files: string[];
extends: string[];
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
*/
rules: {};
};
/**
* This configuration is intended to apply to all JavaScript source files.
* See the eslint-plugin package for what is in the referenced shareable config.
*/
export declare const javaScriptOverride: {
files: string[];
extends: string[];
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
*/
rules: {};
};
export declare const getGlobalEsLintConfiguration: (unitTestRunner?: string, rootProject?: boolean) => Linter.LegacyConfig;
export declare const getGlobalFlatEslintConfiguration: (format: "cjs" | "mjs", rootProject?: boolean) => string;
//# sourceMappingURL=global-eslint-config.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"global-eslint-config.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/init/global-eslint-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAUhC;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;IAG7B;;;OAGG;;CAEJ,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;IAG7B;;;OAGG;;CAEJ,CAAC;AAgCF,eAAO,MAAM,4BAA4B,GACvC,iBAAiB,MAAM,EACvB,cAAc,OAAO,KACpB,MAAM,CAAC,YAoBT,CAAC;AAEF,eAAO,MAAM,gCAAgC,GAC3C,QAAQ,KAAK,GAAG,KAAK,EACrB,cAAc,OAAO,KACpB,MA8EF,CAAC"}

View File

@@ -0,0 +1,127 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getGlobalFlatEslintConfiguration = exports.getGlobalEsLintConfiguration = exports.javaScriptOverride = exports.typeScriptOverride = void 0;
const ast_utils_1 = require("../utils/flat-config/ast-utils");
/**
* This configuration is intended to apply to all TypeScript source files.
* See the eslint-plugin package for what is in the referenced shareable config.
*/
exports.typeScriptOverride = {
files: ['*.ts', '*.tsx'],
extends: ['plugin:@nx/typescript'],
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
*/
rules: {},
};
/**
* This configuration is intended to apply to all JavaScript source files.
* See the eslint-plugin package for what is in the referenced shareable config.
*/
exports.javaScriptOverride = {
files: ['*.js', '*.jsx'],
extends: ['plugin:@nx/javascript'],
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
*/
rules: {},
};
/**
* This configuration is intended to apply to all "source code" (but not
* markup like HTML, or other custom file types like GraphQL)
*/
const moduleBoundariesOverride = {
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
rules: {
'@nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] }],
},
],
},
};
/**
* This configuration is intended to apply to all "source code" (but not
* markup like HTML, or other custom file types like GraphQL)
*/
const jestOverride = {
files: ['*.spec.ts', '*.spec.tsx', '*.spec.js', '*.spec.jsx'],
env: {
jest: true,
},
rules: {},
};
const getGlobalEsLintConfiguration = (unitTestRunner, rootProject) => {
const config = {
root: true,
ignorePatterns: rootProject ? ['!**/*'] : ['**/*'],
plugins: ['@nx'],
/**
* We leverage ESLint's "overrides" capability so that we can set up a root config which will support
* all permutations of Nx workspaces across all frameworks, libraries and tools.
*
* The key point is that we need entirely different ESLint config to apply to different types of files,
* but we still want to share common config where possible.
*/
overrides: [
...(rootProject ? [] : [moduleBoundariesOverride]),
exports.typeScriptOverride,
exports.javaScriptOverride,
...(unitTestRunner === 'jest' ? [jestOverride] : []),
],
};
return config;
};
exports.getGlobalEsLintConfiguration = getGlobalEsLintConfiguration;
const getGlobalFlatEslintConfiguration = (format, rootProject) => {
const nodeList = (0, ast_utils_1.createNodeList)(new Map(), [], format);
let content = (0, ast_utils_1.stringifyNodeList)(nodeList);
content = (0, ast_utils_1.addImportToFlatConfig)(content, 'nx', '@nx/eslint-plugin');
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/base'), { insertAtTheEnd: false });
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/typescript'));
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)('flat/javascript'));
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
ignores: ['**/dist', '**/out-tsc'],
}, format));
if (!rootProject) {
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
rules: {
'@nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [
// This allows a root project to be present without causing lint errors
// since all projects will depend on this base file.
'^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$',
],
depConstraints: [
{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] },
],
},
],
},
}, format));
}
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatOverride)({
files: [
'**/*.ts',
'**/*.tsx',
'**/*.cts',
'**/*.mts',
'**/*.js',
'**/*.jsx',
'**/*.cjs',
'**/*.mjs',
],
rules: {},
}, format));
return content;
};
exports.getGlobalFlatEslintConfiguration = getGlobalFlatEslintConfiguration;

View File

@@ -0,0 +1,4 @@
import { GeneratorCallback, ProjectConfiguration, TargetConfiguration, Tree } from '@nx/devkit';
export declare function migrateConfigToMonorepoStyle(projects: ProjectConfiguration[], tree: Tree, unitTestRunner: string, eslintConfigFormat: 'mjs' | 'cjs', keepExistingVersions?: boolean): GeneratorCallback;
export declare function findLintTarget(project: ProjectConfiguration): TargetConfiguration;
//# sourceMappingURL=init-migration.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"init-migration.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/init/init-migration.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,iBAAiB,EAGjB,oBAAoB,EACpB,mBAAmB,EACnB,IAAI,EAGL,MAAM,YAAY,CAAC;AAwBpB,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,oBAAoB,EAAE,EAChC,IAAI,EAAE,IAAI,EACV,cAAc,EAAE,MAAM,EACtB,kBAAkB,EAAE,KAAK,GAAG,KAAK,EACjC,oBAAoB,CAAC,EAAE,OAAO,GAC7B,iBAAiB,CA+FnB;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,oBAAoB,GAC5B,mBAAmB,CAMrB"}

View File

@@ -0,0 +1,153 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.migrateConfigToMonorepoStyle = migrateConfigToMonorepoStyle;
exports.findLintTarget = findLintTarget;
const devkit_1 = require("@nx/devkit");
const path_1 = require("path");
const eslint_file_1 = require("../utils/eslint-file");
const global_eslint_config_1 = require("./global-eslint-config");
const flat_config_1 = require("../../utils/flat-config");
const versions_1 = require("../../utils/versions");
const ast_utils_1 = require("../utils/flat-config/ast-utils");
const plugin_1 = require("../utils/plugin");
const config_file_1 = require("../../utils/config-file");
function migrateConfigToMonorepoStyle(projects, tree, unitTestRunner, eslintConfigFormat, keepExistingVersions) {
const rootEslintConfig = (0, eslint_file_1.findEslintFile)(tree);
let skipCleanup = false;
if (rootEslintConfig) {
// We do not want to mix the formats
const fileExtension = (0, path_1.extname)(rootEslintConfig);
if (fileExtension === '.mjs' || fileExtension === '.cjs') {
eslintConfigFormat = fileExtension.slice(1);
}
else {
eslintConfigFormat = (0, eslint_file_1.determineEslintConfigFormat)(tree.read(rootEslintConfig, 'utf-8'));
}
}
if (rootEslintConfig?.match(/\.base\./) &&
!projects.some((p) => p.root === '.')) {
// if the migration has been run already, we need to rename the base config
// and only update the extends paths
tree.rename(rootEslintConfig, rootEslintConfig.replace('.base.', '.'));
skipCleanup = true;
}
else {
if ((0, flat_config_1.useFlatConfig)(tree)) {
// we need this for the compat
(0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
'@eslint/js': versions_1.eslintVersion,
}, undefined, keepExistingVersions);
tree.write(tree.exists(`eslint.config.${eslintConfigFormat}`)
? `eslint.base.config.${eslintConfigFormat}`
: `eslint.config.${eslintConfigFormat}`, (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)(eslintConfigFormat));
}
else {
const eslintFile = (0, eslint_file_1.findEslintFile)(tree, '.');
(0, devkit_1.writeJson)(tree, eslintFile ? '.eslintrc.base.json' : '.eslintrc.json', (0, global_eslint_config_1.getGlobalEsLintConfiguration)(unitTestRunner));
}
}
// update extends in all projects' eslint configs
projects.forEach((project) => {
let eslintFile;
const lintTarget = findLintTarget(project);
if (lintTarget) {
// If target is configured in project.json, read file from target options.
eslintFile =
lintTarget.options?.eslintConfig || (0, eslint_file_1.findEslintFile)(tree, project.root);
}
else if ((0, plugin_1.hasEslintPlugin)(tree)) {
// Otherwise, if `@nx/eslint/plugin` is used, match any of the known config files.
for (const f of config_file_1.ESLINT_CONFIG_FILENAMES) {
if (tree.exists((0, devkit_1.joinPathFragments)(project.root, f))) {
eslintFile = f;
break;
}
}
}
if (eslintFile) {
const projectEslintPath = (0, devkit_1.joinPathFragments)(project.root, eslintFile);
if (skipCleanup) {
const content = tree.read(projectEslintPath, 'utf-8');
tree.write(projectEslintPath, content.replace(rootEslintConfig, rootEslintConfig.replace('.base.', '.')));
}
else {
migrateEslintFile(projectEslintPath, tree);
}
}
});
return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
'@nx/eslint-plugin': versions_1.nxVersion,
});
}
function findLintTarget(project) {
return Object.values(project.targets ?? {}).find((target) => target.executor === '@nx/eslint:lint' ||
target.executor === '@nx/linter:eslint');
}
function migrateEslintFile(projectEslintPath, tree) {
const baseFile = (0, eslint_file_1.findEslintFile)(tree);
if ((0, eslint_file_1.isEslintConfigSupported)(tree)) {
if ((0, flat_config_1.useFlatConfig)(tree)) {
let config = tree.read(projectEslintPath, 'utf-8');
// remove @nx plugin
config = (0, ast_utils_1.removePlugin)(config, '@nx', '@nx/eslint-plugin-nx');
// if base config is cjs, we will need to import it using async import
config = (0, ast_utils_1.addImportToFlatConfig)(config, 'baseConfig', `${(0, devkit_1.offsetFromRoot)((0, path_1.dirname)(projectEslintPath))}${baseFile}`);
config = (0, ast_utils_1.addBlockToFlatConfigExport)(config, (0, ast_utils_1.generateSpreadElement)('baseConfig'), { insertAtTheEnd: false });
// cleanup file extends
config = (0, ast_utils_1.removeCompatExtends)(config, [
'plugin:@nx/typescript',
'plugin:@nx/javascript',
]);
config = (0, ast_utils_1.removePredefinedConfigs)(config, '@nx/eslint-plugin', 'nx', [
'flat/base',
'flat/typescript',
'flat/javascript',
]);
tree.write(projectEslintPath, config);
}
else {
(0, devkit_1.updateJson)(tree, projectEslintPath, (json) => {
// we have a new root now
delete json.root;
// remove nrwl/nx plugins
if (json.plugins) {
json.plugins = json.plugins.filter((p) => p !== '@nx');
if (json.plugins.length === 0) {
delete json.plugins;
}
}
// add extends
json.extends = json.extends || [];
// ensure extends is an array
if (typeof json.extends === 'string') {
json.extends = [json.extends];
}
const pathToRootConfig = `${(0, devkit_1.offsetFromRoot)((0, path_1.dirname)(projectEslintPath))}${baseFile}`;
if (json.extends.indexOf(pathToRootConfig) === -1) {
json.extends.push(pathToRootConfig);
}
// cleanup overrides
if (json.overrides) {
json.overrides.forEach((override) => {
if (override.extends) {
override.extends = override.extends.filter((ext) => ext !== 'plugin:@nx/typescript' &&
ext !== 'plugin:@nx/javascript');
if (override.extends.length === 0) {
delete override.extends;
}
}
});
}
return json;
});
}
return;
}
if (projectEslintPath.endsWith('.yml') ||
projectEslintPath.endsWith('.yaml')) {
console.warn('YAML eslint config is not supported yet for migration');
}
if (projectEslintPath.endsWith('.js') || projectEslintPath.endsWith('.cjs')) {
console.warn('JS eslint config is not supported yet for migration');
}
}

11
node_modules/@nx/eslint/src/generators/init/init.d.ts generated vendored Normal file
View File

@@ -0,0 +1,11 @@
import { GeneratorCallback, Tree } from '@nx/devkit';
export interface LinterInitOptions {
skipPackageJson?: boolean;
keepExistingVersions?: boolean;
updatePackageScripts?: boolean;
addPlugin?: boolean;
eslintConfigFormat?: 'mjs' | 'cjs';
}
export declare function initEsLint(tree: Tree, options: LinterInitOptions): Promise<GeneratorCallback>;
export declare function lintInitGenerator(tree: Tree, options: LinterInitOptions): Promise<GeneratorCallback>;
//# sourceMappingURL=init.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/init/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,iBAAiB,EAIjB,IAAI,EAGL,MAAM,YAAY,CAAC;AAWpB,MAAM,WAAW,iBAAiB;IAChC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,kBAAkB,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;CACpC;AA+CD,wBAAsB,UAAU,CAC9B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAwF5B;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,iBAAiB,8BAG3B"}

108
node_modules/@nx/eslint/src/generators/init/init.js generated vendored Normal file
View File

@@ -0,0 +1,108 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.initEsLint = initEsLint;
exports.lintInitGenerator = lintInitGenerator;
const devkit_1 = require("@nx/devkit");
const add_plugin_1 = require("@nx/devkit/src/utils/add-plugin");
const versions_1 = require("../../utils/versions");
const eslint_file_1 = require("../utils/eslint-file");
const plugin_1 = require("../../plugins/plugin");
const plugin_2 = require("../utils/plugin");
const path_1 = require("path");
function updateProductionFileset(tree, format = 'mjs') {
const nxJson = (0, devkit_1.readNxJson)(tree);
const productionFileSet = nxJson.namedInputs?.production;
if (productionFileSet) {
productionFileSet.push('!{projectRoot}/.eslintrc.json');
productionFileSet.push(`!{projectRoot}/eslint.config.${format}`);
// Dedupe and set
nxJson.namedInputs.production = Array.from(new Set(productionFileSet));
}
(0, devkit_1.updateNxJson)(tree, nxJson);
}
function addTargetDefaults(tree, format) {
const nxJson = (0, devkit_1.readNxJson)(tree);
nxJson.targetDefaults ??= {};
nxJson.targetDefaults['@nx/eslint:lint'] ??= {};
nxJson.targetDefaults['@nx/eslint:lint'].cache ??= true;
nxJson.targetDefaults['@nx/eslint:lint'].inputs ??= [
'default',
'^default',
`{workspaceRoot}/.eslintrc.json`,
`{workspaceRoot}/.eslintignore`,
`{workspaceRoot}/eslint.config.${format}`,
'{workspaceRoot}/tools/eslint-rules/**/*',
];
(0, devkit_1.updateNxJson)(tree, nxJson);
}
function updateVsCodeRecommendedExtensions(host) {
if (!host.exists('.vscode/extensions.json')) {
return;
}
(0, devkit_1.updateJson)(host, '.vscode/extensions.json', (json) => {
json.recommendations = json.recommendations || [];
const extension = 'dbaeumer.vscode-eslint';
if (!json.recommendations.includes(extension)) {
json.recommendations.push(extension);
}
return json;
});
}
async function initEsLint(tree, options) {
const nxJson = (0, devkit_1.readNxJson)(tree);
const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
nxJson.useInferencePlugins !== false;
options.addPlugin ??= addPluginDefault;
options.eslintConfigFormat ??= 'mjs';
const hasPlugin = (0, plugin_2.hasEslintPlugin)(tree);
const rootEslintFile = (0, eslint_file_1.findEslintFile)(tree);
if (rootEslintFile) {
const fileExtension = (0, path_1.extname)(rootEslintFile);
if (fileExtension === '.mjs' || fileExtension === '.cjs') {
options.eslintConfigFormat = fileExtension.slice(1);
}
else {
options.eslintConfigFormat = (0, eslint_file_1.determineEslintConfigFormat)(tree.read(rootEslintFile, 'utf-8'));
}
}
const graph = await (0, devkit_1.createProjectGraphAsync)();
const lintTargetNames = [
'lint',
'eslint:lint',
'eslint-lint',
'_lint',
'_eslint:lint',
'_eslint-lint',
];
if (rootEslintFile && options.addPlugin && !hasPlugin) {
await (0, add_plugin_1.addPlugin)(tree, graph, '@nx/eslint/plugin', plugin_1.createNodesV2, {
targetName: lintTargetNames,
}, options.updatePackageScripts);
return () => { };
}
if (rootEslintFile) {
return () => { };
}
updateProductionFileset(tree, options.eslintConfigFormat);
updateVsCodeRecommendedExtensions(tree);
if (options.addPlugin) {
await (0, add_plugin_1.addPlugin)(tree, graph, '@nx/eslint/plugin', plugin_1.createNodesV2, {
targetName: lintTargetNames,
}, options.updatePackageScripts);
}
else {
addTargetDefaults(tree, options.eslintConfigFormat);
}
const tasks = [];
if (!options.skipPackageJson) {
tasks.push((0, devkit_1.removeDependenciesFromPackageJson)(tree, ['@nx/eslint'], []));
tasks.push((0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
'@nx/eslint': versions_1.nxVersion,
eslint: versions_1.eslintVersion,
}, undefined, options.keepExistingVersions));
}
return (0, devkit_1.runTasksInSerial)(...tasks);
}
async function lintInitGenerator(tree, options) {
return await initEsLint(tree, { addPlugin: false, ...options });
}

View File

@@ -0,0 +1,28 @@
{
"$schema": "https://json-schema.org/schema",
"cli": "nx",
"$id": "NxESLintInit",
"title": "Initialize ESLint Plugin",
"description": "Set up the ESLint plugin.",
"type": "object",
"properties": {
"skipPackageJson": {
"description": "Do not add dependencies to `package.json`.",
"type": "boolean",
"default": false
},
"keepExistingVersions": {
"type": "boolean",
"x-priority": "internal",
"description": "Keep existing dependencies versions",
"default": false
},
"updatePackageScripts": {
"type": "boolean",
"x-priority": "internal",
"description": "Update `package.json` scripts with inferred targets",
"default": false
}
},
"required": []
}

View File

@@ -0,0 +1,25 @@
import { GeneratorCallback, Tree } from '@nx/devkit';
import { Linter as LinterEnum, LinterType } from '../utils/linter';
interface LintProjectOptions {
project: string;
linter?: LinterEnum | LinterType;
eslintFilePatterns?: string[];
tsConfigPaths?: string[];
skipFormat: boolean;
setParserOptionsProject?: boolean;
skipPackageJson?: boolean;
unitTestRunner?: string;
rootProject?: boolean;
keepExistingVersions?: boolean;
addPlugin?: boolean;
eslintConfigFormat?: 'mjs' | 'cjs';
/**
* @internal
*/
addExplicitTargets?: boolean;
addPackageJsonDependencyChecks?: boolean;
}
export declare function lintProjectGenerator(tree: Tree, options: LintProjectOptions): Promise<GeneratorCallback>;
export declare function lintProjectGeneratorInternal(tree: Tree, options: LintProjectOptions): Promise<GeneratorCallback>;
export {};
//# sourceMappingURL=lint-project.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"lint-project.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/lint-project/lint-project.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,iBAAiB,EAQjB,IAAI,EAIL,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA0BnE,UAAU,kBAAkB;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kBAAkB,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAEnC;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAC1C;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,kBAAkB,8BAE3E;AAED,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,kBAAkB,8BA4I5B"}

View File

@@ -0,0 +1,289 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lintProjectGenerator = lintProjectGenerator;
exports.lintProjectGeneratorInternal = lintProjectGeneratorInternal;
const devkit_1 = require("@nx/devkit");
const eslint_file_1 = require("../utils/eslint-file");
const path_1 = require("path");
const init_1 = require("../init/init");
const init_migration_1 = require("../init/init-migration");
const project_configuration_1 = require("nx/src/generators/utils/project-configuration");
const flat_config_1 = require("../../utils/flat-config");
const ast_utils_1 = require("../utils/flat-config/ast-utils");
const config_file_1 = require("../../utils/config-file");
const plugin_1 = require("../utils/plugin");
const versions_1 = require("../../utils/versions");
const setup_root_eslint_1 = require("./setup-root-eslint");
const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup");
function lintProjectGenerator(tree, options) {
return lintProjectGeneratorInternal(tree, { addPlugin: false, ...options });
}
async function lintProjectGeneratorInternal(tree, options) {
const nxJson = (0, devkit_1.readNxJson)(tree);
options.eslintConfigFormat ??= 'mjs';
const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&
nxJson.useInferencePlugins !== false;
options.addPlugin ??= addPluginDefault;
const tasks = [];
const initTask = await (0, init_1.lintInitGenerator)(tree, {
skipPackageJson: options.skipPackageJson,
addPlugin: options.addPlugin,
eslintConfigFormat: options.eslintConfigFormat,
});
tasks.push(initTask);
const rootEsLintTask = (0, setup_root_eslint_1.setupRootEsLint)(tree, {
unitTestRunner: options.unitTestRunner,
skipPackageJson: options.skipPackageJson,
rootProject: options.rootProject,
eslintConfigFormat: options.eslintConfigFormat,
});
tasks.push(rootEsLintTask);
const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, options.project);
let lintFilePatterns = options.eslintFilePatterns;
if (!lintFilePatterns && options.rootProject && projectConfig.root === '.') {
lintFilePatterns = ['./src'];
}
if (lintFilePatterns &&
lintFilePatterns.length &&
!lintFilePatterns.includes('{projectRoot}') &&
isBuildableLibraryProject(tree, projectConfig)) {
lintFilePatterns.push(`{projectRoot}/package.json`);
}
const hasPlugin = (0, plugin_1.hasEslintPlugin)(tree);
if (hasPlugin && !options.addExplicitTargets) {
if (lintFilePatterns &&
lintFilePatterns.length &&
lintFilePatterns.some((p) => !['./src', '{projectRoot}', projectConfig.root].includes(p))) {
projectConfig.targets ??= {};
projectConfig.targets['lint'] = {
command: `eslint ${lintFilePatterns
.join(' ')
.replace('{projectRoot}', projectConfig.root)}`,
};
}
}
else {
projectConfig.targets ??= {};
projectConfig.targets['lint'] = {
executor: '@nx/eslint:lint',
};
if (lintFilePatterns && lintFilePatterns.length) {
// only add lintFilePatterns if they are explicitly defined
projectConfig.targets['lint'].options = {
lintFilePatterns,
};
}
}
// we are adding new project which is not the root project or
// companion e2e app so we should check if migration to
// monorepo style is needed
if (!options.rootProject) {
const projectsFromTree = (0, project_configuration_1.getProjects)(tree);
if (await isMigrationToMonorepoNeeded(tree, projectsFromTree)) {
// we only migrate project configurations that have been created
const filteredProjects = [];
projectsFromTree.forEach((project, name) => {
if (name !== options.project) {
filteredProjects.push(project);
}
});
const migrateTask = (0, init_migration_1.migrateConfigToMonorepoStyle)(filteredProjects, tree, options.unitTestRunner, options.eslintConfigFormat, options.keepExistingVersions);
tasks.push(migrateTask);
}
}
// our root `.eslintrc` is already the project config, so we should not override it
// additionally, the companion e2e app would have `rootProject: true`
// so we need to check for the root path as well
if (!options.rootProject || projectConfig.root !== '.') {
const addDependencyChecks = options.addPackageJsonDependencyChecks ||
isBuildableLibraryProject(tree, projectConfig);
createEsLintConfiguration(tree, options, projectConfig, options.setParserOptionsProject, options.rootProject, addDependencyChecks);
if (addDependencyChecks) {
tasks.push((0, devkit_1.addDependenciesToPackageJson)(tree, {}, { 'jsonc-eslint-parser': versions_1.jsoncEslintParserVersion }, undefined, true));
}
}
// Buildable libs need source analysis enabled for linting `package.json`.
if (isBuildableLibraryProject(tree, projectConfig) &&
!isJsAnalyzeSourceFilesEnabled(tree)) {
(0, devkit_1.updateJson)(tree, 'nx.json', (json) => {
json.pluginsConfig ??= {};
json.pluginsConfig['@nx/js'] ??= {};
json.pluginsConfig['@nx/js'].analyzeSourceFiles = true;
return json;
});
}
(0, devkit_1.updateProjectConfiguration)(tree, options.project, projectConfig);
if (!options.skipFormat) {
await (0, devkit_1.formatFiles)(tree);
}
return (0, devkit_1.runTasksInSerial)(...tasks);
}
function createEsLintConfiguration(tree, options, projectConfig, setParserOptionsProject, rootProject, addDependencyChecks) {
// we are only extending root for non-standalone projects or their complementary e2e apps
const extendedRootConfig = rootProject ? undefined : (0, eslint_file_1.findEslintFile)(tree);
const pathToRootConfig = extendedRootConfig
? `${(0, devkit_1.offsetFromRoot)(projectConfig.root)}${extendedRootConfig}`
: 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 = (0, path_1.extname)(extendedRootConfig);
if (fileExtension === '.mjs' || fileExtension === '.cjs') {
options.eslintConfigFormat = fileExtension.slice(1);
}
else {
options.eslintConfigFormat = (0, eslint_file_1.determineEslintConfigFormat)(tree.read(extendedRootConfig, 'utf-8'));
}
}
const overrides = (0, flat_config_1.useFlatConfig)(tree)
? // For flat configs, keep generated overrides minimal; only add parserOptions when explicitly requested.
[
...(setParserOptionsProject
? [
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
languageOptions: {
parserOptions: {
project: [`${projectConfig.root}/tsconfig.*?.json`],
},
},
},
]
: []),
]
: [
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
/**
* NOTE: We no longer set parserOptions.project by default when creating new projects.
*
* We have observed that users rarely add rules requiring type-checking to their Nx workspaces, and therefore
* do not actually need the capabilites which parserOptions.project provides. When specifying parserOptions.project,
* typescript-eslint needs to create full TypeScript Programs for you. When omitting it, it can perform a simple
* parse (and AST tranformation) of the source files it encounters during a lint run, which is much faster and much
* less memory intensive.
*
* In the rare case that users attempt to add rules requiring type-checking to their setup later on (and haven't set
* parserOptions.project), the executor will attempt to look for the particular error typescript-eslint gives you
* and provide feedback to the user.
*/
parserOptions: !setParserOptionsProject
? undefined
: {
project: [`${projectConfig.root}/tsconfig.*?.json`],
},
/**
* Having an empty rules object present makes it more obvious to the user where they would
* extend things from if they needed to
*/
rules: {},
},
{
files: ['*.ts', '*.tsx'],
rules: {},
},
{
files: ['*.js', '*.jsx'],
rules: {},
},
];
if (addDependencyChecks) {
overrides.push({
files: ['*.json'],
parser: 'jsonc-eslint-parser',
rules: {
'@nx/dependency-checks': [
'error',
{
// With flat configs, we don't want to include imports in the eslint js/cjs/mjs/ts/cts/mts files to be checked
ignoredFiles: [
'{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}',
],
},
],
},
});
}
if ((0, flat_config_1.useFlatConfig)(tree)) {
const nodes = [];
const importMap = new Map();
if (extendedRootConfig) {
importMap.set(pathToRootConfig, 'baseConfig');
nodes.push((0, ast_utils_1.generateSpreadElement)('baseConfig'));
}
overrides.forEach((override) => {
nodes.push((0, ast_utils_1.generateFlatOverride)(override, options.eslintConfigFormat));
});
const nodeList = (0, ast_utils_1.createNodeList)(importMap, nodes, options.eslintConfigFormat);
const content = (0, ast_utils_1.stringifyNodeList)(nodeList);
tree.write((0, path_1.join)(projectConfig.root, `eslint.config.${options.eslintConfigFormat}`), content);
}
else {
(0, devkit_1.writeJson)(tree, (0, path_1.join)(projectConfig.root, `.eslintrc.json`), {
extends: extendedRootConfig ? [pathToRootConfig] : undefined,
// Include project files to be linted since the global one excludes all files.
ignorePatterns: ['!**/*'],
overrides,
});
}
}
function isJsAnalyzeSourceFilesEnabled(tree) {
const nxJson = (0, devkit_1.readJson)(tree, 'nx.json');
const jsPluginConfig = nxJson.pluginsConfig?.['@nx/js'];
return (jsPluginConfig?.analyzeSourceFiles ??
nxJson.extends !== 'nx/presets/npm.json');
}
function isBuildableLibraryProject(tree, projectConfig) {
return ((0, ts_solution_setup_1.getProjectType)(tree, projectConfig.root, projectConfig.projectType) ===
'library' &&
projectConfig.targets?.build &&
!!projectConfig.targets.build);
}
/**
* Detect based on the state of lint target configuration of the root project
* if we should migrate eslint configs to monorepo style.
*
* Checks the tree first so explicit root lint targets written earlier in the
* same generator run are visible; the project graph is only consulted to
* surface targets inferred by `@nx/eslint/plugin`, which don't appear on the
* tree.
*/
async function isMigrationToMonorepoNeeded(tree, projectsFromTree) {
// the base config is already created, migration has been done
if ([config_file_1.baseEsLintConfigFile, ...config_file_1.BASE_ESLINT_CONFIG_FILENAMES].some((f) => tree.exists(f))) {
return false;
}
for (const project of projectsFromTree.values()) {
if (project.root === '.') {
if (rootHasEslintLintTarget(project.targets)) {
return true;
}
break;
}
}
if (!(0, plugin_1.hasEslintPlugin)(tree)) {
return false;
}
const graph = await (0, devkit_1.createProjectGraphAsync)();
for (const node of Object.values(graph.nodes)) {
if (node.data.root === '.') {
return rootHasEslintLintTarget(node.data.targets);
}
}
return false;
}
function rootHasEslintLintTarget(targets) {
if (!targets) {
return false;
}
for (const targetConfig of Object.values(targets)) {
if (['@nx/eslint:lint', '@nx/linter:eslint'].includes(targetConfig.executor) ||
(targetConfig.executor === 'nx:run-commands' &&
targetConfig.options?.command &&
targetConfig.options?.command.startsWith('eslint '))) {
return true;
}
}
return false;
}

View File

@@ -0,0 +1,9 @@
import { type GeneratorCallback, type Tree } from '@nx/devkit';
export type SetupRootEsLintOptions = {
unitTestRunner?: string;
skipPackageJson?: boolean;
rootProject?: boolean;
eslintConfigFormat?: 'mjs' | 'cjs';
};
export declare function setupRootEsLint(tree: Tree, options: SetupRootEsLintOptions): GeneratorCallback;
//# sourceMappingURL=setup-root-eslint.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"setup-root-eslint.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/lint-project/setup-root-eslint.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,IAAI,EACV,MAAM,YAAY,CAAC;AAepB,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;CACpC,CAAC;AAEF,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,sBAAsB,GAC9B,iBAAiB,CAWnB"}

View File

@@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupRootEsLint = setupRootEsLint;
const devkit_1 = require("@nx/devkit");
const flat_config_1 = require("../../utils/flat-config");
const versions_1 = require("../../utils/versions");
const global_eslint_config_1 = require("../init/global-eslint-config");
const eslint_file_1 = require("../utils/eslint-file");
function setupRootEsLint(tree, options) {
const rootEslintFile = (0, eslint_file_1.findEslintFile)(tree);
if (rootEslintFile) {
return () => { };
}
options.eslintConfigFormat ??= 'mjs';
if (!(0, flat_config_1.useFlatConfig)(tree)) {
return setUpLegacyRootEslintRc(tree, options);
}
return setUpRootFlatConfig(tree, options);
}
function setUpLegacyRootEslintRc(tree, options) {
(0, devkit_1.writeJson)(tree, '.eslintrc.json', (0, global_eslint_config_1.getGlobalEsLintConfiguration)(options.unitTestRunner, options.rootProject));
if (tree.exists('.eslintignore')) {
let content = tree.read('.eslintignore', 'utf-8');
if (!/^node_modules$/gm.test(content)) {
content = `${content}\nnode_modules\n`;
tree.write('.eslintignore', content);
}
}
else {
tree.write('.eslintignore', 'node_modules\n');
}
return !options.skipPackageJson
? (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
'@nx/eslint-plugin': versions_1.nxVersion,
'@typescript-eslint/parser': versions_1.typescriptESLintVersion,
'@typescript-eslint/eslint-plugin': versions_1.typescriptESLintVersion,
'eslint-config-prettier': versions_1.eslintConfigPrettierVersion,
})
: () => { };
}
function setUpRootFlatConfig(tree, options) {
tree.write(`eslint.config.${options.eslintConfigFormat}`, (0, global_eslint_config_1.getGlobalFlatEslintConfiguration)(options.eslintConfigFormat, options.rootProject));
return !options.skipPackageJson
? (0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
'@eslint/js': versions_1.eslint9__eslintVersion,
'@nx/eslint-plugin': versions_1.nxVersion,
eslint: versions_1.eslint9__eslintVersion,
'eslint-config-prettier': versions_1.eslintConfigPrettierVersion,
'typescript-eslint': versions_1.eslint9__typescriptESLintVersion,
})
: () => { };
}

View File

@@ -0,0 +1,31 @@
import { type GeneratorCallback, type Tree } from '@nx/devkit';
import type { Linter } from 'eslint';
export declare function findEslintFile(tree: Tree, projectRoot?: string): string | null;
export declare function isEslintConfigSupported(tree: Tree, projectRoot?: string): boolean;
export declare function updateRelativePathsInConfig(tree: Tree, sourcePath: string, destinationPath: string): void;
export declare function determineEslintConfigFormat(content: string): 'mjs' | 'cjs';
export declare function addOverrideToLintConfig(tree: Tree, root: string, override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>, options?: {
insertAtTheEnd?: boolean;
checkBaseConfig?: boolean;
}): void;
export declare function updateOverrideInLintConfig(tree: Tree, rootOrFile: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, update: (override: Linter.ConfigOverride<Linter.RulesRecord>) => Linter.ConfigOverride<Linter.RulesRecord>): void;
export declare function lintConfigHasOverride(tree: Tree, rootOrFile: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, checkBaseConfig?: boolean): boolean;
export declare function replaceOverridesInLintConfig(tree: Tree, root: string, overrides: Linter.ConfigOverride<Linter.RulesRecord>[]): void;
export declare function addExtendsToLintConfig(tree: Tree, root: string, plugin: string | {
name: string;
needCompatFixup: boolean;
} | Array<string | {
name: string;
needCompatFixup: boolean;
}>, insertAtTheEnd?: boolean): GeneratorCallback;
export declare function addPredefinedConfigToFlatLintConfig(tree: Tree, root: string, predefinedConfigName: string, options?: {
moduleName?: string;
moduleImportPath?: string;
spread?: boolean;
insertAtTheEnd?: boolean;
checkBaseConfig?: boolean;
}): void;
export declare function addPluginsToLintConfig(tree: Tree, root: string, plugin: string | string[]): void;
export declare function addIgnoresToLintConfig(tree: Tree, root: string, ignorePatterns: string[]): void;
export declare function getPluginImport(pluginName: string): string;
//# sourceMappingURL=eslint-file.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"eslint-file.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/utils/eslint-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iBAAiB,EAKtB,KAAK,IAAI,EAEV,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAuCrC,wBAAgB,cAAc,CAC5B,IAAI,EAAE,IAAI,EACV,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,GAAG,IAAI,CAmBf;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,SAAK,GAAG,OAAO,CAW7E;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,QA6CxB;AAsED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,CAc1E;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,EAC5D,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAE7D,QAiDF;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,OAAO,EACxE,MAAM,EAAE,CACN,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,KAChD,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,QA4C/C;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,OAAO,EACxE,eAAe,UAAQ,GACtB,OAAO,CAyCT;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,QA8BvD;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,EACZ,MAAM,EACF,MAAM,GACN;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,OAAO,CAAA;CAAE,GAC1C,KAAK,CAAC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,EAC9D,cAAc,UAAQ,GACrB,iBAAiB,CA+GnB;AAED,wBAAgB,mCAAmC,CACjD,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,EACZ,oBAAoB,EAAE,MAAM,EAC5B,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CACtB,GACL,IAAI,CA6BN;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,QA+B1B;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,MAAM,EAAE,QAuCzB;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAY1D"}

View File

@@ -0,0 +1,476 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findEslintFile = findEslintFile;
exports.isEslintConfigSupported = isEslintConfigSupported;
exports.updateRelativePathsInConfig = updateRelativePathsInConfig;
exports.determineEslintConfigFormat = determineEslintConfigFormat;
exports.addOverrideToLintConfig = addOverrideToLintConfig;
exports.updateOverrideInLintConfig = updateOverrideInLintConfig;
exports.lintConfigHasOverride = lintConfigHasOverride;
exports.replaceOverridesInLintConfig = replaceOverridesInLintConfig;
exports.addExtendsToLintConfig = addExtendsToLintConfig;
exports.addPredefinedConfigToFlatLintConfig = addPredefinedConfigToFlatLintConfig;
exports.addPluginsToLintConfig = addPluginsToLintConfig;
exports.addIgnoresToLintConfig = addIgnoresToLintConfig;
exports.getPluginImport = getPluginImport;
const devkit_1 = require("@nx/devkit");
const semver_1 = require("semver");
const config_file_1 = require("../../utils/config-file");
const flat_config_1 = require("../../utils/flat-config");
const version_utils_1 = require("../../utils/version-utils");
const versions_1 = require("../../utils/versions");
const ast_utils_1 = require("./flat-config/ast-utils");
const path_utils_1 = require("./flat-config/path-utils");
const ts = require("typescript");
const posix_1 = require("node:path/posix");
function findEslintFile(tree, projectRoot) {
if (projectRoot === undefined) {
for (const file of [
config_file_1.baseEsLintConfigFile,
...config_file_1.BASE_ESLINT_CONFIG_FILENAMES,
]) {
if (tree.exists(file)) {
return file;
}
}
}
projectRoot ??= '';
for (const file of config_file_1.ESLINT_CONFIG_FILENAMES) {
if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, file))) {
return file;
}
}
return null;
}
function isEslintConfigSupported(tree, projectRoot = '') {
const eslintFile = findEslintFile(tree, projectRoot);
if (!eslintFile) {
return false;
}
return (eslintFile.endsWith('.json') ||
eslintFile.endsWith('.config.js') ||
eslintFile.endsWith('.config.cjs') ||
eslintFile.endsWith('.config.mjs'));
}
function updateRelativePathsInConfig(tree, sourcePath, destinationPath) {
if (sourcePath === destinationPath ||
!isEslintConfigSupported(tree, destinationPath)) {
return;
}
const configPath = (0, devkit_1.joinPathFragments)(destinationPath, findEslintFile(tree, destinationPath));
const offset = (0, devkit_1.offsetFromRoot)(destinationPath);
if ((0, flat_config_1.useFlatConfig)(tree)) {
const config = tree.read(configPath, 'utf-8');
tree.write(configPath, replaceFlatConfigPaths(config, sourcePath, offset, destinationPath, tree));
}
else {
(0, devkit_1.updateJson)(tree, configPath, (json) => {
if (typeof json.extends === 'string') {
json.extends = offsetFilePath(sourcePath, json.extends, offset, tree);
}
else if (json.extends) {
json.extends = json.extends.map((extend) => offsetFilePath(sourcePath, extend, offset, tree));
}
json.overrides?.forEach((o) => {
if (o.parserOptions?.project) {
o.parserOptions.project = Array.isArray(o.parserOptions.project)
? o.parserOptions.project.map((p) => p.replace(sourcePath, destinationPath))
: o.parserOptions.project.replace(sourcePath, destinationPath);
}
});
return json;
});
}
}
function replaceFlatConfigPaths(config, sourceRoot, offset, destinationRoot, tree) {
let match;
let newConfig = config;
// replace requires
const requireRegex = RegExp(/require\(['"](.*)['"]\)/g);
while ((match = requireRegex.exec(newConfig)) !== null) {
const newPath = offsetFilePath(sourceRoot, match[1], offset, tree);
newConfig =
newConfig.slice(0, match.index) +
`require('${newPath}')` +
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
const projectRegex = RegExp(/project:\s?\[?['"](.*)['"]\]?/g);
while ((match = projectRegex.exec(newConfig)) !== null) {
const newProjectDef = match[0].replaceAll(sourceRoot, destinationRoot);
newConfig =
newConfig.slice(0, match.index) +
newProjectDef +
newConfig.slice(match.index + match[0].length);
}
return newConfig;
}
function offsetFilePath(projectRoot, pathToFile, offset, tree) {
if (config_file_1.ESLINT_CONFIG_FILENAMES.some((eslintFile) => pathToFile.includes(eslintFile))) {
// if the file is point to base eslint
const rootEslint = findEslintFile(tree);
if (rootEslint) {
return (0, devkit_1.joinPathFragments)(offset, rootEslint);
}
}
if (!pathToFile.startsWith('..')) {
// not a relative path
return pathToFile;
}
return (0, devkit_1.joinPathFragments)(offset, projectRoot, pathToFile);
}
function determineEslintConfigFormat(content) {
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';
}
function addOverrideToLintConfig(tree, root, override, options = {
insertAtTheEnd: true,
}) {
const isBase = options.checkBaseConfig && findEslintFile(tree, root).includes('.base');
if ((0, flat_config_1.useFlatConfig)(tree)) {
let fileName;
if (isBase) {
for (const file of config_file_1.BASE_ESLINT_CONFIG_FILENAMES) {
if (tree.exists((0, devkit_1.joinPathFragments)(root, file))) {
fileName = (0, devkit_1.joinPathFragments)(root, file);
break;
}
}
}
else {
for (const f of flat_config_1.eslintFlatConfigFilenames) {
if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
fileName = (0, devkit_1.joinPathFragments)(root, f);
break;
}
}
}
let content = tree.read(fileName, 'utf8');
const format = content.includes('export default') ? 'mjs' : 'cjs';
const flatOverride = (0, ast_utils_1.generateFlatOverride)(override, format);
// Check if the provided override using legacy eslintrc properties or plugins, if so we need to add compat
if ((0, ast_utils_1.overrideNeedsCompat)(override)) {
content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
}
tree.write(fileName, (0, ast_utils_1.addBlockToFlatConfigExport)(content, flatOverride, options));
}
else {
const fileName = (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintConfigFile : '.eslintrc.json');
(0, devkit_1.updateJson)(tree, fileName, (json) => {
json.overrides ??= [];
if (options.insertAtTheEnd) {
json.overrides.push(override);
}
else {
json.overrides.unshift(override);
}
return json;
});
}
}
function updateOverrideInLintConfig(tree, rootOrFile, lookup, update) {
let fileName;
let root = rootOrFile;
if (tree.exists(rootOrFile) && tree.isFile(rootOrFile)) {
fileName = rootOrFile;
root = (0, posix_1.dirname)(rootOrFile);
}
if ((0, flat_config_1.useFlatConfig)(tree)) {
if (!fileName) {
for (const f of flat_config_1.eslintFlatConfigFilenames) {
if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
fileName = (0, devkit_1.joinPathFragments)(root, f);
break;
}
}
}
let content = tree.read(fileName, 'utf8');
content = (0, ast_utils_1.replaceOverride)(content, root, lookup, update);
tree.write(fileName, content);
}
else {
fileName ??= (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
if (!tree.exists(fileName)) {
return;
}
const existingJson = (0, devkit_1.readJson)(tree, fileName);
if (!existingJson.overrides || !existingJson.overrides.some(lookup)) {
return;
}
(0, devkit_1.updateJson)(tree, fileName, (json) => {
const index = json.overrides.findIndex(lookup);
if (index !== -1) {
const newOverride = update(json.overrides[index]);
if (newOverride) {
json.overrides[index] = newOverride;
}
else {
json.overrides.splice(index, 1);
}
}
return json;
});
}
}
function lintConfigHasOverride(tree, rootOrFile, lookup, checkBaseConfig = false) {
let fileName;
let root = rootOrFile;
if (tree.exists(rootOrFile) && tree.isFile(rootOrFile)) {
fileName = rootOrFile;
root = (0, posix_1.dirname)(rootOrFile);
}
if (!fileName && !isEslintConfigSupported(tree, root)) {
return false;
}
const isBase = !fileName &&
checkBaseConfig &&
findEslintFile(tree, root).includes('.base');
if (isBase) {
for (const file of config_file_1.BASE_ESLINT_CONFIG_FILENAMES) {
if (tree.exists((0, devkit_1.joinPathFragments)(root, file))) {
fileName = (0, devkit_1.joinPathFragments)(root, file);
break;
}
}
}
if ((0, flat_config_1.useFlatConfig)(tree)) {
if (!fileName) {
for (const f of flat_config_1.eslintFlatConfigFilenames) {
if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
fileName = (0, devkit_1.joinPathFragments)(root, f);
break;
}
}
}
const content = tree.read(fileName, 'utf8');
return (0, ast_utils_1.hasOverride)(content, lookup);
}
else {
fileName ??= (0, devkit_1.joinPathFragments)(root, isBase ? config_file_1.baseEsLintConfigFile : '.eslintrc.json');
return (0, devkit_1.readJson)(tree, fileName).overrides?.some(lookup) || false;
}
}
function replaceOverridesInLintConfig(tree, root, overrides) {
if ((0, flat_config_1.useFlatConfig)(tree)) {
let fileName;
for (const f of flat_config_1.eslintFlatConfigFilenames) {
if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
fileName = (0, devkit_1.joinPathFragments)(root, f);
break;
}
}
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
if (overrides.some(ast_utils_1.overrideNeedsCompat)) {
content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
}
content = (0, ast_utils_1.removeOverridesFromLintConfig)(content);
overrides.forEach((override) => {
const flatOverride = (0, ast_utils_1.generateFlatOverride)(override, format);
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, flatOverride);
});
tree.write(fileName, content);
}
else {
const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
(0, devkit_1.updateJson)(tree, fileName, (json) => {
json.overrides = overrides;
return json;
});
}
}
function addExtendsToLintConfig(tree, root, plugin, insertAtTheEnd = false) {
if ((0, flat_config_1.useFlatConfig)(tree)) {
const pluginExtends = [];
let fileName;
for (const f of flat_config_1.eslintFlatConfigFilenames) {
if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
fileName = (0, devkit_1.joinPathFragments)(root, f);
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;
// assume eslint version is 9 if not found, as it's what we'd be generating by default
const eslintVersion = (0, version_utils_1.getInstalledEslintVersion)(tree) ?? versions_1.eslint9__eslintVersion;
if ((0, semver_1.gte)(eslintVersion, '9.0.0')) {
// eslint v9 requires the incompatible plugins to be wrapped with a helper from @eslint/compat
const plugins = (Array.isArray(plugin) ? plugin : [plugin]).map((p) => typeof p === 'string' ? { name: p, needCompatFixup: false } : p);
let compatiblePluginsBatch = [];
plugins.forEach(({ name, needCompatFixup }) => {
if (needCompatFixup) {
if (compatiblePluginsBatch.length > 0) {
// flush the current batch of compatible plugins and reset it
pluginExtends.push((0, ast_utils_1.generatePluginExtendsElement)(compatiblePluginsBatch));
compatiblePluginsBatch = [];
}
// generate the extends for the incompatible plugin
pluginExtends.push((0, ast_utils_1.generatePluginExtendsElementWithCompatFixup)(name));
shouldImportEslintCompat = true;
}
else {
// add the compatible plugin to the current batch
compatiblePluginsBatch.push(name);
}
});
if (compatiblePluginsBatch.length > 0) {
// flush the batch of compatible plugins
pluginExtends.push((0, ast_utils_1.generatePluginExtendsElement)(compatiblePluginsBatch));
}
}
else {
const plugins = (Array.isArray(plugin) ? plugin : [plugin]).map((p) => typeof p === 'string' ? p : p.name);
pluginExtends.push((0, ast_utils_1.generatePluginExtendsElement)(plugins));
}
let content = tree.read(fileName, 'utf8');
if (shouldImportEslintCompat) {
content = (0, ast_utils_1.addImportToFlatConfig)(content, ['fixupConfigRules'], '@eslint/compat');
}
content = (0, ast_utils_1.addFlatCompatToFlatConfig)(content);
// reverse the order to ensure they are added in the correct order at the
// start of the `extends` array
for (const pluginExtend of pluginExtends.reverse()) {
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, pluginExtend, {
insertAtTheEnd,
});
}
tree.write(fileName, content);
if (shouldImportEslintCompat) {
return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, { '@eslint/compat': versions_1.eslintCompat, '@eslint/eslintrc': versions_1.eslintrcVersion }, undefined, true);
}
return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, { '@eslint/eslintrc': versions_1.eslintrcVersion }, undefined, true);
}
else {
const plugins = (Array.isArray(plugin) ? plugin : [plugin]).map((p) => typeof p === 'string' ? p : p.name);
const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
(0, devkit_1.updateJson)(tree, fileName, (json) => {
json.extends ??= [];
json.extends = [
...plugins,
...(Array.isArray(json.extends) ? json.extends : [json.extends]),
];
return json;
});
return () => { };
}
}
function addPredefinedConfigToFlatLintConfig(tree, root, predefinedConfigName, options = {}) {
const { moduleName = 'nx', moduleImportPath = '@nx/eslint-plugin', spread = true, insertAtTheEnd = true, checkBaseConfig = false, } = options;
if (!(0, flat_config_1.useFlatConfig)(tree))
throw new Error('Predefined configs can only be used with flat configs');
let fileName;
for (const f of flat_config_1.eslintFlatConfigFilenames) {
if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
fileName = (0, devkit_1.joinPathFragments)(root, f);
break;
}
}
let content = tree.read(fileName, 'utf8');
content = (0, ast_utils_1.addImportToFlatConfig)(content, moduleName, moduleImportPath);
content = (0, ast_utils_1.addBlockToFlatConfigExport)(content, (0, ast_utils_1.generateFlatPredefinedConfig)(predefinedConfigName, moduleName, spread), { insertAtTheEnd, checkBaseConfig });
tree.write(fileName, content);
}
function addPluginsToLintConfig(tree, root, plugin) {
const plugins = Array.isArray(plugin) ? plugin : [plugin];
if ((0, flat_config_1.useFlatConfig)(tree)) {
let fileName;
for (const f of flat_config_1.eslintFlatConfigFilenames) {
if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
fileName = (0, devkit_1.joinPathFragments)(root, f);
break;
}
}
let content = tree.read(fileName, 'utf8');
const mappedPlugins = [];
plugins.forEach((name) => {
const imp = getPluginImport(name);
const varName = (0, devkit_1.names)(imp).propertyName;
mappedPlugins.push({ name, varName, imp });
});
mappedPlugins.forEach(({ varName, imp }) => {
content = (0, ast_utils_1.addImportToFlatConfig)(content, varName, imp);
});
content = (0, ast_utils_1.addPluginsToExportsBlock)(content, mappedPlugins);
tree.write(fileName, content);
}
else {
const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
(0, devkit_1.updateJson)(tree, fileName, (json) => {
json.plugins = [...plugins, ...(json.plugins ?? [])];
return json;
});
}
}
function addIgnoresToLintConfig(tree, root, ignorePatterns) {
if ((0, flat_config_1.useFlatConfig)(tree)) {
let fileName;
for (const f of flat_config_1.eslintFlatConfigFilenames) {
if (tree.exists((0, devkit_1.joinPathFragments)(root, f))) {
fileName = (0, devkit_1.joinPathFragments)(root, f);
break;
}
}
if (!fileName) {
return;
}
let content = tree.read(fileName, 'utf8');
if ((0, ast_utils_1.hasFlatConfigIgnoresBlock)(content)) {
content = (0, ast_utils_1.addPatternsToFlatConfigIgnoresBlock)(content, ignorePatterns);
tree.write(fileName, content);
}
else {
const block = (0, ast_utils_1.generateAst)({
ignores: ignorePatterns.map((path) => (0, path_utils_1.mapFilePath)(path)),
});
tree.write(fileName, (0, ast_utils_1.addBlockToFlatConfigExport)(content, block));
}
}
else {
const fileName = (0, devkit_1.joinPathFragments)(root, '.eslintrc.json');
if (!tree.exists(fileName)) {
return;
}
(0, devkit_1.updateJson)(tree, fileName, (json) => {
const ignoreSet = new Set([
...(json.ignorePatterns ?? []),
...ignorePatterns,
]);
json.ignorePatterns = Array.from(ignoreSet);
return json;
});
}
}
function getPluginImport(pluginName) {
if (pluginName.includes('eslint-plugin-')) {
return pluginName;
}
if (!pluginName.startsWith('@')) {
return `eslint-plugin-${pluginName}`;
}
if (!pluginName.includes('/')) {
return `${pluginName}/eslint-plugin`;
}
const [scope, name] = pluginName.split('/');
return `${scope}/eslint-plugin-${name}`;
}

View File

@@ -0,0 +1,3 @@
import { Tree } from '@nx/devkit';
export declare function getEslintTargets(tree: Tree): Set<string>;
//# sourceMappingURL=eslint-targets.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"eslint-targets.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/utils/eslint-targets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGlC,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,eAY1C"}

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getEslintTargets = getEslintTargets;
const executor_options_utils_1 = require("@nx/devkit/src/generators/executor-options-utils");
function getEslintTargets(tree) {
const eslintTargetNames = new Set();
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nx/eslint:lint', (_, __, target) => {
eslintTargetNames.add(target);
});
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nx/linter:eslint', (_, __, target) => {
eslintTargetNames.add(target);
});
(0, executor_options_utils_1.forEachExecutorOptions)(tree, '@nrwl/linter:eslint', (_, __, target) => {
eslintTargetNames.add(target);
});
return eslintTargetNames;
}

View File

@@ -0,0 +1,86 @@
import { Linter } from 'eslint';
import * as ts from 'typescript';
/**
* Remove all overrides from the config file
*/
export declare function removeOverridesFromLintConfig(content: string): string;
export declare function addPatternsToFlatConfigIgnoresBlock(content: string, ignorePatterns: string[]): string;
export declare function hasFlatConfigIgnoresBlock(content: string): boolean;
export declare function hasOverride(content: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean): boolean;
/**
* Finds an override matching the lookup function and applies the update function to it.
* Uses property-level AST updates to preserve properties with variable references.
*/
export declare function replaceOverride(content: string, root: string, lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean, update?: (override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>) => Partial<Linter.ConfigOverride<Linter.RulesRecord>>): string;
/**
* 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 declare function addImportToFlatConfig(content: string, variable: string | string[], imp: string): string;
/**
* Remove an import from flat config
*/
export declare function removeImportFromFlatConfig(content: string, variable: string, imp: string): string;
/**
* Injects new ts.expression to the end of the module.exports or export default array.
*/
export declare function addBlockToFlatConfigExport(content: string, config: ts.Expression | ts.SpreadElement, options?: {
insertAtTheEnd?: boolean;
checkBaseConfig?: boolean;
}): string;
export declare function removePlugin(content: string, pluginName: string, pluginImport: string): string;
export declare function removeCompatExtends(content: string, compatExtends: string[]): string;
export declare function removePredefinedConfigs(content: string, moduleImport: string, moduleVariable: string, configs: string[]): string;
/**
* Add plugins block to the top of the export blocks
*/
export declare function addPluginsToExportsBlock(content: string, plugins: {
name: string;
varName: string;
imp: string;
}[]): string;
/**
* Adds compat if missing to flat config
*/
export declare function addFlatCompatToFlatConfig(content: string): string;
/**
* Generate node list representing the imports and the exports blocks
* Optionally add flat compat initialization
*/
export declare function createNodeList(importsMap: Map<string, string>, exportElements: ts.Expression[], format: 'mjs' | 'cjs'): ts.NodeArray<ts.VariableStatement | ts.Identifier | ts.ExpressionStatement | ts.SourceFile>;
export declare function generateSpreadElement(name: string): ts.SpreadElement;
export declare function generatePluginExtendsElement(plugins: string[]): ts.SpreadElement;
export declare function generatePluginExtendsElementWithCompatFixup(plugin: string): ts.SpreadElement;
/**
* Stringifies TS nodes to file content string
*/
export declare function stringifyNodeList(nodes: ts.NodeArray<ts.VariableStatement | ts.Identifier | ts.ExpressionStatement | ts.SourceFile>): string;
/**
* generates AST require statement
*/
export declare function generateRequire(variableName: string | ts.ObjectBindingPattern, imp: string): ts.VariableStatement;
export declare function generateESMImport(variableName: string | ts.ObjectBindingPattern, imp: string): ts.ImportDeclaration;
export declare function overrideNeedsCompat(override: Partial<Linter.ConfigOverride<Linter.RulesRecord>>): string | string[] | {
[name: string]: boolean;
};
/**
* Generates an AST object or spread element representing a modern flat config entry,
* based on a given legacy eslintrc JSON override object
*/
export declare function generateFlatOverride(_override: Partial<Linter.ConfigOverride<Linter.RulesRecord>> & {
ignores?: Linter.FlatConfig['ignores'];
}, format: 'mjs' | 'cjs', importsMap?: Map<string, string>): ts.ObjectLiteralExpression | ts.SpreadElement;
export declare function generateFlatPredefinedConfig(predefinedConfigName: string, moduleName?: string, spread?: boolean): ts.ObjectLiteralExpression | ts.SpreadElement | ts.ElementAccessExpression;
export declare function mapFilePaths<T extends Partial<Linter.ConfigOverride<Linter.RulesRecord>>>(_override: T): T;
/**
* Generates an AST from a JSON-type input
*/
export declare function generateAst<T>(input: unknown, propertyAssignmentReplacer?: {
keyToMatch: RegExp | string;
replacer: (propertyAssignment: ts.PropertyAssignment, propertyName: string) => ts.PropertyAssignment;
}): T;
//# sourceMappingURL=ast-utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ast-utils.d.ts","sourceRoot":"","sources":["../../../../../../../packages/eslint/src/generators/utils/flat-config/ast-utils.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AASjC;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA+BrE;AA2BD,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,CA0CR;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAgBlE;AA8BD,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,OAAO,GACvE,OAAO,CAoCT;AAiJD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,OAAO,EACxE,MAAM,CAAC,EAAE,CACP,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,KACzD,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,GACtD,MAAM,CAsGR;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,EAC3B,GAAG,EAAE,MAAM,GACV,MAAM,CAgBR;AA+QD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,GACV,MAAM,CAeR;AA+DD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,aAAa,EACxC,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAE7D,GACA,MAAM,CA8BR;AA+ID,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,UA8JrB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EAAE,GACtB,MAAM,CA+DR;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EAAE,GAChB,MAAM,CA8CR;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,GACxD,MAAM,CAoBR;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,UAYxD;AA6CD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,cAAc,EAAE,EAAE,CAAC,UAAU,EAAE,EAC/B,MAAM,EAAE,KAAK,GAAG,KAAK,GACpB,EAAE,CAAC,SAAS,CACb,EAAE,CAAC,iBAAiB,GAAG,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,mBAAmB,GAAG,EAAE,CAAC,UAAU,CAC9E,CA4BA;AAyBD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,aAAa,CAEpE;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EAAE,GAChB,EAAE,CAAC,aAAa,CAWlB;AAED,wBAAgB,2CAA2C,CACzD,MAAM,EAAE,MAAM,GACb,EAAE,CAAC,aAAa,CAiBlB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,EAAE,CAAC,SAAS,CACf,EAAE,CAAC,iBAAiB,GACpB,EAAE,CAAC,UAAU,GACb,EAAE,CAAC,mBAAmB,GACtB,EAAE,CAAC,UAAU,CAChB,GACA,MAAM,CAuBR;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,oBAAoB,EAC9C,GAAG,EAAE,MAAM,GACV,EAAE,CAAC,iBAAiB,CAmBtB;AAGD,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,oBAAoB,EAC9C,GAAG,EAAE,MAAM,GACV,EAAE,CAAC,iBAAiB,CAsCtB;AAsBD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;;EAG7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;CACxC,EACD,MAAM,EAAE,KAAK,GAAG,KAAK,EACrB,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,EAAE,CAAC,uBAAuB,GAAG,EAAE,CAAC,aAAa,CAyL/C;AAwDD,wBAAgB,4BAA4B,CAC1C,oBAAoB,EAAE,MAAM,EAC5B,UAAU,SAAO,EACjB,MAAM,UAAO,GACZ,EAAE,CAAC,uBAAuB,GAAG,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,uBAAuB,CAU5E;AAED,wBAAgB,YAAY,CAC1B,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,EAC5D,SAAS,EAAE,CAAC,KAgBb;AAYD;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,EAAE,OAAO,EACd,0BAA0B,CAAC,EAAE;IAC3B,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,QAAQ,EAAE,CACR,kBAAkB,EAAE,EAAE,CAAC,kBAAkB,EACzC,YAAY,EAAE,MAAM,KACjB,EAAE,CAAC,kBAAkB,CAAC;CAC5B,GACA,CAAC,CAuCH"}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
import type { Linter } from 'eslint';
export declare function updateFiles(override: Linter.ConfigOverride<Linter.RulesRecord>): Linter.ConfigOverride<Linter.RulesRecord>;
export declare function mapFilePath(filePath: string): string;
//# sourceMappingURL=path-utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../../../../../../packages/eslint/src/generators/utils/flat-config/path-utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,GAClD,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAQ3C;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,UAY3C"}

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateFiles = updateFiles;
exports.mapFilePath = mapFilePath;
const devkit_1 = require("@nx/devkit");
function updateFiles(override) {
if (override.files) {
override.files = Array.isArray(override.files)
? override.files
: [override.files];
override.files = override.files.map((file) => mapFilePath(file));
}
return override;
}
function mapFilePath(filePath) {
if (filePath.startsWith('!')) {
const fileWithoutBang = filePath.slice(1);
if (fileWithoutBang.startsWith('*.')) {
return `!${(0, devkit_1.joinPathFragments)('**', fileWithoutBang)}`;
}
return filePath;
}
if (filePath.startsWith('*.')) {
return (0, devkit_1.joinPathFragments)('**', filePath);
}
return filePath;
}

View File

@@ -0,0 +1,9 @@
/**
* @deprecated Use LinterType instead. It will be removed in Nx v22.
*/
export declare enum Linter {
EsLint = "eslint",
None = "none"
}
export type LinterType = 'eslint' | 'none';
//# sourceMappingURL=linter.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"linter.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/utils/linter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,MAAM;IAChB,MAAM,WAAW;IACjB,IAAI,SAAS;CACd;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC"}

11
node_modules/@nx/eslint/src/generators/utils/linter.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Linter = void 0;
/**
* @deprecated Use LinterType instead. It will be removed in Nx v22.
*/
var Linter;
(function (Linter) {
Linter["EsLint"] = "eslint";
Linter["None"] = "none";
})(Linter || (exports.Linter = Linter = {}));

View File

@@ -0,0 +1,3 @@
import { Tree } from '@nx/devkit';
export declare function hasEslintPlugin(tree: Tree): boolean;
//# sourceMappingURL=plugin.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/utils/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAc,MAAM,YAAY,CAAC;AAE9C,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAOnD"}

10
node_modules/@nx/eslint/src/generators/utils/plugin.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasEslintPlugin = hasEslintPlugin;
const devkit_1 = require("@nx/devkit");
function hasEslintPlugin(tree) {
const nxJson = (0, devkit_1.readNxJson)(tree);
return nxJson.plugins?.some((p) => typeof p === 'string'
? p === '@nx/eslint/plugin'
: p.plugin === '@nx/eslint/plugin');
}

View File

@@ -0,0 +1,20 @@
<% if (flatConfig) { %>import { RuleTester } from '@typescript-eslint/rule-tester';
import type { RuleTesterConfig } from '@typescript-eslint/rule-tester';
import { rule, RULE_NAME } from './<%= name %>';
const ruleTester = new RuleTester({
languageOptions: {
parser: require('@typescript-eslint/parser'),
},
} as RuleTesterConfig);
<% } else { %>import { TSESLint } from '@typescript-eslint/utils';
import { rule, RULE_NAME } from './<%= name %>';
const ruleTester = new TSESLint.RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
});
<% } %>
ruleTester.run(RULE_NAME, rule, {
valid: [`const example = true;`],
invalid: [],
});

View File

@@ -0,0 +1,36 @@
/**
* This file sets you up with structure needed for an ESLint rule.
*
* It leverages utilities from @typescript-eslint to allow TypeScript to
* provide autocompletions etc for the configuration.
*
* Your rule's custom logic will live within the create() method below
* and you can learn more about writing ESLint rules on the official guide:
*
* https://eslint.org/docs/developer-guide/working-with-rules
*
* You can also view many examples of existing rules here:
*
* https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin/src/rules
*/
import { ESLintUtils } from '@typescript-eslint/utils';
// NOTE: The rule will be available in ESLint configs as "@nx/workspace-<%= name %>"
export const RULE_NAME = '<%= name %>';
export const rule = ESLintUtils.RuleCreator(() => __filename)({
name: RULE_NAME,
meta: {
type: 'problem',
docs: {
description: ``,
},
schema: [],
messages: {},
},
defaultOptions: [],
create(context) {
return {};
},
});

View File

@@ -0,0 +1,26 @@
{
"$schema": "https://json-schema.org/schema",
"$id": "NxWorkspaceRule",
"cli": "nx",
"title": "Create a new Workspace Lint Rule",
"description": "Create a new Workspace Lint Rule.",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the new rule.",
"$default": {
"$source": "argv",
"index": 0
}
},
"directory": {
"type": "string",
"description": "Create the rule under this directory within `tools/eslint-rules/` (can be nested).",
"alias": "dir",
"default": "rules"
}
},
"required": ["name", "directory"],
"examplesFile": "../../../docs/workspace-rule-examples.md"
}

View File

@@ -0,0 +1,7 @@
import { GeneratorCallback, Tree } from '@nx/devkit';
export interface LintWorkspaceRuleGeneratorOptions {
name: string;
directory: string;
}
export declare function lintWorkspaceRuleGenerator(tree: Tree, options: LintWorkspaceRuleGeneratorOptions): Promise<GeneratorCallback>;
//# sourceMappingURL=workspace-rule.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"workspace-rule.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/workspace-rule/workspace-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,iBAAiB,EAKjB,IAAI,EACL,MAAM,YAAY,CAAC;AASpB,MAAM,WAAW,iCAAiC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,iCAAiC,8BAiH3C"}

View File

@@ -0,0 +1,86 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lintWorkspaceRuleGenerator = lintWorkspaceRuleGenerator;
const tslib_1 = require("tslib");
const devkit_1 = require("@nx/devkit");
const string_utils_1 = require("@nx/devkit/src/utils/string-utils");
const path_1 = require("path");
const ts = tslib_1.__importStar(require("typescript"));
const workspace_lint_rules_1 = require("../../utils/workspace-lint-rules");
const workspace_rules_project_1 = require("../workspace-rules-project/workspace-rules-project");
const flat_config_1 = require("../../utils/flat-config");
const versions_1 = require("../../utils/versions");
async function lintWorkspaceRuleGenerator(tree, options) {
const tasks = [];
const flatConfig = (0, flat_config_1.useFlatConfig)(tree);
const nxJson = (0, devkit_1.readNxJson)(tree);
// Ensure that the workspace rules project has been created
tasks.push(await (0, workspace_rules_project_1.lintWorkspaceRulesProjectGenerator)(tree, {
skipFormat: true,
addPlugin: process.env.NX_ADD_PLUGINS !== 'false' &&
nxJson.useInferencePlugins !== false,
}));
if (flatConfig) {
tasks.push((0, devkit_1.addDependenciesToPackageJson)(tree, {}, { '@typescript-eslint/rule-tester': versions_1.eslint9__typescriptESLintVersion }));
}
const ruleDir = (0, devkit_1.joinPathFragments)(workspace_lint_rules_1.workspaceLintPluginDir, options.directory ?? '');
// Generate the required files for the new rule
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), ruleDir, {
tmpl: '',
name: options.name,
flatConfig,
});
const nameCamelCase = (0, string_utils_1.camelize)(options.name);
/**
* Import the new rule into the workspace plugin index.ts and
* register it ready for use in lint configs.
*/
const pluginIndexPath = (0, devkit_1.joinPathFragments)(workspace_lint_rules_1.workspaceLintPluginDir, 'index.ts');
const existingPluginIndexContents = tree.read(pluginIndexPath, 'utf-8');
const pluginIndexSourceFile = ts.createSourceFile(pluginIndexPath, existingPluginIndexContents, ts.ScriptTarget.Latest, true);
function findRulesObject(node) {
if (ts.isPropertyAssignment(node) &&
ts.isIdentifier(node.name) &&
node.name.text === 'rules' &&
ts.isObjectLiteralExpression(node.initializer)) {
return node.initializer;
}
return node.forEachChild(findRulesObject);
}
const rulesObject = pluginIndexSourceFile.forEachChild((node) => findRulesObject(node));
if (rulesObject) {
const ruleNameSymbol = `${nameCamelCase}Name`;
const ruleConfigSymbol = nameCamelCase;
/**
* If the rules object already has entries, we need to make sure our insertion
* takes commas into account.
*/
let leadingComma = '';
if (rulesObject.properties.length > 0) {
if (!rulesObject.properties.hasTrailingComma) {
leadingComma = ',';
}
}
const newContents = (0, devkit_1.applyChangesToString)(existingPluginIndexContents, [
{
type: devkit_1.ChangeType.Insert,
index: 0,
text: `import { RULE_NAME as ${ruleNameSymbol}, rule as ${ruleConfigSymbol} } from './${options.directory ? `${options.directory}/` : ''}${options.name}';\n`,
},
{
type: devkit_1.ChangeType.Insert,
index: rulesObject.getEnd() - 1,
text: `${leadingComma}[${ruleNameSymbol}]: ${ruleConfigSymbol}\n`,
},
]);
tree.write(pluginIndexPath, newContents);
}
await (0, devkit_1.formatFiles)(tree);
devkit_1.logger.info(`NX Reminder: Once you have finished writing your rule logic, you need to actually enable the rule within an appropriate ESLint config in your workspace, for example:
"rules": {
"@nx/workspace-${options.name}": "error"
}
`);
return (0, devkit_1.runTasksInSerial)(...tasks);
}

View File

@@ -0,0 +1,27 @@
/**
* Import your custom workspace rules at the top of this file.
*
* For example:
*
* import { RULE_NAME as myCustomRuleName, rule as myCustomRule } from './rules/my-custom-rule';
*
* In order to quickly get started with writing rules you can use the
* following generator command and provide your desired rule name:
*
* ```sh
* npx nx g @nx/eslint:workspace-rule {{ NEW_RULE_NAME }}
* ```
*/
module.exports = {
/**
* Apply the imported custom rules here.
*
* For example (using the example import above):
*
* rules: {
* [myCustomRuleName]: myCustomRule
* }
*/
rules: {}
};

View File

@@ -0,0 +1,14 @@
{
"extends": "<%= rootTsConfigPath %>",
"compilerOptions": {
"moduleResolution": "node16",
"module": "node16"
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lint.json"
}
]
}

View File

@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "<%= offsetFromRoot %>dist/out-tsc",
"types": ["node"]
},
"exclude": ["**/*.spec.ts"],
"include": ["**/*.ts"]
}

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json-schema.org/schema",
"$id": "NxWorkspaceRulesProject",
"cli": "nx",
"title": "Create the Workspace Lint Rules Project",
"description": "Create the Workspace Lint Rules Project.",
"type": "object",
"examples": [
{
"command": "nx g @nx/eslint:workspace-rules-project",
"description": "Create the Workspace Lint Rules Project"
}
],
"properties": {
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false,
"x-priority": "internal"
}
},
"required": []
}

View File

@@ -0,0 +1,9 @@
import { GeneratorCallback, Tree } from '@nx/devkit';
export declare const WORKSPACE_RULES_PROJECT_NAME = "eslint-rules";
export declare const WORKSPACE_PLUGIN_DIR = "tools/eslint-rules";
export interface LintWorkspaceRulesProjectGeneratorOptions {
skipFormat?: boolean;
addPlugin?: boolean;
}
export declare function lintWorkspaceRulesProjectGenerator(tree: Tree, options?: LintWorkspaceRulesProjectGeneratorOptions): Promise<GeneratorCallback>;
//# sourceMappingURL=workspace-rules-project.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"workspace-rules-project.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/generators/workspace-rules-project/workspace-rules-project.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,iBAAiB,EAKjB,IAAI,EAGL,MAAM,YAAY,CAAC;AASpB,eAAO,MAAM,4BAA4B,iBAAiB,CAAC;AAE3D,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AAEzD,MAAM,WAAW,yCAAyC;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,kCAAkC,CACtD,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,yCAA8C,8BAsGxD"}

View File

@@ -0,0 +1,87 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WORKSPACE_PLUGIN_DIR = exports.WORKSPACE_RULES_PROJECT_NAME = void 0;
exports.lintWorkspaceRulesProjectGenerator = lintWorkspaceRulesProjectGenerator;
const devkit_1 = require("@nx/devkit");
const js_1 = require("@nx/js");
const add_swc_dependencies_1 = require("@nx/js/src/utils/swc/add-swc-dependencies");
const ts_solution_setup_1 = require("@nx/js/src/utils/typescript/ts-solution-setup");
const path_1 = require("path");
const versions_1 = require("../../utils/versions");
const version_utils_1 = require("../../utils/version-utils");
const workspace_lint_rules_1 = require("../../utils/workspace-lint-rules");
exports.WORKSPACE_RULES_PROJECT_NAME = 'eslint-rules';
exports.WORKSPACE_PLUGIN_DIR = 'tools/eslint-rules';
async function lintWorkspaceRulesProjectGenerator(tree, options = {}) {
const { configurationGenerator } = (0, devkit_1.ensurePackage)('@nx/jest', versions_1.nxVersion);
const tasks = [];
// Noop if the workspace rules project already exists
try {
(0, devkit_1.readProjectConfiguration)(tree, exports.WORKSPACE_RULES_PROJECT_NAME);
return;
}
catch { }
// Create the project, the test target is added below by the jest generator
(0, devkit_1.addProjectConfiguration)(tree, exports.WORKSPACE_RULES_PROJECT_NAME, {
root: exports.WORKSPACE_PLUGIN_DIR,
sourceRoot: exports.WORKSPACE_PLUGIN_DIR,
targets: {},
});
// Generate the required files
(0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, 'files'), workspace_lint_rules_1.workspaceLintPluginDir, {
tmpl: '',
offsetFromRoot: (0, devkit_1.offsetFromRoot)(exports.WORKSPACE_PLUGIN_DIR),
rootTsConfigPath: (0, js_1.getRelativePathToRootTsConfig)(tree, exports.WORKSPACE_PLUGIN_DIR),
});
/**
* Ensure that when workspace rules are updated they cause all projects to be affected for now.
* TODO: Explore writing a ProjectGraph plugin to make this more surgical.
*/
const nxJson = (0, devkit_1.readNxJson)(tree);
if (nxJson.targetDefaults?.lint?.inputs) {
nxJson.targetDefaults.lint.inputs.push(`{workspaceRoot}/${exports.WORKSPACE_PLUGIN_DIR}/**/*`);
(0, devkit_1.updateNxJson)(tree, nxJson);
}
// Add jest to the project and return installation task
tasks.push(await configurationGenerator(tree, {
...options,
project: exports.WORKSPACE_RULES_PROJECT_NAME,
supportTsx: false,
skipSerializers: true,
setupFile: 'none',
compiler: (0, ts_solution_setup_1.isUsingTsSolutionSetup)(tree) ? 'swc' : 'tsc',
skipFormat: true,
testEnvironment: 'node',
}));
(0, devkit_1.updateJson)(tree, (0, path_1.join)(workspace_lint_rules_1.workspaceLintPluginDir, 'tsconfig.spec.json'), (json) => {
delete json.compilerOptions?.module;
delete json.compilerOptions?.moduleResolution;
if (json.include) {
json.include = json.include.map((v) => {
if (v.startsWith('src/**')) {
return v.replace('src/', '');
}
return v;
});
}
if (json.exclude) {
json.exclude = json.exclude.map((v) => {
if (v.startsWith('src/**')) {
return v.replace('src/', '');
}
return v;
});
}
return json;
});
// Add swc dependencies
tasks.push((0, add_swc_dependencies_1.addSwcRegisterDependencies)(tree));
const typescriptEslintVersion = (0, version_utils_1.getTypeScriptEslintVersionToInstall)(tree);
tasks.push((0, devkit_1.addDependenciesToPackageJson)(tree, {}, {
'@typescript-eslint/utils': typescriptEslintVersion,
}));
if (!options.skipFormat) {
await (0, devkit_1.formatFiles)(tree);
}
return (0, devkit_1.runTasksInSerial)(...tasks);
}

View File

@@ -0,0 +1,3 @@
import { type GeneratorCallback, type Tree } from '@nx/devkit';
export default function (tree: Tree): Promise<GeneratorCallback>;
//# sourceMappingURL=update-typescript-eslint-v8-13-0.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"update-typescript-eslint-v8-13-0.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/migrations/update-20-2-0/update-typescript-eslint-v8-13-0.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,IAAI,EACV,MAAM,YAAY,CAAC;AAIpB,yBAA+B,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoBrE"}

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
const devkit_1 = require("@nx/devkit");
const semver_1 = require("semver");
const version_utils_1 = require("../../utils/version-utils");
async function default_1(tree) {
const devDependencies = {};
const checkPackageAndMigrate = (pkgName) => {
const pkgVersion = (0, version_utils_1.getInstalledPackageVersion)(pkgName, tree);
if (!!pkgVersion && (0, semver_1.gte)(pkgVersion, '8.0.0') && (0, semver_1.lt)(pkgVersion, '8.13.0')) {
devDependencies[pkgName] = '^8.13.0';
}
};
checkPackageAndMigrate('typescript-eslint');
checkPackageAndMigrate('@typescript-eslint/eslint-plugin');
checkPackageAndMigrate('@typescript-eslint/parser');
checkPackageAndMigrate('@typescript-eslint/utils');
if (Object.keys(devDependencies).length > 0) {
return (0, devkit_1.addDependenciesToPackageJson)(tree, {}, devDependencies);
}
return () => { };
}

View File

@@ -0,0 +1,3 @@
import { type Tree } from '@nx/devkit';
export default function (tree: Tree): Promise<void>;
//# sourceMappingURL=add-file-extensions-to-overrides.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"add-file-extensions-to-overrides.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/migrations/update-20-3-0/add-file-extensions-to-overrides.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAIvC,yBAA+B,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BxD"}

View File

@@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
const tslib_1 = require("tslib");
const ts = tslib_1.__importStar(require("typescript"));
const js_1 = require("@nx/js");
async function default_1(tree) {
let rootConfig;
// NOTE: we don't support generating ESM base config currently so they are not handled.
for (const candidate of ['eslint.config.js', 'eslint.config.cjs']) {
if (tree.exists(candidate)) {
rootConfig = candidate;
break;
}
}
if (!rootConfig)
return;
updateOverrideFileExtensions(tree, rootConfig, 'plugin:@nx/typescript', [`'**/*.ts'`, `'**/*.tsx'`], [`'**/*.cts'`, `'**/*.mts'`]);
updateOverrideFileExtensions(tree, rootConfig, 'plugin:@nx/javascript', [`'**/*.js'`, `'**/*.jsx'`], [`'**/*.cjs'`, `'**/*.mjs'`]);
}
function updateOverrideFileExtensions(tree, configFile, plugin, matchingExts, newExts) {
const content = tree.read(configFile, 'utf-8');
const source = ts.createSourceFile('', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
let compatNode;
const spreadElementNodes = (0, js_1.findNodes)(source, ts.SyntaxKind.SpreadElement);
for (const a of spreadElementNodes) {
const assignmentNodes = (0, js_1.findNodes)(a, ts.SyntaxKind.PropertyAssignment);
if (assignmentNodes.length === 0)
continue;
for (const b of assignmentNodes) {
if (b.name.getText() === 'extends' &&
b.initializer.getText().includes(plugin)) {
compatNode = a;
break;
}
}
}
if (compatNode) {
const arrayNodes = (0, js_1.findNodes)(compatNode, ts.SyntaxKind.ArrayLiteralExpression);
for (const a of arrayNodes) {
if (matchingExts.every((ext) => a.elements.some((e) => e.getText() === ext))) {
const exts = new Set(a.elements.map((e) => e.getText()));
for (const ext of newExts) {
exts.add(ext);
}
(0, js_1.replaceChange)(tree, source, configFile, a.getStart(a.getSourceFile()), `[${Array.from(exts).join(', ')}]`, a.getText()).getText();
}
}
}
}

View File

@@ -0,0 +1,3 @@
import { type Tree } from '@nx/devkit';
export default function (tree: Tree): Promise<void>;
//# sourceMappingURL=update-executor-lint-inputs.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"update-executor-lint-inputs.d.ts","sourceRoot":"","sources":["../../../../../../packages/eslint/src/migrations/update-21-6-0/update-executor-lint-inputs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAyC,MAAM,YAAY,CAAC;AAE9E,yBAA+B,IAAI,EAAE,IAAI,iBA0BxC"}

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
const devkit_1 = require("@nx/devkit");
async function default_1(tree) {
const nxJson = (0, devkit_1.readNxJson)(tree);
const executor = '@nx/eslint:lint';
if (!nxJson.targetDefaults?.[executor]?.inputs) {
return;
}
const inputs = nxJson.targetDefaults[executor].inputs;
if (!inputs.includes('^default')) {
// Add after 'default' if present, otherwise at the beginning
const defaultIndex = inputs.indexOf('default');
if (defaultIndex !== -1) {
inputs.splice(defaultIndex + 1, 0, '^default');
}
else {
inputs.unshift('^default');
}
}
if (!inputs.includes('{workspaceRoot}/tools/eslint-rules/**/*')) {
inputs.push('{workspaceRoot}/tools/eslint-rules/**/*');
}
(0, devkit_1.updateNxJson)(tree, nxJson);
await (0, devkit_1.formatFiles)(tree);
}

8
node_modules/@nx/eslint/src/plugins/plugin.d.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
import { CreateNodesV2 } from '@nx/devkit';
export interface EslintPluginOptions {
targetName?: string;
extensions?: string[];
}
export declare const createNodes: CreateNodesV2<EslintPluginOptions>;
export declare const createNodesV2: CreateNodesV2<EslintPluginOptions>;
//# sourceMappingURL=plugin.d.ts.map

1
node_modules/@nx/eslint/src/plugins/plugin.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../packages/eslint/src/plugins/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,aAAa,EAMd,MAAM,YAAY,CAAC;AAwBpB,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAoID,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,mBAAmB,CAoF1D,CAAC;AAEF,eAAO,MAAM,aAAa,oCAAc,CAAC"}

366
node_modules/@nx/eslint/src/plugins/plugin.js generated vendored Normal file
View File

@@ -0,0 +1,366 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNodesV2 = exports.createNodes = void 0;
const devkit_1 = require("@nx/devkit");
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
const js_1 = require("@nx/js");
const internal_1 = require("@nx/js/src/internal");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const posix_1 = require("node:path/posix");
const file_hasher_1 = require("nx/src/hasher/file-hasher");
const cache_directory_1 = require("nx/src/utils/cache-directory");
const globs_1 = require("nx/src/utils/globs");
const workspace_context_1 = require("nx/src/utils/workspace-context");
const semver_1 = require("semver");
const config_file_1 = require("../utils/config-file");
const resolve_eslint_class_1 = require("../utils/resolve-eslint-class");
const DEFAULT_EXTENSIONS = [
'ts',
'cts',
'mts',
'tsx',
'js',
'cjs',
'mjs',
'jsx',
'html',
'vue',
];
const PROJECT_CONFIG_FILENAMES = ['project.json', 'package.json'];
const ESLINT_CONFIG_GLOB_V2 = (0, globs_1.combineGlobPatterns)([
...config_file_1.ESLINT_CONFIG_FILENAMES.map((f) => `**/${f}`),
...PROJECT_CONFIG_FILENAMES.map((f) => `**/${f}`),
]);
function readTargetsCache(cachePath) {
return process.env.NX_CACHE_PROJECT_GRAPH !== 'false' && (0, node_fs_1.existsSync)(cachePath)
? (0, devkit_1.readJsonFile)(cachePath)
: {};
}
function writeTargetsToCache(cachePath, results) {
(0, devkit_1.writeJsonFile)(cachePath, results);
}
const internalCreateNodesV2 = async (ESLint, configFilePath, options, context, projectRootsByEslintRoots, lintableFilesPerProjectRoot, tsconfigChainsByProjectRoot, projectsCache, hashByRoot, pmc) => {
const configDir = (0, posix_1.dirname)(configFilePath);
const eslintVersion = ESLint.version;
let sharedEslint;
const getEslint = (projectRoot) => {
if ((0, node_fs_1.existsSync)((0, posix_1.join)(context.workspaceRoot, projectRoot, '.eslintignore'))) {
return new ESLint({ cwd: (0, posix_1.join)(context.workspaceRoot, projectRoot) });
}
sharedEslint ??= new ESLint({
cwd: (0, posix_1.join)(context.workspaceRoot, configDir),
});
return sharedEslint;
};
// Collect each project root's contribution in parallel, but write
// them into `projects` afterwards in input order so insertion order
// (and therefore downstream merge order) is deterministic. Mutating
// `projects` from inside `Promise.all` would order keys by which
// async branch resolves first.
const orderedProjectRoots = projectRootsByEslintRoots.get(configDir) ?? [];
const contributions = await Promise.all(orderedProjectRoots.map(async (projectRoot) => {
const hash = hashByRoot.get(projectRoot);
if (projectsCache[hash]) {
// We can reuse the projects in the cache.
return projectsCache[hash];
}
let hasNonIgnoredLintableFiles = false;
if (configDir !== projectRoot || projectRoot === '.') {
const eslint = getEslint(projectRoot);
for (const file of lintableFilesPerProjectRoot.get(projectRoot) ?? []) {
if (!(await eslint.isPathIgnored((0, posix_1.join)(context.workspaceRoot, file)))) {
hasNonIgnoredLintableFiles = true;
break;
}
}
}
else {
hasNonIgnoredLintableFiles = true;
}
if (!hasNonIgnoredLintableFiles) {
// No lintable files in the project, store in the cache and skip further processing
projectsCache[hash] = {};
return null;
}
const project = getProjectUsingESLintConfig(configFilePath, projectRoot, eslintVersion, options, context, pmc, tsconfigChainsByProjectRoot.get(projectRoot) ?? []);
if (project) {
const entry = { [projectRoot]: project };
// Store project into the cache
projectsCache[hash] = entry;
return entry;
}
// No project found, store in the cache
projectsCache[hash] = {};
return null;
}));
const projects = {};
for (const contribution of contributions) {
if (contribution) {
Object.assign(projects, contribution);
}
}
return {
projects,
};
};
exports.createNodes = [
ESLINT_CONFIG_GLOB_V2,
async (configFiles, options, context) => {
options = normalizeOptions(options);
const pmc = (0, devkit_1.getPackageManagerCommand)((0, devkit_1.detectPackageManager)(context.workspaceRoot));
const optionsHash = (0, file_hasher_1.hashObject)(options);
const cachePath = (0, posix_1.join)(cache_directory_1.workspaceDataDirectory, `eslint-${optionsHash}.hash`);
const targetsCache = readTargetsCache(cachePath);
const { eslintConfigFiles, projectRoots, projectRootsByEslintRoots } = splitConfigFiles(configFiles);
const lintableFilesPerProjectRoot = await collectLintableFilesByProjectRoot(projectRoots, options, context);
const tsconfigChainsByProjectRoot = collectTsconfigChainsByProjectRoot(projectRoots, context.workspaceRoot);
const lockFilePattern = (0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot));
const hashes = await (0, calculate_hash_for_create_nodes_1.calculateHashesForCreateNodes)(projectRoots, options, context, projectRoots.map((root) => {
const parentConfigs = eslintConfigFiles.filter((eslintConfig) => isSubDir(root, (0, posix_1.dirname)(eslintConfig)));
return [
...parentConfigs,
(0, posix_1.join)(root, '.eslintignore'),
lockFilePattern,
...(tsconfigChainsByProjectRoot.get(root) ?? []),
];
}));
const hashByRoot = new Map(projectRoots.map((r, i) => [r, hashes[i]]));
try {
if (eslintConfigFiles.length === 0) {
return [];
}
// Determine flat vs legacy from root config, matching ESLint's own
// behavior (find-up from cwd). Nested .eslintrc.* files are irrelevant
// when a root flat config exists. Prefer flat config at root when both
// flat and legacy root configs coexist (e.g., mid-migration).
const rootConfigs = eslintConfigFiles.filter((f) => (0, posix_1.dirname)(f) === '.');
const rootConfig = rootConfigs.find(config_file_1.isFlatConfig) ?? rootConfigs[0];
const ESLint = await (0, resolve_eslint_class_1.resolveESLintClass)({
useFlatConfigOverrideVal: (0, config_file_1.isFlatConfig)(rootConfig ?? eslintConfigFiles[0]),
});
return await (0, devkit_1.createNodesFromFiles)((configFile, options, context) => internalCreateNodesV2(ESLint, configFile, options, context, projectRootsByEslintRoots, lintableFilesPerProjectRoot, tsconfigChainsByProjectRoot, targetsCache, hashByRoot, pmc), eslintConfigFiles, options, context);
}
finally {
writeTargetsToCache(cachePath, targetsCache);
}
},
];
exports.createNodesV2 = exports.createNodes;
function splitConfigFiles(configFiles) {
const eslintConfigFiles = [];
const projectRoots = new Set();
for (const configFile of configFiles) {
if (PROJECT_CONFIG_FILENAMES.includes((0, posix_1.basename)(configFile))) {
projectRoots.add((0, posix_1.dirname)(configFile));
}
else {
eslintConfigFiles.push(configFile);
}
}
const uniqueProjectRoots = Array.from(projectRoots);
const projectRootsByEslintRoots = groupProjectRootsByEslintRoots(eslintConfigFiles, uniqueProjectRoots);
return {
eslintConfigFiles,
projectRoots: uniqueProjectRoots,
projectRootsByEslintRoots,
};
}
function groupProjectRootsByEslintRoots(eslintConfigFiles, projectRoots) {
const projectRootsByEslintRoots = new Map();
for (const eslintConfig of eslintConfigFiles) {
projectRootsByEslintRoots.set((0, posix_1.dirname)(eslintConfig), []);
}
for (const projectRoot of projectRoots) {
const eslintRoot = getRootForDirectory(projectRoot, projectRootsByEslintRoots);
if (eslintRoot) {
projectRootsByEslintRoots.get(eslintRoot).push(projectRoot);
}
}
return projectRootsByEslintRoots;
}
/**
* For each project root that has a `tsconfig.json`, resolves its `extends`
* chain and returns the workspace-relative paths of every reachable file
* that lives OUTSIDE the project root. Files inside the project root are
* already covered by `default` (`{projectRoot}/**\/*`); files resolved
* inside `node_modules` are invalidated via the lockfile; files that
* escape the workspace cannot be expressed as `{workspaceRoot}/...`.
*
* Root projects (`.`) are skipped — everything reachable from a root
* project's tsconfig is inside the project root by definition.
*/
function collectTsconfigChainsByProjectRoot(projectRoots, workspaceRoot) {
const jsonCache = new Map();
const result = new Map();
// The root tsconfig (tsconfig.base.json or tsconfig.json) is already
// handled by the native selective hasher (TsConfiguration hash
// instruction) which only hashes the path aliases relevant to each
// project. Adding it as an explicit file input would bypass that
// optimization and cause every project to be affected on any change.
const rootTsConfigName = (0, js_1.getRootTsConfigFileName)();
for (const projectRoot of projectRoots) {
if (projectRoot === '.')
continue;
const tsconfigPath = (0, posix_1.join)(projectRoot, 'tsconfig.json');
if (!(0, node_fs_1.existsSync)((0, posix_1.join)(workspaceRoot, tsconfigPath)))
continue;
const outside = [];
const projectPrefix = `${projectRoot}/`;
(0, internal_1.walkTsconfigExtendsChain)((0, posix_1.join)(workspaceRoot, tsconfigPath), (absolutePath) => {
const wsRelative = (0, node_path_1.relative)(workspaceRoot, absolutePath)
.split(node_path_1.sep)
.join('/');
if (wsRelative.startsWith('../') || wsRelative === '..') {
return 'continue'; // escapes workspace
}
if (wsRelative.startsWith('node_modules/') ||
wsRelative.includes('/node_modules/')) {
return 'continue'; // external package, lockfile invalidates
}
if (wsRelative === projectRoot ||
wsRelative.startsWith(projectPrefix)) {
return 'continue'; // inside project root, covered by `default`
}
if (wsRelative === rootTsConfigName) {
return 'continue'; // handled by native selective hasher
}
outside.push(wsRelative);
return 'continue';
}, { jsonCache });
result.set(projectRoot, outside);
}
return result;
}
async function collectLintableFilesByProjectRoot(projectRoots, options, context) {
const lintableFilesPerProjectRoot = new Map();
const lintableFiles = await (0, workspace_context_1.globWithWorkspaceContext)(context.workspaceRoot, [
`**/*.{${options.extensions.join(',')}}`,
]);
for (const projectRoot of projectRoots) {
lintableFilesPerProjectRoot.set(projectRoot, []);
}
for (const file of lintableFiles) {
const projectRoot = getRootForDirectory((0, posix_1.dirname)(file), lintableFilesPerProjectRoot);
if (projectRoot) {
lintableFilesPerProjectRoot.get(projectRoot).push(file);
}
}
return lintableFilesPerProjectRoot;
}
function getRootForDirectory(directory, roots) {
let currentPath = (0, posix_1.normalize)(directory);
while (currentPath !== (0, posix_1.dirname)(currentPath)) {
if (roots.has(currentPath)) {
return currentPath;
}
currentPath = (0, posix_1.dirname)(currentPath);
}
return roots.has(currentPath) ? currentPath : null;
}
function getProjectUsingESLintConfig(configFilePath, projectRoot, eslintVersion, options, context, pmc, tsconfigChainOutsideProjectRoot) {
const rootEslintConfig = [
config_file_1.baseEsLintConfigFile,
...config_file_1.BASE_ESLINT_CONFIG_FILENAMES,
...config_file_1.ESLINT_CONFIG_FILENAMES,
].find((f) => (0, node_fs_1.existsSync)((0, posix_1.join)(context.workspaceRoot, f)));
// Add a lint target for each child project without an eslint config, with the root level config as an input
let standaloneSrcPath;
if (projectRoot === '.' &&
(0, node_fs_1.existsSync)((0, posix_1.join)(context.workspaceRoot, projectRoot, 'package.json'))) {
if ((0, node_fs_1.existsSync)((0, posix_1.join)(context.workspaceRoot, projectRoot, 'src'))) {
standaloneSrcPath = 'src';
}
else if ((0, node_fs_1.existsSync)((0, posix_1.join)(context.workspaceRoot, projectRoot, 'lib'))) {
standaloneSrcPath = 'lib';
}
}
if (projectRoot === '.' && !standaloneSrcPath) {
return null;
}
const eslintConfigs = [configFilePath];
if (rootEslintConfig && !eslintConfigs.includes(rootEslintConfig)) {
eslintConfigs.unshift(rootEslintConfig);
}
return {
targets: buildEslintTargets(eslintConfigs, eslintVersion, projectRoot, context.workspaceRoot, options, pmc, standaloneSrcPath, tsconfigChainOutsideProjectRoot),
};
}
function buildEslintTargets(eslintConfigs, eslintVersion, projectRoot, workspaceRoot, options, pmc, standaloneSrcPath, tsconfigChainOutsideProjectRoot = []) {
const isRootProject = projectRoot === '.';
const targets = {};
const targetConfig = {
command: `eslint ${isRootProject && standaloneSrcPath ? `./${standaloneSrcPath}` : '.'}`,
cache: true,
options: {
cwd: projectRoot,
},
inputs: [
'default',
// Certain lint rules can be impacted by changes to dependencies
'^default',
...eslintConfigs.map((config) => `{workspaceRoot}/${config}`),
...((0, node_fs_1.existsSync)((0, posix_1.join)(workspaceRoot, projectRoot, '.eslintignore'))
? [(0, posix_1.join)('{workspaceRoot}', projectRoot, '.eslintignore')]
: []),
// Tsconfig files reached via `extends` that live outside the project
// root — declared so the cache invalidates on upstream changes.
...tsconfigChainOutsideProjectRoot.map((file) => `{workspaceRoot}/${file}`),
'{workspaceRoot}/tools/eslint-rules/**/*',
{ externalDependencies: ['eslint'] },
],
outputs: ['{options.outputFile}'],
metadata: {
technologies: ['eslint'],
description: 'Runs ESLint on project',
help: {
command: `${pmc.exec} eslint --help`,
example: {
options: {
'max-warnings': 0,
},
},
},
},
};
// Always set the environment variable to ensure that the ESLint CLI can run on eslint v8 and v9
const useFlatConfig = eslintConfigs.some((config) => (0, config_file_1.isFlatConfig)(config));
// Flat config is default for 9.0.0+
const defaultSetting = (0, semver_1.gte)(eslintVersion, '9.0.0');
if (useFlatConfig !== defaultSetting) {
targetConfig.options.env = {
ESLINT_USE_FLAT_CONFIG: useFlatConfig ? 'true' : 'false',
};
}
targets[options.targetName] = targetConfig;
return targets;
}
function normalizeOptions(options) {
const normalizedOptions = {
targetName: options?.targetName ?? 'lint',
};
// Normalize user input for extensions (strip leading . characters)
if (Array.isArray(options?.extensions)) {
normalizedOptions.extensions = options.extensions.map((f) => f.replace(/^\.+/, ''));
}
else {
normalizedOptions.extensions = DEFAULT_EXTENSIONS;
}
return normalizedOptions;
}
/**
* Determines if `child` is a subdirectory of `parent`. This is a simplified
* version that takes into account that paths are always relative to the
* workspace root.
*/
function isSubDir(parent, child) {
if (parent === '.') {
return true;
}
parent = (0, posix_1.normalize)(parent);
child = (0, posix_1.normalize)(child);
if (!parent.endsWith(posix_1.sep)) {
parent += posix_1.sep;
}
return child.startsWith(parent);
}

11
node_modules/@nx/eslint/src/utils/config-file.d.ts generated vendored Normal file
View File

@@ -0,0 +1,11 @@
export declare const ESLINT_FLAT_CONFIG_FILENAMES: string[];
export declare const ESLINT_OLD_CONFIG_FILENAMES: string[];
export declare const ESLINT_CONFIG_FILENAMES: string[];
export declare const BASE_ESLINT_CONFIG_FILENAMES: string[];
export declare const baseEsLintConfigFile = ".eslintrc.base.json";
export declare const baseEsLintFlatConfigFile = "eslint.base.config.mjs";
export declare const legacyBaseEsLintFlatConfigFile = "eslint.base.config.js";
export declare function isFlatConfig(configFilePath: string): boolean;
export declare function findFlatConfigFile(directory: string, workspaceRoot: string): string | null;
export declare function findOldConfigFile(filePathOrDirectory: string, workspaceRoot: string): string | null;
//# sourceMappingURL=config-file.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"config-file.d.ts","sourceRoot":"","sources":["../../../../../packages/eslint/src/utils/config-file.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,4BAA4B,UAA4B,CAAC;AAEtE,eAAO,MAAM,2BAA2B,UAOvC,CAAC;AAEF,eAAO,MAAM,uBAAuB,UAGnC,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAA4B,CAAC;AAEtE,eAAO,MAAM,oBAAoB,wBAAwB,CAAC;AAC1D,eAAO,MAAM,wBAAwB,2BAA2B,CAAC;AAEjE,eAAO,MAAM,8BAA8B,0BAA0B,CAAC;AAEtE,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAI5D;AAGD,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,IAAI,CAkBf;AAED,wBAAgB,iBAAiB,CAC/B,mBAAmB,EAAE,MAAM,EAC3B,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,IAAI,CAqBf"}

72
node_modules/@nx/eslint/src/utils/config-file.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.legacyBaseEsLintFlatConfigFile = exports.baseEsLintFlatConfigFile = exports.baseEsLintConfigFile = exports.BASE_ESLINT_CONFIG_FILENAMES = exports.ESLINT_CONFIG_FILENAMES = exports.ESLINT_OLD_CONFIG_FILENAMES = exports.ESLINT_FLAT_CONFIG_FILENAMES = void 0;
exports.isFlatConfig = isFlatConfig;
exports.findFlatConfigFile = findFlatConfigFile;
exports.findOldConfigFile = findOldConfigFile;
const fs_1 = require("fs");
const path_1 = require("path");
const flat_config_1 = require("./flat-config");
exports.ESLINT_FLAT_CONFIG_FILENAMES = flat_config_1.eslintFlatConfigFilenames;
exports.ESLINT_OLD_CONFIG_FILENAMES = [
'.eslintrc',
'.eslintrc.js',
'.eslintrc.cjs',
'.eslintrc.yaml',
'.eslintrc.yml',
'.eslintrc.json',
];
exports.ESLINT_CONFIG_FILENAMES = [
...exports.ESLINT_OLD_CONFIG_FILENAMES,
...exports.ESLINT_FLAT_CONFIG_FILENAMES,
];
exports.BASE_ESLINT_CONFIG_FILENAMES = flat_config_1.baseEslintConfigFilenames;
exports.baseEsLintConfigFile = '.eslintrc.base.json';
exports.baseEsLintFlatConfigFile = 'eslint.base.config.mjs';
// Make sure we can handle previous file extension as well for migrations or custom generators.
exports.legacyBaseEsLintFlatConfigFile = 'eslint.base.config.js';
function isFlatConfig(configFilePath) {
const configFileName = (0, path_1.basename)(configFilePath);
return exports.ESLINT_FLAT_CONFIG_FILENAMES.includes(configFileName);
}
// https://eslint.org/docs/latest/use/configure/configuration-files#configuration-file-resolution
function findFlatConfigFile(directory, workspaceRoot) {
let currentDir = (0, path_1.resolve)(workspaceRoot, directory);
while (true) {
const configFilePath = getConfigFileInDirectory(currentDir, exports.ESLINT_FLAT_CONFIG_FILENAMES);
if (configFilePath) {
return configFilePath;
}
if (currentDir === workspaceRoot) {
break;
}
currentDir = (0, path_1.dirname)(currentDir);
}
return null;
}
function findOldConfigFile(filePathOrDirectory, workspaceRoot) {
let currentDir = (0, path_1.resolve)(workspaceRoot, filePathOrDirectory);
if (!(0, fs_1.statSync)(currentDir).isDirectory()) {
currentDir = (0, path_1.dirname)(currentDir);
}
while (true) {
const configFilePath = getConfigFileInDirectory(currentDir, exports.ESLINT_OLD_CONFIG_FILENAMES);
if (configFilePath) {
return configFilePath;
}
if (currentDir === workspaceRoot) {
break;
}
currentDir = (0, path_1.dirname)(currentDir);
}
return null;
}
function getConfigFileInDirectory(directory, candidateFileNames) {
for (const filename of candidateFileNames) {
const filePath = (0, path_1.join)(directory, filename);
if ((0, fs_1.existsSync)(filePath)) {
return filePath;
}
}
return null;
}

6
node_modules/@nx/eslint/src/utils/flat-config.d.ts generated vendored Normal file
View File

@@ -0,0 +1,6 @@
import { Tree } from '@nx/devkit';
export declare const eslintFlatConfigFilenames: string[];
export declare const baseEslintConfigFilenames: string[];
export declare function getRootESLintFlatConfigFilename(tree: Tree): string;
export declare function useFlatConfig(tree?: Tree): boolean;
//# sourceMappingURL=flat-config.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"flat-config.d.ts","sourceRoot":"","sources":["../../../../../packages/eslint/src/utils/flat-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGlC,eAAO,MAAM,yBAAyB,UAOrC,CAAC;AAEF,eAAO,MAAM,yBAAyB,UASrC,CAAC;AAEF,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAOlE;AAED,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,OAAO,CA6BlD"}

60
node_modules/@nx/eslint/src/utils/flat-config.js generated vendored Normal file
View File

@@ -0,0 +1,60 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.baseEslintConfigFilenames = exports.eslintFlatConfigFilenames = void 0;
exports.getRootESLintFlatConfigFilename = getRootESLintFlatConfigFilename;
exports.useFlatConfig = useFlatConfig;
const semver_1 = require("semver");
exports.eslintFlatConfigFilenames = [
'eslint.config.cjs',
'eslint.config.js',
'eslint.config.mjs',
'eslint.config.cts',
'eslint.config.ts',
'eslint.config.mts',
];
exports.baseEslintConfigFilenames = [
'eslint.base.js',
'eslint.base.ts',
'eslint.base.config.cjs',
'eslint.base.config.js',
'eslint.base.config.mjs',
'eslint.base.config.cts',
'eslint.base.config.ts',
'eslint.base.config.mts',
];
function getRootESLintFlatConfigFilename(tree) {
for (const file of exports.eslintFlatConfigFilenames) {
if (tree.exists(file)) {
return file;
}
}
throw new Error('Could not find root flat config file');
}
function useFlatConfig(tree) {
// Prioritize taking ESLint's own environment variable into account when determining if we should use flat config
// If it is not defined, then default to true.
if (process.env.ESLINT_USE_FLAT_CONFIG === 'true') {
return true;
}
else if (process.env.ESLINT_USE_FLAT_CONFIG === 'false') {
return false;
}
// If we find an existing flat config file in the root of the provided tree, we should use flat config
if (tree) {
const hasRootFlatConfig = exports.eslintFlatConfigFilenames.some((filename) => tree.exists(filename));
if (hasRootFlatConfig) {
return true;
}
}
// Otherwise fallback to checking the installed eslint version
try {
const { ESLint } = require('eslint');
// Default to any v8 version to compare against in this case as it implies a much older version of ESLint was found (and gte() requires a valid version)
const eslintVersion = ESLint.version || '8.0.0';
return (0, semver_1.gte)(eslintVersion, '9.0.0');
}
catch {
// Default to assuming flat config in case ESLint is not yet installed
return true;
}
}

Some files were not shown because too many files have changed in this diff Show More