Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e5ffa35c2d | |||
| d2b31f0792 | |||
| 183d8813c8 | |||
| 499e97e031 | |||
| 1e73846354 | |||
| d477b9489b | |||
| 38fd7e1fb9 | |||
| a28c8b56bc | |||
| dfeb020cea | |||
| cae9f9a95b | |||
| 3d64a456e2 | |||
| e754bc8eda | |||
| a9794d16db | |||
| b1bb06be59 | |||
| e2d68cd0e5 | |||
| ae0318a366 | |||
| 0a94b430b3 | |||
| 71a594be31 | |||
| 074e24a949 | |||
| ed3eaa801e | |||
| 7f89c9bea3 | |||
| 065f0c73d6 | |||
| 47a5245291 | |||
| e901d69a9b | |||
| cbb1958c7e | |||
| 09184018ff | |||
| e7b0f72e98 | |||
| 08f4dd921a | |||
| 3f522b8dae | |||
| 9ea879790d | |||
| 845cac6263 | |||
| 92ce35f542 | |||
| 2ac21e1ccb | |||
| 381d2c598f | |||
| ebd79fb6b9 | |||
| b2ab904357 | |||
| d543f5f192 | |||
| cc45a74cbe | |||
| 953bd884c8 | |||
| 29e30e4c31 | |||
| fc17e16891 | |||
| 56e18a0d24 | |||
| 964f6eb955 | |||
| 93d617caae | |||
| 793067ff4b | |||
| 9df9ff4093 | |||
| 6972a5add6 | |||
| 6ed36f0a76 | |||
| 0fd03d1504 | |||
| 7f04d5b05c | |||
| 8fb421aa95 | |||
| e0242f284d | |||
| 83b235e9dd | |||
| c8a6711dbb | |||
| d7538ffec4 | |||
| 823ba68a9c | |||
| 35d80be8a4 | |||
| 657e35de63 | |||
| cd39cb527b | |||
| 26b05db46a | |||
| 7140c0efca | |||
| f96d6282a8 | |||
| 170bf820ee | |||
| 197b3556d9 | |||
| b69a0587da | |||
| a0d834770d | |||
| a6a54cf5d0 |
@@ -23,6 +23,7 @@ jobs:
|
||||
NX_E2E_RUN_E2E: 'true'
|
||||
NX_CI_EXECUTION_ENV: 'linux'
|
||||
NX_CLOUD_NO_TIMEOUTS: 'true'
|
||||
NX_CLOUD_USE_NEW_STREAM_OUTPUT: 'true'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -3,7 +3,7 @@ name: publish
|
||||
on:
|
||||
# Automated schedule - canary releases from master
|
||||
schedule:
|
||||
- cron: "0 3 * * 2-6" # Tuesdays - Saturdays, at 3am UTC
|
||||
- cron: "0 19 * * 1-5" # Monday - Friday, at 19:00 UTC (7pm UTC)
|
||||
# Manual trigger - PR releases or dry-runs (based on workflow inputs)
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
@@ -21,8 +21,9 @@ env:
|
||||
DEBUG: napi:*
|
||||
NX_RUN_GROUP: ${{ github.run_id }}-${{ github.run_attempt }}
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
NODE_VERSION: 18
|
||||
PNPM_VERSION: 9.8.0 # Aligned with root package.json (pnpm/action-setup will helpfully error if out of sync)
|
||||
NODE_VERSION: 22.16.0
|
||||
PNPM_VERSION: 10.11.1 # Aligned with root package.json (pnpm/action-setup will helpfully error if out of sync)
|
||||
NPM_CONFIG_LOGLEVEL: error
|
||||
|
||||
jobs:
|
||||
# We first need to determine the version we are releasing, and if we need a custom repo or ref to use for the git checkout in subsequent steps.
|
||||
@@ -37,7 +38,7 @@ jobs:
|
||||
# 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 statica and we can use
|
||||
# - 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:
|
||||
@@ -51,31 +52,23 @@ jobs:
|
||||
publish_branch: ${{ steps.script.outputs.publish_branch }}
|
||||
ref: ${{ steps.script.outputs.ref }}
|
||||
repo: ${{ steps.script.outputs.repo }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
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@v4
|
||||
|
||||
# Set up pnpm and node so that we can verify our setup and that the NPM_TOKEN secret will work later
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
check-latest: true
|
||||
|
||||
# Ensure that the NPM_TOKEN secret is still valid before wasting any time deriving data or building projects
|
||||
- name: Check NPM Credentials
|
||||
run: npm whoami && echo "NPM credentials are valid" || (echo "NPM credentials are invalid or have expired." && exit 1)
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Resolve and set checkout and version data to use for release
|
||||
id: script
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.inputs.pr }}
|
||||
with:
|
||||
@@ -86,7 +79,7 @@ jobs:
|
||||
|
||||
- name: (PR Release Only) Check out latest master
|
||||
if: ${{ steps.script.outputs.ref != '' }}
|
||||
uses: actions/checkout@v4
|
||||
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
|
||||
@@ -95,24 +88,31 @@ jobs:
|
||||
|
||||
- name: (PR Release Only) Check out PR branch
|
||||
if: ${{ steps.script.outputs.ref != '' }}
|
||||
uses: actions/checkout@v4
|
||||
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 nx-release.ts has not changed in the PR being released
|
||||
- name: (PR Release Only) Ensure that release scripts have not changed in the PR being released
|
||||
if: ${{ steps.script.outputs.ref != '' }}
|
||||
env:
|
||||
FILE_TO_COMPARE: "scripts/nx-release.ts"
|
||||
run: |
|
||||
if ! cmp -s "latest-master-checkout/${{ env.FILE_TO_COMPARE }}" "pr-branch-checkout/${{ env.FILE_TO_COMPARE }}"; then
|
||||
echo "🛑 Error: The file ${{ env.FILE_TO_COMPARE }} is different on the ${{ steps.script.outputs.ref }} branch on ${{ steps.script.outputs.repo }} vs latest master on nrwl/nx, cancelling workflow. If you did not modify the file, then you likely just need to rebase/merge latest master."
|
||||
exit 1
|
||||
else
|
||||
echo "✅ The file ${{ env.FILE_TO_COMPARE }} is identical between the ${{ steps.script.outputs.ref }} branch on ${{ steps.script.outputs.repo }} and latest master on nrwl/nx."
|
||||
fi
|
||||
# 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 ]
|
||||
@@ -121,12 +121,21 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
settings:
|
||||
- host: macos-13
|
||||
- host: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
setup: |-
|
||||
rustup target add aarch64-apple-darwin
|
||||
build: |
|
||||
pnpm nx run-many --target=build-native -- --target=x86_64-apple-darwin
|
||||
- host: windows-latest
|
||||
build: pnpm nx run-many --target=build-native -- --target=x86_64-pc-windows-msvc
|
||||
setup: |-
|
||||
choco install openjdk --version=21.0.0 -y
|
||||
rustup target add 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=x86_64-pc-windows-msvc
|
||||
target: x86_64-pc-windows-msvc
|
||||
# Windows 32bit (not needed)
|
||||
# - host: windows-latest
|
||||
@@ -136,23 +145,65 @@ jobs:
|
||||
- host: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||
build: |-
|
||||
set -e &&
|
||||
npm i -g pnpm@9.8.0 --force &&
|
||||
pnpm --version &&
|
||||
pnpm install --frozen-lockfile &&
|
||||
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 x86_64-unknown-linux-gnu
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=x86_64-unknown-linux-gnu
|
||||
- host: ubuntu-latest
|
||||
target: x86_64-unknown-linux-musl
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
build: |-
|
||||
set -e &&
|
||||
npm i -g pnpm@9.8.0 --force &&
|
||||
pnpm --version &&
|
||||
pnpm install --frozen-lockfile &&
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=x86_64-unknown-linux-musl
|
||||
- host: macos-13
|
||||
build: |
|
||||
bash -c "
|
||||
set -e
|
||||
echo 'https://dl-cdn.alpinelinux.org/alpine/edge/community' >> /etc/apk/repositories
|
||||
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
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=x86_64-unknown-linux-musl
|
||||
"
|
||||
- host: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
setup: |-
|
||||
rustup target add aarch64-apple-darwin
|
||||
build: |
|
||||
sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*;
|
||||
export CC=$(xcrun -f clang);
|
||||
@@ -163,17 +214,35 @@ jobs:
|
||||
- host: ubuntu-latest
|
||||
target: aarch64-unknown-linux-gnu
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
|
||||
build: |-
|
||||
set -e &&
|
||||
npm i -g pnpm@9.8.0 --force &&
|
||||
pnpm --version &&
|
||||
pnpm install --frozen-lockfile &&
|
||||
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
|
||||
- host: ubuntu-latest
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
setup: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-arm-linux-gnueabihf -y
|
||||
rustup target add armv7-unknown-linux-gnueabihf
|
||||
build: |
|
||||
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=/usr/bin/arm-linux-gnueabihf-gcc pnpm nx run-many --target=build-native -- --target=armv7-unknown-linux-gnueabihf
|
||||
# Android (not needed)
|
||||
@@ -188,30 +257,59 @@ jobs:
|
||||
- host: ubuntu-latest
|
||||
target: aarch64-unknown-linux-musl
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
build: |-
|
||||
set -e &&
|
||||
rustup target add aarch64-unknown-linux-musl &&
|
||||
npm i -g pnpm@9.8.0 --force &&
|
||||
pnpm --version &&
|
||||
pnpm install --frozen-lockfile &&
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=aarch64-unknown-linux-musl
|
||||
build: |
|
||||
bash -c "
|
||||
set -e
|
||||
echo 'https://dl-cdn.alpinelinux.org/alpine/edge/community' >> /etc/apk/repositories
|
||||
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
|
||||
"
|
||||
- host: windows-latest
|
||||
target: aarch64-pc-windows-msvc
|
||||
build: pnpm nx run-many --target=build-native -- --target=aarch64-pc-windows-msvc
|
||||
name: stable - ${{ matrix.settings.target }} - node@18
|
||||
setup: |-
|
||||
choco install openjdk --version=21.0.0 -y
|
||||
rustup target add 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@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref }}
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo || github.repository }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref || github.ref }}
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
if: ${{ !matrix.settings.docker }}
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
@@ -219,13 +317,14 @@ jobs:
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
uses: actions-rust-lang/setup-rust-toolchain@ac90e63697ac2784f4ecfe2964e1a285c304003a # v1
|
||||
if: ${{ !matrix.settings.docker }}
|
||||
with:
|
||||
targets: ${{ matrix.settings.target }}
|
||||
target: ${{ matrix.settings.target }}
|
||||
rustflags: ''
|
||||
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index/
|
||||
@@ -235,7 +334,7 @@ jobs:
|
||||
target/
|
||||
key: ${{ matrix.settings.target }}-cargo-registry
|
||||
|
||||
- uses: goto-bus-stop/setup-zig@v2
|
||||
- uses: goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406 # v2.2.1
|
||||
if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' }}
|
||||
with:
|
||||
version: 0.10.0
|
||||
@@ -256,7 +355,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
|
||||
- name: Setup node x86
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
if: matrix.settings.target == 'i686-pc-windows-msvc'
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
@@ -265,7 +364,7 @@ jobs:
|
||||
architecture: x86
|
||||
|
||||
- name: Build in docker
|
||||
uses: addnab/docker-run-action@v3
|
||||
uses: addnab/docker-run-action@4f65fabd2431ebc8d299f8e5a018d79a769ae185 # v3
|
||||
if: ${{ matrix.settings.docker }}
|
||||
with:
|
||||
image: ${{ matrix.settings.docker }}
|
||||
@@ -278,12 +377,12 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: bindings-${{ matrix.settings.target }}
|
||||
path: |
|
||||
packages/**/*.node
|
||||
packages/**/*.wasm
|
||||
packages/nx/src/native/*.node
|
||||
packages/nx/src/native/*.wasm
|
||||
if-no-files-found: error
|
||||
|
||||
build-freebsd:
|
||||
@@ -293,30 +392,35 @@ jobs:
|
||||
name: Build FreeBSD
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref }}
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo || github.repository }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref || github.ref }}
|
||||
|
||||
- name: Build
|
||||
id: build
|
||||
uses: cross-platform-actions/action@v0.25.0
|
||||
uses: cross-platform-actions/action@462ed697694d2ac9aa49e1225f395f7bb6dd49fe # v0.29.0
|
||||
env:
|
||||
DEBUG: napi:*
|
||||
RUSTUP_IO_THREADS: 1
|
||||
NX_PREFER_TS_NODE: true
|
||||
PLAYWRIGHT_BROWSERS_PATH: 0
|
||||
NODE_VERSION: 22.16.0
|
||||
with:
|
||||
operating_system: freebsd
|
||||
version: '14.0'
|
||||
architecture: x86-64
|
||||
environment_variables: DEBUG RUSTUP_IO_THREADS CI NX_PREFER_TS_NODE PLAYWRIGHT_BROWSERS_PATH
|
||||
environment_variables: DEBUG RUSTUP_IO_THREADS CI NX_PREFER_TS_NODE PLAYWRIGHT_BROWSERS_PATH NODE_VERSION
|
||||
shell: bash
|
||||
run: |
|
||||
env
|
||||
whoami
|
||||
sudo pkg install -y -f node libnghttp2 www/npm git
|
||||
sudo npm install --location=global --ignore-scripts pnpm@9.8.0
|
||||
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"
|
||||
@@ -331,8 +435,59 @@ jobs:
|
||||
whoami
|
||||
env
|
||||
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"
|
||||
pnpm nx reset
|
||||
rm -rf node_modules
|
||||
rm -rf dist
|
||||
@@ -341,16 +496,18 @@ jobs:
|
||||
echo "COMPLETE"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: bindings-freebsd
|
||||
path: packages/**/*.node
|
||||
path: |
|
||||
packages/nx/src/native/*.node
|
||||
if-no-files-found: error
|
||||
|
||||
publish:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
name: Publish
|
||||
runs-on: ubuntu-latest
|
||||
environment: npm-registry
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
@@ -361,31 +518,32 @@ jobs:
|
||||
- build
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref }}
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo || github.repository }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref || github.ref }}
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
check-latest: true
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Use npm 11.5.2
|
||||
run: npm install -g npm@11.5.2
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
@@ -403,6 +561,7 @@ jobs:
|
||||
VERSION: ${{ needs.resolve-required-data.outputs.version }}
|
||||
DRY_RUN: ${{ needs.resolve-required-data.outputs.dry_run_flag }}
|
||||
PUBLISH_BRANCH: ${{ needs.resolve-required-data.outputs.publish_branch }}
|
||||
NX_VERBOSE_LOGGING: true
|
||||
run: |
|
||||
echo ""
|
||||
# Create and check out the publish branch
|
||||
@@ -420,11 +579,11 @@ jobs:
|
||||
|
||||
- name: (PR Release Only) Create comment for successful PR release
|
||||
if: success() && github.event.inputs.pr
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
SUCCESS_COMMENT: ${{ needs.resolve-required-data.outputs.success_comment }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# 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({
|
||||
@@ -434,22 +593,55 @@ jobs:
|
||||
body: successComment
|
||||
});
|
||||
|
||||
report-pending-publish:
|
||||
name: Report Pending Publish to Slack
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
needs:
|
||||
- resolve-required-data
|
||||
- build-freebsd
|
||||
- build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
continue-on-error: true # Don't fail the workflow if notification fails
|
||||
steps:
|
||||
- name: Send Slack notification
|
||||
uses: ravsamhq/notify-slack-action@be814b201e233b2dc673608aa46e5447c8ab13f2 # v11
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
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_users: 'U9NPA6C90' # Jason
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
|
||||
pr_failure_comment:
|
||||
# Run this job if it is a PR release, running on the nrwl origin, and any of the required jobs failed
|
||||
if: ${{ github.repository_owner == 'nrwl' && github.event.inputs.pr && always() && contains(needs.*.result, 'failure') }}
|
||||
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@v7
|
||||
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({
|
||||
@@ -458,3 +650,4 @@ jobs:
|
||||
issue_number: ${{ github.event.inputs.pr }},
|
||||
body: message
|
||||
});
|
||||
|
||||
|
||||
@@ -513,5 +513,10 @@
|
||||
"name": "nx-foundry",
|
||||
"description": "Foundry generators and inferred tasks for Nx",
|
||||
"url": "https://github.com/juliangsibecas/nx-foundry"
|
||||
},
|
||||
{
|
||||
"name": "@aws/nx-plugin",
|
||||
"description": "Nx Plugin for AWS: Accelerate building cloud-native applications with AWS",
|
||||
"url": "https://github.com/awslabs/nx-plugin-for-aws"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -299,6 +299,14 @@ Have a more decent button-like widget that you can place below sections of a tut
|
||||
{% video-link link="https://youtu.be/OQ-Zc5tcxJE?t=64" /%}
|
||||
```
|
||||
|
||||
#### Course video embed
|
||||
|
||||
This is for embedding a video just like with the Youtube component, but in addition to have a link to a Nx Course (nx.dev/courses) video to improve the discoverability of these courses.
|
||||
|
||||
```markdown
|
||||
{% course-video src="https://youtu.be/3hW53b1IJ84" courseTitle="From PNPM Workspaces to Distributed CI" courseUrl="/courses/pnpm-nx-next/lessons-01-nx-init" title="Initialize Nx in Your Project with nx init" /%}
|
||||
```
|
||||
|
||||
#### Project Details View
|
||||
|
||||
Embed a Project Details View that is identical what is shown in Nx Console or `nx show project myproject --web`
|
||||
|
||||
@@ -9,6 +9,15 @@ description: Learn to set up a monorepo with PNPM workspaces for Remix and React
|
||||
|
||||
In this article we're going to have a deep dive into setting up a new monorepo using [PNPM workspaces](https://pnpm.io/workspaces) that hosts a Remix application as well as a React-based library. We will learn how to run commands with PNPM, how to run them in parallel and finally we're going to add Nx for a more sophisticated task scheduling, including command caching and more.
|
||||
|
||||
{% callout type="warning" title="Updated video!" %}
|
||||
|
||||
We made a lot of improvements since we last wrote this article. Here's our **updated content**:
|
||||
|
||||
- [the full all-in-one video on Youtube](https://youtu.be/zX-1tpqUG5c)
|
||||
- [our free course: From PNPM Workspaces to Distributed CI](/courses/pnpm-nx-next)
|
||||
|
||||
{% /callout %}
|
||||
|
||||
**Important:** If you are already familiar with the setup and configuration of a new PNPM workspace, feel free to skip to the part where we add Nx later in the article.
|
||||
|
||||
**Prefer a video walkthrough?**
|
||||
|
||||
@@ -8,6 +8,15 @@ cover_image: /blog/images/articles/monorepos-are-ai-future-proof-bg.jpg
|
||||
youtubeUrl: https://youtu.be/RNilYmJJzdk
|
||||
---
|
||||
|
||||
{% callout type="deepdive" title="Series: Making your LLM smarter" expanded=true %}
|
||||
|
||||
- **Nx Just Made Your LLM Way Smarter**
|
||||
- [Making Cursor Smarter with an MCP Server For Nx Monorepos](/blog/nx-made-cursor-smarter)
|
||||
- [Nx MCP Now Available for VS Code Copilot](/blog/nx-mcp-vscode-copilot)
|
||||
- [Nx and AI: Why They Work so Well Together](/blog/nx-and-ai-why-they-work-together)
|
||||
|
||||
{% /callout %}
|
||||
|
||||
Everyone is constantly pushing to be more productive: delivering more features, reducing overhead, and keeping up with increasing demands. Companies want to move faster, and engineering teams work toward the same goal, looking for ways to streamline development and ship more with fewer resources. LLMs offer a promising way to boost efficiency, whether by assisting individual developers or improving workflows across a team.
|
||||
|
||||
However, the quality of an LLM's response depends entirely on the data and context it has access to. Ask an LLM (in this case, GitHub Copilot with GPT-4o) about the structure of an NPM workspace monorepo, and you probably won't be satisfied with the result:
|
||||
|
||||
@@ -12,7 +12,9 @@ pinned: true
|
||||
{% callout type="deepdive" title="Series: Making your LLM smarter" expanded=true %}
|
||||
|
||||
- [Nx Just Made Your LLM Way Smarter](/blog/nx-just-made-your-llm-smarter)
|
||||
- **Making Cursor Smarter**
|
||||
- **Making Cursor Smarter with an MCP Server For Nx Monorepos**
|
||||
- [Nx MCP Now Available for VS Code Copilot](/blog/nx-mcp-vscode-copilot)
|
||||
- [Nx and AI: Why They Work so Well Together](/blog/nx-and-ai-why-they-work-together)
|
||||
|
||||
{% /callout %}
|
||||
|
||||
@@ -124,19 +126,11 @@ If you want to use our MCP integration with other tools that support the protoco
|
||||
|
||||
## Let's see it in action
|
||||
|
||||
Here are some example queries, showing how Cursor leverages the different exposed Nx MCP tools to get more information to correctly take action. Jump to the [Youtube video for the full run-through]().
|
||||
The [Youtube video]() above showcases the below example queries, showing how Cursor leverages the different exposed Nx MCP tools to get more information to correctly take action.
|
||||
|
||||
**Query:** "If I change the public API of feat-product-detail, which other projects might be affected by that change?"
|
||||
|
||||
{% video-player src="/documentation/blog/media/nx-mcp-cursor-dependencies.mp4" alt="Asking Cursor about the impact of changing a project's public API" showDescription=true showControls=true autoPlay=false loop=false /%}
|
||||
|
||||
**Query:** "Use Nx to generate a new React library for handling past orders."
|
||||
|
||||
{% video-player src="/documentation/blog/media/nx-mcp-cursor-generate-project.mp4" alt="Asking Cursor to generate a new React project for listing past orders" showDescription=true showControls=true autoPlay=false loop=false /%}
|
||||
|
||||
**Query:** "Can you configure Nx release for the packages of this workspace? Just update nx.json with the necessary configuration. Use conventional commits as the versioning strategy. Also feel free to use the Nx docs to pull more info on how to configure it."
|
||||
|
||||
{% video-player src="/documentation/blog/media/nx-mcp-cursor-nx-release.mp4" alt="Asking Cursor to use the docs to configure Nx release for the workspace" showDescription=true showControls=true autoPlay=false loop=false /%}
|
||||
- If I change the public API of feat-product-detail, which other projects might be affected by that change?
|
||||
- Use Nx to generate a new React library for handling past orders.
|
||||
- Can you configure Nx release for the packages of this workspace? Just update nx.json with the necessary configuration. Use conventional commits as the versioning strategy. Also feel free to use the Nx docs to pull more info on how to configure it.
|
||||
|
||||
## Wrapping up
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ authors: ['Victor Savkin', 'Max Kless']
|
||||
tags: [webinar]
|
||||
cover_image: /blog/images/2025-03-19/March-2025-Webinar-card.png
|
||||
time: 1pm EDT/5pm UTC
|
||||
status: Upcoming
|
||||
status: Past - Gated
|
||||
registrationUrl: https://go.nx.dev/march2025-webinar
|
||||
---
|
||||
|
||||
@@ -17,4 +17,4 @@ Presented by Victor Savkin and Max Kless
|
||||
|
||||
In this webinar, Nx CTO Victor Savkin and Senior Engineer Max Kless will break down **why monorepos are inherently more AI-friendly**, giving LLMs the full context they need to generate more accurate, actionable insights. You’ll also learn how Nx provides deep architectural awareness, making AI tools smarter and more effective.
|
||||
|
||||
{% call-to-action title="Register today!" url="https://go.nx.dev/march2025-webinar" description="Save your spot" /%}
|
||||
{% call-to-action title="Download the recording" url="https://go.nx.dev/march2025-webinar" description="Sign up to gain access" /%}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
title: 'Scaffolding New Apps With Angular Rspack'
|
||||
date: 2025-04-15
|
||||
slug: scaffold-angular-rspack-applications
|
||||
authors: [Colum Ferry]
|
||||
tags: [angular, webpack, rspack, nx]
|
||||
cover_image: /blog/images/2025-04-14/scaffold-angular-rspack-applications.avif
|
||||
description: Learn about how to scaffold a new Angular Rspack application with Nx.
|
||||
---
|
||||
|
||||
It is not uncommon to see [Angular](https://angular.dev) applications that rely on a custom [Webpack](https://webpack.js.org) configuration - especially in enterprise settings. These applications rely heavily on the extensibility offered by Webpack via its Plugin and Loader ecosystem.
|
||||
|
||||
However, building Angular applications with Webpack has historically been slow. In fact, slow builds with Webpack is something that even the wider JavaScript Ecosystem has come to expect. The result? Many new bundler options have been created to tackle the issue. Vite, Rollup, Rolldown, Esbuild, Turbopack and more.
|
||||
|
||||
The frustration over build performance became so great that the Angular team decided to switch to [Esbuild](https://esbuild.github.io) which has offered an incredible improvement on build speed.
|
||||
|
||||
The problem with Esbuild – and with the other bundler options listed above – stems from the _reason_ teams needed custom Webpack configurations in the first place. The extensibility.
|
||||
|
||||
While most of the bundler options that are available today offer some kind of API to extend the build process, the ability to replicate the functionality of certain Webpack plugins and loaders is either impossible or would involve a large effort to build a homebrewed solution that would need to be maintained internally over time.
|
||||
|
||||
That is, until [ByteDance](https://bytedance.com/en) released [Rspack](https://rspack.dev).
|
||||
|
||||
## Rspack
|
||||
|
||||

|
||||
|
||||
Rspack is a high performance JavaScript bundler written in Rust. It offers strong compatibility with the Webpack ecosystem, allowing for near-seamless replacement of Webpack, while providing lightning fast build speeds.
|
||||
|
||||
That sounds compelling enough as it is, especially for anyone that is currently using Webpack to build their applications. You can see from the benchmark results below just _how much_ faster than Webpack it really is. You can view the benchmark details [here](https://github.com/rspack-contrib/build-tools-performance).
|
||||
|
||||

|
||||
|
||||
Docusaurus has even [reported that switching to Rspack has resulted in a 2x-4x faster production build](https://docusaurus.io/blog/releases/3.6#docusaurus-faster)!
|
||||
|
||||
However, Angular’s Webpack build pipeline proved to be an issue. It had not been possible to use Rspack as a drop-in replacement for Angular applications.
|
||||
|
||||
Which is why [Angular Rspack](/recipes/angular/rspack/introduction) was created.
|
||||
|
||||
## Angular Rspack
|
||||
|
||||

|
||||
|
||||
Angular Rspack provides Rspack Plugins and Loaders that makes it possible to build Angular applications with Rspack.
|
||||
|
||||
{% callout type="note" title="Learn More" %}
|
||||
|
||||
You can learn more about Angular Rspack from the resources below:
|
||||
|
||||
- [Angular Rspack - Introduction](/recipes/angular/rspack/introduction)
|
||||
- [Using Rspack with Angular](/blog/using-rspack-with-angular)
|
||||
|
||||
{% /callout %}
|
||||
|
||||
At the time of writing, Angular still supports building applications with Webpack alongside Esbuild. However, they are two different build pipelines.
|
||||
|
||||
Therefore, if the Angular team were to **drop support for Webpack** and remove the code from the Angular Devkit packages entirely, **Angular Rspack will continue to operate**.
|
||||
|
||||
- By using Rspack the majority of Plugins and Loaders used by Angular applications with custom Webpack Configurations will continue to be supported
|
||||
- It does not rely on Angular’s Webpack build pipeline directly. Instead it uses the abstractions in place for the Esbuild pipeline.
|
||||
|
||||
From the benchmark results below, you can see the vast improvement using Angular Rspack has over sticking with Webpack. You can view the benchmark details [here](https://github.com/nrwl/ng-bundler-benchmark).
|
||||
|
||||

|
||||
|
||||
You can see that the results from Rspack are comparable to the results provided by Angular’s Esbuild build pipeline.
|
||||
|
||||
That said, it is worth noting that there are still currently some [known limitations and missing features](/recipes/angular/rspack/introduction#known-limitations-and-missing-features).
|
||||
|
||||
## Why Choose Rspack for New Projects?
|
||||
|
||||
Given the results above, it’s clear that migrating Angular Webpack applications to Rspack can lead to significant time savings — reducing CI durations and boosting overall developer productivity.
|
||||
|
||||
But at this point, you might be asking _why_ you should start a new Angular project with **Rspack** instead of **Esbuild**, especially considering that benchmarks show Esbuild is still slightly faster when building Angular apps.
|
||||
|
||||
Here’s why Rspack is often the better choice for new projects:
|
||||
|
||||
- **Speed** – While Esbuild may still win out slightly in raw benchmarks, Rspack delivers massive speed improvements over traditional Webpack — especially in large projects or CI environments. It’s fast enough to feel like a completely different experience for developers used to legacy builds.
|
||||
- **Extensibility** – Rspack provides a much more extensible architecture than Esbuild. This allows teams to customize and fine-tune their build processes far more effectively. For projects that have complex build requirements or need to integrate custom tooling, this flexibility is a major advantage.
|
||||
- **Compatibility** – Rspack is designed to be highly compatible with the existing Webpack ecosystem. This means many plugins and loaders that teams already rely on can continue to work with minimal or no changes. It eases the migration path and preserves your existing investment in tooling and configuration.
|
||||
- **Micro-frontend support** – If you're building applications that rely heavily on **[Module Federation](https://module-federation.io)**, Rspack is the clear winner. Its compatibility and support for advanced use cases in micro-frontends are significantly more mature than what's possible with Esbuild.
|
||||
- **Chunking control** – Esbuild has a long-standing [issue](https://github.com/angular/angular-cli/issues/27715) with generating an excessive number of chunks, offering very limited control over how chunking is performed. This can cause performance issues in certain scenarios. In contrast, Rspack offers fine-grained chunking strategies that let teams precisely shape how their bundles are produced.
|
||||
|
||||
These advantages make Rspack not just a performance play, but a more adaptable and production-ready solution for modern Angular projects.
|
||||
|
||||
## How to Scaffold a New Angular Rspack Project?
|
||||
|
||||
Nx provides two methods for creating a new Angular Rspack project, one for existing Nx Workspaces and one for new Workspaces.
|
||||
|
||||
### New Workspaces
|
||||
|
||||
To create a new Nx Workspace with an Angular Rspack project run the following command and select the options listed below:
|
||||
|
||||
```{% command="npx create-nx-workspace myorg" path="~/" %}
|
||||
|
||||
NX Let's create a new workspace [[https://nx.dev/getting-started/intro](https://nx.dev/getting-started/intro)]
|
||||
|
||||
✔ Which stack do you want to use? · angular
|
||||
✔ Integrated monorepo, or standalone project? · integrated
|
||||
✔ Application name · myorg
|
||||
✔ Which bundler would you like to use? · rspack
|
||||
✔ Default stylesheet format · css
|
||||
✔ Do you want to enable Server-Side Rendering (SSR)? · No
|
||||
✔ Which unit test runner would you like to use? · vitest
|
||||
✔ Test runner to use for end to end (E2E) tests · playwright
|
||||
✔ Which CI provider would you like to use? · skip
|
||||
✔ Would you like remote caching to make your build faster? · skip
|
||||
|
||||
NX Creating your v20.8.0 workspace.
|
||||
|
||||
```
|
||||
|
||||
### Existing Workspaces
|
||||
|
||||
In existing Nx Workspaces, ensure you are on Nx version `20.8.0` or greater and simply run:
|
||||
|
||||
```
|
||||
|
||||
npx nx g @nx/angular:app myapp --bundler=rspack
|
||||
|
||||
```
|
||||
|
||||
### Converting an Existing Angular Webpack Application
|
||||
|
||||
If you have an existing Angular Webpack application you can easily migrate it to use Angular Rspack by using the `@nx/angular:convert-to-rspack` generator. The [Migrate Angular with Webpack to Rspack](/recipes/angular/rspack/migrate-from-webpack) guide explains more on this.
|
||||
|
||||
## Future Work
|
||||
|
||||
It’s been great to see the progress on Angular Rspack so far, but it’s not done yet. We’ll be working on making it closer to feature parity with the Angular CLI by addressing:
|
||||
|
||||
- Internationalization (i18n) support
|
||||
- Static Site Generation (SSG) support
|
||||
- and more!
|
||||
|
||||
Stay tuned to our socials to stay up to date on the latest Angular Rspack news!
|
||||
|
||||
- 🧠 [**Nx Docs**](/getting-started/intro)
|
||||
- 👩💻 [**Nx GitHub**](https://github.com/nrwl/nx)
|
||||
- 💬 [**Nx Official Discord Server**](https://go.nx.dev/community)
|
||||
- 📹 [**Nx Youtube Channel**](https://www.youtube.com/@nxdevtools)
|
||||
@@ -0,0 +1,126 @@
|
||||
---
|
||||
title: 'Nx MCP Now Available for VS Code Copilot'
|
||||
slug: nx-mcp-vscode-copilot
|
||||
authors: ['Juri Strumpflohner']
|
||||
tags: ['nx', 'nx-console', 'ai']
|
||||
cover_image: /blog/images/articles/bg-copilot-nx-mcp.avif
|
||||
description: 'Learn how to enhance VS Code Copilot with Nx MCP integration, providing rich monorepo context for smarter AI assistance.'
|
||||
youtubeUrl: https://youtu.be/dRQq_B1HSLA
|
||||
---
|
||||
|
||||
{% callout type="deepdive" title="Series: Making your LLM smarter" expanded=true %}
|
||||
|
||||
- [Nx Just Made Your LLM Way Smarter](/blog/nx-just-made-your-llm-smarter)
|
||||
- [Making Cursor Smarter with an MCP Server For Nx Monorepos](/blog/nx-made-cursor-smarter)
|
||||
- **Nx MCP Now Available for VS Code Copilot**
|
||||
- [Nx and AI: Why They Work so Well Together](/blog/nx-and-ai-why-they-work-together)
|
||||
|
||||
{% /callout %}
|
||||
|
||||
Back in [February we shipped the first version of Nx Console with AI features](/blog/nx-just-made-your-llm-smarter), directly integrating with GitHub Copilot's APIs to provide rich monorepo context. Now, VS Code has finally adopted the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction), and we've adapted our Cursor-based MCP implementation to work with all MCP clients, including Copilot. This integration makes your AI assistant even more powerful by providing rich context about your Nx workspace. Let's see how to set it up and what it can do for you.
|
||||
|
||||
{% toc /%}
|
||||
|
||||
## Setting up MCP in VS Code
|
||||
|
||||
Getting started with Nx's MCP integration in VS Code is straightforward:
|
||||
|
||||
1. **Install Nx Console** from the VS Code marketplace if you haven't already.
|
||||
2. Once installed, VS Code will show a notification to "Improve Copilot agent with Nx-specific context"
|
||||
3. Click "Yes" to automatically configure the Nx MCP server in your `.vscode/mcp.json` file
|
||||
|
||||

|
||||
|
||||
{% callout type="note" title="Missed the notification?" %}
|
||||
If you missed the notification, you can always run the `nx.configureMcpServer` command from the command palette (`Ctrl/Cmd + Shift + P`).
|
||||
{% /callout %}
|
||||
|
||||
VS Code will automatically start the MCP server when needed during your interactions with the LLM. You can verify the installation by checking your VS Code settings, where you should see the Nx MCP server listed along with its available tools.
|
||||
|
||||
## Why Use Nx MCP in Your Monorepo?
|
||||
|
||||
While VS Code and Copilot can already gather information from your monorepo context, Nx MCP provides several advantages:
|
||||
|
||||
1. **Efficient Data Access**: Instead of analyzing numerous files to infer relationships, Nx MCP provides direct access to pre-computed metadata about your workspace.
|
||||
2. **Rich Project Context**: Nx maintains detailed information about:
|
||||
- Project relationships and dependencies
|
||||
- Project tags and categorization
|
||||
- Available tasks and their configurations
|
||||
- Code ownership
|
||||
- And more...
|
||||
|
||||
This information is already maintained by Nx for optimizing your monorepo, and the Nx MCP makes it now available to your AI assistant as well. Most importantly, this enables Copilot to move beyond simple file-level understanding to gain more of an **architectural awareness of your workspace**. Instead of just seeing individual files and their contents, it now understands the broader context: how projects are connected, where integration points exist, which teams own what, and how changes might impact the broader system. This architectural awareness is particularly powerful in a monorepo setting, where understanding these relationships is crucial for making informed development decisions.
|
||||
|
||||
## Nx MCP in Action
|
||||
|
||||
Let's look at some practical examples of how Nx MCP enhances your AI assistant's capabilities:
|
||||
|
||||
### Understanding Project Relationships
|
||||
|
||||
You can ask questions about project dependencies and get accurate answers based on the actual project graph:
|
||||
|
||||
```typescript
|
||||
// Example query:
|
||||
'Use the provided Nx MCP tools to figure out which order projects
|
||||
are related to the data-access-order project';
|
||||
```
|
||||
|
||||
The AI will use the `nx_workspace` tool to analyze project relationships and can even visualize them using the `nx_visualize_graph` tool, showing both direct dependencies and upstream projects.
|
||||
|
||||
### Smart Code Generation
|
||||
|
||||
While LLMs can generate code on their own, Nx MCP provides a powerful combination of predictable code generation through Nx generators and AI-driven customization:
|
||||
|
||||
```typescript
|
||||
// Example query:
|
||||
"Create a new feature library in packages/order/feat-cancel-order.
|
||||
Make it a React library and link it to the data-access-order project.
|
||||
Don't use a bundler or generate components."
|
||||
```
|
||||
|
||||
The AI will:
|
||||
|
||||
1. Use `nx_generators` to identify available generators
|
||||
2. Execute the React library generator with precise options
|
||||
3. Set up proper project tags and dependencies
|
||||
|
||||
### Documentation-Aware Configuration
|
||||
|
||||
To prevent hallucination and ensure up-to-date information, Nx MCP provides access to the current Nx documentation:
|
||||
|
||||
```typescript
|
||||
// Example query:
|
||||
'Add a task pipeline in nx.json that runs the build of all downstream projects before the dev command';
|
||||
```
|
||||
|
||||
The AI will:
|
||||
|
||||
1. Query the Nx docs for task pipeline configuration
|
||||
2. Update `nx.json` with the correct syntax
|
||||
3. Configure the dependencies accurately
|
||||
|
||||
## Beyond File-Level Thinking
|
||||
|
||||
This integration isn't just about fancy AI features - it's about making your existing editor experience more productive by integrating AI capabilities where they actually make sense. Instead of jumping on the AI hype train, we're focusing on exposing Nx's deep understanding of your workspace directly in your editor through tools like Copilot. This enables:
|
||||
|
||||
- More precise and contextual suggestions
|
||||
- Architecture-aware refactoring
|
||||
- Better understanding of project boundaries and relationships
|
||||
- Improved onboarding assistance for new team members
|
||||
|
||||
We're actively developing this integration and would love to hear your feedback! If you have ideas about what you'd like to see or how we can make this more useful for your workflow, reach out to us on [Twitter/X](https://x.com/NxDevTools), [Bluesky](https://bsky.app/profile/nx.dev), [LinkedIn](https://www.linkedin.com/company/nrwl/), or join our [weekly office hours on Discord](http://go.nx.dev/office-hours).
|
||||
|
||||
Also, subscribe to our [YouTube channel](https://www.youtube.com/@nxdevtools) for upcoming feature announcements and demonstrations.
|
||||
|
||||
Want to try it out? Install [Nx Console from the VS Code marketplace](/getting-started/editor-setup) and follow the MCP setup prompts to get started.
|
||||
|
||||
---
|
||||
|
||||
Learn more:
|
||||
|
||||
- 🧠 [Nx AI Docs](/features/enhance-AI)
|
||||
- 📖 [Making Cursor Smarter with MCP](/blog/nx-made-cursor-smarter)
|
||||
- 👩💻 [Nx GitHub](https://github.com/nrwl/nx)
|
||||
- 👩💻 [Nx Console GitHub](https://github.com/nrwl/nx-console)
|
||||
- 💬 [Nx Official Discord Server](https://go.nx.dev/community)
|
||||
- 📹 [Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
|
||||
@@ -0,0 +1,190 @@
|
||||
---
|
||||
title: 'Nx and AI - Why They Work so Well Together'
|
||||
slug: nx-and-ai-why-they-work-together
|
||||
authors: ['Victor Savkin']
|
||||
tags: ['nx', 'ai', 'monorepo']
|
||||
cover_image: /blog/images/articles/bg-nx-and-ai-powered-dev.avif
|
||||
description: 'Explore how Nx monorepos amplify AI benefits by providing complete context and consistent tooling, creating a growing competitive advantage for enterprise development.'
|
||||
---
|
||||
|
||||
{% callout type="deepdive" title="Series: Making your LLM smarter" expanded=true %}
|
||||
|
||||
- [Nx Just Made Your LLM Way Smarter](/blog/nx-just-made-your-llm-smarter)
|
||||
- [Making Cursor Smarter with an MCP Server For Nx Monorepos](/blog/nx-made-cursor-smarter)
|
||||
- [Nx MCP Now Available for VS Code Copilot](/blog/nx-mcp-vscode-copilot)
|
||||
- **Nx and AI: Why They Work so Well Together**
|
||||
|
||||
{% /callout %}
|
||||
|
||||
**TLDR:**
|
||||
|
||||
1. **Monorepos Amplify AI Benefits**: Monorepos inherently enhance the utility of coding assistants by providing complete context, creating a growing competitive advantage.
|
||||
2. **Consistency is Key**: The primary challenge with AI coding assistants is maintaining consistency and reliability. This affects both AI and human developers' future effectiveness, but can be mitigated with Nx capabilities.
|
||||
3. **Integrated Tooling**: Tools connecting CI, editors, and coding assistants create powerful development workflows.
|
||||
4. **Mitigated Downsides**: Traditional monorepo disadvantages (like the necessity for large-scale code changes) can be effectively addressed with agentic AI.
|
||||
5. **Cross-Repository Context**: Benefits extend beyond single repositories to connected repositories using Nx Cloud Polygraph.
|
||||
|
||||
{% toc /%}
|
||||
|
||||
{% call-to-action title="Are Monorepos the Answer to Better AI-Assisted Development?" url="https://go.nx.dev/april2025-webinar" description="Upcoming webinar: save your spot" /%}
|
||||
|
||||
## It's All About Context
|
||||
|
||||
> _"Context is worth 80 IQ points." — Alan Kay_
|
||||
|
||||
Why do tools like Bolt.new work so well for streamers and indie developers bootstrapping new projects but not so much for enterprise developers that work on an existing large codebase? The answer is context. With Bolt, an LLM can access the entire context of a smaller repo, making local changes with short-term considerations.
|
||||
|
||||
Enterprise systems present a different challenge:
|
||||
|
||||
- Highly complex and interdependent
|
||||
- Built by hundreds of engineers over many years
|
||||
- Often spread across hundreds of repositories
|
||||
- Integrated with numerous external systems
|
||||
|
||||
In these environments, **LLMs lack the necessary context to make changes that account for all considerations**. Like human developers, LLMs struggle with complex systems, but unlike humans, they don't have tacit knowledge from wikis, meetings, or informal conversations. As a result, they only see a small fraction of the system.
|
||||
|
||||

|
||||
|
||||
LLMs rely entirely on provided context. Because monorepos consolidate all code in one place, they fundamentally improve contextual access. However, **raw code access isn't enough. It's analogous to navigating a city using only street view.** This makes it unlikely to pick optimal routes.
|
||||
|
||||

|
||||
|
||||
**What's needed is a "map" of your codebase and organization. Nx via its plugins builds this "map".** It includes:
|
||||
|
||||
1. High-level repository structure
|
||||
2. System architecture
|
||||
3. Organizational structure and ownership
|
||||
4. Information about used tools, frameworks, their versions and dependencies
|
||||
5. CI information (frequency, failures)
|
||||
|
||||
This information is used to make CI execution fast, to ensure boundaries, and now **Nx provides this information to [coding assistants through MCP servers](/features/enhance-AI), enabling LLMs to answer questions like**:
|
||||
|
||||
- "Who should I consult about creating a new bank library for overdraft payments?"
|
||||
- "Create a library using the bank team's best practices."
|
||||
- "Identify and suggest fixes for circular dependencies."
|
||||
- "Which shared libraries does the overdraft library depend on? Which teams own them?"
|
||||
- "Which projects owned by my team fail frequently in CI? Which ones affect other teams' work?"
|
||||
|
||||

|
||||
|
||||
With this map, Nx provides a "30,000-foot view" of the entire system (architecture, organization, CI). Coding assistants are able to read relevant files and see the 0-foot view. This combination creates very powerful interactions.
|
||||
|
||||
But we are working on providing a "10,000-foot view" by compressing relevant content about projects and project clusters in the repository. Another way to think of this is **"d.ts" files for AI coding assistants**. With this, your coding assistant is able to jump between different abstraction levels to get relevant information.
|
||||
|
||||

|
||||
|
||||
## Beyond Speed: Writing Correct and Consistent Code
|
||||
|
||||
> _"It is better to take many small steps in the right direction than to make a great leap forward only to stumble backward." — Chinese Proverb_
|
||||
|
||||
Research shows enterprise developers commit only 20-30 lines of code daily. The bottleneck isn't coding speed but determining the correct approach to minimize technical debt and long-term impact.
|
||||
|
||||
Enterprise developers spend 90%+ of their time not actively writing code. **LLMs excel at generating code, and if that's what they are used for, they can only help with that 10% of the development process.**
|
||||
|
||||
To realize substantial gains we need to:
|
||||
|
||||
1. Enable LLMs to help with the 90% by providing better context (see section above)
|
||||
2. Ensure generated code is correct, consistent with the organization's best practices, and doesn't introduce technical debt
|
||||
|
||||
Without these considerations, you might be 5x more productive in the first month but progressively slower throughout the year due to accumulated technical debt. So you might not even break even.
|
||||
|
||||
### Consistency Through Best Practices and Metadata
|
||||
|
||||
One way to minimize technical debt is by codifying and reusing best practices:
|
||||
|
||||
**Human Approach**:
|
||||
|
||||
- Senior developers examine feature libraries from the bank team
|
||||
- They identify common patterns and create a local plugin with a generator
|
||||
- They teach other developers about the new generator
|
||||
|
||||
**LLM Approach**:
|
||||
|
||||
- Developers ask LLM powered by Nx to extract common patterns from all bank libraries
|
||||
- LLM creates a local plugin with a generator for bank libraries
|
||||
- Developer refines the generator
|
||||
- Another developer requests a new bank feature library
|
||||
- LLM finds and runs the new generator in dry-run mode for confirmation
|
||||
- LLM can open a generator UI for parameter tweaking
|
||||
- Developer selects parameters and generates code
|
||||
- LLM picks it up and offers additional customization
|
||||
|
||||
You don't have to create libraries reflecting organization's best practices. Nx plugins come with many generators.
|
||||
|
||||
The reason why this approach works with Nx is that everything in Nx (including [its generators](/features/generate-code)) comes with a lot of static metadata. Traditionally this metadata was used to power the terminal UI, graphic UI, docs and more. Turns out it's also extremely useful for LLMs. **With this metadata they know all the inputs into each generator and what each of them does. But it gets better! Nx's virtual file system lets LLMs run the generators in dry run mode to get the generator outputs.** With this, LLMs can derive which generators work well with which parameters. **Additional metadata specifically designed for LLMs is being added.**
|
||||
|
||||
LLMs excel at impressive demonstrations but struggle with consistency and correctness. This is why they work better for new applications where correctness is loosely defined and consistency is not important. Being able to access a large library of annotated generators helps LLMs reduce variability of what they generate, which improves consistency and quality. They use a generator and make some small modifications on top instead of trying to author everything from scratch.
|
||||
|
||||
### Migrations
|
||||
|
||||
[Nx Migrations](/features/automate-updating-dependencies) are special generators that update codebases to use newer tool versions and new APIs. AI agents can enhance this process with specialized metadata for validation and fixes.
|
||||
|
||||
### Human in the Loop
|
||||
|
||||
Human oversight remains crucial as AI is good at being "almost correct." Having a human in the loop, especially for high-impact operations, helps with the need to correct what LLMs generate. Nx offers a tool that lets LLMs open the generator UI and allows the developer to customize and preview what will be generated.
|
||||
|
||||
## The Integrated Development Advantage: Multiplying AI's Power
|
||||
|
||||
> _"The whole is greater than the sum of its parts." — Aristotle_
|
||||
|
||||
The utility of your coding assistant goes up significantly as the number of tools it can access increases, which includes the rest of your editor and IDE and your local tools. Nx has always believed in this, and that's why we always put so much effort into our VSCode and IntelliJ plugins. The bet paid off. With agentic AI being available, the value of those plugins is going up 10x.
|
||||
|
||||
This is the sketch I drew a while back, and we have been moving toward this vision:
|
||||
|
||||

|
||||
|
||||
**These are some of the things Nx either already does or is about to do in the next couple of weeks:**
|
||||
|
||||
1. Coding assistants can open and manipulate the [Nx project graph visualization](/features/explore-graph).
|
||||
2. Coding assistants are able to open the generation UI, prefill the right values.
|
||||
3. Coding assistants are able to fetch up-to-date docs to ensure they don't hallucinate.
|
||||
4. Coding assistants get notified about CI execution results while the CI is still running. The assistant gets relevant logs and files and is able to explain or fix the CI error and push changes before the CI execution even completes.
|
||||
5. Coding assistants will integrate with the new Nx interactive UI so they can see what the developer sees and are able to address issues while the Nx command is still running. **Tell the LLM to fix the error, and it will know what error you are looking at.**
|
||||
|
||||
## From Pain Points to Power Moves
|
||||
|
||||
> _"What was once impossible becomes merely difficult; what was difficult becomes easy; and what was easy becomes elegant." — Alan Kay_
|
||||
|
||||
Monorepos offer many benefits but come with caveats. The main concern is managing large-scale changes across the repository, such as updating all applications to the latest React version.
|
||||
|
||||
Nx has always addressed this with ["migrations" - specialized generators](/features/automate-updating-dependencies) optimized for such tasks. Typically, migrations handle 80% of the work, with the remaining 20% potentially taking weeks for a small team in large repos.
|
||||
|
||||
This is precisely where agentic coding assistants excel. **By building on Nx's migration capabilities, AI increases completion from 80% to a much higher percentage, reducing effort from weeks to days or even hours.** What was difficult becomes easy.
|
||||
|
||||
The ability to update all code simultaneously is also a significant benefit of monorepos. Small changes like renaming can be executed in a single step, eliminating entire classes of "breaking" changes. Your code is less defensive. This is a huge advantage preventing your code from becoming stale. It's a form of economy of scale, where you can make a code cleanup for a dozen applications in a time that is a tiny fraction of what it would have taken if they were in separate repos.
|
||||
|
||||
**Agentic coding assistants significantly expand the range of "reliable transformations" that can be done quickly, turning what were before long refactorings into minutes-long tasks. With this, the delta between what you can do in a monorepo compared to a polyrepo gets substantially more significant.**
|
||||
|
||||
## Repository Interconnectedness: Breaking Down the Final Silos
|
||||
|
||||
> _"Silos create boundaries. Networks create possibilities." — Anonymous_
|
||||
|
||||
This document was about Nx monorepos. But most organizations using Nx have dozens or hundreds of Nx repos and oftentimes hundreds or thousands of other repos as well. Because we recognize this reality, we built **Nx Polygraph** (available for [Nx Enterprise users](/enterprise)) which lets you connect multiple repositories into a federated meta-repo. With this, you will get some of the advantages outlined in this document in H1 2025.
|
||||
|
||||
## The Long View: Designing for AI's Continued Evolution
|
||||
|
||||
_"The path of technological progress is neither random nor accidental, but follows paths of least resistance toward inevitable destinations." — Kevin Kelly_
|
||||
|
||||
Some might argue that coding assistants aren't that reliable now and perhaps some of the benefits aren't realizable. A lot of it is new ([VSCode MCP support is a few weeks old](/blog/nx-mcp-vscode-copilot)) so we are working hard to provide the necessary context to them, and not everything at this point is as good as it should be. What matters though is not the current state but the direction.
|
||||
|
||||
**Many advantages are inherent to Nx's approach:**
|
||||
|
||||
- Consolidating information
|
||||
- Providing a navigable "map" of the codebase
|
||||
- Using metadata-driven generators and migrations that are inherently LLM-friendly
|
||||
- Enabling large-scale transformations in a monorepo
|
||||
|
||||
Although it's not entirely impossible, it is significantly harder to provide the same advantages in a polyrepo where information isn't consolidated, where there is no "map", where there is no codification of best practices, where cross-cutting code changes are effortful and non-atomic.
|
||||
|
||||
**The trajectory is clear: monorepos with tools like Nx are positioned to leverage AI capabilities more effectively as the technology evolves.**
|
||||
|
||||
---
|
||||
|
||||
Learn more:
|
||||
|
||||
- 🧠 [Nx AI Docs](/features/enhance-AI)
|
||||
- 📖 [Nx MCP Now Available for VS Code Copilot](/blog/nx-mcp-vscode-copilot)
|
||||
- 👩💻 [Nx GitHub](https://github.com/nrwl/nx)
|
||||
- 👩💻 [Nx Console GitHub](https://github.com/nrwl/nx-console)
|
||||
- 💬 [Nx Official Discord Server](https://go.nx.dev/community)
|
||||
- 📹 [Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: 'Nx Cloud Update - Assignment rules come out of beta and more!'
|
||||
slug: nx-cloud-update-april-2025
|
||||
authors: ['Philip Fulcher']
|
||||
tags: ['nx-cloud']
|
||||
cover_image: /blog/images/2025-04-21/header.avif
|
||||
description: 'Nx Cloud updates for April 2025 include assignment rules coming out of beta, CIPE filters, flaky retry configuration, and more!'
|
||||
youtubeUrl: https://youtu.be/mug02alT7rQ
|
||||
---
|
||||
|
||||
We're rolling out updates to Nx Cloud constantly, so we wanted to bring you up to date with what we've been working on lately.
|
||||
|
||||
{% toc /%}
|
||||
|
||||
## Assignment Rules come out of beta
|
||||
|
||||
In your CI pipeline, you've got some tasks of different sizes. Some tasks are quick, like linting or format checking, but you've got more resource-intensive tasks like building apps or running e2e suites. Your smaller tasks can run on a small agent with fewer CPU cores or less memory, but your larger tasks need a larger agent.
|
||||
|
||||
But when you're spreading tasks across multiple agents, it's inefficient to increase the size of all the agents to accommodate your larger tasks. So ideally, you have smaller agents for small tasks and larger agents for your larger tasks. But how do you make sure tasks get assigned to the right agent?
|
||||
|
||||
Many CI tools have ways of handling this, but we wanted to make things easier. That's why Nx Cloud now has [Assignment Rules](/ci/reference/assignment-rules). This configuration allows you to control which tasks are assigned to certain agents.
|
||||
|
||||
Let's take a look at a quick example for [Nx Agents](/ci/features/distribute-task-execution):
|
||||
|
||||
```yaml {% fileName=".nx/workflows/distribution-config.yaml" /%}
|
||||
# Create five agents using the `linux-medium-js` launch template and five agents using `linux-large-js`
|
||||
distribute-on:
|
||||
default: 5 linux-medium-js, 5 linux-large-js
|
||||
|
||||
assignment-rules:
|
||||
# for our `app1` projects...
|
||||
- projects:
|
||||
- app1
|
||||
# for targets beginning with "e2e-ci"...
|
||||
targets:
|
||||
- e2e-ci*
|
||||
# and using the `production` configuration
|
||||
configurations:
|
||||
- production
|
||||
# run matching tasks on a `linux-medium-js` agent with parallelism set to 5
|
||||
run-on:
|
||||
- agent: linux-medium-js
|
||||
parallelism: 5
|
||||
|
||||
# for lint and build tasks on any project
|
||||
- targets:
|
||||
- lint
|
||||
- build
|
||||
# run matching tasks on a `linux-large-js` agent with parallelism set to 10
|
||||
run-on:
|
||||
- agent: linux-large-js
|
||||
parallelism: 10
|
||||
```
|
||||
|
||||
As you can see, there's a lot of flexibility in the configuration here to fine-tune your agent assignments.
|
||||
|
||||
We're so excited about this feature that we've built it to work with both [Agents](/ci/features/distribute-task-execution) and [Manual DTE](/ci/recipes/dte/github-dte). So, no matter how you're distributing tasks using Nx Cloud, you can use this new feature! Be sure to [check the docs](/ci/reference/assignment-rules) for more details.
|
||||
|
||||
We're also providing a new UI that tells you exactly what tasks are being run on which agents and the rules impacting them.
|
||||
|
||||

|
||||
|
||||
## Find your CIPEs with better filtering
|
||||
|
||||

|
||||
|
||||
Having trouble finding a CI Pipeline Execution (CIPE)? We've added new filter options for the CIPEs list. You can now filter by committer, branch, or date range. You can even sort by duration to help find your longest CIPEs.
|
||||
|
||||
## Control how flaky task retries work with your workspace
|
||||
|
||||

|
||||
|
||||
Flaky retries are great at unblocking your CI pipelines by automatically retrying tasks that Nx Cloud detects as flaky. This means you spend less time re-running CI pipelines when you have a task that passes sometimes and fails other times. You now have more control over this process. You can fine-tune how Nx Cloud retries tasks, specify a time range when flakiness is determined, or even turn it off entirely. This helps optimize when tasks are retried so you can control resource use and CI cost.
|
||||
|
||||
## Request access to protected views
|
||||
|
||||

|
||||
|
||||
Access control is important, but it can be time-consuming to give the right access to the right people. Now, users can request access with a simple button click, and an admin will be alerted to adjust their access. This automated process saves your team time by not having to send another email or submit a support ticket for access.
|
||||
|
||||
## Nx Agents supports custom GitHub domains
|
||||
|
||||

|
||||
|
||||
Using GitHub Enterprise and having a custom URL? Nx Agents now works with these custom domains, check for the option to override your domain during setup.
|
||||
|
||||
{% call-to-action title="Not using Nx Cloud yet?" url="https://cloud.nx.app/get-started/?utm_source=nx-dev" icon="nxcloud" description="Get started now for free!" /%}
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [Nx Cloud Docs](/ci)
|
||||
- [Nx Docs](/getting-started/intro)
|
||||
- [X/Twitter](https://twitter.com/nxdevtools)
|
||||
- [LinkedIn](https://www.linkedin.com/company/nrwl/)
|
||||
- [Nx GitHub](https://github.com/nrwl/nx)
|
||||
- [Nx Official Discord Server](https://go.nx.dev/community)
|
||||
- [Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
|
||||
- [Speed up your CI](/nx-cloud)
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
title: '3 Test Splitting Techniques that Cut E2E Times up to 90%'
|
||||
slug: test-splitting-techniques
|
||||
authors: [Miroslav Jonaš]
|
||||
description: "Learn the techniques for optimizing CI by splitting long-running tests, using sharding, Atomizer, and manual E2E project splitting, all enhanced by Nx Cloud's distributed task execution for improved stability and performance."
|
||||
tags: [nx, architecture]
|
||||
cover_image: /blog/images/articles/bg-test-splitting-techniques.avif
|
||||
---
|
||||
|
||||
> "There's nothing worse than waiting for a build to complete, or those e2e tests to run. With Nx Cloud, our development team has saved over 104 hours, almost cutting our build times in half. The installation is seamless and the results are immediate. It's nice to have a tool that passively saves so much development time." - Director of Software Development, enterprise digital marketing firm
|
||||
|
||||
One of the most impactful core features of Nx is the affected graph. The affected graph helps us to skip unnecessary work and focus only on the things that have been changed, speeding up our CI and helping us ship features and hotfixes faster.
|
||||
|
||||
However, long-running tasks, especially End-to-End (E2E) tests, can become a significant bottleneck and prevent getting the code changes out faster. This is particularly true for monolithic projects, but also in cases when there is a single large E2E project that covers the entire scope of the application. In these scenarios, the full benefits of an affected graph cannot be realized.
|
||||
|
||||
In this guide, we'll explore three techniques to speed up your CI by splitting these lengthy test tasks.
|
||||
|
||||
{% toc /%}
|
||||
|
||||
## Built-in Test Sharding
|
||||
|
||||
One of the simplest ways to split long-running tests is by using built-in test sharding features available in popular testing frameworks like **Jest** and **Playwright**. These tools allow you to divide your test suite into multiple shards that can be executed in parallel, reducing the perceived test execution time.
|
||||
|
||||
In **Jest** we can utilize the new `--shard` option to split your test suite. The example below shows splitting into 4 shards.
|
||||
|
||||
```shell
|
||||
nx affected -t test -- --shard=1/4
|
||||
nx affected -t test -- --shard=2/4
|
||||
nx affected -t test -- --shard=3/4
|
||||
nx affected -t test -- --shard=4/4
|
||||
```
|
||||
|
||||
**Playwright** also supports the `--shard` option:
|
||||
|
||||
```shell
|
||||
nx affected -t e2e -- --shard=1/4
|
||||
nx affected -t e2e -- --shard=2/4
|
||||
nx affected -t e2e -- --shard=3/4
|
||||
nx affected -t e2e -- --shard=4/4
|
||||
```
|
||||
|
||||
Now that we have our tests sharded, we can distribute the test load and achieve faster feedback. But what about the test runners that don't support sharding?
|
||||
|
||||
## Nx Atomizer
|
||||
|
||||
{% youtube
|
||||
src="https://youtu.be/0YxcxIR7QU0"
|
||||
title="10x Faster e2e Tests!"
|
||||
width="100%" /%}
|
||||
|
||||
For more granular control over test distribution, Nx offers the [Atomizer](/ci/features/split-e2e-tasks). This feature allows you to split tasks per file. This splitting further allows us to distribute long-running tasks across a larger number of agents, providing detailed insights into flaky tests and enabling automatic re-runs. If one of the flaky tests fails, we will still cache the results of all the other task slices and can even have a successful run if the flaky test re-run succeeded.
|
||||
|
||||

|
||||
|
||||
With Atomizer, you can achieve a higher level of parallelism and ensure that only the necessary tests are executed, further optimizing your CI pipeline.
|
||||
|
||||
To enable the Atomizer, we need to use supported inferred plugins or create our own.
|
||||
|
||||
```json {% fileName="nx.json" %}
|
||||
{
|
||||
// ...
|
||||
"plugins": [
|
||||
{
|
||||
"plugin": "@nx/cypress/plugin",
|
||||
"options": {
|
||||
"targetName": "e2e",
|
||||
"ciTargetName": "e2e-ci"
|
||||
}
|
||||
},
|
||||
{
|
||||
"plugin": "@nx/playwright/plugin",
|
||||
"options": {
|
||||
"targetName": "e2e",
|
||||
"ciTargetName": "e2e-ci"
|
||||
}
|
||||
},
|
||||
{
|
||||
"plugin": "@nx/jest/plugin",
|
||||
"options": {
|
||||
"targetName": "test",
|
||||
"ciTargetName": "test-ci"
|
||||
}
|
||||
},
|
||||
{
|
||||
"plugin": "@nx/gradle",
|
||||
"options": {
|
||||
"classesTargetName": "classes",
|
||||
"buildTargetName": "build",
|
||||
"testTargetName": "test",
|
||||
"ciTargetName": "test-ci"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The `test-ci` and `e2e-ci` targets will automatically be split into the following format:
|
||||
|
||||
- `e2e-ci--path/to/test/file`
|
||||
- `test-ci--path/to/test/file`
|
||||
|
||||
Or more generically:
|
||||
|
||||
- `{ciTargetName}--{path/to/test/file}`
|
||||
|
||||
You can find more information on how to configure the Atomizer on the respective [Jest](/nx-api/jest#splitting-e2e-tests), [Cypress](/nx-api/cypress#nxcypress-configuration), [Playwright](/nx-api/playwright#nxplaywright-configuration), [Gradle](/nx-api/gradle/documents/overview#nxgradle-configuration) or follow [this recipe](/extending-nx/recipes/project-graph-plugins) to create your own inferred plugin.
|
||||
|
||||
## Manual E2E Project Splitting
|
||||
|
||||
In addition to automated splitting like sharding or atomization, manually splitting E2E projects into scopes can provide additional significant performance benefits.
|
||||
|
||||
Let's look at the simplified graph below:
|
||||
|
||||

|
||||
|
||||
Our E2E project contains tests for each of the application features - products, orders and checkout. Any change made in the graph will cause all our E2E tests to be re-run. Even if we only modified `products`, we will still re-run the tests for `orders` and `checkout`. Although the Atomizer will help us split that work per file and distribute it, we will still end up running unnecessary work.
|
||||
|
||||
By defining scopes that implicitly depend on feature libraries rather than the entire application, you can ensure that only relevant tests are run when changes are made.
|
||||
|
||||
- **Scope Definition**: Break down your E2E tests into smaller, focused scopes.
|
||||
- **Dependency Management**: Ensure that scopes depend on specific feature libraries, reducing unnecessary test execution.
|
||||
|
||||

|
||||
|
||||
This approach offers both speed of distribution and caching efficiency. Every time the application is affected, we will only run the small subset of sanity smoke tests to ensure the application still runs, but specific features will only be tested if the relevant feature library has been modified or affected and skipped otherwise.
|
||||
|
||||
The tricky part comes from the fact that our split E2E applications still depend on the full application being served. But using the combination of `implicitDependencies` and `dependsOn` we can ensure that the application is running for our E2E tests without explicitly depending on it.
|
||||
|
||||
```json {% fileName="libs/checkout-e2e/project.json" %}
|
||||
{
|
||||
...
|
||||
"implicitDependencies": ["checkout"],
|
||||
"targets": {
|
||||
"e2e": {
|
||||
"dependsOn": ["^build", { "target": "build", "projects": "app" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When we look at the graph, we will only see an edge from `checkout-e2e` to `checkout`, but having an explicit `dependsOn` `app:build` ensures that the build of the application was successful and the distributed agent running our E2E task has app's build cache replayed.
|
||||
|
||||
As of Nx version `20.8.0` you can now combine manual splitting with the Atomizer. In order to split atomized projects, we will have to override their `dependsOn` property to target also `app:build`:
|
||||
|
||||
```json {% fileName="libs/checkout-e2e/project.json" %}
|
||||
{
|
||||
...
|
||||
"implicitDependencies": ["checkout"],
|
||||
"targets": {
|
||||
"e2e": {
|
||||
"dependsOn": ["^build", { "target": "build", "projects": "app" }]
|
||||
},
|
||||
"e2e-ci--**/**": {
|
||||
"dependsOn": ["^build", { "target": "build", "projects": "app" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This small improvement gives us the best of both worlds - using the Atomizer to automatically split long running tasks into smaller chunks and using manual splitting to skip entire work if dependencies haven't changed.
|
||||
|
||||
## Conclusion
|
||||
|
||||
By implementing these techniques — the built-in test sharding, Nx Atomizer, and manual E2E project splitting — you can significantly cut down CI time. That means fewer bottlenecks, less time waiting on pipelines, and more time spent delivering features, fixing bugs, and improving the product. When CI runs faster, teams can iterate quickly, merge with confidence, and ship value to users without the drag of slow test cycles.
|
||||
|
||||
Faster CI is just the beginning. When combined with Nx Cloud's distributed task execution, these strategies not only bring stability and improved performance but also offer better developer ergonomics and a comprehensive overview of your testing processes. This powerful combination allows your team to ship with greater confidence.
|
||||
|
||||
Give these techniques a try and see the difference for yourself.
|
||||
|
||||
{% call-to-action title="Ready to go further?" url="/contact/sales?utm_source=nx-blog&utm_medium=blog&utm_campaign=technical-blog&utm_id=040925" icon="nxcloud" description="Let's talk about how Nx Cloud can help you scale with speed." /%}
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: 'Are Monorepos the Answer to Better AI-Assisted Development?'
|
||||
description: 'In this webinar, Nx CTO Victor Savkin and Senior Engineer Max Kless will break down **why monorepos are inherently more AI-friendly**, giving LLMs the full context they need to generate more accurate, actionable insights. You’ll also learn how Nx provides deep architectural awareness, making AI tools smarter and more effective.'
|
||||
date: 2025-04-16
|
||||
slug: 'are-monorepos-the-answer-to-better-aiassisted-development'
|
||||
authors: ['Victor Savkin', 'Max Kless']
|
||||
tags: [webinar]
|
||||
cover_image: /blog/images/2025-04-30/April-2025-Webinar-Card.png
|
||||
time: 10:30am ET/2:30pm UTC
|
||||
status: Upcoming
|
||||
registrationUrl: https://go.nx.dev/april2025-webinar
|
||||
---
|
||||
|
||||
**Apr 30, 2025 - 10:30am ET/2:30pm UTC**
|
||||
|
||||
Presented by Victor Savkin and Max Kless
|
||||
|
||||
In this webinar, Nx CTO Victor Savkin and Senior Engineer Max Kless will break down **why monorepos are inherently more AI-friendly**, giving LLMs the full context they need to generate more accurate, actionable insights. You’ll also learn how Nx provides deep architectural awareness, making AI tools smarter and more effective.
|
||||
|
||||
{% call-to-action title="Register today!" url="https://go.nx.dev/april2025-webinar" description="Save your spot" /%}
|
||||
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 281 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 584 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 67 KiB |
@@ -1542,6 +1542,14 @@
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Getting Started",
|
||||
"path": "/recipes/angular/rspack/getting-started",
|
||||
"id": "getting-started",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Migrate from Angular Webpack",
|
||||
"path": "/recipes/angular/rspack/migrate-from-webpack",
|
||||
@@ -1557,6 +1565,14 @@
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Internationalization (i18n)",
|
||||
"path": "/recipes/angular/rspack/internationalization",
|
||||
"id": "internationalization",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"disableCollapsible": false
|
||||
@@ -3020,6 +3036,14 @@
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Getting Started",
|
||||
"path": "/recipes/angular/rspack/getting-started",
|
||||
"id": "getting-started",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Migrate from Angular Webpack",
|
||||
"path": "/recipes/angular/rspack/migrate-from-webpack",
|
||||
@@ -3035,6 +3059,14 @@
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Internationalization (i18n)",
|
||||
"path": "/recipes/angular/rspack/internationalization",
|
||||
"id": "internationalization",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"disableCollapsible": false
|
||||
@@ -3121,6 +3153,14 @@
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Getting Started",
|
||||
"path": "/recipes/angular/rspack/getting-started",
|
||||
"id": "getting-started",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Migrate from Angular Webpack",
|
||||
"path": "/recipes/angular/rspack/migrate-from-webpack",
|
||||
@@ -3136,6 +3176,14 @@
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Internationalization (i18n)",
|
||||
"path": "/recipes/angular/rspack/internationalization",
|
||||
"id": "internationalization",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"disableCollapsible": false
|
||||
@@ -3148,6 +3196,14 @@
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Getting Started",
|
||||
"path": "/recipes/angular/rspack/getting-started",
|
||||
"id": "getting-started",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Migrate from Angular Webpack",
|
||||
"path": "/recipes/angular/rspack/migrate-from-webpack",
|
||||
@@ -3164,6 +3220,14 @@
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Internationalization (i18n)",
|
||||
"path": "/recipes/angular/rspack/internationalization",
|
||||
"id": "internationalization",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Migration",
|
||||
"path": "/recipes/angular/migration",
|
||||
|
||||
@@ -444,6 +444,16 @@
|
||||
}
|
||||
},
|
||||
"migrations": {
|
||||
"/nx-api/angular/migrations/20.8.1-package-updates": {
|
||||
"description": "",
|
||||
"file": "generated/packages/angular/migrations/20.8.1-package-updates.json",
|
||||
"hidden": false,
|
||||
"name": "20.8.1-package-updates",
|
||||
"version": "20.8.1-beta.0",
|
||||
"originalFilePath": "/packages/angular",
|
||||
"path": "/nx-api/angular/migrations/20.8.1-package-updates",
|
||||
"type": "migration"
|
||||
},
|
||||
"/nx-api/angular/migrations/20.5.0-angular-eslint-package-updates": {
|
||||
"description": "",
|
||||
"file": "generated/packages/angular/migrations/20.5.0-angular-eslint-package-updates.json",
|
||||
|
||||
@@ -2111,6 +2111,17 @@
|
||||
"path": "/recipes/angular/rspack/introduction",
|
||||
"tags": ["introduction"]
|
||||
},
|
||||
{
|
||||
"id": "getting-started",
|
||||
"name": "Getting Started",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "shared/guides/angular-rspack/getting-started",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/getting-started",
|
||||
"tags": ["getting-started", "angular", "rspack", "setup"]
|
||||
},
|
||||
{
|
||||
"id": "migrate-from-webpack",
|
||||
"name": "Migrate from Angular Webpack",
|
||||
@@ -2132,6 +2143,17 @@
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/handling-configurations",
|
||||
"tags": ["handling-configurations", "configurations"]
|
||||
},
|
||||
{
|
||||
"id": "internationalization",
|
||||
"name": "Internationalization (i18n)",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "shared/guides/angular-rspack/internationalization",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/internationalization",
|
||||
"tags": ["internationalization", "i18n"]
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
@@ -4137,6 +4159,17 @@
|
||||
"path": "/recipes/angular/rspack/introduction",
|
||||
"tags": ["introduction"]
|
||||
},
|
||||
{
|
||||
"id": "getting-started",
|
||||
"name": "Getting Started",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "shared/guides/angular-rspack/getting-started",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/getting-started",
|
||||
"tags": ["getting-started", "angular", "rspack", "setup"]
|
||||
},
|
||||
{
|
||||
"id": "migrate-from-webpack",
|
||||
"name": "Migrate from Angular Webpack",
|
||||
@@ -4158,6 +4191,17 @@
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/handling-configurations",
|
||||
"tags": ["handling-configurations", "configurations"]
|
||||
},
|
||||
{
|
||||
"id": "internationalization",
|
||||
"name": "Internationalization (i18n)",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "shared/guides/angular-rspack/internationalization",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/internationalization",
|
||||
"tags": ["internationalization", "i18n"]
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
@@ -4276,6 +4320,17 @@
|
||||
"path": "/recipes/angular/rspack/introduction",
|
||||
"tags": ["introduction"]
|
||||
},
|
||||
{
|
||||
"id": "getting-started",
|
||||
"name": "Getting Started",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "shared/guides/angular-rspack/getting-started",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/getting-started",
|
||||
"tags": ["getting-started", "angular", "rspack", "setup"]
|
||||
},
|
||||
{
|
||||
"id": "migrate-from-webpack",
|
||||
"name": "Migrate from Angular Webpack",
|
||||
@@ -4297,6 +4352,17 @@
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/handling-configurations",
|
||||
"tags": ["handling-configurations", "configurations"]
|
||||
},
|
||||
{
|
||||
"id": "internationalization",
|
||||
"name": "Internationalization (i18n)",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "shared/guides/angular-rspack/internationalization",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/internationalization",
|
||||
"tags": ["internationalization", "i18n"]
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
@@ -4314,6 +4380,17 @@
|
||||
"path": "/recipes/angular/rspack/introduction",
|
||||
"tags": ["introduction"]
|
||||
},
|
||||
"/recipes/angular/rspack/getting-started": {
|
||||
"id": "getting-started",
|
||||
"name": "Getting Started",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "shared/guides/angular-rspack/getting-started",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/getting-started",
|
||||
"tags": ["getting-started", "angular", "rspack", "setup"]
|
||||
},
|
||||
"/recipes/angular/rspack/migrate-from-webpack": {
|
||||
"id": "migrate-from-webpack",
|
||||
"name": "Migrate from Angular Webpack",
|
||||
@@ -4336,6 +4413,17 @@
|
||||
"path": "/recipes/angular/rspack/handling-configurations",
|
||||
"tags": ["handling-configurations", "configurations"]
|
||||
},
|
||||
"/recipes/angular/rspack/internationalization": {
|
||||
"id": "internationalization",
|
||||
"name": "Internationalization (i18n)",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "shared/guides/angular-rspack/internationalization",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/recipes/angular/rspack/internationalization",
|
||||
"tags": ["internationalization", "i18n"]
|
||||
},
|
||||
"/recipes/angular/migration": {
|
||||
"id": "migration",
|
||||
"name": "Migration",
|
||||
|
||||
@@ -881,6 +881,13 @@
|
||||
"name": "Faster Builds with Module Federation",
|
||||
"path": "/concepts/module-federation/faster-builds-with-module-federation"
|
||||
},
|
||||
{
|
||||
"description": "",
|
||||
"file": "shared/guides/angular-rspack/getting-started",
|
||||
"id": "getting-started",
|
||||
"name": "Getting Started",
|
||||
"path": "/recipes/angular/rspack/getting-started"
|
||||
},
|
||||
{
|
||||
"description": "",
|
||||
"file": "shared/recipes/module-federation-with-ssr",
|
||||
@@ -1089,6 +1096,33 @@
|
||||
"path": "/recipes/angular/rspack/introduction"
|
||||
}
|
||||
],
|
||||
"getting-started": [
|
||||
{
|
||||
"description": "",
|
||||
"file": "shared/guides/angular-rspack/getting-started",
|
||||
"id": "getting-started",
|
||||
"name": "Getting Started",
|
||||
"path": "/recipes/angular/rspack/getting-started"
|
||||
}
|
||||
],
|
||||
"rspack": [
|
||||
{
|
||||
"description": "",
|
||||
"file": "shared/guides/angular-rspack/getting-started",
|
||||
"id": "getting-started",
|
||||
"name": "Getting Started",
|
||||
"path": "/recipes/angular/rspack/getting-started"
|
||||
}
|
||||
],
|
||||
"setup": [
|
||||
{
|
||||
"description": "",
|
||||
"file": "shared/guides/angular-rspack/getting-started",
|
||||
"id": "getting-started",
|
||||
"name": "Getting Started",
|
||||
"path": "/recipes/angular/rspack/getting-started"
|
||||
}
|
||||
],
|
||||
"migrate-from-webpack": [
|
||||
{
|
||||
"description": "",
|
||||
@@ -1116,6 +1150,24 @@
|
||||
"path": "/recipes/angular/rspack/handling-configurations"
|
||||
}
|
||||
],
|
||||
"internationalization": [
|
||||
{
|
||||
"description": "",
|
||||
"file": "shared/guides/angular-rspack/internationalization",
|
||||
"id": "internationalization",
|
||||
"name": "Internationalization (i18n)",
|
||||
"path": "/recipes/angular/rspack/internationalization"
|
||||
}
|
||||
],
|
||||
"i18n": [
|
||||
{
|
||||
"description": "",
|
||||
"file": "shared/guides/angular-rspack/internationalization",
|
||||
"id": "internationalization",
|
||||
"name": "Internationalization (i18n)",
|
||||
"path": "/recipes/angular/rspack/internationalization"
|
||||
}
|
||||
],
|
||||
"deployment": [
|
||||
{
|
||||
"description": "",
|
||||
|
||||
@@ -439,6 +439,16 @@
|
||||
}
|
||||
],
|
||||
"migrations": [
|
||||
{
|
||||
"description": "",
|
||||
"file": "generated/packages/angular/migrations/20.8.1-package-updates.json",
|
||||
"hidden": false,
|
||||
"name": "20.8.1-package-updates",
|
||||
"version": "20.8.1-beta.0",
|
||||
"originalFilePath": "/packages/angular",
|
||||
"path": "angular/migrations/20.8.1-package-updates",
|
||||
"type": "migration"
|
||||
},
|
||||
{
|
||||
"description": "",
|
||||
"file": "generated/packages/angular/migrations/20.5.0-angular-eslint-package-updates.json",
|
||||
|
||||
@@ -53,4 +53,5 @@ Below is a reference table that matches versions of [Angular Rspack](/recipes/an
|
||||
|
||||
| Angular Rspack | Angular | Nx |
|
||||
| -------------- | ----------- | ------------------- |
|
||||
| ~20.7.0 | **~19.2.0** | >= 20.8.1 <= latest |
|
||||
| ~20.6.0 | **~19.2.0** | >= 20.6.0 <= latest |
|
||||
|
||||
@@ -127,12 +127,6 @@
|
||||
"description": "Create an application with stricter type checking and build optimization options.",
|
||||
"default": true
|
||||
},
|
||||
"standaloneConfig": {
|
||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"x-deprecated": "Nx only supports standaloneConfig"
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"description": "The port at which the remote application should be served."
|
||||
|
||||
@@ -141,12 +141,6 @@
|
||||
"description": "Create an application with stricter type checking and build optimization options.",
|
||||
"default": true
|
||||
},
|
||||
"standaloneConfig": {
|
||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"x-deprecated": "Nx only supports standaloneConfig"
|
||||
},
|
||||
"setParserOptionsProject": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
|
||||
|
||||
@@ -108,12 +108,6 @@
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"standaloneConfig": {
|
||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"x-deprecated": "Nx only supports standaloneConfig"
|
||||
},
|
||||
"compilationMode": {
|
||||
"description": "Specifies the compilation mode to use. If not specified, it will default to `partial` for publishable libraries and to `full` for buildable libraries. The `full` value can not be used for publishable libraries.",
|
||||
"type": "string",
|
||||
|
||||
@@ -135,12 +135,6 @@
|
||||
"description": "Create an application with stricter type checking and build optimization options.",
|
||||
"default": true
|
||||
},
|
||||
"standaloneConfig": {
|
||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"x-deprecated": "Nx only supports standaloneConfig"
|
||||
},
|
||||
"setParserOptionsProject": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "20.8.1-package-updates",
|
||||
"version": "20.8.1-beta.0",
|
||||
"packages": {
|
||||
"@nx/angular-rspack": {
|
||||
"version": "^20.7.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
},
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"hidden": false,
|
||||
"implementation": "",
|
||||
"path": "/packages/angular",
|
||||
"schema": null,
|
||||
"type": "migration"
|
||||
}
|
||||
@@ -10,12 +10,12 @@
|
||||
"type": "object",
|
||||
"examples": [
|
||||
{
|
||||
"command": "nx g lib mylib --directory=myapp",
|
||||
"description": "Generate `libs/myapp/mylib`"
|
||||
"command": "nx g @nx/react:lib libs/mylib",
|
||||
"description": "Generate a library under `libs/mylib`"
|
||||
},
|
||||
{
|
||||
"command": "nx g lib mylib --appProject=myapp",
|
||||
"description": "Generate a library with routes and add them to `myapp`"
|
||||
"command": "nx g @nx/react:lib mylib --appProject=myapp",
|
||||
"description": "Generate a library under mylib with routes and add them to `myapp`"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
"factory": "./src/generators/convert-to-inferred/convert-to-inferred#convertToInferred",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"$id": "NxWebpackConvertToInferred",
|
||||
"description": "Convert existing Webpack project(s) using `@nx/webpack:wepack` executor to use `@nx/webpack/plugin`.",
|
||||
"title": "Convert a Webpack project from executor to plugin",
|
||||
"$id": "NxRspackConvertToInferred",
|
||||
"description": "Convert existing Rspack project(s) using `@nx/rspack:rspack` executor to use `@nx/rspack/plugin`.",
|
||||
"title": "Convert a Rspack project from executor to plugin",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The project to convert from using the `@nx/webpack:webpack` executor to use `@nx/webpack/plugin`. If not provided, all projects using the `@nx/webpack:webpack` executor will be converted.",
|
||||
"description": "The project to convert from using the `@nx/rspack:rspack` executor to use `@nx/rspack/plugin`. If not provided, all projects using the `@nx/rspack:rspack` executor will be converted.",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"skipFormat": {
|
||||
|
||||
@@ -13,11 +13,8 @@ and the version of NodeJS that we tested it against.
|
||||
|
||||
| Nx Version | Node Version | Typescript Version |
|
||||
| --------------- | ---------------- | ------------------ |
|
||||
| 20.x (latest) | 22.x, 20.x | ~5.4.2 |
|
||||
| 21.x (next) | 22.x, ^20.19.0, | ~5.4.2 |
|
||||
| 20.x (latest) | 22.x, 20.x, 18.x | ~5.4.2 |
|
||||
| 19.x (previous) | 22.x, 20.x, 18.x | ~5.4.2 |
|
||||
| 18.x | 20.x, 18.x | ~5.4.2 |
|
||||
| 17.x | 20.x, 18.x | ~5.1.0 |
|
||||
| 16.x | 20.x, 18.x, 16.x | ~5.1.0 |
|
||||
| 15.x | 18.x, 16.x, 14.x | ~5.0.0 |
|
||||
| 14.x | 16.x, 14.x, 12.x | ~4.7.2 |
|
||||
| 13.x | 14.x, 12.x, 10.x | ~4.6.2 |
|
||||
|
||||
@@ -720,6 +720,12 @@
|
||||
"tags": ["introduction"],
|
||||
"file": "shared/guides/angular-rspack/introduction"
|
||||
},
|
||||
{
|
||||
"name": "Getting Started",
|
||||
"id": "getting-started",
|
||||
"tags": ["getting-started", "angular", "rspack", "setup"],
|
||||
"file": "shared/guides/angular-rspack/getting-started"
|
||||
},
|
||||
{
|
||||
"name": "Migrate from Angular Webpack",
|
||||
"id": "migrate-from-webpack",
|
||||
@@ -731,6 +737,12 @@
|
||||
"id": "handling-configurations",
|
||||
"tags": ["handling-configurations", "configurations"],
|
||||
"file": "shared/guides/angular-rspack/handling-configurations"
|
||||
},
|
||||
{
|
||||
"name": "Internationalization (i18n)",
|
||||
"id": "internationalization",
|
||||
"tags": ["internationalization", "i18n"],
|
||||
"file": "shared/guides/angular-rspack/internationalization"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -165,7 +165,7 @@ configs. Builds can depend on the `.d.ts` files of the compiled libs instead of
|
||||
After Nx computes the hash for a task, it then checks if it ran this exact computation before. First, it checks locally,
|
||||
and then if it is missing, and if a remote cache is configured, it checks remotely.
|
||||
|
||||
If Nx finds the computation, Nx retrieves it and replay it. Nx places the right files in the right folders and prints
|
||||
If Nx finds the computation, Nx retrieves it and replays it. Nx places the right files in the right folders and prints
|
||||
the terminal output. So from the user's point of view, the command ran the same, just a lot faster.
|
||||
|
||||

|
||||
|
||||
@@ -48,7 +48,7 @@ The configuration for package manager workspaces varies based on which package m
|
||||
|
||||
```json {% fileName="package.json" %}
|
||||
{
|
||||
"workspaces": ["apps/**", "packages/**"]
|
||||
"workspaces": ["apps/*", "packages/*"]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -69,7 +69,7 @@ If you want to reference a local library project with its own `build` task, you
|
||||
|
||||
```json {% fileName="package.json" %}
|
||||
{
|
||||
"workspaces": ["apps/**", "packages/**"]
|
||||
"workspaces": ["apps/*", "packages/*"]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -90,7 +90,7 @@ If you want to reference a local library project with its own `build` task, you
|
||||
|
||||
```json {% fileName="package.json" %}
|
||||
{
|
||||
"workspaces": ["apps/**", "packages/**"]
|
||||
"workspaces": ["apps/*", "packages/*"]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -111,8 +111,8 @@ If you want to reference a local library project with its own `build` task, you
|
||||
|
||||
```yaml {% fileName="pnpm-workspace.yaml" %}
|
||||
packages:
|
||||
- 'apps/**'
|
||||
- 'packages/**'
|
||||
- 'apps/*'
|
||||
- 'packages/*'
|
||||
```
|
||||
|
||||
Defining the `packages` property in the root `pnpm-workspaces.yaml` file lets pnpm know to look for project `package.json` files in the specified folders. With this configuration in place, all the dependencies for the individual projects will be installed in the root `node_modules` folder when `pnpm install` is run in the root folder.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Automate Updating Dependencies
|
||||
description: Learn how to use Nx migrate to automatically update package dependencies, configuration files, and source code to match new package versions.
|
||||
keywords: [update]
|
||||
keywords: [update, migrate]
|
||||
---
|
||||
|
||||
# Automate Updating Dependencies
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
title: 'Getting Started - Angular Rspack'
|
||||
description: 'Learn how to get started with Angular Rspack applications in Nx.'
|
||||
---
|
||||
|
||||
# Getting Started
|
||||
|
||||
This guide will walk you through setting up a new Angular Rspack application in Nx. By the end of this guide, you will have a new Angular application with Rspack configured.
|
||||
|
||||
There are two paths you can follow to get started with Angular Rspack in Nx:
|
||||
|
||||
1. Create a new Nx Workspace with preconfigured Angular Rspack application
|
||||
2. Add an existing Angular Rspack application to an Nx Workspace
|
||||
|
||||
## Create a new Nx Workspace with preconfigured Angular Rspack application
|
||||
|
||||
To create a new Nx Workspace with a preconfigured Angular Rspack application, run the following command:
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Client-Side Rendering (CSR)" %}
|
||||
|
||||
```{% command="npx create-nx-workspace myorg" path="~/" %} {% highlightLines=[7,9] %}
|
||||
|
||||
NX Let's create a new workspace [[https://nx.dev/getting-started/intro](https://nx.dev/getting-started/intro)]
|
||||
|
||||
✔ Which stack do you want to use? · angular
|
||||
✔ Integrated monorepo, or standalone project? · integrated
|
||||
✔ Application name · myorg
|
||||
✔ Which bundler would you like to use? · rspack
|
||||
✔ Default stylesheet format · css
|
||||
✔ Do you want to enable Server-Side Rendering (SSR)? · No
|
||||
✔ Which unit test runner would you like to use? · vitest
|
||||
✔ Test runner to use for end to end (E2E) tests · playwright
|
||||
|
||||
NX Creating your v20.8.0 workspace.
|
||||
|
||||
```
|
||||
|
||||
{%/tab %}
|
||||
{% tab label="Server-Side Rendering (SSR)" %}
|
||||
|
||||
```{% command="npx create-nx-workspace myorg" path="~/" %} {% highlightLines=[7,9] %}
|
||||
|
||||
NX Let's create a new workspace [[https://nx.dev/getting-started/intro](https://nx.dev/getting-started/intro)]
|
||||
|
||||
✔ Which stack do you want to use? · angular
|
||||
✔ Integrated monorepo, or standalone project? · integrated
|
||||
✔ Application name · myorg
|
||||
✔ Which bundler would you like to use? · rspack
|
||||
✔ Default stylesheet format · css
|
||||
✔ Do you want to enable Server-Side Rendering (SSR)? · Yes
|
||||
✔ Which unit test runner would you like to use? · vitest
|
||||
✔ Test runner to use for end to end (E2E) tests · playwright
|
||||
|
||||
NX Creating your v20.8.0 workspace.
|
||||
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
This command will create a new Nx Workspace with an Angular Rspack application in the `myorg` directory.
|
||||
|
||||
## Add an existing Angular Rspack application to an Nx Workspace
|
||||
|
||||
To add an existing Angular Rspack application to an Nx Workspace, run the following command:
|
||||
|
||||
{% callout type="info" title="Minimum Nx Version" %}
|
||||
|
||||
The minimum Nx version required to add an Angular Rspack application to an Nx Workspace is `20.6.1`.
|
||||
If you are using an older version of Nx, run `npx nx migrate` to migrate your workspace to the latest version.
|
||||
|
||||
You can learn more about Nx migrations [here](/features/automate-updating-dependencies).
|
||||
|
||||
{% /callout %}
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Client-Side Rendering (CSR)" %}
|
||||
|
||||
```bash
|
||||
npx nx g @nx/angular:app myapp --bundler=rspack
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="Server-Side Rendering (SSR)" %}
|
||||
|
||||
```bash
|
||||
npx nx g @nx/angular:app myapp --bundler=rspack --ssr
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
This command will add an Angular Rspack application to the `myapp` directory.
|
||||
|
||||
## Working with the Angular Rspack application
|
||||
|
||||
After generating the application, you will notice the following:
|
||||
|
||||
- A `rspack.config.ts` file in the root of the project
|
||||
- The `project.json` file does not have a `targets.build` or `targets.serve` target
|
||||
|
||||
The `rspack.config.ts` file is a configuration file for Rspack. It contains the configuration options for Rspack and for Angular Rspack a helper [createConfig](/nx-api/angular-rspack/documents/create-config)
|
||||
function is used to map the options you would expect to set in the `project.json` or `angular.json` files to the Rspack configuration.
|
||||
|
||||
The `project.json` file does not contain the `targets.build` or `targets.serve` targets because Angular Rspack uses Nx's [Inferred Tasks](concepts/inferred-tasks) to build and serve your application with Rspack.
|
||||
|
||||
## Building your Angular Rspack application
|
||||
|
||||
To build your Angular Rspack application, run the following command:
|
||||
|
||||
```bash
|
||||
npx nx build myapp
|
||||
```
|
||||
|
||||
This command will build your Angular Rspack application and output the results to the `dist/browser` directory.
|
||||
If you are using the Angular Rspack application with Server-Side Rendering (SSR), the `dist/server` directory will contain the server files. The same `nx build` command will build both the client and server files.
|
||||
|
||||
## Serving your Angular Rspack application
|
||||
|
||||
To serve your Angular Rspack application, run the following command:
|
||||
|
||||
```bash
|
||||
npx nx serve myapp
|
||||
```
|
||||
|
||||
This command will serve your Angular Rspack application.
|
||||
For Client-Side Rendering (CSR) applications, the default port is `4200`. You can visit the application by navigating to `http://localhost:4200` in your browser.
|
||||
|
||||
For Server-Side Rendering (SSR) applications, the default port is `4000`. You can visit the application by navigating to `http://localhost:4000` in your browser.
|
||||
|
||||
HMR is enabled by default so you can make changes to your application and see the changes in real-time.
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: 'Build-time Internationalization (i18n) with Angular Rspack'
|
||||
description: "Guide on how to use Angular's build-time i18n with Angular Rspack"
|
||||
---
|
||||
|
||||
# Build-time Internationalization (i18n) with Angular Rspack
|
||||
|
||||
Angular Rspack supports Angular's [build-time i18n](https://angular.dev/guide/i18n) out of the box. This guide will walk you through how to use it.
|
||||
You can follow the steps completely, just make sure to place any changes to `angular.json` in your project's `project.json` file. Some of these changes may also need to be made to the `rspack.config` file.
|
||||
The steps below indicate where to make these changes.
|
||||
|
||||
The process of building an Angular Rspack application with i18n is similar to building an Angular application with i18n and reuses most of the same steps and configuration.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `@angular/localize` must be installed in your project.
|
||||
- You must have an `i18n` configuration in your `project.json` file.
|
||||
|
||||
It is assumed you have an `i18n` property in your `project.json` file that looks like this:
|
||||
|
||||
```json {% highlightLines=["3-10"] %}
|
||||
{
|
||||
"name": "my-app",
|
||||
"i18n": {
|
||||
"sourceLocale": "en-GB",
|
||||
"locales": {
|
||||
"fr": {
|
||||
"translation": "src/locale/messages.fr.xlf"
|
||||
}
|
||||
}
|
||||
},
|
||||
"targets": {
|
||||
"extract-i18n": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% callout type="note" title="Extracting i18n messages" %}
|
||||
The `extract-i18n` target found in Angular projects will still be used to extract the i18n messages into XLIFF (or chosen format) files.
|
||||
You simply need to run `nx extract-i18n my-app` to extract the messages.
|
||||
{% /callout %}
|
||||
|
||||
## Step 1: Configure the Rspack Configuration
|
||||
|
||||
To enable i18n, you need to add the following configuration to your `rspack.config` file:
|
||||
|
||||
```js {% highlightLines=[7,15] %}
|
||||
export default createConfig(
|
||||
{
|
||||
options: {
|
||||
root: __dirname,
|
||||
polyfills: [
|
||||
'zone.js',
|
||||
'@angular/localize/init',
|
||||
],
|
||||
...,
|
||||
},
|
||||
},
|
||||
{
|
||||
production: {
|
||||
options: {
|
||||
localize: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Step 2: Run the build
|
||||
|
||||
After configuring the Rspack configuration, you can run the build with the following command:
|
||||
|
||||
```bash
|
||||
npx nx build my-app
|
||||
```
|
||||
|
||||
It will output bundles in the `dist` directory with the following structure:
|
||||
|
||||
```text
|
||||
dist
|
||||
├── browser
|
||||
│ ├── [localeCode]
|
||||
│ │ ├── main.js
|
||||
│ │ ├── main.js.map
|
||||
│ │ ├── index.html
|
||||
│ │ ├── styles.css
|
||||
│ │ ├── ...
|
||||
```
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: 'Introduction - Angular Rspack and Rsbuild'
|
||||
title: 'Introduction - Angular Rspack'
|
||||
description: 'Learn how Rspack can help you speed up your Angular applications.'
|
||||
---
|
||||
|
||||
@@ -17,18 +17,16 @@ This succeeded in reducing the build times for Angular applications, however, it
|
||||
|
||||
---
|
||||
|
||||
## Rspack and Rsbuild
|
||||
## Rspack
|
||||
|
||||
The solution to this problem was to create a new build pipeline that would be able to leverage the existing Webpack ecosystem and plugins, while also providing faster builds for Angular applications.
|
||||
|
||||
This is where [Rspack](https://rspack.dev) and [Rsbuild](https://rsbuild.dev) come into play.
|
||||
This is where [Rspack](https://rspack.dev) come into play.
|
||||
|
||||
Rspack is a high performance JavaScript bundler written in Rust. It offers strong compatibility with the Webpack ecosystem, allowing for almost seamless replacement of webpack, and provides lightning fast build speeds.
|
||||
|
||||
Because it supports the existing Webpack ecosystem, it provides an answer to teams that maintain Angular applications using Webpack and want to migrate to a faster build pipeline.
|
||||
|
||||
Rsbuild is a build tool based on Rspack, however it does not support the Webpack ecosystem. It does provide out-of-the-box (OOTB) Module Federation support and a plugin system for extending the build with build speeds comparable to Esbuild.
|
||||
|
||||
This makes it a great solution for teams that want to migrate to a faster build pipeline, but still want the ability to easily extend their builds and use [Module Federation](https://module-federation.io).
|
||||
|
||||
{% callout type="warning" title="Angular Rspack Status" %}
|
||||
@@ -42,7 +40,6 @@ Please not that Angular Rspack support is still experimental and is not yet cons
|
||||
The following are known limitations and missing features of Angular Rspack:
|
||||
|
||||
- Static Site Generation (SSG) is not supported.
|
||||
- Angular's built-in support for Internationalization (i18n) is not supported.
|
||||
- Server Routing is not supported - still experimental in Angular currently.
|
||||
- App Engine APIs are not supported - still experimental in Angular currently.
|
||||
- Optimization is not currently 1:1 with Angular's optimization - however, there are still great optimizations that are made.
|
||||
@@ -69,7 +66,6 @@ Below is a table of benchmarks for different bundlers, tested on an application
|
||||
| ------------- | ------------ | -------- | ------- |
|
||||
| Webpack | 198.614 | 154.339 | 159.436 |
|
||||
| esbuild | 23.701 | 19.569 | 15.358 |
|
||||
| Rsbuild | 23.949 | 20.490 | 18.209 |
|
||||
| Rspack | 30.589 | 19.269 | 19.940 |
|
||||
|
||||
You can find the benchmarks and run them yourself: [https://github.com/nrwl/ng-bundler-benchmark](https://github.com/nrwl/ng-bundler-benchmark)
|
||||
|
||||
@@ -173,4 +173,4 @@ Now if we serve or app and navigate to `https://localhost:4200/contact` we will
|
||||
|
||||
You can find an example of an Nx Workspace using React Router by clicking below
|
||||
|
||||
{% github-repository url="<https://github.com/nrwl/nx-recipes/tree/main/react-router>" /%}
|
||||
{% github-repository url="https://github.com/nrwl/nx-recipes/tree/main/react-router" /%}
|
||||
|
||||
@@ -21,10 +21,7 @@ an existing NPM/Yarn or PNPM-based monorepo setup, you can easily add Nx to get
|
||||
This is a low-impact operation because all that needs to be done is to install the `nx` package at the root level and
|
||||
add an `nx.json` for configuring caching and task pipelines.
|
||||
|
||||
{% youtube
|
||||
src="https://www.youtube.com/embed/ngdoUQBvAjo"
|
||||
title="Add Nx to a PNPM workspaces monorepo"
|
||||
width="100%" /%}
|
||||
{% course-video src="https://youtu.be/3hW53b1IJ84" courseTitle="From PNPM Workspaces to Distributed CI" courseUrl="/courses/pnpm-nx-next/lessons-01-nx-init" title="Initialize Nx in Your Project with nx init" /%}
|
||||
|
||||
## Installing Nx
|
||||
|
||||
|
||||
@@ -53,4 +53,5 @@ Below is a reference table that matches versions of [Angular Rspack](/recipes/an
|
||||
|
||||
| Angular Rspack | Angular | Nx |
|
||||
| -------------- | ----------- | ------------------- |
|
||||
| ~20.7.0 | **~19.2.0** | >= 20.8.1 <= latest |
|
||||
| ~20.6.0 | **~19.2.0** | >= 20.6.0 <= latest |
|
||||
|
||||
@@ -13,11 +13,8 @@ and the version of NodeJS that we tested it against.
|
||||
|
||||
| Nx Version | Node Version | Typescript Version |
|
||||
| --------------- | ---------------- | ------------------ |
|
||||
| 20.x (latest) | 22.x, 20.x | ~5.4.2 |
|
||||
| 21.x (next) | 22.x, ^20.19.0, | ~5.4.2 |
|
||||
| 20.x (latest) | 22.x, 20.x, 18.x | ~5.4.2 |
|
||||
| 19.x (previous) | 22.x, 20.x, 18.x | ~5.4.2 |
|
||||
| 18.x | 20.x, 18.x | ~5.4.2 |
|
||||
| 17.x | 20.x, 18.x | ~5.1.0 |
|
||||
| 16.x | 20.x, 18.x, 16.x | ~5.1.0 |
|
||||
| 15.x | 18.x, 16.x, 14.x | ~5.0.0 |
|
||||
| 14.x | 16.x, 14.x, 12.x | ~4.7.2 |
|
||||
| 13.x | 14.x, 12.x, 10.x | ~4.6.2 |
|
||||
|
||||
@@ -76,12 +76,6 @@ This generates the following files:
|
||||
}
|
||||
```
|
||||
|
||||
```json {% fileName=".nx-dotnet.rc.json" %}
|
||||
{
|
||||
"nugetPackages": {}
|
||||
}
|
||||
```
|
||||
|
||||
```xml {% fileName="Directory.Build.props" %}
|
||||
<!--
|
||||
This file is imported early in the build order.
|
||||
@@ -120,6 +114,14 @@ This generates the following files:
|
||||
</Project>
|
||||
```
|
||||
|
||||
And on Nx versions earlier than 17:
|
||||
|
||||
```json {% fileName=".nx-dotnet.rc.json" %}
|
||||
{
|
||||
"nugetPackages": {}
|
||||
}
|
||||
```
|
||||
|
||||
## Create an Application
|
||||
|
||||
Use the `app` generator to create a new .NET app. For this demo, use the `nx` path naming convention and the `web-api` project template.
|
||||
|
||||
@@ -112,8 +112,10 @@
|
||||
- [Angular](/recipes/angular)
|
||||
- [Angular Rspack](/recipes/angular/rspack)
|
||||
- [Introduction](/recipes/angular/rspack/introduction)
|
||||
- [Getting Started](/recipes/angular/rspack/getting-started)
|
||||
- [Migrate from Angular Webpack](/recipes/angular/rspack/migrate-from-webpack)
|
||||
- [Handling Configurations](/recipes/angular/rspack/handling-configurations)
|
||||
- [Internationalization (i18n)](/recipes/angular/rspack/internationalization)
|
||||
- [Migration](/recipes/angular/migration)
|
||||
- [Migrating from Angular CLI](/recipes/angular/migration/angular)
|
||||
- [Migrating From Multiple Angular CLI Repos](/recipes/angular/migration/angular-multiple)
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('Gradle', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should track dependencies for new app', () => {
|
||||
xit('should track dependencies for new app', () => {
|
||||
if (type === 'groovy') {
|
||||
createFile(
|
||||
`app2/build.gradle`,
|
||||
|
||||
@@ -13,17 +13,13 @@ import {
|
||||
|
||||
expect.addSnapshotSerializer({
|
||||
serialize(str: string) {
|
||||
return (
|
||||
str
|
||||
// Not all package managers print the package.json path in the output
|
||||
.replace(tmpProjPath(), '')
|
||||
.replace('/private', '')
|
||||
.replace('/packages/package-1', '')
|
||||
// We trim each line to reduce the chances of snapshot flakiness
|
||||
.split('\n')
|
||||
.map((r) => r.trim())
|
||||
.join('\n')
|
||||
);
|
||||
return str
|
||||
.replace(/(\r\n|\n)+/g, '\n')
|
||||
.replace(/\n\s+/g, '\n')
|
||||
.replace(/\s+\n/g, '\n')
|
||||
.replace(tmpProjPath(), '')
|
||||
.replace('/private', '')
|
||||
.replace('/packages/package-1', '');
|
||||
},
|
||||
test(val: string) {
|
||||
return val != null && typeof val === 'string';
|
||||
@@ -48,12 +44,9 @@ describe('Lerna Smoke Tests', () => {
|
||||
.replace(/Running migration.*\n/g, '') // start of individual migration run
|
||||
.replace(/Ran .* from .*\n .*\n\n/g, '') // end of individual migration run
|
||||
.replace(/No changes were made\n\n/g, ''); // no changes during individual migration run
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
||||
Lerna No changes were necessary. This workspace is up to date!
|
||||
|
||||
|
||||
`);
|
||||
expect(result).toContain(
|
||||
'Lerna No changes were necessary. This workspace is up to date!'
|
||||
);
|
||||
}, 1000000);
|
||||
});
|
||||
|
||||
@@ -76,18 +69,12 @@ describe('Lerna Smoke Tests', () => {
|
||||
.replace('$ echo test-package-1', '> echo test-package-1');
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
||||
> package-1:print-name
|
||||
> package-1:print-name
|
||||
> echo test-package-1
|
||||
test-package-1
|
||||
Lerna (powered by Nx) Successfully ran target print-name for project package-1
|
||||
|
||||
> echo test-package-1
|
||||
test-package-1
|
||||
|
||||
|
||||
|
||||
Lerna (powered by Nx) Successfully ran target print-name for project package-1
|
||||
|
||||
|
||||
|
||||
`);
|
||||
`);
|
||||
}, 1000000);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*", "**/.next"]
|
||||
"ignorePatterns": ["!**/*", "**/.next", "public"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { DefaultLayout } from '@nx/nx-dev/ui-common';
|
||||
import {
|
||||
CallToAction,
|
||||
Features,
|
||||
GettingStarted,
|
||||
Hero,
|
||||
} from '@nx/nx-dev/ui-react';
|
||||
import { NxBenefitsVideo } from '@nx/nx-dev/ui-react';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Nx For React',
|
||||
description:
|
||||
'Add Nx to your React project for distributed task execution, intelligent caching, and affected commands.',
|
||||
alternates: {
|
||||
canonical: 'https://nx.dev/react',
|
||||
},
|
||||
openGraph: {
|
||||
url: 'https://nx.dev/react',
|
||||
title: 'Nx For React',
|
||||
description:
|
||||
'Add Nx to your React project for distributed task execution, intelligent caching, and affected commands.',
|
||||
images: [
|
||||
{
|
||||
url: 'https://nx.dev/socials/nx-react-media.png',
|
||||
width: 800,
|
||||
height: 421,
|
||||
alt: 'Nx For React',
|
||||
type: 'image/jpeg',
|
||||
},
|
||||
],
|
||||
siteName: 'Nx',
|
||||
type: 'website',
|
||||
},
|
||||
};
|
||||
|
||||
export default function ReactPage(): JSX.Element {
|
||||
return (
|
||||
<DefaultLayout>
|
||||
<Hero />
|
||||
<div className="mt-8 scroll-mt-8 lg:mt-16">
|
||||
<NxBenefitsVideo />
|
||||
</div>
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="mt-16 scroll-mt-16 lg:mt-32" id="features">
|
||||
<Features />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-16 scroll-mt-16 lg:mt-32">
|
||||
<GettingStarted />
|
||||
</div>
|
||||
<div className="overflow-hidden py-8 sm:py-8">
|
||||
<CallToAction />
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
);
|
||||
}
|
||||
@@ -109,7 +109,7 @@ export default function CustomApp({
|
||||
</Link>
|
||||
<Component {...pageProps} />
|
||||
{/* <LiveStreamNotifier /> */}
|
||||
{/*<WebinarNotifier />*/}
|
||||
<WebinarNotifier />
|
||||
|
||||
{/* Global Site Tag (gtag.js) - Google Analytics */}
|
||||
<Script
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { DefaultLayout } from '@nx/nx-dev/ui-common';
|
||||
import { Hero, PartnersList } from '@nx/nx-dev/ui-partners';
|
||||
import { NextSeo } from 'next-seo';
|
||||
import { useRouter } from 'next/router';
|
||||
import { contactButton } from '../lib/components/headerCtaConfigs';
|
||||
|
||||
export function Partners(): JSX.Element {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextSeo
|
||||
title="Our Partners"
|
||||
description="Partner"
|
||||
openGraph={{
|
||||
url: 'https://nx.dev' + router.asPath,
|
||||
title: 'Partners',
|
||||
description: 'Partner ',
|
||||
images: [
|
||||
{
|
||||
url: 'https://nx.dev/socials/nx-media.png',
|
||||
width: 800,
|
||||
height: 421,
|
||||
alt: 'Nx: Smart Monorepos · Fast CI',
|
||||
type: 'image/jpeg',
|
||||
},
|
||||
],
|
||||
siteName: 'Nx',
|
||||
type: 'website',
|
||||
}}
|
||||
/>
|
||||
<DefaultLayout headerCTAConfig={[contactButton]}>
|
||||
<div>
|
||||
<Hero />
|
||||
</div>
|
||||
<div className="mt-16">
|
||||
<PartnersList />
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Partners;
|
||||
@@ -31,7 +31,7 @@ interface BrowseProps {
|
||||
|
||||
export async function getStaticProps(): Promise<{ props: BrowseProps }> {
|
||||
const res = await fetch(
|
||||
'https://raw.githubusercontent.com/nrwl/nx/master/community/approved-plugins.json'
|
||||
'https://raw.githubusercontent.com/nrwl/nx/refs/heads/20.8.x/community/approved-plugins.json'
|
||||
);
|
||||
const pluginList = await res.json();
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 625 KiB |
@@ -656,7 +656,6 @@ const packagesIndexes = {
|
||||
'/nest': '/packages/nest',
|
||||
'/next': '/packages/next',
|
||||
'/node': '/packages/node',
|
||||
'/react': '/packages/react',
|
||||
'/react-native': '/packages/react',
|
||||
'/rollup': '/packages/rollup',
|
||||
'/storybook': '/packages/storybook',
|
||||
@@ -879,7 +878,7 @@ const missingAndCatchAllRedirects = {
|
||||
// catch all
|
||||
'/(l|latest|p|previous)/(a|angular|r|react|n|node)/:path*': '/:path*',
|
||||
'/(l|latest|p|previous)/:path*': '/:path*',
|
||||
'/(a|angular|r|react|n|node)/:path*': '/:path*',
|
||||
'/(a|angular|n|node)/:path*': '/:path*',
|
||||
// Storybook
|
||||
'/(l|latest)/(r|react)/storybook/overview': '/storybook/overview-react',
|
||||
'/(l|latest)/(a|angular)/storybook/overview': '/storybook/overview-angular',
|
||||
|
||||
@@ -52,21 +52,24 @@ pre,
|
||||
iframe[src*='youtube'] {
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
.prose iframe,
|
||||
.prose img {
|
||||
|
||||
.prose iframe:not(.not-prose):not(.not-prose *),
|
||||
.prose img:not(.not-prose):not(.not-prose *) {
|
||||
display: block;
|
||||
margin: 2rem auto;
|
||||
}
|
||||
.prose iframe {
|
||||
|
||||
.prose iframe:not(.not-prose):not(.not-prose *) {
|
||||
width: 100%;
|
||||
max-width: 560px;
|
||||
}
|
||||
.prose img {
|
||||
|
||||
.prose img:not(.not-prose):not(.not-prose *) {
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.prose iframe[src^="https://staging.nx.app"],
|
||||
.prose iframe[src^="https://nx.app"]
|
||||
.prose iframe:not(.not-prose):not(.not-prose *)[src^="https://staging.nx.app"],
|
||||
.prose iframe:not(.not-prose):not(.not-prose *)[src^="https://nx.app"]
|
||||
{
|
||||
height: 80vh;
|
||||
max-width: 100%;
|
||||
|
||||
@@ -100,7 +100,7 @@ export function BlogDetails({ post, allPosts }: BlogDetailsProps) {
|
||||
</div>
|
||||
|
||||
{/* Main grid layout */}
|
||||
<div className="mx-auto max-w-7xl px-4 lg:px-8">
|
||||
<div className="mx-auto max-w-7xl px-4 lg:px-8" data-content-area>
|
||||
<div className="relative isolate grid grid-cols-1 gap-8 xl:grid-cols-[200px_minmax(0,1fr)_200px]">
|
||||
<div className="hidden min-h-full xl:block">
|
||||
{post.metrics && (
|
||||
|
||||
@@ -17,6 +17,7 @@ export * from './lib/tweet';
|
||||
export * from './lib/typography';
|
||||
export * from './lib/github-star-widget';
|
||||
export * from './lib/youtube.component';
|
||||
export * from './lib/course-video';
|
||||
export * from './lib/x-icon';
|
||||
export * from './lib/discord-icon';
|
||||
export * from './lib/trusted-by';
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { ButtonLink } from './button';
|
||||
import { YouTube } from './youtube.component';
|
||||
|
||||
export interface CourseVideoProps {
|
||||
/**
|
||||
* The YouTube video URL
|
||||
*/
|
||||
src: string;
|
||||
courseTitle: string;
|
||||
courseUrl: string;
|
||||
}
|
||||
|
||||
export function CourseVideo({
|
||||
src: videoUrl,
|
||||
courseTitle,
|
||||
courseUrl,
|
||||
}: CourseVideoProps): JSX.Element {
|
||||
return (
|
||||
<div className="not-prose mx-auto max-w-4xl">
|
||||
<div className="overflow-hidden rounded-xl bg-slate-50 shadow-sm ring-1 ring-slate-900/5 dark:bg-slate-800/50 dark:ring-slate-700/50">
|
||||
<div className="aspect-video w-full">
|
||||
<YouTube
|
||||
src={videoUrl}
|
||||
title={courseTitle}
|
||||
width="100%"
|
||||
disableRoundedCorners={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="px-6 py-4">
|
||||
<div className="flex flex-col items-center gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<h3 className="text-center text-xl font-medium text-slate-900 sm:text-left dark:text-white">
|
||||
{courseTitle}
|
||||
</h3>
|
||||
<ButtonLink
|
||||
href={courseUrl}
|
||||
variant="primary"
|
||||
size="small"
|
||||
title={`Watch the full "${courseTitle}" course`}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
Watch full course
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
MicrophoneIcon,
|
||||
VideoCameraIcon,
|
||||
CheckBadgeIcon,
|
||||
LifebuoyIcon,
|
||||
BookOpenIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { FC, SVGProps } from 'react';
|
||||
@@ -277,6 +278,14 @@ export const companyItems: MenuItem[] = [
|
||||
isNew: false,
|
||||
isHighlight: false,
|
||||
},
|
||||
{
|
||||
name: 'Partners',
|
||||
description: null,
|
||||
icon: LifebuoyIcon,
|
||||
href: '/partners',
|
||||
isNew: false,
|
||||
isHighlight: false,
|
||||
},
|
||||
];
|
||||
|
||||
export const resourceMenuItems = {
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
export function WebinarNotifier(): ReactElement | null {
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const [isVisible, setIsVisible] = useState<boolean>(true);
|
||||
const localStorageKey = 'webinar-march-19-2025--notifier-closed';
|
||||
const localStorageKey = 'webinar-april-30-2025--notifier-closed';
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true);
|
||||
@@ -61,19 +61,19 @@ export function WebinarNotifier(): ReactElement | null {
|
||||
aria-hidden="true"
|
||||
className="size-8 flex-shrink-0"
|
||||
/>
|
||||
<span>Join our webinar + live Q&A on March 19th</span>
|
||||
<span>Join our webinar + live Q&A on April 30th</span>
|
||||
</motion.h3>
|
||||
<motion.div key="live-event" className="mt-4 space-y-4">
|
||||
<p className="mb-2 text-sm">
|
||||
Join Victor Savkin and Max Kless discussing why LLMs struggle with
|
||||
Join Victor Savkin and Max Kless discuss why LLMs struggle with
|
||||
fragmented codebases, how monorepos + Nx create AI-powered
|
||||
architectural decisions, the before-and-after experience of
|
||||
architectural decisions, and the before-and-after experience of
|
||||
AI-powered coding in Nx.
|
||||
</p>
|
||||
<div className="flex flex-wrap items-center justify-end gap-1 sm:gap-4">
|
||||
<a
|
||||
title="Signup"
|
||||
href="https://bit.ly/3XvOwIl"
|
||||
href="https://bit.ly/42dwGwm"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-pink-600 px-2 py-2 text-sm font-semibold text-white transition hover:bg-pink-700 focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 active:text-black/70 md:px-4"
|
||||
|
||||
@@ -107,6 +107,11 @@ const teamMembers = [
|
||||
title: 'Developer Productivity Engineer',
|
||||
imageUrl: 'jordan-powell.avif',
|
||||
},
|
||||
{
|
||||
name: 'Josh VanAllen',
|
||||
title: 'Developer Productivity Engineer',
|
||||
imageUrl: 'josh-vanallen.avif',
|
||||
},
|
||||
{
|
||||
name: 'Juri Strumpflohner',
|
||||
title: 'Director of Developer Experience',
|
||||
|
||||
@@ -25,24 +25,24 @@ export function Hero(): ReactElement {
|
||||
<div className="absolute inset-0">
|
||||
<div className="mx-auto max-w-7xl lg:flex">
|
||||
<div className="mx-auto max-w-3xl px-6 pb-24 pt-36 lg:mx-0 lg:shrink-0 lg:px-8">
|
||||
{/*<p>*/}
|
||||
{/* <a*/}
|
||||
{/* href="https://bit.ly/3XvOwIl"*/}
|
||||
{/* title="See live event in details"*/}
|
||||
{/* className="group/event-link inline-flex space-x-6"*/}
|
||||
{/* >*/}
|
||||
{/* <span className="rounded-full bg-blue-600/10 px-3 py-1 text-sm/6 font-semibold text-blue-600 ring-1 ring-inset ring-blue-600/10 dark:bg-cyan-600/10 dark:text-cyan-600 dark:ring-cyan-600/10">*/}
|
||||
{/* Live event*/}
|
||||
{/* </span>*/}
|
||||
{/* <span className="inline-flex items-center space-x-2 text-sm/6 font-medium">*/}
|
||||
{/* <span>Webinar + live Q&A on March 19th</span>*/}
|
||||
{/* <ChevronRightIcon*/}
|
||||
{/* aria-hidden="true"*/}
|
||||
{/* className="size-5 transform transition-all group-hover/event-link:translate-x-1"*/}
|
||||
{/* />*/}
|
||||
{/* </span>*/}
|
||||
{/* </a>*/}
|
||||
{/*</p>*/}
|
||||
<p>
|
||||
<a
|
||||
href="https://bit.ly/42dwGwm"
|
||||
title="See live event in details"
|
||||
className="group/event-link inline-flex space-x-6"
|
||||
>
|
||||
<span className="rounded-full bg-blue-600/10 px-3 py-1 text-sm/6 font-semibold text-blue-600 ring-1 ring-inset ring-blue-600/10 dark:bg-cyan-600/10 dark:text-cyan-600 dark:ring-cyan-600/10">
|
||||
Live event
|
||||
</span>
|
||||
<span className="inline-flex items-center space-x-2 text-sm/6 font-medium">
|
||||
<span>Webinar + live Q&A on March 19th</span>
|
||||
<ChevronRightIcon
|
||||
aria-hidden="true"
|
||||
className="size-5 transform transition-all group-hover/event-link:translate-x-1"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
<SectionHeading
|
||||
id="get-speed-and-scale"
|
||||
as="h1"
|
||||
|
||||
@@ -61,7 +61,7 @@ export function MakeYourCiFast(): ReactElement {
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative lg:col-span-2">
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 max-lg:rounded-t-[calc(2rem+1px)] lg:rounded-tr-[calc(2rem+1px)] dark:bg-slate-950 dark:ring-white/10">
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 lg:rounded-tr-[calc(2rem+1px)] dark:bg-slate-950 dark:ring-white/10">
|
||||
<img
|
||||
alt="Nx Atomizer: split large tasks & E2E in chunks"
|
||||
src="/images/enterprise/nx-atomizer.avif"
|
||||
@@ -86,7 +86,7 @@ export function MakeYourCiFast(): ReactElement {
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative lg:col-span-3">
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-bl-[calc(2rem+1px)] dark:bg-slate-950 dark:ring-white/10">
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 lg:rounded-bl-[calc(2rem+1px)] dark:bg-slate-950 dark:ring-white/10">
|
||||
<img
|
||||
alt="Nx Agents: simple & fast task distribution"
|
||||
src="/images/enterprise/nx-agents.avif"
|
||||
@@ -111,7 +111,7 @@ export function MakeYourCiFast(): ReactElement {
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative lg:col-span-3">
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-br-[calc(2rem+1px)] dark:bg-slate-950 dark:ring-white/10">
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 lg:rounded-br-[calc(2rem+1px)] dark:bg-slate-950 dark:ring-white/10">
|
||||
<img
|
||||
alt="Partner with the Nx Team for guidances"
|
||||
src="/images/enterprise/nx-partnership.avif"
|
||||
|
||||
@@ -89,6 +89,13 @@ export * from './lib/others/linux';
|
||||
export * from './lib/others/min-io';
|
||||
export * from './lib/others/windows';
|
||||
|
||||
// PARTNERS
|
||||
export * from './lib/partners/bitovi';
|
||||
export * from './lib/partners/callstack';
|
||||
export * from './lib/partners/hero-devs';
|
||||
export * from './lib/partners/push-based';
|
||||
export * from './lib/partners/e-square';
|
||||
|
||||
// PODCASTS
|
||||
export * from './lib/podcasts/amazon-music';
|
||||
export * from './lib/podcasts/apple-podcasts';
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { FC, SVGProps } from 'react';
|
||||
|
||||
export const BitoviIcon: FC<SVGProps<SVGSVGElement>> = (props) => {
|
||||
return (
|
||||
<svg
|
||||
width="1272"
|
||||
height="397"
|
||||
viewBox="0 0 1272 397"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<title>Bitovi</title>
|
||||
<path
|
||||
d="M87.406 385.527C86.4424 391.273 83.3039 393.357 77.9091 393.32C55.4139 393.167 32.917 393.258 10.4208 393.255C1.76093 393.254 0.0181025 391.5 0.0113125 382.625C-0.00603751 359.962 0.00523942 337.299 0.00511742 314.636C0.00458342 219.152 0.0040498 123.668 0.0036068 28.1845C0.0035918 27.1846 -0.00637256 26.1846 0.00688744 25.185C0.104543 17.8226 2.14118 15.6543 9.58571 15.6058C23.916 15.5125 38.2474 15.5778 52.5783 15.5778C60.9103 15.5778 69.2431 15.503 77.5739 15.6024C84.8338 15.689 87.4207 18.3613 87.4254 25.6629C87.4481 60.4904 87.3857 95.3182 87.54 130.145C87.5511 132.657 89.0509 135.162 89.8585 137.67C92.2244 136.871 94.933 136.567 96.9044 135.2C124.939 115.761 155.716 108.891 188.937 116.633C220.768 124.051 244.974 143.068 262.512 170.264C297.044 223.813 295.456 296.485 257.844 347.394C231.279 383.351 194.665 398.544 150.157 394.369C130.507 392.525 112.933 385.042 96.6719 374.069C94.7105 372.745 91.6686 371.696 89.6425 372.286C88.3999 372.648 87.8856 376.334 87.5093 378.604C87.1599 380.713 87.4276 382.924 87.406 385.527ZM108.285 304.752C132.769 325.36 171.824 319.824 188.727 292.726C202.635 270.429 202.851 246.625 192.671 222.932C176.172 184.533 124.995 182.944 102.668 209.253C89.4364 224.844 85.9239 243.273 87.9011 263.033C89.5104 279.116 95.3948 293.292 108.285 304.752Z"
|
||||
fill="#F5532D"
|
||||
/>
|
||||
<path
|
||||
d="M629.699 167.355C651.156 140.72 678.108 122.826 711.354 116.664C766.462 106.45 813.511 122.391 849.112 166.069C896.609 224.343 890.251 306.873 835.654 358.532C785.171 406.298 705.295 408.76 651.119 364.218C591.77 315.424 582.209 228.599 629.699 167.355ZM684.417 269.495C684.74 272.969 684.889 276.469 685.415 279.912C689.248 305.009 708.36 323.515 732.832 325.922C759.021 328.498 781.407 314.182 789.903 289.566C794.931 275 793.706 260.397 790.443 245.778C786.588 228.507 779.974 212.47 767.949 199.19C751.287 180.788 728.065 180.062 711.09 198.002C692.5 217.649 684.947 241.857 684.417 269.495Z"
|
||||
fill="#F5532D"
|
||||
/>
|
||||
<path
|
||||
d="M984.388 190.178C993.296 217.557 1002.07 244.563 1011.03 271.511C1011.71 273.574 1013.65 275.221 1015.01 277.062C1016.24 275.289 1017.97 273.683 1018.62 271.719C1034.3 224.088 1049.86 176.419 1065.44 128.755C1068.04 120.815 1071.36 118.279 1079.63 118.266C1103.62 118.227 1127.62 118.229 1151.61 118.264C1160.38 118.277 1163.64 122.961 1160.59 131.297C1134.15 203.653 1107.81 276.049 1081.12 348.318C1069.65 379.391 1041.84 397.67 1009.11 394.877C979.463 392.346 958.979 375.986 948.681 348.266C921.797 275.897 895.469 203.32 868.967 130.809C866.145 123.09 869.459 118.297 877.573 118.275C901.898 118.211 926.224 118.216 950.549 118.273C958.344 118.291 961.815 121.006 964.337 128.707C971.005 149.064 977.648 169.429 984.388 190.178Z"
|
||||
fill="#F5532D"
|
||||
/>
|
||||
<path
|
||||
d="M594.444 383.135C571.793 393.691 548.244 396.456 523.964 394.697C510.305 393.707 496.938 391.4 484.111 386.317C455.248 374.881 438.984 353.259 433.578 323.244C431.935 314.126 431.324 304.712 431.301 295.432C431.121 223.117 431.198 150.802 431.203 78.4877C431.203 70.9 432.495 69.1687 439.772 67.3766C462.365 61.8128 484.969 56.2969 507.571 50.7726C514.592 49.0568 518.581 52.0108 518.619 59.25C518.71 76.4118 518.644 93.5744 518.654 110.737C518.658 117.696 519.492 118.56 526.322 118.571C538.819 118.592 551.316 118.536 563.812 118.593C571.731 118.629 574.323 121.227 574.334 129.045C574.361 147.707 574.356 166.369 574.337 185.031C574.329 193.01 571.79 195.587 563.986 195.596C551.323 195.612 538.659 195.586 525.996 195.607C519.475 195.617 518.662 196.387 518.659 202.704C518.642 232.029 518.597 261.355 518.674 290.681C518.717 307.243 527.804 316.506 544.447 317.284C555.081 317.781 565.28 315.657 575.269 312.237C580.117 310.577 582.797 311.565 584.672 316.474C591.259 333.722 600.5 349.372 612.146 363.679C615.559 367.872 614.951 371.283 610.385 374.159C605.335 377.341 600.009 380.088 594.444 383.135Z"
|
||||
fill="#F5532D"
|
||||
/>
|
||||
<path
|
||||
d="M1240.51 392.339C1209.22 402.427 1179.59 382.213 1178.2 349.809C1177.57 335.17 1177.99 320.485 1177.98 305.821C1177.97 258.157 1177.97 210.494 1177.98 162.831C1177.99 141.594 1187.26 126.032 1206.77 117.269C1233 105.484 1265.23 122.66 1270.39 150.974C1271.04 154.556 1271.39 158.247 1271.39 161.888C1271.45 223.717 1271.46 285.546 1271.42 347.375C1271.41 368.106 1259.88 384.922 1240.51 392.339Z"
|
||||
fill="#F5532D"
|
||||
/>
|
||||
<path
|
||||
d="M397.323 138.353C401.302 146.1 402.835 153.995 402.83 162.3C402.794 223.8 402.918 285.299 402.765 346.799C402.694 375.263 381.719 395.951 354.298 395.182C330.287 394.509 309.82 374.29 309.537 350.179C309.186 320.183 309.415 290.181 309.408 260.181C309.399 227.015 309.195 193.847 309.488 160.683C309.656 141.569 318.494 127.22 335.611 118.48C356.135 108.001 382.016 115.015 394.781 134.213C395.609 135.458 396.373 136.746 397.323 138.353Z"
|
||||
fill="#F5532D"
|
||||
/>
|
||||
<path
|
||||
d="M1241.17 90.9424C1214.48 99.9006 1187.73 86.4943 1179.94 60.5506C1172.38 35.4027 1187.39 8.28435 1212.49 1.75332C1236.19 -4.41518 1260.22 8.04123 1268.56 30.8252C1276.92 53.668 1266.74 78.6398 1244.7 89.3489C1243.65 89.8574 1242.59 90.3381 1241.17 90.9424Z"
|
||||
fill="#F5532D"
|
||||
/>
|
||||
<path
|
||||
d="M329.154 85.4313C313.045 72.7243 306.367 56.4871 310.684 36.7707C314.677 18.531 326.373 6.43183 344.453 1.75066C363.647 -3.21917 380.264 2.46454 392.554 17.8817C404.709 33.1301 406.477 50.4785 397.772 67.9645C388.851 85.8838 373.579 94.3756 353.633 93.9368C344.823 93.743 336.778 90.5303 329.154 85.4313Z"
|
||||
fill="#F5532D"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { FC, SVGProps } from 'react';
|
||||
|
||||
export const CallstackIcon: FC<SVGProps<SVGSVGElement>> = (props) => {
|
||||
return (
|
||||
<svg
|
||||
width="599"
|
||||
height="321"
|
||||
viewBox="0 0 599 321"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<title>Callstack</title>
|
||||
<path
|
||||
d="M436.051 218.435C444.412 229.58 452.397 240.242 461.7 252.663C450.289 252.663 440.345 251.429 430.826 252.915C409.73 256.21 396.266 248.499 385.38 230.012C373.479 209.8 358.226 191.562 342.632 172.586C342.632 198.878 342.632 225.169 342.632 251.85C325.009 251.85 308.937 251.85 292.372 251.85C292.372 177.634 292.372 103.854 292.372 29.3044C308.665 29.3044 324.949 29.3044 342.375 29.3044C342.375 71.813 342.375 114.496 342.375 157.178C343.047 157.429 343.719 157.679 344.391 157.929C354.275 146.109 364.158 134.29 374.041 122.469C376.59 119.419 379.705 116.679 381.586 113.261C392.185 93.9926 408.342 87.7777 429.561 91.0097C437.776 92.2609 446.34 91.2139 456.796 91.2139C442.239 108.149 429.023 123.542 415.786 138.916C410.043 145.586 404.413 152.363 398.428 158.809C395.285 162.194 395.428 164.683 398.212 168.301C410.856 184.73 423.214 201.381 436.051 218.435Z"
|
||||
fill="#8232FF"
|
||||
/>
|
||||
<path
|
||||
d="M32.5149 275.682C32.2422 252.21 32.0572 229.486 31.8222 206.762C31.7811 202.79 31.7803 198.792 31.3275 194.855C30.119 184.35 25.4529 179.707 14.9436 178.758C10.4752 178.354 5.93991 178.69 0.883057 178.69C0.883057 166.445 0.883057 154.974 0.883057 143C6.36124 142.651 11.6962 142.603 16.9461 141.911C25.7175 140.754 30.9235 135.604 31.2286 126.663C31.6935 113.043 31.6424 99.4049 31.8432 85.7751C32.044 72.1446 31.6844 58.4723 32.6429 44.894C34.2622 21.9541 49.6399 4.94079 72.5986 1.6259C83.6947 0.0237978 95.2055 1.01754 106.508 1.41226C108.271 1.47383 111.313 4.55301 111.401 6.35272C111.885 16.208 111.632 26.0994 111.632 36.3083C106.521 36.0185 102.348 35.4883 98.1922 35.6004C85.0001 35.9565 78.8475 41.8218 78.6099 55.0519C78.2784 73.5163 78.5159 91.9907 78.4722 110.461C78.4621 114.721 78.4216 118.988 78.185 123.24C77.3077 139.008 73.7735 153.308 54.9731 160.218C74.7588 167.515 77.7758 182.914 78.1854 199.269C78.6761 218.864 78.441 238.478 78.4785 258.083C78.5224 281.036 83.4792 285.751 106.194 284.372C107.568 284.288 108.952 284.361 111.606 284.361C111.606 294.706 111.822 304.817 111.379 314.899C111.311 316.448 108.305 319.015 106.5 319.162C91.503 320.379 76.3836 321.722 61.9143 315.954C44.0755 308.843 34.8875 295.152 32.5149 275.682Z"
|
||||
fill="#8232FF"
|
||||
/>
|
||||
<path
|
||||
d="M598.512 161.382C598.511 167.271 598.511 172.329 598.511 178.477C594.724 178.477 591.708 178.421 588.694 178.486C572.726 178.831 567.147 184.038 566.79 200.083C566.424 216.549 566.894 233.033 566.612 249.503C566.419 260.818 566.915 272.39 564.731 283.375C560.223 306.051 541.921 319.931 518.945 319.979C510.708 319.996 502.464 319.772 494.236 320.053C488.938 320.235 486.413 318.716 486.658 312.882C487.039 303.845 486.759 294.781 486.759 284.943C491.259 285.238 495.073 285.658 498.892 285.709C513.333 285.902 520.018 279.878 520.285 265.371C520.666 244.643 520.293 223.902 520.558 203.171C520.641 196.678 521.221 190.094 522.494 183.737C524.715 172.65 531.596 165.058 543.664 160.608C523.673 152.918 520.731 136.991 520.526 119.855C520.294 100.543 520.508 81.2262 520.461 61.9116C520.407 39.7837 514.051 34.112 492 36.3261C490.622 36.4645 489.218 36.3441 486.819 36.3441C486.819 26.0766 486.608 15.931 487.037 5.81245C487.104 4.21941 490.058 1.43992 491.691 1.43012C504.103 1.35562 516.851 0.137348 528.86 2.49996C552.46 7.14293 565.926 24.386 566.474 48.4936C566.97 70.3533 566.633 92.2317 566.694 114.102C566.704 117.507 566.652 120.936 567.011 124.313C568.364 137.03 573.457 141.836 586.157 142.455C589.82 142.633 593.499 142.482 598.512 142.482C598.512 148.879 598.512 154.715 598.512 161.382Z"
|
||||
fill="#8232FF"
|
||||
/>
|
||||
<path
|
||||
d="M257.762 94.8268C266.052 96.7624 268.476 101.34 267.938 109.214C267.166 120.516 267.741 131.91 267.741 144.559C264.816 143.166 262.656 142.228 260.579 141.132C247.549 134.261 233.803 130.325 218.952 130.559C196.499 130.912 178.099 148.782 177.551 170.895C176.968 194.464 194.137 213.44 217.19 214.64C233.51 215.489 248.715 211.657 262.877 203.56C264.765 202.48 266.668 201.426 270.16 199.464C270.16 214.324 270.262 227.785 270.013 241.239C269.986 242.708 268.248 244.773 266.779 245.496C227.852 264.643 169.411 260.915 140.583 219.755C106.843 171.582 131.825 107.256 190.149 91.6546C212.74 85.6117 235.111 86.5895 257.762 94.8268Z"
|
||||
fill="#8232FF"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
import { FC, SVGProps } from 'react';
|
||||
|
||||
export const ESquareIcon: FC<SVGProps<SVGSVGElement>> = (props) => {
|
||||
return (
|
||||
<svg
|
||||
width="1348"
|
||||
height="117"
|
||||
viewBox="0 0 1348 117"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<title>E-Square</title>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M0.0397949 2.2313H73.2583V21.3643H21.0526V47.9899H68.7987V67.123H21.0574V94.7069H74.2053V113.84H0.0446265V2.22646L0.0397949 2.2313ZM132.161 82.2629V64.0882H179.269V82.2629H132.161ZM280.023 0C303.901 0 321.247 13.0684 321.247 35.3959V40.1779H300.876V36.8286C300.876 24.0748 292.759 18.4941 280.023 18.4941C268.886 18.4941 262.04 24.0748 262.04 31.7271C262.04 42.2495 271.906 44.1662 285.28 47.1913L286.71 47.5107C309.627 52.6122 323.155 60.7437 323.155 80.9948C323.155 103.477 305.65 116.071 281.936 116.071C254.72 116.071 237.21 101.406 237.21 75.2544V71.7453H257.904V74.6155C257.904 88.3276 265.702 97.5771 282.569 97.5771C296.098 97.5771 302.466 90.3992 302.466 82.108C302.466 72.3842 294.189 69.1945 279.55 66.0049L278.115 65.6854C255.039 60.5839 241.346 52.6171 241.346 32.366C241.346 12.4343 258.377 0 280.023 0ZM590.204 2.22646H611.212V75.2544C611.212 88.8068 619.329 97.2576 633.815 97.2576C648.3 97.2576 656.417 88.8068 656.417 75.2544V2.2313H677.425V74.3009C677.425 99.8084 661.669 116.071 633.815 116.071C605.96 116.071 590.204 99.8084 590.204 74.3009V2.22646ZM756.06 2.22646H789.799L820.678 113.84H799.032L792.345 88.9616H753.513L746.826 113.84H725.181L756.06 2.22646ZM758.446 69.8334H787.412L773.884 19.7671H771.975L758.446 69.8334ZM955.335 37.4675C955.335 54.3692 945.788 66.4841 931.467 70.7918L954.065 113.84H930.825L910.295 72.5439H891.674V113.84H870.666V2.39103H919.688C940.696 2.39103 955.335 16.2628 955.335 37.4675ZM891.674 21.5192V53.406H917.62C927.645 53.406 934.328 47.1865 934.328 37.4626C934.328 27.734 927.645 21.5192 917.615 21.5192H891.669H891.674ZM1009.15 2.22646H1082.36V21.3595H1030.15V47.9851H1077.9V67.1181H1030.15V94.702H1083.31V113.835H1009.15V2.22646Z"
|
||||
fill="#0F110E"
|
||||
/>
|
||||
<path
|
||||
d="M495.456 2.44971C498.389 2.44971 500.761 4.83105 500.761 7.76417V94.6107H521.914C524.842 94.6107 527.219 96.992 527.219 99.93V111.019C527.219 113.957 524.842 116.333 521.914 116.333H392.31C389.382 116.333 387.005 113.957 387.005 111.019V7.76417C387.005 4.83105 389.382 2.44971 392.31 2.44971H495.456ZM408.607 24.0851V94.7026H479.159V24.0851H408.607Z"
|
||||
fill="url(#paint0_linear_9_3)"
|
||||
/>
|
||||
<path
|
||||
d="M1138.81 104.344C1138.81 111.314 1144.06 116.425 1150.87 116.425C1157.82 116.425 1162.93 111.314 1162.93 104.344C1162.93 97.3742 1157.82 92.263 1150.87 92.263C1144.06 92.263 1138.81 97.3742 1138.81 104.344ZM1206.29 15.1307C1206.29 21.4809 1211.09 26.1274 1217.43 26.1274C1223.92 26.1274 1228.71 21.4809 1228.71 15.1307C1228.71 8.62553 1223.92 3.979 1217.43 3.979C1211.09 3.979 1206.29 8.62553 1206.29 15.1307ZM1224.69 38.2084H1210.16V114.257H1224.69V38.2084ZM1269.61 76.3099C1269.61 100.007 1286.15 116.425 1308.73 116.425C1331.45 116.425 1348 100.007 1348 76.3099C1348 52.6127 1331.45 36.04 1308.73 36.04C1286.15 36.04 1269.61 52.6127 1269.61 76.3099ZM1284.14 76.3099C1284.14 60.3569 1293.88 49.0503 1308.73 49.0503C1323.72 49.0503 1333.46 60.0471 1333.46 76.3099C1333.46 92.263 1323.72 103.415 1308.73 103.415C1293.88 103.415 1284.14 92.5728 1284.14 76.3099Z"
|
||||
fill="#0F110E"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_9_3"
|
||||
x1="387.005"
|
||||
y1="-493.171"
|
||||
x2="8497.63"
|
||||
y2="12795.9"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#272A7F" />
|
||||
<stop offset="1" stopColor="#35DCD8" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
import { FC, SVGProps } from 'react';
|
||||
|
||||
export const HeroDevsIcon: FC<SVGProps<SVGSVGElement>> = (props) => {
|
||||
return (
|
||||
<svg
|
||||
width="484"
|
||||
height="500"
|
||||
viewBox="0 0 484 500"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<title>HeroDevs</title>
|
||||
<g clip-path="url(#clip0_6_102)">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M168.465 193.671C96.0868 172.261 104.27 358.657 188.649 307.589C213 292.848 245.097 271.662 283.516 271.662C318.175 271.662 337.138 309.212 371.797 309.212C421.722 303.779 419.576 209.004 385.85 205.936C350.385 205.936 321.323 265.829 275.427 251.754C235.8 239.604 195.13 201.557 168.465 193.671ZM178.261 163.624C215.231 174.56 247.435 209.939 284.762 221.383C287.8 222.315 290.385 222.337 293.443 221.508C300.588 219.573 308.772 213.546 314.638 209.106C336.291 192.715 356.204 174.493 385.051 174.493H386.495L387.933 174.624C462.26 181.382 461.541 333.617 374.543 343.087L372.827 343.274H370.679C367.931 343.274 365.171 343.193 362.444 342.855C340.063 340.077 324.796 329.948 308.908 318.821C302.583 314.392 291.657 305.994 283.451 305.994C255.029 305.994 229.41 322.751 205.823 337.028C182.609 351.078 155.543 356.167 130.914 342.421C101.852 326.2 89.3382 290.624 88.058 258.549C86.7283 225.261 97.2242 186.159 127.537 168.72C143.279 159.665 161.076 158.541 178.261 163.624Z"
|
||||
fill="url(#paint0_linear_6_102)"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M241.698 41.3313C-41.5403 22.7742 140.463 40.2031 77.0514 123.994C50.0436 159.711 33.9091 204.522 33.9091 253.367C33.9091 370.468 126.94 465.395 241.698 465.395C326.662 465.395 399.707 413.357 431.911 338.83C451.999 298.895 464.1 240.899 432.259 169.912C400.189 96.65 326.827 46.9144 241.698 41.3313ZM243.874 6.80207C374.211 15.3389 483.405 116.04 483.405 253.367C483.405 389.57 375.177 500 241.698 500C108.228 500 0 389.561 0 253.367C0 198.743 17.4913 146.099 50.2069 102.847C55.0511 96.4452 59.5501 89.514 56.6917 81.377C50.3631 63.3884 36.987 53.7323 45.0175 29.9563C54.9336 0.592672 87.2957 -0.126835 112.399 0.0108551C155.723 0.250703 200.613 3.96831 243.874 6.80207Z"
|
||||
fill="url(#paint1_linear_6_102)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_6_102"
|
||||
x1="87.9473"
|
||||
y1="262.865"
|
||||
x2="441.888"
|
||||
y2="259.733"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#F926E1" />
|
||||
<stop offset="1" stopColor="#2AB5FB" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_6_102"
|
||||
x1="-1.04689e-07"
|
||||
y1="269.231"
|
||||
x2="483.483"
|
||||
y2="267.011"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#F926E1" />
|
||||
<stop offset="1" stopColor="#2AB5FB" />
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_6_102">
|
||||
<rect width="484" height="500" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { FC, SVGProps } from 'react';
|
||||
|
||||
export const PushBasedIcon: FC<SVGProps<SVGSVGElement>> = (props) => {
|
||||
return (
|
||||
<svg
|
||||
width="366"
|
||||
height="509"
|
||||
viewBox="0 0 366 509"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<title>Push Based</title>
|
||||
<path
|
||||
d="M129.299 474.655C134.496 449.514 132.497 423.177 136.894 397.637C140.092 378.083 146.489 358.13 159.681 342.168C172.073 327.403 192.461 311.84 207.252 300.666C240.432 275.126 278.01 251.981 303.595 218.061C303.595 218.061 304.394 216.864 304.794 216.465C309.991 208.883 316.387 201.301 320.784 192.92C330.379 174.165 335.176 153.414 335.176 132.663C335.176 49.2598 272.413 0.175781 187.664 0.175781H58.9408C26.5601 0.175781 0.175903 26.5136 0.175903 58.8372V449.514C0.175903 481.838 26.5601 508.176 58.9408 508.176H96.5184C116.107 508.176 125.701 491.814 129.299 474.655Z"
|
||||
fill="#2D0064"
|
||||
/>
|
||||
<path
|
||||
d="M365.176 388.788C365.176 427.788 349.59 458.829 323.614 480.319C322.415 481.115 321.216 482.308 320.416 483.104C298.436 499.819 271.261 508.176 243.286 508.176H144.176C152.568 502.206 157.364 492.257 159.362 481.91C164.558 458.033 162.559 433.359 166.955 409.482C170.152 390.778 176.547 372.074 189.735 357.349C202.123 343.421 222.505 328.696 237.292 318.349C240.888 315.962 244.086 313.176 247.682 310.788C264.467 298.849 282.451 287.308 298.436 274.176C311.225 284.523 323.214 295.666 333.205 308.4C333.205 308.4 334.004 309.594 334.404 309.992C339.599 317.155 345.993 324.319 350.389 332.278C359.981 349.39 364.776 369.288 364.776 388.788H365.176Z"
|
||||
fill="#FF003C"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -39,6 +39,8 @@ import { Tab, Tabs } from './lib/tags/tabs.component';
|
||||
import { tab, tabs } from './lib/tags/tabs.schema';
|
||||
import { Tweet, tweet } from '@nx/nx-dev/ui-common';
|
||||
import { YouTube, youtube } from '@nx/nx-dev/ui-common';
|
||||
import { CourseVideo } from '@nx/nx-dev/ui-common';
|
||||
import { courseVideo } from './lib/tags/course-video.schema';
|
||||
import { VideoLink, videoLink } from './lib/tags/video-link.component';
|
||||
// import { SvgAnimation, svgAnimation } from './lib/tags/svg-animation.component';
|
||||
import { Pill } from './lib/tags/pill.component';
|
||||
@@ -91,6 +93,7 @@ export const getMarkdocCustomConfig = (
|
||||
toc: tableOfContents,
|
||||
tweet,
|
||||
youtube,
|
||||
'course-video': courseVideo,
|
||||
'video-link': videoLink,
|
||||
metrics,
|
||||
// 'svg-animation': svgAnimation,
|
||||
@@ -123,6 +126,7 @@ export const getMarkdocCustomConfig = (
|
||||
Testimonial,
|
||||
Tweet,
|
||||
YouTube,
|
||||
CourseVideo,
|
||||
VideoLink,
|
||||
VideoPlayer,
|
||||
Metrics,
|
||||
@@ -176,4 +180,4 @@ export const renderMarkdown: (
|
||||
};
|
||||
};
|
||||
|
||||
export { Metrics, VideoLink, GithubRepository };
|
||||
export { Metrics, VideoLink, GithubRepository, CourseVideo };
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Schema } from '@markdoc/markdoc';
|
||||
|
||||
export const courseVideo: Schema = {
|
||||
render: 'CourseVideo',
|
||||
attributes: {
|
||||
src: {
|
||||
type: 'String',
|
||||
required: true,
|
||||
},
|
||||
courseTitle: {
|
||||
type: 'String',
|
||||
required: true,
|
||||
},
|
||||
courseUrl: {
|
||||
type: 'String',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||