Claude Agent Skill · by Wshobson

Nx Workspace Patterns

This handles the tedious parts of Nx monorepo setup that trip up most teams. It generates proper nx.json configurations with optimized caching, sets up module b

Install
Terminal · npx
$npx skills add https://github.com/wshobson/agents --skill nx-workspace-patterns
Works with Paperclip

How Nx Workspace Patterns fits into a Paperclip company.

Nx Workspace Patterns drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

S
SaaS FactoryPaired

Pre-configured AI company — 18 agents, 18 skills, one-time purchase.

$27$59
Explore pack
Source file
SKILL.md451 lines
Expand
---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. ## 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``` ### 2. 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 ```json{  "$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 ```json// 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 ```json// .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 ```typescript// tools/generators/feature-lib/index.tsimport {  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 ```yaml# .github/workflows/ci.ymlname: 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 ```typescript// 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 ```bash# Generate new librarynx g @nx/react:lib feature-auth --directory=libs/web --tags=type:feature,scope:web # Run affected testsnx affected -t test --base=main # View dependency graphnx graph # Run specific projectnx build web --configuration=production # Reset cachenx reset # Run migrationsnx migrate latestnx 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