Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d4988b8683 | |||
| 8ef7d0e312 | |||
| bcc850205a | |||
| d4b1ee8644 | |||
| c2d0ecfd82 | |||
| f04e36afec | |||
| 4cd1a4dfd7 | |||
| 38c7506d13 | |||
| 536279bdf8 | |||
| f03f68a536 | |||
| 5578728f01 | |||
| e5ef717a63 | |||
| 06ffc84682 | |||
| 64897b7e33 | |||
| cd5e38150f | |||
| 587d643008 | |||
| 1aa7212651 | |||
| 6488c37403 | |||
| a626ee17f7 | |||
| df2463ef99 | |||
| 8fe17d43a9 | |||
| 6a2e1804da | |||
| 1b1de90100 | |||
| b5b4cd569b | |||
| a8900e4425 | |||
| 95e9eb596c | |||
| 76ab229299 | |||
| 0359f0d39a | |||
| f420522477 | |||
| 1ddd4d40c5 | |||
| e25ec83d57 | |||
| 643b818309 | |||
| 9990b1e14c | |||
| adbfaf8b74 | |||
| 82690be4c1 | |||
| d0cbfec240 | |||
| 0e8d449971 | |||
| f24f1d62ad | |||
| b7c1c0ed27 | |||
| 121bbab070 | |||
| 400e3003d7 | |||
| f73a66984e | |||
| 713f86a64d | |||
| 3419b329a3 |
@@ -348,9 +348,8 @@ jobs:
|
||||
with:
|
||||
name: bindings-${{ matrix.settings.target }}
|
||||
path: |
|
||||
packages/**/*.node
|
||||
packages/**/*.wasm
|
||||
!packages/*/node_modules/**
|
||||
packages/nx/src/native/*.node
|
||||
packages/nx/src/native/*.wasm
|
||||
if-no-files-found: error
|
||||
|
||||
build-freebsd:
|
||||
@@ -416,8 +415,7 @@ jobs:
|
||||
with:
|
||||
name: bindings-freebsd
|
||||
path: |
|
||||
packages/**/*.node
|
||||
!packages/*/node_modules/**
|
||||
packages/nx/src/native/*.node
|
||||
if-no-files-found: error
|
||||
|
||||
publish:
|
||||
|
||||
@@ -108,3 +108,4 @@ tasks/
|
||||
# Upstream docs local configuration (machine-specific)
|
||||
.upstreamdocs.local.json
|
||||
|
||||
astro-docs/.netlify
|
||||
|
||||
@@ -9,6 +9,7 @@ assignment-rules:
|
||||
- agent: linux-extra-large
|
||||
parallelism: 1
|
||||
|
||||
# These projects should not need to be isolated.
|
||||
- projects:
|
||||
- nx-dev
|
||||
targets:
|
||||
@@ -16,6 +17,14 @@ assignment-rules:
|
||||
run-on:
|
||||
- agent: linux-extra-large
|
||||
parallelism: 1
|
||||
- projects:
|
||||
- angular
|
||||
- react
|
||||
targets:
|
||||
- test
|
||||
run-on:
|
||||
- agent: linux-extra-large
|
||||
parallelism: 1
|
||||
|
||||
- targets:
|
||||
- lint
|
||||
|
||||
@@ -137,6 +137,9 @@ rust-toolchain.toml @nrwl/nx-native-reviewers
|
||||
/packages/storybook/** @nrwl/nx-storybook-reviewers
|
||||
/e2e/storybook/** @nrwl/nx-storybook-reviewers
|
||||
|
||||
# Docker
|
||||
/packages/docker/** @nrwl/nx-core-reviewers @Coly010
|
||||
|
||||
## Devkit
|
||||
/docs/generated/devkit/** @nrwl/nx-devkit-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/devkit/** @nrwl/nx-devkit-reviewers @nrwl/nx-docs-reviewers
|
||||
@@ -174,7 +177,7 @@ rust-toolchain.toml @nrwl/nx-native-reviewers
|
||||
|
||||
# Misc
|
||||
/e2e/lerna-smoke-tests/** @vsavkin @JamesHenry
|
||||
/e2e/utils/** @meeroslav @nrwl/nx-testing-tools-reviewers @vsavkin @mandarini
|
||||
/e2e/utils/** @meeroslav @nrwl/nx-testing-tools-reviewers @vsavkin
|
||||
/community @nrwl/nx-docs-reviewers
|
||||
/CONTRIBUTING.md @FrozenPandaz @isaacplmann
|
||||
/CODE_OF_CONDUCT.md @FrozenPandaz @isaacplmann
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
@@ -0,0 +1,54 @@
|
||||
# Starlight Starter Kit: Basics
|
||||
|
||||
[](https://starlight.astro.build)
|
||||
|
||||
```
|
||||
pnpm create astro@latest -- --template starlight
|
||||
```
|
||||
|
||||
[](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics)
|
||||
[](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics)
|
||||
[](https://app.netlify.com/start/deploy?repository=https://github.com/withastro/starlight&create_from_path=examples/basics)
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs)
|
||||
|
||||
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||
|
||||
## 🚀 Project Structure
|
||||
|
||||
Inside of your Astro + Starlight project, you'll see the following folders and files:
|
||||
|
||||
```
|
||||
.
|
||||
├── public/
|
||||
├── src/
|
||||
│ ├── assets/
|
||||
│ ├── content/
|
||||
│ │ ├── docs/
|
||||
│ └── content.config.ts
|
||||
├── astro.config.mjs
|
||||
├── package.json
|
||||
└── tsconfig.json
|
||||
```
|
||||
|
||||
Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name.
|
||||
|
||||
Images can be added to `src/assets/` and embedded in Markdown with a relative link.
|
||||
|
||||
Static assets, like favicons, can be placed in the `public/` directory.
|
||||
|
||||
## 🧞 Commands
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :--------------------- | :----------------------------------------------- |
|
||||
| `pnpm install` | Installs dependencies |
|
||||
| `pnpm dev` | Starts local dev server at `localhost:4321` |
|
||||
| `pnpm build` | Build your production site to `./dist/` |
|
||||
| `pnpm preview` | Preview your build locally, before deploying |
|
||||
| `pnpm astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `pnpm astro -- --help` | Get help using the Astro CLI |
|
||||
|
||||
## 👀 Want to learn more?
|
||||
|
||||
Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).
|
||||
@@ -0,0 +1,99 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
import starlight from '@astrojs/starlight';
|
||||
import { sectionSidebarPlugin } from './src/plugins/section-sidebar.plugin';
|
||||
import netlify from '@astrojs/netlify';
|
||||
import linkValidator from 'starlight-links-validator';
|
||||
import react from '@astrojs/react';
|
||||
import markdoc from '@astrojs/markdoc';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
vite: { plugins: [tailwindcss()] },
|
||||
site: 'https://docs.nx.dev',
|
||||
adapter: netlify(),
|
||||
integrations: [
|
||||
markdoc(),
|
||||
starlight({
|
||||
title: 'Nx',
|
||||
tagline:
|
||||
'An AI-first build platform that connects everything from your editor to CI. Helping you deliver fast, without breaking things.',
|
||||
customCss: ['./src/styles/global.css'],
|
||||
favicon: '/favicon.svg',
|
||||
logo: {
|
||||
src: './src/assets/nx/Nx-light.png',
|
||||
replacesTitle: true,
|
||||
},
|
||||
plugins: [
|
||||
// linkValidator(),
|
||||
sectionSidebarPlugin(),
|
||||
],
|
||||
routeMiddleware: ['./src/plugins/banner.middleware.ts'],
|
||||
markdown: {
|
||||
// this breaks the renderMarkdown function in the plugin loader due to starlight path normalization
|
||||
// as to _why_ it has to normalize a path?
|
||||
// idk just working around the issue for now but we'll want to have linked headers so will need to fix
|
||||
headingLinks: false,
|
||||
},
|
||||
social: [
|
||||
{ icon: 'github', label: 'GitHub', href: 'https://github.com/nrwl/nx' },
|
||||
{
|
||||
icon: 'youtube',
|
||||
label: 'YouTube',
|
||||
href: 'https://www.youtube.com/@NxDevtools?utm_source=nx.dev',
|
||||
},
|
||||
{
|
||||
icon: 'x.com',
|
||||
label: 'X',
|
||||
href: 'https://x.com/NxDevTools?utm_source=nx.dev',
|
||||
},
|
||||
{
|
||||
icon: 'discord',
|
||||
label: 'Discord',
|
||||
href: 'https://go.nx.dev/community',
|
||||
},
|
||||
],
|
||||
editLink: {
|
||||
baseUrl: 'https://github.com/nrwl/nx/tree/main/',
|
||||
},
|
||||
sidebar: [
|
||||
{
|
||||
label: 'Getting Started',
|
||||
autogenerate: { directory: 'getting-started' },
|
||||
},
|
||||
{
|
||||
label: 'Guides',
|
||||
autogenerate: { directory: 'guides' },
|
||||
},
|
||||
{
|
||||
label: 'Concepts',
|
||||
autogenerate: { directory: 'concepts' },
|
||||
},
|
||||
{
|
||||
label: 'References',
|
||||
collapsed: true,
|
||||
items: [
|
||||
// have to use link bc slug is mapped to the /docs/ folder but we need the astro component in the pages/ dir
|
||||
{ label: 'Nx CLI', link: 'api/nx-cli' },
|
||||
{ label: 'Nx Cloud CLI', slug: 'api/nx-cloud-cli' },
|
||||
{ label: 'create-nx-workspace', link: 'api/create-nx-workspace' },
|
||||
{
|
||||
label: 'devkit',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ label: 'Overview', link: 'api/plugins/devkit/' },
|
||||
{
|
||||
label: 'Ng CLI Adapter',
|
||||
link: 'api/plugins/devkit/ngcli_adapter',
|
||||
},
|
||||
],
|
||||
},
|
||||
// plugins are autogenerated from the Nx repository via the sidebar plugin
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
react(),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { defineMarkdocConfig, component } from '@astrojs/markdoc/config';
|
||||
import starlightMarkdoc from '@astrojs/starlight-markdoc';
|
||||
|
||||
export default defineMarkdocConfig({
|
||||
extends: [starlightMarkdoc()],
|
||||
tags: {
|
||||
youtube: {
|
||||
render: component('./src/components/Youtube.astro'),
|
||||
attributes: {
|
||||
src: {
|
||||
type: 'String',
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: 'String',
|
||||
required: true,
|
||||
},
|
||||
width: {
|
||||
type: 'String',
|
||||
default: '100%',
|
||||
},
|
||||
caption: {
|
||||
type: 'String',
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "astro-docs",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.7.0",
|
||||
"@astrojs/markdoc": "^0.15.0",
|
||||
"@astrojs/netlify": "^6.4.0",
|
||||
"@astrojs/react": "^4.3.0",
|
||||
"@astrojs/starlight": "0.34.6",
|
||||
"@astrojs/starlight-markdoc": "^0.4.0",
|
||||
"@astrojs/starlight-tailwind": "^4.0.1",
|
||||
"@nx/nx-dev-ui-icons": "workspace:*",
|
||||
"@nx/nx-dev-ui-markdoc": "workspace:*",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"astro": "^5.10.1",
|
||||
"tailwindcss": "4.1.11"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "astro-docs",
|
||||
"targets": {
|
||||
"comment": "package.json#scripts runs in the project root directory with astro assumes is where the node_modules is. which fails. so run the scripts in project.json#targets with --root command instead",
|
||||
"serve": {
|
||||
"continuous": true,
|
||||
"dependsOn": [
|
||||
{
|
||||
"projects": ["devkit", "create-nx-workspace"],
|
||||
"target": "build"
|
||||
}
|
||||
],
|
||||
"command": "astro dev",
|
||||
"options": {
|
||||
"cwd": "astro-docs"
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
{
|
||||
"projects": ["devkit", "create-nx-workspace"],
|
||||
"target": "build"
|
||||
}
|
||||
],
|
||||
"command": "astro build",
|
||||
"options": {
|
||||
"cwd": "astro-docs"
|
||||
}
|
||||
},
|
||||
"astro": {
|
||||
"command": "astro",
|
||||
"options": {
|
||||
"cwd": "astro-docs"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Nx</title><path d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 334 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 408 KiB |
|
After Width: | Height: | Size: 149 KiB |
@@ -0,0 +1 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Nx</title><path d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 112 KiB |
@@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://<YOUR SITE>/sitemap-index.xml
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="508" height="508" viewBox="0 0 508 508" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M404 179V141.5C404 120.8 387.2 104 366.5 104H141.5C120.8 104 104 120.8 104 141.5V366.5C104 387.2 120.8 404 141.5 404H179M404 179C362.6 179 329 212.6 329 254C329 295.4 295.4 329 254 329C212.6 329 179 362.6 179 404M404 179V365.364C404 386.691 386.691 404 365.364 404L179 404" stroke="#0A1A2D" stroke-width="36"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 427 B |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="508" height="508" viewBox="0 0 508 508" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M404 179V141.5C404 120.8 387.2 104 366.5 104H141.5C120.8 104 104 120.8 104 141.5V366.5C104 387.2 120.8 404 141.5 404H179M404 179C362.6 179 329 212.6 329 254C329 295.4 295.4 329 254 329C212.6 329 179 362.6 179 404M404 179V365.364C404 386.691 386.691 404 365.364 404L179 404" stroke="white" stroke-width="36"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 425 B |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,17 @@
|
||||
<svg width="172" height="39" viewBox="0 0 172 39" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_301_3)">
|
||||
<path d="M69.6733 9.27841V29.642H66.8494L56.4985 14.7074H56.3096V29.642H53.2372V9.27841H56.0809L66.4417 24.233H66.6306V9.27841H69.6733Z" fill="#0F172A"/>
|
||||
<path d="M76.2631 14.3693L79.6338 20.3153L83.0344 14.3693H86.2858L81.523 22.0057L86.3256 29.642H83.0742L79.6338 23.9347L76.2034 29.642H72.9421L77.6949 22.0057L73.0017 14.3693H76.2631Z" fill="#0F172A"/>
|
||||
<path d="M113.901 15.9006H110.798C110.679 15.2377 110.457 14.6544 110.132 14.1506C109.807 13.6468 109.41 13.2192 108.939 12.8679C108.468 12.5166 107.941 12.2514 107.358 12.0724C106.781 11.8935 106.168 11.804 105.518 11.804C104.345 11.804 103.294 12.099 102.366 12.6889C101.445 13.2789 100.716 14.1439 100.179 15.2841C99.6486 16.4242 99.3835 17.8163 99.3835 19.4602C99.3835 21.1174 99.6486 22.5161 100.179 23.6562C100.716 24.7964 101.448 25.6581 102.376 26.2415C103.304 26.8248 104.348 27.1165 105.508 27.1165C106.151 27.1165 106.761 27.0303 107.338 26.858C107.921 26.679 108.448 26.4171 108.919 26.0724C109.39 25.7277 109.787 25.3068 110.112 24.8097C110.444 24.3059 110.672 23.7292 110.798 23.0795L113.901 23.0895C113.735 24.0904 113.413 25.0118 112.936 25.8537C112.465 26.6889 111.859 27.4115 111.116 28.0213C110.381 28.6245 109.539 29.0919 108.591 29.4233C107.643 29.7547 106.609 29.9205 105.489 29.9205C103.725 29.9205 102.154 29.5028 100.776 28.6676C99.3967 27.8258 98.3096 26.6226 97.5142 25.0582C96.7253 23.4938 96.3309 21.6278 96.3309 19.4602C96.3309 17.286 96.7287 15.42 97.5241 13.8622C98.3196 12.2978 99.4067 11.098 100.785 10.2628C102.164 9.42093 103.732 9 105.489 9C106.569 9 107.577 9.15578 108.511 9.46733C109.453 9.77225 110.298 10.223 111.047 10.8196C111.796 11.4096 112.416 12.1321 112.906 12.9872C113.397 13.8357 113.728 14.8068 113.901 15.9006Z" fill="#0F172A"/>
|
||||
<path d="M120.311 9.27841V29.642H117.338V9.27841H120.311Z" fill="#0F172A"/>
|
||||
<path d="M130.744 29.9503C129.312 29.9503 128.063 29.6222 126.996 28.9659C125.928 28.3097 125.1 27.3916 124.51 26.2116C123.92 25.0317 123.625 23.6529 123.625 22.0753C123.625 20.491 123.92 19.1056 124.51 17.919C125.1 16.7325 125.928 15.8111 126.996 15.1548C128.063 14.4986 129.312 14.1705 130.744 14.1705C132.176 14.1705 133.426 14.4986 134.493 15.1548C135.56 15.8111 136.389 16.7325 136.979 17.919C137.569 19.1056 137.864 20.491 137.864 22.0753C137.864 23.6529 137.569 25.0317 136.979 26.2116C136.389 27.3916 135.56 28.3097 134.493 28.9659C133.426 29.6222 132.176 29.9503 130.744 29.9503ZM130.754 27.4545C131.682 27.4545 132.451 27.2093 133.061 26.7188C133.671 26.2282 134.122 25.5753 134.413 24.7599C134.712 23.9446 134.861 23.0464 134.861 22.0653C134.861 21.0909 134.712 20.196 134.413 19.3807C134.122 18.5587 133.671 17.8991 133.061 17.402C132.451 16.9048 131.682 16.6562 130.754 16.6562C129.82 16.6562 129.044 16.9048 128.428 17.402C127.818 17.8991 127.364 18.5587 127.065 19.3807C126.774 20.196 126.628 21.0909 126.628 22.0653C126.628 23.0464 126.774 23.9446 127.065 24.7599C127.364 25.5753 127.818 26.2282 128.428 26.7188C129.044 27.2093 129.82 27.4545 130.754 27.4545Z" fill="#0F172A"/>
|
||||
<path d="M150.857 23.3082V14.3693H153.84V29.642H150.917V26.9972H150.757C150.406 27.8125 149.843 28.492 149.067 29.0355C148.298 29.5724 147.34 29.8409 146.194 29.8409C145.212 29.8409 144.344 29.6255 143.588 29.1946C142.839 28.7571 142.249 28.1108 141.819 27.2557C141.394 26.4006 141.182 25.3433 141.182 24.0838V14.3693H144.155V23.7259C144.155 24.7666 144.444 25.5952 145.02 26.2116C145.597 26.8281 146.346 27.1364 147.267 27.1364C147.824 27.1364 148.378 26.9972 148.928 26.7188C149.485 26.4403 149.945 26.0194 150.31 25.456C150.681 24.8925 150.863 24.1766 150.857 23.3082Z" fill="#0F172A"/>
|
||||
<path d="M163.552 29.9403C162.319 29.9403 161.218 29.6255 160.251 28.9957C159.29 28.3594 158.534 27.4545 157.984 26.2812C157.44 25.1013 157.168 23.6861 157.168 22.0355C157.168 20.3849 157.443 18.973 157.994 17.7997C158.55 16.6264 159.313 15.7282 160.281 15.1051C161.248 14.482 162.345 14.1705 163.572 14.1705C164.52 14.1705 165.282 14.3295 165.859 14.6477C166.442 14.9593 166.893 15.3239 167.211 15.7415C167.536 16.1591 167.788 16.527 167.967 16.8452H168.146V9.27841H171.119V29.642H168.215V27.2656H167.967C167.788 27.5904 167.529 27.9616 167.191 28.3793C166.86 28.7969 166.402 29.1615 165.819 29.473C165.236 29.7846 164.48 29.9403 163.552 29.9403ZM164.208 27.4048C165.063 27.4048 165.786 27.1795 166.376 26.7287C166.972 26.2713 167.423 25.6383 167.728 24.8295C168.04 24.0208 168.195 23.0795 168.195 22.0057C168.195 20.9451 168.043 20.017 167.738 19.2216C167.433 18.4261 166.986 17.8063 166.396 17.3622C165.806 16.9181 165.076 16.696 164.208 16.696C163.313 16.696 162.567 16.928 161.971 17.392C161.374 17.8561 160.924 18.4891 160.619 19.2912C160.32 20.0933 160.171 20.9981 160.171 22.0057C160.171 23.0265 160.324 23.9446 160.629 24.7599C160.933 25.5753 161.384 26.2216 161.981 26.6989C162.584 27.1695 163.326 27.4048 164.208 27.4048Z" fill="#0F172A"/>
|
||||
<path d="M36.7916 10.5417V6.16669C36.7916 3.75169 34.8316 1.79169 32.4166 1.79169H6.16663C3.75163 1.79169 1.79163 3.75169 1.79163 6.16669V32.4167C1.79163 34.8317 3.75163 36.7917 6.16663 36.7917H10.5416M36.7916 10.5417C31.9616 10.5417 28.0416 14.4617 28.0416 19.2917C28.0416 24.1217 24.1216 28.0417 19.2916 28.0417C14.4616 28.0417 10.5416 31.9617 10.5416 36.7917M36.7916 10.5417V32.2841C36.7916 34.7723 34.7722 36.7917 32.2841 36.7917L10.5416 36.7917" stroke="#0F172A" stroke-width="3"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_301_3">
|
||||
<rect width="172" height="39" fill="white" transform="matrix(-1 0 0 1 172 0)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
@@ -0,0 +1,17 @@
|
||||
<svg width="172" height="39" viewBox="0 0 172 39" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_403_2)">
|
||||
<path d="M69.6733 9.27841V29.642H66.8494L56.4985 14.7074H56.3096V29.642H53.2372V9.27841H56.0809L66.4417 24.233H66.6306V9.27841H69.6733Z" fill="white"/>
|
||||
<path d="M76.2631 14.3693L79.6338 20.3153L83.0344 14.3693H86.2858L81.523 22.0057L86.3256 29.642H83.0742L79.6338 23.9347L76.2034 29.642H72.9421L77.6949 22.0057L73.0017 14.3693H76.2631Z" fill="white"/>
|
||||
<path d="M113.901 15.9006H110.798C110.679 15.2377 110.457 14.6544 110.132 14.1506C109.807 13.6468 109.41 13.2192 108.939 12.8679C108.468 12.5166 107.941 12.2514 107.358 12.0724C106.781 11.8935 106.168 11.804 105.518 11.804C104.345 11.804 103.294 12.099 102.366 12.6889C101.445 13.2789 100.716 14.1439 100.179 15.2841C99.6486 16.4242 99.3835 17.8163 99.3835 19.4602C99.3835 21.1174 99.6486 22.5161 100.179 23.6562C100.716 24.7964 101.448 25.6581 102.376 26.2415C103.304 26.8248 104.348 27.1165 105.508 27.1165C106.151 27.1165 106.761 27.0303 107.338 26.858C107.921 26.679 108.448 26.4171 108.919 26.0724C109.39 25.7277 109.787 25.3068 110.112 24.8097C110.444 24.3059 110.672 23.7292 110.798 23.0795L113.901 23.0895C113.735 24.0904 113.413 25.0118 112.936 25.8537C112.465 26.6889 111.859 27.4115 111.116 28.0213C110.381 28.6245 109.539 29.0919 108.591 29.4233C107.643 29.7547 106.609 29.9205 105.489 29.9205C103.725 29.9205 102.154 29.5028 100.776 28.6676C99.3967 27.8258 98.3096 26.6226 97.5142 25.0582C96.7253 23.4938 96.3309 21.6278 96.3309 19.4602C96.3309 17.286 96.7287 15.42 97.5241 13.8622C98.3196 12.2978 99.4067 11.098 100.785 10.2628C102.164 9.42093 103.732 9 105.489 9C106.569 9 107.577 9.15578 108.511 9.46733C109.453 9.77225 110.298 10.223 111.047 10.8196C111.796 11.4096 112.416 12.1321 112.906 12.9872C113.397 13.8357 113.728 14.8068 113.901 15.9006Z" fill="white"/>
|
||||
<path d="M120.311 9.27841V29.642H117.338V9.27841H120.311Z" fill="white"/>
|
||||
<path d="M130.744 29.9503C129.312 29.9503 128.063 29.6222 126.996 28.9659C125.928 28.3097 125.1 27.3916 124.51 26.2116C123.92 25.0317 123.625 23.6529 123.625 22.0753C123.625 20.491 123.92 19.1056 124.51 17.919C125.1 16.7325 125.928 15.8111 126.996 15.1548C128.063 14.4986 129.312 14.1705 130.744 14.1705C132.176 14.1705 133.426 14.4986 134.493 15.1548C135.56 15.8111 136.389 16.7325 136.979 17.919C137.569 19.1056 137.864 20.491 137.864 22.0753C137.864 23.6529 137.569 25.0317 136.979 26.2116C136.389 27.3916 135.56 28.3097 134.493 28.9659C133.426 29.6222 132.176 29.9503 130.744 29.9503ZM130.754 27.4545C131.682 27.4545 132.451 27.2093 133.061 26.7188C133.671 26.2282 134.122 25.5753 134.413 24.7599C134.712 23.9446 134.861 23.0464 134.861 22.0653C134.861 21.0909 134.712 20.196 134.413 19.3807C134.122 18.5587 133.671 17.8991 133.061 17.402C132.451 16.9048 131.682 16.6562 130.754 16.6562C129.82 16.6562 129.044 16.9048 128.428 17.402C127.818 17.8991 127.364 18.5587 127.065 19.3807C126.774 20.196 126.628 21.0909 126.628 22.0653C126.628 23.0464 126.774 23.9446 127.065 24.7599C127.364 25.5753 127.818 26.2282 128.428 26.7188C129.044 27.2093 129.82 27.4545 130.754 27.4545Z" fill="white"/>
|
||||
<path d="M150.857 23.3082V14.3693H153.84V29.642H150.917V26.9972H150.757C150.406 27.8125 149.843 28.492 149.067 29.0355C148.298 29.5724 147.34 29.8409 146.194 29.8409C145.212 29.8409 144.344 29.6255 143.588 29.1946C142.839 28.7571 142.249 28.1108 141.819 27.2557C141.394 26.4006 141.182 25.3433 141.182 24.0838V14.3693H144.155V23.7259C144.155 24.7666 144.444 25.5952 145.02 26.2116C145.597 26.8281 146.346 27.1364 147.267 27.1364C147.824 27.1364 148.378 26.9972 148.928 26.7188C149.485 26.4403 149.945 26.0194 150.31 25.456C150.681 24.8925 150.863 24.1766 150.857 23.3082Z" fill="white"/>
|
||||
<path d="M163.552 29.9403C162.319 29.9403 161.218 29.6255 160.251 28.9957C159.29 28.3594 158.534 27.4545 157.984 26.2812C157.44 25.1013 157.168 23.6861 157.168 22.0355C157.168 20.3849 157.443 18.973 157.994 17.7997C158.55 16.6264 159.313 15.7282 160.281 15.1051C161.248 14.482 162.345 14.1705 163.572 14.1705C164.52 14.1705 165.282 14.3295 165.859 14.6477C166.442 14.9593 166.893 15.3239 167.211 15.7415C167.536 16.1591 167.788 16.527 167.967 16.8452H168.146V9.27841H171.119V29.642H168.215V27.2656H167.967C167.788 27.5904 167.529 27.9616 167.191 28.3793C166.86 28.7969 166.402 29.1615 165.819 29.473C165.236 29.7846 164.48 29.9403 163.552 29.9403ZM164.208 27.4048C165.063 27.4048 165.786 27.1795 166.376 26.7287C166.972 26.2713 167.423 25.6383 167.728 24.8295C168.04 24.0208 168.195 23.0795 168.195 22.0057C168.195 20.9451 168.043 20.017 167.738 19.2216C167.433 18.4261 166.986 17.8063 166.396 17.3622C165.806 16.9181 165.076 16.696 164.208 16.696C163.313 16.696 162.567 16.928 161.971 17.392C161.374 17.8561 160.924 18.4891 160.619 19.2912C160.32 20.0933 160.171 20.9981 160.171 22.0057C160.171 23.0265 160.324 23.9446 160.629 24.7599C160.933 25.5753 161.384 26.2216 161.981 26.6989C162.584 27.1695 163.326 27.4048 164.208 27.4048Z" fill="white"/>
|
||||
<path d="M36.7916 10.5417V6.16669C36.7916 3.75169 34.8316 1.79169 32.4166 1.79169H6.16663C3.75163 1.79169 1.79163 3.75169 1.79163 6.16669V32.4167C1.79163 34.8317 3.75163 36.7917 6.16663 36.7917H10.5416M36.7916 10.5417C31.9616 10.5417 28.0416 14.4617 28.0416 19.2917C28.0416 24.1217 24.1216 28.0417 19.2916 28.0417C14.4616 28.0417 10.5416 31.9617 10.5416 36.7917M36.7916 10.5417V32.2841C36.7916 34.7723 34.7722 36.7917 32.2841 36.7917L10.5416 36.7917" stroke="white" stroke-width="3"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_403_2">
|
||||
<rect width="172" height="39" fill="white" transform="matrix(-1 0 0 1 172 0)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="344" height="221" viewBox="0 0 344 221" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M43.3535 191.192C40.8271 191.192 38.5414 190.639 36.4963 189.532C34.4752 188.401 32.8872 186.861 31.7323 184.912C30.5774 182.939 30 180.726 30 178.272C30 175.818 30.5774 173.616 31.7323 171.667C32.8872 169.694 34.4872 168.154 36.5324 167.048C38.5775 165.917 40.8632 165.351 43.3896 165.351C45.3625 165.351 47.167 165.688 48.8031 166.362C50.4392 167.011 51.8347 167.986 52.9896 169.285L50.6437 171.559C48.743 169.562 46.373 168.563 43.5339 168.563C41.6572 168.563 39.961 168.984 38.4452 169.827C36.9294 170.669 35.7384 171.836 34.8722 173.327C34.0301 174.795 33.609 176.443 33.609 178.272C33.609 180.1 34.0301 181.76 34.8722 183.252C35.7384 184.72 36.9294 185.875 38.4452 186.717C39.961 187.559 41.6572 187.98 43.5339 187.98C46.349 187.98 48.7189 186.969 50.6437 184.948L52.9896 187.222C51.8347 188.521 50.4272 189.508 48.767 190.182C47.1309 190.855 45.3264 191.192 43.3535 191.192Z" fill="#143055"/>
|
||||
<path d="M85.9766 191.192C83.4262 191.192 81.1284 190.639 79.0833 189.532C77.0382 188.401 75.4261 186.849 74.2472 184.876C73.0923 182.903 72.5148 180.702 72.5148 178.272C72.5148 175.842 73.0923 173.64 74.2472 171.667C75.4261 169.694 77.0382 168.154 79.0833 167.048C81.1284 165.917 83.4262 165.351 85.9766 165.351C88.5029 165.351 90.7886 165.917 92.8338 167.048C94.8789 168.154 96.4789 169.694 97.6338 171.667C98.7887 173.616 99.3661 175.818 99.3661 178.272C99.3661 180.726 98.7887 182.939 97.6338 184.912C96.4789 186.861 94.8789 188.401 92.8338 189.532C90.7886 190.639 88.5029 191.192 85.9766 191.192ZM85.9766 187.98C87.8292 187.98 89.4894 187.559 90.957 186.717C92.4488 185.875 93.6157 184.72 94.4578 183.252C95.324 181.76 95.7571 180.1 95.7571 178.272C95.7571 176.443 95.324 174.795 94.4578 173.327C93.6157 171.836 92.4488 170.669 90.957 169.827C89.4894 168.984 87.8292 168.563 85.9766 168.563C84.1239 168.563 82.4397 168.984 80.9239 169.827C79.4322 170.669 78.2532 171.836 77.387 173.327C76.5449 174.795 76.1239 176.443 76.1239 178.272C76.1239 180.1 76.5449 181.76 77.387 183.252C78.2532 184.72 79.4322 185.875 80.9239 186.717C82.4397 187.559 84.1239 187.98 85.9766 187.98Z" fill="#143055"/>
|
||||
<path d="M143.571 165.64V190.903H140.612L125.454 172.064V190.903H121.845V165.64H124.804L139.962 184.479V165.64H143.571Z" fill="#143055"/>
|
||||
<path d="M175.458 191.192C173.557 191.192 171.717 190.903 169.936 190.326C168.18 189.724 166.796 188.954 165.786 188.016L167.121 185.201C168.084 186.067 169.311 186.777 170.802 187.33C172.318 187.86 173.87 188.124 175.458 188.124C177.551 188.124 179.115 187.776 180.15 187.078C181.184 186.356 181.702 185.406 181.702 184.227C181.702 183.36 181.413 182.663 180.836 182.133C180.282 181.58 179.584 181.159 178.742 180.87C177.924 180.581 176.757 180.257 175.241 179.896C173.341 179.439 171.801 178.981 170.622 178.524C169.467 178.067 168.469 177.369 167.626 176.431C166.808 175.469 166.399 174.181 166.399 172.569C166.399 171.222 166.748 170.007 167.446 168.924C168.168 167.842 169.25 166.975 170.694 166.326C172.138 165.676 173.93 165.351 176.072 165.351C177.563 165.351 179.031 165.544 180.475 165.929C181.918 166.314 183.157 166.867 184.192 167.589L183.001 170.476C181.942 169.802 180.811 169.297 179.608 168.96C178.405 168.599 177.226 168.419 176.072 168.419C174.026 168.419 172.487 168.792 171.452 169.538C170.441 170.284 169.936 171.246 169.936 172.425C169.936 173.291 170.225 174.001 170.802 174.554C171.38 175.084 172.09 175.505 172.932 175.818C173.798 176.106 174.965 176.419 176.432 176.756C178.333 177.213 179.861 177.67 181.016 178.127C182.171 178.584 183.157 179.282 183.975 180.221C184.817 181.159 185.239 182.422 185.239 184.01C185.239 185.333 184.878 186.548 184.156 187.655C183.434 188.738 182.339 189.604 180.872 190.254C179.404 190.879 177.599 191.192 175.458 191.192Z" fill="#143055"/>
|
||||
<path d="M218.878 191.192C216.328 191.192 214.03 190.639 211.985 189.532C209.94 188.401 208.328 186.849 207.149 184.876C205.994 182.903 205.416 180.702 205.416 178.272C205.416 175.842 205.994 173.64 207.149 171.667C208.328 169.694 209.94 168.154 211.985 167.048C214.03 165.917 216.328 165.351 218.878 165.351C221.405 165.351 223.69 165.917 225.735 167.048C227.781 168.154 229.381 169.694 230.535 171.667C231.69 173.616 232.268 175.818 232.268 178.272C232.268 180.726 231.69 182.939 230.535 184.912C229.381 186.861 227.781 188.401 225.735 189.532C223.69 190.639 221.405 191.192 218.878 191.192ZM218.878 187.98C220.731 187.98 222.391 187.559 223.859 186.717C225.35 185.875 226.517 184.72 227.359 183.252C228.226 181.76 228.659 180.1 228.659 178.272C228.659 176.443 228.226 174.795 227.359 173.327C226.517 171.836 225.35 170.669 223.859 169.827C222.391 168.984 220.731 168.563 218.878 168.563C217.026 168.563 215.341 168.984 213.826 169.827C212.334 170.669 211.155 171.836 210.289 173.327C209.447 174.795 209.026 176.443 209.026 178.272C209.026 180.1 209.447 181.76 210.289 183.252C211.155 184.72 212.334 185.875 213.826 186.717C215.341 187.559 217.026 187.98 218.878 187.98Z" fill="#143055"/>
|
||||
<path d="M254.747 165.64H258.356V187.763H272.034V190.903H254.747V165.64Z" fill="#143055"/>
|
||||
<path d="M311.472 187.763V190.903H293.138V165.64H310.966V168.78H296.747V176.539H309.414V179.607H296.747V187.763H311.472Z" fill="#143055"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M54.4936 30H30.2225V127.745H59.4859V79.9321L99.0949 127.745H123.366V30H94.1026V77.8129L54.4936 30ZM33.2301 124.737V33.0075H53.0796L97.1102 86.1577V33.0075H120.358V124.737H100.509L56.4783 71.5873V124.737H33.2301ZM153.172 30H186.673L220.749 78.3213L186.683 127.745H151.648L187.249 78.3219L153.172 30ZM158.973 33.0075L190.942 78.3404L157.521 124.737H185.103L217.083 78.341L185.113 33.0075H158.973ZM243.307 108.196H313.984V127.745H243.307V108.196ZM246.314 111.204V124.737H310.976V111.204H246.314Z" fill="#143055"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="344" height="221" viewBox="0 0 344 221" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M43.3535 191.192C40.8271 191.192 38.5414 190.639 36.4963 189.532C34.4752 188.401 32.8872 186.861 31.7323 184.912C30.5774 182.939 30 180.726 30 178.272C30 175.818 30.5774 173.616 31.7323 171.667C32.8872 169.694 34.4872 168.154 36.5324 167.048C38.5775 165.917 40.8632 165.351 43.3896 165.351C45.3625 165.351 47.167 165.688 48.8031 166.362C50.4392 167.011 51.8347 167.986 52.9896 169.285L50.6437 171.559C48.743 169.562 46.373 168.563 43.5339 168.563C41.6572 168.563 39.961 168.984 38.4452 169.827C36.9294 170.669 35.7384 171.836 34.8722 173.327C34.0301 174.795 33.609 176.443 33.609 178.272C33.609 180.1 34.0301 181.76 34.8722 183.252C35.7384 184.72 36.9294 185.875 38.4452 186.717C39.961 187.559 41.6572 187.98 43.5339 187.98C46.349 187.98 48.7189 186.969 50.6437 184.948L52.9896 187.222C51.8347 188.521 50.4272 189.508 48.767 190.182C47.1309 190.855 45.3264 191.192 43.3535 191.192Z" fill="white"/>
|
||||
<path d="M85.9766 191.192C83.4262 191.192 81.1284 190.639 79.0833 189.532C77.0382 188.401 75.4261 186.849 74.2472 184.876C73.0923 182.903 72.5148 180.702 72.5148 178.272C72.5148 175.842 73.0923 173.64 74.2472 171.667C75.4261 169.694 77.0382 168.154 79.0833 167.048C81.1284 165.917 83.4262 165.351 85.9766 165.351C88.5029 165.351 90.7886 165.917 92.8338 167.048C94.8789 168.154 96.4789 169.694 97.6338 171.667C98.7887 173.616 99.3661 175.818 99.3661 178.272C99.3661 180.726 98.7887 182.939 97.6338 184.912C96.4789 186.861 94.8789 188.401 92.8338 189.532C90.7886 190.639 88.5029 191.192 85.9766 191.192ZM85.9766 187.98C87.8292 187.98 89.4894 187.559 90.957 186.717C92.4488 185.875 93.6157 184.72 94.4578 183.252C95.324 181.76 95.7571 180.1 95.7571 178.272C95.7571 176.443 95.324 174.795 94.4578 173.327C93.6157 171.836 92.4488 170.669 90.957 169.827C89.4894 168.984 87.8292 168.563 85.9766 168.563C84.1239 168.563 82.4397 168.984 80.9239 169.827C79.4322 170.669 78.2532 171.836 77.387 173.327C76.5449 174.795 76.1239 176.443 76.1239 178.272C76.1239 180.1 76.5449 181.76 77.387 183.252C78.2532 184.72 79.4322 185.875 80.9239 186.717C82.4397 187.559 84.1239 187.98 85.9766 187.98Z" fill="white"/>
|
||||
<path d="M143.571 165.64V190.903H140.612L125.454 172.064V190.903H121.845V165.64H124.804L139.962 184.479V165.64H143.571Z" fill="white"/>
|
||||
<path d="M175.458 191.192C173.557 191.192 171.717 190.903 169.936 190.326C168.18 189.724 166.796 188.954 165.786 188.016L167.121 185.201C168.084 186.067 169.311 186.777 170.802 187.33C172.318 187.86 173.87 188.124 175.458 188.124C177.551 188.124 179.115 187.776 180.15 187.078C181.184 186.356 181.702 185.406 181.702 184.227C181.702 183.36 181.413 182.663 180.836 182.133C180.282 181.58 179.584 181.159 178.742 180.87C177.924 180.581 176.757 180.257 175.241 179.896C173.341 179.439 171.801 178.981 170.622 178.524C169.467 178.067 168.469 177.369 167.626 176.431C166.808 175.469 166.399 174.181 166.399 172.569C166.399 171.222 166.748 170.007 167.446 168.924C168.168 167.842 169.25 166.975 170.694 166.326C172.138 165.676 173.93 165.351 176.072 165.351C177.563 165.351 179.031 165.544 180.475 165.929C181.918 166.314 183.157 166.867 184.192 167.589L183.001 170.476C181.942 169.802 180.811 169.297 179.608 168.96C178.405 168.599 177.226 168.419 176.072 168.419C174.026 168.419 172.487 168.792 171.452 169.538C170.441 170.284 169.936 171.246 169.936 172.425C169.936 173.291 170.225 174.001 170.802 174.554C171.38 175.084 172.09 175.505 172.932 175.818C173.798 176.106 174.965 176.419 176.432 176.756C178.333 177.213 179.861 177.67 181.016 178.127C182.171 178.584 183.157 179.282 183.975 180.221C184.817 181.159 185.239 182.422 185.239 184.01C185.239 185.333 184.878 186.548 184.156 187.655C183.434 188.738 182.339 189.604 180.872 190.254C179.404 190.879 177.599 191.192 175.458 191.192Z" fill="white"/>
|
||||
<path d="M218.878 191.192C216.328 191.192 214.03 190.639 211.985 189.532C209.94 188.401 208.328 186.849 207.149 184.876C205.994 182.903 205.416 180.702 205.416 178.272C205.416 175.842 205.994 173.64 207.149 171.667C208.328 169.694 209.94 168.154 211.985 167.048C214.03 165.917 216.328 165.351 218.878 165.351C221.405 165.351 223.69 165.917 225.735 167.048C227.781 168.154 229.381 169.694 230.535 171.667C231.69 173.616 232.268 175.818 232.268 178.272C232.268 180.726 231.69 182.939 230.535 184.912C229.381 186.861 227.781 188.401 225.735 189.532C223.69 190.639 221.405 191.192 218.878 191.192ZM218.878 187.98C220.731 187.98 222.391 187.559 223.859 186.717C225.35 185.875 226.517 184.72 227.359 183.252C228.226 181.76 228.659 180.1 228.659 178.272C228.659 176.443 228.226 174.795 227.359 173.327C226.517 171.836 225.35 170.669 223.859 169.827C222.391 168.984 220.731 168.563 218.878 168.563C217.026 168.563 215.341 168.984 213.826 169.827C212.334 170.669 211.155 171.836 210.289 173.327C209.447 174.795 209.026 176.443 209.026 178.272C209.026 180.1 209.447 181.76 210.289 183.252C211.155 184.72 212.334 185.875 213.826 186.717C215.341 187.559 217.026 187.98 218.878 187.98Z" fill="white"/>
|
||||
<path d="M254.747 165.64H258.356V187.763H272.034V190.903H254.747V165.64Z" fill="white"/>
|
||||
<path d="M311.472 187.763V190.903H293.138V165.64H310.966V168.78H296.747V176.539H309.414V179.607H296.747V187.763H311.472Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M54.4937 30H30.2227V127.745H59.486V79.9321L99.095 127.745H123.366V30H94.1028V77.8129L54.4937 30ZM33.2302 124.737V33.0075H53.0797L97.1103 86.1577V33.0075H120.359V124.737H100.509L56.4785 71.5873V124.737H33.2302ZM153.173 30H186.673L220.749 78.3213L186.683 127.745H151.648L187.249 78.3219L153.173 30ZM158.974 33.0075L190.942 78.3404L157.521 124.737H185.103L217.083 78.341L185.114 33.0075H158.974ZM243.307 108.196H313.984V127.745H243.307V108.196ZM246.314 111.204V124.737H310.976V111.204H246.314Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="536" height="97" viewBox="0 0 536 97" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.9208 30H30V66.6005H43.5953V53.8792L54.1338 66.6005H65.0547V30H51.4593V42.7212L40.9208 30ZM93.7248 30H77.8642L90.6309 48.1037L77.307 66.6005H93.7411L106.491 48.1028L93.7248 30ZM32.8504 63.7501V32.8504H39.5807L54.3097 50.6301V32.8504H62.2043V63.7501H55.4739L40.7449 45.9704V63.7501H32.8504ZM94.1312 48.1212L83.3622 32.8504H92.2471L103.016 48.1214L92.2439 63.7501H82.8731L94.1312 48.1212ZM142 56.2685H114.492V66.6005H142V56.2685ZM117.343 63.7501V59.1189H139.15V63.7501H117.343Z" fill="#143055"/>
|
||||
<path d="M186.568 67.2486C182.943 67.2486 179.664 66.4546 176.729 64.8665C173.829 63.2439 171.551 61.0345 169.894 58.2381C168.237 55.4072 167.408 52.2311 167.408 48.7097C167.408 45.1883 168.237 42.0295 169.894 39.2331C171.551 36.4022 173.847 34.1927 176.781 32.6047C179.715 30.9821 182.995 30.1708 186.62 30.1708C189.451 30.1708 192.04 30.6541 194.388 31.6207C196.735 32.5529 198.738 33.9511 200.395 35.8153L197.029 39.0777C194.301 36.2123 190.901 34.7796 186.827 34.7796C184.134 34.7796 181.701 35.3838 179.526 36.5921C177.351 37.8004 175.642 39.4748 174.399 41.6152C173.191 43.7211 172.586 46.0859 172.586 48.7097C172.586 51.3335 173.191 53.7156 174.399 55.856C175.642 57.9619 177.351 59.619 179.526 60.8273C181.701 62.0356 184.134 62.6398 186.827 62.6398C190.866 62.6398 194.267 61.1898 197.029 58.2899L200.395 61.5523C198.738 63.4166 196.718 64.832 194.336 65.7987C191.988 66.7653 189.399 67.2486 186.568 67.2486Z" fill="#143055"/>
|
||||
<path d="M236.852 67.2486C233.192 67.2486 229.895 66.4546 226.961 64.8665C224.026 63.2439 221.713 61.0172 220.022 58.1863C218.365 55.3554 217.536 52.1965 217.536 48.7097C217.536 45.2229 218.365 42.064 220.022 39.2331C221.713 36.4022 224.026 34.1927 226.961 32.6047C229.895 30.9821 233.192 30.1708 236.852 30.1708C240.477 30.1708 243.756 30.9821 246.691 32.6047C249.625 34.1927 251.921 36.4022 253.578 39.2331C255.235 42.0295 256.064 45.1883 256.064 48.7097C256.064 52.2311 255.235 55.4072 253.578 58.2381C251.921 61.0345 249.625 63.2439 246.691 64.8665C243.756 66.4546 240.477 67.2486 236.852 67.2486ZM236.852 62.6398C239.51 62.6398 241.892 62.0356 243.998 60.8273C246.138 59.619 247.813 57.9619 249.021 55.856C250.264 53.7156 250.885 51.3335 250.885 48.7097C250.885 46.0859 250.264 43.7211 249.021 41.6152C247.813 39.4748 246.138 37.8004 243.998 36.5921C241.892 35.3838 239.51 34.7796 236.852 34.7796C234.193 34.7796 231.777 35.3838 229.602 36.5921C227.461 37.8004 225.77 39.4748 224.527 41.6152C223.319 43.7211 222.714 46.0859 222.714 48.7097C222.714 51.3335 223.319 53.7156 224.527 55.856C225.77 57.9619 227.461 59.619 229.602 60.8273C231.777 62.0356 234.193 62.6398 236.852 62.6398Z" fill="#143055"/>
|
||||
<path d="M308.617 30.5851V66.8344H304.371L282.621 39.8027V66.8344H277.443V30.5851H281.689L303.439 57.6167V30.5851H308.617Z" fill="#143055"/>
|
||||
<path d="M343.495 67.2486C340.768 67.2486 338.127 66.8344 335.572 66.0058C333.052 65.1427 331.067 64.038 329.617 62.6916L331.533 58.6524C332.914 59.8952 334.675 60.9136 336.815 61.7077C338.99 62.4672 341.217 62.8469 343.495 62.8469C346.499 62.8469 348.743 62.3463 350.227 61.3452C351.712 60.3095 352.454 58.9458 352.454 57.2542C352.454 56.0113 352.04 55.0102 351.211 54.2507C350.417 53.4566 349.416 52.8525 348.208 52.4382C347.034 52.0239 345.36 51.5579 343.185 51.04C340.457 50.3841 338.248 49.7281 336.556 49.0722C334.899 48.4163 333.466 47.4151 332.258 46.0687C331.084 44.6878 330.497 42.8408 330.497 40.5277C330.497 38.5944 330.998 36.851 331.999 35.2975C333.035 33.7439 334.588 32.5011 336.66 31.569C338.731 30.6368 341.303 30.1708 344.376 30.1708C346.516 30.1708 348.622 30.447 350.693 30.9993C352.765 31.5517 354.543 32.3457 356.027 33.3814L354.318 37.5242C352.799 36.5576 351.177 35.8326 349.451 35.3492C347.724 34.8314 346.033 34.5725 344.376 34.5725C341.441 34.5725 339.232 35.1076 337.747 36.1778C336.297 37.248 335.572 38.6289 335.572 40.3206C335.572 41.5634 335.987 42.5818 336.815 43.3759C337.644 44.1354 338.662 44.7395 339.87 45.1883C341.113 45.6026 342.788 46.0514 344.894 46.5347C347.621 47.1907 349.813 47.8466 351.47 48.5026C353.127 49.1585 354.543 50.1597 355.717 51.5061C356.925 52.8525 357.529 54.6649 357.529 56.9435C357.529 58.8422 357.011 60.5857 355.976 62.1737C354.94 63.7273 353.369 64.9701 351.263 65.9022C349.157 66.7998 346.568 67.2486 343.495 67.2486Z" fill="#143055"/>
|
||||
<path d="M394.922 67.2486C391.263 67.2486 387.966 66.4546 385.032 64.8665C382.097 63.2439 379.784 61.0172 378.092 58.1863C376.435 55.3554 375.607 52.1965 375.607 48.7097C375.607 45.2229 376.435 42.064 378.092 39.2331C379.784 36.4022 382.097 34.1927 385.032 32.6047C387.966 30.9821 391.263 30.1708 394.922 30.1708C398.547 30.1708 401.827 30.9821 404.762 32.6047C407.696 34.1927 409.992 36.4022 411.649 39.2331C413.306 42.0295 414.135 45.1883 414.135 48.7097C414.135 52.2311 413.306 55.4072 411.649 58.2381C409.992 61.0345 407.696 63.2439 404.762 64.8665C401.827 66.4546 398.547 67.2486 394.922 67.2486ZM394.922 62.6398C397.581 62.6398 399.963 62.0356 402.069 60.8273C404.209 59.619 405.884 57.9619 407.092 55.856C408.335 53.7156 408.956 51.3335 408.956 48.7097C408.956 46.0859 408.335 43.7211 407.092 41.6152C405.884 39.4748 404.209 37.8004 402.069 36.5921C399.963 35.3838 397.581 34.7796 394.922 34.7796C392.264 34.7796 389.848 35.3838 387.673 36.5921C385.532 37.8004 383.841 39.4748 382.598 41.6152C381.389 43.7211 380.785 46.0859 380.785 48.7097C380.785 51.3335 381.389 53.7156 382.598 55.856C383.841 57.9619 385.532 59.619 387.673 60.8273C389.848 62.0356 392.264 62.6398 394.922 62.6398Z" fill="#143055"/>
|
||||
<path d="M435.514 30.5851H440.692V62.3291H460.318V66.8344H435.514V30.5851Z" fill="#143055"/>
|
||||
<path d="M506.031 62.3291V66.8344H479.725V30.5851H505.306V35.0903H484.903V46.224H503.08V50.6257H484.903V62.3291H506.031Z" fill="#143055"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="536" height="97" viewBox="0 0 536 97" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.9208 30H30V66.6005H43.5953V53.8792L54.1338 66.6005H65.0547V30H51.4593V42.7212L40.9208 30ZM93.7248 30H77.8642L90.6309 48.1037L77.307 66.6005H93.7411L106.491 48.1028L93.7248 30ZM32.8504 63.7501V32.8504H39.5807L54.3097 50.6301V32.8504H62.2043V63.7501H55.4739L40.7449 45.9704V63.7501H32.8504ZM94.1312 48.1212L83.3622 32.8504H92.2471L103.016 48.1214L92.2439 63.7501H82.8731L94.1312 48.1212ZM142 56.2685H114.492V66.6005H142V56.2685ZM117.343 63.7501V59.1189H139.15V63.7501H117.343Z" fill="white"/>
|
||||
<path d="M186.568 67.2486C182.943 67.2486 179.664 66.4546 176.729 64.8665C173.829 63.2439 171.551 61.0345 169.894 58.2381C168.237 55.4072 167.408 52.2311 167.408 48.7097C167.408 45.1883 168.237 42.0295 169.894 39.2331C171.551 36.4022 173.847 34.1927 176.781 32.6047C179.715 30.9821 182.995 30.1708 186.62 30.1708C189.451 30.1708 192.04 30.6541 194.388 31.6207C196.735 32.5529 198.738 33.9511 200.395 35.8153L197.029 39.0777C194.301 36.2123 190.901 34.7796 186.827 34.7796C184.134 34.7796 181.701 35.3838 179.526 36.5921C177.351 37.8004 175.642 39.4748 174.399 41.6152C173.191 43.7211 172.586 46.0859 172.586 48.7097C172.586 51.3335 173.191 53.7156 174.399 55.856C175.642 57.9619 177.351 59.619 179.526 60.8273C181.701 62.0356 184.134 62.6398 186.827 62.6398C190.866 62.6398 194.267 61.1898 197.029 58.2899L200.395 61.5523C198.738 63.4166 196.718 64.832 194.336 65.7987C191.988 66.7653 189.399 67.2486 186.568 67.2486Z" fill="white"/>
|
||||
<path d="M236.852 67.2486C233.192 67.2486 229.895 66.4546 226.961 64.8665C224.026 63.2439 221.713 61.0172 220.022 58.1863C218.365 55.3554 217.536 52.1965 217.536 48.7097C217.536 45.2229 218.365 42.064 220.022 39.2331C221.713 36.4022 224.026 34.1927 226.961 32.6047C229.895 30.9821 233.192 30.1708 236.852 30.1708C240.477 30.1708 243.756 30.9821 246.691 32.6047C249.625 34.1927 251.921 36.4022 253.578 39.2331C255.235 42.0295 256.064 45.1883 256.064 48.7097C256.064 52.2311 255.235 55.4072 253.578 58.2381C251.921 61.0345 249.625 63.2439 246.691 64.8665C243.756 66.4546 240.477 67.2486 236.852 67.2486ZM236.852 62.6398C239.51 62.6398 241.892 62.0356 243.998 60.8273C246.138 59.619 247.813 57.9619 249.021 55.856C250.264 53.7156 250.885 51.3335 250.885 48.7097C250.885 46.0859 250.264 43.7211 249.021 41.6152C247.813 39.4748 246.138 37.8004 243.998 36.5921C241.892 35.3838 239.51 34.7796 236.852 34.7796C234.193 34.7796 231.777 35.3838 229.602 36.5921C227.461 37.8004 225.77 39.4748 224.527 41.6152C223.319 43.7211 222.714 46.0859 222.714 48.7097C222.714 51.3335 223.319 53.7156 224.527 55.856C225.77 57.9619 227.461 59.619 229.602 60.8273C231.777 62.0356 234.193 62.6398 236.852 62.6398Z" fill="white"/>
|
||||
<path d="M308.617 30.5851V66.8344H304.371L282.621 39.8027V66.8344H277.443V30.5851H281.689L303.439 57.6167V30.5851H308.617Z" fill="white"/>
|
||||
<path d="M343.495 67.2486C340.768 67.2486 338.127 66.8344 335.572 66.0058C333.052 65.1427 331.067 64.038 329.617 62.6916L331.533 58.6524C332.914 59.8952 334.675 60.9136 336.815 61.7077C338.99 62.4672 341.217 62.8469 343.495 62.8469C346.499 62.8469 348.743 62.3463 350.227 61.3452C351.712 60.3095 352.454 58.9458 352.454 57.2542C352.454 56.0113 352.04 55.0102 351.211 54.2507C350.417 53.4566 349.416 52.8525 348.208 52.4382C347.034 52.0239 345.36 51.5579 343.185 51.04C340.457 50.3841 338.248 49.7281 336.556 49.0722C334.899 48.4163 333.466 47.4151 332.258 46.0687C331.084 44.6878 330.497 42.8408 330.497 40.5277C330.497 38.5944 330.998 36.851 331.999 35.2975C333.035 33.7439 334.588 32.5011 336.66 31.569C338.731 30.6368 341.303 30.1708 344.376 30.1708C346.516 30.1708 348.622 30.447 350.693 30.9993C352.765 31.5517 354.543 32.3457 356.027 33.3814L354.318 37.5242C352.799 36.5576 351.177 35.8326 349.451 35.3492C347.724 34.8314 346.033 34.5725 344.376 34.5725C341.441 34.5725 339.232 35.1076 337.747 36.1778C336.297 37.248 335.572 38.6289 335.572 40.3206C335.572 41.5634 335.987 42.5818 336.815 43.3759C337.644 44.1354 338.662 44.7395 339.87 45.1883C341.113 45.6026 342.788 46.0514 344.894 46.5347C347.621 47.1907 349.813 47.8466 351.47 48.5026C353.127 49.1585 354.543 50.1597 355.717 51.5061C356.925 52.8525 357.529 54.6649 357.529 56.9435C357.529 58.8422 357.011 60.5857 355.976 62.1737C354.94 63.7273 353.369 64.9701 351.263 65.9022C349.157 66.7998 346.568 67.2486 343.495 67.2486Z" fill="white"/>
|
||||
<path d="M394.922 67.2486C391.263 67.2486 387.966 66.4546 385.032 64.8665C382.097 63.2439 379.784 61.0172 378.092 58.1863C376.435 55.3554 375.607 52.1965 375.607 48.7097C375.607 45.2229 376.435 42.064 378.092 39.2331C379.784 36.4022 382.097 34.1927 385.032 32.6047C387.966 30.9821 391.263 30.1708 394.922 30.1708C398.547 30.1708 401.827 30.9821 404.762 32.6047C407.696 34.1927 409.992 36.4022 411.649 39.2331C413.306 42.0295 414.135 45.1883 414.135 48.7097C414.135 52.2311 413.306 55.4072 411.649 58.2381C409.992 61.0345 407.696 63.2439 404.762 64.8665C401.827 66.4546 398.547 67.2486 394.922 67.2486ZM394.922 62.6398C397.581 62.6398 399.963 62.0356 402.069 60.8273C404.209 59.619 405.884 57.9619 407.092 55.856C408.335 53.7156 408.956 51.3335 408.956 48.7097C408.956 46.0859 408.335 43.7211 407.092 41.6152C405.884 39.4748 404.209 37.8004 402.069 36.5921C399.963 35.3838 397.581 34.7796 394.922 34.7796C392.264 34.7796 389.848 35.3838 387.673 36.5921C385.532 37.8004 383.841 39.4748 382.598 41.6152C381.389 43.7211 380.785 46.0859 380.785 48.7097C380.785 51.3335 381.389 53.7156 382.598 55.856C383.841 57.9619 385.532 59.619 387.673 60.8273C389.848 62.0356 392.264 62.6398 394.922 62.6398Z" fill="white"/>
|
||||
<path d="M435.514 30.5851H440.692V62.3291H460.318V66.8344H435.514V30.5851Z" fill="white"/>
|
||||
<path d="M506.031 62.3291V66.8344H479.725V30.5851H505.306V35.0903H484.903V46.224H503.08V50.6257H484.903V62.3291H506.031Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="1000" height="1000" viewBox="0 0 1000 1000" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M499.651 555.512L416.132 686.623L277.653 462.192L277.333 697.143H180V300H278.426L418.345 535.244L418.479 428.768L499.651 555.512V555.512ZM515.678 402.082C516.318 400.804 515.678 301.305 515.891 300H418.559C418.612 301.571 418.425 400.564 418.479 402.082H515.678V402.082ZM665.917 512.181C654.556 511.963 643.406 515.267 634.003 521.639C624.6 528.011 617.406 537.137 613.411 547.762C620.029 537.871 629.827 530.531 641.187 526.953C652.546 523.374 664.788 523.772 675.89 528.08C686.45 532.368 700.344 539.319 711.357 535.617C706.19 528.388 699.374 522.491 691.472 518.416C683.57 514.34 674.81 512.203 665.917 512.181V512.181ZM756.316 540.438C746.317 536.976 734.983 533.061 726.743 522.301C725.143 520.17 723.65 517.774 722.05 515.244C718.48 508.669 713.651 502.86 707.837 498.145C699.997 492.393 689.704 489.57 676.37 489.57C663.264 489.573 650.454 493.463 639.563 500.745C628.673 508.028 620.193 518.376 615.197 530.477C621.53 521.672 630.067 514.682 639.954 510.206C649.84 505.73 660.73 503.925 671.535 504.971C682.339 506.017 692.679 509.878 701.52 516.168C710.362 522.457 717.395 530.955 721.917 540.811C724.168 544.503 727.558 547.366 731.578 548.97C735.598 550.574 740.031 550.832 744.21 549.706C757.357 546.909 756.476 559.294 776.29 563.501V559.959C776.21 548.081 769.73 545.312 756.29 540.438H756.316ZM810.289 574.154C813.196 572.389 815.582 569.887 817.207 566.903C818.833 563.919 819.638 560.559 819.543 557.163C819.276 478.411 755.277 411.031 676.344 411.031C652.616 411.011 629.257 416.887 608.369 428.129C587.482 439.37 569.722 455.625 556.691 475.428C556.424 474.736 516.105 411.776 515.678 411.031H418.479L510.825 555.458L420.719 697.143H515.118L556.184 634.131L597.997 697.143H692.424L606.824 562.596C605.406 559.975 604.638 557.052 604.584 554.074C604.581 544.663 606.433 535.344 610.037 526.649C613.64 517.955 618.923 510.054 625.585 503.398C632.246 496.743 640.155 491.464 648.86 487.862C657.565 484.26 666.895 482.406 676.317 482.406H676.344C716.023 482.406 722.637 506.109 731.197 517.241C748.103 539.239 781.863 529.598 781.863 558.282C781.924 561.571 782.846 564.788 784.539 567.611C786.233 570.434 788.637 572.764 791.513 574.37C794.388 575.976 797.635 576.801 800.93 576.763C804.224 576.725 807.451 575.826 810.289 574.154V574.154ZM819.623 577.697C815.009 587.604 804.69 586.698 799.49 594.715C794.796 601.932 802.53 605.687 802.53 605.687C802.53 605.687 811.516 609.842 816.876 597.405C819.649 591.252 820.609 584.408 819.623 577.697V577.697Z" fill="#020617"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="1000" height="1000" viewBox="0 0 1000 1000" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M499.651 555.512L416.132 686.623L277.653 462.192L277.333 697.143H180V300H278.426L418.345 535.244L418.479 428.768L499.651 555.512V555.512ZM515.678 402.082C516.318 400.804 515.678 301.305 515.891 300H418.559C418.612 301.571 418.425 400.564 418.479 402.082H515.678V402.082ZM665.917 512.181C654.556 511.963 643.406 515.267 634.003 521.639C624.6 528.011 617.406 537.137 613.411 547.762C620.029 537.871 629.827 530.531 641.187 526.952C652.546 523.374 664.788 523.772 675.89 528.08C686.45 532.368 700.344 539.319 711.357 535.617C706.19 528.388 699.374 522.491 691.472 518.416C683.57 514.34 674.81 512.203 665.917 512.181V512.181ZM756.316 540.438C746.317 536.975 734.983 533.06 726.743 522.301C725.143 520.17 723.65 517.773 722.05 515.243C718.48 508.669 713.651 502.86 707.837 498.145C699.997 492.393 689.704 489.57 676.37 489.57C663.264 489.573 650.454 493.463 639.563 500.745C628.673 508.028 620.193 518.376 615.197 530.477C621.53 521.672 630.067 514.682 639.954 510.206C649.84 505.73 660.73 503.925 671.535 504.971C682.339 506.017 692.679 509.878 701.52 516.168C710.362 522.457 717.395 530.955 721.917 540.811C724.168 544.503 727.558 547.366 731.578 548.97C735.598 550.574 740.031 550.832 744.21 549.706C757.357 546.909 756.476 559.293 776.29 563.501V559.959C776.21 548.081 769.73 545.311 756.29 540.438H756.316ZM810.289 574.154C813.196 572.389 815.582 569.887 817.207 566.903C818.833 563.919 819.638 560.559 819.543 557.163C819.276 478.411 755.277 411.031 676.344 411.031C652.616 411.011 629.257 416.887 608.369 428.129C587.482 439.37 569.722 455.625 556.691 475.428C556.424 474.735 516.105 411.776 515.678 411.031H418.479L510.825 555.458L420.719 697.143H515.118L556.184 634.131L597.997 697.143H692.424L606.824 562.596C605.406 559.975 604.638 557.052 604.584 554.073C604.581 544.663 606.433 535.344 610.037 526.649C613.64 517.954 618.923 510.054 625.585 503.398C632.246 496.743 640.155 491.463 648.86 487.861C657.565 484.259 666.895 482.406 676.317 482.406H676.344C716.023 482.406 722.637 506.108 731.197 517.241C748.103 539.239 781.863 529.598 781.863 558.281C781.924 561.571 782.846 564.788 784.539 567.611C786.233 570.434 788.637 572.764 791.513 574.37C794.388 575.976 797.635 576.801 800.93 576.763C804.224 576.725 807.451 575.826 810.289 574.154V574.154ZM819.623 577.696C815.009 587.604 804.69 586.698 799.49 594.715C794.796 601.932 802.53 605.687 802.53 605.687C802.53 605.687 811.516 609.842 816.876 597.404C819.649 591.252 820.609 584.408 819.623 577.696V577.696Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
"name": "app1",
|
||||
"type": "app",
|
||||
"data": {
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "app2",
|
||||
"type": "app",
|
||||
"data": {
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lib",
|
||||
"type": "lib",
|
||||
"data": {
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"taskId": "lib:test",
|
||||
"taskGraphs": {
|
||||
"lib:test": {
|
||||
"roots": ["app1:test", "app2:test", "lib:test"],
|
||||
"tasks": {
|
||||
"app1:test": {
|
||||
"id": "app1:test",
|
||||
"target": { "project": "app1", "target": "test" },
|
||||
"projectRoot": "apps/app1",
|
||||
"overrides": {}
|
||||
},
|
||||
"app2:test": {
|
||||
"id": "app2:test",
|
||||
"target": { "project": "app2", "target": "test" },
|
||||
"projectRoot": "apps/app2",
|
||||
"overrides": {}
|
||||
},
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": { "project": "lib", "target": "test" },
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"app1:test": ["lib:test"],
|
||||
"app2:test": ["lib:test"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
"name": "app1",
|
||||
"type": "app",
|
||||
"data": {
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "app2",
|
||||
"type": "app",
|
||||
"data": {
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lib",
|
||||
"type": "lib",
|
||||
"data": {
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"taskId": "lib:test",
|
||||
"taskGraphs": {
|
||||
"lib:test": {
|
||||
"roots": ["app1:test", "app2:test", "lib:test"],
|
||||
"tasks": {
|
||||
"app1:test": {
|
||||
"id": "app1:test",
|
||||
"target": { "project": "app1", "target": "test" },
|
||||
"projectRoot": "apps/app1",
|
||||
"overrides": {}
|
||||
},
|
||||
"app2:test": {
|
||||
"id": "app2:test",
|
||||
"target": { "project": "app2", "target": "test" },
|
||||
"projectRoot": "apps/app2",
|
||||
"overrides": {}
|
||||
},
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": { "project": "lib", "target": "test" },
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
"name": "lib",
|
||||
"type": "lib",
|
||||
"data": {
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"taskId": "lib:test",
|
||||
"taskGraphs": {
|
||||
"lib:test": {
|
||||
"roots": ["lib:test"],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": { "project": "lib", "target": "test" },
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
"name": "app1",
|
||||
"type": "app",
|
||||
"data": {
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "app2",
|
||||
"type": "app",
|
||||
"data": {
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lib",
|
||||
"type": "lib",
|
||||
"data": {
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"app1": [{ "source": "app1", "target": "lib", "type": "static" }],
|
||||
"app2": [{ "source": "app2", "target": "lib", "type": "static" }],
|
||||
"lib": []
|
||||
},
|
||||
"workspaceLayout": {
|
||||
"appsDir": "apps",
|
||||
"libsDir": "libs"
|
||||
},
|
||||
"affectedProjectIds": [],
|
||||
"focus": null,
|
||||
"groupByFolder": false,
|
||||
"exclude": []
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
import { YouTube } from './youtube.component';
|
||||
---
|
||||
|
||||
<YouTube {...Astro.props} />
|
||||
@@ -0,0 +1,11 @@
|
||||
import classNames from 'classnames';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
/**
|
||||
* Small wrapper around classNames and twMerge
|
||||
* It enables conditional and dynamic class usage,
|
||||
* resolves duplications and conflicts for TailwindCSS
|
||||
*/
|
||||
export function cx(...inputs: classNames.ArgumentArray): string {
|
||||
return twMerge(classNames(inputs));
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import { PlayCircleIcon, PlayIcon } from '@heroicons/react/24/solid';
|
||||
import { cx } from './utils/cx';
|
||||
|
||||
// TODO(caleb): this came from typedoc ui markdown impl in nx-dev
|
||||
type Schema = any;
|
||||
|
||||
export const youtube: Schema = {
|
||||
render: 'YouTube',
|
||||
attributes: {
|
||||
src: {
|
||||
type: 'String',
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: 'String',
|
||||
required: true,
|
||||
},
|
||||
width: {
|
||||
type: 'String',
|
||||
default: '100%',
|
||||
},
|
||||
caption: {
|
||||
// Added caption attribute here
|
||||
type: 'String',
|
||||
required: false, // Not required since it's optional
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function computeVideoID(youtubeURL: string): string | undefined {
|
||||
let match;
|
||||
|
||||
// Check for 'https://www.youtube.com/embed/' format
|
||||
match = youtubeURL.match(/youtube\.com\/embed\/([a-zA-Z0-9_-]+)/);
|
||||
if (match && match[1]) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
// Check for 'https://www.youtube.com/watch?v=' format
|
||||
match = youtubeURL.match(/youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)/);
|
||||
if (match && match[1]) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
match = youtubeURL.match(/youtube\.com\/live\/([a-zA-Z0-9_-]+)/);
|
||||
if (match && match[1]) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
// Check for 'https://youtu.be/' format
|
||||
match = youtubeURL.match(/youtu\.be\/([a-zA-Z0-9_-]+)/);
|
||||
if (match && match[1]) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function computeThumbnailURL(youtubeURL: string) {
|
||||
const videoID = computeVideoID(youtubeURL);
|
||||
if (!videoID) {
|
||||
throw new Error(
|
||||
`Could not properly compute the thumbnail URL for ${youtubeURL}`
|
||||
);
|
||||
}
|
||||
return `https://i.ytimg.com/vi_webp/${videoID}/maxresdefault.webp`;
|
||||
}
|
||||
|
||||
export function computeWatchOnYoutubeURL(youtubeURL: string) {
|
||||
const videoID = computeVideoID(youtubeURL);
|
||||
if (!videoID) {
|
||||
throw new Error(
|
||||
`Could not properly compute the embed URL for ${youtubeURL}`
|
||||
);
|
||||
}
|
||||
return 'https://www.youtube.com/watch?v=' + videoID;
|
||||
}
|
||||
|
||||
export function computeEmbedURL(youtubeURL: string) {
|
||||
const videoID = computeVideoID(youtubeURL);
|
||||
if (!videoID) {
|
||||
throw new Error(
|
||||
`Could not properly compute the embed URL for ${youtubeURL}`
|
||||
);
|
||||
}
|
||||
return 'https://www.youtube.com/embed/' + videoID;
|
||||
}
|
||||
|
||||
const youtubeIcon = (
|
||||
<svg
|
||||
fill="currentColor"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="absolute left-1/2 top-1/2 h-12 w-12 -translate-x-1/2 -translate-y-1/2 transform"
|
||||
>
|
||||
<path d="M23.5 6.19a3.02 3.02 0 0 0-2.12-2.14c-1.88-.5-9.38-.5-9.38-.5s-7.5 0-9.38.5A3.02 3.02 0 0 0 .5 6.19C0 8.07 0 12 0 12s0 3.93.5 5.81a3.02 3.02 0 0 0 2.12 2.14c1.87.5 9.38.5 9.38.5s7.5 0 9.38-.5a3.02 3.02 0 0 0 2.12-2.14C24 15.93 24 12 24 12s0-3.93-.5-5.81zM9.54 15.57V8.43L15.82 12l-6.28 3.57z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export function YouTube(props: {
|
||||
title: string;
|
||||
caption?: string;
|
||||
src: string;
|
||||
width?: string;
|
||||
disableRoundedCorners?: boolean;
|
||||
imageOnly?: boolean;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div className="text-center">
|
||||
{' '}
|
||||
{/* Center alignment applied to the container */}
|
||||
{props.imageOnly ? (
|
||||
<a
|
||||
href={computeWatchOnYoutubeURL(props.src)}
|
||||
className="relative block !text-inherit"
|
||||
>
|
||||
{youtubeIcon}
|
||||
<img
|
||||
src={computeThumbnailURL(props.src)}
|
||||
alt={props.title}
|
||||
width={props.width || '100%'}
|
||||
className={cx(
|
||||
{
|
||||
'rounded-lg shadow-lg': !props.disableRoundedCorners,
|
||||
},
|
||||
'border-2 border-slate-200 hover:border-slate-500 dark:border-slate-700/40 dark:hover:border-slate-700'
|
||||
)}
|
||||
/>
|
||||
</a>
|
||||
) : (
|
||||
<iframe
|
||||
src={computeEmbedURL(props.src)}
|
||||
title={props.title}
|
||||
width={props.width || '100%'}
|
||||
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; fullscreen"
|
||||
loading="lazy"
|
||||
credentialless="true"
|
||||
className={cx({
|
||||
'rounded-lg shadow-lg': !props.disableRoundedCorners,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{props.caption && (
|
||||
<p className="mx-auto pt-0 text-slate-500 md:w-1/2 dark:text-slate-400">
|
||||
{props.caption}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { defineCollection, z } from 'astro:content';
|
||||
import { docsLoader } from '@astrojs/starlight/loaders';
|
||||
import { docsSchema } from '@astrojs/starlight/schema';
|
||||
import { PluginLoader } from './plugins/plugin.loader';
|
||||
import { CliLoader } from './plugins/cli.loader';
|
||||
import { DevkitLoader } from './plugins/devkit.loader';
|
||||
import { file } from 'astro/loaders';
|
||||
import { CnwLoader } from './plugins/cnw.loader.ts';
|
||||
|
||||
// Default docs collection handled by Starlight
|
||||
const docs = defineCollection({
|
||||
loader: docsLoader(),
|
||||
schema: docsSchema(),
|
||||
});
|
||||
|
||||
const nxCliDocs = defineCollection({
|
||||
loader: CliLoader(),
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
docType: z.literal('cli'),
|
||||
}),
|
||||
});
|
||||
|
||||
const pluginDocs = defineCollection({
|
||||
loader: PluginLoader(),
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
pluginName: z.string(),
|
||||
packageName: z.string(),
|
||||
docType: z.enum(['generators', 'executors', 'migrations']),
|
||||
description: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
const devkitDocs = defineCollection({
|
||||
loader: DevkitLoader(),
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
docType: z.enum(['devkit', 'ngcli_adapter']),
|
||||
description: z.string().optional(),
|
||||
category: z.string().optional(),
|
||||
kind: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
const cnwDocs = defineCollection({
|
||||
loader: CnwLoader(),
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
docType: z.enum(['cnw']),
|
||||
}),
|
||||
});
|
||||
|
||||
// general notification collection for showing time based notifications
|
||||
// in app, i.e. webinars, or confs etc.
|
||||
// goal is that different pages/parts of the app can query for their specific needs.
|
||||
// i.e. show banner for webinar, or show a dynamic sidebars item for a conf signups etc.
|
||||
const notifications = defineCollection({
|
||||
loader: file('src/content/notifications.json'),
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
// TODO(caleb): should parse this to a date object
|
||||
date: z.string(),
|
||||
// slug will be dismiss key for the banner
|
||||
slug: z.string(),
|
||||
authors: z.array(z.string()),
|
||||
// these have to contain the tag webinar??
|
||||
type: z.enum(['webinar', 'event', 'release']),
|
||||
cover_image: z.string(),
|
||||
// tbf this can probs be derived if given a full timestamp for `date`
|
||||
time: z.string(),
|
||||
// TODO(caleb); get all possible statues, some of these could be derived such as 'Upcoming' or 'Past - *'
|
||||
// we just really want to know if something is gated or not etc
|
||||
status: z.enum(['Upcoming', 'Past - Gated']),
|
||||
registrationUrl: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
docs,
|
||||
notifications,
|
||||
'nx-cli-docs': nxCliDocs,
|
||||
'plugin-docs': pluginDocs,
|
||||
'devkit-docs': devkitDocs,
|
||||
'cnw-docs': cnwDocs,
|
||||
};
|
||||
@@ -0,0 +1,537 @@
|
||||
[
|
||||
{
|
||||
"name": "@ahryman40k/nx-vitepress",
|
||||
"description": "Nx plugin add vitepress project to your workspace",
|
||||
"url": "https://github.com/Ahryman40k/nx-vitepress/tree/main/packages/nx-vitepress"
|
||||
},
|
||||
{
|
||||
"name": "@klerick/nx-angular-mf",
|
||||
"description": "Custom Angular Builder for Microfrontend Architecture",
|
||||
"url": "https://github.com/klerick/nx-angular-mf"
|
||||
},
|
||||
{
|
||||
"name": "@nightwatch/nx",
|
||||
"description": "The NightwatchJS plugin allows your workspace to use the power of NightwatchJS for E2E and Component Testing on Desktop and Mobile",
|
||||
"url": "https://github.com/nightwatchjs/nightwatch-plugin-nx/"
|
||||
},
|
||||
{
|
||||
"name": "@nxkit/playwright",
|
||||
"description": "The Playwright plugin allows your workspace to use the power of Playwright end-to-end testing",
|
||||
"url": "https://github.com/nxkit/nxkit"
|
||||
},
|
||||
{
|
||||
"name": "qwik-nx",
|
||||
"description": "Get first class support for Qwik inside of Nx monorepos with Qwik optimized custom executors, generators and a vite plugin",
|
||||
"url": "https://github.com/qwikifiers/qwik-nx"
|
||||
},
|
||||
{
|
||||
"name": "@nxkit/style-dictionary",
|
||||
"description": "Nx plugin to generate and build Style Dictionary projects inside your Nx workspace",
|
||||
"url": "https://github.com/nxkit/nxkit"
|
||||
},
|
||||
{
|
||||
"name": "nx-plugin-vite",
|
||||
"description": "Nx plugin integrations with Vite.",
|
||||
"url": "https://nx-plugins.netlify.app/"
|
||||
},
|
||||
{
|
||||
"name": "nx-serverless-cdk",
|
||||
"description": "Create CDK applications and construct libraries. Test and debug infrastructure code and AWS Lambda functions locally.",
|
||||
"url": "https://github.com/castleadmin/nx-plugins/tree/main/nx-serverless-cdk/plugin"
|
||||
},
|
||||
{
|
||||
"name": "@ago-dev/nx-aws-cdk-v2",
|
||||
"description": "An nx plugin for the aws-cdk v2.",
|
||||
"url": "https://github.com/adrian-goe/nx-aws-cdk-v2"
|
||||
},
|
||||
{
|
||||
"name": "@berenddeboer/nx-aws-cdk",
|
||||
"description": "Nx plugin to generate a CDK stack with support for the vitest runner. Supports all CDK commands.",
|
||||
"url": "https://github.com/berenddeboer/nx-plugins/tree/main/packages/nx-aws-cdk"
|
||||
},
|
||||
{
|
||||
"name": "@nx-iac/aws-cdk",
|
||||
"description": "Empowers your Nx workspace with AWS CDK capabilities ⚡",
|
||||
"url": "https://github.com/joelklint/nx-aws-cdk"
|
||||
},
|
||||
{
|
||||
"name": "@routineless/nx-aws-cdk",
|
||||
"description": "Nx plugin to generate and manage aws cdk app and lambdas.",
|
||||
"url": "https://github.com/KozelAnatoliy/routineless/tree/main/packages/nx-aws-cdk"
|
||||
},
|
||||
{
|
||||
"name": "@berenddeboer/nx-sst",
|
||||
"description": "Nx plugin to generate an SST stack and execute all SST commands.",
|
||||
"url": "https://github.com/berenddeboer/nx-plugins/tree/main/packages/nx-sst"
|
||||
},
|
||||
{
|
||||
"name": "@rxap/plugin-localazy",
|
||||
"description": "An Nx plugin for localazy.com upload and download tasks.",
|
||||
"url": "https://gitlab.com/rxap/plugins/-/tree/master/libs/localazy"
|
||||
},
|
||||
{
|
||||
"name": "nx-electron",
|
||||
"description": "An Nx plugin for developing Electron applications",
|
||||
"url": "https://github.com/bennymeg/nx-electron"
|
||||
},
|
||||
{
|
||||
"name": "nx-stylelint",
|
||||
"description": "Nx plugin to use stylelint in a nx workspace",
|
||||
"url": "https://github.com/Phillip9587/nx-stylelint"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/ionic-react",
|
||||
"description": "An Nx plugin for developing Ionic React applications and libraries",
|
||||
"url": "https://github.com/nxext/nx-extensions-ionic/tree/main/packages/ionic-react"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/ionic-angular",
|
||||
"description": "An Nx plugin for developing Ionic Angular applications and libraries",
|
||||
"url": "https://github.com/nxext/nx-extensions-ionic/tree/main/packages/ionic-angular"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/capacitor",
|
||||
"description": "An Nx plugin for developing cross-platform applications using Capacitor",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/main/packages/capacitor"
|
||||
},
|
||||
{
|
||||
"name": "@angular-architects/ddd",
|
||||
"description": "Nx plugin for structuring a monorepo with domains and layers",
|
||||
"url": "https://github.com/angular-architects/nx-ddd-plugin"
|
||||
},
|
||||
{
|
||||
"name": "@flowaccount/nx-serverless",
|
||||
"description": "Nx plugin for node/angular-universal schematics and deployment builders in an Nx workspace",
|
||||
"url": "https://github.com/flowaccount/nx-plugins"
|
||||
},
|
||||
{
|
||||
"name": "@ns3/nx-serverless",
|
||||
"description": "Nx plugin for node serverless applications in an Nx workspace",
|
||||
"url": "https://github.com/Bielik20/nx-plugins/tree/master/packages/nx-serverless"
|
||||
},
|
||||
{
|
||||
"name": "@ns3/nx-jest-playwright",
|
||||
"description": "Nx plugin to run jest-playwright e2e tests in an Nx workspace",
|
||||
"url": "https://github.com/Bielik20/nx-plugins/tree/master/packages/nx-jest-playwright"
|
||||
},
|
||||
{
|
||||
"name": "@ns3/nx-playwright",
|
||||
"description": "Nx plugin to run playwright e2e tests in an Nx workspace",
|
||||
"url": "https://github.com/Bielik20/nx-plugins/tree/master/packages/nx-playwright"
|
||||
},
|
||||
{
|
||||
"name": "@nx-plus/nuxt",
|
||||
"description": "Nx plugin adding first class support for Nuxt in your Nx workspace.",
|
||||
"url": "https://github.com/ZachJW34/nx-plus/tree/master/libs/nuxt"
|
||||
},
|
||||
{
|
||||
"name": "@nx-plus/vue",
|
||||
"description": "Nx plugin adding first class support for Vue in your Nx workspace.",
|
||||
"url": "https://github.com/ZachJW34/nx-plus/tree/master/libs/vue"
|
||||
},
|
||||
{
|
||||
"name": "@nx-plus/docusaurus",
|
||||
"description": "Nx plugin adding first class support for Docusaurus in your Nx workspace.",
|
||||
"url": "https://github.com/ZachJW34/nx-plus/tree/master/libs/docusaurus"
|
||||
},
|
||||
{
|
||||
"name": "@twittwer/compodoc",
|
||||
"description": "Nx Plugin to integrate the generation of documentation with Compodoc in the Nx workflow",
|
||||
"url": "https://github.com/twittwer/nx-tools/tree/master/libs/compodoc#readme"
|
||||
},
|
||||
{
|
||||
"name": "@enio.ai/nx-install",
|
||||
"description": "nx-install is a plugin for Nx workspaces to quickly setup custom install commands via npm.",
|
||||
"url": "https://github.com/enio-ireland/enio/tree/develop/packages/nx-install#readme"
|
||||
},
|
||||
{
|
||||
"name": "@enio.ai/typedoc",
|
||||
"description": "typedoc is a plugin for Nx workspaces to quickly setup documentation automation on your projects using typedoc.",
|
||||
"url": "https://github.com/enio-ireland/enio/tree/develop/packages/typedoc#readme"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/svelte",
|
||||
"description": "Nx plugin to use Svelte within nx workspaces",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/svelte"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/stencil",
|
||||
"description": "Nx plugin to use StencilJs within nx workspaces",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/stencil"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/vue",
|
||||
"description": "Nx plugin to use VueJS 3 within nx workspaces",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/vue"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/solid",
|
||||
"description": "Nx plugin to use SolidJS within nx workspaces",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/solid"
|
||||
},
|
||||
{
|
||||
"name": "@nx-go/nx-go",
|
||||
"description": "Nx plugin to use Go in a Nx workspace",
|
||||
"url": "https://github.com/nx-go/nx-go"
|
||||
},
|
||||
{
|
||||
"name": "@nx-golang/gin",
|
||||
"description": "Nx plugin to use Go-Gin in a Nx workspace",
|
||||
"url": "https://github.com/nx-golang/nx-golang"
|
||||
},
|
||||
{
|
||||
"name": "@angular-architects/module-federation",
|
||||
"description": "Nx plugin to use webpack module federation",
|
||||
"url": "https://github.com/angular-architects/module-federation-plugin"
|
||||
},
|
||||
{
|
||||
"name": "@nxrocks/nx-spring-boot",
|
||||
"description": "Nx plugin to generate, run, package, build (and more) Spring Boot projects inside your Nx workspace",
|
||||
"url": "https://github.com/tinesoft/nxrocks/tree/master/packages/nx-spring-boot"
|
||||
},
|
||||
{
|
||||
"name": "@trumbitta/nx-plugin-openapi",
|
||||
"description": "OpenAPI Plugin for Nx. Keep your API spec files in libs, and auto-generate sources.",
|
||||
"url": "https://github.com/trumbitta/nx-trumbitta/tree/main/packages/nx-plugin-openapi"
|
||||
},
|
||||
{
|
||||
"name": "@trumbitta/nx-plugin-unused-deps",
|
||||
"description": "Check the dependency graph of your monorepo, looking for unused NPM packages.",
|
||||
"url": "https://github.com/trumbitta/nx-trumbitta/tree/main/packages/nx-plugin-unused-deps"
|
||||
},
|
||||
{
|
||||
"name": "@nxrocks/nx-flutter",
|
||||
"description": "Nx Plugin adding first class support for Flutter in your Nx workspace",
|
||||
"url": "https://github.com/tinesoft/nxrocks/tree/master/packages/nx-flutter"
|
||||
},
|
||||
{
|
||||
"name": "@srleecode/domain",
|
||||
"description": "Nx Plugin for allowing operations to occur at the domain level instead of the default library level",
|
||||
"url": "https://github.com/srlee309/domain"
|
||||
},
|
||||
{
|
||||
"name": "@jscutlery/semver",
|
||||
"description": "Nx plugin to automate semantic versioning and CHANGELOG generation.",
|
||||
"url": "https://github.com/jscutlery/semver"
|
||||
},
|
||||
{
|
||||
"name": "ngx-deploy-npm",
|
||||
"description": "Publish your libraries to NPM with just one command.",
|
||||
"url": "https://github.com/bikecoders/ngx-deploy-npm"
|
||||
},
|
||||
{
|
||||
"name": "@nx-dotnet/core",
|
||||
"description": "Nx plugin for developing and housing .NET projects within an Nx workspace.",
|
||||
"url": "https://github.com/nx-dotnet/nx-dotnet"
|
||||
},
|
||||
{
|
||||
"name": "@nxrocks/nx-quarkus",
|
||||
"description": "Nx plugin to generate, run, package, build (and more) Quarkus projects inside your Nx workspace",
|
||||
"url": "https://github.com/tinesoft/nxrocks/tree/master/packages/nx-quarkus"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/gcp-secrets",
|
||||
"description": "Nx plugin to generate and securely deploy your Google Cloud Secrets",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/gcp-secrets"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/gcp-storage",
|
||||
"description": "Nx plugin to upload to Google Cloud Storage",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/gcp-storage"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/gcp-functions",
|
||||
"description": "Nx plugin to generate, run, build and deploy your Google Cloud Functions",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/gcp-functions"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/gcp-deployment-manager",
|
||||
"description": "Nx plugin to deploy your Google Cloud Deployments",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/gcp-deployment-manager"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/gcp-cloud-run",
|
||||
"description": "Nx plugin to build and deploy your docker container to Google Cloud Run",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/gcp-cloud-run"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/translations",
|
||||
"description": "Nx plugin to extract, pull, push and translate your apps translations",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/translations"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/firebase-hosting",
|
||||
"description": "Nx plugin to deploy your apps to Firebase hosting",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/firebase-hosting"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/e2e-runner",
|
||||
"description": "Nx plugin that can start your API before running Cypress/Playwright",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/e2e-runner"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/vercel",
|
||||
"description": "Nx plugin to deploy your apps to Vercel",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/vercel"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/strapi",
|
||||
"description": "Nx plugin for developing Strapi applications",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/strapi"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/playwright",
|
||||
"description": "Nx plugin to run playwright e2e tests in an Nx workspace",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/playwright"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/terraform",
|
||||
"description": "Nx plugin for deploying your resources with Terraform",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/terraform"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/pulumi",
|
||||
"description": "Nx plugin for deploying your resources with Pulumi",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/pulumi"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/react-email",
|
||||
"description": "Nx plugin for developing email templates with react.email",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/react-email"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/shadcn-ui",
|
||||
"description": "Nx plugin for working with shadcn/ui",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/shadcn-ui"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/docusaurus",
|
||||
"description": "Nx plugin adding first class support for Docusaurus in your Nx workspace.",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/docusaurus"
|
||||
},
|
||||
{
|
||||
"name": "@nativescript/nx",
|
||||
"description": "Nx Plugin adding first class support for NativeScript in your Nx workspace",
|
||||
"url": "https://github.com/nativescript/nx"
|
||||
},
|
||||
{
|
||||
"name": "@nxtensions/astro",
|
||||
"description": "Nx plugin adding first class support for Astro (https://astro.build).",
|
||||
"url": "https://github.com/nxtensions/nxtensions/tree/main/packages/astro"
|
||||
},
|
||||
{
|
||||
"name": "@nxrs/cargo",
|
||||
"description": "Nx plugin adding first-class support for Rust applications and libraries.",
|
||||
"url": "https://github.com/nxrs/cargo/tree/main/packages/cargo"
|
||||
},
|
||||
{
|
||||
"name": "nx-uvu",
|
||||
"description": "An nx executor for the uvu test library",
|
||||
"url": "https://github.com/jmcdo29/nx-uvu"
|
||||
},
|
||||
{
|
||||
"name": "@ndrsg/nx-http",
|
||||
"description": "Plugin with executors, which can perform http-stuff (e.g. requests, webhooks, file up/downloads, ...)",
|
||||
"url": "https://github.com/ndrsg/nx-ext/tree/main/packages/nx-http"
|
||||
},
|
||||
{
|
||||
"name": "@diogovcs/graphql-mesh",
|
||||
"description": "Nx plugin to add GraphQL Mesh integration to Nx Workspace.",
|
||||
"url": "https://github.com/DiogoVCS/nx-workspace-plugins"
|
||||
},
|
||||
{
|
||||
"name": "@computas/nx-yarn",
|
||||
"description": "A plugin to help make nx with yarn a pleasant experience.",
|
||||
"url": "https://github.com/computas/nx-yarn"
|
||||
},
|
||||
{
|
||||
"name": "@theunderscorer/nx-semantic-release",
|
||||
"description": "Nx plugin for automated releases using semantic-release.",
|
||||
"url": "https://github.com/TheUnderScorer/nx-semantic-release"
|
||||
},
|
||||
{
|
||||
"name": "nx-pwm",
|
||||
"description": "Nx plugin and CLI to help maintain packages, inspired by NX repo tooling",
|
||||
"url": "https://github.com/gioragutt/nx-pwm"
|
||||
},
|
||||
{
|
||||
"name": "@nxrocks/nx-micronaut",
|
||||
"description": "Nx plugin to generate, run, package, build (and more) Micronaut projects inside your Nx workspace",
|
||||
"url": "https://github.com/tinesoft/nxrocks/tree/master/packages/nx-micronaut"
|
||||
},
|
||||
{
|
||||
"name": "@koliveira15/nx-sonarqube",
|
||||
"description": "Nx plugin that scans projects using SonarQube / SonarCloud.",
|
||||
"url": "https://github.com/koliveira15/nx-sonarqube"
|
||||
},
|
||||
{
|
||||
"name": "@mands/nx-playwright",
|
||||
"description": "The Playwright plugin allows your workspace to use the power of Playwright end-to-end testing using a native runner.",
|
||||
"url": "https://github.com/marksandspencer/nx-plugins/tree/main/packages/nx-playwright"
|
||||
},
|
||||
{
|
||||
"name": "@diogovcs/stryker-mutator",
|
||||
"description": "Nx plugin to add Stryker Mutator mutation tests to the Nx Workspace.",
|
||||
"url": "https://github.com/DiogoVCS/nx-workspace-plugins"
|
||||
},
|
||||
{
|
||||
"name": "@spaceribs/nx-web-ext",
|
||||
"description": "Nx plugin that allows you to build, test and deploy web extensions.",
|
||||
"url": "https://github.com/spaceribs/spaceribs/tree/main/packages/nx-web-ext"
|
||||
},
|
||||
{
|
||||
"name": "@spaceribs/nx-betterer",
|
||||
"description": "Nx plugin that applies betterer standards on a per-project basis.",
|
||||
"url": "https://github.com/spaceribs/spaceribs/tree/main/packages/nx-betterer"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extensions/helm",
|
||||
"description": "Nx plugin providing comprehensive support for Helm charts, including generation, packaging, and publishing capabilities.",
|
||||
"url": "https://github.com/marcolongol/nx-extensions/tree/main/packages/helm"
|
||||
},
|
||||
{
|
||||
"name": "@nx-tools/nx-container",
|
||||
"description": "Nx plugin to build OCI containers with Docker, Podman or Kaniko.",
|
||||
"url": "https://github.com/gperdomor/nx-tools/tree/main/plugins/nx-container"
|
||||
},
|
||||
{
|
||||
"name": "@nxrocks/nx-melos",
|
||||
"description": "Nx plugin adding first class support for Melos in your Nx workspace",
|
||||
"url": "https://github.com/tinesoft/nxrocks/tree/master/packages/nx-melos"
|
||||
},
|
||||
{
|
||||
"name": "@monodon/rust",
|
||||
"description": "Adds Cargo and Rust support",
|
||||
"url": "https://github.com/cammisuli/monodon/tree/main/packages/rust"
|
||||
},
|
||||
{
|
||||
"name": "nx-mesh",
|
||||
"description": "GraphQL Mesh support for Nx",
|
||||
"url": "https://github.com/domjtalbot/nx-mesh"
|
||||
},
|
||||
{
|
||||
"name": "@nxazure/func",
|
||||
"description": "Nx plugin to add Azure Functions support to Nx Workspace.",
|
||||
"url": "https://github.com/AlexPshul/nxazure/tree/master/packages/func"
|
||||
},
|
||||
{
|
||||
"name": "@rbnx/webdriverio",
|
||||
"description": "A Nx plugin that adds the WebdriverIO testing framework to a NX workspace.",
|
||||
"url": "https://github.com/Roozenboom/rbnx/tree/main/packages/webdriverio"
|
||||
},
|
||||
{
|
||||
"name": "nx-ngrok",
|
||||
"description": "Ngrok support for Nx",
|
||||
"url": "https://github.com/domjtalbot/nx-ngrok"
|
||||
},
|
||||
{
|
||||
"name": "@nxrocks/nx-ktor",
|
||||
"description": "Nx plugin to generate, run, package, build (and more) Ktor projects inside your Nx workspace",
|
||||
"url": "https://github.com/tinesoft/nxrocks/tree/master/packages/nx-ktor"
|
||||
},
|
||||
{
|
||||
"name": "nx-size-limit",
|
||||
"description": "Adds size-limit support for Nx (performance budget tool for JavaScript)",
|
||||
"url": "https://github.com/LironHazan/nx-size-limit"
|
||||
},
|
||||
{
|
||||
"name": "@loft-orbital/terraform",
|
||||
"description": "Terraform executors and generators",
|
||||
"url": "https://github.com/loft-orbital/nx-plugins/tree/main/packages/terraform"
|
||||
},
|
||||
{
|
||||
"name": "@nxlv/python",
|
||||
"description": "Nx plugin designed to extend the Nx features to work with Python projects based on Poetry.",
|
||||
"url": "https://github.com/lucasvieirasilva/nx-plugins/tree/main/packages/nx-python"
|
||||
},
|
||||
{
|
||||
"name": "nx-gcp-cache",
|
||||
"description": "Nx plugin to use Google Cloud Storage as distributed remote cache",
|
||||
"url": "https://github.com/davidgarciab/nx-gcp-cache"
|
||||
},
|
||||
{
|
||||
"name": "@jnxplus/nx-gradle",
|
||||
"description": "Nx plugin to add Gradle multi-project builds support to Nx workspace",
|
||||
"url": "https://github.com/khalilou88/jnxplus/tree/main/packages/nx-gradle"
|
||||
},
|
||||
{
|
||||
"name": "@jnxplus/nx-maven",
|
||||
"description": "Nx plugin to add Maven multi-module project support to Nx workspace",
|
||||
"url": "https://github.com/khalilou88/jnxplus/tree/main/packages/nx-maven"
|
||||
},
|
||||
{
|
||||
"name": "@naxodev/nx-cloudflare",
|
||||
"description": "Nx plugin for Cloudflare, in particular Cloudflare workers. It allows to generate build and run Cloudflare workers in your Nx workspace.",
|
||||
"url": "https://github.com/naxodev/oss/tree/main/packages/nx-cloudflare"
|
||||
},
|
||||
{
|
||||
"name": "@naxodev/gonx",
|
||||
"description": "Modern Nx plugin to use Go in a Nx workspace. Forked from @nx-go/nx-go",
|
||||
"url": "https://github.com/naxodev/oss/tree/main/packages/gonx"
|
||||
},
|
||||
{
|
||||
"name": "@ziacik/azure-func",
|
||||
"description": "Generating, serving and publishing Azure Functions 4 apps.",
|
||||
"url": "https://github.com/ziacik/nx-tools/tree/master/packages/azure-func"
|
||||
},
|
||||
{
|
||||
"name": "@simondotm/nx-firebase",
|
||||
"description": "Nx plugin to support Firebase Apps and Cloud Functions in Nx workspaces",
|
||||
"url": "https://github.com/simondotm/nx-firebase"
|
||||
},
|
||||
{
|
||||
"name": "@dman926/nx-python-pdm",
|
||||
"description": "Use Python in NX workspaces with PDM",
|
||||
"url": "https://github.com/dman926/nx-python-pdm"
|
||||
},
|
||||
{
|
||||
"name": "@gnuechtel/nx-cucumber",
|
||||
"description": "Plugin to use Cucumber within Nx workspaces",
|
||||
"url": "https://gitlab.com/gnuechtel/open-source/-/tree/main/libs/nx-cucumber"
|
||||
},
|
||||
{
|
||||
"name": "@analogjs/platform",
|
||||
"description": "Official plugin to add Analog to your Nx monorepo.",
|
||||
"url": "https://analogjs.org/docs/integrations/nx"
|
||||
},
|
||||
{
|
||||
"name": "@getlarge/nx-heroku",
|
||||
"description": "Plugin to deploy and promote Nx apps on Heroku",
|
||||
"url": "https://github.com/getlarge/nx-heroku"
|
||||
},
|
||||
{
|
||||
"name": "@getlarge/nx-node-sea",
|
||||
"description": "Plugin that provides integration with Node.js Single Executable Applications (SEA).",
|
||||
"url": "https://github.com/getlarge/nx-node-sea"
|
||||
},
|
||||
{
|
||||
"name": "@huge-nx/conventions",
|
||||
"description": "Plugin to generate and manage Nx workspaces by adhering to established workspace conventions.",
|
||||
"url": "https://github.com/jogelin/huge-nx"
|
||||
},
|
||||
{
|
||||
"name": "nx-github-pages",
|
||||
"description": "A small Nx plugin to make deploying static projects to GitHub Pages easy.",
|
||||
"url": "https://github.com/agentender/nx-github-pages"
|
||||
},
|
||||
{
|
||||
"name": "nx-solhint",
|
||||
"description": "Solhint generators and inferred tasks for Nx",
|
||||
"url": "https://github.com/juliangsibecas/nx-solhint"
|
||||
},
|
||||
{
|
||||
"name": "nx-foundry",
|
||||
"description": "Foundry generators and inferred tasks for Nx",
|
||||
"url": "https://github.com/juliangsibecas/nx-foundry"
|
||||
},
|
||||
{
|
||||
"name": "@robby-rabbitman/nx-plus-web-test-runner",
|
||||
"description": "Web Test Runner plugin for Nx workspaces.",
|
||||
"url": "https://github.com/RobbyRabbitman/nx-plus/tree/main/libs/web-test-runner"
|
||||
},
|
||||
{
|
||||
"name": "@aws/nx-plugin",
|
||||
"description": "Nx Plugin for AWS: Accelerate building cloud-native applications with AWS",
|
||||
"url": "https://github.com/awslabs/nx-plugin-for-aws"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,314 @@
|
||||
---
|
||||
title: Nx Cloud CLI
|
||||
description: Complete reference for all Nx Cloud CLI commands
|
||||
---
|
||||
|
||||
The Nx Cloud command line interface provides various commands to connect and interact with Nx Cloud, manage distributed task execution, and handle authentication.
|
||||
Below is a complete reference for all available commands and their options.
|
||||
|
||||
## Available Commands
|
||||
|
||||
### `nx-cloud login`
|
||||
|
||||
Provision a local personal access token to access Nx Cloud features. This will open your browser to the Nx Cloud application and after signing in will generate a personal access token and save it in a configuration file locally called `nxcloud.ini`.
|
||||
|
||||
This command is the same as running `nx login`.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
npx nx-cloud login [nxCloudUrl]
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
| Option | Type | Description | Default |
|
||||
| ------------ | ------ | ---------------------------------------------- | ---------------------- |
|
||||
| `nxCloudUrl` | string | The URL of the Nx Cloud instance to connect to | `https://cloud.nx.app` |
|
||||
|
||||
#### Configuration File Location
|
||||
|
||||
{% tabs %}
|
||||
{% tabitem label="macOS & Linux" %}
|
||||
|
||||
We look for this file at the following locations:
|
||||
|
||||
- `$XDG_CONFIG_HOME/nxcloud/nxcloud.ini`
|
||||
- `$HOME/config/nxcloud/nxcloud.ini`
|
||||
- `$HOME/.nxcloud.ini`
|
||||
|
||||
If we don't find an existing config file, we create one at `$HOME/config/nxcloud/nxcloud.ini`
|
||||
|
||||
{% /tabitem %}
|
||||
{% tabitem label="Windows" %}
|
||||
|
||||
We look for this file within the `%LOCALAPPDATA%/nxcloud` directory and if it does not exist, we will create a new `nxcloud.ini` file there.
|
||||
|
||||
{% /tabitem %}
|
||||
{% /tabs %}
|
||||
The format of this file is as follows:
|
||||
|
||||
```ini
|
||||
[https://cloud\.nx\.app]
|
||||
personalAccessToken=SOME_ACCESS_TOKEN
|
||||
```
|
||||
|
||||
If you have access to multiple instances of the Nx Cloud application (e.g. self-hosted Enterprise and our managed instance), each instance will be saved to this file under its URL.
|
||||
|
||||
### `nx-cloud logout`
|
||||
|
||||
Revoke a personal access token from your local environment. This will remove the personal access token from the locally initialized configuration file and also invalidate the token from the Nx Cloud application. You will be prompted to remove a single token or all tokens from your local environment.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
npx nx-cloud logout
|
||||
```
|
||||
|
||||
### `nx-cloud configure`
|
||||
|
||||
To provision more than one personal access token for multiple contexts (e.g. home and work machines) you can use the personal access tokens page under your Nx Cloud profile. To save a personal access token to your local `nxcloud.ini` file without needing to edit the file yourself call `nx-cloud configure`.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
npx nx-cloud configure
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
| Option | Type | Description | Default |
|
||||
| ----------------------- | ------ | -------------------------------------- | ---------------------- |
|
||||
| `--personalAccessToken` | string | The personal access token to configure | |
|
||||
| `--nx-cloud-url` | string | The URL of the Nx Cloud instance | `https://cloud.nx.app` |
|
||||
|
||||
#### Examples
|
||||
|
||||
```bash
|
||||
npx nx-cloud configure --personalAccessToken=SOME_ACCESS_TOKEN
|
||||
```
|
||||
|
||||
To configure multiple tokens for different instances of the Nx Cloud app:
|
||||
|
||||
```bash
|
||||
npx nx-cloud configure --personalAccessToken=SOME_ACCESS_TOKEN --nx-cloud-url=https://nx-cloud.my-domain.app
|
||||
```
|
||||
|
||||
### `nx-cloud convert-to-nx-cloud-id`
|
||||
|
||||
When logging into Nx Cloud with a [Personal Access Token](/ci/recipes/security/personal-access-tokens), your `nx.json` file needs to include the `nxCloudId` property, which acts as a unique identifier for your workspace. If you have been using the previous `nxCloudAccessToken` to connect, simply run `npx nx-cloud convert-to-nx-cloud-id` to automatically update your configuration to use `nxCloudId`.
|
||||
|
||||
If you are connecting to Nx Cloud with a workspace that is version 19.6 or lower, this command will also install the latest version of the Nx Cloud npm package and add it into your `package.json`. Only Nx versions 19.7 and higher natively support the `nxCloudId` property in the `nx.json` file; for versions 19.6 and lower, the Nx Cloud npm package will be needed to use that property.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
npx nx-cloud convert-to-nx-cloud-id
|
||||
```
|
||||
|
||||
### `nx-cloud start-ci-run`
|
||||
|
||||
At the beginning of your main job, invoke `npx nx-cloud start-ci-run`. This tells Nx Cloud that the following series of command correspond to the same CI run.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
npx nx-cloud start-ci-run
|
||||
```
|
||||
|
||||
:::warning[Do not run start-ci-run locally]
|
||||
`npx nx-cloud start-ci-run` generates a temporary marker file that can cause a local Nx repo to behave as if it is a part of a CI run. This can cause strange behavior like Nx commands timing out or throwing unexpected errors.
|
||||
To discourage this from happening, this command will run a check to see if it is running in a CI environment. You can bypass this check with `npx nx-cloud start-ci-run --force`.
|
||||
|
||||
If you accidentally run this command locally, remove all generated marker files with `npx nx-cloud cleanup`.
|
||||
:::
|
||||
|
||||
#### Options
|
||||
|
||||
| Option | Type | Description | Default |
|
||||
| ------------------------------- | ------- | --------------------------------------------------------------------------------------- | ------- |
|
||||
| `--distribute-on` | string | Configure the number of agents and launch templates for distributed execution | |
|
||||
| `--require-explicit-completion` | boolean | Disable automatic completion monitoring and require explicit `nx-cloud complete-ci-run` | `false` |
|
||||
| `--stop-agents-after` | string | Comma-separated list of targets after which agents should terminate | |
|
||||
| `--stop-agents-on-failure` | boolean | Terminate all agents when a command fails | `true` |
|
||||
| `--use-dte-by-default` | boolean | Configure Nx to distribute all commands by default | `true` |
|
||||
| `--with-env-vars` | string | Comma-separated list of environment variables to pass to Nx Agents | |
|
||||
| `--force` | boolean | Bypass CI environment check | |
|
||||
|
||||
#### Detailed Option Usage
|
||||
|
||||
##### --distribute-on
|
||||
|
||||
By default, `npx nx-cloud start-ci-run` is intended for use with [Nx Agents](/ci/features/distribute-task-execution) and expects `--distribute-on` to be configured. It will output a warning if this flag is not set. If you are running a distributed execution with a legacy setup without Nx Agents, you can pass `--distribute-on=manual` to disable this warning.
|
||||
|
||||
This command tells Nx Cloud how many agents to use (and what launch templates to use) to distribute tasks. E.g.,
|
||||
`npx nx-cloud start-ci-run --distribute-on="8 linux-medium-js"` will distribute CI using 8 agents that are initialized
|
||||
using the `linux-medium-js` launch template.
|
||||
|
||||
You can also [define the configuration in a file](/ci/features/dynamic-agents) and reference it as follows:
|
||||
`npx nx-cloud start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yaml"`.
|
||||
|
||||
```yaml
|
||||
// .nx/workflows/dynamic-changesets.yaml
|
||||
distribute-on:
|
||||
small-changeset: 3 linux-medium-js
|
||||
medium-changeset: 6 linux-medium-js
|
||||
large-changeset: 10 linux-medium-js
|
||||
```
|
||||
|
||||
##### --stop-agents-after
|
||||
|
||||
You can tell Nx Cloud to terminate agents after it sees a certain
|
||||
target or group of targets: `npx nx-cloud start-ci-run --stop-agents-after=build,test,e2e`.
|
||||
|
||||
:::tip[Be as specific]
|
||||
For the best results, always tell Nx Cloud about all targets that should be expected during your pipeline. This will
|
||||
prevent your CI Pipeline Execution from ending prematurely, cutting off any unfinished work.
|
||||
:::
|
||||
|
||||
Whether you are running commands serially or in parallel (through use of the `&` operand or discrete jobs), you should
|
||||
include one or more targets from each command.
|
||||
|
||||
{% tabs %}
|
||||
{% tabitem label="Serial command execution" %}
|
||||
|
||||
#### Incorrect example:
|
||||
|
||||
```yaml
|
||||
- run: npx nx-cloud start-ci-run --stop-agents-after=build
|
||||
- run: nx affected -t build
|
||||
- run: nx affected -t lint
|
||||
- run: nx affected -t test
|
||||
```
|
||||
|
||||
Nx Cloud will only look for the presence of a `build` target before determining your pipeline can be shut down. Since
|
||||
both `lint` and `test` will only run after `build` is complete, your pipeline would end before all intended work is
|
||||
executed.
|
||||
|
||||
#### Corrected example:
|
||||
|
||||
```yaml
|
||||
- run: npx nx-cloud start-ci-run --stop-agents-after=lint,test,build
|
||||
- run: nx affected -t lint
|
||||
- run: nx affected -t test
|
||||
- run: nx affected -t build
|
||||
```
|
||||
{% /tabitem %}
|
||||
{% tabitem label="Parallel command execution" %}
|
||||
|
||||
#### Incorrect example:
|
||||
|
||||
```yaml
|
||||
- run: npx nx-cloud start-ci-run --stop-agents-after=test
|
||||
- run: nx affected -t build & nx affected -t lint & nx affected -t test
|
||||
```
|
||||
|
||||
At first glance, this example may appear correct. However, since all the commands are executed in parallel, it could
|
||||
be possible that your `test` command results in full cache hits and finishes faster than the `build` and `lint` commands.
|
||||
In this case, Nx Cloud will stop your pipeline, leaving work incomplete for both the `build` and `lint` commands.
|
||||
|
||||
#### Corrected example:
|
||||
|
||||
```yaml
|
||||
- run: npx nx-cloud start-ci-run --stop-agents-after=lint,test,build
|
||||
- run: nx affected -t build & nx affected -t lint & nx affected -t test
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
{% /tabs %}
|
||||
|
||||
#### Advanced shutdown targeting
|
||||
|
||||
In advanced pipeline setups, it is possible that you want to run multiple commands with the same target, but with
|
||||
distinct configurations. An example of this may be doing static translations for different locales during your builds.
|
||||
|
||||
```yaml
|
||||
- run: npx nx-cloud start-ci-run --stop-agents-after=build
|
||||
- run: nx affected -t build --configuration=locale-en
|
||||
- run: nx affected -t build --configuration=locale-es
|
||||
```
|
||||
|
||||
In this case, the `build` target will be executed twice, once with the `locale-en` configuration and once with the
|
||||
`locale-es` configuration. However, Nx Cloud is only looking for the `build` target to assess whether the CI Pipeline
|
||||
Execution can be marked complete.
|
||||
|
||||
To address this, you can pass an optional `configuration` to make your `target`s more specific.
|
||||
|
||||
```yaml
|
||||
- run: npx nx-cloud start-ci-run --stop-agents-after=build:locale-en,build:locale-es
|
||||
- run: nx affected -t build --configuration=locale-en
|
||||
- run: nx affected -t build --configuration=locale-es
|
||||
```
|
||||
|
||||
##### --with-env-vars (Nx Agents Only)
|
||||
|
||||
By default, invoking `npx nx-cloud start-ci-run` will take all environment variables prefixed with `NX_` and send them over to Nx Agents.
|
||||
This means that your access token, verbose logging configuration and other Nx-related environment variables will be the same on your
|
||||
main CI jobs and the Nx Agent machines.
|
||||
|
||||
If you want to pass other environment variables from the main job to Nx Agents, you can do it as follows: `--with-env-vars="VAR1,VAR2"`.
|
||||
This will set `VAR1` and `VAR2` on Nx Agents to the same values set on the main job before any steps run.
|
||||
|
||||
> Note: none of the values passed to Nx Agents are stored by Nx Cloud.
|
||||
|
||||
#### Enabling/Disabling Distribution
|
||||
|
||||
Invoking `npx nx-cloud start-ci-run` will tell Nx to distribute by default. You can enable/disable distribution for
|
||||
individual commands as follows:
|
||||
|
||||
{% tabs %}
|
||||
{% tabitem label="Nx >= 18" %}
|
||||
|
||||
- `nx affected -t build --agents` (explicitly enable distribution)
|
||||
- `nx affected -t build --no-agents` (explicitly disable distribution)
|
||||
|
||||
{% /tabitem %}
|
||||
{% tabitem label="Nx >= 14.7" %}
|
||||
|
||||
- `nx affected -t build --dte` (explicitly enable distribution)
|
||||
- `nx affected -t build --no-dte` (explicitly disable distribution)
|
||||
|
||||
{% /tabitem %}
|
||||
{% /tabs %}
|
||||
|
||||
### `nx-cloud stop-all-agents`
|
||||
|
||||
Same as `npx nx-cloud complete-ci-run`.
|
||||
|
||||
This command tells Nx Cloud to terminate all agents associated with this CI pipeline execution.
|
||||
Invoking this command is not needed anymore. New versions of Nx Cloud can track when the main job terminates
|
||||
and terminate associated agents automatically.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
npx nx-cloud stop-all-agents
|
||||
```
|
||||
|
||||
### `nx-cloud complete-ci-run`
|
||||
|
||||
Explicitly complete a CI run when using `--require-explicit-completion` with `start-ci-run`.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
npx nx-cloud complete-ci-run
|
||||
```
|
||||
|
||||
### `nx-cloud cleanup`
|
||||
|
||||
Remove temporary marker files created by `start-ci-run` if accidentally run locally.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
npx nx-cloud cleanup
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
You can get help for any command by adding the `--help` flag:
|
||||
|
||||
```bash
|
||||
npx nx-cloud <command> --help
|
||||
```
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: Test Plugin
|
||||
description: A test plugin page
|
||||
---
|
||||
|
||||
This is a test plugin page to demonstrate the dynamic sidebar functionality.
|
||||
|
||||
## Plugin Features
|
||||
|
||||
The sidebar should automatically switch to the "Plugins" tab when viewing this page.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @nx/test-plugin
|
||||
```
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: 'Nx Plugins'
|
||||
description: 'Complete reference for all Nx plugin commands'
|
||||
---
|
||||
|
||||
# Nx Plugins
|
||||
|
||||
Nx provides a rich ecosystem of plugins to help you build, test, and deploy your applications. Each plugin offers generators, executors, and migrations specific to its technology stack.
|
||||
|
||||
## Available Plugins
|
||||
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
export const pluginDocs = await getCollection('plugin-docs');
|
||||
export const pluginsByName = pluginDocs.reduce((acc, doc) => {
|
||||
if (!acc[doc.data.pluginName]) {
|
||||
acc[doc.data.pluginName] = [];
|
||||
}
|
||||
acc[doc.data.pluginName].push(doc);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export const sortedPluginNames = Object.keys(pluginsByName).sort();
|
||||
|
||||
<ul>
|
||||
{sortedPluginNames.map((pluginName) => (
|
||||
<li key={pluginName}>
|
||||
<a href={`/api/plugins/${pluginName}`}>@nx/{pluginName}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
@@ -0,0 +1,219 @@
|
||||
---
|
||||
title: Mental Model
|
||||
description: Understand how Nx works with project graphs, task graphs, affected commands, and caching to efficiently manage your monorepo development workflow.
|
||||
---
|
||||
|
||||
Nx is a VSCode of build tools, with a powerful core, driven by metadata, and extensible through [plugins](/concepts/nx-plugins). Nx works with a
|
||||
few concepts to drive your monorepo efficiently, and effectively. This guide covers the mental model around how Nx works
|
||||
with project graphs, task graphs, affected commands, computation hashing and caching.
|
||||
|
||||
## The project graph
|
||||
|
||||
A project graph is used to reflect the source code in your repository and all the external dependencies that aren't
|
||||
authored in your repository, such as Webpack, React, Angular, and so forth.
|
||||
|
||||

|
||||
|
||||
Nx analyzes your file system to detect projects. Projects are identified by the presence of a `package.json` file or `project.json` file. Projects identification can also be customized through plugins. You can manually define dependencies between
|
||||
the project nodes, but you don't have to do it very often. Nx analyzes files' source code, your installed dependencies, TypeScript
|
||||
files, and others figuring out these dependencies for you. Nx also stores the cached project graph, so it only
|
||||
reanalyzes the files you have changed.
|
||||
|
||||

|
||||
|
||||
Nx provides an updated graph after each analysis is done.
|
||||
|
||||
## Metadata-driven
|
||||
|
||||
Everything in Nx comes with metadata to enable toolability. Nx gathers information about your projects and tasks and then uses that information to help you understand and interact with your codebase. With the right plugins installed, most of the metadata can be [inferred directly from your existing configuration files](/concepts/inferred-tasks) so you don't have to define it manually.
|
||||
|
||||
This metadata is used by Nx itself, by VSCode and WebStorm integrations, by GitHub integration, and by third-party
|
||||
tools.
|
||||
|
||||

|
||||
|
||||
These tools are able to implement richer experiences with Nx using this metadata.
|
||||
|
||||
## The task graph
|
||||
|
||||
Nx uses the project graph to create a task graph. Any time you run anything, Nx creates a task graph from the project
|
||||
graph and then executes the tasks in that graph.
|
||||
|
||||
For instance `nx test lib` creates a task graph with a single node:
|
||||
|
||||
\{% graph height="100px" type="task" jsonFile="shared/mental-model/single-task.json" %}
|
||||
\{% /graph %}
|
||||
|
||||
A task is an invocation of a target. If you invoke the same target twice, you create two tasks.
|
||||
|
||||
Nx uses the [project graph](#the-project-graph), but the task graph and project graph aren't isomorphic, meaning they
|
||||
aren't directly connected. In the case above, `app1` and `app2` depend on `lib`, but
|
||||
running `nx run-many -t test -p app1 app2 lib`, the created task graph will look like this:
|
||||
\{% side-by-side %}
|
||||
|
||||
**Project Graph**
|
||||
|
||||
**Task Graph**
|
||||
|
||||
\{% /side-by-side %}
|
||||
|
||||
\{% side-by-side %}
|
||||
\{% graph height="200px" type="project" jsonFile="shared/mental-model/three-projects.json" %}
|
||||
\{% /graph %}
|
||||
|
||||
\{% graph height="200px" type="task" jsonFile="shared/mental-model/disconnected-tasks.json"%}
|
||||
\{% /graph %}
|
||||
\{% /side-by-side %}
|
||||
|
||||
Even though the apps depend on `lib`, testing `app1` doesn't depend on the testing `lib`. This means that the two tasks
|
||||
can
|
||||
run in parallel.
|
||||
|
||||
Let's look at the test target relying on its dependencies.
|
||||
|
||||
```json
|
||||
// project.json
|
||||
{
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/apps/app1"],
|
||||
"dependsOn": ["^test"],
|
||||
"options": {
|
||||
"jestConfig": "apps/app1/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With this, running the same test command creates the following task graph:
|
||||
|
||||
\{% side-by-side %}
|
||||
|
||||
**Project Graph**
|
||||
|
||||
**Task Graph**
|
||||
|
||||
\{% /side-by-side %}
|
||||
|
||||
\{% side-by-side %}
|
||||
\{% graph height="200px" type="project" jsonFile="shared/mental-model/three-projects.json" %}
|
||||
\{% /graph %}
|
||||
|
||||
\{% graph height="200px" type="task" jsonFile="shared/mental-model/connected-tasks.json" %}
|
||||
\{% /graph %}
|
||||
\{% /side-by-side %}
|
||||
|
||||
This often makes more sense for builds, where to build `app1`, you want to build `lib` first. You can also define
|
||||
similar
|
||||
relationships between targets of the same project, including a test target that depends on the build.
|
||||
|
||||
A task graph can contain different targets, and those can run in parallel. For instance, as Nx is building `app2`, it
|
||||
can be testing `app1` at the same time.
|
||||
|
||||

|
||||
|
||||
Nx also runs the tasks in the task graph in the right order. Nx executing tasks in parallel speeds up your overall
|
||||
execution time.
|
||||
|
||||
## Affected commands
|
||||
|
||||
When you run `nx test app1`, you are telling Nx to run the `app1:test` task plus all the tasks it depends on.
|
||||
|
||||
When you run `nx run-many -t test -p app1 lib`, you are telling Nx to do the same for two
|
||||
tasks `app1:test`
|
||||
and `lib:test`.
|
||||
|
||||
When you run `nx run-many -t test`, you are telling Nx to do this for all the projects.
|
||||
|
||||
As your workspace grows, retesting all projects becomes too slow. To address this Nx implements code change analysis to
|
||||
get the min set of projects that need to be retested. How does it work?
|
||||
|
||||
When you run `nx affected -t test`, Nx looks at the files you changed in your PR, it will look at the nature of
|
||||
change (what exactly did you update in those files), and it uses this to figure the list of projects in the workspace
|
||||
that can be affected by this change. It then runs the `run-many` command with that list.
|
||||
|
||||
For instance, if my PR changes `lib`, and I then run `nx affected -t test`, Nx figures out that `app1` and `app2`
|
||||
depend on `lib`, so it will invoke `nx run-many -t test -p app1 app2 lib`.
|
||||
|
||||

|
||||
|
||||
Nx analyzes the nature of the changes. For example, if you change the version of Next.js in the package.json, Nx knows
|
||||
that `app2` cannot be affected by it, so it only retests `app1`.
|
||||
|
||||
## Computation hashing and caching
|
||||
|
||||
Nx runs the tasks in the task graph in the right order. Before running the task, Nx computes its computation hash. As
|
||||
long as the computation hash is the same, the output of running the task is the same.
|
||||
|
||||
How does Nx do it?
|
||||
|
||||
By default, the computation hash for say `nx test app1` includes:
|
||||
|
||||
- All the source files of `app1` and `lib`
|
||||
- Relevant global configuration
|
||||
- Versions of external dependencies
|
||||
- [Runtime values provisioned by the user](/reference/inputs#runtime-inputs)
|
||||
- CLI Command flags
|
||||
|
||||

|
||||
|
||||
This behavior is customizable. For instance, lint checks may only depend on the source code of the project and global
|
||||
configs. Builds can depend on the `.d.ts` files of the compiled libs instead of their source.
|
||||
|
||||
After Nx computes the hash for a task, it then checks if it ran this exact computation before. First, it checks locally,
|
||||
and then if it is missing, and if a remote cache is configured, it checks remotely.
|
||||
|
||||
If Nx finds the computation, Nx retrieves it and replays it. Nx places the right files in the right folders and prints
|
||||
the terminal output. So from the user's point of view, the command ran the same, just a lot faster.
|
||||
|
||||

|
||||
|
||||
If Nx doesn't find this computation, Nx runs the task, and after it completes, it takes the outputs and the terminal
|
||||
output and stores it locally (and if configured remotely). All of this happens transparently, so you don't have to worry
|
||||
about it.
|
||||
|
||||
Although conceptually this is fairly straightforward, Nx optimizes this to make this experience good for you. For
|
||||
instance, Nx:
|
||||
|
||||
- Captures stdout and stderr to make sure the replayed output looks the same, including on Windows.
|
||||
- Minimizes the IO by remembering what files are replayed where.
|
||||
- Only shows relevant output when processing a large task graph.
|
||||
- Provides affordances for troubleshooting cache misses. And many other optimizations.
|
||||
|
||||
As your workspace grows, the task graph looks more like this:
|
||||
|
||||
\{% graph height="400px" type="task" jsonFile="shared/mental-model/large-tasks.json"%}
|
||||
\{% /graph %}
|
||||
|
||||
All of these optimizations are crucial for making Nx usable for any non-trivial workspace. Only the minimum amount of
|
||||
work happens. The rest is either left as is or restored from the cache.
|
||||
|
||||
## Distributed task execution
|
||||
|
||||
Nx supports running commands across multiple machines. You can either set it up by hand or use Nx Cloud. [Read the comparison of the two approaches.](/blog/distributing-ci-binning-and-distributed-task-execution)
|
||||
|
||||
When using the distributed task execution, Nx is able to run any task graph on many agents instead of locally.
|
||||
|
||||
For instance, `nx affected --build` won't run the build locally (which can take hours for large workspaces). Instead,
|
||||
it will send the Task Graph to Nx Cloud. Nx Cloud Agents will then pick up the tasks they can run and execute them.
|
||||
|
||||
Note that this happens transparently. If an agent builds `app1`, it will fetch the outputs for `lib` if it doesn't have
|
||||
them
|
||||
already.
|
||||
|
||||
As agents complete tasks, the main job where you invoked `nx affected --build` will start receiving created files and
|
||||
terminal outputs.
|
||||
|
||||
After `nx affected --build` completes, the machine will have the build files and all the terminal outputs as if it ran
|
||||
it locally.
|
||||
|
||||

|
||||
|
||||
## In summary
|
||||
|
||||
- Nx is able to analyze your source code to create a Project Graph.
|
||||
- Nx can use the project graph and information about projects' targets to create a Task Graph.
|
||||
- Nx is able to perform code-change analysis to create the smallest task graph for your PR.
|
||||
- Nx supports computation caching to never execute the same computation twice. This computation cache is pluggable and
|
||||
can be distributed.
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: Integrate Nx with your Coding Assistant
|
||||
description: Set up Nx MCP server to give AI assistants deep workspace context, terminal integration, and enhanced development capabilities.
|
||||
sidebar:
|
||||
order: 6
|
||||
label: AI Integration
|
||||
---
|
||||
|
||||
Nx provides deep integration with AI coding assistants through the **Nx Model Context Protocol (MCP) server**, giving your AI assistant comprehensive understanding of your monorepo structure, running processes, and development workflows.
|
||||
|
||||
## Setup and Configuration
|
||||
|
||||
### Automatic Setup with Nx Console
|
||||
|
||||
Make sure you have **Nx Console** [installed in your editor](/getting-started/editor-setup), as it automatically exposes the Nx MCP server. Once Nx Console is installed and you open an Nx workspace, you'll receive a notification to **"Improve Copilot/AI agent with Nx-specific context"** - click **"Yes"** to automatically configure it.
|
||||
|
||||

|
||||
|
||||
If you miss the notification, you can manually run the configuration by opening the command palette (`Ctrl/Cmd + Shift + P`) and running `nx.configureMcpServer`.
|
||||
|
||||
### Manual Setup for Other AI Clients
|
||||
|
||||
For MCP-compatible clients like **Claude Desktop**, **Cursor**, or other AI assistants, add the following configuration:
|
||||
|
||||
```json
|
||||
// mcp.json
|
||||
{
|
||||
"servers": {
|
||||
"nx-mcp": {
|
||||
"command": "npx",
|
||||
"args": ["nx-mcp@latest", "/path/to/your/workspace"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace `/path/to/your/workspace` with your actual workspace path.
|
||||
|
||||
## What This Integration Enables
|
||||
|
||||
The Nx AI integration provides your coding assistant with powerful capabilities:
|
||||
|
||||
- **[Workspace Structure Understanding](/blog/nx-mcp-vscode-copilot)** - Deep architectural awareness of your monorepo, project relationships, and dependencies
|
||||
- **[Real-time Terminal Integration](/blog/nx-terminal-integration-ai)** - AI can read your terminal output, running processes, and error messages without copy-pasting
|
||||
- **[CI Pipeline Context](/blog/nx-editor-ci-llm-integration)** - Access to build failures, test results, and deployment status from your CI/CD processes
|
||||
- **[Enhanced Code Generation](/blog/nx-generators-ai-integration)** - AI-powered generator suggestions and custom scaffolding with intelligent defaults
|
||||
- **Cross-project Impact Analysis** - Understanding the implications of changes across your entire monorepo
|
||||
- **Autonomous Error Debugging** - AI independently accesses context to help fix development issues
|
||||
|
||||
Learn more about [why Nx and AI work so well together](/blog/nx-and-ai-why-they-work-together).
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: Editor Integration
|
||||
description: Set up Nx Console in VSCode or JetBrains for visual UI, task management, code generation, and enhanced AI assistance in your Nx workspace.
|
||||
sidebar:
|
||||
order: 5
|
||||
---
|
||||
|
||||
Nx Console editor extensions make your developer experience richer. The extensions:
|
||||
|
||||
- [enhance AI integrations](/features/enhance-AI) by providing workspace-level context and up-to-date docs
|
||||
- show [inferred tasks](/concepts/inferred-tasks) and help you invoke them via the Project Details View
|
||||
- provide a [visual UI for discovering and invoking generators](/recipes/nx-console/console-generate-command)
|
||||
- visualize dependencies between projects and tasks
|
||||
- and more!
|
||||
|
||||
## Download
|
||||
|
||||
### Official Integrations
|
||||
|
||||
If you are using [VSCode](https://code.visualstudio.com/) or a [JetBrains IDE](https://www.jetbrains.com/) you can install Nx Console from their respective marketplaces. Nx Console for VSCode and JetBrains is **built and maintained by the Nx team**.
|
||||
|
||||
\{ % install-nx-console /% }
|
||||
|
||||
- [Install from the VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console)
|
||||
- [Install from the JetBrains Marketplace](https://plugins.jetbrains.com/plugin/21060-nx-console)
|
||||
- [Contribute on GitHub](https://github.com/nrwl/nx-console)
|
||||
|
||||

|
||||
|
||||
### Neovim
|
||||
|
||||
If you are using [Neovim](https://neovim.io/), you can install [Equilibris/nx.nvim](https://github.com/Equilibris/nx.nvim) with your favorite package manager.
|
||||
|
||||
This plugin is **NOT** built or maintained by the Nx team. They are maintained by independent community contributors.
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
title: Installation
|
||||
description: Install Nx globally via npm, Homebrew, Chocolatey, or apt. Add Nx to existing repos with nx init and keep dependencies updated automatically.
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
To install Nx on your machine, choose one of the following methods based on your operating system and package manager. You can also use `npx` to run Nx without installing it globally.
|
||||
{% tabs %}
|
||||
{% tabitem label="npm" %}
|
||||
|
||||
```shell
|
||||
npm add --global nx
|
||||
```
|
||||
|
||||
**Note:** You can also use `yarn`, `pnpm`, or `bun`
|
||||
{% /tabitem %}
|
||||
|
||||
{% tabitem label="Homebrew (macOS, Linux)" %}
|
||||
|
||||
```shell
|
||||
brew install nx
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
|
||||
{% tabitem label="Chocolatey (Windows)" %}
|
||||
|
||||
```shell
|
||||
choco install nx
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
|
||||
{% tabitem label="apt (Ubuntu)" %}
|
||||
|
||||
```shell
|
||||
sudo add-apt-repository ppa:nrwl/nx
|
||||
sudo apt update
|
||||
sudo apt install nx
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
{% /tabs %}
|
||||
{% tabs %}
|
||||
{% tabitem label="npm" %}
|
||||
|
||||
```shell
|
||||
npm add --global nx
|
||||
```
|
||||
|
||||
**Note:** You can also use `yarn`, `pnpm`, or `bun`
|
||||
{% /tabitem %}
|
||||
|
||||
{% tabitem label="Homebrew (macOS, Linux)" %}
|
||||
|
||||
```shell
|
||||
brew install nx
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
|
||||
{% tabitem label="Chocolatey (Windows)" %}
|
||||
|
||||
```shell
|
||||
choco install nx
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
|
||||
{% tabitem label="apt (Ubuntu)" %}
|
||||
|
||||
```shell
|
||||
sudo add-apt-repository ppa:nrwl/nx
|
||||
sudo apt update
|
||||
sudo apt install nx
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
{% /tabs %}
|
||||
|
||||
## Adding Nx to Your Repository
|
||||
|
||||
To add Nx to an existing repository, run:
|
||||
|
||||
```shell
|
||||
nx init
|
||||
```
|
||||
|
||||
**Note:** You can also manually install the [`nx` NPM package](https://www.npmjs.com/package/nx) and create a [nx.json](/reference/nx-json) to configure it. Learn more about [adopting Nx in an existing project](/recipes/adopting-nx)
|
||||
|
||||
### Starter Repository
|
||||
|
||||
To create a starter repository, you can use the `create-nx-workspace` command. This will create a new Nx workspace with a default configuration and example applications.
|
||||
|
||||
```shell
|
||||
npx create-nx-workspace@latest
|
||||
```
|
||||
|
||||
## Update Nx
|
||||
|
||||
When you update Nx, Nx will also [automatically update your dependencies](/features/automate-updating-dependencies) if you have an [Nx plugin](/concepts/nx-plugins) installed for that dependency. To update Nx, run:
|
||||
|
||||
```shell
|
||||
nx migrate latest
|
||||
```
|
||||
|
||||
This will create a `migrations.json` file with any update scripts that need to be run. Run them with:
|
||||
|
||||
```shell
|
||||
nx migrate --run-migrations
|
||||
```
|
||||
|
||||
{% aside type="tip" title="Update One Major Version at a Time" %}
|
||||
To avoid potential issues, it is [recommended to update one major version of Nx at a time](/recipes/tips-n-tricks/advanced-update#one-major-version-at-a-time-small-steps).
|
||||
{% /aside %}
|
||||
|
||||
## Tutorials
|
||||
|
||||
Try one of these tutorials for a full walkthrough of what to do after you install Nx:
|
||||
|
||||
- [TypeScript Monorepo Tutorial](/getting-started/tutorials/typescript-packages-tutorial)
|
||||
- [React Monorepo Tutorial](/getting-started/tutorials/react-monorepo-tutorial)
|
||||
- [Angular Monorepo Tutorial](/getting-started/tutorials/angular-monorepo-tutorial)
|
||||
|
||||
## More Documentation
|
||||
|
||||
- [Add Nx to an Existing Repository](/recipes/adopting-nx)
|
||||
- [Update Nx](/features/automate-updating-dependencies)
|
||||
- [Update Your Global Nx Installation](/recipes/installation/update-global-installation)
|
||||
- [Install Nx in a Non-Javascript Repo](/recipes/installation/install-non-javascript)
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: What is Nx?
|
||||
description: 'Nx is an AI-first build platform that connects everything from your editor to CI Helping you deliver fast, without breaking things.'
|
||||
sidebar:
|
||||
order: 1
|
||||
label: Intro
|
||||
---
|
||||
|
||||
Nx is a powerful, open source, technology-agnostic build platform designed to efficiently manage codebases of any scale. From small single projects to large enterprise monorepos, Nx is designed to **streamline your development workflows**, **minimize errors** and **dramatically reduce waste** by [saving engineering time](https://youtu.be/2BLqiNnBPuU) and cutting compute costs.
|
||||
|
||||
|
||||
{% youtube title="What is Nx?" src="https://www.youtube.com/embed/-_4WMl-Fn0w" /%}
|
||||
|
||||
As teams and codebases grow, productivity bottlenecks multiply: build times increase, CI becomes flaky, and code sharing becomes complex. **Nx reduces friction across your entire development cycle.**
|
||||
|
||||
## Start small, extend as you grow
|
||||
|
||||
Nx is built in a modular fashion, allowing you to adopt as little or as much as you'd like at any moment in your development lifecycle. You can **start with just the core and add additional capabilities incrementally** as your needs grow and complexity increases.
|
||||
|
||||
At the **foundation is Nx Core**, a Rust-based, technology-agnostic task runner. Nx Core creates a knowledge graph of your workspace, understanding project relationships and dependencies. This enables highly optimized and fast task execution regardless of technology stack. It runs `package.json` scripts in [TypeScript monorepos](/technologies/typescript/introduction) or Gradle tasks in [Java projects](/technologies/java/introduction) or [can be extended](/extending-nx/intro/getting-started) to meet your projects specific needs
|
||||
|
||||
{% aside type="tip" title="What do you mean by _running NPM scripts_?" %}
|
||||
|
||||
At the very core, Nx is a super fast, intelligent task runner. Let's take the example of an NPM workspace. This could be a project's `package.json`:
|
||||
|
||||
```json
|
||||
// my-project/package.json
|
||||
{
|
||||
"name": "my-project",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "jest"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then you can simply add Nx to your root `package.json`:
|
||||
|
||||
```json
|
||||
// package.json
|
||||
{
|
||||
"devDependencies": {
|
||||
"nx": "latest"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And once that's done, you can run your tasks via Nx.
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx build my-project
|
||||
```
|
||||
|
||||
This will execute the `build` script from `my-project`'s `package.json`, equivalent to running `npm run build` in that project directory.
|
||||
|
||||
Similarly you [can run tasks across all projects](/features/run-tasks), just specific ones or just those from projects you touched.
|
||||
|
||||
From there, you can gradually enhance your setup by adding features like [task caching](/features/cache-task-results), adding [plugins](/technologies), optimizing your CI via [task distribution](/ci/features/distribute-task-execution), and many more powerful capabilities as your needs grow.
|
||||
|
||||
{% /aside %}
|
||||
|
||||
Nx Core works great alone, but you can incrementally add capabilities as needed. Speed up your CI with [**Nx Cloud**](/ci) through remote caching and distributed task execution. Add [**Nx Console**](/getting-started/editor-setup) integrating Nx with your editor, giving you powerful autocomplete support, project graph visualization, CI run notifications and an MCP to [make your AI coding assistant smarter](/features/enhance-AI). Add [**Nx Plugins**](/technologies) for technology-specific automation and DX improvements and build custom capabilities using [Nx Devkit](/extending-nx/intro/getting-started).
|
||||
|
||||
## Where to go from here?
|
||||
|
||||
{% cardgrid %}
|
||||
|
||||
|
||||
{% linkcard title="Get set up and ready with Nx" href="/getting-started"
|
||||
description="Dive right in with our getting started steps to install Nx, set up your editor, and create your first project"
|
||||
/%}
|
||||
|
||||
{% linkcard
|
||||
title="Step by step with our tutorials"
|
||||
href="/getting-started/tutorials"
|
||||
description="Learn more about Nx through hands-on tutorials for different technology stacks"
|
||||
/%}
|
||||
|
||||
{% linkcard title="Learn with our video courses" href="/courses"
|
||||
description="Dive deeper with comprehensive video courses that walk you through Nx concepts"
|
||||
/%}
|
||||
|
||||
{% linkcard title="Dive deep into Nx features" href="/features"
|
||||
description="Discover all the powerful features that Nx provides to streamline your workflow"
|
||||
/%}
|
||||
|
||||
{% linkcard title="Understand underlying concepts" href="/concepts"
|
||||
description="Improve your understanding of the core concepts of how Nx works under the hood"
|
||||
/%}
|
||||
|
||||
{% linkcard title="Explore Technologies" href="/technologies"
|
||||
description="Explore Nx's technology integrations and how it can support your specific stack"
|
||||
/%}
|
||||
|
||||
{% /cardgrid %}
|
||||
|
||||
**Stay up to date** with our latest news by [⭐️ starring us on Github](https://github.com/nrwl/nx), [subscribing to our Youtube channel](https://www.youtube.com/@nxdevtools), [joining our Discord](https://go.nx.dev/community), [subscribe to our monthly tech newsletter](https://go.nrwl.io/nx-newsletter) or follow us [on X](https://x.com/nxdevtools), [Bluesky](https://bsky.app/profile/nx.dev) and [LinkedIn](https://www.linkedin.com/company/nxdevtools).
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: Start a new project with Nx
|
||||
description: Create a new Nx workspace manually with your favorite CLI or use the guided setup with presets for various technology stacks and configurations.
|
||||
sidebar:
|
||||
label: Start a new project
|
||||
order: 3
|
||||
---
|
||||
|
||||
When you start a new project with Nx, you have two options: manual or guided.
|
||||
|
||||
## Option 1: Manual setup
|
||||
|
||||
In a nutshell, this means using your favorite CLI to create your initial project setup and then adding Nx to it.
|
||||
|
||||
Let's take the example of an NPM workspace. Create a new root-level `package.json` like:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-workspace",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"workspaces": ["packages/*", "apps/*"]
|
||||
}
|
||||
```
|
||||
|
||||
Then you can add Nx to it by using:
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx init
|
||||
```
|
||||
|
||||
{% aside type="note" title="Global Installs" %}
|
||||
Make sure you have [Nx installed globally](/getting-started/installation) or use `npx` if you're in a JavaScript environment
|
||||
{% /aside %}
|
||||
|
||||
Nx will detect the underlying workspace configuration, ask you a couple of questions and install itself into the workspace. As you keep going, you can incrementally add functionality, like:
|
||||
|
||||
- [configure caching](/features/cache-task-results)
|
||||
- [adding Nx plugins to help refine your workflows](/plugin-registry)
|
||||
- [optimizing your CI](/ci/intro/ci-with-nx)
|
||||
|
||||
## Option 2: Guided setup (JavaScript only)
|
||||
|
||||
Alternatively, you can choose a more guided approach by leveraging some of the presets Nx comes with.
|
||||
|
||||
Run the following command to get started:
|
||||
|
||||
```shell {% frame="none" %}
|
||||
npx create-nx-workspace@latest
|
||||
```
|
||||
|
||||
This interactive command will guide you through the setup process, allowing you to:
|
||||
|
||||
- **Choose your workspace name** - This will be the name of your root directory
|
||||
- **Select your preferred package manager** - npm, yarn, or pnpm
|
||||
- **Pick a preset** - Choose from various technology stacks and configurations
|
||||
- **Configure additional options** - Such as styling solutions, testing frameworks, and more
|
||||
|
||||
Choose a preset that matches your technology stack. This gives you a fully configured workspace.
|
||||
|
||||
You can also choose **an empty workspace preset** which sets up the bare minimum configuration including Nx itself. This allows you to add technologies and features incrementally over time as you need them.
|
||||
|
||||
<!-- ## Pick Your Stack!
|
||||
|
||||
{% cards cols="3" lgCols="8" mdCols="6" smCols="5" moreLink="/showcase/example-repos" %}
|
||||
|
||||
{% link-card title="Express" appearance="small" url="/technologies/node/express" icon="express" /%}
|
||||
{% link-card title="Vue" appearance="small" url="/technologies/vue/introduction" icon="vue" /%}
|
||||
{% link-card title="Next" appearance="small" url="/technologies/react/next" icon="nextjs" /%}
|
||||
{% link-card title="Nuxt" appearance="small" url="/technologies/vue/nuxt/introduction" icon="nuxt" /%}
|
||||
{% link-card title="Nest" appearance="small" url="/technologies/node/nest" icon="nestjs" /%}
|
||||
{% link-card title="Remix" appearance="small" url="/technologies/react/remix" icon="remix" /%}
|
||||
{% link-card title="Expo" appearance="small" url="/technologies/react/expo" icon="expo" /%}
|
||||
{% link-card title="React Native" appearance="small" url="/technologies/react/react-native" icon="react" /%}
|
||||
{% link-card title="Fastify" appearance="small" url="/showcase/example-repos/mongo-fastify" icon="fastify" /%}
|
||||
{% link-card title="Svelte" appearance="small" url="/showcase/example-repos/add-svelte" icon="svelte" /%}
|
||||
{% link-card title="Solid" appearance="small" url="/showcase/example-repos/add-solid" icon="solid" /%}
|
||||
{% link-card title="Lit" appearance="small" url="/showcase/example-repos/add-lit" icon="lit" /%}
|
||||
{% link-card title="Astro" appearance="small" url="/showcase/example-repos/add-astro" icon="astro" /%}
|
||||
{% link-card title="Qwik" appearance="small" url="/showcase/example-repos/add-qwik" icon="qwik" /%}
|
||||
|
||||
{% link-card title="Rust" appearance="small" url="/showcase/example-repos/add-rust" icon="rust" /%}
|
||||
{% link-card title="Go" appearance="small" url="https://github.com/nrwl/nx-recipes/blob/main/go/README.md" icon="go" /%}
|
||||
{% link-card title=".NET" appearance="small" url="https://github.com/nrwl/nx-recipes/tree/main/dot-net-standalone" icon="dotnet" /%}
|
||||
{% link-card title="Cypress" appearance="small" url="/technologies/test-tools/cypress/introduction" icon="cypress" /%}
|
||||
{% link-card title="Playwright" appearance="small" url="/technologies/test-tools/playwright/introduction" icon="playwright" /%}
|
||||
{% link-card title="Vite" appearance="small" url="/technologies/build-tools/vite" icon="vite" /%}
|
||||
{% link-card title="Storybook" appearance="small" url="/technologies/test-tools/storybook" icon="storybook" /%}
|
||||
{% link-card title="Jest" appearance="small" url="/technologies/test-tools/jest/introduction" icon="jest" /%}
|
||||
{% link-card title="Rspack" appearance="small" url="/technologies/build-tools/rspack/introduction" icon="rspack" /%}
|
||||
|
||||
{% /cards %} -->
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Start with an existing project
|
||||
description: Add Nx to any existing project with a single command. Start with Nx Core and gradually adopt plugins, CI integrations, and other capabilities.
|
||||
sidebar:
|
||||
order: 4
|
||||
label: Add to existing project
|
||||
---
|
||||
|
||||
In many situations, you have an existing codebase and want to improve it with Nx using an **incremental adoption approach**.
|
||||
|
||||
Thanks to [Nx's modular architecture](/getting-started/intro), you can start with just **Nx Core** and then gradually add [technology-specific plugins](/technologies), [CI integrations](/ci), or other capabilities as your requirements evolve.
|
||||
|
||||
Getting started is remarkably simple. You can add Nx to any existing project with a single command:
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx init
|
||||
```
|
||||
|
||||
{% aside type="note" title="Global Installs" %}
|
||||
Make sure you have [Nx installed globally](/getting-started/installation) or use `npx` if you're in a JavaScript environment
|
||||
{% /aside %}
|
||||
|
||||
This command automatically detects your underlying workspace structure, whether it's a monorepo, a single project, or something in between. Nx will then ask you a series of targeted questions about your setup and preferences, using your answers to auto-configure itself seamlessly into your existing workspace. This intelligent setup process ensures you get the benefits of Nx with minimal configuration changes or disruption to your current development workflow.
|
||||
|
||||
Here are some guides that give you more details based on the technology stack you're using:
|
||||
|
||||
{% cardgrid %}
|
||||
|
||||
{% linkcard title="Add to Existing Monorepo"
|
||||
href="/recipes/adopting-nx/adding-to-monorepo" %}
|
||||
{% /linkcard %}
|
||||
{% linkcard title="Add to Any Project" href="/recipes/adopting-nx/adding-to-existing-project" /%}
|
||||
|
||||
{% linkcard title="Migrate from Angular CLI" href="technologies/angular/migration/angular" /%}
|
||||
|
||||
{% linkcard title="Import Projects into Your Nx Workspace" href="/recipes/adopting-nx/import-project" /%}
|
||||
|
||||
{% /cardgrid %}
|
||||
|
||||
{% linkcard title="See more options" href="/recipes/adopting-nx" /%}
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Example Guide
|
||||
description: A guide in my new Starlight docs site.
|
||||
---
|
||||
|
||||
Guides lead a user through a specific task they want to accomplish, often with a sequence of steps.
|
||||
Writing a good guide requires thinking about what your users are trying to do.
|
||||
|
||||
## Further reading
|
||||
|
||||
- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: Test Guide
|
||||
description: A test guide to demonstrate the sidebar
|
||||
---
|
||||
|
||||
# Test Guide
|
||||
|
||||
This is a test guide page to demonstrate the dynamic sidebar functionality.
|
||||
|
||||
## Overview
|
||||
|
||||
The sidebar should automatically switch to the "Guides" tab when viewing this page.
|
||||
|
||||
## Features
|
||||
|
||||
- Dynamic tab switching based on URL
|
||||
- Multiple group support
|
||||
- Flexible configuration
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: Nx Documentation
|
||||
description: Learn how to use Nx to build faster and scale better
|
||||
template: splash
|
||||
hero:
|
||||
title: Welcome to Nx
|
||||
tagline: An AI-first build platform that connects everything from your editor to CI. Helping you deliver fast, without breaking things.
|
||||
actions:
|
||||
- text: Get Started
|
||||
link: /getting-started/intro
|
||||
icon: right-arrow
|
||||
variant: primary
|
||||
- text: API Reference
|
||||
link: /api/
|
||||
icon: open-book
|
||||
---
|
||||
|
||||
import { Card, CardGrid } from '@astrojs/starlight/components';
|
||||
|
||||
## Explore Nx
|
||||
|
||||
<CardGrid stagger>
|
||||
<Card title="Getting Started" icon="rocket">
|
||||
Learn the basics of Nx and set up your first workspace. [Start learning
|
||||
→](/getting-started/intro)
|
||||
</Card>
|
||||
<Card title="API Reference" icon="document">
|
||||
Browse comprehensive API documentation for all Nx tools and plugins. [View
|
||||
API →](/api/)
|
||||
</Card>
|
||||
<Card title="Nx Cloud" icon="cloud">
|
||||
Speed up your builds with distributed caching and task execution. [Learn
|
||||
about Nx Cloud →](/getting-started/nx-cloud)
|
||||
</Card>
|
||||
<Card title="Plugins" icon="puzzle">
|
||||
Extend Nx with plugins for React, Angular, Vue, and more. [Browse plugins
|
||||
→](/api/plugins)
|
||||
</Card>
|
||||
</CardGrid>
|
||||
@@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"title": "Polygraph Launch Preview: Conformance, Workspace Graph, and Custom Workflows",
|
||||
"description": "Join us for a demo of the new polygraph features in Nx Cloud, launching this month with Nx Enterprise! These features are designed to extend some of the powerful benefits of an Nx workspace to multi-workspace environments and take Nx Cloud beyond CI. We'll cover our updated conformance dashboard, how to visualize dependencies across your repos, the new onboarding experience for workspaces that aren’t yet a part of Nx Cloud, and preview how Nx Cloud will be able to fix your code automatically via agentic AI.",
|
||||
"date": "2025-06-09",
|
||||
"slug": "polygraph-launch-preview-conformance-workspace-graph-and-custom-workflows",
|
||||
"authors": ["James Henry"],
|
||||
"type": "webinar",
|
||||
"cover_image": "/blog/images/2025-06-18/June-2025-Webinar-Card.avif",
|
||||
"time": "10am ET/2pm UTC",
|
||||
"status": "Past - Gated",
|
||||
"registrationUrl": "https://go.nx.dev/june2025-webinar"
|
||||
},
|
||||
{
|
||||
"title": "Securing Your Build Pipeline: Critical Vulnerabilities and Modern Security Practices",
|
||||
"description": "Join Nx CEO, Jeff Cross, and CTO, Victor Savkin, for an essential security briefing on protecting your build infrastructure from emerging threats. With the recent publication of CVE-2025-36852 – CREEP (Cache Race-condition Exploit Enables Poisoning, severity 9.4), it's more critical than ever to understand the security implications of your build and CI/CD choices. This webinar goes beyond just one vulnerability to address the broader security challenges facing modern development teams. Whether you're using Nx or other build systems, you'll learn actionable strategies to secure your entire build pipeline.",
|
||||
"date": "2025-06-25",
|
||||
"slug": "securing-your-build-pipeline-critical-vulnerabilities-and-modern-security-practices",
|
||||
"authors": ["Jeff Cross", "Victor Savkin"],
|
||||
"type": "webinar",
|
||||
"cover_image": ",/blog/images/2025-07-09/2025-July-Webinar-Card.avif",
|
||||
"time": "1pm ET/5pm UTC",
|
||||
"status": "Upcoming",
|
||||
"registrationUrl": "https://go.nx.dev/july2025-webinar"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
import { getEntry, render } from 'astro:content';
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
|
||||
const doc = await getEntry('cnw-docs', 'cnw-cli');
|
||||
|
||||
if (!doc) {
|
||||
throw new Error('CNW CLI documentation not found');
|
||||
}
|
||||
|
||||
const { Content, headings } = await render(doc);
|
||||
---
|
||||
|
||||
<StarlightPage
|
||||
frontmatter={{
|
||||
title: doc.data.title
|
||||
}}
|
||||
headings={headings || []}
|
||||
>
|
||||
<Content />
|
||||
</StarlightPage>
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
import { getEntry, render } from 'astro:content';
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
|
||||
const nxClI = await getEntry('nx-cli-docs', 'api/nx-cli');
|
||||
if (!nxClI || !nxClI.rendered) {
|
||||
throw new Error('No content found for nx-cli docs');
|
||||
}
|
||||
|
||||
const { Content, headings } = await render(nxClI);
|
||||
---
|
||||
|
||||
<StarlightPage
|
||||
frontmatter={{
|
||||
title: '@nx/cli',
|
||||
description: 'Complete reference for @nx/cli',
|
||||
}}
|
||||
headings={headings || []}
|
||||
>
|
||||
<Content />
|
||||
</StarlightPage>
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
import { getCollection, render } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const pluginDocs = await getCollection(
|
||||
'plugin-docs',
|
||||
(entry) => entry.data.docType === 'executors'
|
||||
);
|
||||
|
||||
// Generate paths for each plugin
|
||||
return pluginDocs.map((p) => ({
|
||||
params: { plugin: p.data.pluginName },
|
||||
props: { doc: p }
|
||||
}));
|
||||
}
|
||||
|
||||
const { doc } = Astro.props;
|
||||
const pluginName = doc.data.pluginName;
|
||||
|
||||
if (!doc.body) {
|
||||
throw new Error(`No content found for plugin: ${pluginName}`);
|
||||
}
|
||||
|
||||
const { Content, headings } = await render(doc);
|
||||
---
|
||||
|
||||
<StarlightPage
|
||||
frontmatter={{
|
||||
title: `@nx/${pluginName} - executors`,
|
||||
description: `@nx/${pluginName} plugin executors`
|
||||
}}
|
||||
headings={headings || []}
|
||||
>
|
||||
<Content />
|
||||
</StarlightPage>
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
import { getCollection, render } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const pluginDocs = await getCollection(
|
||||
'plugin-docs',
|
||||
(entry) => entry.data.docType === 'generators'
|
||||
);
|
||||
|
||||
// Generate paths for each plugin
|
||||
return pluginDocs.map((p) => ({
|
||||
params: { plugin: p.data.pluginName },
|
||||
props: { doc: p },
|
||||
}));
|
||||
}
|
||||
|
||||
const { doc } = Astro.props;
|
||||
const pluginName = doc.data.pluginName;
|
||||
|
||||
if (!doc.body) {
|
||||
throw new Error(`No content found for plugin: ${pluginName}`);
|
||||
}
|
||||
const {Content , headings} = await render(doc)
|
||||
---
|
||||
|
||||
<StarlightPage
|
||||
frontmatter={{
|
||||
title: `@nx/${pluginName} - generators`,
|
||||
description: `@nx/${pluginName} plugin generators`,
|
||||
}}
|
||||
headings={headings || []}
|
||||
>
|
||||
<Content />
|
||||
</StarlightPage>
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
import { getCollection, render } from 'astro:content';
|
||||
import { CardGrid, LinkCard } from '@astrojs/starlight/components';
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const pluginDocs = await getCollection('plugin-docs');
|
||||
|
||||
// Group docs by plugin name
|
||||
const docsByPlugin = pluginDocs.reduce(
|
||||
(acc, doc) => {
|
||||
const pluginName = doc.data.pluginName;
|
||||
if (!acc[pluginName]) {
|
||||
acc[pluginName] = [];
|
||||
}
|
||||
acc[pluginName].push(doc);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, CollectionEntry<'plugin-docs'>[]>
|
||||
);
|
||||
|
||||
// Generate paths for each plugin
|
||||
return Object.entries(docsByPlugin).map(([pluginName, docs]) => ({
|
||||
params: { plugin: pluginName },
|
||||
props: {
|
||||
pluginName,
|
||||
docs: docs.sort((a, b) => {
|
||||
const order = ['generators', 'executors', 'migrations'];
|
||||
return order.indexOf(a.data.docType) - order.indexOf(b.data.docType);
|
||||
})
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
const { pluginName, docs } = Astro.props;
|
||||
---
|
||||
|
||||
<StarlightPage
|
||||
frontmatter={{
|
||||
title: `@nx/${pluginName}`,
|
||||
description: `Complete reference for @nx/${pluginName} plugin`
|
||||
}}
|
||||
>
|
||||
|
||||
<CardGrid>
|
||||
|
||||
{
|
||||
docs.map(({ data }) => (
|
||||
<LinkCard title={data.docType.charAt(0).toUpperCase() + data.docType.slice(1)}
|
||||
href={`./${pluginName}/${data.docType}`}>
|
||||
</LinkCard>
|
||||
))
|
||||
}
|
||||
|
||||
</CardGrid>
|
||||
</StarlightPage>
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
import { getCollection, render } from 'astro:content';
|
||||
|
||||
import { parseMarkdownToHtml } from '../../../../utils/markdown';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const pluginDocs = await getCollection(
|
||||
'plugin-docs',
|
||||
(entry) => entry.data.docType === 'migrations'
|
||||
);
|
||||
|
||||
// Generate paths for each plugin
|
||||
return pluginDocs.map((p) => ({
|
||||
params: { plugin: p.data.pluginName },
|
||||
props: { doc: p },
|
||||
}));
|
||||
}
|
||||
|
||||
const { doc } = Astro.props;
|
||||
const pluginName = doc.data.pluginName;
|
||||
|
||||
if (!doc.body) {
|
||||
throw new Error(`No content found for plugin: ${pluginName}`);
|
||||
}
|
||||
const {Content, headings } = await render(doc);
|
||||
---
|
||||
|
||||
<StarlightPage
|
||||
frontmatter={{
|
||||
title: `@nx/${pluginName} - migrations`,
|
||||
description: `@nx/${pluginName} plugin migrations`,
|
||||
}}
|
||||
headings={headings || []}
|
||||
>
|
||||
<Content />
|
||||
</StarlightPage>
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
import { getCollection, render } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const docs = await getCollection(
|
||||
'devkit-docs',
|
||||
// we don't want the overview page, this is custom handled via the index.astro page
|
||||
(doc) => doc.id !== 'devkit-overview' && doc.data.docType === 'devkit'
|
||||
);
|
||||
|
||||
|
||||
return docs.map(doc => ({
|
||||
params: { name: doc.id.split('devkit_')[1] },
|
||||
props: {
|
||||
doc
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
const { doc } = Astro.props;
|
||||
const { name } = Astro.params;
|
||||
if (!doc) {
|
||||
throw new Error(`DevKit doc not found, ${name}`);
|
||||
}
|
||||
|
||||
const { Content, headings } = await render(doc);
|
||||
---
|
||||
|
||||
<StarlightPage
|
||||
frontmatter={{
|
||||
title: doc.data.title
|
||||
}}
|
||||
headings={headings || []}
|
||||
>
|
||||
<Content />
|
||||
</StarlightPage>
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
import { render, getEntry } from 'astro:content';
|
||||
|
||||
const devkitDocs = await getEntry('devkit-docs', 'devkit-overview');
|
||||
|
||||
if (!devkitDocs) {
|
||||
throw new Error('DevKit documentation not found');
|
||||
}
|
||||
|
||||
const { headings, Content } = await render(devkitDocs);
|
||||
---
|
||||
|
||||
<StarlightPage
|
||||
frontmatter={{
|
||||
title: 'DevKit Documentation',
|
||||
description: 'Documentation for the DevKit'
|
||||
}}
|
||||
headings={headings || []}
|
||||
>
|
||||
<Content />
|
||||
</StarlightPage>
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
import { getCollection, render } from 'astro:content';
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const docs = await getCollection(
|
||||
'devkit-docs',
|
||||
// we don't want the overview page, this is custom handled via the index.astro page
|
||||
(doc) => doc.id !== 'ngcli_adapter-overview' && doc.data.docType === 'ngcli_adapter'
|
||||
);
|
||||
|
||||
|
||||
return docs.map(doc => ({
|
||||
params: { name: doc.id.split('ngcli_adapter_')[1] },
|
||||
props: {
|
||||
doc
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
const { doc, name } = Astro.props;
|
||||
if (!doc) {
|
||||
throw new Error(`ngcli_adapter doc not found. ${name}`);
|
||||
}
|
||||
|
||||
const { Content, headings } = await render(doc);
|
||||
---
|
||||
|
||||
<StarlightPage
|
||||
frontmatter={{
|
||||
title: doc.data.title
|
||||
}}
|
||||
headings={headings || []}
|
||||
>
|
||||
<Content />
|
||||
</StarlightPage>
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
import { getEntry, render, getCollection } from 'astro:content';
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
|
||||
const doc = await getEntry('devkit-docs', 'ngcli_adapter-overview');
|
||||
|
||||
if (!doc) {
|
||||
throw new Error('angular cli adapter overview doc not found');
|
||||
}
|
||||
|
||||
const { Content, headings } = await render(doc);
|
||||
---
|
||||
|
||||
<StarlightPage
|
||||
frontmatter={{
|
||||
title: doc.data.title
|
||||
}}
|
||||
headings={headings || []}
|
||||
>
|
||||
<Content />
|
||||
</StarlightPage>
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
|
||||
import { Card, CardGrid, Badge } from '@astrojs/starlight/components';
|
||||
|
||||
// Get all plugin documentation entries
|
||||
const pluginDocs = await getCollection('plugin-docs');
|
||||
|
||||
// Group plugins by plugin name and collect their available doc types
|
||||
const pluginGroups = new Map();
|
||||
|
||||
for (const doc of pluginDocs) {
|
||||
const { pluginName, packageName, docType, description } = doc.data;
|
||||
|
||||
if (!pluginGroups.has(pluginName)) {
|
||||
pluginGroups.set(pluginName, {
|
||||
name: pluginName,
|
||||
packageName,
|
||||
description,
|
||||
generators: false,
|
||||
executors: false,
|
||||
migrations: false,
|
||||
totalDocs: 0
|
||||
});
|
||||
}
|
||||
|
||||
const plugin = pluginGroups.get(pluginName);
|
||||
plugin[docType] = true;
|
||||
plugin.totalDocs++;
|
||||
// Use the first description we find (they should all be the same for the same plugin)
|
||||
if (!plugin.description && description) {
|
||||
plugin.description = description;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to array and sort alphabetically
|
||||
const plugins = Array.from(pluginGroups.values()).sort((a, b) => a.name.localeCompare(b.name));
|
||||
---
|
||||
|
||||
<StarlightPage frontmatter={{
|
||||
title: 'Nx Plugins',
|
||||
description: 'Discover all available Nx plugins to enhance your development workflow. Each plugin provides generators, executors, and migrations to help you build modern applications.'
|
||||
}}
|
||||
>
|
||||
|
||||
<CardGrid stagger={true}>
|
||||
{plugins.map(plugin => (
|
||||
<Card title={plugin.name} icon="open-book">
|
||||
<div class="plugin-description">
|
||||
The Nx Plugin for {plugin.name}
|
||||
</div>
|
||||
|
||||
<div class="plugin-features">
|
||||
{plugin.generators && <a href={`/api/plugins/${plugin.name}/generators`}>
|
||||
<Badge variant="default" text="Generators" size="small" />
|
||||
</a>}
|
||||
|
||||
{plugin.executors && <a href={`/api/plugins/${plugin.name}/executors`}>
|
||||
<Badge variant="default" text="Executors" size="small" />
|
||||
</a>}
|
||||
|
||||
{plugin.migrations && <a href={`/api/plugins/${plugin.name}/migrations`}>
|
||||
<Badge variant="default" text="Migrations" size="small" />
|
||||
</a>}
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
))}
|
||||
</CardGrid>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
TODO: add community plugins
|
||||
|
||||
|
||||
</StarlightPage>
|
||||
|
||||
<style>
|
||||
|
||||
.plugin-features {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
const webinars = await getCollection('notifications', item => item.data.type === 'webinar');
|
||||
|
||||
webinars.sort((a, b) => {
|
||||
return new Date(b.data.date).getTime() - new Date(a.data.date).getTime();
|
||||
});
|
||||
---
|
||||
<pre>
|
||||
{JSON.stringify(webinars, null, 2)}
|
||||
</pre>
|
||||
@@ -0,0 +1,203 @@
|
||||
import type { StarlightPlugin } from '@astrojs/starlight/types';
|
||||
import { readdirSync, existsSync, readFileSync } from 'fs';
|
||||
import { join, basename, extname } from 'path';
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
|
||||
function getStaticPluginFiles(
|
||||
pluginDir: string,
|
||||
workspaceRoot: string
|
||||
): Array<{ label: string; slug: string }> {
|
||||
const staticFiles: Array<{ label: string; slug: string }> = [];
|
||||
const pluginContentDir = join(
|
||||
workspaceRoot,
|
||||
'astro-docs',
|
||||
'src',
|
||||
'content',
|
||||
'docs',
|
||||
'api',
|
||||
'plugins',
|
||||
pluginDir
|
||||
);
|
||||
|
||||
if (!existsSync(pluginContentDir)) {
|
||||
return staticFiles;
|
||||
}
|
||||
|
||||
try {
|
||||
const files = readdirSync(pluginContentDir, { withFileTypes: true });
|
||||
|
||||
for (const file of files) {
|
||||
if (
|
||||
file.isFile() &&
|
||||
(file.name.endsWith('.md') || file.name.endsWith('.mdx'))
|
||||
) {
|
||||
const fileName = basename(file.name, extname(file.name));
|
||||
const label = fileName
|
||||
.split('-')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
|
||||
staticFiles.push({
|
||||
label,
|
||||
slug: `api/plugins/${pluginDir}/${fileName}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore errors when reading static files
|
||||
}
|
||||
|
||||
return staticFiles;
|
||||
}
|
||||
|
||||
export function autoPluginSidebarPlugin(): StarlightPlugin {
|
||||
return {
|
||||
name: 'auto-plugin-sidebar',
|
||||
hooks: {
|
||||
setup({ config, logger, updateConfig }) {
|
||||
logger.info('Setting up auto-plugin-sidebar...');
|
||||
|
||||
// Get all directories in packages (look in parent directory for actual Nx packages)
|
||||
const packagesDir = join(workspaceRoot, 'packages');
|
||||
|
||||
if (!existsSync(packagesDir)) {
|
||||
throw new Error('Packages directory does not exist: ' + packagesDir);
|
||||
}
|
||||
|
||||
const pluginItems: Array<{
|
||||
label: string;
|
||||
link?: string;
|
||||
items?: any[];
|
||||
collapsed?: boolean;
|
||||
}> = [];
|
||||
|
||||
try {
|
||||
const directories = readdirSync(packagesDir, { withFileTypes: true })
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
.map((dirent) => dirent.name);
|
||||
|
||||
for (const dir of directories) {
|
||||
const packageJsonPath = join(packagesDir, dir, 'package.json');
|
||||
|
||||
if (!existsSync(packageJsonPath)) {
|
||||
logger.warn(
|
||||
`Skipping ${dir} - package.json not found in ${packagesDir}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const packageJson = JSON.parse(
|
||||
readFileSync(packageJsonPath, 'utf-8')
|
||||
);
|
||||
const packageName = packageJson.name || `@nx/${dir}`;
|
||||
|
||||
// Only include @nx scoped packages
|
||||
if (!packageName.startsWith('@nx/')) {
|
||||
logger.warn(
|
||||
`Skipping ${dir} - package name does not start with @nx/`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (packageName === '@nx/devkit') {
|
||||
// devkit is special handled
|
||||
continue;
|
||||
}
|
||||
|
||||
const item = generatePluginSideBarConfig(
|
||||
packagesDir,
|
||||
dir,
|
||||
packageName
|
||||
);
|
||||
pluginItems.push(item);
|
||||
}
|
||||
|
||||
// Sort plugin items alphabetically
|
||||
pluginItems.sort((a, b) => a.label.localeCompare(b.label));
|
||||
|
||||
logger.info(`Found ${pluginItems.length} plugins`);
|
||||
|
||||
// Find the API Reference section in the sidebar
|
||||
const sidebar = config.sidebar || [];
|
||||
const apiRefIndex = sidebar.findIndex(
|
||||
(item) =>
|
||||
typeof item === 'object' &&
|
||||
'label' in item &&
|
||||
item.label === 'References'
|
||||
);
|
||||
|
||||
if (apiRefIndex !== -1) {
|
||||
const apiRefSection = sidebar[apiRefIndex] as any;
|
||||
|
||||
if (apiRefSection.items) {
|
||||
apiRefSection.items.push(...pluginItems);
|
||||
|
||||
updateConfig({ sidebar });
|
||||
logger.info(
|
||||
`Successfully added ${pluginItems.length} plugins directly to API Reference`
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error generating plugin sidebar: ${error}`);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function generatePluginSideBarConfig(
|
||||
packagesDir: string,
|
||||
dir: string,
|
||||
packageName: string
|
||||
) {
|
||||
const pluginDir = join(packagesDir, dir);
|
||||
|
||||
// Check for generators.json, executors.json, and migrations.json
|
||||
const hasGenerators = existsSync(join(pluginDir, 'generators.json'));
|
||||
const hasExecutors = existsSync(join(pluginDir, 'executors.json'));
|
||||
const hasMigrations = existsSync(join(pluginDir, 'migrations.json'));
|
||||
|
||||
// Create sub-items for generated docs and static files
|
||||
const subItems: any[] = [];
|
||||
|
||||
if (hasGenerators) {
|
||||
subItems.push({
|
||||
label: 'Generators',
|
||||
link: `/api/plugins/${dir}/generators`,
|
||||
});
|
||||
}
|
||||
|
||||
if (hasExecutors) {
|
||||
subItems.push({
|
||||
label: 'Executors',
|
||||
link: `/api/plugins/${dir}/executors`,
|
||||
});
|
||||
}
|
||||
|
||||
if (hasMigrations) {
|
||||
subItems.push({
|
||||
label: 'Migrations',
|
||||
link: `/api/plugins/${dir}/migrations`,
|
||||
});
|
||||
}
|
||||
|
||||
// Add static markdown files from content/docs/api/plugins/<plugin>/
|
||||
const staticFiles = getStaticPluginFiles(dir, workspaceRoot);
|
||||
subItems.push(...staticFiles);
|
||||
|
||||
if (subItems.length > 0) {
|
||||
return {
|
||||
// make the names pretty for the sidebar
|
||||
label: packageName.replace('@nx/', ''),
|
||||
items: subItems,
|
||||
collapsed: true,
|
||||
};
|
||||
}
|
||||
// If no sub-items, link directly to the plugin overview
|
||||
return {
|
||||
// make the names pretty for the sidebar
|
||||
label: packageName.replace('@nx/', ''),
|
||||
link: `/api/plugins/${dir}`,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { defineRouteMiddleware } from '@astrojs/starlight/route-data';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
export const onRequest = defineRouteMiddleware(async (context) => {
|
||||
const { entry } = context.locals.starlightRoute;
|
||||
|
||||
if (entry.data.banner) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bannerContent = await getCollection('notifications');
|
||||
|
||||
if (bannerContent.length) {
|
||||
bannerContent.sort((a, b) =>
|
||||
new Date(b.data.date) > new Date(a.data.date) ? -1 : 1
|
||||
);
|
||||
|
||||
const nextEventToOccur = bannerContent.at(-1);
|
||||
if (!nextEventToOccur || !nextEventToOccur.data.title) {
|
||||
return;
|
||||
}
|
||||
|
||||
const title = nextEventToOccur.data.title.split(':')[0];
|
||||
// TODO(caleb): based on the 'type' we will render different markup for the banners
|
||||
// i.e. type=webinar => "Register for the {title} webinar"
|
||||
// type=event => "Join us for {title} on {date}"
|
||||
|
||||
entry.data.banner = {
|
||||
content: `<a href="${nextEventToOccur.data.registrationUrl}" title="${nextEventToOccur.data.title}">${title}</a>`,
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,221 @@
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
import { join, relative } from 'path';
|
||||
import { fork } from 'child_process';
|
||||
import type { ParsedCommand } from './utils/nx-command-parser';
|
||||
import type { Loader, LoaderContext } from 'astro/loaders';
|
||||
import { watchAndCall } from './utils/watch.ts';
|
||||
|
||||
function generateCLIMarkdown(commands: Record<string, ParsedCommand>): string {
|
||||
const commandNames = Object.keys(commands).sort();
|
||||
|
||||
const content = `
|
||||
The Nx command line has various subcommands and options to help you manage your Nx workspace and run tasks efficiently.
|
||||
Below is a complete reference for all available commands and their options.
|
||||
You can run nx --help to view all available options.
|
||||
|
||||
## Available Commands
|
||||
|
||||
${commandNames
|
||||
.map((cmdName) => {
|
||||
const cmd = commands[cmdName];
|
||||
let section = `### \`nx ${cmdName}\`\n`;
|
||||
|
||||
section += cmd.description || 'No description available';
|
||||
|
||||
if (cmd.aliases && cmd.aliases.length > 0) {
|
||||
section += `**Aliases:** ${cmd.aliases
|
||||
.map((alias) => `\`${alias}\``)
|
||||
.join(', ')}`;
|
||||
}
|
||||
|
||||
section += `\n\n**Usage:**
|
||||
\`\`\`bash
|
||||
nx ${cmd.command || cmdName}
|
||||
\`\`\`
|
||||
`;
|
||||
|
||||
// Add options table if there are options
|
||||
if (cmd.options && cmd.options.length > 0) {
|
||||
section += '\n#### Options\n\n';
|
||||
section += '| Option | Type | Description | Default |\n';
|
||||
section += '|--------|------|-------------|---------|\n';
|
||||
|
||||
const sortedOptions = cmd.options.sort((a, b) =>
|
||||
a.name[0].localeCompare(b.name[0])
|
||||
);
|
||||
|
||||
for (const option of sortedOptions) {
|
||||
const optionNames = option.name.map((n) => `\`--${n}\``).join(', ');
|
||||
let description = option.description || '';
|
||||
|
||||
// Add alias information
|
||||
if (option.name.length > 1) {
|
||||
const aliases = option.name
|
||||
.slice(1)
|
||||
.map((a) => `\`-${a}\``)
|
||||
.join(', ');
|
||||
description += ` (alias: ${aliases})`;
|
||||
}
|
||||
|
||||
// Add deprecation warning
|
||||
if (option.deprecated) {
|
||||
description += ` **⚠️ Deprecated**${
|
||||
option.deprecated !== true ? `: ${option.deprecated}` : ''
|
||||
}`;
|
||||
}
|
||||
|
||||
// Add choices if available
|
||||
if (option.choices && option.choices.length > 0) {
|
||||
description += ` (choices: ${option.choices
|
||||
.map((c) => `\`${c}\``)
|
||||
.join(', ')})`;
|
||||
}
|
||||
|
||||
const defaultValue =
|
||||
option.default !== undefined
|
||||
? `\`${JSON.stringify(option.default).replace(/"/g, '')}\``
|
||||
: '';
|
||||
|
||||
section += `| ${optionNames} | ${option.type} | ${
|
||||
description || '_No Description_'
|
||||
} | ${defaultValue} |\n`;
|
||||
}
|
||||
|
||||
section += '\n';
|
||||
}
|
||||
|
||||
return section;
|
||||
})
|
||||
.join('\n')}
|
||||
|
||||
## Getting Help
|
||||
|
||||
You can get help for any command by adding the \`--help\` flag:
|
||||
|
||||
\`\`\`bash
|
||||
nx <command> --help
|
||||
\`\`\`
|
||||
`;
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
export interface CliDocEntry {
|
||||
id: string;
|
||||
body: string;
|
||||
rendered?: any;
|
||||
filePath: string;
|
||||
data: {
|
||||
title: string;
|
||||
docType: 'cli';
|
||||
content: string;
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateNxCliDocs(
|
||||
logger: LoaderContext['logger'],
|
||||
watcher?: LoaderContext['watcher']
|
||||
): Promise<CliDocEntry> {
|
||||
logger.info('🔍 Generating Nx CLI documentation...');
|
||||
|
||||
try {
|
||||
// Run the CLI parser in a subprocess to avoid ESM/CJS issues
|
||||
const subprocessPath = join(
|
||||
workspaceRoot,
|
||||
'astro-docs/src/plugins/utils/cli-subprocess.cjs'
|
||||
);
|
||||
|
||||
// Run subprocess and get results
|
||||
const result = await new Promise<{
|
||||
commands: Record<string, ParsedCommand>;
|
||||
}>((resolve, reject) => {
|
||||
const child = fork(subprocessPath, [], {
|
||||
cwd: workspaceRoot,
|
||||
silent: true,
|
||||
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
||||
});
|
||||
|
||||
child.on('message', (message: any) => {
|
||||
if (message.type === 'result') {
|
||||
resolve(message.data);
|
||||
} else if (message.type === 'error') {
|
||||
reject(new Error(message.error));
|
||||
}
|
||||
child.send({ type: 'stop' });
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
child.on('exit', (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(`CLI subprocess exited with code ${code}`));
|
||||
}
|
||||
});
|
||||
|
||||
child.send({ type: 'start' });
|
||||
});
|
||||
|
||||
const markdown = generateCLIMarkdown(result.commands);
|
||||
|
||||
logger.info(
|
||||
`✅ Generated CLI documentation with ${
|
||||
Object.keys(result.commands).length
|
||||
} commands`
|
||||
);
|
||||
|
||||
return {
|
||||
id: 'api/nx-cli',
|
||||
body: markdown,
|
||||
filePath: relative(
|
||||
join(workspaceRoot, 'astro-docs'),
|
||||
join(workspaceRoot, 'packages/nx/src/command-line/nx-commands.ts')
|
||||
),
|
||||
data: {
|
||||
title: 'Nx CLI Reference',
|
||||
docType: 'cli',
|
||||
content: markdown,
|
||||
},
|
||||
};
|
||||
} catch (error: any) {
|
||||
logger.error('❌ Failed to generate CLI docs:');
|
||||
logger.error(error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function CliLoader(options: any = {}): Loader {
|
||||
return {
|
||||
name: 'nx-cli-loader',
|
||||
async load({ store, logger, watcher, renderMarkdown }: LoaderContext) {
|
||||
const generate = async () => {
|
||||
const doc = await generateNxCliDocs(logger, watcher);
|
||||
logger.info('Loaded CLI documentation');
|
||||
|
||||
store.clear();
|
||||
|
||||
if (doc.body) {
|
||||
doc.rendered = await renderMarkdown(doc.body);
|
||||
}
|
||||
|
||||
logger.info(`Processing CLI documentation`);
|
||||
store.set(doc);
|
||||
|
||||
logger.info('Generated CLI documentation');
|
||||
};
|
||||
|
||||
const pathsToWatch = [
|
||||
join(import.meta.dirname, 'cli.loader.ts'),
|
||||
join(import.meta.dirname, 'utils/cli-subprocess.cjs'),
|
||||
join(import.meta.dirname, 'utils/typedoc'),
|
||||
];
|
||||
|
||||
if (watcher) {
|
||||
watchAndCall(watcher, pathsToWatch, generate);
|
||||
}
|
||||
|
||||
await generate();
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
import { join, relative } from 'path';
|
||||
import { fork } from 'child_process';
|
||||
import type { Loader, LoaderContext } from 'astro/loaders';
|
||||
import type { CollectionEntry, RenderedContent } from 'astro:content';
|
||||
import { watchAndCall } from './utils/watch.ts';
|
||||
|
||||
type DocEntry = CollectionEntry<'cnw-docs'>;
|
||||
|
||||
interface ParsedCommandOption {
|
||||
name: string[];
|
||||
type: string;
|
||||
description: string;
|
||||
default: any;
|
||||
deprecated: boolean | string;
|
||||
hidden: boolean;
|
||||
choices?: string[];
|
||||
}
|
||||
|
||||
interface ParsedCommand {
|
||||
name: string;
|
||||
commandString: string;
|
||||
description: string;
|
||||
options?: ParsedCommandOption[];
|
||||
}
|
||||
|
||||
function generateCNWMarkdown(
|
||||
command: ParsedCommand,
|
||||
presets: string[],
|
||||
presetDescriptions: Record<string, string>
|
||||
): string {
|
||||
let content = `
|
||||
${command.description}
|
||||
|
||||
## Usage
|
||||
|
||||
\`\`\`bash
|
||||
npx ${command.commandString}
|
||||
\`\`\`
|
||||
|
||||
`;
|
||||
|
||||
// Add options table
|
||||
if (command.options && command.options.length > 0) {
|
||||
content += '## Options\n\n';
|
||||
content += '| Option | Type | Description |\n';
|
||||
content += '| ----------- | ----------- | ---------- |\n';
|
||||
|
||||
const sortedOptions = command.options
|
||||
.filter((option) => !option.hidden)
|
||||
.sort((a, b) => a.name[0].localeCompare(b.name[0]));
|
||||
|
||||
for (const option of sortedOptions) {
|
||||
const optionNames = option.name
|
||||
.map((n, index) => {
|
||||
if (index === 0) {
|
||||
return `\`--${n}\``;
|
||||
} else {
|
||||
// Aliases should be formatted with single dash for short options
|
||||
return n.length === 1 ? `\`--${n}\`` : `\`--${n}\``;
|
||||
}
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
let description = option.description || '';
|
||||
|
||||
// Add deprecation warning
|
||||
if (option.deprecated) {
|
||||
description += ` **⚠️ Deprecated**${
|
||||
option.deprecated !== true ? `: ${option.deprecated}` : ''
|
||||
}`;
|
||||
}
|
||||
|
||||
// Add choices if available
|
||||
let type = option.type;
|
||||
if (option.choices && option.choices.length > 0) {
|
||||
type = option.choices.map((c) => `\`${c}\``).join(', ');
|
||||
}
|
||||
|
||||
// Add default value
|
||||
if (option.default !== undefined) {
|
||||
description += ` (Default: \`${JSON.stringify(option.default).replace(
|
||||
/"/g,
|
||||
''
|
||||
)}\`)`;
|
||||
}
|
||||
|
||||
content += `| ${optionNames} | ${type} | ${
|
||||
description || '_No Description_'
|
||||
} |\n`;
|
||||
}
|
||||
|
||||
content += '\n';
|
||||
}
|
||||
|
||||
// Add presets table
|
||||
content += '## Presets\n\n';
|
||||
content += '| Preset | Description |\n';
|
||||
content += '| ---------- | ----------|\n';
|
||||
|
||||
const sortedPresets = presets.sort();
|
||||
for (const preset of sortedPresets) {
|
||||
const description =
|
||||
presetDescriptions[preset] || 'No description available';
|
||||
content += `| ${preset} | ${description} |\n`;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
export function CnwLoader(): Loader {
|
||||
return {
|
||||
name: 'cnw-loader',
|
||||
load: async ({ store, logger, watcher, renderMarkdown }) => {
|
||||
// Function to regenerate CNW docs
|
||||
const generate = async () => {
|
||||
store.clear();
|
||||
// @ts-expect-error - astro:content types seem to always be out of sync w/ generated types
|
||||
const entry = await generateCnwDocs(logger, renderMarkdown);
|
||||
store.set(entry);
|
||||
logger.info(`Generated CNW documentation`);
|
||||
};
|
||||
|
||||
if (watcher) {
|
||||
const pathsToWatch = [
|
||||
join(import.meta.dirname, 'cnw.loader.ts'),
|
||||
join(import.meta.dirname, 'utils', 'cnw-subprocess.cjs'),
|
||||
];
|
||||
|
||||
watchAndCall(watcher, pathsToWatch, generate);
|
||||
}
|
||||
await generate();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function generateCnwDocs(
|
||||
logger: LoaderContext['logger'],
|
||||
renderMarkdown: (content: string) => Promise<RenderedContent>
|
||||
): Promise<DocEntry> {
|
||||
logger.info('🔍 Generating Create Nx Workspace documentation...');
|
||||
|
||||
try {
|
||||
// Run the CNW parser in a subprocess to avoid ESM/CJS issues
|
||||
const subprocessPath = join(
|
||||
workspaceRoot,
|
||||
'astro-docs/src/plugins/utils/cnw-subprocess.cjs'
|
||||
);
|
||||
|
||||
// Run subprocess and get results
|
||||
const result = await new Promise<{
|
||||
command: ParsedCommand;
|
||||
presets: string[];
|
||||
presetDescriptions: Record<string, string>;
|
||||
}>((resolve, reject) => {
|
||||
const child = fork(subprocessPath, [], {
|
||||
cwd: workspaceRoot,
|
||||
silent: true,
|
||||
});
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
child.stdout?.on('data', (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
child.stderr?.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
logger.warn(data.toString());
|
||||
});
|
||||
|
||||
child.on('message', (message: any) => {
|
||||
if (message.type === 'result') {
|
||||
resolve(message.data);
|
||||
} else if (message.type === 'error') {
|
||||
reject(new Error(message.error));
|
||||
}
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
child.on('exit', (code) => {
|
||||
if (code !== 0) {
|
||||
reject(
|
||||
new Error(
|
||||
`CNW subprocess exited with code ${code}\nstderr: ${stderr}`
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const markdown = generateCNWMarkdown(
|
||||
result.command,
|
||||
result.presets,
|
||||
result.presetDescriptions
|
||||
);
|
||||
|
||||
logger.info('✅ Generated Create Nx Workspace documentation');
|
||||
|
||||
const entry: DocEntry = {
|
||||
id: 'cnw-cli',
|
||||
body: markdown,
|
||||
filePath: relative(
|
||||
join(workspaceRoot, 'astro-docs'),
|
||||
join(
|
||||
workspaceRoot,
|
||||
'packages/create-nx-workspace/bin/create-nx-workspace.ts'
|
||||
)
|
||||
),
|
||||
data: {
|
||||
title: 'create-nx-workspace',
|
||||
docType: 'cnw',
|
||||
},
|
||||
rendered: await renderMarkdown(markdown),
|
||||
collection: 'cnw-docs',
|
||||
};
|
||||
|
||||
return entry;
|
||||
} catch (error: any) {
|
||||
logger.error('❌ Failed to generate CNW docs:');
|
||||
logger.error(error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,525 @@
|
||||
import * as typedoc from 'typedoc';
|
||||
import { join } from 'path';
|
||||
import {
|
||||
cpSync,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import type { CollectionEntry, RenderedContent } from 'astro:content';
|
||||
import type { LoaderContext } from 'astro/loaders';
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
import NxMarkdownTheme from './utils/typedoc/theme.ts';
|
||||
import { watchAndCall } from './utils/watch.ts';
|
||||
|
||||
type DocEntry = CollectionEntry<'devkit-docs'>;
|
||||
|
||||
const categoryMap: Record<number, string> = {
|
||||
[typedoc.ReflectionKind.Class]: 'Classes',
|
||||
[typedoc.ReflectionKind.Enum]: 'Enumerations',
|
||||
[typedoc.ReflectionKind.Function]: 'Functions',
|
||||
[typedoc.ReflectionKind.Interface]: 'Interfaces',
|
||||
[typedoc.ReflectionKind.TypeAlias]: 'Type Aliases',
|
||||
[typedoc.ReflectionKind.Variable]: 'Variables',
|
||||
};
|
||||
|
||||
const allowedReflections = [
|
||||
typedoc.ReflectionKind.Class,
|
||||
typedoc.ReflectionKind.Enum,
|
||||
typedoc.ReflectionKind.Function,
|
||||
typedoc.ReflectionKind.Interface,
|
||||
typedoc.ReflectionKind.TypeAlias,
|
||||
typedoc.ReflectionKind.Variable,
|
||||
];
|
||||
|
||||
export function DevkitLoader() {
|
||||
return {
|
||||
name: 'nx-devkit-loader',
|
||||
async load({ store, logger, watcher, renderMarkdown }: LoaderContext) {
|
||||
const generate = async () => {
|
||||
logger.info('Generating DevKit documentation');
|
||||
store.clear();
|
||||
|
||||
const { defaultTypedocOptions, outDir, buildDir, projectRoot } =
|
||||
setupTypeDoc(logger);
|
||||
|
||||
const devkitEntryPoint = join(
|
||||
workspaceRoot,
|
||||
'dist',
|
||||
'packages',
|
||||
'devkit',
|
||||
'index.d.ts'
|
||||
);
|
||||
if (!existsSync(devkitEntryPoint)) {
|
||||
logger.warn(
|
||||
'dist/packages/devkit/index.d.ts not found, please run build first'
|
||||
);
|
||||
throw new Error(
|
||||
`dist/packages/devkit/index.d.ts not found, unable to generate docs. Make sure to run devkit build first. ${devkitEntryPoint}`
|
||||
);
|
||||
}
|
||||
|
||||
const devkitDocs = await generateDocsForEntry(
|
||||
{
|
||||
...defaultTypedocOptions,
|
||||
entryPoints: [devkitEntryPoint],
|
||||
tsconfig: join(buildDir, 'tsconfig.lib.json'),
|
||||
out: outDir,
|
||||
excludePrivate: true,
|
||||
publicPath: '/reference/core-api/devkit/',
|
||||
},
|
||||
// @ts-expect-error typemismatch bc auto generated types in .astro vs astro:content etc
|
||||
renderMarkdown,
|
||||
'devkit'
|
||||
);
|
||||
|
||||
devkitDocs.forEach(store.set);
|
||||
|
||||
const devkitOverviewPage = await createOverview(
|
||||
devkitDocs,
|
||||
// @ts-expect-error typemismatch bc auto generated types in .astro vs astro:content etc
|
||||
renderMarkdown,
|
||||
'devkit'
|
||||
);
|
||||
store.set(devkitOverviewPage);
|
||||
|
||||
const ngcliEntryPoint = join(
|
||||
workspaceRoot,
|
||||
'dist',
|
||||
'packages',
|
||||
'devkit',
|
||||
'ngcli-adapter.d.ts'
|
||||
);
|
||||
|
||||
if (!existsSync(ngcliEntryPoint)) {
|
||||
logger.warn(
|
||||
'dist/packages/devkit/ngcli-adapter.d.ts not found, skipping ngcli_adapter documentation generation'
|
||||
);
|
||||
throw new Error(
|
||||
`dist/packages/devkit/ngcli-adapter.d.ts not found, unable to generate docs. Make sure to run devkit build first. ${ngcliEntryPoint}`
|
||||
);
|
||||
}
|
||||
|
||||
const ngcliDocs = await generateDocsForEntry(
|
||||
{
|
||||
...defaultTypedocOptions,
|
||||
entryPoints: [ngcliEntryPoint],
|
||||
tsconfig: join(buildDir, 'tsconfig.lib.json'),
|
||||
out: join(outDir, 'ngcli_adapter'),
|
||||
publicPath: '/reference/core-api/devkit/ngcli_adapter/',
|
||||
},
|
||||
// @ts-expect-error typemismatch bc auto generated types in .astro vs astro:content etc
|
||||
renderMarkdown,
|
||||
'ngcli_adapter'
|
||||
);
|
||||
ngcliDocs.forEach(store.set);
|
||||
|
||||
const ngcliOverviewPage = await createOverview(
|
||||
ngcliDocs,
|
||||
// @ts-expect-error typemismatch bc auto generated types in .astro vs astro:content etc
|
||||
renderMarkdown,
|
||||
'ngcli_adapter'
|
||||
);
|
||||
|
||||
store.set(ngcliOverviewPage);
|
||||
|
||||
logger.info('DevKit documentation generated successfully');
|
||||
};
|
||||
|
||||
if (watcher) {
|
||||
// Watch for changes in the devkit loader files themselves
|
||||
const pathsToWatch = [
|
||||
join(import.meta.dirname, 'devkit.loader.ts'),
|
||||
join(import.meta.dirname, 'utils', 'typedoc'),
|
||||
];
|
||||
watchAndCall(watcher, pathsToWatch, generate);
|
||||
}
|
||||
|
||||
await generate();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function createOverview(
|
||||
records: DocEntry[],
|
||||
renderMarkdown: (content: string) => Promise<RenderedContent>,
|
||||
baseSlug: DocEntry['data']['docType']
|
||||
): Promise<DocEntry> {
|
||||
const record: DocEntry = {
|
||||
id: `${baseSlug}-overview`,
|
||||
body: '',
|
||||
collection: 'devkit-docs',
|
||||
data: {
|
||||
kind: 'Project',
|
||||
category: 'Project',
|
||||
docType: baseSlug,
|
||||
title: `${baseSlug} Overview`,
|
||||
description: `${baseSlug} description`,
|
||||
},
|
||||
};
|
||||
|
||||
// alphabetize categories
|
||||
const categories = Object.values(categoryMap).toSorted((a, b) =>
|
||||
a.localeCompare(b)
|
||||
);
|
||||
|
||||
const baseUrl =
|
||||
`/api/plugins/devkit` +
|
||||
(baseSlug === 'ngcli_adapter' ? `/${baseSlug}` : '');
|
||||
|
||||
for (const name of categories) {
|
||||
const ofKind = records
|
||||
.filter((r) => r.data.category === name)
|
||||
.toSorted((a, b) => a.data.title.localeCompare(b.data.title));
|
||||
|
||||
if (ofKind.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
record.body += `\n## ${name}\n\n`;
|
||||
record.body +=
|
||||
`- ` +
|
||||
ofKind
|
||||
.map(
|
||||
(r) =>
|
||||
`[${r.data.title}](${baseUrl}/${r.data.title
|
||||
.toLowerCase()
|
||||
.replaceAll(' ', '')})`
|
||||
)
|
||||
.join('\n- ');
|
||||
}
|
||||
|
||||
record.rendered = await renderMarkdown(record.body!);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
async function generateDocsForEntry(
|
||||
options: Partial<typedoc.TypeDocOptions> & { [key: string]: unknown },
|
||||
renderMarkdown: (content: string) => Promise<RenderedContent>,
|
||||
baseSlug: DocEntry['data']['docType']
|
||||
) {
|
||||
const records: DocEntry[] = [];
|
||||
const app = await typedoc.Application.bootstrapWithPlugins(
|
||||
options as Partial<typedoc.TypeDocOptions>,
|
||||
[
|
||||
new typedoc.TypeDocReader(),
|
||||
new typedoc.PackageJsonReader(),
|
||||
new typedoc.TSConfigReader(),
|
||||
]
|
||||
);
|
||||
|
||||
app.renderer.defineTheme('nx-markdown-theme', NxMarkdownTheme);
|
||||
|
||||
const project = await app.convert();
|
||||
if (!project) {
|
||||
throw new Error('Failed to convert the project');
|
||||
}
|
||||
|
||||
// Process all reflections and generate markdown content
|
||||
const reflections = project.getReflectionsByKind(typedoc.ReflectionKind.All);
|
||||
|
||||
for (const reflection of reflections) {
|
||||
if (
|
||||
!reflection.name ||
|
||||
reflection.flags.isPrivate ||
|
||||
!allowedReflections.includes(reflection.kind)
|
||||
)
|
||||
continue;
|
||||
|
||||
// Generate markdown content for this reflection
|
||||
const markdownContent = generateMarkdownForReflection(
|
||||
reflection as typedoc.DeclarationReflection,
|
||||
baseSlug
|
||||
);
|
||||
|
||||
if (markdownContent) {
|
||||
const rendered = await renderMarkdown(markdownContent);
|
||||
|
||||
const documentRecord: DocEntry = {
|
||||
id: `${baseSlug}_${reflection.name.toLowerCase().replaceAll(' ', '')}`,
|
||||
body: markdownContent,
|
||||
rendered,
|
||||
collection: 'devkit-docs',
|
||||
data: {
|
||||
title: reflection.name,
|
||||
docType: baseSlug,
|
||||
description: reflection.comment?.summary
|
||||
? parseComment(reflection.comment.summary, baseSlug)
|
||||
: 'No description available',
|
||||
kind: typedoc.ReflectionKind[reflection.kind],
|
||||
category: categoryMap[reflection.kind] || 'Other',
|
||||
},
|
||||
};
|
||||
|
||||
records.push(documentRecord);
|
||||
}
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
function generateMarkdownForReflection(
|
||||
reflection: typedoc.DeclarationReflection,
|
||||
baseSlug: DocEntry['data']['docType']
|
||||
): string | null {
|
||||
let content = '';
|
||||
|
||||
// Comment/description
|
||||
if (reflection.comment) {
|
||||
if (reflection.comment.summary) {
|
||||
content += parseComment(reflection.comment.summary, baseSlug) + '\n\n';
|
||||
}
|
||||
if (reflection.comment.blockTags) {
|
||||
for (const tag of reflection.comment.blockTags) {
|
||||
// TODO: any other tags we need to handle inside a description?
|
||||
if (tag.tag === '@deprecated') {
|
||||
content += `:::caution[Deprecated]\n`;
|
||||
content += parseComment(tag.content, baseSlug) + '\n';
|
||||
content += `:::\n`;
|
||||
} else {
|
||||
content += `**@${tag.tag}**: ${parseComment(tag.content, baseSlug)}`;
|
||||
}
|
||||
}
|
||||
content += '\n\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Signatures (for functions/methods)
|
||||
if (reflection.signatures) {
|
||||
content += '## Signatures\n\n';
|
||||
for (const sig of reflection.signatures) {
|
||||
content += '```typescript\n';
|
||||
content += `${sig.name}(${formatParameters(
|
||||
sig.parameters
|
||||
)}): ${formatType(sig.type)}\n`;
|
||||
content += '```\n\n';
|
||||
|
||||
if (sig.comment?.summary) {
|
||||
content += parseComment(sig.comment.summary, baseSlug) + '\n\n';
|
||||
}
|
||||
|
||||
if (sig.parameters && sig.parameters.length > 0) {
|
||||
content += '### Parameters\n\n';
|
||||
for (const param of sig.parameters) {
|
||||
content += `- **${param.name}**: \`${formatType(param.type)}\``;
|
||||
if (param.comment?.summary) {
|
||||
content += ' - ' + parseComment(param.comment.summary, baseSlug);
|
||||
}
|
||||
content += '\n';
|
||||
}
|
||||
content += '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Properties (for interfaces/classes)
|
||||
if (reflection.children && reflection.children.length > 0) {
|
||||
const properties = reflection.children.filter(
|
||||
(child) =>
|
||||
child.kind === typedoc.ReflectionKind.Property ||
|
||||
child.kind === typedoc.ReflectionKind.Method
|
||||
);
|
||||
|
||||
if (properties.length > 0) {
|
||||
content += '## Properties\n\n';
|
||||
for (const prop of properties) {
|
||||
content += `**${prop.name}**: \`${formatType(prop.type)}\`\n\n`;
|
||||
if (prop.comment?.summary) {
|
||||
content += parseComment(prop.comment.summary, baseSlug) + '\n\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return content || null;
|
||||
}
|
||||
|
||||
function formatParameters(parameters?: typedoc.ParameterReflection[]): string {
|
||||
if (!parameters || parameters.length === 0) return '';
|
||||
return parameters.map((p) => `${p.name}: ${formatType(p.type)}`).join(', ');
|
||||
}
|
||||
|
||||
function formatType(type?: typedoc.SomeType): string {
|
||||
if (!type) return 'unknown';
|
||||
|
||||
if (type.type === 'intrinsic') {
|
||||
return (type as any).name || 'any';
|
||||
} else if (type.type === 'reference') {
|
||||
return (type as any).name || 'any';
|
||||
} else if (type.type === 'union') {
|
||||
const types = (type as any).types;
|
||||
return types ? types.map((t: any) => formatType(t)).join(' | ') : 'any';
|
||||
} else if (type.type === 'array') {
|
||||
const elementType = (type as any).elementType;
|
||||
return elementType ? `${formatType(elementType)}[]` : 'any[]';
|
||||
} else if (type.type === 'literal') {
|
||||
return (type as any).value ? `'${(type as any).value}'` : 'any';
|
||||
} else if (type.type === 'reflection') {
|
||||
return 'Object';
|
||||
}
|
||||
|
||||
return 'any';
|
||||
}
|
||||
|
||||
function parseComment(
|
||||
comment: typedoc.CommentDisplayPart[],
|
||||
baseSlug: DocEntry['data']['docType']
|
||||
) {
|
||||
const result: string[] = [];
|
||||
for (const part of comment) {
|
||||
switch (part.kind) {
|
||||
case 'text':
|
||||
case 'code':
|
||||
result.push(part.text);
|
||||
break;
|
||||
case 'inline-tag':
|
||||
switch (part.tag) {
|
||||
case '@label':
|
||||
case '@inheritdoc':
|
||||
break;
|
||||
case '@link':
|
||||
case '@linkcode':
|
||||
case '@linkplain': {
|
||||
if (part.target) {
|
||||
const baseUrl =
|
||||
`/api/plugins/devkit` +
|
||||
(baseSlug === 'ngcli_adapter' ? `/${baseSlug}` : '');
|
||||
const url =
|
||||
typeof part.target === 'string'
|
||||
? part.target
|
||||
: // TODO: there are cases where links are to a specific header inside another document that we need to handle
|
||||
// i.e. Workspace -> NxJsonConfiguration#<tasksDefaultOptions>
|
||||
|
||||
`${baseUrl}/${part.text.toLowerCase().replaceAll(' ', '')}`;
|
||||
const wrap = part.tag === '@linkcode' ? '`' : '';
|
||||
result.push(
|
||||
url ? `[${wrap}${part.text}${wrap}](${url})` : part.text
|
||||
);
|
||||
} else {
|
||||
result.push(part.text);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
result.push(`{${part.tag} ${part.text}}`);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result.push('');
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
.join('')
|
||||
.split('\n')
|
||||
.filter((line) => !line.startsWith('@note'))
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
function setupTypeDoc(logger: LoaderContext['logger']) {
|
||||
const tempDir = join(tmpdir(), `nx-devkit-docs`);
|
||||
const projectRoot = process.cwd();
|
||||
|
||||
// Setup temporary build directory
|
||||
const buildDir = join(workspaceRoot, 'dist', 'packages', 'devkit');
|
||||
const outDir = join(tempDir, 'docs', 'generated', 'devkit');
|
||||
|
||||
// Create necessary directories
|
||||
mkdirSync(buildDir, { recursive: true });
|
||||
mkdirSync(outDir, { recursive: true });
|
||||
mkdirSync(join(tempDir, 'packages', 'devkit'), { recursive: true });
|
||||
|
||||
// Copy the actual tsconfig.json files
|
||||
const devkitPath = join(workspaceRoot, 'packages', 'devkit');
|
||||
const tsconfigLibPath = join(devkitPath, 'tsconfig.lib.json');
|
||||
const tsconfigPath = join(devkitPath, 'tsconfig.json');
|
||||
const tsconfigBasePath = join(workspaceRoot, 'tsconfig.base.json');
|
||||
|
||||
if (!existsSync(tsconfigLibPath)) {
|
||||
logger.warn(
|
||||
'tsconfig.lib.json not found, skipping DevKit documentation generation'
|
||||
);
|
||||
throw new Error(
|
||||
`tsconfig.lib.json not found, unable to generate docs. ${tsconfigLibPath}`
|
||||
);
|
||||
}
|
||||
|
||||
// Copy tsconfig files to temp directory
|
||||
cpSync(tsconfigLibPath, join(buildDir, 'tsconfig.lib.json'));
|
||||
if (existsSync(tsconfigPath)) {
|
||||
cpSync(tsconfigPath, join(tempDir, 'packages', 'devkit', 'tsconfig.json'));
|
||||
}
|
||||
// Copy tsconfig.base.json if it exists
|
||||
if (existsSync(tsconfigBasePath)) {
|
||||
cpSync(tsconfigBasePath, join(tempDir, 'tsconfig.base.json'));
|
||||
}
|
||||
|
||||
// Update tsconfig paths to point to the absolute paths
|
||||
let tsconfigContent = readFileSync(
|
||||
join(buildDir, 'tsconfig.lib.json'),
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
// Replace relative extends with absolute path
|
||||
if (tsconfigContent.includes('"extends": "../../tsconfig.base.json"')) {
|
||||
tsconfigContent = tsconfigContent.replace(
|
||||
'"extends": "../../tsconfig.base.json"',
|
||||
`"extends": "${join(tempDir, 'packages', 'devkit', 'tsconfig.json')}"`
|
||||
);
|
||||
}
|
||||
|
||||
// Add explicit rootDir and outDir to avoid issues
|
||||
const tsconfigObj = JSON.parse(tsconfigContent);
|
||||
tsconfigObj.compilerOptions = tsconfigObj.compilerOptions || {};
|
||||
tsconfigObj.compilerOptions.rootDir = projectRoot;
|
||||
tsconfigObj.compilerOptions.typeRoots = [
|
||||
join(projectRoot, 'node_modules', '@types'),
|
||||
];
|
||||
|
||||
// Exclude test files to avoid type conflicts
|
||||
tsconfigObj.exclude = [
|
||||
...(tsconfigObj.exclude || []),
|
||||
'**/*.spec.ts',
|
||||
'**/*.test.ts',
|
||||
'**/test/**',
|
||||
'**/tests/**',
|
||||
'node_modules/@types/jasmine/**',
|
||||
'node_modules/@types/jest/**',
|
||||
];
|
||||
|
||||
writeFileSync(
|
||||
join(buildDir, 'tsconfig.lib.json'),
|
||||
JSON.stringify(tsconfigObj, null, 2)
|
||||
);
|
||||
|
||||
rmSync(outDir, { recursive: true, force: true });
|
||||
|
||||
const defaultTypedocOptions: Partial<typedoc.TypeDocOptions> & {
|
||||
[key: string]: unknown;
|
||||
} = {
|
||||
plugin: ['typedoc-plugin-markdown'],
|
||||
disableSources: true,
|
||||
theme: 'markdown',
|
||||
readme: 'none',
|
||||
hideBreadcrumbs: true,
|
||||
allReflectionsHaveOwnDocument: true,
|
||||
skipErrorChecking: true, // Skip TypeScript errors
|
||||
compilerOptions: {
|
||||
skipLibCheck: true,
|
||||
skipDefaultLibCheck: true,
|
||||
noEmit: true,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
projectRoot,
|
||||
outDir,
|
||||
buildDir,
|
||||
defaultTypedocOptions,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { join, relative } from 'path';
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
import {
|
||||
getPropertyDefault,
|
||||
getPropertyType,
|
||||
parseExecutors,
|
||||
parseGenerators,
|
||||
parseMigrations,
|
||||
} from './utils/plugin-schema-parser';
|
||||
import type { Loader, LoaderContext } from 'astro/loaders';
|
||||
import type { CollectionEntry, RenderedContent } from 'astro:content';
|
||||
import { watchAndCall } from './utils/watch.ts';
|
||||
|
||||
// TODO: make this a glob pattern or something so we don't have to manually update
|
||||
// Define the plugins to generate documentation for
|
||||
const PLUGIN_PATHS = [
|
||||
'cypress',
|
||||
'react',
|
||||
'next',
|
||||
'angular',
|
||||
'vue',
|
||||
'vite',
|
||||
'webpack',
|
||||
'jest',
|
||||
'eslint',
|
||||
'storybook',
|
||||
'playwright',
|
||||
'rollup',
|
||||
'esbuild',
|
||||
'rspack',
|
||||
'remix',
|
||||
'expo',
|
||||
'react-native',
|
||||
'detox',
|
||||
'express',
|
||||
'nest',
|
||||
'node',
|
||||
'js',
|
||||
'web',
|
||||
'workspace',
|
||||
'nx',
|
||||
'plugin',
|
||||
'nuxt',
|
||||
'gradle',
|
||||
];
|
||||
|
||||
type DocEntry = CollectionEntry<'plugin-docs'>;
|
||||
|
||||
function generateMigrationItem(
|
||||
name: string,
|
||||
item: any,
|
||||
packageName: string
|
||||
): string {
|
||||
const { config } = item;
|
||||
let markdown = `\n#### \`${name}\`\n`;
|
||||
|
||||
if (config.description) {
|
||||
markdown += `${config.description}\n\n`;
|
||||
}
|
||||
|
||||
return markdown;
|
||||
}
|
||||
|
||||
function generatePackageUpdateItem(item: any): string {
|
||||
const { config } = item;
|
||||
let markdown = `\n#### Package Updates for ${config.name}\n`;
|
||||
|
||||
if (config.packages && Object.keys(config.packages).length > 0) {
|
||||
markdown += `\nThe following packages will be updated:\n\n`;
|
||||
markdown += `| Package | Version |\n`;
|
||||
markdown += `|---------|----------|\n`;
|
||||
|
||||
for (const [packageName, packageConfig] of Object.entries(
|
||||
config.packages
|
||||
) as [string, any][]) {
|
||||
markdown += `| \`${packageName}\` | \`${packageConfig.version}\` |\n`;
|
||||
}
|
||||
|
||||
markdown += `\n`;
|
||||
}
|
||||
|
||||
return markdown;
|
||||
}
|
||||
|
||||
function generateMarkdown(
|
||||
pluginName: string,
|
||||
items: Map<string, any>,
|
||||
docType: 'generators' | 'executors' | 'migrations'
|
||||
) {
|
||||
const packageName = `@nx/${pluginName}`;
|
||||
const typeLabel = docType.charAt(0).toUpperCase() + docType.slice(1);
|
||||
|
||||
let markdown = `
|
||||
The ${packageName} plugin provides various ${docType} to help you create and configure ${pluginName} projects within your Nx workspace.
|
||||
Below is a complete reference for all available ${docType} and their options.
|
||||
`;
|
||||
|
||||
if (docType === 'migrations') {
|
||||
// Group migrations by major.minor version
|
||||
const versionGroups = new Map<string, Array<{ name: string; item: any }>>();
|
||||
|
||||
for (const [name, item] of items.entries()) {
|
||||
const fullVersion = item.config.version || 'unknown';
|
||||
// Extract major.minor from version (e.g., "21.2.3-beta.1" -> "21.2")
|
||||
const majorMinorMatch = fullVersion.match(/^(\d+)\.(\d+)/);
|
||||
const majorMinor = majorMinorMatch
|
||||
? `${majorMinorMatch[1]}.${majorMinorMatch[2]}`
|
||||
: fullVersion;
|
||||
|
||||
if (!versionGroups.has(majorMinor)) {
|
||||
versionGroups.set(majorMinor, []);
|
||||
}
|
||||
versionGroups.get(majorMinor)!.push({ name, item });
|
||||
}
|
||||
|
||||
// Sort versions by descending order (highest first)
|
||||
const sortedVersions = Array.from(versionGroups.keys()).sort((a, b) => {
|
||||
// Handle major.minor version comparison
|
||||
const parseVersion = (version: string) => {
|
||||
const match = version.match(/^(\d+)\.(\d+)/);
|
||||
if (!match) return [0, 0];
|
||||
return [parseInt(match[1]), parseInt(match[2])];
|
||||
};
|
||||
|
||||
const [aMajor, aMinor] = parseVersion(a);
|
||||
const [bMajor, bMinor] = parseVersion(b);
|
||||
|
||||
// Compare major, minor in descending order
|
||||
if (bMajor !== aMajor) return bMajor - aMajor;
|
||||
if (bMinor !== aMinor) return bMinor - aMinor;
|
||||
|
||||
// If versions are equal, fall back to string comparison
|
||||
return b.localeCompare(a);
|
||||
});
|
||||
|
||||
for (const version of sortedVersions) {
|
||||
const items = versionGroups.get(version)!;
|
||||
markdown += `\n## ${version}.x\n`;
|
||||
|
||||
// Combine all items without sub-headers
|
||||
const generators = items.filter(({ item }) => item.type === 'generator');
|
||||
const packageUpdates = items.filter(
|
||||
({ item }) => item.type === 'packageJsonUpdate'
|
||||
);
|
||||
|
||||
// Add generators first
|
||||
for (const { name, item } of generators) {
|
||||
markdown += generateMigrationItem(name, item, packageName);
|
||||
}
|
||||
|
||||
// Add package updates
|
||||
for (const { item } of packageUpdates) {
|
||||
markdown += generatePackageUpdateItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
return markdown;
|
||||
}
|
||||
|
||||
// Sort items alphabetically for non-migration types
|
||||
const sortedItems = Array.from(items.entries()).sort(([a], [b]) =>
|
||||
a.localeCompare(b)
|
||||
);
|
||||
|
||||
for (const [name, { config, schema }] of sortedItems) {
|
||||
const fullItemName = `${packageName}:${name}`;
|
||||
|
||||
markdown += `
|
||||
### \`${name}\`
|
||||
`;
|
||||
|
||||
if (schema?.description || config?.description) {
|
||||
markdown += `${schema?.description || config?.description}\n\n`;
|
||||
}
|
||||
|
||||
if (docType === 'generators') {
|
||||
markdown += `**Usage:**
|
||||
\`\`\`bash
|
||||
nx generate ${fullItemName} [options]
|
||||
\`\`\`
|
||||
`;
|
||||
} else if (docType === 'executors') {
|
||||
markdown += `**Usage:**
|
||||
\`\`\`bash
|
||||
nx run project:${name} [options]
|
||||
\`\`\`
|
||||
`;
|
||||
}
|
||||
|
||||
if (config?.aliases && config.aliases.length > 0) {
|
||||
markdown += `
|
||||
**Aliases:** ${config.aliases.map((a: string) => `\`${a}\``).join(', ')}
|
||||
`;
|
||||
}
|
||||
|
||||
if (!schema) continue;
|
||||
|
||||
// Positional arguments (usually the first required property)
|
||||
const required = schema.required || [];
|
||||
const properties = schema.properties || {};
|
||||
|
||||
// Check for properties with $default.$source === 'argv'
|
||||
const positionalArgs: string[] = [];
|
||||
for (const [propName, prop] of Object.entries(properties) as [
|
||||
string,
|
||||
any
|
||||
][]) {
|
||||
if (
|
||||
prop.$default &&
|
||||
prop.$default.$source === 'argv' &&
|
||||
typeof prop.$default.index === 'number'
|
||||
) {
|
||||
positionalArgs[prop.$default.index] = propName;
|
||||
}
|
||||
}
|
||||
|
||||
if (positionalArgs.length > 0 && docType === 'generators') {
|
||||
markdown += `
|
||||
**Arguments:**
|
||||
\`\`\`bash
|
||||
nx generate ${fullItemName} ${positionalArgs
|
||||
.map((arg) => `<${arg}>`)
|
||||
.join(' ')} [options]
|
||||
\`\`\`
|
||||
`;
|
||||
}
|
||||
|
||||
if (Object.keys(properties).length > 0) {
|
||||
markdown += `
|
||||
#### Options
|
||||
|
||||
| Option | Type | Description | Default |
|
||||
|--------|------|-------------|---------|`;
|
||||
|
||||
// Sort properties alphabetically, but put required ones first
|
||||
const sortedProperties = Object.entries(properties).sort(([a], [b]) => {
|
||||
const aRequired = required.includes(a);
|
||||
const bRequired = required.includes(b);
|
||||
|
||||
if (aRequired && !bRequired) return -1;
|
||||
if (!aRequired && bRequired) return 1;
|
||||
|
||||
return a.localeCompare(b);
|
||||
});
|
||||
|
||||
for (const [propName, property] of sortedProperties as [string, any][]) {
|
||||
// Skip positional arguments from the options table
|
||||
if (positionalArgs.includes(propName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const type = getPropertyType(property);
|
||||
const description = property.description || '';
|
||||
const defaultValue = getPropertyDefault(property);
|
||||
const isRequired = required.includes(propName);
|
||||
|
||||
let optionName = `\`--${propName}\``;
|
||||
|
||||
if (isRequired) {
|
||||
optionName += '[**required**]';
|
||||
}
|
||||
|
||||
markdown += `\n| ${optionName} | ${type} | ${description} | ${defaultValue} |`;
|
||||
}
|
||||
|
||||
markdown += '\n';
|
||||
}
|
||||
|
||||
if (docType === 'generators') {
|
||||
markdown += `
|
||||
## Getting Help
|
||||
|
||||
You can get help for any generator by adding the \`--help\` flag:
|
||||
|
||||
\`\`\`bash
|
||||
nx generate ${packageName}:<generator> --help
|
||||
\`\`\`
|
||||
`;
|
||||
} else if (docType === 'executors') {
|
||||
markdown += `
|
||||
## Getting Help
|
||||
|
||||
You can get help for any executor by adding the \`--help\` flag:
|
||||
|
||||
\`\`\`bash
|
||||
nx run project:${docType} --help
|
||||
\`\`\`
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
return markdown;
|
||||
}
|
||||
|
||||
function getPluginDescription(pluginPath: string, pluginName: string): string {
|
||||
const packageJsonPath = join(pluginPath, 'package.json');
|
||||
|
||||
try {
|
||||
if (existsSync(packageJsonPath)) {
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
||||
if (packageJson.description && packageJson.description.trim()) {
|
||||
return packageJson.description.trim();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// If we can't read the package.json, fall back to default
|
||||
}
|
||||
|
||||
return `The Nx Plugin for ${pluginName}`;
|
||||
}
|
||||
|
||||
export async function generateAllPluginDocs(
|
||||
logger: LoaderContext['logger'],
|
||||
watcher: LoaderContext['watcher'],
|
||||
renderMarkdown: (content: string) => Promise<RenderedContent>
|
||||
): Promise<DocEntry[]> {
|
||||
logger.info('Generating plugin documentation...');
|
||||
const entries: DocEntry[] = [];
|
||||
let successCount = 0;
|
||||
let skipCount = 0;
|
||||
|
||||
for (const relativePath of PLUGIN_PATHS) {
|
||||
const pluginPath = join(workspaceRoot, 'packages', relativePath);
|
||||
|
||||
if (!existsSync(pluginPath)) {
|
||||
logger.warn(`⚠️ Skipping ${relativePath} - path does not exist`);
|
||||
skipCount++;
|
||||
continue;
|
||||
}
|
||||
watcher?.add(pluginPath);
|
||||
|
||||
// Extract plugin name from path
|
||||
const pluginName = relativePath.split('/').pop() || '';
|
||||
|
||||
// Get plugin description from package.json
|
||||
const pluginDescription = getPluginDescription(pluginPath, pluginName);
|
||||
|
||||
try {
|
||||
// Process generators
|
||||
const generators = parseGenerators(pluginPath);
|
||||
if (generators && generators.size > 0) {
|
||||
const markdown = generateMarkdown(pluginName, generators, 'generators');
|
||||
entries.push({
|
||||
id: `${pluginName}-generators`,
|
||||
body: markdown,
|
||||
collection: 'plugin-docs',
|
||||
rendered: await renderMarkdown(markdown),
|
||||
data: {
|
||||
title: `@nx/${pluginName} Generators`,
|
||||
pluginName,
|
||||
packageName: `@nx/${pluginName}`,
|
||||
docType: 'generators',
|
||||
description: pluginDescription,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Process executors
|
||||
const executors = parseExecutors(pluginPath);
|
||||
if (executors && executors.size > 0) {
|
||||
const markdown = generateMarkdown(pluginName, executors, 'executors');
|
||||
entries.push({
|
||||
id: `${pluginName}-executors`,
|
||||
body: markdown,
|
||||
collection: 'plugin-docs',
|
||||
rendered: await renderMarkdown(markdown),
|
||||
data: {
|
||||
title: `@nx/${pluginName} Executors`,
|
||||
pluginName,
|
||||
packageName: `@nx/${pluginName}`,
|
||||
docType: 'executors',
|
||||
description: pluginDescription,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Process migrations
|
||||
const migrations = parseMigrations(pluginPath);
|
||||
if (migrations && migrations.size > 0) {
|
||||
const markdown = generateMarkdown(pluginName, migrations, 'migrations');
|
||||
entries.push({
|
||||
id: `${pluginName}-migrations`,
|
||||
body: markdown,
|
||||
collection: 'plugin-docs',
|
||||
rendered: await renderMarkdown(markdown),
|
||||
data: {
|
||||
title: `@nx/${pluginName} Migrations`,
|
||||
pluginName,
|
||||
packageName: `@nx/${pluginName}`,
|
||||
docType: 'migrations',
|
||||
description: pluginDescription,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (generators?.size || executors?.size || migrations?.size) {
|
||||
logger.info(`✅ Generated documentation for ${pluginName}`);
|
||||
successCount++;
|
||||
} else {
|
||||
logger.warn(
|
||||
`⚠️ Skipping ${pluginName} - no visible documentation found`
|
||||
);
|
||||
skipCount++;
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.error(`❌ Error processing ${pluginName}: ${error.message}`);
|
||||
skipCount++;
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
export function PluginLoader(options: any = {}): Loader {
|
||||
return {
|
||||
name: 'nx-plugin-loader',
|
||||
async load({ store, logger, watcher, renderMarkdown }: LoaderContext) {
|
||||
const generate = async () => {
|
||||
store.clear();
|
||||
const docs = await generateAllPluginDocs(
|
||||
logger,
|
||||
watcher,
|
||||
// @ts-expect-error - astro:content types seem to always be out of sync w/ generated types
|
||||
renderMarkdown
|
||||
);
|
||||
docs.forEach(store.set);
|
||||
logger.info(
|
||||
`Generated plugin documentation with ${docs.length} entries`
|
||||
);
|
||||
};
|
||||
|
||||
if (watcher) {
|
||||
const pathsToWatch = [
|
||||
join(import.meta.dirname, 'plugin.loader.ts'),
|
||||
join(import.meta.dirname, 'utils', 'plugin-schema-parser.ts'),
|
||||
];
|
||||
watchAndCall(watcher, pathsToWatch, generate);
|
||||
}
|
||||
|
||||
await generate();
|
||||
},
|
||||
};
|
||||
}
|
||||