wirejs-scripts 3.0.175 → 3.0.177

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.
Files changed (2) hide show
  1. package/bin.js +242 -496
  2. 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
- * @param {http.IncomingMessage} req
249
- * @returns
250
- */
251
- async function createContext(req) {
252
- const { url, headers, method } = req;
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
- * @type Endpoint
245
+ * Copy all files from src to dest recursively.
382
246
  */
383
- let matchingEndpoint = undefined;
384
-
385
- try {
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
- return true;
440
- }
441
-
442
- /**
443
- * Compare two strings by length for sorting in order of increasing length.
444
- *
445
- * @param {string} a
446
- * @param {string} b
447
- * @returns
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
- * @param {Context} context
504
- * @param {http.ServerResponse} res
505
- * @returns
506
- */
507
- async function trySSRScriptPath(context, res) {
508
- const srcPath = routeSSR(context);
509
- if (!srcPath) return false;
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
- logger.info('SSR handler found', srcPath);
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
- try {
565
- useJSDOM(JSDOM);
566
- const self = {};
567
- const moduleData = await fs.promises.readFile(srcPath);
568
- eval(`${moduleData}`);
569
- const module = self.exports;
570
- if (typeof module.generate === 'function') {
571
- const doc = await module.generate(context);
572
- if (typeof doc.outerHTML === 'undefined') {
573
- res.setHeader('Content-Type', contentType(
574
- context.location.pathname.split('.').pop()
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
- if (hydrationsFound) {
588
- const script = doc.parentNode.createElement('script');
589
- script.src = asJSPath;
590
- doc.parentNode.body.appendChild(script);
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
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
594
- setResponseDetailsFromContext(context, res);
595
- res.end([
596
- doctype ? `<!doctype ${doctype}>\n` : '',
597
- doc.outerHTML
598
- ].join(''));
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
- return true;
612
- }
324
+ if (watch) {
325
+ const apiDir = path.join(CWD, 'api');
613
326
 
614
- /**
615
- *
616
- * @param {http.IncomingMessage} req
617
- * @param {http.ServerResponse} res
618
- * @param {any} compiler
619
- * @returns
620
- */
621
- async function handleRequest(req, res, compiler) {
622
- logger.info('received', JSON.stringify({ url: req.url }, null, 2));
623
- const context = await createContext(req);
624
-
625
- if (req.url.startsWith('/api')) {
626
- return handleApiResponse(context, res, compiler);
627
- }
628
-
629
- if (await tryStaticPath(context, res, fs)) return;
630
- if (await trySSRScriptPath(context, res, fs)) return;
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
- async function compile(watch = false) {
660
- const stats = await new Promise(async (resolve, reject) => {
661
- let compiler;
662
- if (watch) {
663
- const apiDir = path.join(CWD, 'api');
664
- const prebuildApiWatch = await esbuild.context({
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
- packages: 'external',
355
+ platform: 'browser',
670
356
  format: 'esm',
357
+ conditions: ['wirejs:client'],
358
+ sourcemap: true,
359
+ // preserveSymlinks: true, // needed?
671
360
  plugins: [{
672
- name: 'post-build-rebuild-api-client',
361
+ name: 'client-build-done',
673
362
  setup(build) {
674
- build.onEnd(async () => {
675
- await prebuildApi(apiDir);
676
- await refreshCronSchedules(apiDir);
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 prebuildApiWatch.rebuild();
682
- prebuildApiWatch.watch();
378
+ await clientCtx.rebuild();
379
+ clientCtx.watch();
380
+ }
683
381
 
684
- const prebuild = await esbuild.context({
685
- entryPoints: [`${CWD}/src/**/*.ts`],
686
- outdir: `${CWD}/pre-dist`,
687
- bundle: false,
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 prebuild.rebuild();
692
- prebuild.watch();
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
- webpack({
718
- ...getWebpackConfig(),
719
- mode: 'development',
720
- watch: true
721
- }, () => {
722
- console.log();
723
- console.log('Compiled.\n');
724
- console.log(`Local\nhttp://localhost:${httpPort}/\n`);
725
- networkIp = getLocalIPv4();
726
- if (networkIp) {
727
- console.log(`Network\nhttp://${networkIp}:${httpPort}/\n`)
728
- }
729
- if (publicIp) {
730
- console.log(`Public\nhttp://${publicIp}:${httpPort}/\n`);
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
- } else {
740
- logger.log('prebundling JS');
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: [`${CWD}/src/**/*.ts`],
743
- outdir: `${CWD}/pre-dist`,
744
- bundle: false,
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
- logger.log('starting webpack compiler');
749
- compiler = webpack(getWebpackConfig());
750
- compiler.run((err, res) => {
751
- logger.log('invoking webpack compiler');
752
- if (err) {
753
- logger.error('webpack compiler failed');
754
- logger.error(err);
755
- reject(err);
756
- } else {
757
- logger.info('webpack compiler succeeded');
758
- resolve(res);
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
- if (stats?.compilation?.errors?.length > 0) {
765
- logger.log('compilation errors', stats.compilation.errors);
766
- throw new Error('Build failed.');
767
- }
508
+ // Static files
509
+ console.log('copying static files');
510
+ await copyStatic();
768
511
 
769
- return stats;
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.175",
3
+ "version": "3.0.177",
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-resources": "^0.1.177"
31
+ "wirejs-hosting": "^0.1.179",
32
+ "wirejs-resources": "^0.1.179"
42
33
  }
43
34
  }