Skip to content

Emit plain .d.ts for libraries with outputExtensions (TS 7 nightly 20261029) - #41

Draft
NullVoxPopuli wants to merge 6 commits into
mainfrom
nvp/output-extensions
Draft

NullVoxPopuli wants to merge 6 commits into
mainfrom
nvp/output-extensions

Conversation

@NullVoxPopuli

@NullVoxPopuli NullVoxPopuli commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Important

One upstream blocker remains: microsoft/TypeScript#64828 (go-to-definition on a .gts specifier). Its test is marked todo, so CI is green. Everything else is done.

Libraries can now emit counter.d.ts instead of counter.d.gts.ts, so declarations/ matches dist/ file for file. This uses the new outputExtensions option from microsoft/TypeScript#64581, which fixes microsoft/TypeScript#64053.

Changes

  • examples/cli-library-v2: sets outputExtensions: { ".gts": ".js", ".gjs": ".js" } and rewriteRelativeImportExtensions: true. The emitted index.d.ts now says from './components/counter.js', and the ./* → ./declarations/*.d.ts export works for every module. I removed the README note saying it didn't.
  • README: new "Publishing a library" section. MIGRATING_FROM_TS6.md links the #64053 item to it.
  • Test: a new declaration-output-extensions fixture checks that Greeting.d.ts is emitted and Greeting.d.gts.ts is not.
  • Bumps every TS 7 pin from 7.1.0-dev.20260821.1 to 7.1.0-dev.20261029.1.
  • Root ember-source is now 7.5.0-beta.1. See the @extends section.
  • Rebased on v0.4.0.

Why the mapper's package.json doesn't set a default

typescript.contentMapper.outputExtensions can go in the mapper's package.json, but I didn't add it:

  • Any outputExtensions requires rewriteRelativeImportExtensions. Without it, tsc reports TS18120, even with --noEmit.
  • With the flag on, both example apps fail with TS2877. Their extension imports aren't relative (#app/..., cli-app/...), so they can't be rewritten.
  • So a default would break every app on upgrade. Apps could opt out with "outputExtensions": {}, but libraries are the ones that emit.

The other manifest fields don't help either: compilerOptions only lists which options get forwarded to the mapper. It can't set them.

Upstream status

@extends over Component.extend(Mixin): fixed in ember-source 7.5.0-beta.1

microsoft/TypeScript#64072 applies the tag's type arguments to the call's return type. Before emberjs/ember.js#21727, Component.extend(Evented) returned a constructor that is not generic. That gave TS2508, and the signature was lost.

Go-to-definition on a .gts specifier: still broken

test/lsp.js "go to definition from a .ts import into a .gts module" returns []. The test is marked todo with the issue link.

Tracking: #23

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ Noise
⚪ transform 586.69 ms 591.77 ms +0.9% ±5.0%
⚪ server 8-in-flight 645.73 ms 647.29 ms +0.2% ±1.9%
⚪ tsc total 2982.00 ms 2959.00 ms -0.8% ±3.1%
⚪ tsc mapper wait 2950.00 ms 2923.00 ms -0.9% ±2.7%

🟢 faster · 🔴 slower · 🟠 slightly slower · ⚪ within 2% or within round-to-round noise

Full mitata output
clk: ~3.08 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.22.1 (x64-linux)

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
transform (control)          604.85 ms/iter 617.03 ms   █          █ ██    
                    (572.53 ms … 648.88 ms) 627.88 ms ▅ █▅   ▅ ▅ ▅ █ ██▅  ▅
                    (  2.24 mb …  71.80 mb)  43.48 mb █▁██▁▁▁█▁█▁█▁█▁███▁▁█

server 8-in-flight (control) 657.81 ms/iter 663.69 ms █                    
                    (638.33 ms … 701.32 ms) 675.31 ms █  ▅▅▅▅▅▅ ▅▅ ▅▅ ▅ ▅ ▅
                    (  9.61 mb …   9.70 mb)   9.67 mb █▁▁██████▁██▁██▁█▁█▁█

━━━ round 1/3: mapper experiment (core 2) ━━━

clk: ~3.09 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.22.1 (x64-linux)

benchmark                      avg (min … max) p75 / p99    (min … top 1%)
---------------------------------------------- -------------------------------
transform (experiment)          592.14 ms/iter 604.43 ms     █                
                       (560.78 ms … 638.69 ms) 635.72 ms █ █ █     █ █        
                       (177.96 kb …  68.37 mb)  42.07 mb ███▁█▁▁▁▁▁█▁█▁█▁▁▁▁██

server 8-in-flight (experiment) 651.90 ms/iter 666.60 ms   █                  
                       (629.03 ms … 676.57 ms) 675.32 ms   █                 █
                       (  9.66 mb …   9.70 mb)   9.68 mb █▁████▁▁▁██▁▁██▁█▁█▁█

━━━ round 1/3: tsc control ━━━

tsc: 36 files (6 fixtures × 6), 3 runs after warm-up
  run 1: total 2.94s, mapper wait 2.91s
  run 2: total 3.05s, mapper wait 3.02s
  run 3: total 3.05s, mapper wait 3.02s
  p50: total 3.05s, mapper wait 3.02s

━━━ round 1/3: tsc experiment ━━━

tsc: 36 files (6 fixtures × 6), 3 runs after warm-up
  run 1: total 2.91s, mapper wait 2.88s
  run 2: total 2.96s, mapper wait 2.92s
  run 3: total 2.97s, mapper wait 2.94s
  p50: total 2.96s, mapper wait 2.92s

━━━ round 2/3: mapper control (core 2) ━━━

clk: ~3.09 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.22.1 (x64-linux)

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
transform (control)          583.93 ms/iter 601.67 ms    █    █            
                    (549.20 ms … 614.19 ms) 613.87 ms ▅ ▅█ ▅  █▅ ▅  ▅▅▅ ▅▅▅
                    (  1.33 mb …  71.79 mb)  42.44 mb █▁██▁█▁▁██▁█▁▁███▁███

server 8-in-flight (control) 650.26 ms/iter 659.00 ms              █       
                    (626.61 ms … 681.64 ms) 675.82 ms    █ █  █    █       
                    (  9.66 mb …   9.70 mb)   9.68 mb █▁██▁█▁▁█▁█▁██▁▁▁▁█▁█

━━━ round 2/3: mapper experiment (core 0) ━━━

clk: ~3.10 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.22.1 (x64-linux)

benchmark                      avg (min … max) p75 / p99    (min … top 1%)
---------------------------------------------- -------------------------------
transform (experiment)          601.76 ms/iter 610.42 ms    █  █ █ █    █     
                       (558.38 ms … 651.64 ms) 648.76 ms ▅  █  █▅█ █▅▅  █    ▅
                       (  3.28 mb …  70.08 mb)  34.16 mb █▁▁█▁▁███▁███▁▁█▁▁▁▁█

server 8-in-flight (experiment) 649.72 ms/iter 658.80 ms         █            
                       (625.45 ms … 681.22 ms) 679.24 ms  █      █            
                       (  9.66 mb …   9.70 mb)   9.68 mb ██▁▁██▁▁███████▁▁▁▁▁█

━━━ round 2/3: tsc experiment ━━━

tsc: 36 files (6 fixtures × 6), 3 runs after warm-up
  run 1: total 2.99s, mapper wait 2.97s
  run 2: total 3.09s, mapper wait 3.06s
  run 3: total 2.95s, mapper wait 2.92s
  p50: total 2.99s, mapper wait 2.97s

━━━ round 2/3: tsc control ━━━

tsc: 36 files (6 fixtures × 6), 3 runs after warm-up
  run 1: total 2.92s, mapper wait 2.88s
  run 2: total 3.08s, mapper wait 3.02s
  run 3: total 2.96s, mapper wait 2.94s
  p50: total 2.96s, mapper wait 2.94s

━━━ round 3/3: mapper control (core 0) ━━━

clk: ~3.10 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.22.1 (x64-linux)

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
transform (control)          587.92 ms/iter 603.30 ms                █    █
                    (551.74 ms … 633.79 ms) 616.38 ms ▅▅▅▅ ▅▅▅   ▅  ▅█▅  ▅█
                    (  1.76 mb …  71.27 mb)  35.46 mb ████▁███▁▁▁█▁▁███▁▁██

server 8-in-flight (control) 641.39 ms/iter 647.07 ms           █          
                    (620.68 ms … 664.11 ms) 662.77 ms        ▅  █          
                    (  9.66 mb …   9.70 mb)   9.68 mb ▇▁▁▇▇▁▇█▇▁█▁▁▇▇▁▁▇▁▁▇

━━━ round 3/3: mapper experiment (core 2) ━━━

clk: ~3.10 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.22.1 (x64-linux)

benchmark                      avg (min … max) p75 / p99    (min … top 1%)
---------------------------------------------- -------------------------------
transform (experiment)          598.58 ms/iter 610.31 ms    ██                
                       (557.75 ms … 670.48 ms) 636.57 ms ▅  ██  ▅▅▅▅▅▅▅  ▅▅  ▅
                       (  2.81 mb …  68.44 mb)  42.67 mb █▁▁██▁▁███████▁▁██▁▁█

server 8-in-flight (experiment) 633.59 ms/iter 653.37 ms      █        █  █ █ 
                       (577.07 ms … 669.87 ms) 661.40 ms ▅    █▅    ▅▅ █ ▅█▅█▅
                       (  9.66 mb …   9.70 mb)   9.68 mb █▁▁▁▁██▁▁▁▁██▁█▁█████

━━━ round 3/3: tsc control ━━━

tsc: 36 files (6 fixtures × 6), 3 runs after warm-up
  run 1: total 2.98s, mapper wait 2.95s
  run 2: total 3.01s, mapper wait 2.98s
  run 3: total 2.94s, mapper wait 2.92s
  p50: total 2.98s, mapper wait 2.95s

━━━ round 3/3: tsc experiment ━━━

tsc: 36 files (6 fixtures × 6), 3 runs after warm-up
  run 1: total 2.89s, mapper wait 2.86s
  run 2: total 3.22s, mapper wait 3.18s
  run 3: total 2.92s, mapper wait 2.89s
  p50: total 2.92s, mapper wait 2.89s

NullVoxPopuli and others added 5 commits October 29, 2026 12:59
TypeScript 7.1.0-dev.20261014.1 adds `outputExtensions` to content mapper
entries (microsoft/TypeScript#64581, fixing #64053). The library example
opts in, so counter.gts emits counter.d.ts and declarations/ matches dist/.

- Bump the TypeScript 7 nightly to 7.1.0-dev.20261014.1.
- Document "Publishing a library" in the README and point the migration
  guide at it.
- Add a declaration-output-extensions fixture and test.

The mapper's package.json does not set a default: apps would then need
rewriteRelativeImportExtensions, which reports TS2877 for non-relative
extension imports such as #app/components/counter.gts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This nightly includes microsoft/TypeScript#64072 (the #64058 fix). It
applies the JSDoc @extends type arguments to the call's return type, so
over Component.extend(Evented), which returns a constructor that is not
generic, the error changes to TS2508 and the signature is still lost.
Update the upstream-differences snapshot and docs to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….5 beta

ember-source 7.5.0-beta.1 includes emberjs/ember.js#21727, so the
`@extends` over `Component.extend(Mixin)` fixture has no errors.

The go-to-definition test for `export ... from './counter.gts'` is marked
todo until microsoft/TypeScript#64828 is fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@NullVoxPopuli NullVoxPopuli changed the title Emit plain .d.ts for libraries with outputExtensions (TS 7 nightly 20261014) Emit plain .d.ts for libraries with outputExtensions (TS 7 nightly 20261029) Oct 29, 2026
# Conflicts:
#	examples/nvp-library/package.json
#	pnpm-lock.yaml
#	pnpm-workspace.yaml
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.

content-mapper generates inconsistent declaration extensions, making management of package.json#exports hard / verbose

1 participant