Skip to content

Add published-type checks (attw + publint) to publish-check CI #403

Description

@brianmhunt

Context

PR #402 makes TKO publish per-package .d.ts declarations for the first time. Once that lands, the published type/packaging surface becomes something we can regress silently — a future package could ship without a types entry, with a broken exports ordering, or with a declaration that doesn't resolve. We should guard it in CI.

This is a follow-up to #402 — the checks validate the types that #402 introduces, so this only makes sense once #402 is merged.

Proposal

Add two standard tools to the publish-check.yml workflow (which already runs on every PR to verify packages are publishable):

  • publint — validates package.json packaging correctness (exports condition ordering, files, etc.). Resolution-mode-agnostic, near-zero false positives. Straightforward win.
  • @arethetypeswrong/cli (attw --pack) — validates that published declarations actually resolve.

Optionally also wire both into the local verify script so they're caught before a PR is even pushed.

Important: scope attw to TKO's actual audience

TKO is a browser MVVM framework; its consumers use bundlers (Vite/webpack/esbuild/Rollup → moduleResolution: bundler). The node16/nodenext TypeScript resolution mode is out of audience — a no-bundler Node+TS consumer is not a realistic TKO user.

Out of the box, attw fails on the node16/nodenext columns. We should add an .attw.json that ignores the node16-specific rules (e.g. false-cjs, false-esm, false-export-default, unexpected-module-syntax, internal-resolution-error, cjs-resolves-to-esm — exact names to be confirmed against attw --list-rules at implementation time) so the gate fails only on genuinely-relevant problems for bundler consumers (e.g. no-resolution, untyped-resolution). The ignored rules should be documented in .attw.json so the decision is explicit, not a silent suppression.

Evidence — current attw matrix (run locally on #402's branch)

Package node10 node16 (CJS) node16 (ESM) bundler
@tko/observable (CJS-default leaf) 🟢 🟢 🚭 UnexpectedModuleSyntax 🟢
@tko/utils (type:module) 🟢 👺 FalseESM + 🥴 InternalResolutionError 🥴 InternalResolutionError 🟢
@tko/build.knockout (build, .mjs) 🟢 ❗️ FalseExportDefault 🎭 FalseCJS 🟢

bundler is 🟢 everywhere — the audience that matters. The reds are confined to node16/nodenext, which is why they should be ignored rather than fixed.

Related (separate, pre-existing — not blocking this)

The 🚭 UnexpectedModuleSyntax on @tko/observable is a pre-existing JS packaging issue independent of types: dist/index.js contains ESM syntax but the package has no "type": "module", so Node treats it as CommonJS. It would affect a node16 ESM consumer at runtime. Orthogonal to the types work and to bundler consumers; worth its own issue if we ever decide to care about the Node-resolution path.

Acceptance

  • publint runs per publishable package in publish-check.yml
  • attw --pack runs per publishable package in publish-check.yml, with an .attw.json documenting the ignored node16/nodenext rules
  • The gate is green on the current package set (per the matrix above) and would fail a package that ships no/broken types
  • (optional) Both mirrored in the verify script for local pre-push
  • Confirm current tool versions at implementation time (latest at time of writing: @arethetypeswrong/cli 0.18.3, publint 0.3.21)

Depends on #402.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions