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}',