Skip to content

Commit 123db02

Browse files
committed
fix: support .vue files as direct entry inputs
closes #188
1 parent 8fd342c commit 123db02

6 files changed

Lines changed: 90 additions & 0 deletions

File tree

src/tsc/emit-compiler.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,23 @@ function createTsProgram({
7777
: undefined,
7878
)
7979

80+
// TypeScript's createProgram silently skips root files with unsupported
81+
// extensions (.vue is not .ts/.tsx/.d.ts). The only way .vue files enter a
82+
// program is through module resolution (imports from a .ts file). When .vue
83+
// files are direct entries without a .ts file importing them, we add a
84+
// bridge .ts file to context.files so volar can discover them. (#188)
85+
let vueBridgePath: string | undefined
86+
if (vue && entries) {
87+
const vueEntries = entries.filter((e) => e.endsWith('.vue'))
88+
if (vueEntries.length > 0) {
89+
vueBridgePath = path.join(baseDir, '__rolldown_dts_vue_bridge__.ts')
90+
context.files.set(
91+
vueBridgePath,
92+
vueEntries.map((e) => `import '${e.replaceAll('\\', '/')}'`).join('\n'),
93+
)
94+
}
95+
}
96+
8097
debug(`creating program for root project: ${baseDir}`)
8198
return createTsProgramFromParsedConfig({
8299
parsedConfig,
@@ -86,6 +103,7 @@ function createTsProgram({
86103
entries,
87104
vue,
88105
tsMacro,
106+
vueBridgePath,
89107
})
90108
}
91109

@@ -97,10 +115,12 @@ function createTsProgramFromParsedConfig({
97115
entries,
98116
vue,
99117
tsMacro,
118+
vueBridgePath,
100119
}: {
101120
parsedConfig: ts.ParsedCommandLine
102121
fsSystem: ts.System
103122
baseDir: string
123+
vueBridgePath?: string
104124
} & Pick<TscOptions, 'entries' | 'vue' | 'tsMacro' | 'id'>): TscModule {
105125
const compilerOptions: ts.CompilerOptions = {
106126
...defaultCompilerOptions,
@@ -116,8 +136,13 @@ function createTsProgramFromParsedConfig({
116136
),
117137
),
118138
]
139+
if (vueBridgePath) {
140+
rootNames.push(vueBridgePath)
141+
}
119142

120143
const host = ts.createCompilerHost(compilerOptions, true)
144+
host.readFile = (fileName) => fsSystem.readFile(fileName)
145+
host.fileExists = (fileName) => fsSystem.fileExists(fileName)
121146

122147
const createProgram = createProgramFactory(ts, { vue, tsMacro })
123148
const program = createProgram({

tests/__snapshots__/tsc.test.ts.snap

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,34 @@ interface Unused {
477477
export { Unused };"
478478
`;
479479
480+
exports[`tsc > vue-sfc as entry input #188 1`] = `
481+
"// Bar.vue.d.ts
482+
import * as _vue_runtime_core0 from "@vue/runtime-core";
483+
484+
//#region tests/fixtures/vue-sfc-entry/Bar.vue.d.ts
485+
declare const __VLS_export: _vue_runtime_core0.DefineComponent<{}, {}, {}, {}, {}, _vue_runtime_core0.ComponentOptionsMixin, _vue_runtime_core0.ComponentOptionsMixin, {}, string, _vue_runtime_core0.PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, _vue_runtime_core0.ComponentProvideOptions, true, {}, any>;
486+
declare const _default: typeof __VLS_export;
487+
//#endregion
488+
export { _default as default };
489+
// Foo.vue.d.ts
490+
import { Bar } from "./baz.js";
491+
import * as _vue_runtime_core0 from "@vue/runtime-core";
492+
493+
//#region tests/fixtures/vue-sfc-entry/Foo.vue.d.ts
494+
type __VLS_Props = {
495+
foo: Bar;
496+
};
497+
declare const __VLS_export: _vue_runtime_core0.DefineComponent<__VLS_Props, {}, {}, {}, {}, _vue_runtime_core0.ComponentOptionsMixin, _vue_runtime_core0.ComponentOptionsMixin, {}, string, _vue_runtime_core0.PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, _vue_runtime_core0.ComponentProvideOptions, false, {}, any>;
498+
declare const _default: typeof __VLS_export;
499+
//#endregion
500+
export { _default as default };
501+
// baz.d.ts
502+
//#region tests/fixtures/vue-sfc-entry/bar/baz.d.ts
503+
type Bar = 'bar' | 'baz';
504+
//#endregion
505+
export { Bar };"
506+
`;
507+
480508
exports[`tsc > vue-sfc w/ ts-compiler 1`] = `
481509
"// main.d.ts
482510
import * as _vue_runtime_core0 from "@vue/runtime-core";
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<div>Bar</div>
3+
</template>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script setup lang="ts">
2+
import type { Bar } from './bar/baz'
3+
4+
defineProps<{
5+
foo: Bar
6+
}>()
7+
</script>
8+
9+
<template>
10+
{{ foo }}
11+
</template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type Bar = 'bar' | 'baz'

tests/tsc.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,28 @@ describe('tsc', () => {
213213
expect(snapshot).toMatchSnapshot()
214214
})
215215

216+
test('vue-sfc as entry input #188', async () => {
217+
const root = path.resolve(dirname, 'fixtures/vue-sfc-entry')
218+
const { snapshot } = await rolldownBuild(
219+
[
220+
path.resolve(root, 'Foo.vue'),
221+
path.resolve(root, 'Bar.vue'),
222+
path.resolve(root, 'bar/baz.ts'),
223+
],
224+
[
225+
dts({
226+
emitDtsOnly: true,
227+
vue: true,
228+
compilerOptions: {
229+
isolatedDeclarations: false,
230+
},
231+
}),
232+
],
233+
{ external: ['vue'] },
234+
)
235+
expect(snapshot).toMatchSnapshot()
236+
})
237+
216238
test('vue-sfc w/ ts-compiler w/ vueCompilerOptions in tsconfig', async () => {
217239
const root = path.resolve(dirname, 'fixtures/vue-sfc-fallthrough')
218240
const { snapshot } = await rolldownBuild(

0 commit comments

Comments
 (0)