Skip to content
Back to skills

Nx Tasks Reference

ASecurity

What each nx.json target does and when to add it to project.json

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 5, 2026
testingtypescriptgonodegit

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 5, 2026

npx -y skills add JacobLey/leyman --skill nx-tasks-reference --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Nx Tasks Reference?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Nx Tasks Reference
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jacobley-nx-tasks-reference/badge)](https://www.skillsdirectory.com/skills/jacobley-nx-tasks-reference)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: nx-tasks-reference
description: What each nx.json target does and when to add it to project.json
---

# Nx Task Reference

Every package in this repo is wired into the same lifecycle via `nx.json` `targetDefaults`. When adding or modifying a `project.json`, declare the targets the package needs as `{}` — the full implementation comes from the targetDefault.

The orchestration targets (`check:_`, `check:lint`, `check`, …) are no-ops that only enforce ordering. `nx run @leyman/main:lifecycle` adds them to `project.json` and generates their `dependsOn` from [`lifecycle.json`](../../../leyman/main/lifecycle.json). See the [lifecycle skill](../../../leyman/main/.claude/skills/lifecycle/SKILL.md).

### Project layout and caching

Cache inputs are globs, so they only work if every package puts things in the same place. A file outside these locations is invisible to caching: changing it will not rerun anything.

| Path | Tracked | Role | Hashed as |
|------|---------|------|-----------|
| `src/` | yes | Shipped source. Only the package itself reads it | `ts-source` |
| `test/` | yes | Tests, fixtures (any file type) and `test/tsconfig.json` | `test-source` |
| `dist/` | no | Build output (`tsc`): exactly what ships. The only built code dependents may import | `dependency-builds`, for dependents |
| `dist-test/` | no | Compiled tests (`tsc-test`). Never seen by dependents | — |
| `out/` | yes | Generated files that ship (`populate-files`, e.g. JSON schemas) | `package-files` |
| `data/` | yes | Static files that ship and are read at runtime or by dependents | `package-files` |
| `package.json`, `bin.mjs`, `executors.json`, `*.d.ts` at the root | yes | Package entry points | `package-files` |

Named inputs in [`nx.json`](../../../nx.json):

- `ts-source`: the project's own source and build config. A change rebuilds the project.
- `dependency-builds`: the `.js` and `.d.ts` files in dependencies' `dist/`, via `dependentTasksOutputFiles`. Dependents rebuild when a dependency's build output changes, not when its source changes. This is why tests compile to `dist-test/` rather than `dist/tests/`: `dependentTasksOutputFiles` ignores negated outputs, so tests inside `dist/` would rebuild every dependent when they change.
- `^package-files`: dependencies' tracked entry points (`package.json`, `bin.mjs`, `out/`, `data/`…). Targets list it directly, because Nx does not allow `^` inside a named input.
- `test-source`: `ts-source` plus `test/`, `configs/tsconfig.test.json`, the project's own `package-files`, the builds of its dependencies and itself, and the c8 config.
- `shared-globals`: the Node version. The cache is shared between worktrees through the `nx-cache` sidecar.

Gotchas, all verified on Nx 22:

- **Never hash `dist/` with a file glob.** `dist/` is gitignored, and Nx only sees git-visible files, so a glob like `^{projectRoot}/dist/**` silently matches nothing. Use `dependentTasksOutputFiles`.
- **`dependentTasksOutputFiles` does not support extglobs** like `*.(c|m)?js`. Use one entry per extension. Plain file inputs do support them.
- **A generator's inputs must include everything it reads,** not just the files it writes. `barrelify` hashes all of `src/`, since adding a sibling module changes the barrel.
- **Clean output inside the task that writes it** (as `tsc` and the test targets do), not in a separate uncached target. An uncached cleanup runs every time and forces every cached output to be copied back.

Nx hashes each project's npm dependencies (direct and transitive, resolved from `pnpm-lock.yaml`) through the project graph, so a lockfile change only invalidates projects that depend on the changed package. `ts-source` sets `{ "externalDependencies": [] }` to opt out of Nx's default for `nx:run-commands` targets, which hashes _every_ external package in the workspace (`AllExternalDependencies`) and busts all caches on any lockfile change.

Use `nx show target inputs <project>:<target>` to list the files a target hashes. Add `--check <path>` to test a single file. Neither shows `dependentTasksOutputFiles`, so test those by changing a dependency and checking that the dependent reruns.

---

## Work Targets

Every work target except repo-wide commands (like `lifecycle` itself) must be bound in `lifecycle.json`.

### `biome`

Formats with Biome (`scripts/nx/biome-format.sh`). Writes changes, except in CI where it only checks. `-c check` checks without writing.

**Add when:** Every package.

---

### `eslint`

Lints with the project's `eslint.config.js`. `-c fix` auto-fixes.

**Add when:** Add to all TypeScript packages (has `tsconfig.json`).

---

### `barrelify`

Rewrites `index.ts` files that contain an `// AUTO-BARREL` marker with correct re-export statements.

**Add when:** The package has any `index.ts` files marked with `// AUTO-BARREL`.

---

### `update-ts-references`

Keep `tsconfig.json` `references` in sync with the package's inter-package dependencies.

**Add when:** The package has a `tsconfig.json`.

---

### `tsc`

Deletes `./dist`, then compiles TypeScript using SWC (fast transpilation) and `tsc` (declaration file generation + type checking). Clearing `dist` inside the target means it only happens on a cache miss.

Output goes to `./dist`.

**Add when:** Add to all TypeScript packages (has `tsconfig.json`).

---

### `tsc-test`

Deletes `./dist-test`, compiles `test/` into it with SWC, then type-checks the tests with `tsc -p ./test` (no emit; `test/tsconfig.json` extends the shared [`configs/tsconfig.test.json`](../../../configs/tsconfig.test.json)). Runs in `test:compile`, after the package's own build, so tests are type-checked against its published `.d.ts` exactly as a consumer would see them.

**Add when:** The package has tests. Add whenever `mocha-unit-test`, `mocha-integration-test`, or `vitest-unit-test` is present.

---

### `populate-files`

Generates static output files by running `load-populate-files` against `./dist/file-content.js`. Expects the package to export a default array of `PopulateFileParams` from `src/file-content.ts`. Output goes to `./out`.

**Add when:** The package defines a `src/file-content.ts` file.

---

### `sync-injected`

Mirrors the package's published files (`pnpm pack --dry-run`) into its injected copies in `node_modules/.pnpm` (`scripts/nx/sync-injected.mjs`). Workspace dependencies are installed as copies (`injectWorkspacePackages`), made at install time, so without this dependents would keep seeing the `dist/` from the last `pnpm install`. Never cached: it must also run when `tsc` is a cache hit, since restoring `dist/` doesn't update the copies.

A build outside Nx (e.g. running `tsc` directly) doesn't sync. Run the package's `build`, or `pnpm install`, which also refreshes every copy.

**Add when:** Every package with `tsc`.

---

### `mocha-unit-test`

Clears its coverage directory, then runs Mocha unit tests from `./dist-test/unit/**/*.spec.*js` under C8 coverage instrumentation.

**Add when:** The package has unit tests in `test/unit/`. Add to virtually all packages.

---

### `mocha-integration-test`

Clears its coverage directory, then runs Mocha integration tests from `./dist-test/integration/**/*.spec.*js` under C8 coverage instrumentation.

**Add when:** The package has integration tests in `test/integration/`.

---

### `vitest-unit-test`

Clears its coverage directory, then runs Vitest unit tests from `./dist-test/unit/**/*.spec.js` under C8 coverage instrumentation, using the shared [`configs/vitest.config.js`](../../../configs/vitest.config.js).

The shared config runs pre-compiled tests with native `import` and the `threads` pool (so C8 can collect V8 coverage), and sets `sequence.hooks: "list"` (required by `vitest-chain`).

**Add when:** The package tests with Vitest instead of Mocha (e.g. `vitest-chain`). Use instead of `mocha-unit-test`.

---

### `coverage-report`

Validates 100% coverage using only the C8 data from this project's own test targets (`scripts/nx/coverage-report.sh`). Runs in `test:report`, after the test suites. Coverage from dependents' tests does not count.

**Add when:** The package has any test target.

---

## Typical `project.json` Templates

### Standard TypeScript package (unit tests only)

```json
{
  "$schema": "../../leyman/main/node_modules/nx/schemas/project-schema.json",
  "name": "my-package",
  "targets": {
    "biome": {},
    "eslint": {},
    "update-ts-references": {},
    "tsc": {},
    "tsc-test": {},
    "mocha-unit-test": {},
    "coverage-report": {},
    "sync-injected": {},
    "check:_": {},
    "check:lint": {},
    // lots more of ignorable nx-lifecycle managed targets
  }
}
```

Target order in `project.json` technically does not matter, but try to put them in the order described above, as they generally reflect the order they will run.

`nx-lifecycle` will ensure all orchestration targets go at the end.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…