-
-
Notifications
You must be signed in to change notification settings - Fork 215
Expand file tree
/
Copy pathengineBuild.mjs
More file actions
248 lines (221 loc) · 7.34 KB
/
engineBuild.mjs
File metadata and controls
248 lines (221 loc) · 7.34 KB
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/**
* LittleJS Build System
* - Concatenates engine source files into distributable bundles
* - Generates multiple output formats (standard, ES6 module, minified)
* - Creates debug and release builds (with/without debug code)
* - Produces TypeScript definition file (.d.ts)
* - Includes plugin files in builds
* - Validates code and checks for errors
* - Outputs to dist/ folder
*/
import fs from 'node:fs';
import { execSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ROOT_DIR = join(__dirname, '..');
const ENGINE_NAME = 'littlejs';
const BUILD_FOLDER = join(ROOT_DIR, 'dist');
const SOURCE_FOLDER = join(ROOT_DIR, 'src');
const PLUGIN_FOLDER = join(ROOT_DIR, 'plugins');
const engineSourceFiles =
[
`${SOURCE_FOLDER}/engineMath.js`,
`${SOURCE_FOLDER}/engineUtilities.js`,
`${SOURCE_FOLDER}/engineSettings.js`,
`${SOURCE_FOLDER}/engineObject.js`,
`${SOURCE_FOLDER}/engineDraw.js`,
`${SOURCE_FOLDER}/engineInput.js`,
`${SOURCE_FOLDER}/engineAudio.js`,
`${SOURCE_FOLDER}/engineTileLayer.js`,
`${SOURCE_FOLDER}/engineParticles.js`,
`${SOURCE_FOLDER}/engineMedals.js`,
`${SOURCE_FOLDER}/engineWebGL.js`,
];
const enginePluginFiles =
[
`${PLUGIN_FOLDER}/newgrounds.js`,
`${PLUGIN_FOLDER}/postProcess.js`,
`${PLUGIN_FOLDER}/zzfxm.js`,
`${PLUGIN_FOLDER}/uiSystem.js`,
`${PLUGIN_FOLDER}/box2d.js`,
`${PLUGIN_FOLDER}/drawUtilities.js`,
];
const engineExtraFiles =
[
`${PLUGIN_FOLDER}/box2d.wasm.js`,
`${PLUGIN_FOLDER}/box2d.wasm.wasm`,
];
const asciiArt =`
~~~~°°°°ooo°oOo°ooOooOooOo.
__________ ________ ____'°oO.
|LittleJS| |Engine| |[]|_._Y
.|________|_._|______|_._|__|_|_|}
OOO OOO OO OO OO=OO-oo\\
`;
const license = '// LittleJS Engine - MIT License - Copyright 2021 Frank Force\n'+
'// https://github.com/KilledByAPixel/LittleJS\n\n';
console.log(asciiArt);
console.log('Choo Choo... Building LittleJS Engine!');
const startTime = Date.now();
try
{
// Setup build folder
fs.rmSync(BUILD_FOLDER, { recursive: true, force: true });
fs.mkdirSync(BUILD_FOLDER);
// copy extra files to build folder
for (const file of engineExtraFiles)
fs.copyFileSync(file, `${BUILD_FOLDER}/${file.substring(file.lastIndexOf('/')+1)}`);
}
catch (e) { handleError(e, 'Failed to create build folder!'); }
// Build all versions
await buildAll();
console.log(`Engine built in ${((Date.now() - startTime)/1e3).toFixed(2)} seconds! ✨`);
///////////////////////////////////////////////////////////////////////////////
async function buildAll()
{
// Build independent base versions in parallel
await Promise.all([
Build
(
'Build Engine -- all',
`${BUILD_FOLDER}/${ENGINE_NAME}.js`,
[
`${SOURCE_FOLDER}/engine.js`,
`${SOURCE_FOLDER}/engineDebug.js`,
...engineSourceFiles,
...enginePluginFiles
],
[], true
),
Build
(
'Build Engine -- release',
`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
[
`${SOURCE_FOLDER}/engine.js`,
`${SOURCE_FOLDER}/engineRelease.js`,
...engineSourceFiles,
...enginePluginFiles
],
[], true
)
]);
// Build dependent versions in parallel
await Promise.all([
Build
(
'Build Engine -- minified',
`${BUILD_FOLDER}/${ENGINE_NAME}.min.js`,
[`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`],
[closureCompilerStep, uglifyBuildStep, addLicenseStep]
),
Build
(
'Build Engine -- ESM',
`${BUILD_FOLDER}/${ENGINE_NAME}.esm.js`,
[
`${BUILD_FOLDER}/${ENGINE_NAME}.js`,
`${SOURCE_FOLDER}/engineExport.js`,
`${PLUGIN_FOLDER}/pluginExport.js`
],
[typeScriptBuildStep]
),
Build
(
'Build Engine -- ESM minified release',
`${BUILD_FOLDER}/${ENGINE_NAME}.esm.min.js`,
[
`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
`${SOURCE_FOLDER}/engineExport.js`,
`${PLUGIN_FOLDER}/pluginExport.js`
],
[uglifyBuildStep, addLicenseStep]
)
]);
}
///////////////////////////////////////////////////////////////////////////////
// A single build with its own source files, build steps, and output file
// - each build step is a callback that accepts a single filename
async function Build(message, outputFile, files=[], buildSteps=[], isPrimaryBuild)
{
console.log(message);
// copy files into a buffer
let buffer = '';
if (isPrimaryBuild)
{
// add license and strict mode to top
buffer += license;
buffer += `'use strict';\n\n`;
}
for (const file of files)
{
// get file content
let fileContent = fs.readFileSync(file) + '\n';
// remove first 'use strict' and surrounding new lines
if (isPrimaryBuild)
fileContent = fileContent.replace(/'use strict';\s*\n/g, '');
// add it to the buffer
buffer += fileContent;
}
// output file
fs.writeFileSync(outputFile, buffer, {flag: 'w+'});
// execute build steps in order
for (const buildStep of buildSteps)
buildStep(outputFile);
}
// Process with Closure Compiler to minify and check for errors
function closureCompilerStep(filename)
{
const filenameTemp = filename + '.tmp';
fs.copyFileSync(filename, filenameTemp);
try
{
execSync(`npx google-closure-compiler --js=${filenameTemp} --js_output_file=${filename} --warning_level=VERBOSE --jscomp_off=*`);
fs.rmSync(filenameTemp);
}
catch (e) { handleError(e, 'Failed to run Closure Compiler step!'); }
}
// Process with Uglify to minify
function uglifyBuildStep(filename)
{
try
{
execSync(`npx uglifyjs ${filename} -o ${filename}`);
}
catch (e) { handleError(e,'Failed to run Uglify minification step!'); }
};
// Add license to top of file
function addLicenseStep(filename)
{
try
{
// add license to top of minified file
let fileContent = fs.readFileSync(filename, 'utf8');
fileContent = license + fileContent;
fs.writeFileSync(filename, fileContent);
}
catch (e) { handleError(e, 'Failed to add license to minified file!'); }
};
// Build TypeScript definitions
function typeScriptBuildStep(filename)
{
try
{
const tsFilename = join(BUILD_FOLDER, `${ENGINE_NAME}.d.ts`);
execSync(`npx -p typescript tsc ${filename} --declaration --allowJs --emitDeclarationOnly --outFile ${tsFilename}`);
// Make declare module part use the package name littlejsengine
let fileContent = fs.readFileSync(tsFilename, 'utf8');
fileContent = fileContent.replace(`${ENGINE_NAME}\.esm`, 'littlejsengine')
fs.writeFileSync(tsFilename, fileContent);
}
catch (e) { handleError(e, 'Failed to run TypeScript build step!'); }
};
// display the error and exit
function handleError(e,message)
{
console.error(e);
console.error(message);
process.exit(1);
}