-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
build-utils.js
145 lines (112 loc) · 4.67 KB
/
build-utils.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
const dotenv = require("dotenv");
const path = require("path");
const childProcess = require("child_process");
const fs = require("fs");
function defineEnvVars() {
dotenv.config({ path: '.env.local' });
dotenv.config();
const defines = {};
for (const envVar of Object.keys(process.env)) {
const value = process.env[envVar].trim();
let replacement;
if (value.trim() === 'true' || value.trim() === 'false') {
replacement = value;
} else if (!Number.isNaN(parseFloat(value))) {
replacement = parseFloat(value).toString();
} else {
replacement = `"${value}"`;
}
defines[`process.env.${envVar}`] = replacement;
}
return defines;
}
module.exports.defineEnvVars = defineEnvVars;
/**
* @param projectRoot {string} the project root folder, as a path relative to the root of the repository
* @param globalName {string|undefined} the name of the global to define in the output IIFE
* @param entryPoint {string} the entrypoint path, as an absolute path
* @param outFile {string} the output file path, as a path relative to the root of the repository
* @param tsConfigDir {string?} the directory containing the tsconfig.json file to use, optionally
*
* @returns {import('esbuild').BuildOptions}
*/
function esbuildModuleBuild(projectRoot, globalName, entryPoint, outFile, tsConfigDir) {
const isProductionBuild = process.env.FBW_PRODUCTION_BUILD === '1';
process.chdir(projectRoot);
return {
absWorkingDir: __dirname,
define: { DEBUG: 'false', ...defineEnvVars() },
plugins: [
typecheckingPlugin(),
],
tsconfig: tsConfigDir !== undefined ? path.join(tsConfigDir, 'tsconfig.json') : undefined,
entryPoints: [entryPoint],
bundle: true,
treeShaking: false,
minify: isProductionBuild,
outfile: path.join(__dirname, outFile),
format: 'iife',
globalName,
sourcemap: isProductionBuild ? undefined : 'linked',
// Target approximate CoherentGT WebKit version
target: 'safari11',
};
}
module.exports.createModuleBuild = esbuildModuleBuild;
/**
* Returns an esbuild plugin which runs `tsc` typechecking
*
* @returns {import('esbuild').Plugin}
*/
function typecheckingPlugin() {
return {
name: 'typecheck',
/**
* @param build {import('esbuild').PluginBuild}
*/
setup(build) {
build.onStart(() => {
if (!(process.env.FBW_TYPECHECK === '1' || process.env.FBW_TYPECHECK?.toLowerCase() === 'true')) {
return;
}
const { entryPoints } = build.initialOptions;
const entryPointDir = path.dirname(entryPoints[0]);
const tsConfigInEntryPointDir = fs.existsSync(path.join(entryPointDir, 'tsconfig.json'));
let tsConfigDir;
if (tsConfigInEntryPointDir) {
tsConfigDir = entryPointDir;
} else if (build.initialOptions.tsconfig !== undefined) {
tsConfigDir = path.dirname(build.initialOptions.tsconfig);
}
if (tsConfigDir === undefined) {
throw new Error(`Cannot run typechecking: no tsconfig.json file found in '${entryPointDir}' and tsconfig path not specified`);
}
/**
* @type {import('esbuild').PartialMessage[]}
*/
const errors = []
try {
childProcess.execSync('npx tsc --noEmit -p .', { cwd: tsConfigDir });
} catch (e) {
if (!('stdout' in e) || !e.stdout) {
throw e;
}
const tscErrors = e.stdout.toString().split('\n').filter((err) => err.trim() !== '');
errors.push(...tscErrors.map((err) => {
const match = /(.+)\((\d+),(\d+)\):\s+(.+)/.exec(err.trim());
if (match) {
const [, file, line, column, text] = match;
const filePath = path.resolve(tsConfigDir, file);
const lineText = fs.readFileSync(filePath).toString().split('\n')[line - 1];
return { text, location: { file, line: parseInt(line), column: parseInt(column) - 1, lineText } }
} else {
return { text: err };
}
}));
}
return { errors };
})
}
}
}
module.exports.typecheckingPlugin = typecheckingPlugin;