nx-workspace-patterns

安装量: 3.1K
排名: #695

安装

npx skills add https://github.com/wshobson/agents --skill nx-workspace-patterns

Nx Workspace Patterns

Production patterns for Nx monorepo management.

When to Use This Skill Setting up new Nx workspaces Configuring project boundaries Optimizing CI with affected commands Implementing remote caching Managing dependencies between projects Migrating to Nx Core Concepts 1. Nx Architecture workspace/ ├── apps/ # Deployable applications │ ├── web/ │ └── api/ ├── libs/ # Shared libraries │ ├── shared/ │ │ ├── ui/ │ │ └── utils/ │ └── feature/ │ ├── auth/ │ └── dashboard/ ├── tools/ # Custom executors/generators ├── nx.json # Nx configuration └── workspace.json # Project configuration

  1. Library Types Type Purpose Example feature Smart components, business logic feature-auth ui Presentational components ui-buttons data-access API calls, state management data-access-users util Pure functions, helpers util-formatting shell App bootstrapping shell-web Templates Template 1: nx.json Configuration { "$schema": "./node_modules/nx/schemas/nx-schema.json", "npmScope": "myorg", "affected": { "defaultBase": "main" }, "tasksRunnerOptions": { "default": { "runner": "nx/tasks-runners/default", "options": { "cacheableOperations": [ "build", "lint", "test", "e2e", "build-storybook" ], "parallel": 3 } } }, "targetDefaults": { "build": { "dependsOn": ["^build"], "inputs": ["production", "^production"], "cache": true }, "test": { "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"], "cache": true }, "lint": { "inputs": ["default", "{workspaceRoot}/.eslintrc.json"], "cache": true }, "e2e": { "inputs": ["default", "^production"], "cache": true } }, "namedInputs": { "default": ["{projectRoot}//*", "sharedGlobals"], "production": [ "default", "!{projectRoot}//?(*.)+(spec|test).[jt]s?(x)?(.snap)", "!{projectRoot}/tsconfig.spec.json", "!{projectRoot}/jest.config.[jt]s", "!{projectRoot}/.eslintrc.json" ], "sharedGlobals": [ "{workspaceRoot}/babel.config.json", "{workspaceRoot}/tsconfig.base.json" ] }, "generators": { "@nx/react": { "application": { "style": "css", "linter": "eslint", "bundler": "webpack" }, "library": { "style": "css", "linter": "eslint" }, "component": { "style": "css" } } } }

Template 2: Project Configuration // apps/web/project.json { "name": "web", "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "apps/web/src", "projectType": "application", "tags": ["type:app", "scope:web"], "targets": { "build": { "executor": "@nx/webpack:webpack", "outputs": ["{options.outputPath}"], "defaultConfiguration": "production", "options": { "compiler": "babel", "outputPath": "dist/apps/web", "index": "apps/web/src/index.html", "main": "apps/web/src/main.tsx", "tsConfig": "apps/web/tsconfig.app.json", "assets": ["apps/web/src/assets"], "styles": ["apps/web/src/styles.css"] }, "configurations": { "development": { "extractLicenses": false, "optimization": false, "sourceMap": true }, "production": { "optimization": true, "outputHashing": "all", "sourceMap": false, "extractLicenses": true } } }, "serve": { "executor": "@nx/webpack:dev-server", "defaultConfiguration": "development", "options": { "buildTarget": "web:build" }, "configurations": { "development": { "buildTarget": "web:build:development" }, "production": { "buildTarget": "web:build:production" } } }, "test": { "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { "jestConfig": "apps/web/jest.config.ts", "passWithNoTests": true } }, "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": ["apps/web/*/.{ts,tsx,js,jsx}"] } } } }

Template 3: Module Boundary Rules // .eslintrc.json { "root": true, "ignorePatterns": ["/"], "plugins": ["@nx"], "overrides": [ { "files": [".ts", ".tsx", ".js", "*.jsx"], "rules": { "@nx/enforce-module-boundaries": [ "error", { "enforceBuildableLibDependency": true, "allow": [], "depConstraints": [ { "sourceTag": "type:app", "onlyDependOnLibsWithTags": [ "type:feature", "type:ui", "type:data-access", "type:util" ] }, { "sourceTag": "type:feature", "onlyDependOnLibsWithTags": [ "type:ui", "type:data-access", "type:util" ] }, { "sourceTag": "type:ui", "onlyDependOnLibsWithTags": ["type:ui", "type:util"] }, { "sourceTag": "type:data-access", "onlyDependOnLibsWithTags": ["type:data-access", "type:util"] }, { "sourceTag": "type:util", "onlyDependOnLibsWithTags": ["type:util"] }, { "sourceTag": "scope:web", "onlyDependOnLibsWithTags": ["scope:web", "scope:shared"] }, { "sourceTag": "scope:api", "onlyDependOnLibsWithTags": ["scope:api", "scope:shared"] }, { "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"] } ] } ] } } ] }

Template 4: Custom Generator // tools/generators/feature-lib/index.ts import { Tree, formatFiles, generateFiles, joinPathFragments, names, readProjectConfiguration, } from "@nx/devkit"; import { libraryGenerator } from "@nx/react";

interface FeatureLibraryGeneratorSchema { name: string; scope: string; directory?: string; }

export default async function featureLibraryGenerator( tree: Tree, options: FeatureLibraryGeneratorSchema, ) { const { name, scope, directory } = options; const projectDirectory = directory ? ${directory}/${name} : libs/${scope}/feature-${name};

// Generate base library await libraryGenerator(tree, { name: feature-${name}, directory: projectDirectory, tags: type:feature,scope:${scope}, style: "css", skipTsConfig: false, skipFormat: true, unitTestRunner: "jest", linter: "eslint", });

// Add custom files const projectConfig = readProjectConfiguration( tree, ${scope}-feature-${name}, ); const projectNames = names(name);

generateFiles( tree, joinPathFragments(__dirname, "files"), projectConfig.sourceRoot, { ...projectNames, scope, tmpl: "", }, );

await formatFiles(tree); }

Template 5: CI Configuration with Affected

.github/workflows/ci.yml

name: CI

on: push: branches: [main] pull_request: branches: [main]

env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}

jobs: main: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0

  - uses: actions/setup-node@v4
    with:
      node-version: 20
      cache: "npm"

  - name: Install dependencies
    run: npm ci

  - name: Derive SHAs for affected commands
    uses: nrwl/nx-set-shas@v4

  - name: Run affected lint
    run: npx nx affected -t lint --parallel=3

  - name: Run affected test
    run: npx nx affected -t test --parallel=3 --configuration=ci

  - name: Run affected build
    run: npx nx affected -t build --parallel=3

  - name: Run affected e2e
    run: npx nx affected -t e2e --parallel=1

Template 6: Remote Caching Setup // nx.json with Nx Cloud { "tasksRunnerOptions": { "default": { "runner": "nx-cloud", "options": { "cacheableOperations": ["build", "lint", "test", "e2e"], "accessToken": "your-nx-cloud-token", "parallel": 3, "cacheDirectory": ".nx/cache" } } }, "nxCloudAccessToken": "your-nx-cloud-token" }

// Self-hosted cache with S3 { "tasksRunnerOptions": { "default": { "runner": "@nx-aws-cache/nx-aws-cache", "options": { "cacheableOperations": ["build", "lint", "test"], "awsRegion": "us-east-1", "awsBucket": "my-nx-cache-bucket", "awsProfile": "default" } } } }

Common Commands

Generate new library

nx g @nx/react:lib feature-auth --directory=libs/web --tags=type:feature,scope:web

Run affected tests

nx affected -t test --base=main

View dependency graph

nx graph

Run specific project

nx build web --configuration=production

Reset cache

nx reset

Run migrations

nx migrate latest nx migrate --run-migrations

Best Practices Do's Use tags consistently - Enforce with module boundaries Enable caching early - Significant CI savings Keep libs focused - Single responsibility Use generators - Ensure consistency Document boundaries - Help new developers Don'ts Don't create circular deps - Graph should be acyclic Don't skip affected - Test only what changed Don't ignore boundaries - Tech debt accumulates Don't over-granularize - Balance lib count Resources Nx Documentation Module Boundaries Nx Cloud

返回排行榜