Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"@types/cytoscape": "^3.18.2",
"@types/detect-port": "^1.3.2",
"@types/ejs": "3.1.2",
"@types/eslint": "~8.44.2",
"@types/eslint": "~8.56.10",
"@types/express": "4.17.14",
"@types/flat": "^5.0.1",
"@types/fs-extra": "^11.0.0",
Expand Down
22 changes: 13 additions & 9 deletions packages/eslint-plugin/src/rules/dependency-checks.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { join } from 'path';
import { satisfies } from 'semver';
import { NX_VERSION, normalizePath, workspaceRoot } from '@nx/devkit';
import { findNpmDependencies } from '@nx/js/src/utils/find-npm-dependencies';
import { ESLintUtils } from '@typescript-eslint/utils';
import { AST } from 'jsonc-eslint-parser';
import { type JSONLiteral } from 'jsonc-eslint-parser/lib/parser/ast';
import { normalizePath, workspaceRoot, NX_VERSION } from '@nx/devkit';
import { findNpmDependencies } from '@nx/js/src/utils/find-npm-dependencies';
import { readProjectGraph } from '../utils/project-graph-utils';
import { findProject, getSourceFilePath } from '../utils/runtime-lint-utils';
import { join } from 'path';
import { satisfies } from 'semver';
import {
getAllDependencies,
getPackageJson,
getProductionDependencies,
} from '../utils/package-json-utils';
import { ESLintUtils } from '@typescript-eslint/utils';
import { readProjectGraph } from '../utils/project-graph-utils';
import {
findProject,
getParserServices,
getSourceFilePath,
} from '../utils/runtime-lint-utils';

