Compare commits
12 Commits
docs/tutorial
...
17.2.2
| Author | SHA1 | Date | |
|---|---|---|---|
| d0173c49fa | |||
| 718e730783 | |||
| 55327f79e4 | |||
| f61667509f | |||
| 4dc3c8f462 | |||
| d4ff686c10 | |||
| 214907af2a | |||
| 57543dba1d | |||
| 2ea07b9be9 | |||
| 8d4d5754eb | |||
| fb4e280993 | |||
| 20fe50b75b |
@@ -1943,9 +1943,9 @@
|
||||
"isExternal": false,
|
||||
"children": [
|
||||
{
|
||||
"name": "Manually set up your project to use Vite.js",
|
||||
"path": "/recipes/vite/set-up-vite-manually",
|
||||
"id": "set-up-vite-manually",
|
||||
"name": "Configure Vite on your Nx workspace",
|
||||
"path": "/recipes/vite/configure-vite",
|
||||
"id": "configure-vite",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
@@ -3382,9 +3382,9 @@
|
||||
"isExternal": false,
|
||||
"children": [
|
||||
{
|
||||
"name": "Manually set up your project to use Vite.js",
|
||||
"path": "/recipes/vite/set-up-vite-manually",
|
||||
"id": "set-up-vite-manually",
|
||||
"name": "Configure Vite on your Nx workspace",
|
||||
"path": "/recipes/vite/configure-vite",
|
||||
"id": "configure-vite",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
@@ -3393,9 +3393,9 @@
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Manually set up your project to use Vite.js",
|
||||
"path": "/recipes/vite/set-up-vite-manually",
|
||||
"id": "set-up-vite-manually",
|
||||
"name": "Configure Vite on your Nx workspace",
|
||||
"path": "/recipes/vite/configure-vite",
|
||||
"id": "configure-vite",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
|
||||
@@ -2661,14 +2661,14 @@
|
||||
"file": "",
|
||||
"itemList": [
|
||||
{
|
||||
"id": "set-up-vite-manually",
|
||||
"name": "Manually set up your project to use Vite.js",
|
||||
"description": "Manually set up your project to use Vite.js",
|
||||
"id": "configure-vite",
|
||||
"name": "Configure Vite on your Nx workspace",
|
||||
"description": "Configure Vite on your Nx workspace",
|
||||
"mediaImage": "",
|
||||
"file": "shared/packages/vite/set-up-vite-manually",
|
||||
"file": "shared/packages/vite/configure-vite",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/vite/set-up-vite-manually",
|
||||
"path": "/recipes/vite/configure-vite",
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
@@ -4633,14 +4633,14 @@
|
||||
"file": "",
|
||||
"itemList": [
|
||||
{
|
||||
"id": "set-up-vite-manually",
|
||||
"name": "Manually set up your project to use Vite.js",
|
||||
"description": "Manually set up your project to use Vite.js",
|
||||
"id": "configure-vite",
|
||||
"name": "Configure Vite on your Nx workspace",
|
||||
"description": "Configure Vite on your Nx workspace",
|
||||
"mediaImage": "",
|
||||
"file": "shared/packages/vite/set-up-vite-manually",
|
||||
"file": "shared/packages/vite/configure-vite",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/vite/set-up-vite-manually",
|
||||
"path": "/recipes/vite/configure-vite",
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
@@ -4648,15 +4648,15 @@
|
||||
"path": "/recipes/vite",
|
||||
"tags": []
|
||||
},
|
||||
"/recipes/vite/set-up-vite-manually": {
|
||||
"id": "set-up-vite-manually",
|
||||
"name": "Manually set up your project to use Vite.js",
|
||||
"description": "Manually set up your project to use Vite.js",
|
||||
"/recipes/vite/configure-vite": {
|
||||
"id": "configure-vite",
|
||||
"name": "Configure Vite on your Nx workspace",
|
||||
"description": "Configure Vite on your Nx workspace",
|
||||
"mediaImage": "",
|
||||
"file": "shared/packages/vite/set-up-vite-manually",
|
||||
"file": "shared/packages/vite/configure-vite",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/vite/set-up-vite-manually",
|
||||
"path": "/recipes/vite/configure-vite",
|
||||
"tags": []
|
||||
},
|
||||
"/recipes/webpack": {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -313,6 +313,11 @@
|
||||
"default": true,
|
||||
"x-deprecated": "Automatic configuration of Webpack is deprecated in favor of an explicit 'webpack.config.js' file. This option will be removed in Nx 18. See https://nx.dev/recipes/webpack/webpack-config-setup."
|
||||
},
|
||||
"standardWebpackConfigFunction": {
|
||||
"type": "boolean",
|
||||
"description": "Set to true if the webpack config exports a standard webpack function, not an Nx-specific one. See: https://webpack.js.org/configuration/configuration-types/#exporting-a-function",
|
||||
"default": false
|
||||
},
|
||||
"extractLicenses": {
|
||||
"type": "boolean",
|
||||
"description": "Extract all licenses in a separate file, in the case of production builds only."
|
||||
|
||||
+4
-4
@@ -848,10 +848,10 @@
|
||||
"description": "Vite related recipes",
|
||||
"itemList": [
|
||||
{
|
||||
"id": "set-up-vite-manually",
|
||||
"name": "Manually set up your project to use Vite.js",
|
||||
"description": "Manually set up your project to use Vite.js",
|
||||
"file": "shared/packages/vite/set-up-vite-manually"
|
||||
"id": "configure-vite",
|
||||
"name": "Configure Vite on your Nx workspace",
|
||||
"description": "Configure Vite on your Nx workspace",
|
||||
"file": "shared/packages/vite/configure-vite"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
---
|
||||
title: Configure Vite on your Nx workspace
|
||||
description: This guide explains how you can configure Vite in your Nx workspace.
|
||||
---
|
||||
|
||||
# Configure Vite on your Nx workspace
|
||||
|
||||
{% callout type="note" title="Use our generator!" %}
|
||||
It is recommended that you use the [`@nx/vite:configuration`](/nx-api/vite/generators/configuration) generator to set up [Vite](https://vitejs.dev/) for your new or existing projects.
|
||||
{% /callout %}
|
||||
|
||||
The `@nx/vite` plugin generators take care of configuring Vite for you. However, you may need to set up Vite manually in some cases. This guide explains how you can configure Vite in your Nx workspace.
|
||||
|
||||
## TypeScript paths
|
||||
|
||||
You need to use the `nxViteTsPaths()` plugin to make sure that your TypeScript paths are resolved correctly in your monorepo. This
|
||||
|
||||
## Framework plugins
|
||||
|
||||
If you are using React, you need to use the [`@vitejs/plugin-react` plugin](https://www.npmjs.com/package/@vitejs/plugin-react). If you're using Vue, you need to use the [`@vitejs/plugin-vue` plugin](https://www.npmjs.com/package/@vitejs/plugin-vue).
|
||||
|
||||
## Set the `root` path
|
||||
|
||||
Make sure to set the `root: __dirname,` property on your config object. This is necessary to make sure that the paths are resolved correctly in your monorepo.
|
||||
|
||||
## Set the build `outDir` path
|
||||
|
||||
Make sure you set the `outDir` property on your `build` object. Set the path as relative to the workspace root, so for example if your project is located in `apps/my-app`, set the `outDir` to `../../dist/apps/my-app`. If your project is located in `my-app`, set the `outDir` to `../dist/my-app`, etc.
|
||||
|
||||
## DTS plugin
|
||||
|
||||
If you are building a library, you need to use the [`vite-plugin-dts` plugin](https://www.npmjs.com/package/vite-plugin-dts) to generate the `.d.ts` files for your library.
|
||||
|
||||
### Skip diagnostics
|
||||
|
||||
If you are building a library, you can set the `skipDiagnostics` option to `true` to speed up the build. This means that type diagnostic will be skipped during the build process. However, if there are some files with type errors which interrupt the build process, these files will not be emitted and `.d.ts` declaration files will not be generated.
|
||||
|
||||
If you choose to skip diagnostics, here is what your `'vite-plugin-dts'` plugin setup will look like:
|
||||
|
||||
```ts {% fileName="libs/my-lib/vite.config.ts" %}
|
||||
...
|
||||
import dts from 'vite-plugin-dts';
|
||||
import { join } from 'path';
|
||||
...
|
||||
...
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
...,
|
||||
dts({
|
||||
entryRoot: 'src',
|
||||
tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
|
||||
skipDiagnostics: true,
|
||||
}),
|
||||
```
|
||||
|
||||
### Do not skip diagnostics
|
||||
|
||||
If you are building a library, and you want to make sure that all the files are type checked, you can set the `skipDiagnostics` option to `false` to make sure that all the files are type checked. This means that type diagnostic will be run during the build process.
|
||||
|
||||
If you choose to enable diagnostics, here is what your `'vite-plugin-dts'` plugin setup will look like:
|
||||
|
||||
```ts {% fileName="libs/my-lib/vite.config.ts" %}
|
||||
...
|
||||
import dts from 'vite-plugin-dts';
|
||||
...
|
||||
...
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
...,
|
||||
dts({
|
||||
root: '../../',
|
||||
entryRoot: 'libs/my-lib/src',
|
||||
tsConfigFilePath: 'libs/my-lib/tsconfig.lib.json',
|
||||
include: ['libs/my-lib/src/**/*.ts'],
|
||||
outputDir: 'dist/libs/my-lib',
|
||||
skipDiagnostics: false,
|
||||
}),
|
||||
```
|
||||
|
||||
You can read more about the configuration options in the [`vite-plugin-dts` plugin documentation](https://www.npmjs.com/package/vite-plugin-dts).
|
||||
|
||||
## For testing
|
||||
|
||||
If you're using `vitest`, make sure your `test` object in your `vite.config.ts` file looks like this:
|
||||
|
||||
```ts
|
||||
...
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../coverage/<project-root>',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
Note how we're specifying `reporters` and `environment`.
|
||||
|
||||
## How your `vite.config.ts` looks like
|
||||
|
||||
### For applications
|
||||
|
||||
Add a `vite.config.ts` file to the root of your project. If you are not using React, you can skip adding the `react` plugin, of course.
|
||||
|
||||
```ts {% fileName="apps/my-app/vite.config.ts" %}
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
build: {
|
||||
outDir: '../../dist/apps/my-app',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
cacheDir: '../../node_modules/.vite/my-app',
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths()],
|
||||
|
||||
test: {
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/my-app',
|
||||
provider: 'v8',
|
||||
},
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### For libraries
|
||||
|
||||
If you are setting up a library (rather than an application) to use Vite, your `vite.config.ts` file should look like this:
|
||||
|
||||
```ts {% fileName="libs/my-lib/vite.config.ts" %}
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../node_modules/.vite/my-lib',
|
||||
plugins: [
|
||||
react(),
|
||||
nxViteTsPaths(),
|
||||
dts({
|
||||
entryRoot: 'src',
|
||||
tsConfigFilePath: path.join(__dirname, 'tsconfig.lib.json'),
|
||||
skipDiagnostics: true,
|
||||
}),
|
||||
],
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: '../dist/my-lib',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: 'my-lib',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es', 'cjs'],
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react', 'react-dom', 'react/jsx-runtime'],
|
||||
},
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../coverage/my-lib',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
In that config file, you can configure Vite as you would normally do. For more information, see the [Vite.js documentation](https://vitejs.dev/config/).
|
||||
@@ -1,336 +0,0 @@
|
||||
---
|
||||
title: Manually set up your project to use Vite.js
|
||||
description: This guide explains how you can manually set up your project to use Vite.js in your Nx workspace.
|
||||
---
|
||||
|
||||
# Manually set up your project to use Vite.js
|
||||
|
||||
{% callout type="note" title="Use our generator!" %}
|
||||
It is recommended that you use the [`@nx/vite:configuration`](/nx-api/vite/generators/configuration) generator to do convert an existing project to use [Vite](https://vitejs.dev/).
|
||||
{% /callout %}
|
||||
|
||||
You can use the `@nx/vite:dev-server`,`@nx/vite:build` and `@nx/vite:test` executors to serve, build and test your project using [Vite](https://vitejs.dev/) and [Vitest](https://vitest.dev/). To do this, you need to make a few adjustments to your project. It is recommended that you use the [`@nx/vite:configuration`](/nx-api/vite/generators/configuration) generator to do this, but you can also do it manually.
|
||||
|
||||
A reason you may need to do this manually, is if our generator does not support conversion for your project, or if you want to experiment with custom options.
|
||||
|
||||
The list of steps below assumes that your project can be converted to use the `@nx/vite` executors. However, if it's not supported by the [`@nx/vite:configuration`](/nx-api/vite/generators/configuration) generator, it's likely that your project will not work as expected when converted. So, proceed with caution and always commit your code before making any changes.
|
||||
|
||||
## 1. Change the executors in your `project.json`
|
||||
|
||||
### The `serve` target
|
||||
|
||||
This applies to applications, not libraries.
|
||||
|
||||
In your app's `project.json` file, change the executor of your `serve` target to use `@nx/vite:dev-server` and set it up with the following options:
|
||||
|
||||
```json
|
||||
//...
|
||||
"my-app": {
|
||||
"targets": {
|
||||
//...
|
||||
"serve": {
|
||||
"executor": "@nx/vite:dev-server",
|
||||
"defaultConfiguration": "development",
|
||||
"options": {
|
||||
"buildTarget": "my-app:build",
|
||||
},
|
||||
"configurations": {
|
||||
...
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% callout type="note" title="Other options" %}
|
||||
Any extra options that you may need to add to your server's configuration can be added in your project's `vite.config.ts` file. You can find all the options that are supported in the [Vite.js documentation](https://vitejs.dev/config/). You can see which of these options you can add in your `project.json` in the [`@nx/vite:dev-server`](/nx-api/vite/executors/dev-server#options) documentation.
|
||||
{% /callout %}
|
||||
|
||||
### The `build` target
|
||||
|
||||
In your project's `project.json` file, change the executor of your `build` target to use `@nx/vite:build` and set it up with the following options:
|
||||
|
||||
```json
|
||||
//...
|
||||
"my-app": {
|
||||
"targets": {
|
||||
//...
|
||||
"build": {
|
||||
"executor": "@nx/vite:build",
|
||||
...
|
||||
"options": {
|
||||
"outputPath": "dist/apps/my-app"
|
||||
},
|
||||
"configurations": {
|
||||
...
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% callout type="note" title="Other options" %}
|
||||
You can specify more options in the `vite.config.ts` file (see **Step 2** below).
|
||||
{% /callout %}
|
||||
|
||||
## 2. Configure Vite.js
|
||||
|
||||
### TypeScript paths
|
||||
|
||||
You 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.
|
||||
|
||||
### React plugin
|
||||
|
||||
If you are using React, you need to use the [`@vitejs/plugin-react` plugin](https://www.npmjs.com/package/@vitejs/plugin-react).
|
||||
|
||||
### DTS plugin
|
||||
|
||||
If you are building a library, you need to use the [`vite-plugin-dts` plugin](https://www.npmjs.com/package/vite-plugin-dts) to generate the `.d.ts` files for your library.
|
||||
|
||||
#### Skip diagnostics
|
||||
|
||||
If you are building a library, you can set the `skipDiagnostics` option to `true` to speed up the build. This means that type diagnostic will be skipped during the build process. However, if there are some files with type errors which interrupt the build process, these files will not be emitted and `.d.ts` declaration files will not be generated.
|
||||
|
||||
If you choose to skip diagnostics, here is what your `'vite-plugin-dts'` plugin setup will look like:
|
||||
|
||||
```ts {% fileName="libs/my-lib/vite.config.ts" %}
|
||||
...
|
||||
import dts from 'vite-plugin-dts';
|
||||
import { join } from 'path';
|
||||
...
|
||||
...
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
...,
|
||||
dts({
|
||||
entryRoot: 'src',
|
||||
tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
|
||||
skipDiagnostics: true,
|
||||
}),
|
||||
```
|
||||
|
||||
#### Do not skip diagnostics
|
||||
|
||||
If you are building a library, and you want to make sure that all the files are type checked, you can set the `skipDiagnostics` option to `false` to make sure that all the files are type checked. This means that type diagnostic will be run during the build process.
|
||||
|
||||
If you choose to enable diagnostics, here is what your `'vite-plugin-dts'` plugin setup will look like:
|
||||
|
||||
```ts {% fileName="libs/my-lib/vite.config.ts" %}
|
||||
...
|
||||
import dts from 'vite-plugin-dts';
|
||||
...
|
||||
...
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
...,
|
||||
dts({
|
||||
root: '../../',
|
||||
entryRoot: 'libs/my-lib/src',
|
||||
tsConfigFilePath: 'libs/my-lib/tsconfig.lib.json',
|
||||
include: ['libs/my-lib/src/**/*.ts'],
|
||||
outputDir: 'dist/libs/my-lib',
|
||||
skipDiagnostics: false,
|
||||
}),
|
||||
```
|
||||
|
||||
You can read more about the configuration options in the [`vite-plugin-dts` plugin documentation](https://www.npmjs.com/package/vite-plugin-dts)).
|
||||
|
||||
### How your `vite.config.ts` looks like
|
||||
|
||||
#### For applications
|
||||
|
||||
Add a `vite.config.ts` file to the root of your project. If you are not using React, you can skip adding the `react` plugin, of course.
|
||||
|
||||
```ts {% fileName="apps/my-app/vite.config.ts" %}
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
ViteTsConfigPathsPlugin({
|
||||
root: '../../',
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
#### For libraries
|
||||
|
||||
If you are setting up a library (rather than an application) to use vite, your `vite.config.ts` file should look like this:
|
||||
|
||||
```ts {% fileName="libs/my-lib/vite.config.ts" %}
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import viteTsConfigPaths from 'vite-tsconfig-paths';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import { join } from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
dts({
|
||||
entryRoot: 'src',
|
||||
tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
|
||||
skipDiagnostics: true,
|
||||
}),
|
||||
react(),
|
||||
viteTsConfigPaths({
|
||||
root: '../../',
|
||||
}),
|
||||
],
|
||||
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: 'pure-libs-rlv1',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es', 'cjs'],
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react', 'react-dom', 'react/jsx-runtime'],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
{% callout type="note" title="The `root` path" %}
|
||||
Make sure the `root` path in the `ViteTsConfigPathsPlugin` options is correct. It should be the path to the root of your workspace.
|
||||
{% /callout %}
|
||||
|
||||
In 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/).
|
||||
|
||||
## 3. Move `index.html` and point it to your app's entrypoint
|
||||
|
||||
This applies to applications, not libraries.
|
||||
|
||||
First of all, move your `index.html` file to the root of your app (e.g. from `apps/my-app/src/index.html` to `apps/my-app/index.html`).
|
||||
|
||||
Then, add a module `script` tag pointing to the `main.tsx` (or `main.ts`) file of your app:
|
||||
|
||||
```html {% fileName="apps/my-app/index.html" %}
|
||||
...
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## 4. Add a `public` folder
|
||||
|
||||
You can add a `public` folder to the root of your project. You can read more about the public folder in the [Vite.js documentation](https://vitejs.dev/guide/assets.html#the-public-directory).
|
||||
|
||||
```treeview
|
||||
myorg/
|
||||
├── apps/
|
||||
│ ├── my-app/
|
||||
│ │ ├── src/
|
||||
│ │ │ ├── app/
|
||||
│ │ │ ├── assets/
|
||||
│ │ │ ├── ...
|
||||
│ │ │ └── main.tsx
|
||||
│ │ ├── index.html
|
||||
│ │ ├── public/
|
||||
| . | . | ├── favicon.ico
|
||||
│ │ │ └── my-page.md
|
||||
│ │ ├── project.json
|
||||
│ │ ├── ...
|
||||
│ │ ├── tsconfig.app.json
|
||||
│ │ ├── tsconfig.json
|
||||
│ │ └── tsconfig.spec.json
|
||||
```
|
||||
|
||||
You can use the `public` folder to store static **assets**, such as images, fonts, and so on. You can also use it to store Markdown files, which you can then import in your app and use as a source of content.
|
||||
|
||||
## 5. Adjust your project's tsconfig.json
|
||||
|
||||
Change your app's `tsconfig.json` (e.g. `apps/my-app/tsconfig.json`) `compilerOptions` to the following:
|
||||
|
||||
### For React apps
|
||||
|
||||
```json {% fileName="apps/my-app/tsconfig.json" %}
|
||||
...
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": false,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"types": ["vite/client"],
|
||||
"useDefineForClassFields": true
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
### For Web apps
|
||||
|
||||
```json {% fileName="apps/my-app/tsconfig.json" %}
|
||||
...
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"noEmit": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["vite/client"]
|
||||
},
|
||||
"include": ["src"],
|
||||
...
|
||||
```
|
||||
|
||||
You can read more about the TypeScript compiler options in the [Vite.js documentation](https://vitejs.dev/guide/features.html#typescript-compiler-options).
|
||||
|
||||
## 6. Use Vite.js
|
||||
|
||||
Now you can finally serve and build your app using Vite.js:
|
||||
|
||||
### Serve the app
|
||||
|
||||
```bash
|
||||
nx serve my-app
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
nx run my-app:serve
|
||||
```
|
||||
|
||||
Now, visit [http://localhost:4200](http://localhost:4200) to see your app running!
|
||||
|
||||
### Build the app
|
||||
|
||||
```bash
|
||||
nx build my-app
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
nx run my-app:build
|
||||
```
|
||||
@@ -139,7 +139,7 @@
|
||||
- [Next](/recipes/next)
|
||||
- [How to configure Next.js plugins](/recipes/next/next-config-setup)
|
||||
- [Vite](/recipes/vite)
|
||||
- [Manually set up your project to use Vite.js](/recipes/vite/set-up-vite-manually)
|
||||
- [Configure Vite on your Nx workspace](/recipes/vite/configure-vite)
|
||||
- [Webpack](/recipes/webpack)
|
||||
- [How to configure webpack on your Nx workspace](/recipes/webpack/webpack-config-setup)
|
||||
- [Webpack plugins](/recipes/webpack/webpack-plugins)
|
||||
|
||||
@@ -13,6 +13,7 @@ describe('Nuxt Plugin', () => {
|
||||
|
||||
beforeAll(() => {
|
||||
proj = newProject({
|
||||
packages: ['@nx/nuxt', '@nx/storybook'],
|
||||
unsetProjectNameAndRootFormat: false,
|
||||
});
|
||||
runCLI(`generate @nx/nuxt:app ${app} --unitTestRunner=vitest`);
|
||||
|
||||
@@ -17,7 +17,9 @@ describe('Storybook generators and executors for monorepos', () => {
|
||||
const reactStorybookApp = uniq('react-app');
|
||||
let proj;
|
||||
beforeAll(async () => {
|
||||
proj = newProject();
|
||||
proj = newProject({
|
||||
packages: ['@nx/react', '@nx/storybook'],
|
||||
});
|
||||
runCLI(
|
||||
`generate @nx/react:app ${reactStorybookApp} --bundler=webpack --project-name-and-root-format=as-provided --no-interactive`
|
||||
);
|
||||
|
||||
@@ -11,7 +11,12 @@ import {
|
||||
// This test ensures that when CJS is gone from the published `vite` package, Nx will continue to work.
|
||||
|
||||
describe('Vite ESM tests', () => {
|
||||
beforeAll(() => newProject({ unsetProjectNameAndRootFormat: false }));
|
||||
beforeAll(() =>
|
||||
newProject({
|
||||
unsetProjectNameAndRootFormat: false,
|
||||
packages: ['@nx/react'],
|
||||
})
|
||||
);
|
||||
|
||||
it('should build with Vite when it is ESM-only', async () => {
|
||||
const appName = uniq('viteapp');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { cleanupProject, newProject, runCLI, uniq } from '@nx/e2e/utils';
|
||||
|
||||
const myApp = uniq('my-app');
|
||||
const myVueApp = uniq('my-vue-app');
|
||||
|
||||
describe('@nx/vite/plugin', () => {
|
||||
let proj: string;
|
||||
@@ -9,22 +10,21 @@ describe('@nx/vite/plugin', () => {
|
||||
beforeAll(() => {
|
||||
originalEnv = process.env.NX_PCV3;
|
||||
process.env.NX_PCV3 = 'true';
|
||||
proj = newProject({
|
||||
packages: ['@nx/react', '@nx/vue'],
|
||||
});
|
||||
runCLI(
|
||||
`generate @nx/react:app ${myApp} --bundler=vite --unitTestRunner=vitest`
|
||||
);
|
||||
runCLI(`generate @nx/vue:app ${myVueApp} --unitTestRunner=vitest`);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env.NODE_ENV = originalEnv;
|
||||
cleanupProject();
|
||||
});
|
||||
|
||||
describe('build and test React Vite app', () => {
|
||||
beforeAll(() => {
|
||||
proj = newProject();
|
||||
runCLI(
|
||||
`generate @nx/react:app ${myApp} --bundler=vite --unitTestRunner=vitest`
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
describe('build and test React app', () => {
|
||||
it('should build application', () => {
|
||||
const result = runCLI(`build ${myApp}`);
|
||||
expect(result).toContain('Successfully ran target build');
|
||||
@@ -35,24 +35,14 @@ describe('@nx/vite/plugin', () => {
|
||||
expect(result).toContain('Successfully ran target test');
|
||||
}, 200_000);
|
||||
});
|
||||
|
||||
describe('build and test Vue app', () => {
|
||||
beforeAll(() => {
|
||||
proj = newProject();
|
||||
runCLI(`generate @nx/vue:app ${myApp} --unitTestRunner=vitest`);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
cleanupProject();
|
||||
});
|
||||
|
||||
it('should build application', () => {
|
||||
const result = runCLI(`build ${myApp}`);
|
||||
const result = runCLI(`build ${myVueApp}`);
|
||||
expect(result).toContain('Successfully ran target build');
|
||||
}, 200_000);
|
||||
|
||||
it('should test application', () => {
|
||||
const result = runCLI(`test ${myApp}`);
|
||||
const result = runCLI(`test ${myVueApp}`);
|
||||
expect(result).toContain('Successfully ran target test');
|
||||
}, 200_000);
|
||||
});
|
||||
|
||||
@@ -31,7 +31,9 @@ describe('Vite Plugin', () => {
|
||||
describe('Vite on React apps', () => {
|
||||
describe('set up new React app with --bundler=vite option', () => {
|
||||
beforeEach(async () => {
|
||||
proj = newProject();
|
||||
proj = newProject({
|
||||
packages: ['@nx/react'],
|
||||
});
|
||||
runCLI(`generate @nx/react:app ${myApp} --bundler=vite`);
|
||||
createFile(`apps/${myApp}/public/hello.md`, `# Hello World`);
|
||||
});
|
||||
@@ -49,7 +51,9 @@ describe('Vite Plugin', () => {
|
||||
describe('Vite on Web apps', () => {
|
||||
describe('set up new @nx/web app with --bundler=vite option', () => {
|
||||
beforeEach(() => {
|
||||
proj = newProject();
|
||||
proj = newProject({
|
||||
packages: ['@nx/web'],
|
||||
});
|
||||
runCLI(`generate @nx/web:app ${myApp} --bundler=vite`);
|
||||
});
|
||||
afterEach(() => cleanupProject());
|
||||
@@ -122,7 +126,10 @@ describe('Vite Plugin', () => {
|
||||
const app = uniq('demo');
|
||||
const lib = uniq('my-lib');
|
||||
beforeAll(() => {
|
||||
proj = newProject({ name: uniq('vite-incr-build') });
|
||||
proj = newProject({
|
||||
name: uniq('vite-incr-build'),
|
||||
packages: ['@nx/react'],
|
||||
});
|
||||
runCLI(`generate @nx/react:app ${app} --bundler=vite --no-interactive`);
|
||||
|
||||
// only this project will be directly used from dist
|
||||
@@ -206,7 +213,7 @@ export default App;
|
||||
describe('should be able to create libs that use vitest', () => {
|
||||
const lib = uniq('my-lib');
|
||||
beforeEach(() => {
|
||||
proj = newProject({ name: uniq('vite-proj') });
|
||||
proj = newProject({ name: uniq('vite-proj'), packages: ['@nx/react'] });
|
||||
});
|
||||
|
||||
it('should be able to run tests', async () => {
|
||||
@@ -359,7 +366,10 @@ export default defineConfig({
|
||||
|
||||
describe('ESM-only apps', () => {
|
||||
beforeAll(() => {
|
||||
newProject({ unsetProjectNameAndRootFormat: false });
|
||||
newProject({
|
||||
unsetProjectNameAndRootFormat: false,
|
||||
packages: ['@nx/react'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should support ESM-only plugins in vite.config.ts for root apps (#NXP-168)', () => {
|
||||
|
||||
@@ -12,7 +12,9 @@ describe('Storybook generators and executors for Vue projects', () => {
|
||||
const vueStorybookApp = uniq('vue-app');
|
||||
let proj;
|
||||
beforeAll(async () => {
|
||||
proj = newProject();
|
||||
proj = newProject({
|
||||
packages: ['@nx/vue', '@nx/storybook'],
|
||||
});
|
||||
runCLI(
|
||||
`generate @nx/vue:app ${vueStorybookApp} --project-name-and-root-format=as-provided --no-interactive`
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
|
||||
describe('vue tailwind support', () => {
|
||||
beforeAll(() => {
|
||||
newProject({ unsetProjectNameAndRootFormat: false });
|
||||
newProject({ unsetProjectNameAndRootFormat: false, packages: ['@nx/vue'] });
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
|
||||
@@ -12,6 +12,7 @@ describe('Vue Plugin', () => {
|
||||
|
||||
beforeAll(() => {
|
||||
proj = newProject({
|
||||
packages: ['@nx/vue'],
|
||||
unsetProjectNameAndRootFormat: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -381,6 +381,7 @@ const recipesUrls = {
|
||||
'/recipes/next/next-config-setup',
|
||||
'/packages/vite/documents/set-up-vite-manually':
|
||||
'/recipes/vite/set-up-vite-manually',
|
||||
'/recipes/vite/set-up-vite-manually': '/recipes/vite/configure-vite',
|
||||
'/packages/webpack/documents/webpack-plugins':
|
||||
'/recipes/webpack/webpack-plugins',
|
||||
'/packages/webpack/documents/webpack-config-setup':
|
||||
|
||||
@@ -1433,6 +1433,10 @@
|
||||
"version": "~17.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular/pwa": {
|
||||
"version": "~17.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@angular/core": {
|
||||
"version": "~17.0.0",
|
||||
"alwaysAddToPackageJson": true
|
||||
|
||||
+71
-13
@@ -1,6 +1,7 @@
|
||||
use std::path::PathBuf;
|
||||
use tracing::trace;
|
||||
|
||||
use crate::native::glob::build_glob_set;
|
||||
use crate::native::glob::{build_glob_set, contains_glob_pattern};
|
||||
use crate::native::utils::Normalize;
|
||||
use crate::native::walker::nx_walker_sync;
|
||||
|
||||
@@ -10,17 +11,46 @@ use crate::native::walker::nx_walker_sync;
|
||||
pub fn expand_outputs(directory: String, entries: Vec<String>) -> anyhow::Result<Vec<String>> {
|
||||
let directory: PathBuf = directory.into();
|
||||
|
||||
let (existing_paths, not_found): (Vec<_>, Vec<_>) = entries.into_iter().partition(|entry| {
|
||||
let path = directory.join(entry);
|
||||
path.exists()
|
||||
});
|
||||
let has_glob_pattern = entries.iter().any(|entry| contains_glob_pattern(entry));
|
||||
|
||||
if not_found.is_empty() {
|
||||
return Ok(existing_paths);
|
||||
if !has_glob_pattern {
|
||||
trace!("No glob patterns found, checking if entries exist");
|
||||
let existing_directories = entries
|
||||
.into_iter()
|
||||
.filter(|entry| {
|
||||
let path = directory.join(entry);
|
||||
path.exists()
|
||||
})
|
||||
.collect();
|
||||
return Ok(existing_directories);
|
||||
}
|
||||
|
||||
let glob_set = build_glob_set(¬_found)?;
|
||||
let found_paths = nx_walker_sync(directory)
|
||||
let (regular_globs, negated_globs): (Vec<_>, Vec<_>) = entries
|
||||
.into_iter()
|
||||
.partition(|entry| !entry.starts_with('!'));
|
||||
|
||||
let negated_globs = negated_globs
|
||||
.into_iter()
|
||||
.map(|s| s[1..].to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let regular_globs = regular_globs
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
if !s.ends_with('/') {
|
||||
let path = directory.join(&s);
|
||||
if path.is_dir() {
|
||||
return format!("{}/", s);
|
||||
}
|
||||
}
|
||||
s
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
trace!(?negated_globs, ?regular_globs, "Expanding globs");
|
||||
|
||||
let glob_set = build_glob_set(®ular_globs)?;
|
||||
let found_paths = nx_walker_sync(directory, Some(&negated_globs))
|
||||
.filter_map(|path| {
|
||||
if glob_set.is_match(&path) {
|
||||
Some(path.to_normalized_string())
|
||||
@@ -28,9 +58,9 @@ pub fn expand_outputs(directory: String, entries: Vec<String>) -> anyhow::Result
|
||||
None
|
||||
}
|
||||
})
|
||||
.chain(existing_paths);
|
||||
.collect();
|
||||
|
||||
Ok(found_paths.collect())
|
||||
Ok(found_paths)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
@@ -60,7 +90,7 @@ pub fn get_files_for_outputs(
|
||||
if !globs.is_empty() {
|
||||
// todo(jcammisuli): optimize this as nx_walker_sync is very slow on the root directory. We need to change this to only search smaller directories
|
||||
let glob_set = build_glob_set(&globs)?;
|
||||
let found_paths = nx_walker_sync(&directory).filter_map(|path| {
|
||||
let found_paths = nx_walker_sync(&directory, None).filter_map(|path| {
|
||||
if glob_set.is_match(&path) {
|
||||
Some(path.to_normalized_string())
|
||||
} else {
|
||||
@@ -75,7 +105,7 @@ pub fn get_files_for_outputs(
|
||||
for dir in directories {
|
||||
let dir = PathBuf::from(dir);
|
||||
let dir_path = directory.join(&dir);
|
||||
let files_in_dir = nx_walker_sync(&dir_path).filter_map(|e| {
|
||||
let files_in_dir = nx_walker_sync(&dir_path, None).filter_map(|e| {
|
||||
let path = dir_path.join(&e);
|
||||
|
||||
if path.is_file() {
|
||||
@@ -123,6 +153,15 @@ mod test {
|
||||
temp.child("multi").child("src.ts").touch().unwrap();
|
||||
temp.child("multi").child("file.map").touch().unwrap();
|
||||
temp.child("multi").child("file.txt").touch().unwrap();
|
||||
temp.child("apps/web/.next/cache")
|
||||
.child("contents")
|
||||
.touch()
|
||||
.unwrap();
|
||||
temp.child("apps/web/.next/static")
|
||||
.child("contents")
|
||||
.touch()
|
||||
.unwrap();
|
||||
temp.child("apps/web/.next/content-file").touch().unwrap();
|
||||
temp
|
||||
}
|
||||
#[test]
|
||||
@@ -156,4 +195,23 @@ mod test {
|
||||
vec!["multi/file.js", "multi/file.map", "multi/src.ts"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_handle_multiple_outputs_with_negation() {
|
||||
let temp = setup_fs();
|
||||
let entries = vec![
|
||||
"apps/web/.next".to_string(),
|
||||
"!apps/web/.next/cache".to_string(),
|
||||
];
|
||||
let mut result = expand_outputs(temp.display().to_string(), entries).unwrap();
|
||||
result.sort();
|
||||
assert_eq!(
|
||||
result,
|
||||
vec![
|
||||
"apps/web/.next/content-file",
|
||||
"apps/web/.next/static",
|
||||
"apps/web/.next/static/contents"
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,22 @@ pub(crate) fn build_glob_set<S: AsRef<str> + Debug>(globs: &[S]) -> anyhow::Resu
|
||||
NxGlobSetBuilder::new(&result)?.build()
|
||||
}
|
||||
|
||||
pub(crate) fn contains_glob_pattern(value: &str) -> bool {
|
||||
value.contains('!')
|
||||
|| value.contains('?')
|
||||
|| value.contains('@')
|
||||
|| value.contains('+')
|
||||
|| value.contains('*')
|
||||
|| value.contains('|')
|
||||
|| value.contains(',')
|
||||
|| value.contains('{')
|
||||
|| value.contains('}')
|
||||
|| value.contains('[')
|
||||
|| value.contains(']')
|
||||
|| value.contains('(')
|
||||
|| value.contains(')')
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
@@ -2,24 +2,25 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::*;
|
||||
use dashmap::DashMap;
|
||||
use tracing::trace;
|
||||
use tracing::{trace, warn};
|
||||
|
||||
use crate::native::glob::build_glob_set;
|
||||
use crate::native::types::FileData;
|
||||
use crate::native::{glob::build_glob_set, hasher::hash};
|
||||
|
||||
pub fn hash_workspace_files(
|
||||
workspace_file_set: &str,
|
||||
all_workspace_files: &[FileData],
|
||||
cache: Arc<DashMap<String, String>>,
|
||||
) -> Result<String> {
|
||||
let file_set = workspace_file_set
|
||||
.strip_prefix("{workspaceRoot}/")
|
||||
.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"{workspace_file_set} does not start with {}",
|
||||
"{workspaceRoot}/"
|
||||
)
|
||||
})?;
|
||||
let file_set = workspace_file_set.strip_prefix("{workspaceRoot}/");
|
||||
|
||||
let Some(file_set) = file_set else {
|
||||
warn!(
|
||||
"{workspace_file_set} does not start with {}. This will throw an error in Nx 18.",
|
||||
"{workspaceRoot}/"
|
||||
);
|
||||
return Ok(hash(b""));
|
||||
};
|
||||
|
||||
if let Some(cache_results) = cache.get(file_set) {
|
||||
return Ok(cache_results.clone());
|
||||
@@ -50,9 +51,10 @@ mod test {
|
||||
use std::sync::Arc;
|
||||
|
||||
#[test]
|
||||
fn test_hash_workspace_files_error() {
|
||||
let result = hash_workspace_files("packages/{package}", &[], Arc::new(DashMap::new()));
|
||||
assert!(result.is_err());
|
||||
fn invalid_workspace_input_is_just_empty_hash() {
|
||||
let result =
|
||||
hash_workspace_files("packages/{package}", &[], Arc::new(DashMap::new())).unwrap();
|
||||
assert_eq!(result, hash(b""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -20,14 +20,28 @@ pub struct NxFile {
|
||||
|
||||
/// Walks the directory in a single thread and does not ignore any files
|
||||
/// Should only be used for small directories, and not traversing the whole workspace
|
||||
pub fn nx_walker_sync<'a, P>(directory: P) -> impl Iterator<Item = PathBuf>
|
||||
///
|
||||
/// The `ignores` argument is used to filter entries. This is important to make sure that any ignore globs are applied on the `filter_entry` function
|
||||
pub fn nx_walker_sync<'a, P>(
|
||||
directory: P,
|
||||
ignores: Option<&[String]>,
|
||||
) -> impl Iterator<Item = PathBuf>
|
||||
where
|
||||
P: AsRef<Path> + 'a,
|
||||
{
|
||||
let base_dir: PathBuf = directory.as_ref().into();
|
||||
|
||||
let ignore_glob_set =
|
||||
build_glob_set(&["**/node_modules", "**/.git"]).expect("These static ignores always build");
|
||||
let mut base_ignores: Vec<String> = vec![
|
||||
"**/node_modules".into(),
|
||||
"**/.git".into(),
|
||||
"**/.nx/cache".into(),
|
||||
];
|
||||
|
||||
if let Some(additional_ignores) = ignores {
|
||||
base_ignores.extend(additional_ignores.iter().map(|s| format!("**/{}", s)));
|
||||
};
|
||||
|
||||
let ignore_glob_set = build_glob_set(&base_ignores).expect("Should be valid globs");
|
||||
|
||||
// Use WalkDir instead of ignore::WalkBuilder because it's faster
|
||||
WalkDir::new(&base_dir)
|
||||
@@ -50,8 +64,8 @@ where
|
||||
{
|
||||
let directory = directory.as_ref();
|
||||
|
||||
let ignore_glob_set =
|
||||
build_glob_set(&["**/node_modules", "**/.git"]).expect("These static ignores always build");
|
||||
let ignore_glob_set = build_glob_set(&["**/node_modules", "**/.git", "**/.nx/cache"])
|
||||
.expect("These static ignores always build");
|
||||
|
||||
let mut walker = WalkBuilder::new(directory);
|
||||
walker.hidden(false);
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
import { composePlugins, withNx } from '@nx/webpack';
|
||||
import { withReact } from './with-react';
|
||||
|
||||
// Support existing default exports as well as new named export.
|
||||
const legacyExport: any = withReact();
|
||||
legacyExport.withReact = withReact;
|
||||
const plugin = composePlugins(withNx(), withReact());
|
||||
|
||||
/** @deprecated use `import { withReact } from '@nx/react'` */
|
||||
// This is here for backward compatibility if anyone imports {getWebpackConfig} directly.
|
||||
// TODO(jack): Remove in Nx 16
|
||||
const getWebpackConfig = withReact();
|
||||
|
||||
legacyExport.getWebpackConfig = getWebpackConfig;
|
||||
|
||||
module.exports = legacyExport;
|
||||
|
||||
export { getWebpackConfig };
|
||||
module.exports = plugin;
|
||||
|
||||
@@ -74,7 +74,7 @@ The list of executors for building, testing and serving that can be converted to
|
||||
|
||||
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/set-up-vite-manually), but it may not work properly.
|
||||
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.
|
||||
|
||||
You can read more in the [Vite package overview page](/packages/vite).
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ export async function* viteBuildExecutor(
|
||||
options: Record<string, any> & ViteBuildExecutorOptions,
|
||||
context: ExecutorContext
|
||||
) {
|
||||
process.env.VITE_CJS_IGNORE_WARNING = 'true';
|
||||
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
|
||||
const { mergeConfig, build, loadConfigFromFile } = await (Function(
|
||||
'return import("vite")'
|
||||
@@ -49,7 +50,7 @@ export async function* viteBuildExecutor(
|
||||
? process.cwd()
|
||||
: relative(context.cwd, joinPathFragments(context.root, projectRoot));
|
||||
|
||||
const { buildOptions, otherOptions } = await getExtraArgs(options);
|
||||
const { buildOptions, otherOptions } = await getBuildExtraArgs(options);
|
||||
|
||||
const resolved = await loadConfigFromFile(
|
||||
{
|
||||
@@ -188,7 +189,9 @@ export async function* viteBuildExecutor(
|
||||
}
|
||||
}
|
||||
|
||||
async function getExtraArgs(options: ViteBuildExecutorOptions): Promise<{
|
||||
export async function getBuildExtraArgs(
|
||||
options: ViteBuildExecutorOptions
|
||||
): Promise<{
|
||||
buildOptions: BuildOptions;
|
||||
otherOptions: Record<string, any>;
|
||||
}> {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
loadConfigFromFile,
|
||||
type InlineConfig,
|
||||
type ViteDevServer,
|
||||
ServerOptions,
|
||||
} from 'vite';
|
||||
|
||||
import {
|
||||
@@ -20,6 +21,7 @@ export async function* viteDevServerExecutor(
|
||||
options: ViteDevServerExecutorOptions,
|
||||
context: ExecutorContext
|
||||
): AsyncGenerator<{ success: boolean; baseUrl: string }> {
|
||||
process.env.VITE_CJS_IGNORE_WARNING = 'true';
|
||||
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
|
||||
const { mergeConfig, createServer } = await (Function(
|
||||
'return import("vite")'
|
||||
@@ -43,11 +45,11 @@ export async function* viteDevServerExecutor(
|
||||
projectRoot,
|
||||
buildTargetOptions.configFile
|
||||
);
|
||||
const extraArgs = await getExtraArgs(options);
|
||||
const { serverOptions, otherOptions } = await getServerExtraArgs(options);
|
||||
const resolved = await loadConfigFromFile(
|
||||
{
|
||||
mode: extraArgs?.mode ?? 'production',
|
||||
command: 'build',
|
||||
mode: otherOptions?.mode ?? 'development',
|
||||
command: 'serve',
|
||||
},
|
||||
viteConfigPath
|
||||
);
|
||||
@@ -62,9 +64,9 @@ export async function* viteDevServerExecutor(
|
||||
{
|
||||
server: {
|
||||
...(await getViteServerOptions(options, context)),
|
||||
...extraArgs,
|
||||
...serverOptions,
|
||||
},
|
||||
...extraArgs,
|
||||
...otherOptions,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -111,9 +113,12 @@ async function runViteDevServer(server: ViteDevServer): Promise<void> {
|
||||
|
||||
export default viteDevServerExecutor;
|
||||
|
||||
async function getExtraArgs(
|
||||
async function getServerExtraArgs(
|
||||
options: ViteDevServerExecutorOptions
|
||||
): Promise<InlineConfig> {
|
||||
): Promise<{
|
||||
serverOptions: ServerOptions;
|
||||
otherOptions: Record<string, any>;
|
||||
}> {
|
||||
// support passing extra args to vite cli
|
||||
const schema = await import('./schema.json');
|
||||
const extraArgs = {};
|
||||
@@ -123,5 +128,37 @@ async function getExtraArgs(
|
||||
}
|
||||
}
|
||||
|
||||
return extraArgs as InlineConfig;
|
||||
const serverOptions = {} as ServerOptions;
|
||||
const serverSchemaKeys = [
|
||||
'hmr',
|
||||
'warmup',
|
||||
'watch',
|
||||
'middlewareMode',
|
||||
'fs',
|
||||
'origin',
|
||||
'preTransformRequests',
|
||||
'sourcemapIgnoreList',
|
||||
'port',
|
||||
'strictPort',
|
||||
'host',
|
||||
'https',
|
||||
'open',
|
||||
'proxy',
|
||||
'cors',
|
||||
'headers',
|
||||
];
|
||||
|
||||
const otherOptions = {};
|
||||
for (const key of Object.keys(extraArgs)) {
|
||||
if (serverSchemaKeys.includes(key)) {
|
||||
serverOptions[key] = extraArgs[key];
|
||||
} else {
|
||||
otherOptions[key] = extraArgs[key];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
serverOptions,
|
||||
otherOptions,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,20 +5,22 @@ import {
|
||||
parseTargetString,
|
||||
runExecutor,
|
||||
} from '@nx/devkit';
|
||||
import type { InlineConfig, PreviewServer } from 'vite';
|
||||
import type { InlineConfig, PreviewOptions, PreviewServer } from 'vite';
|
||||
import {
|
||||
getNxTargetOptions,
|
||||
getVitePreviewOptions,
|
||||
getProxyConfig,
|
||||
normalizeViteConfigFilePath,
|
||||
} from '../../utils/options-utils';
|
||||
import { ViteBuildExecutorOptions } from '../build/schema';
|
||||
import { VitePreviewServerExecutorOptions } from './schema';
|
||||
import { relative } from 'path';
|
||||
import { getBuildExtraArgs } from '../build/build.impl';
|
||||
|
||||
export async function* vitePreviewServerExecutor(
|
||||
options: VitePreviewServerExecutorOptions,
|
||||
context: ExecutorContext
|
||||
) {
|
||||
process.env.VITE_CJS_IGNORE_WARNING = 'true';
|
||||
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
|
||||
const { mergeConfig, preview, loadConfigFromFile } = await (Function(
|
||||
'return import("vite")'
|
||||
@@ -43,15 +45,20 @@ export async function* vitePreviewServerExecutor(
|
||||
options.buildTarget,
|
||||
context
|
||||
);
|
||||
|
||||
const viteConfigPath = normalizeViteConfigFilePath(
|
||||
context.root,
|
||||
projectRoot,
|
||||
buildTargetOptions.configFile
|
||||
);
|
||||
const extraArgs = await getExtraArgs(options);
|
||||
|
||||
const { buildOptions, otherOptions: otherOptionsFromBuild } =
|
||||
await getBuildExtraArgs(buildTargetOptions);
|
||||
|
||||
const { previewOptions, otherOptions } = await getExtraArgs(options);
|
||||
const resolved = await loadConfigFromFile(
|
||||
{
|
||||
mode: extraArgs?.mode ?? 'production',
|
||||
mode: otherOptions?.mode ?? 'production',
|
||||
command: 'build',
|
||||
},
|
||||
viteConfigPath
|
||||
@@ -81,12 +88,16 @@ export async function* vitePreviewServerExecutor(
|
||||
...{ watch: {} },
|
||||
build: {
|
||||
outDir,
|
||||
...(isCustomBuildTarget ? {} : buildOptions),
|
||||
},
|
||||
...(isCustomBuildTarget ? {} : otherOptionsFromBuild),
|
||||
...otherOptions,
|
||||
preview: {
|
||||
...getProxyConfig(context, otherOptions.proxyConfig),
|
||||
...previewOptions,
|
||||
},
|
||||
...(isCustomBuildTarget ? {} : buildTargetOptions),
|
||||
...extraArgs,
|
||||
};
|
||||
|
||||
// Retrieve the server configuration.
|
||||
const serverConfig: InlineConfig = mergeConfig(
|
||||
{
|
||||
// This should not be needed as it's going to be set in vite.config.ts
|
||||
@@ -96,7 +107,6 @@ export async function* vitePreviewServerExecutor(
|
||||
},
|
||||
{
|
||||
...mergedOptions,
|
||||
preview: getVitePreviewOptions(mergedOptions, context),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -180,7 +190,10 @@ export default vitePreviewServerExecutor;
|
||||
|
||||
async function getExtraArgs(
|
||||
options: VitePreviewServerExecutorOptions
|
||||
): Promise<InlineConfig> {
|
||||
): Promise<{
|
||||
previewOptions: PreviewOptions;
|
||||
otherOptions: Record<string, any>;
|
||||
}> {
|
||||
// support passing extra args to vite cli
|
||||
const schema = await import('./schema.json');
|
||||
const extraArgs = {};
|
||||
@@ -190,5 +203,29 @@ async function getExtraArgs(
|
||||
}
|
||||
}
|
||||
|
||||
return extraArgs as InlineConfig;
|
||||
const previewOptions = {} as PreviewOptions;
|
||||
const previewSchemaKeys = [
|
||||
'port',
|
||||
'strictPort',
|
||||
'host',
|
||||
'https',
|
||||
'open',
|
||||
'proxy',
|
||||
'cors',
|
||||
'headers',
|
||||
];
|
||||
|
||||
const otherOptions = {};
|
||||
for (const key of Object.keys(extraArgs)) {
|
||||
if (previewSchemaKeys.includes(key)) {
|
||||
previewOptions[key] = extraArgs[key];
|
||||
} else {
|
||||
otherOptions[key] = extraArgs[key];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
previewOptions,
|
||||
otherOptions,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import { VitestExecutorOptions } from '../schema';
|
||||
import { normalizeViteConfigFilePath } from '../../../utils/options-utils';
|
||||
import { relative } from 'path';
|
||||
import { NxReporter } from './nx-reporter';
|
||||
|
||||
export async function getOptions(
|
||||
options: VitestExecutorOptions,
|
||||
@@ -76,12 +75,9 @@ export async function getExtraArgs(
|
||||
options: VitestExecutorOptions
|
||||
): Promise<Record<string, any>> {
|
||||
// support passing extra args to vite cli
|
||||
const schema = await import('../schema.json');
|
||||
const extraArgs: Record<string, any> = {};
|
||||
for (const key of Object.keys(options)) {
|
||||
if (!schema.properties[key]) {
|
||||
extraArgs[key] = options[key];
|
||||
}
|
||||
extraArgs[key] = options[key];
|
||||
}
|
||||
|
||||
return extraArgs;
|
||||
|
||||
@@ -14,6 +14,8 @@ export async function* vitestExecutor(
|
||||
|
||||
registerTsConfigPaths(resolve(workspaceRoot, projectRoot, 'tsconfig.json'));
|
||||
|
||||
process.env.VITE_CJS_IGNORE_WARNING = 'true';
|
||||
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
|
||||
const { startVitest } = await (Function(
|
||||
'return import("vitest/node")'
|
||||
)() as Promise<typeof import('vitest/node')>);
|
||||
|
||||
+109
@@ -165,3 +165,112 @@ export default defineConfig({
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`change-vite-ts-paths-plugin migration should convert the file correctly 1`] = `
|
||||
"/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
return {
|
||||
root: __dirname,
|
||||
build: {
|
||||
outDir: '../dist/demo4',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
cacheDir: '../../node_modules/.vite/demo4',
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths()],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
test: {
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../coverage/demo4',
|
||||
provider: 'v8',
|
||||
},
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
},
|
||||
};
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`change-vite-ts-paths-plugin migration should convert the file correctly 2`] = `
|
||||
"/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
return {
|
||||
root: __dirname,
|
||||
build: {
|
||||
outDir: '../dist/demo4',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
cacheDir: '../../node_modules/.vite/demo4',
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths()],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
test: {
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../coverage/demo4',
|
||||
provider: 'v8',
|
||||
},
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
},
|
||||
};
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`change-vite-ts-paths-plugin migration should show warning to the user if could not recognize config 1`] = `
|
||||
"// some invalid config
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import { ChangeType, applyChangesToString } from '@nx/devkit';
|
||||
import { FileReplacement } from '../../../../plugins/rollup-replace-files.plugin';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import { getConfigNode, notFoundWarning } from '../update-vite-config';
|
||||
|
||||
export function addFileReplacements(
|
||||
configContents: string,
|
||||
fileReplacements: FileReplacement[]
|
||||
fileReplacements: FileReplacement[],
|
||||
configPath: string
|
||||
): string {
|
||||
const configNode = getConfigNode(configContents);
|
||||
if (!configNode) {
|
||||
notFoundWarning(configPath);
|
||||
return configContents;
|
||||
}
|
||||
const pluginsObject = tsquery.query(
|
||||
configContents,
|
||||
configNode,
|
||||
`PropertyAssignment:has(Identifier[name="plugins"])`
|
||||
)?.[0];
|
||||
const replaceFilesPlugin = tsquery.query(
|
||||
configContents,
|
||||
configNode,
|
||||
`PropertyAssignment:has(Identifier[name="plugins"]) CallExpression:has(Identifier[name="replaceFiles"])`
|
||||
)?.[0];
|
||||
|
||||
@@ -55,7 +62,7 @@ export function addFileReplacements(
|
||||
return applyChangesToString(configContents, [
|
||||
{
|
||||
type: ChangeType.Insert,
|
||||
index: foundDefineConfig.getStart() + 14,
|
||||
index: configNode.getStart() + 1,
|
||||
text: `plugins: [replaceFiles(${JSON.stringify(fileReplacements)})],`,
|
||||
},
|
||||
firstImportDeclaration
|
||||
|
||||
@@ -4,12 +4,12 @@ import {
|
||||
Tree,
|
||||
applyChangesToString,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
offsetFromRoot,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import ts = require('typescript');
|
||||
import { getConfigNode, notFoundWarning } from '../update-vite-config';
|
||||
|
||||
export function updateBuildOutDirAndRoot(
|
||||
options: Record<string, any>,
|
||||
@@ -17,7 +17,8 @@ export function updateBuildOutDirAndRoot(
|
||||
projectConfig: ProjectConfiguration,
|
||||
targetName: string,
|
||||
tree: Tree,
|
||||
projectName: string
|
||||
projectName: string,
|
||||
configPath: string
|
||||
): string {
|
||||
const foundDefineConfig = tsquery.query(
|
||||
configContents,
|
||||
@@ -25,10 +26,8 @@ export function updateBuildOutDirAndRoot(
|
||||
)?.[0];
|
||||
|
||||
if (!foundDefineConfig) {
|
||||
logger.warn(`
|
||||
Could not find defineConfig in your vite.config file.
|
||||
Please add the build.outDir and root options to your vite.config file.
|
||||
`);
|
||||
notFoundWarning(configPath);
|
||||
return;
|
||||
}
|
||||
|
||||
configContents = fixBuild(
|
||||
@@ -38,10 +37,10 @@ export function updateBuildOutDirAndRoot(
|
||||
targetName,
|
||||
tree,
|
||||
projectName,
|
||||
foundDefineConfig
|
||||
configPath
|
||||
);
|
||||
|
||||
configContents = addRoot(configContents, foundDefineConfig);
|
||||
configContents = addRoot(configContents, configPath);
|
||||
|
||||
return configContents;
|
||||
}
|
||||
@@ -53,8 +52,14 @@ function fixBuild(
|
||||
targetName: string,
|
||||
tree: Tree,
|
||||
projectName: string,
|
||||
foundDefineConfig?: ts.Node
|
||||
configPath: string
|
||||
) {
|
||||
const configNode = getConfigNode(configContents);
|
||||
if (!configNode) {
|
||||
notFoundWarning(configPath);
|
||||
return configContents;
|
||||
}
|
||||
|
||||
let outputPath = '';
|
||||
|
||||
// In vite.config.ts, we want to keep the path relative to workspace root
|
||||
@@ -78,7 +83,7 @@ function fixBuild(
|
||||
updateProjectConfiguration(tree, projectName, projectConfig);
|
||||
|
||||
const buildObject = tsquery.query(
|
||||
configContents,
|
||||
configNode,
|
||||
`PropertyAssignment:has(Identifier[name="build"])`
|
||||
)?.[0];
|
||||
|
||||
@@ -134,28 +139,32 @@ function fixBuild(
|
||||
return applyChangesToString(configContents, changes);
|
||||
}
|
||||
} else {
|
||||
return addBuildProperty(configContents, outputPath, foundDefineConfig);
|
||||
return addBuildProperty(configContents, outputPath, configPath);
|
||||
}
|
||||
|
||||
return configContents;
|
||||
}
|
||||
|
||||
function addRoot(
|
||||
configFileContents: string,
|
||||
foundDefineConfig?: ts.Node
|
||||
): string {
|
||||
function addRoot(configFileContents: string, configPath: string): string {
|
||||
const configNode = getConfigNode(configFileContents);
|
||||
|
||||
if (!configNode) {
|
||||
notFoundWarning(configPath);
|
||||
return configFileContents;
|
||||
}
|
||||
|
||||
const rootOption = tsquery.query(
|
||||
configFileContents,
|
||||
configNode,
|
||||
`PropertyAssignment:has(Identifier[name="root"]) Identifier[name="__dirname"]`
|
||||
)?.[0];
|
||||
|
||||
if (rootOption || !foundDefineConfig) {
|
||||
if (rootOption) {
|
||||
return configFileContents;
|
||||
} else {
|
||||
return applyChangesToString(configFileContents, [
|
||||
{
|
||||
type: ChangeType.Insert,
|
||||
index: foundDefineConfig.getStart() + 14,
|
||||
index: configNode.getStart() + 1,
|
||||
text: `root: __dirname,`,
|
||||
},
|
||||
]);
|
||||
@@ -165,23 +174,25 @@ function addRoot(
|
||||
function addBuildProperty(
|
||||
configFileContents: string,
|
||||
outputPath: string,
|
||||
foundDefineConfig: ts.Node
|
||||
configPath: string
|
||||
): string {
|
||||
if (foundDefineConfig) {
|
||||
return applyChangesToString(configFileContents, [
|
||||
{
|
||||
type: ChangeType.Insert,
|
||||
index: foundDefineConfig.getStart() + 14,
|
||||
text: `build: {
|
||||
const configNode = getConfigNode(configFileContents);
|
||||
if (!configNode) {
|
||||
notFoundWarning(configPath);
|
||||
return configFileContents;
|
||||
}
|
||||
|
||||
return applyChangesToString(configFileContents, [
|
||||
{
|
||||
type: ChangeType.Insert,
|
||||
index: configNode.getStart() + 1,
|
||||
text: `build: {
|
||||
outDir: '${outputPath}',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},`,
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
return configFileContents;
|
||||
}
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -7,13 +7,21 @@ import {
|
||||
} from '@nx/devkit';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import ts = require('typescript');
|
||||
import { getConfigNode, notFoundWarning } from '../update-vite-config';
|
||||
|
||||
export function updateTestConfig(
|
||||
configContents: string,
|
||||
projectConfig: ProjectConfiguration
|
||||
projectConfig: ProjectConfiguration,
|
||||
configPath: string
|
||||
): string {
|
||||
const configNode = getConfigNode(configContents);
|
||||
if (!configNode) {
|
||||
notFoundWarning(configPath);
|
||||
return configContents;
|
||||
}
|
||||
|
||||
const testObject = tsquery.query(
|
||||
configContents,
|
||||
configNode,
|
||||
`PropertyAssignment:has(Identifier[name="test"])`
|
||||
)?.[0];
|
||||
let testCoverageDir: ts.Node;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import {
|
||||
Tree,
|
||||
addProjectConfiguration,
|
||||
logger,
|
||||
readProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
@@ -40,6 +41,36 @@ describe('change-vite-ts-paths-plugin migration', () => {
|
||||
readProjectConfiguration(tree, 'demo3').targets.build.options.outputPath
|
||||
).toBe('dist/demo3');
|
||||
});
|
||||
|
||||
it('should convert the file correctly', async () => {
|
||||
addProject4(tree, 'demo4');
|
||||
await updateBuildDir(tree);
|
||||
expect(tree.read('demo4/vite.config.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(
|
||||
readProjectConfiguration(tree, 'demo4').targets.build.options.outputPath
|
||||
).toBe('dist/demo4');
|
||||
});
|
||||
|
||||
it('should convert the file correctly', async () => {
|
||||
addProject4(tree, 'demo4');
|
||||
await updateBuildDir(tree);
|
||||
expect(tree.read('demo4/vite.config.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(
|
||||
readProjectConfiguration(tree, 'demo4').targets.build.options.outputPath
|
||||
).toBe('dist/demo4');
|
||||
});
|
||||
|
||||
it('should show warning to the user if could not recognize config', async () => {
|
||||
jest.spyOn(logger, 'warn');
|
||||
addProject5(tree, 'demo5');
|
||||
await updateBuildDir(tree);
|
||||
expect(tree.read('demo5/vite.config.ts', 'utf-8')).toMatchSnapshot();
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
`Could not migrate your demo5/vite.config.ts file.`
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function addProject1(tree: Tree, name: string) {
|
||||
@@ -260,3 +291,103 @@ export default defineConfig({
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
function addProject4(tree: Tree, name: string) {
|
||||
addProjectConfiguration(tree, name, {
|
||||
root: `${name}`,
|
||||
sourceRoot: `${name}/src`,
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nx/vite:build',
|
||||
outputs: ['{options.outputPath}'],
|
||||
defaultConfiguration: 'production',
|
||||
options: {
|
||||
outputPath: `dist/${name}`,
|
||||
buildLibsFromSource: false,
|
||||
},
|
||||
configurations: {
|
||||
development: {
|
||||
mode: 'development',
|
||||
},
|
||||
production: {
|
||||
mode: 'production',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
tree.write(
|
||||
`${name}/vite.config.ts`,
|
||||
`
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
return {
|
||||
cacheDir: '../../node_modules/.vite/demo4',
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths()],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
},
|
||||
};
|
||||
});
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
function addProject5(tree: Tree, name: string) {
|
||||
addProjectConfiguration(tree, name, {
|
||||
root: `${name}`,
|
||||
sourceRoot: `${name}/src`,
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nx/vite:build',
|
||||
outputs: ['{options.outputPath}'],
|
||||
defaultConfiguration: 'production',
|
||||
options: {
|
||||
outputPath: `dist/${name}`,
|
||||
buildLibsFromSource: false,
|
||||
},
|
||||
configurations: {
|
||||
development: {
|
||||
mode: 'development',
|
||||
},
|
||||
production: {
|
||||
mode: 'production',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
tree.write(
|
||||
`${name}/vite.config.ts`,
|
||||
`
|
||||
// some invalid config
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { Tree, formatFiles, getProjects, joinPathFragments } from '@nx/devkit';
|
||||
import {
|
||||
Tree,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { ViteBuildExecutorOptions } from '../../executors/build/schema';
|
||||
import { updateBuildOutDirAndRoot } from './lib/edit-build-config';
|
||||
import { updateTestConfig } from './lib/edit-test-config';
|
||||
import { addFileReplacements } from './lib/add-file-replacements';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import ts = require('typescript');
|
||||
|
||||
export default async function updateBuildDir(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
@@ -25,15 +33,17 @@ export default async function updateBuildDir(tree: Tree) {
|
||||
projectConfig,
|
||||
targetName,
|
||||
tree,
|
||||
projectName
|
||||
projectName,
|
||||
config
|
||||
);
|
||||
|
||||
configContents = updateTestConfig(configContents, projectConfig);
|
||||
configContents = updateTestConfig(configContents, projectConfig, config);
|
||||
|
||||
if (options.fileReplacements?.length > 0) {
|
||||
configContents = addFileReplacements(
|
||||
configContents,
|
||||
options.fileReplacements
|
||||
options.fileReplacements,
|
||||
config
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,3 +63,34 @@ function findViteConfig(tree: Tree, searchRoot: string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getConfigNode(configFileContents: string): ts.Node | undefined {
|
||||
if (!configFileContents) {
|
||||
return;
|
||||
}
|
||||
let configNode = tsquery.query(
|
||||
configFileContents,
|
||||
`ObjectLiteralExpression`
|
||||
)?.[0];
|
||||
|
||||
const arrowFunctionReturnStatement = tsquery.query(
|
||||
configFileContents,
|
||||
`ArrowFunction Block ReturnStatement ObjectLiteralExpression`
|
||||
)?.[0];
|
||||
|
||||
if (arrowFunctionReturnStatement) {
|
||||
configNode = arrowFunctionReturnStatement;
|
||||
}
|
||||
|
||||
return configNode;
|
||||
}
|
||||
|
||||
export function notFoundWarning(configPath: string) {
|
||||
logger.warn(`
|
||||
Could not migrate your ${configPath} file.
|
||||
Please add the build.outDir and root options in your ${configPath} file.
|
||||
You can find more information on how to configure vite for Nx here:
|
||||
|
||||
https://nx.dev/recipes/vite/configure-vite
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
readTargetOptions,
|
||||
} from '@nx/devkit';
|
||||
import { existsSync } from 'fs';
|
||||
import { PreviewOptions, ServerOptions } from 'vite';
|
||||
import { ProxyOptions, ServerOptions } from 'vite';
|
||||
import { ViteDevServerExecutorOptions } from '../executors/dev-server/schema';
|
||||
|
||||
/**
|
||||
@@ -117,24 +117,16 @@ export async function getViteServerOptions(
|
||||
return serverOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the options for the vite preview server.
|
||||
*/
|
||||
export function getVitePreviewOptions(
|
||||
options: Record<string, any>,
|
||||
context: ExecutorContext
|
||||
): PreviewOptions {
|
||||
const serverOptions: ServerOptions = {};
|
||||
const proxyConfigPath = getViteServerProxyConfigPath(
|
||||
options.proxyConfig,
|
||||
context
|
||||
);
|
||||
export function getProxyConfig(
|
||||
context: ExecutorContext,
|
||||
proxyConfig?: string
|
||||
): Record<string, string | ProxyOptions> | undefined {
|
||||
const proxyConfigPath = getViteServerProxyConfigPath(proxyConfig, context);
|
||||
if (proxyConfigPath) {
|
||||
logger.info(`Loading proxy configuration from: ${proxyConfigPath}`);
|
||||
serverOptions.proxy = require(proxyConfigPath);
|
||||
return require(proxyConfigPath);
|
||||
}
|
||||
|
||||
return serverOptions;
|
||||
return;
|
||||
}
|
||||
|
||||
export function getNxTargetOptions(target: string, context: ExecutorContext) {
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
"version": "16.0.0-beta.1",
|
||||
"description": "Replace @nrwl/webpack with @nx/webpack",
|
||||
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages"
|
||||
},
|
||||
"update-17-2-1-webpack-config-setup": {
|
||||
"cli": "nx",
|
||||
"version": "17.2.1-beta.0",
|
||||
"description": "Add webpack.config.js file when webpackConfig is not defined",
|
||||
"implementation": "./src/migrations/update-17-2-1/webpack-config-setup"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
|
||||
@@ -81,7 +81,11 @@ export async function* devServerExecutor(
|
||||
|
||||
// Only add the dev server option if user is composable plugin.
|
||||
// Otherwise, user should define `devServer` option directly in their webpack config.
|
||||
if (isNxWebpackComposablePlugin(userDefinedWebpackConfig)) {
|
||||
if (
|
||||
typeof userDefinedWebpackConfig === 'function' &&
|
||||
isNxWebpackComposablePlugin(userDefinedWebpackConfig) &&
|
||||
!buildOptions.standardWebpackConfigFunction
|
||||
) {
|
||||
config = await userDefinedWebpackConfig(
|
||||
{ devServer },
|
||||
{
|
||||
|
||||
@@ -50,6 +50,7 @@ export interface WebpackExecutorOptions {
|
||||
// TODO(v18): Remove this option
|
||||
/** @deprecated set webpackConfig and provide an explicit webpack.config.js file (See: https://nx.dev/recipes/webpack/webpack-config-setup) */
|
||||
isolatedConfig?: boolean;
|
||||
standardWebpackConfigFunction?: boolean;
|
||||
main?: string;
|
||||
memoryLimit?: number;
|
||||
namedChunks?: boolean;
|
||||
|
||||
@@ -237,6 +237,11 @@
|
||||
"default": true,
|
||||
"x-deprecated": "Automatic configuration of Webpack is deprecated in favor of an explicit 'webpack.config.js' file. This option will be removed in Nx 18. See https://nx.dev/recipes/webpack/webpack-config-setup."
|
||||
},
|
||||
"standardWebpackConfigFunction": {
|
||||
"type": "boolean",
|
||||
"description": "Set to true if the webpack config exports a standard webpack function, not an Nx-specific one. See: https://webpack.js.org/configuration/configuration-types/#exporting-a-function",
|
||||
"default": false
|
||||
},
|
||||
"extractLicenses": {
|
||||
"type": "boolean",
|
||||
"description": "Extract all licenses in a separate file, in the case of production builds only."
|
||||
|
||||
@@ -23,7 +23,7 @@ import type {
|
||||
} from './schema';
|
||||
import { normalizeOptions } from './lib/normalize-options';
|
||||
import {
|
||||
composePlugins,
|
||||
composePluginsSync,
|
||||
isNxWebpackComposablePlugin,
|
||||
} from '../../utils/config';
|
||||
import { withNx } from '../../utils/with-nx';
|
||||
@@ -54,9 +54,15 @@ async function getWebpackConfigs(
|
||||
|
||||
const config = options.isolatedConfig
|
||||
? {}
|
||||
: composePlugins(withNx(options), withWeb(options));
|
||||
: (options.target === 'web'
|
||||
? composePluginsSync(withNx(options), withWeb(options))
|
||||
: withNx(options))({}, { options, context });
|
||||
|
||||
if (isNxWebpackComposablePlugin(userDefinedWebpackConfig)) {
|
||||
if (
|
||||
typeof userDefinedWebpackConfig === 'function' &&
|
||||
isNxWebpackComposablePlugin(userDefinedWebpackConfig) &&
|
||||
!options.standardWebpackConfigFunction
|
||||
) {
|
||||
// Old behavior, call the Nx-specific webpack config function that user exports
|
||||
return await userDefinedWebpackConfig(config, {
|
||||
options,
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { addProjectConfiguration, Tree } from '@nx/devkit';
|
||||
import webpackConfigSetup from './webpack-config-setup';
|
||||
|
||||
describe('17.2.1 migration (setup webpack.config file)', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it.each`
|
||||
executor
|
||||
${'@nx/webpack:webpack'}
|
||||
${'@nrwl/webpack:webpack'}
|
||||
`(
|
||||
'should create webpack.config.js for projects that did not set webpackConfig',
|
||||
async ({ executor }) => {
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'apps/myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor,
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await webpackConfigSetup(tree);
|
||||
|
||||
expect(tree.read('apps/myapp/webpack.config.js', 'utf-8'))
|
||||
.toEqual(`const { composePlugins, withNx } = require('@nx/webpack');
|
||||
|
||||
// Nx plugins for webpack.
|
||||
module.exports = composePlugins(withNx(), (config) => {
|
||||
// Note: This was added by an Nx migration. Webpack builds are required to have a corresponding Webpack config file.
|
||||
// See: https://nx.dev/recipes/webpack/webpack-config-setup
|
||||
return config;
|
||||
});
|
||||
`);
|
||||
}
|
||||
);
|
||||
|
||||
it('should not create webpack.config.js when webpackConfig is already set', async () => {
|
||||
tree.write(
|
||||
`apps/myapp/webpack.config.js`,
|
||||
`
|
||||
module.exports = { /* CUSTOM */ };
|
||||
`
|
||||
);
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'apps/myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/webpack:webpack',
|
||||
options: {
|
||||
webpackConfig: 'apps/myapp/webpack.config.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await webpackConfigSetup(tree);
|
||||
|
||||
expect(tree.read('apps/myapp/webpack.config.js', 'utf-8')).toContain(
|
||||
'/* CUSTOM */'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not create webpack.config.js when isolatedConfig is set to false', async () => {
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'apps/myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/webpack:webpack',
|
||||
options: {
|
||||
// Technically this is not possible, since isolatedConfig without webpackConfig does not work
|
||||
// Handling this edge-case anyway
|
||||
isolatedConfig: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await webpackConfigSetup(tree);
|
||||
|
||||
expect(tree.exists('apps/myapp/webpack.config.js')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
formatFiles,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { WebpackExecutorOptions } from '../../executors/webpack/schema';
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
const update = (
|
||||
options: WebpackExecutorOptions,
|
||||
projectName: string,
|
||||
targetName: string,
|
||||
configurationName: string
|
||||
) => {
|
||||
// Only handle webpack config for default configuration
|
||||
if (configurationName) return;
|
||||
|
||||
const projectConfiguration = readProjectConfiguration(tree, projectName);
|
||||
|
||||
if (!options.webpackConfig && options.isolatedConfig !== false) {
|
||||
options.webpackConfig = `${projectConfiguration.root}/webpack.config.js`;
|
||||
tree.write(
|
||||
options.webpackConfig,
|
||||
`
|
||||
const { composePlugins, withNx } = require('@nx/webpack');
|
||||
|
||||
// Nx plugins for webpack.
|
||||
module.exports = composePlugins(withNx(), (config) => {
|
||||
// Note: This was added by an Nx migration. Webpack builds are required to have a corresponding Webpack config file.
|
||||
// See: https://nx.dev/recipes/webpack/webpack-config-setup
|
||||
return config;
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
projectConfiguration.targets[targetName].options = options;
|
||||
updateProjectConfiguration(tree, projectName, projectConfiguration);
|
||||
}
|
||||
};
|
||||
|
||||
forEachExecutorOptions<WebpackExecutorOptions>(
|
||||
tree,
|
||||
'@nx/webpack:webpack',
|
||||
update
|
||||
);
|
||||
forEachExecutorOptions<WebpackExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/webpack:webpack',
|
||||
update
|
||||
);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@@ -43,6 +43,7 @@ async function addMigrationPackageGroup(
|
||||
'@angular/material',
|
||||
'@angular/cdk',
|
||||
'@angular/ssr',
|
||||
'@angular/pwa',
|
||||
].includes(pkgName)
|
||||
) {
|
||||
continue;
|
||||
|
||||
@@ -9,6 +9,7 @@ const packagesToUpdate: PackageSpec[] = [
|
||||
'@angular-devkit/build-angular',
|
||||
'@angular-devkit/core',
|
||||
'@angular-devkit/schematics',
|
||||
'@angular/pwa',
|
||||
'@angular/ssr',
|
||||
'@schematics/angular',
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user