From f92fae1077ebb69f5b086cc262ec7a01da513484 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 28 May 2022 22:13:00 +0800 Subject: [PATCH] feat: update auto-import cache logic for TS 4.7 (#1360) --- .../vue-typescript/src/typescriptRuntime.ts | 4 +- .../src/utils/moduleSpecifierCache.ts | 68 ++++++++++-------- .../src/utils/packageJsonCache.ts | 46 ++++++------ packages/vue-typescript/src/utils/ts.ts | 11 +-- pnpm-lock.yaml | 70 +++++++++---------- 5 files changed, 107 insertions(+), 92 deletions(-) diff --git a/packages/vue-typescript/src/typescriptRuntime.ts b/packages/vue-typescript/src/typescriptRuntime.ts index 959333f16..33f1e853b 100644 --- a/packages/vue-typescript/src/typescriptRuntime.ts +++ b/packages/vue-typescript/src/typescriptRuntime.ts @@ -43,7 +43,9 @@ export function createTypeScriptRuntime(options: { let lastProjectVersion: string | undefined; let tsProjectVersion = 0; - injectCacheLogicToLanguageServiceHost(ts, tsLsHost, tsLsRaw); + if (!options.isVueTsc) { + injectCacheLogicToLanguageServiceHost(ts, tsLsHost, tsLsRaw); + } return { vueLsHost: options.vueLsHost, diff --git a/packages/vue-typescript/src/utils/moduleSpecifierCache.ts b/packages/vue-typescript/src/utils/moduleSpecifierCache.ts index ad5192cd4..47d24f81a 100644 --- a/packages/vue-typescript/src/utils/moduleSpecifierCache.ts +++ b/packages/vue-typescript/src/utils/moduleSpecifierCache.ts @@ -1,4 +1,4 @@ -import type { Path, UserPreferences } from 'typescript/lib/tsserverlibrary'; +import type { Path, UserPreferences, SourceFile } from 'typescript/lib/tsserverlibrary'; export interface ModulePath { path: string; @@ -9,35 +9,45 @@ export interface ModulePath { export interface ResolvedModuleSpecifierInfo { modulePaths: readonly ModulePath[] | undefined; moduleSpecifiers: readonly string[] | undefined; - isAutoImportable: boolean | undefined; + isBlockedByPackageJsonDependencies: boolean | undefined; +} + +export interface ModuleSpecifierOptions { + overrideImportMode?: SourceFile["impliedNodeFormat"]; } export interface ModuleSpecifierCache { - get(fromFileName: Path, toFileName: Path, preferences: UserPreferences): Readonly | undefined; - set(fromFileName: Path, toFileName: Path, preferences: UserPreferences, modulePaths: readonly ModulePath[], moduleSpecifiers: readonly string[]): void; - setIsAutoImportable(fromFileName: Path, toFileName: Path, preferences: UserPreferences, isAutoImportable: boolean): void; - setModulePaths(fromFileName: Path, toFileName: Path, preferences: UserPreferences, modulePaths: readonly ModulePath[]): void; + get(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions): Readonly | undefined; + set(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, modulePaths: readonly ModulePath[], moduleSpecifiers: readonly string[]): void; + setBlockedByPackageJsonDependencies(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, isBlockedByPackageJsonDependencies: boolean): void; + setModulePaths(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, modulePaths: readonly ModulePath[]): void; clear(): void; count(): number; } -// export interface ModuleSpecifierResolutionCacheHost { -// watchNodeModulesForPackageJsonChanges(directoryPath: string): FileWatcher; -// } +export interface FileWatcher { + close(): void; +} + +export interface ModuleSpecifierResolutionCacheHost { + watchNodeModulesForPackageJsonChanges(directoryPath: string): FileWatcher; +} + +export const nodeModulesPathPart = "/node_modules/"; export function createModuleSpecifierCache( // host: ModuleSpecifierResolutionCacheHost ): ModuleSpecifierCache { - // let containedNodeModulesWatchers: Map | undefined; // TODO + let containedNodeModulesWatchers: Map | undefined; let cache: Map | undefined; let currentKey: string | undefined; const result: ModuleSpecifierCache = { - get(fromFileName, toFileName, preferences) { - if (!cache || currentKey !== key(fromFileName, preferences)) return undefined; + get(fromFileName, toFileName, preferences, options) { + if (!cache || currentKey !== key(fromFileName, preferences, options)) return undefined; return cache.get(toFileName); }, - set(fromFileName, toFileName, preferences, modulePaths, moduleSpecifiers) { - ensureCache(fromFileName, preferences).set(toFileName, createInfo(modulePaths, moduleSpecifiers, /*isAutoImportable*/ true)); + set(fromFileName, toFileName, preferences, options, modulePaths, moduleSpecifiers) { + ensureCache(fromFileName, preferences, options).set(toFileName, createInfo(modulePaths, moduleSpecifiers, /*isBlockedByPackageJsonDependencies*/ false)); // If any module specifiers were generated based off paths in node_modules, // a package.json file in that package was read and is an input to the cached. @@ -59,30 +69,30 @@ export function createModuleSpecifierCache( } } }, - setModulePaths(fromFileName, toFileName, preferences, modulePaths) { - const cache = ensureCache(fromFileName, preferences); + setModulePaths(fromFileName, toFileName, preferences, options, modulePaths) { + const cache = ensureCache(fromFileName, preferences, options); const info = cache.get(toFileName); if (info) { info.modulePaths = modulePaths; } else { - cache.set(toFileName, createInfo(modulePaths, /*moduleSpecifiers*/ undefined, /*isAutoImportable*/ undefined)); + cache.set(toFileName, createInfo(modulePaths, /*moduleSpecifiers*/ undefined, /*isBlockedByPackageJsonDependencies*/ undefined)); } }, - setIsAutoImportable(fromFileName, toFileName, preferences, isAutoImportable) { - const cache = ensureCache(fromFileName, preferences); + setBlockedByPackageJsonDependencies(fromFileName, toFileName, preferences, options, isBlockedByPackageJsonDependencies) { + const cache = ensureCache(fromFileName, preferences, options); const info = cache.get(toFileName); if (info) { - info.isAutoImportable = isAutoImportable; + info.isBlockedByPackageJsonDependencies = isBlockedByPackageJsonDependencies; } else { - cache.set(toFileName, createInfo(/*modulePaths*/ undefined, /*moduleSpecifiers*/ undefined, isAutoImportable)); + cache.set(toFileName, createInfo(/*modulePaths*/ undefined, /*moduleSpecifiers*/ undefined, isBlockedByPackageJsonDependencies)); } }, clear() { - // containedNodeModulesWatchers?.forEach(watcher => watcher.close()); + containedNodeModulesWatchers?.forEach(watcher => watcher.close()); cache?.clear(); - // containedNodeModulesWatchers?.clear(); + containedNodeModulesWatchers?.clear(); currentKey = undefined; }, count() { @@ -94,8 +104,8 @@ export function createModuleSpecifierCache( // } return result; - function ensureCache(fromFileName: Path, preferences: UserPreferences) { - const newKey = key(fromFileName, preferences); + function ensureCache(fromFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions) { + const newKey = key(fromFileName, preferences, options); if (cache && (currentKey !== newKey)) { result.clear(); } @@ -103,15 +113,15 @@ export function createModuleSpecifierCache( return cache ||= new Map(); } - function key(fromFileName: Path, preferences: UserPreferences) { - return `${fromFileName},${preferences.importModuleSpecifierEnding},${preferences.importModuleSpecifierPreference}`; + function key(fromFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions) { + return `${fromFileName},${preferences.importModuleSpecifierEnding},${preferences.importModuleSpecifierPreference},${options.overrideImportMode}`; } function createInfo( modulePaths: readonly ModulePath[] | undefined, moduleSpecifiers: readonly string[] | undefined, - isAutoImportable: boolean | undefined, + isBlockedByPackageJsonDependencies: boolean | undefined, ): ResolvedModuleSpecifierInfo { - return { modulePaths, moduleSpecifiers, isAutoImportable }; + return { modulePaths, moduleSpecifiers, isBlockedByPackageJsonDependencies }; } } diff --git a/packages/vue-typescript/src/utils/packageJsonCache.ts b/packages/vue-typescript/src/utils/packageJsonCache.ts index e743f756e..25c46daa7 100644 --- a/packages/vue-typescript/src/utils/packageJsonCache.ts +++ b/packages/vue-typescript/src/utils/packageJsonCache.ts @@ -1,22 +1,28 @@ import type { Path, server } from 'typescript/lib/tsserverlibrary'; -export const enum PackageJsonDependencyGroup { - Dependencies = 1 << 0, - DevDependencies = 1 << 1, - PeerDependencies = 1 << 2, - OptionalDependencies = 1 << 3, - All = Dependencies | DevDependencies | PeerDependencies | OptionalDependencies, +interface PackageJsonPathFields { + typings?: string; + types?: string; + typesVersions?: Map>; + main?: string; + tsconfig?: string; + type?: string; + imports?: object; + exports?: object; + name?: string; +} + +interface VersionPaths { + version: string; + paths: Map; } export interface PackageJsonInfo { - fileName: string; - parseable: boolean; - dependencies?: Map; - devDependencies?: Map; - peerDependencies?: Map; - optionalDependencies?: Map; - get(dependencyName: string, inGroups?: PackageJsonDependencyGroup): string | undefined; - has(dependencyName: string, inGroups?: PackageJsonDependencyGroup): boolean; + packageDirectory: string; + packageJsonContent: PackageJsonPathFields; + versionPaths: VersionPaths | undefined; + /** false: resolved to nothing. undefined: not yet resolved */ + resolvedEntrypoints: string[] | false | undefined; } export const enum Ternary { @@ -38,20 +44,15 @@ export interface PackageJsonCache { searchDirectoryAndAncestors(directory: Path): void; } -export function canCreatePackageJsonCache(ts: typeof import('typescript/lib/tsserverlibrary')) { - return 'createPackageJsonInfo' in ts && 'getDirectoryPath' in ts && 'combinePaths' in ts && 'tryFileExists' in ts && 'forEachAncestorDirectory' in ts; -} - export function createPackageJsonCache( ts: typeof import('typescript/lib/tsserverlibrary'), host: ProjectService, ): PackageJsonCache { const { createPackageJsonInfo, getDirectoryPath, combinePaths, tryFileExists, forEachAncestorDirectory } = ts as any; - const packageJsons = new Map(); - const directoriesWithoutPackageJson = new Map(); + const packageJsons = new Map(); + const directoriesWithoutPackageJson = new Map(); return { addOrUpdate, - // @ts-expect-error forEach: packageJsons.forEach.bind(packageJsons), get: packageJsons.get.bind(packageJsons), delete: fileName => { @@ -63,8 +64,7 @@ export function createPackageJsonCache( }, directoryHasPackageJson, searchDirectoryAndAncestors: directory => { - // @ts-expect-error - forEachAncestorDirectory(directory, ancestor => { + forEachAncestorDirectory(directory, (ancestor: Path) => { if (directoryHasPackageJson(ancestor) !== Ternary.Maybe) { return true; } diff --git a/packages/vue-typescript/src/utils/ts.ts b/packages/vue-typescript/src/utils/ts.ts index fcda33b32..7080dcac5 100644 --- a/packages/vue-typescript/src/utils/ts.ts +++ b/packages/vue-typescript/src/utils/ts.ts @@ -1,6 +1,6 @@ import type * as ts from 'typescript/lib/tsserverlibrary'; import { createModuleSpecifierCache } from './moduleSpecifierCache'; -import { createPackageJsonCache, canCreatePackageJsonCache, PackageJsonInfo, Ternary } from './packageJsonCache'; +import { createPackageJsonCache, PackageJsonInfo, Ternary } from './packageJsonCache'; import * as path from 'path'; export function injectCacheLogicToLanguageServiceHost( @@ -10,8 +10,9 @@ export function injectCacheLogicToLanguageServiceHost( ) { const versionNums = ts.version.split('.').map(s => Number(s)); - if (versionNums[0] > 4 || (versionNums[0] === 4 && versionNums[1] >= 7)) { - console.log('Please update to v0.35.0 or higher for TypeScript version:', ts.version); + const greaterThan47 = versionNums[0] > 4 || (versionNums[0] === 4 && versionNums[1] >= 7); + if (!greaterThan47) { + console.log('Please downgrade to v0.34.17 or lower for TypeScript version:', ts.version); return; } @@ -29,7 +30,6 @@ export function injectCacheLogicToLanguageServiceHost( || !_getDirectoryPath || !_toPath || !_createGetCanonicalFileName - || !canCreatePackageJsonCache(ts) ) return; const moduleSpecifierCache = createModuleSpecifierCache(); @@ -40,6 +40,9 @@ export function injectCacheLogicToLanguageServiceHost( getPackageJsonAutoImportProvider() { return service.getProgram(); }, + getGlobalTypingsCacheLocation() { + return undefined; + }, }); const packageJsonCache = createPackageJsonCache(ts, { ...host, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1378ce683..47cc75486 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,7 +22,7 @@ importers: specifiers: '@volar/typescript-plugin-forward': file:typescript-plugin-forward esbuild: latest - typescript-vue-plugin: 0.34.16 + typescript-vue-plugin: 0.34.17 vsce: latest dependencies: '@volar/typescript-plugin-forward': file:extensions/vscode-typescript-vue-plugin/typescript-plugin-forward @@ -34,9 +34,9 @@ importers: extensions/vscode-vue-language-features: specifiers: '@types/vscode': 1.63.0 - '@volar/preview': 0.34.16 - '@volar/shared': 0.34.16 - '@volar/vue-language-server': 0.34.16 + '@volar/preview': 0.34.17 + '@volar/shared': 0.34.17 + '@volar/vue-language-server': 0.34.17 '@vue/compiler-dom': ^3.2.36 '@vue/compiler-sfc': ^3.2.36 '@vue/reactivity': ^3.2.36 @@ -63,7 +63,7 @@ importers: packages/code-gen: specifiers: - '@volar/source-map': 0.34.16 + '@volar/source-map': 0.34.17 dependencies: '@volar/source-map': link:../source-map @@ -78,10 +78,10 @@ importers: packages/pug-language-service: specifiers: - '@volar/code-gen': 0.34.16 - '@volar/shared': 0.34.16 - '@volar/source-map': 0.34.16 - '@volar/transforms': 0.34.16 + '@volar/code-gen': 0.34.17 + '@volar/shared': 0.34.17 + '@volar/source-map': 0.34.17 + '@volar/transforms': 0.34.17 pug-lexer: ^5.0.1 pug-parser: ^6.0.0 vscode-html-languageservice: ^5.0.0 @@ -119,7 +119,7 @@ importers: packages/transforms: specifiers: - '@volar/shared': 0.34.16 + '@volar/shared': 0.34.17 vscode-languageserver-types: ^3.17.1 dependencies: '@volar/shared': link:../shared @@ -128,7 +128,7 @@ importers: packages/typescript-language-service: specifiers: '@types/semver': ^7.3.9 - '@volar/shared': 0.34.16 + '@volar/shared': 0.34.17 semver: ^7.3.7 typescript: latest upath: ^2.0.1 @@ -148,7 +148,7 @@ importers: packages/typescript-vue-plugin: specifiers: - '@volar/vue-typescript': 0.34.16 + '@volar/vue-typescript': 0.34.17 typescript: latest dependencies: '@volar/vue-typescript': link:../vue-typescript @@ -157,8 +157,8 @@ importers: packages/vue-code-gen: specifiers: - '@volar/code-gen': 0.34.16 - '@volar/source-map': 0.34.16 + '@volar/code-gen': 0.34.17 + '@volar/source-map': 0.34.17 '@vue/compiler-core': ^3.2.36 '@vue/compiler-dom': ^3.2.36 '@vue/shared': ^3.2.36 @@ -174,9 +174,9 @@ importers: packages/vue-language-server: specifiers: - '@volar/shared': 0.34.16 - '@volar/vue-language-service': 0.34.16 - '@volar/vue-typescript': 0.34.16 + '@volar/shared': 0.34.17 + '@volar/vue-language-service': 0.34.17 + '@volar/vue-typescript': 0.34.17 '@vue/shared': ^3.2.36 request-light: ^0.5.8 upath: ^2.0.1 @@ -203,14 +203,14 @@ importers: '@johnsoncodehk/html2pug': ^1.0.0 '@johnsoncodehk/pug-beautify': ^0.2.2 '@types/semver': ^7.3.9 - '@volar/pug-language-service': 0.34.16 - '@volar/shared': 0.34.16 - '@volar/source-map': 0.34.16 - '@volar/transforms': 0.34.16 - '@volar/typescript-language-service': 0.34.16 - '@volar/vue-code-gen': 0.34.16 - '@volar/vue-language-service-types': 0.34.16 - '@volar/vue-typescript': 0.34.16 + '@volar/pug-language-service': 0.34.17 + '@volar/shared': 0.34.17 + '@volar/source-map': 0.34.17 + '@volar/transforms': 0.34.17 + '@volar/typescript-language-service': 0.34.17 + '@volar/vue-code-gen': 0.34.17 + '@volar/vue-language-service-types': 0.34.17 + '@volar/vue-typescript': 0.34.17 '@vscode/emmet-helper': ^2.8.4 '@vue/reactivity': ^3.2.36 '@vue/shared': ^3.2.36 @@ -263,16 +263,16 @@ importers: packages/vue-tsc: specifiers: - '@volar/vue-typescript': 0.34.16 + '@volar/vue-typescript': 0.34.17 dependencies: '@volar/vue-typescript': link:../vue-typescript packages/vue-typescript: specifiers: - '@volar/code-gen': 0.34.16 - '@volar/pug-language-service': 0.34.16 - '@volar/source-map': 0.34.16 - '@volar/vue-code-gen': 0.34.16 + '@volar/code-gen': 0.34.17 + '@volar/pug-language-service': 0.34.17 + '@volar/source-map': 0.34.17 + '@volar/vue-code-gen': 0.34.17 '@vue/compiler-sfc': ^3.2.36 '@vue/reactivity': ^3.2.36 typescript: latest @@ -2332,7 +2332,7 @@ packages: dev: true /fd-slicer/1.1.0: - resolution: {integrity: sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=} + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} dependencies: pend: 1.2.0 dev: true @@ -2345,12 +2345,12 @@ packages: dev: true /file-type/3.9.0: - resolution: {integrity: sha1-JXoHg4TR24CHvESdEH1SpSZyuek=} + resolution: {integrity: sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==} engines: {node: '>=0.10.0'} dev: true /file-type/5.2.0: - resolution: {integrity: sha1-LdvqfHP/42No365J3DOMBYwritY=} + resolution: {integrity: sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==} engines: {node: '>=4'} dev: true @@ -2367,12 +2367,12 @@ packages: dev: true /filter-obj/1.1.0: - resolution: {integrity: sha1-mzERErxsYSehbgFsbF1/GeCAXFs=} + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} engines: {node: '>=0.10.0'} dev: true /find-up/2.1.0: - resolution: {integrity: sha1-RdG35QbHF93UgndaK3eSCjwMV6c=} + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} engines: {node: '>=4'} dependencies: locate-path: 2.0.0