diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts
index 9939900410..7ba5704f4b 100644
--- a/packages/react/src/generators/application/application.spec.ts
+++ b/packages/react/src/generators/application/application.spec.ts
@@ -941,7 +941,10 @@ describe('app', () => {
it('should create correct tsconfig compilerOptions', () => {
const tsconfigJson = readJson(viteAppTree, '/apps/my-app/tsconfig.json');
- expect(tsconfigJson.compilerOptions.types).toMatchObject(['vite/client']);
+ expect(tsconfigJson.compilerOptions.types).toMatchObject([
+ 'vite/client',
+ 'vitest',
+ ]);
});
it('should create index.html and vite.config file at the root of the app', () => {
diff --git a/packages/react/src/utils/create-ts-config.ts b/packages/react/src/utils/create-ts-config.ts
index 363ddaa85c..6a1ea02232 100644
--- a/packages/react/src/utils/create-ts-config.ts
+++ b/packages/react/src/utils/create-ts-config.ts
@@ -11,6 +11,7 @@ export function createTsConfig(
style?: string;
bundler?: string;
rootProject?: boolean;
+ unitTestRunner?: string;
},
relativePathToRootTsConfig: string
) {
@@ -36,7 +37,10 @@ export function createTsConfig(
}
if (options.bundler === 'vite') {
- json.compilerOptions.types = ['vite/client'];
+ json.compilerOptions.types =
+ options.unitTestRunner === 'vitest'
+ ? ['vite/client', 'vitest']
+ : ['vite/client'];
}
// inline tsconfig.base.json into the project
diff --git a/packages/storybook/src/generators/configuration/configuration-nested.spec.ts b/packages/storybook/src/generators/configuration/configuration-nested.spec.ts
index 640c9a2f2a..7f3e0bfd9a 100644
--- a/packages/storybook/src/generators/configuration/configuration-nested.spec.ts
+++ b/packages/storybook/src/generators/configuration/configuration-nested.spec.ts
@@ -36,7 +36,7 @@ describe('@nrwl/storybook:configuration for workspaces with Root project', () =>
skipLibCheck: true,
strict: true,
target: 'ESNext',
- types: ['vite/client'],
+ types: ['vite/client', 'vitest'],
useDefineForClassFields: true,
noImplicitOverride: true,
noPropertyAccessFromIndexSignature: true,
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 c2208d8dac..1072a02cc3 100644
--- a/packages/vite/src/generators/configuration/__snapshots__/configuration.spec.ts.snap
+++ b/packages/vite/src/generators/configuration/__snapshots__/configuration.spec.ts.snap
@@ -2,7 +2,6 @@
exports[`@nrwl/vite:configuration library mode should add config for building library 1`] = `
"
-
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteTsConfigPaths from 'vite-tsconfig-paths';
@@ -49,7 +48,6 @@ import { join } from 'path';
exports[`@nrwl/vite:configuration library mode should set up non buildable library correctly 1`] = `
"
- ///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteTsConfigPaths from 'vite-tsconfig-paths';
@@ -295,7 +293,8 @@ exports[`@nrwl/vite:configuration transform React app to use Vite by providing c
\\"builder\\": \\"@nrwl/vite:dev-server\\",
\\"defaultConfiguration\\": \\"development\\",
\\"options\\": {
- \\"buildTarget\\": \\"my-test-mixed-react-app:build\\"
+ \\"buildTarget\\": \\"my-test-mixed-react-app:build\\",
+ \\"hmr\\": true
},
\\"configurations\\": {
\\"development\\": {
@@ -339,7 +338,6 @@ exports[`@nrwl/vite:configuration transform React app to use Vite by providing c
exports[`@nrwl/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 viteTsConfigPaths from 'vite-tsconfig-paths';
@@ -429,7 +427,8 @@ exports[`@nrwl/vite:configuration transform React app to use Vite should transfo
\\"builder\\": \\"@nrwl/vite:dev-server\\",
\\"defaultConfiguration\\": \\"development\\",
\\"options\\": {
- \\"buildTarget\\": \\"my-test-react-app:build\\"
+ \\"buildTarget\\": \\"my-test-react-app:build\\",
+ \\"hmr\\": true
},
\\"configurations\\": {
\\"development\\": {
@@ -473,7 +472,6 @@ exports[`@nrwl/vite:configuration transform React app to use Vite should transfo
exports[`@nrwl/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 viteTsConfigPaths from 'vite-tsconfig-paths';
@@ -597,7 +595,6 @@ exports[`@nrwl/vite:configuration transform Web app to use Vite should transform
exports[`@nrwl/vite:configuration vitest should create a vitest configuration if "includeVitest" is true 1`] = `
"
- ///
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteTsConfigPaths from 'vite-tsconfig-paths';
diff --git a/packages/vite/src/generators/configuration/configuration.ts b/packages/vite/src/generators/configuration/configuration.ts
index b46c359fbb..8c14566c85 100644
--- a/packages/vite/src/generators/configuration/configuration.ts
+++ b/packages/vite/src/generators/configuration/configuration.ts
@@ -16,6 +16,7 @@ import {
handleUnsupportedUserProvidedTargets,
handleUnknownExecutors,
UserProvidedTargetName,
+ TargetFlags,
} from '../../utils/generator-utils';
import initGenerator from '../init/init';
@@ -39,6 +40,7 @@ export async function viteConfigurationGenerator(tree: Tree, schema: Schema) {
* This is for when we are convering an existing project
* to use the vite executors.
* */
+ let projectAlreadyHasViteTargets: TargetFlags;
if (!schema.newProject) {
const userProvidedTargetName: UserProvidedTargetName = {
build: schema.buildTarget,
@@ -50,8 +52,9 @@ export async function viteConfigurationGenerator(tree: Tree, schema: Schema) {
validFoundTargetName,
projectContainsUnsupportedExecutor,
userProvidedTargetIsUnsupported,
+ alreadyHasNxViteTargets,
} = findExistingTargetsInProject(targets, userProvidedTargetName);
-
+ projectAlreadyHasViteTargets = alreadyHasNxViteTargets;
/**
* This means that we only found unsupported build targets in that project.
* The only way that buildTarget is defined, means that it is supported.
@@ -68,6 +71,20 @@ export async function viteConfigurationGenerator(tree: Tree, schema: Schema) {
);
}
+ if (
+ alreadyHasNxViteTargets.build &&
+ (alreadyHasNxViteTargets.serve ||
+ (!alreadyHasNxViteTargets.serve && projectType === 'library')) &&
+ alreadyHasNxViteTargets.test
+ ) {
+ throw new Error(
+ `The project ${schema.project} is aready configured to use the @nrwl/vite executors.
+ Please try a different project, or remove the existing targets
+ and re-run this generator to reset the existing Vite Configuration.
+ `
+ );
+ }
+
/**
* This means that we did not find any supported executors
* so we don't have any valid target names.
@@ -84,7 +101,7 @@ export async function viteConfigurationGenerator(tree: Tree, schema: Schema) {
!validFoundTargetName.serve &&
!validFoundTargetName.test
) {
- await handleUnknownExecutors();
+ await handleUnknownExecutors(schema.project);
}
/**
@@ -127,13 +144,15 @@ export async function viteConfigurationGenerator(tree: Tree, schema: Schema) {
});
tasks.push(initTask);
- addOrChangeBuildTarget(tree, schema, buildTargetName);
+ if (!projectAlreadyHasViteTargets?.build) {
+ addOrChangeBuildTarget(tree, schema, buildTargetName);
+ }
- if (!schema.includeLib) {
+ if (!schema.includeLib && !projectAlreadyHasViteTargets?.serve) {
addOrChangeServeTarget(tree, schema, serveTargetName);
}
- createOrEditViteConfig(tree, schema);
+ createOrEditViteConfig(tree, schema, false, projectAlreadyHasViteTargets);
if (schema.includeVitest) {
const vitestTask = await vitestGenerator(tree, {
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 30a9a740b4..8c214be827 100644
--- a/packages/vite/src/generators/vitest/__snapshots__/vitest.spec.ts.snap
+++ b/packages/vite/src/generators/vitest/__snapshots__/vitest.spec.ts.snap
@@ -2,7 +2,6 @@
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 viteTsConfigPaths from 'vite-tsconfig-paths';
@@ -37,7 +36,6 @@ exports[`vitest generator insourceTests should add the insourceSource option in
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 viteTsConfigPaths from 'vite-tsconfig-paths';
@@ -70,7 +68,6 @@ exports[`vitest generator vite.config should create correct vite.config.ts file
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 viteTsConfigPaths from 'vite-tsconfig-paths';
diff --git a/packages/vite/src/generators/vitest/vitest-generator.ts b/packages/vite/src/generators/vitest/vitest-generator.ts
index 6c6e90f545..1900936bee 100644
--- a/packages/vite/src/generators/vitest/vitest-generator.ts
+++ b/packages/vite/src/generators/vitest/vitest-generator.ts
@@ -96,6 +96,17 @@ function updateTsConfig(
path: './tsconfig.spec.json',
});
}
+
+ if (!json.compilerOptions?.types?.includes('vitest')) {
+ if (json.compilerOptions?.types) {
+ json.compilerOptions.types.push('vitest');
+ } else {
+ if (!json.compilerOptions) {
+ json.compilerOptions = {};
+ }
+ json.compilerOptions.types = ['vitest'];
+ }
+ }
return 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 5af4f8747e..d5711c5162 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,6 +1,54 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`generator utils ensureBuildOptionsInViteConfig should add build options if build options don't exist 1`] = `
+exports[`ensureBuildOptionsInViteConfig should add build and test options if defineConfig is empty 1`] = `
+"import dts from 'vite-plugin-dts';
+import { join } from 'path';
+
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
+
+ export default defineConfig({
+
+ // Configuration for building your library.
+ // See: https://vitejs.dev/guide/build.html#library-mode
+ build: {
+ lib: {
+ // Could also be a dictionary or array of multiple entry points.
+ entry: 'src/index.ts',
+ name: 'my-app',
+ fileName: 'index',
+ // Change this to the formats you want to support.
+ // Don't forgot to update your package.json as well.
+ formats: ['es', 'cjs']
+ },
+ rollupOptions: {
+ // External packages that should not be bundled into your library.
+ external: [\\"'react', 'react-dom', 'react/jsx-runtime'\\"]
+ }
+ },plugins: [
+ dts({
+ tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
+ // Faster builds by skipping tests. Set this to false to enable type checking.
+ skipDiagnostics: true,
+ }),
+ react(),
+ viteTsConfigPaths({
+ root: '../../',
+ }),
+ ],
+ test: {
+ globals: true,
+ cache: {
+ dir: '../node_modules/.vitest'
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ },});
+ "
+`;
+
+exports[`ensureBuildOptionsInViteConfig should add build option but not update test option if test already setup 1`] = `
"import dts from 'vite-plugin-dts';
import { join } from 'path';
import { defineConfig } from 'vite';
@@ -52,16 +100,15 @@ import { defineConfig } from 'vite';
"
`;
-exports[`generator utils ensureBuildOptionsInViteConfig should add build options if defineConfig is empty 1`] = `
+exports[`ensureBuildOptionsInViteConfig should add build options if build options don't exist 1`] = `
"import dts from 'vite-plugin-dts';
import { join } from 'path';
-
- import { defineConfig } from 'vite';
+import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteTsConfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
-
+
// Configuration for building your library.
// See: https://vitejs.dev/guide/build.html#library-mode
build: {
@@ -79,21 +126,33 @@ import { join } from 'path';
external: [\\"'react', 'react-dom', 'react/jsx-runtime'\\"]
}
},plugins: [
- dts({
+ ...[
+ react(),
+ viteTsConfigPaths({
+ root: '../../',
+ }),
+ ],
+ dts({
tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
// Faster builds by skipping tests. Set this to false to enable type checking.
skipDiagnostics: true,
}),
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
+ ],
+
+ test: {
+ globals: true,
+ cache: {
+ dir: '../../node_modules/.vitest',
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ },
+
});
"
`;
-exports[`generator utils ensureBuildOptionsInViteConfig should add build options if defineConfig is not used 1`] = `
+exports[`ensureBuildOptionsInViteConfig should add build options if defineConfig is not used 1`] = `
"import dts from 'vite-plugin-dts';
import { join } from 'path';
import { defineConfig } from 'vite';
@@ -117,6 +176,13 @@ import { defineConfig } from 'vite';
// External packages that should not be bundled into your library.
external: [\\"'react', 'react-dom', 'react/jsx-runtime'\\"]
}
+ },test: {
+ globals: true,
+ cache: {
+ dir: '../node_modules/.vitest'
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
},
plugins: [
...[
@@ -135,7 +201,7 @@ import { defineConfig } from 'vite';
"
`;
-exports[`generator utils ensureBuildOptionsInViteConfig should add build options if it is using conditional config 1`] = `
+exports[`ensureBuildOptionsInViteConfig should add build options if it is using conditional config - do nothing for test 1`] = `
"
import { defineConfig } from 'vite';
export default defineConfig(({ command, mode, ssrBuild }) => {
@@ -157,7 +223,7 @@ exports[`generator utils ensureBuildOptionsInViteConfig should add build options
"
`;
-exports[`generator utils ensureBuildOptionsInViteConfig should add new build options if some build options already exist 1`] = `
+exports[`ensureBuildOptionsInViteConfig should add new build options if some build options already exist 1`] = `
"import dts from 'vite-plugin-dts';
import { join } from 'path';
import { defineConfig } from 'vite';
@@ -199,4 +265,93 @@ import { defineConfig } from 'vite';
"
`;
-exports[`generator utils ensureBuildOptionsInViteConfig should not do anything if cannot understand syntax of vite config 1`] = `"console.log('Unknown syntax')"`;
+exports[`ensureBuildOptionsInViteConfig should not do anything if cannot understand syntax of vite config 1`] = `"console.log('Unknown syntax')"`;
+
+exports[`ensureBuildOptionsInViteConfig should not do anything if project has everything setup already 1`] = `
+"
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
+
+ export default defineConfig({
+ plugins: [
+ dts({
+ tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
+ // Faster builds by skipping tests. Set this to false to enable type checking.
+ skipDiagnostics: true,
+ }),
+ react(),
+ viteTsConfigPaths({
+ root: '../../../',
+ }),
+ ],
+
+ // Configuration for building your library.
+ // See: https://vitejs.dev/guide/build.html#library-mode
+ build: {
+ lib: {
+ // Could also be a dictionary or array of multiple entry points.
+ entry: 'src/index.ts',
+ name: 'pure-libs-react-vite',
+ fileName: 'index',
+ // Change this to the formats you want to support.
+ // Don't forgot to update your package.json as well.
+ formats: ['es', 'cjs'],
+ },
+ rollupOptions: {
+ // External packages that should not be bundled into your library.
+ external: ['react', 'react-dom', 'react/jsx-runtime'],
+ },
+ },
+
+ test: {
+ globals: true,
+ cache: {
+ dir: '../../../node_modules/.vitest',
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ },
+ });
+ "
+`;
+
+exports[`ensureBuildOptionsInViteConfig should update both test and build options - keep existing settings 1`] = `
+"import dts from 'vite-plugin-dts';
+import { join } from 'path';
+import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
+
+ export default defineConfig({
+ plugins: [
+ ...[
+ react(),
+ viteTsConfigPaths({
+ root: '../../',
+ }),
+ ],
+ dts({
+ tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
+ // Faster builds by skipping tests. Set this to false to enable type checking.
+ skipDiagnostics: true,
+ }),
+ ],
+
+ test: {
+ ...{
+ 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'\\"]}}
+ }
+
+ });
+ "
+`;
diff --git a/packages/vite/src/utils/generator-utils.ts b/packages/vite/src/utils/generator-utils.ts
index b24ea348ec..ebdffbd3be 100644
--- a/packages/vite/src/utils/generator-utils.ts
+++ b/packages/vite/src/utils/generator-utils.ts
@@ -15,7 +15,7 @@ import { VitestExecutorOptions } from '../executors/test/schema';
import { Schema } from '../generators/configuration/schema';
import { ensureBuildOptionsInViteConfig } from './vite-config-edit-utils';
-export interface UserProvidedTargetIsUnsupported {
+export interface TargetFlags {
build?: boolean;
serve?: boolean;
test?: boolean;
@@ -41,7 +41,8 @@ export function findExistingTargetsInProject(
): {
validFoundTargetName: ValidFoundTargetName;
projectContainsUnsupportedExecutor?: boolean;
- userProvidedTargetIsUnsupported?: UserProvidedTargetIsUnsupported;
+ userProvidedTargetIsUnsupported?: TargetFlags;
+ alreadyHasNxViteTargets?: TargetFlags;
} {
let validFoundBuildTarget: string | undefined,
validFoundServeTarget: string | undefined,
@@ -49,7 +50,10 @@ export function findExistingTargetsInProject(
projectContainsUnsupportedExecutor: boolean | undefined,
unsupportedUserProvidedTargetBuild: boolean | undefined,
unsupportedUserProvidedTargetServe: boolean | undefined,
- unsupportedUserProvidedTargetTest: boolean | undefined;
+ unsupportedUserProvidedTargetTest: boolean | undefined,
+ alreadyHasNxViteTargetBuild: boolean | undefined,
+ alreadyHasNxViteTargetServe: boolean | undefined,
+ alreadyHasNxViteTargetTest: boolean | undefined;
const arrayOfSupportedBuilders = [
'@nxext/vite:build',
@@ -82,6 +86,12 @@ export function findExistingTargetsInProject(
'@nrwl/js:tsc',
];
+ const arrayOfNxViteExecutors = [
+ '@nrwl/vite:build',
+ '@nrwl/vite:dev-server',
+ '@nrwl/vite:test',
+ ];
+
// First, we check if the user has provided a target
// If they have, we check if the executor the target is using is supported
// If it's not supported, then we set the unsupported flag to true for that target
@@ -134,6 +144,17 @@ export function findExistingTargetsInProject(
) {
break;
}
+
+ if (targets[target].executor === '@nrwl/vite:build') {
+ alreadyHasNxViteTargetBuild = true;
+ }
+ if (targets[target].executor === '@nrwl/vite:dev-server') {
+ alreadyHasNxViteTargetServe = true;
+ }
+ if (targets[target].executor === '@nrwl/vite:test') {
+ alreadyHasNxViteTargetTest = true;
+ }
+
if (
!validFoundBuildTarget &&
arrayOfSupportedBuilders.includes(targets[target].executor)
@@ -152,7 +173,10 @@ export function findExistingTargetsInProject(
) {
validFoundTestTarget = target;
}
- if (arrayofUnsupportedExecutors.includes(targets[target].executor)) {
+ if (
+ !arrayOfNxViteExecutors.includes(targets[target].executor) &&
+ arrayofUnsupportedExecutors.includes(targets[target].executor)
+ ) {
projectContainsUnsupportedExecutor = true;
}
}
@@ -169,6 +193,11 @@ export function findExistingTargetsInProject(
serve: unsupportedUserProvidedTargetServe,
test: unsupportedUserProvidedTargetTest,
},
+ alreadyHasNxViteTargets: {
+ build: alreadyHasNxViteTargetBuild,
+ serve: alreadyHasNxViteTargetServe,
+ test: alreadyHasNxViteTargetTest,
+ },
};
}
@@ -270,6 +299,9 @@ export function addOrChangeServeTarget(
}
project.targets[target].options = {
...serveOptions,
+ https: project.targets[target].options?.https,
+ hmr: project.targets[target].options?.hmr,
+ open: project.targets[target].options?.open,
};
project.targets[target].executor = '@nrwl/vite:dev-server';
} else {
@@ -308,6 +340,7 @@ export function editTsConfig(tree: Tree, options: Schema) {
config.compilerOptions = {
target: 'ESNext',
useDefineForClassFields: true,
+ module: 'ESNext',
lib: ['DOM', 'DOM.Iterable', 'ESNext'],
allowJs: false,
skipLibCheck: true,
@@ -315,13 +348,14 @@ export function editTsConfig(tree: Tree, options: Schema) {
allowSyntheticDefaultImports: true,
strict: true,
forceConsistentCasingInFileNames: true,
- module: 'ESNext',
moduleResolution: 'Node',
resolveJsonModule: true,
isolatedModules: true,
noEmit: true,
jsx: 'react-jsx',
- types: ['vite/client'],
+ types: options.includeVitest
+ ? ['vite/client', 'vitest']
+ : ['vite/client'],
};
config.include = [...config.include, 'src'];
break;
@@ -331,17 +365,19 @@ export function editTsConfig(tree: Tree, options: Schema) {
useDefineForClassFields: true,
module: 'ESNext',
lib: ['ESNext', 'DOM'],
- moduleResolution: 'Node',
+ skipLibCheck: true,
+ esModuleInterop: true,
strict: true,
+ moduleResolution: 'Node',
resolveJsonModule: true,
isolatedModules: true,
- esModuleInterop: true,
noEmit: true,
noUnusedLocals: true,
noUnusedParameters: true,
noImplicitReturns: true,
- skipLibCheck: true,
- types: ['vite/client'],
+ types: options.includeVitest
+ ? ['vite/client', 'vitest']
+ : ['vite/client'],
};
config.include = [...config.include, 'src'];
break;
@@ -418,7 +454,8 @@ export function moveAndEditIndexHtml(
export function createOrEditViteConfig(
tree: Tree,
options: Schema,
- onlyVitest?: boolean
+ onlyVitest?: boolean,
+ projectAlreadyHasViteTargets?: TargetFlags
) {
const projectConfig = readProjectConfiguration(tree, options.project);
@@ -484,9 +521,6 @@ export function createOrEditViteConfig(
}
},`
: '';
- const vitestTypes = options.includeVitest
- ? `/// `
- : '';
const defineOption = options.inSourceTests
? `define: {
@@ -529,13 +563,15 @@ export function createOrEditViteConfig(
buildOption,
dtsPlugin,
dtsImportLine,
- pluginOption
+ pluginOption,
+ testOption,
+ offsetFromRoot(projectConfig.root),
+ projectAlreadyHasViteTargets
);
return;
}
viteConfigContent = `
- ${vitestTypes}
import { defineConfig } from 'vite';
${reactPluginImportLine}
import viteTsConfigPaths from 'vite-tsconfig-paths';
@@ -588,7 +624,7 @@ export function getViteConfigPathForProject(
}
export async function handleUnsupportedUserProvidedTargets(
- userProvidedTargetIsUnsupported: UserProvidedTargetIsUnsupported,
+ userProvidedTargetIsUnsupported: TargetFlags,
userProvidedTargetName: UserProvidedTargetName,
validFoundTargetName: ValidFoundTargetName
) {
@@ -654,10 +690,10 @@ async function handleUnsupportedUserProvidedTargetsErrors(
}
}
-export async function handleUnknownExecutors() {
+export async function handleUnknownExecutors(projectName: string) {
logger.warn(
`
- We could not find any targets in your project that use executors which
+ We could not find any targets in project ${projectName} that use executors which
can be converted to the @nrwl/vite executors.
This either means that your project may not have a target
@@ -691,8 +727,15 @@ function handleViteConfigFileExists(
buildOption: string,
dtsPlugin: string,
dtsImportLine: string,
- pluginOption: string
+ pluginOption: string,
+ testOption: string,
+ offsetFromRoot: string,
+ projectAlreadyHasViteTargets?: TargetFlags
) {
+ if (projectAlreadyHasViteTargets.build && projectAlreadyHasViteTargets.test) {
+ return;
+ }
+
logger.info(`vite.config.ts already exists for project ${options.project}.`);
const buildOptionObject = {
lib: {
@@ -709,6 +752,16 @@ function handleViteConfigFileExists(
],
},
};
+
+ const testOptionObject = {
+ globals: true,
+ cache: {
+ dir: `${offsetFromRoot}node_modules/.vitest`,
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ };
+
const changed = ensureBuildOptionsInViteConfig(
tree,
viteConfigPath,
@@ -716,7 +769,10 @@ function handleViteConfigFileExists(
buildOptionObject,
dtsPlugin,
dtsImportLine,
- pluginOption
+ pluginOption,
+ testOption,
+ testOptionObject,
+ projectAlreadyHasViteTargets
);
if (!changed) {
@@ -729,7 +785,7 @@ function handleViteConfigFileExists(
);
} else {
logger.info(`
- Vite configuration file (${viteConfigPath}) has been updated with the required settings for build.
+ Vite configuration file (${viteConfigPath}) has been updated with the required settings for the new target(s).
`);
}
}
diff --git a/packages/vite/src/utils/test-files/test-vite-configs.ts b/packages/vite/src/utils/test-files/test-vite-configs.ts
new file mode 100644
index 0000000000..b900420038
--- /dev/null
+++ b/packages/vite/src/utils/test-files/test-vite-configs.ts
@@ -0,0 +1,254 @@
+export const noBuildOptions = `
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
+
+ export default defineConfig({
+ plugins: [
+ react(),
+ viteTsConfigPaths({
+ root: '../../',
+ }),
+ ],
+
+ test: {
+ globals: true,
+ cache: {
+ dir: '../../node_modules/.vitest',
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ },
+
+ });
+ `;
+
+export const someBuildOptions = `
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
+
+ export default defineConfig({
+ plugins: [
+ react(),
+ viteTsConfigPaths({
+ root: '../../',
+ }),
+ ],
+
+ test: {
+ globals: true,
+ cache: {
+ dir: '../../node_modules/.vitest',
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ },
+
+ build: {
+ my: 'option',
+ }
+
+ });
+ `;
+
+export const noContentDefineConfig = `
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
+
+ export default defineConfig({});
+ `;
+
+export const conditionalConfig = `
+ import { defineConfig } from 'vite';
+ export default defineConfig(({ command, mode, ssrBuild }) => {
+ if (command === 'serve') {
+ return {
+ port: 4200,
+ host: 'localhost',
+ }
+ } else {
+ // command === 'build'
+ return {
+ my: 'option',
+ }
+ }
+ })
+ `;
+
+export const configNoDefineConfig = `
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
+
+ export default {
+ plugins: [
+ react(),
+ viteTsConfigPaths({
+ root: '../../',
+ }),
+ ],
+ };
+ `;
+
+export const noBuildOptionsHasTestOption = `
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
+
+ export default defineConfig({
+ plugins: [
+ react(),
+ viteTsConfigPaths({
+ root: '../../',
+ }),
+ ],
+
+ test: {
+ globals: true,
+ cache: {
+ dir: '../../node_modules/.vitest',
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ },
+
+ });
+ `;
+
+export const someBuildOptionsSomeTestOption = `
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
+
+ export default defineConfig({
+ plugins: [
+ react(),
+ viteTsConfigPaths({
+ root: '../../',
+ }),
+ ],
+
+ test: {
+ my: 'option',
+ },
+
+ build: {
+ my: 'option',
+ }
+
+ });
+ `;
+
+export const hasEverything = `
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import viteTsConfigPaths from 'vite-tsconfig-paths';
+
+ export default defineConfig({
+ plugins: [
+ dts({
+ tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
+ // Faster builds by skipping tests. Set this to false to enable type checking.
+ skipDiagnostics: true,
+ }),
+ react(),
+ viteTsConfigPaths({
+ root: '../../../',
+ }),
+ ],
+
+ // Configuration for building your library.
+ // See: https://vitejs.dev/guide/build.html#library-mode
+ build: {
+ lib: {
+ // Could also be a dictionary or array of multiple entry points.
+ entry: 'src/index.ts',
+ name: 'pure-libs-react-vite',
+ fileName: 'index',
+ // Change this to the formats you want to support.
+ // Don't forgot to update your package.json as well.
+ formats: ['es', 'cjs'],
+ },
+ rollupOptions: {
+ // External packages that should not be bundled into your library.
+ external: ['react', 'react-dom', 'react/jsx-runtime'],
+ },
+ },
+
+ test: {
+ globals: true,
+ cache: {
+ dir: '../../../node_modules/.vitest',
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ },
+ });
+ `;
+
+export const buildOption = `
+ // Configuration for building your library.
+ // See: https://vitejs.dev/guide/build.html#library-mode
+ build: {
+ lib: {
+ // Could also be a dictionary or array of multiple entry points.
+ entry: 'src/index.ts',
+ name: 'my-app',
+ fileName: 'index',
+ // Change this to the formats you want to support.
+ // Don't forgot to update your package.json as well.
+ formats: ['es', 'cjs']
+ },
+ rollupOptions: {
+ // External packages that should not be bundled into your library.
+ external: ["'react', 'react-dom', 'react/jsx-runtime'"]
+ }
+ },`;
+export const buildOptionObject = {
+ lib: {
+ entry: 'src/index.ts',
+ name: 'my-app',
+ fileName: 'index',
+ formats: ['es', 'cjs'],
+ },
+ rollupOptions: {
+ external: ["'react', 'react-dom', 'react/jsx-runtime'"],
+ },
+};
+
+export const testOption = `test: {
+ globals: true,
+ cache: {
+ dir: '../node_modules/.vitest'
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ },`;
+
+export const testOptionObject = {
+ globals: true,
+ cache: {
+ dir: `../node_modules/.vitest`,
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+};
+
+export const dtsPlugin = `dts({
+ tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
+ // Faster builds by skipping tests. Set this to false to enable type checking.
+ skipDiagnostics: true,
+ }),`;
+export const dtsImportLine = `import dts from 'vite-plugin-dts';\nimport { join } from 'path';`;
+
+export const pluginOption = `
+ plugins: [
+ ${dtsPlugin}
+ react(),
+ viteTsConfigPaths({
+ root: '../../',
+ }),
+ ],
+ `;
diff --git a/packages/vite/src/utils/test-utils.ts b/packages/vite/src/utils/test-utils.ts
index ed26b92d83..9752bdd1bb 100644
--- a/packages/vite/src/utils/test-utils.ts
+++ b/packages/vite/src/utils/test-utils.ts
@@ -1,4 +1,4 @@
-import { parseJson, Tree, writeJson } from '@nrwl/devkit';
+import { Tree, writeJson } from '@nrwl/devkit';
import * as reactAppConfig from './test-files/react-project.config.json';
import * as reactViteConfig from './test-files/react-vite-project.config.json';
import * as webAppConfig from './test-files/web-project.config.json';
@@ -90,8 +90,7 @@ export function mockViteReactAppGenerator(tree: Tree): Tree {
tree.write(
`apps/${appName}/vite.config.ts`,
- `///
- import { defineConfig } from 'vite';
+ `import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
@@ -534,9 +533,7 @@ export function mockReactLibNonBuildableVitestRunnerGenerator(
tree.write(
`libs/${libName}/vite.config.ts`,
- `
- ///
- import { defineConfig } from 'vite';
+ `import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteTsConfigPaths from 'vite-tsconfig-paths';
diff --git a/packages/vite/src/utils/vite-config-edit-utils.spec.ts b/packages/vite/src/utils/vite-config-edit-utils.spec.ts
index c790283851..69aefaadc2 100644
--- a/packages/vite/src/utils/vite-config-edit-utils.spec.ts
+++ b/packages/vite/src/utils/vite-config-edit-utils.spec.ts
@@ -1,281 +1,221 @@
import { Tree } from '@nrwl/devkit';
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
import { tsquery } from '@phenomnomnominal/tsquery';
-
+import {
+ buildOption,
+ buildOptionObject,
+ conditionalConfig,
+ configNoDefineConfig,
+ dtsImportLine,
+ dtsPlugin,
+ hasEverything,
+ noBuildOptions,
+ noBuildOptionsHasTestOption,
+ noContentDefineConfig,
+ pluginOption,
+ someBuildOptions,
+ someBuildOptionsSomeTestOption,
+ testOption,
+ testOptionObject,
+} from './test-files/test-vite-configs';
import { ensureBuildOptionsInViteConfig } from './vite-config-edit-utils';
-describe('generator utils', () => {
+describe('ensureBuildOptionsInViteConfig', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyV1Workspace();
});
- describe('ensureBuildOptionsInViteConfig', () => {
- let tree: Tree;
+ it("should add build options if build options don't exist", () => {
+ tree.write('apps/my-app/vite.config.ts', noBuildOptions);
+ ensureBuildOptionsInViteConfig(
+ tree,
+ 'apps/my-app/vite.config.ts',
+ buildOption,
+ buildOptionObject,
+ dtsPlugin,
+ dtsImportLine,
+ pluginOption,
+ testOption,
+ testOptionObject,
+ { build: false, test: true, serve: false }
+ );
+ const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
+ const file = tsquery.ast(appFileContent);
+ const buildNode = tsquery.query(
+ file,
+ 'PropertyAssignment:has(Identifier[name="build"])'
+ );
+ expect(buildNode).toBeDefined();
+ expect(appFileContent).toMatchSnapshot();
+ });
- const buildOption = `
- // Configuration for building your library.
- // See: https://vitejs.dev/guide/build.html#library-mode
- build: {
- lib: {
- // Could also be a dictionary or array of multiple entry points.
- entry: 'src/index.ts',
- name: 'my-app',
- fileName: 'index',
- // Change this to the formats you want to support.
- // Don't forgot to update your package.json as well.
- formats: ['es', 'cjs']
- },
- rollupOptions: {
- // External packages that should not be bundled into your library.
- external: ["'react', 'react-dom', 'react/jsx-runtime'"]
- }
- },`;
- const buildOptionObject = {
- lib: {
- entry: 'src/index.ts',
- name: 'my-app',
- fileName: 'index',
- formats: ['es', 'cjs'],
- },
- rollupOptions: {
- external: ["'react', 'react-dom', 'react/jsx-runtime'"],
- },
- };
- const dtsPlugin = `dts({
- tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
- // Faster builds by skipping tests. Set this to false to enable type checking.
- skipDiagnostics: true,
- }),`;
- const dtsImportLine = `import dts from 'vite-plugin-dts';\nimport { join } from 'path';`;
+ it('should add new build options if some build options already exist', () => {
+ tree.write('apps/my-app/vite.config.ts', someBuildOptions);
+ ensureBuildOptionsInViteConfig(
+ tree,
+ 'apps/my-app/vite.config.ts',
+ buildOption,
+ buildOptionObject,
+ dtsPlugin,
+ dtsImportLine,
+ pluginOption,
+ testOption,
+ testOptionObject,
+ { build: false, test: true, serve: false }
+ );
+ const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
+ const file = tsquery.ast(appFileContent);
+ const buildNode = tsquery.query(
+ file,
+ 'PropertyAssignment:has(Identifier[name="build"])'
+ );
+ expect(buildNode).toBeDefined();
+ expect(appFileContent).toMatchSnapshot();
+ });
- const pluginOption = `
- plugins: [
- ${dtsPlugin}
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
- `;
+ it('should add build and test options if defineConfig is empty', () => {
+ tree.write('apps/my-app/vite.config.ts', noContentDefineConfig);
+ ensureBuildOptionsInViteConfig(
+ tree,
+ 'apps/my-app/vite.config.ts',
+ buildOption,
+ buildOptionObject,
+ dtsPlugin,
+ dtsImportLine,
+ pluginOption,
+ testOption,
+ testOptionObject,
+ { build: false, test: false, serve: false }
+ );
+ const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
+ const file = tsquery.ast(appFileContent);
+ const buildNode = tsquery.query(
+ file,
+ 'PropertyAssignment:has(Identifier[name="build"])'
+ );
+ expect(buildNode).toBeDefined();
+ expect(appFileContent).toMatchSnapshot();
+ });
- const noBuildOptions = `
- import { defineConfig } from 'vite';
- import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
+ it('should add build options if it is using conditional config - do nothing for test', () => {
+ tree.write('apps/my-app/vite.config.ts', conditionalConfig);
+ ensureBuildOptionsInViteConfig(
+ tree,
+ 'apps/my-app/vite.config.ts',
+ buildOption,
+ buildOptionObject,
+ dtsPlugin,
+ dtsImportLine,
+ pluginOption,
+ testOption,
+ testOptionObject,
+ { build: false, test: false, serve: false }
+ );
+ const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
+ const file = tsquery.ast(appFileContent);
+ const buildNode = tsquery.query(
+ file,
+ 'PropertyAssignment:has(Identifier[name="build"])'
+ );
+ expect(buildNode).toBeDefined();
+ expect(appFileContent).toMatchSnapshot();
+ });
- export default defineConfig({
- plugins: [
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
+ it('should add build options if defineConfig is not used', () => {
+ tree.write('apps/my-app/vite.config.ts', configNoDefineConfig);
+ ensureBuildOptionsInViteConfig(
+ tree,
+ 'apps/my-app/vite.config.ts',
+ buildOption,
+ buildOptionObject,
+ dtsPlugin,
+ dtsImportLine,
+ pluginOption,
+ testOption,
+ testOptionObject,
+ { build: false, test: false, serve: false }
+ );
+ const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
+ const file = tsquery.ast(appFileContent);
+ const buildNode = tsquery.query(
+ file,
+ 'PropertyAssignment:has(Identifier[name="build"])'
+ );
+ expect(buildNode).toBeDefined();
+ expect(appFileContent).toMatchSnapshot();
+ });
- test: {
- globals: true,
- cache: {
- dir: '../../node_modules/.vitest',
- },
- environment: 'jsdom',
- include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
- },
+ it('should not do anything if cannot understand syntax of vite config', () => {
+ tree.write('apps/my-app/vite.config.ts', `console.log('Unknown syntax')`);
+ ensureBuildOptionsInViteConfig(
+ tree,
+ 'apps/my-app/vite.config.ts',
+ buildOption,
+ buildOptionObject,
+ dtsPlugin,
+ dtsImportLine,
+ pluginOption,
+ testOption,
+ testOptionObject,
+ { build: false, test: false, serve: false }
+ );
+ const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
+ expect(appFileContent).toMatchSnapshot();
+ });
- });
- `;
+ it('should not do anything if project has everything setup already', () => {
+ tree.write('apps/my-app/vite.config.ts', hasEverything);
+ ensureBuildOptionsInViteConfig(
+ tree,
+ 'apps/my-app/vite.config.ts',
+ buildOption,
+ buildOptionObject,
+ dtsPlugin,
+ dtsImportLine,
+ pluginOption,
+ testOption,
+ testOptionObject,
+ { build: true, test: true, serve: true }
+ );
+ const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
+ expect(appFileContent).toMatchSnapshot();
+ });
- const someBuildOptions = `
- import { defineConfig } from 'vite';
- import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
+ it('should add build option but not update test option if test already setup', () => {
+ tree.write('apps/my-app/vite.config.ts', noBuildOptionsHasTestOption);
+ ensureBuildOptionsInViteConfig(
+ tree,
+ 'apps/my-app/vite.config.ts',
+ buildOption,
+ buildOptionObject,
+ dtsPlugin,
+ dtsImportLine,
+ pluginOption,
+ testOption,
+ testOptionObject,
+ { build: false, test: true, serve: true }
+ );
+ const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
+ expect(appFileContent).toMatchSnapshot();
+ });
- export default defineConfig({
- plugins: [
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
-
- test: {
- globals: true,
- cache: {
- dir: '../../node_modules/.vitest',
- },
- environment: 'jsdom',
- include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
- },
-
- build: {
- my: 'option',
- }
-
- });
- `;
-
- const noContentDefineConfig = `
- import { defineConfig } from 'vite';
- import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
-
- export default defineConfig({});
- `;
-
- const conditionalConfig = `
- import { defineConfig } from 'vite';
- export default defineConfig(({ command, mode, ssrBuild }) => {
- if (command === 'serve') {
- return {
- port: 4200,
- host: 'localhost',
- }
- } else {
- // command === 'build'
- return {
- my: 'option',
- }
- }
- })
- `;
-
- const configNoDefineConfig = `
- import { defineConfig } from 'vite';
- import react from '@vitejs/plugin-react';
- import viteTsConfigPaths from 'vite-tsconfig-paths';
-
- export default {
- plugins: [
- react(),
- viteTsConfigPaths({
- root: '../../',
- }),
- ],
- };
- `;
-
- beforeEach(() => {
- tree = createTreeWithEmptyV1Workspace();
- });
-
- it("should add build options if build options don't exist", () => {
- tree.write('apps/my-app/vite.config.ts', noBuildOptions);
- ensureBuildOptionsInViteConfig(
- tree,
- 'apps/my-app/vite.config.ts',
- buildOption,
- buildOptionObject,
- dtsPlugin,
- dtsImportLine,
- pluginOption
- );
- const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
- const file = tsquery.ast(appFileContent);
- const buildNode = tsquery.query(
- file,
- 'PropertyAssignment:has(Identifier[name="build"])'
- );
- expect(buildNode).toBeDefined();
- expect(appFileContent).toMatchSnapshot();
- });
-
- it('should add new build options if some build options already exist', () => {
- tree.write('apps/my-app/vite.config.ts', someBuildOptions);
- ensureBuildOptionsInViteConfig(
- tree,
- 'apps/my-app/vite.config.ts',
- buildOption,
- buildOptionObject,
- dtsPlugin,
- dtsImportLine,
- pluginOption
- );
- const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
- const file = tsquery.ast(appFileContent);
- const buildNode = tsquery.query(
- file,
- 'PropertyAssignment:has(Identifier[name="build"])'
- );
- expect(buildNode).toBeDefined();
- expect(appFileContent).toMatchSnapshot();
- });
-
- it('should add build options if defineConfig is empty', () => {
- tree.write('apps/my-app/vite.config.ts', noContentDefineConfig);
- ensureBuildOptionsInViteConfig(
- tree,
- 'apps/my-app/vite.config.ts',
- buildOption,
- buildOptionObject,
- dtsPlugin,
- dtsImportLine,
- pluginOption
- );
- const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
- const file = tsquery.ast(appFileContent);
- const buildNode = tsquery.query(
- file,
- 'PropertyAssignment:has(Identifier[name="build"])'
- );
- expect(buildNode).toBeDefined();
- expect(appFileContent).toMatchSnapshot();
- });
-
- it('should add build options if it is using conditional config', () => {
- tree.write('apps/my-app/vite.config.ts', conditionalConfig);
- ensureBuildOptionsInViteConfig(
- tree,
- 'apps/my-app/vite.config.ts',
- buildOption,
- buildOptionObject,
- dtsPlugin,
- dtsImportLine,
- pluginOption
- );
- const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
- const file = tsquery.ast(appFileContent);
- const buildNode = tsquery.query(
- file,
- 'PropertyAssignment:has(Identifier[name="build"])'
- );
- expect(buildNode).toBeDefined();
- expect(appFileContent).toMatchSnapshot();
- });
-
- it('should add build options if defineConfig is not used', () => {
- tree.write('apps/my-app/vite.config.ts', configNoDefineConfig);
- ensureBuildOptionsInViteConfig(
- tree,
- 'apps/my-app/vite.config.ts',
- buildOption,
- buildOptionObject,
- dtsPlugin,
- dtsImportLine,
- pluginOption
- );
- const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
- const file = tsquery.ast(appFileContent);
- const buildNode = tsquery.query(
- file,
- 'PropertyAssignment:has(Identifier[name="build"])'
- );
- expect(buildNode).toBeDefined();
- expect(appFileContent).toMatchSnapshot();
- });
-
- it('should not do anything if cannot understand syntax of vite config', () => {
- tree.write('apps/my-app/vite.config.ts', `console.log('Unknown syntax')`);
- ensureBuildOptionsInViteConfig(
- tree,
- 'apps/my-app/vite.config.ts',
- buildOption,
- buildOptionObject,
- dtsPlugin,
- dtsImportLine,
- pluginOption
- );
- const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
- expect(appFileContent).toMatchSnapshot();
- });
+ it('should update both test and build options - keep existing settings', () => {
+ tree.write('apps/my-app/vite.config.ts', someBuildOptionsSomeTestOption);
+ ensureBuildOptionsInViteConfig(
+ tree,
+ 'apps/my-app/vite.config.ts',
+ buildOption,
+ buildOptionObject,
+ dtsPlugin,
+ dtsImportLine,
+ pluginOption,
+ testOption,
+ testOptionObject,
+ { build: false, test: false, serve: true }
+ );
+ const appFileContent = tree.read('apps/my-app/vite.config.ts', 'utf-8');
+ expect(appFileContent).toMatchSnapshot();
});
});
diff --git a/packages/vite/src/utils/vite-config-edit-utils.ts b/packages/vite/src/utils/vite-config-edit-utils.ts
index c65a8f888d..de446fe71f 100644
--- a/packages/vite/src/utils/vite-config-edit-utils.ts
+++ b/packages/vite/src/utils/vite-config-edit-utils.ts
@@ -2,59 +2,77 @@ import { applyChangesToString, ChangeType, Tree } from '@nrwl/devkit';
import { findNodes } from 'nx/src/utils/typescript';
import ts = require('typescript');
import { tsquery } from '@phenomnomnominal/tsquery';
+import { TargetFlags } from './generator-utils';
export function ensureBuildOptionsInViteConfig(
tree: Tree,
path: string,
- buildConfigContent: string,
+ buildConfigString: string,
buildConfigObject: {},
dtsPlugin: string,
dtsImportLine: string,
- pluginOption: string
+ pluginOption: string,
+ testConfigString: string,
+ testConfigObject: {},
+ projectAlreadyHasViteTargets?: TargetFlags
): boolean {
const fileContent = tree.read(path, 'utf-8');
- const file = tsquery.ast(fileContent);
- const newContent = handlePluginNode(
- file,
- fileContent,
- dtsPlugin,
- dtsImportLine,
- pluginOption
- );
- const buildUpdatedContent = handleBuildNode(
- newContent ?? fileContent,
- buildConfigContent,
- buildConfigObject
- );
+ let updatedContent = undefined;
- if (buildUpdatedContent) {
- tree.write(path, buildUpdatedContent);
+ if (!projectAlreadyHasViteTargets?.test && testConfigString?.length) {
+ updatedContent = handleBuildOrTestNode(
+ fileContent,
+ testConfigString,
+ testConfigObject,
+ 'test'
+ );
+ }
+
+ if (!projectAlreadyHasViteTargets?.build && buildConfigString?.length) {
+ updatedContent = handlePluginNode(
+ updatedContent ?? fileContent,
+ dtsPlugin,
+ dtsImportLine,
+ pluginOption
+ );
+
+ updatedContent = handleBuildOrTestNode(
+ updatedContent ?? fileContent,
+ buildConfigString,
+ buildConfigObject,
+ 'build'
+ );
+ }
+
+ if (updatedContent) {
+ tree.write(path, updatedContent);
return true;
} else {
return false;
}
}
-function handleBuildNode(
+function handleBuildOrTestNode(
updatedFileContent: string,
- buildConfigContent: string,
- buildConfigObject: {}
+ configContentString: string,
+ configContentObject: {},
+ name: 'build' | 'test'
): string | undefined {
const buildNode = tsquery.query(
updatedFileContent,
- 'PropertyAssignment:has(Identifier[name="build"])'
+ `PropertyAssignment:has(Identifier[name="${name}"])`
);
if (buildNode.length) {
return tsquery.replace(
updatedFileContent,
- 'PropertyAssignment:has(Identifier[name="build"])',
+ `PropertyAssignment:has(Identifier[name="${name}"])`,
(node: ts.Node) => {
const found = tsquery.query(node, 'ObjectLiteralExpression');
- return `build: {
+ return `${name}: {
...${found?.[0].getText()},
- ...${JSON.stringify(buildConfigObject)}
+ ...${JSON.stringify(configContentObject)}
}`;
}
);
@@ -71,11 +89,16 @@ function handleBuildNode(
);
if (conditionalConfig.length) {
- return transformConditionalConfig(
- conditionalConfig,
- updatedFileContent,
- buildConfigContent
- );
+ if (name === 'build') {
+ return transformConditionalConfig(
+ conditionalConfig,
+ updatedFileContent,
+ configContentString
+ );
+ } else {
+ // no test config in conditional config
+ return undefined;
+ }
} else {
const propertyAssignments = tsquery.query(
foundDefineConfig[0],
@@ -87,7 +110,7 @@ function handleBuildNode(
{
type: ChangeType.Insert,
index: propertyAssignments[0].getStart(),
- text: buildConfigContent,
+ text: configContentString,
},
]);
} else {
@@ -95,7 +118,7 @@ function handleBuildNode(
{
type: ChangeType.Insert,
index: foundDefineConfig[0].getStart() + 14,
- text: buildConfigContent,
+ text: configContentString,
},
]);
}
@@ -117,7 +140,7 @@ function handleBuildNode(
{
type: ChangeType.Insert,
index: startOfObject + 1,
- text: buildConfigContent,
+ text: configContentString,
},
]);
} catch {
@@ -131,7 +154,6 @@ function transformCurrentBuildObject(
index: number,
returnStatements: ts.ReturnStatement[],
appFileContent: string,
-
buildConfigObject: {}
): string | undefined {
if (!returnStatements?.[index]) {
@@ -244,12 +266,12 @@ function transformConditionalConfig(
}
function handlePluginNode(
- file: ts.SourceFile,
appFileContent: string,
dtsPlugin: string,
dtsImportLine: string,
pluginOption: string
): string | undefined {
+ const file = tsquery.ast(appFileContent);
const pluginsNode = tsquery.query(
file,
'PropertyAssignment:has(Identifier[name="plugins"])'
@@ -341,15 +363,7 @@ function handlePluginNode(
if (writeFile) {
if (!appFileContent.includes(`import dts from 'vite-plugin-dts'`)) {
- if (appFileContent.includes('/// ')) {
- return appFileContent.replace(
- '/// ',
- `///
- ${dtsImportLine}\n`
- );
- } else {
- return dtsImportLine + '\n' + appFileContent;
- }
+ return dtsImportLine + '\n' + appFileContent;
}
return appFileContent;
}
diff --git a/packages/web/src/generators/application/application.spec.ts b/packages/web/src/generators/application/application.spec.ts
index 00fab792bb..9589fd2988 100644
--- a/packages/web/src/generators/application/application.spec.ts
+++ b/packages/web/src/generators/application/application.spec.ts
@@ -160,7 +160,10 @@ describe('app', () => {
path: './tsconfig.spec.json',
},
]);
- expect(tsconfig.compilerOptions.types).toMatchObject(['vite/client']);
+ expect(tsconfig.compilerOptions.types).toMatchObject([
+ 'vite/client',
+ 'vitest',
+ ]);
expect(tree.exists('apps/my-app-e2e/cypress.config.ts')).toBeTruthy();
expect(tree.exists('apps/my-app/index.html')).toBeTruthy();
@@ -563,7 +566,10 @@ describe('app', () => {
it('should create correct tsconfig compilerOptions', () => {
const tsconfigJson = readJson(viteAppTree, '/apps/my-app/tsconfig.json');
- expect(tsconfigJson.compilerOptions.types).toMatchObject(['vite/client']);
+ expect(tsconfigJson.compilerOptions.types).toMatchObject([
+ 'vite/client',
+ 'vitest',
+ ]);
});
it('should create index.html and vite.config file at the root of the app', () => {