Skip to content

Prepare the package for publishing to npm - #1

Merged
ScottMorris merged 5 commits into
mainfrom
chore/prepare-npm-publish
Oct 15, 2026
Merged

ScottMorris merged 5 commits into
mainfrom
chore/prepare-npm-publish

Conversation

@ScottMorris

@ScottMorris ScottMorris commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Publishable package. The tarball now includes LICENSE-APACHE and LICENSE-MIT, and scripts/check-package.mjs (run in CI and in the release) fails when a required file is missing or source, demo or asset files would be published.
  • Works outside a bundler. Every relative import in src now names its .js file. The built package previously failed to load under plain Node ESM (ERR_MODULE_NOT_FOUND on ./types), which a smoke install of the packed tarball exposed. src/imports.test.ts guards it.
  • CI and release workflows, following undertone: CI builds, tests, lints, checks formatting and the package, and builds the demo. The release workflow verifies the tag against package.json, packs and checks the tarball, attaches it to a GitHub release and publishes with provenance after approval on the npm-publish environment. A manual run with release_draft skips the npm step.
  • Release notes and Dependabot. .github/release.yml sorts notes by label. .github/dependabot.yml is set up for npm and github-actions but switched off (open-pull-requests-limit: 0), like jar.
  • Lockfile and docs. bun.lock is in lockfile format 1 (bun 1.3, as undertone uses). AGENTS.md documents releasing, and README.md gets an npm badge and the tested three versions.

Maintainer-facing changes

  • Repository settings done alongside this PR: an npm-publish environment with a required reviewer, the same as undertone. The organisation NPM_TOKEN secret is visible to all repositories, so no change was needed.
  • The version stays 0.0.0. The first release follows the chore/release-v<version> flow in AGENTS.md.

Known limitations

  • The npm-publish approval and the real npm publish have not been exercised. The look and smoothness of the moods have only been checked in software-rendered screenshots, not on a real GPU.
  • One test failed once earlier while the machine was heavily loaded and could not be reproduced in 25 further runs (16 in parallel), so no change was made for it.
  • The README hero uses a relative path like undertone's; npm resolves it from the repository field.

Test plan

  • bun install --frozen-lockfile
  • bun run test (163 tests; also tested against three 0.186.1 in a scratch copy)
  • bun run lint and bun run format:check
  • bun run build and bunx vite build demo
  • node scripts/check-package.mjs; npm pack --dry-run lists both licences and the readme
  • Smoke install of the packed tarball with three: both entry points import under Node ESM, and the types resolve under nodenext
  • CI is green on this pull request
  • After merge: run the Release workflow manually with release_draft set to true

**Summary**

- Relative imports in `src` now end in `.js` (and `./fallback.css.js` for the fallback stylesheet module), so the built package loads under plain Node ESM and `moduleResolution: nodenext`.

**Why**

- The published `dist` had extensionless imports such as `./types`, which bundlers resolve but Node refuses (`ERR_MODULE_NOT_FOUND`). A smoke install of the packed tarball showed `import('@liminal-hq/jax')` failing outside a bundler.

**Details**

- A mechanical change across 18 files; `tsc` with `moduleResolution: bundler` accepts `.js` for `.ts` sources, so nothing else changes.
- `src/imports.test.ts` fails if a relative import ever loses its extension.

**Validation**

- `bun run test`, `bun run lint`, `bun run format:check` and `bun run build` pass; the packed tarball imports and type-checks under Node ESM and `nodenext`.
**Summary**

- The tarball now includes `LICENSE-APACHE` and `LICENSE-MIT`, and `scripts/check-package.mjs` fails when a required file is missing or source, demo or asset files would be published.

**Why**

- npm only adds a file named exactly `LICENSE` on its own, so both licences were missing from the package.

**Details**

- `package.json`: `files` lists the licences, `description` matches the GitHub description, more `keywords`, and a `check:package` script.
- `bun.lock` is regenerated in lockfile format 1 (bun 1.3), the same format `undertone` uses, so CI's `--frozen-lockfile` install accepts it.
- Version stays `0.0.0`; the release bump goes on its own `chore/release-v<version>` branch.

**Validation**

- `npm pack --dry-run` lists both licences and `README.md`; `bun install --frozen-lockfile` passes.
…dabot config

**Summary**

- `ci.yml` builds, tests, lints, checks formatting and the package contents, and builds the demo on pull requests and pushes to `main`.
- `release.yml` follows `undertone`: a `v*` tag or a manual run verifies the tag against `package.json`, packs and checks the tarball, attaches it to a GitHub release and publishes to npm with provenance after approval on the `npm-publish` environment.

**Details**

- A manual run with `release_draft` set to true skips the npm step, so the pipeline can be exercised without publishing.
- `.github/release.yml` sorts release notes by label, excluding `skip-changelog` and `internal`.
- `.github/dependabot.yml` is set up for `npm` and `github-actions` but switched off with `open-pull-requests-limit: 0`; raise the limits to turn it on.

**Notes**

- The `npm-publish` environment and the organisation `NPM_TOKEN` secret are repository settings, not part of this change.
**Summary**

- `AGENTS.md` describes releasing (bump on `chore/release-v<version>`, tag or run the workflow, approve `npm-publish`) and the dormant Dependabot config; `README.md` gets an npm version badge and the `three` versions tested (0.160 and 0.186).
@ScottMorris ScottMorris added build Build tooling and packaging updates chore Maintenance and housekeeping ci Continuous integration workflows and checks labels Oct 15, 2026
… the input out of the shell

**Summary**

- The release workflow now compares the tag with the version in `package.json` before it creates a GitHub release, for tag pushes and manual runs alike.
- The manual `release_tag` input reaches the script through `env` instead of being interpolated into it.

**Why**

- The version check only ran for tag pushes, so a manual run with `release_tag: v9.9.9` could attach and publish the `0.0.0` package under the wrong release.
- Interpolating `${{ inputs.release_tag }}` into the script let a crafted value run commands in a job holding `contents: write`.

**Details**

- The tag must also match `^v[0-9A-Za-z._-]+$` before use, and the check moved into the "Resolve release tag" step so nothing is created when it fails.
- The old push-only check in the verify job is removed because the earlier step covers it.

**Validation**

- Ran the step's script for a matching tag push, a mismatched push, a manual run with no tag, a mismatched manual tag, a quote-injection value and a non-`v` tag: only the two matching cases succeed.
@ScottMorris
ScottMorris merged commit e09ff9e into main Oct 15, 2026
5 checks passed
@ScottMorris
ScottMorris deleted the chore/prepare-npm-publish branch October 15, 2026 15:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build Build tooling and packaging updates chore Maintenance and housekeeping ci Continuous integration workflows and checks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant