From fe17fc3287be64bc764dbe3281bc37bda4562fd6 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Tue, 20 Feb 2024 16:38:12 +0200 Subject: [PATCH] fix(vite): project conversion generator (#21646) --- .../vite/generators/configuration.json | 14 +- .../application/application.spec.ts | 5 +- packages/vite/docs/configuration-examples.md | 47 +-- .../__snapshots__/configuration.spec.ts.snap | 282 ------------------ .../configuration/configuration.spec.ts | 141 +-------- .../generators/configuration/configuration.ts | 128 +------- .../configuration/lib/convert-non-vite.ts | 100 +++++++ .../src/generators/configuration/schema.d.ts | 3 - .../src/generators/configuration/schema.json | 12 - .../src/generators/vitest/vitest-generator.ts | 6 +- .../change-ts-paths-plugin.ts | 11 +- .../update-17-2-0/update-vite-config.ts | 11 +- .../lib => utils}/find-vite-config.ts | 10 + .../vite/src/utils/generator-utils.spec.ts | 98 +----- packages/vite/src/utils/generator-utils.ts | 268 ++++------------- .../test-files/react-project.config.json | 82 ----- .../utils/test-files/web-project.config.json | 69 ----- packages/vite/src/utils/test-utils.ts | 38 +-- .../application/application.spec.ts | 4 +- 19 files changed, 225 insertions(+), 1104 deletions(-) create mode 100644 packages/vite/src/generators/configuration/lib/convert-non-vite.ts rename packages/vite/src/{migrations/update-17-2-0/lib => utils}/find-vite-config.ts (54%) delete mode 100644 packages/vite/src/utils/test-files/react-project.config.json delete mode 100644 packages/vite/src/utils/test-files/web-project.config.json diff --git a/docs/generated/packages/vite/generators/configuration.json b/docs/generated/packages/vite/generators/configuration.json index 6c5d46e83c53d..52caa18226535 100644 --- a/docs/generated/packages/vite/generators/configuration.json +++ b/docs/generated/packages/vite/generators/configuration.json @@ -44,18 +44,6 @@ "default": false, "hidden": true }, - "buildTarget": { - "type": "string", - "description": "The build target of the project to be transformed to use the @nx/vite:build executor." - }, - "serveTarget": { - "type": "string", - "description": "The serve target of the project to be transformed to use the @nx/vite:dev-server and @nx/vite:preview-server executors." - }, - "testTarget": { - "type": "string", - "description": "The test target of the project to be transformed to use the @nx/vite:test executor." - }, "skipFormat": { "description": "Skip formatting files.", "type": "boolean", @@ -69,7 +57,7 @@ "default": "jsdom" } }, - "examplesFile": "---\ntitle: Examples for the Vite configuration generator\ndescription: This page contains examples for the Vite @nx/vite:configuration generator, which helps you set up Vite on your Nx workspace, or convert an existing project to use Vite.\n---\n\nThis generator is used for converting an existing React or Web project to use [Vite.js](https://vitejs.dev/) and the [@nx/vite executors](/packages/vite#executors).\n\nIt will change the `build` and `serve` targets to use the `@nx/vite` executors for serving and building the application. If you choose so, it will also change your `test` target to use the `@nx/vite:test` executor. It will create a `vite.config.ts` file at the root of your project with the correct settings, or if there's already a `vite.config.ts` file, it will modify it to include the correct settings.\n\n{% callout type=\"caution\" title=\"Your code will be modified!\" %}\nThis generator will modify your code, so make sure to commit your changes before running it.\n{% /callout %}\n\n```bash\nnx g @nx/vite:configuration\n```\n\nWhen running this generator, you will be prompted to provide the following:\n\n- The `project`, as the name of the project you want to generate the configuration for.\n- The `uiFramework` you want to use. Supported values are: `react` and `none`.\n\nYou must provide a `project` and a `uiFramework` for the generator to work.\n\nYou may also pass the `includeVitest` flag. This will also change your `test` target to use the `@nx/vite:test` executor, and configure your project for testing with [Vitest](https://vitest.dev/), by adding the `test` configuration in your `vite.config.ts` file.\n\n## Converting custom (specific) targets\n\nBy default, the `@nx/vite:configuration` generator will search your project's configuration to find the targets for serving, building, and testing your project, and it will attempt to convert these targets to use the `@nx/vite` executors.\n\nYour targets for building, serving and testing may not be named `build`, `serve` and `test`. Nx will try to infer the correct targets to convert, and it will attempt to convert the first one it finds for each of these actions if you have more than one. If you have more than one target for serving, building, or testing your project, you can pass the `--serveTarget`, `--buildTarget`, and `--testTarget` flags to the generator, to tell Nx specifically which targets to convert.\n\nNx will determine if the targets you provided (or the ones it inferred) are valid and can be converted to use the `@nx/vite` executors. If the targets are not valid, the generator will fail. If no targets are found - or recognized to be either supported or unsupported - Nx will ask you whether you want to convert your project anyway. If you choose to do so, Nx will configure your project to use Vite.js, creating new targets for you, and creating or modifying your `vite.config.ts` file. You can then test on your own if the result works or not, and modify the configuration as needed. It's suggested that if Nx does not recognize your targets automatically, you commit your changes before running the generator, so you can revert the changes if needed.\n\n## Projects that can be converted to use the `@nx/vite` executors\n\nUsually, React and Web projects generated with the `@nx/react` and the `@nx/web` generators can be converted to use the `@nx/vite` executors without any issues.\n\nThe list of executors for building, testing and serving that can be converted to use the `@nx/vite` executors is:\n\n### Supported `build` executors\n\n- `@nxext/vite:build`\n- `@nx/js:babel`\n- `@nx/js:swc`\n- `@nx/webpack:webpack`\n- `@nx/rollup:rollup`\n- `@nx/web:rollup`\n\n### Supported `serve` executors\n\n- `@nxext/vite:dev`\n- `@nx/webpack:dev-server`\n\n### Supported `test` executors\n\n- `@nx/jest:jest`\n- `@nxext/vitest:vitest`\n\n### Unsupported executors\n\n- `@nx/angular:ng-packagr-lite`\n- `@nx/angular:package`\n- `@nx/angular:webpack-browser`\n- `@angular-devkit/build-angular:browser`\n- `@angular-devkit/build-angular:dev-server`\n- `@nx/esbuild:esbuild`\n- `@nx/react-native:start`\n- `@nx/next:build`\n- `@nx/next:server`\n- `@nx/js:tsc`\n- any executor _not_ listed in the lists of \"supported executors\"\n- any project that does _not_ have a target for building, serving or testing\n\nWe **cannot** guarantee that projects using unsupported executors - _or any executor that is NOT listed in the list of \"supported executors\"_ - for either building, testing or serving will work correctly when converted to use the `@nx/vite` executors.\n\nIf you have a project that does _not_ use one of the supported executors you can try to [configure it to use the `@nx/vite` executors manually](/recipes/vite/configure-vite), but it may not work properly.\n\nYou can read more in the [Vite package overview page](/packages/vite).\n\n## Examples\n\n### Change a React app to use Vite\n\n```bash\nnx g @nx/vite:configuration --project=my-react-app --uiFramework=react --includeVitest\n```\n\nThis will change the `my-react-app` project to use the `@nx/vite` executors for building, serving and testing the application.\n\n### Change a Web app to use Vite\n\n```bash\nnx g @nx/vite:configuration --project=my-web-app --uiFramework=none --includeVitest\n```\n\nThis will change the `my-web-app` project to use the `@nx/vite` executors for building, serving and testing the application.\n\n### Change only my custom provided targets to use Vite\n\n```bash\nnx g @nx/vite:configuration --project=my-react-app --uiFramework=react --includeVitest --buildTarget=my-build --serveTarget=my-serve --testTarget=my-test\n```\n\nThis will change the `my-build`, `my-serve` and `my-test` targets to use the `@nx/vite` executors for building, serving and testing the application, even if you have other targets for these actions as well.\n", + "examplesFile": "---\ntitle: Examples for the Vite configuration generator\ndescription: This page contains examples for the Vite @nx/vite:configuration generator, which helps you set up Vite on your Nx workspace, or convert an existing project to use Vite.\n---\n\nThis generator is used for converting an existing React or Web project to use [Vite.js](https://vitejs.dev/).\n\nIt will create a `vite.config.ts` file at the root of your project with the correct settings, or if there's already a `vite.config.ts` file, it will modify it to include the correct settings.\n\n{% callout type=\"caution\" title=\"Your code will be modified!\" %}\nThis generator will modify your code, so make sure to commit your changes before running it.\n{% /callout %}\n\n```bash\nnx g @nx/vite:configuration\n```\n\nWhen running this generator, you will be prompted to provide the following:\n\n- The `project`, as the name of the project you want to generate the configuration for.\n- The `uiFramework` you want to use. Supported values are: `react` and `none`.\n\nYou must provide a `project` and a `uiFramework` for the generator to work.\n\nYou may also pass the `includeVitest` flag. This will also configure your project for testing with [Vitest](https://vitest.dev/), by adding the `test` configuration in your `vite.config.ts` file.\n\n## How to use\n\nIf you have an existing project that does not use Vite, you may want to convert it to use Vite. This can be a `webpack` project, a buildable JS library that uses the `@nx/js:babel`, the `@nx/js:swc` or the `@nx/rollup:rollup` executor, or even a non-buildable library.\nBy default, the `@nx/vite:configuration` generator will search your project to find the relevant configuration (either a `webpack.config.ts` file for example, or the `@nx/js` executors). If it determines that your project can be converted, then Nx will generate the configuration for you. If it cannot determine that your project can be converted, it will ask you if you want to convert it anyway or throw an error if it determines that it cannot be converted.\n\nYou can then test on your own if the result works or not, and modify the configuration as needed. It's suggested that you commit your changes before running the generator, so you can revert the changes if needed.\n\n## Projects that can be converted to use the `@nx/vite` executors\n\nUsually, React and Web projects generated with the `@nx/react` and the `@nx/web` generators can be converted to use the `@nx/vite` executors without any issues.\n\nThe list of executors for building, testing and serving that can be converted to use the `@nx/vite` executors is:\n\n### Supported `build` executors\n\n- `@nxext/vite:build`\n- `@nx/js:babel`\n- `@nx/js:swc`\n- `@nx/rollup:rollup`\n- `@nx/webpack:webpack`\n- `@nx/web:rollup`\n\n### Unsupported executors\n\n- `@nx/angular:ng-packagr-lite`\n- `@nx/angular:package`\n- `@nx/angular:webpack-browser`\n- `@angular-devkit/build-angular:browser`\n- `@angular-devkit/build-angular:dev-server`\n- `@nx/esbuild:esbuild`\n- `@nx/react-native:start`\n- `@nx/next:build`\n- `@nx/next:server`\n- `@nx/js:tsc`\n- any executor _not_ listed in the lists of \"supported executors\"\n- any project that does _not_ have a target for building, serving or testing\n\nWe **cannot** guarantee that projects using unsupported executors - _or any executor that is NOT listed in the list of \"supported executors\"_ - for either building, testing or serving will work correctly when converted to use Vite.\n\nYou can read more in the [Vite package overview page](/packages/vite).\n\n## Examples\n\n### Convert a React app to use Vite\n\n```bash\nnx g @nx/vite:configuration --project=my-react-app --uiFramework=react --includeVitest\n```\n\nThis will configure the `my-react-app` project to use Vite.\n\n### Convert a Web app to use Vite\n\n```bash\nnx g @nx/vite:configuration --project=my-web-app --uiFramework=none --includeVitest\n```\n\nThis will configure the `my-web-app` project to use Vite.\n", "presets": [] }, "description": "Add Vite configuration to an application.", diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts index 98c06f2b9205a..fcae904d1b303 100644 --- a/packages/react/src/generators/application/application.spec.ts +++ b/packages/react/src/generators/application/application.spec.ts @@ -925,10 +925,7 @@ describe('app', () => { it('should create correct tsconfig compilerOptions', () => { const tsconfigJson = readJson(viteAppTree, '/my-app/tsconfig.json'); - expect(tsconfigJson.compilerOptions.types).toMatchObject([ - 'vite/client', - 'vitest', - ]); + expect(tsconfigJson.compilerOptions.jsx).toBe('react-jsx'); }); it('should create index.html and vite.config file at the root of the app', () => { diff --git a/packages/vite/docs/configuration-examples.md b/packages/vite/docs/configuration-examples.md index 6231cc1695af0..1154e2779735b 100644 --- a/packages/vite/docs/configuration-examples.md +++ b/packages/vite/docs/configuration-examples.md @@ -3,9 +3,9 @@ title: Examples for the Vite configuration generator description: This page contains examples for the Vite @nx/vite:configuration generator, which helps you set up Vite on your Nx workspace, or convert an existing project to use Vite. --- -This generator is used for converting an existing React or Web project to use [Vite.js](https://vitejs.dev/) and the [@nx/vite executors](/packages/vite#executors). +This generator is used for converting an existing React or Web project to use [Vite.js](https://vitejs.dev/). -It will change the `build` and `serve` targets to use the `@nx/vite` executors for serving and building the application. If you choose so, it will also change your `test` target to use the `@nx/vite:test` executor. It will create a `vite.config.ts` file at the root of your project with the correct settings, or if there's already a `vite.config.ts` file, it will modify it to include the correct settings. +It will create a `vite.config.ts` file at the root of your project with the correct settings, or if there's already a `vite.config.ts` file, it will modify it to include the correct settings. {% callout type="caution" title="Your code will be modified!" %} This generator will modify your code, so make sure to commit your changes before running it. @@ -22,15 +22,14 @@ When running this generator, you will be prompted to provide the following: You must provide a `project` and a `uiFramework` for the generator to work. -You may also pass the `includeVitest` flag. This will also change your `test` target to use the `@nx/vite:test` executor, and configure your project for testing with [Vitest](https://vitest.dev/), by adding the `test` configuration in your `vite.config.ts` file. +You may also pass the `includeVitest` flag. This will also configure your project for testing with [Vitest](https://vitest.dev/), by adding the `test` configuration in your `vite.config.ts` file. -## Converting custom (specific) targets +## How to use -By default, the `@nx/vite:configuration` generator will search your project's configuration to find the targets for serving, building, and testing your project, and it will attempt to convert these targets to use the `@nx/vite` executors. +If you have an existing project that does not use Vite, you may want to convert it to use Vite. This can be a `webpack` project, a buildable JS library that uses the `@nx/js:babel`, the `@nx/js:swc` or the `@nx/rollup:rollup` executor, or even a non-buildable library. +By default, the `@nx/vite:configuration` generator will search your project to find the relevant configuration (either a `webpack.config.ts` file for example, or the `@nx/js` executors). If it determines that your project can be converted, then Nx will generate the configuration for you. If it cannot determine that your project can be converted, it will ask you if you want to convert it anyway or throw an error if it determines that it cannot be converted. -Your targets for building, serving and testing may not be named `build`, `serve` and `test`. Nx will try to infer the correct targets to convert, and it will attempt to convert the first one it finds for each of these actions if you have more than one. If you have more than one target for serving, building, or testing your project, you can pass the `--serveTarget`, `--buildTarget`, and `--testTarget` flags to the generator, to tell Nx specifically which targets to convert. - -Nx will determine if the targets you provided (or the ones it inferred) are valid and can be converted to use the `@nx/vite` executors. If the targets are not valid, the generator will fail. If no targets are found - or recognized to be either supported or unsupported - Nx will ask you whether you want to convert your project anyway. If you choose to do so, Nx will configure your project to use Vite.js, creating new targets for you, and creating or modifying your `vite.config.ts` file. You can then test on your own if the result works or not, and modify the configuration as needed. It's suggested that if Nx does not recognize your targets automatically, you commit your changes before running the generator, so you can revert the changes if needed. +You can then test on your own if the result works or not, and modify the configuration as needed. It's suggested that you commit your changes before running the generator, so you can revert the changes if needed. ## Projects that can be converted to use the `@nx/vite` executors @@ -43,20 +42,10 @@ The list of executors for building, testing and serving that can be converted to - `@nxext/vite:build` - `@nx/js:babel` - `@nx/js:swc` -- `@nx/webpack:webpack` - `@nx/rollup:rollup` +- `@nx/webpack:webpack` - `@nx/web:rollup` -### Supported `serve` executors - -- `@nxext/vite:dev` -- `@nx/webpack:dev-server` - -### Supported `test` executors - -- `@nx/jest:jest` -- `@nxext/vitest:vitest` - ### Unsupported executors - `@nx/angular:ng-packagr-lite` @@ -72,34 +61,24 @@ The list of executors for building, testing and serving that can be converted to - any executor _not_ listed in the lists of "supported executors" - any project that does _not_ have a target for building, serving or testing -We **cannot** guarantee that projects using unsupported executors - _or any executor that is NOT listed in the list of "supported executors"_ - for either building, testing or serving will work correctly when converted to use the `@nx/vite` executors. - -If you have a project that does _not_ use one of the supported executors you can try to [configure it to use the `@nx/vite` executors manually](/recipes/vite/configure-vite), but it may not work properly. +We **cannot** guarantee that projects using unsupported executors - _or any executor that is NOT listed in the list of "supported executors"_ - for either building, testing or serving will work correctly when converted to use Vite. You can read more in the [Vite package overview page](/packages/vite). ## Examples -### Change a React app to use Vite +### Convert a React app to use Vite ```bash nx g @nx/vite:configuration --project=my-react-app --uiFramework=react --includeVitest ``` -This will change the `my-react-app` project to use the `@nx/vite` executors for building, serving and testing the application. +This will configure the `my-react-app` project to use Vite. -### Change a Web app to use Vite +### Convert a Web app to use Vite ```bash nx g @nx/vite:configuration --project=my-web-app --uiFramework=none --includeVitest ``` -This will change the `my-web-app` project to use the `@nx/vite` executors for building, serving and testing the application. - -### Change only my custom provided targets to use Vite - -```bash -nx g @nx/vite:configuration --project=my-react-app --uiFramework=react --includeVitest --buildTarget=my-build --serveTarget=my-serve --testTarget=my-test -``` - -This will change the `my-build`, `my-serve` and `my-test` targets to use the `@nx/vite` executors for building, serving and testing the application, even if you have other targets for these actions as well. +This will configure the `my-web-app` project to use Vite. 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 7d811246a8554..1eec26f8151e7 100644 --- a/packages/vite/src/generators/configuration/__snapshots__/configuration.spec.ts.snap +++ b/packages/vite/src/generators/configuration/__snapshots__/configuration.spec.ts.snap @@ -121,36 +121,6 @@ export default defineConfig({ " `; -exports[`@nx/vite:configuration library mode should set up non buildable library correctly 2`] = ` -"{ - "projects": { - "react-lib-nonb-jest": { - "name": "react-lib-nonb-jest", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "root": "libs/react-lib-nonb-jest", - "sourceRoot": "libs/react-lib-nonb-jest/src", - "projectType": "library", - "targets": { - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "libs/react-lib-nonb-jest/jest.config.ts", - "passWithNoTests": true - } - } - }, - "tags": [] - } - } -} -" -`; - exports[`@nx/vite:configuration library mode should set up non buildable library which already has vite.config.ts correctly 1`] = ` "import dts from 'vite-plugin-dts'; import * as path from 'path'; @@ -207,91 +177,6 @@ export default defineConfig({ " `; -exports[`@nx/vite:configuration library mode should set up non buildable library which already has vite.config.ts correctly 2`] = ` -"{ - "projects": { - "react-lib-nonb-vitest": { - "name": "react-lib-nonb-vitest", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "root": "libs/react-lib-nonb-vitest", - "sourceRoot": "libs/react-lib-nonb-vitest/src", - "projectType": "library", - "targets": { - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, - "test": { - "executor": "@nx/vite:test", - "outputs": ["{projectRoot}/coverage"], - "options": { - "passWithNoTests": true, - "reportsDirectory": "{workspaceRoot}/coverage/{projectRoot}" - } - } - }, - "tags": [] - } - } -} -" -`; - -exports[`@nx/vite:configuration transform React app to use Vite by providing custom targets transform React app if supported executor is provided should transform workspace.json project config 1`] = ` -"{ - "projects": { - "my-test-mixed-react-app": { - "name": "my-test-mixed-react-app", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "root": "apps/my-test-mixed-react-app", - "sourceRoot": "apps/my-test-mixed-react-app/src", - "projectType": "application", - "targets": { - "invalid-build": { - "executor": "@nx/js:tsc", - "outputs": ["{options.outputPath}"] - }, - "valid-build": { - "executor": "@nx/webpack:webpack", - "outputs": ["{options.outputPath}"] - }, - "serve": { - "executor": "@nx/webpack:dev-server", - "defaultConfiguration": "development", - "options": { - "buildTarget": "my-test-mixed-react-app:build", - "hmr": true - }, - "configurations": { - "development": { - "buildTarget": "my-test-mixed-react-app:build:development" - }, - "production": { - "buildTarget": "my-test-mixed-react-app:build:production", - "hmr": false - } - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/my-test-mixed-react-app/jest.config.ts", - "passWithNoTests": true - } - } - }, - "tags": [] - } - } -} -" -`; - 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'; @@ -349,96 +234,6 @@ exports[`@nx/vite:configuration transform React app to use Vite should move inde " `; -exports[`@nx/vite:configuration transform React app to use Vite should transform workspace.json project config 1`] = ` -"{ - "projects": { - "my-test-react-app": { - "name": "my-test-react-app", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "root": "apps/my-test-react-app", - "sourceRoot": "apps/my-test-react-app/src", - "projectType": "application", - "targets": { - "build": { - "executor": "@nx/webpack:webpack", - "outputs": ["{options.outputPath}"], - "defaultConfiguration": "production", - "options": { - "compiler": "babel", - "outputPath": "dist/apps/my-test-react-app", - "index": "apps/my-test-react-app/src/index.html", - "baseHref": "/", - "main": "apps/my-test-react-app/src/main.tsx", - "polyfills": "apps/my-test-react-app/src/polyfills.ts", - "tsConfig": "apps/my-test-react-app/tsconfig.app.json", - "assets": [ - "apps/my-test-react-app/src/favicon.ico", - "apps/my-test-react-app/src/assets" - ], - "styles": ["apps/my-test-react-app/src/styles.css"], - "scripts": [], - "webpackConfig": "@nx/react/plugins/webpack" - }, - "configurations": { - "development": { - "extractLicenses": false, - "optimization": false, - "sourceMap": true, - "vendorChunk": true - }, - "production": { - "fileReplacements": [ - { - "replace": "apps/my-test-react-app/src/environments/environment.ts", - "with": "apps/my-test-react-app/src/environments/environment.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false - } - } - }, - "serve": { - "executor": "@nx/webpack:dev-server", - "defaultConfiguration": "development", - "options": { - "buildTarget": "my-test-react-app:build", - "hmr": true - }, - "configurations": { - "development": { - "buildTarget": "my-test-react-app:build:development" - }, - "production": { - "buildTarget": "my-test-react-app:build:production", - "hmr": false - } - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/my-test-react-app/jest.config.ts", - "passWithNoTests": true - } - } - }, - "tags": [] - } - } -} -" -`; - 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'; @@ -496,83 +291,6 @@ exports[`@nx/vite:configuration transform Web app to use Vite should move index. " `; -exports[`@nx/vite:configuration transform Web app to use Vite should transform workspace.json project config 1`] = ` -"{ - "projects": { - "my-test-web-app": { - "name": "my-test-web-app", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "root": "apps/my-test-web-app", - "sourceRoot": "apps/my-test-web-app/src", - "tags": [], - "targets": { - "build": { - "executor": "@nx/webpack:webpack", - "outputs": ["{options.outputPath}"], - "defaultConfiguration": "production", - "options": { - "outputPath": "dist/apps/my-test-web-app", - "compiler": "babel", - "main": "apps/my-test-web-app/src/main.ts", - "tsConfig": "apps/my-test-web-app/tsconfig.app.json", - "assets": [ - "apps/my-test-web-app/src/favicon.ico", - "apps/my-test-web-app/src/assets" - ], - "index": "apps/my-test-web-app/src/index.html", - "baseHref": "/", - "polyfills": "apps/my-test-web-app/src/polyfills.ts", - "styles": ["apps/my-test-web-app/src/styles.css"], - "scripts": [] - }, - "configurations": { - "production": { - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false, - "fileReplacements": [ - { - "replace": "apps/my-test-web-app/src/environments/environment.ts", - "with": "apps/my-test-web-app/src/environments/environment.prod.ts" - } - ] - } - } - }, - "serve": { - "executor": "@nx/webpack:dev-server", - "options": { - "buildTarget": "my-test-web-app:build" - }, - "configurations": { - "production": { - "buildTarget": "my-test-web-app:build:production" - } - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/my-test-web-app/jest.config.ts", - "passWithNoTests": true - } - } - } - } - } -} -" -`; - exports[`@nx/vite:configuration vitest should create a vitest configuration if "includeVitest" is true 1`] = ` "/// import { defineConfig } from 'vite'; diff --git a/packages/vite/src/generators/configuration/configuration.spec.ts b/packages/vite/src/generators/configuration/configuration.spec.ts index 7e025951d18c1..88ffc7e045a94 100644 --- a/packages/vite/src/generators/configuration/configuration.spec.ts +++ b/packages/vite/src/generators/configuration/configuration.spec.ts @@ -12,7 +12,6 @@ import { mockUnknownAppGenerator, mockWebAppGenerator, } from '../../utils/test-utils'; -import { ViteConfigurationGeneratorSchema } from './schema'; describe('@nx/vite:configuration', () => { let tree: Tree; @@ -56,7 +55,7 @@ describe('@nx/vite:configuration', () => { tree, 'apps/my-test-react-app/tsconfig.json' ); - expect(tsconfigJson.compilerOptions.types).toMatchObject(['vite/client']); + expect(tsconfigJson.compilerOptions.jsx).toBe('react-jsx'); }); it('should create vite.config file at the root of the app', () => { @@ -65,10 +64,6 @@ describe('@nx/vite:configuration', () => { tree.read('apps/my-test-react-app/vite.config.ts', 'utf-8') ).toMatchSnapshot(); }); - - it('should transform workspace.json project config', () => { - expect(tree.read('workspace.json', 'utf-8')).toMatchSnapshot(); - }); }); describe('transform Web app to use Vite', () => { @@ -84,7 +79,6 @@ describe('@nx/vite:configuration', () => { ); await viteConfigurationGenerator(tree, { addPlugin: true, - uiFramework: 'react', uiFramework: 'none', project: 'my-test-web-app', }); @@ -106,7 +100,7 @@ describe('@nx/vite:configuration', () => { it('should create correct tsconfig compilerOptions', () => { const tsconfigJson = readJson(tree, 'apps/my-test-web-app/tsconfig.json'); - expect(tsconfigJson.compilerOptions.types).toMatchObject(['vite/client']); + expect(tsconfigJson.compilerOptions.noImplicitReturns).toBeTruthy(); }); it('should create vite.config file at the root of the app', () => { @@ -115,10 +109,6 @@ describe('@nx/vite:configuration', () => { tree.read('apps/my-test-web-app/vite.config.ts', 'utf-8') ).toMatchSnapshot(); }); - - it('should transform workspace.json project config', () => { - expect(tree.read('workspace.json', 'utf-8')).toMatchSnapshot(); - }); }); describe('do not transform Angular app to use Vite', () => { @@ -132,14 +122,13 @@ describe('@nx/vite:configuration', () => { try { await viteConfigurationGenerator(tree, { addPlugin: true, - uiFramework: 'react', uiFramework: 'none', project: 'my-test-angular-app', }); } catch (e) { expect(e).toBeDefined(); expect(e.toString()).toContain( - 'The project my-test-angular-app cannot be converted to use the @nx/vite executors' + 'Nx cannot convert your project to use vite.' ); } }); @@ -151,27 +140,6 @@ describe('@nx/vite:configuration', () => { mockUnknownAppGenerator(tree); }); - it('should throw when trying to convert something unknown', async () => { - const { Confirm } = require('enquirer'); - const confirmSpy = jest.spyOn(Confirm.prototype, 'run'); - confirmSpy.mockResolvedValue(true); - expect.assertions(2); - - try { - await viteConfigurationGenerator(tree, { - addPlugin: true, - uiFramework: 'react', - uiFramework: 'none', - project: 'my-test-random-app', - }); - } catch (e) { - expect(e).toBeDefined(); - expect(e.toString()).toContain( - 'Error: Cannot find apps/my-test-random-app/tsconfig.json' - ); - } - }); - it('should throw when trying to convert something unknown and user denies conversion', async () => { const { Confirm } = require('enquirer'); const confirmSpy = jest.spyOn(Confirm.prototype, 'run'); @@ -182,115 +150,18 @@ describe('@nx/vite:configuration', () => { try { await viteConfigurationGenerator(tree, { addPlugin: true, - uiFramework: 'react', uiFramework: 'none', project: 'my-test-random-app', }); } catch (e) { expect(e).toBeDefined(); expect(e.toString()).toContain( - 'Nx could not verify that the executors you are using can be converted to the @nx/vite executors.' + 'Nx could not verify that your project can be converted to use Vite.' ); } }); }); - describe('transform React app to use Vite by providing custom targets', () => { - describe('transform React app if supported executor is provided', () => { - beforeEach(async () => { - tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); - mockReactMixedAppGenerator(tree); - const existing = 'existing'; - const existingVersion = '1.0.0'; - addDependenciesToPackageJson( - tree, - { '@nx/vite': nxVersion, [existing]: existingVersion }, - { [existing]: existingVersion } - ); - await viteConfigurationGenerator(tree, { - addPlugin: true, - uiFramework: 'react', - project: 'my-test-mixed-react-app', - buildTarget: 'valid-build', - }); - }); - it('should add vite packages and react-related dependencies for vite', async () => { - const packageJson = readJson(tree, '/package.json'); - expect(packageJson.devDependencies).toMatchObject({ - vite: expect.any(String), - '@vitejs/plugin-react': expect.any(String), - }); - }); - - it('should create vite.config file at the root of the app', () => { - expect(tree.exists('apps/my-test-mixed-react-app/vite.config.ts')).toBe( - true - ); - }); - - it('should transform workspace.json project config', () => { - expect(tree.read('workspace.json', 'utf-8')).toMatchSnapshot(); - }); - }); - - describe('do NOT transform React app if unsupported executor is provided', () => { - beforeEach(async () => { - tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); - mockReactMixedAppGenerator(tree); - const existing = 'existing'; - const existingVersion = '1.0.0'; - addDependenciesToPackageJson( - tree, - { '@nx/vite': nxVersion, [existing]: existingVersion }, - { [existing]: existingVersion } - ); - }); - it('should throw when trying to convert and user denies', async () => { - const { Confirm } = require('enquirer'); - const confirmSpy = jest.spyOn(Confirm.prototype, 'run'); - confirmSpy.mockResolvedValue(false); - expect.assertions(2); - - try { - await viteConfigurationGenerator(tree, { - addPlugin: true, - uiFramework: 'react', - uiFramework: 'none', - project: 'my-test-mixed-react-app', - buildTarget: 'invalid-build', - }); - } catch (e) { - expect(e).toBeDefined(); - expect(e.toString()).toContain( - 'The build target invalid-build cannot be converted to use the @nx/vite:build executor' - ); - } - }); - - it('should NOT throw error when trying to convert and user confirms', async () => { - const { Confirm } = require('enquirer'); - const confirmSpy = jest.spyOn(Confirm.prototype, 'run'); - confirmSpy.mockResolvedValue(true); - expect.assertions(1); - - try { - await viteConfigurationGenerator(tree, { - addPlugin: true, - uiFramework: 'react', - uiFramework: 'none', - project: 'my-test-mixed-react-app', - buildTarget: 'invalid-build', - }); - expect( - tree.exists('apps/my-test-mixed-react-app/vite.config.ts') - ).toBe(true); - } catch (e) { - throw new Error('Should not throw error'); - } - }); - }); - }); - describe('vitest', () => { beforeAll(async () => { tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); @@ -351,8 +222,6 @@ describe('@nx/vite:configuration', () => { expect( tree.read('libs/react-lib-nonb-jest/vite.config.ts', 'utf-8') ).toMatchSnapshot(); - - expect(tree.read('workspace.json', 'utf-8')).toMatchSnapshot(); }); it('should set up non buildable library which already has vite.config.ts correctly', async () => { @@ -372,8 +241,6 @@ describe('@nx/vite:configuration', () => { expect( tree.read('libs/react-lib-nonb-vitest/vite.config.ts', 'utf-8') ).toMatchSnapshot(); - - expect(tree.read('workspace.json', 'utf-8')).toMatchSnapshot(); } catch (e) { throw new Error('Should not throw error'); } diff --git a/packages/vite/src/generators/configuration/configuration.ts b/packages/vite/src/generators/configuration/configuration.ts index a925c12e73d7e..e0b3ebf960499 100644 --- a/packages/vite/src/generators/configuration/configuration.ts +++ b/packages/vite/src/generators/configuration/configuration.ts @@ -11,24 +11,18 @@ import { import { initGenerator as jsInitGenerator } from '@nx/js'; import { - addOrChangeBuildTarget, - addOrChangeServeTarget, + addBuildTarget, + addServeTarget, addPreviewTarget, createOrEditViteConfig, - deleteWebpackConfig, - editTsConfig, - findExistingTargetsInProject, - handleUnknownExecutors, - handleUnsupportedUserProvidedTargets, - moveAndEditIndexHtml, TargetFlags, - UserProvidedTargetName, } from '../../utils/generator-utils'; import initGenerator from '../init/init'; import vitestGenerator from '../vitest/vitest-generator'; import { ViteConfigurationGeneratorSchema } from './schema'; import { ensureDependencies } from '../../utils/ensure-dependencies'; +import { convertNonVite } from './lib/convert-non-vite'; export function viteConfigurationGenerator( host: Tree, @@ -49,16 +43,9 @@ export async function viteConfigurationGeneratorInternal( schema.addPlugin ??= process.env.NX_ADD_PLUGINS !== 'false'; const projectConfig = readProjectConfiguration(tree, schema.project); - const { - targets, - - root: projectRoot, - } = projectConfig; + const { targets, root: projectRoot } = projectConfig; const projectType = projectConfig.projectType ?? 'library'; - let buildTargetName = 'build'; - let serveTargetName = 'serve'; - let testTargetName = 'test'; schema.includeLib ??= projectType === 'library'; @@ -73,104 +60,7 @@ export async function viteConfigurationGeneratorInternal( let projectAlreadyHasViteTargets: TargetFlags = {}; if (!schema.newProject) { - const userProvidedTargetName: UserProvidedTargetName = { - build: schema.buildTarget, - serve: schema.serveTarget, - test: schema.testTarget, - }; - - const { - 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. - * - * If the `unsupported` flag was false, it would mean that we did not find - * a build target at all, so we can create a new one. - * - * So we only throw if we found a target, but it is unsupported. - */ - if (!validFoundTargetName.build && projectContainsUnsupportedExecutor) { - throw new Error( - `The project ${schema.project} cannot be converted to use the @nx/vite executors.` - ); - } - - if ( - alreadyHasNxViteTargets.build && - (alreadyHasNxViteTargets.serve || projectType === 'library') && - alreadyHasNxViteTargets.test - ) { - throw new Error( - `The project ${schema.project} is already configured to use the @nx/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. - * - * However, the executors that we may have found are not in the - * list of the specifically unsupported executors either. - * - * So, we should warn the user about it. - */ - - if ( - !projectContainsUnsupportedExecutor && - !validFoundTargetName.build && - !validFoundTargetName.serve && - !validFoundTargetName.test - ) { - await handleUnknownExecutors(schema.project); - } - - /** - * There is a possibility at this stage that the user has provided - * targets with unsupported executors. - * We keep track here of which of the targets that the user provided - * are unsupported. - * We do this with the `userProvidedTargetIsUnsupported` object, - * which contains flags for each target (whether it is supported or not). - * - * We also keep track of the targets that we found in the project, - * through the findExistingTargetsInProject function, which returns - * targets for build/serve/test that use supported executors, and - * can be converted to use the vite executors. These are the - * kept in the validFoundTargetName object. - */ - await handleUnsupportedUserProvidedTargets( - userProvidedTargetIsUnsupported, - userProvidedTargetName, - validFoundTargetName - ); - - /** - * Once the user is at this stage, then they can go ahead and convert. - */ - - buildTargetName = validFoundTargetName.build ?? buildTargetName; - serveTargetName = validFoundTargetName.serve ?? serveTargetName; - - if (projectType === 'application') { - moveAndEditIndexHtml(tree, schema, buildTargetName); - } - - deleteWebpackConfig( - tree, - projectRoot, - targets?.[buildTargetName]?.options?.webpackConfig - ); - - editTsConfig(tree, schema); + await convertNonVite(tree, schema, projectRoot, projectType, targets); } const jsInitTask = await jsInitGenerator(tree, { @@ -192,15 +82,15 @@ export async function viteConfigurationGeneratorInternal( if (!hasPlugin) { if (!projectAlreadyHasViteTargets.build) { - addOrChangeBuildTarget(tree, schema, buildTargetName); + addBuildTarget(tree, schema, 'build'); } if (!schema.includeLib) { if (!projectAlreadyHasViteTargets.serve) { - addOrChangeServeTarget(tree, schema, serveTargetName); + addServeTarget(tree, schema, 'serve'); } if (!projectAlreadyHasViteTargets.preview) { - addPreviewTarget(tree, schema, serveTargetName); + addPreviewTarget(tree, schema, 'preview'); } } } @@ -267,7 +157,7 @@ export async function viteConfigurationGeneratorInternal( inSourceTests: schema.inSourceTests, coverageProvider: 'v8', skipViteConfig: true, - testTarget: testTargetName, + testTarget: 'test', skipFormat: true, addPlugin: schema.addPlugin, }); diff --git a/packages/vite/src/generators/configuration/lib/convert-non-vite.ts b/packages/vite/src/generators/configuration/lib/convert-non-vite.ts new file mode 100644 index 0000000000000..6a6153f7e4ace --- /dev/null +++ b/packages/vite/src/generators/configuration/lib/convert-non-vite.ts @@ -0,0 +1,100 @@ +import { + TargetConfiguration, + Tree, + joinPathFragments, + logger, +} from '@nx/devkit'; +import { + findViteConfig, + findWebpackConfig, +} from '../../../utils/find-vite-config'; +import { ViteConfigurationGeneratorSchema } from '../schema'; +import { + deleteWebpackConfig, + editTsConfig, + findExistingJsBuildTargetInProject, + handleUnknownConfiguration, + moveAndEditIndexHtml, +} from '../../../utils/generator-utils'; + +export async function convertNonVite( + tree: Tree, + schema: ViteConfigurationGeneratorSchema, + projectRoot: string, + projectType: string, + targets: { + [targetName: string]: TargetConfiguration; + } +) { + // Check if it has vite + const hasViteConfig = findViteConfig(tree, projectRoot); + const hasIndexHtmlAtRoot = tree.exists( + joinPathFragments(projectRoot, 'index.html') + ); + + // Check if it has webpack + const hasWebpackConfig = findWebpackConfig(tree, projectRoot); + if (hasWebpackConfig) { + if (projectType === 'application') { + moveAndEditIndexHtml(tree, schema); + } + deleteWebpackConfig(tree, projectRoot, hasWebpackConfig); + editTsConfig(tree, schema); + return; + } + + if ( + projectType === 'application' && + hasViteConfig && + hasIndexHtmlAtRoot && + !hasWebpackConfig + ) { + throw new Error( + `The project ${schema.project} is already configured to use Vite.` + ); + return; + } + + if (projectType === 'library' && hasViteConfig) { + // continue anyway - it could need to be updated - only update vite.config.ts in any case + editTsConfig(tree, schema); + return; + } + + // Does the project have js executors? + const { supported: jsTargetName, unsupported } = + findExistingJsBuildTargetInProject(targets); + if (jsTargetName) { + editTsConfig(tree, schema); + return; + } + + if (unsupported) { + throw new Error(` + Nx cannot convert your project to use vite. + Please try again with a different project. + `); + } + + // If it's a library, it's most possible it's non-buildable + // So fix the tsconfig and return, to continue with the rest of the setup + if ( + projectType === 'library' && + !hasViteConfig && + !hasWebpackConfig && + !jsTargetName + ) { + editTsConfig(tree, schema); + return; + } + + /** + * The project is an app. + * The project has no js executors, no webpack config, no vite config. + * We did not find any configuration that hints the project can + * definitely be converted. + * So, we should warn the user about it. + * They can choose whether to convert it or not + */ + await handleUnknownConfiguration(schema.project); +} diff --git a/packages/vite/src/generators/configuration/schema.d.ts b/packages/vite/src/generators/configuration/schema.d.ts index 1080789ac97dd..907958eb2b28a 100644 --- a/packages/vite/src/generators/configuration/schema.d.ts +++ b/packages/vite/src/generators/configuration/schema.d.ts @@ -6,9 +6,6 @@ export interface ViteConfigurationGeneratorSchema { includeVitest?: boolean; inSourceTests?: boolean; includeLib?: boolean; - buildTarget?: string; - serveTarget?: string; - testTarget?: string; skipFormat?: boolean; testEnvironment?: 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime' | string; addPlugin?: boolean; diff --git a/packages/vite/src/generators/configuration/schema.json b/packages/vite/src/generators/configuration/schema.json index e6d89c42f3ff7..f19908c15dd39 100644 --- a/packages/vite/src/generators/configuration/schema.json +++ b/packages/vite/src/generators/configuration/schema.json @@ -44,18 +44,6 @@ "default": false, "hidden": true }, - "buildTarget": { - "type": "string", - "description": "The build target of the project to be transformed to use the @nx/vite:build executor." - }, - "serveTarget": { - "type": "string", - "description": "The serve target of the project to be transformed to use the @nx/vite:dev-server and @nx/vite:preview-server executors." - }, - "testTarget": { - "type": "string", - "description": "The test target of the project to be transformed to use the @nx/vite:test executor." - }, "skipFormat": { "description": "Skip formatting files.", "type": "boolean", diff --git a/packages/vite/src/generators/vitest/vitest-generator.ts b/packages/vite/src/generators/vitest/vitest-generator.ts index 8441937a58330..714a33061d2d6 100644 --- a/packages/vite/src/generators/vitest/vitest-generator.ts +++ b/packages/vite/src/generators/vitest/vitest-generator.ts @@ -14,7 +14,6 @@ import { import { addOrChangeTestTarget, createOrEditViteConfig, - findExistingTargetsInProject, } from '../../utils/generator-utils'; import { VitestGeneratorSchema } from './schema'; @@ -68,10 +67,7 @@ export async function vitestGeneratorInternal( : p.plugin === '@nx/vite/plugin') || hasPlugin ); if (!hasPluginCheck) { - const testTarget = - schema.testTarget ?? - findExistingTargetsInProject(targets).validFoundTargetName.test ?? - 'test'; + const testTarget = schema.testTarget ?? 'test'; addOrChangeTestTarget(tree, schema, testTarget); } diff --git a/packages/vite/src/migrations/update-16-6-0-change-ts-paths-plugin/change-ts-paths-plugin.ts b/packages/vite/src/migrations/update-16-6-0-change-ts-paths-plugin/change-ts-paths-plugin.ts index 4f6c5ebd7f265..44c5e10c1cefd 100644 --- a/packages/vite/src/migrations/update-16-6-0-change-ts-paths-plugin/change-ts-paths-plugin.ts +++ b/packages/vite/src/migrations/update-16-6-0-change-ts-paths-plugin/change-ts-paths-plugin.ts @@ -2,6 +2,7 @@ import { Tree, getProjects, joinPathFragments } from '@nx/devkit'; import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils'; import { ViteBuildExecutorOptions } from '../../executors/build/schema'; import { tsquery } from '@phenomnomnominal/tsquery'; +import { findViteConfig } from '../../utils/find-vite-config'; export default function update(tree: Tree) { const projects = getProjects(tree); @@ -51,13 +52,3 @@ export default function update(tree: Tree) { } ); } - -function findViteConfig(tree: Tree, searchRoot: string) { - const allowsExt = ['js', 'mjs', 'ts', 'cjs', 'mts', 'cts']; - - for (const ext of allowsExt) { - if (tree.exists(joinPathFragments(searchRoot, `vite.config.${ext}`))) { - return joinPathFragments(searchRoot, `vite.config.${ext}`); - } - } -} diff --git a/packages/vite/src/migrations/update-17-2-0/update-vite-config.ts b/packages/vite/src/migrations/update-17-2-0/update-vite-config.ts index 7989166f432bb..13ffa53de0733 100644 --- a/packages/vite/src/migrations/update-17-2-0/update-vite-config.ts +++ b/packages/vite/src/migrations/update-17-2-0/update-vite-config.ts @@ -12,6 +12,7 @@ import { updateTestConfig } from './lib/edit-test-config'; import { addFileReplacements } from './lib/add-file-replacements'; import { tsquery } from '@phenomnomnominal/tsquery'; import ts = require('typescript'); +import { findViteConfig } from '../../utils/find-vite-config'; export default async function updateBuildDir(tree: Tree) { const projects = getProjects(tree); @@ -54,16 +55,6 @@ export default async function updateBuildDir(tree: Tree) { await formatFiles(tree); } -function findViteConfig(tree: Tree, searchRoot: string) { - const allowsExt = ['js', 'mjs', 'ts', 'cjs', 'mts', 'cts']; - - for (const ext of allowsExt) { - if (tree.exists(joinPathFragments(searchRoot, `vite.config.${ext}`))) { - return joinPathFragments(searchRoot, `vite.config.${ext}`); - } - } -} - export function getConfigNode(configFileContents: string): ts.Node | undefined { if (!configFileContents) { return; diff --git a/packages/vite/src/migrations/update-17-2-0/lib/find-vite-config.ts b/packages/vite/src/utils/find-vite-config.ts similarity index 54% rename from packages/vite/src/migrations/update-17-2-0/lib/find-vite-config.ts rename to packages/vite/src/utils/find-vite-config.ts index a2e6847b46431..23f562cf90b31 100644 --- a/packages/vite/src/migrations/update-17-2-0/lib/find-vite-config.ts +++ b/packages/vite/src/utils/find-vite-config.ts @@ -9,3 +9,13 @@ export function findViteConfig(tree: Tree, searchRoot: string) { } } } + +export function findWebpackConfig(tree: Tree, searchRoot: string) { + const allowsExt = ['js', 'ts', 'mjs', 'cjs']; + + for (const ext of allowsExt) { + if (tree.exists(joinPathFragments(searchRoot, `webpack.config.${ext}`))) { + return joinPathFragments(searchRoot, `webpack.config.${ext}`); + } + } +} diff --git a/packages/vite/src/utils/generator-utils.spec.ts b/packages/vite/src/utils/generator-utils.spec.ts index 42a997aa2e7c8..832119ca00049 100644 --- a/packages/vite/src/utils/generator-utils.spec.ts +++ b/packages/vite/src/utils/generator-utils.spec.ts @@ -5,9 +5,8 @@ import { } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { - findExistingTargetsInProject, + findExistingJsBuildTargetInProject, getViteConfigPathForProject, - handleUnsupportedUserProvidedTargets, } from './generator-utils'; import { mockReactAppGenerator, @@ -97,105 +96,22 @@ describe('generator utils', () => { }); }); - describe('findExistingTargetsInProject', () => { - it('should return the correct targets', () => { + describe('findExistingJsBuildTargetInProject', () => { + it('should return no targets', () => { mockReactAppGenerator(tree); const { targets } = readProjectConfiguration(tree, 'my-test-react-app'); - const existingTargets = findExistingTargetsInProject(targets); - expect(existingTargets).toMatchObject({ - userProvidedTargetIsUnsupported: {}, - validFoundTargetName: { - build: 'build', - serve: 'serve', - test: 'test', - }, - projectContainsUnsupportedExecutor: false, - }); + const existingTargets = findExistingJsBuildTargetInProject(targets); + expect(existingTargets).toMatchObject({}); }); it('should return the correct - undefined - targets for Angular apps', () => { mockAngularAppGenerator(tree); const { targets } = readProjectConfiguration(tree, 'my-test-angular-app'); - const existingTargets = findExistingTargetsInProject(targets); + const existingTargets = findExistingJsBuildTargetInProject(targets); expect(existingTargets).toMatchObject({ - userProvidedTargetIsUnsupported: {}, - validFoundTargetName: { - test: 'test', - }, - projectContainsUnsupportedExecutor: true, + unsupported: 'build', }); }); }); - - describe('handleUnsupportedUserProvidedTargets', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - it('should throw error if unsupported and user does not want to confirm', async () => { - const { Confirm } = require('enquirer'); - const confirmSpy = jest.spyOn(Confirm.prototype, 'run'); - confirmSpy.mockResolvedValueOnce(false); - const object = { - unsupportedUserProvidedTarget: { - build: true, - }, - userProvidedTargets: { - build: 'my-build', - serve: 'my-serve', - test: 'my-test', - }, - targets: { - build: 'build', - serve: 'serve', - test: 'test', - }, - }; - - expect(async () => { - await handleUnsupportedUserProvidedTargets( - object.unsupportedUserProvidedTarget, - object.userProvidedTargets, - object.targets - ); - }).rejects.toThrowErrorMatchingInlineSnapshot(` - "The build target my-build cannot be converted to use the @nx/vite:build executor. - Please try again, either by providing a different build target or by not providing a target at all (Nx will - convert the first one it finds, most probably this one: build) - - Please note that converting a potentially non-compatible project to use Vite.js may result in unexpected behavior. Always commit - your changes before converting a project to use Vite.js, and test the converted project thoroughly before deploying it. - " - `); - }); - - it('should NOT throw error if unsupported and user confirms', async () => { - const { Confirm } = require('enquirer'); - const confirmSpy = jest.spyOn(Confirm.prototype, 'run'); - confirmSpy.mockResolvedValue(true); - const object = { - unsupportedUserProvidedTarget: { - build: true, - }, - userProvidedTargets: { - build: 'my-build', - serve: 'my-serve', - test: 'my-test', - }, - targets: { - build: 'build', - serve: 'serve', - test: 'test', - }, - }; - - expect(async () => { - await handleUnsupportedUserProvidedTargets( - object.unsupportedUserProvidedTarget, - object.userProvidedTargets, - object.targets - ); - }).not.toThrow(); - }); - }); }); diff --git a/packages/vite/src/utils/generator-utils.ts b/packages/vite/src/utils/generator-utils.ts index e969eaff7fe7b..49d5a071655b7 100644 --- a/packages/vite/src/utils/generator-utils.ts +++ b/packages/vite/src/utils/generator-utils.ts @@ -10,7 +10,6 @@ import { writeJson, } from '@nx/devkit'; import { ViteBuildExecutorOptions } from '../executors/build/schema'; -import { ViteDevServerExecutorOptions } from '../executors/dev-server/schema'; import { VitePreviewServerExecutorOptions } from '../executors/preview-server/schema'; import { VitestExecutorOptions } from '../executors/test/schema'; import { ViteConfigurationGeneratorSchema } from '../generators/configuration/schema'; @@ -22,45 +21,20 @@ export type TargetFlags = Partial>; export type UserProvidedTargetName = Partial>; export type ValidFoundTargetName = Partial>; -export function findExistingTargetsInProject( - targets: { - [targetName: string]: TargetConfiguration; - }, - userProvidedTargets?: UserProvidedTargetName -): { - validFoundTargetName: ValidFoundTargetName; - projectContainsUnsupportedExecutor: boolean; - userProvidedTargetIsUnsupported: TargetFlags; - alreadyHasNxViteTargets: TargetFlags; +export function findExistingJsBuildTargetInProject(targets: { + [targetName: string]: TargetConfiguration; +}): { + supported?: string; + unsupported?: string; } { - const output: ReturnType = { - validFoundTargetName: {}, - projectContainsUnsupportedExecutor: false, - userProvidedTargetIsUnsupported: {}, - alreadyHasNxViteTargets: {}, - }; + const output: { + supported?: string; + unsupported?: string; + } = {}; const supportedExecutors = { - build: [ - '@nxext/vite:build', - '@nx/js:babel', - '@nx/js:swc', - '@nx/webpack:webpack', - '@nx/rollup:rollup', - '@nrwl/js:babel', - '@nrwl/js:swc', - '@nrwl/webpack:webpack', - '@nrwl/rollup:rollup', - '@nrwl/web:rollup', - ], - serve: [ - '@nxext/vite:dev', - '@nx/webpack:dev-server', - '@nrwl/webpack:dev-server', - ], - test: ['@nx/jest:jest', '@nrwl/jest:jest', '@nxext/vitest:vitest'], + build: ['@nx/js:babel', '@nx/js:swc', '@nx/rollup:rollup'], }; - const unsupportedExecutors = [ '@nx/angular:ng-packagr-lite', '@nx/angular:package', @@ -73,7 +47,6 @@ export function findExistingTargetsInProject( '@nx/react-native:build-android', '@nx/react-native:bundle', '@nx/next:build', - '@nx/next:server', '@nx/js:tsc', '@nrwl/angular:ng-packagr-lite', '@nrwl/angular:package', @@ -86,81 +59,22 @@ export function findExistingTargetsInProject( '@nrwl/react-native:build-android', '@nrwl/react-native:bundle', '@nrwl/next:build', - '@nrwl/next:server', '@nrwl/js:tsc', '@angular-devkit/build-angular:browser', - '@angular-devkit/build-angular:dev-server', + '@angular-devkit/build-angular:browser-esbuild', + '@angular-devkit/build-angular:application', ]; - // 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 - - function checkUserProvidedTarget(target: Target) { - if (userProvidedTargets?.[target]) { - if ( - supportedExecutors[target].includes( - targets[userProvidedTargets[target]]?.executor - ) - ) { - output.validFoundTargetName[target] = userProvidedTargets[target]; - } else { - output.userProvidedTargetIsUnsupported[target] = true; - } - } - } - - checkUserProvidedTarget('build'); - checkUserProvidedTarget('serve'); - checkUserProvidedTarget('test'); - - // Returns early when we have a build, serve, and test targets. - if ( - output.validFoundTargetName.build && - output.validFoundTargetName.serve && - output.validFoundTargetName.test - ) { - return output; - } - - // We try to find the targets that are using the supported executors - // for build, serve and test, since these are the ones we will be converting + // We try to find the target that is using the supported executors + // for build since this is the one we will be converting for (const target in targets) { const executorName = targets[target].executor; - - const hasViteTargets = output.alreadyHasNxViteTargets; - hasViteTargets.build ||= - executorName === '@nx/vite:build' || executorName === '@nrwl/vite:build'; - hasViteTargets.serve ||= - executorName === '@nx/vite:dev-server' || - executorName === '@nrwl/vite:dev-server'; - hasViteTargets.test ||= - executorName === '@nx/vite:test' || executorName === '@nrwl/vite:test'; - hasViteTargets.preview ||= - executorName === '@nx/vite:preview-server' || - executorName === '@nrwl/vite:preview-server'; - - const foundTargets = output.validFoundTargetName; - if ( - !foundTargets.build && - supportedExecutors.build.includes(executorName) - ) { - foundTargets.build = target; - } - if ( - !foundTargets.serve && - supportedExecutors.serve.includes(executorName) - ) { - foundTargets.serve = target; - } - if (!foundTargets.test && supportedExecutors.test.includes(executorName)) { - foundTargets.test = target; + if (supportedExecutors.build.includes(executorName)) { + output.supported = target; + } else if (unsupportedExecutors.includes(executorName)) { + output.unsupported = target; } - - output.projectContainsUnsupportedExecutor ||= - unsupportedExecutors.includes(executorName); } - return output; } @@ -196,51 +110,39 @@ export function addOrChangeTestTarget( updateProjectConfiguration(tree, options.project, project); } -export function addOrChangeBuildTarget( +export function addBuildTarget( tree: Tree, options: ViteConfigurationGeneratorSchema, target: string ) { addBuildTargetDefaults(tree, '@nx/vite:build'); const project = readProjectConfiguration(tree, options.project); - const buildOptions: ViteBuildExecutorOptions = { outputPath: joinPathFragments( 'dist', project.root != '.' ? project.root : options.project ), }; - project.targets ??= {}; - - if (project.targets[target]) { - if (project.targets[target].executor === '@nxext/vite:build') { - buildOptions['base'] = project.targets[target].options?.baseHref; - buildOptions['sourcemap'] = project.targets[target].options?.sourcemaps; - } - project.targets[target].options = { ...buildOptions }; - project.targets[target].executor = '@nx/vite:build'; - } else { - project.targets[target] = { - executor: '@nx/vite:build', - outputs: ['{options.outputPath}'], - defaultConfiguration: 'production', - options: buildOptions, - configurations: { - development: { - mode: 'development', - }, - production: { - mode: 'production', - }, + project.targets[target] = { + executor: '@nx/vite:build', + outputs: ['{options.outputPath}'], + defaultConfiguration: 'production', + options: buildOptions, + configurations: { + development: { + mode: 'development', }, - }; - } + production: { + mode: 'production', + }, + }, + }; updateProjectConfiguration(tree, options.project, project); } -export function addOrChangeServeTarget( +export function addServeTarget( tree: Tree, options: ViteConfigurationGeneratorSchema, target: string @@ -249,35 +151,23 @@ export function addOrChangeServeTarget( project.targets ??= {}; - if (project.targets[target]) { - const serveTarget = project.targets[target]; - const serveOptions: ViteDevServerExecutorOptions = { + project.targets[target] = { + executor: '@nx/vite:dev-server', + defaultConfiguration: 'development', + options: { buildTarget: `${options.project}:build`, - }; - if (serveTarget.executor === '@nxext/vite:dev') { - serveOptions.proxyConfig = project.targets[target].options.proxyConfig; - } - serveTarget.executor = '@nx/vite:dev-server'; - serveTarget.options = serveOptions; - } else { - project.targets[target] = { - executor: '@nx/vite:dev-server', - defaultConfiguration: 'development', - options: { - buildTarget: `${options.project}:build`, + }, + configurations: { + development: { + buildTarget: `${options.project}:build:development`, + hmr: true, }, - configurations: { - development: { - buildTarget: `${options.project}:build:development`, - hmr: true, - }, - production: { - buildTarget: `${options.project}:build:production`, - hmr: false, - }, + production: { + buildTarget: `${options.project}:build:production`, + hmr: false, }, - }; - } + }, + }; updateProjectConfiguration(tree, options.project, project); } @@ -288,7 +178,6 @@ export function addOrChangeServeTarget( * @param tree * @param options * @param serveTarget An existing serve target. - * @param previewTarget The preview target to create. */ export function addPreviewTarget( tree: Tree, @@ -339,44 +228,26 @@ export function editTsConfig( const config = readJson(tree, `${projectConfig.root}/tsconfig.json`); - const commonCompilerOptions = { - target: 'ESNext', - useDefineForClassFields: true, - module: 'ESNext', - strict: true, - moduleResolution: 'Node', - resolveJsonModule: true, - isolatedModules: true, - types: ['vite/client'], - noEmit: true, - }; - switch (options.uiFramework) { case 'react': config.compilerOptions = { - ...commonCompilerOptions, - lib: ['DOM', 'DOM.Iterable', 'ESNext'], + jsx: 'react-jsx', allowJs: false, esModuleInterop: false, - skipLibCheck: true, allowSyntheticDefaultImports: true, - forceConsistentCasingInFileNames: true, - jsx: 'react-jsx', + strict: true, }; - config.include = [...config.include, 'src']; break; case 'none': config.compilerOptions = { - ...commonCompilerOptions, - lib: ['ESNext', 'DOM'], - skipLibCheck: true, - esModuleInterop: true, + module: 'commonjs', + forceConsistentCasingInFileNames: true, strict: true, - noUnusedLocals: true, - noUnusedParameters: true, + noImplicitOverride: true, + noPropertyAccessFromIndexSignature: true, noImplicitReturns: true, + noFallthroughCasesInSwitch: true, }; - config.include = [...config.include, 'src']; break; default: break; @@ -405,19 +276,14 @@ export function deleteWebpackConfig( export function moveAndEditIndexHtml( tree: Tree, - options: ViteConfigurationGeneratorSchema, - buildTarget: string + options: ViteConfigurationGeneratorSchema ) { const projectConfig = readProjectConfiguration(tree, options.project); - let indexHtmlPath = - projectConfig.targets?.[buildTarget]?.options?.index ?? - `${projectConfig.root}/src/index.html`; - let mainPath = - projectConfig.targets?.[buildTarget]?.options?.main ?? - `${projectConfig.root}/src/main.ts${ - options.uiFramework === 'react' ? 'x' : '' - }`; + let indexHtmlPath = `${projectConfig.root}/src/index.html`; + let mainPath = `${projectConfig.root}/src/main.ts${ + options.uiFramework === 'react' ? 'x' : '' + }`; if (projectConfig.root !== '.') { mainPath = mainPath.replace(projectConfig.root, ''); @@ -763,21 +629,17 @@ async function handleUnsupportedUserProvidedTargetsErrors( } } -export async function handleUnknownExecutors(projectName: string) { +export async function handleUnknownConfiguration(projectName: string) { if (process.env.NX_INTERACTIVE === 'false') { return; } logger.warn( ` - We could not find any targets in project ${projectName} that use executors which - can be converted to the @nx/vite executors. - - This either means that your project may not have a target - for building, serving, or testing at all, or that your targets are - using executors that are not known to Nx. + We could not find any configuration in project ${projectName} that + indicates whether we can definitely convert to Vite. - If you still want to convert your project to use the @nx/vite executors, + If you still want to convert your project to use Vite, please make sure to commit your changes before running this generator. ` ); @@ -785,13 +647,13 @@ export async function handleUnknownExecutors(projectName: string) { const { Confirm } = require('enquirer'); const prompt = new Confirm({ name: 'question', - message: `Should Nx convert your project to use the @nx/vite executors?`, + message: `Should Nx convert your project to use Vite?`, initial: true, }); const shouldConvert = await prompt.run(); if (!shouldConvert) { throw new Error(` - Nx could not verify that the executors you are using can be converted to the @nx/vite executors. + Nx could not verify that your project can be converted to use Vite. Please try again with a different project. `); } diff --git a/packages/vite/src/utils/test-files/react-project.config.json b/packages/vite/src/utils/test-files/react-project.config.json deleted file mode 100644 index 58a5a7aa0c859..0000000000000 --- a/packages/vite/src/utils/test-files/react-project.config.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "my-test-react-app", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "root": "apps/my-test-react-app", - "sourceRoot": "apps/my-test-react-app/src", - "projectType": "application", - "targets": { - "build": { - "executor": "@nx/webpack:webpack", - "outputs": ["{options.outputPath}"], - "defaultConfiguration": "production", - "options": { - "compiler": "babel", - "outputPath": "dist/apps/my-test-react-app", - "index": "apps/my-test-react-app/src/index.html", - "baseHref": "/", - "main": "apps/my-test-react-app/src/main.tsx", - "polyfills": "apps/my-test-react-app/src/polyfills.ts", - "tsConfig": "apps/my-test-react-app/tsconfig.app.json", - "assets": [ - "apps/my-test-react-app/src/favicon.ico", - "apps/my-test-react-app/src/assets" - ], - "styles": ["apps/my-test-react-app/src/styles.css"], - "scripts": [], - "webpackConfig": "@nx/react/plugins/webpack" - }, - "configurations": { - "development": { - "extractLicenses": false, - "optimization": false, - "sourceMap": true, - "vendorChunk": true - }, - "production": { - "fileReplacements": [ - { - "replace": "apps/my-test-react-app/src/environments/environment.ts", - "with": "apps/my-test-react-app/src/environments/environment.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false - } - } - }, - "serve": { - "executor": "@nx/webpack:dev-server", - "defaultConfiguration": "development", - "options": { - "buildTarget": "my-test-react-app:build", - "hmr": true - }, - "configurations": { - "development": { - "buildTarget": "my-test-react-app:build:development" - }, - "production": { - "buildTarget": "my-test-react-app:build:production", - "hmr": false - } - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/my-test-react-app/jest.config.ts", - "passWithNoTests": true - } - } - }, - "tags": [] -} diff --git a/packages/vite/src/utils/test-files/web-project.config.json b/packages/vite/src/utils/test-files/web-project.config.json deleted file mode 100644 index 88c114689c90f..0000000000000 --- a/packages/vite/src/utils/test-files/web-project.config.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "my-test-web-app", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "root": "apps/my-test-web-app", - "sourceRoot": "apps/my-test-web-app/src", - "tags": [], - "targets": { - "build": { - "executor": "@nx/webpack:webpack", - "outputs": ["{options.outputPath}"], - "defaultConfiguration": "production", - "options": { - "outputPath": "dist/apps/my-test-web-app", - "compiler": "babel", - "main": "apps/my-test-web-app/src/main.ts", - "tsConfig": "apps/my-test-web-app/tsconfig.app.json", - "assets": [ - "apps/my-test-web-app/src/favicon.ico", - "apps/my-test-web-app/src/assets" - ], - "index": "apps/my-test-web-app/src/index.html", - "baseHref": "/", - "polyfills": "apps/my-test-web-app/src/polyfills.ts", - "styles": ["apps/my-test-web-app/src/styles.css"], - "scripts": [] - }, - "configurations": { - "production": { - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false, - "fileReplacements": [ - { - "replace": "apps/my-test-web-app/src/environments/environment.ts", - "with": "apps/my-test-web-app/src/environments/environment.prod.ts" - } - ] - } - } - }, - "serve": { - "executor": "@nx/webpack:dev-server", - "options": { - "buildTarget": "my-test-web-app:build" - }, - "configurations": { - "production": { - "buildTarget": "my-test-web-app:build:production" - } - } - }, - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "apps/my-test-web-app/jest.config.ts", - "passWithNoTests": true - } - } - } -} diff --git a/packages/vite/src/utils/test-utils.ts b/packages/vite/src/utils/test-utils.ts index 5cbea4463e484..1b3271603a01d 100644 --- a/packages/vite/src/utils/test-utils.ts +++ b/packages/vite/src/utils/test-utils.ts @@ -1,7 +1,5 @@ import { Tree, writeJson } from '@nx/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'; import * as angularAppConfig from './test-files/angular-project.config.json'; import * as randomAppConfig from './test-files/unknown-project.config.json'; import * as mixedAppConfig from './test-files/react-mixed-project.config.json'; @@ -147,6 +145,8 @@ export function mockReactAppGenerator(tree: Tree): Tree { `import ReactDOM from 'react-dom';\n` ); + tree.write(`apps/${appName}/webpack.config.ts`, ``); + tree.write( `apps/${appName}/tsconfig.json`, `{ @@ -221,18 +221,7 @@ export function mockReactAppGenerator(tree: Tree): Tree { ` ); - writeJson(tree, 'workspace.json', { - projects: { - 'my-test-react-app': { - ...reactAppConfig, - root: `apps/${appName}`, - projectType: 'application', - }, - }, - }); - writeJson(tree, `apps/${appName}/project.json`, { - ...reactAppConfig, root: `apps/${appName}`, projectType: 'application', }); @@ -363,6 +352,8 @@ export function mockWebAppGenerator(tree: Tree): Tree { ` ); + tree.write(`apps/${appName}/webpack.config.ts`, ``); + tree.write( `apps/${appName}/src/index.html`, ` @@ -382,23 +373,16 @@ export function mockWebAppGenerator(tree: Tree): Tree { ` ); - writeJson( - tree, - 'workspace.json', - - { - projects: { - 'my-test-web-app': { - ...webAppConfig, - root: `apps/${appName}`, - projectType: 'application', - }, + writeJson(tree, 'workspace.json', { + projects: { + 'my-test-web-app': { + root: `apps/${appName}`, + projectType: 'application', }, - } - ); + }, + }); writeJson(tree, `apps/${appName}/project.json`, { - ...webAppConfig, root: `apps/${appName}`, projectType: 'application', }); diff --git a/packages/web/src/generators/application/application.spec.ts b/packages/web/src/generators/application/application.spec.ts index 7e4ee0b759642..08d171ca720a5 100644 --- a/packages/web/src/generators/application/application.spec.ts +++ b/packages/web/src/generators/application/application.spec.ts @@ -179,8 +179,6 @@ describe('app', () => { path: './tsconfig.spec.json', }, ]); - expect(tsconfig.compilerOptions.types).toMatchObject(['vite/client']); - expect(tree.exists('my-app-e2e/cypress.config.ts')).toBeTruthy(); expect(tree.exists('my-app/index.html')).toBeTruthy(); expect(tree.exists('my-app/vite.config.ts')).toBeTruthy(); @@ -578,7 +576,7 @@ describe('app', () => { it('should create correct tsconfig compilerOptions', () => { const tsconfigJson = readJson(viteAppTree, '/my-app/tsconfig.json'); - expect(tsconfigJson.compilerOptions.types).toMatchObject(['vite/client']); + expect(tsconfigJson.compilerOptions.noImplicitReturns).toBeTruthy(); }); it('should create index.html and vite.config file at the root of the app', () => {