Skip to content

Generate and publish TypeScript declarations#153

Open
myabc wants to merge 3 commits into
bigskysoftware:mainfrom
myabc:publish-types
Open

Generate and publish TypeScript declarations#153
myabc wants to merge 3 commits into
bigskysoftware:mainfrom
myabc:publish-types

Conversation

@myabc

@myabc myabc commented Jun 17, 2026

Copy link
Copy Markdown

Fixes #152.

Idiomorph already authors its types as JSDoc in src/idiomorph.js, but they were never emitted or shipped — so consumers have to hand-maintain their own idiomorph.d.ts. This wires up generation and publishing.

Changes

Use @callback to preserve Morph/NoOp signatures

  • @typedef {Function} Morph / NoOp collapsed to bare Function in generated declarations, dropping the morph() call signature. @callback keeps it.
  • This surfaced a latent inaccuracy: morph() actually returns Promise<Node[]> | Node[] (the restoreFocus path is async), not undefined | Node[]@returns corrected to match.

Generate and publish TypeScript declarations

  • Add a types npm script that emits module-shaped .d.ts from the ESM bundles (dist/idiomorph.esm.js / idiomorph-ext.esm.js). Generating from the ESM build (which has export {Idiomorph}) yields a proper module declaration; compiling src directly emits a global declare var that won't resolve for import { Idiomorph } from "idiomorph".
  • Wire types into the dist build, after gen-modules.
  • Expose declarations via package.json: top-level types, a types condition on the . and ./htmx exports, and dist/*.d.ts in files.
  • Scope the dist copy to src/*.js and gitignore src/*.d.ts so a stray declaration can't leak into the published bundle.
  • tsconfig stays noEmit so typecheck remains a pure check.

Verification

A consumer can now:

import { Idiomorph, Config } from "idiomorph";

const cfg: Config = { morphStyle: "innerHTML" };
const nodes = Idiomorph.morph(document.body, "<div/>", cfg);
// morphStyle is validated; morph()'s signature and return type are intact

npm run typecheck and npm run format:check both pass.

myabc added 2 commits June 18, 2026 00:54
@typedef {Function} collapsed these to bare Function in generated
declarations, losing the morph() call signature. @callback keeps it.

This also surfaced that morph() actually returns Promise<Node[]> | Node[]
(the restoreFocus path is async), not undefined | Node[]; @returns fixed.
JSDoc types lived in src but were never shipped, forcing consumers to
hand-maintain their own idiomorph.d.ts.

- Add a 'types' script emitting module-shaped .d.ts from the ESM bundles
  (compiling src directly yields a global 'declare var' instead).
- Wire it into 'dist'; expose via package.json 'types' + export conditions
  and ship dist/*.d.ts.
- Scope the dist copy to src/*.js and gitignore src/*.d.ts so stray
  declarations can't leak; revert tsconfig to noEmit.
Copilot AI review requested due to automatic review settings June 17, 2026 22:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds first-class TypeScript declaration publishing for Idiomorph by generating module-shaped .d.ts files from the ESM bundles and wiring them into the package’s publish surface (types, exports, and files). It also updates JSDoc callback typedefs/signatures to preserve call signatures in the generated declarations.

Changes:

  • Switch JSDoc typedefs for Morph/NoOp to @callback and correct morph()’s return type to Promise<Node[]> | Node[].
  • Add an npm types build step to emit and ship dist/*.d.ts, and expose them via package.json (types + exports types conditions).
  • Tighten dist packaging (copy only src/*.js) and ignore accidental src/*.d.ts.

Reviewed changes

Copilot reviewed 2 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/idiomorph.js Updates JSDoc to generate correct callable types and return type for morph().
package.json Adds types generation script; publishes declarations via types/exports and includes .d.ts in files.
.gitignore Prevents accidental src/*.d.ts from being committed/published.
dist/idiomorph.js Regenerated dist bundle reflecting JSDoc/signature and small runtime changes.
dist/idiomorph.esm.js Regenerated ESM bundle used as the source for declaration generation.
dist/idiomorph.cjs.js Regenerated CJS bundle for exports.require.
dist/idiomorph.amd.js Regenerated AMD bundle.
dist/idiomorph.min.js Regenerated minified browser bundle.
dist/idiomorph-ext.js Regenerated htmx extension bundle.
dist/idiomorph-ext.esm.js Regenerated ESM htmx extension bundle used for declaration generation.
dist/idiomorph-ext.min.js Regenerated minified htmx extension bundle.
dist/idiomorph.d.ts New generated declaration file for the main entry.
dist/idiomorph-ext.d.ts New generated declaration file for the ./htmx entry.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread package.json
@myabc

myabc commented Jun 17, 2026

Copy link
Copy Markdown
Author

This could be made more robust by adding an npm lifecycle hook so stale/missing types can't get published:

  "prepublishOnly": "npm run dist"

since npm publish runs that automatically first dist/ (incl. .d.ts) is always fresh in the tarball. You could go even further and add a CI check that git diff --exit-code dist is clean, so a forgotten rebuild fails the PR instead of shipping stale output.

The single ESM-shaped .d.ts described a named 'Idiomorph' export, but the
CJS bundle does 'module.exports = Idiomorph' (require() returns the object
directly). CJS/node16 consumers got types that passed yet crashed at
runtime.

Generate a separate idiomorph.d.cts ('export = Idiomorph', with a namespace
merge exposing Config et al) from the CJS bundle, and split the '.' export
into per-condition types: idiomorph.d.ts for import, idiomorph.d.cts for
require. Add dist/*.d.cts to files.

Now a CJS named import correctly fails to typecheck (TS2595) instead of
compiling to a runtime crash; 'import x = require("idiomorph")' works.
@myabc

myabc commented Jun 17, 2026

Copy link
Copy Markdown
Author

#122 would simplify this ofc - and allow us to drop the last commit.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Idiomorph not published with types

2 participants