Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ffec8312e | |||
| f59cae8ee8 | |||
| fdf6eae74e | |||
| 001d288c05 | |||
| 0e48a0f57c | |||
| 887c2c2212 | |||
| b6eb77209a | |||
| 023d230a22 | |||
| fe29c88d0a | |||
| d75d8b4bc9 | |||
| 9788a7a8c3 | |||
| 6694149eb1 | |||
| 004698f617 | |||
| ff12ac7bfc | |||
| 275662d70a | |||
| 0076bfcd53 | |||
| e9c00fe1c8 | |||
| 9e2ba1eb40 | |||
| 1ccf1ab532 | |||
| 96333fca57 | |||
| 7e691e9fbb | |||
| 0a4875c083 | |||
| 220cd014a5 | |||
| bd5d0d8906 | |||
| 0b7db04902 | |||
| 5a74cc00f4 | |||
| 07e37a5f09 | |||
| eb28deb8a8 | |||
| f636c95986 | |||
| be0e5634a7 | |||
| f88d89047e | |||
| 668770e4b6 | |||
| 6f0ce741de | |||
| b495f21345 | |||
| 67de1a95b7 | |||
| 37565378dc | |||
| 7a4b377889 | |||
| ceefe6e023 | |||
| e0d9838d3d | |||
| 955d5cbbea | |||
| 552978e23e | |||
| ee9b7a3913 | |||
| f1a99ee048 | |||
| e56be6c7a2 | |||
| 31584c83f5 |
@@ -1,12 +0,0 @@
|
||||
[build]
|
||||
target-dir = 'build/target'
|
||||
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
rustflags = [
|
||||
"-C",
|
||||
"target-feature=-crt-static",
|
||||
]
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-musl-gcc"
|
||||
rustflags = ["-C", "target-feature=-crt-static"]
|
||||
+188
-94
@@ -4,171 +4,265 @@ version: 2.1
|
||||
# ORBS
|
||||
# -------------------------
|
||||
orbs:
|
||||
nx: nrwl/nx@1.6.2
|
||||
rust: circleci/rust@1.6.0
|
||||
browser-tools: circleci/browser-tools@1.4.8
|
||||
nx: nrwl/nx@1.0.0
|
||||
|
||||
# -------------------------
|
||||
# EXECUTORS
|
||||
# DEFAULTS
|
||||
# -------------------------
|
||||
machine:
|
||||
pre:
|
||||
- mkdir ~/.cache/yarn
|
||||
- mkdir ~/Library/Caches/Homebrew
|
||||
- mkdir /usr/local/Homebrew
|
||||
|
||||
dependencies:
|
||||
cache_directories:
|
||||
- ~/.cache/yarn
|
||||
- /usr/local/Homebrew
|
||||
- ~/Library/Caches/Homebrew
|
||||
override:
|
||||
- yarn install
|
||||
- brew install
|
||||
|
||||
defaults: &defaults
|
||||
working_directory: ~/repo
|
||||
|
||||
# -------------------------
|
||||
# EXECUTORS
|
||||
# -------------------------
|
||||
executors:
|
||||
linux:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: cimg/rust:1.73.0-browsers
|
||||
resource_class: medium+
|
||||
- image: cimg/node:14.17-browsers
|
||||
|
||||
macos:
|
||||
<<: *defaults
|
||||
resource_class: macos.m1.medium.gen1
|
||||
macos:
|
||||
xcode: '14.2.0'
|
||||
xcode: &_XCODE_VERSION '13.0.0'
|
||||
|
||||
android:
|
||||
<<: *defaults
|
||||
docker:
|
||||
- image: reactnativecommunity/react-native-android
|
||||
resource_class: 'large'
|
||||
environment:
|
||||
- ADB_INSTALL_TIMEOUT: 10
|
||||
- _JAVA_OPTIONS: '-Xmx1024m -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap'
|
||||
- GRADLE_OPTS: '-Xmx2014m -Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-XX:+UnlockExperimentalVMOptions -XX:+HeapDumpOnOutOfMemoryError"'
|
||||
|
||||
# -------------------------
|
||||
# COMMANDS
|
||||
# -------------------------
|
||||
commands:
|
||||
run-pnpm-install:
|
||||
run-yarn-install:
|
||||
parameters:
|
||||
os:
|
||||
type: string
|
||||
steps:
|
||||
- restore_cache:
|
||||
name: Restore pnpm Package Cache
|
||||
name: Restore Yarn 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
|
||||
- nrwl-nx-yarn-packages-<< parameters.os >>-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: yarn install --prefer-offline --frozen-lockfile --non-interactive --cache-folder ~/.cache/yarn
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
key: nrwl-nx-yarn-packages-<< parameters.os >>-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- node_modules
|
||||
- ~/.cache/yarn
|
||||
|
||||
install-pnpm:
|
||||
steps:
|
||||
- run:
|
||||
name: Install PNPM
|
||||
command: npm install --prefix=$HOME/.local -g pnpm@6.9.1
|
||||
|
||||
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: Install pnpm package manager (macos)
|
||||
name: Configure Detox Environment, Install applesimutils
|
||||
command: |
|
||||
npm install -g @pnpm/exe@8
|
||||
- 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" }}
|
||||
paths:
|
||||
- ~/.pnpm-store
|
||||
- ~/.cache/Cypress
|
||||
- node_modules
|
||||
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-yarn-install:
|
||||
os: << parameters.os >>
|
||||
- install-pnpm
|
||||
|
||||
# -------------------------
|
||||
# JOBS
|
||||
# -------------------------
|
||||
jobs:
|
||||
# -------------------------
|
||||
# JOBS: Main Linux
|
||||
# JOBS: Agent
|
||||
# -------------------------
|
||||
main-linux:
|
||||
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: 'false'
|
||||
steps:
|
||||
- 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: npx nx-cloud start-agent
|
||||
no_output_timeout: 60m
|
||||
|
||||
# -------------------------
|
||||
# JOBS: Main
|
||||
# -------------------------
|
||||
main:
|
||||
executor: linux
|
||||
environment:
|
||||
NX_CLOUD_DISTRIBUTED_EXECUTION: 'true'
|
||||
NX_E2E_CI_CACHE_KEY: e2e-circleci-linux
|
||||
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:
|
||||
name: Check Documentation
|
||||
command: pnpm nx documentation --no-dte
|
||||
no_output_timeout: 20m
|
||||
command: yarn documentation
|
||||
- run:
|
||||
name: Run Checks/Lint/Test/Build
|
||||
no_output_timeout: 60m
|
||||
name: Check Imports
|
||||
command: yarn check-imports
|
||||
- run:
|
||||
name: Check Formatting
|
||||
command: yarn nx format:check --base=$NX_BASE --head=$NX_HEAD
|
||||
- run:
|
||||
name: Check Commit
|
||||
command: yarn check-commit
|
||||
- run:
|
||||
name: Check Package dependencies
|
||||
command: yarn depcheck
|
||||
- run:
|
||||
name: Run Tests
|
||||
command: |
|
||||
pids=()
|
||||
npx nx affected --target=test --base=$NX_BASE --head=$NX_HEAD --parallel --max-parallel=3
|
||||
- run:
|
||||
name: Run Lint
|
||||
command: |
|
||||
npx nx affected --target=lint --base=$NX_BASE --head=$NX_HEAD --parallel --max-parallel=3
|
||||
- run:
|
||||
name: Run Builds
|
||||
command: |
|
||||
npx nx affected --target=build --base=$NX_BASE --head=$NX_HEAD --parallel --max-parallel=3
|
||||
- run:
|
||||
name: Run E2E Tests
|
||||
command: |
|
||||
npx nx affected --target=e2e --base=$NX_BASE --head=$NX_HEAD --exclude=e2e-react-native,e2e-detox --parallel --max-parallel=1
|
||||
no_output_timeout: 45m
|
||||
- run:
|
||||
name: Stop All Running Agents for This CI Run
|
||||
command: npx nx-cloud stop-all-agents
|
||||
|
||||
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 &
|
||||
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) &
|
||||
pids+=($!)
|
||||
|
||||
for pid in "${pids[@]}"; do
|
||||
wait "$pid"
|
||||
done
|
||||
# -------------------------
|
||||
# JOBS: Main-MacOS
|
||||
# -------------------------
|
||||
mainmacos:
|
||||
executor: macos
|
||||
environment:
|
||||
NX_CLOUD_DISTRIBUTED_EXECUTION: 'true'
|
||||
NX_E2E_CI_CACHE_KEY: e2e-circleci-macos
|
||||
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:
|
||||
main-branch-name: 'master'
|
||||
- run:
|
||||
name: Run E2E Tests for macOS
|
||||
name: Run E2E Tests
|
||||
command: |
|
||||
pnpm nx affected -t e2e-macos-ci --parallel=1 --base=$NX_BASE --head=$NX_HEAD
|
||||
npx nx affected --target=e2e --base=$NX_BASE --head=$NX_HEAD --exclude=e2e-js,e2e-next,e2e-gatsby,e2e-workspace-create,e2e-workspace-integrations,e2e-workspace-core,e2e-react,e2e-web,e2e-angular-extensions,e2e-angular-core,e2e-cli,e2e-nx-plugin,e2e-storybook,e2e-cypress,e2e-node,e2e-linter,e2e-jest --max-parallel=1
|
||||
no_output_timeout: 45m
|
||||
- run:
|
||||
name: Stop All Running Agents for This CI Run
|
||||
command: npx nx-cloud stop-all-agents
|
||||
|
||||
# -------------------------
|
||||
# WORKFLOWS(JOBS)
|
||||
# WORKFLOWS
|
||||
# -------------------------
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
build:
|
||||
jobs:
|
||||
- main-linux
|
||||
- agent:
|
||||
name: 'agent1'
|
||||
- agent:
|
||||
name: 'agent2'
|
||||
- agent:
|
||||
name: 'agent3'
|
||||
- agent:
|
||||
name: 'agent4'
|
||||
- agent:
|
||||
name: 'agent5'
|
||||
- agent:
|
||||
name: 'agent6'
|
||||
- agent:
|
||||
name: 'agent7'
|
||||
- agent:
|
||||
name: 'agent8'
|
||||
- agent:
|
||||
name: 'agent9'
|
||||
pm: 'npm'
|
||||
os: 'macos'
|
||||
# - agent:
|
||||
# name: 'agent10'
|
||||
# pm: 'npm'
|
||||
# os: 'android'
|
||||
# -------------------------
|
||||
# Pull request logic
|
||||
# -------------------------
|
||||
- main:
|
||||
name: pull-request
|
||||
- mainmacos:
|
||||
name: main-macos-e2e
|
||||
name: pull-request-macos
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
module.exports = {
|
||||
types: [
|
||||
{ value: 'feat', name: 'feat: A new feature' },
|
||||
{ value: 'fix', name: 'fix: A bug fix' },
|
||||
{ value: 'docs', name: 'docs: Documentation only changes' },
|
||||
{
|
||||
value: 'cleanup',
|
||||
name: 'cleanup: A code change that neither fixes a bug nor adds a feature',
|
||||
},
|
||||
{
|
||||
value: 'chore',
|
||||
name: "chore: Other changes that don't modify src or test files",
|
||||
},
|
||||
],
|
||||
|
||||
scopes: [
|
||||
{ name: 'angular', description: 'anything Angular specific' },
|
||||
{ name: 'core', description: 'anything Nx core specific' },
|
||||
{ name: 'nxdev', description: 'anything related to docs infrastructure' },
|
||||
{ name: 'nextjs', description: 'anything Next specific' },
|
||||
{ name: 'gatsby', description: 'anything Gatsby specific' },
|
||||
{ name: 'nest', description: 'anything Nest specific' },
|
||||
{ name: 'node', description: 'anything Node specific' },
|
||||
{ name: 'express', description: 'anything Express specific' },
|
||||
{ name: 'nx-plugin', description: 'anything Nx Plugin specific' },
|
||||
{ name: 'react', description: 'anything React specific' },
|
||||
{ name: 'react-native', description: 'anything React Native specific' },
|
||||
{ name: 'web', description: 'anything Web specific' },
|
||||
{ name: 'linter', description: 'anything Linter specific' },
|
||||
{ name: 'storybook', description: 'anything Storybook specific' },
|
||||
{ name: 'dep-graph', description: 'anything dep-graph app specific' },
|
||||
{
|
||||
name: 'testing',
|
||||
description: 'anything testing specific (e.g., jest or cypress)',
|
||||
},
|
||||
{
|
||||
name: 'repo',
|
||||
description: 'anything related to managing the repo itself',
|
||||
},
|
||||
{ name: 'misc', description: 'misc stuff' },
|
||||
{ name: 'devkit', description: 'devkit-related changes' },
|
||||
],
|
||||
|
||||
allowTicketNumber: true,
|
||||
isTicketNumberRequired: false,
|
||||
ticketNumberPrefix: 'TICKET-',
|
||||
ticketNumberRegExp: '\\d{1,5}',
|
||||
|
||||
// it needs to match the value for field type. Eg.: 'fix'
|
||||
/*
|
||||
scopeOverrides: {
|
||||
fix: [
|
||||
{name: 'merge'},
|
||||
{name: 'style'},
|
||||
{name: 'e2eTest'},
|
||||
{name: 'unitTest'}
|
||||
]
|
||||
},
|
||||
*/
|
||||
// override the messages, defaults are as follows
|
||||
messages: {
|
||||
type: "Select the type of change that you're committing:",
|
||||
scope: '\nDenote the SCOPE of this change (optional):',
|
||||
// used if allowCustomScopes is true
|
||||
customScope: 'Denote the SCOPE of this change:',
|
||||
subject:
|
||||
'Write a SHORT, IMPERATIVE (lowercase) description of the change:\n',
|
||||
body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
|
||||
breaking: 'List any BREAKING CHANGES (optional):\n',
|
||||
footer:
|
||||
'List any ISSUES CLOSED by this change (optional). E.g.: #31, #34:\n',
|
||||
confirmCommit: 'Are you sure you want to proceed with the commit above?',
|
||||
},
|
||||
|
||||
allowCustomScopes: false,
|
||||
allowBreakingChanges: ['feat', 'fix'],
|
||||
// skip any questions you want
|
||||
skipQuestions: ['ticketNumber'],
|
||||
|
||||
// limit subject length
|
||||
subjectLimit: 100,
|
||||
// breaklineChar: '|', // It is supported for fields body and footer.
|
||||
// footerPrefix : 'ISSUES CLOSED:'
|
||||
// askForBreakingChangeFirst : true, // default is false
|
||||
};
|
||||
@@ -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
|
||||
@@ -1,33 +0,0 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"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.
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# 4 space indentation
|
||||
[*.{js,ts,jsx,tsx}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[package.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -1 +0,0 @@
|
||||
node_modules
|
||||
+4
-58
@@ -5,64 +5,10 @@
|
||||
"node": true
|
||||
},
|
||||
"ignorePatterns": ["**/*.ts"],
|
||||
"plugins": ["@typescript-eslint", "@nx"],
|
||||
"extends": ["plugin:storybook/recommended"],
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": [],
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"no-restricted-imports": ["error", "create-nx-workspace"],
|
||||
"@typescript-eslint/no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": [
|
||||
{
|
||||
"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."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storybook/no-uninstalled-addons": [
|
||||
"error",
|
||||
{
|
||||
"ignore": ["@nx/react/plugins/storybook"],
|
||||
"packageJsonLocation": "../../package.json"
|
||||
}
|
||||
]
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.json"],
|
||||
"parser": "jsonc-eslint-parser",
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["**/executors/**/schema.json", "**/generators/**/schema.json"],
|
||||
"rules": {
|
||||
"@nx/workspace/valid-schema-description": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {
|
||||
"@nx/enforce-module-boundaries": [
|
||||
"error",
|
||||
{
|
||||
"enforceBuildableLibDependency": true,
|
||||
"checkDynamicDependenciesExceptions": [".*"],
|
||||
"allow": [],
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "*",
|
||||
"onlyDependOnLibsWithTags": ["*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
"overrides": []
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: "🐞 Bug Report"
|
||||
about: Report a bug
|
||||
labels: "type: bug"
|
||||
|
||||
---
|
||||
|
||||
<!-- Please do your best to fill out all of the sections below! -->
|
||||
|
||||
## Current Behavior
|
||||
<!-- What is the behavior that currently you experience? -->
|
||||
|
||||
## Expected Behavior
|
||||
<!-- What is the behavior that you expect to happen? -->
|
||||
<!-- Is this a regression? .i.e Did this used to be the behavior at one point? -->
|
||||
|
||||
## Steps to Reproduce
|
||||
<!-- Help us help you by making it easy for us to reproduce your issue! -->
|
||||
|
||||
<!-- Can you reproduce this on https://github.com/nrwl/nx-examples? -->
|
||||
<!-- If so, open a PR with your changes and link it below. -->
|
||||
<!-- If not, please provide a minimal Github repo -->
|
||||
<!-- At the very least, provide as much detail as possible to help us reproduce the issue -->
|
||||
|
||||
<!-- Remove this line -->
|
||||
This issue may not be prioritized if details are not provided to help us reproduce the issue.
|
||||
|
||||
### Failure Logs
|
||||
<!-- Please include any relevant log snippets or files here. -->
|
||||
|
||||
### Environment
|
||||
<!-- It's important for us to know the context in which you experience this behavior! -->
|
||||
<!-- Please paste the result of `nx report` below! -->
|
||||
@@ -1,82 +0,0 @@
|
||||
name: 🐞 Bug Report
|
||||
description: This form is to report unexpected behavior in Nx.
|
||||
labels: ["type: bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking your precious time to file an issue! 🙏 We are sorry for the inconvenience this issue has caused you and want to resolve it as soon as possible.
|
||||
|
||||
Help us help you! We know that your time is precious and hate to ask for any more of it, but the first step in fixing this issue is to understand the issue. Taking some extra time to ensure that we are able to reproduce the issue will help us significantly in resolving the issue.
|
||||
- type: textarea
|
||||
id: current-behavior
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: What is the behavior that currently you experience?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: What is the behavior that you expect to happen?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: repo
|
||||
attributes:
|
||||
label: GitHub Repo
|
||||
description: |
|
||||
This is extremely important! If this issue is reproduce-able on https://github.com/nrwl/nx-examples, you may use that as the repo.
|
||||
If not, please do take a few minutes of your time to create a repo to help us reproduce the issue.
|
||||
This is the best way to help us reproduce the issue and fix it as soon as possible.
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: Please provide some instructions to reproduce the issue in the repo provided above. Be as detailed as possible.
|
||||
value: |
|
||||
1.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: nx-report
|
||||
attributes:
|
||||
label: Nx Report
|
||||
description: Please paste the contents shown by `nx report`. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
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:
|
||||
label: Operating System
|
||||
description: What Operating System are you using?
|
||||
options:
|
||||
- label: macOS
|
||||
- label: Linux
|
||||
- label: Windows
|
||||
- label: Other (Please specify)
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: Is there any additional information that you can provide?
|
||||
- type: markdown
|
||||
id: disclaimer
|
||||
attributes:
|
||||
value: |
|
||||
> If we are not able to reproduce the issue, we will likely prioritize fixing other issues we can reproduce. Please do your best to fill out all of the sections above.
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
name: "📖 Documentation issue"
|
||||
about: Help improve our docs.
|
||||
labels: "type: docs"
|
||||
---
|
||||
### Documentation issue
|
||||
|
||||
<!-- (Update "[ ]" to "[x]" to check a box) -->
|
||||
|
||||
- [ ] Reporting a typo
|
||||
- [ ] Reporting a documentation bug
|
||||
- [ ] Documentation improvement
|
||||
- [ ] Documentation feedback
|
||||
|
||||
<!--
|
||||
If your issue is not regarding the documentation, please choose an issue type:
|
||||
https://github.com/nrwl/nx/issues/new/choose
|
||||
-->
|
||||
|
||||
### Is there a specific documentation page you are reporting?
|
||||
|
||||
Enter the URL or documentation section here.
|
||||
|
||||
### Additional context or description
|
||||
|
||||
Provide any additional details here as needed.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
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. -->
|
||||
|
||||
## 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 -->
|
||||
|
||||
|
||||
@@ -1,31 +1,9 @@
|
||||
<!--
|
||||
_[Please make sure you have read the submission guidelines before posting an PR](https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#submit-pr)_
|
||||
|
||||
# Community Plugin Submission
|
||||
|
||||
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,15 +20,4 @@ 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)
|
||||
-->
|
||||
|
||||
# Community Plugin Submission
|
||||
|
||||
## {replace with plugin's name}
|
||||
|
||||
<!--
|
||||
Describe what your plugin is and what is its goal or issues it addresses. If you don't provide a description, we will not merge your PR.
|
||||
Is it focused on a technology, tooling or behaviour? Does the plugin provide generators, executors or graph support?
|
||||
Do you know who is already using the plugin? Mention who is the author of the plugin.
|
||||
-->
|
||||
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)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
name: Unmergeable Labels Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [synchronize, opened, reopened, labeled, unlabeled]
|
||||
|
||||
jobs:
|
||||
do-not-merge:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
name: Prevent Merging
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for label
|
||||
run: |
|
||||
echo "${{ toJSON(github.event.*.labels.*.name) }}"
|
||||
node -e 'const forbidden = ["target: next major version", "pr status: needs tests", "pr status: in-progress", "blocked: needs rebase", "pr status: do not merge"];
|
||||
const match = ${{ toJSON(github.event.*.labels.*.name) }}.find(l => forbidden.includes(l.toLowerCase()));
|
||||
if (match) {
|
||||
console.log("Cannot merge PRs that are labeled with " + match);
|
||||
process.exit(1)
|
||||
}'
|
||||
+126
-565
@@ -2,625 +2,186 @@ name: E2E matrix
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 5 * * *"
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
debug_enabled:
|
||||
type: boolean
|
||||
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
|
||||
required: false
|
||||
default: false
|
||||
|
||||
env:
|
||||
CYPRESS_CACHE_FOLDER: ${{ github.workspace }}/.cypress
|
||||
|
||||
permissions: { }
|
||||
jobs:
|
||||
preinstall:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
node_version:
|
||||
- 20
|
||||
- 18
|
||||
exclude:
|
||||
# run just node v20 on macos
|
||||
- os: macos-latest
|
||||
node_version: 18
|
||||
|
||||
name: Cache install (${{ matrix.os }}, node v${{ matrix.node_version }})
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install PNPM
|
||||
run: |
|
||||
npm install -g @pnpm/exe@8
|
||||
|
||||
- name: Set node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache node_modules
|
||||
id: cache-modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
lookup-only: true
|
||||
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
|
||||
|
||||
- name: Homebrew cache directory path
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
id: homebrew-cache-dir-path
|
||||
run: echo "dir=$(brew --cache)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache Homebrew
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
lookup-only: true
|
||||
path: ${{ steps.homebrew-cache-dir-path.outputs.dir }}
|
||||
key: brew-${{ matrix.node_version }}
|
||||
restore-keys: |
|
||||
brew-
|
||||
|
||||
- name: Cache Cypress
|
||||
id: cache-cypress
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
lookup-only: true
|
||||
path: '${{ github.workspace }}/.cypress'
|
||||
key: ${{ runner.os }}-cypress
|
||||
|
||||
- name: Install Cypress
|
||||
if: steps.cache-cypress.outputs.cache-hit != 'true'
|
||||
run: npx cypress install
|
||||
|
||||
e2e:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
needs: preinstall
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 90
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
os-name: ubuntu
|
||||
os-cache-dir: /tmp/nx-cache
|
||||
- os: macos-latest
|
||||
os-name: osx
|
||||
os-cache-dir: /tmp/nx-cache
|
||||
- os: windows-latest
|
||||
os-name: windows
|
||||
os-cache-dir: ./nx-cache
|
||||
node_version:
|
||||
- 20
|
||||
- 18
|
||||
- '14'
|
||||
- '16'
|
||||
package_manager:
|
||||
- npm
|
||||
- yarn
|
||||
- pnpm
|
||||
project:
|
||||
- e2e-angular
|
||||
packages:
|
||||
- e2e-angular-core,e2e-angular-extensions
|
||||
- e2e-cli,e2e-nx-plugin,e2e-jest,e2e-linter
|
||||
- e2e-cypress
|
||||
- e2e-detox
|
||||
- e2e-esbuild
|
||||
- e2e-eslint
|
||||
- e2e-expo
|
||||
- e2e-gradle
|
||||
- e2e-jest
|
||||
- e2e-js
|
||||
- e2e-lerna-smoke-tests
|
||||
- e2e-gatsby,e2e-react
|
||||
- e2e-next
|
||||
- e2e-node
|
||||
- e2e-nuxt
|
||||
- e2e-nx-init
|
||||
- e2e-nx
|
||||
- e2e-playwright
|
||||
- e2e-plugin
|
||||
- e2e-react
|
||||
- e2e-react-native
|
||||
- e2e-release
|
||||
- e2e-remix
|
||||
- e2e-rollup
|
||||
- e2e-storybook
|
||||
- e2e-vite
|
||||
- e2e-vue
|
||||
- e2e-web
|
||||
- e2e-webpack
|
||||
- e2e-workspace-create
|
||||
include:
|
||||
# os short names
|
||||
- os: ubuntu-latest
|
||||
os_name: 'Linux'
|
||||
- os: macos-latest
|
||||
os_name: 'MacOS'
|
||||
# test timeouts
|
||||
- os: ubuntu-latest
|
||||
os_timeout: 60
|
||||
- os: macos-latest
|
||||
os_timeout: 90
|
||||
# codeowner groups
|
||||
- project: e2e-angular
|
||||
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
|
||||
codeowners: 'S04SYJGKSCT'
|
||||
- project: e2e-next
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-node
|
||||
codeowners: 'S04SJ6HHP0X'
|
||||
- project: e2e-nx-init
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-nx
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-plugin
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-release
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-react
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-react-native
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-web
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-rollup
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-storybook
|
||||
codeowners: 'S04SVQ8H0G5'
|
||||
- project: e2e-playwright
|
||||
codeowners: 'S04SVQ8H0G5'
|
||||
- project: e2e-remix
|
||||
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'
|
||||
- e2e-storybook
|
||||
- e2e-workspace-integrations,e2e-workspace-core,e2e-workspace-create
|
||||
- e2e-react-native,e2e-detox
|
||||
exclude:
|
||||
# exclude react-native tests from ubuntu
|
||||
- os: ubuntu-latest
|
||||
project: e2e-react-native
|
||||
node_version: '14'
|
||||
package_manager: pnpm
|
||||
- os: ubuntu-latest
|
||||
project: e2e-detox
|
||||
node_version: '16'
|
||||
package_manager: npm
|
||||
- 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
|
||||
project: e2e-cypress
|
||||
- node_version: 18
|
||||
project: e2e-detox
|
||||
- node_version: 18
|
||||
project: e2e-esbuild
|
||||
- node_version: 18
|
||||
project: e2e-expo
|
||||
- node_version: 18
|
||||
project: e2e-gradle
|
||||
- node_version: 18
|
||||
project: e2e-jest
|
||||
- node_version: 18
|
||||
project: e2e-js
|
||||
- node_version: 18
|
||||
project: e2e-eslint
|
||||
- node_version: 18
|
||||
project: e2e-next
|
||||
- node_version: 18
|
||||
project: e2e-node
|
||||
- node_version: 18
|
||||
project: e2e-nuxt
|
||||
- node_version: 18
|
||||
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
|
||||
project: e2e-react-native
|
||||
- node_version: 18
|
||||
project: e2e-web
|
||||
- node_version: 18
|
||||
project: e2e-remix
|
||||
- node_version: 18
|
||||
project: e2e-rollup
|
||||
- node_version: 18
|
||||
project: e2e-storybook
|
||||
- node_version: 18
|
||||
project: e2e-vite
|
||||
- node_version: 18
|
||||
project: e2e-vue
|
||||
- node_version: 18
|
||||
project: e2e-webpack
|
||||
# run just npm v20 on macos
|
||||
node_version: '16'
|
||||
package_manager: yarn
|
||||
- os: macos-latest
|
||||
node_version: '14'
|
||||
- os: macos-latest
|
||||
package_manager: yarn
|
||||
- os: macos-latest
|
||||
package_manager: pnpm
|
||||
- os: macos-latest
|
||||
node_version: 18
|
||||
packages: e2e-workspace-core
|
||||
- os: macos-latest
|
||||
packages: e2e-workspace-integrations
|
||||
- os: macos-latest
|
||||
packages: e2e-workspace-create
|
||||
- os: windows-latest
|
||||
node_version: '14'
|
||||
- os: windows-latest
|
||||
package_manager: yarn
|
||||
- os: windows-latest
|
||||
package_manager: pnpm
|
||||
- os: windows-latest
|
||||
packages: e2e-react-native,e2e-detox
|
||||
fail-fast: false
|
||||
|
||||
name: ${{ matrix.os_name }}/${{ matrix.package_manager }}/${{ matrix.node_version }} ${{ join(matrix.project) }}
|
||||
name: ${{ matrix.os-name }}/n v${{ matrix.node_version }}/${{ matrix.package_manager }} - ${{ matrix.packages }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Prepare dir for output
|
||||
run: mkdir -p outputs
|
||||
- name: Install PNPM
|
||||
if: ${{ matrix.package_manager == 'pnpm' }}
|
||||
uses: pnpm/action-setup@v2.0.1
|
||||
with:
|
||||
version: 6.9.1
|
||||
|
||||
- name: Install PNPM
|
||||
run: |
|
||||
npm install -g @pnpm/exe@8
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v2.4.0
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
registry-url: http://localhost:4872
|
||||
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: 'pnpm'
|
||||
- name: Yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- name: Cache node_modules
|
||||
id: cache-modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-modules-${{ matrix.node_version }}-${{ github.run_id }}
|
||||
- name: Cache yarn
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ matrix.os }}-node-${{ matrix.node-version }}-yarn-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ matrix.os }}-node-${{ matrix.node-version }}-yarn-
|
||||
|
||||
- name: Install packages
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Install packages
|
||||
run: yarn install --prefer-offline --non-interactive
|
||||
|
||||
- name: Cleanup
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: |
|
||||
# Workaround to provide additional free space for testing.
|
||||
# https://github.com/actions/virtual-environments/issues/2840
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
sudo apt-get install lsof
|
||||
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
- name: Cleanup
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: |
|
||||
# Workaround to provide additional free space for testing.
|
||||
# https://github.com/actions/virtual-environments/issues/2840
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
sudo apt-get install lsof
|
||||
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
|
||||
- name: Homebrew cache directory path
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
id: homebrew-cache-dir-path
|
||||
run: echo "dir=$(brew --cache)" >> $GITHUB_OUTPUT
|
||||
- name: Homebrew cache directory path
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
id: homebrew-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(brew --cache)"
|
||||
|
||||
- name: Cache Homebrew
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.homebrew-cache-dir-path.outputs.dir }}
|
||||
key: brew-${{ matrix.node_version }}
|
||||
restore-keys: |
|
||||
brew-
|
||||
- name: Cache Homebrew
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.homebrew-cache-dir-path.outputs.dir }}
|
||||
key: brew-${{ matrix.node-version }}
|
||||
restore-keys: |
|
||||
brew-
|
||||
|
||||
- name: Cache Cypress
|
||||
id: cache-cypress
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '${{ github.workspace }}/.cypress'
|
||||
key: ${{ runner.os }}-cypress
|
||||
- name: Install applesimutils, reset ios simulators
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: Install Cypress
|
||||
if: steps.cache-cypress.outputs.cache-hit != 'true'
|
||||
run: npx cypress install
|
||||
- name: Run e2e tests
|
||||
run: yarn nx run-many --target=e2e --projects="${{ join(matrix.packages) }}"
|
||||
env:
|
||||
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-gha-${{ matrix.os }}-${{ matrix.node_version }}-${{ matrix.package_manager }}
|
||||
NX_E2E_RUN_CYPRESS: ${{ 'true' }}
|
||||
NODE_OPTIONS: --max_old_space_size=8192
|
||||
SELECTED_PM: ${{ matrix.package_manager }}
|
||||
npm_config_registry: http://localhost:4872
|
||||
YARN_REGISTRY: http://localhost:4872
|
||||
VERBOSE_OUTPUT: ${{ 'true' }}
|
||||
NX_CACHE_DIRECTORY: ${{ matrix.os-cache-dir }}
|
||||
|
||||
- name: Install applesimutils, reset ios simulators
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
run: |
|
||||
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
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled && failure() }}
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
with:
|
||||
sudo: ${{ matrix.os != 'windows-latest' }} # disable sudo for windows debugging
|
||||
|
||||
- name: Configure git metadata (needed for lerna smoke tests)
|
||||
run: |
|
||||
git config --global user.email test@test.com
|
||||
git config --global user.name "Test Test"
|
||||
|
||||
- name: Set starting timestamp
|
||||
id: before-e2e
|
||||
run: |
|
||||
echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run e2e tests
|
||||
id: e2e-run
|
||||
run: pnpm nx run-many -t e2e-local -p ${{ matrix.project }}
|
||||
timeout-minutes: ${{ matrix.os_timeout }}
|
||||
env:
|
||||
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-gha-${{ matrix.os }}-${{ matrix.node_version }}-${{ matrix.package_manager }}
|
||||
NODE_OPTIONS: --max_old_space_size=8192
|
||||
SELECTED_PM: ${{ matrix.package_manager }}
|
||||
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_VERBOSE_LOGGING: 'true'
|
||||
NX_PERF_LOGGING: 'false'
|
||||
NX_DAEMON: 'true'
|
||||
NX_SKIP_LOG_GROUPING: 'true'
|
||||
|
||||
- name: Save matrix config in file
|
||||
if: ${{ always() }}
|
||||
id: save-matrix
|
||||
shell: bash
|
||||
run: |
|
||||
before=${{ steps.before-e2e.outputs.timestamp }}
|
||||
now=$(date +%s)
|
||||
delta=$(($now - $before))
|
||||
matrix=$((
|
||||
echo '${{ toJSON(matrix) }}'
|
||||
) | jq --argjson delta $delta -c '. + { "status": "${{ steps.e2e-run.outcome}}", "duration": $delta }')
|
||||
echo "$matrix" > matrix
|
||||
path=outputs/${{ matrix.os_name}}-${{ matrix.node_version}}-${{ matrix.package_manager}}-${{ matrix.project }}
|
||||
echo "path=$path" >> $GITHUB_OUTPUT
|
||||
echo "$matrix" > $path
|
||||
|
||||
- name: Upload matrix config
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: outputs
|
||||
path: ${{ steps.save-matrix.outputs.path }}
|
||||
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled && failure() }}
|
||||
uses: mxschmitt/action-tmate@v3.8
|
||||
timeout-minutes: 15
|
||||
|
||||
process-result:
|
||||
if: ${{ always() && github.repository_owner == 'nrwl' }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: e2e
|
||||
outputs:
|
||||
message: ${{ steps.process-json.outputs.SLACK_MESSAGE }}
|
||||
proj-duration: ${{ steps.process-json.outputs.SLACK_PROJ_DURATION }}
|
||||
pm-duration: ${{ steps.process-json.outputs.SLACK_PM_DURATION }}
|
||||
codeowners: ${{ steps.process-json.outputs.CODEOWNERS }}
|
||||
steps:
|
||||
- name: Load outputs
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: outputs
|
||||
path: outputs
|
||||
|
||||
- name: Join and stringify matrix configs
|
||||
id: combine-json
|
||||
run: |
|
||||
combined=$((jq -s . outputs/*) | jq tostring)
|
||||
echo "combined=$combined" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Make slack outputs
|
||||
id: process-json
|
||||
uses: actions/github-script@v6
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
script: |
|
||||
const combined = JSON.parse(${{ steps.combine-json.outputs.combined }});
|
||||
const failedProjects = combined.filter(c => c.status === 'failure').sort((a, b) => a.project.localeCompare(b.project));
|
||||
|
||||
// codeowners
|
||||
const codeowners = new Set();
|
||||
failedProjects.forEach(c => {
|
||||
codeowners.add(c.codeowners);
|
||||
});
|
||||
core.setOutput('CODEOWNERS', Array.from(codeowners).join(','));
|
||||
|
||||
function trimSpace(res) {
|
||||
return res.split('\n').map((l) => l.trim()).join('\n');
|
||||
}
|
||||
|
||||
// failed message
|
||||
let lastProject;
|
||||
let result = `
|
||||
\`\`\`
|
||||
| Failed project | PM | OS | Node |
|
||||
|--------------------------------|------|-------|------|`;
|
||||
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} |`
|
||||
lastProject = matrix.project;
|
||||
});
|
||||
result += `\`\`\``;
|
||||
core.setOutput('SLACK_MESSAGE', trimSpace(result));
|
||||
|
||||
function humanizeDuration(num) {
|
||||
let res = '';
|
||||
const hours = Math.floor(num / 3600);
|
||||
if (hours) {
|
||||
res += `${hours}h `;
|
||||
}
|
||||
const mins = Math.floor((num % 3600) / 60);
|
||||
if (mins) {
|
||||
res += `${mins}m `;
|
||||
}
|
||||
const sec = num % 60;
|
||||
if (sec) {
|
||||
res += `${sec}s`
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// duration message
|
||||
const timeReport = {};
|
||||
const pmReport = {
|
||||
npm: 0,
|
||||
yarn: 0,
|
||||
pnpm: 0
|
||||
};
|
||||
const macosProjects = ['e2e-detox', 'e2e-expo', 'e2e-react-native'];
|
||||
combined.forEach((matrix) => {
|
||||
if (matrix.os_name === 'Linux' && matrix.node_version === 18) {
|
||||
pmReport[matrix.package_manager] += matrix.duration;
|
||||
}
|
||||
if (matrix.os_name === 'Linux' || macosProjects.includes(matrix.project)) {
|
||||
if (timeReport[matrix.project]) {
|
||||
if (matrix.duration > timeReport[matrix.project].max) {
|
||||
timeReport[matrix.project].max = matrix.duration;
|
||||
timeReport[
|
||||
matrix.project
|
||||
].maxEnv = `${matrix.os_name}, ${matrix.package_manager}`;
|
||||
}
|
||||
if (matrix.duration < timeReport[matrix.project].min) {
|
||||
timeReport[matrix.project].min = matrix.duration;
|
||||
timeReport[
|
||||
matrix.project
|
||||
].minEnv = `${matrix.os_name}, ${matrix.package_manager}`;
|
||||
}
|
||||
} else {
|
||||
timeReport[matrix.project] = {
|
||||
min: matrix.duration,
|
||||
max: matrix.duration,
|
||||
minEnv: `${matrix.os_name}, ${matrix.package_manager}`,
|
||||
maxEnv: `${matrix.os_name}, ${matrix.package_manager}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// project time report
|
||||
let resultPkg = `
|
||||
\`\`\`
|
||||
| Project | Time |
|
||||
|--------------------------------|---------------------------|`;
|
||||
function mapProjectTime(proj, section) {
|
||||
let res = '';
|
||||
res += `${humanizeDuration(timeReport[proj][section])}`;
|
||||
res += ` (${timeReport[proj][section + 'Env']})`
|
||||
return res;
|
||||
}
|
||||
function durationIcon(proj, section) {
|
||||
if (timeReport[proj][section] < 12 * 60) {
|
||||
return `${section} ✅`;
|
||||
}
|
||||
if (timeReport[proj][section] < 15 * 60) {
|
||||
return `${section} ❗`;
|
||||
}
|
||||
return `${section} ❌`;
|
||||
}
|
||||
Object.keys(timeReport).forEach(proj => {
|
||||
resultPkg += `\n| ${proj.padEnd(30)} | |`;
|
||||
resultPkg += `\n| ${durationIcon(proj, 'min').padStart(29)} | ${mapProjectTime(proj, 'min').padEnd(25)} |`;
|
||||
resultPkg += `\n| ${durationIcon(proj, 'max').padStart(29)} | ${mapProjectTime(proj, 'max').padEnd(25)} |`;
|
||||
});
|
||||
resultPkg += `\`\`\``;
|
||||
core.setOutput('SLACK_PROJ_DURATION', trimSpace(resultPkg));
|
||||
|
||||
// Print project duration report inline to allow reviewing on manual runs (when no slack message will be sent)
|
||||
console.log(trimSpace(resultPkg));
|
||||
|
||||
let resultPm = `
|
||||
\`\`\`
|
||||
| PM | Total time |
|
||||
|------|-------------|`;
|
||||
Object.keys(pmReport).forEach(pm => {
|
||||
resultPm += `\n| ${pm.padEnd(4)} | ${humanizeDuration(pmReport[pm]).padEnd(11)} |`
|
||||
});
|
||||
resultPm += `\`\`\``;
|
||||
core.setOutput('SLACK_PM_DURATION', trimSpace(resultPm));
|
||||
|
||||
// Print package manager duration report inline to allow reviewing on manual runs (when no slack message will be sent)
|
||||
console.log(trimSpace(resultPm));
|
||||
|
||||
report-failure:
|
||||
if: ${{ failure() && github.repository_owner == 'nrwl' && github.event_name != 'workflow_dispatch' }}
|
||||
needs: process-result
|
||||
runs-on: ubuntu-latest
|
||||
name: Report failure
|
||||
steps:
|
||||
- name: Send notification
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: 'failure'
|
||||
message_format: '{emoji} Workflow has {status_message} ${{ needs.process-result.outputs.message }}'
|
||||
notification_title: '{workflow}'
|
||||
footer: '<{run_url}|View Run> / Last commit <{commit_url}|{commit_sha}>'
|
||||
mention_groups: ${{ needs.process-result.outputs.codeowners }}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
|
||||
report-success:
|
||||
if: ${{ success() && github.repository_owner == 'nrwl' && github.event_name != 'workflow_dispatch' }}
|
||||
report:
|
||||
if: ${{ always() && github.repository_owner == 'nrwl' && github.event_name != 'workflow_dispatch' }}
|
||||
needs: e2e
|
||||
runs-on: ubuntu-latest
|
||||
name: Report status
|
||||
steps:
|
||||
- name: Send notification
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
uses: ravsamhq/notify-slack-action@v1
|
||||
with:
|
||||
status: ${{ needs.e2e.result }}
|
||||
message_format: '{emoji} Workflow has {status_message}'
|
||||
notification_title: '{workflow}'
|
||||
footer: '<{run_url}|View Run> / Last commit <{commit_url}|{commit_sha}>'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
|
||||
report-pm-time:
|
||||
if: ${{ always() && github.repository_owner == 'nrwl' && github.event_name != 'workflow_dispatch' }}
|
||||
needs: process-result
|
||||
runs-on: ubuntu-latest
|
||||
name: Report duration per package manager
|
||||
steps:
|
||||
- name: Send notification
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: 'skipped'
|
||||
message_format: '${{ needs.process-result.outputs.pm-duration }}'
|
||||
notification_title: 'Total duration per package manager (ubuntu only)'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
|
||||
report-proj-time:
|
||||
if: ${{ always() && github.repository_owner == 'nrwl' && github.event_name != 'workflow_dispatch' }}
|
||||
needs: process-result
|
||||
runs-on: ubuntu-latest
|
||||
name: Report duration per package manager
|
||||
steps:
|
||||
- name: Send notification
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: 'skipped'
|
||||
message_format: '${{ needs.process-result.outputs.proj-duration }}'
|
||||
notification_title: 'E2E Project duration stats'
|
||||
message_format: '{emoji} *{workflow}* {status_message} (last commit <{commit_url}|{commit_sha}>)'
|
||||
notification_title: '{workflow} has {status_message}'
|
||||
footer: '<{run_url}|View Run>'
|
||||
mention_users: 'U01UELKLYF2,U9NPA6C90'
|
||||
mention_users_when: 'failure,warnings'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
|
||||
@@ -1,388 +0,0 @@
|
||||
name: E2E matrix (Windows)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 5 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
debug_enabled:
|
||||
type: boolean
|
||||
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
|
||||
required: false
|
||||
default: false
|
||||
|
||||
env:
|
||||
CYPRESS_CACHE_FOLDER: ${{ github.workspace }}/.cypress
|
||||
|
||||
permissions: { }
|
||||
jobs:
|
||||
preinstall:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node_version:
|
||||
- 20
|
||||
- 18
|
||||
|
||||
name: Cache install (node v${{ matrix.node_version }})
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Set node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache node_modules
|
||||
id: cache-modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
lookup-only: true
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-modules-${{ matrix.node_version }}-${{ github.run_id }}
|
||||
|
||||
- name: Install packages
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Cache Cypress
|
||||
id: cache-cypress
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
lookup-only: true
|
||||
path: '${{ github.workspace }}/.cypress'
|
||||
key: windows-cypress
|
||||
|
||||
- name: Install Cypress
|
||||
if: steps.cache-cypress.outputs.cache-hit != 'true'
|
||||
run: pnpm cypress install
|
||||
|
||||
e2e:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
needs: preinstall
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node_version:
|
||||
- 20
|
||||
- 18
|
||||
package_manager:
|
||||
- npm
|
||||
project:
|
||||
- e2e-angular
|
||||
- e2e-cypress
|
||||
- e2e-esbuild
|
||||
- e2e-eslint
|
||||
- e2e-jest
|
||||
- e2e-js
|
||||
- e2e-lerna-smoke-tests
|
||||
- e2e-next
|
||||
- e2e-node
|
||||
- e2e-nuxt
|
||||
- e2e-nx-init
|
||||
- e2e-nx
|
||||
- e2e-playwright
|
||||
- e2e-plugin
|
||||
- e2e-react
|
||||
- e2e-release
|
||||
- e2e-remix
|
||||
- e2e-rollup
|
||||
- e2e-storybook
|
||||
- e2e-vite
|
||||
- e2e-vue
|
||||
- e2e-web
|
||||
- e2e-webpack
|
||||
- e2e-workspace-create
|
||||
include:
|
||||
# codeowner groups
|
||||
- project: e2e-angular
|
||||
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
|
||||
codeowners: 'S04SYJGKSCT'
|
||||
- project: e2e-next
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-node
|
||||
codeowners: 'S04SJ6HHP0X'
|
||||
- project: e2e-nx-init
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-nx
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-plugin
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-release
|
||||
codeowners: 'S04SYHYKGNP'
|
||||
- project: e2e-react
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-react-native
|
||||
codeowners: 'S04TNCNJG5N'
|
||||
- project: e2e-web
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-rollup
|
||||
codeowners: 'S04SJ6PL98X'
|
||||
- project: e2e-storybook
|
||||
codeowners: 'S04SVQ8H0G5'
|
||||
- project: e2e-playwright
|
||||
codeowners: 'S04SVQ8H0G5'
|
||||
- project: e2e-remix
|
||||
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'
|
||||
exclude:
|
||||
# exclude non-CNW/Lerna tests from non-LTS node versions
|
||||
- node_version: 18
|
||||
project: e2e-angular
|
||||
- node_version: 18
|
||||
project: e2e-cypress
|
||||
- node_version: 18
|
||||
project: e2e-esbuild
|
||||
- node_version: 18
|
||||
project: e2e-jest
|
||||
- node_version: 18
|
||||
project: e2e-js
|
||||
- node_version: 18
|
||||
project: e2e-eslint
|
||||
- node_version: 18
|
||||
project: e2e-next
|
||||
- node_version: 18
|
||||
project: e2e-node
|
||||
- node_version: 18
|
||||
project: e2e-nuxt
|
||||
- node_version: 18
|
||||
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
|
||||
project: e2e-web
|
||||
- node_version: 18
|
||||
project: e2e-remix
|
||||
- node_version: 18
|
||||
project: e2e-rollup
|
||||
- node_version: 18
|
||||
project: e2e-storybook
|
||||
- node_version: 18
|
||||
project: e2e-vite
|
||||
- node_version: 18
|
||||
project: e2e-vue
|
||||
- node_version: 18
|
||||
project: e2e-webpack
|
||||
fail-fast: false
|
||||
|
||||
name: ${{ matrix.project }} (v${{ matrix.node_version }})
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare dir for output
|
||||
run: mkdir -p outputs
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8.7.4
|
||||
run_install: false
|
||||
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache node_modules
|
||||
id: cache-modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-modules-${{ matrix.node_version }}-${{ github.run_id }}
|
||||
|
||||
- name: Install packages
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Cache Cypress
|
||||
id: cache-cypress
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '${{ github.workspace }}/.cypress'
|
||||
key: ${{ runner.os }}-cypress
|
||||
|
||||
- name: Install Cypress
|
||||
if: steps.cache-cypress.outputs.cache-hit != 'true'
|
||||
run: npx cypress install
|
||||
|
||||
- name: Configure git metadata (needed for lerna smoke tests)
|
||||
run: |
|
||||
git config --global user.email test@test.com
|
||||
git config --global user.name "Test Test"
|
||||
|
||||
- name: Run e2e tests
|
||||
id: e2e-run
|
||||
run: pnpm nx run ${{ matrix.project }}:e2e-local
|
||||
shell: bash
|
||||
timeout-minutes: 180
|
||||
env:
|
||||
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-gha-windows-${{ matrix.node_version }}-${{ matrix.package_manager }}
|
||||
NODE_OPTIONS: --max_old_space_size=8192
|
||||
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_VERBOSE_LOGGING: 'true'
|
||||
NX_PERF_LOGGING: 'false'
|
||||
NX_DAEMON: 'true'
|
||||
NX_SKIP_LOG_GROUPING: 'true'
|
||||
|
||||
- name: Save matrix config in file
|
||||
if: ${{ always() }}
|
||||
id: save-matrix
|
||||
shell: bash
|
||||
run: |
|
||||
matrix=$((
|
||||
echo '${{ toJSON(matrix) }}'
|
||||
) | jq -c '. + { "status": "${{ steps.e2e-run.outcome}}" }')
|
||||
echo "$matrix" > matrix
|
||||
path=outputs/windows-${{ matrix.node_version}}-${{ matrix.package_manager}}-${{ matrix.project }}
|
||||
echo "path=$path" >> $GITHUB_OUTPUT
|
||||
echo "$matrix" > $path
|
||||
|
||||
- name: Upload matrix config
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: outputs
|
||||
path: ${{ steps.save-matrix.outputs.path }}
|
||||
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled && failure() }}
|
||||
uses: mxschmitt/action-tmate@v3.8
|
||||
timeout-minutes: 15
|
||||
with:
|
||||
sudo: false # disable sudo for windows debugging
|
||||
|
||||
process-result:
|
||||
if: ${{ always() && github.repository_owner == 'nrwl' }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: e2e
|
||||
outputs:
|
||||
message: ${{ steps.process-json.outputs.SLACK_MESSAGE }}
|
||||
codeowners: ${{ steps.process-json.outputs.CODEOWNERS }}
|
||||
steps:
|
||||
- name: Load outputs
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: outputs
|
||||
path: outputs
|
||||
|
||||
- name: Join and stringify matrix configs
|
||||
id: combine-json
|
||||
shell: bash
|
||||
run: |
|
||||
combined=$((jq -s . outputs/*) | jq tostring)
|
||||
echo "combined=$combined" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Make slack outputs
|
||||
id: process-json
|
||||
uses: actions/github-script@v6
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
with:
|
||||
script: |
|
||||
const combined = JSON.parse(${{ steps.combine-json.outputs.combined }});
|
||||
const failedProjects = combined.filter(c => c.status === 'failure').sort((a, b) => a.project.localeCompare(b.project));
|
||||
|
||||
// codeowners
|
||||
const codeowners = new Set();
|
||||
failedProjects.forEach(c => {
|
||||
codeowners.add(c.codeowners);
|
||||
});
|
||||
core.setOutput('CODEOWNERS', Array.from(codeowners).join(','));
|
||||
|
||||
// message
|
||||
let result = `
|
||||
*OS* Windows
|
||||
*Package manager* npm
|
||||
\`\`\`
|
||||
| Failed project | Node |
|
||||
|--------------------------------|------|`;
|
||||
failedProjects.forEach(matrix => {
|
||||
result += `\n| ${matrix.project.padEnd(30)} | v${matrix.node_version} |`
|
||||
});
|
||||
result += `\`\`\``;
|
||||
const message = result.split('\n').map(l => l.trim()).join('\n');
|
||||
core.setOutput('SLACK_MESSAGE', message);
|
||||
|
||||
report-failure:
|
||||
if: ${{ failure() && github.repository_owner == 'nrwl' && github.event_name != 'workflow_dispatch' }}
|
||||
needs: process-result
|
||||
runs-on: ubuntu-latest
|
||||
name: Report failure
|
||||
steps:
|
||||
- name: Send notification
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: 'failure'
|
||||
message_format: '{emoji} Workflow has {status_message} ${{ needs.process-result.outputs.message }}'
|
||||
notification_title: '{workflow}'
|
||||
footer: '<{run_url}|View Run> / Last commit <{commit_url}|{commit_sha}>'
|
||||
mention_groups: ${{ needs.process-result.outputs.codeowners }}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
|
||||
report-success:
|
||||
if: ${{ success() && github.repository_owner == 'nrwl' && github.event_name != 'workflow_dispatch' }}
|
||||
needs: e2e
|
||||
runs-on: ubuntu-latest
|
||||
name: Report success
|
||||
steps:
|
||||
- name: Send notification
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: ${{ needs.e2e.result }}
|
||||
message_format: '{emoji} Workflow has {status_message}'
|
||||
notification_title: '{workflow}'
|
||||
footer: '<{run_url}|View Run> / Last commit <{commit_url}|{commit_sha}>'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
@@ -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 }}
|
||||
@@ -1,69 +0,0 @@
|
||||
name: Issue Statistics
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
issues-report:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
runs-on: ubuntu-latest
|
||||
name: Report status
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache node_modules
|
||||
id: cache-modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
lookup-only: true
|
||||
path: '**/node_modules'
|
||||
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install packages
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Download artifact
|
||||
id: download-artifact
|
||||
uses: dawidd6/action-download-artifact@v2 # Needed since we are downloading artifact from a different workflow run, official actions/download-artifact doesn't support this.
|
||||
with:
|
||||
name: cached-issue-data
|
||||
path: ${{ github.workspace }}/scripts/issues-scraper/cached
|
||||
search_artifacts: true
|
||||
continue-on-error: true
|
||||
|
||||
- name: Collect Issue Data
|
||||
id: collect
|
||||
run: npx ts-node ./scripts/issues-scraper/index.ts
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: cached-issue-data
|
||||
path: ./scripts/issues-scraper/cached/data.json
|
||||
|
||||
- name: Send GitHub Action trigger data to Slack workflow
|
||||
id: slack
|
||||
uses: slackapi/slack-github-action@v1.23.0
|
||||
with:
|
||||
payload: ${{ steps.collect.outputs.SLACK_MESSAGE }}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_ISSUES_REPORT_URL }}
|
||||
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
|
||||
@@ -1,30 +0,0 @@
|
||||
name: "Lock Threads"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Once a day, at midnight UTC
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: lock
|
||||
|
||||
jobs:
|
||||
action:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v4
|
||||
id: lockthreads
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: "30" # Lock issues after 30 days of being closed
|
||||
pr-inactive-days: "5" # Lock closed PRs after 5 days. This ensures that issues that stem from a PR are opened as issues, rather than comments on the recently merged PR.
|
||||
add-issue-labels: "outdated"
|
||||
issue-comment: >
|
||||
This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.
|
||||
pr-comment: >
|
||||
This pull request has already been merged/closed. If you experience issues related to these changes, please open a new issue referencing this pull request.
|
||||
@@ -5,42 +5,25 @@ on:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
audit:
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install PNPM
|
||||
run: |
|
||||
npm install -g @pnpm/exe@8
|
||||
- name: Run a security audit
|
||||
run: npx audit-ci --critical --report-type summary
|
||||
|
||||
- name: Run a security audit
|
||||
run: pnpm dlx audit-ci --critical --report-type summary
|
||||
|
||||
# - name: Run Dependency confusion supply chain check
|
||||
# run: npx snync -d .
|
||||
|
||||
report:
|
||||
if: ${{ always() && github.repository_owner == 'nrwl' && github.event_name != 'workflow_dispatch' }}
|
||||
needs: audit
|
||||
runs-on: ubuntu-latest
|
||||
name: Report status
|
||||
steps:
|
||||
- name: Send notification
|
||||
uses: ravsamhq/notify-slack-action@v2
|
||||
with:
|
||||
status: ${{ needs.audit.result }}
|
||||
message_format: '{emoji} Audit has {status_message}'
|
||||
notification_title: '{workflow}'
|
||||
footer: '<{run_url}|View Run> / Last commit <{commit_url}|{commit_sha}>'
|
||||
mention_users: 'U01UELKLYF2,U9NPA6C90'
|
||||
mention_users_when: 'failure,warnings'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
- name: Report Status
|
||||
if: ${{ always() && github.repository_owner == 'nrwl' }}
|
||||
uses: ravsamhq/notify-slack-action@v1
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
message_format: '{emoji} *{workflow}* {status_message} on <{commit_url}|{commit_sha}>'
|
||||
notification_title: '{workflow} has {status_message}'
|
||||
footer: 'From <{repo_url}|{repo}>'
|
||||
mention_users: 'U01UELKLYF2'
|
||||
mention_users_when: 'failure,warnings'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
|
||||
@@ -1,459 +0,0 @@
|
||||
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)
|
||||
|
||||
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' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
settings:
|
||||
- host: macos-13
|
||||
target: x86_64-apple-darwin
|
||||
build: |
|
||||
pnpm nx run-many --target=build-native -- --target=x86_64-apple-darwin
|
||||
- host: windows-latest
|
||||
build: pnpm nx run-many --target=build-native -- --target=x86_64-pc-windows-msvc
|
||||
target: x86_64-pc-windows-msvc
|
||||
# Windows 32bit (not needed)
|
||||
# - host: windows-latest
|
||||
# build: |
|
||||
# yarn nx -- run-many --target=build-native -- --target=i686-pc-windows-msvc
|
||||
# target: i686-pc-windows-msvc
|
||||
- host: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||
build: |-
|
||||
set -e &&
|
||||
npm i -g pnpm@8.15.7 --force &&
|
||||
pnpm --version &&
|
||||
pnpm install --frozen-lockfile &&
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=x86_64-unknown-linux-gnu
|
||||
- host: ubuntu-latest
|
||||
target: x86_64-unknown-linux-musl
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
build: |-
|
||||
set -e &&
|
||||
npm i -g pnpm@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
|
||||
target: aarch64-apple-darwin
|
||||
build: |
|
||||
sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*;
|
||||
export CC=$(xcrun -f clang);
|
||||
export CXX=$(xcrun -f clang++);
|
||||
SYSROOT=$(xcrun --sdk macosx --show-sdk-path);
|
||||
export CFLAGS="-isysroot $SYSROOT -isystem $SYSROOT";
|
||||
pnpm nx run-many --target=build-native -- --target=aarch64-apple-darwin
|
||||
- host: ubuntu-latest
|
||||
target: aarch64-unknown-linux-gnu
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
|
||||
build: |-
|
||||
set -e &&
|
||||
npm i -g pnpm@8.15.7 --force &&
|
||||
pnpm --version &&
|
||||
pnpm install --frozen-lockfile &&
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=aarch64-unknown-linux-gnu
|
||||
- host: ubuntu-latest
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
setup: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-arm-linux-gnueabihf -y
|
||||
build: |
|
||||
pnpm nx run-many --target=build-native -- --target=armv7-unknown-linux-gnueabihf
|
||||
# Android (not needed)
|
||||
# - host: ubuntu-latest
|
||||
# target: aarch64-linux-android
|
||||
# build: |
|
||||
# pnpm nx run-many --target=build-native -- --target=aarch64-linux-android
|
||||
# - host: ubuntu-latest
|
||||
# target: armv7-linux-androideabi
|
||||
# build: |
|
||||
# pnpm nx run-many --target=build-native -- --target=armv7-linux-androideabi
|
||||
- host: ubuntu-latest
|
||||
target: aarch64-unknown-linux-musl
|
||||
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
build: |-
|
||||
set -e &&
|
||||
rustup target add aarch64-unknown-linux-musl &&
|
||||
npm i -g pnpm@8.15.7 --force &&
|
||||
pnpm --version &&
|
||||
pnpm install --frozen-lockfile &&
|
||||
pnpm nx run-many --verbose --target=build-native -- --target=aarch64-unknown-linux-musl
|
||||
- host: windows-latest
|
||||
target: aarch64-pc-windows-msvc
|
||||
build: pnpm nx run-many --target=build-native -- --target=aarch64-pc-windows-msvc
|
||||
name: stable - ${{ matrix.settings.target }} - node@18
|
||||
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: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
if: ${{ !matrix.settings.docker }}
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
check-latest: true
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
if: ${{ !matrix.settings.docker }}
|
||||
with:
|
||||
targets: ${{ matrix.settings.target }}
|
||||
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
.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
|
||||
if: matrix.settings.target == 'i686-pc-windows-msvc'
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
check-latest: true
|
||||
cache: pnpm
|
||||
architecture: x86
|
||||
|
||||
- name: Build in docker
|
||||
uses: addnab/docker-run-action@v3
|
||||
if: ${{ matrix.settings.docker }}
|
||||
with:
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
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 }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
check-latest: true
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
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
|
||||
});
|
||||
@@ -1,111 +1,169 @@
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
- cron: "* 0 * * *"
|
||||
name: Stale Bot workflow
|
||||
|
||||
permissions: { }
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: ${{ github.repository_owner == 'nrwl' }}
|
||||
permissions:
|
||||
issues: write # to close stale issues (actions/stale)
|
||||
pull-requests: write # to close stale PRs (actions/stale)
|
||||
|
||||
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! 🙏
|
||||
|
||||
+2
-46
@@ -1,6 +1,5 @@
|
||||
node_modules
|
||||
/.idea
|
||||
/.fleet
|
||||
.idea
|
||||
/.vscode
|
||||
dist
|
||||
/build
|
||||
@@ -11,48 +10,5 @@ tmp
|
||||
*.log
|
||||
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
|
||||
CHANGELOG.md
|
||||
|
||||
# Next.js
|
||||
.next
|
||||
out
|
||||
|
||||
# Angular Cache
|
||||
.angular
|
||||
|
||||
# Local dev files
|
||||
.env.local
|
||||
.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/
|
||||
dep-graph/dep-graph/src/assets/environment.js
|
||||
@@ -0,0 +1 @@
|
||||
_
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
changedFiles="$(git diff-tree -r --name-only --no-commit-id $1 $2)"
|
||||
node ./scripts/notify-lockfile-changes.js $changedFiles
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
changedFiles="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"
|
||||
node ./scripts/notify-lockfile-changes.js $changedFiles
|
||||
+6
-5
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
pnpm check-lock-files &&
|
||||
pnpm check-commit &&
|
||||
pnpm documentation &&
|
||||
pnpm pretty-quick --check
|
||||
yarn check-lock-files
|
||||
yarn check-commit
|
||||
yarn documentation
|
||||
yarn pretty-quick --check
|
||||
|
||||
@@ -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
|
||||
+24
-44
@@ -1,44 +1,24 @@
|
||||
tmp
|
||||
/test
|
||||
/build
|
||||
node_modules
|
||||
/package.json
|
||||
/pnpm-lock.yaml
|
||||
packages/workspace/src/generators/**/files/**/*.json
|
||||
packages/angular/src/schematics/**/files/**/*.json
|
||||
packages/angular/src/migrations/**/files/**/*.json
|
||||
packages/web/src/generators/**/files/**/*.json
|
||||
packages/node/src/schematics/**/files/**/*.json
|
||||
packages/express/src/schematics/**/files/**/*.json
|
||||
packages/nest/src/schematics/**/files/**/*.json
|
||||
packages/react/src/schematics/**/files/**/*.json
|
||||
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
|
||||
nx-dev/nx-dev/.next/
|
||||
nx-dev/nx-dev/public/documentation
|
||||
graph/client/src/assets/environment.js
|
||||
graph/client/src/assets/dev/environment.js
|
||||
graph/client/src/assets/generated-graphs
|
||||
graph/client/src/assets/generated-project-graphs
|
||||
graph/client/src/assets/generated-task-graphs
|
||||
/.vscode
|
||||
/.idea
|
||||
/.fleet
|
||||
/.github
|
||||
/coverage
|
||||
/.yarn
|
||||
/.verdaccio/build/local-registry
|
||||
/dist
|
||||
/.env
|
||||
CODEOWNERS
|
||||
|
||||
/.nx/cache
|
||||
|
||||
.pnpm-store
|
||||
|
||||
/.nx/workspace-data
|
||||
tmp
|
||||
/test
|
||||
/build
|
||||
node_modules
|
||||
/package.json
|
||||
packages/workspace/src/generators/**/files/**/*.json
|
||||
packages/workspace/src/core/dep-graph/vendor.js
|
||||
packages/angular/src/schematics/**/files/**/*.json
|
||||
packages/angular/src/migrations/**/files/**/*.json
|
||||
packages/web/src/generators/**/files/**/*.json
|
||||
packages/node/src/schematics/**/files/**/*.json
|
||||
packages/express/src/schematics/**/files/**/*.json
|
||||
packages/nest/src/schematics/**/files/**/*.json
|
||||
packages/react/src/schematics/**/files/**/*.json
|
||||
packages/jest/src/schematics/**/files/**/*.json
|
||||
packages/**/schematics/**/files/**/*.html
|
||||
packages/**/generators/**/files/**/*.html
|
||||
/.vscode
|
||||
/.idea
|
||||
/.github
|
||||
/coverage
|
||||
/.yarn
|
||||
/.verdaccio/build/local-registry
|
||||
/dist
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"endOfLine": "lf",
|
||||
"plugins": ["prettier-plugin-tailwindcss"]
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
|
||||
+3
-10
@@ -9,14 +9,10 @@ auth:
|
||||
uplinks:
|
||||
npmjs:
|
||||
url: https://registry.npmjs.org/
|
||||
maxage: 60m
|
||||
max_fails: 20
|
||||
fail_timeout: 2m
|
||||
cache: true
|
||||
yarn:
|
||||
url: https://registry.yarnpkg.com
|
||||
maxage: 60m
|
||||
max_fails: 20
|
||||
fail_timeout: 2m
|
||||
cache: true
|
||||
|
||||
packages:
|
||||
'@*/*':
|
||||
@@ -42,7 +38,4 @@ packages:
|
||||
logs:
|
||||
type: stdout
|
||||
format: pretty
|
||||
level: warn
|
||||
|
||||
publish:
|
||||
allow_offline: true # set offline to true to allow publish offline
|
||||
level: http
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
test:$6FrCaT/v0dwE:autocreated 2020-03-25T19:10:50.254Z
|
||||
dddd:ZpRSQxtFPtZog:autocreated 2021-08-21T07:57:21.687Z
|
||||
|
||||
+147392
File diff suppressed because one or more lines are too long
-194
@@ -1,194 +0,0 @@
|
||||
# Any file not covered by a rule below, will default to Jason + Victor and a few select others.
|
||||
* @FrozenPandaz @vsavkin
|
||||
/packages/**/* @FrozenPandaz @vsavkin @AgentEnder @jaysoo @JamesHenry
|
||||
/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
|
||||
|
||||
# Docs Site + Graph
|
||||
/docs @nrwl/nx-docs-reviewers
|
||||
/docs/nx-cloud @StalkAltan @rarmatei @nrwl/nx-docs-reviewers
|
||||
/graph/** @philipjfulcher @FrozenPandaz @bcabanes @MaxKless @xiongemi
|
||||
/images @nrwl/nx-docs-reviewers
|
||||
/nx-dev/** @nrwl/nx-docs-reviewers
|
||||
/typedoc-theme @nrwl/nx-docs-reviewers
|
||||
|
||||
# Plugin Verticals
|
||||
|
||||
## Angular
|
||||
/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
|
||||
/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
|
||||
|
||||
## React
|
||||
/docs/generated/packages/react/** @nrwl/nx-react-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/next/** @nrwl/nx-react-reviewers @nrwl/nx-docs-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
|
||||
/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
|
||||
/packages/react/src/generators/cypress-component-configuration/** @nrwl/nx-react-reviewers @nrwl/nx-testing-tools-reviewers
|
||||
/packages/react/src/generators/component-test/** @nrwl/nx-react-reviewers @nrwl/nx-testing-tools-reviewers
|
||||
|
||||
# React Native
|
||||
/docs/generated/packages/detox/** @nrwl/nx-react-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/expo/** @nrwl/nx-react-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/react-native/** @nrwl/nx-react-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/react-native/** @nrwl/nx-react-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/detox/** @nrwl/nx-react-reviewers
|
||||
/e2e/detox/** @nrwl/nx-react-reviewers
|
||||
/packages/expo/** @nrwl/nx-react-reviewers
|
||||
/e2e/expo/** @nrwl/nx-react-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
|
||||
/docs/generated/packages/express/** @nrwl/nx-node-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/node/** @nrwl/nx-node-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/express/** @nrwl/nx-node-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/nest/** @nrwl/nx-node-reviewers @FrozenPandaz @nrwl/nx-docs-reviewers
|
||||
/packages/node/** @nrwl/nx-node-reviewers
|
||||
/packages/express/** @nrwl/nx-node-reviewers
|
||||
/packages/nest/** @nrwl/nx-node-reviewers
|
||||
/e2e/node/** @nrwl/nx-node-reviewers
|
||||
|
||||
## JS
|
||||
/docs/generated/packages/js/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/web/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/webpack/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/esbuild/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/rollup/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/vite/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/js/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/web/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/webpack/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/esbuild/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/vite/** @nrwl/nx-js-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/js/** @nrwl/nx-js-reviewers
|
||||
/e2e/js/** @nrwl/nx-js-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
|
||||
/packages/rollup/** @nrwl/nx-js-reviewers
|
||||
/e2e/rollup/** @nrwl/nx-js-reviewers
|
||||
/packages/vite/** @nrwl/nx-js-reviewers
|
||||
/e2e/vite/** @nrwl/nx-js-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
|
||||
/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
|
||||
/packages/eslint-plugin/** @nrwl/nx-linter-reviewers
|
||||
/packages/eslint/** @nrwl/nx-linter-reviewers
|
||||
/e2e/eslint/** @nrwl/nx-linter-reviewers
|
||||
.eslint* @nrwl/nx-linter-reviewers
|
||||
|
||||
# Storybook
|
||||
/docs/generated/packages/storybook/** @nrwl/nx-storybook-reviewers @nrwl/nx-docs-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
|
||||
|
||||
## 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/public-api.ts @FrozenPandaz @vsavkin
|
||||
|
||||
# Gradle
|
||||
/packages/gradle/** @FrozenPandaz @xiongemi
|
||||
/e2e/gradle/** @FrozenPandaz @xiongemi
|
||||
|
||||
# Nx-Plugin
|
||||
/docs/generated/packages/plugin/** @nrwl/nx-devkit-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/shared/packages/plugin/** @nrwl/nx-devkit-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/plugin/** @nrwl/nx-devkit-reviewers
|
||||
/e2e/plugin/** @nrwl/nx-devkit-reviewers
|
||||
|
||||
## Core
|
||||
/docs/generated/cli/** @nrwl/nx-core-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/nx/** @nrwl/nx-core-reviewers @nrwl/nx-docs-reviewers
|
||||
/docs/generated/packages/workspace/** @nrwl/nx-core-reviewers @nrwl/nx-docs-reviewers
|
||||
/packages/nx/** @nrwl/nx-core-reviewers
|
||||
/packages/nx/src/adapter @nrwl/nx-core-reviewers @leosvelperez
|
||||
/packages/nx/src/native @nrwl/nx-core-reviewers @nrwl/nx-native-reviewers
|
||||
/packages/nx/src/plugins/js/lock-file @nrwl/nx-core-reviewers @meeroslav
|
||||
/packages/nx/src/command-line/init/implementation/angular/** @nrwl/nx-angular-reviewers @nrwl/nx-core-reviewers
|
||||
/e2e/nx-init/src/nx-init-angular.test.ts @nrwl/nx-angular-reviewers
|
||||
/packages/nx/src/command-line/init/implementation/react/** @nrwl/nx-react-reviewers
|
||||
/e2e/nx-init/src/nx-init-react.test.ts @nrwl/nx-react-reviewers
|
||||
/e2e/nx-init/src/files/cra/** @nrwl/nx-react-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
|
||||
|
||||
# Misc
|
||||
/e2e/lerna-smoke-tests/** @vsavkin @JamesHenry
|
||||
/e2e/utils/** @meeroslav @nrwl/nx-testing-tools-reviewers @vsavkin @mandarini
|
||||
/community @nrwl/nx-docs-reviewers
|
||||
/CONTRIBUTING.md @FrozenPandaz @isaacplmann
|
||||
/CODE_OF_CONDUCT.md @FrozenPandaz @isaacplmann
|
||||
/CODEOWNERS @FrozenPandaz @AgentEnder
|
||||
|
||||
# Scripts
|
||||
/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
|
||||
|
||||
# Global Files
|
||||
project.json @FrozenPandaz @vsavkin
|
||||
jest.config.ts @nrwl/nx-testing-tools-reviewers @FrozenPandaz
|
||||
jest.preset.js @nrwl/nx-testing-tools-reviewers @FrozenPandaz
|
||||
|
||||
# Overrides - These are applied last, so override any matches above.
|
||||
docs/generated/manifests/* @nrwl/nrwlians
|
||||
docs/generated/packages-metadata.json @FrozenPandaz @jaysoo @AgentEnder @nrwl/nx-docs-reviewers
|
||||
+1
-1
@@ -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.
|
||||
|
||||
|
||||
+77
-179
@@ -6,64 +6,40 @@ We would love for you to contribute to Nx! Read this document to see how to do i
|
||||
|
||||
Watch this 5-minute video:
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=8LCA_4qxc08" target="_blank" rel="noreferrer">
|
||||
<a href="https://www.youtube.com/watch?v=8LCA_4qxc08" target="_blank">
|
||||
<p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/how-to-contribute.png" width="600" alt="Nx - How to contribute"></p>
|
||||
</a>
|
||||
|
||||
## Got a Question?
|
||||
|
||||
We are trying to keep GitHub issues for bug reports and feature requests. Using the `nrwl` tag
|
||||
on [Stack Overflow](https://stackoverflow.com/questions/tagged/nrwl) is a much better place to ask general questions
|
||||
about how to use Nx.
|
||||
We are trying to keep GitHub issues for bug reports and feature requests. Using the `nrwl` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/nrwl) is a much better place to ask general questions about how to use Nx.
|
||||
|
||||
## Found an Issue?
|
||||
|
||||
If you find a bug in the source code or a mistake in the documentation, you can help us
|
||||
by [submitting an issue](https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#submit-issue)
|
||||
to [our GitHub Repository](https://github.com/nrwl/nx). Even better, you
|
||||
can [submit a Pull Request](https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#submit-pr) with a fix.
|
||||
If you find a bug in the source code or a mistake in the documentation, you can help us by [submitting an issue](https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#submit-issue) to [our GitHub Repository](https://github.com/nrwl/nx). Even better, you can [submit a Pull Request](https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#submit-pr) with a fix.
|
||||
|
||||
## Project Structure
|
||||
|
||||
Source code and documentation are included in the top-level folders listed below.
|
||||
|
||||
- `docs` - Markdown and configuration files for documentation including tutorials, guides for each supported platform,
|
||||
and API docs.
|
||||
- `docs` - Markdown and configuration files for documentation including tutorials, guides for each supported platform, and API docs.
|
||||
- `e2e` - E2E tests.
|
||||
- `packages` - Source code for Nx packages such as Angular, React, Web, NestJS, Next and others including generators and
|
||||
executors (or builders).
|
||||
- `packages` - Source code for Nx packages such as Angular, React, Web, NestJS, Next and others including generators and executors (or builders).
|
||||
- `scripts` - Miscellaneous scripts for project tasks such as building documentation, testing, and code formatting.
|
||||
- `tmp` - Folder used by e2e tests. If you are a WebStorm user, make sure to mark this folder as excluded.
|
||||
|
||||
## Development Workstation Setup
|
||||
|
||||
If you are using `VSCode`, and provided you have [Docker](https://docker.com) installed on your machine, then you can leverage [Dev Containers](https://containers.dev) through this [VSCode extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), to easily setup your development environment, with everything needed to contribute to Nx, already installed (namely `NodeJS`, `Yarn`, `Rust`, `Cargo`, plus some useful extensions like `Nx Console`).
|
||||
|
||||
To do so, simply:
|
||||
|
||||
- Checkout the repo
|
||||
- Open it with VSCode
|
||||
- Open the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) and select "Dev Containers: Open Folder in Container..."
|
||||
|
||||
The repo comes with a preconfigured `devcontainer.json` file (located in `.devcontainer/` folder at root), that `VSCode` will automatically use to install the aforementioned tools, inside a Docker image. It will even run `pnpm install` for you, so you can start contributing to Nx right after.
|
||||
|
||||
If you open the repo in [Github Codespace](https://github.com/features/codespaces), it will also leverage this config file, to setup the codespace, with the same required tools.
|
||||
|
||||
## Building the Project
|
||||
|
||||
> Nx uses Rust to build native bindings for Node. Please make sure that you have Rust installed via [rustup.rs](https://rustup.rs)
|
||||
> If you have VSCode + Docker, this can be automated for you, see [section](#development-workstation-setup) above
|
||||
|
||||
After cloning the project to your machine, to install the dependencies, run:
|
||||
|
||||
```bash
|
||||
pnpm i
|
||||
yarn
|
||||
```
|
||||
|
||||
To build all the packages, run:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
yarn build
|
||||
```
|
||||
|
||||
## Publishing to a local registry
|
||||
@@ -73,48 +49,28 @@ it can be useful to publish to a local registry.
|
||||
|
||||
Check out [this video for a live walkthrough](https://youtu.be/Tx257WpNsxc) or follow the instructions below:
|
||||
|
||||
- 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
|
||||
```bash
|
||||
# Starts the local registry. Keep this running in a separate terminal.
|
||||
yarn local-registry start
|
||||
|
||||
# Set npm and yarn to use the local registry.
|
||||
# Note: This reroutes your installs to your local registry
|
||||
yarn local-registry enable
|
||||
|
||||
# Revert npm and yarn to use their default registries
|
||||
yarn local-registry disable
|
||||
```
|
||||
|
||||
To publish packages to a local registry, do the following:
|
||||
|
||||
- Run `yarn local-registry start` 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 `yarn local-registry enable` in Terminal 2
|
||||
- Run `yarn nx-release 999.9.9 --local` in Terminal 2
|
||||
- Run `cd ./tmp` in Terminal 2
|
||||
- Run `npx create-nx-workspace@20.0.0` in Terminal 2
|
||||
- Run `npx create-nx-workspace@999.9.9` 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.
|
||||
|
||||
### Publishing for Yarn 2+ (Berry)
|
||||
|
||||
Yarn Berry operates slightly differently than Yarn Classic. In order to publish packages for Berry follow next steps:
|
||||
|
||||
- Run `yarn set version berry` to switch to latest Yarn version.
|
||||
- Create `.yarnrc.yml` in root with following contents:
|
||||
|
||||
```yml
|
||||
nodeLinker: node-modules
|
||||
npmRegistryServer: 'http://localhost:4873'
|
||||
unsafeHttpWhitelist:
|
||||
- localhost
|
||||
```
|
||||
|
||||
- Run `pnpm local-registry` in Terminal 1 (keep it running)
|
||||
- If you are creating nx workspace outside of your nx repo, make sure to add npm registry info to your root yarnrc (
|
||||
usually in ~/.yarnrc.yml). The file should look something like this:
|
||||
|
||||
```yml
|
||||
npmRegistries:
|
||||
'https://registry.yarnpkg.com':
|
||||
npmAuthToken: npm_******************
|
||||
yarnPath: .yarn/releases/yarn-3.2.2.cjs
|
||||
npmRegistryServer: 'http://localhost:4873'
|
||||
unsafeHttpWhitelist:
|
||||
- localhost
|
||||
```
|
||||
|
||||
- Run `pnpm nx-release minor --local` in Terminal 2 to publish next minor version. 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).
|
||||
If you have problems publishing, make sure you use Node 14 and NPM 6 instead of Node 15 and NPM 7.
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
@@ -132,134 +88,98 @@ nx test jest
|
||||
|
||||
### Running E2E Tests
|
||||
|
||||
**Use Node 18 and NPM 8. E2E tests won't work on Node 15 and NPM 7.**
|
||||
**Use Node 14 and NPM 6. E2E tests won't work on Node 15 and NPM 7.**
|
||||
|
||||
To make sure your changes do not break any E2E tests, run:
|
||||
|
||||
```bash
|
||||
nx e2e e2e-vite # or any other project here
|
||||
nx e2e e2e-cli # or any other project here
|
||||
```
|
||||
|
||||
Running E2E tests can take some time, so it is often useful to run a single test. To run a single suite of tests, run:
|
||||
|
||||
```bash
|
||||
nx e2e e2e-vite -t versions # I often add qqqq to my test name so I can use -t qqqq
|
||||
nx e2e e2e-cli -t versions # I often add qqqq to my test name so I can use -t qqqq
|
||||
```
|
||||
|
||||
Sometimes tests pass locally but they fail on the CI. To reproduce the CI environment and be able to debug the issue, run:
|
||||
|
||||
```bash
|
||||
NX_VERBOSE_LOGGING=true CI=true SELECTED_PM=pnpm pnpm nx e2e e2e-vite --t="should do something is this test"
|
||||
```
|
||||
|
||||
The above command sets verbose logging (this exposes stack traces and underlying errors), sets the defaults to be CI-like and sets Pnpm as the selected package manager.
|
||||
|
||||
### Developing on Windows
|
||||
|
||||
To build Nx on Windows, you need to use WSL.
|
||||
|
||||
- Run `pnpm install` in WSL. Yarn will compile several dependencies. If you don't run `install` in WSL, they will be
|
||||
compiled for Windows.
|
||||
- Run `yarn install` in WSL. Yarn will compile several dependencies. If you don't run `install` in WSL, they will be compiled for Windows.
|
||||
- Run `nx affected --target=test` and other commands in WSL.
|
||||
|
||||
## Documentation Contributions
|
||||
|
||||
We would love for you to contribute to our documentation as well! Please feel welcome to submit fixes or enhancements to
|
||||
our existing documentation pages and the `nx-dev` application in this repo.
|
||||
We would love for you to contribute to our documentation as well! Please feel welcome to submit fixes or enhancements to our existing documentation pages and the `nx-dev` application in this repo.
|
||||
|
||||
### Documentation Structure
|
||||
|
||||
#### Documentation Pages
|
||||
|
||||
Our documentation pages can be found within this repo under the `docs` directory.
|
||||
Our documentation pages can be found within this repo under the `docs` directory. Pages that we consider framework specific are nested in their own subdirectories - otherwise they should be nested within the `docs/shared` directory.
|
||||
|
||||
The `docs/map.json` file is considered our source of truth for our site's structure, and should be updated when adding a
|
||||
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.
|
||||
The `docs/map.json` file is considered our source of truth for our site's structure, and should be updated when adding a 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.
|
||||
|
||||
#### Nx-Dev Application
|
||||
|
||||
Our public `nx.dev` documentation site is a [Next.js](https://nextjs.org/) application, that can be found in
|
||||
the `nx-dev` directory of this repo.
|
||||
The documentation site is consuming the `docs/` directly by copy-ing its content while deploying, so the website is
|
||||
always in sync and reflects the latest version of `docs/`.
|
||||
Our public `nx.dev` documentation site is a [Next.js](https://nextjs.org/) application, that can be found in the `nx-dev` directory of this repo.
|
||||
|
||||
Jump to [Running the Documentation Site Locally](#running-the-documentation-site-locally) to see how to preview your
|
||||
changes while serving.
|
||||
The `nx-dev/nx-dev/public/documentation` directory contains `.md` files that are generated from the `docs` directory when new releases are cut. As such, these should not be changed when submitting a change to existing docs.
|
||||
|
||||
Jump to [Running the Documentation Site Locally](#running-the-documentation-site-locally) to see how to preview your changes while serving.
|
||||
|
||||
### Changing Generated API documentation
|
||||
|
||||
`.md` files documenting the API for our CLI (including executor and generator API docs) are generated via the
|
||||
corresponding `schema.json` file for the given command.
|
||||
`.md` files documenting the API for our CLI (including executor and generator API docs) are generated via the corresponding `schema.json` file for the given command.
|
||||
|
||||
After adjusting the `schema.json` file, `.md` files for these commands can be generated by running:
|
||||
|
||||
```bash
|
||||
pnpm documentation
|
||||
yarn documentation
|
||||
```
|
||||
|
||||
This will update the corresponding contents of the `docs` directory. These are generated automatically on push (via
|
||||
husky) as well.
|
||||
This will update the corresponding contents of the `docs` directory. These are generated automatically on push (via husky) as well.
|
||||
|
||||
Note that adjusting the `schema.json` files will also affect the CLI manuals and Nx Console behavior, in addition to
|
||||
adjusting the docs.
|
||||
Note that adjusting the `schema.json` files will also affect the CLI manuals and Nx Console behavior, in addition to adjusting the docs.
|
||||
|
||||
### Running the Documentation Site Locally
|
||||
|
||||
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`
|
||||
By default, the site displays the `Latest` cut release of the docs. To see your current changes in the docs be sure to select `Preview` from the version selection box of the site.
|
||||
|
||||
If you see an error that states: `FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory`,
|
||||
you need
|
||||
to [increase the max memory size of V8's old memory section](https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-megabytes):
|
||||
|
||||
```bash
|
||||
export NODE_OPTIONS="--max-old-space-size=4096"
|
||||
```
|
||||
|
||||
After configuring this, try to run `npx nx serve nx-dev` again.
|
||||
<img src="https://raw.githubusercontent.com/nrwl/nx/master/images/selecting-preview-from-version-selection-box.png" width="600" alt="Selecting Preview from Version Selection box">
|
||||
|
||||
### PR Preview
|
||||
|
||||
When submitting a PR, this repo will automatically generate a preview of the `nx-dev` application based on the contents
|
||||
of your pull request.
|
||||
When submitting a PR, this repo will automatically generate a preview of the `nx-dev` application based on the contents of your pull request.
|
||||
|
||||
Once the preview site is launched, a comment will automatically be added to your PR with the link your PR's preview. To
|
||||
check your docs changes, make sure to select `Preview` from the version selection box of the site.
|
||||
Once the preview site is launched, a comment will automatically be added to your PR with the link your your PR's preview. To check your docs changes, make sure to select `Preview` from the version selection box of the site.
|
||||
|
||||
## Submission Guidelines
|
||||
|
||||
### <a name="submit-issue"></a> Submitting an Issue
|
||||
|
||||
Before you submit an issue, please search the issue tracker. An issue for your problem may already exist and has been
|
||||
resolved, or the discussion might inform you of workarounds readily available.
|
||||
Before you submit an issue, please search the issue tracker. An issue for your problem may already exist and has been resolved, or the discussion might inform you of workarounds readily available.
|
||||
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. Having a
|
||||
reproducible scenario gives us wealth of important information without going back and forth with you requiring
|
||||
additional information, such as:
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. Having a reproducible scenario gives us wealth of important information without going back and forth with you requiring additional information, such as:
|
||||
|
||||
- the output of `nx report`
|
||||
- `yarn.lock` or `package-lock.json` or `pnpm-lock.yaml`
|
||||
- `yarn.lock` or `package-lock.json`
|
||||
- and most importantly - a use-case that fails
|
||||
|
||||
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are
|
||||
fixing the right problem.
|
||||
A minimal reproduction allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem.
|
||||
|
||||
We will be insisting on a minimal reproduction in order to save maintainers' time and ultimately be able to fix more
|
||||
bugs. Interestingly, from our experience, users often find coding problems themselves while preparing a minimal
|
||||
reproduction repository. We understand that sometimes it might be hard to extract essentials bits of code from a larger
|
||||
codebase, but we really need to isolate the problem before we can fix it.
|
||||
We will be insisting on a minimal reproduction in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience, users often find coding problems themselves while preparing a minimal repository. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
|
||||
|
||||
You can file new issues by filling out our [issue form](https://github.com/nrwl/nx/issues/new/choose).
|
||||
You can file new issues by filling out our [issue form](https://github.com/nrwl/nx/issues/new).
|
||||
|
||||
### <a name="submit-pr"></a> Submitting a PR
|
||||
|
||||
@@ -267,22 +187,22 @@ Please follow the following guidelines:
|
||||
|
||||
- Make sure unit tests pass (`nx affected --target=test`)
|
||||
- Target a specific project with: `nx run proj:test` (i.e. `nx run angular:test` to target `packages/angular`)
|
||||
- Target a specific unit test file (i.e. `packages/angular/src/utils/ast-command-line-utils.spec.ts`)
|
||||
with `npx jest angular/src/utils/ast-utils` or `npx jest packages/angular/src/utils/ast-utils`
|
||||
- Target a specific unit test file (i.e. `packages/angular/src/utils/ast-utils.spec.ts`) with `npx jest angular/src/utils/ast-utils` or `npx jest packages/angular/src/utils/ast-utils`
|
||||
- For more options on running tests - check `npx jest --help` or visit [jestjs.io](https://jestjs.io/)
|
||||
- Debug with `node --inspect-brk ./node_modules/jest/bin/jest.js build/packages/angular/src/utils/ast-utils.spec.js`
|
||||
- Make sure e2e tests pass (this can take a while, so you can always let CI check those) (`nx affected --target=e2e`)
|
||||
- Target a specific e2e test with `nx e2e e2e-cypress`
|
||||
- Make sure you run `nx format`
|
||||
- Update documentation with `pnpm documentation`. For documentation, check for spelling and grammatical errors.
|
||||
- Update your commit message to follow the guidelines below (use `pnpm commit` to automate compliance)
|
||||
- `pnpm check-commit` will check to make sure your commit messages are formatted correctly
|
||||
- Make sure e2e tests pass (this can take a while, so you can always let CI check those) (`yarn e2e`)
|
||||
- Target a specific e2e test (i.e. `/build/e2e/cypress.test.js`) with `yarn e2e cypress`
|
||||
- Debug with `node --inspect-brk ./node_modules/jest/bin/jest.js build/e2e/cypress.test.js`
|
||||
- Make sure you run `yarn format`
|
||||
- Update documentation with `yarn documentation`. For documentation, check for spelling and grammatical errors.
|
||||
- Update your commit message to follow the guidelines below (use `yarn commit` to automate compliance)
|
||||
- `yarn check-commit` will check to make sure your commit messages are formatted correctly
|
||||
|
||||
#### Commit Message Guidelines
|
||||
|
||||
The commit message should follow the following format:
|
||||
|
||||
```plain
|
||||
```
|
||||
type(scope): subject
|
||||
BLANK LINE
|
||||
body
|
||||
@@ -292,12 +212,9 @@ body
|
||||
|
||||
The type must be one of the following:
|
||||
|
||||
- feat - New or improved behavior being introduced (e.g. Updating to new versions of React or Jest which bring in new
|
||||
features)
|
||||
- fix - Fixes the current unexpected behavior to match expected behavior (e.g. Fixing the library generator to create
|
||||
the proper named project)
|
||||
- cleanup - Code Style changes that have little to no effect on the user (e.g. Refactoring some functions into a
|
||||
different file)
|
||||
- feat - New or improved behavior being introduced (e.g. Updating to new versions of React or Jest which bring in new features)
|
||||
- fix - Fixes the current unexpected behavior to match expected behavior (e.g. Fixing the library generator to create the proper named project)
|
||||
- cleanup - Code Style changes that have little to no effect on the user (e.g. Refactoring some functions into a different file)
|
||||
- docs - Changes to the documentation (e.g. Adding more details into the getting started guide)
|
||||
- chore - Changes that have absolutely no effect on users (e.g. Updating the version of Nx used to build the repo)
|
||||
|
||||
@@ -306,46 +223,32 @@ The type must be one of the following:
|
||||
The scope must be one of the following:
|
||||
|
||||
- angular - anything Angular specific
|
||||
- bundling - anything bundling specific (e.g. rollup, webpack, etc.)
|
||||
- core - anything Nx core specific
|
||||
- detox - anything Detox specific
|
||||
- devkit - devkit-related changes
|
||||
- graph - anything graph app specific
|
||||
- expo - anything Expo specific
|
||||
- express - anything Express specific
|
||||
- js - anything related to @nx/js package or general js/ts support
|
||||
- linter - anything Linter specific
|
||||
- nest - anything Nest specific
|
||||
- nxdev - anything related to docs infrastructure
|
||||
- nextjs - anything Next specific
|
||||
- nest - anything Nest specific
|
||||
- node - anything Node specific
|
||||
- nx-cloud - anything Nx Cloud specific
|
||||
- nx-plugin - anything Nx Plugin specific
|
||||
- nx-dev - anything related to docs infrastructure
|
||||
- linter - anything Linter specific
|
||||
- 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
|
||||
- storybook - anything Storybook specific
|
||||
- testing - anything testing specific (e.g., jest or cypress)
|
||||
- repo - anything related to managing the repo itself
|
||||
- misc - misc stuff
|
||||
- devkit - devkit-related changes
|
||||
|
||||
##### Subject and Body
|
||||
|
||||
The subject must contain a description of the change, and the body of the message contains any additional details to
|
||||
provide more context about the change.
|
||||
The subject must contain a description of the change, and the body of the message contains any additional details to provide more context about the change.
|
||||
|
||||
Including the issue number that the PR relates to also helps with tracking.
|
||||
|
||||
#### Example
|
||||
|
||||
```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
|
||||
```
|
||||
@@ -353,9 +256,4 @@ Closes #157
|
||||
#### Commitizen
|
||||
|
||||
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.
|
||||
**Nx is a [Commitizen](https://github.com/commitizen/cz-cli) friendly repository**, just do `git add` and execute `yarn commit`.
|
||||
|
||||
Generated
-3106
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
||||
|
||||
[workspace]
|
||||
resolver = '2'
|
||||
members = [
|
||||
'packages/nx',
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -1,22 +1,22 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2017-2024 Narwhal Technologies Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2017-2021 Narwhal Technologies Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@@ -1,44 +1,87 @@
|
||||
<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%">
|
||||
</picture>
|
||||
</p>
|
||||
<p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx.png"
|
||||
width="100%" alt="Nx - Smart, Extensible Build Framework"></p>
|
||||
|
||||
<div style="text-align: center;">
|
||||
|
||||
[](https://circleci.com/gh/nrwl/nx)
|
||||
[]()
|
||||
[](https://www.npmjs.com/package/nx)
|
||||
[]()
|
||||
[](https://www.npmjs.com/@nrwl/workspace)
|
||||
[]()
|
||||
[](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
|
||||
# What is Nx?
|
||||
|
||||
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.
|
||||
🔎 **Smart, Extensible Build Framework**
|
||||
|
||||
A few links to help you get started:
|
||||
Nx is a smart and extensible build framework to help you architect, test, and build at any scale — integrating seamlessly with modern technologies and frameworks while providing a distributed graph-based task execution, computation caching, smart rebuilds of affected projects, powerful code generators, editor support, GitHub apps, and more.
|
||||
|
||||
- [Nx.Dev: Documentation, Guides, Interactive Tutorials](https://nx.dev)
|
||||
- [Nx.Dev: Core Tutorials](https://nx.dev/getting-started/intro)
|
||||
- [Recipe: Adding Nx to an Existing Monorepo](https://nx.dev/recipes/adopting-nx/adding-to-monorepo)
|
||||
- [Official Nx YouTube Channel](https://www.youtube.com/@NxDevtools)
|
||||
### Best-in-Class Support for Monorepos
|
||||
|
||||
<strong>Nx</strong> provides distributed graph-based task execution and computation caching.
|
||||
|
||||
<strong>Nx</strong> is smart. It analyzes your workspace and figures out what can be affected by every code change.
|
||||
That's why Nx doesn't rebuild and retest everything on every commit--<strong>it only rebuilds what is necessary</strong>
|
||||
.
|
||||
|
||||
<strong>Nx</strong> partitions commands into a graph of smaller tasks. Nx then runs those tasks in parallel,
|
||||
and <strong>it can even distribute them across multiple machines without any configuration</strong>.
|
||||
|
||||
<strong>Nx also uses a distributed computation cache.</strong> If someone has already built or tested similar code, Nx
|
||||
will use their results to speed up the command for everyone else.
|
||||
|
||||
### Holistic Dev Experience Powered by an Advanced CLI and Editor Plugins
|
||||
|
||||
<strong>Nx</strong> helps scale your development from one team building one application to many teams building multiple
|
||||
frontend and backend applications all in the same workspace. <strong >When using Nx, developers have a holistic dev
|
||||
experience powered by an advanced CLI</strong > (with editor plugins), capabilities for controlled code sharing and
|
||||
consistent code generation.
|
||||
|
||||
### Rich Plugin Ecosystem
|
||||
|
||||
<strong>Nx</strong> is an open platform with plugins for many modern tools and frameworks. It has support for
|
||||
TypeScript, React, Angular, Cypress, Jest, Prettier, Nest.js, Next.js, Storybook, Ionic among others. With Nx, you get a
|
||||
consistent dev experience regardless of the tools used.
|
||||
|
||||
# Documentation & Resources
|
||||
|
||||
Even though Nx isn't technology specific, we provide 3 separate flavours of the documentation site to it make it easier
|
||||
for you to get up and running. For every link below, you will be able to select whether you want your examples to be
|
||||
written in React, Node or Angular.
|
||||
|
||||
- [Nx Documentation and Guides](https://nx.dev)
|
||||
- [Intro into Nx](https://nx.dev/getting-started/intro)
|
||||
- [Interactive Tutorial with Videos](https://nx.dev/tutorial/01-create-application)
|
||||
|
||||
### Quick Start Videos
|
||||
|
||||
- [Scale Your React Development with Nx](https://www.youtube.com/watch?v=sNz-4PUM0k8)
|
||||
- [Scale your Node Development with Nx](https://www.youtube.com/watch?v=iIh5h_G52kI)
|
||||
- [Modern Angular with Nx Dev Tools](https://www.youtube.com/watch?v=cXOkmOy-8dk)
|
||||
|
||||
### Videos, Blogs, Books, Examples
|
||||
|
||||
<p style="text-align: center;"><a href="https://nx.dev/#learning-materials" target="_blank"><img src="./images/nx-courses-and-videos.svg"
|
||||
width="100%" alt="Nx - Smart, Extensible Build Framework"></a></p>
|
||||
|
||||
- [Official Nx YouTube Channel](https://www.youtube.com/c/Nrwl_io)
|
||||
- [Scale React Development with Nx (Free Egghead course)](https://egghead.io/playlists/scale-react-development-with-nx-4038)
|
||||
- [Official NxPlaybook video courses](https://nxplaybook.com)
|
||||
- [Nx Dev Tools for Monorepos, In-Depth Explainer (React)](https://www.youtube.com/watch?v=jCf92IyR-GE)
|
||||
- [Nx Dev Tools for Monorepos, In-Depth Explainer (Angular)](https://youtu.be/h5FIGDn5YM0)
|
||||
- [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>
|
||||
- [Angular Enterprise Monorepo Patterns Book (free)](https://go.nrwl.io/angular-enterprise-monorepo-patterns-new-book?utm_campaign=Book%3A%20Monorepo%20Patterns%2C%20Jan%202019&utm_source=Github&utm_medium=Banner%20Ad)
|
||||
- [Nx Examples Repo](https://github.com/nrwl/nx-examples)
|
||||
|
||||
# Engage with the Core Team and the Community
|
||||
|
||||
- [Nx.Dev Community Page: Community Discord 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.
|
||||
- [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)
|
||||
|
||||
## Want to help?
|
||||
@@ -47,33 +90,33 @@ If you want to file a bug or submit a PR, read up on
|
||||
our [guidelines for contributing](https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md) and watch this video that will
|
||||
help you get started.
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=8LCA_4qxc08" target="_blank" rel="noreferrer">
|
||||
<a href="https://www.youtube.com/watch?v=8LCA_4qxc08" target="_blank">
|
||||
<p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/how-to-contribute.png" width="600" alt="Nx - How to contribute video"></p>
|
||||
</a>
|
||||
|
||||
## Core Team
|
||||
|
||||
| Victor Savkin | Jason Jean | Benjamin Cabanes | Jack Hsu |
|
||||
| ---------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------- |
|
||||
|  |  |  |  |
|
||||
| [vsavkin](https://github.com/vsavkin) | [FrozenPandaz](https://github.com/FrozenPandaz) | [bcabanes](https://github.com/bcabanes) | [jaysoo](https://github.com/jaysoo) |
|
||||
| Victor Savkin | Jason Jean | Benjamin Cabanes | Brandon Roberts |
|
||||
| ---------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
||||
|  |  |  |  |
|
||||
| [vsavkin](https://github.com/vsavkin) | [FrozenPandaz](https://github.com/FrozenPandaz) | [bcabanes](https://github.com/bcabanes) | [brandonroberts](https://github.com/brandonroberts) |
|
||||
|
||||
| Jo Hanna Pearce | Jon Cammisuli | Isaac Mann | Juri Strumpflohner |
|
||||
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
|  |  |  |  |
|
||||
| [jdpearce](https://github.com/jdpearce) | [cammisuli](https://github.com/cammisuli) | [isaacplmann](https://github.com/isaacplmann) | [juristr](https://github.com/juristr) |
|
||||
| Jack Hsu | Jo Hanna Pearce | Jon Cammisuli | Isaac Mann |
|
||||
| ----------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------- |
|
||||
|  |  |  |  |
|
||||
| [jaysoo](https://github.com/jaysoo) | [jdpearce](https://github.com/jdpearce) | [cammisuli](https://github.com/cammisuli) | [isaacplmann](https://github.com/isaacplmann) |
|
||||
|
||||
| Philip Fulcher | Caleb Ukle | Katerina Skroumpelou | Colum Ferry |
|
||||
| ------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
||||
|  |  |  |  |
|
||||
| [philipjfulcher](https://github.com/philipjfulcher) | [barbados-clemens](https://github.com/barbados-clemens) | [mandarini](https://github.com/mandarini) | [Coly010](https://github.com/Coly010) |
|
||||
| Juri Strumpflohner | Philip Fulcher | Kirils Ladovs | Katerina Skroumpelou |
|
||||
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
|  |  |  |  |
|
||||
| [juristr](https://github.com/juristr) | [philipjfulcher](https://github.com/philipjfulcher) | [kirjai](https://github.com/kirjai) | [mandarini](https://github.com/mandarini) |
|
||||
|
||||
| Emily Xiong | Miroslav Jonaš | Leosvel Pérez Espinosa | Zachary DeRose |
|
||||
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
||||
|  |  |  |  |
|
||||
| [xiongemi](https://github.com/xiongemi) | [meeroslav](https://github.com/meeroslav) | [leosvelperez](https://github.com/leosvelperez) | [ZackDeRose](https://github.com/ZackDeRose) |
|
||||
| Colum Ferry | Emily Xiong | Miroslav Jonaš | Leosvel Pérez Espinosa |
|
||||
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
||||
|  |  |  |  |
|
||||
| [Coly010](https://github.com/Coly010) | [xiongemi](https://github.com/xiongemi) | [meeroslav](https://github.com/meeroslav) | [leosvelperez](https://github.com/leosvelperez) |
|
||||
|
||||
| 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) |
|
||||
| Zachary DeRose | Craigory Coppola | Chau Tran |
|
||||
| ------------------------------------------------------------------------ | -------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
||||
|  |  |  |
|
||||
| [ZackDeRose](https://github.com/ZackDeRose) | [AgentEnder](https://github.com/AgentEnder) | [nartc](https://github.com/nartc) |
|
||||
|
||||
+55
-315
@@ -1,63 +1,8 @@
|
||||
[
|
||||
{
|
||||
"name": "@ahryman40k/nx-vitepress",
|
||||
"description": "Nx plugin add vitepress project to your workspace",
|
||||
"url": "https://github.com/Ahryman40k/nx-vitepress/tree/main/packages/nx-vitepress"
|
||||
},
|
||||
{
|
||||
"name": "@nightwatch/nx",
|
||||
"description": "The NightwatchJS plugin allows your workspace to use the power of NightwatchJS for E2E and Component Testing on Desktop and Mobile",
|
||||
"url": "https://github.com/nightwatchjs/nightwatch-plugin-nx/"
|
||||
},
|
||||
{
|
||||
"name": "@nxkit/playwright",
|
||||
"description": "The Playwright plugin allows your workspace to use the power of Playwright end-to-end testing",
|
||||
"url": "https://github.com/nxkit/nxkit"
|
||||
},
|
||||
{
|
||||
"name": "qwik-nx",
|
||||
"description": "Get first class support for Qwik inside of Nx monorepos with Qwik optimized custom executors, generators and a vite plugin",
|
||||
"url": "https://github.com/qwikifiers/qwik-nx"
|
||||
},
|
||||
{
|
||||
"name": "@nxkit/style-dictionary",
|
||||
"description": "Nx plugin to generate and build Style Dictionary projects inside your Nx workspace",
|
||||
"url": "https://github.com/nxkit/nxkit"
|
||||
},
|
||||
{
|
||||
"name": "nx-plugin-vite",
|
||||
"description": "Nx plugin integrations with Vite.",
|
||||
"url": "https://nx-plugins.netlify.app/"
|
||||
},
|
||||
{
|
||||
"name": "nx-serverless-cdk",
|
||||
"description": "Create CDK applications and construct libraries. Test and debug infrastructure code and AWS Lambda functions locally.",
|
||||
"url": "https://github.com/castleadmin/nx-plugins/tree/main/nx-serverless-cdk/plugin"
|
||||
},
|
||||
{
|
||||
"name": "@ago-dev/nx-aws-cdk-v2",
|
||||
"description": "An nx plugin for the aws-cdk v2.",
|
||||
"url": "https://github.com/adrian-goe/nx-aws-cdk-v2"
|
||||
},
|
||||
{
|
||||
"name": "@berenddeboer/nx-aws-cdk",
|
||||
"description": "Nx plugin to generate a CDK stack with support for the vitest runner. Supports all CDK commands.",
|
||||
"url": "https://github.com/berenddeboer/nx-plugins/tree/main/packages/nx-aws-cdk"
|
||||
},
|
||||
{
|
||||
"name": "@nx-iac/aws-cdk",
|
||||
"description": "Empowers your Nx workspace with AWS CDK capabilities ⚡",
|
||||
"url": "https://github.com/joelklint/nx-aws-cdk"
|
||||
},
|
||||
{
|
||||
"name": "@routineless/nx-aws-cdk",
|
||||
"description": "Nx plugin to generate and manage aws cdk app and lambdas.",
|
||||
"url": "https://github.com/KozelAnatoliy/routineless/tree/main/packages/nx-aws-cdk"
|
||||
},
|
||||
{
|
||||
"name": "@berenddeboer/nx-sst",
|
||||
"description": "Nx plugin to generate an SST stack and execute all SST commands.",
|
||||
"url": "https://github.com/berenddeboer/nx-plugins/tree/main/packages/nx-sst"
|
||||
"name": "@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": "@rxap/plugin-localazy",
|
||||
@@ -75,25 +20,35 @@
|
||||
"url": "https://github.com/Phillip9587/nx-stylelint"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/ionic-react",
|
||||
"name": "@nxtend/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/nxtend-team/nxtend/tree/main/packages/ionic-react"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/ionic-angular",
|
||||
"name": "@nxtend/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/nxtend-team/nxtend/tree/main/packages/ionic-angular"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/capacitor",
|
||||
"name": "@nxtend/capacitor",
|
||||
"description": "An Nx plugin for developing cross-platform applications using Capacitor",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/main/packages/capacitor"
|
||||
"url": "https://github.com/nxtend-team/nxtend/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",
|
||||
@@ -110,9 +65,24 @@
|
||||
"url": "https://github.com/Bielik20/nx-plugins/tree/master/packages/nx-jest-playwright"
|
||||
},
|
||||
{
|
||||
"name": "@ns3/nx-playwright",
|
||||
"description": "Nx plugin to run playwright e2e tests in an Nx workspace",
|
||||
"url": "https://github.com/Bielik20/nx-plugins/tree/master/packages/nx-playwright"
|
||||
"name": "@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": "@nx-tools/nx-docker",
|
||||
"description": "Nx plugin to build docker images of your affected apps",
|
||||
"url": "https://github.com/gperdomor/nx-tools/tree/main/packages/nx-docker"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
@@ -134,16 +104,6 @@
|
||||
"description": "Nx Plugin to integrate the generation of documentation with Compodoc in the Nx workflow",
|
||||
"url": "https://github.com/twittwer/nx-tools/tree/master/libs/compodoc#readme"
|
||||
},
|
||||
{
|
||||
"name": "@enio.ai/nx-install",
|
||||
"description": "nx-install is a plugin for Nx workspaces to quickly setup custom install commands via npm.",
|
||||
"url": "https://github.com/enio-ireland/enio/tree/develop/packages/nx-install#readme"
|
||||
},
|
||||
{
|
||||
"name": "@enio.ai/typedoc",
|
||||
"description": "typedoc is a plugin for Nx workspaces to quickly setup documentation automation on your projects using typedoc.",
|
||||
"url": "https://github.com/enio-ireland/enio/tree/develop/packages/typedoc#readme"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/svelte",
|
||||
"description": "Nx plugin to use Svelte within nx workspaces",
|
||||
@@ -155,25 +115,15 @@
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/stencil"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/vue",
|
||||
"description": "Nx plugin to use VueJS 3 within nx workspaces",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/vue"
|
||||
},
|
||||
{
|
||||
"name": "@nxext/solid",
|
||||
"description": "Nx plugin to use SolidJS within nx workspaces",
|
||||
"url": "https://github.com/nxext/nx-extensions/tree/master/packages/solid"
|
||||
"name": "@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",
|
||||
"url": "https://github.com/nx-go/nx-go"
|
||||
},
|
||||
{
|
||||
"name": "@nx-golang/gin",
|
||||
"description": "Nx plugin to use Go-Gin in a Nx workspace",
|
||||
"url": "https://github.com/nx-golang/nx-golang"
|
||||
},
|
||||
{
|
||||
"name": "@angular-architects/module-federation",
|
||||
"description": "Nx plugin to use webpack module federation",
|
||||
@@ -215,7 +165,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"
|
||||
},
|
||||
@@ -254,239 +209,24 @@
|
||||
"description": "Nx plugin to extract, pull, push and translate your apps translations",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/translations"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/firebase-hosting",
|
||||
"description": "Nx plugin to deploy your apps to Firebase hosting",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/firebase-hosting"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/e2e-runner",
|
||||
"description": "Nx plugin that can start your API before running Cypress/Playwright",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/e2e-runner"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/vercel",
|
||||
"description": "Nx plugin to deploy your apps to Vercel",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/vercel"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/strapi",
|
||||
"description": "Nx plugin for developing Strapi applications",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/strapi"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/playwright",
|
||||
"description": "Nx plugin to run playwright e2e tests in an Nx workspace",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/playwright"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/terraform",
|
||||
"description": "Nx plugin for deploying your resources with Terraform",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/terraform"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/pulumi",
|
||||
"description": "Nx plugin for deploying your resources with Pulumi",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/pulumi"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/react-email",
|
||||
"description": "Nx plugin for developing email templates with react.email",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/react-email"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/shadcn-ui",
|
||||
"description": "Nx plugin for working with shadcn/ui",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/shadcn-ui"
|
||||
},
|
||||
{
|
||||
"name": "@nx-extend/docusaurus",
|
||||
"description": "Nx plugin adding first class support for Docusaurus in your Nx workspace.",
|
||||
"url": "https://github.com/tripss/nx-extend/tree/master/packages/docusaurus"
|
||||
},
|
||||
{
|
||||
"name": "@nativescript/nx",
|
||||
"description": "Nx Plugin adding first class support for NativeScript in your Nx workspace",
|
||||
"url": "https://github.com/nativescript/nx"
|
||||
},
|
||||
{
|
||||
"name": "@nxtensions/astro",
|
||||
"description": "Nx plugin adding first class support for Astro (https://astro.build).",
|
||||
"url": "https://github.com/nxtensions/nxtensions/tree/main/packages/astro"
|
||||
"name": "@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": "@nxrs/cargo",
|
||||
"description": "Nx plugin adding first-class support for Rust applications and libraries.",
|
||||
"url": "https://github.com/nxrs/cargo/tree/main/packages/cargo"
|
||||
"name": "@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": "nx-uvu",
|
||||
"description": "An nx executor for the uvu test library",
|
||||
"url": "https://github.com/jmcdo29/nx-uvu"
|
||||
},
|
||||
{
|
||||
"name": "@ndrsg/nx-http",
|
||||
"description": "Plugin with executors, which can perform http-stuff (e.g. requests, webhooks, file up/downloads, ...)",
|
||||
"url": "https://github.com/ndrsg/nx-ext/tree/main/packages/nx-http"
|
||||
},
|
||||
{
|
||||
"name": "@diogovcs/graphql-mesh",
|
||||
"description": "Nx plugin to add GraphQL Mesh integration to Nx Workspace.",
|
||||
"url": "https://github.com/DiogoVCS/nx-workspace-plugins"
|
||||
},
|
||||
{
|
||||
"name": "@computas/nx-yarn",
|
||||
"description": "A plugin to help make nx with yarn a pleasant experience.",
|
||||
"url": "https://github.com/computas/nx-yarn"
|
||||
},
|
||||
{
|
||||
"name": "@theunderscorer/nx-semantic-release",
|
||||
"description": "Nx plugin for automated releases using semantic-release.",
|
||||
"url": "https://github.com/TheUnderScorer/nx-semantic-release"
|
||||
},
|
||||
{
|
||||
"name": "nx-pwm",
|
||||
"description": "Nx plugin and CLI to help maintain packages, inspired by NX repo tooling",
|
||||
"url": "https://github.com/gioragutt/nx-pwm"
|
||||
},
|
||||
{
|
||||
"name": "@nxrocks/nx-micronaut",
|
||||
"description": "Nx plugin to generate, run, package, build (and more) Micronaut projects inside your Nx workspace",
|
||||
"url": "https://github.com/tinesoft/nxrocks/tree/master/packages/nx-micronaut"
|
||||
},
|
||||
{
|
||||
"name": "@koliveira15/nx-sonarqube",
|
||||
"description": "Nx plugin that scans projects using SonarQube / SonarCloud.",
|
||||
"url": "https://github.com/koliveira15/nx-sonarqube"
|
||||
},
|
||||
{
|
||||
"name": "@mands/nx-playwright",
|
||||
"description": "The Playwright plugin allows your workspace to use the power of Playwright end-to-end testing using a native runner.",
|
||||
"url": "https://github.com/marksandspencer/nx-plugins/tree/main/packages/nx-playwright"
|
||||
},
|
||||
{
|
||||
"name": "@diogovcs/stryker-mutator",
|
||||
"description": "Nx plugin to add Stryker Mutator mutation tests to the Nx Workspace.",
|
||||
"url": "https://github.com/DiogoVCS/nx-workspace-plugins"
|
||||
},
|
||||
{
|
||||
"name": "@spaceribs/nx-web-ext",
|
||||
"description": "Nx plugin that allows you to build, test and deploy web extensions.",
|
||||
"url": "https://github.com/spaceribs/spaceribs/tree/main/packages/nx-web-ext"
|
||||
},
|
||||
{
|
||||
"name": "@spaceribs/nx-betterer",
|
||||
"description": "Nx plugin that applies betterer standards on a per-project basis.",
|
||||
"url": "https://github.com/spaceribs/spaceribs/tree/main/packages/nx-betterer"
|
||||
},
|
||||
{
|
||||
"name": "@nx-tools/nx-container",
|
||||
"description": "Nx plugin to build OCI containers with Docker, Podman or Kaniko.",
|
||||
"url": "https://github.com/gperdomor/nx-tools/tree/main/packages/nx-container"
|
||||
},
|
||||
{
|
||||
"name": "@nxrocks/nx-melos",
|
||||
"description": "Nx plugin adding first class support for Melos in your Nx workspace",
|
||||
"url": "https://github.com/tinesoft/nxrocks/tree/master/packages/nx-melos"
|
||||
},
|
||||
{
|
||||
"name": "@monodon/rust",
|
||||
"description": "Adds Cargo and Rust support",
|
||||
"url": "https://github.com/cammisuli/monodon/tree/main/packages/rust"
|
||||
},
|
||||
{
|
||||
"name": "nx-mesh",
|
||||
"description": "GraphQL Mesh support for Nx",
|
||||
"url": "https://github.com/domjtalbot/nx-mesh"
|
||||
},
|
||||
{
|
||||
"name": "@nxazure/func",
|
||||
"description": "Nx plugin to add Azure Functions support to Nx Workspace.",
|
||||
"url": "https://github.com/AlexPshul/nxazure/tree/master/packages/func"
|
||||
},
|
||||
{
|
||||
"name": "@rbnx/webdriverio",
|
||||
"description": "A Nx plugin that adds the WebdriverIO testing framework to a NX workspace.",
|
||||
"url": "https://github.com/Roozenboom/rbnx/tree/main/packages/webdriverio"
|
||||
},
|
||||
{
|
||||
"name": "nx-ngrok",
|
||||
"description": "Ngrok support for Nx",
|
||||
"url": "https://github.com/domjtalbot/nx-ngrok"
|
||||
},
|
||||
{
|
||||
"name": "@nxrocks/nx-ktor",
|
||||
"description": "Nx plugin to generate, run, package, build (and more) Ktor projects inside your Nx workspace",
|
||||
"url": "https://github.com/tinesoft/nxrocks/tree/master/packages/nx-ktor"
|
||||
},
|
||||
{
|
||||
"name": "nx-size-limit",
|
||||
"description": "Adds size-limit support for Nx (performance budget tool for JavaScript)",
|
||||
"url": "https://github.com/LironHazan/nx-size-limit"
|
||||
},
|
||||
{
|
||||
"name": "@loft-orbital/terraform",
|
||||
"description": "Terraform executors and generators",
|
||||
"url": "https://github.com/loft-orbital/nx-plugins/tree/main/packages/terraform"
|
||||
},
|
||||
{
|
||||
"name": "@nxlv/python",
|
||||
"description": "Nx plugin designed to extend the Nx features to work with Python projects based on Poetry.",
|
||||
"url": "https://github.com/lucasvieirasilva/nx-plugins/tree/main/packages/nx-python"
|
||||
},
|
||||
{
|
||||
"name": "nx-gcp-cache",
|
||||
"description": "Nx plugin to use Google Cloud Storage as distributed remote cache",
|
||||
"url": "https://github.com/davidgarciab/nx-gcp-cache"
|
||||
},
|
||||
{
|
||||
"name": "@jnxplus/nx-gradle",
|
||||
"description": "Nx plugin to add Gradle multi-project builds support to Nx workspace",
|
||||
"url": "https://github.com/khalilou88/jnxplus/tree/main/packages/nx-gradle"
|
||||
},
|
||||
{
|
||||
"name": "@jnxplus/nx-maven",
|
||||
"description": "Nx plugin to add Maven multi-module project support to Nx workspace",
|
||||
"url": "https://github.com/khalilou88/jnxplus/tree/main/packages/nx-maven"
|
||||
},
|
||||
{
|
||||
"name": "@naxodev/nx-cloudflare",
|
||||
"description": "Nx plugin for Cloudflare, in particular Cloudflare workers. It allows to generate build and run Cloudflare workers in your Nx workspace.",
|
||||
"url": "https://github.com/naxodev/oss/tree/main/packages/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"
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"fileServerFolder": ".",
|
||||
"fixturesFolder": "./src/fixtures",
|
||||
"integrationFolder": "./src/watch-mode-integration",
|
||||
"modifyObstructiveCode": false,
|
||||
"pluginsFile": "./src/plugins/index",
|
||||
"supportFile": "./src/support/index.ts",
|
||||
"video": true,
|
||||
"videosFolder": "../../dist/cypress/dep-graph/dep-graph-e2e/videos",
|
||||
"screenshotsFolder": "../../dist/cypress/dep-graph/dep-graph-e2e/screenshots",
|
||||
"chromeWebSecurity": false
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"fileServerFolder": ".",
|
||||
"fixturesFolder": "./src/fixtures",
|
||||
"integrationFolder": "./src/integration",
|
||||
"modifyObstructiveCode": false,
|
||||
"pluginsFile": "./src/plugins/index",
|
||||
"supportFile": "./src/support/index.ts",
|
||||
"video": true,
|
||||
"videosFolder": "../../dist/cypress/dep-graph/dep-graph-e2e/videos",
|
||||
"screenshotsFolder": "../../dist/cypress/dep-graph/dep-graph-e2e/screenshots",
|
||||
"chromeWebSecurity": false
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"root": "dep-graph/dep-graph-e2e",
|
||||
"sourceRoot": "dep-graph/dep-graph-e2e/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"e2e-disabled": {
|
||||
"executor": "@nrwl/cypress:cypress",
|
||||
"options": {
|
||||
"cypressConfig": "dep-graph/dep-graph-e2e/cypress.json",
|
||||
"tsConfig": "dep-graph/dep-graph-e2e/tsconfig.e2e.json",
|
||||
"devServerTarget": "dep-graph-dep-graph:serve-for-e2e",
|
||||
"baseUrl": "http://localhost:4200"
|
||||
}
|
||||
},
|
||||
"e2e-watch-disabled": {
|
||||
"executor": "@nrwl/cypress:cypress",
|
||||
"options": {
|
||||
"cypressConfig": "dep-graph/dep-graph-e2e/cypress-watch-mode.json",
|
||||
"tsConfig": "dep-graph/dep-graph-e2e/tsconfig.e2e.json",
|
||||
"devServerTarget": "dep-graph-dep-graph:serve-for-e2e:watch",
|
||||
"baseUrl": "http://localhost:4200"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:lint",
|
||||
"options": {
|
||||
"linter": "eslint",
|
||||
"tsConfig": ["dep-graph/dep-graph-e2e/tsconfig.e2e.json"],
|
||||
"exclude": ["**/node_modules/**", "!dep-graph/dep-graph-e2e/**/*"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"implicitDependencies": ["dep-graph-dep-graph"]
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import {
|
||||
getCheckedProjectItems,
|
||||
getDeselectAllButton,
|
||||
getIncludeProjectsInPathButton,
|
||||
getProjectItems,
|
||||
getSelectAllButton,
|
||||
getSelectProjectsMessage,
|
||||
getTextFilterInput,
|
||||
getUncheckedProjectItems,
|
||||
getUnfocusProjectButton,
|
||||
} from '../support/app.po';
|
||||
|
||||
describe('dep-graph-client', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('/assets/graphs/*').as('getGraph');
|
||||
|
||||
cy.visit('/');
|
||||
|
||||
// wait for first graph to finish loading
|
||||
cy.wait('@getGraph');
|
||||
});
|
||||
|
||||
it('should display message to select projects', () => {
|
||||
getSelectProjectsMessage().should('be.visible');
|
||||
});
|
||||
|
||||
it('should hide select projects message when a project is selected', () => {
|
||||
cy.contains('nx-dev').scrollIntoView().should('be.visible');
|
||||
cy.get('[data-project="nx-dev"]').should('be.visible');
|
||||
cy.get('[data-project="nx-dev"]').click({ force: true });
|
||||
getSelectProjectsMessage().should('not.be.visible');
|
||||
});
|
||||
|
||||
describe('selecting a different project', () => {
|
||||
it('should change the available projects', () => {
|
||||
getProjectItems().should('have.length', 55);
|
||||
cy.get('[data-cy=project-select]').select('Ocean', { force: true });
|
||||
getProjectItems().should('have.length', 124);
|
||||
});
|
||||
});
|
||||
|
||||
describe('select all button', () => {
|
||||
it('should check all project items', () => {
|
||||
getSelectAllButton().scrollIntoView().click({ force: true });
|
||||
getCheckedProjectItems().should('have.length', 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deselect all button', () => {
|
||||
it('should uncheck all project items', () => {
|
||||
getDeselectAllButton().click();
|
||||
getUncheckedProjectItems().should('have.length', 55);
|
||||
getSelectProjectsMessage().should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe('selecting projects', () => {
|
||||
it('should select a project by clicking on the project name', () => {
|
||||
cy.get('[data-project="nx-dev"]').should('have.data', 'active', false);
|
||||
cy.get('[data-project="nx-dev"]').click({
|
||||
force: true,
|
||||
});
|
||||
|
||||
cy.get('[data-project="nx-dev"]').should('have.data', 'active', true);
|
||||
});
|
||||
|
||||
it('should deselect a project by clicking on the project name again', () => {
|
||||
cy.get('[data-project="nx-dev"][data-active="false"]').click({
|
||||
force: true,
|
||||
});
|
||||
cy.get('[data-project="nx-dev"][data-active="true"]')
|
||||
.should('exist')
|
||||
.click({
|
||||
force: true,
|
||||
});
|
||||
cy.get('[data-project="nx-dev"][data-active="false"]').should('exist');
|
||||
});
|
||||
|
||||
it('should select a project by clicking on the selected icon', () => {
|
||||
cy.get('[data-project="nx-dev"][data-active="false"]').click({
|
||||
force: true,
|
||||
});
|
||||
cy.get('[data-project="nx-dev"][data-active="true"]')
|
||||
.should('exist')
|
||||
.parent()
|
||||
.siblings()
|
||||
.first()
|
||||
.should('exist')
|
||||
.click({
|
||||
force: true,
|
||||
});
|
||||
cy.get('[data-project="nx-dev"][data-active="false"]').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('focusing projects in sidebar', () => {
|
||||
it('should select appropriate projects', () => {
|
||||
cy.contains('nx-dev').scrollIntoView().should('be.visible');
|
||||
cy.get('[data-project="nx-dev"]').prev('button').click({ force: true });
|
||||
|
||||
cy.get('[data-project="nx-dev"]').should('have.data', 'active', true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unfocus button', () => {
|
||||
it('should uncheck all project items', () => {
|
||||
cy.get('[data-project="nx-dev"]').prev('button').click({ force: true });
|
||||
getUnfocusProjectButton().click();
|
||||
|
||||
getUncheckedProjectItems().should('have.length', 55);
|
||||
});
|
||||
});
|
||||
|
||||
describe('text filtering', () => {
|
||||
it('should filter projects by text when pressing enter', () => {
|
||||
getTextFilterInput().type('nx-dev{enter}');
|
||||
|
||||
getCheckedProjectItems().should('have.length', 9);
|
||||
});
|
||||
|
||||
it('should include projects in path when option is checked', () => {
|
||||
getTextFilterInput().type('nx-dev');
|
||||
getIncludeProjectsInPathButton().click();
|
||||
|
||||
getCheckedProjectItems().should('have.length', 17);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
const { preprocessTypescript } = require('@nrwl/cypress/plugins/preprocessor');
|
||||
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
// Preprocess Typescript file using Nx helper
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
export const getSelectProjectsMessage = () => cy.get('#no-projects-chosen');
|
||||
export const getGraph = () => cy.get('#graph-container');
|
||||
export const getSelectAllButton = () => cy.get('[data-cy=selectAllButton]');
|
||||
export const getDeselectAllButton = () => cy.get('[data-cy=deselectAllButton]');
|
||||
export const getUnfocusProjectButton = () => cy.get('[data-cy=unfocusButton]');
|
||||
|
||||
export const getProjectItems = () => cy.get('[data-project]');
|
||||
|
||||
export const getCheckedProjectItems = () => cy.get('[data-active="true"]');
|
||||
export const getUncheckedProjectItems = () => cy.get('[data-active="false"]');
|
||||
|
||||
export const getGroupByfolderItems = () =>
|
||||
cy.get('input[name=displayOptions][value=groupByFolder]');
|
||||
|
||||
export const getTextFilterInput = () => cy.get('[data-cy=textFilterInput]');
|
||||
export const getIncludeProjectsInPathButton = () =>
|
||||
cy.get('input[name=textFilterCheckbox]');
|
||||
@@ -0,0 +1,69 @@
|
||||
import { getProjectItems } from '../support/app.po';
|
||||
|
||||
describe('dep-graph-client in watch mode', () => {
|
||||
beforeEach(() => {
|
||||
cy.clock();
|
||||
cy.visit('/');
|
||||
cy.tick(1000);
|
||||
});
|
||||
|
||||
it('should auto-select new libs as they are created', () => {
|
||||
const excludedValues = ['existing-app-1', 'existing-lib-1'];
|
||||
|
||||
cy.tick(5000);
|
||||
checkSelectedProjects(3, excludedValues);
|
||||
|
||||
cy.tick(5000);
|
||||
checkSelectedProjects(4, excludedValues);
|
||||
|
||||
cy.tick(5000);
|
||||
checkSelectedProjects(5, excludedValues);
|
||||
});
|
||||
|
||||
it('should retain selected projects new libs as they are created', () => {
|
||||
cy.contains('existing-app-1').siblings('button').click();
|
||||
cy.contains('existing-lib-1').siblings('button').click();
|
||||
|
||||
cy.tick(5000);
|
||||
|
||||
checkSelectedProjects(3, []);
|
||||
|
||||
cy.tick(5000);
|
||||
checkSelectedProjects(4, []);
|
||||
|
||||
cy.tick(5000);
|
||||
checkSelectedProjects(5, []);
|
||||
});
|
||||
|
||||
it('should not re-add new libs if they were un-selected', () => {
|
||||
cy.tick(5000);
|
||||
cy.get('[data-project*="3"][data-active="true"]')
|
||||
.should('exist')
|
||||
.click({ force: true });
|
||||
|
||||
cy.get('[data-project*="3"][data-active="false"]').should('exist');
|
||||
|
||||
cy.tick(5000);
|
||||
cy.tick(5000);
|
||||
|
||||
cy.get('[data-project*="3"]')
|
||||
.first()
|
||||
.should((project) => {
|
||||
expect(project.data('active')).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function checkSelectedProjects(
|
||||
expectedNumberOfProjects: number,
|
||||
excludedProjects: string[]
|
||||
) {
|
||||
getProjectItems().should((projects) => {
|
||||
expect(projects.length).to.equal(expectedNumberOfProjects);
|
||||
projects.each(function () {
|
||||
if (!excludedProjects.includes(this.dataset.project)) {
|
||||
expect(this.dataset.active).to.eq('true');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"allowJs": true,
|
||||
"types": ["cypress", "node"],
|
||||
"lib": ["DOM"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js"]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.e2e.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["@nrwl/web/babel"]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{ "extends": "../../.eslintrc", "rules": {}, "ignorePatterns": ["!**/*"] }
|
||||
@@ -0,0 +1,13 @@
|
||||
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
#
|
||||
# If you need to support different browsers in production, you may tweak the list below.
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major version
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
@@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
name: 'dep-graph-dep-graph',
|
||||
preset: '../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.[tj]s$': 'ts-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageDirectory: '../../coverage/dep-graph/dep-graph',
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {
|
||||
config: './dep-graph/dep-graph/tailwind.config.js',
|
||||
},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"root": "dep-graph/dep-graph",
|
||||
"sourceRoot": "dep-graph/dep-graph/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"build-base": {
|
||||
"executor": "@nrwl/web:build",
|
||||
"options": {
|
||||
"maxWorkers": 8,
|
||||
"outputPath": "build/apps/dep-graph",
|
||||
"index": "dep-graph/dep-graph/src/index.html",
|
||||
"main": "dep-graph/dep-graph/src/main.ts",
|
||||
"polyfills": "dep-graph/dep-graph/src/polyfills.ts",
|
||||
"tsConfig": "dep-graph/dep-graph/tsconfig.app.json",
|
||||
"styles": ["dep-graph/dep-graph/src/styles.scss"],
|
||||
"scripts": [],
|
||||
"assets": [],
|
||||
"optimization": true,
|
||||
"outputHashing": "none",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"dev": {
|
||||
"fileReplacements": [],
|
||||
"assets": [
|
||||
"dep-graph/dep-graph/src/favicon.ico",
|
||||
"dep-graph/dep-graph/src/assets/graphs/",
|
||||
{
|
||||
"input": "dep-graph/dep-graph/src/assets",
|
||||
"output": "/",
|
||||
"glob": "environment.js"
|
||||
}
|
||||
],
|
||||
"optimization": false,
|
||||
"outputHashing": "none",
|
||||
"sourceMap": true,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": false,
|
||||
"vendorChunk": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"outputs": ["{options.outputPath}"]
|
||||
},
|
||||
"serve-base": {
|
||||
"executor": "@nrwl/web:dev-server",
|
||||
"options": {
|
||||
"buildTarget": "dep-graph-dep-graph:build-base:dev"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:lint",
|
||||
"options": {
|
||||
"linter": "eslint",
|
||||
"tsConfig": [
|
||||
"dep-graph/dep-graph/tsconfig.app.json",
|
||||
"dep-graph/dep-graph/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": ["**/node_modules/**", "!dep-graph/dep-graph/**/*"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["coverage/dep-graph/dep-graph"],
|
||||
"options": {
|
||||
"jestConfig": "dep-graph/dep-graph/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@nrwl/workspace:run-commands",
|
||||
"outputs": [],
|
||||
"options": {
|
||||
"commands": [
|
||||
"npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/copy-dep-graph-environment.ts dev",
|
||||
"nx serve-base dep-graph-dep-graph"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"watch": {
|
||||
"commands": [
|
||||
"npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/copy-dep-graph-environment.ts watch",
|
||||
"nx serve-base dep-graph-dep-graph"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve-for-e2e": {
|
||||
"executor": "@nrwl/workspace:run-commands",
|
||||
"outputs": [],
|
||||
"options": {
|
||||
"commands": [
|
||||
"npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/copy-dep-graph-environment.ts dev",
|
||||
"nx serve-base dep-graph-dep-graph"
|
||||
],
|
||||
"readyWhen": "No issues found."
|
||||
},
|
||||
"configurations": {
|
||||
"watch": {
|
||||
"commands": [
|
||||
"npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/copy-dep-graph-environment.ts watch",
|
||||
"nx serve-base dep-graph-dep-graph"
|
||||
],
|
||||
"readyWhen": "No issues found."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["core"]
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// nx-ignore-next-line
|
||||
import type { DepGraphClientResponse } from '@nrwl/workspace/src/command-line/dep-graph';
|
||||
import { fromEvent } from 'rxjs';
|
||||
import { startWith } from 'rxjs/operators';
|
||||
import { DebuggerPanel } from './debugger-panel';
|
||||
import { GraphComponent } from './graph';
|
||||
import { useDepGraphService } from './machines/dep-graph.service';
|
||||
import { DepGraphSend } from './machines/interfaces';
|
||||
import { AppConfig, DEFAULT_CONFIG, ProjectGraphService } from './models';
|
||||
import { GraphTooltipService } from './tooltip-service';
|
||||
import { SidebarComponent } from './ui-sidebar/sidebar';
|
||||
|
||||
export class AppComponent {
|
||||
private sidebar = new SidebarComponent();
|
||||
private tooltipService = new GraphTooltipService();
|
||||
private graph = new GraphComponent(this.tooltipService);
|
||||
private debuggerPanel: DebuggerPanel;
|
||||
|
||||
private windowResize$ = fromEvent(window, 'resize').pipe(startWith({}));
|
||||
|
||||
private send: DepGraphSend;
|
||||
|
||||
constructor(
|
||||
private config: AppConfig = DEFAULT_CONFIG,
|
||||
private projectGraphService: ProjectGraphService
|
||||
) {
|
||||
const [_, send] = useDepGraphService();
|
||||
this.send = send;
|
||||
|
||||
this.loadProjectGraph(config.defaultProjectGraph);
|
||||
this.render();
|
||||
|
||||
if (window.watch === true) {
|
||||
setInterval(
|
||||
() => this.updateProjectGraph(config.defaultProjectGraph),
|
||||
5000
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async loadProjectGraph(projectGraphId: string) {
|
||||
const projectInfo = this.config.projectGraphs.find(
|
||||
(graph) => graph.id === projectGraphId
|
||||
);
|
||||
|
||||
const project: DepGraphClientResponse =
|
||||
await this.projectGraphService.getProjectGraph(projectInfo.url);
|
||||
|
||||
const workspaceLayout = project?.layout;
|
||||
|
||||
this.send({
|
||||
type: 'initGraph',
|
||||
projects: project.projects,
|
||||
dependencies: project.dependencies,
|
||||
affectedProjects: project.affected,
|
||||
workspaceLayout: workspaceLayout,
|
||||
});
|
||||
|
||||
if (!!window.focusedProject) {
|
||||
this.send({
|
||||
type: 'focusProject',
|
||||
projectName: window.focusedProject,
|
||||
});
|
||||
}
|
||||
|
||||
if (window.groupByFolder) {
|
||||
this.send({
|
||||
type: 'setGroupByFolder',
|
||||
groupByFolder: window.groupByFolder,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async updateProjectGraph(projectGraphId: string) {
|
||||
const projectInfo = this.config.projectGraphs.find(
|
||||
(graph) => graph.id === projectGraphId
|
||||
);
|
||||
|
||||
const project: DepGraphClientResponse =
|
||||
await this.projectGraphService.getProjectGraph(projectInfo.url);
|
||||
|
||||
this.send({
|
||||
type: 'updateGraph',
|
||||
projects: project.projects,
|
||||
dependencies: project.dependencies,
|
||||
});
|
||||
}
|
||||
|
||||
private render() {
|
||||
const debuggerPanelContainer = document.getElementById('debugger-panel');
|
||||
|
||||
if (this.config.showDebugger) {
|
||||
debuggerPanelContainer.hidden = false;
|
||||
debuggerPanelContainer.style.display = 'flex';
|
||||
|
||||
this.debuggerPanel = new DebuggerPanel(
|
||||
debuggerPanelContainer,
|
||||
this.config.projectGraphs,
|
||||
this.config.defaultProjectGraph
|
||||
);
|
||||
|
||||
this.debuggerPanel.selectProject$.subscribe((id) => {
|
||||
this.loadProjectGraph(id);
|
||||
});
|
||||
|
||||
this.graph.renderTimes$.subscribe(
|
||||
(renderTime) => (this.debuggerPanel.renderTime = renderTime)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Subject } from 'rxjs';
|
||||
import { GraphPerfReport } from './graph';
|
||||
import { ProjectGraphList } from './models';
|
||||
import { removeChildrenFromContainer } from './util';
|
||||
|
||||
export class DebuggerPanel {
|
||||
set renderTime(renderTime: GraphPerfReport) {
|
||||
this.renderReportElement.innerHTML = `Last render took ${renderTime.renderTime}ms: <b class="font-mono text-medium">${renderTime.numNodes} nodes</b> | <b class="font-mono text-medium">${renderTime.numEdges} edges</b>.`;
|
||||
}
|
||||
|
||||
private selectProjectSubject = new Subject<string>();
|
||||
|
||||
selectProject$ = this.selectProjectSubject.asObservable();
|
||||
|
||||
private renderReportElement: HTMLElement;
|
||||
|
||||
constructor(
|
||||
private container: HTMLElement,
|
||||
private projectGraphs: ProjectGraphList[],
|
||||
private initialSelectedGraph: string
|
||||
) {
|
||||
this.render();
|
||||
}
|
||||
|
||||
private render() {
|
||||
removeChildrenFromContainer(this.container);
|
||||
|
||||
const header = document.createElement('h4');
|
||||
header.className = 'text-lg font-bold mr-4';
|
||||
header.innerText = `Debugger`;
|
||||
|
||||
const select = document.createElement('select');
|
||||
select.className =
|
||||
'w-auto flex items-center px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white';
|
||||
|
||||
this.projectGraphs.forEach((projectGraph) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = projectGraph.id;
|
||||
option.innerText = projectGraph.label;
|
||||
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
select.value = this.initialSelectedGraph;
|
||||
select.dataset['cy'] = 'project-select';
|
||||
|
||||
select.onchange = (event) =>
|
||||
this.selectProjectSubject.next(
|
||||
(event.currentTarget as HTMLSelectElement).value
|
||||
);
|
||||
|
||||
this.renderReportElement = document.createElement('p');
|
||||
this.renderReportElement.className = 'text-sm';
|
||||
|
||||
this.container.appendChild(header);
|
||||
this.container.appendChild(select);
|
||||
this.container.appendChild(this.renderReportElement);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// nx-ignore-next-line
|
||||
import { DepGraphClientResponse } from '@nrwl/workspace/src/command-line/dep-graph';
|
||||
import { ProjectGraphService } from './models';
|
||||
|
||||
export class FetchProjectGraphService implements ProjectGraphService {
|
||||
async getHash(): Promise<string> {
|
||||
const request = new Request('currentHash', { mode: 'no-cors' });
|
||||
|
||||
const response = await fetch(request);
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async getProjectGraph(url: string): Promise<DepGraphClientResponse> {
|
||||
const request = new Request(url, { mode: 'no-cors' });
|
||||
|
||||
const response = await fetch(request);
|
||||
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
import type {
|
||||
ProjectGraph,
|
||||
ProjectGraphDependency,
|
||||
ProjectGraphNode,
|
||||
} from '@nrwl/devkit';
|
||||
import type { VirtualElement } from '@popperjs/core';
|
||||
import * as cy from 'cytoscape';
|
||||
import cytoscapeDagre from 'cytoscape-dagre';
|
||||
import popper from 'cytoscape-popper';
|
||||
import { Subject } from 'rxjs';
|
||||
import type { Instance } from 'tippy.js';
|
||||
import { useDepGraphService } from './machines/dep-graph.service';
|
||||
import { ProjectNodeToolTip } from './project-node-tooltip';
|
||||
import { edgeStyles, nodeStyles } from './styles-graph';
|
||||
import { GraphTooltipService } from './tooltip-service';
|
||||
import {
|
||||
CytoscapeDagreConfig,
|
||||
ParentNode,
|
||||
ProjectEdge,
|
||||
ProjectNode,
|
||||
} from './util-cytoscape';
|
||||
|
||||
export interface GraphPerfReport {
|
||||
renderTime: number;
|
||||
numNodes: number;
|
||||
numEdges: number;
|
||||
}
|
||||
export class GraphComponent {
|
||||
private graph: cy.Core;
|
||||
private openTooltip: Instance = null;
|
||||
|
||||
private renderTimesSubject = new Subject<GraphPerfReport>();
|
||||
renderTimes$ = this.renderTimesSubject.asObservable();
|
||||
|
||||
private send;
|
||||
constructor(private tooltipService: GraphTooltipService) {
|
||||
cy.use(cytoscapeDagre);
|
||||
cy.use(popper);
|
||||
|
||||
const [state$, send] = useDepGraphService();
|
||||
this.send = send;
|
||||
|
||||
state$.subscribe((state) => {
|
||||
const projects = state.context.selectedProjects.map((projectName) =>
|
||||
state.context.projects.find((project) => project.name === projectName)
|
||||
);
|
||||
this.render(
|
||||
projects,
|
||||
state.context.groupByFolder,
|
||||
state.context.workspaceLayout,
|
||||
state.context.focusedProject,
|
||||
state.context.affectedProjects,
|
||||
state.context.dependencies
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render(
|
||||
selectedProjects: ProjectGraphNode[],
|
||||
groupByFolder: boolean,
|
||||
workspaceLayout,
|
||||
focusedProject: string,
|
||||
affectedProjects: string[],
|
||||
dependencies: Record<string, ProjectGraphDependency[]>
|
||||
) {
|
||||
const time = Date.now();
|
||||
|
||||
if (selectedProjects.length === 0) {
|
||||
document.getElementById('no-projects-chosen').style.display = 'flex';
|
||||
} else {
|
||||
document.getElementById('no-projects-chosen').style.display = 'none';
|
||||
}
|
||||
|
||||
this.tooltipService.hideAll();
|
||||
this.generateCytoscapeLayout(
|
||||
selectedProjects,
|
||||
groupByFolder,
|
||||
workspaceLayout,
|
||||
focusedProject,
|
||||
affectedProjects,
|
||||
dependencies
|
||||
);
|
||||
this.listenForProjectNodeClicks();
|
||||
this.listenForProjectNodeHovers();
|
||||
|
||||
const renderTime = Date.now() - time;
|
||||
|
||||
const report: GraphPerfReport = {
|
||||
renderTime,
|
||||
numNodes: this.graph.nodes().length,
|
||||
numEdges: this.graph.edges().length,
|
||||
};
|
||||
|
||||
this.renderTimesSubject.next(report);
|
||||
}
|
||||
|
||||
private generateCytoscapeLayout(
|
||||
selectedProjects: ProjectGraphNode[],
|
||||
groupByFolder: boolean,
|
||||
workspaceLayout,
|
||||
focusedProject: string,
|
||||
affectedProjects: string[],
|
||||
dependencies: Record<string, ProjectGraphDependency[]>
|
||||
) {
|
||||
const elements = this.createElements(
|
||||
selectedProjects,
|
||||
groupByFolder,
|
||||
workspaceLayout,
|
||||
focusedProject,
|
||||
affectedProjects,
|
||||
dependencies
|
||||
);
|
||||
|
||||
this.graph = cy({
|
||||
container: document.getElementById('graph-container'),
|
||||
elements: [...elements],
|
||||
layout: <CytoscapeDagreConfig>{
|
||||
name: 'dagre',
|
||||
nodeDimensionsIncludeLabels: true,
|
||||
rankSep: 75,
|
||||
edgeSep: 50,
|
||||
ranker: 'network-simplex',
|
||||
},
|
||||
boxSelectionEnabled: false,
|
||||
style: [...nodeStyles, ...edgeStyles],
|
||||
});
|
||||
|
||||
this.graph.on('zoom', () => {
|
||||
if (this.openTooltip) {
|
||||
this.openTooltip.hide();
|
||||
this.openTooltip = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private createElements(
|
||||
selectedProjects: ProjectGraphNode[],
|
||||
groupByFolder: boolean,
|
||||
workspaceLayout: {
|
||||
appsDir: string;
|
||||
libsDir: string;
|
||||
},
|
||||
focusedProject: string,
|
||||
affectedProjects: string[],
|
||||
dependencies: Record<string, ProjectGraphDependency[]>
|
||||
) {
|
||||
let elements: cy.ElementDefinition[] = [];
|
||||
const filteredProjectNames = selectedProjects.map(
|
||||
(project) => project.name
|
||||
);
|
||||
|
||||
const projectNodes: ProjectNode[] = [];
|
||||
const edgeNodes: ProjectEdge[] = [];
|
||||
const parents: Record<
|
||||
string,
|
||||
{ id: string; parentId: string; label: string }
|
||||
> = {};
|
||||
|
||||
selectedProjects.forEach((project) => {
|
||||
const workspaceRoot =
|
||||
project.type === 'app' || project.type === 'e2e'
|
||||
? workspaceLayout.appsDir
|
||||
: workspaceLayout.libsDir;
|
||||
|
||||
const projectNode = new ProjectNode(project, workspaceRoot);
|
||||
projectNode.focused = project.name === focusedProject;
|
||||
projectNode.affected = affectedProjects.includes(project.name);
|
||||
|
||||
projectNodes.push(projectNode);
|
||||
|
||||
dependencies[project.name].forEach((dep) => {
|
||||
if (filteredProjectNames.includes(dep.target)) {
|
||||
const edge = new ProjectEdge(dep);
|
||||
edge.affected =
|
||||
affectedProjects.includes(dep.source) &&
|
||||
affectedProjects.includes(dep.target);
|
||||
edgeNodes.push(edge);
|
||||
}
|
||||
});
|
||||
|
||||
if (groupByFolder) {
|
||||
const ancestors = projectNode.getAncestors();
|
||||
ancestors.forEach((ancestor) => (parents[ancestor.id] = ancestor));
|
||||
}
|
||||
});
|
||||
|
||||
const projectElements = projectNodes.map((projectNode) =>
|
||||
projectNode.getCytoscapeNodeDef(groupByFolder)
|
||||
);
|
||||
|
||||
const edgeElements = edgeNodes.map((edgeNode) =>
|
||||
edgeNode.getCytosacpeNodeDef()
|
||||
);
|
||||
|
||||
elements = projectElements.concat(edgeElements);
|
||||
|
||||
if (groupByFolder) {
|
||||
const parentElements = Object.keys(parents).map((id) =>
|
||||
new ParentNode(parents[id]).getCytoscapeNodeDef()
|
||||
);
|
||||
elements = parentElements.concat(elements);
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
listenForProjectNodeClicks() {
|
||||
this.graph.$('node:childless').on('click', (event) => {
|
||||
const node = event.target;
|
||||
|
||||
let ref: VirtualElement = node.popperRef(); // used only for positioning
|
||||
|
||||
const content = new ProjectNodeToolTip(node).render();
|
||||
|
||||
this.openTooltip = this.tooltipService.open(ref, content);
|
||||
});
|
||||
}
|
||||
|
||||
listenForProjectNodeHovers(): void {
|
||||
this.graph.on('mouseover', (event) => {
|
||||
const node = event.target;
|
||||
if (!node.isNode || !node.isNode() || node.isParent()) return;
|
||||
|
||||
this.graph
|
||||
.elements()
|
||||
.difference(node.outgoers().union(node.incomers()))
|
||||
.not(node)
|
||||
.addClass('transparent');
|
||||
node
|
||||
.addClass('highlight')
|
||||
.outgoers()
|
||||
.union(node.incomers())
|
||||
.addClass('highlight');
|
||||
});
|
||||
this.graph.on('mouseout', (event) => {
|
||||
const node = event.target;
|
||||
if (!node.isNode || !node.isNode() || node.isParent()) return;
|
||||
|
||||
this.graph.elements().removeClass('transparent');
|
||||
node
|
||||
.removeClass('highlight')
|
||||
.outgoers()
|
||||
.union(node.incomers())
|
||||
.removeClass('highlight');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// nx-ignore-next-line
|
||||
import { DepGraphClientResponse } from '@nrwl/workspace/src/command-line/dep-graph';
|
||||
import { ProjectGraphService } from './models';
|
||||
|
||||
export class LocalProjectGraphService implements ProjectGraphService {
|
||||
async getHash(): Promise<string> {
|
||||
return new Promise((resolve) => resolve('some-hash'));
|
||||
}
|
||||
|
||||
async getProjectGraph(url: string): Promise<DepGraphClientResponse> {
|
||||
return new Promise((resolve) => resolve(window.projectGraphResponse));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { assign } from '@xstate/immer';
|
||||
import { DepGraphStateNodeConfig } from './interfaces';
|
||||
|
||||
export const customSelectedStateConfig: DepGraphStateNodeConfig = {
|
||||
on: {
|
||||
updateGraph: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
const existingProjectNames = ctx.projects.map(
|
||||
(project) => project.name
|
||||
);
|
||||
const newProjectNames = event.projects.map((project) => project.name);
|
||||
const selectedProjects = newProjectNames.filter(
|
||||
(projectName) => !existingProjectNames.includes(projectName)
|
||||
);
|
||||
|
||||
ctx.projects = event.projects;
|
||||
ctx.dependencies = event.dependencies;
|
||||
ctx.selectedProjects = [...ctx.selectedProjects, ...selectedProjects];
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,148 @@
|
||||
import { assign } from '@xstate/immer';
|
||||
import { Machine } from 'xstate';
|
||||
import { customSelectedStateConfig } from './custom-selected.state';
|
||||
import { focusedStateConfig } from './focused.state';
|
||||
import { DepGraphContext, DepGraphEvents, DepGraphSchema } from './interfaces';
|
||||
import { textFilteredStateConfig } from './text-filtered.state';
|
||||
import { unselectedStateConfig } from './unselected.state';
|
||||
|
||||
export const initialContext: DepGraphContext = {
|
||||
projects: [],
|
||||
dependencies: {},
|
||||
affectedProjects: [],
|
||||
selectedProjects: [],
|
||||
focusedProject: null,
|
||||
textFilter: '',
|
||||
includePath: false,
|
||||
searchDepth: 1,
|
||||
searchDepthEnabled: false,
|
||||
groupByFolder: false,
|
||||
workspaceLayout: {
|
||||
libsDir: '',
|
||||
appsDir: '',
|
||||
},
|
||||
};
|
||||
|
||||
export const depGraphMachine = Machine<
|
||||
DepGraphContext,
|
||||
DepGraphSchema,
|
||||
DepGraphEvents
|
||||
>(
|
||||
{
|
||||
id: 'DepGraph',
|
||||
initial: 'idle',
|
||||
context: initialContext,
|
||||
states: {
|
||||
idle: {},
|
||||
unselected: unselectedStateConfig,
|
||||
customSelected: customSelectedStateConfig,
|
||||
focused: focusedStateConfig,
|
||||
textFiltered: textFilteredStateConfig,
|
||||
},
|
||||
on: {
|
||||
initGraph: {
|
||||
target: 'unselected',
|
||||
actions: assign((ctx, event) => {
|
||||
ctx.projects = event.projects;
|
||||
ctx.affectedProjects = event.affectedProjects;
|
||||
ctx.dependencies = event.dependencies;
|
||||
ctx.workspaceLayout = event.workspaceLayout;
|
||||
}),
|
||||
},
|
||||
|
||||
selectProject: {
|
||||
target: 'customSelected',
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
ctx.selectedProjects.push(event.projectName);
|
||||
}),
|
||||
],
|
||||
},
|
||||
selectAll: {
|
||||
target: 'customSelected',
|
||||
actions: [
|
||||
assign((ctx) => {
|
||||
ctx.selectedProjects = ctx.projects.map((project) => project.name);
|
||||
}),
|
||||
],
|
||||
},
|
||||
selectAffected: {
|
||||
target: 'customSelected',
|
||||
actions: [
|
||||
assign((ctx) => {
|
||||
ctx.selectedProjects = ctx.affectedProjects;
|
||||
}),
|
||||
],
|
||||
},
|
||||
deselectProject: [
|
||||
{
|
||||
target: 'unselected',
|
||||
cond: 'deselectLastProject',
|
||||
},
|
||||
{
|
||||
target: 'customSelected',
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
const index = ctx.selectedProjects.findIndex(
|
||||
(project) => project === event.projectName
|
||||
);
|
||||
|
||||
ctx.selectedProjects.splice(index, 1);
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
deselectAll: {
|
||||
target: 'unselected',
|
||||
},
|
||||
focusProject: {
|
||||
target: 'focused',
|
||||
},
|
||||
setGroupByFolder: {
|
||||
actions: [
|
||||
assign((ctx, event: any) => {
|
||||
ctx.groupByFolder = event.groupByFolder;
|
||||
}),
|
||||
],
|
||||
},
|
||||
setIncludeProjectsByPath: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
ctx.includePath = event.includeProjectsByPath;
|
||||
}),
|
||||
],
|
||||
},
|
||||
incrementSearchDepth: {
|
||||
actions: [
|
||||
assign((ctx) => {
|
||||
ctx.searchDepth = ctx.searchDepth + 1;
|
||||
}),
|
||||
],
|
||||
},
|
||||
decrementSearchDepth: {
|
||||
actions: [
|
||||
assign((ctx) => {
|
||||
ctx.searchDepth = ctx.searchDepth > 1 ? ctx.searchDepth - 1 : 1;
|
||||
}),
|
||||
],
|
||||
},
|
||||
setSearchDepthEnabled: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
ctx.searchDepthEnabled = event.searchDepthEnabled;
|
||||
}),
|
||||
],
|
||||
},
|
||||
filterByText: {
|
||||
target: 'textFiltered',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
guards: {
|
||||
deselectLastProject: (ctx) => {
|
||||
return ctx.selectedProjects.length <= 1;
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,38 @@
|
||||
import { from } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
import { interpret, Interpreter, Typestate } from 'xstate';
|
||||
import { depGraphMachine } from './dep-graph.machine';
|
||||
import {
|
||||
DepGraphContext,
|
||||
DepGraphEvents,
|
||||
DepGraphSend,
|
||||
DepGraphStateObservable,
|
||||
} from './interfaces';
|
||||
|
||||
let depGraphService: Interpreter<
|
||||
DepGraphContext,
|
||||
any,
|
||||
DepGraphEvents,
|
||||
Typestate<DepGraphContext>
|
||||
>;
|
||||
|
||||
let depGraphState$: DepGraphStateObservable;
|
||||
|
||||
export function useDepGraphService(): [DepGraphStateObservable, DepGraphSend] {
|
||||
if (!depGraphService) {
|
||||
depGraphService = interpret(depGraphMachine, {
|
||||
devTools: !!window.useXstateInspect,
|
||||
});
|
||||
depGraphService.start();
|
||||
|
||||
depGraphState$ = from(depGraphService).pipe(
|
||||
map((state) => ({
|
||||
value: state.value,
|
||||
context: state.context,
|
||||
})),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
return [depGraphState$, depGraphService.send];
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
import { ProjectGraphDependency, ProjectGraphNode } from '@nrwl/devkit';
|
||||
import { depGraphMachine } from './dep-graph.machine';
|
||||
|
||||
export const mockProjects: ProjectGraphNode[] = [
|
||||
{
|
||||
name: 'app1',
|
||||
type: 'app',
|
||||
data: {},
|
||||
},
|
||||
{
|
||||
name: 'app2',
|
||||
type: 'app',
|
||||
data: {},
|
||||
},
|
||||
{
|
||||
name: 'ui-lib',
|
||||
type: 'lib',
|
||||
data: {},
|
||||
},
|
||||
{
|
||||
name: 'feature-lib1',
|
||||
type: 'lib',
|
||||
data: {},
|
||||
},
|
||||
{
|
||||
name: 'feature-lib2',
|
||||
type: 'lib',
|
||||
data: {},
|
||||
},
|
||||
{
|
||||
name: 'auth-lib',
|
||||
type: 'lib',
|
||||
data: {},
|
||||
},
|
||||
];
|
||||
|
||||
export const mockDependencies: Record<string, ProjectGraphDependency[]> = {
|
||||
app1: [
|
||||
{
|
||||
type: 'static',
|
||||
source: 'app1',
|
||||
target: 'auth-lib',
|
||||
},
|
||||
{
|
||||
type: 'static',
|
||||
source: 'app1',
|
||||
target: 'feature-lib1',
|
||||
},
|
||||
],
|
||||
app2: [
|
||||
{
|
||||
type: 'static',
|
||||
source: 'app2',
|
||||
target: 'auth-lib',
|
||||
},
|
||||
{
|
||||
type: 'static',
|
||||
source: 'app2',
|
||||
target: 'feature-lib2',
|
||||
},
|
||||
],
|
||||
'feature-lib1': [
|
||||
{
|
||||
type: 'static',
|
||||
source: 'feature-lib1',
|
||||
target: 'ui-lib',
|
||||
},
|
||||
],
|
||||
'feature-lib2': [
|
||||
{
|
||||
type: 'static',
|
||||
source: 'feature-lib2',
|
||||
target: 'ui-lib',
|
||||
},
|
||||
],
|
||||
'ui-lib': [],
|
||||
};
|
||||
|
||||
describe('dep-graph machine', () => {
|
||||
describe('initGraph', () => {
|
||||
it('should set projects, dependencies, and workspaceLayout', () => {
|
||||
const result = depGraphMachine.transition(depGraphMachine.initialState, {
|
||||
type: 'initGraph',
|
||||
projects: mockProjects,
|
||||
dependencies: mockDependencies,
|
||||
affectedProjects: [],
|
||||
workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
|
||||
});
|
||||
expect(result.context.projects).toEqual(mockProjects);
|
||||
expect(result.context.dependencies).toEqual(mockDependencies);
|
||||
expect(result.context.workspaceLayout).toEqual({
|
||||
appsDir: 'apps',
|
||||
libsDir: 'libs',
|
||||
});
|
||||
});
|
||||
|
||||
it('should start with no projects selected', () => {
|
||||
const result = depGraphMachine.transition(depGraphMachine.initialState, {
|
||||
type: 'initGraph',
|
||||
projects: mockProjects,
|
||||
dependencies: mockDependencies,
|
||||
affectedProjects: [],
|
||||
workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
|
||||
});
|
||||
|
||||
expect(result.value).toEqual('unselected');
|
||||
expect(result.context.selectedProjects).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('selecting projects', () => {
|
||||
it('should select projects', () => {
|
||||
let result = depGraphMachine.transition(depGraphMachine.initialState, {
|
||||
type: 'initGraph',
|
||||
projects: mockProjects,
|
||||
dependencies: mockDependencies,
|
||||
affectedProjects: [],
|
||||
workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'selectProject',
|
||||
projectName: 'app1',
|
||||
});
|
||||
|
||||
expect(result.value).toEqual('customSelected');
|
||||
expect(result.context.selectedProjects).toEqual(['app1']);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'selectProject',
|
||||
projectName: 'app2',
|
||||
});
|
||||
|
||||
expect(result.context.selectedProjects).toEqual(['app1', 'app2']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deselecting projects', () => {
|
||||
it('should deselect projects', () => {
|
||||
let result = depGraphMachine.transition(depGraphMachine.initialState, {
|
||||
type: 'initGraph',
|
||||
projects: mockProjects,
|
||||
dependencies: mockDependencies,
|
||||
affectedProjects: [],
|
||||
workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'selectProject',
|
||||
projectName: 'app1',
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'selectProject',
|
||||
projectName: 'app2',
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'deselectProject',
|
||||
projectName: 'app1',
|
||||
});
|
||||
|
||||
expect(result.value).toEqual('customSelected');
|
||||
expect(result.context.selectedProjects).toEqual(['app2']);
|
||||
});
|
||||
|
||||
it('should go to unselected when last project is deselected', () => {
|
||||
let result = depGraphMachine.transition(depGraphMachine.initialState, {
|
||||
type: 'initGraph',
|
||||
projects: mockProjects,
|
||||
dependencies: mockDependencies,
|
||||
affectedProjects: [],
|
||||
workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'selectProject',
|
||||
projectName: 'app1',
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'selectProject',
|
||||
projectName: 'app2',
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'deselectProject',
|
||||
projectName: 'app1',
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'deselectProject',
|
||||
projectName: 'app2',
|
||||
});
|
||||
|
||||
expect(result.value).toEqual('unselected');
|
||||
expect(result.context.selectedProjects).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('focusing projects', () => {
|
||||
it('should set the focused project', () => {
|
||||
let result = depGraphMachine.transition(depGraphMachine.initialState, {
|
||||
type: 'initGraph',
|
||||
projects: mockProjects,
|
||||
dependencies: mockDependencies,
|
||||
affectedProjects: [],
|
||||
workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'focusProject',
|
||||
projectName: 'app1',
|
||||
});
|
||||
|
||||
expect(result.value).toEqual('focused');
|
||||
expect(result.context.focusedProject).toEqual('app1');
|
||||
});
|
||||
|
||||
it('should select the projects by the focused project', () => {
|
||||
let result = depGraphMachine.transition(depGraphMachine.initialState, {
|
||||
type: 'initGraph',
|
||||
projects: mockProjects,
|
||||
dependencies: mockDependencies,
|
||||
affectedProjects: [],
|
||||
workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'focusProject',
|
||||
projectName: 'app1',
|
||||
});
|
||||
|
||||
expect(result.context.selectedProjects).toEqual([
|
||||
'app1',
|
||||
'ui-lib',
|
||||
'feature-lib1',
|
||||
'auth-lib',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should select no projects on unfocus', () => {
|
||||
let result = depGraphMachine.transition(depGraphMachine.initialState, {
|
||||
type: 'initGraph',
|
||||
projects: mockProjects,
|
||||
dependencies: mockDependencies,
|
||||
affectedProjects: [],
|
||||
workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'focusProject',
|
||||
projectName: 'app1',
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'unfocusProject',
|
||||
});
|
||||
|
||||
expect(result.value).toEqual('unselected');
|
||||
expect(result.context.selectedProjects).toEqual([]);
|
||||
});
|
||||
|
||||
it('should not decrement search depth below 1', () => {
|
||||
let result = depGraphMachine.transition(depGraphMachine.initialState, {
|
||||
type: 'initGraph',
|
||||
projects: mockProjects,
|
||||
dependencies: mockDependencies,
|
||||
affectedProjects: [],
|
||||
workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'focusProject',
|
||||
projectName: 'app1',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(1);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'incrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(2);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'incrementSearchDepth',
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'incrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(4);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'decrementSearchDepth',
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'decrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(2);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'decrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(1);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'decrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(1);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'decrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filtering projects by text', () => {
|
||||
it('should not decrement search depth below 1', () => {
|
||||
let result = depGraphMachine.transition(depGraphMachine.initialState, {
|
||||
type: 'initGraph',
|
||||
projects: mockProjects,
|
||||
dependencies: mockDependencies,
|
||||
affectedProjects: [],
|
||||
workspaceLayout: { appsDir: 'apps', libsDir: 'libs' },
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'filterByText',
|
||||
search: 'app1',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(1);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'incrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(2);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'incrementSearchDepth',
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'incrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(4);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'decrementSearchDepth',
|
||||
});
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'decrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(2);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'decrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(1);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'decrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(1);
|
||||
|
||||
result = depGraphMachine.transition(result, {
|
||||
type: 'decrementSearchDepth',
|
||||
});
|
||||
|
||||
expect(result.context.searchDepth).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
import { assign } from '@xstate/immer';
|
||||
import { selectProjectsForFocusedProject } from '../util';
|
||||
import { DepGraphStateNodeConfig } from './interfaces';
|
||||
|
||||
export const focusedStateConfig: DepGraphStateNodeConfig = {
|
||||
entry: [
|
||||
assign((ctx, event: any) => {
|
||||
ctx.selectedProjects = selectProjectsForFocusedProject(
|
||||
ctx.projects,
|
||||
ctx.dependencies,
|
||||
event.projectName,
|
||||
ctx.searchDepthEnabled ? ctx.searchDepth : -1
|
||||
);
|
||||
|
||||
ctx.focusedProject = event.projectName;
|
||||
}),
|
||||
],
|
||||
exit: [
|
||||
assign((ctx) => {
|
||||
ctx.focusedProject = null;
|
||||
}),
|
||||
],
|
||||
on: {
|
||||
incrementSearchDepth: {
|
||||
actions: [
|
||||
assign((ctx) => {
|
||||
const searchDepth = ctx.searchDepth + 1;
|
||||
const selectedProjects = selectProjectsForFocusedProject(
|
||||
ctx.projects,
|
||||
ctx.dependencies,
|
||||
ctx.focusedProject,
|
||||
ctx.searchDepthEnabled ? searchDepth : -1
|
||||
);
|
||||
|
||||
ctx.selectedProjects = selectedProjects;
|
||||
ctx.searchDepth = searchDepth;
|
||||
}),
|
||||
],
|
||||
},
|
||||
decrementSearchDepth: {
|
||||
actions: [
|
||||
assign((ctx) => {
|
||||
const searchDepth = ctx.searchDepth > 1 ? ctx.searchDepth - 1 : 1;
|
||||
const selectedProjects = selectProjectsForFocusedProject(
|
||||
ctx.projects,
|
||||
ctx.dependencies,
|
||||
ctx.focusedProject,
|
||||
ctx.searchDepthEnabled ? searchDepth : -1
|
||||
);
|
||||
|
||||
ctx.selectedProjects = selectedProjects;
|
||||
ctx.searchDepth = searchDepth;
|
||||
}),
|
||||
],
|
||||
},
|
||||
setSearchDepthEnabled: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
const selectedProjects = selectProjectsForFocusedProject(
|
||||
ctx.projects,
|
||||
ctx.dependencies,
|
||||
ctx.focusedProject,
|
||||
event.searchDepthEnabled ? ctx.searchDepth : -1
|
||||
);
|
||||
|
||||
(ctx.searchDepthEnabled = event.searchDepthEnabled),
|
||||
(ctx.selectedProjects = selectedProjects);
|
||||
}),
|
||||
],
|
||||
},
|
||||
unfocusProject: {
|
||||
target: 'unselected',
|
||||
},
|
||||
updateGraph: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
const selectedProjects = selectProjectsForFocusedProject(
|
||||
event.projects,
|
||||
event.dependencies,
|
||||
ctx.focusedProject,
|
||||
ctx.searchDepthEnabled ? ctx.searchDepth : -1
|
||||
);
|
||||
|
||||
ctx.projects = event.projects;
|
||||
ctx.dependencies = event.dependencies;
|
||||
ctx.selectedProjects = selectedProjects;
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
import { ProjectGraphDependency, ProjectGraphNode } from '@nrwl/devkit';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ActionObject, StateNodeConfig, StateValue } from 'xstate';
|
||||
|
||||
// The hierarchical (recursive) schema for the states
|
||||
export interface DepGraphSchema {
|
||||
states: {
|
||||
idle: {};
|
||||
unselected: {};
|
||||
focused: {};
|
||||
textFiltered: {};
|
||||
customSelected: {};
|
||||
};
|
||||
}
|
||||
|
||||
// The events that the machine handles
|
||||
export type DepGraphEvents =
|
||||
| { type: 'selectProject'; projectName: string }
|
||||
| { type: 'deselectProject'; projectName: string }
|
||||
| { type: 'selectAll' }
|
||||
| { type: 'deselectAll' }
|
||||
| { type: 'selectAffected' }
|
||||
| { type: 'setGroupByFolder'; groupByFolder: boolean }
|
||||
| { type: 'setIncludeProjectsByPath'; includeProjectsByPath: boolean }
|
||||
| { type: 'incrementSearchDepth' }
|
||||
| { type: 'decrementSearchDepth' }
|
||||
| { type: 'setSearchDepthEnabled'; searchDepthEnabled: boolean }
|
||||
| { type: 'focusProject'; projectName: string }
|
||||
| { type: 'unfocusProject' }
|
||||
| { type: 'filterByText'; search: string }
|
||||
| { type: 'clearTextFilter' }
|
||||
| {
|
||||
type: 'initGraph';
|
||||
projects: ProjectGraphNode[];
|
||||
dependencies: Record<string, ProjectGraphDependency[]>;
|
||||
affectedProjects: string[];
|
||||
workspaceLayout: {
|
||||
libsDir: string;
|
||||
appsDir: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: 'updateGraph';
|
||||
projects: ProjectGraphNode[];
|
||||
dependencies: Record<string, ProjectGraphDependency[]>;
|
||||
};
|
||||
|
||||
// The context (extended state) of the machine
|
||||
export interface DepGraphContext {
|
||||
projects: ProjectGraphNode[];
|
||||
dependencies: Record<string, ProjectGraphDependency[]>;
|
||||
affectedProjects: string[];
|
||||
selectedProjects: string[];
|
||||
focusedProject: string | null;
|
||||
textFilter: string;
|
||||
includePath: boolean;
|
||||
searchDepth: number;
|
||||
searchDepthEnabled: boolean;
|
||||
groupByFolder: boolean;
|
||||
workspaceLayout: {
|
||||
libsDir: string;
|
||||
appsDir: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type DepGraphStateNodeConfig = StateNodeConfig<
|
||||
DepGraphContext,
|
||||
{},
|
||||
DepGraphEvents,
|
||||
ActionObject<DepGraphContext, DepGraphEvents>
|
||||
>;
|
||||
|
||||
export type DepGraphSend = (event: DepGraphEvents | DepGraphEvents[]) => void;
|
||||
export type DepGraphStateObservable = Observable<{
|
||||
value: StateValue;
|
||||
context: DepGraphContext;
|
||||
}>;
|
||||
@@ -0,0 +1,103 @@
|
||||
import { assign } from '@xstate/immer';
|
||||
import { filterProjectsByText } from '../util';
|
||||
import { DepGraphStateNodeConfig } from './interfaces';
|
||||
|
||||
export const textFilteredStateConfig: DepGraphStateNodeConfig = {
|
||||
entry: [
|
||||
assign((ctx, event: any) => {
|
||||
ctx.textFilter = event.search;
|
||||
ctx.selectedProjects = filterProjectsByText(
|
||||
event.search,
|
||||
ctx.includePath,
|
||||
ctx.searchDepthEnabled ? ctx.searchDepth : -1,
|
||||
ctx.projects,
|
||||
ctx.dependencies
|
||||
);
|
||||
}),
|
||||
],
|
||||
on: {
|
||||
clearTextFilter: {
|
||||
target: 'unselected',
|
||||
actions: assign((ctx) => {
|
||||
ctx.includePath = false;
|
||||
ctx.textFilter = '';
|
||||
}),
|
||||
},
|
||||
setIncludeProjectsByPath: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
ctx.includePath = event.includeProjectsByPath;
|
||||
ctx.selectedProjects = filterProjectsByText(
|
||||
ctx.textFilter,
|
||||
event.includeProjectsByPath,
|
||||
ctx.searchDepthEnabled ? ctx.searchDepth : -1,
|
||||
ctx.projects,
|
||||
ctx.dependencies
|
||||
);
|
||||
}),
|
||||
],
|
||||
},
|
||||
incrementSearchDepth: {
|
||||
actions: [
|
||||
assign((ctx) => {
|
||||
const searchDepth = ctx.searchDepth + 1;
|
||||
ctx.selectedProjects = filterProjectsByText(
|
||||
ctx.textFilter,
|
||||
ctx.includePath,
|
||||
ctx.searchDepthEnabled ? searchDepth : -1,
|
||||
ctx.projects,
|
||||
ctx.dependencies
|
||||
);
|
||||
|
||||
ctx.searchDepth = searchDepth;
|
||||
}),
|
||||
],
|
||||
},
|
||||
decrementSearchDepth: {
|
||||
actions: [
|
||||
assign((ctx) => {
|
||||
const searchDepth = ctx.searchDepth > 1 ? ctx.searchDepth - 1 : 1;
|
||||
ctx.selectedProjects = filterProjectsByText(
|
||||
ctx.textFilter,
|
||||
ctx.includePath,
|
||||
ctx.searchDepthEnabled ? searchDepth : -1,
|
||||
ctx.projects,
|
||||
ctx.dependencies
|
||||
);
|
||||
|
||||
ctx.searchDepth = searchDepth;
|
||||
}),
|
||||
],
|
||||
},
|
||||
setSearchDepthEnabled: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
ctx.searchDepthEnabled = event.searchDepthEnabled;
|
||||
ctx.selectedProjects = filterProjectsByText(
|
||||
ctx.textFilter,
|
||||
ctx.includePath,
|
||||
event.searchDepthEnabled ? ctx.searchDepth : -1,
|
||||
ctx.projects,
|
||||
ctx.dependencies
|
||||
);
|
||||
}),
|
||||
],
|
||||
},
|
||||
updateGraph: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
ctx.selectedProjects = filterProjectsByText(
|
||||
ctx.textFilter,
|
||||
ctx.includePath,
|
||||
ctx.searchDepthEnabled ? ctx.searchDepth : -1,
|
||||
event.projects,
|
||||
event.dependencies
|
||||
);
|
||||
|
||||
ctx.projects = event.projects;
|
||||
ctx.dependencies = event.dependencies;
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import { assign } from '@xstate/immer';
|
||||
import { DepGraphStateNodeConfig } from './interfaces';
|
||||
|
||||
export const unselectedStateConfig: DepGraphStateNodeConfig = {
|
||||
entry: [
|
||||
assign((ctx) => {
|
||||
ctx.selectedProjects = [];
|
||||
}),
|
||||
],
|
||||
on: {
|
||||
updateGraph: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
const existingProjectNames = ctx.projects.map(
|
||||
(project) => project.name
|
||||
);
|
||||
const newProjectNames = event.projects.map((project) => project.name);
|
||||
const selectedProjects = newProjectNames.filter(
|
||||
(projectName) => !existingProjectNames.includes(projectName)
|
||||
);
|
||||
|
||||
ctx.projects = event.projects;
|
||||
ctx.dependencies = event.dependencies;
|
||||
ctx.selectedProjects = [...ctx.selectedProjects, ...selectedProjects];
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
import { ProjectGraphDependency, ProjectGraphNode } from '@nrwl/devkit';
|
||||
// nx-ignore-next-line
|
||||
import { DepGraphClientResponse } from '@nrwl/workspace/src/command-line/dep-graph';
|
||||
import { ProjectGraphService } from '../app/models';
|
||||
|
||||
export class MockProjectGraphService implements ProjectGraphService {
|
||||
private response: DepGraphClientResponse = {
|
||||
hash: '79054025255fb1a26e4bc422aef54eb4',
|
||||
layout: {
|
||||
appsDir: 'apps',
|
||||
libsDir: 'libs',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'existing-app-1',
|
||||
type: 'app',
|
||||
data: {
|
||||
root: 'apps/app1',
|
||||
tags: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'existing-lib-1',
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/lib1',
|
||||
tags: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
dependencies: {
|
||||
'existing-app-1': [
|
||||
{
|
||||
source: 'existing-app-1',
|
||||
target: 'existing-lib-1',
|
||||
type: 'statis',
|
||||
},
|
||||
],
|
||||
'existing-lib-1': [],
|
||||
},
|
||||
affected: [],
|
||||
focus: null,
|
||||
exclude: [],
|
||||
groupByFolder: false,
|
||||
};
|
||||
|
||||
constructor(updateFrequency: number = 5000) {
|
||||
setInterval(() => this.updateResponse(), updateFrequency);
|
||||
}
|
||||
|
||||
async getHash(): Promise<string> {
|
||||
return new Promise((resolve) => resolve(this.response.hash));
|
||||
}
|
||||
|
||||
getProjectGraph(url: string): Promise<DepGraphClientResponse> {
|
||||
return new Promise((resolve) => resolve(this.response));
|
||||
}
|
||||
|
||||
private createNewProject(): ProjectGraphNode {
|
||||
const type = Math.random() > 0.25 ? 'lib' : 'app';
|
||||
const name = `${type}-${this.response.projects.length + 1}`;
|
||||
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
data: {
|
||||
root: type === 'app' ? `apps/${name}` : `libs/${name}`,
|
||||
tags: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private updateResponse() {
|
||||
const newProject = this.createNewProject();
|
||||
const libProjects = this.response.projects.filter(
|
||||
(project) => project.type === 'lib'
|
||||
);
|
||||
|
||||
const targetDependency =
|
||||
libProjects[Math.floor(Math.random() * libProjects.length)];
|
||||
const newDependency: ProjectGraphDependency[] = [
|
||||
{
|
||||
source: newProject.name,
|
||||
target: targetDependency.name,
|
||||
type: 'static',
|
||||
},
|
||||
];
|
||||
|
||||
this.response = {
|
||||
...this.response,
|
||||
projects: [...this.response.projects, newProject],
|
||||
dependencies: {
|
||||
...this.response.dependencies,
|
||||
[newProject.name]: newDependency,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// nx-ignore-next-line
|
||||
import { DepGraphClientResponse } from '@nrwl/workspace/src/command-line/dep-graph';
|
||||
|
||||
export interface ProjectGraphList {
|
||||
id: string;
|
||||
label: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface WorkspaceLayout {
|
||||
libsDir: string;
|
||||
appsDir: string;
|
||||
}
|
||||
|
||||
export interface ProjectGraphService {
|
||||
getHash: () => Promise<string>;
|
||||
getProjectGraph: (url: string) => Promise<DepGraphClientResponse>;
|
||||
}
|
||||
export interface Environment {
|
||||
environment: 'dev' | 'watch' | 'release';
|
||||
}
|
||||
|
||||
export interface AppConfig {
|
||||
showDebugger: boolean;
|
||||
projectGraphs: ProjectGraphList[];
|
||||
defaultProjectGraph: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_CONFIG: AppConfig = {
|
||||
showDebugger: false,
|
||||
projectGraphs: [],
|
||||
defaultProjectGraph: null,
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
import * as cy from 'cytoscape';
|
||||
import { useDepGraphService } from './machines/dep-graph.service';
|
||||
|
||||
export class ProjectNodeToolTip {
|
||||
constructor(private node: cy.NodeSingular) {}
|
||||
|
||||
render() {
|
||||
const wrapper = document.createElement('div');
|
||||
const header = this.createHeader();
|
||||
const tags = this.createTags();
|
||||
const buttons = this.createButtons();
|
||||
|
||||
wrapper.appendChild(header);
|
||||
wrapper.appendChild(tags);
|
||||
wrapper.appendChild(buttons);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private createHeader() {
|
||||
const header = document.createElement('h4');
|
||||
const typeLabel = document.createElement('span');
|
||||
const projectName = document.createTextNode(this.node.attr('id'));
|
||||
|
||||
typeLabel.classList.add('tag');
|
||||
typeLabel.innerText = this.node.attr('type');
|
||||
|
||||
header.appendChild(typeLabel);
|
||||
header.appendChild(projectName);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private createTags() {
|
||||
const wrapper = document.createElement('p');
|
||||
const tagLabel = document.createElement('strong');
|
||||
const tags = document.createTextNode(
|
||||
this.node.attr('tags')?.join(', ') ?? ''
|
||||
);
|
||||
|
||||
tagLabel.innerText = 'tags';
|
||||
|
||||
wrapper.appendChild(tagLabel);
|
||||
wrapper.appendChild(document.createElement('br'));
|
||||
wrapper.appendChild(tags);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private createButtons() {
|
||||
const wrapper = document.createElement('div');
|
||||
const focusButton = document.createElement('button');
|
||||
const excludeButton = document.createElement('button');
|
||||
|
||||
wrapper.classList.add('flex');
|
||||
|
||||
const [_, send] = useDepGraphService();
|
||||
|
||||
focusButton.addEventListener('click', () =>
|
||||
send({ type: 'focusProject', projectName: this.node.attr('id') })
|
||||
);
|
||||
focusButton.innerText = 'Focus';
|
||||
|
||||
excludeButton.addEventListener('click', () => {
|
||||
send({ type: 'deselectProject', projectName: this.node.attr('id') });
|
||||
});
|
||||
|
||||
excludeButton.innerText = 'Exclude';
|
||||
|
||||
wrapper.appendChild(focusButton);
|
||||
wrapper.appendChild(excludeButton);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Stylesheet } from 'cytoscape';
|
||||
import { NrwlPalette } from './palette';
|
||||
|
||||
const allEdges: Stylesheet = {
|
||||
selector: 'edge',
|
||||
style: {
|
||||
width: '1px',
|
||||
'line-color': NrwlPalette.black,
|
||||
'curve-style': 'unbundled-bezier',
|
||||
'target-arrow-shape': 'triangle',
|
||||
'target-arrow-fill': 'filled',
|
||||
'target-arrow-color': NrwlPalette.black,
|
||||
},
|
||||
};
|
||||
|
||||
const affectedEdges: Stylesheet = {
|
||||
selector: 'edge.affected',
|
||||
style: {
|
||||
'line-color': NrwlPalette.red,
|
||||
'target-arrow-color': NrwlPalette.red,
|
||||
'curve-style': 'unbundled-bezier',
|
||||
},
|
||||
};
|
||||
|
||||
const implicitEdges: Stylesheet = {
|
||||
selector: 'edge.implicit',
|
||||
style: {
|
||||
label: 'implicit',
|
||||
'font-size': '16px',
|
||||
'edge-text-rotation': 'autorotate',
|
||||
'curve-style': 'unbundled-bezier',
|
||||
},
|
||||
};
|
||||
|
||||
const dynamicEdges: Stylesheet = {
|
||||
selector: 'edge.dynamic',
|
||||
style: {
|
||||
'line-dash-pattern': [5, 5],
|
||||
'line-style': 'dashed',
|
||||
'curve-style': 'unbundled-bezier',
|
||||
},
|
||||
};
|
||||
|
||||
export const edgeStyles: Stylesheet[] = [
|
||||
allEdges,
|
||||
affectedEdges,
|
||||
implicitEdges,
|
||||
dynamicEdges,
|
||||
];
|
||||
@@ -0,0 +1,126 @@
|
||||
import { Stylesheet } from 'cytoscape';
|
||||
import { FONTS } from './fonts';
|
||||
import { NrwlPalette } from './palette';
|
||||
|
||||
const allNodes: Stylesheet = {
|
||||
selector: 'node',
|
||||
style: {
|
||||
'font-size': '32px',
|
||||
'font-family': FONTS,
|
||||
'border-style': 'solid',
|
||||
'border-color': NrwlPalette.darkGray,
|
||||
'border-width': '1px',
|
||||
'text-halign': 'center',
|
||||
'text-valign': 'center',
|
||||
'padding-left': '16px',
|
||||
color: NrwlPalette.black,
|
||||
label: 'data(id)',
|
||||
width: 'label',
|
||||
backgroundColor: NrwlPalette.white,
|
||||
'transition-property':
|
||||
'background-color, border-color, line-color, target-arrow-color',
|
||||
'transition-duration': 250,
|
||||
'transition-timing-function': 'ease-out',
|
||||
},
|
||||
};
|
||||
|
||||
const appNodes: Stylesheet = {
|
||||
selector: 'node[type="app"]',
|
||||
style: {
|
||||
shape: 'round-rectangle',
|
||||
},
|
||||
};
|
||||
|
||||
const libNodes: Stylesheet = {
|
||||
selector: 'node[type="lib"]',
|
||||
style: {
|
||||
shape: 'round-rectangle',
|
||||
},
|
||||
};
|
||||
|
||||
const e2eNodes: Stylesheet = {
|
||||
selector: 'node[type="e2e"]',
|
||||
style: {
|
||||
shape: 'round-rectangle',
|
||||
},
|
||||
};
|
||||
|
||||
const focusedNodes: Stylesheet = {
|
||||
selector: 'node.focused',
|
||||
style: {
|
||||
color: NrwlPalette.white,
|
||||
'border-color': NrwlPalette.gray,
|
||||
backgroundColor: NrwlPalette.green,
|
||||
},
|
||||
};
|
||||
|
||||
const affectedNodes: Stylesheet = {
|
||||
selector: 'node.affected',
|
||||
style: {
|
||||
color: NrwlPalette.white,
|
||||
'border-color': NrwlPalette.gray,
|
||||
backgroundColor: NrwlPalette.red,
|
||||
},
|
||||
};
|
||||
|
||||
const parentNodes: Stylesheet = {
|
||||
selector: ':parent',
|
||||
style: {
|
||||
'background-opacity': 0.5,
|
||||
'background-color': NrwlPalette.gray,
|
||||
'border-color': NrwlPalette.darkGray,
|
||||
label: 'data(label)',
|
||||
'text-halign': 'center',
|
||||
'text-valign': 'top',
|
||||
'font-weight': 'bold',
|
||||
'font-size': '48px',
|
||||
},
|
||||
};
|
||||
|
||||
const highlightedNodes: Stylesheet = {
|
||||
selector: 'node.highlight',
|
||||
style: {
|
||||
color: NrwlPalette.white,
|
||||
'border-color': NrwlPalette.gray,
|
||||
backgroundColor: NrwlPalette.blue,
|
||||
},
|
||||
};
|
||||
|
||||
const transparentProjectNodes: Stylesheet = {
|
||||
selector: 'node.transparent:childless',
|
||||
style: { opacity: 0.5 },
|
||||
};
|
||||
|
||||
const transparentParentNodes: Stylesheet = {
|
||||
selector: 'node.transparent:parent',
|
||||
style: {
|
||||
'text-opacity': 0.5,
|
||||
'background-opacity': 0.25,
|
||||
'border-opacity': 0.5,
|
||||
},
|
||||
};
|
||||
|
||||
const highlightedEdges: Stylesheet = {
|
||||
selector: 'edge.highlight',
|
||||
style: { 'mid-target-arrow-color': NrwlPalette.blue },
|
||||
};
|
||||
|
||||
const transparentEdges: Stylesheet = {
|
||||
selector: 'edge.transparent',
|
||||
style: { opacity: 0.2 },
|
||||
};
|
||||
|
||||
export const nodeStyles = [
|
||||
allNodes,
|
||||
appNodes,
|
||||
libNodes,
|
||||
e2eNodes,
|
||||
focusedNodes,
|
||||
affectedNodes,
|
||||
parentNodes,
|
||||
highlightedNodes,
|
||||
transparentProjectNodes,
|
||||
transparentParentNodes,
|
||||
highlightedEdges,
|
||||
transparentEdges,
|
||||
];
|
||||
@@ -0,0 +1,10 @@
|
||||
export enum NrwlPalette {
|
||||
blue = 'hsla(214, 62%, 21%, 1)',
|
||||
green = 'hsla(162, 47%, 50%, 1)',
|
||||
lightBlue = 'hsla(192, 75%, 59%, 1)',
|
||||
gray = 'hsla(0, 0%, 92%, 1)',
|
||||
darkGray = 'hsla(0, 0%, 72%, 1)',
|
||||
black = 'hsla(220, 9%, 46%, 1)',
|
||||
red = 'hsla(347, 92%, 65%, 1)',
|
||||
white = '#fff',
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { VirtualElement } from '@popperjs/core';
|
||||
import tippy, { Instance, hideAll } from 'tippy.js';
|
||||
|
||||
export class GraphTooltipService {
|
||||
open(ref: VirtualElement, tooltipContent: HTMLElement): Instance {
|
||||
let instance = tippy(document.createElement('div'), {
|
||||
trigger: 'manual',
|
||||
theme: 'nx',
|
||||
interactive: true,
|
||||
appendTo: document.body,
|
||||
content: tooltipContent,
|
||||
getReferenceClientRect: ref.getBoundingClientRect,
|
||||
});
|
||||
|
||||
instance.show();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
hideAll() {
|
||||
hideAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import { useDepGraphService } from '../machines/dep-graph.service';
|
||||
import { DepGraphSend } from '../machines/interfaces';
|
||||
import { removeChildrenFromContainer } from '../util';
|
||||
|
||||
export class DisplayOptionsPanel {
|
||||
searchDepthDisplay: HTMLSpanElement;
|
||||
affectedButtonElement: HTMLElement;
|
||||
groupByFolderCheckboxElement: HTMLInputElement;
|
||||
|
||||
send: DepGraphSend;
|
||||
|
||||
constructor(private container: HTMLElement) {
|
||||
const [state$, send] = useDepGraphService();
|
||||
this.send = send;
|
||||
this.render();
|
||||
|
||||
state$.subscribe((state) => {
|
||||
if (state.context.affectedProjects.length > 0) {
|
||||
this.affectedButtonElement.classList.remove('hidden');
|
||||
this.affectedButtonElement.addEventListener('click', () =>
|
||||
this.send({ type: 'selectAffected' })
|
||||
);
|
||||
}
|
||||
|
||||
this.searchDepthDisplay.innerText = state.context.searchDepth.toString();
|
||||
|
||||
if (
|
||||
this.groupByFolderCheckboxElement.checked !==
|
||||
state.context.groupByFolder
|
||||
) {
|
||||
this.groupByFolderCheckboxElement.checked = state.context.groupByFolder;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static renderHtmlTemplate(): HTMLElement {
|
||||
const render = document.createElement('template');
|
||||
render.innerHTML = `
|
||||
<div>
|
||||
<div class="mt-8 px-4">
|
||||
<button type="button" class="w-full flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50" data-cy="selectAllButton">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="-ml-1 mr-2 h-5 w-5 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
Show all projects
|
||||
</button>
|
||||
<button type="button" class="mt-3 w-full flex items-center px-4 py-2 border border-red-300 rounded-md shadow-sm text-sm font-medium text-red-500 bg-white hover:bg-red-50 hidden" data-cy="affectedButton">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="-ml-1 mr-2 h-5 w-5 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
Show affected projects
|
||||
</button>
|
||||
<button type="button" class="mt-3 w-full flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50" data-cy="deselectAllButton">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="-ml-1 mr-2 h-5 w-5 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
|
||||
</svg>
|
||||
Hide all projects
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 px-4">
|
||||
<div class="flex items-start">
|
||||
<div class="flex items-center h-5">
|
||||
<input id="displayOptions" name="displayOptions" value="groupByFolder" type="checkbox" class="h-4 w-4 border-gray-300 rounded">
|
||||
</div>
|
||||
<div class="ml-3 text-sm">
|
||||
<label for="displayOptions" class="cursor-pointer font-medium text-gray-700">Group by folder</label>
|
||||
<p class="text-gray-500">Visually arrange libraries by folders with different colors.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 px-4">
|
||||
<div class="mt-4 flex items-start">
|
||||
<div class="flex items-center h-5">
|
||||
<input id="depthFilter" name="depthFilter" value="groupByFolder" type="checkbox" class="h-4 w-4 border-gray-300 rounded">
|
||||
</div>
|
||||
<div class="ml-3 text-sm">
|
||||
<label for="depthFilter" class="cursor-pointer font-medium text-gray-700">Activate proximity</label>
|
||||
<p class="text-gray-500">Explore connected libraries step by step.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 px-10">
|
||||
<div class="flex rounded-md shadow-sm text-gray-500">
|
||||
<button id="depthFilterDecrement" title="Remove ancestor level" class="inline-flex items-center py-2 px-4 rounded-l-md border border-gray-300 bg-gray-50 text-gray-500 hover:bg-gray-100">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4" />
|
||||
</svg>
|
||||
</button>
|
||||
<span id="depthFilterValue" class="p-1.5 bg-white flex-1 block w-full rounded-none border-t border-b border-gray-300 text-center font-mono">1</span>
|
||||
<button id="depthFilterIncrement" title="Add ancestor level" class="inline-flex items-center py-2 px-4 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 hover:bg-gray-100">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`.trim();
|
||||
return render.content.firstChild as HTMLElement;
|
||||
}
|
||||
|
||||
private render() {
|
||||
removeChildrenFromContainer(this.container);
|
||||
|
||||
const element = DisplayOptionsPanel.renderHtmlTemplate();
|
||||
|
||||
this.affectedButtonElement = element.querySelector(
|
||||
'[data-cy="affectedButton"]'
|
||||
);
|
||||
|
||||
const selectAllButtonElement: HTMLElement = element.querySelector(
|
||||
'[data-cy="selectAllButton"]'
|
||||
);
|
||||
selectAllButtonElement.addEventListener('click', () => {
|
||||
this.send({ type: 'selectAll' });
|
||||
});
|
||||
|
||||
const deselectAllButtonElement: HTMLElement = element.querySelector(
|
||||
'[data-cy="deselectAllButton"]'
|
||||
);
|
||||
deselectAllButtonElement.addEventListener('click', () => {
|
||||
this.send({ type: 'deselectAll' });
|
||||
});
|
||||
|
||||
this.groupByFolderCheckboxElement =
|
||||
element.querySelector('#displayOptions');
|
||||
|
||||
this.groupByFolderCheckboxElement.addEventListener(
|
||||
'change',
|
||||
(event: InputEvent) =>
|
||||
this.send({
|
||||
type: 'setGroupByFolder',
|
||||
groupByFolder: (event.target as HTMLInputElement).checked,
|
||||
})
|
||||
);
|
||||
|
||||
this.searchDepthDisplay = element.querySelector('#depthFilterValue');
|
||||
const incrementButtonElement: HTMLInputElement = element.querySelector(
|
||||
'#depthFilterIncrement'
|
||||
);
|
||||
const decrementButtonElement: HTMLInputElement = element.querySelector(
|
||||
'#depthFilterDecrement'
|
||||
);
|
||||
const searchDepthEnabledElement: HTMLInputElement =
|
||||
element.querySelector('#depthFilter');
|
||||
|
||||
incrementButtonElement.addEventListener('click', () => {
|
||||
this.send({ type: 'incrementSearchDepth' });
|
||||
});
|
||||
decrementButtonElement.addEventListener('click', () => {
|
||||
this.send({ type: 'decrementSearchDepth' });
|
||||
});
|
||||
|
||||
searchDepthEnabledElement.addEventListener('change', (event: InputEvent) =>
|
||||
this.send({
|
||||
type: 'setSearchDepthEnabled',
|
||||
searchDepthEnabled: (<HTMLInputElement>event.target).checked,
|
||||
})
|
||||
);
|
||||
|
||||
this.container.appendChild(element);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { map } from 'rxjs/operators';
|
||||
import { useDepGraphService } from '../machines/dep-graph.service';
|
||||
import { DepGraphSend } from '../machines/interfaces';
|
||||
import { removeChildrenFromContainer } from '../util';
|
||||
|
||||
export class FocusedProjectPanel {
|
||||
private send: DepGraphSend;
|
||||
|
||||
constructor(private container: HTMLElement) {
|
||||
const [state$, send] = useDepGraphService();
|
||||
this.send = send;
|
||||
|
||||
state$
|
||||
.pipe(map(({ context }) => context.focusedProject))
|
||||
.subscribe((focusedProject) => this.render(focusedProject));
|
||||
}
|
||||
|
||||
private static renderHtmlTemplate(): HTMLElement {
|
||||
const render = document.createElement('template');
|
||||
render.innerHTML = `
|
||||
<div class="mt-10 px-4">
|
||||
<div class="p-2 shadow-sm bg-green-nx-base text-gray-50 border border-gray-200 rounded-md flex items-center group relative cursor-pointer overflow-hidden" data-cy="unfocusButton">
|
||||
<p class="truncate transition duration-200 ease-in-out group-hover:opacity-60">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 inline -mt-1 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 9l3 3m0 0l-3 3m3-3H8m13 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span id="focused-project-name">e2e-some-other-very-long-project-name</span>
|
||||
</p>
|
||||
<div class="absolute right-2 flex transition-all translate-x-32 transition duration-200 ease-in-out group-hover:translate-x-0 pl-2 rounded-md text-gray-700 items-center text-sm font-medium bg-white shadow-sm ring-1 ring-gray-500">
|
||||
Reset
|
||||
<span class="p-1 rounded-md">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`.trim();
|
||||
return render.content.firstChild as HTMLElement;
|
||||
}
|
||||
|
||||
private render(projectName?: string) {
|
||||
removeChildrenFromContainer(this.container);
|
||||
|
||||
const element = FocusedProjectPanel.renderHtmlTemplate();
|
||||
const projectNameElement: HTMLElement = element.querySelector(
|
||||
'#focused-project-name'
|
||||
);
|
||||
const unfocusButtonElement = element.querySelector(
|
||||
'[data-cy="unfocusButton"]'
|
||||
);
|
||||
|
||||
if (projectName && projectName !== '') {
|
||||
projectNameElement.innerText = `Focused on ${projectName}`;
|
||||
this.container.hidden = false;
|
||||
} else {
|
||||
this.container.hidden = true;
|
||||
}
|
||||
|
||||
unfocusButtonElement.addEventListener('click', () =>
|
||||
this.send({ type: 'unfocusProject' })
|
||||
);
|
||||
|
||||
this.container.appendChild(element);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
import type { ProjectGraphNode } from '@nrwl/devkit';
|
||||
import { useDepGraphService } from '../machines/dep-graph.service';
|
||||
import { DepGraphSend } from '../machines/interfaces';
|
||||
import {
|
||||
parseParentDirectoriesFromPilePath,
|
||||
removeChildrenFromContainer,
|
||||
} from '../util';
|
||||
|
||||
export class ProjectList {
|
||||
private projectItems: Record<string, HTMLElement> = {};
|
||||
|
||||
private send: DepGraphSend;
|
||||
|
||||
constructor(private container: HTMLElement) {
|
||||
const [state$, send] = useDepGraphService();
|
||||
this.send = send;
|
||||
|
||||
state$.subscribe((state) => {
|
||||
this.render(state.context.projects, state.context.workspaceLayout);
|
||||
this.setSelectedProjects(state.context.selectedProjects);
|
||||
});
|
||||
}
|
||||
|
||||
private static renderHtmlItemTemplate(): HTMLElement {
|
||||
const render = document.createElement('template');
|
||||
render.innerHTML = `
|
||||
<li class="text-xs text-gray-600 block cursor-default select-none relative py-1 pl-3 pr-9">
|
||||
<div class="flex items-center">
|
||||
<button type="button" class="flex rounded-md" title="Focus on this library">
|
||||
<span class="p-1 rounded-md flex items-center font-medium bg-white transition hover:bg-gray-50 shadow-sm ring-1 ring-gray-200">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2h-1.528A6 6 0 004 9.528V4z" />
|
||||
<path fill-rule="evenodd" d="M8 10a4 4 0 00-3.446 6.032l-1.261 1.26a1 1 0 101.414 1.415l1.261-1.261A4 4 0 108 10zm-2 4a2 2 0 114 0 2 2 0 01-4 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<label class="font-mono font-normal ml-3 p-2 transition hover:bg-gray-50 cursor-pointer block rounded-md truncate w-full" data-project="project-name" data-active="false">
|
||||
project-name
|
||||
</label>
|
||||
</div>
|
||||
<span role="selection-icon" title="This library is visible" class="text-green-nx-base absolute inset-y-0 right-0 flex items-center cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
`.trim();
|
||||
return render.content.firstChild as HTMLElement;
|
||||
}
|
||||
|
||||
setSelectedProjects(selectedProjects: string[]) {
|
||||
Object.keys(this.projectItems).forEach((projectName) => {
|
||||
this.projectItems[projectName].dataset['active'] = selectedProjects
|
||||
.includes(projectName)
|
||||
.toString();
|
||||
this.projectItems[projectName].dispatchEvent(new CustomEvent('change'));
|
||||
});
|
||||
}
|
||||
|
||||
checkAllProjects() {
|
||||
this.send({ type: 'selectAll' });
|
||||
}
|
||||
|
||||
uncheckAllProjects() {
|
||||
this.send({ type: 'deselectAll' });
|
||||
}
|
||||
|
||||
uncheckProject(projectName: string) {
|
||||
this.send({ type: 'deselectProject', projectName });
|
||||
}
|
||||
|
||||
private render(
|
||||
projects: ProjectGraphNode[],
|
||||
workspaceLayout: { appsDir: string; libsDir: string }
|
||||
) {
|
||||
removeChildrenFromContainer(this.container);
|
||||
|
||||
const appProjects = this.getProjectsByType('app', projects);
|
||||
const libProjects = this.getProjectsByType('lib', projects);
|
||||
const e2eProjects = this.getProjectsByType('e2e', projects);
|
||||
|
||||
const appDirectoryGroups = this.groupProjectsByDirectory(
|
||||
appProjects,
|
||||
workspaceLayout
|
||||
);
|
||||
const libDirectoryGroups = this.groupProjectsByDirectory(
|
||||
libProjects,
|
||||
workspaceLayout
|
||||
);
|
||||
const e2eDirectoryGroups = this.groupProjectsByDirectory(
|
||||
e2eProjects,
|
||||
workspaceLayout
|
||||
);
|
||||
|
||||
const sortedAppDirectories = Object.keys(appDirectoryGroups).sort();
|
||||
const sortedLibDirectories = Object.keys(libDirectoryGroups).sort();
|
||||
const sortedE2EDirectories = Object.keys(e2eDirectoryGroups).sort();
|
||||
|
||||
const appsHeader = document.createElement('h2');
|
||||
appsHeader.className =
|
||||
'mt-8 text-lg font-bold border-b border-gray-50 border-solid';
|
||||
appsHeader.textContent = 'App projects';
|
||||
this.container.append(appsHeader);
|
||||
|
||||
sortedAppDirectories.forEach((directoryName) => {
|
||||
this.createProjectList(directoryName, appDirectoryGroups[directoryName]);
|
||||
});
|
||||
|
||||
const e2eHeader = document.createElement('h2');
|
||||
e2eHeader.className =
|
||||
'mt-8 text-lg font-bold border-b border-gray-50 border-solid';
|
||||
e2eHeader.textContent = 'E2E projects';
|
||||
this.container.append(e2eHeader);
|
||||
|
||||
sortedE2EDirectories.forEach((directoryName) => {
|
||||
this.createProjectList(directoryName, e2eDirectoryGroups[directoryName]);
|
||||
});
|
||||
|
||||
const libHeader = document.createElement('h2');
|
||||
libHeader.className =
|
||||
'mt-8 text-lg font-bold border-b border-gray-50 border-solid';
|
||||
libHeader.textContent = 'Lib projects';
|
||||
this.container.append(libHeader);
|
||||
|
||||
sortedLibDirectories.forEach((directoryName) => {
|
||||
this.createProjectList(directoryName, libDirectoryGroups[directoryName]);
|
||||
});
|
||||
}
|
||||
|
||||
private getProjectsByType(type: string, projects: ProjectGraphNode[]) {
|
||||
return projects
|
||||
.filter((project) => project.type === type)
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
private groupProjectsByDirectory(
|
||||
projects: ProjectGraphNode[],
|
||||
workspaceLayout: { appsDir: string; libsDir: string }
|
||||
) {
|
||||
let groups = {};
|
||||
|
||||
projects.forEach((project) => {
|
||||
const workspaceRoot =
|
||||
project.type === 'app' || project.type === 'e2e'
|
||||
? workspaceLayout.appsDir
|
||||
: workspaceLayout.libsDir;
|
||||
const directories = parseParentDirectoriesFromPilePath(
|
||||
project.data.root,
|
||||
workspaceRoot
|
||||
);
|
||||
const directory = directories.join('/');
|
||||
|
||||
if (!groups.hasOwnProperty(directory)) {
|
||||
groups[directory] = [];
|
||||
}
|
||||
groups[directory].push(project);
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
private createProjectList(headerText, projects) {
|
||||
const header = document.createElement('h3');
|
||||
header.className =
|
||||
'mt-4 py-2 uppercase tracking-wide font-semibold text-sm lg:text-xs text-gray-900 cursor-text';
|
||||
header.textContent = headerText;
|
||||
|
||||
const formGroup = document.createElement('ul');
|
||||
formGroup.className = 'mt-2 -ml-3';
|
||||
|
||||
let sortedProjects = [...projects];
|
||||
sortedProjects.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
projects.forEach((project) => {
|
||||
const element = ProjectList.renderHtmlItemTemplate();
|
||||
const selectedIconElement: HTMLElement = element.querySelector(
|
||||
'span[role="selection-icon"]'
|
||||
);
|
||||
const focusButtonElement: HTMLElement = element.querySelector('button');
|
||||
focusButtonElement.addEventListener('click', () =>
|
||||
this.send({ type: 'focusProject', projectName: project.name })
|
||||
);
|
||||
|
||||
const projectNameElement: HTMLElement = element.querySelector('label');
|
||||
projectNameElement.innerText = project.name;
|
||||
projectNameElement.dataset['project'] = project.name;
|
||||
projectNameElement.dataset['active'] = 'false';
|
||||
selectedIconElement.classList.add('hidden');
|
||||
|
||||
projectNameElement.addEventListener('click', (event) => {
|
||||
const el = event.target as HTMLElement;
|
||||
if (el.dataset['active'] === 'true') {
|
||||
this.send({
|
||||
type: 'deselectProject',
|
||||
projectName: el.dataset['project'],
|
||||
});
|
||||
} else {
|
||||
this.send({
|
||||
type: 'selectProject',
|
||||
projectName: el.dataset['project'],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
projectNameElement.addEventListener('change', (event) => {
|
||||
const el = event.target as HTMLElement;
|
||||
if (el.dataset['active'] === 'false') {
|
||||
selectedIconElement.classList.add('hidden');
|
||||
} else selectedIconElement.classList.remove('hidden');
|
||||
});
|
||||
|
||||
selectedIconElement.addEventListener('click', () => {
|
||||
projectNameElement.dispatchEvent(new Event('click'));
|
||||
});
|
||||
|
||||
this.projectItems[project.name] = projectNameElement;
|
||||
|
||||
formGroup.append(element);
|
||||
});
|
||||
|
||||
this.container.append(header);
|
||||
this.container.append(formGroup);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { DisplayOptionsPanel } from './display-options-panel';
|
||||
import { FocusedProjectPanel } from './focused-project-panel';
|
||||
import { ProjectList } from './project-list';
|
||||
import { TextFilterPanel } from './text-filter-panel';
|
||||
|
||||
declare var ResizeObserver;
|
||||
|
||||
export class SidebarComponent {
|
||||
private displayOptionsPanel: DisplayOptionsPanel;
|
||||
private focusedProjectPanel: FocusedProjectPanel;
|
||||
private textFilterPanel: TextFilterPanel;
|
||||
private projectList: ProjectList;
|
||||
|
||||
constructor() {
|
||||
const displayOptionsPanelContainer = document.getElementById(
|
||||
'display-options-panel'
|
||||
);
|
||||
|
||||
this.displayOptionsPanel = new DisplayOptionsPanel(
|
||||
displayOptionsPanelContainer
|
||||
);
|
||||
|
||||
const focusedProjectPanelContainer =
|
||||
document.getElementById('focused-project');
|
||||
|
||||
this.focusedProjectPanel = new FocusedProjectPanel(
|
||||
focusedProjectPanelContainer
|
||||
);
|
||||
|
||||
const textFilterPanelContainer =
|
||||
document.getElementById('text-filter-panel');
|
||||
this.textFilterPanel = new TextFilterPanel(textFilterPanelContainer);
|
||||
|
||||
const projectListContainer = document.getElementById('project-lists');
|
||||
this.projectList = new ProjectList(projectListContainer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { fromEvent, Subscription } from 'rxjs';
|
||||
import { debounceTime, filter, map } from 'rxjs/operators';
|
||||
import { useDepGraphService } from '../machines/dep-graph.service';
|
||||
import { DepGraphSend } from '../machines/interfaces';
|
||||
import { removeChildrenFromContainer } from '../util';
|
||||
|
||||
export interface TextFilterChangeEvent {
|
||||
text: string;
|
||||
includeInPath: boolean;
|
||||
}
|
||||
|
||||
export class TextFilterPanel {
|
||||
private textInput: HTMLInputElement;
|
||||
private includeInPathCheckbox: HTMLInputElement;
|
||||
private send: DepGraphSend;
|
||||
|
||||
constructor(private container: HTMLElement) {
|
||||
const [_, send] = useDepGraphService();
|
||||
this.send = send;
|
||||
this.render();
|
||||
}
|
||||
|
||||
private static renderHtmlTemplate(): HTMLElement {
|
||||
const render = document.createElement('template');
|
||||
render.innerHTML = `
|
||||
<div>
|
||||
<div class="mt-10 px-4">
|
||||
<form class="flex rounded-md shadow-sm relative" onSubmit="return false">
|
||||
<span class="inline-flex items-center p-2 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
|
||||
</svg>
|
||||
</span>
|
||||
<input type="text" class="p-1.5 bg-white text-gray-600 flex-1 block w-full rounded-none rounded-r-md border border-gray-300" placeholder="lib name, other lib name" data-cy="textFilterInput" name="filter">
|
||||
<button id="textFilterReset" type="reset" class="p-1 top-1 right-1 absolute bg-white inline-block rounded-md text-gray-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2M3 12l6.414 6.414a2 2 0 001.414.586H19a2 2 0 002-2V7a2 2 0 00-2-2h-8.172a2 2 0 00-1.414.586L3 12z" />
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="mt-4 px-4">
|
||||
<div class="flex items-start">
|
||||
<div class="flex items-center h-5">
|
||||
<input disabled id="includeInPath" name="textFilterCheckbox" type="checkbox" value="includeInPath" class="h-4 w-4 border-gray-300 rounded">
|
||||
</div>
|
||||
<div class="ml-3 text-sm">
|
||||
<label for="includeInPath" class="font-medium text-gray-700 cursor-pointer">Include related libraries</label>
|
||||
<p class="text-gray-500">Show libraries that are related to your search.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`.trim();
|
||||
return render.content.firstChild as HTMLElement;
|
||||
}
|
||||
|
||||
private render() {
|
||||
removeChildrenFromContainer(this.container);
|
||||
|
||||
const element = TextFilterPanel.renderHtmlTemplate();
|
||||
const resetInputElement: HTMLElement =
|
||||
element.querySelector('#textFilterReset');
|
||||
resetInputElement.classList.add('hidden');
|
||||
|
||||
this.textInput = element.querySelector('input[type="text"]');
|
||||
this.textInput.addEventListener('keyup', (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
this.send({ type: 'filterByText', search: this.textInput.value });
|
||||
}
|
||||
|
||||
if (!!this.textInput.value.length) {
|
||||
resetInputElement.classList.remove('hidden');
|
||||
this.includeInPathCheckbox.disabled = false;
|
||||
} else {
|
||||
resetInputElement.classList.add('hidden');
|
||||
this.includeInPathCheckbox.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
fromEvent(this.textInput, 'keyup')
|
||||
.pipe(
|
||||
filter((event: KeyboardEvent) => event.key !== 'Enter'),
|
||||
debounceTime(500),
|
||||
map(() =>
|
||||
this.send({ type: 'filterByText', search: this.textInput.value })
|
||||
)
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.includeInPathCheckbox = element.querySelector('#includeInPath');
|
||||
this.includeInPathCheckbox.addEventListener('change', () =>
|
||||
this.send({
|
||||
type: 'setIncludeProjectsByPath',
|
||||
includeProjectsByPath: this.includeInPathCheckbox.checked,
|
||||
})
|
||||
);
|
||||
|
||||
resetInputElement.addEventListener('click', () => {
|
||||
this.textInput.value = '';
|
||||
this.includeInPathCheckbox.checked = false;
|
||||
this.includeInPathCheckbox.disabled = true;
|
||||
resetInputElement.classList.add('hidden');
|
||||
this.send([{ type: 'clearTextFilter' }]);
|
||||
});
|
||||
|
||||
this.container.appendChild(element);
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -1,14 +1,14 @@
|
||||
import { EdgeSingular, Position, BaseLayoutOptions } from 'cytoscape';
|
||||
import * as cy from 'cytoscape';
|
||||
|
||||
export interface CytoscapeDagreConfig extends BaseLayoutOptions {
|
||||
export interface CytoscapeDagreConfig extends cy.BaseLayoutOptions {
|
||||
// dagre algo options, uses default value on undefined
|
||||
nodeSep: number; // the separation between adjacent nodes in the same rank
|
||||
edgeSep: number; // the separation between adjacent edges in the same rank
|
||||
rankSep: number; // the separation between each rank in the layout
|
||||
rankDir: 'TB' | 'LR'; // 'TB' for top to bottom flow, 'LR' for left to right,
|
||||
ranker: 'network-simplex' | 'tight-tree' | 'longest-path'; // Type of algorithm to assign a rank to each node in the input graph. Possible values: 'network-simplex', 'tight-tree' or 'longest-path'
|
||||
minLen: (edge: EdgeSingular) => number; // number of ranks to keep between the source and target of the edge
|
||||
edgeWeight: (edge: EdgeSingular) => number; // higher weight edges are generally made shorter and straighter than lower weight edges
|
||||
minLen: (edge: cy.EdgeSingular) => number; // number of ranks to keep between the source and target of the edge
|
||||
edgeWeight: (edge: cy.EdgeSingular) => number; // higher weight edges are generally made shorter and straighter than lower weight edges
|
||||
|
||||
// general layout options
|
||||
fit: boolean; // whether to fit to viewport
|
||||
@@ -22,7 +22,7 @@ export interface CytoscapeDagreConfig extends BaseLayoutOptions {
|
||||
boundingBox:
|
||||
| { x1: number; y1: number; x2: number; y2: number }
|
||||
| { x1: number; y1: number; w: number; h: number }; // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
|
||||
transform: (node, pos) => Position; // a function that applies a transform to the final node position
|
||||
transform: (node, pos) => cy.Position; // a function that applies a transform to the final node position
|
||||
ready: () => void; // on layoutready
|
||||
stop: () => void; // on layoutstop
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { ProjectGraphDependency } from '@nrwl/devkit';
|
||||
import * as cy from 'cytoscape';
|
||||
|
||||
export class ProjectEdge {
|
||||
affected = false;
|
||||
|
||||
constructor(private dep: ProjectGraphDependency) {}
|
||||
|
||||
getCytosacpeNodeDef(): cy.NodeDefinition {
|
||||
let edge: cy.EdgeDefinition;
|
||||
edge = {
|
||||
group: 'edges',
|
||||
data: {
|
||||
id: `${this.dep.source}|${this.dep.target}`,
|
||||
source: this.dep.source,
|
||||
target: this.dep.target,
|
||||
},
|
||||
};
|
||||
edge.classes = this.dep.type ?? '';
|
||||
if (this.affected) {
|
||||
edge.classes += ' affected';
|
||||
}
|
||||
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './cytoscape.models';
|
||||
export * from './edge';
|
||||
export * from './parent-node';
|
||||
export * from './project-node';
|
||||
+1
-3
@@ -5,15 +5,13 @@ export class ParentNode {
|
||||
private config: { id: string; parentId: string; label: string }
|
||||
) {}
|
||||
|
||||
getCytoscapeNodeDef(): cy.NodeDefinition & { pannable?: boolean } {
|
||||
getCytoscapeNodeDef(): cy.NodeDefinition {
|
||||
return {
|
||||
group: 'nodes',
|
||||
classes: 'parentNode',
|
||||
data: {
|
||||
id: this.config.id,
|
||||
parent: this.config.parentId,
|
||||
label: this.config.label,
|
||||
type: 'dir',
|
||||
},
|
||||
selectable: false,
|
||||
grabbable: false,
|
||||
+9
-6
@@ -4,7 +4,6 @@ describe('ProjectNode', () => {
|
||||
describe('app nodes', () => {
|
||||
it('should not set parentId if groupByFolder is false', () => {
|
||||
const projectNode = new ProjectNode(
|
||||
{},
|
||||
{
|
||||
name: 'sub-app',
|
||||
type: 'app',
|
||||
@@ -12,7 +11,9 @@ describe('ProjectNode', () => {
|
||||
projectType: 'application',
|
||||
root: 'apps/sub/app',
|
||||
sourceRoot: 'apps/sub/app/src',
|
||||
prefix: 'sub-app',
|
||||
tags: [],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
'apps'
|
||||
@@ -25,7 +26,6 @@ describe('ProjectNode', () => {
|
||||
|
||||
it('should not set parentId if app is not nested', () => {
|
||||
const projectNode = new ProjectNode(
|
||||
{},
|
||||
{
|
||||
name: 'app',
|
||||
type: 'app',
|
||||
@@ -33,7 +33,9 @@ describe('ProjectNode', () => {
|
||||
projectType: 'application',
|
||||
root: 'apps/app',
|
||||
sourceRoot: 'apps/app/src',
|
||||
prefix: 'app',
|
||||
tags: [],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
'apps'
|
||||
@@ -46,7 +48,6 @@ describe('ProjectNode', () => {
|
||||
|
||||
it('should set parentId if the app is nested and groupByFolder is true', () => {
|
||||
const projectNode = new ProjectNode(
|
||||
{},
|
||||
{
|
||||
name: 'sub-app',
|
||||
type: 'app',
|
||||
@@ -54,7 +55,9 @@ describe('ProjectNode', () => {
|
||||
projectType: 'application',
|
||||
root: 'apps/sub/app',
|
||||
sourceRoot: 'apps/sub/app/src',
|
||||
prefix: 'sub-app',
|
||||
tags: [],
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
'apps'
|
||||
@@ -69,7 +72,6 @@ describe('ProjectNode', () => {
|
||||
describe('lib nodes', () => {
|
||||
it('should not set parentId if groupByFolder is false', () => {
|
||||
const projectNode = new ProjectNode(
|
||||
{},
|
||||
{
|
||||
name: 'sub-lib',
|
||||
type: 'lib',
|
||||
@@ -77,6 +79,7 @@ describe('ProjectNode', () => {
|
||||
root: 'libs/sub/lib',
|
||||
sourceRoot: 'libs/sub/lib/src',
|
||||
projectType: 'library',
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
'libs'
|
||||
@@ -89,7 +92,6 @@ describe('ProjectNode', () => {
|
||||
|
||||
it('should not set parentId if lib is not nested', () => {
|
||||
const projectNode = new ProjectNode(
|
||||
{},
|
||||
{
|
||||
name: 'lib',
|
||||
type: 'lib',
|
||||
@@ -97,6 +99,7 @@ describe('ProjectNode', () => {
|
||||
root: 'libs/lib',
|
||||
sourceRoot: 'libs/lib/src',
|
||||
projectType: 'library',
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
'libs'
|
||||
@@ -109,7 +112,6 @@ describe('ProjectNode', () => {
|
||||
|
||||
it('should set parentId if the lib is nested and groupByFolder is true', () => {
|
||||
const projectNode = new ProjectNode(
|
||||
{},
|
||||
{
|
||||
name: 'sub-lib',
|
||||
type: 'lib',
|
||||
@@ -117,6 +119,7 @@ describe('ProjectNode', () => {
|
||||
root: 'libs/sub/lib',
|
||||
sourceRoot: 'libs/sub/lib/src',
|
||||
projectType: 'library',
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
'libs'
|
||||
@@ -0,0 +1,97 @@
|
||||
import type { ProjectGraphNode } from '@nrwl/devkit';
|
||||
import * as cy from 'cytoscape';
|
||||
import { parseParentDirectoriesFromPilePath } from '../util';
|
||||
|
||||
interface NodeDataDefinition extends cy.NodeDataDefinition {
|
||||
id: string;
|
||||
type: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface Ancestor {
|
||||
id: string;
|
||||
parentId: string;
|
||||
label: string;
|
||||
}
|
||||
export class ProjectNode {
|
||||
affected = false;
|
||||
focused = false;
|
||||
|
||||
constructor(
|
||||
private project: ProjectGraphNode,
|
||||
private workspaceRoot: string
|
||||
) {}
|
||||
|
||||
getCytoscapeNodeDef(groupByFolder: boolean): cy.NodeDefinition {
|
||||
return {
|
||||
group: 'nodes',
|
||||
data: this.getData(groupByFolder),
|
||||
classes: this.getClasses(),
|
||||
selectable: false,
|
||||
grabbable: false,
|
||||
pannable: true,
|
||||
};
|
||||
}
|
||||
|
||||
private getData(groupByFolder: boolean): NodeDataDefinition {
|
||||
return {
|
||||
id: this.project.name,
|
||||
type: this.project.type,
|
||||
tags: this.project.data.tags,
|
||||
parent:
|
||||
groupByFolder && this.project.data.hasOwnProperty('root')
|
||||
? this.getParentId()
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
private getClasses(): string {
|
||||
let classes = this.project.type ?? '';
|
||||
|
||||
if (this.focused) {
|
||||
classes += ' focused';
|
||||
}
|
||||
|
||||
if (this.affected) {
|
||||
classes += ' affected';
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
private getParentId(): string | null {
|
||||
const ancestors = this.getAncestors();
|
||||
|
||||
if (ancestors.length > 0) {
|
||||
return ancestors[ancestors.length - 1].id;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public getAncestors(): Ancestor[] {
|
||||
// if there's no root, we can't figure out the parent
|
||||
if (!this.project.data.root) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const directories = parseParentDirectoriesFromPilePath(
|
||||
this.project.data.root,
|
||||
this.workspaceRoot
|
||||
);
|
||||
|
||||
return directories.map((directory, index, allDirectories) => {
|
||||
const label = [...allDirectories].slice(0, index + 1).join('/');
|
||||
const id = `dir-${label}`;
|
||||
const parentId =
|
||||
index > 0
|
||||
? `dir-${[...allDirectories].slice(0, index).join('/')}`
|
||||
: null;
|
||||
return {
|
||||
label,
|
||||
id,
|
||||
parentId,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user