/
transform.js
110 lines (102 loc) · 3.61 KB
/
transform.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
/* eslint global-require: 0, import/no-dynamic-require: 0, object-curly-newline: 0 */
const path = require('path');
const fs = require('fs');
const importCwd = require('import-cwd');
const { isSourceMap, isTypescript, getPathWithExt, ensureFilePath } = require('./file-utils');
function tryRequire(name) {
const found = importCwd.silent(name);
if (!found) {
return require(`${name}`);
}
return found;
}
const babel = tryRequire('babel-core');
const babelPluginIstanbul = tryRequire('babel-plugin-istanbul').default;
const tsc = tryRequire('typescript');
const cacheSourceMap = new Map();
const cacheTransform = new Map();
function readFile(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, content) => {
if (err) {
reject(err);
return;
}
resolve(content);
});
});
}
function getBabelOpts(filePath, argv) {
const sourceRoot = argv.coverage ? path.dirname(filePath) : null;
const filename = filePath;
const plugins = argv.coverage && argv.instrument.testExclude.shouldInstrument(filePath) ?
[[babelPluginIstanbul, {}]] :
[];
return { filename, sourceRoot, plugins };
}
function transformTypescript(filePath, sourceRoot, tsContent, argv) {
const { transform: { typescript: { compilerOptions } } } = argv;
const fileName = argv.coverage ? path.basename(filePath) : filePath;
compilerOptions.sourceRoot = argv.coverage ? path.resolve(path.dirname(filePath)) : sourceRoot;
compilerOptions.inlineSources = true;
if (!compilerOptions.sourceMap && !compilerOptions.inlineSourceMap) {
compilerOptions.inlineSourceMap = true;
}
if (compilerOptions.module !== 'amd') {
compilerOptions.module = 'amd';
}
if (!compilerOptions.target) {
compilerOptions.target = 'es5';
}
const transpileOpts = { fileName, compilerOptions };
const res = tsc.transpileModule(tsContent, transpileOpts);
tsContent = res.outputText;
let tsBabelOpts = {};
if (res.sourceMapText) {
const inputSourceMap = JSON.parse(res.sourceMapText);
tsBabelOpts = { sourceMaps: true, inputSourceMap };
} else {
tsBabelOpts = { sourceMaps: 'inline' };
}
return { tsContent, tsBabelOpts };
}
async function transformFile(filePath, argv) {
if (isSourceMap(filePath)) {
return cacheSourceMap.get(filePath);
}
filePath = ensureFilePath(filePath);
const cache = cacheTransform.get(filePath);
if (cache) {
return cache;
}
let content = await readFile(filePath);
let babelOpts = getBabelOpts(filePath, argv);
if (isTypescript(filePath)) {
const { tsContent, tsBabelOpts } = transformTypescript(filePath, babelOpts.sourceRoot, content, argv); // eslint-disable-line
content = tsContent;
babelOpts = Object.assign({}, babelOpts, tsBabelOpts);
}
const { code, map } = babel.transform(content, babelOpts);
if (map) {
const key = getPathWithExt(filePath, 'js.map');
cacheSourceMap.set(key, map);
}
cacheTransform.set(filePath, code);
return code;
}
module.exports = function transform(argv) {
return async (ctx, next) => {
await next();
let { url } = ctx;
// We need to remove the leading slash else it will be excluded by default
if (ctx.url.length && ctx.url.startsWith('/')) {
url = ctx.url.substring(1);
}
const shouldInstrument = argv.coverage && argv.instrument && argv.instrument.testExclude.shouldInstrument(url); //eslint-disable-line
const shouldTransform = argv.transform && argv.transform.testExclude.shouldInstrument(url);
if (shouldInstrument || shouldTransform) {
const { response } = ctx;
response.body = await transformFile(url, argv);
}
};
};