mirror of
https://github.com/wshobson/agents.git
synced 2026-03-18 09:37:15 +00:00
Remove references to non-existent resource files (references/, assets/, scripts/, examples/) from 115 skill SKILL.md files. These sections pointed to directories and files that were never created, causing confusion when users install skills. Also fix broken Code of Conduct links in issue templates to use absolute GitHub URLs instead of relative paths that 404.
452 lines
11 KiB
Markdown
452 lines
11 KiB
Markdown
---
|
|
name: nx-workspace-patterns
|
|
description: 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.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
|
|
|
|
```yaml
|
|
# .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
|
|
|
|
```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 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
|