Skip to content

feat: content mapper (v4 alpha) - #6170

Draft
johnsoncodehk wants to merge 32 commits into
masterfrom
feat/vue-content-mapper
Draft

feat: content mapper (v4 alpha)#6170
johnsoncodehk wants to merge 32 commits into
masterfrom
feat/vue-content-mapper

Conversation

@johnsoncodehk

@johnsoncodehk johnsoncodehk commented Aug 20, 2026

Copy link
Copy Markdown
Member

Summary

Vue Language Tools v4 type-checks .vue files with tsc itself through a content mapper — no separate vue-tsc binary.

Quick start

// tsconfig.json
{
  "compilerOptions": { "noEmit": true },
  "contentMappers": [
    {
      "package": "@vue/content-mapper",
      "extensions": [".vue"],
      "options": {
        "target": 3.5
      }
    }
  ]
}
tsc --runExternalCode   # replaces `vue-tsc --noEmit`

Vue compiler options go directly under options (the top-level vueCompilerOptions field is gone). Add "languageFeatures": false under options for CLI-only type-checking.

contentMappers and --runExternalCode are the TypeScript content-mapper hook (microsoft/typescript-go#4712). Until it ships in a published typescript, you need a tsgo build (see Known blocker).

Breaking changes

  • strictTemplates, strictVModel, and all checkUnknown* options are removed. Their "permissive" mode was implemented in vue-tsc by suppressing specific TypeScript error codes in codegen — a per-code filter that the content-mapper protocol cannot express (it can only map-or-drop a location, or ignore a whole line via @vue-ignore). So the knobs are dropped, and template checking aligns with TypeScript's own behavior — lang="ts" is strict, lang="js" is non-strict unless checkJs / @ts-check.

  • Unknown components / directives are now always reported, so they must be declared:

    declare module 'vue' {
      interface GlobalComponents { Foo: typeof import('./Foo.vue').default }
      interface GlobalDirectives { vTooltip: typeof import('./v-tooltip').default }
    }

    GlobalComponents / GlobalDirectives were added in Vue 3.3, so the minimum target is now 3.3 (target: 3 is dropped).

Status

Legend: [x] done · [ ] todo

Foundation

  • vue-tsc removed
  • retire @vue/typescript-plugin (the TS 5/6 plugin; superseded by the content mapper)
  • content-mapper protocol (initialize / openProject / transform / closeProject)
  • built-in directives typed in codegen (vHtml / vText / vCloak / vMemo; vOnce / vSlot fixed upstream in Vue 3.6)
  • span mappings + diagnostic directives + worker pool
  • unit tests (spanMappings, diagnosticDirectives, project, workerPool, localization)
  • integration tests against a locally built tsgo (build, dts, fixtures, corpus)
  • CI builds tsgo from source and runs the content-mapper tests

Diagnostic model

  • unmapped codegen diagnostics eliminated at the codegen source (fix(language-core): eliminate unmapped codegen diagnostics #6179)
  • code-level diagnostic filtering removed (synthetic vars consumed with void; unknown props/events always checked)
  • permissive options removed (strictTemplates, strictVModel, checkUnknownComponents, checkUnknownDirectives) → template checking follows TS/JS defaults
  • @vue-expect-error / @vue-ignorediagnosticDirectives (partial)
  • slot / template-ref synthesized ignore eliminated at source (fix(language-core): use 'default' slot name for bare v-slot #6190, fix(language-core): eliminate synthesized ignore in codegen #6191)
  • remaining synthesized @ts-ignore (semantic diagnostics, exposed under content mapper):
    • auto-import candidate identifiers (context.ts) — TS2304, structural (auto-import needs unresolved names)
    • __VLS_withDotValue assertions (template.ts) — TS2454, needs restructure to map real uninitialized errors
    • synthetic trailing args (elementDirectives.ts) — TS2345/TS2554, structural (runtime 4-arg contract)
    • compound event handlers (elementEvents.ts) — TS6133 (fixable via void $event), TS7031/TS2493 structural
    • first component instantiation (element.ts) — TS2304/TS7006/TS2345, structural (instance/generic inference)
    • JS typedef guards (localTypes.ts) — TS2503, structural (global JSX namespace)
    • scoped classes type alias (scopedClasses.ts) — TS6196, fixable via a non-JSDoc reference
  • duplicate CSS module names report TS2300 (correct, but new vs vue-tsc)

Mapping & features

  • linkedCodeMap removed (refactor(language-core): remove linkedCodeMap and __VLS_SetupExposed #6175)
  • setup bindings unwrapped by value (refactor(language-core): unwrap setup bindings by value #6184)
  • template token boundary double-mapping fixed (fix(language-core): prevent template token boundary double-mapping #6178)
  • shouldHighlight / shouldRename → static SpanMapFeature bits
  • SpanMapKind.Alias for kebab→camel substitution — TBD whether needed
  • Vue-specific completion markers — not yet carried into SpanMapping
  • replace @vue/typescript-plugin request forwarding (TS 7 / content mapper has no such semantic). 17 _vue: requests to migrate:
    • project routing — projectInfo
    • native LSP (SpanMapFeature) — documentHighlights-full, encodedSemanticClassifications-full
    • twoslash query — quickinfo (InlayHints, not Hover)
    • completion (markers / completion data) — getAutoImportSuggestions, getComponentProps, resolveAutoImportCompletionEntry
    • template/component type-level metadata (consumed by language-server VLS) — getComponentMeta, getComponentSlots, getComponentDirectives, getElementAttrs, getElementNames, getComponentNames
    • helpers — collectExtractProps, resolveModuleName, getImportPathForFile, isRefAtPosition

Config & emit

  • mapper options flattened (Vue options live directly under options, no vueCompilerOptions layer)
  • minimum target raised to 3.3
  • decouple contentMappers[].extensions and contentMappers[].options.extensions (both list Vue SFC extensions; they can drift)
  • config surface (configIdentity / watchedFiles) (partial)

Implementation notes

Classic typescript stays pinned <7 (via typescript-native-bridge) to keep tsc -b / tsslint working; typescript-7 (7.1.0-dev) is reserved for content-mapper tests, run by a tsgo binary built from microsoft/typescript-go in CI.

Vue is pinned to 3.6.0-rc.6 — the rc that carries the defineComponent({ __typeProps, __typeEmits, __typeRefs, __typeEl }) + extended DefineComponent API (the intermediate DefineComponent2 is gone), and the vOnce / vSlot casing fix.

Testing

TSGO_PATH=/path/to/tsgo pnpm test content-mapper

Known blocker

npm typescript@7.1.0-dev does not yet expose --runExternalCode (TS PR #4712 approved, not merged/published). CI builds tsgo from source.

References

@johnsoncodehk johnsoncodehk changed the title feat: content mapper prototype (WIP) feat: content mapper (v4 alpha) Aug 20, 2026
@escaton

This comment was marked as resolved.

@escaton

This comment was marked as resolved.

johnsoncodehk and others added 9 commits August 23, 2026 04:27
…pper

# Conflicts:
#	packages/content-mapper/tests/unmapped-diagnostics.spec.ts
#	packages/tsc/tests/typecheck.spec.ts
#	pnpm-lock.yaml
The master merge brought #6184 (unwrap setup bindings by value), #6181
(calling template bindings without .value), #6179 (unmapped diagnostics),
and the withDotValue/unwrapBindingAccess fixtures, all of which change the
generated virtual code and the diagnostics the corpus reports.
tsgo sends compilerOptions in numeric-enum wire format, but
normalizeCompilerOptions ran them through convertCompilerOptionsFromJson,
which expects string enums and silently dropped numeric lib/target/module/
moduleResolution/jsx. Pass the options through unchanged.
The codegen emits JSDoc casts for lang="js" SFCs, but getVirtualExtension
normalized .js/.jsx to .ts/.tsx. In a .ts file, JSDoc casts in spread
positions (`.../** @type {T} */ ({})`) are not applied, collapsing the ctx
type to {} and producing false positives like "$emit does not exist on {}".

Match the virtual extension to the script lang so JSDoc codegen is checked
with JS semantics. js SFCs without checkJs/@ts-check are then not
type-checked, which follows standard .js behavior.
…nt vnode

Extract the "const <name> = " mapping boilerplate into a reusable helper
and use it in template element codegen for the vnode variable declaration.
The content mapper has no per-code diagnostic filtering, so the three
verification.shouldReport filters are removed at the source instead:

- unknown component tags: a // @ts-ignore before the component var (permissive
  kept for Vue runtime/auto-import resolution; the {} fallback still yields the
  opaque errored-any that flows permissively downstream)
- synthetic vnode var: consumed with "void vnodeVar" (the export block is
  already read via a typeof query)
- unknown props/events: now always checked (strict), matching JSX and TS 7
  strict-by-default; the permissive asFunctionalComponent1 / Element1 helpers
  and doNotReport* features are deleted

checkUnknownProps / checkUnknownEvents are removed from the resolved options
and marked deprecated in the vueCompilerOptions JSON schema. Fixtures that
relied on the lenient default pin the diagnostic with @vue-expect-error /
@vue-ignore.
@johnsoncodehk
johnsoncodehk force-pushed the feat/vue-content-mapper branch from 8e3c605 to 34750d8 Compare August 27, 2026 20:35
checkUnknownProps is now always strict, so the __VLS_PROPS_FALLBACK
Record<string, unknown> fallback (props-fallback.d.ts) is no longer needed.
Remove the reference, the conditional in the setup return props, and the file;
fixtures that passed a placeholder prop to generic components use @vue-ignore.
rename @vue/typescript-content-mapper to @vue/content-mapper

remove strictTemplates, strictVModel, and checkUnknown* options; template checking now follows TS/JS defaults

flatten content-mapper options (drop the vueCompilerOptions layer)
rewrite CLI usage and package table to use @vue/content-mapper instead of vue-tsc

deprecate top-level vueCompilerOptions; define Vue options under contentMappers[].options
…pper

# Conflicts:
#	packages/content-mapper/tests/__snapshots__/dts.spec.ts.snap
#	packages/tsc/tests/typecheck.spec.ts
#	pnpm-lock.yaml
#	test-workspace/package.json
@johnsoncodehk
johnsoncodehk force-pushed the feat/vue-content-mapper branch from 3f18e2b to 8213f5a Compare August 28, 2026 20:53
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.

3 participants