Skip to content

fix(types): emit CommonJS declarations for every module - #853

Open
tyler-reitz wants to merge 4 commits into
FirebaseExtended:mainfrom
tyler-reitz:fix/cjs-types
Open

tyler-reitz wants to merge 4 commits into
FirebaseExtended:mainfrom
tyler-reitz:fix/cjs-types

Conversation

@tyler-reitz

@tyler-reitz tyler-reitz commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

CommonJS TypeScript projects using module: node16 and skipLibCheck: false can import reactfire without TS1479.

Closes #850

Cause

dist/index.d.cts (the require types) was a copy of index.d.ts. Its relative imports resolved to .d.ts files, which TypeScript reads as ESM in this "type": "module" package. TS1479 first appears in 4.3.0, which added index.d.cts. 4.2.3 through 4.2.6 have no require types, so the same projects got TS7016 there instead.

Changes

  • build:types-cjs runs scripts/emit-cjs-types.mjs, which writes a .d.cts next to every dist/*.d.ts and rewrites relative ./x.js imports to ./x.cjs, so each resolves to its .d.cts sibling. It reads dist recursively, rewrites from, import() and side-effect imports, and throws on a relative import without .js.

No runtime change.

⚠️ CI still cannot catch this class of bug: attw is green on both the broken and the fixed output, because it resolves the require types without type-checking them.

Testing

Fixture: CommonJS package, module: node16, skipLibCheck: false, firebase 13.1.0, React 19.

Before After
TypeScript 5.0.4, 5.4.5, 5.9.3 12 TS1479 0 TS1479; all other errors identical
TypeScript 6.0.3 (repo version), packed tarball TS1479 no errors in reactfire's files
TypeScript 7.0.2 (lib: esnext), CJS and ESM consumers 12 TS1479 (CJS) 0 errors
attw, four resolution modes green green
  • The fixture asserts useUser's status type is the real union, so a resolution that falls back to any fails it (confirmed by replacing the types with any).
  • On TypeScript 5.9.3 the 2 errors left are Temporal in @firebase/firestore's own declarations. The 5.0 and 5.4 rows have more pre-existing errors; the fix removes only the TS1479s and leaves the rest unchanged.
  • The script's output for today's dist is byte-identical before and after 9aa5ad6; a test dist with a side-effect import and a nested declaration file is fully converted by the new script and not by the old one.
  • tsc -p tsconfig.test.json and oxlint are clean. No src change, so the emulator suite and docs were not re-run.

The require condition's index.d.cts was a copy of index.d.ts, so its
relative imports resolved to .d.ts files that TypeScript reads as ESM in
this "type": "module" package. CommonJS projects using module: node16
with skipLibCheck: false got TS1479 on each one.

build:types-cjs now writes a .d.cts next to every .d.ts and points
relative imports at the .cjs siblings. A new Verify package step
type-checks a CommonJS fixture against the packed tarball, since attw
passes on the broken output.

Closes FirebaseExtended#850

@armando-navarro armando-navarro 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.

Approving. The 12 TS1479 errors reproduce on main and are gone on this branch on every TypeScript version in your table, and apart from the .d.cts files and the script line in package.json, the two packed tarballs are identical.

Below are some optional considerations that don't need to hold up this PR.

1. Published 4.2.3 does not show the same errors (optional)

  • The description says "This predates 4.3.0: published 4.2.3 has the same errors".
  • 4.2.3 through 4.2.6 ship no index.d.cts and have no types entry under require.
  • The same CommonJS project gets TS7016 on those versions: "Could not find a declaration file for module 'reactfire'".
  • TS1479 first appears in published 4.3.0.

2. The pre-filled squash message describes the type-check step you removed (optional)

  • This repo pre-fills the squash message from the commit messages.
  • The message of 8352261 says "A new Verify package step type-checks a CommonJS fixture against the packed tarball".
  • c12ed6d removes that step.
  • I'd recommend dropping that sentence when you merge.
  • I could not find why the step was removed. What was the reason?

3. Two cases the script would leave unconverted (optional)

The description says a new file cannot slip through unconverted. I found two cases where a file would, with no error from the script. Neither can happen today, because src has no side-effect import and no subfolders.

  • Case 1: a file in src gains a side-effect import, such as import './telemetry.js'.
    • The script only rewrites paths that follow from or import(, so this path stays .js.
    • On TypeScript 6.0.3 a CommonJS project then gets TS1479 again.
    • On 5.9.3 that happens only with noUncheckedSideEffectImports on.
    • attw passes on that package, so CI would not show it.
  • Case 2: a declaration file ends up in a subfolder of dist.
    • The script only reads the top level of dist, so that file gets no .d.cts.
    • The import pointing at it is still rewritten to .cjs.
    • attw fails on that package, so CI does catch this one.
    • Passing { recursive: true } to readdirSync covers it, and the output for today's dist is byte-identical.

None of these needs another round from me.

emit-cjs-types now rewrites bare `import './x.js'` specifiers and reads
dist recursively, so neither case leaves a .d.ts without its .d.cts
counterpart. Output for today's dist is byte-identical.
@tyler-reitz

Copy link
Copy Markdown
Contributor Author

Thanks Armando.

  1. You're right. 4.2.3 through 4.2.6 ship no require types, so those projects got TS7016; TS1479 is new in 4.3.0. Body corrected.
  2. We're keeping custom CI checks to a minimum, so I dropped the step and ran the fixture by hand instead. I'll drop that sentence from the squash message.
  3. Both fixed in 9aa5ad6: side-effect imports are rewritten and dist is read recursively. Today's output is byte-identical, and a test dist with both cases is fully converted.

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.

index.d.cts makes CommonJS TypeScript projects using module: node16 fail with TS1479

2 participants