Skip to content

fix(build): ship type declarations in the published package#40

Open
dipdapdop wants to merge 4 commits into
kinde-oss:mainfrom
dipdapdop:fix/ship-dts-declarations
Open

fix(build): ship type declarations in the published package#40
dipdapdop wants to merge 4 commits into
kinde-oss:mainfrom
dipdapdop:fix/ship-dts-declarations

Conversation

@dipdapdop

Copy link
Copy Markdown

Explain your changes

Fixes #39.

Problem

  • @kinde/webhooks ships no .d.ts files despite package.json advertising "types": "dist/main.d.ts", so TypeScript consumers get Cannot find module '@kinde/webhooks' or its corresponding type declarations.

Root cause

vite-plugin-dts computes each declaration's output path relative to Vite's root, which was "lib".
With outDir: "../dist" the .d.ts files were written under lib/dist/ (outside the configured outDir), so the plugin's strictOutput (on by default) silently skipped writing them.

  • The package therefore shipped only the JS bundles.
  • The tsc && build step also emits nothing because tsconfig sets noEmit.

Fix

Remove the left-over root: "lib" and the unused src alias (nothing depends on them:
no index.html, no public/, no "src" imports) and set the build outDir to "dist".

Declarations then resolve into the repo-root dist/ that files: ["dist"] publishes.
lib/main.ts already re-exports the event types, so consumers import from the package root.

Verification

  • npm pack now includes dist/main.d.ts, dist/lib/main.d.ts and
    dist/lib/types.d.ts.
  • A consumer importing event types from @kinde/webhooks (package root, not a
    dist/ deep path) type-checks cleanly.
  • The JS bundles are byte-for-byte identical to the previous config (matching
    SHA-256 for webhooks.js, webhooks.cjs and both jsrsasign chunks), so
    runtime output is unchanged.
  • The existing suite passes (11/11) and prettier --check is clean.
  • Adds a dist smoke suite (pnpm test:dist) that imports the built bundle and
    type-checks a root import, guarding this regression. Verified it fails on the
    pre-fix config (TS2307) and passes on the fix.

Checklist

🛟 If you need help, consider asking for advice over in the Kinde community.

The published package contained no type declaration files, even though
package.json advertises "types": "dist/main.d.ts" (issue kinde-oss#39). TypeScript
consumers got "Cannot find module '@kinde/webhooks' or its corresponding
type declarations".

Root cause: vite-plugin-dts computes each declaration's output path
relative to Vite's root, which was set to "lib". With outDir "../dist"
that placed the .d.ts files under lib/dist, outside the configured
outDir, so the plugin's strictOutput (on by default) silently skipped
writing them. The package therefore shipped only the JS bundles. The
"tsc &&" build step also emits nothing because tsconfig sets noEmit.

Fix: remove the vestigial root "lib" and the unused "src" alias (no
index.html, public dir, or "src" imports depend on them) and set the
build outDir to "dist". Declarations then resolve into the repo-root
dist that files ["dist"] publishes, and lib/main.ts already re-exports
all the event types so consumers import from the package root.

Verification:
- npm pack now includes dist/main.d.ts, dist/lib/main.d.ts and
  dist/lib/types.d.ts.
- A consumer importing the event types from "@kinde/webhooks" (package
  root, not a dist deep path) type-checks cleanly.
- The JS bundles are byte for byte identical to the previous config
  (matching SHA-256 for webhooks.js, webhooks.cjs and both jsrsasign
  chunks), so runtime output is unchanged.
- The existing test suite passes (11/11) and prettier --check is clean.

Closes kinde-oss#39
Adds a suite that exercises the BUILT dist/ rather than the lib/ source,
to guard the packaging regression fixed in the previous commit (kinde-oss#39).
The existing suite imports from ./main, so it could not catch declarations
being absent from the published tarball.

The new suite asserts, against the real shipped files:
- the JS entry points named by package.json main/module exist, the ESM
  bundle imports, and the runtime surface works (decodeWebhook is callable,
  WebhookEventType resolves, decodeWebhook("") returns null);
- the declaration file named by package.json types exists, and a consumer
  importing the event types from the package root type-checks (a spawned
  tsc run that reproduces the exact kinde-oss#39 failure mode).

Wiring:
- test/dist-smoke.test.ts holds the suite (outside lib/, so the tsc build
  step does not compile it).
- vitest.config.ts excludes test/ from the default `test` script, so the
  source suite still runs without a build.
- vitest.dist.config.ts scopes the dist suite, run via the new `test:dist`
  script which builds first.

Verified the suite fails on the pre-fix config (missing dist/main.d.ts and
the consumer tsc errors with TS2307) and passes on the fixed config.
@dipdapdop dipdapdop requested review from a team as code owners June 23, 2026 12:48
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@dipdapdop, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 48 minutes and 5 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 773f3614-c090-4511-a417-bb3ab18b8d09

📥 Commits

Reviewing files that changed from the base of the PR and between 0537a9e and 4c83f3f.

📒 Files selected for processing (1)
  • test/dist-smoke.test.ts

Walkthrough

The Vite build config is corrected to output into dist instead of ../dist and the dts plugin is reconfigured with include/exclude globs rather than a plugin-level outDir. Two new Vitest config files split the default source suite from a separate dist smoke suite, and test/dist-smoke.test.ts is added to validate JS entrypoints, ESM exports, and .d.ts type-checking end-to-end.

Changes

Dist declaration fix and smoke test

Layer / File(s) Summary
Vite build output and dts plugin config
vite.config.ts
build.outDir changes from ../dist to dist; dts plugin drops its own outDir and gains include: ["lib"] and exclude globs for test files, consolidating output directory control in build.outDir.
Vitest config split for dist smoke tests
vitest.config.ts, vitest.dist.config.ts
vitest.config.ts adds a default export that excludes node_modules, dist, and test/ from the main run. vitest.dist.config.ts adds a new config that targets only test/**/*.test.ts for the dist smoke suite.
Dist artifact smoke test suite
test/dist-smoke.test.ts
New Vitest suite reads package.json for main, module, and types entry paths, guards on a built dist/ directory, asserts JS entrypoint files exist, dynamically imports the ESM bundle to verify decodeWebhook and WebhookEventType exports, and spawns tsc against a temporary consumer project to type-check the built declarations end-to-end.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(build): ship type declarations in the published package' directly and specifically describes the main change: fixing the build configuration to properly ship type declaration files in the published package.
Description check ✅ Passed The description is well-related to the changeset, explaining the problem (missing .d.ts files), root cause (vite-plugin-dts path resolution issue), the fix applied, and verification steps confirming the solution works.
Linked Issues check ✅ Passed All requirements from issue #39 are addressed: vite.config.ts removes vestigial root and src alias settings, sets outDir to 'dist' enabling proper .d.ts emission, test suite verifies declarations ship and type-check correctly, and JS bundles remain unchanged.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the build configuration and adding verification tests for the .d.ts shipping issue. No unrelated refactoring, feature additions, or out-of-scope work is present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@dipdapdop

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@dtoxvanilla1991 dtoxvanilla1991 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good work.

Comment thread vitest.config.ts
// The default `test` script runs the source suite (lib/). The dist smoke
// suite (test/dist-smoke.test.ts) requires a prior build and is run
// separately via the `test:dist` script, so it is excluded here.
exclude: ["**/node_modules/**", "**/dist/**", "test/**"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Excluding test/** from the default suite is reasonable, but the current workflow still only runs pnpm build and pnpm test:coverage. That means this new dist smoke suite never runs in CI, so the declaration-packaging regression this PR is trying to guard can slip back in without a failing PR build. Please wire pnpm test:dist into the PR workflow or fold this check into an automated path.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@dtoxvanilla1991 Yeah good point, ta. Hopefully fixed 0537a9e.

I've added a step to build-test-ci.yml that runs the dist smoke straight after the build:

- run: pnpm build
- name: Dist smoke (declaration-packaging regression guard)
  run: pnpm exec vitest run --config vitest.dist.config.ts
- run: pnpm test:coverage

I run the dist config directly rather than the test:dist script so it reuses the build from the step above instead of rebuilding. It sits before test:coverage so a packaging regression will fast-fail the build. The default suite already excludes test/** and dist/**, so the two suites stay separate and the smoke runs only once.

@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

The dist smoke suite (test/dist-smoke.test.ts, via vitest.dist.config.ts)
asserts the published dist/ ships its .d.ts and that a consumer can import
the declared types. As reviewed on the PR, CI only runs build and
test:coverage, so that suite never executed in CI and the
declaration-packaging regression this PR fixes could silently return
without failing a PR build.

Add a step after the existing build that runs the dist config directly
(pnpm exec vitest run --config vitest.dist.config.ts) rather than the
test:dist script, so it reuses the build from the step above instead of
rebuilding. No untrusted workflow input is referenced.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/dist-smoke.test.ts`:
- Around line 109-112: The current implementation uses execFileSync to invoke
the tsc binary directly from node_modules/.bin/tsc, which fails on Windows due
to npm creating .cmd wrapper shims. Instead of executing the tsc binary
directly, invoke TypeScript via Node by passing "node" as the command to
execFileSync and the path to the TypeScript CLI as the first argument in the
arguments array. This ensures cross-platform compatibility by using Node to
execute the TypeScript package rather than relying on shell-specific binary
wrappers.
- Around line 51-53: The dynamic import() calls at lines 52 and 59 use raw
filesystem paths which cause Windows compatibility failures because the drive
letter is interpreted as a URL protocol scheme. Convert both filesystem paths
created by join(repoRoot, pkg.module) to file:// URLs using Node.js's
pathToFileURL utility from the url module before passing them to the import()
function. This ensures the paths are properly formatted for cross-platform
compatibility.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 595a424d-0c24-4e00-95f1-f9413d3e91ca

📥 Commits

Reviewing files that changed from the base of the PR and between 8f0afca and 0537a9e.

⛔ Files ignored due to path filters (2)
  • .github/workflows/build-test-ci.yml is excluded by !**/*.yml
  • package.json is excluded by !**/*.json
📒 Files selected for processing (4)
  • test/dist-smoke.test.ts
  • vite.config.ts
  • vitest.config.ts
  • vitest.dist.config.ts

Comment thread test/dist-smoke.test.ts
Comment thread test/dist-smoke.test.ts Outdated
Two cross-platform fixes from review:

1. Dynamic import() was passed a bare filesystem path. On Windows a
   drive-letter path is parsed as a URL scheme and throws
   ERR_UNSUPPORTED_ESM_URL_SCHEME. Wrap the path in pathToFileURL(...).href
   via a small importPath() helper at both call sites.

2. The tsc consumer-typecheck spawned node_modules/.bin/tsc. On Windows that
   shim is a .cmd wrapper that execFileSync (no shell) cannot resolve. Invoke
   node against the typescript package's JS bin entry instead, which is
   OS-neutral.

Verified locally: build + the dist config run green (5/5). No untrusted input.
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.

Bug: @kinde/webhooks ships no type declarations (.d.ts) despite "types": "dist/main.d.ts"

2 participants