fix(vite): ignore vite temp files in eslint config (#29909)
## Current Behavior Vite config temp files can sometimes cause errors to be thrown by ESLint. ## Expected Behavior Vite config temp files should be ignored by ESLint. ## Related Issue(s) Fixes #
This commit is contained in:
parent
9e204f973c
commit
eb0505b1ad
@ -5530,6 +5530,16 @@
|
||||
"path": "/nx-api/vite/migrations/update-20-5-0-update-resolve-conditions",
|
||||
"type": "migration"
|
||||
},
|
||||
"/nx-api/vite/migrations/eslint-ignore-vite-temp-files": {
|
||||
"description": "Add vite config temporary files to the ESLint configuration ignore patterns if ESLint is used.",
|
||||
"file": "generated/packages/vite/migrations/eslint-ignore-vite-temp-files.json",
|
||||
"hidden": false,
|
||||
"name": "eslint-ignore-vite-temp-files",
|
||||
"version": "20.5.0-beta.3",
|
||||
"originalFilePath": "/packages/vite",
|
||||
"path": "/nx-api/vite/migrations/eslint-ignore-vite-temp-files",
|
||||
"type": "migration"
|
||||
},
|
||||
"/nx-api/vite/migrations/20.5.0-package-updates": {
|
||||
"description": "",
|
||||
"file": "generated/packages/vite/migrations/20.5.0-package-updates.json",
|
||||
|
||||
@ -5496,6 +5496,16 @@
|
||||
"path": "vite/migrations/update-20-5-0-update-resolve-conditions",
|
||||
"type": "migration"
|
||||
},
|
||||
{
|
||||
"description": "Add vite config temporary files to the ESLint configuration ignore patterns if ESLint is used.",
|
||||
"file": "generated/packages/vite/migrations/eslint-ignore-vite-temp-files.json",
|
||||
"hidden": false,
|
||||
"name": "eslint-ignore-vite-temp-files",
|
||||
"version": "20.5.0-beta.3",
|
||||
"originalFilePath": "/packages/vite",
|
||||
"path": "vite/migrations/eslint-ignore-vite-temp-files",
|
||||
"type": "migration"
|
||||
},
|
||||
{
|
||||
"description": "",
|
||||
"file": "generated/packages/vite/migrations/20.5.0-package-updates.json",
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "eslint-ignore-vite-temp-files",
|
||||
"version": "20.5.0-beta.3",
|
||||
"description": "Add vite config temporary files to the ESLint configuration ignore patterns if ESLint is used.",
|
||||
"implementation": "/packages/vite/src/migrations/update-20-5-0/eslint-ignore-vite-temp-files.ts",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/vite",
|
||||
"schema": null,
|
||||
"type": "migration",
|
||||
"examplesFile": "#### Sample Code Changes\n\nAdd `vite.config.*.timestamp*` and `vitest.config.*.timestamp*` to the root `eslint.config.mjs` file (using **ESLint Flat Config**).\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```js {% fileName=\"eslint.config.mjs\" %}\nexport default [\n {\n ignores: ['dist'],\n },\n];\n```\n\n{% /tab %}\n{% tab label=\"After\" %}\n\n```js {% highlightLines=[3] fileName=\"eslint.config.mjs\" %}\nexport default [\n {\n ignores: ['dist', 'vite.config.*.timestamp*', 'vitest.config.*.timestamp*'],\n },\n];\n```\n\n{% /tab %}\n\n{% /tabs %}\n\nAdd `vite.config.*.timestamp*` and `vitest.config.*.timestamp*` to the project's `.eslintrc.json` file (using **eslintrc** format config).\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"apps/app1/eslintrc.json\" %}\n{\n \"ignorePatterns\": [\"!**/*\"]\n}\n```\n\n{% /tab %}\n{% tab label=\"After\" %}\n\n```json {% highlightLines=[4,5] fileName=\"apps/app1/eslintrc.json\" %}\n{\n \"ignorePatterns\": [\n \"!**/*\",\n \"vite.config.*.timestamp*\",\n \"vitest.config.*.timestamp*\"\n ]\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n"
|
||||
}
|
||||
@ -7,6 +7,7 @@ import {
|
||||
} from '../../utils/config-file';
|
||||
import {
|
||||
addExtendsToLintConfig,
|
||||
addIgnoresToLintConfig,
|
||||
findEslintFile,
|
||||
lintConfigHasOverride,
|
||||
replaceOverridesInLintConfig,
|
||||
@ -526,4 +527,197 @@ module.exports = [
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addIgnoresToLintConfig', () => {
|
||||
it('should add a new block with ignores to esm flat config when there is none', () => {
|
||||
tree.write('eslint.config.mjs', 'export default [];');
|
||||
|
||||
addIgnoresToLintConfig(tree, '', ['**/some-dir/**/*']);
|
||||
|
||||
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
"**/some-dir/**/*"
|
||||
]
|
||||
}
|
||||
];
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should update existing block with ignores in esm flat config', () => {
|
||||
tree.write(
|
||||
'eslint.config.mjs',
|
||||
`export default [
|
||||
{
|
||||
ignores: ["dist"],
|
||||
}
|
||||
];
|
||||
`
|
||||
);
|
||||
|
||||
addIgnoresToLintConfig(tree, '', ['**/some-dir/**/*']);
|
||||
|
||||
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"export default [
|
||||
{
|
||||
"ignores": [
|
||||
"dist",
|
||||
"**/some-dir/**/*"
|
||||
]
|
||||
}
|
||||
];
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not duplicate existing patterns in a block with ignores in esm flat config', () => {
|
||||
tree.write(
|
||||
'eslint.config.mjs',
|
||||
`export default [
|
||||
{
|
||||
ignores: ["dist"],
|
||||
}
|
||||
];
|
||||
`
|
||||
);
|
||||
|
||||
addIgnoresToLintConfig(tree, '', ['**/some-dir/**/*', 'dist']);
|
||||
|
||||
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"export default [
|
||||
{
|
||||
"ignores": [
|
||||
"dist",
|
||||
"**/some-dir/**/*"
|
||||
]
|
||||
}
|
||||
];
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should add a new block with ignores to cjs flat config when there is none', () => {
|
||||
tree.write('eslint.config.cjs', 'module.exports = [];');
|
||||
|
||||
addIgnoresToLintConfig(tree, '', ['**/some-dir/**/*']);
|
||||
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"module.exports = [,
|
||||
{
|
||||
ignores: [
|
||||
"**/some-dir/**/*"
|
||||
]
|
||||
}];"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should update existing block with ignores in cjs flat config', () => {
|
||||
tree.write(
|
||||
'eslint.config.cjs',
|
||||
`module.exports = [
|
||||
{
|
||||
ignores: ["dist"],
|
||||
}
|
||||
];
|
||||
`
|
||||
);
|
||||
|
||||
addIgnoresToLintConfig(tree, '', ['**/some-dir/**/*']);
|
||||
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"module.exports = [
|
||||
{
|
||||
"ignores": [
|
||||
"dist",
|
||||
"**/some-dir/**/*"
|
||||
]
|
||||
}
|
||||
];
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not duplicate existing patterns in a block with ignores in cjs flat config', () => {
|
||||
tree.write(
|
||||
'eslint.config.cjs',
|
||||
`module.exports = [
|
||||
{
|
||||
ignores: ["dist"],
|
||||
}
|
||||
];
|
||||
`
|
||||
);
|
||||
|
||||
addIgnoresToLintConfig(tree, '', ['**/some-dir/**/*', 'dist']);
|
||||
|
||||
expect(tree.read('eslint.config.cjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"module.exports = [
|
||||
{
|
||||
"ignores": [
|
||||
"dist",
|
||||
"**/some-dir/**/*"
|
||||
]
|
||||
}
|
||||
];
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should add ignore patterns to eslintrc config when there is none', () => {
|
||||
tree.write('.eslintrc.json', '{}');
|
||||
|
||||
addIgnoresToLintConfig(tree, '', ['**/some-dir/**/*']);
|
||||
|
||||
expect(readJson(tree, '.eslintrc.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"**/some-dir/**/*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should update existing ignore patterns in eslintrc config', () => {
|
||||
tree.write(
|
||||
'.eslintrc.json',
|
||||
`{
|
||||
"ignorePatterns": ["dist"]
|
||||
}`
|
||||
);
|
||||
|
||||
addIgnoresToLintConfig(tree, '', ['**/some-dir/**/*']);
|
||||
|
||||
expect(readJson(tree, '.eslintrc.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"dist",
|
||||
"**/some-dir/**/*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not duplicate existing ignore patterns in eslintrc config', () => {
|
||||
tree.write(
|
||||
'.eslintrc.json',
|
||||
`{
|
||||
"ignorePatterns": ["dist"]
|
||||
}`
|
||||
);
|
||||
|
||||
addIgnoresToLintConfig(tree, '', ['**/some-dir/**/*', 'dist']);
|
||||
|
||||
expect(readJson(tree, '.eslintrc.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"dist",
|
||||
"**/some-dir/**/*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
baseEsLintConfigFile,
|
||||
ESLINT_CONFIG_FILENAMES,
|
||||
BASE_ESLINT_CONFIG_FILENAMES,
|
||||
ESLINT_FLAT_CONFIG_FILENAMES,
|
||||
} from '../../utils/config-file';
|
||||
import {
|
||||
eslintFlatConfigFilenames,
|
||||
@ -29,12 +30,14 @@ import {
|
||||
addBlockToFlatConfigExport,
|
||||
addFlatCompatToFlatConfig,
|
||||
addImportToFlatConfig,
|
||||
addPatternsToFlatConfigIgnoresBlock,
|
||||
addPluginsToExportsBlock,
|
||||
generateAst,
|
||||
generateFlatOverride,
|
||||
generateFlatPredefinedConfig,
|
||||
generatePluginExtendsElement,
|
||||
generatePluginExtendsElementWithCompatFixup,
|
||||
hasFlatConfigIgnoresBlock,
|
||||
hasOverride,
|
||||
overrideNeedsCompat,
|
||||
removeOverridesFromLintConfig,
|
||||
@ -609,15 +612,25 @@ export function addIgnoresToLintConfig(
|
||||
}
|
||||
}
|
||||
|
||||
if (!fileName) {
|
||||
return;
|
||||
}
|
||||
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
if (hasFlatConfigIgnoresBlock(content)) {
|
||||
content = addPatternsToFlatConfigIgnoresBlock(content, ignorePatterns);
|
||||
tree.write(fileName, content);
|
||||
} else {
|
||||
const block = generateAst<ts.ObjectLiteralExpression>({
|
||||
ignores: ignorePatterns.map((path) => mapFilePath(path)),
|
||||
});
|
||||
tree.write(
|
||||
fileName,
|
||||
addBlockToFlatConfigExport(tree.read(fileName, 'utf8'), block)
|
||||
);
|
||||
tree.write(fileName, addBlockToFlatConfigExport(content, block));
|
||||
}
|
||||
} else {
|
||||
const fileName = joinPathFragments(root, '.eslintrc.json');
|
||||
if (!tree.exists(fileName)) {
|
||||
return;
|
||||
}
|
||||
updateJson(tree, fileName, (json) => {
|
||||
const ignoreSet = new Set([
|
||||
...(json.ignorePatterns ?? []),
|
||||
|
||||
@ -75,6 +75,84 @@ function findModuleExports(source: ts.SourceFile): ts.NodeArray<ts.Node> {
|
||||
});
|
||||
}
|
||||
|
||||
export function addPatternsToFlatConfigIgnoresBlock(
|
||||
content: string,
|
||||
ignorePatterns: string[]
|
||||
): string {
|
||||
const source = ts.createSourceFile(
|
||||
'',
|
||||
content,
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
ts.ScriptKind.JS
|
||||
);
|
||||
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||
const exportsArray =
|
||||
format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
|
||||
if (!exportsArray) {
|
||||
return content;
|
||||
}
|
||||
const changes: StringChange[] = [];
|
||||
for (const node of exportsArray) {
|
||||
if (!isFlatConfigIgnoresBlock(node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const start = node.properties.pos + 1; // keep leading line break
|
||||
const data = parseTextToJson(node.getFullText());
|
||||
changes.push({
|
||||
type: ChangeType.Delete,
|
||||
start,
|
||||
length: node.properties.end - start,
|
||||
});
|
||||
data.ignores = Array.from(
|
||||
new Set([...(data.ignores ?? []), ...ignorePatterns])
|
||||
);
|
||||
changes.push({
|
||||
type: ChangeType.Insert,
|
||||
index: start,
|
||||
text:
|
||||
' ' +
|
||||
JSON.stringify(data, null, 2)
|
||||
.slice(2, -2) // Remove curly braces and start/end line breaks
|
||||
.replaceAll(/\n/g, '\n '), // Maintain indentation
|
||||
});
|
||||
break;
|
||||
}
|
||||
return applyChangesToString(content, changes);
|
||||
}
|
||||
|
||||
export function hasFlatConfigIgnoresBlock(content: string): boolean {
|
||||
const source = ts.createSourceFile(
|
||||
'',
|
||||
content,
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
ts.ScriptKind.JS
|
||||
);
|
||||
const format = content.includes('export default') ? 'mjs' : 'cjs';
|
||||
const exportsArray =
|
||||
format === 'mjs' ? findExportDefault(source) : findModuleExports(source);
|
||||
if (!exportsArray) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return exportsArray.some(isFlatConfigIgnoresBlock);
|
||||
}
|
||||
|
||||
function isFlatConfigIgnoresBlock(
|
||||
node: ts.Node
|
||||
): node is ts.ObjectLiteralExpression {
|
||||
return (
|
||||
ts.isObjectLiteralExpression(node) &&
|
||||
node.properties.length === 1 &&
|
||||
(node.properties[0].name.getText() === 'ignores' ||
|
||||
node.properties[0].name.getText() === '"ignores"') &&
|
||||
ts.isPropertyAssignment(node.properties[0]) &&
|
||||
ts.isArrayLiteralExpression(node.properties[0].initializer)
|
||||
);
|
||||
}
|
||||
|
||||
function isOverride(node: ts.Node): boolean {
|
||||
return (
|
||||
(ts.isObjectLiteralExpression(node) &&
|
||||
|
||||
@ -22,7 +22,14 @@ exports[`app generated files content - as-provided - my-app general application
|
||||
exports[`app generated files content - as-provided - my-app general application should configure eslint correctly (eslintrc) 1`] = `
|
||||
"{
|
||||
"extends": ["@nuxt/eslint-config", "../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*", ".nuxt/**", ".output/**", "node_modules"],
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
".nuxt/**",
|
||||
".output/**",
|
||||
"node_modules",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx", "*.vue"],
|
||||
@ -416,7 +423,14 @@ exports[`app generated files content - as-provided - myApp general application s
|
||||
exports[`app generated files content - as-provided - myApp general application should configure eslint correctly (eslintrc) 1`] = `
|
||||
"{
|
||||
"extends": ["@nuxt/eslint-config", "../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*", ".nuxt/**", ".output/**", "node_modules"],
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
".nuxt/**",
|
||||
".output/**",
|
||||
"node_modules",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx", "*.vue"],
|
||||
|
||||
@ -122,6 +122,9 @@ export async function applicationGeneratorInternal(
|
||||
tasks.push(twTask);
|
||||
}
|
||||
|
||||
const lintTask = await addLinting(tree, options);
|
||||
tasks.push(lintTask);
|
||||
|
||||
if (options.bundler === 'vite') {
|
||||
await setupViteConfiguration(tree, options, tasks);
|
||||
} else if (options.bundler === 'rsbuild') {
|
||||
@ -144,9 +147,6 @@ export async function applicationGeneratorInternal(
|
||||
);
|
||||
}
|
||||
|
||||
const lintTask = await addLinting(tree, options);
|
||||
tasks.push(lintTask);
|
||||
|
||||
const e2eTask = await addE2e(tree, options);
|
||||
tasks.push(e2eTask);
|
||||
|
||||
|
||||
@ -659,7 +659,13 @@ export default defineConfig({
|
||||
exports[`Remix Application Standalone Project Repo should create the application correctly 5`] = `
|
||||
"{
|
||||
"root": true,
|
||||
"ignorePatterns": ["!**/*", "build", "public/build"],
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"build",
|
||||
"public/build",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*"
|
||||
],
|
||||
"plugins": ["@nx"],
|
||||
"overrides": [
|
||||
{
|
||||
|
||||
@ -47,6 +47,66 @@ describe('Remix Application', () => {
|
||||
expect(tree.read('.eslintrc.json', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should ignore vite temp files', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
await applicationGenerator(tree, {
|
||||
name: 'test',
|
||||
directory: '.',
|
||||
addPlugin: true,
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
expect(tree.read('.gitignore', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"null
|
||||
.cache
|
||||
build
|
||||
public/build
|
||||
.env
|
||||
|
||||
vite.config.*.timestamp*
|
||||
vitest.config.*.timestamp*"
|
||||
`);
|
||||
expect(tree.read('.eslintrc.json', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"{
|
||||
"root": true,
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"build",
|
||||
"public/build",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*"
|
||||
],
|
||||
"plugins": [
|
||||
"@nx"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@nx/typescript"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@nx/javascript"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
describe('--unitTestRunner', () => {
|
||||
it('should generate the correct files for testing using vitest', async () => {
|
||||
// ARRANGE
|
||||
@ -177,6 +237,62 @@ describe('Remix Application', () => {
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should ignore vite temp files', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
await applicationGenerator(tree, {
|
||||
directory: 'test',
|
||||
addPlugin: true,
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
expect(tree.read('.gitignore', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"vite.config.*.timestamp*
|
||||
vitest.config.*.timestamp*"
|
||||
`);
|
||||
expect(tree.read(`${appDir}/.eslintrc.json`, 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"{
|
||||
"extends": [
|
||||
"../.eslintrc.json"
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"build",
|
||||
"public/build",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx",
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
describe('--directory', () => {
|
||||
it('should create the application correctly', async () => {
|
||||
// ARRANGE
|
||||
|
||||
@ -37,7 +37,7 @@ import initGenerator from '../init/init';
|
||||
import { updateDependencies } from '../utils/update-dependencies';
|
||||
import {
|
||||
addE2E,
|
||||
addViteTempFilesToGitIgnore,
|
||||
ignoreViteTempFiles,
|
||||
normalizeOptions,
|
||||
updateUnitTestConfig,
|
||||
} from './lib';
|
||||
@ -312,7 +312,7 @@ export default {...nxPreset};
|
||||
|
||||
tasks.push(await addE2E(tree, options));
|
||||
|
||||
addViteTempFilesToGitIgnore(tree);
|
||||
await ignoreViteTempFiles(tree, options.projectRoot);
|
||||
|
||||
updateTsconfigFiles(
|
||||
tree,
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
import { stripIndents, Tree } from '@nx/devkit';
|
||||
|
||||
export function addViteTempFilesToGitIgnore(tree: Tree) {
|
||||
let newGitIgnoreContents = `vite.config.*.timestamp*`;
|
||||
if (tree.exists('.gitignore')) {
|
||||
const gitIgnoreContents = tree.read('.gitignore', 'utf-8');
|
||||
if (!gitIgnoreContents.includes(newGitIgnoreContents)) {
|
||||
newGitIgnoreContents = stripIndents`${gitIgnoreContents}
|
||||
${newGitIgnoreContents}`;
|
||||
|
||||
tree.write('.gitignore', newGitIgnoreContents);
|
||||
}
|
||||
} else {
|
||||
tree.write('.gitignore', newGitIgnoreContents);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
import { ensurePackage, readJson, stripIndents, type Tree } from '@nx/devkit';
|
||||
import { getPackageVersion } from '../../../utils/versions';
|
||||
|
||||
export async function ignoreViteTempFiles(
|
||||
tree: Tree,
|
||||
projectRoot?: string | undefined
|
||||
): Promise<void> {
|
||||
addViteTempFilesToGitIgnore(tree);
|
||||
await ignoreViteTempFilesInEslintConfig(tree, projectRoot);
|
||||
}
|
||||
|
||||
function addViteTempFilesToGitIgnore(tree: Tree): void {
|
||||
let gitIgnoreContents = tree.exists('.gitignore')
|
||||
? tree.read('.gitignore', 'utf-8')
|
||||
: '';
|
||||
|
||||
if (!/^vite\.config\.\*\.timestamp\*$/m.test(gitIgnoreContents)) {
|
||||
gitIgnoreContents = stripIndents`${gitIgnoreContents}
|
||||
vite.config.*.timestamp*`;
|
||||
}
|
||||
if (!/^vitest\.config\.\*\.timestamp\*$/m.test(gitIgnoreContents)) {
|
||||
gitIgnoreContents = stripIndents`${gitIgnoreContents}
|
||||
vitest.config.*.timestamp*`;
|
||||
}
|
||||
|
||||
tree.write('.gitignore', gitIgnoreContents);
|
||||
}
|
||||
|
||||
async function ignoreViteTempFilesInEslintConfig(
|
||||
tree: Tree,
|
||||
projectRoot: string | undefined
|
||||
): Promise<void> {
|
||||
if (!isEslintInstalled(tree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensurePackage('@nx/eslint', getPackageVersion(tree, 'nx'));
|
||||
const { addIgnoresToLintConfig, isEslintConfigSupported } = await import(
|
||||
'@nx/eslint/src/generators/utils/eslint-file'
|
||||
);
|
||||
if (!isEslintConfigSupported(tree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { useFlatConfig } = await import('@nx/eslint/src/utils/flat-config');
|
||||
const isUsingFlatConfig = useFlatConfig(tree);
|
||||
if (!projectRoot && !isUsingFlatConfig) {
|
||||
// root eslintrc files ignore all files and the root eslintrc files add
|
||||
// back all the project files, so we only add the ignores to the project
|
||||
// eslintrc files
|
||||
return;
|
||||
}
|
||||
|
||||
// for flat config, we update the root config file
|
||||
const directory = isUsingFlatConfig ? '' : projectRoot ?? '';
|
||||
|
||||
addIgnoresToLintConfig(tree, directory, [
|
||||
'**/vite.config.*.timestamp*',
|
||||
'**/vitest.config.*.timestamp*',
|
||||
]);
|
||||
}
|
||||
|
||||
export function isEslintInstalled(tree: Tree): boolean {
|
||||
try {
|
||||
require('eslint');
|
||||
return true;
|
||||
} catch {}
|
||||
|
||||
// it might not be installed yet, but it might be in the tree pending install
|
||||
const { devDependencies, dependencies } = tree.exists('package.json')
|
||||
? readJson(tree, 'package.json')
|
||||
: {};
|
||||
|
||||
return !!devDependencies?.['eslint'] || !!dependencies?.['eslint'];
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
export * from './normalize-options';
|
||||
export * from './update-unit-test-config';
|
||||
export * from './add-e2e';
|
||||
export * from './add-vite-temp-files-to-gitignore';
|
||||
export * from './ignore-vite-temp-files';
|
||||
|
||||
@ -29,7 +29,15 @@
|
||||
"error",
|
||||
{
|
||||
"buildTargets": ["build-base"],
|
||||
"ignoredDependencies": ["nx", "typescript", "vite"]
|
||||
"ignoredDependencies": [
|
||||
"nx",
|
||||
"typescript",
|
||||
"vite",
|
||||
// we only check if the package is installed
|
||||
"eslint",
|
||||
// we ensure it is installed and only use it when eslint is installed
|
||||
"@nx/eslint"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -44,6 +44,11 @@
|
||||
"version": "20.5.0-beta.3",
|
||||
"description": "Update resolve.conditions to include defaults that are no longer provided by Vite.",
|
||||
"implementation": "./src/migrations/update-20-5-0/update-resolve-conditions"
|
||||
},
|
||||
"eslint-ignore-vite-temp-files": {
|
||||
"version": "20.5.0-beta.3",
|
||||
"description": "Add vite config temporary files to the ESLint configuration ignore patterns if ESLint is used.",
|
||||
"implementation": "./src/migrations/update-20-5-0/eslint-ignore-vite-temp-files"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
|
||||
@ -81,7 +81,11 @@ export async function viteConfigurationGeneratorInternal(
|
||||
tsConfigName: projectRoot === '.' ? 'tsconfig.json' : 'tsconfig.base.json',
|
||||
});
|
||||
tasks.push(jsInitTask);
|
||||
const initTask = await initGenerator(tree, { ...schema, skipFormat: true });
|
||||
const initTask = await initGenerator(tree, {
|
||||
...schema,
|
||||
projectRoot,
|
||||
skipFormat: true,
|
||||
});
|
||||
tasks.push(initTask);
|
||||
tasks.push(ensureDependencies(tree, schema));
|
||||
|
||||
|
||||
@ -132,17 +132,200 @@ describe('@nx/vite:init', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should ignore vite temp files in gitignore', async () => {
|
||||
await initGenerator(tree, {});
|
||||
|
||||
expect(tree.read('.gitignore', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"vite.config.*.timestamp*
|
||||
vitest.config.*.timestamp*"
|
||||
`);
|
||||
});
|
||||
|
||||
it(`should not add multiple instances of the same vite temp file glob to gitignore`, async () => {
|
||||
// ARRANGE
|
||||
tree.write('.gitignore', 'vite.config.*.timestamp*');
|
||||
tree.write(
|
||||
'.gitignore',
|
||||
`vitest.config.*.timestamp*
|
||||
vite.config.*.timestamp*`
|
||||
);
|
||||
|
||||
// ACT
|
||||
await initGenerator(tree, {});
|
||||
|
||||
// ASSERT
|
||||
expect(tree.read('.gitignore', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"vite.config.*.timestamp*
|
||||
vitest.config.*.timestamp*"
|
||||
"vitest.config.*.timestamp*
|
||||
vite.config.*.timestamp*"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should ignore vite temp files in eslint flat config without a block with ignores', async () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies = { eslint: '9.0.0' };
|
||||
return json;
|
||||
});
|
||||
tree.write('eslint.config.mjs', `export default [];`);
|
||||
|
||||
await initGenerator(tree, {});
|
||||
|
||||
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"export default [
|
||||
{
|
||||
ignores: ['**/vite.config.*.timestamp*', '**/vitest.config.*.timestamp*'],
|
||||
},
|
||||
];
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should ignore vite temp files in eslint flat config with a block with ignores', async () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies = { eslint: '9.0.0' };
|
||||
return json;
|
||||
});
|
||||
tree.write(
|
||||
'eslint.config.mjs',
|
||||
`export default [
|
||||
{
|
||||
ignores: ['dist'],
|
||||
},
|
||||
];`
|
||||
);
|
||||
|
||||
await initGenerator(tree, {});
|
||||
|
||||
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"export default [
|
||||
{
|
||||
ignores: [
|
||||
'dist',
|
||||
'**/vite.config.*.timestamp*',
|
||||
'**/vitest.config.*.timestamp*',
|
||||
],
|
||||
},
|
||||
];
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not duplicate vite temp files in eslint flat config', async () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies = { eslint: '9.0.0' };
|
||||
return json;
|
||||
});
|
||||
tree.write(
|
||||
'eslint.config.mjs',
|
||||
`export default [
|
||||
{
|
||||
ignores: ['**/vitest.config.*.timestamp*', '**/vite.config.*.timestamp*'],
|
||||
},
|
||||
];`
|
||||
);
|
||||
|
||||
await initGenerator(tree, {});
|
||||
|
||||
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"export default [
|
||||
{
|
||||
ignores: ['**/vitest.config.*.timestamp*', '**/vite.config.*.timestamp*'],
|
||||
},
|
||||
];
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should ignore vite temp files in project eslintrc config without ignorePatterns', async () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies = { eslint: '9.0.0' };
|
||||
return json;
|
||||
});
|
||||
tree.write('.eslintrc.json', JSON.stringify({ ignorePatterns: ['**/*'] }));
|
||||
tree.write('apps/my-app/.eslintrc.json', `{}`);
|
||||
|
||||
await initGenerator(tree, { projectRoot: 'apps/my-app' });
|
||||
|
||||
expect(readJson(tree, '.eslintrc.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"**/*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(tree, 'apps/my-app/.eslintrc.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should ignore vite temp files in project eslintrc config with ignorePatterns config', async () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies = { eslint: '9.0.0' };
|
||||
return json;
|
||||
});
|
||||
tree.write('.eslintrc.json', JSON.stringify({ ignorePatterns: ['**/*'] }));
|
||||
tree.write(
|
||||
'apps/my-app/.eslintrc.json',
|
||||
JSON.stringify({ ignorePatterns: ['!**/*'] })
|
||||
);
|
||||
|
||||
await initGenerator(tree, { projectRoot: 'apps/my-app' });
|
||||
|
||||
expect(readJson(tree, '.eslintrc.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"**/*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(tree, 'apps/my-app/.eslintrc.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not duplicate vite temp files in project eslintrc config', async () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies = { eslint: '9.0.0' };
|
||||
return json;
|
||||
});
|
||||
tree.write('.eslintrc.json', JSON.stringify({ ignorePatterns: ['**/*'] }));
|
||||
tree.write(
|
||||
'apps/my-app/.eslintrc.json',
|
||||
JSON.stringify({
|
||||
ignorePatterns: [
|
||||
'!**/*',
|
||||
'**/vitest.config.*.timestamp*',
|
||||
'**/vite.config.*.timestamp*',
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
await initGenerator(tree, { projectRoot: 'apps/my-app' });
|
||||
|
||||
expect(readJson(tree, '.eslintrc.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"**/*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(tree, 'apps/my-app/.eslintrc.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"**/vitest.config.*.timestamp*",
|
||||
"**/vite.config.*.timestamp*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -13,7 +13,7 @@ import { setupPathsPlugin } from '../setup-paths-plugin/setup-paths-plugin';
|
||||
import { createNodesV2 } from '../../plugins/plugin';
|
||||
import { InitGeneratorSchema } from './schema';
|
||||
import { checkDependenciesInstalled, moveToDevDependencies } from './lib/utils';
|
||||
import { addViteTempFilesToGitIgnore } from '../../utils/add-vite-temp-files-to-gitignore';
|
||||
import { ignoreViteTempFiles } from '../../utils/ignore-vite-temp-files';
|
||||
|
||||
export function updateNxJsonSettings(tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
@ -96,7 +96,7 @@ export async function initGeneratorInternal(
|
||||
}
|
||||
|
||||
updateNxJsonSettings(tree);
|
||||
addViteTempFilesToGitIgnore(tree);
|
||||
await ignoreViteTempFiles(tree, schema.projectRoot);
|
||||
|
||||
if (schema.setupPathsPlugin) {
|
||||
await setupPathsPlugin(tree, { skipFormat: true });
|
||||
|
||||
@ -7,4 +7,5 @@ export interface InitGeneratorSchema {
|
||||
addPlugin?: boolean;
|
||||
vitestOnly?: boolean;
|
||||
useViteV5?: boolean;
|
||||
projectRoot?: string;
|
||||
}
|
||||
|
||||
@ -73,6 +73,7 @@ export async function vitestGeneratorInternal(
|
||||
const useVite5 =
|
||||
major(coerce(pkgJson.devDependencies['vite']) ?? '6.0.0') === 5;
|
||||
const initTask = await initGenerator(tree, {
|
||||
projectRoot: root,
|
||||
skipFormat: true,
|
||||
addPlugin: schema.addPlugin,
|
||||
useViteV5: useVite5,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { addViteTempFilesToGitIgnore as _addViteTempFilesToGitIgnore } from '../../utils/add-vite-temp-files-to-gitignore';
|
||||
import { addViteTempFilesToGitIgnore as _addViteTempFilesToGitIgnore } from '../../utils/ignore-vite-temp-files';
|
||||
|
||||
export default function addViteTempFilesToGitIgnore(tree: Tree) {
|
||||
// need to check if .gitignore exists before adding to it
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { addViteTempFilesToGitIgnore as _addViteTempFilesToGitIgnore } from '../../utils/add-vite-temp-files-to-gitignore';
|
||||
import { addViteTempFilesToGitIgnore as _addViteTempFilesToGitIgnore } from '../../utils/ignore-vite-temp-files';
|
||||
|
||||
export default function addViteTempFilesToGitIgnore(tree: Tree) {
|
||||
// need to check if .gitignore exists before adding to it
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
#### Sample Code Changes
|
||||
|
||||
Add `vite.config.*.timestamp*` and `vitest.config.*.timestamp*` to the root `eslint.config.mjs` file (using **ESLint Flat Config**).
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Before" %}
|
||||
|
||||
```js {% fileName="eslint.config.mjs" %}
|
||||
export default [
|
||||
{
|
||||
ignores: ['dist'],
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="After" %}
|
||||
|
||||
```js {% highlightLines=[3] fileName="eslint.config.mjs" %}
|
||||
export default [
|
||||
{
|
||||
ignores: ['dist', 'vite.config.*.timestamp*', 'vitest.config.*.timestamp*'],
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
|
||||
{% /tabs %}
|
||||
|
||||
Add `vite.config.*.timestamp*` and `vitest.config.*.timestamp*` to the project's `.eslintrc.json` file (using **eslintrc** format config).
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Before" %}
|
||||
|
||||
```json {% fileName="apps/app1/eslintrc.json" %}
|
||||
{
|
||||
"ignorePatterns": ["!**/*"]
|
||||
}
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="After" %}
|
||||
|
||||
```json {% highlightLines=[4,5] fileName="apps/app1/eslintrc.json" %}
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"vite.config.*.timestamp*",
|
||||
"vitest.config.*.timestamp*"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
|
||||
{% /tabs %}
|
||||
@ -0,0 +1,148 @@
|
||||
import { addProjectConfiguration, type Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { isEslintInstalled } from '../../utils/ignore-vite-temp-files';
|
||||
import migration from './eslint-ignore-vite-temp-files';
|
||||
|
||||
jest.mock('../../utils/ignore-vite-temp-files', () => ({
|
||||
...jest.requireActual('../../utils/ignore-vite-temp-files'),
|
||||
isEslintInstalled: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('eslint-ignore-vite-temp-files migration', () => {
|
||||
let tree: Tree;
|
||||
let isEslintInstalledMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
isEslintInstalledMock = (isEslintInstalled as jest.Mock).mockReturnValue(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('should not throw an error if eslint is not installed', async () => {
|
||||
isEslintInstalledMock.mockReturnValue(false);
|
||||
|
||||
await expect(migration(tree)).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should not throw an error if there are no eslint config files', async () => {
|
||||
await expect(migration(tree)).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should only update the root eslint config when using flat config', async () => {
|
||||
tree.write('eslint.config.mjs', 'export default [];');
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
root: 'apps/app1',
|
||||
projectType: 'application',
|
||||
sourceRoot: 'apps/app1/src',
|
||||
targets: {},
|
||||
});
|
||||
tree.write('apps/app1/eslint.config.mjs', 'export default [];');
|
||||
|
||||
await migration(tree);
|
||||
|
||||
expect(tree.read('eslint.config.mjs', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"export default [
|
||||
{
|
||||
ignores: ['**/vite.config.*.timestamp*', '**/vitest.config.*.timestamp*'],
|
||||
},
|
||||
];
|
||||
"
|
||||
`);
|
||||
expect(tree.read('apps/app1/eslint.config.mjs', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"export default [];
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should update the project eslint config when using eslintrc config and it is using vite', async () => {
|
||||
tree.write(
|
||||
'.eslintrc.json',
|
||||
`{
|
||||
"ignorePatterns": ["**/*"]
|
||||
}
|
||||
`
|
||||
);
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
root: 'apps/app1',
|
||||
projectType: 'application',
|
||||
sourceRoot: 'apps/app1/src',
|
||||
targets: {},
|
||||
});
|
||||
tree.write(
|
||||
'apps/app1/.eslintrc.json',
|
||||
`{
|
||||
"ignorePatterns": ["!**/*"]
|
||||
}
|
||||
`
|
||||
);
|
||||
tree.write('apps/app1/vite.config.ts', 'export default {};');
|
||||
addProjectConfiguration(tree, 'app2', {
|
||||
root: 'apps/app2',
|
||||
projectType: 'application',
|
||||
sourceRoot: 'apps/app2/src',
|
||||
targets: {},
|
||||
});
|
||||
tree.write(
|
||||
'apps/app2/.eslintrc.json',
|
||||
`{
|
||||
"ignorePatterns": ["!**/*"]
|
||||
}
|
||||
`
|
||||
);
|
||||
tree.write('apps/app2/vitest.config.ts', 'export default {};');
|
||||
// app not using vite, it should not be updated
|
||||
addProjectConfiguration(tree, 'app3', {
|
||||
root: 'apps/app3',
|
||||
projectType: 'application',
|
||||
sourceRoot: 'apps/app3/src',
|
||||
targets: {},
|
||||
});
|
||||
tree.write(
|
||||
'apps/app3/.eslintrc.json',
|
||||
`{
|
||||
"ignorePatterns": ["!**/*"]
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
await migration(tree);
|
||||
|
||||
expect(tree.read('.eslintrc.json', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"{
|
||||
"ignorePatterns": ["**/*"]
|
||||
}
|
||||
"
|
||||
`);
|
||||
expect(tree.read('apps/app1/.eslintrc.json', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"{
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*"
|
||||
]
|
||||
}
|
||||
"
|
||||
`);
|
||||
expect(tree.read('apps/app2/.eslintrc.json', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"{
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*"
|
||||
]
|
||||
}
|
||||
"
|
||||
`);
|
||||
expect(tree.read('apps/app3/.eslintrc.json', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"{
|
||||
"ignorePatterns": ["!**/*"]
|
||||
}
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,54 @@
|
||||
import {
|
||||
ensurePackage,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
globAsync,
|
||||
type Tree,
|
||||
} from '@nx/devkit';
|
||||
import { isEslintInstalled } from '../../utils/ignore-vite-temp-files';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
if (!isEslintInstalled(tree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensurePackage('@nx/eslint', nxVersion);
|
||||
const { addIgnoresToLintConfig, isEslintConfigSupported } = await import(
|
||||
'@nx/eslint/src/generators/utils/eslint-file'
|
||||
);
|
||||
if (!isEslintConfigSupported(tree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { useFlatConfig } = await import('@nx/eslint/src/utils/flat-config');
|
||||
const isUsingFlatConfig = useFlatConfig(tree);
|
||||
|
||||
if (isUsingFlatConfig) {
|
||||
// using flat config, so we update the root eslint config
|
||||
addIgnoresToLintConfig(tree, '', [
|
||||
'**/vite.config.*.timestamp*',
|
||||
'**/vitest.config.*.timestamp*',
|
||||
]);
|
||||
} else {
|
||||
// not using flat config, so we update each project's eslint config
|
||||
const projects = getProjects(tree);
|
||||
|
||||
for (const [, { root: projectRoot }] of projects) {
|
||||
const viteConfigFiles = await globAsync(tree, [
|
||||
`${projectRoot}/**/{vite,vitest}.config.{js,ts,mjs,mts,cjs,cts}`,
|
||||
]);
|
||||
if (!viteConfigFiles.length) {
|
||||
// the project doesn't use vite or vitest, so we skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
addIgnoresToLintConfig(tree, projectRoot, [
|
||||
'**/vite.config.*.timestamp*',
|
||||
'**/vitest.config.*.timestamp*',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
import { stripIndents, Tree } from '@nx/devkit';
|
||||
|
||||
export function addViteTempFilesToGitIgnore(tree: Tree) {
|
||||
let newGitIgnoreContents = `vite.config.*.timestamp*`;
|
||||
if (tree.exists('.gitignore')) {
|
||||
const gitIgnoreContents = tree.read('.gitignore', 'utf-8');
|
||||
if (!gitIgnoreContents.includes(newGitIgnoreContents)) {
|
||||
newGitIgnoreContents = stripIndents`${gitIgnoreContents}
|
||||
${newGitIgnoreContents}`;
|
||||
|
||||
tree.write('.gitignore', newGitIgnoreContents);
|
||||
}
|
||||
} else {
|
||||
tree.write('.gitignore', newGitIgnoreContents);
|
||||
}
|
||||
|
||||
newGitIgnoreContents = `vitest.config.*.timestamp*`;
|
||||
if (tree.exists('.gitignore')) {
|
||||
const gitIgnoreContents = tree.read('.gitignore', 'utf-8');
|
||||
if (!gitIgnoreContents.includes(newGitIgnoreContents)) {
|
||||
newGitIgnoreContents = stripIndents`${gitIgnoreContents}
|
||||
${newGitIgnoreContents}`;
|
||||
|
||||
tree.write('.gitignore', newGitIgnoreContents);
|
||||
}
|
||||
} else {
|
||||
tree.write('.gitignore', newGitIgnoreContents);
|
||||
}
|
||||
}
|
||||
75
packages/vite/src/utils/ignore-vite-temp-files.ts
Normal file
75
packages/vite/src/utils/ignore-vite-temp-files.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { ensurePackage, readJson, stripIndents, type Tree } from '@nx/devkit';
|
||||
import { nxVersion } from './versions';
|
||||
|
||||
export async function ignoreViteTempFiles(
|
||||
tree: Tree,
|
||||
projectRoot?: string | undefined
|
||||
): Promise<void> {
|
||||
addViteTempFilesToGitIgnore(tree);
|
||||
await ignoreViteTempFilesInEslintConfig(tree, projectRoot);
|
||||
}
|
||||
|
||||
export function addViteTempFilesToGitIgnore(tree: Tree): void {
|
||||
let gitIgnoreContents = tree.exists('.gitignore')
|
||||
? tree.read('.gitignore', 'utf-8')
|
||||
: '';
|
||||
|
||||
if (!/^vite\.config\.\*\.timestamp\*$/m.test(gitIgnoreContents)) {
|
||||
gitIgnoreContents = stripIndents`${gitIgnoreContents}
|
||||
vite.config.*.timestamp*`;
|
||||
}
|
||||
if (!/^vitest\.config\.\*\.timestamp\*$/m.test(gitIgnoreContents)) {
|
||||
gitIgnoreContents = stripIndents`${gitIgnoreContents}
|
||||
vitest.config.*.timestamp*`;
|
||||
}
|
||||
|
||||
tree.write('.gitignore', gitIgnoreContents);
|
||||
}
|
||||
|
||||
async function ignoreViteTempFilesInEslintConfig(
|
||||
tree: Tree,
|
||||
projectRoot: string | undefined
|
||||
): Promise<void> {
|
||||
if (!isEslintInstalled(tree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensurePackage('@nx/eslint', nxVersion);
|
||||
const { addIgnoresToLintConfig, isEslintConfigSupported } = await import(
|
||||
'@nx/eslint/src/generators/utils/eslint-file'
|
||||
);
|
||||
if (!isEslintConfigSupported(tree)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { useFlatConfig } = await import('@nx/eslint/src/utils/flat-config');
|
||||
const isUsingFlatConfig = useFlatConfig(tree);
|
||||
if (!projectRoot && !isUsingFlatConfig) {
|
||||
// root eslintrc files ignore all files and the root eslintrc files add
|
||||
// back all the project files, so we only add the ignores to the project
|
||||
// eslintrc files
|
||||
return;
|
||||
}
|
||||
|
||||
// for flat config, we update the root config file
|
||||
const directory = isUsingFlatConfig ? '' : projectRoot ?? '';
|
||||
|
||||
addIgnoresToLintConfig(tree, directory, [
|
||||
'**/vite.config.*.timestamp*',
|
||||
'**/vitest.config.*.timestamp*',
|
||||
]);
|
||||
}
|
||||
|
||||
export function isEslintInstalled(tree: Tree): boolean {
|
||||
try {
|
||||
require('eslint');
|
||||
return true;
|
||||
} catch {}
|
||||
|
||||
// it might not be installed yet, but it might be in the tree pending install
|
||||
const { devDependencies, dependencies } = tree.exists('package.json')
|
||||
? readJson(tree, 'package.json')
|
||||
: {};
|
||||
|
||||
return !!devDependencies?.['eslint'] || !!dependencies?.['eslint'];
|
||||
}
|
||||
@ -94,7 +94,11 @@ exports[`application generator should set up project correctly for cypress 3`] =
|
||||
"@vue/eslint-config-prettier/skip-formatting",
|
||||
"../.eslintrc.json"
|
||||
],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx", "*.vue"],
|
||||
@ -300,7 +304,11 @@ exports[`application generator should set up project correctly with given option
|
||||
"@vue/eslint-config-prettier/skip-formatting",
|
||||
"../.eslintrc.json"
|
||||
],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx", "*.vue"],
|
||||
|
||||
@ -240,6 +240,8 @@ exports[`library should generate files 1`] = `
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
"**/vite.config.*.timestamp*",
|
||||
"**/vitest.config.*.timestamp*",
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
|
||||
@ -317,6 +317,25 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) {
|
||||
|
||||
createApplicationFiles(host, options);
|
||||
|
||||
if (options.linter === 'eslint') {
|
||||
const { lintProjectGenerator } = ensurePackage<typeof import('@nx/eslint')>(
|
||||
'@nx/eslint',
|
||||
nxVersion
|
||||
);
|
||||
const lintTask = await lintProjectGenerator(host, {
|
||||
linter: options.linter,
|
||||
project: options.projectName,
|
||||
tsConfigPaths: [
|
||||
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
|
||||
],
|
||||
unitTestRunner: options.unitTestRunner,
|
||||
skipFormat: true,
|
||||
setParserOptionsProject: options.setParserOptionsProject,
|
||||
addPlugin: options.addPlugin,
|
||||
});
|
||||
tasks.push(lintTask);
|
||||
}
|
||||
|
||||
if (options.bundler === 'vite') {
|
||||
const { viteConfigurationGenerator, createOrEditViteConfig } =
|
||||
ensurePackage<typeof import('@nx/vite')>('@nx/vite', nxVersion);
|
||||
@ -387,25 +406,6 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) {
|
||||
);
|
||||
}
|
||||
|
||||
if (options.linter === 'eslint') {
|
||||
const { lintProjectGenerator } = ensurePackage<typeof import('@nx/eslint')>(
|
||||
'@nx/eslint',
|
||||
nxVersion
|
||||
);
|
||||
const lintTask = await lintProjectGenerator(host, {
|
||||
linter: options.linter,
|
||||
project: options.projectName,
|
||||
tsConfigPaths: [
|
||||
joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
|
||||
],
|
||||
unitTestRunner: options.unitTestRunner,
|
||||
skipFormat: true,
|
||||
setParserOptionsProject: options.setParserOptionsProject,
|
||||
addPlugin: options.addPlugin,
|
||||
});
|
||||
tasks.push(lintTask);
|
||||
}
|
||||
|
||||
const nxJson = readNxJson(host);
|
||||
let hasPlugin: PluginConfiguration | undefined;
|
||||
let buildPlugin: string;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user