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:
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.vue — tsconfig.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
Summary
When
pathsare declared in a referenced project (the common "solution-style" layout) and that project'sincludeexplicitly lists a non-TS extension (e.g.src/**/*.vue), imports originating from those files are not resolved through the project'spaths.is_file_included_in_tsconfigrejects the importer by extension before theincludeglobs are consulted. So for a.vueimporter no referenced project matches, the empty solution-style roottsconfig.json(which has nopaths) is selected, and the alias never resolves.Why this matters
This is a blocker for migrating from the
vite-tsconfig-pathsplugin to Vite's built-inresolve.tsconfigPaths(Vite 8 / rolldown-vite, which resolves through oxc-resolver). The defaultcreate-viteVue + TS template uses exactly this layout:With
resolve.tsconfigPaths: true,import Foo from '@/components/Foo.vue'inside a.vuefile fails to resolve in both dev and build. Downstream report: vitejs/vite#22047.Reproduction
Resolve
@/components/Foo.vuefromsrc/App.vuewithTsconfigDiscovery::Auto:src/components/Foo.vue—tsconfig.app.jsonincludessrc/App.vueand provides the alias.NotFound— the solution roottsconfig.json(nopaths) is selected.It works when the importer is
.ts(e.g.src/main.ts), or whenpathsis moved into the roottsconfig.json— confirming the cause is the non-TS extension gate during solution-style tsconfig selection.Root cause
is_file_extension_allowed_in_tsconfigallows only.ts/.tsx/.mts/.cts(+ JS variants whenallowJs). It is applied as a blanket pre-gate at step 0 ofis_file_included_in_tsconfig, so a.vuefile returnsfalsebefore theincludeglobsrc/**/*.vueis ever evaluated. As a resultresolve_tsconfig_solutioncannot match the referencedtsconfig.app.jsonand 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 (*), sosrc/**/*.vuewould match.vueif it were reached. It also contradicts TypeScript's ownincludesemantics, where an explicit*.vuematches.vuefiles (the supported-extensions filter only applies to bare-wildcard expansion — seegetSubPatternFromSpec/isImplicitGlob).Environment
oxc_resolver11.21.0resolve.tsconfigPaths; related:resolve.tsconfigPathsconfiguration not working in Vite 8 vitejs/vite#22047