Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c98ab477e | |||
| dc645caa96 | |||
| 69d7d019d9 | |||
| 17ab5324cc | |||
| 6c8b019814 | |||
| dc926bd3ed | |||
| 09552c8ad4 | |||
| 3b2b940584 | |||
| 525e0ca128 | |||
| 99c2db954e | |||
| 57d751c213 | |||
| 8bf3199fa3 | |||
| 03b5f355fc | |||
| a3d46ecd11 | |||
| 9e6505faf3 | |||
| 6da364c6eb | |||
| cdc3a42d90 | |||
| ac14278be3 | |||
| f4c1950e6f | |||
| 6ee8bcccee | |||
| b8fe86dfa3 | |||
| 444ae346bd | |||
| 2c2317a85e | |||
| ffd4900d11 | |||
| 5f694de454 | |||
| 2bc73ccf8c | |||
| 7975e490d4 | |||
| 1d94c375a5 | |||
| 55b804082e |
@@ -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
@@ -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": []
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -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
|
||||
)
|
||||
|
||||
+4
-3
@@ -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
|
||||
|
||||
+4
-3
@@ -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';
|
||||
|
||||
+4
-2
@@ -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) { %>
|
||||
|
||||
+4
-2
@@ -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
-1
@@ -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
-7
@@ -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' }));
|
||||
|
||||
|
||||
+1
-1
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
+1
-1
@@ -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"
|
||||
}
|
||||
|
||||
+5
-5
@@ -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
|
||||
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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}.`
|
||||
|
||||
Vendored
+2
-8
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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,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
@@ -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
@@ -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',
|
||||
|
||||
+19
@@ -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"
|
||||
]
|
||||
}
|
||||
+19
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user