-
Notifications
You must be signed in to change notification settings - Fork 671
/
index.js
128 lines (92 loc) · 3.74 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import {
flattenDeep,
find,
chunk,
uniq,
} from 'lodash';
import stripBom from 'strip-bom';
import { readFile } from '../utils/promisified-functions';
import { GeneralError } from '../errors/runtime';
import { RUNTIME_ERRORS } from '../errors/types';
import { getTestFileCompilers, initTestFileCompilers } from './compilers';
const SOURCE_CHUNK_LENGTH = 1000;
export default class Compiler {
constructor (sources, compilerOptions, { baseUrl, esm } = {} ) {
this.sources = sources;
this.esm = esm;
initTestFileCompilers(compilerOptions, { baseUrl, esm });
}
static getSupportedTestFileExtensions () {
return uniq(flattenDeep(getTestFileCompilers().map(compiler => compiler.getSupportedExtension())));
}
static async createTestFileInfo (filename, esm = false) {
let code = null;
try {
code = await readFile(filename);
}
catch (err) {
throw new GeneralError(RUNTIME_ERRORS.cannotFindSpecifiedTestSource, filename);
}
code = stripBom(code).toString();
const compiler = find(getTestFileCompilers(esm), someCompiler => someCompiler.canCompile(code, filename));
if (!compiler)
return null;
return {
filename,
code,
compiler,
compiledCode: null,
};
}
async _createTestFilesInfo (filenames) {
const testFilesInfo = await Promise.all(filenames.map(filename => Compiler.createTestFileInfo(filename)));
return testFilesInfo.filter(info => !!info);
}
async _precompileFiles (compiler, testFilesInfo) {
if (!compiler.canPrecompile || this.esm && compiler.canCompileInEsm)
return;
const precompiledCode = await compiler.precompile(testFilesInfo);
for (let i = 0; i < testFilesInfo.length; i++)
testFilesInfo[i].compiledCode = precompiledCode[i];
}
_getCompilerTasks (testFilesInfo) {
const tasks = new WeakMap();
const compilers = [];
for (const info of testFilesInfo) {
const { compiler } = info;
if (!tasks.has(compiler)) {
compilers.push(compiler);
tasks.set(compiler, []);
}
tasks.get(info.compiler).push(info);
}
return compilers.map(compiler => ({ compiler, compilerTestFilesInfo: tasks.get(compiler) }));
}
async _getTests ({ compiler, filename, code, compiledCode }) {
if (compiledCode || this.esm && compiler.canCompileInEsm)
return await compiler.execute(compiledCode, filename);
return await compiler.compile(code, filename);
}
async _compileTestFiles (filenames) {
const testFilesInfo = await this._createTestFilesInfo(filenames);
const compilerTasks = this._getCompilerTasks(testFilesInfo);
await Promise.all(compilerTasks.map(({ compiler, compilerTestFilesInfo }) => this._precompileFiles(compiler, compilerTestFilesInfo)));
const tests = [];
for (const info of testFilesInfo)
tests.push(await this._getTests(info));
return tests;
}
async getTests () {
// NOTE: split sources into chunks because the fs module can't read all files
// simultaneously if the number of them is too large (several thousands).
const sourceChunks = chunk(this.sources, SOURCE_CHUNK_LENGTH);
let tests = [];
while (sourceChunks.length)
tests = tests.concat(await this._compileTestFiles(sourceChunks.shift()));
Compiler.cleanUp();
return flattenDeep(tests).filter(test => !!test);
}
static cleanUp () {
getTestFileCompilers().forEach(compiler => compiler.cleanUp());
}
}