nx-workspace-patterns

配置并优化Nx单仓库工作区。适用于设置Nx、配置项目边界、优化构建缓存或实现受影响的命令时使用。

查看详情
name:nx-workspace-patternsdescription:Configure and optimize Nx monorepo workspaces. Use when setting up Nx, configuring project boundaries, optimizing build caching, or implementing affected commands.

Nx Workspace Patterns

Production patterns for Nx monorepo management.

Do not use this skill when

  • The task is unrelated to nx workspace patterns

  • You need a different domain or tool outside this scope
  • Instructions

  • Clarify goals, constraints, and required inputs.

  • Apply relevant best practices and validate outcomes.

  • Provide actionable steps and verification.

  • If detailed examples are required, open resources/implementation-playbook.md.
  • Use this skill when

  • 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

    2. Library Types

    TypePurposeExample
    featureSmart components, business logicfeature-auth
    uiPresentational componentsui-buttons
    data-accessAPI calls, state managementdata-access-users
    utilPure functions, helpersutil-formatting
    shellApp bootstrappingshell-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