@webqit/webflo 0.11.61-0 → 1.0.0
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/.gitignore +7 -7
- package/LICENSE +20 -20
- package/README.md +2079 -2074
- package/docker/Dockerfile +42 -42
- package/docker/README.md +91 -91
- package/docker/package.json +2 -2
- package/package.json +80 -81
- package/src/{Context.js → AbstractContext.js} +71 -79
- package/src/config-pi/deployment/Env.js +68 -68
- package/src/config-pi/deployment/Layout.js +63 -63
- package/src/config-pi/deployment/Origins.js +139 -139
- package/src/config-pi/deployment/Proxy.js +74 -74
- package/src/config-pi/deployment/index.js +17 -17
- package/src/config-pi/index.js +15 -15
- package/src/config-pi/runtime/Client.js +116 -98
- package/src/config-pi/runtime/Server.js +125 -125
- package/src/config-pi/runtime/client/Worker.js +109 -134
- package/src/config-pi/runtime/client/index.js +11 -11
- package/src/config-pi/runtime/index.js +17 -17
- package/src/config-pi/runtime/server/Headers.js +74 -74
- package/src/config-pi/runtime/server/Redirects.js +69 -69
- package/src/config-pi/runtime/server/index.js +13 -13
- package/src/config-pi/static/Manifest.js +319 -319
- package/src/config-pi/static/Ssg.js +49 -49
- package/src/config-pi/static/index.js +13 -13
- package/src/deployment-pi/index.js +10 -10
- package/src/deployment-pi/origins/index.js +216 -216
- package/src/index.js +11 -19
- package/src/runtime-pi/HttpEvent.js +126 -106
- package/src/runtime-pi/HttpUser.js +126 -0
- package/src/runtime-pi/MessagingOverBroadcast.js +9 -0
- package/src/runtime-pi/MessagingOverChannel.js +85 -0
- package/src/runtime-pi/MessagingOverSocket.js +106 -0
- package/src/runtime-pi/MultiportMessagingAPI.js +81 -0
- package/src/runtime-pi/WebfloCookieStorage.js +27 -0
- package/src/runtime-pi/WebfloEventTarget.js +39 -0
- package/src/runtime-pi/WebfloMessageEvent.js +58 -0
- package/src/runtime-pi/WebfloMessagingAPI.js +69 -0
- package/src/runtime-pi/{Router.js → WebfloRouter.js} +99 -130
- package/src/runtime-pi/WebfloRuntime.js +52 -0
- package/src/runtime-pi/WebfloStorage.js +109 -0
- package/src/runtime-pi/client/ClientMessaging.js +5 -0
- package/src/runtime-pi/client/Context.js +3 -7
- package/src/runtime-pi/client/CookieStorage.js +17 -0
- package/src/runtime-pi/client/Router.js +38 -48
- package/src/runtime-pi/client/SessionStorage.js +33 -0
- package/src/runtime-pi/client/Url.js +156 -205
- package/src/runtime-pi/client/WebfloClient.js +544 -0
- package/src/runtime-pi/client/WebfloRootClient1.js +179 -0
- package/src/runtime-pi/client/WebfloRootClient2.js +109 -0
- package/src/runtime-pi/client/WebfloSubClient.js +165 -0
- package/src/runtime-pi/client/Workport.js +118 -178
- package/src/runtime-pi/client/generate.js +480 -471
- package/src/runtime-pi/client/index.js +16 -21
- package/src/runtime-pi/client/worker/ClientMessaging.js +5 -0
- package/src/runtime-pi/client/worker/Context.js +3 -7
- package/src/runtime-pi/client/worker/CookieStorage.js +17 -0
- package/src/runtime-pi/client/worker/SessionStorage.js +13 -0
- package/src/runtime-pi/client/worker/WebfloWorker.js +294 -0
- package/src/runtime-pi/client/worker/Workport.js +17 -85
- package/src/runtime-pi/client/worker/index.js +10 -21
- package/src/runtime-pi/index.js +6 -13
- package/src/runtime-pi/server/ClientMessaging.js +18 -0
- package/src/runtime-pi/server/ClientMessagingRegistry.js +57 -0
- package/src/runtime-pi/server/Context.js +11 -15
- package/src/runtime-pi/server/CookieStorage.js +17 -0
- package/src/runtime-pi/server/Router.js +93 -159
- package/src/runtime-pi/server/SessionStorage.js +53 -0
- package/src/runtime-pi/server/WebfloServer.js +755 -0
- package/src/runtime-pi/server/index.js +10 -21
- package/src/runtime-pi/util-http.js +322 -86
- package/src/runtime-pi/util-url.js +146 -146
- package/src/runtime-pi/xURL.js +108 -105
- package/src/runtime-pi/xfetch.js +22 -22
- package/src/services-pi/cert/http-auth-hook.js +22 -22
- package/src/services-pi/cert/http-cleanup-hook.js +22 -22
- package/src/services-pi/cert/index.js +79 -79
- package/src/services-pi/index.js +8 -8
- package/src/static-pi/index.js +10 -10
- package/src/webflo.js +30 -30
- package/test/index.test.js +26 -26
- package/test/site/package.json +9 -9
- package/test/site/public/bundle.html +5 -5
- package/test/site/public/bundle.html.json +3 -3
- package/test/site/public/bundle.js +2 -2
- package/test/site/public/bundle.webflo.js +15 -15
- package/test/site/public/index.html +29 -29
- package/test/site/public/index1.html +34 -34
- package/test/site/public/page-2/bundle.html +4 -4
- package/test/site/public/page-2/bundle.js +2 -2
- package/test/site/public/page-2/index.html +45 -45
- package/test/site/public/page-2/main.html +2 -2
- package/test/site/public/page-4/subpage/bundle.js +2 -2
- package/test/site/public/page-4/subpage/index.html +30 -30
- package/test/site/public/sparoots.json +4 -4
- package/test/site/public/worker.js +3 -3
- package/test/site/server/index.js +15 -15
- package/src/runtime-pi/Application.js +0 -29
- package/src/runtime-pi/Cookies.js +0 -82
- package/src/runtime-pi/Runtime.js +0 -21
- package/src/runtime-pi/client/Application.js +0 -100
- package/src/runtime-pi/client/Runtime.js +0 -332
- package/src/runtime-pi/client/createStorage.js +0 -57
- package/src/runtime-pi/client/oohtml/full.js +0 -7
- package/src/runtime-pi/client/oohtml/namespacing.js +0 -7
- package/src/runtime-pi/client/oohtml/scripting.js +0 -8
- package/src/runtime-pi/client/oohtml/templating.js +0 -8
- package/src/runtime-pi/client/worker/Application.js +0 -44
- package/src/runtime-pi/client/worker/Runtime.js +0 -269
- package/src/runtime-pi/server/Application.js +0 -116
- package/src/runtime-pi/server/Runtime.js +0 -557
- package/src/runtime-pi/xFormData.js +0 -24
- package/src/runtime-pi/xHeaders.js +0 -146
- package/src/runtime-pi/xRequest.js +0 -46
- package/src/runtime-pi/xRequestHeaders.js +0 -109
- package/src/runtime-pi/xResponse.js +0 -33
- package/src/runtime-pi/xResponseHeaders.js +0 -117
- package/src/runtime-pi/xxHttpMessage.js +0 -102
|
@@ -1,472 +1,481 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* imports
|
|
4
|
-
*/
|
|
5
|
-
import Fs from 'fs';
|
|
6
|
-
import Url from 'url';
|
|
7
|
-
import Path from 'path';
|
|
8
|
-
import Jsdom from 'jsdom';
|
|
9
|
-
import EsBuild from 'esbuild';
|
|
10
|
-
import { _afterLast, _beforeLast } from '@webqit/util/str/index.js';
|
|
11
|
-
import { _isObject, _isArray } from '@webqit/util/js/index.js';
|
|
12
|
-
import { jsFile } from '@webqit/backpack/src/dotfile/index.js';
|
|
13
|
-
import { gzipSync, brotliCompressSync } from 'zlib';
|
|
14
|
-
import { pattern } from '../util-url.js';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @generate
|
|
18
|
-
*/
|
|
19
|
-
export async function generate() {
|
|
20
|
-
const cx = this || {};
|
|
21
|
-
// -----------
|
|
22
|
-
if (!cx.config.runtime?.Client) {
|
|
23
|
-
throw new Error(`The Client configurator "config.runtime.Client" is required in context.`);
|
|
24
|
-
}
|
|
25
|
-
if (!cx.config.deployment?.Layout) {
|
|
26
|
-
throw new Error(`The Client configurator "config.deployment.Layout" is required in context.`);
|
|
27
|
-
}
|
|
28
|
-
const clientConfig = await (new cx.config.runtime.Client(cx)).read();
|
|
29
|
-
if (clientConfig.
|
|
30
|
-
throw new Error(`The Service Worker configurator "config.runtime.client.Worker" is required in context.`);
|
|
31
|
-
}
|
|
32
|
-
const workerConfig = await (new cx.config.runtime.client.Worker(cx)).read();
|
|
33
|
-
// -----------
|
|
34
|
-
if (!cx.config.deployment?.Layout) {
|
|
35
|
-
throw new Error(`The Layout configurator "config.deployment.Layout" is required in context.`);
|
|
36
|
-
}
|
|
37
|
-
const layoutConfig = await (new cx.config.deployment.Layout(cx)).read();
|
|
38
|
-
// -----------
|
|
39
|
-
const dirPublic = Path.resolve(cx.CWD || '', layoutConfig.PUBLIC_DIR);
|
|
40
|
-
const dirClient = Path.resolve(cx.CWD || '', layoutConfig.CLIENT_DIR);
|
|
41
|
-
const dirWorker = Path.resolve(cx.CWD || '', layoutConfig.WORKER_DIR);
|
|
42
|
-
const dirSelf = Path.dirname(Url.fileURLToPath(import.meta.url)).replace(/\\/g, '/');
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
// -----------
|
|
61
|
-
//
|
|
62
|
-
const
|
|
63
|
-
let
|
|
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
|
-
return
|
|
175
|
-
}, []);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
let
|
|
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
|
-
gen.code.push(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
//
|
|
387
|
-
let
|
|
388
|
-
if (
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* imports
|
|
4
|
+
*/
|
|
5
|
+
import Fs from 'fs';
|
|
6
|
+
import Url from 'url';
|
|
7
|
+
import Path from 'path';
|
|
8
|
+
import Jsdom from 'jsdom';
|
|
9
|
+
import EsBuild from 'esbuild';
|
|
10
|
+
import { _afterLast, _beforeLast } from '@webqit/util/str/index.js';
|
|
11
|
+
import { _isObject, _isArray } from '@webqit/util/js/index.js';
|
|
12
|
+
import { jsFile } from '@webqit/backpack/src/dotfile/index.js';
|
|
13
|
+
import { gzipSync, brotliCompressSync } from 'zlib';
|
|
14
|
+
import { pattern } from '../util-url.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @generate
|
|
18
|
+
*/
|
|
19
|
+
export async function generate() {
|
|
20
|
+
const cx = this || {};
|
|
21
|
+
// -----------
|
|
22
|
+
if (!cx.config.runtime?.Client) {
|
|
23
|
+
throw new Error(`The Client configurator "config.runtime.Client" is required in context.`);
|
|
24
|
+
}
|
|
25
|
+
if (!cx.config.deployment?.Layout) {
|
|
26
|
+
throw new Error(`The Client configurator "config.deployment.Layout" is required in context.`);
|
|
27
|
+
}
|
|
28
|
+
const clientConfig = await (new cx.config.runtime.Client(cx)).read();
|
|
29
|
+
if (clientConfig.service_worker?.filename && !cx.config.runtime.client?.Worker) {
|
|
30
|
+
throw new Error(`The Service Worker configurator "config.runtime.client.Worker" is required in context.`);
|
|
31
|
+
}
|
|
32
|
+
const workerConfig = await (new cx.config.runtime.client.Worker(cx)).read();
|
|
33
|
+
// -----------
|
|
34
|
+
if (!cx.config.deployment?.Layout) {
|
|
35
|
+
throw new Error(`The Layout configurator "config.deployment.Layout" is required in context.`);
|
|
36
|
+
}
|
|
37
|
+
const layoutConfig = await (new cx.config.deployment.Layout(cx)).read();
|
|
38
|
+
// -----------
|
|
39
|
+
const dirPublic = Path.resolve(cx.CWD || '', layoutConfig.PUBLIC_DIR);
|
|
40
|
+
const dirClient = Path.resolve(cx.CWD || '', layoutConfig.CLIENT_DIR);
|
|
41
|
+
const dirWorker = Path.resolve(cx.CWD || '', layoutConfig.WORKER_DIR);
|
|
42
|
+
const dirSelf = Path.dirname(Url.fileURLToPath(import.meta.url)).replace(/\\/g, '/');
|
|
43
|
+
if (clientConfig.bundle_public_env || workerConfig.bundle_public_env) {
|
|
44
|
+
if (!cx.config.deployment?.Env) {
|
|
45
|
+
throw new Error(`The Layout configurator "config.deployment.Env" is required in context to bundle public env.`);
|
|
46
|
+
}
|
|
47
|
+
const envConfig = await (new cx.config.deployment.Env(cx)).read();
|
|
48
|
+
for (const key in envConfig.entries) {
|
|
49
|
+
if (!key.includes('PUBLIC_') && !key.includes('_PUBLIC')) continue;
|
|
50
|
+
if (clientConfig.bundle_public_env) {
|
|
51
|
+
if (!clientConfig.env) { clientConfig.env = {}; }
|
|
52
|
+
clientConfig.env[key] = envConfig.entries[key];
|
|
53
|
+
}
|
|
54
|
+
if (workerConfig.bundle_public_env) {
|
|
55
|
+
if (!workerConfig.env) { workerConfig.env = {}; }
|
|
56
|
+
workerConfig.env[key] = envConfig.entries[key];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// -----------
|
|
61
|
+
// Scan Subdocuments
|
|
62
|
+
const scanSubroots = (sparoot, rootFileName) => {
|
|
63
|
+
let dir = Path.join(dirPublic, sparoot), passes = 0;
|
|
64
|
+
return [ Fs.readdirSync(dir).reduce((sparoots, f) => {
|
|
65
|
+
let resource = Path.join(dir, f);
|
|
66
|
+
if (Fs.statSync(resource).isDirectory()) {
|
|
67
|
+
let subsparoot = Path.join(sparoot, f);
|
|
68
|
+
if (Fs.existsSync(Path.join(resource, rootFileName))) {
|
|
69
|
+
return sparoots.concat(subsparoot);
|
|
70
|
+
}
|
|
71
|
+
passes ++;
|
|
72
|
+
return sparoots.concat(scanSubroots(subsparoot, rootFileName)[ 0 ]);
|
|
73
|
+
}
|
|
74
|
+
return sparoots;
|
|
75
|
+
}, []), passes ];
|
|
76
|
+
};
|
|
77
|
+
// -----------
|
|
78
|
+
// Generate client build
|
|
79
|
+
const generateClient = async function(sparoot, spaGraphCallback = null) {
|
|
80
|
+
let [ subsparoots, targets ] = (sparoot && scanSubroots(sparoot, 'index.html')) || [ [], false ];
|
|
81
|
+
if (!sparoot) sparoot = '/';
|
|
82
|
+
let spaRouting = { root: sparoot, subroots: subsparoots, targets };
|
|
83
|
+
let codeSplitting = !!(sparoot !== '/' || subsparoots.length);
|
|
84
|
+
let outfileMain = Path.join(sparoot, clientConfig.bundle_filename),
|
|
85
|
+
outfileWebflo = _beforeLast(clientConfig.bundle_filename, '.js') + '.webflo.js';
|
|
86
|
+
let gen = { imports: {}, code: [], };
|
|
87
|
+
// ------------------
|
|
88
|
+
const initWebflo = gen => {
|
|
89
|
+
gen.imports[`${dirSelf}/index.js`] = `* as Webflo`;
|
|
90
|
+
gen.code.push(``);
|
|
91
|
+
gen.code.push(`if (!self.webqit) {self.webqit = {};}`);
|
|
92
|
+
gen.code.push(`webqit.Webflo = Webflo`);
|
|
93
|
+
return gen;
|
|
94
|
+
};
|
|
95
|
+
// ------------------
|
|
96
|
+
if (!codeSplitting) {
|
|
97
|
+
initWebflo(gen);
|
|
98
|
+
} else if (sparoot === '/') {
|
|
99
|
+
if (cx.logger) {
|
|
100
|
+
cx.logger.log(cx.logger.style.keyword(`-----------------`));
|
|
101
|
+
cx.logger.log(`Base Build`);
|
|
102
|
+
cx.logger.log(cx.logger.style.keyword(`-----------------`));
|
|
103
|
+
}
|
|
104
|
+
let gen1 = initWebflo({ imports: {}, code: [], });
|
|
105
|
+
await bundle.call(cx, gen1, Path.join(dirPublic, outfileWebflo), true/* asModule */);
|
|
106
|
+
}
|
|
107
|
+
// ------------------
|
|
108
|
+
if (cx.logger) {
|
|
109
|
+
cx.logger.log(cx.logger.style.keyword(`-----------------`));
|
|
110
|
+
cx.logger.log(`Client Build ` + cx.logger.style.comment(`(sparoot:${sparoot}; is-split:${codeSplitting})`));
|
|
111
|
+
cx.logger.log(cx.logger.style.keyword(`-----------------`));
|
|
112
|
+
}
|
|
113
|
+
gen.code.push(`const { start } = webqit.Webflo`);
|
|
114
|
+
// ------------------
|
|
115
|
+
// Bundle
|
|
116
|
+
declareStart.call(cx, gen, dirClient, dirPublic, clientConfig, spaRouting);
|
|
117
|
+
await bundle.call(cx, gen, Path.join(dirPublic, outfileMain), true/* asModule */);
|
|
118
|
+
// ------------------
|
|
119
|
+
// Embed/unembed
|
|
120
|
+
let targetDocumentFile = Path.join(dirPublic, sparoot, 'index.html'),
|
|
121
|
+
outfileWebfloPublic = Path.join(clientConfig.public_base_url, outfileWebflo),
|
|
122
|
+
outfileMainPublic = Path.join(clientConfig.public_base_url, outfileMain),
|
|
123
|
+
embedList = [],
|
|
124
|
+
unembedList = [];
|
|
125
|
+
if (cx.flags['auto-embed']) {
|
|
126
|
+
if (codeSplitting) {
|
|
127
|
+
embedList.push(outfileWebfloPublic);
|
|
128
|
+
} else {
|
|
129
|
+
unembedList.push(outfileWebfloPublic);
|
|
130
|
+
}
|
|
131
|
+
embedList.push(outfileMainPublic);
|
|
132
|
+
} else {
|
|
133
|
+
unembedList.push(outfileWebfloPublic, outfileMainPublic);
|
|
134
|
+
}
|
|
135
|
+
handleEmbeds(targetDocumentFile, embedList, unembedList);
|
|
136
|
+
// ------------------
|
|
137
|
+
// Recurse
|
|
138
|
+
spaGraphCallback && spaGraphCallback(sparoot, subsparoots);
|
|
139
|
+
if (cx.flags.recursive) {
|
|
140
|
+
while (subsparoots.length) {
|
|
141
|
+
await generateClient(subsparoots.shift(), spaGraphCallback);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
// -----------
|
|
146
|
+
// Generate worker build
|
|
147
|
+
const generateWorker = async function(workerroot, workerGraphCallback = null) {
|
|
148
|
+
let [ subworkerroots, targets ] = workerroot && scanSubroots(workerroot, 'workerroot') || [ [], false ];
|
|
149
|
+
if (!workerroot) workerroot = '/';
|
|
150
|
+
let workerRouting = { root: workerroot, subroots: subworkerroots, targets };
|
|
151
|
+
let gen = { imports: {}, code: [], };
|
|
152
|
+
if (cx.logger) {
|
|
153
|
+
cx.logger.log(cx.logger.style.comment(`-----------------`));
|
|
154
|
+
cx.logger.log(`Worker Build - workerroot:${workerroot}`);
|
|
155
|
+
cx.logger.log(cx.logger.style.comment(`-----------------`));
|
|
156
|
+
}
|
|
157
|
+
// ------------------
|
|
158
|
+
// >> Modules import
|
|
159
|
+
gen.imports[`${dirSelf}/worker/index.js`] = `{ start }`;
|
|
160
|
+
gen.code.push(``);
|
|
161
|
+
gen.code.push(`self.webqit = {}`);
|
|
162
|
+
gen.code.push(``);
|
|
163
|
+
// ------------------
|
|
164
|
+
// Bundle
|
|
165
|
+
for (const strategy of [ 'cache_first_urls', 'cache_only_urls' ]) {
|
|
166
|
+
if (workerConfig[strategy].length) {
|
|
167
|
+
// Separate URLs from patterns
|
|
168
|
+
let [ urls, patterns ] = workerConfig[strategy].reduce(([ urls, patterns ], url) => {
|
|
169
|
+
let patternInstance = pattern(url, 'http://localhost'),
|
|
170
|
+
isPattern = patternInstance.isPattern();
|
|
171
|
+
if (isPattern && (patternInstance.pattern.pattern.hostname !== 'localhost' || patternInstance.pattern.pattern.port)) {
|
|
172
|
+
throw new Error(`Pattern URLs must have no origin part. Recieved "${url}".`);
|
|
173
|
+
}
|
|
174
|
+
return isPattern ? [ urls, patterns.concat(patternInstance) ] : [ urls.concat(url), patterns ];
|
|
175
|
+
}, [ [], [] ]);
|
|
176
|
+
// Resolve patterns
|
|
177
|
+
if (patterns.length) {
|
|
178
|
+
// List all files
|
|
179
|
+
let scan = dir => Fs.readdirSync(dir).reduce((result, f) => {
|
|
180
|
+
let resource = Path.join(dir, f);
|
|
181
|
+
if (f.startsWith('.')) return result;
|
|
182
|
+
return result.concat(Fs.statSync(resource).isDirectory() ? scan(resource) : '/' + Path.relative(dirPublic, resource));
|
|
183
|
+
}, []);
|
|
184
|
+
let files = scan(dirPublic);
|
|
185
|
+
// Resolve patterns from files
|
|
186
|
+
workerConfig[strategy] = patterns.reduce((all, pattern) => {
|
|
187
|
+
let matchedFiles = files.filter(file => pattern.test(file, 'http://localhost'));
|
|
188
|
+
if (matchedFiles.length) return all.concat(matchedFiles);
|
|
189
|
+
throw new Error(`The pattern "${pattern.pattern.pattern.pathname}" didn't match any files.`);
|
|
190
|
+
}, urls);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
declareStart.call(cx, gen, dirWorker, dirPublic, workerConfig, workerRouting);
|
|
195
|
+
await bundle.call(cx, gen, Path.join(dirPublic, workerroot, clientConfig.service_worker.filename));
|
|
196
|
+
// ------------------
|
|
197
|
+
// Recurse
|
|
198
|
+
workerGraphCallback && workerGraphCallback(workerroot, subworkerroots);
|
|
199
|
+
if (cx.flags.recursive) {
|
|
200
|
+
while (subworkerroots.length) {
|
|
201
|
+
await generateWorker(subworkerroots.shift());
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
// -----------
|
|
206
|
+
// Generate now...
|
|
207
|
+
let sparootsFile = Path.join(dirPublic, 'sparoots.json');
|
|
208
|
+
if (clientConfig.spa_routing !== false) {
|
|
209
|
+
const sparoots = [];
|
|
210
|
+
await generateClient('/', root => sparoots.push(root));
|
|
211
|
+
Fs.writeFileSync(sparootsFile, JSON.stringify(sparoots, null, 4));
|
|
212
|
+
} else {
|
|
213
|
+
await generateClient();
|
|
214
|
+
Fs.existsSync(sparootsFile) && Fs.unlinkSync(sparootsFile);
|
|
215
|
+
}
|
|
216
|
+
if (clientConfig.service_worker.filename) {
|
|
217
|
+
await generateWorker('/');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Compile routes.
|
|
223
|
+
*
|
|
224
|
+
* @param object gen
|
|
225
|
+
* @param string routesDir
|
|
226
|
+
* @param string targetPublic
|
|
227
|
+
* @param object paramsObj
|
|
228
|
+
* @param object routing
|
|
229
|
+
*
|
|
230
|
+
* @return Object
|
|
231
|
+
*/
|
|
232
|
+
function declareStart(gen, routesDir, targetDir, paramsObj, routing) {
|
|
233
|
+
const cx = this || {};
|
|
234
|
+
// ------------------
|
|
235
|
+
// >> Routes mapping
|
|
236
|
+
gen.code.push(`// >> Routes`);
|
|
237
|
+
declareRoutesObj.call(cx, gen, routesDir, targetDir, 'layout', routing);
|
|
238
|
+
gen.code.push(``);
|
|
239
|
+
// ------------------
|
|
240
|
+
// >> Params
|
|
241
|
+
gen.code.push(`// >> Params`);
|
|
242
|
+
declareParamsObj.call(cx, gen, { ...paramsObj, routing }, 'params');
|
|
243
|
+
gen.code.push(``);
|
|
244
|
+
// ------------------
|
|
245
|
+
// >> Startup
|
|
246
|
+
gen.code.push(`// >> Startup`);
|
|
247
|
+
gen.code.push(`webqit.app = await start.call({ layout, params })`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Compile routes.
|
|
252
|
+
*
|
|
253
|
+
* @param object gen
|
|
254
|
+
* @param string routesDir
|
|
255
|
+
* @param string targetDir
|
|
256
|
+
* @param string varName
|
|
257
|
+
* @param object routing
|
|
258
|
+
*
|
|
259
|
+
* @return void
|
|
260
|
+
*/
|
|
261
|
+
function declareRoutesObj(gen, routesDir, targetDir, varName, routing) {
|
|
262
|
+
const cx = this || {};
|
|
263
|
+
let _routesDir = Path.join(routesDir, routing.root),
|
|
264
|
+
_targetDir = Path.join(targetDir, routing.root);
|
|
265
|
+
cx.logger && cx.logger.log(cx.logger.style.keyword(`> `) + `Declaring routes...`);
|
|
266
|
+
// ----------------
|
|
267
|
+
// Directory walker
|
|
268
|
+
const walk = (dir, callback) => {
|
|
269
|
+
Fs.readdirSync(dir).forEach(f => {
|
|
270
|
+
let resource = Path.join(dir, f);
|
|
271
|
+
let _namespace = '/' + Path.relative(routesDir, resource).replace(/\\/g, '/');
|
|
272
|
+
let namespace = _beforeLast(_namespace, '/index.js') || '/';
|
|
273
|
+
if (Fs.statSync(resource).isDirectory()) {
|
|
274
|
+
if (routing.subroots.includes(namespace)) return;
|
|
275
|
+
walk(resource, callback);
|
|
276
|
+
} else {
|
|
277
|
+
let relativePath = Path.relative(_targetDir, resource).replace(/\\/g, '/');
|
|
278
|
+
callback(resource, namespace, relativePath);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
};
|
|
282
|
+
// ----------------
|
|
283
|
+
// >> Routes mapping
|
|
284
|
+
gen.code.push(`const ${varName} = {};`);
|
|
285
|
+
let indexCount = 0;
|
|
286
|
+
if (Fs.existsSync(_routesDir)) {
|
|
287
|
+
walk(_routesDir, (file, namespace, relativePath) => {
|
|
288
|
+
if (relativePath.endsWith('/index.js')) {
|
|
289
|
+
// Import code
|
|
290
|
+
let routeName = 'index' + (++ indexCount);
|
|
291
|
+
// IMPORTANT: we;re taking a step back here so that the parent-child relationship for
|
|
292
|
+
// the directories be involved
|
|
293
|
+
gen.imports[relativePath] = '* as ' + routeName;
|
|
294
|
+
// Definition code
|
|
295
|
+
gen.code.push(`${varName}['${namespace}'] = ${routeName};`);
|
|
296
|
+
// Show
|
|
297
|
+
cx.logger && cx.logger.log(cx.logger.style.comment(` [${namespace}]: `) + cx.logger.style.url(relativePath) + cx.logger.style.comment(` (${Fs.statSync(file).size / 1024} KB)`));
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
if (!indexCount) {
|
|
302
|
+
cx.logger && cx.logger.log(cx.logger.style.comment(` (none)`));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Compile params.
|
|
308
|
+
*
|
|
309
|
+
* @param object gen
|
|
310
|
+
* @param object paramsObj
|
|
311
|
+
* @param string varName
|
|
312
|
+
*
|
|
313
|
+
* @return void
|
|
314
|
+
*/
|
|
315
|
+
function declareParamsObj(gen, paramsObj, varName = null, indentation = 0) {
|
|
316
|
+
const cx = this || {};
|
|
317
|
+
// ----------------
|
|
318
|
+
// Params compilation
|
|
319
|
+
if (varName) gen.code.push(`const ${varName} = {`);
|
|
320
|
+
_isArray(paramsObj)
|
|
321
|
+
Object.keys(paramsObj).forEach(name => {
|
|
322
|
+
let _name = ` ${' '.repeat(indentation)}${(_isArray(paramsObj) ? '' : (name.includes(' ') ? `'${name}'` : name) + ': ')}`;
|
|
323
|
+
if ([ 'boolean', 'number' ].includes(typeof paramsObj[name])) {
|
|
324
|
+
gen.code.push(`${_name}${paramsObj[name]},`);
|
|
325
|
+
} else if (_isArray(paramsObj[name])) {
|
|
326
|
+
gen.code.push(`${_name}[`);
|
|
327
|
+
declareParamsObj.call(cx, gen, paramsObj[name], null, indentation + 1);
|
|
328
|
+
gen.code.push(` ${' '.repeat(indentation)}],`);
|
|
329
|
+
} else if (_isObject(paramsObj[name])) {
|
|
330
|
+
gen.code.push(`${_name}{`);
|
|
331
|
+
declareParamsObj.call(cx, gen, paramsObj[name], null, indentation + 1);
|
|
332
|
+
gen.code.push(` ${' '.repeat(indentation)}},`);
|
|
333
|
+
} else {
|
|
334
|
+
gen.code.push(`${_name}'${paramsObj[name]}',`);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
if (varName) gen.code.push(`};`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Bundle generated file
|
|
342
|
+
*
|
|
343
|
+
* @param object gen
|
|
344
|
+
* @param String outfile
|
|
345
|
+
* @param boolean asModule
|
|
346
|
+
*
|
|
347
|
+
* @return Promise
|
|
348
|
+
*/
|
|
349
|
+
async function bundle(gen, outfile, asModule = false) {
|
|
350
|
+
const cx = this || {};
|
|
351
|
+
const compression = !cx.flags.compression ? false : (
|
|
352
|
+
cx.flags.compression === true ? ['gz'] : cx.flags.compression.split(',').map(s => s.trim())
|
|
353
|
+
);
|
|
354
|
+
const moduleFile = `${_beforeLast(outfile, '.')}.esm.js`;
|
|
355
|
+
|
|
356
|
+
// ------------------
|
|
357
|
+
// >> Show waiting...
|
|
358
|
+
if (cx.logger) {
|
|
359
|
+
let waiting = cx.logger.waiting(cx.logger.f`Writing the ES module file: ${moduleFile}`);
|
|
360
|
+
waiting.start();
|
|
361
|
+
jsFile.write(gen, moduleFile, 'ES Module file');
|
|
362
|
+
waiting.stop();
|
|
363
|
+
} else {
|
|
364
|
+
jsFile.write(gen, moduleFile, 'ES Module file');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// ----------------
|
|
368
|
+
// >> Webpack config
|
|
369
|
+
const bundlingConfig = {
|
|
370
|
+
entryPoints: [ moduleFile ],
|
|
371
|
+
outfile,
|
|
372
|
+
bundle: true,
|
|
373
|
+
minify: true,
|
|
374
|
+
banner: { js: '/** @webqit/webflo */', },
|
|
375
|
+
footer: { js: '', },
|
|
376
|
+
format: 'esm',
|
|
377
|
+
};
|
|
378
|
+
if (!asModule) {
|
|
379
|
+
// Support top-level await
|
|
380
|
+
// See: https://github.com/evanw/esbuild/issues/253#issuecomment-826147115
|
|
381
|
+
bundlingConfig.banner.js += '(async () => {';
|
|
382
|
+
bundlingConfig.footer.js += '})();';
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// ----------------
|
|
386
|
+
// The bundling process
|
|
387
|
+
let waiting;
|
|
388
|
+
if (cx.logger) {
|
|
389
|
+
waiting = cx.logger.waiting(`Bundling...`);
|
|
390
|
+
cx.logger.log(cx.logger.style.keyword(`> `) + 'Bundling...');
|
|
391
|
+
waiting.start();
|
|
392
|
+
}
|
|
393
|
+
// Main
|
|
394
|
+
await EsBuild.build(bundlingConfig);
|
|
395
|
+
// Compress...
|
|
396
|
+
let compressedFiles = [], removals = [];
|
|
397
|
+
if (compression) {
|
|
398
|
+
const contents = Fs.readFileSync(bundlingConfig.outfile);
|
|
399
|
+
if (compression.includes('gz')) {
|
|
400
|
+
const gzip = gzipSync(contents, {});
|
|
401
|
+
Fs.writeFileSync(bundlingConfig.outfile + '.gz', gzip);
|
|
402
|
+
compressedFiles.push(bundlingConfig.outfile + '.gz');
|
|
403
|
+
} else {
|
|
404
|
+
removals.push(bundlingConfig.outfile + '.gz');
|
|
405
|
+
}
|
|
406
|
+
if (compression.includes('br')) {
|
|
407
|
+
const brotli = brotliCompressSync(contents, {});
|
|
408
|
+
Fs.writeFileSync(bundlingConfig.outfile + '.br', brotli);
|
|
409
|
+
compressedFiles.push(bundlingConfig.outfile + '.br');
|
|
410
|
+
} else {
|
|
411
|
+
removals.push(bundlingConfig.outfile + '.br');
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// Remove moduleFile build
|
|
415
|
+
Fs.unlinkSync(bundlingConfig.entryPoints[0]);
|
|
416
|
+
removals.forEach(file => Fs.existsSync(file) && Fs.unlinkSync(file));
|
|
417
|
+
if (waiting) waiting.stop();
|
|
418
|
+
// ----------------
|
|
419
|
+
// Stats
|
|
420
|
+
if (cx.logger) {
|
|
421
|
+
[bundlingConfig.outfile].concat(compressedFiles).forEach(file => {
|
|
422
|
+
let ext = '.' + _afterLast(file, '.');
|
|
423
|
+
cx.logger.info(cx.logger.style.comment(` [${ext}]: `) + cx.logger.style.url(file) + cx.logger.style.comment(` (${Fs.statSync(file).size / 1024} KB)`));
|
|
424
|
+
});
|
|
425
|
+
cx.logger.log('');
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Handles auto-embeds
|
|
431
|
+
*
|
|
432
|
+
* @param String targetDocumentFile
|
|
433
|
+
* @param Array embedList
|
|
434
|
+
* @param Array unembedList
|
|
435
|
+
*
|
|
436
|
+
* @return Void
|
|
437
|
+
*/
|
|
438
|
+
function handleEmbeds(targetDocumentFile, embedList, unembedList) {
|
|
439
|
+
let targetDocument, successLevel = 0;
|
|
440
|
+
if (Fs.existsSync(targetDocumentFile) && (targetDocument = Fs.readFileSync(targetDocumentFile).toString()) && targetDocument.trim().startsWith('<!DOCTYPE html')) {
|
|
441
|
+
successLevel = 1;
|
|
442
|
+
let dom = new Jsdom.JSDOM(targetDocument), by = 'webflo', touched;
|
|
443
|
+
let embed = (src, after) => {
|
|
444
|
+
src = src.replace(/\\/g, '/');
|
|
445
|
+
let embedded = dom.window.document.querySelector(`script[src="${src}"]`);
|
|
446
|
+
if (!embedded) {
|
|
447
|
+
embedded = dom.window.document.createElement('script');
|
|
448
|
+
embedded.setAttribute('type', 'module');
|
|
449
|
+
embedded.setAttribute('src', src);
|
|
450
|
+
embedded.setAttribute('by', by);
|
|
451
|
+
if (after) {
|
|
452
|
+
after.after(embedded, `\n\t\t`);
|
|
453
|
+
} else {
|
|
454
|
+
dom.window.document.head.appendChild(embedded);
|
|
455
|
+
}
|
|
456
|
+
touched = true;
|
|
457
|
+
}
|
|
458
|
+
return embedded;
|
|
459
|
+
};
|
|
460
|
+
let unembed = src => {
|
|
461
|
+
src = Path.join('/', src);
|
|
462
|
+
src = src.replace(/\\/g, '/');
|
|
463
|
+
let embedded = dom.window.document.querySelector(`script[src="${src}"][by="${by}"]`);
|
|
464
|
+
if (embedded) {
|
|
465
|
+
embedded.remove();
|
|
466
|
+
touched = true;
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
embedList.reduce((prev, src) => {
|
|
470
|
+
return embed(src, prev);
|
|
471
|
+
}, [ ...dom.window.document.head.querySelectorAll(`script[src]`) ].pop() || dom.window.document.querySelector(`script`));
|
|
472
|
+
unembedList.forEach(src => {
|
|
473
|
+
unembed(src);
|
|
474
|
+
});
|
|
475
|
+
if (touched) {
|
|
476
|
+
Fs.writeFileSync(targetDocumentFile, dom.serialize());
|
|
477
|
+
successLevel = 2;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return successLevel;
|
|
472
481
|
}
|