docs(nxdev): improve markdown capabilities (#10965)

This commit is contained in:
Benjamin Cabanes 2022-06-30 15:30:38 -04:00 committed by GitHub
parent aa2934674e
commit 778f13fdaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 1108 additions and 512 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -14,8 +14,14 @@ Most developers will benefit from Nx Cloud without ever changing any of their wo
The [top level organization page](https://nx.app/orgs/5e38af6de037b5000598b2d6/workspaces/5edaf12087863a0005781f17) displays recent runs and a helpful dashboard of information about commands run in the repository. The [top level organization page](https://nx.app/orgs/5e38af6de037b5000598b2d6/workspaces/5edaf12087863a0005781f17) displays recent runs and a helpful dashboard of information about commands run in the repository.
<iframe src="https://nx.app/orgs/5e38af6de037b5000598b2d6/workspaces/5edaf12087863a0005781f17?hideHeader=true"></iframe> {% iframe
src="https://nx.app/orgs/5e38af6de037b5000598b2d6/workspaces/5edaf12087863a0005781f17?hideHeader=true"
title="Nx Cloud dashboard"
width="100%" /%}
Each branch in the repository has its own [dedicated page](https://nx.app/branch?workspaceId=5edaf12087863a0005781f17&branchName=master) where you can view agent utilization and a waterfall task execution graph for the most recent runs against that branch. Each branch in the repository has its own [dedicated page](https://nx.app/branch?workspaceId=5edaf12087863a0005781f17&branchName=master) where you can view agent utilization and a waterfall task execution graph for the most recent runs against that branch.
<iframe src="https://nx.app/branch?workspaceId=5edaf12087863a0005781f17&branchName=master&hideHeader=true"></iframe> {% iframe
src="https://nx.app/branch?workspaceId=5edaf12087863a0005781f17&branchName=master&hideHeader=true"
title="Nx Cloud branch view"
width="100%" /%}

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 1: Create Application # Angular Nx Tutorial - Step 1: Create Application
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/i37yJKK8qGI" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/i37yJKK8qGI"
title="Nx.dev Tutorial | Angular | Step 1: Create Application"
width="100%" /%}
In this tutorial you use Nx to build a full-stack application out of common libraries using modern technologies like Cypress and Nest. In this tutorial you use Nx to build a full-stack application out of common libraries using modern technologies like Cypress and Nest.

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 2: Add E2E Tests # Angular Nx Tutorial - Step 2: Add E2E Tests
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/owRAO75DIR4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/owRAO75DIR4"
title="Nx.dev Tutorial | Angular | Step 2: Add E2E Test"
width="100%" /%}
By default, Nx uses [Cypress](/cypress/overview) to run E2E tests. By default, Nx uses [Cypress](/cypress/overview) to run E2E tests.

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 3: Display Todos # Angular Nx Tutorial - Step 3: Display Todos
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/JlKAwGXmpac" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/JlKAwGXmpac"
title="Nx.dev Tutorial | Angular | Step 3: Display Todos"
width="100%" /%}
Great! You have a failing E2E test. Now you can make it pass! Great! You have a failing E2E test. Now you can make it pass!

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 4: Connect to an API # Angular Nx Tutorial - Step 4: Connect to an API
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/digMpZzPtg8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/JlKAwGXmpac"
title="Nx.dev Tutorial | Angular | Step 4: Connect to API"
width="100%" /%}
Real-world applications do not live in isolationthey need APIs to talk to. Setup your app to talk to an API. Real-world applications do not live in isolationthey need APIs to talk to. Setup your app to talk to an API.

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 5: Add Node Application Implementing an API # Angular Nx Tutorial - Step 5: Add Node Application Implementing an API
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/SsCx2WErVTI" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/SsCx2WErVTI"
title="Nx.dev Tutorial | Angular | Step 5: Add Node Application Implementing API"
width="100%" /%}
The requests fail because the API has not been created yet. Using Nx you can develop node applications next to your Angular applications. You can use same commands to run and test them. You can share code between the backend and the frontend. Use this capability to implement the API service. The requests fail because the API has not been created yet. Using Nx you can develop node applications next to your Angular applications. You can use same commands to run and test them. You can share code between the backend and the frontend. Use this capability to implement the API service.

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 6: Proxy # Angular Nx Tutorial - Step 6: Proxy
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/7d6jDAbmVnE" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/7d6jDAbmVnE"
title="Nx.dev Tutorial | Angular | Step 6: Configure Proxy"
width="100%" /%}
You passed `--frontendProject=todos` when creating the node application. What did that argument do? You passed `--frontendProject=todos` when creating the node application. What did that argument do?

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 7: Share Code # Angular Nx Tutorial - Step 7: Share Code
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/icyOSQ6gAm0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/icyOSQ6gAm0"
title="Nx.dev Tutorial | Angular | Step 7: Share Code"
width="100%" /%}
Awesome! The application is working end to end! However, there is a problem. Both the backend and the frontend define the `Todo` interface. The interface is in sync now, but in a real application, over time, it will diverge, and, as a result, runtime errors will creep in. You should share this interface between the backend and the frontend. In Nx, you can do this by creating a library. Awesome! The application is working end to end! However, there is a problem. Both the backend and the frontend define the `Todo` interface. The interface is in sync now, but in a real application, over time, it will diverge, and, as a result, runtime errors will creep in. You should share this interface between the backend and the frontend. In Nx, you can do this by creating a library.

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 8: Create Libs # Angular Nx Tutorial - Step 8: Create Libs
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/szaH7fNw0zg" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/szaH7fNw0zg"
title="Nx.dev Tutorial | Angular | Step 8: Create Libraries"
width="100%" /%}
Libraries are not just a way to share code in Nx. They are also useful for factoring out code into small units with a well-defined public API. Libraries are not just a way to share code in Nx. They are also useful for factoring out code into small units with a well-defined public API.
@ -12,7 +15,7 @@ Every library has an `index.ts` file, which defines its public API. Other applic
To illustrate how useful libraries can be, create a library of Angular components. To illustrate how useful libraries can be, create a library of Angular components.
Use the generate to scaffold a new library: Use the generator to scaffold a new library:
```sh ```sh
npx nx g @nrwl/angular:lib ui npx nx g @nrwl/angular:lib ui

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 9: Using the Project Graph # Angular Nx Tutorial - Step 9: Using the Project Graph
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/8fr2RukmfW0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/8fr2RukmfW0"
title="Nx.dev Tutorial | Angular | Step 9: Dep Graph"
width="100%" /%}
An Nx workspace can contain dozens or hundreds of applications and libraries. As a codebase grows, it becomes more difficult to understand how they depend on each other and the implications of making a particular change. An Nx workspace can contain dozens or hundreds of applications and libraries. As a codebase grows, it becomes more difficult to understand how they depend on each other and the implications of making a particular change.

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 10: Computation Caching # Angular Nx Tutorial - Step 10: Computation Caching
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/HX3--ilBhBs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/HX3--ilBhBs"
title="Nx.dev Tutorial | Angular | Step 10: Use Computation Caching"
width="100%" /%}
Nx has built-in computation caching, which helps drastically improve the performance of the commands. Nx has built-in computation caching, which helps drastically improve the performance of the commands.

View File

@ -1,6 +1,9 @@
# Angular Nx Tutorial - Step 11: Testing Affected Projects # Angular Nx Tutorial - Step 11: Testing Affected Projects
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/5t77CPl-bbM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/5t77CPl-bbM"
title="Nx.dev Tutorial | Angular | Step 11: Test Affected Projects"
width="100%" /%}
Because Nx understands the project graph of your workspace, Nx is efficient at retesting and rebuilding your projects. Because Nx understands the project graph of your workspace, Nx is efficient at retesting and rebuilding your projects.

View File

@ -16,7 +16,7 @@ Adding Nx to your CI pipeline makes this more efficient.
Nx provides out-of-the-box implementation of CI workflows for `GitHub`, `Azure` and `CircleCI` during the [creation of the Nx workspace](/cli/create-nx-workspace#ci) or later using the [ci-workflow](/packages/workspace/generators/ci-workflow) generator. Nx provides out-of-the-box implementation of CI workflows for `GitHub`, `Azure` and `CircleCI` during the [creation of the Nx workspace](/cli/create-nx-workspace#ci) or later using the [ci-workflow](/packages/workspace/generators/ci-workflow) generator.
<div class="nx-cloud-section"> {% nx-cloud-section %}
## Distributed CI with Nx Cloud ## Distributed CI with Nx Cloud
@ -45,7 +45,7 @@ npx nx-cloud stop-all-agents
Learn more about configuring your CI environment using Nx Cloud with [Distributed Caching](/nx-cloud/set-up/set-up-caching) and [Distributed Task Execution](/nx-cloud/set-up/set-up-dte) in the Nx Cloud docs. Learn more about configuring your CI environment using Nx Cloud with [Distributed Caching](/nx-cloud/set-up/set-up-caching) and [Distributed Task Execution](/nx-cloud/set-up/set-up-dte) in the Nx Cloud docs.
</div> {% /nx-cloud-section %}
## CI provider specific documentation ## CI provider specific documentation

View File

@ -56,35 +56,53 @@ Even though we started building Nx Console as a tool for experts, we also aimed
The `Generate` action allows you to choose a generator and then opens a form listing out all the options for that generator. As you make changes to the form, the generator is executed in `--dry-run` mode in a terminal so you can preview the results of running the generator in real time. The `Generate` action allows you to choose a generator and then opens a form listing out all the options for that generator. As you make changes to the form, the generator is executed in `--dry-run` mode in a terminal so you can preview the results of running the generator in real time.
<iframe loading="lazy" width="560" height="420" src="https://www.youtube.com/embed/-nUr66MWRiE?rel=0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/-nUr66MWRiE"
title="Nx Console Generate UI Form"
width="100%" /%}
**From the Command Palette** **From the Command Palette**
You can also launch the `Generate` action from the Command Palette (`⇧⌘P`) by selecting `nx: generate (ui)`. You can also launch the `Generate` action from the Command Palette (`⇧⌘P`) by selecting `nx: generate (ui)`.
<iframe loading="lazy" width="560" height="420" src="https://www.youtube.com/embed/Sk2XjFwF8Zo?rel=0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/Sk2XjFwF8Zo"
title="Nx Console Generate UI from Command Palette"
width="100%" /%}
You can even construct the generator options while staying entirely within the Command Palette. Use `⇧⌘P` to open the Command Palette, then select `nx: generate`. After choosing a generator, select any of the listed options to modify the generator command. When you're satisfied with the constructed command, choose the `Execute` command at the top of the list. You can even construct the generator options while staying entirely within the Command Palette. Use `⇧⌘P` to open the Command Palette, then select `nx: generate`. After choosing a generator, select any of the listed options to modify the generator command. When you're satisfied with the constructed command, choose the `Execute` command at the top of the list.
<iframe loading="lazy" width="560" height="420" src="https://www.youtube.com/embed/q5NTTqRYq9c?rel=0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/q5NTTqRYq9c"
title="Nx Console Generate with Command Palette"
width="100%" /%}
#### Run #### Run
The `Run` action allows you to choose an executor command and then opens a form listing out all the options for that executor. The frequently used executor commands `build`, `serve`, `test`, `e2e` and `lint` also have their own dedicated actions. The `Run` action allows you to choose an executor command and then opens a form listing out all the options for that executor. The frequently used executor commands `build`, `serve`, `test`, `e2e` and `lint` also have their own dedicated actions.
<iframe loading="lazy" width="560" height="420" src="https://www.youtube.com/embed/rNImFxo9gYs?rel=0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/rNImFxo9gYs"
title="Nx Console Run UI Form"
width="100%" /%}
**From the Command Palette** **From the Command Palette**
You can also construct the executor command options while staying entirely within the Command Palette. Use `⇧⌘P` to open the Command Palette, then select `nx: test`. After choosing a project, select any of the listed options to modify the executor command options. When you're satisfied with the constructed command, choose the `Execute` command at the top of the list. You can also construct the executor command options while staying entirely within the Command Palette. Use `⇧⌘P` to open the Command Palette, then select `nx: test`. After choosing a project, select any of the listed options to modify the executor command options. When you're satisfied with the constructed command, choose the `Execute` command at the top of the list.
<iframe loading="lazy" width="560" height="420" src="https://www.youtube.com/embed/CsUkSyQcxwQ?rel=0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/CsUkSyQcxwQ"
title="Nx Console Run from Command Palette"
width="100%" /%}
#### Common Nx Commands #### Common Nx Commands
You can also launch other common Nx commands with the options listed out in the Command Palette. You can also launch other common Nx commands with the options listed out in the Command Palette.
<iframe loading="lazy" width="560" height="420" src="https://www.youtube.com/embed/v6Tso0lB6S4?rel=0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/v6Tso0lB6S4"
title="Nx Console Affected"
width="100%" /%}
#### Projects #### Projects
@ -96,7 +114,10 @@ Clicking the ![folder-light.svg](./folder-light.svg) icon next to a project reve
Clicking the ![continue-light.svg](./continue-light.svg) icon next to an executor command executes that command without prompting for options. Clicking the ![continue-light.svg](./continue-light.svg) icon next to an executor command executes that command without prompting for options.
<iframe loading="lazy" width="560" height="420" src="https://www.youtube.com/embed/ve_N3unDqAg?rel=0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/ve_N3unDqAg"
title="Nx Console Projects Pane"
width="100%" /%}
#### Streamlining #### Streamlining

View File

@ -21,7 +21,10 @@ Nx plugins helps you develop [Angular](/packages/angular) applications with full
modern tools and libraries like [Jest](/jest/overview), [Cypress](/cypress/overview), modern tools and libraries like [Jest](/jest/overview), [Cypress](/cypress/overview),
[ESLint](/linter/eslint), [Storybook](/packages/storybook), [NgRx](/guides/misc-ngrx) and more. [ESLint](/linter/eslint), [Storybook](/packages/storybook), [NgRx](/guides/misc-ngrx) and more.
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/cXOkmOy-8dk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> {% youtube
src="https://www.youtube.com/embed/cXOkmOy-8dk"
title="Modern Angular with Nx Dev Tools"
width="100%" /%}
Check out the following to get started: Check out the following to get started:

View File

@ -21,7 +21,10 @@ and libraries like [Jest](/jest/overview), [Cypress](/cypress/overview),
[Storybook](/packages/storybook), [ESLint](/linter/eslint), and more. Nx also supports React [Storybook](/packages/storybook), [ESLint](/linter/eslint), and more. Nx also supports React
frameworks like [Next.js](/next/overview), Remix, and has great support for [React Native](/react-native/overview). frameworks like [Next.js](/next/overview), Remix, and has great support for [React Native](/react-native/overview).
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/sNz-4PUM0k8" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/sNz-4PUM0k8"
title="Scale your React development with Nx"
width="100%" /%}
Check out the following to get started: Check out the following to get started:

View File

@ -4,9 +4,12 @@
The `@nrwl/js` package ships with corresponding generators and executors that best work when it comes to developing TypeScript applications and libraries. The `@nrwl/js` package ships with corresponding generators and executors that best work when it comes to developing TypeScript applications and libraries.
> Note, you can also opt-out of TypeScript and use plain JavaScript by passing the `--js` flag to the generators. > Note, you can also opt out of TypeScript and use plain JavaScript by passing the `--js` flag to the generators.
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/-OmQ-PaSY5M" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/-OmQ-PaSY5M"
title="Develop great Typescript Packages with Nx"
width="100%" /%}
`@nrwl/js` is particularly useful if you want to `@nrwl/js` is particularly useful if you want to

View File

@ -193,7 +193,3 @@ The schema files for both Nx Devkit executors and Angular Builders are the same.
If you are writing an Nx plugin, use Nx Devkit. It's **much** easier to use and debug. It has better docs and more people supporting it. If you are writing an Nx plugin, use Nx Devkit. It's **much** easier to use and debug. It has better docs and more people supporting it.
Do you have to rewrite your Nx Plugin if it is written using Angular Devkit? No. Nx supports both and will always support both. And, most importantly, the end user might not even know what you used to write a generator or an executor. Do you have to rewrite your Nx Plugin if it is written using Angular Devkit? No. Nx supports both and will always support both. And, most importantly, the end user might not even know what you used to write a generator or an executor.
```
```

View File

@ -2,7 +2,10 @@
Nx 12.7 comes with a dedicated Storybook preset for React which drammatically simplifies the Storybook setup and makes sure that Storybook uses the same webpack configuration as your React applications running within an Nx workspace. Nx 12.7 comes with a dedicated Storybook preset for React which drammatically simplifies the Storybook setup and makes sure that Storybook uses the same webpack configuration as your React applications running within an Nx workspace.
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/oUE74McS_NY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/oUE74McS_NY"
title="New in Nx 12.7: React Storybook Preset"
width="100%" /%}
Here are the main differences to the previous versions of Nx: Here are the main differences to the previous versions of Nx:

View File

@ -12,7 +12,10 @@ npx add-nx-to-monorepo
Here's a short video walking you through the steps of adding Nx to a Lerna & Yarn workspaces based monorepo: Here's a short video walking you through the steps of adding Nx to a Lerna & Yarn workspaces based monorepo:
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/hCm373Aq1uQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/hCm373Aq1uQ"
title="Add Nx to a Lerna & Yarn workspaces monorepo"
width="100%" /%}
`npx add-nx-to-monorepo` does the following: `npx add-nx-to-monorepo` does the following:
@ -94,12 +97,21 @@ In addition, Nx makes a lot of things much easier, like building large apps incr
### Speeding Up Facebook React Monorepo with Nx ### Speeding Up Facebook React Monorepo with Nx
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/XLP2RAOwfLQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> {% youtube
src="https://www.youtube.com/embed/XLP2RAOwfLQ"
title="Speeding Up github.com/facebook/react with Nx"
width="100%" /%}
### Speeding Up Remotion Monorepo with Nx ### Speeding Up Remotion Monorepo with Nx
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/TXySu4dZLp0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allow="fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/TXySu4dZLp0"
title="Speeding Up Remotion Monorepo with Nx"
width="100%" /%}
### Speeding Up Storybook Monorepo with Nx ### Speeding Up Storybook Monorepo with Nx
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/3o8w6jbDr4A" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allow="fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/3o8w6jbDr4A"
title="Speeding Up Storybook Monorepo with Nx"
width="100%" /%}

View File

@ -410,7 +410,10 @@ Learn more about the advantages of Nx in the following guides:
## From Nx Console ## From Nx Console
<iframe loading="lazy" width="750" height="420" src="https://www.youtube.com/embed/vRj9SNVYKrE?rel=0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/vRj9SNVYKrE"
title="Nx Console Updates 17.15.0"
width="100%" /%}
As of Nx Console version 17.15.0, Angular CLI users will receive a notice periodically when running commands via Nx Console, asking if they want to use Nx to make their Angular commands faster. As of Nx Console version 17.15.0, Angular CLI users will receive a notice periodically when running commands via Nx Console, asking if they want to use Nx to make their Angular commands faster.

View File

@ -25,7 +25,10 @@ Start from [the commands mentioned in this article](https://nx.dev/migration/mig
See it in action: See it in action:
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/_XmbVpwo1vs" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/_XmbVpwo1vs"
title="Switch from CRA to Nx"
width="100%" /%}
## Doing the migration manually ## Doing the migration manually

View File

@ -40,7 +40,7 @@ jobs:
The `main` job implements the CI workflow. The `main` job implements the CI workflow.
<div class="nx-cloud-section"> {% nx-cloud-section %}
## Distributed CI with Nx Cloud ## Distributed CI with Nx Cloud
@ -97,4 +97,4 @@ jobs:
You can also use our [ci-workflow generator](/packages/workspace/generators/ci-workflow) to generate the pipeline file. You can also use our [ci-workflow generator](/packages/workspace/generators/ci-workflow) to generate the pipeline file.
</div> {% /nx-cloud-section %}

View File

