kss-modern 0.0.1
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/README.md +15 -0
- package/dist/node/lib/index.d.mts +86 -0
- package/dist/node/lib/index.d.ts +86 -0
- package/dist/node/lib/index.mjs +1900 -0
- package/dist/node/lib/vite-pug/worker.d.mts +24 -0
- package/dist/node/lib/vite-pug/worker.d.ts +24 -0
- package/dist/node/lib/vite-pug/worker.mjs +107 -0
- package/dist/styleguide-assets/.vite/manifest.json +63 -0
- package/dist/styleguide-assets/assets/worker-D4V8OS0H.js +12 -0
- package/dist/styleguide-assets/browser-BSX_g1yp.js +22671 -0
- package/dist/styleguide-assets/client-BZlkq2d8.js +1 -0
- package/dist/styleguide-assets/client-fullpage-1rKiNQVN.js +62 -0
- package/dist/styleguide-assets/clipboard-DhKzwemV.js +19 -0
- package/dist/styleguide-assets/fonts/geist-mono-latin-300-normal.woff2 +0 -0
- package/dist/styleguide-assets/fonts/geist-mono-latin-400-normal.woff2 +0 -0
- package/dist/styleguide-assets/fonts/geist-mono-latin-600-normal.woff2 +0 -0
- package/dist/styleguide-assets/fonts/geist-sans-latin-300-normal.woff2 +0 -0
- package/dist/styleguide-assets/fonts/geist-sans-latin-400-normal.woff2 +0 -0
- package/dist/styleguide-assets/fonts/geist-sans-latin-600-normal.woff2 +0 -0
- package/dist/styleguide-assets/html-validator-DqoaRE1y.js +136 -0
- package/dist/styleguide-assets/icons/external-link.svg +4 -0
- package/dist/styleguide-assets/icons/figma.svg +1 -0
- package/dist/styleguide-assets/icons/phpstorm.svg +1 -0
- package/dist/styleguide-assets/icons/vscode.svg +1 -0
- package/dist/styleguide-assets/icons-oK5xUF8R.js +37 -0
- package/dist/styleguide-assets/main-B97JcbYk.js +3603 -0
- package/dist/styleguide-assets/styleguide-BQbUioN5.css +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,1900 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import { glob } from 'tinyglobby';
|
|
6
|
+
import { fromAsyncCodeToHtml } from '@shikijs/markdown-it/async';
|
|
7
|
+
import MarkdownItAsync from 'markdown-it-async';
|
|
8
|
+
import { codeToHtml } from 'shiki';
|
|
9
|
+
import { objectEntries } from '@antfu/utils';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
import { Worker } from 'node:worker_threads';
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import chokidar from 'chokidar';
|
|
14
|
+
|
|
15
|
+
function isValidHexColor(hexCode) {
|
|
16
|
+
return /^#[0-9A-F]{6}$/i.test(hexCode);
|
|
17
|
+
}
|
|
18
|
+
function shadeColor(hexColor, percent) {
|
|
19
|
+
let color = hexColor;
|
|
20
|
+
color = color.replace(/^\s*#|\s*$/g, "");
|
|
21
|
+
if (color.length === 3) {
|
|
22
|
+
color = color.replace(/(.)/g, "$1$1");
|
|
23
|
+
}
|
|
24
|
+
const hexR = color.substring(0, 2);
|
|
25
|
+
const hexG = color.substring(2, 4);
|
|
26
|
+
const hexB = color.substring(4, 6);
|
|
27
|
+
let r = Number.parseInt(hexR, 16);
|
|
28
|
+
let g = Number.parseInt(hexG, 16);
|
|
29
|
+
let b = Number.parseInt(hexB, 16);
|
|
30
|
+
if (Number.isNaN(r))
|
|
31
|
+
r = 0;
|
|
32
|
+
if (Number.isNaN(g))
|
|
33
|
+
g = 0;
|
|
34
|
+
if (Number.isNaN(b))
|
|
35
|
+
b = 0;
|
|
36
|
+
const lightness = (r * 0.299 + g * 0.587 + b * 0.114) / 255;
|
|
37
|
+
const adjust = lightness > 0.4 ? -1 : 1;
|
|
38
|
+
const newR = Math.min(255, Math.max(0, r + adjust * (percent * 255 / 100)));
|
|
39
|
+
const newG = Math.min(255, Math.max(0, g + adjust * (percent * 255 / 100)));
|
|
40
|
+
const newB = Math.min(255, Math.max(0, b + adjust * (percent * 255 / 100)));
|
|
41
|
+
const newHexRColor = `${newR.toString(16)}`.padStart(2, "0");
|
|
42
|
+
const newHexGColor = `${newG.toString(16)}`.padStart(2, "0");
|
|
43
|
+
const newHexBColor = `${newB.toString(16)}`.padStart(2, "0");
|
|
44
|
+
return `#${newHexRColor}${newHexGColor}${newHexBColor}`;
|
|
45
|
+
}
|
|
46
|
+
async function createFaviconPreviewFile(outputPath, theme) {
|
|
47
|
+
const faviconFilePath = path.resolve(outputPath, `favicon/preview.svg`);
|
|
48
|
+
const doesFileExist = await fs.pathExists(faviconFilePath);
|
|
49
|
+
if (doesFileExist)
|
|
50
|
+
return;
|
|
51
|
+
const faviconContent = `
|
|
52
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
|
53
|
+
<style>
|
|
54
|
+
.fill-theme-color { fill: ${theme.light}; }
|
|
55
|
+
.fill-theme-color-20 { fill: ${shadeColor(theme.light, 20)}; }
|
|
56
|
+
.fill-theme-color-40 { fill: ${shadeColor(theme.light, 40)}; }
|
|
57
|
+
|
|
58
|
+
${theme.dark ? `
|
|
59
|
+
@media (prefers-color-scheme: dark) {
|
|
60
|
+
.fill-theme-color { fill: ${theme.dark}; }
|
|
61
|
+
.fill-theme-color-20 { fill: ${shadeColor(theme.dark, 20)}; }
|
|
62
|
+
.fill-theme-color-40 { fill: ${shadeColor(theme.dark, 40)}; }
|
|
63
|
+
}
|
|
64
|
+
` : ""}
|
|
65
|
+
</style>
|
|
66
|
+
|
|
67
|
+
<g fill="none" fill-rule="nonzero">
|
|
68
|
+
<path class="fill-theme-color" d="M4.5 0C5.32842712 0 6 .67157287 6 1.5V13c0 1.6568542-1.34314575 3-3 3s-3-1.3431458-3-3V1.5C0 .67157287.67157287 0 1.5 0ZM3 11c-1.1045695 0-2 .8954305-2 2s.8954305 2 2 2 2-.8954305 2-2-.8954305-2-2-2Z" />
|
|
69
|
+
<path class="fill-theme-color-20" d="M7.5 12.743V4.257l1.51-1.51c.28133215-.28151251.6630087-.43967977 1.061-.43967977.3979913 0 .7796678.15816726 1.061.43967977l2.121 2.121c.2815125.28133215.4396798.6630087.4396798 1.061 0 .3979913-.1581673.77966785-.4396798 1.061L7.5 12.743Z" />
|
|
70
|
+
<path class="fill-theme-color-40" d="M6.364 16H14.5c.8284271 0 1.5-.6715729 1.5-1.5v-3c0-.8284271-.6715729-1.5-1.5-1.5h-2.136l-6 6Z" />
|
|
71
|
+
</g>
|
|
72
|
+
</svg>
|
|
73
|
+
`;
|
|
74
|
+
await fs.outputFile(faviconFilePath, faviconContent);
|
|
75
|
+
}
|
|
76
|
+
async function createFaviconFullPageFile(outputPath, theme) {
|
|
77
|
+
const faviconFilePath = path.resolve(outputPath, `favicon/fullpage.svg`);
|
|
78
|
+
const doesFileExist = await fs.pathExists(faviconFilePath);
|
|
79
|
+
if (doesFileExist)
|
|
80
|
+
return;
|
|
81
|
+
const faviconContent = `
|
|
82
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="fill-icon">
|
|
83
|
+
<style>
|
|
84
|
+
.fill-icon {
|
|
85
|
+
fill: ${theme.light};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
${theme.dark ? `
|
|
89
|
+
@media (prefers-color-scheme: dark) {
|
|
90
|
+
.fill-icon { fill: ${theme.dark}; }
|
|
91
|
+
}
|
|
92
|
+
` : ""}
|
|
93
|
+
</style>
|
|
94
|
+
|
|
95
|
+
<path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z" />
|
|
96
|
+
<path fill-rule="evenodd" d="M1.38 8.28a.87.87 0 0 1 0-.566 7.003 7.003 0 0 1 13.238.006.87.87 0 0 1 0 .566A7.003 7.003 0 0 1 1.379 8.28ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" clip-rule="evenodd" />
|
|
97
|
+
</svg>
|
|
98
|
+
`;
|
|
99
|
+
await fs.outputFile(faviconFilePath, faviconContent);
|
|
100
|
+
}
|
|
101
|
+
async function generateFaviconFiles(outputPath, theme) {
|
|
102
|
+
await fs.ensureDir(path.resolve(outputPath, "favicon"));
|
|
103
|
+
const hasLightDarkTheme = typeof theme === "object" && theme.light && theme.dark;
|
|
104
|
+
if (hasLightDarkTheme) {
|
|
105
|
+
const isValidLightColor = isValidHexColor(theme.light);
|
|
106
|
+
if (!isValidLightColor) {
|
|
107
|
+
throw new Error(`Invalid light theme color ${theme.light} provided. Please provide a valid hex color code.`);
|
|
108
|
+
}
|
|
109
|
+
const isValidDarkColor = isValidHexColor(theme.dark);
|
|
110
|
+
if (!isValidDarkColor) {
|
|
111
|
+
throw new Error(`Invalid dark theme color ${theme.dark} provided. Please provide a valid hex color code.`);
|
|
112
|
+
}
|
|
113
|
+
await Promise.all([
|
|
114
|
+
createFaviconPreviewFile(outputPath, theme),
|
|
115
|
+
createFaviconFullPageFile(outputPath, theme)
|
|
116
|
+
]);
|
|
117
|
+
} else {
|
|
118
|
+
const color = theme;
|
|
119
|
+
const isValidThemeColor = isValidHexColor(color);
|
|
120
|
+
if (!isValidThemeColor) {
|
|
121
|
+
throw new Error(`Invalid theme color ${color} provided. Please provide a valid hex color code or an object with light and dark theme colors.`);
|
|
122
|
+
}
|
|
123
|
+
await Promise.all([
|
|
124
|
+
createFaviconPreviewFile(outputPath, { light: color }),
|
|
125
|
+
createFaviconFullPageFile(outputPath, { light: color })
|
|
126
|
+
]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function log(message, style = "important") {
|
|
131
|
+
const computedMessage = style === "important" ? `\x1B[38;2;63;94;90m${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \x1B[38;2;32;252;143m[Styleguide]\x1B[0m ${message}` : `\x1B[38;2;63;94;90m${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \x1B[38;2;32;252;143m[Styleguide]\x1B[0m \x1B[38;2;63;94;90m${message}`;
|
|
132
|
+
console.log(computedMessage);
|
|
133
|
+
}
|
|
134
|
+
async function logicalWriteFile(filepath, content) {
|
|
135
|
+
const dir = path.dirname(filepath);
|
|
136
|
+
await fs.ensureDir(dir);
|
|
137
|
+
const isFileExisting = await fs.exists(filepath);
|
|
138
|
+
if (isFileExisting) {
|
|
139
|
+
const oldContent = await fs.readFile(filepath, "utf-8");
|
|
140
|
+
if (oldContent === content) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
await fs.writeFile(filepath, content);
|
|
145
|
+
}
|
|
146
|
+
function fixAccessibilityIssues(html) {
|
|
147
|
+
let parsedMarkup = html;
|
|
148
|
+
const omitValue = ["required", "disabled", "checked", "selected", "multiple", "readonly"];
|
|
149
|
+
omitValue.forEach((value) => {
|
|
150
|
+
parsedMarkup = parsedMarkup.replaceAll(`${value}="${value}"`, value).replaceAll(`${value}=""`, value);
|
|
151
|
+
});
|
|
152
|
+
return parsedMarkup;
|
|
153
|
+
}
|
|
154
|
+
function sanitizeSpecialCharacters(text) {
|
|
155
|
+
return text.replaceAll(">", ">").replaceAll("<", "<").replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'");
|
|
156
|
+
}
|
|
157
|
+
function ensureStartingSlash(input) {
|
|
158
|
+
return input.startsWith("/") ? input : `/${input}`;
|
|
159
|
+
}
|
|
160
|
+
function* idGenerator() {
|
|
161
|
+
let id = 0;
|
|
162
|
+
while (true) {
|
|
163
|
+
yield id++;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const idGen = idGenerator();
|
|
167
|
+
function generateId() {
|
|
168
|
+
const { value } = idGen.next();
|
|
169
|
+
return value;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const defaultOptions = {
|
|
173
|
+
types: ["info", "warning", "success", "error", "tip", "note"]};
|
|
174
|
+
function infoBlock(state, start, end, silent) {
|
|
175
|
+
const pos = state.bMarks[start] + state.tShift[start];
|
|
176
|
+
const max = state.eMarks[start];
|
|
177
|
+
if (pos + 3 > max)
|
|
178
|
+
return false;
|
|
179
|
+
if (state.src.charCodeAt(pos) !== 58)
|
|
180
|
+
return false;
|
|
181
|
+
if (state.src.charCodeAt(pos + 1) !== 58)
|
|
182
|
+
return false;
|
|
183
|
+
if (state.src.charCodeAt(pos + 2) !== 58)
|
|
184
|
+
return false;
|
|
185
|
+
const markup = state.src.slice(pos, pos + 3);
|
|
186
|
+
const params = state.src.slice(pos + 3, max).trim();
|
|
187
|
+
const spaceIndex = params.indexOf(" ");
|
|
188
|
+
const type = spaceIndex === -1 ? params : params.slice(0, spaceIndex);
|
|
189
|
+
const title = spaceIndex === -1 ? "" : params.slice(spaceIndex + 1).trim();
|
|
190
|
+
if (!type || !defaultOptions.types.includes(type)) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
let nextLine = start + 1;
|
|
194
|
+
let foundClosing = false;
|
|
195
|
+
while (nextLine < end) {
|
|
196
|
+
const linePos = state.bMarks[nextLine] + state.tShift[nextLine];
|
|
197
|
+
const lineMax = state.eMarks[nextLine];
|
|
198
|
+
const line = state.src.slice(linePos, lineMax).trim();
|
|
199
|
+
if (line === ":::") {
|
|
200
|
+
foundClosing = true;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
nextLine++;
|
|
204
|
+
}
|
|
205
|
+
if (!foundClosing) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
if (silent) {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
const openToken = state.push("info_open", "div", 1);
|
|
212
|
+
openToken.info = type;
|
|
213
|
+
openToken.markup = markup;
|
|
214
|
+
openToken.map = [start, nextLine + 1];
|
|
215
|
+
openToken.meta = { type, title };
|
|
216
|
+
const contentStart = start + 1;
|
|
217
|
+
const contentEnd = nextLine;
|
|
218
|
+
if (contentStart < contentEnd) {
|
|
219
|
+
const oldParent = state.parentType;
|
|
220
|
+
const oldLineMax = state.lineMax;
|
|
221
|
+
state.parentType = "info";
|
|
222
|
+
state.lineMax = contentEnd;
|
|
223
|
+
state.md.block.tokenize(state, contentStart, contentEnd);
|
|
224
|
+
state.parentType = oldParent;
|
|
225
|
+
state.lineMax = oldLineMax;
|
|
226
|
+
}
|
|
227
|
+
const closeToken = state.push("info_close", "div", -1);
|
|
228
|
+
closeToken.markup = markup;
|
|
229
|
+
closeToken.map = [nextLine, nextLine + 1];
|
|
230
|
+
state.line = nextLine + 1;
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
function renderInfoOpen(tokens, idx) {
|
|
234
|
+
const token = tokens[idx];
|
|
235
|
+
const { type, title } = token.meta;
|
|
236
|
+
const alertStyles = {
|
|
237
|
+
info: {
|
|
238
|
+
container: "bg-blue-100 border border-blue-200 dark:bg-blue-900/20 dark:border-blue-900/40",
|
|
239
|
+
title: "text-blue-800 dark:text-blue-200",
|
|
240
|
+
content: "[&_*]:!text-blue-700 [&_*]:dark:!text-blue-300"
|
|
241
|
+
},
|
|
242
|
+
warning: {
|
|
243
|
+
container: "bg-yellow-50 border border-yellow-300 dark:bg-yellow-900/20 dark:border-yellow-900/40",
|
|
244
|
+
title: "text-yellow-800 dark:text-yellow-200",
|
|
245
|
+
content: "[&_*]:!text-yellow-700 [&_*]:dark:!text-yellow-300"
|
|
246
|
+
},
|
|
247
|
+
error: {
|
|
248
|
+
container: "bg-red-100 border border-red-200 dark:bg-red-900/20 dark:border-red-900/40",
|
|
249
|
+
title: "text-red-800 dark:text-red-200",
|
|
250
|
+
content: "[&_*]:!text-red-700 [&_*]:dark:!text-red-300"
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
const alertIcons = {
|
|
254
|
+
info: `<svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="size-5 text-blue-500"><path d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-7-4a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM9 9a.75.75 0 0 0 0 1.5h.253a.25.25 0 0 1 .244.304l-.459 2.066A1.75 1.75 0 0 0 10.747 15H11a.75.75 0 0 0 0-1.5h-.253a.25.25 0 0 1-.244-.304l.459-2.066A1.75 1.75 0 0 0 9.253 9H9Z" clip-rule="evenodd" fill-rule="evenodd" /></svg>`,
|
|
255
|
+
warning: `<svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="size-5 text-yellow-500"><path d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495ZM10 5a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 5Zm0 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" fill-rule="evenodd" /></svg>`,
|
|
256
|
+
error: `<svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="size-5 text-red-500"><path d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z" clip-rule="evenodd" fill-rule="evenodd" /></svg>`
|
|
257
|
+
};
|
|
258
|
+
const styles = alertStyles[type] || alertStyles.info;
|
|
259
|
+
const iconHtml = alertIcons[type] || alertIcons.info;
|
|
260
|
+
return `
|
|
261
|
+
<div class="flex rounded-md p-4 ${styles.container}" role="alert">
|
|
262
|
+
<div class="shrink-0">
|
|
263
|
+
${iconHtml}
|
|
264
|
+
</div>
|
|
265
|
+
<div class="ml-3">
|
|
266
|
+
<p class="!text-sm !font-bold ${styles.title}">${title}</p>
|
|
267
|
+
<div class="mt-1 !text-sm ${styles.content}">
|
|
268
|
+
`;
|
|
269
|
+
}
|
|
270
|
+
function renderInfoClose() {
|
|
271
|
+
return `
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
`;
|
|
276
|
+
}
|
|
277
|
+
function markdownItComponentInfo(md) {
|
|
278
|
+
md.block.ruler.before("fence", "info", infoBlock, {
|
|
279
|
+
alt: ["paragraph", "reference", "blockquote", "list"]
|
|
280
|
+
});
|
|
281
|
+
md.renderer.rules.info_open = renderInfoOpen;
|
|
282
|
+
md.renderer.rules.info_close = renderInfoClose;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let md;
|
|
286
|
+
function shiftHeadingLevels(markdownContent, rootHeadingLevel) {
|
|
287
|
+
const getHasHeadingLevel = (level) => new RegExp(`^#{${level}} `, "m").test(markdownContent);
|
|
288
|
+
const shiftDown = (shiftAmount) => {
|
|
289
|
+
return markdownContent.replace(/^(#{1,6}) (.*)$/gm, (_, hashes, text) => {
|
|
290
|
+
const newLevel = Math.min(hashes.length + shiftAmount, 6);
|
|
291
|
+
return `${"#".repeat(newLevel)} ${text}`;
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
const hasH1 = getHasHeadingLevel(1);
|
|
295
|
+
const hasH2 = getHasHeadingLevel(2);
|
|
296
|
+
if (rootHeadingLevel === 1 && hasH1) {
|
|
297
|
+
return shiftDown(1);
|
|
298
|
+
}
|
|
299
|
+
if (rootHeadingLevel === 2 && (hasH1 || hasH2)) {
|
|
300
|
+
const shiftDownLevel = hasH1 ? 2 : 1;
|
|
301
|
+
return shiftDown(shiftDownLevel);
|
|
302
|
+
}
|
|
303
|
+
return markdownContent;
|
|
304
|
+
}
|
|
305
|
+
async function parseMarkdown(data) {
|
|
306
|
+
if (!md) {
|
|
307
|
+
md = MarkdownItAsync({ linkify: true, typographer: true });
|
|
308
|
+
md.use(markdownItComponentInfo);
|
|
309
|
+
md.use(
|
|
310
|
+
fromAsyncCodeToHtml(
|
|
311
|
+
codeToHtml,
|
|
312
|
+
{
|
|
313
|
+
themes: {
|
|
314
|
+
light: "github-light-default",
|
|
315
|
+
dark: "aurora-x"
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
if ("filePath" in data) {
|
|
322
|
+
const doesFileExist = fs.existsSync(data.filePath);
|
|
323
|
+
if (!doesFileExist) {
|
|
324
|
+
log(`Error: Markdown file not found: "${data.filePath}"`, "important");
|
|
325
|
+
return '<p class="font-bold text-red-600">Error: Markdown file not found!</p>';
|
|
326
|
+
}
|
|
327
|
+
let fileContent = await fs.readFile(data.filePath, "utf8");
|
|
328
|
+
fileContent = shiftHeadingLevels(fileContent, data.rootHeadingLevel);
|
|
329
|
+
const parsedMarkdown = await md.renderAsync(fileContent);
|
|
330
|
+
return parsedMarkdown;
|
|
331
|
+
} else {
|
|
332
|
+
let fileContent = data.markdownContent.replace("Markdown:", "");
|
|
333
|
+
fileContent = shiftHeadingLevels(fileContent, data.rootHeadingLevel);
|
|
334
|
+
return await md.renderAsync(fileContent);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function parseColors(text) {
|
|
339
|
+
text = text.trim();
|
|
340
|
+
const colors = [];
|
|
341
|
+
const regex = /^(\S+)\s*:\s*(var\(--[a-z0-9-]+\)|[a-z]+|#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(?:rgb|hsl)a?\((?:-?\d+%?[,\s]+){2,3}[\d.]+%?\))(?:\s*-\s*(.*))?$/gim;
|
|
342
|
+
let test = regex.exec(text);
|
|
343
|
+
while (test !== null) {
|
|
344
|
+
const color = {
|
|
345
|
+
color: test[2]
|
|
346
|
+
};
|
|
347
|
+
if (test[1]) {
|
|
348
|
+
color.name = test[1];
|
|
349
|
+
}
|
|
350
|
+
if (test[3]) {
|
|
351
|
+
color.description = test[3];
|
|
352
|
+
}
|
|
353
|
+
colors.push(color);
|
|
354
|
+
test = regex.exec(text);
|
|
355
|
+
}
|
|
356
|
+
return colors;
|
|
357
|
+
}
|
|
358
|
+
function parseIcons(text) {
|
|
359
|
+
text = text.trim();
|
|
360
|
+
const icons = [];
|
|
361
|
+
const regex = /^([^:]+):\s*(<svg[^>]*>[\s\S]*?<\/svg>|<i[^>]*><\/i>)$/gm;
|
|
362
|
+
let match = regex.exec(text);
|
|
363
|
+
while (match !== null) {
|
|
364
|
+
const icon = {
|
|
365
|
+
name: match[1].trim(),
|
|
366
|
+
svg: match[2]
|
|
367
|
+
};
|
|
368
|
+
icons.push(icon);
|
|
369
|
+
match = regex.exec(text);
|
|
370
|
+
}
|
|
371
|
+
icons.forEach((icon, index) => {
|
|
372
|
+
const isValidSvg = icon.svg.startsWith("<svg") && icon.svg.endsWith("</svg>");
|
|
373
|
+
const isValidI = icon.svg.startsWith("<i") && icon.svg.endsWith("</i>");
|
|
374
|
+
if (!isValidSvg && !isValidI) {
|
|
375
|
+
console.warn(`Warning: Icon "${icon.name}" at index ${index} may have malformed content`);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
return icons;
|
|
379
|
+
}
|
|
380
|
+
function toFloat(value) {
|
|
381
|
+
return Number.isNaN(Number(value)) ? 0 : Number.parseFloat(value);
|
|
382
|
+
}
|
|
383
|
+
function kssParser(input, options = {}) {
|
|
384
|
+
options.markdown = options.markdown ?? true;
|
|
385
|
+
options.header = options.header ?? true;
|
|
386
|
+
options.custom = options.custom || [];
|
|
387
|
+
let files = [];
|
|
388
|
+
const styleGuide = {
|
|
389
|
+
files: [],
|
|
390
|
+
sections: []
|
|
391
|
+
};
|
|
392
|
+
if (typeof input === "string") {
|
|
393
|
+
files.push({ contents: input });
|
|
394
|
+
} else {
|
|
395
|
+
files = input.map((file) => {
|
|
396
|
+
if (typeof file === "string") {
|
|
397
|
+
return { contents: file };
|
|
398
|
+
} else {
|
|
399
|
+
styleGuide.files.push(file.path);
|
|
400
|
+
return file;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
for (const file of files) {
|
|
405
|
+
const comments = findCommentBlocks(file.contents);
|
|
406
|
+
for (const comment of comments) {
|
|
407
|
+
const newSection = {
|
|
408
|
+
raw: comment.raw,
|
|
409
|
+
header: "",
|
|
410
|
+
description: "",
|
|
411
|
+
modifiers: [],
|
|
412
|
+
parameters: [],
|
|
413
|
+
markup: "",
|
|
414
|
+
source: {
|
|
415
|
+
filename: file.base ? path.relative(file.base, file.path || "").replace(/\\/g, "/") : file.path || "",
|
|
416
|
+
path: file.path || "",
|
|
417
|
+
line: comment.line
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
const paragraphs = comment.text.split("\n\n");
|
|
421
|
+
for (let i = 0; i < paragraphs.length; i++) {
|
|
422
|
+
const reference = findReference(paragraphs[i]);
|
|
423
|
+
if (reference) {
|
|
424
|
+
newSection.reference = reference;
|
|
425
|
+
paragraphs.splice(i, 1);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (!newSection.reference) {
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
processProperty.call(newSection, paragraphs, "Colors", parseColors);
|
|
432
|
+
processProperty.call(newSection, paragraphs, "Wrapper", (x) => x.trim());
|
|
433
|
+
processProperty.call(newSection, paragraphs, "htmlclass", (x) => x.trim());
|
|
434
|
+
processProperty.call(newSection, paragraphs, "bodyclass", (x) => x.trim());
|
|
435
|
+
processProperty.call(newSection, paragraphs, "Icons", parseIcons);
|
|
436
|
+
processProperty.call(newSection, paragraphs, "Figma", (x) => x.trim());
|
|
437
|
+
processProperty.call(newSection, paragraphs, "Markup");
|
|
438
|
+
processProperty.call(newSection, paragraphs, "Weight", toFloat);
|
|
439
|
+
for (const customProperty of options.custom) {
|
|
440
|
+
processProperty.call(newSection, paragraphs, customProperty);
|
|
441
|
+
}
|
|
442
|
+
if (paragraphs.length === 0) {
|
|
443
|
+
newSection.header = newSection.reference;
|
|
444
|
+
} else if (paragraphs.length === 1) {
|
|
445
|
+
newSection.header = newSection.description = paragraphs[0];
|
|
446
|
+
} else {
|
|
447
|
+
newSection.header = paragraphs[0];
|
|
448
|
+
const possibleModifiers = paragraphs.pop() || "";
|
|
449
|
+
newSection.modifiers = possibleModifiers.split("\n");
|
|
450
|
+
newSection.description = paragraphs.join("\n\n");
|
|
451
|
+
const numModifierLines = newSection.modifiers.length;
|
|
452
|
+
let hasModifiers = true;
|
|
453
|
+
let lastModifier = 0;
|
|
454
|
+
for (let j = 0; j < numModifierLines; j++) {
|
|
455
|
+
if (newSection.modifiers[j].match(/^\s*(?:\S.*?|[\t\v\f \xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF])\s+-\s/g)) {
|
|
456
|
+
lastModifier = j;
|
|
457
|
+
} else if (j === 0) {
|
|
458
|
+
hasModifiers = false;
|
|
459
|
+
j = numModifierLines;
|
|
460
|
+
} else {
|
|
461
|
+
newSection.modifiers[lastModifier] += ` ${newSection.modifiers[j].replace(/^\s+|\s+$/g, "")}`;
|
|
462
|
+
newSection.modifiers[j] = "";
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
newSection.modifiers = newSection.modifiers.filter((line) => line !== "");
|
|
466
|
+
if (hasModifiers) {
|
|
467
|
+
if (newSection.markup) {
|
|
468
|
+
newSection.modifiers = createModifiers(newSection.modifiers);
|
|
469
|
+
} else {
|
|
470
|
+
newSection.parameters = createParameters(newSection.modifiers);
|
|
471
|
+
newSection.modifiers = [];
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
newSection.description += `
|
|
475
|
+
|
|
476
|
+
${possibleModifiers}`;
|
|
477
|
+
newSection.modifiers = [];
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
newSection.header = newSection.header.replace(/\n/g, " ");
|
|
481
|
+
newSection.deprecated = hasPrefix(newSection.description, "Deprecated");
|
|
482
|
+
newSection.experimental = hasPrefix(newSection.description, "Experimental");
|
|
483
|
+
if (options.header) {
|
|
484
|
+
if (newSection.description.match(/\n{2,}/)) {
|
|
485
|
+
newSection.description = newSection.description.replace(/^.*\n{2,}/, "");
|
|
486
|
+
} else {
|
|
487
|
+
newSection.description = "";
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
styleGuide.sections.push(newSection);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return styleGuide;
|
|
494
|
+
}
|
|
495
|
+
function findCommentBlocks(input) {
|
|
496
|
+
const commentRegex = {
|
|
497
|
+
single: /^\s*\/\/.*$/,
|
|
498
|
+
docblockStart: /^\s*\/\*\*\s*$/,
|
|
499
|
+
multiStart: /^\s*\/\*+\s*$/,
|
|
500
|
+
multiFinish: /^\s*\*\/\s*$/
|
|
501
|
+
};
|
|
502
|
+
input = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
503
|
+
const blocks = [];
|
|
504
|
+
let block = {
|
|
505
|
+
line: 0,
|
|
506
|
+
text: "",
|
|
507
|
+
raw: ""
|
|
508
|
+
};
|
|
509
|
+
let indentAmount = false;
|
|
510
|
+
let insideSingleBlock = false;
|
|
511
|
+
let insideMultiBlock = false;
|
|
512
|
+
let insideDocblock = false;
|
|
513
|
+
input += "\n";
|
|
514
|
+
const lines = input.split("\n");
|
|
515
|
+
for (let i = 0; i < lines.length; i++) {
|
|
516
|
+
const line = lines[i].replace(/\s*$/, "");
|
|
517
|
+
if (!insideMultiBlock && !insideDocblock && line.match(commentRegex.single)) {
|
|
518
|
+
block.raw += `${line}
|
|
519
|
+
`;
|
|
520
|
+
block.text += `${line.replace(/^\s*\/\/\s?/, "")}
|
|
521
|
+
`;
|
|
522
|
+
if (!insideSingleBlock) {
|
|
523
|
+
block.line = i + 1;
|
|
524
|
+
}
|
|
525
|
+
insideSingleBlock = true;
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
if (insideSingleBlock || (insideMultiBlock || insideDocblock) && line.match(commentRegex.multiFinish)) {
|
|
529
|
+
const doneWithCurrentLine = !insideSingleBlock;
|
|
530
|
+
block.text = block.text.replace(/^\n+/, "").replace(/\n+$/, "");
|
|
531
|
+
blocks.push({ ...block });
|
|
532
|
+
insideMultiBlock = insideDocblock = insideSingleBlock = false;
|
|
533
|
+
indentAmount = false;
|
|
534
|
+
block = { line: 0, text: "", raw: "" };
|
|
535
|
+
if (doneWithCurrentLine)
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
if (line.match(commentRegex.docblockStart)) {
|
|
539
|
+
insideDocblock = true;
|
|
540
|
+
block.raw += `${line}
|
|
541
|
+
`;
|
|
542
|
+
block.line = i + 1;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
if (insideDocblock) {
|
|
546
|
+
block.raw += `${line}
|
|
547
|
+
`;
|
|
548
|
+
block.text += `${line.replace(/^\s*\*\s?/, "")}
|
|
549
|
+
`;
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
if (line.match(commentRegex.multiStart)) {
|
|
553
|
+
insideMultiBlock = true;
|
|
554
|
+
block.raw += `${line}
|
|
555
|
+
`;
|
|
556
|
+
block.line = i + 1;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if (insideMultiBlock) {
|
|
560
|
+
block.raw += `${line}
|
|
561
|
+
`;
|
|
562
|
+
if (indentAmount === false) {
|
|
563
|
+
if (line === "")
|
|
564
|
+
continue;
|
|
565
|
+
indentAmount = line.match(/^\s*/)[0];
|
|
566
|
+
}
|
|
567
|
+
block.text += `${line.replace(new RegExp(`^${indentAmount}`), "")}
|
|
568
|
+
`;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return blocks;
|
|
572
|
+
}
|
|
573
|
+
function createModifiers(rawModifiers) {
|
|
574
|
+
return rawModifiers.map((entry) => {
|
|
575
|
+
const modifier = entry.split(/\s+-\s+/, 1)[0];
|
|
576
|
+
const description = entry.replace(modifier, "").replace(/^\s+-\s+/, "");
|
|
577
|
+
return {
|
|
578
|
+
name: modifier,
|
|
579
|
+
description
|
|
580
|
+
};
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
function createParameters(rawParameters) {
|
|
584
|
+
return rawParameters.map((entry) => {
|
|
585
|
+
const parameter = entry.split(/\s+-\s+/, 1)[0];
|
|
586
|
+
const defaultValue = "";
|
|
587
|
+
const description = entry.replace(parameter, "").replace(/^\s+-\s+/, "");
|
|
588
|
+
if (/\s+=\s+/.test(parameter)) {
|
|
589
|
+
const tokens = parameter.split(/\s+=\s+/);
|
|
590
|
+
return {
|
|
591
|
+
name: tokens[0],
|
|
592
|
+
defaultValue: tokens[1],
|
|
593
|
+
description
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
return {
|
|
597
|
+
name: parameter,
|
|
598
|
+
defaultValue,
|
|
599
|
+
description
|
|
600
|
+
};
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
function findReference(text) {
|
|
604
|
+
text = text.trim().replace(/\s+/g, " ");
|
|
605
|
+
const regex = /^style\s?guide\s?[-:]?\s?/i;
|
|
606
|
+
if (regex.test(text)) {
|
|
607
|
+
return text.replace(regex, "");
|
|
608
|
+
}
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
function processProperty(paragraphs, propertyName, processValue) {
|
|
612
|
+
let indexToRemove = false;
|
|
613
|
+
propertyName = propertyName.toLowerCase();
|
|
614
|
+
for (let i = 0; i < paragraphs.length; i++) {
|
|
615
|
+
if (hasPrefix(paragraphs[i], propertyName)) {
|
|
616
|
+
this[propertyName] = paragraphs[i].replace(
|
|
617
|
+
new RegExp(`^\\s*${propertyName}\\:\\s+?`, "gim"),
|
|
618
|
+
""
|
|
619
|
+
);
|
|
620
|
+
if (typeof processValue === "function") {
|
|
621
|
+
this[propertyName] = processValue(this[propertyName]);
|
|
622
|
+
}
|
|
623
|
+
indexToRemove = i;
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (indexToRemove !== false) {
|
|
628
|
+
paragraphs.splice(indexToRemove, 1);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
function hasPrefix(description, prefix) {
|
|
632
|
+
return new RegExp(`^\\s*${prefix}\\:`, "gim").test(description);
|
|
633
|
+
}
|
|
634
|
+
function extractMarkdownPath(input) {
|
|
635
|
+
const normalizedInput = input.trim();
|
|
636
|
+
const markdownPrefix = /^Markdown:\s*/i;
|
|
637
|
+
if (!markdownPrefix.test(normalizedInput)) {
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
const path2 = normalizedInput.replace(markdownPrefix, "").trim();
|
|
641
|
+
if (!path2.endsWith(".md")) {
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
return path2;
|
|
645
|
+
}
|
|
646
|
+
async function parse(text, config) {
|
|
647
|
+
const data = kssParser(text).sections.filter((section) => Boolean(section.reference));
|
|
648
|
+
const overwrittenSectionsIds = [];
|
|
649
|
+
const sortedData = data.sort((a, b) => {
|
|
650
|
+
return a.reference.localeCompare(b.reference);
|
|
651
|
+
});
|
|
652
|
+
let output = [];
|
|
653
|
+
async function computeDescription(description, rootHeadingLevel) {
|
|
654
|
+
if (!description || !description.includes("Markdown:"))
|
|
655
|
+
return { description, hasMarkdownDescription: false };
|
|
656
|
+
const markdownPath = extractMarkdownPath(description);
|
|
657
|
+
if (!markdownPath) {
|
|
658
|
+
return {
|
|
659
|
+
description: await parseMarkdown({ markdownContent: description, rootHeadingLevel }),
|
|
660
|
+
hasMarkdownDescription: true
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
description: await parseMarkdown({ filePath: path.join(config.contentDir, markdownPath), rootHeadingLevel }),
|
|
665
|
+
hasMarkdownDescription: true
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
for await (const section of sortedData) {
|
|
669
|
+
if (!section.reference)
|
|
670
|
+
return;
|
|
671
|
+
const sectionIds = section.reference.split(".");
|
|
672
|
+
const isFirstLevelSection = sectionIds.length === 1 || sectionIds.length === 2 && sectionIds[1] === "0";
|
|
673
|
+
if (isFirstLevelSection) {
|
|
674
|
+
const { description, hasMarkdownDescription } = await computeDescription(section.description, 1);
|
|
675
|
+
const isSectionIdDuplicated = !!output[sectionIds[0]];
|
|
676
|
+
if (isSectionIdDuplicated) {
|
|
677
|
+
overwrittenSectionsIds.push(section.reference);
|
|
678
|
+
}
|
|
679
|
+
output[sectionIds[0]] = {
|
|
680
|
+
id: section.reference,
|
|
681
|
+
sectionLevel: "first",
|
|
682
|
+
header: section.header,
|
|
683
|
+
description,
|
|
684
|
+
hasMarkdownDescription,
|
|
685
|
+
markup: section.markup,
|
|
686
|
+
sections: [],
|
|
687
|
+
modifiers: section.modifiers.map((modifier) => ({
|
|
688
|
+
value: modifier.name,
|
|
689
|
+
description: modifier.description
|
|
690
|
+
})),
|
|
691
|
+
colors: section.colors,
|
|
692
|
+
icons: section.icons,
|
|
693
|
+
figma: section.figma,
|
|
694
|
+
wrapper: section.wrapper,
|
|
695
|
+
htmlclass: section.htmlclass,
|
|
696
|
+
bodyclass: section.bodyclass,
|
|
697
|
+
sourceFileName: section.source.filename,
|
|
698
|
+
previewFileName: `preview-${section.reference}.html`,
|
|
699
|
+
fullpageFileName: `fullpage-${section.reference}.html`
|
|
700
|
+
};
|
|
701
|
+
} else {
|
|
702
|
+
const firstLevelParentSection = output[sectionIds[0]];
|
|
703
|
+
if (!firstLevelParentSection)
|
|
704
|
+
throw new Error(`First level parent section ${firstLevelParentSection} not found for section ${section.reference}`);
|
|
705
|
+
const isThirdLevelSection = sectionIds.length >= 3;
|
|
706
|
+
if (isThirdLevelSection) {
|
|
707
|
+
const secondLevelParentSection = firstLevelParentSection.sections[sectionIds[1]];
|
|
708
|
+
if (!secondLevelParentSection)
|
|
709
|
+
throw new Error(`Second level parent section ${sectionIds[0]}.${sectionIds[1]} not found for section ${section.reference}`);
|
|
710
|
+
const { description, hasMarkdownDescription } = await computeDescription(section.description, 2);
|
|
711
|
+
const isSectionIdDuplicated = secondLevelParentSection.sections.some((s) => s.id === section.reference);
|
|
712
|
+
if (isSectionIdDuplicated) {
|
|
713
|
+
overwrittenSectionsIds.push(section.reference);
|
|
714
|
+
}
|
|
715
|
+
secondLevelParentSection.sections.push({
|
|
716
|
+
id: section.reference,
|
|
717
|
+
sectionLevel: "third",
|
|
718
|
+
header: section.header,
|
|
719
|
+
description,
|
|
720
|
+
hasMarkdownDescription,
|
|
721
|
+
markup: section.markup,
|
|
722
|
+
modifiers: section.modifiers.map((modifier) => ({
|
|
723
|
+
value: modifier.name,
|
|
724
|
+
description: modifier.description
|
|
725
|
+
})),
|
|
726
|
+
colors: section.colors,
|
|
727
|
+
icons: section.icons,
|
|
728
|
+
figma: section.figma,
|
|
729
|
+
wrapper: section.wrapper,
|
|
730
|
+
htmlclass: section.htmlclass,
|
|
731
|
+
bodyclass: section.bodyclass,
|
|
732
|
+
sourceFileName: section.source.filename,
|
|
733
|
+
previewFileName: `preview-${section.reference}.html`,
|
|
734
|
+
fullpageFileName: `fullpage-${section.reference}.html`
|
|
735
|
+
});
|
|
736
|
+
} else {
|
|
737
|
+
const { description, hasMarkdownDescription } = await computeDescription(section.description, 1);
|
|
738
|
+
const isSectionIdDuplicated = !!firstLevelParentSection.sections[sectionIds[1]];
|
|
739
|
+
if (isSectionIdDuplicated) {
|
|
740
|
+
overwrittenSectionsIds.push(section.reference);
|
|
741
|
+
}
|
|
742
|
+
firstLevelParentSection.sections[sectionIds[1]] = {
|
|
743
|
+
id: section.reference,
|
|
744
|
+
sectionLevel: "second",
|
|
745
|
+
header: section.header,
|
|
746
|
+
description,
|
|
747
|
+
hasMarkdownDescription,
|
|
748
|
+
markup: section.markup,
|
|
749
|
+
modifiers: section.modifiers.map((modifier) => ({
|
|
750
|
+
value: modifier.name,
|
|
751
|
+
description: modifier.description
|
|
752
|
+
})),
|
|
753
|
+
colors: section.colors,
|
|
754
|
+
icons: section.icons,
|
|
755
|
+
figma: section.figma,
|
|
756
|
+
wrapper: section.wrapper,
|
|
757
|
+
htmlclass: section.htmlclass,
|
|
758
|
+
bodyclass: section.bodyclass,
|
|
759
|
+
sections: [],
|
|
760
|
+
sourceFileName: section.source.filename,
|
|
761
|
+
previewFileName: `preview-${section.reference}.html`,
|
|
762
|
+
fullpageFileName: `fullpage-${section.reference}.html`
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
output = output.filter((section) => Boolean(section));
|
|
768
|
+
output.forEach((firstLevelSection) => {
|
|
769
|
+
firstLevelSection.sections = firstLevelSection.sections.filter((section) => Boolean(section));
|
|
770
|
+
firstLevelSection.sections.forEach((secondLevelSection) => {
|
|
771
|
+
secondLevelSection.sections = secondLevelSection.sections.filter((section) => Boolean(section)).sort((a, b) => a.id.localeCompare(b.id));
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
return {
|
|
775
|
+
content: output,
|
|
776
|
+
overwrittenSectionsIds
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
async function generateFullPageFile(data) {
|
|
781
|
+
const computedScriptTags = data.js.filter((entry) => entry.type !== "overwriteStyleguide").map((js) => {
|
|
782
|
+
const additionalAttributes = js.additionalAttributes ? Object.entries(js.additionalAttributes).map(([key, value]) => `${key}="${value}"`).join(" ") : "";
|
|
783
|
+
return `<script src="${js.src}" ${additionalAttributes}><\/script>`;
|
|
784
|
+
}).join("\n");
|
|
785
|
+
const computedStyleTags = data.css.filter((entry) => entry.type !== "overwriteStyleguide").map((css) => {
|
|
786
|
+
return `<link rel="stylesheet" type="text/css" href="${css.src}">`;
|
|
787
|
+
}).join("\n");
|
|
788
|
+
const content = `
|
|
789
|
+
<!DOCTYPE html>
|
|
790
|
+
<html lang="${data.page.lang}"${data.page.htmlclass ? ` class="scroll-smooth ${data.page.htmlclass}"` : ""}>
|
|
791
|
+
<head>
|
|
792
|
+
<title>${sanitizeSpecialCharacters(data.page.title)}</title>
|
|
793
|
+
${data.page.description ? `<meta name="description" content="${sanitizeSpecialCharacters(data.page.description)}">` : ""}
|
|
794
|
+
<meta charset="UTF-8">
|
|
795
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
796
|
+
<meta name="generator" content="styleguide">
|
|
797
|
+
${typeof data.theme === "object" && "dark" in data.theme && "light" in data.theme ? `
|
|
798
|
+
<link rel="icon" type="image/svg+xml" href="/styleguide-assets/favicon/fullpage.svg">
|
|
799
|
+
` : `
|
|
800
|
+
<meta name="theme-color" content="${data.theme}">
|
|
801
|
+
<link rel="icon" type="image/svg+xml" href="/styleguide-assets/favicon/fullpage-light.svg">
|
|
802
|
+
`}
|
|
803
|
+
${data.ogImageUrl ? `<meta property="og:image" content="${data.ogImageUrl}">` : ""}
|
|
804
|
+
<script type="module" src="/styleguide-assets/client-fullpage-1rKiNQVN.js"><\/script>
|
|
805
|
+
${computedStyleTags}
|
|
806
|
+
</head>
|
|
807
|
+
<body${data.page.bodyclass ? ` class="${data.page.bodyclass}"` : ""}>
|
|
808
|
+
${data.html}
|
|
809
|
+
${computedScriptTags}
|
|
810
|
+
</body>
|
|
811
|
+
</html>
|
|
812
|
+
`.trim();
|
|
813
|
+
await logicalWriteFile(data.filePath, content);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
const sanitizeId = (id) => id.toLowerCase().replaceAll(".", "-");
|
|
817
|
+
function getHasSectionExternalFullpage(section) {
|
|
818
|
+
return section.markup.length > 0 && (section.icons === void 0 || section.icons.length === 0) && (section.colors === void 0 || section.colors.length === 0);
|
|
819
|
+
}
|
|
820
|
+
function getHeaderHtml() {
|
|
821
|
+
return `
|
|
822
|
+
<header class="sticky top-0 z-10 mx-auto flex w-full min-[1222px]:border-x border-b pr-6 max-w-[1600px] border-styleguide-border bg-styleguide-bg-highlight">
|
|
823
|
+
<a class="mr-4 flex items-center border-r py-4 pr-4 pl-6 border-styleguide-border w-[260px] font-semibold tracking-tight text-styleguide-theme-highlight" href="/">
|
|
824
|
+
${globalThis.styleguideConfiguration.projectTitle}
|
|
825
|
+
</a>
|
|
826
|
+
|
|
827
|
+
<div class="flex grow items-center justify-end py-4 md:justify-between">
|
|
828
|
+
<button
|
|
829
|
+
class="inline-flex items-center cursor-pointer justify-between gap-2 rounded-full border p-2 text-sm transition border-styleguide-border hover:text-styleguide-highlight focus:text-styleguide-highlight active:scale-[0.96] md:py-1.5 md:rounded-md md:px-2"
|
|
830
|
+
type="button"
|
|
831
|
+
aria-controls="search-dialog"
|
|
832
|
+
aria-expanded="false"
|
|
833
|
+
data-open-search=""
|
|
834
|
+
>
|
|
835
|
+
<span class="sr-only md:not-sr-only">Search page</span>
|
|
836
|
+
|
|
837
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5 md:hidden">
|
|
838
|
+
<path fill-rule="evenodd" d="M2 4.75A.75.75 0 0 1 2.75 4h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 4.75ZM2 10a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 10Zm0 5.25a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1-.75-.75Z" clip-rule="evenodd" />
|
|
839
|
+
</svg>
|
|
840
|
+
|
|
841
|
+
<kbd class="relative hidden items-center gap-1 rounded-md border font-semibold text-[10px] border-styleguide-border px-1.5 pt-[4px] pb-[3px] md:inline-flex">
|
|
842
|
+
<span class="winlinux-only inline-block text-[10px] leading-[10px]">Ctrl</span>
|
|
843
|
+
<span class="mac-only inline-block text-[12px] leading-[12px]">\u2318</span>
|
|
844
|
+
<span class="leading-[10px]">K</span>
|
|
845
|
+
</kbd>
|
|
846
|
+
</button>
|
|
847
|
+
|
|
848
|
+
${!(globalThis.styleguideConfiguration.deactivateDarkMode ?? false) ? `
|
|
849
|
+
<form class="hidden theme-select md:block">
|
|
850
|
+
<fieldset class="flex items-center rounded-3xl border border-styleguide-border">
|
|
851
|
+
<legend class="sr-only">Select a display theme:</legend>
|
|
852
|
+
|
|
853
|
+
<input id="normal" class="sr-only" type="radio" value="normal" name="theme">
|
|
854
|
+
<label for="normal" class="cursor-pointer p-2">
|
|
855
|
+
<span class="sr-only">System</span>
|
|
856
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
857
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M9 17.25v1.007a3 3 0 0 1-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0 1 15 18.257V17.25m6-12V15a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 15V5.25m18 0A2.25 2.25 0 0 0 18.75 3H5.25A2.25 2.25 0 0 0 3 5.25m18 0V12a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 12V5.25" />
|
|
858
|
+
</svg>
|
|
859
|
+
</label>
|
|
860
|
+
|
|
861
|
+
<input id="light" class="sr-only" type="radio" value="light" name="theme">
|
|
862
|
+
<label for="light" class="cursor-pointer p-2">
|
|
863
|
+
<span class="sr-only">Light</span>
|
|
864
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
865
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z" />
|
|
866
|
+
</svg>
|
|
867
|
+
</label>
|
|
868
|
+
|
|
869
|
+
<input id="dark" class="sr-only" type="radio" value="dark" name="theme">
|
|
870
|
+
<label for="dark" class="cursor-pointer p-2">
|
|
871
|
+
<span class="sr-only">Dark</span>
|
|
872
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
|
873
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z" />
|
|
874
|
+
</svg>
|
|
875
|
+
</label>
|
|
876
|
+
</fieldset>
|
|
877
|
+
</form>
|
|
878
|
+
` : ``}
|
|
879
|
+
</div>
|
|
880
|
+
</header>
|
|
881
|
+
`.trim();
|
|
882
|
+
}
|
|
883
|
+
function getSidebarMenuHtml(sections, activePageHref) {
|
|
884
|
+
const getItemHtml = (item) => {
|
|
885
|
+
if (item.href.includes(activePageHref)) {
|
|
886
|
+
return `<div class="menu-item menu-item--active">${item.label}</div>`;
|
|
887
|
+
}
|
|
888
|
+
return `<a class="menu-item" href="${item.href}">${item.label}</a>`;
|
|
889
|
+
};
|
|
890
|
+
return `
|
|
891
|
+
<ul class="grid gap-6 px-3 py-6">
|
|
892
|
+
${sections.map((section) => `
|
|
893
|
+
<li>
|
|
894
|
+
<span class="block rounded-md px-3 text-sm font-semibold text-styleguide-theme-highlight">
|
|
895
|
+
${section.title}
|
|
896
|
+
</span>
|
|
897
|
+
<ul class="mt-3 grid gap-2">
|
|
898
|
+
${section.items.map((item) => `
|
|
899
|
+
<li>
|
|
900
|
+
${getItemHtml(item)}
|
|
901
|
+
</li>
|
|
902
|
+
`).join("\n")}
|
|
903
|
+
</ul>
|
|
904
|
+
</li>
|
|
905
|
+
`).join("\n")}
|
|
906
|
+
</ul>
|
|
907
|
+
`.trim();
|
|
908
|
+
}
|
|
909
|
+
function getCodeAuditDialog() {
|
|
910
|
+
return `
|
|
911
|
+
<dialog
|
|
912
|
+
id="code-audit-dialog"
|
|
913
|
+
class="fixed -inset-x-0 top-auto bottom-0 z-30 pt-6 -mb-px w-full max-w-none overflow-y-auto rounded-t-2xl border search bg-styleguide-bg border-styleguide-border text-styleguide md:max-w-[640px] md:top-1/2 md:bottom-auto md:left-1/2 md:-translate-x-1/2 md:-translate-y-1/2 md:rounded-2xl mx-0"
|
|
914
|
+
>
|
|
915
|
+
<h2 class="flex items-center gap-2 mb-4 px-6">
|
|
916
|
+
<svg class="-mt-0.5 h-4 transition duration-200" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 122.88" aria-hidden="true"><title>accessibility</title><path fill="currentcolor" d="M61.44,0A61.46,61.46,0,1,1,18,18,61.21,61.21,0,0,1,61.44,0Zm-.39,74.18L52.1,98.91a4.94,4.94,0,0,1-2.58,2.83A5,5,0,0,1,42.7,95.5l6.24-17.28a26.3,26.3,0,0,0,1.17-4,40.64,40.64,0,0,0,.54-4.18c.24-2.53.41-5.27.54-7.9s.22-5.18.29-7.29c.09-2.63-.62-2.8-2.73-3.3l-.44-.1-18-3.39A5,5,0,0,1,27.08,46a5,5,0,0,1,5.05-7.74l19.34,3.63c.77.07,1.52.16,2.31.25a57.64,57.64,0,0,0,7.18.53A81.13,81.13,0,0,0,69.9,42c.9-.1,1.75-.21,2.6-.29l18.25-3.42A5,5,0,0,1,94.5,39a5,5,0,0,1,1.3,7,5,5,0,0,1-3.21,2.09L75.15,51.37c-.58.13-1.1.22-1.56.29-1.82.31-2.72.47-2.61,3.06.08,1.89.31,4.15.61,6.51.35,2.77.81,5.71,1.29,8.4.31,1.77.6,3.19,1,4.55s.79,2.75,1.39,4.42l6.11,16.9a5,5,0,0,1-6.82,6.24,4.94,4.94,0,0,1-2.58-2.83L63,74.23,62,72.4l-1,1.78Zm.39-53.52a8.83,8.83,0,1,1-6.24,2.59,8.79,8.79,0,0,1,6.24-2.59Zm36.35,4.43a51.42,51.42,0,1,0,15,36.35,51.27,51.27,0,0,0-15-36.35Z"/></svg>
|
|
917
|
+
<span class="text-lg font-semibold text-styleguide-highlight leading-[1]">
|
|
918
|
+
Code Audit Results
|
|
919
|
+
</span>
|
|
920
|
+
</h2>
|
|
921
|
+
|
|
922
|
+
<ul class="audit-results"></ul>
|
|
923
|
+
</dialog>
|
|
924
|
+
`;
|
|
925
|
+
}
|
|
926
|
+
function renderTab(data) {
|
|
927
|
+
const tabId = generateId();
|
|
928
|
+
return `
|
|
929
|
+
<div class="tabs">
|
|
930
|
+
<div
|
|
931
|
+
class="inline-flex relative justify-start flex-wrap rounded-md p-0.5 bg-styleguide-bg-highlight"
|
|
932
|
+
role="tablist"
|
|
933
|
+
>
|
|
934
|
+
<div
|
|
935
|
+
class="tab-trigger-background absolute inset-y-0.5 left-0 bg-[rgb(242,242,242)] dark:bg-[rgb(26,26,26)] rounded"
|
|
936
|
+
></div>
|
|
937
|
+
|
|
938
|
+
${data.map((tab, index) => `
|
|
939
|
+
<button
|
|
940
|
+
id="tab-trigger-${tabId}-${index}"
|
|
941
|
+
role="tab"
|
|
942
|
+
aria-selected="${(index === 0).toString()}"
|
|
943
|
+
aria-controls="tab-panel-${tabId}-${index}"
|
|
944
|
+
class="inline-flex gap-1 items-center relative px-4 py-2 text-sm cursor-pointer aria-selected:text-styleguide-highlight transition duration-400"
|
|
945
|
+
>
|
|
946
|
+
${tab.icon ?? ""}
|
|
947
|
+
${tab.title}
|
|
948
|
+
</button>
|
|
949
|
+
`).join("\n")}
|
|
950
|
+
</div>
|
|
951
|
+
|
|
952
|
+
${data.map((tab, index) => `
|
|
953
|
+
<div
|
|
954
|
+
id="tab-panel-${tabId}-${index}"
|
|
955
|
+
class="${index === 0 ? "" : "hidden"}"
|
|
956
|
+
role="tabpanel"
|
|
957
|
+
aria-labelledby="tab-trigger-${tabId}-${index}"
|
|
958
|
+
tabindex="${index === 0 ? "0" : "-1"}"
|
|
959
|
+
>
|
|
960
|
+
${tab.content}
|
|
961
|
+
</div>
|
|
962
|
+
`).join("\n")}
|
|
963
|
+
</div>
|
|
964
|
+
`;
|
|
965
|
+
}
|
|
966
|
+
function getMainContentHtml(secondLevelSection, config) {
|
|
967
|
+
let output = "";
|
|
968
|
+
function renderSection(section) {
|
|
969
|
+
if (section.colors && section.colors.length > 0) {
|
|
970
|
+
output += getMainContentSectionWrapper(section, getMainContentColors(section));
|
|
971
|
+
} else if (section.icons && section.icons.length > 0) {
|
|
972
|
+
output += getMainContentSectionWrapper(section, getMainContentIcons(section));
|
|
973
|
+
} else {
|
|
974
|
+
const content = section.markup ? getMainContentRegular(section, config) : void 0;
|
|
975
|
+
output += getMainContentSectionWrapper(section, content);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
renderSection(secondLevelSection);
|
|
979
|
+
secondLevelSection.sections.forEach(renderSection);
|
|
980
|
+
return output;
|
|
981
|
+
}
|
|
982
|
+
function getMainContentSectionWrapper(section, html) {
|
|
983
|
+
let headingTag = "";
|
|
984
|
+
let headingClass = "";
|
|
985
|
+
if (section.sectionLevel === "second") {
|
|
986
|
+
headingTag = "h1";
|
|
987
|
+
headingClass = "text-4xl";
|
|
988
|
+
} else if (section.sectionLevel === "third") {
|
|
989
|
+
const isDeeplyNested = section.id.split(".").length > 3;
|
|
990
|
+
headingTag = isDeeplyNested ? "h3" : "h2";
|
|
991
|
+
headingClass = isDeeplyNested ? "text-xl" : "text-2xl";
|
|
992
|
+
}
|
|
993
|
+
const computeDescription = () => {
|
|
994
|
+
if (!section.description)
|
|
995
|
+
return "";
|
|
996
|
+
if (section.hasMarkdownDescription) {
|
|
997
|
+
return `
|
|
998
|
+
<div class="markdown-container-folded relative mb-8">
|
|
999
|
+
<div class="markdown-container mt-2 max-h-[400px] overflow-hidden">
|
|
1000
|
+
${section.description}
|
|
1001
|
+
</div>
|
|
1002
|
+
|
|
1003
|
+
<div class="markdown-show-more-container hidden absolute inset-x-0 bottom-0 flex justify-center max-w-[65ch] after:absolute after:inset-x-0 after:bottom-0 after:h-[120px] after:bg-gradient-to-t after:from-styleguide-bg after:to-transparent after:pointer-events-none">
|
|
1004
|
+
<button
|
|
1005
|
+
type="button"
|
|
1006
|
+
class="markdown-show-more px-2 py-1 bg-styleguide-bg-highlight rounded-2xl cursor-pointer border border-styleguide-border hover:text-styleguide-highlight active:scale-[0.96] md:min-w-[150px] z-10"
|
|
1007
|
+
>
|
|
1008
|
+
Show more
|
|
1009
|
+
</button>
|
|
1010
|
+
</div>
|
|
1011
|
+
</div>
|
|
1012
|
+
`;
|
|
1013
|
+
}
|
|
1014
|
+
return `<p class="mt-2${section.sectionLevel === "second" ? " text-xl" : ""}">${section.description}</p>`;
|
|
1015
|
+
};
|
|
1016
|
+
return `
|
|
1017
|
+
<section
|
|
1018
|
+
id="section-${sanitizeId(section.id)}"
|
|
1019
|
+
class="styleguide-section border-b px-4 py-10 border-b-styleguide-border scroll-mt-[50px] md:px-10"
|
|
1020
|
+
>
|
|
1021
|
+
<div class="flex items-center justify-between gap-6">
|
|
1022
|
+
<a class="relative group" href="#section-${sanitizeId(section.id)}">
|
|
1023
|
+
<svg class="absolute top-1/2 -left-6 -translate-y-1/2 opacity-0 transition size-[18px] group-hover:opacity-100 group-focus:opacity-100"
|
|
1024
|
+
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
|
1025
|
+
<path d="M12.232 4.232a2.5 2.5 0 0 1 3.536 3.536l-1.225 1.224a.75.75 0 0 0 1.061 1.06l1.224-1.224a4 4 0 0 0-5.656-5.656l-3 3a4 4 0 0 0 .225 5.865.75.75 0 0 0 .977-1.138 2.5 2.5 0 0 1-.142-3.667l3-3Z"/>
|
|
1026
|
+
<path d="M11.603 7.963a.75.75 0 0 0-.977 1.138 2.5 2.5 0 0 1 .142 3.667l-3 3a2.5 2.5 0 0 1-3.536-3.536l1.225-1.224a.75.75 0 0 0-1.061-1.06l-1.224 1.224a4 4 0 1 0 5.656 5.656l3-3a4 4 0 0 0-.225-5.865Z"/>
|
|
1027
|
+
</svg>
|
|
1028
|
+
<${headingTag} class="${headingClass} font-semibold text-styleguide-theme-highlight">${section.header}</${headingTag}>
|
|
1029
|
+
</a>
|
|
1030
|
+
|
|
1031
|
+
${getHasSectionExternalFullpage(section) ? `
|
|
1032
|
+
<a
|
|
1033
|
+
class="p-2 group hover:text-styleguide-highlight focus:text-styleguide-highlight"
|
|
1034
|
+
href="/${section.fullpageFileName}"
|
|
1035
|
+
target="_blank"
|
|
1036
|
+
title="Open ${section.header} in fullpage"
|
|
1037
|
+
>
|
|
1038
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="h-4 w-4">
|
|
1039
|
+
<path class="transition group-hover:translate-x-px group-hover:-translate-y-px group-focus:translate-x-px group-focus:-translate-y-px" d="M6.22 8.72a.75.75 0 0 0 1.06 1.06l5.22-5.22v1.69a.75.75 0 0 0 1.5 0v-3.5a.75.75 0 0 0-.75-.75h-3.5a.75.75 0 0 0 0 1.5h1.69L6.22 8.72Z"/>
|
|
1040
|
+
<path class="transition" d="M3.5 6.75c0-.69.56-1.25 1.25-1.25H7A.75.75 0 0 0 7 4H4.75A2.75 2.75 0 0 0 2 6.75v4.5A2.75 2.75 0 0 0 4.75 14h4.5A2.75 2.75 0 0 0 12 11.25V9a.75.75 0 0 0-1.5 0v2.25c0 .69-.56 1.25-1.25 1.25h-4.5c-.69 0-1.25-.56-1.25-1.25v-4.5Z"/>
|
|
1041
|
+
</svg>
|
|
1042
|
+
</a>
|
|
1043
|
+
` : ""}
|
|
1044
|
+
</div>
|
|
1045
|
+
${computeDescription()}
|
|
1046
|
+
${html ?? ""}
|
|
1047
|
+
</section>
|
|
1048
|
+
`;
|
|
1049
|
+
}
|
|
1050
|
+
function getMainContentRegular(section, config) {
|
|
1051
|
+
let sourceFilePath = "";
|
|
1052
|
+
if (config.mode === "development" && config.launchInEditor && section.markup.includes('<pug src="')) {
|
|
1053
|
+
const regexModifierLine = /<pug src="(.+?)".*(?:[\n\r\u2028\u2029]\s*)?(modifierClass="(.+?)")? *><\/pug>/g;
|
|
1054
|
+
const vitePugTags = section.markup.match(regexModifierLine);
|
|
1055
|
+
if (!vitePugTags)
|
|
1056
|
+
throw new Error("No Vite Pug tags found");
|
|
1057
|
+
vitePugTags.forEach((vitePugTag) => {
|
|
1058
|
+
const pugSourcePath = vitePugTag.match(/src="(.+?)"/)?.[1];
|
|
1059
|
+
if (!pugSourcePath || !pugSourcePath.endsWith(".pug")) {
|
|
1060
|
+
throw new Error("No or invalid Pug source path found");
|
|
1061
|
+
}
|
|
1062
|
+
sourceFilePath = pugSourcePath;
|
|
1063
|
+
});
|
|
1064
|
+
} else if (config.mode === "development" && section.sourceFileName) {
|
|
1065
|
+
sourceFilePath = path.join(config.contentDir, section.sourceFileName);
|
|
1066
|
+
}
|
|
1067
|
+
const shouldLaunchInEditor = config.mode === "development" && config.launchInEditor && sourceFilePath;
|
|
1068
|
+
let openInEditorPathPhpStorm = "";
|
|
1069
|
+
let openInEditorPathVscode = "";
|
|
1070
|
+
if (shouldLaunchInEditor) {
|
|
1071
|
+
const computedRootPath = config.launchInEditor && typeof config.launchInEditor === "object" && "rootDir" in config.launchInEditor ? config.launchInEditor.rootDir : process.cwd();
|
|
1072
|
+
const computedFilePath = ensureStartingSlash(path.join(computedRootPath, sourceFilePath));
|
|
1073
|
+
openInEditorPathPhpStorm = shouldLaunchInEditor ? `phpstorm://open?file=${computedFilePath}` : "";
|
|
1074
|
+
openInEditorPathVscode = shouldLaunchInEditor ? `vscode://file//${computedFilePath}` : "";
|
|
1075
|
+
}
|
|
1076
|
+
const codePreviewMarkup = `
|
|
1077
|
+
<!-- Preview Box -->
|
|
1078
|
+
<div class="mt-4 overflow-hidden rounded-2xl border border-styleguide-border">
|
|
1079
|
+
<div class="w-full border-b p-6 bg-styleguide-bg-highlight border-styleguide-border">
|
|
1080
|
+
<iframe
|
|
1081
|
+
id="preview-fullpage-${sanitizeId(section.id)}"
|
|
1082
|
+
src="/${section.fullpageFileName}"
|
|
1083
|
+
class="preview-iframe"
|
|
1084
|
+
data-preview="true"
|
|
1085
|
+
title="${section.header} Preview"
|
|
1086
|
+
scrolling="no"
|
|
1087
|
+
></iframe>
|
|
1088
|
+
</div>
|
|
1089
|
+
|
|
1090
|
+
<!-- Code -->
|
|
1091
|
+
<details class="group @container">
|
|
1092
|
+
<summary class="flex cursor-pointer justify-between items-center rounded-b-2xl px-6 text-sm font-light bg-styleguide-bg">
|
|
1093
|
+
<span class="flex gap-2 items-center py-4 select-none">
|
|
1094
|
+
<svg class="h-4 w-4 group-open:rotate-90 transition-transform" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor">
|
|
1095
|
+
<path fill-rule="evenodd" d="M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
|
|
1096
|
+
</svg>
|
|
1097
|
+
|
|
1098
|
+
<span class="group-open:hidden">Show code</span>
|
|
1099
|
+
<span class="group-open:block hidden">Hide code</span>
|
|
1100
|
+
</span>
|
|
1101
|
+
|
|
1102
|
+
<span class="flex items-center">
|
|
1103
|
+
${openInEditorPathPhpStorm ? `<a
|
|
1104
|
+
class="inline-flex items-center group/phpstorm gap-1.5 p-4 desktop-device-only cursor-pointer active:scale-90 transition hover:text-styleguide-highlight duration-200"
|
|
1105
|
+
href="${openInEditorPathPhpStorm}"
|
|
1106
|
+
>
|
|
1107
|
+
<span class="hidden @2xl:inline-block">Open in</span>
|
|
1108
|
+
<span class="sr-only">PHPStorm</span>
|
|
1109
|
+
<img
|
|
1110
|
+
src="styleguide-assets/icons/phpstorm.svg"
|
|
1111
|
+
width="70"
|
|
1112
|
+
height="70"
|
|
1113
|
+
class="size-4 saturate-0 group-hover/phpstorm:saturate-100 transition"
|
|
1114
|
+
alt="PHPStorm Logo"
|
|
1115
|
+
aria-hidden="true"
|
|
1116
|
+
>
|
|
1117
|
+
</a>
|
|
1118
|
+
` : ""}
|
|
1119
|
+
|
|
1120
|
+
${openInEditorPathVscode ? `<a
|
|
1121
|
+
class="inline-flex items-center group/vscode gap-1.5 p-4 desktop-device-only cursor-pointer active:scale-90 transition hover:text-styleguide-highlight duration-200"
|
|
1122
|
+
href="${openInEditorPathVscode}"
|
|
1123
|
+
>
|
|
1124
|
+
<span class="hidden @2xl:inline-block">Open in</span>
|
|
1125
|
+
<span class="sr-only">VsCode</span>
|
|
1126
|
+
<img
|
|
1127
|
+
src="styleguide-assets/icons/vscode.svg"
|
|
1128
|
+
width="100"
|
|
1129
|
+
height="100"
|
|
1130
|
+
class="size-4 saturate-0 group-hover/vscode:saturate-100 transition"
|
|
1131
|
+
alt="VsCode Logo"
|
|
1132
|
+
aria-hidden="true"
|
|
1133
|
+
>
|
|
1134
|
+
</a>
|
|
1135
|
+
` : ""}
|
|
1136
|
+
|
|
1137
|
+
<button
|
|
1138
|
+
class="inline-flex items-center gap-1.5 p-4 cursor-pointer active:scale-90 transition hover:text-styleguide-highlight duration-200"
|
|
1139
|
+
type="button"
|
|
1140
|
+
data-clipboard-value="${encodeURIComponent(section.markup)}"
|
|
1141
|
+
data-clipboard-uri-encoded="true"
|
|
1142
|
+
>
|
|
1143
|
+
<svg class="h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
|
1144
|
+
<title>Clipboard</title>
|
|
1145
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M15.666 3.888A2.25 2.25 0 0 0 13.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 0 1-.75.75H9a.75.75 0 0 1-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 0 1-2.25 2.25H6.75A2.25 2.25 0 0 1 4.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 0 1 1.927-.184" />
|
|
1146
|
+
</svg>
|
|
1147
|
+
|
|
1148
|
+
<span class="hidden @2xl:inline-block">Copy</span>
|
|
1149
|
+
<span class="sr-only"> Code</span>
|
|
1150
|
+
</button>
|
|
1151
|
+
|
|
1152
|
+
<button
|
|
1153
|
+
class="inline-flex items-center gap-1.5 p-4 cursor-pointer active:scale-90 transition hover:text-styleguide-highlight duration-200"
|
|
1154
|
+
type="button"
|
|
1155
|
+
data-code-audit-iframe="preview-fullpage-${sanitizeId(section.id)}"
|
|
1156
|
+
aria-controls="code-audit-dialog"
|
|
1157
|
+
aria-expanded="false"
|
|
1158
|
+
>
|
|
1159
|
+
<svg class="h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 122.88" aria-hidden="true"><title>accessibility</title><path fill="currentcolor" d="M61.44,0A61.46,61.46,0,1,1,18,18,61.21,61.21,0,0,1,61.44,0Zm-.39,74.18L52.1,98.91a4.94,4.94,0,0,1-2.58,2.83A5,5,0,0,1,42.7,95.5l6.24-17.28a26.3,26.3,0,0,0,1.17-4,40.64,40.64,0,0,0,.54-4.18c.24-2.53.41-5.27.54-7.9s.22-5.18.29-7.29c.09-2.63-.62-2.8-2.73-3.3l-.44-.1-18-3.39A5,5,0,0,1,27.08,46a5,5,0,0,1,5.05-7.74l19.34,3.63c.77.07,1.52.16,2.31.25a57.64,57.64,0,0,0,7.18.53A81.13,81.13,0,0,0,69.9,42c.9-.1,1.75-.21,2.6-.29l18.25-3.42A5,5,0,0,1,94.5,39a5,5,0,0,1,1.3,7,5,5,0,0,1-3.21,2.09L75.15,51.37c-.58.13-1.1.22-1.56.29-1.82.31-2.72.47-2.61,3.06.08,1.89.31,4.15.61,6.51.35,2.77.81,5.71,1.29,8.4.31,1.77.6,3.19,1,4.55s.79,2.75,1.39,4.42l6.11,16.9a5,5,0,0,1-6.82,6.24,4.94,4.94,0,0,1-2.58-2.83L63,74.23,62,72.4l-1,1.78Zm.39-53.52a8.83,8.83,0,1,1-6.24,2.59,8.79,8.79,0,0,1,6.24-2.59Zm36.35,4.43a51.42,51.42,0,1,0,15,36.35,51.27,51.27,0,0,0-15-36.35Z"/></svg>
|
|
1160
|
+
<span class="hidden md:inline-block">Audit</span>
|
|
1161
|
+
</button>
|
|
1162
|
+
|
|
1163
|
+
${getHasSectionExternalFullpage(section) ? `
|
|
1164
|
+
<a
|
|
1165
|
+
class="hidden [.styleguide-section--large_&]:inline-block -mr-2 px-2 py-4 group/externallink hover:text-styleguide-highlight focus:text-styleguide-highlight"
|
|
1166
|
+
href="/${section.fullpageFileName}"
|
|
1167
|
+
target="_blank"
|
|
1168
|
+
title="Open ${section.header} in fullpage"
|
|
1169
|
+
>
|
|
1170
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="h-4 w-4">
|
|
1171
|
+
<path class="transition group-hover/externallink:translate-x-px group-hover/externallink:-translate-y-px group-focus/externallink:translate-x-px group/externallink-focus:-translate-y-px" d="M6.22 8.72a.75.75 0 0 0 1.06 1.06l5.22-5.22v1.69a.75.75 0 0 0 1.5 0v-3.5a.75.75 0 0 0-.75-.75h-3.5a.75.75 0 0 0 0 1.5h1.69L6.22 8.72Z"/>
|
|
1172
|
+
<path class="transition" d="M3.5 6.75c0-.69.56-1.25 1.25-1.25H7A.75.75 0 0 0 7 4H4.75A2.75 2.75 0 0 0 2 6.75v4.5A2.75 2.75 0 0 0 4.75 14h4.5A2.75 2.75 0 0 0 12 11.25V9a.75.75 0 0 0-1.5 0v2.25c0 .69-.56 1.25-1.25 1.25h-4.5c-.69 0-1.25-.56-1.25-1.25v-4.5Z"/>
|
|
1173
|
+
</svg>
|
|
1174
|
+
</a>
|
|
1175
|
+
` : ""}
|
|
1176
|
+
</span>
|
|
1177
|
+
</summary>
|
|
1178
|
+
|
|
1179
|
+
<div class="border-t p-6 text-sm bg-styleguide-bg-highlight border-styleguide-border">
|
|
1180
|
+
<div
|
|
1181
|
+
id="code-fullpage-${sanitizeId(section.id)}"
|
|
1182
|
+
class="overflow-x-auto w-full code-highlight"
|
|
1183
|
+
data-source-code="${encodeURIComponent(section.markup)}"
|
|
1184
|
+
></div>
|
|
1185
|
+
</div>
|
|
1186
|
+
</details>
|
|
1187
|
+
</div>
|
|
1188
|
+
|
|
1189
|
+
${section.modifiers?.length > 0 ? `
|
|
1190
|
+
<!-- Modifiers List -->
|
|
1191
|
+
<h3 class="mt-6 flex items-center text-xl font-semibold text-styleguide-theme-highlight gap-1.5">
|
|
1192
|
+
${section.header} Modifiers
|
|
1193
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 text-styleguide">
|
|
1194
|
+
<path d="M7.25 1.75a.75.75 0 0 1 1.5 0v1.5a.75.75 0 0 1-1.5 0v-1.5ZM11.536 2.904a.75.75 0 1 1 1.06 1.06l-1.06 1.061a.75.75 0 0 1-1.061-1.06l1.06-1.061ZM14.5 7.5a.75.75 0 0 0-.75-.75h-1.5a.75.75 0 0 0 0 1.5h1.5a.75.75 0 0 0 .75-.75ZM4.464 9.975a.75.75 0 0 1 1.061 1.06l-1.06 1.061a.75.75 0 1 1-1.061-1.06l1.06-1.061ZM4.5 7.5a.75.75 0 0 0-.75-.75h-1.5a.75.75 0 0 0 0 1.5h1.5a.75.75 0 0 0 .75-.75ZM5.525 3.964a.75.75 0 0 1-1.06 1.061l-1.061-1.06a.75.75 0 0 1 1.06-1.061l1.061 1.06ZM8.779 7.438a.75.75 0 0 0-1.368.366l-.396 5.283a.75.75 0 0 0 1.212.646l.602-.474.288 1.074a.75.75 0 1 0 1.449-.388l-.288-1.075.759.11a.75.75 0 0 0 .726-1.165L8.78 7.438Z" />
|
|
1195
|
+
</svg>
|
|
1196
|
+
</h3>
|
|
1197
|
+
<ul class="mt-4 grid gap-6 overflow-hidden rounded-2xl border p-6 border-styleguide-border bg-styleguide-bg-highlight md:gap-x-10 md:gap-y-14">
|
|
1198
|
+
${section.modifiers?.map((modifier) => `
|
|
1199
|
+
<li>
|
|
1200
|
+
<div>
|
|
1201
|
+
<div class="flex items-center justify-between gap-6">
|
|
1202
|
+
<div class="flex items-center gap-3">
|
|
1203
|
+
<h4 class="font-semibold text-styleguide-theme-highlight">${modifier.description}</h4>
|
|
1204
|
+
|
|
1205
|
+
<button
|
|
1206
|
+
class="inline-block rounded-md cursor-copy border py-1 font-mono font-semibold transition duration-500 text-[10px] border-styleguide-border px-2.5 bg-styleguide-bg-highlight hover:text-styleguide-highlight focus:text-styleguide-highlight"
|
|
1207
|
+
type="button"
|
|
1208
|
+
title="Copy content"
|
|
1209
|
+
data-clipboard-value="${modifier.value}"
|
|
1210
|
+
data-clipboard-alert-message="${modifier.value.split(".").length === 0 ? "Copied class name to clipboard!" : "Copied class names to clipboard!"}"
|
|
1211
|
+
>
|
|
1212
|
+
${modifier.value}
|
|
1213
|
+
</button>
|
|
1214
|
+
</div>
|
|
1215
|
+
|
|
1216
|
+
<a
|
|
1217
|
+
class="p-2 group"
|
|
1218
|
+
href="/${section.fullpageFileName}?modifier=${modifier.value}"
|
|
1219
|
+
target="_blank"
|
|
1220
|
+
title="Open in fullpage"
|
|
1221
|
+
>
|
|
1222
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4">
|
|
1223
|
+
<path class="transition group-hover:translate-x-px group-hover:-translate-y-px group-focus:translate-x-px group-focus:-translate-y-px"
|
|
1224
|
+
d="M6.22 8.72a.75.75 0 0 0 1.06 1.06l5.22-5.22v1.69a.75.75 0 0 0 1.5 0v-3.5a.75.75 0 0 0-.75-.75h-3.5a.75.75 0 0 0 0 1.5h1.69L6.22 8.72Z"/>
|
|
1225
|
+
<path d="M3.5 6.75c0-.69.56-1.25 1.25-1.25H7A.75.75 0 0 0 7 4H4.75A2.75 2.75 0 0 0 2 6.75v4.5A2.75 2.75 0 0 0 4.75 14h4.5A2.75 2.75 0 0 0 12 11.25V9a.75.75 0 0 0-1.5 0v2.25c0 .69-.56 1.25-1.25 1.25h-4.5c-.69 0-1.25-.56-1.25-1.25v-4.5Z"/>
|
|
1226
|
+
</svg>
|
|
1227
|
+
</a>
|
|
1228
|
+
</div>
|
|
1229
|
+
|
|
1230
|
+
<iframe
|
|
1231
|
+
src="/${section.fullpageFileName}"
|
|
1232
|
+
class="preview-iframe mt-2"
|
|
1233
|
+
data-preview="true"
|
|
1234
|
+
data-modifier="${modifier.value}"
|
|
1235
|
+
title="${section.header} Preview - Modifier: ${modifier.value}"
|
|
1236
|
+
scrolling="no"
|
|
1237
|
+
></iframe>
|
|
1238
|
+
</div>
|
|
1239
|
+
</li>
|
|
1240
|
+
`).join("\n")}
|
|
1241
|
+
</ul>
|
|
1242
|
+
` : ""}
|
|
1243
|
+
`;
|
|
1244
|
+
if (section.figma) {
|
|
1245
|
+
const computedFigmaUrl = new URL(section.figma);
|
|
1246
|
+
computedFigmaUrl.searchParams.append("theme", "system");
|
|
1247
|
+
computedFigmaUrl.searchParams.append("footer", "false");
|
|
1248
|
+
computedFigmaUrl.searchParams.append("mode", "dev");
|
|
1249
|
+
computedFigmaUrl.searchParams.append("page-selector", "false");
|
|
1250
|
+
return `
|
|
1251
|
+
<div class="mt-4">
|
|
1252
|
+
${renderTab([
|
|
1253
|
+
{
|
|
1254
|
+
title: "Preview",
|
|
1255
|
+
content: codePreviewMarkup
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
title: "Design",
|
|
1259
|
+
icon: `<img src="styleguide-assets/icons/figma.svg" width="600" height="600" alt="Figma Logo" class="size-3">`,
|
|
1260
|
+
content: `
|
|
1261
|
+
<div class="mt-4 overflow-hidden rounded-2xl border border-styleguide-border">
|
|
1262
|
+
<div class="w-full border-b p-6 bg-styleguide-bg-highlight border-styleguide-border">
|
|
1263
|
+
<iframe
|
|
1264
|
+
width="800"
|
|
1265
|
+
height="450"
|
|
1266
|
+
src="${computedFigmaUrl.href}"
|
|
1267
|
+
class="aspect-video size-full"
|
|
1268
|
+
title="${section.header} Figma Design Preview"
|
|
1269
|
+
allowfullscreen
|
|
1270
|
+
loading="lazy"
|
|
1271
|
+
></iframe>
|
|
1272
|
+
</div>
|
|
1273
|
+
</div>
|
|
1274
|
+
`
|
|
1275
|
+
}
|
|
1276
|
+
])}
|
|
1277
|
+
</div>
|
|
1278
|
+
`;
|
|
1279
|
+
}
|
|
1280
|
+
return codePreviewMarkup;
|
|
1281
|
+
}
|
|
1282
|
+
function getMainContentColors(section) {
|
|
1283
|
+
return `
|
|
1284
|
+
<ul class="my-6 grid grid-cols-2 gap-4 md:grid-cols-4 xl:grid-cols-6">
|
|
1285
|
+
${section.colors?.map(
|
|
1286
|
+
(color) => `<li>
|
|
1287
|
+
<button
|
|
1288
|
+
class="relative w-full rounded-2xl px-4 py-6 cursor-copy"
|
|
1289
|
+
type="button"
|
|
1290
|
+
style="background-color: ${color.color}"
|
|
1291
|
+
data-clipboard-value="${color.color}"
|
|
1292
|
+
data-clipboard-alert-message="Copied color to clipboard!"
|
|
1293
|
+
>
|
|
1294
|
+
<span class="sr-only">Copy color to clipboard</span>
|
|
1295
|
+
</button>
|
|
1296
|
+
</li>`
|
|
1297
|
+
).join("\n")}
|
|
1298
|
+
</ul>
|
|
1299
|
+
`;
|
|
1300
|
+
}
|
|
1301
|
+
function getAlerts() {
|
|
1302
|
+
return `
|
|
1303
|
+
<div id="alerts"></div>
|
|
1304
|
+
`;
|
|
1305
|
+
}
|
|
1306
|
+
function getMainContentIcons(section) {
|
|
1307
|
+
return `
|
|
1308
|
+
<form
|
|
1309
|
+
id="icon-search"
|
|
1310
|
+
class="my-6 flex items-center overflow-hidden rounded-2xl border px-4 py-2 gap-2.5 bg-styleguide-bg-highlight border-styleguide-border"
|
|
1311
|
+
onsubmit="return false;"
|
|
1312
|
+
>
|
|
1313
|
+
<label for="icon-search-input">
|
|
1314
|
+
<span class="sr-only">Search icons</span>
|
|
1315
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
|
|
1316
|
+
<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11ZM2 9a7 7 0 1 1 12.452 4.391l3.328 3.329a.75.75 0 1 1-1.06 1.06l-3.329-3.328A7 7 0 0 1 2 9Z" clip-rule="evenodd" />
|
|
1317
|
+
</svg>
|
|
1318
|
+
</label>
|
|
1319
|
+
|
|
1320
|
+
<input
|
|
1321
|
+
id="icon-search-input"
|
|
1322
|
+
class="grow bg-transparent icon-search-input h-[28px] focus:outline-hidden"
|
|
1323
|
+
placeholder="Search icons..."
|
|
1324
|
+
aria-autocomplete="list"
|
|
1325
|
+
autocomplete="off"
|
|
1326
|
+
role="combobox"
|
|
1327
|
+
spellcheck="false"
|
|
1328
|
+
type="text"
|
|
1329
|
+
value=""
|
|
1330
|
+
aria-controls="icon-search"
|
|
1331
|
+
>
|
|
1332
|
+
|
|
1333
|
+
<button
|
|
1334
|
+
id="icon-search-input-reset"
|
|
1335
|
+
class="hidden icon-search-input-reset"
|
|
1336
|
+
type="reset"
|
|
1337
|
+
>
|
|
1338
|
+
<span class="sr-only">Reset search input</span>
|
|
1339
|
+
|
|
1340
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4">
|
|
1341
|
+
<path d="M5.28 4.22a.75.75 0 0 0-1.06 1.06L6.94 8l-2.72 2.72a.75.75 0 1 0 1.06 1.06L8 9.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L9.06 8l2.72-2.72a.75.75 0 0 0-1.06-1.06L8 6.94 5.28 4.22Z" />
|
|
1342
|
+
</svg>
|
|
1343
|
+
</button>
|
|
1344
|
+
</form>
|
|
1345
|
+
|
|
1346
|
+
<div class="@container min-h-[400px] md:min-h-[250px]">
|
|
1347
|
+
<ul
|
|
1348
|
+
id="icon-search-list"
|
|
1349
|
+
class="grid grid-cols-2 gap-4 @lg:grid-cols-3 @xl:grid-cols-4"
|
|
1350
|
+
>
|
|
1351
|
+
${section.icons?.map((icon) => `
|
|
1352
|
+
<li class="relative grid gap-4 rounded-2xl border border-transparent px-4 py-6 items icon-search-list__item bg-styleguide-bg-highlight transition hover:border-styleguide-border focus:border-styleguide-border">
|
|
1353
|
+
<div class="relative flex w-full justify-center size-6 text-styleguide-highlight [&>svg]:h-full [&>svg]:w-auto [&>i]:size-[24px] [&>i]:text-[24px] [&>i]:leading-[1em]">
|
|
1354
|
+
${icon.svg}
|
|
1355
|
+
|
|
1356
|
+
<!-- Successfully copied icon -->
|
|
1357
|
+
<svg class="pointer-events-none icon-search-list__item-copy-icon size-5 text-styleguide-highlight" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
|
1358
|
+
<path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z" clip-rule="evenodd" />
|
|
1359
|
+
</svg>
|
|
1360
|
+
</div>
|
|
1361
|
+
<p class="text-center font-mono text-sm font-semibold hyphens-auto break-all">${icon.name}</p>
|
|
1362
|
+
<button
|
|
1363
|
+
class="absolute inset-0 cursor-copy bg-transparent icon-search-list__item-copy"
|
|
1364
|
+
type="button"
|
|
1365
|
+
title="Copy svg content"
|
|
1366
|
+
></button>
|
|
1367
|
+
</li>
|
|
1368
|
+
`).join("\n")}
|
|
1369
|
+
</ul>
|
|
1370
|
+
</div>
|
|
1371
|
+
`;
|
|
1372
|
+
}
|
|
1373
|
+
function getNextPageControlsHtml(data) {
|
|
1374
|
+
return `
|
|
1375
|
+
<nav class="flex w-full justify-between px-4 py-10 md:px-10">
|
|
1376
|
+
<div>
|
|
1377
|
+
${data.before ? `
|
|
1378
|
+
<a id="styleguide-previous" class="flex items-center gap-1 group" href="${data.before.href}">
|
|
1379
|
+
<svg class="h-5 w-5 transition group-hover:-translate-x-0.5 group-focus:-translate-x-0.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
|
1380
|
+
<path fill-rule="evenodd" d="M11.78 5.22a.75.75 0 0 1 0 1.06L8.06 10l3.72 3.72a.75.75 0 1 1-1.06 1.06l-4.25-4.25a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Z" clip-rule="evenodd"/>
|
|
1381
|
+
</svg>
|
|
1382
|
+
|
|
1383
|
+
<span class="grid gap-1.5">
|
|
1384
|
+
<span class="text-sm font-light leading-none">Previous</span>
|
|
1385
|
+
<span class="font-semibold leading-none text-styleguide-highlight">${data.before.label}</span>
|
|
1386
|
+
</span>
|
|
1387
|
+
</a>
|
|
1388
|
+
` : ``}
|
|
1389
|
+
</div>
|
|
1390
|
+
|
|
1391
|
+
<div>
|
|
1392
|
+
${data.after ? `
|
|
1393
|
+
<a id="styleguide-next" class="flex items-center gap-1 group" href="${data.after.href}">
|
|
1394
|
+
<span class="grid text-right gap-1.5">
|
|
1395
|
+
<span class="text-sm font-light leading-none">Next</span>
|
|
1396
|
+
<span class="font-semibold leading-none text-styleguide-highlight">${data.after.label}</span>
|
|
1397
|
+
</span>
|
|
1398
|
+
|
|
1399
|
+
<svg class="h-5 w-5 transition group-hover:translate-x-0.5 group-focus:translate-x-0.5"
|
|
1400
|
+
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
|
1401
|
+
<path fill-rule="evenodd" d="M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
|
|
1402
|
+
</svg>
|
|
1403
|
+
</a>
|
|
1404
|
+
` : ``}
|
|
1405
|
+
</div>
|
|
1406
|
+
</nav>
|
|
1407
|
+
`.trim();
|
|
1408
|
+
}
|
|
1409
|
+
function getSearchHtml(sections) {
|
|
1410
|
+
return `
|
|
1411
|
+
<dialog
|
|
1412
|
+
id="search-dialog"
|
|
1413
|
+
class="fixed -inset-x-px top-auto bottom-0 z-30 -mb-px w-full opacity-0 max-w-none overflow-y-auto rounded-t-2xl border search bg-styleguide-bg border-styleguide-border text-styleguide md:max-w-[640px] md:top-1/2 md:bottom-auto md:left-1/2 md:-translate-x-1/2 md:-translate-y-1/2 md:rounded-2xl mx-0"
|
|
1414
|
+
>
|
|
1415
|
+
<h2 class="sr-only">Search</h2>
|
|
1416
|
+
<div class="border-b px-4 py-3 border-styleguide-border">
|
|
1417
|
+
<label for="search-input" class="sr-only">Search styleguide</label>
|
|
1418
|
+
<input
|
|
1419
|
+
id="search-input"
|
|
1420
|
+
class="w-full bg-transparent text-[18px] h-[28px] focus:outline-hidden"
|
|
1421
|
+
placeholder="Search..."
|
|
1422
|
+
aria-autocomplete="list"
|
|
1423
|
+
autocomplete="off"
|
|
1424
|
+
role="combobox"
|
|
1425
|
+
spellcheck="false"
|
|
1426
|
+
type="text"
|
|
1427
|
+
value=""
|
|
1428
|
+
aria-expanded="false"
|
|
1429
|
+
>
|
|
1430
|
+
</div>
|
|
1431
|
+
|
|
1432
|
+
<nav id="search-list" class="grid gap-4 px-4 py-3 text-sm">
|
|
1433
|
+
${sections.map((section) => `
|
|
1434
|
+
<div class="search-category">
|
|
1435
|
+
<h3 class="mb-2">${section.title}</h3>
|
|
1436
|
+
<ul>
|
|
1437
|
+
${section.items.map((item) => `
|
|
1438
|
+
<li
|
|
1439
|
+
class="search-category__item search-category__item--active"
|
|
1440
|
+
data-search-keywords="${item.searchKeywords.map((x) => x.replaceAll('"', `'`)).join(",")}"
|
|
1441
|
+
>
|
|
1442
|
+
<a
|
|
1443
|
+
class="flex items-center gap-4 px-1.5 py-2.5 group rounded-md hover:bg-[rgb(242,242,242)] focus:bg-[rgb(242,242,242)] dark:hover:bg-[rgb(26,26,26)] dark:focus:bg-[rgb(26,26,26)] transition"
|
|
1444
|
+
href="${item.href}"
|
|
1445
|
+
>
|
|
1446
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="transition-all size-5 group-hover:translate-x-0.5 group-focus:translate-x-0.5">
|
|
1447
|
+
<path fill-rule="evenodd" d="M3 10a.75.75 0 0 1 .75-.75h10.638L10.23 5.29a.75.75 0 1 1 1.04-1.08l5.5 5.25a.75.75 0 0 1 0 1.08l-5.5 5.25a.75.75 0 1 1-1.04-1.08l4.158-3.96H3.75A.75.75 0 0 1 3 10Z" clip-rule="evenodd" />
|
|
1448
|
+
</svg>
|
|
1449
|
+
<span class="text-styleguide-highlight">
|
|
1450
|
+
${item.label}
|
|
1451
|
+
</span>
|
|
1452
|
+
</a>
|
|
1453
|
+
</li>
|
|
1454
|
+
`).join("\n")}
|
|
1455
|
+
</ul>
|
|
1456
|
+
</div>
|
|
1457
|
+
`).join("\n")}
|
|
1458
|
+
</nav>
|
|
1459
|
+
|
|
1460
|
+
<p id="search-no-results" class="hidden px-6 py-8 text-center text-sm">
|
|
1461
|
+
No results found
|
|
1462
|
+
</p>
|
|
1463
|
+
</dialog>
|
|
1464
|
+
|
|
1465
|
+
<div class="dialog-backdrop"></div>
|
|
1466
|
+
`.trim();
|
|
1467
|
+
}
|
|
1468
|
+
async function generatePreviewFile(data) {
|
|
1469
|
+
const computedScriptTags = data.js.filter((entry) => entry.type === "overwriteStyleguide").map((js) => {
|
|
1470
|
+
const additionalAttributes = js.additionalAttributes ? objectEntries(js.additionalAttributes).map(([key, value]) => `${key}="${value}"`).join(" ") : "";
|
|
1471
|
+
return `<script src="${js.src}" ${additionalAttributes}><\/script>`;
|
|
1472
|
+
}).join("\n");
|
|
1473
|
+
const computedStyleTags = data.css.filter((entry) => entry.type === "overwriteStyleguide").map((css) => {
|
|
1474
|
+
return `<link rel="stylesheet" type="text/css" href="${css.src}">`;
|
|
1475
|
+
}).join("\n");
|
|
1476
|
+
const computedPreloadIframes = data.html.preloadIframes.map((url) => `<link rel="preload" href="${url}" as="document">`).join("\n");
|
|
1477
|
+
const shouldRenderMetaDescription = data.page.description && data.page.description.length > 0 && !(data.page.description.includes("<") && data.page.description.includes(">"));
|
|
1478
|
+
const content = `
|
|
1479
|
+
<!DOCTYPE html>
|
|
1480
|
+
<html lang="${data.page.lang}" class="scroll-smooth">
|
|
1481
|
+
<head>
|
|
1482
|
+
<title>${sanitizeSpecialCharacters(data.page.title)}</title>
|
|
1483
|
+
${data.page.description && shouldRenderMetaDescription ? `<meta name="description" content="${data.page.description.replaceAll(`'`, "").replaceAll(`"`, "")}">` : ""}
|
|
1484
|
+
<meta charset="UTF-8">
|
|
1485
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1486
|
+
<meta name="generator" content="styleguide">
|
|
1487
|
+
${typeof data.theme === "object" && "dark" in data.theme && "light" in data.theme ? `
|
|
1488
|
+
<meta name="theme-color" media="(prefers-color-scheme: light)" content="${data.theme.light}">
|
|
1489
|
+
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="${data.theme.dark}">
|
|
1490
|
+
<link rel="icon" type="image/svg+xml" href="/styleguide-assets/favicon/preview.svg">
|
|
1491
|
+
` : `
|
|
1492
|
+
<meta name="theme-color" content="${data.theme}">
|
|
1493
|
+
<link rel="icon" type="image/svg+xml" href="/styleguide-assets/favicon/preview-light.svg">
|
|
1494
|
+
`}
|
|
1495
|
+
<link rel="stylesheet" type="text/css" href="/styleguide-assets/styleguide-BQbUioN5.css">
|
|
1496
|
+
<script type="module" src="/styleguide-assets/client-BZlkq2d8.js"><\/script>
|
|
1497
|
+
<link rel="preload" href="/styleguide-assets/fonts/geist-mono-latin-400-normal.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
|
1498
|
+
<link rel="preload" href="/styleguide-assets/fonts/geist-mono-latin-600-normal.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
|
1499
|
+
<link rel="preload" href="/styleguide-assets/fonts/geist-mono-latin-300-normal.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
|
1500
|
+
<link rel="preload" href="/styleguide-assets/fonts/geist-mono-latin-400-normal.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
|
1501
|
+
<link rel="preload" href="/styleguide-assets/fonts/geist-mono-latin-600-normal.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
|
1502
|
+
${computedPreloadIframes}
|
|
1503
|
+
${data.ogImageUrl ? `<meta property="og:image" content="${data.ogImageUrl}">` : ""}
|
|
1504
|
+
${computedStyleTags}
|
|
1505
|
+
<style>
|
|
1506
|
+
:root {
|
|
1507
|
+
--styleguide-color-theme-highlight: ${typeof data.theme === "string" ? data.theme : `light-dark(${data.theme.light}, ${data.theme.dark})`};
|
|
1508
|
+
}
|
|
1509
|
+
</style>
|
|
1510
|
+
</head>
|
|
1511
|
+
<body class="relative min-h-screen antialiased text-styleguide${globalThis.styleguideConfiguration.deactivateDarkMode ? " theme-light" : ""}">
|
|
1512
|
+
${data.html.header}
|
|
1513
|
+
|
|
1514
|
+
<main class="relative flex h-full min-h-screen min-[1222px]:border-x min-[1220px]:mx-auto max-w-[1600px] border-styleguide-border">
|
|
1515
|
+
<aside
|
|
1516
|
+
class="sticky order-1 hidden flex-col overflow-y-auto border-r z-100 w-[260px] border-styleguide-border shrink-0 md:flex"
|
|
1517
|
+
style="top: var(--header-height); max-height: calc(100vh - var(--header-height))"
|
|
1518
|
+
>
|
|
1519
|
+
<nav>
|
|
1520
|
+
${data.html.sidebarMenu}
|
|
1521
|
+
</nav>
|
|
1522
|
+
</aside>
|
|
1523
|
+
|
|
1524
|
+
<div class="order-2 w-full md:w-[calc(100%-260px)]">
|
|
1525
|
+
${data.html.mainContent}
|
|
1526
|
+
${data.html.nextPageControls}
|
|
1527
|
+
</div>
|
|
1528
|
+
</main>
|
|
1529
|
+
|
|
1530
|
+
${data.html.alerts}
|
|
1531
|
+
${data.html.search}
|
|
1532
|
+
${data.html.codeAuditDialog}
|
|
1533
|
+
|
|
1534
|
+
<script type="speculationrules">
|
|
1535
|
+
{
|
|
1536
|
+
"prerender": [{
|
|
1537
|
+
"where": {
|
|
1538
|
+
"href_matches": "/*"
|
|
1539
|
+
},
|
|
1540
|
+
"eagerness": "moderate"
|
|
1541
|
+
}]
|
|
1542
|
+
}
|
|
1543
|
+
<\/script>
|
|
1544
|
+
|
|
1545
|
+
${computedScriptTags}
|
|
1546
|
+
</body>
|
|
1547
|
+
</html>
|
|
1548
|
+
`.trim();
|
|
1549
|
+
await logicalWriteFile(data.filePath, content);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
const MAX_POOL_SIZE = os.cpus().length;
|
|
1553
|
+
let workerPool = [];
|
|
1554
|
+
async function terminateAllWorkers() {
|
|
1555
|
+
await Promise.all(workerPool.map(({ worker }) => worker.terminate));
|
|
1556
|
+
}
|
|
1557
|
+
const processCache = /* @__PURE__ */ new Map();
|
|
1558
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
1559
|
+
const __dirname = path.dirname(__filename);
|
|
1560
|
+
const workerFilePath = __dirname.includes("dist/") ? "./vite-pug/worker.mjs" : "./worker.ts";
|
|
1561
|
+
const workerFilePathResolved = path.resolve(__dirname, workerFilePath);
|
|
1562
|
+
const signals = ["SIGINT", "SIGTERM", "SIGQUIT"];
|
|
1563
|
+
signals.forEach((signal) => process.on(signal, async () => await terminateAllWorkers()));
|
|
1564
|
+
async function compilePugMarkup(mode, contentDir, repository) {
|
|
1565
|
+
const clonedRepository = structuredClone(repository);
|
|
1566
|
+
const needsProcessingIds = Array.from(clonedRepository.entries()).map(([id]) => id);
|
|
1567
|
+
if (needsProcessingIds.length === 0)
|
|
1568
|
+
return clonedRepository;
|
|
1569
|
+
if (mode === "production") {
|
|
1570
|
+
needsProcessingIds.forEach((id) => {
|
|
1571
|
+
const cachedMarkup = processCache.get(id);
|
|
1572
|
+
if (!cachedMarkup)
|
|
1573
|
+
return;
|
|
1574
|
+
clonedRepository.set(id, { markup: cachedMarkup });
|
|
1575
|
+
needsProcessingIds.splice(needsProcessingIds.indexOf(id), 1);
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
workerPool = Array.from({ length: Math.min(needsProcessingIds.length, MAX_POOL_SIZE) }, (_, index) => ({
|
|
1579
|
+
worker: new Worker(workerFilePathResolved, {
|
|
1580
|
+
name: `pug-worker-${index}`
|
|
1581
|
+
}),
|
|
1582
|
+
busy: false,
|
|
1583
|
+
currentTaskId: void 0
|
|
1584
|
+
}));
|
|
1585
|
+
workerPool.forEach((workerNode) => {
|
|
1586
|
+
workerNode.worker.on("message", (result) => {
|
|
1587
|
+
if ("error" in result) {
|
|
1588
|
+
console.error(result.error);
|
|
1589
|
+
workerNode.busy = false;
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
const { id, html } = result;
|
|
1593
|
+
clonedRepository.set(id, { markup: fixAccessibilityIssues(html) });
|
|
1594
|
+
workerNode.busy = false;
|
|
1595
|
+
});
|
|
1596
|
+
});
|
|
1597
|
+
while (needsProcessingIds.length > 0 || workerPool.some((worker) => worker.busy)) {
|
|
1598
|
+
if (needsProcessingIds.length === 0 && workerPool.some((worker) => worker.busy)) {
|
|
1599
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
const availableWorker = workerPool.find((worker) => !worker.busy);
|
|
1603
|
+
if (availableWorker) {
|
|
1604
|
+
const id = needsProcessingIds.pop();
|
|
1605
|
+
const { markup } = clonedRepository.get(id);
|
|
1606
|
+
availableWorker.busy = true;
|
|
1607
|
+
availableWorker.currentTaskId = id;
|
|
1608
|
+
availableWorker.worker.postMessage({
|
|
1609
|
+
id,
|
|
1610
|
+
mode,
|
|
1611
|
+
html: markup,
|
|
1612
|
+
contentDir
|
|
1613
|
+
});
|
|
1614
|
+
} else {
|
|
1615
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
await terminateAllWorkers();
|
|
1619
|
+
return clonedRepository;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
function watchForFileContentChanges(path, regex, callback) {
|
|
1623
|
+
if (typeof callback !== "function") {
|
|
1624
|
+
throw new TypeError("styleguide watch requires a callback function");
|
|
1625
|
+
}
|
|
1626
|
+
const regexFileContents = /* @__PURE__ */ new Map();
|
|
1627
|
+
const registerCSSFileContentMatches = (filePath) => {
|
|
1628
|
+
const currentFileContent = readFileSync(filePath, "utf8");
|
|
1629
|
+
const currentFileMatches = currentFileContent.match(regex);
|
|
1630
|
+
if (currentFileMatches === null) {
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
regexFileContents.set(filePath, currentFileMatches);
|
|
1634
|
+
callback();
|
|
1635
|
+
};
|
|
1636
|
+
const handleCSSContentChanges = (filePath) => {
|
|
1637
|
+
const previousFileMatches = regexFileContents.get(filePath);
|
|
1638
|
+
const hasFileBeenReadBefore = previousFileMatches !== void 0;
|
|
1639
|
+
const currentFileContent = readFileSync(filePath, "utf8");
|
|
1640
|
+
const currentFileMatches = currentFileContent.match(regex);
|
|
1641
|
+
if (!hasFileBeenReadBefore) {
|
|
1642
|
+
regexFileContents.set(filePath, currentFileMatches);
|
|
1643
|
+
if (currentFileMatches === null) {
|
|
1644
|
+
return;
|
|
1645
|
+
}
|
|
1646
|
+
callback();
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
const haveFileMatchesChanged = JSON.stringify(previousFileMatches) !== JSON.stringify(currentFileMatches);
|
|
1650
|
+
if (!haveFileMatchesChanged) {
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
regexFileContents.set(filePath, currentFileMatches);
|
|
1654
|
+
callback();
|
|
1655
|
+
};
|
|
1656
|
+
const handleCSSFileRemoval = (filePath) => {
|
|
1657
|
+
regexFileContents.delete(filePath);
|
|
1658
|
+
callback();
|
|
1659
|
+
};
|
|
1660
|
+
const validCSSFileTypes = [".css", ".scss", ".sass", ".less"];
|
|
1661
|
+
chokidar.watch(path, {
|
|
1662
|
+
ignoreInitial: true,
|
|
1663
|
+
// @ts-expect-error - chokidar types seem to be incomplete, ignore
|
|
1664
|
+
ignored: (path2, stats) => {
|
|
1665
|
+
return stats?.isFile() && !validCSSFileTypes.some((type) => path2.endsWith(type));
|
|
1666
|
+
}
|
|
1667
|
+
}).on("add", registerCSSFileContentMatches).on("change", handleCSSContentChanges).on("unlink", handleCSSFileRemoval);
|
|
1668
|
+
chokidar.watch(path, {
|
|
1669
|
+
ignoreInitial: true,
|
|
1670
|
+
// @ts-expect-error - chokidar types seem to be incomplete, ignore
|
|
1671
|
+
ignored: (path2, stats) => {
|
|
1672
|
+
return stats?.isFile() && !path2.endsWith(".md");
|
|
1673
|
+
}
|
|
1674
|
+
}).on("add", callback).on("change", callback).on("unlink", callback);
|
|
1675
|
+
}
|
|
1676
|
+
function watchStyleguideForChanges(path, callback) {
|
|
1677
|
+
const kssSectionRegex = /\/\*{1,2}[\s\S]*?Styleguide[\s\S]*?\*\//g;
|
|
1678
|
+
watchForFileContentChanges(
|
|
1679
|
+
path,
|
|
1680
|
+
kssSectionRegex,
|
|
1681
|
+
callback
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
globalThis.isWatchMode = false;
|
|
1686
|
+
async function buildStyleguide(config) {
|
|
1687
|
+
globalThis.styleguideConfiguration = config;
|
|
1688
|
+
const styleguideContentPaths = await glob(`${config.contentDir}/**/*.{css,scss}`);
|
|
1689
|
+
const styleguideContent = (await Promise.all(styleguideContentPaths.map((file) => fs.readFile(file, "utf-8")))).join("\n");
|
|
1690
|
+
const rawParsedOutput = await parse(styleguideContent, config);
|
|
1691
|
+
if (!rawParsedOutput)
|
|
1692
|
+
throw new Error("Could not parse content");
|
|
1693
|
+
const { content: parsedContent, overwrittenSectionsIds } = rawParsedOutput;
|
|
1694
|
+
if (config.mode === "production" && await fs.exists(config.outDir)) {
|
|
1695
|
+
const files = await glob(`${config.outDir}/**/*.html`);
|
|
1696
|
+
await Promise.all(files.map((file) => fs.unlink(file)));
|
|
1697
|
+
}
|
|
1698
|
+
const baseDirectory = path.relative(process.cwd(), config.outDir);
|
|
1699
|
+
const getFullPageFilePath = (fileName) => path.join(baseDirectory, fileName);
|
|
1700
|
+
const getPreviewPageFilePath = (fileName, isHtmlIndexPage = false) => {
|
|
1701
|
+
return isHtmlIndexPage ? path.join(baseDirectory, "index.html") : path.join(baseDirectory, fileName);
|
|
1702
|
+
};
|
|
1703
|
+
const handleGenerateFullPage = async (data) => {
|
|
1704
|
+
if (data.markup === void 0 || data.markup.length === 0)
|
|
1705
|
+
return;
|
|
1706
|
+
let htmlMarkup = data.markup;
|
|
1707
|
+
if (data.wrapper) {
|
|
1708
|
+
htmlMarkup = data.wrapper.replace("<wrapper-content/>", htmlMarkup);
|
|
1709
|
+
}
|
|
1710
|
+
await generateFullPageFile({
|
|
1711
|
+
id: data.id,
|
|
1712
|
+
filePath: getFullPageFilePath(data.fullpageFileName),
|
|
1713
|
+
page: {
|
|
1714
|
+
title: data.header,
|
|
1715
|
+
description: !data.hasMarkdownDescription ? data.description : void 0,
|
|
1716
|
+
lang: config.html.lang,
|
|
1717
|
+
htmlclass: data.htmlclass,
|
|
1718
|
+
bodyclass: data.bodyclass
|
|
1719
|
+
},
|
|
1720
|
+
css: config.html.assets.css,
|
|
1721
|
+
js: config.html.assets.js,
|
|
1722
|
+
html: htmlMarkup,
|
|
1723
|
+
theme: config.theme,
|
|
1724
|
+
ogImageUrl: config.plugins?.ogImage ? config.plugins.ogImage(data) : void 0
|
|
1725
|
+
});
|
|
1726
|
+
};
|
|
1727
|
+
const searchSectionMapping = [];
|
|
1728
|
+
const menuSectionMapping = [];
|
|
1729
|
+
const fileWriteTasks = [];
|
|
1730
|
+
let markupRepository = /* @__PURE__ */ new Map();
|
|
1731
|
+
parsedContent.forEach((firstLevelSection) => {
|
|
1732
|
+
firstLevelSection.sections.forEach((secondLevelSection) => {
|
|
1733
|
+
if (secondLevelSection.markup)
|
|
1734
|
+
markupRepository.set(secondLevelSection.id, { markup: secondLevelSection.markup });
|
|
1735
|
+
secondLevelSection.sections.forEach((thirdLevelSection) => {
|
|
1736
|
+
if (thirdLevelSection.markup)
|
|
1737
|
+
markupRepository.set(thirdLevelSection.id, { markup: thirdLevelSection.markup });
|
|
1738
|
+
});
|
|
1739
|
+
});
|
|
1740
|
+
});
|
|
1741
|
+
markupRepository = await compilePugMarkup(config.mode, config.contentDir, markupRepository);
|
|
1742
|
+
parsedContent.forEach((firstLevelSection, indexFirstLevel) => {
|
|
1743
|
+
searchSectionMapping[indexFirstLevel] = {
|
|
1744
|
+
title: firstLevelSection.header,
|
|
1745
|
+
items: []
|
|
1746
|
+
};
|
|
1747
|
+
menuSectionMapping[indexFirstLevel] = {
|
|
1748
|
+
title: firstLevelSection.header,
|
|
1749
|
+
items: []
|
|
1750
|
+
};
|
|
1751
|
+
firstLevelSection.sections.forEach((secondLevelSection, indexSecondLevel) => {
|
|
1752
|
+
const menuHref = indexFirstLevel === 0 && indexSecondLevel === 0 ? "/index.html" : `/${secondLevelSection.previewFileName}`;
|
|
1753
|
+
searchSectionMapping[indexFirstLevel].items.push({
|
|
1754
|
+
label: secondLevelSection.header,
|
|
1755
|
+
searchKeywords: [
|
|
1756
|
+
secondLevelSection.header,
|
|
1757
|
+
secondLevelSection.description,
|
|
1758
|
+
...secondLevelSection.sections.map((thirdLevelSection) => [thirdLevelSection.header, thirdLevelSection.description]).flat().filter(Boolean)
|
|
1759
|
+
].filter(Boolean).map((keyword) => keyword.replaceAll('"', "")),
|
|
1760
|
+
href: menuHref
|
|
1761
|
+
});
|
|
1762
|
+
menuSectionMapping[indexFirstLevel].items.push({
|
|
1763
|
+
label: secondLevelSection.header,
|
|
1764
|
+
href: menuHref
|
|
1765
|
+
});
|
|
1766
|
+
if (secondLevelSection.markup) {
|
|
1767
|
+
fileWriteTasks.push(
|
|
1768
|
+
(async () => {
|
|
1769
|
+
try {
|
|
1770
|
+
secondLevelSection.markup = markupRepository.get(secondLevelSection.id).markup;
|
|
1771
|
+
await handleGenerateFullPage(secondLevelSection);
|
|
1772
|
+
} catch (error) {
|
|
1773
|
+
console.error(`Error processing section ${secondLevelSection.id}:`, error);
|
|
1774
|
+
}
|
|
1775
|
+
})()
|
|
1776
|
+
);
|
|
1777
|
+
}
|
|
1778
|
+
secondLevelSection.sections.forEach((thirdLevelSection) => {
|
|
1779
|
+
if (!thirdLevelSection.markup)
|
|
1780
|
+
return;
|
|
1781
|
+
fileWriteTasks.push(
|
|
1782
|
+
(async () => {
|
|
1783
|
+
try {
|
|
1784
|
+
thirdLevelSection.markup = markupRepository.get(thirdLevelSection.id).markup;
|
|
1785
|
+
await handleGenerateFullPage(thirdLevelSection);
|
|
1786
|
+
} catch (error) {
|
|
1787
|
+
console.error(`Error processing section ${thirdLevelSection.id}:`, error);
|
|
1788
|
+
}
|
|
1789
|
+
})()
|
|
1790
|
+
);
|
|
1791
|
+
});
|
|
1792
|
+
});
|
|
1793
|
+
});
|
|
1794
|
+
const headerHtml = getHeaderHtml();
|
|
1795
|
+
const searchHtml = getSearchHtml(searchSectionMapping);
|
|
1796
|
+
parsedContent.forEach((firstLevelSection, indexFirstLevel) => {
|
|
1797
|
+
firstLevelSection.sections.forEach((secondLevelSection, indexSecondLevel) => {
|
|
1798
|
+
let sectionBefore = firstLevelSection.sections[indexSecondLevel - 1];
|
|
1799
|
+
if (!sectionBefore && !(indexFirstLevel === 0)) {
|
|
1800
|
+
sectionBefore = parsedContent[indexFirstLevel - 1].sections.at(-1);
|
|
1801
|
+
}
|
|
1802
|
+
let sectionAfter = firstLevelSection.sections[indexSecondLevel + 1];
|
|
1803
|
+
if (!sectionAfter && parsedContent[indexFirstLevel + 1]) {
|
|
1804
|
+
sectionAfter = parsedContent[indexFirstLevel + 1].sections.at(0);
|
|
1805
|
+
}
|
|
1806
|
+
const nextPageControlsData = {};
|
|
1807
|
+
if (sectionBefore) {
|
|
1808
|
+
nextPageControlsData.before = {
|
|
1809
|
+
label: sectionBefore.header,
|
|
1810
|
+
href: sectionBefore.previewFileName
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
if (sectionAfter) {
|
|
1814
|
+
nextPageControlsData.after = {
|
|
1815
|
+
label: sectionAfter.header,
|
|
1816
|
+
href: sectionAfter.previewFileName
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1819
|
+
const preloadIframes = [];
|
|
1820
|
+
if (secondLevelSection.markup) {
|
|
1821
|
+
preloadIframes.push(secondLevelSection.fullpageFileName);
|
|
1822
|
+
}
|
|
1823
|
+
secondLevelSection.sections.forEach((thirdLevelSection) => {
|
|
1824
|
+
if (thirdLevelSection.markup) {
|
|
1825
|
+
preloadIframes.push(thirdLevelSection.fullpageFileName);
|
|
1826
|
+
}
|
|
1827
|
+
});
|
|
1828
|
+
fileWriteTasks.push(
|
|
1829
|
+
generatePreviewFile({
|
|
1830
|
+
filePath: getPreviewPageFilePath(secondLevelSection.previewFileName, indexFirstLevel === 0 && indexSecondLevel === 0),
|
|
1831
|
+
page: {
|
|
1832
|
+
title: secondLevelSection.header,
|
|
1833
|
+
description: secondLevelSection.description,
|
|
1834
|
+
lang: config.html.lang
|
|
1835
|
+
},
|
|
1836
|
+
css: config.html.assets.css,
|
|
1837
|
+
js: config.html.assets.js,
|
|
1838
|
+
html: {
|
|
1839
|
+
header: headerHtml,
|
|
1840
|
+
sidebarMenu: getSidebarMenuHtml(
|
|
1841
|
+
menuSectionMapping,
|
|
1842
|
+
secondLevelSection.previewFileName
|
|
1843
|
+
),
|
|
1844
|
+
mainContent: getMainContentHtml(secondLevelSection, config),
|
|
1845
|
+
nextPageControls: getNextPageControlsHtml(nextPageControlsData),
|
|
1846
|
+
search: searchHtml,
|
|
1847
|
+
codeAuditDialog: getCodeAuditDialog(),
|
|
1848
|
+
alerts: getAlerts(),
|
|
1849
|
+
preloadIframes
|
|
1850
|
+
},
|
|
1851
|
+
theme: config.theme,
|
|
1852
|
+
ogImageUrl: config.plugins?.ogImage ? config.plugins.ogImage(secondLevelSection) : void 0
|
|
1853
|
+
})
|
|
1854
|
+
);
|
|
1855
|
+
});
|
|
1856
|
+
});
|
|
1857
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
1858
|
+
const __dirname = path.dirname(__filename);
|
|
1859
|
+
const findAssetsDirectoryPath = () => {
|
|
1860
|
+
const isLibraryDevelopmentIndexTs = __filename.endsWith("/lib/index.ts");
|
|
1861
|
+
if (isLibraryDevelopmentIndexTs)
|
|
1862
|
+
return path.resolve(process.cwd(), "dist/styleguide-assets");
|
|
1863
|
+
return path.resolve(__dirname, "../../styleguide-assets");
|
|
1864
|
+
};
|
|
1865
|
+
const assetsDirectoryPath = findAssetsDirectoryPath();
|
|
1866
|
+
const assetsDirectoryOutputPath = path.join(config.outDir, "styleguide-assets");
|
|
1867
|
+
const isAssetsDirectoryAlreadyCopied = await fs.exists(assetsDirectoryOutputPath) && (await fs.readdir(assetsDirectoryOutputPath)).length > 0;
|
|
1868
|
+
if (!isAssetsDirectoryAlreadyCopied) {
|
|
1869
|
+
await fs.copy(assetsDirectoryPath, assetsDirectoryOutputPath);
|
|
1870
|
+
await generateFaviconFiles(assetsDirectoryOutputPath, config.theme);
|
|
1871
|
+
}
|
|
1872
|
+
await Promise.all(fileWriteTasks);
|
|
1873
|
+
const errors = {};
|
|
1874
|
+
if (overwrittenSectionsIds.length > 0) {
|
|
1875
|
+
errors.overwrittenSectionsIds = overwrittenSectionsIds;
|
|
1876
|
+
}
|
|
1877
|
+
return {
|
|
1878
|
+
errors: Object.keys(errors).length > 0 ? errors : void 0
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
async function watchStyleguide(config, onChange, onError) {
|
|
1882
|
+
globalThis.isWatchMode = true;
|
|
1883
|
+
const initialBuild = await buildStyleguide(config);
|
|
1884
|
+
if (onError && initialBuild.errors) {
|
|
1885
|
+
onError(initialBuild.errors);
|
|
1886
|
+
}
|
|
1887
|
+
const contentDirPath = config.contentDir.endsWith("/") ? config.contentDir : `${config.contentDir}/`;
|
|
1888
|
+
watchStyleguideForChanges(contentDirPath, () => {
|
|
1889
|
+
(async () => {
|
|
1890
|
+
const localBuild = await buildStyleguide(config);
|
|
1891
|
+
if (onChange)
|
|
1892
|
+
onChange();
|
|
1893
|
+
if (onError && localBuild.errors) {
|
|
1894
|
+
onError(localBuild.errors);
|
|
1895
|
+
}
|
|
1896
|
+
})();
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
export { buildStyleguide, watchStyleguide };
|