This repository uses a pnpm + Nx TypeScript monorepo structure. The guidance below is written for agentic coding assistants that will run in this repo (linters, builders, test runners, and change authors). Follow these rules to produce consistent, reviewable changes.
- Install dependencies:
pnpm i - Build all packages:
pnpm run build(runsslimbuild+ docs) - Build affected packages:
pnpm run build:affectedorpnpm nx affected -t build - Run the full workspace build (CI):
pnpm run build:ci - Run typedoc (docs):
pnpm run build:docs - Start demo server:
cd demo/vanilla && pnpm start
- Format README + markdown:
pnpm run prettify:readme - Check formatting (CI):
pnpm run prettify:ci:readme - Run ESLint (package-local):
pnpm nx run <project>:lintorpnpm --filter <package> run lint - Husky hooks are enabled: local
git commitruns hooks (preparescript inpackage.json). Do not bypass hooks.
- Run all tests (workspace):
pnpm exec vitestorpnpm nx run-many -t test - Run package tests (example):
pnpm --filter @tsparticles/tests test - Run a single test file with Vitest:
- From package:
pnpm --filter @tsparticles/tests test:particle(predefined script) - Directly:
pnpm exec vitest run path/to/test/file.ts(e.g.utils/tests/src/tests/Particle.ts) - With Nx (project-level):
pnpm nx test <project> --testFile=src/tests/Particle.ts(use project-specific options)
- From package:
- Run tests in watch UI:
pnpm --filter @tsparticles/tests test:uiorpnpm exec vitest --ui - Coverage: vitest with v8 provider configured in
utils/tests/vitest.config.ts— CI exposes coverage artifacts underutils/tests/coverage/.
Notes on single-test execution: prefer package scripts that target a single file (many test packages include narrow scripts). If unavailable, run pnpm exec vitest run <path> from repo root.
- Slim/full bundle build:
pnpm run slimbuild(usesnx run-many -t build) - Build for CI with module/UMD targets:
pnpm run build:ci - Bundle configs are under
bundles/*/webpack.config.jsand package-leveltsconfig.*.jsonfiles.
Follow the repository-wide style enforced by @tsparticles/prettier-config and @tsparticles/eslint-config.
General rules
- Language: TypeScript (>= 5.x) is the source language. Compiled outputs and bundles may be JavaScript.
- Formatting: Prettier (configured via
prettierfield inpackage.json). Runpnpm run prettify:readmefor docs andpnpm exec prettier --write .for project-wide formatting when needed. - Linting: ESLint with shared config
@tsparticles/eslint-config. Runnpx eslint <path>only when debugging lint issues.
Imports
- Group imports: 1) external packages, 2) workspace packages (internal), 3) relative imports; separate groups with a blank line.
- Prefer named imports for library exports; avoid default imports when the upstream uses named exports.
- Use absolute package imports for workspace packages (e.g.
@tsparticles/engine) when available; otherwise use relative paths.
Types & API surfaces
- Prefer explicit types and interfaces for public package APIs. Avoid
anyon exported types. - Use
unknowninstead ofanywhen receiving untyped input and narrow immediately with type guards. - Keep public option shapes documented and typed under
engine/src/Options/Interfaces/*.
Naming
- Files exporting a class or main type should use matching PascalCase file names (e.g.
TiltUpdater.tsexportsTiltUpdater). - Use PascalCase for classes & types, camelCase for functions/variables, UPPER_SNAKE_CASE only for constants that are truly constant across runs.
Error handling
- Throw
Errorfor unrecoverable issues. Use typed Error subclasses for domain-specific errors when helpful. - Prefer returning
undefinedfor optional/absent results and document this in types (avoid using exceptions for control flow). - Add defensive checks for external inputs (image URLs, user-provided options) and surface clear error messages.
Performance & hot paths
- Avoid per-frame allocations in hot loops; reuse objects or introduce pooling where it measurably improves throughput.
- Replace expensive operations on hot paths (e.g.
JSON.stringifyfor memo keys) with bounded caches and efficient keying.
Global state
- Do not mutate
globalThisunless strictly required; prefer opt-in bootstrap APIs that return instances.
Tests & fixtures
- Tests use Vitest + jsdom; canvas tests use
canvaspackage and custom fixtures underutils/tests/src/Fixture/. - Keep tests deterministic: avoid timing-based assertions; use mocked timers or explicit ticks where needed.
Commit messages
- Use Conventional Commits. Commitlint is enforced (
@commitlint/config-conventional). Example:fix(engine): handle NaN in velocity.
PRs
- Provide clear description, testing notes, and update
CHANGELOG.mdif the change affects public APIs.
- Always prefer workspace-aware commands: use
pnpm+nxrather than invoking global CLIs. - NEVER bypass Husky hooks. If you cannot run hooks in the environment, stage files and report failure rather than committing.
- When creating or modifying packages, update related
bundles/*/package.dist.jsonif you intend to publish. - For code edits: include tests or update existing tests that cover the change. If tests are flaky locally, document the failure and do not proceed to commit-only fixes without fixing tests.
- For large refactors, produce a short migration plan and update
PROJECT.mdorCHANGELOG.mdas appropriate.
- Root
package.json— workspace scripts and devDependencies pnpm-workspace.yaml— workspace packagesnx.json— Nx task configurationengine/src/— core runtimebundles/*/— bundle assembly and webpack configsutils/tests/— primary test package and fixtures.github/workflows/— CI and publish pipelines
-
Cursor skills & rules found under
.cursor/: follow the skill definitions before invoking local helpers. Notable files:.cursor/skills/monitor-ci/SKILL.md.cursor/skills/link-workspace-packages/SKILL.md.cursor/skills/nx-workspace/SKILL.md.cursor/skills/nx-generate/SKILL.md.cursor/skills/nx-import/SKILL.md.cursor/skills/nx-run-tasks/SKILL.md.cursor/skills/nx-plugins/SKILL.md
-
Equivalent GitHub Copilot assets are also present under
.github/:.github/skills/monitor-ci/SKILL.md.github/skills/link-workspace-packages/SKILL.md.github/skills/nx-workspace/SKILL.md.github/skills/nx-generate/SKILL.md.github/skills/nx-import/SKILL.md.github/skills/nx-run-tasks/SKILL.md.github/skills/nx-plugins/SKILL.md.github/agents/ci-monitor-subagent.agent.md.github/prompts/monitor-ci.prompt.md
-
There is no
.github/copilot-instructions.mdpresent; follow the repository linting and commit rules instead.
If you are an automated agent making changes: run the full test suite locally (pnpm exec vitest) and ensure lint + format pass before proposing a commit. When in doubt, open a short PR with a clear testing checklist.
- For navigating/exploring the workspace, invoke the
nx-workspaceskill first - it has patterns for querying projects, targets, and dependencies - When running tasks (for example build, lint, test, e2e, etc.), always prefer running the task through
nx(i.e.nx run,nx run-many,nx affected) instead of using the underlying tooling directly - Prefix nx commands with the workspace's package manager (e.g.,
pnpm nx build,npm exec nx test) - avoids using globally installed CLI - You have access to the Nx MCP server and its tools, use them to help the user
- For Nx plugin best practices, check
node_modules/@nx/<plugin>/PLUGIN.md. Not all plugins have this file - proceed without it if unavailable. - NEVER guess CLI flags - always check nx_docs or
--helpfirst when unsure
- For scaffolding tasks (creating apps, libs, project structure, setup), ALWAYS invoke the
nx-generateskill FIRST before exploring or calling MCP tools
- USE for: advanced config options, unfamiliar flags, migration guides, plugin configuration, edge cases
- DON'T USE for: basic generator syntax (
nx g @nx/react:app), standard commands, things you already know - The
nx-generateskill handles generator discovery internally - don't call nx_docs just to look up generator syntax