Skip to content

Commit dc32c9a

Browse files
committed
Test utility for checking TypeScript compilation in-memory
1 parent 79a14d7 commit dc32c9a

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

src/tests/compile-ts-fragment.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2020 The Polymer Project Authors. All rights reserved.
4+
* This code may only be used under the BSD style license found at
5+
* http://polymer.github.io/LICENSE.txt The complete set of authors may be found
6+
* at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
7+
* be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
8+
* Google as part of the polymer project is also subject to an additional IP
9+
* rights grant found at http://polymer.github.io/PATENTS.txt
10+
*/
11+
12+
import * as ts from 'typescript';
13+
14+
/**
15+
* Filesystem caching for the TypeScript CompilerHost implementation used in
16+
* `compileTsFragment`. Shared across multiple invocations to significantly
17+
* speed up the loading of common files (e.g. TypeScript core lib d.ts files).
18+
* Assumes files on disk never change.
19+
*/
20+
export class CompilerHostCache {
21+
fileExists = new Map<string, boolean>();
22+
readFile = new Map<string, string | undefined>();
23+
getDirectories = new Map<string, string[]>();
24+
getSourceFile = new Map<string, ts.SourceFile | undefined>();
25+
}
26+
27+
/**
28+
* The return type of `compileTsFragment`.
29+
*/
30+
export interface CompileResult {
31+
/** The compiled code. */
32+
code: string;
33+
/** Errors and warnings from compilation. */
34+
diagnostics: ts.Diagnostic[];
35+
}
36+
37+
/**
38+
* Compile a fragment of TypeScript source code with the given options and
39+
* optional transformers.
40+
*/
41+
export function compileTsFragment(
42+
inputCode: string,
43+
options: ts.CompilerOptions,
44+
cache: CompilerHostCache,
45+
transformers?: ts.CustomTransformers
46+
): CompileResult {
47+
const dummyTsFilename = '__DUMMY__.ts';
48+
const dummyJsFilename = '__DUMMY__.js';
49+
const dummySourceFile = ts.createSourceFile(
50+
dummyTsFilename,
51+
inputCode,
52+
ts.ScriptTarget.Latest
53+
);
54+
55+
// Patch this host to [1] hallucinate our code fragment as a virtual file and
56+
// intercept its compiled output, and [2] use the cache for real filesystem
57+
// operations.
58+
const host = ts.createCompilerHost(options);
59+
60+
const realFileExists = host.fileExists.bind(host);
61+
host.fileExists = (name) => {
62+
if (name === dummyTsFilename) {
63+
return true;
64+
}
65+
let exists = cache.fileExists.get(name);
66+
if (exists === undefined) {
67+
exists = realFileExists(name);
68+
cache.fileExists.set(name, exists);
69+
}
70+
return exists;
71+
};
72+
73+
const realReadFile = host.readFile.bind(host);
74+
host.readFile = (name) => {
75+
if (name === dummyTsFilename) {
76+
return inputCode;
77+
}
78+
if (cache.readFile.has(name)) {
79+
return cache.readFile.get(name);
80+
}
81+
const data = realReadFile(name);
82+
cache.readFile.set(name, data);
83+
return data;
84+
};
85+
86+
const realGetDirectories = host.getDirectories?.bind(host);
87+
host.getDirectories = (path) => {
88+
if (!realGetDirectories) {
89+
return [];
90+
}
91+
let dirs = cache.getDirectories.get(path);
92+
if (dirs === undefined) {
93+
dirs = realGetDirectories(path);
94+
cache.getDirectories.set(path, dirs);
95+
}
96+
return dirs;
97+
};
98+
99+
const realGetSourceFile = host.getSourceFile.bind(host);
100+
host.getSourceFile = (name, ...args) => {
101+
if (name === dummyTsFilename) {
102+
return dummySourceFile;
103+
}
104+
let file = cache.getSourceFile.get(name);
105+
if (file === undefined) {
106+
file = realGetSourceFile(name, ...args);
107+
cache.getSourceFile.set(name, file);
108+
}
109+
return file;
110+
};
111+
112+
let outputCode = '';
113+
host.writeFile = (name, data) => {
114+
if (name === dummyJsFilename) {
115+
outputCode = data;
116+
} else {
117+
throw new Error('Did not expect to write file other than dummy JS');
118+
}
119+
};
120+
121+
const program = ts.createProgram([dummyTsFilename], options, host);
122+
const emitResult = program.emit(
123+
undefined,
124+
undefined,
125+
undefined,
126+
undefined,
127+
transformers
128+
);
129+
return {
130+
code: outputCode,
131+
diagnostics: emitResult.diagnostics.concat(
132+
ts.getPreEmitDiagnostics(program)
133+
),
134+
};
135+
}

0 commit comments

Comments
 (0)