diff --git a/packages/linter/migrations.json b/packages/linter/migrations.json index 118b9589b41dd..a8f39d348116d 100644 --- a/packages/linter/migrations.json +++ b/packages/linter/migrations.json @@ -65,6 +65,11 @@ "version": "16.0.0-beta.1", "description": "Replace @nrwl/linter with @nx/linter", "implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages" + }, + "update-16-8-0-add-ignored-files": { + "version": "16.8.0", + "description": "update-16-8-0-add-ignored-files", + "implementation": "./src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files" } }, "packageJsonUpdates": { diff --git a/packages/linter/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.spec.ts b/packages/linter/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.spec.ts new file mode 100644 index 0000000000000..0bc8b28b375cc --- /dev/null +++ b/packages/linter/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.spec.ts @@ -0,0 +1,193 @@ +import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; +import { addProjectConfiguration, readJson, Tree, writeJson } from '@nx/devkit'; + +import update from './update-16-8-0-add-ignored-files'; + +describe('update-16-8-0-add-ignored-files migration', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + }); + + it('should run successfully when eslint config is not present', async () => { + addProjectConfiguration(tree, 'my-pkg', { + root: 'packages/my-pkg', + sourceRoot: 'packages/my-pkg/src', + projectType: 'library', + targets: { + build: { + executor: '@nx/vite:build', + options: {}, + }, + }, + }); + + expect(() => update(tree)).not.toThrow(); + }); + + it.each` + executor | expectedPattern + ${'@nx/vite:build'} | ${'{projectRoot}/vite.config.{js,ts,mjs,mts}'} + ${'@nx/vite:test'} | ${'{projectRoot}/vite.config.{js,ts,mjs,mts}'} + ${'@nx/esbuild:esbuild'} | ${'{projectRoot}/esbuild.config.{js,ts,mjs,mts}'} + ${'@nx/rollup:rollup'} | ${'{projectRoot}/rollup.config.{js,ts,mjs,mts}'} + `( + 'should add ignoredFiles to projects using vite, esbuild, and rollup', + async ({ executor, expectedPattern }) => { + addProjectConfiguration(tree, 'my-pkg', { + root: 'packages/my-pkg', + sourceRoot: 'packages/my-pkg/src', + projectType: 'library', + targets: { + build: { + executor, + options: {}, + }, + }, + }); + writeJson(tree, `packages/my-pkg/.eslintrc.json`, { + root: true, + ignorePatterns: ['!**/*'], + plugins: ['@nx'], + overrides: [ + { + files: ['*.json'], + parser: 'jsonc-eslint-parser', + rules: { + '@nx/dependency-checks': 'error', + }, + }, + ], + }); + + update(tree); + + expect(readJson(tree, 'packages/my-pkg/.eslintrc.json')).toEqual({ + root: true, + ignorePatterns: ['!**/*'], + plugins: ['@nx'], + overrides: [ + { + files: ['*.json'], + parser: 'jsonc-eslint-parser', + rules: { + '@nx/dependency-checks': [ + 'error', + { + ignoredFiles: [expectedPattern], + }, + ], + }, + }, + ], + }); + } + ); + + it('should retain existing severity', () => { + addProjectConfiguration(tree, 'my-pkg', { + root: 'packages/my-pkg', + sourceRoot: 'packages/my-pkg/src', + projectType: 'library', + targets: { + build: { + executor: '@nx/vite:build', + options: {}, + }, + }, + }); + writeJson(tree, `packages/my-pkg/.eslintrc.json`, { + root: true, + ignorePatterns: ['!**/*'], + plugins: ['@nx'], + overrides: [ + { + files: ['*.json'], + parser: 'jsonc-eslint-parser', + rules: { + '@nx/dependency-checks': 'warn', + }, + }, + ], + }); + + update(tree); + + expect(readJson(tree, 'packages/my-pkg/.eslintrc.json')).toEqual({ + root: true, + ignorePatterns: ['!**/*'], + plugins: ['@nx'], + overrides: [ + { + files: ['*.json'], + parser: 'jsonc-eslint-parser', + rules: { + '@nx/dependency-checks': [ + 'warn', + { + ignoredFiles: ['{projectRoot}/vite.config.{js,ts,mjs,mts}'], + }, + ], + }, + }, + ], + }); + }); + + it('should retain existing options', () => { + addProjectConfiguration(tree, 'my-pkg', { + root: 'packages/my-pkg', + sourceRoot: 'packages/my-pkg/src', + projectType: 'library', + targets: { + build: { + executor: '@nx/vite:build', + options: {}, + }, + }, + }); + writeJson(tree, `packages/my-pkg/.eslintrc.json`, { + root: true, + ignorePatterns: ['!**/*'], + plugins: ['@nx'], + overrides: [ + { + files: ['*.json'], + parser: 'jsonc-eslint-parser', + rules: { + '@nx/dependency-checks': [ + 'error', + { + checkVersionMismatches: false, + }, + ], + }, + }, + ], + }); + + update(tree); + + expect(readJson(tree, 'packages/my-pkg/.eslintrc.json')).toEqual({ + root: true, + ignorePatterns: ['!**/*'], + plugins: ['@nx'], + overrides: [ + { + files: ['*.json'], + parser: 'jsonc-eslint-parser', + rules: { + '@nx/dependency-checks': [ + 'error', + { + checkVersionMismatches: false, + ignoredFiles: ['{projectRoot}/vite.config.{js,ts,mjs,mts}'], + }, + ], + }, + }, + ], + }); + }); +}); diff --git a/packages/linter/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.ts b/packages/linter/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.ts new file mode 100644 index 0000000000000..90fe479ebedec --- /dev/null +++ b/packages/linter/src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files.ts @@ -0,0 +1,74 @@ +import { getProjects, Tree } from '@nx/devkit'; +import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils'; +import { + findEslintFile, + lintConfigHasOverride, + updateOverrideInLintConfig, +} from '../../generators/utils/eslint-file'; + +// Add `ignoredFiles` pattern to projects using vite, esbuild, and rollup. +// This is needed because the @nx/dependency-checks lint rule will complain +// about dependencies used in build config files, even though they are not +// production dependencies. +export default function update(tree: Tree) { + const projects = getProjects(tree); + + const addIgnorePattern = + (ignorePattern: string) => (_options: unknown, projectName: string) => { + const project = projects.get(projectName); + if (!findEslintFile(tree, project.root)) return; + if ( + lintConfigHasOverride( + tree, + project.root, + (o) => + Array.isArray(o.files) + ? o.files.some((f) => f.match(/\.json$/)) + : !!o.files?.match(/\.json$/), + true + ) + ) { + updateOverrideInLintConfig( + tree, + project.root, + (o) => !!o.rules?.['@nx/dependency-checks'], + (o) => { + const value = o.rules['@nx/dependency-checks']; + let ruleSeverity: 0 | 1 | 2 | 'error' | 'warn' | 'off'; + let ruleOptions: any; + if (Array.isArray(value)) { + ruleSeverity = value[0]; + ruleOptions = value[1]; + } else { + ruleSeverity = value; + ruleOptions = {}; + } + ruleOptions.ignoredFiles = [ignorePattern]; + o.rules['@nx/dependency-checks'] = [ruleSeverity, ruleOptions]; + return o; + } + ); + } + }; + + forEachExecutorOptions( + tree, + '@nx/vite:build', + addIgnorePattern('{projectRoot}/vite.config.{js,ts,mjs,mts}') + ); + forEachExecutorOptions( + tree, + '@nx/vite:test', + addIgnorePattern('{projectRoot}/vite.config.{js,ts,mjs,mts}') + ); + forEachExecutorOptions( + tree, + '@nx/esbuild:esbuild', + addIgnorePattern('{projectRoot}/esbuild.config.{js,ts,mjs,mts}') + ); + forEachExecutorOptions( + tree, + '@nx/rollup:rollup', + addIgnorePattern('{projectRoot}/rollup.config.{js,ts,mjs,mts}') + ); +}