brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
monorepoUse this skill when the user says 'monorepo', 'Nx', 'Turborepo', 'NX', 'nx build', 'nx affected', 'workspace', 'dependency graph', 'build orchestration', 'remote caching', 'task orchestration', 'shared configuration', 'lerna', 'pnpm workspace', 'yarn workspace', 'npm workspace', 'project graph', 'module boundary', 'computation caching'. Covers: Nx workspace…
Independent GitHub snapshot · Automated classification · Original documentation governs
Configure and manage monorepo workspaces with Nx or Turborepo for efficient build orchestration and caching.
Exact user phrases: "monorepo", "Nx", "Turborepo", "nx build", "nx affected", "workspace", "dependency graph", "build orchestration", "remote caching", "pnpm workspace", "yarn workspace", "project graph", "module boundary".
Before activating, verify:
Writes to nx.json, turbo.json, project.json, workspace config, and CI workflow files.
nx.json, turbo.json, project.json, or CI config with no extraneous explanation.
No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick.
This skill is complete when:
Direct file write. No response text.
Nx workspace: npx create-nx-workspace@latest → configure nx.json with cacheable operations → nx build app → nx affected:test --base=main. Turborepo: turbo.json with pipeline → turbo run build --filter=app.
| Tool | Best For | Key Feature |
|---|---|---|
| Nx | Large monorepos, need module boundaries, code generation | Dependency graph, affected commands, generators, enforce-module-boundaries |
| Turborepo | Simpler monorepos, focused on caching | Zero-config caching, remote cache, pipeline |
| Lerna | Legacy monorepos, npm publishing | Package publishing, canary releases, independent versioning |
Workspace size < 10 packages?
├── Yes → npm workspaces (simplest, no extra tooling)
└── No → > 50 packages or need strict isolation?
├── Yes → pnpm workspaces (fast, disk-efficient, strict)
└── No → yarn workspaces (good balance of speed and features)
Need module boundary enforcement?
├── Yes → Nx (enforce-module-boundaries rule)
└── No → Only need caching?
├── Yes → Turborepo (simpler setup)
└── No → Need code generators and dependency graph?
├── Yes → Nx (nx g, nx graph)
└── No → Either (both support basic task orchestration)
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
- "libs/*"
- "tools/*"
// nx.json
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"defaultBase": "main",
"nxCloudAccessToken": "…",
"plugins": [
{
"plugin": "@nx/js/typescript",
"options": {
"buildTargetName": "build"
}
}
],
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"cache": true,
"inputs": ["{projectRoot}/**/*", "sharedGlobals"]
},
"test": {
"dependsOn": ["build"],
"cache": true,
"inputs": ["default", "^production"]
},
"lint": {
"cache": true
}
}
}
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"globalEnv": ["NODE_ENV", "API_URL", "CI"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"cache": true,
"inputs": ["$TURBO_DEFAULTquot;, ".env.production"],
"outputs": ["dist/**", ".next/**", "build/**"],
"env": ["NODE_ENV", "DATABASE_URL"]
},
"test": {
"dependsOn": ["build"],
"cache": true,
"inputs": ["$TURBO_DEFAULTquot;, "vitest.config.ts"],
"outputs": []
},
"lint": { "cache": false },
"typecheck": { "dependsOn": ["^build"], "cache": false },
"dev": { "cache": false, "persistent": true }
}
}
{
"name": "web",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/web/src",
"projectType": "application",
"tags": ["scope:web", "type:app"],
"implicitDependencies": ["shared-ui"],
"targets": {
"build": {
"executor": "@nx/next:build",
"options": { "outputPath": "dist/apps/web" }
}
}
}
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Derive appropriate SHAs for base and head
uses: nrwl/nx-set-shas@v4
- run: npx nx-cloud record -- nx format:check
- run: npx nx affected -t lint,test,build --parallel=3
# Turborepo equivalent:
# turbo run build test lint --filter=...[main]
# Nx Cloud
npx nx connect-to-nx-cloud
# Turborepo remote cache (Vercel)
turbo login
turbo link
# Self-hosted remote cache (S3-compatible)
# Turborepo: TURBO_API, TURBO_TOKEN, TURBO_TEAM env vars
# Nx: NX_CLOUD_NO_TIMEOUTS, custom NX_CLOUD_API
{
"rules": {
"@nx/enforce-module-boundaries": [
"error",
{
"allow": [],
"depConstraints": [
{ "sourceTag": "scope:web", "onlyDependOnLibsWithTags": ["scope:web", "scope:shared"] },
{ "sourceTag": "scope:api", "onlyDependOnLibsWithTags": ["scope:api", "scope:shared"] },
{ "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"] },
{ "sourceTag": "type:app", "onlyDependOnLibsWithTags": ["type:feature", "type:ui", "type:util"] },
{ "sourceTag": "type:feature", "onlyDependOnLibsWithTags": ["type:ui", "type:util"] },
{ "sourceTag": "type:ui", "onlyDependOnLibsWithTags": ["type:util"] },
{ "sourceTag": "type:util", "onlyDependOnLibsWithTags": [] }
]
}
]
}
}
// tools/generators/feature/index.ts
import { Tree, formatFiles, generateFiles, joinPathFragments } from '@nx/devkit';
export default async function (tree: Tree, schema: { name: string; path: string }) {
generateFiles(tree, joinPathFragments(__dirname, 'files'), schema.path, {
name: schema.name,
tmpl: '',
});
await formatFiles(tree);
}
{
"tasksRunnerOptions": {
"default": {
"runner": "nx-cloud",
"options": {
"cacheableOperations": ["build", "test", "lint"],
"parallel": 3,
"agent": {
"containers": 4
}
}
}
}
}
# Visualize full dependency graph
nx graph
# Focus on specific project
nx graph --focus=my-app
# Show affected by a change
nx graph --affected --base=main
# Export graph data
nx graph --file=graph.html
# Find circular dependencies
nx graph --focus=my-app --watch
# Nx: add Nx to existing project
npx nx@latest init
# Move existing package into workspace
npx nx generate @nx/workspace:move my-package --destination packages/my-package
# Set up shared config
# tsconfig.base.json with path mappings
{
"compilerOptions": {
"paths": {
"@myorg/shared": ["packages/shared/src/index.ts"]
}
}
}
# Extract shared code into library
npx nx g @nx/js:lib shared --directory=packages/shared
{
"cache": {
"enabled": true,
"path": ".cache/nx",
"cleanup": {
"maxSize": "5GB",
"maxAge": "30d"
}
}
}
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"cache": true,
"outputs": ["dist/**"]
},
"build:production": {
"dependsOn": ["^build:production"],
"cache": true,
"outputs": ["dist/**"],
"env": ["NODE_ENV"]
}
}
}
# docker-compose for self-hosted Nx Cloud
version: '3.8'
services:
nx-cloud:
image: nrwl/nx-cloud:latest
ports:
- "8080:8080"
environment:
- NX_CLOUD_ENCRYPTION_KEY=${NX_CLOUD_ENCRYPTION_KEY}
- STORAGE_PROVIDER=s3
- S3_ENDPOINT=${S3_ENDPOINT}
- S3_BUCKET=nx-cloud-cache
- S3_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- S3_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
volumes:
- nx-cloud-data:/data
// tools/plugins/shared-config/src/index.ts
import { createNodesFromFiles } from '@nx/devkit';
import type { Tree, CreateNodesFunction } from '@nx/devkit';
export const createNodes: CreateNodesFunction = [
'**/project.json',
async (configFilePath, options, context) => {
const projectRoot = configFilePath.replace('/project.json', '');
return {
projects: {
[projectRoot]: {
targets: {
'shared-lint': {
executor: 'nx:run-commands',
options: {
command: `eslint ${projectRoot}`,
cwd: context.root,
},
},
},
},
},
};
},
];
# Ensure all workspace packages use the same dependency versions
pnpm add -D syncpack
# syncpack.config.mjs
export default {
dependencyTypes: ['dev', 'prod', 'peer'],
semverGroups: [
{
range: '^',
dependencyTypes: ['dev', 'prod', 'peer'],
dependencies: ['**'],
packages: ['**'],
},
],
versionGroups: [
{
label: 'Use workspace protocol for internal deps',
dependencies: ['@myorg/**'],
dependencyTypes: ['dev', 'prod'],
pinVersion: 'workspace:^',
},
],
};
# Check for mismatched versions
pnpm syncpack list-mismatches
# Fix mismatched versions
pnpm syncpack fix-mismatches
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
env:
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
NX_BRANCH: ${{ github.head_ref || github.ref_name }}
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Restore cached Nx
id: nx-cache
uses: actions/cache@v4
with:
path: .cache/nx
key: nx-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm install --frozen-lockfile
- run: npx nx-cloud record -- nx format:check
- run: npx nx affected -t typecheck
- run: npx nx affected -t lint --parallel=3
- run: npx nx affected -t test --parallel=3
- run: npx nx affected -t build --parallel=3
e2e:
needs: [quality]
runs-on: ubuntu-latest
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false)
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: npx nx affected -t e2e --base=main
| Feature | Nx | Turborepo | Lerna | pnpm Workspaces |
|---|---|---|---|---|
| Local caching | Yes | Yes | No | No |
| Remote caching | Nx Cloud / self-hosted | Vercel / self-hosted | No | No |
| Affected commands | Yes (git-based) | Yes (filter) | No | No |
| Module boundaries | Yes (eslint rule) | No | No | No |
| Code generators | Yes (nx g) | No | No | No |
| Dependency graph | Yes (nx graph) | No | No | No |
| Task distribution | Yes (Nx Agents) | No | No | No |
| CI integration | Deep (nx-set-shas) | Basic (filter) | Basic | None |
| Learning curve | Steep | Low | Medium | Low |
| Best for project count | 50+ | 10-100 | <30 | <20 |
workspace:^) prevents accidental registry publishing of internal packagesnx.json and turbo.json exposure in CI logs — tokens may appear in verbose output.nxignore to prevent sensitive directories from being cached or distributedfetch-depth: 0 in CI for proper git history needed by affected commands.nx.json inputs to fine-tune cache invalidation per target.nx graph --affected --base=main before merge to validate dependency impact.nx graph --watch.tsconfig.paths to enforce type-safe module resolution.pnpm over npm or yarn for faster installs and disk efficiency..nvmrc or engines field.nx affected --exclude=legacy-app --base=main to skip known-slow projects.project.json targets focused — one target per concern.pnpm-lock.yaml) and check it in — never regenerate in CI.fetch-depth: 0 in CI — affected commands don't detect changes.npm workspaces — slower installs, no lockfile deduplication.latest tag in shared dependencies — can't reproduce builds.project.json target for everything — no incremental benefit.tsconfig.paths — IDE resolution mismatches with runtime.nx run-many instead of nx affected — wasteful full builds.nx.json — CI secret exposure..nxignore — caching node_modules or dist archives with secrets.After completing this skill: