@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,484 @@
1
+ import path from "path";
2
+ import deepMerge from "deepmerge";
3
+
4
+ import * as helpers from "../../helpers.js";
5
+ import { t } from "../../i18n/index.js";
6
+ import { mergeRootDataWithVariationData } from "../resolve.js";
7
+
8
+ /**
9
+ * @param {object|Array|string|boolean} entry - a value from the mock data object
10
+ * @param {object} component
11
+ * @returns {Promise<object|Array|string|boolean>} the resolved value from the mock data object
12
+ */
13
+ export const resolveRefs = async function (entry, component) {
14
+ const messages = [];
15
+
16
+ if (entry) {
17
+ if (
18
+ typeof entry === "string" ||
19
+ typeof entry === "number" ||
20
+ typeof entry === "boolean" ||
21
+ entry instanceof Map ||
22
+ entry === null
23
+ ) {
24
+ return {
25
+ messages,
26
+ data: entry,
27
+ };
28
+ }
29
+
30
+ if (entry instanceof Array) {
31
+ const o = [];
32
+
33
+ for (const ent of entry) {
34
+ let newComponent = component;
35
+
36
+ if (ent.$ref) {
37
+ if (!ent.$ref.startsWith("#/$defs/")) {
38
+ if (ent.$ref.startsWith("/")) {
39
+ ent.$ref = ent.$ref.replace("/", "");
40
+ }
41
+ const path = ent.$ref.split("#")[0];
42
+ const { resolved, message } = resolveNamespace(path);
43
+
44
+ if (resolved) {
45
+ newComponent = global.state.routes.find(
46
+ (route) => route.paths.dir.short === resolved,
47
+ );
48
+ } else {
49
+ messages.push(message);
50
+ }
51
+ }
52
+ }
53
+
54
+ const json = await resolve(ent, newComponent);
55
+ const result = await resolveRefs(json.data, newComponent);
56
+
57
+ o.push(result.data);
58
+
59
+ for (const msg of [...json.messages, ...result.messages]) {
60
+ if (msg) {
61
+ messages.push(msg);
62
+ }
63
+ }
64
+ }
65
+
66
+ return {
67
+ messages,
68
+ data: o,
69
+ };
70
+ }
71
+
72
+ let o = entry;
73
+
74
+ for (const [key, value] of Object.entries({ ...entry })) {
75
+ if (key === "$ref") {
76
+ let newComponent;
77
+
78
+ if (value.startsWith("#/$defs/")) {
79
+ newComponent = component;
80
+ } else {
81
+ let path = value.split("#")[0];
82
+
83
+ if (path.startsWith("/")) {
84
+ path = path.replace("/", "");
85
+ }
86
+
87
+ const { resolved, message } = resolveNamespace(path);
88
+
89
+ if (resolved) {
90
+ newComponent = global.state.routes.find(
91
+ (route) => route.paths.dir.short === resolved,
92
+ );
93
+ } else {
94
+ messages.push(message);
95
+ }
96
+ }
97
+
98
+ let { message, data: resolvedValue } =
99
+ await getRootOrVariantDataOfReference(value, newComponent);
100
+
101
+ if (message) {
102
+ messages.push(message);
103
+ }
104
+
105
+ const json = await resolve(resolvedValue, newComponent);
106
+ const result = await resolveRefs(json.data, newComponent);
107
+
108
+ for (const msg of [...json.messages, ...result.messages]) {
109
+ if (msg) {
110
+ messages.push(msg);
111
+ }
112
+ }
113
+
114
+ o = { ...o, ...result.data };
115
+
116
+ delete o.$ref;
117
+ } else {
118
+ const json = await resolve(value, component);
119
+ const result = await resolveRefs(json.data, component);
120
+
121
+ o[key] = result.data;
122
+
123
+ for (const msg of [...json.messages, ...result.messages]) {
124
+ if (msg) {
125
+ messages.push(msg);
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ return {
132
+ messages,
133
+ data: o,
134
+ };
135
+ }
136
+
137
+ return {
138
+ messages,
139
+ data: entry,
140
+ };
141
+ };
142
+
143
+ /**
144
+ * @param {object|Array|string|boolean} entry - a value from the mock data object
145
+ * @param {object} component
146
+ * @returns {Promise<object|Array|string|boolean>} the resolved value from the mock data object
147
+ */
148
+ async function resolve(entry, component) {
149
+ const messages = [];
150
+
151
+ if (entry !== null) {
152
+ if (Array.isArray(entry)) {
153
+ return {
154
+ messages,
155
+ data: entry,
156
+ };
157
+ }
158
+
159
+ if (
160
+ typeof entry === "string" ||
161
+ typeof entry === "number" ||
162
+ typeof entry === "boolean" ||
163
+ entry instanceof Map ||
164
+ entry === null
165
+ ) {
166
+ return {
167
+ messages,
168
+ data: entry,
169
+ };
170
+ }
171
+
172
+ if (entry === undefined) {
173
+ return {
174
+ messages: [
175
+ ...messages,
176
+ {
177
+ type: "warn",
178
+ text: t("referencedMockFileNotFound"),
179
+ },
180
+ ],
181
+ data: entry,
182
+ };
183
+ }
184
+
185
+ if (entry.$ref) {
186
+ const customData = helpers.cloneDeep(entry);
187
+ delete customData.$ref;
188
+
189
+ let newComponent;
190
+
191
+ if (entry.$ref.startsWith("#/$defs/")) {
192
+ newComponent = component;
193
+ } else {
194
+ let path = entry.$ref.split("#")[0];
195
+
196
+ if (path.startsWith("/")) {
197
+ path = path.replace("/", "");
198
+ }
199
+
200
+ const { resolved, message } = resolveNamespace(path);
201
+
202
+ if (resolved) {
203
+ newComponent = global.state.routes.find(
204
+ (route) => route.paths.dir.short === resolved,
205
+ );
206
+
207
+ if (!newComponent) {
208
+ messages.push({
209
+ type: "warn",
210
+ text: `${resolved} not found`,
211
+ });
212
+ }
213
+ } else {
214
+ messages.push(message);
215
+ }
216
+ }
217
+
218
+ if (newComponent) {
219
+ const { message, data } = await getRootOrVariantDataOfReference(
220
+ entry.$ref,
221
+ newComponent,
222
+ );
223
+
224
+ return {
225
+ messages: [...messages, message],
226
+ data: deepMerge(data, customData),
227
+ };
228
+ } else {
229
+ return {
230
+ messages,
231
+ data: customData,
232
+ };
233
+ }
234
+ }
235
+ }
236
+
237
+ return {
238
+ messages,
239
+ data: entry,
240
+ };
241
+ }
242
+
243
+ /**
244
+ * @param {string} ref - the reference to another mock data
245
+ * @param {object} component
246
+ * @returns {Promise<object>} the resolved data object
247
+ */
248
+ async function getRootOrVariantDataOfReference(ref, component) {
249
+ const { mocks } = global.config.files;
250
+
251
+ let [shortVal, variation] = ref.split("#");
252
+ let jsonFromData;
253
+ let mocksBaseName;
254
+ let fullPath;
255
+
256
+ if (ref.startsWith("#/$defs/")) {
257
+ const componentJson = helpers.cloneDeep(
258
+ global.state.fileContents[
259
+ component.paths.mocks.full(global.config.files.mocks.extension[0])
260
+ ] ||
261
+ global.state.fileContents[
262
+ component.paths.mocks.full(global.config.files.mocks.extension[1])
263
+ ],
264
+ );
265
+
266
+ mocksBaseName = ref;
267
+
268
+ const defRef = ref.replace("#/$defs/", "");
269
+ if ("$defs" in componentJson && defRef in componentJson.$defs) {
270
+ return {
271
+ data: componentJson.$defs[defRef],
272
+ };
273
+ }
274
+
275
+ return {
276
+ message: {
277
+ type: "warn",
278
+ text: t("fileNotFoundLinkIncorrect")
279
+ .replace("{{filePath}}", mocksBaseName)
280
+ .replace(
281
+ "{{component}}",
282
+ component.paths.mocks.short(
283
+ global.config.files.mocks.extension[0],
284
+ ) ||
285
+ component.paths.mocks.short(
286
+ global.config.files.mocks.extension[1],
287
+ ),
288
+ ),
289
+ },
290
+ data: {},
291
+ };
292
+ }
293
+
294
+ if (ref.startsWith(`${global.config.files.mocks.name}/#/$defs/`)) {
295
+ const defaultFile = helpers.getFullPathFromShortPath(
296
+ `${global.config.files.mocks.name}.${global.config.files.mocks.extension[0]}`,
297
+ );
298
+ const jsFile = helpers.getFullPathFromShortPath(
299
+ `${global.config.files.mocks.name}.${global.config.files.mocks.extension[1]}`,
300
+ );
301
+ const globalData = {
302
+ ...(global.state.fileContents[defaultFile] ||
303
+ global.state.fileContents[jsFile]),
304
+ };
305
+
306
+ const defRef = ref.replace(
307
+ `${global.config.files.mocks.name}/#/$defs/`,
308
+ "",
309
+ );
310
+
311
+ if (defRef in globalData.$defs) {
312
+ return {
313
+ data: globalData.$defs[defRef],
314
+ };
315
+ }
316
+
317
+ return {
318
+ message: {
319
+ type: "warn",
320
+ text: t("fileNotFoundLinkIncorrect")
321
+ .replace("{{filePath}}", ref)
322
+ .replace(
323
+ "{{component}}",
324
+ component.paths.mocks.short(
325
+ global.config.files.mocks.extension[0],
326
+ ) ||
327
+ component.paths.mocks.short(
328
+ global.config.files.mocks.extension[1],
329
+ ),
330
+ ),
331
+ },
332
+ data: {},
333
+ };
334
+ }
335
+
336
+ if (shortVal.startsWith("@")) {
337
+ const namespace = shortVal.split("/")[0];
338
+ const resolvedNamespace = global.config.namespaces[namespace];
339
+
340
+ if (!resolvedNamespace) {
341
+ return {
342
+ message: {
343
+ type: "warn",
344
+ text: `Could not resolve namespace ${namespace}`,
345
+ },
346
+ data: null,
347
+ };
348
+ }
349
+
350
+ shortVal = path.relative(namespace, shortVal);
351
+ mocksBaseName = path.join(shortVal, mocks.name);
352
+ fullPath = path.join(
353
+ process.cwd(),
354
+ resolvedNamespace,
355
+ shortVal,
356
+ mocks.name,
357
+ );
358
+ } else {
359
+ mocksBaseName = path.join(shortVal, mocks.name);
360
+ fullPath = helpers.getFullPathFromShortPath(mocksBaseName);
361
+ }
362
+
363
+ jsonFromData =
364
+ global.state.fileContents[`${fullPath}.${mocks.extension[0]}`] ||
365
+ global.state.fileContents[`${fullPath}.${mocks.extension[1]}`];
366
+
367
+ if (jsonFromData) {
368
+ if (variation?.startsWith("/$defs/") && "$defs" in jsonFromData) {
369
+ const defRef = variation.replace("/$defs/", "");
370
+
371
+ if (defRef in jsonFromData.$defs) {
372
+ return {
373
+ data: jsonFromData.$defs[defRef],
374
+ };
375
+ }
376
+
377
+ return {
378
+ message: {
379
+ type: "warn",
380
+ text: t("fileNotFoundLinkIncorrect")
381
+ .replace("{{filePath}}", ref)
382
+ .replace(
383
+ "{{component}}",
384
+ component.paths.mocks.short(
385
+ global.config.files.mocks.extension[0],
386
+ ) ||
387
+ component.paths.mocks.short(
388
+ global.config.files.mocks.extension[1],
389
+ ),
390
+ ),
391
+ },
392
+ data: {},
393
+ };
394
+ }
395
+
396
+ const embeddedJson = jsonFromData;
397
+ const rootJson = helpers.removeInternalKeys(embeddedJson);
398
+ let variantJson = {};
399
+ let message;
400
+
401
+ if (variation && embeddedJson.$variants && embeddedJson.$variants.length) {
402
+ const variant = embeddedJson.$variants.find((vari) => {
403
+ if (vari.$name) {
404
+ return (
405
+ helpers.normalizeString(vari.$name) ===
406
+ helpers.normalizeString(variation)
407
+ );
408
+ }
409
+ return false;
410
+ });
411
+
412
+ if (variant) {
413
+ variantJson = helpers.removeInternalKeys(variant);
414
+ } else {
415
+ message = {
416
+ type: "warn",
417
+ text: t("variationNotFound")
418
+ .replace("{{variation}}", variation)
419
+ .replace("{{fileName}}", mocksBaseName),
420
+ };
421
+ }
422
+ }
423
+
424
+ return {
425
+ message,
426
+ data: mergeRootDataWithVariationData(
427
+ helpers.cloneDeep(rootJson),
428
+ variantJson,
429
+ ),
430
+ };
431
+ }
432
+
433
+ return {
434
+ message: {
435
+ type: "warn",
436
+ text: t("fileNotFoundLinkIncorrect")
437
+ .replace("{{filePath}}", mocksBaseName)
438
+ .replace(
439
+ "{{component}}",
440
+ component.paths.mocks.short(global.config.files.mocks.extension[0]) ||
441
+ component.paths.mocks.short(global.config.files.mocks.extension[1]),
442
+ ),
443
+ },
444
+ data: {},
445
+ };
446
+ }
447
+
448
+ /**
449
+ * @param {string} namespacedPath
450
+ * @returns {object}
451
+ */
452
+ function resolveNamespace(namespacedPath) {
453
+ if (
454
+ !Object.keys(global.config.namespaces).some((namespace) =>
455
+ namespacedPath.startsWith(namespace),
456
+ )
457
+ ) {
458
+ return {
459
+ resolved: namespacedPath,
460
+ };
461
+ }
462
+
463
+ const namespace = namespacedPath.split("/")[0];
464
+ const resolvedNamespace = global.config.namespaces[namespace];
465
+
466
+ if (!resolvedNamespace) {
467
+ return {
468
+ message: {
469
+ type: "warn",
470
+ text: `Could not resolve namespace ${namespace}`,
471
+ },
472
+ resolved: null,
473
+ };
474
+ }
475
+
476
+ return {
477
+ resolved: namespacedPath.replace(
478
+ namespace,
479
+ helpers.getShortPathFromFullPath(
480
+ path.join(process.cwd(), resolvedNamespace),
481
+ ),
482
+ ),
483
+ };
484
+ }
@@ -0,0 +1,246 @@
1
+ import fs from "fs";
2
+ import path from "node:path";
3
+
4
+ import * as helpers from "../../helpers.js";
5
+ import { t } from "../../i18n/index.js";
6
+ import { extendTemplateData } from "../../render/helpers.js";
7
+ import { overwriteRenderKey } from "../resolve.js";
8
+
9
+ /**
10
+ * @param {object|Array|string|boolean} entry - a value from the mock data object
11
+ * @returns {Promise<{ messages, data }>} the resolved value from the mock data object
12
+ */
13
+ export const resolveTpls = async function (entry) {
14
+ const messages = [];
15
+
16
+ if (entry) {
17
+ if (
18
+ typeof entry === "string" ||
19
+ typeof entry === "number" ||
20
+ typeof entry === "boolean" ||
21
+ entry instanceof Map ||
22
+ entry === null
23
+ ) {
24
+ return {
25
+ messages,
26
+ data: entry,
27
+ };
28
+ }
29
+
30
+ if (entry instanceof Array) {
31
+ const o = [];
32
+
33
+ for (const ent of entry) {
34
+ const json = await resolve(ent);
35
+ const result = await resolveTpls(json.data);
36
+
37
+ o.push(result.data);
38
+
39
+ for (const msg of [...json.messages, ...result.messages]) {
40
+ if (msg) {
41
+ messages.push(msg);
42
+ }
43
+ }
44
+ }
45
+
46
+ return {
47
+ messages,
48
+ data: o,
49
+ };
50
+ }
51
+
52
+ const o = { ...entry };
53
+
54
+ for (const key of Object.keys(o)) {
55
+ const tpl = await resolve(o[key]);
56
+ const tpls = await resolveTpls(tpl.data);
57
+
58
+ for (const msg of [...tpl.messages, ...tpls.messages]) {
59
+ if (msg) {
60
+ messages.push(msg);
61
+ }
62
+ }
63
+
64
+ o[key] = tpls.data;
65
+ }
66
+
67
+ return {
68
+ messages,
69
+ data: o,
70
+ };
71
+ }
72
+
73
+ return {
74
+ messages,
75
+ data: entry,
76
+ };
77
+ };
78
+
79
+ /**
80
+ * @param {object|Array|string|boolean} entry
81
+ * @returns {Promise<{ messages, data }>}
82
+ */
83
+ async function resolve(entry) {
84
+ const messages = [];
85
+
86
+ if (entry !== null) {
87
+ if (Array.isArray(entry)) {
88
+ const arr = [];
89
+
90
+ for (const ent of entry) {
91
+ const result = await resolve(ent);
92
+
93
+ arr.push(result.data);
94
+
95
+ for (const msg of messages) {
96
+ if (msg) {
97
+ messages.push();
98
+ }
99
+ }
100
+ }
101
+
102
+ return {
103
+ messages,
104
+ data: arr,
105
+ };
106
+ }
107
+
108
+ if (
109
+ typeof entry === "string" ||
110
+ typeof entry === "number" ||
111
+ typeof entry === "boolean" ||
112
+ entry instanceof Map
113
+ ) {
114
+ return {
115
+ messages,
116
+ data: entry,
117
+ };
118
+ }
119
+
120
+ let entries = { ...entry };
121
+
122
+ for (const [key, val] of Object.entries(entries)) {
123
+ if (key !== "$tpl") {
124
+ const result = await resolve(val);
125
+
126
+ entries[key] = result.data;
127
+ }
128
+ }
129
+
130
+ const result = await render(entries);
131
+
132
+ return {
133
+ messages: [...messages, ...result.messages],
134
+ data: result.data,
135
+ };
136
+ }
137
+
138
+ return {
139
+ messages,
140
+ data: entry,
141
+ };
142
+ }
143
+
144
+ /**
145
+ * @param {object|Array|string|boolean} entry
146
+ * @returns {Promise<{ messages, data }>}
147
+ */
148
+ async function render(entry) {
149
+ const messages = [];
150
+
151
+ if (entry.$tpl) {
152
+ let data = { ...entry };
153
+ delete data.$tpl;
154
+ let filePath;
155
+ let shortPath;
156
+ let component;
157
+
158
+ if (entry.$tpl.startsWith("@")) {
159
+ const namespace = entry.$tpl.split("/")[0];
160
+ const resolvedNamespace = global.config.namespaces[namespace];
161
+
162
+ if (!resolvedNamespace) {
163
+ return {
164
+ messages: [
165
+ {
166
+ type: "warn",
167
+ text: `Could not resolve namespace ${namespace}`,
168
+ },
169
+ ],
170
+ data: null,
171
+ };
172
+ }
173
+
174
+ shortPath = helpers.getShortPathFromFullPath(
175
+ path.join(
176
+ process.cwd(),
177
+ entry.$tpl.replace(namespace, resolvedNamespace),
178
+ ),
179
+ );
180
+ } else {
181
+ shortPath = entry.$tpl;
182
+
183
+ if (shortPath.startsWith("/")) {
184
+ shortPath = shortPath.replace("/", "");
185
+ }
186
+ }
187
+
188
+ component = global.state.routes.find(
189
+ ({ alias }) => alias === shortPath || `/${alias}` === shortPath,
190
+ );
191
+
192
+ if (!component) {
193
+ messages.push({
194
+ type: "error",
195
+ text: t("templateDoesNotExist").replace("{{template}}", entry.$tpl),
196
+ });
197
+
198
+ return {
199
+ messages,
200
+ data: null,
201
+ };
202
+ }
203
+
204
+ try {
205
+ fs.statSync(component.paths.tpl.full);
206
+
207
+ data = await extendTemplateData(global.config, data, component);
208
+
209
+ return new Promise((resolve1) => {
210
+ global.app.render(component.paths.tpl.full, data ?? {}, (err, html) => {
211
+ if (err) {
212
+ messages.push({
213
+ type: "warn",
214
+ text: t("renderingTemplateFailed").replace(
215
+ "{{filePath}}",
216
+ component.paths.tpl.short,
217
+ ),
218
+ });
219
+ }
220
+
221
+ resolve1({
222
+ messages,
223
+ data: html,
224
+ });
225
+ });
226
+ });
227
+ } catch (err) {
228
+ if (err.code === "ENOENT") {
229
+ messages.push({
230
+ type: "error",
231
+ text: t("templateDoesNotExist").replace("{{template}}", filePath),
232
+ verbose: err,
233
+ });
234
+ return {
235
+ messages,
236
+ data: null,
237
+ };
238
+ }
239
+ }
240
+ } else {
241
+ return {
242
+ messages,
243
+ data: overwriteRenderKey(entry),
244
+ };
245
+ }
246
+ }