Skip to content

Commit 86d7edf

Browse files
committed
WIP
1 parent 94f96cd commit 86d7edf

File tree

3 files changed

+64
-46
lines changed

3 files changed

+64
-46
lines changed

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
"diff": "^4.0.1",
162162
"make-error": "^1.1.1",
163163
"source-map-support": "^0.5.17",
164+
"v8-compile-cache": "^2.2.0",
164165
"yn": "3.1.1"
165166
},
166167
"prettier": {

src/index.ts

Lines changed: 58 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { relative, basename, extname, resolve, dirname, join } from 'path';
21
import { Module } from 'module';
2+
import { relative, basename, extname, resolve, dirname, join, delimiter as pathDelimiter } from 'path';
33
import * as util from 'util';
44
import { fileURLToPath } from 'url';
55

6+
import * as ynModule from 'yn';
67
import sourceMapSupport = require('source-map-support');
78
import { BaseError } from 'make-error';
89
import type * as _ts from 'typescript';
@@ -374,21 +375,6 @@ export interface Service {
374375
*/
375376
export type Register = Service;
376377

377-
/**
378-
* Cached fs operation wrapper.
379-
*/
380-
function cachedLookup<T>(fn: (arg: string) => T): (arg: string) => T {
381-
const cache = new Map<string, T>();
382-
383-
return (arg: string): T => {
384-
if (!cache.has(arg)) {
385-
cache.set(arg, fn(arg));
386-
}
387-
388-
return cache.get(arg)!;
389-
};
390-
}
391-
392378
/** @internal */
393379
export function getExtensions(config: _ts.ParsedCommandLine) {
394380
const tsExtensions = ['.ts'];
@@ -779,29 +765,42 @@ export function create(rawOptions: CreateOptions = {}): Service {
779765
}
780766

781767
/**
782-
* Create filesystem access functions usable in `*Host` implementations which
783-
* implement appropriate caching
768+
* Create filesystem access functions which implement appropriate caching and
769+
* are usable in `*Host` implementations.
784770
*/
785-
function createCachedFilesystemFunctions(opts: {
771+
function createCachedFilesystem(opts: {
786772
readFile: ReadFileFunction;
787773
fileExists: FileExistsFunction;
788774
}) {
789775
const { readFile: _readFile, fileExists: _fileExists } = opts;
790-
const readFile = cachedLookup(debugFn('readFile', _readFile));
791-
const readDirectory = ts.sys.readDirectory;
776+
777+
const fileExistsCache = new Map<string, boolean>();
778+
const fileExists = cachedLookup(debugFn('fileExists', _fileExists), fileExistsCache);
779+
const readFileCache = new Map<string, string | undefined>();
780+
const readFile = cachedLookup(debugFn('readFile', _readFile), readFileCache);
781+
function setFileContents(path: string, content: string | undefined) {
782+
readFileCache.set(path, content);
783+
fileExistsCache.set(path, true);
784+
}
785+
// Not cached until TS exposes a proper way to inject fs dependencies so that
786+
// this function obeys our readFile, etc caches.
787+
const readDirectory = (path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[] => {
788+
debug('readDirectory', path, extensions, exclude, include, depth);
789+
return ts.sys.readDirectory(path, extensions, exclude, include, depth);
790+
}
792791
const getDirectories = cachedLookup(
793792
debugFn('getDirectories', ts.sys.getDirectories)
794793
);
795-
const fileExists = cachedLookup(debugFn('fileExists', _fileExists));
796794
const directoryExists = cachedLookup(
797795
debugFn('directoryExists', ts.sys.directoryExists)
798796
);
799-
const resolvePath = cachedLookup(
800-
debugFn('resolvePath', ts.sys.resolvePath)
801-
);
797+
// Cache probably has little effect; this is an in-memory transformation based on CWD
798+
const resolvePath = debugFn('resolvePath', ts.sys.resolvePath);
799+
// Note: cache does not understand when intermediate symlinks are changed; cannot be invalidated correctly.
802800
const realpath = ts.sys.realpath
803801
? cachedLookup(debugFn('realpath', ts.sys.realpath))
804802
: undefined;
803+
805804
return {
806805
readFile,
807806
readDirectory,
@@ -810,7 +809,19 @@ export function create(rawOptions: CreateOptions = {}): Service {
810809
directoryExists,
811810
resolvePath,
812811
realpath,
812+
setFileContents,
813+
fileContents: readFileCache,
813814
};
815+
816+
function cachedLookup<T>(fn: (arg: string) => T, cache = new Map<string, T>()): (arg: string) => T {
817+
return (arg: string): T => {
818+
if (!cache.has(arg)) {
819+
cache.set(arg, fn(arg));
820+
}
821+
822+
return cache.get(arg)!;
823+
};
824+
}
814825
}
815826

816827
function createUpdateMemoryCacheFunction(opts: {
@@ -823,12 +834,14 @@ export function create(rawOptions: CreateOptions = {}): Service {
823834
>['markBucketOfFilenameInternal'];
824835
rootFileNames: Set<string>;
825836
fileVersions: Map<string, number>;
826-
fileContents: Map<string, string>;
837+
fileContents: Map<string, string | undefined>;
838+
setFileContents(path: string, contents: string | undefined): void;
827839
}) {
828840
const {
829841
onProjectMustUpdate,
830842
isFileKnownToBeInternal,
831843
fileContents,
844+
setFileContents,
832845
fileVersions,
833846
markBucketOfFilenameInternal,
834847
rootFileNames,
@@ -848,7 +861,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
848861
// Avoid incrementing cache when nothing has changed.
849862
if (contents !== previousContents) {
850863
fileVersions.set(fileName, previousVersion + 1);
851-
fileContents.set(fileName, contents);
864+
setFileContents(fileName, contents);
852865
projectMustUpdate = true;
853866
}
854867
if (projectMustUpdate) onProjectMustUpdate();
@@ -866,8 +879,9 @@ export function create(rawOptions: CreateOptions = {}): Service {
866879
readDirectory,
867880
realpath,
868881
resolvePath,
869-
} = createCachedFilesystemFunctions({ readFile, fileExists });
870-
const fileContents = new Map<string, string>();
882+
setFileContents,
883+
fileContents,
884+
} = createCachedFilesystem({ readFile, fileExists });
871885
const rootFileNames = new Set(config.fileNames);
872886
const fileVersions = new Map(
873887
Array.from(rootFileNames).map((fileName) => [fileName, 0])
@@ -891,26 +905,22 @@ export function create(rawOptions: CreateOptions = {}): Service {
891905
Required<Pick<_ts.LanguageServiceHost, 'fileExists' | 'readFile'>> = {
892906
getProjectVersion: () => String(projectVersion),
893907
getScriptFileNames: () => Array.from(rootFileNames),
894-
getScriptVersion: (fileName: string) => {
895-
const version = fileVersions.get(fileName);
896-
return version ? version.toString() : '';
897-
},
908+
// Language service calls getScriptSnapshot, then getScriptVersion, in that order
898909
getScriptSnapshot(fileName: string) {
899-
// TODO ordering of this with getScriptVersion? Should they sync up?
900-
let contents = fileContents.get(fileName);
901-
902-
// Read contents into TypeScript memory cache.
903-
if (contents === undefined) {
904-
contents = cachedReadFile(fileName);
905-
if (contents === undefined) return;
906-
907-
fileVersions.set(fileName, 1);
908-
fileContents.set(fileName, contents);
909-
projectVersion++;
910-
}
911-
910+
debug('getScriptSnapshot', fileName);
911+
const contents = cachedReadFile(fileName);
912+
if (contents === undefined) return;
912913
return ts.ScriptSnapshot.fromString(contents);
913914
},
915+
getScriptVersion(fileName: string) {
916+
debug('getScriptVersion', fileName);
917+
let version = fileVersions.get(fileName);
918+
if(version === undefined) {
919+
version = 1;
920+
fileVersions.set(fileName, version);
921+
}
922+
return version.toString();
923+
},
914924
readFile: cachedReadFile,
915925
readDirectory,
916926
getDirectories,
@@ -944,6 +954,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
944954
const { updateMemoryCache } = createUpdateMemoryCacheFunction({
945955
rootFileNames,
946956
fileContents,
957+
setFileContents,
947958
fileVersions,
948959
isFileKnownToBeInternal,
949960
markBucketOfFilenameInternal,
@@ -1090,6 +1101,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
10901101
rootFileNames,
10911102
fileVersions,
10921103
fileContents,
1104+
setFileContents,
10931105
isFileKnownToBeInternal,
10941106
markBucketOfFilenameInternal,
10951107
onProjectMustUpdate() {

0 commit comments

Comments
 (0)