Skip to content

feat(types): harden package shape (SD-2978)#3251

Merged
caio-pizzol merged 5 commits into
mainfrom
caio-pizzol/SD-2978-packaging-honesty
May 12, 2026
Merged

feat(types): harden package shape (SD-2978)#3251
caio-pizzol merged 5 commits into
mainfrom
caio-pizzol/SD-2978-packaging-honesty

Conversation

@caio-pizzol
Copy link
Copy Markdown
Contributor

@caio-pizzol caio-pizzol commented May 12, 2026

Make SuperDoc's package shape honest so CommonJS+TypeScript consumers stop hitting TS1471/TS1541 and so the manifest matches what the tarball actually ships.

Six things change:

  • Add .d.cts declaration shims for the three entries that advertise require/main (., ./types, ./super-editor). Generated by scripts/ensure-types.cjs from the ESM .d.ts using the TypeScript compiler API plus with { "resolution-mode": "import" } import attributes. exports[*].types becomes nested { import, require } for those three entries.
  • Add a prepack/postpack sanitize step that strips the source condition from the published package.json (the source manifest keeps it for workspace dev, since tsconfig.base.json resolves through customConditions: ["source"]).
  • Remove unpkg and jsdelivr manifest fields. They pointed at dist/superdoc.min.js, which the build doesn't emit.
  • Reorder ./super-editor's conditions so types comes first, per publint.
  • Add tests/consumer-typecheck/package-shape-gate.mjs that runs publint --strict and @arethetypeswrong/cli --pack against the packed tarball. Wired into PR CI, release-superdoc.yml, and release-stable.yml. The stable lane is what publishes the npm latest tag, so gating only release-superdoc.yml would have left the most-consumed dist-tag unverified.
  • Add tests/consumer-typecheck/src/imports-cjs.cts with AssertNotAny<T> checks for require('superdoc'), require('superdoc/types'), require('superdoc/super-editor') under node16 / nodenext.

The audit needed a small follow-up to read the new nested types: { import, require } shape (it previously assumed string).

Behavior change for bare CDN URLs. Removing the unpkg/jsdelivr manifest fields means bare URLs like https://cdn.jsdelivr.net/npm/superdoc/ (no file path) now fall back to main (the CommonJS bundle) instead of pointing at superdoc.min.js. Direct CDN URLs that include the full file path (the form AGENTS.md documents and that cdn-smoke-test exercises) continue to work. The previous fields pointed at a path the pack:es build doesn't reliably emit, so the change brings the manifest into honest agreement with what ships.

Note for consumers on TypeScript <5.3: the with { "resolution-mode": "import" } import-attribute syntax used by the generated .d.cts shims requires TS 5.3+ (Nov 2023). Anything older won't parse the shim.

Verified:

  • node tests/consumer-typecheck/typecheck-matrix.mjs: 57 passed, 0 failed
  • node tests/consumer-typecheck/package-shape-gate.mjs: publint clean, attw all 🟢 (CJS, ESM, node10, bundler) for all 3 CJS entries
  • node tests/consumer-typecheck/deep-type-audit.mjs: 1799 findings, exit 0
  • Source manifest restored after pnpm pack (backup file gone, source conditions intact)

Linear: SD-2978

The original SD-2978 commit only added consumer matrix + package-shape
gates to release-superdoc.yml. Between sessions, release-stable.yml
landed as the new central orchestrator for the npm `latest` publish
lane, and SuperDoc's stable releases now route through that workflow
instead of release-superdoc.yml (which is now `@next` only).

Without this patch the stable lane would publish without the matrix or
publint/attw gates running, defeating the purpose of "package-shape
honest in CI" because the most-consumed dist-tag would still be
unverified at publish time.

Adds the same three steps (matrix, deep-type-audit, package-shape-gate)
between Build packages and the orchestrator step in release-stable.yml.
…} (SD-2978)

The deep audit assumed `entry.types` is always a string. SD-2978's
manifest changes nest it as `{ import: '...d.ts', require: '...d.cts' }`
for the three entries that publish CJS. The audit threw
ERR_INVALID_ARG_TYPE on `path.resolve(root, entry.types)` when entry.types
was an object.

Add a small helper that picks the ESM target from either shape (string
or condition object). Walking the .d.ts side is sufficient because the
.d.cts is a generated shim of the same surface.

Verified: audit now exits 0 with the same 1799 findings as pre-fix.
@caio-pizzol caio-pizzol requested a review from a team as a code owner May 12, 2026 18:36
@linear
Copy link
Copy Markdown

linear Bot commented May 12, 2026

SD-2978

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e393bccef

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread tests/consumer-typecheck/package-shape-gate.mjs
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

caio-pizzol and others added 2 commits May 12, 2026 16:18
…:es (SD-2978)

Code review found that `pnpm run pack` was broken by the new
prepack/postpack lifecycle. pnpm wraps prepack/postpack around scripts
named exactly `pack`, and the user `pack` script itself invokes
`pnpm pack` which triggers a second prepack. The inner prepack hit the
"backup exists" guard and exited 1, the outer postpack was skipped, and
the workspace was left with `package.json` mutated and
`.package.json.prepack-backup` orphaned.

Two changes:

1. Make `prepare` re-entrant. If the backup file exists AND the current
   manifest already looks sanitized (no `source` conditions, no `unpkg`
   or `jsdelivr` fields), no-op so the inner prepack falls through and
   the inner postpack can restore cleanly. If the backup exists but the
   manifest is NOT sanitized, fail loudly with a clear message — that
   means the workspace is in an inconsistent state from a previous
   failed pack and the developer needs to clean up. `restore` was
   already idempotent (no-op when backup missing).

2. Route `pack:local` through `pack:es` directly. Both ultimately do
   the same thing, but going through `pack:es` (whose name does not
   collide with the lifecycle trigger) avoids the double-fire on the
   common local-pack path.

Verified with a synthetic harness covering 5 cases: clean run,
double-fire (outer + inner), failed run (state inspection), retry after
failure (self-heal), inconsistent state (loud refusal). All pass.

Verified live in the worktree:
- pnpm run pack:es: tarball created, manifest restored, no orphan backup
- pnpm run pack: same (was broken before this commit, now works)
@caio-pizzol caio-pizzol merged commit 6d65d9a into main May 12, 2026
64 checks passed
@caio-pizzol caio-pizzol deleted the caio-pizzol/SD-2978-packaging-honesty branch May 12, 2026 19:49
@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 12, 2026

🎉 This PR is included in @superdoc-dev/react v1.2.0-next.122

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 12, 2026

🎉 This PR is included in @superdoc-dev/mcp v0.3.0-next.79

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 12, 2026

🎉 This PR is included in vscode-ext v2.3.0-next.124

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 12, 2026

🎉 This PR is included in superdoc-cli v0.8.0-next.96

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 12, 2026

🎉 This PR is included in superdoc-sdk v1.8.0-next.78

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot Bot commented May 12, 2026

🎉 This PR is included in superdoc v1.30.0-next.78

The release is available on GitHub release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants