html-bundle 5.5.2 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -30
- package/dist/bundle.mjs +279 -566
- package/dist/utils.mjs +195 -0
- package/logo.jpg +0 -0
- package/package.json +2 -1
- package/src/bundle.mts +315 -653
- package/src/index.d.ts +3 -0
- package/src/utils.mts +231 -0
package/src/bundle.mts
CHANGED
|
@@ -1,732 +1,394 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import type { TextNode } from "parse5";
|
|
4
|
+
import type { AcceptedPlugin } from "postcss";
|
|
4
5
|
import { performance } from "perf_hooks";
|
|
5
|
-
import
|
|
6
|
+
import { readFile, rm, writeFile, readdir } from "fs/promises";
|
|
7
|
+
import { execFile } from "child_process";
|
|
8
|
+
import { promisify } from "util";
|
|
6
9
|
import glob from "glob";
|
|
7
|
-
import
|
|
8
|
-
import Fastify, {
|
|
9
|
-
FastifyReply,
|
|
10
|
-
FastifyRequest,
|
|
11
|
-
FastifyServerOptions,
|
|
12
|
-
} from "fastify";
|
|
13
|
-
import fastifyStatic from "fastify-static";
|
|
14
|
-
import postcss, { AcceptedPlugin, ProcessOptions } from "postcss";
|
|
15
|
-
import postcssrc from "postcss-load-config";
|
|
16
|
-
import cssnano from "cssnano";
|
|
10
|
+
import postcss from "postcss";
|
|
17
11
|
import esbuild from "esbuild";
|
|
18
12
|
import critical from "critical";
|
|
19
13
|
import { minify } from "html-minifier-terser";
|
|
20
14
|
import { watch } from "chokidar";
|
|
21
15
|
import { serialize, parse, parseFragment } from "parse5";
|
|
16
|
+
import { getTagName, findElements } from "@web/parse5-utils";
|
|
17
|
+
import awaitSpawn from "await-spawn";
|
|
22
18
|
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const isHMR = process.argv.includes("--hmr");
|
|
34
|
-
const
|
|
35
|
-
const isSecure = process.argv.includes("--secure");
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
let expectedTasks = 0; // This will be increased in globHandlers
|
|
55
|
-
let finishedTasks = 0; // Current status
|
|
56
|
-
taskEmitter.on("done", () => {
|
|
57
|
-
finishedTasks++;
|
|
58
|
-
|
|
59
|
-
if (finishedTasks === expectedTasks) {
|
|
60
|
-
console.log(
|
|
61
|
-
`🚀 Build finished in ${(performance.now() - start).toFixed(2)}ms ✨`
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
if (isHMR && !addedHMR) {
|
|
65
|
-
addedHMR = true;
|
|
66
|
-
if (file) {
|
|
67
|
-
const postCSSWatcher = watch(file);
|
|
68
|
-
const tailwindCSSWatcher = watch(file.replace("postcss", "tailwind"));
|
|
69
|
-
postCSSWatcher.on("change", () => rebuildCSS("postcss"));
|
|
70
|
-
tailwindCSSWatcher.on("change", () => rebuildCSS("tailwind"));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
console.log(`⌛ Waiting for file changes ...`);
|
|
74
|
-
const watcher = watch(SOURCE_FOLDER);
|
|
75
|
-
// The add watcher will add all the files initially - do not rebuild them
|
|
76
|
-
let initialAdd = 0;
|
|
77
|
-
let hasJSTS = false;
|
|
78
|
-
|
|
79
|
-
watcher.on("add", (filename) => {
|
|
80
|
-
start = performance.now();
|
|
81
|
-
rebuildCSS();
|
|
82
|
-
// Return if it was added by the build system itself
|
|
83
|
-
if (/-bundle-\d+\.(j|t)sx?$/.test(filename)) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
filename = String.raw`${filename}`.replace(/\\/g, "/");
|
|
88
|
-
if (filename.endsWith(".html") || filename.endsWith(".css")) {
|
|
89
|
-
initialAdd++;
|
|
90
|
-
} else if (hasJSTS === false && /\.(j|t)sx?$/.test(filename)) {
|
|
91
|
-
hasJSTS = true;
|
|
92
|
-
initialAdd++;
|
|
93
|
-
}
|
|
94
|
-
if (initialAdd <= expectedTasks) return;
|
|
95
|
-
|
|
96
|
-
const [buildFilename, buildPathDir] = getBuildNames(filename);
|
|
97
|
-
fs.mkdir(buildPathDir, { recursive: true }, (err) => {
|
|
98
|
-
errorHandler(err);
|
|
99
|
-
|
|
100
|
-
rebuild(filename);
|
|
101
|
-
console.log(`⚡ added ${buildFilename}`);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
watcher.on("change", (filename) => {
|
|
105
|
-
start = performance.now();
|
|
106
|
-
rebuildCSS();
|
|
107
|
-
// Return if it was changed by the build system itself
|
|
108
|
-
if (/-bundle-\d+\.(j|t)sx?$/.test(filename)) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
19
|
+
fileCopy,
|
|
20
|
+
createDefaultServer,
|
|
21
|
+
getPostCSSConfig,
|
|
22
|
+
getBuildPath,
|
|
23
|
+
createDir,
|
|
24
|
+
bundleConfig,
|
|
25
|
+
serverSentEvents,
|
|
26
|
+
addHMRCode,
|
|
27
|
+
} from "./utils.mjs";
|
|
28
|
+
|
|
29
|
+
const isHMR = process.argv.includes("--hmr") || bundleConfig.hmr;
|
|
30
|
+
const isCritical = process.argv.includes("--critical") || bundleConfig.critical;
|
|
31
|
+
const isSecure = process.argv.includes("--secure") || bundleConfig.secure; // uses CSP for critical too
|
|
32
|
+
const handlerFile = process.argv.includes("--handler")
|
|
33
|
+
? process.argv[process.argv.indexOf("--handler") + 1]
|
|
34
|
+
: bundleConfig.handler;
|
|
35
|
+
|
|
36
|
+
process.env.NODE_ENV = isHMR ? "development" : "production"; // just in case other tools are using it
|
|
37
|
+
let timer = performance.now();
|
|
38
|
+
let { plugins, options, file: postcssFile } = await getPostCSSConfig();
|
|
39
|
+
let CSSprocessor = postcss(plugins as AcceptedPlugin[]);
|
|
40
|
+
let fastify;
|
|
41
|
+
const inlineFiles = new Set<string>();
|
|
42
|
+
const TEMPLATE_LITERAL_MINIFIER = /\n\s+/g;
|
|
43
|
+
const INLINE_BUNDLE_FILE = /-bundle-\d+.tsx$/;
|
|
44
|
+
const SUPPORTED_FILES = /\.(html|css|jsx?|tsx?)$/;
|
|
45
|
+
const execFilePromise = promisify(execFile);
|
|
46
|
+
|
|
47
|
+
if (bundleConfig.deletePrev) {
|
|
48
|
+
await rm(bundleConfig.build, { force: true, recursive: true });
|
|
49
|
+
}
|
|
111
50
|
|
|
112
|
-
|
|
113
|
-
rebuild(filename);
|
|
114
|
-
const [buildFilename] = getBuildNames(filename);
|
|
115
|
-
console.log(`⚡ modified ${buildFilename}`);
|
|
116
|
-
});
|
|
117
|
-
watcher.on("unlink", (filename) => {
|
|
118
|
-
start = performance.now();
|
|
119
|
-
// Return if it was deleted by the build system itself
|
|
120
|
-
if (/-bundle-\d+\.(j|t)sx?$/.test(filename)) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
51
|
+
glob(`${bundleConfig.src}/**/*`, build);
|
|
123
52
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
errorHandler(err);
|
|
129
|
-
|
|
130
|
-
console.log(`⚡ deleted ${buildFilename}`);
|
|
131
|
-
const length = fs.readdirSync(buildPathDir).length;
|
|
132
|
-
if (!length) fs.rmdir(buildPathDir, errorHandler);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
function rebuildCSS(config?: string) {
|
|
140
|
-
start = performance.now();
|
|
141
|
-
if (config)
|
|
142
|
-
console.log(`⚡ modified ${config}.config – CSS will rebuild now.`);
|
|
143
|
-
const newConfig = createPostCSSConfig();
|
|
144
|
-
plugins = newConfig.plugins;
|
|
145
|
-
options = newConfig.options;
|
|
146
|
-
CSSprocessor = postcss(plugins as AcceptedPlugin[]);
|
|
147
|
-
glob(`${SOURCE_FOLDER}/**/*.css`, {}, (err, files) => {
|
|
148
|
-
errorHandler(err);
|
|
149
|
-
expectedTasks += files.length;
|
|
150
|
-
for (const filename of files) {
|
|
151
|
-
const [buildFilename, buildPathDir] = getBuildNames(filename);
|
|
152
|
-
fs.mkdirSync(buildPathDir, { recursive: true });
|
|
153
|
-
minifyCSS(filename, buildFilename);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
53
|
+
async function build(err: any, files: string[], firstRun = true) {
|
|
54
|
+
if (err) {
|
|
55
|
+
console.error(err);
|
|
56
|
+
process.exit(1);
|
|
156
57
|
}
|
|
157
58
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const CONNECTIONS: Array<any> = []; // HMR
|
|
163
|
-
let htmlTasks = 0;
|
|
164
|
-
|
|
165
|
-
// Server for HMR
|
|
166
|
-
type serverSentEventObject = (
|
|
167
|
-
| { html: string }
|
|
168
|
-
| { css: string }
|
|
169
|
-
| { js: string }
|
|
170
|
-
) & { filename: string };
|
|
171
|
-
let serverSentEvents: undefined | ((data: serverSentEventObject) => void);
|
|
172
|
-
if (isHMR) {
|
|
173
|
-
fastify = Fastify(
|
|
174
|
-
isSecure
|
|
175
|
-
? ({
|
|
176
|
-
http2: true,
|
|
177
|
-
https: {
|
|
178
|
-
key: fs.readFileSync(
|
|
179
|
-
path.join(process.cwd(), "localhost-key.pem")
|
|
180
|
-
),
|
|
181
|
-
cert: fs.readFileSync(path.join(process.cwd(), "localhost.pem")),
|
|
182
|
-
},
|
|
183
|
-
} as FastifyServerOptions)
|
|
184
|
-
: void 0
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
fastify.setNotFoundHandler((_req, reply) => {
|
|
188
|
-
const file = fs.readFileSync(
|
|
189
|
-
path.join(process.cwd(), BUILD_FOLDER, "/index.html"),
|
|
190
|
-
{
|
|
191
|
-
encoding: "utf-8",
|
|
192
|
-
}
|
|
193
|
-
);
|
|
194
|
-
reply.header("Content-Type", "text/html; charset=UTF-8");
|
|
195
|
-
return reply.send(addHMRCode(file, "build/index.html"));
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
fastify.register(fastifyStatic, {
|
|
199
|
-
root: path.join(process.cwd(), BUILD_FOLDER),
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
fastify.get("/events", (_req, reply) => {
|
|
203
|
-
reply.raw.setHeader("Content-Type", "text/event-stream");
|
|
204
|
-
reply.raw.setHeader("Cache-Control", "no-cache");
|
|
205
|
-
!isSecure && reply.raw.setHeader("Connection", "keep-alive");
|
|
206
|
-
|
|
207
|
-
CONNECTIONS.push(reply.raw);
|
|
208
|
-
|
|
209
|
-
serverSentEvents = (data) =>
|
|
210
|
-
CONNECTIONS.forEach((rep) => {
|
|
211
|
-
rep.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
212
|
-
});
|
|
213
|
-
});
|
|
59
|
+
if (isHMR && firstRun) {
|
|
60
|
+
fastify = await createDefaultServer(isSecure);
|
|
61
|
+
fastify.listen(bundleConfig.port);
|
|
62
|
+
console.log(`💻 Sever listening on port ${bundleConfig.port}.`);
|
|
214
63
|
}
|
|
215
64
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
glob(`${SOURCE_FOLDER}/**/*.html`, {}, (err, files) => {
|
|
219
|
-
errorHandler(err);
|
|
220
|
-
|
|
221
|
-
expectedTasks += files.length;
|
|
222
|
-
htmlTasks += files.length;
|
|
223
|
-
|
|
224
|
-
if (isHMR) {
|
|
225
|
-
createHMRHandlers(files);
|
|
226
|
-
fastify.listen(5000);
|
|
227
|
-
console.log(`💻 Sever listening on port 5000.`);
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
glob(`${SOURCE_FOLDER}/**/*.css`, {}, (err, files) => {
|
|
231
|
-
errorHandler(err);
|
|
232
|
-
|
|
233
|
-
expectedTasks += files.length;
|
|
234
|
-
for (const filename of files) {
|
|
235
|
-
const [buildFilename, buildPathDir] = getBuildNames(filename);
|
|
236
|
-
fs.mkdirSync(buildPathDir, { recursive: true });
|
|
237
|
-
minifyCSS(filename, buildFilename);
|
|
238
|
-
}
|
|
239
|
-
});
|
|
65
|
+
for (const file of files) {
|
|
66
|
+
await createDir(file);
|
|
240
67
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
68
|
+
if (!SUPPORTED_FILES.test(file)) {
|
|
69
|
+
if (handlerFile) {
|
|
70
|
+
const { stdout } = await execFilePromise("node", [handlerFile, file]);
|
|
71
|
+
if (String(stdout)) console.log("📋 Logging Handler: ", String(stdout));
|
|
72
|
+
} else {
|
|
73
|
+
await fileCopy(file);
|
|
74
|
+
}
|
|
246
75
|
} else {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
minifyTSJS().catch(errorHandler);
|
|
252
|
-
});
|
|
253
|
-
globHTML.on("getReady", () => {
|
|
254
|
-
if (expectedTasks - htmlTasks === finishedTasks) {
|
|
255
|
-
// After CSS and JS because critical needs file built css files and inline script might reference js files.
|
|
256
|
-
|
|
257
|
-
glob(`${SOURCE_FOLDER}/**/*.html`, {}, async (err, files) => {
|
|
258
|
-
errorHandler(err);
|
|
259
|
-
|
|
260
|
-
await createGlobalJS(files);
|
|
261
|
-
files.forEach((filename) => {
|
|
262
|
-
const [buildFilename, buildPathDir] = getBuildNames(filename);
|
|
263
|
-
fs.mkdirSync(buildPathDir, { recursive: true });
|
|
264
|
-
minifyHTML(filename, buildFilename);
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
function createGlobalJS(files: Array<string>) {
|
|
271
|
-
const scriptFilenames: string[] = [];
|
|
272
|
-
|
|
273
|
-
files.forEach((filename) => {
|
|
274
|
-
const fileText = fs.readFileSync(filename, { encoding: "utf-8" });
|
|
275
|
-
|
|
276
|
-
let DOM;
|
|
277
|
-
if (fileText.includes("<!DOCTYPE html>") || fileText.includes("<html")) {
|
|
278
|
-
DOM = parse(fileText);
|
|
76
|
+
if (file.endsWith(".html")) {
|
|
77
|
+
await writeInlineScripts(file);
|
|
78
|
+
} else if (file.endsWith(".css")) {
|
|
79
|
+
await minifyCSS(file, getBuildPath(file));
|
|
279
80
|
} else {
|
|
280
|
-
|
|
81
|
+
inlineFiles.add(file);
|
|
281
82
|
}
|
|
282
|
-
|
|
283
|
-
const scripts = findElements(DOM, (e) => getTagName(e) === "script");
|
|
284
|
-
scripts.forEach((script, index) => {
|
|
285
|
-
const scriptTextNode = script.childNodes[0];
|
|
286
|
-
const isReferencedScript = script.attrs.find((a) => a.name === "src");
|
|
287
|
-
//@ts-ignore
|
|
288
|
-
const scriptContent = scriptTextNode?.value;
|
|
289
|
-
if (!scriptContent || isReferencedScript) return;
|
|
290
|
-
|
|
291
|
-
const jsFilename = filename.replace(".html", `-bundle-${index}.js`);
|
|
292
|
-
scriptFilenames.push(jsFilename);
|
|
293
|
-
fs.writeFileSync(jsFilename, scriptContent);
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
scriptFilenames.forEach((file) => JSTSFiles.add(file));
|
|
298
|
-
return minifyTSJS(true)
|
|
299
|
-
.catch(console.error)
|
|
300
|
-
.finally(() =>
|
|
301
|
-
scriptFilenames.forEach((file) => {
|
|
302
|
-
JSTSFiles.delete(file);
|
|
303
|
-
try {
|
|
304
|
-
fs.rmSync(file);
|
|
305
|
-
} catch {}
|
|
306
|
-
})
|
|
307
|
-
);
|
|
83
|
+
}
|
|
308
84
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
format: "esm",
|
|
316
|
-
incremental: isHMR,
|
|
317
|
-
sourcemap: isHMR,
|
|
318
|
-
splitting: true,
|
|
319
|
-
define: {
|
|
320
|
-
"process.env.NODE_ENV": isHMR ? '"development"' : '"production"',
|
|
321
|
-
},
|
|
322
|
-
loader: { ".js": "jsx", ".ts": "tsx" },
|
|
323
|
-
bundle: true,
|
|
324
|
-
minify: true,
|
|
325
|
-
outdir: BUILD_FOLDER,
|
|
326
|
-
outbase: SOURCE_FOLDER,
|
|
327
|
-
})
|
|
328
|
-
.then(() => {
|
|
329
|
-
if (!isInline) {
|
|
330
|
-
taskEmitter.emit("done");
|
|
331
|
-
globHTML.emit("getReady");
|
|
332
|
-
|
|
333
|
-
if (serverSentEvents && file) {
|
|
334
|
-
const changedFile = tsMaybeX2JS(file);
|
|
335
|
-
const [buildFilename] = getBuildNames(changedFile);
|
|
336
|
-
const js = fs.readFileSync(buildFilename, { encoding: "utf8" });
|
|
337
|
-
serverSentEvents({
|
|
338
|
-
js,
|
|
339
|
-
filename: buildFilename.split(`${BUILD_FOLDER}/`).pop()!,
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
});
|
|
85
|
+
await minifyCode();
|
|
86
|
+
for (const file of inlineFiles) {
|
|
87
|
+
if (INLINE_BUNDLE_FILE.test(file)) {
|
|
88
|
+
inlineFiles.delete(file);
|
|
89
|
+
await rm(file);
|
|
90
|
+
}
|
|
344
91
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
...(options as ProcessOptions),
|
|
350
|
-
from: filename,
|
|
351
|
-
to: buildFilename,
|
|
352
|
-
})
|
|
353
|
-
.then((result) => {
|
|
354
|
-
fs.writeFileSync(buildFilename, result.css);
|
|
355
|
-
taskEmitter.emit("done");
|
|
356
|
-
globHTML.emit("getReady");
|
|
357
|
-
|
|
358
|
-
if (serverSentEvents) {
|
|
359
|
-
serverSentEvents({
|
|
360
|
-
css: result.css,
|
|
361
|
-
filename: buildFilename.split(`${BUILD_FOLDER}/`).pop()!,
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
})
|
|
365
|
-
.catch((err: Error) => {
|
|
366
|
-
console.error(err);
|
|
367
|
-
});
|
|
92
|
+
for (const file of files) {
|
|
93
|
+
if (file.endsWith(".html")) {
|
|
94
|
+
await minifyHTML(file, getBuildPath(file));
|
|
95
|
+
}
|
|
368
96
|
}
|
|
369
97
|
|
|
370
|
-
|
|
371
|
-
|
|
98
|
+
console.log(
|
|
99
|
+
`🚀 Build finished in ${(performance.now() - timer).toFixed(2)}ms ✨`
|
|
100
|
+
);
|
|
372
101
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
DOM = parse(fileText);
|
|
376
|
-
} else {
|
|
377
|
-
DOM = parseFragment(fileText);
|
|
378
|
-
}
|
|
102
|
+
if (isHMR && firstRun) {
|
|
103
|
+
console.log(`⌛ Waiting for file changes ...`);
|
|
379
104
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
// Use bundled file and remove it from fs
|
|
389
|
-
const bundledFilename = buildFilename.replace(
|
|
390
|
-
".html",
|
|
391
|
-
`-bundle-${index}.js`
|
|
105
|
+
if (postcssFile) {
|
|
106
|
+
const postCSSWatcher = watch(postcssFile);
|
|
107
|
+
const tailwindCSSWatcher = watch(
|
|
108
|
+
postcssFile.replace("postcss", "tailwind")
|
|
109
|
+
); // Assuming that the file ext is the same
|
|
110
|
+
const tsConfigWatcher = watch(
|
|
111
|
+
postcssFile.split("\\").slice(0, -1).join("\\") + "\\tsconfig.json"
|
|
392
112
|
);
|
|
393
|
-
try {
|
|
394
|
-
const scriptContent = fs.readFileSync(bundledFilename, {
|
|
395
|
-
encoding: "utf-8",
|
|
396
|
-
});
|
|
397
|
-
fs.rmSync(bundledFilename);
|
|
398
|
-
// Replace src with bundled code
|
|
399
|
-
//@ts-ignore
|
|
400
|
-
scriptTextNode.value = scriptContent.replace(
|
|
401
|
-
TEMPLATE_LITERAL_MINIFIER,
|
|
402
|
-
" "
|
|
403
|
-
);
|
|
404
|
-
} catch {}
|
|
405
|
-
});
|
|
406
113
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
114
|
+
const cssFiles = files.filter((file) => file.endsWith(".css"));
|
|
115
|
+
postCSSWatcher.on(
|
|
116
|
+
"change",
|
|
117
|
+
async () => await rebuildCSS(cssFiles, "postcss")
|
|
118
|
+
);
|
|
119
|
+
tailwindCSSWatcher.on(
|
|
120
|
+
"change",
|
|
121
|
+
async () => await rebuildCSS(cssFiles, "tailwind")
|
|
122
|
+
);
|
|
123
|
+
tsConfigWatcher.on("change", async () => {
|
|
124
|
+
timer = performance.now();
|
|
125
|
+
await build(null, files, false);
|
|
418
126
|
});
|
|
419
|
-
//@ts-ignore
|
|
420
|
-
node.value = css;
|
|
421
127
|
}
|
|
422
128
|
|
|
423
|
-
|
|
129
|
+
const watcher = watch(bundleConfig.src);
|
|
130
|
+
watcher.on("add", async (file) => {
|
|
131
|
+
file = String.raw`${file}`.replace(/\\/g, "/"); // glob and chokidar diff
|
|
132
|
+
if (files.includes(file) || INLINE_BUNDLE_FILE.test(file)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
424
135
|
|
|
425
|
-
|
|
426
|
-
fileText = await minify(fileText, {
|
|
427
|
-
collapseWhitespace: true,
|
|
428
|
-
removeComments: true,
|
|
429
|
-
});
|
|
136
|
+
await rebuild(file);
|
|
430
137
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
.generate({
|
|
439
|
-
base: buildDir,
|
|
440
|
-
html: fileText,
|
|
441
|
-
target: fileWithBase,
|
|
442
|
-
inline: !isCSP,
|
|
443
|
-
extract: true,
|
|
444
|
-
rebase: () => {},
|
|
445
|
-
})
|
|
446
|
-
.then(({ html }: any) => {
|
|
447
|
-
taskEmitter.emit("done");
|
|
448
|
-
|
|
449
|
-
if (serverSentEvents) {
|
|
450
|
-
serverSentEvents({
|
|
451
|
-
html: addHMRCode(html, buildFilename),
|
|
452
|
-
filename: buildFilename,
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
})
|
|
456
|
-
.catch((err: Error) => {
|
|
457
|
-
console.error(err);
|
|
458
|
-
});
|
|
459
|
-
} else {
|
|
460
|
-
fs.writeFileSync(buildFilename, fileText);
|
|
138
|
+
console.log(`⚡ added ${file} to the build`);
|
|
139
|
+
});
|
|
140
|
+
watcher.on("change", async (file) => {
|
|
141
|
+
if (INLINE_BUNDLE_FILE.test(file)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
file = String.raw`${file}`.replace(/\\/g, "/");
|
|
461
145
|
|
|
462
|
-
|
|
146
|
+
await rebuild(file);
|
|
463
147
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
148
|
+
console.log(`⚡ modified ${file} on the build`);
|
|
149
|
+
});
|
|
150
|
+
watcher.on("unlink", async (file) => {
|
|
151
|
+
if (INLINE_BUNDLE_FILE.test(file)) {
|
|
152
|
+
return;
|
|
469
153
|
}
|
|
470
|
-
|
|
471
|
-
}
|
|
154
|
+
file = String.raw`${file}`.replace(/\\/g, "/");
|
|
472
155
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
return { plugins: [cssnano], options: {}, file: "" };
|
|
479
|
-
}
|
|
480
|
-
}
|
|
156
|
+
inlineFiles.delete(file);
|
|
157
|
+
const buildFile = getBuildPath(file)
|
|
158
|
+
.replace(".ts", ".js")
|
|
159
|
+
.replace(".jsx", ".js");
|
|
160
|
+
await rm(buildFile);
|
|
481
161
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
`${BUILD_FOLDER}/`
|
|
486
|
-
);
|
|
487
|
-
const buildFilenameArr = buildFilename.split("/");
|
|
488
|
-
buildFilenameArr.pop();
|
|
489
|
-
const buildPathDir = buildFilenameArr.join("/");
|
|
490
|
-
return [buildFilename, buildPathDir];
|
|
491
|
-
}
|
|
162
|
+
const bfDir = buildFile.split("/").slice(0, -1).join("/");
|
|
163
|
+
const stats = await readdir(bfDir);
|
|
164
|
+
if (!stats.length) await rm(bfDir);
|
|
492
165
|
|
|
493
|
-
|
|
494
|
-
|
|
166
|
+
console.log(`⚡ deleted ${file} from the build`);
|
|
167
|
+
});
|
|
495
168
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
await minifyCSS(filename, buildFilename);
|
|
500
|
-
}
|
|
169
|
+
async function rebuild(file: string) {
|
|
170
|
+
// Rebuild all CSS because a change in any file might need to trigger PostCSS zu rebuild(e.g. Tailwind CSS)
|
|
171
|
+
await rebuildCSS(files.filter((file) => file.endsWith(".css")));
|
|
501
172
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
minifyHTML(filename, buildFilename);
|
|
508
|
-
} else if (
|
|
509
|
-
/\.(j|t)sx?$/.test(filename) ||
|
|
510
|
-
(filename.endsWith(".css") && isCritical)
|
|
511
|
-
) {
|
|
512
|
-
for (const htmlFilename of files) {
|
|
513
|
-
const [htmlBuildFilename] = getBuildNames(htmlFilename);
|
|
514
|
-
await minifyHTML(htmlFilename, htmlBuildFilename);
|
|
173
|
+
let html;
|
|
174
|
+
if (file.endsWith(".html")) {
|
|
175
|
+
// To refill the inlineFiles needed to build JS
|
|
176
|
+
for (const htmlFile of files.filter((file) => file.endsWith(".html"))) {
|
|
177
|
+
await writeInlineScripts(htmlFile);
|
|
515
178
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
js: fs.readFileSync(jsFile, { encoding: "utf-8" }),
|
|
523
|
-
filename: jsFile.split(`${BUILD_FOLDER}/`).pop()!,
|
|
524
|
-
});
|
|
525
|
-
} catch {}
|
|
179
|
+
await minifyCode();
|
|
180
|
+
for (const file of inlineFiles) {
|
|
181
|
+
if (INLINE_BUNDLE_FILE.test(file)) {
|
|
182
|
+
inlineFiles.delete(file);
|
|
183
|
+
await rm(file);
|
|
184
|
+
}
|
|
526
185
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
const getHMRCode = (
|
|
532
|
-
filename: string,
|
|
533
|
-
id: string
|
|
534
|
-
) => `import { render, html, setShouldSetReactivity, $$, setGlobalSchedule, setInsertDiffing } from "https://unpkg.com/hydro-js/dist/library.js";
|
|
535
|
-
|
|
536
|
-
if (!window.eventsource${id}) {
|
|
537
|
-
setGlobalSchedule(false);
|
|
538
|
-
setShouldSetReactivity(false);
|
|
539
|
-
|
|
540
|
-
window.eventsource${id} = new EventSource("/events");
|
|
541
|
-
window.eventsource${id}.addEventListener("message", ({ data }) => {
|
|
542
|
-
const dataObj = JSON.parse(data);
|
|
543
|
-
|
|
544
|
-
if ("html" in dataObj && "${filename}" === dataObj.filename) {
|
|
545
|
-
setInsertDiffing(true);
|
|
546
|
-
|
|
547
|
-
let newHTML;
|
|
548
|
-
let isBody;
|
|
549
|
-
|
|
550
|
-
if (dataObj.html.startsWith('<!DOCTYPE html>') || dataObj.html.startsWith('<html')) {
|
|
551
|
-
newHTML = html\`\${dataObj.html}\`;
|
|
186
|
+
html = await minifyHTML(file, getBuildPath(file));
|
|
187
|
+
} else if (/\.(jsx?|tsx?)$/.test(file)) {
|
|
188
|
+
inlineFiles.add(file);
|
|
189
|
+
await minifyCode();
|
|
552
190
|
} else {
|
|
553
|
-
|
|
554
|
-
|
|
191
|
+
const { stdout } = await execFilePromise("node", [handlerFile, file]);
|
|
192
|
+
if (String(stdout)) console.log("📋 Logging Handler: ", String(stdout));
|
|
555
193
|
}
|
|
556
194
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
// Render new Elements in old Elements, also remove rest old Elements and add add new elements after the last old one
|
|
562
|
-
hmrWheres.forEach((where, index) => {
|
|
563
|
-
if (index < hmrElems.length) {
|
|
564
|
-
render(hmrElems[index], where);
|
|
565
|
-
} else {
|
|
566
|
-
where.remove();
|
|
567
|
-
}
|
|
568
|
-
});
|
|
569
|
-
for (let rest = hmrWheres.length; rest < hmrElems.length; rest++) {
|
|
570
|
-
if (hmrWheres.length) {
|
|
571
|
-
const template = document.createElement('template');
|
|
572
|
-
hmrElems[hmrWheres.length - 1].after(template);
|
|
573
|
-
render(hmrElems[rest], template);
|
|
574
|
-
template.remove();
|
|
575
|
-
} else {
|
|
576
|
-
render(hmrElems[rest])
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
} else {
|
|
580
|
-
const oldElementCount = document.body.querySelectorAll('*').length;
|
|
581
|
-
render(newHTML, document.documentElement);
|
|
582
|
-
const newElementCount = document.body.querySelectorAll('*').length;
|
|
195
|
+
serverSentEvents?.({ file, html });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
583
199
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
200
|
+
async function minifyCSS(file: string, buildFile: string) {
|
|
201
|
+
try {
|
|
202
|
+
const fileText = await readFile(file, { encoding: "utf-8" });
|
|
203
|
+
const result = await CSSprocessor.process(fileText, {
|
|
204
|
+
...options,
|
|
205
|
+
from: file,
|
|
206
|
+
to: buildFile,
|
|
207
|
+
});
|
|
208
|
+
await writeFile(buildFile, result.css);
|
|
209
|
+
} catch (err) {
|
|
210
|
+
console.error(err);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function minifyCode(): Promise<unknown> {
|
|
215
|
+
try {
|
|
216
|
+
return await esbuild.build({
|
|
217
|
+
entryPoints: Array.from(inlineFiles),
|
|
218
|
+
charset: "utf8",
|
|
219
|
+
format: "esm",
|
|
220
|
+
incremental: isHMR,
|
|
221
|
+
sourcemap: isHMR,
|
|
222
|
+
splitting: true,
|
|
223
|
+
define: {
|
|
224
|
+
"process.env.NODE_ENV": `"${process.env.NODE_ENV}"`,
|
|
225
|
+
},
|
|
226
|
+
loader: { ".js": "jsx", ".ts": "tsx" },
|
|
227
|
+
bundle: true,
|
|
228
|
+
minify: true,
|
|
229
|
+
outdir: bundleConfig.build,
|
|
230
|
+
outbase: bundleConfig.src,
|
|
231
|
+
...bundleConfig.esbuild,
|
|
232
|
+
});
|
|
233
|
+
// Stop app from crashing.
|
|
234
|
+
} catch (err: any) {
|
|
235
|
+
if (!isHMR) {
|
|
236
|
+
console.error(err);
|
|
597
237
|
}
|
|
598
238
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
if (
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
239
|
+
let missingPkg = false;
|
|
240
|
+
if (err?.errors) {
|
|
241
|
+
for (const error of err.errors) {
|
|
242
|
+
if (error.text?.startsWith("Could not resolve")) {
|
|
243
|
+
missingPkg = true;
|
|
244
|
+
const packageNameRegex = /(?<=").*(?=")/;
|
|
245
|
+
const [pkgName] = error.text.match(packageNameRegex);
|
|
246
|
+
console.log(`📦 Package ${pkgName} was installed for you`);
|
|
247
|
+
|
|
248
|
+
await awaitSpawn(process.platform === "win32" ? "npm.cmd" : "npm", [
|
|
249
|
+
"install",
|
|
250
|
+
pkgName,
|
|
251
|
+
]);
|
|
606
252
|
}
|
|
607
253
|
}
|
|
608
|
-
};
|
|
609
|
-
});
|
|
610
254
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
255
|
+
if (missingPkg) {
|
|
256
|
+
missingPkg = false;
|
|
257
|
+
return minifyCode();
|
|
258
|
+
}
|
|
615
259
|
}
|
|
616
260
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const htmlFilesCache = new Map();
|
|
264
|
+
async function writeInlineScripts(file: string) {
|
|
265
|
+
let fileText = await readFile(file, { encoding: "utf-8" });
|
|
266
|
+
|
|
267
|
+
let DOM;
|
|
268
|
+
if (fileText.includes("<!DOCTYPE html>") || fileText.includes("<html")) {
|
|
269
|
+
DOM = parse(fileText);
|
|
270
|
+
} else {
|
|
271
|
+
DOM = parseFragment(fileText);
|
|
624
272
|
}
|
|
625
|
-
|
|
626
|
-
|
|
273
|
+
|
|
274
|
+
if (isHMR) {
|
|
275
|
+
fileText = addHMRCode(fileText, file, DOM);
|
|
627
276
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
277
|
+
htmlFilesCache.set(file, [fileText, DOM]);
|
|
278
|
+
|
|
279
|
+
const scripts = findElements(DOM, (e) => getTagName(e) === "script");
|
|
280
|
+
for (let index = 0; index < scripts.length; index++) {
|
|
281
|
+
const script = scripts[index];
|
|
282
|
+
const scriptTextNode = script.childNodes[0] as TextNode;
|
|
283
|
+
const isReferencedScript = script.attrs.find((a) => a.name === "src");
|
|
284
|
+
const scriptContent = scriptTextNode?.value;
|
|
285
|
+
if (!scriptContent || isReferencedScript) continue;
|
|
286
|
+
|
|
287
|
+
const jsFile = file.replace(".html", `-bundle-${index}.tsx`);
|
|
288
|
+
inlineFiles.add(jsFile);
|
|
289
|
+
await writeFile(jsFile, scriptContent);
|
|
634
290
|
}
|
|
635
|
-
}
|
|
291
|
+
}
|
|
636
292
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
}
|
|
293
|
+
async function minifyHTML(file: string, buildFile: string) {
|
|
294
|
+
let fileText, DOM;
|
|
640
295
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
296
|
+
if (htmlFilesCache.has(file)) {
|
|
297
|
+
const cache = htmlFilesCache.get(file);
|
|
298
|
+
fileText = cache[0];
|
|
299
|
+
DOM = cache[1];
|
|
300
|
+
} else {
|
|
301
|
+
fileText = await readFile(file, { encoding: "utf-8" });
|
|
646
302
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
getHMRCode(filename, htmlIdMap.get(filename))
|
|
650
|
-
);
|
|
651
|
-
let ast;
|
|
652
|
-
if (html.includes("<!DOCTYPE html>") || html.includes("<html")) {
|
|
653
|
-
ast = parse(html);
|
|
654
|
-
const headNode = findElement(ast, (e) => getTagName(e) === "head");
|
|
655
|
-
appendChild(headNode as ParentNode, script);
|
|
303
|
+
if (fileText.includes("<!DOCTYPE html>") || fileText.includes("<html")) {
|
|
304
|
+
DOM = parse(fileText);
|
|
656
305
|
} else {
|
|
657
|
-
|
|
658
|
-
appendChild(ast, script);
|
|
659
|
-
ast.childNodes.forEach((node) =>
|
|
660
|
-
//@ts-ignore
|
|
661
|
-
node.attrs?.push({ name: "data-hmr", value: htmlIdMap.get(filename) })
|
|
662
|
-
);
|
|
306
|
+
DOM = parseFragment(fileText);
|
|
663
307
|
}
|
|
308
|
+
}
|
|
664
309
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
310
|
+
// Minify Code
|
|
311
|
+
const scripts = findElements(DOM, (e) => getTagName(e) === "script");
|
|
312
|
+
for (let index = 0; index < scripts.length; index++) {
|
|
313
|
+
const script = scripts[index];
|
|
314
|
+
const scriptTextNode = script.childNodes[0] as TextNode;
|
|
315
|
+
const isReferencedScript = script.attrs.find((a) => a.name === "src");
|
|
316
|
+
if (!scriptTextNode?.value || isReferencedScript) continue;
|
|
317
|
+
|
|
318
|
+
// Use bundled file
|
|
319
|
+
const buildInlineScript = buildFile.replace(".html", `-bundle-${index}.js`);
|
|
673
320
|
|
|
674
|
-
|
|
321
|
+
const scriptContent = await readFile(buildInlineScript, {
|
|
322
|
+
encoding: "utf-8",
|
|
323
|
+
});
|
|
324
|
+
await rm(buildInlineScript);
|
|
325
|
+
scriptTextNode.value = scriptContent.replace(
|
|
326
|
+
TEMPLATE_LITERAL_MINIFIER,
|
|
327
|
+
" "
|
|
328
|
+
);
|
|
675
329
|
}
|
|
676
330
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
331
|
+
// Minify Inline Style
|
|
332
|
+
const styles = findElements(DOM, (e) => getTagName(e) === "style");
|
|
333
|
+
for (const style of styles) {
|
|
334
|
+
const node = style.childNodes[0] as TextNode;
|
|
335
|
+
const styleContent = node?.value;
|
|
336
|
+
if (!styleContent) continue;
|
|
682
337
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
}
|
|
687
|
-
//@ts-ignore
|
|
688
|
-
fastify.get(newFilename, HMRHandler);
|
|
338
|
+
const { css } = await CSSprocessor.process(styleContent, {
|
|
339
|
+
...options,
|
|
340
|
+
from: undefined,
|
|
689
341
|
});
|
|
342
|
+
node.value = css;
|
|
690
343
|
}
|
|
691
344
|
|
|
692
|
-
|
|
693
|
-
let filename = request.url;
|
|
694
|
-
if (filename.endsWith("/")) {
|
|
695
|
-
filename += "index.html";
|
|
696
|
-
}
|
|
697
|
-
filename = BUILD_FOLDER + filename;
|
|
698
|
-
const file = fs.readFileSync(filename, {
|
|
699
|
-
encoding: "utf-8",
|
|
700
|
-
});
|
|
345
|
+
fileText = serialize(DOM);
|
|
701
346
|
|
|
702
|
-
|
|
703
|
-
|
|
347
|
+
// Minify HTML
|
|
348
|
+
try {
|
|
349
|
+
fileText = await minify(fileText, {
|
|
350
|
+
collapseWhitespace: true,
|
|
351
|
+
removeComments: true,
|
|
352
|
+
...bundleConfig["html-minifier-terser"],
|
|
353
|
+
});
|
|
354
|
+
} catch (e) {
|
|
355
|
+
console.error(e);
|
|
704
356
|
}
|
|
705
357
|
|
|
706
|
-
|
|
707
|
-
|
|
358
|
+
if (!isCritical) {
|
|
359
|
+
await writeFile(buildFile, fileText);
|
|
360
|
+
return fileText;
|
|
361
|
+
} else {
|
|
362
|
+
const buildFileArr = buildFile.split("/");
|
|
363
|
+
const fileWithBase = buildFileArr.pop();
|
|
364
|
+
const buildDir = buildFileArr.join("/");
|
|
365
|
+
|
|
366
|
+
// critical is generating the files on the fs
|
|
367
|
+
try {
|
|
368
|
+
const { html } = await critical.generate({
|
|
369
|
+
base: buildDir,
|
|
370
|
+
html: fileText,
|
|
371
|
+
target: fileWithBase,
|
|
372
|
+
inline: !isSecure,
|
|
373
|
+
extract: true,
|
|
374
|
+
rebase: () => {},
|
|
375
|
+
...bundleConfig.critical,
|
|
376
|
+
});
|
|
377
|
+
return html;
|
|
378
|
+
} catch (err) {
|
|
708
379
|
console.error(err);
|
|
709
|
-
process.exit(1);
|
|
710
380
|
}
|
|
711
381
|
}
|
|
382
|
+
}
|
|
712
383
|
|
|
713
|
-
|
|
714
|
-
|
|
384
|
+
async function rebuildCSS(files: string[], config?: string) {
|
|
385
|
+
const newConfig = await getPostCSSConfig();
|
|
386
|
+
plugins = newConfig.plugins;
|
|
387
|
+
options = newConfig.options;
|
|
388
|
+
CSSprocessor = postcss(plugins as AcceptedPlugin[]);
|
|
389
|
+
for (const file of files) {
|
|
390
|
+
await minifyCSS(file, getBuildPath(file));
|
|
715
391
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
fastify = Fastify(
|
|
719
|
-
isSecure
|
|
720
|
-
? ({
|
|
721
|
-
http2: true,
|
|
722
|
-
https: {
|
|
723
|
-
key: fs.readFileSync(path.join(process.cwd(), "localhost-key.pem")),
|
|
724
|
-
cert: fs.readFileSync(path.join(process.cwd(), "localhost.pem")),
|
|
725
|
-
},
|
|
726
|
-
} as FastifyServerOptions)
|
|
727
|
-
: void 0
|
|
728
|
-
);
|
|
729
|
-
fastify.register(fastifyStatic, {
|
|
730
|
-
root: path.join(process.cwd(), BUILD_FOLDER),
|
|
731
|
-
});
|
|
392
|
+
|
|
393
|
+
if (config) console.log(`⚡ modified ${config}.config`);
|
|
732
394
|
}
|