From bd1d9546e11050e61c52947e85e5ddf8b7720b22 Mon Sep 17 00:00:00 2001 From: Blake Newman Date: Wed, 3 Apr 2024 10:49:01 +0100 Subject: [PATCH] fix(tsc): remove fake global types holder (#4196) remove fake global types holder, instead extracting global types when writing `.d.ts`. This allows composite projects to rebuild without detecting changes which adjusting `options.rootNames` was causing Co-authored-by: Johnson Chu --- .../lib/generators/globalTypes.ts | 10 +-- packages/tsc/index.ts | 61 ++++++++----------- packages/tsc/tests/dts.spec.ts | 23 +++++-- 3 files changed, 49 insertions(+), 45 deletions(-) diff --git a/packages/language-core/lib/generators/globalTypes.ts b/packages/language-core/lib/generators/globalTypes.ts index 48fb161d8c..4a235750f6 100644 --- a/packages/language-core/lib/generators/globalTypes.ts +++ b/packages/language-core/lib/generators/globalTypes.ts @@ -4,11 +4,12 @@ import { getSlotsPropertyName } from '../utils/shared'; export function generateGlobalTypes(vueCompilerOptions: VueCompilerOptions) { const fnPropsType = `(K extends { $props: infer Props } ? Props : any)${vueCompilerOptions.strictTemplates ? '' : ' & Record'}`; return ` -; declare global { +; export const __VLS_globalTypesStart = {}; +declare global { // @ts-ignore -type __VLS_IntrinsicElements = __VLS_PickNotAny>>; +type __VLS_IntrinsicElements = __VLS_PickNotAny>>; // @ts-ignore -type __VLS_Element = __VLS_PickNotAny; +type __VLS_Element = __VLS_PickNotAny; // @ts-ignore type __VLS_GlobalComponents = ${[ `__VLS_PickNotAny`, @@ -125,5 +126,6 @@ type __VLS_NormalizeEmits = __VLS_PrettifyGlobal< > >; type __VLS_PrettifyGlobal = { [K in keyof T]: T[K]; } & {}; -}`; +} +export const __VLS_globalTypesEnd = {};`; }; diff --git a/packages/tsc/index.ts b/packages/tsc/index.ts index bfebda7a66..1ec48a4c43 100644 --- a/packages/tsc/index.ts +++ b/packages/tsc/index.ts @@ -1,6 +1,5 @@ import { runTsc } from '@volar/typescript/lib/quickstart/runTsc'; import * as vue from '@vue/language-core'; -import type * as ts from 'typescript'; const windowsPathReg = /\\/g; @@ -17,7 +16,25 @@ export function run() { const vueOptions = typeof configFilePath === 'string' ? vue.createParsedCommandLine(ts, ts.sys, configFilePath.replace(windowsPathReg, '/')).vueOptions : vue.resolveVueCompilerOptions({}); - const fakeGlobalTypesHolder = createFakeGlobalTypesHolder(options); + const writeFile = options.host!.writeFile.bind(options.host); + const getCanonicalFileName = options.host?.useCaseSensitiveFileNames?.() + ? (fileName: string) => fileName + : (fileName: string) => fileName.toLowerCase(); + const canonicalRootFileNames = new Set( + options.rootNames + .map(rootName => rootName.replace(windowsPathReg, '/')) + .map(getCanonicalFileName) + ); + const canonicalGlobalTypesHolderFileNames = new Set(); + options.host!.writeFile = (fileName, contents, ...args) => { + if ( + fileName.endsWith('.d.ts') + && canonicalGlobalTypesHolderFileNames.has(getCanonicalFileName(fileName.replace(windowsPathReg, '/')).slice(0, -5)) + ) { + contents = removeEmitGlobalTypes(contents); + } + return writeFile(fileName, contents, ...args); + }; if ( runExtensions.length === vueOptions.extensions.length && runExtensions.every(ext => vueOptions.extensions.includes(ext)) @@ -25,7 +42,11 @@ export function run() { const vueLanguagePlugin = vue.createVueLanguagePlugin( ts, id => id, - fileName => fileName === fakeGlobalTypesHolder, + fileName => { + const canonicalFileName = getCanonicalFileName(fileName); + canonicalGlobalTypesHolderFileNames.add(canonicalFileName); + return canonicalRootFileNames.has(canonicalFileName); + }, options.options, vueOptions, false, @@ -54,36 +75,6 @@ export function run() { } } -export function createFakeGlobalTypesHolder(options: ts.CreateProgramOptions) { - const firstVueFile = options.rootNames.find(fileName => fileName.endsWith('.vue')); - if (firstVueFile) { - const fakeFileName = firstVueFile + '__VLS_globalTypes.vue'; - - (options.rootNames as string[]).push(fakeFileName); - - const fileExists = options.host!.fileExists.bind(options.host); - const readFile = options.host!.readFile.bind(options.host); - const writeFile = options.host!.writeFile.bind(options.host); - - options.host!.fileExists = fileName => { - if (fileName.endsWith('__VLS_globalTypes.vue')) { - return true; - } - return fileExists(fileName); - }; - options.host!.readFile = fileName => { - if (fileName.endsWith('__VLS_globalTypes.vue')) { - return ''; - } - return readFile(fileName); - }; - options.host!.writeFile = (fileName, ...args) => { - if (fileName.endsWith('__VLS_globalTypes.vue.d.ts')) { - return; - } - return writeFile(fileName, ...args); - }; - - return fakeFileName.replace(windowsPathReg, '/'); - } +export function removeEmitGlobalTypes(dts: string) { + return dts.replace(/[^\n]*__VLS_globalTypesStart[\w\W]*__VLS_globalTypesEnd[^\n]*\n/, ''); } diff --git a/packages/tsc/tests/dts.spec.ts b/packages/tsc/tests/dts.spec.ts index a3024df974..956a843ff9 100644 --- a/packages/tsc/tests/dts.spec.ts +++ b/packages/tsc/tests/dts.spec.ts @@ -4,7 +4,7 @@ import * as ts from 'typescript'; import { describe, expect, it } from 'vitest'; import { proxyCreateProgram } from '@volar/typescript'; import * as vue from '@vue/language-core'; -import { createFakeGlobalTypesHolder } from '..'; +import { removeEmitGlobalTypes } from '..'; const workspace = path.resolve(__dirname, '../../../test-workspace/component-meta'); const normalizePath = (filename: string) => filename.replace(/\\/g, '/'); @@ -24,7 +24,6 @@ describe('vue-tsc-dts', () => { rootNames: readFilesRecursive(workspace), options: compilerOptions }; - const fakeGlobalTypesHolder = createFakeGlobalTypesHolder(options); let vueExts: string[] = []; const createProgram = proxyCreateProgram(ts, ts.createProgram, (ts, options) => { @@ -36,7 +35,21 @@ describe('vue-tsc-dts', () => { const vueLanguagePlugin = vue.createVueLanguagePlugin( ts, id => id, - fileName => fileName === fakeGlobalTypesHolder, + fileName => { + const rootFileNames = options.rootNames.map(rootName => rootName.replace(windowsPathReg, '/')); + if (options.host?.useCaseSensitiveFileNames?.()) { + return rootFileNames.includes(fileName); + } + else { + const lowerFileName = fileName.toLowerCase(); + for (const rootFileName of rootFileNames) { + if (rootFileName.toLowerCase() === lowerFileName) { + return true; + } + } + return false; + } + }, options.options, vueOptions, false, @@ -52,9 +65,6 @@ describe('vue-tsc-dts', () => { for (const intputFile of options.rootNames) { - if (intputFile.endsWith('__VLS_globalTypes.vue')) - continue; - const expectedOutputFile = intputFile.endsWith('.ts') ? intputFile.slice(0, -'.ts'.length) + '.d.ts' : intputFile.endsWith('.tsx') @@ -67,6 +77,7 @@ describe('vue-tsc-dts', () => { sourceFile, (outputFile, text) => { expect(outputFile.replace(windowsPathReg, '/')).toBe(expectedOutputFile.replace(windowsPathReg, '/')); + text = removeEmitGlobalTypes(text); outputText = text; }, undefined,