docs(nxdev): remove flavors and versions
This commit is contained in:
parent
6dae831e55
commit
9827b5258c
@ -9,7 +9,7 @@ Compiles an application into an output directory named dist/ at the given output
|
||||
|
||||
## Usage
|
||||
|
||||
The `build` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `build` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Builds and serves an app, then runs end-to-end tests using the configured E2E te
|
||||
|
||||
## Usage
|
||||
|
||||
The `e2e` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `e2e` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Runs linting tools on application code in a given project folder using the confi
|
||||
|
||||
## Usage
|
||||
|
||||
The `lint` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `lint` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Builds and serves an application, rebuilding on file changes.
|
||||
|
||||
## Usage
|
||||
|
||||
The `serve` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `serve` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Runs unit tests in a project using the configured unit test runner.
|
||||
|
||||
## Usage
|
||||
|
||||
The `test` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `test` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Compiles an application into an output directory named dist/ at the given output
|
||||
|
||||
## Usage
|
||||
|
||||
The `build` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `build` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Builds and serves an app, then runs end-to-end tests using the configured E2E te
|
||||
|
||||
## Usage
|
||||
|
||||
The `e2e` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `e2e` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Runs linting tools on application code in a given project folder using the confi
|
||||
|
||||
## Usage
|
||||
|
||||
The `lint` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `lint` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Builds and serves an application, rebuilding on file changes.
|
||||
|
||||
## Usage
|
||||
|
||||
The `serve` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `serve` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Runs unit tests in a project using the configured unit test runner.
|
||||
|
||||
## Usage
|
||||
|
||||
The `test` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `test` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Compiles an application into an output directory named dist/ at the given output
|
||||
|
||||
## Usage
|
||||
|
||||
The `build` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `build` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Builds and serves an app, then runs end-to-end tests using the configured E2E te
|
||||
|
||||
## Usage
|
||||
|
||||
The `e2e` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `e2e` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Runs linting tools on application code in a given project folder using the confi
|
||||
|
||||
## Usage
|
||||
|
||||
The `lint` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `lint` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Builds and serves an application, rebuilding on file changes.
|
||||
|
||||
## Usage
|
||||
|
||||
The `serve` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `serve` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Runs unit tests in a project using the configured unit test runner.
|
||||
|
||||
## Usage
|
||||
|
||||
The `test` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `test` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Compiles an application into an output directory named dist/ at the given output
|
||||
|
||||
## Usage
|
||||
|
||||
The `build` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `build` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Builds and serves an app, then runs end-to-end tests using the configured E2E te
|
||||
|
||||
## Usage
|
||||
|
||||
The `e2e` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `e2e` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Runs linting tools on application code in a given project folder using the confi
|
||||
|
||||
## Usage
|
||||
|
||||
The `lint` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `lint` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Builds and serves an application, rebuilding on file changes.
|
||||
|
||||
## Usage
|
||||
|
||||
The `serve` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `serve` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Runs unit tests in a project using the configured unit test runner.
|
||||
|
||||
## Usage
|
||||
|
||||
The `test` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `test` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -101,34 +101,34 @@ myorg/
|
||||
|
||||
## See Also
|
||||
|
||||
- [Using DataPersistence](/{{version}}/angular/guides/misc-data-persistence)
|
||||
- [Using NgRx](/{{version}}/angular/guides/misc-ngrx)
|
||||
- [Upgrading an AngularJS application to Angular](/{{version}}/angular/guides/misc-upgrade)
|
||||
- [Using DataPersistence](/guides/misc-data-persistence)
|
||||
- [Using NgRx](/guides/misc-ngrx)
|
||||
- [Upgrading an AngularJS application to Angular](/guides/misc-upgrade)
|
||||
|
||||
## Executors / Builders
|
||||
|
||||
- [delegate-build](/{{framework}}/angular/delegate-build) - Delegates the build to a different target while supporting incremental builds.
|
||||
- [ng-packagr-lite](/{{framework}}/angular/ng-packagr-lite) - Builds a library with support for incremental builds.
|
||||
- [package](/{{framework}}/angular/package) - Builds and packages an Angular library to be distributed as an NPM package. It supports incremental builds.
|
||||
- [webpack-browser](/{{framework}}/angular/webpack-browser) - Builds a browser application with support for incremental builds and custom webpack configuration.
|
||||
- [delegate-build](/angular/delegate-build) - Delegates the build to a different target while supporting incremental builds.
|
||||
- [ng-packagr-lite](/angular/ng-packagr-lite) - Builds a library with support for incremental builds.
|
||||
- [package](/angular/package) - Builds and packages an Angular library to be distributed as an NPM package. It supports incremental builds.
|
||||
- [webpack-browser](/angular/webpack-browser) - Builds a browser application with support for incremental builds and custom webpack configuration.
|
||||
|
||||
## Generators
|
||||
|
||||
- [application](/{{framework}}/angular/application) - Creates an Angular application.
|
||||
- [convert-tslint-to-eslint](/{{framework}}/angular/convert-tslint-to-eslint) - Converts a project from TSLint to ESLint.
|
||||
- [downgrade-module](/{{framework}}/angular/downgrade-module) - Sets up a Downgrade Module.
|
||||
- [karma](/{{framework}}/angular/karma) - Adds Karma configuration to a workspace.
|
||||
- [karma-project](/{{framework}}/angular/karma-project) - Adds Karma configuration to a project.
|
||||
- [library](/{{framework}}/angular/library) - Creates an Angular library.
|
||||
- [move](/{{framework}}/angular/move) - Moves an Angular application or library to another folder within the workspace and updates the project configuration.
|
||||
- [ngrx](/{{framework}}/angular/ngrx) - Adds NgRx support to an application or library.
|
||||
- [setup-mfe](/{{framework}}/angular/setup-mfe) - Generate a Module Federation configuration for a given Angular application.
|
||||
- [stories](/{{framework}}/angular/stories) - Creates stories/specs for all components declared in a project.
|
||||
- [storybook-configuration](/{{framework}}/angular/storybook-configuration) - Adds Storybook configuration to a project.
|
||||
- [storybook-migrate-defaults-5-to-6](/{{framework}}/angular/storybook-migrate-defaults-5-to-6) - Generates default Storybook configuration files using Storybook version >=6.x specs, for projects that already have Storybook instances and configurations of versions <6.x.
|
||||
- [storybook-migrate-stories-to-6-2](/{{framework}}/angular/storybook-migrate-stories-to-6-2) - Migrates stories to match the new syntax in v6.2 where the component declaration should be in the default export.
|
||||
- [upgrade-module](/{{framework}}/angular/upgrade-module) - Sets up an Upgrade Module.
|
||||
- [web-worker](/{{framework}}/angular/web-worker) - Creates a Web Worker.
|
||||
- [application](/angular/application) - Creates an Angular application.
|
||||
- [convert-tslint-to-eslint](/angular/convert-tslint-to-eslint) - Converts a project from TSLint to ESLint.
|
||||
- [downgrade-module](/angular/downgrade-module) - Sets up a Downgrade Module.
|
||||
- [karma](/angular/karma) - Adds Karma configuration to a workspace.
|
||||
- [karma-project](/angular/karma-project) - Adds Karma configuration to a project.
|
||||
- [library](/angular/library) - Creates an Angular library.
|
||||
- [move](/angular/move) - Moves an Angular application or library to another folder within the workspace and updates the project configuration.
|
||||
- [ngrx](/angular/ngrx) - Adds NgRx support to an application or library.
|
||||
- [setup-mfe](/angular/setup-mfe) - Generate a Module Federation configuration for a given Angular application.
|
||||
- [stories](/angular/stories) - Creates stories/specs for all components declared in a project.
|
||||
- [storybook-configuration](/angular/storybook-configuration) - Adds Storybook configuration to a project.
|
||||
- [storybook-migrate-defaults-5-to-6](/angular/storybook-migrate-defaults-5-to-6) - Generates default Storybook configuration files using Storybook version >=6.x specs, for projects that already have Storybook instances and configurations of versions <6.x.
|
||||
- [storybook-migrate-stories-to-6-2](/angular/storybook-migrate-stories-to-6-2) - Migrates stories to match the new syntax in v6.2 where the component declaration should be in the default export.
|
||||
- [upgrade-module](/angular/upgrade-module) - Sets up an Upgrade Module.
|
||||
- [web-worker](/angular/web-worker) - Creates a Web Worker.
|
||||
|
||||
## Public API
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
In this tutorial you use Nx to build a full-stack application out of common libraries using modern technologies like Cypress and Nest.
|
||||
|
||||
> This tutorial uses several Nx plugins to provide a rich dev experience. **All the plugins are optional.** [Read about using Nx Core without plugins](/{{framework}}/getting-started/nx-core).
|
||||
> This tutorial uses several Nx plugins to provide a rich dev experience. **All the plugins are optional.** [Read about using Nx Core without plugins](/getting-started/nx-core).
|
||||
|
||||
## Create a new workspace
|
||||
|
||||
@ -130,7 +130,7 @@ Internally, the Nx CLI delegates to the Angular CLI when running commands or gen
|
||||
produces the same result as `ng serve`, and `nx build` produces the same results as `ng build`. However, the Nx CLI
|
||||
supports advanced capabilities that aren't supported by the Angular CLI. For instance, Nx's computation cache only
|
||||
works when using the Nx CLI. In other words, using `nx` instead `ng` results in the same output, but often performs
|
||||
a lot better. [Read more about Nx CLI and Angular CLI.](/{{framework}}/using-nx/nx-cli)
|
||||
a lot better. [Read more about Nx CLI and Angular CLI.](/using-nx/nx-cli)
|
||||
|
||||
## What's Next
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/owRAO75DIR4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe>
|
||||
|
||||
By default, Nx uses [Cypress](/{{framework}}/cypress/overview) to run E2E tests.
|
||||
By default, Nx uses [Cypress](/cypress/overview) to run E2E tests.
|
||||
|
||||
**Open `apps/todos-e2e/src/support/app.po.ts`.** It's a page object file that contains helpers for querying the page.
|
||||
|
||||
|
||||
@ -126,7 +126,7 @@ import { AppService } from './app.service';
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
We recommend using the [Nest](/{{framework}}/nest/overview) framework when creating node applications. Nest is a powerful framework which helps develop robust node applications. You can also use Express or any node libraries with Nx.
|
||||
We recommend using the [Nest](/nest/overview) framework when creating node applications. Nest is a powerful framework which helps develop robust node applications. You can also use Express or any node libraries with Nx.
|
||||
|
||||
In this case you have an application that registers a service and a controller. Services in Nest are responsible for the business logic, and controllers are responsible for implementing Http endpoints.
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ And notice the output:
|
||||
Nx read the output from cache instead of running the command for 1 out of 2 projects.
|
||||
```
|
||||
|
||||
Nx built `api` and retrieved `todos` from its computation cache. Read more about the cache here [here](/{{framework}}/using-nx/caching).
|
||||
Nx built `api` and retrieved `todos` from its computation cache. Read more about the cache here [here](/using-nx/caching).
|
||||
|
||||
> Add --parallel to any command, and Nx does most of the work in parallel.
|
||||
|
||||
|
||||
@ -13,6 +13,6 @@ In this tutorial you:
|
||||
|
||||
**Dive Deep:**
|
||||
|
||||
- [Nx CLI](/{{framework}}/using-nx/nx-cli)
|
||||
- [Computation Caching](/{{framework}}/using-nx/caching)
|
||||
- [Rebuilding What is Affected](/{{framework}}/using-nx/affected)
|
||||
- [Nx CLI](/using-nx/nx-cli)
|
||||
- [Computation Caching](/using-nx/caching)
|
||||
- [Rebuilding What is Affected](/using-nx/affected)
|
||||
|
||||
@ -9,7 +9,7 @@ Compiles an application into an output directory named dist/ at the given output
|
||||
|
||||
## Usage
|
||||
|
||||
The `build` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `build` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Builds and serves an app, then runs end-to-end tests using the configured E2E te
|
||||
|
||||
## Usage
|
||||
|
||||
The `e2e` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `e2e` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Runs linting tools on application code in a given project folder using the confi
|
||||
|
||||
## Usage
|
||||
|
||||
The `lint` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `lint` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Builds and serves an application, rebuilding on file changes.
|
||||
|
||||
## Usage
|
||||
|
||||
The `serve` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `serve` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Runs unit tests in a project using the configured unit test runner.
|
||||
|
||||
## Usage
|
||||
|
||||
The `test` command is a built-in alias to the [run command](/{{framework}}/cli/run).
|
||||
The `test` command is a built-in alias to the [run command](/cli/run).
|
||||
|
||||
These two commands are equivalent:
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ All of the core plugins are written using Nx Devkit, and you can use the same ut
|
||||
executors.
|
||||
|
||||
The Nx team maintains a core set of plugins for many application and tooling frameworks. You can also extend an Nx
|
||||
workspace by writing your own plugins. The [Nx Plugin](/{{framework}}/nx-plugin/overview) plugin provides guidance on
|
||||
workspace by writing your own plugins. The [Nx Plugin](/nx-plugin/overview) plugin provides guidance on
|
||||
how you can build your own custom plugins.
|
||||
|
||||
### Listing Nx plugins
|
||||
@ -63,15 +63,15 @@ Trees(ASTs), and more.
|
||||
### Pay as you go
|
||||
|
||||
As with most things in Nx, the core of Nx Devkit is very simple. It only uses language primitives and immutable
|
||||
objects (the tree being the only exception). See [Simplest Generator](/{{framework}}/generators/creating-files)
|
||||
and [Simplest Executor](/{{framework}}/executors/using-builders#simplest-executor) for examples on creating generators
|
||||
and executors. The [Using Executors](/{{framework}}/executors/using-builders)
|
||||
and [Using Generators](/{{framework}}/generators/using-schematics) guides also have additional information on executors
|
||||
objects (the tree being the only exception). See [Simplest Generator](/generators/creating-files)
|
||||
and [Simplest Executor](/executors/using-builders#simplest-executor) for examples on creating generators
|
||||
and executors. The [Using Executors](/executors/using-builders)
|
||||
and [Using Generators](/generators/using-schematics) guides also have additional information on executors
|
||||
and generators.
|
||||
|
||||
## Learn more
|
||||
|
||||
- [Using Nx Core Without Plugins](/{{framework}}/getting-started/nx-core)
|
||||
- [Workspace generators](/{{framework}}/generators/workspace-generators)
|
||||
- [Workspace executors](/{{framework}}/executors/creating-custom-builders)
|
||||
- [Using Nx Core Without Plugins](/getting-started/nx-core)
|
||||
- [Workspace generators](/generators/workspace-generators)
|
||||
- [Workspace executors](/executors/creating-custom-builders)
|
||||
- [Nx Community Plugins](/community)
|
||||
|
||||
@ -66,11 +66,11 @@ myorg/
|
||||
|
||||
## Executors / Builders
|
||||
|
||||
- [build](/{{framework}}/gatsby/build) - Builds a Gatsby application
|
||||
- [server](/{{framework}}/gatsby/server) - Builds and serves a Gatsby application
|
||||
- [build](/gatsby/build) - Builds a Gatsby application
|
||||
- [server](/gatsby/server) - Builds and serves a Gatsby application
|
||||
|
||||
## Generators
|
||||
|
||||
- [application](/{{framework}}/gatsby/application) - Create a Gatsby application
|
||||
- [component](/{{framework}}/gatsby/component) - Create a Gatsby component
|
||||
- [page](/{{framework}}/gatsby/page) - Create a Gatsby page
|
||||
- [application](/gatsby/application) - Create a Gatsby application
|
||||
- [component](/gatsby/component) - Create a Gatsby component
|
||||
- [page](/gatsby/page) - Create a Gatsby page
|
||||
|
||||
@ -4,19 +4,19 @@
|
||||
|
||||
Generators provide a way to automate many tasks you regularly perform as part of your development workflow. Whether it is scaffolding out components, features, ensuring libraries are generated and structured in a certain way, or updating your configuration files, generators help you standardize these tasks in a consistent, and predictable manner.
|
||||
|
||||
The [Workspace Generators](/{{framework}}/generators/workspace-generators) guide shows you how to create, run, and customize workspace generators within your Nx workspace.
|
||||
The [Workspace Generators](/generators/workspace-generators) guide shows you how to create, run, and customize workspace generators within your Nx workspace.
|
||||
|
||||
## Types of Generators
|
||||
|
||||
There are three main types of generators:
|
||||
|
||||
1. **Plugin Generators** are available when an Nx plugin has been installed in your workspace.
|
||||
2. **Workspace Generators** are generators that you can create for your own workspace. [Workspace generators](/{{framework}}/generators/workspace-generators) allow you to codify the processes that are unique to your own organization.
|
||||
3. **Update Generators** are invoked by Nx plugins when you [update Nx](/{{framework}}/using-nx/updating-nx) to keep your config files in sync with the latest versions of third party tools.
|
||||
2. **Workspace Generators** are generators that you can create for your own workspace. [Workspace generators](/generators/workspace-generators) allow you to codify the processes that are unique to your own organization.
|
||||
3. **Update Generators** are invoked by Nx plugins when you [update Nx](/using-nx/updating-nx) to keep your config files in sync with the latest versions of third party tools.
|
||||
|
||||
## Invoking Plugin Generators
|
||||
|
||||
Generators allow you to create or modify your codebase in a simple and repeatable way. Generators are invoked using the [`nx generate`](/{{framework}}/cli/generate) command.
|
||||
Generators allow you to create or modify your codebase in a simple and repeatable way. Generators are invoked using the [`nx generate`](/cli/generate) command.
|
||||
|
||||
```bash
|
||||
nx generate [plugin]:[generator-name] [options]
|
||||
|
||||
@ -18,10 +18,10 @@ the boilerplate. The vast majority of the features though will work the same way
|
||||
|
||||
These guides will help you get started:
|
||||
|
||||
- [Installing Nx CLI & creating a new Nx Workspace](/{{framework}}/getting-started/nx-setup)
|
||||
- [Adding Nx to an existing monorepo](/{{framework}}/migration/adding-to-monorepo)
|
||||
- [Using Nx without plugins](/{{framework}}/getting-started/nx-core)
|
||||
- [Nx and TypeScript](/{{framework}}/getting-started/nx-and-typescript)
|
||||
- [Installing Nx CLI & creating a new Nx Workspace](/getting-started/nx-setup)
|
||||
- [Adding Nx to an existing monorepo](/migration/adding-to-monorepo)
|
||||
- [Using Nx without plugins](/getting-started/nx-core)
|
||||
- [Nx and TypeScript](/getting-started/nx-and-typescript)
|
||||
- [Nx and React](/default/getting-started/nx-and-react)
|
||||
- [Nx and Angular](/default/getting-started/nx-and-angular)
|
||||
|
||||
@ -29,27 +29,27 @@ These guides will help you get started:
|
||||
|
||||
**Best-in-Class Support for Monorepos**
|
||||
|
||||
- [Smart rebuilds of affected projects](/{{framework}}/using-nx/affected)
|
||||
- [Computation caching](/{{framework}}/using-nx/caching)
|
||||
- [Distributed task execution](/{{framework}}/using-nx/dte)
|
||||
- [Code sharing and ownership management](/{{framework}}/structure/monorepo-tags)
|
||||
- [Smart rebuilds of affected projects](/using-nx/affected)
|
||||
- [Computation caching](/using-nx/caching)
|
||||
- [Distributed task execution](/using-nx/dte)
|
||||
- [Code sharing and ownership management](/structure/monorepo-tags)
|
||||
|
||||
**Integrated Development Experience**
|
||||
|
||||
- [High-quality editor plugins](/{{framework}}/using-nx/console) & [GitHub apps](https://github.com/apps/nx-cloud)
|
||||
- [Powerful code generators](/{{framework}}/generators/using-schematics)
|
||||
- [Workspace visualizations](/{{framework}}/structure/dependency-graph)
|
||||
- [High-quality editor plugins](/using-nx/console) & [GitHub apps](https://github.com/apps/nx-cloud)
|
||||
- [Powerful code generators](/generators/using-schematics)
|
||||
- [Workspace visualizations](/structure/dependency-graph)
|
||||
|
||||
**Supports Your Ecosystem**
|
||||
|
||||
- [Rich plugin ecosystem](/{{framework}}/getting-started/nx-devkit) from Nrwl and the [community](/community)
|
||||
- [Rich plugin ecosystem](/getting-started/nx-devkit) from Nrwl and the [community](/community)
|
||||
- Consistent dev experience for any framework
|
||||
- [Automatic upgrade to the latest versions of all frameworks and tools](/{{framework}}/using-nx/updating-nx)
|
||||
- [Automatic upgrade to the latest versions of all frameworks and tools](/using-nx/updating-nx)
|
||||
|
||||
## Learn While Doing
|
||||
|
||||
- [Using Nx without plugins](/{{framework}}/getting-started/nx-core)
|
||||
- [Nx and TypeScript](/{{framework}}/getting-started/nx-and-typescript)
|
||||
- [Using Nx without plugins](/getting-started/nx-core)
|
||||
- [Nx and TypeScript](/getting-started/nx-and-typescript)
|
||||
- [React: Interactive Nx Tutorial (with videos)](/default/react-tutorial/01-create-application)
|
||||
- [Node: Interactive Nx Tutorial (with videos)](/default/node-tutorial/01-create-application)
|
||||
- [Angular: Interactive Nx Tutorial (with videos)](/default/angular-tutorial/01-create-application)
|
||||
|
||||
@ -73,7 +73,7 @@ myorg/
|
||||
|
||||
`/libs/` contains the library projects. There are many kinds of libraries, and each library defines its own external API so that boundaries between libraries remain clear.
|
||||
|
||||
`/tools/` contains scripts that act on your code base. This could be database scripts, [custom executors](/{{framework}}/executors/creating-custom-builders), or [workspace generators](/{{framework}}/generators/workspace-generators).
|
||||
`/tools/` contains scripts that act on your code base. This could be database scripts, [custom executors](/executors/creating-custom-builders), or [workspace generators](/generators/workspace-generators).
|
||||
|
||||
`/workspace.json` lists every project in your workspace. (this file optional)
|
||||
|
||||
|
||||
@ -61,14 +61,14 @@ For example:
|
||||
2. `workspaceRoot/apps/my-app/.env` contains `AUTH_URL=https://prod-url.com/auth`
|
||||
3. Nx will first load the variables from `apps/my-app/.env.local` into the process. When it tries to load the variables from `apps/my-app/.env`, it will notice that `AUTH_URL` already exists, so it will ignore it.
|
||||
|
||||
We recommend nesting your **app** specific `env` files in `apps/your-app`, and creating workspace/root level `env` files for workspace-specific settings (like the [Nx Cloud token](/{{framework}}/using-nx/caching#distributed-computation-caching)).
|
||||
We recommend nesting your **app** specific `env` files in `apps/your-app`, and creating workspace/root level `env` files for workspace-specific settings (like the [Nx Cloud token](/using-nx/caching#distributed-computation-caching)).
|
||||
|
||||
### Pointing to custom env files
|
||||
|
||||
If you want to load variables from `env` files other than the ones listed above:
|
||||
|
||||
1. Use the [env-cmd](https://www.npmjs.com/package/env-cmd) package: `env-cmd -f .qa.env nx serve`
|
||||
2. Use the `envFile` option of the [run-commands](/{{framework}}/workspace/run-commands-executor#envfile) builder and execute your command inside of the builder
|
||||
2. Use the `envFile` option of the [run-commands](/workspace/run-commands-executor#envfile) builder and execute your command inside of the builder
|
||||
|
||||
## Using Environment Variables in index.html
|
||||
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
> In our teams we see a shift away from Lerna and a strong preference to use Nx for managing JavaScript-based monorepos.
|
||||
> [Thoughtworks Technology Radar 2021](https://www.thoughtworks.com/en-ca/radar/tools/nx)
|
||||
|
||||
- Want to know how to create a **new** Nx workspace and use Lerna/Yarn with it, check out [Using Nx Core Without Plugins](/{{framework}}/getting-started/nx-core).
|
||||
- Want to add Nx to an existing Lerna/Yarn/PNPM mononorepo, check out [Adding Nx to Lerna/Yarn/PNPM/NPM Workspace](/{{framework}}/migration/adding-to-monorepo).
|
||||
- Want to build a publishable TS/JS library, checkout [Nx and TypeScript](/{{framework}}/getting-started/nx-and-typescript).
|
||||
- Want to know how to create a **new** Nx workspace and use Lerna/Yarn with it, check out [Using Nx Core Without Plugins](/getting-started/nx-core).
|
||||
- Want to add Nx to an existing Lerna/Yarn/PNPM mononorepo, check out [Adding Nx to Lerna/Yarn/PNPM/NPM Workspace](/migration/adding-to-monorepo).
|
||||
- Want to build a publishable TS/JS library, checkout [Nx and TypeScript](/getting-started/nx-and-typescript).
|
||||
|
||||
This guide clarifies some misconceptions about how Nx and Lerna/Yarn relate.
|
||||
|
||||
@ -60,7 +60,7 @@ published to NPM.
|
||||
|
||||
### Misconception: Nx is "all-in"
|
||||
|
||||
While Nx does have many plugins, each of them is optional. If you check out [Using Nx Core Without Plugins](/{{framework}}/getting-started/nx-core), you will see that Nx at its core is very minimal. Much like VS Code, Nx is very minimal but can easily be extended by adding plugins. Saying this is akin to saying that VS Code is "all in". The fullness and richness of the experience depends on how many plugins you choose to use. You could install a lot of Nx Plugins that will do a lot of the heavy lifting in, for instance, connecting your Next.js, Storybook and Cypress. You could but you don't have to.
|
||||
While Nx does have many plugins, each of them is optional. If you check out [Using Nx Core Without Plugins](/getting-started/nx-core), you will see that Nx at its core is very minimal. Much like VS Code, Nx is very minimal but can easily be extended by adding plugins. Saying this is akin to saying that VS Code is "all in". The fullness and richness of the experience depends on how many plugins you choose to use. You could install a lot of Nx Plugins that will do a lot of the heavy lifting in, for instance, connecting your Next.js, Storybook and Cypress. You could but you don't have to.
|
||||
|
||||
### Misconception: Nx is configuration over convention
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ The most common additional options are:
|
||||
- `syntax` - NgRx introduced new creator functions for actions, reducers, and effects that provide the same type-safety with less code than action classes.
|
||||
- `facade` - Optional. If you prefer to further encapsulate NgRx from your components, add an injectable facade. See the blog [Better State Management with Facades](https://blog.nrwl.io/nrwl-nx-6-2-angular-6-1-and-better-state-management-e139da2cd074#cb93) for details.
|
||||
|
||||
See the [API Docs](/{{framework}}/angular/ngrx) for detailed descriptions of all the available options. Also visit the [NgRx](https://ngrx.io) website for more guides and documentation about the libraries.
|
||||
See the [API Docs](/angular/ngrx) for detailed descriptions of all the available options. Also visit the [NgRx](https://ngrx.io) website for more guides and documentation about the libraries.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||

|
||||
|
||||
Nx provides a holistic dev experience powered by an advanced CLI and editor plugins. It provides rich support for common tools like [Cypress](/{{version}}/{{framework}}/cypress/overview), Storybook, Jest, and more.
|
||||
Nx provides a holistic dev experience powered by an advanced CLI and editor plugins. It provides rich support for common tools like [Cypress](/cypress/overview), Storybook, Jest, and more.
|
||||
|
||||
In this guide we will show you how to develop [Next.js](https://nextjs.org/) applications with Nx.
|
||||
|
||||
@ -105,8 +105,8 @@ Nx allows you to create libraries with just one command. Some reasons you might
|
||||
- Publish a package to be used outside the monorepo
|
||||
- Better visualize the architecture using `npx nx dep-graph`
|
||||
|
||||
For more information on Nx libraries, see our documentation on [Creating Libraries](/{{version}}/{{framework}}/structure/creating-libraries)
|
||||
and [Library Types](/{{version}}/{{framework}}/structure/library-types).
|
||||
For more information on Nx libraries, see our documentation on [Creating Libraries](/structure/creating-libraries)
|
||||
and [Library Types](/structure/library-types).
|
||||
|
||||
To generate a new library run:
|
||||
|
||||
|
||||
@ -147,7 +147,7 @@ Nx is still able to pick those up natively, so you can still run `nx build mylib
|
||||
|
||||
Nx doesn’t provide an out of the box process for the publishing itself and leaves it to the developer to invoke the final command. The reason is that the actual publishing process can be very specific to your project and target you deploy to and might have a lot of custom pre-deployment setup (e.g. generating changelogs, determining semver etc.). Make sure to check out our [community page](/community) as there are a lot of community provided packages integrating into the publishing process.
|
||||
|
||||
However, integrating your custom publishing process with Nx can be pretty straightforward, especially with the help of Nx [run-commands](/{{framework}}/executors/run-commands-builder) and [“Target Dependencies”](/{{framework}}/configuration/projectjson#dependson).
|
||||
However, integrating your custom publishing process with Nx can be pretty straightforward, especially with the help of Nx [run-commands](/executors/run-commands-builder) and [“Target Dependencies”](/configuration/projectjson#dependson).
|
||||
|
||||
To add a new run-command to our project, we can leverage the `run-command` generator:
|
||||
|
||||
@ -204,4 +204,4 @@ We can however even automate this further by leveraging the `targetDependencies`
|
||||
}
|
||||
```
|
||||
|
||||
Now, just running `nx publish hello-tsc` will automatically run the `nx build hello-tsc` command first. And of course, if `build` has already run, it won't execute again, thanks to [Nx computation caching](/{{framework}}/using-nx/caching).
|
||||
Now, just running `nx publish hello-tsc` will automatically run the `nx build hello-tsc` command first. And of course, if `build` has already run, it won't execute again, thanks to [Nx computation caching](/using-nx/caching).
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Nx Devkit and Angular Devkit
|
||||
|
||||
> Note: this document covers the difference between Nx Devkit and Angular Devkit. See the [Nx Devkit](/{{framework}}/getting-started/nx-devkit) guide for more in-depth details about Nx Devkit.
|
||||
> Note: this document covers the difference between Nx Devkit and Angular Devkit. See the [Nx Devkit](/getting-started/nx-devkit) guide for more in-depth details about Nx Devkit.
|
||||
|
||||
Nx comes with a devkit to write generators and executors, but you can also use Angular devkit (schematics and builders). In other words, you can use an Angular schematic to implement a generator, and you can use an Angular builder to implement an executor.
|
||||
|
||||
@ -183,7 +183,7 @@ export default createBuilder<NextBuildBuilderOptions>(run);
|
||||
|
||||
### Notable Differences
|
||||
|
||||
- Nx Devkit executors return a Promise (or async iterable). If you want, you can always convert an observable to a promise or an async iterable. See [Using Rxjs Observables](/{{framework}}/executors/using-builders#using-rxjs-observables)
|
||||
- Nx Devkit executors return a Promise (or async iterable). If you want, you can always convert an observable to a promise or an async iterable. See [Using Rxjs Observables](/executors/using-builders#using-rxjs-observables)
|
||||
- Nx Devkit executors do not have to be wrapped using `createBuilder`.
|
||||
|
||||
The schema files for both Nx Devkit executors and Angular Builders are the same. Nx can run both of them in the same way.
|
||||
|
||||
@ -77,7 +77,7 @@ If you want to learn more, check out our article on [Distributing CI - Binning a
|
||||
|
||||
All the available Nx commands can be executed via the command line. But as your monorepo grows, with multiple teams and hundreds of projects, even just finding the project to run a command against can sometimes be difficult. Having a high quality IDE integration can be a time saver there.
|
||||
|
||||
- Nx has [VSCode](https://nx.dev/l/r/using-nx/console) and WebStorm/Intellij plugins.
|
||||
- Nx has [VSCode](https://nx.dev/using-nx/console) and WebStorm/Intellij plugins.
|
||||
- Turborepo doesn’t have any plugins, and the maintainer has indicated there's no intention to provide editor support.
|
||||
|
||||
Learn more [by watching this Egghead lesson](https://egghead.io/lessons/javascript-generate-new-projects-for-nx-with-nx-console).
|
||||
@ -125,7 +125,7 @@ At this point, Turborepo doesn’t do any of that. So for a monorepo of any non-
|
||||
Nx is like the **VSCode of build tools**. In VSCode you can get started with the plain, core VSCode setup and it would be fine. But if you want to enhance your experience, there's the option to add extensions for managing Git, Docker, Mongo etc. Similarly, **you don’t have to use all the Nx plugins or, say, the Nx Cloud GitHub integration.**
|
||||
Nx doesn’t replace any of your tools, and it’s not “all in”. You can start without any Nx plugins and Nx Cloud affordances, as with Turborepo. Or you can add them in as you go, both natively supported plugins by Nx as well as our growing [set of community plugins](https://nx.dev/community). **Turborepo isn’t pluggable**, so if you use the same analogy, you would have to use different tools (GitTower, DataGrip, Mongo Compass) to meet the same needs.
|
||||
|
||||
Read [this guide](https://nx.dev/l/r/getting-started/nx-core) to learn more about how to only use Nx Core.
|
||||
Read [this guide](https://nx.dev/getting-started/nx-core) to learn more about how to only use Nx Core.
|
||||
|
||||
## Tech and Performance
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
A monorepo is a single git repository that holds the source code for multiple applications and libraries, along with the tooling for them.
|
||||
|
||||
> If you are familiar with Lerna or Yarn workspaces, check out [this guide](/{{framework}}/guides/lerna-and-nx) (with a quick video) showing how to add Nx to a Lerna/Yarn workspace, what the difference is, when to use both and when to use just Nx.
|
||||
> If you are familiar with Lerna or Yarn workspaces, check out [this guide](/guides/lerna-and-nx) (with a quick video) showing how to add Nx to a Lerna/Yarn workspace, what the difference is, when to use both and when to use just Nx.
|
||||
|
||||
## What are the benefits of a monorepo?
|
||||
|
||||
@ -36,7 +36,7 @@ Nx provides tools to give you the benefits of a monorepo without the drawbacks o
|
||||
|
||||
- **Consistent Code Generation** - Generators allow you to customize and standardize organizational conventions and structure, removing the need to perform the same manual setup tasks repetitively.
|
||||
|
||||
- **Affected Commands** - [Nx’s affected commands](/{{framework}}/cli/affected) analyze your source code, the context of the changes, and only runs tasks on the affected projects impacted by the source code changes.
|
||||
- **Affected Commands** - [Nx’s affected commands](/cli/affected) analyze your source code, the context of the changes, and only runs tasks on the affected projects impacted by the source code changes.
|
||||
|
||||
- **Distributed Caching** - Nx provides local caching and support for distributed caching of command executions. With distributed caching, when someone on your team runs a command, everyone else gets access to those artifacts to speed up their command executions, bringing them down from minutes to seconds. Nx helps you scale your development to massive applications and libraries even more with distributed task execution and incremental builds.
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ On the other hand, the executor of a **buildable library**, performs a subset of
|
||||
nx g @nrwl/react:lib mylib --buildable
|
||||
```
|
||||
|
||||
Read more about [Publishable and Buildable Nx Libraries here.](/{{framework}}/structure/buildable-and-publishable-libraries)
|
||||
Read more about [Publishable and Buildable Nx Libraries here.](/structure/buildable-and-publishable-libraries)
|
||||
|
||||
## Nx computation cache and Nx Cloud
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ Watch this 3-min video to see how the command works and what next steps are:
|
||||
3. Set up a `tsconfig` file mapping if needed.
|
||||
4. Set up Nx Cloud (if you chose "yes").
|
||||
|
||||
> If you are familiar with Turborepo, check out [this guide](/{{framework}}/guides/turbo-and-nx). At this point, Nx can do anything Turbo can, and much more.
|
||||
> If you are familiar with Turborepo, check out [this guide](/guides/turbo-and-nx). At this point, Nx can do anything Turbo can, and much more.
|
||||
|
||||
## What You Get Right Away
|
||||
|
||||
@ -58,7 +58,7 @@ commands you run. They don't preserve animations and colors. We instrument Node.
|
||||
as is. When running, say, an npm script via Nx, the output will not be modified. The same is true when Nx restores the
|
||||
output from cache.
|
||||
|
||||
[Learn about computation caching.](/{{framework}}/using-nx/caching)
|
||||
[Learn about computation caching.](/using-nx/caching)
|
||||
|
||||
### Distributed Task Execution
|
||||
|
||||
@ -79,7 +79,7 @@ outputs as if it ran it locally.
|
||||
**Using Distributed Task Execution you can keep your CI fast, with practically no effort, regardless of the size of your
|
||||
workspace.**
|
||||
|
||||
[Learn more distributed task execution.](/{{framework}}/using-nx/dte)
|
||||
[Learn more distributed task execution.](/using-nx/dte)
|
||||
|
||||
### Affected Commands
|
||||
|
||||
@ -87,7 +87,7 @@ Nx automatically analyzes your workspace to know what projects are affected by y
|
||||
run: `nx affected --target=test` to see it in action. Often, Nx is able to do a better job detecting affected than other
|
||||
tools because it looks not just at the changed files but also at the nature of the changes.
|
||||
|
||||
[Learn more "affected".](/{{framework}}/using-nx/affected)
|
||||
[Learn more "affected".](/using-nx/affected)
|
||||
|
||||
### Workspace Visualization
|
||||
|
||||
@ -153,7 +153,7 @@ Nx + Lerna:
|
||||
}
|
||||
```
|
||||
|
||||
[Learn more about Nx CLI.](/{{framework}}/using-nx/nx-cli)
|
||||
[Learn more about Nx CLI.](/using-nx/nx-cli)
|
||||
|
||||
## Next Steps
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ Select `empty` when prompted:
|
||||
|
||||
## Exploring your workspace
|
||||
|
||||
Take a tour of your [Nx workspace](/{{framework}}/getting-started/nx-setup). There are some important areas to know about as you migrate.
|
||||
Take a tour of your [Nx workspace](/getting-started/nx-setup). There are some important areas to know about as you migrate.
|
||||
|
||||
### apps
|
||||
|
||||
@ -57,10 +57,10 @@ nx generate @nrwl/react:application my-application
|
||||
|
||||
There are a lot of options when creating your application. If you want to follow Nx recommendations, you can accept the defaults. If you have a well-established codebase, you can configure those options at the time of application generation. You can find documentation for these options for the different frameworks here:
|
||||
|
||||
- [Angular](/{{framework}}/angular/application)
|
||||
- [React](/{{framework}}/react/application)
|
||||
- [Angular](/angular/application)
|
||||
- [React](/react/application)
|
||||
|
||||
You may also find it useful to use the [Nx Console](/{{framework}}/using-nx/console) in Visual Studio Code. This will give you a visual way to generate your application with all of the options laid out in front of you.
|
||||
You may also find it useful to use the [Nx Console](/using-nx/console) in Visual Studio Code. This will give you a visual way to generate your application with all of the options laid out in front of you.
|
||||
|
||||
### Configuration files
|
||||
|
||||
@ -72,11 +72,11 @@ In general, you should not replace the configuration files provided for you. You
|
||||
|
||||
In addition to configuration files for external libraries, your Nx workspace will have configuration files for Nx itself. This will be `angular.json` for workspaces using the Angular CLI and `workspace.json` for workspaces using the Nx CLI. This file will define all of the individual projects in your workspace (of which your application is one) and the tasks available for them.
|
||||
|
||||
For example, your generated application should have four [tasks available](/{{framework}}/executors/using-builders) for it: `build`, `serve`, `lint`, and `test`. Each of these comes with its own configuration. If you find you need to adjust the configuration of a task for your codebase, this is the place to begin looking.
|
||||
For example, your generated application should have four [tasks available](/executors/using-builders) for it: `build`, `serve`, `lint`, and `test`. Each of these comes with its own configuration. If you find you need to adjust the configuration of a task for your codebase, this is the place to begin looking.
|
||||
|
||||
These workspace configuration files can seem a little long and intimidating. The Nx Console can help you navigate it more easily with its Workspace JSON panel. By clicking on a project in your workspace, it will navigate you to the right place in the workspace file to begin making edits.
|
||||
|
||||
Additionally, there is an `nx.json` file that contains metadata about your projects. [This metadata includes tags](/{{framework}}/structure/monorepo-tags) that can help you impose constraints on your applications and library dependencies.
|
||||
Additionally, there is an `nx.json` file that contains metadata about your projects. [This metadata includes tags](/structure/monorepo-tags) that can help you impose constraints on your applications and library dependencies.
|
||||
|
||||
## Migrating your code
|
||||
|
||||
@ -106,7 +106,7 @@ nx serve my-application
|
||||
|
||||
If this doesn’t work for you, you may need to add or modify some configuration on the `build` task in your workspace configuration file.
|
||||
|
||||
[Learn more about local serving](/{{framework}}/cli/serve)
|
||||
[Learn more about local serving](/cli/serve)
|
||||
|
||||
### Unit tests
|
||||
|
||||
@ -120,7 +120,7 @@ It is recommended that unit tests live next to the code they exercise and code s
|
||||
|
||||
Testing configuration files can be found in the root of your application as well as the workspace configuration file.
|
||||
|
||||
[Learn more about unit testing](/{{framework}}/cli/test)
|
||||
[Learn more about unit testing](/cli/test)
|
||||
|
||||
### End to End Tests
|
||||
|
||||
@ -132,7 +132,7 @@ nx e2e my-application-e2e
|
||||
|
||||
All of the configuration for your e2e tests should be in this directory.
|
||||
|
||||
[Learn more about end-to-end testing](/{{framework}}/cli/e2e)
|
||||
[Learn more about end-to-end testing](/cli/e2e)
|
||||
|
||||
### Linting
|
||||
|
||||
@ -152,7 +152,7 @@ nx lint my-application
|
||||
|
||||
Global configuration files for linting will be at the root of your workspace. Each application and library will extend those configuration files. Global configuration changes should be made in the root, while application-or-library-specific changes should occur in the application or library configuration files.
|
||||
|
||||
[Learn more about linting](/{{framework}}/cli/lint)
|
||||
[Learn more about linting](/cli/lint)
|
||||
|
||||
### Formatting
|
||||
|
||||
@ -162,19 +162,19 @@ Nx uses Prettier to ensure standard formatting across your codebase. Prettier co
|
||||
nx format:write
|
||||
```
|
||||
|
||||
[Learn more about formatting](/{{framework}}/cli/format-write)
|
||||
[Learn more about formatting](/cli/format-write)
|
||||
|
||||
### Adding tasks
|
||||
|
||||
Nx offers built-in tasks for the most common needs: `serve`, `build`, `test`, `e2e`, and `lint`. You likely have additional tasks that are needed to manage or deploy your codebase. These tasks might include deployment, i18n workflows, or uploading assets to CDNs. These tasks can be set up as scripts that you run manually with node, ts-node, or npm scripts. You can migrate those tasks over as-is, to begin with.
|
||||
|
||||
You should consider implementing them as Nx tasks which should be a quick transition with the `run-commands` builder. [The `run-commands` builder](/{{framework}}/executors/run-commands-builder) will allow you to run any custom commands you need as an Nx task. By implementing these commands in an Nx task, they are able to take advantage of the dependency graph in Nx and only run when necessary. They are also able to be cached and only be re-run when necessary.
|
||||
You should consider implementing them as Nx tasks which should be a quick transition with the `run-commands` builder. [The `run-commands` builder](/executors/run-commands-builder) will allow you to run any custom commands you need as an Nx task. By implementing these commands in an Nx task, they are able to take advantage of the dependency graph in Nx and only run when necessary. They are also able to be cached and only be re-run when necessary.
|
||||
|
||||
Your use-case may also be covered by one of our community plugins. Plugin authors are able to extend the functionality of Nx through our plugin API.
|
||||
|
||||
[Learn more about the `run-commands` builder](/{{framework}}/workspace/run-commands-executor)
|
||||
[Learn more about the `run-commands` builder](/workspace/run-commands-executor)
|
||||
|
||||
[Learn more about caching](/{{framework}}/using-nx/caching)
|
||||
[Learn more about caching](/using-nx/caching)
|
||||
|
||||
[Learn more about community plugins](/community)
|
||||
|
||||
@ -200,4 +200,4 @@ It’s important to remember: don’t just drop your code anywhere! Always gener
|
||||
|
||||
If you’re consolidating multiple repositories or libraries into a single Nx workspace, you may have concerns about code boundaries. Previously, you may have had well-established boundaries by separating code into different repositories or having a public API for a library. Nx features a tagging system that allows you to enforce these code boundaries in a granular way. Each project can be tagged, and you can constrain dependencies based on these tags.
|
||||
|
||||
[Learn more about tags and dependency constraints](/{{framework}}/structure/monorepo-tags)
|
||||
[Learn more about tags and dependency constraints](/structure/monorepo-tags)
|
||||
|
||||
@ -90,10 +90,10 @@ Your workspace is now powered by Nx! You can verify out that your application st
|
||||
|
||||
Learn more about the advantages of Nx in the following guides:
|
||||
|
||||
- [Using Cypress for e2e tests](/{{framework}}/cypress/overview)
|
||||
- [Using Jest for unit tests](/{{framework}}/jest/overview)
|
||||
- [Computation Caching](/{{framework}}/using-nx/caching)
|
||||
- [Rebuilding and Retesting What is Affected](/{{framework}}/using-nx/affected)
|
||||
- [Using Cypress for e2e tests](/cypress/overview)
|
||||
- [Using Jest for unit tests](/jest/overview)
|
||||
- [Computation Caching](/using-nx/caching)
|
||||
- [Rebuilding and Retesting What is Affected](/using-nx/affected)
|
||||
|
||||
## Transitioning Manually
|
||||
|
||||
@ -374,6 +374,6 @@ yarn lint
|
||||
|
||||
Learn more about the advantages of Nx in the following guides:
|
||||
|
||||
[Using Cypress for e2e tests](/{{framework}}/cypress/overview) \
|
||||
[Using Jest for unit tests](/{{framework}}/jest/overview) \
|
||||
[Rebuilding and Retesting What is Affected](/{{framework}}/using-nx/affected)
|
||||
[Using Cypress for e2e tests](/cypress/overview) \
|
||||
[Using Jest for unit tests](/jest/overview) \
|
||||
[Rebuilding and Retesting What is Affected](/using-nx/affected)
|
||||
|
||||
@ -371,7 +371,7 @@ But migrating AngularJS code means we need to switch some of our tools to a more
|
||||
npm install -D @nrwl/web babel-plugin-angularjs-annotate
|
||||
```
|
||||
|
||||
Nx already has most of what you need for webpack added as a dependency. `@nrwl/web` contains the [executors](/{{version}}/{{framework}}/executors/using-builders) we need to use to build and serve the application with webpack and
|
||||
Nx already has most of what you need for webpack added as a dependency. `@nrwl/web` contains the [executors](/executors/using-builders) we need to use to build and serve the application with webpack and
|
||||
`babel-plugin-angularjs-annotate` is going to accomplish the same thing that `browserify-ngannotate` previously did in gulp: add dependency injection annotations.
|
||||
|
||||
Start with a `webpack.config.js` file in your application’s root directory:
|
||||
|
||||
@ -6,7 +6,7 @@ You can either use a CLI tool to migrate your app automatically, or you can foll
|
||||
|
||||
**Note:** This guide has been updated for Nx 13 and may not work for earlier versions of Nx.
|
||||
|
||||
If you have a monorepo (more than one project in the same repo), follow the [Adding Nx to Lerna/Yarn/PNPM/NPM Workspace](https://nx.dev/l/r/migration/adding-to-monorepo) guide instead.
|
||||
If you have a monorepo (more than one project in the same repo), follow the [Adding Nx to Lerna/Yarn/PNPM/NPM Workspace](https://nx.dev/migration/adding-to-monorepo) guide instead.
|
||||
|
||||
## Using a tool that will do it for you
|
||||
|
||||
@ -18,8 +18,8 @@ Just `cd` into your Create-React-App (CRA) project and run the following command
|
||||
npx cra-to-nx
|
||||
```
|
||||
|
||||
Then just sit back and wait. After a while, take advantage of the [full magic of Nx](https://nx.dev/l/r/getting-started/intro).
|
||||
Start from [the commands mentioned in this article](https://nx.dev/l/r/migration/migration-cra#try-nx).
|
||||
Then just sit back and wait. After a while, take advantage of the [full magic of Nx](https://nx.dev/getting-started/intro).
|
||||
Start from [the commands mentioned in this article](https://nx.dev/migration/migration-cra#try-nx).
|
||||
|
||||
**Note:** The command will fail if you try execute it and you have uncommitted changes in your repository. Commit any local changes, and then try to run the command.
|
||||
|
||||
|
||||
@ -36,4 +36,4 @@ Note that for these files, the file history of the standalone project will be no
|
||||
|
||||
## Using `git mv`
|
||||
|
||||
If your standalone project was not an Nx workspace, it's likely that your migration work will also entail moving directories to match a typical Nx Workspace structure. You can find more information in the [Manual migration](/{{framework}}/migration/manual) page, but when migrating an existing project, you'll want to ensure that you use [`git mv`](https://git-scm.com/docs/git-mv) when moving a file or directory to ensure that file history from the old standalone repo is not lost!
|
||||
If your standalone project was not an Nx workspace, it's likely that your migration work will also entail moving directories to match a typical Nx Workspace structure. You can find more information in the [Manual migration](/migration/manual) page, but when migrating an existing project, you'll want to ensure that you use [`git mv`](https://git-scm.com/docs/git-mv) when moving a file or directory to ensure that file history from the old standalone repo is not lost!
|
||||
|
||||
@ -136,7 +136,7 @@ For a large organization it's crucial to establish how projects can depend on ea
|
||||
- Libraries with a broader scope (e.g., `shared/ui`) should not depend on the libraries with narrower scope (e.g., `happynrwlapp/search/utils-testing`).
|
||||
- Component libraries should only depend on other component libraries and utility libraries, but should not depend feature libraries.
|
||||
|
||||
Nx provides a feature called tags that can be used to codify and statically-enforce these rules. Read more about tags [here](/{{framework}}/structure/monorepo-tags).
|
||||
Nx provides a feature called tags that can be used to codify and statically-enforce these rules. Read more about tags [here](/structure/monorepo-tags).
|
||||
|
||||
## Code Ownership
|
||||
|
||||
@ -188,7 +188,7 @@ Note `all the projects affected by a PR/commit`. This is very important. Monorep
|
||||
- The performance of CI checks will degrade over time. The time it takes to run the CI checks should be proportional to the impact of the change, not the size of the repo.
|
||||
- We will be affected by the code your change didn’t touch
|
||||
|
||||
We should utilize `affected:*` commands to build and test projects. Read more about them [here](/{{framework}}/cli/affected).
|
||||
We should utilize `affected:*` commands to build and test projects. Read more about them [here](/cli/affected).
|
||||
|
||||
### Trunk-based development
|
||||
|
||||
|
||||
@ -6,4 +6,4 @@ The Nx Plugin for Next.js contains executors and generators for managing Next.js
|
||||
- Integration with building, serving, and exporting a Next.js application.
|
||||
- Integration with React libraries within the workspace.
|
||||
|
||||
See the [Next.js guide](/{{version}}/react/guides/nextjs) for information about using Next.js and Nx.
|
||||
See the [Next.js guide](/guides/nextjs) for information about using Next.js and Nx.
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
In this tutorial you use Nx to build a server application out of common libraries using modern technologies.
|
||||
|
||||
> This tutorial uses several Nx plugins to provide a rich dev experience. **All the plugins are optional.** [Read about using Nx Core without plugins](/{{framework}}/getting-started/nx-core).
|
||||
> This tutorial uses several Nx plugins to provide a rich dev experience. **All the plugins are optional.** [Read about using Nx Core without plugins](/getting-started/nx-core).
|
||||
|
||||
## Create a New Workspace
|
||||
|
||||
|
||||
@ -13,6 +13,6 @@ In this tutorial you:
|
||||
|
||||
**Dive Deep:**
|
||||
|
||||
- [Nx CLI](/{{framework}}/using-nx/nx-cli)
|
||||
- [Computation Caching](/{{framework}}/using-nx/caching)
|
||||
- [Rebuilding What is Affected](/{{framework}}/using-nx/affected)
|
||||
- [Nx CLI](/using-nx/nx-cli)
|
||||
- [Computation Caching](/using-nx/caching)
|
||||
- [Rebuilding What is Affected](/using-nx/affected)
|
||||
|
||||
@ -156,7 +156,7 @@ Finally, run `yarn`.
|
||||
The example uses Yarn to connect the two packages. Most of the time, however, there are better ways to do it. The React,
|
||||
Node, Angular plugins for Nx allow different projects in your workspace to import each other without having to maintain
|
||||
cumbersome `package.json` files. Instead, they use Webpack, Rollup and Jest plugins to enable this use case in a more
|
||||
elegant way. [Read about the relationship between Nx and Yarn/Lerna/PNPM](/{{framework}}/guides/lerna-and-nx).
|
||||
elegant way. [Read about the relationship between Nx and Yarn/Lerna/PNPM](/guides/lerna-and-nx).
|
||||
|
||||
## What Nx Core Provides
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ Nx plugins are npm packages that contain generators and executors to extend a Nx
|
||||
|
||||
> A list of plugins that is maintained by Nrwl is found in the [Nrwl/nx repo](https://github.com/nrwl/nx/tree/master/packages). \
|
||||
> A list of custom plugins created by the community is found in the [Community](/community) section.
|
||||
> Plugins are written using Nx Devkit. **Read [Nx Devkit](/{{framework}}/getting-started/nx-devkit) for more information.**
|
||||
> Plugins are written using Nx Devkit. **Read [Nx Devkit](/getting-started/nx-devkit) for more information.**
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/fC1-4fAZDP4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe>
|
||||
|
||||
|
||||
@ -69,22 +69,22 @@ myorg/
|
||||
|
||||
## See Also
|
||||
|
||||
- [Using Cypress](/{{framework}}/cypress/overview)
|
||||
- [Using Jest](/{{framework}}/jest/overview)
|
||||
- [Using Cypress](/cypress/overview)
|
||||
- [Using Jest](/jest/overview)
|
||||
- [Using Storybook](/default/storybook/overview-react)
|
||||
|
||||
## Executors / Builders
|
||||
|
||||
React applications are built using the executors from the `@nrwl/web` plugin.
|
||||
|
||||
- [build](/{{framework}}/web/build) - Builds a web components application
|
||||
- [dev-server](/{{framework}}/web/package) - Builds and serves a web application
|
||||
- [package](/{{framework}}/web/package) - Bundles artifacts for a buildable library that can be distributed as an NPM package.
|
||||
- [build](/web/build) - Builds a web components application
|
||||
- [dev-server](/web/package) - Builds and serves a web application
|
||||
- [package](/web/package) - Bundles artifacts for a buildable library that can be distributed as an NPM package.
|
||||
|
||||
## Generators
|
||||
|
||||
- [application](/{{framework}}/react/application) - Create an React application
|
||||
- [component](/{{framework}}/react/component) - Create an React component
|
||||
- [library](/{{framework}}/react/library) - Create an React library
|
||||
- [redux](/{{framework}}/react/redux) - Generate a Redux slice for a project
|
||||
- [storybook-configuration](/{{framework}}/react/storybook-configuration) - Set up Storybook for a react library
|
||||
- [application](/react/application) - Create an React application
|
||||
- [component](/react/component) - Create an React component
|
||||
- [library](/react/library) - Create an React library
|
||||
- [redux](/react/redux) - Generate a Redux slice for a project
|
||||
- [storybook-configuration](/react/storybook-configuration) - Set up Storybook for a react library
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
|
||||
In this tutorial you use Nx to build a full-stack application out of common libraries using modern technologies.
|
||||
|
||||
> Next.js: Nx also has first-class Next.js support. Read more about it [here](/{{framework}}/next/overview)
|
||||
> Next.js: Nx also has first-class Next.js support. Read more about it [here](/next/overview)
|
||||
|
||||
> This tutorial uses several Nx plugins to provide a rich dev experience. **All the plugins are optional.** [Read about using Nx Core without plugins](/{{framework}}/getting-started/nx-core).
|
||||
> This tutorial uses several Nx plugins to provide a rich dev experience. **All the plugins are optional.** [Read about using Nx Core without plugins](/getting-started/nx-core).
|
||||
|
||||
## Create a new workspace
|
||||
|
||||
|
||||
@ -71,6 +71,6 @@ Options:
|
||||
--help Show available options for project target.
|
||||
```
|
||||
|
||||
It helps with good editor integration (see [VSCode Support](/{{framework}}/using-nx/console#nx-console-for-vscode)).
|
||||
It helps with good editor integration (see [VSCode Support](/using-nx/console#nx-console-for-vscode)).
|
||||
|
||||
But, most importantly, it provides a holistic dev experience regardless of the tools used, and enables advanced build features like distributed computation caching and distributed builds.
|
||||
|
||||
@ -52,6 +52,6 @@ Based on the state of the source code and the environment, Nx figured out that i
|
||||
Nx read the output from cache instead of running the command for 1 out of 2 projects.
|
||||
```
|
||||
|
||||
Nx built `api` and retrieved `todos` from its computation cache. Read more about the cache [here](/{{framework}}/using-nx/caching).
|
||||
Nx built `api` and retrieved `todos` from its computation cache. Read more about the cache [here](/using-nx/caching).
|
||||
|
||||
> Add --parallel to any command, and Nx does most of the work in parallel.
|
||||
|
||||
@ -13,6 +13,6 @@ In this tutorial you:
|
||||
|
||||
**Dive Deep:**
|
||||
|
||||
- [Nx CLI](/{{framework}}/using-nx/nx-cli)
|
||||
- [Computation Caching](/{{framework}}/using-nx/caching)
|
||||
- [Rebuilding What is Affected](/{{framework}}/using-nx/affected)
|
||||
- [Nx CLI](/using-nx/nx-cli)
|
||||
- [Computation Caching](/using-nx/caching)
|
||||
- [Rebuilding What is Affected](/using-nx/affected)
|
||||
|
||||
@ -42,7 +42,7 @@ For each project for which you want to enable `make`, add a target in `workspace
|
||||
}
|
||||
```
|
||||
|
||||
For more information, see the [run-commands api doc](/{{framework}}/workspace/run-commands-executor).
|
||||
For more information, see the [run-commands api doc](/workspace/run-commands-executor).
|
||||
|
||||
##### 3. Trigger the executor from the terminal
|
||||
|
||||
@ -58,4 +58,4 @@ To run the executor for all affected projects:
|
||||
nx affected --target=make
|
||||
```
|
||||
|
||||
For more information, see the [nx affected](/{{framework}}/cli/affected).
|
||||
For more information, see the [nx affected](/cli/affected).
|
||||
|
||||
@ -59,7 +59,7 @@ Each executor definition has an `executor` property and, optionally, an `options
|
||||
|
||||
## Running executors
|
||||
|
||||
The [`nx run`](/{{framework}}/cli/run) cli command (or the shorthand versions) can be used to run executors.
|
||||
The [`nx run`](/cli/run) cli command (or the shorthand versions) can be used to run executors.
|
||||
|
||||
```bash
|
||||
nx run [project]:[command]
|
||||
@ -245,7 +245,7 @@ The `runExecutor` utility will find the target in the configuration, find the ex
|
||||
- `readTargetOptions` -- Reads and combines options for a given target.
|
||||
- `runExecutor` -- Constructs options and invokes an executor.
|
||||
|
||||
See more helper functions in the [Devkit API Docs](/{{framework}}/nx-devkit/index#functions)
|
||||
See more helper functions in the [Devkit API Docs](/nx-devkit/index#functions)
|
||||
|
||||
## Using RxJS observables
|
||||
|
||||
@ -274,5 +274,5 @@ export default async function (opts) {
|
||||
|
||||
## See Also
|
||||
|
||||
- [`nx affected`](/{{framework}}/cli/affected)
|
||||
- [`nx run-many`](/{{framework}}/cli/run-many)
|
||||
- [`nx affected`](/cli/affected)
|
||||
- [`nx run-many`](/cli/run-many)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Affected
|
||||
|
||||
> Before reading this guide, check out the [mental model guide](/{{framework}}/using-nx/mental-model). It will help you understand how computation caching fits into the rest of Nx.
|
||||
> Before reading this guide, check out the [mental model guide](/using-nx/mental-model). It will help you understand how computation caching fits into the rest of Nx.
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Computation Caching
|
||||
|
||||
> Before reading this guide, check out the [mental model guide](/{{framework}}/using-nx/mental-model). It will help you understand how computation caching fits into the rest of Nx.
|
||||
> Before reading this guide, check out the [mental model guide](/using-nx/mental-model). It will help you understand how computation caching fits into the rest of Nx.
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Distributed Task Execution
|
||||
|
||||
> Before reading this guide, check out the [mental model guide](/{{framework}}/using-nx/mental-model). It will help you understand how computation caching fits into the rest of Nx.
|
||||
> Before reading this guide, check out the [mental model guide](/using-nx/mental-model). It will help you understand how computation caching fits into the rest of Nx.
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ Run the `nx help` command to see a full list of commands in the Nx CLI.
|
||||
|
||||
## Acting on Code
|
||||
|
||||
The [`nx run` command](/{{framework}}/cli/run) executes a target on a single project. For convenience, you can also
|
||||
The [`nx run` command](/cli/run) executes a target on a single project. For convenience, you can also
|
||||
run `nx [target] [project]` which is an alias to `nx run [project]:[target]`.
|
||||
|
||||
```bash
|
||||
@ -28,21 +28,21 @@ nx build my-js-app
|
||||
|
||||
However, `nx build` is only an abstraction over what it means to "build" projects rather than tied to a certain
|
||||
implementation. For instance, if you have a `project.json` file defining `build` using
|
||||
a **[executor](/{{framework}}/executors/using-builders)**, that executor will be invoked. If you don't specify an
|
||||
a **[executor](/executors/using-builders)**, that executor will be invoked. If you don't specify an
|
||||
executor for the build target, `nx build my-react-app` will invoke the `build` npm script in the project's folder. Every
|
||||
argument you pass into `run` will be forwarded to the executor or the npm script.
|
||||
|
||||
Along with running a target on a single project, Nx provides some commands to run the same target across multiple
|
||||
projects.
|
||||
|
||||
The [`nx run-many` command](/{{framework}}/cli/run-many) runs the same target name across a list of projects.
|
||||
The [`nx run-many` command](/cli/run-many) runs the same target name across a list of projects.
|
||||
|
||||
```bash
|
||||
nx run-many --target=build --projects=app1,app2
|
||||
nx run-many --target=test --all # Runs all projects that have a test target, use this sparingly.
|
||||
```
|
||||
|
||||
The [`nx affected` command](/{{framework}}/cli/affected) isolates set projects that may have changed in behavior and
|
||||
The [`nx affected` command](/cli/affected) isolates set projects that may have changed in behavior and
|
||||
runs a target across them. This is more efficient than running all projects every time.
|
||||
|
||||
```bash
|
||||
@ -51,7 +51,7 @@ nx affected --target=build
|
||||
|
||||
## Modifying Code
|
||||
|
||||
The [`nx generate` command](/{{framework}}/cli/generate) generates and modifies code.
|
||||
The [`nx generate` command](/cli/generate) generates and modifies code.
|
||||
|
||||
```bash
|
||||
nx generate @nrwl/js:lib my-lib
|
||||
@ -60,12 +60,12 @@ nx generate @nrwl/react:storybook-configuration shared-button # Configures story
|
||||
```
|
||||
|
||||
Again, like `nx run`, `nx generate` is only an abstraction over generating code. `nx generate` can generate anything you
|
||||
want via **generators**. **[Generators](/{{framework}}/generators/using-schematics)** can be installed as part of a
|
||||
want via **generators**. **[Generators](/generators/using-schematics)** can be installed as part of a
|
||||
plugin or developed locally within an Nx workspace to fit the needs of your organization.
|
||||
|
||||
A [Workspace Generator](/{{framework}}/generators/workspace-generators) is custom generator for your
|
||||
A [Workspace Generator](/generators/workspace-generators) is custom generator for your
|
||||
workspace. `nx generate workspace-generator my-generator` generates a workspace generator which can be run with
|
||||
the [`nx workspace-generator` command](/{{framework}}/cli/workspace-generator). This can be useful to allow your
|
||||
the [`nx workspace-generator` command](/cli/workspace-generator). This can be useful to allow your
|
||||
organization to consistently generate projects according to your own standards.
|
||||
|
||||
```bash
|
||||
@ -73,9 +73,9 @@ nx workspace-generator my-generator
|
||||
```
|
||||
|
||||
Upgrading a package is not always as simple as bumping the version in `package.json`.
|
||||
The [`nx migrate` command](/{{framework}}/cli/migrate) facilitates not only managing package versions but also runs
|
||||
The [`nx migrate` command](/cli/migrate) facilitates not only managing package versions but also runs
|
||||
migrations specified by package maintainers. See
|
||||
the [full guide to updating Nx](/{{framework}}/using-nx/updating-nx).
|
||||
the [full guide to updating Nx](/using-nx/updating-nx).
|
||||
|
||||
```bash
|
||||
nx migrate latest # Updates the version of Nx in `package.json` and schedules migrations to be run
|
||||
@ -85,11 +85,11 @@ nx migrate --run-migrations # Runs the migrations scheduled by the previous comm
|
||||
## Understanding the codebase
|
||||
|
||||
Nx creates and maintains a dependency graph between projects based on import statements in your code and uses that
|
||||
information to run executors only on the [affected](/{{framework}}/cli/affected) projects in a codebase. A visual
|
||||
version of the [dependency graph](/{{framework}}/structure/dependency-graph) is also available to help developers
|
||||
information to run executors only on the [affected](/cli/affected) projects in a codebase. A visual
|
||||
version of the [dependency graph](/structure/dependency-graph) is also available to help developers
|
||||
understand the architecture of the codebase.
|
||||
|
||||
The [`nx dep-graph` command](/{{framework}}/cli/dep-graph) displays this dependency graph in a web browser for you to
|
||||
The [`nx dep-graph` command](/cli/dep-graph) displays this dependency graph in a web browser for you to
|
||||
explore.
|
||||
|
||||
```bash
|
||||
@ -98,7 +98,7 @@ nx dep-graph --watch # Updates the browser as code is changed
|
||||
nx affected:dep-graph # Highlights projects which may have changed in behavior
|
||||
```
|
||||
|
||||
The [`nx list` command](/{{framework}}/cli/list) lists the currently installed Nx plugins and other available plugins.
|
||||
The [`nx list` command](/cli/list) lists the currently installed Nx plugins and other available plugins.
|
||||
The `nx list` command can list the generators and executors that are available for a plugin.
|
||||
|
||||
**List installed plugins:**
|
||||
|
||||
@ -68,15 +68,15 @@ myorg/
|
||||
|
||||
## See Also
|
||||
|
||||
- [Using Cypress](/{{framework}}/cypress/overview)
|
||||
- [Using Jest](/{{framework}}/cypress/overview)
|
||||
- [Using Cypress](/cypress/overview)
|
||||
- [Using Jest](/cypress/overview)
|
||||
|
||||
## Executors / Builders
|
||||
|
||||
- [build](/{{framework}}/web/build) - Builds a web components application
|
||||
- [dev-server](/{{framework}}/web/dev-server) - Builds and serves a web application
|
||||
- [package](/{{framework}}/web/package) - Bundles artifacts for a buildable library that can be distributed as an NPM package.
|
||||
- [build](/web/build) - Builds a web components application
|
||||
- [dev-server](/web/dev-server) - Builds and serves a web application
|
||||
- [package](/web/package) - Bundles artifacts for a buildable library that can be distributed as an NPM package.
|
||||
|
||||
## Generators
|
||||
|
||||
- [application](/{{framework}}/web/application) - Create an Web Components application
|
||||
- [application](/web/application) - Create an Web Components application
|
||||
|
||||
@ -4,12 +4,12 @@ The workspace plugin contains executors and generators that are useful for any N
|
||||
|
||||
## Generators
|
||||
|
||||
- [library](/{{framework}}/workspace/library) - Create a plain typescript library
|
||||
- [move](/{{framework}}/workspace/move) - Move a project to another directory and update all references
|
||||
- [remove](/{{framework}}/workspace/remove) - Remove a project from the workspace
|
||||
- [run-commands](/{{framework}}/workspace/run-commands-executor) - Add a target to a project that uses the run-commands executor
|
||||
- [workspace-generator](/{{framework}}/workspace/workspace-generator) - Scaffold a custom generator for use within your workspace
|
||||
- [library](/workspace/library) - Create a plain typescript library
|
||||
- [move](/workspace/move) - Move a project to another directory and update all references
|
||||
- [remove](/workspace/remove) - Remove a project from the workspace
|
||||
- [run-commands](/workspace/run-commands-executor) - Add a target to a project that uses the run-commands executor
|
||||
- [workspace-generator](/workspace/workspace-generator) - Scaffold a custom generator for use within your workspace
|
||||
|
||||
## Executors / Builders
|
||||
|
||||
- [run-commands](/{{framework}}/workspace/run-commands-executor) - Execute an arbitrary command line script
|
||||
- [run-commands](/workspace/run-commands-executor) - Execute an arbitrary command line script
|
||||
|
||||
@ -17,7 +17,7 @@ One typical scenario for this may be that you use Nx to develop your organizatio
|
||||
|
||||
A normal Nx library - let’s call it "workspace library" - is not made for building or publishing. Rather it only includes common lint and test targets in its `angular.json` or `workspace.json`. These libraries are directly referenced from one of the monorepo’s applications and built together with them.
|
||||
|
||||
Keep in mind that the `--publishable` flag does not enable automatic publishing. Rather it adds to your Nx workspace library a builder target that **compiles** and **bundles** your app. The resulting artifact will be ready to be published to some registry (e.g. [npm](https://npmjs.com/)). By having that builder, you can invoke the build via a command like: `nx build mylib` (where "mylib" is the name of the lib) which will then produce an optimized bundle in the `dist/mylib` folder. Nx [also analyzes](/{{framework}}/angular/package#updatebuildableprojectdepsinpackagejson) the library’s dependencies and automatically compiles the dependencies in the resulting `package.json` file.
|
||||
Keep in mind that the `--publishable` flag does not enable automatic publishing. Rather it adds to your Nx workspace library a builder target that **compiles** and **bundles** your app. The resulting artifact will be ready to be published to some registry (e.g. [npm](https://npmjs.com/)). By having that builder, you can invoke the build via a command like: `nx build mylib` (where "mylib" is the name of the lib) which will then produce an optimized bundle in the `dist/mylib` folder. Nx [also analyzes](/angular/package#updatebuildableprojectdepsinpackagejson) the library’s dependencies and automatically compiles the dependencies in the resulting `package.json` file.
|
||||
|
||||
One particularity when generating a library with `--publishable` is that it requires you to also provide an `--importPath`. Your import path is the actual scope of your distributable package (e.g.: `@myorg/mylib`) - which needs to be a [valid npm package name](https://docs.npmjs.com/files/package.json#name).
|
||||
|
||||
@ -29,6 +29,6 @@ For more details on the mechanics, remember that Nx is an open source project, s
|
||||
|
||||
Buildable libraries are similar to "publishable libraries" described above. Their scope however is not to distribute or publish them to some external registry. Thus they might not be optimized for bundling and distribution.
|
||||
|
||||
Buildable libraries are mostly used for producing some pre-compiled output that can be directly referenced from an Nx workspace application without the need to again compile it. A typical scenario is to leverage Nx’s [incremental building](/{{framework}}/ci/incremental-builds) capabilities.
|
||||
Buildable libraries are mostly used for producing some pre-compiled output that can be directly referenced from an Nx workspace application without the need to again compile it. A typical scenario is to leverage Nx’s [incremental building](/ci/incremental-builds) capabilities.
|
||||
|
||||
For more details on the mechanics, remember that Nx is an open source project, so you can see the actual impact of the generator by looking at the source code (the best starting point is probably `packages/<framework>/src/generators/library/library.ts`).
|
||||
|
||||
@ -16,7 +16,7 @@ The `nx dep-graph` command generates a graph of how apps and libraries depend on
|
||||
|
||||
### 3. Enforcing Constraints
|
||||
|
||||
You can enforce constraints on how different types of libraries depend on each other [using tags](/{{framework}}/structure/monorepo-tags). Following pre-determined conventions on what kind of code can go in different types of libraries allows your tagging system to enforce good architectural patterns.
|
||||
You can enforce constraints on how different types of libraries depend on each other [using tags](/structure/monorepo-tags). Following pre-determined conventions on what kind of code can go in different types of libraries allows your tagging system to enforce good architectural patterns.
|
||||
|
||||
Also, each library defines its own API, which allows for encapsulating logic that other parts of codebase can not access. You can even use a [CODEOWNERS file](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners) to assign ownership of a certain library to a user or team.
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ Libraries should be grouped by _scope_. A library's scope is either application
|
||||
|
||||
## Move Generator
|
||||
|
||||
Don't be too anxious about choosing the exact right folder structure from the beginning. Libraries can be moved or renamed using the [`@nrwl/workspace:move` generator](/{{framework}}/workspace/move).
|
||||
Don't be too anxious about choosing the exact right folder structure from the beginning. Libraries can be moved or renamed using the [`@nrwl/workspace:move` generator](/workspace/move).
|
||||
|
||||
For instance, if a library under the `booking` folder is now being shared by multiple apps, you can move it to the shared folder like this:
|
||||
|
||||
@ -14,7 +14,7 @@ nx g move --project booking-some-library shared/some-library
|
||||
|
||||
## Remove Generator
|
||||
|
||||
Similarly, if you no longer need a library, you can remove it with the [`@nrwl/workspace:remove` generator](/{{framework}}/workspace/remove).
|
||||
Similarly, if you no longer need a library, you can remove it with the [`@nrwl/workspace:remove` generator](/workspace/remove).
|
||||
|
||||
```bash
|
||||
nx g remove booking-some-library
|
||||
|
||||
@ -115,7 +115,7 @@ If a file hasn't changed since the last invocation, it doesn't need to be reanal
|
||||
|
||||
## Visualizing the Project Graph
|
||||
|
||||
You can then visualize the project graph as described [here](/{{framework}}/structure/dependency-graph). However, there is a cache that Nx uses to avoid recalculating the project graph as much as possible. As you develop your project graph plugin, it might be a good idea to set the following environment variable to disable the project graph cache: `NX_CACHE_PROJECT_GRAPH=false`.
|
||||
You can then visualize the project graph as described [here](/structure/dependency-graph). However, there is a cache that Nx uses to avoid recalculating the project graph as much as possible. As you develop your project graph plugin, it might be a good idea to set the following environment variable to disable the project graph cache: `NX_CACHE_PROJECT_GRAPH=false`.
|
||||
|
||||
## Example Project Graph Plugin
|
||||
|
||||
|
||||
@ -6,82 +6,21 @@ describe('DocumentsApi', () => {
|
||||
|
||||
describe('getDocument', () => {
|
||||
it('should retrieve documents that exist', () => {
|
||||
const result = api.getDocument(
|
||||
api.getDefaultVersion(),
|
||||
api.getDefaultFlavor(),
|
||||
['getting-started', 'intro']
|
||||
);
|
||||
const result = api.getDocument(['getting-started', 'intro']);
|
||||
|
||||
expect(result.filePath).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should throw error if segments do not match a file', () => {
|
||||
expect(() =>
|
||||
api.getDocument(
|
||||
api.getDefaultVersion(),
|
||||
{ id: 'vue', alias: 'v', name: 'Vue', path: 'does not exist' },
|
||||
['does', 'not', 'exist']
|
||||
)
|
||||
).toThrow();
|
||||
expect(() => api.getDocument(['does', 'not', 'exist'])).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDocumentsRoot', () => {
|
||||
it('should support latest', () => {
|
||||
expect(api.getDocumentsRoot('latest')).toMatch(
|
||||
/nx-dev\/nx-dev\/public\/documentation\/latest/
|
||||
);
|
||||
});
|
||||
|
||||
it('should support previous', () => {
|
||||
expect(api.getDocumentsRoot('previous')).toMatch(
|
||||
/nx-dev\/nx-dev\/public\/documentation\/previous/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVersions', () => {
|
||||
it('should return versions data', () => {
|
||||
expect(api.getVersions()).toEqual([
|
||||
expect.objectContaining({ id: 'latest' }),
|
||||
expect.objectContaining({ id: 'previous' }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFlavors', () => {
|
||||
it('should return versions data', () => {
|
||||
expect(api.getFlavors()).toEqual([
|
||||
expect.objectContaining({ id: 'angular' }),
|
||||
expect.objectContaining({ id: 'react' }),
|
||||
expect.objectContaining({ id: 'node' }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStaticDocumentPaths', () => {
|
||||
const paths = api
|
||||
.getVersions()
|
||||
.flatMap((v) =>
|
||||
api.getFlavors().flatMap((f) => api.getStaticDocumentPaths(v, f))
|
||||
);
|
||||
const urls = paths.map((p) => p.params.segments.join('/'));
|
||||
|
||||
it.each`
|
||||
version | flavor
|
||||
${'l'} | ${'r'}
|
||||
${'l'} | ${'a'}
|
||||
${'l'} | ${'n'}
|
||||
${'p'} | ${'r'}
|
||||
${'p'} | ${'a'}
|
||||
${'p'} | ${'n'}
|
||||
`('should return paths for all flavors', ({ version, flavor }) =>
|
||||
expect(urls).toContainEqual(
|
||||
expect.stringMatching(`${version}/${flavor}/getting-started`)
|
||||
)
|
||||
);
|
||||
|
||||
it('should return generic paths for the latest version', () =>
|
||||
expect(urls).toContainEqual(expect.stringMatching(/^getting-started\//)));
|
||||
});
|
||||
// describe('getStaticDocumentPaths', () => {
|
||||
// const paths = api.getStaticDocumentPaths();
|
||||
// const urls = paths.map((p) => p.params.segments.join('/'));
|
||||
//
|
||||
// it('should return paths', () =>
|
||||
// expect(urls).toContainEqual(expect.stringMatching('/getting-started')));
|
||||
// });
|
||||
});
|
||||
|
||||
@ -2,12 +2,7 @@ import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import matter from 'gray-matter';
|
||||
import { extractTitle } from './documents.utils';
|
||||
import {
|
||||
DocumentData,
|
||||
DocumentMetadata,
|
||||
FlavorMetadata,
|
||||
VersionMetadata,
|
||||
} from './documents.models';
|
||||
import { DocumentData, DocumentMetadata } from './documents.models';
|
||||
|
||||
export interface StaticDocumentPaths {
|
||||
params: { segments: string[] };
|
||||
@ -17,9 +12,7 @@ export class DocumentsApi {
|
||||
constructor(
|
||||
private readonly options: {
|
||||
publicDocsRoot: string;
|
||||
versions: VersionMetadata[];
|
||||
flavors: FlavorMetadata[];
|
||||
documentsMap: Map<string, DocumentMetadata[]>;
|
||||
documents: DocumentMetadata;
|
||||
}
|
||||
) {
|
||||
if (!options.publicDocsRoot) {
|
||||
@ -27,32 +20,9 @@ export class DocumentsApi {
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultVersion(): VersionMetadata {
|
||||
const found = this.options.versions.find((v) => v.default);
|
||||
if (found) return found;
|
||||
throw new Error('Cannot find default version');
|
||||
}
|
||||
getDocument(path: string[]): DocumentData {
|
||||
const docPath = this.getFilePath(path);
|
||||
|
||||
getDefaultFlavor(): FlavorMetadata {
|
||||
const found = this.options.flavors.find((f) => f.default);
|
||||
if (found) return found;
|
||||
throw new Error('Cannot find default flavor');
|
||||
}
|
||||
|
||||
getVersions(): VersionMetadata[] {
|
||||
return this.options.versions;
|
||||
}
|
||||
|
||||
getFlavors(): FlavorMetadata[] {
|
||||
return this.options.flavors;
|
||||
}
|
||||
|
||||
getDocument(
|
||||
version: VersionMetadata,
|
||||
flavor: FlavorMetadata,
|
||||
path: string[]
|
||||
): DocumentData {
|
||||
const docPath = this.getFilePath(version.id, flavor.id, path);
|
||||
const originalContent = readFileSync(docPath, 'utf8');
|
||||
const file = matter(originalContent);
|
||||
|
||||
@ -70,21 +40,14 @@ export class DocumentsApi {
|
||||
};
|
||||
}
|
||||
|
||||
getDocuments(version: string) {
|
||||
const docs = this.options.documentsMap.get(version);
|
||||
if (docs) {
|
||||
return docs;
|
||||
} else {
|
||||
throw new Error(`Cannot find documents for ${version}`);
|
||||
}
|
||||
getDocuments() {
|
||||
const docs = this.options.documents;
|
||||
if (docs) return docs;
|
||||
throw new Error(`Cannot find any documents`);
|
||||
}
|
||||
|
||||
getStaticDocumentPaths(
|
||||
version: VersionMetadata,
|
||||
flavor: FlavorMetadata
|
||||
): StaticDocumentPaths[] {
|
||||
getStaticDocumentPaths(): StaticDocumentPaths[] {
|
||||
const paths: StaticDocumentPaths[] = [];
|
||||
const defaultVersion = this.getDefaultVersion();
|
||||
|
||||
function recur(curr, acc) {
|
||||
if (curr.itemList) {
|
||||
@ -94,53 +57,23 @@ export class DocumentsApi {
|
||||
} else {
|
||||
paths.push({
|
||||
params: {
|
||||
segments: [version.alias, flavor.alias, ...acc, curr.id],
|
||||
segments: [...acc, curr.id],
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Generic path generation
|
||||
* For generic paths such as `/getting-started/intro`, use the default version and react flavor.
|
||||
*/
|
||||
if (version.id === defaultVersion.id && flavor.id === 'react')
|
||||
paths.push({
|
||||
params: {
|
||||
segments: [...acc, curr.id],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const item = this.getDocuments(version.id).find(
|
||||
(item) => item.id === flavor.id
|
||||
);
|
||||
if (!item || !item.itemList)
|
||||
throw new Error(
|
||||
`Can't find items for version:${version.id} and flavor:${flavor.id}`
|
||||
);
|
||||
item.itemList.forEach((item) => {
|
||||
if (!this.options.documents || !this.options.documents.itemList)
|
||||
throw new Error(`Can't find any items`);
|
||||
this.options.documents.itemList.forEach((item) => {
|
||||
recur(item, []);
|
||||
});
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
getDocumentsRoot(version: string): string {
|
||||
const versionPath = this.options.versions.find(
|
||||
(x) => x.id === version
|
||||
)?.path;
|
||||
|
||||
if (versionPath) {
|
||||
return join(this.options.publicDocsRoot, versionPath);
|
||||
}
|
||||
|
||||
throw new Error(`Cannot find root for ${version}`);
|
||||
}
|
||||
|
||||
private getFilePath(versionId, flavorId, path): string {
|
||||
let items = this.getDocuments(versionId).find(
|
||||
(item) => item.id === flavorId
|
||||
)?.itemList;
|
||||
private getFilePath(path: string[]): string {
|
||||
let items = this.options.documents?.itemList;
|
||||
|
||||
if (!items) {
|
||||
throw new Error(`Document not found`);
|
||||
@ -155,7 +88,7 @@ export class DocumentsApi {
|
||||
throw new Error(`Document not found`);
|
||||
}
|
||||
}
|
||||
const file = found.file ?? [flavorId, ...path].join('/');
|
||||
return join(this.getDocumentsRoot(versionId), `${file}.md`);
|
||||
const file = found.file ?? ['default', ...path].join('/');
|
||||
return join(this.options.publicDocsRoot, 'latest', `${file}.md`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,14 +8,9 @@ describe('MenuApi', () => {
|
||||
|
||||
describe('getMenu', () => {
|
||||
it('should group by section', () => {
|
||||
const menu = api.getMenu(
|
||||
docsApi.getDefaultVersion(),
|
||||
docsApi.getDefaultFlavor()
|
||||
);
|
||||
const menu = api.getMenu();
|
||||
|
||||
expect(menu).toEqual({
|
||||
version: docsApi.getDefaultVersion(),
|
||||
flavor: docsApi.getDefaultFlavor(),
|
||||
sections: expect.arrayContaining([
|
||||
expect.objectContaining({ id: 'basic', itemList: expect.any(Array) }),
|
||||
expect.objectContaining({ id: 'api', itemList: expect.any(Array) }),
|
||||
@ -26,21 +21,5 @@ describe('MenuApi', () => {
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
it('should add path to menu items', () => {
|
||||
const menu = api.getMenu(
|
||||
docsApi.getDefaultVersion(),
|
||||
docsApi.getDefaultFlavor()
|
||||
);
|
||||
|
||||
// first basic section item should have prefix by version and flavor
|
||||
// e.g. "latest/react/getting-started/intro"
|
||||
expect(menu?.sections?.[0]?.itemList?.[0]?.itemList?.[0].url).toMatch(
|
||||
/l\/r/
|
||||
);
|
||||
expect(menu?.sections?.[0]?.itemList?.[0]?.itemList?.[0].path).toMatch(
|
||||
/latest\/react/
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -6,24 +6,19 @@ import {
|
||||
getBasicSection,
|
||||
getDeepDiveSection,
|
||||
} from './menu.utils';
|
||||
import { FlavorMetadata, VersionMetadata } from './documents.models';
|
||||
|
||||
export class MenuApi {
|
||||
private readonly menuCache = new Map<string, Menu>();
|
||||
private menuCache: Menu | null = null;
|
||||
|
||||
constructor(private readonly documentsApi: DocumentsApi) {}
|
||||
|
||||
getMenu(version: VersionMetadata, flavor: FlavorMetadata): Menu {
|
||||
const key = `${version.id}-${flavor.id}`;
|
||||
let menu = this.menuCache.get(key);
|
||||
getMenu(): Menu {
|
||||
let menu = this.menuCache;
|
||||
|
||||
if (!menu) {
|
||||
const root = this.documentsApi.getDocuments(version.id);
|
||||
const items = createMenuItems(version, flavor, root);
|
||||
const items = createMenuItems(this.documentsApi.getDocuments());
|
||||
if (items) {
|
||||
menu = {
|
||||
version: version,
|
||||
flavor: flavor,
|
||||
sections: [
|
||||
getBasicSection(items),
|
||||
getDeepDiveSection(items),
|
||||
@ -31,9 +26,9 @@ export class MenuApi {
|
||||
],
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Cannot find documents for flavor id: "${flavor.id}"`);
|
||||
throw new Error(`Cannot find any documents`);
|
||||
}
|
||||
this.menuCache.set(key, menu);
|
||||
this.menuCache = menu;
|
||||
}
|
||||
|
||||
return menu;
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
import {
|
||||
DocumentMetadata,
|
||||
FlavorMetadata,
|
||||
VersionMetadata,
|
||||
} from './documents.models';
|
||||
import { DocumentMetadata, VersionMetadata } from './documents.models';
|
||||
|
||||
export interface Menu {
|
||||
version: VersionMetadata;
|
||||
flavor: FlavorMetadata;
|
||||
sections: MenuSection[];
|
||||
}
|
||||
|
||||
|
||||
@ -1,22 +1,14 @@
|
||||
import {
|
||||
DocumentMetadata,
|
||||
FlavorMetadata,
|
||||
VersionMetadata,
|
||||
} from '@nrwl/nx-dev/data-access-documents';
|
||||
import { DocumentMetadata } from '@nrwl/nx-dev/data-access-documents';
|
||||
import { MenuItem, MenuSection } from './menu.models';
|
||||
|
||||
export function createMenuItems(
|
||||
version: VersionMetadata,
|
||||
flavor: FlavorMetadata,
|
||||
root: DocumentMetadata[]
|
||||
): MenuItem[] {
|
||||
const items = root.find((x) => x.id === flavor.id)?.itemList;
|
||||
export function createMenuItems(root: DocumentMetadata): MenuItem[] {
|
||||
const items = root?.itemList;
|
||||
|
||||
const createPathMetadata = (g: DocumentMetadata, parentId = ''): MenuItem => {
|
||||
const pathData = {
|
||||
...g,
|
||||
path: `/${version.id}/${flavor.id}/${parentId}/${g.id}`,
|
||||
url: `/${version.alias}/${flavor.alias}/${parentId}/${g.id}`,
|
||||
path: `/${parentId}/${g.id}`,
|
||||
url: `/${parentId}/${g.id}`,
|
||||
};
|
||||
|
||||
if (Array.isArray(g.itemList)) {
|
||||
|
||||
@ -8,40 +8,13 @@ function readJsonFile(f) {
|
||||
|
||||
export function createDocumentApiOptions() {
|
||||
return {
|
||||
versions: readJsonFile(
|
||||
documents: readJsonFile(
|
||||
join(
|
||||
join(__dirname, '../../../nx-dev/public/documentation'),
|
||||
'versions.json'
|
||||
)
|
||||
),
|
||||
flavors: readJsonFile(
|
||||
join(
|
||||
join(__dirname, '../../../nx-dev/public/documentation'),
|
||||
'flavors.json'
|
||||
)
|
||||
),
|
||||
documentsMap: new Map<string, DocumentMetadata[]>([
|
||||
[
|
||||
'latest',
|
||||
readJsonFile(
|
||||
join(
|
||||
join(__dirname, '../../../nx-dev/public/documentation'),
|
||||
'latest',
|
||||
'map.json'
|
||||
)
|
||||
),
|
||||
],
|
||||
[
|
||||
'previous',
|
||||
readJsonFile(
|
||||
join(
|
||||
join(__dirname, '../../../nx-dev/public/documentation'),
|
||||
'previous',
|
||||
'map.json'
|
||||
)
|
||||
),
|
||||
],
|
||||
]),
|
||||
'map.json'
|
||||
)
|
||||
).find((x) => x.id === 'default') as DocumentMetadata,
|
||||
publicDocsRoot: join(__dirname, '../../../nx-dev/public/documentation'),
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,23 +3,14 @@ import ReactMarkdown from 'react-markdown';
|
||||
import autolinkHeadings from 'rehype-autolink-headings';
|
||||
import gfm from 'remark-gfm';
|
||||
import slug from 'rehype-slug';
|
||||
import {
|
||||
DocumentData,
|
||||
FlavorMetadata,
|
||||
VersionMetadata,
|
||||
} from '@nrwl/nx-dev/data-access-documents';
|
||||
import { DocumentData } from '@nrwl/nx-dev/data-access-documents';
|
||||
import { sendCustomEvent } from '@nrwl/nx-dev/feature-analytics';
|
||||
import { transformLinkPath } from './renderers/transform-link-path';
|
||||
import { transformImagePath } from './renderers/transform-image-path';
|
||||
import { renderIframes } from './renderers/render-iframe';
|
||||
import { CodeBlock } from './code-block';
|
||||
|
||||
export interface ContentProps {
|
||||
document: DocumentData;
|
||||
flavor: FlavorMetadata;
|
||||
flavorList: FlavorMetadata[];
|
||||
version: VersionMetadata;
|
||||
versionList: VersionMetadata[];
|
||||
}
|
||||
|
||||
interface ComponentsConfig {
|
||||
@ -52,7 +43,7 @@ const components: any = (config: ComponentsConfig) => ({
|
||||
|
||||
export function Content(props: ContentProps) {
|
||||
return (
|
||||
<div className="min-w-0 flex-auto px-4 sm:px-6 xl:px-8 pt-10 pb-24 lg:pb-16">
|
||||
<div className="min-w-0 flex-auto px-4 sm:px-6 xl:px-8 pt-8 pb-24 lg:pb-16">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[gfm]}
|
||||
rehypePlugins={[
|
||||
@ -67,14 +58,7 @@ export function Content(props: ContentProps) {
|
||||
renderIframes,
|
||||
]}
|
||||
children={props.document.content}
|
||||
transformLinkUri={transformLinkPath({
|
||||
framework: props.flavor,
|
||||
frameworkList: props.flavorList,
|
||||
version: props.version,
|
||||
versionList: props.versionList,
|
||||
})}
|
||||
transformImageUri={transformImagePath({
|
||||
version: props.version,
|
||||
document: props.document,
|
||||
})}
|
||||
className="prose max-w-none"
|
||||
@ -93,7 +77,7 @@ export function Content(props: ContentProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function createAnchorContent(node) {
|
||||
function createAnchorContent(node: any) {
|
||||
node.properties.className = ['group'];
|
||||
return {
|
||||
type: 'element',
|
||||
|
||||
@ -1,22 +1,12 @@
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { useRouter } from 'next/router';
|
||||
import { BreadcrumbJsonLd, NextSeo } from 'next-seo';
|
||||
import {
|
||||
DocumentData,
|
||||
FlavorMetadata,
|
||||
Menu,
|
||||
MenuItem,
|
||||
VersionMetadata,
|
||||
} from '@nrwl/nx-dev/data-access-documents';
|
||||
import { NextSeo } from 'next-seo';
|
||||
import { DocumentData, Menu } from '@nrwl/nx-dev/data-access-documents';
|
||||
import Content from './content';
|
||||
import Sidebar from './sidebar';
|
||||
|
||||
export interface DocumentationFeatureDocViewerProps {
|
||||
version: VersionMetadata;
|
||||
flavor: FlavorMetadata;
|
||||
flavorList: FlavorMetadata[];
|
||||
versionList: VersionMetadata[];
|
||||
menu: Menu;
|
||||
document: DocumentData;
|
||||
toc: any;
|
||||
@ -25,11 +15,7 @@ export interface DocumentationFeatureDocViewerProps {
|
||||
|
||||
export function DocViewer({
|
||||
document,
|
||||
version,
|
||||
versionList,
|
||||
menu,
|
||||
flavor,
|
||||
flavorList,
|
||||
navIsOpen,
|
||||
}: DocumentationFeatureDocViewerProps) {
|
||||
const router = useRouter();
|
||||
@ -37,7 +23,6 @@ export function DocViewer({
|
||||
return (
|
||||
<>
|
||||
<NextSeo
|
||||
noindex={version.id === 'previous'}
|
||||
title={document.data.title + ' | Nx'}
|
||||
openGraph={{
|
||||
url: 'https://nx.dev' + router.asPath,
|
||||
@ -69,13 +54,7 @@ export function DocViewer({
|
||||
navIsOpen && 'overflow-hidden max-h-screen fixed'
|
||||
)}
|
||||
>
|
||||
<Content
|
||||
document={document}
|
||||
flavor={flavor}
|
||||
flavorList={flavorList}
|
||||
version={version}
|
||||
versionList={versionList}
|
||||
/>
|
||||
<Content document={document} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -3,29 +3,21 @@ import { transformImagePath } from './transform-image-path';
|
||||
describe('transformImagePath', () => {
|
||||
it('should transform relative paths', () => {
|
||||
const opts = {
|
||||
version: {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
document: {
|
||||
content: '',
|
||||
excerpt: '',
|
||||
filePath:
|
||||
'nx-dev/nx-dev/public/documentation/latest/angular/migration/migration-angularjs.md',
|
||||
'nx-dev/nx-dev/public/documentation/latest/shared/using-nx/dte.md',
|
||||
data: {},
|
||||
},
|
||||
};
|
||||
const transform = transformImagePath(opts);
|
||||
|
||||
expect(transform('./test.png')).toEqual(
|
||||
'/documentation/latest/angular/migration/test.png'
|
||||
'/documentation/latest/shared/using-nx/test.png'
|
||||
);
|
||||
expect(transform('../test.png')).toEqual(
|
||||
'/documentation/latest/angular/test.png'
|
||||
'/documentation/latest/shared/test.png'
|
||||
);
|
||||
expect(transform('../../test.png')).toEqual(
|
||||
'/documentation/latest/test.png'
|
||||
@ -53,7 +45,7 @@ describe('transformImagePath', () => {
|
||||
const transform = transformImagePath(opts);
|
||||
|
||||
expect(transform('/shared/test.png')).toEqual(
|
||||
'/documentation/latest/shared/test.png'
|
||||
'/documentation/shared/test.png'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -6,10 +6,8 @@ import {
|
||||
import { join } from 'path';
|
||||
|
||||
export function transformImagePath({
|
||||
version,
|
||||
document,
|
||||
}: {
|
||||
version: VersionMetadata;
|
||||
document: DocumentData;
|
||||
}): (src: string) => string {
|
||||
return (src) => {
|
||||
@ -25,6 +23,6 @@ export function transformImagePath({
|
||||
);
|
||||
}
|
||||
|
||||
return uriTransformer(`/documentation/${version.id}`.concat(src));
|
||||
return uriTransformer(`/documentation`.concat(src));
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,374 +0,0 @@
|
||||
import { transformLinkPath } from './transform-link-path';
|
||||
|
||||
describe('transformLinkPath', () => {
|
||||
it('should transform path containing version and flavour', () => {
|
||||
const transform = transformLinkPath({
|
||||
framework: {
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
frameworkList: [
|
||||
{ id: 'angular', name: 'Angular', alias: 'a', path: 'angular' },
|
||||
{
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
{ id: 'node', name: 'Node', alias: 'n', path: 'node' },
|
||||
],
|
||||
version: {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
versionList: [
|
||||
{
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: 'Previous',
|
||||
id: 'previous',
|
||||
alias: 'p',
|
||||
release: '11.3.0',
|
||||
path: 'previous',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(
|
||||
transform('/%7B%7Bversion%7D%7D/%7B%7Bframework%7D%7D/node/overview')
|
||||
).toEqual('/l/r/node/overview');
|
||||
});
|
||||
it('should transform path containing flavour only', () => {
|
||||
const transform = transformLinkPath({
|
||||
framework: {
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
frameworkList: [
|
||||
{ id: 'angular', name: 'Angular', alias: 'a', path: 'angular' },
|
||||
{
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
{ id: 'node', name: 'Node', alias: 'n', path: 'node' },
|
||||
],
|
||||
version: {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
versionList: [
|
||||
{
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: 'Previous',
|
||||
id: 'previous',
|
||||
alias: 'p',
|
||||
release: '11.3.0',
|
||||
path: 'previous',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(transform('/l/%7B%7Bframework%7D%7D/node/overview')).toEqual(
|
||||
'/l/r/node/overview'
|
||||
);
|
||||
});
|
||||
it('should transform path containing version only', () => {
|
||||
const transform = transformLinkPath({
|
||||
framework: {
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
frameworkList: [
|
||||
{ id: 'angular', name: 'Angular', alias: 'a', path: 'angular' },
|
||||
{
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
{ id: 'node', name: 'Node', alias: 'n', path: 'node' },
|
||||
],
|
||||
version: {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
versionList: [
|
||||
{
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: 'Previous',
|
||||
id: 'previous',
|
||||
alias: 'p',
|
||||
release: '11.3.0',
|
||||
path: 'previous',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(transform('/%7B%7Bversion%7D%7D/r/node/overview')).toEqual(
|
||||
'/l/r/node/overview'
|
||||
);
|
||||
});
|
||||
it('should always prepend version if framework detected', () => {
|
||||
const transform = transformLinkPath({
|
||||
framework: {
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
frameworkList: [
|
||||
{ id: 'angular', name: 'Angular', alias: 'a', path: 'angular' },
|
||||
{
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
{ id: 'node', name: 'Node', alias: 'n', path: 'node' },
|
||||
],
|
||||
version: {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
versionList: [
|
||||
{
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: 'Previous',
|
||||
id: 'previous',
|
||||
alias: 'p',
|
||||
release: '11.3.0',
|
||||
path: 'previous',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(transform('/a/node/overview')).toEqual('/l/a/node/overview');
|
||||
expect(transform('/r/node/overview')).toEqual('/l/r/node/overview');
|
||||
});
|
||||
|
||||
it('should always use version & framework aliasing', () => {
|
||||
const transform = transformLinkPath({
|
||||
framework: {
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
frameworkList: [
|
||||
{ id: 'angular', name: 'Angular', alias: 'a', path: 'angular' },
|
||||
{
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
{ id: 'node', name: 'Node', alias: 'n', path: 'node' },
|
||||
],
|
||||
version: {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
versionList: [
|
||||
{
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: 'Previous',
|
||||
id: 'previous',
|
||||
alias: 'p',
|
||||
release: '11.3.0',
|
||||
path: 'previous',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(transform('/latest/angular/node/overview')).toEqual(
|
||||
'/l/a/node/overview'
|
||||
);
|
||||
expect(transform('/latest/a/node/overview')).toEqual('/l/a/node/overview');
|
||||
expect(transform('/previous/react/node/overview')).toEqual(
|
||||
'/p/r/node/overview'
|
||||
);
|
||||
expect(transform('/p/react/node/overview')).toEqual('/p/r/node/overview');
|
||||
});
|
||||
it('should do nothing if unrecognized path', () => {
|
||||
const transform = transformLinkPath({
|
||||
framework: {
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
frameworkList: [
|
||||
{ id: 'angular', name: 'Angular', alias: 'a', path: 'angular' },
|
||||
{
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
{ id: 'node', name: 'Node', alias: 'n', path: 'node' },
|
||||
],
|
||||
version: {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
versionList: [
|
||||
{
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: 'Previous',
|
||||
id: 'previous',
|
||||
alias: 'p',
|
||||
release: '11.3.0',
|
||||
path: 'previous',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(transform('/%7B%7Bxxx%7D%7D/%7B%7Byyy%7D%7D/node/overview')).toEqual(
|
||||
'/%7B%7Bxxx%7D%7D/%7B%7Byyy%7D%7D/node/overview'
|
||||
);
|
||||
});
|
||||
it('should not transform path when internal or anchor links', () => {
|
||||
const transform = transformLinkPath({
|
||||
framework: {
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
frameworkList: [
|
||||
{ id: 'angular', name: 'Angular', alias: 'a', path: 'angular' },
|
||||
{
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
{ id: 'node', name: 'Node', alias: 'n', path: 'node' },
|
||||
],
|
||||
version: {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
versionList: [
|
||||
{
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: 'Previous',
|
||||
id: 'previous',
|
||||
alias: 'p',
|
||||
release: '11.3.0',
|
||||
path: 'previous',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(transform('#something-path')).toEqual('#something-path');
|
||||
expect(transform('https://somewhere.path')).toEqual(
|
||||
'https://somewhere.path'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,123 +0,0 @@
|
||||
import { uriTransformer } from 'react-markdown';
|
||||
import {
|
||||
FlavorMetadata,
|
||||
VersionMetadata,
|
||||
} from '@nrwl/nx-dev/data-access-documents';
|
||||
|
||||
export function transformLinkPath(options: {
|
||||
framework: FlavorMetadata;
|
||||
frameworkList: FlavorMetadata[];
|
||||
version: VersionMetadata;
|
||||
versionList: VersionMetadata[];
|
||||
}): (href: string) => string {
|
||||
return (href) =>
|
||||
uriTransformer(
|
||||
href
|
||||
? interpolation(href, {
|
||||
framework: {
|
||||
marker: '%7B%7Bframework%7D%7D',
|
||||
value: options.framework.alias,
|
||||
},
|
||||
frameworkList: options.frameworkList.map((f) => f.alias),
|
||||
version: {
|
||||
marker: '%7B%7Bversion%7D%7D',
|
||||
value: options.version.alias,
|
||||
},
|
||||
versionList: options.versionList.map((v) => v.alias),
|
||||
})
|
||||
: href
|
||||
);
|
||||
}
|
||||
|
||||
function interpolation(
|
||||
path: string,
|
||||
config: {
|
||||
framework: { marker: string; value: string };
|
||||
frameworkList: string[];
|
||||
version: { marker: string; value: string };
|
||||
versionList: string[];
|
||||
}
|
||||
): string {
|
||||
// Skip external and anchor links
|
||||
if (path.startsWith('#') || path.startsWith('http')) return path;
|
||||
|
||||
// -> /{{version}}/{{framework}}/path/to/document
|
||||
if (path.includes([config.version.marker, config.framework.marker].join('/')))
|
||||
path = path.replace(
|
||||
[config.version.marker, config.framework.marker].join('/'),
|
||||
`${config.version.value}/${config.framework.value}`
|
||||
);
|
||||
// -> /version/{{framework}}/path/to/document
|
||||
if (path.includes(config.framework.marker))
|
||||
path = path.replace(
|
||||
[config.framework.marker].join('/'),
|
||||
config.framework.value
|
||||
);
|
||||
// -> /{{version}}/framework/path/to/document
|
||||
if (path.includes(config.version.marker))
|
||||
path = path.replace(
|
||||
[config.version.marker].join('/'),
|
||||
config.version.value
|
||||
);
|
||||
|
||||
const isPrependedWithVersion = (
|
||||
value: string,
|
||||
versionList: string[]
|
||||
): boolean => versionList.includes(value.split('/').filter(Boolean)[0]);
|
||||
const isSupportedFramework = (
|
||||
value: string,
|
||||
frameworkList: string[]
|
||||
): boolean => frameworkList.includes(value.split('/').filter(Boolean)[0]);
|
||||
|
||||
/**
|
||||
* Always prepend the link with a version if not already present only
|
||||
* if the path contains a known framework
|
||||
*/
|
||||
if (
|
||||
!isPrependedWithVersion(path, config.versionList) &&
|
||||
isSupportedFramework(path, config.frameworkList)
|
||||
) {
|
||||
path =
|
||||
'/' + config.version.value + (path.startsWith('/') ? path : '/' + path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Version aliasing if not done already on supported versions only
|
||||
* /latest/react/gatsby/overview => /l/react/gatsby/overview
|
||||
*/
|
||||
const aliasVersion = (path: string): string => {
|
||||
const explodedPath = path.split('/').filter(Boolean);
|
||||
if (
|
||||
!!explodedPath[0] &&
|
||||
explodedPath[0].length > 1 &&
|
||||
config.versionList.includes(explodedPath[0].charAt(0))
|
||||
)
|
||||
return (path =
|
||||
'/' +
|
||||
[explodedPath[0].charAt(0), explodedPath.slice(1).join('/')].join('/'));
|
||||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Framework aliasing if not done already on supported framework only
|
||||
* /l/react/gatsby/overview => /l/r/gatsby/overview
|
||||
*/
|
||||
const aliasFramework = (path: string): string => {
|
||||
const explodedPath = path.split('/').filter(Boolean);
|
||||
if (
|
||||
!!explodedPath[1] &&
|
||||
explodedPath[1].length > 1 &&
|
||||
config.frameworkList.includes(explodedPath[1].charAt(0))
|
||||
)
|
||||
return (path =
|
||||
'/' +
|
||||
[
|
||||
explodedPath[0],
|
||||
explodedPath[1].charAt(0),
|
||||
explodedPath.slice(2).join('/'),
|
||||
].join('/'));
|
||||
return path;
|
||||
};
|
||||
|
||||
return aliasFramework(aliasVersion(path));
|
||||
}
|
||||
@ -2,115 +2,48 @@ import React from 'react';
|
||||
import { screen } from '@testing-library/dom';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import Sidebar, { createNextPath } from './sidebar';
|
||||
import { VersionsAndFlavorsProvider } from '@nrwl/nx-dev/feature-versions-and-flavors';
|
||||
import Sidebar from './sidebar';
|
||||
|
||||
describe('Sidebar', () => {
|
||||
it('should render sections', () => {
|
||||
render(
|
||||
<VersionsAndFlavorsProvider
|
||||
value={{
|
||||
activeFlavor: {
|
||||
id: 'angular',
|
||||
name: 'Angular',
|
||||
alias: 'a',
|
||||
path: 'angular',
|
||||
},
|
||||
activeVersion: {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
flavors: [
|
||||
<Sidebar
|
||||
navIsOpen={false}
|
||||
menu={{
|
||||
sections: [
|
||||
{
|
||||
id: 'angular',
|
||||
name: 'Angular',
|
||||
alias: 'a',
|
||||
path: 'angular',
|
||||
id: 'basic',
|
||||
name: 'Basic',
|
||||
hideSectionHeader: true,
|
||||
itemList: [
|
||||
{
|
||||
id: 'getting-started',
|
||||
name: 'getting started',
|
||||
itemList: [
|
||||
{ id: 'a', name: 'A', path: '/a', url: '/a' },
|
||||
{ id: 'b', name: 'B', path: '/b', url: '/b' },
|
||||
{ id: 'c', name: 'C', path: '/c', url: '/c' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
versions: [
|
||||
{
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: 'Previous (v10.4.13)',
|
||||
id: 'previous',
|
||||
alias: 'p',
|
||||
release: '10.4.13',
|
||||
path: '10.4.13',
|
||||
default: false,
|
||||
id: 'api',
|
||||
name: 'API',
|
||||
itemList: [
|
||||
{
|
||||
id: 'overview',
|
||||
name: 'overview',
|
||||
itemList: [
|
||||
{ id: 'd', name: 'D', path: '/d', url: '/d' },
|
||||
{ id: 'e', name: 'E', path: '/e', url: '/e' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
<Sidebar
|
||||
navIsOpen={false}
|
||||
menu={{
|
||||
version: {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: '12.9.0',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
},
|
||||
flavor: {
|
||||
id: 'angular',
|
||||
name: 'Angular',
|
||||
alias: 'a',
|
||||
path: 'angular',
|
||||
},
|
||||
sections: [
|
||||
{
|
||||
id: 'basic',
|
||||
name: 'Basic',
|
||||
hideSectionHeader: true,
|
||||
itemList: [
|
||||
{
|
||||
id: 'getting-started',
|
||||
name: 'getting started',
|
||||
itemList: [
|
||||
{ id: 'a', name: 'A', path: '/a', url: '/a' },
|
||||
{ id: 'b', name: 'B', path: '/b', url: '/b' },
|
||||
{ id: 'c', name: 'C', path: '/c', url: '/c' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'api',
|
||||
name: 'API',
|
||||
itemList: [
|
||||
{
|
||||
id: 'overview',
|
||||
name: 'overview',
|
||||
itemList: [
|
||||
{ id: 'd', name: 'D', path: '/d', url: '/d' },
|
||||
{ id: 'e', name: 'E', path: '/e', url: '/e' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</VersionsAndFlavorsProvider>
|
||||
/>
|
||||
);
|
||||
|
||||
// TODO: figure out the type errors and fix
|
||||
@ -122,15 +55,3 @@ describe('Sidebar', () => {
|
||||
expect(screen.getByTestId('section-h4:api')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('createNextPath', () => {
|
||||
it('should replace version and flavor in the current path', () => {
|
||||
expect(
|
||||
createNextPath('latest', 'react', '/previous/react/guides/eslint')
|
||||
).toEqual('/latest/react/guides/eslint');
|
||||
|
||||
expect(
|
||||
createNextPath('previous', 'angular', '/previous/react/guides/eslint')
|
||||
).toEqual('/previous/angular/guides/eslint');
|
||||
});
|
||||
});
|
||||
|
||||
@ -7,37 +7,13 @@ import {
|
||||
MenuSection,
|
||||
} from '@nrwl/nx-dev/data-access-documents';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Selector } from '@nrwl/nx-dev/ui-common';
|
||||
import { useSelectedFlavor } from '@nrwl/nx-dev/feature-flavor-selection';
|
||||
import {
|
||||
useActiveFlavor,
|
||||
useActiveVersion,
|
||||
useFlavors,
|
||||
useVersions,
|
||||
} from '@nrwl/nx-dev/feature-versions-and-flavors';
|
||||
|
||||
export interface SidebarProps {
|
||||
menu: Menu;
|
||||
navIsOpen?: boolean;
|
||||
}
|
||||
|
||||
// Exported for testing
|
||||
export function createNextPath(
|
||||
version: string,
|
||||
flavor: string,
|
||||
currentPath: string
|
||||
): string {
|
||||
const genericPath = currentPath.split('/').slice(3).join('/');
|
||||
return `/${version}/${flavor}/${genericPath}`;
|
||||
}
|
||||
|
||||
export function Sidebar({ menu, navIsOpen }: SidebarProps) {
|
||||
const version = useActiveVersion();
|
||||
const flavor = useActiveFlavor();
|
||||
const { setSelectedFlavor } = useSelectedFlavor();
|
||||
const flavorList = useFlavors();
|
||||
const versionList = useVersions();
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div
|
||||
data-testid="sidebar"
|
||||
@ -52,42 +28,11 @@ export function Sidebar({ menu, navIsOpen }: SidebarProps) {
|
||||
className="h-full overflow-y-auto scrolling-touch lg:h-auto lg:block lg:relative lg:sticky lg:bg-transparent overflow-auto lg:top-18 bg-white mr-24 lg:mr-0 px-2 sm:pr-4 xl:pr-6"
|
||||
>
|
||||
<div className="hidden lg:block h-12 pointer-events-none absolute inset-x-0 z-10 bg-gradient-to-b from-white" />
|
||||
<div className="px-1 pt-6 sm:px-3 xl:px-5 lg:pt-10">
|
||||
<Selector
|
||||
items={versionList.map((version) => ({
|
||||
label: version.name,
|
||||
value: version.alias,
|
||||
}))}
|
||||
selected={{ label: version.name, value: version.alias }}
|
||||
onChange={(item) =>
|
||||
router.push(
|
||||
createNextPath(item.value, flavor.alias, router.asPath)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="px-1 pt-3 sm:px-3 xl:px-5">
|
||||
<Selector
|
||||
items={flavorList.map((flavor) => ({
|
||||
label: flavor.name,
|
||||
value: flavor.alias,
|
||||
data: flavor,
|
||||
}))}
|
||||
selected={{ label: flavor.name, value: flavor.alias }}
|
||||
onChange={(item) =>
|
||||
!!router.push(
|
||||
createNextPath(version.alias, item.value, router.asPath)
|
||||
) &&
|
||||
item.data &&
|
||||
setSelectedFlavor(item.data)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="px-1 py-6 sm:px-3 xl:px-5 h-1 w-full border-b border-gray-50" />
|
||||
|
||||
<nav
|
||||
id="nav"
|
||||
data-testid="navigation"
|
||||
className="px-1 pt-1 font-medium text-base sm:px-3 xl:px-5 lg:text-sm pb-10 lg:pb-14 sticky?lg:h-(screen-18)"
|
||||
className="px-1 pt-16 font-medium text-base sm:px-3 xl:px-5 lg:text-sm pb-10 lg:pb-14 sticky?lg:h-(screen-18)"
|
||||
>
|
||||
{menu.sections.map((section, index) => (
|
||||
<SidebarSection key={section.id + '-' + index} section={section} />
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
{
|
||||
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
displayName: 'nx-dev-feature-flavor-selection',
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage//nx-dev/feature-flavor-selection',
|
||||
};
|
||||
@ -1,25 +0,0 @@
|
||||
{
|
||||
"root": "nx-dev/feature-flavor-selection",
|
||||
"sourceRoot": "nx-dev/feature-flavor-selection/src",
|
||||
"projectType": "library",
|
||||
"tags": ["scope:nx-dev", "type:feature"],
|
||||
"targets": {
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"nx-dev/feature-flavor-selection/**/*.{ts,tsx,js,jsx}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["coverage/nx-dev/feature-flavor-selection"],
|
||||
"options": {
|
||||
"jestConfig": "nx-dev/feature-flavor-selection/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export * from './lib/use-selected-flavor/use-selected-flavor';
|
||||
export * from './lib/flavors-banner';
|
||||
export * from './lib/utils';
|
||||
@ -1,107 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
import { Dialog } from '@headlessui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import {
|
||||
FlavorMetadata,
|
||||
VersionMetadata,
|
||||
} from '@nrwl/nx-dev/data-access-documents';
|
||||
import {
|
||||
useFlavors,
|
||||
useVersions,
|
||||
} from '@nrwl/nx-dev/feature-versions-and-flavors';
|
||||
|
||||
import { pathCleaner } from '../utils';
|
||||
|
||||
export interface FlavorSelectionDialogProps {
|
||||
version: VersionMetadata;
|
||||
open: boolean;
|
||||
onSelect: (f: FlavorMetadata) => void;
|
||||
}
|
||||
|
||||
export function FlavorSelectionDialog(props: FlavorSelectionDialogProps) {
|
||||
const router = useRouter();
|
||||
const flavors = useFlavors();
|
||||
const versions = useVersions();
|
||||
const cleanPath = pathCleaner(versions, flavors);
|
||||
const angularFlavor = flavors.find((f) => f.alias === 'a') as FlavorMetadata;
|
||||
const reactFlavor = flavors.find((f) => f.alias === 'r') as FlavorMetadata;
|
||||
const nodeFlavor = flavors.find((f) => f.alias === 'n') as FlavorMetadata;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
as="div"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
open={props.open}
|
||||
onClose={() => {}}
|
||||
>
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<Dialog.Overlay className="fixed inset-0 backdrop-filter backdrop-blur" />
|
||||
<div className="z-50 bg-white rounded-md w-11/12 max-w-xl filter drop-shadow-2xl">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-xl sm:text-2xl lg:text-2xl leading-6 m-5"
|
||||
>
|
||||
Before You Continue...
|
||||
</Dialog.Title>
|
||||
<Dialog.Description className="m-5">
|
||||
Nx is a smart, fast and extensible build system that has first-class
|
||||
support for many frontend and backend technologies.
|
||||
</Dialog.Description>
|
||||
<Dialog.Description className="m-5">
|
||||
Please select the flavor you want to learn more about.
|
||||
</Dialog.Description>
|
||||
<div className="p-5 w-full grid grid-cols-3 gap-5 justify-items-center">
|
||||
<Link
|
||||
href={`${props.version.alias}/r${cleanPath(router.asPath)}`}
|
||||
replace
|
||||
>
|
||||
<a
|
||||
onClick={() => props.onSelect(reactFlavor)}
|
||||
className="w-full bg-white border border-gray-100 shadow-sm hover:shadow-md transition-all ease-out duration-180 rounded-md py-4 px-3 space-x-1 text-base tracking-tight font-bold leading-tight text-center flex flex-col justify-center items-center px-2 py-4 space-y-4"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" className="w-1/2" fill="#52C1DE">
|
||||
<path d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z" />
|
||||
</svg>
|
||||
<div className="sm:text-base md:text-sm lg:text-base">
|
||||
React
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<Link
|
||||
href={`${props.version.alias}/a${cleanPath(router.asPath)}`}
|
||||
replace
|
||||
>
|
||||
<a
|
||||
onClick={() => props.onSelect(angularFlavor)}
|
||||
className="w-full bg-white border border-gray-100 shadow-sm hover:shadow-md transition-all ease-out duration-180 rounded-md py-4 px-3 space-x-1 text-base tracking-tight font-bold leading-tight text-center flex flex-col justify-center items-center px-2 py-4 space-y-4"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" className="w-1/2" fill="#E23236">
|
||||
<path d="M9.931 12.645h4.138l-2.07-4.908m0-7.737L.68 3.982l1.726 14.771L12 24l9.596-5.242L23.32 3.984 11.999.001zm7.064 18.31h-2.638l-1.422-3.503H8.996l-1.422 3.504h-2.64L12 2.65z" />
|
||||
</svg>
|
||||
<div className="sm:text-base md:text-sm lg:text-base">
|
||||
Angular
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<Link
|
||||
href={`${props.version.alias}/n${cleanPath(router.asPath)}`}
|
||||
replace
|
||||
>
|
||||
<a
|
||||
onClick={() => props.onSelect(nodeFlavor)}
|
||||
className="w-full bg-white border border-gray-100 shadow-sm hover:shadow-md transition-all ease-out duration-180 rounded-md py-4 px-3 space-x-1 text-base tracking-tight font-bold leading-tight text-center flex flex-col justify-center items-center px-2 py-4 space-y-4"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" className="w-1/2" fill="#77AE64">
|
||||
<path d="M11.998,24c-0.321,0-0.641-0.084-0.922-0.247l-2.936-1.737c-0.438-0.245-0.224-0.332-0.08-0.383 c0.585-0.203,0.703-0.25,1.328-0.604c0.065-0.037,0.151-0.023,0.218,0.017l2.256,1.339c0.082,0.045,0.197,0.045,0.272,0l8.795-5.076 c0.082-0.047,0.134-0.141,0.134-0.238V6.921c0-0.099-0.053-0.192-0.137-0.242l-8.791-5.072c-0.081-0.047-0.189-0.047-0.271,0 L3.075,6.68C2.99,6.729,2.936,6.825,2.936,6.921v10.15c0,0.097,0.054,0.189,0.139,0.235l2.409,1.392 c1.307,0.654,2.108-0.116,2.108-0.89V7.787c0-0.142,0.114-0.253,0.256-0.253h1.115c0.139,0,0.255,0.112,0.255,0.253v10.021 c0,1.745-0.95,2.745-2.604,2.745c-0.508,0-0.909,0-2.026-0.551L2.28,18.675c-0.57-0.329-0.922-0.945-0.922-1.604V6.921 c0-0.659,0.353-1.275,0.922-1.603l8.795-5.082c0.557-0.315,1.296-0.315,1.848,0l8.794,5.082c0.57,0.329,0.924,0.944,0.924,1.603 v10.15c0,0.659-0.354,1.273-0.924,1.604l-8.794,5.078C12.643,23.916,12.324,24,11.998,24z M19.099,13.993 c0-1.9-1.284-2.406-3.987-2.763c-2.731-0.361-3.009-0.548-3.009-1.187c0-0.528,0.235-1.233,2.258-1.233 c1.807,0,2.473,0.389,2.747,1.607c0.024,0.115,0.129,0.199,0.247,0.199h1.141c0.071,0,0.138-0.031,0.186-0.081 c0.048-0.054,0.074-0.123,0.067-0.196c-0.177-2.098-1.571-3.076-4.388-3.076c-2.508,0-4.004,1.058-4.004,2.833 c0,1.925,1.488,2.457,3.895,2.695c2.88,0.282,3.103,0.703,3.103,1.269c0,0.983-0.789,1.402-2.642,1.402 c-2.327,0-2.839-0.584-3.011-1.742c-0.02-0.124-0.126-0.215-0.253-0.215h-1.137c-0.141,0-0.254,0.112-0.254,0.253 c0,1.482,0.806,3.248,4.655,3.248C17.501,17.007,19.099,15.91,19.099,13.993z" />
|
||||
</svg>
|
||||
<div className="sm:text-base md:text-sm lg:text-base">Node</div>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default FlavorSelectionDialog;
|
||||
@ -1,167 +0,0 @@
|
||||
import {
|
||||
FlavorMetadata,
|
||||
VersionMetadata,
|
||||
} from '@nrwl/nx-dev/data-access-documents';
|
||||
import { useRouter } from 'next/router';
|
||||
import { motion, useAnimation } from 'framer-motion';
|
||||
import {
|
||||
useFlavors,
|
||||
useVersions,
|
||||
} from '@nrwl/nx-dev/feature-versions-and-flavors';
|
||||
import { pathCleaner } from './utils';
|
||||
import Link from 'next/link';
|
||||
|
||||
export interface FlavorSelectionDialogProps {
|
||||
version: VersionMetadata;
|
||||
isSelected: boolean;
|
||||
onSelect: (f: FlavorMetadata) => void;
|
||||
}
|
||||
|
||||
export function FlavorsBanner(props: FlavorSelectionDialogProps) {
|
||||
const router = useRouter();
|
||||
const flavors = useFlavors();
|
||||
const versions = useVersions();
|
||||
const cleanPath = pathCleaner(versions, flavors);
|
||||
const angularFlavor = flavors.find((f) => f.alias === 'a') as FlavorMetadata;
|
||||
const reactFlavor = flavors.find((f) => f.alias === 'r') as FlavorMetadata;
|
||||
const nodeFlavor = flavors.find((f) => f.alias === 'n') as FlavorMetadata;
|
||||
const defaultFlavor = flavors.find((f) => f.default) as FlavorMetadata;
|
||||
const controls = useAnimation();
|
||||
|
||||
if (props.isSelected) controls.start('hidden');
|
||||
else controls.start('visible');
|
||||
|
||||
return (
|
||||
<div className="relative w-full max-w-screen-lg mx-auto">
|
||||
<motion.div
|
||||
key={'angular'}
|
||||
initial="hidden"
|
||||
variants={{
|
||||
hidden: { y: -20, opacity: 0 },
|
||||
visible: { y: 0, opacity: 1 },
|
||||
}}
|
||||
animate={controls}
|
||||
transition={{
|
||||
when: 'beforeChildren',
|
||||
staggerChildren: 0.12,
|
||||
ease: 'linear',
|
||||
duration: 0.88,
|
||||
type: 'tween',
|
||||
}}
|
||||
exit="hidden"
|
||||
className="absolute left-0 top-0 w-full py-3 px-3 sm:px-6 lg:px-8 bg-white z-40 rounded-md shadow-md"
|
||||
>
|
||||
<div className="flex items-center justify-between flex-wrap">
|
||||
<div className="flex-1 flex items-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<p className="ml-3 font-medium text-gray-500">
|
||||
Nx comes with dedicated documentation for each framework:
|
||||
</p>
|
||||
</div>
|
||||
<div className="order-3 mt-2 flex-shrink-0 flex w-full sm:order-2 sm:mt-0 sm:w-auto">
|
||||
<div className="relative z-0 flex flex-grow w-full shadow-sm rounded-md">
|
||||
<Link
|
||||
href={`${props.version.alias}/r${cleanPath(router.asPath)}`}
|
||||
replace
|
||||
>
|
||||
<a
|
||||
onClick={() => props.onSelect(reactFlavor)}
|
||||
className="flex flex-grow items-center justify-center px-4 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-600 bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-blue-nx-base"
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4 mr-2"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z" />
|
||||
</svg>
|
||||
React
|
||||
</a>
|
||||
</Link>
|
||||
<Link
|
||||
href={`${props.version.alias}/a${cleanPath(router.asPath)}`}
|
||||
replace
|
||||
>
|
||||
<a
|
||||
onClick={() => props.onSelect(angularFlavor)}
|
||||
className="-ml-px flex flex-grow items-center justify-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-600 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-blue-nx-base"
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4 mr-2"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M9.931 12.645h4.138l-2.07-4.908m0-7.737L.68 3.982l1.726 14.771L12 24l9.596-5.242L23.32 3.984 11.999.001zm7.064 18.31h-2.638l-1.422-3.503H8.996l-1.422 3.504h-2.64L12 2.65z" />
|
||||
</svg>{' '}
|
||||
Angular
|
||||
</a>
|
||||
</Link>
|
||||
<Link
|
||||
href={`${props.version.alias}/n${cleanPath(router.asPath)}`}
|
||||
replace
|
||||
>
|
||||
<a
|
||||
onClick={() => props.onSelect(nodeFlavor)}
|
||||
className="-ml-px flex flex-grow items-center justify-center px-4 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-600 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-blue-nx-base"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
role="img"
|
||||
className="w-4 h-4 mr-2"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M11.998,24c-0.321,0-0.641-0.084-0.922-0.247l-2.936-1.737c-0.438-0.245-0.224-0.332-0.08-0.383 c0.585-0.203,0.703-0.25,1.328-0.604c0.065-0.037,0.151-0.023,0.218,0.017l2.256,1.339c0.082,0.045,0.197,0.045,0.272,0l8.795-5.076 c0.082-0.047,0.134-0.141,0.134-0.238V6.921c0-0.099-0.053-0.192-0.137-0.242l-8.791-5.072c-0.081-0.047-0.189-0.047-0.271,0 L3.075,6.68C2.99,6.729,2.936,6.825,2.936,6.921v10.15c0,0.097,0.054,0.189,0.139,0.235l2.409,1.392 c1.307,0.654,2.108-0.116,2.108-0.89V7.787c0-0.142,0.114-0.253,0.256-0.253h1.115c0.139,0,0.255,0.112,0.255,0.253v10.021 c0,1.745-0.95,2.745-2.604,2.745c-0.508,0-0.909,0-2.026-0.551L2.28,18.675c-0.57-0.329-0.922-0.945-0.922-1.604V6.921 c0-0.659,0.353-1.275,0.922-1.603l8.795-5.082c0.557-0.315,1.296-0.315,1.848,0l8.794,5.082c0.57,0.329,0.924,0.944,0.924,1.603 v10.15c0,0.659-0.354,1.273-0.924,1.604l-8.794,5.078C12.643,23.916,12.324,24,11.998,24z M19.099,13.993 c0-1.9-1.284-2.406-3.987-2.763c-2.731-0.361-3.009-0.548-3.009-1.187c0-0.528,0.235-1.233,2.258-1.233 c1.807,0,2.473,0.389,2.747,1.607c0.024,0.115,0.129,0.199,0.247,0.199h1.141c0.071,0,0.138-0.031,0.186-0.081 c0.048-0.054,0.074-0.123,0.067-0.196c-0.177-2.098-1.571-3.076-4.388-3.076c-2.508,0-4.004,1.058-4.004,2.833 c0,1.925,1.488,2.457,3.895,2.695c2.88,0.282,3.103,0.703,3.103,1.269c0,0.983-0.789,1.402-2.642,1.402 c-2.327,0-2.839-0.584-3.011-1.742c-0.02-0.124-0.126-0.215-0.253-0.215h-1.137c-0.141,0-0.254,0.112-0.254,0.253 c0,1.482,0.806,3.248,4.655,3.248C17.501,17.007,19.099,15.91,19.099,13.993z" />
|
||||
</svg>
|
||||
Node
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-2 flex-shrink-0 sm:order-3 sm:ml-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => props.onSelect(defaultFlavor)}
|
||||
className="-mr-1 flex p-2 rounded-md text-gray-500 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-nx-base sm:-mr-2"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FlavorsBanner;
|
||||
@ -1,163 +0,0 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { VersionsAndFlavorsProvider } from '@nrwl/nx-dev/feature-versions-and-flavors';
|
||||
import { useSelectedFlavor } from './use-selected-flavor';
|
||||
import { FlavorMetadata } from '@nrwl/nx-dev/data-access-documents';
|
||||
import { NextRouter, useRouter } from 'next/router';
|
||||
|
||||
jest.mock('next/router', () => {
|
||||
const router = {
|
||||
asPath: '/getting-started/intro',
|
||||
replace: () => null,
|
||||
};
|
||||
return { useRouter: () => router };
|
||||
});
|
||||
|
||||
class MockStorage {
|
||||
private cache: Record<string, string> = {};
|
||||
|
||||
getItem(key: string) {
|
||||
return this.cache[key];
|
||||
}
|
||||
|
||||
setItem(key: string, value: string) {
|
||||
this.cache[key] = value;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.cache = {};
|
||||
}
|
||||
}
|
||||
|
||||
const mockFlavors: FlavorMetadata[] = [
|
||||
{ id: 'angular', name: 'Angular', alias: 'a', path: 'angular' },
|
||||
{
|
||||
id: 'react',
|
||||
name: 'React',
|
||||
alias: 'r',
|
||||
path: 'react',
|
||||
default: true,
|
||||
},
|
||||
{ id: 'node', name: 'Node', alias: 'n', path: 'node' },
|
||||
];
|
||||
|
||||
const latestVersion = {
|
||||
name: 'Latest',
|
||||
id: 'latest',
|
||||
alias: 'l',
|
||||
release: 'master',
|
||||
path: 'latest',
|
||||
default: true,
|
||||
};
|
||||
|
||||
function createWrapper(opts: { activeAlias: string; isFallback: boolean }) {
|
||||
return function Wrapper(props: any) {
|
||||
return (
|
||||
<VersionsAndFlavorsProvider
|
||||
value={{
|
||||
flavors: mockFlavors,
|
||||
versions: [latestVersion],
|
||||
activeFlavor: getFlavor(opts),
|
||||
activeVersion: latestVersion,
|
||||
isFallbackActiveFlavor: opts.isFallback,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</VersionsAndFlavorsProvider>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function getFlavor(opts: { activeAlias: string }) {
|
||||
return mockFlavors.find(
|
||||
(f) => f.alias === opts.activeAlias
|
||||
) as FlavorMetadata;
|
||||
}
|
||||
|
||||
describe('useSelectedFlavor', () => {
|
||||
let router: NextRouter;
|
||||
let replaceSpy: jest.SpyInstance;
|
||||
let mockStorage: MockStorage;
|
||||
|
||||
beforeAll(() => {
|
||||
router = useRouter();
|
||||
replaceSpy = jest.spyOn(router, 'replace');
|
||||
mockStorage = new MockStorage();
|
||||
Object.defineProperty(window, 'localStorage', { value: mockStorage });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
replaceSpy.mockReset();
|
||||
mockStorage.clear();
|
||||
});
|
||||
|
||||
it('should store flavor in storage when `setSelectedFlavor` is called', () => {
|
||||
router.asPath = '/getting-started/intro';
|
||||
const { result } = renderHook(() => useSelectedFlavor(), {
|
||||
wrapper: createWrapper({ activeAlias: 'a', isFallback: true }),
|
||||
});
|
||||
|
||||
act(() =>
|
||||
result.current.setSelectedFlavor(getFlavor({ activeAlias: 'r' }))
|
||||
);
|
||||
|
||||
expect(mockStorage.getItem('flavor')).toEqual('r');
|
||||
|
||||
act(() =>
|
||||
result.current.setSelectedFlavor(getFlavor({ activeAlias: 'n' }))
|
||||
);
|
||||
|
||||
expect(mockStorage.getItem('flavor')).toEqual('n');
|
||||
});
|
||||
|
||||
it('should redirect when active and selected flavors are different', () => {
|
||||
router.asPath = '/l/a/getting-started/intro';
|
||||
const { result } = renderHook(() => useSelectedFlavor(), {
|
||||
wrapper: createWrapper({ activeAlias: 'a', isFallback: true }),
|
||||
});
|
||||
|
||||
act(() =>
|
||||
result.current.setSelectedFlavor(getFlavor({ activeAlias: 'r' }))
|
||||
);
|
||||
|
||||
expect(router.replace).toHaveBeenCalledWith('/l/r/getting-started/intro');
|
||||
});
|
||||
|
||||
it('should not redirect when selected and active flavors are the same', () => {
|
||||
router.asPath = '/l/r/getting-started/intro';
|
||||
const { result } = renderHook(() => useSelectedFlavor(), {
|
||||
wrapper: createWrapper({ activeAlias: 'r', isFallback: true }),
|
||||
});
|
||||
|
||||
act(() =>
|
||||
result.current.setSelectedFlavor(getFlavor({ activeAlias: 'r' }))
|
||||
);
|
||||
|
||||
expect(router.replace).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should redirect when URL is a fallback URL', () => {
|
||||
router.asPath = '/getting-started/intro';
|
||||
const { result } = renderHook(() => useSelectedFlavor(), {
|
||||
wrapper: createWrapper({ activeAlias: 'a', isFallback: true }),
|
||||
});
|
||||
|
||||
act(() =>
|
||||
result.current.setSelectedFlavor(getFlavor({ activeAlias: 'a' }))
|
||||
);
|
||||
|
||||
expect(router.replace).toHaveBeenCalledWith('/l/a/getting-started/intro');
|
||||
});
|
||||
|
||||
it('should not redirect when URL is not fallback URL', () => {
|
||||
router.asPath = '/l/r/getting-started/intro';
|
||||
const { result } = renderHook(() => useSelectedFlavor(), {
|
||||
wrapper: createWrapper({ activeAlias: 'r', isFallback: false }),
|
||||
});
|
||||
|
||||
act(() =>
|
||||
result.current.setSelectedFlavor(getFlavor({ activeAlias: 'r' }))
|
||||
);
|
||||
|
||||
expect(router.replace).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -1,50 +0,0 @@
|
||||
import {
|
||||
useActiveFlavor,
|
||||
useActiveVersion,
|
||||
useFlavors,
|
||||
useVersions,
|
||||
} from '@nrwl/nx-dev/feature-versions-and-flavors';
|
||||
import { pathCleaner } from '@nrwl/nx-dev/feature-flavor-selection';
|
||||
import { useStorage } from '@nrwl/nx-dev/feature-storage';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { FlavorMetadata } from '@nrwl/nx-dev/data-access-documents';
|
||||
|
||||
export function useSelectedFlavor() {
|
||||
const versions = useVersions();
|
||||
const flavors = useFlavors();
|
||||
const activeFlavor = useActiveFlavor();
|
||||
const activeVersion = useActiveVersion();
|
||||
const cleanPath = pathCleaner(versions, flavors);
|
||||
const { value: selectedFlavor, setValue: setSelectedFlavor } =
|
||||
useStorage('flavor');
|
||||
const router = useRouter();
|
||||
const handleSetFlavor = (f: FlavorMetadata) => setSelectedFlavor(f.alias);
|
||||
|
||||
const flavorSelected = useMemo(
|
||||
() => !(activeFlavor.isFallback && !selectedFlavor) && !!selectedFlavor,
|
||||
[activeFlavor.isFallback, selectedFlavor]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeFlavor.isFallback || !selectedFlavor) return;
|
||||
|
||||
// If the selected flavor is different then, navigate away.
|
||||
// Otherwise, replace current URL _if_ it is missing version+flavor.
|
||||
if (activeFlavor.alias !== selectedFlavor) {
|
||||
router.replace(
|
||||
`/${activeVersion.alias}/${selectedFlavor}${cleanPath(router.asPath)}`
|
||||
);
|
||||
} else if (!router.asPath.startsWith(`/${activeVersion.alias}`)) {
|
||||
router.replace(
|
||||
`/${activeVersion.alias}/${selectedFlavor}${cleanPath(router.asPath)}`
|
||||
);
|
||||
}
|
||||
}, [router, activeVersion, activeFlavor, selectedFlavor]);
|
||||
|
||||
return {
|
||||
selectedFlavor,
|
||||
setSelectedFlavor: handleSetFlavor,
|
||||
flavorSelected,
|
||||
};
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
import {
|
||||
FlavorMetadata,
|
||||
VersionMetadata,
|
||||
} from '@nrwl/nx-dev/data-access-documents';
|
||||
|
||||
export function pathCleaner(
|
||||
versions: VersionMetadata[],
|
||||
flavors: FlavorMetadata[]
|
||||
) {
|
||||
return (path: string): string => {
|
||||
const [first, second, ...others] = path.split('/').filter(Boolean);
|
||||
const cleanPath = [];
|
||||
|
||||
if (!versions.find((v) => [v.alias, v.id].includes(first))) {
|
||||
cleanPath.push(first);
|
||||
}
|
||||
|
||||
if (!flavors.find((f) => [f.alias, f.id].includes(second))) {
|
||||
cleanPath.push(second);
|
||||
}
|
||||
|
||||
return '/' + (others ? cleanPath.concat(...others) : cleanPath).join('/');
|
||||
};
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"lib": ["dom"]
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["node"]
|
||||
},
|
||||
"files": [
|
||||
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||
"../../node_modules/@nrwl/next/typings/image.d.ts"
|
||||
],
|
||||
"exclude": ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.tsx", "**/*.test.tsx"],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user