From f394608658fb19e46c82fc120de612fdc840026b Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Mon, 21 Nov 2022 20:18:40 +0200 Subject: [PATCH] feat(bundling): vite generators (#13158) --- docs/generated/packages/react.json | 19 + docs/generated/packages/vite.json | 82 +- docs/generated/packages/web.json | 8 +- docs/packages.json | 2 +- docs/shared/vite-plugin.md | 24 +- e2e/react/src/cypress-component-tests.test.ts | 4 +- e2e/react/src/react.module-federation.test.ts | 4 +- e2e/react/src/react.test.ts | 22 +- e2e/vite/src/vite.test.ts | 193 +++-- e2e/web/src/web-vite.test.ts | 94 ++ e2e/web/src/web.test.ts | 42 +- packages/react/docs/application-examples.md | 43 + packages/react/package.json | 1 + .../application/application.spec.ts | 154 +++- .../src/generators/application/application.ts | 30 +- .../application/files/common-vite/index.html | 15 + .../files/common-vite/public/.gitkeep | 0 .../src/app/__fileName__.spec.tsx__tmpl__ | 26 + .../files/common-vite/src/app/nx-welcome.tsx | 820 ++++++++++++++++++ .../files/common-vite/src/assets/.gitkeep | 0 .../environments/environment.prod.ts__tmpl__ | 3 + .../src/environments/environment.ts__tmpl__ | 6 + .../files/common-vite/src/favicon.ico | Bin 0 -> 15086 bytes .../files/common-vite/src/main.tsx__tmpl__ | 10 + .../common-vite/src/polyfills.ts__tmpl__ | 7 + .../common-vite/tsconfig.app.json__tmpl__ | 14 + .../files/common-vite/tsconfig.json__tmpl__ | 29 + .../generators/application/lib/add-project.ts | 12 +- .../lib/create-application-files.ts | 5 +- .../src/generators/application/schema.d.ts | 1 + .../src/generators/application/schema.json | 9 +- .../cypress-component-configuration.spec.ts | 15 +- .../lib/update-configs.ts | 5 +- packages/react/src/generators/host/host.ts | 6 +- .../react/src/generators/host/schema.d.ts | 1 + .../react/src/generators/host/schema.json | 6 + .../src/generators/library/library.spec.ts | 2 + .../react/src/generators/remote/remote.ts | 1 + .../react/src/generators/remote/schema.d.ts | 1 + .../react/src/generators/remote/schema.json | 6 + .../generators/stories/stories.nextjs.spec.ts | 1 + packages/vite/docs/configuration-examples.md | 32 + packages/vite/docs/init-examples.md | 23 + packages/vite/generators.json | 18 +- packages/vite/index.ts | 1 + .../vite/src/executors/build/build.impl.ts | 2 +- packages/vite/src/executors/build/schema.d.ts | 6 +- .../executors/dev-server/dev-server.impl.ts | 2 +- .../vite/src/executors/dev-server/schema.d.ts | 2 - .../vite/src/executors/dev-server/schema.json | 35 +- .../vite/src/executors/test/vitest.impl.ts | 1 - .../configuration/configuration.spec.ts | 94 ++ .../generators/configuration/configuration.ts | 52 ++ .../src/generators/configuration/schema.d.ts | 5 + .../src/generators/configuration/schema.json | 33 + packages/vite/src/generators/init/init.ts | 4 +- packages/vite/src/generators/init/schema.json | 7 +- packages/vite/src/utils/generator-utils.ts | 354 ++++++++ .../{helper-functions.ts => options-utils.ts} | 0 .../test-files/react-project.config.json | 85 ++ .../utils/test-files/web-project.config.json | 72 ++ packages/vite/src/utils/test-utils.ts | 148 ++++ packages/web/docs/application-examples.md | 43 + packages/web/package.json | 1 + .../application/application.spec.ts | 101 +++ .../src/generators/application/application.ts | 40 +- .../files/app-vite/.babelrc__tmpl__ | 5 + .../application/files/app-vite/browserslist | 13 + .../application/files/app-vite/index.html | 15 + .../app-vite/src/app/app.element.__style__ | 416 +++++++++ .../src/app/app.element.spec.ts__tmpl__ | 21 + .../app-vite/src/app/app.element.ts__tmpl__ | 385 ++++++++ .../files/app-vite/src/assets/.gitkeep | 0 .../environments/environment.prod.ts__tmpl__ | 3 + .../src/environments/environment.ts__tmpl__ | 6 + .../files/app-vite/src/favicon.ico | Bin 0 -> 15086 bytes .../files/app-vite/src/main.ts__tmpl__ | 1 + .../files/app-vite/src/polyfills.ts__tmpl__ | 7 + .../files/app-vite/src/styles.__style__ | 1 + .../files/app-vite/tsconfig.app.json | 9 + .../files/app-vite/tsconfig.json__tmpl__ | 27 + .../src/generators/application/schema.d.ts | 2 +- .../src/generators/application/schema.json | 8 +- packages/web/src/generators/init/schema.d.ts | 2 +- packages/web/src/generators/init/schema.json | 2 +- tsconfig.base.json | 2 + 86 files changed, 3603 insertions(+), 206 deletions(-) create mode 100644 e2e/web/src/web-vite.test.ts create mode 100644 packages/react/docs/application-examples.md create mode 100644 packages/react/src/generators/application/files/common-vite/index.html create mode 100644 packages/react/src/generators/application/files/common-vite/public/.gitkeep create mode 100644 packages/react/src/generators/application/files/common-vite/src/app/__fileName__.spec.tsx__tmpl__ create mode 100644 packages/react/src/generators/application/files/common-vite/src/app/nx-welcome.tsx create mode 100644 packages/react/src/generators/application/files/common-vite/src/assets/.gitkeep create mode 100644 packages/react/src/generators/application/files/common-vite/src/environments/environment.prod.ts__tmpl__ create mode 100644 packages/react/src/generators/application/files/common-vite/src/environments/environment.ts__tmpl__ create mode 100644 packages/react/src/generators/application/files/common-vite/src/favicon.ico create mode 100644 packages/react/src/generators/application/files/common-vite/src/main.tsx__tmpl__ create mode 100644 packages/react/src/generators/application/files/common-vite/src/polyfills.ts__tmpl__ create mode 100644 packages/react/src/generators/application/files/common-vite/tsconfig.app.json__tmpl__ create mode 100644 packages/react/src/generators/application/files/common-vite/tsconfig.json__tmpl__ create mode 100644 packages/vite/docs/configuration-examples.md create mode 100644 packages/vite/docs/init-examples.md create mode 100644 packages/vite/src/generators/configuration/configuration.spec.ts create mode 100644 packages/vite/src/generators/configuration/configuration.ts create mode 100644 packages/vite/src/generators/configuration/schema.d.ts create mode 100644 packages/vite/src/generators/configuration/schema.json create mode 100644 packages/vite/src/utils/generator-utils.ts rename packages/vite/src/utils/{helper-functions.ts => options-utils.ts} (100%) create mode 100644 packages/vite/src/utils/test-files/react-project.config.json create mode 100644 packages/vite/src/utils/test-files/web-project.config.json create mode 100644 packages/vite/src/utils/test-utils.ts create mode 100644 packages/web/docs/application-examples.md create mode 100644 packages/web/src/generators/application/files/app-vite/.babelrc__tmpl__ create mode 100644 packages/web/src/generators/application/files/app-vite/browserslist create mode 100644 packages/web/src/generators/application/files/app-vite/index.html create mode 100644 packages/web/src/generators/application/files/app-vite/src/app/app.element.__style__ create mode 100644 packages/web/src/generators/application/files/app-vite/src/app/app.element.spec.ts__tmpl__ create mode 100644 packages/web/src/generators/application/files/app-vite/src/app/app.element.ts__tmpl__ create mode 100644 packages/web/src/generators/application/files/app-vite/src/assets/.gitkeep create mode 100644 packages/web/src/generators/application/files/app-vite/src/environments/environment.prod.ts__tmpl__ create mode 100644 packages/web/src/generators/application/files/app-vite/src/environments/environment.ts__tmpl__ create mode 100644 packages/web/src/generators/application/files/app-vite/src/favicon.ico create mode 100644 packages/web/src/generators/application/files/app-vite/src/main.ts__tmpl__ create mode 100644 packages/web/src/generators/application/files/app-vite/src/polyfills.ts__tmpl__ create mode 100644 packages/web/src/generators/application/files/app-vite/src/styles.__style__ create mode 100644 packages/web/src/generators/application/files/app-vite/tsconfig.app.json create mode 100644 packages/web/src/generators/application/files/app-vite/tsconfig.json__tmpl__ diff --git a/docs/generated/packages/react.json b/docs/generated/packages/react.json index e113d68667ec2..335aa360f304f 100644 --- a/docs/generated/packages/react.json +++ b/docs/generated/packages/react.json @@ -234,9 +234,16 @@ "type": "boolean", "default": false, "hidden": true + }, + "bundler": { + "description": "The bundler to use.", + "enum": ["vite", "webpack"], + "x-prompt": "Which bundler do you want to use?", + "default": "webpack" } }, "required": [], + "examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Application\" %}\n\nCreate an application named `my-app`:\n\n```bash\nnx g @nrwl/react:application my-app\n```\n\n{% /tab %}\n\n{% tab label=\"Application using Vite as bundler\" %}\n\nCreate an application named `my-app`:\n\n```bash\nnx g @nrwl/react:app my-app --bundler=vite\n```\n\n{% /tab %}\n\n{% tab label=\"Specify directory and style extension\" %}\n\nCreate an application named `my-app` in the `my-dir` directory and use `scss` for styles:\n\n```bash\nnx g @nrwl/react:app my-app --directory=my-dir --style=scss\n```\n\n{% /tab %}\n\n{% tab label=\"Add tags\" %}\n\nAdd tags to the application (used for linting).\n\n```bash\nnx g @nrwl/react:app my-app --tags=scope:admin,type:ui\n```\n\n{% /tab %}\n{% /tabs %}\n", "presets": [] }, "aliases": ["app"], @@ -1056,6 +1063,12 @@ "devServerPort": { "type": "number", "description": "The port for the dev server of the remote app." + }, + "bundler": { + "description": "The bundler to use.", + "enum": ["vite", "webpack"], + "x-prompt": "Which bundler do you want to use?", + "default": "webpack" } }, "required": ["name"], @@ -1217,6 +1230,12 @@ "devServerPort": { "type": "number", "description": "The port for the dev server of the remote app." + }, + "bundler": { + "description": "The bundler to use.", + "enum": ["vite", "webpack"], + "x-prompt": "Which bundler do you want to use?", + "default": "webpack" } }, "required": ["name"], diff --git a/docs/generated/packages/vite.json b/docs/generated/packages/vite.json index 4e1be625ba81b..c453f9ff13dfc 100644 --- a/docs/generated/packages/vite.json +++ b/docs/generated/packages/vite.json @@ -11,7 +11,7 @@ "id": "overview", "path": "/packages/vite", "file": "shared/vite-plugin", - "content": "The Nx plugin for [Vite](https://vitejs.dev/) and [Vitest](https://vitest.dev/).\n\n{% callout type=\"warning\" title=\"Early release plugin\" %}\nThis Nx plugin is in active development and may not be ready for real-world use. The planned release date for the stable plugin is December, 2022.\n{% /callout %}\n\n[Vite.js](https://vitejs.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects.\n\nWhy should you use this plugin?\n\n- Instant dev server start\n- Lightning fast Hot-Module Reloading\n- _Fast_ builds using Vite.\n- Vite-powered tests with smart and instant watch mode\n\nRead more about Vite and Vitest in the [Vite documentation](https://vitejs.dev/).\n\n## Setting up Vite\n\nTo create a new workspace, run `npx create-nx-workspace@latest --preset=vite`.\n\n### Add Vite to an existing workspace\n\nTo add the Vite plugin to an existing workspace, run the following:\n\n{% tabs %}\n{% tab label=\"npm\" %}\n\n```shell\nnpm install -D @nrwl/vite\n```\n\n{% /tab %}\n{% tab label=\"yarn\" %}\n\n```shell\nyarn add -D @nrwl/vite\n```\n\n{% /tab %}\n{% tab label=\"pnpm\" %}\n\n```shell\npnpm install -D @nrwl/vite\n```\n\n{% /tab %}\n{% /tabs %}\n\n### Initialize Vite.js\n\nAfter you install the plugin, you need to initialize Vite.js. You can do this by running the `init` executor. This executor will make sure to install all the necessary dependencies.\n\n```bash\nnx g @nrwl/vite:init\n```\n\n{% callout type=\"note\" title=\"Choosing a framework\" %}\nYou will notice that the executor will ask you of the framework you are planning to use. This is just to make sure that the right dependencies are installed. You can always install manually any other dependencies you need.\n{% /callout %}\n\n## Using Vite.js in your applications\n\nYou can use the `@nrwl/vite:dev-server` and the `@nrwl/vite:build` executors to serve and build your applications using Vite.js. To do this, you need to make a few adjustments to your application.\n\n{% github-repository url=\"https://github.com/mandarini/nx-recipes/tree/feat/react-vite-recipe/vite-example\" /%}\n\n### 1. Change the executors in your `project.json`\n\n#### The `serve` target\n\nIn your app's `project.json` file, change the executor of your `serve` target to use `@nrwl/vite:dev-server` and set it up with the following options:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"serve\": {\n \"executor\": \"@nrwl/vite:dev-server\",\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"port\": 4200,\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n{% callout type=\"note\" title=\"Other options\" %}\nYou do not have to set the `port` here, necessarily. You can also specify the port in the `vite.config.ts` file (see **Step 2** below).\nThe same goes for all other Vite.js options that you can find the [Vite.js documentation](https://vitejs.dev/config/). All these can be added in your `vite.config.ts` file.\n{% /callout %}\n\n#### The `build` target\n\nIn your app's `project.json` file, change the executor of your `build` target to use `@nrwl/vite:build` and set it up with the following options:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"build\": {\n \"executor\": \"@nrwl/vite:build\",\n ...\n \"options\": {\n \"outputPath\": \"dist/apps/my-app\"\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n{% callout type=\"note\" title=\"Other options\" %}\nYou can specify more options in the `vite.config.ts` file (see **Step 2** below).\n{% /callout %}\n\n### 2. Configure Vite.js\n\n#### TypeScript paths\n\nYou need to use the [`vite-tsconfig-paths` plugin](https://www.npmjs.com/package/vite-tsconfig-paths) to make sure that your TypeScript paths are resolved correctly in your monorepo.\n\n#### React plugin\n\nIf you are using React, you need to use the [`@vitejs/plugin-react` plugin](https://www.npmjs.com/package/@vitejs/plugin-react).\n\n#### How your `vite.config.ts` looks like\n\nAdd a `vite.config.ts` file to the root of your app. If you are not using React, you can skip adding the `react` plugin, of course.\n\n```ts\n// eg. apps/my-app/vite.config.ts\nimport { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';\n\nexport default defineConfig({\n plugins: [\n react(),\n ViteTsConfigPathsPlugin({\n root: '../../',\n projects: ['tsconfig.base.json'],\n }),\n ],\n});\n```\n\n{% callout type=\"note\" title=\"The `root` path\" %}\nMake sure the `root` path in the `ViteTsConfigPathsPlugin` options is correct. It should be the path to the root of your workspace.\n{% /callout %}\n\nIn that config file, you can configure Vite.js as you would normally do. For more information, see the [Vite.js documentation](https://vitejs.dev/config/).\n\n#### Creating a root `vite.config.ts` file\n\nYou can create a `vite.config.ts` file to the root of your workspace, as well as at the root of each of your applications. This file is used to configure Vite. You can read more about the configuration options in the [Vite documentation](https://vitejs.dev/config/).\n\nThe root `vite.config.ts` file can be used for all applications, and you can place in there general configurations that would apply for all your apps using Vite in your workspace. The application-specific `vite.config.ts` files can be used to override the root configuration, or, for example, import framework-specific plugins (eg. the `'@vitejs/plugin-react'` for React apps). The application-specific configuration files extend (using [`mergeConfig`](https://vitejs.dev/guide/api-javascript.html#mergeconfig)) the root configuration file. You can adjust this behavior to your needs.\n\nSo, if you are using a root `vite.config.ts` file, you should adjust your code as follows:\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n plugins: [],\n});\n```\n\nand then in your app's `vite.config.ts` file:\n\n```ts\n// eg. apps/my-app/vite.config.ts\nimport { mergeConfig } from 'vite';\nimport baseConfig from '../../vite.config';\nimport react from '@vitejs/plugin-react';\nimport ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';\n\nexport default mergeConfig(baseConfig, {\n plugins: [\n react(),\n ViteTsConfigPathsPlugin({\n root: '../../',\n projects: ['tsconfig.base.json'],\n }),\n ],\n});\n```\n\n### 3. Move `index.html` and point it to your app's entrypoint\n\nFirst of all, move your `index.html` file to the root of your app (eg. from `apps/my-app/src/index.html` to `apps/my-app/index.html`).\n\nThen, add a module `script` tag pointing to the `main.tsx` (or `main.ts`) file of your app:\n\n```html\n...\n \n
\n \n \n\n```\n\n### 4. Add a `public` folder\n\nYou can add a `public` folder to the root of your app. You can read more about the public folder in the [Vite.js documentation](https://vitejs.dev/guide/assets.html#the-public-directory). Use that folder as you would normally do.\n\n```treeview\nmyorg/\n├── apps/\n│ ├── my-app/\n│ │ ├── src/\n│ │ │ ├── app/\n│ │ │ ├── assets/\n│ │ │ ├── ...\n│ │ │ └── main.tsx\n│ │ ├── index.html\n│ │ ├── public/\n│ │ │ └── my-page.md\n│ │ ├── project.json\n│ │ ├── ...\n│ │ ├── tsconfig.app.json\n│ │ ├── tsconfig.json\n│ │ └── tsconfig.spec.json\n```\n\n### 5. Adjust your app's tsconfig.json\n\nChange your app's `tsconfig.json` (eg. `apps/my-app/tsconfig.json`) `compilerOptions` to the following:\n\n#### For React apps\n\n```json\n...\n \"compilerOptions\": {\n \"jsx\": \"react-jsx\",\n \"allowJs\": false,\n \"esModuleInterop\": false,\n \"allowSyntheticDefaultImports\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"isolatedModules\": true,\n \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Node\",\n \"noEmit\": true,\n \"resolveJsonModule\": true,\n \"skipLibCheck\": true,\n \"strict\": true,\n \"target\": \"ESNext\",\n \"types\": [\"vite/client\"],\n \"useDefineForClassFields\": true\n },\n...\n```\n\n#### For Web apps\n\n```json\n...\n \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"useDefineForClassFields\": true,\n \"module\": \"ESNext\",\n \"lib\": [\"ESNext\", \"DOM\"],\n \"moduleResolution\": \"Node\",\n \"strict\": true,\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"esModuleInterop\": true,\n \"noEmit\": true,\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"noImplicitReturns\": true,\n \"skipLibCheck\": true,\n \"types\": [\"vite/client\"]\n },\n \"include\": [\"src\"],\n...\n```\n\nYou can read more about the TypeScript compiler options in the [Vite.js documentation](https://vitejs.dev/guide/features.html#typescript-compiler-options).\n\n### 6. Use Vite.js!\n\nNow you can finally serve and build your app using Vite.js:\n\n#### Serve the app\n\n```\nnx serve my-app\n```\n\nor\n\n```\nnx run my-app:serve\n```\n\nNow, visit [http://localhost:4200](http://localhost:4200) to see your app running!\n\n#### Build the app\n\n```\nnx build my-app\n```\n\nor\n\n```\nnx run my-app:build\n```\n" + "content": "The Nx plugin for [Vite](https://vitejs.dev/) and [Vitest](https://vitest.dev/).\n\n{% callout type=\"warning\" title=\"Early release plugin\" %}\nThis Nx plugin is in active development and may not be ready for real-world use. The planned release date for the stable plugin is December, 2022.\n{% /callout %}\n\n[Vite.js](https://vitejs.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects.\n\nWhy should you use this plugin?\n\n- Instant dev server start\n- Lightning fast Hot-Module Reloading\n- _Fast_ builds using Vite.\n- Vite-powered tests with smart and instant watch mode\n\nRead more about Vite and Vitest in the [Vite documentation](https://vitejs.dev/).\n\n## Setting up Vite\n\nTo create a new workspace, run `npx create-nx-workspace@latest --preset=vite`.\n\n### Add Vite to an existing workspace\n\nTo add the Vite plugin to an existing workspace, run the following:\n\n{% tabs %}\n{% tab label=\"npm\" %}\n\n```shell\nnpm install -D @nrwl/vite\n```\n\n{% /tab %}\n{% tab label=\"yarn\" %}\n\n```shell\nyarn add -D @nrwl/vite\n```\n\n{% /tab %}\n{% tab label=\"pnpm\" %}\n\n```shell\npnpm install -D @nrwl/vite\n```\n\n{% /tab %}\n{% /tabs %}\n\n### Initialize Vite.js\n\nAfter you install the plugin, you need to initialize Vite.js. You can do this by running the `init` executor. This executor will make sure to install all the necessary dependencies.\n\n```bash\nnx g @nrwl/vite:init\n```\n\n{% callout type=\"note\" title=\"Choosing a framework\" %}\nYou will notice that the executor will ask you of the framework you are planning to use. This is just to make sure that the right dependencies are installed. You can always install manually any other dependencies you need.\n{% /callout %}\n\n## Generate an application using Vite\n\nYou can generate a React or a Web application that uses Vite.js. The `@nrwl/react:app` and `@nrwl/web:app` generators accept the `bundler` option, where you can pass `vite`. This will generate a new application configured to use Vite.js, and it will also install all the necessary dependencies.\n\nTo generate a React application using Vite.js, run the following:\n\n```bash\nnx g @nrwl/react:app my-app --bundler=vite\n```\n\nTo generate a Web application using Vite.js, run the following:\n\n```bash\nnx g @nrwl/web:app my-app --bundler=vite\n```\n\n## Modify an existing React or Web application to use Vite.js\n\nYou can use the `@nrwl/vite:configuration` generator to change your React or Web application to use Vite.js. This generator will modify your application's configuration to use Vite.js, and it will also install all the necessary dependencies.\n\nYou can read more about this generator on the [`@nrwl/vite:configuration`](/packages/vite/generators/configuration) generator page.\n\n## Set up your apps to use Vite.js manually\n\nYou can use the `@nrwl/vite:dev-server` and the `@nrwl/vite:build` executors to serve and build your applications using Vite.js. To do this, you need to make a few adjustments to your application.\n\n{% github-repository url=\"https://github.com/mandarini/nx-recipes/tree/feat/react-vite-recipe/vite-example\" /%}\n\n### 1. Change the executors in your `project.json`\n\n#### The `serve` target\n\nIn your app's `project.json` file, change the executor of your `serve` target to use `@nrwl/vite:dev-server` and set it up with the following options:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"serve\": {\n \"executor\": \"@nrwl/vite:dev-server\",\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"port\": 4200,\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n{% callout type=\"note\" title=\"Other options\" %}\nYou do not have to set the `port` here, necessarily. You can also specify the port in the `vite.config.ts` file (see **Step 2** below).\nThe same goes for all other Vite.js options that you can find the [Vite.js documentation](https://vitejs.dev/config/). All these can be added in your `vite.config.ts` file.\n{% /callout %}\n\n#### The `build` target\n\nIn your app's `project.json` file, change the executor of your `build` target to use `@nrwl/vite:build` and set it up with the following options:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"build\": {\n \"executor\": \"@nrwl/vite:build\",\n ...\n \"options\": {\n \"outputPath\": \"dist/apps/my-app\"\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n{% callout type=\"note\" title=\"Other options\" %}\nYou can specify more options in the `vite.config.ts` file (see **Step 2** below).\n{% /callout %}\n\n### 2. Configure Vite.js\n\n#### TypeScript paths\n\nYou need to use the [`vite-tsconfig-paths` plugin](https://www.npmjs.com/package/vite-tsconfig-paths) to make sure that your TypeScript paths are resolved correctly in your monorepo.\n\n#### React plugin\n\nIf you are using React, you need to use the [`@vitejs/plugin-react` plugin](https://www.npmjs.com/package/@vitejs/plugin-react).\n\n#### How your `vite.config.ts` looks like\n\nAdd a `vite.config.ts` file to the root of your app. If you are not using React, you can skip adding the `react` plugin, of course.\n\n```ts\n// eg. apps/my-app/vite.config.ts\nimport { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';\n\nexport default defineConfig({\n plugins: [\n react(),\n ViteTsConfigPathsPlugin({\n root: '../../',\n projects: ['tsconfig.base.json'],\n }),\n ],\n});\n```\n\n{% callout type=\"note\" title=\"The `root` path\" %}\nMake sure the `root` path in the `ViteTsConfigPathsPlugin` options is correct. It should be the path to the root of your workspace.\n{% /callout %}\n\nIn that config file, you can configure Vite.js as you would normally do. For more information, see the [Vite.js documentation](https://vitejs.dev/config/).\n\n#### Creating a root `vite.config.ts` file\n\nYou can create a `vite.config.ts` file to the root of your workspace, as well as at the root of each of your applications. This file is used to configure Vite. You can read more about the configuration options in the [Vite documentation](https://vitejs.dev/config/).\n\nThe root `vite.config.ts` file can be used for all applications, and you can place in there general configurations that would apply for all your apps using Vite in your workspace. The application-specific `vite.config.ts` files can be used to override the root configuration, or, for example, import framework-specific plugins (eg. the `'@vitejs/plugin-react'` for React apps). The application-specific configuration files extend (using [`mergeConfig`](https://vitejs.dev/guide/api-javascript.html#mergeconfig)) the root configuration file. You can adjust this behavior to your needs.\n\nSo, if you are using a root `vite.config.ts` file, you should adjust your code as follows:\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n plugins: [],\n});\n```\n\nand then in your app's `vite.config.ts` file:\n\n```ts\n// eg. apps/my-app/vite.config.ts\nimport { mergeConfig } from 'vite';\nimport baseConfig from '../../vite.config';\nimport react from '@vitejs/plugin-react';\nimport ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';\n\nexport default mergeConfig(baseConfig, {\n plugins: [\n react(),\n ViteTsConfigPathsPlugin({\n root: '../../',\n projects: ['tsconfig.base.json'],\n }),\n ],\n});\n```\n\n### 3. Move `index.html` and point it to your app's entrypoint\n\nFirst of all, move your `index.html` file to the root of your app (eg. from `apps/my-app/src/index.html` to `apps/my-app/index.html`).\n\nThen, add a module `script` tag pointing to the `main.tsx` (or `main.ts`) file of your app:\n\n```html\n...\n \n
\n \n \n\n```\n\n### 4. Add a `public` folder\n\nYou can add a `public` folder to the root of your app. You can read more about the public folder in the [Vite.js documentation](https://vitejs.dev/guide/assets.html#the-public-directory). Use that folder as you would normally do.\n\n```treeview\nmyorg/\n├── apps/\n│ ├── my-app/\n│ │ ├── src/\n│ │ │ ├── app/\n│ │ │ ├── assets/\n│ │ │ ├── ...\n│ │ │ └── main.tsx\n│ │ ├── index.html\n│ │ ├── public/\n│ │ │ └── my-page.md\n│ │ ├── project.json\n│ │ ├── ...\n│ │ ├── tsconfig.app.json\n│ │ ├── tsconfig.json\n│ │ └── tsconfig.spec.json\n```\n\n### 5. Adjust your app's tsconfig.json\n\nChange your app's `tsconfig.json` (eg. `apps/my-app/tsconfig.json`) `compilerOptions` to the following:\n\n#### For React apps\n\n```json\n...\n \"compilerOptions\": {\n \"jsx\": \"react-jsx\",\n \"allowJs\": false,\n \"esModuleInterop\": false,\n \"allowSyntheticDefaultImports\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"isolatedModules\": true,\n \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Node\",\n \"noEmit\": true,\n \"resolveJsonModule\": true,\n \"skipLibCheck\": true,\n \"strict\": true,\n \"target\": \"ESNext\",\n \"types\": [\"vite/client\"],\n \"useDefineForClassFields\": true\n },\n...\n```\n\n#### For Web apps\n\n```json\n...\n \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"useDefineForClassFields\": true,\n \"module\": \"ESNext\",\n \"lib\": [\"ESNext\", \"DOM\"],\n \"moduleResolution\": \"Node\",\n \"strict\": true,\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"esModuleInterop\": true,\n \"noEmit\": true,\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"noImplicitReturns\": true,\n \"skipLibCheck\": true,\n \"types\": [\"vite/client\"]\n },\n \"include\": [\"src\"],\n...\n```\n\nYou can read more about the TypeScript compiler options in the [Vite.js documentation](https://vitejs.dev/guide/features.html#typescript-compiler-options).\n\n### 6. Use Vite.js!\n\nNow you can finally serve and build your app using Vite.js:\n\n#### Serve the app\n\n```\nnx serve my-app\n```\n\nor\n\n```\nnx run my-app:serve\n```\n\nNow, visit [http://localhost:4200](http://localhost:4200) to see your app running!\n\n#### Build the app\n\n```\nnx build my-app\n```\n\nor\n\n```\nnx run my-app:build\n```\n" } ], "generators": [ @@ -20,8 +20,8 @@ "factory": "./src/generators/init/init", "schema": { "cli": "nx", - "title": "Add Vite Configuration to the workspace", - "description": "Add Vite Configuration to the workspace.", + "title": "Initialize Vite in the workspace.", + "description": "Initialize Vite in the workspace.", "$id": "init-vite-plugin", "type": "object", "properties": { @@ -33,13 +33,54 @@ "x-prompt": "What UI framework plugin should Vite use?" } }, + "examplesFile": "This is a generator will initialize Vite.js in your workspace. It will install all the necessary dependencies. You can read more about how this generator works, in the [Vite package overview page](/packages/vite).\n\nYou can use it on its own like this:\n\n```bash\nnx g @nrwl/vite:configuration\n```\n\nHowever, this generator will be called when you are either converting an existing React or Web app to use Vite, using the [`@nrwl/vite:configuration` generator](/packages/vite/generators/configuration), or when you are creating a new React or Web app using the [`@nrwl/react:app`](/packages/react/generators/application) or [`@nrwl/web:app`](<(/packages/web/generators/application)>) generators, if you choose `vite` as the `bundler`.\n\n## Examples\n\n### Install all the necessary dependencies for Vite and the React plugin\n\n```bash\nnx g @nrwl/vite:init --uiFramework=react\n```\n\n### Install all the necessary dependencies for Vite\n\n```bash\nnx g @nrwl/vite:init --uiFramework=none\n```\n", "presets": [] }, - "description": "Add Vite configuration to the workspace.", + "description": "Initialize Vite in the workspace.", "aliases": ["ng-add"], "hidden": true, "implementation": "/packages/vite/src/generators/init/init.ts", "path": "/packages/vite/src/generators/init/schema.json" + }, + { + "name": "configuration", + "factory": "./src/generators/configuration/configuration", + "schema": { + "cli": "nx", + "title": "Add Vite Configuration to an application.", + "description": "Add Vite Configuration to an application.", + "$id": "configure-vite-app", + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "The name of the project.", + "$default": { "$source": "argv", "index": 0 }, + "aliases": ["name", "projectName"], + "x-dropdown": "project", + "x-prompt": "What is the name of the project to set up a webpack for?" + }, + "uiFramework": { + "type": "string", + "description": "UI Framework to use for Vite.", + "enum": ["react", "none"], + "default": "none", + "x-prompt": "What UI framework plugin should Vite use?" + }, + "newProject": { + "type": "boolean", + "description": "Is this a new project?", + "default": false + } + }, + "examplesFile": "This is a generator for setting up Vite configuration for an existing React or Web application. It will change the build and serve targets to use the `@nrwl/vite` executors for serving and building the application. This generator will modify your code, so make sure to commit your changes before running it.\n\n```bash\nnx g @nrwl/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 can read more about how this generator works, 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 @nrwl/vite:configuration --project=my-app --uiFramework=react\n```\n\nThis will change the `my-app` project to use Vite instead of the default Webpack configuration. The changes this generator will do are described in the [Vite package overview page](/packages/vite).\n\n### Change a Web app to use Vite\n\n```bash\nnx g @nrwl/vite:configuration --project=my-app --uiFramework=none\n```\n\nThis will change the `my-app` project to use Vite instead of the existing bundler configuration.\n", + "presets": [] + }, + "description": "Add Vite configuration to an application.", + "aliases": ["ng-add"], + "hidden": false, + "implementation": "/packages/vite/src/generators/configuration/configuration.ts", + "path": "/packages/vite/src/generators/configuration/schema.json" } ], "executors": [ @@ -53,43 +94,20 @@ "cli": "nx", "description": "Dev server for Vite.", "type": "object", - "presets": [{ "name": "Default minimum setup", "keys": [] }], + "presets": [ + { "name": "Default minimum setup", "keys": ["buildTarget"] }, + { "name": "Using a Different Port", "keys": ["buildTarget", "port"] } + ], "properties": { "buildTarget": { "type": "string", "description": "Target which builds the application." }, - "baseHref": { - "type": "string", - "description": "Base url for the application being built." - }, "proxyConfig": { "type": "string", "description": "Path to the proxy configuration file.", "x-completion-type": "file" }, - "fileReplacements": { - "description": "Replace files with other files in the build.", - "type": "array", - "items": { - "type": "object", - "properties": { - "replace": { - "type": "string", - "description": "The file to be replaced.", - "x-completion-type": "file" - }, - "with": { - "type": "string", - "description": "The file to replace with.", - "x-completion-type": "file" - } - }, - "additionalProperties": false, - "required": ["replace", "with"] - }, - "default": [] - }, "port": { "type": "number", "description": "Port to listen on.", @@ -112,7 +130,7 @@ } }, "definitions": {}, - "required": [], + "required": ["buildTarget"], "examplesFile": "`project.json`:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"serve\": {\n \"executor\": \"@nrwl/vite:dev-server\",\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n```bash\nnx serve my-app\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Set up a custom port\" %}\n\nYou can always set the port in your `vite.config.ts` file. However, you can also set it directly in your `project.json` file, in the `serve` target options:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"serve\": {\n \"executor\": \"@nrwl/vite:dev-server\",\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"port\": 4200,\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"Set a custom path for vite.config.ts\" %}\n\nNx will automatically look in the root of your application for a `vite.config.ts` (or a `vite.config.js`) file. If you want to use a different path, you can set it in your `project.json` file, in the `serve` target options:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"serve\": {\n \"executor\": \"@nrwl/vite:dev-server\",\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"configFile\": \"apps/my-app/vite.config.other-path.ts\"\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\nor even\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"serve\": {\n \"executor\": \"@nrwl/vite:dev-server\",\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"configFile\": \"vite.config.base.ts\"\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"Specify a proxyConfig\" %}\n\nYou can specify a proxy config by pointing to the path of your proxy configuration file:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"serve\": {\n \"executor\": \"@nrwl/vite:dev-server\",\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"proxyConfig\": \"apps/my-app/proxy.conf.json\"\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n" }, "description": "Vite dev server.", diff --git a/docs/generated/packages/web.json b/docs/generated/packages/web.json index af00ffd1bb518..1f9baa4a5c37b 100644 --- a/docs/generated/packages/web.json +++ b/docs/generated/packages/web.json @@ -29,7 +29,7 @@ "bundler": { "type": "string", "description": "The bundler to use.", - "enum": ["webpack", "none"], + "enum": ["webpack", "none", "vite"], "default": "webpack" }, "unitTestRunner": { @@ -119,8 +119,9 @@ "bundler": { "type": "string", "description": "The bundler to use.", - "enum": ["webpack", "none"], - "default": "webpack" + "enum": ["webpack", "none", "vite"], + "default": "webpack", + "x-prompt": "Which bundler do you want to use?" }, "linter": { "description": "The tool to use for running lint checks.", @@ -160,6 +161,7 @@ } }, "required": [], + "examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Application\" %}\n\nCreate an application named `my-app`:\n\n```bash\nnx g @nrwl/web:application my-app\n```\n\n{% /tab %}\n\n{% tab label=\"Application using Vite as bundler\" %}\n\nCreate an application named `my-app`:\n\n```bash\nnx g @nrwl/web:app my-app --bundler=vite\n```\n\n{% /tab %}\n\n{% tab label=\"Specify directory\" %}\n\nCreate an application named `my-app` in the `my-dir` directory:\n\n```bash\nnx g @nrwl/web:app my-app --directory=my-dir\n```\n\n{% /tab %}\n\n{% tab label=\"Add tags\" %}\n\nAdd tags to the application (used for linting).\n\n```bash\nnx g @nrwl/web:app my-app --tags=scope:admin,type:ui\n```\n\n{% /tab %}\n{% /tabs %}\n", "presets": [] }, "aliases": ["app"], diff --git a/docs/packages.json b/docs/packages.json index cf80faefce647..00fbf1cc24767 100644 --- a/docs/packages.json +++ b/docs/packages.json @@ -371,7 +371,7 @@ "path": "generated/packages/vite.json", "schemas": { "executors": ["dev-server", "build", "test"], - "generators": ["init"] + "generators": ["init", "configuration"] } }, { diff --git a/docs/shared/vite-plugin.md b/docs/shared/vite-plugin.md index f5ee71c541b72..fd1cfb7943873 100644 --- a/docs/shared/vite-plugin.md +++ b/docs/shared/vite-plugin.md @@ -59,7 +59,29 @@ nx g @nrwl/vite:init You will notice that the executor will ask you of the framework you are planning to use. This is just to make sure that the right dependencies are installed. You can always install manually any other dependencies you need. {% /callout %} -## Using Vite.js in your applications +## Generate an application using Vite + +You can generate a React or a Web application that uses Vite.js. The `@nrwl/react:app` and `@nrwl/web:app` generators accept the `bundler` option, where you can pass `vite`. This will generate a new application configured to use Vite.js, and it will also install all the necessary dependencies. + +To generate a React application using Vite.js, run the following: + +```bash +nx g @nrwl/react:app my-app --bundler=vite +``` + +To generate a Web application using Vite.js, run the following: + +```bash +nx g @nrwl/web:app my-app --bundler=vite +``` + +## Modify an existing React or Web application to use Vite.js + +You can use the `@nrwl/vite:configuration` generator to change your React or Web application to use Vite.js. This generator will modify your application's configuration to use Vite.js, and it will also install all the necessary dependencies. + +You can read more about this generator on the [`@nrwl/vite:configuration`](/packages/vite/generators/configuration) generator page. + +## Set up your apps to use Vite.js manually You can use the `@nrwl/vite:dev-server` and the `@nrwl/vite:build` executors to serve and build your applications using Vite.js. To do this, you need to make a few adjustments to your application. diff --git a/e2e/react/src/cypress-component-tests.test.ts b/e2e/react/src/cypress-component-tests.test.ts index 72ed374ddb87e..a1e1fc4949da2 100644 --- a/e2e/react/src/cypress-component-tests.test.ts +++ b/e2e/react/src/cypress-component-tests.test.ts @@ -16,7 +16,9 @@ describe('React Cypress Component Tests', () => { beforeAll(() => { projectName = newProject({ name: uniq('cy-react') }); - runCLI(`generate @nrwl/react:app ${appName} --no-interactive`); + runCLI( + `generate @nrwl/react:app ${appName} --bundler=webpack --no-interactive` + ); runCLI( `generate @nrwl/react:component fancy-cmp --project=${appName} --no-interactive` ); diff --git a/e2e/react/src/react.module-federation.test.ts b/e2e/react/src/react.module-federation.test.ts index 35a67e643b1d7..bbea37f4fcc60 100644 --- a/e2e/react/src/react.module-federation.test.ts +++ b/e2e/react/src/react.module-federation.test.ts @@ -25,7 +25,9 @@ describe('React Module Federation', () => { const remote2 = uniq('remote2'); const remote3 = uniq('remote3'); - runCLI(`generate @nrwl/react:host ${shell} --style=css --no-interactive`); + runCLI( + `generate @nrwl/react:host ${shell} --bundler=webpack --style=css --no-interactive` + ); runCLI( `generate @nrwl/react:remote ${remote1} --style=css --host=${shell} --no-interactive` ); diff --git a/e2e/react/src/react.test.ts b/e2e/react/src/react.test.ts index 926c63a0f3ef0..41e3fc1c89b27 100644 --- a/e2e/react/src/react.test.ts +++ b/e2e/react/src/react.test.ts @@ -28,7 +28,9 @@ describe('React Applications', () => { const libName = uniq('lib'); const libWithNoComponents = uniq('lib'); - runCLI(`generate @nrwl/react:app ${appName} --style=css --no-interactive`); + runCLI( + `generate @nrwl/react:app ${appName} --style=css --bundler=webpack --no-interactive` + ); runCLI(`generate @nrwl/react:lib ${libName} --style=css --no-interactive`); runCLI( `generate @nrwl/react:lib ${libWithNoComponents} --no-interactive --no-component` @@ -63,7 +65,9 @@ describe('React Applications', () => { it('should generate app with legacy-ie support', async () => { const appName = uniq('app'); - runCLI(`generate @nrwl/react:app ${appName} --style=css --no-interactive`); + runCLI( + `generate @nrwl/react:app ${appName} --style=css --bundler=webpack --no-interactive` + ); // changing browser support of this application updateFile(`apps/${appName}/.browserslistrc`, `IE 11`); @@ -90,7 +94,9 @@ describe('React Applications', () => { const appName = uniq('app'); const libName = uniq('lib'); - runCLI(`generate @nrwl/react:app ${appName} --no-interactive --js`); + runCLI( + `generate @nrwl/react:app ${appName} --bundler=webpack --no-interactive --js` + ); runCLI(`generate @nrwl/react:lib ${libName} --no-interactive --js`); const mainPath = `apps/${appName}/src/main.js`; @@ -173,7 +179,7 @@ describe('React Applications: --style option', () => { `('should support global and css modules', ({ style }) => { const appName = uniq('app'); runCLI( - `generate @nrwl/react:app ${appName} --style=${style} --no-interactive` + `generate @nrwl/react:app ${appName} --style=${style} --bundler=webpack --no-interactive` ); // make sure stylePreprocessorOptions works @@ -210,7 +216,9 @@ describe('React Applications: additional packages', () => { it('should generate app with routing', async () => { const appName = uniq('app'); - runCLI(`generate @nrwl/react:app ${appName} --routing --no-interactive`); + runCLI( + `generate @nrwl/react:app ${appName} --routing --bundler=webpack --no-interactive` + ); runCLI(`build ${appName} --outputHashing none`); @@ -226,7 +234,7 @@ describe('React Applications: additional packages', () => { const appName = uniq('app'); const libName = uniq('lib'); - runCLI(`g @nrwl/react:app ${appName} --no-interactive`); + runCLI(`g @nrwl/react:app ${appName} --bundler=webpack --no-interactive`); runCLI(`g @nrwl/react:redux lemon --project=${appName}`); runCLI(`g @nrwl/react:lib ${libName} --no-interactive`); runCLI(`g @nrwl/react:redux orange --project=${libName}`); @@ -252,7 +260,7 @@ describe('React Applications and Libs with PostCSS', () => { const appName = uniq('app'); const libName = uniq('lib'); - runCLI(`g @nrwl/react:app ${appName} --no-interactive`); + runCLI(`g @nrwl/react:app ${appName} --bundler=webpack --no-interactive`); runCLI(`g @nrwl/react:lib ${libName} --no-interactive`); const mainPath = `apps/${appName}/src/main.tsx`; diff --git a/e2e/vite/src/vite.test.ts b/e2e/vite/src/vite.test.ts index bc923379d33e8..caadc8c089edf 100644 --- a/e2e/vite/src/vite.test.ts +++ b/e2e/vite/src/vite.test.ts @@ -19,13 +19,14 @@ const myApp = uniq('my-app'); describe('Vite Plugin', () => { let proj: string; - beforeEach(() => { - proj = newProject(); - runCLI(`generate @nrwl/react:app ${myApp}`); - runCLI(`generate @nrwl/vite:init`); - updateFile( - `apps/${myApp}/index.html`, - ` + describe('set up new project manually', () => { + beforeEach(() => { + proj = newProject(); + runCLI(`generate @nrwl/react:app ${myApp}`); + runCLI(`generate @nrwl/vite:init`); + updateFile( + `apps/${myApp}/index.html`, + ` @@ -38,15 +39,15 @@ describe('Vite Plugin', () => {
- + ` - ); + ); - createFile( - `apps/${myApp}/vite.config.ts`, - ` + createFile( + `apps/${myApp}/vite.config.ts`, + ` /// import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; @@ -65,11 +66,11 @@ describe('Vite Plugin', () => { environment: 'jsdom', } });` - ); + ); - updateFile( - `apps/${myApp}/tsconfig.json`, - ` + updateFile( + `apps/${myApp}/tsconfig.json`, + ` { "extends": "../../tsconfig.base.json", "compilerOptions": { @@ -102,56 +103,130 @@ describe('Vite Plugin', () => { ] } ` - ); + ); - updateProjectConfig(myApp, (config) => { - config.targets.build.executor = '@nrwl/vite:build'; - config.targets.serve.executor = '@nrwl/vite:dev-server'; - config.targets.test.executor = '@nrwl/vite:test'; + updateProjectConfig(myApp, (config) => { + config.targets.build.executor = '@nrwl/vite:build'; + config.targets.serve.executor = '@nrwl/vite:dev-server'; + config.targets.test.executor = '@nrwl/vite:test'; - config.targets.build.options = { - outputPath: `dist/apps/${myApp}`, - }; + config.targets.build.options = { + outputPath: `dist/apps/${myApp}`, + }; - config.targets.serve.options = { - buildTarget: `${myApp}:build`, - }; + config.targets.serve.options = { + buildTarget: `${myApp}:build`, + }; - config.targets.serve.options = { - config: `apps/${myApp}/vite.config.ts`, - }; + config.targets.serve.options = { + config: `apps/${myApp}/vite.config.ts`, + }; - return config; + return config; + }); + }); + afterEach(() => cleanupProject()); + + it('should build applications', async () => { + runCLI(`build ${myApp}`); + expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined(); + rmDist(); + }, 200000); + + it('should serve applications in dev mode', async () => { + const port = 4212; + const p = await runCommandUntil( + `run ${myApp}:serve --port=${port}`, + (output) => { + return output.includes('Local:'); + } + ); + try { + await promisifiedTreeKill(p.pid, 'SIGKILL'); + await killPorts(port); + } catch { + // ignore + } + }, 200000); + + it('should test applications', async () => { + const result = await runCLIAsync(`test ${myApp}`); + expect(result.combinedOutput).toContain( + `Successfully ran target test for project ${myApp}` + ); }); }); - afterEach(() => cleanupProject()); - - it('should build applications', async () => { - runCLI(`build ${myApp}`); - expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined(); - rmDist(); - }, 200000); - - it('should serve applications in dev mode', async () => { - const port = 4212; - const p = await runCommandUntil( - `run ${myApp}:serve --port=${port}`, - (output) => { - return output.includes('Local:'); + + describe('set up new React project with --bundler=vite option', () => { + beforeEach(() => { + proj = newProject(); + runCLI(`generate @nrwl/react:app ${myApp} --bundler=vite`); + }); + afterEach(() => cleanupProject()); + it('should build applications', async () => { + runCLI(`build ${myApp}`); + expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined(); + rmDist(); + }, 200000); + + it('should serve applications in dev mode', async () => { + const port = 4212; + const p = await runCommandUntil( + `run ${myApp}:serve --port=${port}`, + (output) => { + return output.includes('Local:'); + } + ); + try { + await promisifiedTreeKill(p.pid, 'SIGKILL'); + await killPorts(port); + } catch { + // ignore } - ); - try { - await promisifiedTreeKill(p.pid, 'SIGKILL'); - await killPorts(port); - } catch { - // ignore - } - }, 200000); - - it('should test applications', async () => { - const result = await runCLIAsync(`test ${myApp}`); - expect(result.combinedOutput).toContain( - `Successfully ran target test for project ${myApp}` - ); + }, 200000); + + it('should test applications', async () => { + const result = await runCLIAsync(`test ${myApp}`); + expect(result.combinedOutput).toContain( + `Successfully ran target test for project ${myApp}` + ); + }); + }); + + describe('convert React webpack project to vite using the vite:configuration generator', () => { + beforeEach(() => { + proj = newProject(); + runCLI(`generate @nrwl/react:app ${myApp} --bundler=webpack`); + runCLI(`generate @nrwl/vite:configuration ${myApp}`); + }); + afterEach(() => cleanupProject()); + it('should build applications', async () => { + runCLI(`build ${myApp}`); + expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined(); + rmDist(); + }, 200000); + + it('should serve applications in dev mode', async () => { + const port = 4212; + const p = await runCommandUntil( + `run ${myApp}:serve --port=${port}`, + (output) => { + return output.includes('Local:'); + } + ); + try { + await promisifiedTreeKill(p.pid, 'SIGKILL'); + await killPorts(port); + } catch { + // ignore + } + }, 200000); + + it('should test applications', async () => { + const result = await runCLIAsync(`test ${myApp}`); + expect(result.combinedOutput).toContain( + `Successfully ran target test for project ${myApp}` + ); + }); }); }); diff --git a/e2e/web/src/web-vite.test.ts b/e2e/web/src/web-vite.test.ts new file mode 100644 index 0000000000000..3bd8a3bc45d65 --- /dev/null +++ b/e2e/web/src/web-vite.test.ts @@ -0,0 +1,94 @@ +import { + checkFilesDoNotExist, + checkFilesExist, + cleanupProject, + createFile, + isNotWindows, + killPorts, + newProject, + removeFile, + runCLI, + runCLIAsync, + runCypressTests, + uniq, +} from '@nrwl/e2e/utils'; + +describe('Web Components Applications with bundler set as vite', () => { + beforeEach(() => newProject()); + afterEach(() => cleanupProject()); + + it('should be able to generate a web app', async () => { + const appName = uniq('app'); + runCLI(`generate @nrwl/web:app ${appName} --bundler=vite --no-interactive`); + + const lintResults = runCLI(`lint ${appName}`); + expect(lintResults).toContain('All files pass linting.'); + + runCLI(`build ${appName}`); + checkFilesExist(`dist/apps/${appName}/index.html`); + + const testResults = await runCLIAsync(`test ${appName}`); + + expect(testResults.combinedOutput).toContain( + 'Test Suites: 1 passed, 1 total' + ); + + const lintE2eResults = runCLI(`lint ${appName}-e2e`); + + expect(lintE2eResults).toContain('All files pass linting.'); + + if (isNotWindows() && runCypressTests()) { + const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`); + expect(e2eResults).toContain('All specs passed!'); + expect(await killPorts()).toBeTruthy(); + } + }, 500000); + + it('should be able to generate a web app with standaloneConfig', async () => { + const appName = uniq('app'); + runCLI( + `generate @nrwl/web:app ${appName} --bundler=vite --no-interactive --standalone-config` + ); + + checkFilesExist(`apps/${appName}/project.json`); + + const lintResults = runCLI(`lint ${appName}`); + expect(lintResults).toContain('All files pass linting.'); + }, 120000); + + it('should remove previous output before building', async () => { + const appName = uniq('app'); + const libName = uniq('lib'); + + runCLI(`generate @nrwl/web:app ${appName} --bundler=vite --no-interactive`); + runCLI( + `generate @nrwl/react:lib ${libName} --buildable --no-interactive --compiler swc` + ); + + createFile(`dist/apps/${appName}/_should_remove.txt`); + createFile(`dist/libs/${libName}/_should_remove.txt`); + createFile(`dist/apps/_should_not_remove.txt`); + checkFilesExist( + `dist/apps/${appName}/_should_remove.txt`, + `dist/apps/_should_not_remove.txt` + ); + runCLI(`build ${appName}`); + runCLI(`build ${libName}`); + checkFilesDoNotExist( + `dist/apps/${appName}/_should_remove.txt`, + `dist/libs/${libName}/_should_remove.txt` + ); + checkFilesExist(`dist/apps/_should_not_remove.txt`); + }, 120000); + + it('should support workspaces w/o workspace config file', async () => { + removeFile('workspace.json'); + const myapp = uniq('myapp'); + runCLI(`generate @nrwl/web:app ${myapp} --bundler=vite --directory=myDir`); + + runCLI(`build my-dir-${myapp}`); + expect(() => + checkFilesDoNotExist('workspace.json', 'angular.json') + ).not.toThrow(); + }, 1000000); +}); diff --git a/e2e/web/src/web.test.ts b/e2e/web/src/web.test.ts index cd66dffd9a0e5..ea958f95e7d87 100644 --- a/e2e/web/src/web.test.ts +++ b/e2e/web/src/web.test.ts @@ -23,7 +23,9 @@ describe('Web Components Applications', () => { it('should be able to generate a web app', async () => { const appName = uniq('app'); - runCLI(`generate @nrwl/web:app ${appName} --no-interactive`); + runCLI( + `generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive` + ); const lintResults = runCLI(`lint ${appName}`); expect(lintResults).toContain('All files pass linting.'); @@ -61,7 +63,7 @@ describe('Web Components Applications', () => { it('should be able to generate a web app with standaloneConfig', async () => { const appName = uniq('app'); runCLI( - `generate @nrwl/web:app ${appName} --no-interactive --standalone-config` + `generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive --standalone-config` ); checkFilesExist(`apps/${appName}/project.json`); @@ -74,7 +76,9 @@ describe('Web Components Applications', () => { const appName = uniq('app'); const libName = uniq('lib'); - runCLI(`generate @nrwl/web:app ${appName} --no-interactive --compiler swc`); + runCLI( + `generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive --compiler swc` + ); runCLI( `generate @nrwl/react:lib ${libName} --buildable --no-interactive --compiler swc` ); @@ -112,7 +116,9 @@ describe('Web Components Applications', () => { it('should do another build if differential loading is needed', async () => { const appName = uniq('app'); - runCLI(`generate @nrwl/web:app ${appName} --no-interactive`); + runCLI( + `generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive` + ); updateFile(`apps/${appName}/browserslist`, `IE 9-11`); @@ -126,7 +132,9 @@ describe('Web Components Applications', () => { it('should emit decorator metadata when it is enabled in tsconfig', async () => { const appName = uniq('app'); - runCLI(`generate @nrwl/web:app ${appName} --no-interactive`); + runCLI( + `generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive` + ); updateFile(`apps/${appName}/src/app/app.element.ts`, (content) => { const newContent = `${content} @@ -180,7 +188,9 @@ describe('Web Components Applications', () => { it('should support workspaces w/o workspace config file', async () => { removeFile('workspace.json'); const myapp = uniq('myapp'); - runCLI(`generate @nrwl/web:app ${myapp} --directory=myDir`); + runCLI( + `generate @nrwl/web:app ${myapp} --bundler=webpack --directory=myDir` + ); runCLI(`build my-dir-${myapp}`); expect(() => @@ -190,7 +200,9 @@ describe('Web Components Applications', () => { it('should support custom webpackConfig option', async () => { const appName = uniq('app'); - runCLI(`generate @nrwl/web:app ${appName} --no-interactive`); + runCLI( + `generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive` + ); updateProjectConfig(appName, (config) => { config.targets.build.options.webpackConfig = `apps/${appName}/webpack.config.js`; @@ -284,7 +296,9 @@ describe('CLI - Environment Variables', () => { const nxSharedEnv = process.env.NX_SHARED_ENV; `; - runCLI(`generate @nrwl/web:app ${appName} --no-interactive`); + runCLI( + `generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive` + ); const content = readFile(main); @@ -307,7 +321,9 @@ describe('CLI - Environment Variables', () => { const main2 = `apps/${appName2}/src/main.ts`; const newCode2 = `const envVars = [process.env.NODE_ENV, process.env.NX_BUILD, process.env.NX_API, process.env.NX_WS_BASE, process.env.NX_WS_ENV_LOCAL, process.env.NX_WS_LOCAL_ENV, process.env.NX_APP_BASE, process.env.NX_APP_ENV_LOCAL, process.env.NX_APP_LOCAL_ENV, process.env.NX_SHARED_ENV];`; - runCLI(`generate @nrwl/web:app ${appName2} --no-interactive`); + runCLI( + `generate @nrwl/web:app ${appName2} --bundler=webpack --no-interactive` + ); const content2 = readFile(main2); @@ -336,7 +352,9 @@ describe('Build Options', () => { const appName = uniq('app'); - runCLI(`generate @nrwl/web:app ${appName} --no-interactive`); + runCLI( + `generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive` + ); const srcPath = `apps/${appName}/src`; const fooCss = `${srcPath}/foo.css`; @@ -413,7 +431,9 @@ describe('index.html interpolation', () => { test('should interpolate environment variables', () => { const appName = uniq('app'); - runCLI(`generate @nrwl/web:app ${appName} --no-interactive`); + runCLI( + `generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive` + ); const srcPath = `apps/${appName}/src`; const indexPath = `${srcPath}/index.html`; diff --git a/packages/react/docs/application-examples.md b/packages/react/docs/application-examples.md new file mode 100644 index 0000000000000..b3314804eadd6 --- /dev/null +++ b/packages/react/docs/application-examples.md @@ -0,0 +1,43 @@ +## Examples + +{% tabs %} +{% tab label="Simple Application" %} + +Create an application named `my-app`: + +```bash +nx g @nrwl/react:application my-app +``` + +{% /tab %} + +{% tab label="Application using Vite as bundler" %} + +Create an application named `my-app`: + +```bash +nx g @nrwl/react:app my-app --bundler=vite +``` + +{% /tab %} + +{% tab label="Specify directory and style extension" %} + +Create an application named `my-app` in the `my-dir` directory and use `scss` for styles: + +```bash +nx g @nrwl/react:app my-app --directory=my-dir --style=scss +``` + +{% /tab %} + +{% tab label="Add tags" %} + +Add tags to the application (used for linting). + +```bash +nx g @nrwl/react:app my-app --tags=scope:admin,type:ui +``` + +{% /tab %} +{% /tabs %} diff --git a/packages/react/package.json b/packages/react/package.json index f302646592a64..60c574f5e4dd8 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -39,6 +39,7 @@ "@nrwl/js": "file:../js", "@nrwl/linter": "file:../linter", "@nrwl/storybook": "file:../storybook", + "@nrwl/vite": "file:../vite", "@nrwl/web": "file:../web", "@nrwl/webpack": "file:../webpack", "@nrwl/workspace": "file:../workspace", diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts index 01e1b8d2e9818..8c612c41d1736 100644 --- a/packages/react/src/generators/application/application.spec.ts +++ b/packages/react/src/generators/application/application.spec.ts @@ -328,7 +328,11 @@ describe('app', () => { }); it('should setup the nrwl web build builder', async () => { - await applicationGenerator(appTree, { ...schema, name: 'my-app' }); + await applicationGenerator(appTree, { + ...schema, + name: 'my-app', + bundler: 'webpack', + }); const workspaceJson = getProjects(appTree); const targetConfig = workspaceJson.get('my-app').targets; @@ -363,8 +367,28 @@ describe('app', () => { }); }); + it('should setup the nrwl vite builder if bundler is vite', async () => { + await applicationGenerator(appTree, { + ...schema, + name: 'my-app', + bundler: 'vite', + }); + + const workspaceJson = getProjects(appTree); + const targetConfig = workspaceJson.get('my-app').targets; + expect(targetConfig.build.executor).toEqual('@nrwl/vite:build'); + expect(targetConfig.build.outputs).toEqual(['{options.outputPath}']); + expect(targetConfig.build.options).toEqual({ + outputPath: 'dist/apps/my-app', + }); + }); + it('should setup the nrwl web dev server builder', async () => { - await applicationGenerator(appTree, { ...schema, name: 'my-app' }); + await applicationGenerator(appTree, { + ...schema, + name: 'my-app', + bundler: 'webpack', + }); const workspaceJson = getProjects(appTree); const targetConfig = workspaceJson.get('my-app').targets; @@ -379,6 +403,25 @@ describe('app', () => { }); }); + it('should setup the nrwl vite dev server builder if bundler is vite', async () => { + await applicationGenerator(appTree, { + ...schema, + name: 'my-app', + bundler: 'vite', + }); + + const workspaceJson = getProjects(appTree); + const targetConfig = workspaceJson.get('my-app').targets; + expect(targetConfig.serve.executor).toEqual('@nrwl/vite:dev-server'); + expect(targetConfig.serve.options).toEqual({ + buildTarget: 'my-app:build', + }); + expect(targetConfig.serve.configurations.production).toEqual({ + buildTarget: 'my-app:build:production', + hmr: false, + }); + }); + it('should setup the eslint builder', async () => { await applicationGenerator(appTree, { ...schema, name: 'my-app' }); @@ -584,7 +627,11 @@ describe('app', () => { }); it('should exclude styles from workspace.json', async () => { - await applicationGenerator(appTree, { ...schema, style: 'none' }); + await applicationGenerator(appTree, { + ...schema, + style: 'none', + bundler: 'webpack', + }); const workspaceJson = getProjects(appTree); @@ -592,6 +639,20 @@ describe('app', () => { [] ); }); + + it('should not break if bundler is vite', async () => { + await applicationGenerator(appTree, { + ...schema, + style: 'none', + bundler: 'vite', + }); + + const workspaceJson = getProjects(appTree); + + expect( + workspaceJson.get('my-app').targets.build.options.styles + ).toBeUndefined(); + }); }); describe('--style styled-components', () => { @@ -658,6 +719,7 @@ describe('app', () => { await applicationGenerator(appTree, { ...schema, style: '@emotion/styled', + bundler: 'webpack', }); const workspaceJson = getProjects(appTree); @@ -667,6 +729,20 @@ describe('app', () => { ); }); + it('should not break if bundler is vite', async () => { + await applicationGenerator(appTree, { + ...schema, + style: '@emotion/styled', + bundler: 'vite', + }); + + const workspaceJson = getProjects(appTree); + + expect( + workspaceJson.get('my-app').targets.build.options.styles + ).toBeUndefined(); + }); + it('should add dependencies to package.json', async () => { await applicationGenerator(appTree, { ...schema, @@ -735,9 +811,10 @@ describe('app', () => { }); }); - it('should adds custom webpack config', async () => { + it('should add custom webpack config', async () => { await applicationGenerator(appTree, { ...schema, + bundler: 'webpack', }); const workspaceJson = getProjects(appTree); @@ -747,6 +824,19 @@ describe('app', () => { ).toEqual('@nrwl/react/plugins/webpack'); }); + it('should NOT add custom webpack config if bundler is vite', async () => { + await applicationGenerator(appTree, { + ...schema, + bundler: 'vite', + }); + + const workspaceJson = getProjects(appTree); + + expect( + workspaceJson.get('my-app').targets.build.options.webpackConfig + ).toBeUndefined(); + }); + it('should add required polyfills for core-js and regenerator', async () => { await applicationGenerator(appTree, { ...schema, @@ -839,6 +929,7 @@ describe('app', () => { await applicationGenerator(appTree, { ...schema, rootProject: true, + bundler: 'webpack', }); expect(appTree.read('/src/main.tsx')).toBeDefined(); expect(appTree.read('/e2e/cypress.config.ts')).toBeDefined(); @@ -851,5 +942,60 @@ describe('app', () => { ].options['outputPath'] ).toEqual('dist/my-app'); }); + + it('should create files at the root if bundler is vite', async () => { + await applicationGenerator(appTree, { + ...schema, + name: 'my-app2', + rootProject: true, + bundler: 'vite', + }); + expect(appTree.read('/src/main.tsx')).toBeDefined(); + expect(appTree.read('/e2e/cypress.config.ts')).toBeDefined(); + expect(readJson(appTree, '/tsconfig.json').extends).toEqual( + './tsconfig.base.json' + ); + expect( + readJson(appTree, '/workspace.json').projects['my-app2'].architect[ + 'build' + ].options['outputPath'] + ).toEqual('dist/my-app2'); + }); + }); + + describe('setup React app with --bundler=vite', () => { + let viteAppTree: Tree; + beforeAll(async () => { + viteAppTree = createTreeWithEmptyV1Workspace(); + await applicationGenerator(viteAppTree, { ...schema, bundler: 'vite' }); + }); + + it('should setup targets with vite configuration', () => { + const workspaceJson = getProjects(viteAppTree); + const targetConfig = workspaceJson.get('my-app').targets; + expect(targetConfig.build.executor).toEqual('@nrwl/vite:build'); + expect(targetConfig.serve.executor).toEqual('@nrwl/vite:dev-server'); + expect(targetConfig.serve.options).toEqual({ + buildTarget: 'my-app:build', + }); + }); + it('should add dependencies in package.json', () => { + const packageJson = readJson(viteAppTree, '/package.json'); + + expect(packageJson.devDependencies).toMatchObject({ + vite: expect.any(String), + '@vitejs/plugin-react': expect.any(String), + }); + }); + + it('should create correct tsconfig compilerOptions', () => { + const tsconfigJson = readJson(viteAppTree, '/apps/my-app/tsconfig.json'); + expect(tsconfigJson.compilerOptions.types).toMatchObject(['vite/client']); + }); + + it('should create index.html and vite.config file at the root of the app', () => { + expect(viteAppTree.exists('/apps/my-app/index.html')).toBe(true); + expect(viteAppTree.exists('/apps/my-app/vite.config.ts')).toBe(true); + }); }); }); diff --git a/packages/react/src/generators/application/application.ts b/packages/react/src/generators/application/application.ts index d1db68915d172..fad877b60a629 100644 --- a/packages/react/src/generators/application/application.ts +++ b/packages/react/src/generators/application/application.ts @@ -26,6 +26,7 @@ import reactInitGenerator from '../init/init'; import { Linter, lintProjectGenerator } from '@nrwl/linter'; import { swcCoreVersion } from '@nrwl/js/src/utils/versions'; import { swcLoaderVersion } from '@nrwl/webpack/src/utils/versions'; +import { viteConfigurationGenerator } from '@nrwl/vite'; async function addLinting(host: Tree, options: NormalizedSchema) { const tasks: GeneratorCallback[] = []; @@ -69,6 +70,8 @@ async function addLinting(host: Tree, options: NormalizedSchema) { } export async function applicationGenerator(host: Tree, schema: Schema) { + const tasks = []; + const options = normalizeOptions(host, schema); const initTask = await reactInitGenerator(host, { @@ -76,28 +79,39 @@ export async function applicationGenerator(host: Tree, schema: Schema) { skipFormat: true, }); + tasks.push(initTask); + createApplicationFiles(host, options); addProject(host, options); + + if (options.bundler === 'vite') { + const viteTask = await viteConfigurationGenerator(host, { + uiFramework: 'react', + project: options.projectName, + newProject: true, + }); + tasks.push(viteTask); + } + const lintTask = await addLinting(host, options); + tasks.push(lintTask); + const cypressTask = await addCypress(host, options); + tasks.push(cypressTask); const jestTask = await addJest(host, options); + tasks.push(jestTask); updateJestConfig(host, options); const styledTask = addStyledModuleDependencies(host, options.styledModule); + tasks.push(styledTask); const routingTask = addRouting(host, options); + tasks.push(routingTask); setDefaults(host, options); if (!options.skipFormat) { await formatFiles(host); } - return runTasksInSerial( - initTask, - lintTask, - cypressTask, - jestTask, - styledTask, - routingTask - ); + return runTasksInSerial(...tasks); } export default applicationGenerator; diff --git a/packages/react/src/generators/application/files/common-vite/index.html b/packages/react/src/generators/application/files/common-vite/index.html new file mode 100644 index 0000000000000..3f7a9aca4fa93 --- /dev/null +++ b/packages/react/src/generators/application/files/common-vite/index.html @@ -0,0 +1,15 @@ + + + + + <%= className %> + + + + + + +
+ + + diff --git a/packages/react/src/generators/application/files/common-vite/public/.gitkeep b/packages/react/src/generators/application/files/common-vite/public/.gitkeep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/react/src/generators/application/files/common-vite/src/app/__fileName__.spec.tsx__tmpl__ b/packages/react/src/generators/application/files/common-vite/src/app/__fileName__.spec.tsx__tmpl__ new file mode 100644 index 0000000000000..86f0708d4445b --- /dev/null +++ b/packages/react/src/generators/application/files/common-vite/src/app/__fileName__.spec.tsx__tmpl__ @@ -0,0 +1,26 @@ +import { render } from '@testing-library/react'; +<% if (routing) { %> +import { BrowserRouter } from 'react-router-dom'; +<% } %> + +import App from './<%= fileName %>'; + +describe('App', () => { + it('should render successfully', () => { + <% if (routing) { %> + const { baseElement } = render(); + <% } else { %> + const { baseElement } = render(); + <% } %> + expect(baseElement).toBeTruthy(); + }); + + it('should have a greeting as the title', () => { + <% if (routing) { %> + const { getByText } = render(); + <% } else { %> + const { getByText } = render(); + <% } %> + expect(getByText(/Welcome <%= projectName %>/gi)).toBeTruthy(); + }); +}); diff --git a/packages/react/src/generators/application/files/common-vite/src/app/nx-welcome.tsx b/packages/react/src/generators/application/files/common-vite/src/app/nx-welcome.tsx new file mode 100644 index 0000000000000..3c1a440fef714 --- /dev/null +++ b/packages/react/src/generators/application/files/common-vite/src/app/nx-welcome.tsx @@ -0,0 +1,820 @@ +/* + * * * * * * * * * * * * * * * * * * * * * * * * * * * * + This is a starter component and can be deleted. + * * * * * * * * * * * * * * * * * * * * * * * * * * * * + Delete this file and get started with your project! + * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ +export function NxWelcome({ title }: { title: string }) { + return ( + <> +