Skip to content

tsconfig: solution-style paths not applied to .vue/.svelte importers explicitly listed in include #1212

Description

@shulaoda

Summary

When paths are declared in a referenced project (the common "solution-style" layout) and that project's include explicitly lists a non-TS extension (e.g. src/**/*.vue), imports originating from those files are not resolved through the project's paths.

is_file_included_in_tsconfig rejects the importer by extension before the include globs are consulted. So for a .vue importer no referenced project matches, the empty solution-style root tsconfig.json (which has no paths) is selected, and the alias never resolves.

Why this matters

This is a blocker for migrating from the vite-tsconfig-paths plugin to Vite's built-in resolve.tsconfigPaths (Vite 8 / rolldown-vite, which resolves through oxc-resolver). The default create-vite Vue + TS template uses exactly this layout:

// tsconfig.json
{ "files": [], "references": [{ "path": "./tsconfig.app.json" }] }

// tsconfig.app.json
{
  "include": ["src/**/*.ts", "src/**/*.vue"],
  "compilerOptions": { "paths": { "@/*": ["./src/*"] } }
}

With resolve.tsconfigPaths: true, import Foo from '@/components/Foo.vue' inside a .vue file fails to resolve in both dev and build. Downstream report: vitejs/vite#22047.

Reproduction

tsconfig.json            // { "files": [], "references": [{ "path": "./tsconfig.app.json" }] }
tsconfig.app.json        // { "include": ["src/**/*.vue"], "compilerOptions": { "paths": { "@/*": ["./src/*"] } } }
src/App.vue              // importer
src/components/Foo.vue   // target

Resolve @/components/Foo.vue from src/App.vue with TsconfigDiscovery::Auto:

  • Expected: src/components/Foo.vuetsconfig.app.json includes src/App.vue and provides the alias.
  • Actual: NotFound — the solution root tsconfig.json (no paths) is selected.

It works when the importer is .ts (e.g. src/main.ts), or when paths is moved into the root tsconfig.json — confirming the cause is the non-TS extension gate during solution-style tsconfig selection.

Root cause

is_file_extension_allowed_in_tsconfig allows only .ts/.tsx/.mts/.cts (+ JS variants when allowJs). It is applied as a blanket pre-gate at step 0 of is_file_included_in_tsconfig, so a .vue file returns false before the include glob src/**/*.vue is ever evaluated. As a result resolve_tsconfig_solution cannot match the referenced tsconfig.app.json and falls back to the solution root.

This pre-gate is redundant with — and stricter than — the per-pattern matcher is_glob_match, which already does the right thing: it only extension-gates wildcard-terminated patterns (*), so src/**/*.vue would match .vue if it were reached. It also contradicts TypeScript's own include semantics, where an explicit *.vue matches .vue files (the supported-extensions filter only applies to bare-wildcard expansion — see getSubPatternFromSpec / isImplicitGlob).

Environment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    Priority

    None yet

    Effort

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions