Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5f658b95d | |||
| 8fac5d79da | |||
| c3ed7f9f5d | |||
| e337cb6de7 | |||
| d082cd1610 | |||
| fef174f509 | |||
| a6a81cfc33 | |||
| a45cdd6f55 | |||
| c0da413fb8 | |||
| 7fc8a62880 | |||
| 88c2529dbc | |||
| c41de4b383 | |||
| 84ed1d728e | |||
| f371a6e217 | |||
| 8d0f277520 | |||
| 6cb2af4e6f | |||
| f808ea86b0 | |||
| a714301ad0 | |||
| d59e8cc6af | |||
| 10405bcfe9 | |||
| 07c07e4e86 | |||
| 7325b9ce28 | |||
| c83dd29c3c | |||
| 399ee5b3bd | |||
| 6f9e1477d4 | |||
| 1e39ba671f | |||
| 7ead9a022f | |||
| 99cfc14893 | |||
| aa7b2b011d | |||
| 245bed7a07 | |||
| e47b45b958 | |||
| 69ec3796fe | |||
| 145fe01e3d | |||
| afe0cecb1e | |||
| 485090572c | |||
| e8ce99d234 | |||
| 46eac466de | |||
| c006e90f97 | |||
| c3f401ceda | |||
| 85bed15280 | |||
| 74777a1754 | |||
| 11fe2a700e | |||
| 926ade4270 | |||
| 7231561c92 | |||
| e458ecf2b1 | |||
| 371d20446f | |||
| 392d72e00d |
@@ -1,6 +1,7 @@
|
||||
name: 🐞 Bug Report
|
||||
description: This form is to report unexpected behavior in Nx.
|
||||
labels: ["type: bug"]
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
@@ -8,25 +8,21 @@ on:
|
||||
permissions: {}
|
||||
jobs:
|
||||
audit:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install PNPM
|
||||
run: |
|
||||
npm install -g @pnpm/exe@8
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.8.0 # Aligned with root package.json (pnpm/action-setup will helpfully error if out of sync)
|
||||
|
||||
- name: Run a security audit
|
||||
run: pnpm dlx audit-ci --critical --report-type summary
|
||||
|
||||
# - name: Run Dependency confusion supply chain check
|
||||
# run: npx snync -d .
|
||||
|
||||
report:
|
||||
if: ${{ always() && github.repository_owner == 'nrwl' && github.event_name != 'workflow_dispatch' }}
|
||||
needs: audit
|
||||
|
||||
@@ -79,12 +79,14 @@ rust-toolchain @nrwl/nx-native-reviewers
|
||||
/docs/generated/packages/js/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/web/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/webpack/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/rspack/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/esbuild/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/rollup/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/vite/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/js/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/web/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/webpack/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/rspack/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/esbuild/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/vite/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/js/** @nrwl/nx-js-reviewers
|
||||
@@ -94,6 +96,9 @@ rust-toolchain @nrwl/nx-native-reviewers
|
||||
/packages/webpack/** @nrwl/nx-js-reviewers
|
||||
/packages/webpack/src/utils/module-federation @jaysoo @Coly010
|
||||
/e2e/webpack/** @nrwl/nx-js-reviewers
|
||||
/packages/rspack/** @nrwl/nx-js-reviewers
|
||||
/packages/rspack/src/utils/module-federation @jaysoo @Coly010
|
||||
/e2e/rspack/** @nrwl/nx-js-reviewers
|
||||
/packages/esbuild/** @nrwl/nx-js-reviewers
|
||||
/e2e/esbuild/** @nrwl/nx-js-reviewers
|
||||
/packages/rollup/** @nrwl/nx-js-reviewers
|
||||
|
||||
@@ -61,13 +61,13 @@ Tasks can fail:
|
||||
Let’s see how varying a few parameters affects the probability of the CI execution failing.
|
||||
|
||||
| Number of VMS | Avg Tests per VM | Flaky Test Probability | Slow Test Probability | Broken CI Builds (Flaky) | Slow CI Builds |
|
||||
| ------------- | ---------------- | ---------------------- | --------------------- | ------------------------ | -------------- |
|
||||
| 5 | 10 | 0.1% | 0.3% | 5% | 15% |
|
||||
| 10 | 10 | 0.1% | 0.3% | 10% | 26% |
|
||||
| 50 | 10 | 0.1% | 0.3% | 39% | 78% |
|
||||
| 5 | 10 | 0.5% | 1% | 23% | 41% |
|
||||
| 10 | 10 | 0.5% | 1% | 40% | 65% |
|
||||
| 50 | 10 | 0.5% | 1% | 92% | 99% |
|
||||
| :-----------: | :--------------: | :--------------------: | :-------------------: | :----------------------: | :------------: |
|
||||
| 5 | 10 | 0.1% | 0.3% | 5% | 15% |
|
||||
| 10 | 10 | 0.1% | 0.3% | 10% | 26% |
|
||||
| 50 | 10 | 0.1% | 0.3% | 39% | 78% |
|
||||
| 5 | 10 | 0.5% | 1% | 23% | 41% |
|
||||
| 10 | 10 | 0.5% | 1% | 40% | 65% |
|
||||
| 50 | 10 | 0.5% | 1% | 92% | 99% |
|
||||
|
||||
**The result is much worse than most intuitively expect.** For instance, assuming that an **e2e test has 1 in 1000 chance (0.1%) of failing** for a flaky reason, when the number of e2e tests reaches 500, **the probability of the CI failing for a flaky reason reaches 39%**, and the vast majority of CI executions are slowed down. Note, this is an exceptionally stable test suite. The bottom part of the table is more representative of a typical e2e suite, and the CI becomes “broken” at a much smaller scale.
|
||||
|
||||
|
||||
@@ -6,6 +6,12 @@ tags: [nx, release]
|
||||
cover_image: /blog/images/evolving-nx/thumbnail.png
|
||||
---
|
||||
|
||||
_Update from Jeff Cross, October 17, 2024_
|
||||
|
||||
In my original version of this post, I said that Powerpack is completely new functionality. I mistakenly thought that all custom remote cache solutions were using private/unsupported APIs, but I've since learned that we've had docs and some APIs that helped people build their own remote caches. I'm sorry for the error, and have updated the text to indicate that Powerpack is mostly new functionality. We've also updated the [Powerpack page](/powerpack) to make it more explicit how some teams can get free Powerpack licenses.
|
||||
|
||||
---
|
||||
|
||||
Over the years, Nx has grown from a small 20% side project of our consulting business into a tool that empowers millions of developers worldwide and helps Fortune 500 companies ship high-quality software faster. In the last two years, we successfully transformed our consulting business into a product company, where our team can fully focus on evolving Nx and building Nx Cloud to extend Nx’s capabilities beyond local development.
|
||||
|
||||
This success is in large part thanks to:
|
||||
@@ -21,7 +27,7 @@ When we have new ideas to make Nx better, we’ve always had two options: it cou
|
||||
|
||||
> If you want to get into the technical details, we wrote a separate blog post diving deeper into the technical details: [Introducing Nx Powerpack](/blog/introducing-nx-powerpack).
|
||||
|
||||
Everything in Powerpack is new functionality, not previously free features that we’re now putting behind a paywall. However, this change coincides with some Nx improvements that will eventually interfere with users who were relying on our original filesystem-based implementation of local caching. We’ve completely rewritten Nx's local caching to be faster and more secure, partly by using a local database instead of checking the filesystem for artifact metadata. With this rewrite, any custom remote caches that rely on metadata reflected in the filesystem will not work as of Nx 21. This is why we decided to build an API into Powerpack to be able to connect Nx’s cache to different clouds and data sources. Now with Powerpack, teams can use an officially-supported implementation of remote caching, without needing to use Nx Cloud.
|
||||
Powerpack is mostly new functionality. However, this change coincides with some Nx improvements that will eventually interfere with users who were relying on our original filesystem-based implementation of local caching. We’ve completely rewritten Nx's local caching to be faster and more secure, partly by using a local database instead of checking the filesystem for artifact metadata. With this rewrite, any custom remote caches that rely on metadata reflected in the filesystem will not work as of Nx 21. This is why we decided to build an API into Powerpack to be able to connect Nx’s cache to different clouds and data sources. Now with Powerpack, teams can use an officially-supported implementation of remote caching, without needing to use Nx Cloud.
|
||||
|
||||
There’s a Steve Jobs quote that I think rings true with all of us at Nx:
|
||||
|
||||
|
||||
@@ -6,7 +6,15 @@ tags: [nx, release]
|
||||
cover_image: /blog/images/2024-10-03/nx-20-header.png
|
||||
---
|
||||
|
||||
I know it's hard to believe but Nx 20 is here! There's a lot to cover in this release, so let's just dive right in.
|
||||
I know it's hard to believe but Nx 20 is here! There's a lot of great updates in this release, but look back at some of the major features from Nx 19:
|
||||
|
||||
- Added [`nx import`][nx-import] to import existing external projects while maintaining git history.
|
||||
- Added a [generator][crystalize-command] to convert all your Nx Plugins to use inferred tasks.
|
||||
- Moved to [rspack][nx-rspack] as the default bundler for Module Federation
|
||||
- Added [file-based versioning][file-based-versioning] for Nx Release
|
||||
- Created a new [plugin for Gradle][nx-gradle] and support for [task atomization][gradle-atomizer]
|
||||
|
||||
With Nx 20 we're keeping the momentum going and bringing some quality improvements to the overall monorepo experience as well as a boost in speed for TypeScript projects.
|
||||
|
||||
## `@nx` All The Things!
|
||||
|
||||
@@ -73,3 +81,9 @@ There's a lot more in Nx 20, so be sure to check the full changelog for all the
|
||||
[rspack]: https://rspack.dev
|
||||
[rescope]: /deprecated/rescope
|
||||
[colum]: https://x.com/FerryColum
|
||||
[gradle-atomizer]: /blog/nx-19-5-adds-stackblitz-new-features-and-more#experimental-gradle-test-atomization
|
||||
[nx-gradle]: /blog/nx-19-release#new-plugin-nxgradle
|
||||
[file-based-versioning]: /recipes/nx-release/file-based-versioning-version-plans
|
||||
[nx-rspack]: /nx-api/rspack
|
||||
[crystalize-command]: /blog/nx-19-8-update#crystalize-your-entire-workspace-in-one-command
|
||||
[nx-import]: /nx-api/nx/documents/import
|
||||
|
||||
@@ -48,6 +48,35 @@ There are four different ways to authenticate with AWS. They will be attempted i
|
||||
|
||||
Both the `AWS_ACCESS_KEY_ID` and the `AWS_SECRET_ACCESS_KEY` environment variables are required to use the environment variable authentication method.
|
||||
|
||||
Here's an example of using OICD in GitHub Actions to set the environment variables in CI:
|
||||
|
||||
```yaml {% fileName=".github/workflows/ci.yml" %}
|
||||
name: CI
|
||||
...
|
||||
permissions:
|
||||
id-token: write
|
||||
...
|
||||
|
||||
env:
|
||||
NX_DB_CACHE: true
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
...
|
||||
|
||||
- name: 'Configure AWS Credentials'
|
||||
uses: aws-actions/configure-aws-credentials@v4.0.2
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::123456789123:role/GhAIBucketUserRole
|
||||
aws-region: us-east-1
|
||||
|
||||
...
|
||||
|
||||
- run: pnpm exec nx affected -t lint test build
|
||||
```
|
||||
|
||||
#### INI Config Files
|
||||
|
||||
AWS can read your authentication credentials from [shared INI config files](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html). The files are located at `~/.aws/credentials` and `~/.aws/config`. Both files are expected to be INI formatted with section names corresponding to profiles. Sections in the credentials file are treated as profile names, whereas profile sections in the config file must have the format of `[profile profile-name]`, except for the default profile. Profiles that appear in both files will not be merged, and the version that appears in the credentials file will be given precedence over the profile found in the config file.
|
||||
|
||||
@@ -171,7 +171,7 @@ Named inputs targets can refer to reduce duplication
|
||||
|
||||
• `Optional` **neverConnectToCloud**: `boolean`
|
||||
|
||||
Set this to false to disable connection to Nx Cloud
|
||||
Set this to true to disable connection to Nx Cloud
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ Named inputs targets can refer to reduce duplication
|
||||
|
||||
• `Optional` **neverConnectToCloud**: `boolean`
|
||||
|
||||
Set this to false to disable connection to Nx Cloud
|
||||
Set this to true to disable connection to Nx Cloud
|
||||
|
||||
#### Inherited from
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ npm add -D @nx/rspack
|
||||
|
||||
### Generate a new project using Rspack
|
||||
|
||||
You can generate a [React](/nx-api/react) application or library that uses Rspack. The [`@nx/react:app`](/nx-api/react/generators/application) and [`@nx/react:lib`](/nx-api/react/generators/library) generators accept the `bundler` option, where you can pass `rspack`. This will generate a new application configured to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin.
|
||||
You can generate a [React](/nx-api/react) application that uses Rspack. The [`@nx/react:app`](/nx-api/react/generators/application) generator accepts the `bundler` option, where you can pass `rspack`. This will generate a new application configured to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin.
|
||||
|
||||
To generate a React application using Rspack, run the following:
|
||||
|
||||
@@ -85,12 +85,6 @@ To generate a React application using Rspack, run the following:
|
||||
nx g @nx/react:app my-app --bundler=rspack
|
||||
```
|
||||
|
||||
To generate a React library using Rspack, run the following:
|
||||
|
||||
```bash
|
||||
nx g @nx/react:lib my-lib --bundler=rspack
|
||||
```
|
||||
|
||||
### Modify an existing React project to use Rspack
|
||||
|
||||
You can use the `@nx/rspack:configuration` generator to change your React to use Rspack. This generator will modify your project's configuration to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin.
|
||||
|
||||
@@ -165,11 +165,11 @@ Along with all the UI changes to support agents (following their logs and track
|
||||
|
||||
When upgrading to this version and anything above it, you will need to use Helm version 0.12.0+:
|
||||
|
||||
| Chart Version | Compatible Images |
|
||||
| ------------- | ---------------------------------- |
|
||||
| Chart Version | Compatible Images |
|
||||
| :-----------: | :--------------------------------: |
|
||||
| <= `0.10.11` | `2306.01.2.patch4` **and earlier** |
|
||||
| >= `0.11.0` | `2308.22.7` **and later** |
|
||||
| >= `0.12.0` | `2312.11.7` **and later** |
|
||||
| >= `0.11.0` | `2308.22.7` **and later** |
|
||||
| >= `0.12.0` | `2312.11.7` **and later** |
|
||||
|
||||
##### New UI features and improvements
|
||||
|
||||
@@ -268,10 +268,10 @@ product. It's faster, it handles resource caching better, and should allow the f
|
||||
|
||||
When upgrading to this version and anything above it, you will need to use Helm version 0.11.1:
|
||||
|
||||
| Chart Version | Compatible Images |
|
||||
| ------------- | ---------------------------------- |
|
||||
| Chart Version | Compatible Images |
|
||||
| :-----------: | :--------------------------------: |
|
||||
| <= `0.10.11` | `2306.01.2.patch4` **and earlier** |
|
||||
| >= `0.11.0` | `2308.22.7` **and later** |
|
||||
| >= `0.11.0` | `2308.22.7` **and later** |
|
||||
|
||||
##### VCS proxy support
|
||||
|
||||
|
||||
@@ -48,6 +48,35 @@ There are four different ways to authenticate with AWS. They will be attempted i
|
||||
|
||||
Both the `AWS_ACCESS_KEY_ID` and the `AWS_SECRET_ACCESS_KEY` environment variables are required to use the environment variable authentication method.
|
||||
|
||||
Here's an example of using OICD in GitHub Actions to set the environment variables in CI:
|
||||
|
||||
```yaml {% fileName=".github/workflows/ci.yml" %}
|
||||
name: CI
|
||||
...
|
||||
permissions:
|
||||
id-token: write
|
||||
...
|
||||
|
||||
env:
|
||||
NX_DB_CACHE: true
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
...
|
||||
|
||||
- name: 'Configure AWS Credentials'
|
||||
uses: aws-actions/configure-aws-credentials@v4.0.2
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::123456789123:role/GhAIBucketUserRole
|
||||
aws-region: us-east-1
|
||||
|
||||
...
|
||||
|
||||
- run: pnpm exec nx affected -t lint test build
|
||||
```
|
||||
|
||||
#### INI Config Files
|
||||
|
||||
AWS can read your authentication credentials from [shared INI config files](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html). The files are located at `~/.aws/credentials` and `~/.aws/config`. Both files are expected to be INI formatted with section names corresponding to profiles. Sections in the credentials file are treated as profile names, whereas profile sections in the config file must have the format of `[profile profile-name]`, except for the default profile. Profiles that appear in both files will not be merged, and the version that appears in the credentials file will be given precedence over the profile found in the config file.
|
||||
|
||||
@@ -77,7 +77,7 @@ npm add -D @nx/rspack
|
||||
|
||||
### Generate a new project using Rspack
|
||||
|
||||
You can generate a [React](/nx-api/react) application or library that uses Rspack. The [`@nx/react:app`](/nx-api/react/generators/application) and [`@nx/react:lib`](/nx-api/react/generators/library) generators accept the `bundler` option, where you can pass `rspack`. This will generate a new application configured to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin.
|
||||
You can generate a [React](/nx-api/react) application that uses Rspack. The [`@nx/react:app`](/nx-api/react/generators/application) generator accepts the `bundler` option, where you can pass `rspack`. This will generate a new application configured to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin.
|
||||
|
||||
To generate a React application using Rspack, run the following:
|
||||
|
||||
@@ -85,12 +85,6 @@ To generate a React application using Rspack, run the following:
|
||||
nx g @nx/react:app my-app --bundler=rspack
|
||||
```
|
||||
|
||||
To generate a React library using Rspack, run the following:
|
||||
|
||||
```bash
|
||||
nx g @nx/react:lib my-lib --bundler=rspack
|
||||
```
|
||||
|
||||
### Modify an existing React project to use Rspack
|
||||
|
||||
You can use the `@nx/rspack:configuration` generator to change your React to use Rspack. This generator will modify your project's configuration to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin.
|
||||
|
||||
@@ -10,7 +10,7 @@ Activating Powerpack is a two step process.
|
||||
|
||||
## 1. Purchase a License
|
||||
|
||||
You'll need to [purchase a license](https://cloud.nx.app/powerpack/purchase) online. The license is a seat-based license. If you have an open-source repository, reach out to [powerpack-support@nrwl.io](mailto:powerpack-support@nrwl.io) for a free license.
|
||||
You'll need to [purchase a license](https://cloud.nx.app/powerpack/purchase) online. The license is a seat-based license. If you are a small team, the purchase form has an option to apply for a free license. If you have an open-source repository, you can [register for a free OSS license](https://forms.gle/mWjQo6Vrv5Kt6WYh9) as well.
|
||||
|
||||
{% call-to-action title="Buy a Powerpack License" icon="nx" description="Unlock all the features of Nx" url="https://cloud.nx.app/powerpack/purchase" /%}
|
||||
|
||||
|
||||
@@ -2,38 +2,36 @@
|
||||
|
||||
The following environment variables are ones that you can set to change the behavior of Nx in different environments.
|
||||
|
||||
| Property | Type | Description |
|
||||
| --------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| NX_ADD_PLUGINS | boolean | If set to `false`, Nx will not add plugins to infer tasks. This is `true` by default. Workspaces created before Nx 18 will have this disabled via a migration for backwards compatibility |
|
||||
| NX_ADD_TS_PLUGIN | boolean | If set to `false` when creating a new workspace using the `ts` preset, Nx will not add the `@nx/js/typescript` plugin to infer tasks and will not set up the workspace with [TypeScript project references](https://www.typescriptlang.org/docs/handbook/project-references.html). This is `true` by default. |
|
||||
| NX_BASE | string | The default base branch to use when calculating the affected projects. Can be overridden on the command line with `--base`. |
|
||||
| NX_CACHE_DIRECTORY | string | The cache for task outputs is stored in `.nx/cache` by default. Set this variable to use a different directory. |
|
||||
| NX_CACHE_PROJECT_GRAPH | boolean | If set to `false`, disables the project graph cache. Most useful when developing a plugin that modifies the project graph. |
|
||||
| NX_DAEMON | boolean | If set to `false`, disables the Nx daemon process. Disable the daemon to print `console.log` statements in plugin code you are developing. |
|
||||
| NX_DEFAULT_PROJECT | string | The default project used for commands which require a project. e.g. `nx build`, `nx g component`, etc. |
|
||||
| NX_HEAD | string | The default head branch to use when calculating the affected projects. Can be overridden on the command line with `--head`. |
|
||||
| NX_PERF_LOGGING | boolean | If set to `true`, will print debug information useful for for profiling executors and Nx itself |
|
||||
| NX_PROFILE | string | Prepend `NX_PROFILE=profile.json` before running targets with Nx to generate a file that be [loaded in Chrome dev tools](/troubleshooting/performance-profiling) to visualize the performance of Nx across multiple processes. |
|
||||
| NX_WORKSPACE_DATA_CACHE_DIRECTORY | string | The project graph cache and some other internal nx caches are stored in `.nx/workspace-data` by default. Set this variable to use a different directory. |
|
||||
| NX_PROJECT_GRAPH_MAX_WORKERS | number | The number of workers to use when calculating the project graph. |
|
||||
| NX_PARALLEL | number | The number of tasks Nx should run in parallel. Overrides any configured value inside nx.json |
|
||||
| NX_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. Not read if `NX_TASKS_RUNNER` is set. |
|
||||
| NX_SKIP_NX_CACHE | boolean | Rerun the tasks even when the results are available in the cache |
|
||||
| NX_TASKS_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. Preferred over `NX_RUNNER`. |
|
||||
| NX_TASKS_RUNNER_DYNAMIC_OUTPUT | boolean | If set to `false`, will use non-dynamic terminal output strategy (what you see in CI), even when you terminal can support the dynamic version |
|
||||
| NX_VERBOSE_LOGGING | boolean | If set to `true`, will print debug information useful for troubleshooting |
|
||||
| NX_DRY_RUN | boolean | If set to `true`, will perform a dry run of the generator. No files will be created and no packages will be installed. |
|
||||
| NX_INTERACTIVE | boolean | If set to `true`, will allow Nx to prompt you in the terminal to answer some further questions when running generators. |
|
||||
| NX_GENERATE_QUIET | boolean | If set to `true`, will prevent Nx logging file operations during generate |
|
||||
| NX_PREFER_TS_NODE | boolean | If set to `true`, Nx will use `ts-node` for local execution of plugins even if `@swc-node/register` is installed. |
|
||||
| NX_IGNORE_CYCLES | boolean | If set to `true`, Nx will ignore errors created by a task graph circular dependency. Can be overriden on the command line with `--nxIgnoreCycles` |
|
||||
| NX_BATCH_MODE | boolean | If set to `true`, Nx will run task(s) in batches for executors which support batches. |
|
||||
| NX_SKIP_LOG_GROUPING | boolean | If set to `true`, Nx will not group command's logs on CI. |
|
||||
| NX_MIGRATE_CLI_VERSION | string | The version of Nx to use for running the `nx migrate` command. If not set, it defaults to `latest`. |
|
||||
| NX_LOAD_DOT_ENV_FILES | boolean | If set to 'false', Nx will not load any environment files (e.g. `.local.env`, `.env.local`) |
|
||||
| NX_NATIVE_FILE_CACHE_DIRECTORY | string | The cache for native `.node` files is stored under a global temp directory by default. Set this variable to use a different directory. This is interpreted as an absolute path. |
|
||||
| NX_PLUGIN_NO_TIMEOUTS | boolean | If set to `true`, plugin operations will not timeout |
|
||||
| NX_SOCKET_DIRECTORY | string | Sets the directory that Nx will use when creating sockets to communicate with child processes. May be needed if the derived socket path is too long. |
|
||||
| Property | Type | Description |
|
||||
| ------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| NX_ADD_PLUGINS | boolean | If set to `false`, Nx will not add plugins to infer tasks. This is `true` by default. Workspaces created before Nx 18 will have this disabled via a migration for backwards compatibility |
|
||||
| NX_ADD_TS_PLUGIN | boolean | If set to `false` when creating a new workspace using the `ts` preset, Nx will not add the `@nx/js/typescript` plugin to infer tasks and will not set up the workspace with [TypeScript project references](https://www.typescriptlang.org/docs/handbook/project-references.html). This is `true` by default. |
|
||||
| NX_BASE | string | The default base branch to use when calculating the affected projects. Can be overridden on the command line with `--base`. |
|
||||
| NX_CACHE_DIRECTORY | string | The cache for task outputs is stored in `.nx/cache` by default. Set this variable to use a different directory. |
|
||||
| NX_CACHE_PROJECT_GRAPH | boolean | If set to `false`, disables the project graph cache. Most useful when developing a plugin that modifies the project graph. |
|
||||
| NX_DAEMON | boolean | If set to `false`, disables the Nx daemon process. Disable the daemon to print `console.log` statements in plugin code you are developing. |
|
||||
| NX_DEFAULT_PROJECT | string | The default project used for commands which require a project. e.g. `nx build`, `nx g component`, etc. |
|
||||
| NX_HEAD | string | The default head branch to use when calculating the affected projects. Can be overridden on the command line with `--head`. |
|
||||
| NX_PERF_LOGGING | boolean | If set to `true`, will print debug information useful for for profiling executors and Nx itself |
|
||||
| NX_PROFILE | string | Prepend `NX_PROFILE=profile.json` before running targets with Nx to generate a file that be [loaded in Chrome dev tools](/troubleshooting/performance-profiling) to visualize the performance of Nx across multiple processes. |
|
||||
| NX_WORKSPACE_DATA_DIRECTORY | string | The project graph cache and some other internal nx caches are stored in `.nx/workspace-data` by default. Set this variable to use a different directory. |
|
||||
| NX_PARALLEL | number | The number of tasks Nx should run in parallel. Overrides any configured value inside nx.json |
|
||||
| NX_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. Not read if `NX_TASKS_RUNNER` is set. |
|
||||
| NX_SKIP_NX_CACHE | boolean | Rerun the tasks even when the results are available in the cache |
|
||||
| NX_TASKS_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. Preferred over `NX_RUNNER`. |
|
||||
| NX_TASKS_RUNNER_DYNAMIC_OUTPUT | boolean | If set to `false`, will use non-dynamic terminal output strategy (what you see in CI), even when you terminal can support the dynamic version |
|
||||
| NX_VERBOSE_LOGGING | boolean | If set to `true`, will print debug information useful for troubleshooting |
|
||||
| NX_DRY_RUN | boolean | If set to `true`, will perform a dry run of the generator. No files will be created and no packages will be installed. |
|
||||
| NX_INTERACTIVE | boolean | If set to `true`, will allow Nx to prompt you in the terminal to answer some further questions when running generators. |
|
||||
| NX_GENERATE_QUIET | boolean | If set to `true`, will prevent Nx logging file operations during generate |
|
||||
| NX_PREFER_TS_NODE | boolean | If set to `true`, Nx will use `ts-node` for local execution of plugins even if `@swc-node/register` is installed. |
|
||||
| NX_IGNORE_CYCLES | boolean | If set to `true`, Nx will ignore errors created by a task graph circular dependency. Can be overridden on the command line with `--nxIgnoreCycles` |
|
||||
| NX_BATCH_MODE | boolean | If set to `true`, Nx will run task(s) in batches for executors which support batches. |
|
||||
| NX_SKIP_LOG_GROUPING | boolean | If set to `true`, Nx will not group command's logs on CI. |
|
||||
| NX_MIGRATE_CLI_VERSION | string | The version of Nx to use for running the `nx migrate` command. If not set, it defaults to `latest`. |
|
||||
| NX_LOAD_DOT_ENV_FILES | boolean | If set to 'false', Nx will not load any environment files (e.g. `.local.env`, `.env.local`) |
|
||||
| NX_NATIVE_FILE_CACHE_DIRECTORY | string | The cache for native `.node` files is stored under a global temp directory by default. Set this variable to use a different directory. This is interpreted as an absolute path. |
|
||||
| NX_PLUGIN_NO_TIMEOUTS | boolean | If set to `true`, plugin operations will not timeout |
|
||||
|
||||
Nx will set the following environment variables so they can be accessible within the process even outside of executors and generators.
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ should use `nx migrate` to ensure that they stay on a supported version.
|
||||
The following are the currently supported major versions of Nx.
|
||||
|
||||
| Version | Support Type | Release Date |
|
||||
| ------- | ------------ | ------------ |
|
||||
| v19 | Current | 2024-05-06 |
|
||||
| v18\* | LTS | 2024-02-03 |
|
||||
| v17 | LTS | 2023-10-19 |
|
||||
| v16 | LTS | 2023-04-27 |
|
||||
| :-----: | :----------: | :----------: |
|
||||
| v20 | Current | 2024-10-06 |
|
||||
| v19 | LTS | 2024-05-06 |
|
||||
| v18\* | LTS | 2024-02-03 |
|
||||
| v17 | LTS | 2023-10-19 |
|
||||
|
||||
**\*Note:** v18 is a special release and does not fit into the normal release cycle. Thus, v16 continues to be supported
|
||||
according to schedule.
|
||||
|
||||
@@ -273,7 +273,7 @@ export function runCommandUntil(
|
||||
...opts.env,
|
||||
FORCE_COLOR: 'false',
|
||||
},
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
return new Promise((res, rej) => {
|
||||
let output = '';
|
||||
|
||||
@@ -65,7 +65,7 @@ function getPublishedVersion(): Promise<string | undefined> {
|
||||
exec(
|
||||
'npm view nx@latest version',
|
||||
{
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
},
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useCallback } from 'react';
|
||||
import { ProjectDetails } from '@nx/graph-internal/ui-project-details';
|
||||
import {
|
||||
ErrorToastUI,
|
||||
ExpandedTargetsProvider,
|
||||
getExternalApiService,
|
||||
} from '@nx/graph/shared';
|
||||
import { useMachine, useSelector } from '@xstate/react';
|
||||
import { ProjectDetails } from '@nx/graph-internal/ui-project-details';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useCallback } from 'react';
|
||||
import { Interpreter } from 'xstate';
|
||||
import {
|
||||
ProjectDetailsEvents,
|
||||
ProjectDetailsState,
|
||||
} from './project-details.machine';
|
||||
import { Interpreter } from 'xstate';
|
||||
|
||||
export function ProjectDetailsApp({
|
||||
service,
|
||||
@@ -26,6 +26,10 @@ export function ProjectDetailsApp({
|
||||
service,
|
||||
(state) => state.context.connectedToCloud
|
||||
);
|
||||
const disabledTaskSyncGenerators = useSelector(
|
||||
service,
|
||||
(state) => state.context.disabledTaskSyncGenerators
|
||||
);
|
||||
|
||||
const handleViewInProjectGraph = useCallback(
|
||||
(data: { projectName: string }) => {
|
||||
@@ -83,6 +87,7 @@ export function ProjectDetailsApp({
|
||||
viewInProjectGraphPosition="bottom"
|
||||
connectedToCloud={connectedToCloud}
|
||||
onNxConnect={handleNxConnect}
|
||||
disabledTaskSyncGenerators={disabledTaskSyncGenerators}
|
||||
/>
|
||||
</ExpandedTargetsProvider>
|
||||
<ErrorToastUI errors={errors} />
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface ProjectDetailsState {
|
||||
sourceMap: null | Record<string, string[]>;
|
||||
errors?: GraphError[];
|
||||
connectedToCloud?: boolean;
|
||||
disabledTaskSyncGenerators?: string[];
|
||||
}
|
||||
|
||||
export type ProjectDetailsEvents = {
|
||||
@@ -20,6 +21,7 @@ export type ProjectDetailsEvents = {
|
||||
sourceMap: Record<string, string[]>;
|
||||
connectedToCloud: boolean;
|
||||
errors?: GraphError[];
|
||||
disabledTaskSyncGenerators?: string[];
|
||||
};
|
||||
|
||||
const initialContext: ProjectDetailsState = {
|
||||
@@ -50,6 +52,7 @@ export const projectDetailsMachine = createMachine<
|
||||
ctx.sourceMap = event.sourceMap;
|
||||
ctx.connectedToCloud = event.connectedToCloud;
|
||||
ctx.errors = event.errors;
|
||||
ctx.disabledTaskSyncGenerators = event.disabledTaskSyncGenerators;
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Metadata, Viewport } from 'next';
|
||||
import AppRouterAnalytics from './app-router-analytics';
|
||||
import GlobalScripts from './global-scripts';
|
||||
|
||||
import { LiveStreamNotifier } from '@nx/nx-dev/ui-common';
|
||||
import '../styles/main.css';
|
||||
|
||||
// Metadata for the entire site
|
||||
@@ -80,6 +80,7 @@ export default function RootLayout({
|
||||
</head>
|
||||
<body className="h-full bg-white text-slate-700 antialiased selection:bg-blue-500 selection:text-white dark:bg-slate-900 dark:text-slate-400 dark:selection:bg-sky-500">
|
||||
{children}
|
||||
<LiveStreamNotifier />
|
||||
<GlobalScripts gaMeasurementId={gaMeasurementId} />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,6 +7,7 @@ import Script from 'next/script';
|
||||
import { useEffect } from 'react';
|
||||
import '../styles/main.css';
|
||||
import Link from 'next/link';
|
||||
import { LiveStreamNotifier } from '@nx/nx-dev/ui-common';
|
||||
|
||||
export default function CustomApp({
|
||||
Component,
|
||||
@@ -79,6 +80,7 @@ export default function CustomApp({
|
||||
Skip to content
|
||||
</Link>
|
||||
<Component {...pageProps} />
|
||||
<LiveStreamNotifier />
|
||||
|
||||
{/* Global Site Tag (gtag.js) - Google Analytics */}
|
||||
<Script
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.9 KiB |
@@ -488,6 +488,7 @@ const nxCloudUrls = {
|
||||
'/ci/recipes/set-up/connect-to-cloud': '/ci/intro/connect-to-nx-cloud',
|
||||
'/ci/intro/connect-to-cloud': '/ci/intro/connect-to-nx-cloud',
|
||||
'/pricing/special-offer': 'https://forms.gle/FBzvsspz1o63fDAz6',
|
||||
'/powerpack/special-offer': 'https://forms.gle/mWjQo6Vrv5Kt6WYh9',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -862,6 +863,10 @@ const missingAndCatchAllRedirects = {
|
||||
'/packages/:path*': '/nx-api/:path*',
|
||||
};
|
||||
|
||||
const marketing = {
|
||||
'/conf': 'https://monorepo.world',
|
||||
};
|
||||
|
||||
const movePluginFeaturesToCore = {
|
||||
'/plugin-features/use-task-executors':
|
||||
'/concepts/executors-and-configurations',
|
||||
@@ -1171,4 +1176,5 @@ module.exports = {
|
||||
blogPosts,
|
||||
decisionsSection,
|
||||
featurePagesUpdate,
|
||||
marketing,
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ export * from './lib/discord-icon';
|
||||
export * from './lib/trusted-by';
|
||||
export * from './lib/testimonials';
|
||||
export * from './lib/square-dotted-pattern';
|
||||
export * from './lib/live-stream-notifier';
|
||||
|
||||
export { resourceMenuItems } from './lib/headers/menu-items';
|
||||
export { solutionsMenuItems } from './lib/headers/menu-items';
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
solutionsMenuItems,
|
||||
} from './menu-items';
|
||||
import { SectionsMenu } from './sections-menu';
|
||||
import { AnnouncementBanner } from '../announcement-banner';
|
||||
import { DiscordIcon } from '../discord-icon';
|
||||
import { NxCloudAnimatedIcon, NxIcon } from '@nx/nx-dev/ui-icons';
|
||||
|
||||
@@ -170,9 +169,6 @@ export function DocumentationHeader({
|
||||
|
||||
return (
|
||||
<div className="border-b border-slate-200 bg-slate-50 dark:border-slate-700 dark:bg-slate-800/60 print:hidden">
|
||||
<div className="hidden w-full md:block">
|
||||
<AnnouncementBanner />
|
||||
</div>
|
||||
<div className="mx-auto flex w-full items-center gap-6 lg:px-8 lg:py-4">
|
||||
{/*MOBILE MENU*/}
|
||||
<div className="flex w-full items-center lg:hidden">
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, ReactElement } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { MonorepoWorldIcon } from '@nx/nx-dev/ui-icons';
|
||||
import { ButtonLink } from './button';
|
||||
import {
|
||||
PlayIcon,
|
||||
XMarkIcon,
|
||||
ChatBubbleLeftRightIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
|
||||
export function LiveStreamNotifier(): ReactElement | null {
|
||||
const [isVisible, setIsVisible] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
const isClosedSession = sessionStorage.getItem(
|
||||
'live-stream-notifier-closed'
|
||||
);
|
||||
if (isClosedSession === 'true') {
|
||||
setIsVisible(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const closeNotifier = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
setIsVisible(false);
|
||||
sessionStorage.setItem('live-stream-notifier-closed', 'true');
|
||||
};
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
layout
|
||||
initial={{ y: '120%' }}
|
||||
animate={{ y: 0 }}
|
||||
exit={{ y: '120%' }}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 300,
|
||||
damping: 30,
|
||||
mass: 1,
|
||||
}}
|
||||
className="fixed bottom-0 left-0 right-0 z-30 w-full overflow-hidden bg-slate-950 text-white shadow-lg md:bottom-4 md:left-auto md:right-4 md:w-[512px] md:rounded-lg"
|
||||
style={{ originY: 1 }}
|
||||
>
|
||||
<div className="relative p-4">
|
||||
<button
|
||||
onClick={closeNotifier}
|
||||
className="absolute right-2 top-2 rounded-full p-1 hover:bg-slate-800 focus:outline-none focus:ring-2 focus:ring-white"
|
||||
>
|
||||
<XMarkIcon className="size-5" aria-hidden="true" />
|
||||
<span className="sr-only">Close</span>
|
||||
</button>
|
||||
<div>
|
||||
<motion.h3
|
||||
layout="position"
|
||||
className="flex items-center gap-2 pr-8 text-lg font-semibold"
|
||||
>
|
||||
<MonorepoWorldIcon
|
||||
aria-hidden="true"
|
||||
className="size-8 flex-shrink-0"
|
||||
/>
|
||||
<span>Monorepo World live replays available!</span>
|
||||
</motion.h3>
|
||||
<motion.div key="live-event" className="mt-4 space-y-4">
|
||||
<p className="mb-2 text-sm">
|
||||
Watch the replays of exciting talks on developer tooling and
|
||||
monorepos! Catch all the insightful presentations from the event
|
||||
on our YouTube channel.
|
||||
</p>
|
||||
<div className="flex flex-wrap items-center gap-1 sm:gap-4">
|
||||
<a
|
||||
title="Watch track 1"
|
||||
href="http://go.nx.dev/MWTrack1"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-[#DDFB24] px-2 py-2 text-sm font-semibold text-black transition hover:bg-[#B2CF04] focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 active:text-black/70 md:px-4"
|
||||
>
|
||||
<PlayIcon aria-hidden="true" className="size-4" />
|
||||
<span>Track 1</span>
|
||||
</a>
|
||||
<a
|
||||
href="http://go.nx.dev/MWTrack2"
|
||||
target="_blank"
|
||||
title="Watch track 2"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-[#DDFB24] px-2 py-2 text-sm font-semibold text-black transition hover:bg-[#B2CF04] focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 active:text-black/70 md:px-4"
|
||||
>
|
||||
<PlayIcon aria-hidden="true" className="size-4" />
|
||||
<span>Track 2</span>
|
||||
</a>
|
||||
<ButtonLink
|
||||
variant="secondary"
|
||||
size="small"
|
||||
href="https://discord.gg/7yFabzBP"
|
||||
target="_blank"
|
||||
title="Join the discussion on Discord"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<ChatBubbleLeftRightIcon
|
||||
aria-hidden="true"
|
||||
className="size-4"
|
||||
/>
|
||||
<span>#monorepo-world</span>
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,11 @@ import { Theme, useTheme } from '@nx/nx-dev/ui-theme';
|
||||
import { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useIsomorphicLayoutEffect } from '@nx/nx-dev/ui-primitives';
|
||||
import { MonorepoWorldIcon } from '@nx/nx-dev/ui-icons';
|
||||
import {
|
||||
MonorepoWorldIcon,
|
||||
RustIcon,
|
||||
TypeScriptIcon,
|
||||
} from '@nx/nx-dev/ui-icons';
|
||||
import { motion, MotionConfig } from 'framer-motion';
|
||||
|
||||
export function Hero(): JSX.Element {
|
||||
@@ -80,57 +84,13 @@ export function Hero(): JSX.Element {
|
||||
Contact us
|
||||
</ButtonLink>
|
||||
</div>
|
||||
<div className="mt-12 flex justify-center">
|
||||
<MotionConfig reducedMotion="user">
|
||||
<motion.div
|
||||
initial={{
|
||||
y: 16,
|
||||
opacity: 0,
|
||||
}}
|
||||
whileInView={{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
}}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
ease: 'easeOut',
|
||||
duration: 0.225,
|
||||
delay: 1,
|
||||
}}
|
||||
className="pointer-events-auto relative mx-auto w-full max-w-lg overflow-hidden rounded-lg bg-slate-950 text-left text-white shadow-lg ring-1 ring-white ring-opacity-5 transition hover:bg-slate-800"
|
||||
>
|
||||
<div className="p-4">
|
||||
<div className="flex items-start gap-6">
|
||||
<div className="-m-4 hidden flex-shrink-0 sm:block">
|
||||
<img
|
||||
src="/images/conferences/monorepoworld-vertical-banner.avif"
|
||||
alt="MonorepoWorld conference image"
|
||||
className="size-32"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-0 flex-1 pt-1">
|
||||
<p className="text-base font-semibold">
|
||||
<a
|
||||
href="https://monorepo.world/?utm_source=nxdev&utm_medium=website&utm_campaign=homepage_banner&utm_campaign=monorepoworld"
|
||||
title="Join us at Monorepo World"
|
||||
target="_blank"
|
||||
>
|
||||
<span className="absolute inset-0" />
|
||||
The Nx team will be at Monorepo World!
|
||||
</a>
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-slate-300">
|
||||
The ultimate conference for{' '}
|
||||
<span className="text-[#DDFB24]">monorepos</span> and
|
||||
developer{' '}
|
||||
<span className="text-[#DDFB24]">tooling</span> on 0ct.
|
||||
7. 2024, Mountain View, CA.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</MotionConfig>
|
||||
|
||||
<div className="mt-4 flex items-center justify-center gap-2 text-sm italic">
|
||||
Built with
|
||||
<RustIcon aria-hidden="true" className="size-5 shrink-0" />
|
||||
<span className="sr-only">Rust</span> for speed &
|
||||
<TypeScriptIcon aria-hidden="true" className="size-4 shrink-0" />
|
||||
<span className="sr-only">TypeScript</span> for extensibility.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,6 @@ import { pill } from './lib/tags/pill.schema';
|
||||
import { fence } from './lib/nodes/fence.schema';
|
||||
import { FenceWrapper } from './lib/nodes/fence-wrapper.component';
|
||||
import { VideoPlayer, videoPlayer } from './lib/tags/video-player.component';
|
||||
import { td } from './lib/nodes/td.schema';
|
||||
// TODO fix this export
|
||||
export { GithubRepository } from './lib/tags/github-repository.component';
|
||||
|
||||
@@ -70,7 +69,6 @@ export const getMarkdocCustomConfig = (
|
||||
heading: getHeadingSchema(headingClass),
|
||||
image: getImageSchema(documentFilePath),
|
||||
link,
|
||||
td,
|
||||
},
|
||||
tags: {
|
||||
callout,
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Schema } from '@markdoc/markdoc';
|
||||
|
||||
export const td: Schema = {
|
||||
render: 'td',
|
||||
attributes: {
|
||||
className: { type: 'String', default: 'text-center' },
|
||||
},
|
||||
};
|
||||
@@ -23,8 +23,11 @@ export function Tabs({
|
||||
const [currentTab, setCurrentTab] = useState<string>(labels[0]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleTabSelectedEvent = (defaultTab?: string) => {
|
||||
const handleTabSelectedEvent = (
|
||||
tabSelectedEvent: CustomEvent<{ defaultTab?: string }>
|
||||
) => {
|
||||
const selectedTab = localStorage.getItem(SELECTED_TAB_KEY);
|
||||
const defaultTab = tabSelectedEvent.detail.defaultTab;
|
||||
if (selectedTab && labels.includes(selectedTab)) {
|
||||
setCurrentTab(selectedTab);
|
||||
} else if (defaultTab) {
|
||||
@@ -32,15 +35,23 @@ export function Tabs({
|
||||
}
|
||||
};
|
||||
|
||||
handleTabSelectedEvent(labels[0]);
|
||||
window.addEventListener(TAB_SELECTED_EVENT, handleTabSelectedEvent);
|
||||
handleTabSelectedEvent(
|
||||
new CustomEvent(TAB_SELECTED_EVENT, { detail: { defaultTab: labels[0] } })
|
||||
);
|
||||
window.addEventListener(
|
||||
TAB_SELECTED_EVENT,
|
||||
handleTabSelectedEvent as EventListener
|
||||
);
|
||||
return () =>
|
||||
window.removeEventListener(TAB_SELECTED_EVENT, handleTabSelectedEvent);
|
||||
window.removeEventListener(
|
||||
TAB_SELECTED_EVENT,
|
||||
handleTabSelectedEvent as EventListener
|
||||
);
|
||||
}, [labels]);
|
||||
|
||||
const handleTabClick = (label: string) => {
|
||||
localStorage.setItem(SELECTED_TAB_KEY, label);
|
||||
window.dispatchEvent(new Event(TAB_SELECTED_EVENT));
|
||||
window.dispatchEvent(new CustomEvent(TAB_SELECTED_EVENT, { detail: {} }));
|
||||
setCurrentTab(label);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { ButtonLink, SectionHeading, Strong } from '@nx/nx-dev/ui-common';
|
||||
import { ReactElement, useState, useEffect } from 'react';
|
||||
import { Dialog, Transition } from '@headlessui/react';
|
||||
import Link from 'next/link';
|
||||
|
||||
const MOBILE_BREAKPOINT = 768;
|
||||
const YOUTUBE_URL = 'https://youtu.be/KZ0nh2lj8zE?si=D1hkyP3vy36e-VZt';
|
||||
@@ -60,6 +61,17 @@ export function Hero(): ReactElement {
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<p className="mt-6 text-sm italic">
|
||||
Want to use Powerpack for OSS?
|
||||
<br />
|
||||
<Link
|
||||
href="/powerpack/special-offer"
|
||||
prefetch={false}
|
||||
className="font-semibold underline"
|
||||
>
|
||||
Apply for a free OSS license.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{!isMobile && (
|
||||
|
||||
@@ -8,7 +8,12 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { ButtonLink, SectionHeading, Strong } from '@nx/nx-dev/ui-common';
|
||||
import {
|
||||
ButtonLink,
|
||||
SectionHeading,
|
||||
Strong,
|
||||
TextLink,
|
||||
} from '@nx/nx-dev/ui-common';
|
||||
import { cx } from '@nx/nx-dev/ui-primitives';
|
||||
import { AnimatedCurvedBeam } from '@nx/nx-dev/ui-animations';
|
||||
import { CircleStackIcon, ServerIcon } from '@heroicons/react/24/outline';
|
||||
@@ -35,7 +40,14 @@ export function PowerpackFeatures(): ReactElement {
|
||||
Nx Powerpack enables you to use <Strong>Amazon S3</Strong> or a{' '}
|
||||
<Strong>shared network drive</Strong> as your remote cache
|
||||
storage, offering a flexible, self-managed solution for faster
|
||||
builds.
|
||||
builds. Nx Powerpack self-hosted cache storage is{' '}
|
||||
<TextLink
|
||||
href="https://cloud.nx.app/powerpack/purchase?utm_source=nx.dev&utm_medium=referral&utm_campaign=nx-powerpackurl"
|
||||
title="Get a Powerpack license"
|
||||
>
|
||||
free for small existing teams
|
||||
</TextLink>
|
||||
.
|
||||
</p>
|
||||
<div className="mt-16">
|
||||
<ButtonLink
|
||||
|
||||
@@ -1127,6 +1127,10 @@
|
||||
"ng-packagr": {
|
||||
"version": "~18.2.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"zone.js": {
|
||||
"version": "~0.14.10",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+6
-4
@@ -128,10 +128,12 @@ export async function* moduleFederationDevServerExecutor(
|
||||
|
||||
// Set NX_MF_DEV_REMOTES for the Nx Runtime Library Control Plugin
|
||||
process.env.NX_MF_DEV_REMOTES = JSON.stringify([
|
||||
...(remotes.devRemotes.map((r) =>
|
||||
typeof r === 'string' ? r : r.remoteName
|
||||
) ?? []),
|
||||
project.name,
|
||||
...(
|
||||
remotes.devRemotes.map((r) =>
|
||||
typeof r === 'string' ? r : r.remoteName
|
||||
) ?? []
|
||||
).map((r) => r.replace(/-/g, '_')),
|
||||
project.name.replace(/-/g, '_'),
|
||||
]);
|
||||
|
||||
const staticRemotesConfig = parseStaticRemotesConfig(
|
||||
|
||||
+6
-2
@@ -124,8 +124,12 @@ export async function* moduleFederationSsrDevServerExecutor(
|
||||
|
||||
// Set NX_MF_DEV_REMOTES for the Nx Runtime Library Control Plugin
|
||||
process.env.NX_MF_DEV_REMOTES = JSON.stringify([
|
||||
...(options.devRemotes ?? []),
|
||||
project.name,
|
||||
...(
|
||||
options.devRemotes.map((r) =>
|
||||
typeof r === 'string' ? r : r.remoteName
|
||||
) ?? []
|
||||
).map((r) => r.replace(/-/g, '_')),
|
||||
project.name.replace(/-/g, '_'),
|
||||
]);
|
||||
|
||||
const devRemotes = await startRemotes(
|
||||
|
||||
@@ -12,9 +12,9 @@ import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
|
||||
describe('federate-module', () => {
|
||||
let schema: Schema = {
|
||||
name: 'my-federated-module',
|
||||
remote: 'my-remote',
|
||||
path: 'apps/my-remote/src/my-federated-module.ts',
|
||||
remoteDirectory: 'apps/my-remote',
|
||||
remote: 'myremote',
|
||||
path: 'apps/myremote/src/my-federated-module.ts',
|
||||
remoteDirectory: 'apps/myremote',
|
||||
};
|
||||
|
||||
describe('no remote', () => {
|
||||
@@ -29,32 +29,32 @@ describe('federate-module', () => {
|
||||
|
||||
const projects = getProjects(tree);
|
||||
|
||||
expect(projects.get('my-remote').root).toEqual('apps/my-remote');
|
||||
expect(projects.get('myremote').root).toEqual('apps/myremote');
|
||||
|
||||
expect(tree.exists('apps/my-remote/module-federation.config.ts')).toBe(
|
||||
expect(tree.exists('apps/myremote/module-federation.config.ts')).toBe(
|
||||
true
|
||||
);
|
||||
|
||||
const content = tree.read(
|
||||
'apps/my-remote/module-federation.config.ts',
|
||||
'apps/myremote/module-federation.config.ts',
|
||||
'utf-8'
|
||||
);
|
||||
expect(content).toContain(
|
||||
`'./my-federated-module': 'apps/my-remote/src/my-federated-module.ts'`
|
||||
`'./my-federated-module': 'apps/myremote/src/my-federated-module.ts'`
|
||||
);
|
||||
|
||||
const tsconfig = JSON.parse(tree.read('tsconfig.base.json', 'utf-8'));
|
||||
expect(
|
||||
tsconfig.compilerOptions.paths['my-remote/my-federated-module']
|
||||
).toEqual(['apps/my-remote/src/my-federated-module.ts']);
|
||||
tsconfig.compilerOptions.paths['myremote/my-federated-module']
|
||||
).toEqual(['apps/myremote/src/my-federated-module.ts']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with remote', () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
let remoteSchema: remoteSchma = {
|
||||
name: 'my-remote',
|
||||
directory: 'my-remote',
|
||||
name: 'myremote',
|
||||
directory: 'myremote',
|
||||
e2eTestRunner: E2eTestRunner.Cypress,
|
||||
skipFormat: true,
|
||||
linter: Linter.EsLint,
|
||||
@@ -78,7 +78,7 @@ describe('federate-module', () => {
|
||||
);
|
||||
|
||||
expect(content).not.toContain(
|
||||
`'./my-federated-module': 'apps/my-remote/src/my-federated-module.ts'`
|
||||
`'./my-federated-module': 'apps/myremote/src/my-federated-module.ts'`
|
||||
);
|
||||
|
||||
await federateModuleGenerator(tree, {
|
||||
@@ -92,7 +92,7 @@ describe('federate-module', () => {
|
||||
'utf-8'
|
||||
);
|
||||
expect(content).toContain(
|
||||
`'./my-federated-module': 'apps/my-remote/src/my-federated-module.ts'`
|
||||
`'./my-federated-module': 'apps/myremote/src/my-federated-module.ts'`
|
||||
);
|
||||
|
||||
const tsconfig = JSON.parse(tree.read('tsconfig.base.json', 'utf-8'));
|
||||
@@ -100,7 +100,7 @@ describe('federate-module', () => {
|
||||
tsconfig.compilerOptions.paths[
|
||||
`${remoteSchema.name}/my-federated-module`
|
||||
]
|
||||
).toEqual(['apps/my-remote/src/my-federated-module.ts']);
|
||||
).toEqual(['apps/myremote/src/my-federated-module.ts']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+10
-10
@@ -19,6 +19,7 @@ import { UsersFacade } from './+state/users.facade';
|
||||
imports: [
|
||||
BrowserModule,
|
||||
RouterModule.forRoot(appRoutes),
|
||||
EffectsModule.forRoot([]),
|
||||
StoreModule.forRoot(
|
||||
{},
|
||||
{
|
||||
@@ -29,7 +30,6 @@ import { UsersFacade } from './+state/users.facade';
|
||||
},
|
||||
}
|
||||
),
|
||||
EffectsModule.forRoot([]),
|
||||
StoreRouterConnectingModule.forRoot(),
|
||||
StoreModule.forFeature(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),
|
||||
EffectsModule.forFeature([UsersEffects]),
|
||||
@@ -90,6 +90,7 @@ import { UsersEffects } from './+state/users.effects';
|
||||
imports: [
|
||||
BrowserModule,
|
||||
RouterModule.forRoot(appRoutes),
|
||||
EffectsModule.forRoot([]),
|
||||
StoreModule.forRoot(
|
||||
{},
|
||||
{
|
||||
@@ -100,7 +101,6 @@ import { UsersEffects } from './+state/users.effects';
|
||||
},
|
||||
}
|
||||
),
|
||||
EffectsModule.forRoot([]),
|
||||
StoreRouterConnectingModule.forRoot(),
|
||||
StoreModule.forFeature(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),
|
||||
EffectsModule.forFeature([UsersEffects]),
|
||||
@@ -373,6 +373,7 @@ import { StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||
imports: [
|
||||
BrowserModule,
|
||||
RouterModule.forRoot(appRoutes),
|
||||
EffectsModule.forRoot([]),
|
||||
StoreModule.forRoot({}, {
|
||||
metaReducers: [],
|
||||
runtimeChecks: {
|
||||
@@ -380,7 +381,6 @@ import { StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||
strictStateImmutability: true
|
||||
}
|
||||
}),
|
||||
EffectsModule.forRoot([]),
|
||||
StoreRouterConnectingModule.forRoot(),
|
||||
],
|
||||
providers: [],
|
||||
@@ -399,14 +399,16 @@ import { appRoutes } from './app.routes';
|
||||
import { NxWelcomeComponent } from './nx-welcome.component';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
||||
import { StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, NxWelcomeComponent],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
RouterModule.forRoot(appRoutes),
|
||||
EffectsModule.forRoot([]),
|
||||
StoreDevtoolsModule.instrument({ logOnly: !isDevMode() }),
|
||||
StoreModule.forRoot({}, {
|
||||
metaReducers: [],
|
||||
runtimeChecks: {
|
||||
@@ -414,9 +416,7 @@ import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
||||
strictStateImmutability: true
|
||||
}
|
||||
}),
|
||||
EffectsModule.forRoot([]),
|
||||
StoreRouterConnectingModule.forRoot(),
|
||||
StoreDevtoolsModule.instrument({ logOnly: !isDevMode() }),
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
@@ -436,7 +436,7 @@ import { UsersEffects } from './+state/users.effects';
|
||||
import { UsersFacade } from './+state/users.facade';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),UsersFacade,provideEffects(),provideStore(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),UsersFacade,provideStore(),provideEffects(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
@@ -482,7 +482,7 @@ import * as fromUsers from './+state/users.reducer';
|
||||
import { UsersEffects } from './+state/users.effects';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),provideEffects(),provideStore(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
providers: [provideEffects(UsersEffects),provideState(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer),provideStore(),provideEffects(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
@@ -729,7 +729,7 @@ import { provideStore } from '@ngrx/store';
|
||||
import { provideEffects } from '@ngrx/effects';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideEffects(),provideStore(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
providers: [provideStore(),provideEffects(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
@@ -743,7 +743,7 @@ import { provideEffects } from '@ngrx/effects';
|
||||
import { provideStoreDevtools } from '@ngrx/store-devtools';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideStoreDevtools({ logOnly: !isDevMode() }),provideEffects(),provideStore(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
providers: [provideStore(),provideStoreDevtools({ logOnly: !isDevMode() }),provideEffects(),provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(appRoutes) ]
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -181,15 +181,6 @@ export function addImportsToModule(
|
||||
sourceFile = addImport(sourceFile, 'EffectsModule', '@ngrx/effects');
|
||||
}
|
||||
|
||||
sourceFile = addRootStoreImport(
|
||||
tree,
|
||||
isParentStandalone,
|
||||
sourceFile,
|
||||
parentPath,
|
||||
provideRootStore,
|
||||
storeForRoot
|
||||
);
|
||||
|
||||
sourceFile = addRootEffectsImport(
|
||||
tree,
|
||||
isParentStandalone,
|
||||
@@ -199,6 +190,25 @@ export function addImportsToModule(
|
||||
effectsForEmptyRoot
|
||||
);
|
||||
|
||||
if (options.addDevTools) {
|
||||
sourceFile = addStoreDevTools(
|
||||
tree,
|
||||
sourceFile,
|
||||
parentPath,
|
||||
isParentStandalone,
|
||||
addImport
|
||||
);
|
||||
}
|
||||
|
||||
sourceFile = addRootStoreImport(
|
||||
tree,
|
||||
isParentStandalone,
|
||||
sourceFile,
|
||||
parentPath,
|
||||
provideRootStore,
|
||||
storeForRoot
|
||||
);
|
||||
|
||||
// this is just a heuristic
|
||||
const hasRouter = sourceText.indexOf('RouterModule') > -1;
|
||||
|
||||
@@ -211,14 +221,4 @@ export function addImportsToModule(
|
||||
storeRouterModule
|
||||
);
|
||||
}
|
||||
|
||||
if (options.addDevTools) {
|
||||
sourceFile = addStoreDevTools(
|
||||
tree,
|
||||
sourceFile,
|
||||
parentPath,
|
||||
isParentStandalone,
|
||||
addImport
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,4 +443,19 @@ describe('MF Remote App Generator', () => {
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson).toEqual(initialPackageJson);
|
||||
});
|
||||
|
||||
it('should error when an invalid remote name is passed to the remote generator', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
await expect(
|
||||
generateTestRemoteApplication(tree, {
|
||||
directory: 'test/my-remote',
|
||||
})
|
||||
).rejects.toMatchInlineSnapshot(`
|
||||
[Error: Invalid remote name: my-remote. Remote project names must:
|
||||
- Start with a letter, dollar sign ($) or underscore (_)
|
||||
- Followed by any valid character (letters, digits, underscores, or dollar signs)
|
||||
The regular expression used is ^[a-zA-Z_$][a-zA-Z_$0-9]*$.]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
runTasksInSerial,
|
||||
stripIndents,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import {
|
||||
@@ -39,6 +40,16 @@ export async function remote(tree: Tree, schema: Schema) {
|
||||
directory: options.directory,
|
||||
});
|
||||
|
||||
const REMOTE_NAME_REGEX = '^[a-zA-Z_$][a-zA-Z_$0-9]*$';
|
||||
const remoteNameRegex = new RegExp(REMOTE_NAME_REGEX);
|
||||
if (!remoteNameRegex.test(remoteProjectName)) {
|
||||
throw new Error(
|
||||
stripIndents`Invalid remote name: ${remoteProjectName}. Remote project names must:
|
||||
- Start with a letter, dollar sign ($) or underscore (_)
|
||||
- Followed by any valid character (letters, digits, underscores, or dollar signs)
|
||||
The regular expression used is ${REMOTE_NAME_REGEX}.`
|
||||
);
|
||||
}
|
||||
const port = options.port ?? findNextAvailablePort(tree);
|
||||
|
||||
const appInstallTask = await applicationGenerator(tree, {
|
||||
|
||||
@@ -166,8 +166,11 @@ function addLazyLoadedRouteToHostAppModule(
|
||||
: 'RemoteEntryModule';
|
||||
const routeToAdd =
|
||||
hostFederationType === 'dynamic'
|
||||
? `loadRemoteModule('${options.appName}', './${routePathName}')`
|
||||
: `import('${options.appName}/${routePathName}')`;
|
||||
? `loadRemoteModule('${options.appName.replace(
|
||||
/-/g,
|
||||
'_'
|
||||
)}', './${routePathName}')`
|
||||
: `import('${options.appName.replace(/-/g, '_')}/${routePathName}')`;
|
||||
|
||||
addRoute(
|
||||
tree,
|
||||
|
||||
@@ -12,7 +12,7 @@ export function setupTspathForRemote(tree: Tree, options: Schema) {
|
||||
|
||||
const exportName = options.standalone ? 'Routes' : 'Module';
|
||||
|
||||
addTsConfigPath(tree, `${options.appName}/${exportName}`, [
|
||||
addTsConfigPath(tree, `${options.appName.replace(/-/g, '_')}/${exportName}`, [
|
||||
joinPathFragments(project.root, exportPath),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export function spawnAndWait(command: string, args: string[], cwd: string) {
|
||||
ESLINT_USE_FLAT_CONFIG: process.env.ESLINT_USE_FLAT_CONFIG ?? 'true',
|
||||
},
|
||||
shell: true,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
childProcess.on('exit', (code) => {
|
||||
@@ -36,7 +36,7 @@ export function execAndWait(command: string, cwd: string) {
|
||||
return new Promise<{ code: number; stdout: string }>((res, rej) => {
|
||||
exec(
|
||||
command,
|
||||
{ cwd, env: { ...process.env, NX_DAEMON: 'false' }, windowsHide: true },
|
||||
{ cwd, env: { ...process.env, NX_DAEMON: 'false' }, windowsHide: false },
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
const logFile = join(cwd, 'error.log');
|
||||
|
||||
@@ -8,7 +8,7 @@ export function deduceDefaultBase(): string {
|
||||
const nxDefaultBase = 'main';
|
||||
try {
|
||||
return (
|
||||
execSync('git config --get init.defaultBranch', { windowsHide: true })
|
||||
execSync('git config --get init.defaultBranch', { windowsHide: false })
|
||||
.toString()
|
||||
.trim() || nxDefaultBase
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { output } from '../output';
|
||||
|
||||
export function checkGitVersion(): string | null | undefined {
|
||||
try {
|
||||
let gitVersionOutput = execSync('git --version', { windowsHide: true })
|
||||
let gitVersionOutput = execSync('git --version', { windowsHide: false })
|
||||
.toString()
|
||||
.trim();
|
||||
return gitVersionOutput.match(/[0-9]+\.[0-9]+\.+[0-9]+/)?.[0];
|
||||
@@ -43,7 +43,7 @@ export async function initializeGitRepo(
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
};
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
spawn('git', args, spawnOptions).on('close', (code) => {
|
||||
|
||||
@@ -139,7 +139,7 @@ function shouldRecordStats(): boolean {
|
||||
try {
|
||||
const stdout = execSync(pmc.getRegistryUrl, {
|
||||
encoding: 'utf-8',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
const url = new URL(stdout.trim());
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export function showNxWarning(workspaceName: string) {
|
||||
execSync('nx --version', {
|
||||
cwd: pathToRunNxCommand,
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
} catch (e) {
|
||||
// no nx found
|
||||
|
||||
@@ -123,7 +123,7 @@ export function getPackageManagerVersion(
|
||||
const version = execSync(`${packageManager} --version`, {
|
||||
cwd,
|
||||
encoding: 'utf-8',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}).trim();
|
||||
pmVersionCache.set(packageManager, version);
|
||||
return version;
|
||||
|
||||
@@ -78,14 +78,14 @@ function startWebServer(webServerCommand: string) {
|
||||
// Windows is fine so we leave it attached to this process
|
||||
detached: process.platform !== 'win32',
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (process.platform === 'win32') {
|
||||
try {
|
||||
execSync('taskkill /pid ' + serverProcess.pid + ' /T /F', {
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
} catch (e) {
|
||||
if (process.env.NX_VERBOSE_LOGGING === 'true') {
|
||||
|
||||
@@ -43,7 +43,7 @@ export function installPackagesTask(
|
||||
const execSyncOptions: ExecSyncOptions = {
|
||||
cwd: join(tree.root, cwd),
|
||||
stdio: process.env.NX_GENERATE_QUIET === 'true' ? 'ignore' : 'inherit',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
};
|
||||
// ensure local registry from process is not interfering with the install
|
||||
// when we start the process from temp folder the local registry would override the custom registry
|
||||
|
||||
@@ -497,7 +497,7 @@ export function ensurePackage<T extends any = any>(
|
||||
execSync(preInstallCommand, {
|
||||
cwd: tempDir,
|
||||
stdio: isVerbose ? 'inherit' : 'ignore',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
}
|
||||
let addCommand = getPackageManagerCommand(packageManager).addDev;
|
||||
@@ -508,7 +508,7 @@ export function ensurePackage<T extends any = any>(
|
||||
execSync(`${addCommand} ${pkg}@${requiredVersion}`, {
|
||||
cwd: tempDir,
|
||||
stdio: isVerbose ? 'inherit' : 'ignore',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
addToNodePath(join(workspaceRoot, 'node_modules'));
|
||||
|
||||
@@ -23,8 +23,10 @@ const isPrettierAvailable =
|
||||
* related plugins and rules below.
|
||||
*/
|
||||
export default tseslint.config(
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
files: ['**/*.js', '**/*.jsx'],
|
||||
extends: [eslint.configs.recommended, ...tseslint.configs.recommended],
|
||||
},
|
||||
...(isPrettierAvailable ? [require('eslint-config-prettier')] : []),
|
||||
{
|
||||
languageOptions: {
|
||||
|
||||
@@ -14,8 +14,10 @@ const isPrettierAvailable =
|
||||
* to one ecosystem, such as React, Angular, Node etc.
|
||||
*/
|
||||
export default tseslint.config(
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
extends: [eslint.configs.recommended, ...tseslint.configs.recommended],
|
||||
},
|
||||
...(isPrettierAvailable ? [require('eslint-config-prettier')] : []),
|
||||
{
|
||||
plugins: { '@typescript-eslint': tseslint.plugin },
|
||||
|
||||
@@ -114,7 +114,7 @@ function copyPackageJsonAndLock(
|
||||
packageManager: PackageManager,
|
||||
workspaceRoot: string,
|
||||
projectRoot: string
|
||||
) {
|
||||
): () => void {
|
||||
const packageJson = pathResolve(workspaceRoot, 'package.json');
|
||||
const rootPackageJson = readJsonFile<PackageJson>(packageJson);
|
||||
// do not copy package.json and lock file if workspaces are enabled
|
||||
@@ -123,7 +123,8 @@ function copyPackageJsonAndLock(
|
||||
existsSync(pathResolve(workspaceRoot, 'pnpm-workspace.yaml'))) ||
|
||||
rootPackageJson.workspaces
|
||||
) {
|
||||
return;
|
||||
// no resource taken, no resource cleaned up
|
||||
return () => {};
|
||||
}
|
||||
|
||||
const packageJsonProject = pathResolve(projectRoot, 'package.json');
|
||||
|
||||
@@ -68,7 +68,7 @@ export function podInstall(
|
||||
execSync('touch .xcode.env', {
|
||||
cwd: iosDirectory,
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
}
|
||||
execSync(
|
||||
@@ -78,7 +78,7 @@ export function podInstall(
|
||||
{
|
||||
cwd: iosDirectory,
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
|
||||
@@ -11,13 +11,13 @@ export function resolveEas(workspaceRoot: string): string {
|
||||
|
||||
let npmGlobalPath: string, yarnGlobalPath: string;
|
||||
try {
|
||||
npmGlobalPath = execSync('npm root -g', { windowsHide: true })
|
||||
npmGlobalPath = execSync('npm root -g', { windowsHide: false })
|
||||
?.toString()
|
||||
?.trim()
|
||||
?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes
|
||||
} catch {}
|
||||
try {
|
||||
yarnGlobalPath = execSync('yarn global dir', { windowsHide: true })
|
||||
yarnGlobalPath = execSync('yarn global dir', { windowsHide: false })
|
||||
?.toString()
|
||||
?.trim()
|
||||
?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes
|
||||
|
||||
@@ -21,7 +21,7 @@ export async function killTree(pid: number, signal: NodeJS.Signals) {
|
||||
exec(
|
||||
'taskkill /pid ' + pid + ' /T /F',
|
||||
{
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
},
|
||||
(error) => {
|
||||
// Ignore Fatal errors (128) because it might be due to the process already being killed.
|
||||
@@ -37,7 +37,7 @@ export async function killTree(pid: number, signal: NodeJS.Signals) {
|
||||
pidsToProcess,
|
||||
function (parentPid) {
|
||||
return spawn('pgrep', ['-P', parentPid], {
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
},
|
||||
function () {
|
||||
@@ -55,7 +55,7 @@ export async function killTree(pid: number, signal: NodeJS.Signals) {
|
||||
'ps',
|
||||
['-o', 'pid', '--no-headers', '--ppid', parentPid],
|
||||
{
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
@@ -128,7 +128,7 @@ Please update the local dependency on "${depName}" to be a valid semantic versio
|
||||
env: processEnv(true),
|
||||
cwd: context.root,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
const resultJson = JSON.parse(result.toString());
|
||||
@@ -154,7 +154,7 @@ Please update the local dependency on "${depName}" to be a valid semantic versio
|
||||
env: processEnv(true),
|
||||
cwd: context.root,
|
||||
stdio: 'ignore',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
console.log(
|
||||
`Added the dist-tag ${tag} to v${currentVersion} for registry ${registry}.\n`
|
||||
@@ -269,7 +269,7 @@ Please update the local dependency on "${depName}" to be a valid semantic versio
|
||||
env: processEnv(true),
|
||||
cwd: context.root,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,12 +49,12 @@ export async function verdaccioExecutor(
|
||||
options.location === 'none' ? [] : [setupNpm(options), setupYarn(options)];
|
||||
|
||||
const processExitListener = (signal?: number | NodeJS.Signals) => {
|
||||
if (childProcess) {
|
||||
childProcess.kill(signal);
|
||||
}
|
||||
for (const fn of cleanupFunctions) {
|
||||
fn();
|
||||
}
|
||||
if (childProcess) {
|
||||
childProcess.kill(signal);
|
||||
}
|
||||
};
|
||||
process.on('exit', processExitListener);
|
||||
process.on('SIGTERM', processExitListener);
|
||||
@@ -136,7 +136,7 @@ function createVerdaccioOptions(
|
||||
|
||||
function setupNpm(options: VerdaccioExecutorSchema) {
|
||||
try {
|
||||
execSync('npm --version', { env, windowsHide: true });
|
||||
execSync('npm --version', { env, windowsHide: false });
|
||||
} catch (e) {
|
||||
return () => {};
|
||||
}
|
||||
@@ -151,7 +151,7 @@ function setupNpm(options: VerdaccioExecutorSchema) {
|
||||
npmRegistryPaths.push(
|
||||
execSync(
|
||||
`npm config get ${registryName} --location ${options.location}`,
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
)
|
||||
?.toString()
|
||||
?.trim()
|
||||
@@ -159,12 +159,12 @@ function setupNpm(options: VerdaccioExecutorSchema) {
|
||||
);
|
||||
execSync(
|
||||
`npm config set ${registryName} http://localhost:${options.port}/ --location ${options.location}`,
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
);
|
||||
|
||||
execSync(
|
||||
`npm config set //localhost:${options.port}/:_authToken="secretVerdaccioToken" --location ${options.location}`,
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
);
|
||||
|
||||
logger.info(
|
||||
@@ -181,7 +181,7 @@ function setupNpm(options: VerdaccioExecutorSchema) {
|
||||
try {
|
||||
const currentNpmRegistryPath = execSync(
|
||||
`npm config get registry --location ${options.location}`,
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
)
|
||||
?.toString()
|
||||
?.trim()
|
||||
@@ -194,7 +194,7 @@ function setupNpm(options: VerdaccioExecutorSchema) {
|
||||
) {
|
||||
execSync(
|
||||
`npm config set ${registryName} ${npmRegistryPaths[index]} --location ${options.location}`,
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
);
|
||||
logger.info(
|
||||
`Reset npm ${registryName} to ${npmRegistryPaths[index]}`
|
||||
@@ -204,7 +204,7 @@ function setupNpm(options: VerdaccioExecutorSchema) {
|
||||
`npm config delete ${registryName} --location ${options.location}`,
|
||||
{
|
||||
env,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}
|
||||
);
|
||||
logger.info('Cleared custom npm registry');
|
||||
@@ -212,7 +212,7 @@ function setupNpm(options: VerdaccioExecutorSchema) {
|
||||
});
|
||||
execSync(
|
||||
`npm config delete //localhost:${options.port}/:_authToken --location ${options.location}`,
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(`Failed to reset npm registry: ${e.message}`);
|
||||
@@ -231,7 +231,7 @@ function getYarnUnsafeHttpWhitelist(isYarnV1: boolean) {
|
||||
JSON.parse(
|
||||
execSync(`yarn config get unsafeHttpWhitelist --json`, {
|
||||
env,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}).toString()
|
||||
)
|
||||
)
|
||||
@@ -247,13 +247,13 @@ function setYarnUnsafeHttpWhitelist(
|
||||
`yarn config set unsafeHttpWhitelist --json '${JSON.stringify(
|
||||
Array.from(currentWhitelist)
|
||||
)}'` + (options.location === 'user' ? ' --home' : ''),
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
);
|
||||
} else {
|
||||
execSync(
|
||||
`yarn config unset unsafeHttpWhitelist` +
|
||||
(options.location === 'user' ? ' --home' : ''),
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -266,7 +266,9 @@ function setupYarn(options: VerdaccioExecutorSchema) {
|
||||
try {
|
||||
isYarnV1 =
|
||||
major(
|
||||
execSync('yarn --version', { env, windowsHide: true }).toString().trim()
|
||||
execSync('yarn --version', { env, windowsHide: false })
|
||||
.toString()
|
||||
.trim()
|
||||
) === 1;
|
||||
} catch {
|
||||
// This would fail if yarn is not installed which is okay
|
||||
@@ -281,7 +283,7 @@ function setupYarn(options: VerdaccioExecutorSchema) {
|
||||
yarnRegistryPaths.push(
|
||||
execSync(`yarn config get ${scopeName}${registryConfigName}`, {
|
||||
env,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
})
|
||||
?.toString()
|
||||
?.trim()
|
||||
@@ -291,7 +293,7 @@ function setupYarn(options: VerdaccioExecutorSchema) {
|
||||
execSync(
|
||||
`yarn config set ${scopeName}${registryConfigName} http://localhost:${options.port}/` +
|
||||
(options.location === 'user' ? ' --home' : ''),
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
);
|
||||
|
||||
logger.info(
|
||||
@@ -318,7 +320,7 @@ function setupYarn(options: VerdaccioExecutorSchema) {
|
||||
try {
|
||||
const currentYarnRegistryPath = execSync(
|
||||
`yarn config get ${registryConfigName}`,
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
)
|
||||
?.toString()
|
||||
?.trim()
|
||||
@@ -339,7 +341,7 @@ function setupYarn(options: VerdaccioExecutorSchema) {
|
||||
{
|
||||
env,
|
||||
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}
|
||||
);
|
||||
logger.info(
|
||||
@@ -349,7 +351,7 @@ function setupYarn(options: VerdaccioExecutorSchema) {
|
||||
execSync(
|
||||
`yarn config ${isYarnV1 ? 'delete' : 'unset'} ${registryName}` +
|
||||
(options.location === 'user' ? ' --home' : ''),
|
||||
{ env, windowsHide: true }
|
||||
{ env, windowsHide: false }
|
||||
);
|
||||
logger.info(`Cleared custom yarn ${registryConfigName}`);
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ To fix this you will either need to add a package.json file at that location, or
|
||||
exec(
|
||||
`npm view ${packageName} version --"${registryConfigKey}=${registry}" --tag=${tag}`,
|
||||
{
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
},
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
|
||||
@@ -133,7 +133,7 @@ function execLockFileUpdate(
|
||||
...process.env,
|
||||
...env,
|
||||
},
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
} catch (e) {
|
||||
output.error({
|
||||
|
||||
@@ -23,7 +23,7 @@ export async function setupVerdaccio(
|
||||
generateFiles(tree, path.join(__dirname, 'files'), '.verdaccio', {
|
||||
npmUplinkRegistry:
|
||||
execSync('npm config get registry', {
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
})
|
||||
?.toString()
|
||||
?.trim() ?? 'https://registry.npmjs.org',
|
||||
|
||||
@@ -51,6 +51,12 @@ type GeneratorOptions = {
|
||||
};
|
||||
|
||||
type NormalizedGeneratorOptions = Required<GeneratorOptions>;
|
||||
type TsconfigInfoCaches = {
|
||||
composite: Map<string, boolean>;
|
||||
content: Map<string, string>;
|
||||
exists: Map<string, boolean>;
|
||||
isFile: Map<string, boolean>;
|
||||
};
|
||||
|
||||
export async function syncGenerator(tree: Tree): Promise<SyncGeneratorResult> {
|
||||
// Ensure that the plugin has been wired up in nx.json
|
||||
@@ -71,24 +77,28 @@ export async function syncGenerator(tree: Tree): Promise<SyncGeneratorResult> {
|
||||
]);
|
||||
}
|
||||
|
||||
const tsconfigInfoCaches: TsconfigInfoCaches = {
|
||||
composite: new Map(),
|
||||
content: new Map(),
|
||||
exists: new Map(),
|
||||
isFile: new Map(),
|
||||
};
|
||||
// Root tsconfig containing project references for the whole workspace
|
||||
const rootTsconfigPath = 'tsconfig.json';
|
||||
if (!tree.exists(rootTsconfigPath)) {
|
||||
if (!tsconfigExists(tree, tsconfigInfoCaches, rootTsconfigPath)) {
|
||||
throw new SyncError('Missing root "tsconfig.json"', [
|
||||
`A "tsconfig.json" file must exist in the workspace root in order to sync the project graph information to the TypeScript configuration files.`,
|
||||
]);
|
||||
}
|
||||
|
||||
const rawTsconfigContentsCache = new Map<string, string>();
|
||||
const stringifiedRootJsonContents = readRawTsconfigContents(
|
||||
tree,
|
||||
rawTsconfigContentsCache,
|
||||
tsconfigInfoCaches,
|
||||
rootTsconfigPath
|
||||
);
|
||||
const rootTsconfig = parseJson<Tsconfig>(stringifiedRootJsonContents);
|
||||
const projectGraph = await createProjectGraphAsync();
|
||||
const projectRoots = new Set<string>();
|
||||
const tsconfigHasCompositeEnabledCache = new Map<string, boolean>();
|
||||
|
||||
const tsconfigProjectNodeValues = Object.values(projectGraph.nodes).filter(
|
||||
(node) => {
|
||||
@@ -97,18 +107,17 @@ export async function syncGenerator(tree: Tree): Promise<SyncGeneratorResult> {
|
||||
node.data.root,
|
||||
'tsconfig.json'
|
||||
);
|
||||
return tsconfigExists(
|
||||
tree,
|
||||
rawTsconfigContentsCache,
|
||||
projectTsconfigPath
|
||||
);
|
||||
return tsconfigExists(tree, tsconfigInfoCaches, projectTsconfigPath);
|
||||
}
|
||||
);
|
||||
|
||||
const tsSysFromTree: ts.System = {
|
||||
...ts.sys,
|
||||
fileExists(path) {
|
||||
return tsconfigExists(tree, tsconfigInfoCaches, path);
|
||||
},
|
||||
readFile(path) {
|
||||
return readRawTsconfigContents(tree, rawTsconfigContentsCache, path);
|
||||
return readRawTsconfigContents(tree, tsconfigInfoCaches, path);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -125,9 +134,10 @@ export async function syncGenerator(tree: Tree): Promise<SyncGeneratorResult> {
|
||||
const resolvedRefPath = getTsConfigPathFromReferencePath(
|
||||
tree,
|
||||
rootTsconfigPath,
|
||||
ref.path
|
||||
ref.path,
|
||||
tsconfigInfoCaches
|
||||
);
|
||||
if (tsconfigExists(tree, rawTsconfigContentsCache, resolvedRefPath)) {
|
||||
if (tsconfigExists(tree, tsconfigInfoCaches, resolvedRefPath)) {
|
||||
// we only keep the references that still exist
|
||||
referencesSet.add(normalizeReferencePath(ref.path));
|
||||
} else {
|
||||
@@ -150,7 +160,7 @@ export async function syncGenerator(tree: Tree): Promise<SyncGeneratorResult> {
|
||||
.filter((ref) =>
|
||||
hasCompositeEnabled(
|
||||
tsSysFromTree,
|
||||
tsconfigHasCompositeEnabledCache,
|
||||
tsconfigInfoCaches,
|
||||
joinPathFragments(ref, 'tsconfig.json')
|
||||
)
|
||||
)
|
||||
@@ -159,7 +169,7 @@ export async function syncGenerator(tree: Tree): Promise<SyncGeneratorResult> {
|
||||
}));
|
||||
patchTsconfigJsonReferences(
|
||||
tree,
|
||||
rawTsconfigContentsCache,
|
||||
tsconfigInfoCaches,
|
||||
rootTsconfigPath,
|
||||
updatedReferences
|
||||
);
|
||||
@@ -192,9 +202,7 @@ export async function syncGenerator(tree: Tree): Promise<SyncGeneratorResult> {
|
||||
sourceProjectNode.data.root,
|
||||
'tsconfig.json'
|
||||
);
|
||||
if (
|
||||
!tsconfigExists(tree, rawTsconfigContentsCache, sourceProjectTsconfigPath)
|
||||
) {
|
||||
if (!tsconfigExists(tree, tsconfigInfoCaches, sourceProjectTsconfigPath)) {
|
||||
if (process.env.NX_VERBOSE_LOGGING === 'true') {
|
||||
logger.warn(
|
||||
`Skipping project "${projectName}" as there is no tsconfig.json file found in the project root "${sourceProjectNode.data.root}".`
|
||||
@@ -216,9 +224,7 @@ export async function syncGenerator(tree: Tree): Promise<SyncGeneratorResult> {
|
||||
sourceProjectNode.data.root,
|
||||
runtimeTsConfigFileName
|
||||
);
|
||||
if (
|
||||
!tsconfigExists(tree, rawTsconfigContentsCache, runtimeTsConfigPath)
|
||||
) {
|
||||
if (!tsconfigExists(tree, tsconfigInfoCaches, runtimeTsConfigPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -227,8 +233,7 @@ export async function syncGenerator(tree: Tree): Promise<SyncGeneratorResult> {
|
||||
updateTsConfigReferences(
|
||||
tree,
|
||||
tsSysFromTree,
|
||||
rawTsconfigContentsCache,
|
||||
tsconfigHasCompositeEnabledCache,
|
||||
tsconfigInfoCaches,
|
||||
runtimeTsConfigPath,
|
||||
dependencies,
|
||||
sourceProjectNode.data.root,
|
||||
@@ -243,8 +248,7 @@ export async function syncGenerator(tree: Tree): Promise<SyncGeneratorResult> {
|
||||
updateTsConfigReferences(
|
||||
tree,
|
||||
tsSysFromTree,
|
||||
rawTsconfigContentsCache,
|
||||
tsconfigHasCompositeEnabledCache,
|
||||
tsconfigInfoCaches,
|
||||
sourceProjectTsconfigPath,
|
||||
dependencies,
|
||||
sourceProjectNode.data.root,
|
||||
@@ -270,16 +274,17 @@ export default syncGenerator;
|
||||
*/
|
||||
function readRawTsconfigContents(
|
||||
tree: Tree,
|
||||
rawTsconfigContentsCache: Map<string, string>,
|
||||
tsconfigInfoCaches: TsconfigInfoCaches,
|
||||
tsconfigPath: string
|
||||
): string {
|
||||
if (!rawTsconfigContentsCache.has(tsconfigPath)) {
|
||||
rawTsconfigContentsCache.set(
|
||||
if (!tsconfigInfoCaches.content.has(tsconfigPath)) {
|
||||
tsconfigInfoCaches.content.set(
|
||||
tsconfigPath,
|
||||
tree.read(tsconfigPath, 'utf-8')
|
||||
);
|
||||
}
|
||||
return rawTsconfigContentsCache.get(tsconfigPath);
|
||||
|
||||
return tsconfigInfoCaches.content.get(tsconfigPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,19 +293,20 @@ function readRawTsconfigContents(
|
||||
*/
|
||||
function tsconfigExists(
|
||||
tree: Tree,
|
||||
rawTsconfigContentsCache: Map<string, string>,
|
||||
tsconfigInfoCaches: TsconfigInfoCaches,
|
||||
tsconfigPath: string
|
||||
): boolean {
|
||||
return rawTsconfigContentsCache.has(tsconfigPath)
|
||||
? true
|
||||
: tree.exists(tsconfigPath);
|
||||
if (!tsconfigInfoCaches.exists.has(tsconfigPath)) {
|
||||
tsconfigInfoCaches.exists.set(tsconfigPath, tree.exists(tsconfigPath));
|
||||
}
|
||||
|
||||
return tsconfigInfoCaches.exists.get(tsconfigPath);
|
||||
}
|
||||
|
||||
function updateTsConfigReferences(
|
||||
tree: Tree,
|
||||
tsSysFromTree: ts.System,
|
||||
rawTsconfigContentsCache: Map<string, string>,
|
||||
tsconfigHasCompositeEnabledCache: Map<string, boolean>,
|
||||
tsconfigInfoCaches: TsconfigInfoCaches,
|
||||
tsConfigPath: string,
|
||||
dependencies: ProjectGraphProjectNode[],
|
||||
projectRoot: string,
|
||||
@@ -310,7 +316,7 @@ function updateTsConfigReferences(
|
||||
): boolean {
|
||||
const stringifiedJsonContents = readRawTsconfigContents(
|
||||
tree,
|
||||
rawTsconfigContentsCache,
|
||||
tsconfigInfoCaches,
|
||||
tsConfigPath
|
||||
);
|
||||
const tsConfig = parseJson<Tsconfig>(stringifiedJsonContents);
|
||||
@@ -335,12 +341,13 @@ function updateTsConfigReferences(
|
||||
const resolvedRefPath = getTsConfigPathFromReferencePath(
|
||||
tree,
|
||||
tsConfigPath,
|
||||
ref.path
|
||||
ref.path,
|
||||
tsconfigInfoCaches
|
||||
);
|
||||
if (
|
||||
isProjectReferenceWithinNxProject(
|
||||
tree,
|
||||
rawTsconfigContentsCache,
|
||||
tsconfigInfoCaches,
|
||||
resolvedRefPath,
|
||||
projectRoot,
|
||||
projectRoots
|
||||
@@ -362,12 +369,12 @@ function updateTsConfigReferences(
|
||||
dep.data.root,
|
||||
runtimeTsConfigFileName
|
||||
);
|
||||
if (tsconfigExists(tree, rawTsconfigContentsCache, runtimeTsConfigPath)) {
|
||||
if (tsconfigExists(tree, tsconfigInfoCaches, runtimeTsConfigPath)) {
|
||||
// Check composite is true in the dependency runtime tsconfig file before proceeding
|
||||
if (
|
||||
!hasCompositeEnabled(
|
||||
tsSysFromTree,
|
||||
tsconfigHasCompositeEnabledCache,
|
||||
tsconfigInfoCaches,
|
||||
runtimeTsConfigPath
|
||||
)
|
||||
) {
|
||||
@@ -386,7 +393,7 @@ function updateTsConfigReferences(
|
||||
if (
|
||||
tsconfigExists(
|
||||
tree,
|
||||
rawTsconfigContentsCache,
|
||||
tsconfigInfoCaches,
|
||||
possibleRuntimeTsConfigPath
|
||||
)
|
||||
) {
|
||||
@@ -394,7 +401,7 @@ function updateTsConfigReferences(
|
||||
if (
|
||||
!hasCompositeEnabled(
|
||||
tsSysFromTree,
|
||||
tsconfigHasCompositeEnabledCache,
|
||||
tsconfigInfoCaches,
|
||||
possibleRuntimeTsConfigPath
|
||||
)
|
||||
) {
|
||||
@@ -410,7 +417,7 @@ function updateTsConfigReferences(
|
||||
if (
|
||||
!hasCompositeEnabled(
|
||||
tsSysFromTree,
|
||||
tsconfigHasCompositeEnabledCache,
|
||||
tsconfigInfoCaches,
|
||||
joinPathFragments(dep.data.root, 'tsconfig.json')
|
||||
)
|
||||
) {
|
||||
@@ -433,7 +440,7 @@ function updateTsConfigReferences(
|
||||
if (hasChanges) {
|
||||
patchTsconfigJsonReferences(
|
||||
tree,
|
||||
rawTsconfigContentsCache,
|
||||
tsconfigInfoCaches,
|
||||
tsConfigPath,
|
||||
references
|
||||
);
|
||||
@@ -508,14 +515,14 @@ function normalizeReferencePath(path: string): string {
|
||||
|
||||
function isProjectReferenceWithinNxProject(
|
||||
tree: Tree,
|
||||
rawTsconfigContentsCache: Map<string, string>,
|
||||
tsconfigInfoCaches: TsconfigInfoCaches,
|
||||
refTsConfigPath: string,
|
||||
projectRoot: string,
|
||||
projectRoots: Set<string>
|
||||
): boolean {
|
||||
let currentPath = getTsConfigDirName(
|
||||
tree,
|
||||
rawTsconfigContentsCache,
|
||||
tsconfigInfoCaches,
|
||||
refTsConfigPath
|
||||
);
|
||||
|
||||
@@ -554,14 +561,10 @@ function isProjectReferenceIgnored(
|
||||
|
||||
function getTsConfigDirName(
|
||||
tree: Tree,
|
||||
rawTsconfigContentsCache: Map<string, string>,
|
||||
tsconfigInfoCaches: TsconfigInfoCaches,
|
||||
tsConfigPath: string
|
||||
): string {
|
||||
return (
|
||||
rawTsconfigContentsCache.has(tsConfigPath)
|
||||
? true
|
||||
: tree.isFile(tsConfigPath)
|
||||
)
|
||||
return tsconfigIsFile(tree, tsconfigInfoCaches, tsConfigPath)
|
||||
? dirname(tsConfigPath)
|
||||
: normalize(tsConfigPath);
|
||||
}
|
||||
@@ -569,14 +572,15 @@ function getTsConfigDirName(
|
||||
function getTsConfigPathFromReferencePath(
|
||||
tree: Tree,
|
||||
ownerTsConfigPath: string,
|
||||
referencePath: string
|
||||
referencePath: string,
|
||||
tsconfigInfoCaches: TsconfigInfoCaches
|
||||
): string {
|
||||
const resolvedRefPath = joinPathFragments(
|
||||
dirname(ownerTsConfigPath),
|
||||
referencePath
|
||||
);
|
||||
|
||||
return tree.isFile(resolvedRefPath)
|
||||
return tsconfigIsFile(tree, tsconfigInfoCaches, resolvedRefPath)
|
||||
? resolvedRefPath
|
||||
: joinPathFragments(resolvedRefPath, 'tsconfig.json');
|
||||
}
|
||||
@@ -587,13 +591,13 @@ function getTsConfigPathFromReferencePath(
|
||||
*/
|
||||
function patchTsconfigJsonReferences(
|
||||
tree: Tree,
|
||||
rawTsconfigContentsCache: Map<string, string>,
|
||||
tsconfigInfoCaches: TsconfigInfoCaches,
|
||||
tsconfigPath: string,
|
||||
updatedReferences: { path: string }[]
|
||||
) {
|
||||
const stringifiedJsonContents = readRawTsconfigContents(
|
||||
tree,
|
||||
rawTsconfigContentsCache,
|
||||
tsconfigInfoCaches,
|
||||
tsconfigPath
|
||||
);
|
||||
const edits = modify(
|
||||
@@ -609,17 +613,40 @@ function patchTsconfigJsonReferences(
|
||||
|
||||
function hasCompositeEnabled(
|
||||
tsSysFromTree: ts.System,
|
||||
tsconfigHasCompositeEnabledCache: Map<string, boolean>,
|
||||
tsconfigInfoCaches: TsconfigInfoCaches,
|
||||
tsconfigPath: string
|
||||
): boolean {
|
||||
if (!tsconfigHasCompositeEnabledCache.has(tsconfigPath)) {
|
||||
if (!tsconfigInfoCaches.composite.has(tsconfigPath)) {
|
||||
const parsed = ts.parseJsonConfigFileContent(
|
||||
ts.readConfigFile(tsconfigPath, tsSysFromTree.readFile).config,
|
||||
tsSysFromTree,
|
||||
dirname(tsconfigPath)
|
||||
);
|
||||
const enabledVal = parsed.options.composite === true;
|
||||
tsconfigHasCompositeEnabledCache.set(tsconfigPath, enabledVal);
|
||||
tsconfigInfoCaches.composite.set(
|
||||
tsconfigPath,
|
||||
parsed.options.composite === true
|
||||
);
|
||||
}
|
||||
return tsconfigHasCompositeEnabledCache.get(tsconfigPath);
|
||||
|
||||
return tsconfigInfoCaches.composite.get(tsconfigPath);
|
||||
}
|
||||
|
||||
function tsconfigIsFile(
|
||||
tree: Tree,
|
||||
tsconfigInfoCaches: TsconfigInfoCaches,
|
||||
tsconfigPath: string
|
||||
): boolean {
|
||||
if (tsconfigInfoCaches.isFile.has(tsconfigPath)) {
|
||||
return tsconfigInfoCaches.isFile.get(tsconfigPath);
|
||||
}
|
||||
|
||||
if (tsconfigInfoCaches.content.has(tsconfigPath)) {
|
||||
// if it has content, it's a file
|
||||
tsconfigInfoCaches.isFile.set(tsconfigPath, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
tsconfigInfoCaches.isFile.set(tsconfigPath, tree.isFile(tsconfigPath));
|
||||
|
||||
return tsconfigInfoCaches.isFile.get(tsconfigPath);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export function startLocalRegistry({
|
||||
execSync(
|
||||
`npm config set //localhost:${port}/:_authToken "secretVerdaccioToken"`,
|
||||
{
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -63,7 +63,7 @@ export function startLocalRegistry({
|
||||
resolve(() => {
|
||||
childProcess.kill();
|
||||
execSync(`npm config delete //localhost:${port}/:_authToken`, {
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
});
|
||||
childProcess?.stdout?.off('data', listener);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { type CreateNodesContext } from '@nx/devkit';
|
||||
import { detectPackageManager, type CreateNodesContext } from '@nx/devkit';
|
||||
import { TempFs } from '@nx/devkit/internal-testing-utils';
|
||||
import { minimatch } from 'minimatch';
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||
import { getLockFileName } from 'nx/src/plugins/js/lock-file/lock-file';
|
||||
import { setupWorkspaceContext } from 'nx/src/utils/workspace-context';
|
||||
import { PLUGIN_NAME, createNodesV2, type TscPluginOptions } from './plugin';
|
||||
|
||||
@@ -25,6 +27,10 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||
process.chdir(tempFs.tempDir);
|
||||
originalCacheProjectGraph = process.env.NX_CACHE_PROJECT_GRAPH;
|
||||
process.env.NX_CACHE_PROJECT_GRAPH = 'false';
|
||||
const lockFileName = getLockFileName(
|
||||
detectPackageManager(context.workspaceRoot)
|
||||
);
|
||||
applyFilesToTempFsAndContext(tempFs, context, { [lockFileName]: '' });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -16,12 +16,11 @@ import {
|
||||
type ProjectConfiguration,
|
||||
type TargetConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
||||
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
|
||||
import { minimatch } from 'minimatch';
|
||||
import { existsSync, readdirSync, statSync } from 'node:fs';
|
||||
import { basename, dirname, join, normalize, relative } from 'node:path';
|
||||
import { hashObject } from 'nx/src/hasher/file-hasher';
|
||||
import { hashArray, hashFile, hashObject } from 'nx/src/hasher/file-hasher';
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||
import { getLockFileName } from 'nx/src/plugins/js/lock-file/lock-file';
|
||||
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
||||
@@ -91,10 +90,19 @@ export const createNodesV2: CreateNodesV2<TscPluginOptions> = [
|
||||
const cachePath = join(workspaceDataDirectory, `tsc-${optionsHash}.hash`);
|
||||
const targetsCache = readTargetsCache(cachePath);
|
||||
const normalizedOptions = normalizePluginOptions(options);
|
||||
const lockFileName = getLockFileName(
|
||||
detectPackageManager(context.workspaceRoot)
|
||||
);
|
||||
try {
|
||||
return await createNodesFromFiles(
|
||||
(configFile, options, context) =>
|
||||
createNodesInternal(configFile, options, context, targetsCache),
|
||||
createNodesInternal(
|
||||
configFile,
|
||||
options,
|
||||
context,
|
||||
lockFileName,
|
||||
targetsCache
|
||||
),
|
||||
configFilePaths,
|
||||
normalizedOptions,
|
||||
context
|
||||
@@ -112,7 +120,16 @@ export const createNodes: CreateNodes<TscPluginOptions> = [
|
||||
'`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 20, this will change to the createNodesV2 API.'
|
||||
);
|
||||
const normalizedOptions = normalizePluginOptions(options);
|
||||
return createNodesInternal(configFilePath, normalizedOptions, context, {});
|
||||
const lockFileName = getLockFileName(
|
||||
detectPackageManager(context.workspaceRoot)
|
||||
);
|
||||
return createNodesInternal(
|
||||
configFilePath,
|
||||
normalizedOptions,
|
||||
context,
|
||||
lockFileName,
|
||||
{}
|
||||
);
|
||||
},
|
||||
];
|
||||
|
||||
@@ -120,6 +137,7 @@ async function createNodesInternal(
|
||||
configFilePath: string,
|
||||
options: NormalizedPluginOptions,
|
||||
context: CreateNodesContext,
|
||||
lockFileName: string,
|
||||
targetsCache: Record<string, TscProjectResult>
|
||||
): Promise<CreateNodesResult> {
|
||||
const projectRoot = dirname(configFilePath);
|
||||
@@ -150,13 +168,23 @@ async function createNodesInternal(
|
||||
return {};
|
||||
}
|
||||
|
||||
const nodeHash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
/**
|
||||
* The cache key is composed by:
|
||||
* - hashes of the content of the relevant files that can affect what's inferred by the plugin:
|
||||
* - current config file
|
||||
* - config files extended by the current config file (recursively up to the root config file)
|
||||
* - lock file
|
||||
* - hash of the plugin options
|
||||
* - current config file path
|
||||
*/
|
||||
const extendedConfigFiles = getExtendedConfigFiles(
|
||||
fullConfigPath,
|
||||
readCachedTsConfig(fullConfigPath)
|
||||
);
|
||||
// The hash is calculated at the node/project level, so we add the config file path to avoid conflicts when caching
|
||||
const nodeHash = hashArray([
|
||||
...[configFilePath, ...extendedConfigFiles, lockFileName].map(hashFile),
|
||||
hashObject(options),
|
||||
]);
|
||||
const cacheKey = `${nodeHash}_${configFilePath}`;
|
||||
|
||||
targetsCache[cacheKey] ??= buildTscTargets(
|
||||
|
||||
@@ -108,7 +108,7 @@ async function getNpmConfigValue(key: string, cwd: string): Promise<string> {
|
||||
async function execAsync(command: string, cwd: string): Promise<string> {
|
||||
// Must be non-blocking async to allow spinner to render
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
exec(command, { cwd, windowsHide: true }, (error, stdout, stderr) => {
|
||||
exec(command, { cwd, windowsHide: false }, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export async function compileSwc(
|
||||
const swcCmdLog = execSync(getSwcCmd(normalizedOptions), {
|
||||
encoding: 'utf8',
|
||||
cwd: normalizedOptions.swcCliOptions.swcCwd,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
logger.log(swcCmdLog.replace(/\n/, ''));
|
||||
const isCompileSuccess = swcCmdLog.includes('Successfully compiled');
|
||||
@@ -138,7 +138,7 @@ export async function* compileSwcWatch(
|
||||
|
||||
const swcWatcher = exec(getSwcCmd(normalizedOptions, true), {
|
||||
cwd: normalizedOptions.swcCliOptions.swcCwd,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
processOnExit = () => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`app generated files content - as-provided - my-app general application should add nuxt entries in .gitignore 1`] = `
|
||||
"
|
||||
# Nuxt dev/build outputs
|
||||
"# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache"
|
||||
.cache
|
||||
**/vite.config.{js,ts,mjs,mts,cjs,cts}.timestamp*"
|
||||
`;
|
||||
|
||||
exports[`app generated files content - as-provided - my-app general application should add the nuxt and vitest plugins 1`] = `
|
||||
@@ -364,13 +364,13 @@ export default defineNuxtConfig({
|
||||
`;
|
||||
|
||||
exports[`app generated files content - as-provided - myApp general application should add nuxt entries in .gitignore 1`] = `
|
||||
"
|
||||
# Nuxt dev/build outputs
|
||||
"# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache"
|
||||
.cache
|
||||
**/vite.config.{js,ts,mjs,mts,cjs,cts}.timestamp*"
|
||||
`;
|
||||
|
||||
exports[`app generated files content - as-provided - myApp general application should add the nuxt and vitest plugins 1`] = `
|
||||
|
||||
@@ -159,7 +159,7 @@ export async function applicationGenerator(tree: Tree, schema: Schema) {
|
||||
execSync(`npx -y nuxi prepare`, {
|
||||
cwd: options.appProjectRoot,
|
||||
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(
|
||||
|
||||
@@ -252,14 +252,14 @@ function getLocalNxVersion(workspace: WorkspaceTypeAndRoot): string | null {
|
||||
function _getLatestVersionOfNx(): string {
|
||||
try {
|
||||
return execSync('npm view nx@latest version', {
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
})
|
||||
.toString()
|
||||
.trim();
|
||||
} catch {
|
||||
try {
|
||||
return execSync('pnpm view nx@latest version', {
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
})
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
@@ -57,6 +57,12 @@
|
||||
"version": "20.0.0-beta.7",
|
||||
"description": "Migration for v20.0.0-beta.7",
|
||||
"implementation": "./src/migrations/update-20-0-0/move-use-daemon-process"
|
||||
},
|
||||
"use-legacy-cache": {
|
||||
"version": "20.0.1",
|
||||
"description": "Set `useLegacyCache` to true for migrating workspaces",
|
||||
"implementation": "./src/migrations/update-20-0-1/use-legacy-cache",
|
||||
"x-repair-skip": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,10 @@
|
||||
"type": "string",
|
||||
"description": "Specifies the encryption key used to encrypt artifacts data before sending it to nx cloud."
|
||||
},
|
||||
"neverConnectToCloud": {
|
||||
"type": "boolean",
|
||||
"description": "Set this to true to disable all connections to Nx Cloud."
|
||||
},
|
||||
"parallel": {
|
||||
"type": "number",
|
||||
"description": "Specifies how many tasks are ran in parallel by Nx for the default tasks runner."
|
||||
|
||||
@@ -26,7 +26,7 @@ async function requirePowerpack(): Promise<any> {
|
||||
execSync(
|
||||
`${getPackageManagerCommand().addDev} @nx/powerpack-license@latest`,
|
||||
{
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ async function installPackage(
|
||||
exec(
|
||||
`${pmc.addDev} ${pkgName}@${version}`,
|
||||
{
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
},
|
||||
(error, stdout) => {
|
||||
if (error) {
|
||||
|
||||
@@ -50,7 +50,7 @@ export async function viewLogs(): Promise<number> {
|
||||
const pmc = getPackageManagerCommand();
|
||||
execSync(`${pmc.exec} nx-cloud upload-and-show-run-details`, {
|
||||
stdio: [0, 1, 2],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
if (!cloudUsed) {
|
||||
|
||||
@@ -54,7 +54,7 @@ export async function nxExecCommand(
|
||||
NX_PROJECT_ROOT_PATH:
|
||||
projectGraph.nodes?.[process.env.NX_TASK_TARGET_PROJECT]?.data?.root,
|
||||
},
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
} else {
|
||||
// nx exec is being ran inside of Nx's context
|
||||
@@ -105,7 +105,7 @@ async function runScriptAsNxTarget(
|
||||
projectGraph.nodes?.[projectName]?.data?.root
|
||||
)
|
||||
: workspaceRoot,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -132,7 +132,7 @@ function runTargetOnProject(
|
||||
execSync(command, {
|
||||
stdio: 'inherit',
|
||||
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ function write(patterns: string[]) {
|
||||
)}`,
|
||||
{
|
||||
stdio: [0, 1, 2],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -222,7 +222,7 @@ function write(patterns: string[]) {
|
||||
)} --parser json`,
|
||||
{
|
||||
stdio: [0, 1, 2],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -239,7 +239,7 @@ async function check(patterns: string[]): Promise<string[]> {
|
||||
return new Promise((resolve) => {
|
||||
exec(
|
||||
`node "${prettierPath}" --list-different ${patterns.join(' ')}`,
|
||||
{ encoding: 'utf-8', windowsHide: true },
|
||||
{ encoding: 'utf-8', windowsHide: false },
|
||||
(error, stdout) => {
|
||||
if (error) {
|
||||
// The command failed so there are files with different formatting. Prettier writes them to stdout, newline separated.
|
||||
|
||||
@@ -1270,6 +1270,6 @@ function getHelpTextFromTarget(
|
||||
|
||||
return execSync(command, {
|
||||
cwd: target.options?.cwd ?? workspaceRoot,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}).toString();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ export function setupIntegratedWorkspace(): void {
|
||||
execSync(`${pmc.exec} nx g @nx/angular:ng-add`, {
|
||||
stdio: [0, 1, 2],
|
||||
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ export async function getLegacyMigrationFunctionIfApplicable(
|
||||
output.log({ title: '📝 Setting up workspace' });
|
||||
execSync(`${pmc.exec} ${legacyMigrationCommand}`, {
|
||||
stdio: [0, 1, 2],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
if (useNxCloud) {
|
||||
@@ -149,7 +149,7 @@ async function installDependencies(
|
||||
}
|
||||
writeJsonFile(`package.json`, json);
|
||||
|
||||
execSync(pmc.install, { stdio: [0, 1, 2], windowsHide: true });
|
||||
execSync(pmc.install, { stdio: [0, 1, 2], windowsHide: false });
|
||||
}
|
||||
|
||||
async function resolvePackageVersion(
|
||||
|
||||
@@ -68,7 +68,7 @@ export function generateDotNxSetup(version?: string) {
|
||||
export function normalizeVersionForNxJson(pkg: string, version: string) {
|
||||
if (!valid(version)) {
|
||||
version = execSync(`npm view ${pkg}@${version} version`, {
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}).toString();
|
||||
}
|
||||
return version.trimEnd();
|
||||
|
||||
@@ -90,7 +90,7 @@ function performInstallation(
|
||||
cp.execSync('npm i', {
|
||||
cwd: path.dirname(installationPath),
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
} catch (e) {
|
||||
// revert possible changes to the current installation
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { execSync } from 'child_process';
|
||||
|
||||
export function checkForUncommittedChanges() {
|
||||
const gitResult = execSync('git status --porcelain', {
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}).toString();
|
||||
|
||||
const filteredResults = gitResult
|
||||
|
||||
@@ -70,7 +70,7 @@ function installDependencies(options: NormalizedOptions) {
|
||||
|
||||
execSync(`${options.pmc.addDev} ${dependencies.join(' ')}`, {
|
||||
stdio: [0, 1, 2],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ async function normalizeOptions(options: Options): Promise<NormalizedOptions> {
|
||||
};
|
||||
const isCRA5 = /^[^~]?5/.test(deps['react-scripts']);
|
||||
const npmVersion = execSync('npm -v', {
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}).toString();
|
||||
// Should remove this check 04/2023 once Node 14 & npm 6 reach EOL
|
||||
const npxYesFlagNeeded = !npmVersion.startsWith('6'); // npm 7 added -y flag to npx
|
||||
@@ -131,11 +131,11 @@ async function reorgnizeWorkspaceStructure(options: NormalizedOptions) {
|
||||
|
||||
execSync(`echo "node_modules" >> .gitignore`, {
|
||||
stdio: [0, 1, 2],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
execSync(`echo "dist" >> .gitignore`, {
|
||||
stdio: [0, 1, 2],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
process.chdir('..');
|
||||
@@ -177,7 +177,7 @@ function createTempWorkspace(options: NormalizedOptions) {
|
||||
} ${
|
||||
options.addE2e ? '--e2eTestRunner=playwright' : '--e2eTestRunner=none'
|
||||
}`,
|
||||
{ stdio: [0, 1, 2], windowsHide: true }
|
||||
{ stdio: [0, 1, 2], windowsHide: false }
|
||||
);
|
||||
|
||||
output.log({ title: '👋 Welcome to Nx!' });
|
||||
@@ -330,7 +330,7 @@ async function addBundler(options: NormalizedOptions) {
|
||||
|
||||
execSync(`echo "SKIP_PREFLIGHT_CHECK=true" > .env`, {
|
||||
stdio: [0, 1, 2],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,28 +68,28 @@ function deduceDefaultBase() {
|
||||
try {
|
||||
execSync(`git rev-parse --verify main`, {
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
return 'main';
|
||||
} catch {
|
||||
try {
|
||||
execSync(`git rev-parse --verify dev`, {
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
return 'dev';
|
||||
} catch {
|
||||
try {
|
||||
execSync(`git rev-parse --verify develop`, {
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
return 'develop';
|
||||
} catch {
|
||||
try {
|
||||
execSync(`git rev-parse --verify next`, {
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
return 'next';
|
||||
} catch {
|
||||
@@ -144,7 +144,11 @@ export function runInstall(
|
||||
repoRoot: string,
|
||||
pmc: PackageManagerCommands = getPackageManagerCommand()
|
||||
) {
|
||||
execSync(pmc.install, { stdio: [0, 1, 2], cwd: repoRoot, windowsHide: true });
|
||||
execSync(pmc.install, {
|
||||
stdio: [0, 1, 2],
|
||||
cwd: repoRoot,
|
||||
windowsHide: false,
|
||||
});
|
||||
}
|
||||
|
||||
export async function initCloud(
|
||||
|
||||
@@ -95,7 +95,7 @@ export async function initHandler(options: InitArgs) {
|
||||
} else {
|
||||
execSync(`npx --yes create-nx-workspace@${version} ${args}`, {
|
||||
stdio: [0, 1, 2],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export function installPlugins(
|
||||
{
|
||||
stdio: [0, 1, 2],
|
||||
cwd: repoRoot,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ function runMigration() {
|
||||
}
|
||||
execSync(`${p} _migrate ${process.argv.slice(3).join(' ')}`, {
|
||||
stdio: ['inherit', 'inherit', 'inherit'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -156,14 +156,14 @@ function nxCliPath() {
|
||||
execSync(pmc.preInstall, {
|
||||
cwd: tmpDir,
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
// if it's berry ensure we set the node_linker to node-modules
|
||||
if (packageManager === 'yarn' && pmc.ciInstall.includes('immutable')) {
|
||||
execSync('yarn config set nodeLinker node-modules', {
|
||||
cwd: tmpDir,
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -171,7 +171,7 @@ function nxCliPath() {
|
||||
execSync(pmc.install, {
|
||||
cwd: tmpDir,
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
// Set NODE_PATH so that these modules can be used for module resolution
|
||||
|
||||
@@ -1387,7 +1387,7 @@ function runInstall() {
|
||||
output.log({
|
||||
title: `Running '${pmCommands.install}' to make sure necessary packages are installed`,
|
||||
});
|
||||
execSync(pmCommands.install, { stdio: [0, 1, 2], windowsHide: true });
|
||||
execSync(pmCommands.install, { stdio: [0, 1, 2], windowsHide: false });
|
||||
}
|
||||
|
||||
export async function executeMigrations(
|
||||
|
||||
@@ -323,7 +323,7 @@ async function getCommitForVersionPlanFile(
|
||||
exec(
|
||||
`git log --diff-filter=A --pretty=format:"%s|%h|%an|%ae|%b" -n 1 -- ${rawVersionPlan.absolutePath}`,
|
||||
{
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
},
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
|
||||
@@ -77,7 +77,7 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
|
||||
}
|
||||
|
||||
// If no release groups have version plans enabled, it doesn't make sense to use the plan command only to set yourself up for an error at release time
|
||||
if (!releaseGroups.some((group) => group.versionPlans === true)) {
|
||||
if (!releaseGroups.some((group) => !!group.versionPlans)) {
|
||||
if (releaseGroups.length === 1) {
|
||||
output.warn({
|
||||
title: `Version plans are not enabled in your release configuration`,
|
||||
|
||||
@@ -10,7 +10,7 @@ export async function execCommand(
|
||||
...options,
|
||||
stdio: ['pipe', 'pipe', 'pipe'], // stdin, stdout, stderr
|
||||
encoding: 'utf-8',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
let stdout = '';
|
||||
|
||||
@@ -367,7 +367,7 @@ async function resolveGithubToken(hostname: string): Promise<string | null> {
|
||||
return execSync(`gh auth token`, {
|
||||
encoding: 'utf8',
|
||||
stdio: 'pipe',
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
}).trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function launchEditor(filePath: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const editorProcess = spawn(cmd, [...args, filePath], {
|
||||
stdio: 'inherit', // This will ensure the editor uses the current terminal
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
editorProcess.on('exit', (code) => {
|
||||
@@ -30,7 +30,7 @@ export async function launchEditor(filePath: string) {
|
||||
function getGitConfig(key): string | null {
|
||||
try {
|
||||
return execSync(`git config --get ${key}`, {
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
})
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
@@ -762,7 +762,7 @@ function runPreVersionCommand(
|
||||
maxBuffer: LARGE_BUFFER,
|
||||
stdio,
|
||||
env,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
} catch (e) {
|
||||
const title = verbose
|
||||
|
||||
@@ -134,7 +134,7 @@ async function printTargetRunHelpInternal(
|
||||
} else {
|
||||
const cp = exec(helpCommand, {
|
||||
env,
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
cp.on('exit', (code) => {
|
||||
process.exit(code);
|
||||
|
||||
@@ -132,7 +132,7 @@ class BatchCommandRunner extends BatchFunctionRunner {
|
||||
[this.projectNameEnv]: env[this.projectNameEnv],
|
||||
[this.fileChangesEnv]: env[this.fileChangesEnv],
|
||||
},
|
||||
windowsHide: true,
|
||||
windowsHide: false,
|
||||
});
|
||||
commandExec.on('close', () => {
|
||||
resolve();
|
||||
|
||||
@@ -507,7 +507,7 @@ export interface NxJsonConfiguration<T = '*' | string[]> {
|
||||
useInferencePlugins?: boolean;
|
||||
|
||||
/**
|
||||
* Set this to false to disable connection to Nx Cloud
|
||||
* Set this to true to disable connection to Nx Cloud
|
||||
*/
|
||||
neverConnectToCloud?: boolean;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user