From d4cc80dd6c9cbeeb9f706bf513de56a1f1d65423 Mon Sep 17 00:00:00 2001 From: FrozenPandaz Date: Tue, 24 Mar 2026 12:42:32 -0400 Subject: [PATCH] fix(vitest): resolve addPlugin default in init generator The init generator had a wrapper function that hardcoded addPlugin: false, preventing the plugin from being added when running nx add @nx/vitest. Merged the two functions so the ??= default logic runs correctly. --- .../vitest/src/generators/init/init.spec.ts | 182 ++++++++++++++++++ packages/vitest/src/generators/init/init.ts | 9 +- 2 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 packages/vitest/src/generators/init/init.spec.ts diff --git a/packages/vitest/src/generators/init/init.spec.ts b/packages/vitest/src/generators/init/init.spec.ts new file mode 100644 index 00000000000..ca0a49e6a99 --- /dev/null +++ b/packages/vitest/src/generators/init/init.spec.ts @@ -0,0 +1,182 @@ +import { + ProjectGraph, + readJson, + readNxJson, + Tree, + updateJson, + NxJsonConfiguration, +} from '@nx/devkit'; +import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; + +import { initGenerator } from './init'; + +let projectGraph: ProjectGraph; +jest.mock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockImplementation(async () => { + return projectGraph; + }), +})); + +describe('@nx/vitest:init', () => { + let tree: Tree; + + beforeEach(() => { + projectGraph = { + nodes: {}, + dependencies: {}, + }; + tree = createTreeWithEmptyWorkspace(); + }); + + it('should add the plugin by default when addPlugin is not provided', async () => { + await initGenerator(tree, { + skipFormat: true, + skipPackageJson: true, + }); + const nxJson = readNxJson(tree); + expect(nxJson.plugins).toContainEqual( + expect.objectContaining({ plugin: '@nx/vitest' }) + ); + }); + + it('should update namedInputs for production', async () => { + updateJson(tree, 'nx.json', (json) => { + json.namedInputs ??= {}; + json.namedInputs.production = ['default']; + return json; + }); + + await initGenerator(tree, { + skipFormat: true, + skipPackageJson: true, + }); + + const nxJson = readNxJson(tree); + expect(nxJson.namedInputs.production).toContain( + '!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)' + ); + expect(nxJson.namedInputs.production).toContain( + '!{projectRoot}/tsconfig.spec.json' + ); + }); + + it('should add required packages to package.json', async () => { + await initGenerator(tree, { + skipFormat: true, + skipPackageJson: false, + }); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson.devDependencies['vitest']).toBeDefined(); + expect(packageJson.devDependencies['vite']).toBeDefined(); + expect(packageJson.devDependencies['@nx/vitest']).toBeDefined(); + }); + + it('should not add packages when skipPackageJson is true', async () => { + await initGenerator(tree, { + skipFormat: true, + skipPackageJson: true, + }); + + const packageJson = readJson(tree, 'package.json'); + expect(packageJson.devDependencies['vitest']).toBeUndefined(); + }); + + it('should not add the plugin when NX_ADD_PLUGINS is false', async () => { + const original = process.env.NX_ADD_PLUGINS; + process.env.NX_ADD_PLUGINS = 'false'; + + try { + await initGenerator(tree, { + skipFormat: true, + skipPackageJson: true, + }); + const nxJson = readNxJson(tree); + expect(nxJson.plugins).toBeUndefined(); + } finally { + process.env.NX_ADD_PLUGINS = original; + } + }); + + describe('with addPlugin: true', () => { + it('should add the vitest plugin to nx.json', async () => { + await initGenerator(tree, { + skipFormat: true, + addPlugin: true, + skipPackageJson: true, + }); + + const nxJson = readNxJson(tree); + expect(nxJson.plugins).toContainEqual( + expect.objectContaining({ plugin: '@nx/vitest' }) + ); + }); + + it('should not add plugin if already in array', async () => { + updateJson(tree, 'nx.json', (json) => { + json.plugins = ['@nx/vitest']; + return json; + }); + + await initGenerator(tree, { + skipFormat: true, + addPlugin: true, + skipPackageJson: true, + }); + + const nxJson = readNxJson(tree); + expect(nxJson.plugins).toEqual(['@nx/vitest']); + }); + + it('should not add target defaults when plugin is registered', async () => { + updateJson(tree, 'nx.json', (json) => { + json.namedInputs ??= {}; + json.namedInputs.production = ['default']; + return json; + }); + + await initGenerator(tree, { + skipFormat: true, + addPlugin: true, + skipPackageJson: true, + }); + + const nxJson = readNxJson(tree); + expect(nxJson.targetDefaults?.['@nx/vitest:test']).toBeUndefined(); + }); + }); + + describe('with addPlugin: false', () => { + it('should not add the vitest plugin to nx.json', async () => { + await initGenerator(tree, { + skipFormat: true, + addPlugin: false, + skipPackageJson: true, + }); + + const nxJson = readNxJson(tree); + expect(nxJson.plugins).toBeUndefined(); + }); + + it('should add target defaults for @nx/vitest:test', async () => { + updateJson(tree, 'nx.json', (json) => { + json.namedInputs ??= {}; + json.namedInputs.production = ['default']; + return json; + }); + + await initGenerator(tree, { + skipFormat: true, + addPlugin: false, + skipPackageJson: true, + }); + + const nxJson = readNxJson(tree); + expect(nxJson.targetDefaults['@nx/vitest:test']).toEqual({ + cache: true, + inputs: ['default', '^production'], + }); + }); + }); +}); diff --git a/packages/vitest/src/generators/init/init.ts b/packages/vitest/src/generators/init/init.ts index dd9ab9d0395..e10582c14c0 100644 --- a/packages/vitest/src/generators/init/init.ts +++ b/packages/vitest/src/generators/init/init.ts @@ -71,14 +71,7 @@ export function updateNxJsonSettings(tree: Tree) { updateNxJson(tree, nxJson); } -export function initGenerator(tree: Tree, schema: InitGeneratorSchema) { - return initGeneratorInternal(tree, { addPlugin: false, ...schema }); -} - -export async function initGeneratorInternal( - tree: Tree, - schema: InitGeneratorSchema -) { +export async function initGenerator(tree: Tree, schema: InitGeneratorSchema) { const nxJson = readNxJson(tree); const addPluginDefault = process.env.NX_ADD_PLUGINS !== 'false' &&