export type Options = [
{
Expand Down Expand Up @@ -96,10 +100,10 @@ export default ESLintUtils.RuleCreator(
},
]
) {
if (!(context.parserServices as any).isJSON) {
if (!getParserServices(context).isJSON) {
return {};
}
const fileName = normalizePath(context.getFilename());
const fileName = normalizePath(context.filename ?? context.getFilename());
// support only package.json
if (!fileName.endsWith('/package.json')) {
return {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export default ESLintUtils.RuleCreator(
const projectPath = normalizePath(
(global as any).projectPath || workspaceRoot
);
const fileName = normalizePath(context.getFilename());
const fileName = normalizePath(context.filename ?? context.getFilename());

const {
projectGraph,
Expand Down
22 changes: 13 additions & 9 deletions packages/eslint-plugin/src/rules/nx-plugin-checks.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import type { AST } from 'jsonc-eslint-parser';
import type { TSESLint } from '@typescript-eslint/utils';
import { ESLintUtils } from '@typescript-eslint/utils';
import type { AST } from 'jsonc-eslint-parser';

import {
ProjectGraphProjectNode,
readJsonFile,
workspaceRoot,
} from '@nx/devkit';
import { findProject, getSourceFilePath } from '../utils/runtime-lint-utils';
import { existsSync } from 'fs';
import { getRootTsConfigPath } from '@nx/js';
import { registerTsProject } from '@nx/js/src/internal';
import { existsSync } from 'fs';
import * as path from 'path';
import { readProjectGraph } from '../utils/project-graph-utils';
import { valid } from 'semver';
import { getRootTsConfigPath } from '@nx/js';
import { readProjectGraph } from '../utils/project-graph-utils';
import {
findProject,
getParserServices,
getSourceFilePath,
} from '../utils/runtime-lint-utils';

type Options = [
{
Expand Down Expand Up @@ -113,14 +117,14 @@ export default ESLintUtils.RuleCreator(() => ``)<Options, MessageIds>({
defaultOptions: [DEFAULT_OPTIONS],
create(context) {
// jsonc-eslint-parser adds this property to parserServices where appropriate
if (!(context.parserServices as any).isJSON) {
if (!getParserServices(context).isJSON) {
return {};
}

const { projectGraph, projectRootMappings } = readProjectGraph(RULE_NAME);

const sourceFilePath = getSourceFilePath(
context.getFilename(),
context.filename ?? context.getFilename(),
workspaceRoot
);

Expand Down Expand Up @@ -301,7 +305,7 @@ export function validateEntry(
});
} else {
const schemaFilePath = path.join(
path.dirname(context.getFilename()),
path.dirname(context.filename ?? context.getFilename()),
schemaNode.value.value
);
if (!existsSync(schemaFilePath)) {
Expand Down Expand Up @@ -399,7 +403,7 @@ export function validateImplemenationNode(
let resolvedPath: string;

const modulePath = path.join(
path.dirname(context.getFilename()),
path.dirname(context.filename ?? context.getFilename()),
implementationPath
);

Expand Down
40 changes: 29 additions & 11 deletions packages/eslint-plugin/src/utils/runtime-lint-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import * as path from 'path';
import { join } from 'path';
import {
DependencyType,
joinPathFragments,
Expand All @@ -11,18 +9,19 @@ import {
ProjectGraphProjectNode,
workspaceRoot,
} from '@nx/devkit';
import { getPath, pathExists } from './graph-utils';
import { readFileIfExisting } from 'nx/src/utils/fileutils';
import {
findProjectForPath,
ProjectRootMappings,
} from 'nx/src/project-graph/utils/find-project-for-path';
import { getRootTsConfigFileName } from '@nx/js';
import {
resolveModuleByImport,
TargetProjectLocator,
} from '@nx/js/src/internal';
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils';
import { AST_NODE_TYPES, TSESLint, TSESTree } from '@typescript-eslint/utils';
import * as path from 'node:path';
import {
findProjectForPath,
ProjectRootMappings,
} from 'nx/src/project-graph/utils/find-project-for-path';
import { readFileIfExisting } from 'nx/src/utils/fileutils';
import { getPath, pathExists } from './graph-utils';

export type Deps = { [projectName: string]: ProjectGraphDependency[] };
type SingleSourceTagConstraint = {
Expand Down Expand Up @@ -393,7 +392,7 @@ function packageExistsInPackageJson(
projectRoot: string
): boolean {
const content = readFileIfExisting(
join(workspaceRoot, projectRoot, 'package.json')
path.join(workspaceRoot, projectRoot, 'package.json')
);
if (content) {
const { dependencies, devDependencies, peerDependencies } =
Expand Down Expand Up @@ -499,7 +498,7 @@ export function belongsToDifferentNgEntryPoint(
const resolvedImportFile = resolveModuleByImport(
importExpr,
filePath, // not strictly necessary, but speeds up resolution
join(workspaceRoot, getRootTsConfigFileName())
path.join(workspaceRoot, getRootTsConfigFileName())
);

if (!resolvedImportFile) {
Expand Down Expand Up @@ -560,3 +559,22 @@ export function appIsMFERemote(project: ProjectGraphProjectNode): boolean {

return false;
}

/**
* parserServices moved from the context object to the nested sourceCode object in v8,
* and was removed from its original location in v9.
*/
export function getParserServices(
context: Readonly<TSESLint.RuleContext<any, any>>
): any {
if (context.sourceCode && context.sourceCode.parserServices) {
return context.sourceCode.parserServices;
}
const parserServices = context.parserServices;
if (!parserServices) {
throw new Error(
'Parser Services are not available, please check your ESLint configuration'
);
}
return parserServices;
}
2 changes: 1 addition & 1 deletion packages/eslint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"dependencies": {
"@nx/devkit": "file:../devkit",
"@nx/js": "file:../js",
"eslint": "^8.0.0",
"eslint": "^8.0.0 || ^9.0.0",
"tslib": "^2.3.0",
"typescript": "~5.4.2"
},
Expand Down
16 changes: 1 addition & 15 deletions packages/eslint/src/executors/lint/utility/eslint-utils.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
import type { ESLint } from 'eslint';
import { resolveESLintClass } from '../../../utils/resolve-eslint-class';
import type { Schema } from '../schema';

async function resolveESLintClass(
useFlatConfig = false
): Promise<typeof ESLint> {
try {
if (!useFlatConfig) {
return (await import('eslint')).ESLint;
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { FlatESLint } = require('eslint/use-at-your-own-risk');
return FlatESLint;
} catch {
throw new Error('Unable to find ESLint. Ensure ESLint is installed.');
}
}

export async function resolveAndInstantiateESLint(
eslintConfigPath: string | undefined,
options: Schema,
Expand Down
27 changes: 27 additions & 0 deletions packages/eslint/src/plugins/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ describe('@nx/eslint/plugin', () => {
],
"options": {
"cwd": ".",
"env": {
"ESLINT_USE_FLAT_CONFIG": "false",
},
},
"outputs": [
"{options.outputFile}",
Expand Down Expand Up @@ -180,6 +183,9 @@ describe('@nx/eslint/plugin', () => {
],
"options": {
"cwd": "apps/my-app",
"env": {
"ESLINT_USE_FLAT_CONFIG": "false",
},
},
"outputs": [
"{options.outputFile}",
Expand Down Expand Up @@ -221,6 +227,9 @@ describe('@nx/eslint/plugin', () => {
],
"options": {
"cwd": "apps/my-app",
"env": {
"ESLINT_USE_FLAT_CONFIG": "false",
},
},
"outputs": [
"{options.outputFile}",
Expand Down Expand Up @@ -334,6 +343,9 @@ describe('@nx/eslint/plugin', () => {
],
"options": {
"cwd": "apps/my-app",
"env": {
"ESLINT_USE_FLAT_CONFIG": "false",
},
},
"outputs": [
"{options.outputFile}",
Expand All @@ -359,6 +371,9 @@ describe('@nx/eslint/plugin', () => {
],
"options": {
"cwd": "libs/my-lib",
"env": {
"ESLINT_USE_FLAT_CONFIG": "false",
},
},
"outputs": [
"{options.outputFile}",
Expand Down Expand Up @@ -444,6 +459,9 @@ describe('@nx/eslint/plugin', () => {
],
"options": {
"cwd": "apps/my-app",
"env": {
"ESLINT_USE_FLAT_CONFIG": "false",
},
},
"outputs": [
"{options.outputFile}",
Expand All @@ -470,6 +488,9 @@ describe('@nx/eslint/plugin', () => {
],
"options": {
"cwd": "libs/my-lib",
"env": {
"ESLINT_USE_FLAT_CONFIG": "false",
},
},
"outputs": [
"{options.outputFile}",
Expand Down Expand Up @@ -513,6 +534,9 @@ describe('@nx/eslint/plugin', () => {
],
"options": {
"cwd": "apps/myapp",
"env": {
"ESLINT_USE_FLAT_CONFIG": "false",
},
},
"outputs": [
"{options.outputFile}",
Expand Down Expand Up @@ -561,6 +585,9 @@ describe('@nx/eslint/plugin', () => {
],
"options": {
"cwd": "apps/myapp/nested/mylib",
"env": {
"ESLINT_USE_FLAT_CONFIG": "false",
},
},
"outputs": [
"{options.outputFile}",
Expand Down
27 changes: 8 additions & 19 deletions packages/eslint/src/plugins/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
CreateNodesResult,
TargetConfiguration,
} from '@nx/devkit';
import type { ESLint } from 'eslint';
import { existsSync } from 'node:fs';
import { dirname, join, normalize, sep } from 'node:path';
import { combineGlobPatterns } from 'nx/src/utils/globs';
Expand All @@ -15,6 +14,7 @@ import {
baseEsLintFlatConfigFile,
isFlatConfig,
} from '../utils/config-file';
import { resolveESLintClass } from '../utils/resolve-eslint-class';

export interface EslintPluginOptions {
targetName?: string;
Expand Down Expand Up @@ -66,7 +66,7 @@ export const createNodes: CreateNodes<EslintPluginOptions> = [
).sort((a, b) => (a !== b && isSubDir(a, b) ? -1 : 1));
const excludePatterns = dedupedProjectRoots.map((root) => `${root}/**/*`);

const ESLint = resolveESLintClass(isFlatConfig(configFilePath));
const ESLint = await resolveESLintClass(isFlatConfig(configFilePath));
const childProjectRoots = new Set<string>();

await Promise.all(
Expand Down Expand Up @@ -188,11 +188,12 @@ function buildEslintTargets(
],
outputs: ['{options.outputFile}'],
};
if (eslintConfigs.some((config) => isFlatConfig(config))) {
targetConfig.options.env = {
ESLINT_USE_FLAT_CONFIG: 'true',
};
}

// Always set the environment variable to ensure that the ESLint CLI can run on eslint v8 and v9
const useFlatConfig = eslintConfigs.some((config) => isFlatConfig(config));
targetConfig.options.env = {
ESLINT_USE_FLAT_CONFIG: useFlatConfig ? 'true' : 'false',
};

targets[options.targetName] = targetConfig;

Expand All @@ -213,18 +214,6 @@ function normalizeOptions(options: EslintPluginOptions): EslintPluginOptions {
return options;
}

function resolveESLintClass(useFlatConfig = false): typeof ESLint {
try {
if (!useFlatConfig) {
return require('eslint').ESLint;
}

return require('eslint/use-at-your-own-risk').FlatESLint;
} catch {
throw new Error('Unable to find ESLint. Ensure ESLint is installed.');
}
}

/**
* Determines if `child` is a subdirectory of `parent`. This is a simplified
* version that takes into account that paths are always relative to the
Expand Down
Loading