Keep instructions concise, only add non-obvious information. Proactively update AGENTS.md to prevent future mistakes.
- Variables and functions should have clear, descriptive names that reflect their purpose and behavior and not e.g. their data types.
- Code should be organized so that things that will most likely change together are located near each other, instead of e.g. grouping solely by technical categories.
- TypeScript + Rust hybrid: Rust code in
rust/(bindings_napi, bindings_wasm, parse_ast crates) called vianative.jsandnative.wasm.js - Generated files: Files with "Do not edit this file directly" comments (e.g.,
src/ast/bufferParsers.ts,src/ast/childNodeKeys.ts,src/ast/nodeIds.ts) are generated fromscripts/ast-types.tsviascripts/generate-ast-converters.tsand files that are imported in that file - Tests run against full artifact only—no unit tests to allow easy refactoring of internal APIs
- Test cases in
test/*/samples/are configured via_config.jsfiles; focus tests withsolo: true - See CONTRIBUTING.md "How to write tests" for test type selection (function/form/chunking-form/cli/etc.)
When adding/modifying functions that cross the JS-Rust boundary:
- Rust implementations: Update
rust/bindings_napi/src/lib.rs(Node native) andrust/bindings_wasm/src/lib.rs(WASM) - JS handover points (must have matching signatures):
native.js- Node native build (NAPI)native.wasm.js- Node WASM buildbrowser/src/wasm.ts- Browser WASM build
- Build process:
rollup.config.tsreplacesnative.jswithbrowser/src/wasm.tsfor browser builds; CI replaces it withnative.wasm.jsfor Node WASM builds - TypeScript definitions:
native.d.tsis auto-generated by napi-rs for NAPI bindings; WASM types are generated inwasm/bindings_wasm.d.ts - Testing builds (will also generate definitions):
- Node native:
npm run build:napi - Browser WASM:
npm run build:wasm - Node WASM:
npm run build:wasm:node
- Node native:
- Focus on performance, avoid unnecessary copying of data or AST loops, build the final buffer once
- When adding headers, reserve space for them once upfront to avoid the need to change all existing buffer references
browser/src/path.tsreplacesnode:pathin the browser build (wired viarollup.config.tsaliases)src/utils/relativeId.tsimportsrelativedirectly from../../browser/src/path(not viasrc/utils/path.ts) so it works in both buildssrc/utils/path.tsre-exports fromnode:path; for browser builds rollup.config.ts substitutesbrowser/src/path.tstransparently for all other imports
- Node build: Artifacts placed in
dist/(JavaScript +.nodenative modules) - Browser build: Artifacts placed in
browser/dist/; browser tests usebrowser/dist/rollup.browser.js - All tests import from these dist folders - tests run against the full built artifact only
npm run build:quick- Rebuild both JavaScript and Rust for Node build, copy to dist/npm run update:js- Rebuild only JavaScript for both Node (dist/) and browser (browser/dist/), then copy native to dist/; run this after any change tosrc/orbrowser/src/npm run update:napi- Rebuild only Rust NAPI, copy to dist/npm run build:copy-native- Copy Rust.nodefiles to dist/ (called internally by update commands)
npm run build- Full build: WASM + AST converters + NAPI (release) + JavaScript + copy nativenpm run build:napi- Build Rust NAPI bindings (use-- --releasefor optimized)npm run build:wasm- Build browser WASMnpm run build:js- Build JavaScript (Node and browser)
- Rust changes only:
npm run update:napithennpm run test:only - JavaScript changes only:
npm run update:jsthennpm run test:only - AST schema changes:
npm run build:ast-convertersto regenerate TypeScript and Rust files - Testing changes:
npm run build:quickthennpm run test:only
- Always test edge cases, especially in core logic or build/test infrastructure
- Test names and descriptions use clear, descriptive language of the expected behavior, e.g. "description: 'does X when Y happens"
- Directory based tests
- Should be preferred for tests if possible
- Reside in directories in
test/<category>/samples - Each test has a
_config.jsfile with a description field and potentially other configuration options - The preferred way to work on a single or few of those tests is to add
solo: trueto the test's_config.jsfile first, and then run:npm run build:quick npm run test:quick - Each test category is run by a
test/<category>/index.jsfile - The available options for each test category are defined in
test/types.d.ts
- Traditional file based tests
- Are run directly by mocha using
describeanditblocks - Can be found in
test/misc/,test/hooksandtest/incremental
- Are run directly by mocha using
- function
- Will bundle a
main.jsfile next to the_config.jsfile and then run it - Has
node:assertinjected as a global variable in the bundled test code (main.js), allowing you to useassertdirectly without importing - In
_config.js, you must explicitly require assert:const assert = require('node:assert/strict');to use it in theexportsfunction or other config functions - Use when testing if bundled code still works (use inline assert in the code or the exports field in
_config.jsto make assertions) - Use when testing build errors, warnings, or plugin hooks
- Will bundle a
- form
- Will bundle a
main.jsfile and store the output either as an_actualdirectory with outputs for each format (es.js, cjs.js, amd.js, system.js, umd.js, iife.js) or an_actual.jsfile for a single format, compared with an existing_expecteddirectory or_expected.jsfile - The single
_actual.jsfile will be generated if an_expected.jsfile is present. Having a single file is preferred, so when writing a form test, start with writing the_expected.jsfile before running the test for the first time
- Will bundle a
- chunking-form
- Similar to form, but always generates all formats and produces a separate directory for each format; the entire directory is compared with an
_expecteddirectory - Use when multiple files are expected in the output
- Similar to form, but always generates all formats and produces a separate directory for each format; the entire directory is compared with an
- cli
- Use when running rollup as a command line tool
- Ignore style/linting issues in test sample files (
test/*/samples/) except for_config.jsfiles - Test samples intentionally violate best practices to test edge cases—do not flag style violations in these files
- Focus reviews on production code quality