docs(nxdev): improve markdown capabilities (#10965)
This commit is contained in:
parent
aa2934674e
commit
778f13fdaf
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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%" /%}
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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!
|
||||||
|
|
||||||
|
|||||||
@ -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 isolation — they need APIs to talk to. Setup your app to talk to an API.
|
Real-world applications do not live in isolation — they need APIs to talk to. Setup your app to talk to an API.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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?
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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  icon next to a project reve
|
|||||||
|
|
||||||
Clicking the  icon next to an executor command executes that command without prompting for options.
|
Clicking the  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
|
||||||
|
|
||||||
|
|||||||
@ -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:
|
||||||
|
|
||||||
|
|||||||
@ -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:
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|||||||
@ -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:
|
||||||
|
|
||||||
|
|||||||
@ -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%" /%}
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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 %}
|
||||||
|
|||||||
@ -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 %}
|
||||||
|
|||||||
@ -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 %}
|
||||||
|
|||||||
@ -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 %}
|
||||||
|
|||||||
@ -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 %}
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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!
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
4
docs/shared/nx-cloud-logo.svg
Normal file
4
docs/shared/nx-cloud-logo.svg
Normal 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 |
@ -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%" /%}
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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!
|
||||||
|
|
||||||
|
|||||||
@ -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 isolation — they need APIs to talk to. Setup your app to talk to an API.
|
Real-world applications do not live in isolation — they need APIs to talk to. Setup your app to talk to an API.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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?
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
</div>
|
{% /nx-cloud-section %}
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -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));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -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`,
|
||||||
|
|||||||
@ -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>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -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}</>;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@ -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];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
|
|||||||
12
nx-dev/ui-markdoc/.babelrc
Normal file
12
nx-dev/ui-markdoc/.babelrc
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@nrwl/react/babel",
|
||||||
|
{
|
||||||
|
"runtime": "automatic",
|
||||||
|
"useBuiltIns": "usage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
18
nx-dev/ui-markdoc/.eslintrc.json
Normal file
18
nx-dev/ui-markdoc/.eslintrc.json
Normal 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": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
7
nx-dev/ui-markdoc/README.md
Normal file
7
nx-dev/ui-markdoc/README.md
Normal 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).
|
||||||
10
nx-dev/ui-markdoc/jest.config.ts
Normal file
10
nx-dev/ui-markdoc/jest.config.ts
Normal 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',
|
||||||
|
};
|
||||||
23
nx-dev/ui-markdoc/project.json
Normal file
23
nx-dev/ui-markdoc/project.json
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
nx-dev/ui-markdoc/src/index.ts
Normal file
70
nx-dev/ui-markdoc/src/index.ts
Normal 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,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -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>
|
||||||
);
|
);
|
||||||
9
nx-dev/ui-markdoc/src/lib/nodes/fence.schema.ts
Normal file
9
nx-dev/ui-markdoc/src/lib/nodes/fence.schema.ts
Normal 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' },
|
||||||
|
},
|
||||||
|
};
|
||||||
28
nx-dev/ui-markdoc/src/lib/nodes/heading.component.tsx
Normal file
28
nx-dev/ui-markdoc/src/lib/nodes/heading.component.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
38
nx-dev/ui-markdoc/src/lib/nodes/heading.schema.ts
Normal file
38
nx-dev/ui-markdoc/src/lib/nodes/heading.schema.ts
Normal 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
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -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);
|
||||||
|
|
||||||
@ -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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
43
nx-dev/ui-markdoc/src/lib/nodes/helpers/uri-transformer.ts
Normal file
43
nx-dev/ui-markdoc/src/lib/nodes/helpers/uri-transformer.ts
Normal 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)';
|
||||||
|
}
|
||||||
29
nx-dev/ui-markdoc/src/lib/nodes/image.schema.ts
Normal file
29
nx-dev/ui-markdoc/src/lib/nodes/image.schema.ts
Normal 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
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
18
nx-dev/ui-markdoc/src/lib/nodes/link.component.tsx
Normal file
18
nx-dev/ui-markdoc/src/lib/nodes/link.component.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
47
nx-dev/ui-markdoc/src/lib/nodes/link.schema.ts
Normal file
47
nx-dev/ui-markdoc/src/lib/nodes/link.schema.ts
Normal 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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
90
nx-dev/ui-markdoc/src/lib/tags/callout.component.tsx
Normal file
90
nx-dev/ui-markdoc/src/lib/tags/callout.component.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
21
nx-dev/ui-markdoc/src/lib/tags/callout.schema.ts
Normal file
21
nx-dev/ui-markdoc/src/lib/tags/callout.schema.ts
Normal 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',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
4
nx-dev/ui-markdoc/src/lib/tags/iframe.component.tsx
Normal file
4
nx-dev/ui-markdoc/src/lib/tags/iframe.component.tsx
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// TODO@ben: add tailwindcss classes
|
||||||
|
export function Iframe(props: any) {
|
||||||
|
return <iframe {...props} title={props.tile} frameBorder="0" />;
|
||||||
|
}
|
||||||
19
nx-dev/ui-markdoc/src/lib/tags/iframe.schema.ts
Normal file
19
nx-dev/ui-markdoc/src/lib/tags/iframe.schema.ts
Normal 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%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { Schema } from '@markdoc/markdoc';
|
||||||
|
|
||||||
|
export const nxCloudSection: Schema = {
|
||||||
|
render: 'NxCloudSection',
|
||||||
|
attributes: {},
|
||||||
|
};
|
||||||
11
nx-dev/ui-markdoc/src/lib/tags/side-by-side.component.tsx
Normal file
11
nx-dev/ui-markdoc/src/lib/tags/side-by-side.component.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
6
nx-dev/ui-markdoc/src/lib/tags/side-by-side.schema.ts
Normal file
6
nx-dev/ui-markdoc/src/lib/tags/side-by-side.schema.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Schema } from '@markdoc/markdoc';
|
||||||
|
|
||||||
|
export const sideBySide: Schema = {
|
||||||
|
render: 'SideBySide',
|
||||||
|
attributes: {},
|
||||||
|
};
|
||||||
61
nx-dev/ui-markdoc/src/lib/tags/tabs.component.tsx
Normal file
61
nx-dev/ui-markdoc/src/lib/tags/tabs.component.tsx
Normal 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>;
|
||||||
|
}
|
||||||
23
nx-dev/ui-markdoc/src/lib/tags/tabs.schema.ts
Normal file
23
nx-dev/ui-markdoc/src/lib/tags/tabs.schema.ts
Normal 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',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
33
nx-dev/ui-markdoc/src/lib/tags/vs-code-ad.component.tsx
Normal file
33
nx-dev/ui-markdoc/src/lib/tags/vs-code-ad.component.tsx
Normal 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>
|
||||||
|
);
|
||||||
6
nx-dev/ui-markdoc/src/lib/tags/vs-code-ad.schema.ts
Normal file
6
nx-dev/ui-markdoc/src/lib/tags/vs-code-ad.schema.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Schema } from '@markdoc/markdoc';
|
||||||
|
|
||||||
|
export const vsCodeAd: Schema = {
|
||||||
|
render: 'VsCodeAd',
|
||||||
|
attributes: {},
|
||||||
|
};
|
||||||
12
nx-dev/ui-markdoc/src/lib/tags/youtube.components.tsx
Normal file
12
nx-dev/ui-markdoc/src/lib/tags/youtube.components.tsx
Normal 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"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
19
nx-dev/ui-markdoc/src/lib/tags/youtube.schema.ts
Normal file
19
nx-dev/ui-markdoc/src/lib/tags/youtube.schema.ts
Normal 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%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
25
nx-dev/ui-markdoc/tsconfig.json
Normal file
25
nx-dev/ui-markdoc/tsconfig.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
23
nx-dev/ui-markdoc/tsconfig.lib.json
Normal file
23
nx-dev/ui-markdoc/tsconfig.lib.json
Normal 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"]
|
||||||
|
}
|
||||||
20
nx-dev/ui-markdoc/tsconfig.spec.json
Normal file
20
nx-dev/ui-markdoc/tsconfig.spec.json
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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"],
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user