@webqit/webflo 0.20.4-next.2 → 0.20.4-next.4

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 (56) hide show
  1. package/package.json +13 -34
  2. package/site/docs/concepts/realtime.md +45 -44
  3. package/site/docs/getting-started.md +40 -40
  4. package/src/{Context.js → CLIContext.js} +9 -8
  5. package/src/build-pi/esbuild-plugin-uselive-transform.js +42 -0
  6. package/src/{runtime-pi/webflo-client/webflo-codegen.js → build-pi/index.js} +148 -142
  7. package/src/index.js +3 -1
  8. package/src/init-pi/index.js +7 -4
  9. package/src/init-pi/templates/pwa/.gitignore +6 -0
  10. package/src/init-pi/templates/pwa/.webqit/webflo/client.json +15 -0
  11. package/src/init-pi/templates/pwa/.webqit/webflo/layout.json +7 -0
  12. package/src/init-pi/templates/pwa/package.json +2 -2
  13. package/src/init-pi/templates/pwa/public/manifest.json +2 -2
  14. package/src/init-pi/templates/web/.gitignore +6 -0
  15. package/src/init-pi/templates/web/.webqit/webflo/client.json +12 -0
  16. package/src/init-pi/templates/web/.webqit/webflo/layout.json +7 -0
  17. package/src/init-pi/templates/web/package.json +2 -2
  18. package/src/runtime-pi/AppBootstrap.js +38 -0
  19. package/src/runtime-pi/WebfloRuntime.js +68 -56
  20. package/src/runtime-pi/apis.js +9 -0
  21. package/src/runtime-pi/index.js +2 -4
  22. package/src/runtime-pi/webflo-client/DeviceCapabilities.js +1 -1
  23. package/src/runtime-pi/webflo-client/WebfloClient.js +33 -36
  24. package/src/runtime-pi/webflo-client/WebfloRootClient1.js +23 -17
  25. package/src/runtime-pi/webflo-client/WebfloRootClient2.js +1 -1
  26. package/src/runtime-pi/webflo-client/WebfloSubClient.js +14 -14
  27. package/src/runtime-pi/webflo-client/bootstrap.js +38 -0
  28. package/src/runtime-pi/webflo-client/index.js +2 -8
  29. package/src/runtime-pi/webflo-client/webflo-devmode.js +3 -3
  30. package/src/runtime-pi/webflo-fetch/LiveResponse.js +154 -116
  31. package/src/runtime-pi/webflo-fetch/index.js +436 -5
  32. package/src/runtime-pi/webflo-messaging/wq-message-port.js +1 -1
  33. package/src/runtime-pi/webflo-routing/HttpCookies.js +1 -1
  34. package/src/runtime-pi/webflo-routing/HttpEvent.js +12 -11
  35. package/src/runtime-pi/webflo-routing/HttpUser.js +7 -7
  36. package/src/runtime-pi/webflo-routing/WebfloRouter.js +12 -7
  37. package/src/runtime-pi/webflo-server/ServerSideCookies.js +3 -1
  38. package/src/runtime-pi/webflo-server/ServerSideSession.js +2 -1
  39. package/src/runtime-pi/webflo-server/WebfloServer.js +138 -200
  40. package/src/runtime-pi/webflo-server/bootstrap.js +59 -0
  41. package/src/runtime-pi/webflo-server/index.js +2 -6
  42. package/src/runtime-pi/webflo-server/webflo-devmode.js +24 -31
  43. package/src/runtime-pi/webflo-url/Url.js +1 -1
  44. package/src/runtime-pi/webflo-url/xURL.js +1 -1
  45. package/src/runtime-pi/webflo-worker/WebfloWorker.js +11 -15
  46. package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +2 -1
  47. package/src/runtime-pi/webflo-worker/bootstrap.js +39 -0
  48. package/src/runtime-pi/webflo-worker/index.js +3 -7
  49. package/src/webflo-cli.js +1 -2
  50. package/src/runtime-pi/webflo-fetch/cookies.js +0 -10
  51. package/src/runtime-pi/webflo-fetch/fetch.js +0 -16
  52. package/src/runtime-pi/webflo-fetch/formdata.js +0 -54
  53. package/src/runtime-pi/webflo-fetch/headers.js +0 -151
  54. package/src/runtime-pi/webflo-fetch/message.js +0 -49
  55. package/src/runtime-pi/webflo-fetch/request.js +0 -62
  56. package/src/runtime-pi/webflo-fetch/response.js +0 -110
@@ -1,21 +1,18 @@
1
1
  import Fs from 'fs';
2
2
  import Path from 'path';
3
+ import Url from 'url';
3
4
  import Jsdom from 'jsdom';
4
5
  import EsBuild from 'esbuild';
5
6
  import { gzipSync, brotliCompressSync } from 'zlib';
6
7
  import { _afterLast, _beforeLast } from '@webqit/util/str/index.js';
7
8
  import { _isObject, _isArray } from '@webqit/util/js/index.js';
8
9
  import { jsFile } from '@webqit/backpack/src/dotfile/index.js';
9
- import { Context } from '../../Context.js';
10
- import {
11
- readClientConfig,
12
- readWorkerConfig,
13
- readLayoutConfig,
14
- readEnvConfig,
15
- scanRoots,
16
- scanRouteHandlers,
17
- } from '../../deployment-pi/util.js';
18
- import '../webflo-url/urlpattern.js';
10
+ import { bootstrap as serverBootstrap } from '../runtime-pi/webflo-server/bootstrap.js';
11
+ import { bootstrap as clientBootstrap } from '../runtime-pi/webflo-client/bootstrap.js';
12
+ import { bootstrap as workerBootstrap } from '../runtime-pi/webflo-worker/bootstrap.js';
13
+ import { UseLiveTransform } from './esbuild-plugin-uselive-transform.js';
14
+ import { CLIContext } from '../CLIContext.js';
15
+ import '../runtime-pi/webflo-url/urlpattern.js';
19
16
 
20
17
  function declareConfig({ $source, configExport, indentation = 0 }) {
21
18
  const varName = 'config';
@@ -51,20 +48,25 @@ function declareConfig({ $source, configExport, indentation = 0 }) {
51
48
  }
52
49
  }
53
50
 
