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
Depends on #402.
Context
PR #402 makes TKO publish per-package
.d.tsdeclarations for the first time. Once that lands, the published type/packaging surface becomes something we can regress silently β a future package could ship without atypesentry, with a brokenexportsordering, 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.ymlworkflow (which already runs on every PR to verify packages are publishable):publintβ validatespackage.jsonpackaging 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
verifyscript 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). Thenode16/nodenextTypeScript 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.jsonthat 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 againstattw --list-rulesat 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.jsonso the decision is explicit, not a silent suppression.Evidence β current attw matrix (run locally on #402's branch)
@tko/observable(CJS-default leaf)@tko/utils(type:module)@tko/build.knockout(build,.mjs)bundleris π’ 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/observableis a pre-existing JS packaging issue independent of types:dist/index.jscontains 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
publintruns per publishable package inpublish-check.ymlattw --packruns per publishable package inpublish-check.yml, with an.attw.jsondocumenting the ignored node16/nodenext rulesverifyscript for local pre-push@arethetypeswrong/cli0.18.3,publint0.3.21)Depends on #402.