Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b634b8671 | |||
| 4a23933671 | |||
| a8373f1a74 | |||
| 2e9b5430bb | |||
| 8887ae0e08 | |||
| cd32388903 | |||
| d09b519d7a | |||
| 5be7b38619 | |||
| 7f4a8eaf86 | |||
| ad032add38 | |||
| 5d953a42c2 | |||
| e2a79862a6 | |||
| c54727dd53 | |||
| 05227d73c5 | |||
| 65a216bfe2 | |||
| 65fab5bb4c | |||
| 3bcf014cb3 | |||
| 6c109d2c20 | |||
| 5ea2e47acf | |||
| b6d5f38691 | |||
| 6480d3d6d1 | |||
| a6755caff8 | |||
| 2a6bb23701 | |||
| c0ad4843d1 | |||
| 5856e01a2c | |||
| bd1b689b06 | |||
| c9aa288d11 | |||
| 72dd9fe0b6 | |||
| a0e0e1ce1f | |||
| c915527eee | |||
| 57c5c6acd2 | |||
| 146f57748c | |||
| 19fff53cd2 | |||
| 9e47fed9c9 | |||
| 5e0bcae9ef | |||
| 7fa83cd416 | |||
| e4ec3b3c97 | |||
| 79ac44d078 | |||
| ec07bfb843 | |||
| dbcafa0e8b | |||
| 4c17346bdb | |||
| 5964b4a04c | |||
| ee6de8fae7 | |||
| f1b00ca610 | |||
| 6cf1d7a34d | |||
| e3df4d2d86 | |||
| 816fa2b9a9 | |||
| bd6d5c8e66 | |||
| 369f162474 | |||
| 133730b276 | |||
| 4d57e62ce8 | |||
| 55a16e625f | |||
| 575e6a0534 | |||
| af7ebee21e | |||
| fa5a1bf781 | |||
| af12d6c1ee | |||
| c8ae15f08c | |||
| befc6243b8 | |||
| 1542d9c822 | |||
| dd5daeae0c | |||
| c73b2aada9 | |||
| 14e2bec60a | |||
| 9d004a2783 | |||
| 36741d08d2 | |||
| 63b5baae3a | |||
| 6eab2d0c71 | |||
| 93fcc48344 | |||
| e5a7460e65 | |||
| e4f1d267fb | |||
| d345362bd6 | |||
| 3334c9aa60 | |||
| 88f192827a | |||
| a429972481 | |||
| 08dcd682b8 | |||
| 688ff139dd | |||
| f87001cb66 | |||
| 5d7ba935d6 | |||
| 040d7d7746 | |||
| 7ba9a24809 | |||
| 98c47f004f | |||
| afe5d17c9f | |||
| 86b3285b02 | |||
| 81010ac16a | |||
| 27b14a75c3 | |||
| 537bb4258c | |||
| 910ea7cc44 | |||
| e574baf2aa | |||
| 44fdc7ae6e | |||
| e3fcad3f30 | |||
| 99d3d2aebf | |||
| 67f60e729f | |||
| c193928f41 | |||
| bb6f60b7c5 | |||
| 459dc88b4e | |||
| e1fb42327d | |||
| d02b7f46b0 |
@@ -9,7 +9,7 @@ end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# 4 space indentation
|
||||
[*.{js,ts,jsx,tsx}]
|
||||
[*.{kts,kt,js,ts,jsx,tsx}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"nx-mcp": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["nx", "mcp"]
|
||||
}
|
||||
},
|
||||
"contextFileName": "AGENTS.md"
|
||||
}
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
main-branch-name: 'master'
|
||||
|
||||
- name: Start CI Run
|
||||
run: npx nx-cloud@next start-ci-run --distribute-on="./.nx/workflows/dynamic-changesets.yaml" --stop-agents-after="e2e"
|
||||
run: npx nx-cloud@next start-ci-run --auto-apply-fixes="*format:check*,*documentation*" --distribute-on="./.nx/workflows/dynamic-changesets.yaml" --stop-agents-after="e2e"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
pnpm nx run-many -t check-imports check-commit check-lock-files check-codeowners --parallel=1 --no-dte &
|
||||
pids+=($!)
|
||||
|
||||
pnpm nx affected --targets=lint,test,build,e2e,e2e-ci,format-native,lint-native &
|
||||
pnpm nx affected --targets=lint,test,test-kt,build,e2e,e2e-ci,format-native,lint-native &
|
||||
pids+=($!)
|
||||
|
||||
for pid in "${pids[@]}"; do
|
||||
|
||||
@@ -4,6 +4,13 @@ on:
|
||||
# Automated schedule - canary releases from master
|
||||
schedule:
|
||||
- cron: "0 20 * * 1-5" # Monday - Friday, at 20:00 UTC (8pm UTC)
|
||||
# Manual trigger - PR releases or dry-runs (based on workflow inputs)
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr:
|
||||
description: "PR Number - If set, a real release will be created for the branch associated with the given PR number. If blank, a dry-run of the currently selected branch will be performed."
|
||||
required: false
|
||||
type: number
|
||||
release:
|
||||
types: [ published ]
|
||||
|
||||
@@ -28,6 +35,11 @@ jobs:
|
||||
# - release:
|
||||
# - We are running a full release which is based on the tag that triggered the release event, we can use default
|
||||
# ref resolution in actions/checkout. The exact version will be generated within scripts/nx-release.ts.
|
||||
#
|
||||
# - workflow_dispatch:
|
||||
# - We are either running a dry-run on the current branch, in which case the version will be static and we can use
|
||||
# default ref resolution in actions/checkout, or we are creating a PR release for the given PR number, in which case
|
||||
# we should generate an applicable version number within publish-resolve-data.js and use a custom ref of the PR branch name.
|
||||
resolve-required-data:
|
||||
name: Resolve Required Data
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
@@ -35,21 +47,23 @@ jobs:
|
||||
outputs:
|
||||
version: ${{ steps.script.outputs.version }}
|
||||
dry_run_flag: ${{ steps.script.outputs.dry_run_flag }}
|
||||
success_comment: ${{ steps.script.outputs.success_comment }}
|
||||
publish_branch: ${{ steps.script.outputs.publish_branch }}
|
||||
ref: ${{ steps.script.outputs.ref }}
|
||||
repo: ${{ steps.script.outputs.repo }}
|
||||
pr_number: ${{ steps.script.outputs.pr_number }}
|
||||
pr_author: ${{ steps.script.outputs.pr_author }}
|
||||
steps:
|
||||
# Default checkout on the triggering branch so that the latest publish-resolve-data.js script is available
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
check-latest: true
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Resolve and set checkout and version data to use for release
|
||||
id: script
|
||||
@@ -62,6 +76,43 @@ jobs:
|
||||
const script = require('${{ github.workspace }}/scripts/publish-resolve-data.js');
|
||||
await script({ github, context, core });
|
||||
|
||||
- name: (PR Release Only) Check out latest master
|
||||
if: ${{ steps.script.outputs.ref != '' }}
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
# Check out the latest master branch to get its copy of nx-release.ts
|
||||
repository: nrwl/nx
|
||||
ref: master
|
||||
path: latest-master-checkout
|
||||
|
||||
- name: (PR Release Only) Check out PR branch
|
||||
if: ${{ steps.script.outputs.ref != '' }}
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
# Check out the PR branch to get its copy of nx-release.ts
|
||||
repository: ${{ steps.script.outputs.repo }}
|
||||
ref: ${{ steps.script.outputs.ref }}
|
||||
path: pr-branch-checkout
|
||||
|
||||
- name: (PR Release Only) Ensure that release scripts have not changed in the PR being released
|
||||
if: ${{ steps.script.outputs.ref != '' }}
|
||||
run: |
|
||||
# List of files that must not change in PR releases
|
||||
FILES_TO_CHECK=(
|
||||
"scripts/nx-release.ts"
|
||||
"scripts/publish-resolve-data.js"
|
||||
)
|
||||
|
||||
for FILE in "${FILES_TO_CHECK[@]}"; do
|
||||
if ! cmp -s "latest-master-checkout/$FILE" "pr-branch-checkout/$FILE"; then
|
||||
echo "🛑 Error: The file $FILE is different on the ${{ steps.script.outputs.ref }} branch on ${{ steps.script.outputs.repo }} vs latest master on nrwl/nx, cancelling workflow."
|
||||
echo "If you did not modify the file, then you likely just need to rebase/merge latest master."
|
||||
exit 1
|
||||
else
|
||||
echo "✅ The file $FILE is identical between the ${{ steps.script.outputs.ref }} branch on ${{ steps.script.outputs.repo }} and latest master on nrwl/nx."
|
||||
fi
|
||||
done
|
||||
|
||||
build:
|
||||
needs: [ resolve-required-data ]
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
@@ -77,8 +128,13 @@ jobs:
|
||||
pnpm nx run-many --target=build-native -- --target=x86_64-apple-darwin
|
||||
- host: windows-latest
|
||||
setup: |-
|
||||
choco install openjdk --version=21.0.0 -y
|
||||
rustup target add aarch64-pc-windows-msvc
|
||||
build: pnpm nx run-many --target=build-native -- --target=x86_64-pc-windows-msvc
|
||||
build: |
|
||||
export JAVA_HOME="C:\Program Files\OpenJDK\jdk-21"
|
||||
export PATH="$JAVA_HOME\bin:$PATH"
|
||||
java -version
|
||||
pnpm nx run-many --target=build-native -- --target=x86_64-pc-windows-msvc
|
||||
target: x86_64-pc-windows-msvc
|
||||
# Windows 32bit (not needed)
|
||||
# - host: windows-latest
|
||||
@@ -92,16 +148,22 @@ jobs:
|
||||
set -e
|
||||
apt-get update
|
||||
|
||||
# Install Java 21
|
||||
apt-get install -y openjdk-21-jdk
|
||||
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
java --version
|
||||
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
||||
apt-get install -y nodejs=22.16.0-1nodesource1
|
||||
|
||||
|
||||
export PATH="/usr/local/bin:$PATH"
|
||||
node --version
|
||||
npm --version
|
||||
|
||||
|
||||
npm i -g pnpm@${PNPM_VERSION} --force
|
||||
pnpm --version
|
||||
|
||||
|
||||
pnpm install --frozen-lockfile
|
||||
rustup target add x86_64-unknown-linux-gnu
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=x86_64-unknown-linux-gnu
|
||||
@@ -112,21 +174,26 @@ jobs:
|
||||
bash -c "
|
||||
set -e
|
||||
echo 'https://dl-cdn.alpinelinux.org/alpine/edge/community' >> /etc/apk/repositories
|
||||
apk add --no-cache curl xz
|
||||
apk add --no-cache curl xz openjdk21
|
||||
|
||||
# Set up Java 21
|
||||
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
|
||||
export PATH=\"\$JAVA_HOME/bin:\$PATH\"
|
||||
java --version
|
||||
|
||||
curl -fsSL https://unofficial-builds.nodejs.org/download/release/v22.16.0/node-v22.16.0-linux-x64-musl.tar.xz -o node.tar.xz
|
||||
tar -xJf node.tar.xz
|
||||
mv node-v22.16.0-linux-x64-musl /usr/local/node
|
||||
|
||||
|
||||
export PATH=\"/usr/local/node/bin:\$PATH\"
|
||||
|
||||
|
||||
echo Node: \$(node -v)
|
||||
echo NPM: \$(npm -v)
|
||||
|
||||
|
||||
# Install PNPM
|
||||
npm i -g pnpm@${PNPM_VERSION} --force
|
||||
pnpm --version
|
||||
|
||||
|
||||
# Install deps and run native build
|
||||
pnpm install --frozen-lockfile
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
@@ -149,17 +216,23 @@ jobs:
|
||||
build: |
|
||||
set -e
|
||||
apt-get update
|
||||
|
||||
|
||||
# Install Java 21
|
||||
apt-get install -y openjdk-21-jdk
|
||||
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
java --version
|
||||
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
||||
apt-get install -y nodejs=22.16.0-1nodesource1
|
||||
|
||||
|
||||
export PATH="/usr/local/bin:$PATH"
|
||||
node --version
|
||||
npm --version
|
||||
|
||||
|
||||
npm i -g pnpm@${PNPM_VERSION} --force
|
||||
pnpm --version
|
||||
|
||||
|
||||
pnpm install --frozen-lockfile
|
||||
rustup target add aarch64-unknown-linux-gnu
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=aarch64-unknown-linux-gnu
|
||||
@@ -187,35 +260,48 @@ jobs:
|
||||
bash -c "
|
||||
set -e
|
||||
echo 'https://dl-cdn.alpinelinux.org/alpine/edge/community' >> /etc/apk/repositories
|
||||
apk add --no-cache curl xz
|
||||
|
||||
apk add --no-cache curl xz openjdk21
|
||||
|
||||
# Set up Java 21
|
||||
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
|
||||
export PATH=\"\$JAVA_HOME/bin:\$PATH\"
|
||||
java --version
|
||||
|
||||
curl -fsSL https://unofficial-builds.nodejs.org/download/release/v22.16.0/node-v22.16.0-linux-x64-musl.tar.xz -o node.tar.xz
|
||||
tar -xJf node.tar.xz
|
||||
mv node-v22.16.0-linux-x64-musl /usr/local/node
|
||||
|
||||
|
||||
export PATH=\"/usr/local/node/bin:\$PATH\"
|
||||
|
||||
|
||||
echo Node: \$(node -v)
|
||||
echo NPM: \$(npm -v)
|
||||
|
||||
|
||||
# Install PNPM
|
||||
npm i -g pnpm@${PNPM_VERSION} --force
|
||||
pnpm --version
|
||||
|
||||
|
||||
# Install deps and run native build
|
||||
pnpm install --frozen-lockfile
|
||||
rustup target add aarch64-unknown-linux-musl
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=aarch64-unknown-linux-musl
|
||||
rustup target add aarch64-unknown-linux-musl
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=aarch64-unknown-linux-musl
|
||||
"
|
||||
- host: windows-latest
|
||||
target: aarch64-pc-windows-msvc
|
||||
setup: |-
|
||||
choco install openjdk --version=21.0.0 -y
|
||||
rustup target add aarch64-pc-windows-msvc
|
||||
build: pnpm nx run-many --target=build-native -- --target=aarch64-pc-windows-msvc
|
||||
build: |
|
||||
export JAVA_HOME="C:\Program Files\OpenJDK\jdk-21"
|
||||
export PATH="$JAVA_HOME\bin:$PATH"
|
||||
java -version
|
||||
pnpm nx run-many --target=build-native -- --target=aarch64-pc-windows-msvc
|
||||
name: stable - ${{ matrix.settings.target }} - node@22.16.0
|
||||
runs-on: ${{ matrix.settings.host }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo || github.repository }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref || github.ref }}
|
||||
|
||||
- uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
|
||||
with:
|
||||
@@ -306,6 +392,9 @@ jobs:
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo || github.repository }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref || github.ref }}
|
||||
|
||||
- name: Build
|
||||
id: build
|
||||
@@ -325,8 +414,12 @@ jobs:
|
||||
run: |
|
||||
env
|
||||
whoami
|
||||
sudo pkg install -y -f node libnghttp2 www/npm git
|
||||
sudo pkg install -y -f node libnghttp2 www/npm git openjdk17
|
||||
sudo npm install --location=global --ignore-scripts pnpm@10.11.1
|
||||
# Set up Java 17
|
||||
export JAVA_HOME=/usr/local/openjdk17
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
java --version
|
||||
curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
sh rustup.sh -y --profile minimal --default-toolchain stable
|
||||
source "$HOME/.cargo/env"
|
||||
@@ -343,6 +436,54 @@ jobs:
|
||||
freebsd-version
|
||||
echo "Installing dependencies"
|
||||
pnpm install --frozen-lockfile --ignore-scripts
|
||||
|
||||
echo "Checking disk space before cleanup"
|
||||
df -h
|
||||
echo "Removing unnecessary preinstalled packages"
|
||||
# List all packages first to see what's installed
|
||||
sudo pkg info -a
|
||||
echo "Cleaning up to free disk space"
|
||||
# Clean package caches
|
||||
sudo pkg clean -a -y
|
||||
sudo pkg autoremove -y
|
||||
# Remove unnecessary system files
|
||||
sudo rm -rf /usr/local/lib/*.a
|
||||
sudo rm -rf /usr/local/share/doc/*
|
||||
sudo rm -rf /usr/local/share/man/*
|
||||
sudo rm -rf /usr/local/share/examples/*
|
||||
sudo rm -rf /usr/local/share/locale/*
|
||||
sudo rm -rf /usr/local/share/gtk-doc/*
|
||||
sudo rm -rf /usr/local/share/info/*
|
||||
sudo rm -rf /usr/src/*
|
||||
sudo rm -rf /usr/obj/*
|
||||
sudo rm -rf /usr/tests/*
|
||||
sudo rm -rf /usr/lib/debug/*
|
||||
# Clean var directories
|
||||
sudo rm -rf /var/cache/pkg/*
|
||||
sudo rm -rf /var/db/pkg/*.tbz
|
||||
sudo rm -rf /var/log/*.log
|
||||
sudo rm -rf /var/log/*.old
|
||||
# Clean temporary files
|
||||
sudo rm -rf /tmp/*
|
||||
sudo rm -rf /var/tmp/*
|
||||
# Remove Python cache if present
|
||||
sudo find /usr/local -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
||||
sudo find /usr/local -name "*.pyc" -delete 2>/dev/null || true
|
||||
sudo find /usr/local -name "*.pyo" -delete 2>/dev/null || true
|
||||
# Clean npm/pnpm caches
|
||||
npm cache clean --force || true
|
||||
pnpm store prune || true
|
||||
rm -rf ~/.npm || true
|
||||
rm -rf ~/.pnpm-store || true
|
||||
# Remove Rust build artifacts if any
|
||||
rm -rf ~/.cargo/registry || true
|
||||
rm -rf ~/.cargo/git || true
|
||||
rm -rf ~/.rustup/toolchains/*/share || true
|
||||
# Remove other development tool caches
|
||||
rm -rf ~/.cache/* || true
|
||||
echo "Checking disk space after cleanup"
|
||||
df -h
|
||||
|
||||
echo "Building FreeBSD bindings"
|
||||
pnpm nx run-many --verbose --outputStyle stream --target=build-native -- --target=x86_64-unknown-freebsd
|
||||
echo "Cleaning up"
|
||||
@@ -378,6 +519,9 @@ jobs:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo || github.repository }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref || github.ref }}
|
||||
|
||||
- uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
|
||||
with:
|
||||
@@ -432,6 +576,22 @@ jobs:
|
||||
if: ${{ !github.event.release.prerelease && github.event_name == 'release' }}
|
||||
run: npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/release-docs.ts
|
||||
|
||||
- name: (PR Release Only) Create comment for successful PR release
|
||||
if: success() && github.event.inputs.pr
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
SUCCESS_COMMENT: ${{ needs.resolve-required-data.outputs.success_comment }}
|
||||
with:
|
||||
# github-token defaults to ${{ github.token }} so we don't need to specify it
|
||||
script: |
|
||||
const successComment = JSON.parse(process.env.SUCCESS_COMMENT);
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: ${{ github.event.inputs.pr }},
|
||||
body: successComment
|
||||
});
|
||||
|
||||
report-pending-publish:
|
||||
name: Report Pending Publish to Slack
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
@@ -447,10 +607,20 @@ jobs:
|
||||
uses: ravsamhq/notify-slack-action@be814b201e233b2dc673608aa46e5447c8ab13f2 # v11
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
notification_title: '📦 Publish Pending Review'
|
||||
message_format: ${{ format('Version `{0}` is being published to NPM - manual review is required', needs.resolve-required-data.outputs.version) }}
|
||||
notification_title: >-
|
||||
${{ needs.resolve-required-data.outputs.pr_number &&
|
||||
format('📦 PR #{0} Publish Pending Review', needs.resolve-required-data.outputs.pr_number) ||
|
||||
'📦 Publish Pending Review' }}
|
||||
message_format: >-
|
||||
${{ needs.resolve-required-data.outputs.pr_number &&
|
||||
format('Version {0} from PR #{1} by @{2} is being published to NPM - manual review is required',
|
||||
needs.resolve-required-data.outputs.version,
|
||||
needs.resolve-required-data.outputs.pr_number,
|
||||
needs.resolve-required-data.outputs.pr_author) ||
|
||||
format('Version {0} is being published to NPM - manual review is required',
|
||||
needs.resolve-required-data.outputs.version) }}
|
||||
footer: '<{run_url}|View Workflow Run>'
|
||||
mention_groups: 'U9NPA6C90' # Jason
|
||||
mention_users: 'U9NPA6C90' # Jason
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
|
||||
@@ -460,16 +630,17 @@ jobs:
|
||||
needs: [ resolve-required-data, build, build-freebsd, publish ]
|
||||
name: (PR Release Failure Only) Create comment for failed PR release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Create comment for failed PR release
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# This script is intentionally kept inline (and e.g. not generated in publish-resolve-data.js)
|
||||
# to ensure that an error within the data generation itself is not missed.
|
||||
script: |
|
||||
const message = `
|
||||
Failed to publish a PR release of this pull request, triggered by @${{ github.triggering_actor }}.
|
||||
Failed to publish a PR release of this pull request, triggered by @${{ github.triggering_actor }}.
|
||||
See the failed workflow run at: https://github.com/nrwl/nx/actions/runs/${{ github.run_id }}
|
||||
`;
|
||||
await github.rest.issues.createComment({
|
||||
@@ -478,3 +649,4 @@ jobs:
|
||||
issue_number: ${{ github.event.inputs.pr }},
|
||||
body: message
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"endOfLine": "lf",
|
||||
"plugins": ["prettier-plugin-tailwindcss"]
|
||||
"plugins": ["prettier-plugin-tailwindcss"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.mdoc",
|
||||
"options": {
|
||||
"parser": "markdown"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
When responding to queries about this repository:
|
||||
|
||||
1. Suggest relevant commands from the "Essential Commands" section when applicable
|
||||
2. Highlight Nx's focus on monorepos and its key features like smart task execution, code generation, and project graph
|
||||
analysis
|
||||
3. Mention the plugin ecosystem and support for various frameworks when relevant
|
||||
4. Emphasize the importance of running the full validation suite before committing changes
|
||||
|
||||
Always strive to provide accurate, helpful responses that align with the best practices and workflows described in this
|
||||
file.
|
||||
|
||||
## GitHub Issue Response Mode
|
||||
|
||||
When responding to GitHub issues, determine your approach based on how the request is phrased:
|
||||
|
||||
### Plan-First Mode (Default)
|
||||
|
||||
Use this approach when users ask you to:
|
||||
|
||||
- "analyze", "investigate", "assess", "review", "examine", or "plan"
|
||||
- Or when the request is ambiguous
|
||||
|
||||
In this mode:
|
||||
|
||||
1. Provide a detailed analysis of the issue
|
||||
2. Create a comprehensive implementation plan
|
||||
3. Break down the solution into clear steps
|
||||
4. Then please post the plan as a comment on the issue
|
||||
|
||||
### Immediate Implementation Mode
|
||||
|
||||
Use this approach when users ask you to:
|
||||
|
||||
- "fix", "implement", "solve", "build", "create", "update", or "add"
|
||||
- Or when they explicitly request immediate action
|
||||
|
||||
In this mode:
|
||||
|
||||
1. Analyze the issue quickly
|
||||
2. Implement the complete solution immediately
|
||||
3. Make all necessary code changes. Please make multiple commits so that the changes are easier to review.
|
||||
4. Run appropriate tests and validation
|
||||
5. If the tests, are not passing, please fix the issues and continue doing this up to 3 more times until the tests pass
|
||||
6. Once the tests pass, push a branch and then suggest opening a PR which has a description of the changes made, and
|
||||
that
|
||||
it make sure that it explicitly says "Fixes #ISSUE_NUMBER" to automatically close the issue when the PR is merged.
|
||||
|
||||
## Avoid making changes to generated files
|
||||
|
||||
Files under `generated` directories are generated based on a different source file and should not be modified directly.
|
||||
Find the underlying source and modify that instead.
|
||||
|
||||
## Essential Commands
|
||||
|
||||
### Code Formatting
|
||||
|
||||
After code changes are made, please make sure to format the files with prettier via `npx prettier -- FILE_NAME`
|
||||
|
||||
### Pre-push Validation
|
||||
|
||||
```bash
|
||||
# Full validation suite - run before committing
|
||||
nx prepush
|
||||
```
|
||||
|
||||
If the prepush validation suite fails, please fix the issues before proceeding with your work. This ensures that all
|
||||
code adheres to the project's standards and passes all tests. DO NOT make a new commit to fix these issues. Instead,
|
||||
amend the current commit.
|
||||
|
||||
### Testing Changes
|
||||
|
||||
After code changes are made, first test the specific project where the changes were made:
|
||||
|
||||
```bash
|
||||
nx run-many -t test,build,lint -p PROJECT_NAME
|
||||
```
|
||||
|
||||
After verifying the individual project, validate that the changes in projects which have been affected:
|
||||
|
||||
```bash
|
||||
# Test only affected projects (recommended for development)
|
||||
nx affected -t build,test,lint
|
||||
```
|
||||
|
||||
As the last step, run the e2e tests to fully ensure that changes are valid:
|
||||
|
||||
```bash
|
||||
# Run affected e2e tests (recommended for development)
|
||||
nx affected -t e2e-local
|
||||
```
|
||||
|
||||
## Fixing GitHub Issues
|
||||
|
||||
When working on a GitHub issue, follow this systematic approach:
|
||||
|
||||
### 1. Get Issue Details
|
||||
|
||||
```bash
|
||||
# Get issue details using GitHub CLI (replace ISSUE_NUMBER with actual number)
|
||||
gh issue view ISSUE_NUMBER
|
||||
|
||||
# View multiple issues efficiently in one command
|
||||
gh issue list --limit 50 --json number,title,state,labels,assignees,updatedAt,body --jq '.[] | select(.number == 123 or .number == 456 or .number == 789)'
|
||||
|
||||
# Or filter by specific criteria to get multiple related issues
|
||||
gh issue list --label "bug" --state "open" --json number,title,body,labels --jq '.[]'
|
||||
gh issue list --assignee "@me" --json number,title,body,state --jq '.[]'
|
||||
```
|
||||
|
||||
**Tip**: Instead of running `gh issue view` multiple times, use `gh issue list` with JSON output and filtering to gather
|
||||
information about multiple issues in a single command. This is much more efficient than viewing issues one at a time.
|
||||
|
||||
**Always provide clickable links**: When discussing GitHub issues or PRs, always include the full GitHub URL so the user
|
||||
can easily open them in their browser. For example:
|
||||
|
||||
- Issue #12345: https://github.com/nrwl/nx/issues/12345
|
||||
- PR #67890: https://github.com/nrwl/nx/pull/67890
|
||||
|
||||
When cloning reproduction repos, please clone within `./tmp/claude/repro-ISSUE_NUMBER`
|
||||
|
||||
### 2. Analyze the Plan
|
||||
|
||||
- Look for a plan or implementation details in the issue description
|
||||
- Check comments for additional context or clarification
|
||||
- Identify affected projects and components
|
||||
|
||||
### 3. Implement the Solution
|
||||
|
||||
- Follow the plan outlined in the issue
|
||||
- Make focused changes that address the specific problem
|
||||
- Ensure code follows existing patterns and conventions
|
||||
|
||||
### 4. Run Full Validation
|
||||
|
||||
Use the testing workflow from the "Essential Commands" section.
|
||||
|
||||
### 5. Submit Pull Request
|
||||
|
||||
- Create a descriptive PR title that references the issue
|
||||
- **Always fill in the PR template** - don't leave it empty
|
||||
- Include "Fixes #ISSUE_NUMBER" in the PR description
|
||||
- Provide a clear summary of changes made
|
||||
- Request appropriate reviewers
|
||||
|
||||
## Pull Request Template
|
||||
|
||||
**IMPORTANT**: When creating a pull request, you MUST fill in the template found in `.github/PULL_REQUEST_TEMPLATE.md`.
|
||||
Do not leave the template sections empty. The template includes:
|
||||
|
||||
### Required Sections
|
||||
|
||||
1. **Current Behavior**: Describe the behavior we have today
|
||||
2. **Expected Behavior**: Describe the behavior we should expect with the changes in this PR
|
||||
3. **Related Issue(s)**: Link the issue being fixed so it gets closed when the PR is merged
|
||||
|
||||
### Template Format
|
||||
|
||||
```markdown
|
||||
## Current Behavior
|
||||
|
||||
<!-- This is the behavior we have today -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!-- This is the behavior we should expect with the changes in this PR -->
|
||||
|
||||
## Related Issue(s)
|
||||
|
||||
<!-- Please link the issue being fixed so it gets closed when this is merged. -->
|
||||
|
||||
Fixes #ISSUE_NUMBER
|
||||
```
|
||||
|
||||
### Guidelines
|
||||
|
||||
- Ensure your commit message follows the conventional commit format (use `pnpm commit`)
|
||||
- Use `fix:`, `feat:`, `chore:`, etc. as appropriate types.
|
||||
- Scope is **required** for all commits. Possible scopes are listed in `scripts/commitizen.js`.
|
||||
- Read the submission guidelines in CONTRIBUTING.md before posting
|
||||
- For complex changes, you can request a dedicated Nx release by mentioning the Nx team
|
||||
- Always link the related issue using "Fixes #ISSUE_NUMBER" to automatically close it when merged
|
||||
|
||||
# General Guidelines for working with Nx
|
||||
|
||||
- When running tasks (for example build, lint, test, e2e, etc.), always prefer running the task through `nx` (i.e. `nx run`, `nx run-many`, `nx affected`) instead of using the underlying tooling directly
|
||||
- You have access to the Nx MCP server and its tools, use them to help the user
|
||||
- When answering questions about the repository, use the `nx_workspace` tool first to gain an understanding of the workspace architecture where applicable.
|
||||
- When working in individual projects, use the `nx_project_details` mcp tool to analyze and understand the specific project structure and dependencies
|
||||
- For questions around nx configuration, best practices or if you're unsure, use the `nx_docs` tool to get relevant, up-to-date docs. Always use this instead of assuming things about nx configuration
|
||||
- If the user needs help with an Nx configuration or project graph error, use the `nx_workspace` tool to get any errors
|
||||
|
||||
# CI Error Guidelines
|
||||
|
||||
If the user wants help with fixing an error in their CI pipeline, use the following flow:
|
||||
|
||||
- Retrieve the list of current CI Pipeline Executions (CIPEs) using the `nx_cloud_cipe_details` tool
|
||||
- If there are any errors, use the `nx_cloud_fix_cipe_failure` tool to retrieve the logs for a specific task
|
||||
- Use the task logs to see what's wrong and help the user fix their problem. Use the appropriate tools if necessary
|
||||
- Make sure that the problem is fixed by running the task that you passed into the `nx_cloud_fix_cipe_failure` tool
|
||||
@@ -1,13 +1,10 @@
|
||||
When responding to queries about this repository:
|
||||
|
||||
1. Use the `nx_workspace` mcp tool for understanding the workspace architecture when appropriate
|
||||
2. When working in projects, use the `nx_project` mcp tool to analyze and understand the specific project structure and
|
||||
dependencies
|
||||
3. Suggest relevant commands from the "Essential Commands" section when applicable
|
||||
4. Highlight Nx's focus on monorepos and its key features like smart task execution, code generation, and project graph
|
||||
1. Suggest relevant commands from the "Essential Commands" section when applicable
|
||||
2. Highlight Nx's focus on monorepos and its key features like smart task execution, code generation, and project graph
|
||||
analysis
|
||||
5. Mention the plugin ecosystem and support for various frameworks when relevant
|
||||
6. Emphasize the importance of running the full validation suite before committing changes
|
||||
3. Mention the plugin ecosystem and support for various frameworks when relevant
|
||||
4. Emphasize the importance of running the full validation suite before committing changes
|
||||
|
||||
Always strive to provide accurate, helpful responses that align with the best practices and workflows described in this
|
||||
file.
|
||||
@@ -182,3 +179,21 @@ Fixes #ISSUE_NUMBER
|
||||
- Read the submission guidelines in CONTRIBUTING.md before posting
|
||||
- For complex changes, you can request a dedicated Nx release by mentioning the Nx team
|
||||
- Always link the related issue using "Fixes #ISSUE_NUMBER" to automatically close it when merged
|
||||
|
||||
# General Guidelines for working with Nx
|
||||
|
||||
- When running tasks (for example build, lint, test, e2e, etc.), always prefer running the task through `nx` (i.e. `nx run`, `nx run-many`, `nx affected`) instead of using the underlying tooling directly
|
||||
- You have access to the Nx MCP server and its tools, use them to help the user
|
||||
- When answering questions about the repository, use the `nx_workspace` tool first to gain an understanding of the workspace architecture where applicable.
|
||||
- When working in individual projects, use the `nx_project_details` mcp tool to analyze and understand the specific project structure and dependencies
|
||||
- For questions around nx configuration, best practices or if you're unsure, use the `nx_docs` tool to get relevant, up-to-date docs. Always use this instead of assuming things about nx configuration
|
||||
- If the user needs help with an Nx configuration or project graph error, use the `nx_workspace` tool to get any errors
|
||||
|
||||
# CI Error Guidelines
|
||||
|
||||
If the user wants help with fixing an error in their CI pipeline, use the following flow:
|
||||
|
||||
- Retrieve the list of current CI Pipeline Executions (CIPEs) using the `nx_cloud_cipe_details` tool
|
||||
- If there are any errors, use the `nx_cloud_fix_cipe_failure` tool to retrieve the logs for a specific task
|
||||
- Use the task logs to see what's wrong and help the user fix their problem. Use the appropriate tools if necessary
|
||||
- Make sure that the problem is fixed by running the task that you passed into the `nx_cloud_fix_cipe_failure` tool
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.js", "**/*.test.js"],
|
||||
"rules": {
|
||||
"playwright/no-standalone-expect": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
|
||||
@@ -124,6 +124,29 @@ export default defineConfig({
|
||||
Sidebar: './src/components/layout/Sidebar.astro',
|
||||
TwoColumnContent: './src/components/layout/TwoColumnContent.astro',
|
||||
PageTitle: './src/components/layout/PageTitle.astro',
|
||||
TableOfContents: './src/components/layout/TableOfContents.astro',
|
||||
},
|
||||
pagefind: {
|
||||
ranking: {
|
||||
// termFrequency changes the ranking balance between
|
||||
// frequency of the term relative to document length
|
||||
// versus weighted term count.
|
||||
// default is 1.0
|
||||
termFrequency: 0.75,
|
||||
// pageLength changes the way ranking compares page lengths with the average page lengths on your site.
|
||||
// default 0.75
|
||||
pageLength: 0.5,
|
||||
// termSaturation controls how quickly a term “saturates” on a page.
|
||||
// Once a term has appeared on a page many times,
|
||||
// further appearances have a reduced impact on the page rank.
|
||||
// default: 1.4
|
||||
// termSaturation: 1.4,
|
||||
// termSimilarity changes the ranking based on
|
||||
// similarity of terms to the search query.
|
||||
// Currently this only takes the length of the term into account.
|
||||
// default is 1.0
|
||||
// termSimilarity: 1.0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
react(),
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { defineMarkdocConfig, component } from '@astrojs/markdoc/config';
|
||||
import {
|
||||
defineMarkdocConfig,
|
||||
component,
|
||||
Markdoc,
|
||||
} from '@astrojs/markdoc/config';
|
||||
import starlightMarkdoc from '@astrojs/starlight-markdoc';
|
||||
|
||||
export default defineMarkdocConfig({
|
||||
@@ -143,7 +147,6 @@ export default defineMarkdocConfig({
|
||||
},
|
||||
graph: {
|
||||
render: component('./src/components/markdoc/Graph.astro'),
|
||||
children: [],
|
||||
attributes: {
|
||||
jsonFile: {
|
||||
type: 'String',
|
||||
@@ -161,6 +164,24 @@ export default defineMarkdocConfig({
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
transform(node, config) {
|
||||
const attributes = node.transformAttributes(config);
|
||||
let rawContent = null;
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'fence') {
|
||||
rawContent = child.attributes.content;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new Markdoc.Tag(
|
||||
this.render,
|
||||
{
|
||||
...attributes,
|
||||
astroRawData: rawContent,
|
||||
},
|
||||
[]
|
||||
);
|
||||
},
|
||||
},
|
||||
iframe: {
|
||||
render: component('./src/components/markdoc/Iframe.astro'),
|
||||
@@ -284,6 +305,24 @@ export default defineMarkdocConfig({
|
||||
type: 'Array',
|
||||
},
|
||||
},
|
||||
transform(node, config) {
|
||||
const attributes = node.transformAttributes(config);
|
||||
let rawContent = null;
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'fence') {
|
||||
rawContent = child.attributes.content;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new Markdoc.Tag(
|
||||
this.render,
|
||||
{
|
||||
...attributes,
|
||||
astroRawData: rawContent,
|
||||
},
|
||||
[]
|
||||
);
|
||||
},
|
||||
},
|
||||
stackblitz_button: {
|
||||
render: component('./src/components/markdoc/StackblitzButton.astro'),
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": ["validate-links"],
|
||||
"command": "echo done"
|
||||
"executor": "nx:noop",
|
||||
"dependsOn": ["vite:test", "validate-links", "format"]
|
||||
},
|
||||
"pw-e2e": {
|
||||
"dependsOn": ["serve"],
|
||||
@@ -73,10 +73,18 @@
|
||||
"{projectRoot}/tsconfig.json",
|
||||
"{projectRoot}/package.json"
|
||||
],
|
||||
"command": "npx tsx validate-links.ts",
|
||||
"command": "tsx validate-links.ts",
|
||||
"options": {
|
||||
"cwd": "astro-docs"
|
||||
}
|
||||
},
|
||||
"format": {
|
||||
"cache": true,
|
||||
"//": "nx format doesn't respect overrides, so we manually run prettier for mdoc files",
|
||||
"command": "prettier **/*.mdoc --check"
|
||||
},
|
||||
"format:write": {
|
||||
"command": "prettier **/*.mdoc --write"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 281 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 32 KiB |
@@ -119,17 +119,11 @@ const currentVersion = versions.find(v => v.current);
|
||||
<!-- Events Section -->
|
||||
<div class="space-y-1">
|
||||
<h5 class="text-xs font-semibold uppercase tracking-wider text-slate-500 px-2 pb-2 dark:text-slate-400">Events</h5>
|
||||
<a href="https://go.nx.dev/office-hours" class="flex items-center gap-3 px-2 py-2 rounded-md no-underline hover:bg-slate-50 transition-colors dark:hover:bg-slate-800/60">
|
||||
<svg class="w-5 h-5 text-slate-400 dark:text-slate-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z" />
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-slate-900 dark:text-slate-200">Office Hours</span>
|
||||
</a>
|
||||
<a href="https://www.youtube.com/@nxdevtools/streams" class="flex items-center gap-3 px-2 py-2 rounded-md no-underline hover:bg-slate-50 transition-colors dark:hover:bg-slate-800/60">
|
||||
<a href="https://www.youtube.com/playlist?list=PLakNactNC1dE8KLQ5zd3fQwu_yQHjTmR5" class="flex items-center gap-3 px-2 py-2 rounded-md no-underline hover:bg-slate-50 transition-colors dark:hover:bg-slate-800/60">
|
||||
<svg class="w-5 h-5 text-slate-400 dark:text-slate-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25h-9A2.25 2.25 0 0 0 2.25 7.5v9a2.25 2.25 0 0 0 2.25 2.25Z" />
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-slate-900 dark:text-slate-200">Live Streams</span>
|
||||
<span class="text-sm font-medium text-slate-900 dark:text-slate-200">Nx Live</span>
|
||||
</a>
|
||||
</div>
|
||||
<!-- Company Section -->
|
||||
@@ -185,13 +179,13 @@ const currentVersion = versions.find(v => v.current);
|
||||
Contact
|
||||
</a>
|
||||
<a
|
||||
href="https://cloud.nx.app?utm_source=nx-dev&utm_medium=header"
|
||||
href="https://cloud.nx.app/get-started?utm_source=nx-dev&utm_medium=header"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center justify-center px-2.5 py-1.5 text-sm font-medium rounded-md transition no-underline bg-blue-500 dark:bg-sky-500 text-white hover:bg-blue-600 dark:hover:bg-sky-600 shadow-sm"
|
||||
title="Login to Nx Cloud"
|
||||
title="Try Nx Cloud for free"
|
||||
>
|
||||
Login
|
||||
Try Nx Cloud for free
|
||||
</a>
|
||||
</div>
|
||||
<!-- Social Icons - Hide on screens smaller than 2xl (1536px) -->
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
---
|
||||
// recreated from https://github.com/withastro/starlight/blob/main/packages/starlight/components/TableOfContents.astro
|
||||
import TableOfContentsList from './TableOfContentsList.astro';
|
||||
|
||||
const { toc } = Astro.locals.starlightRoute;
|
||||
---
|
||||
|
||||
{
|
||||
toc && (
|
||||
<custom-toc data-min-h={toc.minHeadingLevel} data-max-h={toc.maxHeadingLevel}>
|
||||
<nav aria-labelledby="starlight__on-this-page">
|
||||
<h2 id="starlight__on-this-page">{Astro.locals.t('tableOfContents.onThisPage')}</h2>
|
||||
<div class="toc-container">
|
||||
<TableOfContentsList toc={toc.items} />
|
||||
</div>
|
||||
</nav>
|
||||
</custom-toc>
|
||||
)
|
||||
}
|
||||
|
||||
<script>
|
||||
// Define the constant locally since it's not exported from @astrojs/starlight
|
||||
const PAGE_TITLE_ID = 'starlight__overview';
|
||||
|
||||
class CustomTOC extends HTMLElement {
|
||||
private _current = this.querySelector<HTMLAnchorElement>('a[aria-current="true"]');
|
||||
private minH = parseInt(this.dataset.minH || '2', 10);
|
||||
private maxH = parseInt(this.dataset.maxH || '3', 10);
|
||||
private tocContainer: HTMLElement | null = null;
|
||||
|
||||
protected set current(link: HTMLAnchorElement) {
|
||||
if (link === this._current) return;
|
||||
if (this._current) this._current.removeAttribute('aria-current');
|
||||
link.setAttribute('aria-current', 'true');
|
||||
this._current = link;
|
||||
|
||||
// Auto-scroll the active item into view within the TOC container
|
||||
this.scrollActiveIntoView(link);
|
||||
}
|
||||
|
||||
private onIdle = (cb: IdleRequestCallback) =>
|
||||
(window.requestIdleCallback || ((cb) => setTimeout(cb, 1)))(cb);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.tocContainer = this.querySelector('.toc-container');
|
||||
this.onIdle(() => this.init());
|
||||
}
|
||||
|
||||
private scrollActiveIntoView(link: HTMLAnchorElement): void {
|
||||
if (!this.tocContainer) return;
|
||||
|
||||
// Check if user prefers reduced motion
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
// Get the position of the link relative to the TOC container
|
||||
const containerRect = this.tocContainer.getBoundingClientRect();
|
||||
const linkRect = link.getBoundingClientRect();
|
||||
|
||||
// Calculate if the link is outside the visible area
|
||||
const isAboveView = linkRect.top < containerRect.top;
|
||||
const isBelowView = linkRect.bottom > containerRect.bottom;
|
||||
|
||||
if (isAboveView || isBelowView) {
|
||||
// Scroll the link into view with some padding
|
||||
const offsetTop = link.offsetTop - this.tocContainer.offsetTop;
|
||||
const scrollPosition = offsetTop - this.tocContainer.clientHeight / 2 + link.clientHeight / 2;
|
||||
|
||||
this.tocContainer.scrollTo({
|
||||
top: Math.max(0, scrollPosition),
|
||||
behavior: prefersReducedMotion ? 'auto' : 'smooth'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private init = (): void => {
|
||||
const links = Array.from(this.querySelectorAll('a'))
|
||||
|
||||
const getElementHeading = (el: Element): HTMLHeadingElement | null => {
|
||||
if (!el) return null;
|
||||
const isHeading = (el: Element): el is HTMLHeadingElement => {
|
||||
if (el instanceof HTMLHeadingElement) {
|
||||
if (el.id === PAGE_TITLE_ID) return true;
|
||||
const level = el.tagName[1];
|
||||
if (level) {
|
||||
const int = parseInt(level, 10);
|
||||
if (int >= this.minH && int <= this.maxH) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return isHeading(el) ? el : getElementHeading(el.parentElement!);
|
||||
};
|
||||
|
||||
const setCurrent: IntersectionObserverCallback = (entries) => {
|
||||
for (const { isIntersecting, target } of entries) {
|
||||
if (!isIntersecting) continue;
|
||||
const heading = getElementHeading(target);
|
||||
if (!heading) continue;
|
||||
const link = links.find((link) => link.hash === '#' + encodeURIComponent(heading.id));
|
||||
if (link) {
|
||||
this.current = link;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const toObserve = document.querySelectorAll('main [id], main [id] ~ *, main .content > *');
|
||||
let observer: IntersectionObserver | undefined;
|
||||
|
||||
const observe = () => {
|
||||
if (observer) observer.disconnect();
|
||||
observer = new IntersectionObserver(setCurrent, { rootMargin: this.getRootMargin() });
|
||||
toObserve.forEach((h) => observer!.observe(h));
|
||||
};
|
||||
|
||||
observe();
|
||||
let timeout: NodeJS.Timeout;
|
||||
// Re-observe on resize to adjust for layout changes
|
||||
window.addEventListener('resize', () => {
|
||||
if(observer) {
|
||||
observer.disconnect();
|
||||
observer = undefined;
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => this.onIdle(observe), 200)
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
private getRootMargin(): `-${number}px 0% ${number}px` {
|
||||
const navBarHeight = document.querySelector('header')?.getBoundingClientRect().height || 0;
|
||||
const mobileTocHeight = this.querySelector('summary')?.getBoundingClientRect().height || 0;
|
||||
// Account for footer by reducing bottom margin
|
||||
const footerHeight = document.querySelector('footer')?.getBoundingClientRect().height || 0;
|
||||
const top = navBarHeight + mobileTocHeight + 32;
|
||||
const bottom = top + 53;
|
||||
const height = document.documentElement.clientHeight;
|
||||
// Adjust for footer
|
||||
return `-${top}px 0% ${bottom - height + footerHeight}px`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('custom-toc', CustomTOC);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@layer starlight.core {
|
||||
custom-toc {
|
||||
display: block;
|
||||
}
|
||||
|
||||
custom-toc nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
custom-toc h2 {
|
||||
color: var(--sl-color-text);
|
||||
font-size: var(--sl-text-sm);
|
||||
font-weight: 600;
|
||||
line-height: var(--sl-line-height-headings);
|
||||
margin: 0 0 0.5rem 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Container with proper scrolling */
|
||||
.toc-container {
|
||||
/* Calculate height based on viewport minus nav and footer with padding */
|
||||
max-height: calc(100vh - var(--sl-nav-height) - 25rem);
|
||||
/*
|
||||
* prevent ToC from going to 0 height.
|
||||
* if this overlaps the footer it's not a huge issue for such small viewport
|
||||
* */
|
||||
min-height: 300px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Only enable smooth scrolling if user doesn't prefer reduced motion */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.toc-container {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
/* Scrollbar styling */
|
||||
.toc-container::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.toc-container::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.toc-container::-webkit-scrollbar-thumb {
|
||||
background: var(--sl-color-gray-5);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.toc-container:hover::-webkit-scrollbar-thumb {
|
||||
background: var(--sl-color-gray-4);
|
||||
}
|
||||
|
||||
/* Firefox scrollbar */
|
||||
.toc-container {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--sl-color-gray-5) transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
import type { TocItem } from '@astrojs/starlight/utils/generateToC';
|
||||
|
||||
interface Props {
|
||||
toc: TocItem[];
|
||||
depth?: number;
|
||||
isMobile?: boolean;
|
||||
}
|
||||
|
||||
const { toc, isMobile = false, depth = 0 } = Astro.props;
|
||||
---
|
||||
|
||||
<ul class:list={{ isMobile }}>
|
||||
{
|
||||
toc.map((heading) => (
|
||||
<li>
|
||||
<a href={'#' + heading.slug}>
|
||||
<span>{heading.text}</span>
|
||||
</a>
|
||||
{heading.children.length > 0 && (
|
||||
<Astro.self toc={heading.children} depth={depth + 1} isMobile={isMobile} />
|
||||
)}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
<style define:vars={{ depth }}>
|
||||
@layer starlight.core {
|
||||
ul {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
a {
|
||||
--pad-inline: 0.5rem;
|
||||
display: block;
|
||||
border-radius: 0.25rem;
|
||||
padding-block: 0.25rem;
|
||||
padding-inline: calc(1rem * var(--depth) + var(--pad-inline)) var(--pad-inline);
|
||||
line-height: 1.25;
|
||||
color: var(--sl-color-gray-3);
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
font-size: var(--sl-text-sm);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--sl-color-gray-2);
|
||||
}
|
||||
|
||||
a[aria-current='true'] {
|
||||
color: var(--sl-color-text-accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a:focus-visible {
|
||||
outline: 2px solid var(--sl-color-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media (min-width: 50rem) {
|
||||
a {
|
||||
padding-inline-start: calc(1rem * var(--depth) + var(--pad-inline));
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
// Copied from https://github.com/withastro/starlight/blob/f14eb0c/packages/starlight/components/TwoColumnContent.astro with modifications.
|
||||
const { data} = Astro.locals.starlightRoute.entry;
|
||||
---
|
||||
|
||||
<div class="lg:sl-flex">
|
||||
@@ -12,7 +13,8 @@
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
<div class="main-pane" data-testid="main-pane"><slot /></div>
|
||||
<div class="main-pane" data-testid="main-pane" data-pagefind-weight={data.weight} data-pagefind-filter={data.filter}
|
||||
><slot /></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -3,6 +3,4 @@ import { type GraphProps, Graph as Default } from '@nx/nx-dev-ui-markdoc/src/lib
|
||||
type Props = GraphProps;
|
||||
---
|
||||
|
||||
<Default client:visible {...Astro.props} isAstro={true}>
|
||||
<slot />
|
||||
</Default>
|
||||
<Default client:visible {...Astro.props} />
|
||||
|
||||
@@ -48,10 +48,7 @@ const sortedPages = childPages.sort((a, b) => {
|
||||
const processedPages = sortedPages.map(page => {
|
||||
let href = `/${page.id.replace('.mdoc', '')}`;
|
||||
|
||||
// Check if this is a subdirectory index page
|
||||
const fileName = page.id.split('/').pop()?.replace('.mdoc', '');
|
||||
const isIndexPage = fileName === 'index';
|
||||
|
||||
// Check if this is a subdirectory index page
|
||||
const title = page.data.sidebar?.label || page.data.title || 'Untitled';
|
||||
|
||||
// Ensure href is never undefined
|
||||
@@ -68,7 +65,6 @@ const processedPages = sortedPages.map(page => {
|
||||
};
|
||||
}).filter(page => page.href !== '#'); // Filter out any pages with invalid hrefs
|
||||
---
|
||||
|
||||
{processedPages.length > 0 ? (
|
||||
<Cards>
|
||||
{processedPages.map(page => (
|
||||
|
||||
@@ -4,9 +4,7 @@ type Props = ProjectDetailsProps;
|
||||
---
|
||||
|
||||
<div class="pdv">
|
||||
<Default client:visible {...Astro.props} isAstro={true}>
|
||||
<slot />
|
||||
</Default>
|
||||
<Default client:visible {...Astro.props} />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -6,17 +6,30 @@ import { PluginLoader } from './plugins/plugin.loader';
|
||||
import { NxReferencePackagesLoader } from './plugins/nx-reference-packages.loader';
|
||||
import { CommunityPluginsLoader } from './plugins/community-plugins.loader';
|
||||
|
||||
const baseSchema = z.object({
|
||||
title: z.string(),
|
||||
/**
|
||||
* Slug should be from the root route without any prefix requirements i.e. `/docs`
|
||||
**/
|
||||
slug: z.string(),
|
||||
const searchSchema = z.object({
|
||||
weight: z
|
||||
.number()
|
||||
.min(0, 'Search weight cannot be lower than 0')
|
||||
.max(10, 'Search weight cannot be higher than 10')
|
||||
.optional(),
|
||||
filter: z.string().optional(),
|
||||
});
|
||||
|
||||
const baseSchema = z
|
||||
.object({
|
||||
title: z.string(),
|
||||
/**
|
||||
* Slug should be from the root route without any prefix requirements i.e. `/docs`
|
||||
**/
|
||||
slug: z.string(),
|
||||
})
|
||||
.and(searchSchema);
|
||||
// Default docs collection handled by Starlight
|
||||
const docs = defineCollection({
|
||||
loader: docsLoader(),
|
||||
schema: docsSchema(),
|
||||
schema: docsSchema({
|
||||
extend: searchSchema,
|
||||
}),
|
||||
});
|
||||
|
||||
const nxReferencePackages = defineCollection({
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Nx Cloud AI
|
||||
sidebar:
|
||||
order: 100
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Nx Cloud AI offers a suite of features designed to **enhance your development workflow with AI-driven capabilities**. These tools assist in troubleshooting, optimizing resource allocation, and improving your overall development process.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: Building Blocks of Fast CI
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Nx has many features that make your CI faster. Each of these features speeds up your CI in a different way, so that enabling an individual feature will have an immediate impact. These features are also designed to complement each other so that you can use them together to create a fully optimized CI pipeline.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: Cache Security
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
{% aside type="caution" title="Use Caution With Read-Write Tokens" %}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: Heartbeat and Main Job Completion Handling
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
### What is the heartbeat process?
|
||||
|
||||
@@ -3,6 +3,7 @@ title: CI Concepts
|
||||
sidebar:
|
||||
hidden: true
|
||||
description: Continuous Integration concepts and best practices
|
||||
pagefind: false
|
||||
---
|
||||
|
||||
{% index_page_cards path="concepts/ci-concepts" /%}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: Parallelization and Distribution
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Nx speeds up your CI in several ways. One method is to reduce wasted calculations with the [affected command](/docs/features/ci-features/affected) and [remote caching](/docs/features/ci-features/remote-cache). No matter how effective you are at eliminating wasted calculations in CI, there will always be some tasks that really do need to be executed and sometimes that list of tasks will be everything in the repository.
|
||||
@@ -32,7 +33,7 @@ You can either distribute tasks across machines manually, or use Nx Cloud distri
|
||||
|
||||
One way to manually distribute tasks is to use binning. Binning is a distribution strategy where there is a main job that divides the work into bins, one for each agent machine. Then every agent executes the work prepared for it. Here is a simplified version of the binning strategy.
|
||||
|
||||
```yaml
|
||||
```yaml
|
||||
// main-job.yml
|
||||
# Get the list of affected projects
|
||||
- nx show projects --affected --json > affected-projects.json
|
||||
@@ -41,13 +42,13 @@ One way to manually distribute tasks is to use binning. Binning is a distributio
|
||||
- node storeAffectedProjects.js
|
||||
```
|
||||
|
||||
```yaml
|
||||
```yaml
|
||||
// lint-agent.yml
|
||||
# Run lint for all projects defined in PROJECTS
|
||||
- nx run-many --projects=$PROJECTS -t lint
|
||||
```
|
||||
|
||||
```yaml
|
||||
```yaml
|
||||
// test-agent.yml
|
||||
# Run test for all projects defined in PROJECTS
|
||||
- nx run-many --projects=$PROJECTS -t test
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: Reduce Wasted Time in CI
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
This article explores two ways that Nx improves the average speed of your CI pipeline - `nx affected` and remote caching. Using the `nx affected` command speeds up the first CI run for a PR and remote caching speeds up every CI run after that. Both `nx affected` and remote caching provide more benefits to repositories with more projects and a flatter project structure.
|
||||
@@ -19,6 +20,8 @@ If we look at these two trivial examples, you can see that the repository with m
|
||||
{% cards smCols=2 mdCols=2 lgCols=2 %}
|
||||
|
||||
{% graph title="One Project" height="200px" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"hash": "85fd0561bd88f0bcd8703a9e9369592e2805f390d04982fb2401e700dc9ebc59",
|
||||
"projects": [
|
||||
@@ -41,10 +44,13 @@ If we look at these two trivial examples, you can see that the repository with m
|
||||
"groupByFolder": false,
|
||||
"exclude": []
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
{% graph title="Four Projects" height="200px" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"hash": "85fd0561bd88f0bcd8703a9e9369592e2805f390d04982fb2401e700dc9ebc59",
|
||||
"projects": [
|
||||
@@ -89,6 +95,8 @@ If we look at these two trivial examples, you can see that the repository with m
|
||||
"groupByFolder": false,
|
||||
"exclude": []
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
{% /cards %}
|
||||
@@ -104,6 +112,8 @@ Consider the following example repo structures.
|
||||
{% cards mdCols=3 lgCols=3 %}
|
||||
|
||||
{% graph title="Stacked" height="200px" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"hash": "85fd0561bd88f0bcd8703a9e9369592e2805f390d04982fb2401e700dc9ebc59",
|
||||
"projects": [
|
||||
@@ -140,9 +150,13 @@ Consider the following example repo structures.
|
||||
"groupByFolder": false,
|
||||
"exclude": []
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
{% graph title="Grouped" height="200px" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"hash": "85fd0561bd88f0bcd8703a9e9369592e2805f390d04982fb2401e700dc9ebc59",
|
||||
"projects": [
|
||||
@@ -179,10 +193,13 @@ Consider the following example repo structures.
|
||||
"groupByFolder": false,
|
||||
"exclude": []
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
{% graph title="Flat" height="200px" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"hash": "85fd0561bd88f0bcd8703a9e9369592e2805f390d04982fb2401e700dc9ebc59",
|
||||
"projects": [
|
||||
@@ -219,6 +236,8 @@ Consider the following example repo structures.
|
||||
"groupByFolder": false,
|
||||
"exclude": []
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
{% /cards %}
|
||||
@@ -290,6 +309,8 @@ Take a look at the example below. The projects are setup in the suboptimal stack
|
||||
|
||||
{% cards smCols=2 mdCols=2 lgCols=2 %}
|
||||
{% graph title="First CI Run" height="200px" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"hash": "85fd0561bd88f0bcd8703a9e9369592e2805f390d04982fb2401e700dc9ebc59",
|
||||
"projects": [
|
||||
@@ -326,9 +347,13 @@ Take a look at the example below. The projects are setup in the suboptimal stack
|
||||
"groupByFolder": false,
|
||||
"exclude": []
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
{% graph title="Second CI Run (project2 Changed)" height="200px" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"hash": "85fd0561bd88f0bcd8703a9e9369592e2805f390d04982fb2401e700dc9ebc59",
|
||||
"projects": [
|
||||
@@ -365,6 +390,8 @@ Take a look at the example below. The projects are setup in the suboptimal stack
|
||||
"groupByFolder": false,
|
||||
"exclude": []
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
{% /cards %}
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Code Ownership
|
||||
description: Learn about code ownership challenges in monorepos and how Nx helps manage shared code with tools like CODEOWNERS and module boundary rules.
|
||||
sidebar:
|
||||
order: 100
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
One of the most obvious benefits of having a monorepo is that you can easily share code across projects. This enables you to apply the Don't Repeat Yourself principle across the whole codebase. Code sharing could mean using a function or a component in multiple projects. Or code sharing could mean using a typescript interface to define the network API interface for both the front end and back end applications.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Dependency Management Strategies
|
||||
description: Compare independently maintained dependencies versus single version policy approaches for monorepos, with guidance on choosing the right strategy for your team.
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
When working with a monorepo, one of the key architectural decisions is how to manage dependencies across your projects. This document outlines two main strategies and helps you choose the right approach for your team.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Folder Structure
|
||||
description: Learn about organizing your Nx monorepo with effective folder structures, and how to easily move or remove projects as your organization evolves.
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Nx can work with any folder structure you choose, but it is good to have a plan in place for the folder structure of your monorepo.
|
||||
@@ -32,6 +33,7 @@ Let's use Nrwl Airlines as an example organization. This organization has two ap
|
||||
The purpose of these folders is to help with organizing by scope. We recommend grouping projects together which are (usually) updated together. It helps minimize the amount of time a developer spends navigating the folder tree to find the right file.
|
||||
|
||||
{% filetree %}
|
||||
|
||||
- apps/
|
||||
- booking/
|
||||
- check-in/
|
||||
@@ -45,6 +47,7 @@ The purpose of these folders is to help with organizing by scope. We recommend g
|
||||
- seatmap/ <---- grouping folder
|
||||
- data-access/ <---- project
|
||||
- feature-seatmap/ <---- project
|
||||
|
||||
{% /filetree %}
|
||||
|
||||
## Sharing Projects
|
||||
@@ -54,6 +57,7 @@ One of the main advantages of using a monorepo is that there is more visibility
|
||||
Let's consider our reference monorepo. The `shared-data-access` project contains the code needed to communicate with the back-end (for example, the URL prefix). We know that this would be the same for all libs; therefore, we should place this in the shared lib and properly document it so that all projects can use it instead of writing their own versions.
|
||||
|
||||
{% filetree %}
|
||||
|
||||
- libs/
|
||||
- booking/
|
||||
- data-access/ <---- app-specific project
|
||||
@@ -62,4 +66,5 @@ Let's consider our reference monorepo. The `shared-data-access` project contains
|
||||
- seatmap/
|
||||
- data-access/ <---- shared project
|
||||
- feature-seatmap/ <---- shared project
|
||||
|
||||
{% /filetree %}
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Architectural Decisions
|
||||
sidebar:
|
||||
hidden: true
|
||||
description: Key architectural decisions and patterns
|
||||
pagefind: false
|
||||
---
|
||||
|
||||
{% index_page_cards path="concepts/decisions" /%}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Monorepo or Polyrepo
|
||||
description: Evaluate the organizational considerations for choosing between monorepo and polyrepo approaches, including team agreements on code management and workflows.
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Monorepos have a lot of benefits, but there are also some costs involved. We feel strongly that the [technical challenges](/docs/concepts/decisions/why-monorepos) involved in maintaining large monorepos are fully addressed through the efficient use of Nx and Nx Cloud. Rather, the limiting factors in how large your monorepo grows are interpersonal.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Project Dependency Rules
|
||||
description: Learn how to organize your Nx workspace with library types like feature, UI, data-access, and utility libraries, and enforce dependency rules between them.
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
There are many types of libraries in a workspace. You can identify the type of a library through a naming convention and/or by using the project tagging system. With explicitly defined types, you can also use Nx to enforce project dependency rules based on the types of each project. This article explains one possible way to organize your repository projects by type. Every repository is different and yours may need a different set of types.
|
||||
@@ -40,12 +41,13 @@ A feature library contains a set of files that configure a business use case or
|
||||
A feature library can depend on any type of library.
|
||||
|
||||
{% filetree %}
|
||||
|
||||
- libs/
|
||||
- my-app/
|
||||
- feature-home/
|
||||
- src/
|
||||
- index.ts
|
||||
- lib/
|
||||
- feature-home/
|
||||
- src/
|
||||
- index.ts
|
||||
- lib/
|
||||
|
||||
{% /filetree %}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Project Size
|
||||
description: Understand the trade-offs of project granularity in Nx, including benefits like faster commands, clearer boundaries, and improved developer experience.
|
||||
keywords: [library]
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Like a lot of decisions in programming, deciding to make a new Nx project or not is all about trade-offs. Each organization will decide on their own conventions, but here are some trade-offs to bear in mind as you have the conversation.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Monorepos
|
||||
description: Understand the benefits of monorepos including shared code, atomic changes, developer mobility, and consistent dependencies across your organization.
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
A monorepo is a single git repository that holds the source code for multiple applications and libraries, along with the tooling for them.
|
||||
@@ -37,7 +38,7 @@ Nx provides tools to give you the benefits of a monorepo without the drawbacks o
|
||||
|
||||
- **Consistent Code Generation** - Generators allow you to customize and standardize organizational conventions and structure, removing the need to perform the same manual setup tasks repetitively.
|
||||
|
||||
- **Affected Commands** - [Nx's affected commands](/docs/reference/nx-commands#affected) analyze your source code, the context of the changes, and only runs tasks on the affected projects impacted by the source code changes.
|
||||
- **Affected Commands** - [Nx's affected commands](/docs/reference/nx-commands#nx-affected) analyze your source code, the context of the changes, and only runs tasks on the affected projects impacted by the source code changes.
|
||||
|
||||
- **Remote Caching** - Nx provides local caching and support for remote caching of command executions. With remote caching, when someone on your team runs a command, everyone else gets access to those artifacts to speed up their command executions, bringing them down from minutes to seconds. Nx helps you scale your development to massive applications and libraries even more with distributed task execution and incremental builds.
|
||||
|
||||
|
||||
@@ -1,485 +0,0 @@
|
||||
---
|
||||
title: Faster Builds with Module Federation
|
||||
description: Learn how Module Federation in Nx enables faster builds by splitting large SPAs into smaller remote applications while minimizing common downsides.
|
||||
sidebar:
|
||||
order: 100
|
||||
---
|
||||
|
||||
As applications grow, builds can become unacceptably slow, which leads to slow CI/CD pipelines and long dev-server
|
||||
startup times. This slowness
|
||||
decreases team productivity due to long waits for local compilation, and clogged up CI/CD pipelines.
|
||||
|
||||
Module Federation provides a solution to the scaling problem by allowing a Single Page Application (SPA) to be sliced
|
||||
into multiple smaller
|
||||
remote applications that are built independently. There are some costs to slicing an application through Module
|
||||
Federation:
|
||||
|
||||
- Cognitive overhead of developing multiple applications versus a SPA.
|
||||
- Coordinating build and deployment across many applications is a huge headache, especially in a multi-repo setup.
|
||||
- Version-Mismatch-Hell where different applications are deployed with different versions of shared libraries can lead
|
||||
to unexpected errors.
|
||||
|
||||
Nx provides the best experience for teams that want faster builds, but want to also minimize the downsides that come
|
||||
with Module Federation. Starting in Nx 14,
|
||||
we provide specialized generators, executors, and utilities that make Module Federation easy to set up and work with.
|
||||
|
||||
## When should I use Module Federation?
|
||||
|
||||
Whether Module Federation makes sense for your team depends on the size of your application. Although Nx hides most of
|
||||
the complexity around Module Federation, it still comes with some downsides:
|
||||
|
||||
- Developers need to think about which remotes they are working on, since it is a waste of CPU and memory to run _all_
|
||||
remotes in development mode. In practice this may not be a problem if the teams are already divided by domain or
|
||||
feature.
|
||||
- Increased orchestration since remotes are independent of each other, shared state may require the host application to
|
||||
coordinate it between remotes. For example, sharing Redux state across remotes is more complicated versus a SPA.
|
||||
|
||||
{% aside type="note" title="Nx Saves Machine Resources" %}
|
||||
As Nx knows the remote applications that your host application depends on, it will serve your remote applications automatically when you serve your host.
|
||||
To prevent dev machines from running out of resources, and allowing for a smoother DX, a technique is employed to build your remote applications first, then
|
||||
they will all be served by a single file server (`http-server`).
|
||||
|
||||
We have tested this in a workspace that contains 100 remote applications and found that it allows for scaling the DX very well, without causing resource issues.
|
||||
{% /aside %}
|
||||
|
||||
## Architectural overview
|
||||
|
||||
With Module Federation, a large application is split into:
|
||||
|
||||
1. A single **Host** application that references external...
|
||||
2. **Remote** applications, which handle a single domain or feature.
|
||||
|
||||
In the next section, we will see an example with a host app (`host`) and three remotes (`shop`, `cart`, `about`).
|
||||
Although all the applications are independently built, thus have no dependency between them, conceptually you can think
|
||||
of them in the following hierarchy.
|
||||
|
||||

|
||||
|
||||
## Creating an example workspace
|
||||
|
||||
The best way to understand the setup is through an example. In this section, we will create a `host` application with
|
||||
three remotes under these routes:
|
||||
|
||||
1. `/shop`
|
||||
1. `/cart`
|
||||
1. `/about`
|
||||
|
||||
But before we begin, we've put together a couple of example repos (React and Angular) for you to inspect if you want to
|
||||
skip ahead.
|
||||
|
||||
{% github_repository url="https://github.com/nrwl/ng-module-federation" /%}
|
||||
|
||||
{% github_repository url="https://github.com/nrwl/react-module-federation" /%}
|
||||
|
||||
These examples have fully
|
||||
functioning [CI](https://github.com/nrwl/react-module-federation/blob/main/.github/workflows/ci.yml) [workflows](https://github.com/nrwl/ng-module-federation/blob/main/.github/workflows/ci.yml)
|
||||
that are simple to set up. You can see what the CI does by viewing the sample pull requests in each repo. Also notice
|
||||
the [Nx Cloud](https://nx.app) integration, which gives you insight into each pipeline. We'll touch on
|
||||
this [later in this guide](#remote-computation-caching-with-nx-cloud).
|
||||
|
||||

|
||||
|
||||
Now, let's continue by creating an empty Nx workspace.
|
||||
|
||||
```shell {% frame="none" %}
|
||||
# Replace acme with desired scope
|
||||
npx create-nx-workspace acme --preset=apps
|
||||
cd acme
|
||||
```
|
||||
|
||||
{% aside type="tip" title="Enabling remote caching" %}
|
||||
You will be prompted to enable Nx Cloud in the workspace. For the best experience, we highly recommend using Nx Cloud to
|
||||
take advantage of remote caching and other features it provides.
|
||||
{% /aside %}
|
||||
|
||||
Then, for React users, install the `@nx/react` plugin; and for Angular users, install the `@nx/angular` plugin.
|
||||
|
||||
```shell {% frame="none" %}
|
||||
# If you use React
|
||||
nx add @nx/react
|
||||
|
||||
# If you use Angular
|
||||
nx add @nx/angular
|
||||
```
|
||||
|
||||
Next, generate the host and remote applications.
|
||||
|
||||
{% tabs %}
|
||||
{% tabitem label="React" %}
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx g @nx/react:host apps/host --remotes=shop,cart,about
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
{% tabitem label="Angular" %}
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx g @nx/angular:host apps/host --remotes=shop,cart,about
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
{% /tabs %}
|
||||
|
||||
{% aside type="note" title="More details" %}
|
||||
You can leave off the `--remotes` option and add them later with `nx g @nx/react:remote apps/shop --host=host`
|
||||
or `nx g @nx/angular:remote apps/shop --host=host`.
|
||||
{% /aside %}
|
||||
|
||||
Now, serve `host` to view it in your browser.
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx serve host --open
|
||||
```
|
||||
|
||||
The above command serves `host` in development mode, whereas the remotes are built and served statically. That is,
|
||||
changes to `host` will update its bundle, but changes to remotes will not update.
|
||||
|
||||
To run one or more remotes in development mode, use the `--devRemotes` option.
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx serve host --open --devRemotes="shop,cart"
|
||||
```
|
||||
|
||||
The above command starts the `shop` and `cart` remotes in development mode, but `about` will remain static.
|
||||
|
||||
{% aside type="note" title="More details" %}
|
||||
Both commands serve the whole system. By passing `--devRemotes`, you configure what parts of it you will be changing.
|
||||
For instance, in the example above, you can go to the about page and back. This is different from having different
|
||||
versions of the app for every team.
|
||||
{% /aside %}
|
||||
|
||||
## What was generated?
|
||||
|
||||
To understand how Module Federation works with Nx, let's take a look at three files that control this feature.
|
||||
|
||||
### `apps/host/project.json`
|
||||
|
||||
The `build` target uses `@nx/webpack:webpack` for React, and `@nx/angular:webpack-browser` for Angular. This is the same
|
||||
as a normal SPA that uses custom webpack configuration (`webpackConfig`), but difference is in the webpack configuration
|
||||
file.
|
||||
|
||||
If you use Module Federation to speed up your CI and improve your local development, and not to deploy different remotes
|
||||
independently, you need to create implicit dependencies from the host to all the remotes. Semantically, the host and the
|
||||
remotes comprise one application, so you cannot build the host without the remotes. Adding implicit dependencies also
|
||||
makes distributed builds possible ([see below](#production-build-and-deployment)). To create these dependencies, add
|
||||
the `implicitDependencies` configuration.
|
||||
|
||||
```text
|
||||
// apps/host/project.json
|
||||
{
|
||||
//...
|
||||
"implicitDependencies": ["about", "shop", "cart"]
|
||||
}
|
||||
```
|
||||
|
||||
In the future, Nx may automatically handle this for you.
|
||||
|
||||
### `apps/host/webpack.config.ts`
|
||||
|
||||
The webpack configuration uses an utility function that Nx provides: `withModuleFederation`.
|
||||
|
||||
```javascript
|
||||
// For Angular, you'll see `@nx/angular/module-federation`
|
||||
import { withModuleFederation } from '@nx/react/module-federation';
|
||||
import moduleFederationConfig from './module-federation.config';
|
||||
|
||||
export default withModuleFederation({
|
||||
...moduleFederationConfig,
|
||||
});
|
||||
```
|
||||
|
||||
We'll talk about [what `withModuleFederation` does](#what-does-withmodulefederation-do) in a bit, but for now the
|
||||
important part of the configuration is the use of `module-federation.config.ts` which we will examine next.
|
||||
|
||||
{% aside type="note" title="Extending the Webpack Config" %}
|
||||
If you find yourself needing to add additional webpack plugins to your config, you can do so by following the pattern
|
||||
below. This example shows how you could use it to add the `LicenseWebpackPlugin` but you can use this method to apply
|
||||
any webpack plugins you need.
|
||||
|
||||
```js
|
||||
import { withModuleFederation } from '@nx/angular/module-federation';
|
||||
import config from './module-federation.config';
|
||||
import { LicenseWebpackPlugin } from 'license-webpack-plugin';
|
||||
import { resolve } from 'path';
|
||||
|
||||
export default async function (wco) {
|
||||
const wmf = await withModuleFederation(config);
|
||||
return wmf({
|
||||
...wco,
|
||||
plugins: [
|
||||
...(wco.plugins ?? []),
|
||||
new LicenseWebpackPlugin({
|
||||
stats: {
|
||||
warnings: false,
|
||||
errors: false,
|
||||
},
|
||||
perChunkOutput: false,
|
||||
outputFilename: '3rdpartylicenses.txt',
|
||||
skipChildCompilers: true,
|
||||
modulesDirectories: [resolve(__dirname, '../../node_modules')],
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
{% /aside %}
|
||||
|
||||
### `apps/host/module-federation.config.ts`
|
||||
|
||||
This file is the main configuration for the `host`, and you'll see `module-federation.config.ts` for the generated
|
||||
remotes as well.
|
||||
|
||||
```javascript
|
||||
import { ModuleFederationConfig } from '@nx/webpack';
|
||||
|
||||
export const config: ModuleFederationConfig = {
|
||||
name: 'host',
|
||||
remotes: ['shop', 'cart', 'about'],
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
The required `name` property is the magic to link the host and remotes together. The `host` application references the
|
||||
three remotes by their names.
|
||||
|
||||
{% aside type="note" title="More details" %}
|
||||
It is important that the values in `remotes` property matches the `name` property of the remote applications. Otherwise,
|
||||
webpack will throw an error. Nx handles this automatically for you so there shouldn't be an issue unless it was modified
|
||||
manually.
|
||||
{% /aside %}
|
||||
|
||||
## What does `withModuleFederation` do?
|
||||
|
||||
In the previous section, we saw `withModuleFederation` used in the webpack config. This function is an abstraction on
|
||||
top of `@module-federation/enhanced` with some Nx-specific behavior.
|
||||
|
||||
- All libraries (npm and workspace) are shared singletons by default, so you don't manually configure them.
|
||||
- Remotes are referenced by name only, since Nx knows which ports each remote is running on (in development mode).
|
||||
- Access to the latest features of Module Federation (such as RuntimePlugins). Learn more at [module-federation.io](https://module-federation.io).
|
||||
|
||||
With Nx, the developer experience (DX) when working with Module Federation matches more closely to development on a SPA.
|
||||
You don't have to worry about managing a bunch of configuration, and most things just work out of the box.
|
||||
|
||||
### Excluding or overriding shared libraries
|
||||
|
||||
There are cases where excluding or changing the shared configuration is required. For example, shared libraries are not
|
||||
tree shaken, so to enable this behavior you must exclude them from being shared.
|
||||
|
||||
To exclude a library or change its configuration, you can provide
|
||||
the `shared: (libraryName, sharedConfig) => sharedConfig` function in your configuration file.
|
||||
|
||||
```javascript
|
||||
// module-federation.config.ts
|
||||
import { ModuleFederationConfig } from '@nx/webpack';
|
||||
|
||||
export const config: ModuleFederationConfig = {
|
||||
name: 'host',
|
||||
remotes: ['shop', 'cart', 'about'],
|
||||
shared: (name, config) => {
|
||||
// We want lodash to be tree shaken, and bundled into each host/remote separately.
|
||||
if (name === 'lodash') {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The `shared` function can return an `undefined` to use Nx's default value, `false` to exclude it from being shared, or
|
||||
a [shared config](https://webpack.js.org/plugins/module-federation-plugin/#sharing-hints) that webpack supports.
|
||||
|
||||
{% aside type="note" title="Analysis" %}
|
||||
The default configuration, without overrides, should work well for most workspaces, and we encourage you to analyze your
|
||||
bundles before optimizing the shared behavior.
|
||||
|
||||
To analyze the size of your bundles, run build with `--statsJson` and use a tool
|
||||
like [`webpack-bundle-analyzer`](https://www.npmjs.com/package/webpack-bundle-analyzer).
|
||||
|
||||
If you have any feedback regarding this feature, we'd love to hear from you--check
|
||||
our [community page](https://nx.dev/community) for links to our Discord and Twitter.
|
||||
{% /aside %}
|
||||
|
||||
## Remote computation caching with Nx Cloud
|
||||
|
||||
To use Module Federation well, we recommend that you enable [Nx Cloud](https://nx.app). If you haven't enabled it yet
|
||||
when using `create-nx-workspace`, create an account at [https://cloud.nx.app](https://cloud.nx.app) and connect to your repository.
|
||||
|
||||
With Nx Cloud enabled, a large set of builds can be skipped entirely when running the application locally (and in
|
||||
CI/CD). When you run builds through Nx + Nx Cloud, the artifacts are stored in the remote cache, so as long as the
|
||||
source of a given remote hasn't changed, it will be served from cache.
|
||||
|
||||
You can see this behavior locally if you serve the `host` twice.
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx serve host
|
||||
|
||||
# (kill server)
|
||||
|
||||
nx serve host
|
||||
```
|
||||
|
||||
The second serve starts up much faster, because the three remotes (`shop`, `cart`, `about`) are read from cache. Not
|
||||
only that, any other copy of the workspace will also benefit from the cache if they haven't changed a particular remote.
|
||||
If, say, someone is working on `shop`, they will get the `cart` and `about` builds from the cache.
|
||||
|
||||
If you inspect the terminal output, you'll see something like this, even if you are on different machines.
|
||||
|
||||
```shell {% frame="none" %}
|
||||
> nx run about:build:development [existing outputs match the cache, left as is]
|
||||
|
||||
(snip)
|
||||
|
||||
NX Successfully ran target build for project about
|
||||
|
||||
Nx read the output from the cache instead of running the command for 1 out of 1 tasks.
|
||||
|
||||
```
|
||||
|
||||
{% aside type="note" title="More details" %}
|
||||
This caching behavior is _crucial_. If you don't have a build system supporting remote computation caching, using
|
||||
Module Federation will be slower. It takes longer to build `shop`, `cart` and `about` separately than building all of
|
||||
them together as part of the same process.
|
||||
|
||||
**When using Nx, you rarely have to build all of them because most of the time you work on one remote, other remotes
|
||||
will be retrieved from cache.**
|
||||
{% /aside %}
|
||||
|
||||
This also helps things like end-to-end (E2E) testing because testing against a static server is much more efficient than
|
||||
starting many servers in development mode. When the CI pipeline runs E2E tests, all the remotes should be served
|
||||
statically from cache.
|
||||
|
||||
In addition to computation caching, Nx Cloud also comes with:
|
||||
|
||||
- Distributed task execution, which simplifies your CI/CD setup, and speeds up your builds.
|
||||
- GitHub integration, so you can easily access important information without digging through a bunch of CI/CD logs.
|
||||
- Actionable insights, which improve caching and task distribution.
|
||||
|
||||

|
||||
|
||||
## Production build and deployment with Nx Cloud
|
||||
|
||||
In this section, we'll examine how to set up your production build and simulate a deployment to `http://localhost:3000`.
|
||||
|
||||
First, make sure you have implicit dependencies from `host` to each remote. In case you didn't already set this up, add
|
||||
the following line to the `host`'s project configuration.
|
||||
|
||||
```jsonc title="apps/host/project.json"
|
||||
{
|
||||
//...
|
||||
"implicitDependencies": ["about", "shop", "cart"]
|
||||
}
|
||||
```
|
||||
|
||||
Next, open up the production webpack configuration file and update the remote URLs to their own sub-folder
|
||||
under `http://localhost:3000`.
|
||||
|
||||
```javascript title="apps/host/webpack.config.prod.js"
|
||||
import { withModuleFederation } from '@nx/react/module-federation';
|
||||
import moduleFederationConfig from './module-federation.config';
|
||||
|
||||
export default withModuleFederation({
|
||||
...moduleFederationConfig,
|
||||
remotes: [
|
||||
['shop', 'http://localhost:3000/shop'],
|
||||
['cart', 'http://localhost:3000/cart'],
|
||||
['about', 'http://localhost:3000/about'],
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
Now you can run `nx build host` to build all the `host` and all the implicit dependencies in production mode.
|
||||
|
||||
{% aside type="note" title="Remote caching" %}
|
||||
Again, if you don't use [Nx Cloud's Distributed Tasks Execution](/docs/features/ci-features/distribute-task-execution) using Module Federation will be slower
|
||||
than building everything in a single process. It's only if you enable Distributed Tasks Execution, your CI will be able
|
||||
to build each remote on a separate machine, in parallel, (or not build it at all and retrieve it from cache), which will
|
||||
reduce the CI time.
|
||||
{% /aside %}
|
||||
|
||||
After running that command you'll see the following artifacts in `dist` folder.
|
||||
|
||||
```text
|
||||
dist/apps
|
||||
├── about
|
||||
├── cart
|
||||
├── host
|
||||
└── shop
|
||||
```
|
||||
|
||||
Now, we can add a simple deploy command to simulate deploying this folder to production.
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx g @nx/workspace:run-commands \
|
||||
deploy \
|
||||
--project=host \
|
||||
--command="rm -rf production && mkdir production && cp -r dist/apps/host/* production && cp -r dist/apps/{shop,cart,about} production && http-server -p 3000 -a localhost production"
|
||||
```
|
||||
|
||||
You can then run `nx deploy host` to see the application running on `http://localhost:3000`. If you inspect
|
||||
the `production` folder you'll see the following files.
|
||||
|
||||
```text
|
||||
production/
|
||||
├── about
|
||||
│ ├── remoteEntry.js
|
||||
│ └── (snip)
|
||||
├── cart
|
||||
│ ├── remoteEntry.js
|
||||
│ └── (snip)
|
||||
├── shop
|
||||
│ ├── remoteEntry.js
|
||||
│ └── (snip)
|
||||
├── index.html
|
||||
└── (snip)
|
||||
```
|
||||
|
||||
The above command is just an example. You'll need to use what make sense for your team and workspace.
|
||||
|
||||
For examples of how CI/CD pipelines can be configured using Nx Cloud and GitHub, see
|
||||
our [React](https://github.com/nrwl/react-module-federation)
|
||||
and [Angular](https://github.com/nrwl/ng-module-federation) examples.
|
||||
|
||||
{% github_repository url="https://github.com/nrwl/ng-module-federation" /%}
|
||||
|
||||
{% github_repository url="https://github.com/nrwl/react-module-federation" /%}
|
||||
|
||||
## Using buildable libs
|
||||
|
||||
By using Module Federation you essentially split your application build process vertically. You can also split it
|
||||
horizontally by making some libraries buildable.
|
||||
|
||||
We don't recommend making all libraries in your workspace buildable--it will make some things faster but many other
|
||||
things slower. But in some scenarios making a few large libraries at the bottom of your graph buildable can speed up
|
||||
your CI.
|
||||
|
||||
Because Nx Cloud's Distributed Tasks Execution works with any task graph, having buildable libraries is handled
|
||||
automatically. If you have a buildable `components` library that all remotes depend on, Nx Cloud will build the library
|
||||
first before building the remotes.
|
||||
|
||||
## Summary
|
||||
|
||||
You could use Module Federation to implement [micro frontends](/docs/technologies/module-federation/concepts/micro-frontend-architecture),
|
||||
but this
|
||||
guide showed how to use it to speed up your builds.
|
||||
|
||||
Module Federation allows you to split a single build process into multiple processes which can run in parallel or even
|
||||
on multiple machines. The result of each build process can be cached independently. For this to work well in practice
|
||||
you need to have a build system supporting remote computation caching and distributed tasks execution (e.g., Nx +
|
||||
Nx Cloud).
|
||||
|
||||
When a developer runs say `nx serve host --devRemotes=cart`, they still run the whole application, but `shop`
|
||||
and `about` are served statically, from cache. As a result, the serve time and the time it takes to see the changes on
|
||||
the screen go down, often by an order of magnitude.
|
||||
|
||||
When a CI machine runs say `nx build host --configuration=production`, the `shop`, `about` and `cart` remotes will
|
||||
either be build on separate machines or retrieved from cache. Once all of them are built, the build process for `host`
|
||||
will combine the file artifacts from all the remotes. Nx Cloud takes care of distributing the tasks and moving file
|
||||
artifacts across machines. As a result, the worst case scenario build time (when nothing is cached) goes from building
|
||||
all the code to building the largest remote, which is often an order of magnitude faster.
|
||||
|
||||
## Resources
|
||||
|
||||
- [React Module Federation example](https://github.com/nrwl/react-module-federation)
|
||||
- [Angular Module Federation example](https://github.com/nrwl/ng-module-federation)
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
title: Module Federation Concepts
|
||||
sidebar:
|
||||
hidden: true
|
||||
description: Core concepts of Module Federation
|
||||
---
|
||||
|
||||
{% index_page_cards path="concepts/module-federation" /%}
|
||||
@@ -1,156 +0,0 @@
|
||||
---
|
||||
title: Manage Library Versions with Module Federation
|
||||
description: Learn how Module Federation's Shared API prevents dependency conflicts and reduces bundle size by managing shared library versions across federated modules.
|
||||
---
|
||||
|
||||
Federated modules are bundled and packaged independently with all the dependencies they need to run smoothly in federated applications called _remotes_. This means that if you have a federated module that depends on a library, the library will be bundled with the federated module within a remote. This independence provides much flexibility, allowing individual federated modules to function without relying on external resources.
|
||||
|
||||
A challenge arises when these federated modules are integrated into a _host_ or other _remotes_. Given that each federated module carries its own dependencies, the host application may have inadvertently downloaded multiple copies of the same dependency. This redundancy does two things:
|
||||
|
||||
1. Multiple copies of the same dependency can create bottlenecks and conflicts, resulting in unexpected behaviour.
|
||||
2. With redundant dependencies, the host application can become bloated, increasing the bandwidth and consuming more memory and resources on the user's device.
|
||||
|
||||
To mitigate these issues, Module Federation has a shared API. Its primary function is to act as a gatekeeper, ensuring that only one copy of a dependency is downloaded, regardless of how many federated modules request it.
|
||||
|
||||
### How it works
|
||||
|
||||
The Shared API maintains a registry of all the downloaded dependencies. When a federated module requests a dependency, the Shared API checks the registry. If the dependency already exists, the module is directed to use the existing copy. If not, the dependency is downloaded and added to the registry.
|
||||
|
||||

|
||||
|
||||
{% aside type="note" title="Lost?" %}
|
||||
If you are not familiar with the concepts of federated modules, remotes, and hosts, please read the [Faster builds with module federation](/docs/technologies/module-federation/concepts/faster-builds-with-module-federation) for an introduction.
|
||||
{% /aside %}
|
||||
|
||||
## Our Approach
|
||||
|
||||
Although the Shared API is a powerful tool, it can be challenging to manage. The Shared API is configured in the Module Federation Config File, which is a JavaScript or TypeScript file. This file is not part of the build process, so should you want to use a different version of a workspace dependency, you would have to manually record the change outside of the build process which can be tedious and error-prone.
|
||||
|
||||
**Nx** recommends the Single Version Policy (SVP) for managing library versions. The SVP is a simple concept: a library should have only one version in a given application. This means that if you have a workspace library used by multiple remotes and hosts, it should only have one version across all of them. The SVP becomes essential in this context for a variety of reasons:
|
||||
|
||||
### 1. Consistency
|
||||
|
||||
Ensuring that all federated modules rely on the same version of a shared dependency provides consistent behaviour across the entire application. Different library versions can have varying behaviour or bugs, leading to unexpected or inconsistent results.
|
||||
|
||||
### 2. Conflicts
|
||||
|
||||
Mixing multiple versions of a library or module in the same runtime can lead to conflicts. This is especially problematic with libraries that maintain internal state or have side effects.
|
||||
|
||||
### 3. API Compatibility
|
||||
|
||||
As a library evolves, functions and methods get added, removed or changed. By ensuring a single version, you eliminate the risk of using incompatible APIs in one version but not another.
|
||||
|
||||
### 4. Singleton Libraries
|
||||
|
||||
Some libraries are designed to be singletons (React, Angular, Redux, etc.). These libraries are intended to be instantiated once and shared across the entire application. Multiple versions of such libraries can break the intended behaviour or even cause runtime errors.
|
||||
|
||||
For these reasons, we **recommend** using the SVP to manage shared workspace libraries when using Module Federation. However, we understand that there are cases where you may want to use different versions of a library. For example, you may use a different library version in a remote than in a host. In these cases, you can opt out as described below.
|
||||
|
||||
## How are library versions managed?
|
||||
|
||||
With **Nx** there are two ways to manage how library versions are shared / managed with Module Federation:
|
||||
|
||||
### 1. Opt in to sharing library versions
|
||||
|
||||
This is the default behaviour for **Nx**. All dependencies are **singletons** and will be shared between remotes and hosts.
|
||||
|
||||
### 2. Opt out from sharing library versions
|
||||
|
||||
This means that the library will not be shared between remotes and hosts. Each remote and host will load its own version of the library.
|
||||
A common use-case for this is if you want to enable tree-shaking for a library like _lodash_. If you share this library, it will be bundled with the remote and host, and tree-shaking will not be possible.
|
||||
|
||||
## How are library versions determined?
|
||||
|
||||
**Nx** determines the version of a library by looking at a `package.json`. If the library is an npm package, the version is determined by the version declared in the workspace `package.json`. If the library is a workspace library, the version is determined by the version in the `package.json` of the project that consumes the shared library. RemoteA consumes Counter, which is a workspace library exposed and shared by RemoteB. The version of Counter is determined by the version in RemoteB's `package.json`. If the `package.json` does not exist or the library is not declared, Nx will use the version in the `package.json` of the workspace library.
|
||||
|
||||

|
||||
|
||||
There are twos ways to manage library versions with **Nx**:
|
||||
|
||||
{% tabs %}
|
||||
{% tabitem label="Opt out from sharing library" %}
|
||||
|
||||
```ts title="remote/module-federation.config.ts"
|
||||
import { ModuleFederationConfig } from '@nx/webpack';
|
||||
|
||||
const config: ModuleFederationConfig = {
|
||||
name: 'remote',
|
||||
exposes: {
|
||||
'./Module': './src/remote-entry.ts',
|
||||
},
|
||||
// Determine which libraries to share
|
||||
shared: (packageName: string) {
|
||||
// I do not want to share this package and I will load my own version
|
||||
if(packageName === '@acme/utils') return false;
|
||||
}
|
||||
};
|
||||
export default config;
|
||||
```
|
||||
|
||||
This would result in the following webpack config:
|
||||
|
||||
```js title="webpack.config.js"
|
||||
module.exports = {
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
// additional config
|
||||
name: 'remote',
|
||||
shared: {
|
||||
react: { singleton: true, eager: true },
|
||||
// acme/utils will not be shared
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
{% tabitem label="Opt in to sharing library versions" %}
|
||||
|
||||
```ts title="remote/module-federation.config.ts"
|
||||
import { ModuleFederationConfig } from '@nx/webpack';
|
||||
|
||||
const config: ModuleFederationConfig = {
|
||||
name: 'remote',
|
||||
exposes: {
|
||||
'./Module': './src/remote-entry.ts',
|
||||
},
|
||||
// By not declaring a shared function, all dependencies will be shared
|
||||
};
|
||||
export default config;
|
||||
```
|
||||
|
||||
This would result in the following webpack config:
|
||||
|
||||
```js title="webpack.config.js"
|
||||
module.exports = {
|
||||
// Additional config ignored for brevity
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
// ...
|
||||
name: 'remote',
|
||||
shared: {
|
||||
react: { singleton: true, eager: true, version: '18.2.0' },
|
||||
'acme/utils': { singleton: true, eager: true, version: '1.0.0' }, // <--- This version is determined by the logic discussed earlier
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
{% /tabs %}
|
||||
|
||||
### Github Repository Example
|
||||
|
||||
Here is a working example of how to opt in and out of sharing library versions:
|
||||
|
||||
{% github_repository url="https://github.com/jaysoo/module-federation-example" /%}
|
||||
|
||||
## Benefits
|
||||
|
||||
By taking advantage of **Nx**'s approach to managing library versions, you can:
|
||||
|
||||
- Streamline the process of updating a library version behind a versoning scheme like SemVer.
|
||||
- Opt-in or out of sharing library versions with Module Federation based on your needs.
|
||||
- Reduce the download size of your application by sharing workspace libraries between remotes and hosts.
|
||||
@@ -1,182 +0,0 @@
|
||||
---
|
||||
title: Micro Frontend Architecture
|
||||
description: Explore how Nx supports Micro Frontend architecture with Module Federation, enabling independent deployment while managing associated challenges.
|
||||
---
|
||||
|
||||
Nx provides out-of-the-box [Module Federation](/docs/technologies/module-federation/concepts/faster-builds-with-module-federation) support to both
|
||||
React and Angular. The Micro Frontend (MFE) architecture builds on top of Module Federation by providing _independent
|
||||
deployability_.
|
||||
|
||||
If you have not read the [Module Federation guide](/docs/technologies/module-federation/concepts/faster-builds-with-module-federation)
|
||||
yet, we recommend that you read it
|
||||
before continuing with this MFE guide.
|
||||
|
||||
## When should I use micro frontend architecture?
|
||||
|
||||
We recommend MFE for teams that require applications to be deployed independently. It is important to consider the cost
|
||||
of MFEs and decide whether it makes sense for your own teams.
|
||||
|
||||
- Version mismatches where applications are deployed with different versions of shared libraries, which can lead to
|
||||
incompatibility issues.
|
||||
- Independent deployments can lead to unexpected errors, such as any host-level changes to orchestration/coordination
|
||||
logic that breaks compatibility with remotes.
|
||||
|
||||
If you are looking at optimizing builds and do not need independent deployments, we recommend reading our guide on
|
||||
[Faster Builds with Module Federation](/docs/technologies/module-federation/concepts/faster-builds-with-module-federation).
|
||||
|
||||
If you need to use MFEs, keep reading, and we'll examine the architecture and strategies to deal with shared libraries
|
||||
and
|
||||
deployments.
|
||||
|
||||
## Architectural overview
|
||||
|
||||
With MFE architecture, a large application is split into:
|
||||
|
||||
1. A single **Host** application that references external...
|
||||
2. **Remote** applications, which handle a single domain or feature.
|
||||
|
||||
In a normal Module Federation setup,
|
||||
we [recommend setting up implicit dependencies](/docs/technologies/module-federation/concepts/faster-builds-with-module-federation#architectural-overview)
|
||||
from the host application to remote applications. However, in an MFE architecture you _do not_ want these dependencies
|
||||
to exist between host and remotes.
|
||||
|
||||
For example, if you have a `shell` host application, with three remotes -- `about`, `cart`, `shop` -- and a shared
|
||||
`ui-button` library, then your project graph might look something like this.
|
||||
|
||||

|
||||
|
||||
Keeping the applications independent allows them to be deployed on different cadences, which is the whole point of MFEs.
|
||||
|
||||
## Generating applications
|
||||
|
||||
The generator for MFEs is the same as with basic Module Federation. You can use `nx g host` to create a new host
|
||||
application, and `nx g remote` for remote applications.
|
||||
|
||||
{% tabs %}
|
||||
{% tabitem label="React" %}
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx g @nx/react:host apps/shell --remotes=shop,cart
|
||||
nx g @nx/react:remote apps/about --host=shell
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
{% tabitem label="Angular" %}
|
||||
|
||||
```shell {% frame="none" %}
|
||||
nx g @nx/angular:host apps/shell --remotes=shop,cart
|
||||
nx g @nx/angular:remote apps/about --host=shell
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
{% /tabs %}
|
||||
|
||||
That is! You can now run `nx serve shell` to develop on the `shell` application, while keeping all remotes static. To
|
||||
develop on one or more remote applications, pass the `--devRemotes` option.
|
||||
|
||||
e.g. `nx serve shell --devRemotes=cart,shop`.
|
||||
|
||||
## Deployment strategies
|
||||
|
||||
How applications are deployed depends on the teams and organizational requirements. There are two approaches:
|
||||
|
||||
1. À la carte deployments - Each application is deployed according to a release schedule, and can have different cadences.
|
||||
2. Affected deployments - When changes are merged, use Nx to test and deploy the affected applications automatically.
|
||||
|
||||
Often times, teams mix both approach so deployments to staging (or other shared environments) are automatic. Then,
|
||||
promotion from staging to production occurs on a set cadence (e.g. weekly releases). It is also recommended to agree on
|
||||
a process to handle changes to core libraries (i.e. ones that are shared between applications). Since the core changes
|
||||
affect all applications, it also blocks all other releases, thus should not occur too frequently.
|
||||
|
||||
You may also choose to fully automate deployments, even to production. This type of pipeline requires good end-to-end
|
||||
testing to provide higher confidence that the applications behave correctly. You will also need good rollback mechanisms
|
||||
in case of a bad deployment.
|
||||
|
||||
## Shared libraries
|
||||
|
||||
Since deployments with MFEs are not atomic, there is a chance that shared libraries -- both external (npm) and workspace --
|
||||
between the host and remotes are mismatched. The default Nx setup configures all libraries as singletons, which requires
|
||||
that all affected applications be deployed for any given changeset, and makes à la carte deployments riskier.
|
||||
|
||||
There are mitigation strategies that can minimize mismatch errors. One such strategy is to share as little as possible
|
||||
between applications.
|
||||
|
||||
For example, you can create a base configuration file that only shares core libraries that _have_ to be shared.
|
||||
|
||||
```javascript title="module-federation.config.ts"
|
||||
import { ModuleFederationConfig } from '@nx/webpack';
|
||||
// Core libraries such as react, angular, redux, ngrx, etc. must be
|
||||
// singletons. Otherwise the applications will not work together.
|
||||
const coreLibraries = new Set([
|
||||
'react',
|
||||
'react-dom',
|
||||
'react-router-dom',
|
||||
// A workspace library for a publish/subscribe
|
||||
// system of communication.
|
||||
'@acme/pub-sub',
|
||||
]);
|
||||
|
||||
export const config: ModuleFederationConfig = {
|
||||
// Share core libraries, and avoid everything else
|
||||
shared: (libraryName, defaultConfig) => {
|
||||
if (coreLibraries.has(libraryName)) {
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
// Returning false means the library is not shared.
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
Then, in the `shell` and remote applications, you can extend from the base configuration.
|
||||
|
||||
```javascript title="apps/shell/module-federation.config.ts"
|
||||
import { ModuleFederationConfig } from '@nx/webpack';
|
||||
import baseConfig from '../../module-federation.config';
|
||||
|
||||
export const config: ModuleFederationConfig = {
|
||||
...baseConfig,
|
||||
name: 'shell',
|
||||
remotes: ['shop', 'cart', 'about'],
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
{% aside type="note" title="More details" %}
|
||||
You can return any configuration [object that webpack's Module Federation supports](https://webpack.js.org/plugins/module-federation-plugin/#sharing-hints).
|
||||
{% /aside %}
|
||||
|
||||
There are downsides to not sharing a library (such as increasing network traffic due to duplication), so consider what
|
||||
you share carefully. If you are not sure, then start with a small set of core libraries, and expand it as needed.
|
||||
|
||||
## Strategic collaboration over micro frontend anarchy
|
||||
|
||||
[Micro frontend anarchy](https://www.thoughtworks.com/en-ca/radar/techniques/micro-frontend-anarchy) refers to an MFE
|
||||
setup that mixes a range of competing technologies together. For example, using Angular in some applications, and React
|
||||
in another. Although it is possible to do this mixing with MFEs, we recommend choosing strategic collaboration instead.
|
||||
|
||||
Teams should agree upon a set of adopted technologies, such as UI/backend framework, styling solutions (CSS vs CSS-in-JS),
|
||||
etc. Standardizing technologies enable developers to collaborate across teams more easily, since there is consistency
|
||||
in each vertical. The only time mixing competing technologies make sense is as a part of a deliberate transition strategy,
|
||||
such as migrating from React to Vue, for example.
|
||||
|
||||
## Summary
|
||||
|
||||
While Module Federation enables faster builds by vertically slicing your application into smaller ones, the
|
||||
MFE architecture layers _independent deployments_ on top of federation. Teams should only choose MFEs
|
||||
if they want to deploy their host and remotes on different cadences.
|
||||
|
||||
Teams should consider a process for changes to core libraries that require deploying all applications. These types of
|
||||
changes should occur infrequently as to not disrupt other releases for bug fixes or new features.
|
||||
|
||||
Since deployments are not atomic, there can be cases of mismatched libraries between the host and remotes. We recommend
|
||||
that teams deploy their applications whenever changes to a shared library affects them. You can further mitigate mismatch
|
||||
issues by minimizing the amount of libraries you share (using the `shared` configuration option in
|
||||
`module-federation.config.ts`).
|
||||
|
||||
Teams should also avoid MFE anarchy, where competing technologies are mixed together. Instead, teams should agree upon
|
||||
the adopted technologies, which allows easier collaboration across teams.
|
||||
@@ -1,143 +0,0 @@
|
||||
---
|
||||
title: Module Federation and Nx
|
||||
description: Learn how Nx provides out-of-the-box support for Module Federation, making it easier to share code between applications in a monorepo environment.
|
||||
---
|
||||
|
||||
Module Federation is a technique that allows developers to share code and resources across multiple applications. It has become more popular in recent years since the addition of the [ModuleFederationPlugin](https://webpack.js.org/plugins/module-federation-plugin/) in [Webpack](https://webpack.js.org).
|
||||
|
||||
{% aside type="note" title="Nx uses @module-federation/enhanced" %}
|
||||
As of Nx 19.5, our Module Federation support is provided by the [@module-federation/enhanced](https://npmjs.com/@module-federation/enhanced) package.
|
||||
This package is owned and maintained by [Zack Jackson](https://x.com/ScriptedAlchemy), the creator of Module Federation, and the [ByteDance](https://x.com/ByteDanceTalk) team.
|
||||
|
||||
Using this package for Nx's Module Federation support keeps our support aligned with the latest improvements, features and bug fixes for Module Federation.
|
||||
|
||||
You can learn more about Module Federation Enhanced on their [docs](https://module-federation.io/).
|
||||
{% /aside %}
|
||||
|
||||
An increasing number of enterprise applications have started to adopt Module Federation to help them develop and scale their applications quickly, while reducing some overhead in sharing code between teams.
|
||||
|
||||
Nx added out-of-the-box support for Module Federation with Webpack to make it more approachable, less complex and leverage some unique benefits that can only be realised in a monorepo.
|
||||
|
||||
## What is Module Federation?
|
||||
|
||||
Module Federation is a method in which code can be split into smaller deployable modules that can be shared and consumed at runtime between applications.
|
||||
This method allows for the development of Micro Frontends which can reduce coordination between teams and allow for a faster development cycle with each team adhering to its own release cadence.
|
||||
|
||||
{% aside type="caution" title="Release Cadences" %}
|
||||
Although teams can adhere to their own release cadence, some changes should still be coordinated with teams, such as package upgrades, as this can lead to an incompatibility and unexpected behaviour between the applications within the Module Federation Architecture.
|
||||
|
||||
For example, upgrading Angular or React to the latest version should be coordinated to prevent any issues where different versions are loaded at runtime, causing unexpected behaviour.
|
||||
{% /aside %}
|
||||
|
||||
In order to achieve this, Module Federation introduces three terms for the applications that make up the Module Federation architecture; `host`, `remote` and `federated modules`.
|
||||
|
||||
### What is a Remote?
|
||||
|
||||
A `remote` is an application that exposes a federated module that can be fetched over the network at runtime. The federated module can be any valid JavaScript module, and therefore grows to include things such as a React Component, an Angular Routing File, a plain old JavaScript object (POJO) and more.
|
||||
|
||||
{% aside type="note" title="Creating a Remote" %}
|
||||
Follow our [How to Create a Remote Application guide](/docs/technologies/module-federation/guides/create-a-remote) to learn more.
|
||||
{% /aside %}
|
||||
|
||||
### What is a Host?
|
||||
|
||||
A `host` is an application that consumes federated modules from `remote` applications at runtime.
|
||||
|
||||
When you write your host application, you import the module from your remote as though it was part of the build, but at build time, Webpack is aware that this module will only exist at runtime, and only after it has made a network request to the corresponding remote application to fetch the JS bundle.
|
||||
|
||||
The federated module will then be executed as though it was always part of the `host` application.
|
||||
|
||||
{% aside type="note" title="Creating a Host" %}
|
||||
Follow our [How to Create a Host Application guide](/docs/technologies/module-federation/guides/create-a-host) to learn more.
|
||||
{% /aside %}
|
||||
|
||||
### What is a Federated Module?
|
||||
|
||||
A `federated module` is any valid JavaScript module that is exposed by a `remote` application with the aim that it will be consumed by a `host` application.
|
||||
|
||||
This means that React Components, Angular Components, Services, Application State, Functions, UI Components and more can be shared between applications and updated without the need to redeploy everything.
|
||||
|
||||
{% aside type="note" title="Federating a Module" %}
|
||||
Follow our [How to Federate a Module guide](/docs/technologies/module-federation/guides/federate-a-module) to learn more.
|
||||
{% /aside %}
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
Module Federation is not without its complexity, especially if you choose to use it to enable independent deployments where independent `remotes` are deployed on different release cadences. Some of the pitfalls you might encounter are listed below:
|
||||
|
||||
#### Increased Bundle Size
|
||||
|
||||
Module Federation allows you to share third-party packages across `remotes` and `hosts`, which can be essential in cases such as `react`, `react-dom` and `@angular/*` packages. This means that when you load a `federated module` from a `remote`, Webpack does not need to re-download a copy of these packages. Instead, the `federated module` uses the already loaded packages.
|
||||
|
||||
However, when third-party packages are shared between `remotes` and `hosts`, Webpack is unable to perform efficient tree-shaking on those packages as it is unaware of exactly what code will be used by any of the remotes. This can lead to an increased bundle size for some third-party packages.
|
||||
|
||||
A solution to help mitigate the impact of this is to only share exactly what is necessary between `remotes` and `hosts`.
|
||||
|
||||
#### Managing Versions
|
||||
|
||||
Module Federation also supports the ability to manage the versions of third-party packages that are compatible across `remotes` and `hosts`. This support ensures that each `federated module` works with a version of the package it was intended to use, but it can also add some overhead on ensuring that the package versions remain up to date.
|
||||
|
||||
Versioned packages and libraries can cause further issues if they store internal state or use a Singleton to manage a single instance across your application. If a `remote` is deployed with a new version of the package or library, there is the possibility that your `federated module` will download a new copy of that package if your `host` does not have the same version it is expecting.
|
||||
|
||||
Having multiple versions of a package would then break the package's Singleton nature as there will now be multiple instances of it running.
|
||||
|
||||
{% aside type="note" title="Managing Versions" %}
|
||||
Follow our [Manage Library Versions Guide](/docs/technologies/module-federation/concepts/manage-library-versions-with-module-federation) to learn more.
|
||||
{% /aside %}
|
||||
|
||||
## Nx Support for Module Federation
|
||||
|
||||
Nx offers out-of-the-box support for Module Federation with React and Angular. There are a number of features that can assist you when developing a Module Federation architecture for your application, such as:
|
||||
|
||||
- Generators - to aid in scaffolding `remotes`, `hosts` and `federated modules`
|
||||
- Executors - to aid in building your applications with Module Federation and for great DX when developing locally
|
||||
- Type Safety - allowing for type-safety between `hosts` and `remotes` to catch issues early and to take advantage of autocompletion in IDEs
|
||||
- Versioning of Libraries - to aid in preventing some common issues regarding incompatible package versions being used by `federated modules`
|
||||
- Scaling DX - techniques to ensure a smooth DX regardless of the number of remotes in the workspace
|
||||
|
||||
### Develop as a User
|
||||
|
||||
For both the best DX (Development Experience) and most accurate development of a Module Federation architecture we recommend viewing it as a single application. In other words, the `host` and all the `remotes` are composed to form a single application.
|
||||
|
||||
The `host` is the entry point and the `remotes` are modules used by the application. It just happens that the `remotes` are fetched over-the-wire at runtime rather than being bundled into the application.
|
||||
|
||||
To support this, as well as to ensure a great local DX, we built our Module Federation support in such a way that when developing locally you should always run `serve` on your `host` application. This will start up your full Module Federation architecture; serving your `host` with `webpack-dev-server` and each `remote` via a single `http-server`. You can learn more about this on our [Nx Module Federation Technical Overview](/docs/technologies/module-federation/concepts/nx-module-federation-technical-overview).
|
||||
|
||||
With the introduction of Continuous Tasks in Nx 21 when you're working on a specific `remote` application, you now only need to run `nx serve remote` and it will serve the application along with your `host` application.
|
||||
|
||||
{% aside type="note" title="Continuous Tasks Support in Module Federation" %}
|
||||
This is currently only supported for Rspack Module Federation using the `@nx/rspack/plugin` Inference Plugin.
|
||||
{% /aside %}
|
||||
|
||||
If you are using Webpack Module Federation, or are not using [Inferred Tasks](/docs/concepts/inferred-tasks), you should use the `--devRemotes` option to specify the `remote` you are currently developing; e.g. `nx serve host --devRemotes=remote1`. This ensures that the `remote` is served via `webpack-dev-server` allowing for HMR and live reloading.
|
||||
|
||||
## Use Cases
|
||||
|
||||
Nx has identified some common use cases that have made developers reach for Module Federation. They are Faster Builds and Independent Deployability.
|
||||
|
||||
### Faster Builds
|
||||
|
||||
As Module Federation allows you to split your application into smaller deployable chunks that are only required at runtime, you can take advantage of this to reduce the build times of your application.
|
||||
|
||||
You can run the builds of multiple smaller applications in parallel and deploy all of them together, maintaining a single release cadence and coordination across teams but benefiting with reduced build times locally for developers and in CI.
|
||||
|
||||
If you add [Nx Cloud](https://nx.app) to your Nx Workspace, then you can even get cache hits from some of the builds from other team members and CI, reducing the build time further.
|
||||
|
||||
{% aside type="note" title="Faster Builds with Module Federation" %}
|
||||
Follow our [Faster Builds with Module Federation Guide](/docs/technologies/module-federation/concepts/faster-builds-with-module-federation) to learn more.
|
||||
{% /aside %}
|
||||
|
||||
### Independent Deployability
|
||||
|
||||
Independent Deployability is the concept where individual teams within an organization deploy their work on their own release cadence, regardless of other teams, allowing for more team autonomy. This can be achieved with Module Federation and becomes more and more appealing as the organization and application grows.
|
||||
|
||||
With Module Federation, each team can own a `remote` that can be deployed when needed, and it will be consumed by the `host` application as expected, allowing for updates to that `remote` to be made without the need to redeploy everything.
|
||||
This lends itself to more of a Micro Frontend approach.
|
||||
|
||||
{% aside type="note" title="Micro Frontend Architecture" %}
|
||||
Follow our [Micro Frontend Architecture Guide](/docs/technologies/module-federation/concepts/micro-frontend-architecture) to learn more.
|
||||
{% /aside %}
|
||||
|
||||
You can also check out our example repository for Independent Deployability with Module Federation and Nx below:
|
||||
|
||||
{% github_repository url="https://github.com/jaysoo/module-federation-example/tree/main" /%}
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
title: Nx Module Federation Technical Overview
|
||||
description: Understand the technical details of how Nx implements Module Federation through executors and webpack configuration utilities.
|
||||
---
|
||||
|
||||
Nx's Module Federation support is provided through a mixture of `executors` and the `withModuleFederation()` util that is used in your `webpack.config` or `rspack.config` file. Understanding what is happening under the hood can help when developing applications that use Module Federation as well as debugging any potential issues you run into.
|
||||
With Rspack, Module Federation support can also be provided through the [`NxModuleFederationPlugin`](nx-api/module-federation/documents/nx-module-federation-plugin) and [`NxModuleFederationDevServerPlugin`](nx-api/module-federation/documents/nx-module-federation-dev-server-plugin) plugins that can be used in the `rspack.config` file when utilizing [Inferred Tasks](/docs/concepts/inferred-tasks).
|
||||
|
||||
## What happens when you serve your host?
|
||||
|
||||
When you serve your host application via `nx serve host`, the Nx `module-federation-dev-server` executor or `NxModuleFederationDevServerPlugin` is invoked. These do a few things that aim to provide a more holistic local development while ensuring a great DX (development experience).
|
||||
|
||||
{% aside type="note" title="Using Module Federation with SSR?" %}
|
||||
The same technique outlined below also applies to the `module-federation-ssr-dev-server` and the `NxModuleFederationSSRDevServerPlugin`.
|
||||
This is important to know when it comes to deploying your SSR Module Federation application as it indicates that you can place the build artifacts from the `remotes` onto something like an Amazon S3 Bucket and your `host` will be able to find these files correctly.
|
||||
{% /aside %}
|
||||
|
||||
The executor does the following:
|
||||
|
||||
1. Finds all the `remotes` that the `host` depends on.
|
||||
2. Determines which `remotes` need to be served statically and which need to be served via `webpack-dev-server`.
|
||||
3. For the `static remotes`, it will invoke `nx run-many -t build --projects={listOfStaticRemotes}`.
|
||||
4. If required, it will move the built artifacts of each `remote` to a common directory.
|
||||
5. It will run `http-server` at the common directory such that those files are available on the network from a single port.
|
||||
6. It will create proxy servers via `express` listening on the ports where each `remote` _should_ be located (as configured in the host's `module-federation.config.ts` or `module-federation.manifest.json` file).
|
||||
- These proxy servers will proxy requests from the server to the `http-server` to fetch the correct files as requested by Module Federation.
|
||||
7. **Only Applicable for Executor Usage**: If the `--devRemotes` option has been passed, it will serve each `dev remote` via `webpack-dev-server` allowing for HMR and live reloading when working on those remotes.
|
||||
8. It will serve the `host` via `webpack-dev-server`.
|
||||
|
||||
If you prefer diagrams, the one below outlines the above steps.
|
||||
|
||||

|
||||
|
||||
## Using Module Federation with Continuous Tasks
|
||||
|
||||
Continuous Tasks are a new feature in Nx 21. Using Rspack Module Federation, you can now use Continuous Tasks to serve your `remotes` and `host` application.
|
||||
|
||||
Thanks to the benefits of Continuous Tasks, you no longer need to run `nx serve host --devRemotes=remote` to serve your `host` application with HMR enabled for your remote applications.
|
||||
Instead, you can run `nx serve remote` and it will serve the `remote` along with your `host` application with HMR enabled.
|
||||
|
||||
This is a great way to develop your application locally and have a great DX. It also makes it easier to explain to your team how to work with Module Federation as there is no longer any special command required to serve their application.
|
||||
|
||||
{% aside type="note" title="Using Continuous Tasks with Webpack Module Federation?" %}
|
||||
Webpack Module Federation does not support Continuous Tasks. If you are using Webpack Module Federation, you should use the `--devRemotes` option to specify the `remote` you are currently developing; e.g. `npx nx serve host --devRemotes=remote`.
|
||||
{% /aside %}
|
||||
|
||||
## The `NxRuntimeLibraryControlPlugin`
|
||||
|
||||
Previously, when using shared workspace libraries as part of your Module Federation application, there was a chance that the workspace library would be provided by one of the `static remotes`. This would cause issues where changes to those shared libraries would not be reflected in the locally served application.
|
||||
|
||||
To combat this issue, we developed the `NxRuntimeLibraryControlPlugin`. This is a _Runtime Plugin_ that will ensure that workspace libraries are only shared via any active `dev remote`. This means that any changes to the shared library will be picked up by `webpack-dev-server` and, as such, reflected in the locally served application.
|
||||
|
||||
This plugin is enabled by default, however, you can turn it off in your `module-federation.config` file:
|
||||
|
||||
```ts
|
||||
export const config: ModuleFederationConfig = {
|
||||
...,
|
||||
disableNxRuntimeLibraryControlPlugin: true
|
||||
}
|
||||
```
|
||||
@@ -3,6 +3,7 @@ title: Publishable and Buildable Nx Libraries
|
||||
description: Learn about buildable and publishable libraries in Nx, when to use them, and how they adjust your project configuration for distribution.
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
The `--buildable` and `--publishable` options are available on the Nx library generators for the following plugins:
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
---
|
||||
title: Common Tasks
|
||||
title: Common Task Configurations
|
||||
description: Learn about standard task naming conventions in Nx projects, including build, serve, test, and lint tasks, for consistent project configuration.
|
||||
keywords: [build, serve, test, lint]
|
||||
sidebar:
|
||||
order: 1
|
||||
label: Common Tasks
|
||||
filter: 'type:Concepts'
|
||||
weight: 5.0
|
||||
---
|
||||
|
||||
The tasks that are [inferred by plugins](/docs/concepts/inferred-tasks) or that you define in your [project configuration](/docs/reference/project-configuration) can have any name that you want, but it is helpful for developers if you keep your task naming convention consistent across the projects in your repository. This way, if a developer moves from one project to another, they already know how to launch tasks for the new project. Here are some common task names that you can define for your projects.
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Executors and Configurations
|
||||
description: Learn about Nx executors, pre-packaged node scripts that run tasks consistently across projects, and how to configure them in project.json files.
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Executors are pre-packaged node scripts that can be used to run tasks in a consistent way.
|
||||
|
||||
@@ -3,6 +3,7 @@ title: How Caching Works
|
||||
description: Learn how Nx's computation hashing enables powerful caching, including what factors determine cache validity and how local and remote caches work together.
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Before running any cacheable task, Nx computes its computation hash. As long as the computation hash is the same, the output of
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Core Concepts
|
||||
sidebar:
|
||||
hidden: true
|
||||
description: Understand the core concepts of how Nx works under the hood
|
||||
pagefind: false
|
||||
---
|
||||
|
||||
Learn about all the different concepts Nx uses to manage your tasks and enhance your productivity.
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Inferred Tasks (Project Crystal)
|
||||
description: Learn how Nx plugins automatically infer tasks from tool configurations, enabling caching, task dependencies, and optimized execution without manual setup.
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
In Nx version 18, Nx plugins can automatically infer tasks for your projects based on the configuration of different tools. Many tools have configuration files which determine what a tool does. Nx is able to cache the results of running the tool. Nx plugins use the same configuration files to infer how Nx should [run the task](/docs/features/run-tasks). This includes [fine-tuned cache settings](/docs/features/cache-task-results) and automatic [task dependencies](/docs/concepts/task-pipeline-configuration).
|
||||
@@ -56,6 +57,7 @@ nx show project my-project --web
|
||||
|
||||
{% project_details%}
|
||||
|
||||
```json
|
||||
{
|
||||
"project": {
|
||||
"name": "myreactapp",
|
||||
@@ -69,27 +71,19 @@ nx show project my-project --web
|
||||
"command": "vite build"
|
||||
},
|
||||
"cache": true,
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": [
|
||||
"production",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
"vite"
|
||||
]
|
||||
"externalDependencies": ["vite"]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
"{workspaceRoot}/dist/apps/myreactapp"
|
||||
],
|
||||
"outputs": ["{workspaceRoot}/dist/apps/myreactapp"],
|
||||
"executor": "nx:run-commands",
|
||||
"configurations": {},
|
||||
"metadata": {
|
||||
"technologies": [
|
||||
"vite"
|
||||
]
|
||||
"technologies": ["vite"]
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
@@ -101,9 +95,7 @@ nx show project my-project --web
|
||||
"executor": "nx:run-commands",
|
||||
"configurations": {},
|
||||
"metadata": {
|
||||
"technologies": [
|
||||
"vite"
|
||||
]
|
||||
"technologies": ["vite"]
|
||||
}
|
||||
},
|
||||
"preview": {
|
||||
@@ -114,9 +106,7 @@ nx show project my-project --web
|
||||
"executor": "nx:run-commands",
|
||||
"configurations": {},
|
||||
"metadata": {
|
||||
"technologies": [
|
||||
"vite"
|
||||
]
|
||||
"technologies": ["vite"]
|
||||
}
|
||||
},
|
||||
"serve-static": {
|
||||
@@ -137,20 +127,14 @@ nx show project my-project --web
|
||||
"default",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
"vitest"
|
||||
]
|
||||
"externalDependencies": ["vitest"]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
"{workspaceRoot}/coverage/apps/myreactapp"
|
||||
],
|
||||
"outputs": ["{workspaceRoot}/coverage/apps/myreactapp"],
|
||||
"executor": "nx:run-commands",
|
||||
"configurations": {},
|
||||
"metadata": {
|
||||
"technologies": [
|
||||
"vite"
|
||||
]
|
||||
"technologies": ["vite"]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
@@ -165,17 +149,13 @@ nx show project my-project --web
|
||||
"{workspaceRoot}/apps/myreactapp/.eslintrc.json",
|
||||
"{workspaceRoot}/tools/eslint-rules/**/*",
|
||||
{
|
||||
"externalDependencies": [
|
||||
"eslint"
|
||||
]
|
||||
"externalDependencies": ["eslint"]
|
||||
}
|
||||
],
|
||||
"executor": "nx:run-commands",
|
||||
"configurations": {},
|
||||
"metadata": {
|
||||
"technologies": [
|
||||
"eslint"
|
||||
]
|
||||
"technologies": ["eslint"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -186,25 +166,14 @@ nx show project my-project --web
|
||||
"tags": [],
|
||||
"implicitDependencies": [],
|
||||
"metadata": {
|
||||
"technologies": [
|
||||
"react"
|
||||
]
|
||||
"technologies": ["react"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceMap": {
|
||||
"root": [
|
||||
"apps/myreactapp/project.json",
|
||||
"nx/core/project-json"
|
||||
],
|
||||
"targets": [
|
||||
"apps/myreactapp/project.json",
|
||||
"nx/core/project-json"
|
||||
],
|
||||
"targets.build": [
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"root": ["apps/myreactapp/project.json", "nx/core/project-json"],
|
||||
"targets": ["apps/myreactapp/project.json", "nx/core/project-json"],
|
||||
"targets.build": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.build.command": [
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
@@ -233,10 +202,7 @@ nx show project my-project --web
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.serve": [
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.serve": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.serve.command": [
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
@@ -249,10 +215,7 @@ nx show project my-project --web
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.preview": [
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.preview": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.preview.command": [
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
@@ -281,10 +244,7 @@ nx show project my-project --web
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.test": [
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.test": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.test.command": [
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
@@ -293,10 +253,7 @@ nx show project my-project --web
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.test.cache": [
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.test.cache": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.test.inputs": [
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
@@ -309,18 +266,12 @@ nx show project my-project --web
|
||||
"apps/myreactapp/vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.lint": [
|
||||
"apps/myreactapp/project.json",
|
||||
"@nx/eslint/plugin"
|
||||
],
|
||||
"targets.lint": ["apps/myreactapp/project.json", "@nx/eslint/plugin"],
|
||||
"targets.lint.command": [
|
||||
"apps/myreactapp/project.json",
|
||||
"@nx/eslint/plugin"
|
||||
],
|
||||
"targets.lint.cache": [
|
||||
"apps/myreactapp/project.json",
|
||||
"@nx/eslint/plugin"
|
||||
],
|
||||
"targets.lint.cache": ["apps/myreactapp/project.json", "@nx/eslint/plugin"],
|
||||
"targets.lint.options": [
|
||||
"apps/myreactapp/project.json",
|
||||
"@nx/eslint/plugin"
|
||||
@@ -333,28 +284,14 @@ nx show project my-project --web
|
||||
"apps/myreactapp/project.json",
|
||||
"@nx/eslint/plugin"
|
||||
],
|
||||
"name": [
|
||||
"apps/myreactapp/project.json",
|
||||
"nx/core/project-json"
|
||||
],
|
||||
"$schema": [
|
||||
"apps/myreactapp/project.json",
|
||||
"nx/core/project-json"
|
||||
],
|
||||
"sourceRoot": [
|
||||
"apps/myreactapp/project.json",
|
||||
"nx/core/project-json"
|
||||
],
|
||||
"projectType": [
|
||||
"apps/myreactapp/project.json",
|
||||
"nx/core/project-json"
|
||||
],
|
||||
"tags": [
|
||||
"apps/myreactapp/project.json",
|
||||
"nx/core/project-json"
|
||||
]
|
||||
"name": ["apps/myreactapp/project.json", "nx/core/project-json"],
|
||||
"$schema": ["apps/myreactapp/project.json", "nx/core/project-json"],
|
||||
"sourceRoot": ["apps/myreactapp/project.json", "nx/core/project-json"],
|
||||
"projectType": ["apps/myreactapp/project.json", "nx/core/project-json"],
|
||||
"tags": ["apps/myreactapp/project.json", "nx/core/project-json"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /project_details %}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Mental Model
|
||||
description: Understand how Nx works with project graphs, task graphs, affected commands, and caching to efficiently manage your monorepo development workflow.
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Nx is a VSCode of build tools, with a powerful core, driven by metadata, and extensible through [plugins](/docs/concepts/nx-plugins). Nx works with a
|
||||
@@ -45,6 +46,7 @@ For instance `nx test lib` creates a task graph with a single node:
|
||||
|
||||
{% graph height="100px" type="task" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
@@ -60,23 +62,22 @@ For instance `nx test lib` creates a task graph with a single node:
|
||||
],
|
||||
"taskIds": ["lib:test"],
|
||||
"taskGraph": {
|
||||
"roots": [
|
||||
"lib:test"
|
||||
],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
"roots": ["lib:test"],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
@@ -96,6 +97,7 @@ running `nx run-many -t test -p app1 app2 lib`, the created task graph will look
|
||||
{% side_by_side %}
|
||||
{% graph height="200px" type="project" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
@@ -111,28 +113,28 @@ running `nx run-many -t test -p app1 app2 lib`, the created task graph will look
|
||||
],
|
||||
"taskIds": ["lib:test"],
|
||||
"taskGraph": {
|
||||
"roots": [
|
||||
"lib:test"
|
||||
],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
"roots": ["lib:test"],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
{% graph height="200px" type="task"%}
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
@@ -148,23 +150,22 @@ running `nx run-many -t test -p app1 app2 lib`, the created task graph will look
|
||||
],
|
||||
"taskIds": ["lib:test"],
|
||||
"taskGraph": {
|
||||
"roots": [
|
||||
"lib:test"
|
||||
],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
"roots": ["lib:test"],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
{% /side_by_side %}
|
||||
@@ -202,6 +203,7 @@ With this, running the same test command creates the following task graph:
|
||||
{% side_by_side %}
|
||||
{% graph height="200px" type="project" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
@@ -217,28 +219,28 @@ With this, running the same test command creates the following task graph:
|
||||
],
|
||||
"taskIds": ["lib:test"],
|
||||
"taskGraph": {
|
||||
"roots": [
|
||||
"lib:test"
|
||||
],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
"roots": ["lib:test"],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
{% graph height="200px" type="task" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
@@ -254,23 +256,22 @@ With this, running the same test command creates the following task graph:
|
||||
],
|
||||
"taskIds": ["lib:test"],
|
||||
"taskGraph": {
|
||||
"roots": [
|
||||
"lib:test"
|
||||
],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
"roots": ["lib:test"],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
{% /side_by_side %}
|
||||
@@ -356,6 +357,7 @@ As your workspace grows, the task graph looks more like this:
|
||||
|
||||
{% graph height="400px" type="task"%}
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
@@ -371,23 +373,22 @@ As your workspace grows, the task graph looks more like this:
|
||||
],
|
||||
"taskIds": ["lib:test"],
|
||||
"taskGraph": {
|
||||
"roots": [
|
||||
"lib:test"
|
||||
],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
"roots": ["lib:test"],
|
||||
"tasks": {
|
||||
"lib:test": {
|
||||
"id": "lib:test",
|
||||
"target": {
|
||||
"project": "lib",
|
||||
"target": "test"
|
||||
},
|
||||
"projectRoot": "libs/lib",
|
||||
"overrides": {}
|
||||
}
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Nx Daemon
|
||||
description: Learn about the Nx Daemon, a background process that speeds up project graph computation in large workspaces by maintaining state between commands.
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
In version 13 we introduced the opt-in Nx Daemon which Nx can leverage to dramatically speed up project graph computation, particularly for large workspaces.
|
||||
|
||||
@@ -3,6 +3,7 @@ title: What Are Nx Plugins?
|
||||
description: Learn how Nx plugins help developers integrate tools and frameworks with Nx by providing automated configuration, code generation, and dependency management.
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Nx plugins help developers use a tool or framework with Nx. They allow the plugin author who knows the best way to use a tool with Nx to codify their expertise and allow the whole community to reuse those solutions.
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Sync Generators
|
||||
description: Learn how to use Nx sync generators to maintain repository state and update configuration files based on the project graph before tasks are run.
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
In Nx 19.8, you can use sync generators to ensure that your repository is maintained in a correct state. One specific application is to use the project graph to update files. These can be global configuration files or scripts, or at the task level to ensure that files are in sync before a task is run.
|
||||
@@ -56,6 +57,8 @@ nx show project <name>
|
||||
The above command opens up the project details view, and the registered sync generators are under the **Sync Generators** for each target. Most sync generators are inferred when using an [inference plugin](/docs/concepts/inferred-tasks). For example, the `@nx/js/typescript` plugin registers the `@nx/js:typescript-sync` generator on `build` and `typecheck` targets.
|
||||
|
||||
{% project_details title="Project Details View" expandedTargets=["build"] %}
|
||||
|
||||
```json
|
||||
{
|
||||
"project": {
|
||||
"name": "foo",
|
||||
@@ -86,6 +89,8 @@ The above command opens up the project details view, and the registered sync gen
|
||||
"targets.build": ["packages/foo/tsconfig.ts", "@nx/js/typescript"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /project_details %}
|
||||
|
||||
Task sync generators can be thought of like the `dependsOn` property, but for generators instead of task dependencies.
|
||||
@@ -115,6 +120,7 @@ Nx processes the file system in order to [create the project graph](/docs/featur
|
||||
|
||||
{% graph title="Project Graph" height="200px" type="project" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
@@ -174,6 +180,7 @@ Nx processes the file system in order to [create the project graph](/docs/featur
|
||||
"groupByFolder": false,
|
||||
"exclude": []
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
{% /side_by_side %}
|
||||
|
||||
@@ -3,6 +3,7 @@ title: 'What is a Task Pipeline'
|
||||
description: 'Learn how Nx manages task dependencies and execution order in monorepo workspaces, ensuring proper build sequences for interconnected projects.'
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
If you have a monorepo workspace (or modularized app), you rarely just run one task. Almost certainly there are relationships among the projects in the workspace and hence tasks need to follow a certain order.
|
||||
@@ -11,6 +12,7 @@ As you can see in the graph visualization below, the `myreactapp` project depend
|
||||
|
||||
{% graph height="450px" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
@@ -53,6 +55,7 @@ As you can see in the graph visualization below, the `myreactapp` project depend
|
||||
"focus": null,
|
||||
"groupByFolder": false
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ title: 'Managing Configuration Files'
|
||||
description: 'Learn how Nx helps manage different types of configuration files in your workspace, including both Nx-specific and tool-specific configurations at global and project levels.'
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
Besides providing caching and task orchestration, Nx also helps incorporate numerous tools and frameworks into your repo. With all these pieces of software commingling, you can end up with a lot of configuration files. Nx plugins help to abstract away some of the difficulties of managing this configuration, but the configuration is all still accessible, in case there is a particular setting that you need to change.
|
||||
|
||||
@@ -3,6 +3,7 @@ title: 'TypeScript Project Linking'
|
||||
description: 'Learn how to efficiently reference code between TypeScript projects in your monorepo using project linking instead of relative paths.'
|
||||
sidebar:
|
||||
order: 1
|
||||
filter: 'type:Concepts'
|
||||
---
|
||||
|
||||
{% youtube src="https://youtu.be/D9D8KNffyBk" title="TypeScript Monorepos Done Right!" /%}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: Configure Conformance Rules in Nx Cloud
|
||||
filter: 'type:Features'
|
||||
---
|
||||
|
||||
[Nx Cloud Enterprise](https://nx.dev/enterprise) allows you to publish your organization's [Nx Conformance](/docs/enterprise/powerpack/conformance) rules to your Nx Cloud Organization, and consume them in any of your other Nx Workspaces without having to deal with the complexity and friction of dealing with a private NPM registry or similar. Authentication is handled automatically through your Nx Cloud connection and rules are downloaded and applied based on your preferences configured in the Nx Cloud UI.
|
||||
|
||||
@@ -3,6 +3,8 @@ title: 'Run Language-Agnostic Conformance Rules'
|
||||
description: 'Learn how to use Nx Powerpack and Nx Enterprise conformance rules to enforce organizational standards, maintain consistency, and ensure security across your workspace.'
|
||||
sidebar:
|
||||
order: 8
|
||||
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
{% youtube src="https://youtu.be/6wg23sLveTQ" title="Nx Powerpack workspace conformance" /%}
|
||||
|
||||
@@ -3,6 +3,7 @@ title: 'Nx Powerpack Features'
|
||||
description: 'Explore the enterprise-focused features available in Nx Powerpack, including conformance rules and code ownership management.'
|
||||
sidebar:
|
||||
order: 5
|
||||
pagefind: false
|
||||
---
|
||||
|
||||
Nx PowerPack is a suite of paid extensions for the Nx CLI specifically designed for enterprises. Powerpack is available for Nx version 19.8 and higher.
|
||||
|
||||
@@ -3,9 +3,9 @@ title: 'Free Licenses and Trials'
|
||||
description: 'Learn about Nx Powerpack free licenses for small teams and open source projects, as well as trial options and extended evaluation periods.'
|
||||
sidebar:
|
||||
order: 7
|
||||
filter: 'type:Features'
|
||||
---
|
||||
|
||||
|
||||
{% callout type="deepdive" title="Looking for self-hosted caching?" %}
|
||||
|
||||
Self-hosted caching is now free for everyone. [Read more about remote caching options here](/docs/guides/tasks--caching/self-hosted-caching).
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: 'Define Code Ownership at the Project Level'
|
||||
description: 'Learn how to use Nx Powerpack owners plugin to manage code ownership at the project level and automatically generate CODEOWNERS files for GitHub, Bitbucket, or GitLab.'
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
{% youtube src="https://youtu.be/mor6urvw-L0" title="Nx Powerpack Codeowners" /%}
|
||||
@@ -23,10 +24,9 @@ Then, add the Owners plugin to your workspace.
|
||||
|
||||
The ownership configuration is defined in the `nx.json` file or in individual project configuration files. Nx then uses a [sync generator](/docs/concepts/sync-generators) to automatically compile those settings into a valid CODEOWNERS file for GitHub, Bitbucket or GitLab. See the [plugin documentation](/docs/reference/core-api/owners) for more details.
|
||||
|
||||
|
||||
**Define Project Owners**:
|
||||
|
||||
```json
|
||||
```json
|
||||
// nx.json
|
||||
{
|
||||
"owners": {
|
||||
@@ -57,7 +57,6 @@ The ownership configuration is defined in the `nx.json` file or in individual pr
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
```json
|
||||
// packages/my-project/project.json
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: Publish Conformance Rules to Nx Cloud
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
[Nx Cloud Enterprise](https://nx.dev/enterprise) allows you to publish your organization's [Nx Conformance](/docs/enterprise/powerpack/conformance) rules to your Nx Cloud Organization, and consume them in any of your other Nx Workspaces without having to deal with the complexity and friction of dealing with a private NPM registry or similar. Authentication is handled automatically through your Nx Cloud connection and rules are downloaded and applied based on your preferences configured in the Nx Cloud UI.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: BitBucket Data Center Auth
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
This page is for configuring auth via BitBucket Data Center (on-prem). If you are using BitBucket Cloud please refer to the docs [here](/docs/enterprise/single-tenant/auth-bitbucket).
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: BitBucket Cloud Auth
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
This page is only for BitBucket Cloud (bitbucket.org). If you have an on-premise version of BitBucket Data Center please refer to the docs [here](/docs/enterprise/single-tenant/auth-bitbucket-data-center).
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: GitHub Auth
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
First, you'll need to create a GitHub OAuth app for your organisation.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: GitLab Auth
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
Before creating your container, you'll need to create a GitLab app for your organisation.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
title: SAML Auth
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
SAML support for Nx Cloud is an addon for Nx Enterprise contracts and requires an unlock key. Please [get in touch](mailto:cloud-support@nrwl.io)
|
||||
if you are interested.
|
||||
|
||||
|
||||
{% tabs syncKey="saml-idp" %}
|
||||
{% tabitem label="Azure AD" %}
|
||||
|
||||
@@ -88,49 +88,51 @@ if you are interested.
|
||||
2. `SAML_ENTRY_POINT=<your-login-url-from-above>`
|
||||
|
||||
{% /steps %}
|
||||
|
||||
{% /tabitem %}
|
||||
{% tabitem label="Okta" %}
|
||||
|
||||
{% steps %}
|
||||
1. Create a new Okta App Integration:
|
||||
|
||||

|
||||
1. Create a new Okta App Integration:
|
||||
|
||||

|
||||

|
||||
|
||||
2. Give it a name:
|
||||

|
||||
|
||||

|
||||
2. Give it a name:
|
||||
|
||||
3. On the Next page, configure it as below:
|
||||

|
||||
|
||||
1. The Single Sign On URL needs to point to your Nx Cloud instance URL and ends with `/auth-callback`
|
||||
2. The Audience should be `nx-private-cloud`
|
||||
3. On the Next page, configure it as below:
|
||||
|
||||

|
||||
1. The Single Sign On URL needs to point to your Nx Cloud instance URL and ends with `/auth-callback`
|
||||
2. The Audience should be `nx-private-cloud`
|
||||
|
||||
4. Under **Advanced Settings**, make sure both **Response** and **Assertion** are set to **Signed**
|
||||

|
||||
|
||||

|
||||
4. Under **Advanced Settings**, make sure both **Response** and **Assertion** are set to **Signed**
|
||||
|
||||
5. Scroll down to attribute statements and configure them as per below:
|
||||

|
||||
|
||||

|
||||
5. Scroll down to attribute statements and configure them as per below:
|
||||
|
||||
6. Click “Next”, and select the first option on the next screen.
|
||||
7. Go to the assignments tab and assign the users that can login to the Nx Cloud WebApp:
|
||||

|
||||
|
||||
1. **Note:** This just gives them permission to use the Nx Cloud web app with their own workspace. Users will still need to be invited manually through the web app to your main workspace.
|
||||
6. Click “Next”, and select the first option on the next screen.
|
||||
7. Go to the assignments tab and assign the users that can login to the Nx Cloud WebApp:
|
||||
|
||||

|
||||
1. **Note:** This just gives them permission to use the Nx Cloud web app with their own workspace. Users will still need to be invited manually through the web app to your main workspace.
|
||||
|
||||
8. Then in the Sign-On tab scroll down:
|
||||

|
||||
|
||||

|
||||
8. Then in the Sign-On tab scroll down:
|
||||
|
||||
9. Scroll down and from the list of certificates, download the one with the “Active” status:
|
||||

|
||||
|
||||

|
||||
9. Scroll down and from the list of certificates, download the one with the “Active” status:
|
||||
|
||||

|
||||
|
||||
10. Extract the downloaded certificate value as a one-line string:
|
||||
1. `awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' okta.cert`
|
||||
@@ -141,17 +143,20 @@ if you are interested.
|
||||
|
||||
12. Then find the row similar to the below, and copy the highlighted URL (see screenshot as well):
|
||||
|
||||
1. ```html
|
||||
<md:SingleSignOnService
|
||||
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
Location="https://trial-xxxxx.okta.com/app/trial-xxxxx_nxcloudtest_1/xxxxxxxxx/sso/saml"
|
||||
/>
|
||||
```
|
||||
1. ```html
|
||||
<md:SingleSignOnService
|
||||
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
Location="https://trial-xxxxx.okta.com/app/trial-xxxxx_nxcloudtest_1/xxxxxxxxx/sso/saml"
|
||||
/>
|
||||
```
|
||||
|
||||

|
||||
|
||||

|
||||
{% /steps %}
|
||||
|
||||
{% /tabitem %}
|
||||
{% /tabs %}
|
||||
|
||||
## Connect Your Nx Cloud Installation to Your SAML Set Up
|
||||
|
||||
Contact your developer productivity engineer to connect your Nx Cloud instance to the SAML configuration.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: Custom GitHub App
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
Before creating your container, you'll need to create a GitHub app for your organisation.
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Single Tenant
|
||||
sidebar:
|
||||
hidden: true
|
||||
description: Single tenant deployment options
|
||||
pagefind: false
|
||||
---
|
||||
|
||||
{% index_page_cards path="enterprise/single-tenant" /%}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Single Tenant Nx Cloud Hosting
|
||||
sidebar:
|
||||
order: 5
|
||||
filter: 'type:Features'
|
||||
---
|
||||
|
||||
We offer multiple ways of running Nx Cloud for our Enterprise customers. The below options are listed in recommended order, from easiest to most complex in terms of set-up and maintenance for your team. Please carefully consider your organization's requirements and level of infrastructure expertise before deciding on a deployment option.
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
title: Activate Nx Powerpack
|
||||
description: Learn how to obtain and register an Nx Powerpack license to unlock enterprise features like conformance rules and code ownership.
|
||||
sidebar:
|
||||
sidebar:
|
||||
order: 4
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
Nx Powerpack unlocks features of Nx that are particularly useful for larger organizations. Powerpack is available for Nx version 19.8 and higher. The features include the ability to:
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Custom Workflows
|
||||
description: 'Run scheduled tasks to gather data and run checks automatically across your organization with automated data collection and proactive monitoring.'
|
||||
sidebar:
|
||||
order: 3
|
||||
filter: 'type:Features'
|
||||
---
|
||||
|
||||
Custom Workflows enable you to run scheduled tasks and automated checks across all or a selection of repositories in your organization. This powerful feature extends beyond traditional CI/CD by creating an avenue to proactively monitor and automate compliance checking, ensuring your organization's standards are maintained consistently.
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Enterprise
|
||||
sidebar:
|
||||
hidden: true
|
||||
description: Enterprise features and deployment options for Nx
|
||||
pagefind: false
|
||||
---
|
||||
|
||||
{% index_page_cards path="enterprise" /%}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
title: Metadata-Only Workspaces
|
||||
description: 'Include non-Nx repositories in Polygraph features without requiring full Nx adoption. Enable zero-friction onboarding for legacy and existing repositories.'
|
||||
sidebar:
|
||||
sidebar:
|
||||
order: 2
|
||||
filter: 'type:Features'
|
||||
---
|
||||
|
||||
Metadata-only workspaces are a way to connect repositories to Nx Cloud that don't have Nx installed or configured. Unlike traditional Nx Cloud workspaces that require Nx to be set up in the repository, metadata-only workspaces can be connected with minimal configuration, allowing you to:
|
||||
|
||||
@@ -4,6 +4,7 @@ description: 'Scale development practices across multiple repositories with cros
|
||||
sidebar:
|
||||
order: 1
|
||||
label: Polygraph
|
||||
filter: 'type:Features'
|
||||
---
|
||||
|
||||
No longer needing to choose between monorepo or poly-repo.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Compose Executors
|
||||
description: Learn how to compose and chain Nx executors together, including how to invoke other targets and executors from within your custom executors.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
|
||||
An executor is just a function, so you can import and invoke it directly, as follows:
|
||||
|
||||
```typescript
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Composing Generators
|
||||
description: Learn how to combine and reuse Nx generators to build complex workflows from simpler building blocks, including using Nx Devkit generators and jscodeshift codemods.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
Generators are useful individually, but reusing and composing generators allows you to build whole workflows out of simpler building blocks.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Creating an Install Package
|
||||
description: Learn how to create a custom "create-x" package for your Nx plugin to provide a seamless project bootstrapping experience with npm, yarn, or other package managers.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/ocllb5KEXZk" title="Build your own CLI" width="100%" /%}
|
||||
|
||||
@@ -3,10 +3,10 @@ title: Create a Custom Plugin Preset
|
||||
description: Learn how to create a custom preset generator for your Nx plugin that can be used with create-nx-workspace to generate customized workspaces.
|
||||
sidebar:
|
||||
label: Custom Plugin Preset
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
|
||||
When you create a new nx workspace, you run the command: [`npx create-nx-workspace`](/docs/reference/nx-commands#create-nx-workspace).
|
||||
When you create a new nx workspace, you run the command: [`npx create-nx-workspace`](/docs/reference/create-nx-workspace).
|
||||
This command accepts a `--preset` option, for example: `npx create-nx-workspace --preset=react-standalone`.
|
||||
This preset option is pointing to a special generator function (remember, a generator is a function that simplifies an entire code generation script into a single function) that Nx will call when this `npx create-nx-workspace` command is run, that will generate your initial workspace.
|
||||
|
||||
@@ -47,20 +47,21 @@ The word `preset` is required for the name of this generator
|
||||
You should have a similar structure to this:
|
||||
|
||||
{% filetree %}
|
||||
|
||||
- happynrwl/
|
||||
- e2e/
|
||||
- jest.config.js
|
||||
- jest.preset.js
|
||||
- nx.json
|
||||
- package-lock.json
|
||||
- package.json
|
||||
- packages/
|
||||
- happynrwl/
|
||||
- src/
|
||||
- executors/
|
||||
- generators/
|
||||
- jest.config.js
|
||||
- jest.preset.js
|
||||
- nx.json
|
||||
- package-lock.json
|
||||
- package.json
|
||||
- packages/
|
||||
- happynrwl/
|
||||
- src/
|
||||
- executors/
|
||||
- generators/
|
||||
- happynrwl/
|
||||
- preset/ <-- Here
|
||||
- preset/ <-- Here
|
||||
- index.ts
|
||||
- tools/
|
||||
- tsconfig.base.json/
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Create a Sync Generator
|
||||
description: Learn how to create and register sync generators in Nx to ensure your file system is in the correct state before tasks run, including global and task-specific sync generators.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
|
||||
Sync generators are generators that are used to ensure that your file system is in the correct state before a task is run or the CI process is started. From a technical perspective, a sync generator is no different from any other generator, but it has some additional performance considerations and needs to be registered in a particular way.
|
||||
|
||||
{% aside type="caution" title="Disable the Nx Daemon during development" %}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Creating Files with a Generator
|
||||
description: Learn how to create, update, and manage files in your Nx workspace using generators, including working with static and dynamic file templates.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
|
||||
Generators provide an API for managing files within your workspace. You can use generators to do things such as create, update, move, and delete files. Files with static or dynamic content can also be created.
|
||||
|
||||
The generator below shows you how to generate a library, and then scaffold out additional files with the newly created library.
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Extending Nx
|
||||
sidebar:
|
||||
hidden: true
|
||||
description: In-depth guides for extending Nx with plugins, executors, generators, and more.
|
||||
pagefind: false
|
||||
---
|
||||
|
||||
{% index_page_cards path="extending-nx" /%}
|
||||
|
||||
@@ -4,6 +4,7 @@ description: Learn how to create custom Nx plugins to enforce best practices, in
|
||||
sidebar:
|
||||
label: 'Intro'
|
||||
order: 1
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
Nx's core functionality focuses on task running and understanding your project and task graph. Nx plugins leverage that functionality to enforce best practices, seamlessly integrate tooling and allow developers to get up and running quickly.
|
||||
@@ -25,9 +26,11 @@ Get started developing your own plugin with a few terminal commands:
|
||||
```shell {% frame="none" %}
|
||||
npx create-nx-plugin my-plugin
|
||||
```
|
||||
|
||||
{% /tabitem %}
|
||||
|
||||
{% tabitem label="Add a plugin to an existing workspace" %}
|
||||
|
||||
```shell {% frame="none" %}
|
||||
npx nx add @nx/plugin
|
||||
npx nx g plugin tools/my-plugin
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Write a Simple Executor
|
||||
description: Learn how to create, run, and customize local executors in your Nx workspace to standardize development, building, and deployment tasks.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
Creating Executors for your workspace standardizes scripts that are run during your development/building/deploying tasks in order to provide guidance in the terminal with `--help` and when invoking with [Nx Console](/docs/getting-started/editor-setup)
|
||||
@@ -25,6 +26,7 @@ nx generate @nx/plugin:executor tools/my-plugin/src/executors/echo
|
||||
After the command is finished, the executor is created in the plugin `executors` folder.
|
||||
|
||||
{% filetree %}
|
||||
|
||||
- happynrwl/
|
||||
- apps/
|
||||
- tools/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Local Generators
|
||||
description: Learn how to create, run, and customize local generators in your Nx workspace to automate common development tasks and standardize workflows.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
Local plugin generators provide a way to automate many tasks you regularly perform as part of your development workflow. Whether it is scaffolding out components, features, or ensuring libraries are generated and structured in a certain way, generators help you standardize these tasks in a consistent, and predictable manner.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Migration Generators
|
||||
description: Learn how to create migration generators for your Nx plugin to automatically update configuration files when your plugin makes breaking changes.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
When your plugin is being used in other repos, it is helpful to provide migration generators to automatically update configuration files when your plugin makes a breaking change.
|
||||
@@ -23,7 +24,7 @@ nx generate @nx/plugin:migration libs/pluginName/src/migrations/change-executor-
|
||||
|
||||
This command will update the following files:
|
||||
|
||||
```json
|
||||
```json
|
||||
// package.json
|
||||
{
|
||||
"nx-migrations": {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Modifying Files with a Generator
|
||||
description: Learn different approaches to modify existing files in your Nx workspace using generators, from simple JSON updates to advanced AST manipulation.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
Modifying existing files is an order of magnitude harder than creating new files, so care should be taken when trying to automate this process. When the situation merits it, automating a process can lead to tremendous benefits across the organization. Here are some approaches listed from simplest to most complex.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Enforce Organizational Best Practices with a Local Plugin
|
||||
description: Learn how to create a custom Nx plugin that encodes your organization's best practices into code generators for consistent project creation.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
|
||||
Every repository has a unique set of conventions and best practices that developers need to learn in order to write code that integrates well with the rest of the code base. It is important to document those best practices, but developers don't always read the documentation and even if they have read the documentation, they don't consistently follow the documentation every time they perform a task. Nx allows you to encode these best practices in code generators that have been tailored to your specific repository.
|
||||
|
||||
In this tutorial, we will create a generator that helps enforce the follow best practices:
|
||||
@@ -17,7 +17,7 @@ In this tutorial, we will create a generator that helps enforce the follow best
|
||||
|
||||
Let's first create a new workspace with the `create-nx-workspace` command:
|
||||
|
||||
```shell {% frame="none" %}
|
||||
```shell {% frame="none" %}
|
||||
npx create-nx-workspace myorg --preset=react-monorepo --ci=github
|
||||
```
|
||||
|
||||
@@ -129,7 +129,7 @@ The `schema.d.ts` file is used for type checking inside the implementation file.
|
||||
|
||||
The schema files not only provide structure to the CLI, but also allow [Nx Console](/docs/getting-started/editor-setup) to show an accurate UI for the generator.
|
||||
|
||||

|
||||

|
||||
|
||||
Notice how we made the `description` argument optional in both the JSON and type files. If we call the generator without passing a directory, the project will be created in a directory with same name as the project. We can test the changes to the generator with the following command:
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Extending the Project Graph
|
||||
description: Learn how to create project graph plugins for Nx to add custom nodes and dependencies, enabling support for additional languages and technologies.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
The Project Graph is the representation of the source code in your repo. Projects can have files associated with them. Projects can have dependencies on each other.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Publish Your Nx Plugin
|
||||
description: Learn how to publish your Nx plugin to npm and get it listed in the official Nx plugin registry so others can discover and use it.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
|
||||
In order to use your plugin in other workspaces or share it with the community, you will need to publish it to an npm registry. To publish your plugin follow these steps:
|
||||
|
||||
1. `nx nx-release-publish nx-cfonts`
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
title: Hook into the Task Running Lifecycle
|
||||
description: Learn how to extend Nx's task running process with preTasksExecution and postTasksExecution hooks to implement custom logic before and after tasks run.
|
||||
sidebar:
|
||||
sidebar:
|
||||
label: Task Running Lifecycle
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
|
||||
Nx plugins can hook into the task running lifecycle to execute custom logic before and after tasks are run. This is useful for implementing custom analytics, environment validation, or any other pre/post processing that should happen when running tasks.
|
||||
|
||||
{% aside type="note" title="New API for deprecated custom task runners" %}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Integrate a New Tool with a Tooling Plugin
|
||||
description: Learn how to create a custom Nx plugin that integrates a tool or framework into an Nx repository, using Astro as an example.
|
||||
filter: 'type:Guides'
|
||||
---
|
||||
|
||||
|
||||
Nx Plugins can be used to easily integrate a tool or framework into an Nx repository. If there is no plugin available for your favorite tool or framework, you can write your own.
|
||||
|
||||
In this tutorial, we'll create a plugin that helps to integrate the _Astro_ framework. `Astro` is a JavaScript web framework optimized for building fast, content-driven websites. We'll call our plugin `nx-astro`.
|
||||
@@ -246,7 +246,7 @@ export interface InitGeneratorSchema {}
|
||||
{% /tabitem %}
|
||||
{% tabitem label="schema.json" %}
|
||||
|
||||
```json
|
||||
```json
|
||||
// src/generators/init/schema.json
|
||||
{
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
@@ -316,7 +316,7 @@ The `generateFiles` function will use the template files in the `files` folder t
|
||||
{% /tabitem %}
|
||||
{% tabitem label="astro.config.mjs" %}
|
||||
|
||||
```js
|
||||
```js
|
||||
// src/generators/application/files/astro.config.mjs
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ title: Run Only Tasks Affected by a PR
|
||||
description: Learn how to use Nx's affected command to determine and run tasks only on projects affected by your changes, improving CI speed and efficiency.
|
||||
sidebar:
|
||||
order: 13
|
||||
|
||||
filter: 'type:Features'
|
||||
---
|
||||
|
||||
{% youtube src="https://youtu.be/q-cu5Lw3DoE" title="Only Run Tasks for Projects That Changed" /%}
|
||||
@@ -16,6 +18,7 @@ This drastically improves the speed of your CI and reduces the amount of compute
|
||||
|
||||
{% graph title="Making a change in lib10 only affects a sub-part of the project graph (shown in purple)" height="400px" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
@@ -200,6 +203,7 @@ This drastically improves the speed of your CI and reduces the amount of compute
|
||||
},
|
||||
"affectedProjectIds": ["lib10", "lib4", "lib5", "app2"]
|
||||
}
|
||||
```
|
||||
|
||||
{% /graph %}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ description: 'Learn how to use Nx Agents to distribute task execution across mul
|
||||
keywords: [distributed tasks]
|
||||
sidebar:
|
||||
order: 12
|
||||
filter: 'type:Features'
|
||||
---
|
||||
|
||||
{% youtube
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
title: Dynamically Allocate Agents
|
||||
sidebar:
|
||||
order: 14
|
||||
filter: 'type:Features'
|
||||
---
|
||||
|
||||
By default, when you set up [Nx Agents](/docs/features/ci-features/distribute-task-execution) you specify the number and type of agents to use.
|
||||
|
||||
```yaml {% meta="{9}" %}
|
||||
// .github/workflows/main.yaml"
|
||||
// .github/workflows/main.yaml"
|
||||
...
|
||||
jobs:
|
||||
- job: main
|
||||
|
||||
@@ -3,6 +3,7 @@ title: Explain with AI
|
||||
sidebar:
|
||||
order: 17
|
||||
badge: beta
|
||||
filter: 'type:Features'
|
||||
---
|
||||
|
||||
{% youtube
|
||||
|
||||