Compare commits

...

29 Commits

Author SHA1 Message Date
Jason Jean 3c98ab477e chore(repo): increase the resource class for nx workflow agents (#20287)
(cherry picked from commit a5c50f2be6)
2023-11-17 17:35:57 -05:00
FrozenPandaz dc645caa96 chore(repo): turn daemon off in ci
(cherry picked from commit e8780f30fe)
2023-11-17 17:35:50 -05:00
Rares Matei 69d7d019d9 chore(repo): use new workflows launch templates (#20213)
Co-authored-by: Brian Bailey <126210301+brian-nrwl@users.noreply.github.com>
(cherry picked from commit 06f887c150)
2023-11-17 17:35:43 -05:00
Craigory Coppola 17ab5324cc fix(misc): @nx/web:file-server should not error on destructuring null (#20318)
(cherry picked from commit 2a5d541243)
2023-11-17 17:12:15 -05:00
Leosvel Pérez Espinosa 6c8b019814 fix(angular): fix builder/executor resolution in ngcli-adapter (#20307)
(cherry picked from commit 4dfc18fe6e)
2023-11-17 15:40:32 -05:00
Leosvel Pérez Espinosa dc926bd3ed fix(misc): fix project-name-and-root-utils type imports in schemas (#20305)
(cherry picked from commit e4f6be2d4f)
2023-11-17 15:40:29 -05:00
Leosvel Pérez Espinosa 09552c8ad4 fix(angular): generate component with as-provided format when generating a library (#20306)
(cherry picked from commit 479777b6c4)
2023-11-17 15:40:23 -05:00
Craigory Coppola 3b2b940584 fix(react): types should be in compilerOptions of tsconfig.spec.json (#20290)
(cherry picked from commit 4bd4ec29bf)
2023-11-17 15:40:13 -05:00
Leosvel Pérez Espinosa 525e0ca128 fix(angular): display template error when cache is disabled in ng-packagr executors (#20277)
(cherry picked from commit f79fe20520)
2023-11-17 15:39:58 -05:00
Philipp Kirchniawy 99c2db954e fix(core): pass workingDirectory to executed schematic, if found in schema (#19371)
Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com>
(cherry picked from commit 4a5659ef7f)
2023-11-17 15:39:36 -05:00
Nicholas Cunningham 57d751c213 fix(nextjs): read nextConfig path correctly when provided as an option. (#20241)
(cherry picked from commit b1bb89256c)
2023-11-17 15:39:28 -05:00
Denis Frenademetz 8bf3199fa3 fix(angular): use esm2022 for secondary entrypoints in ng-packagr-lite executor (#20130)
Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com>
(cherry picked from commit 96043fbcc9)
2023-11-14 12:51:48 -05:00
Colum Ferry 03b5f355fc fix(module-federation): add tsconfig file for linting with TS webpack #20150 (#20210)
(cherry picked from commit c7c845dbc4)
2023-11-13 14:50:47 -05:00
Leosvel Pérez Espinosa a3d46ecd11 fix(angular): improve express server setup for static files in mf remote apps (#20218)
(cherry picked from commit 58575616c0)
2023-11-13 14:50:25 -05:00
Leosvel Pérez Espinosa 9e6505faf3 fix(angular): set the correct target version for package update (#20219)
(cherry picked from commit b3208edc05)
2023-11-13 14:50:18 -05:00
Katerina Skroumpelou 6da364c6eb fix(vite): vitest versions alignment (#20215)
(cherry picked from commit 34963809dc)
2023-11-13 14:49:44 -05:00
Leosvel Pérez Espinosa cdc3a42d90 fix(angular): fix misc issues with migration replacing the nguniversal usages (#20209)
(cherry picked from commit b4fbdf12eb)
2023-11-13 14:49:30 -05:00
Leosvel Pérez Espinosa ac14278be3 feat(angular): update component generator to use a single string styles or styleUrl property (#20146)
(cherry picked from commit 2883fa3a6f)
2023-11-13 14:49:27 -05:00
Kræn Hansen f4c1950e6f fix(js): filtering of local dependencies when running nx release version (#19995)
(cherry picked from commit b90e3007c4)
2023-11-13 14:49:12 -05:00
Leosvel Pérez Espinosa 6ee8bcccee fix(angular): update jest-preset-angular fixing an issue with component single string styles and styleUrl props (#20205)
(cherry picked from commit 23565f1391)
2023-11-13 14:49:10 -05:00
Craigory Coppola b8fe86dfa3 fix(testing): target defaults migration should not throw if workspace contains inferred projects (#20189)
(cherry picked from commit fec681be13)
2023-11-13 14:48:56 -05:00
Jack Hsu 444ae346bd fix(webpack): maintain entry arrays that are accessed from within composePlugins() (#20187)
(cherry picked from commit 000c98cd00)
2023-11-10 15:29:00 -05:00
MaxKless 2c2317a85e cleanup(storybook): rename name to project to be consistent with other generators (#20181)
(cherry picked from commit e8e8f94f7a)
2023-11-10 15:28:53 -05:00
Katerina Skroumpelou ffd4900d11 fix(vite): vitest should use v8 as default (#20156)
(cherry picked from commit 34552539fe)
2023-11-10 15:28:42 -05:00
Austin Fahsl 5f694de454 fix(release): catch 403 when publishing an existing version (#20155)
(cherry picked from commit 0af7ad483c)
2023-11-10 15:28:39 -05:00
Jason Jean 2bc73ccf8c fix(core): drop file lock after its used (#20165)
(cherry picked from commit c04fa3ad5f)
2023-11-10 15:28:35 -05:00
Craigory Coppola 7975e490d4 fix(devkit): make parseTargetString more tolerant to bad graph shapes (#20170)
(cherry picked from commit 1bd95104cc)
2023-11-10 15:28:25 -05:00
Jason Jean 1d94c375a5 cleanup(core): create root map in js and send to rust instead of full… (#20166)
(cherry picked from commit 7a62353454)
2023-11-10 15:28:19 -05:00
Jason Jean 55b804082e chore(repo): update nx to 17.1.1 (#20160)
(cherry picked from commit 628937316e)
2023-11-10 15:28:13 -05:00
120 changed files with 1127 additions and 858 deletions
+1 -1
View File
@@ -88,7 +88,7 @@ jobs:
NX_CI_EXECUTION_ENV: 'linux'
steps:
- checkout
- run: npx nx-cloud@next start-ci-run --stop-agents-after="e2e"
- run: npx nx-cloud@next start-ci-run --distributes-on="8 linux-medium" --stop-agents-after="e2e"
- run:
command: |
sudo apt-get update
+48 -52
View File
@@ -1,59 +1,55 @@
parallelism: 8
env:
CI: 'true'
GIT_AUTHOR_EMAIL: test@test.com
GIT_AUTHOR_NAME: Test
GIT_COMMITTER_EMAIL: test@test.com
GIT_COMMITTER_NAME: Test
NX_E2E_CI_CACHE_KEY: e2e-circleci-linux
NX_VERBOSE_LOGGING: 'false'
NX_DAEMON: 'true'
NX_PERF_LOGGING: 'false'
NX_NATIVE_LOGGING: 'false'
SELECTED_PM: 'pnpm'
NX_E2E_RUN_E2E: 'true'
NPM_CONFIG_PREFIX: '/home/workflows/.npm-global'
NX_CLOUD_ACCESS_TOKEN: '{{secrets.NX_CLOUD_ACCESS_TOKEN}}'
on:
- events:
- CREATE_RUN_GROUP
steps:
- name: Checkout
uses: 'nrwl/nx-cloud-workflows/v1.1/workflow-steps/checkout/main.yaml'
- name: Cache restore
uses: 'nrwl/nx-cloud-workflows/v1.1/workflow-steps/cache/main.yaml'
launch-templates:
linux-medium:
resource-class: 'docker_linux_amd64/medium+'
env:
KEY: 'pnpm-lock.yaml'
PATHS: |
node_modules
~/.cache/Cypress
~/.pnpm-store
BASE_BRANCH: 'master'
CI: 'true'
GIT_AUTHOR_EMAIL: test@test.com
GIT_AUTHOR_NAME: Test
GIT_COMMITTER_EMAIL: test@test.com
GIT_COMMITTER_NAME: Test
NX_E2E_CI_CACHE_KEY: e2e-circleci-linux
NX_VERBOSE_LOGGING: 'false'
NX_PERF_LOGGING: 'false'
NX_NATIVE_LOGGING: 'false'
SELECTED_PM: 'pnpm'
NX_E2E_RUN_E2E: 'true'
NPM_CONFIG_PREFIX: '/home/workflows/.npm-global'
NX_CLOUD_ACCESS_TOKEN: '{{secrets.NX_CLOUD_ACCESS_TOKEN}}'
init-steps:
- name: Checkout
uses: 'nrwl/nx-cloud-workflows/v1.1/workflow-steps/checkout/main.yaml'
- name: Install Pnpm
script: |
npm install -g @pnpm/exe@8.7.4
- name: Cache restore
uses: 'nrwl/nx-cloud-workflows/v1.1/workflow-steps/cache/main.yaml'
env:
KEY: 'pnpm-lock.yaml'
PATHS: |
node_modules
~/.cache/Cypress
~/.pnpm-store
BASE_BRANCH: 'master'
- name: Pnpm Install
script: |
pnpm install --frozen-lockfile
- name: Install Pnpm
script: |
npm install -g @pnpm/exe@8.7.4
- name: Install Cypress
script: pnpm exec cypress install
- name: Pnpm Install
script: |
pnpm install --frozen-lockfile
- name: Install Rust
script: |
curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh -s -- -y
source "$HOME/.cargo/env"
rustup toolchain install 1.70.0
- name: Install Cypress
script: pnpm exec cypress install
- name: Configure git metadata (needed for lerna smoke tests)
script: |
git config --global user.email test@test.com
git config --global user.name "Test Test"
- name: Install Rust
script: |
curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh -s -- -y
source "$HOME/.cargo/env"
rustup toolchain install 1.70.0
- name: Run Agent
script: |
source "$HOME/.cargo/env"
npx nx-cloud start-agent
- name: Configure git metadata (needed for lerna smoke tests)
script: |
git config --global user.email test@test.com
git config --global user.name "Test Test"
- name: Load Cargo Env
script: echo "PATH=$HOME/.cargo/bin:$PATH" >> $NX_CLOUD_ENV
@@ -78,6 +78,11 @@
"type": "boolean",
"description": "Whether the module federation configuration and webpack configuration files should use TS.",
"default": true
},
"setParserOptionsProject": {
"type": "boolean",
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
"default": false
}
},
"required": ["appName", "mfType"],
@@ -9,9 +9,9 @@
"cli": "nx",
"type": "object",
"properties": {
"name": {
"project": {
"type": "string",
"aliases": ["project", "projectName"],
"aliases": ["name", "projectName"],
"description": "Project for which to generate Storybook configuration.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "For which project do you want to generate Storybook configuration?",
@@ -90,7 +90,7 @@
}
},
"additionalProperties": false,
"required": ["name"],
"required": ["project"],
"examplesFile": "This generator will set up Storybook for your **Angular** project. By default, starting Nx 16, Storybook v7 is used.\n\n```bash\nnx g @nx/angular:storybook-configuration project-name\n```\n\nYou can read more about how this generator works, in the [Storybook for Angular overview page](/recipes/storybook/overview-angular#generate-storybook-configuration-for-an-angular-project).\n\nWhen running this generator, you will be prompted to provide the following:\n\n- The `name` of the project you want to generate the configuration for.\n- Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/angular/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/packages/storybook/documents/interaction-tests).\n- Whether you want to `generateStories` for the components in your project. If you choose `yes`, a `.stories.ts` file will be generated next to each of your components in your project.\n\nYou must provide a `name` for the generator to work.\n\nBy default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended.\n\nThere are a number of other options available. Let's take a look at some examples.\n\n## Examples\n\n### Generate Storybook configuration\n\n```bash\nnx g @nx/angular:storybook-configuration ui\n```\n\nThis will generate Storybook configuration for the `ui` project using TypeScript for the Storybook configuration files (the files inside the `.storybook` directory, eg. `.storybook/main.ts`).\n\n### Ignore certain paths when generating stories\n\n```bash\nnx g @nx/angular:storybook-configuration ui --generateStories=true --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts\n```\n\nThis will generate a Storybook configuration for the `ui` project and generate stories for all components in the `libs/ui/src/lib` directory, except for the ones in the `libs/ui/src/not-stories` directory, and the ones in the `apps/my-app` directory that end with `.something.ts`, and also for components that their file name is of the pattern `*.other.*`.\n\nThis is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component.\n\nBy default, Nx will ignore the following paths:\n\n```text\n*.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx\n```\n\nbut you can change this behaviour easily, as explained above.\n\n### Generate Storybook configuration using JavaScript\n\n```bash\nnx g @nx/angular:storybook-configuration ui --tsConfiguration=false\n```\n\nBy default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`).\n",
"presets": []
},
@@ -9,9 +9,9 @@
"description": "Set up Storybook for a React app or library.",
"type": "object",
"properties": {
"name": {
"project": {
"type": "string",
"aliases": ["project", "projectName"],
"aliases": ["name", "projectName"],
"description": "Project for which to generate Storybook configuration.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "For which project do you want to generate Storybook configuration?",
@@ -87,7 +87,7 @@
]
}
},
"required": ["name"],
"required": ["project"],
"examplesFile": "This generator will set up Storybook for your **React** project. You can also use this generator to generate Storybook configuration for your **Next.js** project. By default, starting Nx 16, Storybook v7 is used.\n\n```bash\nnx g @nx/react:storybook-configuration project-name\n```\n\nYou can read more about how this generator works, in the [Storybook for React overview page](/recipes/storybook/overview-react#generate-storybook-configuration-for-an-react-project).\n\nWhen running this generator, you will be prompted to provide the following:\n\n- The `name` of the project you want to generate the configuration for.\n- Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/react/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/react/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/packages/storybook/documents/interaction-tests)..\n- Whether you want to `generateStories` for the components in your project. If you choose `yes`, a `.stories.ts` file will be generated next to each of your components in your project.\n\nYou must provide a `name` for the generator to work.\n\nBy default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/react/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended.\n\nThere are a number of other options available. Let's take a look at some examples.\n\n## Examples\n\n### Generate Storybook configuration\n\n```bash\nnx g @nx/react:storybook-configuration ui\n```\n\nThis will generate Storybook configuration for the `ui` project using TypeScript for the Storybook configuration files (the files inside the `.storybook` directory, eg. `.storybook/main.ts`).\n\n### Ignore certain paths when generating stories\n\n```bash\nnx g @nx/react:storybook-configuration ui --generateStories=true --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts\n```\n\nThis will generate a Storybook configuration for the `ui` project and generate stories for all components in the `libs/ui/src/lib` directory, except for the ones in the `libs/ui/src/not-stories` directory, and the ones in the `apps/my-app` directory that end with `.something.ts`, and also for components that their file name is of the pattern `*.other.*`.\n\nThis is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component.\n\nBy default, Nx will ignore the following paths:\n\n```text\n*.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx\n```\n\nbut you can change this behaviour easily, as explained above.\n\n### Generate stories using JavaScript instead of TypeScript\n\n```bash\nnx g @nx/react:storybook-configuration ui --generateStories=true --js=true\n```\n\nThis will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components.\n\n### Generate Storybook configuration using JavaScript\n\n```bash\nnx g @nx/react:storybook-configuration ui --tsConfiguration=false\n```\n\nBy default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`).\n",
"presets": []
},
@@ -9,9 +9,9 @@
"description": "Add Storybook configuration to a UI library or an application.",
"type": "object",
"properties": {
"name": {
"project": {
"type": "string",
"aliases": ["project", "projectName"],
"aliases": ["name", "projectName"],
"description": "Project for which to generate Storybook configuration.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "For which project do you want to generate Storybook configuration?",
@@ -103,7 +103,7 @@
"x-priority": "internal"
}
},
"required": ["name", "uiFramework"],
"required": ["project", "uiFramework"],
"examplesFile": "---\ntitle: Storybook configuration generator examples\ndescription: This page contains examples for the @nx/storybook:configuration generator.\n---\n\nThis is a framework-agnostic generator for setting up Storybook configuration for a project.\n\n```bash\nnx g @nx/storybook:configuration\n```\n\n{% callout type=\"info\" title=\"Nx uses Storybook 7\" %}\nNx does not support Storybook v6 any more. So, Nx will configure your project to use Storybook v7. If you are not on Storybook 7 yet, please migrate. Please follow our [Storybook 7 migration generator](/packages/storybook/generators/migrate-7) guide.\n{% /callout %}\n\nIf you are using Angular, React, Next.js, Vue or React Native in your project, it's best to use the framework specific Storybook configuration generator:\n\n- [React Storybook Configuration Generator](/nx-api/react/generators/storybook-configuration) (React and Next.js projects)\n\n- [Angular Storybook Configuration Generator](/nx-api/angular/generators/storybook-configuration)\n\n- [React Native Storybook Configuration Generator](/nx-api/react-native/generators/storybook-configuration)\n\n- [Vue Storybook Configuration Generator](/nx-api/vue/generators/storybook-configuration)\n\nIf you are not using one of the framework-specific generators mentioned above, when running this generator you will be prompted to provide the following:\n\n- The `name` of the project you want to generate the configuration for.\n- The `uiFramework` you want to use. Supported values are:\n - `@storybook/angular`\n - `@storybook/html-webpack5`\n - `@storybook/nextjs`\n - `@storybook/preact-webpack5`\n - `@storybook/react-webpack5`\n - `@storybook/react-vite`\n - `@storybook/server-webpack5`\n - `@storybook/svelte-webpack5`\n - `@storybook/svelte-vite`\n - `@storybook/sveltekit`\n - `@storybook/vue-webpack5`\n - `@storybook/vue-vite`\n - `@storybook/vue3-webpack5`\n - `@storybook/vue3-vite`\n - `@storybook/web-components-webpack5`\n - `@storybook/web-components-vite`\n- Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/angular/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/packages/storybook/documents/interaction-tests).\n\nYou must provide a `name` and a `uiFramework` for the generator to work.\n\nYou can read more about how this generator works, in the [Storybook package overview page](/packages/storybook#generating-storybook-configuration).\n\n## Examples\n\n### Generate Storybook configuration using JavaScript\n\n```bash\nnx g @nx/storybook:configuration ui --uiFramework=@storybook/web-components-vite --tsConfiguration=false\n```\n\nBy default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`).\n",
"presets": []
},
@@ -32,7 +32,7 @@
},
"coverageProvider": {
"type": "string",
"enum": ["v8", "c8", "istanbul"],
"enum": ["v8", "istanbul", "custom"],
"default": "v8",
"description": "Coverage provider to use."
},
@@ -9,9 +9,9 @@
"description": "Set up Storybook for a Vue project.",
"type": "object",
"properties": {
"name": {
"project": {
"type": "string",
"aliases": ["project", "projectName"],
"aliases": ["name", "projectName"],
"description": "Project for which to generate Storybook configuration.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "For which project do you want to generate Storybook configuration?",
@@ -72,7 +72,7 @@
]
}
},
"required": ["name"],
"required": ["project"],
"examplesFile": "---\ntitle: Storybook configuration generator examples\ndescription: This page contains examples for the @nx/vue:storybook-configuration generator.\n---\n\nThis generator will set up Storybook for your **Vue** project. You can also use this generator to generate Storybook configuration for your **Nuxt** project.\n\n```bash\nnx g @nx/vue:storybook-configuration project-name\n```\n\nor\n\n```bash\nnx g @nx/nuxt:storybook-configuration project-name\n```\n\nYou can read more about how this generator works, in the [Storybook for Vue overview page](/recipes/storybook/overview-vue#generate-storybook-configuration-for-a-vue-project).\n\nWhen running this generator, you will be prompted to provide the following:\n\n- The `name` of the project you want to generate the configuration for.\n- Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/vue/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/vue/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/packages/storybook/documents/interaction-tests)..\n- Whether you want to `generateStories` for the components in your project. If you choose `yes`, a `.stories.ts` file will be generated next to each of your components in your project.\n\nYou must provide a `name` for the generator to work.\n\nBy default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/vue/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended.\n\nThere are a number of other options available. Let's take a look at some examples.\n\n## Examples\n\n### Generate Storybook configuration\n\n```bash\nnx g @nx/vue:storybook-configuration ui\n```\n\nThis will generate Storybook configuration for the `ui` project using TypeScript for the Storybook configuration files (the files inside the `.storybook` directory, eg. `.storybook/main.ts`).\n\n### Ignore certain paths when generating stories\n\n```bash\nnx g @nx/vue:storybook-configuration ui --generateStories=true --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts\n```\n\nThis will generate a Storybook configuration for the `ui` project and generate stories for all components in the `libs/ui/src/lib` directory, except for the ones in the `libs/ui/src/not-stories` directory, and the ones in the `apps/my-app` directory that end with `.something.ts`, and also for components that their file name is of the pattern `*.other.*`.\n\nThis is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component.\n\nBy default, Nx will ignore the following paths:\n\n```text\n*.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx\n```\n\nbut you can change this behaviour easily, as explained above.\n\n### Generate stories using JavaScript instead of TypeScript\n\n```bash\nnx g @nx/vue:storybook-configuration ui --generateStories=true --js=true\n```\n\nThis will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components.\n\n### Generate Storybook configuration using JavaScript\n\n```bash\nnx g @nx/vue:storybook-configuration ui --tsConfiguration=false\n```\n\nBy default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`).\n",
"presets": []
},
+1 -1
View File
@@ -379,7 +379,7 @@ export default App;
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
coverage: {
provider: "c8",
provider: "v8",
enabled: true,
lines: 100,
statements: 100,
+27
View File
@@ -135,4 +135,31 @@ module.exports = composePlugins(withNx(), (config) => {
let output = runCommand(`node dist/${appName}/main.js`);
expect(output).toMatch(/Hello/);
}, 500_000);
// Issue: https://github.com/nrwl/nx/issues/20179
it('should allow main/styles entries to be spread within composePlugins() function (#20179)', () => {
const appName = uniq('app');
runCLI(`generate @nx/web:app ${appName} --bundler webpack`);
updateFile(`apps/${appName}/src/main.ts`, `console.log('Hello');\n`);
updateFile(
`apps/${appName}/webpack.config.js`,
`
const { composePlugins, withNx, withWeb } = require('@nx/webpack');
module.exports = composePlugins(withNx(), withWeb(), (config) => {
return {
...config,
entry: {
main: [...config.entry.main],
styles: [...config.entry.styles],
}
};
});
`
);
expect(() => {
runCLI(`build ${appName} --outputHashing none`);
}).not.toThrow();
});
});
+18 -18
View File
@@ -30,9 +30,9 @@
"@angular-devkit/build-angular": "~17.0.0",
"@angular-devkit/core": "~17.0.0",
"@angular-devkit/schematics": "~17.0.0",
"@angular-eslint/eslint-plugin": "~17.0.0",
"@angular-eslint/eslint-plugin-template": "~17.0.0",
"@angular-eslint/template-parser": "~17.0.0",
"@angular-eslint/eslint-plugin": "17.0.1",
"@angular-eslint/eslint-plugin-template": "17.0.1",
"@angular-eslint/template-parser": "17.0.1",
"@angular/cli": "~17.0.0",
"@angular/common": "~17.0.0",
"@angular/compiler": "~17.0.0",
@@ -63,19 +63,19 @@
"@ngrx/effects": "~16.0.0",
"@ngrx/router-store": "~16.0.0",
"@ngrx/store": "~16.0.0",
"@nx/angular": "17.1.0-beta.4",
"@nx/cypress": "17.1.0-beta.4",
"@nx/devkit": "17.1.0-beta.4",
"@nx/esbuild": "17.1.0-beta.4",
"@nx/eslint-plugin": "17.1.0-beta.4",
"@nx/jest": "17.1.0-beta.4",
"@nx/js": "17.1.0-beta.4",
"@nx/next": "17.1.0-beta.4",
"@nx/playwright": "17.1.0-beta.4",
"@nx/react": "17.1.0-beta.4",
"@nx/storybook": "17.1.0-beta.4",
"@nx/web": "17.1.0-beta.4",
"@nx/webpack": "17.1.0-beta.4",
"@nx/angular": "17.1.1",
"@nx/cypress": "17.1.1",
"@nx/devkit": "17.1.1",
"@nx/esbuild": "17.1.1",
"@nx/eslint-plugin": "17.1.1",
"@nx/jest": "17.1.1",
"@nx/js": "17.1.1",
"@nx/next": "17.1.1",
"@nx/playwright": "17.1.1",
"@nx/react": "17.1.1",
"@nx/storybook": "17.1.1",
"@nx/web": "17.1.1",
"@nx/webpack": "17.1.1",
"@parcel/watcher": "2.0.4",
"@phenomnomnominal/tsquery": "~5.0.1",
"@playwright/test": "^1.36.1",
@@ -227,7 +227,7 @@
"node-fetch": "^2.6.7",
"npm-package-arg": "11.0.1",
"nuxi": "npm:nuxi-nightly@3.9.2-1699007958.251cab5",
"nx": "17.1.0-beta.4",
"nx": "17.1.1",
"octokit": "^2.0.14",
"open": "^8.4.0",
"openai": "~4.3.1",
@@ -293,7 +293,7 @@
"xstate": "4.34.0",
"yargs": "^17.6.2",
"yargs-parser": "21.1.1",
"@nx/eslint": "17.1.0-beta.4"
"@nx/eslint": "17.1.1"
},
"author": "Victor Savkin",
"license": "MIT",
+16
View File
@@ -1489,6 +1489,22 @@
"alwaysAddToPackageJson": false
}
}
},
"17.1.3-jest": {
"version": "17.1.3-beta.0",
"requires": {
"@angular-devkit/build-angular": ">=13.0.0 <18.0.0",
"@angular/compiler-cli": ">=13.0.0 <18.0.0",
"@angular/core": ">=13.0.0 <18.0.0",
"@angular/platform-browser-dynamic": ">=13.0.0 <18.0.0",
"jest": "^29.0.0"
},
"packages": {
"jest-preset-angular": {
"version": "~13.1.4",
"alwaysAddToPackageJson": false
}
}
}
}
}
@@ -135,7 +135,13 @@ export const nxWritePackageTransform = (options: NgPackagrOptions) =>
await writeFile(
path.join(ngEntryPoint.destinationPath, 'package.json'),
JSON.stringify(
{ module: relativeUnixFromDestPath(destinationFiles.fesm2022) },
{
module: relativeUnixFromDestPath(
angularVersion.major < 16
? (destinationFiles as any).esm2020
: destinationFiles.esm2022
),
},
undefined,
2
)
@@ -100,9 +100,10 @@ export async function compileSourceFiles(
);
cache.oldNgtscProgram = angularProgram;
} else {
// When not in watch mode, the startup cost of the incremental analysis can be avoided by
// using an abstract builder that only wraps a TypeScript program.
builder = ts.createAbstractBuilder(typeScriptProgram, tsCompilerHost);
builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
typeScriptProgram,
tsCompilerHost
);
}
// Update semantic diagnostics cache
@@ -113,9 +113,10 @@ export async function compileSourceFiles(
);
cache.oldNgtscProgram = angularProgram;
} else {
// When not in watch mode, the startup cost of the incremental analysis can be avoided by
// using an abstract builder that only wraps a TypeScript program.
builder = ts.createAbstractBuilder(typeScriptProgram, tsCompilerHost);
builder = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
typeScriptProgram,
tsCompilerHost
);
}
// Update semantic diagnostics cache
@@ -23,7 +23,7 @@ exports[`app --minimal should skip "nx-welcome.component.ts" file and references
@Component({
selector: 'proj-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
styleUrl: './app.component.css',
})
export class AppComponent {}
"
@@ -81,7 +81,7 @@ exports[`app --minimal should skip "nx-welcome.component.ts" file and references
@Component({
selector: 'proj-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
styleUrl: './app.component.css',
})
export class AppComponent {}
"
@@ -125,7 +125,7 @@ import { RouterModule } from '@angular/router';
imports: [RouterModule],
selector: 'proj-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
styleUrl: './app.component.css',
})
export class AppComponent {}
"
@@ -169,7 +169,7 @@ exports[`app --minimal should skip "nx-welcome.component.ts" file and references
imports: [],
selector: 'proj-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
styleUrl: './app.component.css',
})
export class AppComponent {}
"
@@ -714,7 +714,7 @@ import { NxWelcomeComponent } from './nx-welcome.component';
imports: [NxWelcomeComponent, RouterModule],
selector: 'proj-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
styleUrl: './app.component.css',
})
export class AppComponent {
title = 'standalone';
@@ -782,7 +782,7 @@ import { NxWelcomeComponent } from './nx-welcome.component';
imports: [NxWelcomeComponent],
selector: 'proj-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
styleUrl: './app.component.css',
})
export class AppComponent {
title = 'standalone';
@@ -3,9 +3,11 @@ import { Component<% if(viewEncapsulation) { %>, ViewEncapsulation<% } %> } from
@Component({
selector: '<%= rootSelector %>',<% if(!inlineTemplate) { %>
templateUrl: './app.component.html',<% } else { %>
template: `<% if(minimal) { %><h1>Welcome <%= appName %></h1><% } else { %><<%= nxWelcomeSelector %>></<%= nxWelcomeSelector %>><% } %> <% if(routing) { %><router-outlet></router-outlet><% } %>`,<% } %><% if(!inlineStyle) { %>
template: `<% if(minimal) { %><h1>Welcome <%= appName %></h1><% } else { %><<%= nxWelcomeSelector %>></<%= nxWelcomeSelector %>><% } %> <% if(routing) { %><router-outlet></router-outlet><% } %>`,<% } %><% if (angularMajorVersion < 17) { if(!inlineStyle) { %>
styleUrls: ['./app.component.<%= style %>'],<% } else { %>
styles: [''],<% } %><% if(viewEncapsulation) { %>
styles: [``],<% } %><% } else { if(!inlineStyle) { %>
styleUrl: './app.component.<%= style %>',<% } else { %>
styles: ``,<% } %><% } %><% if(viewEncapsulation) { %>
encapsulation: ViewEncapsulation.<%= viewEncapsulation %>,<% } %>
})
export class AppComponent {<% if(!minimal) { %>
@@ -7,9 +7,11 @@ import { NxWelcomeComponent } from './nx-welcome.component';<% } %>
imports: [<% if(!minimal) { %>NxWelcomeComponent, <% } %><% if(routing) { %>RouterModule<% } %>],
selector: '<%= rootSelector %>',<% if(!inlineTemplate) { %>
templateUrl: './app.component.html',<% } else { %>
template: `<% if(minimal) { %><h1>Welcome <%= appName %></h1><% } else { %><<%= nxWelcomeSelector %>></<%= nxWelcomeSelector %>><% } %> <% if(routing) { %><router-outlet></router-outlet><% } %>`,<% } %><% if(!inlineStyle) { %>
template: `<% if(minimal) { %><h1>Welcome <%= appName %></h1><% } else { %><<%= nxWelcomeSelector %>></<%= nxWelcomeSelector %>><% } %> <% if(routing) { %><router-outlet></router-outlet><% } %>`,<% } %><% if (angularMajorVersion < 17) { if(!inlineStyle) { %>
styleUrls: ['./app.component.<%= style %>'],<% } else { %>
styles: [''],<% } %><% if(viewEncapsulation) { %>
styles: [``],<% } %><% } else { if(!inlineStyle) { %>
styleUrl: './app.component.<%= style %>',<% } else { %>
styles: ``,<% } %><% } %><% if(viewEncapsulation) { %>
encapsulation: ViewEncapsulation.<%= viewEncapsulation %>,<% } %>
})
export class AppComponent {<% if(!minimal) { %>
@@ -1,4 +1,4 @@
import { ApplicationConfig } from <% if (installedAngularInfo.major >= 16) { %>'@angular/core';<% } else { %>'@angular/platform-browser';<% } %><% if (routing) { %>
import { ApplicationConfig } from <% if (angularMajorVersion >= 16) { %>'@angular/core';<% } else { %>'@angular/platform-browser';<% } %><% if (routing) { %>
import { provideRouter } from '@angular/router';
import { appRoutes } from './app.routes';<% } %>
@@ -1,11 +1,5 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';<% if(installedAngularInfo.major === 14) { %>
import { enableProdMode } from '@angular/core';
import { environment } from './environments/environment';
if(environment.production) {
enableProdMode();
}<% } %>
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
@@ -11,9 +11,9 @@ export async function createFiles(
options: NormalizedSchema,
rootOffset: string
) {
const installedAngularInfo = getInstalledAngularVersionInfo(tree);
const { major: angularMajorVersion } = getInstalledAngularVersionInfo(tree);
const isUsingApplicationBuilder =
installedAngularInfo.major >= 17 && options.bundler === 'esbuild';
angularMajorVersion >= 17 && options.bundler === 'esbuild';
const substitutions = {
rootSelector: `${options.prefix}-root`,
@@ -27,7 +27,7 @@ export async function createFiles(
minimal: options.minimal,
nxWelcomeSelector: `${options.prefix}-nx-welcome`,
rootTsConfig: joinPathFragments(rootOffset, getRootTsConfigFileName(tree)),
installedAngularInfo,
angularMajorVersion,
rootOffset,
isUsingApplicationBuilder,
tpl: '',
@@ -6,7 +6,7 @@ exports[`component Generator --flat should create the component correctly and ex
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -18,7 +18,7 @@ exports[`component Generator --flat should create the component correctly and no
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -42,7 +42,19 @@ exports[`component Generator --path should create the component correctly and ex
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
`;
exports[`component Generator compat should inline styles when --inline-style=true 1`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styles: \`\`,
})
export class ExampleComponent {}
"
@@ -54,7 +66,7 @@ exports[`component Generator secondary entry points should create the component
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -72,7 +84,7 @@ exports[`component Generator should create component files correctly: component
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -121,7 +133,7 @@ exports[`component Generator should create the component correctly and export it
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -142,7 +154,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -154,7 +166,7 @@ exports[`component Generator should create the component correctly and not expor
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -169,7 +181,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -181,7 +193,7 @@ exports[`component Generator should create the component correctly and not expor
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -193,7 +205,7 @@ exports[`component Generator should create the component correctly but not expor
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -205,7 +217,7 @@ exports[`component Generator should inline styles when --inline-style=true 1`] =
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styles: [],
styles: \`\`,
})
export class ExampleComponent {}
"
@@ -217,7 +229,7 @@ exports[`component Generator should inline template when --inline-template=true
@Component({
selector: 'proj-example',
template: \`<p>example works!</p>\`,
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
"
@@ -985,4 +985,41 @@ describe('component Generator', () => {
expect(indexSource).toBe('');
});
});
describe('compat', () => {
it('should inline styles when --inline-style=true', async () => {
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write('libs/lib1/src/index.ts', '');
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
inlineStyle: true,
standalone: false,
});
expect(
tree.read('libs/lib1/src/lib/example/example.component.ts', 'utf-8')
).toMatchSnapshot();
expect(
tree.exists('libs/lib1/src/lib/example/example.component.css')
).toBe(false);
});
});
});
@@ -1,11 +1,7 @@
import type { Tree } from '@nx/devkit';
import {
formatFiles,
generateFiles,
joinPathFragments,
names,
} from '@nx/devkit';
import { formatFiles, generateFiles, joinPathFragments } from '@nx/devkit';
import { addToNgModule } from '../utils';
import { getInstalledAngularVersionInfo } from '../utils/version-utils';
import {
exportComponentInEntryPoint,
findModuleFromOptions,
@@ -26,6 +22,7 @@ export async function componentGeneratorInternal(
) {
const options = await normalizeOptions(tree, rawOptions);
const { major: angularMajorVersion } = getInstalledAngularVersionInfo(tree);
generateFiles(
tree,
joinPathFragments(__dirname, 'files'),
@@ -43,6 +40,7 @@ export async function componentGeneratorInternal(
viewEncapsulation: options.viewEncapsulation,
displayBlock: options.displayBlock,
selector: options.selector,
angularMajorVersion,
tpl: '',
}
);
@@ -6,7 +6,7 @@ import { CommonModule } from '@angular/common';<% } %>
standalone: true,
imports: [CommonModule],<%}%><% if(inlineTemplate) { %>
template: `<p><%= name %> works!</p>`<% } else { %>
templateUrl: './<%= fileName %>.html'<% } if(inlineStyle) { %>,
templateUrl: './<%= fileName %>.html'<% } if (angularMajorVersion < 17) { if(inlineStyle) { %>,
styles: [<% if(displayBlock){ %>
`
:host {
@@ -14,7 +14,13 @@ import { CommonModule } from '@angular/common';<% } %>
}
`<% } %>
]<% } else if (style !== 'none') { %>,
styleUrls: ['./<%= fileName %>.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>,
styleUrls: ['./<%= fileName %>.<%= style %>']<% } %><% } else { if(inlineStyle) { %>,
styles: `<% if(displayBlock){ %>
:host {
display: block;
}
<% } %>`<% } else if (style !== 'none') { %>,
styleUrl: './<%= fileName %>.<%= style %>'<% } %><% } %><% if(!!viewEncapsulation) { %>,
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
})
@@ -916,7 +916,7 @@ import { NxWelcomeComponent } from './nx-welcome.component';
imports: [NxWelcomeComponent, RouterModule],
selector: 'proj-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
styleUrl: './app.component.css',
})
export class AppComponent {
title = 'host';
@@ -74,6 +74,7 @@ export async function hostInternal(tree: Tree, schema: Schema) {
prefix: options.prefix,
typescriptConfiguration,
standalone: options.standalone,
setParserOptionsProject: options.setParserOptionsProject,
});
let installTasks = [appInstallTask];
@@ -14,7 +14,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
templateUrl: './my-lib.component.html',
styleUrls: ['./my-lib.component.css'],
styleUrl: './my-lib.component.css',
})
export class MyLibComponent {}
"
@@ -61,7 +61,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
templateUrl: './my-lib.component.html',
styleUrls: ['./my-lib.component.css'],
styleUrl: './my-lib.component.css',
})
export class MyLibComponent {}
"
@@ -114,7 +114,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
templateUrl: './my-lib.component.html',
styleUrls: ['./my-lib.component.css'],
styleUrl: './my-lib.component.css',
})
export class MyLibComponent {}
"
@@ -163,7 +163,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
template: \`<p>my-lib works!</p>\`,
styles: [],
styles: \`\`,
encapsulation: ViewEncapsulation.ShadowDom,
changeDetection: ChangeDetectionStrategy.OnPush,
})
@@ -185,7 +185,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
template: \`<p>my-lib works!</p>\`,
styles: [],
styles: \`\`,
})
export class MyLibComponent {}
"
@@ -205,7 +205,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
template: \`<p>my-lib works!</p>\`,
styles: [],
styles: \`\`,
})
export class MyLibComponent {}
"
@@ -260,7 +260,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
templateUrl: './my-lib.component.html',
styleUrls: ['./my-lib.component.css'],
styleUrl: './my-lib.component.css',
})
export class MyLibComponent {}
"
@@ -381,7 +381,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
templateUrl: './my-lib.component.html',
styleUrls: ['./my-lib.component.css'],
styleUrl: './my-lib.component.css',
})
export class MyLibComponent {}
"
@@ -426,7 +426,7 @@ import { CommonModule } from '@angular/common';
standalone: true,
imports: [CommonModule],
templateUrl: './my-lib.component.html',
styleUrls: ['./my-lib.component.css'],
styleUrl: './my-lib.component.css',
})
export class MyLibComponent {}
"
@@ -1,8 +1,8 @@
import { Tree } from 'nx/src/generators/tree';
import { NormalizedSchema } from './normalized-schema';
import componentGenerator from '../../component/component';
import { addLoadChildren } from './add-load-children';
import { joinPathFragments, type Tree } from '@nx/devkit';
import { componentGenerator } from '../../component/component';
import { addChildren } from './add-children';
import { addLoadChildren } from './add-load-children';
import type { NormalizedSchema } from './normalized-schema';
export async function addStandaloneComponent(
tree: Tree,
@@ -11,10 +11,15 @@ export async function addStandaloneComponent(
await componentGenerator(tree, {
...componentOptions,
name: componentOptions.name,
directory: joinPathFragments(
libraryOptions.projectRoot,
'src',
'lib',
componentOptions.flat ? '' : componentOptions.name
),
nameAndDirectoryFormat: 'as-provided',
standalone: true,
export: true,
project: libraryOptions.name,
flat: componentOptions.flat,
skipFormat: true,
});
@@ -38,7 +38,7 @@ export function app(): express.Express {
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
// serve static files
server.use('/', express.static(browserBundles, { maxAge: '1y' }));
server.get('*.*', express.static(browserBundles, { maxAge: '1y' }));
server.use('/server', express.static(serverBundles, { maxAge: '1y' }));
// All regular routes use the Universal engine
@@ -151,7 +151,7 @@ export function app(): express.Express {
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.use('/', express.static(browserBundles, { maxAge: '1y' }));
server.get('*.*', express.static(browserBundles, { maxAge: '1y' }));
// Serve static files from /server
server.use('/server', express.static(serverBundles, { maxAge: '1y' }));
@@ -382,7 +382,7 @@ export function app(): express.Express {
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.use('/', express.static(browserBundles, { maxAge: '1y' }));
server.get('*.*', express.static(browserBundles, { maxAge: '1y' }));
// Serve static files from /server
server.use('/server', express.static(serverBundles, { maxAge: '1y' }));
@@ -36,7 +36,7 @@ export function app(): express.Express {
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
// serve static files
server.use('/', express.static(browserBundles, { maxAge: '1y' }));
server.get('*.*', express.static(browserBundles, { maxAge: '1y' }));
server.use('/server', express.static(serverBundles, { maxAge: '1y' }));
// All regular routes use the Universal engine
@@ -28,7 +28,7 @@ export function app(): express.Express {
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.use('/', express.static(browserBundles, { maxAge: '1y' }));
server.get('*.*', express.static(browserBundles, { maxAge: '1y' }));
// Serve static files from /server
server.use('/server', express.static(serverBundles, { maxAge: '1y' }));
@@ -68,6 +68,7 @@ export async function remoteInternal(tree: Tree, schema: Schema) {
standalone: options.standalone,
prefix: options.prefix,
typescriptConfiguration,
setParserOptionsProject: options.setParserOptionsProject,
});
const installSwcHelpersTask = addDependenciesToPackageJson(
@@ -34,7 +34,7 @@ describe('scam-to-standalone', () => {
imports: [CommonModule],
selector: 'proj-bar',
templateUrl: './bar.component.html',
styleUrls: ['./bar.component.css'],
styleUrl: './bar.component.css',
})
export class BarComponent {}
"
@@ -46,7 +46,7 @@ describe('convertComponentToScam', () => {
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
@@ -152,7 +152,7 @@ describe('convertComponentToScam', () => {
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
@@ -262,7 +262,7 @@ describe('convertComponentToScam', () => {
@Component({
selector: 'proj-example',
templateUrl: './example.random.html',
styleUrls: ['./example.random.css'],
styleUrl: './example.random.css',
})
export class ExampleRandom {}
@@ -373,7 +373,7 @@ describe('convertComponentToScam', () => {
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
@@ -431,7 +431,7 @@ describe('convertComponentToScam', () => {
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
@@ -31,7 +31,7 @@ describe('SCAM Generator', () => {
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
@@ -162,7 +162,7 @@ describe('SCAM Generator', () => {
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
@@ -205,7 +205,7 @@ describe('SCAM Generator', () => {
@Component({
selector: 'proj-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css'],
styleUrl: './example.component.css',
})
export class ExampleComponent {}
@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"target": "ES2020"
},
"include": [
"src/main.ts",
<% if(type === "remote") { %> "src/remote-entry/<% if(standalone) { %>entry.routes.ts", <% } else { %> entry.module.ts", <% } } %>
"webpack.config.ts",
"webpack.prod.config.ts"
]
}
@@ -39,4 +39,8 @@ export function generateWebpackConfig(
standalone: options.standalone,
}
);
if (!options.setParserOptionsProject) {
tree.delete(joinPathFragments(appRoot, 'tsconfig.lint.json'));
}
}
+1
View File
@@ -15,6 +15,7 @@ export interface Schema {
standalone?: boolean;
skipE2E?: boolean;
typescriptConfiguration?: boolean;
setParserOptionsProject?: boolean;
}
export interface NormalizedOptions extends Schema {
@@ -78,6 +78,11 @@
"type": "boolean",
"description": "Whether the module federation configuration and webpack configuration files should use TS.",
"default": true
},
"setParserOptionsProject": {
"type": "boolean",
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
"default": false
}
},
"required": ["appName", "mfType"],
@@ -8,15 +8,15 @@ export async function generateStories(
tree: Tree,
options: StorybookConfigurationOptions
) {
const project = readProjectConfiguration(tree, options.name);
const project = readProjectConfiguration(tree, options.project);
const e2eProjectName = getE2eProjectName(
options.name,
options.project,
project.root,
options.cypressDirectory
);
await angularStoriesGenerator(tree, {
name: options.name,
name: options.project,
generateCypressSpecs:
options.configureCypress && options.generateCypressSpecs,
cypressProject: e2eProjectName,
@@ -8,7 +8,7 @@ export async function generateStorybookConfiguration(
): Promise<GeneratorCallback> {
const { configurationGenerator } = ensurePackage('@nx/storybook', nxVersion);
return await configurationGenerator(tree, {
name: options.name,
project: options.project,
uiFramework: '@storybook/angular',
configureCypress: options.configureCypress,
linter: options.linter,
@@ -4,7 +4,7 @@ export interface StorybookConfigurationOptions {
configureStaticServe?: boolean;
generateStories: boolean;
linter: Linter;
name: string;
project: string;
tsConfiguration?: boolean;
skipFormat?: boolean;
ignorePaths?: string[];
@@ -6,9 +6,9 @@
"cli": "nx",
"type": "object",
"properties": {
"name": {
"project": {
"type": "string",
"aliases": ["project", "projectName"],
"aliases": ["name", "projectName"],
"description": "Project for which to generate Storybook configuration.",
"$default": {
"$source": "argv",
@@ -93,6 +93,6 @@
}
},
"additionalProperties": false,
"required": ["name"],
"required": ["project"],
"examplesFile": "../../../docs/storybook-configuration-examples.md"
}
@@ -46,7 +46,7 @@ describe('StorybookConfiguration generator', () => {
it('should only configure storybook', async () => {
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
name: libName,
project: libName,
generateStories: false,
});
@@ -66,7 +66,7 @@ describe('StorybookConfiguration generator', () => {
it('should configure storybook to use webpack 5', async () => {
await storybookConfigurationGenerator(tree, {
name: libName,
project: libName,
generateStories: false,
linter: Linter.None,
});
@@ -78,7 +78,7 @@ describe('StorybookConfiguration generator', () => {
it('should configure storybook with interaction tests and install dependencies', async () => {
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
name: libName,
project: libName,
generateStories: true,
});
@@ -138,7 +138,7 @@ describe('StorybookConfiguration generator', () => {
});
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
name: libName,
project: libName,
generateStories: true,
});
@@ -175,7 +175,7 @@ describe('StorybookConfiguration generator', () => {
});
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
name: libName,
project: libName,
generateStories: true,
});
@@ -60,7 +60,9 @@ describe('replace-nguniversal-engines migration', () => {
};
projectGraph = {
dependencies: {
app1: [{ source: 'app1', target: 'npm:@angular/core', type: 'static' }],
app1: [
{ source: 'app1', target: 'npm:@nguniversal/common', type: 'static' },
],
},
nodes: { app1: { data: project, name: 'app1', type: 'app' } },
};
@@ -214,6 +216,20 @@ if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
expect(tree.exists('src/express.tokens.ts')).toBe(true);
});
it('should import tokens file correctly in nested paths', async () => {
const filePath = 'src/nested/folder/home/home.component.ts';
tree.write(
filePath,
`import { RESPONSE } from '@nguniversal/express-engine/tokens';`
);
await migration(tree);
expect(tree.read(filePath, 'utf-8')).toContain(
`import { RESPONSE } from '../../../express.tokens';`
);
});
it('should not create tokens file when "@nguniversal/express-engine/tokens" is not used', async () => {
await migration(tree);
@@ -224,4 +240,13 @@ if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
);
expect(tree.exists('src/express.tokens.ts')).toBe(false);
});
it('should not process non-TypeScript files', async () => {
const content = `import { ngExpressEngine } from '@nguniversal/express-engine';`;
tree.write('src/foo.txt', content);
await migration(tree);
expect(tree.read('src/foo.txt', 'utf-8')).toBe(content);
});
});
@@ -27,6 +27,10 @@ const UNIVERSAL_PACKAGES = [
**/
const NGUNIVERSAL_PACKAGE_REGEXP =
/@nguniversal\/(common(\/engine)?|express-engine)/g;
const serverExecutors = [
'@angular-devkit/build-angular:server',
'@nx/angular:webpack-server',
];
export default async function (tree: Tree) {
const packageJson = readJson(tree, 'package.json');
@@ -40,7 +44,8 @@ export default async function (tree: Tree) {
}
const projects = await getProjectsFilteredByDependencies(tree, [
'npm:@angular/core',
'npm:@nguniversal/common',
'npm:@nguniversal/express-engine',
]);
for (const { project } of projects) {
if (project.projectType !== 'application') {
@@ -52,7 +57,7 @@ export default async function (tree: Tree) {
string /** Output Path */
>();
for (const target of Object.values(project.targets ?? {})) {
if (target.executor !== '@angular-devkit/build-angular:server') {
if (!serverExecutors.includes(target.executor)) {
continue;
}
@@ -74,33 +79,36 @@ export default async function (tree: Tree) {
const tokensFilePath = `${root}/express.tokens.ts`;
visitNotIgnoredFiles(tree, root, (path) => {
const content = tree.read(path, 'utf8');
let updatedContent = content;
if (!path.endsWith('.ts') || path.endsWith('.d.ts')) {
return;
}
let content = tree.read(path, 'utf8');
if (!content.includes('@nguniversal/')) {
return;
}
// Check if file is importing tokens
if (content.includes('@nguniversal/express-engine/tokens')) {
hasExpressTokens ||= true;
let tokensFileRelativePath: string = relative(
dirname(normalizePath(path)),
normalizePath(tokensFilePath)
let tokensFileRelativePath: string = normalizePath(
relative(dirname(path), tokensFilePath)
);
if (tokensFileRelativePath.charAt(0) !== '.') {
tokensFileRelativePath = './' + tokensFileRelativePath;
}
updatedContent = updatedContent.replaceAll(
content = content.replaceAll(
'@nguniversal/express-engine/tokens',
tokensFileRelativePath.slice(0, -3)
);
}
updatedContent = updatedContent.replaceAll(
NGUNIVERSAL_PACKAGE_REGEXP,
'@angular/ssr'
);
tree.write(path, updatedContent);
content = content.replaceAll(NGUNIVERSAL_PACKAGE_REGEXP, '@angular/ssr');
tree.write(path, content);
});
// Replace server file and add tokens file if needed
+1 -1
View File
@@ -24,7 +24,7 @@ export const postcssUrlVersion = '~10.1.3';
export const autoprefixerVersion = '^10.4.0';
export const tsNodeVersion = '10.9.1';
export const jestPresetAngularVersion = '~13.1.3';
export const jestPresetAngularVersion = '~13.1.4';
export const typesNodeVersion = '16.11.7';
export const jasmineMarblesVersion = '^0.9.2';
@@ -62,6 +62,27 @@ describe('parseTargetString', () => {
target: 'build',
});
});
// When running a converted executor, its possible that the context has a project name that
// isn't present within the project graph. In these cases, this function should still behave predictably.
it('should produce accurate results if the project graph doesnt contain the project', () => {
expect(
parseTargetString('foo:build', { ...mockContext, projectName: 'foo' })
).toEqual({
project: 'foo',
target: 'build',
});
expect(
parseTargetString('foo:build:production', {
...mockContext,
projectName: 'foo',
})
).toEqual({
project: 'foo',
target: 'build',
configuration: 'production',
});
});
});
describe('targetToTargetString', () => {
@@ -66,7 +66,8 @@ export function parseTargetString(
if (
!projectGraph.nodes[maybeProject] &&
projectGraphOrCtx &&
'projectName' in projectGraphOrCtx
'projectName' in projectGraphOrCtx &&
maybeProject !== projectGraphOrCtx.projectName
) {
targetString = `${projectGraphOrCtx.projectName}:${targetString}`;
}
@@ -559,4 +559,46 @@ describe('move-options-to-target-defaults migration', () => {
},
});
});
it("should't error if a project is present in the graph but not using project.json", async () => {
projectGraph.nodes['csproj'] = {
name: 'csproj',
type: 'lib',
data: {
root: 'csproj',
targets: {
build: {
command: 'echo HELLO',
},
},
},
};
addProjectConfiguration(tree, 'proj1', {
root: 'proj1',
targets: {
test: {
executor: '@nx/jest:jest',
options: {
jestConfig: 'jest.config.js',
passWithNoTests: true,
},
configurations: {
ci: {
ci: true,
codeCoverage: true,
},
},
},
},
});
updateNxJson(tree, {
targetDefaults: {
build: {
inputs: ['default', '^production'],
},
},
});
const promise = update(tree);
await expect(promise).resolves.not.toThrow();
});
});
@@ -174,7 +174,7 @@ function isTargetDefaultUsed(
targetName,
targetDefaults,
// It might seem like we should use the graph here too but we don't want to pass an executor which was processed in the graph
projectMap.get(p.name).targets?.[targetName]?.executor
projectMap.get(p.name)?.targets?.[targetName]?.executor
) === targetDefault
) {
return true;
@@ -102,10 +102,17 @@ export default async function runExecutor(
const stdoutData = JSON.parse(err.stdout?.toString() || '{}');
if (
// handle npm conflict error
stdoutData.error?.code === 'EPUBLISHCONFLICT' ||
// handle npm conflict error when the package has a scope
(stdoutData.error?.code === 'E403' &&
stdoutData.error?.body?.error?.includes(
stdoutData.error?.summary?.includes(
'You cannot publish over the previously published versions'
)) ||
// handle verdaccio conflict error
(stdoutData.error?.code === 'E409' &&
stdoutData.error?.summary?.includes(
'this package is already present'
))
) {
console.warn(
@@ -123,6 +130,11 @@ export default async function runExecutor(
if (stdoutData.error.detail) {
console.error(stdoutData.error.detail);
}
if (context.isVerbose) {
console.error('npm publish stdout:');
console.error(JSON.stringify(stdoutData, null, 2));
}
return {
success: false,
};
@@ -128,7 +128,7 @@ export async function libraryGeneratorInternal(
const vitestTask = await vitestGenerator(tree, {
project: options.name,
uiFramework: 'none',
coverageProvider: 'c8',
coverageProvider: 'v8',
skipFormat: true,
testEnvironment: options.testEnvironment,
});
@@ -164,6 +164,68 @@ To fix this you will either need to add a package.json file at that location, or
outputSpy.mockRestore();
});
});
describe('package with mixed "prod" and "dev" dependencies', () => {
beforeEach(() => {
projectGraph = createWorkspaceWithPackageDependencies(tree, {
'my-app': {
projectRoot: 'libs/my-app',
packageName: 'my-app',
version: '0.0.1',
packageJsonPath: 'libs/my-app/package.json',
localDependencies: [
{
projectName: 'my-lib-1',
dependencyCollection: 'dependencies',
version: '0.0.1',
},
{
projectName: 'my-lib-2',
dependencyCollection: 'devDependencies',
version: '0.0.1',
},
],
},
'my-lib-1': {
projectRoot: 'libs/my-lib-1',
packageName: 'my-lib-1',
version: '0.0.1',
packageJsonPath: 'libs/my-lib-1/package.json',
localDependencies: [],
},
'my-lib-2': {
projectRoot: 'libs/my-lib-2',
packageName: 'my-lib-2',
version: '0.0.1',
packageJsonPath: 'libs/my-lib-2/package.json',
localDependencies: [],
},
});
});
it('updates local dependencies only where it needs to', async () => {
await releaseVersionGenerator(tree, {
projects: Object.values(projectGraph.nodes), // version all projects
projectGraph,
specifier: 'major',
currentVersionResolver: 'disk',
releaseGroup: createReleaseGroup(),
});
expect(readJson(tree, 'libs/my-app/package.json')).toMatchInlineSnapshot(`
{
"dependencies": {
"my-lib-1": "1.0.0",
},
"devDependencies": {
"my-lib-2": "1.0.0",
},
"name": "my-app",
"version": "1.0.0",
}
`);
});
});
});
function createReleaseGroup(
@@ -270,13 +270,10 @@ To fix this you will either need to add a package.json file at that location, or
);
const dependentProjects = Object.values(localPackageDependencies)
.filter((localPackageDependencies) => {
return localPackageDependencies.some(
(localPackageDependency) =>
localPackageDependency.target === project.name
);
})
.flat();
.flat()
.filter((localPackageDependency) => {
return localPackageDependency.target === project.name;
});
if (dependentProjects.length > 0) {
log(
@@ -138,7 +138,7 @@ export function findNextConfigPath(
): string {
if (userDefinedConfigPath) {
const file = userDefinedConfigPath;
if (existsSync(file)) return file;
if (existsSync(join(dirname, file))) return file;
throw new Error(
`Cannot find the Next.js config file: ${userDefinedConfigPath}. Is the path correct in project.json?`
);
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-directory-utils';
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
import type { Linter } from '@nx/eslint';
import type { SupportedStyles } from '@nx/react';
@@ -36,7 +36,7 @@ describe('nuxt:storybook-configuration', () => {
it('should configure with vue3 framework and styles import', async () => {
appTree = await createTestApp('test-ui-app');
await storybookConfigurationGenerator(appTree, {
name: 'test-ui-app',
project: 'test-ui-app',
});
expect(
@@ -59,7 +59,7 @@ describe('nuxt:storybook-configuration', () => {
appTree.write('test-ui-app/src/pages/about.vue', componentContent);
await storybookConfigurationGenerator(appTree, {
name: 'test-ui-app',
project: 'test-ui-app',
generateStories: true,
});
@@ -21,7 +21,7 @@ export async function storybookConfigurationGenerator(
...options,
});
const projectConfiguration = readProjectConfiguration(host, options.name);
const projectConfiguration = readProjectConfiguration(host, options.project);
const storybookConfigFolder =
projectConfiguration.targets?.storybook?.options?.configDir;
@@ -1,7 +1,7 @@
import { Linter } from '@nx/eslint';
export interface Schema {
name: string;
project: string;
interactionTests?: boolean;
generateStories?: boolean;
js?: boolean;
@@ -6,9 +6,9 @@
"description": "Set up Storybook for a Nuxt project.",
"type": "object",
"properties": {
"name": {
"project": {
"type": "string",
"aliases": ["project", "projectName"],
"aliases": ["name", "projectName"],
"description": "Project for which to generate Storybook configuration.",
"$default": {
"$source": "argv",
@@ -75,6 +75,6 @@
]
}
},
"required": ["name"],
"required": ["project"],
"examplesFile": "../../../docs/storybook-configuration-examples.md"
}
+2
View File
@@ -8,9 +8,11 @@ import { verifyOrUpdateNxCloudClient } from '../src/nx-cloud/update-manager';
import { getCloudOptions } from '../src/nx-cloud/utilities/get-cloud-options';
import { isNxCloudUsed } from '../src/utils/nx-cloud-utils';
import { readNxJson } from '../src/config/nx-json';
import { setupWorkspaceContext } from '../src/utils/workspace-context';
(async () => {
try {
setupWorkspaceContext(workspaceRoot);
if (isMainNxPackage() && fileExists(join(workspaceRoot, 'nx.json'))) {
const b = new Date();
assertSupportedPlatform();
+6 -3
View File
@@ -1072,12 +1072,14 @@ async function getWrappedWorkspaceNodeModulesArchitectHost(
builderName
);
const builderInfo = this.readExecutor(packageName, builderName);
const { builders, executors } =
readJsonFile<ExecutorsJson>(executorsFilePath);
return {
name: builderStr,
builderName,
description:
readJsonFile<ExecutorsJson>(executorsFilePath).builders[builderName]
.description,
builders?.[builderName]?.description ??
executors?.[builderName]?.description,
optionSchema: builderInfo.schema,
import: resolveImplementation(
executorConfig.implementation,
@@ -1109,7 +1111,8 @@ async function getWrappedWorkspaceNodeModulesArchitectHost(
batchImplementation?: string;
schema: string;
hasher?: string;
} = executorsJson.builders?.[builder];
} =
executorsJson.builders?.[builder] ?? executorsJson.executors?.[builder];
if (!executorConfig) {
throw new Error(
`Cannot find builder '${builder}' in ${executorsFilePath}.`
+2 -8
View File
@@ -103,15 +103,9 @@ export const enum WorkspaceErrors {
ParseError = 'ParseError',
Generic = 'Generic'
}
export interface ConfigurationParserResult {
projectNodes: Record<string, object>
externalNodes: Record<string, object>
}
export interface NxWorkspaceFiles {
projectFileMap: Record<string, Array<FileData>>
globalFiles: Array<FileData>
projectConfigurations: Record<string, object>
externalNodes: Record<string, object>
}
export class ImportResult {
file: string
@@ -140,9 +134,9 @@ export class Watcher {
export class WorkspaceContext {
workspaceRoot: string
constructor(workspaceRoot: string)
getWorkspaceFiles(globs: Array<string>, parseConfigurations: (arg0: Array<string>) => ConfigurationParserResult): NxWorkspaceFiles
getWorkspaceFiles(globs: Array<string>, parseConfigurations: (arg0: Array<string>) => Record<string, string>): NxWorkspaceFiles
glob(globs: Array<string>): Array<string>
getProjectConfigurations(globs: Array<string>, parseConfigurations: (arg0: Array<string>) => ConfigurationParserResult): ConfigurationParserResult
getProjectConfigurations(globs: Array<string>, parseConfigurations: (arg0: Array<string>) => Record<string, string>): Record<string, string>
incrementalUpdate(updatedFiles: Array<string>, deletedFiles: Array<string>): Record<string, string>
allFileData(): Array<FileData>
}
@@ -10,15 +10,9 @@ describe('workspace files', () => {
const res = {};
for (const filename of filenames) {
const json = readJsonFile(join(tempDir, filename));
res[json.name] = {
...json,
root: dirname(filename),
};
res[dirname(filename)] = json.name;
}
return {
projectNodes: res,
externalNodes: {},
};
return res;
};
}
@@ -57,14 +51,9 @@ describe('workspace files', () => {
let globs = ['project.json', '**/project.json', 'libs/*/package.json'];
const context = new WorkspaceContext(fs.tempDir);
let { projectFileMap, projectConfigurations, globalFiles } =
context.getWorkspaceFiles(
globs,
createParseConfigurationsFunction(fs.tempDir)
);
let sortedConfigs = Object.values(projectConfigurations).sort((a, b) =>
a['name'].localeCompare(b['name'])
let { projectFileMap, globalFiles } = context.getWorkspaceFiles(
globs,
createParseConfigurationsFunction(fs.tempDir)
);
expect(projectFileMap).toMatchInlineSnapshot(`
@@ -121,30 +110,6 @@ describe('workspace files', () => {
],
}
`);
expect(sortedConfigs).toMatchInlineSnapshot(`
[
{
"name": "nested-project",
"root": "libs/nested/project",
},
{
"name": "package-project",
"root": "libs/package-project",
},
{
"name": "project1",
"root": "libs/project1",
},
{
"name": "project2",
"root": "libs/project2",
},
{
"name": "project3",
"root": "libs/project3",
},
]
`);
expect(globalFiles).toMatchInlineSnapshot(`
[
{
@@ -215,57 +180,6 @@ describe('workspace files', () => {
`);
});
it('should dedupe configuration files', async () => {
const fs = new TempFs('workspace-files');
const nxJson: NxJsonConfiguration = {};
await fs.createFiles({
'./nx.json': JSON.stringify(nxJson),
'./package.json': JSON.stringify({
name: 'repo-name',
version: '0.0.0',
dependencies: {},
}),
'./project.json': JSON.stringify({
name: 'repo-name',
}),
'./libs/project1/project.json': JSON.stringify({
name: 'project1',
}),
'./libs/project1/package.json': JSON.stringify({
name: 'project1',
}),
'./libs/project1/index.js': '',
});
const context = new WorkspaceContext(fs.tempDir);
let globs = ['project.json', '**/project.json', '**/package.json'];
let nodes = context.getProjectConfigurations(globs, (filenames) => {
const res = {};
for (const filename of filenames) {
const json = readJsonFile(join(fs.tempDir, filename));
res[json.name] = {
...json,
root: dirname(filename),
};
}
return {
externalNodes: {},
projectNodes: res,
};
});
expect(nodes.projectNodes).toEqual({
project1: {
name: 'project1',
root: 'libs/project1',
},
'repo-name': expect.objectContaining({
name: 'repo-name',
root: '.',
}),
});
});
// describe('errors', () => {
// it('it should infer names of configuration files without a name', async () => {
// const fs = new TempFs('workspace-files');
@@ -1,6 +1,6 @@
use crate::native::utils::glob::build_glob_set;
use crate::native::utils::path::Normalize;
use crate::native::workspace::types::ConfigurationParserResult;
use std::collections::HashMap;
use crate::native::workspace::errors::{InternalWorkspaceErrors, WorkspaceErrors};
use rayon::prelude::*;
@@ -12,7 +12,7 @@ pub(super) fn glob_files(
files: Option<&[(PathBuf, String)]>,
) -> napi::Result<Vec<String>, WorkspaceErrors> {
let Some(files) = files else {
return Ok(Default::default())
return Ok(Default::default());
};
let globs =
@@ -29,12 +29,11 @@ pub(super) fn get_project_configurations<ConfigurationParser>(
globs: Vec<String>,
files: Option<&[(PathBuf, String)]>,
parse_configurations: ConfigurationParser,
) -> napi::Result<ConfigurationParserResult>
) -> napi::Result<HashMap<String, String>>
where
ConfigurationParser: Fn(Vec<String>) -> napi::Result<ConfigurationParserResult>,
ConfigurationParser: Fn(Vec<String>) -> napi::Result<HashMap<String, String>>,
{
let config_paths =
glob_files(globs, files).map_err(anyhow::Error::from)?;
let config_paths = glob_files(globs, files).map_err(anyhow::Error::from)?;
parse_configurations(config_paths)
}
+17 -30
View File
@@ -18,8 +18,6 @@ use crate::native::workspace::errors::WorkspaceErrors;
use crate::native::workspace::workspace_files::NxWorkspaceFiles;
use crate::native::workspace::{config_files, workspace_files};
use crate::native::workspace::types::ConfigurationParserResult;
#[napi]
pub struct WorkspaceContext {
pub workspace_root: String,
@@ -67,13 +65,14 @@ impl FilesWorker {
FilesWorker(Some(files_lock))
}
pub fn get_files(&self) -> Option<MutexGuard<'_, RawMutex, Files>> {
pub fn get_files(&self) -> Option<Vec<(PathBuf, String)>> {
let Some(files_sync) = &self.0 else {
trace!("there were no files because the workspace root did not exist");
return None
return None;
};
let (files_lock, cvar) = &files_sync.deref();
trace!("locking files");
let mut files = files_lock.lock();
let files_len = files.len();
if files_len == 0 {
@@ -81,8 +80,11 @@ impl FilesWorker {
cvar.wait(&mut files);
}
let cloned_files = files.clone();
drop(files);
trace!("files are available");
Some(files)
Some(cloned_files)
}
pub fn update_files(
@@ -93,7 +95,7 @@ impl FilesWorker {
) -> HashMap<String, String> {
let Some(files_sync) = &self.0 else {
trace!("there were no files because the workspace root did not exist");
return HashMap::new();
return HashMap::new();
};
let (files_lock, _) = &files_sync.deref();
@@ -108,8 +110,8 @@ impl FilesWorker {
.par_iter()
.filter_map(|path| {
let full_path = workspace_root_path.join(path);
let Ok( content ) = std::fs::read(full_path) else {
trace!( "could not read file: ?full_path");
let Ok(content) = std::fs::read(full_path) else {
trace!("could not read file: ?full_path");
return None;
};
Some((path.to_string(), xxh3::xxh3_64(&content).to_string()))
@@ -153,30 +155,18 @@ impl WorkspaceContext {
parse_configurations: ConfigurationParser,
) -> napi::Result<NxWorkspaceFiles, WorkspaceErrors>
where
ConfigurationParser: Fn(Vec<String>) -> napi::Result<ConfigurationParserResult>,
ConfigurationParser: Fn(Vec<String>) -> napi::Result<HashMap<String, String>>,
{
workspace_files::get_files(
globs,
parse_configurations,
self.files_worker
.get_files()
.as_deref()
.map(|files| files.as_slice()),
self.files_worker.get_files().as_deref(),
)
}
#[napi]
pub fn glob(
&self,
globs: Vec<String>,
) -> napi::Result<Vec<String>, WorkspaceErrors> {
config_files::glob_files(
globs,
self.files_worker
.get_files()
.as_deref()
.map(|files| files.as_slice()),
)
pub fn glob(&self, globs: Vec<String>) -> napi::Result<Vec<String>, WorkspaceErrors> {
config_files::glob_files(globs, self.files_worker.get_files().as_deref())
}
#[napi]
@@ -184,16 +174,13 @@ impl WorkspaceContext {
&self,
globs: Vec<String>,
parse_configurations: ConfigurationParser,
) -> napi::Result<ConfigurationParserResult>
) -> napi::Result<HashMap<String, String>>
where
ConfigurationParser: Fn(Vec<String>) -> napi::Result<ConfigurationParserResult>,
ConfigurationParser: Fn(Vec<String>) -> napi::Result<HashMap<String, String>>,
{
config_files::get_project_configurations(
globs,
self.files_worker
.get_files()
.as_deref()
.map(|files| files.as_slice()),
self.files_worker.get_files().as_deref(),
parse_configurations,
)
}
-10
View File
@@ -1,15 +1,5 @@
use std::collections::HashMap;
use napi::JsObject;
#[derive(Debug, Eq, PartialEq)]
pub enum FileLocation {
Global,
Project(String),
}
#[napi(object)]
pub struct ConfigurationParserResult {
pub project_nodes: HashMap<String, JsObject>,
pub external_nodes: HashMap<String, JsObject>,
}
@@ -1,4 +1,3 @@
use napi::JsObject;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
@@ -9,15 +8,13 @@ use crate::native::types::FileData;
use crate::native::utils::path::Normalize;
use crate::native::workspace::config_files;
use crate::native::workspace::errors::{InternalWorkspaceErrors, WorkspaceErrors};
use crate::native::workspace::types::{ConfigurationParserResult, FileLocation};
use crate::native::workspace::types::FileLocation;
#[napi(object)]
#[derive(Default)]
pub struct NxWorkspaceFiles {
pub project_file_map: HashMap<String, Vec<FileData>>,
pub global_files: Vec<FileData>,
pub project_configurations: HashMap<String, JsObject>,
pub external_nodes: HashMap<String, JsObject>,
}
pub(super) fn get_files<ConfigurationParser>(
@@ -26,18 +23,17 @@ pub(super) fn get_files<ConfigurationParser>(
file_data: Option<&[(PathBuf, String)]>,
) -> napi::Result<NxWorkspaceFiles, WorkspaceErrors>
where
ConfigurationParser: Fn(Vec<String>) -> napi::Result<ConfigurationParserResult>,
ConfigurationParser: Fn(Vec<String>) -> napi::Result<HashMap<String, String>>,
{
let Some(file_data) = file_data else {
return Ok(Default::default())
return Ok(Default::default());
};
trace!("{globs:?}");
let parsed_graph_nodes =
let root_map = transform_root_map(
config_files::get_project_configurations(globs, Some(file_data), parse_configurations)
.map_err(|e| InternalWorkspaceErrors::ParseError(e.to_string()))?;
let root_map = create_root_map(&parsed_graph_nodes.project_nodes);
.map_err(|e| InternalWorkspaceErrors::ParseError(e.to_string()))?,
);
trace!(?root_map);
@@ -89,19 +85,12 @@ where
Ok(NxWorkspaceFiles {
project_file_map,
global_files,
external_nodes: parsed_graph_nodes.external_nodes,
project_configurations: parsed_graph_nodes.project_nodes,
})
}
fn create_root_map(
project_configurations: &HashMap<String, JsObject>,
) -> hashbrown::HashMap<PathBuf, String> {
project_configurations
.iter()
.map(|(project_name, project_configuration)| {
let root: String = project_configuration.get("root").unwrap().unwrap();
(PathBuf::from(root), project_name.clone())
})
fn transform_root_map(root_map: HashMap<String, String>) -> hashbrown::HashMap<PathBuf, String> {
root_map
.into_iter()
.map(|(project_root, project_name)| (PathBuf::from(project_root), project_name))
.collect()
}
@@ -96,6 +96,7 @@ export function buildProjectsConfigurationsFromProjectPathsAndPlugins(
): {
projects: Record<string, ProjectConfiguration>;
externalNodes: Record<string, ProjectGraphExternalNode>;
rootMap: Record<string, string>;
} {
const projectRootMap: Map<string, ProjectConfiguration> = new Map();
const externalNodes: Record<string, ProjectGraphExternalNode> = {};
@@ -133,9 +134,12 @@ export function buildProjectsConfigurationsFromProjectPathsAndPlugins(
}
}
const rootMap = createRootMap(projectRootMap);
return {
projects: readProjectConfigurationsFromRootMap(projectRootMap),
externalNodes,
rootMap,
};
}
@@ -290,3 +294,10 @@ export function readTargetDefaultsForTarget(
return targetDefaults?.[targetName];
}
}
function createRootMap(projectRootMap: Map<string, ProjectConfiguration>) {
const map: Record<string, string> = {};
for (const [projectRoot, { name: projectName }] of projectRootMap) {
map[projectRoot] = projectName;
}
return map;
}
@@ -60,25 +60,26 @@ export async function retrieveWorkspaceFiles(
);
performance.mark('get-workspace-files:start');
let projects: Record<string, ProjectConfiguration>;
let externalNodes: Record<string, ProjectGraphExternalNode>;
const { projectConfigurations, projectFileMap, globalFiles, externalNodes } =
getNxWorkspaceFilesFromContext(
workspaceRoot,
globs,
(configs: string[]) => {
const projectConfigurations = createProjectConfigurations(
workspaceRoot,
nxJson,
configs,
plugins
);
const { projectFileMap, globalFiles } = getNxWorkspaceFilesFromContext(
workspaceRoot,
globs,
(configs: string[]) => {
const projectConfigurations = createProjectConfigurations(
workspaceRoot,
nxJson,
configs,
plugins
);
return {
projectNodes: projectConfigurations.projects,
externalNodes: projectConfigurations.externalNodes,
};
}
) as NxWorkspaceFiles;
projects = projectConfigurations.projects;
externalNodes = projectConfigurations.externalNodes;
return projectConfigurations.rootMap;
}
) as NxWorkspaceFiles;
performance.mark('get-workspace-files:end');
performance.measure(
'get-workspace-files',
@@ -94,9 +95,9 @@ export async function retrieveWorkspaceFiles(
},
projectConfigurations: {
version: 2,
projects: projectConfigurations,
projects,
} as ProjectsConfigurations,
externalNodes: externalNodes as Record<string, ProjectGraphExternalNode>,
externalNodes,
};
}
@@ -176,26 +177,30 @@ function _retrieveProjectConfigurations(
externalNodes: Record<string, ProjectGraphExternalNode>;
projectNodes: Record<string, ProjectConfiguration>;
} {
return getProjectConfigurationsFromContext(
let result: {
externalNodes: Record<string, ProjectGraphExternalNode>;
projectNodes: Record<string, ProjectConfiguration>;
};
getProjectConfigurationsFromContext(
workspaceRoot,
globs,
(configs: string[]) => {
const projectConfigurations = createProjectConfigurations(
const { projects, externalNodes, rootMap } = createProjectConfigurations(
workspaceRoot,
nxJson,
configs,
plugins
);
return {
projectNodes: projectConfigurations.projects,
externalNodes: projectConfigurations.externalNodes,
result = {
projectNodes: projects,
externalNodes: externalNodes,
};
return rootMap;
}
) as {
externalNodes: Record<string, ProjectGraphExternalNode>;
projectNodes: Record<string, ProjectConfiguration>;
};
);
return result;
}
export async function retrieveProjectConfigurationPaths(
@@ -232,24 +237,28 @@ export function retrieveProjectConfigurationsWithoutPluginInference(
return projectsWithoutPluginCache.get(cacheKey);
}
const projectConfigurations = getProjectConfigurationsFromContext(
let projects: Record<string, ProjectConfiguration>;
getProjectConfigurationsFromContext(
root,
projectGlobPatterns,
(configs: string[]) => {
const { projects } = createProjectConfigurations(root, nxJson, configs, [
{ plugin: getNxPackageJsonWorkspacesPlugin(root) },
{ plugin: CreateProjectJsonProjectsPlugin },
]);
return {
projectNodes: projects,
externalNodes: {},
};
const projectConfigurations = createProjectConfigurations(
root,
nxJson,
configs,
[
{ plugin: getNxPackageJsonWorkspacesPlugin(root) },
{ plugin: CreateProjectJsonProjectsPlugin },
]
);
projects = projectConfigurations.projects;
return projectConfigurations.rootMap;
}
).projectNodes as Record<string, ProjectConfiguration>;
);
projectsWithoutPluginCache.set(cacheKey, projectConfigurations);
projectsWithoutPluginCache.set(cacheKey, projects);
return projectConfigurations;
return projects;
}
function buildAllWorkspaceFiles(
@@ -279,10 +288,11 @@ export function createProjectConfigurations(
): {
projects: Record<string, ProjectConfiguration>;
externalNodes: Record<string, ProjectGraphExternalNode>;
rootMap: Record<string, string>;
} {
performance.mark('build-project-configs:start');
const { projects, externalNodes } =
const { projects, externalNodes, rootMap } =
buildProjectsConfigurationsFromProjectPathsAndPlugins(
nxJson,
configFiles,
@@ -302,6 +312,7 @@ export function createProjectConfigurations(
return {
projects: projectConfigurations,
externalNodes,
rootMap,
};
}
+22
View File
@@ -710,6 +710,28 @@ describe('params', () => {
expect(params).toEqual({ a: './somepath' });
});
it('should use relativeCwd to set workingDirectory', () => {
const params = {};
convertSmartDefaultsIntoNamedParams(
params,
{
properties: {
a: {
type: 'string',
$default: {
$source: 'workingDirectory',
},
visible: false,
},
},
},
null,
'./somepath'
);
expect(params).toEqual({ a: './somepath' });
});
it('should set unparsed overrides', () => {
const params = { __overrides_unparsed__: ['one'] };
convertSmartDefaultsIntoNamedParams(
+9 -1
View File
@@ -33,7 +33,8 @@ type PropertyDescription = {
$default?:
| { $source: 'argv'; index: number }
| { $source: 'projectName' }
| { $source: 'unparsed' };
| { $source: 'unparsed' }
| { $source: 'workingDirectory' };
additionalProperties?: boolean;
const?: any;
'x-prompt'?:
@@ -701,6 +702,13 @@ export function convertSmartDefaultsIntoNamedParams(
relativeCwd
) {
opts[k] = relativeCwd.replace(/\\/g, '/');
} else if (
opts[k] === undefined &&
v.$default !== undefined &&
v.$default.$source === 'workingDirectory' &&
relativeCwd
) {
opts[k] = relativeCwd.replace(/\\/g, '/');
}
});
const leftOverPositionalArgs = [];
+3 -3
View File
@@ -1,4 +1,4 @@
import type { ConfigurationParserResult, WorkspaceContext } from '../native';
import type { WorkspaceContext } from '../native';
import { performance } from 'perf_hooks';
let workspaceContext: WorkspaceContext | undefined;
@@ -19,7 +19,7 @@ export function setupWorkspaceContext(workspaceRoot: string) {
export function getNxWorkspaceFilesFromContext(
workspaceRoot: string,
globs: string[],
parseConfigurations: (files: string[]) => ConfigurationParserResult
parseConfigurations: (files: string[]) => Record<string, string>
) {
ensureContextAvailable(workspaceRoot);
return workspaceContext.getWorkspaceFiles(globs, parseConfigurations);
@@ -36,7 +36,7 @@ export function globWithWorkspaceContext(
export function getProjectConfigurationsFromContext(
workspaceRoot: string,
globs: string[],
parseConfigurations: (files: string[]) => ConfigurationParserResult
parseConfigurations: (files: string[]) => Record<string, string>
) {
ensureContextAvailable(workspaceRoot);
return workspaceContext.getProjectConfigurations(globs, parseConfigurations);
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-directory-utils';
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
import type { Linter } from '@nx/eslint';
export interface CreatePackageSchema {
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-directory-utils';
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
import type { Linter } from '@nx/eslint';
export interface Schema {
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-directory-utils';
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
import type { Linter } from '@nx/eslint';
export interface Schema {
@@ -38,7 +38,7 @@ export async function storybookConfigurationGenerator(
>('@nx/storybook', nxVersion);
const installTask = await configurationGenerator(host, {
name: schema.name,
project: schema.name,
uiFramework: '@storybook/react-native',
configureCypress: false,
js: false,
@@ -64,6 +64,8 @@ describe('app', () => {
'vite/client',
'node',
'vitest',
'@nx/react/typings/cssmodule.d.ts',
'@nx/react/typings/image.d.ts',
]);
});
@@ -192,7 +192,7 @@ export async function applicationGeneratorInternal(
const vitestTask = await vitestGenerator(host, {
uiFramework: 'react',
coverageProvider: 'c8',
coverageProvider: 'v8',
project: options.projectName,
inSourceTests: options.inSourceTests,
skipFormat: true,
@@ -8,15 +8,17 @@ export function updateSpecConfig(host: Tree, options: NormalizedSchema) {
}
updateJson(host, `${options.appProjectRoot}/tsconfig.spec.json`, (json) => {
json.types = json.types || [];
const compilerOptions = json.compilerOptions ?? {};
const types = compilerOptions.types ?? [];
if (options.style === 'styled-jsx') {
json.types.push('@nx/react/typings/styled-jsx.d.ts');
types.push('@nx/react/typings/styled-jsx.d.ts');
}
json.types = [
...json.types,
types.push(
'@nx/react/typings/cssmodule.d.ts',
'@nx/react/typings/image.d.ts',
];
'@nx/react/typings/image.d.ts'
);
compilerOptions.types = types;
json.compilerOptions = compilerOptions;
return json;
});
@@ -0,0 +1,19 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"node",
"@nx/react/typings/cssmodule.d.ts",
"@nx/react/typings/image.d.ts"
]
},
"include": [
"src/**/*.js",
"src/**/*.jsx",
"src/**/*.ts",
"src/**/*.tsx",
"webpack.config.ts",
"webpack.prod.config.ts"
]
}
@@ -105,6 +105,12 @@ export async function hostGeneratorInternal(
updateProjectConfiguration(host, options.projectName, projectConfig);
}
if (!options.setParserOptionsProject) {
host.delete(
joinPathFragments(options.appProjectRoot, 'tsconfig.lint.json')
);
}
if (!options.skipFormat) {
await formatFiles(host);
}
@@ -147,7 +147,7 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) {
const vitestTask = await vitestGenerator(host, {
uiFramework: 'react',
project: options.name,
coverageProvider: 'c8',
coverageProvider: 'v8',
inSourceTests: options.inSourceTests,
skipFormat: true,
testEnvironment: 'jsdom',
@@ -0,0 +1,19 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"node",
"@nx/react/typings/cssmodule.d.ts",
"@nx/react/typings/image.d.ts"
]
},
"include": [
"src/**/*.js",
"src/**/*.jsx",
"src/**/*.ts",
"src/**/*.tsx",
"webpack.config.ts",
"webpack.prod.config.ts"
]
}
@@ -0,0 +1,19 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": [
"node",
"@nx/react/typings/cssmodule.d.ts",
"@nx/react/typings/image.d.ts"
]
},
"include": [
"src/**/*.js",
"src/**/*.jsx",
"src/**/*.ts",
"src/**/*.tsx",
"webpack.config.ts",
"webpack.prod.config.ts"
]
}
@@ -119,6 +119,11 @@ export async function remoteGeneratorInternal(host: Tree, schema: Schema) {
);
updateProjectConfiguration(host, options.projectName, projectConfig);
}
if (!options.setParserOptionsProject) {
host.delete(
joinPathFragments(options.appProjectRoot, 'tsconfig.lint.json')
);
}
if (!options.skipFormat) {
await formatFiles(host);
@@ -37,7 +37,7 @@ describe('react:storybook-configuration', () => {
it('should configure everything and install correct dependencies', async () => {
appTree = await createTestUILib('test-ui-lib');
await storybookConfigurationGenerator(appTree, {
name: 'test-ui-lib',
project: 'test-ui-lib',
});
expect(
@@ -59,7 +59,7 @@ describe('react:storybook-configuration', () => {
it('should generate stories for components', async () => {
appTree = await createTestUILib('test-ui-lib');
await storybookConfigurationGenerator(appTree, {
name: 'test-ui-lib',
project: 'test-ui-lib',
generateStories: true,
});
@@ -89,7 +89,7 @@ describe('react:storybook-configuration', () => {
`
);
await storybookConfigurationGenerator(appTree, {
name: 'test-ui-lib',
project: 'test-ui-lib',
generateStories: true,
js: true,
});
@@ -102,7 +102,7 @@ describe('react:storybook-configuration', () => {
it('should configure everything at once', async () => {
appTree = await createTestAppLib('test-ui-app');
await storybookConfigurationGenerator(appTree, {
name: 'test-ui-app',
project: 'test-ui-app',
});
expect(appTree.exists('test-ui-app/.storybook/main.ts')).toBeTruthy();
@@ -112,7 +112,7 @@ describe('react:storybook-configuration', () => {
it('should generate stories for components', async () => {
appTree = await createTestAppLib('test-ui-app');
await storybookConfigurationGenerator(appTree, {
name: 'test-ui-app',
project: 'test-ui-app',
generateStories: true,
});
@@ -130,7 +130,7 @@ describe('react:storybook-configuration', () => {
it('should generate stories for components without interaction tests', async () => {
appTree = await createTestAppLib('test-ui-app');
await storybookConfigurationGenerator(appTree, {
name: 'test-ui-app',
project: 'test-ui-app',
generateStories: true,
interactionTests: false,
});
@@ -14,14 +14,14 @@ async function generateStories(host: Tree, schema: StorybookConfigureSchema) {
const { getE2eProjectName } = await import(
'@nx/cypress/src/utils/project-name'
);
const projectConfig = readProjectConfiguration(host, schema.name);
const projectConfig = readProjectConfiguration(host, schema.project);
const cypressProject = getE2eProjectName(
schema.name,
schema.project,
projectConfig.root,
schema.cypressDirectory
);
await storiesGenerator(host, {
project: schema.name,
project: schema.project,
generateCypressSpecs:
schema.configureCypress && schema.generateCypressSpecs,
js: schema.js,
@@ -41,7 +41,7 @@ export async function storybookConfigurationGenerator(
>('@nx/storybook', nxVersion);
let uiFramework = '@storybook/react-vite';
const projectConfig = readProjectConfiguration(host, schema.name);
const projectConfig = readProjectConfiguration(host, schema.project);
if (
projectConfig.targets['build']?.executor === '@nx/webpack:webpack' ||
@@ -53,7 +53,7 @@ export async function storybookConfigurationGenerator(
}
const installTask = await configurationGenerator(host, {
name: schema.name,
project: schema.project,
configureCypress: schema.configureCypress,
js: schema.js,
linter: schema.linter,
@@ -1,7 +1,7 @@
import { Linter } from '@nx/eslint';
export interface StorybookConfigureSchema {
name: string;
project: string;
interactionTests?: boolean;
generateStories?: boolean;
js?: boolean;
@@ -6,9 +6,9 @@
"description": "Set up Storybook for a React app or library.",
"type": "object",
"properties": {
"name": {
"project": {
"type": "string",
"aliases": ["project", "projectName"],
"aliases": ["name", "projectName"],
"description": "Project for which to generate Storybook configuration.",
"$default": {
"$source": "argv",
@@ -90,6 +90,6 @@
]
}
},
"required": ["name"],
"required": ["project"],
"examplesFile": "../../../docs/storybook-configuration-examples.md"
}
@@ -85,7 +85,7 @@ describe('@nx/storybook:configuration for workspaces with Root project', () => {
it('should generate files for root app - js for tsConfiguration: false', async () => {
await configurationGenerator(tree, {
name: 'web',
project: 'web',
uiFramework: '@storybook/react-webpack5',
tsConfiguration: false,
});
@@ -99,7 +99,7 @@ describe('@nx/storybook:configuration for workspaces with Root project', () => {
writeJson(tree, 'apps/reapp/tsconfig.json', {});
await configurationGenerator(tree, {
name: 'reapp',
project: 'reapp',
uiFramework: '@storybook/react-webpack5',
});
@@ -112,7 +112,7 @@ describe('@nx/storybook:configuration for workspaces with Root project', () => {
expect(tree.exists('apps/reapp/.storybook/preview.ts')).toBeTruthy();
await configurationGenerator(tree, {
name: 'web',
project: 'web',
uiFramework: '@storybook/react-vite',
});
@@ -59,7 +59,7 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
it('should generate TypeScript Configuration files by default', async () => {
await configurationGenerator(tree, {
name: 'test-ui-lib',
project: 'test-ui-lib',
standaloneConfig: false,
uiFramework: '@storybook/angular',
});
@@ -76,7 +76,7 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
it('should update `tsconfig.lib.json` file', async () => {
await configurationGenerator(tree, {
name: 'test-ui-lib',
project: 'test-ui-lib',
standaloneConfig: false,
uiFramework: '@storybook/react-webpack5',
});
@@ -93,7 +93,7 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
it('should update `tsconfig.json` file', async () => {
await configurationGenerator(tree, {
name: 'test-ui-lib',
project: 'test-ui-lib',
standaloneConfig: false,
uiFramework: '@storybook/react-webpack5',
});
@@ -132,7 +132,7 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
});
await configurationGenerator(tree, {
name: 'test-ui-lib2',
project: 'test-ui-lib2',
standaloneConfig: false,
uiFramework: '@storybook/react-webpack5',
});
@@ -155,7 +155,7 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
});
await configurationGenerator(tree, {
name: 'test-ui-lib2',
project: 'test-ui-lib2',
standaloneConfig: false,
uiFramework: '@storybook/react-webpack5',
});
@@ -171,7 +171,7 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
it('should generate TS config for project by default', async () => {
await configurationGenerator(tree, {
name: 'test-ui-lib',
project: 'test-ui-lib',
standaloneConfig: false,
uiFramework: '@storybook/angular',
});
@@ -186,7 +186,7 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
it('should add test-storybook target', async () => {
await configurationGenerator(tree, {
name: 'test-ui-lib',
project: 'test-ui-lib',
interactionTests: true,
uiFramework: '@storybook/react-webpack5',
});
@@ -261,54 +261,54 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
tree.write('apps/wv1/vite.config.custom.ts', 'export default {}');
await configurationGenerator(tree, {
name: 'reapp',
project: 'reapp',
tsConfiguration: false,
uiFramework: '@storybook/react-vite',
});
await configurationGenerator(tree, {
name: 'main-vite',
project: 'main-vite',
tsConfiguration: false,
uiFramework: '@storybook/react-vite',
});
await configurationGenerator(tree, {
name: 'main-vite-ts',
project: 'main-vite-ts',
uiFramework: '@storybook/react-vite',
});
await configurationGenerator(tree, {
name: 'main-webpack',
project: 'main-webpack',
uiFramework: '@storybook/react-webpack5',
});
await configurationGenerator(tree, {
name: 'reappw',
project: 'reappw',
uiFramework: '@storybook/react-webpack5',
});
await configurationGenerator(tree, {
name: 'react-rollup',
project: 'react-rollup',
uiFramework: '@storybook/react-webpack5',
});
await configurationGenerator(tree, {
name: 'react-vite',
project: 'react-vite',
uiFramework: '@storybook/react-vite',
});
await configurationGenerator(tree, {
name: 'nextapp',
project: 'nextapp',
uiFramework: '@storybook/nextjs',
});
await configurationGenerator(tree, {
name: 'react-swc',
project: 'react-swc',
uiFramework: '@storybook/react-webpack5',
});
await configurationGenerator(tree, {
name: 'wv1',
project: 'wv1',
uiFramework: '@storybook/web-components-vite',
});
await configurationGenerator(tree, {
name: 'ww1',
project: 'ww1',
uiFramework: '@storybook/web-components-webpack5',
});
});
@@ -56,7 +56,7 @@ export async function configurationGenerator(
const { projectType, targets, root } = readProjectConfiguration(
tree,
schema.name
schema.project
);
const { nextBuildTarget, compiler, viteBuildTarget } =
findStorybookAndBuildTargetsAndCompiler(targets);
@@ -74,14 +74,14 @@ export async function configurationGenerator(
if (viteBuildTarget) {
if (schema.uiFramework === '@storybook/react-webpack5') {
logger.info(
`Your project ${schema.name} uses Vite as a bundler.
`Your project ${schema.project} uses Vite as a bundler.
Nx will configure Storybook for this project to use Vite as well.`
);
schema.uiFramework = '@storybook/react-vite';
}
if (schema.uiFramework === '@storybook/web-components-webpack5') {
logger.info(
`Your project ${schema.name} uses Vite as a bundler.
`Your project ${schema.project} uses Vite as a bundler.
Nx will configure Storybook for this project to use Vite as well.`
);
schema.uiFramework = '@storybook/web-components-vite';
@@ -105,7 +105,7 @@ export async function configurationGenerator(
createProjectStorybookDir(
tree,
schema.name,
schema.project,
schema.uiFramework,
schema.js,
schema.tsConfiguration,
@@ -138,11 +138,11 @@ export async function configurationGenerator(
addStorybookToNamedInputs(tree);
if (schema.uiFramework === '@storybook/angular') {
addAngularStorybookTask(tree, schema.name, schema.interactionTests);
addAngularStorybookTask(tree, schema.project, schema.interactionTests);
} else {
addStorybookTask(
tree,
schema.name,
schema.project,
schema.uiFramework,
schema.interactionTests
);
@@ -154,10 +154,10 @@ export async function configurationGenerator(
// TODO(katerina): Nx 18 -> remove Cypress
if (schema.configureCypress) {
const e2eProject = await getE2EProjectName(tree, schema.name);
const e2eProject = await getE2EProjectName(tree, schema.project);
if (!e2eProject) {
const cypressTask = await cypressProjectGenerator(tree, {
name: schema.name,
name: schema.project,
js: schema.js,
linter: schema.linter,
directory: schema.cypressDirectory,
@@ -170,7 +170,7 @@ export async function configurationGenerator(
tasks.push(cypressTask);
} else {
logger.warn(
`There is already an e2e project setup for ${schema.name}, called ${e2eProject}.`
`There is already an e2e project setup for ${schema.project}, called ${e2eProject}.`
);
}
}
@@ -141,20 +141,20 @@ export function addAngularStorybookTask(
export function addStaticTarget(tree: Tree, opts: StorybookConfigureSchema) {
const nrwlWeb = ensurePackage<typeof import('@nx/web')>('@nx/web', nxVersion);
nrwlWeb.webStaticServeGenerator(tree, {
buildTarget: `${opts.name}:build-storybook`,
outputPath: joinPathFragments('dist/storybook', opts.name),
buildTarget: `${opts.project}:build-storybook`,
outputPath: joinPathFragments('dist/storybook', opts.project),
targetName: 'static-storybook',
});
const projectConfig = readProjectConfiguration(tree, opts.name);
const projectConfig = readProjectConfiguration(tree, opts.project);
projectConfig.targets['static-storybook'].configurations = {
ci: {
buildTarget: `${opts.name}:build-storybook:ci`,
buildTarget: `${opts.project}:build-storybook:ci`,
},
};
updateProjectConfiguration(tree, opts.name, projectConfig);
updateProjectConfiguration(tree, opts.project, projectConfig);
}
export function createStorybookTsconfigFile(
@@ -282,7 +282,7 @@ export function configureTsProjectConfig(
tree: Tree,
schema: StorybookConfigureSchema
) {
const { name: projectName } = schema;
const { project: projectName } = schema;
let tsConfigPath: string;
let tsConfigContent: TsConfig;
@@ -323,7 +323,7 @@ export function configureTsSolutionConfig(
tree: Tree,
schema: StorybookConfigureSchema
) {
const { name: projectName } = schema;
const { project: projectName } = schema;
const { root } = readProjectConfiguration(tree, projectName);
const tsConfigPath = join(root, 'tsconfig.json');
@@ -368,7 +368,7 @@ export function configureTsSolutionConfig(
* This is done within the eslint config file.
*/
export function updateLintConfig(tree: Tree, schema: StorybookConfigureSchema) {
const { name: projectName } = schema;
const { project: projectName } = schema;
const { root } = readProjectConfiguration(tree, projectName);
@@ -2,7 +2,7 @@ import { Linter } from '@nx/eslint';
import { UiFramework } from '../../utils/models';
export interface StorybookConfigureSchema {
name: string;
project: string;
uiFramework?: UiFramework;
linter?: Linter;
js?: boolean;
@@ -6,9 +6,9 @@
"description": "Add Storybook configuration to a UI library or an application.",
"type": "object",
"properties": {
"name": {
"project": {
"type": "string",
"aliases": ["project", "projectName"],
"aliases": ["name", "projectName"],
"description": "Project for which to generate Storybook configuration.",
"$default": {
"$source": "argv",
@@ -103,6 +103,6 @@
"x-priority": "internal"
}
},
"required": ["name", "uiFramework"],
"required": ["project", "uiFramework"],
"examplesFile": "../../../docs/configuration-generator-examples.md"
}
+18
View File
@@ -43,6 +43,24 @@
}
},
"packageJsonUpdates": {
"17.2.0": {
"vitest": {
"version": "~0.34.6",
"alwaysAddToPackageJson": false
},
"@vitest/coverage-v8": {
"version": "~0.34.6",
"alwaysAddToPackageJson": false
},
"@vitest/ui": {
"version": "~0.34.6",
"alwaysAddToPackageJson": false
},
"@vitest/coverage-istanbul": {
"version": "~0.34.6",
"alwaysAddToPackageJson": false
}
},
"16.4.0-beta.7": {
"version": "16.4.0-beta.7",
"packages": {
@@ -108,12 +108,14 @@ async function getSettings(
const packageJson = existsSync(packageJsonPath)
? readJsonFile(packageJsonPath)
: undefined;
let provider: 'v8' | 'c8' = 'v8';
let provider: 'v8' | 'istanbul' | 'custom';
if (
packageJson?.dependencies?.['@vitest/coverage-c8'] ||
packageJson?.devDependencies?.['@vitest/coverage-c8']
packageJson?.dependencies?.['@vitest/coverage-istanbul'] ||
packageJson?.devDependencies?.['@vitest/coverage-istanbul']
) {
provider = 'c8';
provider = 'istanbul';
} else {
provider = 'v8';
}
const offset = relative(workspaceRoot, context.cwd);
// if reportsDirectory is not provided vitest will remove all files in the project root

Some files were not shown because too many files have changed in this diff Show More