@ -37,7 +37,7 @@ To use the [Nx Orb](https://github.com/nrwl/nx-orb) with a private repository on
> Note: It should be a user token, not project token. > Note: It should be a user token, not project token.
<div class="nx-cloud-section"> {% nx-cloud-section %}
## Distributed CI with Nx Cloud ## Distributed CI with Nx Cloud
@ -93,4 +93,4 @@ workflows:
You can also use our [ci-workflow generator](/packages/workspace/generators/ci-workflow) to generate the configuration file. You can also use our [ci-workflow generator](/packages/workspace/generators/ci-workflow) to generate the configuration file.
</div> {% /nx-cloud-section %}

View File

@ -32,7 +32,7 @@ jobs:
The `pr` and `main` jobs implement the CI workflow. Setting `timeout-minutes` is needed only if you have very slow tasks. The `pr` and `main` jobs implement the CI workflow. Setting `timeout-minutes` is needed only if you have very slow tasks.
<div class="nx-cloud-section"> {% nx-cloud-section %}
## Distributed CI with Nx Cloud ## Distributed CI with Nx Cloud
@ -71,4 +71,4 @@ jobs:
You can also use our [ci-workflow generator](/packages/workspace/generators/ci-workflow) to generate the workflow file. You can also use our [ci-workflow generator](/packages/workspace/generators/ci-workflow) to generate the workflow file.
</div> {% /nx-cloud-section %}

View File

@ -61,7 +61,7 @@ build:
The `build` and `test` jobs implement the CI workflow using `.distributed` as template to keep CI configuration file more readable. The `build` and `test` jobs implement the CI workflow using `.distributed` as template to keep CI configuration file more readable.
<div class="nx-cloud-section"> {% nx-cloud-section %}
## Distributed CI with Nx Cloud ## Distributed CI with Nx Cloud
@ -69,4 +69,4 @@ In order to use distributed task execution, we need to start agents and set the
Read more about the [Distributed CI setup with Nx Cloud](/using-nx/ci-overview#distributed-ci-with-nx-cloud). Read more about the [Distributed CI setup with Nx Cloud](/using-nx/ci-overview#distributed-ci-with-nx-cloud).
</div> {% /nx-cloud-section %}

View File

@ -51,7 +51,7 @@ pipeline {
The `pr` and `main` jobs implement the CI workflow. The `pr` and `main` jobs implement the CI workflow.
<div class="nx-cloud-section"> {% nx-cloud-section %}
## Distributed CI with Nx Cloud ## Distributed CI with Nx Cloud
@ -107,4 +107,4 @@ pipeline {
} }
``` ```
</div> {% /nx-cloud-section %}

View File

@ -1,6 +1,9 @@
# Node Nx Tutorial - Step 1: Create Application # Node Nx Tutorial - Step 1: Create Application
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/UcBSBQYNlhE" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/UcBSBQYNlhE"
title="Nx.dev Tutorial | Node | Step 1: Create Application"
width="100%" /%}
In this tutorial you use Nx to build a server application out of common libraries using modern technologies. In this tutorial you use Nx to build a server application out of common libraries using modern technologies.

View File

@ -1,6 +1,9 @@
# Node Nx Tutorial - Step 2: Display todos # Node Nx Tutorial - Step 2: Display todos
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/I4-sO2LeVbU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/I4-sO2LeVbU"
title="Nx.dev Tutorial | Node | Step 2: Display Todos"
width="100%" /%}
Great! you now have a server application set up to show some data when going to the `/api` route. Great! you now have a server application set up to show some data when going to the `/api` route.

View File

@ -1,6 +1,9 @@
# Node Nx Tutorial - Step 3: Share Code # Node Nx Tutorial - Step 3: Share Code
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/MqqwOoKa-Ho" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/MqqwOoKa-Ho"
title="Nx.dev Tutorial | Node | Step 3: Share Code"
width="100%" /%}
Awesome! The application is working as expected! Awesome! The application is working as expected!

View File

@ -1,6 +1,9 @@
# Node Nx Tutorial - Step 4: Create Libraries # Node Nx Tutorial - Step 4: Create Libraries
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/V29I_DHGlB8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/V29I_DHGlB8"
title="Nx.dev Tutorial | Node | Step 4: Create Libraries"
width="100%" /%}
Libraries are not just a way to share code in Nx. They are also useful for factoring out code into small units with a well-defined public API. Libraries are not just a way to share code in Nx. They are also useful for factoring out code into small units with a well-defined public API.

View File

@ -1,6 +1,9 @@
# Node Nx Tutorial - Step 5: Project Graph # Node Nx Tutorial - Step 5: Project Graph
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/l9MjZ9IPdu4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/l9MjZ9IPdu4"
title="Nx.dev Tutorial | Node | Step 5: Dep Graph"
width="100%" /%}
An Nx workspace can contain dozens or hundreds of applications and libraries. As a codebase grows, it becomes more difficult to understand how they depend on each other and the implications of making a particular change. An Nx workspace can contain dozens or hundreds of applications and libraries. As a codebase grows, it becomes more difficult to understand how they depend on each other and the implications of making a particular change.

View File

@ -1,6 +1,9 @@
# Node Nx Tutorial - Step 6: Computation Caching # Node Nx Tutorial - Step 6: Computation Caching
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/gXChzhI1Qpg" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/gXChzhI1Qpg"
title="Nx.dev Tutorial | Node | Step 6: Computation Caching"
width="100%" /%}
Nx has built-in computation caching, which drastically improves the performance of the commands. Nx has built-in computation caching, which drastically improves the performance of the commands.

View File

@ -1,6 +1,9 @@
# Node Nx Tutorial - Step 7: Test Affected Projects # Node Nx Tutorial - Step 7: Test Affected Projects
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/TRRVLyHfN60" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/TRRVLyHfN60"
title="Nx.dev Tutorial | Node | Step 7: Test Affected Projects"
width="100%" /%}
In addition to supporting computation caching, Nx scales your development by doing code change analysis to see what apps or libraries are affected by a particular pull request. In addition to supporting computation caching, Nx scales your development by doing code change analysis to see what apps or libraries are affected by a particular pull request.

View File

@ -0,0 +1,4 @@
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M128 16V32C110.336 32 96 46.336 96 64C96 81.664 81.664 96 64 96C46.336 96 32 110.336 32 128H16C7.168 128 0 120.832 0 112V16C0 7.168 7.168 0 16 0H112C120.832 0 128 7.168 128 16Z" fill="#0E2039"/>
<path d="M128 32V112C128 120.832 120.832 128 112 128H32C32 110.336 46.336 96 64 96C81.664 96 96 81.664 96 64C96 46.336 110.336 32 128 32Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 465 B

View File

@ -186,4 +186,7 @@ what `build` means. It can be an npm script, a custom Nx executor, a Gradle task
As you can see, the core of Nx is generic, simple, and unobtrusive. Nx Plugins are completely optional, but they can As you can see, the core of Nx is generic, simple, and unobtrusive. Nx Plugins are completely optional, but they can
really level up your developer experience. Watch this video to see the plugins in action. really level up your developer experience. Watch this video to see the plugins in action.
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/BO1rwynFBLM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/BO1rwynFBLM"
title="Lerna/Yarn to Nx: Faster Build Times + Better Dev Ergonomics"
width="100%" /%}

View File

@ -4,7 +4,10 @@ Nx plugins are npm packages that contain generators and executors to extend a Nx
> A list of custom plugins created by the community is found in the [Community](/community) section. > 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](/devkit/index) for more information.** > Plugins are written using Nx Devkit. **Read [Nx Devkit](/devkit/index) for more information.**
<iframe loading="lazy" 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> {% youtube
src="https://www.youtube.com/embed/fC1-4fAZDP4"
title="Nx Tutorial: Building Custom Plugins for Nx"
width="100%" /%}
## Generating a Plugin ## Generating a Plugin
@ -14,7 +17,7 @@ To get started with building a Nx Plugin, run the following command:
npx create-nx-plugin my-org --pluginName my-plugin npx create-nx-plugin my-org --pluginName my-plugin
``` ```
This command creates a brand new workspace, and sets up a pre-configured plugin with the specified name. This command creates a brand-new workspace, and sets up a pre-configured plugin with the specified name.
> Note, the command above will create a plugin the package name set to `@my-org/my-plugin`. You can pass `--importPath` to provide a different package name. > Note, the command above will create a plugin the package name set to `@my-org/my-plugin`. You can pass `--importPath` to provide a different package name.
@ -31,7 +34,7 @@ The created generator contains boilerplate that will do the following:
- Add the plugin's project to the `nx.json` file - Add the plugin's project to the `nx.json` file
- Add files to the disk using templates - Add files to the disk using templates
There will be a exported default function that will be the main entry for the generator. There will be an exported default function that will be the main entry for the generator.
### Generator options ### Generator options
@ -175,9 +178,12 @@ We will then verify the plugin, offer suggestions or merge the pull request!
## Preset ## Preset
A Preset is a customization option which you provide when creating a new workspace. TS, Node, React are some of the internal presets that Nx provides by default. A Preset is a customization option which you provide when creating a new workspace. TS, Node, React are some internal presets that Nx provides by default.
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/yGUrF0-uqaU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/yGUrF0-uqaU"
title="Develop a Nx Preset for your Nx Plugin"
width="100%" /%}
### Custom Preset ### Custom Preset

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 1: Create Application # React Nx Tutorial - Step 1: Create Application
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/HcQE5R6ucng" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/HcQE5R6ucng"
title="Nx.dev Tutorial | React | Step 1: Create Application"
width="100%" /%}
In this tutorial you use Nx to build a full-stack application out of common libraries using modern technologies. In this tutorial you use Nx to build a full-stack application out of common libraries using modern technologies.

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 2: Add E2E Tests # React Nx Tutorial - Step 2: Add E2E Tests
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/3HSzqt3WiVg" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/3HSzqt3WiVg"
title="Nx.dev Tutorial | React | Step 2: Add E2E Tests"
width="100%" /%}
By default, Nx uses [Cypress](https://cypress.io) to run E2E tests. By default, Nx uses [Cypress](https://cypress.io) to run E2E tests.

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 3: Display Todos # React Nx Tutorial - Step 3: Display Todos
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/fNehP0WX__c" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/fNehP0WX__c"
title="Nx.dev Tutorial | React | Step 3: Display Todos"
width="100%" /%}
Great! You have a failing E2E test. Now you can make it pass! Great! You have a failing E2E test. Now you can make it pass!

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 4: Connect to an API # React Nx Tutorial - Step 4: Connect to an API
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/HexxYHpIfAo" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/HexxYHpIfAo"
title="Nx.dev Tutorial | React | Step 4: Connect to an API"
width="100%" /%}
Real-world applications do not live in isolationthey need APIs to talk to. Setup your app to talk to an API. Real-world applications do not live in isolationthey need APIs to talk to. Setup your app to talk to an API.

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 5: Add Node Application Implementing API # React Nx Tutorial - Step 5: Add Node Application Implementing API
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/XgfknOqgxQ0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/XgfknOqgxQ0"
title="Nx.dev Tutorial | React | Step 5: Add Node Application Implementing API"
width="100%" /%}
The requests fail because the API has not been created yet. Using Nx you develop node applications next to your React applications. You can use same commands to run and test them. You share code between the backend and the frontend. Use this capability to implement the API service. The requests fail because the API has not been created yet. Using Nx you develop node applications next to your React applications. You can use same commands to run and test them. You share code between the backend and the frontend. Use this capability to implement the API service.

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 6: Proxy Configuration # React Nx Tutorial - Step 6: Proxy Configuration
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/xfvCz-yLeEw" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/xfvCz-yLeEw"
title="Nx.dev Tutorial | React | Step 6: Proxy"
width="100%" /%}
You passed `--frontendProject=todos` when creating the node application. What did that argument do? You passed `--frontendProject=todos` when creating the node application. What did that argument do?

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 7: Share Code # React Nx Tutorial - Step 7: Share Code
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/-zzw4_oT_2I" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/-zzw4_oT_2I"
title="Nx.dev Tutorial | React | Step 7: Share Code"
width="100%" /%}
Awesome! The application is working end to end! However, there is a problem. Both the backend and the frontend define the `Todo` interface. The interface is in sync now, but in a real application, over time, it diverges, and, as a result, runtime errors creep in. You should share this interface between the backend and the frontend. In Nx, you do this by creating a library. Awesome! The application is working end to end! However, there is a problem. Both the backend and the frontend define the `Todo` interface. The interface is in sync now, but in a real application, over time, it diverges, and, as a result, runtime errors creep in. You should share this interface between the backend and the frontend. In Nx, you do this by creating a library.

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 8: Create Libs # React Nx Tutorial - Step 8: Create Libs
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/a1CAYlXizWM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/a1CAYlXizWM"
title="Nx.dev Tutorial | React | Step 8: Create Libs"
width="100%" /%}
Libraries are not just a way to share code in Nx. They are also useful for factoring out code into small units with a well-defined public API. Libraries are not just a way to share code in Nx. They are also useful for factoring out code into small units with a well-defined public API.

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 9: Project Graph # React Nx Tutorial - Step 9: Project Graph
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/Dr7jI9RYcmY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/Dr7jI9RYcmY"
title="Nx.dev Tutorial | React | Step 9: Dep Graph"
width="100%" /%}
An Nx workspace can contain dozens or hundreds of applications and libraries. As a codebase grows, it can be difficult to understand how they depend on each other and the implications of making a particular change. An Nx workspace can contain dozens or hundreds of applications and libraries. As a codebase grows, it can be difficult to understand how they depend on each other and the implications of making a particular change.

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 10: Computation Caching # React Nx Tutorial - Step 10: Computation Caching
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/aNjvT3VX1Ts" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/aNjvT3VX1Ts"
title="Nx.dev Tutorial | React | step 10: Computation Caching"
width="100%" /%}
Nx has built-in computation caching, which helps drastically improve the performance of the commands. Nx has built-in computation caching, which helps drastically improve the performance of the commands.

View File

@ -1,6 +1,9 @@
# React Nx Tutorial - Step 11: Test Affected Projects # React Nx Tutorial - Step 11: Test Affected Projects
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/_mBBFRjs01g" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/_mBBFRjs01g"
title="Nx.dev Tutorial | React | Step 11: Test Affected Projects"
width="100%" /%}
In addition to supporting computation caching, Nx scales your development by doing code change analysis to see what is affected by a particular pull request. In addition to supporting computation caching, Nx scales your development by doing code change analysis to see what is affected by a particular pull request.

View File

@ -199,7 +199,7 @@ The cache is stored in `node_modules/.cache/nx` by default. To change the cache
By default, Nx uses a local computation cache. Nx stores the cached values only for a week, after which they are deleted. To clear the cache run `nx reset`, and Nx creates a new one the next time it tries to access it. By default, Nx uses a local computation cache. Nx stores the cached values only for a week, after which they are deleted. To clear the cache run `nx reset`, and Nx creates a new one the next time it tries to access it.
<div class="nx-cloud-section"> {% nx-cloud-section %}
## Distributed Computation Caching ## Distributed Computation Caching
@ -243,7 +243,7 @@ Tony pushes a branch up to CI, but CI doesn't pass. He asks Sofija to help. Sofi
Maya and Trey push up changes to two different apps that both depend on an unchanged shared buildable library. CI reuses the build output of the shared buildable library when building the apps in the two different PRs. Maya and Trey push up changes to two different apps that both depend on an unchanged shared buildable library. CI reuses the build output of the shared buildable library when building the apps in the two different PRs.
</div> {% /nx-cloud-section %}
## Example ## Example

View File

@ -2,7 +2,7 @@
> 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. > 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.
<div class="nx-cloud-section"> {% nx-cloud-section %}
## Overview ## Overview
@ -35,4 +35,4 @@ These are the savings you get by enabling Distributed Task Execution in your CI
![DTE](/shared/using-nx/dte.png) ![DTE](/shared/using-nx/dte.png)
</div> {% /nx-cloud-section %}

View File

@ -2,7 +2,10 @@
To be able to support the monorepo-style development, the tools must know how different projects in your workspace depend on each other. Nx uses advanced code analysis to construct this project graph. And it gives you a way to explore it: To be able to support the monorepo-style development, the tools must know how different projects in your workspace depend on each other. Nx uses advanced code analysis to construct this project graph. And it gives you a way to explore it:
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/cMZ-ReC-jWU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"></iframe> {% youtube
src="https://www.youtube.com/embed/cMZ-ReC-jWU"
title="Nx Tutorial: Improved Dependency Graph Visualization for Nx"
width="100%" /%}
## How the Project Graph is Built ## How the Project Graph is Built

View File

