diff --git a/docs/shared/migration/adding-to-existing-project.md b/docs/shared/migration/adding-to-existing-project.md index 698e5773d1..260e2182f5 100644 --- a/docs/shared/migration/adding-to-existing-project.md +++ b/docs/shared/migration/adding-to-existing-project.md @@ -1,6 +1,7 @@ # Adding Nx to your Existing Project -Nx can be added to any type of project, not just monorepos. The main benefit is to get caching abilities for the package scripts. Each project usually has a set of scripts in the `package.json`: +Nx can be added to any type of project, not just monorepos. The main benefit is to get caching abilities for the package +scripts. Each project usually has a set of scripts in the `package.json`: ```json {% fileName="package.json" %} { @@ -16,7 +17,8 @@ Nx can be added to any type of project, not just monorepos. The main benefit is You can make these scripts faster by leveraging Nx's caching capabilities. For example: - You change some spec files: in that case the `build` task can be cached and doesn't have to re-run. -- You update your docs, changing a couple of markdown files: then there's no need to re-run builds, tests, linting on your CI. All you might want to do is trigger the Docusaurus build. +- You update your docs, changing a couple of markdown files: then there's no need to re-run builds, tests, linting on + your CI. All you might want to do is trigger the Docusaurus build. ## Install Nx on a Non-Monorepo Project @@ -26,7 +28,55 @@ Run the following command: npx nx@latest init ``` -This will set up Nx for you - updating the `package.json` file and creating a new `nx.json` file with Nx configuration based on your answers during the set up process. The set up process will suggest installing Nx plugins that might be useful based on your existing repository. The example below is using the `@nx/eslint` and `@nx/next` plugins to run ESLint and Next.js tasks with Nx: +Running this command will ask you a few questions about your workspace and then set up Nx for you accordingly. The setup +process detects tools which are used in your workspace and suggests installing Nx plugins to integrate the tools you use +with Nx. Running those tools through Nx will have caching enabled when possible, providing you with a faster alternative +for running those tools. You can start with a few to see how it works and then add more with +the [`nx add`](/nx-api/nx/documents/add) command later. You can also decide to add them all and get the full experience +right +away because adding plugins will not break your existing workflow. + +The first thing you may notice is that Nx updates your `package.json` scripts during the setup process. Nx Plugins setup +Nx commands which run the underlying tool with caching enabled. When a `package.json` script runs a command which can be +run through Nx, Nx will replace that script in the `package.json` scripts with an Nx command that has +caching automatically enabled. Anywhere those `package.json` scripts are used, including your CI, will become faster +when possible. Let's go through an example where the `@nx/next/plugin` and `@nx/eslint/plugin` plugins are added to a +workspace with the +following `package.json`. + +```diff {% fileName="package.json" %} +{ + "name": "my-workspace", + ... + "scripts": { +- "build": "next build && echo 'Build complete'", ++ "build": "nx next:build && echo 'Build complete'", +- "lint": "eslint ./src", ++ "lint": "nx eslint:lint", + "test": "node ./run-tests.js" + }, ++ "nx": {} +} +``` + +The `@nx/next/plugin` plugin adds a `next:build` target which runs `next build` and sets up caching correctly. In other +words, running `nx next:build` is the same as running `next build` with the added benefit of it being cacheable. Hence, +Nx replaces `next build` in the `package.json` `build` script to add caching to anywhere running `npm run build`. +Similarly, `@nx/eslint/plugin` sets up the `nx eslint:lint` command to run `eslint ./src` with caching enabled. +The `test` script was not recognized by any Nx plugin, so it was left as is. After Nx has been setup, +running `npm run build` or `npm run lint` multiple times, will be instant when possible. + +You can also run any npm scripts directly through Nx with `nx build` or `nx lint` which will run the `npm run build` +and `npm run lint` scripts respectively. In the later portion of the setup flow, Nx will ask if you would like some of +those npm scripts to be cacheable. By making those cacheable, running `nx build` rather than `npm run build` will add +another layer of cacheability. However, `nx build` must be run instead of `npm run build` to take advantage of the +cache. + +## Inferred Tasks + +You may have noticed that `@nx/next` provides `dev` and `start` tasks in addition to the `next:build` task. Those tasks +were created by the `@nx/next/plugin` plugin from your existing Next.js configuration. You can see the configuration for +the Nx Plugins in `nx.json`: ```json {% fileName="nx.json" %} { @@ -34,13 +84,13 @@ This will set up Nx for you - updating the `package.json` file and creating a ne { "plugin": "@nx/eslint/plugin", "options": { - "targetName": "lint" + "targetName": "eslint:lint" } }, { "plugin": "@nx/next/plugin", "options": { - "buildTargetName": "build", + "buildTargetName": "next:build", "devTargetName": "dev", "startTargetName": "start" } @@ -49,33 +99,11 @@ This will set up Nx for you - updating the `package.json` file and creating a ne } ``` -When Nx updates your `package.json` scripts, it looks for scripts that can be replaced with an Nx command that has caching automatically enabled. The `package.json` defined above would be updated to look like this: +Each plugin can accept options to customize the projects which they create. You can see more information about +configuring the plugins on the [`@nx/next/plugin`](/nx-api/next) and [`@nx/eslint/plugin`](/nx-api/eslint) plugin pages. -```json {% fileName="package.json" %} -{ - "name": "my-workspace", - ... - "scripts": { - "build": "nx build", - "lint": "nx lint", - "test": "node ./run-tests.js" - }, - ... - "nx": { - "includedScripts": [] - } -} -``` - -The `@nx/next` plugin can run `next build` for you and set up caching correctly, so it replaces `next build` with `nx build`. Similarly, `@nx/eslint` can set up caching for `eslint ./src`. When you run `npm run build` or `npm run lint` multiple times, you'll see that caching is enabled. You can also call Nx directly from the terminal with `nx build` or `nx lint`. - -The `test` script was not recognized by any Nx plugin, so it was left as is. - -The `includedScripts` array allows you to specify `package.json` scripts that can be run with the `nx build` syntax. - -## Inferred Tasks - -You may have noticed that `@nx/next` provides `dev` and `start` tasks in addition to the `build` task. Those tasks were created by the `@nx/next` plugin from your existing Next.js configuration. To view all available tasks, open the Project Details view with Nx Console or use the terminal to launch the project details in a browser window. +To view all available tasks, open the Project Details view with Nx Console or use the terminal to launch the project +details in a browser window. ```shell nx show project my-workspace --web @@ -90,7 +118,7 @@ nx show project my-workspace --web "data": { "root": ".", "targets": { - "lint": { + "eslint:lint": { "cache": true, "options": { "cwd": ".", @@ -107,7 +135,7 @@ nx show project my-workspace --web "executor": "nx:run-commands", "configurations": {} }, - "build": { + "next:build": { "options": { "cwd": ".", "command": "next build" @@ -146,7 +174,6 @@ nx show project my-workspace --web "sourceRoot": ".", "name": "my-workspace", "projectType": "library", - "includedScripts": [], "implicitDependencies": [], "tags": [] } @@ -154,20 +181,20 @@ nx show project my-workspace --web "sourceMap": { "root": ["package.json", "nx/core/package-json-workspaces"], "targets": ["package.json", "nx/core/package-json-workspaces"], - "targets.lint": ["package.json", "@nx/eslint/plugin"], - "targets.lint.command": ["package.json", "@nx/eslint/plugin"], - "targets.lint.cache": ["package.json", "@nx/eslint/plugin"], - "targets.lint.options": ["package.json", "@nx/eslint/plugin"], - "targets.lint.inputs": ["package.json", "@nx/eslint/plugin"], - "targets.lint.options.cwd": ["package.json", "@nx/eslint/plugin"], - "targets.build": ["next.config.js", "@nx/next/plugin"], - "targets.build.command": ["next.config.js", "@nx/next/plugin"], - "targets.build.options": ["next.config.js", "@nx/next/plugin"], - "targets.build.dependsOn": ["next.config.js", "@nx/next/plugin"], - "targets.build.cache": ["next.config.js", "@nx/next/plugin"], - "targets.build.inputs": ["next.config.js", "@nx/next/plugin"], - "targets.build.outputs": ["next.config.js", "@nx/next/plugin"], - "targets.build.options.cwd": ["next.config.js", "@nx/next/plugin"], + "targets.eslint:lint": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.eslint:lint.command": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.eslint:lint.cache": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.eslint:lint.options": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.eslint:lint.inputs": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.eslint:lint.options.cwd": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.next:build": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.command": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.options": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.dependsOn": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.cache": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.inputs": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.outputs": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.options.cwd": ["next.config.js", "@nx/next/plugin"], "targets.dev": ["next.config.js", "@nx/next/plugin"], "targets.dev.command": ["next.config.js", "@nx/next/plugin"], "targets.dev.options": ["next.config.js", "@nx/next/plugin"], @@ -180,7 +207,6 @@ nx show project my-workspace --web "sourceRoot": ["package.json", "nx/core/package-json-workspaces"], "name": ["package.json", "nx/core/package-json-workspaces"], "projectType": ["package.json", "nx/core/package-json-workspaces"], - "includedScripts": ["package.json", "nx/core/package-json-workspaces"], "targets.nx-release-publish": [ "package.json", "nx/core/package-json-workspaces" @@ -203,34 +229,38 @@ nx show project my-workspace --web {% /project-details %} -The project detail view lists all available tasks, the configuration values for those tasks and where those configuration values are being set. +The project detail view lists all available tasks, the configuration values for those tasks and where those +configuration values are being set. ## Configure an Existing Script to Run with Nx If you want to run one of your existing scripts with Nx, you need to tell Nx about it. 1. Preface the script with `nx exec -- ` to have `npm run test` invoke the command with Nx. -2. Add the script to `includedScripts`. -3. Define caching settings. +2. Define caching settings. -The `nx exec` command allows you to keep using `npm test` or `npm run test` (or other package manager's alternatives) as you're accustomed to. But still get the benefits of making those operations cacheable. Configuring the `test` script from the example above to run with Nx would look something like this: +The `nx exec` command allows you to keep using `npm test` or `npm run test` (or other package manager's alternatives) as +you're accustomed to. But still get the benefits of making those operations cacheable. Configuring the `test` script +from the example above to run with Nx would look something like this: ```json {% fileName="package.json" %} { "name": "my-workspace", ... "scripts": { - "build": "nx build", - "lint": "nx lint", + "build": "nx next:build", + "lint": "nx eslint:lint", "test": "nx exec -- node ./run-tests.js" }, ... "nx": { - "includedScripts": ["test"], "targets": { "test": { "cache": "true", - "inputs": ["default", "^default"], + "inputs": [ + "default", + "^default" + ], "outputs": [] } } @@ -238,17 +268,22 @@ The `nx exec` command allows you to keep using `npm test` or `npm run test` (or } ``` -Now if you run `npm run test` or `nx test` twice, the results will be retrieved from the cache. The `inputs` used in this example are as cautious as possible, so you can significantly improve the value of the cache by [customizing Nx Inputs](/recipes/running-tasks/configure-inputs) for each task. +Now if you run `npm run test` or `nx test` twice, the results will be retrieved from the cache. The `inputs` used in +this example are as cautious as possible, so you can significantly improve the value of the cache +by [customizing Nx Inputs](/recipes/running-tasks/configure-inputs) for each task. ## Learn More {% cards %} -{% card title="Customizing Inputs and Named Inputs" description="Learn more about how to fine-tune caching with custom inputs" type="documentation" url="/recipes/running-tasks/configure-inputs" /%} +{% card title="Customizing Inputs and Named Inputs" description="Learn more about how to fine-tune caching with custom +inputs" type="documentation" url="/recipes/running-tasks/configure-inputs" /%} -{% card title="Cache Task Results" description="Learn more about how caching works" type="documentation" url="/features/cache-task-results" /%} +{% card title="Cache Task Results" description="Learn more about how caching works" type="documentation" url=" +/features/cache-task-results" /%} -{% card title="Adding Nx to NPM/Yarn/PNPM Workspace" description="Learn more about how to add Nx to an existing monorepo" type="documentation" url="/recipes/adopting-nx/adding-to-monorepo" /%} +{% card title="Adding Nx to NPM/Yarn/PNPM Workspace" description="Learn more about how to add Nx to an existing +monorepo" type="documentation" url="/recipes/adopting-nx/adding-to-monorepo" /%} {% /cards %} diff --git a/docs/shared/migration/adding-to-monorepo.md b/docs/shared/migration/adding-to-monorepo.md index 1a06ddb4b9..d49271b72d 100644 --- a/docs/shared/migration/adding-to-monorepo.md +++ b/docs/shared/migration/adding-to-monorepo.md @@ -1,10 +1,13 @@ # Adding Nx to NPM/Yarn/PNPM Workspace {% callout type="note" title="Migrating from Lerna?" %} -Interested in migrating from [Lerna](https://github.com/lerna/lerna) in particular? In case you missed it, Lerna v6 is powering Nx underneath. As a result, Lerna gets all the modern features such as caching and task pipelines. Read more on [https://lerna.js.org/upgrade](https://lerna.js.org/upgrade). +Interested in migrating from [Lerna](https://github.com/lerna/lerna) in particular? In case you missed it, Lerna v6 is +powering Nx underneath. As a result, Lerna gets all the modern features such as caching and task pipelines. Read more +on [https://lerna.js.org/upgrade](https://lerna.js.org/upgrade). {% /callout %} -Nx has first-class support for [monorepos](/getting-started/tutorials/npm-workspaces-tutorial). As a result, if you have an existing NPM/Yarn or PNPM-based monorepo setup, you can easily add Nx to get +Nx has first-class support for [monorepos](/getting-started/tutorials/npm-workspaces-tutorial). As a result, if you have +an existing NPM/Yarn or PNPM-based monorepo setup, you can easily add Nx to get - fast [task scheduling](/features/run-tasks) - support for [task pipelines](/concepts/task-pipeline-configuration) @@ -12,7 +15,8 @@ Nx has first-class support for [monorepos](/getting-started/tutorials/npm-worksp - [remote caching with Nx Cloud](/ci/features/remote-cache) - [distributed task execution with Nx Cloud](/ci/features/distribute-task-execution) -This is a low-impact operation because all that needs to be done is to install the `nx` package at the root level and add an `nx.json` for configuring caching and task pipelines. +This is a low-impact operation because all that needs to be done is to install the `nx` package at the root level and +add an `nx.json` for configuring caching and task pipelines. {% youtube src="https://www.youtube.com/embed/ngdoUQBvAjo" @@ -27,7 +31,55 @@ Run the following command to automatically set up Nx: npx nx@latest init ``` -This will set up Nx for you - updating the `package.json` file and creating a new `nx.json` file with Nx configuration based on your answers during the set up process. The set up process will suggest installing Nx plugins that might be useful based on your existing repository. The example below is using the `@nx/eslint` and `@nx/next` plugins to run ESLint and Next.js tasks with Nx: +Running this command will ask you a few questions about your workspace and then set up Nx for you accordingly. The setup +process detects tools which are used in your workspace and suggests installing Nx plugins to integrate the tools you use +with Nx. Running those tools through Nx will have caching enabled when possible, providing you with a faster alternative +for running those tools. You can start with a few to see how it works and then add more with +the [`nx add`](/nx-api/nx/documents/add) command later. You can also decide to add them all and get the full experience +right +away because adding plugins will not break your existing workflow. + +The first thing you may notice is that Nx updates your `package.json` scripts during the setup process. Nx Plugins setup +Nx commands which run the underlying tool with caching enabled. When a `package.json` script runs a command which can be +run through Nx, Nx will replace that script in the `package.json` scripts with an Nx command that has +caching automatically enabled. Anywhere those `package.json` scripts are used, including your CI, will become faster +when possible. Let's go through an example where the `@nx/next/plugin` and `@nx/eslint/plugin` plugins are added to a +workspace with the +following `package.json`. + +```diff {% fileName="package.json" %} +{ + "name": "my-workspace", + ... + "scripts": { +- "build": "next build && echo 'Build complete'", ++ "build": "nx next:build && echo 'Build complete'", +- "lint": "eslint ./src", ++ "lint": "nx eslint:lint", + "test": "node ./run-tests.js" + }, + ... +} +``` + +The `@nx/next/plugin` plugin adds a `next:build` target which runs `next build` and sets up caching correctly. In other +words, running `nx next:build` is the same as running `next build` with the added benefit of it being cacheable. Hence, +Nx replaces `next build` in the `package.json` `build` script to add caching to anywhere running `npm run build`. +Similarly, `@nx/eslint/plugin` sets up the `nx eslint:lint` command to run `eslint ./src` with caching enabled. +The `test` script was not recognized by any Nx plugin, so it was left as is. After Nx has been setup, +running `npm run build` or `npm run lint` multiple times, will be instant when possible. + +You can also run any npm scripts directly through Nx with `nx build` or `nx lint` which will run the `npm run build` +and `npm run lint` scripts respectively. In the later portion of the setup flow, Nx will ask if you would like some of +those npm scripts to be cacheable. By making those cacheable, running `nx build` rather than `npm run build` will add +another layer of cacheability. However, `nx build` must be run instead of `npm run build` to take advantage of the +cache. + +## Inferred Tasks + +You may have noticed that `@nx/next` provides `dev` and `start` tasks in addition to the `next:build` task. Those tasks +were created by the `@nx/next/plugin` plugin from your existing Next.js configuration. You can see the configuration for +the Nx Plugins in `nx.json`: ```json {% fileName="nx.json" %} { @@ -35,13 +87,13 @@ This will set up Nx for you - updating the `package.json` file and creating a ne { "plugin": "@nx/eslint/plugin", "options": { - "targetName": "lint" + "targetName": "eslint:lint" } }, { "plugin": "@nx/next/plugin", "options": { - "buildTargetName": "build", + "buildTargetName": "next:build", "devTargetName": "dev", "startTargetName": "start" } @@ -50,48 +102,11 @@ This will set up Nx for you - updating the `package.json` file and creating a ne } ``` -When Nx updates your `package.json` scripts, it looks for scripts that can be replaced with an Nx command that has caching automatically enabled. Assuming you initially had a `package.json` file looking like the following: +Each plugin can accept options to customize the projects which they create. You can see more information about +configuring the plugins on the [`@nx/next/plugin`](/nx-api/next) and [`@nx/eslint/plugin`](/nx-api/eslint) plugin pages. -```json {% fileName="package.json" %} -{ - "name": "my-workspace", - ... - "scripts": { - "build": "next build", - "lint": "eslint ./src", - "test": "node ./run-tests.js" - }, - ... -} -``` - -After setting up Nx, the `package.json` file would be updated to look like this: - -```json {% fileName="package.json" %} -{ - "name": "my-workspace", - ... - "scripts": { - "build": "nx build", - "lint": "nx lint", - "test": "node ./run-tests.js" - }, - ... - "nx": { - "includedScripts": [] - } -} -``` - -The `@nx/next` plugin can run `next build` for you and set up caching correctly, so it replaces `next build` with `nx build`. Similarly, `@nx/eslint` can set up caching for `eslint ./src`. When you run `npm run build` or `npm run lint` multiple times, you'll see that caching is enabled. You can also call Nx directly from the terminal with `nx build` or `nx lint`. - -The `test` script was not recognized by any Nx plugin, so it was left as is. - -The `includedScripts` array allows you to specify `package.json` scripts that can be run with the `nx build` syntax. - -## Inferred Tasks - -You may have noticed that `@nx/next` provides `dev` and `start` tasks in addition to the `build` task. Those tasks were created by the `@nx/next` plugin from your existing Next.js configuration. To view all available tasks, open the Project Details view with Nx Console or use the terminal to launch the project details in a browser window. +To view all available tasks, open the Project Details view with Nx Console or use the terminal to launch the project +details in a browser window. ```shell nx show project my-workspace --web @@ -106,7 +121,7 @@ nx show project my-workspace --web "data": { "root": ".", "targets": { - "lint": { + "eslint:lint": { "cache": true, "options": { "cwd": ".", @@ -123,7 +138,7 @@ nx show project my-workspace --web "executor": "nx:run-commands", "configurations": {} }, - "build": { + "next:build": { "options": { "cwd": ".", "command": "next build" @@ -162,7 +177,6 @@ nx show project my-workspace --web "sourceRoot": ".", "name": "my-workspace", "projectType": "library", - "includedScripts": [], "implicitDependencies": [], "tags": [] } @@ -170,20 +184,20 @@ nx show project my-workspace --web "sourceMap": { "root": ["package.json", "nx/core/package-json-workspaces"], "targets": ["package.json", "nx/core/package-json-workspaces"], - "targets.lint": ["package.json", "@nx/eslint/plugin"], - "targets.lint.command": ["package.json", "@nx/eslint/plugin"], - "targets.lint.cache": ["package.json", "@nx/eslint/plugin"], - "targets.lint.options": ["package.json", "@nx/eslint/plugin"], - "targets.lint.inputs": ["package.json", "@nx/eslint/plugin"], - "targets.lint.options.cwd": ["package.json", "@nx/eslint/plugin"], - "targets.build": ["next.config.js", "@nx/next/plugin"], - "targets.build.command": ["next.config.js", "@nx/next/plugin"], - "targets.build.options": ["next.config.js", "@nx/next/plugin"], - "targets.build.dependsOn": ["next.config.js", "@nx/next/plugin"], - "targets.build.cache": ["next.config.js", "@nx/next/plugin"], - "targets.build.inputs": ["next.config.js", "@nx/next/plugin"], - "targets.build.outputs": ["next.config.js", "@nx/next/plugin"], - "targets.build.options.cwd": ["next.config.js", "@nx/next/plugin"], + "targets.eslint:lint": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.eslint:lint.command": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.eslint:lint.cache": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.eslint:lint.options": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.eslint:lint.inputs": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.eslint:lint.options.cwd": [".eslintrc.json", "@nx/eslint/plugin"], + "targets.next:build": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.command": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.options": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.dependsOn": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.cache": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.inputs": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.outputs": ["next.config.js", "@nx/next/plugin"], + "targets.next:build.options.cwd": ["next.config.js", "@nx/next/plugin"], "targets.dev": ["next.config.js", "@nx/next/plugin"], "targets.dev.command": ["next.config.js", "@nx/next/plugin"], "targets.dev.options": ["next.config.js", "@nx/next/plugin"], @@ -196,7 +210,6 @@ nx show project my-workspace --web "sourceRoot": ["package.json", "nx/core/package-json-workspaces"], "name": ["package.json", "nx/core/package-json-workspaces"], "projectType": ["package.json", "nx/core/package-json-workspaces"], - "includedScripts": ["package.json", "nx/core/package-json-workspaces"], "targets.nx-release-publish": [ "package.json", "nx/core/package-json-workspaces" @@ -219,34 +232,38 @@ nx show project my-workspace --web {% /project-details %} -The project detail view lists all available tasks, the configuration values for those tasks and where those configuration values are being set. +The project detail view lists all available tasks, the configuration values for those tasks and where those +configuration values are being set. ## Configure an Existing Script to Run with Nx If you want to run one of your existing scripts with Nx, you need to tell Nx about it. 1. Preface the script with `nx exec -- ` to have `npm run test` invoke the command with Nx. -2. Add the script to `includedScripts`. -3. Define caching settings. +2. Define caching settings. -The `nx exec` command allows you to keep using `npm test` or `npm run test` (or other package manager's alternatives) as you're accustomed to. But still get the benefits of making those operations cacheable. Configuring the `test` script from the example above to run with Nx would look something like this: +The `nx exec` command allows you to keep using `npm test` or `npm run test` (or other package manager's alternatives) as +you're accustomed to. But still get the benefits of making those operations cacheable. Configuring the `test` script +from the example above to run with Nx would look something like this: ```json {% fileName="package.json" %} { "name": "my-workspace", ... "scripts": { - "build": "nx build", - "lint": "nx lint", + "build": "nx next:build", + "lint": "nx eslint:lint", "test": "nx exec -- node ./run-tests.js" }, ... "nx": { - "includedScripts": ["test"], "targets": { "test": { "cache": "true", - "inputs": ["default", "^default"], + "inputs": [ + "default", + "^default" + ], "outputs": [] } } @@ -254,11 +271,14 @@ The `nx exec` command allows you to keep using `npm test` or `npm run test` (or } ``` -Now if you run `npm run test` or `nx test` twice, the results will be retrieved from the cache. The `inputs` used in this example are as cautious as possible, so you can significantly improve the value of the cache by [customizing Nx Inputs](/recipes/running-tasks/configure-inputs) for each task. +Now if you run `npm run test` or `nx test` twice, the results will be retrieved from the cache. The `inputs` used in +this example are as cautious as possible, so you can significantly improve the value of the cache +by [customizing Nx Inputs](/recipes/running-tasks/configure-inputs) for each task. ## Incrementally Adopting Nx -All the features of Nx can be enabled independently of each other. Hence, Nx can easily be adopted incrementally by initially using Nx just for a subset of your scripts and then gradually adding more. +All the features of Nx can be enabled independently of each other. Hence, Nx can easily be adopted incrementally by +initially using Nx just for a subset of your scripts and then gradually adding more. For example, use Nx to run your builds: @@ -266,7 +286,8 @@ For example, use Nx to run your builds: npx nx run-many -t build ``` -But instead keep using NPM/Yarn/PNPM workspace commands for your tests and other scripts. Here's an example of using PNPM commands to run tests across packages +But instead keep using NPM/Yarn/PNPM workspace commands for your tests and other scripts. Here's an example of using +PNPM commands to run tests across packages ```shell pnpm run -r test @@ -278,14 +299,19 @@ This allows for incrementally adopting Nx in your existing workspace. {% cards %} -{% card title="Cache Task Results" description="Learn more about how caching works" type="documentation" url="/features/cache-task-results" /%} +{% card title="Cache Task Results" description="Learn more about how caching works" type="documentation" url=" +/features/cache-task-results" /%} -{% card title="Task Pipeline Configuration" description="Learn more about how to setup task dependencies" type="documentation" url="/concepts/task-pipeline-configuration" /%} +{% card title="Task Pipeline Configuration" description="Learn more about how to setup task dependencies" type=" +documentation" url="/concepts/task-pipeline-configuration" /%} -{% card title="Nx Ignore" description="Learn about how to ignore certain projects using .nxignore" type="documentation" url="/reference/nxignore" /%} +{% card title="Nx Ignore" description="Learn about how to ignore certain projects using .nxignore" type="documentation" +url="/reference/nxignore" /%} -{% card title="Nx and Turbo" description="Read about how Nx compares to Turborepo" url="/concepts/more-concepts/turbo-and-nx" /%} +{% card title="Nx and Turbo" description="Read about how Nx compares to Turborepo" url=" +/concepts/more-concepts/turbo-and-nx" /%} -{% card title="Integrated Repos vs Package-Based Repos" description="Learn about two styles of monorepos." url="/concepts/integrated-vs-package-based" /%} +{% card title="Integrated Repos vs Package-Based Repos" description="Learn about two styles of monorepos." url=" +/concepts/integrated-vs-package-based" /%} {% /cards %} diff --git a/e2e/detox/src/detox-legacy.test.ts b/e2e/detox/src/detox-legacy.test.ts index 5a8dd5bf88..405da62cfa 100644 --- a/e2e/detox/src/detox-legacy.test.ts +++ b/e2e/detox/src/detox-legacy.test.ts @@ -11,17 +11,22 @@ import { describe('@nx/detox (legacy)', () => { const appName = uniq('myapp'); + let originalEnv: string; beforeAll(() => { + originalEnv = process.env.NX_ADD_PLUGINS; + process.env.NX_ADD_PLUGINS = 'false'; newProject(); }); - afterAll(() => cleanupProject()); + afterAll(() => { + process.env.NX_ADD_PLUGINS = originalEnv; + cleanupProject(); + }); it('should create files and run lint command for react-native apps', async () => { runCLI( - `generate @nx/react-native:app ${appName} --e2eTestRunner=detox --linter=eslint --install=false`, - { env: { NX_ADD_PLUGINS: 'false' } } + `generate @nx/react-native:app ${appName} --e2eTestRunner=detox --linter=eslint --install=false` ); checkFilesExist(`apps/${appName}-e2e/.detoxrc.json`); checkFilesExist(`apps/${appName}-e2e/tsconfig.json`); @@ -38,8 +43,7 @@ describe('@nx/detox (legacy)', () => { it('should create files and run lint command for expo apps', async () => { const expoAppName = uniq('myapp'); runCLI( - `generate @nx/expo:app ${expoAppName} --e2eTestRunner=detox --linter=eslint`, - { env: { NX_ADD_PLUGINS: 'false' } } + `generate @nx/expo:app ${expoAppName} --e2eTestRunner=detox --linter=eslint` ); checkFilesExist(`apps/${expoAppName}-e2e/.detoxrc.json`); checkFilesExist(`apps/${expoAppName}-e2e/tsconfig.json`); @@ -57,8 +61,7 @@ describe('@nx/detox (legacy)', () => { const appName = uniq('app1'); runCLI( - `generate @nx/react-native:app ${appName} --e2eTestRunner=detox --linter=eslint --install=false --project-name-and-root-format=as-provided --interactive=false`, - { env: { NX_ADD_PLUGINS: 'false' } } + `generate @nx/react-native:app ${appName} --e2eTestRunner=detox --linter=eslint --install=false --project-name-and-root-format=as-provided --interactive=false` ); // check files are generated without the layout directory ("apps/") and diff --git a/e2e/expo/src/expo-legacy.test.ts b/e2e/expo/src/expo-legacy.test.ts index 2ba7579f4d..b33e7d54c5 100644 --- a/e2e/expo/src/expo-legacy.test.ts +++ b/e2e/expo/src/expo-legacy.test.ts @@ -23,10 +23,15 @@ describe('@nx/expo (legacy)', () => { let proj: string; let appName = uniq('my-app'); let libName = uniq('lib'); + let originalEnv: string; beforeAll(() => { proj = newProject({ packages: ['@nx/expo'] }); // we create empty preset above which skips creation of `production` named input + + originalEnv = process.env.NX_ADD_PLUGINS; + process.env.NX_ADD_PLUGINS = 'false'; + updateJson('nx.json', (nxJson) => { nxJson.namedInputs = { default: ['{projectRoot}/**/*', 'sharedGlobals'], @@ -36,14 +41,16 @@ describe('@nx/expo (legacy)', () => { return nxJson; }); runCLI( - `generate @nx/expo:application ${appName} --e2eTestRunner=cypress --no-interactive`, - { env: { NX_ADD_PLUGINS: 'false' } } + `generate @nx/expo:application ${appName} --e2eTestRunner=cypress --no-interactive` ); runCLI( `generate @nx/expo:library ${libName} --buildable --publishable --importPath=${proj}/${libName}` ); }); - afterAll(() => cleanupProject()); + afterAll(() => { + process.env.NX_ADD_PLUGINS = originalEnv; + cleanupProject(); + }); it('should test and lint', async () => { const componentName = uniq('Component'); @@ -193,8 +200,7 @@ describe('@nx/expo (legacy)', () => { const libName = uniq('@my-org/lib1'); runCLI( - `generate @nx/expo:application ${appName} --project-name-and-root-format=as-provided --no-interactive`, - { env: { NX_ADD_PLUGINS: 'false' } } + `generate @nx/expo:application ${appName} --project-name-and-root-format=as-provided --no-interactive` ); // check files are generated without the layout directory ("apps/") and @@ -268,8 +274,7 @@ describe('@nx/expo (legacy)', () => { it('should run e2e for playwright', async () => { const appName2 = uniq('my-app'); runCLI( - `generate @nx/expo:application ${appName2} --e2eTestRunner=playwright --no-interactive`, - { env: { NX_ADD_PLUGINS: 'false' } } + `generate @nx/expo:application ${appName2} --e2eTestRunner=playwright --no-interactive` ); if (runE2ETests()) { const results = runCLI(`e2e ${appName2}-e2e`, { verbose: true }); diff --git a/e2e/react-native/src/react-native-legacy.test.ts b/e2e/react-native/src/react-native-legacy.test.ts index eae99ada9b..a4b81d2c49 100644 --- a/e2e/react-native/src/react-native-legacy.test.ts +++ b/e2e/react-native/src/react-native-legacy.test.ts @@ -23,8 +23,12 @@ describe('@nx/react-native (legacy)', () => { let proj: string; let appName = uniq('my-app'); let libName = uniq('lib'); + let originalEnv: string; beforeAll(() => { + originalEnv = process.env.NX_ADD_PLUGINS; + process.env.NX_ADD_PLUGINS = 'false'; + proj = newProject(); // we create empty preset above which skips creation of `production` named input updateJson('nx.json', (nxJson) => { @@ -36,14 +40,16 @@ describe('@nx/react-native (legacy)', () => { return nxJson; }); runCLI( - `generate @nx/react-native:application ${appName} --bunlder=webpack --e2eTestRunner=cypress --install=false --no-interactive`, - { env: { NX_ADD_PLUGINS: 'false' } } + `generate @nx/react-native:application ${appName} --bunlder=webpack --e2eTestRunner=cypress --install=false --no-interactive` ); runCLI( `generate @nx/react-native:library ${libName} --buildable --publishable --importPath=${proj}/${libName} --no-interactive` ); }); - afterAll(() => cleanupProject()); + afterAll(() => { + process.env.NX_ADD_PLUGINS = originalEnv; + cleanupProject(); + }); it('should build for web', async () => { const results = runCLI(`build ${appName}`); @@ -269,8 +275,7 @@ describe('@nx/react-native (legacy)', () => { const libName = uniq('@my-org/lib1'); runCLI( - `generate @nx/react-native:application ${appName} --project-name-and-root-format=as-provided --install=false --no-interactive`, - { env: { NX_ADD_PLUGINS: 'false' } } + `generate @nx/react-native:application ${appName} --project-name-and-root-format=as-provided --install=false --no-interactive` ); // check files are generated without the layout directory ("apps/") and @@ -306,8 +311,7 @@ describe('@nx/react-native (legacy)', () => { it('should run build with vite bundler and e2e with playwright', async () => { const appName2 = uniq('my-app'); runCLI( - `generate @nx/react-native:application ${appName2} --bundler=vite --e2eTestRunner=playwright --install=false --no-interactive`, - { env: { NX_ADD_PLUGINS: 'false' } } + `generate @nx/react-native:application ${appName2} --bundler=vite --e2eTestRunner=playwright --install=false --no-interactive` ); const buildResults = runCLI(`build ${appName2}`); expect(buildResults).toContain('Successfully ran target build'); diff --git a/packages/angular/plugins/component-testing.ts b/packages/angular/plugins/component-testing.ts index 0ddd3a126f..25c385d2b6 100644 --- a/packages/angular/plugins/component-testing.ts +++ b/packages/angular/plugins/component-testing.ts @@ -47,7 +47,7 @@ export function nxComponentTestingPreset( pathToConfig: string, options?: NxComponentTestingOptions ) { - if (global.NX_GRAPH_CREATION || global.NX_CYPRESS_INIT_GENERATOR_RUNNING) { + if (global.NX_GRAPH_CREATION) { // this is only used by plugins, so we don't need the component testing // options, cast to any to avoid type errors return nxBaseCypressPreset(pathToConfig, { diff --git a/packages/angular/src/generators/add-linting/add-linting.spec.ts b/packages/angular/src/generators/add-linting/add-linting.spec.ts index a2ad38741a..140b17f100 100644 --- a/packages/angular/src/generators/add-linting/add-linting.spec.ts +++ b/packages/angular/src/generators/add-linting/add-linting.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { ProjectConfiguration, Tree, diff --git a/packages/angular/src/generators/application/application.spec.ts b/packages/angular/src/generators/application/application.spec.ts index f9a00cb4a3..514129d1c5 100644 --- a/packages/angular/src/generators/application/application.spec.ts +++ b/packages/angular/src/generators/application/application.spec.ts @@ -32,6 +32,10 @@ jest.mock('@nx/devkit', () => { return { ...original, ensurePackage: (pkg: string) => jest.requireActual(pkg), + createProjectGraphAsync: jest.fn().mockResolvedValue({ + nodes: {}, + dependencies: {}, + }), }; }); diff --git a/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.spec.ts b/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.spec.ts index 45c69d06da..db2d419fb5 100644 --- a/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.spec.ts +++ b/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import type { Tree } from '@nx/devkit'; import * as devkit from '@nx/devkit'; diff --git a/packages/angular/src/generators/component-story/component-story.spec.ts b/packages/angular/src/generators/component-story/component-story.spec.ts index d46a08fae0..a18c072b0c 100644 --- a/packages/angular/src/generators/component-story/component-story.spec.ts +++ b/packages/angular/src/generators/component-story/component-story.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import type { Tree } from '@nx/devkit'; import * as devkit from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/generators/component-test/component-test.spec.ts b/packages/angular/src/generators/component-test/component-test.spec.ts index 0a56ee6248..190dce7ee2 100644 --- a/packages/angular/src/generators/component-test/component-test.spec.ts +++ b/packages/angular/src/generators/component-test/component-test.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { assertMinimumCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts b/packages/angular/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts index 605fa1c11c..0a318c4b0c 100644 --- a/packages/angular/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts +++ b/packages/angular/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts @@ -9,19 +9,21 @@ import { updateProjectConfiguration, } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; -import { componentGenerator } from '../component/component'; -import { librarySecondaryEntryPointGenerator } from '../library-secondary-entry-point/library-secondary-entry-point'; -import { generateTestApplication, generateTestLibrary } from '../utils/testing'; -import { cypressComponentConfiguration } from './cypress-component-configuration'; let projectGraph: ProjectGraph = { nodes: {}, dependencies: {} }; -jest.mock('@nx/cypress/src/utils/cypress-version'); jest.mock('@nx/devkit', () => ({ ...jest.requireActual('@nx/devkit'), createProjectGraphAsync: jest .fn() .mockImplementation(async () => projectGraph), })); + +import { componentGenerator } from '../component/component'; +import { librarySecondaryEntryPointGenerator } from '../library-secondary-entry-point/library-secondary-entry-point'; +import { generateTestApplication, generateTestLibrary } from '../utils/testing'; +import { cypressComponentConfiguration } from './cypress-component-configuration'; + +jest.mock('@nx/cypress/src/utils/cypress-version'); // nested code imports graph from the repo, which might have innacurate graph version jest.mock('nx/src/project-graph/project-graph', () => ({ ...jest.requireActual('nx/src/project-graph/project-graph'), diff --git a/packages/angular/src/generators/federate-module/federate-module.spec.ts b/packages/angular/src/generators/federate-module/federate-module.spec.ts index 94458d6bef..b0f1722046 100644 --- a/packages/angular/src/generators/federate-module/federate-module.spec.ts +++ b/packages/angular/src/generators/federate-module/federate-module.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { getProjects } from '@nx/devkit'; import { Schema } from './schema'; import { Schema as remoteSchma } from '../remote/schema'; diff --git a/packages/angular/src/generators/host/host.spec.ts b/packages/angular/src/generators/host/host.spec.ts index 183b9c260d..18d8eb53f9 100644 --- a/packages/angular/src/generators/host/host.spec.ts +++ b/packages/angular/src/generators/host/host.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, updateJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { diff --git a/packages/angular/src/generators/library-secondary-entry-point/library-secondary-entry-point.spec.ts b/packages/angular/src/generators/library-secondary-entry-point/library-secondary-entry-point.spec.ts index 987a46c3c0..1c6f10309c 100644 --- a/packages/angular/src/generators/library-secondary-entry-point/library-secondary-entry-point.spec.ts +++ b/packages/angular/src/generators/library-secondary-entry-point/library-secondary-entry-point.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import * as devkit from '@nx/devkit'; import { addProjectConfiguration, readJson, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/generators/library/library.spec.ts b/packages/angular/src/generators/library/library.spec.ts index 19dd1eb31e..5dfa64c650 100644 --- a/packages/angular/src/generators/library/library.spec.ts +++ b/packages/angular/src/generators/library/library.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { getProjects, NxJsonConfiguration, diff --git a/packages/angular/src/generators/move/move.spec.ts b/packages/angular/src/generators/move/move.spec.ts index 34c978ae1f..2cddb74e85 100644 --- a/packages/angular/src/generators/move/move.spec.ts +++ b/packages/angular/src/generators/move/move.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import * as devkit from '@nx/devkit'; import { ProjectGraph, readJson, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/generators/ng-add/migrate-from-angular-cli.spec.ts b/packages/angular/src/generators/ng-add/migrate-from-angular-cli.spec.ts index 008d41f2cc..14d19e00f5 100644 --- a/packages/angular/src/generators/ng-add/migrate-from-angular-cli.spec.ts +++ b/packages/angular/src/generators/ng-add/migrate-from-angular-cli.spec.ts @@ -1,4 +1,6 @@ -import type { Tree } from '@nx/devkit'; +import 'nx/src/internal-testing-utils/mock-project-graph'; + +import { Tree } from '@nx/devkit'; import { readJson, readProjectConfiguration, diff --git a/packages/angular/src/generators/ng-add/migrators/projects/app.migrator.spec.ts b/packages/angular/src/generators/ng-add/migrators/projects/app.migrator.spec.ts index 75f9d4f527..9be7711bf1 100644 --- a/packages/angular/src/generators/ng-add/migrators/projects/app.migrator.spec.ts +++ b/packages/angular/src/generators/ng-add/migrators/projects/app.migrator.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import type { ProjectConfiguration, TargetConfiguration, diff --git a/packages/angular/src/generators/ng-add/migrators/projects/e2e.migrator.spec.ts b/packages/angular/src/generators/ng-add/migrators/projects/e2e.migrator.spec.ts index a136262d2e..41573440e2 100644 --- a/packages/angular/src/generators/ng-add/migrators/projects/e2e.migrator.spec.ts +++ b/packages/angular/src/generators/ng-add/migrators/projects/e2e.migrator.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + // mock so we can test multiple versions jest.mock('@nx/cypress/src/utils/cypress-version'); // mock bc the nxE2EPreset uses fs for path normalization diff --git a/packages/angular/src/generators/ngrx-feature-store/ngrx-feature-store.spec.ts b/packages/angular/src/generators/ngrx-feature-store/ngrx-feature-store.spec.ts index e6769a768d..df0332ea9f 100644 --- a/packages/angular/src/generators/ngrx-feature-store/ngrx-feature-store.spec.ts +++ b/packages/angular/src/generators/ngrx-feature-store/ngrx-feature-store.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import type { Tree } from '@nx/devkit'; import { readJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/generators/ngrx-root-store/ngrx-root-store.spec.ts b/packages/angular/src/generators/ngrx-root-store/ngrx-root-store.spec.ts index 2c65bcd349..7148489abe 100644 --- a/packages/angular/src/generators/ngrx-root-store/ngrx-root-store.spec.ts +++ b/packages/angular/src/generators/ngrx-root-store/ngrx-root-store.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import type { Tree } from '@nx/devkit'; import { readJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/generators/ngrx/ngrx.spec.ts b/packages/angular/src/generators/ngrx/ngrx.spec.ts index b55f0335dc..72b2e3ea32 100644 --- a/packages/angular/src/generators/ngrx/ngrx.spec.ts +++ b/packages/angular/src/generators/ngrx/ngrx.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import type { Tree } from '@nx/devkit'; import * as devkit from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/generators/remote/remote.spec.ts b/packages/angular/src/generators/remote/remote.spec.ts index a60faeb650..8e2fbb7868 100644 --- a/packages/angular/src/generators/remote/remote.spec.ts +++ b/packages/angular/src/generators/remote/remote.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { E2eTestRunner } from '../../utils/test-runners'; import { getProjects, diff --git a/packages/angular/src/generators/scam-to-standalone/scam-to-standalone.spec.ts b/packages/angular/src/generators/scam-to-standalone/scam-to-standalone.spec.ts index a49a80b5fd..87e89d3007 100644 --- a/packages/angular/src/generators/scam-to-standalone/scam-to-standalone.spec.ts +++ b/packages/angular/src/generators/scam-to-standalone/scam-to-standalone.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import scamGenerator from '../scam/scam'; import { generateTestApplication } from '../utils/testing'; diff --git a/packages/angular/src/generators/setup-ssr/setup-ssr.spec.ts b/packages/angular/src/generators/setup-ssr/setup-ssr.spec.ts index a49a6433af..fb18673bf5 100644 --- a/packages/angular/src/generators/setup-ssr/setup-ssr.spec.ts +++ b/packages/angular/src/generators/setup-ssr/setup-ssr.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { NxJsonConfiguration, readJson, diff --git a/packages/angular/src/generators/stories/stories-app.spec.ts b/packages/angular/src/generators/stories/stories-app.spec.ts index fb924e816b..7d67280605 100644 --- a/packages/angular/src/generators/stories/stories-app.spec.ts +++ b/packages/angular/src/generators/stories/stories-app.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import type { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/generators/stories/stories-lib.spec.ts b/packages/angular/src/generators/stories/stories-lib.spec.ts index 77b6831003..0d47aa130c 100644 --- a/packages/angular/src/generators/stories/stories-lib.spec.ts +++ b/packages/angular/src/generators/stories/stories-lib.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { Tree } from '@nx/devkit'; import { writeJson } from '@nx/devkit'; diff --git a/packages/angular/src/generators/web-worker/web-worker.spec.ts b/packages/angular/src/generators/web-worker/web-worker.spec.ts index b250f789f1..fc298a45d9 100644 --- a/packages/angular/src/generators/web-worker/web-worker.spec.ts +++ b/packages/angular/src/generators/web-worker/web-worker.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import type { Tree } from '@nx/devkit'; import * as devkit from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/migrations/update-14-8-0/rename-webpack-server.spec.ts b/packages/angular/src/migrations/update-14-8-0/rename-webpack-server.spec.ts index afee042506..707a28f9ed 100644 --- a/packages/angular/src/migrations/update-14-8-0/rename-webpack-server.spec.ts +++ b/packages/angular/src/migrations/update-14-8-0/rename-webpack-server.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, updateJson } from '@nx/devkit'; import * as devkit from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/migrations/update-15-2-0/remove-browserlist-config.spec.ts b/packages/angular/src/migrations/update-15-2-0/remove-browserlist-config.spec.ts index a543dc79cb..9be54c6387 100644 --- a/packages/angular/src/migrations/update-15-2-0/remove-browserlist-config.spec.ts +++ b/packages/angular/src/migrations/update-15-2-0/remove-browserlist-config.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import type { Tree } from '@nx/devkit'; import * as devkit from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/angular/src/migrations/update-15-2-0/remove-browserlist-config.ts b/packages/angular/src/migrations/update-15-2-0/remove-browserlist-config.ts index f24952a52d..94dabd5a94 100644 --- a/packages/angular/src/migrations/update-15-2-0/remove-browserlist-config.ts +++ b/packages/angular/src/migrations/update-15-2-0/remove-browserlist-config.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import type { Tree } from '@nx/devkit'; import { logger, visitNotIgnoredFiles } from '@nx/devkit'; import { basename } from 'path'; diff --git a/packages/angular/src/migrations/update-15-2-0/update-typescript-target.spec.ts b/packages/angular/src/migrations/update-15-2-0/update-typescript-target.spec.ts index d41cf1c8cb..46b5d45687 100644 --- a/packages/angular/src/migrations/update-15-2-0/update-typescript-target.spec.ts +++ b/packages/angular/src/migrations/update-15-2-0/update-typescript-target.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, readProjectConfiguration, diff --git a/packages/angular/src/migrations/update-15-2-0/update-workspace-config.spec.ts b/packages/angular/src/migrations/update-15-2-0/update-workspace-config.spec.ts index a2d8e4a51b..3c5967bd06 100644 --- a/packages/angular/src/migrations/update-15-2-0/update-workspace-config.spec.ts +++ b/packages/angular/src/migrations/update-15-2-0/update-workspace-config.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import * as devkit from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Builders } from '@schematics/angular/utility/workspace-models'; diff --git a/packages/cypress/src/generators/component-configuration/component-configuration.spec.ts b/packages/cypress/src/generators/component-configuration/component-configuration.spec.ts index 01267532c4..cc24a75fcc 100644 --- a/packages/cypress/src/generators/component-configuration/component-configuration.spec.ts +++ b/packages/cypress/src/generators/component-configuration/component-configuration.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, ProjectConfiguration, diff --git a/packages/cypress/src/generators/configuration/configuration.spec.ts b/packages/cypress/src/generators/configuration/configuration.spec.ts index 703118ae01..449b1e22b3 100644 --- a/packages/cypress/src/generators/configuration/configuration.spec.ts +++ b/packages/cypress/src/generators/configuration/configuration.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, ProjectConfiguration, diff --git a/packages/cypress/src/generators/configuration/configuration.ts b/packages/cypress/src/generators/configuration/configuration.ts index 80049da8e5..8635ee5f96 100644 --- a/packages/cypress/src/generators/configuration/configuration.ts +++ b/packages/cypress/src/generators/configuration/configuration.ts @@ -70,6 +70,7 @@ export async function configurationGeneratorInternal( opts.addPlugin ??= process.env.NX_ADD_PLUGINS !== 'false'; const tasks: GeneratorCallback[] = []; + const projectGraph = await createProjectGraphAsync(); if (!installedCypressVersion()) { tasks.push(await jsInitGenerator(tree, { ...options, skipFormat: true })); tasks.push( @@ -79,10 +80,9 @@ export async function configurationGeneratorInternal( }) ); } else if (opts.addPlugin) { - addPlugin(tree); + await addPlugin(tree, projectGraph, false); } - const projectGraph = await createProjectGraphAsync(); const nxJson = readNxJson(tree); const hasPlugin = nxJson.plugins?.some((p) => typeof p === 'string' diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts b/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts index 88c80119fa..f9f811c116 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, readJson, diff --git a/packages/cypress/src/generators/init/init.spec.ts b/packages/cypress/src/generators/init/init.spec.ts index 0b43f78e4a..d75397a3a3 100644 --- a/packages/cypress/src/generators/init/init.spec.ts +++ b/packages/cypress/src/generators/init/init.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { NxJsonConfiguration, readJson, Tree, updateJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/cypress/src/generators/init/init.ts b/packages/cypress/src/generators/init/init.ts index f4b0fafc86..f4d633d870 100644 --- a/packages/cypress/src/generators/init/init.ts +++ b/packages/cypress/src/generators/init/init.ts @@ -1,14 +1,19 @@ import { addDependenciesToPackageJson, + createProjectGraphAsync, formatFiles, GeneratorCallback, + ProjectGraph, readNxJson, removeDependenciesFromPackageJson, runTasksInSerial, Tree, updateNxJson, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; +import { + addPlugin as _addPlugin, + generateCombinations, +} from '@nx/devkit/src/utils/add-plugin'; import { createNodes } from '../../plugins/plugin'; import { cypressVersion, nxVersion } from '../../utils/versions'; import { Schema } from './schema'; @@ -55,30 +60,28 @@ function updateDependencies(tree: Tree, options: Schema) { return runTasksInSerial(...tasks); } -export function addPlugin(tree: Tree) { - const nxJson = readNxJson(tree); - nxJson.plugins ??= []; - - for (const plugin of nxJson.plugins) { - if ( - typeof plugin === 'string' - ? plugin === '@nx/cypress/plugin' - : plugin.plugin === '@nx/cypress/plugin' - ) { - return; - } - } - - nxJson.plugins.push({ - plugin: '@nx/cypress/plugin', - options: { - targetName: 'e2e', - componentTestingTargetName: 'component-test', - ciTargetName: 'e2e-ci', - openTargetName: 'open-cypress', - } as CypressPluginOptions, - }); - updateNxJson(tree, nxJson); +export function addPlugin( + tree: Tree, + graph: ProjectGraph, + updatePackageScripts: boolean +) { + return _addPlugin( + tree, + graph, + '@nx/cypress/plugin', + createNodes, + { + targetName: ['e2e', 'cypress:e2e', 'cypress-e2e'], + openTargetName: ['open-cypress', 'cypress-open'], + componentTestingTargetName: [ + 'component-test', + 'cypress:component-test', + 'cypress-component-test', + ], + ciTargetName: ['e2e-ci', 'cypress:e2e-ci', 'cypress-e2e-ci'], + }, + updatePackageScripts + ); } function updateProductionFileset(tree: Tree) { @@ -115,7 +118,11 @@ export async function cypressInitGeneratorInternal( nxJson.useInferencePlugins !== false; if (options.addPlugin) { - addPlugin(tree); + await addPlugin( + tree, + await createProjectGraphAsync(), + options.updatePackageScripts + ); } else { setupE2ETargetDefaults(tree); } @@ -125,12 +132,6 @@ export async function cypressInitGeneratorInternal( installTask = updateDependencies(tree, options); } - if (options.updatePackageScripts) { - global.NX_CYPRESS_INIT_GENERATOR_RUNNING = true; - await updatePackageScripts(tree, createNodes); - global.NX_CYPRESS_INIT_GENERATOR_RUNNING = false; - } - if (!options.skipFormat) { await formatFiles(tree); } diff --git a/packages/cypress/src/generators/migrate-to-cypress-11/migrate-to-cypress-11.spec.ts b/packages/cypress/src/generators/migrate-to-cypress-11/migrate-to-cypress-11.spec.ts index 096c504355..99a2a7c040 100644 --- a/packages/cypress/src/generators/migrate-to-cypress-11/migrate-to-cypress-11.spec.ts +++ b/packages/cypress/src/generators/migrate-to-cypress-11/migrate-to-cypress-11.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, joinPathFragments, diff --git a/packages/cypress/src/migrations/update-15-1-0/cypress-11.spec.ts b/packages/cypress/src/migrations/update-15-1-0/cypress-11.spec.ts index ee3659ff76..ec83540f2f 100644 --- a/packages/cypress/src/migrations/update-15-1-0/cypress-11.spec.ts +++ b/packages/cypress/src/migrations/update-15-1-0/cypress-11.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, readProjectConfiguration, @@ -8,6 +10,7 @@ import { libraryGenerator } from '@nx/js'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import updateToCypress11 from './cypress-11'; import { installedCypressVersion } from '../../utils/cypress-version'; + jest.mock('../../utils/cypress-version'); import cypressComponentConfiguration from '../../generators/component-configuration/component-configuration'; @@ -21,7 +24,6 @@ describe('Cypress 11 Migration', () => { beforeEach(() => { tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); - jest.resetAllMocks(); }); it('should not update if cypress { diff --git a/packages/cypress/src/migrations/update-16-2-0/update-cy-tsconfig.spec.ts b/packages/cypress/src/migrations/update-16-2-0/update-cy-tsconfig.spec.ts index 1184993efa..c5c36ad25f 100644 --- a/packages/cypress/src/migrations/update-16-2-0/update-cy-tsconfig.spec.ts +++ b/packages/cypress/src/migrations/update-16-2-0/update-cy-tsconfig.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree, readJson, diff --git a/packages/detox/src/generators/application/application.spec.ts b/packages/detox/src/generators/application/application.spec.ts index 7131940ee8..b0e1ee0e5b 100644 --- a/packages/detox/src/generators/application/application.spec.ts +++ b/packages/detox/src/generators/application/application.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, readJson, diff --git a/packages/detox/src/generators/init/init.spec.ts b/packages/detox/src/generators/init/init.spec.ts index cdf2c4a801..5b7f508f0b 100644 --- a/packages/detox/src/generators/init/init.spec.ts +++ b/packages/detox/src/generators/init/init.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree, readJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { detoxInitGenerator } from './init'; diff --git a/packages/detox/src/generators/init/init.ts b/packages/detox/src/generators/init/init.ts index ac48480f3c..8753d6ecf9 100644 --- a/packages/detox/src/generators/init/init.ts +++ b/packages/detox/src/generators/init/init.ts @@ -1,14 +1,17 @@ import { addDependenciesToPackageJson, + createProjectGraphAsync, formatFiles, GeneratorCallback, readNxJson, removeDependenciesFromPackageJson, runTasksInSerial, Tree, - updateNxJson, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; +import { + addPlugin, + generateCombinations, +} from '@nx/devkit/src/utils/add-plugin'; import { createNodes, DetoxPluginOptions } from '../../plugins/plugin'; import { detoxVersion, nxVersion } from '../../utils/versions'; import { Schema } from './schema'; @@ -33,11 +36,18 @@ export async function detoxInitGeneratorInternal(host: Tree, schema: Schema) { } if (schema.addPlugin) { - addPlugin(host); - } - - if (schema.updatePackageScripts) { - await updatePackageScripts(host, createNodes); + await addPlugin( + host, + await createProjectGraphAsync(), + '@nx/detox/plugin', + createNodes, + { + buildTargetName: ['build', 'detox:build', 'detox-build'], + startTargetName: ['start', 'detox:start', 'detox-start'], + testTargetName: ['test', 'detox:test', 'detox-test'], + }, + schema.updatePackageScripts + ); } if (!schema.skipFormat) { @@ -64,29 +74,4 @@ function moveDependency(host: Tree) { return removeDependenciesFromPackageJson(host, ['@nx/detox'], []); } -function addPlugin(host: Tree) { - const nxJson = readNxJson(host); - nxJson.plugins ??= []; - - for (const plugin of nxJson.plugins) { - if ( - typeof plugin === 'string' - ? plugin === '@nx/detox/plugin' - : plugin.plugin === '@nx/detox/plugin' - ) { - return; - } - } - - nxJson.plugins.push({ - plugin: '@nx/detox/plugin', - options: { - buildTargetName: 'build', - startTargetName: 'start', - testTargetName: 'test', - } as DetoxPluginOptions, - }); - updateNxJson(host, nxJson); -} - export default detoxInitGenerator; diff --git a/packages/devkit/src/utils/add-plugin.spec.ts b/packages/devkit/src/utils/add-plugin.spec.ts new file mode 100644 index 0000000000..10fad482fc --- /dev/null +++ b/packages/devkit/src/utils/add-plugin.spec.ts @@ -0,0 +1,470 @@ +import { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace'; +import type { Tree } from 'nx/src/generators/tree'; +import { readJson, writeJson } from 'nx/src/generators/utils/json'; +import type { PackageJson } from 'nx/src/utils/package-json'; +import { CreateNodes } from 'nx/src/project-graph/plugins'; +import { ProjectGraph } from 'nx/src/devkit-exports'; +import { TempFs } from 'nx/src/internal-testing-utils/temp-fs'; + +import { addPlugin, generateCombinations } from './add-plugin'; + +describe('addPlugin', () => { + let tree: Tree; + let createNodes: CreateNodes; + let graph: ProjectGraph; + let fs: TempFs; + + beforeEach(async () => { + tree = createTreeWithEmptyWorkspace(); + + fs = new TempFs('add-plugin'); + tree.root = fs.tempDir; + + graph = { + nodes: { + app1: { + name: 'app1', + type: 'app', + data: { + root: 'app1', + targets: {}, + }, + }, + }, + dependencies: { + app1: [], + }, + }; + createNodes = [ + '**/next.config.{js,cjs,mjs}', + (_, { targetName }) => ({ + projects: { + app1: { + name: 'app1', + targets: { + [targetName]: { command: 'next build' }, + }, + }, + }, + }), + ]; + + await fs.createFiles({ + 'app1/next.config.js': '', + }); + }); + + afterEach(() => { + fs.cleanup(); + }); + + describe('adding the plugin', () => { + it('should not conflicting with the existing graph', async () => { + graph.nodes.app1.data.targets.build = {}; + + await addPlugin( + tree, + graph, + '@nx/next/plugin', + createNodes, + + { + targetName: ['build', '_build'], + }, + true + ); + + expect(readJson(tree, 'nx.json').plugins).toContainEqual({ + plugin: '@nx/next/plugin', + options: { + targetName: '_build', + }, + }); + }); + }); + + it('should throw an error if no non-conflicting options are provided', async () => { + graph.nodes.app1.data.targets.build = {}; + + try { + await addPlugin( + tree, + graph, + '@nx/next/plugin', + createNodes, + + { + targetName: ['build'], + }, + true + ); + fail('Should have thrown an error'); + } catch (e) {} + }); + + describe('updating package scripts', () => { + test.each` + script + ${'next-remote-watch'} + ${'anext build'} + ${'next builda'} + `('should not replace "$script"', async ({ script }) => { + writeJson(tree, 'app1/package.json', { + name: 'app1', + scripts: { + build: script, + }, + }); + + await addPlugin( + tree, + graph, + '@nx/next/plugin', + createNodes, + + { + targetName: ['build'], + }, + true + ); + + const { scripts } = readJson(tree, 'app1/package.json'); + expect(scripts.build).toBe(script); + }); + + test.each` + script | expected + ${'next build'} | ${'nx build'} + ${'npx next build'} | ${'npx nx build'} + ${'next build --debug'} | ${'nx build --debug'} + ${'NODE_OPTIONS="--inspect" next build'} | ${'NODE_OPTIONS="--inspect" nx build'} + ${'NODE_OPTIONS="--inspect" npx next build --debug'} | ${'NODE_OPTIONS="--inspect" npx nx build --debug'} + ${'next build && echo "Done"'} | ${'nx build && echo "Done"'} + ${'echo "Building..." && next build'} | ${'echo "Building..." && nx build'} + ${'echo "Building..." && next build && echo "Done"'} | ${'echo "Building..." && nx build && echo "Done"'} + ${'echo "Building..." &&next build&& echo "Done"'} | ${'echo "Building..." &&nx build&& echo "Done"'} + ${'echo "Building..." && NODE_OPTIONS="--inspect" npx next build --debug && echo "Done"'} | ${'echo "Building..." && NODE_OPTIONS="--inspect" npx nx build --debug && echo "Done"'} + `( + 'should replace "$script" with "$expected"', + async ({ script, expected }) => { + writeJson(tree, 'app1/package.json', { + name: 'app1', + scripts: { + build: script, + }, + }); + + await addPlugin( + tree, + graph, + '@nx/next/plugin', + createNodes, + + { + targetName: ['build'], + }, + true + ); + + const { scripts } = readJson(tree, 'app1/package.json'); + expect(scripts.build).toBe(expected); + } + ); + + test.each` + script | expected + ${'cypress run --e2e --config-file cypress.config.ts'} | ${'nx e2e'} + ${'echo "Starting..." && cypress run --e2e --config-file cypress.config.ts && echo "Done"'} | ${'echo "Starting..." && nx e2e && echo "Done"'} + `( + 'should replace "$script" with "$expected"', + async ({ script, expected }) => { + await fs.createFile('app1/cypress.config.ts', ''); + writeJson(tree, 'app1/package.json', { + name: 'app1', + scripts: { + e2e: script, + }, + }); + + createNodes = [ + '**/cypress.config.{js,ts,mjs,mts,cjs,cts}', + () => ({ + projects: { + app1: { + name: 'app1', + targets: { + e2e: { + command: + 'cypress run --config-file cypress.config.ts --e2e', + }, + }, + }, + }, + }), + ]; + + await addPlugin( + tree, + graph, + '@nx/cypress/plugin', + createNodes, + + { + targetName: ['e2e'], + }, + true + ); + + const { scripts } = readJson(tree, 'app1/package.json'); + expect(scripts.e2e).toBe(expected); + } + ); + + it('should handle scripts with name different than the target name', async () => { + writeJson(tree, 'app1/package.json', { + name: 'app1', + scripts: { + 'build:dev': 'next build', + }, + }); + + await addPlugin( + tree, + graph, + '@nx/next/plugin', + createNodes, + + { + targetName: ['build'], + }, + true + ); + + const { scripts } = readJson(tree, 'app1/package.json'); + expect(scripts['build:dev']).toBe('nx build'); + }); + + it('should support replacing multiple scripts', async () => { + writeJson(tree, 'app1/package.json', { + name: 'app1', + scripts: { + dev: 'PORT=4000 next dev --experimental-https', + start: 'next build && PORT=4000 next start --experimental-https', + }, + }); + + createNodes = [ + '**/next.config.{js,cjs,mjs}', + () => ({ + projects: { + app1: { + name: 'app1', + targets: { + build: { command: 'next build' }, + dev: { command: 'next dev' }, + start: { command: 'next start' }, + }, + }, + }, + }), + ]; + + await addPlugin( + tree, + graph, + '@nx/next/plugin', + createNodes, + + { + targetName: ['build'], + }, + true + ); + + const { scripts } = readJson(tree, 'app1/package.json'); + expect(scripts.dev).toBe('PORT=4000 nx dev --experimental-https'); + expect(scripts.start).toBe( + 'nx build && PORT=4000 nx start --experimental-https' + ); + }); + + it('should support multiple occurrences of the same command within a script', async () => { + await fs.createFile('app1/tsconfig.json', ''); + writeJson(tree, 'app1/package.json', { + name: 'app1', + scripts: { + typecheck: 'tsc -p tsconfig.lib.json && tsc -p tsconfig.spec.json', + }, + }); + + createNodes = [ + '**/tsconfig.json', + () => ({ + projects: { + app1: { + name: 'app1', + targets: { + build: { command: 'tsc' }, + }, + }, + }, + }), + ]; + + await addPlugin( + tree, + graph, + '@nx/next/plugin', + createNodes, + + { + targetName: ['build'], + }, + true + ); + + const { scripts } = readJson(tree, 'app1/package.json'); + expect(scripts.typecheck).toBe( + 'nx build -p tsconfig.lib.json && nx build -p tsconfig.spec.json' + ); + }); + + it('should support multiple occurrences of the same command within a script with extra commands', async () => { + await fs.createFile('app1/tsconfig.json', ''); + writeJson(tree, 'app1/package.json', { + name: 'app1', + scripts: { + typecheck: + 'echo "Typechecking..." && tsc -p tsconfig.lib.json && tsc -p tsconfig.spec.json && echo "Done"', + }, + }); + + createNodes = [ + '**/tsconfig.json', + () => ({ + projects: { + app1: { + name: 'app1', + targets: { + build: { command: 'tsc' }, + }, + }, + }, + }), + ]; + + await addPlugin( + tree, + graph, + '@nx/next/plugin', + createNodes, + + { + targetName: ['build'], + }, + true + ); + + const { scripts } = readJson(tree, 'app1/package.json'); + expect(scripts.typecheck).toBe( + 'echo "Typechecking..." && nx build -p tsconfig.lib.json && nx build -p tsconfig.spec.json && echo "Done"' + ); + }); + }); +}); +describe('generateCombinations', () => { + it('should return all combinations for a 2x2 array of strings', () => { + const input = { + prop1: ['1', '2'], + prop2: ['a', 'b'], + }; + expect(generateCombinations(input).map((v) => JSON.stringify(v))) + .toMatchInlineSnapshot(` + [ + "{"prop1":"1","prop2":"a"}", + "{"prop1":"2","prop2":"a"}", + "{"prop1":"1","prop2":"b"}", + "{"prop1":"2","prop2":"b"}", + ] + `); + }); + + it('should handle arrays of 2x3 array of strings', () => { + const input = { + prop1: ['1', '2', '3'], + prop2: ['a', 'b'], + }; + expect(generateCombinations(input).map((v) => JSON.stringify(v))) + .toMatchInlineSnapshot(` + [ + "{"prop1":"1","prop2":"a"}", + "{"prop1":"2","prop2":"a"}", + "{"prop1":"3","prop2":"a"}", + "{"prop1":"1","prop2":"b"}", + "{"prop1":"2","prop2":"b"}", + "{"prop1":"3","prop2":"b"}", + ] + `); + }); + + it('should handle arrays of 3x2 array of strings', () => { + const input = { + prop1: ['1', '2'], + prop2: ['a', 'b'], + prop3: ['i', 'ii'], + }; + expect(generateCombinations(input).map((v) => JSON.stringify(v))) + .toMatchInlineSnapshot(` + [ + "{"prop1":"1","prop2":"a","prop3":"i"}", + "{"prop1":"2","prop2":"a","prop3":"i"}", + "{"prop1":"1","prop2":"b","prop3":"i"}", + "{"prop1":"2","prop2":"b","prop3":"i"}", + "{"prop1":"1","prop2":"a","prop3":"ii"}", + "{"prop1":"2","prop2":"a","prop3":"ii"}", + "{"prop1":"1","prop2":"b","prop3":"ii"}", + "{"prop1":"2","prop2":"b","prop3":"ii"}", + ] + `); + }); + + it('should be able to be used to generate possible combinations of plugin options', () => { + const possibleOptions = generateCombinations({ + buildTargetName: ['build', 'vite:build', 'vite-build'], + testTargetName: ['test', 'vite:test', 'vite-test'], + serveTargetName: ['serve', 'vite:serve', 'vite-serve'], + previewTargetName: ['preview', 'vite:preview', 'vite-preview'], + serveStaticTargetName: [ + 'serve-static', + 'vite:serve-static', + 'vite-serve-static', + ], + }); + + expect(possibleOptions.length).toEqual(3 ** 5); + + expect(possibleOptions[0]).toEqual({ + buildTargetName: 'build', + previewTargetName: 'preview', + testTargetName: 'test', + serveTargetName: 'serve', + serveStaticTargetName: 'serve-static', + }); + // The first defined option is the first to be changed + expect(possibleOptions[1]).toEqual({ + buildTargetName: 'vite:build', + previewTargetName: 'preview', + testTargetName: 'test', + serveTargetName: 'serve', + serveStaticTargetName: 'serve-static', + }); + + expect(possibleOptions[possibleOptions.length - 1]).toEqual({ + buildTargetName: 'vite-build', + previewTargetName: 'vite-preview', + testTargetName: 'vite-test', + serveTargetName: 'vite-serve', + serveStaticTargetName: 'vite-serve-static', + }); + }); +}); diff --git a/packages/devkit/src/utils/update-package-scripts.ts b/packages/devkit/src/utils/add-plugin.ts similarity index 50% rename from packages/devkit/src/utils/update-package-scripts.ts rename to packages/devkit/src/utils/add-plugin.ts index 46c4478e3a..18e412db07 100644 --- a/packages/devkit/src/utils/update-package-scripts.ts +++ b/packages/devkit/src/utils/add-plugin.ts @@ -1,16 +1,123 @@ -import type { - CreateNodes, - CreateNodesFunction, - CreateNodesResult, - NxJsonConfiguration, - Tree, +import { + type CreateNodes, + type ProjectConfiguration, + type ProjectGraph, + type Tree, } from 'nx/src/devkit-exports'; import type { PackageJson } from 'nx/src/utils/package-json'; -import { basename, dirname } from 'path'; import * as yargs from 'yargs-parser'; import { requireNx } from '../../nx'; -const { glob, readJson, readNxJson, workspaceRoot, writeJson } = requireNx(); +const { + readJson, + writeJson, + readNxJson, + updateNxJson, + retrieveProjectConfigurations, + LoadedNxPlugin, + ProjectConfigurationsError, +} = requireNx(); + +import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils'; + +/** + * Iterates through various forms of plugin options to find the one which does not conflict with the current graph + + */ +export async function addPlugin( + tree: Tree, + graph: ProjectGraph, + pluginName: string, + createNodesTuple: CreateNodes, + options: Partial< + Record + >, + shouldUpdatePackageJsonScripts: boolean +): Promise { + const graphNodes = Object.values(graph.nodes); + const nxJson = readNxJson(tree); + + let pluginOptions: PluginOptions; + let projConfigs: ConfigurationResult; + const combinations = generateCombinations(options); + optionsLoop: for (const _pluginOptions of combinations) { + pluginOptions = _pluginOptions as PluginOptions; + + nxJson.plugins ??= []; + if ( + nxJson.plugins.some((p) => + typeof p === 'string' ? p === pluginName : p.plugin === pluginName + ) + ) { + // Plugin has already been added + return; + } + global.NX_GRAPH_CREATION = true; + try { + projConfigs = await retrieveProjectConfigurations( + [ + new LoadedNxPlugin( + { + name: pluginName, + createNodes: createNodesTuple, + }, + { + plugin: pluginName, + options: pluginOptions, + } + ), + ], + tree.root, + nxJson + ); + } catch (e) { + // Errors are okay for this because we're only running 1 plugin + if (e instanceof ProjectConfigurationsError) { + projConfigs = e.partialProjectConfigurationsResult; + } else { + throw e; + } + } + global.NX_GRAPH_CREATION = false; + + for (const projConfig of Object.values(projConfigs.projects)) { + const node = graphNodes.find( + (node) => node.data.root === projConfig.root + ); + + if (!node) { + continue; + } + + for (const targetName in projConfig.targets) { + if (node.data.targets[targetName]) { + // Conflicting Target Name, check the next one + pluginOptions = null; + continue optionsLoop; + } + } + } + + break; + } + + if (!pluginOptions) { + throw new Error( + 'Could not add the plugin in a way which does not conflict with existing targets. Please report this error at: https://github.com/nrwl/nx/issues/new/choose' + ); + } + + nxJson.plugins.push({ + plugin: pluginName, + options: pluginOptions, + }); + + updateNxJson(tree, nxJson); + + if (shouldUpdatePackageJsonScripts) { + updatePackageScripts(tree, projConfigs); + } +} type TargetCommand = { command: string; @@ -18,35 +125,20 @@ type TargetCommand = { configuration?: string; }; -export async function updatePackageScripts( +function updatePackageScripts( tree: Tree, - createNodesTuple: CreateNodes -): Promise { - const nxJson = readNxJson(tree); - - const [pattern, createNodes] = createNodesTuple; - const matchingFiles = glob(tree, [pattern]); - - for (const file of matchingFiles) { - const projectRoot = getProjectRootFromConfigFile(file); - await processProject( - tree, - projectRoot, - file, - createNodes, - nxJson, - matchingFiles - ); + projectConfigurations: ConfigurationResult +) { + for (const projectConfig of Object.values(projectConfigurations.projects)) { + const projectRoot = projectConfig.root; + processProject(tree, projectRoot, projectConfig); } } -async function processProject( +function processProject( tree: Tree, projectRoot: string, - projectConfigurationFile: string, - createNodesFunction: CreateNodesFunction, - nxJsonConfiguration: NxJsonConfiguration, - configFiles: string[] + projectConfiguration: ProjectConfiguration ) { const packageJsonPath = `${projectRoot}/package.json`; if (!tree.exists(packageJsonPath)) { @@ -57,17 +149,7 @@ async function processProject( return; } - const result = await createNodesFunction( - projectConfigurationFile, - {}, - { - nxJsonConfiguration, - workspaceRoot, - configFiles, - } - ); - - const targetCommands = getInferredTargetCommands(result); + const targetCommands = getInferredTargetCommands(projectConfiguration); if (!targetCommands.length) { return; } @@ -187,82 +269,86 @@ async function processProject( } } - if (process.env.NX_RUNNING_NX_INIT === 'true') { - // running `nx init` so we want to exclude everything by default - packageJson.nx ??= {}; - packageJson.nx.includedScripts = []; - } else if (replacedTargets.size) { - /** - * Running `nx add`. In this case we want to: - * - if `includedScripts` is already set: exclude scripts that match inferred targets that were used to replace a script - * - if `includedScripts` is not set: set `includedScripts` with all scripts except the ones that match an inferred target that was used to replace a script - */ - const includedScripts = - packageJson.nx?.includedScripts ?? Object.keys(packageJson.scripts); - const filteredScripts = includedScripts.filter( - (s) => !replacedTargets.has(s) - ); - if (filteredScripts.length !== includedScripts.length) { - packageJson.nx ??= {}; - packageJson.nx.includedScripts = filteredScripts; - } - } - writeJson(tree, packageJsonPath, packageJson); } -function getInferredTargetCommands(result: CreateNodesResult): TargetCommand[] { +function getInferredTargetCommands( + project: ProjectConfiguration +): TargetCommand[] { const targetCommands: TargetCommand[] = []; - for (const project of Object.values(result.projects ?? {})) { - for (const [targetName, target] of Object.entries(project.targets ?? {})) { - if (target.command) { - targetCommands.push({ command: target.command, target: targetName }); + for (const [targetName, target] of Object.entries(project.targets ?? {})) { + if (target.command) { + targetCommands.push({ command: target.command, target: targetName }); + } else if ( + target.executor === 'nx:run-commands' && + target.options?.command + ) { + targetCommands.push({ + command: target.options.command, + target: targetName, + }); + } + + if (!target.configurations) { + continue; + } + + for (const [configurationName, configuration] of Object.entries( + target.configurations + )) { + if (configuration.command) { + targetCommands.push({ + command: configuration.command, + target: targetName, + configuration: configurationName, + }); } else if ( target.executor === 'nx:run-commands' && - target.options?.command + configuration.options?.command ) { targetCommands.push({ - command: target.options.command, + command: configuration.options.command, target: targetName, + configuration: configurationName, }); } - - if (!target.configurations) { - continue; - } - - for (const [configurationName, configuration] of Object.entries( - target.configurations - )) { - if (configuration.command) { - targetCommands.push({ - command: configuration.command, - target: targetName, - configuration: configurationName, - }); - } else if ( - target.executor === 'nx:run-commands' && - configuration.options?.command - ) { - targetCommands.push({ - command: configuration.options.command, - target: targetName, - configuration: configurationName, - }); - } - } } } return targetCommands; } -function getProjectRootFromConfigFile(file: string): string { - let projectRoot = dirname(file); - if (basename(projectRoot) === '.storybook') { - projectRoot = dirname(projectRoot); - } +export function generateCombinations( + input: Record +): Record[] { + // This is reversed so that combinations have the first defined property updated first + const keys = Object.keys(input).reverse(); + return _generateCombinations(Object.values(input).reverse()).map( + (combination) => { + const result = {}; + combination.reverse().forEach((combo, i) => { + result[keys[keys.length - i - 1]] = combo; + }); - return projectRoot; + return result; + } + ); +} + +/** + * Generate all possible combinations of a 2-dimensional array. + * + * Useful for generating all possible combinations of options for a plugin + */ +function _generateCombinations(input: T[][]): T[][] { + if (input.length === 0) { + return [[]]; + } else { + const [first, ...rest] = input; + const partialCombinations = _generateCombinations(rest); + return first.flatMap((value) => + partialCombinations.map((combination) => [value, ...combination]) + ); + } } diff --git a/packages/devkit/src/utils/update-package-scripts.spec.ts b/packages/devkit/src/utils/update-package-scripts.spec.ts deleted file mode 100644 index 317cc5db24..0000000000 --- a/packages/devkit/src/utils/update-package-scripts.spec.ts +++ /dev/null @@ -1,394 +0,0 @@ -import { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace'; -import type { Tree } from 'nx/src/generators/tree'; -import { readJson, writeJson } from 'nx/src/generators/utils/json'; -import type { PackageJson } from 'nx/src/utils/package-json'; -import { updatePackageScripts } from './update-package-scripts'; - -describe('updatePackageScripts', () => { - let tree: Tree; - - beforeEach(() => { - tree = createTreeWithEmptyWorkspace(); - }); - - test.each` - script - ${'next-remote-watch'} - ${'anext build'} - ${'next builda'} - `('should not replace "$script"', async ({ script }) => { - tree.write('app1/next.config.js', ''); - writeJson(tree, 'app1/package.json', { - name: 'app1', - scripts: { - build: script, - }, - }); - - await updatePackageScripts(tree, [ - '**/next.config.{js,cjs,mjs}', - () => ({ - projects: { - app1: { - targets: { - build: { command: 'next build' }, - }, - }, - }, - }), - ]); - - const { scripts } = readJson(tree, 'app1/package.json'); - expect(scripts.build).toBe(script); - }); - - test.each` - script | expected - ${'next build'} | ${'nx build'} - ${'npx next build'} | ${'npx nx build'} - ${'next build --debug'} | ${'nx build --debug'} - ${'NODE_OPTIONS="--inspect" next build'} | ${'NODE_OPTIONS="--inspect" nx build'} - ${'NODE_OPTIONS="--inspect" npx next build --debug'} | ${'NODE_OPTIONS="--inspect" npx nx build --debug'} - ${'next build && echo "Done"'} | ${'nx build && echo "Done"'} - ${'echo "Building..." && next build'} | ${'echo "Building..." && nx build'} - ${'echo "Building..." && next build && echo "Done"'} | ${'echo "Building..." && nx build && echo "Done"'} - ${'echo "Building..." &&next build&& echo "Done"'} | ${'echo "Building..." &&nx build&& echo "Done"'} - ${'echo "Building..." && NODE_OPTIONS="--inspect" npx next build --debug && echo "Done"'} | ${'echo "Building..." && NODE_OPTIONS="--inspect" npx nx build --debug && echo "Done"'} - `( - 'should replace "$script" with "$expected"', - async ({ script, expected }) => { - tree.write('app1/next.config.js', ''); - writeJson(tree, 'app1/package.json', { - name: 'app1', - scripts: { - build: script, - }, - }); - - await updatePackageScripts(tree, [ - '**/next.config.{js,cjs,mjs}', - () => ({ - projects: { - app1: { - targets: { - build: { command: 'next build' }, - }, - }, - }, - }), - ]); - - const { scripts } = readJson(tree, 'app1/package.json'); - expect(scripts.build).toBe(expected); - } - ); - - test.each` - script | expected - ${'cypress run --e2e --config-file cypress.config.ts'} | ${'nx e2e'} - ${'echo "Starting..." && cypress run --e2e --config-file cypress.config.ts && echo "Done"'} | ${'echo "Starting..." && nx e2e && echo "Done"'} - `( - 'should replace "$script" with "$expected"', - async ({ script, expected }) => { - tree.write('app1/cypress.config.ts', ''); - writeJson(tree, 'app1/package.json', { - name: 'app1', - scripts: { - e2e: script, - }, - }); - - await updatePackageScripts(tree, [ - '**/cypress.config.{js,ts,mjs,mts,cjs,cts}', - () => ({ - projects: { - app1: { - targets: { - e2e: { - command: 'cypress run --config-file cypress.config.ts --e2e', - }, - }, - }, - }, - }), - ]); - - const { scripts } = readJson(tree, 'app1/package.json'); - expect(scripts.e2e).toBe(expected); - } - ); - - it('should handle scripts with name different than the target name', async () => { - tree.write('app1/next.config.js', ''); - writeJson(tree, 'app1/package.json', { - name: 'app1', - scripts: { - 'build:dev': 'next build', - }, - }); - - await updatePackageScripts(tree, [ - '**/next.config.{js,cjs,mjs}', - () => ({ - projects: { - app1: { - targets: { - build: { - command: 'next build', - }, - }, - }, - }, - }), - ]); - - const { scripts } = readJson(tree, 'app1/package.json'); - expect(scripts['build:dev']).toBe('nx build'); - }); - - it('should support replacing multiple scripts', async () => { - tree.write('app1/next.config.js', ''); - writeJson(tree, 'app1/package.json', { - name: 'app1', - scripts: { - dev: 'PORT=4000 next dev --experimental-https', - start: 'next build && PORT=4000 next start --experimental-https', - }, - }); - - await updatePackageScripts(tree, [ - '**/next.config.{js,cjs,mjs}', - () => ({ - projects: { - app1: { - targets: { - build: { command: 'next build' }, - dev: { command: 'next dev' }, - start: { command: 'next start' }, - }, - }, - }, - }), - ]); - - const { scripts } = readJson(tree, 'app1/package.json'); - expect(scripts.dev).toBe('PORT=4000 nx dev --experimental-https'); - expect(scripts.start).toBe( - 'nx build && PORT=4000 nx start --experimental-https' - ); - }); - - it('should support multiple occurrences of the same command within a script', async () => { - tree.write('app1/tsconfig.json', ''); - writeJson(tree, 'app1/package.json', { - name: 'app1', - scripts: { - typecheck: 'tsc -p tsconfig.lib.json && tsc -p tsconfig.spec.json', - }, - }); - - await updatePackageScripts(tree, [ - '**/tsconfig.json', - () => ({ - projects: { - app1: { - targets: { - build: { command: 'tsc' }, - }, - }, - }, - }), - ]); - - const { scripts } = readJson(tree, 'app1/package.json'); - expect(scripts.typecheck).toBe( - 'nx build -p tsconfig.lib.json && nx build -p tsconfig.spec.json' - ); - }); - - it('should support multiple occurrences of the same command within a script with extra commands', async () => { - tree.write('app1/tsconfig.json', ''); - writeJson(tree, 'app1/package.json', { - name: 'app1', - scripts: { - typecheck: - 'echo "Typechecking..." && tsc -p tsconfig.lib.json && tsc -p tsconfig.spec.json && echo "Done"', - }, - }); - - await updatePackageScripts(tree, [ - '**/tsconfig.json', - () => ({ - projects: { - app1: { - targets: { - build: { command: 'tsc' }, - }, - }, - }, - }), - ]); - - const { scripts } = readJson(tree, 'app1/package.json'); - expect(scripts.typecheck).toBe( - 'echo "Typechecking..." && nx build -p tsconfig.lib.json && nx build -p tsconfig.spec.json && echo "Done"' - ); - }); - - it('should set "includedScripts" to an empty array when running "nx init"', async () => { - const originalEnvVarValue = process.env.NX_RUNNING_NX_INIT; - process.env.NX_RUNNING_NX_INIT = 'true'; - - tree.write('vite.config.ts', ''); - writeJson(tree, 'package.json', { - name: 'app1', - scripts: { - build: 'vite build', - serve: 'vite', - test: 'vitest', - coverage: 'vitest run --coverage', - foo: 'echo "foo"', - }, - }); - - await updatePackageScripts(tree, [ - '**/{vite,vitest}.config.{js,ts,mjs,mts,cjs,cts}', - () => ({ - projects: { - app1: { - targets: { - build: { command: 'vite build' }, - serve: { command: 'vite serve' }, - test: { command: 'vitest run' }, - }, - }, - }, - }), - ]); - - const packageJson = readJson(tree, 'package.json'); - expect(packageJson.scripts).toStrictEqual({ - build: 'nx build', - serve: 'vite', - test: 'vitest', - coverage: 'nx test --coverage', - foo: 'echo "foo"', - }); - expect(packageJson.nx.includedScripts).toStrictEqual([]); - - process.env.NX_RUNNING_NX_INIT = originalEnvVarValue; - }); - - it('should set "includedScripts" to all scripts except the ones matching inferred target names when "includedScripts" is not set', async () => { - tree.write('vite.config.ts', ''); - writeJson(tree, 'package.json', { - name: 'app1', - scripts: { - build: 'vite build', - serve: 'vite', - test: 'vitest', - coverage: 'vitest run --coverage', - foo: 'echo "foo"', - }, - }); - - await updatePackageScripts(tree, [ - '**/{vite,vitest}.config.{js,ts,mjs,mts,cjs,cts}', - () => ({ - projects: { - app1: { - targets: { - build: { command: 'vite build' }, - serve: { command: 'vite serve' }, - test: { command: 'vitest run' }, - }, - }, - }, - }), - ]); - - const packageJson = readJson(tree, 'package.json'); - expect(packageJson.scripts).toStrictEqual({ - build: 'nx build', - serve: 'vite', - test: 'vitest', - coverage: 'nx test --coverage', - foo: 'echo "foo"', - }); - // "build": excluded because a script (itself) was replaced with a target "build" - // "serve" not excluded even though it matches the name of an inferred target because no script was replaced with a target "serve" - // "test" excluded even though it was not replaced because another script was replaced with a target "test" - // "coverage" not excluded even though it was replaced because it does not match a target - // "foo" not excluded because it does not match a target - expect(packageJson.nx.includedScripts).toStrictEqual([ - 'serve', - 'coverage', - 'foo', - ]); - }); - - it('should not set "nx.includedScripts" when no script matched an inferred target', async () => { - tree.write('vite.config.ts', ''); - writeJson(tree, 'package.json', { - name: 'app1', - scripts: { - serve: 'vite', - coverage: 'vitest run --coverage', - foo: 'echo "foo"', - }, - }); - - await updatePackageScripts(tree, [ - '**/{vite,vitest}.config.{js,ts,mjs,mts,cjs,cts}', - () => ({ - projects: { - app1: { - targets: { - build: { command: 'vite build' }, - serve: { command: 'vite serve' }, - test: { command: 'vitest run' }, - }, - }, - }, - }), - ]); - - const packageJson = readJson(tree, 'package.json'); - expect(packageJson.scripts).toStrictEqual({ - serve: 'vite', - coverage: 'nx test --coverage', - foo: 'echo "foo"', - }); - expect(packageJson.nx).toBeUndefined(); - }); - - it('should exclude replaced package.json scripts from nx if they are initially included', async () => { - tree.write('next.config.js', ''); - writeJson(tree, 'package.json', { - name: 'app1', - scripts: { - build: 'next build', - foo: 'echo "foo"', - }, - nx: { - includedScripts: ['build', 'foo'], - }, - }); - - await updatePackageScripts(tree, [ - '**/next.config.{js,cjs,mjs}', - () => ({ - projects: { - app1: { - targets: { - build: { command: 'next build' }, - }, - }, - }, - }), - ]); - - const { nx } = readJson(tree, 'package.json'); - expect(nx.includedScripts).toStrictEqual(['foo']); - }); -}); diff --git a/packages/eslint/src/generators/convert-to-flat-config/generator.spec.ts b/packages/eslint/src/generators/convert-to-flat-config/generator.spec.ts index 62755d9254..17d3fa5974 100644 --- a/packages/eslint/src/generators/convert-to-flat-config/generator.spec.ts +++ b/packages/eslint/src/generators/convert-to-flat-config/generator.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { NxJsonConfiguration, diff --git a/packages/eslint/src/generators/init/init.spec.ts b/packages/eslint/src/generators/init/init.spec.ts index 552b65d8f3..bcf161b287 100644 --- a/packages/eslint/src/generators/init/init.spec.ts +++ b/packages/eslint/src/generators/init/init.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { NxJsonConfiguration, readJson, Tree, updateJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { LinterInitOptions, lintInitGenerator } from './init'; diff --git a/packages/eslint/src/generators/init/init.ts b/packages/eslint/src/generators/init/init.ts index d6f91ed8c3..dfe622ea8c 100644 --- a/packages/eslint/src/generators/init/init.ts +++ b/packages/eslint/src/generators/init/init.ts @@ -1,15 +1,17 @@ -import type { GeneratorCallback, Tree } from '@nx/devkit'; import { addDependenciesToPackageJson, + createProjectGraphAsync, + GeneratorCallback, readNxJson, removeDependenciesFromPackageJson, runTasksInSerial, + Tree, updateNxJson, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; import { eslintVersion, nxVersion } from '../../utils/versions'; import { findEslintFile } from '../utils/eslint-file'; -import { EslintPluginOptions, createNodes } from '../../plugins/plugin'; +import { createNodes } from '../../plugins/plugin'; import { hasEslintPlugin } from '../utils/plugin'; export interface LinterInitOptions { @@ -47,29 +49,6 @@ function addTargetDefaults(tree: Tree) { updateNxJson(tree, nxJson); } -function addPlugin(tree: Tree) { - const nxJson = readNxJson(tree); - nxJson.plugins ??= []; - - for (const plugin of nxJson.plugins) { - if ( - typeof plugin === 'string' - ? plugin === '@nx/eslint/plugin' - : plugin.plugin === '@nx/eslint/plugin' - ) { - return; - } - } - - nxJson.plugins.push({ - plugin: '@nx/eslint/plugin', - options: { - targetName: 'lint', - } as EslintPluginOptions, - }); - updateNxJson(tree, nxJson); -} - export async function initEsLint( tree: Tree, options: LinterInitOptions @@ -82,12 +61,28 @@ export async function initEsLint( const hasPlugin = hasEslintPlugin(tree); const rootEslintFile = findEslintFile(tree); - if (rootEslintFile && options.addPlugin && !hasPlugin) { - addPlugin(tree); + const graph = await createProjectGraphAsync(); - if (options.updatePackageScripts) { - await updatePackageScripts(tree, createNodes); - } + const lintTargetNames = [ + 'lint', + 'eslint:lint', + 'eslint-lint', + '_lint', + '_eslint:lint', + '_eslint-lint', + ]; + + if (rootEslintFile && options.addPlugin && !hasPlugin) { + await addPlugin( + tree, + graph, + '@nx/eslint/plugin', + createNodes, + { + targetName: lintTargetNames, + }, + options.updatePackageScripts + ); return () => {}; } @@ -99,7 +94,16 @@ export async function initEsLint( updateProductionFileset(tree); if (options.addPlugin) { - addPlugin(tree); + await addPlugin( + tree, + graph, + '@nx/eslint/plugin', + createNodes, + { + targetName: lintTargetNames, + }, + options.updatePackageScripts + ); } else { addTargetDefaults(tree); } @@ -121,10 +125,6 @@ export async function initEsLint( ); } - if (options.updatePackageScripts) { - await updatePackageScripts(tree, createNodes); - } - return runTasksInSerial(...tasks); } diff --git a/packages/eslint/src/generators/lint-project/lint-project.spec.ts b/packages/eslint/src/generators/lint-project/lint-project.spec.ts index 342ac38ef0..ba308961eb 100644 --- a/packages/eslint/src/generators/lint-project/lint-project.spec.ts +++ b/packages/eslint/src/generators/lint-project/lint-project.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, readJson, diff --git a/packages/eslint/src/generators/workspace-rule/workspace-rule.spec.ts b/packages/eslint/src/generators/workspace-rule/workspace-rule.spec.ts index 1c9468d7ee..c4a85162d6 100644 --- a/packages/eslint/src/generators/workspace-rule/workspace-rule.spec.ts +++ b/packages/eslint/src/generators/workspace-rule/workspace-rule.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { lintWorkspaceRuleGenerator } from './workspace-rule'; diff --git a/packages/eslint/src/generators/workspace-rules-project/workspace-rules-project.spec.ts b/packages/eslint/src/generators/workspace-rules-project/workspace-rules-project.spec.ts index 6e60123c1f..0a0482e781 100644 --- a/packages/eslint/src/generators/workspace-rules-project/workspace-rules-project.spec.ts +++ b/packages/eslint/src/generators/workspace-rules-project/workspace-rules-project.spec.ts @@ -1,8 +1,9 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, NxJsonConfiguration, readJson, - readProjectConfiguration, Tree, updateJson, } from '@nx/devkit'; diff --git a/packages/expo/src/generators/application/application.spec.ts b/packages/expo/src/generators/application/application.spec.ts index cc502e65a5..3b3b574eb1 100644 --- a/packages/expo/src/generators/application/application.spec.ts +++ b/packages/expo/src/generators/application/application.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { getProjects, readJson, diff --git a/packages/expo/src/generators/component/component.spec.ts b/packages/expo/src/generators/component/component.spec.ts index 8c291a5e0d..3f3f96c078 100644 --- a/packages/expo/src/generators/component/component.spec.ts +++ b/packages/expo/src/generators/component/component.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { logger, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Linter } from '@nx/eslint'; diff --git a/packages/expo/src/generators/init/init.spec.ts b/packages/expo/src/generators/init/init.spec.ts index 2af0563d89..b40251d9a1 100644 --- a/packages/expo/src/generators/init/init.spec.ts +++ b/packages/expo/src/generators/init/init.spec.ts @@ -1,4 +1,6 @@ -import { Tree, readJson, updateJson } from '@nx/devkit'; +import 'nx/src/internal-testing-utils/mock-project-graph'; + +import { readJson, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { expoInitGenerator } from './init'; diff --git a/packages/expo/src/generators/init/init.ts b/packages/expo/src/generators/init/init.ts index f1a37634ce..da4a087481 100644 --- a/packages/expo/src/generators/init/init.ts +++ b/packages/expo/src/generators/init/init.ts @@ -1,15 +1,15 @@ import { addDependenciesToPackageJson, + createProjectGraphAsync, formatFiles, GeneratorCallback, readNxJson, removeDependenciesFromPackageJson, runTasksInSerial, Tree, - updateNxJson, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; -import { createNodes, ExpoPluginOptions } from '../../../plugins/plugin'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; +import { createNodes } from '../../../plugins/plugin'; import { expoCliVersion, expoVersion, @@ -18,7 +18,6 @@ import { reactNativeVersion, reactVersion, } from '../../utils/versions'; -import { hasExpoPlugin } from '../../utils/has-expo-plugin'; import { addGitIgnoreEntry } from './lib/add-git-ignore-entry'; import { Schema } from './schema'; @@ -37,7 +36,29 @@ export async function expoInitGeneratorInternal(host: Tree, schema: Schema) { addGitIgnoreEntry(host); if (schema.addPlugin) { - addPlugin(host); + await addPlugin( + host, + await createProjectGraphAsync(), + '@nx/expo/plugin', + createNodes, + { + startTargetName: ['start', 'expo:start', 'expo-start'], + buildTargetName: ['build', 'expo:build', 'expo-build'], + prebuildTargetName: ['prebuild', 'expo:prebuild', 'expo-prebuild'], + serveTargetName: ['serve', 'expo:serve', 'expo-serve'], + installTargetName: ['install', 'expo:install', 'expo-install'], + exportTargetName: ['export', 'expo:export', 'expo-export'], + submitTargetName: ['submit', 'expo:submit', 'expo-submit'], + runIosTargetName: ['run-ios', 'expo:run-ios', 'expo-run-ios'], + runAndroidTargetName: [ + 'run-android', + 'expo:run-android', + 'expo-run-android', + ], + }, + + schema.updatePackageScripts + ); } const tasks: GeneratorCallback[] = []; @@ -46,10 +67,6 @@ export async function expoInitGeneratorInternal(host: Tree, schema: Schema) { tasks.push(updateDependencies(host, schema)); } - if (schema.updatePackageScripts) { - await updatePackageScripts(host, createNodes); - } - if (!schema.skipFormat) { await formatFiles(host); } @@ -79,29 +96,4 @@ function moveDependency(host: Tree) { return removeDependenciesFromPackageJson(host, ['@nx/react-native'], []); } -function addPlugin(host: Tree) { - const nxJson = readNxJson(host); - - if (hasExpoPlugin(host)) { - return; - } - - nxJson.plugins ??= []; - nxJson.plugins.push({ - plugin: '@nx/expo/plugin', - options: { - startTargetName: 'start', - serveTargetName: 'serve', - runIosTargetName: 'run-ios', - runAndroidTargetName: 'run-android', - exportTargetName: 'export', - prebuildTargetName: 'prebuild', - installTargetName: 'install', - buildTargetName: 'build', - submitTargetName: 'submit', - } as ExpoPluginOptions, - }); - updateNxJson(host, nxJson); -} - export default expoInitGenerator; diff --git a/packages/expo/src/generators/library/library.spec.ts b/packages/expo/src/generators/library/library.spec.ts index 2f921ff081..403e302856 100644 --- a/packages/expo/src/generators/library/library.spec.ts +++ b/packages/expo/src/generators/library/library.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { getProjects, readJson, diff --git a/packages/expo/src/utils/add-linting.spec.ts b/packages/expo/src/utils/add-linting.spec.ts index c97cb4f6d3..e6c8cac411 100644 --- a/packages/expo/src/utils/add-linting.spec.ts +++ b/packages/expo/src/utils/add-linting.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Linter } from '@nx/eslint'; diff --git a/packages/gradle/src/generators/init/init.ts b/packages/gradle/src/generators/init/init.ts index 2b0ab56cca..e4d1c4d8f9 100644 --- a/packages/gradle/src/generators/init/init.ts +++ b/packages/gradle/src/generators/init/init.ts @@ -8,8 +8,6 @@ import { Tree, updateNxJson, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; -import { createNodes } from '../../plugin/nodes'; import { nxVersion } from '../../utils/versions'; import { InitGeneratorSchema } from './schema'; import { hasGradlePlugin } from '../../utils/has-gradle-plugin'; @@ -34,10 +32,6 @@ export async function initGenerator(tree: Tree, options: InitGeneratorSchema) { addPlugin(tree); addProjectReportToBuildGradle(tree); - if (options.updatePackageScripts && tree.exists('package.json')) { - await updatePackageScripts(tree, createNodes); - } - if (!options.skipFormat) { await formatFiles(tree); } diff --git a/packages/jest/src/generators/configuration/configuration.spec.ts b/packages/jest/src/generators/configuration/configuration.spec.ts index 42e89d53c4..68691b9079 100644 --- a/packages/jest/src/generators/configuration/configuration.spec.ts +++ b/packages/jest/src/generators/configuration/configuration.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, readJson, diff --git a/packages/jest/src/generators/init/init.spec.ts b/packages/jest/src/generators/init/init.spec.ts index eb59d466d9..6841ce4218 100644 --- a/packages/jest/src/generators/init/init.spec.ts +++ b/packages/jest/src/generators/init/init.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { type NxJsonConfiguration, readJson, diff --git a/packages/jest/src/generators/init/init.ts b/packages/jest/src/generators/init/init.ts index c0e72f02fc..78c6fed320 100644 --- a/packages/jest/src/generators/init/init.ts +++ b/packages/jest/src/generators/init/init.ts @@ -7,33 +7,13 @@ import { updateNxJson, type GeneratorCallback, type Tree, + createProjectGraphAsync, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; import { createNodes } from '../../plugins/plugin'; import { jestVersion, nxVersion } from '../../utils/versions'; import { isPresetCjs } from '../../utils/config/is-preset-cjs'; import type { JestInitSchema } from './schema'; - -function addPlugin(tree: Tree) { - const nxJson = readNxJson(tree); - - nxJson.plugins ??= []; - if ( - !nxJson.plugins.some((p) => - typeof p === 'string' - ? p === '@nx/jest/plugin' - : p.plugin === '@nx/jest/plugin' - ) - ) { - nxJson.plugins.push({ - plugin: '@nx/jest/plugin', - options: { - targetName: 'test', - }, - }); - } - updateNxJson(tree, nxJson); -} +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; function updateProductionFileSet(tree: Tree, presetExt: 'cjs' | 'js') { const nxJson = readNxJson(tree); @@ -121,7 +101,16 @@ export async function jestInitGeneratorInternal( if (!tree.exists('jest.preset.js') && !tree.exists('jest.preset.cjs')) { updateProductionFileSet(tree, presetExt); if (options.addPlugin) { - addPlugin(tree); + await addPlugin( + tree, + await createProjectGraphAsync(), + '@nx/jest/plugin', + createNodes, + { + targetName: ['test', 'jest:test', 'jest-test'], + }, + options.updatePackageScripts + ); } else { addJestTargetDefaults(tree, presetExt); } @@ -133,10 +122,6 @@ export async function jestInitGeneratorInternal( tasks.push(updateDependencies(tree, options)); } - if (options.updatePackageScripts) { - await updatePackageScripts(tree, createNodes); - } - if (!options.skipFormat) { await formatFiles(tree); } diff --git a/packages/js/src/generators/convert-to-swc/convert-to-swc.spec.ts b/packages/js/src/generators/convert-to-swc/convert-to-swc.spec.ts index 74591dbe5c..b424fb99f7 100644 --- a/packages/js/src/generators/convert-to-swc/convert-to-swc.spec.ts +++ b/packages/js/src/generators/convert-to-swc/convert-to-swc.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { join } from 'path'; diff --git a/packages/js/src/generators/library/library.spec.ts b/packages/js/src/generators/library/library.spec.ts index 6e7e9bbed7..423ee5b406 100644 --- a/packages/js/src/generators/library/library.spec.ts +++ b/packages/js/src/generators/library/library.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { getPackageManagerCommand, getProjects, diff --git a/packages/js/src/generators/setup-build/generator.spec.ts b/packages/js/src/generators/setup-build/generator.spec.ts index 4c41d0368e..66655b65b7 100644 --- a/packages/js/src/generators/setup-build/generator.spec.ts +++ b/packages/js/src/generators/setup-build/generator.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { addProjectConfiguration, diff --git a/packages/js/src/generators/setup-verdaccio/generator.spec.ts b/packages/js/src/generators/setup-verdaccio/generator.spec.ts index 62663a75c6..022799b97a 100644 --- a/packages/js/src/generators/setup-verdaccio/generator.spec.ts +++ b/packages/js/src/generators/setup-verdaccio/generator.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Tree, readJson, updateJson } from '@nx/devkit'; diff --git a/packages/js/src/migrations/update-15-8-0/rename-swcrc-config.spec.ts b/packages/js/src/migrations/update-15-8-0/rename-swcrc-config.spec.ts index 4285e43fcf..c8fca2d0fc 100644 --- a/packages/js/src/migrations/update-15-8-0/rename-swcrc-config.spec.ts +++ b/packages/js/src/migrations/update-15-8-0/rename-swcrc-config.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readProjectConfiguration, Tree, diff --git a/packages/next/plugins/component-testing.ts b/packages/next/plugins/component-testing.ts index 4974d33064..f867c4dcc8 100644 --- a/packages/next/plugins/component-testing.ts +++ b/packages/next/plugins/component-testing.ts @@ -29,7 +29,7 @@ export function nxComponentTestingPreset( pathToConfig: string, options?: NxComponentTestingOptions ) { - if (global.NX_GRAPH_CREATION || global.NX_CYPRESS_INIT_GENERATOR_RUNNING) { + if (global.NX_GRAPH_CREATION) { // this is only used by plugins, so we don't need the component testing // options, cast to any to avoid type errors return nxBaseCypressPreset(pathToConfig) as any; diff --git a/packages/next/src/generators/init/init.spec.ts b/packages/next/src/generators/init/init.spec.ts index 96071ccba5..a0b3497ed2 100644 --- a/packages/next/src/generators/init/init.spec.ts +++ b/packages/next/src/generators/init/init.spec.ts @@ -1,12 +1,23 @@ import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; -import { readJson, NxJsonConfiguration, Tree } from '@nx/devkit'; +import { readJson, Tree, ProjectGraph } from '@nx/devkit'; import { nextInitGenerator } from './init'; +let projectGraph: ProjectGraph; +jest.mock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockImplementation(async () => { + return projectGraph; + }), +})); describe('init', () => { let tree: Tree; beforeEach(() => { + projectGraph = { + nodes: {}, + dependencies: {}, + }; tree = createTreeWithEmptyWorkspace(); }); diff --git a/packages/next/src/generators/init/init.ts b/packages/next/src/generators/init/init.ts index bb6351de2f..7608702ef4 100644 --- a/packages/next/src/generators/init/init.ts +++ b/packages/next/src/generators/init/init.ts @@ -5,12 +5,12 @@ import { type GeneratorCallback, type Tree, readNxJson, + createProjectGraphAsync, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; import { reactDomVersion, reactVersion } from '@nx/react/src/utils/versions'; import { addGitIgnoreEntry } from '../../utils/add-gitignore-entry'; import { nextVersion, nxVersion } from '../../utils/versions'; -import { addPlugin } from './lib/add-plugin'; import type { InitSchema } from './schema'; function updateDependencies(host: Tree, schema: InitSchema) { @@ -52,7 +52,24 @@ export async function nextInitGeneratorInternal( schema.addPlugin ??= addPluginDefault; if (schema.addPlugin) { - addPlugin(host); + const { createNodes } = await import('../../plugins/plugin'); + await addPlugin( + host, + await createProjectGraphAsync(), + '@nx/next/plugin', + createNodes, + { + startTargetName: ['start', 'next:start', 'next-start'], + buildTargetName: ['build', 'next:build', 'next-build'], + devTargetName: ['dev', 'next:dev', 'next-dev'], + serveStaticTargetName: [ + 'serve-static', + 'next:serve-static', + 'next-serve-static', + ], + }, + schema.updatePackageScripts + ); } addGitIgnoreEntry(host); @@ -62,11 +79,6 @@ export async function nextInitGeneratorInternal( installTask = updateDependencies(host, schema); } - if (schema.updatePackageScripts) { - const { createNodes } = await import('../../plugins/plugin'); - await updatePackageScripts(host, createNodes); - } - return installTask; } diff --git a/packages/next/src/generators/init/lib/add-plugin.ts b/packages/next/src/generators/init/lib/add-plugin.ts deleted file mode 100644 index 216d065ab4..0000000000 --- a/packages/next/src/generators/init/lib/add-plugin.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Tree, readNxJson, updateNxJson } from '@nx/devkit'; - -export function addPlugin(tree: Tree) { - const nxJson = readNxJson(tree); - nxJson.plugins ??= []; - - for (const plugin of nxJson.plugins) { - if ( - typeof plugin === 'string' - ? plugin === '@nx/next/plugin' - : plugin.plugin === '@nx/next/plugin' - ) { - return; - } - } - - nxJson.plugins.push({ - plugin: '@nx/next/plugin', - options: { - buildTargetName: 'build', - devTargetName: 'dev', - startTargetName: 'start', - serveStaticTargetName: 'serve-static', - }, - }); - - updateNxJson(tree, nxJson); -} diff --git a/packages/nuxt/src/generators/application/application.spec.ts b/packages/nuxt/src/generators/application/application.spec.ts index c1db687c18..ef1210a1ff 100644 --- a/packages/nuxt/src/generators/application/application.spec.ts +++ b/packages/nuxt/src/generators/application/application.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Tree, readJson, readProjectConfiguration } from '@nx/devkit'; import { applicationGenerator } from './application'; @@ -57,6 +59,37 @@ describe('app', () => { expect(tree.read('my-app/project.json', 'utf-8')).toMatchSnapshot(); expect(tree.read('my-app/tsconfig.json', 'utf-8')).toMatchSnapshot(); }); + + it('should add the nuxt and vitest plugins', () => { + const nxJson = readJson(tree, 'nx.json'); + expect(nxJson.plugins).toMatchObject([ + { + plugin: '@nx/eslint/plugin', + options: { targetName: 'lint' }, + }, + { + plugin: '@nx/vite/plugin', + options: { testTargetName: 'test' }, + }, + { + plugin: '@nx/nuxt/plugin', + options: { buildTargetName: 'build', serveTargetName: 'serve' }, + }, + { + plugin: '@nx/cypress/plugin', + options: { targetName: 'e2e' }, + }, + ]); + expect( + nxJson.plugins.indexOf( + nxJson.plugins.find((p) => p.plugin === '@nx/nuxt/plugin') + ) + ).toBeGreaterThan( + nxJson.plugins.indexOf( + nxJson.plugins.find((p) => p.plugin === '@nx/vite/plugin') + ) + ); + }); }); describe('styles setup', () => { diff --git a/packages/nuxt/src/generators/application/application.ts b/packages/nuxt/src/generators/application/application.ts index 24d98a1d61..4b16a33536 100644 --- a/packages/nuxt/src/generators/application/application.ts +++ b/packages/nuxt/src/generators/application/application.ts @@ -40,11 +40,6 @@ export async function applicationGenerator(tree: Tree, schema: Schema) { skipFormat: true, }); tasks.push(jsInitTask); - const nuxtInitTask = await nuxtInitGenerator(tree, { - ...options, - skipFormat: true, - }); - tasks.push(nuxtInitTask); tasks.push(ensureDependencies(tree, options)); addProjectConfiguration(tree, options.name, { @@ -113,6 +108,12 @@ export async function applicationGenerator(tree: Tree, schema: Schema) { tasks.push(await addVitest(tree, options)); } + const nuxtInitTask = await nuxtInitGenerator(tree, { + ...options, + skipFormat: true, + }); + tasks.push(nuxtInitTask); + tasks.push(await addE2e(tree, options)); if (options.js) toJS(tree); diff --git a/packages/nuxt/src/generators/init/init.spec.ts b/packages/nuxt/src/generators/init/init.spec.ts index 0510516139..b5885a2dee 100644 --- a/packages/nuxt/src/generators/init/init.spec.ts +++ b/packages/nuxt/src/generators/init/init.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, readNxJson, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { nuxtInitGenerator } from './init'; @@ -31,10 +33,6 @@ describe('init', () => { options: { buildTargetName: 'build', serveTargetName: 'serve' }, plugin: '@nx/nuxt/plugin', }, - { - options: { testTargetName: 'test' }, - plugin: '@nx/vite/plugin', - }, ]); }); }); diff --git a/packages/nuxt/src/generators/init/init.ts b/packages/nuxt/src/generators/init/init.ts index 30dc670fc8..819b22f609 100644 --- a/packages/nuxt/src/generators/init/init.ts +++ b/packages/nuxt/src/generators/init/init.ts @@ -1,22 +1,28 @@ -import { GeneratorCallback, Tree } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; +import { createProjectGraphAsync, GeneratorCallback, Tree } from '@nx/devkit'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; import { createNodes } from '../../plugins/plugin'; import { InitSchema } from './schema'; -import { addPlugin, updateDependencies } from './lib/utils'; +import { updateDependencies } from './lib/utils'; export async function nuxtInitGenerator(host: Tree, schema: InitSchema) { - addPlugin(host); + await addPlugin( + host, + await createProjectGraphAsync(), + '@nx/nuxt/plugin', + createNodes, + { + buildTargetName: ['build', 'nuxt:build', 'nuxt-build'], + serveTargetName: ['serve', 'nuxt:serve', 'nuxt-serve'], + }, + schema.updatePackageScripts + ); let installTask: GeneratorCallback = () => {}; if (!schema.skipPackageJson) { installTask = updateDependencies(host, schema); } - if (schema.updatePackageScripts) { - await updatePackageScripts(host, createNodes); - } - return installTask; } diff --git a/packages/nuxt/src/generators/storybook-configuration/configuration.spec.ts b/packages/nuxt/src/generators/storybook-configuration/configuration.spec.ts index 21a95f4ada..c2954143b3 100644 --- a/packages/nuxt/src/generators/storybook-configuration/configuration.spec.ts +++ b/packages/nuxt/src/generators/storybook-configuration/configuration.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { logger, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Linter } from '@nx/eslint'; diff --git a/packages/nx/src/command-line/init/implementation/add-nx-to-monorepo.ts b/packages/nx/src/command-line/init/implementation/add-nx-to-monorepo.ts index 4e11f0ee06..a767f5d1fe 100644 --- a/packages/nx/src/command-line/init/implementation/add-nx-to-monorepo.ts +++ b/packages/nx/src/command-line/init/implementation/add-nx-to-monorepo.ts @@ -96,10 +96,7 @@ export async function addNxToMonorepo(options: Options) { ); if (!options.legacy) { packageJsonFiles.forEach((packageJsonPath) => { - markPackageJsonAsNxProject( - join(repoRoot, packageJsonPath), - cacheableOperations - ); + markPackageJsonAsNxProject(join(repoRoot, packageJsonPath)); }); } diff --git a/packages/nx/src/command-line/init/implementation/add-nx-to-npm-repo.ts b/packages/nx/src/command-line/init/implementation/add-nx-to-npm-repo.ts index b8dbffde2f..c8a3263ebe 100644 --- a/packages/nx/src/command-line/init/implementation/add-nx-to-npm-repo.ts +++ b/packages/nx/src/command-line/init/implementation/add-nx-to-npm-repo.ts @@ -85,10 +85,7 @@ export async function addNxToNpmRepo(options: Options) { if (options.legacy) { markRootPackageJsonAsNxProjectLegacy(repoRoot, cacheableOperations, pmc); } else { - markPackageJsonAsNxProject( - join(repoRoot, 'package.json'), - cacheableOperations - ); + markPackageJsonAsNxProject(join(repoRoot, 'package.json')); } output.log({ title: '📦 Installing dependencies' }); diff --git a/packages/nx/src/command-line/init/implementation/utils.ts b/packages/nx/src/command-line/init/implementation/utils.ts index c8bfeb835b..ce133924bc 100644 --- a/packages/nx/src/command-line/init/implementation/utils.ts +++ b/packages/nx/src/command-line/init/implementation/utils.ts @@ -194,21 +194,13 @@ export function markRootPackageJsonAsNxProjectLegacy( writeJsonFile(`package.json`, json); } -export function markPackageJsonAsNxProject( - packageJsonPath: string, - cacheableScripts: string[] -) { +export function markPackageJsonAsNxProject(packageJsonPath: string) { const json = readJsonFile(packageJsonPath); if (!json.scripts) { return; } - json.nx = { includedScripts: [] }; - for (let script of cacheableScripts) { - if (json.scripts[script]) { - json.nx.includedScripts.push(script); - } - } + json.nx = {}; writeJsonFile(packageJsonPath, json); } diff --git a/packages/nx/src/command-line/init/init-v2.ts b/packages/nx/src/command-line/init/init-v2.ts index 84878c1192..62e11f83cb 100644 --- a/packages/nx/src/command-line/init/init-v2.ts +++ b/packages/nx/src/command-line/init/init-v2.ts @@ -74,34 +74,35 @@ export async function initHandler(options: InitArgs): Promise { const { plugins, updatePackageScripts } = await detectPlugins(); - if (!plugins.length) { - // If no plugins are detected/chosen, guide users to setup - // their targetDefaults correctly so their package scripts will work. - const packageJson: PackageJson = readJsonFile('package.json'); - if (isMonorepo(packageJson)) { - await addNxToMonorepo({ interactive: options.interactive }); - } else { - await addNxToNpmRepo({ interactive: options.interactive }); - } + const packageJson: PackageJson = readJsonFile('package.json'); + if (isMonorepo(packageJson)) { + await addNxToMonorepo({ + interactive: options.interactive, + nxCloud: false, + }); } else { - const useNxCloud = - options.nxCloud ?? - (options.interactive - ? await connectExistingRepoToNxCloudPrompt() - : false); + await addNxToNpmRepo({ + interactive: options.interactive, + nxCloud: false, + }); + } + const useNxCloud = + options.nxCloud ?? + (options.interactive ? await connectExistingRepoToNxCloudPrompt() : false); - const repoRoot = process.cwd(); - const pmc = getPackageManagerCommand(); + const repoRoot = process.cwd(); + const pmc = getPackageManagerCommand(); - createNxJsonFile(repoRoot, [], [], {}); - updateGitIgnore(repoRoot); + createNxJsonFile(repoRoot, [], [], {}); + updateGitIgnore(repoRoot); - addDepsToPackageJson(repoRoot, plugins); + addDepsToPackageJson(repoRoot, plugins); - output.log({ title: '📦 Installing Nx' }); + output.log({ title: '📦 Installing Nx' }); - runInstall(repoRoot, pmc); + runInstall(repoRoot, pmc); + if (plugins.length > 0) { output.log({ title: '🔨 Configuring plugins' }); for (const plugin of plugins) { execSync( @@ -114,24 +115,17 @@ export async function initHandler(options: InitArgs): Promise { } ); } + } - if (!updatePackageScripts) { - const rootPackageJsonPath = join(repoRoot, 'package.json'); - const json = readJsonFile(rootPackageJsonPath); - json.nx = { includedScripts: [] }; - writeJsonFile(rootPackageJsonPath, json); - } - - if (useNxCloud) { - output.log({ title: '🛠️ Setting up Nx Cloud' }); - execSync( - `${pmc.exec} nx g nx:connect-to-nx-cloud --installationSource=nx-init --quiet --hideFormatLogs --no-interactive`, - { - stdio: [0, 1, 2], - cwd: repoRoot, - } - ); - } + if (useNxCloud) { + output.log({ title: '🛠️ Setting up Nx Cloud' }); + execSync( + `${pmc.exec} nx g nx:connect-to-nx-cloud --installationSource=nx-init --quiet --hideFormatLogs --no-interactive`, + { + stdio: [0, 1, 2], + cwd: repoRoot, + } + ); } output.log({ @@ -220,9 +214,8 @@ async function detectPlugins(): Promise<{ { name: 'plugins', type: 'multiselect', - message: `Which plugins would you like to add?`, + message: `Which plugins would you like to add? Press to select and to submit.`, choices: plugins.map((p) => ({ name: p, value: p })), - initial: plugins.map((_, i) => i) as unknown as number, // casting to avoid type error due to bad d.ts file from enquirer }, ]).then((r) => r.plugins); diff --git a/packages/nx/src/devkit-internals.ts b/packages/nx/src/devkit-internals.ts index a24b33c986..7e50804f08 100644 --- a/packages/nx/src/devkit-internals.ts +++ b/packages/nx/src/devkit-internals.ts @@ -20,4 +20,7 @@ export { createProjectRootMappingsFromProjectConfigurations, findProjectForPath, } from './project-graph/utils/find-project-for-path'; +export { retrieveProjectConfigurations } from './project-graph/utils/retrieve-workspace-files'; +export { LoadedNxPlugin } from './project-graph/plugins/internal-api'; +export * from './project-graph/error-types'; export { registerTsProject } from './plugins/js/utils/register'; diff --git a/packages/nx/src/internal-testing-utils/mock-project-graph.ts b/packages/nx/src/internal-testing-utils/mock-project-graph.ts new file mode 100644 index 0000000000..59550e717f --- /dev/null +++ b/packages/nx/src/internal-testing-utils/mock-project-graph.ts @@ -0,0 +1,9 @@ +jest.doMock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockImplementation(async () => { + return { + nodes: {}, + dependencies: {}, + }; + }), +})); diff --git a/packages/nx/src/project-graph/error-types.ts b/packages/nx/src/project-graph/error-types.ts index 87ca50d23a..92eb4fef9d 100644 --- a/packages/nx/src/project-graph/error-types.ts +++ b/packages/nx/src/project-graph/error-types.ts @@ -1,9 +1,70 @@ import { CreateNodesResultWithContext } from './plugins/internal-api'; import { ConfigurationResult } from './utils/project-configuration-utils'; +import { ProjectConfiguration } from '../config/workspace-json-project-json'; + +export class ProjectsWithConflictingNamesError extends Error { + constructor( + conflicts: Map, + public projects: Record + ) { + super( + [ + `The following projects are defined in multiple locations:`, + ...Array.from(conflicts.entries()).map(([project, roots]) => + [`- ${project}: `, ...roots.map((r) => ` - ${r}`)].join('\n') + ), + '', + "To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.", + ].join('\n') + ); + this.name = this.constructor.name; + } +} + +export function isProjectsWithConflictingNamesError( + e: unknown +): e is ProjectsWithConflictingNamesError { + return ( + e instanceof ProjectsWithConflictingNamesError || + (typeof e === 'object' && + 'name' in e && + e?.name === ProjectsWithConflictingNamesError.prototype.name) + ); +} + +export class ProjectsWithNoNameError extends Error { + constructor( + projectRoots: string[], + public projects: Record + ) { + super( + `The projects in the following directories have no name provided:\n - ${projectRoots.join( + '\n - ' + )}` + ); + this.name = this.constructor.name; + } +} + +export function isProjectsWithNoNameError( + e: unknown +): e is ProjectsWithNoNameError { + return ( + e instanceof ProjectsWithNoNameError || + (typeof e === 'object' && + 'name' in e && + e?.name === ProjectsWithNoNameError.prototype.name) + ); +} export class ProjectConfigurationsError extends Error { constructor( - public readonly errors: Array, + public readonly errors: Array< + | MergeNodesError + | CreateNodesError + | ProjectsWithNoNameError + | ProjectsWithConflictingNamesError + >, public readonly partialProjectConfigurationsResult: ConfigurationResult ) { super('Failed to create project configurations'); diff --git a/packages/nx/src/project-graph/file-utils.ts b/packages/nx/src/project-graph/file-utils.ts index 800654067c..cffed56f53 100644 --- a/packages/nx/src/project-graph/file-utils.ts +++ b/packages/nx/src/project-graph/file-utils.ts @@ -173,6 +173,7 @@ export function readPackageJson(): any { return {}; // if package.json doesn't exist } } + // Original Exports export { FileData }; // TODO(17): Remove these exports diff --git a/packages/nx/src/project-graph/project-graph.ts b/packages/nx/src/project-graph/project-graph.ts index 05b8967a3c..5fa8035661 100644 --- a/packages/nx/src/project-graph/project-graph.ts +++ b/packages/nx/src/project-graph/project-graph.ts @@ -34,6 +34,8 @@ import { CreateNodesError, MergeNodesError, ProjectConfigurationsError, + ProjectsWithNoNameError, + ProjectsWithConflictingNamesError, } from './error-types'; import { DaemonProjectGraphError } from '../daemon/daemon-project-graph-error'; import { loadNxPlugins, LoadedNxPlugin } from './plugins/internal-api'; @@ -179,7 +181,12 @@ export async function buildProjectGraphAndSourceMapsWithoutDaemon() { export class ProjectGraphError extends Error { readonly #errors: Array< - CreateNodesError | ProcessDependenciesError | ProcessProjectGraphError + | CreateNodesError + | MergeNodesError + | ProjectsWithNoNameError + | ProjectsWithConflictingNamesError + | ProcessDependenciesError + | ProcessProjectGraphError >; readonly #partialProjectGraph: ProjectGraph; readonly #partialSourceMaps: ConfigurationSourceMaps; @@ -188,6 +195,8 @@ export class ProjectGraphError extends Error { errors: Array< | CreateNodesError | MergeNodesError + | ProjectsWithNoNameError + | ProjectsWithConflictingNamesError | ProcessDependenciesError | ProcessProjectGraphError >, diff --git a/packages/nx/src/project-graph/utils/project-configuration-utils.spec.ts b/packages/nx/src/project-graph/utils/project-configuration-utils.spec.ts index 69ad981d3f..4c6380fb58 100644 --- a/packages/nx/src/project-graph/utils/project-configuration-utils.spec.ts +++ b/packages/nx/src/project-graph/utils/project-configuration-utils.spec.ts @@ -1555,7 +1555,11 @@ describe('project-configuration-utils', () => { undefined, {}, ['libs/a/project.json', 'libs/b/project.json'], - [new LoadedNxPlugin(fakeTagPlugin, { plugin: fakeTagPlugin.name })] + [ + new LoadedNxPlugin(fakeTagPlugin, { + plugin: fakeTagPlugin.name, + }), + ] ); expect(projectConfigurations.projects).toEqual({ diff --git a/packages/nx/src/project-graph/utils/project-configuration-utils.ts b/packages/nx/src/project-graph/utils/project-configuration-utils.ts index 20448f4489..13caded32c 100644 --- a/packages/nx/src/project-graph/utils/project-configuration-utils.ts +++ b/packages/nx/src/project-graph/utils/project-configuration-utils.ts @@ -26,6 +26,10 @@ import { MergeNodesError, ProjectConfigurationsError, isAggregateCreateNodesError, + ProjectsWithNoNameError, + ProjectsWithConflictingNamesError, + isProjectsWithConflictingNamesError, + isProjectsWithNoNameError, } from '../error-types'; export type SourceInformation = [file: string, plugin: string]; @@ -321,7 +325,12 @@ export async function createProjectConfigurations( performance.mark('build-project-configs:start'); const results: Array>> = []; - const errors: Array = []; + const errors: Array< + | CreateNodesError + | MergeNodesError + | ProjectsWithNoNameError + | ProjectsWithConflictingNamesError + > = []; // We iterate over plugins first - this ensures that plugins specified first take precedence. for (const { @@ -425,7 +434,21 @@ export async function createProjectConfigurations( Object.assign(externalNodes, pluginExternalNodes); } - const projects = readProjectConfigurationsFromRootMap(projectRootMap); + let projects: Record; + try { + projects = readProjectConfigurationsFromRootMap(projectRootMap); + } catch (e) { + if ( + isProjectsWithNoNameError(e) || + isProjectsWithConflictingNamesError(e) + ) { + projects = e.projects; + errors.push(e); + } else { + throw e; + } + } + const rootMap = createRootMap(projectRootMap); performance.mark('createNodes:merge - end'); @@ -467,7 +490,8 @@ export function readProjectConfigurationsFromRootMap( // If there are projects that have the same name, that is an error. // This object tracks name -> (all roots of projects with that name) // to provide better error messaging. - const errors: Map = new Map(); + const conflicts = new Map(); + const projectRootsWithNoName: string[] = []; for (const [root, configuration] of projectRootMap.entries()) { // We're setting `// targets` as a comment `targets` is empty due to Project Crystal. @@ -478,31 +502,26 @@ export function readProjectConfigurationsFromRootMap( const { name } = readJsonFile(join(root, 'package.json')); configuration.name = name; } catch { - throw new Error(`Project at ${root} has no name provided.`); + projectRootsWithNoName.push(root); } } if (configuration.name in projects) { - let rootErrors = errors.get(configuration.name) ?? [ + let rootErrors = conflicts.get(configuration.name) ?? [ projects[configuration.name].root, ]; rootErrors.push(root); - errors.set(configuration.name, rootErrors); + conflicts.set(configuration.name, rootErrors); + projects[configuration.name] = configuration; } else { projects[configuration.name] = configuration; } } - if (errors.size > 0) { - throw new Error( - [ - `The following projects are defined in multiple locations:`, - ...Array.from(errors.entries()).map(([project, roots]) => - [`- ${project}: `, ...roots.map((r) => ` - ${r}`)].join('\n') - ), - '', - "To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.", - ].join('\n') - ); + if (conflicts.size > 0) { + throw new ProjectsWithConflictingNamesError(conflicts, projects); + } + if (projectRootsWithNoName.length > 0) { + throw new ProjectsWithNoNameError(projectRootsWithNoName, projects); } return projects; } diff --git a/packages/nx/src/project-graph/utils/retrieve-workspace-files.ts b/packages/nx/src/project-graph/utils/retrieve-workspace-files.ts index 1b54a2154c..d61a11cef9 100644 --- a/packages/nx/src/project-graph/utils/retrieve-workspace-files.ts +++ b/packages/nx/src/project-graph/utils/retrieve-workspace-files.ts @@ -6,8 +6,8 @@ import { } from '../../adapter/angular-json'; import { NxJsonConfiguration, readNxJson } from '../../config/nx-json'; import { - createProjectConfigurations, ConfigurationResult, + createProjectConfigurations, } from './project-configuration-utils'; import { LoadedNxPlugin, loadNxPlugins } from '../plugins/internal-api'; import { @@ -16,7 +16,6 @@ import { } from '../../utils/workspace-context'; import { buildAllWorkspaceFiles } from './build-all-workspace-files'; import { join } from 'path'; -import { NxPlugin } from '../plugins'; /** * Walks the workspace directory to create the `projectFileMap`, `ProjectConfigurations` and `allWorkspaceFiles` @@ -60,17 +59,21 @@ export async function retrieveWorkspaceFiles( /** * Walk through the workspace and return `ProjectConfigurations`. Only use this if the projectFileMap is not needed. */ -export async function retrieveProjectConfigurations( + +export function retrieveProjectConfigurations( plugins: LoadedNxPlugin[], workspaceRoot: string, nxJson: NxJsonConfiguration ): Promise { - const projects = await _retrieveProjectConfigurations( + const globPatterns = configurationGlobs(plugins); + const workspaceFiles = globWithWorkspaceContext(workspaceRoot, globPatterns); + + return createProjectConfigurations( workspaceRoot, nxJson, + workspaceFiles, plugins ); - return projects; } export async function retrieveProjectConfigurationsWithAngularProjects( @@ -95,27 +98,11 @@ export async function retrieveProjectConfigurationsWithAngularProjects( workspaceRoot ); - const res = _retrieveProjectConfigurations(workspaceRoot, nxJson, plugins); + const res = retrieveProjectConfigurations(plugins, workspaceRoot, nxJson); cleanup(); return res; } -function _retrieveProjectConfigurations( - workspaceRoot: string, - nxJson: NxJsonConfiguration, - plugins: LoadedNxPlugin[] -): Promise { - const globPatterns = configurationGlobs(plugins); - const workspaceFiles = globWithWorkspaceContext(workspaceRoot, globPatterns); - - return createProjectConfigurations( - workspaceRoot, - nxJson, - workspaceFiles, - plugins - ); -} - export function retrieveProjectConfigurationPaths( root: string, plugins: Array<{ createNodes?: readonly [string, ...unknown[]] } & unknown> diff --git a/packages/nx/src/utils/nx-plugin.deprecated.ts b/packages/nx/src/utils/nx-plugin.deprecated.ts index 6bd67d63ab..f4bde883ad 100644 --- a/packages/nx/src/utils/nx-plugin.deprecated.ts +++ b/packages/nx/src/utils/nx-plugin.deprecated.ts @@ -5,7 +5,6 @@ import ProjectJsonProjectsPlugin from '../plugins/project-json/build-nodes/proje import TargetDefaultsPlugin from '../plugins/target-defaults/target-defaults-plugin'; import * as PackageJsonWorkspacesPlugin from '../plugins/package-json-workspaces'; import { NxPluginV2 } from '../project-graph/plugins'; -import { LoadedNxPlugin } from '../project-graph/plugins/internal-api'; /** * @deprecated Add targets to the projects in a {@link CreateNodes} function instead. This will be removed in Nx 19 diff --git a/packages/playwright/src/generators/init/init.spec.ts b/packages/playwright/src/generators/init/init.spec.ts index f93ee184ab..c4dea0c653 100644 --- a/packages/playwright/src/generators/init/init.spec.ts +++ b/packages/playwright/src/generators/init/init.spec.ts @@ -1,12 +1,24 @@ -import { readNxJson, Tree, updateNxJson } from '@nx/devkit'; +import { ProjectGraph, readNxJson, Tree, updateNxJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { initGenerator } from './init'; +let projectGraph: ProjectGraph; +jest.mock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockImplementation(async () => { + return projectGraph; + }), +})); + describe('@nx/playwright:init', () => { let tree: Tree; beforeEach(() => { + projectGraph = { + nodes: {}, + dependencies: {}, + }; tree = createTreeWithEmptyWorkspace(); }); diff --git a/packages/playwright/src/generators/init/init.ts b/packages/playwright/src/generators/init/init.ts index ac008f8246..ae90ae3a95 100644 --- a/packages/playwright/src/generators/init/init.ts +++ b/packages/playwright/src/generators/init/init.ts @@ -1,13 +1,13 @@ import { addDependenciesToPackageJson, + createProjectGraphAsync, formatFiles, GeneratorCallback, readNxJson, runTasksInSerial, Tree, - updateNxJson, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; import { createNodes } from '../../plugins/plugin'; import { nxVersion, playwrightVersion } from '../../utils/versions'; import { InitGeneratorSchema } from './schema'; @@ -45,11 +45,14 @@ export async function initGeneratorInternal( } if (options.addPlugin) { - addPlugin(tree); - } - - if (options.updatePackageScripts) { - await updatePackageScripts(tree, createNodes); + await addPlugin( + tree, + await createProjectGraphAsync(), + '@nx/playwright/plugin', + createNodes, + { targetName: ['e2e', 'playwright:e2e', 'playwright-e2e'] }, + options.updatePackageScripts + ); } if (!options.skipFormat) { @@ -59,25 +62,4 @@ export async function initGeneratorInternal( return runTasksInSerial(...tasks); } -function addPlugin(tree: Tree) { - const nxJson = readNxJson(tree); - nxJson.plugins ??= []; - - if ( - !nxJson.plugins.some((p) => - typeof p === 'string' - ? p === '@nx/playwright/plugin' - : p.plugin === '@nx/playwright/plugin' - ) - ) { - nxJson.plugins.push({ - plugin: '@nx/playwright/plugin', - options: { - targetName: 'e2e', - }, - }); - updateNxJson(tree, nxJson); - } -} - export default initGenerator; diff --git a/packages/plugin/src/generators/create-package/create-package.spec.ts b/packages/plugin/src/generators/create-package/create-package.spec.ts index b76013fca5..5c2a5875e2 100644 --- a/packages/plugin/src/generators/create-package/create-package.spec.ts +++ b/packages/plugin/src/generators/create-package/create-package.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { joinPathFragments, readJson, diff --git a/packages/plugin/src/generators/e2e-project/e2e.spec.ts b/packages/plugin/src/generators/e2e-project/e2e.spec.ts index 67df561e97..d5fb89da1d 100644 --- a/packages/plugin/src/generators/e2e-project/e2e.spec.ts +++ b/packages/plugin/src/generators/e2e-project/e2e.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree, addProjectConfiguration, diff --git a/packages/plugin/src/generators/executor/executor.spec.ts b/packages/plugin/src/generators/executor/executor.spec.ts index 3215fef82a..e0f827c133 100644 --- a/packages/plugin/src/generators/executor/executor.spec.ts +++ b/packages/plugin/src/generators/executor/executor.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree, readJson, readProjectConfiguration } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { executorGenerator } from './executor'; diff --git a/packages/plugin/src/generators/generator/generator.spec.ts b/packages/plugin/src/generators/generator/generator.spec.ts index b2543f6c37..d21d815eba 100644 --- a/packages/plugin/src/generators/generator/generator.spec.ts +++ b/packages/plugin/src/generators/generator/generator.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { GeneratorsJson, readJson, diff --git a/packages/plugin/src/generators/lint-checks/generator.spec.ts b/packages/plugin/src/generators/lint-checks/generator.spec.ts index 7485c4dd71..21e04270c0 100644 --- a/packages/plugin/src/generators/lint-checks/generator.spec.ts +++ b/packages/plugin/src/generators/lint-checks/generator.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Tree, diff --git a/packages/plugin/src/generators/migration/migration.spec.ts b/packages/plugin/src/generators/migration/migration.spec.ts index 2e1d0ac3f4..2e979aecab 100644 --- a/packages/plugin/src/generators/migration/migration.spec.ts +++ b/packages/plugin/src/generators/migration/migration.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { migrationGenerator } from './migration'; diff --git a/packages/plugin/src/generators/plugin/plugin.spec.ts b/packages/plugin/src/generators/plugin/plugin.spec.ts index ff7e6adae2..6d86517677 100644 --- a/packages/plugin/src/generators/plugin/plugin.spec.ts +++ b/packages/plugin/src/generators/plugin/plugin.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { getProjects, joinPathFragments, diff --git a/packages/plugin/src/generators/preset/generator.spec.ts b/packages/plugin/src/generators/preset/generator.spec.ts index 14dfc06ffb..9a486fddaa 100644 --- a/packages/plugin/src/generators/preset/generator.spec.ts +++ b/packages/plugin/src/generators/preset/generator.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Tree, readProjectConfiguration, readJson } from '@nx/devkit'; diff --git a/packages/plugin/src/migrations/update-15-0-0/specify-output-capture.spec.ts b/packages/plugin/src/migrations/update-15-0-0/specify-output-capture.spec.ts index 1cfb2f9f0e..11c36067a0 100644 --- a/packages/plugin/src/migrations/update-15-0-0/specify-output-capture.spec.ts +++ b/packages/plugin/src/migrations/update-15-0-0/specify-output-capture.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, updateJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Linter } from '@nx/eslint'; diff --git a/packages/plugin/src/migrations/update-15-9-0/update-configs-jest-29.spec.ts b/packages/plugin/src/migrations/update-15-9-0/update-configs-jest-29.spec.ts index 316dc9f29e..581d6dd424 100644 --- a/packages/plugin/src/migrations/update-15-9-0/update-configs-jest-29.spec.ts +++ b/packages/plugin/src/migrations/update-15-9-0/update-configs-jest-29.spec.ts @@ -5,8 +5,6 @@ import { Tree, updateProjectConfiguration, } from '@nx/devkit'; -import { updateConfigsJest29 } from './jest-29-configs'; -import { libraryGenerator } from '@nx/js'; let projectGraph: ProjectGraph; jest.mock('@nx/devkit', () => ({ @@ -16,6 +14,9 @@ jest.mock('@nx/devkit', () => ({ }), })); +import { updateConfigsJest29 } from './jest-29-configs'; +import { libraryGenerator } from '@nx/js'; + describe('Nx Plugin Migration - jest 29 update configs', () => { let tree: Tree; diff --git a/packages/plugin/src/migrations/update-15-9-0/update-tests-jest-29.spec.ts b/packages/plugin/src/migrations/update-15-9-0/update-tests-jest-29.spec.ts index 7ee6cdf0ba..a8c1ed80fc 100644 --- a/packages/plugin/src/migrations/update-15-9-0/update-tests-jest-29.spec.ts +++ b/packages/plugin/src/migrations/update-15-9-0/update-tests-jest-29.spec.ts @@ -5,8 +5,6 @@ import { updateProjectConfiguration, } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; -import { libraryGenerator } from '@nx/js'; -import { updateTestsJest29 } from './jest-29-tests'; let projectGraph: ProjectGraph; jest.mock('@nx/devkit', () => ({ @@ -15,6 +13,10 @@ jest.mock('@nx/devkit', () => ({ .fn() .mockImplementation(async () => projectGraph), })); + +import { libraryGenerator } from '@nx/js'; +import { updateTestsJest29 } from './jest-29-tests'; + describe('Nx Plugin Migration - jest 29 mocked usage in tests', () => { let tree: Tree; beforeEach(() => { diff --git a/packages/plugin/src/migrations/update-16-0-0/cli-in-schema-json.spec.ts b/packages/plugin/src/migrations/update-16-0-0/cli-in-schema-json.spec.ts index 70aead7bde..d338f68861 100644 --- a/packages/plugin/src/migrations/update-16-0-0/cli-in-schema-json.spec.ts +++ b/packages/plugin/src/migrations/update-16-0-0/cli-in-schema-json.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { ExecutorsJson, GeneratorsJson, diff --git a/packages/react-native/src/generators/application/application.spec.ts b/packages/react-native/src/generators/application/application.spec.ts index 14db3d8449..ca2343713d 100644 --- a/packages/react-native/src/generators/application/application.spec.ts +++ b/packages/react-native/src/generators/application/application.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree, getProjects, diff --git a/packages/react-native/src/generators/component/component.spec.ts b/packages/react-native/src/generators/component/component.spec.ts index 809087900b..6b6a8ac376 100644 --- a/packages/react-native/src/generators/component/component.spec.ts +++ b/packages/react-native/src/generators/component/component.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { logger, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createApp, createLib } from '../../utils/testing-generators'; diff --git a/packages/react-native/src/generators/init/init.spec.ts b/packages/react-native/src/generators/init/init.spec.ts index 85ccf173c1..2e0939bc08 100644 --- a/packages/react-native/src/generators/init/init.spec.ts +++ b/packages/react-native/src/generators/init/init.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree, readJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { reactNativeInitGenerator } from './init'; diff --git a/packages/react-native/src/generators/init/init.ts b/packages/react-native/src/generators/init/init.ts index 5a1e5a21ff..be1331f55c 100644 --- a/packages/react-native/src/generators/init/init.ts +++ b/packages/react-native/src/generators/init/init.ts @@ -1,15 +1,15 @@ import { addDependenciesToPackageJson, + createProjectGraphAsync, formatFiles, GeneratorCallback, readNxJson, removeDependenciesFromPackageJson, runTasksInSerial, Tree, - updateNxJson, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; -import { createNodes, ReactNativePluginOptions } from '../../../plugins/plugin'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; +import { createNodes } from '../../../plugins/plugin'; import { nxVersion, reactDomVersion, @@ -39,7 +39,57 @@ export async function reactNativeInitGeneratorInternal( schema.addPlugin ??= addPluginDefault; if (schema.addPlugin) { - addPlugin(host); + await addPlugin( + host, + await createProjectGraphAsync(), + '@nx/react-native/plugin', + createNodes, + { + startTargetName: ['start', 'react-native:start', 'react-native-start'], + upgradeTargetname: [ + 'update', + 'react-native:update', + 'react-native-update', + ], + bundleTargetName: [ + 'bundle', + 'react-native:bundle', + 'react-native-bundle', + ], + + podInstallTargetName: [ + 'pod-install', + 'react-native:pod-install', + 'react-native-pod-install', + ], + runIosTargetName: [ + 'run-ios', + 'react-native:run-ios', + 'react-native-run-ios', + ], + runAndroidTargetName: [ + 'run-android', + 'react-native:run-android', + 'react-native-run-android', + ], + buildIosTargetName: [ + 'build-ios', + 'react-native:build-ios', + 'react-native-build-ios', + ], + buildAndroidTargetName: [ + 'build-android', + 'react-native:build-android', + 'react-native-build-android', + ], + syncDepsTargetName: [ + 'sync-deps', + 'react-native:sync-deps', + 'react-native-sync-deps', + ], + }, + schema.updatePackageScripts + ); } const tasks: GeneratorCallback[] = []; @@ -48,10 +98,6 @@ export async function reactNativeInitGeneratorInternal( tasks.push(updateDependencies(host, schema)); } - if (schema.updatePackageScripts) { - await updatePackageScripts(host, createNodes); - } - if (!schema.skipFormat) { await formatFiles(host); } @@ -79,35 +125,4 @@ function moveDependency(host: Tree) { return removeDependenciesFromPackageJson(host, ['@nx/react-native'], []); } -function addPlugin(host: Tree) { - const nxJson = readNxJson(host); - nxJson.plugins ??= []; - - for (const plugin of nxJson.plugins) { - if ( - typeof plugin === 'string' - ? plugin === '@nx/react-native/plugin' - : plugin.plugin === '@nx/react-native/plugin' - ) { - return; - } - } - - nxJson.plugins.push({ - plugin: '@nx/react-native/plugin', - options: { - startTargetName: 'start', - podInstallTargetName: 'pod-install', - bundleTargetName: 'bundle', - runIosTargetName: 'run-ios', - runAndroidTargetName: 'run-android', - buildIosTargetName: 'build-ios', - buildAndroidTargetName: 'build-android', - syncDepsTargetName: 'sync-deps', - upgradeTargetname: 'upgrade', - } as ReactNativePluginOptions, - }); - updateNxJson(host, nxJson); -} - export default reactNativeInitGenerator; diff --git a/packages/react-native/src/generators/library/library.spec.ts b/packages/react-native/src/generators/library/library.spec.ts index 9ecd71d0eb..0c0d799ac2 100644 --- a/packages/react-native/src/generators/library/library.spec.ts +++ b/packages/react-native/src/generators/library/library.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, readProjectConfiguration, diff --git a/packages/react-native/src/utils/add-linting.spec.ts b/packages/react-native/src/utils/add-linting.spec.ts index dda057ead8..388026a4a5 100644 --- a/packages/react-native/src/utils/add-linting.spec.ts +++ b/packages/react-native/src/utils/add-linting.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Linter } from '@nx/eslint'; diff --git a/packages/react/plugins/component-testing/index.ts b/packages/react/plugins/component-testing/index.ts index ac7ad6f292..d43afb00a2 100644 --- a/packages/react/plugins/component-testing/index.ts +++ b/packages/react/plugins/component-testing/index.ts @@ -66,7 +66,7 @@ export function nxComponentTestingPreset( testingType: 'component', }); - if (global.NX_GRAPH_CREATION || global.NX_CYPRESS_INIT_GENERATOR_RUNNING) { + if (global.NX_GRAPH_CREATION) { // this is only used by plugins, so we don't need the component testing // options, cast to any to avoid type errors return basePresetSettings as any; diff --git a/packages/react/src/generators/application/application.legacy.spec.ts b/packages/react/src/generators/application/application.legacy.spec.ts index 5db0430df7..7f3701f655 100644 --- a/packages/react/src/generators/application/application.legacy.spec.ts +++ b/packages/react/src/generators/application/application.legacy.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { getProjects, readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts index fcae904d1b..b834227e75 100644 --- a/packages/react/src/generators/application/application.spec.ts +++ b/packages/react/src/generators/application/application.spec.ts @@ -1,9 +1,10 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { getProjects, readJson, readNxJson, - readProjectConfiguration, Tree, updateNxJson, } from '@nx/devkit'; diff --git a/packages/react/src/generators/component-cypress-spec/component-cypress-spec.spec.ts b/packages/react/src/generators/component-cypress-spec/component-cypress-spec.spec.ts index 42bb2ac866..6307d4bec5 100644 --- a/packages/react/src/generators/component-cypress-spec/component-cypress-spec.spec.ts +++ b/packages/react/src/generators/component-cypress-spec/component-cypress-spec.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Linter } from '@nx/eslint'; diff --git a/packages/react/src/generators/component-story/component-story.spec.ts b/packages/react/src/generators/component-story/component-story.spec.ts index 4102c79ba6..3945b70569 100644 --- a/packages/react/src/generators/component-story/component-story.spec.ts +++ b/packages/react/src/generators/component-story/component-story.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { getProjects, Tree, updateProjectConfiguration } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import libraryGenerator from '../library/library'; diff --git a/packages/react/src/generators/component-test/component-test.spec.ts b/packages/react/src/generators/component-test/component-test.spec.ts index 4b5fa164ec..b6b64387a8 100644 --- a/packages/react/src/generators/component-test/component-test.spec.ts +++ b/packages/react/src/generators/component-test/component-test.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { assertMinimumCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/react/src/generators/component/component.spec.ts b/packages/react/src/generators/component/component.spec.ts index b5d278cc48..72c77fabb2 100644 --- a/packages/react/src/generators/component/component.spec.ts +++ b/packages/react/src/generators/component/component.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { logger, diff --git a/packages/react/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts b/packages/react/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts index 92900a3adc..ded692ebb0 100644 --- a/packages/react/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts +++ b/packages/react/src/generators/cypress-component-configuration/cypress-component-configuration.spec.ts @@ -7,11 +7,6 @@ import { updateProjectConfiguration, } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; -import { Linter } from '@nx/eslint'; -import { applicationGenerator } from '../application/application'; -import { componentGenerator } from '../component/component'; -import { libraryGenerator } from '../library/library'; -import { cypressComponentConfigGenerator } from './cypress-component-configuration'; let projectGraph: ProjectGraph; jest.mock('@nx/devkit', () => ({ @@ -21,6 +16,13 @@ jest.mock('@nx/devkit', () => ({ .fn() .mockImplementation(async () => projectGraph), })); + +import { Linter } from '@nx/eslint'; +import { applicationGenerator } from '../application/application'; +import { componentGenerator } from '../component/component'; +import { libraryGenerator } from '../library/library'; +import { cypressComponentConfigGenerator } from './cypress-component-configuration'; + jest.mock('@nx/cypress/src/utils/cypress-version'); // nested code imports graph from the repo, which might have innacurate graph version jest.mock('nx/src/project-graph/project-graph', () => ({ @@ -470,9 +472,9 @@ describe('React:CypressComponentTestConfiguration', () => { buildTarget: 'my-app:build', }); }).resolves; - expect( - require('@nx/devkit').createProjectGraphAsync - ).not.toHaveBeenCalled(); + expect(require('@nx/devkit').createProjectGraphAsync).toHaveBeenCalledTimes( + 1 + ); }); it('should setup cypress config files correctly', async () => { diff --git a/packages/react/src/generators/federate-module/federate-module.spec.ts b/packages/react/src/generators/federate-module/federate-module.spec.ts index 8787192729..237f2de9a1 100644 --- a/packages/react/src/generators/federate-module/federate-module.spec.ts +++ b/packages/react/src/generators/federate-module/federate-module.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree, getProjects } from '@nx/devkit'; import { Schema } from './schema'; import { Schema as remoteSchma } from '../remote/schema'; diff --git a/packages/react/src/generators/hook/hook.spec.ts b/packages/react/src/generators/hook/hook.spec.ts index e5a5c3a6b7..af732193a4 100644 --- a/packages/react/src/generators/hook/hook.spec.ts +++ b/packages/react/src/generators/hook/hook.spec.ts @@ -1,5 +1,7 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createApp, createLib } from '../../utils/testing-generators'; -import { logger, readJson, Tree } from '@nx/devkit'; +import { logger, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { hookGenerator } from './hook'; diff --git a/packages/react/src/generators/library/library.spec.ts b/packages/react/src/generators/library/library.spec.ts index 3f29de13dc..80de7cf231 100644 --- a/packages/react/src/generators/library/library.spec.ts +++ b/packages/react/src/generators/library/library.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { getProjects, diff --git a/packages/react/src/generators/redux/redux.spec.ts b/packages/react/src/generators/redux/redux.spec.ts index e8afcbfe8b..65593e8c50 100644 --- a/packages/react/src/generators/redux/redux.spec.ts +++ b/packages/react/src/generators/redux/redux.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Linter } from '@nx/eslint'; diff --git a/packages/react/src/generators/remote/remote.spec.ts b/packages/react/src/generators/remote/remote.spec.ts index 51da2f98cb..7352126fb7 100644 --- a/packages/react/src/generators/remote/remote.spec.ts +++ b/packages/react/src/generators/remote/remote.spec.ts @@ -1,4 +1,5 @@ -import * as devkit from '@nx/devkit'; +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { ProjectGraph, readJson, readNxJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Linter } from '@nx/eslint'; diff --git a/packages/react/src/generators/stories/stories.app.spec.ts b/packages/react/src/generators/stories/stories.app.spec.ts index eff11ed002..75026ad3e8 100644 --- a/packages/react/src/generators/stories/stories.app.spec.ts +++ b/packages/react/src/generators/stories/stories.app.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/react/src/generators/stories/stories.lib.spec.ts b/packages/react/src/generators/stories/stories.lib.spec.ts index ad89c17b7d..e48d10a7a8 100644 --- a/packages/react/src/generators/stories/stories.lib.spec.ts +++ b/packages/react/src/generators/stories/stories.lib.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree } from '@nx/devkit'; import storiesGenerator from './stories'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/react/src/generators/stories/stories.nextjs.spec.ts b/packages/react/src/generators/stories/stories.nextjs.spec.ts index c152e3e2ea..bc54fe60a6 100644 --- a/packages/react/src/generators/stories/stories.nextjs.spec.ts +++ b/packages/react/src/generators/stories/stories.nextjs.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree } from '@nx/devkit'; import storiesGenerator from './stories'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/react/src/generators/storybook-configuration/configuration.spec.ts b/packages/react/src/generators/storybook-configuration/configuration.spec.ts index 60c31572d1..62778e9820 100644 --- a/packages/react/src/generators/storybook-configuration/configuration.spec.ts +++ b/packages/react/src/generators/storybook-configuration/configuration.spec.ts @@ -27,10 +27,10 @@ describe('react:storybook-configuration', () => { mockedInstalledCypressVersion.mockReturnValue(10); jest.spyOn(logger, 'warn').mockImplementation(() => {}); jest.spyOn(logger, 'debug').mockImplementation(() => {}); - jest.resetModules(); }); afterEach(() => { + jest.resetModules(); jest.restoreAllMocks(); }); diff --git a/packages/remix/src/generators/action/action.impl.spec.ts b/packages/remix/src/generators/action/action.impl.spec.ts index 86ec128650..f2b9fe9820 100644 --- a/packages/remix/src/generators/action/action.impl.spec.ts +++ b/packages/remix/src/generators/action/action.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + jest.mock('../../utils/remix-config'); import * as remixConfigUtils from '../../utils/remix-config'; diff --git a/packages/remix/src/generators/application/application.impl.spec.ts b/packages/remix/src/generators/application/application.impl.spec.ts index 2fd13383e4..1e9d681ac9 100644 --- a/packages/remix/src/generators/application/application.impl.spec.ts +++ b/packages/remix/src/generators/application/application.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import type { Tree } from '@nx/devkit'; import { joinPathFragments, readJson } from '@nx/devkit'; import { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils'; diff --git a/packages/remix/src/generators/cypress-component-configuration/cypress-component-configuration.impl.spec.ts b/packages/remix/src/generators/cypress-component-configuration/cypress-component-configuration.impl.spec.ts index 06898fbec1..0ecf702ea4 100644 --- a/packages/remix/src/generators/cypress-component-configuration/cypress-component-configuration.impl.spec.ts +++ b/packages/remix/src/generators/cypress-component-configuration/cypress-component-configuration.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { joinPathFragments, readProjectConfiguration } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import libraryGenerator from '../library/library.impl'; diff --git a/packages/remix/src/generators/init/init.spec.ts b/packages/remix/src/generators/init/init.spec.ts index 48fc5639d9..92b6626577 100644 --- a/packages/remix/src/generators/init/init.spec.ts +++ b/packages/remix/src/generators/init/init.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { readJson } from '@nx/devkit'; import initGenerator from './init'; diff --git a/packages/remix/src/generators/init/init.ts b/packages/remix/src/generators/init/init.ts index 6f4d4e5ec1..80e7214b18 100644 --- a/packages/remix/src/generators/init/init.ts +++ b/packages/remix/src/generators/init/init.ts @@ -3,42 +3,18 @@ import { formatFiles, GeneratorCallback, readNxJson, - updateNxJson, addDependenciesToPackageJson, runTasksInSerial, + createProjectGraphAsync, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; +import { + addPlugin, + generateCombinations, +} from '@nx/devkit/src/utils/add-plugin'; import { createNodes } from '../../plugins/plugin'; import { nxVersion, remixVersion } from '../../utils/versions'; import { type Schema } from './schema'; -function addPlugin(tree) { - const nxJson = readNxJson(tree); - nxJson.plugins ??= []; - - for (const plugin of nxJson.plugins) { - if ( - typeof plugin === 'string' - ? plugin === '@nx/remix/plugin' - : plugin.plugin === '@nx/remix/plugin' - ) { - return; - } - } - - nxJson.plugins.push({ - plugin: '@nx/remix/plugin', - options: { - buildTargetName: 'build', - devTargetName: 'dev', - startTargetName: 'start', - typecheckTargetName: 'typecheck', - }, - }); - - updateNxJson(tree, nxJson); -} - export function remixInitGenerator(tree: Tree, options: Schema) { return remixInitGeneratorInternal(tree, { addPlugin: false, ...options }); } @@ -68,11 +44,23 @@ export async function remixInitGeneratorInternal(tree: Tree, options: Schema) { nxJson.useInferencePlugins !== false; options.addPlugin ??= addPluginDefault; if (options.addPlugin) { - addPlugin(tree); - } - - if (options.updatePackageScripts) { - await updatePackageScripts(tree, createNodes); + await addPlugin( + tree, + await createProjectGraphAsync(), + '@nx/remix/plugin', + createNodes, + { + startTargetName: ['start', 'remix:start', 'remix-start'], + buildTargetName: ['build', 'remix:build', 'remix-build'], + devTargetName: ['dev', 'remix:dev', 'remix-dev'], + typecheckTargetName: [ + 'typecheck', + 'remix:typecheck', + 'remix-typecheck', + ], + }, + options.updatePackageScripts + ); } if (!options.skipFormat) { diff --git a/packages/remix/src/generators/library/library.impl.spec.ts b/packages/remix/src/generators/library/library.impl.spec.ts index 3b0078947a..0b95e8565e 100644 --- a/packages/remix/src/generators/library/library.impl.spec.ts +++ b/packages/remix/src/generators/library/library.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, readProjectConfiguration } from '@nx/devkit'; import { type ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/remix/src/generators/loader/loader.impl.spec.ts b/packages/remix/src/generators/loader/loader.impl.spec.ts index 6b90cc13ac..2141192688 100644 --- a/packages/remix/src/generators/loader/loader.impl.spec.ts +++ b/packages/remix/src/generators/loader/loader.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + jest.mock('../../utils/remix-config'); import * as remixConfigUtils from '../../utils/remix-config'; diff --git a/packages/remix/src/generators/meta/lib/v2.impl.spec.ts b/packages/remix/src/generators/meta/lib/v2.impl.spec.ts index 71cae49ec1..c3decba551 100644 --- a/packages/remix/src/generators/meta/lib/v2.impl.spec.ts +++ b/packages/remix/src/generators/meta/lib/v2.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + jest.mock('../../../utils/remix-config'); import * as remixConfigUtils from '../../../utils/remix-config'; diff --git a/packages/remix/src/generators/meta/meta.impl.spec.ts b/packages/remix/src/generators/meta/meta.impl.spec.ts index e32b2fd551..e24d10f801 100644 --- a/packages/remix/src/generators/meta/meta.impl.spec.ts +++ b/packages/remix/src/generators/meta/meta.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + jest.mock('../../utils/remix-config'); import * as remixConfigUtils from '../../utils/remix-config'; diff --git a/packages/remix/src/generators/resource-route/resource-route.impl.spec.ts b/packages/remix/src/generators/resource-route/resource-route.impl.spec.ts index 033c3b92f1..86a58b457b 100644 --- a/packages/remix/src/generators/resource-route/resource-route.impl.spec.ts +++ b/packages/remix/src/generators/resource-route/resource-route.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + jest.mock('../../utils/remix-config'); import * as remixConfigUtils from '../../utils/remix-config'; diff --git a/packages/remix/src/generators/route/route.impl.spec.ts b/packages/remix/src/generators/route/route.impl.spec.ts index 506ca3b138..d3f9cbb61c 100644 --- a/packages/remix/src/generators/route/route.impl.spec.ts +++ b/packages/remix/src/generators/route/route.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + jest.mock('../../utils/remix-config'); import * as remixConfigUtils from '../../utils/remix-config'; import { Tree } from '@nx/devkit'; diff --git a/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.spec.ts b/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.spec.ts index 3b842f3209..00833faa9b 100644 --- a/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.spec.ts +++ b/packages/remix/src/generators/setup-tailwind/setup-tailwind.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import applicationGenerator from '../application/application.impl'; diff --git a/packages/remix/src/generators/setup/setup.impl.spec.ts b/packages/remix/src/generators/setup/setup.impl.spec.ts index f94c9c3c09..6fa8bb4a9c 100644 --- a/packages/remix/src/generators/setup/setup.impl.spec.ts +++ b/packages/remix/src/generators/setup/setup.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import setupGenerator from './setup.impl'; diff --git a/packages/remix/src/generators/storybook-configuration/storybook-configuration.impl.spec.ts b/packages/remix/src/generators/storybook-configuration/storybook-configuration.impl.spec.ts index 1f7b0e1e0f..39266366d8 100644 --- a/packages/remix/src/generators/storybook-configuration/storybook-configuration.impl.spec.ts +++ b/packages/remix/src/generators/storybook-configuration/storybook-configuration.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import libraryGenerator from '../library/library.impl'; import storybookConfigurationGenerator from './storybook-configuration.impl'; diff --git a/packages/remix/src/generators/style/style.impl.spec.ts b/packages/remix/src/generators/style/style.impl.spec.ts index 6aec4acedd..d3a5e74d0f 100644 --- a/packages/remix/src/generators/style/style.impl.spec.ts +++ b/packages/remix/src/generators/style/style.impl.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + jest.mock('../../utils/remix-config'); import * as remixConfigUtils from '../../utils/remix-config'; import { Tree } from '@nx/devkit'; diff --git a/packages/rollup/src/generators/configuration/configuration.spec.ts b/packages/rollup/src/generators/configuration/configuration.spec.ts index fc70c47b37..4f04fc2ae0 100644 --- a/packages/rollup/src/generators/configuration/configuration.spec.ts +++ b/packages/rollup/src/generators/configuration/configuration.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, readJson, diff --git a/packages/rollup/src/generators/init/init.spec.ts b/packages/rollup/src/generators/init/init.spec.ts index 70658c5df0..c01555d689 100644 --- a/packages/rollup/src/generators/init/init.spec.ts +++ b/packages/rollup/src/generators/init/init.spec.ts @@ -1,13 +1,27 @@ -import { Tree, readJson } from '@nx/devkit'; +import 'nx/src/internal-testing-utils/mock-project-graph'; + +import { Tree, readJson, ProjectGraph } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { nxVersion } from '../../utils/versions'; import { rollupInitGenerator } from './init'; +let projectGraph: ProjectGraph; +jest.mock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockImplementation(async () => { + return projectGraph; + }), +})); + describe('rollupInitGenerator', () => { let tree: Tree; beforeEach(async () => { + projectGraph = { + nodes: {}, + dependencies: {}, + }; tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); }); diff --git a/packages/rollup/src/generators/init/init.ts b/packages/rollup/src/generators/init/init.ts index dda52be7c5..59be1a26d7 100644 --- a/packages/rollup/src/generators/init/init.ts +++ b/packages/rollup/src/generators/init/init.ts @@ -1,40 +1,15 @@ import { addDependenciesToPackageJson, + createProjectGraphAsync, formatFiles, GeneratorCallback, Tree, - readNxJson, - updateNxJson, } from '@nx/devkit'; import { nxVersion } from '../../utils/versions'; import { Schema } from './schema'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; import { createNodes } from '../../plugins/plugin'; -function addPlugin(tree: Tree) { - const nxJson = readNxJson(tree); - nxJson.plugins ??= []; - - for (const plugin of nxJson.plugins) { - if ( - typeof plugin === 'string' - ? plugin === '@nx/rollup/plugin' - : plugin.plugin === '@nx/rollup/plugin' - ) { - return; - } - } - - nxJson.plugins.push({ - plugin: '@nx/rollup/plugin', - options: { - buildTargetName: 'build', - }, - }); - - updateNxJson(tree, nxJson); -} - export async function rollupInitGenerator(tree: Tree, schema: Schema) { let task: GeneratorCallback = () => {}; @@ -50,11 +25,16 @@ export async function rollupInitGenerator(tree: Tree, schema: Schema) { schema.addPlugin ??= process.env.NX_ADD_PLUGINS !== 'false'; if (schema.addPlugin) { - addPlugin(tree); - } - - if (schema.updatePackageScripts) { - await updatePackageScripts(tree, createNodes); + await addPlugin( + tree, + await createProjectGraphAsync(), + '@nx/rollup/plugin', + createNodes, + { + buildTargetName: ['build', 'rollup:build', 'rollup-build'], + }, + schema.updatePackageScripts + ); } if (!schema.skipFormat) { diff --git a/packages/storybook/src/generators/init/init.spec.ts b/packages/storybook/src/generators/init/init.spec.ts index 3ddf5198e3..36338752e5 100644 --- a/packages/storybook/src/generators/init/init.spec.ts +++ b/packages/storybook/src/generators/init/init.spec.ts @@ -1,11 +1,27 @@ -import { readJson, type NxJsonConfiguration, type Tree } from '@nx/devkit'; +import { + readJson, + type NxJsonConfiguration, + type Tree, + ProjectGraph, +} from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { initGenerator } from './init'; +let projectGraph: ProjectGraph; +jest.mock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockImplementation(async () => { + return projectGraph; + }), +})); describe('@nx/storybook:init', () => { let tree: Tree; beforeEach(() => { + projectGraph = { + nodes: {}, + dependencies: {}, + }; tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); }); diff --git a/packages/storybook/src/generators/init/init.ts b/packages/storybook/src/generators/init/init.ts index 7bedbaead4..bc754beb73 100644 --- a/packages/storybook/src/generators/init/init.ts +++ b/packages/storybook/src/generators/init/init.ts @@ -1,5 +1,6 @@ import { addDependenciesToPackageJson, + createProjectGraphAsync, formatFiles, GeneratorCallback, installPackagesTask, @@ -9,11 +10,10 @@ import { updateJson, updateNxJson, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; import { gte } from 'semver'; import { createNodes } from '../../plugins/plugin'; import { - addPlugin, getInstalledStorybookVersion, storybookMajorVersion, } from '../../utils/utilities'; @@ -102,7 +102,37 @@ export async function initGeneratorInternal(tree: Tree, schema: Schema) { schema.addPlugin ??= addPluginDefault; if (schema.addPlugin) { - addPlugin(tree); + await addPlugin( + tree, + await createProjectGraphAsync(), + '@nx/storybook/plugin', + createNodes, + { + serveStorybookTargetName: [ + 'storybook', + 'serve:storybook', + 'serve-storybook', + 'storybook:serve', + 'storybook-serve', + ], + buildStorybookTargetName: [ + 'build-storybook', + 'build:storybook', + 'storybook:build', + ], + testStorybookTargetName: [ + 'test-storybook', + 'test:storybook', + 'storybook:test', + ], + staticStorybookTargetName: [ + 'static-storybook', + 'static:storybook', + 'storybook:static', + ], + }, + schema.updatePackageScripts + ); updateGitignore(tree); } else { addCacheableOperation(tree); @@ -114,10 +144,6 @@ export async function initGeneratorInternal(tree: Tree, schema: Schema) { tasks.push(checkDependenciesInstalled(tree, schema)); } - if (schema.updatePackageScripts) { - await updatePackageScripts(tree, createNodes); - } - if (!schema.skipFormat) { await formatFiles(tree); } diff --git a/packages/storybook/src/migrations/update-15-7-0/add-addon-essentials-to-all.spec.ts b/packages/storybook/src/migrations/update-15-7-0/add-addon-essentials-to-all.spec.ts index 01e5b27e8e..130b5d5740 100644 --- a/packages/storybook/src/migrations/update-15-7-0/add-addon-essentials-to-all.spec.ts +++ b/packages/storybook/src/migrations/update-15-7-0/add-addon-essentials-to-all.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, ProjectConfiguration, diff --git a/packages/storybook/src/utils/utilities.ts b/packages/storybook/src/utils/utilities.ts index e44bbfa0a9..5ddb355b51 100644 --- a/packages/storybook/src/utils/utilities.ts +++ b/packages/storybook/src/utils/utilities.ts @@ -275,29 +275,3 @@ export function pleaseUpgrade(): string { https://nx.dev/nx-api/storybook/generators/migrate-7 `; } - -export function addPlugin(tree: Tree) { - const nxJson = readNxJson(tree); - nxJson.plugins ??= []; - - for (const plugin of nxJson.plugins) { - if ( - typeof plugin === 'string' - ? plugin === '@nx/storybook/plugin' - : plugin.plugin === '@nx/storybook/plugin' - ) { - return; - } - } - - nxJson.plugins.push({ - plugin: '@nx/storybook/plugin', - options: { - buildStorybookTargetName: 'build-storybook', - serveStorybookTargetName: 'storybook', - testStorybookTargetName: 'test-storybook', - staticStorybookTargetName: 'static-storybook', - }, - }); - updateNxJson(tree, nxJson); -} diff --git a/packages/vite/src/generators/configuration/configuration.spec.ts b/packages/vite/src/generators/configuration/configuration.spec.ts index 88ffc7e045..8a84712bd8 100644 --- a/packages/vite/src/generators/configuration/configuration.spec.ts +++ b/packages/vite/src/generators/configuration/configuration.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addDependenciesToPackageJson, readJson, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { nxVersion } from '../../utils/versions'; diff --git a/packages/vite/src/generators/init/init.spec.ts b/packages/vite/src/generators/init/init.spec.ts index c9c03855f2..66e70f9d6d 100644 --- a/packages/vite/src/generators/init/init.spec.ts +++ b/packages/vite/src/generators/init/init.spec.ts @@ -1,6 +1,7 @@ import { addDependenciesToPackageJson, NxJsonConfiguration, + ProjectGraph, readJson, readNxJson, Tree, @@ -9,13 +10,25 @@ import { import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { nxVersion } from '../../utils/versions'; +let projectGraph: ProjectGraph; +jest.mock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockImplementation(async () => { + return projectGraph; + }), +})); + import { initGenerator } from './init'; describe('@nx/vite:init', () => { let tree: Tree; beforeEach(() => { - tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); + tree = createTreeWithEmptyWorkspace(); + projectGraph = { + nodes: {}, + dependencies: {}, + }; }); describe('dependencies for package.json', () => { diff --git a/packages/vite/src/generators/init/init.ts b/packages/vite/src/generators/init/init.ts index bd5637531e..5d7fd3d602 100644 --- a/packages/vite/src/generators/init/init.ts +++ b/packages/vite/src/generators/init/init.ts @@ -1,4 +1,5 @@ import { + createProjectGraphAsync, formatFiles, GeneratorCallback, readNxJson, @@ -6,15 +7,11 @@ import { Tree, updateNxJson, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; import { createNodes } from '../../plugins/plugin'; import { InitGeneratorSchema } from './schema'; -import { - addPlugin, - checkDependenciesInstalled, - moveToDevDependencies, -} from './lib/utils'; +import { checkDependenciesInstalled, moveToDevDependencies } from './lib/utils'; export function updateNxJsonSettings(tree: Tree) { const nxJson = readNxJson(tree); @@ -61,8 +58,26 @@ export async function initGeneratorInternal( process.env.NX_ADD_PLUGINS !== 'false' && nxJson.useInferencePlugins !== false; schema.addPlugin ??= addPluginDefault; + if (schema.addPlugin) { - addPlugin(tree); + await addPlugin( + tree, + await createProjectGraphAsync(), + '@nx/vite/plugin', + createNodes, + { + buildTargetName: ['build', 'vite:build', 'vite-build'], + testTargetName: ['test', 'vite:test', 'vite-test'], + serveTargetName: ['serve', 'vite:serve', 'vite-serve'], + previewTargetName: ['preview', 'vite:preview', 'vite-preview'], + serveStaticTargetName: [ + 'serve-static', + 'vite:serve-static', + 'vite-serve-static', + ], + }, + schema.updatePackageScripts + ); } updateNxJsonSettings(tree); @@ -73,10 +88,6 @@ export async function initGeneratorInternal( tasks.push(checkDependenciesInstalled(tree, schema)); } - if (schema.updatePackageScripts) { - await updatePackageScripts(tree, createNodes); - } - if (!schema.skipFormat) { await formatFiles(tree); } diff --git a/packages/vite/src/generators/init/lib/utils.ts b/packages/vite/src/generators/init/lib/utils.ts index 6594c1c7ad..9df4fd31a1 100644 --- a/packages/vite/src/generators/init/lib/utils.ts +++ b/packages/vite/src/generators/init/lib/utils.ts @@ -61,30 +61,3 @@ export function createVitestConfig(tree: Tree) { updateNxJson(tree, nxJson); } - -export function addPlugin(tree: Tree) { - const nxJson = readNxJson(tree); - nxJson.plugins ??= []; - - for (const plugin of nxJson.plugins) { - if ( - typeof plugin === 'string' - ? plugin === '@nx/vite/plugin' - : plugin.plugin === '@nx/vite/plugin' - ) { - return; - } - } - - nxJson.plugins.push({ - plugin: '@nx/vite/plugin', - options: { - buildTargetName: 'build', - previewTargetName: 'preview', - testTargetName: 'test', - serveTargetName: 'serve', - serveStaticTargetName: 'serve-static', - }, - }); - updateNxJson(tree, nxJson); -} diff --git a/packages/vite/src/generators/init/schema.d.ts b/packages/vite/src/generators/init/schema.d.ts index 302469d4fc..970386bd65 100644 --- a/packages/vite/src/generators/init/schema.d.ts +++ b/packages/vite/src/generators/init/schema.d.ts @@ -4,4 +4,5 @@ export interface InitGeneratorSchema { keepExistingVersions?: boolean; updatePackageScripts?: boolean; addPlugin?: boolean; + vitestOnly?: boolean; } diff --git a/packages/vite/src/generators/vitest/vitest.spec.ts b/packages/vite/src/generators/vitest/vitest.spec.ts index 6320e7e6bb..01e0ada232 100644 --- a/packages/vite/src/generators/vitest/vitest.spec.ts +++ b/packages/vite/src/generators/vitest/vitest.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Tree } from '@nx/devkit'; diff --git a/packages/vue/src/generators/init/init.spec.ts b/packages/vue/src/generators/init/init.spec.ts index 7f612c4d61..16c773a17c 100644 --- a/packages/vue/src/generators/init/init.spec.ts +++ b/packages/vue/src/generators/init/init.spec.ts @@ -1,11 +1,23 @@ -import { readJson, Tree } from '@nx/devkit'; +import { ProjectGraph, readJson, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { vueInitGenerator } from './init'; +let projectGraph: ProjectGraph; +jest.mock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockImplementation(async () => { + return projectGraph; + }), +})); + describe('init', () => { let tree: Tree; beforeEach(() => { + projectGraph = { + nodes: {}, + dependencies: {}, + }; tree = createTreeWithEmptyWorkspace(); }); diff --git a/packages/web/src/generators/application/application.legacy.spec.ts b/packages/web/src/generators/application/application.legacy.spec.ts index 0af23aa933..05fc18a328 100644 --- a/packages/web/src/generators/application/application.legacy.spec.ts +++ b/packages/web/src/generators/application/application.legacy.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { getProjects, readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/web/src/generators/application/application.spec.ts b/packages/web/src/generators/application/application.spec.ts index 08d171ca72..260a068411 100644 --- a/packages/web/src/generators/application/application.spec.ts +++ b/packages/web/src/generators/application/application.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { readProjectConfiguration, Tree } from '@nx/devkit'; import { getProjects, readJson } from '@nx/devkit'; diff --git a/packages/webpack/src/generators/configuration/configuration.spec.ts b/packages/webpack/src/generators/configuration/configuration.spec.ts index 32ead187f0..ff7e9a2ade 100644 --- a/packages/webpack/src/generators/configuration/configuration.spec.ts +++ b/packages/webpack/src/generators/configuration/configuration.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { addProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/webpack/src/generators/init/init.spec.ts b/packages/webpack/src/generators/init/init.spec.ts index 114f329136..5b76da0a74 100644 --- a/packages/webpack/src/generators/init/init.spec.ts +++ b/packages/webpack/src/generators/init/init.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/webpack/src/generators/init/init.ts b/packages/webpack/src/generators/init/init.ts index 361fe52f39..b991bf0efe 100644 --- a/packages/webpack/src/generators/init/init.ts +++ b/packages/webpack/src/generators/init/init.ts @@ -1,13 +1,13 @@ import { addDependenciesToPackageJson, + createProjectGraphAsync, formatFiles, GeneratorCallback, readNxJson, Tree, - updateNxJson, } from '@nx/devkit'; -import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts'; -import { createNodes, WebpackPluginOptions } from '../../plugins/plugin'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; +import { createNodes } from '../../plugins/plugin'; import { nxVersion, webpackCliVersion } from '../../utils/versions'; import { Schema } from './schema'; @@ -23,7 +23,36 @@ export async function webpackInitGeneratorInternal(tree: Tree, schema: Schema) { schema.addPlugin ??= addPluginDefault; if (schema.addPlugin) { - addPlugin(tree); + await addPlugin( + tree, + await createProjectGraphAsync(), + '@nx/webpack/plugin', + createNodes, + { + buildTargetName: [ + 'build', + 'webpack:build', + 'build:webpack', + 'webpack-build', + 'build-webpack', + ], + serveTargetName: [ + 'serve', + 'webpack:serve', + 'serve:webpack', + 'webpack-serve', + 'serve-webpack', + ], + previewTargetName: [ + 'preview', + 'webpack:preview', + 'preview:webpack', + 'webpack-preview', + 'preview-webpack', + ], + }, + schema.updatePackageScripts + ); } let installTask: GeneratorCallback = () => {}; @@ -46,10 +75,6 @@ export async function webpackInitGeneratorInternal(tree: Tree, schema: Schema) { ); } - if (schema.updatePackageScripts) { - await updatePackageScripts(tree, createNodes); - } - if (!schema.skipFormat) { await formatFiles(tree); } @@ -57,29 +82,4 @@ export async function webpackInitGeneratorInternal(tree: Tree, schema: Schema) { return installTask; } -function addPlugin(tree: Tree) { - const nxJson = readNxJson(tree); - nxJson.plugins ??= []; - - for (const plugin of nxJson.plugins) { - if ( - typeof plugin === 'string' - ? plugin === '@nx/webpack/plugin' - : plugin.plugin === '@nx/webpack/plugin' - ) { - return; - } - } - - nxJson.plugins.push({ - plugin: '@nx/webpack/plugin', - options: { - buildTargetName: 'build', - serveTargetName: 'serve', - previewTargetName: 'preview', - } as WebpackPluginOptions, - }); - updateNxJson(tree, nxJson); -} - export default webpackInitGenerator; diff --git a/packages/workspace/src/generators/convert-to-monorepo/convert-to-monorepo.spec.ts b/packages/workspace/src/generators/convert-to-monorepo/convert-to-monorepo.spec.ts index 0f1f5107a1..86468fd95c 100644 --- a/packages/workspace/src/generators/convert-to-monorepo/convert-to-monorepo.spec.ts +++ b/packages/workspace/src/generators/convert-to-monorepo/convert-to-monorepo.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { monorepoGenerator } from './convert-to-monorepo'; diff --git a/packages/workspace/src/generators/move/lib/check-destination.spec.ts b/packages/workspace/src/generators/move/lib/check-destination.spec.ts index 83fc4c4635..2eb9e7991f 100644 --- a/packages/workspace/src/generators/move/lib/check-destination.spec.ts +++ b/packages/workspace/src/generators/move/lib/check-destination.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { ProjectConfiguration, readProjectConfiguration, diff --git a/packages/workspace/src/generators/move/lib/move-project-files.spec.ts b/packages/workspace/src/generators/move/lib/move-project-files.spec.ts index cd5eaeafe4..b07564401c 100644 --- a/packages/workspace/src/generators/move/lib/move-project-files.spec.ts +++ b/packages/workspace/src/generators/move/lib/move-project-files.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { ProjectConfiguration, readProjectConfiguration, diff --git a/packages/workspace/src/generators/move/lib/update-cypress-config.spec.ts b/packages/workspace/src/generators/move/lib/update-cypress-config.spec.ts index b11a2f7a19..ddeda61777 100644 --- a/packages/workspace/src/generators/move/lib/update-cypress-config.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-cypress-config.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { ProjectConfiguration, readJson, diff --git a/packages/workspace/src/generators/move/lib/update-eslint-config.spec.ts b/packages/workspace/src/generators/move/lib/update-eslint-config.spec.ts index 62cb232e94..52da633bdd 100644 --- a/packages/workspace/src/generators/move/lib/update-eslint-config.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-eslint-config.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { joinPathFragments, offsetFromRoot, diff --git a/packages/workspace/src/generators/move/lib/update-imports.spec.ts b/packages/workspace/src/generators/move/lib/update-imports.spec.ts index ef5291c5ef..49139aa146 100644 --- a/packages/workspace/src/generators/move/lib/update-imports.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-imports.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, readProjectConfiguration, diff --git a/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts b/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts index ea04c6875e..6a256a6218 100644 --- a/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { NormalizedSchema } from '../schema'; diff --git a/packages/workspace/src/generators/move/lib/update-package-json.spec.ts b/packages/workspace/src/generators/move/lib/update-package-json.spec.ts index 633a4ad656..7792ed41b1 100644 --- a/packages/workspace/src/generators/move/lib/update-package-json.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-package-json.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, Tree, writeJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { NormalizedSchema } from '../schema'; diff --git a/packages/workspace/src/generators/move/lib/update-project-root-files.spec.ts b/packages/workspace/src/generators/move/lib/update-project-root-files.spec.ts index 92a14d78b6..5faaf8293c 100644 --- a/packages/workspace/src/generators/move/lib/update-project-root-files.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-project-root-files.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { NormalizedSchema } from '../schema'; diff --git a/packages/workspace/src/generators/move/lib/update-readme.spec.ts b/packages/workspace/src/generators/move/lib/update-readme.spec.ts index 7ed7e9051f..2860373686 100644 --- a/packages/workspace/src/generators/move/lib/update-readme.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-readme.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { join } from 'path'; diff --git a/packages/workspace/src/generators/move/lib/update-storybook-config.spec.ts b/packages/workspace/src/generators/move/lib/update-storybook-config.spec.ts index 1f528670bd..1f295d7ff6 100644 --- a/packages/workspace/src/generators/move/lib/update-storybook-config.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-storybook-config.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { NormalizedSchema } from '../schema'; diff --git a/packages/workspace/src/generators/move/move.spec.ts b/packages/workspace/src/generators/move/move.spec.ts index ef0a907463..5e7c07ebd4 100644 --- a/packages/workspace/src/generators/move/move.spec.ts +++ b/packages/workspace/src/generators/move/move.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readJson, readProjectConfiguration, diff --git a/packages/workspace/src/generators/preset/preset.spec.ts b/packages/workspace/src/generators/preset/preset.spec.ts index 6696c8d199..b9f7e2dd8b 100644 --- a/packages/workspace/src/generators/preset/preset.spec.ts +++ b/packages/workspace/src/generators/preset/preset.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { presetGenerator } from './preset'; diff --git a/packages/workspace/src/generators/remove/lib/remove-project.spec.ts b/packages/workspace/src/generators/remove/lib/remove-project.spec.ts index a4ecbe2042..349040d637 100644 --- a/packages/workspace/src/generators/remove/lib/remove-project.spec.ts +++ b/packages/workspace/src/generators/remove/lib/remove-project.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Schema } from '../schema'; diff --git a/packages/workspace/src/generators/remove/lib/update-jest-config.spec.ts b/packages/workspace/src/generators/remove/lib/update-jest-config.spec.ts index 24a658fbaa..bc01857768 100644 --- a/packages/workspace/src/generators/remove/lib/update-jest-config.spec.ts +++ b/packages/workspace/src/generators/remove/lib/update-jest-config.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { readProjectConfiguration, Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/workspace/src/generators/run-commands/run-commands.spec.ts b/packages/workspace/src/generators/run-commands/run-commands.spec.ts index 845eb5df9a..55d2879898 100644 --- a/packages/workspace/src/generators/run-commands/run-commands.spec.ts +++ b/packages/workspace/src/generators/run-commands/run-commands.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import runCommands from './run-commands'; import { readProjectConfiguration } from 'nx/src/generators/utils/project-configuration'; diff --git a/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.spec.ts b/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.spec.ts index 257557f924..1685f65510 100644 --- a/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.spec.ts +++ b/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Tree,