underpost 2.7.7 → 2.7.81

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 (39) hide show
  1. package/.github/workflows/ghpkg.yml +75 -0
  2. package/.github/workflows/publish.yml +21 -3
  3. package/.github/workflows/pwa-microservices-template.test.yml +30 -0
  4. package/bin/deploy.js +13 -2
  5. package/bin/index.js +1 -1
  6. package/docker-compose.yml +1 -1
  7. package/package.json +1 -1
  8. package/src/client/components/core/Css.js +0 -222
  9. package/src/client/components/core/Docs.js +1 -1
  10. package/src/client/components/core/Translate.js +6 -2
  11. package/src/client/components/core/Worker.js +14 -4
  12. package/src/client/services/default/default.management.js +118 -120
  13. package/src/client/ssr/Render.js +224 -3
  14. package/src/client/ssr/common/Alert.js +75 -0
  15. package/src/client/ssr/common/SsrCore.js +91 -0
  16. package/src/client/ssr/common/Translate.js +26 -0
  17. package/src/client/ssr/common/Worker.js +28 -0
  18. package/src/client/ssr/{body-components → components/body}/CacheControl.js +1 -1
  19. package/src/client/ssr/{body-components → components/body}/DefaultSplashScreen.js +15 -4
  20. package/src/client/ssr/offline/default.index.js +31 -0
  21. package/src/client/ssr/pages/404.js +12 -0
  22. package/src/client/ssr/pages/500.js +12 -0
  23. package/src/client/sw/default.sw.js +11 -9
  24. package/src/mailer/EmailRender.js +1 -1
  25. package/src/server/client-build-live.js +6 -0
  26. package/src/server/client-build.js +95 -25
  27. package/src/server/client-formatted.js +11 -1
  28. package/src/server/client-icons.js +1 -1
  29. package/src/server/conf.js +9 -7
  30. package/src/server/runtime.js +24 -7
  31. package/.github/workflows/test.yml +0 -80
  32. package/src/client/ssr/head-components/Microdata.js +0 -11
  33. package/src/client/ssr/head-components/Production.js +0 -1
  34. package/src/client/ssr/head-components/Seo.js +0 -14
  35. /package/src/client/ssr/{email-components → components/email}/DefaultRecoverEmail.js +0 -0
  36. /package/src/client/ssr/{email-components → components/email}/DefaultVerifyEmail.js +0 -0
  37. /package/src/client/ssr/{head-components → components/head}/Css.js +0 -0
  38. /package/src/client/ssr/{head-components → components/head}/DefaultScripts.js +0 -0
  39. /package/src/client/ssr/{head-components → components/head}/PwaDefault.js +0 -0
@@ -0,0 +1,91 @@
1
+ const s = (el) => document.querySelector(el);
2
+
3
+ const append = (el, html) => s(el).insertAdjacentHTML('beforeend', html);
4
+
5
+ const htmls = (el, html) => (s(el).innerHTML = html);
6
+
7
+ const s4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
8
+
9
+ const range = (start, end) => {
10
+ return end < start
11
+ ? range(end, start).reverse()
12
+ : Array.apply(0, Array(end - start + 1)).map((element, index) => index + start);
13
+ };
14
+
15
+ const timer = (ms) => new Promise((res) => setTimeout(res, ms));
16
+
17
+ const newInstance = (obj) => {
18
+ // structuredClone() 2022 ES6 feature
19
+ try {
20
+ return JSON.parse(JSON.stringify(obj));
21
+ } catch (error) {
22
+ return { error: error.message };
23
+ }
24
+ };
25
+
26
+ const fullScreenIn = () => {
27
+ const elem = document.documentElement;
28
+ if (elem.requestFullscreen) {
29
+ elem.requestFullscreen();
30
+ } else if (elem.mozRequestFullScreen) {
31
+ /* Firefox */
32
+ elem.mozRequestFullScreen();
33
+ } else if (elem.webkitRequestFullscreen) {
34
+ /* Chrome, Safari & Opera */
35
+ elem.webkitRequestFullscreen();
36
+ } else if (elem.msRequestFullscreen) {
37
+ /* IE/Edge */
38
+ elem = window.top.document.body; //To break out of frame in IE
39
+ elem.msRequestFullscreen();
40
+ }
41
+ };
42
+
43
+ const borderChar = (px, color, selectors) => {
44
+ if (selectors) {
45
+ return selectors
46
+ .map(
47
+ (selector) => html`
48
+ <style>
49
+ ${selector} {
50
+ text-shadow: ${px}px -${px}px ${px}px ${color}, -${px}px ${px}px ${px}px ${color},
51
+ -${px}px -${px}px ${px}px ${color}, ${px}px ${px}px ${px}px ${color};
52
+ }
53
+ </style>
54
+ `,
55
+ )
56
+ .join('');
57
+ }
58
+ return html`
59
+ text-shadow: ${px}px -${px}px ${px}px ${color}, -${px}px ${px}px ${px}px ${color}, -${px}px -${px}px ${px}px
60
+ ${color}, ${px}px ${px}px ${px}px ${color};
61
+ `;
62
+ };
63
+ const getLang = () =>
64
+ localStorage.getItem('lang') ? localStorage.getItem('lang') : navigator.language || navigator.userLanguage;
65
+
66
+ const loggerFactory = (meta) => {
67
+ meta = meta.url.split('/').pop();
68
+ const types = ['error', 'warn', 'info', 'debug'];
69
+ const logger = {
70
+ log: function (type, args) {
71
+ if (location.hostname !== 'localhost' && console.log() !== null) {
72
+ console.log = () => null;
73
+ console.error = () => null;
74
+ console.info = () => null;
75
+ console.warn = () => null;
76
+ }
77
+ return location.hostname === 'localhost'
78
+ ? console[type](`[${meta}] ${new Date().toISOString()} ${type}:`, ...args)
79
+ : null;
80
+ },
81
+ };
82
+ types.map(
83
+ (type) =>
84
+ (logger[type] = function (...args) {
85
+ return this.log(type, args);
86
+ }),
87
+ );
88
+ return logger;
89
+ };
90
+
91
+ export { getLang, s, append, s4, range, timer, htmls, newInstance, fullScreenIn, borderChar, loggerFactory };
@@ -0,0 +1,26 @@
1
+ const TranslateCore = {
2
+ ['server-maintenance']: {
3
+ en: "The server is under maintenance <br> we'll be back soon.",
4
+ es: 'El servidor está en mantenimiento <br> volveremos pronto.',
5
+ },
6
+ ['no-internet-connection']: {
7
+ en: 'No internet connection <br> verify your network',
8
+ es: 'Sin conexión a internet <br> verifica tu red',
9
+ },
10
+ ['page-not-found']: {
11
+ en: 'Page not found',
12
+ es: 'Página no encontrada',
13
+ },
14
+ ['page-broken']: {
15
+ es: 'Algo salio mal',
16
+ en: 'Something went wrong',
17
+ },
18
+ ['back']: {
19
+ en: 'Back to <br> homepage',
20
+ es: 'Volver a <br> la pagina principal',
21
+ },
22
+ };
23
+
24
+ const Translate = (key) => TranslateCore[key][getLang().match('es') ? 'es' : 'en'];
25
+
26
+ export { Translate };
@@ -0,0 +1,28 @@
1
+ const Worker = {
2
+ instance: async function (options = { render: async () => {} }) {
3
+ window.serviceWorkerReady = true;
4
+ append(
5
+ 'body',
6
+ html`
7
+ <style>
8
+ body {
9
+ background-color: #dcdcdc;
10
+ color: #303030;
11
+ font-family: arial;
12
+ font-size: 16px;
13
+ }
14
+ .page-render {
15
+ min-height: 100vh;
16
+ }
17
+ a {
18
+ color: #000000;
19
+ }
20
+ </style>
21
+ <div class="in page-render"></div>
22
+ `,
23
+ );
24
+ await options.render();
25
+ },
26
+ };
27
+
28
+ export { Worker };
@@ -109,6 +109,6 @@ SrrComponent = ({ ttiLoadTimeLimit }) => {
109
109
  const CacheControl = ${CacheControl};
110
110
  CacheControl({ ttiLoadTimeLimit: ${ttiLoadTimeLimit ? ttiLoadTimeLimit : 1000 * 70 * 1} });
111
111
  </script>
112
- <div class="clean-cache-container">v2.7.7</div>
112
+ <div class="clean-cache-container">v2.7.81</div>
113
113
  `;
114
114
  };
@@ -1,8 +1,19 @@
1
- SrrComponent = ({ base64BackgroundImage }) => html`
1
+ SrrComponent = ({ base64BackgroundImage, metadata }) => html`
2
+ ${base64BackgroundImage
3
+ ? html`<style>
4
+ .ssr-background-image {
5
+ background-image: url('${base64BackgroundImage}');
6
+ }
7
+ </style>`
8
+ : metadata?.themeColor
9
+ ? html`<style>
10
+ .ssr-background-image {
11
+ background: ${metadata.themeColor};
12
+ }
13
+ </style>`
14
+ : ''}
15
+
2
16
  <style>
3
- .ssr-background-image {
4
- background-image: url('${base64BackgroundImage}');
5
- }
6
17
  .ssr-top-bar {
7
18
  background: #ffffff;
8
19
  height: 100px;
@@ -0,0 +1,31 @@
1
+ import { htmls, loggerFactory } from '../common/SsrCore.js';
2
+ import { Alert } from '../common/Alert.js';
3
+ import { Translate } from '../common/Translate.js';
4
+ import { Worker } from '../common/Worker.js';
5
+ /*imports*/
6
+
7
+ const logger = loggerFactory({ url: '/offline.js' });
8
+
9
+ window.onload = () =>
10
+ Worker.instance({
11
+ render: async () => {
12
+ window.ononline = async () => {
13
+ location.href = '/';
14
+ };
15
+ window.onoffline = async () => {
16
+ htmls(`.page-render`, html`${await Alert.noInternet({ Translate })}`);
17
+ };
18
+ try {
19
+ if (navigator.onLine) {
20
+ const maintenance = await fetch(location.origin + '/favicon.ico');
21
+ if (maintenance.status !== 200) {
22
+ htmls(`.page-render`, html`${await Alert.maintenance({ Translate })}`);
23
+ } else window.ononline();
24
+ }
25
+ throw new Error(`no internet connection`);
26
+ } catch (error) {
27
+ logger.error(error);
28
+ window.onoffline();
29
+ }
30
+ },
31
+ });
@@ -0,0 +1,12 @@
1
+ import { e404 } from '../common/Alert.js';
2
+ import { append } from '../common/SsrCore.js';
3
+ import { Translate } from '../common/Translate.js';
4
+ import { Worker } from '../common/Worker.js';
5
+ /*imports*/
6
+
7
+ window.onload = () =>
8
+ Worker.instance({
9
+ render: async () => {
10
+ append('.page-render', await e404({ Translate }));
11
+ },
12
+ });
@@ -0,0 +1,12 @@
1
+ import { e500 } from '../common/Alert.js';
2
+ import { append } from '../common/SsrCore.js';
3
+ import { Translate } from '../common/Translate.js';
4
+ import { Worker } from '../common/Worker.js';
5
+ /*imports*/
6
+
7
+ window.onload = () =>
8
+ Worker.instance({
9
+ render: async () => {
10
+ append('.page-render', await e500({ Translate }));
11
+ },
12
+ });
@@ -179,15 +179,17 @@ self.addEventListener('fetch', (event) => {
179
179
  }
180
180
 
181
181
  logger.error('Fetch failed; returning offline page instead.', { error, path });
182
-
183
- // const cache = await caches.open(CACHE_NAME);
184
- // const cachedResponse = await cache.match(OFFLINE_URL);
185
- // return cachedResponse;
186
-
187
- const response = new Response(JSON.stringify({ status: 'error', message: 'offline test response' }));
188
- // response.status = 200;
189
- response.headers.set('Content-Type', 'application/json');
190
- return response;
182
+ try {
183
+ const cache = await caches.open('/offline.html');
184
+ const cachedResponse = await cache.match('/offline.html');
185
+ return cachedResponse;
186
+ } catch (error) {
187
+ logger.error('Error opening cache for offline page', { error, path });
188
+ const response = new Response(JSON.stringify({ status: 'error', message: 'offline test response' }));
189
+ // response.status = 200;
190
+ response.headers.set('Content-Type', 'application/json');
191
+ return response;
192
+ }
191
193
  }
192
194
  })(),
193
195
  );
@@ -59,7 +59,7 @@ const EmailRender = {
59
59
  for (const templateKey of Object.keys(options.templates)) {
60
60
  const ssrEmailComponent = options.templates[templateKey];
61
61
  let SrrComponent;
62
- eval(await srcFormatted(fs.readFileSync(`./src/client/ssr/email-components/${ssrEmailComponent}.js`, 'utf8')));
62
+ eval(await srcFormatted(fs.readFileSync(`./src/client/ssr/components/email/${ssrEmailComponent}.js`, 'utf8')));
63
63
  templates[templateKey] = SrrComponent(this, options);
64
64
  }
65
65
  return templates;
@@ -71,6 +71,12 @@ const clientLiveBuild = async () => {
71
71
  } else if (srcPath.split('src')[1].startsWith(`\\client\\sw`)) {
72
72
  const publicBuildPath = `./public/${baseHost}/sw.js`;
73
73
  liveClientBuildPaths.push({ srcBuildPath, publicBuildPath });
74
+ } else if (
75
+ srcPath.split('src')[1].startsWith(`\\client\\offline`) &&
76
+ srcPath.split('src')[1].startsWith(`index.js`)
77
+ ) {
78
+ const publicBuildPath = `./public/${baseHost}/offline.js`;
79
+ liveClientBuildPaths.push({ srcBuildPath, publicBuildPath });
74
80
  } else if (srcPath.split('src')[1].startsWith(`\\client`) && srcPath.slice(-9) === '.index.js') {
75
81
  for (const view of views) {
76
82
  const publicBuildPath = `./public/${baseHost}${view.path === '/' ? '' : view.path}/${clientId}.index.js`;
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  import fs from 'fs-extra';
4
- import { srcFormatted, componentFormatted, viewFormatted } from './client-formatted.js';
4
+ import { srcFormatted, componentFormatted, viewFormatted, ssrFactory } from './client-formatted.js';
5
5
  import { loggerFactory } from './logger.js';
6
6
  import { cap, newInstance, orderArrayFromAttrInt, titleFormatted } from '../client/components/core/CommonJs.js';
7
7
  import UglifyJS from 'uglify-js';
@@ -112,6 +112,16 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
112
112
  const enableLiveRebuild =
113
113
  options && options.liveClientBuildPaths && options.liveClientBuildPaths.length > 0 ? true : false;
114
114
 
115
+ // common ssr components
116
+ let jsSsrCommonComponents = '';
117
+ {
118
+ const files = await fs.readdir(`./src/client/ssr/common`);
119
+ for (const relativePath of files)
120
+ jsSsrCommonComponents += await srcFormatted(
121
+ fs.readFileSync(`./src/client/ssr/common/${relativePath}`, 'utf8').split('export')[0],
122
+ );
123
+ }
124
+
115
125
  let currentPort = parseInt(process.env.PORT) + 1;
116
126
  for (const host of Object.keys(confServer)) {
117
127
  const paths = orderArrayFromAttrInt(Object.keys(confServer[host]), 'length', 'asc');
@@ -274,18 +284,14 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
274
284
 
275
285
  const buildId = `${client}.index`;
276
286
  const siteMapLinks = [];
287
+ let Render = () => '';
288
+ eval(await srcFormatted(fs.readFileSync(`./src/client/ssr/Render.js`, 'utf8')));
277
289
 
278
290
  if (views) {
279
- // build service worker
280
- if (path === '/') {
281
- const jsSrcPath = fs.existsSync(`./src/client/sw/${publicClientId}.sw.js`)
282
- ? `./src/client/sw/${publicClientId}.sw.js`
283
- : `./src/client/sw/default.sw.js`;
284
-
285
- const jsPublicPath = `${rootClientPath}/sw.js`;
286
-
291
+ const buildJsSrcPage = async (jsSrcPath, jsPublicPath) => {
287
292
  if (!(enableLiveRebuild && !options.liveClientBuildPaths.find((p) => p.srcBuildPath === jsSrcPath))) {
288
- const jsSrc = viewFormatted(await srcFormatted(fs.readFileSync(jsSrcPath, 'utf8')), dists, path, baseHost);
293
+ let jsSrc = viewFormatted(await srcFormatted(fs.readFileSync(jsSrcPath, 'utf8')), dists, path, baseHost);
294
+ if (jsSrc.split('/*imports*/')[1]) jsSrc = jsSrc.split('/*imports*/')[1];
289
295
 
290
296
  fs.writeFileSync(
291
297
  jsPublicPath,
@@ -293,7 +299,76 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
293
299
  'utf8',
294
300
  );
295
301
  }
302
+ };
303
+
304
+ if (path === '/') {
305
+ // service woker
306
+ await buildJsSrcPage(
307
+ fs.existsSync(`./src/client/sw/${publicClientId}.sw.js`)
308
+ ? `./src/client/sw/${publicClientId}.sw.js`
309
+ : `./src/client/sw/default.sw.js`,
310
+ `${rootClientPath}/sw.js`,
311
+ );
312
+ }
313
+
314
+ // offline html
315
+ {
316
+ await buildJsSrcPage(
317
+ fs.existsSync(`./src/client/ssr/offline/${publicClientId}.index.js`)
318
+ ? `./src/client/ssr/offline/${publicClientId}.index.js`
319
+ : `./src/client/ssr/offline/default.index.js`,
320
+ `${rootClientPath}/offline.js`,
321
+ );
322
+
323
+ const htmlSrc = Render({
324
+ title: metadata?.title ? metadata.title : cap(client),
325
+ ssrPath: '/',
326
+ ssrHeadComponents: '',
327
+ ssrBodyComponents: '',
328
+ baseSsrLib: jsSsrCommonComponents + fs.readFileSync(`${rootClientPath}/offline.js`, 'utf8'),
329
+ });
330
+
331
+ fs.writeFileSync(
332
+ `${rootClientPath}offline.html`,
333
+ minifyBuild || process.env.NODE_ENV === 'production'
334
+ ? await minify(htmlSrc, {
335
+ minifyCSS: true,
336
+ minifyJS: true,
337
+ collapseBooleanAttributes: true,
338
+ collapseInlineTagWhitespace: true,
339
+ collapseWhitespace: true,
340
+ })
341
+ : htmlSrc,
342
+ 'utf8',
343
+ );
296
344
  }
345
+ // ssr pages
346
+ for (const page of await fs.readdir('./src/client/ssr/pages')) {
347
+ await buildJsSrcPage(`./src/client/ssr/pages/${page}`, `${rootClientPath}/${page}`);
348
+
349
+ const htmlSrc = Render({
350
+ title: metadata?.title ? metadata.title : cap(client),
351
+ ssrPath: '/',
352
+ ssrHeadComponents: '',
353
+ ssrBodyComponents: '',
354
+ baseSsrLib: jsSsrCommonComponents + fs.readFileSync(`${rootClientPath}/${page}`, 'utf8'),
355
+ });
356
+
357
+ fs.writeFileSync(
358
+ `${rootClientPath}${page.slice(0, -3)}.html`,
359
+ minifyBuild || process.env.NODE_ENV === 'production'
360
+ ? await minify(htmlSrc, {
361
+ minifyCSS: true,
362
+ minifyJS: true,
363
+ collapseBooleanAttributes: true,
364
+ collapseInlineTagWhitespace: true,
365
+ collapseWhitespace: true,
366
+ })
367
+ : htmlSrc,
368
+ 'utf8',
369
+ );
370
+ }
371
+
297
372
  if (
298
373
  !(
299
374
  enableLiveRebuild &&
@@ -345,11 +420,9 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
345
420
  confSSR[view.ssr].head.unshift('Production');
346
421
 
347
422
  for (const ssrHeadComponent of confSSR[view.ssr].head) {
348
- let SrrComponent;
349
- eval(
350
- await srcFormatted(
351
- fs.readFileSync(`./src/client/ssr/head-components/${ssrHeadComponent}.js`, 'utf8'),
352
- ),
423
+ const SrrComponent = await ssrFactory(
424
+ `./src/client/ssr/components/head/${ssrHeadComponent}.js`,
425
+ jsSsrCommonComponents,
353
426
  );
354
427
 
355
428
  switch (ssrHeadComponent) {
@@ -431,11 +504,9 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
431
504
  }
432
505
 
433
506
  for (const ssrBodyComponent of confSSR[view.ssr].body) {
434
- let SrrComponent;
435
- eval(
436
- await srcFormatted(
437
- fs.readFileSync(`./src/client/ssr/body-components/${ssrBodyComponent}.js`, 'utf8'),
438
- ),
507
+ const SrrComponent = await ssrFactory(
508
+ `./src/client/ssr/components/body/${ssrBodyComponent}.js`,
509
+ jsSsrCommonComponents,
439
510
  );
440
511
  switch (ssrBodyComponent) {
441
512
  case 'UnderpostDefaultSplashScreen':
@@ -448,7 +519,10 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
448
519
  .readFileSync(backgroundImage)
449
520
  .toString('base64')}`,
450
521
  });
522
+ break;
451
523
  } else {
524
+ ssrHeadComponents += SrrComponent({ metadata });
525
+ break;
452
526
  const bufferBackgroundImage = await getBufferPngText({
453
527
  text: ' ',
454
528
  textColor: metadata?.themeColor ? metadata.themeColor : '#ececec',
@@ -459,7 +533,6 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
459
533
  base64BackgroundImage: `data:image/png;base64,${bufferBackgroundImage.toString('base64')}`,
460
534
  });
461
535
  }
462
- break;
463
536
 
464
537
  case 'CyberiaSplashScreenLore': {
465
538
  ssrBodyComponents += SrrComponent({
@@ -519,16 +592,13 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
519
592
  }
520
593
  }
521
594
  }
522
-
523
- let Render = () => '';
524
- eval(await srcFormatted(fs.readFileSync(`./src/client/ssr/Render.js`, 'utf8')));
525
-
526
595
  const htmlSrc = Render({
527
596
  title,
528
597
  buildId,
529
598
  ssrPath,
530
599
  ssrHeadComponents,
531
600
  ssrBodyComponents,
601
+ baseSsrLib: jsSsrCommonComponents,
532
602
  });
533
603
 
534
604
  /** @type {import('sitemap').SitemapItem} */
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ import fs from 'fs-extra';
4
+
3
5
  const srcFormatted = (src) =>
4
6
  src
5
7
  .replaceAll(' html`', '`')
@@ -45,4 +47,12 @@ const viewFormatted = (src, dists, proxyPath, baseHost = '') => {
45
47
  return src.replaceAll(`from './`, componentFromFormatted).replaceAll(`from '../`, componentFromFormatted);
46
48
  };
47
49
 
48
- export { srcFormatted, JSONweb, componentFormatted, viewFormatted };
50
+ const ssrFactory = async (componentPath = '', jsSsrCommonComponents) => {
51
+ let SrrComponent = () => {};
52
+ let render = await srcFormatted(fs.readFileSync(componentPath, 'utf8'));
53
+ if (render.split('/*imports*/')[1]) render = render.split('/*imports*/')[1];
54
+ eval(jsSsrCommonComponents + render);
55
+ return SrrComponent;
56
+ };
57
+
58
+ export { srcFormatted, JSONweb, componentFormatted, viewFormatted, ssrFactory };
@@ -140,7 +140,7 @@ const buildIcons = async ({
140
140
  for (const file of response.files)
141
141
  fs.writeFileSync(`./src/client/public/${publicClientId}/${file.name}`, file.contents, 'utf8');
142
142
 
143
- const ssrPath = `./src/client/ssr/head-components/Pwa${getCapVariableName(publicClientId)}.js`;
143
+ const ssrPath = `./src/client/ssr/components/head/Pwa${getCapVariableName(publicClientId)}.js`;
144
144
  if (!fs.existsSync(ssrPath))
145
145
  fs.writeFileSync(ssrPath, 'SrrComponent = () => html`' + response.html.join(`\n`) + '`;', 'utf8');
146
146
  } catch (error) {
@@ -240,8 +240,8 @@ const buildClientSrc = async (
240
240
  }
241
241
 
242
242
  fs.writeFileSync(
243
- `./src/client/ssr/head-components/${toClientVariableName}Scripts.js`,
244
- formattedSrc(fs.readFileSync(`./src/client/ssr/head-components/${fromClientVariableName}Scripts.js`, 'utf8')),
243
+ `./src/client/ssr/components/head/${toClientVariableName}Scripts.js`,
244
+ formattedSrc(fs.readFileSync(`./src/client/ssr/components/head/${fromClientVariableName}Scripts.js`, 'utf8')),
245
245
  'utf8',
246
246
  );
247
247
 
@@ -560,7 +560,7 @@ const validateTemplatePath = (absolutePath = '') => {
560
560
  const confServer = DefaultConf.server[host][path];
561
561
  const confClient = DefaultConf.client[client];
562
562
  const confSsr = DefaultConf.ssr[ssr];
563
- const clients = Object.keys(confClient).concat(['core', 'test']);
563
+ const clients = Object.keys(confClient).concat(['core', 'test', 'default']);
564
564
 
565
565
  if (absolutePath.match('src/api') && !confServer.apis.find((p) => absolutePath.match(`src/api/${p}/`))) {
566
566
  return false;
@@ -584,14 +584,14 @@ const validateTemplatePath = (absolutePath = '') => {
584
584
  return false;
585
585
  }
586
586
  if (
587
- absolutePath.match('src/client/ssr/body-components') &&
588
- !confSsr.body.find((p) => absolutePath.match(`src/client/ssr/body-components/${p}.js`))
587
+ absolutePath.match('src/client/ssr/components/body') &&
588
+ !confSsr.body.find((p) => absolutePath.match(`src/client/ssr/components/body/${p}.js`))
589
589
  ) {
590
590
  return false;
591
591
  }
592
592
  if (
593
- absolutePath.match('src/client/ssr/head-components') &&
594
- !confSsr.head.find((p) => absolutePath.match(`src/client/ssr/head-components/${p}.js`))
593
+ absolutePath.match('src/client/ssr/components/head') &&
594
+ !confSsr.head.find((p) => absolutePath.match(`src/client/ssr/components/head/${p}.js`))
595
595
  ) {
596
596
  return false;
597
597
  }
@@ -599,6 +599,7 @@ const validateTemplatePath = (absolutePath = '') => {
599
599
  if (
600
600
  absolutePath.match('/client') &&
601
601
  absolutePath.match('.index.js') &&
602
+ !absolutePath.match('/offline') &&
602
603
  !clients.find((p) => absolutePath.match(`src/client/${capFirst(p)}.index.js`))
603
604
  ) {
604
605
  return false;
@@ -695,6 +696,7 @@ const execDeploy = async (options = { deployId: 'default' }) => {
695
696
  const deployRun = async (dataDeploy, reset) => {
696
697
  if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
697
698
  if (reset) fs.writeFileSync(`./tmp/runtime-router.json`, '{}', 'utf8');
699
+ await fixDependencies();
698
700
  for (const deploy of dataDeploy) await execDeploy(deploy);
699
701
  const { failed } = await deployTest(dataDeploy);
700
702
  if (failed.length > 0) {
@@ -1,4 +1,4 @@
1
- import fs from 'fs';
1
+ import fs from 'fs-extra';
2
2
  import express from 'express';
3
3
  import cors from 'cors';
4
4
  import dotenv from 'dotenv';
@@ -114,10 +114,10 @@ export PATH=$PATH:/opt/lampp/bin`,
114
114
  Listen ${port}
115
115
 
116
116
  <VirtualHost *:${port}>
117
- DocumentRoot "${directory ? directory.replace(path, '/') : `${getRootDirectory()}${rootHostPath}`}"
117
+ DocumentRoot "${directory ? directory : `${getRootDirectory()}${rootHostPath}`}"
118
118
  ServerName ${host}:${port}
119
119
 
120
- <Directory "${directory ? directory.replace(path, '/') : `${getRootDirectory()}${rootHostPath}`}">
120
+ <Directory "${directory ? directory : `${getRootDirectory()}${rootHostPath}`}">
121
121
  Options Indexes FollowSymLinks MultiViews
122
122
  AllowOverride All
123
123
  Require all granted
@@ -134,6 +134,13 @@ export PATH=$PATH:/opt/lampp/bin`,
134
134
  : ''
135
135
  }
136
136
 
137
+ ErrorDocument 400 ${path === '/' ? '' : path}/400.html
138
+ ErrorDocument 404 ${path === '/' ? '' : path}/400.html
139
+ ErrorDocument 500 ${path === '/' ? '' : path}/500.html
140
+ ErrorDocument 502 ${path === '/' ? '' : path}/500.html
141
+ ErrorDocument 503 ${path === '/' ? '' : path}/500.html
142
+ ErrorDocument 504 ${path === '/' ? '' : path}/500.html
143
+
137
144
  </VirtualHost>
138
145
 
139
146
  `);
@@ -196,10 +203,10 @@ export PATH=$PATH:/opt/lampp/bin`,
196
203
  Listen ${port}
197
204
 
198
205
  <VirtualHost *:${port}>
199
- DocumentRoot "${directory ? directory.replace(path, '/') : `${getRootDirectory()}${rootHostPath}`}"
206
+ DocumentRoot "${directory ? directory : `${getRootDirectory()}${rootHostPath}`}"
200
207
  ServerName ${host}:${port}
201
208
 
202
- <Directory "${directory ? directory.replace(path, '/') : `${getRootDirectory()}${rootHostPath}`}">
209
+ <Directory "${directory ? directory : `${getRootDirectory()}${rootHostPath}`}">
203
210
  Options Indexes FollowSymLinks MultiViews
204
211
  AllowOverride All
205
212
  Require all granted
@@ -216,6 +223,13 @@ export PATH=$PATH:/opt/lampp/bin`,
216
223
  : ''
217
224
  }
218
225
 
226
+ ErrorDocument 400 ${path === '/' ? '' : path}/400.html
227
+ ErrorDocument 404 ${path === '/' ? '' : path}/400.html
228
+ ErrorDocument 500 ${path === '/' ? '' : path}/500.html
229
+ ErrorDocument 502 ${path === '/' ? '' : path}/500.html
230
+ ErrorDocument 503 ${path === '/' ? '' : path}/500.html
231
+ ErrorDocument 504 ${path === '/' ? '' : path}/500.html
232
+
219
233
  </VirtualHost>
220
234
 
221
235
  `);
@@ -369,13 +383,16 @@ export PATH=$PATH:/opt/lampp/bin`,
369
383
  app.use(`${apiPath}/${api}`, router);
370
384
  })();
371
385
  }
372
-
373
386
  app.use(function (req, res, next) {
374
- res.status(404).send('Sorry cant find that!');
387
+ const path404 = `${directory ? directory : `${getRootDirectory()}${rootHostPath}`}/404.html`;
388
+ if (fs.existsSync(path404)) return res.status(404).sendFile(path404);
389
+ else res.status(404).send('Sorry cant find that!');
375
390
  });
376
391
 
377
392
  app.use(function (err, req, res, next) {
378
393
  logger.error(err, err.stack);
394
+ const path500 = `${directory ? directory : `${getRootDirectory()}${rootHostPath}`}/500.html`;
395
+ if (fs.existsSync(path500)) return res.status(500).sendFile(path500);
379
396
  res.status(500).send('Something broke!');
380
397
  });
381
398