microsoft / microsoft/TypeScript
Design Meeting Notes, 2026-07-23
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Go
- Sterne
- 111k
- Forks
- 14.3k
- Ø Merge
- 2 T. 4 Std.
- Gemergte PRs (30 T.)
- 132
Beschreibung
JavaScript Emit API
https://github.com/microsoft/typescript-go/pull/4699
- How would something like "compile on save" be reimplemented?
- Idea: LSP has
didSaveevent, clients can listen and trigger a custom emit command - Does emit in LSP make sense?
- We do expect it for the playground.
- Sounds like yes.
- Idea: LSP has
- Source maps?
- Should add those too.
- What's the (internal) API look like?
-
program.emit(emitOnly?: EmitOnly)- file system (including VFS if supplied), whole program, respects emit-blocking options likenoEmit,noEmitOnError, ...- Some weirdness in interaction, e.g.
emitDeclarationOnly
- Some weirdness in interaction, e.g.
-
program.emitToString(emitOnly?: EmitOnly)- in-memory string results, whole program, respects emit-blocking options- Basically a string over IPC
-
program.getJavaScriptEmit(files?: readonly DocumentIdentifier[])- in-memory string results, specific file selection, bypasses emit-blocking options- In-memory, does not respect
noEmit,emitDeclarationOnly, etc.
- In-memory, does not respect
-
program.getDeclarationEmit(files?: readonly DocumentIdentifier[])- (same) -
And EmitOnly is:
export enum EmitOnly { All = 0, OnlyJs = 1, OnlyDts = 2, }
-
- May have to consider how pre/post transformations work here, plus dynamically contributed content - recall that Angular might have been looking for this with ngc?
Content Mappers
- https://github.com/microsoft/TypeScript/issues/63800#issuecomment-4919145963
- https://github.com/microsoft/typescript-go/pull/4712
// tsconfig.json
{
"compilerOptions": {
// ...
},
"contentMappers": [
{
"package": "vue-content-mapper",
"extensions": [".vue"],
}
],
"include": ["src"]
}
// node_modules/vue-content-mapper/package.json
{
"name": "vue-content-mapper",
"version": "1.0.0",
"tsContentMapper": {
"exec": ["node", "dist/server.js"],
"compilerOptions": ["module", "jsx", "jsxImportSource"]
}
}
- What kind of versioning contract can we provide here?
- Stuff like LSP/Language Server Protocol doesn't provide a version per se - it's all capabilities based. Looks like same with AHP/Agent Host Protocol.
- Can keep version, but stick close to that model.
- Stuff like LSP/Language Server Protocol doesn't provide a version per se - it's all capabilities based. Looks like same with AHP/Agent Host Protocol.
- Why does this have to be a protocol instead of something like a potentially long-running process? Feels heavy.
- Don't want to have multiple invocations of the mapper.
- Cascading resolution: we don't always know which files the program itself includes (you can end up with
.vuefiles outside of the program) - Also, we have long-running things like
--watchand LSP where the program state will change over time. - These transforms are all supposed to be idempotent.
- One issue is: are these able to react to their configurations, and invalidate their resolution?
- We can react to the package itself changing, but not currently to configuration.
- Maybe we need something about config files that the plugin can watch.
- Do we allow this only for
--moduleResolution bundleror for--moduleResolution nodenextand others?- It works across everything.
- Top-level
contentMappersfield - how does this work with tsconfig inheritance?- It currently doesn't.
- This does have some invasiveness in our LSP implementation.
- Two different spans in a
.tsfile can map to the same span in a.vuefile.- Also, want these operations to coalesce occasionally.
- So operations at Line X Column Y now potentially have multiple results!
- So some operations default to the first result (hover), some concatenate results (go-to-definition).
- Two different spans in a
- This doesn't completely subsume Volar - Volar can mask/fix up errors.
- But you can build an LSP over this to do this if desired.
- What are examples of that funky LSP behavior?
- Take for example:
<script setup lang="ts"> import { ref, computed } from "vue"; const count = ref(0); const label = ref("clicks"); const label = computed(() => count.value === 1 ? "click" : "clicks"); const doubled = computed(() => count.value *2); function increment(): void { count.value++; } </script> <template> <div class="counter"> <p>{{ count }} {{ label }}</p> <p>doubled = {{ doubled }}</p> <button @click="increment()">+1</button> <ul> <li v-for="(n, i) in [count, doubled]" :key="i">{{ n }}</li> </ul> <span v-if="count > 5">high</span> </div> </template> - Kinda remaps to...
// SCRIPT SETUP START: export default createComponent({ // ... setup(props) { // VERBATIM-ISH SCRIPT BLOCK: const count = ref(0); // ... // GENERATED AFTER SCRIPT: return { count, }; }); // GENERATED FROM TEMPLATE BLOCK: function render(props) const { count } = props; // usage of count count; } - A reference to
countin the<template>block has a definition that doesn't exist in the original.vuesource, but also has a mapping to have a mapping back to a node that is not the "true" definition site.
- Take for example:
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Dies ist ein Protokoll eines Design-Meetings und keine Implementierungsaufgabe; es nennt keine Quelldateien oder Tests. Beginne mit dem Lesen der verlinkten Pull Requests 4699 und 4712 sowie des referenzierten TypeScript-Issue-Kommentars. Für den Abschluss wäre eine vereinbarte JavaScript-Emit-API und ein vereinbarter Umfang des content-mapper-Protokolls erforderlich, bevor Implementierungsarbeit definiert werden kann.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- javascript, typescript
- Bereich
- compilers, devtools
- Issue-Typ
- Feature
- Schwierigkeit
- 5/5
- Geschätzter Aufwand
- Über eine Woche
- Aktivitätsstatus
- Ruhig
- Klarheit
- Muss geklärt werden
- Anfängerfreundlichkeit
- 25/100