diff --git a/docs/generated/packages/angular/generators/application.json b/docs/generated/packages/angular/generators/application.json index 43a703df81352..aba0a20dc87dd 100644 --- a/docs/generated/packages/angular/generators/application.json +++ b/docs/generated/packages/angular/generators/application.json @@ -96,8 +96,9 @@ }, "unitTestRunner": { "type": "string", - "enum": ["jest", "none"], + "enum": ["jest", "vitest", "none"], "description": "Test runner to use for unit tests.", + "x-prompt": "Which unit test runner would you like to use?", "default": "jest" }, "e2eTestRunner": { diff --git a/docs/generated/packages/angular/generators/federate-module.json b/docs/generated/packages/angular/generators/federate-module.json index 1c978a84bdc94..bdc649cccae2c 100644 --- a/docs/generated/packages/angular/generators/federate-module.json +++ b/docs/generated/packages/angular/generators/federate-module.json @@ -51,8 +51,9 @@ }, "unitTestRunner": { "type": "string", - "enum": ["jest", "none"], + "enum": ["jest", "vitest", "none"], "description": "Test runner to use for unit tests of the remote if it needs to be created.", + "x-prompt": "Which unit test runner would you like to use?", "default": "jest" }, "e2eTestRunner": { diff --git a/docs/generated/packages/angular/generators/host.json b/docs/generated/packages/angular/generators/host.json index 98e630227e4b2..b5a8f60a83661 100644 --- a/docs/generated/packages/angular/generators/host.json +++ b/docs/generated/packages/angular/generators/host.json @@ -105,8 +105,9 @@ }, "unitTestRunner": { "type": "string", - "enum": ["jest", "none"], + "enum": ["jest", "vitest", "none"], "description": "Test runner to use for unit tests.", + "x-prompt": "Which unit test runner would you like to use?", "default": "jest" }, "e2eTestRunner": { diff --git a/docs/generated/packages/angular/generators/library.json b/docs/generated/packages/angular/generators/library.json index 13db7611a0d24..8ee7fb9dba2dc 100644 --- a/docs/generated/packages/angular/generators/library.json +++ b/docs/generated/packages/angular/generators/library.json @@ -88,8 +88,9 @@ }, "unitTestRunner": { "type": "string", - "enum": ["jest", "none"], + "enum": ["jest", "vitest", "none"], "description": "Test runner to use for unit tests.", + "x-prompt": "Which unit test runner would you like to use?", "default": "jest" }, "importPath": { diff --git a/docs/generated/packages/angular/generators/remote.json b/docs/generated/packages/angular/generators/remote.json index c7192468a953b..268d6b61bbf4a 100644 --- a/docs/generated/packages/angular/generators/remote.json +++ b/docs/generated/packages/angular/generators/remote.json @@ -99,8 +99,9 @@ }, "unitTestRunner": { "type": "string", - "enum": ["jest", "none"], + "enum": ["jest", "vitest", "none"], "description": "Test runner to use for unit tests.", + "x-prompt": "Which unit test runner would you like to use?", "default": "jest" }, "e2eTestRunner": { diff --git a/e2e/angular/src/projects.test.ts b/e2e/angular/src/projects.test.ts index 5fbdac544e98b..158ede3dcd4fc 100644 --- a/e2e/angular/src/projects.test.ts +++ b/e2e/angular/src/projects.test.ts @@ -568,4 +568,21 @@ describe('Angular Projects', () => { runCLI(`server ${webpackApp} --output-hashing none`) ).toThrow(); }, 500_000); + + it('should generate apps and libs with vitest', async () => { + const app = uniq('app'); + const lib = uniq('lib'); + + runCLI( + `generate @nx/angular:app ${app} --unit-test-runner=vitest --no-interactive` + ); + runCLI( + `generate @nx/angular:lib ${lib} --unit-test-runner=vitest --no-interactive` + ); + + // Make sure we are using vitest + checkFilesExist(`${app}/vite.config.mts`, `${lib}/vite.config.mts`); + + runCLI(`run-many --target test --projects=${app},${lib} --parallel`); + }); }); diff --git a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap index 5bac4baf253a8..32cec32a23d0f 100644 --- a/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap +++ b/packages/angular/src/generators/application/__snapshots__/application.spec.ts.snap @@ -427,6 +427,146 @@ exports[`app --strict should enable strict type checking: e2e tsconfig.json 1`] } `; +exports[`app --unit-test-runner vitest should add tsconfig.spec.json 1`] = ` +"{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../dist/out-tsc", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ] + }, + "include": [ + "vite.config.ts", + "vitest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ], + "files": ["src/test-setup.ts"] +} +" +`; + +exports[`app --unit-test-runner vitest should generate src/test-setup.ts 1`] = ` +"import '@analogjs/vitest-angular/setup-zone'; + +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting, +} from '@angular/platform-browser-dynamic/testing'; +import { getTestBed } from '@angular/core/testing'; + +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +" +`; + +exports[`app --unit-test-runner vitest should generate vite.config.mts 1`] = ` +"/// +import { defineConfig } from 'vite'; +import angular from '@analogjs/vite-plugin-angular'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../node_modules/.vite/my-app', + plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])], + // Uncomment this if you are using workers. + // worker: { + // plugins: [ nxViteTsPaths() ], + // }, + test: { + watch: false, + globals: true, + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + setupFiles: ['src/test-setup.ts'], + reporters: ['default'], + coverage: { + reportsDirectory: '../coverage/my-app', + provider: 'v8', + }, + }, +}); +" +`; + +exports[`app --unit-test-runner vitest should generate vite.config.mts if package type is module 1`] = ` +"/// +import { defineConfig } from 'vite'; +import angular from '@analogjs/vite-plugin-angular'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../node_modules/.vite/my-app', + plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])], + // Uncomment this if you are using workers. + // worker: { + // plugins: [ nxViteTsPaths() ], + // }, + test: { + watch: false, + globals: true, + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + setupFiles: ['src/test-setup.ts'], + reporters: ['default'], + coverage: { + reportsDirectory: '../coverage/my-app', + provider: 'v8', + }, + }, +}); +" +`; + +exports[`app --unit-test-runner vitest should generate vite.config.mts if workspace package type is module 1`] = ` +"/// +import { defineConfig } from 'vite'; +import angular from '@analogjs/vite-plugin-angular'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin'; + +export default defineConfig({ + root: __dirname, + cacheDir: '../node_modules/.vite/my-app', + plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])], + // Uncomment this if you are using workers. + // worker: { + // plugins: [ nxViteTsPaths() ], + // }, + test: { + watch: false, + globals: true, + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + setupFiles: ['src/test-setup.ts'], + reporters: ['default'], + coverage: { + reportsDirectory: '../coverage/my-app', + provider: 'v8', + }, + }, +}); +" +`; + exports[`app angular compat support should import "ApplicationConfig" from "@angular/platform-browser" 1`] = ` "import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; diff --git a/packages/angular/src/generators/application/application.spec.ts b/packages/angular/src/generators/application/application.spec.ts index 47720e3a74d43..023aea6f27da4 100644 --- a/packages/angular/src/generators/application/application.spec.ts +++ b/packages/angular/src/generators/application/application.spec.ts @@ -1,5 +1,5 @@ import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; -import type { Tree } from '@nx/devkit'; +import { Tree, writeJson } from '@nx/devkit'; import * as devkit from '@nx/devkit'; import { NxJsonConfiguration, @@ -698,6 +698,110 @@ describe('app', () => { }); }); + describe('vitest', () => { + it('should generate vite.config.mts', async () => { + await generateApp(appTree, 'my-app', { + skipFormat: false, + unitTestRunner: UnitTestRunner.Vitest, + }); + + expect( + appTree.read('my-app/vite.config.mts', 'utf-8') + ).toMatchSnapshot(); + }); + + it('should generate src/test-setup.ts', async () => { + await generateApp(appTree, 'my-app', { + unitTestRunner: UnitTestRunner.Vitest, + }); + + expect( + appTree.read('my-app/src/test-setup.ts', 'utf-8') + ).toMatchSnapshot(); + }); + + it('should exclude src/test-setup.ts in tsconfig.app.json', async () => { + await generateApp(appTree, 'my-app', { + unitTestRunner: UnitTestRunner.Vitest, + }); + + const tsConfig = readJson(appTree, 'my-app/tsconfig.app.json'); + expect(tsConfig.exclude).toContain('src/test-setup.ts'); + }); + + it('should add tsconfig.spec.json', async () => { + await generateApp(appTree, 'my-app', { + unitTestRunner: UnitTestRunner.Vitest, + }); + + expect( + appTree.read('my-app/tsconfig.spec.json', 'utf-8') + ).toMatchSnapshot(); + }); + + it('should add a reference to tsconfig.spec.json in tsconfig.json', async () => { + await generateApp(appTree, 'my-app', { + unitTestRunner: UnitTestRunner.Vitest, + }); + + const { references } = readJson(appTree, 'my-app/tsconfig.json'); + expect(references).toContainEqual({ + path: './tsconfig.spec.json', + }); + }); + + it('should add @nx/vite dependency', async () => { + await generateApp(appTree, 'my-app', { + unitTestRunner: UnitTestRunner.Vitest, + }); + + const { devDependencies } = readJson(appTree, 'package.json'); + expect(devDependencies['@nx/vite']).toBeDefined(); + }); + + it('should add vitest-angular', async () => { + await generateApp(appTree, 'my-app', { + unitTestRunner: UnitTestRunner.Vitest, + }); + + const { devDependencies } = readJson(appTree, 'package.json'); + expect(devDependencies['@analogjs/vite-plugin-angular']).toBeDefined(); + expect(devDependencies['@analogjs/vitest-angular']).toBeDefined(); + }); + + it('should generate vite.config.mts if package type is module', async () => { + writeJson(appTree, 'my-app/package.json', { + name: 'my-app', + type: 'module', + }); + + await generateApp(appTree, 'my-app', { + skipFormat: false, + unitTestRunner: UnitTestRunner.Vitest, + }); + + expect( + appTree.read('my-app/vite.config.mts', 'utf-8') + ).toMatchSnapshot(); + }); + + it('should generate vite.config.mts if workspace package type is module', async () => { + updateJson(appTree, 'package.json', (json) => ({ + ...json, + type: 'module', + })); + + await generateApp(appTree, 'my-app', { + skipFormat: false, + unitTestRunner: UnitTestRunner.Vitest, + }); + + expect( + appTree.read('my-app/vite.config.mts', 'utf-8') + ).toMatchSnapshot(); + }); + }); + describe('none', () => { it('should not generate test configuration', async () => { await generateApp(appTree, 'my-app', { diff --git a/packages/angular/src/generators/application/lib/add-unit-test-runner.ts b/packages/angular/src/generators/application/lib/add-unit-test-runner.ts index 934b44df42a6a..822d84511a1ce 100644 --- a/packages/angular/src/generators/application/lib/add-unit-test-runner.ts +++ b/packages/angular/src/generators/application/lib/add-unit-test-runner.ts @@ -1,15 +1,26 @@ import { Tree } from '@nx/devkit'; import { UnitTestRunner } from '../../../utils/test-runners'; import { addJest } from '../../utils/add-jest'; +import { addVitest } from '../../utils/add-vitest'; import type { NormalizedSchema } from './normalized-schema'; export async function addUnitTestRunner(host: Tree, options: NormalizedSchema) { - if (options.unitTestRunner === UnitTestRunner.Jest) { - await addJest(host, { - name: options.name, - projectRoot: options.appProjectRoot, - skipPackageJson: options.skipPackageJson, - strict: options.strict, - }); + switch (options.unitTestRunner) { + case UnitTestRunner.Jest: + await addJest(host, { + name: options.name, + projectRoot: options.appProjectRoot, + skipPackageJson: options.skipPackageJson, + strict: options.strict, + }); + break; + case UnitTestRunner.Vitest: + await addVitest(host, { + name: options.name, + projectRoot: options.appProjectRoot, + skipPackageJson: options.skipPackageJson, + strict: options.strict, + }); + break; } } diff --git a/packages/angular/src/generators/application/schema.json b/packages/angular/src/generators/application/schema.json index ec1dce93c52b2..e3d58fe4e1eec 100644 --- a/packages/angular/src/generators/application/schema.json +++ b/packages/angular/src/generators/application/schema.json @@ -99,8 +99,9 @@ }, "unitTestRunner": { "type": "string", - "enum": ["jest", "none"], + "enum": ["jest", "vitest", "none"], "description": "Test runner to use for unit tests.", + "x-prompt": "Which unit test runner would you like to use?", "default": "jest" }, "e2eTestRunner": { diff --git a/packages/angular/src/generators/federate-module/schema.json b/packages/angular/src/generators/federate-module/schema.json index 8abbcde9663c7..50576437aea86 100644 --- a/packages/angular/src/generators/federate-module/schema.json +++ b/packages/angular/src/generators/federate-module/schema.json @@ -51,8 +51,9 @@ }, "unitTestRunner": { "type": "string", - "enum": ["jest", "none"], + "enum": ["jest", "vitest", "none"], "description": "Test runner to use for unit tests of the remote if it needs to be created.", + "x-prompt": "Which unit test runner would you like to use?", "default": "jest" }, "e2eTestRunner": { diff --git a/packages/angular/src/generators/host/schema.json b/packages/angular/src/generators/host/schema.json index 8320fc13ca42a..4e63378f3faa1 100644 --- a/packages/angular/src/generators/host/schema.json +++ b/packages/angular/src/generators/host/schema.json @@ -108,8 +108,9 @@ }, "unitTestRunner": { "type": "string", - "enum": ["jest", "none"], + "enum": ["jest", "vitest", "none"], "description": "Test runner to use for unit tests.", + "x-prompt": "Which unit test runner would you like to use?", "default": "jest" }, "e2eTestRunner": { diff --git a/packages/angular/src/generators/library/library.ts b/packages/angular/src/generators/library/library.ts index af46e11472c67..5605a607748a2 100644 --- a/packages/angular/src/generators/library/library.ts +++ b/packages/angular/src/generators/library/library.ts @@ -30,6 +30,8 @@ import { addJest } from '../utils/add-jest'; import { setGeneratorDefaults } from './lib/set-generator-defaults'; import { ensureAngularDependencies } from '../utils/ensure-angular-dependencies'; import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command'; +import { UnitTestRunner } from '../../utils/test-runners'; +import { addVitest } from '../utils/add-vitest'; import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; export async function libraryGenerator( @@ -130,13 +132,23 @@ async function addUnitTestRunner( host: Tree, options: NormalizedSchema['libraryOptions'] ) { - if (options.unitTestRunner === 'jest') { - await addJest(host, { - name: options.name, - projectRoot: options.projectRoot, - skipPackageJson: options.skipPackageJson, - strict: options.strict, - }); + switch (options.unitTestRunner) { + case UnitTestRunner.Jest: + await addJest(host, { + name: options.name, + projectRoot: options.projectRoot, + skipPackageJson: options.skipPackageJson, + strict: options.strict, + }); + break; + case UnitTestRunner.Vitest: + await addVitest(host, { + name: options.name, + projectRoot: options.projectRoot, + skipPackageJson: options.skipPackageJson, + strict: options.strict, + }); + break; } } diff --git a/packages/angular/src/generators/library/schema.json b/packages/angular/src/generators/library/schema.json index ab24380e60dc9..bad7652a38605 100644 --- a/packages/angular/src/generators/library/schema.json +++ b/packages/angular/src/generators/library/schema.json @@ -88,8 +88,9 @@ }, "unitTestRunner": { "type": "string", - "enum": ["jest", "none"], + "enum": ["jest", "vitest", "none"], "description": "Test runner to use for unit tests.", + "x-prompt": "Which unit test runner would you like to use?", "default": "jest" }, "importPath": { diff --git a/packages/angular/src/generators/remote/schema.json b/packages/angular/src/generators/remote/schema.json index a4b7b6c30990a..8176ca31a6ad5 100644 --- a/packages/angular/src/generators/remote/schema.json +++ b/packages/angular/src/generators/remote/schema.json @@ -102,8 +102,9 @@ }, "unitTestRunner": { "type": "string", - "enum": ["jest", "none"], + "enum": ["jest", "vitest", "none"], "description": "Test runner to use for unit tests.", + "x-prompt": "Which unit test runner would you like to use?", "default": "jest" }, "e2eTestRunner": { diff --git a/packages/angular/src/generators/utils/add-vitest.ts b/packages/angular/src/generators/utils/add-vitest.ts new file mode 100644 index 0000000000000..79383c97647ab --- /dev/null +++ b/packages/angular/src/generators/utils/add-vitest.ts @@ -0,0 +1,84 @@ +import { + addDependenciesToPackageJson, + ensurePackage, + joinPathFragments, + type Tree, +} from '@nx/devkit'; +import { analogVitestAngular, nxVersion } from '../../utils/versions'; + +export type AddVitestOptions = { + name: string; + projectRoot: string; + skipPackageJson: boolean; + strict: boolean; +}; + +export async function addVitest( + tree: Tree, + options: AddVitestOptions +): Promise { + if (!options.skipPackageJson) { + addDependenciesToPackageJson( + tree, + {}, + { + '@analogjs/vitest-angular': analogVitestAngular, + '@analogjs/vite-plugin-angular': analogVitestAngular, + }, + undefined, + true + ); + } + + const { createOrEditViteConfig, viteConfigurationGenerator } = ensurePackage< + typeof import('@nx/vite') + >('@nx/vite', nxVersion); + + const relativeTestSetupPath = joinPathFragments('src', 'test-setup.ts'); + + const setupFile = joinPathFragments( + options.projectRoot, + relativeTestSetupPath + ); + if (!tree.exists(setupFile)) { + tree.write( + setupFile, + `import '@analogjs/vitest-angular/setup-zone'; + +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting, +} from '@angular/platform-browser-dynamic/testing'; +import { getTestBed } from '@angular/core/testing'; + +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +` + ); + + await viteConfigurationGenerator(tree, { + project: options.name, + newProject: true, + uiFramework: 'none', + includeVitest: true, + testEnvironment: 'jsdom', + }); + + createOrEditViteConfig( + tree, + { + project: options.name, + includeLib: false, + includeVitest: true, + inSourceTests: false, + imports: [`import angular from '@analogjs/vite-plugin-angular'`], + plugins: ['angular()'], + setupFile: relativeTestSetupPath, + useEsmExtension: true, + }, + true + ); + } +} diff --git a/packages/angular/src/utils/backward-compatible-versions.ts b/packages/angular/src/utils/backward-compatible-versions.ts index 392bec4315533..9d8f22f864495 100644 --- a/packages/angular/src/utils/backward-compatible-versions.ts +++ b/packages/angular/src/utils/backward-compatible-versions.ts @@ -17,13 +17,16 @@ export type PackageVersionNames = export type VersionMap = { angularV16: Record< - Exclude, + Exclude< + CompatPackageVersionNames, + 'analogVitestAngular' | 'typescriptEslintVersion' + >, string >; angularV17: Record< Exclude< CompatPackageVersionNames, - 'ngUniversalVersion' | 'typescriptEslintVersion' + 'analogVitestAngular' | 'ngUniversalVersion' | 'typescriptEslintVersion' >, string >; diff --git a/packages/angular/src/utils/test-runners.ts b/packages/angular/src/utils/test-runners.ts index 642a233683ef5..4e2977193ac53 100644 --- a/packages/angular/src/utils/test-runners.ts +++ b/packages/angular/src/utils/test-runners.ts @@ -1,6 +1,7 @@ export enum UnitTestRunner { Jest = 'jest', None = 'none', + Vitest = 'vitest', } export enum E2eTestRunner { diff --git a/packages/angular/src/utils/versions.ts b/packages/angular/src/utils/versions.ts index 03df9c63693ec..c2146379b1c1a 100644 --- a/packages/angular/src/utils/versions.ts +++ b/packages/angular/src/utils/versions.ts @@ -28,5 +28,6 @@ export const tsNodeVersion = '10.9.1'; export const jestPresetAngularVersion = '~14.1.0'; export const typesNodeVersion = '18.16.9'; export const jasmineMarblesVersion = '^0.9.2'; +export const analogVitestAngular = '~1.9.1'; export const jsoncEslintParserVersion = '^2.1.0'; diff --git a/packages/react/src/generators/application/__snapshots__/application.spec.ts.snap b/packages/react/src/generators/application/__snapshots__/application.spec.ts.snap index 2c1e65d18cc1b..fba1aec069996 100644 --- a/packages/react/src/generators/application/__snapshots__/application.spec.ts.snap +++ b/packages/react/src/generators/application/__snapshots__/application.spec.ts.snap @@ -214,11 +214,11 @@ import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin'; export default defineConfig({ root: __dirname, cacheDir: '../node_modules/.vite/my-app', - server:{ + server: { port: 4200, host: 'localhost', }, - preview:{ + preview: { port: 4300, host: 'localhost', }, @@ -244,7 +244,7 @@ export default defineConfig({ coverage: { reportsDirectory: '../coverage/my-app', provider: 'v8', - } + }, }, }); " @@ -278,11 +278,11 @@ import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin'; export default defineConfig({ root: __dirname, cacheDir: '../node_modules/.vite/my-app', - server:{ + server: { port: 4200, host: 'localhost', }, - preview:{ + preview: { port: 4300, host: 'localhost', }, @@ -308,7 +308,7 @@ export default defineConfig({ coverage: { reportsDirectory: '../coverage/my-app', provider: 'v8', - } + }, }, }); " diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts index 20ed780069c7e..75b6f01a2dc78 100644 --- a/packages/react/src/generators/application/application.spec.ts +++ b/packages/react/src/generators/application/application.spec.ts @@ -52,6 +52,7 @@ describe('app', () => { it('should add vite types to tsconfigs', async () => { await applicationGenerator(appTree, { ...schema, + skipFormat: false, bundler: 'vite', unitTestRunner: 'vitest', }); @@ -198,6 +199,7 @@ describe('app', () => { it('should use preview vite types to tsconfigs', async () => { await applicationGenerator(appTree, { ...schema, + skipFormat: false, bundler: 'vite', unitTestRunner: 'vitest', }); diff --git a/packages/react/src/generators/library/__snapshots__/library.spec.ts.snap b/packages/react/src/generators/library/__snapshots__/library.spec.ts.snap index d96e4b37d8b69..cd3143bed9ce5 100644 --- a/packages/react/src/generators/library/__snapshots__/library.spec.ts.snap +++ b/packages/react/src/generators/library/__snapshots__/library.spec.ts.snap @@ -39,7 +39,15 @@ import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin'; export default defineConfig({ root: __dirname, cacheDir: '../node_modules/.vite/my-lib', - plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md']), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })], + plugins: [ + react(), + nxViteTsPaths(), + nxCopyAssetsPlugin(['*.md']), + dts({ + entryRoot: 'src', + tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'), + }), + ], // Uncomment this if you are using workers. // worker: { // plugins: [ nxViteTsPaths() ], @@ -60,11 +68,11 @@ export default defineConfig({ fileName: 'index', // Change this to the formats you want to support. // Don't forget to update your package.json as well. - formats: ['es', 'cjs'] + formats: ['es', 'cjs'], }, rollupOptions: { // External packages that should not be bundled into your library. - external: ['react','react-dom','react/jsx-runtime'] + external: ['react', 'react-dom', 'react/jsx-runtime'], }, }, test: { @@ -76,7 +84,7 @@ export default defineConfig({ coverage: { reportsDirectory: '../coverage/my-lib', provider: 'v8', - } + }, }, }); " diff --git a/packages/react/src/generators/library/library.spec.ts b/packages/react/src/generators/library/library.spec.ts index 0f738d2fe2637..ae4617f35c4f5 100644 --- a/packages/react/src/generators/library/library.spec.ts +++ b/packages/react/src/generators/library/library.spec.ts @@ -70,6 +70,7 @@ describe('lib', () => { it('should add vite types to tsconfigs', async () => { await libraryGenerator(tree, { ...defaultSchema, + skipFormat: false, bundler: 'vite', unitTestRunner: 'vitest', }); diff --git a/packages/vite/src/generators/init/init.spec.ts b/packages/vite/src/generators/init/init.spec.ts index 43baee1a01def..43981247bae30 100644 --- a/packages/vite/src/generators/init/init.spec.ts +++ b/packages/vite/src/generators/init/init.spec.ts @@ -73,6 +73,7 @@ describe('@nx/vite:init', () => { "default", "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", "!{projectRoot}/tsconfig.spec.json", + "!{projectRoot}/src/test-setup.[jt]s", ], }, "plugins": [ diff --git a/packages/vite/src/generators/init/init.ts b/packages/vite/src/generators/init/init.ts index 4d1e5922e631b..c6521fef3f2b1 100644 --- a/packages/vite/src/generators/init/init.ts +++ b/packages/vite/src/generators/init/init.ts @@ -22,7 +22,8 @@ export function updateNxJsonSettings(tree: Tree) { if (productionFileSet) { productionFileSet.push( '!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)', - '!{projectRoot}/tsconfig.spec.json' + '!{projectRoot}/tsconfig.spec.json', + '!{projectRoot}/src/test-setup.[jt]s' ); nxJson.namedInputs.production = Array.from(new Set(productionFileSet)); diff --git a/packages/vite/src/generators/vitest/vitest-generator.ts b/packages/vite/src/generators/vitest/vitest-generator.ts index d932ca24e2aeb..f6b7b2f2d026a 100644 --- a/packages/vite/src/generators/vitest/vitest-generator.ts +++ b/packages/vite/src/generators/vitest/vitest-generator.ts @@ -155,6 +155,8 @@ function updateTsConfig( projectRoot: string, projectType: ProjectType ) { + const setupFile = tryFindSetupFile(tree, projectRoot); + if (tree.exists(joinPathFragments(projectRoot, 'tsconfig.spec.json'))) { updateJson( tree, @@ -168,6 +170,11 @@ function updateTsConfig( json.compilerOptions.types = ['vitest']; } } + + if (setupFile) { + json.files = [...(json.files ?? []), setupFile]; + } + return json; } ); @@ -221,18 +228,11 @@ function updateTsConfig( } } - if (options.inSourceTests) { - if (tree.exists(runtimeTsconfigPath)) { - updateJson(tree, runtimeTsconfigPath, (json) => { + if (tree.exists(runtimeTsconfigPath)) { + updateJson(tree, runtimeTsconfigPath, (json) => { + if (options.inSourceTests) { (json.compilerOptions.types ??= []).push('vitest/importMeta'); - return json; - }); - } - - addTsLibDependencies(tree); - } else { - if (tree.exists(runtimeTsconfigPath)) { - updateJson(tree, runtimeTsconfigPath, (json) => { + } else { const uniqueExclude = new Set([ ...(json.exclude || []), 'vite.config.ts', @@ -247,14 +247,19 @@ function updateTsConfig( 'src/**/*.spec.jsx', ]); json.exclude = [...uniqueExclude]; - return json; - }); - } else { - logger.warn( - `Couldn't find a runtime tsconfig file at ${runtimeTsconfigPath} to exclude the test files from. ` + - `If you're using a different filename for your runtime tsconfig, please provide it with the '--runtimeTsconfigFileName' flag.` - ); - } + } + + if (setupFile) { + json.exclude = [...(json.exclude ?? []), setupFile]; + } + + return json; + }); + } else { + logger.warn( + `Couldn't find a runtime tsconfig file at ${runtimeTsconfigPath} to exclude the test files from. ` + + `If you're using a different filename for your runtime tsconfig, please provide it with the '--runtimeTsconfigFileName' flag.` + ); } } @@ -290,4 +295,11 @@ function getCoverageProviderDependency( } } +function tryFindSetupFile(tree: Tree, projectRoot: string) { + const setupFile = joinPathFragments('src', 'test-setup.ts'); + if (tree.exists(joinPathFragments(projectRoot, setupFile))) { + return setupFile; + } +} + export default vitestGenerator; diff --git a/packages/vite/src/utils/generator-utils.ts b/packages/vite/src/utils/generator-utils.ts index 8e76ac40095d3..964e4e348b199 100644 --- a/packages/vite/src/utils/generator-utils.ts +++ b/packages/vite/src/utils/generator-utils.ts @@ -348,6 +348,8 @@ export interface ViteConfigFileOptions { imports?: string[]; plugins?: string[]; coverageProvider?: 'v8' | 'istanbul' | 'custom'; + setupFile?: string; + useEsmExtension?: boolean; } export function createOrEditViteConfig( @@ -357,11 +359,15 @@ export function createOrEditViteConfig( projectAlreadyHasViteTargets?: TargetFlags, vitestFileName?: boolean ) { - const { root: projectRoot } = readProjectConfiguration(tree, options.project); + const { root: projectRoot, projectType } = readProjectConfiguration( + tree, + options.project + ); + const extension = options.useEsmExtension ? 'mts' : 'ts'; const viteConfigPath = vitestFileName - ? `${projectRoot}/vitest.config.ts` - : `${projectRoot}/vite.config.ts`; + ? `${projectRoot}/vitest.config.${extension}` + : `${projectRoot}/vite.config.${extension}`; const isUsingTsPlugin = isUsingTsSolutionSetup(tree); const buildOutDir = isUsingTsPlugin @@ -436,12 +442,13 @@ export function createOrEditViteConfig( watch: false, globals: true, environment: '${options.testEnvironment ?? 'jsdom'}', - include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],${ - options.inSourceTests - ? ` - includeSource: ['src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],` - : '' - } + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], +${options.setupFile ? ` setupFiles: ['${options.setupFile}'],\n` : ''}\ +${ + options.inSourceTests + ? ` includeSource: ['src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],\n` + : '' +}\ reporters: ['default'], coverage: { reportsDirectory: '${reportsDirectory}',