From 5c3ab1b82ed007443332f83004b54c2139d4d851 Mon Sep 17 00:00:00 2001 From: Miroslav Jonas Date: Fri, 18 Aug 2023 11:10:34 +0200 Subject: [PATCH] feat(linter): move move logic to linter --- .../src/generators/utils/eslint-file.ts | 93 ++++++++++++ .../move/lib/update-eslint-config.spec.ts | 2 + .../move/lib/update-eslint-config.ts | 133 ++---------------- 3 files changed, 109 insertions(+), 119 deletions(-) diff --git a/packages/linter/src/generators/utils/eslint-file.ts b/packages/linter/src/generators/utils/eslint-file.ts index e67a9a8be36bc..b4c06b5384d53 100644 --- a/packages/linter/src/generators/utils/eslint-file.ts +++ b/packages/linter/src/generators/utils/eslint-file.ts @@ -1,6 +1,7 @@ import { joinPathFragments, names, + offsetFromRoot, readJson, Tree, updateJson, @@ -59,6 +60,98 @@ export function isEslintConfigSupported(tree: Tree, projectRoot = ''): boolean { return eslintFile.endsWith('.json') || eslintFile.endsWith('.config.js'); } +export function updateRelativePathsInConfig( + tree: Tree, + sourcePath: string, + destinationPath: string +) { + if ( + sourcePath === destinationPath || + !isEslintConfigSupported(tree, destinationPath) + ) { + return; + } + + const configPath = joinPathFragments( + destinationPath, + findEslintFile(tree, destinationPath) + ); + const offset = offsetFromRoot(destinationPath); + + if (useFlatConfig(tree)) { + const config = tree.read(configPath, 'utf-8'); + tree.write( + configPath, + replaceFlatConfigPaths(config, sourcePath, offset, destinationPath) + ); + } else { + updateJson(tree, configPath, (json) => { + if (typeof json.extends === 'string') { + json.extends = offsetFilePath(sourcePath, json.extends, offset); + } else if (json.extends) { + json.extends = json.extends.map((extend: string) => + offsetFilePath(sourcePath, extend, offset) + ); + } + + json.overrides?.forEach( + (o: { parserOptions?: { project?: string | string[] } }) => { + 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: string, + sourceRoot: string, + offset: string, + destinationRoot: string +): string { + 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); + newConfig = + newConfig.slice(0, match.index) + + `require('${newPath}')` + + newConfig.slice(match.index + match[0].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: string, + pathToFile: string, + offset: string +): string { + if (!pathToFile.startsWith('..')) { + // not a relative path + return pathToFile; + } + return joinPathFragments(offset, projectRoot, pathToFile); +} + export function addOverrideToLintConfig( tree: Tree, root: string, diff --git a/packages/workspace/src/generators/move/lib/update-eslint-config.spec.ts b/packages/workspace/src/generators/move/lib/update-eslint-config.spec.ts index 4a2c70dd13302..425864a542e49 100644 --- a/packages/workspace/src/generators/move/lib/update-eslint-config.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-eslint-config.spec.ts @@ -248,6 +248,8 @@ describe('updateEslint (flat config)', () => { }; tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); + tree.delete('.eslintrc.json'); + tree.write('eslint.config.js', `module.exports = [];`); }); it('should handle config not existing', async () => { diff --git a/packages/workspace/src/generators/move/lib/update-eslint-config.ts b/packages/workspace/src/generators/move/lib/update-eslint-config.ts index 95c32996a13ad..d12963fc52817 100644 --- a/packages/workspace/src/generators/move/lib/update-eslint-config.ts +++ b/packages/workspace/src/generators/move/lib/update-eslint-config.ts @@ -1,36 +1,6 @@ -import { - joinPathFragments, - offsetFromRoot, - ProjectConfiguration, - Tree, - updateJson, -} from '@nx/devkit'; -import { join } from 'path'; +import { ensurePackage, ProjectConfiguration, Tree } from '@nx/devkit'; import { NormalizedSchema } from '../schema'; - -interface PartialEsLintrcOverride { - parserOptions?: { - project?: string[]; - }; -} - -interface PartialEsLintRcJson { - extends: string | string[]; - overrides?: PartialEsLintrcOverride[]; -} - -function offsetFilePath( - project: ProjectConfiguration, - pathToFile: string, - offset: string -): string { - if (!pathToFile.startsWith('..')) { - // not a relative path - return pathToFile; - } - const pathFromRoot = join(project.root, pathToFile); - return joinPathFragments(offset, pathFromRoot); -} +import { nxVersion } from '../../../utils/versions'; /** * Update the .eslintrc file of the project if it exists. @@ -42,93 +12,18 @@ export function updateEslintConfig( schema: NormalizedSchema, project: ProjectConfiguration ) { - const offset = offsetFromRoot(schema.relativeToRootDestination); - const eslintJsonPath = join( - schema.relativeToRootDestination, - '.eslintrc.json' - ); - - if (tree.exists(eslintJsonPath)) { - return updateJson( - tree, - eslintJsonPath, - (eslintRcJson) => { - if (typeof eslintRcJson.extends === 'string') { - eslintRcJson.extends = offsetFilePath( - project, - eslintRcJson.extends, - offset - ); - } else if (eslintRcJson.extends) { - eslintRcJson.extends = eslintRcJson.extends.map((extend: string) => - offsetFilePath(project, extend, offset) - ); - } - - eslintRcJson.overrides?.forEach( - (o: { parserOptions?: { project?: string | string[] } }) => { - if (o.parserOptions?.project) { - o.parserOptions.project = Array.isArray(o.parserOptions.project) - ? o.parserOptions.project.map((p) => - p.replace(project.root, schema.relativeToRootDestination) - ) - : o.parserOptions.project.replace( - project.root, - schema.relativeToRootDestination - ); - } - } - ); - return eslintRcJson; - } - ); + // if there is no suitable eslint config, we don't need to do anything + if (!tree.exists('.eslintrc.json') && !tree.exists('eslint.config.js')) { + return; } - - const eslintFlatPath = join( - schema.relativeToRootDestination, - 'eslint.config.js' + ensurePackage('@nx/linter', nxVersion); + const { + updateRelativePathsInConfig, + // nx-ignore-next-line + } = require('@nx/linter/src/generators/utils/eslint-file'); + updateRelativePathsInConfig( + tree, + project.root, + schema.relativeToRootDestination ); - if (tree.exists(eslintFlatPath)) { - const config = tree.read(eslintFlatPath, 'utf-8'); - tree.write( - eslintFlatPath, - replaceFlatConfigPaths( - config, - project, - offset, - schema.relativeToRootDestination - ) - ); - } -} - -function replaceFlatConfigPaths( - config: string, - project: ProjectConfiguration, - offset: string, - pathToDestination: string -): string { - let match; - let newConfig = config; - - // replace requires - const requireRegex = RegExp(/require\(['"](.*)['"]\)/g); - while ((match = requireRegex.exec(newConfig)) !== null) { - const newPath = offsetFilePath(project, match[1], offset); - newConfig = - newConfig.slice(0, match.index) + - `require('${newPath}')` + - newConfig.slice(match.index + match[0].length); - } - // replace projects - const projectRegex = RegExp(/project:\s?\[?['"](.*)['"]\]?/g); - while ((match = projectRegex.exec(newConfig)) !== null) { - const newProjectDef = match[0].replaceAll(project.root, pathToDestination); - newConfig = - newConfig.slice(0, match.index) + - newProjectDef + - newConfig.slice(match.index + match[0].length); - } - - return newConfig; }