54
- function declareRoutes({ $context, $config, $source, which, offset, roots = [] }) {
51
+ function declareRoutes({ $context, $source, bootstrap }) {
55
52
  const { flags: FLAGS, logger: LOGGER } = $context;
56
53
  LOGGER?.log(LOGGER.style.keyword(`> `) + `Declaring routes...`);
54
+
57
55
  // Define vars
58
56
  const varName = 'routes';
59
- const targetDir = Path.join(FLAGS.outdir || $config.LAYOUT.PUBLIC_DIR, offset);
57
+ const targetDir = FLAGS.outdir || bootstrap.outdir;
58
+
60
59
  // >> Routes mapping
61
60
  $source.code.push(`const ${varName} = {};`);
61
+
62
62
  // Route entries
63
63
  let routeCount = 0;
64
- scanRouteHandlers($config.LAYOUT, which, (file, route, filename, fstat) => {
64
+ for (const route of Object.keys(bootstrap.routes)) {
65
+ const file = bootstrap.routes[route];
66
+ const fstat = Fs.statSync(file);
65
67
  // The "import" code
66
68
  const routeId = 'route' + (++routeCount);
67
- const importPath = Path.relative(targetDir, file);
69
+ const importPath = file;
68
70
  $source.imports[importPath] = '* as ' + routeId;
69
71
  // The route def
70
72
  $source.code.push(`${varName}['${route}'] = ${routeId};`);
@@ -72,35 +74,52 @@ function declareRoutes({ $context, $config, $source, which, offset, roots = [] }
72
74
  LOGGER?.log(
73
75
  LOGGER.style.comment(` [${route}]: `) + LOGGER.style.url(importPath) + LOGGER.style.comment(` (${fstat.size / 1024} KB)`)
74
76
  );
75
- }, offset, roots);
76
- // >> Specials
77
- $source.code.push(`Object.defineProperty(${varName}, '$root', { value: '${offset}' });`);
78
- $source.code.push(`Object.defineProperty(${varName}, '$sparoots', { value: ${JSON.stringify(roots)} });`);
77
+ }
78
+
79
79
  if (!routeCount) {
80
80
  LOGGER?.log(LOGGER.style.comment(` (none)`));
81
81
  }
82
82
  }
83
83
 
84
84
  function writeImportWebflo($source, which) {
85
- $source.imports[`@webqit/webflo/src/runtime-pi/webflo-${which}/index.js`] = `{ start }`;
85
+ const importUrl = Url.fileURLToPath(import.meta.url);
86
+ const importPath = Path.join(Path.dirname(importUrl), `../runtime-pi/webflo-${which}/index.js`);
87
+ $source.imports[importPath] = `{ start }`;
86
88
  }
87
89
 
88
- function writeScriptBody({ $context, $config, $source, which, offset, roots, configExport }) {
90
+ function writeScriptBody({ $context, $source, bootstrap, configExport, which }) {
91
+ // >> Init
92
+ if (bootstrap.$init) {
93
+ const importPath = bootstrap.$init;
94
+ $source.imports[importPath] = `* as init`;
95
+ } else {
96
+ $source.code.push(`const init = null;`);
97
+ }
98
+
89
99
  // >> Config
90
100
  $source.code.push(`// >> Config export`);
91
101
  declareConfig({ $source, configExport });
92
102
  $source.code.push(``);
103
+
93
104
  // >> Routes mapping
94
105
  $source.code.push(`// >> Routes`);
95
- declareRoutes({ $context, $config, $source, which, offset, roots });
106
+ declareRoutes({ $context, $source, bootstrap, which });
107
+ $source.code.push(``);
108
+
109
+ // >> Specials
110
+ $source.code.push(`// >> Routes`);
111
+ $source.code.push(`const $root = '${bootstrap.offset || null}';`);
112
+ $source.code.push(`const $roots = ${bootstrap.$roots?.length ? JSON.stringify(bootstrap.$roots) : '[]'};`);
113
+ $source.code.push(`const $sparoots = ${bootstrap.$sparoots?.length ? JSON.stringify(bootstrap.$sparoots) : '[]'};`);
96
114
  $source.code.push(``);
115
+
97
116
  // >> Startup
98
117
  $source.code.push(`// >> Startup`);
99
- $source.code.push(`self.webqit = self.webqit || {};`);
100
- $source.code.push(`self.webqit.app = await start.call({ config, routes })`);
118
+ $source.code.push(`globalThis.webqit = globalThis.webqit || {};`);
119
+ $source.code.push(`globalThis.webqit.app = await start({ init, config, routes, $root, $roots, $sparoots });`);
101
120
  }
102
121
 
103
- async function bundleScript({ $context, $source, which, outfile, asModule = false, ...restParams }) {
122
+ async function bundleScript({ $context, $source, which, outfile, asModule = true, ...restParams }) {
104
123
  const { flags: FLAGS, logger: LOGGER } = $context;
105
124
  // >> Show banner...
106
125
  LOGGER?.log(LOGGER.style.keyword(`---`));
@@ -126,11 +145,14 @@ async function bundleScript({ $context, $source, which, outfile, asModule = fals
126
145
  const bundlingConfig = {
127
146
  entryPoints: [moduleFile],
128
147
  outfile,
129
- bundle: true,
130
- minify: true,
148
+ format: asModule ? 'esm' : 'iife',
149
+ platform: which === 'server' ? 'node' : 'browser', // optional but good for clarity
150
+ bundle: which === 'server' ? false : true,
151
+ minify: which === 'server' ? false : true,
152
+ treeShaking: true, // Important optimization
131
153
  banner: { js: '/** @webqit/webflo */', },
132
154
  footer: { js: '', },
133
- format: 'esm',
155
+ plugins: [UseLiveTransform()],
134
156
  ...(restParams.buildParams || {})
135
157
  };
136
158
  if (!asModule) {
@@ -248,38 +270,37 @@ function handleEmbeds($context, embeds, targetDocumentFile) {
248
270
 
249
271
  // -------------
250
272
 
251
- async function generateClientScript({ $context, $config, offset = '', roots = [], ...restParams }) {
273
+ async function generateClientScript({ $context, bootstrap, ...restParams }) {
252
274
  const { flags: FLAGS, logger: LOGGER } = $context;
253
- // -----------
254
- const inSplitMode = !!roots.length || !!offset;
255
- const targetDocumentFile = Path.join($config.LAYOUT.PUBLIC_DIR, offset, 'index.html');
256
- // For when we're in split mode
257
- const outfile_theWebfloClient = $config.CLIENT.filename.replace(/\.js$/, '.webflo.js');
258
- const outfile_theWebfloClientPublic = Path.join($config.CLIENT.public_base_url, outfile_theWebfloClient);
259
- // For when we're monolith mode
260
- const outfile_mainBuild = Path.join(offset, $config.CLIENT.filename);
261
- let publicBaseUrl = $config.CLIENT.public_base_url;
262
- if (FLAGS.outdir) {
263
- publicBaseUrl = '/' + Path.relative($config.LAYOUT.PUBLIC_DIR, FLAGS.outdir);
264
- }
265
- const outfile_mainBuildPublic = Path.join(publicBaseUrl, outfile_mainBuild);
266
- // The source code
275
+
276
+ const inSplitMode = !!bootstrap.$roots.length || !!bootstrap.offset;
277
+
278
+ const targetDocumentFile = Path.join(FLAGS.outdir || bootstrap.outdir, 'index.html');
279
+ const publicBaseUrl = bootstrap.config.CLIENT.public_base_url;
280
+
281
+ const sharedBuild_filename = bootstrap.config.CLIENT.filename.replace(/\.js$/, '.webflo.js');
282
+ const outfile_sharedBuild = Path.join(bootstrap.config.LAYOUT.PUBLIC_DIR, sharedBuild_filename);
283
+ const outfile_sharedBuildPublic = Path.join(publicBaseUrl, sharedBuild_filename);
284
+
285
+ const outfile_clientBuild = Path.join(FLAGS.outdir || bootstrap.outdir, bootstrap.config.CLIENT.filename);
286
+ const outfile_clientBuildPublic = Path.join(publicBaseUrl, Path.relative(bootstrap.config.LAYOUT.PUBLIC_DIR, outfile_clientBuild));
287
+
267
288
  const $source = { imports: {}, code: [] };
268
289
  const embeds = { all: [], current: [] };
269
- // -----------
270
- // 1. Derive params
271
- const configExport = structuredClone({ CLIENT: $config.CLIENT, ENV: $config.ENV });
272
- if ($config.CLIENT.capabilities?.service_worker === true) {
273
- configExport.CLIENT.capabilities.service_worker = {
274
- filename: Path.join(publicBaseUrl.replace(/^\//, ''), $config.WORKER.filename),
275
- scope: $config.WORKER.scope
290
+
291
+ const configExport = structuredClone({ ENV: bootstrap.config.ENV, CLIENT: bootstrap.config.CLIENT, WORKER: {} });
292
+ if (bootstrap.config.CLIENT.capabilities?.service_worker === true) {
293
+ const outfile_workerBuild = Path.join(FLAGS.outdir || bootstrap.outdir, bootstrap.config.WORKER.filename);
294
+ const outfile_workerBuildPublic = Path.join(publicBaseUrl, Path.relative(bootstrap.config.LAYOUT.PUBLIC_DIR, outfile_workerBuild));
295
+ configExport.WORKER = {
296
+ filename: outfile_workerBuildPublic,
297
+ scope: bootstrap.config.WORKER.scope
276
298
  };
277
299
  }
278
- // 2. Add the Webflo Runtime
300
+
279
301
  const outfiles = [];
280
302
  if (inSplitMode) {
281
- if (!offset) {
282
- // We're building the Webflo client as a standalone script
303
+ if (!bootstrap.offset) {
283
304
  LOGGER?.log(LOGGER.style.keyword(`---`));
284
305
  LOGGER?.log(`[SPLIT_MODE] Base Build`);
285
306
  LOGGER?.log(LOGGER.style.keyword(`---`));
@@ -290,79 +311,64 @@ async function generateClientScript({ $context, $config, offset = '', roots = []
290
311
  $context,
291
312
  $source: $$source,
292
313
  which: 'client',
293
- outfile: Path.join($config.LAYOUT.PUBLIC_DIR, outfile_theWebfloClient),
314
+ outfile: outfile_sharedBuild,
294
315
  asModule: true
295
316
  });
296
317
  outfiles.push(..._outfiles);
297
318
  }
298
319
  if (FLAGS['auto-embed']) {
299
- embeds.current.push(outfile_theWebfloClientPublic);
300
- embeds.current.push(outfile_mainBuildPublic);
320
+ embeds.current.push(outfile_sharedBuildPublic);
321
+ embeds.current.push(outfile_clientBuildPublic);
301
322
  }
302
323
  } else {
303
- // We're building the Webflo client as part of the main script
304
324
  writeImportWebflo($source, 'client');
305
325
  if (FLAGS['auto-embed']) {
306
- embeds.current.push(outfile_mainBuildPublic);
326
+ embeds.current.push(outfile_clientBuildPublic);
307
327
  }
308
328
  }
309
- // 3. Write the body and bundle
329
+
310
330
  writeScriptBody({
311
331
  $context,
312
- $config,
313
332
  $source,
314
- which: 'client',
315
- offset,
316
- roots,
333
+ bootstrap,
317
334
  configExport,
335
+ which: 'client',
318
336
  ...restParams
319
337
  });
320
- // 4. Bundle
338
+
321
339
  const _outfiles = await bundleScript({
322
340
  $context,
323
341
  $source,
324
342
  which: 'client',
325
- outfile: Path.join(FLAGS.outdir || $config.LAYOUT.PUBLIC_DIR, outfile_mainBuild),
343
+ outfile: outfile_clientBuild,
326
344
  asModule: true,
327
345
  ...restParams
328
346
  });
329
347
  outfiles.push(..._outfiles);
330
- // 4. Embed/unembed
331
- embeds.all.push(outfile_theWebfloClientPublic);
332
- embeds.all.push(outfile_mainBuildPublic);
348
+
349
+ embeds.all.push(outfile_sharedBuildPublic);
350
+ embeds.all.push(outfile_clientBuildPublic);
333
351
  handleEmbeds($context, embeds, targetDocumentFile);
334
- // -----------
335
- if (FLAGS.recursive && roots.length) {
336
- const $roots = roots.slice(0);
337
- const _outfiles = await generateClientScript({
338
- $context,
339
- $config,
340
- offset: $roots.shift(),
341
- roots: $roots,
342
- ...restParams
343
- });
344
- return outfiles.concat(_outfiles);
345
- }
352
+
346
353
  return outfiles;
347
354
  }
348
355
 
349
- async function generateWorkerScript({ $context, $config, offset = '', roots = [], ...restParams }) {
356
+ async function generateWorkerScript({ $context, bootstrap, ...restParams }) {
350
357
  const { flags: FLAGS } = $context;
351
- // -----------
352
- const outfile_mainBuild = Path.join(offset, $config.WORKER.filename);
358
+
353
359
  const $source = { imports: {}, code: [] };
354
- // -----------
355
- // 1. Derive params
356
- const configExport = structuredClone({ WORKER: $config.WORKER, ENV: $config.ENV });
357
- if ($config.CLIENT.capabilities?.webpush === true) {
358
- configExport.WORKER.capabilities = {
360
+
361
+ const outfile_workerBuild = Path.join(FLAGS.outdir || bootstrap.outdir, bootstrap.config.WORKER.filename);
362
+
363
+ const configExport = structuredClone({ ENV: bootstrap.config.ENV, CLIENT: { capabilities: {} }, WORKER: bootstrap.config.WORKER });
364
+ if (bootstrap.config.CLIENT.capabilities?.webpush === true) {
365
+ configExport.CLIENT.capabilities = {
359
366
  webpush: true
360
367
  };
361
368
  }
362
- // Fetching strategies
369
+
363
370
  for (const strategy of ['cache_first_urls', 'cache_only_urls']) {
364
371
  if (configExport.WORKER[strategy].length) {
365
- // Separate URLs from patterns
366
372
  const [urls, patterns] = configExport.WORKER[strategy].reduce(([urls, patterns], url) => {
367
373
  const patternInstance = new URLPattern(url, 'http://localhost');
368
374
  const isPattern = patternInstance.isPattern();
@@ -371,20 +377,17 @@ async function generateWorkerScript({ $context, $config, offset = '', roots = []
371
377
  }
372
378
  return isPattern ? [urls, patterns.concat(patternInstance)] : [urls.concat(url), patterns];
373
379
  }, [[], []]);
374
- // Resolve patterns
375
380
  if (patterns.length) {
376
- // List all files
377
381
  function scanDir(dir) {
378
382
  Fs.readdirSync(dir).reduce((result, f) => {
379
383
  const resource = Path.join(dir, f);
380
384
  if (f.startsWith('.')) return result;
381
385
  return result.concat(
382
- Fs.statSync(resource).isDirectory() ? scanDir(resource) : '/' + Path.relative($config.LAYOUT.PUBLIC_DIR, resource)
386
+ Fs.statSync(resource).isDirectory() ? scanDir(resource) : '/' + Path.relative(bootstrap.config.LAYOUT.PUBLIC_DIR, resource)
383
387
  );
384
388
  }, []);
385
389
  }
386
- const files = scanDir($config.LAYOUT.PUBLIC_DIR);
387
- // Resolve patterns from files
390
+ const files = scanDir(bootstrap.config.LAYOUT.PUBLIC_DIR);
388
391
  configExport.WORKER[strategy] = patterns.reduce((all, pattern) => {
389
392
  const matchedFiles = files.filter((file) => pattern.test(file, 'http://localhost'));
390
393
  if (matchedFiles.length) return all.concat(matchedFiles);
@@ -393,77 +396,80 @@ async function generateWorkerScript({ $context, $config, offset = '', roots = []
393
396
  }
394
397
  }
395
398
  }
396
- // 2. Add the Webflo Runtime
399
+
397
400
  writeImportWebflo($source, 'worker');
398
- // 3. Write the body and bundle
399
401
  writeScriptBody({
400
402
  $context,
401
- $config,
402
403
  $source,
403
- which: 'worker',
404
- offset,
405
- roots,
404
+ bootstrap,
406
405
  configExport,
406
+ which: 'worker',
407
407
  ...restParams
408
408
  });
409
- // 4. Bundle
410
- const outfiles = await bundleScript({
409
+
410
+ return await bundleScript({
411
411
  $context,
412
412
  $source,
413
413
  which: 'worker',
414
- outfile: Path.join(FLAGS.outdir || $config.LAYOUT.PUBLIC_DIR, outfile_mainBuild),
415
- asModule: false,
414
+ outfile: outfile_workerBuild,
415
+ asModule: true,
416
+ ...restParams
417
+ });
418
+ }
419
+
420
+ async function generateServerScript({ $context, bootstrap, ...restParams }) {
421
+ const $source = { imports: {}, code: [] };
422
+ const outfile_serverBuild = Path.join(bootstrap.outdir, 'app.js'); // Must not consult FLAGS.outdir
423
+
424
+ writeImportWebflo($source, 'server');
425
+ writeScriptBody({
426
+ $context,
427
+ $source,
428
+ bootstrap,
429
+ configExport: bootstrap.config,
430
+ which: 'server',
431
+ ...restParams
432
+ });
433
+
434
+ return await bundleScript({
435
+ $context,
436
+ $source,
437
+ which: 'server',
438
+ outfile: outfile_serverBuild,
439
+ asModule: true,
416
440
  ...restParams
417
441
  });
418
- // -----------
419
- if (FLAGS.recursive && roots.length) {
420
- const $roots = roots.slice(0);
421
- const _outfiles = await generateWorkerScript({
422
- $context,
423
- $config,
424
- offset: $roots.shift(),
425
- roots: $roots,
426
- ...restParams
427
- });
428
- return outfiles.concat(_outfiles);
429
- }
430
- return outfiles;
431
442
  }
432
443
 
433
- export async function generate({ client = true, worker = true, buildParams = {} } = {}) {
444
+ export async function build() {
434
445
  const $context = this;
435
- if (!($context instanceof Context)) {
436
- throw new Error(`The "this" context must be a Webflo Context object.`);
446
+ if (!($context instanceof CLIContext)) {
447
+ throw new Error(`The "this" context must be a Webflo CLIContext object.`);
437
448
  }
438
- // Resolve common details
439
- const $config = {
440
- LAYOUT: await readLayoutConfig($context),
441
- ENV: { ...await readEnvConfig($context), data: {} },
442
- CLIENT: await readClientConfig($context),
443
- WORKER: await readWorkerConfig($context),
444
- };
445
- if ($config.CLIENT.copy_public_variables) {
446
- const publicEnvPattern = /(?:^|_)PUBLIC(?:_|$)/;
447
- for (const key in process.env) {
448
- if (publicEnvPattern.test(key)) {
449
- $config.ENV.data[key] = process.env[key];
450
- }
451
- }
452
- }
453
- // Build
449
+
450
+ const buildParams = {};
454
451
  const outfiles = [];
455
- if (client) {
456
- const documentRoots = scanRoots($config.LAYOUT.PUBLIC_DIR, 'index.html');
457
- const _outfiles = await generateClientScript({ $context, $config, roots: documentRoots, buildParams });
452
+ if ($context.flags.client) {
453
+ const bootstrap = await clientBootstrap($context);
454
+ const _outfiles = await generateClientScript({ $context, bootstrap, buildParams });
455
+ outfiles.push(..._outfiles);
456
+ }
457
+
458
+ if ($context.flags.worker) {
459
+ const bootstrap = await workerBootstrap($context);
460
+ const _outfiles = await generateWorkerScript({ $context, bootstrap, buildParams });
458
461
  outfiles.push(..._outfiles);
459
462
  }
460
- if (worker) {
461
- const applicationRoots = scanRoots($config.LAYOUT.PUBLIC_DIR, 'manifest.json');
462
- const _outfiles = await generateWorkerScript({ $context, $config, roots: applicationRoots, buildParams });
463
+
464
+ if (false) { // TODO: WebfloServer needs to be buildable first
465
+ const bootstrap = await serverBootstrap($context);
466
+ const _outfiles = await generateServerScript({ $context, bootstrap, buildParams });
463
467
  outfiles.push(..._outfiles);
464
468
  }
469
+
465
470
  if (process.send) {
466
471
  process.send({ outfiles });
467
472
  }
473
+
468
474
  return { outfiles };
469
475
  }
package/src/index.js CHANGED
@@ -3,12 +3,14 @@ import * as deployment from './deployment-pi/index.js';
3
3
  import * as runtime from './runtime-pi/index.js';
4
4
  import * as services from './services-pi/index.js';
5
5
  import * as starter from './init-pi/index.js';
6
+ import * as build from './build-pi/index.js';
6
7
 
7
- export { Context } from './Context.js';
8
+ export { CLIContext } from './CLIContext.js';
8
9
  export {
9
10
  config,
10
11
  deployment,
11
12
  runtime,
12
13
  services,
13
14
  starter,
15
+ build,
14
16
  }
@@ -4,7 +4,8 @@ import Path from 'path';
4
4
  import { exec } from 'child_process';
5
5
  import { _toTitle } from '@webqit/util/str/index.js';
6
6
  import { readInitConfig } from '../deployment-pi/util.js';
7
- import { Context } from '../Context.js';
7
+ import { CLIContext } from '../CLIContext.js';
8
+ import * as deployment from '../deployment-pi/index.js';
8
9
 
9
10
  export const desc = {
10
11
  init: 'Generate a preset Webflo starter app.',
@@ -12,9 +13,11 @@ export const desc = {
12
13
 
13
14
  export async function init(projectName = 'my-webflo-app', projectTitle = '', projectDescription = '') {
14
15
  const $context = this;
16
+ //middlewares: [ deployment.origins.webhook ],
15
17
 
16
- if (!($context instanceof Context)) {
17
- throw new Error(`The "this" context must be a Webflo Context object.`);
18
+
19
+ if (!($context instanceof CLIContext)) {
20
+ throw new Error(`The "this" context must be a Webflo CLIContext object.`);
18
21
  }
19
22
 
20
23
  const { flags: FLAGS, logger: LOGGER } = $context;
@@ -37,7 +40,7 @@ export async function init(projectName = 'my-webflo-app', projectTitle = '', pro
37
40
  process.exit(1);
38
41
  }
39
42
 
40
- LOGGER?.log(LOGGER.style.keyword(`> `) + `Initializing your webflo app: "${projectName}" using template "${template}"...\n`);
43
+ LOGGER?.log(LOGGER.style.keyword(`> `) + `Initializing your webflo app "${projectName}" using template "${template}"...\n`);
41
44
 
42
45
  // 1. Create project dir
43
46
  await Fs2.mkdir(targetDir, { recursive: true });
@@ -0,0 +1,6 @@
1
+ node_modules
2
+ .*
3
+ !/.github
4
+ !/.gitignore
5
+ !/.webqit
6
+ /.webqit/webflo/@runtime
@@ -0,0 +1,15 @@
1
+ {
2
+ "filename": "app.js",
3
+ "public_base_url": "/",
4
+ "copy_public_variables": true,
5
+ "spa_routing": true,
6
+ "capabilities": {
7
+ "service_worker": true,
8
+ "webpush": true,
9
+ "custom_install": true,
10
+ "exposed": [
11
+ "display-mode",
12
+ "notifications"
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "PUBLIC_DIR": "./public",
3
+ "VIEWS_DIR": "./app",
4
+ "SERVER_DIR": "./app",
5
+ "CLIENT_DIR": "./app",
6
+ "WORKER_DIR": "./app"
7
+ }
@@ -5,9 +5,9 @@
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build:html": "oohtml bundle --recursive --outdir=public/assets --auto-embed=app",
8
- "build:js": "webflo generate::client --recursive --outdir=public/assets --auto-embed",
8
+ "build:js": "webflo build --client --worker --server --recursive --outdir=public/assets --auto-embed",
9
9
  "build": "npm run build:html && npm run build:js",
10
- "dev": "webflo start --dev --build-sensitivity=2",
10
+ "dev": "webflo start --dev --build-sensitivity=1",
11
11
  "start": "webflo start"
12
12
  },
13
13
  "dependencies": {
@@ -10,12 +10,12 @@
10
10
  "scope": "/",
11
11
  "icons": [
12
12
  {
13
- "src": "/assets/...",
13
+ "src": "/assets/logo.png",
14
14
  "sizes": "192x192",
15
15
  "type": "image/png"
16
16
  },
17
17
  {
18
- "src": "/assets/...",
18
+ "src": "/assets/logo.png",
19
19
  "sizes": "512x512",
20
20
  "type": "image/png",
21
21
  "purpose": "maskable"
@@ -0,0 +1,6 @@
1
+ node_modules
2
+ .*
3
+ !/.github
4
+ !/.gitignore
5
+ !/.webqit
6
+ /.webqit/webflo/@runtime
@@ -0,0 +1,12 @@
1
+ {
2
+ "filename": "app.js",
3
+ "public_base_url": "/",
4
+ "copy_public_variables": true,
5
+ "spa_routing": true,
6
+ "capabilities": {
7
+ "service_worker": false,
8
+ "webpush": false,
9
+ "custom_install": false,
10
+ "exposed": []
11
+ }
12
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "PUBLIC_DIR": "./public",
3
+ "VIEWS_DIR": "./app",
4
+ "SERVER_DIR": "./app",
5
+ "CLIENT_DIR": "./app",
6
+ "WORKER_DIR": "./app"
7
+ }
@@ -5,9 +5,9 @@
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build:html": "oohtml bundle --recursive --outdir=public/assets --auto-embed=app",
8
- "build:js": "webflo generate::client --recursive --outdir=public/assets --auto-embed",
8
+ "build:js": "webflo build --client --worker --server --recursive --outdir=public/assets --auto-embed",
9
9
  "build": "npm run build:html && npm run build:js",
10
- "dev": "webflo start --dev --build-sensitivity=2",
10
+ "dev": "webflo start --dev --build-sensitivity=1",
11
11
  "start": "webflo start"
12
12
  },
13
13
  "dependencies": {
@@ -0,0 +1,38 @@
1
+ import { CLIContext } from '../CLIContext.js';
2
+
3
+ export class AppBootstrap {
4
+
5
+ #json;
6
+ #cx;
7
+ #init;
8
+
9
+ constructor(json = {}) {
10
+ this.#json = json;
11
+ this.#cx = new CLIContext(json.cx || {});
12
+ this.#init = { ...(json.init || {})};
13
+ }
14
+
15
+ // cx
16
+ get cx() { return this.#cx; }
17
+
18
+ // init
19
+ get init() { return this.#init; }
20
+
21
+ // $root
22
+ get $root() { return this.#json.offset || ''; }
23
+
24
+ // $roots
25
+ get $roots() { return this.#json.$roots || []; }
26
+
27
+ // $sparoots
28
+ get $sparoots() { return this.#json.$sparoots || []; }
29
+
30
+ // config
31
+ get config() { return this.#json.config || {}; }
32
+
33
+ // routes
34
+ get routes() { return this.#json.routes || {}; }
35
+
36
+ // middlewares
37
+ get middlewares() { return this.#json.middlewares || []; }
38
+ }