Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2032bd2dac | |||
| 515bfdaba5 | |||
| f3fc658c7d | |||
| 38861fda89 | |||
| 60e6c3631a | |||
| d471af77bd | |||
| 20c932d952 | |||
| 4184ff5ecc | |||
| ef05a372f7 | |||
| 39c4f039ba | |||
| 544a100359 | |||
| db1331630a | |||
| 0b5cb5b1c1 | |||
| 5a42215007 | |||
| e075e2d6e1 | |||
| 3c8f7592b9 | |||
| b2a7927245 | |||
| 7be9aef725 | |||
| 70b977bcd6 | |||
| f45f484ee5 | |||
| 09f8a8d7d1 | |||
| 0b57d42f19 | |||
| e54535e0d3 | |||
| 55963d60fa | |||
| 9151176784 | |||
| 85a84d1d80 | |||
| 2deb3a9ae3 | |||
| ee46f990ff | |||
| 02a5f1c64e | |||
| 0a24474f77 | |||
| 6acf28e86c | |||
| b90cd236f3 | |||
| 69d0c8b435 | |||
| 38bfae2aa7 | |||
| 2bab225141 | |||
| 683a02ffff | |||
| 43f129dfbd | |||
| baaa579b39 | |||
| cb140ed260 |
@@ -4,9 +4,10 @@ version: 2.1
|
||||
# ORBS
|
||||
# -------------------------
|
||||
orbs:
|
||||
nx: nrwl/nx@1.6.2
|
||||
nx: nrwl/nx@1.6.1
|
||||
node: circleci/node@5.0.2
|
||||
rust: circleci/rust@1.6.0
|
||||
browser-tools: circleci/browser-tools@1.4.8
|
||||
browser-tools: circleci/browser-tools@1.4.0
|
||||
|
||||
# -------------------------
|
||||
# EXECUTORS
|
||||
@@ -19,14 +20,14 @@ executors:
|
||||
linux:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: cimg/rust:1.73.0-browsers
|
||||
- image: cimg/rust:1.66.1-browsers
|
||||
resource_class: medium+
|
||||
|
||||
macos:
|
||||
<<: *defaults
|
||||
resource_class: macos.m1.medium.gen1
|
||||
resource_class: macos.x86.medium.gen2
|
||||
macos:
|
||||
xcode: '14.2.0'
|
||||
xcode: &_XCODE_VERSION '13.0.0'
|
||||
|
||||
# -------------------------
|
||||
# COMMANDS
|
||||
@@ -41,27 +42,14 @@ commands:
|
||||
name: Restore pnpm Package Cache
|
||||
keys:
|
||||
- node-deps-{{ arch }}-v3-{{ checksum "pnpm-lock.yaml" }}
|
||||
- when:
|
||||
condition:
|
||||
equal: [<< parameters.os >>, linux]
|
||||
steps:
|
||||
- run:
|
||||
name: Install pnpm package manager (linux)
|
||||
command: |
|
||||
npm install --prefix=$HOME/.local -g @pnpm/exe@8
|
||||
- when:
|
||||
condition:
|
||||
equal: [<< parameters.os >>, macos]
|
||||
steps:
|
||||
- run:
|
||||
name: Install pnpm package manager (macos)
|
||||
command: |
|
||||
npm install -g @pnpm/exe@8
|
||||
- run:
|
||||
name: Install pnpm package manager
|
||||
command: |
|
||||
npm install -g @pnpm/exe@8.3.1
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: |
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm playwright install --with-deps
|
||||
- save_cache:
|
||||
name: Save pnpm Package Cache
|
||||
key: node-deps-{{ arch }}-v3-{{ checksum "pnpm-lock.yaml" }}
|
||||
@@ -69,10 +57,94 @@ commands:
|
||||
- ~/.pnpm-store
|
||||
- ~/.cache/Cypress
|
||||
- node_modules
|
||||
|
||||
setup:
|
||||
parameters:
|
||||
os:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- when:
|
||||
condition:
|
||||
equal: [<< parameters.os >>, macos]
|
||||
steps:
|
||||
- restore_cache:
|
||||
name: Restore Homebrew packages
|
||||
keys:
|
||||
- nrwl-nx-homebrew-packages
|
||||
- run:
|
||||
name: Configure Detox Environment, Install applesimutils
|
||||
command: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew >/dev/null
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install applesimutils >/dev/null
|
||||
xcrun simctl shutdown all && xcrun simctl erase all
|
||||
no_output_timeout: 20m
|
||||
- save_cache:
|
||||
name: Save Homebrew Cache
|
||||
key: nrwl-nx-homebrew-packages
|
||||
paths:
|
||||
- /usr/local/Homebrew
|
||||
- ~/Library/Caches/Homebrew
|
||||
- when:
|
||||
condition:
|
||||
equal: [<< parameters.os >>, linux]
|
||||
steps:
|
||||
- run:
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ca-certificates lsof
|
||||
- browser-tools/install-chrome
|
||||
- browser-tools/install-chromedriver
|
||||
- node/install:
|
||||
# Use LTS version
|
||||
node-version: ''
|
||||
- run-pnpm-install:
|
||||
os: << parameters.os >>
|
||||
|
||||
# -------------------------
|
||||
# JOBS
|
||||
# -------------------------
|
||||
jobs:
|
||||
# -------------------------
|
||||
# JOBS: Agent
|
||||
# -------------------------
|
||||
agent:
|
||||
parameters:
|
||||
os:
|
||||
type: string
|
||||
default: 'linux'
|
||||
pm:
|
||||
type: string
|
||||
default: 'pnpm'
|
||||
executor: << parameters.os >>
|
||||
environment:
|
||||
GIT_AUTHOR_EMAIL: test@test.com
|
||||
GIT_AUTHOR_NAME: Test
|
||||
GIT_COMMITTER_EMAIL: test@test.com
|
||||
GIT_COMMITTER_NAME: Test
|
||||
NX_E2E_CI_CACHE_KEY: e2e-circleci-<< parameters.os >>
|
||||
SELECTED_PM: << parameters.pm >>
|
||||
NX_E2E_RUN_CYPRESS: 'true'
|
||||
NX_VERBOSE_LOGGING: 'false'
|
||||
NX_NATIVE_LOGGING: 'false'
|
||||
NX_PERF_LOGGING: 'false'
|
||||
steps:
|
||||
- run:
|
||||
name: Configure git metadata (needed for lerna smoke tests)
|
||||
command: |
|
||||
git config --global user.email test@test.com
|
||||
git config --global user.name "Test Test"
|
||||
- run:
|
||||
name: Set dynamic nx run variable
|
||||
command: |
|
||||
echo "export NX_RUN_GROUP=\"run-group-<< parameters.os >>-$CIRCLE_WORKFLOW_ID\";" >> $BASH_ENV
|
||||
- setup:
|
||||
os: << parameters.os >>
|
||||
- run:
|
||||
name: Agent
|
||||
command: pnpm nx-cloud start-agent
|
||||
no_output_timeout: 60m
|
||||
|
||||
# -------------------------
|
||||
# JOBS: Main Linux
|
||||
# -------------------------
|
||||
@@ -80,24 +152,20 @@ jobs:
|
||||
executor: linux
|
||||
environment:
|
||||
NX_E2E_CI_CACHE_KEY: e2e-circleci-linux
|
||||
NX_VERBOSE_LOGGING: 'false'
|
||||
NX_DAEMON: 'true'
|
||||
NX_PERF_LOGGING: 'false'
|
||||
NX_NATIVE_LOGGING: 'false'
|
||||
NX_E2E_RUN_E2E: 'true'
|
||||
NX_CI_EXECUTION_ENV: 'linux'
|
||||
NX_CLOUD_DTE_V2: 'true'
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Set dynamic nx run variable
|
||||
command: |
|
||||
echo "export NX_RUN_GROUP=\"run-group-linux-$CIRCLE_WORKFLOW_ID\";" >> $BASH_ENV
|
||||
- setup:
|
||||
os: linux
|
||||
- nx/set-shas:
|
||||
main-branch-name: 'master'
|
||||
- run: npx nx-cloud@next start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yaml" --stop-agents-after="e2e"
|
||||
- run:
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ca-certificates lsof libvips-dev libglib2.0-dev libgirepository1.0-dev
|
||||
- browser-tools/install-chrome
|
||||
- run-pnpm-install:
|
||||
os: linux
|
||||
- run: pnpm nx-cloud start-ci-run --stop-agents-after="e2e"
|
||||
- run:
|
||||
name: Check Documentation
|
||||
command: pnpm nx documentation --no-dte
|
||||
@@ -111,11 +179,15 @@ jobs:
|
||||
pnpm nx-cloud record -- nx format:check --base=$NX_BASE --head=$NX_HEAD &
|
||||
pids+=($!)
|
||||
|
||||
pnpm nx run-many -t check-imports check-commit check-lock-files check-codeowners documentation --parallel=1 --no-dte &
|
||||
pnpm nx run-many -t check-imports check-commit check-lock-files depcheck check-codeowners documentation --parallel=1 --no-dte &
|
||||
pids+=($!)
|
||||
|
||||
(pnpm nx affected --targets=lint,test,build --base=$NX_BASE --head=$NX_HEAD --parallel=3 &&
|
||||
pnpm nx affected --targets=e2e,e2e-ci --base=$NX_BASE --head=$NX_HEAD --parallel=1) &
|
||||
pnpm nx affected --target=lint --base=$NX_BASE --head=$NX_HEAD --parallel=3 &
|
||||
pids+=($!)
|
||||
pnpm nx affected --target=test --base=$NX_BASE --head=$NX_HEAD --parallel=1 &
|
||||
pids+=($!)
|
||||
(pnpm nx affected --target=build --base=$NX_BASE --head=$NX_HEAD --parallel=3 &&
|
||||
pnpm nx affected --target=e2e --base=$NX_BASE --head=$NX_HEAD --parallel=1) &
|
||||
pids+=($!)
|
||||
|
||||
for pid in "${pids[@]}"; do
|
||||
@@ -128,29 +200,15 @@ jobs:
|
||||
executor: macos
|
||||
environment:
|
||||
NX_E2E_CI_CACHE_KEY: e2e-circleci-macos
|
||||
NX_DAEMON: 'true'
|
||||
NX_PERF_LOGGING: 'false'
|
||||
NX_CI_EXECUTION_ENV: 'macos'
|
||||
SELECTED_PM: 'npm' # explicitly define npm for macOS tests
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: Restore Homebrew packages
|
||||
keys:
|
||||
- nrwl-nx-homebrew-packages
|
||||
- run:
|
||||
name: Configure Detox Environment, Install applesimutils
|
||||
name: Set dynamic nx run variable
|
||||
command: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew >/dev/null
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install applesimutils >/dev/null
|
||||
xcrun simctl shutdown all && xcrun simctl erase all
|
||||
no_output_timeout: 20m
|
||||
- save_cache:
|
||||
name: Save Homebrew Cache
|
||||
key: nrwl-nx-homebrew-packages
|
||||
paths:
|
||||
- /usr/local/Homebrew
|
||||
- ~/Library/Caches/Homebrew
|
||||
- run-pnpm-install:
|
||||
echo "export NX_RUN_GROUP=\"run-group-macos-$CIRCLE_WORKFLOW_ID\";" >> $BASH_ENV
|
||||
- setup:
|
||||
os: macos
|
||||
- rust/install
|
||||
- nx/set-shas:
|
||||
@@ -158,7 +216,7 @@ jobs:
|
||||
- run:
|
||||
name: Run E2E Tests for macOS
|
||||
command: |
|
||||
pnpm nx affected -t e2e-macos-ci --parallel=1 --base=$NX_BASE --head=$NX_HEAD
|
||||
pnpm nx affected -t e2e-macos --parallel=1 --base=$NX_BASE --head=$NX_HEAD
|
||||
no_output_timeout: 45m
|
||||
|
||||
# -------------------------
|
||||
@@ -169,6 +227,22 @@ workflows:
|
||||
|
||||
build:
|
||||
jobs:
|
||||
- agent:
|
||||
name: 'agent1'
|
||||
- agent:
|
||||
name: 'agent2'
|
||||
- agent:
|
||||
name: 'agent3'
|
||||
- agent:
|
||||
name: 'agent4'
|
||||
- agent:
|
||||
name: 'agent5'
|
||||
- agent:
|
||||
name: 'agent6'
|
||||
- agent:
|
||||
name: 'agent7'
|
||||
- agent:
|
||||
name: 'agent8'
|
||||
- main-linux
|
||||
- mainmacos:
|
||||
name: main-macos-e2e
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
FROM mcr.microsoft.com/devcontainers/typescript-node:20-bullseye
|
||||
|
||||
# Update the underlying (Debian) OS, to make sure we have the latest security patches and libraries like 'GLIBC'
|
||||
RUN sudo apt-get update && sudo apt-get -y upgrade
|
||||
|
||||
# Update pnpm
|
||||
RUN npm install -g pnpm@8.15.7
|
||||
@@ -3,31 +3,26 @@
|
||||
{
|
||||
"name": "NxDevContainer",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"build": {
|
||||
// Path is relative to the devcontainer.json file.
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node:0-18",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/rust:1": {}
|
||||
},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// 4211 = nx graph port
|
||||
"forwardPorts": [4211],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "./.devcontainer/postCreateCommand.sh",
|
||||
// Configure tool-specific properties.
|
||||
"postCreateCommand": "pnpm install",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"nrwl.angular-console",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"eamodio.gitlens"
|
||||
],
|
||||
"settings": {
|
||||
"debug.javascript.autoAttachFilter": "onlyWithFlag" // workaround for that issue: https://github.com/microsoft/vscode-js-debug/issues/374#issuecomment-622239998
|
||||
}
|
||||
"extensions": ["nrwl.angular-console"]
|
||||
}
|
||||
}
|
||||
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Install dependencies
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
{
|
||||
"group": ["nx/src/plugins/js*"],
|
||||
"message": "Imports from 'nx/src/plugins/js' are not allowed. Use '@nx/js' instead"
|
||||
},
|
||||
{
|
||||
"group": ["**/native-bindings", "**/native-bindings.js", ""],
|
||||
"message": "Direct imports from native-bindings.js are not allowed. Import from index.js instead."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -28,8 +24,7 @@
|
||||
"storybook/no-uninstalled-addons": [
|
||||
"error",
|
||||
{
|
||||
"ignore": ["@nx/react/plugins/storybook"],
|
||||
"packageJsonLocation": "../../package.json"
|
||||
"ignore": ["@nx/react/plugins/storybook"]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -52,7 +47,6 @@
|
||||
"error",
|
||||
{
|
||||
"enforceBuildableLibDependency": true,
|
||||
"checkDynamicDependenciesExceptions": [".*"],
|
||||
"allow": [],
|
||||
"depConstraints": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: 🐞 Bug Report
|
||||
description: This form is to report unexpected behavior in Nx.
|
||||
labels: ["type: bug"]
|
||||
labels: [ "type: bug" ]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -53,13 +53,6 @@ body:
|
||||
label: Failure Logs
|
||||
description: Please include any relevant log snippets or files here. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: input
|
||||
id: pm
|
||||
attributes:
|
||||
label: Package Manager Version
|
||||
description: |
|
||||
If `nx report` doesn't work, please provide the name and the version of your package manager.
|
||||
You can get version information by running `yarn --version`, `pnpm --version` or `npm --version`, depending on the package manager used.
|
||||
- type: checkboxes
|
||||
id: os
|
||||
attributes:
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: "\U0001F680 Feature Request"
|
||||
about: Suggest a new feature.
|
||||
labels: "type: feature"
|
||||
|
||||
---
|
||||
<!-- Please do your best to fill out all of the sections below! -->
|
||||
<!-- Use this issue type for concrete suggestions, otherwise, open a discussion type issue instead. -->
|
||||
|
||||
- [ ] I'd be willing to implement this feature ([contributing guide](https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md))
|
||||
|
||||
|
||||
## Description
|
||||
<!-- What is the behavior that you would like to see introduced? -->
|
||||
|
||||
## Motivation
|
||||
<!-- Why do you believe this behavior would be beneficial? -->
|
||||
|
||||
## Suggested Implementation
|
||||
<!-- How do you imagine this might work? -->
|
||||
|
||||
## Alternate Implementations
|
||||
<!-- How else do you imagine this might work? -->
|
||||
@@ -1,17 +1,14 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: "\U0001F680 Feature Request"
|
||||
about: "Suggest a new feature to make Nx better"
|
||||
url: https://github.com/nrwl/nx/discussions/new?category=feature-requests
|
||||
- name: Start a Discussion
|
||||
about: "Start a discussion to share your experience with Nx"
|
||||
url: https://github.com/nrwl/nx/discussions/new/choose
|
||||
- name: Join the Discord
|
||||
url: https://go.nx.dev/community
|
||||
about: "The Nx Official Discord Server is a great place for questions to be asked and answered. Please use the #forum if you need help with your workspace!"
|
||||
- name: Are you looking for integration with a new tool?
|
||||
url: https://nx.dev/community
|
||||
about: "There are a lot of awesome Plugins for Nx provided by the community! Check here to see if there is a community plugin to integrate your tool."
|
||||
- name: Read the community guidelines
|
||||
about: "Please make sure you have read the submission guidelines before posting an issue"
|
||||
url: https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-an-issue
|
||||
- name: Want to start a discussion?
|
||||
about: "Want to start a thread to discuss an idea? Use the discussions feature provided by GitHub."
|
||||
url: https://github.com/nrwl/nx/discussions
|
||||
- name: Have a question?
|
||||
url: https://go.nrwl.io/join-slack
|
||||
about: "The Community Slack is a great place for questions to be asked and answered. Please use the #support channel if you need help with your workspace!"
|
||||
- name: Are you looking for integration with a new tool?
|
||||
url: https://nx.dev/community
|
||||
about: "There are a lot of awesome Plugins for Nx provided by the community! Check here to see if there is a community plugin to integrate your tool."
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
<!-- Please make sure that your commit message follows our format -->
|
||||
<!-- Example: `fix(nx): must begin with lowercase` -->
|
||||
|
||||
<!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. -->
|
||||
|
||||
## Current Behavior
|
||||
<!-- This is the behavior we have today -->
|
||||
|
||||
|
||||
@@ -5,27 +5,6 @@ _[Please make sure you have read the submission guidelines before posting an PR]
|
||||
|
||||
Thanks for submitting your Nx Plugin to our community plugins list. Make sure to follow these steps to ensure that your PR is approved in a timely manner.
|
||||
|
||||
## Plugin Requirements
|
||||
|
||||
Before you submit your plugin to be listed in our registry, it needs to meet the following requirements:
|
||||
- Run some kind of automated e2e tests in your repository
|
||||
- Include `@nx/devkit` as a `dependency` in the plugin's `package.json`
|
||||
- List a `repository.url` in the plugin's `package.json`
|
||||
|
||||
i.e.
|
||||
|
||||
```
|
||||
{
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nrwl/nx.git",
|
||||
"directory": "packages/web"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: We reserve the right to remove unmaintained plugins from the registry. If the plugins become maintained again, they can be resubmitted to the registry.
|
||||
|
||||
## Steps to Submit Your Plugin
|
||||
- Use the following commit message template: `chore(core): nx plugin submission [PLUGIN_NAME]`
|
||||
- Update the `community/approved-plugins.json` file with a new entry for your plugin that includes `name`, `url`, `description`:
|
||||
@@ -42,7 +21,7 @@ Example:
|
||||
}]
|
||||
```
|
||||
|
||||
Once merged, your plugin will be available when running the `nx list` command, and will also be available in the Plugin Registry on [nx.dev](https://nx.dev/plugin-registry)
|
||||
Once merged, your will plugin will be available when running the `nx list` command, and will also be available in the Plugin browser on [nx.dev](https://nx.dev)
|
||||
-->
|
||||
|
||||
# Community Plugin Submission
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Unmergeable Labels Check
|
||||
name: Unmergable Labels Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
@@ -2,7 +2,7 @@ name: E2E matrix
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 5 * * *"
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
debug_enabled:
|
||||
@@ -14,10 +14,9 @@ on:
|
||||
env:
|
||||
CYPRESS_CACHE_FOLDER: ${{ github.workspace }}/.cypress
|
||||
|
||||
permissions: { }
|
||||
permissions: {}
|
||||
jobs:
|
||||
preinstall:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -25,21 +24,24 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
node_version:
|
||||
- 20
|
||||
- 19
|
||||
- 18
|
||||
- 16
|
||||
exclude:
|
||||
# run just node v20 on macos
|
||||
# run just node v18 on macos
|
||||
- os: macos-latest
|
||||
node_version: 18
|
||||
node_version: 19
|
||||
- os: macos-latest
|
||||
node_version: 16
|
||||
|
||||
name: Cache install (${{ matrix.os }}, node v${{ matrix.node_version }})
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install PNPM
|
||||
run: |
|
||||
npm install -g @pnpm/exe@8
|
||||
npm install -g @pnpm/exe@8.3.1
|
||||
|
||||
- name: Set node
|
||||
uses: actions/setup-node@v3
|
||||
@@ -55,11 +57,6 @@ jobs:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-modules-${{ matrix.node_version }}-${{ github.run_id }}
|
||||
|
||||
- name: Ensure Python setuptools Installed on Macos
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
id: brew-install-python-setuptools
|
||||
run: brew install python-setuptools
|
||||
|
||||
- name: Install packages
|
||||
if: steps.cache-modules.outputs.cache-hit != 'true'
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -92,7 +89,6 @@ jobs:
|
||||
run: npx cypress install
|
||||
|
||||
e2e:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
needs: preinstall
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -104,41 +100,41 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
node_version:
|
||||
- 20
|
||||
- 19
|
||||
- 18
|
||||
- 16
|
||||
package_manager:
|
||||
- npm
|
||||
- yarn
|
||||
- pnpm
|
||||
project:
|
||||
- e2e-angular
|
||||
- e2e-angular-core
|
||||
- e2e-angular-extensions
|
||||
- e2e-cypress
|
||||
- e2e-detox
|
||||
- e2e-esbuild
|
||||
- e2e-eslint
|
||||
- e2e-expo
|
||||
- e2e-gradle
|
||||
- e2e-jest
|
||||
- e2e-js
|
||||
- e2e-lerna-smoke-tests
|
||||
- e2e-linter
|
||||
- e2e-next
|
||||
- e2e-node
|
||||
- e2e-nuxt
|
||||
- e2e-nx-init
|
||||
- e2e-nx
|
||||
- e2e-playwright
|
||||
- e2e-plugin
|
||||
- e2e-react
|
||||
- e2e-nx-misc
|
||||
- e2e-nx-plugin
|
||||
- e2e-nx-run
|
||||
- e2e-react-core
|
||||
- e2e-react-extensions
|
||||
- e2e-react-native
|
||||
- e2e-release
|
||||
- e2e-remix
|
||||
- e2e-web
|
||||
- e2e-rollup
|
||||
- e2e-storybook
|
||||
- e2e-storybook-angular
|
||||
- e2e-vite
|
||||
- e2e-vue
|
||||
- e2e-web
|
||||
- e2e-webpack
|
||||
- e2e-workspace-create
|
||||
- e2e-workspace-create-npm
|
||||
include:
|
||||
# os short names
|
||||
- os: ubuntu-latest
|
||||
@@ -151,7 +147,9 @@ jobs:
|
||||
- os: macos-latest
|
||||
os_timeout: 90
|
||||
# codeowner groups
|
||||
- project: e2e-angular
|
||||
- project: e2e-angular-core
|
||||
codeowners: 'S04SS457V38'
|
||||
- project: e2e-angular-extensions
|
||||
codeowners: 'S04SS457V38'
|
||||
- project: e2e-cypress
|
||||
codeowners: 'S04T16BTJJY'
|
||||
@@ -161,15 +159,13 @@ jobs:
|
||||
codeowners: 'S04SJ6HHP0X'
|
||||
- project: e2e-expo
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-gradle
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-jest
|
||||
codeowners: 'S04T16BTJJY'
|
||||
- project: e2e-js
|
||||
codeowners: 'S04SJ6HHP0X'
|
||||
- project: e2e-lerna-smoke-tests
|
||||
codeowners: 'S04TNCVEETS'
|
||||
- project: e2e-eslint
|
||||
- project: e2e-linter
|
||||
codeowners: 'S04SYJGKSCT'
|
||||
- project: e2e-next
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
@@ -177,13 +173,15 @@ jobs:
|
||||
codeowners: 'S04SJ6HHP0X'
|
||||
- project: e2e-nx-init
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-nx
|
||||
- project: e2e-nx-misc
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-plugin
|
||||
- project: e2e-nx-plugin
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-release
|
||||
- project: e2e-nx-run
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-react
|
||||
- project: e2e-react-core
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-react-extensions
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-react-native
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
@@ -193,20 +191,16 @@ jobs:
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-storybook
|
||||
codeowners: 'S04SVQ8H0G5'
|
||||
- project: e2e-playwright
|
||||
codeowners: 'S04SVQ8H0G5'
|
||||
- project: e2e-remix
|
||||
- project: e2e-storybook-angular
|
||||
codeowners: 'S04SVQ8H0G5'
|
||||
- project: e2e-vite
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-vue
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-nuxt
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-webpack
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-workspace-create
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-workspace-create-npm
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
exclude:
|
||||
# exclude react-native tests from ubuntu
|
||||
- os: ubuntu-latest
|
||||
@@ -216,76 +210,124 @@ jobs:
|
||||
- os: ubuntu-latest
|
||||
project: e2e-expo
|
||||
# exclude non-CNW/Lerna tests from non-LTS node versions
|
||||
- node_version: 18
|
||||
project: e2e-angular
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-angular-core
|
||||
- node_version: 16
|
||||
project: e2e-angular-extensions
|
||||
- node_version: 16
|
||||
project: e2e-cypress
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-detox
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-esbuild
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-expo
|
||||
- node_version: 18
|
||||
project: e2e-gradle
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-jest
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-js
|
||||
- node_version: 18
|
||||
project: e2e-eslint
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-linter
|
||||
- node_version: 16
|
||||
project: e2e-next
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-node
|
||||
- node_version: 18
|
||||
project: e2e-nuxt
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-nx-init
|
||||
- node_version: 18
|
||||
project: e2e-nx
|
||||
- node_version: 18
|
||||
project: e2e-plugin
|
||||
- node_version: 18
|
||||
project: e2e-playwright
|
||||
- node_version: 18
|
||||
project: e2e-react
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-nx-misc
|
||||
- node_version: 16
|
||||
project: e2e-nx-plugin
|
||||
- node_version: 16
|
||||
project: e2e-lerna-smoke-tests
|
||||
- node_version: 16
|
||||
project: e2e-react-core
|
||||
- node_version: 16
|
||||
project: e2e-react-extensions
|
||||
- node_version: 16
|
||||
project: e2e-react-native
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-web
|
||||
- node_version: 18
|
||||
project: e2e-remix
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-rollup
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-storybook
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-storybook-angular
|
||||
- node_version: 16
|
||||
project: e2e-vite
|
||||
- node_version: 18
|
||||
project: e2e-vue
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-webpack
|
||||
# run just npm v20 on macos
|
||||
- node_version: 19
|
||||
project: e2e-angular-core
|
||||
- node_version: 19
|
||||
project: e2e-angular-extensions
|
||||
- node_version: 19
|
||||
project: e2e-cypress
|
||||
- node_version: 19
|
||||
project: e2e-detox
|
||||
- node_version: 19
|
||||
project: e2e-esbuild
|
||||
- node_version: 19
|
||||
project: e2e-expo
|
||||
- node_version: 19
|
||||
project: e2e-jest
|
||||
- node_version: 19
|
||||
project: e2e-js
|
||||
- node_version: 19
|
||||
project: e2e-linter
|
||||
- node_version: 19
|
||||
project: e2e-next
|
||||
- node_version: 19
|
||||
project: e2e-node
|
||||
- node_version: 19
|
||||
project: e2e-nx-init
|
||||
- node_version: 19
|
||||
project: e2e-nx-misc
|
||||
- node_version: 19
|
||||
project: e2e-nx-plugin
|
||||
- node_version: 19
|
||||
project: e2e-lerna-smoke-tests
|
||||
- node_version: 19
|
||||
project: e2e-react-core
|
||||
- node_version: 19
|
||||
project: e2e-react-extensions
|
||||
- node_version: 19
|
||||
project: e2e-react-native
|
||||
- node_version: 19
|
||||
project: e2e-web
|
||||
- node_version: 19
|
||||
project: e2e-rollup
|
||||
- node_version: 19
|
||||
project: e2e-storybook
|
||||
- node_version: 19
|
||||
project: e2e-storybook-angular
|
||||
- node_version: 19
|
||||
project: e2e-vite
|
||||
- node_version: 19
|
||||
project: e2e-webpack
|
||||
# run just npm v18 on macos
|
||||
- os: macos-latest
|
||||
package_manager: yarn
|
||||
- os: macos-latest
|
||||
package_manager: pnpm
|
||||
- os: macos-latest
|
||||
node_version: 18
|
||||
node_version: 16
|
||||
- os: macos-latest
|
||||
node_version: 19
|
||||
fail-fast: false
|
||||
|
||||
name: ${{ matrix.os_name }}/${{ matrix.package_manager }}/${{ matrix.node_version }} ${{ join(matrix.project) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare dir for output
|
||||
run: mkdir -p outputs
|
||||
|
||||
- name: Install PNPM
|
||||
run: |
|
||||
npm install -g @pnpm/exe@8
|
||||
npm install -g @pnpm/exe@8.3.1
|
||||
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v3
|
||||
@@ -359,7 +401,7 @@ jobs:
|
||||
|
||||
- name: Run e2e tests
|
||||
id: e2e-run
|
||||
run: pnpm nx run-many -t e2e-local -p ${{ matrix.project }}
|
||||
run: pnpm nx run-many -t e2e,e2e-macos -p ${{ matrix.project }}
|
||||
timeout-minutes: ${{ matrix.os_timeout }}
|
||||
env:
|
||||
GIT_AUTHOR_EMAIL: test@test.com
|
||||
@@ -372,12 +414,11 @@ jobs:
|
||||
npm_config_registry: http://localhost:4872
|
||||
YARN_REGISTRY: http://localhost:4872
|
||||
NX_CACHE_DIRECTORY: 'tmp'
|
||||
NX_E2E_SKIP_CLEANUP: 'true'
|
||||
NX_E2E_RUN_E2E: 'true'
|
||||
NX_E2E_SKIP_BUILD_CLEANUP: 'true'
|
||||
NX_E2E_RUN_CYPRESS: 'true'
|
||||
NX_E2E_VERBOSE_LOGGING: 'true'
|
||||
NX_PERF_LOGGING: 'false'
|
||||
NX_DAEMON: 'true'
|
||||
NX_SKIP_LOG_GROUPING: 'true'
|
||||
|
||||
- name: Save matrix config in file
|
||||
if: ${{ always() }}
|
||||
@@ -408,7 +449,7 @@ jobs:
|
||||
timeout-minutes: 15
|
||||
|
||||
process-result:
|
||||
if: ${{ always() && github.repository_owner == 'nrwl' }}
|
||||
if: ${{ always() }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: e2e
|
||||
outputs:
|
||||
@@ -458,7 +499,7 @@ jobs:
|
||||
|--------------------------------|------|-------|------|`;
|
||||
failedProjects.forEach(matrix => {
|
||||
const project = matrix.project !== lastProject ? matrix.project : '...';
|
||||
result += `\n| ${project.padEnd(30)} | ${matrix.package_manager.padEnd(4)} | ${matrix.os_name} | v${matrix.node_version} |`
|
||||
result += `\n| ${project.padEnd(30)} | ${matrix.package_manager.padEnd(4)} | ${matrix.os_name} | v${matrix.node_version.toString().padEnd(3)} |`
|
||||
lastProject = matrix.project;
|
||||
});
|
||||
result += `\`\`\``;
|
||||
|
||||
@@ -2,7 +2,7 @@ name: E2E matrix (Windows)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 5 * * *"
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
debug_enabled:
|
||||
@@ -14,26 +14,26 @@ on:
|
||||
env:
|
||||
CYPRESS_CACHE_FOLDER: ${{ github.workspace }}/.cypress
|
||||
|
||||
permissions: { }
|
||||
permissions: {}
|
||||
jobs:
|
||||
preinstall:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node_version:
|
||||
- 20
|
||||
- 19
|
||||
- 18
|
||||
- 16
|
||||
|
||||
name: Cache install (node v${{ matrix.node_version }})
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
version: 8.3.1
|
||||
run_install: false
|
||||
|
||||
- name: Set node
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
- name: Cache node_modules
|
||||
id: cache-modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
lookup-only: true
|
||||
path: '**/node_modules'
|
||||
@@ -63,10 +63,9 @@ jobs:
|
||||
|
||||
- name: Install Cypress
|
||||
if: steps.cache-cypress.outputs.cache-hit != 'true'
|
||||
run: pnpm cypress install
|
||||
run: npx cypress install
|
||||
|
||||
e2e:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
needs: preinstall
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -74,56 +73,53 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
node_version:
|
||||
- 20
|
||||
- 19
|
||||
- 18
|
||||
- 16
|
||||
package_manager:
|
||||
- npm
|
||||
project:
|
||||
- e2e-angular
|
||||
- e2e-angular-core
|
||||
- e2e-angular-extensions
|
||||
- e2e-cypress
|
||||
- e2e-esbuild
|
||||
- e2e-eslint
|
||||
- e2e-jest
|
||||
- e2e-js
|
||||
- e2e-lerna-smoke-tests
|
||||
- e2e-linter
|
||||
- e2e-next
|
||||
- e2e-node
|
||||
- e2e-nuxt
|
||||
- e2e-nx-init
|
||||
- e2e-nx
|
||||
- e2e-playwright
|
||||
- e2e-nx-misc
|
||||
- e2e-plugin
|
||||
- e2e-react
|
||||
- e2e-release
|
||||
- e2e-remix
|
||||
- e2e-nx-run
|
||||
- e2e-react-core
|
||||
- e2e-react-extensions
|
||||
- e2e-web
|
||||
- e2e-rollup
|
||||
- e2e-storybook
|
||||
- e2e-storybook-angular
|
||||
- e2e-vite
|
||||
- e2e-vue
|
||||
- e2e-web
|
||||
- e2e-webpack
|
||||
- e2e-workspace-create
|
||||
- e2e-workspace-create-npm
|
||||
include:
|
||||
# codeowner groups
|
||||
- project: e2e-angular
|
||||
- project: e2e-angular-core
|
||||
codeowners: 'S04SS457V38'
|
||||
- project: e2e-angular-extensions
|
||||
codeowners: 'S04SS457V38'
|
||||
- project: e2e-cypress
|
||||
codeowners: 'S04T16BTJJY'
|
||||
- project: e2e-detox
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-esbuild
|
||||
codeowners: 'S04SJ6HHP0X'
|
||||
- project: e2e-expo
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-gradle
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-jest
|
||||
codeowners: 'S04T16BTJJY'
|
||||
- project: e2e-js
|
||||
codeowners: 'S04SJ6HHP0X'
|
||||
- project: e2e-lerna-smoke-tests
|
||||
codeowners: 'S04TNCVEETS'
|
||||
- project: e2e-eslint
|
||||
- project: e2e-linter
|
||||
codeowners: 'S04SYJGKSCT'
|
||||
- project: e2e-next
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
@@ -131,15 +127,15 @@ jobs:
|
||||
codeowners: 'S04SJ6HHP0X'
|
||||
- project: e2e-nx-init
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-nx
|
||||
- project: e2e-nx-misc
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-plugin
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-release
|
||||
- project: e2e-nx-run
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-react
|
||||
- project: e2e-react-core
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-react-native
|
||||
- project: e2e-react-extensions
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-web
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
@@ -147,70 +143,108 @@ jobs:
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-storybook
|
||||
codeowners: 'S04SVQ8H0G5'
|
||||
- project: e2e-playwright
|
||||
codeowners: 'S04SVQ8H0G5'
|
||||
- project: e2e-remix
|
||||
- project: e2e-storybook-angular
|
||||
codeowners: 'S04SVQ8H0G5'
|
||||
- project: e2e-vite
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-vue
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-nuxt
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-webpack
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-workspace-create
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-workspace-create-npm
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
exclude:
|
||||
# exclude non-CNW/Lerna tests from non-LTS node versions
|
||||
- node_version: 18
|
||||
project: e2e-angular
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-angular-core
|
||||
- node_version: 16
|
||||
project: e2e-angular-extensions
|
||||
- node_version: 16
|
||||
project: e2e-cypress
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-esbuild
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-jest
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-js
|
||||
- node_version: 18
|
||||
project: e2e-eslint
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-linter
|
||||
- node_version: 16
|
||||
project: e2e-next
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-node
|
||||
- node_version: 18
|
||||
project: e2e-nuxt
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-nx-init
|
||||
- node_version: 18
|
||||
project: e2e-nx
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-nx-misc
|
||||
- node_version: 16
|
||||
project: e2e-plugin
|
||||
- node_version: 18
|
||||
project: e2e-playwright
|
||||
- node_version: 18
|
||||
project: e2e-react
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-lerna-smoke-tests
|
||||
- node_version: 16
|
||||
project: e2e-react-core
|
||||
- node_version: 16
|
||||
project: e2e-react-extensions
|
||||
- node_version: 16
|
||||
project: e2e-web
|
||||
- node_version: 18
|
||||
project: e2e-remix
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-rollup
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-storybook
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-storybook-angular
|
||||
- node_version: 16
|
||||
project: e2e-vite
|
||||
- node_version: 18
|
||||
project: e2e-vue
|
||||
- node_version: 18
|
||||
- node_version: 16
|
||||
project: e2e-webpack
|
||||
- node_version: 19
|
||||
project: e2e-angular-core
|
||||
- node_version: 19
|
||||
project: e2e-angular-extensions
|
||||
- node_version: 19
|
||||
project: e2e-cypress
|
||||
- node_version: 19
|
||||
project: e2e-esbuild
|
||||
- node_version: 19
|
||||
project: e2e-jest
|
||||
- node_version: 19
|
||||
project: e2e-js
|
||||
- node_version: 19
|
||||
project: e2e-linter
|
||||
- node_version: 19
|
||||
project: e2e-next
|
||||
- node_version: 19
|
||||
project: e2e-node
|
||||
- node_version: 19
|
||||
project: e2e-nx-init
|
||||
- node_version: 19
|
||||
project: e2e-nx-misc
|
||||
- node_version: 19
|
||||
project: e2e-plugin
|
||||
- node_version: 19
|
||||
project: e2e-lerna-smoke-tests
|
||||
- node_version: 19
|
||||
project: e2e-react-core
|
||||
- node_version: 19
|
||||
project: e2e-react-extensions
|
||||
- node_version: 19
|
||||
project: e2e-web
|
||||
- node_version: 19
|
||||
project: e2e-rollup
|
||||
- node_version: 19
|
||||
project: e2e-storybook
|
||||
- node_version: 19
|
||||
project: e2e-storybook-angular
|
||||
- node_version: 19
|
||||
project: e2e-vite
|
||||
- node_version: 19
|
||||
project: e2e-webpack
|
||||
fail-fast: false
|
||||
|
||||
name: ${{ matrix.project }} (v${{ matrix.node_version }})
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare dir for output
|
||||
run: mkdir -p outputs
|
||||
@@ -218,7 +252,7 @@ jobs:
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8.7.4
|
||||
version: 8.3.1
|
||||
run_install: false
|
||||
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
@@ -255,9 +289,9 @@ jobs:
|
||||
|
||||
- name: Run e2e tests
|
||||
id: e2e-run
|
||||
run: pnpm nx run ${{ matrix.project }}:e2e-local
|
||||
run: pnpm nx run ${{ matrix.project }}:e2e
|
||||
shell: bash
|
||||
timeout-minutes: 180
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
GIT_AUTHOR_EMAIL: test@test.com
|
||||
GIT_AUTHOR_NAME: Test
|
||||
@@ -268,12 +302,11 @@ jobs:
|
||||
SELECTED_PM: ${{ matrix.package_manager }}
|
||||
npm_config_registry: http://localhost:4872
|
||||
NX_CACHE_DIRECTORY: 'tmp'
|
||||
NX_E2E_SKIP_CLEANUP: 'true'
|
||||
NX_E2E_RUN_E2E: 'true'
|
||||
NX_E2E_SKIP_BUILD_CLEANUP: 'true'
|
||||
NX_E2E_RUN_CYPRESS: 'true'
|
||||
NX_E2E_VERBOSE_LOGGING: 'true'
|
||||
NX_PERF_LOGGING: 'false'
|
||||
NX_DAEMON: 'true'
|
||||
NX_SKIP_LOG_GROUPING: 'true'
|
||||
|
||||
- name: Save matrix config in file
|
||||
if: ${{ always() }}
|
||||
@@ -303,7 +336,7 @@ jobs:
|
||||
sudo: false # disable sudo for windows debugging
|
||||
|
||||
process-result:
|
||||
if: ${{ always() && github.repository_owner == 'nrwl' }}
|
||||
if: ${{ always() }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: e2e
|
||||
outputs:
|
||||
@@ -348,7 +381,7 @@ jobs:
|
||||
| Failed project | Node |
|
||||
|--------------------------------|------|`;
|
||||
failedProjects.forEach(matrix => {
|
||||
result += `\n| ${matrix.project.padEnd(30)} | v${matrix.node_version} |`
|
||||
result += `\n| ${matrix.project.padEnd(30)} | v${matrix.node_version.toString().padEnd(3)} |`
|
||||
});
|
||||
result += `\`\`\``;
|
||||
const message = result.split('\n').map(l => l.trim()).join('\n');
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
name: Generate embeddings
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 5 * * 0,4" # sunday, thursday 5AM
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
cache-and-install:
|
||||
if: github.repository == 'nrwl/nx'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
|
||||
- name: Run embeddings script
|
||||
run: pnpm exec nx run tools-documentation-create-embeddings:run-node
|
||||
env:
|
||||
NX_NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NX_NEXT_PUBLIC_SUPABASE_URL }}
|
||||
NX_SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.NX_SUPABASE_SERVICE_ROLE_KEY }}
|
||||
NX_OPENAI_KEY: ${{ secrets.NX_OPENAI_KEY }}
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
version: 8.2
|
||||
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v3
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
- name: Install PNPM
|
||||
run: |
|
||||
npm install -g @pnpm/exe@8
|
||||
npm install -g @pnpm/exe@8.3.1
|
||||
|
||||
- name: Run a security audit
|
||||
run: pnpm dlx audit-ci --critical --report-type summary
|
||||
|
||||
@@ -1,127 +1,19 @@
|
||||
name: publish
|
||||
|
||||
on:
|
||||
# Automated schedule - canary releases from master
|
||||
schedule:
|
||||
- cron: "0 3 * * 2-6" # Tuesdays - Saturdays, at 3am UTC
|
||||
# Manual trigger - PR releases or dry-runs (based on workflow inputs)
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr:
|
||||
description: "PR Number - If set, a real release will be created for the branch associated with the given PR number. If blank, a dry-run of the currently selected branch will be performed."
|
||||
required: false
|
||||
type: number
|
||||
release:
|
||||
types: [ published ]
|
||||
|
||||
# Dynamically generate the display name for the GitHub UI based on the event type and inputs
|
||||
run-name: ${{ github.event.inputs.pr && format('PR Release for {0}', github.event.inputs.pr) || github.event_name == 'schedule' && 'Canary Release' || github.event_name == 'workflow_dispatch' && !github.event.inputs.pr && 'Release Dry-Run' || github.ref_name }}
|
||||
|
||||
env:
|
||||
DEBUG: napi:*
|
||||
NX_RUN_GROUP: ${{ github.run_id }}-${{ github.run_attempt }}
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
NODE_VERSION: 18
|
||||
PNPM_VERSION: 8.15.7 # Aligned with root package.json (pnpm/action-setup will helpfully error if out of sync)
|
||||
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
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.
|
||||
# These values depend upon the event type that triggered the workflow:
|
||||
#
|
||||
# - schedule:
|
||||
# - We are running a canary release which always comes from the master branch, we can use default ref resolution
|
||||
# in actions/checkout. The exact version will be generated within scripts/nx-release.ts.
|
||||
#
|
||||
# - release:
|
||||
# - We are running a full release which is based on the tag that triggered the release event, we can use default
|
||||
# ref resolution in actions/checkout. The exact version will be generated within scripts/nx-release.ts.
|
||||
#
|
||||
# - workflow_dispatch:
|
||||
# - We are either running a dry-run on the current branch, in which case the version will be statica and we can use
|
||||
# default ref resolution in actions/checkout, or we are creating a PR release for the given PR number, in which case
|
||||
# we should generate an applicable version number within publish-resolve-data.js and use a custom ref of the PR branch name.
|
||||
resolve-required-data:
|
||||
name: Resolve Required Data
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.script.outputs.version }}
|
||||
dry_run_flag: ${{ steps.script.outputs.dry_run_flag }}
|
||||
success_comment: ${{ steps.script.outputs.success_comment }}
|
||||
publish_branch: ${{ steps.script.outputs.publish_branch }}
|
||||
ref: ${{ steps.script.outputs.ref }}
|
||||
repo: ${{ steps.script.outputs.repo }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
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 }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
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)
|
||||
|
||||
- name: Resolve and set checkout and version data to use for release
|
||||
id: script
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.inputs.pr }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const script = require('${{ github.workspace }}/scripts/publish-resolve-data.js');
|
||||
await script({ github, context, core });
|
||||
|
||||
- name: (PR Release Only) Check out latest master
|
||||
if: ${{ steps.script.outputs.ref != '' }}
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# Check out the latest master branch to get its copy of nx-release.ts
|
||||
repository: nrwl/nx
|
||||
ref: master
|
||||
path: latest-master-checkout
|
||||
|
||||
- name: (PR Release Only) Check out PR branch
|
||||
if: ${{ steps.script.outputs.ref != '' }}
|
||||
uses: actions/checkout@v4
|
||||
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
|
||||
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
|
||||
|
||||
build:
|
||||
needs: [resolve-required-data]
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
if: "!contains(github.event.head_commit.message, 'skip ci')"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
settings:
|
||||
- host: macos-13
|
||||
- host: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
build: |
|
||||
pnpm nx run-many --target=build-native -- --target=x86_64-apple-darwin
|
||||
@@ -138,20 +30,13 @@ jobs:
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||
build: |-
|
||||
set -e &&
|
||||
npm i -g pnpm@8.15.7 --force &&
|
||||
pnpm --version &&
|
||||
pnpm install --frozen-lockfile &&
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=x86_64-unknown-linux-gnu
|
||||
pnpm nx run-many --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@8.15.7 --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: set -e && pnpm nx run-many --target=build-native -- --target=x86_64-unknown-linux-musl
|
||||
- host: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
build: |
|
||||
sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*;
|
||||
@@ -165,10 +50,8 @@ jobs:
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
|
||||
build: |-
|
||||
set -e &&
|
||||
npm i -g pnpm@8.15.7 --force &&
|
||||
pnpm --version &&
|
||||
pnpm install --frozen-lockfile &&
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=aarch64-unknown-linux-gnu
|
||||
pnpm nx run-many --target=build-native -- --target=aarch64-unknown-linux-gnu
|
||||
- host: ubuntu-latest
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
setup: |
|
||||
@@ -191,30 +74,24 @@ jobs:
|
||||
build: |-
|
||||
set -e &&
|
||||
rustup target add aarch64-unknown-linux-musl &&
|
||||
npm i -g pnpm@8.15.7 --force &&
|
||||
pnpm --version &&
|
||||
pnpm install --frozen-lockfile &&
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=aarch64-unknown-linux-musl
|
||||
pnpm nx run-many --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
|
||||
runs-on: ${{ matrix.settings.host }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
version: 8.2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
if: ${{ !matrix.settings.docker }}
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: 18
|
||||
check-latest: true
|
||||
cache: 'pnpm'
|
||||
|
||||
@@ -225,7 +102,7 @@ jobs:
|
||||
targets: ${{ matrix.settings.target }}
|
||||
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index/
|
||||
@@ -234,36 +111,29 @@ jobs:
|
||||
.cargo-cache
|
||||
target/
|
||||
key: ${{ matrix.settings.target }}-cargo-registry
|
||||
|
||||
- uses: goto-bus-stop/setup-zig@v2
|
||||
if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' }}
|
||||
with:
|
||||
version: 0.10.0
|
||||
|
||||
- name: Setup toolchain
|
||||
run: ${{ matrix.settings.setup }}
|
||||
if: ${{ matrix.settings.setup }}
|
||||
shell: bash
|
||||
|
||||
- name: Setup node x86
|
||||
if: matrix.settings.target == 'i686-pc-windows-msvc'
|
||||
run: yarn config set supportedArchitectures.cpu "ia32"
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies
|
||||
if: ${{ !matrix.settings.docker }}
|
||||
run: pnpm install --frozen-lockfile
|
||||
timeout-minutes: 30
|
||||
|
||||
- name: Setup node x86
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
if: matrix.settings.target == 'i686-pc-windows-msvc'
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: 18
|
||||
check-latest: true
|
||||
cache: pnpm
|
||||
architecture: x86
|
||||
|
||||
- name: Build in docker
|
||||
uses: addnab/docker-run-action@v3
|
||||
if: ${{ matrix.settings.docker }}
|
||||
@@ -271,189 +141,98 @@ jobs:
|
||||
image: ${{ matrix.settings.docker }}
|
||||
options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build
|
||||
run: ${{ matrix.settings.build }}
|
||||
|
||||
- name: Build
|
||||
run: ${{ matrix.settings.build }}
|
||||
if: ${{ !matrix.settings.docker }}
|
||||
shell: bash
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bindings-${{ matrix.settings.target }}
|
||||
path: packages/**/*.node
|
||||
if-no-files-found: error
|
||||
|
||||
build-freebsd:
|
||||
needs: [resolve-required-data]
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
runs-on: macos-13-large
|
||||
name: Build FreeBSD
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
if: ${{ github.event_name != 'schedule' && !github.event.inputs.pr }}
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref }}
|
||||
|
||||
- name: Build
|
||||
id: build
|
||||
if: ${{ github.event_name != 'schedule' && !github.event.inputs.pr }}
|
||||
uses: cross-platform-actions/action@v0.22.0
|
||||
env:
|
||||
DEBUG: napi:*
|
||||
RUSTUP_IO_THREADS: 1
|
||||
NX_PREFER_TS_NODE: true
|
||||
PLAYWRIGHT_BROWSERS_PATH: 0
|
||||
with:
|
||||
operating_system: freebsd
|
||||
version: '13.2'
|
||||
architecture: x86-64
|
||||
environment_variables: DEBUG RUSTUP_IO_THREADS CI NX_PREFER_TS_NODE PLAYWRIGHT_BROWSERS_PATH
|
||||
shell: bash
|
||||
run: |
|
||||
env
|
||||
whoami
|
||||
sudo pkg install -y -f node libnghttp2 npm git
|
||||
sudo npm install --location=global --ignore-scripts pnpm@8.15.7
|
||||
curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
sh rustup.sh -y --profile minimal --default-toolchain stable
|
||||
source "$HOME/.cargo/env"
|
||||
echo "~~~~ rustc --version ~~~~"
|
||||
rustc --version
|
||||
echo "~~~~ node -v ~~~~"
|
||||
node -v
|
||||
echo "~~~~ pnpm --version ~~~~"
|
||||
pnpm --version
|
||||
pwd
|
||||
ls -lah
|
||||
whoami
|
||||
env
|
||||
freebsd-version
|
||||
mkdir -p /Users/runner/work/_temp/_github_workflow
|
||||
echo "{}" > /Users/runner/work/_temp/_github_workflow/event.json
|
||||
pnpm install --frozen-lockfile --ignore-scripts
|
||||
pnpm nx run-many --verbose --outputStyle stream --target=build-native -- --target=x86_64-unknown-freebsd
|
||||
pnpm nx reset
|
||||
rm -rf node_modules
|
||||
rm -rf dist
|
||||
echo "KILL ALL NODE PROCESSES"
|
||||
killall node || true
|
||||
echo "COMPLETE"
|
||||
|
||||
- name: Upload artifact
|
||||
if: ${{ github.event_name != 'schedule' && !github.event.inputs.pr }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: bindings-freebsd
|
||||
path: packages/**/*.node
|
||||
if-no-files-found: error
|
||||
|
||||
runs-on: macos-12
|
||||
name: Build FreeBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
id: build
|
||||
uses: vmactions/freebsd-vm@v0
|
||||
env:
|
||||
DEBUG: napi:*
|
||||
RUSTUP_HOME: /usr/local/rustup
|
||||
CARGO_HOME: /usr/local/cargo
|
||||
RUSTUP_IO_THREADS: 1
|
||||
with:
|
||||
envs: DEBUG RUSTUP_HOME CARGO_HOME RUSTUP_IO_THREADS
|
||||
usesh: true
|
||||
mem: 4096
|
||||
prepare: |
|
||||
pkg install -y -f curl node libnghttp2 npm
|
||||
npm install --location=global --ignore-scripts pnpm
|
||||
curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
sh rustup.sh -y --profile minimal --default-toolchain stable
|
||||
export PATH="/usr/local/cargo/bin:$PATH"
|
||||
echo "~~~~ rustc --version ~~~~"
|
||||
rustc --version
|
||||
echo "~~~~ node -v ~~~~"
|
||||
node -v
|
||||
echo "~~~~ pnpm --version ~~~~"
|
||||
pnpm --version
|
||||
run: |
|
||||
export PATH="/usr/local/cargo/bin:$PATH"
|
||||
pwd
|
||||
ls -lah
|
||||
whoami
|
||||
env
|
||||
freebsd-version
|
||||
mkdir -p /Users/runner/work/_temp/_github_workflow
|
||||
echo "{}" > /Users/runner/work/_temp/_github_workflow/event.json
|
||||
pnpm install --frozen-lockfile --ignore-scripts
|
||||
pnpm nx run-many --target=build-native -- --target=x86_64-unknown-freebsd
|
||||
rm -rf node_modules
|
||||
rm -rf dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bindings-freebsd
|
||||
path: packages/**/*.node
|
||||
if-no-files-found: error
|
||||
publish:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
name: Publish
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
needs:
|
||||
- resolve-required-data
|
||||
- build-freebsd
|
||||
- build
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
repository: ${{ needs.resolve-required-data.outputs.repo }}
|
||||
ref: ${{ needs.resolve-required-data.outputs.ref }}
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
version: 8.2
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version: 18
|
||||
check-latest: true
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
# This command will appropriately fail if no artifacts are available
|
||||
- name: List artifacts
|
||||
run: ls -R artifacts
|
||||
shell: bash
|
||||
|
||||
- name: Publish
|
||||
env:
|
||||
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 }}
|
||||
run: |
|
||||
echo ""
|
||||
# Create and check out the publish branch
|
||||
git checkout -b $PUBLISH_BRANCH
|
||||
echo ""
|
||||
echo "Version set to: $VERSION"
|
||||
echo "DRY_RUN set to: $DRY_RUN"
|
||||
echo ""
|
||||
pnpm nx-release --local=false $VERSION $DRY_RUN
|
||||
|
||||
- name: (Stable Release Only) Trigger Docs Release
|
||||
# Publish docs only on a full release
|
||||
if: ${{ !github.event.release.prerelease && github.event_name == 'release' }}
|
||||
run: npx ts-node ./scripts/release-docs.ts
|
||||
|
||||
- name: (PR Release Only) Create comment for successful PR release
|
||||
if: success() && github.event.inputs.pr
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SUCCESS_COMMENT: ${{ needs.resolve-required-data.outputs.success_comment }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const successComment = JSON.parse(process.env.SUCCESS_COMMENT);
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: ${{ github.event.inputs.pr }},
|
||||
body: successComment
|
||||
});
|
||||
|
||||
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
|
||||
steps:
|
||||
- name: Create comment for failed PR release
|
||||
uses: actions/github-script@v7
|
||||
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 }}.
|
||||
See the failed workflow run at: https://github.com/nrwl/nx/actions/runs/${{ github.run_id }}
|
||||
`;
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: ${{ github.event.inputs.pr }},
|
||||
body: message
|
||||
});
|
||||
git checkout -b publish/$GITHUB_REF_NAME
|
||||
npm config set //registry.npmjs.org/:_authToken=$NPM_TOKEN
|
||||
pnpm nx-release --local=false $GITHUB_REF_NAME
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
|
||||
name: Stale Bot workflow
|
||||
|
||||
permissions: { }
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -16,96 +16,163 @@ jobs:
|
||||
name: stale
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# This handles issues that need more info
|
||||
- name: stale-more-info-needed
|
||||
id: stale-more-info-needed
|
||||
uses: actions/stale@v9.0.0
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 7
|
||||
days-before-close: 21
|
||||
days-before-stale: 14
|
||||
days-before-close: 14
|
||||
stale-issue-label: "stale"
|
||||
operations-per-run: 300
|
||||
remove-stale-when-updated: true
|
||||
only-labels: "blocked: more info needed"
|
||||
stale-issue-message: |
|
||||
This issue has been automatically marked as stale because more information has not been provided within 7 days.
|
||||
It will be closed in 21 days if no information is provided.
|
||||
If information has been provided, please reply to keep it active.
|
||||
This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs.
|
||||
If we missed this issue please reply to keep it active.
|
||||
Thanks for being a part of the Nx community! 🙏
|
||||
|
||||
# This handles PRs that need to be rebased
|
||||
- name: stale-needs-rebase
|
||||
id: stale-needs-rebase
|
||||
uses: actions/stale@v9.0.0
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 7
|
||||
days-before-close: 21
|
||||
days-before-stale: 14
|
||||
days-before-close: 14
|
||||
stale-issue-label: "stale"
|
||||
operations-per-run: 300
|
||||
remove-stale-when-updated: true
|
||||
only-labels: "blocked: needs rebased"
|
||||
stale-issue-message: |
|
||||
This PR has been automatically marked as stale because it has not been rebased in 7 days.
|
||||
It will be closed in 21 days if it is not rebased.
|
||||
If the PR has been rebased or you are working on rebasing it, please reply to keep it active.
|
||||
If you do not have time, please let us know and we can rebase it.
|
||||
This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs.
|
||||
If we missed this issue please reply to keep it active.
|
||||
Thanks for being a part of the Nx community! 🙏
|
||||
|
||||
# This handles issues that do not have a repro
|
||||
- name: stale-repro-needed
|
||||
id: stale-repro-needed
|
||||
uses: actions/stale@v9.0.0
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 7
|
||||
days-before-close: 21
|
||||
days-before-stale: 14
|
||||
days-before-close: 14
|
||||
stale-issue-label: "stale"
|
||||
operations-per-run: 300
|
||||
remove-stale-when-updated: true
|
||||
only-labels: "blocked: repro needed"
|
||||
stale-issue-message: |
|
||||
This issue has been automatically marked as stale because no reproduction was provided within 7 days.
|
||||
Please help us help you. Providing a repository exhibiting the issue helps us diagnose and fix the issue.
|
||||
Any time that we spend reproducing this issue is time taken away from addressing this issue and other issues.
|
||||
This issue will be closed in 21 days if a reproduction is not provided.
|
||||
If a reproduction has been provided, please reply to keep it active.
|
||||
This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs.
|
||||
If we missed this issue please reply to keep it active.
|
||||
Thanks for being a part of the Nx community! 🙏
|
||||
|
||||
- name: stale-retry-with-latest
|
||||
id: stale-retry-with-latest
|
||||
uses: actions/stale@v9.0.0
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 7
|
||||
days-before-close: 21
|
||||
days-before-stale: 14
|
||||
days-before-close: 14
|
||||
stale-issue-label: "stale"
|
||||
operations-per-run: 300
|
||||
remove-stale-when-updated: true
|
||||
only-labels: "blocked: retry with latest"
|
||||
stale-issue-message: |
|
||||
This issue has been automatically marked as stale because no results of retrying on the latest version of Nx was provided within 7 days.
|
||||
It will be closed in 21 days if no results are provided.
|
||||
If the issue is still present, please reply to keep it active.
|
||||
If the issue was not present, please close this issue.
|
||||
This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs.
|
||||
If we missed this issue please reply to keep it active.
|
||||
Thanks for being a part of the Nx community! 🙏
|
||||
|
||||
|
||||
# This handles issues are really old and were made with a previous major
|
||||
- name: stale-bug
|
||||
id: stale-bug
|
||||
uses: actions/stale@v9.0.0
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 180
|
||||
days-before-close: 21
|
||||
days-before-close: 14
|
||||
stale-issue-label: "stale"
|
||||
operations-per-run: 300
|
||||
remove-stale-when-updated: true
|
||||
only-labels: "type: bug"
|
||||
stale-issue-message: |
|
||||
This issue has been automatically marked as stale because it hasn't had any activity for 6 months.
|
||||
Many things may have changed within this time. The issue may have already been fixed or it may not be relevant anymore.
|
||||
If at this point, this is still an issue, please respond with updated information.
|
||||
It will be closed in 21 days if no further activity occurs.
|
||||
This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs.
|
||||
If we missed this issue please reply to keep it active.
|
||||
Thanks for being a part of the Nx community! 🙏
|
||||
|
||||
- name: stale-cleanup
|
||||
id: stale-cleanup
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 180
|
||||
days-before-close: 14
|
||||
stale-issue-label: "stale"
|
||||
operations-per-run: 300
|
||||
remove-stale-when-updated: true
|
||||
only-labels: "type: cleanup"
|
||||
stale-issue-message: |
|
||||
This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs.
|
||||
If we missed this issue please reply to keep it active.
|
||||
Thanks for being a part of the Nx community! 🙏
|
||||
|
||||
- name: stale-docs
|
||||
id: stale-docs
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 180
|
||||
days-before-close: 14
|
||||
stale-issue-label: "stale"
|
||||
operations-per-run: 300
|
||||
remove-stale-when-updated: true
|
||||
only-labels: "type: docs"
|
||||
stale-issue-message: |
|
||||
This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs.
|
||||
If we missed this issue please reply to keep it active.
|
||||
Thanks for being a part of the Nx community! 🙏
|
||||
|
||||
- name: stale-enhancement
|
||||
id: stale-enhancement
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 250
|
||||
days-before-close: 14
|
||||
stale-issue-label: "stale"
|
||||
operations-per-run: 300
|
||||
remove-stale-when-updated: true
|
||||
only-labels: "type: enhancement"
|
||||
stale-issue-message: |
|
||||
This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs.
|
||||
If we missed this issue please reply to keep it active.
|
||||
Thanks for being a part of the Nx community! 🙏
|
||||
|
||||
- name: stale-feature
|
||||
id: stale-feature
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 250
|
||||
days-before-close: 14
|
||||
stale-issue-label: "stale"
|
||||
operations-per-run: 300
|
||||
remove-stale-when-updated: true
|
||||
only-labels: "type: feature"
|
||||
stale-issue-message: |
|
||||
This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs.
|
||||
If we missed this issue please reply to keep it active.
|
||||
Thanks for being a part of the Nx community! 🙏
|
||||
|
||||
- name: stale-question
|
||||
id: stale-question
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 45
|
||||
days-before-close: 14
|
||||
stale-issue-label: "stale"
|
||||
operations-per-run: 300
|
||||
remove-stale-when-updated: true
|
||||
only-labels: "type: question / discussion"
|
||||
stale-issue-message: |
|
||||
This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs.
|
||||
If we missed this issue please reply to keep it active.
|
||||
Thanks for being a part of the Nx community! 🙏
|
||||
|
||||
@@ -12,47 +12,25 @@ tmp
|
||||
jest.debug.config.js
|
||||
.tool-versions
|
||||
/.nx-cache
|
||||
/.nx
|
||||
/.verdaccio/build/local-registry
|
||||
/graph/client/src/assets/environment.js
|
||||
/graph/client/src/assets/dev/environment.js
|
||||
/graph/client/src/assets/generated-project-graphs
|
||||
/graph/client/src/assets/generated-task-graphs
|
||||
/graph/client/src/assets/generated-task-inputs
|
||||
/graph/client/src/assets/generated-source-maps
|
||||
/nx-dev/nx-dev/public/documentation
|
||||
/nx-dev/nx-dev/public/images/open-graph
|
||||
|
||||
# Issues scraper creates these files, stored by github's cache
|
||||
/scripts/issues-scraper/cached
|
||||
|
||||
# We don't commit a CHANELGELOG.md file to the repo, we only create Github releases
|
||||
# Lerna creates this
|
||||
CHANGELOG.md
|
||||
|
||||
# Next.js
|
||||
.next
|
||||
out
|
||||
|
||||
# Angular Cache
|
||||
.angular
|
||||
|
||||
# Local dev files
|
||||
.env.local
|
||||
.env
|
||||
.bashrc
|
||||
.nx
|
||||
|
||||
*.node
|
||||
|
||||
# Fix for issue when working on the repo in a dev container
|
||||
.pnpm-store
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
|
||||
.cargo/.package-cache
|
||||
.cargo/bin/
|
||||
.cargo/env
|
||||
.cargo/registry/
|
||||
.local/
|
||||
.npm/
|
||||
.profile
|
||||
.rustup/
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
launch-templates:
|
||||
linux-medium:
|
||||
resource-class: 'docker_linux_amd64/medium+'
|
||||
image: 'ubuntu22.04-node20.11-v3'
|
||||
env:
|
||||
GIT_AUTHOR_EMAIL: test@test.com
|
||||
GIT_AUTHOR_NAME: Test
|
||||
GIT_COMMITTER_EMAIL: test@test.com
|
||||
GIT_COMMITTER_NAME: Test
|
||||
SELECTED_PM: 'pnpm'
|
||||
NPM_CONFIG_PREFIX: '/home/workflows/.npm-global'
|
||||
init-steps:
|
||||
- name: Checkout
|
||||
uses: 'nrwl/nx-cloud-workflows/v3.6/workflow-steps/checkout/main.yaml'
|
||||
- name: Cache restore
|
||||
uses: 'nrwl/nx-cloud-workflows/v3.6/workflow-steps/cache/main.yaml'
|
||||
env:
|
||||
KEY: 'pnpm-lock.yaml'
|
||||
PATHS: |
|
||||
node_modules
|
||||
~/.cache/Cypress
|
||||
~/.cache/ms-playwright
|
||||
~/.pnpm-store
|
||||
BASE_BRANCH: 'master'
|
||||
- name: Install e2e deps
|
||||
script: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ca-certificates lsof libvips-dev libglib2.0-dev libgirepository1.0-dev
|
||||
- name: Install Pnpm
|
||||
script: |
|
||||
npm install -g pnpm@8
|
||||
|
||||
- name: Pnpm Install
|
||||
script: |
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
- name: Install Browsers
|
||||
script: |
|
||||
pnpm exec cypress install
|
||||
pnpm exec playwright install
|
||||
|
||||
- name: Install Rust
|
||||
script: |
|
||||
curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
source "$HOME/.cargo/env"
|
||||
rustup toolchain install 1.70.0
|
||||
|
||||
- name: Configure git metadata (needed for lerna smoke tests)
|
||||
script: |
|
||||
git config --global user.email test@test.com
|
||||
git config --global user.name "Test Test"
|
||||
|
||||
- name: Load Cargo Env
|
||||
script: echo "PATH=$HOME/.cargo/bin:$PATH" >> $NX_CLOUD_ENV
|
||||
|
||||
- name: Install zip and unzip
|
||||
script: sudo apt-get -yqq install zip unzip
|
||||
@@ -1,4 +0,0 @@
|
||||
distribute-on:
|
||||
small-changeset: 8 linux-medium
|
||||
medium-changeset: 10 linux-medium
|
||||
large-changeset: 12 linux-medium
|
||||
@@ -16,9 +16,7 @@ packages/jest/src/schematics/**/files/**/*.json
|
||||
packages/nx/src/plugins/js/lock-file/__fixtures__/**/*.*
|
||||
packages/**/schematics/**/files/**/*.html
|
||||
packages/**/generators/**/files/**/*.html
|
||||
packages/nx/src/native/**/*.rs
|
||||
packages/nx/src/native/native-bindings.js
|
||||
packages/nx/src/native/index.d.ts
|
||||
packages/nx/src/native/
|
||||
nx-dev/nx-dev/.next/
|
||||
nx-dev/nx-dev/public/documentation
|
||||
graph/client/src/assets/environment.js
|
||||
@@ -36,9 +34,3 @@ graph/client/src/assets/generated-task-graphs
|
||||
/dist
|
||||
/.env
|
||||
CODEOWNERS
|
||||
|
||||
/.nx/cache
|
||||
|
||||
.pnpm-store
|
||||
|
||||
/.nx/workspace-data
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"endOfLine": "lf",
|
||||
"plugins": ["prettier-plugin-tailwindcss"]
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
|
||||
@@ -10,13 +10,9 @@ uplinks:
|
||||
npmjs:
|
||||
url: https://registry.npmjs.org/
|
||||
maxage: 60m
|
||||
max_fails: 20
|
||||
fail_timeout: 2m
|
||||
yarn:
|
||||
url: https://registry.yarnpkg.com
|
||||
maxage: 60m
|
||||
max_fails: 20
|
||||
fail_timeout: 2m
|
||||
|
||||
packages:
|
||||
'@*/*':
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
/e2e/**/* @FrozenPandaz @vsavkin @AgentEnder @jaysoo @JamesHenry
|
||||
/scripts/**/* @FrozenPandaz @vsavkin @AgentEnder @jaysoo @JamesHenry
|
||||
/tools/**/* @FrozenPandaz @vsavkin @AgentEnder @jaysoo @JamesHenry
|
||||
package.json @nrwl/nx-core-reviewers
|
||||
pnpm-lock.yaml @nrwl/nx-core-reviewers
|
||||
rust-toolchain @nrwl/nx-native-reviewers
|
||||
package.json @nrwl/nx-pipelines-reviewers
|
||||
pnpm-lock.yaml @nrwl/nx-pipelines-reviewers
|
||||
|
||||
# Docs Site + Graph
|
||||
/docs @nrwl/nx-docs-reviewers
|
||||
/docs/nx-cloud @StalkAltan @rarmatei @nrwl/nx-docs-reviewers
|
||||
/graph/** @philipjfulcher @FrozenPandaz @bcabanes @MaxKless @xiongemi
|
||||
/graph/** @philipjfulcher @FrozenPandaz @bcabanes
|
||||
/images @nrwl/nx-docs-reviewers
|
||||
/nx-dev/** @nrwl/nx-docs-reviewers
|
||||
/typedoc-theme @nrwl/nx-docs-reviewers
|
||||
@@ -22,7 +21,8 @@ rust-toolchain @nrwl/nx-native-reviewers
|
||||
/docs/generated/packages/angular/** @nrwl/nx-angular-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/angular/** @nrwl/nx-angular-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/angular/** @nrwl/nx-angular-reviewers
|
||||
/e2e/angular/** @nrwl/nx-angular-reviewers
|
||||
/e2e/angular-core/** @nrwl/nx-angular-reviewers
|
||||
/e2e/angular-extensions/** @nrwl/nx-angular-reviewers
|
||||
/packages/angular/plugins/component-testing.ts @nrwl/nx-angular-reviewers @nrwl/nx-testing-tools-reviewers
|
||||
/packages/angular/src/generators/cypress-component-configuration/** @nrwl/nx-angular-reviewers @nrwl/nx-testing-tools-reviewers
|
||||
/packages/angular/src/generators/component-test/** @nrwl/nx-angular-reviewers @nrwl/nx-testing-tools-reviewers
|
||||
@@ -33,7 +33,8 @@ rust-toolchain @nrwl/nx-native-reviewers
|
||||
/docs/shared/packages/react/** @nrwl/nx-react-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/next/** @nrwl/nx-react-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/react/** @nrwl/nx-react-reviewers
|
||||
/e2e/react/** @nrwl/nx-react-reviewers
|
||||
/e2e/react-core/** @nrwl/nx-react-reviewers
|
||||
/e2e/react-extensions/** @nrwl/nx-react-reviewers
|
||||
/packages/next/** @nrwl/nx-react-reviewers
|
||||
/e2e/next/** @nrwl/nx-react-reviewers
|
||||
/packages/react/plugins/component-testing/** @nrwl/nx-react-reviewers @nrwl/nx-testing-tools-reviewers
|
||||
@@ -52,17 +53,6 @@ rust-toolchain @nrwl/nx-native-reviewers
|
||||
/packages/react-native/** @nrwl/nx-react-reviewers
|
||||
/e2e/react-native/** @nrwl/nx-react-reviewers
|
||||
|
||||
## remix
|
||||
/docs/generated/packages/remix/** @nrwl/nx-react-reviewers @nrwl/nx-docs-reviewers @Coly010
|
||||
/packages/remix/** @nrwl/nx-react-reviewers @Coly010
|
||||
/e2e/remix/** @nrwl/nx-react-reviewers @Coly010
|
||||
|
||||
# Vue
|
||||
/packages/vue/** @nrwl/nx-vue-reviewers
|
||||
/e2e/vue/** @nrwl/nx-vue-reviewers
|
||||
/packages/nuxt/** @nrwl/nx-vue-reviewers
|
||||
/e2e/nuxt/** @nrwl/nx-vue-reviewers
|
||||
|
||||
## Node
|
||||
/docs/generated/packages/node/** @nrwl/nx-node-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/nest/** @nrwl/nx-node-reviewers @nrwl/nx-docs-reviewers
|
||||
@@ -92,7 +82,6 @@ rust-toolchain @nrwl/nx-native-reviewers
|
||||
/packages/web/** @nrwl/nx-js-reviewers
|
||||
/e2e/web/** @nrwl/nx-js-reviewers
|
||||
/packages/webpack/** @nrwl/nx-js-reviewers
|
||||
/packages/webpack/src/utils/module-federation @jaysoo @Coly010
|
||||
/e2e/webpack/** @nrwl/nx-js-reviewers
|
||||
/packages/esbuild/** @nrwl/nx-js-reviewers
|
||||
/e2e/esbuild/** @nrwl/nx-js-reviewers
|
||||
@@ -103,25 +92,21 @@ rust-toolchain @nrwl/nx-native-reviewers
|
||||
|
||||
## Tools
|
||||
/docs/generated/packages/cypress/** @nrwl/nx-testing-tools-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/cypress/** @nrwl/nx-testing-tools-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/jest/** @nrwl/nx-testing-tools-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/jest/** @nrwl/nx-testing-tools-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/playwright/** @nrwl/nx-testing-tools-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/playwright/** @nrwl/nx-testing-tools-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/cypress/** @nrwl/nx-testing-tools-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/cypress/** @nrwl/nx-testing-tools-reviewers
|
||||
/e2e/cypress/** @nrwl/nx-testing-tools-reviewers
|
||||
/packages/jest/** @nrwl/nx-testing-tools-reviewers
|
||||
/e2e/jest/** @nrwl/nx-testing-tools-reviewers
|
||||
/packages/playwright/** @nrwl/nx-testing-tools-reviewers
|
||||
/e2e/playwright/** @nrwl/nx-testing-tools-reviewers
|
||||
|
||||
# Linter
|
||||
/docs/generated/packages/eslint-plugin/** @nrwl/nx-linter-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/eslint/** @nrwl/nx-linter-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/eslint/** @nrwl/nx-linter-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/linter/** @nrwl/nx-linter-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/linter/** @nrwl/nx-linter-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/eslint-plugin/** @nrwl/nx-linter-reviewers
|
||||
/packages/eslint/** @nrwl/nx-linter-reviewers
|
||||
/e2e/eslint/** @nrwl/nx-linter-reviewers
|
||||
/packages/linter/** @nrwl/nx-linter-reviewers
|
||||
/e2e/linter/** @nrwl/nx-linter-reviewers
|
||||
.eslint* @nrwl/nx-linter-reviewers
|
||||
|
||||
# Storybook
|
||||
@@ -129,17 +114,17 @@ rust-toolchain @nrwl/nx-native-reviewers
|
||||
/docs/shared/packages/storybook/** @nrwl/nx-storybook-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/storybook/** @nrwl/nx-storybook-reviewers
|
||||
/e2e/storybook/** @nrwl/nx-storybook-reviewers
|
||||
/e2e/storybook-angular/** @nrwl/nx-storybook-reviewers
|
||||
|
||||
## Devkit
|
||||
/docs/generated/devkit/** @nrwl/nx-devkit-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/devkit/** @nrwl/nx-devkit-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/devkit/** @nrwl/nx-devkit-reviewers
|
||||
/packages/devkit/index.ts @FrozenPandaz @vsavkin
|
||||
/packages/devkit/index.js @FrozenPandaz @vsavkin
|
||||
/packages/devkit/index.d.ts @FrozenPandaz @vsavkin
|
||||
/packages/devkit/public-api.ts @FrozenPandaz @vsavkin
|
||||
|
||||
# Gradle
|
||||
/packages/gradle/** @FrozenPandaz @xiongemi
|
||||
/e2e/gradle/** @FrozenPandaz @xiongemi
|
||||
/packages/devkit/nx.ts @FrozenPandaz @vsavkin
|
||||
/packages/devkit/src/utils/module-federation @jaysoo @Coly010
|
||||
|
||||
# Nx-Plugin
|
||||
/docs/generated/packages/plugin/** @nrwl/nx-devkit-reviewers @nrwl/nx-docs-reviewers
|
||||
@@ -163,23 +148,23 @@ rust-toolchain @nrwl/nx-native-reviewers
|
||||
/e2e/nx*/** @nrwl/nx-core-reviewers
|
||||
/packages/workspace/** @nrwl/nx-core-reviewers
|
||||
/e2e/workspace-create/** @nrwl/nx-core-reviewers
|
||||
/e2e/release/** @nrwl/nx-core-reviewers
|
||||
/e2e/workspace-create-npm/** @nrwl/nx-core-reviewers
|
||||
|
||||
# Misc
|
||||
/e2e/lerna-smoke-tests/** @vsavkin @JamesHenry
|
||||
/e2e/utils/** @meeroslav @nrwl/nx-testing-tools-reviewers @vsavkin @mandarini
|
||||
/community @nrwl/nx-docs-reviewers
|
||||
/community @nrwl/nx-devkit-reviewers
|
||||
/CONTRIBUTING.md @FrozenPandaz @isaacplmann
|
||||
/CODE_OF_CONDUCT.md @FrozenPandaz @isaacplmann
|
||||
/CODEOWNERS @FrozenPandaz @AgentEnder
|
||||
|
||||
# Scripts
|
||||
/scripts/depcheck @FrozenPandaz @vsavkin @jaysoo
|
||||
/scripts/documentation @nrwl/nx-docs-reviewers
|
||||
/scripts/angular-support-upgrades @nrwl/nx-angular-reviewers
|
||||
|
||||
# CI
|
||||
/.circleci/** @nrwl/nx-pipelines-reviewers
|
||||
/.nx/workflows/** @nrwl/nx-pipelines-reviewers
|
||||
/.github/** @nrwl/nx-pipelines-reviewers
|
||||
/.husky/** @nrwl/nx-pipelines-reviewers
|
||||
/packages/workspace/src/generators/ci-workflow/** @nrwl/nx-pipelines-reviewers
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
As contributors and maintainers of the Nx project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
|
||||
|
||||
Communication through any of Nx's channels (GitHub, Gitter, Discord, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||
Communication through any of Nx's channels (GitHub, Gitter, Slack, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||
|
||||
We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Nx project to do the same.
|
||||
|
||||
|
||||
@@ -76,13 +76,13 @@ Check out [this video for a live walkthrough](https://youtu.be/Tx257WpNsxc) or f
|
||||
- Run `pnpm local-registry` in Terminal 1 (keep it running)
|
||||
- Run `npm adduser --registry http://localhost:4873` in Terminal 2 (real credentials are not required, you just need to
|
||||
be logged in. You can use test/test/test@test.io.)
|
||||
- Run `pnpm nx-release 20.0.0 --local` in Terminal 2 - you can choose any nonexistent version number here, but it's recommended to use the next major
|
||||
- Run `pnpm nx-release 16.0.0 --local` in Terminal 2 - you can choose any nonexistent version number here, but it's recommended to use the next major
|
||||
- Run `cd ./tmp` in Terminal 2
|
||||
- Run `npx create-nx-workspace@20.0.0` in Terminal 2
|
||||
- Run `npx create-nx-workspace@17.0.0` in Terminal 2
|
||||
|
||||
If you have problems publishing, make sure you use Node 18 and NPM 8.
|
||||
|
||||
**NOTE:** To use this newly published local version, you need to make a new workspace, run `nx migrate` or change all of your target packages to this new version, eg: `"nx": "^20.0.0",` and re-run `pnpm i` in your testing project.
|
||||
**NOTE:** To use this newly published local version, you need to make a new workspace or change all of your target packages to this new version, eg: `"nx": "^16.0.0",` and re-run `pnpm i` in your testing project.
|
||||
|
||||
### Publishing for Yarn 2+ (Berry)
|
||||
|
||||
@@ -112,7 +112,9 @@ Yarn Berry operates slightly differently than Yarn Classic. In order to publish
|
||||
- localhost
|
||||
```
|
||||
|
||||
- Run `pnpm nx-release minor --local` in Terminal 2 to publish next minor version. The output will report the version of published packages.
|
||||
- Run `pnpm nx-release --local` in Terminal 2 to publish next minor version. If this version already exists, you can
|
||||
bump the minor version in `lerna.json` to toggle the next minor. The output will report the version of published
|
||||
packages.
|
||||
- Go to your target folder (e.g. `cd ./tmp`) in Terminal 2
|
||||
- Run `yarn dlx create-nx-workspace@123.4.5` in Terminal 2 (replace `123.4.5` with the version that got published).
|
||||
|
||||
@@ -177,8 +179,6 @@ The `docs/map.json` file is considered our source of truth for our site's struct
|
||||
new page to our documentation to ensure that it is included in the documentation site. We also run automated scripts
|
||||
based on this `map.json` data to safeguard against common human errors that could break our site.
|
||||
|
||||
When you make a change to the `map.json` file, make sure to run `pnpm documentation` to propagate your changes to the `nx-dev` application.
|
||||
|
||||
#### Nx-Dev Application
|
||||
|
||||
Our public `nx.dev` documentation site is a [Next.js](https://nextjs.org/) application, that can be found in
|
||||
@@ -211,10 +211,10 @@ adjusting the docs.
|
||||
To run `nx-dev` locally, run the command:
|
||||
|
||||
```bash
|
||||
npx nx serve-docs nx-dev
|
||||
npx nx serve nx-dev
|
||||
```
|
||||
|
||||
You can then access the application locally at `localhost:4200`. Changes to markdown documentation files will be automatically applied to the site when you refresh the browser.
|
||||
You can then access the application locally at `localhost:4200`.
|
||||
|
||||
#### Troubleshooting: `JavaScript heap out of memory`
|
||||
|
||||
@@ -318,17 +318,15 @@ The scope must be one of the following:
|
||||
- nest - anything Nest specific
|
||||
- nextjs - anything Next specific
|
||||
- node - anything Node specific
|
||||
- nx-cloud - anything Nx Cloud specific
|
||||
- nx-cloud - anything NxCloud specific
|
||||
- nx-plugin - anything Nx Plugin specific
|
||||
- nx-dev - anything related to docs infrastructure
|
||||
- react - anything React specific
|
||||
- react-native - anything React Native specific
|
||||
- release - anything related to nx release
|
||||
- repo - anything related to managing the Nx repo itself
|
||||
- storybook - anything Storybook specific
|
||||
- testing - anything testing specific (e.g., Jest or Cypress)
|
||||
- vite - anything Vite specific
|
||||
- vue - anything Vue specific
|
||||
- web - anything Web specific
|
||||
- webpack - anything Webpack specific
|
||||
- misc - misc stuff
|
||||
@@ -345,7 +343,7 @@ Including the issue number that the PR relates to also helps with tracking.
|
||||
```plain
|
||||
feat(angular): add an option to generate lazy-loadable modules
|
||||
|
||||
`nx generate lib mylib --lazy` provisions the mylib project in .eslintrc.json
|
||||
`nx generate lib mylib --lazy` provisions the mylib project in tslint.json
|
||||
|
||||
Closes #157
|
||||
```
|
||||
@@ -355,7 +353,3 @@ Closes #157
|
||||
To simplify and automate the process of committing with this format,
|
||||
**Nx is a [Commitizen](https://github.com/commitizen/cz-cli) friendly repository**, just do `git add` and
|
||||
execute `pnpm commit`.
|
||||
|
||||
#### PR releases
|
||||
|
||||
If you are working on a particularly complex change or feature addition, you can request a dedicated Nx release for the associated pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate.
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
|
||||
[workspace]
|
||||
resolver = '2'
|
||||
members = [
|
||||
'packages/nx',
|
||||
'packages/nx'
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2017-2024 Narwhal Technologies Inc.
|
||||
Copyright (c) 2017-2023 Narwhal Technologies Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<p style="text-align: center;">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-dark.svg">
|
||||
<img alt="Nx - Smart Monorepos · Fast CI" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
|
||||
<img alt="Nx - Smart, Fast and Extensible Build System" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
@@ -13,15 +13,15 @@
|
||||
[]()
|
||||
[](http://commitizen.github.io/cz-cli/)
|
||||
[](https://gitter.im/nrwl-nx/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://go.nx.dev/community)
|
||||
[](https://go.nrwl.io/join-slack)
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
# Smart Monorepos · Fast CI
|
||||
# Smart, Fast and Extensible Build System
|
||||
|
||||
Nx is a build system with built-in tooling and advanced CI capabilities. It helps you maintain and scale monorepos, both locally and on CI.
|
||||
Nx is a next generation build system with first class monorepo support and powerful integrations.
|
||||
|
||||
A few links to help you get started:
|
||||
|
||||
@@ -32,11 +32,11 @@ A few links to help you get started:
|
||||
- [Blog Posts About Nx](https://blog.nrwl.io/nx/home)
|
||||
|
||||
<p style="text-align: center;"><a href="https://nx.dev/#learning-materials" target="_blank" rel="noreferrer"><img src="./images/nx-courses-and-videos.svg"
|
||||
width="100%" alt="Nx - Smart Monorepos · Fast CI"></a></p>
|
||||
width="100%" alt="Nx - Smart, Fast and Extensible Build System"></a></p>
|
||||
|
||||
# Engage with the Core Team and the Community
|
||||
|
||||
- [Nx.Dev Community Page: Community Discord Channel, Newsletter, etc.](https://nx.dev/community)
|
||||
- [Nx.Dev Community Page: Community Slack Channel, Newsletter, etc.](https://nx.dev/community)
|
||||
- [The Nx Show Playlist on YouTube](https://www.youtube.com/playlist?list=PLakNactNC1dE8KLQ5zd3fQwu_yQHjTmR5). It's a
|
||||
regular YouTube stream where we talk all things Nx. Join the stream, ask questions, etc.
|
||||
- [Follow Nx on Twitter](https://twitter.com/NxDevTools)
|
||||
@@ -73,7 +73,7 @@ help you get started.
|
||||
|  |  |  |  |
|
||||
| [xiongemi](https://github.com/xiongemi) | [meeroslav](https://github.com/meeroslav) | [leosvelperez](https://github.com/leosvelperez) | [ZackDeRose](https://github.com/ZackDeRose) |
|
||||
|
||||
| Craigory Coppola | Chau Tran | Nicholas Cunningham | Max Kless |
|
||||
| -------------------------------------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
||||
|  |  |  |  |
|
||||
| [AgentEnder](https://github.com/AgentEnder) | [nartc](https://github.com/nartc) | [ndcunningham](https://github.com/ndcunningham) | [MaxKless](https://github.com/MaxKless) |
|
||||
| Craigory Coppola | Chau Tran | Nicholas Cunningham |
|
||||
| -------------------------------------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
|  |  |  |
|
||||
| [AgentEnder](https://github.com/AgentEnder) | [nartc](https://github.com/nartc) | [ndcunningham](https://github.com/ndcunningham) |
|
||||
|
||||
@@ -25,40 +25,20 @@
|
||||
"url": "https://github.com/nxkit/nxkit"
|
||||
},
|
||||
{
|
||||
"name": "nx-plugin-vite",
|
||||
"description": "Nx plugin integrations with Vite.",
|
||||
"name": "nx-plugins",
|
||||
"description": "Nx plugin integrations with ESBuild / Vite / Snowpack / Prisma, with derived ESBuild / Snowpack / ... plugins.",
|
||||
"url": "https://nx-plugins.netlify.app/"
|
||||
},
|
||||
{
|
||||
"name": "nx-serverless-cdk",
|
||||
"description": "Create CDK applications and construct libraries. Test and debug infrastructure code and AWS Lambda functions locally.",
|
||||
"url": "https://github.com/castleadmin/nx-plugins/tree/main/nx-serverless-cdk/plugin"
|
||||
"name": "@codebrew/nx-aws-cdk",
|
||||
"description": "An Nx plugin for aws cdk develop.",
|
||||
"url": "https://github.com/codebrewlab/nx-plugins/tree/main/packages/nx-aws-cdk"
|
||||
},
|
||||
{
|
||||
"name": "@ago-dev/nx-aws-cdk-v2",
|
||||
"description": "An nx plugin for the aws-cdk v2.",
|
||||
"url": "https://github.com/adrian-goe/nx-aws-cdk-v2"
|
||||
},
|
||||
{
|
||||
"name": "@berenddeboer/nx-aws-cdk",
|
||||
"description": "Nx plugin to generate a CDK stack with support for the vitest runner. Supports all CDK commands.",
|
||||
"url": "https://github.com/berenddeboer/nx-plugins/tree/main/packages/nx-aws-cdk"
|
||||
},
|
||||
{
|
||||
"name": "@nx-iac/aws-cdk",
|
||||
"description": "Empowers your Nx workspace with AWS CDK capabilities ⚡",
|
||||
"url": "https://github.com/joelklint/nx-aws-cdk"
|
||||
},
|
||||
{
|
||||
"name": "@routineless/nx-aws-cdk",
|
||||
"description": "Nx plugin to generate and manage aws cdk app and lambdas.",
|
||||
"url": "https://github.com/KozelAnatoliy/routineless/tree/main/packages/nx-aws-cdk"
|
||||
},
|
||||
{
|
||||
"name": "@berenddeboer/nx-sst",
|
||||
"description": "Nx plugin to generate an SST stack and execute all SST commands.",
|
||||
"url": "https://github.com/berenddeboer/nx-plugins/tree/main/packages/nx-sst"
|
||||
},
|
||||
{
|
||||
"name": "@rxap/plugin-localazy",
|
||||
"description": "An Nx plugin for localazy.com upload and download tasks.",
|
||||
@@ -77,23 +57,33 @@
|
||||
{
|
||||
"name": "@nxext/ionic-react",
|
||||
"description": "An Nx plugin for developing Ionic React applications and libraries",
|
||||
"url": "https://github.com/nxext/nx-extensions-ionic/tree/main/packages/ionic-react"
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/main/packages/ionic-react"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/ionic-angular",
|
||||
"description": "An Nx plugin for developing Ionic Angular applications and libraries",
|
||||
"url": "https://github.com/nxext/nx-extensions-ionic/tree/main/packages/ionic-angular"
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/main/packages/ionic-angular"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/capacitor",
|
||||
"description": "An Nx plugin for developing cross-platform applications using Capacitor",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/main/packages/capacitor"
|
||||
},
|
||||
{
|
||||
"name": "@nxtend/firebase",
|
||||
"description": "An Nx plugin for developing applications using Firebase",
|
||||
"url": "https://github.com/nxtend-team/nxtend/tree/main/packages/firebase"
|
||||
},
|
||||
{
|
||||
"name": "@angular-architects/ddd",
|
||||
"description": "Nx plugin for structuring a monorepo with domains and layers",
|
||||
"url": "https://github.com/angular-architects/nx-ddd-plugin"
|
||||
},
|
||||
{
|
||||
"name": "@offeringsolutions/nx-karma-to-jest",
|
||||
"description": "Nx plugin for replacing karma with jest in an Nx workspace",
|
||||
"url": "https://github.com/FabianGosebrink/nx-karma-to-jest"
|
||||
},
|
||||
{
|
||||
"name": "@flowaccount/nx-serverless",
|
||||
"description": "Nx plugin for node/angular-universal schematics and deployment builders in an Nx workspace",
|
||||
@@ -114,6 +104,21 @@
|
||||
"description": "Nx plugin to run playwright e2e tests in an Nx workspace",
|
||||
"url": "https://github.com/Bielik20/nx-plugins/tree/master/packages/nx-playwright"
|
||||
},
|
||||
{
|
||||
"name": "@dev-thought/nx-deploy-it",
|
||||
"description": "Nx plugin to deploy applications on your favorite cloud provider",
|
||||
"url": "https://github.com/Dev-Thought/nx-plugins/tree/master/libs/nx-deploy-it"
|
||||
},
|
||||
{
|
||||
"name": "@offeringsolutions/nx-protractor-to-cypress",
|
||||
"description": "Nx plugin to replace protractor with cypress in an nx workspace",
|
||||
"url": "https://github.com/FabianGosebrink/nx-protractor-to-cypress"
|
||||
},
|
||||
{
|
||||
"name": "@angular-custom-builders/lite-serve",
|
||||
"description": "Nx plugin to run the e2e test on an existing dist folder",
|
||||
"url": "https://github.com/mauriziovitale/angular-plugins/tree/master/libs/lite-serve"
|
||||
},
|
||||
{
|
||||
"name": "@nx-plus/nuxt",
|
||||
"description": "Nx plugin adding first class support for Nuxt in your Nx workspace.",
|
||||
@@ -155,15 +160,20 @@
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/stencil"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/vue",
|
||||
"description": "Nx plugin to use VueJS 3 within nx workspaces",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/vue"
|
||||
"name": "@nxext/vite",
|
||||
"description": "Nx plugin to use ViteJS within nx workspaces",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/vite"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/solid",
|
||||
"description": "Nx plugin to use SolidJS within nx workspaces",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/solid"
|
||||
},
|
||||
{
|
||||
"name": "@joelcode/gcp-function",
|
||||
"description": "Nx plugin to generate, test, lint, build, serve, & deploy Google Cloud Function",
|
||||
"url": "https://github.com/JoelCode/gcp-function"
|
||||
},
|
||||
{
|
||||
"name": "@nx-go/nx-go",
|
||||
"description": "Nx plugin to use Go in a Nx workspace",
|
||||
@@ -215,7 +225,12 @@
|
||||
"url": "https://github.com/bikecoders/ngx-deploy-npm"
|
||||
},
|
||||
{
|
||||
"name": "@nx-dotnet/core",
|
||||
"name": "@trafilea/nx-shopify",
|
||||
"description": "Nx plugin for developing performance-first Shopify themes",
|
||||
"url": "https://github.com/trafilea/nx-shopify"
|
||||
},
|
||||
{
|
||||
"name": "nx-dotnet",
|
||||
"description": "Nx plugin for developing and housing .NET projects within an Nx workspace.",
|
||||
"url": "https://github.com/nx-dotnet/nx-dotnet"
|
||||
},
|
||||
@@ -289,26 +304,26 @@
|
||||
"description": "Nx plugin for deploying your resources with Pulumi",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/pulumi"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/react-email",
|
||||
"description": "Nx plugin for developing email templates with react.email",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/react-email"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/shadcn-ui",
|
||||
"description": "Nx plugin for working with shadcn/ui",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/shadcn-ui"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/docusaurus",
|
||||
"description": "Nx plugin adding first class support for Docusaurus in your Nx workspace.",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/docusaurus"
|
||||
},
|
||||
{
|
||||
"name": "@nativescript/nx",
|
||||
"description": "Nx Plugin adding first class support for NativeScript in your Nx workspace",
|
||||
"url": "https://github.com/nativescript/nx"
|
||||
},
|
||||
{
|
||||
"name": "@nx-clean/plugin-core",
|
||||
"description": "Nx Plugin to generate projects following Clean Architecture practices",
|
||||
"url": "https://github.com/guiseek/nx-clean/tree/main/libs/plugin/core"
|
||||
},
|
||||
{
|
||||
"name": "@jnxplus/nx-boot-gradle",
|
||||
"description": "Nx plugin to add Spring Boot and Gradle multi-project builds support to Nx workspace",
|
||||
"url": "https://github.com/khalilou88/jnxplus/tree/main/packages/nx-boot-gradle"
|
||||
},
|
||||
{
|
||||
"name": "@jnxplus/nx-boot-maven",
|
||||
"description": "Nx plugin to add Spring Boot and Maven multi-module project support to Nx workspace",
|
||||
"url": "https://github.com/khalilou88/jnxplus/tree/main/packages/nx-boot-maven"
|
||||
},
|
||||
{
|
||||
"name": "@nxtensions/astro",
|
||||
"description": "Nx plugin adding first class support for Astro (https://astro.build).",
|
||||
@@ -356,7 +371,7 @@
|
||||
},
|
||||
{
|
||||
"name": "@koliveira15/nx-sonarqube",
|
||||
"description": "Nx plugin that scans projects using SonarQube / SonarCloud.",
|
||||
"description": "Nx plugin that analyses projects using SonarQube / SonarCloud.",
|
||||
"url": "https://github.com/koliveira15/nx-sonarqube"
|
||||
},
|
||||
{
|
||||
@@ -424,6 +439,16 @@
|
||||
"description": "Adds size-limit support for Nx (performance budget tool for JavaScript)",
|
||||
"url": "https://github.com/LironHazan/nx-size-limit"
|
||||
},
|
||||
{
|
||||
"name": "@jnxplus/nx-quarkus-gradle",
|
||||
"description": "Nx plugin to add Quarkus and Gradle multi-project builds support to Nx workspace",
|
||||
"url": "https://github.com/khalilou88/jnxplus/tree/main/packages/nx-quarkus-gradle"
|
||||
},
|
||||
{
|
||||
"name": "@jnxplus/nx-quarkus-maven",
|
||||
"description": "Nx plugin to add Quarkus and Maven multi-module project support to Nx workspace",
|
||||
"url": "https://github.com/khalilou88/jnxplus/tree/main/packages/nx-quarkus-maven"
|
||||
},
|
||||
{
|
||||
"name": "@loft-orbital/terraform",
|
||||
"description": "Terraform executors and generators",
|
||||
@@ -433,60 +458,5 @@
|
||||
"name": "@nxlv/python",
|
||||
"description": "Nx plugin designed to extend the Nx features to work with Python projects based on Poetry.",
|
||||
"url": "https://github.com/lucasvieirasilva/nx-plugins/tree/main/packages/nx-python"
|
||||
},
|
||||
{
|
||||
"name": "nx-gcp-cache",
|
||||
"description": "Nx plugin to use Google Cloud Storage as distributed remote cache",
|
||||
"url": "https://github.com/davidgarciab/nx-gcp-cache"
|
||||
},
|
||||
{
|
||||
"name": "@jnxplus/nx-gradle",
|
||||
"description": "Nx plugin to add Gradle multi-project builds support to Nx workspace",
|
||||
"url": "https://github.com/khalilou88/jnxplus/tree/main/packages/nx-gradle"
|
||||
},
|
||||
{
|
||||
"name": "@jnxplus/nx-maven",
|
||||
"description": "Nx plugin to add Maven multi-module project support to Nx workspace",
|
||||
"url": "https://github.com/khalilou88/jnxplus/tree/main/packages/nx-maven"
|
||||
},
|
||||
{
|
||||
"name": "@naxodev/nx-cloudflare",
|
||||
"description": "Nx plugin for Cloudflare, in particular Cloudflare workers. It allows to generate build and run Cloudflare workers in your Nx workspace.",
|
||||
"url": "https://github.com/naxodev/oss/tree/main/packages/plugins/nx-cloudflare"
|
||||
},
|
||||
{
|
||||
"name": "@ziacik/azure-func",
|
||||
"description": "Generating, serving and publishing Azure Functions 4 apps.",
|
||||
"url": "https://github.com/ziacik/nx-tools/tree/master/packages/azure-func"
|
||||
},
|
||||
{
|
||||
"name": "@simondotm/nx-firebase",
|
||||
"description": "Nx plugin to support Firebase Apps and Cloud Functions in Nx workspaces",
|
||||
"url": "https://github.com/simondotm/nx-firebase"
|
||||
},
|
||||
{
|
||||
"name": "@dman926/nx-python-pdm",
|
||||
"description": "Use Python in NX workspaces with PDM",
|
||||
"url": "https://github.com/dman926/nx-python-pdm"
|
||||
},
|
||||
{
|
||||
"name": "@gnuechtel/nx-cucumber",
|
||||
"description": "Plugin to use Cucumber within Nx workspaces",
|
||||
"url": "https://gitlab.com/gnuechtel/open-source/-/tree/main/libs/nx-cucumber"
|
||||
},
|
||||
{
|
||||
"name": "@analogjs/platform",
|
||||
"description": "Official plugin to add Analog to your Nx monorepo.",
|
||||
"url": "https://analogjs.org/docs/integrations/nx"
|
||||
},
|
||||
{
|
||||
"name": "@getlarge/nx-heroku",
|
||||
"description": "Plugin to deploy and promote Nx apps on Heroku",
|
||||
"url": "https://github.com/getlarge/nx-heroku"
|
||||
},
|
||||
{
|
||||
"name": "@huge-nx/conventions",
|
||||
"description": "Plugin to generate and manage Nx workspaces by adhering to established workspace conventions.",
|
||||
"url": "https://github.com/jogelin/huge-nx"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,59 +1,5 @@
|
||||
# Documentation
|
||||
|
||||
## Structure of the Documentation
|
||||
|
||||
When writing documentation, it is important to know the audience you are writing for and what kind of document you are writing. We try to follow the framework listed below, but there are always some exceptions since the documentation is always changing. This framework is valuable to keep in mind, but we also follow the principle that if a PR is better than the existing content on the site, we'll merge it in and follow up to make it better later if it doesn't perfectly match the standards.
|
||||
|
||||
### Types of Documents
|
||||
|
||||
We are generally following the [Diataxis](https://diataxis.fr) model where documents are divided into tutorials, concept guides, recipes and reference.
|
||||
|
||||
- Tutorial - Focused on explaining a concept through step by step instructions.
|
||||
- Concept guide - Explains why something works the way it does or how to think about something.
|
||||
- Recipe - Focused directions to accomplish a specific task. Describes how to do something.
|
||||
- Reference - Lists what you can do with the tool (i.e. API docs).
|
||||
|
||||
### Audiences
|
||||
|
||||
We also have different audiences in mind when writing docs:
|
||||
|
||||
👶 New user starting from scratch
|
||||
|
||||
- They know their framework of choice
|
||||
- They have probably heard the term monorepo but don't really know what it is
|
||||
- They're smart and eager to learn
|
||||
|
||||
👶 New user migrating an existing repo
|
||||
|
||||
- They know their framework of choice
|
||||
- They know how npm workspaces work
|
||||
- They're smart and eager to learn
|
||||
|
||||
👦 Intermediate User
|
||||
|
||||
- They know how to create an Nx repo or add Nx to an existing repo
|
||||
- They have heard the terms integrated and package-based
|
||||
- They know what a project is and how to make one
|
||||
- They understand how to run a task and the basics of caching
|
||||
- They can launch the graph
|
||||
- They know that it is possible to enforce project boundaries
|
||||
|
||||
👨🦳 Advanced User
|
||||
|
||||
- They know everything about Nx except the specific piece of knowledge that is being taught by this document.
|
||||
|
||||
### Outline
|
||||
|
||||
- Getting Started - These documents assume a new user and are generally concept guides with a lot of links to other parts of the site. There are some elements of recipes mixed in, but those should be kept to a minimum.
|
||||
- Tutorials - These are tutorials written for a new user. After completing one of these tutorials, the user should have enough knowledge to be an intermediate user.
|
||||
- Core Features - These are primarily recipes with a little concept mixed in. These documents should be short and provide the basic information that people will want 80% of the time and link to anything more complex. A new user should be able to click through these documents and skim them to get a good understanding of what Nx does without getting overwhelmed with details.
|
||||
- Concepts - These are concept guides written for a new user. Any recipe content should be split into a recipe document and linked.
|
||||
- More Concepts (or other categories under Concepts) - These are concept guides written for an intermediate user.
|
||||
- Recipes - These are recipes written for an advanced user.
|
||||
- Nx with Your Favorite Tech - These are tutorials written for an intermediate user.
|
||||
- Benchmarks - Reference documents linking to external resources.
|
||||
- Reference - Reference documents.
|
||||
|
||||
## Markdown syntax available
|
||||
|
||||
The default markdown syntax is supported when writing documentation.
|
||||
@@ -74,12 +20,6 @@ description: This is a custom description
|
||||
---
|
||||
```
|
||||
|
||||
### Social Media Images
|
||||
|
||||
You can specify a custom social media image for a page by specifying `mediaImage` in the `map.json` entry for that page. `mediaImage` is a path relative to `/docs`.
|
||||
|
||||
Note that you won't see the social media image in the preview generated for your PR, because it loads from the live `nx.dev` site. You can make sure the image is correct by looking at `[preview-url]/images/open-graph/[page-url].[image-extension]`.
|
||||
|
||||
### Custom markdown syntax
|
||||
|
||||
The documentation website [nx.dev](https://nx.dev) is using custom Markdown syntax to enable the authors to add functionality to its content.
|
||||
@@ -94,16 +34,6 @@ Your content goes here.
|
||||
{% /callout %}
|
||||
```
|
||||
|
||||
#### Disclosure
|
||||
|
||||
A disclosure can be used for less important information that is initially collapsed.
|
||||
|
||||
```markdown
|
||||
{% disclosure title="string" %}
|
||||
Your content goes here.
|
||||
{% /disclosure %}
|
||||
```
|
||||
|
||||
#### Cards
|
||||
|
||||
Cards allow showing content in a grid system with a title, a description, a type and an url (internal/external).
|
||||
@@ -137,52 +67,6 @@ You can add specific languages and a filename on the code snippet displayed.
|
||||
```
|
||||
````
|
||||
|
||||
#### Line Highlighting
|
||||
|
||||
You can define groups of lines that can be interactively highlighted to illustrate a point.
|
||||
|
||||
````
|
||||
```javascript {% lineGroups={ first:[2,3],second:[4,5] } %}
|
||||
const code = "goes here";
|
||||
This is in the first group
|
||||
This is also in the first group
|
||||
This is in the second group
|
||||
This is also in the second group
|
||||
```
|
||||
````
|
||||
|
||||
The line groups can be highlighted using a button on the code fence itself, or by clicking on a link that you provide that changes the url fragment.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
[This will highlight the first group.](#first)
|
||||
```
|
||||
|
||||
You can also statically highlight a set of lines (the user won't be able to change what is highlighted):
|
||||
|
||||
````
|
||||
```javascript {% highlightLines=[2,3] %}
|
||||
const code = "goes here";
|
||||
This is highlighted
|
||||
This is also highlighted
|
||||
This is not highlighted
|
||||
Neither is this
|
||||
```
|
||||
````
|
||||
|
||||
You can also specify ranges like `highlightLines=[2,3,"8-10"]`.
|
||||
|
||||
#### Terminal command
|
||||
|
||||
To display a terminal command, use:
|
||||
|
||||
````
|
||||
```shell
|
||||
npx nx build
|
||||
```
|
||||
````
|
||||
|
||||
#### Terminal Output
|
||||
|
||||
You can display your terminal output with a dedicated component the same way you would show code.
|
||||
@@ -201,14 +85,6 @@ You can optionally also pass a `path` like
|
||||
```
|
||||
````
|
||||
|
||||
#### Terminal Video Output
|
||||
|
||||
You can have a more dynamic visualization of a terminal output by using the following component:
|
||||
|
||||
```
|
||||
{% terminal-video src="/documentation/shared/images/caching/cache-terminal-animation.mp4" /%}
|
||||
```
|
||||
|
||||
#### Custom iframes
|
||||
|
||||
We can display a special iframe and setting its width inside the document.
|
||||
@@ -235,7 +111,7 @@ We can display a special button inviting the reader to go to a GitHub repository
|
||||
You can add an "open in stackblitz" button as follows:
|
||||
|
||||
```markdown
|
||||
{% stackblitz-button url="github.com/nrwl/nx-recipes/tree/main/angular-standalone?file=README.md" /%}
|
||||
{% stackblitz-button url="github.com/nrwl/nx-recipes/tree/main/standalone-angular-app?file=README.md" /%}
|
||||
```
|
||||
|
||||
#### Install Nx Console
|
||||
@@ -246,6 +122,16 @@ We can display a special button inviting the reader to go to a VSCode marketplac
|
||||
{% install-nx-console /%}
|
||||
```
|
||||
|
||||
#### Nx Cloud section
|
||||
|
||||
We can display Nx Cloud related content in the documentation with a visual cue.
|
||||
|
||||
```markdown
|
||||
{% nx-cloud-section %}
|
||||
Your content goes here.
|
||||
{% /nx-cloud-section %}
|
||||
```
|
||||
|
||||
#### Side by side
|
||||
|
||||
You can show content in a grid of 2 columns, via the `side-by-side` shortcode.
|
||||
@@ -275,7 +161,7 @@ Yarn related information.
|
||||
|
||||
##### Youtube
|
||||
|
||||
Embed a YouTube video directly with the following shortcode, control the title and the associated width. `src` can be the Youtube URL from the browser, the "share" button (short YT url) or the embed URL.
|
||||
Embed a YouTube video directly with the following shortcode, control the title and the associated width.
|
||||
|
||||
```markdown
|
||||
{% youtube
|
||||
@@ -284,57 +170,6 @@ title="Nx Console Run UI Form"
|
||||
width="100%" /%}
|
||||
```
|
||||
|
||||
#### Youtube Section Link
|
||||
|
||||
Have a more decent button-like widget that you can place below sections of a tutorial with a link to a specific point in a Youtube video.
|
||||
|
||||
```markdown
|
||||
{% video-link link="https://youtu.be/OQ-Zc5tcxJE?t=64" /%}
|
||||
```
|
||||
|
||||
#### Project Details View
|
||||
|
||||
Embed a Project Details View that is identical what is shown in Nx Console or `nx show project myproject --web`
|
||||
|
||||
````markdown
|
||||
{% project-details title="Test" height="100px" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"project": {
|
||||
"name": "demo",
|
||||
"data": {
|
||||
"root": " packages/demo",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"dev": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "vite dev"
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"executor": "nx:run-commands",
|
||||
"inputs": ["production", "^production"],
|
||||
"outputs": ["{projectRoot}/dist"],
|
||||
"options": {
|
||||
"command": "vite build"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceMap": {
|
||||
"targets": ["packages/demo/vite.config.ts", "@nx/vite"],
|
||||
"targets.dev": ["packages/demo/vite.config.ts", "@nx/vite"],
|
||||
"targets.build": ["packages/demo/vite.config.ts", "@nx/vite"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /project-details %}
|
||||
````
|
||||
|
||||
#### Graph
|
||||
|
||||
Embed an Nx Graph visualization that can be panned by the user.
|
||||
@@ -408,22 +243,3 @@ Embed an Nx Graph visualization that can be panned by the user.
|
||||
|
||||
{% /graph %}
|
||||
````
|
||||
|
||||
## Publishing Process
|
||||
|
||||
There are multiple versions of the `nx.dev` site.
|
||||
|
||||
- [canary.nx.dev](https://canary.nx.dev) contains the documentation on the `master` branch
|
||||
- [nx.dev](https://nx.dev) contains the documentation as of the latest release of Nx to npm. The main site will not include reference documentation for APIs that have been merged to the codebase, but not yet released to the public.
|
||||
- `[version].nx.dev` contains the documentation for that version of Nx. `[version]` in this case is the major version up to the current LTS version of Nx. So [18.nx.dev](https://18.nx.dev) will show the Nx documentation as of the last released version of Nx 18.
|
||||
|
||||
When a commit that contains documentation is merged into `master`, it will be immediately published to `canary.nx.dev`. Whenever a new release of Nx is published to npm, that documentation will then be available on the main site.
|
||||
|
||||
### Immediately Publishing Time-Sensitive Documentation
|
||||
|
||||
If you have a documentation change that should be published immediately, you'll need to create 2 PRs.
|
||||
|
||||
1. First, create a PR against `master` in the normal manner.
|
||||
2. Your second PR needs to be made against the website branch with the latest major version of Nx. So your second PR can be created against `website-19` if the latest version of Nx is `19.1.0`. Once `website-19` is updated with your changes, the main `nx.dev` site will be updated.
|
||||
|
||||
Later, when Nx `19.1.1` is released, `website-19` will be overwritten with whatever is on `master` and the first PR you created will take effect.
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
---
|
||||
title: Nx Cloud 3.0 — Faster Cache, More Powerful DTE, Better Ergonomics
|
||||
slug: 'nx-cloud-3-0-faster-more-efficient-modernized'
|
||||
authors: [Juri Strumpflohner]
|
||||
cover_image: '/blog/images/2023-04-19/featured_img.webp'
|
||||
tags: [nx, nx-cloud]
|
||||
---
|
||||
|
||||
It has been almost 2 years since we released [Nx Cloud 2.0](https://nx.app/). Since then, it has saved over 400 years of computation by leveraging its distributed caching and task execution. And we keep adding 8 years every single week. Not only does this tremendously [impact our environment](https://dev.to/nx/helping-the-environment-by-saving-two-centuries-of-compute-time-4nep), but it also helps developers be more productive and companies save money.
|
||||
|
||||
In the last couple of months we have quadrupled the team and have done some amazing things. And we have some big plans for what is coming next. Here’s all you need to know!
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
- [New, Streamlined UI](#new-streamlined-ui)
|
||||
- [Prefetching and Faster Cache Uploading](#prefetching-and-faster-cache-uploading)
|
||||
- [DTE Just got Better](#dte-just-got-better)
|
||||
- [Direct Integration With GitHub, GitLab and Bitbucket](#direct-integration-with-github-gitlab-and-bitbucket)
|
||||
- [Enterprise Support](#enterprise-support)
|
||||
- [New, Simplified Plans and Pricing Model](#new-simplified-plans-and-pricing-model)
|
||||
- [Coming Next](#coming-next)
|
||||
- [Learn more](#learn-more)
|
||||
|
||||
**Prefer a Video? We’ve got you Covered!**
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/cG2hEI5L3qI?si=9frDSD8_HK1iTNEi" /%}
|
||||
|
||||
## New, Streamlined UI
|
||||
|
||||
This latest release of Nx Cloud comes with a new, streamlined design that offers users a more modern and visually appealing experience. This includes the main [Nx Cloud website](https://nx.app/), where we improved our messaging, including interactive visualizations to better explain some core concepts around remote caching and distributed task execution.
|
||||
|
||||

|
||||
|
||||
The Nx Cloud application — showing your runs, cache saved, and stats — also got a significant overhaul, making the UI more lightweight and easier to parse.
|
||||
|
||||

|
||||
|
||||
More UI-related updates and improvements are already underway.
|
||||
|
||||
## Prefetching and Faster Cache Uploading
|
||||
|
||||

|
||||
|
||||
At Nx, we are performance addicts! Especially when it comes to local development, every millisecond counts! In the latest update of the Nx CLI, we added the ability to **offload some of the remote cache management to the [Nx Daemon](/concepts/nx-daemon)**. As a result you no longer have to wait for the cache to upload. This saves valuable time, allowing the command to complete instantly and immediately providing you with the necessary link.
|
||||
We also **prefetch cache results in the background** to have them ready when needed.
|
||||
|
||||
Both optimizations can save seconds.
|
||||
|
||||
## DTE Just got Better
|
||||
|
||||

|
||||
|
||||
[Distributed Task Execution (short DTE)](/ci/features/distribute-task-execution) is a core part of what makes Nx Cloud stand out compared to other solutions. And we made some significant improvements to both the ergonomics and speed.
|
||||
|
||||
**Identify failed tasks early** — You can now view information about the in-progress DTEs, allowing you to quickly identify and address any failed functions without waiting for the command to complete.
|
||||
|
||||
**Simplified CI setup** — We simplified the setup process for most CI systems, eliminating the need to pass environment variables manually. Instead, everything is automatically derived from the context.
|
||||
|
||||
**Improved performance and efficiency** — Nx Cloud now intelligently figures out which tasks will be cache hits before sending them to agents. Rather it can directly send them to the main job, reducing unnecessary round trips and drastically speeding up CI runs.
|
||||
|
||||
**Efficient agent management** — We have introduced a new command, `npx nx-cloud start-ci-run –stop-agents-after=e2e`, that allows you to notify Nx Cloud when specific commands, such as long-running e2e tasks, have started. This helps Nx Cloud proactively identify and shut down agents that will not be needed, improving compute efficiency.
|
||||
|
||||
**Reduce fix costs** — Even though cache hits are basically free, spinning processes still have fixed costs. We fixed that (see what I did there) by leveraging a new Nx feature that allows to have a long-running process on an agent to which we can arbitrarily feed new tasks. This removed the DTE overheads and also improved the predictability of runs.
|
||||
|
||||
## Direct Integration With GitHub, GitLab and Bitbucket
|
||||
|
||||
Our GitHub integration has been enhanced. Now, the list of runs is updated in real-time as soon as they are created or their status changes, providing developers with up-to-date information directly on GitHub.
|
||||
|
||||

|
||||
|
||||
In addition to the GitHub, we expanded our Nx Cloud live status updates to work on GitLab and BitBucket.
|
||||
|
||||

|
||||
|
||||
## Enterprise Support
|
||||
|
||||

|
||||
|
||||
We have extensive experience working with Fortune 500 companies, helping them scale their development using monorepos. This has given us valuable insight into the unique security requirements of these companies. Our [Enterprise plan](/enterprise) reflects that allowing organizations to have a **fully self-contained version of Nx Cloud** that can be **hosted on their own servers** and comes with dedicated support from the Nx and Nx Cloud core team.
|
||||
|
||||
We’ve recently made a couple of improvements to our enterprise offering.
|
||||
|
||||
- **Helm Charts** — We added a **Helm chart** to simplify the process of deploying Nx Cloud to on-premises infrastructure, allowing organizations to quickly set up and manage their own instance of Nx Cloud within their secure environment.
|
||||
- **Stability improvements** — We significantly reworked our on-premises solution to be identical to our SaaS deployment. This revamp resulted in a more robust and reliable on-premises deployment of Nx Cloud, ensuring enterprise-grade performance and reliability.
|
||||
- **SSO** — We now support AWS Identity and Access Management (IAM) for seamless integration with existing AWS environments and the SAML protocol for a more flexible single sign-on integration across various providers. This enables organizations to leverage their existing identity management systems for authentication and authorization.
|
||||
|
||||
Learn more at [enterprise](/enterprise).
|
||||
|
||||
## New, Simplified Plans and Pricing Model
|
||||
|
||||
Nx Cloud has evolved a lot since we first released it in 2020, and is changing even more in 2023. To better adapt to Nx Cloud being a critical CI tool, we changed our pricing model to be more consistent and predictable for CI workloads.
|
||||
|
||||
Nx Cloud’s previous pricing was based on time savings from Nx Cloud, which made sense when Nx Cloud was strictly a distributed caching service. The [new pricing model](https://nx.app/pricing) is based entirely on the number of CI pipeline executions per calendar month. We believe this is a simpler and more transparent model that should help you predict your costs far more easily.
|
||||
|
||||

|
||||
|
||||
Our Free plan allows one administrator and up to 300 CI pipeline executions per month. Our Pro plan allows more administrators, and uncapped CI pipeline executions, with a flat fee and incremental charges per 100 CI pipeline executions. The OSS plan comes with unlimited CI pipeline executions. Nx has been open source from the beginning and we care a lot about that ecosystem. So this is our contribution to help OSS projects and make their CI pipeline executions faster.
|
||||
|
||||
Finally, the **Enterprise plan** is for companies that want full control over where their data is hosted, get hands-on, dedicated support from the Nx & Nx Cloud team, and enterprise features such as SSO & SAML-based authentication support.
|
||||
|
||||
All these changes should allow developers to choose the plan that best suits their needs and budget more easily, ensuring a seamless and transparent experience regarding pricing and subscription management.
|
||||
|
||||
Learn more at [https://nx.app/pricing](https://nx.app/pricing).
|
||||
|
||||
## Coming Next
|
||||
|
||||
We’ve got some big plans for Nx Cloud. You really want to write your CI script by focusing on what you want to achieve rather than thinking about making it fast. We’re going to make this happen!
|
||||
|
||||
The current Distributed Task Execution (DTE) already goes a long way, but you still have to provision the agents by yourself. Providing the correct number of agents is crucial for maximizing efficiency and reducing idle time. And it is not even a static number but might depend on the actual run itself. Nx has extensive knowledge about the structure of the workspace and according tasks. We want to leverage that information. Imagine a setup where you have a roughly 20 line CI config for a repo with hundreds of developers and Nx Cloud automatically determines for each run the ideal number of agents required, provisions them, distributes all tasks efficiently and then disposes all agents again. All fully automatically. And it will be fast. To the point where you wouldn’t even need your Jenkins, CircleCI etc at all.
|
||||
|
||||
In addition, we are actively exploring ways to provide advanced analytics for your workspace, including insights into the frequency and duration of specific tasks. This valuable information can help identify large tasks that could benefit from being broken down into smaller ones, leveraging caching and other speed improvements to optimize performance. Stay tuned for more to come!
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,338 +0,0 @@
|
||||
---
|
||||
title: Nx Console gets Lit
|
||||
slug: 'nx-console-gets-lit'
|
||||
authors: [Max Kless]
|
||||
cover_image: '/blog/images/2023-06-29/featured_img.webp'
|
||||
tags: [nx, nx-console]
|
||||
---
|
||||
|
||||
Over the last few weeks, we rebuilt one of Nx Console’s most liked features from the ground up: The Generate UI. It looks better, loads faster, and preliminary research shows using it makes you happier, too ;)
|
||||
|
||||
You can use it today by installing the latest version of Nx Console for VSCode and JetBrains IDEs! 🎉🎉🎉
|
||||
|
||||
- [Nx Console on the VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console)
|
||||
- [Nx Console on the JetBrains Marketplace](https://plugins.jetbrains.com/plugin/21060-nx-console)
|
||||
|
||||
If you’re curious to learn more about the rewrite and the motivations behind it, this is the blog post for you! We’ll touch on these topics and more:
|
||||
|
||||
- Why did we choose to rewrite?
|
||||
- What’s Lit and why did we use it over Angular?
|
||||
- How did the rewrite go and what Lit features were important for us?
|
||||
- What does the performance look like before and after?
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/p455D4W7330?si=FRbiKJhGxT8dYzf9" /%}
|
||||
|
||||
## Background: Why Migrate from Angular to Lit?
|
||||
|
||||
### A Short History of Nx Console
|
||||
|
||||
Let’s go back in time: Nx Console has been around for a while. [It first launched in 2018](https://blog.nrwl.io/announcing-the-first-stable-release-of-angular-console-the-ui-for-the-angular-cli-bb19093a92d4?source=friends_link&sk=d955094b2cb872a130b47c8040ec820e) — then called Angular Console — as a standalone Electron app which let you run Angular schematics and builders from a graphical interface. Of course, it was built with Angular and looked something like this:
|
||||
|
||||

|
||||
|
||||
In 2019, [it was ported to a VSCode extension](https://blog.nrwl.io/brand-new-ui-custom-vscode-task-support-more-in-angular-console-9-0-5e4d3a109fb9?source=friends_link&sk=47fd72d5700b4cb3de09e559c5524ce5) with the now familiar UI and support for the standalone app was dropped.
|
||||
|
||||
In 2020, support for the entire Nx ecosystem was added, it was renamed to Nx Console and an amazing transformation began: Today, Nx Console is much more than just a single form — it tightly integrates Nx into your IDE, gives you the overview of your projects that you need and puts your task-running just a click away.
|
||||
|
||||
### Rewriting in Lit
|
||||
|
||||
This evolution brought significant improvements to the usability of Nx Console and the value we could provide to developers, but not without presenting its own set of challenges. Inevitably, the codebase grew complex and convoluted over time — the context in which it ran changed, the scope of the product changed, yet the technology remained the same. Adding small features or fixing bugs became increasingly time consuming, and every PR compounded the problem.
|
||||
|
||||
The UI looked somewhat outdated and had been built with only VSCode in mind — which became painfully obvious when support for JetBrains IDEs was added. While the web-based nature of the Generate UI allowed us to reuse the code in the new environment, the design looked out of place.
|
||||
|
||||
In addition, we started questioning our usage of Angular. Angular is a great framework for building web apps, and in the original context of Angular Console, it made a lot of sense: A tool built by and for Angular engineers — of course it’s written in Angular. But things changed, and Angular started to feel overkill for what we needed. Angular has a huge number of features right out of the box. But for our simple form, we didn’t need routing, http requests or modules to organize our code. The amount of boilerplate and overhead Angular introduces is significant.
|
||||
|
||||
So, ultimately, **we decided to pull the plug and rewrite the entire thing in [Lit](https://lit.dev/).**
|
||||
|
||||
Lit is a lightweight framework built on top of web components and “adds just what you need to be happy and productive: reactivity, declarative templates and a handful of thoughtful features to reduce boilerplate and make your job easier” (taken from their docs). We had used it before to [build the Nx Cloud view](https://blog.nrwl.io/nx-console-meets-nx-cloud-d45dc099dc5d?source=friends_link&sk=90215eb316ef332c1324f2d50cd3ad85) and were happy with the simple setup and great DX. So the decision to reuse it here was an easy one, since it allowed us to reuse code, build tooling and expertise. The rewrite also gave us the opportunity to improve the design for both supported IDEs and give the entire UI a clean, new coat of paint.
|
||||
|
||||

|
||||
|
||||
Before I dive deeper into specifics, let’s have a look at the general architecture of Nx Console first.
|
||||
|
||||
## Nx Console Architectural Overview
|
||||
|
||||
Nx Console is composed of 3 core parts:
|
||||
|
||||
- The **nxls** is a language server based on the [Language Server Protocol (LSP)](https://microsoft.github.io/language-server-protocol/) and acts as the “brain” of Nx Console. It analyzes your Nx workspace and provides information on it, including code completion and more.
|
||||
- The **Generate UI** is the form-based view for running Nx generators.
|
||||
- The **platform-specific wrappers**. These are written in Typescript and Kotlin and connect the rest of Nx Console to IDE-specific APIs. Having the other parts separate greatly reduces the amount of duplicated code we have to write in order to support multiple IDEs
|
||||
|
||||
This architecture’s modularity meant we could quickly switch out the Generate UI for a new version without significantly impacting the rest of the codebase — only the parts that actually render the UI and communicate with it had to be adjusted slightly. It also allowed us to ensure backward compatibility: the old generate UI is still available via a feature toggle in the settings.
|
||||
|
||||
If you want to dive deeper, there are many more resources on the architecture of Nx Console and how it’s built:
|
||||
|
||||
- [In-depth blog post about expanding to JetBrains IDEs](https://blog.nrwl.io/expanding-nx-console-to-jetbrains-ides-8a5b80fff2d7?source=friends_link&sk=967080ea30bdbf9f8132f098a2cdd188)
|
||||
- [Accompanying Youtube video by Zack DeRose](https://www.youtube.com/watch?v=xUTm6GDqwJM)
|
||||
- [The Power of Nx Console — talk by Jon Cammisuli](https://www.youtube.com/watch?v=3C_9g9kt2KM)
|
||||
|
||||
## Migrating to Lit: Step by Step
|
||||
|
||||
To rebuild our UI, we first needed a new Lit app to work on. While there’s no native Nx plugin for Lit, generating the code we need was still very straightforward:
|
||||
|
||||
`nx generate @nx/web:app --name generate-ui-v2`
|
||||
|
||||
This generates an entire project for us, with a `tsconfig.json`, `index.html`, `main.ts`, and a `project.json`, where our Nx-specific config lives.
|
||||
|
||||
I also installed a couple of dependencies:
|
||||
|
||||
- The `@nx/esbuild` plugin because I like fast build times 🏎️
|
||||
- TailwindCSS because I don’t like writing CSS 🤫
|
||||
- `@vscode/webview-ui-toolkit` because it does all of the VSCode work for me 🤖
|
||||
|
||||
This is really where Nx shines, because it allows you to take these tools and quickly patch them together and build a pipeline that does exactly what you need. And it also allows you to think about your workspace visually. This is what this is what my task graph for building the Lit app ultimately looks like:
|
||||
|
||||

|
||||
|
||||
You can see three build steps:
|
||||
|
||||
- `generate-ui-v2:_build` uses esbuild to bundle my Lit components written in Typescript and spits out a `main.js` file
|
||||
- `generate-ui-v2:extract-dependencies` copies the third party assets we need into the dist folder. Right now it’s just codicons `.css` and `.ttf` files.
|
||||
- `generate-ui-v2:build` finally runs tailwind over the bundled code. This could also be done with `postCss` or a custom `esbuild` plugin but running tailwind directly is the easier, so why complicate things?
|
||||
|
||||
> 💡 There are different ways to generate this visualisation for your own workspaces:
|
||||
>
|
||||
> - In VSCode, use the Nx Project View or the `Nx: Focus task in Graph` action
|
||||
> - In JetBrains IDEs, use the Nx Toolwindow or context menus
|
||||
> - In the command line, run `nx build {{your project}} --graph`
|
||||
|
||||
In the bigger context of Nx Console, here’s what happens when you build the VSCode extension:
|
||||
|
||||

|
||||
|
||||
You can see that we’re still building both the new & old generate UI as well as the Nxls and the Nx Cloud webview before combining them all into one VSCode extension artifact.
|
||||
|
||||
### Building the Form
|
||||
|
||||
Lit is a very small library that provides useful abstractions over browser-native features like web components and messages. Here’s what a simple Lit component, written with Typescript, looks like:
|
||||
|
||||
```ts {% fileName="main.ts" %}
|
||||
@customElement('root-element')
|
||||
export class Root extends LitElement {
|
||||
render() {
|
||||
return html`<p>Hello World</p>`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html {% fileName="index.html" %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<script type="module" src="main.js"></script>
|
||||
<root-element></root-element>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
If you need to pass information up the DOM, you use normal events and you can set properties to send information to descendants. Check out the [Lit Docs](https://lit.dev/docs/components/rendering/) to learn more about how the render() method works, how to leverage reactivity, the shadow DOM and so much more.
|
||||
|
||||
> 💡 All code samples in this section have been adapted for brevity and clarity. So you won’t find this exact code anywhere, but it demonstrates the concepts well.
|
||||
|
||||
### Communicating with the IDE — using Reactive Controllers
|
||||
|
||||
To communicate with the host IDE, we were able to reuse almost all the logic from the previous UI (for more details, see [Communicating with IntelliJ](https://blog.nrwl.io/expanding-nx-console-to-jetbrains-ides-8a5b80fff2d7?source=friends_link&sk=967080ea30bdbf9f8132f098a2cdd188#d151) from the last blog post). Instead of a service that exposes observables that our component can consume, we used a [Reactive Controller](https://lit.dev/docs/composition/controllers/). Controllers are a neat feature of Lit — they hook into a component and can request DOM updates on their behalf. This eliminates the need for observable streams and subscriptions while keeping the communication code self-contained. Look at this example:
|
||||
|
||||
```ts {% fileName="main.ts" %}
|
||||
@customElement('root-element')
|
||||
export class Root extends LitElement {
|
||||
icc: IdeCommunicationController;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.icc = new IdeCommunicationController(this);
|
||||
}
|
||||
render() {
|
||||
return html`${JSON.stringify(this.icc.generatorSchema)}`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```ts {% fileName="ide-communication-controller.ts" %}
|
||||
// ide-communication-controller.ts
|
||||
export class IdeCommunicationController implements ReactiveController {
|
||||
generatorSchema: GeneratorSchema | undefined;
|
||||
constructor(private host: ReactiveControllerHost) {}
|
||||
// ...
|
||||
private handleMessageFromIde(message: InputMessage) {
|
||||
// ...
|
||||
this.generatorSchema = message.payload;
|
||||
this.host.requestUpdate();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can see that `root-element` can really just deal with rendering the form contents, delegating communicating with the IDE and when to update the DOM to the controller.
|
||||
|
||||
### Rendering the form fields — using Mixins
|
||||
|
||||
The core part of the UI is the form. We built all kinds of inputs: text fields, checkboxes, (multi-) select boxes and array fields. While those each have unique implementations, displaying them is also going to take a lot of repeated code. Every fields needs a label and description. Every field needs to know about its validation state, how dispatch change events and what aria attributes to set. In order to keep this code clean and DRY, we used [Class Mixins](https://lit.dev/docs/composition/mixins/). Mixins aren’t really a Lit-specific feature, but I don’t see them used much in other frameworks. A mixin is essentially a factory that takes a class and returns another, modified class. Check out this example:
|
||||
|
||||
```ts {% fileName="field-mixin.ts" %}
|
||||
const Field = (superClass) =>
|
||||
class extends superClass {
|
||||
// we can define (reactive) properties that every field is going to need
|
||||
@property()
|
||||
option: Option;
|
||||
protected get fieldId(): string {
|
||||
return `${this.option.name}-field`;
|
||||
}
|
||||
|
||||
// we can define methods that should be available to all fields
|
||||
dispatchValue(value: string) {
|
||||
// ...
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```ts {% fileName="field-wrapper-mixin.ts" %}
|
||||
const FieldWrapper = (superClass) =>
|
||||
class extends superClass {
|
||||
// we can define a render() method so that fields are all rendered the same
|
||||
protected render() {
|
||||
return html` <label for="${this.fieldId}">${this.option.name}</label>
|
||||
<p>${this.option.description}</p>
|
||||
${this.renderField()}`;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```ts {% fileName="input-field.ts" %}
|
||||
@customElement('input-field')
|
||||
export class InputField extends FieldWrapper(Field(LitElement)) {
|
||||
renderField() {
|
||||
return html` <input
|
||||
id="${this.fieldId}"
|
||||
@input="${(e) => this.dispatchValue(e.target.value)}"
|
||||
/>`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can see that each component and mixin deals with a specific subset of the overall logic, keeping our code cleanly separated and reusable. A checkbox, for example, is a special case because it’s layout on the page is slightly different — no problem, we simply wrote a CheckboxWrapper with some modifications without having to worry about changing the checkbox logic itself.
|
||||
|
||||
> 💡 Properly typing mixins is complicated so I left that part out. Refer to [Mixins in Typescript](https://lit.dev/docs/composition/mixins/#mixins-in-typescript) to learn more or [check out our source code on GitHub](https://github.com/nrwl/nx-console).
|
||||
|
||||
### Injecting Services & Data — Lit Context
|
||||
|
||||
If you’re coming from the Angular world, you probably appreciate the great dependency injection (DI) mechanism they have. You can centrally define some services and reuse them across your app, without thinking about passing on props from component to component — the DI system takes care of it. Lit provides something similar via the []`@lit-labs/context`](https://lit.dev/docs/data/context/) package. It’s based on the [Context Community Protocol](https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md) and similar to React’s context API.
|
||||
|
||||
Under the hood, it still works with normal browser events, but it abstracts it away from you so you can easily share data and services across your app.
|
||||
|
||||
The Generate UI is rendered in both VSCode and JetBrains IDEs. In order to look appropriate, components need to know which environment they’re in and adjust styling accordingly. Instead of passing this information from component to component, we can make it available contextually! And with a proper mixin, reading the editor context only has to be implemented once, too.
|
||||
|
||||
Have a look at the following example:
|
||||
|
||||
```ts {% fileName="editor-context.ts" %}
|
||||
export const editorContext = createContext<'vscode' | 'intellij'>(
|
||||
Symbol('editor')
|
||||
);
|
||||
|
||||
const EditorContext = (superClass) =>
|
||||
class extends superClass {
|
||||
@consume({ context: editorContext })
|
||||
@state()
|
||||
editor: 'vscode' | 'intellij';
|
||||
};
|
||||
```
|
||||
|
||||
```ts {% fileName="ide-communication-controller.ts" %}
|
||||
export class IdeCommunicationController implements ReactiveController {
|
||||
// ...
|
||||
constructor(private host: ReactiveElement) {
|
||||
const editor = isVscode() ? 'vscode' : 'intellij';
|
||||
// provide the context to all DOM children of the host element
|
||||
new ContextProvider(host, {
|
||||
context: editorContext,
|
||||
initialValue: editor,
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```ts {% fileName="some-component.ts" %}
|
||||
@customElement('some-component')
|
||||
export class SomeComponent extends EditorContext(LitElement) {
|
||||
render() {
|
||||
return html`<p>I am rendered in ${this.editor}</p>`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### VSCode Webview UI Toolkit
|
||||
|
||||
As we mentioned above, a big part of why we rewrote the UI is that it looked quite out of place in JetBrains IDEs. It was still useful, of course, but it’s important to make sure the form _feels_ right.
|
||||
|
||||
In VSCode, this is very easy to achieve, thanks to the [VSCode Webview UI Toolkit](https://github.com/microsoft/vscode-webview-ui-toolkit). It’s a set of web components, provided by Microsoft, that are designed to look good and be used in VSCode webviews.
|
||||
|
||||

|
||||
|
||||
Using it, you get the native look, a11y, and theme-aware styling for free! Thanks to everyone who built it, it’s a huge help!
|
||||
|
||||
### E2E Testing with Cypress
|
||||
|
||||
One big upside of using a webview is the huge Javascript ecosystem is available to you! To make sure that no regressions are introduced later on, we use [Cypress](https://www.cypress.io/). We can mock the editor communication and provide different schemas, make sure the form is rendered correctly and the right messages are sent back to the IDE.
|
||||
|
||||
While there’s no particular Lit integration for Cypress, the tool itself is framework agnostic so it still works perfectly fine. Using the [`@nx/cypress`](/nx-api/cypress) executors did most of the work for us so setup was pretty quick too.
|
||||
|
||||
### Results: Comparing Performance
|
||||
|
||||
There’s a number of different aspects to performance we can compare between the two implementations. The biggest one by far is not really quantifiable: The maintainability and looks of the new UI. In my opinion, it looks a lot fresher and more native in both environments. We got rid of a lot of legacy code and the new version is easier to reason about and work with.
|
||||
But there are thing we _can_ measure, so let’s talk numbers!
|
||||
|
||||
### Startup Time
|
||||
|
||||
It takes time to bootstrap a large framework like Angular, so skipping that, the UI should load quicker than before.
|
||||
|
||||
We measured the median time it took to render all options of the `@nx/angular:application` generator in both VSCode and IntelliJ. You can see that the results are pretty clear-cut, even though they are not hugely impactful.
|
||||
|
||||
Old UI (Angular)New UI (Lit)SpeedupVSCode65 ms39 ms~ 1.7xIntelliJ189 ms122 ms~ 1.5x
|
||||
|
||||
### Bundle Size
|
||||
|
||||
As mentioned earlier, Angular comes with a lot more features out-of-the-box than Lit, so it would make sense that the built bundle will be bigger.
|
||||
|
||||
We were able to reduce the bundle size (w/o compression) from about 733 kB to 282 kB, which comes out to about a 2,6x decrease. Unlike a website, where the bundle needs to be shipped to users when they load a page, Nx Console users only need to download it once when installing the plugin. This means we’re not affected by network speeds after installation, which makes the bundle size less critical.
|
||||
|
||||
> 💡 Because of a misconfiguration from a few Angular versions ago, the bundle size that we reported in:
|
||||
|
||||
{% tweet url="https://twitter.com/MaxKless/status/1671095858182381569" /%}
|
||||
|
||||
was overly bloated. We corrected it, but Lit still came out ahead in terms of size and rendering times.
|
||||
|
||||
### Build Time
|
||||
|
||||
While it might not be important to users of Nx Console, the time it takes to build the project makes a difference to us developers.
|
||||
|
||||
Since Lit is just javascript files that don’t require a custom compiler or build tooling, we decided to use [`esbuild`](https://esbuild.github.io/) (via `@nx/esbuild`), which is written in Go and extremely fast. On the other hand, the old UI used the `@angular-builders/custom-webpack:browser` builder, which uses webpack under the hood.
|
||||
|
||||
We went from about 3.5 seconds to less than 2 seconds of build time, which is less of an improvement than we expected. Since we also have to run tailwind over our files, some of that additional `esbuild` speed seems to be relativized.
|
||||
|
||||
## Looking Ahead
|
||||
|
||||
Rebuilding the UI has paved the road to reduce a lot of the maintenance burden of Nx Console. It will allow us to move even quicker on building new features to provide the best developer experience possible for you.
|
||||
|
||||
Specifically, the updated architecture enabled us to build a (still secret and WIP) plugin feature for Nx Console. Just like Nx, there are always going to be things that are unique to your workspace. We want to make it easy for you to extend and modify Nx Console in ways that help you make the most of using it.
|
||||
|
||||
So keep your eyes peeled for announcements and let us know via GitHub or Twitter if you have any ideas! We’d love to chat.
|
||||
|
||||
## One more thing!
|
||||
|
||||
Nx Console is a tool by developers for developers and there’s one thing we love — keyboard shortcuts. So of course we had to build some in. In addition to being keyboard-friendly and tabbable, you can do the following:
|
||||
|
||||
- `Cmd/Ctrl + Enter` to run the generator
|
||||
- `Cmd/Ctrl + Shift + Enter` to start a dry run
|
||||
- `Cmd/Ctrl + Shift + S` to focus the search bar and look for a specific option. Just `tab` to get back to the form
|
||||
|
||||
If the prettier UI and better performance haven’t convinced you, this surely will! 😉
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,611 +0,0 @@
|
||||
---
|
||||
title: Qwikify your Development with Nx
|
||||
slug: 'qwikify-your-development-with-nx'
|
||||
authors: ['Colum Ferry']
|
||||
cover_image: '/blog/images/2023-08-15/featured_img.png'
|
||||
tags: [nx, changelog, release]
|
||||
---
|
||||
|
||||
In the ever-evolving web development landscape, efficiency and modularity have become paramount. This is where [Nx]() and [Qwik](https://qwik.dev/) come into play.
|
||||
|
||||
Qwik is a modern web framework that focuses on application performance by reducing the amount of JavaScript that needs to be shipped to the browser. You can learn more about how Qwik achieves this with [Resumability in their docs](https://qwik.dev/docs/concepts/resumable/).
|
||||
|
||||
Nx is a powerful tool that helps you build extensible and maintainable codebases that scale as your application and team grows. Nx utilises computation cache and workspace analysis to ensure maximum efficiency and developer experience. You can [learn more about Nx here](/getting-started/why-nx).
|
||||
|
||||
In this blog post, we’ll explore how to combine the strengths of Nx and Qwik to create a todo app. To do this, we’ll take advantage of an Nx Plugin that was created by the Qwikifiers team to maximise the integration between Qwik and Nx, called [`qwik-nx`](https://github.com/qwikifiers/qwik-nx).
|
||||
|
||||
> You do not necessarily need to use an Nx Plugin for Qwik. Instead, you could use the [Qwik CLI](https://qwik.dev/docs/getting-started/#create-an-app-using-the-cli) to create your application and [add Nx later](/recipes/adopting-nx/adding-to-existing-project#install-nx-on-a-nonmonorepo-project).
|
||||
> In this blog post we use the `qwik-nx` plugin to leverage better DX provided by the generators offered by the Plugin.
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
- [Creating the Workspace](#creating-the-workspace)
|
||||
- [Generate the App](#generate-the-app)
|
||||
- [Generate a new Route](#generate-a-new-route)
|
||||
- [Build a Basic UI](#build-a-basic-ui)
|
||||
- [Generate a Library](#generate-a-library)
|
||||
- [Add a Qwik Context](#add-a-qwik-context)
|
||||
- [Using the Context](#using-the-context)
|
||||
- [Adding a `routeLoader$` to load data on Navigation](#adding-a-routeloader-to-load-data-on-navigation)
|
||||
- [Handle the Form Action to add todos](#handle-the-form-action-to-add-todos)
|
||||
- [Improve the Architecture](#improve-the-architecture)
|
||||
- [Conclusion](#conclusion)
|
||||
- [Further Reading](#further-reading)
|
||||
- [Learn more](#learn-more)
|
||||
|
||||
You can learn more about this integration in the video below:
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/SY22NaWHv0s?si=YrQp4qn7APU1f0U9" /%}
|
||||
|
||||
## Creating the Workspace
|
||||
|
||||
Let’s start by setting up our development environment. We’ll create an Nx workspace and integrate Qwik into it. Begin by generating an empty integrated workspace:
|
||||
|
||||
```shell
|
||||
npx create-nx-workspace@latest qwik-todo-app
|
||||
```
|
||||
|
||||

|
||||
|
||||
> You can also use the `preset` created by the `qwik-nx` plugin by running `npx create-qwik-nx` or `npx -y create-nx-workspace@latest --preset=qwik-nx`. This will skip a few of the next steps by installing the appropriate dependencies and generating your Qwik app.
|
||||
>
|
||||
> The `create-qwik-nx` package is an example of creating an Install Package with Nx. You can learn more here: [https://nx.dev/extending-nx/recipes/create-install-package](/extending-nx/recipes/create-install-package)
|
||||
|
||||
Next, navigate into the workspace and install the `qwik-nx` plugin.
|
||||
|
||||
```shell
|
||||
npm install --save-dev qwik-nx
|
||||
```
|
||||
|
||||
> You can view a compatibility matrix for which version of `qwik-nx` works with each version of `nx` [here](https://github.com/qwikifiers/qwik-nx#qwik-nx--nx-compatibility-chart).
|
||||
|
||||
## Generate the App
|
||||
|
||||
One of the benefits of using an Nx Plugin is that it comes with additional features such as automatic migrations, executors to act on your code and generators to scaffold code (_like CodeMods_).
|
||||
|
||||
Now, let’s use the application generator provided by `qwik-nx` to scaffold the todo application:
|
||||
|
||||
```shell
|
||||
nx g qwik-nx:app todo
|
||||
```
|
||||
|
||||
This will generate the starter project that Qwik itself provides in your Nx Workspace. It will also install all the necessary packages to build a Qwik application.
|
||||
|
||||
At this point, you can already run the `nx serve todo` and `nx build todo` commands to have a feel around of the application that was created.
|
||||
|
||||
## Generate a new Route
|
||||
|
||||
Qwik has another package called Qwik City that uses directory-based routing to handle navigation within your application. [Learn more about directory-based routing with Qwik City](https://qwik.dev/docs/qwikcity/).
|
||||
|
||||
The `qwik-nx` plugin can help generate new routes within our application. Let’s use it to generate a route where we can store our todo logic.
|
||||
|
||||
```shell
|
||||
nx g qwik-nx:route --name=todo --project=todo
|
||||
```
|
||||
|
||||
After running this command, you’ll see a new directory and file created in your workspace:
|
||||
|
||||

|
||||
|
||||
The newly created file should look like this:
|
||||
|
||||
```js {% fileName="apps/todo/src/routes/todo/index.tsx" %}
|
||||
import { component$ } from '@builder.io/qwik';
|
||||
|
||||
export default component$(() => {
|
||||
return <div>This is the todo</div>;
|
||||
});
|
||||
```
|
||||
|
||||
As you can see, it’s very simple, just a standard Qwik Component.
|
||||
|
||||
If you run `nx serve todo` and navigate to `http://localhost:4200/todo` you can see that the route works and the component renders the content correctly.
|
||||
|
||||

|
||||
|
||||
## Build a Basic UI
|
||||
|
||||
We want to build a todo application, so let’s add some UI elements to make this look more like an actual todo application.
|
||||
|
||||
Update `apps/todo/src/routes/todo/index.tsx` to match the following:
|
||||
|
||||
```js {% fileName="apps/todo/src/routes/todo/index.tsx" %}
|
||||
import { component$ } from '@builder.io/qwik';
|
||||
import { Form } from '@builder.io/qwik-city';
|
||||
|
||||
export default component$(() => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Todos</h1>
|
||||
<div>
|
||||
<label>
|
||||
<input type="checkbox" /> {'My First Todo'}
|
||||
</label>
|
||||
</div>
|
||||
<Form>
|
||||
<input type="hidden" name="id" value={1} />
|
||||
<input type="text" name="message" />
|
||||
<button type="submit">Add</button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
You’ll see the page update and look like the following:
|
||||
|
||||

|
||||
|
||||
Awesome!
|
||||
|
||||
However, you’ll notice that when you click `Add`, nothing happens! Let’s add some logic to store new todos.
|
||||
|
||||
## Generate a Library
|
||||
|
||||
Nx helps you organise your workspace in a modular fashion by creating workspace libraries that focus on specific functionality.
|
||||
|
||||
Instead of organising your features into subfolders of your application, with Nx, you’ll extract them into workspace libraries (libraries that are not intended to be published, but still used by other libraries and applications in your repository). This helps to create a much stronger boundary between modules and features in your application as libraries have a public API (the `index.ts` file), allowing you to control exactly what can be accessed by consumers.
|
||||
|
||||
> [Learn more about defining and ensuring project boundaries in the Nx docs.](/features/enforce-module-boundaries)
|
||||
>
|
||||
> By doing this, you start to build out a project graph for your workspace and your application. Defining your architecture in this manner also helps to reduce the areas in your application that each change affects.
|
||||
>
|
||||
> [Learn more about the Project Graph.](/concepts/mental-model#the-project-graph)
|
||||
|
||||
Using this feature of Nx, we can organise the state management of our todo application into its own library, separating the logic from the application itself.
|
||||
|
||||
Let’s generate a new library with the help of `qwik-nx`.
|
||||
|
||||
```shell
|
||||
nx g qwik-nx:lib data-access
|
||||
```
|
||||
|
||||

|
||||
|
||||
We do not need some of the files that were automatically generated so we can delete them:
|
||||
|
||||
```
|
||||
libs/data-access/src/lib/data-access.tsx
|
||||
libs/data-access/src/lib/data-access.css
|
||||
libs/data-access/src/lib/data-access.spec.tsx
|
||||
```
|
||||
|
||||
## Add a Qwik Context
|
||||
|
||||
Qwik uses [Contexts](https://qwik.dev/docs/components/context/) to help store state across both the server-side and client-side and across routes within the application.
|
||||
|
||||
We’ll use a Context to store the todos in the application, but first, let’s create a file to store the TS Interfaces we’ll use in our application.
|
||||
|
||||
Create `libs/data-access/src/lib/api.ts` and add the following:
|
||||
|
||||
```ts {% fileName="libs/data-access/src/lib/api.ts" %}
|
||||
export interface Todo {
|
||||
id: number;
|
||||
message: string;
|
||||
}
|
||||
```
|
||||
|
||||
Next, let’s create a new file `libs/data-access/src/lib/todo.context.tsx` and add the following content:
|
||||
|
||||
```tsx {% fileName="libs/data-access/src/lib/todo.context.tsx" %}
|
||||
import {
|
||||
component$,
|
||||
createContextId,
|
||||
Slot,
|
||||
useContextProvider,
|
||||
useStore,
|
||||
} from '@builder.io/qwik';
|
||||
import { Todo } from './api';
|
||||
|
||||
interface TodoStore {
|
||||
todos: Todo[];
|
||||
lastId: number;
|
||||
}
|
||||
|
||||
export const TodoContext = createContextId<TodoStore>('todo.context');
|
||||
export const TodoContextProvider = component$(() => {
|
||||
const todoStore = useStore<TodoStore>({
|
||||
todos: [],
|
||||
lastId: 0,
|
||||
});
|
||||
useContextProvider(TodoContext, todoStore);
|
||||
return <Slot />;
|
||||
});
|
||||
```
|
||||
|
||||
This will create our Context and set up a Store within our application to store the todos. Qwik takes advantage of signals to update state and inform the framework of which components need to be re-rendered when the state changes.
|
||||
|
||||
> [Learn more about how Qwik uses Signals.](https://qwik.dev/docs/components/state/)
|
||||
|
||||
Finally, let’s update the public entry point to the library to expose our Context and Interface.
|
||||
|
||||
## Using the Context
|
||||
|
||||
Let’s update the root page to add our Context Provider. Open `apps/todo/src/root.tsx` and add `TodoContextProvider` after `QwikCityProvider` in the component tree. Your file should look like the following:
|
||||
|
||||
```tsx {% fileName="apps/todo/src/root.tsx" %}
|
||||
import { component$, useStyles$ } from '@builder.io/qwik';
|
||||
import {
|
||||
QwikCityProvider,
|
||||
RouterOutlet,
|
||||
ServiceWorkerRegister,
|
||||
} from '@builder.io/qwik-city';
|
||||
import { RouterHead } from './components/router-head/router-head';
|
||||
import globalStyles from './global.css?inline';
|
||||
import { TodoContextProvider } from '@qwik-todo-app/data-access';
|
||||
|
||||
export default component$(() => {
|
||||
/**
|
||||
* The root of a QwikCity site always start with the <QwikCityProvider> component,
|
||||
* immediately followed by the document's <head> and <body>.
|
||||
*
|
||||
* Don't remove the `<head>` and `<body>` elements.
|
||||
*/
|
||||
useStyles$(globalStyles);
|
||||
return (
|
||||
<QwikCityProvider>
|
||||
<TodoContextProvider>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<RouterHead />
|
||||
</head>
|
||||
<body lang="en">
|
||||
<RouterOutlet />
|
||||
<ServiceWorkerRegister />
|
||||
</body>
|
||||
</TodoContextProvider>
|
||||
</QwikCityProvider>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
Update `libs/data-access/src/index.ts` to match the following:
|
||||
|
||||
```ts {% fileName="libs/data-access/src/index.ts" %}
|
||||
export * from './lib/todo.context';
|
||||
export * from './lib/api';
|
||||
```
|
||||
|
||||
Now that our Context is in place, let’s use it in our todo route to manage our todos.
|
||||
|
||||
Update `apps/todo/src/routes/todo/index.tsx` to match the following:
|
||||
|
||||
```tsx {% fileName="apps/todo/src/routes/todo/index.tsx" %}
|
||||
import { component$ } from '@builder.io/qwik';
|
||||
import { Form } from '@builder.io/qwik-city';
|
||||
import { TodoContext } from '@qwik-todo-app/data-access';
|
||||
|
||||
export default component$(() => {
|
||||
const todoStore = useContext(TodoContext);
|
||||
return (
|
||||
<div>
|
||||
<h1>Todos</h1>
|
||||
{todoStore.todos.map((t) => (
|
||||
<div key={`todo-${t.id}`}>
|
||||
<label>
|
||||
<input type="checkbox" /> {t.message}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
<Form>
|
||||
<input type="hidden" name="id" value={1} />
|
||||
<input type="text" name="message" />
|
||||
<button type="submit">Add</button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
Our store has no todos in it when the application starts up, so if you serve the application you will no longer see any todos listed. Let’s fix that!
|
||||
|
||||
## Adding a `routeLoader$` to load data on Navigation
|
||||
|
||||
Qwik allows you to fetch data when a route is navigated to, allowing you to fetch data before the page is rendered. The data will be fetched on the server before the component is rendered and downloaded to the client.
|
||||
|
||||
> [Learn more about routeLoader$.](https://qwik.dev/docs/route-loader/)
|
||||
|
||||
It does this by providing a function called `routeLoader$`. We’ll use this function to preload our store with some todos that will theoretically exist in a database.
|
||||
|
||||
For this blog post, we’ll create an in-memory db to store some initial todos.
|
||||
|
||||
We’ll start by updating our `libs/data-access/src/lib/api.ts` to add our in-memory DB.
|
||||
|
||||
```ts {% fileName="libs/data-access/src/lib/api.ts" %}
|
||||
export interface Todo {
|
||||
id: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface DB {
|
||||
store: Record<string, any[]>;
|
||||
get: (storeName: string) => any[];
|
||||
set: (storeName: string, value: any[]) => boolean;
|
||||
add: (storeName: string, value: any) => boolean;
|
||||
}
|
||||
export const db: DB = {
|
||||
store: { todos: [] },
|
||||
get(storeName) {
|
||||
return db.store[storeName];
|
||||
},
|
||||
set(storeName, value) {
|
||||
try {
|
||||
db.store[storeName] = value;
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
add(storeName, value) {
|
||||
try {
|
||||
db.store[storeName].push(value);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Now that we have this, let’s use it in our `/todo` route to load some data when the user navigates to `/todo`.
|
||||
|
||||
Update `apps/todo/src/routes/todo/index.tsx` to match the following:
|
||||
|
||||
```tsx {% fileName="apps/todo/src/routes/todo/index.tsx" %}
|
||||
import { component$ } from '@builder.io/qwik';
|
||||
import { Form, routeLoader$ } from '@builder.io/qwik-city';
|
||||
import { TodoContext, db } from '@qwik-todo-app/data-access';
|
||||
|
||||
export const useGetTodos = routeLoader$(() => {
|
||||
// A network request or db connection could be made here to fetch persisted todos
|
||||
// For illustrative purposes, we're going to seed a rudimentary in-memory DB if it hasn't been already
|
||||
// Then return the value from it
|
||||
if (db.get('todos')?.length === 0) {
|
||||
db.set('todos', [
|
||||
{
|
||||
id: 1,
|
||||
message: 'First todo',
|
||||
},
|
||||
]);
|
||||
}
|
||||
const todos: Todo[] = db.get('todos');
|
||||
const lastId = [...todos].sort((a, b) => b.id - a.id)[0].id;
|
||||
return { todos, lastId };
|
||||
});
|
||||
export default component$(() => {
|
||||
const todoStore = useContext(TodoContext);
|
||||
const persistedTodos = useGetTodos();
|
||||
useTask$(({ track }) => {
|
||||
track(() => persistedTodos.value);
|
||||
if (persistedTodos.value) {
|
||||
todoStore.todos = persistedTodos.value.todos;
|
||||
todoStore.lastId =
|
||||
todoStore.lastId > persistedTodos.value.lastId
|
||||
? todoStore.lastId
|
||||
: persistedTodos.value.lastId;
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<h1>Todos</h1>
|
||||
{todoStore.todos.map((t) => (
|
||||
<div key={`todo-${t.id}`}>
|
||||
<label>
|
||||
<input type="checkbox" /> {t.message}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
<Form>
|
||||
<input type="hidden" name="id" value={1} />
|
||||
<input type="text" name="message" />
|
||||
<button type="submit">Add</button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
When you serve the application, you’ll see the first todo is fetched and rendered correctly!
|
||||
|
||||
## Handle the Form Action to add todos
|
||||
|
||||
Qwik also allows you to handle form actions on the server using the `routeAction$` API. Let’s create the logic to add new todos to the store.
|
||||
|
||||
> [Learn more about `routeAction$`](https://qwik.dev/docs/action/)
|
||||
|
||||
Update `apps/todo/src/routes/todo/index.tsx`:
|
||||
|
||||
```tsx {% fileName="apps/todo/src/routes/todo/index.tsx" %}
|
||||
import { component$ } from '@builder.io/qwik';
|
||||
import { Form, routeLoader$ } from '@builder.io/qwik-city';
|
||||
import { TodoContext, db } from '@qwik-todo-app/data-access';
|
||||
|
||||
export const useGetTodos = routeLoader$(() => {
|
||||
// A network request or db connection could be made here to fetch persisted todos
|
||||
// For illustrative purposes, we're going to seed a rudimentary in-memory DB if it hasn't been already
|
||||
// Then return the value from it
|
||||
if (db.get('todos')?.length === 0) {
|
||||
db.set('todos', [
|
||||
{
|
||||
id: 1,
|
||||
message: 'First todo',
|
||||
},
|
||||
]);
|
||||
}
|
||||
const todos: Todo[] = db.get('todos');
|
||||
const lastId = [...todos].sort((a, b) => b.id - a.id)[0].id;
|
||||
return { todos, lastId };
|
||||
});
|
||||
export const useAddTodo = routeAction$(
|
||||
(todo: { id: string; message: string }) => {
|
||||
const success = db.add('todos', {
|
||||
id: parseInt(todo.id),
|
||||
message: todo.message,
|
||||
});
|
||||
return { success };
|
||||
},
|
||||
zod$({ id: z.string(), message: z.string() })
|
||||
);
|
||||
export default component$(() => {
|
||||
const todoStore = useContext(TodoContext);
|
||||
const persistedTodos = useGetTodos();
|
||||
const addTodoAction = useAddTodo();
|
||||
|
||||
useTask$(({ track }) => {
|
||||
track(() => persistedTodos.value);
|
||||
if (persistedTodos.value) {
|
||||
todoStore.todos = persistedTodos.value.todos;
|
||||
todoStore.lastId =
|
||||
todoStore.lastId > persistedTodos.value.lastId
|
||||
? todoStore.lastId
|
||||
: persistedTodos.value.lastId;
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<h1>Todos</h1>
|
||||
{todoStore.todos.map((t) => (
|
||||
<div key={`todo-${t.id}`}>
|
||||
<label>
|
||||
<input type="checkbox" /> {t.message}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
<Form action={addTodoAction}>
|
||||
<input type="hidden" name="id" value={todoStore.lastId + 1} />
|
||||
<input type="text" name="message" />
|
||||
<button type="submit">Add</button>
|
||||
</Form>
|
||||
{addTodoAction.value?.success && <p>Todo added!</p>}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
Awesome! We can now add todos to our application!
|
||||
|
||||
However, you might have noticed that our file is starting to get very long. Not only that there’s a lot of logic in the route file itself. Let’s use Nx to separate the logic into the library we created earlier to keep logic collocated.
|
||||
|
||||
## Improve the Architecture
|
||||
|
||||
To separate the logic, create a new file `libs/data-access/src/lib/todos.ts` and move the logic for loading and adding todos into their own functions:
|
||||
|
||||
```ts {% fileName="libs/data-access/src/lib/todos.ts" %}
|
||||
import { db, Todo } from './api';
|
||||
|
||||
export function getTodos() {
|
||||
// A network request or db connection could be made here to fetch persisted todos
|
||||
// For illustrative purposes, we're going to seed a rudimentary in-memory DB if it hasn't been already
|
||||
// Then return the value from it
|
||||
if (db.get('todos')?.length === 0) {
|
||||
db.set('todos', [
|
||||
{
|
||||
id: 1,
|
||||
message: 'First todo',
|
||||
},
|
||||
]);
|
||||
}
|
||||
const todos: Todo[] = db.get('todos');
|
||||
const lastId = [...todos].sort((a, b) => b.id - a.id)[0].id;
|
||||
return { todos, lastId };
|
||||
}
|
||||
export function addTodo(todo: { id: string; message: string }) {
|
||||
const success = db.add('todos', {
|
||||
id: parseInt(todo.id),
|
||||
message: todo.message,
|
||||
});
|
||||
return { success };
|
||||
}
|
||||
```
|
||||
|
||||
Next, update `libs/data-access/src/index.ts`
|
||||
|
||||
```ts {% fileName="libs/data-access/src/index.ts" %}
|
||||
export * from './lib/todo.context';
|
||||
export * from './lib/api';
|
||||
export * from './lib/todo';
|
||||
```
|
||||
|
||||
Finally, let’s update `apps/todo/src/routes/todo/index.tsx` to use our newly created functions:
|
||||
|
||||
```tsx {% fileName="apps/todo/src/routes/todo/index.tsx" %}
|
||||
import { component$, useContext, useTask$ } from '@builder.io/qwik';
|
||||
import {
|
||||
Form,
|
||||
routeAction$,
|
||||
routeLoader$,
|
||||
z,
|
||||
zod$,
|
||||
} from '@builder.io/qwik-city';
|
||||
import { addTodo, getTodos, TodoContext } from '@acme/data-access';
|
||||
|
||||
export const useGetTodos = routeLoader$(() => getTodos());
|
||||
export const useAddTodo = routeAction$(
|
||||
(todo) => addTodo(todo),
|
||||
zod$({ id: z.string(), message: z.string() })
|
||||
);
|
||||
export default component$(() => {
|
||||
const todoStore = useContext(TodoContext);
|
||||
const persistedTodos = useGetTodos();
|
||||
const addTodoAction = useAddTodo();
|
||||
useTask$(({ track }) => {
|
||||
track(() => persistedTodos.value);
|
||||
if (persistedTodos.value) {
|
||||
todoStore.todos = persistedTodos.value.todos;
|
||||
todoStore.lastId =
|
||||
todoStore.lastId > persistedTodos.value.lastId
|
||||
? todoStore.lastId
|
||||
: persistedTodos.value.lastId;
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<h1>Todos</h1>
|
||||
{todoStore.todos.map((t) => (
|
||||
<div key={`todo-${t.id}`}>
|
||||
<label>
|
||||
<input type="checkbox" /> {t.message}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
<Form action={addTodoAction}>
|
||||
<input type="hidden" name="id" value={todoStore.lastId + 1} />
|
||||
<input type="text" name="message" />
|
||||
<button type="submit">Add</button>
|
||||
</Form>
|
||||
{addTodoAction.value?.success && <p>Todo added!</p>}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
If you run `nx serve todo` again, you’ll notice that our refactor will not have changed anything for the user, but it has made the codebase more manageable!
|
||||
|
||||
Now, if we need to update the logic for loading or adding todos, we only need to retest the library, and not the full application, improving our CI times!
|
||||
|
||||

|
||||
|
||||
## Conclusion
|
||||
|
||||
The collaboration between Nx and Qwik has led us to create a todo app that showcases efficient development practices and modular design. By centralizing route logic in a library, we’ve not only demonstrated the capabilities of Nx and Qwik but also highlighted how this approach can significantly improve cache and CI times.
|
||||
|
||||
This journey through Qwik and Nx demonstrates how thoughtful architecture and the right tools can significantly enhance your development experience. So go ahead, Qwikify your development and build amazing web applications with ease!
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [Qwik](https://qwik.dev/)
|
||||
- [qwik-nx](https://github.com/qwikifiers/qwik-nx)
|
||||
- [Enforce Module Boundaries](/features/enforce-module-boundaries)
|
||||
- [Nx Core Concepts](/concepts)
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
title: Nx Raises $16M Series A
|
||||
slug: 'nx-raises-16m-series-a'
|
||||
authors: [Jeff Cross]
|
||||
tags: [nx]
|
||||
---
|
||||
|
||||
Victor and I are excited to announce that Nx has raised another $16M in a Series A funding round with Nexus Venture Partners and a16z! See our announcement video for more, and be sure to check out the [live stream of Nx Conf 2023](https://youtube.com/live/IQ5YyEYZw68?feature=share) tomorrow to see what we’re up to!
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/KuyYhC4ClW8?si=qoZL6i6X1E7wjChD" %}
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,498 +0,0 @@
|
||||
---
|
||||
title: Nx Conf 2023 — Recap
|
||||
slug: 'nx-conf-2023-recap'
|
||||
authors: [Juri Strumpflohner]
|
||||
cover_image: '/blog/images/2023-10-13/featured_img.webp'
|
||||
tags: [nx, nx-conf]
|
||||
---
|
||||
|
||||
The 3rd edition of [Nx Conf](/conf), this year live from New York City. If you missed the talks, no worries, we’ve got you covered. This article does a brief recap of all the presentations and has links to the individual recordings and slides.
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/P_W8MwX25a0?si=gpjul3hdqmOiBdev" /%}
|
||||
|
||||
## Keynote
|
||||
|
||||
**Speaker:** [Juri Strumpflohner](https://twitter.com/juristr) & [Victor Savkin](https://twitter.com/victorsavkin)
|
||||
|
||||
{% youtube src="https://www.youtube.com/watch?v=WSqivWlEDFw" /%}
|
||||
|
||||
Juri Strumpflohner (me 😅) opens the keynote. He focuses on the Nx ecosystem, how it is [much more than monorepos](/getting-started/why-nx) by helping you integrate your framework of choice (whether that is Angular, React, or Vue) with tooling such as ESLint, Playwright, Cypress, Webpack, ESBuild, Vite etc.
|
||||
|
||||

|
||||
|
||||
Such integration capabilities really help push the developer productivity, whether that’s in single-project workspaces or monorepos.
|
||||
Juri also dives deeper into efforts from the team to provide high quality educational content around Nx and its capabilities. The [Nx docs](/getting-started/intro) have been restructured to follow the [Diataxis](https://diataxis.fr/) framework, dividing content into into learning-, task-, understanding- and information-oriented sections.
|
||||
|
||||

|
||||
|
||||
This makes it easier to find keep the content organized and focused and makes it easier for the reader to choose between solution-oriented [recipes](/recipes) vs learning-oriented [tutorials](/getting-started/tutorials).
|
||||
|
||||
The Nx team not only produces written content, but also video content mainly on the [Nx YouTube Channel](https://www.youtube.com/@nxdevtools). Juri shows some of the growth stats, with the channel now having more than 14k subscribers and around 3.7k hours of watch time per month. The channel serves mostly short-form videos about new releases, highlighting new features as well as longer-form tutorial videos.
|
||||
|
||||
The Nx Community also got a special place in the keynote. Juri highlighted in particular the transition from the “Nrwl Community Slack” to the new “Nx Community” on Discord.
|
||||
|
||||

|
||||
|
||||
Discord is built for managing communities and will open up a series of features to come. Things like a built-in forum to ask questions, the ability to highlight Nx events (e.g. the Nx live stream) and having powerful automation and moderation tools. Since the launch a couple of weeks ago, already 1500+ members have signed up. If you didn’t sign up yet, go to [https://go.nx.dev/community](https://go.nx.dev/community).
|
||||
Juri also highlighted the [Nx Champions](/community) program that got launched this year which helps with efforts to grow the Nx community. The program features members that stood out in the Nx community by helping support others, producing content or speaking at conferences about Nx and related tech.
|
||||
|
||||
Finally there were also two **announcements**:
|
||||
|
||||
- the Nx team decided to move off Medium for a better publishing experience but mainly also to avoid the paywall, something that the team has no control over. The new blog is currently being built and will be hosted at [blog](/blog). This also allows to better resurface information by being able to integrate blog articles into the Nx docs search and make them also accessible to the Nx AI Assistant which was the 2nd announcement.
|
||||
- the **Nx AI Assistant** is an experiment the team is running to use AI to improve discoverability on the Nx docs. The ChatGPT powered assistant allows to ask natural language questions and responds based on the Nx docs training data, also including links to the sources. Try it out at [ai-chat](/ai-chat) and use the feedback thumbs-up/down buttons to help improve it over time 🙏.
|
||||
|
||||
Also, Nx is open source: [https://github.com/nrwl/nx](https://github.com/nrwl/nx). Contribute! And while you’re there, don’t forget to give us a star 😃.
|
||||
|
||||
**Victor Savkin** began the second segment of the keynote discussing the hidden expenses that often plague large teams.
|
||||
|
||||

|
||||
|
||||
He emphasized that as an organization grows, so does the supporting work. This isn’t limited to just communication and management. Every artifact, be it code, process, or otherwise, needs to be assimilated into the broader context. This supporting work becomes intricate and demanding. He pointed out that the remote work trend, while it started with a lot of momentum, struggles in larger corporations because it demands even more coordination and effort. In contrast, smaller entities often outshine their larger counterparts due to the reduced overhead of supporting work.
|
||||
|
||||
One of Victor’s main points was the potential of software developers. Given the right tools and environment, they can achieve remarkable feats. CI is one such tool.
|
||||
|
||||

|
||||
|
||||
The state of CI configuration, Victor explained, is essentially a meticulous blueprint of tasks for the CI tool. This blueprint defines everything from the number of machines to their precise roles. Historically, these processes were performed manually, with individuals running test cases and subsequently approving or rejecting them.
|
||||
|
||||
However, scaling CI is a challenge. In larger workspace, up to 50 agents is pretty common, all requiring careful coordination and configuration to ensure tasks run in the correct order.
|
||||
|
||||

|
||||
|
||||
This might be manageable in a static repository setup, but monorepos introduce an added layer of complexity. Here, static “CI recipes” just won’t cut it. The result is either an overuse of computational resources, leading to waste of money, or a painfully slow CI.
|
||||
|
||||
Victor critiqued that most current CI setups are:
|
||||
|
||||
- Oriented towards machines
|
||||
- Low-level in their configuration
|
||||
- Maintenance-heavy
|
||||
- Detached from developer intentions
|
||||
- Challenging to implement with monorepos
|
||||
|
||||
The big question: how can we revolutionize CI? Victor then showcased a demo workspace and highlights where the complexity of setting up CI comes from:
|
||||
|
||||

|
||||
|
||||
Even running e2e tests on CI requires:
|
||||
|
||||
- Building the application to be tested to produce an artifact
|
||||
- That usually requires first building all libraries the app depends on, in the correct order
|
||||
- Once all libraries are built, the application itself can be build
|
||||
- Finally e2e can be run on the application artifact
|
||||
|
||||
Notice, since we want to do this as fast as possible, we want to parallelize these operations across machines. This involves also taking care of transferring build artifacts between them.
|
||||
|
||||
This is where **Nx Cloud Workflows** shines. It enables developers to draft CI configurations at a far more abstract level. Instead of delving into the minutiae of tasks, developers specify **what** commands to execute, with the **how** being implicitly managed by Nx Cloud.
|
||||
|
||||
```yaml
|
||||
env:
|
||||
NODE_OPTIONS: '--max_old_space_size=4096'
|
||||
setup:
|
||||
- name: Git Checkout
|
||||
uses: 'nx-cloud-steps/checkout'
|
||||
- name: Npm Install
|
||||
uses: 'nx-cloud-steps/npm-install'
|
||||
steps:
|
||||
- name: CI Checks
|
||||
parallel-scripts: |
|
||||
nx affected -t build e2e --parallel=1
|
||||
nx affected -t test lint --parallel=3
|
||||
```
|
||||
|
||||
This elevated abstraction is possible because Nx Cloud & Nx are intimately familiar with the workspace, understanding the interdependencies between projects and tasks.
|
||||
|
||||

|
||||
|
||||
Beyond simplifying CI configurations, Nx Cloud Workflows helps optimize computational resource use. Agents are instantiated dynamically based on the project’s requirements.
|
||||
|
||||

|
||||
|
||||
Victor highlighted that most current CI setups:
|
||||
|
||||
- Consistently utilize a predetermined number of agents, irrespective of the actual needs of the PR
|
||||
- Possess non-reusable agents since each is tailored to a specific task, like building or testing
|
||||
- Struggle with granular retries
|
||||
|
||||
As a metaphor Victor mentions that **Nx Cloud Workflows is to CI what S3 is to file uploads.**
|
||||
|
||||

|
||||
|
||||
Sounds interesting? Watch the entire talk below:
|
||||
|
||||
{% youtube src="https://www.youtube.com/watch?v=WSqivWlEDFw" /%}
|
||||
|
||||
## Nx Cloud Workflows: Next-Gen CI with First-Class Monorepo Support
|
||||
|
||||
**Speaker:** Simon Critchley
|
||||
[Slides](https://docs.google.com/presentation/d/18EHfZ4UUtnAlzl15Ul_GPsr5lxNFtEEFPuT1uvxIEAk/edit?usp=sharing)
|
||||
|
||||
{% youtube src="https://youtu.be/JG1FWfZFByM?si=pNQNCLsbn5U0uSFB" /%}
|
||||
|
||||
Simon Critchley (Senior Software Architect at Nx) dives into the more technical details of the newly announced **Nx Cloud Workflows** (which is in private beta as of this writing).
|
||||
|
||||

|
||||
|
||||
Apart from the distributed caching, Nx Cloud already offers [DTE (Distributed Task Execution)](/ci/features/distribute-task-execution), which is a mechanism to seamlessly distribute tasks across various machines to achieve a high degree of parallelism. Up until now it was on you to configure your existing CI in a way to provision machines (agents) to Nx Cloud DTE. This required some fine-tuning to understand the best level of parallelism given the underlying monorepo and tasks that need to be run. If you have too many machines, it’ll be wasteful, if you have to few your CI will be slower.
|
||||
|
||||
Nx Cloud Workflow is an advancement of the DTE mechanism, where machines can be automatically provisioned and scaled based on the tasks that need to be run. Essentially it is a replacement for your existing CI but way smarter because it is able to leverage the knowledge it has about the Nx workspace, historical data and can thus do a series of optimizations. Traditional CI systems are running low-level commands on machines. Nx Cloud Workflows is task oriented instead: you tell it to run builds, tests, e2e, linting on the affected projects of the Nx workspace and it figures out how to split them up into fine-grained tasks and then distributes them among dynamically provisioned agents.
|
||||
|
||||
From a developer’s perspective, Nx Cloud Workflows are written in Yaml (or alternatively in JSON which is handy if you need to generate them). The format is very similar to existing CI providers to make sure the knowledge you have easily transfers. If you want an example, we’re dog-fooding Nx Cloud Workflows on the Nx repo (note the config will change as the product progresses): [https://github.com/nrwl/nx/blob/master/.nx/workflows/agents.yaml](https://github.com/nrwl/nx/blob/master/.nx/workflows/agents.yaml)
|
||||
|
||||
Simon then dives way deeper into the underlying architecture. In particular, how scheduling in Kubernetes works, which is used at the infrastructure level for Nx Cloud Workflows.
|
||||
|
||||

|
||||
|
||||
Workflow scheduling is handled by Kubernetes, specifically through a “Workflow Controller” that interacts with the K8s API Server and triggers Kubernetes Pods. Each Pod contains a Workflow executor and sidecar containers. All containers in the Nx Cloud workflow share a workspace volume for collaborative access. This allows for interesting optimizations in terms of sharing `node_modules` and restoring cache results.
|
||||
|
||||

|
||||
|
||||
The Workflow executor, written in Go, is responsible for receiving and executing workflow steps, capturing output, buffering logs, and communicating with the Workflow Controller. It also exposes its own grpc API for distributed locking, file system cache operations, and setting output parameters for each step, ensuring efficient workflow execution.
|
||||
|
||||
Nx Cloud Workflow is in its early development phase. The Kubernetes scheduler supports Docker based executors (basically any Docker image can be used as build image).
|
||||
|
||||

|
||||
|
||||
Windows, macOS and Linux is currently in development which will use a Cloud VM scheduler on the cloud of your choice.
|
||||
|
||||
## United by Nxcellent DX
|
||||
|
||||
**Speaker:** [Michael Hladky](https://twitter.com/Michael_Hladky)
|
||||
[Slides](https://docs.google.com/presentation/d/1F6_x3Jiu_3b9tZpxQnCrU60Eb1FhYR8E5jVnb1t7DjA/edit?usp=sharing)
|
||||
|
||||
{% youtube src="https://youtu.be/i0UdoImryJQ?si=a5lx6VnS__qhiyex" /%}
|
||||
|
||||
Michael talks about how to leverage Nx to migrate multiple repositories into a monorepo to streamline development and increase developer productivity. He goes through
|
||||
|
||||
- how a project gets started
|
||||
- how to prep moving to a monorepo from a polyrepo situation
|
||||
- how to do the move in parallel
|
||||
- how to measure the progress
|
||||
|
||||
The project usually starts with an architectural audit. That involves a detailed analysis of the underlying codebase, also including an executive summary for non technical folks. Once the audit is done, the actual move is planned and prepared. A part of that is to define “Nx migration goals”, like
|
||||
|
||||
- Single Version Policy
|
||||
- Improved Maintenance
|
||||
- Shared Infrastructure
|
||||
- Shared Architecture
|
||||
- Efficient Task Runs
|
||||
- Frictionless Code-Sharing
|
||||
- Frequent Deployments
|
||||
- Decouple Deployment
|
||||
|
||||
To prioritize which ones to address first, Michael uses the following metaphor:
|
||||
|
||||

|
||||
|
||||
The important part here is to understand why “apples get bad” in the first place. Is it the harvesting process?
|
||||
|
||||
Once the priorities are defined and roadmap laid out, the goal is to **move in parallel.**
|
||||
As part of that move the following gets produced:
|
||||
|
||||
- Migration Guide
|
||||
- Training Program
|
||||
- Communication Strategy
|
||||
- Impact Measurement
|
||||
|
||||
The migration guide defines where to start, what the company goals are, how the deliveries will be integrated into the existing process and how the interaction with the various teams will happen.
|
||||
|
||||
The training program is there to tackle the bottleneck of missing knowledge, right from the beginning. Each stage of the process will have a different training content and collect feedback.
|
||||
|
||||
Finally the impact measurement; Nx Cloud already has graphs to measure how much time got saved in CI due to the improvements made. Michael mentions they go further, also measuring communication and productivity.
|
||||
|
||||
An interesting part is also how they perform the repository synching (from polyrepo to monorepo). They leverage an Nx plugin that
|
||||
|
||||
- contains shared build logic
|
||||
- has rules to run that produce actionable feedback about what is needed to sync/align the polyrepo repository s.t. it can be merged into the monorepo
|
||||
- it also allows to track progress and produce according reporting of where the company is at
|
||||
|
||||
Sounds interesting? Watch the full talk below:
|
||||
|
||||
{% youtube src="https://www.youtube.com/watch?v=i0UdoImryJQ" /%}
|
||||
|
||||
## Redefining Projects with Nx: A Dive into the New Inference API
|
||||
|
||||
**Speaker:** [Craigory Coppola](https://twitter.com/enderagent)
|
||||
[Slides](https://craigory.dev/presentations/view/nx-conf-2023-inference/#1)
|
||||
|
||||
Demo Repo:
|
||||
{% github-repository url="https://github.com/AgentEnder/inference-demo" /%}
|
||||
|
||||
{% youtube src="https://www.youtube.com/watch?v=bnjOu7iOrMg" /%}
|
||||
|
||||
Craigory did a deep dive into the new Nx inference API. This is particularly interesting if you’re developing [Nx plugins](/extending-nx/intro/getting-started) or if you have leverage/build some more advanced automation for your Nx workspace.
|
||||
|
||||
What is project inference:
|
||||
|
||||
- how Nx reads your project configuration
|
||||
- introduced between v13.3 and v14
|
||||
- initially to support package-based monorepos which use package.json scripts rather than `project.json`. Generalizing the project config reading mechanism also allowed to define an API that community plugins can leverage and which allows to integrate even languages outside the JS ecosystem into Nx (e.g. where projects are just defined differently, such as .Net, Java, Python,..)
|
||||
|
||||
Inference API v1
|
||||
|
||||
- `projectFilePatterns` - identify files that represent the root of a project
|
||||
- `registerProjectTargets` - takes a project file and converts to a list of targets that Nx knows how to run
|
||||
|
||||
Shortcomings have been
|
||||
|
||||
- strict 1–1 mapping between project files and projects
|
||||
- the logic of finding proj files and targets had to be decoupled which introduced potential failure points
|
||||
- no way to add dynamic metadata to a project
|
||||
|
||||
Project graph API v2
|
||||
|
||||
- `createNodes` - finds graph nodes based on files on disk
|
||||
- `createDependencies` - finds edges to be added to the graph
|
||||
|
||||
Still 2 parts, but there’s no overlap between these two and they have very specific purposes.
|
||||
|
||||
`createNodes` is a tuple composed of:
|
||||
|
||||
- `projectFilePattern`
|
||||
- `CreateNodesFunction` It can return a map of projects and external nodes, so there's no more the shortcoming of a 1-1 as it was in v1 API. With this new setup multiple plugins might detect the same project. Nx merges the configuration that has been identified. Kinda what happens right now if you mix `package.json` and `project.json` targets in an Nx workspace.
|
||||
|
||||
Craigory demos the inference API by building a spell checking plugin for Nx.
|
||||
|
||||
`createDependencies` replaces `processProjectGraph`. It is more constrained but mainly because things like adding nodes should be done in a different function now (e.g. `createNodes`). This has also advantages for Nx itself, as it knows all nodes will have been created after the `createNodes` has terminated.
|
||||
|
||||
This API is still marked as experimental, next steps will be
|
||||
|
||||
- mark as stable
|
||||
- remove v1 API after deprecation period
|
||||
- plugin authors should start looking into the new API, provide feedback and think about migration scenarios
|
||||
|
||||
## Package-based to Integrated: One Small Step or One Giant Leap?
|
||||
|
||||
**Speaker:** [Isaac Mann](https://twitter.com/MannIsaac)
|
||||
[Slides](https://drive.google.com/file/d/1V1ocFIjYkRrcNXWuvUK1T8dQEceDcjvw/view?pli=1)
|
||||
|
||||
Repo:
|
||||
{% github-repository url="https://github.com/isaacplmann/space-station-tracker/tree/final" /%}
|
||||
|
||||
Watch on YouTube:
|
||||
{% youtube src="https://www.youtube.com/watch?v=nY0_o7zWBLM" /%}
|
||||
|
||||
Isaac initiated his talk by drawing an analogy between software development and the moon landing. While Neil Armstrong is often singularly celebrated for setting foot on the moon, Isaac emphasized that the monumental achievement was the collective effort of countless individuals on the ground crew, from engineers to support staff.
|
||||
|
||||
> Isaac: Nx wants to be that indispensable ground support crew for developers, allowing them to push the boundaries of software development.
|
||||
|
||||
Currently, Nx offers support for two predominant monorepo styles: package-based and integrated.
|
||||
|
||||
### Package-based Monorepos:
|
||||
|
||||
- These are tailored for flexibility and innovation.
|
||||
- Packages within this setup can have diverse configurations.
|
||||
- Every package boasts its individual node_modules and dependencies.
|
||||
- Crucially, each can be upgraded independently, offering a high degree of autonomy.
|
||||
|
||||
### Integrated Monorepos:
|
||||
|
||||
- These are structured to prioritize consistency and maintainability.
|
||||
- Updates within this framework are automated, and the setup adheres to a single-version policy.
|
||||
|
||||
Isaac then drew parallels between the Apollo program and the package-based mindset. The Apollo missions were evolutionary in nature, constantly pushing the envelope and embracing innovation with each successive mission. However, this trailblazing approach wasn’t without its perils, as evidenced by tragic accidents. This experimental approach was feasible because the core team, responsible for creating the setup, remained consistent throughout the project’s duration.
|
||||
|
||||
In contrast, Isaac likened the International Space Station (ISS) to the integrated mindset. The ISS epitomizes the challenges of coordination, given its international collaboration involving numerous countries. Emphasizing longevity, the ISS necessitates that every new crew member be proficient in operating its intricate machinery.
|
||||
|
||||
Shifting gears, Isaac delved into a hands-on demonstration. He outlined the process of transitioning from a package-based monorepo to an integrated one, including demoing:
|
||||
|
||||
- Initializing Nx with `nx init`.
|
||||
- Establishing new projects to facilitate type sharing across applications.
|
||||
- Harnessing the power of the Nx graph visualization to navigate and understand the project structure.
|
||||
- Employing the module boundary rule, ensuring constraints are maintained in the revamped structure.
|
||||
- Devising a novel Nx generator, streamlining the process of setting up new libraries within the workspace.
|
||||
- Finally, he showcased the seamless upgrade mechanism, ensuring the workspace is always aligned with the latest version.
|
||||
|
||||
## Nx’t Level Publishing
|
||||
|
||||
**Speaker:** [James Henry](https://twitter.com/MrJamesHenry)
|
||||
[Slides](https://main--idyllic-dieffenbachia-3699ec.netlify.app/1)
|
||||
|
||||
Watch on YouTube:
|
||||
{% youtube src="https://www.youtube.com/watch?v=p5qW5-2nKqI" /%}
|
||||
|
||||
James Henry unveils the new versioning and publishing functionality that is now built into Nx itself.
|
||||
|
||||
Nx has always given you the building blocks to do integrate the versioning and publishing; you could easily use Lerna with Nx, or changesets, release-it as well as a series of Nx Community plugins
|
||||
The nx core team decided to go into this problem space and provide an opinionated approach that is easy to use within existing Nx workspaces; something that works and scales; also something that works outside the JS ecosystem which Nx can also be used with
|
||||
|
||||
New command `nx release`
|
||||
|
||||
- `nx release version` to determine and apply version updates
|
||||
- `nx release` changelog generate a CHANGELOG.md file and optional GitHub releases based on git commits
|
||||
- `nx release` publish takes a newly versioned project and publishes them to a remote registry (e.g. NPM)
|
||||
|
||||
This new command is not tight to `package.json` files but is general purpose; clearly publishing JS/TS packages is the main use case right now in Nx
|
||||
Important to note that all the existing plugins are still valid and will still be going forward.
|
||||
|
||||
James goes forward demoing how `nx release` works in a simple NPM package: [jump directly into the video](https://www.youtube.com/watch?si=xoIW0Q-mQWTGgrek&t=411&v=p5qW5-2nKqI&feature=youtu.be).
|
||||
|
||||
Nx release can be added to any npm package. All that’s needed is the `nx` and `@nx/js` package and a `nx: {}` node in the `package.json`.
|
||||
|
||||
Nx release has a dry-run mode; `nx release version --dry-run`. This will output a diff of what version would be created and on which `package.json` files (if there are multiple).
|
||||
The same mechanism also works for the `changelog` command, e.g. `nx release changelog --dry-run`. In addition to just dry-run, the `changelog` command also has an `--interactive` mode that allows to get the generated changelog in an interactive editor where you can adjust and add extra stuff.
|
||||
|
||||
James then moves on to show how the release process works in a monorepo scenario using pnpm workspaces, where there are multiple NPM packages that need to be published: [jump to the video](https://www.youtube.com/watch?si=BvFvRQjgtwOxu-Dv&t=777&v=p5qW5-2nKqI&feature=youtu.be).
|
||||
|
||||
The monorepo has a `pkg-a` and `pkg-b` where there's a relationship between them as follows: `pkg-a --> pkg-b`.
|
||||
If the `version` command is used in such scenario, it will also be applied to the dependent packages (`pkg-b`) since Nx knows about the dependencies via the project graph.
|
||||
A nice feature is also that the changelog will...
|
||||
|
||||
- automatically group first by type (e.g. features grouped together, fixes etc)
|
||||
- within each type grouping it will be grouped by scope such as pkg-a etc which will be alphabetized
|
||||
- if there’s a fix on the entire repo that’ll come first before the per-package changes
|
||||
|
||||
`nx release changelog --create-release=github` allows to also automatically push the changelog to a Github release.
|
||||
|
||||
When running `nx release publish`, Nx also takes into account the right order of how the packages should go on NPM. Like if there's a relationship `pkg-a --> pkg-b`, Nx automatically detects such dependency and will go and publish pkg-b first and then `pkg-a` which depends on it. That to make sure that even if there's a network error in between packages, you'd still not break anything.
|
||||
|
||||
Future roadmap:
|
||||
|
||||
- ability to customize how the release works by defining it in `nx.json`
|
||||
- “release groups” will allow to group packages and define whether versions should be in sync or versioned independently; filter by projects, or even just publish a subset of projects of a workspace
|
||||
- publishing also automatically takes into account the provenance data on NPM
|
||||
|
||||
## Lightning Talk: What if your stories were — already — your e2e tests?
|
||||
|
||||
**Speaker:** [Katerina Skroumpelou](https://twitter.com/psybercity)
|
||||
[Slides](https://drive.google.com/file/d/1XY8tefqqcM4k4swNPhlSrJiKxurMGiup/view)
|
||||
|
||||
Repo
|
||||
{% github-repository url="https://github.com/mandarini/storybook-play" /%}
|
||||
|
||||
[Live Demo](https://storybook-play.vercel.app/?path=/story/app--primary)
|
||||
|
||||
Watch on YouTube:
|
||||
{% youtube src="https://www.youtube.com/watch?v=SWlvsDNXCsQ" /%}
|
||||
|
||||
Katerina starts by introducing the concept of UI testing for error detection, usability verification, regression prevention but also for validating the user journey and making sure the software is reliability.
|
||||
|
||||
Nx had support to use Storybook for doing component-level tests, both using a Cypress e2e setup as well as Cypress component tests. This is where Nx would fill in to launch Storybook to render a single component and then launch Cypress and run dedicated tests against that served component.
|
||||
Meanwhile Storybook has introduced so-called [Interaction Tests](https://storybook.js.org/docs/writing-tests/interaction-testing) which allow you to do something similar.
|
||||
|
||||
You can read more on [our docs](https://storybook.js.org/docs/writing-tests/interaction-testing). Or check out our corresponding video on Youtube:
|
||||
{% youtube src="https://www.youtube.com/watch?v=SaHoUx-TUs8" /%}
|
||||
|
||||
## Lightning Talk: Nx Cloud Demo
|
||||
|
||||
**Speaker:** [Johanna Pearce](https://twitter.com/jhannapearce)
|
||||
|
||||
Watch on YouTube:
|
||||
{% youtube src="https://www.youtube.com/watch?v=xc6fJpwk4Lo" /%}
|
||||
|
||||
Johanna gives us a tour through the Nx Cloud UI. So there’s not much to say other than [go watch the talk](https://www.youtube.com/watch?v=xc6fJpwk4Lo&feature=youtu.be) :)
|
||||
|
||||
## From DIY to DTE — An Enterprise Experience
|
||||
|
||||
**Speaker:** Adrian Baran
|
||||
[Slides](https://docs.google.com/presentation/d/1LPgCuYoVEIJqqhk8TLq4RAcHrJ5phJuiHHEGA3y_aws/edit?pli=1#slide=id.g25ac4b0c8f6_0_72)
|
||||
{% youtube src="https://www.youtube.com/watch?v=MsUN0wQHPAs" /%}
|
||||
|
||||
Adrian Baran, a Senior Software Engineer at Cisco, embarked on his talk by presenting the status quo of their monorepo which encompasses approximately 150 projects at the start of the experiment.
|
||||
|
||||

|
||||
|
||||
Their CI workflow is powered by CircleCI. For the purpose of clarity, Adrian zoomed in on the frequently executed tasks: `build`, `lint`, and `test`. To execute these tasks, they leverage the [Nx affected](/features/run-tasks#run-tasks-on-projects-affected-by-a-pr) command to avoid running all commands on all projects.
|
||||
|
||||

|
||||
|
||||
Despite that, on average CI runs on PRs still lingered around the 1-hour mark. The immediate remedy? Harnessing CircleCI to parallelize tasks across multiple machines, while continuing to utilize Nx affected. Adrian wrote about that journey in a blog post last year, titled [Nx Affected + CircleCI Parallelism = Faster CI/CD Pipelines](https://medium.com/@abaran30/nx-affected-circleci-parallelism-faster-ci-cd-pipelines-b4edc4caaaac).
|
||||
|
||||
They ended up with a loooot of parallelism:
|
||||
|
||||

|
||||
|
||||
The outcome was promising though — they witnessed a significant reduction, approximately 75%, in execution time, even in scenarios where all projects were influenced by a PR. Done, right?
|
||||
|
||||
However, as their monorepo burgeoned to encompass 400+ projects, the DIY solution began to show its limitations. The incessant parallelization implied the addition of more agents, leading to escalating costs. Furthermore, they observed considerable variability in task durations.
|
||||
|
||||

|
||||
|
||||
Their workaround was to manually allocate specific tasks to distinct agents. This method in addition to capping on a max number of agents helped keep costs under control, but regrettably, it also compromised performance due to the lowered parallelism.
|
||||
|
||||
This is when Cisco looked into running an [Nx Enterprise](/enterprise) pilot, specifically with the goal of adopting [DTE](/ci/features/distribute-task-execution). In the 2nd part of the talk, Adrian outlined their journey of setting up DTE, starting with the generation of the initial CI setup:
|
||||
|
||||
```shell
|
||||
nx g @nx/workspace:ci-workflow --ci=circleci
|
||||
```
|
||||
|
||||
They tuned the generated configuration, but failed to get it properly running, many of the issues leading to resource exhaustion. As a result, they went the route of going on a 1–1 mapping setup between DIY and DTE. That made sure the DTE setup was as close as possible and allowed for a gradual transition approach, which proved invaluable, allowing them to maintain stability whilst progressively migrating to DTE.
|
||||
|
||||
The comparative analysis of their DIY solution versus DTE revealed roughly equivalent performance metrics. However, the DTE framework shone in its simplicity, maintainability, and resource efficiency, achieving similar outcomes with a 75% reduction in resource utilization.
|
||||
|
||||

|
||||
|
||||
### Adrian’s Key Insights:
|
||||
|
||||
- DTE setup warrants a dual-pronged strategy: an immediate plan for initiation and a long-term vision for transition. Notably, Nx DTE supports incremental adoption.
|
||||
- Patience is paramount during the tuning phase to determine the optimal number of agents.
|
||||
- While results might vary, Adrian humorously assures that one can always bank on the Nx team for support. 😅
|
||||
|
||||
## Vanquishing Deployment Dragons with Nx wizardry
|
||||
|
||||
**Speaker:** [Miroslav Jonas](https://twitter.com/meeroslav)
|
||||
[Slides](https://speakerdeck.com/meeroslav/vanquishing-deployment-dragons-with-nx-wizardry)
|
||||
|
||||
Watch on YouTube:
|
||||
{% youtube src="https://www.youtube.com/watch?v=jGF8vo2ChfI" /%}
|
||||
|
||||
There’s not much to say here. Lean back and [immerse yourself into the world of dragons and wizards.](https://www.youtube.com/watch?v=jGF8vo2ChfI)
|
||||
|
||||
## Optimizing your OSS infrastructure with Nx Plugins
|
||||
|
||||
**Speaker:** [Brandon Roberts](https://twitter.com/brandontroberts)
|
||||
[Slides](https://github.com/brandonroberts/nx-conf-2023/blob/main/nx-oss-infrastructure.pdf)
|
||||
Watch on YouTube:
|
||||
{% youtube src="https://www.youtube.com/watch?v=bNuXH25CTO0" /%}
|
||||
|
||||
Brandon discussed his work on the open-source tool [Analog](https://analogjs.org/) and how Nx Plugins are crucial to its development.
|
||||
|
||||
He introduced Analog, shedding light on its foundational ecosystem and the its core features.
|
||||
|
||||

|
||||
|
||||
Nx plays a crucial role in helping integrate and maintain these different tools:
|
||||
|
||||

|
||||
|
||||
How? Brandon dives straight into it by explaining how Nx plugins in particular can be useful, explaining:
|
||||
|
||||
- features of Nx plugins such as generators, executors, automated migrations, presets and how to use them just locally to automate your workspace
|
||||
- how to create a new plugin
|
||||
- the anatomy of a generator and how they can be useful in scaffolding new setups, but also integrating technology, like adding tRPC to your stack, etc.
|
||||
- similarly Nx executors provide a thin abstraction layer over the actual commands, allowing the plugin developer to update the underlying tooling without necessarily disrupting the end user
|
||||
- most importantly allowing to write automatic migrations he can leverage with Analog, like running `nx migrate @analogjs/platform@latest` to update a given workspace automatically to the latest version, across potentially breaking changes
|
||||
|
||||
Brandon highlighted a pivotal aspect for OSS package/framework authors: the power to not just assimilate into pre-existing Nx workspaces via custom Nx plugins but also to steer the entire workspace setup process. This is particularly beneficial when tailored setups specific to individual use cases are required. By leveraging an [Nx preset](/extending-nx/recipes/create-preset), one can achieve this tailored configuration. Brandon also touched upon the possibility of advancing further by constructing an [install package](/extending-nx/recipes/create-install-package) through Nx.
|
||||
|
||||
## Level Up Your Productivity with Nx Console
|
||||
|
||||
**Speaker:** [Jonathan Cammisuli](https://twitter.com/jcammisuli) & [Max Kless](https://twitter.com/MaxKless)
|
||||
[Slides](https://github.com/Cammisuli/nx-conf-2023/tree/main/tools/presentation)
|
||||
|
||||
Watch on YouTube:
|
||||
{% youtube src="https://www.youtube.com/watch?v=TTjVcWCdwVY" /%}
|
||||
|
||||
Jon and Max are the masterminds behind [Nx Console](/getting-started/editor-setup), the Nx IDE extension for Code and JetBrains.
|
||||
|
||||
They mention the release of the JetBrains IDE support earlier this year and give a big shoutout to [Issam](https://github.com/iguissouma) who was the original author of the Nx Console Webstorm community plugin and who helped a lot with the official Nx Console version for JetBrains IDE.
|
||||
|
||||
Jon and Max demo the new IntelliJ version of Nx console and how it properly integrates as well as how they wrote the new UI completely from the ground up [using Lit](/blog/nx-console-gets-lit).
|
||||
|
||||
Clearly they did not spare showing off the awesome new graph capabilities built into Nx Console that allow you to directly navigate to files.
|
||||
|
||||

|
||||
|
||||
## That’s a wrap
|
||||
|
||||
If you enjoyed these, [subscribe to our YouTube channel](https://www.youtube.com/@nxdevtools) where we keep releasing educational content and fun videos.
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,287 +0,0 @@
|
||||
---
|
||||
title: 'Nx 17 has Landed'
|
||||
slug: 'nx-17-release'
|
||||
authors: ['Juri Strumpflohner', 'Zack DeRose']
|
||||
cover_image: '/blog/images/2023-10-20/featured_img.png'
|
||||
tags: [nx, design]
|
||||
---
|
||||
|
||||
We’re excited to announce the release of Nx version 17!
|
||||
|
||||
This article will cover the main things you need to know to get the most out of Nx 17!
|
||||
|
||||
Here’s a Table of Contents so you can skip straight to the updates you care about the most:
|
||||
|
||||
- [It's a Vue-tiful Day for Nx](#its-a-vuetiful-day-for-nx)
|
||||
- [Enhancements to Module Federation Support](#enhancements-to-module-federation-support)
|
||||
- [More Consistent Generator Paths](#more-consistent-generator-paths)
|
||||
- [The NEW Nx AI Chatbot](#the-new-nx-ai-chatbot)
|
||||
- [More Seamless Integration With Nx Cloud](#more-seamless-integration-with-nx-cloud)
|
||||
- [`nx.json` Simplification](#simplification)
|
||||
- [Nx Repo Begins Dog-Fooding Nx Workflows](#nx-repo-dogfooding-nx-workflows)
|
||||
- [Task Graphing Improvements](#task-graphing-improvements)
|
||||
- [`@nx/linter` Renames to `@nx/eslint`](#renamed-to)
|
||||
- [New Experimental Feature: Nx Release](#new-experimental-feature-nx-release)
|
||||
- [Experimental: Nx Project Inference API v2](#experimental-nx-project-inference-api-v2)
|
||||
- [20k Github Stars!!](#20k-github-stars)
|
||||
|
||||
**Prefer a video?**
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/1Z0iA9K1o8M?si=9XxIboXSZ5yxFpIt" /%}
|
||||
|
||||
---
|
||||
|
||||
## It’s a Vue-tiful Day for Nx!
|
||||
|
||||
Ever since we added Vite as a first-class citizen to Nx workspaces (see `@nx/vite`), we started falling in love with the Vue community. The only logical next step: Nx now provides a brand new Vue plugin that is being maintained by the Nx team! (And we're already working on a [Nuxt](https://nuxt.com/) plugin 🤫)
|
||||
|
||||
The first place you might notice this new support is in the `create-nx-workspace` script:
|
||||
|
||||

|
||||
|
||||
This option will create a new Nx workspace with a fresh Vue application, all set up and ready to develop! To add new Vue projects to your existing Nx workspaces, add our new @nx/vue package as a dev dependency to your workspace, e.g.:
|
||||
|
||||
```shell
|
||||
> npm add -D @nx/vue
|
||||
```
|
||||
|
||||
And you’ll have access to Nx generators so that you can generate Vue applications, libraries, components, and more in your workspace:
|
||||
|
||||

|
||||
|
||||
We’re very excited for this support to land, and we’re eager to get it into our user’s hands and see what Nx can do to help Vue developers so we can continue to refine our support and make Vue with Nx an excellent developer experience.
|
||||
|
||||
If you’re eager to learn more, make sure to check out our new [Vue standalone tutorial](/getting-started/tutorials/vue-standalone-tutorial).
|
||||
|
||||
## Enhancements to Module Federation Support
|
||||
|
||||
Nx already had great support for Module Federation — Nx 17 improves on this support:
|
||||
|
||||
### NEW: Typesafe Config
|
||||
|
||||
Projects created with Nx’s generators for module federation will now be created with a `module-federation.config.ts` file (as opposed to a `.js` file). A new `ModuleFederationConfig` interface is also exported from the `@nx/webpack` plugin.
|
||||
|
||||
### Better Typesafety Across Modules
|
||||
|
||||
Nx 17 improves type-safety across modules, so proper type-safety is currently supported across dynamic imports.
|
||||
|
||||

|
||||
|
||||
### NEW GENERATOR: Federate ANYTHING.
|
||||
|
||||
The `@nx/react` and `@nx/angular` now include a `federate-module` generator. This will allow you to create a federated module from any Nx project.
|
||||
|
||||
To run this generator, use the command:
|
||||
|
||||
```shell
|
||||
> nx g federate-module <module name> --path=<path to module to be exposed> --remote=<name of remote exposing module>
|
||||
```
|
||||
|
||||
This will add a new module to the `exposes` map in the specified `remote` application, such that it can be consumed by a `host` application.
|
||||
|
||||
### Managing Module Versions
|
||||
|
||||
Nx now supports targeted versioning for federated modules.
|
||||
|
||||
To create versions for a given project, you can use the `version` property of that project's `package.json` file and then build that project to create the version locally.
|
||||
|
||||
Then, when consuming this library, you can use the `shared` method of the `ModuleFederationConfig`:
|
||||
|
||||
```ts
|
||||
import { ModuleFederationConfig } from '@nx/webpack';
|
||||
|
||||
const config: ModuleFederationConfig = {
|
||||
name: 'my-remote',
|
||||
exposes: {
|
||||
'./Module': 'apps/my-remote/src/app/remote-entry/entry.module.ts',
|
||||
},
|
||||
remotes: ['federated-is-odd'],
|
||||
shared: (libName, configuration) => {
|
||||
if (libName === 'is-odd') {
|
||||
return {
|
||||
singleton: true,
|
||||
strictVersion: true,
|
||||
requiredVersion: '0.0.1',
|
||||
};
|
||||
}
|
||||
return configuration;
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
This config will ensure that version `0.0.1` of the `is-odd` is used.
|
||||
|
||||
## More Consistent Generator Paths
|
||||
|
||||
Similar to the [adjustments we made in 16.8](https://www.youtube.com/watch?v=bw8pRh0iC4A&t=14s) for most of our project-creating generators, v17 brings updates to how component generators work. These updates aim to give you more control over the name and file location of your components.
|
||||
|
||||
Towards this end, we’ve added a new `--nameAndDirectoryFormat` option that you can set to either `as-provided` or `derived`.
|
||||
|
||||
When set to `as-provided`, the generator will use the `name` option for the name of your component and the `directory` option to determine where on your file system to add the component. `as-provided` will be used by default if none is specified.
|
||||
|
||||
When set to `derived`, the generator will try to determine where to create your component based on the `project` option - which will mainly operate how component generators do now.
|
||||
|
||||
In addition, component generators now follow any given casing for component files. For example, let’s say we have an integrated monorepo with a react application called “my-app” and want to add a “Home” component. With Nx 17, you can run the command:
|
||||
|
||||
```shell
|
||||
> nx g component Home --directory=apps/my-app/src/app
|
||||
```
|
||||
|
||||
And a `Home.tsx` file will be added in the `apps/my-app/src/app` directory.
|
||||
|
||||
You can now also build your directory path right into the generator command. For example, the same “Home” component would be created via:
|
||||
|
||||
```shell
|
||||
> nx g component apps/my-app/src/app/Home
|
||||
```
|
||||
|
||||
Finally, generators will now factory in your current working directory, so you can also create this “Home” component via:
|
||||
|
||||
```shell
|
||||
> cd apps/my-app/src/app
|
||||
> nx g component Home
|
||||
```
|
||||
|
||||
## The NEW Nx AI ChatBot
|
||||
|
||||
We’ve added a new AI ChatBot to our docs site. You can access it now at [https://nx.dev/ai-chat](/ai-chat).
|
||||
|
||||

|
||||
|
||||
This feature is still in beta, so please use the thumbs up/thumbs down buttons to provide feedback on whether the chatbot is accurate and helpful!
|
||||
|
||||
## More Seamless Integration With Nx Cloud
|
||||
|
||||
After running the `nx migrate` command to upgrade to Nx 17 and using Nx Cloud, you may have observed the removal of `nx-cloud` from your dev dependencies. Don't worry - Nx Cloud is not only still around but thriving. Instead of having a standalone package, we've integrated the Nx Cloud communication layer directly into the `nx` package. This ensures a seamless connection whenever you opt in and eliminates potential version misalignment concerns with our API endpoint.
|
||||
|
||||
## `nx.json` Simplification
|
||||
|
||||
Our Nx Cloud updates come alongside configuration changes in your `nx.json` file and project configuration.
|
||||
|
||||
Specifically, we’ve added an optional `nxCloudAccessToken` to the `nx.json` file - as long as a token is provided here, we'll make sure that you take advantage of the currently deployed version of Nx Cloud when running commands with Nx.
|
||||
|
||||
We’ve also removed the need to specify `cacheableOperations` at the task-runner level. From now on, every `target` configured in your `project.json` can be defined as cacheable using the `cache` property.
|
||||
|
||||
```json {% fileName="nx.json" %}
|
||||
{
|
||||
"targetDefaults": {
|
||||
"build": {
|
||||
"cache": true,
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you use the `nx migrate` command, all updates will be handled for you using the `targetDefaults` in your `nx.json` file. [More in the docs.](/features/cache-task-results)
|
||||
|
||||
We’ve been working hard at reducing and simplifying all the configuration required for your Nx workspaces. Checkout our latest guide on how to [Reduce Repetitive Configuration](/recipes/running-tasks/reduce-repetitive-configuration) for more, and stay tuned as we’ve got new efforts underway to make this simplification even more appealing!
|
||||
|
||||
## Nx Repo Dog-Fooding Nx Workflows
|
||||
|
||||
At Nx Conf in New York City, we unveiled the next big step for Nx: **Nx Workflows**.
|
||||
|
||||
If you missed it, Simon Critchley walks you through in his [Nx Conf](https://dev.to/nx/nx-conf-2023-recap-53ep) talk:
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/JG1FWfZFByM?si=7_NzxJP8nA7RbFhl" /%}
|
||||
|
||||
Put simply, Nx Workflows represents Nx entering the CI provider arena, where Nx can now provide you with Cloud Computation to run your CI tasks. This creates the foundation for a whole new class of Nx Cloud features that we’re excited to work on in the coming cycles.
|
||||
|
||||
The Nx repo itself is now “dog-fooding” this latest feature (dog-fooding refers to using our tool in our own projects, or “eating our own dog food”), and you can see how it’s going now on our [public Nx Cloud workspace](https://staging.nx.app/orgs/62d013d4d26f260059f7765e/workspaces/62d013ea0852fe0a2df74438/overview).
|
||||
|
||||

|
||||
|
||||
Nx Workflows are still in the experimental phase. We’re running pilots with our enterprise clients and are excited to open this up for everyone soon!
|
||||
|
||||
## Task Graphing Improvements
|
||||
|
||||
Task Caching in Nx is based on a set of “input” files calculated for a given task. You can specify specific files or patterns of files in your project.json for a particular task or in the `targetDefaults` of your `nx.json` to set the default file sets for your inputs.
|
||||
|
||||
There have been some difficulties in determining precisely which files were included and which weren’t for a given task. This is where our latest update to the task graph comes in:
|
||||
|
||||

|
||||
|
||||
You can open this graph using the command:
|
||||
|
||||
```shell
|
||||
> nx graph
|
||||
```
|
||||
|
||||
And then selecting “Task” from the “Project”/”Task” graph dropdown in the top left. Clicking on a specific task now allows you to see a comprehensive list of all files that were factored in as inputs for this task:
|
||||
|
||||

|
||||
|
||||
## `@nx/linter` Renamed to `@nx/eslint`
|
||||
|
||||
After running `nx migrate`, you may have noticed that the `@nx/linter` plugin was removed and replaced with `@nx/eslint`.
|
||||
|
||||
In Nx 17, we removed any remaining traces of `tslint` from our linter package, so this is mainly a simple rename to more accurately describe the package (and to remove any confusion that this package is intended to support linting for other languages/platforms).
|
||||
|
||||
## New Experimental Feature: Nx Release
|
||||
|
||||
`nx release` is a new top level command on the Nx CLI which is designed to help you with versioning, changelog generation, and publishing of your projects:
|
||||
|
||||
- `nx release version` - Determine and apply version updates to projects and their dependents
|
||||
- `nx release changelog` - Generate CHANGELOG.md files and optional Github releases based on git commits
|
||||
- `nx release publish` - Take the freshly versioned projects and publish them to a remote registry
|
||||
|
||||
`nx release` is still experiment and therefore subject to change, but the Nx repo itself is now using these commands to version itself, as well as generate changelogs, [Github releases](https://github.com/nrwl/nx/releases/tag/17.0.3), and publish our packages to npm.
|
||||
|
||||
As we solidify this command, we intend to bring robust support for various versioning and publishing strategies, as well as built-in support for publishing packages or modules to a variety of languages, registries, and platforms.
|
||||
|
||||
For more [checkout our API docs](/nx-api/nx/documents/release), and be sure to catch James Henry’s announcement of this new command at [Nx Conf](https://dev.to/nx/nx-conf-2023-recap-53ep):
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/p5qW5-2nKqI?si=FzpGMJwPVINc1hgL" /%}
|
||||
|
||||
## Experimental: Nx Project Inference API v2
|
||||
|
||||
At Nx, we’re OBSESSED with building a better, more robust experience for our developers. Towards this end, we’re now in [v2 of our Project Inference API](/extending-nx/recipes/project-graph-plugins).
|
||||
|
||||
This API is a way of extending the Nx project graph, which can be particularly helpful for extending Nx to support other languages, allowing Nx to determine where to find and draw boundaries around projects in your workspace. A great example is our very own [Vue plugin](/getting-started/tutorials/vue-standalone-tutorial).
|
||||
|
||||
Interestingly, v2 includes support for dynamic targets as well. This opens up exciting new doors to reducing configuration, and we hope to expand on this to better support our first-party plugins in the near future.
|
||||
|
||||
For most developers, the main thing you need to know is that plugins may now add additional targets that you won’t see in your `project.json` file. To see your actual project configuration, you can now use the command:
|
||||
|
||||
```shell
|
||||
> nx show project <project_name>
|
||||
```
|
||||
|
||||
For plugin authors, check out the [v2 documentation](/extending-nx/recipes/project-graph-plugins) to see how you can take advantage of the new API to deliver a better experience to your users.
|
||||
|
||||
## 20k Github Stars!!
|
||||
|
||||
Nx is SOOO CLOSE to 20,000 stars on github! If Nx has been helpful to you, [please help us get there](https://github.com/nrwl/nx)!
|
||||
|
||||

|
||||
|
||||
## How to Update Nx
|
||||
|
||||
Nx is known to [help you automatically migrate](/features/automate-updating-dependencies) to the new version (including potentially breaking changes). To update simply run:
|
||||
|
||||
```shell
|
||||
> npx nx migrate latest
|
||||
```
|
||||
|
||||
This will update your Nx workspace dependencies, configuration and code to the latest version. After updating your dependencies, run any necessary migrations:
|
||||
|
||||
```shell
|
||||
> npx nx migrate --run-migrations
|
||||
```
|
||||
|
||||
## Wrapping up
|
||||
|
||||
That’s all for now folks! We’re just starting up a new iteration of development on Nx, so be sure to subscribe to our [YouTube channel](https://www.youtube.com/@nxdevtools) to get updates when new features land! Until next time, KEEP WORKING HARD!
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,624 +0,0 @@
|
||||
---
|
||||
title: State Management Nx React Native/Expo Apps with TanStack Query and Redux
|
||||
slug: 'state-management-nx-react-native-expo-apps-with-tanstack-query-and-redux'
|
||||
authors: [Emily Xiong]
|
||||
image: '/blog/images/2023-11-08/featured_img.webp'
|
||||
tags: [nx, React Native]
|
||||
---
|
||||
|
||||
There are currently countless numbers of state management libraries out there. This blog will show you how to use state management for React Native in Nx monorepo with [TanStack Query](https://tanstack.com/query/latest) (which happens to use [Nx on their repo](https://cloud.nx.app/orgs/6412ca9d1c251d000efa21ba/workspaces/6412c827e6da5d7b4a0b1fe3/overview)) and Redux.
|
||||
|
||||
This blog will show:
|
||||
|
||||
- How to set up these libraries and their dev tools
|
||||
- How to build the sample page below in React Native / Expo with state management
|
||||
- How to do unit testing
|
||||
|
||||
It will call an API and show a cat fact on the page, allowing users to like or dislike the data.
|
||||
|
||||

|
||||
|
||||
Github repo: [https://github.com/xiongemi/nx-expo-monorepo](https://github.com/xiongemi/nx-expo-monorepo)
|
||||
|
||||
---
|
||||
|
||||
## Before We Start
|
||||
|
||||
From [TanStack Query documentation](https://tanstack.com/query/latest/docs/framework/react/guides/does-this-replace-client-state), it says:
|
||||
|
||||
- [TanStack Query](https://tanstack.com/query/latest/docs/framework/react/overview) is a **server-state** library.
|
||||
- [Redux](https://redux.js.org/) is a client-state library.
|
||||
|
||||
What is the difference between the server state and the client state?
|
||||
|
||||
In short:
|
||||
|
||||
- Calling an API, dealing with asynchronous data-> server state
|
||||
- Everything else about UI, dealing with synchronous data -> client state
|
||||
|
||||
## Installation
|
||||
|
||||
To use **[TanStack Query / React Query](https://tanstack.com/query/latest)** for the server state, I need to install:
|
||||
|
||||
- Library: [@tanstack/react-query](https://tanstack.com/query/latest)
|
||||
- Dev tools: [@tanstack/react-query-devtools](https://tanstack.com/query/latest/docs/framework/react/devtools)
|
||||
|
||||
I will use **Redux** for everything else.
|
||||
|
||||
- Library: [redux](https://github.com/reduxjs/redux), react-redux, @reduxjs/toolkit
|
||||
- Dev tools: [@redux-devtools/extension](https://github.com/zalmoxisus/redux-devtools-extension)
|
||||
- Logger: [redux-logger](https://github.com/LogRocket/redux-logger), [@types/redux-logger](https://www.npmjs.com/package/@types/redux-logger)
|
||||
- Storage: [redux-persist](https://github.com/rt2zz/redux-persist), [@react-native-async-storage/async-storage](https://github.com/react-native-async-storage/async-storage)
|
||||
|
||||
To install all the above packages:
|
||||
|
||||
```shell
|
||||
#npm
|
||||
npm install @tanstack/react-query @tanstack/react-query-devtools redux react-redux @reduxjs/toolkit @redux-devtools/extension redux-logger @types/redux-logger redux-persist @react-native-async-storage/async-storage --save-dev
|
||||
|
||||
#yarn
|
||||
yarn add @tanstack/react-query @tanstack/react-query-devtools redux react-redux @reduxjs/toolkit @redux-devtools/extension redux-logger @types/redux-logger redux-persist @react-native-async-storage/async-storage --dev
|
||||
|
||||
#pnpm
|
||||
pnpm add @tanstack/react-query @tanstack/react-query-devtools redux react-redux @reduxjs/toolkit @redux-devtools/extension redux-logger @types/redux-logger redux-persist @react-native-async-storage/async-storage --save-dev
|
||||
```
|
||||
|
||||
## Server State with React Query
|
||||
|
||||
### Setup Devtools
|
||||
|
||||
First, you need to add React Query / TanStack Query in the `App.tsx`:
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
const App = () => {
|
||||
const queryClient = new QueryClient();
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{Platform.OS === 'web' && <ReactQueryDevtools />}
|
||||
...
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
Note: the [React Query Devtools](https://tanstack.com/query/latest/docs/framework/react/devtools) currently do not support react native, and it only works on the web, so there is a condition: `{ Platform.OS === ‘web’ && <ReactQueryDevtools />}.`
|
||||
|
||||
For the react native apps, in order to use this tool, you need to use [react-native-web](https://necolas.github.io/react-native-web/) to interpolate your native app to the web app first.
|
||||
|
||||
If you open my Expo app on the web by running `nx start cats` and choose the options `Press w │ open web`, you should be able to use the dev tools and see the state of my react queries:
|
||||
|
||||

|
||||
|
||||
Or you can run npx nx serve cats to launch the app in a web browser and debug from there.
|
||||
|
||||
### Create a Query
|
||||
|
||||
What is a query?
|
||||
|
||||
> “A query is a declarative dependency on an asynchronous source of data that is tied to a unique key. A query can be used with any Promise-based method (including GET and POST methods) to fetch data from a server.” [(https://tanstack.com/query/v4/docs/react/guides/queries)](https://tanstack.com/query/v4/docs/react/guides/queries)
|
||||
|
||||
Now let’s add our first query. In this example, it will be added under `lib/queries` folder. To create a query to fetch a new fact about cats, run the command:
|
||||
|
||||
```shell
|
||||
# expo workspace
|
||||
npx nx generate @nx/expo:lib use-cat-fact --directory=queries
|
||||
|
||||
# react-native workspace
|
||||
npx nx generate @nx/react-native:lib use-cat-fact --directory=queries
|
||||
```
|
||||
|
||||
Or use [Nx Console](/recipes/nx-console):
|
||||
|
||||

|
||||
|
||||
Now notice under libs folder, `use-cat-fact` folder got created under `libs/queries`:
|
||||
|
||||

|
||||
|
||||
If you use React Native CLI, just add a folder in your workspace root.
|
||||
|
||||
For this app, let’s use this API: [https://catfact.ninja/](https://catfact.ninja/). At `libs/queries/use-cat-fact/src/lib/use-cat-fact.ts`, add code to fetch the data from this API:
|
||||
|
||||
```ts
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
export const fetchCatFact = async (): Promise<string> => {
|
||||
const response = await fetch('https://catfact.ninja/fact');
|
||||
const data = await response.json();
|
||||
return data.fact;
|
||||
};
|
||||
|
||||
export const useCatFact = () => {
|
||||
return useQuery({
|
||||
queryKey: ['cat-fact'],
|
||||
queryFn: fetchCatFact,
|
||||
enabled: false,
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
Essentially, you have created a custom hook that calls useQuery function from the TanStack Query library.
|
||||
|
||||
### Unit Testing
|
||||
|
||||
If you render this hook directly and run the unit test with the command `npx nx test queries-use-cat-fact`, this error will show up in the console:
|
||||
|
||||
```shell
|
||||
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
|
||||
1. You might have mismatching versions of React and the renderer (such as React DOM)
|
||||
2. You might be breaking the Rules of Hooks
|
||||
3. You might have more than one copy of React in the same app
|
||||
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
|
||||
```
|
||||
|
||||
To solve this, you need to wrap your component inside the renderHook function from `@testing-library/react-native` library:
|
||||
|
||||
1. **Install Library to Mock Fetch**
|
||||
|
||||
Depending on which library you use to make HTTP requests. (e.g. fetch, axios), you need to install a library to mock the response.
|
||||
|
||||
- If you use `fetch` to fetch data, you need to install `jest*fetch-mock`.
|
||||
- If you use `axios` to fetch data, you need to install `axio*-mock-adapter`.
|
||||
|
||||
For this example, since it uses `fetch`, you need to install `jest-fetch-mock`:
|
||||
|
||||
```shell
|
||||
#npm
|
||||
npm install jest-fetch-mock --save-dev
|
||||
|
||||
#yarn
|
||||
yard add jest-fetch-mock --dev
|
||||
```
|
||||
|
||||
You also need to mock `fetch` library in `libs/queries/use-cat-fact/test-setup.ts`:
|
||||
|
||||
```ts
|
||||
import fetchMock from 'jest-fetch-mock';
|
||||
|
||||
fetchMock.enableMocks();
|
||||
```
|
||||
|
||||
2. **Create Mock Query Provider**
|
||||
|
||||
In order to test out `useQuery` hook, you need to wrap it inside a mock `QueryClientProvider`. Since this mock query provider is going to be used more than once, let’s create a library for this wrapper:
|
||||
|
||||
```shell
|
||||
# expo library
|
||||
npx nx generate @nx/expo:library test-wrapper --directory=queries
|
||||
|
||||
# react native library
|
||||
npx nx generate @nx/react-native:library test-wrapper --directory=queries
|
||||
```
|
||||
|
||||
Then a component inside this library:
|
||||
|
||||
```shell
|
||||
# expo library
|
||||
npx nx generate @nx/expo:component test-wrapper --project=queries-test-wrapper
|
||||
|
||||
# react native library
|
||||
npx nx generate @nx/react-native:component test-wrapper --project=queries-test-wrapper
|
||||
```
|
||||
|
||||
Add the mock `QueryClientProvider` in `libs/queries/test-wrapper/src/lib/test-wrapper/test-wrapper.tsx`:
|
||||
|
||||
```tsx
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import React from 'react';
|
||||
|
||||
export interface TestWrapperProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function TestWrapper({ children }: TestWrapperProps) {
|
||||
const queryClient = new QueryClient();
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default TestWrapper;
|
||||
```
|
||||
|
||||
3. **Use Mock Responses in Unit Test**
|
||||
|
||||
Then this is what the unit test for my query would look like:
|
||||
|
||||
```tsx
|
||||
import { TestWrapper } from '@nx-expo-monorepo/queries/test-wrapper';
|
||||
import { renderHook, waitFor } from '@testing-library/react-native';
|
||||
import { useCatFact } from './use-cat-fact';
|
||||
import fetchMock from 'jest-fetch-mock';
|
||||
|
||||
describe('useCatFact', () => {
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('status should be success', async () => {
|
||||
// simulating a server response
|
||||
fetchMock.mockResponseOnce(
|
||||
JSON.stringify({
|
||||
fact: 'random cat fact',
|
||||
})
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useCatFact(), {
|
||||
wrapper: TestWrapper,
|
||||
});
|
||||
result.current.refetch(); // refetching the query
|
||||
expect(result.current.isLoading).toBeTruthy();
|
||||
|
||||
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
expect(result.current.data).toEqual('random cat fact');
|
||||
});
|
||||
|
||||
it('status should be error', async () => {
|
||||
fetchMock.mockRejectOnce();
|
||||
|
||||
const { result } = renderHook(() => useCatFact(), {
|
||||
wrapper: TestWrapper,
|
||||
});
|
||||
result.current.refetch(); // refetching the query
|
||||
expect(result.current.isLoading).toBeTruthy();
|
||||
|
||||
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
If you use `axios`, your unit test would look like this:
|
||||
|
||||
```tsx
|
||||
// If you use axios, your unit test would look like this:
|
||||
import { TestWrapper } from '@nx-expo-monorepo/queries/test-wrapper';
|
||||
import { renderHook, waitFor } from '@testing-library/react-native';
|
||||
import { useCatFact } from './use-cat-fact';
|
||||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
|
||||
// This sets the mock adapter on the default instance
|
||||
const mockAxios = new MockAdapter(axios);
|
||||
|
||||
describe('useCatFact', () => {
|
||||
afterEach(() => {
|
||||
mockAxios.reset();
|
||||
});
|
||||
|
||||
it('status should be success', async () => {
|
||||
// simulating a server response
|
||||
mockAxios.onGet().replyOnce(200, {
|
||||
fact: 'random cat fact',
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useCatFact(), {
|
||||
wrapper: TestWrapper,
|
||||
});
|
||||
result.current.refetch(); // refetching the query
|
||||
expect(result.current.isLoading).toBeTruthy();
|
||||
|
||||
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
expect(result.current.data).toEqual('random cat fact');
|
||||
});
|
||||
|
||||
it('status should be error', async () => {
|
||||
mockAxios.onGet().replyOnce(500);
|
||||
|
||||
const { result } = renderHook(() => useCatFact(), {
|
||||
wrapper: TestWrapper,
|
||||
});
|
||||
result.current.refetch(); // refetching the query
|
||||
expect(result.current.isLoading).toBeTruthy();
|
||||
|
||||
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Notice that this file imports `TestWrapper` from `@nx-expo-monorepo/queries/test-wrapper`, and it is added to `renderHook` function with `{ wrapper: TestWrapper }`.
|
||||
|
||||
Now you run the test command `nx test queries-use-cat-fact`, it should pass:
|
||||
|
||||
```shell
|
||||
PASS queries-use-cat-fact libs/queries/use-cat-fact/src/lib/use-cat-fact.spec.ts (5.158 s)
|
||||
useCatFact
|
||||
✓ status should be success (44 ms)
|
||||
✓ status should be error (96 ms)
|
||||
```
|
||||
|
||||
### Integrate with Component
|
||||
|
||||
Currently `userQuery` returns the following properties:
|
||||
|
||||
- `isLoading` or `status === 'loading'` - The query has no data yet
|
||||
- `isError` or `status === 'error'` - The query encountered an error
|
||||
- `isSuccess` or `status === 'success'` - The query was successful and data is available
|
||||
|
||||
Now with components controlled by the server state, you can leverage the above properties and change your component to follow the below pattern:
|
||||
|
||||
```ts
|
||||
export interface CarouselProps {
|
||||
isError: boolean;
|
||||
isLoading: boolean;
|
||||
isSuccess: boolean;
|
||||
}
|
||||
|
||||
|
||||
export function Carousel({
|
||||
isSuccess,
|
||||
isError,
|
||||
isLoading,
|
||||
}: CarouselProps) {
|
||||
return (
|
||||
<>
|
||||
{isSuccess && (
|
||||
...
|
||||
)}
|
||||
{isLoading && (
|
||||
...
|
||||
)}
|
||||
{isError && (
|
||||
...
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Carousel;
|
||||
```
|
||||
|
||||
Then in the parent component, you can use the query created above:
|
||||
|
||||
```tsx
|
||||
import { useCatFact } from '@nx-expo-monorepo/queries/use-cat-fact';
|
||||
import { Carousel } from '@nx-expo-monorepo/ui';
|
||||
import React from 'react';
|
||||
|
||||
export function Facts() {
|
||||
const { data, isLoading, isSuccess, isError, refetch, isFetching } =
|
||||
useCatFact();
|
||||
|
||||
return (
|
||||
<Carousel
|
||||
content={data}
|
||||
isLoading={isLoading || isFetching}
|
||||
isSuccess={isSuccess}
|
||||
isError={isError}
|
||||
onReload={refetch}
|
||||
>
|
||||
...
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
If you serve the app on the web and open the [React Query Devtools](https://tanstack.com/query/v4/docs/framework/react/devtools), you should be able to see the query I created `cat-fact` and data in the query.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Redux
|
||||
|
||||
### Create a Library
|
||||
|
||||
First, you need to create a library for redux:
|
||||
|
||||
```shell
|
||||
# expo library
|
||||
npx nx generate @nx/expo:lib cat --directory=states
|
||||
|
||||
# react native library
|
||||
npx nx generate @nx/react-native:lib cat --directory=states
|
||||
```
|
||||
|
||||
This should create a folder under libs:
|
||||
|
||||

|
||||
|
||||
### Create a State
|
||||
|
||||
For this app, it is going to track when users click the like button, so you need to create a state called `likes`.
|
||||
|
||||

|
||||
|
||||
You can use the [Nx Console](/recipes/nx-console) to create a redux slice:
|
||||
|
||||

|
||||
|
||||
Or run this command:
|
||||
|
||||
```shell
|
||||
npx nx generate @nx/react:redux likes --project=states-cat --directory=likes
|
||||
```
|
||||
|
||||
Then update the redux slice at `libs/states/cat/src/lib/likes/likes.slice.ts`:
|
||||
|
||||
```ts
|
||||
import {
|
||||
createEntityAdapter,
|
||||
createSelector,
|
||||
createSlice,
|
||||
EntityState,
|
||||
} from '@reduxjs/toolkit';
|
||||
|
||||
export const LIKES_FEATURE_KEY = 'likes';
|
||||
|
||||
export interface LikesEntity {
|
||||
id: string;
|
||||
content: string;
|
||||
dateAdded: number;
|
||||
}
|
||||
|
||||
export type LikesState = EntityState<LikesEntity>;
|
||||
|
||||
export const likesAdapter = createEntityAdapter<LikesEntity>();
|
||||
|
||||
export const initialLikesState: LikesState = likesAdapter.getInitialState();
|
||||
|
||||
export const likesSlice = createSlice({
|
||||
name: LIKES_FEATURE_KEY,
|
||||
initialState: initialLikesState,
|
||||
reducers: {
|
||||
like: likesAdapter.addOne,
|
||||
remove: likesAdapter.removeOne,
|
||||
clear: likesAdapter.removeAll,
|
||||
},
|
||||
});
|
||||
|
||||
/*
|
||||
* Export reducer for store configuration.
|
||||
*/
|
||||
export const likesReducer = likesSlice.reducer;
|
||||
|
||||
export const likesActions = likesSlice.actions;
|
||||
|
||||
const { selectAll } = likesAdapter.getSelectors();
|
||||
|
||||
const getlikesState = <ROOT extends { likes: LikesState }>(
|
||||
rootState: ROOT
|
||||
): LikesState => rootState[LIKES_FEATURE_KEY];
|
||||
|
||||
const selectAllLikes = createSelector(getlikesState, selectAll);
|
||||
|
||||
export const likesSelectors = {
|
||||
selectAllLikes,
|
||||
};
|
||||
```
|
||||
|
||||
Every time the “like” button gets clicked, you want to store the content of what users liked. So you need to create an entity to store this information.
|
||||
|
||||
```ts
|
||||
export interface LikesEntity {
|
||||
id: string;
|
||||
content: string;
|
||||
dateAdded: number;
|
||||
}
|
||||
```
|
||||
|
||||
This state has 3 actions:
|
||||
|
||||
- like: when users click like
|
||||
- remove: when users cancel the like
|
||||
- clear: when users clear all the likes
|
||||
|
||||
### Root Store
|
||||
|
||||
Then you have to add the root store and create a transform function to stringify the redux state:
|
||||
|
||||
```html
|
||||
<script src="https://gist.github.com/xiongemi/5f364d84f89b647dcaaf7e5437ea789c.js"></script>
|
||||
```
|
||||
|
||||
### Connect Redux State with UI
|
||||
|
||||
Then in `apps/cats/src/app/App.tsx`, you have to:
|
||||
|
||||
- wrap the app inside the `StoreProvider` with the root store to connect with the Redux state.
|
||||
- wrap the app inside `PersistGate` to persist the redux state in the storage
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
import {
|
||||
createRootStore,
|
||||
transformEntityStateToPersist,
|
||||
} from '@nx-expo-monorepo/states/cat';
|
||||
import { Loading } from '@nx-expo-monorepo/ui';
|
||||
import { Provider as StoreProvider } from 'react-redux';
|
||||
|
||||
const App = () => {
|
||||
const persistConfig = {
|
||||
key: 'root',
|
||||
storage: AsyncStorage,
|
||||
transforms: [transformEntityStateToPersist],
|
||||
};
|
||||
const { store, persistor } = createRootStore(persistConfig);
|
||||
|
||||
return (
|
||||
<PersistGate loading={<Loading />} persistor={persistor}>
|
||||
<StoreProvider store={store}>...</StoreProvider>
|
||||
</PersistGate>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
In your component where the like button is located, you need to dispatch the like action. I created a file at `apps/cats/src/app/facts/facts.props.ts`:
|
||||
|
||||
```ts
|
||||
import {
|
||||
likesActions,
|
||||
LikesEntity,
|
||||
RootState,
|
||||
} from '@nx-expo-monorepo/states/cat';
|
||||
import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit';
|
||||
|
||||
const mapDispatchToProps = (
|
||||
dispatch: ThunkDispatch<RootState, void, AnyAction>
|
||||
) => {
|
||||
return {
|
||||
like(item: LikesEntity) {
|
||||
dispatch(likesActions.like(item));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
type mapDispatchToPropsType = ReturnType<typeof mapDispatchToProps>;
|
||||
|
||||
type FactsProps = mapDispatchToPropsType;
|
||||
|
||||
export { mapDispatchToProps };
|
||||
export type { FactsProps };
|
||||
```
|
||||
|
||||
Now you have passed the `like` function to the props of the facts component. Now inside the facts component, you can call the like function from props to dispatch the like action.
|
||||
|
||||
### Debugging
|
||||
|
||||
To debug redux with Expo, I can simply open the Debugger Menu by entering “d” in the console or in the app, then choose the option “Open JS Debugger”.
|
||||
|
||||

|
||||
|
||||
Then you can view my redux logs in the JS Debugger console:
|
||||
|
||||

|
||||
|
||||
Or you can run `npx nx serve cats` to launch the app in web view. Then you can use Redux Devtools and debug the native app like a web app:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Here is a simple app that uses TanStack Query and Redux for state management. These 2 tools are pretty powerful and they manage both server and client state for you, which is easy to scale, test, and debug.
|
||||
|
||||
Nx is a powerful monorepo tool. Together with Nx and these 2 state management tools, it will be very easy to scale up any app.
|
||||
|
||||
- TanStack Query site: [https://tanstack.com/query/latest](https://tanstack.com/query/latest)
|
||||
- Official @nx/expo plugin: [/nx-api/expo](/nx-api/expo)
|
||||
- Official @nx/react-native plugin: [/nx-api/react-native](/nx-api/react-native)
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,315 +0,0 @@
|
||||
---
|
||||
title: Nx Docs AI Assistant
|
||||
slug: 'nx-docs-ai-assistant'
|
||||
authors: [Katerina Skroumpelou]
|
||||
cover_image: '/blog/images/2023-11-21/featured_img.webp'
|
||||
tags: [nx, docs, AI]
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
The [Nx Docs AI Assistant](/ai-chat) is a tool designed to provide users with answers straight from the Nx documentation. In this article I will explain how it is built, and how we ensure accuracy and relevance.
|
||||
|
||||
In the end of this document I have added a [“glossary”](#glossary) of terms that are used throughout this document.
|
||||
|
||||
## Why have an AI assistant for documentation?
|
||||
|
||||
First of all, let’s answer this simple question: why do you need an AI assistant for a documentation site in the first place? Using an AI assistant for documentation search and retrieval can offer a number of benefits for both users and authors. For users, the challenges of navigating through a large volume and density of documentation are alleviated. Unlike static keyword matching, AI enables more personalized and contextual search, allowing for more complex or sophisticated queries beyond simple keywords. This creates a dynamic feedback loop where users can ask follow-up questions, mix and combine documents, and ultimately enjoy an enhanced user experience that goes beyond basic documentation retrieval.
|
||||
|
||||
For authors, a docs AI assistant provides valuable insights into user behavior. It can identify the questions users are frequently asking, pointing to areas where more documentation may be needed. Additionally, if the AI consistently provides unsatisfactory or incorrect responses to certain queries, it could highlight unclear or lacking portions of the documentation. This not only allows for targeted improvements but also makes more parts of the documentation easily accessible to users through intelligent linking. Overall, it can enrich user interaction and help with future content strategy.
|
||||
|
||||
## The Nx Docs AI Assistant Workflow
|
||||
|
||||
### Overview
|
||||
|
||||
In a nutshell, the Nx Docs AI Assistant works in the following way:
|
||||
|
||||
1. Split our docs into smaller chunks
|
||||
2. Create an [embedding](#embeddings) for each chunk
|
||||
3. Save all these embeddings in [Postgres using pgvector (Supabase!)](https://supabase.com/docs/guides/database/extensions/pgvector)
|
||||
4. Get question from the user
|
||||
5. Create embedding for user’s question
|
||||
6. Perform a vector similarity search on your database — bring back all the chunks of your documentation that are similar to the user’s question
|
||||
7. Use the [GPT chat completion](https://platform.openai.com/docs/guides/text-generation/chat-completions-api) function. Pass a prompt, the user’s question and the retrieved chunks from the docs. GPT will then try to extract the relevant facts from these chunks, in order to formulate a coherent answer.
|
||||
|
||||
This is based on the Web Q&A Tutorial from OpenAI [(https://platform.openai.com/docs/tutorials/web-qa-embeddings)](https://platform.openai.com/docs/tutorials/web-qa-embeddings) and Supabase’s Vector Search example [(https://supabase.com/docs/guides/ai/examples/nextjs-vector-search)](https://supabase.com/docs/guides/ai/examples/nextjs-vector-search).
|
||||
|
||||
It’s important to note here that we are not “training the model on our docs”. The model is pretrained. We are just giving the model parts of our docs which are relevant to the user’s question, and the model creates a coherent answer to the question. It’s basically like pasting in ChatGPT a docs page and asking it “how do I do that?”. Except in this case, we’re first searching our documentation and giving GPT only the relevant parts (more about how we do that later in this article), which it can “read” and extract information from.
|
||||
|
||||
## Step 1: Preprocessing our docs
|
||||
|
||||
Every few days, we run an [automated script that will generate embeddings](https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/.github/workflows/generate-embeddings.yml) (numeric/vector representations of words and phrases) for our documentation, and store these embeddings in Supabase. As mentioned above, this step has 3 parts:
|
||||
|
||||
### Split our docs into smaller chunks
|
||||
|
||||
Most of this code follows the example from [Supabase’s Clippy](https://github.com/supabase-community/nextjs-openai-doc-search). It breaks the markdown tree into chunks, it keeps the heading and it also creates a checksum, to keep track of changes.
|
||||
|
||||
Ref in the code: [https://github.com/nrwl/nx/blob/0197444df5ea906f38f06913b2bc366e04b0acc2/tools/documentation/create-embeddings/src/main.mts#L66](https://github.com/nrwl/nx/blob/0197444df5ea906f38f06913b2bc366e04b0acc2/tools/documentation/create-embeddings/src/main.mts#L66)
|
||||
|
||||
This part is copied from: [https://github.com/supabase-community/nextjs-openai-doc-search/blob/main/lib/generate-embeddings.ts](https://github.com/supabase-community/nextjs-openai-doc-search/blob/main/lib/generate-embeddings.ts)
|
||||
|
||||
```js
|
||||
export function processMdxForSearch(content: string) {
|
||||
// …
|
||||
const mdTree = fromMarkdown(content, {});
|
||||
const sectionTrees = splitTreeBy(mdTree, (node) => node.type === 'heading');
|
||||
// …
|
||||
const sections = sectionTrees.map((tree: any) => {
|
||||
const [firstNode] = tree.children;
|
||||
const heading =
|
||||
firstNode.type === 'heading' ? toString(firstNode) : undefined;
|
||||
return {
|
||||
content: toMarkdown(tree),
|
||||
heading,
|
||||
slug,
|
||||
};
|
||||
});
|
||||
return {
|
||||
checksum,
|
||||
sections,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Create an embedding for each chunk
|
||||
|
||||
Using `openai.embeddings.create` function with the model “text-embedding-ada-002” we are creating an embedding for each chunk.
|
||||
|
||||
Ref in the code: [https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/tools/documentation/create-embeddings/src/main.mts#L314](https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/tools/documentation/create-embeddings/src/main.mts#L314)
|
||||
|
||||
```js
|
||||
const embeddingResponse = await openai.embeddings.create({
|
||||
model: 'text-embedding-ada-002',
|
||||
input,
|
||||
});
|
||||
```
|
||||
|
||||
### Save all these embeddings in Postgres using pgvector, on Supabase.
|
||||
|
||||
Store this embedding in Supabase, in a database that has already been created, following the steps mentioned here:
|
||||
|
||||
[https://supabase.com/docs/guides/ai/examples/nextjs-vector-search?database-method=dashboard#prepare-the-database](https://supabase.com/docs/guides/ai/examples/nextjs-vector-search?database-method=dashboard#prepare-the-database)
|
||||
|
||||
Essentially, we are setting up two PostgreSQL tables on Supabase. Then, we are inserting the embeddings into these tables.
|
||||
|
||||
Ref in code: [https://github.com/nrwl/nx/blob/master/tools/documentation/create-embeddings/src/main.mts#L327](https://github.com/nrwl/nx/blob/master/tools/documentation/create-embeddings/src/main.mts#L327)
|
||||
|
||||
```js
|
||||
const { data: pageSection } = await supabaseClient
|
||||
.from('nods_page_section')
|
||||
.insert({
|
||||
page_id: page.id,
|
||||
slug,
|
||||
heading,
|
||||
longer_heading,
|
||||
content,
|
||||
url_partial,
|
||||
token_count,
|
||||
embedding,
|
||||
}); // …
|
||||
```
|
||||
|
||||
## Step 2: User query analysis and search
|
||||
|
||||
When a user poses a question to the assistant, we calculate the embedding for the user’s question. The way we do that is, again, using openai.embeddings.create function with the model text-embedding-ada-002.
|
||||
|
||||
Ref in code: [https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/nx-dev/nx-dev/pages/api/query-ai-handler.ts#L58](https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/nx-dev/nx-dev/pages/api/query-ai-handler.ts#L58)
|
||||
|
||||
```js
|
||||
const embeddingResponse: OpenAI.Embeddings.CreateEmbeddingResponse =
|
||||
await openai.embeddings.create({
|
||||
model: 'text-embedding-ada-002',
|
||||
input: sanitizedQuery + getLastAssistantMessageContent(messages),
|
||||
});
|
||||
```
|
||||
|
||||
The assistant compares the query embedding with these documentation embeddings to identify relevant sections. This comparison is essentially measuring how close the query’s vector is to the documentation vectors. The closer they are, the more related the content. The way this works is that it sends the user’s question embedding to Supabase, to a PostgreSQL function, which runs a vector comparison between the user’s question embedding and the stored embeddings in the table. The PostgreSQL function returns all the similar documentation chunks.
|
||||
|
||||
The function that is used uses the dot product between vectors to calculate similarity. For normalized vectors, the dot product is equivalent to cosine similarity. Specifically, when two vectors A and B are normalized (i.e., their magnitudes are each 1), the cosine similarity between them is the same as their dot product. The OpenAI embeddings are normalized to length 1, so cosine similarity and dot product will produce the same results.
|
||||
|
||||
Ref in code: [https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/nx-dev/nx-dev/pages/api/query-ai-handler.ts#L70](https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/nx-dev/nx-dev/pages/api/query-ai-handler.ts#L70)
|
||||
|
||||
```js
|
||||
const { data: pageSections } = await supabaseClient.rpc('match_page_sections', {
|
||||
embedding,
|
||||
// …
|
||||
});
|
||||
```
|
||||
|
||||
## Step 3: Generating a Response
|
||||
|
||||
With the relevant sections (documentation chunks) identified and retrieved, GPT (the generative AI) steps in. Using the relevant sections as context and following a systematic approach, GPT crafts a response.
|
||||
|
||||
This approach the AI is instructed to use (in the **prompt**) is the following:
|
||||
|
||||
- Identify CLUES from the query and documentation.
|
||||
- Deduce REASONING based solely on the provided Nx Documentation.
|
||||
- EVALUATE its reasoning, ensuring alignment with Nx Documentation.
|
||||
- Rely on previous messages for contextual continuity.
|
||||
|
||||
### Ensuring Quality
|
||||
|
||||
If there’s no matching section in the documentation for a query, the script throws a “no_results” error. So, after the initial search in the docs (PostgreSQL function), if the search returns no results (no vectors found that are similar enough to the user’s question vector), the process stops, and our Assistant replies that it does not know the answer.
|
||||
|
||||
### The use of useChat function
|
||||
|
||||
It’s necessary here to clarify that we use the useChat [(https://sdk.vercel.ai/docs/api-reference/use-chat)](https://sdk.vercel.ai/docs/api-reference/use-chat) function of the [Vercel AI SDK](https://sdk.vercel.ai/docs/introduction). This function, as mentioned in the docs, does the following:
|
||||
|
||||
> It enables the streaming of chat messages from your AI provider, manages the state for chat input, and updates the UI automatically as new messages are received.
|
||||
|
||||
It essentially takes care of the following things:
|
||||
|
||||
1. You don’t have to worry about manually creating a “messages” array to store your “conversation” (messages you exchange) with the GPT endpoint
|
||||
2. You don’t have to manually implement the streaming functionality in your UI
|
||||
|
||||
Then, in your React component, you can call this function directly, and get the messages object from it, to render your messages in your UI. It exposes input, handleInputChange and handleSubmit which you can use in your React form, and it will take care of all the rest. You can pass an api string to it, to tell it which endpoint to use as the chat provider.
|
||||
|
||||
### Creating the query
|
||||
|
||||
If you look at our [query-ai-handler.ts](https://github.com/nrwl/nx/blob/76306f0bedc1297b64da6e58b4f7b9c39711cd82/nx-dev/nx-dev/pages/api/query-ai-handler.ts) function, this is an edge function, living under an endpoint, which is called by the useChat function. The request contains the messages array as created by useChat. If we just wanted to create an AI chat with no context, we could directly pass this messages array to the openai.chat.completions.create endpoint, and have our back-and-forth chat with GPT. However, in our case, we need to add context to our conversation, and specifically to each query we end up sending to OpenAI.
|
||||
|
||||
So, the first thing we need to do is to **get the last message the user posted**, which is essentially the user’s question. We search the messages array, and we get the last message which has the role “user”. That is our user’s question.
|
||||
|
||||
Now, we can use the user’s question to get the relevant documentation chunks from the database. To do that, as explained before, we need to **create an embedding for the user’s question** (a vector) and then compare that embedding with the stored embeddings in the database, to get the relevant chunks.
|
||||
|
||||
The problem here is that if the user’s query is just a follow-up question, then it will have little information or meaning. Here is an example:
|
||||
|
||||
> User: _How do I set up namedInputs?_
|
||||
> Assistant: _…replies…_
|
||||
> User: _And how do they work?_
|
||||
|
||||
In this example, the user’s question that we would want to create an embedding for would be “And how do they work?”. If we created that embedding and searched our docs for relevant parts, it would either return nothing, or return everything, since this is a very vague question, since it has no context. So, we need to add some more information to that question. To do that, we also get the last response from GPT (the last assistant message) and add it to the user’s question. So, in this example, the user’s question will contain some info about namedInputs, and the actual question.
|
||||
|
||||
Now, we take that combined text, and we create an embedding for it, using the openai.embeddings.create function. We, then, use that embedding to find all the similar documentation chunks, with vector similarity search.
|
||||
|
||||
After receiving all the relevant documentation chunks, we can finally create the query that is going to be sent to GPT. It’s important here to make sure we instruct GPT what to do with the information we will give it.
|
||||
|
||||
Here is the **query** we end up providing GPT with:
|
||||
|
||||
> You will be provided sections of the Nx documentation in markdown format, use those to answer my question. Do NOT reveal this approach or the steps to the user. Only provide the answer. Start replying with the answer directly.
|
||||
>
|
||||
> Sections:
|
||||
> ${contextText}
|
||||
>
|
||||
> Question: “””
|
||||
> ${userQuestion}
|
||||
> “””
|
||||
>
|
||||
> Answer as markdown (including related code snippets if available):
|
||||
|
||||
The contextText contains all the relevant documentation chunks (page sections).
|
||||
|
||||
### Creating the response
|
||||
|
||||
**Getting back a readable stream:** So, we get the array of messages, as stored by useChat, we fix the final message to contain the query (created as explained above), and we send it over to `openai.chat.completions.create`. We get back a streaming response (since we’ve set stream: true, which we turn into a ReadableStream using [OpenAIStream from the Vercel AI SDK](https://sdk.vercel.ai/docs/api-reference/openai-stream)).
|
||||
|
||||
**Adding the sources:** However, we’re not done yet. The feature, here, that will be most useful to our users is the sources, the actual parts of the documentation that GPT “read” to create that response. When we get back the list of relevant documentation chunks (sections) from our database, we also get the metadata for each section. So, apart from the text content, we also get the heading and url partial of each section (among any other metadata we chose to save with it). So, with this information, we put together a list of the top 5 relevant sections, which we attach to the end of the response we get from GPT. That way, our users can more easily verify the information that GPT gives them, but also they can dive deeper into the relevant docs themselves. It’s all about exploring and retrieving relevant information, after all.
|
||||
|
||||
**Sending the final response to the UI:** With the sources appended to the response, we return a StreamingTextResponse from our edge function, which the useChat function receives, and appends to the messages array automatically.
|
||||
|
||||
## Allow user to reset the chat
|
||||
|
||||
As explained, each question and answer relies on the previous questions and answers of the current chat. If a user needs to ask something completely irrelevant or different, we are giving the user the ability to do so by providing a “Clear chat” button, which will reset the chat history, and start clean.
|
||||
|
||||
## Gathering feedback and evaluating the results
|
||||
|
||||
It’s very important to gather feedback from the users and evaluate the results. Any AI assistant is going to give wrong answers, because it does not have the ability to critically evaluate the responses it creates. It relies on things it has read, but not in the way a human relies on them. It generates the next most probable word (see glossary for generative AI below). For that reason, it’s important to do the following things:
|
||||
|
||||
1. Inform users that they should always double-check the answers and do not rely 100% on the AI responses
|
||||
2. Provide users with feedback buttons and/or a feedback form, where they can evaluate whether a response was good or bad. At Nx we do that, and we also associate each button click with the question the user asked, which will give us an idea around which questions the AI gets right or wrong.
|
||||
3. Have a list of questions that you ask the AI assistant, and evaluate its responses internally. Use these questions as a standard for any changes made in the assistant.
|
||||
|
||||
## Wrapping up
|
||||
|
||||
In this guide, we’ve explored the intricacies of the Nx Docs AI Assistant, an innovative tool that enhances the experience of both users and authors of Nx documentation. From understanding the need for an AI assistant in navigating complex documentation to the detailed workflow of the Nx Docs AI Assistant, we have covered the journey from preprocessing documentation to generating coherent and context-aware responses.
|
||||
|
||||
Let’s see at some key takeaways:
|
||||
|
||||
**Enhanced User Experience:** The AI assistant significantly improves user interaction with documentation by offering personalized, context-aware responses to queries. This not only makes information retrieval more efficient but also elevates the overall user experience.
|
||||
|
||||
**Insights for Authors:** By analyzing frequently asked questions and areas where the AI struggles, authors can pinpoint documentation gaps and areas for improvement, ensuring that the Nx documentation is as clear and comprehensive as possible.
|
||||
|
||||
**OpenAI API utilization:** The use of embeddings, vector similarity search, and GPT’s generative AI capabilities demonstrate a sophisticated approach to AI-driven documentation assistance. This blend of technologies ensures that users receive accurate and relevant responses.
|
||||
|
||||
**Continuous Learning and Improvement:** The system’s design includes mechanisms for gathering user feedback and evaluating AI responses, which are crucial for ongoing refinement and enhancement of the assistant’s capabilities.
|
||||
|
||||
**Transparency and User Trust:** By openly communicating the limitations of the AI and encouraging users to verify the information, the system fosters trust and promotes responsible use of AI technology.
|
||||
|
||||
**Accessibility and Efficiency:** The AI assistant makes Nx documentation more accessible and navigable, especially for complex or nuanced queries, thereby saving time and enhancing productivity and developer experience.
|
||||
|
||||
## Future steps
|
||||
|
||||
OpenAI released the Assistants API, which takes the burden of chunking the docs, creating embeddings, storing the docs in a vector database, and querying that database off the shoulders of the developers. This new API offers all these features out of the box, removing the need to create a customized solution, as the one explained above. It’s still in beta, and it remains to be seen how it’s going to evolve, and if it’s going to overcome some burdens it poses at the moment. You can [read more about the new Assistants API in this blog post](https://pakotinia.medium.com/openais-assistants-api-a-hands-on-demo-110a861cf2d0), which contains a detailed demo on how to use it for documentation q&a.
|
||||
|
||||
## Glossary
|
||||
|
||||
### Core concepts
|
||||
|
||||
I find it useful to start by explaining what some terms — which are going to be used quite a lot throughout this blog post — mean.
|
||||
|
||||
### Embeddings
|
||||
|
||||
#### What they are
|
||||
|
||||
In the context of machine learning, embeddings are a type of representation for text data. Instead of treating words as mere strings of characters, embeddings transform them into **vectors** (lists of numbers) in a way that captures their meanings. In embeddings, vectors are like digital fingerprints for words or phrases, converting their essence into a series of numbers that can be easily analyzed and compared.
|
||||
|
||||
#### Why they matter
|
||||
|
||||
With embeddings, words or phrases with similar meanings end up having **vectors** that are close to each other, making it easier to compare and identify related content.
|
||||
|
||||
### Generative AI
|
||||
|
||||
#### What it is
|
||||
|
||||
Generative AI, the technology driving the Nx Docs AI Assistant, is a subset of AI that’s trained, not just to classify input data, but to generate new content.
|
||||
|
||||
#### How it works
|
||||
|
||||
Generative AI operates like a sophisticated software compiler. Just as a compiler takes in high-level code and translates it into machine instructions, generative AI takes in textual prompts and processes them through layers of neural network operations, resulting in detailed and coherent text outputs. It’s like providing a programmer with a high-level task description, and they write the necessary code to achieve it, except here the ‘programmer’ is the AI, and the ‘code’ is the generated text response.
|
||||
|
||||
#### What Does “Generation” Mean in AI Context?
|
||||
|
||||
In AI, especially with natural language processing models, “generation” refers to the process of producing sequences of data, in our case, text. It’s about creating content that wasn’t explicitly in the training data but follows the same patterns and structures.
|
||||
|
||||
#### How Does GPT Predict the Next Word?
|
||||
|
||||
For our Nx Docs AI assistant we use GPT. GPT, which stands for “Generative Pre-trained Transformer”, works using a predictive mechanism. At its core, it’s trained to predict the next word in a sentence. When you provide GPT with a prompt, it uses that as a starting point and keeps predicting the next word until it completes the response or reaches a set limit.
|
||||
|
||||
It’s like reading a sentence and trying to guess the next word based on what you’ve read so far. GPT does this but by using a massive amount of textual data it has seen during training, enabling it to make highly informed predictions.
|
||||
|
||||
### Context and Prompting — their role in AI models
|
||||
|
||||
#### Context
|
||||
|
||||
In the context of AI, “context” refers to the surrounding information, data, or conditions that provide a framework or background for understanding and interpreting a specific input, ensuring that the AI’s responses or actions are relevant, coherent, and meaningful in a given situation
|
||||
|
||||
#### Prompts
|
||||
|
||||
The prompt acts as an initial “seed” that guides the AI’s output. While the AI is trained on vast amounts of text, it relies on the prompt for context. For example, a prompt like “tell me about cats” might result in a broad answer, but “summarize the history of domesticated cats” narrows the model’s focus.
|
||||
|
||||
By refining prompts, users can better direct the AI’s response, ensuring the output matches their intent. In essence, the prompt is a tool to direct the AI’s vast capabilities to a desired outcome.
|
||||
|
||||
### The GPT Chat Completion Roles
|
||||
|
||||
### System
|
||||
|
||||
The “System” role typically sets the “persona” or the “character” of the AI. It gives high-level instructions on how the model should behave during the conversation. We start the instructions with “You are a knowledgeable Nx representative.” We also instruct the model about the format of its answer: “Your answer should be in the form of a Markdown article”. You can read the full instructions on GitHub.
|
||||
|
||||
#### User
|
||||
|
||||
The “User” role is straightforward. This is the input from the end-user, which the AI responds to. The user’s query becomes the User role message. This role guides what the AI should be talking about in its response. It’s a direct prompt to the AI to generate a specific answer. In our case, we take the user’s query, and we add it in a longer prompt, which specific steps the model must follow (as explained above). That way, the model focuses on the specific steps we’ve laid out, making it the immediate context for generating the answer. This is one more step towards more accurate answers based on our documentation only. Inside the prompt, which has the instructions, and the user’s query, we always add the context text as well, which are the relevant parts that are retrieved from the Nx Documentation.
|
||||
|
||||
#### Assistant
|
||||
|
||||
This role, in the context of OpenAI’s chat models, is the response of the AI. Previous Assistant responses can be included in the chat history to provide context, especially if a conversation has back-and-forth elements. This helps the model generate coherent and contextually relevant responses in a multi-turn conversation.
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](/community)
|
||||
- [Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
|
||||
- [Speed up your CI](https://nx.app/)
|
||||
@@ -1,336 +0,0 @@
|
||||
---
|
||||
title: Unit Testing Expo Apps With Jest
|
||||
slug: 'unit-testing-expo-apps-with-jest'
|
||||
authors: [Emily Xiong]
|
||||
cover_image: '/blog/images/2023-11-22/featured_img.webp'
|
||||
tags: [nx, unit testing]
|
||||
---
|
||||
|
||||
In my latest [blog](https://dev.to/nx/step-by-step-guide-to-creating-an-expo-monorepo-with-nx-3b17), I successfully navigated through the steps of setting up an Expo Monorepo with [Nx](). The next challenge? Testing! This blog dives into:
|
||||
|
||||
- Crafting effective unit tests for Expo components utilizing Jest
|
||||
- Addressing common issues encountered during unit testing
|
||||
|
||||
Repo:
|
||||
{% github-repository url="https://github.com/xiongemi/nx-expo-monorepo" /%}
|
||||
|
||||
## Stacks
|
||||
|
||||
Here’s my setup
|
||||
|
||||
- Testing framework: [jest](https://jestjs.io/)
|
||||
- Testing library: [@testing-library/react-native](https://callstack.github.io/react-native-testing-library/)
|
||||
- Jest Preset: [jest-expo](https://www.npmjs.com/package/jest-expo)
|
||||
|
||||
## Writing and Running Unit Tests
|
||||
|
||||
When you use Nx, it not only configures and sets up Jest, but also creates a default unit test for every expo component that is being generated. Here’s what that looks like:
|
||||
|
||||
```typescript
|
||||
import { render } from '@testing-library/react-native';
|
||||
import React from 'react';
|
||||
|
||||
import Loading from './loading';
|
||||
|
||||
describe('Loading', () => {
|
||||
it('should render successfully', () => {
|
||||
const { root } = render(<Loading />);
|
||||
expect(root).toBeTruthy();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
To run all unit tests for a given project, use:
|
||||
|
||||
```shell
|
||||
npx nx test <project-name>
|
||||
```
|
||||
|
||||
Here’s the output of running this for my example app:
|
||||
|
||||

|
||||
|
||||
When it comes to writing tests, the [React Native Testing Library](https://callstack.github.io/react-native-testing-library/docs/api-queries) is a game-changer for writing cleaner unit tests in React Native applications. Its intuitive query API simplifies the process of selecting elements within your components, making it straightforward to write more maintainable and readable tests. You mark elements with a `testID`
|
||||
|
||||
```html
|
||||
<Headline testID="title">{film.title}</Headline>
|
||||
```
|
||||
|
||||
Then in the test file, you can use the function `getByTestId` to query `testID`:
|
||||
|
||||
```typescript
|
||||
const { getByTestId } = render(<your component>);
|
||||
expect(getByTestId('title')).toHaveTextContent(...);
|
||||
```
|
||||
|
||||
You can find more options for querying elements on the official React Native Testing Library docs: [https://callstack.github.io/react-native-testing-library/docs/api-queries](https://callstack.github.io/react-native-testing-library/docs/api-queries).
|
||||
|
||||
## Troubleshooting Common Issues When Writing Tests
|
||||
|
||||
However, unit tests do not always pass. Here are some common errors I ran into and how to resolve them.
|
||||
|
||||
### **Error: AsyncStorage is null.**
|
||||
|
||||
I am using the library `@react-native-async-storage/async-storage`, and I got the below error when running unit testing:
|
||||
|
||||
```shell
|
||||
\[@RNC/AsyncStorage\]: NativeModule: AsyncStorage is null.
|
||||
|
||||
To fix this issue try these steps:
|
||||
|
||||
• Rebuild and restart the app.
|
||||
|
||||
• Run the packager with \`--reset-cache\` flag.
|
||||
|
||||
• If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then rebuild and re-run the app.
|
||||
|
||||
• If this happens while testing with Jest, check out docs how to integrate AsyncStorage with it: https://react-native-async-storage.github.io/async-storage/docs/advanced/jest
|
||||
|
||||
If none of these fix the issue, please open an issue on the Github repository: https://github.com/react-native-async-storage/async-storage/issues
|
||||
|
||||
5 | import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||
6 | import { Platform } from 'react-native';
|
||||
> 7 | import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
```
|
||||
|
||||
The issue is that `@react-native-async-storage/async-storage` library can only be used in `NativeModule`. Since unit testing with Jest only tests JS/TS file logic, I need to mock this library.
|
||||
|
||||
In the app’s test-setup.ts file, add the below lines:
|
||||
|
||||
```typescript
|
||||
jest.mock('@react-native-async-storage/async-storage', () =>
|
||||
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
|
||||
);
|
||||
```
|
||||
|
||||
## Error: Could not find “store”
|
||||
|
||||
I am using Redux for state management, and I got this error for my stateful components:
|
||||
|
||||
```
|
||||
Could not find "store" in the context of "Connect(Bookmarks)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(Bookmarks) in connect options.
|
||||
```
|
||||
|
||||
To fix this, the simple way is to mock a redux store. I need to install [redux-mock-store](https://github.com/reduxjs/redux-mock-store) and its typing:
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="npm" %}
|
||||
|
||||
```shell
|
||||
npm install redux-mock-store @types/redux-mock-store --save-dev
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="yarn" %}
|
||||
|
||||
```shell
|
||||
yarn add redux-mock-store @types/redux-mock-store --dev
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
Then I can create a mock store using this library like the below code:
|
||||
|
||||
```typescript
|
||||
import configureStore, { MockStoreEnhanced } from 'redux-mock-store';
|
||||
|
||||
const mockStore = configureStore<any>(\[\]);
|
||||
|
||||
let store: MockStoreEnhanced<any>;
|
||||
|
||||
beforeEach(() => {
|
||||
store = mockStore({});
|
||||
store.dispatch = jest.fn();
|
||||
});
|
||||
```
|
||||
|
||||
For example, one of my stateful components’ unit test will become:
|
||||
|
||||
```typescript
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react-native';
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore, { MockStoreEnhanced } from 'redux-mock-store';
|
||||
import { RootState, initialRootState } from '@nx-expo-monorepo/states/cat';
|
||||
|
||||
import Bookmarks from './bookmarks';
|
||||
|
||||
describe('Bookmarks', () => {
|
||||
const mockStore = configureStore<RootState>(\[\]);
|
||||
|
||||
let store: MockStoreEnhanced<RootState>;
|
||||
|
||||
beforeEach(() => {
|
||||
store = mockStore(initialRootState);
|
||||
store.dispatch = jest.fn();
|
||||
});
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { container } = render(
|
||||
<Provider store={store}>
|
||||
<Bookmarks />
|
||||
</Provider>
|
||||
);
|
||||
expect(container).toBeTruthy();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
The above code will apply the initial redux state to my components.
|
||||
|
||||
### Error: No QueryClient set
|
||||
|
||||
Because I use [TanStack Query](https://tanstack.com/query/latest) , when I run unit tests, I have this error:
|
||||
|
||||
```
|
||||
No QueryClient set, use QueryClientProvider to set one
|
||||
```
|
||||
|
||||
This error occurred because I used `useQuery` from `@tanstack/react-query` in my component; however, in this unit test, the context of this hook is not provided.
|
||||
|
||||
To solve this, I can just mock the `useQuery` function:
|
||||
|
||||
```typescript
|
||||
import * as ReactQuery from '@tanstack/react-query';
|
||||
|
||||
jest.spyOn(ReactQuery, 'useQuery').mockImplementation(
|
||||
jest.fn().mockReturnValue({
|
||||
data: 'random cat fact',
|
||||
isLoading: false,
|
||||
isSuccess: true,
|
||||
refetch: jest.fn(),
|
||||
isFetching: false,
|
||||
isError: false,
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### Error: Couldn’t find a navigation object
|
||||
|
||||
If you use `@react-navigation` library for navigation, and inside your component, there are hooks from this library like `useNavigation` and `useRoute`, you are likely to get this error:
|
||||
|
||||
```
|
||||
Couldn't find a navigation object. Is your component inside NavigationContainer?
|
||||
```
|
||||
|
||||
The fix this, I need to mock the `@react-nativgation/native` library. In the app’s test-setup.ts file, I need to add:
|
||||
|
||||
```typescript
|
||||
jest.mock('@react-navigation/native', () => {
|
||||
return {
|
||||
useNavigation: () => ({
|
||||
navigate: jest.fn(),
|
||||
dispatch: jest.fn(),
|
||||
setOptions: jest.fn(),
|
||||
}),
|
||||
useRoute: () => ({
|
||||
params: {
|
||||
id: '123',
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
### SyntaxError: Unexpected token ‘export’
|
||||
|
||||
I got this error when using a library with ECMAScript Module (ESM), such as `[udid](https://github.com/uuidjs/uuid)`:
|
||||
|
||||
```
|
||||
/Users/emilyxiong/Code/nx-expo-monorepo/node\_modules/uuid/dist/esm-browser/index.js:1
|
||||
({"Object.<anonymous>":function(module,exports,require,\_\_dirname,\_\_filename,jest){export { default as v1 } from './v1.js';
|
||||
^^^^^^
|
||||
|
||||
SyntaxError: Unexpected token 'export'
|
||||
|
||||
5 | import { connect } from 'react-redux';
|
||||
6 | import 'react-native-get-random-values';
|
||||
> 7 | import { v4 as uuidv4 } from 'uuid';
|
||||
```
|
||||
|
||||
Jest does not work with ESM out of the box. The simple solution is to map this library to the CommonJS version of this library.
|
||||
|
||||
In the app’s `jest.config.ts`, there should be an option called `moduleNameMapper`. The library I used is called `uuid`, so I need to add the map `uuid: require.resolve(‘uuid’)` under `moduleNameMapper`. So when the code encounters imports from `uuid` library, it will resolve the CommonJS version of it:
|
||||
|
||||
```typescript
|
||||
module.exports = {
|
||||
moduleNameMapper: {
|
||||
uuid: require.resolve('uuid'),
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Alternatively, I can also mock this library in the test files:
|
||||
|
||||
```typescript
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
jest.mock('uuid', () => {
|
||||
return {
|
||||
v4: jest.fn(() => 1),
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
## Error: Jest encountered an unexpected token
|
||||
|
||||
I got this error when I was importing from a library such as [react-native-vector-icons](https://github.com/oblador/react-native-vector-icons):
|
||||
|
||||
```
|
||||
console.error
|
||||
Jest encountered an unexpected token
|
||||
|
||||
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
|
||||
|
||||
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
|
||||
|
||||
By default "node\_modules" folder is ignored by transformers.
|
||||
|
||||
Here's what you can do:
|
||||
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
|
||||
• If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
|
||||
• To have some of your "node\_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
|
||||
• If you need a custom transformation specify a "transform" option in your config.
|
||||
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
|
||||
|
||||
You'll find more details and examples of these config options in the docs:
|
||||
https://jestjs.io/docs/configuration
|
||||
For information about custom transformations, see:
|
||||
https://jestjs.io/docs/code-transformation
|
||||
```
|
||||
|
||||
To fix this, add this library name to `transformIgnorePatterns` in the app's jest.config.ts.
|
||||
|
||||
What is `transformIgnorePatterns`? The `transformIgnorePatterns` allows developers to specify which files shall be transformed by Babel. `transformIgnorePatterns` is an array of regexp pattern strings that should be matched against all source file paths before the transformation. If the file path matches any patterns, it will not be transformed by Babel.
|
||||
|
||||
By default, Jest will ignore all the files under node_modules and only transform the files under the project’s src.
|
||||
|
||||
However, some libraries such as `react-native-paper` or `react-native-svg`, the library files are in `.ts` or `.tsx`. These files are not compiled to `js`. So I need to add these libraries' names to `transformIgnorePatterns`, so these libraries will be transformed by Babel along with my project. source file. The default generated `jest.config.js` already has:
|
||||
|
||||
```
|
||||
transformIgnorePatterns: \[
|
||||
'node\_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.\*|@expo-google-fonts/.\*|react-navigation|@react-navigation/.\*|@unimodules/.\*|unimodules|sentry-expo|native-base|react-native-svg)',
|
||||
\]
|
||||
```
|
||||
|
||||
If I have an error related to a library with an unexpected token, I need to check whether they are compiled or not.
|
||||
|
||||
- If this library source files are already transformed to `.js`, then its name should match regex, so it would be ignored, so it will NOT be transformed.
|
||||
- If this library source files are NOT transformed to `.js` (e.g. still in `.ts` or `.tsx`), then its name should NOT match regex, so it will be transformed.
|
||||
|
||||
## Summary
|
||||
|
||||
Here are some common errors that I will probably run into while doing unit testing. The solution to most problems is to find a way to mock a library that is not relevant to my component logic.
|
||||
|
||||
With Nx, you do not need to explicitly install any testing library, so you can dive right in and focus on writing the tests rather than spending time on setup.
|
||||
|
||||
## Learn more
|
||||
|
||||
- [Add Cypress, Playwright, and Storybook to Nx Expo Apps](https://medium.com/@emilyxiong/add-cypress-playwright-and-storybook-to-nx-expo-apps-1d3e409ce834)
|
||||
- 🧠 [Nx Docs](/getting-started/intro)
|
||||
- 👩💻 [Nx GitHub](https://github.com/nrwl/nx)
|
||||
- 💬 [Nx Community Discord](https://go.nx.dev/community)
|
||||
- 📹 [Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
|
||||
- 🚀 [Speed up your CI](https://nx.app/)
|
||||
@@ -1,215 +0,0 @@
|
||||
---
|
||||
title: Nx 17.2 Update
|
||||
slug: 'nx-17-2-release'
|
||||
authors: [Zack DeRose]
|
||||
cover_image: '/blog/images/2023-12-20/featured_img.png'
|
||||
tags: [nx, changelog, release]
|
||||
---
|
||||
|
||||
It’s been a bit since we launched [Nx 17](/blog/nx-17-release)! In this article, we’ll go over some of the new developments and improvements that have landed in Nx 17.2:
|
||||
|
||||
- [Nx Closes In On 4 Million Weekly NPM Downloads!!](#nx-closes-in-on-4-million-weekly-npm-downloads)
|
||||
- [New Simplified Project Configuration On the Way](#new-simplified-project-configuration-on-the-way)
|
||||
- [Rust for Speed, Typescript for Extensibility](#rust-for-speed-typescript-for-extensibility)
|
||||
- [Module Federation Updates](#module-federation-updates)
|
||||
- [Nx Release Updates](#nx-release-updates)
|
||||
- [Angular 17 (AND NgRx 17) Support](#angular-17-and-ngrx-17-support)
|
||||
- [Smart Monorepos — Fast CI](#smart-monorepos-fast-ci)
|
||||
- [New Canary Releases](#new-canary-releases)
|
||||
- [Upcoming Release Livestream](#upcoming-release-livestream)
|
||||
- [Automatically Update Nx](#automatically-update-nx)
|
||||
|
||||
## Nx Closes In On 4 Million Weekly NPM Downloads!!
|
||||
|
||||
2023 has been a great year for Nx! We worked with a lot of the teams out there building fantastic open source tools. And you can see the results: we made [Vite](https://vitejs.dev/) a first class citizen in many of our Nx plugins, we added support for [Rspack](https://www.youtube.com/watch?v=jGTE7xAcg24), streamlined our Node experience by adding an Nx team maintained [Fastify plugin](https://www.youtube.com/watch?si=P5MPIiD_mxTpQStY&v=LHLW0b4fr2w&feature=youtu.be), support for Storybook interaction testing, welcomed Playwright to the family and much more, continuing our missing to push developer productivity to the limits!
|
||||
|
||||
And our downloads on NPM keep confirming this. We are about to cross 4 million downloads per week.
|
||||
|
||||

|
||||
|
||||
If this made you curious, keep an eye on [our blog](/blog) or [X/Twitter](https://twitter.com/nxdevtools) as we’re going to release a 2023 year recap blog post next week.
|
||||
|
||||
## New Simplified Project Configuration On the Way
|
||||
|
||||
Adoption is crucial, and simplicity is the driver for adoption. Last year we heavily optimized how you can use Nx in an existing project. Just drop the `nx` package (or run `nx init`) and that's it. Nx understands your workspace and efficiently runs your `package.json` scripts.
|
||||
|
||||
Using Nx at that level is definitely useful as you get intelligent parallelization, task pipelines and caching. But it is just the tip of the iceberg of what Nx is actually capable of. Nx plugins provide much more, especially in terms of DX and developer productivity by taking away some of the burden of configuring your monorepo tooling. But many devs new to Nx found it harder to get started with them initially and incrementally migrating to Nx plugins wasn’t as straightforward as we’d wanted it to be.
|
||||
|
||||
This is something that’s gonna change drastically in 2024. And we’ve layed the first cornerstone for that. But it is behind a feature flag still as we’re streamlining the last bits. The goal?
|
||||
|
||||
- Going almost configuration-less (good defaults, you customize when you need to)
|
||||
- Allowing easy drop-in of Nx plugins into existing workspaces (provides immediate productivity gains, but stays out of your way)
|
||||
|
||||
This opens up a series of possibilities which we’re already super excited about. You’ll hear more about this in the new year ;)
|
||||
|
||||
## Rust for Speed, Typescript for Extensibility
|
||||
|
||||
At Nx, we’ve heavily embraced Typescript from the beginning, and we’ve been very happy with that decision.
|
||||
|
||||
If you’ve been paying attention to previous release announcements though, you’ve probably noticed that we’ve been moving more and more of the computationally intensive and performance critical pieces of the core of Nx from Typescript to Rust.
|
||||
|
||||
That trend continues in Nx 17.2 with Nx [using Rust for its task hashing by default](https://github.com/nrwl/nx/pull/19617). There’s no adjustments needed for this change, and Nx will continue to behave the same way, just faster!
|
||||
|
||||
{% tweet url="https://twitter.com/victorsavkin/status/1724464283227234420" /%}
|
||||
|
||||
## Module Federation Updates
|
||||
|
||||
Module Federation has been a particularly hot topic lately, and 17.2 is coming in with some great enhancements to Nx’s already best-in-class support for Module Federation!
|
||||
|
||||
To start, we’ve greatly reduced the CPU and memory used for standing up your Module Federation “web” locally. These enhancements should be great news to larger workspaces using a Module Federation approach, where there were heavy CPU and memory costs to serving your entire federation locally.
|
||||
|
||||
We accomplished these improvements by batching any applications that are not being watched for changes (listed with the `--devRemotes` option) to a single server, rather than a unique server for each application. We also parallelized the builds of these static applications when you start up your serve! You can now use the `--parallel={number}` option to specify how many builds you want going at any given time.
|
||||
|
||||
In addition to performance improvements, we’ve brought the concept of dynamic federation to our React module federation support. Dynamic federation allows a host application to dynamically load remotes via the manifest file.
|
||||
|
||||
You can generate your react module federation workspace now to use dyanmic federation via the `--dynamic` flag:
|
||||
|
||||
```shell
|
||||
nx generate @nx/react:host acme --remotes=nx --dynamic
|
||||
```
|
||||
|
||||
Or you can use the utility itself by importing from `@nx/react/mf`:
|
||||
|
||||
```ts
|
||||
import { loadRemoteModule } from '@nx/react/mf';
|
||||
```
|
||||
|
||||
Lastly, we have an [example repo](https://github.com/jaysoo/nx-react-vite-module-federation) available now to illustrate how to create a plugin for Module Federation using Nx with Vite! This is something that we are monitoring and may provide an out-of-the-box solution to this in a future release!
|
||||
|
||||
## Nx Release Updates
|
||||
|
||||
Nx 17 launched with the new `nx release` capability in the Nx CLI. Since then we've been streamlining the experience, accounting for various edge cases and release scenarios. (extensive docs are being worked on rn ;)
|
||||
|
||||
To give you full flexibility, in 17.2, we’ve added a programmatic API, which will allow you to easily write custom release scripts:
|
||||
|
||||
```ts
|
||||
import { releaseChangelog, releasePublish, releaseVersion } from 'nx/release';
|
||||
|
||||
(async () => {
|
||||
const { workspaceVersion, projectsVersionData } = await releaseVersion({
|
||||
specifier: 'minor',
|
||||
});
|
||||
await releaseChangelog({
|
||||
versionData: projectsVersionData,
|
||||
version: workspaceVersion,
|
||||
});
|
||||
await releasePublish();
|
||||
process.exit(0);
|
||||
})();
|
||||
```
|
||||
|
||||
This script above demonstrates how you can use this API to create your own script for updating your workspace’s version, then creating a changelog, and then publishing your package!
|
||||
|
||||
We’ve also added first class support for independently released projects, meaning you can now target a specific project for release with the `--projects` command. For example, you can create a new version for just one project in your workspace with the command:
|
||||
|
||||
```shell
|
||||
nx release version patch --project=my-project
|
||||
```
|
||||
|
||||
## Angular 17 (AND NgRx 17) Support
|
||||
|
||||

|
||||
|
||||
[Angular](https://angular.dev/) is in the middle of [a HUGE renaissance](https://blog.angular.dev/introducing-angular-v17-4d7033312e4b), between their new logo, new docs site, and introduction of some awesome features like Signals.
|
||||
|
||||
Nx is here to support the transition! Nx has always been a great fit for Angular, and now supports Angular 17 as well as NgRx 17.
|
||||
|
||||
To automatically migrate existing workspaces to Angular v17, run the commands:
|
||||
|
||||
```shell
|
||||
nx migrate latest
|
||||
nx migrate --run-migrations
|
||||
```
|
||||
|
||||
You can also use the `--interactive` flag if you want to migrate your workspace to the latest version of Nx while staying on your current version of Angular:
|
||||
|
||||
```shell
|
||||
> nx migrate latest --interactive
|
||||
✔ Do you want to update to TypeScript v5.2? (Y/n) · true
|
||||
✔ Do you want to update the Angular version to v17? (Y/n) · false
|
||||
|
||||
> NX The migrate command has run successfully.
|
||||
|
||||
- package.json has been updated.
|
||||
- migrations.json has been generated.
|
||||
|
||||
> NX Next steps:
|
||||
|
||||
- Run 'nx migrate --run-migrations'
|
||||
```
|
||||
|
||||
## Smart Monorepos — Fast CI
|
||||
|
||||
We just gave our Nx homepage a small facelift, including a new tagline, subtagline and illustration to better reflect Nx’s mission statement.
|
||||
|
||||

|
||||
|
||||
When you enter the monorepo space, having good local development experience and tooling to support you is one thing, scaling is the other. And scaling comes with multiple challenges, from scaling teams working on the monorepo to maintaining high throughput on CI.
|
||||
|
||||
The latter is a common headache and we’ve seen companies struggle. With Nx we’re moving into the direction of becoming your e2e solution for monorepos, where we don’t just cover your local dev experience, but also provide robust and scalable solutions on CI.
|
||||
|
||||

|
||||
|
||||
We’re super excited to have launched “Nx Agents” to Early Access. If you haven’t seen Victor’s video yet about how he reduced e2e tests from 90 minutes to 10, then make sure to [check it out](/ci/features/distribute-task-execution).
|
||||
|
||||
**“Nx Agents”** are the next iteration of DTE, providing a more flexible, cost effective and more performant approach to distribution on CI. This includes things like being able to dynamically allocate machines based on the size of the PR and flaky task detection and re-running. Also, it can be configured with a single line:
|
||||
|
||||
```yaml
|
||||
- name: Start CI run
|
||||
run: 'npx nx-cloud start-ci-run --distributes-on="8 linux-medium-js"'
|
||||
...
|
||||
```
|
||||
|
||||
You can run Nx Agents on any CI provider. If you’re curious, [sign up for early access](https://go.nx.dev/nx-agents-ea)!
|
||||
|
||||
## New Canary Releases
|
||||
|
||||
We’ve added a new npm release tag: canary!
|
||||
|
||||

|
||||
|
||||
This canary release is created via a [cron job](https://github.com/nrwl/nx/blob/master/.github/workflows/publish.yml#L5C61-L5C61) that will regularly publish the current contents of the master branch of Nx.
|
||||
|
||||
You can give the canary version a try new for a new workspace using the command:
|
||||
|
||||
```shell
|
||||
npx create-nx-workspace@canary
|
||||
```
|
||||
|
||||
This should be useful for previewing new not-yet released features!
|
||||
|
||||
## Upcoming Release Livestream
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/OXXTUjSO1hs?si=iDFETYdpg-0BrAIP" title="Nx 17.2 Release Livestream" /%}
|
||||
|
||||
We’re going live in January with the Nx team to go over these updates as well! Be sure to click the link to get notified when we go live! And feel free to come with your questions in the chat!
|
||||
|
||||
## Automatically Update Nx
|
||||
|
||||
Updating Nx and its plugins is easy as we ship an [automated migration command](/features/automate-updating-dependencies).
|
||||
|
||||
```shell
|
||||
npx nx migrate latest
|
||||
```
|
||||
|
||||
After updating your dependencies, run any necessary migrations.
|
||||
|
||||
```shell
|
||||
npx nx migrate --run-migrations
|
||||
|
||||
```
|
||||
|
||||
## Wrapping up
|
||||
|
||||
That’s all for now folks! We’re just starting up a new iteration of development on Nx, so be sure to subscribe to our [YouTube channel](https://www.youtube.com/@nxdevtools) to get updates when new features land! Until next time, KEEP WORKING HARD!
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,392 +0,0 @@
|
||||
---
|
||||
title: Nx — Highlights of 2023
|
||||
slug: 'nx-highlights-of-2023'
|
||||
authors: [Juri Strumpflohner, Victor Savkin, Zack DeRose]
|
||||
cover_image: /blog/images/2023-12-28/featured_img.png
|
||||
tags: [nx, nx-cloud]
|
||||
---
|
||||
|
||||
It is that time again: getting flooded by Year of Review blog posts. We did it last year, and we should do it again! So here we go, and be warned, 2023 was massive!!
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
- [Top 10 Nx Highlights of 2023](#top-10-nx-highlights-of-2023)
|
||||
- [TypeScript for Extensibility — Rust for Speed](#typescript-for-extensibility-rust-for-speed)
|
||||
- [First Class Vite Support](#first-class-vite-support)
|
||||
- [Nx’t Level Publishing](#nxt-level-publishing)
|
||||
- [Improved Node Backend Development: Fastify and Docker](#improved-node-backend-development-fastify-and-docker)
|
||||
- [Nx Console support for IntelliJ](#nx-console-support-for-intellij)
|
||||
- [Playwright for e2e testing](#playwright-for-e2e-testing)
|
||||
- [TypeScript Packaging and Batch Mode](#typescript-packaging-and-batch-mode)
|
||||
- [Nx team maintained Vue plugin](#nx-team-maintained-vue-plugin)
|
||||
- [Extending Nx: Local Generators, Build your Own CLI, Verdaccio Support](#extending-nx-local-generators-build-your-own-cli-verdaccio-support)
|
||||
- [Module Federation](#module-federation)
|
||||
- [Many OSS repos adopt Nx](#many-oss-repos-adopt-nx)
|
||||
- [Nx Community](#nx-community)
|
||||
- [New Content & Improved Docs](#new-content-improved-docs)
|
||||
- [New Tagline: Smart Monorepos — Fast CI](#new-tagline-smart-monorepos-fast-ci)
|
||||
- [Nx Conf](#nx-conf)
|
||||
- [Looking ahead — 2024](#looking-ahead-2024)
|
||||
- [Solving CI](#solving-ci)
|
||||
- [Solving the Simplicity vs Power Dilemma](#solving-the-simplicity-vs-power-dilemma)
|
||||
|
||||
## Top 10 Nx Highlights of 2023
|
||||
|
||||
We shipped a ton of features in 2023. You can find all our release blog posts and release-related info here: [https://nx.dev/changelog](/changelog) or check out our [Dev.to collection](https://dev.to/nx).
|
||||
|
||||
We've picked out 10 highlights for you.
|
||||
|
||||
### TypeScript for Extensibility — Rust for Speed
|
||||
|
||||
At Nx, we’ve heavily embraced Typescript from the beginning and we’ve been very happy with that decision. Nx also stands as the [fastest JS monorepo tool](https://github.com/vsavkin/large-monorepo) available, demonstrating that adopting TypeScript does not necessarily compromise speed. However, we don’t stop here. To push the boundaries further, we started to rewrite the most performance critical and computationally intensive parts of the Nx core in Rust.
|
||||
|
||||
Our initial focus was on [rewriting the task hasher](https://dev.to/nx/nx-158-rust-hasher-nx-console-for-intellij-deno-node-and-storybook-27ng#rustifying-the-nx-hasher), previously reliant on Git with a Node fallback. This shift to Rust brings a noticeable performance boost, particularly in large repositories, while maintaining the same user experience.
|
||||
|
||||
Following this, we revamped the TypeScript dependency resolution, observing an almost 5x speed increase with our Rust-based approach over the traditional TSC method.
|
||||
|
||||
{% tweet url="https://twitter.com/juristr/status/1726977598218199302" /%}
|
||||
|
||||
Such enhancements are especially crucial for the efficient [project graph calculation](/features/explore-graph). As we continue to evolve Nx, Rust will play a key role in optimizing performance-critical components. This strategic use of Rust complements our ongoing commitment to TypeScript, ensuring Nx remains as extensible and powerful as ever.
|
||||
|
||||
### First Class Vite Support
|
||||
|
||||
Vite is rapidly transforming the landscape of frontend development! Its refreshing simplicity and innovative approach have made a significant mark in the developer community. What truly stands out is not just Vite’s technological aspect but also how its team approaches and grows the community around the tool. Vite stands for speed and community, values that deeply resonate with us here at Nx.
|
||||
|
||||
Our collaboration with our friends in the Vite core team has been incredibly fruitful. Vite is not just compatible but a first-class option with many of Nx’s frontend plugins. When you create a new Nx powered React workspace, Vite (and [Vitest](https://vitest.dev/)) are your default options.
|
||||
|
||||

|
||||
|
||||
We also built some powerful code generators that not only facilitate a seamless [transition from Webpack to Vite](/nx-api/vite/generators/configuration#nxviteconfiguration) but also pave the way for an effortless [migration from a CRA-based setup](/recipes/adopting-nx/adding-to-existing-project) to a modern Nx + Vite based workspace. To see this process in action, [check out this short video](https://www.youtube.com/watch?v=zvYb7XCLQzU).
|
||||
|
||||
[AnalogJS](https://analogjs.org/) — the fullstack Angular meta-framework which also heavily builds on top of Vite — is using the `@nx/vite` plugin to power its Angular and Nx based workspaces.
|
||||
|
||||
We also spoke at both editions of [ViteConf](https://viteconf.org/23/). If you’re curious check out [Juri’s talk about High Speed Monorepos](https://www.youtube.com/watch?si=A5Nkg3rxe3DlODc4&v=TiU-hdn7_To&feature=youtu.be) and this year’s talk by [Katerina on Streamlining your Vite dev flow with Nx](https://www.youtube.com/watch?si=A5Nkg3rxe3DlODc4&v=TiU-hdn7_To&feature=youtu.be).
|
||||
|
||||
### Nx’t Level Publishing
|
||||
|
||||
Open source libraries and frameworks share a common necessity: the need to develop multiple packages cohesively and efficiently while managing their versioning and publishing to NPM. Nx has emerged as a go-to choice for handling such open source monorepos (as we’ll explore further in the next section of this blog post). Until recently, one area Nx did not address directly was versioning and release management. Traditionally, this gap has been filled with tools like [release-it](https://github.com/release-it/release-it), [changesets](https://github.com/changesets/changesets), or custom Node scripts, similar to our approach in the Nx repository.
|
||||
|
||||
However, many in our community have expressed a desire for a more native, integrated experience for versioning and publishing, akin to what Lerna offers. In response to this feedback, we’ve introduced [the “nx release” command](/features/manage-releases), a solution designed to seamlessly integrate these processes into the Nx workflow.
|
||||
|
||||
James Henry gave a deep dive talk of an early version of it at this year’s Nx Conf:
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/p5qW5-2nKqI" /%}
|
||||
|
||||
Since its introduction, the “nx release” feature has significantly evolved, leveraging the power of the Nx project graph to effectively understand inter-package dependencies. This understanding is crucial as it allows for:
|
||||
|
||||
- Versioning packages offering support for both independent and “locked” versioning strategies.
|
||||
- Releasing packages in the correct sequence, ensuring dependency integrity.
|
||||
|
||||
Beyond these core functionalities, the feature also includes a robust grouping mechanism, supports semantic versioning, and changelog generation. Additionally, it provides various release targets, such as GitHub and NPM. For those having special requirements, the [programmatic API](/features/manage-releases#using-the-programmatic-api-for-nx-release) offers maximum flexibility.
|
||||
|
||||
### Improved Node Backend Development: Fastify and Docker
|
||||
|
||||
Colocating frontend and backend code within the same monorepo has become a popular practice. It greatly facilitates cross-functional teams and helps ensure end-to-end type safety. Although you can use [other backend stacks](https://www.nx-dotnet.com/) with Nx, Node is a popular backend companion for JS based frontends. We had support for [Express](https://expressjs.com/) and [NestJS](https://nestjs.com/) backend for a while.
|
||||
|
||||
This year we added another popular option: [Fastify](https://fastify.dev/). Known for its high performance, excellent developer experience, and useful built-in features like logging, Fastify aligns well with Nx’s modular software design principles. Its extensible and modular nature complements the Nx philosophy perfectly.
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/LHLW0b4fr2w?si=WeTqm5msQxssQ_d3" /%}
|
||||
|
||||
In tandem with Fastify, we’ve also introduced [Docker support](https://youtu.be/LHLW0b4fr2w?feature=shared) for Node deployments.
|
||||
|
||||
### Nx Console support for IntelliJ
|
||||
|
||||
Nx Console has evolved from an experimental side project of the Nx team to a core part for enhancing your productivity when working in a monorepo. Being integrated right into your editor it can provide useful information and functionality right where you need it, whether that’s running commands, [providing contextual autocomplete support](https://twitter.com/juristr/status/1653032530474565638) or the ability to explore the project and task graph.
|
||||
|
||||

|
||||
|
||||
This year we not only added a lot of new features to Nx Console, but also rewrote its [internals](https://blog.nrwl.io/nx-console-gets-lit-ca339743ff4f) which paved the way to expand Nx Console to other code editors: **JetBrains IDEs.**
|
||||
|
||||
Yes, this means you can now use the latest Nx Console directly in your [Webstorm IDE](https://www.jetbrains.com/webstorm/). Read the [announcement blog post](https://blog.nrwl.io/expanding-nx-console-to-jetbrains-ides-8a5b80fff2d7) for all the details or go ahead and install Nx Console if you didn’t already:
|
||||
|
||||
- [Nx Console for VSCode](https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console)
|
||||
- [Nx Console for IntelliJ](https://plugins.jetbrains.com/plugin/21060-nx-console)
|
||||
|
||||
### Playwright for e2e testing
|
||||
|
||||
2023 saw Nx introduce official support for [Playwright](https://playwright.dev/) — a popular testing tool from Microsoft.
|
||||
|
||||
In this video, Zack DeRose explains how you can use Playwright to test both a single web application, as well as stand up a full-stack system — including a backend server and a frontend application — and write and run tests using Playwright:
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/k1U3PuBrZFQ" /%}
|
||||
|
||||
The repo for this video can also be found [here](https://github.com/nrwl/tic-tac-toe-playwright).
|
||||
|
||||
Generally, Playwright fits in the Nx ecosystem as a tool that developers can use as a possible alternative to Cypress — a popular e2e testing tool that Nx has supported for a long time now! In addition to publishing an official [@nx/playwright package](https://www.npmjs.com/package/@nx/playwright), running the command to create a new workspace will now prompt for Playwright as an option for React, Angular, and Vue stacks:
|
||||
|
||||
```shell
|
||||
$ npx create-nx-workspace@latest
|
||||
> NX Let's create a new workspace [https://nx.dev/getting-started/intro]
|
||||
✔ Which stack do you want to use? · react
|
||||
✔ What framework would you like to use? · none
|
||||
✔ Integrated monorepo, or standalone project? · integrated
|
||||
✔ Which bundler would you like to use? · vite
|
||||
? Test runner to use for end to end (E2E) tests …
|
||||
Cypress [ https://www.cypress.io/ ]
|
||||
Playwright [ https://playwright.dev/ ]
|
||||
None
|
||||
```
|
||||
|
||||
Similar options will appear when using Nx generators to create new frontend web applications for an existing workspace.
|
||||
|
||||
### TypeScript Packaging and Batch Mode
|
||||
|
||||
TypeScript has won. It has become the prevalent way of writing modern JavaScript applications. And we kept improving our support to streamline development and polish some of the rough edges. Like properly defining secondary package entry points. You can define them in the `package.json`, but both creating these entries but especially maintaining them can be quite painful.
|
||||
|
||||
```
|
||||
{
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": "./src/index.js",
|
||||
"./foo": "./src/foo.js",
|
||||
"./bar": "./src/bar.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
So in [v16.8](https://blog.nrwl.io/nx-16-8-release-e38e3bb503b5#7b41) we added the ability to automatically have these generated for you by defining the `additionalEntryPoints` and `generateExportsField` when using the `@nx/js` plugin.
|
||||
|
||||
```
|
||||
// packages/my-awesome-lib/project.json
|
||||
{
|
||||
"name": "my-awesome-lib",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
...
|
||||
"options": {
|
||||
"main": "packages/my-awesome-lib/src/index.ts",
|
||||
...
|
||||
"additionalEntryPoints": ["packages/my-awesome-lib/src/foo.ts"],
|
||||
"generateExportsField": true
|
||||
},
|
||||
},
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Similarly we improved the ability to package your TS libraries in multiple formats (ESM and CJS). When using the `@nx/rollup` plugin, all you need to do is define the `format` property in your config:
|
||||
|
||||
```
|
||||
// packages/my-awesome-lib/project.json
|
||||
{
|
||||
"name": "my-awesome-lib",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/rollup:rollup",
|
||||
...
|
||||
"options": {
|
||||
"main": "packages/my-awesome-lib/src/index.ts",
|
||||
...
|
||||
"format": ["esm", "cjs"],
|
||||
"additionalEntryPoints": ["packages/my-awesome-lib/src/foo.ts"],
|
||||
"generateExportsField": true
|
||||
},
|
||||
},
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here’s a video that walks you through:
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/Vy4d0-SF5cY?si=mHatqRPRqHAK0X9o" /%}
|
||||
|
||||
But we wouldn’t be talking about Nx if we didn’t also look into speeding up TypeScript compilation for large monorepos. We called it “[batch mode](/showcase/benchmarks/tsc-batch-mode)”. When enabling batch mode, Nx leverages the underlying [project graph](/features/explore-graph) to generate TypeScript project references behind the scenes for you, to fully leverage TS incremental building. The results are amazing. According to [our benchmarks](https://github.com/nrwl/large-ts-monorepo), batch mode has the potential to speed up Typescript compilation by up to 5x for large monorepos.
|
||||
|
||||

|
||||
|
||||
### Nx team maintained Vue plugin
|
||||
|
||||
After adding Vite as a first-class citizen of Nx workspaces, it was only a matter of time before Nx started offering official support to Vue!
|
||||
|
||||
Vue is currently the second most popular frontend framework (according to npm downloads) behind React and slightly ahead of Angular.
|
||||
|
||||

|
||||
|
||||
The first place your might notice Nx’s support for Vue is in the `create-nx-workspace` script:
|
||||
|
||||

|
||||
|
||||
The option above will create a new Nx workspace with a fresh new Vue application, all set up and ready to develop! To add new Vue projects to an existing Nx workspace, you can also add our `@nx/vue` package as a dev dependency to your workspace:
|
||||
|
||||
```shell
|
||||
% npm add -D @nx/vue
|
||||
```
|
||||
|
||||
And you’ll then have access to Nx generators so you can create Vue applications, libraries, and more in your workspace!
|
||||
|
||||

|
||||
|
||||
Checkout out our [Vue standalone tutorial](/getting-started/tutorials/vue-standalone-tutorial) for more, as well as our [Vue API docs](/nx-api/vue), and stay tuned as Nx prepares to offer more Vue support (including support for [Nuxt](https://nuxt.com/), a full-stack framework built around Vue) in the near future!
|
||||
|
||||
### Extending Nx: Local Generators, Build your Own CLI, Verdaccio Support
|
||||
|
||||
Extensibility is at the heart of Nx, serving as the cornerstone of its flexibility. It enables the Nx core team to continually expand capabilities through dedicated plugins and simultaneously paves the way for a rich array of [community plugin contributions](/plugin-registry). Furthermore, Nx’s adaptable nature is particularly beneficial for large enterprises, as it allows for the creation of custom automation solutions, specifically tailored to meet their unique organizational needs.
|
||||
|
||||
In 2023 we kept improving Nx’s extensibility, unifying the Nx plugin development model and how you develop workspace-local automations. You can now scaffold a new plugin into your Nx workspace and run it right away which makes it an interesting approach to automate your monorepo.
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/myqfGDWC2go?si=q6_9JReS1nF8d3pZ" /%}
|
||||
|
||||
When creating automations with Nx you cannot just enhance existing Nx workspaces, but also develop a complete [Nx preset](/extending-nx/recipes/create-preset) that controls the entire appearance of an Nx workspace. Basically your own, personalized `create-nx-workspace`. You can publish and then use your preset like:
|
||||
|
||||
```shell
|
||||
npx create-nx-workspace myrepo --preset=@yourpkg/nx-preset
|
||||
```
|
||||
|
||||
We wanted to make building on top of Nx even more pleasant, allowing you to introduce your own branding by [building your own CLI with Nx](https://www.youtube.com/watch?v=ocllb5KEXZk). The [Qwik-Nx](https://github.com/qwikifiers/qwik-nx) repo is a great example where they allow you to scaffold a new Nx workspace for Qwik development with:
|
||||
|
||||
```shell
|
||||
npx create-qwik-nx
|
||||
```
|
||||
|
||||
And finally, we extracted our own [Verdaccio](https://verdaccio.org/) setup that we’ve been using to run our e2e tests in the [Nx repo](https://github.com/nrwl/nx) s.t. you can use it for your own plugin development as well. Check out [this video](https://www.youtube.com/watch?v=t1c925TzrzE) for a walkthrough on how this works.
|
||||
|
||||
### Module Federation
|
||||
|
||||
[Module Federation](https://medium.com/swlh/webpack-5-module-federation-a-game-changer-to-javascript-architecture-bcdd30e02669) is an exciting new feature of Webpack 5 that has gained a significant amount of interest in 2023.
|
||||
|
||||
Simply put, Module Federation allows a Javascript application running in a browser to dynamically load code from another application hosted at a different url, while facilitating optimal loading of shared dependencies.
|
||||
|
||||
This is an exciting development as it allows a paradigm shift in how you can architect, build, and deploy Javascript applications! And this is especially exciting for monorepo fans, as Nx has best-in-class support for module federation that makes a Module Federation approach easy to adopt and simple to understand!
|
||||
|
||||
Currently, our `@nx/angular` and `@nx/react` plugins both have generators to [create a “host” application](/recipes/module-federation/create-a-host) that will load and consume federated modules from [“remote” applications](/recipes/module-federation/create-a-remote), which you can also generate using Nx. Then, by running a simple command with Nx, you can serve all applications required for your host application with the command:
|
||||
|
||||
```shell
|
||||
nx serve host-application --devRemotes=remote-application
|
||||
```
|
||||
|
||||
Where in the example above your host application is named “host-application” and a remote application that you want live updates on as you’re developing is named “remote-application”.
|
||||
|
||||
Throughout 2023, we’ve continued to increase Nx’s support and general dev experience around Module Federation, including [adding a generator to federate an existing module](/recipes/module-federation/federate-a-module), improving the local developer experience by improving local webserver performance, and introducing the concept of [Dynamic Module Federation](/recipes/angular/dynamic-module-federation-with-angular#advanced-angular-micro-frontends-with-dynamic-module-federation) which will allow you to dynamically specify the location of your remote applications via a “module-federation.manifest.json” file!
|
||||
|
||||
At Nx, we’re excited about the Module Federation support we offer for our users, and think that it has many interesting applications when paired with Nx’s CI capabilities, in particular allowing for [much shorter build times](/concepts/module-federation/faster-builds-with-module-federation#faster-builds-with-module-federation) especially for larger Angular applications.
|
||||
|
||||
## Many OSS repos adopt Nx
|
||||
|
||||
By simply installing the `nx` package (or initializing with `nx init` in any project or monorepo), you already get some cool features:
|
||||
|
||||
- Advanced task scheduling, including task pipelines and parallel execution.
|
||||
- Efficient caching mechanisms.
|
||||
|
||||
> If you want to learn more about such setup, make sure to check out our blog post on [how to adopt Nx on a npm/yarn/pnpm workspace](https://dev.to/nx/setup-a-monorepo-with-pnpm-workspaces-and-speed-it-up-with-nx-1eem) or the corresponding [video version](https://www.youtube.com/watch?si=0XH6Sp025xM3Rru5&v=ngdoUQBvAjo&feature=youtu.be).
|
||||
|
||||
Numerous open-source packages are adopting Nx in this lightweight manner. It enables them to maintain their existing setup while notably enhancing the local developer experience (DX) in task execution and accelerating processes on CI.
|
||||
|
||||
I picked out some of the more well-known OSS repos that started using Nx this year:
|
||||
|
||||
[**Tanstack**](https://tanstack.com/) — Tanstack has evolved to an entire ecosystem consisting of the famous [Tanstack (or React) Query](https://github.com/tanstack/query), [Tanstack Table](https://github.com/tanstack/table), now also [Tanstack Router](https://github.com/tanstack/router) and [Tanstack Form](https://github.com/tanstack/form). It started with Tanstack Query, which adopted Nx and Nx Cloud. [Zack talked about this collab with Dominik](https://www.youtube.com/watch?v=NvPXK6DVZGE), and we also had [Dominik](https://twitter.com/TkDodo) on our [Nx live stream](https://www.youtube.com/live/IbU6b6s0H1Q?si=0QZexPwulLXB9FIN). Now, all the above-mentioned Tanstack libs have adopted Nx, and there’s more coming.
|
||||
|
||||
[**Sentry JavaScript**](https://github.com/getsentry/sentry-javascript/) — Sentry, renowned for its comprehensive solutions in frontend monitoring and error logging, recently adopted Nx for their [official JavaScript SDK](https://github.com/getsentry/sentry-javascript/). This move integrates Nx’s capabilities into their monorepo, containing packages for popular frontend and Node.js backend integrations. They also published a blog post on [the benefits they’ve seen following the adoption of Nx in their monorepo](https://sentry.engineering/blog/reduce-ci-time-with-nx-caching) (hint: reducing CI times by 35%).
|
||||
|
||||
[**RxJS**](https://github.com/ReactiveX/rxjs) — The library for reactive programming in JavaScript. It is widely popular, with over 40 million downloads/week on NPM. RxJS only recently adopted Nx, not only leveraging speed improvements via caching, but also leveraging Nx’s latest `nx release` feature to publish packages to NPM.
|
||||
|
||||
[**AnalogJS**](https://analogjs.org/) — Analog is a full-stack Angular meta-framework that brings exciting features to Angular, like faster Vite setup, support for both server-side and static rendering, and easy file-based routing. Analog uses an Nx monorepo for its development and also uses [Nx’s DevKit](/extending-nx/intro/getting-started) to create tools that work great in both Nx and Angular CLI workspaces.
|
||||
|
||||
[**Qwikifier**](https://github.com/qwikifiers/qwik-nx) — The Qwikifiers community built a dedicated Nx plugin to combine the power of Qwik and Nx. Their repo is a great example of building Nx plugins and [using Nx to build your own CLI](/extending-nx/recipes/create-install-package).
|
||||
|
||||
[**Builder.io Mitosis**](https://github.com/BuilderIO/mitosis) — [BuilderIO](https://www.builder.io/) has an ambitious compiler project that allows you to write a component once and then compile it to different frameworks. Check out their [mind-blowing demo page](https://mitosis.builder.io/?outputTab=G4VwpkA%3D). They adopted Nx to [coordinate task dependencies](/concepts/task-pipeline-configuration) and speed up their CI builds.
|
||||
|
||||
[**Ghost**](https://github.com/TryGhost/Ghost) — Are you into blogging? You might want to look at [Ghost](https://ghost.org/). They were using Lerna in the past and migrated to a fully Nx-powered workspace.
|
||||
|
||||
And these are just some of them that joined in 2023. If I missed some cool ones (which I’m pretty sure), [ping me](https://twitter.com/juristr) and let me know!
|
||||
|
||||
## Nx Community
|
||||
|
||||
Nx has a huge community! We’re lucky to have so many folks rooting for Nx, whether on socials, talking at conferences, writing blog posts or [creating awesome plugins](/plugin-registry).
|
||||
|
||||
**Nx Champions** — This year we finally launched which we had planned for a long time. Our [Nx Champions](/community) program.
|
||||
|
||||

|
||||
|
||||
These are individuals who stood out for their contributions and passion for helping within the Nx community. We wanted to build a more connected relationship with these folks and have a channel to gather more direct feedback as well. Get to know [all of our champions](/community).
|
||||
|
||||
**New Discord server** — Around September we also switched over from our previous Nx Slack community to a brand new [**Nx community Discord**](https://go.nx.dev/community), which is already 2,600 members and counting. Discord is popular among OSS communities and allows new folks to join easily. In addition, we now have a dedicated forum integrated, as well as a couple of useful automations. More coming next year!
|
||||
|
||||
Make sure [you join](https://go.nx.dev/community)!
|
||||
|
||||
## New Content & Improved Docs
|
||||
|
||||
Our [Youtube channel](https://www.youtube.com/@nxdevtools) has grown to over 15k subscribers and peaks of 65k views a month. We love to provide educational video content, so make sure to subscribe! It got a little silent towards the end of the year, but we’ve been working a lot behind the scenes. So stay tuned!
|
||||
|
||||
We also poured a lot of [effort into the docs](/getting-started/intro). We restructured them following the [Diataxis](https://diataxis.fr/) to make pages less overwhelming and more structured based on their type of content. You’ll find
|
||||
|
||||
- [**Concept docs**](/concepts) — which explain some of the inner workings and mental model behind certain features. Like [how caching works](/concepts/how-caching-works).
|
||||
- [**Recipes**](/recipes) — which are solution oriented. You already know how to cook, we provide the exact recipe for it.
|
||||
- [**Tutorials**](/getting-started/tutorials) — for when you just want to sit down and follow along, step by step to learn how to use Nx in a certain context.
|
||||
- [**Reference**](/reference) and [**API docs**](/nx-api) — pure, raw and to the point.
|
||||
|
||||
We created a brand new [“Why Nx”](/getting-started/why-nx) page explaining the overall architecture of Nx including a [brand new video](https://www.youtube.com/watch?v=-_4WMl-Fn0w) giving you a holistic overview of what Nx is capable of.
|
||||
|
||||
We also refreshed our [entry pages](/getting-started/intro), including dedicated examples of using Nx with popular stacks:
|
||||
|
||||

|
||||
|
||||
You can also browse them in the [nx-recipes](https://github.com/nrwl/nx-recipes) GitHub repository.
|
||||
|
||||
{% tweet url="https://twitter.com/juristr/status/1736023402933318011" /%}
|
||||
|
||||
{% tweet url="https://twitter.com/juristr/status/1726977598218199302" /%}
|
||||
|
||||
And obviously, we jumped on the AI train as well. A couple of months ago, we added the [Nx Assistant](/ai-chat). A ChatGPT-powered interface trained on our docs. [Katerina](https://twitter.com/psybercity) wrote about it [on our blog](https://blog.nrwl.io/nx-docs-ai-assistant-433d238e45d4). The AI chat allows to interactively ask questions about Nx and will give you relevant answers from our docs (including linking to the sources).
|
||||
|
||||
## New Tagline: Smart Monorepos — Fast CI
|
||||
|
||||
Nx stands out for its flexibility, accommodating for both monorepo and non-monorepo project structures. This approach allows users to begin with simpler project configurations, leveraging the benefits of Nx’s robust tooling, and later, when the need arises, seamlessly [migrate to a monorepo](/recipes/tips-n-tricks/standalone-to-integrated).
|
||||
|
||||
However, Nx’s true strength becomes most apparent at scale, typically within a monorepo setup. We wanted to capture it in our new tagline: **Smart Monorepos — Fast CI**.
|
||||
|
||||
{% twitter url="https://twitter.com/juristr/status/1734558895547568634" /%}
|
||||
|
||||
Setting up an efficient and maintainable CI process for monorepos can be a complex task, so we’ve also made it a focal point in our new tagline. Nx expands beyond the local development experience, helping you set up an efficient CI process. We’re publicly launching [Nx Agents](/ci/features/distribute-task-execution) to add seamless distribution to your CI pipeline, and more are coming in 2024.
|
||||
|
||||
As part of that, we also restructured our docs to have a section entirely dedicated to CI: [https://nx.dev/ci](/ci/intro/ci-with-nx).
|
||||
|
||||
## Nx Conf
|
||||
|
||||
We did it again! The second in-person Nx Conf was a resounding success, this time set against the vibrant backdrop of the Big Apple.
|
||||
|
||||

|
||||
|
||||
There’s not much to say. Check out some of the amazing talks. I did a [Nx Conf 2023 recap blog post](https://dev.to/nx/nx-conf-2023-recap-53ep).
|
||||
|
||||
## Looking ahead — 2024
|
||||
|
||||
Although we shipped a lot in 2023, in many ways 2023 was about preparing for what we are planning to ship in Q1 2024.
|
||||
|
||||
### Solving CI
|
||||
|
||||
Legacy CI systems are a performance and productivity bottleneck if you use a powerful build system like Nx. Big tech companies know it and that’s why their CI systems look nothing like Circle or Jenkins. We’ve been narrowing this gap over the years, but only this year we finally built a turn-key CI solution that gives you great performance, scalability, dev ergonomics, and must better cost efficiency.
|
||||
|
||||
It has three components:
|
||||
|
||||
- [**Nx Cach**](/ci/features/remote-cache): Built-in local and remote caching to speed up your tasks and save you time and money. Available now.
|
||||
- [**Nx Agents**](/ci/features/distribute-task-execution): A single line to enable distributed computation, across multiple machines. Fully managed agents, dynamically allocated based on PR size. Available early Feb.
|
||||
- **Nx Workflows**: Next generation, fully managed CI solution with distribution at its core, designed from the ground up for monorepos. _Available later in 2024._
|
||||
|
||||
Optimal parallelization and distribution, using the right numbers of agents for each PR, rerunning flaky tests, splitting and distributing large test suites, handling dependencies between tasks across machines — are just some of the things we can now handle automatically for you. Turn it on and enjoy the speed.
|
||||
|
||||
### Solving the Simplicity vs Power Dilemma
|
||||
|
||||
Balancing simplicity and power is the trickiest part of the dev tools design. Simple onboarding for small projects or handling the biggest enterprise systems? Two years ago we solved this problem by giving you a choice between the package-based setup (a more powerful version of something like Turborepo) and the integrated setup (we manage your whole monorepo in the most optimal way). But now we believe we have a much better solution, where you have both, the simplicity of the former with the power of the latter. So you no longer have to choose.
|
||||
|
||||
We took inspiration from VSCode. Any project you open in VSCode will work right away: it is simple, and you don’t need to configure anything. If you install say a Playwright plugin, VSCode becomes aware of Playwright. It can run and debug your tests right in the editor. That’s what the Nx experience is going to be like. Any project, any tool will work right away. But if you — for instance — install the Playwright plugin, Nx will become aware of Playwright and will be able to cache test runs in the most optimal way and distribute your e2e tests across machines for best efficiency. All the benefits with none of the costs.
|
||||
|
||||
The whole team is excited about it as the new experience feels much more elegant.
|
||||
|
||||
As always, we try very hard not to break folks, so all your current workspaces will keep working, and we will [provide automatic migrations](/features/automate-updating-dependencies) to bring you to this new way of using Nx.
|
||||
|
||||
Exciting stuff! So keep an eye on our channels, and subscribe if you haven’t already ;)
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,188 +0,0 @@
|
||||
---
|
||||
title: What if Nx Plugins Were More Like VSCode Extensions
|
||||
slug: 'what-if-nx-plugins-were-more-like-vscode-extensions'
|
||||
authors: [Juri Strumpflohner]
|
||||
cover_image: '/blog/images/2024-02-05/featured_img.png'
|
||||
tags: [nx, releases]
|
||||
reposts: []
|
||||
---
|
||||
|
||||
Enhance, but don’t interfere! That’s the ideal! And this is how extensions work in VSCode (or Webstorm). You can use VSCode without any extension and get some basic functionality, or you can add an extension on top to enhance your experience and, ideally, increase your productivity.
|
||||
|
||||
Table of Contents
|
||||
|
||||
- [Adding Nx to an Existing Monorepo](#adding-nx-to-an-existing-monorepo)
|
||||
- [Project Crystal](#project-crystal)
|
||||
- [Project Crystal Plugins in an Nx Monorepo](#project-crystal-plugins-in-an-nx-monorepo)
|
||||
- [Inferred Targets](#inferred-targets)
|
||||
- [Visualizing Inferred Targets](#visualizing-inferred-targets)
|
||||
- [More Transparency and a Single Source of Truth](#more-transparency-and-a-single-source-of-truth)
|
||||
- [Enhancing existing Monorepos with Nx Plugins](#enhancing-existing-monorepos-with-nx-plugins)
|
||||
- [This is just the Beginning](#this-is-just-the-beginning)
|
||||
- [Learn more](#learn-more)
|
||||
|
||||
---
|
||||
|
||||
**Prefer a video? We’ve got you covered!**
|
||||
{% youtube src="https://www.youtube.com/embed/wADNsVItnsM?si=sQ3-Dlx6KBRBUMkE" title="What if Nx Plugins Were More Like VSCode Extensions" /%}
|
||||
|
||||
Also, make sure to check out [Launch Nx Conf](/launch-nx) on Thursday, Feb 8th, where we’ll have more in-depth talks about Project Crystal as well as other exciting features around Nx and Nx Cloud.
|
||||
|
||||
---
|
||||
|
||||
Take, for instance, the Playwright plugin. You install it, and it’ll automatically detect the Playwright config file and enhance your workspace by providing quick run buttons alongside your tests or even a dedicated Test Explorer window.
|
||||
|
||||

|
||||
_The Playwright VSCode extension enhancing the developer experience_
|
||||
|
||||
## Adding Nx to an Existing Monorepo
|
||||
|
||||
You can add Nx to an existing npm/yarn/pnpm monorepo quite straightforwardly. You run:
|
||||
|
||||
```shell
|
||||
npx nx@latest init
|
||||
```
|
||||
|
||||
You’ll get an `nx` package installed and an `nx.json` allowing you to define [task dependencies](/recipes/running-tasks/defining-task-pipeline) and caching. With that, you're now able to run commands like `nx build <your project>` or nx `run-many -t build test` to run all `build` and `test` targets in your workspace in parallel. Nx will read and use your existing `package.json` scripts. I've written an in-depth [blog post about adopting Nx in such a scenario](https://dev.to/nx/setup-a-monorepo-with-pnpm-workspaces-and-speed-it-up-with-nx-1eem).
|
||||
|
||||
This is the most lightweight setup you can get while still getting some improvements via Nx regarding faster task running and more intelligent parallelization. But, you need to deal with the remaining of the monorepo setup.
|
||||
|
||||
## Project Crystal
|
||||
|
||||
Nx always had more to offer, though, which it mainly did through its plugins. They’re optional but usually something you’d get set up when creating a new workspace with `create-nx-workspace`. Nx Plugins are extremely powerful, helping you not only create and configure new monorepos, but also taking away the burden of integrating various tooling as well as providing features for enforcing consistency and helping with maintainability. These aspects are fundamental in enterprise settings, where Nx Plugins have proven to help teams successfully manage their monorepos.
|
||||
|
||||
However, this is a balancing act. More abstraction and automation means more support but also potentially a learning curve and giving up some low-level control. It also requires a slightly more significant upfront investment when migrating to an Nx plugin-powered monorepo.
|
||||
|
||||
**These are things we wanted to solve**, and **Project Crystal** is the first step in that direction.
|
||||
|
||||

|
||||
|
||||
Some of the main objectives of Project Crystal are to...
|
||||
|
||||
- make Nx plugins more transparent
|
||||
- reduce the amount of configuration required
|
||||
- allow Nx plugins to be drop-in enhancements in existing npm/yarn/pnpm monorepos
|
||||
- allow for a migration to an Nx plugin-powered monorepo
|
||||
|
||||
## Project Crystal Plugins in an Nx Monorepo
|
||||
|
||||
> Note: starting with Nx 18, Project Crystal will be active for new workspaces only. You can opt-in to use them though.
|
||||
|
||||
When you create a new Nx workspace using
|
||||
|
||||
```shell
|
||||
npx create-nx-workspace myorg
|
||||
```
|
||||
|
||||
... and you choose an “integrated monorepo” you’ll get the usual setup powered by Nx Plugins and all the features and benefits that come with them. Where you’ll see Project Crystal in action is when you open a `project.json` file, which will most likely look like the following:
|
||||
|
||||
```json {% fileName="project.json" }
|
||||
{
|
||||
"name": "reactapp",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "apps/reactapp/src",
|
||||
"projectType": "application",
|
||||
"targets": {},
|
||||
"tags": []
|
||||
}
|
||||
```
|
||||
|
||||
### Inferred Targets
|
||||
|
||||
Starting with Nx 18 and Project Crystal, we don’t generate any targets anymore, but the corresponding Nx plugin instead [infers them](/concepts/inferred-tasks). If we open the `nx.json`, you'll see a new property, `plugins`:
|
||||
|
||||
```json {% fileName="nx.json" %}
|
||||
{
|
||||
...
|
||||
"plugins": [
|
||||
{
|
||||
"plugin": "@nx/vite/plugin",
|
||||
"options": {
|
||||
"buildTargetName": "build",
|
||||
"previewTargetName": "preview",
|
||||
"testTargetName": "test",
|
||||
"serveTargetName": "serve",
|
||||
"serveStaticTargetName": "serve-static"
|
||||
}
|
||||
},
|
||||
{
|
||||
"plugin": "@nx/eslint/plugin",
|
||||
"options": {
|
||||
"targetName": "lint"
|
||||
}
|
||||
},
|
||||
{
|
||||
"plugin": "@nx/cypress/plugin",
|
||||
"options": {
|
||||
"targetName": "e2e",
|
||||
"componentTestingTargetName": "component-test"
|
||||
}
|
||||
},
|
||||
...
|
||||
],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Notice the options property defining the names of the potentially inferred targets for each plugin. These targets will be generated dynamically s.t. you can still run `nx build reactapp` even though there's no `build` target explicitly defined in the `project.json` of your `apps/reactapp` project.
|
||||
|
||||
This dramatically reduces the redundancy of repeatedly configuring the same tasks (e.g., Jest or Vitest tasks) throughout your various projects. Instead, with this new approach, you get the defaults, and if you need to override them, you can still define them in your `project.json` file as you were accustomed to before.
|
||||
|
||||
### Visualizing Inferred Targets
|
||||
|
||||
Dynamically inferred targets help with the maintainability aspect and reduce the configuration overhead overall. But how do we know which targets are available for a given project?
|
||||
|
||||
Option 1 is to run the following command:
|
||||
|
||||
```shell
|
||||
npx nx show project reactapp --web
|
||||
```
|
||||
|
||||
This opens your browser with the following view:
|
||||
|
||||

|
||||
_Browser view of the inferred targets_
|
||||
|
||||
Option 2 is [Nx Console](/getting-started/editor-setup), which is an extension for VSCode as well as IntelliJ (Webstorm etc.). It comes with a project detail view, as shown below, as well as “Codelens” features that enhance your configuration files with context-based information and features.
|
||||
|
||||

|
||||
_Nx Console showing the inferred targets in a dedicated view_
|
||||
|
||||
## More Transparency and a Single Source of Truth
|
||||
|
||||
We also wanted the new approach to plugins to be closer to the actual CLI tool of the framework you’re using. If you have a React + Vite project, `nx build` should be as close as possible to the `vite build` while still providing the enhancements around caching configuration.
|
||||
|
||||
And this is what happens. Behind the scenes, the plugin configures caching with inputs and outputs and task dependencies (e.g., `^build`) but then mostly pipes through to the Vite CLI (in this particular case), Remix, Next CLI, etc.
|
||||
|
||||

|
||||
|
||||
Furthermore, the framework-specific config — in the example of Vite, the `vite.config.ts` - is the single source of truth from which Nx infers configuration such as caching. If you change your Vite `build.outDir`, Nx automatically picks that up and uses that as the caching output directory.
|
||||
|
||||
## Enhancing existing Monorepos with Nx Plugins
|
||||
|
||||
As mentioned earlier, one of the key goals of Project Crystal was to improve the adoption story of Nx Plugins, which implicitly also helps with migrating to Nx plugin-based monorepos. By reducing the config footprint of an Nx plugin and by automatically inferring tasks from existing framework configs, we’ve moved in a direction where plugins have become much more of a drop-in approach.
|
||||
|
||||
Starting with Nx 18, if you now run `nx init` on an existing npm/yarn/pnpm workspace, you'll also get asked about installing plugins based on the setup you have in your monorepo.
|
||||
|
||||

|
||||
_Asking about installing plugins based on your monorepo tooling_
|
||||
|
||||
You can also obviously start with no plugin at all and incrementally add them as you go and feel comfortable using the new `add` command:
|
||||
|
||||
```shell
|
||||
npx nx add @nx/vite
|
||||
```
|
||||
|
||||
## This is just the Beginning
|
||||
|
||||
We just released Project Crystal, so this is just the beginning of it. While we’ve moved many of our existing Nx plugins to adopt the new approach, there are still some more refinements to be done in the coming weeks. But we are excited about the possibilities Project Crystal enables for Nx and its adoption story going forward, making Nx plugins more approachable, transparent, and lightweight.
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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)
|
||||
- [Speed up your CI](https://nx.app/)
|
||||
@@ -1,190 +0,0 @@
|
||||
---
|
||||
title: Introducing @nx/nuxt Enhanced Nuxt.js Support in Nx
|
||||
slug: 'introducing-nx-nuxt-enhanced-nuxt-js-support-in-nx'
|
||||
cover_image: '/blog/images/2024-02-06/featured_img.png'
|
||||
authors: ['Katerina Skroumpelou']
|
||||
tags: [devtools, javascript, monorepos, nuxt]
|
||||
---
|
||||
|
||||
We're excited to introduce a new way to enhance your [Nuxt](https://nuxt.com/) development workflow! After the Vue plugin, we're introducing our new Nx plugin for Nuxt, `@nx/nuxt`. Designed for Nuxt developers and existing Nx users alike, this integration brings the best of both worlds into your development ecosystem, enabling you to leverage Nx's powerful capabilities seamlessly within your Nuxt projects.
|
||||
|
||||
## Why Consider Nx for Your Nuxt Projects?
|
||||
|
||||
Using Nx with your Nuxt.js projects presents the following advantages:
|
||||
|
||||
- **Monorepo Management**: Simplify the management of multiple projects within a single repository, facilitating code sharing and reducing overhead.
|
||||
- **Modular Development**: Break down your Nuxt app into manageable, independent modules that can be developed, tested, and deployed in isolation.
|
||||
- **Enhanced Caching**: Accelerate your development with Nx's intelligent caching, automatically configured for your Nuxt projects.
|
||||
- **Nx generators**: Nx provides generators for scaffolding new Nuxt applications, with support for Jest, Storybook, and e2e test generation with Cypress or Playwright.
|
||||
- **Automated upgrades**: Nx offers a set of migrators that help you upgrade your projects.
|
||||
|
||||
## Getting Started with Nx and Nuxt.js
|
||||
|
||||
Whether you're initiating a new project or integrating into an existing one, `@nx/nuxt` offers a straightforward setup process:
|
||||
|
||||
### Starting a New Nx Workspace with Nuxt
|
||||
|
||||
Creating a new Nx workspace optimized for Nuxt is as simple as running:
|
||||
|
||||
```shell
|
||||
npx create-nx-workspace@latest --preset=nuxt
|
||||
```
|
||||
|
||||
Our setup wizard will guide you through the initial configuration, ensuring your workspace is tailored to your needs:
|
||||
|
||||
```shell
|
||||
npx create-nx-workspace@latest
|
||||
|
||||
> NX Let's create a new workspace [/getting-started/intro)/getting-started/intro]
|
||||
|
||||
✔ Where would you like to create your workspace? · my-org
|
||||
✔ Which stack do you want to use? · vue
|
||||
✔ What framework would you like to use? · nuxt
|
||||
✔ Integrated monorepo, or standalone project? · integrated
|
||||
✔ Application name · my-app
|
||||
✔ Test runner to use for end to end (E2E) tests · playwright (also cypress)
|
||||
✔ Default stylesheet format · scss (also css, less)
|
||||
✔ Set up CI with caching, distribution and test deflaking · github
|
||||
```
|
||||
|
||||
This command will create a new Nx workspace with a single Nuxt application, complete with essential features and ready for development.
|
||||
|
||||
## Enhancing an Existing Nuxt Project with Nx
|
||||
|
||||
Integrating Nx into an existing Nuxt.js project has never been easier, with the help of the `nx init` command. This command will add Nx to your project without the need to disrupt your current setup.
|
||||
|
||||
### How It Works
|
||||
|
||||
When you run `nx init` in your existing Nuxt.js project, Nx does the following:
|
||||
|
||||
- **Installs @nx/nuxt**: Adds the necessary Nx and @nx/nuxt dependencies to your project, enabling Nx's features while keeping your existing setup intact.
|
||||
- **Understands Existing Configurations**: Nx automatically recognizes your nuxt.config.js or nuxt.config.ts file, ensuring that all your custom configurations, scripts, and commands are preserved and utilized.
|
||||
- **Minimal Configuration**: Only a minimal `nx.json` file is added to your project. This file is used to configure the `@nx/nuxt` plugin if needed, but in most cases, your existing Nuxt.js configurations will suffice.
|
||||
|
||||
To begin the integration process, simply navigate to the root of your existing Nuxt.js project and run:
|
||||
|
||||
```shell
|
||||
npx nx init
|
||||
```
|
||||
|
||||
This approach offers several key benefits for teams looking to adopt Nx:
|
||||
|
||||
- **Zero Disruption**: Your project will continue to use its existing configurations, and the existing configuration entrypoint files. There's no need to learn new configuration syntaxes or reconfigure your project to start using Nx.
|
||||
- **Immediate Value**: Instantly gain access to Nx's powerful developer tools and build system, without significant changes to your project.
|
||||
- **Future Flexibility**: As your project grows, Nx is ready to scale with you. You can gradually adopt more Nx features and plugins over time, at a pace that suits your team.
|
||||
|
||||
## Using Nx to run your Nuxt app
|
||||
|
||||
Nx scans your workspace to look for Nuxt configuration files (eg. `nuxt.config.ts`). It uses these files to understand where your Nuxt projects live, and uses them to set up tasks that can be invoked through Nx, like `serve` and `build`. So, in your Nx workspace, you can then run:
|
||||
|
||||
```shell
|
||||
nx serve my-nuxt-app
|
||||
```
|
||||
|
||||
and
|
||||
|
||||
```shell
|
||||
nx build my-nuxt-app
|
||||
```
|
||||
|
||||
and these commands will call the `nuxt` CLI under the hood, enhanced with Nx's features.
|
||||
|
||||
You can see a visual representation of your task dependencies by running
|
||||
|
||||
```shell
|
||||
nx graph
|
||||
```
|
||||
|
||||

|
||||
|
||||
You can also see how Nx configures your tasks, by running:
|
||||
|
||||
```shell
|
||||
nx show project my-nuxt-app --web
|
||||
```
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Using Nx Console
|
||||
|
||||
You get access to all these features through our VSCode and WebStorm [Nx Console extension](/getting-started/editor-setup).
|
||||
|
||||
You can use Nx Console to visualize tasks, and understand where each inferred task (like `build` and `serve` in Nuxt's case) is coming from, with our codelens-like feature, as an alternative to the `--web` flag on the `nx show project` command.
|
||||
|
||||
Nx Console is also very convenient for generating code and running tasks, since it offers a graphical user interface for all the amazing features of the Nx CLI.
|
||||
|
||||
## What Does Nx Bring to Your Nuxt Development?
|
||||
|
||||
With `@nx/nuxt`, your Nuxt projects gain automatic recognition of `build` and `serve` processes. There's no need to deal with unfamiliar configurations; Nx intuitively understands your Nuxt project structure and optimizes accordingly.
|
||||
|
||||
### Modularize and Scale with Ease
|
||||
|
||||
One of the most compelling aspects of using Nx with Nuxt.js is the ability to modularize large applications into manageable libraries or components. This not only makes your codebase more organized and maintainable but also significantly enhances your development workflow and CI processes.
|
||||
|
||||
#### Breaking Down a Monolithic Nuxt App
|
||||
|
||||
Large Nuxt applications can become challenging to maintain and scale over time. By adopting Nx, you can structure your Nuxt app as a collection of smaller, focused libraries. Each library can encapsulate specific functionalities or features, such as UI components, utilities, or business logic.
|
||||
|
||||
#### Independent Development and Testing
|
||||
|
||||
This modular structure allows teams to work on different aspects of the application in parallel, reducing bottlenecks and improving collaboration. Furthermore, you can run tests, linters, and other checks independently for each library, making your development process more efficient and targeted.
|
||||
|
||||
For instance, if you want to create a new Vue UI library, you can use the following command:
|
||||
|
||||
```shell
|
||||
nx generate @nx/vue:lib my-shared-ui
|
||||
```
|
||||
|
||||
This command creates a my-shared-ui library within your workspace, which can then be used across your Nuxt app and potentially other applications within the same workspace.
|
||||
|
||||
#### Enhancing CI with Modular Builds
|
||||
|
||||
On the CI front, Nx's modular approach makes things much faster. You can configure your CI pipeline to build, test, and deploy only the affected libraries and applications, thanks to Nx's advanced dependency graph analysis. This results in faster CI runs and more efficient resource utilization.
|
||||
|
||||
#### Sharing Code Between Applications
|
||||
|
||||
Nx's workspace model facilitates code sharing between projects, which is particularly useful in monorepos containing multiple front-end projects. With Nx, sharing UI components, utilities, or services between these applications becomes straightforward.
|
||||
To share code, simply import the library into your Nuxt and Vue applications as needed. Nx takes care of the rest, ensuring that dependencies are correctly managed and that your applications remain buildable and testable.
|
||||
|
||||
Imagine a scenario where your workspace contains a Nuxt application for your public-facing website and a Vue application for an internal tool. You can create a shared library for common UI components, such as buttons, inputs, and modals, and use these components in both applications. This not only reduces duplication but also ensures consistency across your projects.
|
||||
|
||||
```ts
|
||||
// Importing a shared UI component in your Nuxt app
|
||||
import { MyButton } from '@my-org/my-shared-ui';
|
||||
```
|
||||
|
||||
```ts
|
||||
// Importing the same component in your Vue app
|
||||
import { MyButton } from '@my-org/my-shared-ui';
|
||||
```
|
||||
|
||||
### Visualizing Your Project Structure
|
||||
|
||||
Nx provides a clear overview of your project's structure and dependencies, making it easier to manage complex applications. The Nx Console extension for VSCode, for instance, offers a graphical interface to visualize and run tasks, enhancing your development experience.
|
||||
|
||||
In your workspace, you can run
|
||||
|
||||
```shell
|
||||
nx graph
|
||||
```
|
||||
|
||||
and see the structure of your projects:
|
||||
|
||||

|
||||
|
||||
## Embracing Nx in Your Nuxt Journey
|
||||
|
||||
Whether you're starting a new Nuxt project or looking to enhance an existing one, Nx offers a compelling set of tools and features to streamline your development process. From modularization to caching, the integration of Nx into your Nuxt projects promises a more efficient, scalable, and enjoyable development experience. By embracing Nx's capabilities in your Nuxt development, you're not just optimizing your current workflow; you're future-proofing your development process. As your projects grow and evolve, Nx's modular architecture and powerful tooling will continue to provide value, making your development experience more enjoyable and productive.
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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 Community Discord](https://go.nx.dev/community)
|
||||
- [Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
|
||||
- [Speed up your CI](https://nx.app)
|
||||
@@ -1,127 +0,0 @@
|
||||
---
|
||||
title: Fast, Effortless CI
|
||||
slug: 'fast-effortless-ci'
|
||||
authors: [Isaac Mann]
|
||||
cover_image: '/blog/images/2024-02-07/featured_img.png'
|
||||
tags: [nx, nx-cloud, release]
|
||||
reposts: []
|
||||
---
|
||||
|
||||
## From 90-minute to 10-minute CI Pipelines
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/_FSHQIwITic?si=GaAz4B0nYUEzVftN" title="Fast, Effortless CI" /%}
|
||||
|
||||
> TL;DR; Nx is releasing a new product called Nx Agents that dramatically improves the speed and maintainability of your CI pipeline.
|
||||
|
||||
In 2014, the state of the art for running tests and builds in your repository were tools like Gulp and Grunt. They were good enough to get the job done, but they were fundamentally **low-level** build tools. That is, they did exactly what they were programmed to do and no more. That approach works well in a single project where the configuration does not change frequently, but becomes problematic in a monorepo environment where there are multiple applications and multiple teams working in the same repository.
|
||||
|
||||
Nx was created in 2017 to address this problem. Nx is a build system that operates on a **higher level** where developers define the relationships between tasks and then Nx to decides the optimal way to run those tasks. In the same way, developers can define the inputs and outputs of tasks, then Nx automatically caches those task results. Developers tell Nx what a task does and then Nx can decide how best to run that task.
|
||||
|
||||
With [Nx Agents](/ci/features/distribute-task-execution), Nx is applying this same mindset to the problem of slow and costly CI pipelines. Nx gives you both **Smart Monorepos** and **Fast CI**.
|
||||
|
||||
## Why is CI So Hard?
|
||||
|
||||
Just like build tools from the last decade, CI pipelines are defined in a low-level, machine-oriented way. Each step in the pipeline is defined explicitly. It is up to the CI developer to ensure that all pre-requisites are available for each new step in the pipeline. If tasks need to be run in parallel on multiple machines, all the assets needed by each of those tasks need to be copied over to those machines before the tasks are run. Then, if the task dependencies ever change, the CI pipeline configuration needs to be updated to account for those changes.
|
||||
|
||||
The script below is a very simple example with only three tasks and one file being shared between them, but you can already see the complexity inherent in the system.
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
build_base:
|
||||
steps:
|
||||
- run: npm run build-base
|
||||
- name: Save assets for use by other jobs
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: base_output
|
||||
path: base/output.ts
|
||||
|
||||
build_app1:
|
||||
needs: build_base
|
||||
steps:
|
||||
- name: Download base output
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: base_output
|
||||
- run: npm run build-app1
|
||||
|
||||
build_app2:
|
||||
needs: build_base
|
||||
steps:
|
||||
- name: Download base output
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: base_output
|
||||
- run: npm run build-app2
|
||||
```
|
||||
|
||||
At any point in the future, if a task is added to the system or there is a change to the output files of build_base, this pipeline will need to be updated.
|
||||
|
||||
## A Build System That Runs Your CI
|
||||
|
||||
Part of the reason CI is so difficult to maintain is that it has no knowledge of your repository. Your CI provider can’t optimize your pipeline because it doesn’t even know the language you’re using, let alone relationships between your projects. A build system, on the other hand, must know all that information in order to properly function.
|
||||
|
||||
The key that unlocks all the power of Nx Agents is this architectural shift:
|
||||
|
||||
> Rather than the traditional approach where your CI provider invokes a build tool, the Nx build system will manage your CI pipeline.
|
||||
|
||||
Nx already knows how your repository is structured and the best way to run tasks locally. Nx can use that exact same knowledge to run tasks in the best way on multiple machines in CI.
|
||||
|
||||
## Distribute Tasks with Nx Agents
|
||||
|
||||
When using Nx Agents, distributing tasks across multiple machines becomes as simple as running those tasks on your local machine. This is because any task artifacts will automatically be copied to the agent machines where they are needed.
|
||||
|
||||
Instead of explicitly defining what order to run tasks, your CI pipeline only needs to tell Nx **what** needs to be accomplished and Nx will figure out **how** best to do it.
|
||||
|
||||

|
||||
|
||||
The pipeline configuration below will work no matter how many projects are in the repository or how complex the dependencies between those projects are.
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
main:
|
||||
# Tell Nx Cloud how many agents to use and the name of the last task
|
||||
- run: |
|
||||
nx-cloud start-ci-run \
|
||||
--distribute-on="3 linux-medium-js" \
|
||||
--stop-agents-after="e2e-ci"
|
||||
# Run tasks the same way you would locally
|
||||
- run: nx affected -t lint test build --parallel=3
|
||||
- run: nx affected -t e2e-ci --parallel=1
|
||||
```
|
||||
|
||||
The only reason to modify this file is if you need to change the number of agent machines or there is another type of task that needs to run in CI.
|
||||
|
||||
The `linux-medium-js` name in the CI configuration refers to a built-in launch template that Nx provides. If you can not find a template in [the default list](https://github.com/nrwl/nx-cloud-workflows/blob/main/launch-templates/linux.yaml) that meets your needs, you can provide your own. [With a single yaml file](/ci/reference/launch-templates), you can set up your agent environment in exactly the way you want with your own launch template.
|
||||
|
||||
## Dynamically Allocate Agents
|
||||
|
||||
Nx understands that some CI pipelines need more resources than others. To account for this, Nx Agents gives you the ability to [define three different classes of agent allocation configurations](/ci/features/dynamic-agents). You can use fewer agents for smaller PRs and more agents for larger PRs. This allows you to save money where possible and use the full power of Nx Agents when needed.
|
||||
|
||||

|
||||
|
||||
## Automatically Split E2E Tasks by File
|
||||
|
||||
Typically, e2e tests are the tasks that take the longest in CI. In order to take advantage of parallelization and task distribution, these large tasks would need to be split into smaller tasks, but doing this manually would involve duplicating a lot of configuration code and making sure to keep that configuration synchronized. Nx 18’s [Project Crystal](/blog/what-if-nx-plugins-were-more-like-vscode-extensions) allows you to [automatically create separate Cypress and Playwright tasks](/ci/features/split-e2e-tasks) for each spec file in the e2e project. These individual tasks can all be triggered by running the `e2e-ci` task. What was once a tedious manual process can now be done for you automatically.
|
||||
|
||||

|
||||
|
||||
## Identify and Re-run Flaky Tasks
|
||||
|
||||
There are some tasks that will fail or succeed in CI without any changes to the task’s code. These are flaky tasks and in order to merge a change in unrelated code, developers need to manually re-run the entire pipeline until that flaky task succeeds. Because Nx is already tracking inputs and outputs of tasks, it knows when a task is flaky. Now, Nx Cloud will [automatically re-run a flaky task if it fails](/ci/features/flaky-tasks), without a developer needing to manually trigger it.
|
||||
|
||||

|
||||
|
||||
## Run Some Tasks on Another CI Provider
|
||||
|
||||
If you have a task that can’t be run on Nx Agents for some reason, you can easily [flag it to run directly on the main CI job](/ci/reference/nx-cloud-cli#enablingdisabling-distribution). Add a `--no-agents` flag to the command and Nx will not run it on an agent.
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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)
|
||||
- [Speed up your CI](https://nx.app/)
|
||||
@@ -1,322 +0,0 @@
|
||||
---
|
||||
title: Versioning and Releasing Packages in a Monorepo
|
||||
slug: 'versioning-and-releasing-packages-in-a-monorepo'
|
||||
authors: [Juri Strumpflohner]
|
||||
cover_image: '/blog/images/2024-02-09/featured_img.png'
|
||||
tags: [nx, nx-cloud, releases, changelog]
|
||||
---
|
||||
|
||||
When it comes to publishing NPM packages, there are a bunch of libraries and utilities out there that help with the process. Many of them are tricky when it comes to properly configuring them in a monorepo.
|
||||
|
||||
Nx already has all that knowledge, and it can leverage the information it has about your project dependencies and relationships to optimize your task runs.
|
||||
|
||||
Here’s the structure of our current example workspace we’re going to refer to in this article:
|
||||
|
||||

|
||||
|
||||
As you can see `@tuskdesign/forms` relies on `@tuskdesign/buttons` and as such has to consider that when running versioning and publishing.
|
||||
|
||||
> **Note:** it is worth mentioning that the Nx community has also stepped up in the past and created jscutlery/semver, a package that adds semantic versioning and publishing to your Nx workspace. Make sure to check that out as well.
|
||||
|
||||
---
|
||||
|
||||
**Prefer a video?**
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/KjZKFGu3_9I?si=L-8oRzy-hV-WF_pS" title="Versioning and Releasing Packages in a Monorepo" /%}
|
||||
|
||||
---
|
||||
|
||||
## Table Of Contents
|
||||
|
||||
- [Adding Nx](#adding-nx)
|
||||
- [Installing the JavaScript/TypeScript versioning Package](#installing-the-javascripttypescript-versioning-package)
|
||||
- [Running Nx Release](#running-nx-release)
|
||||
- [Excluding Packages](#excluding-packages)
|
||||
- [Running the Versioning and Changelog Generation](#running-the-versioning-and-changelog-generation)
|
||||
- [Versioning using Conventional Commits](#versioning-using-conventional-commits)
|
||||
- [Generating a GitHub Release](#generating-a-github-release)
|
||||
- [Programmatic Mode](#programmatic-mode)
|
||||
- [Wrapping Up](#wrapping-up)
|
||||
- [Learn more](#learn-more)
|
||||
|
||||
## Adding Nx
|
||||
|
||||
You can add Nx to your existing monorepo workspace using the following command:
|
||||
|
||||
```shell
|
||||
pnpm dlx nx@latest init
|
||||
```
|
||||
|
||||
(use `npx nx@latest init` in a NPM workspace)
|
||||
|
||||
This brings up a couple of questions including whether to install [Project Crystal plugins](/blog/what-if-nx-plugins-were-more-like-vscode-extensions).
|
||||
|
||||

|
||||
|
||||
It gives you some additional benefits ([you can read more here](/blog/what-if-nx-plugins-were-more-like-vscode-extensions)), but you don’t have to as it is not required for Nx Release.
|
||||
|
||||
## Installing the JavaScript/TypeScript versioning Package
|
||||
|
||||
Nx Release is made to handle the versioning and publishing of any package. For now, the Nx team provides the JS/TS package publishing approach, which comes with the `@nx/js`. You could provide your own implementation, like for Cargo, NuGet etc.
|
||||
|
||||
```shell
|
||||
pnpm add @nx/js -w
|
||||
```
|
||||
|
||||
_(We use the `-w` flag to install it at the monorepo root level)_
|
||||
|
||||
## Running Nx Release
|
||||
|
||||
Once you’re set-up, you can already go ahead and run the following command:
|
||||
|
||||
```shell
|
||||
pnpm nx release --dry-run
|
||||
```
|
||||
|
||||
This command will do the versioning, changelog generation, and publishing steps together. Note the `--dry-run` simply simulating a run.
|
||||
|
||||

|
||||
|
||||
You’ll get asked whether you want to release a major, pre-major, minor… release or choose an exact version.
|
||||
|
||||
Once this runs through, you might hit the following error:
|
||||
|
||||

|
||||
|
||||
Since Nx Release has never been run on this repository, it cannot figure out the historical information, for instance, to generate the changelog starting from previous git tags. Re-run the command with `--first-release`
|
||||
|
||||
```shell
|
||||
pnpm nx release --dry-run --first-release
|
||||
```
|
||||
|
||||
If you inspect the console output, you can see that:
|
||||
|
||||
- it would increment the version in the package.json
|
||||
- update the pnpm (or npm) lockfile
|
||||
- stage the changes with git
|
||||
- creates a `CHANGELOG.md` file
|
||||
- git commits everything
|
||||
- git tags the commit using the version
|
||||
|
||||
The dry-run mode also nicely previews all `package.json` changes in a git diff style:
|
||||
|
||||

|
||||
|
||||
Note, if you want to get even more insights into what is happening when running the command, you can use the `--verbose`, which will also print the actual git commands.
|
||||
|
||||
## Excluding Packages
|
||||
|
||||
If you look closely at the dry-run logs, you may notice that Nx Release bumped the version on all of our packages:
|
||||
|
||||
- `@tuskdesign/forms`
|
||||
- `@tuskdesign/buttons`
|
||||
- `@tuskdesign/demo`
|
||||
|
||||

|
||||
|
||||
While we want to have it bumped on the `forms` and `buttons` packages, the `demo` is just for us to test things in the workspace. In this particular workspace, Nx Release doesn't have a way to distinguish what is an app and what is a library/package. Note, if you're in an Nx-generated workspace that uses Nx Plugins, you'd potentially have that classification in the `project.json` files.
|
||||
|
||||
In our particular scenario, let's exclude `@tuskdesign/demo` as follows:
|
||||
|
||||
```json {% fileName="nx.json" %}
|
||||
{
|
||||
...
|
||||
"release": {
|
||||
"projects": ["*", "!@tuskdesign/demo"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also explicitly list the packages to be released individually. Or, as shown above, include all (`*`) and exclude the private package.
|
||||
|
||||
If you re-run the `nx release` command, you'll see that `@tuskdesign/demo` will be ignored now.
|
||||
|
||||
## Running the Versioning and Changelog Generation
|
||||
|
||||
Once you have configured the excluded packages, feel free to go ahead and run the command without `--dry-run:`
|
||||
|
||||
```
|
||||
pnpm nx release --first-release
|
||||
```
|
||||
|
||||
You can skip the release part when prompted for now. Check how the workspace got updated, incrementing the `package.json` version property and updating the version on the package dependency definition, i.e. the `@tuskdesign/buttons` version got updated in the `@tuskdesign/forms` package.json.
|
||||
|
||||
## Versioning using Conventional Commits
|
||||
|
||||
Instead of manually confirming the next version each time, we can use a versioning strategy: [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) is a commonly adopted approach for publishing packages.
|
||||
|
||||
To configure conventional commits with Nx Release, go to the `nx.json` and adjust it as follows:
|
||||
|
||||
```json {% fileName="nx.json" %}
|
||||
{
|
||||
...
|
||||
"release": {
|
||||
"projects": ["*", "!@tuskdesign/demo"],
|
||||
"version": {
|
||||
"conventionalCommits": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you now run...
|
||||
|
||||
```shell
|
||||
pnpm nx release --dry-run
|
||||
```
|
||||
|
||||
... you'll notice that it doesn't pick up any changes because it leverages conventional commit, and we haven't changed anything yet.
|
||||
|
||||

|
||||
|
||||
Let’s go ahead and change something in our `@tuskdesign/buttons` package and then commit it as follows:
|
||||
|
||||
```shell
|
||||
git commit -am 'feat(buttons): add new background shadow'
|
||||
```
|
||||
|
||||
Now re-run the `nx release command` (don't forget the `--dry-run`). You'll see how it chooses `v1.2.0` as our new version (we had `v1.1.0` previously), given we added a new feature (denoted by the `feat(...)` conventional commit).
|
||||
|
||||

|
||||
|
||||
It also generates a nice `CHANGELOG.md` for us:
|
||||
|
||||
```shell
|
||||
## 1.2.0 (2024-02-09)
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- **buttons:** add new background shadow
|
||||
|
||||
### ❤️ Thank You
|
||||
|
||||
- Juri
|
||||
|
||||
## 1.1.0 (2024-02-09)
|
||||
|
||||
This was a version bump only, there were no code changes.
|
||||
```
|
||||
|
||||
## Generating a GitHub Release
|
||||
|
||||
Instead of just generating a `CHANGELOG.md` entry in our repository you can also opt-in for creating a Github release (here's the example of the [Nx repository](https://github.com/nrwl/nx/releases)).
|
||||
|
||||
```json {% fileName="nx.json" %}
|
||||
Use the `createRelease` property and set it to `github`.
|
||||
{
|
||||
...
|
||||
"release": {
|
||||
"projects": ["*", "!@tuskdesign/demo"],
|
||||
"version": {
|
||||
"conventionalCommits": true
|
||||
},
|
||||
"changelog": {
|
||||
"workspaceChangelog": {
|
||||
"createRelease": "github"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To see the working, you need to make sure to:
|
||||
|
||||
- push the repo to GitHub
|
||||
- make some change so you can run the `nx release` command again and get a changelog generated
|
||||
- now also get a GH release created
|
||||
|
||||
Note, you can still use `--dry-run` and it'd show you the URL where the GitHub release would be created. You can also use the `--skip-publish` to skip the NPM publishing.
|
||||
|
||||
## Programmatic Mode
|
||||
|
||||
As you’ve seen, you can use `nx release` right away with minimal configuration. However, we are very well aware that many real-world scenarios are more complex, you want/need more control over when the version is happening, when the changelog generation kicks in and so on. This is why we also introduced a **programmatic API.**
|
||||
|
||||
This approach gives you full control to embed Nx Release into your current release flow. There’s a nice [example script in our docs](/features/manage-releases#using-the-programmatic-api-for-nx-release) that can help you get started.
|
||||
|
||||
Create a file — I call it `release.ts` - at the root of my workspace. Nx Release obviously doesn't care how the file is called or where you place it. You can also go with plain JS.
|
||||
|
||||
Here’s the [example script from our docs](/features/manage-releases#using-the-programmatic-api-for-nx-release):
|
||||
|
||||
```ts
|
||||
import { releaseChangelog, releasePublish, releaseVersion } from 'nx/release';
|
||||
import * as yargs from 'yargs';
|
||||
|
||||
(async () => {
|
||||
const options = await yargs
|
||||
.version(false) // don't use the default meaning of version in yargs
|
||||
.option('version', {
|
||||
description:
|
||||
'Explicit version specifier to use, if overriding conventional commits',
|
||||
type: 'string',
|
||||
})
|
||||
.option('dryRun', {
|
||||
alias: 'd',
|
||||
description:
|
||||
'Whether or not to perform a dry-run of the release process, defaults to true',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
})
|
||||
.option('verbose', {
|
||||
description:
|
||||
'Whether or not to enable verbose logging, defaults to false',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
})
|
||||
.parseAsync();
|
||||
|
||||
const { workspaceVersion, projectsVersionData } = await releaseVersion({
|
||||
specifier: options.version,
|
||||
dryRun: options.dryRun,
|
||||
verbose: options.verbose,
|
||||
});
|
||||
|
||||
await releaseChangelog({
|
||||
versionData: projectsVersionData,
|
||||
version: workspaceVersion,
|
||||
dryRun: options.dryRun,
|
||||
verbose: options.verbose,
|
||||
});
|
||||
|
||||
// The returned number value from releasePublish will be zero if all projects are published successfully, non-zero if not
|
||||
const publishStatus = await releasePublish({
|
||||
dryRun: options.dryRun,
|
||||
verbose: options.verbose,
|
||||
});
|
||||
process.exit(publishStatus);
|
||||
})();
|
||||
```
|
||||
|
||||
You can invoke it with [tsx](https://github.com/privatenumber/tsx) or [tsnode](https://www.npmjs.com/package/ts-node).
|
||||
|
||||
```
|
||||
pnpm dlx tsx release.ts
|
||||
```
|
||||
|
||||
Notice by default in the script we have `dry-run` enabled as a more cautious approach to not accidentally publish something as we keep editing and trying our programmatic setup.
|
||||
|
||||
From here on you have full control and can pretty much do whatever works best for your workspace setup. Common examples include:
|
||||
|
||||
- moving files to a common root-level `dist/` folder and version and release them from there. This is pretty common to avoid messing with your src files and swapping versions there, allowing you to always depend on the latest local packages for instance.
|
||||
- setting up fully automated releases on CI, including enabling provenance support. Our docs have [more details on how to set that up](/recipes/nx-release/publish-in-ci-cd) or check out the linked talk above which goes through those steps.
|
||||
|
||||
## Wrapping Up
|
||||
|
||||
With this release of Nx Release it is fully ready to be used. Make sure to check out our docs on [Managing Releases](/features/manage-releases) as well as our [release-related recipes](/recipes/nx-release).
|
||||
|
||||
Here are some example repositories already leveraging Nx release:
|
||||
|
||||
- [Our own Nx Repo](https://github.com/nrwl/nx/blob/master/scripts/nx-release.ts)
|
||||
- [RxJS repo](https://github.com/ReactiveX/rxjs/tree/master/scripts)
|
||||
- [Typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/blob/main/tools/release/release.mts)
|
||||
- [Watch the live stream](https://www.youtube.com/watch?v=lYNa6Ct4RkY) with [Kent](https://twitter.com/kentcdodds) and [James](https://twitter.com/MrJamesHenry) as they enable Nx Release on the [EpicWeb workshop app repository](https://github.com/epicweb-dev/kcdshop)
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,148 +0,0 @@
|
||||
---
|
||||
title: Launch Nx Week Recap
|
||||
slug: 'launch-nx-week-recap'
|
||||
authors: [Zack DeRose]
|
||||
cover_image: '/blog/images/2024-02-15/featured_img.png'
|
||||
tags: [nx, nx-cloud, releases, changelog]
|
||||
---
|
||||
|
||||
We just finished wrapping up [Launch Nx Week](/launch-nx), which ran from February 5–9, including a full conference on Thursday!
|
||||
|
||||
In this article, we’re going to recap all the things launched during Launch Nx Conf, as well as all the talks given during the conference! Here’s a set of links so you can fast-forward to the stuff you’re most interested in if you want!
|
||||
|
||||
**Prefer a video?**
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/Ed1ZCNqWF1Q?si=m8LRyeJy_ZbCODJP" title="Launch Nx Week Recap" /%}
|
||||
|
||||
---
|
||||
|
||||
- [Nx 18.0 && Project Crystal](#nx-180-project-crystal)
|
||||
- [Project Crystal — By Juri Strumpflohner](#conference-talk-project-crystal)
|
||||
- [Project Crystal + .NET in Action — By Craigory Coppola](#conference-talk-project-crystal-net-in-action)
|
||||
- [New Plugin: @nx/nuxt](#new-plugin-nxnuxt)
|
||||
- [Nx Agents](#nx-agents-launched)
|
||||
- [Nx Agents Walkthrough: Effortlessly Fast CI Built for Monorepos — By Rares Matei](#conference-talk-nx-agents-walkthrough-effortlessly-fast-ci-built-for-monorepos)
|
||||
- [Solving E2E Tests — By Altan Stalker](#conference-talk-solving-e2e-tests)
|
||||
- [Tusky](#tusky)
|
||||
- [Nx Release](#nx-release-is-stable)
|
||||
- [Releasing Nx Release — By James Henry](#conference-talk-releasing-nx-release)
|
||||
|
||||
## Nx 18.0 && Project Crystal
|
||||
|
||||
Exciting news!! We’ve broken our standard release cadence of publishing a new major version every 6 months to release Nx 18, just 3 months after releasing Nx 17 in December 2024.
|
||||
|
||||
Juri announced launched Project Crystal with this video:
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/Ed1ZCNqWF1Q?si=m8LRyeJy_ZbCODJP" title="Nx Project Crystal" /%}
|
||||
|
||||
The short version is: Nx project crystal means Nx plugins make your codebases work more seemlessly with less config.
|
||||
|
||||
For the long version, checkout this blog post by Juri where he describes [how Nx plugins are now more like VsCode Extentions](/blog/what-if-nx-plugins-were-more-like-vscode-extensions)!
|
||||
|
||||
And don’t miss our two conference talks on this subject:
|
||||
|
||||
### Conference Talk: Project Crystal
|
||||
|
||||
**Speaker:** [Juri Strumpflohner](https://twitter.com/juristr) | [slides](https://drive.google.com/file/d/1q6M0drdssU7Zb-4Y_f99fuupuOl1KYQN/view)
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/PzCgpM7qtTU?si=U5aEC7XjeS1NeKbT" title="Juri Conference Talk Project Crystal" /%}
|
||||
|
||||
### Conference Talk: Project Crystal + .NET in Action
|
||||
|
||||
**Speaker:** [Craigory Coppola](https://twitter.com/enderagent) | [slides](https://docs.google.com/presentation/d/1uveIe6HB7xwSkh7FBGZfF8Unh5YZzm5X/edit?usp=sharing&ouid=109667724870581513512&rtpof=true&sd=true) | [example repo](https://github.com/AgentEnder/nx-launch-conf-demos)
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/fh-yzOuQGE8?si=a-XRXJfaBCerz3i-" title="Craigory Conference Talk Project Crystal + .Net" /%}
|
||||
|
||||
## New Plugin: @nx/nuxt
|
||||
|
||||
On Tuesday we launched our newest plugin, and our first plugin to be :gem:crystalized:gem: from it’s very beginning: @nx/nuxt!
|
||||
|
||||
Zack explains Nuxt and how to use the new plugin in this video:
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/1L-bDvEemoc?si=wHQvuTDfXFs3vjaC" title="Zack explaining new plugin @nx/nuxt" /%}
|
||||
|
||||
To add @nx/nuxt to your codebase, use the command:
|
||||
|
||||
```shell
|
||||
> nx add @nx/nuxt
|
||||
```
|
||||
|
||||
Huge thanks to [Katerina](https://twitter.com/psybercity) for her work on the plugin, and Nuxt maintainer, [Daniel Roe](https://twitter.com/danielcroe), for helping to guide the project!
|
||||
|
||||
Zack, Katerina, and Daniel got together to live code a working tic-tac-toe app using the new Nuxt plugin and [partykit](https://www.partykit.io/) in this livestream:
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/uHwUxFYX2DY?si=lyYg3XPIx688k8HY" title="@nx/nuxt with Nuxt maintainer, Daniel Roe!" /%}
|
||||
|
||||
## Nx Agents Launched
|
||||
|
||||
Nx Agents are here!! We launched Nx Agents on Wednesday, and it climbed into the top 20 on [Product Hunt](https://www.producthunt.com/products/nx-cloud#nx-agents)!
|
||||
|
||||
We’re very excited about Nx Agents because we think that in its current state, Continuous Integration/Deployment is broken, and we think that Nx paired with Nx Agents fixes Continuous Integration. Zack explains more in this video:
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/_FSHQIwITic?si=jDpwTVXLYFXiHEm0" title="Nx Agents" /%}
|
||||
|
||||
Be sure to also checkout the [blog post from Isaac on Nx Agents](/blog/fast-effortless-ci), including explanations of exclusive features like auto-detection and retrying for flaky tasks and automatically splitting lengthy end-to-end tests!
|
||||
|
||||
You can [signup for Nx Agents NOW](https://nx.app/products/agents#content), and find out [more of the details in our docs](/ci/features/distribute-task-execution)!
|
||||
|
||||
Rares and Altan are on the team building Nx Cloud, and during the conference, they dove deeper into some of these topics:
|
||||
|
||||
### Conference Talk: Nx Agents Walkthrough: Effortlessly Fast CI Built for Monorepos
|
||||
|
||||
**Speaker:** [Rares Matei](https://twitter.com/__rares) | [slides](https://drive.google.com/file/d/1k-cGCJUMP4axcCWoeih8n3dvo1oO_i_X/view?usp=sharing) | [example repo](https://github.com/rarmatei/shops-workflows/pulls) | [failed runs example](https://cloud.nx.app/cipes/65b27cf6d3ef5934decad746?utm_source=pull-request&utm_medium=comment) | [nx agents full run](https://cloud.nx.app/cipes/65b38179d3ef5934dede74c2?utm_source=pull-request&utm_medium=comment)
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/XS-exYYP_Gg?si=skZTGYPEVJG7BrYZ" title="Nx Agents Walkthrough" /%}
|
||||
|
||||
### Conference Talk: Solving E2E Tests
|
||||
|
||||
**Speaker:** [Altan Stalker](https://twitter.com/StalkAltan)
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/EO_tGa0Nx1s?si=3AvGaJkJaCeEjz1r" title="Solving E2E Tests" /%}
|
||||
|
||||
## Tusky
|
||||
|
||||
Thursday, we had a surprise announcement for our newest product on Nx Cloud: Tusky.
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/Xfvv09wSoM8?si=jnzyLHtdWBLwx_U0" title="Tusky" /%}
|
||||
|
||||
Tusky is an Artificial Intelligence for your codebase, and it will be available on Nx Cloud soon — starting in Spring of 2024.
|
||||
|
||||
In the teaser video, you can see some ways Tusky will be integrated into Nx Cloud to provide explainations on failed tasks, or cache misses.
|
||||
|
||||
The more exciting features of Tusky though includes the ability to perfectly optimize the number of agents used per PR — even spinning up and tearing down agents mid-pipeline! This is a huge optimization for task distribution, and will help optimize both walltime of your CI, as well as compute costs!
|
||||
|
||||
Tusky will also be able to provide organizational-level insights to your codebase, including automatically detecting development groups (like lines of business) inside your codebase based on commit history, and providing statistical insights on their commit patterns. We’re also excited about Tusky being able to make suggestions on things you can do to further optimize your codebase — like generating a PR to introduce [Codeowners](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) to the repo!
|
||||
|
||||
## Nx Release Is Stable
|
||||
|
||||
Versioning and publishing packages is always a bit tricky. Mix in the added complexity of having multiple packages — sometimes with different versioning or publishing strategies — inside the same codebase, and things can get weird quick!
|
||||
|
||||
For a long time, Nx has been purposefully versioning and publishing agnostic, but given our time spent as [stewards of Lerna](https://blog.nrwl.io/lerna-is-dead-long-live-lerna-61259f97dbd9) (the OG Javascript monorepo tool), we’ve been able to take alot of that experience and finally feel confident creating our own versioning and publishing implementation.
|
||||
|
||||
Therefore, we’ve been working on a new command to the Nx CLI: [nx release](/recipes/nx-release/get-started-with-nx-release#get-started-with-nx-release). We launched this on Friday of our Launch Nx week!
|
||||
|
||||
Juri goes into [full details in this blog post](/blog/versioning-and-releasing-packages-in-a-monorepo), and James Henry — our Director of Engineering and the primary engineer responsible for both maintaining Lerna and creating Nx Release — expands further in his conference talk:
|
||||
|
||||
### Conference Talk: Releasing Nx Release
|
||||
|
||||
**Speaker:** [James Henry](https://twitter.com/MrJamesHenry) | [example repo](https://github.com/JamesHenry/nx-release-cmd)
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/KjZKFGu3_9I?si=83DjaHhpBSP7NP4n" title="Releasing Nx Release" /%}
|
||||
|
||||
## Wrapping up
|
||||
|
||||
Juri, Zack, and Victor wrapped up the week with a livestream recapping the launches from the week — including answering your questions live:
|
||||
|
||||
{% youtube src="https://www.youtube.com/embed/xjLrFvEcxZw?si=O70JD4NgseP0lknA" title="Launch Nx Week Wrap Up" /%}
|
||||
|
||||
That’s all for now folks! We’re just starting up a new iteration of development on Nx, so be sure to subscribe to [our YouTube channel](https://www.youtube.com/@nxdevtools) to get updates when new features land! Until next time, KEEP WORKING HARD!
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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)
|
||||
- [Speed up your CI](https://nx.app/)
|
||||
@@ -1,105 +0,0 @@
|
||||
---
|
||||
title: Monorepos - Why Speed Matters
|
||||
slug: 'monorepos-why-speed-matters'
|
||||
authors: ['Katerina Skroumpelou', 'Jeff Cross']
|
||||
tags: nx, nxdevtools, speed, ci
|
||||
cover_image: '/blog/images/2024-03-20/featured_img.png'
|
||||
---
|
||||
|
||||
In the ever-evolving landscape of software development, efficiency and speed are vital. As projects grow in complexity, developers and teams need tools that can keep up without sacrificing quality or performance.
|
||||
|
||||
Nx is a suite of powerful tools designed to optimize your development workflow, which sets the [building blocks for a fast CI](/ci/concepts/building-blocks-fast-ci). Nx is always innovating in many ways to make developers’ lives easier, but this post is exclusively focused on the things Nx has done in the past year to make development faster and faster.
|
||||
|
||||
## Why speed matters
|
||||
|
||||
The ability to iterate quickly and efficiently is vital for any software project. Speed in the development process offers several critical advantages:
|
||||
|
||||
- **Faster feedback loops:** Quick iterations mean immediate feedback, allowing teams to adapt, learn, and improve their work on the fly.
|
||||
- **Reduced time to market:** Accelerating the development process can significantly cut down the overall time to market, providing a competitive edge which reclaims revenue that would have otherwise been lost.
|
||||
- **Decreased developer frustration:** [No more waiting for builds and tests to complete](/ci/concepts/reduce-waste). A streamlined workflow keeps morale high and productivity higher.
|
||||
|
||||
If you’re using Nx already, you’re already familiar with
|
||||
|
||||
- [**Affected**](/ci/features/affected) - identifying and running tasks only on projects impacted by code changes,
|
||||
- [**Nx Replay**](/ci/features/remote-cache) - our powerful cache and
|
||||
- [**Nx Agents**](/ci/features/distribute-task-execution) - the concept of [Parallelization and Distribution](/ci/concepts/parallelization-distribution).
|
||||
|
||||
But let’s see all the extra things we did this past year to make everything faster.
|
||||
|
||||
## Speed at the core
|
||||
|
||||
### Rustifying Nx
|
||||
|
||||
The Nx daemon has seen significant enhancements, notably through the use of Rust to calculate file hashes behind the scenes. This improvement not only speeds up the start-up times but also optimizes performance even without the daemon, especially on CI environments where the daemon isn't used. The benchmark results at [this repo](https://github.com/vsavkin/large-monorepo) showcase the remarkable speed improvements, making Nx competitive with native code solutions while maintaining the accessibility and flexibility of Node.js. Nx is still Node-first, so contributions are easier and only the most performance-critical parts of Nx are native code.
|
||||
|
||||
### **Task Hasher and archive file innovations**
|
||||
|
||||
The introduction of a task hasher written in Rust, alongside the use of an archive file to store workspace file hashes (`.nx/cache`), has significantly reduced the need for repetitive file system accesses. This innovation means that running multiple Nx commands in CI is much faster, as file hashing becomes unnecessary after the initial run.
|
||||
|
||||
**The Archive file**
|
||||
|
||||
The archive file is a binary file that contains the workspace file hashes with their last modified time. Every time Nx starts (ie, running `nx run project:target`) it gets all the files with their last modified time, and compares it to the archive. If the file exists in the archive, then Nx does not access the file system to read the file to hash (reading individual files is slower than just getting a list of files from a directory). So running multiple nx commands in CI is quick to start because Nx does not need to constantly hash files.
|
||||
|
||||
### Nx Replay
|
||||
|
||||

|
||||
|
||||
Nx Replay enables caching and reusing of task results. It’s our well known Nx remote cache! It allows developers to avoid re-running expensive tasks by retrieving the cached results from a remote cache. This significantly improves build and test performance, as well as developer productivity. Nx Replay is also critical to the functioning of Nx Agents, which rely on the remote cache to ensure that the results of a task will be shared with every agent that needs them. By using Nx Replay, developers can optimize their workflows and reduce the time spent waiting for tasks to complete.
|
||||
|
||||
With Nx Replay, you can see significant speed improvements in your CI pipelines for modified PRs. What’s also important is that if a task has been executed in CI, a developer running that same task locally can reuse the task result instead of actually running the task. So you will also see improvements locally.
|
||||
|
||||
### **Nx Agents**
|
||||
|
||||

|
||||
|
||||
[Nx Agents](/ci/features/distribute-task-execution) represent the pinnacle of task distribution optimization, ensuring that tasks are executed as efficiently as possible based on the specific requirements of each change. Some features that make up this effort are:
|
||||
|
||||
- [Easy integration with existing providers](/ci/features/distribute-task-execution#cicd-guides)
|
||||
- Distribution is handled on the Nx Cloud infrastructure and all you need is a single line. What’s more, all results are played back to your original CI provider script which triggers the Nx Cloud distribution, so that you can make use of the resulting artifacts
|
||||
- [Efficient task distribution](/ci/features/dynamic-agents)
|
||||
- Save compute resources and reduce costs, minimizing idle time and compute waste
|
||||
- Dynamic sizing based on PR size
|
||||
- [Tusky](https://nx.app/products/tusky) - our AI solution - coming soon
|
||||
- You set your desired cost/speed ratio, and you forget about any more configuration. We ensure maximum speed up to limits you set yourself.
|
||||
|
||||
You can read more about Nx Agents [here](https://nx.app/products/agents#content).
|
||||
|
||||
### **Atomizer**
|
||||
|
||||
The [Atomizer](/ci/features/split-e2e-tasks) splits your Cypress or Playwright e2e tests by file. This significantly enhances granularity for caching, parallel execution, and flaky test identification. This granular approach ensures that individual test results can be cached and only the necessary tests rerun, greatly reducing CI pipeline times and facilitating more accurate flaky test detection.
|
||||
|
||||
{% youtube src="https://www.youtube.com/watch?v=0YxcxIR7QU0" /%}
|
||||
|
||||
### **Addressing flaky tests with test deflaking**
|
||||
|
||||
Flaky tests can be a significant bottleneck in the CI process. Nx tackles this issue head-on by intelligently [re-running only the flaky tasks](/ci/features/flaky-tasks), rather than the entire pipeline. This approach not only saves time but also provides developers with more confidence in their CI pipeline's reliability.
|
||||
|
||||

|
||||
|
||||
Nx creates a hash of all the inputs for a task whenever it is run. If it encounters a task that fails with a particular set of inputs and then succeeds with those same inputs, Nx knows for a fact that the task is flaky.
|
||||
|
||||
## New Nx features that tie in with our core speed improvements
|
||||
|
||||
### Module Federation
|
||||
|
||||
With the help of Nx and the Module Federation setup that Nx offers, you can split up large Angular apps into smaller “vertical slices”. This can significantly speed up your builds and app startup time. Nx has revolutionized the use of Module Federation, especially in how static remotes are built and served. We make use of Nx’s task orchestration, allowing users to fine tune the number of builds happening in parallel to improve local startup time, manage machine resources better, allow for scaling.
|
||||
|
||||
### First-Class Playwright support
|
||||
|
||||
With first-class support for Playwright through **`@nx/playwright`**, Nx offers out-of-the-box generators to run Playwright tests efficiently. This integration is especially powerful with features like Atomizer, enhancing the testing process's speed and reliability.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Nx provides an unparalleled toolkit for developers and teams looking to optimize their development workflows, and we keep making it faster. By intelligently leveraging modern technologies and innovative optimizations, Nx delivers speed, efficiency, and reliability, allowing teams to focus on what matters most: building great software.
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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 Community Discord](https://go.nx.dev/community)
|
||||
- [Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
|
||||
- [Speed up your CI](https://nx.app)
|
||||
@@ -1,189 +0,0 @@
|
||||
---
|
||||
title: Reliable CI. A new execution model fixing both flakiness and slowness
|
||||
slug: 'reliable-ci-a-new-execution-model-fixing-both-flakiness-and-slowness'
|
||||
authors: [Victor Savkin]
|
||||
cover_image: '/blog/images/2024-03-21/featured_img.png'
|
||||
tags: [nx, nx-cloud, releases]
|
||||
---
|
||||
|
||||
The proverbial slow and flaky CI isn’t the failure of the developers or even the testing tools. It’s the failure of the CI execution model we relied on for the last 20 years.
|
||||
|
||||
**By switching from the old CI model, implemented as a graph of VMs, to the new one, implemented as a graph of tasks, we can solve both the flakiness and slowness.**
|
||||
|
||||
In this blog post I’ll explore why this is the case, and how you can do it by using Nx Cloud.
|
||||
|
||||
---
|
||||
|
||||
## History of CI
|
||||
|
||||
It’s easy to forget that Continuous Integration has been around for only 20 years. For reference, UI frameworks have been around since the 1970s.
|
||||
|
||||

|
||||
|
||||
The innovation in the CI space has been primarily in where the CI process is described and how the configuration is written. How the CI actually works has largely remained the same.
|
||||
|
||||
## How CI works
|
||||
|
||||
A traditional CI execution is a directed acyclic graph (DAG) of virtual machines (VMs). Each VM (often called a job) installs dependencies, restores NPM cache, and then runs the necessary steps to verify that, say, some tests pass.
|
||||
|
||||

|
||||
|
||||
**This CI execution is a distributed process, and a poorly designed one. It works reasonably well only when the number of jobs/VMs is very small and falls apart when the number gets larger.**
|
||||
|
||||
---
|
||||
|
||||
## Why does the current system not work?
|
||||
|
||||
**Every distributed system needs efficient communication between nodes and the ability to tolerate failure.**
|
||||
|
||||
The traditional CI execution model offers **very basic** means of communication: the status code propagation and ad-hoc ways of uploading/downloading files. Because the communication is effortful, in practice, **it’s either not done at all or very minimally.**
|
||||
|
||||
To understand why the traditional CI execution model fails to handle failures, let’s review the types of failures we can have.
|
||||
|
||||
Failures can be:
|
||||
|
||||
- hard _(npm fails to install, nothing can run)_
|
||||
- soft _(a test takes a lot longer than it should because, perhaps, due to an out-of-memory issue)_
|
||||
|
||||
Focusing on the latter is just as important as focusing on the former. **Slow CI is broken CI.**
|
||||
|
||||
Tasks can fail:
|
||||
|
||||
- legitimately _(a broken build)_
|
||||
- for external reasons _(npm install fails cause npm is down)_
|
||||
- for unknown reasons _(a flaky test)_
|
||||
|
||||
**The traditional CI execution model doesn’t tolerate any of these failures.**
|
||||
|
||||
## Problem in numbers
|
||||
|
||||
Let’s see how varying a few parameters affects the probability of the CI execution failing.
|
||||
|
||||
| Number of VMS | Avg Tests per VM | Flaky Test Probability | Slow Test Probability | Broken CI Builds (Flaky) | Slow CI Builds |
|
||||
| ------------- | ---------------- | ---------------------- | --------------------- | ------------------------ | -------------- |
|
||||
| 5 | 10 | 0.1% | 0.3% | 5% | 15% |
|
||||
| 10 | 10 | 0.1% | 0.3% | 10% | 26% |
|
||||
| 50 | 10 | 0.1% | 0.3% | 39% | 78% |
|
||||
| 5 | 10 | 0.5% | 1% | 23% | 41% |
|
||||
| 10 | 10 | 0.5% | 1% | 40% | 65% |
|
||||
| 50 | 10 | 0.5% | 1% | 92% | 99% |
|
||||
|
||||
**The result is much worse than most intuitively expect.** For instance, assuming that an **e2e test has 1 in 1000 chance (0.1%) of failing** for a flaky reason, when the number of e2e tests reaches 500, **the probability of the CI failing for a flaky reason reaches 39%**, and the vast majority of CI executions are slowed down. Note, this is an exceptionally stable test suite. The bottom part of the table is more representative of a typical e2e suite, and the CI becomes “broken” at a much smaller scale.
|
||||
|
||||
One can try to fix it by increasing the robustness of the tests, but at some point, doing this is costly. If an e2e test has a 10% chance of a flaky failure, it’s relatively easy to bring this number to 1%. Going from 1% to 0.5% is harder. Going from 0.5% to 0.1% is exceptionally hard and may be practically impossible. Testing complex systems is simply a difficult task.
|
||||
|
||||
This is the formula for the failed CI run:
|
||||
|
||||

|
||||
|
||||
**As you can see, as the number of tests grows, the exponent will make more and more CI executions fail for flaky reasons. In this model, the only way to combat it is by increasing $chanceThatTestRunsWithoutFlakes$, but the more tests you have, the more challenging this becomes.** That's why a lot of projects move away from e2e tests or run them nightly (where they always fail).
|
||||
|
||||
**It's not the team's fault. It's simply a losing battle with the current CI execution model.**
|
||||
|
||||
We focused on the tests but the issue is more general.
|
||||
|
||||
VMs sometimes fail to execute their setup steps (e.g., some package fails to install), and in this model it results in a failed CI execution.
|
||||
|
||||
One of the setup steps can take a lot longer. Because the probabilities multiply, when the number of agents is high enough, this will happen frequently and will slow down a large number of CI executions. This is the formula for this case:
|
||||
|
||||

|
||||
|
||||
For instance, with 50 agents, if there is a **1% chance that NPM install will take an extra 5 minutes, 40% of CI executions will be affected by it**, and your CI execution time goes from, say, **20m to 25m**. The slowness doesn't amortize across agents.
|
||||
|
||||
---
|
||||
|
||||
## Solution: Change the model
|
||||
|
||||
**There is only that much gain to be had by optimizing the effectiveness and robustness of small units. Instead, we should focus on creating the systems we can use to achieve incredible performance and resilience with ordinary small units.**
|
||||
|
||||
The CI model above has a fundamental problem: it is static. It defines a fixed number of VM, where each has a unique job to do. The VMs aren't interchangeable. If one of them fails or is slow, no other VM can help it.
|
||||
|
||||
Nx Cloud introduces an entirely different model. Instead of the graph of VMs, the CI execution is expressed as a graph of tasks.
|
||||
|
||||
By adding the following line…
|
||||
|
||||
```shell
|
||||
npx nx-cloud start-ci-run --distribute-on="5 linux-medium-js"
|
||||
```
|
||||
|
||||
... you are telling Nx Cloud to create five Nx Agents to execute all the tasks from all the commands that will follow.
|
||||
|
||||

|
||||
|
||||
**Nx Agents** are essentially VMs, but they are different from the ones in the traditional CI execution model.
|
||||
|
||||
You can imagine the **traditional CI** model as a team where every member is given a **unique set of tasks.** If a team member gets sick, their work won't be completed, and the **CI execution will fail.** If a team member is slow, **no one can help them.** Everyone will just have to wait for them to complete their assignment.
|
||||
|
||||
The **Nx Agent model** is a team where the **work doesn't get assigned ahead of time.** Instead, there is a pile of work in the middle of the room, and every **team member can take any piece of work** and do it. Once they are done with one piece of work, they can take another one. If a member gets sick, **a slightly smaller team can complete the job.** If a member is **slow**, the **other members will split the work.** In practice, it is even better than this. If an Nx Agent fails, another one will be started in its place.
|
||||
|
||||

|
||||
|
||||
## Solution in numbers
|
||||
|
||||
Let's examine the two cases above.
|
||||
|
||||
First, `npm install` **failing will not break the build.** Nx Cloud will simply spawn another Nx Agent in its place.
|
||||
|
||||
The slowness of the `npm install` will affect the CI execution but in a very different way because it will be **amortized across agents.** So with 50 agents executing, adding 5 minutes to an `npm install` of one of the agents, will only increase the CI execution time by 6 seconds _(5 _ 60 / 50)\*.
|
||||
|
||||
Second, **[Nx Cloud knows what specific tests are flaky](/ci/features/flaky-tasks), and if they fail, it will rerun them on a separate agent.** Rerunning them on a separate agent is crucial. Often flaky failures will continue to fail when rerun on the same agent.
|
||||
|
||||
This is the formula:
|
||||
|
||||

|
||||
|
||||
If we examine the case above, where the test flaky happened 1 in 1000, two retries will result in _1 - (1–0.999)^3^ = 0.999999999_, which **brings the likelihood of the CI failing for a flaky reason to 0.000005%.**
|
||||
|
||||
**The Nx Cloud model handles both hard and soft failures. Your agents can crush or be slow - doesn't matter. Your tests can fail or be slow - doesn't matter. Your CI will complete fast.**
|
||||
|
||||
---
|
||||
|
||||
## Can we fix the traditional model?
|
||||
|
||||
It's possible to make the traditional CI model more reliable but the solution isn't adequate for large workspaces.
|
||||
|
||||
For instance, in principle, it is possible to add retrying logic to every CI step. It's cumbersome and widely-used CI steps (e.g., popular Github Actions) don't do it. But even if you manage to wrap every step, this isn't bulletproof because many failures are unrecoverable.
|
||||
|
||||
**Slow setup steps simply cannot be fixed or amortized.**
|
||||
|
||||
One can add rerunning the same e2e test multiple times on the same machine to deal with flakes but there are numerous problems with this.
|
||||
|
||||
**One of the most common reasons why e2e tests are flaky is because they create side effects.** For instance, they can create records in the db. They have some logic to clean it up, but it is imperfect. Or tests can assume that some fixture will be modified once. Ideally, this would never happen, but in practice it happens all the time. **Nx Cloud will always retry the same task on a different agent to avoid this problem.**
|
||||
|
||||
**Don't forget, you actually need to know which tests are flaky. Most of your failures will be legitimate so rerunning all the tests will make you CI slow. And those reruns cannot be amortized.**
|
||||
|
||||
There are other practical problems. If you have enough VMs, rebuilding the app under test on each of them becomes impractical, so you need to build it once and send it to all your VMs. Your e2e tests have to be partitioned into small units to allow for efficient distribution. And the list of problems goes on.
|
||||
|
||||
**The traditional CI model is not really fixable because it makes no assumptions about its execution steps and, as a result, it cannot do anything automatically.**
|
||||
|
||||
**The Nx Cloud model only works when your build system and CI are built for each other.** The CI needs to know the task graph, what a task requires, what files it creates. Without this information, nothing can be distributed, nothing can be rerun or deflaked. Nx and Nx Cloud (with Nx Agents) fit together. Nx can run tasks locally or can pass this metadata to Nx Cloud which will orchestrate the same computation across many VMs. Nx Cloud will move the right files to the right agents, split large e2e test suites, and deflake the tests automatically. This only works because of the assumptions made by the CI and the build tool.
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
|
||||
The focus of this post is robustness. But the Nx Cloud model is also significantly faster and more efficient. Because Nx Agents are interchangeable, Nx Cloud will use a different number of agents depending on the size of the code change. It can split the work more effectively across agents, reduce the number of npm installs required, split large e2e suites into smaller units and more.
|
||||
|
||||
## Summary
|
||||
|
||||
The spirit of this post is similar to Alan Kay's quote, "A change of perspective is worth 80 IQ points". By changing the CI execution model, Nx Cloud makes some difficult, almost unsolvable, problems easy.
|
||||
|
||||
---
|
||||
|
||||
You can learn more about Nx Cloud on [nx.app](https://nx.app) and Nx open source on [nx.dev]().
|
||||
|
||||
**Nx Cloud Pro includes a 2-month free trial** that is definitely worth trying out if you're curious what Cloud Pro can do for your CI. You can try out Nx Agents, e2e test splitting, deflaking and more. [Learn more about Nx Cloud Pro.](https://nx.app/campaigns/pro)
|
||||
|
||||
We also have a **Pro for Startups** plan which offers agents that are 3.5x cheaper than analogous VMs on CircleCI or Github Actions. [Learn more about Nx Pro for Startups.](https://nx.app/campaigns/pro-for-startups)
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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 Community Discord](https://go.nx.dev/community)
|
||||
- [Nx Youtube Channel](https://www.youtube.com/@nxdevtools)
|
||||
- [Speed up your CI](https://nx.app)
|
||||
@@ -1,218 +0,0 @@
|
||||
---
|
||||
title: Manage Your Gradle Project using Nx
|
||||
slug: 'manage-your-gradle-project-using-nx'
|
||||
authors: ['Emily Xiong']
|
||||
cover_image: '/blog/images/2024-04-19/featured_img.png'
|
||||
tags: [nx, gradle, how-to]
|
||||
---
|
||||
|
||||
Here’s my situation: I have a Gradle workspace with multiple Gradle libraries. How do I easily view the relationships between different libraries? I have a monorepo workspace with both Gradle and Javascript libraries, how do I manage these libraries of different tech stacks?
|
||||
|
||||
We are very excited to announce our support for Gradle with our new plugin: `@nx/gradle`.
|
||||
|
||||
The Nx Gradle plugin registers Gradle projects in your Nx workspace. It allows Gradle tasks to be run through Nx. Nx effortlessly makes your [CI faster](/ci/intro/ci-with-nx).
|
||||
|
||||
> **Note:** this plugin is currently experimental.
|
||||
|
||||
This blog will show you:
|
||||
|
||||
- [What is Nx?](#what-is-nx)
|
||||
- [How to add Nx to a Gradle workspace](#how-to-add-nx-to-a-gradle-workspace)
|
||||
- [How to add @nx/gradle to an existing Nx workspace](#how-to-add-nxgradle-to-an-existing-nx-workspace)
|
||||
|
||||
---
|
||||
|
||||
## What is Nx?
|
||||
|
||||
Before we start, let’s answer this question: what is Nx and why should we use it?
|
||||
|
||||
From [nx.dev](): “Nx is a build system with built-in tooling and advanced CI capabilities. It helps you maintain and scale monorepos, both locally and on CI.” It sounds good, what benefits does it bring?
|
||||
|
||||
Nx adds the following features to your workspace:
|
||||
|
||||
- [Cache task results](/features/cache-task-results): By storing task outputs in a cache, subsequent runs can skip redundant computations and reuse previously calculated results, significantly speeding up build processes. Nx intelligently manages this caching mechanism, invalidating the cache automatically when relevant inputs change.
|
||||
- [Distribute task execution](/ci/features/distribute-task-execution): Nx CI efficiently distributes tasks across multiple machines for faster build times. It uses a distributed task execution algorithm to intelligently divide and assign tasks to available resources, minimizing redundant work and maximizing parallelism.
|
||||
- [Run only tasks affected by a PR](/ci/features/affected): Nx identifies changes made since a specified base commit or branch, and then selectively runs tasks (like tests, linting, or builds) related to those changes.
|
||||
- [Interactively explore your workspace](/features/explore-graph): Nx allows developers to visualize and understand the dependencies and relationships within their projects.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## How to add Nx to a Gradle Workspace?
|
||||
|
||||
Now we understand the benefits of Nx, now let’s set it up. The setup is pretty easy, just need to run one command.
|
||||
|
||||
In the workspace, run the below command:
|
||||
|
||||
```shell
|
||||
npx nx@latest init
|
||||
```
|
||||
|
||||
In the terminal, it should output:
|
||||
|
||||
```shell
|
||||
Setting Nx up installation in `.nx`. You can run Nx commands like: `./nx --help`
|
||||
CREATE nx.json
|
||||
UPDATE .gitignore
|
||||
CREATE .nx/nxw.js
|
||||
CREATE nx.bat
|
||||
CREATE nx
|
||||
|
||||
NX Recommended Plugins:
|
||||
|
||||
Add these Nx plugins to integrate with the tools used in your workspace.
|
||||
|
||||
✔ Which plugins would you like to add? Press <Space> to select and <Enter> to submit. · @nx/gradle
|
||||
|
||||
|
||||
added 111 packages, and audited 112 packages in 2s
|
||||
|
||||
21 packages are looking for funding
|
||||
run `npm fund` for details
|
||||
|
||||
found 0 vulnerabilities
|
||||
|
||||
✔ Installing @nx/gradle@latest...
|
||||
✔ Initializing @nx/gradle...
|
||||
```
|
||||
|
||||
That’s it! Now we have Nx in our Gradle project.
|
||||
|
||||
### Example: Gradle Init
|
||||
|
||||
If you create your Gradle workspace using gradle init, by running `npx nx@latest init`, you will get:
|
||||
|
||||

|
||||
|
||||
It adds a .nx folder in the workspace root and nx executable files. Now you can run commands using ./nx (or nx.bat for Windows machines).
|
||||
|
||||
For example, you can run Gradle tasks using Nx now:
|
||||
|
||||
```shell
|
||||
# macos/linux
|
||||
./nx <gradle task> <gradle project>
|
||||
|
||||
# windows
|
||||
nx.bat <gradle task> <gradle project>
|
||||
```
|
||||
|
||||
If you build your Gradle library using the command `./gradlew :app:build` or `gradlew.bat :app:build`, you can run `./nx build app` or `nx.bat build app` to build your Gradle library.
|
||||
|
||||
Now you can an alternative way to run Gradle tasks, how can we leverage Nx?
|
||||
|
||||
### Nx Graph
|
||||
|
||||
To see the project graph, run the below command:
|
||||
|
||||
```shell
|
||||
# macos/linux
|
||||
./nx graph
|
||||
|
||||
# windows
|
||||
nx.bat graph
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Run Only Tasks Affected by a PR
|
||||
|
||||
As mentioned before, Nx enables the ability to run only the tasks affected by a specific PR by running the below command:
|
||||
|
||||
```shell
|
||||
# macos/linux
|
||||
./nx affected -t <task>
|
||||
|
||||
# windows
|
||||
nx.bat affected -t <task>
|
||||
```
|
||||
|
||||
For example, when you run `nx affected -t build`, Nx uses your git information to determine the files you changed in your PR. Nx determines the list of projects in the workspace that can be affected by this change and only runs the build task against changed files.
|
||||
|
||||
You can also visualize the affected projects highlighted using the [Nx graph](/features/explore-graph). Simply run:
|
||||
|
||||
```shell
|
||||
# macos/linux
|
||||
./nx affected:graph
|
||||
|
||||
# windows
|
||||
nx.bat affected:graph
|
||||
```
|
||||
|
||||
### Nx Console
|
||||
|
||||
Furthermore, instead of running the command in terminal, you can use the editor tool [Nx Console](/getting-started/editor-setup). Anything you can do with Nx, you can do with Nx Console.
|
||||
|
||||

|
||||
|
||||
To download:
|
||||
|
||||
- [**Nx Console - Visual Studio Marketplace**](https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console)
|
||||
- [**Nx Console - IntelliJ IDEs Plugin | Marketplace**](https://plugins.jetbrains.com/plugin/21060-nx-console)
|
||||
|
||||
---
|
||||
|
||||
## How to add @nx/gradle to an existing Nx workspace?
|
||||
|
||||
If you have an existing Nx workspace, to add `@nx/gradle`, just run:
|
||||
|
||||
```shell
|
||||
npx nx add @nx/gradle
|
||||
```
|
||||
|
||||
That is it, it will add `@nx/gradle` plugin to your Nx workspace.
|
||||
|
||||
You can view inferred tasks for Gradle project in your workspace, open the [project details view](/features/explore-graph#explore-projects-in-your-workspace) in Nx Console or run `nx show project my-project --web` in the command line.
|
||||
|
||||
For all the interred tasks, you can run using Nx instead of Gradle:
|
||||
|
||||
```shell
|
||||
nx <gradle task> <gradle project> [options]
|
||||
```
|
||||
|
||||
For example, if you run `./gradlew :app:build` or `gradlew.bat :app:build` using Gradle command, to run using Nx: `nx build app`.
|
||||
|
||||
### How @nx/gradle Infers Tasks
|
||||
|
||||
The `@nx/gradle` plugin will create an Nx project for each Gradle configuration file present. Any of the following files will be recognized as a Gradle configuration file:
|
||||
|
||||
- `gradle.build`
|
||||
- `gradle.build.kts`
|
||||
|
||||
### @nx/gradle Configuration
|
||||
|
||||
The `@nx/gradle` is configured in the plugins array in `nx.json`:
|
||||
|
||||
```json {% fileName="nx.json" %}
|
||||
{
|
||||
"plugins": [
|
||||
{
|
||||
"plugin": "@nx/gradle",
|
||||
"options": {
|
||||
"testTargetName": "test",
|
||||
"classesTargetName": "classes",
|
||||
"buildTargetName": "build"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Once a Gradle configuration file has been identified, the targets are created with the name you specify under `testTargetName`, `classesTargetName`, or `buildTargetName` in the `nx.json` plugins array. The default names for the inferred targets are `test`, `classes`, and `build`.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Here is how to set up Nx with the Gradle workspace. Hopefully, this gives you a good insight into how to get started with Gradle with Nx. The plugin is currently experimental, you can submit GitHub issues: [https://github.com/nrwl/nx/issues](https://github.com/nrwl/nx/issues).
|
||||
|
||||
---
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,238 +0,0 @@
|
||||
---
|
||||
title: Nx 19.0 Release!!
|
||||
slug: 'nx-19-release'
|
||||
authors: ['Zack DeRose']
|
||||
cover_image: '/blog/images/2024-05-08/nx-19-thumbnail.png'
|
||||
tags: [nx, release]
|
||||
---
|
||||
|
||||
Hey folks!
|
||||
|
||||
It's been awhile since February, where in the midsts our big launch week, we launched Nx 18. Nx 18 introduced the biggest new enhancement to Nx to date: Project Crystal.
|
||||
|
||||
[](/blog/what-if-nx-plugins-were-more-like-vscode-extensions)
|
||||
|
||||
You can read more on project crystal [here](/blog/what-if-nx-plugins-were-more-like-vscode-extensions). The main thing to know though is that Project Crystal adjusts the idea of Nx plugins, so that the mere presence of the plugin in your Nx Workspace removes the need to maintain any configuration for most cases.
|
||||
|
||||
Nx 18 was a significant departure for us in several ways - maybe the most noteworthy was that we broke our years-long cadence of releasing a new major version every 6 months.
|
||||
|
||||
Nx 19 represents a return to form, matching the reliable 6-month schedule. Nx 19 is releasing now 6 months from when Nx 17 released, making Nx 18 a special case to match the impact that Project Crystal had on Nx as a whole.
|
||||
|
||||
Here's the rundown of the major things we'll cover in this update:
|
||||
|
||||
- [NEW PLUGIN: @nx/gradle](#new-plugin-nxgradle)
|
||||
- [Nx Atomizer Enhancements](#nx-atomizer-enhancements)
|
||||
- [Associated Technologies Added To Tasks](#associated-technologies-added-to-tasks)
|
||||
- [Generators to Convert to Project Crystal](#generators-to-convert-to-project-crystal)
|
||||
- [BREAKING CHANGE: Updating Bundled Environment Variables: `NX_` to `NX_PUBLIC_`](#breaking-change-updating-bundled-environment-variables-to)
|
||||
- [General Crystal Polishing](#general-crystal-polishing)
|
||||
- [Nx Cloud Updates!](#nx-cloud-updates)
|
||||
- [More Miscellaneous Updates!](#more-miscellaneous-updates)
|
||||
- [New Conference: Monorepo World 2024](#new-conference-monorepo-world-2024)
|
||||
- [Wrapping Up, And A Heartfelt Thank You](#wrapping-up-and-a-heartfelt-thank-you)
|
||||
- [Learn More](#learn-more)
|
||||
|
||||
**Prefer a video?**
|
||||
|
||||
{% youtube
|
||||
src="https://youtu.be/U6eO8-w9DR0"
|
||||
title="Nx 19.0 Has Landed!!"
|
||||
width="100%" /%}
|
||||
|
||||
---
|
||||
|
||||
## NEW PLUGIN: @nx/gradle
|
||||
|
||||
[](/blog/manage-your-gradle-project-using-nx)
|
||||
|
||||
We've launched a new first-party plugin for Gradle!
|
||||
|
||||
Emily, the engineer that worked on this effort, posted an entire article where you can find the full details [here](/blog/manage-your-gradle-project-using-nx).
|
||||
|
||||
Thanks to the benefits of Project Crystal - which allows us to determine information to create Nx Tasks based on the way your Gradle projects are setup - by adding this plugin, it makes it easy to add Gradle into an existing workspace without needing any Nx-specific configuration.
|
||||
|
||||
In addition, you can now find [a new tutorial on using Nx with Gradle](/getting-started/tutorials/gradle-tutorial) which will expand on the [Spring framework](https://spring.io/)'s tutorial for multi-module projects and show you how Nx further enhances the developer experience of that workspace. This tutorial takes you all the way through setting up your CI pipeline with Nx Cloud, so be sure to check it out!
|
||||
|
||||
As an editorial note, this Gradle plugin is a big step for Nx, as it represents the first major step we're taking outside of the Javascript ecosystem. This has always been the goal: to robustly support monorepos and full-stack development, even across language and ecosystem barriers. We're using this Gradle plugin internally for our closed-sourced projects (in particular Nx Cloud) and in [Nx Console](https://github.com/nrwl/nx-console/blob/master/package.json#L74) and we're very proud of this plugin and all that it represents.
|
||||
|
||||
## Nx Atomizer Enhancements
|
||||
|
||||
The Nx Atomizer launched in Nx 18 with Project Crystal. Essentially, "Atomizer" is the name we use to describe how Nx will automatically shard your e2e tests in your CI pipelines via our first party plugins. You can find out more information in the video below:
|
||||
|
||||
{% youtube
|
||||
src="https://youtu.be/0YxcxIR7QU0"
|
||||
title="10x Faster e2e Tests!"
|
||||
width="100%" /%}
|
||||
|
||||
While the Atomizer was great for reducing the time of end-to-end tests in CI pipelines, one unfortunate trade-off of the Atomizer was that rather than having a single task to represent your end-to-end validation tasks, you instead had a collection of tasks. This tended to clutter up tools when viewing your Nx Tasks. It also wasn't very clear which tasks belonged to which group!
|
||||
|
||||
We've addressed this in Nx 19 by introducing the concept of Task Grouping inside of our tooling.
|
||||
|
||||
Now, when running the command:
|
||||
|
||||
```shell
|
||||
nx show project nx-dev-e2e --web
|
||||
```
|
||||
|
||||
You'll see the following in the project view:
|
||||
|
||||

|
||||
|
||||
Notice how all tasks are now appropriately grouped in the `E2E (CI)` group!
|
||||
|
||||
You can also find the same enhancements in Nx Cloud. Below is a view of all tasks in the [CI pipeline](https://staging.nx.app/runs/ctbAZfiLy3):
|
||||
|
||||
[](https://staging.nx.app/runs/ctbAZfiLy3)
|
||||
|
||||
Notice how all e2e groups are collapsed by default to give a concise view, while allowing you to expand to see how each individual task is progressing!
|
||||
|
||||
Project Crystal is gathering the information to group your tasks, so this is automatically inferred for you based on the actual contents of your workspace. You can see this information by running the command:
|
||||
|
||||
```shell
|
||||
nx show project nx-dev-e2e --json | jq
|
||||
```
|
||||
|
||||

|
||||
|
||||
All Nx Plugins that support this concept of "Atomization" will do this automatically for you, and if you are writing a custom plugin for your own tool or special use-case, you can implect this inferrence to group your targets in Nx and Nx Cloud tooling as well. We'll be adding more documentation on how to implement this in your own plugins in the future.
|
||||
|
||||
## Associated Technologies Added To Tasks
|
||||
|
||||
We've added icons for associated technologies on tasks in the project detail web view:
|
||||
|
||||

|
||||
|
||||
And in Nx Cloud:
|
||||
|
||||

|
||||
|
||||
This will help easily identify at-a-glance the technology your tasks are associated with. All this is supported out of the box by our plugins and you should see this as soon as updating to Nx 19.
|
||||
|
||||
To see how we support this new feature, checkout the json view of our project again:
|
||||
|
||||
```shell
|
||||
nx show project e2e-angular --json | jq
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Generators to Convert to Project Crystal
|
||||
|
||||
As part of Nx 19, we've included generators to our Playwright, Cypress, and ESLint plugins to allow you to easily convert your projects to opt into Project Crystal features!
|
||||
|
||||
To convert your workspace, you can use the command:
|
||||
|
||||
```shell
|
||||
nx generate convert-to-inferred
|
||||
```
|
||||
|
||||
Before running this command you could expect your project detail view to look like this:
|
||||
|
||||
```shell
|
||||
nx show project my-react-app-e2e --web
|
||||
```
|
||||
|
||||

|
||||
|
||||
And after running the generator, you can expect your project detail view to look like this:
|
||||
|
||||

|
||||
|
||||
Notice that in addition to reducing the configuration in your `project.json` file, this generator will also turn on the Nx Atomizer for your Cypress and Playwright projects, so Nx will allow sharding for your end-to-end tasks in CI based on the tests present in your workspace going forward. Note that Nx Agents are very highly highly encouraged as a way of taking advantage of this sharding capability.
|
||||
|
||||
As a note, if you cannot see `e2e-ci` tasks after converting your cypress projects, you can [take a closer look here](/nx-api/cypress/documents/overview#splitting-e2e-tasks-by-file) at how to configure them correctly.
|
||||
|
||||
## BREAKING CHANGE: Updating Bundled Environment Variables: `NX_` to `NX_PUBLIC_`
|
||||
|
||||
An important update for folks using environment variables in their builds!
|
||||
|
||||
Previously, when building your projects in an Nx workspace, our builds were setup to include any environment variables prefixed with `NX_` and include them in the built artifact so they would be accessible there. This is often used for setting things like urls for different versions of external services to hit - for example, sending requests to your local development server while developing, but pointing to the production service for your production build.
|
||||
|
||||
Going forward, these environment variables will need to be prefixed with `NX_PUBLIC_`. This is a security consideration - by requiring a more specific prefix we're making sure that no secrets end up in your production builds unintentionally!
|
||||
|
||||
Because of the nature of this change we will NOT be providing an automatic migration. So be sure to manually update any environment variables that you want bundled into your builds from `NX_` to `NX_PUBLIC_`.
|
||||
|
||||
## General Crystal Polishing
|
||||
|
||||
Along with the features above (many of which further build upon Nx Project Crystal), we've done quite a bit since Nx 18 to polish the features that we introduced with Project Crystal.
|
||||
|
||||
Since Nx 18 release, we also started using Project Crystal inside of the Nx repo itself (as well as in Nx Console and in our closed-source monorepo as well). This has allowed us to find many improvements we can make, as well as issues we can fix.
|
||||
|
||||
You can find a full list of fixes and features applied in this major release [here](https://github.com/nrwl/nx/releases/tag/19.0.0).
|
||||
|
||||
[](https://github.com/nrwl/nx/releases/tag/19.0.0)
|
||||
|
||||
With Project Crystal landed now, we're also adjusting our priorities to place a higher importance on stability. You should see this reflected in Nx 19.
|
||||
|
||||
As a note, we had noticed an increase in the rate of Github issues since Nx 18 launched. Previously, we had not allocated enough time to handling these issues. To address this, our team is going to be doubling the amount of time focusing on maintainence.
|
||||
|
||||
In addition - we are adding more process to make the triage of these issues more efficient. Github issues will now be assigned directly to specific members of the Nx Core Team. The main intention of this change is to increase the efficiency of the time that we do spend on addressing issues.
|
||||
|
||||
## Nx Cloud Updates!
|
||||
|
||||
[](https://nx.app/#deep-understanding)
|
||||
|
||||
We've got some cool stats to share from our users regarding the benefits of [Nx Cloud](https://nx.app), our premium CI service. The three areas we've identified as the critical aspects of a CI provider are: speed, cost, and reliablity. In these areas we've seen:
|
||||
|
||||
- **speed**: Reported 30% - 70% faster CI
|
||||
- **cost**: Reported 40% - 75% reduction in CI costs
|
||||
- **reliability**: Nx Cloud's automatic detection and retrying of flaky tests makes the issue of flaky tests largely go away entirely. You can read more on [our thoughts on reliability here](/blog/reliable-ci-a-new-execution-model-fixing-both-flakiness-and-slowness).
|
||||
|
||||
In February, we launched two big enhancements to Nx Cloud: the [Atomizer](/ci/features/split-e2e-tasks) and [Nx Agents](https://nx.app/products/agents#content).
|
||||
|
||||
Since then, the Atomizer has received a nice UI update (as we had seen earlier):
|
||||
|
||||
[](https://staging.nx.app/runs/ctbAZfiLy3)
|
||||
|
||||
Since February, we also revamped our task distribution algorithms. This has resulted in a 5-20% (depending on the repo) increase in both speed and cost efficiency for our users.
|
||||
|
||||
If you are interested in trying Nx Cloud, go to [https://nx.app](https://nx.app).
|
||||
|
||||
## More Miscellaneous updates!
|
||||
|
||||
We've been updating our docs site. We've tweeked the search to make it more helpful, updated navigation, and as you can tell since you're reading it, we've moved our blog to [nx.dev/blog](blog) as well.
|
||||
|
||||
We've also added a page for [Nx Enterprise](/enterprise):
|
||||
|
||||
[](/enterprise)
|
||||
|
||||
And! We've revamped our newsletter, written by our CTO, Victor Savkin. [Subscribe here](https://go.nx.dev/nx-newsletter) to start receiving it!
|
||||
|
||||
## NEW CONFERENCE: Monorepo World 2024
|
||||
|
||||
[](https://monorepo.world)
|
||||
|
||||
We've got a new conference coming up: [Monorepo World 2024](https://monorepo.world)!
|
||||
|
||||
This conference will cover all things dealing with monorepos, and we're excited to host it at the Computer History Museum in Mountain View, California this October 7th!
|
||||
|
||||
You can [reserve your tickets now](https://ti.to/nx-conf/monorepoworld2024)! In person tickets start at $300 a piece for Early Bird (open until May 31), and online attendance is free.
|
||||
|
||||
The [Call for Speakers](https://sessionize.com/monorepo-world) is open now, and will close on June 20, 2024.
|
||||
|
||||
We'll be excited to see y'all there!
|
||||
|
||||
## Wrapping Up, And A Heartfelt Thank You
|
||||
|
||||
That's it for now!
|
||||
|
||||
I did want to take a moment to give a heartfelt thank you to everyone out there. If you weren't aware, I had something of a [personal tragedy](https://twitter.com/zackderose/status/1759695615573864758) in February, not long after our big launch week.
|
||||
|
||||
[](https://twitter.com/zackderose/status/1759695615573864758)
|
||||
|
||||
For me, this blogpost represents the beginning of my return to Nx, and to the larger dev community.
|
||||
|
||||
To everyone out there - your kindness and care meant more to me than you could know. I intend to return that the positivity and care you invested in me in those dark days back as best I can. So here's looking forward to hard work, more releases, and better days to come.
|
||||
|
||||
With Many Thanks,
|
||||
|
||||
Zack
|
||||
|
||||
## Learn more
|
||||
|
||||
- [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](https://nx.app/)
|
||||
@@ -1,56 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "Juri Strumpflohner",
|
||||
"image": "/blog/images/Juri Strumpfloner.jpeg",
|
||||
"twitter": "juristr",
|
||||
"github": "juristr"
|
||||
},
|
||||
{
|
||||
"name": "Colum Ferry",
|
||||
"image": "/blog/images/Colum Ferry.jpeg",
|
||||
"twitter": "FerryColum",
|
||||
"github": "Coly010"
|
||||
},
|
||||
{
|
||||
"name": "Emily Xiong",
|
||||
"image": "/blog/images/Emily Xiong.jpeg",
|
||||
"twitter": "xiongemily",
|
||||
"github": "xiongemi"
|
||||
},
|
||||
{
|
||||
"name": "Isaac Mann",
|
||||
"image": "/blog/images/Isaac Mann.jpeg",
|
||||
"twitter": "mannisaac",
|
||||
"github": "isaacplmann"
|
||||
},
|
||||
{
|
||||
"name": "Katerina Skroumpelou",
|
||||
"image": "/blog/images/Katerina Skroumpelou.jpeg",
|
||||
"twitter": "psybercity",
|
||||
"github": "mandarini"
|
||||
},
|
||||
{
|
||||
"name": "Max Kless",
|
||||
"image": "/blog/images/Max Kless.jpeg",
|
||||
"twitter": "MaxKless",
|
||||
"github": "MaxKless"
|
||||
},
|
||||
{
|
||||
"name": "Victor Savkin",
|
||||
"image": "/blog/images/Victor Savkin.jpeg",
|
||||
"twitter": "victorsavkin",
|
||||
"github": "vsavkin"
|
||||
},
|
||||
{
|
||||
"name": "Zack DeRose",
|
||||
"image": "/blog/images/Zack DeRose.jpeg",
|
||||
"twitter": "zackderose",
|
||||
"github": "ZackDeRose"
|
||||
},
|
||||
{
|
||||
"name": "Jeff Cross",
|
||||
"image": "/blog/images/Jeff Cross.jpeg",
|
||||
"twitter": "jeffbcross",
|
||||
"github": "jeffbcross"
|
||||
}
|
||||
]
|
||||
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 409 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 185 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 16 KiB |