@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.
- package/LICENSE.md +21 -0
- package/README.md +43 -0
- package/api/app.js +39 -0
- package/api/index.js +236 -0
- package/bin/miyagi.js +2 -0
- package/dist/css/iframe.css +31 -0
- package/dist/css/main.css +1 -0
- package/dist/js/_iframe-links-DdifIr4P.js +1 -0
- package/dist/js/_mock-data-Dypo4Bl_.js +1 -0
- package/dist/js/_prism-By3NMwUd.js +1 -0
- package/dist/js/iframe.build.js +1 -0
- package/dist/js/iframe.js +1 -0
- package/dist/js/index-BKDKaBC6.js +1 -0
- package/dist/js/jsontree.js +1 -0
- package/dist/js/main.build.js +1 -0
- package/dist/js/main.js +1 -0
- package/frontend/assets/css/iframe/accordion-tabs.css +77 -0
- package/frontend/assets/css/iframe/jsontree.js.css +325 -0
- package/frontend/assets/css/iframe/prism.css +132 -0
- package/frontend/assets/css/iframe/styleguide/colors.css +61 -0
- package/frontend/assets/css/iframe/styleguide/fonts.css +37 -0
- package/frontend/assets/css/iframe/styleguide/index.css +109 -0
- package/frontend/assets/css/iframe/styleguide/spacings.css +21 -0
- package/frontend/assets/css/iframe.css +410 -0
- package/frontend/assets/css/main/menu/config-switcher.css +49 -0
- package/frontend/assets/css/main/menu/config-switchers.css +67 -0
- package/frontend/assets/css/main/menu/goto.css +24 -0
- package/frontend/assets/css/main/menu/nav.css +113 -0
- package/frontend/assets/css/main/menu/search.css +64 -0
- package/frontend/assets/css/main/menu/title.css +40 -0
- package/frontend/assets/css/main/menu.css +114 -0
- package/frontend/assets/css/main/reset.css +217 -0
- package/frontend/assets/css/main.css +71 -0
- package/frontend/assets/css/shared.css +34 -0
- package/frontend/assets/css/tokens.css +112 -0
- package/frontend/assets/favicon.ico +0 -0
- package/frontend/assets/js/_accordion-tabs.js +403 -0
- package/frontend/assets/js/_goto.js +63 -0
- package/frontend/assets/js/_iframe-links.js +19 -0
- package/frontend/assets/js/_is-triggered.js +15 -0
- package/frontend/assets/js/_main.js +379 -0
- package/frontend/assets/js/_mock-data.js +13 -0
- package/frontend/assets/js/_prism.js +1098 -0
- package/frontend/assets/js/_search.js +190 -0
- package/frontend/assets/js/_socket.js +9 -0
- package/frontend/assets/js/config-switcher/development-mode.js +49 -0
- package/frontend/assets/js/config-switcher/index.js +63 -0
- package/frontend/assets/js/config-switcher/text-direction.js +30 -0
- package/frontend/assets/js/config-switcher/theme.js +87 -0
- package/frontend/assets/js/iframe.build.js +43 -0
- package/frontend/assets/js/iframe.js +52 -0
- package/frontend/assets/js/jsontree.js +979 -0
- package/frontend/assets/js/main.build.js +40 -0
- package/frontend/assets/js/main.js +42 -0
- package/frontend/assets/js/styleguide/color-converter.js +741 -0
- package/frontend/assets/js/styleguide/index.js +119 -0
- package/frontend/views/component_variation.twig.miyagi +57 -0
- package/frontend/views/design-tokens/colors.twig.miyagi +43 -0
- package/frontend/views/design-tokens/sizes.twig.miyagi +35 -0
- package/frontend/views/design-tokens/typography.twig.miyagi +38 -0
- package/frontend/views/iframe_component.twig.miyagi +141 -0
- package/frontend/views/iframe_component_variation.twig.miyagi +55 -0
- package/frontend/views/iframe_index.twig.miyagi +14 -0
- package/frontend/views/layouts/iframe_default.twig.miyagi +22 -0
- package/frontend/views/main.twig.miyagi +24 -0
- package/frontend/views/menu/config-switchers.twig.miyagi +83 -0
- package/frontend/views/menu/goto.twig.miyagi +9 -0
- package/frontend/views/menu/menu.twig.miyagi +21 -0
- package/frontend/views/menu/nav.twig.miyagi +95 -0
- package/frontend/views/menu/search.twig.miyagi +13 -0
- package/frontend/views/menu/title.twig.miyagi +24 -0
- package/index.js +3 -0
- package/lib/build/index.js +1020 -0
- package/lib/cli/app.js +38 -0
- package/lib/cli/component.js +56 -0
- package/lib/cli/index.js +5 -0
- package/lib/cli/lint.js +180 -0
- package/lib/config.js +74 -0
- package/lib/default-config.js +105 -0
- package/lib/generator/component.js +199 -0
- package/lib/generator/mocks.js +201 -0
- package/lib/helpers.js +184 -0
- package/lib/i18n/en.js +91 -0
- package/lib/i18n/index.js +17 -0
- package/lib/index.js +166 -0
- package/lib/init/args.js +55 -0
- package/lib/init/config.js +330 -0
- package/lib/init/engines.js +65 -0
- package/lib/init/index.js +102 -0
- package/lib/init/rendering.js +12 -0
- package/lib/init/router.js +249 -0
- package/lib/init/static.js +133 -0
- package/lib/init/twing/cache.js +34 -0
- package/lib/init/twing/functions.js +51 -0
- package/lib/init/views.js +19 -0
- package/lib/init/watcher.js +402 -0
- package/lib/logger.js +94 -0
- package/lib/mocks/get.js +111 -0
- package/lib/mocks/index.js +9 -0
- package/lib/mocks/resolve/ref.js +484 -0
- package/lib/mocks/resolve/tpl.js +246 -0
- package/lib/mocks/resolve.js +205 -0
- package/lib/render/helpers.js +51 -0
- package/lib/render/index.js +38 -0
- package/lib/render/views/iframe/component.docs.js +77 -0
- package/lib/render/views/iframe/component.js +338 -0
- package/lib/render/views/iframe/design-tokens/colors.js +52 -0
- package/lib/render/views/iframe/design-tokens/index.js +9 -0
- package/lib/render/views/iframe/design-tokens/sizes.js +49 -0
- package/lib/render/views/iframe/design-tokens/typography.js +52 -0
- package/lib/render/views/iframe/docs.js +68 -0
- package/lib/render/views/iframe/index.js +44 -0
- package/lib/render/views/iframe/variation.js +116 -0
- package/lib/render/views/iframe/variation.standalone.js +89 -0
- package/lib/render/views/main/component.docs.js +53 -0
- package/lib/render/views/main/component.js +74 -0
- package/lib/render/views/main/design-tokens.js +53 -0
- package/lib/render/views/main/docs.js +47 -0
- package/lib/render/views/main/index.js +46 -0
- package/lib/state/components.js +132 -0
- package/lib/state/css.js +50 -0
- package/lib/state/docs.js +111 -0
- package/lib/state/file-contents.js +207 -0
- package/lib/state/helpers.js +86 -0
- package/lib/state/index.js +56 -0
- package/lib/state/menu/index.js +275 -0
- package/lib/state/menu/structure.js +146 -0
- package/lib/state/partials.js +23 -0
- package/lib/state/source-tree.js +75 -0
- package/lib/styleguide/color-names.js +150 -0
- package/lib/styleguide/colors.js +135 -0
- package/lib/styleguide/helpers.js +37 -0
- package/lib/styleguide/index.js +17 -0
- package/lib/styleguide/media-queries.js +26 -0
- package/lib/styleguide/spacings.js +35 -0
- package/lib/styleguide/typography.js +61 -0
- package/lib/validator/mocks.js +105 -0
- package/package.json +117 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import deepMerge from "deepmerge";
|
|
2
|
+
import * as helpers from "../helpers.js";
|
|
3
|
+
import { resolveRefs } from "./resolve/ref.js";
|
|
4
|
+
import { resolveTpls } from "./resolve/tpl.js";
|
|
5
|
+
import { extendTemplateData } from "../render/helpers.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {string} method
|
|
9
|
+
* @returns {Function}
|
|
10
|
+
*/
|
|
11
|
+
function getMergeMethod(method) {
|
|
12
|
+
const methods = {
|
|
13
|
+
combine: (target, source, options) => {
|
|
14
|
+
const destination = target.slice();
|
|
15
|
+
|
|
16
|
+
source.forEach((item, index) => {
|
|
17
|
+
if (options.isMergeableObject(item)) {
|
|
18
|
+
if (typeof destination[index] === "undefined") {
|
|
19
|
+
destination[index] = options.cloneUnlessOtherwiseSpecified(
|
|
20
|
+
item,
|
|
21
|
+
options,
|
|
22
|
+
);
|
|
23
|
+
} else {
|
|
24
|
+
destination[index] = deepMerge(target[index], item, options);
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
destination[index] = options.cloneUnlessOtherwiseSpecified(
|
|
28
|
+
item,
|
|
29
|
+
options,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return destination;
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
overwrite: (destinationArray, sourceArray) => sourceArray,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return methods[method];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {object} data - the mock data object that will be passed into the component
|
|
45
|
+
* @param {object} component
|
|
46
|
+
* @param {object} [rootData] - the root mock data object
|
|
47
|
+
* @returns {Promise<{ merged, resolved, messages }>} the resolved data object
|
|
48
|
+
*/
|
|
49
|
+
export const resolveData = async function (data, component, rootData) {
|
|
50
|
+
const mergedWithGlobalData = mergeWithGlobalData(
|
|
51
|
+
rootData ? mergeRootDataWithVariationData(rootData, data) : data,
|
|
52
|
+
);
|
|
53
|
+
const { data: refsResolved, messages: refMessages } = await resolveRefs(
|
|
54
|
+
{ ...mergedWithGlobalData },
|
|
55
|
+
component,
|
|
56
|
+
);
|
|
57
|
+
const extended = await extendTemplateData(
|
|
58
|
+
global.config,
|
|
59
|
+
refsResolved,
|
|
60
|
+
component,
|
|
61
|
+
);
|
|
62
|
+
const { data: tplsResolved, messages: tplMessages } =
|
|
63
|
+
await resolveTpls(extended);
|
|
64
|
+
const resolved = overwriteRenderKey(tplsResolved);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
merged: mergedWithGlobalData,
|
|
68
|
+
resolved,
|
|
69
|
+
messages: [...refMessages, ...tplMessages],
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {object} data - the mock data object that will be passed into the component
|
|
75
|
+
* @returns {object} the resolved data object
|
|
76
|
+
*/
|
|
77
|
+
export const overwriteRenderKey = function (data) {
|
|
78
|
+
let o;
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
["string", "number", "boolean"].includes(typeof data) ||
|
|
82
|
+
data instanceof Map
|
|
83
|
+
) {
|
|
84
|
+
return data;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (Array.isArray(data)) {
|
|
88
|
+
for (let item of data) {
|
|
89
|
+
item = overwriteRenderKey(item);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return data;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (data) {
|
|
96
|
+
o = { ...data };
|
|
97
|
+
const entries = Object.entries(o);
|
|
98
|
+
|
|
99
|
+
for (const [key, val] of entries) {
|
|
100
|
+
if (key === "$render") {
|
|
101
|
+
let str = "";
|
|
102
|
+
|
|
103
|
+
if (val) {
|
|
104
|
+
for (const html of val) {
|
|
105
|
+
str += html;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
o = str;
|
|
110
|
+
} else {
|
|
111
|
+
if (
|
|
112
|
+
typeof val == "string" ||
|
|
113
|
+
typeof val === "number" ||
|
|
114
|
+
typeof val === "boolean" ||
|
|
115
|
+
val instanceof Map ||
|
|
116
|
+
val === null
|
|
117
|
+
) {
|
|
118
|
+
o[key] = val;
|
|
119
|
+
} else if (Array.isArray(val)) {
|
|
120
|
+
val.forEach((v, i) => {
|
|
121
|
+
if (
|
|
122
|
+
typeof v == "string" ||
|
|
123
|
+
typeof v === "number" ||
|
|
124
|
+
typeof v === "boolean" ||
|
|
125
|
+
v instanceof Map ||
|
|
126
|
+
v === null
|
|
127
|
+
) {
|
|
128
|
+
o[key][i] = v;
|
|
129
|
+
} else {
|
|
130
|
+
o[key][i] = overwriteRenderKey(v);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
o[key] = overwriteRenderKey(val);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return o;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {object} rootData - the root mock data of a component
|
|
145
|
+
* @param {object} variationData - a variation mock data of a component
|
|
146
|
+
* @returns {object} the merged data
|
|
147
|
+
*/
|
|
148
|
+
export const mergeRootDataWithVariationData = function (
|
|
149
|
+
rootData,
|
|
150
|
+
variationData,
|
|
151
|
+
) {
|
|
152
|
+
if (!rootData) {
|
|
153
|
+
return variationData;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!variationData) {
|
|
157
|
+
return rootData;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const merged = deepMerge(rootData, variationData, {
|
|
161
|
+
customMerge: (key) => {
|
|
162
|
+
const options = variationData.$opts || rootData.$opts;
|
|
163
|
+
|
|
164
|
+
if (options) {
|
|
165
|
+
const option = options[key];
|
|
166
|
+
|
|
167
|
+
if (option) {
|
|
168
|
+
return getMergeMethod(option);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return undefined;
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (merged.$opts) {
|
|
177
|
+
delete merged.$opts;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return merged;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @param {object} data - the mock data object that will be passed into the component
|
|
185
|
+
* @returns {object} the merged data object
|
|
186
|
+
*/
|
|
187
|
+
function mergeWithGlobalData(data) {
|
|
188
|
+
const defaultFile = helpers.getFullPathFromShortPath(
|
|
189
|
+
`${global.config.files.mocks.name}.${global.config.files.mocks.extension[0]}`,
|
|
190
|
+
);
|
|
191
|
+
const jsFile = helpers.getFullPathFromShortPath(
|
|
192
|
+
`${global.config.files.mocks.name}.${global.config.files.mocks.extension[1]}`,
|
|
193
|
+
);
|
|
194
|
+
const globalData = {
|
|
195
|
+
...(global.state.fileContents[defaultFile] ||
|
|
196
|
+
global.state.fileContents[jsFile]),
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
delete globalData.$defs;
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
...globalData,
|
|
203
|
+
...data,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for the render module
|
|
3
|
+
* @module renderHelpers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from "path";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {object} config - the user configuration object
|
|
10
|
+
* @param {object} data - the mock data object that will be passed into the component
|
|
11
|
+
* @param {object} component
|
|
12
|
+
* @returns {Promise<object>} the extended data object
|
|
13
|
+
*/
|
|
14
|
+
export const extendTemplateData = async (config, data, component) => {
|
|
15
|
+
for (const extension of config.extensions) {
|
|
16
|
+
if (extension) {
|
|
17
|
+
const ext = Array.isArray(extension) ? extension[0] : extension;
|
|
18
|
+
|
|
19
|
+
if (ext.extendTemplateData) {
|
|
20
|
+
data = await ext.extendTemplateData(
|
|
21
|
+
path.join(config.components.folder, component.paths.tpl.short),
|
|
22
|
+
{},
|
|
23
|
+
data,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return data;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const getUserUiConfig = (cookies = {}) => {
|
|
33
|
+
const projectName = global.config.projectName.replaceAll(" ", "-");
|
|
34
|
+
const mode = cookies[`miyagi_${projectName}_mode`];
|
|
35
|
+
const theme = cookies[`miyagi_${projectName}_theme`];
|
|
36
|
+
const componentTextDirection =
|
|
37
|
+
cookies[`miyagi_${projectName}_text_direction`];
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
mode: global.config.isBuild ? "presentation" : mode || "dev",
|
|
41
|
+
theme: theme || global.config.ui.mode,
|
|
42
|
+
componentTextDirection:
|
|
43
|
+
componentTextDirection || global.config.components.textDirection,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const getThemeMode = (cookies = {}) => {
|
|
48
|
+
return cookies[
|
|
49
|
+
`miyagi_${global.config.projectName.replaceAll(" ", "-")}_theme`
|
|
50
|
+
];
|
|
51
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering module
|
|
3
|
+
* @module render
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import renderIframeComponent from "./views/iframe/component.js";
|
|
7
|
+
import renderIframeComponentDocs from "./views/iframe/component.docs.js";
|
|
8
|
+
import renderIframeDocs from "./views/iframe/docs.js";
|
|
9
|
+
import renderIframeIndex from "./views/iframe/index.js";
|
|
10
|
+
import renderIframeVariation from "./views/iframe/variation.js";
|
|
11
|
+
import renderIframeVariationStandalone from "./views/iframe/variation.standalone.js";
|
|
12
|
+
import renderMainComponent from "./views/main/component.js";
|
|
13
|
+
import renderMainComponentDocs from "./views/main/component.docs.js";
|
|
14
|
+
import renderMainDocs from "./views/main/docs.js";
|
|
15
|
+
import renderMainIndex from "./views/main/index.js";
|
|
16
|
+
import iframeDesignTokens from "./views/iframe/design-tokens/index.js";
|
|
17
|
+
import renderMainDesignTokens from "./views/main/design-tokens.js";
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
renderMainIndex,
|
|
21
|
+
renderMainComponent,
|
|
22
|
+
renderMainComponentDocs,
|
|
23
|
+
renderMainDocs,
|
|
24
|
+
renderIframeVariation,
|
|
25
|
+
renderIframeVariationStandalone,
|
|
26
|
+
renderIframeComponent,
|
|
27
|
+
renderIframeComponentDocs,
|
|
28
|
+
renderIframeDocs,
|
|
29
|
+
renderIframeIndex,
|
|
30
|
+
renderMainDesignTokens,
|
|
31
|
+
iframe: {
|
|
32
|
+
designTokens: {
|
|
33
|
+
colors: iframeDesignTokens.colors,
|
|
34
|
+
sizes: iframeDesignTokens.sizes,
|
|
35
|
+
typography: iframeDesignTokens.typography,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import config from "../../../default-config.js";
|
|
3
|
+
import { getUserUiConfig, getThemeMode } from "../../helpers.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {object} object - parameter object
|
|
7
|
+
* @param {object} object.res - the express response object
|
|
8
|
+
* @param {object} object.component
|
|
9
|
+
* @param {Function} [object.cb] - callback function
|
|
10
|
+
* @param {object} [object.cookies]
|
|
11
|
+
*/
|
|
12
|
+
export default async function renderIframeComponentDocs({
|
|
13
|
+
res,
|
|
14
|
+
component,
|
|
15
|
+
cb,
|
|
16
|
+
cookies,
|
|
17
|
+
}) {
|
|
18
|
+
const componentDocumentation =
|
|
19
|
+
global.state.fileContents[component.paths.docs.full];
|
|
20
|
+
const componentName = getHeadlineFromFileName(
|
|
21
|
+
path.basename(
|
|
22
|
+
component.paths.docs.full,
|
|
23
|
+
path.extname(component.paths.docs.full),
|
|
24
|
+
),
|
|
25
|
+
);
|
|
26
|
+
const themeMode = getThemeMode(cookies);
|
|
27
|
+
|
|
28
|
+
await res.render(
|
|
29
|
+
"iframe_component.twig.miyagi",
|
|
30
|
+
{
|
|
31
|
+
lang: global.config.ui.lang,
|
|
32
|
+
miyagiDev: !!process.env.MIYAGI_DEVELOPMENT,
|
|
33
|
+
prod: process.env.NODE_ENV === "production",
|
|
34
|
+
projectName: config.projectName,
|
|
35
|
+
userProjectName: global.config.projectName,
|
|
36
|
+
isBuild: global.config.isBuild,
|
|
37
|
+
userUiConfig: getUserUiConfig(cookies),
|
|
38
|
+
theme: themeMode
|
|
39
|
+
? Object.assign(global.config.ui.theme, { mode: themeMode })
|
|
40
|
+
: global.config.ui.theme,
|
|
41
|
+
documentation: componentDocumentation,
|
|
42
|
+
name: componentDocumentation?.includes("<h1") ? null : componentName,
|
|
43
|
+
uiTextDirection: global.config.ui.textDirection,
|
|
44
|
+
},
|
|
45
|
+
(html) => {
|
|
46
|
+
if (res.send) {
|
|
47
|
+
res.send(html);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (cb) {
|
|
51
|
+
cb(null, html);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {string} file
|
|
59
|
+
* @returns {string}
|
|
60
|
+
*/
|
|
61
|
+
function getHeadlineFromFileName(file) {
|
|
62
|
+
if (typeof file !== "string") return "";
|
|
63
|
+
|
|
64
|
+
let fileName = file;
|
|
65
|
+
|
|
66
|
+
if (fileName.startsWith("/")) {
|
|
67
|
+
fileName = fileName.slice(1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (file.endsWith("README.md") || file.endsWith("index.md")) {
|
|
71
|
+
fileName = path.dirname(fileName);
|
|
72
|
+
} else {
|
|
73
|
+
fileName = path.basename(fileName, ".md");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return fileName.replaceAll("-", " ");
|
|
77
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import jsonToYaml from "js-yaml";
|
|
3
|
+
import config from "../../../default-config.js";
|
|
4
|
+
import { t } from "../../../i18n/index.js";
|
|
5
|
+
import * as helpers from "../../../helpers.js";
|
|
6
|
+
import validateMocks from "../../../validator/mocks.js";
|
|
7
|
+
import { getComponentData } from "../../../mocks/index.js";
|
|
8
|
+
import { getUserUiConfig, getThemeMode } from "../../helpers.js";
|
|
9
|
+
import log from "../../../logger.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {object} object - parameter object
|
|
13
|
+
* @param {object} object.res - the express response object
|
|
14
|
+
* @param {object} object.component
|
|
15
|
+
* @param {Function} [object.cb] - callback function
|
|
16
|
+
* @param {object} [object.cookies]
|
|
17
|
+
* @param {boolean} [object.noCli]
|
|
18
|
+
*/
|
|
19
|
+
export default async function renderIframeComponent({
|
|
20
|
+
res,
|
|
21
|
+
component,
|
|
22
|
+
cb,
|
|
23
|
+
cookies,
|
|
24
|
+
noCli,
|
|
25
|
+
}) {
|
|
26
|
+
const hasTemplate =
|
|
27
|
+
component.paths.tpl &&
|
|
28
|
+
Object.values(global.state.partials).includes(component.paths.tpl.full);
|
|
29
|
+
const componentJson = (await getComponentData(component)) || [];
|
|
30
|
+
const componentDocumentation =
|
|
31
|
+
global.state.fileContents[path.join(component.paths.dir.full, "README.md")];
|
|
32
|
+
const componentSchema =
|
|
33
|
+
global.state.fileContents[component.paths.schema.full];
|
|
34
|
+
const componentTemplate = hasTemplate
|
|
35
|
+
? global.state.fileContents[component.paths.tpl.full]
|
|
36
|
+
: null;
|
|
37
|
+
|
|
38
|
+
let mockFilePath;
|
|
39
|
+
|
|
40
|
+
const defaultMockDataPath = component.paths.mocks.full(
|
|
41
|
+
global.config.files.mocks.extension[0],
|
|
42
|
+
);
|
|
43
|
+
const jsMockDataPath = component.paths.mocks.full(
|
|
44
|
+
global.config.files.mocks.extension[1],
|
|
45
|
+
);
|
|
46
|
+
const jsMockData = global.state.fileContents[jsMockDataPath];
|
|
47
|
+
|
|
48
|
+
if (jsMockData) {
|
|
49
|
+
mockFilePath = jsMockDataPath;
|
|
50
|
+
} else {
|
|
51
|
+
mockFilePath = defaultMockDataPath;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const componentMocks = global.state.fileContents[mockFilePath];
|
|
55
|
+
|
|
56
|
+
let componentSchemaString;
|
|
57
|
+
if (componentSchema) {
|
|
58
|
+
if (["yaml", "yml"].includes(global.config.files.schema.extension)) {
|
|
59
|
+
componentSchemaString = jsonToYaml.dump(componentSchema);
|
|
60
|
+
} else {
|
|
61
|
+
componentSchemaString = JSON.stringify(componentSchema, null, 2);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let componentMocksString;
|
|
66
|
+
if (componentMocks) {
|
|
67
|
+
if (["yaml", "yml"].includes(global.config.files.mocks.extension[0])) {
|
|
68
|
+
componentMocksString = jsonToYaml.dump(componentMocks);
|
|
69
|
+
} else {
|
|
70
|
+
componentMocksString = JSON.stringify(componentMocks, null, 2);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const fileContents = {
|
|
75
|
+
schema: componentSchema
|
|
76
|
+
? {
|
|
77
|
+
string: componentSchemaString,
|
|
78
|
+
type: global.config.files.schema.extension,
|
|
79
|
+
file: path.join(
|
|
80
|
+
global.config.components.folder,
|
|
81
|
+
component.paths.schema.short,
|
|
82
|
+
),
|
|
83
|
+
}
|
|
84
|
+
: null,
|
|
85
|
+
mocks: componentMocks
|
|
86
|
+
? {
|
|
87
|
+
string: componentMocksString,
|
|
88
|
+
type: global.config.files.mocks.extension[0],
|
|
89
|
+
file: path.join(
|
|
90
|
+
global.config.components.folder,
|
|
91
|
+
helpers.getShortPathFromFullPath(mockFilePath),
|
|
92
|
+
),
|
|
93
|
+
}
|
|
94
|
+
: null,
|
|
95
|
+
template: componentTemplate
|
|
96
|
+
? {
|
|
97
|
+
string: componentTemplate,
|
|
98
|
+
type: global.config.files.templates.extension,
|
|
99
|
+
file: path.join(
|
|
100
|
+
global.config.components.folder,
|
|
101
|
+
helpers.getShortPathFromFullPath(component.paths.tpl.full),
|
|
102
|
+
),
|
|
103
|
+
}
|
|
104
|
+
: null,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
await renderVariations({
|
|
108
|
+
res,
|
|
109
|
+
component,
|
|
110
|
+
context: componentJson.filter((entry) => entry !== null),
|
|
111
|
+
componentDocumentation,
|
|
112
|
+
fileContents,
|
|
113
|
+
name: component.name,
|
|
114
|
+
cb,
|
|
115
|
+
templateFilePath: hasTemplate ? component.paths.tpl.full : null,
|
|
116
|
+
cookies,
|
|
117
|
+
noCli,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @typedef {object} FileContents
|
|
123
|
+
* @property {object} schema - schema object
|
|
124
|
+
* @property {string} schema.string - string with schema
|
|
125
|
+
* @property {("yaml"|"yml"|"json")} schema.type - the file type of the schema file
|
|
126
|
+
* @property {boolean} [schema.selected] - true if the schema tab should initially be visible
|
|
127
|
+
* @property {string} schema.file - the schema file path
|
|
128
|
+
* @property {object} mocks - mocks object
|
|
129
|
+
* @property {string} mocks.string - string with mocks
|
|
130
|
+
* @property {("yaml"|"yml"|"js"|"json")} mocks.type - the file type of the mocks file
|
|
131
|
+
* @property {boolean} [mocks.selected] - true if the mocks tab should initially be visible
|
|
132
|
+
* @property {string} mocks.file - the mock file path
|
|
133
|
+
* @property {object} template - template object
|
|
134
|
+
* @property {string} template.string - string with template
|
|
135
|
+
* @property {string} template.type - the file type of the template file
|
|
136
|
+
* @property {boolean} [template.selected] - true if the template tab should initially be visible
|
|
137
|
+
* @property {string} template.file - the template file path
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {object} object - parameter object
|
|
142
|
+
* @param {object} object.res - the express response object
|
|
143
|
+
* @param {object} object.component
|
|
144
|
+
* @param {Array} object.context - mock data for each variation
|
|
145
|
+
* @param {string} object.componentDocumentation - html string with documentation
|
|
146
|
+
* @param {FileContents} object.fileContents - file contents object
|
|
147
|
+
* @param {string} object.name - component name
|
|
148
|
+
* @param {Function} object.cb - callback function
|
|
149
|
+
* @param {string} object.templateFilePath - the absolute component file path
|
|
150
|
+
* @param {object} [object.cookies]
|
|
151
|
+
* @param {boolean} object.noCli
|
|
152
|
+
* @returns {Promise}
|
|
153
|
+
*/
|
|
154
|
+
async function renderVariations({
|
|
155
|
+
res,
|
|
156
|
+
component,
|
|
157
|
+
context,
|
|
158
|
+
componentDocumentation,
|
|
159
|
+
fileContents,
|
|
160
|
+
name,
|
|
161
|
+
cb,
|
|
162
|
+
templateFilePath,
|
|
163
|
+
cookies,
|
|
164
|
+
noCli,
|
|
165
|
+
}) {
|
|
166
|
+
const variations = [];
|
|
167
|
+
const promises = [];
|
|
168
|
+
const validatedMocks = validateMocks(component, context, noCli);
|
|
169
|
+
|
|
170
|
+
if (templateFilePath) {
|
|
171
|
+
if (context.length > 0) {
|
|
172
|
+
for (let i = 0, len = context.length; i < len; i += 1) {
|
|
173
|
+
const entry = context[i];
|
|
174
|
+
|
|
175
|
+
variations[i] = getData(
|
|
176
|
+
context[i].name,
|
|
177
|
+
component.paths.dir.short,
|
|
178
|
+
entry,
|
|
179
|
+
validatedMocks,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
promises.push(Promise.resolve());
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let schemaJson;
|
|
188
|
+
|
|
189
|
+
if (fileContents?.schema?.string) {
|
|
190
|
+
try {
|
|
191
|
+
schemaJson = JSON.stringify(jsonToYaml.load(fileContents.schema.string));
|
|
192
|
+
} catch (err) {
|
|
193
|
+
log("error", null, err);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let mocksJson;
|
|
198
|
+
|
|
199
|
+
if (fileContents?.mocks?.string) {
|
|
200
|
+
try {
|
|
201
|
+
mocksJson = JSON.stringify(jsonToYaml.load(fileContents.mocks.string));
|
|
202
|
+
} catch (err) {
|
|
203
|
+
log("error", null, err);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return Promise.all(promises)
|
|
208
|
+
.then(async () => {
|
|
209
|
+
const themeMode = getThemeMode(cookies);
|
|
210
|
+
const renderFileTabs = !!(
|
|
211
|
+
fileContents.schema ||
|
|
212
|
+
fileContents.mocks ||
|
|
213
|
+
fileContents.template
|
|
214
|
+
);
|
|
215
|
+
const componentsEntry = global.state.components.find(
|
|
216
|
+
({ shortPath }) => shortPath === component.paths.dir.short,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
await res.render(
|
|
220
|
+
"iframe_component.twig.miyagi",
|
|
221
|
+
{
|
|
222
|
+
lang: global.config.ui.lang,
|
|
223
|
+
variations,
|
|
224
|
+
cssFiles: global.config.assets.css,
|
|
225
|
+
jsFilesHead: global.config.assets.js.filter(
|
|
226
|
+
(entry) => entry.position === "head" || !entry.position,
|
|
227
|
+
),
|
|
228
|
+
jsFilesBody: global.config.assets.js.filter(
|
|
229
|
+
(entry) => entry.position === "body",
|
|
230
|
+
),
|
|
231
|
+
assets: {
|
|
232
|
+
css: componentsEntry
|
|
233
|
+
? componentsEntry.assets.css
|
|
234
|
+
? path.join("/", componentsEntry.assets.css)
|
|
235
|
+
: false
|
|
236
|
+
: false,
|
|
237
|
+
js: componentsEntry
|
|
238
|
+
? componentsEntry.assets.js
|
|
239
|
+
? path.join("/", componentsEntry.assets.js)
|
|
240
|
+
: false
|
|
241
|
+
: false,
|
|
242
|
+
},
|
|
243
|
+
miyagiDev: !!process.env.MIYAGI_DEVELOPMENT,
|
|
244
|
+
prod: process.env.NODE_ENV === "production",
|
|
245
|
+
projectName: config.projectName,
|
|
246
|
+
userProjectName: global.config.projectName,
|
|
247
|
+
isBuild: global.config.isBuild,
|
|
248
|
+
userUiConfig: getUserUiConfig(cookies),
|
|
249
|
+
theme: themeMode
|
|
250
|
+
? Object.assign(global.config.ui.theme, { mode: themeMode })
|
|
251
|
+
: global.config.ui.theme,
|
|
252
|
+
documentation: componentDocumentation,
|
|
253
|
+
schema: schemaJson,
|
|
254
|
+
schemaError:
|
|
255
|
+
validatedMocks?.length > 0 && validatedMocks[0].type === "schema"
|
|
256
|
+
? validatedMocks[0].data.map((error) => error.message).join("\n")
|
|
257
|
+
: null,
|
|
258
|
+
mocks: mocksJson,
|
|
259
|
+
template: fileContents.template,
|
|
260
|
+
renderInformation: renderFileTabs || variations.length > 0,
|
|
261
|
+
renderFileTabs,
|
|
262
|
+
folder: path.join(
|
|
263
|
+
global.config.components.folder,
|
|
264
|
+
component.paths.dir.short,
|
|
265
|
+
),
|
|
266
|
+
name: componentDocumentation?.includes("<h1>") ? null : name,
|
|
267
|
+
uiTextDirection: global.config.ui.textDirection,
|
|
268
|
+
componentLanguage: global.config.components.lang,
|
|
269
|
+
},
|
|
270
|
+
(html) => {
|
|
271
|
+
if (res.send) {
|
|
272
|
+
res.send(html);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (cb) {
|
|
276
|
+
cb(null, html);
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
);
|
|
280
|
+
})
|
|
281
|
+
.catch(() => {
|
|
282
|
+
if (cb) {
|
|
283
|
+
cb(true);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @param {string} variation
|
|
290
|
+
* @param {string} shortPath
|
|
291
|
+
* @param {object} entry
|
|
292
|
+
* @param {Array} validatedMocks
|
|
293
|
+
* @returns {object}
|
|
294
|
+
*/
|
|
295
|
+
function getData(variation, shortPath, entry, validatedMocks) {
|
|
296
|
+
let standaloneUrl;
|
|
297
|
+
|
|
298
|
+
if (global.config.isBuild) {
|
|
299
|
+
standaloneUrl = `component-${helpers.normalizeString(
|
|
300
|
+
shortPath,
|
|
301
|
+
)}-variation-${helpers.normalizeString(variation)}.html`;
|
|
302
|
+
} else {
|
|
303
|
+
standaloneUrl = `/component?file=${shortPath}&variation=${encodeURIComponent(
|
|
304
|
+
variation,
|
|
305
|
+
)}`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const data = {
|
|
309
|
+
url: global.config.isBuild
|
|
310
|
+
? `component-${helpers.normalizeString(
|
|
311
|
+
shortPath,
|
|
312
|
+
)}-variation-${helpers.normalizeString(variation)}-embedded.html`
|
|
313
|
+
: `/component?file=${shortPath}&variation=${variation}&embedded=true`,
|
|
314
|
+
file: shortPath,
|
|
315
|
+
variation,
|
|
316
|
+
normalizedVariation: helpers.normalizeString(variation),
|
|
317
|
+
standaloneUrl,
|
|
318
|
+
mockData: JSON.stringify(entry.raw),
|
|
319
|
+
mockDataResolved: JSON.stringify(entry.resolved),
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const validationEntry = validatedMocks?.find(
|
|
323
|
+
(item) => item.variant === entry.name,
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
if (validationEntry) {
|
|
327
|
+
data.mockValidation = {
|
|
328
|
+
valid: false,
|
|
329
|
+
copy: t("validator.mocks.invalid"),
|
|
330
|
+
};
|
|
331
|
+
} else {
|
|
332
|
+
data.mockValidation = {
|
|
333
|
+
valid: true,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return data;
|
|
338
|
+
}
|