@schalkneethling/miyagi-core 4.0.2

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 (138) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +43 -0
  3. package/api/app.js +39 -0
  4. package/api/index.js +236 -0
  5. package/bin/miyagi.js +2 -0
  6. package/dist/css/iframe.css +31 -0
  7. package/dist/css/main.css +1 -0
  8. package/dist/js/_iframe-links-DdifIr4P.js +1 -0
  9. package/dist/js/_mock-data-Dypo4Bl_.js +1 -0
  10. package/dist/js/_prism-By3NMwUd.js +1 -0
  11. package/dist/js/iframe.build.js +1 -0
  12. package/dist/js/iframe.js +1 -0
  13. package/dist/js/index-BKDKaBC6.js +1 -0
  14. package/dist/js/jsontree.js +1 -0
  15. package/dist/js/main.build.js +1 -0
  16. package/dist/js/main.js +1 -0
  17. package/frontend/assets/css/iframe/accordion-tabs.css +77 -0
  18. package/frontend/assets/css/iframe/jsontree.js.css +325 -0
  19. package/frontend/assets/css/iframe/prism.css +132 -0
  20. package/frontend/assets/css/iframe/styleguide/colors.css +61 -0
  21. package/frontend/assets/css/iframe/styleguide/fonts.css +37 -0
  22. package/frontend/assets/css/iframe/styleguide/index.css +109 -0
  23. package/frontend/assets/css/iframe/styleguide/spacings.css +21 -0
  24. package/frontend/assets/css/iframe.css +410 -0
  25. package/frontend/assets/css/main/menu/config-switcher.css +49 -0
  26. package/frontend/assets/css/main/menu/config-switchers.css +67 -0
  27. package/frontend/assets/css/main/menu/goto.css +24 -0
  28. package/frontend/assets/css/main/menu/nav.css +113 -0
  29. package/frontend/assets/css/main/menu/search.css +64 -0
  30. package/frontend/assets/css/main/menu/title.css +40 -0
  31. package/frontend/assets/css/main/menu.css +114 -0
  32. package/frontend/assets/css/main/reset.css +217 -0
  33. package/frontend/assets/css/main.css +71 -0
  34. package/frontend/assets/css/shared.css +34 -0
  35. package/frontend/assets/css/tokens.css +112 -0
  36. package/frontend/assets/favicon.ico +0 -0
  37. package/frontend/assets/js/_accordion-tabs.js +403 -0
  38. package/frontend/assets/js/_goto.js +63 -0
  39. package/frontend/assets/js/_iframe-links.js +19 -0
  40. package/frontend/assets/js/_is-triggered.js +15 -0
  41. package/frontend/assets/js/_main.js +379 -0
  42. package/frontend/assets/js/_mock-data.js +13 -0
  43. package/frontend/assets/js/_prism.js +1098 -0
  44. package/frontend/assets/js/_search.js +190 -0
  45. package/frontend/assets/js/_socket.js +9 -0
  46. package/frontend/assets/js/config-switcher/development-mode.js +49 -0
  47. package/frontend/assets/js/config-switcher/index.js +63 -0
  48. package/frontend/assets/js/config-switcher/text-direction.js +30 -0
  49. package/frontend/assets/js/config-switcher/theme.js +87 -0
  50. package/frontend/assets/js/iframe.build.js +43 -0
  51. package/frontend/assets/js/iframe.js +52 -0
  52. package/frontend/assets/js/jsontree.js +979 -0
  53. package/frontend/assets/js/main.build.js +40 -0
  54. package/frontend/assets/js/main.js +42 -0
  55. package/frontend/assets/js/styleguide/color-converter.js +741 -0
  56. package/frontend/assets/js/styleguide/index.js +119 -0
  57. package/frontend/views/component_variation.twig.miyagi +57 -0
  58. package/frontend/views/design-tokens/colors.twig.miyagi +43 -0
  59. package/frontend/views/design-tokens/sizes.twig.miyagi +35 -0
  60. package/frontend/views/design-tokens/typography.twig.miyagi +38 -0
  61. package/frontend/views/iframe_component.twig.miyagi +141 -0
  62. package/frontend/views/iframe_component_variation.twig.miyagi +55 -0
  63. package/frontend/views/iframe_index.twig.miyagi +14 -0
  64. package/frontend/views/layouts/iframe_default.twig.miyagi +22 -0
  65. package/frontend/views/main.twig.miyagi +24 -0
  66. package/frontend/views/menu/config-switchers.twig.miyagi +83 -0
  67. package/frontend/views/menu/goto.twig.miyagi +9 -0
  68. package/frontend/views/menu/menu.twig.miyagi +21 -0
  69. package/frontend/views/menu/nav.twig.miyagi +95 -0
  70. package/frontend/views/menu/search.twig.miyagi +13 -0
  71. package/frontend/views/menu/title.twig.miyagi +24 -0
  72. package/index.js +3 -0
  73. package/lib/build/index.js +1020 -0
  74. package/lib/cli/app.js +38 -0
  75. package/lib/cli/component.js +56 -0
  76. package/lib/cli/index.js +5 -0
  77. package/lib/cli/lint.js +180 -0
  78. package/lib/config.js +74 -0
  79. package/lib/default-config.js +105 -0
  80. package/lib/generator/component.js +199 -0
  81. package/lib/generator/mocks.js +201 -0
  82. package/lib/helpers.js +184 -0
  83. package/lib/i18n/en.js +91 -0
  84. package/lib/i18n/index.js +17 -0
  85. package/lib/index.js +166 -0
  86. package/lib/init/args.js +55 -0
  87. package/lib/init/config.js +330 -0
  88. package/lib/init/engines.js +65 -0
  89. package/lib/init/index.js +102 -0
  90. package/lib/init/rendering.js +12 -0
  91. package/lib/init/router.js +249 -0
  92. package/lib/init/static.js +133 -0
  93. package/lib/init/twing/cache.js +34 -0
  94. package/lib/init/twing/functions.js +51 -0
  95. package/lib/init/views.js +19 -0
  96. package/lib/init/watcher.js +402 -0
  97. package/lib/logger.js +94 -0
  98. package/lib/mocks/get.js +111 -0
  99. package/lib/mocks/index.js +9 -0
  100. package/lib/mocks/resolve/ref.js +484 -0
  101. package/lib/mocks/resolve/tpl.js +246 -0
  102. package/lib/mocks/resolve.js +205 -0
  103. package/lib/render/helpers.js +51 -0
  104. package/lib/render/index.js +38 -0
  105. package/lib/render/views/iframe/component.docs.js +77 -0
  106. package/lib/render/views/iframe/component.js +338 -0
  107. package/lib/render/views/iframe/design-tokens/colors.js +52 -0
  108. package/lib/render/views/iframe/design-tokens/index.js +9 -0
  109. package/lib/render/views/iframe/design-tokens/sizes.js +49 -0
  110. package/lib/render/views/iframe/design-tokens/typography.js +52 -0
  111. package/lib/render/views/iframe/docs.js +68 -0
  112. package/lib/render/views/iframe/index.js +44 -0
  113. package/lib/render/views/iframe/variation.js +116 -0
  114. package/lib/render/views/iframe/variation.standalone.js +89 -0
  115. package/lib/render/views/main/component.docs.js +53 -0
  116. package/lib/render/views/main/component.js +74 -0
  117. package/lib/render/views/main/design-tokens.js +53 -0
  118. package/lib/render/views/main/docs.js +47 -0
  119. package/lib/render/views/main/index.js +46 -0
  120. package/lib/state/components.js +132 -0
  121. package/lib/state/css.js +50 -0
  122. package/lib/state/docs.js +111 -0
  123. package/lib/state/file-contents.js +207 -0
  124. package/lib/state/helpers.js +86 -0
  125. package/lib/state/index.js +56 -0
  126. package/lib/state/menu/index.js +275 -0
  127. package/lib/state/menu/structure.js +146 -0
  128. package/lib/state/partials.js +23 -0
  129. package/lib/state/source-tree.js +75 -0
  130. package/lib/styleguide/color-names.js +150 -0
  131. package/lib/styleguide/colors.js +135 -0
  132. package/lib/styleguide/helpers.js +37 -0
  133. package/lib/styleguide/index.js +17 -0
  134. package/lib/styleguide/media-queries.js +26 -0
  135. package/lib/styleguide/spacings.js +35 -0
  136. package/lib/styleguide/typography.js +61 -0
  137. package/lib/validator/mocks.js +105 -0
  138. package/package.json +117 -0
@@ -0,0 +1,1020 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { writeFile, readdir } from "node:fs/promises";
4
+ import * as helpers from "../helpers.js";
5
+ import render from "../render/index.js";
6
+ import appConfig from "../default-config.js";
7
+ import { t } from "../i18n/index.js";
8
+ import log from "../logger.js";
9
+ import { getVariationData } from "../mocks/index.js";
10
+
11
+ /**
12
+ * Module for creating a static build
13
+ * @module build
14
+ */
15
+ export default () => {
16
+ return new Promise((resolve, reject) => {
17
+ const config = { ...global.config };
18
+ const { build } = config;
19
+ const buildFolder = build.folder;
20
+
21
+ global.config = config;
22
+
23
+ log("info", null, "Clearing build directory.");
24
+ fs.rm(path.resolve(buildFolder), { recursive: true }, () => {
25
+ log("info", null, "Creating build directory.");
26
+ fs.mkdir(path.resolve(buildFolder), { recursive: true }, () => {
27
+ const promises = [];
28
+
29
+ promises.push(
30
+ new Promise((res, rej) => {
31
+ buildDistDirectory(buildFolder).then(res).catch(rej);
32
+ }),
33
+ );
34
+
35
+ promises.push(
36
+ new Promise((res, rej) => {
37
+ buildUserFavicon(buildFolder, global.config.ui.theme.favicon)
38
+ .then(res)
39
+ .catch(rej);
40
+ }),
41
+ );
42
+
43
+ promises.push(
44
+ new Promise((res, rej) => {
45
+ buildUserAssets(buildFolder, global.config.assets, [
46
+ global.config.ui.theme.logo.dark,
47
+ global.config.ui.theme.logo.light,
48
+ ])
49
+ .then(res)
50
+ .catch(rej);
51
+ }),
52
+ );
53
+
54
+ promises.push(
55
+ new Promise((res, rej) => {
56
+ buildComponentAssets(
57
+ buildFolder,
58
+ global.state.components,
59
+ global.config.components.folder,
60
+ )
61
+ .then(res)
62
+ .catch(rej);
63
+ }),
64
+ );
65
+
66
+ promises.push(
67
+ new Promise((res, rej) => {
68
+ buildIframeIndex(buildFolder).then(res).catch(rej);
69
+ }),
70
+ );
71
+
72
+ promises.push(
73
+ new Promise((res, rej) => {
74
+ buildIframeDesignTokens(buildFolder).then(res).catch(rej);
75
+ }),
76
+ );
77
+
78
+ promises.push(
79
+ new Promise((res, rej) => {
80
+ buildDesignTokens(buildFolder).then(res).catch(rej);
81
+ }),
82
+ );
83
+
84
+ promises.push(
85
+ new Promise((res, rej) => {
86
+ buildIndex(buildFolder).then(res).catch(rej);
87
+ }),
88
+ );
89
+
90
+ global.state.routes.forEach((route) => {
91
+ if (route.type === "docs") {
92
+ promises.push(
93
+ new Promise((res, rej) => {
94
+ buildDoc(route, buildFolder).then(res).catch(rej);
95
+ }),
96
+ );
97
+ }
98
+ });
99
+
100
+ // const partials = Object.keys(global.state.partials);
101
+ // const files = partials.map((partial) => {
102
+ // return {
103
+ // file: partial,
104
+ // dir: path.dirname(partial),
105
+ // };
106
+ // });
107
+
108
+ // Object.keys(global.state.fileContents).forEach((file) => {
109
+ // if (file.endsWith("/README.md")) {
110
+ // const filePath = path.dirname(
111
+ // file.replace(
112
+ // path.join(process.cwd(), global.config.components.folder, "/"),
113
+ // ""
114
+ // )
115
+ // );
116
+
117
+ // if (!files.find((file) => file.dir === filePath)) {
118
+ // files.push({
119
+ // file: null,
120
+ // dir: filePath,
121
+ // });
122
+ // }
123
+ // }
124
+ // });
125
+
126
+ const paths = [];
127
+ for (const route of global.state.routes) {
128
+ if (route.type === "components") {
129
+ promises.push(
130
+ new Promise((res, rej) => {
131
+ if (route.paths.tpl) {
132
+ buildComponent({
133
+ component: route,
134
+ buildFolder,
135
+ })
136
+ .then((component) => {
137
+ if (component) {
138
+ for (const path of getFilePathsForJsonOutput(
139
+ buildFolder,
140
+ component,
141
+ )) {
142
+ paths.push(path);
143
+ }
144
+ }
145
+ res();
146
+ })
147
+ .catch((err) => {
148
+ rej(err);
149
+ });
150
+ } else {
151
+ buildComponentDocs({
152
+ component: route,
153
+ buildFolder,
154
+ })
155
+ .then((component) => {
156
+ if (component) {
157
+ for (const path of getFilePathsForJsonOutput(
158
+ buildFolder,
159
+ component,
160
+ )) {
161
+ paths.push(path);
162
+ }
163
+ }
164
+ res();
165
+ })
166
+ .catch((err) => {
167
+ rej(err);
168
+ });
169
+ }
170
+ }),
171
+ );
172
+ }
173
+ }
174
+
175
+ Promise.all(promises)
176
+ .then(async () => {
177
+ await createJsonOutputFile(buildFolder, paths);
178
+
179
+ resolve(
180
+ t("buildDone").replace(
181
+ "{{count}}",
182
+ (await readdir(buildFolder, { recursive: true })).length,
183
+ ),
184
+ );
185
+ })
186
+ .catch((err) => {
187
+ reject(err);
188
+ });
189
+ });
190
+ });
191
+ });
192
+
193
+ /**
194
+ * Creates an "output.json" file with the given array as content
195
+ * @param {string} buildFolder
196
+ * @param {Array} paths - all paths → standalone views of component variations
197
+ */
198
+ async function createJsonOutputFile(buildFolder, paths) {
199
+ log("info", null, `Writing ${path.join(buildFolder, "output.json")}.`);
200
+ await writeFile(
201
+ path.join(buildFolder, "output.json"),
202
+ JSON.stringify(
203
+ paths.map((path) => {
204
+ return {
205
+ path,
206
+ };
207
+ }),
208
+ null,
209
+ 2,
210
+ ),
211
+ );
212
+ }
213
+
214
+ /**
215
+ * Accepts an array with arrays and returns its values with cwd and buildFolder
216
+ * @param {string} buildFolder
217
+ * @param {Array} component - an array containing arrays with file paths
218
+ * @returns {Array} the flattened arrays
219
+ */
220
+ function getFilePathsForJsonOutput(buildFolder, component) {
221
+ const paths = [];
222
+
223
+ for (const entries of component) {
224
+ if (entries) {
225
+ for (const file of entries) {
226
+ if (file) {
227
+ paths.push(
228
+ file.replace(path.join(process.cwd(), buildFolder, "/"), ""),
229
+ );
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ return paths;
236
+ }
237
+
238
+ /**
239
+ * Copies the user favicon
240
+ * @param {string} buildFolder - the build folder from the user configuration
241
+ * @param {string} faviconPath - the favicon path from the user configuration
242
+ * @returns {Promise} gets resolved after the favicon has been copied
243
+ */
244
+ function buildUserFavicon(buildFolder, faviconPath) {
245
+ return new Promise((resolve) => {
246
+ if (faviconPath) {
247
+ log(
248
+ "info",
249
+ null,
250
+ `Copying ${faviconPath} → ${buildFolder}/favicon.ico.`,
251
+ );
252
+ fs.cp(
253
+ path.join(process.cwd(), faviconPath),
254
+ `${buildFolder}/favicon.ico`,
255
+ { recursive: true },
256
+ resolve,
257
+ );
258
+ } else {
259
+ resolve();
260
+ }
261
+ });
262
+ }
263
+
264
+ /**
265
+ * Copies the dist directory
266
+ * @param {string} buildFolder - the build folder from the user configuration
267
+ * @returns {Promise} gets resolved when the dist directory has been copied
268
+ */
269
+ function buildDistDirectory(buildFolder) {
270
+ log(
271
+ "info",
272
+ null,
273
+ `Copying ${path.join(import.meta.dirname, "../../dist/")} → ${path.join(buildFolder, "/miyagi")}`,
274
+ );
275
+ return new Promise((resolve) =>
276
+ fs.cp(
277
+ path.join(import.meta.dirname, "../../dist/"),
278
+ `${path.join(process.cwd(), buildFolder, "/miyagi")}`,
279
+ {
280
+ recursive: true,
281
+ },
282
+ resolve,
283
+ ),
284
+ );
285
+ }
286
+
287
+ /**
288
+ * @param {string} buildFolder
289
+ * @param {Array} components
290
+ * @param {string} componentsFolder
291
+ * @returns {Promise}
292
+ */
293
+ async function buildComponentAssets(
294
+ buildFolder,
295
+ components,
296
+ componentsFolder,
297
+ ) {
298
+ const promises = [];
299
+
300
+ ["css", "js"].forEach((type) => {
301
+ components.forEach(({ assets }) => {
302
+ if (assets[type]) {
303
+ promises.push(
304
+ new Promise((resolve) => {
305
+ log(
306
+ "info",
307
+ null,
308
+ `Copying ${path.join(componentsFolder, assets[type])} → ${path.join(buildFolder, assets[type])}.`,
309
+ );
310
+ fs.cp(
311
+ path.join(process.cwd(), assets[type]),
312
+ path.join(process.cwd(), buildFolder, assets[type]),
313
+ {
314
+ recursive: true,
315
+ },
316
+ resolve,
317
+ );
318
+ }),
319
+ );
320
+ }
321
+ });
322
+ });
323
+
324
+ return Promise.all(promises);
325
+ }
326
+
327
+ /**
328
+ * Copies the user assets
329
+ * @param {string} buildFolder - the build folder from the user configuration
330
+ * @param {object} assetsConfig - the assets object from the user configuration
331
+ * @param {Array} logoPaths - the logo paths from the user configuration
332
+ * @returns {Promise} gets resolved when all assets have been copied
333
+ */
334
+ async function buildUserAssets(buildFolder, assetsConfig, logoPaths) {
335
+ const promises = [];
336
+
337
+ for (const folder of assetsConfig.folder) {
338
+ promises.push(
339
+ new Promise((resolve) => {
340
+ log(
341
+ "info",
342
+ null,
343
+ `Copying ${path.join(assetsConfig.root, folder)} → ${path.join(buildFolder, folder)}`,
344
+ );
345
+ fs.cp(
346
+ path.resolve(path.join(assetsConfig.root, folder)),
347
+ path.join(process.cwd(), buildFolder, folder),
348
+ {
349
+ recursive: true,
350
+ },
351
+ resolve,
352
+ );
353
+ }),
354
+ );
355
+ }
356
+
357
+ if (logoPaths) {
358
+ logoPaths.forEach((logoPath) => {
359
+ if (
360
+ logoPath &&
361
+ assetsConfig.folder.filter((entry) => logoPath.startsWith(entry))
362
+ .length === 0
363
+ ) {
364
+ promises.push(
365
+ new Promise((resolve) => {
366
+ log(
367
+ "info",
368
+ null,
369
+ `Copying ${path.resolve(logoPath)} → ${path.join(buildFolder, logoPath)}`,
370
+ );
371
+ fs.cp(
372
+ path.resolve(logoPath),
373
+ path.join(buildFolder, logoPath),
374
+ { recursive: true },
375
+ resolve,
376
+ );
377
+ }),
378
+ );
379
+ }
380
+ });
381
+ }
382
+
383
+ const cssJsFiles = [
384
+ ...new Set([
385
+ ...assetsConfig.css.map((file) =>
386
+ path.join(
387
+ assetsConfig.root,
388
+ typeof file === "string" ? file : file.src,
389
+ ),
390
+ ),
391
+ ...assetsConfig.js.map((file) =>
392
+ path.join(
393
+ assetsConfig.root,
394
+ typeof file === "string" ? file : file.src,
395
+ ),
396
+ ),
397
+ ...assetsConfig.customProperties.files.map((file) =>
398
+ path.join(assetsConfig.root, file),
399
+ ),
400
+ ]),
401
+ ];
402
+
403
+ for (const file of cssJsFiles) {
404
+ if (
405
+ assetsConfig.folder.filter((entry) => file.startsWith(entry)).length ===
406
+ 0
407
+ ) {
408
+ promises.push(
409
+ new Promise((resolve) => {
410
+ log(
411
+ "info",
412
+ null,
413
+ `Copying ${file} → ${path.join(buildFolder, file)}`,
414
+ );
415
+ fs.cp(
416
+ file,
417
+ path.join(buildFolder, file),
418
+ { recursive: true },
419
+ resolve,
420
+ );
421
+ }),
422
+ );
423
+ }
424
+ }
425
+
426
+ return Promise.all(promises);
427
+ }
428
+
429
+ /**
430
+ * Rendeers and builds the component overview
431
+ * @param {string} buildFolder - the build folder from the user configuration
432
+ * @returns {Promise} gets resolved when the view has been rendered
433
+ */
434
+ function buildIframeIndex(buildFolder) {
435
+ const promises = [];
436
+
437
+ for (const embedded of [false, true]) {
438
+ promises.push(
439
+ new Promise((resolve, reject) => {
440
+ render
441
+ .renderIframeIndex({
442
+ res: global.app,
443
+ cb: async (err, response) => {
444
+ if (err) {
445
+ if (typeof err === "string") {
446
+ reject(err);
447
+ } else if (err.message) {
448
+ reject(err.message);
449
+ }
450
+ } else {
451
+ const filePath = path.join(
452
+ buildFolder,
453
+ embedded
454
+ ? global.config.indexPath.embedded
455
+ : global.config.indexPath.default,
456
+ );
457
+ log("info", null, `Writing ${filePath}`);
458
+
459
+ try {
460
+ await writeFile(filePath, response);
461
+ resolve();
462
+ } catch (err) {
463
+ reject(err.message);
464
+ }
465
+ }
466
+ },
467
+ })
468
+ .catch((e) => reject(e));
469
+ }),
470
+ );
471
+ }
472
+
473
+ return Promise.all(promises);
474
+ }
475
+
476
+ /**
477
+ * @param {string} buildFolder - the build folder from the user configuration
478
+ * @returns {Promise} gets resolved when the view has been rendered
479
+ */
480
+ function buildIframeDesignTokens(buildFolder) {
481
+ const promises = [];
482
+
483
+ for (const embedded of [false, true]) {
484
+ for (const type of ["colors", "sizes", "typography"]) {
485
+ promises.push(
486
+ new Promise((resolve, reject) => {
487
+ render.iframe.designTokens[type]({
488
+ res: global.app,
489
+ cb: async (err, response) => {
490
+ if (err) {
491
+ if (typeof err === "string") {
492
+ reject(err);
493
+ } else if (err.message) {
494
+ reject(err.message);
495
+ }
496
+ } else {
497
+ const filePath = path.join(
498
+ `${buildFolder}/iframe-design-tokens-${type}${
499
+ embedded ? "-embedded" : ""
500
+ }.html`,
501
+ );
502
+ log("info", null, `Writing ${filePath}`);
503
+
504
+ try {
505
+ await writeFile(filePath, response);
506
+ resolve();
507
+ } catch (err) {
508
+ reject(err.message);
509
+ }
510
+ }
511
+ },
512
+ }).catch((e) => reject(e));
513
+ }),
514
+ );
515
+ }
516
+ }
517
+
518
+ return Promise.all(promises);
519
+ }
520
+
521
+ /**
522
+ * @param {string} buildFolder - the build folder from the user configuration
523
+ * @returns {Promise} gets resolved when the view has been rendered
524
+ */
525
+ function buildDesignTokens(buildFolder) {
526
+ const promises = [];
527
+
528
+ for (const type of ["colors", "sizes", "typography"]) {
529
+ promises.push(
530
+ new Promise((resolve, reject) => {
531
+ render.renderMainDesignTokens({
532
+ res: global.app,
533
+ type,
534
+ cb: async (err, response) => {
535
+ if (err) {
536
+ if (typeof err === "string") {
537
+ reject(err);
538
+ } else if (err.message) {
539
+ reject(err.message);
540
+ }
541
+ } else {
542
+ const filePath = path.join(
543
+ `${buildFolder}/design-tokens-${type}.html`,
544
+ );
545
+ log("info", null, `Writing ${filePath}`);
546
+
547
+ try {
548
+ await writeFile(filePath, response);
549
+ resolve();
550
+ } catch (err) {
551
+ reject(err.message);
552
+ }
553
+ }
554
+ },
555
+ });
556
+ }),
557
+ );
558
+ }
559
+
560
+ return Promise.all(promises);
561
+ }
562
+
563
+ /**
564
+ * Renders and builds the index view
565
+ * @param {string} buildFolder - the build folder from the user configuration
566
+ * @returns {Promise} gets resolved when the view has been rendered
567
+ */
568
+ function buildIndex(buildFolder) {
569
+ return new Promise((resolve, reject) => {
570
+ render.renderMainIndex({
571
+ res: global.app,
572
+ cb: async (err, response) => {
573
+ if (err) {
574
+ if (typeof err === "string") {
575
+ reject(err);
576
+ } else if (err.message) {
577
+ reject(err.message);
578
+ }
579
+ } else {
580
+ const filePath = path.join(`${buildFolder}/index.html`);
581
+ log("info", null, `Writing ${filePath}`);
582
+
583
+ try {
584
+ await writeFile(filePath, response);
585
+ resolve();
586
+ } catch (err) {
587
+ reject(err.message);
588
+ }
589
+ }
590
+ },
591
+ });
592
+ });
593
+ }
594
+
595
+ /**
596
+ * Renders and builds a variation
597
+ * @param {object} object - parameter object
598
+ * @param {string} object.buildFolder - the build folder from the user configuration
599
+ * @param {object} object.component
600
+ * @param {string} object.variation - the variation name
601
+ * @returns {Promise} gets resolved when all variation views have been rendered
602
+ */
603
+ async function buildVariation({ buildFolder, component, variation }) {
604
+ const data = (await getVariationData(component, variation)) || {};
605
+
606
+ return new Promise((res, rej) => {
607
+ const promises = [];
608
+
609
+ promises.push(
610
+ new Promise((resolve, reject) => {
611
+ const filePath = path.join(
612
+ buildFolder,
613
+ component.route.embedded.replace(
614
+ "-embedded.html",
615
+ `-variation-${helpers.normalizeString(variation)}-embedded.html`,
616
+ ),
617
+ );
618
+
619
+ render.renderIframeVariation({
620
+ res: global.app,
621
+ component,
622
+ variation,
623
+ data,
624
+ cb: async (err, response) => {
625
+ if (err) {
626
+ if (typeof err === "string") {
627
+ reject(err);
628
+ } else if (err.message) {
629
+ reject(err.message);
630
+ }
631
+ } else {
632
+ log("info", null, `Writing ${filePath}`);
633
+
634
+ try {
635
+ await writeFile(filePath, response);
636
+ resolve();
637
+ } catch (err) {
638
+ reject(err.message);
639
+ }
640
+ }
641
+ },
642
+ });
643
+ }),
644
+ );
645
+
646
+ promises.push(
647
+ new Promise((resolve, reject) => {
648
+ const fileName = component.route.default.replace(
649
+ ".html",
650
+ `-variation-${helpers.normalizeString(variation)}.html`,
651
+ );
652
+ const filePath = path.join(buildFolder, fileName);
653
+
654
+ render.renderIframeVariationStandalone({
655
+ res: global.app,
656
+ component,
657
+ componentData: data.resolved,
658
+ cb: async (err, response) => {
659
+ if (err) {
660
+ if (typeof err === "string") {
661
+ reject(err);
662
+ } else if (err.message) {
663
+ reject(err.message);
664
+ }
665
+ } else {
666
+ log("info", null, `Writing ${filePath}`);
667
+
668
+ try {
669
+ await writeFile(filePath, response);
670
+ resolve(fileName);
671
+ } catch (err) {
672
+ reject(err.message);
673
+ }
674
+ }
675
+ },
676
+ });
677
+ }),
678
+ );
679
+
680
+ promises.push(
681
+ new Promise((resolve, reject) => {
682
+ render.renderMainComponent({
683
+ res: global.app,
684
+ component,
685
+ variation,
686
+ cb: async (err, response) => {
687
+ if (err) {
688
+ if (typeof err === "string") {
689
+ reject(err);
690
+ } else if (err.message) {
691
+ reject(err.message);
692
+ }
693
+ } else {
694
+ const filePath = path.join(
695
+ buildFolder,
696
+ component.route.default
697
+ .replace("component-", "show-")
698
+ .replace(
699
+ ".html",
700
+ `-variation-${helpers.normalizeString(variation)}.html`,
701
+ ),
702
+ );
703
+
704
+ log("info", null, `Writing ${filePath}`);
705
+
706
+ try {
707
+ await writeFile(filePath, response);
708
+ resolve();
709
+ } catch (err) {
710
+ reject(err.message);
711
+ }
712
+ }
713
+ },
714
+ });
715
+ }),
716
+ );
717
+
718
+ return Promise.all(promises).then(res).catch(rej);
719
+ });
720
+ }
721
+
722
+ /**
723
+ * Renders and builds a variation
724
+ * @param {object} object - parameter object
725
+ * @param {object} object.component
726
+ * @param {string} object.buildFolder - the build folder from the user configuration
727
+ * @returns {Promise} gets resolved when all component views have been rendered
728
+ */
729
+ function buildComponent({ component, buildFolder }) {
730
+ return new Promise((res, rej) => {
731
+ const promises = [];
732
+ const data =
733
+ global.state.fileContents[
734
+ component.paths.mocks.full(global.config.files.mocks.extension[0])
735
+ ] ||
736
+ global.state.fileContents[
737
+ component.paths.mocks.full(global.config.files.mocks.extension[1])
738
+ ];
739
+
740
+ promises.push(
741
+ new Promise((resolve, reject) => {
742
+ render.renderMainComponent({
743
+ res: global.app,
744
+ component,
745
+ variation: null,
746
+ cb: async (err, response) => {
747
+ if (err) {
748
+ if (typeof err === "string") {
749
+ reject(err);
750
+ } else if (err.message) {
751
+ reject(err.message);
752
+ }
753
+ } else {
754
+ const filePath = path.join(
755
+ buildFolder,
756
+ component.route.default.replace("component-", "show-"),
757
+ );
758
+ log("info", null, `Writing ${filePath}`);
759
+
760
+ try {
761
+ await writeFile(filePath, response);
762
+ resolve();
763
+ } catch (err) {
764
+ reject(err.message);
765
+ }
766
+ }
767
+ },
768
+ });
769
+ }),
770
+ );
771
+
772
+ for (const embedded of [false, true]) {
773
+ if (component.paths.tpl) {
774
+ promises.push(
775
+ new Promise((resolve, reject) => {
776
+ render.renderIframeComponent({
777
+ res: global.app,
778
+ component,
779
+ noCli: embedded,
780
+ cb: async (err, response) => {
781
+ if (err) {
782
+ if (typeof err === "string") {
783
+ reject(err);
784
+ } else if (err.message) {
785
+ reject(err.message);
786
+ }
787
+ } else {
788
+ const filePath = path.join(
789
+ buildFolder,
790
+ component.route.default.replace(
791
+ ".html",
792
+ embedded ? "-embedded.html" : ".html",
793
+ ),
794
+ );
795
+ log("info", null, `Writing ${filePath}`);
796
+
797
+ try {
798
+ await writeFile(filePath, response);
799
+ resolve();
800
+ } catch (err) {
801
+ reject(err.message);
802
+ }
803
+ }
804
+ },
805
+ });
806
+ }),
807
+ );
808
+ }
809
+ }
810
+
811
+ if (component.paths.tpl) {
812
+ let variations = [];
813
+
814
+ if (data) {
815
+ const dataWithoutInternalKeys = helpers.removeInternalKeys(data);
816
+
817
+ if (
818
+ !data.$hidden &&
819
+ Object.keys(dataWithoutInternalKeys).length > 0
820
+ ) {
821
+ variations.push({
822
+ $name: data.$name || appConfig.defaultVariationName,
823
+ ...dataWithoutInternalKeys,
824
+ });
825
+ }
826
+
827
+ if (data.$variants) {
828
+ variations = [...variations, ...data.$variants];
829
+ }
830
+ } else {
831
+ variations.push({
832
+ $name: appConfig.defaultVariationName,
833
+ });
834
+ }
835
+
836
+ for (const variation of variations) {
837
+ const name = variation.$name;
838
+
839
+ if (!name) break;
840
+
841
+ promises.push(
842
+ new Promise((resolve, reject) =>
843
+ buildVariation({
844
+ buildFolder,
845
+ component,
846
+ variation: name,
847
+ })
848
+ .then((fileName) => {
849
+ resolve(fileName);
850
+ })
851
+ .catch((err) => {
852
+ reject(err);
853
+ }),
854
+ ),
855
+ );
856
+ }
857
+ }
858
+
859
+ return Promise.all(promises).then(res).catch(rej);
860
+ });
861
+ }
862
+
863
+ /**
864
+ * Renders and builds a variation
865
+ * @param {object} object - parameter object
866
+ * @param {object} object.component
867
+ * @param {string} object.buildFolder - the build folder from the user configuration
868
+ * @returns {Promise} gets resolved when all component views have been rendered
869
+ */
870
+ function buildComponentDocs({ component, buildFolder }) {
871
+ return new Promise((res, rej) => {
872
+ const promises = [];
873
+
874
+ promises.push(
875
+ new Promise((resolve, reject) => {
876
+ render.renderMainComponentDocs({
877
+ res: global.app,
878
+ component,
879
+ cb: async (err, response) => {
880
+ if (err) {
881
+ if (typeof err === "string") {
882
+ reject(err);
883
+ } else if (err.message) {
884
+ reject(err.message);
885
+ }
886
+ } else {
887
+ const filePath = path.join(
888
+ buildFolder,
889
+ component.route.default.replace("component-", "show-"),
890
+ );
891
+ log("info", null, `Writing ${filePath}`);
892
+
893
+ try {
894
+ await writeFile(filePath, response);
895
+ resolve();
896
+ } catch (err) {
897
+ reject(err.message);
898
+ }
899
+ }
900
+ },
901
+ });
902
+ }),
903
+ );
904
+
905
+ for (const embedded of [false, true]) {
906
+ promises.push(
907
+ new Promise((resolve, reject) => {
908
+ render.renderIframeComponentDocs({
909
+ res: global.app,
910
+ component,
911
+ cb: async (err, response) => {
912
+ if (err) {
913
+ if (typeof err === "string") {
914
+ reject(err);
915
+ } else if (err.message) {
916
+ reject(err.message);
917
+ }
918
+ } else {
919
+ const filePath = path.join(
920
+ buildFolder,
921
+ component.route.default.replace(
922
+ ".html",
923
+ embedded ? "-embedded.html" : ".html",
924
+ ),
925
+ );
926
+ log("info", null, `Writing ${filePath}`);
927
+
928
+ try {
929
+ await writeFile(filePath, response);
930
+ resolve();
931
+ } catch (err) {
932
+ reject(err.message);
933
+ }
934
+ }
935
+ },
936
+ });
937
+ }),
938
+ );
939
+ }
940
+
941
+ return Promise.all(promises).then(res).catch(rej);
942
+ });
943
+ }
944
+
945
+ /**
946
+ * @param {object} doc
947
+ * @param {string} buildFolder
948
+ * @returns {Promise}
949
+ */
950
+ function buildDoc(doc, buildFolder) {
951
+ const normalizedFileName = helpers.normalizeString(doc.paths.dir.short);
952
+ const promises = [];
953
+
954
+ promises.push(
955
+ new Promise((resolve, reject) => {
956
+ render.renderMainDocs({
957
+ res: global.app,
958
+ doc,
959
+ cb: async (err, response) => {
960
+ if (err) {
961
+ if (typeof err === "string") {
962
+ reject(err);
963
+ } else if (err.message) {
964
+ reject(err.message);
965
+ }
966
+ } else {
967
+ const filePath = path.join(
968
+ `${buildFolder}/show-${normalizedFileName}.html`,
969
+ );
970
+ log("info", null, `Writing ${filePath}`);
971
+
972
+ try {
973
+ await writeFile(filePath, response);
974
+ resolve();
975
+ } catch (err) {
976
+ reject(err.message);
977
+ }
978
+ }
979
+ },
980
+ });
981
+ }),
982
+ );
983
+
984
+ for (const embedded of [false, true]) {
985
+ promises.push(
986
+ new Promise((resolve, reject) => {
987
+ render.renderIframeDocs({
988
+ res: global.app,
989
+ doc,
990
+ cb: async (err, response) => {
991
+ if (err) {
992
+ if (typeof err === "string") {
993
+ reject(err);
994
+ } else if (err.message) {
995
+ reject(err.message);
996
+ }
997
+ } else {
998
+ const filePath = path.join(
999
+ `${buildFolder}/component-${normalizedFileName}${
1000
+ -embedded ? "-embedded" : ""
1001
+ }.html`,
1002
+ );
1003
+ log("info", null, `Writing ${filePath}`);
1004
+
1005
+ try {
1006
+ await writeFile(filePath, response);
1007
+ resolve();
1008
+ } catch (err) {
1009
+ reject(err.message);
1010
+ }
1011
+ }
1012
+ },
1013
+ });
1014
+ }),
1015
+ );
1016
+ }
1017
+
1018
+ return Promise.all(promises);
1019
+ }
1020
+ };