microsoft / microsoft/TypeScript

Design Meeting Notes, 2026-07-23

オープン
#63,676 コメント 0 件 リアクション 2 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Design Notes
主要言語
Go
スター
111k
フォーク
14.3k
平均マージ
2日 4時間
マージ済み PR(30日)
132

説明

JavaScript Emit API

https://github.com/microsoft/typescript-go/pull/4699

  • How would something like "compile on save" be reimplemented?
    • Idea: LSP has didSave event, 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.
  • 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 like noEmit, noEmitOnError, ...

      • Some weirdness in interaction, e.g. emitDeclarationOnly
    • 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.
    • 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

// 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.
  • 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 .vue files outside of the program)
    • Also, we have long-running things like --watch and 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 bundler or for --moduleResolution nodenext and others?
    • It works across everything.
  • Top-level contentMappers field - 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 .ts file can map to the same span in a .vue file.
      • 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).
  • 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 count in the <template> block has a definition that doesn't exist in the original .vue source, but also has a mapping to have a mapping back to a node that is not the "true" definition site.

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

これは実装タスクではなく設計会議の記録であり、ソースファイルやテストは記載されていません。まず、リンクされている pull request 4699 と 4712、および参照されている TypeScript の issue コメントを読んでください。完了とするには、実装作業を定義する前に、合意された JavaScript emit API と content-mapper プロトコルのスコープが必要です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
javascript, typescript
領域
compilers, devtools
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
静か
明瞭さ
説明が足りない
初心者へのやさしさ
25/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。