LearnNewsExamplesServices
Frontmatter
id19418
titlenpm-publish.yml publishes without the dist bundles the engine imports
stateClosed
labels
bugaibuild
assigneesneo-opus-grace
createdAtOct 6, 2026, 1:08 PM
updatedAtOct 6, 2026, 2:16 PM
githubUrlhttps://github.com/neomjs/neo/issues/19418
authorneo-opus-grace
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtOct 6, 2026, 2:16 PM
milestonev13.2

npm-publish.yml publishes without the dist bundles the engine imports

neo-opus-grace
neo-opus-grace commented on Oct 6, 2026, 1:08 PM

Context

Found by the v13.2 release rehearsal: the operator asked on 2026-10-06 to double-check the release scripts before the cut. publish.mjs ends with gh release create, and that release event runs .github/workflows/npm-publish.yml: npm ci, then npm publish, on a fresh checkout of the tag.

The Problem

On dev at 94ae5deb00, a clean worktree ran exactly the workflow's steps: npm ci, then npm pack, which runs the same prepack/prepare lifecycle as npm publish. The results:

  • The tarball holds 0 dist/ files (5,985 files, 23.3 MB).
  • A scratch consumer installed from it fails on import('neo.mjs/buildScripts/util/templateBuildProcessor.mjs') with ERR_MODULE_NOT_FOUND … node_modules/neo.mjs/dist/parse5.mjs.
  • npm run check-package-contents on that tree exits 1 with "13 required entr(ies) missing from the npm tarball".

Control on the same tree, after npm run bundle-browser-deps (5.8 s):

  • the guard passes, "all 13 required entr(ies) present";
  • the tarball carries dist/{parse5,marked,mermaid}.mjs, dist/highlight/ and dist/monaco/;
  • the consumer import links.

The published 13.1.0 tarball also holds 0 dist/ files, but the re-includes landed after it (#17957, 2026-08-31). That makes 13.2.0 the first release meant to carry the bundles, and its notes say so in 📦 An Engine package a consumer can build. Published as the workflow stands, 13.2.0 breaks the dist/esm build of every consumer that installs it, along with any module that imports parse5 or marked at module scope. An npm version cannot be republished, so the fix would be a 13.2.1.

The Architectural Reality

  • Design authority: .npmignore, the authoritative header since #19223: "src/functional/util/HtmlTemplateProcessor.mjs imports this bundle by relative path, and buildScripts/util/templateBuildProcessor.mjs imports it at module scope — so an installed engine needs it to RUN the dist/esm build". REQUIRED_ENTRIES in buildScripts/util/check-package-contents.mjs lists the same 13 entries (#17943, closed by #17957).
  • dist/ is gitignored. A checkout holds no bundles until npm run bundle-browser-deps runs: bundle-parse5, bundle-marked, bundle-mermaid, bundle-monaco, build-highlightjs.
  • Both PR-time checks run that step before they pack: check-package-contents.yml and consumer-runtime-build.yml. npm-publish.yml does not. The checks therefore measure a package the publisher never builds.
  • The premise that hid this is on record. #17957's session (56bc214a-5b55-41a2-848a-cdfa372abbcd) added the bundle step to the check because "npm publish runs build-all (which bundles parse5) first". Only publish.mjs runs build-all, in the operator's checkout. npm publishes from CI's fresh checkout.

The Fix

.github/workflows/npm-publish.yml, between npm ci and npm publish:

      - run: npm run bundle-browser-deps
      - run: npm run check-package-contents

The guard runs the real npm pack and fails on any missing required entry. A later drift then fails the publish before npm's immutable version is spent. A unit arm beside the guard's own spec (test/playwright/unit/buildScripts/checkPackageContents.spec.mjs) reads the workflow with js-yaml, following check-guard-ci-parity.spec.mjs, and pins both steps before npm publish in that job.

Acceptance Criteria

  • The publish job in npm-publish.yml runs npm run bundle-browser-deps and then npm run check-package-contents before npm publish. The release and workflow_dispatch triggers share that one job.
  • A unit arm fails when either step is removed or moved after npm publish. The PR shows the red by mutation.
  • (post-merge, at the cut) The 13.2.0 publish run prints the guard's OK line, and the registry tarball for neo.mjs@13.2.0 lists dist/parse5.mjs.

Out of Scope

  • Which bundles the package declares: that is #18823's decision. This ticket makes the publisher produce whatever .npmignore and REQUIRED_ENTRIES declare.
  • publish.mjs itself, and republishing 13.1.0.

Related

#17943 / #17957 (the re-includes), #18814, #19223 (prepack composition), #18872 (consumer-runtime check), #18823 (bundle-size decision), #14800 (13.2 notes; cut list).

Live latest-open sweep: checked the latest 20 open issues at 2026-10-06T11:07:47Z; no equivalent found. A2A claim sweep: last 30 messages (all read-states); no overlapping claim. MC sweep: "npm-publish.yml npm publish package missing dist parse5 bundle, bundle-browser-deps not run before publish, published tarball lacks bundles", 6 results; prior decision #17957 rested on the false premise quoted above; #18823 open and out of scope. Own-assignment sweep: 3 open (#19413, #15000, #14800), none overlapping.

Origin Session ID: 40a3c119-6419-4e87-9248-002c686708fe

Retrieval Hint: "release rehearsal npm-publish dist bundles parse5 13.2"