# Contributing to asn1js Thank you for your interest in contributing to `asn1js`! This document describes how to set up the project, which checks a change must pass, and how releases are made. ## Development Setup ### Prerequisites - Node.js `^22.18.0 || ^24.11.0 || >=26.0.0` for development, as required by tsdown and Vitest (CI uses Node.js 24). The published package supports `^20.19.0 || >=22.12.0`. - npm (the repository is locked with `package-lock.json`) ### Installation ```bash git clone https://github.com/PeculiarVentures/ASN1.js.git cd ASN1.js npm ci npm test npm run build ``` ## Project Layout - `src/index.ts` is the package's only entry point and must stay aligned with the package exports. It also registers the built-in BER encoder and the fast parser paths. - `src/.ts` contains one ASN.1 type per file (`Sequence`, `Integer`, `OctetString`, `UTCTime`, ...). Each type class registers itself in the shared type store (`src/TypeStore.ts`) in a `static` block, which is how `fromBER` creates typed results. - `src/parser.ts` contains `fromBER`; `src/schema.ts` contains `compareSchema` and `verifySchema`. - `src/internals/` contains the identification, length and value blocks, the BER encoder and helpers. They are not exported. - `build/` is generated by `npm run build` and is not committed. - `website/` is the [asn1js.org](https://asn1js.org) Docusaurus site, a separate npm package with its own lockfile. Its API pages are generated from `src/` with TypeDoc. The root lint, format, test and build tools ignore it. ## Development Workflow ### Code Quality Before opening a pull request, run: ```bash npm run lint # oxlint npm run format:check # oxfmt npm run typecheck # tsc --noEmit npm test # vitest, runs once and exits npm run build # tsdown, also checks the package with attw ``` Fix lint and formatting issues with `npm run lint:fix` and `npm run format`. Use `npm run coverage` to produce a coverage report. ### Testing - Use Vitest. - Put tests next to the code they cover as `src/.spec.ts` (public-API tests in `src/index.spec.ts`). - `src/parser.compat.spec.ts` runs the [ASN.1:2008 test suite](https://github.com/YuryStrozhevsky/asn1-test-suite) fixtures from the `asn1-test-suite` dev dependency. - Cover new behavior and bug fixes with tests. ### Build And Package Exports The package is ESM-only; there is no CommonJS build, and CommonJS consumers load it through `require(esm)`. tsdown bundles `src/index.ts` into a single `build/index.js` with a `build/index.d.ts` declaration, both with the LICENSE banner. Members marked `@internal` are left out of the declaration (`stripInternal`). The syntax target comes from `engines.node`; do not set `target` in `tsdown.config.ts`. The output stays bundled because importing the package has side effects inside it: the type classes register themselves in the type store, and `src/index.ts` registers the fast encoder and parser. With a single module, bundlers keep all of that whenever any export is used, so the package can still be published with `"sideEffects": false`. Don't switch to unbundled output without moving that registration. `exports` and the legacy `main`/`module`/`types` fields in `package.json` are generated by tsdown on every build and must not be edited by hand. To change the entry point, change `entry` in `tsdown.config.ts`, run `npm run build`, and commit the regenerated `package.json`. CI fails if the build changes `package.json`. Use named exports only (no `export default`) and no top-level `await`, so `require()` of the package keeps working on supported Node.js versions. The tsconfig sets `"useDefineForClassFields": false`, as in v3: a class field without an initializer is not emitted, so it doesn't reset a value that a base-class constructor already set (for example `GeneralizedTime.millisecond`). `vitest.config.ts` applies the same semantics to the tests. ### Commit Messages Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). Pull requests are squash-merged, so the pull request title becomes the commit message and must follow the same format. ```plain type(scope): description [optional body] [optional footer] ``` Types: `feat`, `fix`, `perf`, `refactor`, `docs`, `test`, `build`, `ci`, `chore`, `style`, `revert`. Mark breaking changes with `!` after the type or scope (for example `feat!: ...`) and/or a `BREAKING CHANGE:` footer. ### Pull Requests 1. Fork the repository and create a branch from `master`. 2. Make your changes, with tests. 3. Run the checks listed above. 4. Open a pull request with a Conventional Commit title. ## Code Style - Write TypeScript. - Follow the oxlint (`.oxlintrc.json`) and oxfmt (`.oxfmtrc.json`) configuration. Keep the lint config practical: prefer fixing the source over adding rules that only create churn, and disable a rule only when it fights the established style. - Keep changes small and localized. - Keep the code free of Node.js-specific APIs; the package runs in Node.js and browsers. - Don't add import-time side effects beyond the type-store and encoder/parser registration described above. - Document every public exported type and function with concise TSDoc. - Keep public exports and class identities stable; `pkijs`, `@peculiar/asn1-schema` and other packages depend on them and use `instanceof` checks, and breaking changes need a major release. ## Releases Releases are managed by [release-please](https://github.com/googleapis/release-please) (`release-please-config.json`, `.release-please-manifest.json`): 1. On every push to `master`, the **Publish** workflow first runs lint, format, typecheck, tests, and build (the **Test** workflow). Only when they pass does release-please open or update a `chore(release): vX.Y.Z` pull request that bumps `package.json`/`package-lock.json`. The version comes from the commits: a breaking change bumps major, `feat` bumps minor, and `fix`, `perf`, `build`, and `revert` bump patch. Other types do not trigger a release. 2. Merge the release pull request. release-please creates the `vX.Y.Z` tag and a GitHub Release with the notes from the pull request; the workflow then publishes that commit to npm via Trusted Publisher, with provenance, and moves the major tag (for example `v4`) when this is the newest release of that major. To retry a failed npm publish, run **Publish** manually from the `vX.Y.Z` tag. Set the optional `RELEASE_TOKEN` secret (PAT or GitHub App token) so the Test workflow runs on the release pull request. Release notes live only in [GitHub Releases](https://github.com/PeculiarVentures/ASN1.js/releases); there is no `CHANGELOG.md`. Only `feat`, `fix`, `perf`, `build`, `revert`, and breaking changes are listed. Force a version with a `Release-As: x.y.z` line in a commit body. To fix the notes for a merged change, add a `BEGIN_COMMIT_OVERRIDE` … `END_COMMIT_OVERRIDE` block with the corrected commit message to that pull request's description; edits to the release pull request description are overwritten when new commits land. The website is deployed separately: the **Build and publish website** workflow builds `website/` and publishes it to GitHub Pages on every push to `master`. ## License By contributing to this project, you agree that your contributions will be licensed under the BSD 3-Clause License.