@ -1,118 +1,15 @@
import { sendCustomEvent } from '@nrwl/nx-dev/feature-analytics';
import { DocumentData } from '@nrwl/nx-dev/models-document'; import { DocumentData } from '@nrwl/nx-dev/models-document';
import { ReactComponentElement } from 'react'; import { renderMarkdown } from '@nrwl/nx-dev/ui-markdoc';
import ReactMarkdown from 'react-markdown'; import { ReactNode } from 'react';
import raw from 'rehype-raw';
import autolinkHeadings from 'rehype-autolink-headings';
import slug from 'rehype-slug';
import gfm from 'remark-gfm';
import { CodeBlock } from './code-block';
import { renderIframes } from './renderers/render-iframe';
import { transformImagePath } from './renderers/transform-image-path';
export interface ContentProps { export interface ContentProps {
document: DocumentData; document: DocumentData;
} }
interface ComponentsConfig { export const Content = (props: ContentProps): ReactNode => (
readonly code: { callback: (command: string) => void };
}
const components: any = (config: ComponentsConfig) => ({
img({ node, alt, src, ...props }) {
return <img src={src} alt={alt} loading="lazy" />;
},
code({ node, inline, className, children, ...props }) {
const language = /language-(\w+)/.exec(className || '')?.[1];
return !inline && language ? (
<CodeBlock
text={String(children).replace(/\n$/, '')}
language={language}
{...props}
callback={(command) => config.code.callback(command)}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
pre({ children }) {
return <>{children}</>;
},
});
export function Content(props: ContentProps): ReactComponentElement<any> {
return (
<div className="min-w-0 flex-auto px-4 pt-8 pb-24 sm:px-6 lg:pb-16 xl:px-8"> <div className="min-w-0 flex-auto px-4 pt-8 pb-24 sm:px-6 lg:pb-16 xl:px-8">
<ReactMarkdown <div className="prose max-w-none">{renderMarkdown(props.document)}</div>
remarkPlugins={[gfm]}
rehypePlugins={[
slug,
raw,
[
autolinkHeadings,
{
behavior: 'append',
content: createAnchorContent,
},
],
renderIframes,
]}
children={props.document.content}
transformImageUri={transformImagePath({
document: props.document,
})}
className="prose max-w-none"
components={components({
code: {
callback: () =>
sendCustomEvent(
'code-snippets',
'click',
props.document.filePath
),
},
})}
/>
</div> </div>
); );
}
function createAnchorContent(node: any) {
node.properties.className = ['group'];
return {
type: 'element',
tagName: 'svg',
properties: {
xmlns: 'http://www.w3.org/2000/svg',
className: [
'inline',
'ml-2',
'mb-1',
`h-5`,
`w-5`,
'opacity-0',
'group-hover:opacity-100',
],
fill: 'none',
viewBox: '0 0 24 24',
stroke: 'currentColor',
},
children: [
{
type: 'element',
tagName: 'path',
properties: {
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2',
d: 'M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1',
},
children: [],
},
],
};
}
export default Content; export default Content;

View File

@ -1,21 +0,0 @@
export function renderIframes(): (tree: any) => void {
return (tree): void => {
const iframeList = tree.children.filter(
(child) => child.type === 'raw' && child.value.includes('<iframe')
);
iframeList.forEach((item) => {
item.type = 'element';
item.tagName = 'iframe';
item.children = [];
item.properties = {};
let match;
const regex = new RegExp(
'[\\s\\r\\t\\n]*([a-z0-9\\-_]+)[\\s\\r\\t\\n]*=[\\s\\r\\t\\n]*([\'"])((?:\\\\\\2|(?!\\2).)*)\\2',
'ig'
);
while ((match = regex.exec(item.value))) {
item.properties[match[1]] = match[3];
}
});
};
}

View File

@ -1,25 +0,0 @@
import { DocumentData } from '@nrwl/nx-dev/data-access-documents';
import { join } from 'path';
import { uriTransformer } from 'react-markdown';
export function transformImagePath({
document,
}: {
document: DocumentData;
}): (src: string) => string {
return (src) => {
const isRelative = src.startsWith('.');
if (!/\.(gif|jpe?g|tiff?|png|webp|bmp|svg)$/i.test(src)) {
return uriTransformer(src);
}
if (isRelative) {
return uriTransformer(
join('/', document.filePath.split('/').splice(3).join('/'), '..', src)
);
}
return uriTransformer(`/documentation`.concat(src));
};
}

View File

@ -2,15 +2,15 @@ import { XCircleIcon } from '@heroicons/react/solid';
import { getSchemaFromReference } from '@nrwl/nx-dev/data-access-packages'; import { getSchemaFromReference } from '@nrwl/nx-dev/data-access-packages';
import { JsonSchema1, NxSchema } from '@nrwl/nx-dev/models-package'; import { JsonSchema1, NxSchema } from '@nrwl/nx-dev/models-package';
import { Breadcrumbs } from '@nrwl/nx-dev/ui-common'; import { Breadcrumbs } from '@nrwl/nx-dev/ui-common';
import { renderMarkdown } from '@nrwl/nx-dev/ui-markdoc';
import Link from 'next/link'; import Link from 'next/link';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useState } from 'react'; import React, { ReactNode, useState } from 'react';
import { generateJsonExampleFor, isErrors } from './examples'; import { generateJsonExampleFor, isErrors } from './examples';
import { SchemaViewModel } from './get-schema-view-model'; import { SchemaViewModel } from './get-schema-view-model';
import { SchemaEditor } from './schema-editor'; import { SchemaEditor } from './schema-editor';
import { SchemaViewer } from './schema-viewer'; import { SchemaViewer } from './schema-viewer';
import { Heading2, Heading3 } from './ui/headings'; import { Heading2, Heading3 } from './ui/headings';
import { Markdown } from './ui/markdown/markdown';
function pathCleaner(path: string): string { function pathCleaner(path: string): string {
return path.split('?')[0]; return path.split('?')[0];
@ -77,6 +77,19 @@ export function Content({
(x): x is { name: string; href: string; current: boolean } => !!x (x): x is { name: string; href: string; current: boolean } => !!x
); );
}, },
get markdown(): ReactNode {
return renderMarkdown({
content: getMarkdown({
type: schemaViewModel.type,
packageName: schemaViewModel.packageName,
schemaName: schemaViewModel.schemaMetadata.name,
schemaAlias: schemaViewModel.schemaMetadata.aliases[0] ?? '',
schema: schemaViewModel.currentSchema as NxSchema,
}),
filePath: '',
data: {},
});
},
}; };
return ( return (
@ -116,15 +129,7 @@ export function Content({
{/* We remove the top description on sub property lookup */} {/* We remove the top description on sub property lookup */}
{!schemaViewModel.subReference && ( {!schemaViewModel.subReference && (
<> <>
<Markdown <div className="prose max-w-none">{vm.markdown}</div>
content={getMarkdown({
type: schemaViewModel.type,
packageName: schemaViewModel.packageName,
schemaName: schemaViewModel.schemaMetadata.name,
schemaAlias: schemaViewModel.schemaMetadata.aliases[0] ?? '',
schema: schemaViewModel.currentSchema,
})}
/>
<div className="h-12">{/* SPACER */}</div> <div className="h-12">{/* SPACER */}</div>
</> </>
)} )}
@ -221,10 +226,9 @@ const getMarkdown = (data: {
packageName: string; packageName: string;
schemaAlias: string; schemaAlias: string;
schemaName: string; schemaName: string;
schema: JsonSchema1; schema: NxSchema;
type: 'executors' | 'generators'; type: 'executors' | 'generators';
}): string => { }): string => {
const hasExamples = !!data.schema['examples'];
const hasExamplesFile = !!data.schema['examplesFile']; const hasExamplesFile = !!data.schema['examplesFile'];
const executorNotice: string = `Options can be configured in \`project.json\` when defining the executor, or when invoking it. Read more about how to configure targets and executors here: [https://nx.dev/configuration/projectjson#targets](https://nx.dev/configuration/projectjson#targets).`; const executorNotice: string = `Options can be configured in \`project.json\` when defining the executor, or when invoking it. Read more about how to configure targets and executors here: [https://nx.dev/configuration/projectjson#targets](https://nx.dev/configuration/projectjson#targets).`;
@ -241,9 +245,11 @@ const getMarkdown = (data: {
? data.schema['examplesFile'] ? data.schema['examplesFile']
: getUsage(data.packageName, data.schemaName, data.schemaAlias) : getUsage(data.packageName, data.schemaName, data.schemaAlias)
: '', : '',
hasExamples !!data.schema['examples']
? `### Examples \n ${data.schema['examples'] ? `### Examples \n ${data.schema['examples']
.map((e) => `${e.description}: \n \`\`\`bash\n${e.command}\n\`\`\``) .map(
(e: any) => `${e.description}: \n \`\`\`bash\n${e.command}\n\`\`\``
)
.join('\n')}` .join('\n')}`
: '', : '',
`\n\n`, `\n\n`,

View File

@ -2,14 +2,14 @@ import { ChipIcon, CogIcon } from '@heroicons/react/solid';
import { Menu } from '@nrwl/nx-dev/models-menu'; import { Menu } from '@nrwl/nx-dev/models-menu';
import { PackageMetadata } from '@nrwl/nx-dev/models-package'; import { PackageMetadata } from '@nrwl/nx-dev/models-package';
import { Sidebar } from '@nrwl/nx-dev/ui-common'; import { Sidebar } from '@nrwl/nx-dev/ui-common';
import { renderMarkdown } from '@nrwl/nx-dev/ui-markdoc';
import cx from 'classnames'; import cx from 'classnames';
import { NextSeo } from 'next-seo'; import { NextSeo } from 'next-seo';
import Link from 'next/link'; import Link from 'next/link';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { ReactComponentElement } from 'react'; import React, { ReactComponentElement, ReactNode } from 'react';
import { getPublicPackageName } from './get-public-package-name'; import { getPublicPackageName } from './get-public-package-name';
import { Heading1, Heading2 } from './ui/headings'; import { Heading1, Heading2 } from './ui/headings';
import { Markdown } from './ui/markdown/markdown';
export function PackageSchemaList({ export function PackageSchemaList({
pkg, pkg,
@ -30,6 +30,7 @@ export function PackageSchemaList({
readme: { content: string; filePath: string }; readme: { content: string; filePath: string };
}; };
seo: { title: string; description: string; url: string; imageUrl: string }; seo: { title: string; description: string; url: string; imageUrl: string };
markdown: ReactNode;
} = { } = {
pkg: { pkg: {
name: getPublicPackageName(pkg.name), name: getPublicPackageName(pkg.name),
@ -53,6 +54,13 @@ export function PackageSchemaList({
.replace(/\//gi, '-')}.jpg`, .replace(/\//gi, '-')}.jpg`,
url: 'https://nx.dev' + router.asPath, url: 'https://nx.dev' + router.asPath,
}, },
get markdown(): ReactNode {
return renderMarkdown({
content: this.pkg.readme.content,
filePath: this.pkg.readme.filePath,
data: {},
});
},
}; };
return ( return (
@ -109,11 +117,7 @@ export function PackageSchemaList({
<Heading1 title={vm.pkg.name} /> <Heading1 title={vm.pkg.name} />
<Markdown <div className="prose mb-16 max-w-none">{vm.markdown}</div>
content={vm.pkg.readme.content}
documentFilePath={vm.pkg.readme.filePath}
classes="mb-16"
/>
<Heading2 title="Package reference" /> <Heading2 title="Package reference" />
@ -147,10 +151,13 @@ export function PackageSchemaList({
</a> </a>
</Link> </Link>
</p> </p>
<Markdown <div className="prose-sm">
content={executors.description} {renderMarkdown({
classes="prose-sm" content: executors.description,
/> data: {},
filePath: '',
})}
</div>
</div> </div>
</li> </li>
))} ))}
@ -182,10 +189,13 @@ export function PackageSchemaList({
</a> </a>
</Link> </Link>
</p> </p>
<Markdown <div className="prose-sm">
content={generators.description} {renderMarkdown({
classes="prose-sm" content: generators.description,
/> data: {},
filePath: '',
})}
</div>
</div> </div>
</li> </li>
))} ))}

View File

@ -1,10 +1,10 @@
import { Lookup } from '@nrwl/nx-dev/data-access-packages'; import { Lookup } from '@nrwl/nx-dev/data-access-packages';
import { JsonSchema } from '@nrwl/nx-dev/models-package'; import { JsonSchema } from '@nrwl/nx-dev/models-package';
import { renderMarkdown } from '@nrwl/nx-dev/ui-markdoc';
import { getParameterMetadata } from './parameter-metadata'; import { getParameterMetadata } from './parameter-metadata';
import { getEnum } from './types/get-enum'; import { getEnum } from './types/get-enum';
import { Type } from './types/type'; import { Type } from './types/type';
import { Heading3 } from './ui/headings'; import { Heading3 } from './ui/headings';
import { Markdown } from './ui/markdown/markdown';
export const ParameterView = (props: { export const ParameterView = (props: {
key: string; key: string;
@ -60,11 +60,21 @@ export const ParameterView = (props: {
)} )}
</div> </div>
<Markdown content={props.description} /> <div className="prose">
{renderMarkdown({
content: props.description,
data: {},
filePath: '',
})}
</div>
{props.deprecated && props.schema['x-deprecated'] && ( {props.deprecated && !!props.schema['x-deprecated'] && (
<div className="mt-2 rounded-md bg-red-100 p-4"> <div className="prose mt-2 rounded-md bg-red-100 p-4">
<Markdown content={props.schema['x-deprecated'] as string} /> {renderMarkdown({
content: props.schema['x-deprecated'] as string,
data: {},
filePath: '',
})}
</div> </div>
)} )}
</div> </div>

View File

@ -1,69 +0,0 @@
import React, { useEffect, useState } from 'react';
// @ts-ignore
import { CopyToClipboard } from 'react-copy-to-clipboard';
import SyntaxHighlighter from 'react-syntax-highlighter';
export function CodeBlock({
text,
language,
callback,
...rest
}: {
text: string;
language: string;
[key: string]: any;
callback: (text: string) => void;
}) {
const [copied, setCopied] = useState(false);
useEffect(() => {
let t: NodeJS.Timeout;
if (copied) {
t = setTimeout(() => {
setCopied(false);
}, 3000);
}
return () => {
t && clearTimeout(t);
};
}, [copied]);
return (
<div className="code-block group relative">
<CopyToClipboard
text={text}
onCopy={() => {
setCopied(true);
callback(text);
}}
>
<button
type="button"
className="absolute top-2 right-2 flex opacity-0 transition-opacity group-hover:opacity-100"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
<span className="ml-1 text-sm">{copied ? 'Copied!' : 'Copy'}</span>
</button>
</CopyToClipboard>
<SyntaxHighlighter
showLineNumbers={!['bash', 'text', 'treeview'].includes(language)}
useInlineStyles={false}
language={language}
children={text}
style=""
{...rest}
/>
</div>
);
}

View File

@ -1,104 +0,0 @@
import classNames from 'classnames';
import ReactMarkdown from 'react-markdown';
import autolinkHeadings from 'rehype-autolink-headings';
import slug from 'rehype-slug';
import gfm from 'remark-gfm';
import { CodeBlock } from './code-block';
import { renderIframes } from './renderers/render-iframe';
import { transformImagePath } from './renderers/transform-image-path';
export const Markdown = ({
content,
classes = '',
documentFilePath = '',
}: {
content: string;
classes?: string;
documentFilePath?: string;
}) => (
<ReactMarkdown
remarkPlugins={[gfm]}
rehypePlugins={[
slug,
[
autolinkHeadings,
{
behavior: 'append',
content: createAnchorContent,
},
],
renderIframes,
]}
children={content}
className={classNames('prose max-w-none', classes)}
transformImageUri={transformImagePath(documentFilePath)}
components={components({
code: {
callback: () => void 0,
},
})}
/>
);
function createAnchorContent(node: any) {
node.properties.className = ['group'];
return {
type: 'element',
tagName: 'svg',
properties: {
xmlns: 'http://www.w3.org/2000/svg',
className: [
'inline',
'ml-2',
'mb-1',
`h-5`,
`w-5`,
'opacity-0',
'group-hover:opacity-100',
],
fill: 'none',
viewBox: '0 0 24 24',
stroke: 'currentColor',
},
children: [
{
type: 'element',
tagName: 'path',
properties: {
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2',
d: 'M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1',
},
children: [],
},
],
};
}
interface ComponentsConfig {
readonly code: { callback: (command: string) => void };
}
const components: any = (config: ComponentsConfig) => ({
img({ node, alt, src, ...props }) {
return <img src={src} alt={alt} loading="lazy" />;
},
code({ node, inline, className, children, ...props }) {
const language = /language-(\w+)/.exec(className || '')?.[1];
return !inline && language ? (
<CodeBlock
text={String(children).replace(/\n$/, '')}
language={language}
{...props}
callback={(command) => config.code.callback(command)}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
pre({ children }) {
return <>{children}</>;
},
});

View File

@ -1,21 +0,0 @@
export function renderIframes(): (tree: any) => void {
return (tree): void => {
const iframeList = tree.children.filter(
(child) => child.type === 'raw' && child.value.includes('<iframe')
);
iframeList.forEach((item) => {
item.type = 'element';
item.tagName = 'iframe';
item.children = [];
item.properties = {};
let match;
const regex = new RegExp(
'[\\s\\r\\t\\n]*([a-z0-9\\-_]+)[\\s\\r\\t\\n]*=[\\s\\r\\t\\n]*([\'"])((?:\\\\\\2|(?!\\2).)*)\\2',
'ig'
);
while ((match = regex.exec(item.value))) {
item.properties[match[1]] = match[3];
}
});
};
}

View File

@ -39,13 +39,14 @@ pre,
General CSS rules for markdown iframes and img General CSS rules for markdown iframes and img
*/ */
iframe[src*='youtube'] { iframe[src*='youtube'] {
aspect-ratio: 16 / 9;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
border-radius: 0.375rem; border-radius: 0.375rem;
} }
.prose iframe, .prose iframe,
.prose img { .prose img {
display: block; display: block;
margin: 1.7rem auto; margin: 2rem auto;
} }
.prose iframe { .prose iframe {
width: 100%; width: 100%;
@ -78,7 +79,6 @@ iframe[src*='youtube'] {
/* /*
Headers Headers
*/ */
.prose h5, .prose h5,
.prose h6 { .prose h6 {
font-weight: 600; font-weight: 600;
@ -184,18 +184,3 @@ iframe[src*='youtube'] {
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
} }
/* Nx Cloud section */
.nx-cloud-section {
border-left-width: 0.25em;
border-left-color: hsla(162, 47%, 50%, 1);
padding-left: 1em;
margin-left: -1.25em;
}
.nx-cloud-section h2 {
background: url('/documentation/shared/nx-cloud-logo-horizontal-white.svg')
no-repeat left center;
background-size: contain;
padding-left: 40px;
}

View File

@ -0,0 +1,12 @@
{
"presets": [
[
"@nrwl/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}

View File

@ -0,0 +1,18 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

View File

@ -0,0 +1,7 @@
# nx-dev-ui-markdown
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test nx-dev-ui-markdown` to execute the unit tests via [Jest](https://jestjs.io).

View File

@ -0,0 +1,10 @@
/* eslint-disable */
export default {
displayName: 'nx-dev-ui-markdoc',
preset: '../../jest.preset.js',
transform: {
'^.+\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/nx-dev/ui-markdoc',
};

View File

@ -0,0 +1,23 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "nx-dev/ui-markdoc/src",
"projectType": "library",
"tags": [],
"targets": {
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["nx-dev/ui-markdoc/**/*.{ts,tsx,js,jsx}"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/nx-dev/ui-markdoc"],
"options": {
"jestConfig": "nx-dev/ui-markdoc/jest.config.ts",
"passWithNoTests": true
}
}
}
}

View File

@ -0,0 +1,70 @@
import Markdoc from '@markdoc/markdoc';
import { DocumentData } from '@nrwl/nx-dev/models-document';
import React, { ReactNode } from 'react';
import { Fence } from './lib/nodes/fence.component';
import { fence } from './lib/nodes/fence.schema';
import { Heading } from './lib/nodes/heading.component';
import { heading } from './lib/nodes/heading.schema';
import { getImageSchema } from './lib/nodes/image.schema';
import { CustomLink } from './lib/nodes/link.component';
import { link } from './lib/nodes/link.schema';
import { Callout } from './lib/tags/callout.component';
import { callout } from './lib/tags/callout.schema';
import { Iframe } from './lib/tags/iframe.component';
import { iframe } from './lib/tags/iframe.schema';
import { nxCloudSection } from './lib/tags/nx-cloud-section.schema';
import { NxCloudSection } from './lib/tags/nx-cloud-section.component';
import { SideBySide } from './lib/tags/side-by-side.component';
import { sideBySide } from './lib/tags/side-by-side.schema';
import { Tab, Tabs } from './lib/tags/tabs.component';
import { tab, tabs } from './lib/tags/tabs.schema';
import { YouTube } from './lib/tags/youtube.components';
import { youtube } from './lib/tags/youtube.schema';
export const getMarkdocCustomConfig = (
document: DocumentData
): { config: any; components: any } => ({
config: {
nodes: {
fence,
heading,
image: getImageSchema(document),
link,
},
tags: {
callout,
iframe,
'nx-cloud-section': nxCloudSection,
'side-by-side': sideBySide,
tab,
tabs,
youtube,
},
},
components: {
Callout,
CustomLink,
Fence,
Heading,
Iframe,
NxCloudSection,
SideBySide,
Tab,
Tabs,
YouTube,
},
});
export const renderMarkdown: (document: DocumentData) => ReactNode = (
document: DocumentData
): ReactNode => {
const configuration = getMarkdocCustomConfig(document);
const ast = Markdoc.parse(document.content.toString());
return Markdoc.renderers.react(
Markdoc.transform(ast, configuration.config),
React,
{
components: configuration.components,
}
);
};

View File

@ -14,16 +14,12 @@ function resolveLanguage(lang: string) {
} }
} }
export function CodeBlock({ export function Fence({
text, children,
language, language,
callback,
...rest
}: { }: {
text: string; children: string;
language: string; language: string;
[key: string]: any;
callback: (text: string) => void;
}) { }) {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
useEffect(() => { useEffect(() => {
@ -40,15 +36,14 @@ export function CodeBlock({
return ( return (
<div className="code-block group relative"> <div className="code-block group relative">
<CopyToClipboard <CopyToClipboard
text={text} text={children}
onCopy={() => { onCopy={() => {
setCopied(true); setCopied(true);
callback(text);
}} }}
> >
<button <button
type="button" type="button"
className="absolute top-2 right-2 flex opacity-0 transition-opacity group-hover:opacity-100" className="not-prose absolute top-2 right-2 flex opacity-0 transition-opacity group-hover:opacity-100"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -71,9 +66,7 @@ export function CodeBlock({
showLineNumbers={!['bash', 'text', 'treeview'].includes(language)} showLineNumbers={!['bash', 'text', 'treeview'].includes(language)}
useInlineStyles={false} useInlineStyles={false}
language={resolveLanguage(language)} language={resolveLanguage(language)}
children={text} children={children}
style=""
{...rest}
/> />
</div> </div>
); );

View File

@ -0,0 +1,9 @@
import { Schema } from '@markdoc/markdoc';
export const fence: Schema = {
render: 'Fence',
attributes: {
text: { type: 'String', required: true },
language: { type: 'String' },
},
};

View File

@ -0,0 +1,28 @@
import { LinkIcon } from '@heroicons/react/outline';
import { ReactNode } from 'react';
export function Heading({
id = '',
level = 1,
children,
className,
}: {
id: string;
level: number;
children: ReactNode;
className: string;
}) {
const Component: any = `h${level}`;
return (
<Component
id={id}
className={['not-prose group', className].filter(Boolean).join(' ')}
>
{children}
<a aria-hidden="true" href={`#${id}`}>
<LinkIcon className="ml-2 mb-1 inline h-5 w-5 opacity-0 group-hover:opacity-100" />
</a>
</Component>
);
}

View File

@ -0,0 +1,38 @@
import { RenderableTreeNode, Schema, Tag } from '@markdoc/markdoc';
function generateID(
children: RenderableTreeNode[],
attributes: Record<string, any>
) {
if (attributes.id && typeof attributes.id === 'string') {
return attributes.id;
}
return children
.filter((child) => typeof child === 'string')
.join(' ')
.replace(/[?]/g, '')
.replace(/\s+/g, '-')
.toLowerCase();
}
export const heading: Schema = {
render: 'Heading',
children: ['inline'],
attributes: {
id: { type: 'String' },
level: { type: 'Number', required: true, default: 1 },
className: { type: 'String' },
},
transform(node, config) {
const attributes = node.transformAttributes(config);
const children = node.transformChildren(config);
const id = generateID(children, attributes);
return new Tag(
this.render,
// `h${node.attributes['level']}`,
{ ...attributes, id },
children
);
},
};

View File

@ -3,12 +3,10 @@ import { transformImagePath } from './transform-image-path';
describe('transformImagePath', () => { describe('transformImagePath', () => {
it('should transform relative paths', () => { it('should transform relative paths', () => {
const opts = { const opts = {
document: {
content: '', content: '',
excerpt: '', excerpt: '',
filePath: 'nx-dev/nx-dev/public/documentation/shared/using-nx/dte.md', filePath: 'nx-dev/nx-dev/public/documentation/shared/using-nx/dte.md',
data: {}, data: {},
},
}; };
const transform = transformImagePath(opts); const transform = transformImagePath(opts);
@ -21,13 +19,11 @@ describe('transformImagePath', () => {
it('should transform absolute paths', () => { it('should transform absolute paths', () => {
const opts = { const opts = {
document: {
content: '', content: '',
excerpt: '', excerpt: '',
filePath: filePath:
'nx-dev/nx-dev/public/documentation/angular/generators/workspace-generators.md', 'nx-dev/nx-dev/public/documentation/angular/generators/workspace-generators.md',
data: {}, data: {},
},
}; };
const transform = transformImagePath(opts); const transform = transformImagePath(opts);

View File

@ -1,8 +1,9 @@
import { DocumentData } from '@nrwl/nx-dev/models-document';
import { join } from 'path'; import { join } from 'path';
import { uriTransformer } from 'react-markdown'; import { uriTransformer } from './uri-transformer';
export function transformImagePath( export function transformImagePath(
documentFilePath: string document: DocumentData
): (src: string) => string { ): (src: string) => string {
return (src) => { return (src) => {
const isRelative = src.startsWith('.'); const isRelative = src.startsWith('.');
@ -13,7 +14,7 @@ export function transformImagePath(
if (isRelative) { if (isRelative) {
return uriTransformer( return uriTransformer(
join('/', documentFilePath.split('/').splice(3).join('/'), '..', src) join('/', document.filePath.split('/').splice(3).join('/'), '..', src)
); );
} }

View File

@ -0,0 +1,43 @@
/**
* @param {string} uri
* @returns {string}
*/
export function uriTransformer(uri: string): string {
const protocols = ['http', 'https', 'mailto', 'tel'];
const url = (uri || '').trim();
const first = url.charAt(0);
if (first === '#' || first === '/') {
return url;
}
const colon = url.indexOf(':');
if (colon === -1) {
return url;
}
let index = -1;
while (++index < protocols.length) {
const protocol = protocols[index];
if (
colon === protocol.length &&
url.slice(0, protocol.length).toLowerCase() === protocol
) {
return url;
}
}
index = url.indexOf('?');
if (index !== -1 && colon > index) {
return url;
}
index = url.indexOf('#');
if (index !== -1 && colon > index) {
return url;
}
return 'javascript:void(0)';
}

View File

@ -0,0 +1,29 @@
import {
Config,
Node,
RenderableTreeNodes,
Schema,
Tag,
} from '@markdoc/markdoc';
import { DocumentData } from '@nrwl/nx-dev/models-document';
import { transformImagePath } from './helpers/transform-image-path';
export const getImageSchema = (document: DocumentData): Schema => ({
render: 'img',
attributes: {
src: { type: 'String', required: true },
alt: { type: 'String', required: true },
},
transform(node: Node, config: Config): RenderableTreeNodes {
const attributes = node.transformAttributes(config);
const children = node.transformChildren(config);
const src = transformImagePath(document)(attributes['src']);
return new Tag(
this.render,
// `h${node.attributes['level']}`,
{ ...attributes, src, loading: 'lazy' },
children
);
},
});

View File

@ -0,0 +1,18 @@
import Link from 'next/link';
export function CustomLink(props: any) {
const target =
props.target || (props.href.startsWith('http') ? '_blank' : undefined);
return (
<Link {...props} passHref>
<a
target={target}
rel={target === '_blank' ? 'noreferrer' : undefined}
className={props.className}
>
{props.children}
</a>
</Link>
);
}

View File

@ -0,0 +1,47 @@
export const link = {
render: 'CustomLink',
description: 'Displays a Next.js link',
attributes: {
href: {
description: 'The path or URL to navigate to.',
type: String,
errorLevel: 'critical',
required: true,
},
as: {
description:
'Optional decorator for the path that will be shown in the browser URL bar.',
type: String,
},
passHref: {
description: 'Forces Link to send the href property to its child.',
type: Boolean,
default: false,
},
prefetch: {
description: 'Prefetch the page in the background.',
type: Boolean,
},
replace: {
description:
'Replace the current history state instead of adding a new url into the stack.',
type: Boolean,
default: false,
},
scroll: {
description: 'Scroll to the top of the page after a navigation.',
type: Boolean,
default: true,
},
shallow: {
description:
'Update the path of the current page without rerunning getStaticProps, getServerSideProps or getInitialProps.',
type: Boolean,
default: true,
},
locale: {
description: 'The active locale is automatically prepended.',
type: Boolean,
},
},
};

View File

@ -0,0 +1,90 @@
import {
CheckCircleIcon,
ExclamationIcon,
InformationCircleIcon,
XCircleIcon,
} from '@heroicons/react/outline';
import cx from 'classnames';
import { ReactNode } from 'react';
type CalloutType = 'note' | 'warning' | 'check' | 'error';
const typeMap: Record<
CalloutType,
{
icon: JSX.Element;
backgroundColor: string;
borderColor: string;
titleColor: string;
textColor: string;
}
> = {
note: {
icon: (
<InformationCircleIcon
className="h-5 w-5 text-slate-500"
aria-hidden="true"
/>
),
backgroundColor: 'bg-slate-50',
borderColor: 'ring-slate-100',
titleColor: 'text-slate-600',
textColor: 'text-slate-700',
},
warning: {
icon: (
<ExclamationIcon className="h-5 w-5 text-yellow-500" aria-hidden="true" />
),
backgroundColor: 'bg-yellow-50',
borderColor: 'ring-yellow-100',
titleColor: 'text-yellow-600',
textColor: 'text-yellow-700',
},
check: {
icon: (
<CheckCircleIcon className="h-5 w-5 text-green-500" aria-hidden="true" />
),
backgroundColor: 'bg-green-50',
borderColor: 'ring-green-100',
titleColor: 'text-green-600',
textColor: 'text-green-700',
},
error: {
icon: <XCircleIcon className="h-5 w-5 text-red-500" aria-hidden="true" />,
backgroundColor: 'bg-red-50',
borderColor: 'ring-red-100',
titleColor: 'text-red-600',
textColor: 'text-red-700',
},
};
export function Callout({
title,
type,
children,
}: {
title: string;
type: CalloutType;
children: ReactNode;
}) {
const ui = typeMap[type] || typeMap.note;
return (
<aside
className={cx(
'not-prose mb-6 rounded-md p-4 ring-1',
ui.backgroundColor,
ui.borderColor
)}
>
<div className="flex">
<div className="flex-shrink-0">{ui.icon}</div>
<div className="ml-3">
<h5 className={cx('mt-0 text-sm font-medium', ui.titleColor)}>
{title}
</h5>
<div className={cx('mt-2 text-sm', ui.textColor)}>{children}</div>
</div>
</div>
</aside>
);
}

View File

@ -0,0 +1,21 @@
import { Schema } from '@markdoc/markdoc';
export const callout: Schema = {
render: 'Callout',
description: 'Display the enclosed content in a callout box',
children: ['paragraph', 'tag', 'list'],
attributes: {
type: {
type: 'String',
default: 'note',
matches: ['caution', 'check', 'note', 'warning'],
errorLevel: 'critical',
description:
'Controls the color and icon of the callout. Can be: "caution", "check", "note", "warning"',
},
title: {
type: 'String',
description: 'The title displayed at the top of the callout',
},
},
};

View File

@ -0,0 +1,4 @@
// TODO@ben: add tailwindcss classes
export function Iframe(props: any) {
return <iframe {...props} title={props.tile} frameBorder="0" />;
}

View File

@ -0,0 +1,19 @@
import { Schema } from '@markdoc/markdoc';
export const iframe: Schema = {
render: 'Iframe',
attributes: {
src: {
type: 'String',
required: true,
},
title: {
type: 'String',
required: true,
},
width: {
type: 'String',
default: '50%',
},
},
};

View File

@ -0,0 +1,44 @@
import { ReactNode } from 'react';
export function NxCloudSection({ children }: { children: ReactNode }) {
return (
<div className="border-green-nx-base mt-16 mb-4 border-l-4 pl-4">
<aside className="not-prose mb-4 flex flex-wrap items-center justify-between rounded-lg border border-gray-100 bg-gray-50 p-4">
<div className="flex flex w-0 flex-1 items-center">
<span className="flex">
<svg
className="h-10 w-10 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 128 128"
stroke="currentColor"
aria-hidden="true"
>
<path
d="M128 16V32C110.336 32 96 46.336 96 64C96 81.664 81.664 96 64 96C46.336 96 32 110.336 32 128H16C7.168 128 0 120.832 0 112V16C0 7.168 7.168 0 16 0H112C120.832 0 128 7.168 128 16Z"
fill="#0E2039"
/>
<path
d="M128 32V112C128 120.832 120.832 128 112 128H32C32 110.336 46.336 96 64 96C81.664 96 96 81.664 96 64C96 46.336 110.336 32 128 32Z"
fill="white"
/>
</svg>
</span>
<p className="ml-4 flex text-base">
This section is about Nx Cloud specifically.
</p>
</div>
<div className="order-3 mt-2 w-full flex-shrink-0 sm:order-2 sm:mt-0 sm:w-auto">
<a
href="https://nx.app/?utm_source=nxdev"
className="text-blue-nx-base flex items-center justify-center rounded-md border border-transparent bg-white px-4 py-2 text-sm font-medium shadow-sm hover:bg-slate-100"
>
Learn more
</a>
</div>
</aside>
{children}
</div>
);
}

View File

@ -0,0 +1,6 @@
import { Schema } from '@markdoc/markdoc';
export const nxCloudSection: Schema = {
render: 'NxCloudSection',
attributes: {},
};

View File

@ -0,0 +1,11 @@
import { Children, ReactNode } from 'react';
export function SideBySide({ children }: { children: ReactNode }) {
const [first, ...rest] = Children.toArray(children);
return (
<div className="not-prose grid items-center divide-x divide-solid divide-slate-50 md:grid-cols-2">
<div className="md:pr-6">{first}</div>
<div className="md:pl-6">{rest}</div>
</div>
);
}

View File

@ -0,0 +1,6 @@
import { Schema } from '@markdoc/markdoc';
export const sideBySide: Schema = {
render: 'SideBySide',
attributes: {},
};

View File

@ -0,0 +1,61 @@
// TODO@ben: refactor to use HeadlessUI tabs
import cx from 'classnames';
import { createContext, ReactNode, useContext, useState } from 'react';
export const TabContext = createContext('');
export function Tabs({
labels,
children,
}: {
labels: string[];
children: ReactNode;
}) {
const [currentTab, setCurrentTab] = useState(labels[0]);
return (
<TabContext.Provider value={currentTab}>
<section className="mb-8 py-4">
<div className="not-prose hidden sm:block">
<div className="border-b border-gray-100">
<nav className="-mb-px flex space-x-8" aria-label="Tabs">
{labels.map((label: string) => (
<button
key={label}
role="tab"
aria-selected={label === currentTab}
onClick={() => setCurrentTab(label)}
className={cx(
'whitespace-nowrap border-b-2 border-transparent py-4 px-2 text-sm font-medium',
label === currentTab
? 'border-slate-500 text-slate-800'
: 'text-slate-500 hover:border-slate-500 hover:text-slate-800'
)}
>
{label}
</button>
))}
</nav>
</div>
</div>
{children}
</section>
</TabContext.Provider>
);
}
export function Tab({
label,
children,
}: {
label: string;
children: ReactNode;
}) {
const currentTab = useContext(TabContext);
if (label !== currentTab) {
return null;
}
return <div className="prose">{children}</div>;
}

View File

@ -0,0 +1,23 @@
import { Schema, Tag } from '@markdoc/markdoc';
export const tabs: Schema = {
render: 'Tabs',
attributes: {},
transform(node, config) {
const labels = node
.transformChildren(config)
.filter((child) => child && child.name === 'Tab')
.map((tab) => (typeof tab === 'object' ? tab.attributes.label : null));
return new Tag(this.render, { labels }, node.transformChildren(config));
},
};
export const tab: Schema = {
render: 'Tab',
attributes: {
label: {
type: 'String',
},
},
};

View File

@ -0,0 +1,33 @@
export const VsCodeAd = () => (
<div className="not-prose bg-white">
<div className="mx-auto max-w-7xl py-16">
<div className="overflow-hidden rounded-lg bg-indigo-700 shadow-xl lg:grid lg:grid-cols-2 lg:gap-2">
<div className="px-6 py-8">
<div className="lg:self-center">
<h2 className="mt-0 text-3xl font-extrabold text-white">
<span className="block">NxConsole</span>
<span className="block">The best Nx companion for VsCode.</span>
</h2>
<p className="mt-4 text-lg leading-6 text-indigo-200">
Do you know Nx has an official VsCode Plugin available?! Pilote Nx
with the right UI directly from your editor.
</p>
<a
href="#"
className="mt-2 inline-flex items-center rounded-md border border-transparent bg-white px-2 py-3 text-indigo-600 shadow hover:bg-indigo-50"
>
Add it to VsCode now
</a>
</div>
</div>
<div className="aspect-w-5 aspect-h-3 md:aspect-w-2 md:aspect-h-1 -mt-6">
<img
className="translate-x-6 translate-y-6 transform rounded-md object-cover object-left-top sm:translate-x-16 lg:translate-y-20"
src="/images/nx-console.webp"
alt="App screenshot"
/>
</div>
</div>
</div>
</div>
);

View File

@ -0,0 +1,6 @@
import { Schema } from '@markdoc/markdoc';
export const vsCodeAd: Schema = {
render: 'VsCodeAd',
attributes: {},
};

View File

@ -0,0 +1,12 @@
// TODO@ben: add tailwindcss classes
export function YouTube(props: any) {
return (
<iframe
{...props}
title={props.title}
frameBorder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"
loading="lazy"
/>
);
}

View File

@ -0,0 +1,19 @@
import { Schema } from '@markdoc/markdoc';
export const youtube: Schema = {
render: 'YouTube',
attributes: {
src: {
type: 'String',
required: true,
},
title: {
type: 'String',
required: true,
},
width: {
type: 'String',
default: '50%',
},
},
};

View File

@ -0,0 +1,25 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@ -0,0 +1,23 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"jest.config.ts",
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

View File

@ -0,0 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"**/*.test.ts",
"**/*.spec.ts",
"**/*.test.tsx",
"**/*.spec.tsx",
"**/*.test.js",
"**/*.spec.js",
"**/*.test.jsx",
"**/*.spec.jsx",
"**/*.d.ts"
]
}

View File

@ -268,6 +268,7 @@
"@docsearch/react": "3.0.0", "@docsearch/react": "3.0.0",
"@headlessui/react": "^1.1.1", "@headlessui/react": "^1.1.1",
"@heroicons/react": "^1.0.1", "@heroicons/react": "^1.0.1",
"@markdoc/markdoc": "^0.1.3",
"@monaco-editor/react": "^4.3.1", "@monaco-editor/react": "^4.3.1",
"@napi-rs/canvas": "^0.1.19", "@napi-rs/canvas": "^0.1.19",
"@tailwindcss/aspect-ratio": "^0.4.0", "@tailwindcss/aspect-ratio": "^0.4.0",
@ -309,4 +310,3 @@
"minimist": "^1.2.6" "minimist": "^1.2.6"
} }
} }

View File

@ -76,6 +76,7 @@
"@nrwl/nx-dev/ui-community": ["nx-dev/ui-community/src/index.ts"], "@nrwl/nx-dev/ui-community": ["nx-dev/ui-community/src/index.ts"],
"@nrwl/nx-dev/ui-conference": ["nx-dev/ui-conference/src/index.ts"], "@nrwl/nx-dev/ui-conference": ["nx-dev/ui-conference/src/index.ts"],
"@nrwl/nx-dev/ui-home": ["nx-dev/ui-home/src/index.ts"], "@nrwl/nx-dev/ui-home": ["nx-dev/ui-home/src/index.ts"],
"@nrwl/nx-dev/ui-markdoc": ["nx-dev/ui-markdoc/src/index.ts"],
"@nrwl/nx-dev/ui-member-card": ["nx-dev/ui-member-card/src/index.ts"], "@nrwl/nx-dev/ui-member-card": ["nx-dev/ui-member-card/src/index.ts"],
"@nrwl/nx-dev/ui-sponsor-card": ["nx-dev/ui-sponsor-card/src/index.ts"], "@nrwl/nx-dev/ui-sponsor-card": ["nx-dev/ui-sponsor-card/src/index.ts"],
"@nrwl/nx-plugin": ["packages/nx-plugin"], "@nrwl/nx-plugin": ["packages/nx-plugin"],

View File

@ -62,6 +62,7 @@
"nx-dev-ui-community": "nx-dev/ui-community", "nx-dev-ui-community": "nx-dev/ui-community",
"nx-dev-ui-conference": "nx-dev/ui-conference", "nx-dev-ui-conference": "nx-dev/ui-conference",
"nx-dev-ui-home": "nx-dev/ui-home", "nx-dev-ui-home": "nx-dev/ui-home",
"nx-dev-ui-markdoc": "nx-dev/ui-markdoc",
"nx-dev-ui-member-card": "nx-dev/ui-member-card", "nx-dev-ui-member-card": "nx-dev/ui-member-card",
"nx-dev-ui-sponsor-card": "nx-dev/ui-sponsor-card", "nx-dev-ui-sponsor-card": "nx-dev/ui-sponsor-card",
"nx-plugin": "packages/nx-plugin", "nx-plugin": "packages/nx-plugin",

View File

@ -3663,6 +3663,11 @@
npmlog "^4.1.2" npmlog "^4.1.2"
write-file-atomic "^3.0.3" write-file-atomic "^3.0.3"
"@markdoc/markdoc@^0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@markdoc/markdoc/-/markdoc-0.1.3.tgz#6bcbfa536f7ad72534b7390365a5481d9f698076"
integrity sha512-4n2cobUSeOob+237wZhpNiOEHCSfOydlQnKHTFdRY6ug/DupXGaxkkkszJYQxRftiMs8jmb10XvVO1svEYD43Q==
"@mdx-js/mdx@^1.6.22": "@mdx-js/mdx@^1.6.22":
version "1.6.22" version "1.6.22"
resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba"