diff --git a/.gitignore b/.gitignore
index 1ec0aa30b1cf3a..71311c125c8430 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ tmp
*.log
jest.debug.config.js
.tool-versions
+/.nx
/.nx-cache
/.verdaccio/build/local-registry
/graph/client/src/assets/environment.js
@@ -19,7 +20,6 @@ jest.debug.config.js
/graph/client/src/assets/generated-task-graphs
/nx-dev/nx-dev/public/documentation
/nx-dev/nx-dev/public/images/open-graph
-
# Issues scraper creates these files, stored by github's cache
/scripts/issues-scraper/cached
diff --git a/docs/map.json b/docs/map.json
index 934778e187d72e..ac1c941031e00e 100644
--- a/docs/map.json
+++ b/docs/map.json
@@ -2079,6 +2079,20 @@
}
]
},
+ {
+ "name": "vue",
+ "id": "vue",
+ "description": "Vue package.",
+ "itemList": [
+ {
+ "id": "overview",
+ "path": "/packages/vue",
+ "name": "Overview of the Nx Vue Plugin",
+ "description": "The Nx Plugin for Vue contains generators for managing Vue applications and libraries within an Nx workspace. This page also explains how to configure Vue on your Nx workspace.",
+ "file": "shared/packages/vue/vue-plugin"
+ }
+ ]
+ },
{
"name": "webpack",
"id": "webpack",
diff --git a/docs/packages.json b/docs/packages.json
index e48b86656430ad..11483d176b7abd 100644
--- a/docs/packages.json
+++ b/docs/packages.json
@@ -361,6 +361,16 @@
"generators": ["init", "configuration", "vitest"]
}
},
+ {
+ "name": "vue",
+ "packageName": "vue",
+ "description": "The Vue plugin for Nx contains executors and generators for managing Vue applications and libraries within an Nx workspace. It provides:\n\n\n- Integration with libraries such as Jest, Cypress, and Storybook.\n\n- Generators for applications, libraries, components, hooks, and more.\n\n- Library build support for publishing packages to npm or other registries.\n\n- Utilities for automatic workspace refactoring.",
+ "path": "generated/packages/vite.json",
+ "schemas": {
+ "executors": [],
+ "generators": ["init", "library", "application", "component"]
+ }
+ },
{
"name": "web",
"packageName": "web",
diff --git a/docs/shared/packages/vue/vue-plugin.md b/docs/shared/packages/vue/vue-plugin.md
new file mode 100644
index 00000000000000..909efd6957661d
--- /dev/null
+++ b/docs/shared/packages/vue/vue-plugin.md
@@ -0,0 +1,60 @@
+---
+title: Overview of the Nx Vue Plugin
+description: The Nx Plugin for Vue contains generators for managing Vue applications and libraries within an Nx workspace. This page also explains how to configure Vue on your Nx workspace.
+---
+
+The Nx plugin for [Vue](https://vuejs.org/).
+
+## Setting up a new Nx workspace with Vue
+
+You can create a new workspace that uses Vue with one of the following commands:
+
+- Generate a new monorepo with a Vue app set up with Vue
+
+```shell
+npx create-nx-workspace@latest --preset=vue
+```
+
+## Add Vue to an existing workspace
+
+There are a number of ways to use Vue in your existing workspace.
+
+### Install the `@nx/vue` plugin
+
+{% tabs %}
+{% tab label="npm" %}
+
+```shell
+npm install -D @nx/vue
+```
+
+{% /tab %}
+{% tab label="yarn" %}
+
+```shell
+yarn add -D @nx/vue
+```
+
+{% /tab %}
+{% tab label="pnpm" %}
+
+```shell
+pnpm install -D @nx/vue
+```
+
+{% /tab %}
+{% /tabs %}
+
+### Generate a new project using Vue
+
+To generate a Vue application, run the following:
+
+```bash
+nx g @nx/vue:app my-app
+```
+
+To generate a Vue library, run the following:
+
+```bash
+nx g @nx/vue:lib my-lib
+```
diff --git a/e2e/vue/src/vue.test.ts b/e2e/vue/src/vue.test.ts
new file mode 100644
index 00000000000000..a6023b20882e5c
--- /dev/null
+++ b/e2e/vue/src/vue.test.ts
@@ -0,0 +1,63 @@
+import {
+ cleanupProject,
+ killPorts,
+ newProject,
+ promisifiedTreeKill,
+ runCLI,
+ runCLIAsync,
+ runCommandUntil,
+ uniq,
+} from '@nx/e2e/utils';
+
+const myApp = uniq('my-app');
+const myLib = uniq('my-lib');
+
+describe('Vue Plugin', () => {
+ let proj: string;
+
+ describe('Vite on React apps', () => {
+ describe('successfully create and serve a vue app', () => {
+ beforeEach(() => {
+ proj = newProject({
+ unsetProjectNameAndRootFormat: false,
+ });
+ runCLI(`generate @nx/vue:app ${myApp}`);
+ runCLI(`generate @nx/vue:lib ${myLib} --bundler=vite`);
+ });
+ afterEach(() => cleanupProject());
+
+ it('should serve application in dev mode', async () => {
+ const p = await runCommandUntil(`run ${myApp}:serve`, (output) => {
+ return output.includes('Local:');
+ });
+ try {
+ await promisifiedTreeKill(p.pid, 'SIGKILL');
+ await killPorts(4200);
+ } catch (e) {
+ // ignore
+ }
+ }, 200_000);
+
+ it('should test application', async () => {
+ const result = await runCLIAsync(`test ${myApp}`);
+ expect(result.combinedOutput).toContain(
+ `Successfully ran target test for project ${myApp}`
+ );
+ });
+
+ it('should build application', async () => {
+ const result = await runCLIAsync(`build ${myApp}`);
+ expect(result.combinedOutput).toContain(
+ `Successfully ran target build for project ${myApp}`
+ );
+ });
+
+ it('should build library', async () => {
+ const result = await runCLIAsync(`build ${myLib}`);
+ expect(result.combinedOutput).toContain(
+ `Successfully ran target build for project ${myLib}`
+ );
+ });
+ });
+ });
+});
diff --git a/packages/linter/index.ts b/packages/linter/index.ts
index dc8c31e7133369..74631defb1a941 100644
--- a/packages/linter/index.ts
+++ b/packages/linter/index.ts
@@ -3,6 +3,7 @@ export { lintInitGenerator } from './src/generators/init/init';
export { Linter } from './src/generators/utils/linter';
/** @deprecated This will be removed in v17 */
export * from './src/utils/convert-tslint-to-eslint';
+export * from './src/utils/versions';
// @nx/angular needs it for the Angular CLI workspace migration to Nx to
// infer whether a config is using type aware rules and set the
diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts
index cd9a5113ccc3e3..2c04b808011aaa 100644
--- a/packages/react/src/generators/application/application.spec.ts
+++ b/packages/react/src/generators/application/application.spec.ts
@@ -63,6 +63,7 @@ describe('app', () => {
'vitest/importMeta',
'vite/client',
'node',
+ 'vitest',
]);
});
diff --git a/packages/react/src/generators/application/application.ts b/packages/react/src/generators/application/application.ts
index 9e6fe7d9e525ba..9eea9beea4d3f9 100644
--- a/packages/react/src/generators/application/application.ts
+++ b/packages/react/src/generators/application/application.ts
@@ -39,6 +39,7 @@ import {
addExtendsToLintConfig,
isEslintConfigSupported,
} from '@nx/linter/src/generators/utils/eslint-file';
+import { createOrEditViteConfig } from '@nx/vite';
async function addLinting(host: Tree, options: NormalizedSchema) {
const tasks: GeneratorCallback[] = [];
@@ -138,6 +139,28 @@ export async function applicationGeneratorInternal(
skipFormat: true,
});
tasks.push(viteTask);
+ createOrEditViteConfig(
+ host,
+ {
+ project: options.projectName,
+ includeLib: false,
+ includeVitest: options.unitTestRunner === 'vitest',
+ inSourceTests: options.inSourceTests,
+ rollupOptionsExternal: [
+ `'react'`,
+ `'react-dom'`,
+ `'react/jsx-runtime'`,
+ ],
+ rollupOptionsExternalString: `"'react', 'react-dom', 'react/jsx-runtime'"`,
+ imports: [
+ options.compiler === 'swc'
+ ? `import react from '@vitejs/plugin-react-swc'`
+ : `import react from '@vitejs/plugin-react'`,
+ ],
+ plugins: ['react()'],
+ },
+ false
+ );
} else if (options.bundler === 'webpack') {
const { webpackInitGenerator } = ensurePackage<
typeof import('@nx/webpack')
@@ -180,6 +203,28 @@ export async function applicationGeneratorInternal(
skipFormat: true,
});
tasks.push(vitestTask);
+ createOrEditViteConfig(
+ host,
+ {
+ project: options.projectName,
+ includeLib: false,
+ includeVitest: true,
+ inSourceTests: options.inSourceTests,
+ rollupOptionsExternal: [
+ `'react'`,
+ `'react-dom'`,
+ `'react/jsx-runtime'`,
+ ],
+ rollupOptionsExternalString: `"'react', 'react-dom', 'react/jsx-runtime'"`,
+ imports: [
+ options.compiler === 'swc'
+ ? `import react from '@vitejs/plugin-react-swc'`
+ : `import react from '@vitejs/plugin-react'`,
+ ],
+ plugins: ['react()'],
+ },
+ true
+ );
}
if (
diff --git a/packages/react/src/generators/application/lib/find-free-port.spec.ts b/packages/react/src/generators/application/lib/find-free-port.spec.ts
index 42ed2a4d0802a7..97fcd26ec534bc 100644
--- a/packages/react/src/generators/application/lib/find-free-port.spec.ts
+++ b/packages/react/src/generators/application/lib/find-free-port.spec.ts
@@ -5,7 +5,7 @@ import { findFreePort } from './find-free-port';
describe('findFreePort', () => {
it('should return the largest port + 1', () => {
- const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
+ const tree = createTreeWithEmptyWorkspace();
addProject(tree, 'app1', 4200);
addProject(tree, 'app2', 4201);
addProject(tree, 'no-serve');
@@ -16,7 +16,7 @@ describe('findFreePort', () => {
});
it('should default to port 4200', () => {
- const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
+ const tree = createTreeWithEmptyWorkspace();
addProject(tree, 'no-serve');
const port = findFreePort(tree);
diff --git a/packages/react/src/generators/application/lib/find-free-port.ts b/packages/react/src/generators/application/lib/find-free-port.ts
index 887de4cef663c8..53de59facbb373 100644
--- a/packages/react/src/generators/application/lib/find-free-port.ts
+++ b/packages/react/src/generators/application/lib/find-free-port.ts
@@ -1,8 +1,7 @@
-import { Tree } from 'nx/src/generators/tree';
-import { getProjects } from '@nx/devkit';
+import { getProjects, Tree } from '@nx/devkit';
-export function findFreePort(host: Tree) {
- const projects = getProjects(host);
+export function findFreePort(tree: Tree) {
+ const projects = getProjects(tree);
let port = -Infinity;
for (const [, p] of projects.entries()) {
const curr = p.targets?.serve?.options?.port;
diff --git a/packages/react/src/generators/library/library.spec.ts b/packages/react/src/generators/library/library.spec.ts
index ebc8bfd12427f7..2420d6b64e499a 100644
--- a/packages/react/src/generators/library/library.spec.ts
+++ b/packages/react/src/generators/library/library.spec.ts
@@ -80,6 +80,7 @@ describe('lib', () => {
'vitest/importMeta',
'vite/client',
'node',
+ 'vitest',
]);
});
diff --git a/packages/react/src/generators/library/library.ts b/packages/react/src/generators/library/library.ts
index 413291220efa88..969cc4b8d0917c 100644
--- a/packages/react/src/generators/library/library.ts
+++ b/packages/react/src/generators/library/library.ts
@@ -69,9 +69,8 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) {
// Set up build target
if (options.buildable && options.bundler === 'vite') {
- const { viteConfigurationGenerator } = ensurePackage<
- typeof import('@nx/vite')
- >('@nx/vite', nxVersion);
+ const { viteConfigurationGenerator, createOrEditViteConfig } =
+ ensurePackage('@nx/vite', nxVersion);
const viteTask = await viteConfigurationGenerator(host, {
uiFramework: 'react',
project: options.name,
@@ -84,6 +83,28 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) {
testEnvironment: 'jsdom',
});
tasks.push(viteTask);
+ createOrEditViteConfig(
+ host,
+ {
+ project: options.name,
+ includeLib: true,
+ includeVitest: options.unitTestRunner === 'vitest',
+ inSourceTests: options.inSourceTests,
+ rollupOptionsExternal: [
+ `'react'`,
+ `'react-dom'`,
+ `'react/jsx-runtime'`,
+ ],
+ rollupOptionsExternalString: `"'react', 'react-dom', 'react/jsx-runtime'"`,
+ imports: [
+ options.compiler === 'swc'
+ ? `import react from '@vitejs/plugin-react-swc'`
+ : `import react from '@vitejs/plugin-react'`,
+ ],
+ plugins: ['react()'],
+ },
+ false
+ );
} else if (options.buildable && options.bundler === 'rollup') {
const rollupTask = await addRollupBuildTarget(host, options);
tasks.push(rollupTask);
@@ -120,10 +141,9 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) {
options.unitTestRunner === 'vitest' &&
options.bundler !== 'vite' // tests are already configured if bundler is vite
) {
- const { vitestGenerator } = ensurePackage(
- '@nx/vite',
- nxVersion
- );
+ const { vitestGenerator, createOrEditViteConfig } = ensurePackage<
+ typeof import('@nx/vite')
+ >('@nx/vite', nxVersion);
const vitestTask = await vitestGenerator(host, {
uiFramework: 'react',
project: options.name,
@@ -133,6 +153,24 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) {
testEnvironment: 'jsdom',
});
tasks.push(vitestTask);
+ createOrEditViteConfig(
+ host,
+ {
+ project: options.name,
+ includeLib: true,
+ includeVitest: true,
+ inSourceTests: options.inSourceTests,
+ rollupOptionsExternal: [
+ `'react'`,
+ `'react-dom'`,
+ `'react/jsx-runtime'`,
+ ],
+ rollupOptionsExternalString: `"'react', 'react-dom', 'react/jsx-runtime'"`,
+ imports: [`import react from '@vitejs/plugin-react'`],
+ plugins: ['react()'],
+ },
+ true
+ );
}
if (options.component) {
diff --git a/packages/vite/src/generators/configuration/__snapshots__/configuration.spec.ts.snap b/packages/vite/src/generators/configuration/__snapshots__/configuration.spec.ts.snap
index 13b7f088bbde14..6255454a5b971a 100644
--- a/packages/vite/src/generators/configuration/__snapshots__/configuration.spec.ts.snap
+++ b/packages/vite/src/generators/configuration/__snapshots__/configuration.spec.ts.snap
@@ -1,24 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`@nx/vite:configuration library mode should add config for building library 1`] = `
-"///
+"///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
-import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import dts from 'vite-plugin-dts';
import * as path from 'path';
+import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({
cacheDir: '../node_modules/.vite/my-lib',
plugins: [
+ react(),
+ nxViteTsPaths(),
dts({
entryRoot: 'src',
tsConfigFilePath: path.join(__dirname, 'tsconfig.lib.json'),
skipDiagnostics: true,
}),
- react(),
- nxViteTsPaths(),
],
// Uncomment this if you are using workers.
@@ -48,24 +48,24 @@ export default defineConfig({
`;
exports[`@nx/vite:configuration library mode should set up non buildable library correctly 1`] = `
-"///
+"///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
-import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import dts from 'vite-plugin-dts';
import * as path from 'path';
+import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({
cacheDir: '../../node_modules/.vite/react-lib-nonb-jest',
plugins: [
+ react(),
+ nxViteTsPaths(),
dts({
entryRoot: 'src',
tsConfigFilePath: path.join(__dirname, 'tsconfig.lib.json'),
skipDiagnostics: true,
}),
- react(),
- nxViteTsPaths(),
],
// Uncomment this if you are using workers.
@@ -143,7 +143,7 @@ exports[`@nx/vite:configuration library mode should set up non buildable library
import * as path from 'path';
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
-import viteTsConfigPaths from 'vite-tsconfig-paths';
+import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({
// Configuration for building your library.
@@ -165,12 +165,8 @@ export default defineConfig({
},
},
plugins: [
- ...[
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
+ nxViteTsPaths(),
+ react(),
dts({
entryRoot: 'src',
tsConfigFilePath: path.join(__dirname, 'tsconfig.lib.json'),
@@ -180,9 +176,7 @@ export default defineConfig({
test: {
globals: true,
- cache: {
- dir: '../../node_modules/.vitest',
- },
+ cache: { dir: '../../node_modules/.vitest' },
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
},
@@ -286,7 +280,7 @@ exports[`@nx/vite:configuration transform React app to use Vite by providing cus
`;
exports[`@nx/vite:configuration transform React app to use Vite should create vite.config file at the root of the app 1`] = `
-"///
+"///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
@@ -427,7 +421,7 @@ exports[`@nx/vite:configuration transform React app to use Vite should transform
`;
exports[`@nx/vite:configuration transform Web app to use Vite should create vite.config file at the root of the app 1`] = `
-"///
+"///
import { defineConfig } from 'vite';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
@@ -555,7 +549,7 @@ exports[`@nx/vite:configuration transform Web app to use Vite should transform w
`;
exports[`@nx/vite:configuration vitest should create a vitest configuration if "includeVitest" is true 1`] = `
-"///
+"///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
diff --git a/packages/vite/src/generators/configuration/configuration.spec.ts b/packages/vite/src/generators/configuration/configuration.spec.ts
index 4d423ba05a92a5..13920f06013148 100644
--- a/packages/vite/src/generators/configuration/configuration.spec.ts
+++ b/packages/vite/src/generators/configuration/configuration.spec.ts
@@ -350,7 +350,6 @@ describe('@nx/vite:configuration', () => {
const { Confirm } = require('enquirer');
const confirmSpy = jest.spyOn(Confirm.prototype, 'run');
confirmSpy.mockResolvedValue(true);
- expect.assertions(2);
mockReactLibNonBuildableVitestRunnerGenerator(tree);
diff --git a/packages/vite/src/generators/configuration/configuration.ts b/packages/vite/src/generators/configuration/configuration.ts
index bafa4642cae372..f433f5f9dfa751 100644
--- a/packages/vite/src/generators/configuration/configuration.ts
+++ b/packages/vite/src/generators/configuration/configuration.ts
@@ -198,7 +198,32 @@ export async function viteConfigurationGenerator(
});
}
- createOrEditViteConfig(tree, schema, false, projectAlreadyHasViteTargets);
+ if (schema.uiFramework === 'react') {
+ createOrEditViteConfig(
+ tree,
+ {
+ project: schema.project,
+ includeLib: schema.includeLib,
+ includeVitest: schema.includeVitest,
+ inSourceTests: schema.inSourceTests,
+ rollupOptionsExternal: [
+ `'react'`,
+ `'react-dom'`,
+ `'react/jsx-runtime'`,
+ ],
+ rollupOptionsExternalString: `"'react', 'react-dom', 'react/jsx-runtime'"`,
+ imports: [
+ schema.compiler === 'swc'
+ ? `import react from '@vitejs/plugin-react-swc'`
+ : `import react from '@vitejs/plugin-react'`,
+ ],
+ plugins: ['react()'],
+ },
+ false
+ );
+ } else {
+ createOrEditViteConfig(tree, schema, false, projectAlreadyHasViteTargets);
+ }
if (schema.includeVitest) {
const vitestTask = await vitestGenerator(tree, {
diff --git a/packages/vite/src/generators/init/init.ts b/packages/vite/src/generators/init/init.ts
index bf2c38b9611904..9a52754d89163e 100644
--- a/packages/vite/src/generators/init/init.ts
+++ b/packages/vite/src/generators/init/init.ts
@@ -45,7 +45,7 @@ function checkDependenciesInstalled(host: Tree, schema: InitGeneratorSchema) {
devDependencies['happy-dom'] = happyDomVersion;
} else if (schema.testEnvironment === 'edge-runtime') {
devDependencies['@edge-runtime/vm'] = edgeRuntimeVmVersion;
- } else if (schema.testEnvironment !== 'node') {
+ } else if (schema.testEnvironment !== 'node' && schema.testEnvironment) {
logger.info(
`A custom environment was provided: ${schema.testEnvironment}. You need to install it manually.`
);
diff --git a/packages/vite/src/generators/vitest/__snapshots__/vitest.spec.ts.snap b/packages/vite/src/generators/vitest/__snapshots__/vitest.spec.ts.snap
index 4087eb8e6d90aa..247c582f920251 100644
--- a/packages/vite/src/generators/vitest/__snapshots__/vitest.spec.ts.snap
+++ b/packages/vite/src/generators/vitest/__snapshots__/vitest.spec.ts.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`vitest generator insourceTests should add the insourceSource option in the vite config 1`] = `
-"///
+"///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
@@ -33,7 +33,7 @@ export default defineConfig({
`;
exports[`vitest generator vite.config should create correct vite.config.ts file for apps 1`] = `
-"///
+"///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
@@ -61,7 +61,7 @@ export default defineConfig({
`;
exports[`vitest generator vite.config should create correct vite.config.ts file for non buildable libs 1`] = `
-"///
+"///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
diff --git a/packages/vite/src/generators/vitest/vitest-generator.ts b/packages/vite/src/generators/vitest/vitest-generator.ts
index 92388d8d0aef6d..9cfab4d019defc 100644
--- a/packages/vite/src/generators/vitest/vitest-generator.ts
+++ b/packages/vite/src/generators/vitest/vitest-generator.ts
@@ -52,15 +52,36 @@ export async function vitestGenerator(
tasks.push(initTask);
if (!schema.skipViteConfig) {
- createOrEditViteConfig(
- tree,
- {
- ...schema,
- includeVitest: true,
- includeLib: projectType === 'library',
- },
- true
- );
+ if (schema.uiFramework === 'react') {
+ createOrEditViteConfig(
+ tree,
+ {
+ project: schema.project,
+ includeLib: projectType === 'library',
+ includeVitest: true,
+ inSourceTests: schema.inSourceTests,
+ rollupOptionsExternal: [
+ `'react'`,
+ `'react-dom'`,
+ `'react/jsx-runtime'`,
+ ],
+ rollupOptionsExternalString: `"'react', 'react-dom', 'react/jsx-runtime'"`,
+ imports: [`import react from '@vitejs/plugin-react'`],
+ plugins: ['react()'],
+ },
+ true
+ );
+ } else {
+ createOrEditViteConfig(
+ tree,
+ {
+ ...schema,
+ includeVitest: true,
+ includeLib: projectType === 'library',
+ },
+ true
+ );
+ }
}
createFiles(tree, schema, root);
@@ -89,26 +110,55 @@ function updateTsConfig(
options: VitestGeneratorSchema,
projectRoot: string
) {
- updateJson(tree, joinPathFragments(projectRoot, 'tsconfig.json'), (json) => {
- if (
- json.references &&
- !json.references.some((r) => r.path === './tsconfig.spec.json')
- ) {
- json.references.push({
- path: './tsconfig.spec.json',
- });
- }
+ if (tree.exists(joinPathFragments(projectRoot, 'tsconfig.spec.json'))) {
+ updateJson(
+ tree,
+ joinPathFragments(projectRoot, 'tsconfig.spec.json'),
+ (json) => {
+ if (!json.compilerOptions?.types?.includes('vitest')) {
+ if (json.compilerOptions?.types) {
+ json.compilerOptions.types.push('vitest');
+ } else {
+ json.compilerOptions ??= {};
+ json.compilerOptions.types = ['vitest'];
+ }
+ }
+ return json;
+ }
+ );
- if (!json.compilerOptions?.types?.includes('vitest')) {
- if (json.compilerOptions?.types) {
- json.compilerOptions.types.push('vitest');
- } else {
- json.compilerOptions ??= {};
- json.compilerOptions.types = ['vitest'];
+ updateJson(
+ tree,
+ joinPathFragments(projectRoot, 'tsconfig.json'),
+ (json) => {
+ if (
+ json.references &&
+ !json.references.some((r) => r.path === './tsconfig.spec.json')
+ ) {
+ json.references.push({
+ path: './tsconfig.spec.json',
+ });
+ }
+ return json;
}
- }
- return json;
- });
+ );
+ } else {
+ updateJson(
+ tree,
+ joinPathFragments(projectRoot, 'tsconfig.json'),
+ (json) => {
+ if (!json.compilerOptions?.types?.includes('vitest')) {
+ if (json.compilerOptions?.types) {
+ json.compilerOptions.types.push('vitest');
+ } else {
+ json.compilerOptions ??= {};
+ json.compilerOptions.types = ['vitest'];
+ }
+ }
+ return json;
+ }
+ );
+ }
if (options.inSourceTests) {
const tsconfigLibPath = joinPathFragments(projectRoot, 'tsconfig.lib.json');
diff --git a/packages/vite/src/generators/vitest/vitest.spec.ts b/packages/vite/src/generators/vitest/vitest.spec.ts
index 8bb3ca9474028e..a1ccf542303119 100644
--- a/packages/vite/src/generators/vitest/vitest.spec.ts
+++ b/packages/vite/src/generators/vitest/vitest.spec.ts
@@ -96,6 +96,7 @@ describe('vitest generator', () => {
"vitest/importMeta",
"vite/client",
"node",
+ "vitest",
],
},
"extends": "./tsconfig.json",
diff --git a/packages/vite/src/utils/__snapshots__/vite-config-edit-utils.spec.ts.snap b/packages/vite/src/utils/__snapshots__/vite-config-edit-utils.spec.ts.snap
index 4972eb3df2939c..238797a188409f 100644
--- a/packages/vite/src/utils/__snapshots__/vite-config-edit-utils.spec.ts.snap
+++ b/packages/vite/src/utils/__snapshots__/vite-config-edit-utils.spec.ts.snap
@@ -1,19 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ensureViteConfigIsCorrect should add build and test options if defineConfig is empty 1`] = `
-"import dts from 'vite-plugin-dts';
-import { joinPathFragments } from '@nx/devkit';
+"import dts from 'vite-plugin-dts';import { joinPathFragments } from '@nx/devkit'
///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({
-
// Configuration for building your library.
// See: https://vitejs.dev/guide/build.html#library-mode
- build: {
+ plugins: [react(),
+nxViteTsPaths()],build: {
lib: {
// Could also be a dictionary or array of multiple entry points.
entry: 'src/index.ts',
@@ -27,18 +26,7 @@ import { joinPathFragments } from '@nx/devkit';
// External packages that should not be bundled into your library.
external: ["'react', 'react-dom', 'react/jsx-runtime'"]
}
- },plugins: [
- dts({
- entryRoot: 'src',
- tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'),
- skipDiagnostics: true,
- }),
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
- test: {
+ },test: {
globals: true,
cache: {
dir: '../node_modules/.vitest'
@@ -50,11 +38,10 @@ import { joinPathFragments } from '@nx/devkit';
`;
exports[`ensureViteConfigIsCorrect should add build option but not update test option if test already setup 1`] = `
-"import dts from 'vite-plugin-dts';
-import { joinPathFragments } from '@nx/devkit';
+"import dts from 'vite-plugin-dts';import { joinPathFragments } from '@nx/devkit'
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({
@@ -74,19 +61,9 @@ import { defineConfig } from 'vite';
// External packages that should not be bundled into your library.
external: ["'react', 'react-dom', 'react/jsx-runtime'"]
}
- },plugins: [
- ...[
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
- dts({
- entryRoot: 'src',
- tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'),
- skipDiagnostics: true,
- }),
- ],
+ },plugins: [react(),
+nxViteTsPaths(),
+],
test: {
globals: true,
@@ -102,11 +79,10 @@ import { defineConfig } from 'vite';
`;
exports[`ensureViteConfigIsCorrect should add build options if build options don't exist 1`] = `
-"import dts from 'vite-plugin-dts';
-import { joinPathFragments } from '@nx/devkit';
+"import dts from 'vite-plugin-dts';import { joinPathFragments } from '@nx/devkit'
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({
@@ -126,19 +102,9 @@ import { defineConfig } from 'vite';
// External packages that should not be bundled into your library.
external: ["'react', 'react-dom', 'react/jsx-runtime'"]
}
- },plugins: [
- ...[
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
- dts({
- entryRoot: 'src',
- tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'),
- skipDiagnostics: true,
- }),
- ],
+ },plugins: [react(),
+nxViteTsPaths(),
+],
test: {
globals: true,
@@ -154,11 +120,10 @@ import { defineConfig } from 'vite';
`;
exports[`ensureViteConfigIsCorrect should add build options if defineConfig is not used 1`] = `
-"import dts from 'vite-plugin-dts';
-import { joinPathFragments } from '@nx/devkit';
+"import dts from 'vite-plugin-dts';import { joinPathFragments } from '@nx/devkit'
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default {
// Configuration for building your library.
@@ -185,19 +150,9 @@ import { defineConfig } from 'vite';
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
},
- plugins: [
- ...[
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
- dts({
- entryRoot: 'src',
- tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'),
- skipDiagnostics: true,
- }),
- ],
+ plugins: [react(),
+nxViteTsPaths(),
+],
};
"
`;
@@ -219,33 +174,22 @@ exports[`ensureViteConfigIsCorrect should add build options if it is using condi
my: 'option',
},
..."\\n // Configuration for building your library.\\n // See: https://vitejs.dev/guide/build.html#library-mode\\n build: {\\n lib: {\\n // Could also be a dictionary or array of multiple entry points.\\n entry: 'src/index.ts',\\n name: 'my-app',\\n fileName: 'index',\\n // Change this to the formats you want to support.\\n // Don't forget to update your package.json as well.\\n formats: ['es', 'cjs']\\n },\\n rollupOptions: {\\n // External packages that should not be bundled into your library.\\n external: [\\"'react', 'react-dom', 'react/jsx-runtime'\\"]\\n }\\n },"
- }
+ }
}
})
"
`;
exports[`ensureViteConfigIsCorrect should add new build options if some build options already exist 1`] = `
-"import dts from 'vite-plugin-dts';
-import { joinPathFragments } from '@nx/devkit';
+"import dts from 'vite-plugin-dts';import { joinPathFragments } from '@nx/devkit'
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({
- plugins: [
- ...[
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
- dts({
- entryRoot: 'src',
- tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'),
- skipDiagnostics: true,
- }),
- ],
+ plugins: [react(),
+nxViteTsPaths(),
+],
test: {
globals: true,
@@ -257,11 +201,11 @@ import { defineConfig } from 'vite';
},
build: {
- ...{
- my: 'option',
- },
- ...{"lib":{"entry":"src/index.ts","name":"my-app","fileName":"index","formats":["es","cjs"]},"rollupOptions":{"external":["'react', 'react-dom', 'react/jsx-runtime'"]}}
- }
+ 'my': 'option',
+'lib': {"entry":"src/index.ts","name":"my-app","fileName":"index","formats":["es","cjs"]},
+'rollupOptions': {"external":["'react', 'react-dom', 'react/jsx-runtime'"]},
+
+ }
});
"
@@ -271,25 +215,17 @@ exports[`ensureViteConfigIsCorrect should not do anything if cannot understand s
exports[`ensureViteConfigIsCorrect should not do anything if project has everything setup already 1`] = `
"
- ///
- import { defineConfig } from 'vite';
+import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import dts from 'vite-plugin-dts';
import { joinPathFragments } from '@nx/devkit';
export default defineConfig({
- plugins: [
- dts({
- entryRoot: 'src',
- tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'),
- skipDiagnostics: true,
- }),
- react(),
- viteTsConfigPaths({
- root: '../../../',
- }),
- ],
+ plugins: [dts({ entryRoot: 'src', tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'), skipDiagnostics: true }),
+react(),
+nxViteTsPaths(),
+],
// Configuration for building your library.
// See: https://vitejs.dev/guide/build.html#library-mode
@@ -322,40 +258,31 @@ exports[`ensureViteConfigIsCorrect should not do anything if project has everyth
`;
exports[`ensureViteConfigIsCorrect should update both test and build options - keep existing settings 1`] = `
-"import dts from 'vite-plugin-dts';
-import { joinPathFragments } from '@nx/devkit';
+"import dts from 'vite-plugin-dts';import { joinPathFragments } from '@nx/devkit'
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({
- plugins: [
- ...[
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
- dts({
- entryRoot: 'src',
- tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'),
- skipDiagnostics: true,
- }),
- ],
+ plugins: [react(),
+nxViteTsPaths(),
+],
test: {
- ...{
- my: 'option',
- },
- ...{"globals":true,"cache":{"dir":"../node_modules/.vitest"},"environment":"jsdom","include":["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"]}
- },
+ 'my': 'option',
+'globals': true,
+'cache': {"dir":"../node_modules/.vitest"},
+'environment': "jsdom",
+'include': ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
+
+ },
build: {
- ...{
- my: 'option',
- },
- ...{"lib":{"entry":"src/index.ts","name":"my-app","fileName":"index","formats":["es","cjs"]},"rollupOptions":{"external":["'react', 'react-dom', 'react/jsx-runtime'"]}}
- }
+ 'my': 'option',
+'lib': {"entry":"src/index.ts","name":"my-app","fileName":"index","formats":["es","cjs"]},
+'rollupOptions': {"external":["'react', 'react-dom', 'react/jsx-runtime'"]},
+
+ }
});
"
diff --git a/packages/vite/src/utils/generator-utils.ts b/packages/vite/src/utils/generator-utils.ts
index c89d04e6fff051..cbaf67522a8c85 100644
--- a/packages/vite/src/utils/generator-utils.ts
+++ b/packages/vite/src/utils/generator-utils.ts
@@ -441,14 +441,14 @@ export function moveAndEditIndexHtml(
const indexHtmlContent = tree.read(indexHtmlPath, 'utf8');
if (
!indexHtmlContent.includes(
- ``
+ ``
)
) {
tree.write(
`${projectConfig.root}/index.html`,
indexHtmlContent.replace(
'
-
-
+
+
',
- `
+ `
`
)
);
@@ -461,25 +461,37 @@ export function moveAndEditIndexHtml(
tree.write(
`${projectConfig.root}/index.html`,
`
-
+