wirejs-scripts 3.0.176 → 3.0.178
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/bin.js +242 -496
- package/package.json +3 -12
package/bin.js
CHANGED
|
@@ -2,29 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
import process from 'process';
|
|
4
4
|
import { execSync, spawn } from 'child_process';
|
|
5
|
-
import http from 'http';
|
|
6
5
|
import fs from 'fs';
|
|
7
6
|
import os from 'os';
|
|
8
7
|
import path from 'path';
|
|
9
8
|
import { fileURLToPath } from 'url';
|
|
10
9
|
|
|
11
|
-
import webpack from 'webpack';
|
|
12
|
-
import webpackConfigure from './configs/webpack.config.js';
|
|
13
10
|
import { rimraf } from 'rimraf';
|
|
14
11
|
import esbuild from 'esbuild';
|
|
15
|
-
import { contentType } from 'mime-types';
|
|
16
12
|
|
|
17
|
-
import { JSDOM } from 'jsdom';
|
|
18
|
-
import { useJSDOM } from 'wirejs-dom/v2';
|
|
19
13
|
import {
|
|
20
|
-
requiresContext,
|
|
21
|
-
Context,
|
|
22
|
-
CookieJar,
|
|
23
14
|
CronJob,
|
|
24
|
-
Endpoint,
|
|
25
15
|
SystemAttribute,
|
|
26
16
|
} from 'wirejs-resources';
|
|
27
17
|
import { prebuildApi } from 'wirejs-resources/internal';
|
|
18
|
+
import { createServer as wirejsCreateServer } from 'wirejs-hosting';
|
|
28
19
|
import nodeCron from 'node-cron';
|
|
29
20
|
|
|
30
21
|
import * as DefaultGateway from 'default-gateway';
|
|
@@ -33,7 +24,6 @@ import * as NatPmp from 'nat-pmp';
|
|
|
33
24
|
const CWD = process.cwd();
|
|
34
25
|
const __filename = fileURLToPath(import.meta.url);
|
|
35
26
|
const __dirname = path.dirname(__filename);
|
|
36
|
-
const getWebpackConfig = () => webpackConfigure(process.env, process.argv);
|
|
37
27
|
const [_nodeBinPath, _scriptPath, action, ...actionArgs] = process.argv;
|
|
38
28
|
const [subAction] = actionArgs;
|
|
39
29
|
const processes = [];
|
|
@@ -243,530 +233,286 @@ async function startForwardingWSLPort(wslPort = httpPort, publicPort = httpPort)
|
|
|
243
233
|
}
|
|
244
234
|
}
|
|
245
235
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
const body = method.toLowerCase() === 'post'
|
|
254
|
-
? (await postData(req))
|
|
255
|
-
: undefined;
|
|
256
|
-
const origin = headers.origin || `http://${headers.host}`;
|
|
257
|
-
const location = new URL(`${origin}${url}`);
|
|
258
|
-
const cookies = new CookieJar(headers.cookie);
|
|
259
|
-
return new Context({
|
|
260
|
-
cookies,
|
|
261
|
-
location,
|
|
262
|
-
httpMethod: method,
|
|
263
|
-
requestHeaders: headers,
|
|
264
|
-
requestBody: body,
|
|
265
|
-
runtimeAttributes: [
|
|
266
|
-
new SystemAttribute('wirejs', 'deployment-type', {
|
|
267
|
-
description: 'Deployment under which your system is running.',
|
|
268
|
-
value: 'dev'
|
|
269
|
-
}),
|
|
270
|
-
new SystemAttribute('wirejs', 'http-origin-local', {
|
|
271
|
-
description: 'HTTP origin (base address) to use for local development.',
|
|
272
|
-
value: `http://localhost:${httpPort}`,
|
|
273
|
-
}),
|
|
274
|
-
new SystemAttribute('wirejs', 'http-origin-network', {
|
|
275
|
-
description: 'HTTP origin (base address) for machines on your network to use.',
|
|
276
|
-
value: `http://${networkIp}:${httpPort}`,
|
|
277
|
-
}),
|
|
278
|
-
new SystemAttribute('wirejs', 'http-origin-public', {
|
|
279
|
-
description: 'HTTP origin (base address) for machines outside your network to use. Only populated for `npm run start:public`, and only accessible in environments that support NAT-PMP.',
|
|
280
|
-
value: publicIp ? `http://${publicIp}:${httpPort}` : null,
|
|
281
|
-
}),
|
|
282
|
-
]
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
*
|
|
288
|
-
* @param {object} api
|
|
289
|
-
* @param {{method: string, args: any[]}} call
|
|
290
|
-
* @param {Context} context
|
|
291
|
-
* @returns {Promise<any>}
|
|
292
|
-
*/
|
|
293
|
-
async function callApiMethod(api, call, context) {
|
|
294
|
-
try {
|
|
295
|
-
const [scope, ...rest] = call.method;
|
|
296
|
-
logger.info('api method parsed', { scope, rest });
|
|
297
|
-
if (rest.length === 0) {
|
|
298
|
-
logger.info('api method resolved. invoking...', requiresContext(api[scope]));
|
|
299
|
-
if (requiresContext(api[scope])) {
|
|
300
|
-
return {
|
|
301
|
-
data: await api[scope](context, ...call.args.slice(1))
|
|
302
|
-
};
|
|
303
|
-
} else {
|
|
304
|
-
return {
|
|
305
|
-
data: await api[scope](...call.args)
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
} else {
|
|
309
|
-
logger.info('nested scope found');
|
|
310
|
-
return callApiMethod(api[scope], {
|
|
311
|
-
...call,
|
|
312
|
-
method: rest,
|
|
313
|
-
}, context);
|
|
314
|
-
}
|
|
315
|
-
} catch (error) {
|
|
316
|
-
console.log(error);
|
|
317
|
-
return { error: error.message };
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
*
|
|
323
|
-
* @param {Context} context
|
|
324
|
-
* @param {http.ServerResponse} res
|
|
325
|
-
* @returns
|
|
326
|
-
*/
|
|
327
|
-
async function handleApiResponse(context, res) {
|
|
328
|
-
const url = context.location.pathname;
|
|
329
|
-
|
|
330
|
-
if (url === '/api') {
|
|
331
|
-
const body = context.requestBody;
|
|
332
|
-
const calls = JSON.parse(body);
|
|
333
|
-
logger.info('handling API request', body);
|
|
334
|
-
|
|
335
|
-
const apiPath = path.join(CWD, 'api', 'dist', 'index.js');
|
|
336
|
-
const api = await import(`${apiPath}?cache-id=${new Date().getTime()}`);
|
|
337
|
-
|
|
338
|
-
const responses = [];
|
|
339
|
-
for (const call of calls) {
|
|
340
|
-
logger.info('handling API call', call);
|
|
341
|
-
responses.push(await callApiMethod(api, call, context));
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
logger.info('setting cookies', context.cookies.getSetCookies());
|
|
345
|
-
for (const cookie of context.cookies.getSetCookies()) {
|
|
346
|
-
const cookieOptions = [];
|
|
347
|
-
if (cookie.maxAge) cookieOptions.push(`Max-Age=${cookie.maxAge}`);
|
|
348
|
-
if (cookie.httpOnly) cookieOptions.push('HttpOnly');
|
|
349
|
-
if (cookie.secure) cookieOptions.push('Secure');
|
|
350
|
-
if (cookie.path) cookieOptions.push(`Path=${cookie.path}`);
|
|
351
|
-
|
|
352
|
-
logger.info('setting cookie', cookie.name, cookie.value, cookieOptions);
|
|
353
|
-
res.appendHeader(
|
|
354
|
-
'Set-Cookie',
|
|
355
|
-
`${cookie.name}=${cookie.value}; ${cookieOptions.join('; ')}`
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
360
|
-
// internal API isn't allowed to set individual response headers, codes, etc.
|
|
361
|
-
// because they may be batched.
|
|
362
|
-
res.end(JSON.stringify(
|
|
363
|
-
responses
|
|
364
|
-
));
|
|
365
|
-
} else {
|
|
366
|
-
logger.error('Bad endpoint given', { url });
|
|
367
|
-
|
|
368
|
-
res.statusCode = 404;
|
|
369
|
-
res.end("404 - Endpoint not found");
|
|
370
|
-
}
|
|
371
|
-
}
|
|
236
|
+
async function compile(watch = false) {
|
|
237
|
+
const srcDir = path.join(CWD, 'src');
|
|
238
|
+
const ssrDir = path.join(srcDir, 'ssr');
|
|
239
|
+
const ssgDir = path.join(srcDir, 'ssg');
|
|
240
|
+
const staticDir = path.join(CWD, 'static');
|
|
241
|
+
const distDir = path.join(CWD, 'dist');
|
|
242
|
+
const ssrOutDir = path.join(distDir, 'ssr');
|
|
372
243
|
|
|
373
|
-
/**
|
|
374
|
-
*
|
|
375
|
-
* @param {Context} context
|
|
376
|
-
* @param {http.ServerResponse} res
|
|
377
|
-
* @returns
|
|
378
|
-
*/
|
|
379
|
-
async function tryEndpointPath(context, res) {
|
|
380
244
|
/**
|
|
381
|
-
*
|
|
245
|
+
* Copy all files from src to dest recursively.
|
|
382
246
|
*/
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const apiPath = path.join(CWD, 'api', 'dist', 'index.js');
|
|
387
|
-
await import(`${apiPath}?cache-id=${new Date().getTime()}`);
|
|
388
|
-
const allHandlers = [...Endpoint.list()];
|
|
389
|
-
const matchingHandlers = allHandlers
|
|
390
|
-
.filter(e => globMatch(e.path, context.location.pathname));
|
|
391
|
-
matchingEndpoint = matchingHandlers.sort(byLength).pop();
|
|
392
|
-
} catch (error) {
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (!matchingEndpoint) return;
|
|
397
|
-
|
|
398
|
-
const response = await matchingEndpoint.handle(context)
|
|
399
|
-
setResponseDetailsFromContext(context, res);
|
|
400
|
-
res.end(response);
|
|
401
|
-
return true;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
*
|
|
406
|
-
* @param {Context} context
|
|
407
|
-
* @returns
|
|
408
|
-
*/
|
|
409
|
-
function fullPathFrom(context) {
|
|
410
|
-
const relativePath = context.location.pathname === '/'
|
|
411
|
-
? 'index.html'
|
|
412
|
-
: context.location.pathname;
|
|
413
|
-
return path.join(CWD, 'dist', relativePath);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
*
|
|
419
|
-
* @param {Context} context
|
|
420
|
-
* @param {http.ServerResponse} res
|
|
421
|
-
* @returns
|
|
422
|
-
*/
|
|
423
|
-
async function tryStaticPath(context, res) {
|
|
424
|
-
const fullPath = fullPathFrom(context);
|
|
425
|
-
|
|
426
|
-
logger.info('checking static', fullPath);
|
|
427
|
-
if (!fs.existsSync(fullPath)) return false;
|
|
428
|
-
logger.info('static found');
|
|
429
|
-
|
|
430
|
-
res.setHeader('Content-Type', contentType(path.extname(fullPath)));
|
|
431
|
-
|
|
432
|
-
try {
|
|
433
|
-
res.end(fs.readFileSync(fullPath));
|
|
434
|
-
} catch {
|
|
435
|
-
res.statusCode = 404;
|
|
436
|
-
res.end("404 - File not found (b)");
|
|
247
|
+
async function copyStatic() {
|
|
248
|
+
if (!fs.existsSync(staticDir)) return;
|
|
249
|
+
await fs.promises.cp(staticDir, distDir, { recursive: true });
|
|
437
250
|
}
|
|
438
251
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
function byLength(a, b) {
|
|
450
|
-
return a.length - b.length;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* @param {string} pattern - string pattern, where `*` matches anything
|
|
455
|
-
* @param {string} text
|
|
456
|
-
* @returns
|
|
457
|
-
*/
|
|
458
|
-
function globMatch(pattern, text) {
|
|
459
|
-
const parts = pattern.split('%');
|
|
460
|
-
const regex = new RegExp(parts.join('.+'));
|
|
461
|
-
return regex.test(text);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
function toJSPath(path) {
|
|
465
|
-
if (path.endsWith('/')) {
|
|
466
|
-
return path + 'index.js';
|
|
467
|
-
} else if (!path.endsWith('.js')) {
|
|
468
|
-
return path + '.js';
|
|
469
|
-
} else {
|
|
470
|
-
return path;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
*
|
|
476
|
-
* @param {Context} context
|
|
477
|
-
* @param {string} [forceExt]
|
|
478
|
-
*/
|
|
479
|
-
function routeSSR(context, forceExt) {
|
|
480
|
-
const SSR_ROOT = path.join(CWD, 'dist', 'ssr');
|
|
481
|
-
|
|
482
|
-
const asJSPath = forceExt
|
|
483
|
-
? toJSPath(context.location.pathname)
|
|
484
|
-
: context.location.pathname;
|
|
485
|
-
|
|
486
|
-
try {
|
|
487
|
-
const allHandlers = fs.readdirSync(SSR_ROOT, { recursive: true })
|
|
488
|
-
.filter(p => p.endsWith('.js'))
|
|
489
|
-
.map(p => `/${p}`)
|
|
490
|
-
;
|
|
491
|
-
const matchingHandlers = allHandlers.filter(h => globMatch(h, asJSPath));
|
|
492
|
-
const match = matchingHandlers.sort(byLength).pop();
|
|
493
|
-
|
|
494
|
-
if (match) {
|
|
495
|
-
return path.join(SSR_ROOT, match);
|
|
252
|
+
/**
|
|
253
|
+
* Find all .ts files recursively in a directory.
|
|
254
|
+
*/
|
|
255
|
+
function findTs(dir) {
|
|
256
|
+
try {
|
|
257
|
+
return fs.readdirSync(dir, { recursive: true, withFileTypes: true })
|
|
258
|
+
.filter(e => e.isFile() && e.name.endsWith('.ts'))
|
|
259
|
+
.map(e => path.join(e.parentPath ?? e.path, e.name));
|
|
260
|
+
} catch {
|
|
261
|
+
return [];
|
|
496
262
|
}
|
|
497
|
-
} catch (error) {
|
|
498
|
-
return;
|
|
499
263
|
}
|
|
500
|
-
}
|
|
501
264
|
|
|
502
|
-
/**
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
async function
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
logger.info('SSR handler associated script found', srcPath);
|
|
512
|
-
|
|
513
|
-
res.setHeader('Content-Type', 'text/javascript');
|
|
514
|
-
|
|
515
|
-
try {
|
|
516
|
-
res.end(fs.readFileSync(srcPath));
|
|
517
|
-
} catch {
|
|
518
|
-
res.statusCode = 404;
|
|
519
|
-
res.end("404 - File not found (c)");
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
return true;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
*
|
|
527
|
-
* @param {Context} context
|
|
528
|
-
* @param {http.ServerResponse} res
|
|
529
|
-
*/
|
|
530
|
-
function setResponseDetailsFromContext(context, res) {
|
|
531
|
-
for (const [k, v] of Object.entries(context.responseHeaders)) {
|
|
532
|
-
res.setHeader(k, v);
|
|
533
|
-
}
|
|
534
|
-
for (const cookie of context.cookies.getSetCookies()) {
|
|
535
|
-
const cookieOptions = [];
|
|
536
|
-
if (cookie.maxAge) cookieOptions.push(`Max-Age=${cookie.maxAge}`);
|
|
537
|
-
if (cookie.httpOnly) cookieOptions.push('HttpOnly');
|
|
538
|
-
if (cookie.secure) cookieOptions.push('Secure');
|
|
539
|
-
if (cookie.path) cookieOptions.push(`Path=${cookie.path}`);
|
|
540
|
-
const cookieStr = cookieOptions.length > 0
|
|
541
|
-
? `${cookie.name}=${cookie.value}; ${cookieOptions.join('; ')}`
|
|
542
|
-
: `${cookie.name}=${cookie.value}`;
|
|
543
|
-
res.appendHeader('Set-Cookie', cookieStr);
|
|
544
|
-
}
|
|
545
|
-
if (context.locationIsDirty) {
|
|
546
|
-
res.setHeader('Location', context.location.href);
|
|
547
|
-
}
|
|
548
|
-
if (context.responseCode) res.statusCode = context.responseCode;
|
|
549
|
-
}
|
|
265
|
+
/**
|
|
266
|
+
* Build SSG files and write the generated HTML to dist/.
|
|
267
|
+
* Also produces browser-compatible IIFE bundles at dist/*.js so that
|
|
268
|
+
* client-side hydration scripts referenced from the generated HTML are served.
|
|
269
|
+
*/
|
|
270
|
+
async function buildSsg() {
|
|
271
|
+
const ssgFiles = findTs(ssgDir);
|
|
272
|
+
if (ssgFiles.length === 0) return;
|
|
550
273
|
|
|
551
|
-
|
|
552
|
-
*
|
|
553
|
-
* @param {Context} context
|
|
554
|
-
* @param {http.ServerResponse} res
|
|
555
|
-
* @returns
|
|
556
|
-
*/
|
|
557
|
-
async function trySSRPath(context, res) {
|
|
558
|
-
const asJSPath = context.location.pathname.replace(/\.(\w+)$/, '') + '.js';
|
|
559
|
-
const srcPath = routeSSR(context, 'js');
|
|
560
|
-
if (!srcPath) return false;
|
|
274
|
+
const preSsgDir = path.join(CWD, 'pre-dist', 'ssg');
|
|
561
275
|
|
|
562
|
-
|
|
276
|
+
// 1. Build server-side bundles (ESM, for ssg.js import()) into pre-dist/ssg/
|
|
277
|
+
await esbuild.build({
|
|
278
|
+
entryPoints: ssgFiles,
|
|
279
|
+
outdir: preSsgDir,
|
|
280
|
+
outbase: ssgDir,
|
|
281
|
+
bundle: true,
|
|
282
|
+
platform: 'node',
|
|
283
|
+
format: 'esm',
|
|
284
|
+
conditions: ['wirejs:client'],
|
|
285
|
+
});
|
|
563
286
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
setResponseDetailsFromContext(context, res);
|
|
577
|
-
res.end(doc);
|
|
578
|
-
} else {
|
|
579
|
-
const doctype = doc.parentNode.doctype?.name || '';
|
|
580
|
-
|
|
581
|
-
let hydrationsFound = 0;
|
|
582
|
-
while (globalThis.pendingDehydrations?.length > 0) {
|
|
583
|
-
globalThis.pendingDehydrations.shift()(doc);
|
|
584
|
-
hydrationsFound++;
|
|
585
|
-
}
|
|
287
|
+
// 2. Build browser-side bundles (IIFE, for client hydration) into dist/
|
|
288
|
+
// These are referenced by <script src="..."> tags injected by ssg.js.
|
|
289
|
+
await esbuild.build({
|
|
290
|
+
entryPoints: ssgFiles,
|
|
291
|
+
outdir: distDir,
|
|
292
|
+
outbase: ssgDir,
|
|
293
|
+
bundle: true,
|
|
294
|
+
platform: 'browser',
|
|
295
|
+
format: 'iife',
|
|
296
|
+
conditions: ['wirejs:client'],
|
|
297
|
+
sourcemap: true,
|
|
298
|
+
});
|
|
586
299
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
300
|
+
// 3. Run ssg.js on each server-side bundle to produce HTML.
|
|
301
|
+
// Pass CWD/pre-dist as SRC_DIR so ssg.js strips the correct prefix
|
|
302
|
+
// when building the hydration <script src="..."> path (e.g. /index.js).
|
|
303
|
+
const preDist = path.join(CWD, 'pre-dist');
|
|
304
|
+
const compiledSsgFiles = fs.readdirSync(preSsgDir, { recursive: true, withFileTypes: true })
|
|
305
|
+
.filter(e => e.isFile() && e.name.endsWith('.js'))
|
|
306
|
+
.map(e => path.join(e.parentPath ?? e.path, e.name));
|
|
592
307
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
308
|
+
for (const ssgFile of compiledSsgFiles) {
|
|
309
|
+
try {
|
|
310
|
+
const html = execSync(
|
|
311
|
+
`node ${path.join(__dirname, 'ssg.js')} ${preDist} ${ssgFile}`,
|
|
312
|
+
{ stdio: ['pipe', 'pipe', 'pipe'] }
|
|
313
|
+
).toString();
|
|
314
|
+
const relative = path.relative(preSsgDir, ssgFile).replace(/\.js$/, '.html');
|
|
315
|
+
const htmlOut = path.join(distDir, relative);
|
|
316
|
+
await fs.promises.mkdir(path.dirname(htmlOut), { recursive: true });
|
|
317
|
+
await fs.promises.writeFile(htmlOut, html);
|
|
318
|
+
} catch (err) {
|
|
319
|
+
logger.error(`Could not generate SSG page ${ssgFile}`, err);
|
|
599
320
|
}
|
|
600
|
-
return true;
|
|
601
|
-
} else {
|
|
602
|
-
logger.info('SSR module missing generate function');
|
|
603
|
-
return false;
|
|
604
321
|
}
|
|
605
|
-
} catch (error) {
|
|
606
|
-
logger.error('ssr error', error);
|
|
607
|
-
res.statusCode = 404;
|
|
608
|
-
res.end("404 - File not found (a)");
|
|
609
322
|
}
|
|
610
323
|
|
|
611
|
-
|
|
612
|
-
|
|
324
|
+
if (watch) {
|
|
325
|
+
const apiDir = path.join(CWD, 'api');
|
|
613
326
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
if (await trySSRPath(context, res, fs)) return;
|
|
632
|
-
if (await tryEndpointPath(context, res, fs)) return;
|
|
633
|
-
|
|
634
|
-
// if we've made it this far, we don't have what you're looking for
|
|
635
|
-
res.statusCode = '404';
|
|
636
|
-
res.end('404 - Not found');
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
*
|
|
641
|
-
* @param {http.IncomingMessage} request
|
|
642
|
-
* @returns
|
|
643
|
-
*/
|
|
644
|
-
async function postData(request) {
|
|
645
|
-
return new Promise((resolve, reject) => {
|
|
646
|
-
const buffer = [];
|
|
647
|
-
const timeout = setTimeout(() => {
|
|
648
|
-
reject("Post data not received.");
|
|
649
|
-
}, 5000);
|
|
650
|
-
request.on('data', data => buffer.push(data));
|
|
651
|
-
request.on('end', () => {
|
|
652
|
-
if (!timeout) return;
|
|
653
|
-
clearTimeout(timeout);
|
|
654
|
-
resolve(buffer.join(''));
|
|
327
|
+
// API watch (unchanged)
|
|
328
|
+
const prebuildApiCtx = await esbuild.context({
|
|
329
|
+
entryPoints: [path.join(CWD, 'api', '**', '*.ts')],
|
|
330
|
+
outdir: path.join(apiDir, 'dist'),
|
|
331
|
+
platform: 'node',
|
|
332
|
+
bundle: true,
|
|
333
|
+
packages: 'external',
|
|
334
|
+
format: 'esm',
|
|
335
|
+
plugins: [{
|
|
336
|
+
name: 'post-build-rebuild-api-client',
|
|
337
|
+
setup(build) {
|
|
338
|
+
build.onEnd(async () => {
|
|
339
|
+
await prebuildApi(apiDir);
|
|
340
|
+
await refreshCronSchedules(apiDir);
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}]
|
|
655
344
|
});
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
entryPoints: [path.join(CWD, 'api', '**', '*.ts')],
|
|
666
|
-
outdir: path.join(apiDir, 'dist'),
|
|
667
|
-
platform: 'node',
|
|
345
|
+
await prebuildApiCtx.rebuild();
|
|
346
|
+
prebuildApiCtx.watch();
|
|
347
|
+
|
|
348
|
+
// Client index bundle (esbuild, replaces webpack)
|
|
349
|
+
const indexEntry = path.join(srcDir, 'index.ts');
|
|
350
|
+
if (fs.existsSync(indexEntry)) {
|
|
351
|
+
const clientCtx = await esbuild.context({
|
|
352
|
+
entryPoints: [indexEntry],
|
|
353
|
+
outdir: distDir,
|
|
668
354
|
bundle: true,
|
|
669
|
-
|
|
355
|
+
platform: 'browser',
|
|
670
356
|
format: 'esm',
|
|
357
|
+
conditions: ['wirejs:client'],
|
|
358
|
+
sourcemap: true,
|
|
359
|
+
// preserveSymlinks: true, // needed?
|
|
671
360
|
plugins: [{
|
|
672
|
-
name: '
|
|
361
|
+
name: 'client-build-done',
|
|
673
362
|
setup(build) {
|
|
674
|
-
build.onEnd(
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
363
|
+
build.onEnd(() => {
|
|
364
|
+
console.log();
|
|
365
|
+
console.log('Compiled.\n');
|
|
366
|
+
console.log(`Local\nhttp://localhost:${httpPort}/\n`);
|
|
367
|
+
networkIp = getLocalIPv4();
|
|
368
|
+
if (networkIp) {
|
|
369
|
+
console.log(`Network\nhttp://${networkIp}:${httpPort}/\n`);
|
|
370
|
+
}
|
|
371
|
+
if (publicIp) {
|
|
372
|
+
console.log(`Public\nhttp://${publicIp}:${httpPort}/\n`);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
678
375
|
}
|
|
679
376
|
}]
|
|
680
377
|
});
|
|
681
|
-
await
|
|
682
|
-
|
|
378
|
+
await clientCtx.rebuild();
|
|
379
|
+
clientCtx.watch();
|
|
380
|
+
}
|
|
683
381
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
382
|
+
// SSR watch
|
|
383
|
+
const ssrFiles = findTs(ssrDir);
|
|
384
|
+
if (ssrFiles.length > 0) {
|
|
385
|
+
const ssrCtx = await esbuild.context({
|
|
386
|
+
entryPoints: ssrFiles,
|
|
387
|
+
outdir: ssrOutDir,
|
|
388
|
+
outbase: ssrDir,
|
|
389
|
+
entryNames: '[dir]/[name]',
|
|
390
|
+
bundle: true,
|
|
391
|
+
platform: 'browser',
|
|
688
392
|
format: 'esm',
|
|
689
393
|
conditions: ['wirejs:client'],
|
|
394
|
+
sourcemap: true,
|
|
690
395
|
});
|
|
691
|
-
await
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (actionArgs.includes('--public') || actionArgs.includes('-p')) {
|
|
695
|
-
console.log("Attempting to open external port.");
|
|
696
|
-
const gatewayIp = await getGateway();
|
|
697
|
-
const natPmpClient = NatPmp.connect(gatewayIp);
|
|
698
|
-
natPmpClient.externalIp((err, info) => {
|
|
699
|
-
if (err) throw err;
|
|
700
|
-
const ip = info.ip.join('.');
|
|
701
|
-
console.log(`Found external address: ${ip} ... trying to connect.`);
|
|
702
|
-
for (const port of [httpPort, wsPort]) {
|
|
703
|
-
natPmpClient.portMapping({
|
|
704
|
-
private: port,
|
|
705
|
-
public: port,
|
|
706
|
-
ttl: 3600,
|
|
707
|
-
}, (err, info) => {
|
|
708
|
-
if (err) throw err;
|
|
709
|
-
console.log(`Listening externally at ${ip}:${port}`);
|
|
710
|
-
publicIp = ip
|
|
711
|
-
});
|
|
712
|
-
};
|
|
713
|
-
});
|
|
714
|
-
if (isWSL()) startForwardingWSLPort();
|
|
715
|
-
}
|
|
396
|
+
await ssrCtx.rebuild();
|
|
397
|
+
ssrCtx.watch();
|
|
398
|
+
}
|
|
716
399
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
400
|
+
// Static files
|
|
401
|
+
await copyStatic();
|
|
402
|
+
// SSG (run once; changes require a restart)
|
|
403
|
+
await buildSsg();
|
|
404
|
+
|
|
405
|
+
// NAT-PMP / --public flag
|
|
406
|
+
if (actionArgs.includes('--public') || actionArgs.includes('-p')) {
|
|
407
|
+
console.log("Attempting to open external port.");
|
|
408
|
+
const gatewayIp = await getGateway();
|
|
409
|
+
const natPmpClient = NatPmp.connect(gatewayIp);
|
|
410
|
+
natPmpClient.externalIp((err, info) => {
|
|
411
|
+
if (err) throw err;
|
|
412
|
+
const ip = info.ip.join('.');
|
|
413
|
+
console.log(`Found external address: ${ip} ... trying to connect.`);
|
|
414
|
+
for (const port of [httpPort, wsPort]) {
|
|
415
|
+
natPmpClient.portMapping({
|
|
416
|
+
private: port,
|
|
417
|
+
public: port,
|
|
418
|
+
ttl: 3600,
|
|
419
|
+
}, (err, info) => {
|
|
420
|
+
if (err) throw err;
|
|
421
|
+
console.log(`Listening externally at ${ip}:${port}`);
|
|
422
|
+
publicIp = ip;
|
|
423
|
+
});
|
|
731
424
|
}
|
|
732
|
-
}).run(() => { });
|
|
733
|
-
|
|
734
|
-
logger.log('Starting server...');
|
|
735
|
-
const server = http.createServer(handleRequest);
|
|
736
|
-
server.listen(httpPort).on('listening', () => {
|
|
737
|
-
console.log(`Started listening on port ${httpPort}`);
|
|
738
425
|
});
|
|
739
|
-
|
|
740
|
-
|
|
426
|
+
if (isWSL()) startForwardingWSLPort();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
logger.log('Starting server...');
|
|
430
|
+
const server = wirejsCreateServer({
|
|
431
|
+
apiDir: path.join(CWD, 'api'),
|
|
432
|
+
distDir,
|
|
433
|
+
port: httpPort,
|
|
434
|
+
attributes: () => [
|
|
435
|
+
new SystemAttribute('wirejs', 'deployment-type', {
|
|
436
|
+
description: 'Deployment under which your system is running.',
|
|
437
|
+
value: 'dev',
|
|
438
|
+
}),
|
|
439
|
+
new SystemAttribute('wirejs', 'http-origin-local', {
|
|
440
|
+
description: 'HTTP origin (base address) to use for local development.',
|
|
441
|
+
value: `http://localhost:${httpPort}`,
|
|
442
|
+
}),
|
|
443
|
+
new SystemAttribute('wirejs', 'http-origin-network', {
|
|
444
|
+
description: 'HTTP origin (base address) for machines on your network to use.',
|
|
445
|
+
value: networkIp ? `http://${networkIp}:${httpPort}` : null,
|
|
446
|
+
}),
|
|
447
|
+
new SystemAttribute('wirejs', 'http-origin-public', {
|
|
448
|
+
description: 'HTTP origin (base address) for machines outside your network to use. Only populated for `npm run start:public`.',
|
|
449
|
+
value: publicIp ? `http://${publicIp}:${httpPort}` : null,
|
|
450
|
+
}),
|
|
451
|
+
],
|
|
452
|
+
});
|
|
453
|
+
server.listen(httpPort).on('listening', () => {
|
|
454
|
+
console.log(`Started listening on port ${httpPort}`);
|
|
455
|
+
});
|
|
456
|
+
} else {
|
|
457
|
+
// Non-watch build
|
|
458
|
+
console.log('non-watch build');
|
|
459
|
+
|
|
460
|
+
// Client index bundle
|
|
461
|
+
console.log('building index.ts');
|
|
462
|
+
const indexEntry = path.join(srcDir, 'index.ts');
|
|
463
|
+
if (fs.existsSync(indexEntry)) {
|
|
741
464
|
await esbuild.build({
|
|
742
|
-
entryPoints: [
|
|
743
|
-
outdir:
|
|
744
|
-
bundle:
|
|
465
|
+
entryPoints: [indexEntry],
|
|
466
|
+
outdir: distDir,
|
|
467
|
+
bundle: true,
|
|
468
|
+
platform: 'node',
|
|
745
469
|
format: 'esm',
|
|
746
470
|
conditions: ['wirejs:client'],
|
|
471
|
+
sourcemap: true,
|
|
472
|
+
// preserveSymlinks: true,
|
|
747
473
|
});
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// SSR bundles
|
|
477
|
+
console.log('building ssr');
|
|
478
|
+
const ssrFiles = findTs(ssrDir);
|
|
479
|
+
if (ssrFiles.length > 0) {
|
|
480
|
+
// server-side bundle
|
|
481
|
+
await esbuild.build({
|
|
482
|
+
entryPoints: ssrFiles,
|
|
483
|
+
outdir: ssrOutDir,
|
|
484
|
+
outbase: ssrDir,
|
|
485
|
+
entryNames: '[dir]/[name]',
|
|
486
|
+
bundle: true,
|
|
487
|
+
platform: 'node',
|
|
488
|
+
format: 'cjs',
|
|
489
|
+
conditions: ['wirejs:client'],
|
|
490
|
+
sourcemap: true,
|
|
491
|
+
// preserveSymlinks: true,
|
|
492
|
+
});
|
|
493
|
+
// client bundle
|
|
494
|
+
await esbuild.build({
|
|
495
|
+
entryPoints: ssrFiles,
|
|
496
|
+
outdir: ssrOutDir,
|
|
497
|
+
outbase: ssrDir,
|
|
498
|
+
entryNames: '[dir]/[name].client',
|
|
499
|
+
bundle: true,
|
|
500
|
+
platform: 'browser',
|
|
501
|
+
format: 'iife',
|
|
502
|
+
conditions: ['wirejs:client'],
|
|
503
|
+
sourcemap: true,
|
|
504
|
+
// preserveSymlinks: true,
|
|
760
505
|
});
|
|
761
506
|
}
|
|
762
|
-
});
|
|
763
507
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
508
|
+
// Static files
|
|
509
|
+
console.log('copying static files');
|
|
510
|
+
await copyStatic();
|
|
768
511
|
|
|
769
|
-
|
|
512
|
+
// SSG
|
|
513
|
+
console.log('copying ssg files');
|
|
514
|
+
await buildSsg();
|
|
515
|
+
}
|
|
770
516
|
}
|
|
771
517
|
|
|
772
518
|
const engine = {
|
|
@@ -835,10 +581,10 @@ const engine = {
|
|
|
835
581
|
await rimraf('pre-dist');
|
|
836
582
|
logger.log('cleared old pre-dist folder');
|
|
837
583
|
|
|
838
|
-
await fs.promises.mkdir('dist');
|
|
584
|
+
await fs.promises.mkdir('dist', { recursive: true });
|
|
839
585
|
logger.log('recreated dist folder');
|
|
840
586
|
|
|
841
|
-
await fs.promises.mkdir('pre-dist');
|
|
587
|
+
await fs.promises.mkdir('pre-dist', { recursive: true });
|
|
842
588
|
logger.log('recreated pre-dist folder');
|
|
843
589
|
|
|
844
590
|
await compile(watch);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wirejs-scripts",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.178",
|
|
4
4
|
"description": "Basic build and start commands for wirejs apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,22 +22,13 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/svidgen/create-wirejs-app#readme",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"copy-webpack-plugin": "^10.2.4",
|
|
26
|
-
"css-loader": "^5.2.0",
|
|
27
25
|
"default-gateway": "^7.2.2",
|
|
28
26
|
"esbuild": "^0.24.2",
|
|
29
|
-
"file-loader": "^6.2.0",
|
|
30
|
-
"glob": "^7.2.0",
|
|
31
|
-
"jsdom": "^25.0.1",
|
|
32
|
-
"marked": "^2.0.1",
|
|
33
|
-
"mime-types": "^2.1.35",
|
|
34
27
|
"nat-pmp": "^1.0.0",
|
|
35
28
|
"node-cron": "^4.2.1",
|
|
36
|
-
"raw-loader": "^4.0.2",
|
|
37
29
|
"rimraf": "^6.0.1",
|
|
38
|
-
"style-loader": "^2.0.0",
|
|
39
|
-
"webpack": "^5.97.1",
|
|
40
30
|
"wirejs-dom": "^1.0.44",
|
|
41
|
-
"wirejs-
|
|
31
|
+
"wirejs-hosting": "^0.1.180",
|
|
32
|
+
"wirejs-resources": "^0.1.180"
|
|
42
33
|
}
|
|
43
34
|
}
|