# Developer instructions This document contains instructions for developers working on the `@api3/commons` package. ## Getting started ```sh # To install dependencies pnpm i # To run tests pnpm test ``` Inspect the `package.json` for more scripts. ## Build The package is published for both module systems. `pnpm run build` runs `tsc` twice: - `build:cjs` uses `tsconfig.cjs.json` and writes `dist/cjs`. It only sets `outDir`. `tsconfig.json` sets `module` to `nodenext`, which emits CommonJS because the root `package.json` has no `type` field. - `build:esm` uses `tsconfig.esm.json` and writes `dist/esm`. It sets `module` to `esnext`, and `moduleResolution` to `bundler`, which is the only module resolution that `esnext` accepts. Each step also writes a small `package.json` into its output directory that declares the module type. That file is what makes Node.js and TypeScript read the built files, and their declarations, with the right module system. It is also why the package needs no `.cjs` or `.d.cts` files. The `src` tree is not bundled. Every module keeps its own file, so that consumers can tree shake them apart. Run `pnpm run verify:dist` after a build. It starts a Node.js process for each module system and asserts that both entrypoints load and export the same names. ### Import extensions Every import in `src` must carry a `.js` extension, because ES modules do not resolve an extensionless path: - Relative imports point at the built file: `./async-storage.js`, `../promise-utils/index.js`. TypeScript maps the extension back to the `.ts` source, and Jest does the same through the `moduleNameMapper` in `jest.config.js`. - A deep import of a dependency without an `exports` field needs the extension too, which is why the lodash imports look like `lodash/isError.js`. `tsc` emits the specifier as written. Without the extension, neither `dist/esm` nor its declarations resolve for a consumer that uses ES modules. `pnpm run verify:dist` catches this, because Node.js fails to load the ES modules build. ## Release Releases are generated by the tag-and-release CI workflow job. To release a new version follow this step: 1. Merge a PR that bumps the `version` number within `package.json`. ## Adding new common utility 1. Create a new directory in `src` with the name of the utility. 2. Create `index.ts` file in the directory. This file will be the entry point for the utility. 3. Re-export the new utility from `universal-index.ts` or `node-index.ts` file. For example `export * from './my-utility/index.js';`. 4. Create a `README.md` with documentation. ## Testing the package locally It is preferred (simpler and faster) to test the package using the `file:` protocol. 1. Implement some changes and run `pnpm run build` to build the commons package. 2. In some other project, specify `file:`. For example: `"@api3/commons": "file:../commons"`. 3. Re-install the dependencies in the project (doesn't matter what package manager you use). For example: `pnpm i`. You can repeat the above steps as many times as you want. The package will be re-installed from the local directory with the locally built content. ### Using Verdaccio The common pattern is to move some part of implementation to commons and then use it in some other repo. It is valuable to see whether nothing broke in the process (before publishing the package). You can use [verdaccio](https://verdaccio.org/). 1. Start verdaccio (either as a docker service or directly on host machine). See: https://verdaccio.org/docs/installation. 2. Implement and commit your changes. You should have a clean working tree. 3. `pnpm version minor --no-git-tag-version` - Will bump the `package.json` version. Feel free to replace `minor` with a `path` or `major` if necessary. 4. `pnpm publish --access public --registry http://localhost:4873 --no-git-checks` - Will do the publishing to the local registry. It will disable git checks (which ensure that the working tree is clean). 5. You can now install the package in the target repository. Use `pnpm add @api3/commons --registry http://localhost:4873`. Tip: To unpublish a package from the local registry, you can just remove verdaccio storage. On my machine it can be done via `rm -rf $HOME/.local/share/verdaccio/storage`.