@soubiran/vite 0.4.0 → 0.5.0
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 +243 -0
- package/dist/index.d.mts +7 -0
- package/dist/{vite.config.mjs → index.mjs} +455 -411
- package/dist/types-DznJAci_.d.mts +36 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +1 -0
- package/dist/utils-DYsc-HAW.mjs +21 -0
- package/dist/utils.mjs +2 -0
- package/package.json +12 -8
- package/dist/src/utils.mjs +0 -10
- package/dist/vite.config.d.mts +0 -78
- /package/dist/{src/utils.d.mts → utils.d.mts} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as vueIncludePatterns, i as markdownExtensionRE, n as toUrl, r as componentIncludePatterns, t as getUri } from "./utils-DYsc-HAW.mjs";
|
|
2
2
|
import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
4
3
|
import ui from "@nuxt/ui/vite";
|
|
5
4
|
import soubiranComposablesImports from "@soubiran/ui/imports";
|
|
6
5
|
import soubiranResolver from "@soubiran/ui/resolver";
|
|
6
|
+
import soubiranWrapperClasses from "@soubiran/ui/wrapper-classes";
|
|
7
7
|
import { unheadVueComposablesImports } from "@unhead/vue";
|
|
8
8
|
import vue from "@vitejs/plugin-vue";
|
|
9
9
|
import matter from "gray-matter";
|
|
@@ -11,8 +11,11 @@ import fonts from "unplugin-fonts/vite";
|
|
|
11
11
|
import icons from "unplugin-icons/vite";
|
|
12
12
|
import markdown from "unplugin-vue-markdown/vite";
|
|
13
13
|
import vueRouter from "unplugin-vue-router/vite";
|
|
14
|
-
import { defineConfig, mergeConfig } from "vite";
|
|
15
14
|
import { joinURL, withoutTrailingSlash } from "ufo";
|
|
15
|
+
import { Buffer } from "node:buffer";
|
|
16
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
17
|
+
import fs from "fs-extra";
|
|
18
|
+
import sharp from "sharp";
|
|
16
19
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
17
20
|
import { blurhashToDataUri } from "@unpic/placeholder";
|
|
18
21
|
import MarkdownItGitHubAlerts from "markdown-it-github-alerts";
|
|
@@ -20,14 +23,11 @@ import implicitFigures from "markdown-it-image-figures";
|
|
|
20
23
|
import linkAttributes from "markdown-it-link-attributes";
|
|
21
24
|
import { fromAsyncCodeToHtml } from "@shikijs/markdown-it/async";
|
|
22
25
|
import { codeToHtml } from "shiki";
|
|
23
|
-
import { Buffer } from "node:buffer";
|
|
24
|
-
import fs from "fs-extra";
|
|
25
|
-
import sharp from "sharp";
|
|
26
|
-
import { cwd } from "node:process";
|
|
27
26
|
import { cyan, dim, green, yellow } from "ansis";
|
|
27
|
+
import { cwd } from "node:process";
|
|
28
28
|
import { createHash } from "node:crypto";
|
|
29
29
|
import { SitemapStream } from "sitemap";
|
|
30
|
-
//#region src/assert.ts
|
|
30
|
+
//#region src/markdown/assert.ts
|
|
31
31
|
function createAssert(customAssert) {
|
|
32
32
|
return (id, frontmatter) => {
|
|
33
33
|
if (frontmatter.description) {
|
|
@@ -38,7 +38,7 @@ function createAssert(customAssert) {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
//#endregion
|
|
41
|
-
//#region src/canonical.ts
|
|
41
|
+
//#region src/markdown/canonical.ts
|
|
42
42
|
function getCanonicalUrl(id, hostname) {
|
|
43
43
|
return joinURL(toUrl(hostname), getUri(id));
|
|
44
44
|
}
|
|
@@ -56,6 +56,226 @@ function canonical(id, frontmatter, hostname) {
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
//#endregion
|
|
59
|
+
//#region src/domain/promise.ts
|
|
60
|
+
const promises = [];
|
|
61
|
+
async function resolveAll() {
|
|
62
|
+
await Promise.all(promises);
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/markdown/og.ts
|
|
66
|
+
const ogSVG = fs.readFileSync(new URL("./og-template.svg", import.meta.url), "utf-8");
|
|
67
|
+
const titleBreakRE = /(.{0,30})(?:\s|$)/g;
|
|
68
|
+
const templateTokenRE = /\{\{([^}]+)\}\}/g;
|
|
69
|
+
const titleSuffixRE = /\s-\s.*$/;
|
|
70
|
+
async function generate(title, hostname, output) {
|
|
71
|
+
if (fs.existsSync(output)) return;
|
|
72
|
+
await fs.mkdir(dirname(output), { recursive: true });
|
|
73
|
+
const lines = title.trim().split(titleBreakRE).filter(Boolean);
|
|
74
|
+
const data = {
|
|
75
|
+
line1: lines[0],
|
|
76
|
+
line2: lines[1],
|
|
77
|
+
line3: lines[2],
|
|
78
|
+
headline: "",
|
|
79
|
+
hostname
|
|
80
|
+
};
|
|
81
|
+
const svg = ogSVG.replace(templateTokenRE, (_, name) => data[name] || "");
|
|
82
|
+
console.log(`Generating ${output}`);
|
|
83
|
+
try {
|
|
84
|
+
await sharp(Buffer.from(svg)).resize(1200 * 1.1, 630 * 1.1).png().toFile(output);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.error("Failed to generate og image", e);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function og(id, frontmatter, hostname) {
|
|
90
|
+
(() => {
|
|
91
|
+
const path = `og/${basename(id, ".md")}.png`;
|
|
92
|
+
promises.push(generate(frontmatter.title.replace(titleSuffixRE, "").trim(), hostname, `public/${path}`));
|
|
93
|
+
frontmatter.image = `https://${hostname}/${path}`;
|
|
94
|
+
})();
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/markdown/structured-data/constants.ts
|
|
98
|
+
const DEFAULT_PERSON = {
|
|
99
|
+
name: "Estéban Soubiran",
|
|
100
|
+
sameAs: [
|
|
101
|
+
"https://x.com/soubiran_",
|
|
102
|
+
"https://www.linkedin.com/in/esteban25",
|
|
103
|
+
"https://www.twitch.tv/barbapapazes",
|
|
104
|
+
"https://www.youtube.com/@barbapapazes",
|
|
105
|
+
"https://github.com/barbapapazes",
|
|
106
|
+
"https://soubiran.dev",
|
|
107
|
+
"https://esteban-soubiran.site",
|
|
108
|
+
"https://barbapapazes.dev"
|
|
109
|
+
]
|
|
110
|
+
};
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/markdown/structured-data/schemas/article.ts
|
|
113
|
+
/**
|
|
114
|
+
* @see https://developer.yoast.com/features/schema/pieces/article/
|
|
115
|
+
*/
|
|
116
|
+
function article(id, structuredData, properties, options) {
|
|
117
|
+
const { title, description } = properties;
|
|
118
|
+
return { data: {
|
|
119
|
+
"@type": "Article",
|
|
120
|
+
"@id": joinURL(toUrl(options.hostname), "#", "schema", "Article", getUri(id)),
|
|
121
|
+
"headline": title,
|
|
122
|
+
"description": description,
|
|
123
|
+
"isPartOf": { "@id": structuredData.webpage.data["@id"] },
|
|
124
|
+
"mainEntityOfPage": { "@id": structuredData.webpage.data["@id"] },
|
|
125
|
+
"datePublished": structuredData.webpage.data.datePublished ? structuredData.webpage.data.datePublished : void 0,
|
|
126
|
+
"author": { "@id": structuredData.person.data["@id"] },
|
|
127
|
+
"publisher": { "@id": structuredData.person.data["@id"] },
|
|
128
|
+
"inLanguage": structuredData.webpage.data.inLanguage
|
|
129
|
+
} };
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/markdown/structured-data/schemas/breadcrumb.ts
|
|
133
|
+
/**
|
|
134
|
+
* @see https://developer.yoast.com/features/schema/pieces/breadcrumb/
|
|
135
|
+
*/
|
|
136
|
+
function breadcrumb(id, items, options) {
|
|
137
|
+
return { data: {
|
|
138
|
+
"@type": "BreadcrumbList",
|
|
139
|
+
"@id": joinURL(toUrl(options.hostname), "#", "schema", "BreadcrumbList", getUri(id)),
|
|
140
|
+
"itemListElement": items.map((item, index) => ({
|
|
141
|
+
"@type": "ListItem",
|
|
142
|
+
"position": index + 1,
|
|
143
|
+
"name": item.title,
|
|
144
|
+
...item.type && item.url ? { item: {
|
|
145
|
+
"@type": item.type,
|
|
146
|
+
"@id": item.url
|
|
147
|
+
} } : {}
|
|
148
|
+
}))
|
|
149
|
+
} };
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/markdown/structured-data/schemas/person.ts
|
|
153
|
+
/**
|
|
154
|
+
* @see https://developer.yoast.com/features/schema/pieces/person/
|
|
155
|
+
*/
|
|
156
|
+
function person(properties, options) {
|
|
157
|
+
return { data: {
|
|
158
|
+
"@type": "Person",
|
|
159
|
+
"@id": joinURL(options.url, "#", "schema", "Person", "1"),
|
|
160
|
+
"name": properties.name,
|
|
161
|
+
"sameAs": properties.sameAs
|
|
162
|
+
} };
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/markdown/structured-data/schemas/webpage.ts
|
|
166
|
+
/**
|
|
167
|
+
* @see https://developer.yoast.com/features/schema/pieces/webpage/
|
|
168
|
+
*/
|
|
169
|
+
function webpage(id, structuredData, properties, options) {
|
|
170
|
+
const { title, description, datePublished, keywords } = properties;
|
|
171
|
+
const canonicalUrl = getCanonicalUrl(id, options.hostname);
|
|
172
|
+
const data = {
|
|
173
|
+
"@type": "WebPage",
|
|
174
|
+
"@id": canonicalUrl,
|
|
175
|
+
"url": canonicalUrl,
|
|
176
|
+
"name": title,
|
|
177
|
+
"description": description,
|
|
178
|
+
"isPartOf": { "@id": structuredData.website.data["@id"] },
|
|
179
|
+
"inLanguage": "en-US",
|
|
180
|
+
"potentialAction": [{
|
|
181
|
+
"@type": "ReadAction",
|
|
182
|
+
"target": [canonicalUrl]
|
|
183
|
+
}],
|
|
184
|
+
...datePublished ? { datePublished: datePublished.toISOString() } : {},
|
|
185
|
+
...keywords ? { keywords } : {}
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
data,
|
|
189
|
+
setBreadcrumb(breadcrumbData) {
|
|
190
|
+
data.breadcrumb = { "@id": breadcrumbData.data["@id"] };
|
|
191
|
+
},
|
|
192
|
+
setCollection() {
|
|
193
|
+
data["@type"] = "CollectionPage";
|
|
194
|
+
delete data.potentialAction;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/markdown/structured-data/schemas/website.ts
|
|
200
|
+
/**
|
|
201
|
+
* @see https://developer.yoast.com/features/schema/pieces/website/
|
|
202
|
+
*/
|
|
203
|
+
function website(structuredData, options) {
|
|
204
|
+
return { data: {
|
|
205
|
+
"@type": "WebSite",
|
|
206
|
+
"@id": joinURL(options.url, "#", "schema", "WebSite", "1"),
|
|
207
|
+
"url": options.url,
|
|
208
|
+
"name": options.name,
|
|
209
|
+
"inLanguage": ["en-US"],
|
|
210
|
+
"publisher": { "@id": structuredData.person.data["@id"] }
|
|
211
|
+
} };
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/markdown/structured-data/index.ts
|
|
215
|
+
function structuredData(id, frontmatter, options) {
|
|
216
|
+
const { name, hostname, extractPage, getPageConfig } = options;
|
|
217
|
+
const graph = {
|
|
218
|
+
"@context": "https://schema.org",
|
|
219
|
+
"@graph": []
|
|
220
|
+
};
|
|
221
|
+
const structuredDataOptions = {
|
|
222
|
+
name,
|
|
223
|
+
hostname,
|
|
224
|
+
url: toUrl(hostname)
|
|
225
|
+
};
|
|
226
|
+
const personData = person(DEFAULT_PERSON, structuredDataOptions);
|
|
227
|
+
const websiteData = website({ person: personData }, structuredDataOptions);
|
|
228
|
+
const webpageData = webpage(id, { website: websiteData }, {
|
|
229
|
+
title: frontmatter.title,
|
|
230
|
+
description: frontmatter.description,
|
|
231
|
+
datePublished: frontmatter.date ? new Date(frontmatter.date) : void 0,
|
|
232
|
+
keywords: frontmatter.tags
|
|
233
|
+
}, structuredDataOptions);
|
|
234
|
+
const page = extractPage(id);
|
|
235
|
+
const pageConfig = getPageConfig?.(page, frontmatter);
|
|
236
|
+
if (pageConfig?.type === "article") {
|
|
237
|
+
const articleData = article(id, {
|
|
238
|
+
person: personData,
|
|
239
|
+
webpage: webpageData
|
|
240
|
+
}, {
|
|
241
|
+
title: frontmatter.title,
|
|
242
|
+
description: frontmatter.description
|
|
243
|
+
}, structuredDataOptions);
|
|
244
|
+
graph["@graph"].push(articleData.data);
|
|
245
|
+
if (pageConfig.breadcrumbItems) {
|
|
246
|
+
const breadcrumbData = breadcrumb(id, pageConfig.breadcrumbItems, structuredDataOptions);
|
|
247
|
+
graph["@graph"].push(breadcrumbData.data);
|
|
248
|
+
webpageData.setBreadcrumb(breadcrumbData);
|
|
249
|
+
}
|
|
250
|
+
} else if (pageConfig?.type === "collection") webpageData.setCollection();
|
|
251
|
+
graph["@graph"].push(personData.data, websiteData.data, webpageData.data);
|
|
252
|
+
frontmatter.script ??= [];
|
|
253
|
+
frontmatter.script.push({
|
|
254
|
+
type: "application/ld+json",
|
|
255
|
+
innerHTML: JSON.stringify(graph)
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/markdown/frontmatter.ts
|
|
260
|
+
function markdownFrontmatterFactory(options) {
|
|
261
|
+
return (frontmatter, frontmatterOptions, id, defaults) => {
|
|
262
|
+
createAssert(options.assertRules)(id, frontmatter);
|
|
263
|
+
og(id, frontmatter, options.hostname);
|
|
264
|
+
canonical(id, frontmatter, options.hostname);
|
|
265
|
+
structuredData(id, frontmatter, {
|
|
266
|
+
name: options.title,
|
|
267
|
+
hostname: options.hostname,
|
|
268
|
+
extractPage: options.extractPage,
|
|
269
|
+
getPageConfig: options.getPageConfig
|
|
270
|
+
});
|
|
271
|
+
frontmatter.page = options.extractPage(id);
|
|
272
|
+
return {
|
|
273
|
+
head: defaults(frontmatter, frontmatterOptions),
|
|
274
|
+
frontmatter
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
//#endregion
|
|
59
279
|
//#region src/markdown-it/custom-image.ts
|
|
60
280
|
function customImage(md, hostname) {
|
|
61
281
|
md.use((md) => {
|
|
@@ -87,13 +307,14 @@ function customImage(md, hostname) {
|
|
|
87
307
|
}
|
|
88
308
|
//#endregion
|
|
89
309
|
//#region src/markdown-it/custom-link.ts
|
|
310
|
+
const internalLinkRE$1 = /^https?:\/\/(?:[a-z0-9-]+\.)?soubiran\.dev(?:[/?#]|$)/;
|
|
90
311
|
function customLink(md, hostname) {
|
|
91
312
|
md.use((md) => {
|
|
92
313
|
const linkRule = md.renderer.rules.link_open;
|
|
93
314
|
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
|
|
94
315
|
const token = tokens[idx];
|
|
95
316
|
const href = token.attrGet("href");
|
|
96
|
-
if (href &&
|
|
317
|
+
if (href && internalLinkRE$1.test(href)) {
|
|
97
318
|
let linkText = "";
|
|
98
319
|
let nextIdx = idx + 1;
|
|
99
320
|
while (nextIdx < tokens.length && tokens[nextIdx].type !== "link_close") {
|
|
@@ -126,12 +347,14 @@ function implicitFiguresRule(md) {
|
|
|
126
347
|
}
|
|
127
348
|
//#endregion
|
|
128
349
|
//#region src/markdown-it/link-attributes.ts
|
|
350
|
+
const internalLinkRE = /^https?:\/\/(?:[a-z0-9-]+\.)?soubiran\.dev(?:[/?#]|$)/;
|
|
351
|
+
const externalLinkRE = /^https?:\/\//;
|
|
129
352
|
function linkAttributesRule(md) {
|
|
130
353
|
md.use(linkAttributes, [{
|
|
131
|
-
matcher: (link) =>
|
|
354
|
+
matcher: (link) => internalLinkRE.test(link),
|
|
132
355
|
attrs: { target: "_blank" }
|
|
133
356
|
}, {
|
|
134
|
-
matcher: (link) =>
|
|
357
|
+
matcher: (link) => externalLinkRE.test(link),
|
|
135
358
|
attrs: {
|
|
136
359
|
target: "_blank",
|
|
137
360
|
rel: "noopener"
|
|
@@ -371,52 +594,30 @@ function tableOfContentsRule(md) {
|
|
|
371
594
|
});
|
|
372
595
|
}
|
|
373
596
|
//#endregion
|
|
374
|
-
//#region src/
|
|
375
|
-
|
|
376
|
-
async
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
await fs.mkdir(dirname(output), { recursive: true });
|
|
385
|
-
const lines = title.trim().split(/(.{0,30})(?:\s|$)/g).filter(Boolean);
|
|
386
|
-
const data = {
|
|
387
|
-
line1: lines[0],
|
|
388
|
-
line2: lines[1],
|
|
389
|
-
line3: lines[2],
|
|
390
|
-
headline: "",
|
|
391
|
-
hostname
|
|
597
|
+
//#region src/markdown/rules.ts
|
|
598
|
+
function markdownRulesFactory(hostname) {
|
|
599
|
+
return async (md) => {
|
|
600
|
+
githubAlerts(md);
|
|
601
|
+
implicitFiguresRule(md);
|
|
602
|
+
linkAttributesRule(md);
|
|
603
|
+
tableOfContentsRule(md);
|
|
604
|
+
customLink(md, hostname);
|
|
605
|
+
customImage(md, hostname);
|
|
606
|
+
await shikiHighlight(md);
|
|
392
607
|
};
|
|
393
|
-
const svg = ogSVG.replace(/\{\{([^}]+)\}\}/g, (_, name) => data[name] || "");
|
|
394
|
-
console.log(`Generating ${output}`);
|
|
395
|
-
try {
|
|
396
|
-
await sharp(Buffer.from(svg)).resize(1200 * 1.1, 630 * 1.1).png().toFile(output);
|
|
397
|
-
} catch (e) {
|
|
398
|
-
console.error("Failed to generate og image", e);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
function og(id, frontmatter, hostname) {
|
|
402
|
-
(() => {
|
|
403
|
-
const path = `og/${basename(id, ".md")}.png`;
|
|
404
|
-
promises.push(generate(frontmatter.title.replace(/\s-\s.*$/, "").trim(), hostname, `public/${path}`));
|
|
405
|
-
frontmatter.image = `https://${hostname}/${path}`;
|
|
406
|
-
})();
|
|
407
608
|
}
|
|
408
609
|
//#endregion
|
|
409
|
-
//#region src/
|
|
610
|
+
//#region src/domain/api.ts
|
|
410
611
|
/**
|
|
411
612
|
* Recursively scan a directory for markdown files
|
|
412
613
|
*/
|
|
413
|
-
function scanMarkdownFiles(dir
|
|
614
|
+
function scanMarkdownFiles(dir) {
|
|
414
615
|
const files = [];
|
|
415
616
|
try {
|
|
416
617
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
417
618
|
for (const entry of entries) {
|
|
418
619
|
const fullPath = join(dir, entry.name);
|
|
419
|
-
if (entry.isDirectory()) files.push(...scanMarkdownFiles(fullPath
|
|
620
|
+
if (entry.isDirectory()) files.push(...scanMarkdownFiles(fullPath));
|
|
420
621
|
else if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "index.md") files.push(fullPath);
|
|
421
622
|
}
|
|
422
623
|
} catch {}
|
|
@@ -428,26 +629,28 @@ function scanMarkdownFiles(dir, baseDir) {
|
|
|
428
629
|
function processMarkdownFile(filePath, category) {
|
|
429
630
|
const { data } = matter(readFileSync(filePath, "utf-8"));
|
|
430
631
|
return {
|
|
431
|
-
path: `/${category}/${filePath.split("/").pop()?.replace(
|
|
632
|
+
path: `/${category}/${filePath.split("/").pop()?.replace(markdownExtensionRE, "") || ""}`,
|
|
432
633
|
...data
|
|
433
634
|
};
|
|
434
635
|
}
|
|
435
636
|
/**
|
|
436
637
|
* Generates the pages API JSON files in dist/api directory
|
|
437
638
|
*/
|
|
438
|
-
|
|
639
|
+
function generateJsonApi(outDir, categories, logger) {
|
|
439
640
|
const pagesDir = resolve(cwd(), "pages");
|
|
440
|
-
const distDir = resolve(cwd(),
|
|
641
|
+
const distDir = resolve(cwd(), outDir);
|
|
441
642
|
for (const name of categories) {
|
|
442
643
|
const processedFiles = scanMarkdownFiles(join(pagesDir, name)).map((file) => processMarkdownFile(file, name));
|
|
443
644
|
const apiDir = join(distDir, "api");
|
|
444
645
|
const path = join(apiDir, `${name}.json`);
|
|
445
646
|
mkdirSync(apiDir, { recursive: true });
|
|
446
647
|
writeFileSync(path, JSON.stringify(processedFiles, null, 2));
|
|
447
|
-
|
|
648
|
+
logger.info(`${dim(`${outDir}/`)}${cyan(path.replace(`${distDir}/`, ""))}`);
|
|
448
649
|
}
|
|
449
650
|
}
|
|
450
|
-
|
|
651
|
+
//#endregion
|
|
652
|
+
//#region src/plugins/api.ts
|
|
653
|
+
function api_default(categories = []) {
|
|
451
654
|
let config;
|
|
452
655
|
return {
|
|
453
656
|
name: "api",
|
|
@@ -457,83 +660,28 @@ function apiPlugin(categories = []) {
|
|
|
457
660
|
closeBundle() {
|
|
458
661
|
if (this.environment.name !== "client") return;
|
|
459
662
|
if (categories.length === 0) return;
|
|
460
|
-
const time =
|
|
663
|
+
const time = Date.now();
|
|
461
664
|
config.logger.info(yellow("Generate API files"));
|
|
462
|
-
|
|
463
|
-
config.logger.info(green(`✓ generated in ${
|
|
665
|
+
generateJsonApi(config.build.outDir, categories, config.logger);
|
|
666
|
+
config.logger.info(green(`✓ generated in ${Date.now() - time}ms`));
|
|
464
667
|
}
|
|
465
668
|
};
|
|
466
669
|
}
|
|
467
670
|
//#endregion
|
|
468
|
-
//#region src/plugins/
|
|
469
|
-
|
|
470
|
-
* Sanitize markdown content for LLM consumption
|
|
471
|
-
* - Removes HTML tags while preserving content inside paired tags
|
|
472
|
-
* - Adds title as H1 heading at the top
|
|
473
|
-
* - Safe for build-time processing
|
|
474
|
-
*/
|
|
475
|
-
function sanitizeMarkdown(content, title) {
|
|
476
|
-
let sanitized = content;
|
|
477
|
-
let prevSanitized = "";
|
|
478
|
-
while (sanitized !== prevSanitized) {
|
|
479
|
-
prevSanitized = sanitized;
|
|
480
|
-
sanitized = sanitized.replace(/<[^>]+>([^<]*)<\/[^>]+>/g, "$1");
|
|
481
|
-
sanitized = sanitized.replace(/<[^>]*>/g, "");
|
|
482
|
-
}
|
|
483
|
-
if (title) sanitized = `# ${title}\n\n${sanitized}`;
|
|
484
|
-
return sanitized.trim();
|
|
485
|
-
}
|
|
486
|
-
/**
|
|
487
|
-
* Recursively copy and sanitize markdown files from source to target directory
|
|
488
|
-
* - Parses frontmatter and removes it
|
|
489
|
-
* - Sanitizes HTML tags
|
|
490
|
-
* - Preserves directory structure
|
|
491
|
-
* - Converts /<something>/index.md to /<something>.md (except for root /index.md)
|
|
492
|
-
*/
|
|
493
|
-
function copyAndSanitizeMarkdownFiles(config, sourceDir, targetDir, isRoot = true) {
|
|
494
|
-
const outDir = join(resolve(cwd()), config.build.outDir);
|
|
495
|
-
const entries = readdirSync(sourceDir);
|
|
496
|
-
for (const entry of entries) {
|
|
497
|
-
const sourcePath = join(sourceDir, entry);
|
|
498
|
-
if (statSync(sourcePath).isDirectory()) {
|
|
499
|
-
const newTargetDir = join(targetDir, entry);
|
|
500
|
-
if (!existsSync(newTargetDir)) mkdirSync(newTargetDir, { recursive: true });
|
|
501
|
-
copyAndSanitizeMarkdownFiles(config, sourcePath, newTargetDir, false);
|
|
502
|
-
} else if (entry.endsWith(".md")) {
|
|
503
|
-
let targetPath;
|
|
504
|
-
if (entry === "index.md" && !isRoot) {
|
|
505
|
-
const parentDirName = basename(sourceDir);
|
|
506
|
-
targetPath = join(dirname(targetDir), `${parentDirName}.md`);
|
|
507
|
-
} else targetPath = join(targetDir, entry);
|
|
508
|
-
const targetDirPath = dirname(targetPath);
|
|
509
|
-
if (!existsSync(targetDirPath)) mkdirSync(targetDirPath, { recursive: true });
|
|
510
|
-
const { data, content } = matter(readFileSync(sourcePath, "utf-8"));
|
|
511
|
-
const sanitizedContent = sanitizeMarkdown(content, data.title);
|
|
512
|
-
writeFileSync(targetPath, sanitizedContent, "utf-8");
|
|
513
|
-
config.logger.info(`${dim(`${config.build.outDir}/`)}${cyan(targetPath.replace(`${outDir}/`, ""))}`);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
function markdownPlugin() {
|
|
518
|
-
let config;
|
|
671
|
+
//#region src/plugins/config.ts
|
|
672
|
+
function config_default() {
|
|
519
673
|
return {
|
|
520
|
-
name: "
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const pagesDir = resolve(cwd(), "pages");
|
|
527
|
-
const distDir = resolve(cwd(), config.build.outDir);
|
|
528
|
-
const time = /* @__PURE__ */ new Date();
|
|
529
|
-
config.logger.info(yellow("Copy and Sanitize Markdown"));
|
|
530
|
-
copyAndSanitizeMarkdownFiles(config, pagesDir, distDir);
|
|
531
|
-
config.logger.info(green(`✓ copied in ${(/* @__PURE__ */ new Date()).getTime() - time.getTime()}ms`));
|
|
674
|
+
name: "soubiran:config",
|
|
675
|
+
config() {
|
|
676
|
+
return {
|
|
677
|
+
optimizeDeps: { exclude: ["@soubiran/ui"] },
|
|
678
|
+
resolve: { alias: { "@": resolve("./src") } }
|
|
679
|
+
};
|
|
532
680
|
}
|
|
533
681
|
};
|
|
534
682
|
}
|
|
535
683
|
//#endregion
|
|
536
|
-
//#region src/
|
|
684
|
+
//#region src/domain/meta.ts
|
|
537
685
|
/**
|
|
538
686
|
* Generate a hash of the content for change detection
|
|
539
687
|
*/
|
|
@@ -555,15 +703,14 @@ function scanPagesForMeta(pagesDir, baseUri = "") {
|
|
|
555
703
|
} else if (entry.endsWith(".md")) {
|
|
556
704
|
const parsed = matter(readFileSync(fullPath, "utf-8"));
|
|
557
705
|
let uri = baseUri;
|
|
558
|
-
if (entry !== "index.md") uri = joinURL(baseUri, entry.replace(
|
|
706
|
+
if (entry !== "index.md") uri = joinURL(baseUri, entry.replace(markdownExtensionRE, ""));
|
|
559
707
|
uri = withoutTrailingSlash(uri);
|
|
560
708
|
pages.push({
|
|
561
709
|
uri,
|
|
562
710
|
title: parsed.data.title,
|
|
563
711
|
description: parsed.data.description,
|
|
564
712
|
content: parsed.content,
|
|
565
|
-
id: parsed.data.id
|
|
566
|
-
filePath: fullPath
|
|
713
|
+
id: parsed.data.id
|
|
567
714
|
});
|
|
568
715
|
}
|
|
569
716
|
}
|
|
@@ -572,9 +719,9 @@ function scanPagesForMeta(pagesDir, baseUri = "") {
|
|
|
572
719
|
/**
|
|
573
720
|
* Generate meta.json file with all pages metadata
|
|
574
721
|
*/
|
|
575
|
-
|
|
722
|
+
function generateMeta(outDir, hostname, logger) {
|
|
576
723
|
const pagesDir = resolve(cwd(), "pages");
|
|
577
|
-
const distDir = resolve(cwd(),
|
|
724
|
+
const distDir = resolve(cwd(), outDir);
|
|
578
725
|
const pages = scanPagesForMeta(pagesDir).filter((page) => page.title).map((page) => ({
|
|
579
726
|
id: page.id,
|
|
580
727
|
title: page.title,
|
|
@@ -586,9 +733,11 @@ async function generateMeta(config, hostname) {
|
|
|
586
733
|
pages.sort((a, b) => a.uri.localeCompare(b.uri));
|
|
587
734
|
const metaPath = join(distDir, "meta.json");
|
|
588
735
|
writeFileSync(metaPath, JSON.stringify(pages, null, 2));
|
|
589
|
-
|
|
736
|
+
logger.info(`${dim(`${outDir}/`)}${cyan(metaPath.replace(`${distDir}/`, ""))}`);
|
|
590
737
|
}
|
|
591
|
-
|
|
738
|
+
//#endregion
|
|
739
|
+
//#region src/plugins/meta.ts
|
|
740
|
+
function meta_default(hostname) {
|
|
592
741
|
let config;
|
|
593
742
|
return {
|
|
594
743
|
name: "meta",
|
|
@@ -597,321 +746,216 @@ function metaPlugin(hostname) {
|
|
|
597
746
|
},
|
|
598
747
|
closeBundle() {
|
|
599
748
|
if (this.environment.name !== "client") return;
|
|
600
|
-
const time =
|
|
749
|
+
const time = Date.now();
|
|
601
750
|
config.logger.info(yellow("Generate meta.json"));
|
|
602
|
-
generateMeta(config, hostname);
|
|
603
|
-
config.logger.info(green(`✓ generated in ${
|
|
751
|
+
generateMeta(config.build.outDir, hostname, config.logger);
|
|
752
|
+
config.logger.info(green(`✓ generated in ${Date.now() - time}ms`));
|
|
604
753
|
}
|
|
605
754
|
};
|
|
606
755
|
}
|
|
607
756
|
//#endregion
|
|
608
|
-
//#region src/
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
}
|
|
617
|
-
//#endregion
|
|
618
|
-
//#region src/structured-data/article.ts
|
|
619
|
-
/**
|
|
620
|
-
* @see https://developer.yoast.com/features/schema/pieces/article/
|
|
621
|
-
*/
|
|
622
|
-
function article(id, structuredData, properties, options) {
|
|
623
|
-
const { title, description } = properties;
|
|
624
|
-
return { data: {
|
|
625
|
-
"@type": "Article",
|
|
626
|
-
"@id": joinURL(toUrl(options.hostname), "#", "schema", "Article", getUri(id)),
|
|
627
|
-
"headline": title,
|
|
628
|
-
"description": description,
|
|
629
|
-
"isPartOf": { "@id": structuredData.webpage.data["@id"] },
|
|
630
|
-
"mainEntityOfPage": { "@id": structuredData.webpage.data["@id"] },
|
|
631
|
-
"datePublished": structuredData.webpage.data.datePublished ? structuredData.webpage.data.datePublished : void 0,
|
|
632
|
-
"author": { "@id": structuredData.person.data["@id"] },
|
|
633
|
-
"publisher": { "@id": structuredData.person.data["@id"] },
|
|
634
|
-
"inLanguage": structuredData.webpage.data.inLanguage
|
|
635
|
-
} };
|
|
757
|
+
//#region src/plugins/promise.ts
|
|
758
|
+
function promise_default() {
|
|
759
|
+
return {
|
|
760
|
+
name: "soubiran:promise",
|
|
761
|
+
async closeBundle() {
|
|
762
|
+
await resolveAll();
|
|
763
|
+
}
|
|
764
|
+
};
|
|
636
765
|
}
|
|
637
766
|
//#endregion
|
|
638
|
-
//#region src/
|
|
767
|
+
//#region src/domain/raw-markdown.ts
|
|
768
|
+
const pairedHtmlTagRE = /<[^>]+>([^<]*)<\/[^>]+>/g;
|
|
769
|
+
const htmlTagRE = /<[^>]*>/g;
|
|
639
770
|
/**
|
|
640
|
-
*
|
|
771
|
+
* Sanitize markdown content for LLM consumption
|
|
772
|
+
* - Removes HTML tags while preserving content inside paired tags
|
|
773
|
+
* - Adds title as H1 heading at the top
|
|
774
|
+
* - Safe for build-time processing
|
|
641
775
|
*/
|
|
642
|
-
function
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
"@id": item.url
|
|
653
|
-
} } : {}
|
|
654
|
-
}))
|
|
655
|
-
} };
|
|
776
|
+
function sanitizeMarkdown(content, title) {
|
|
777
|
+
let sanitized = content;
|
|
778
|
+
let prevSanitized = "";
|
|
779
|
+
while (sanitized !== prevSanitized) {
|
|
780
|
+
prevSanitized = sanitized;
|
|
781
|
+
sanitized = sanitized.replace(pairedHtmlTagRE, "$1");
|
|
782
|
+
sanitized = sanitized.replace(htmlTagRE, "");
|
|
783
|
+
}
|
|
784
|
+
if (title) sanitized = `# ${title}\n\n${sanitized}`;
|
|
785
|
+
return sanitized.trim();
|
|
656
786
|
}
|
|
657
|
-
//#endregion
|
|
658
|
-
//#region src/structured-data/person.ts
|
|
659
787
|
/**
|
|
660
|
-
*
|
|
788
|
+
* Recursively copy and sanitize markdown files from source to target directory
|
|
789
|
+
* - Parses frontmatter and removes it
|
|
790
|
+
* - Sanitizes HTML tags
|
|
791
|
+
* - Preserves directory structure
|
|
792
|
+
* - Converts /<something>/index.md to /<something>.md (except for root /index.md)
|
|
661
793
|
*/
|
|
662
|
-
function
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
794
|
+
function copyAndSanitizeMarkdownFiles(outDir, logger, sourceDir, targetDir, isRoot = true) {
|
|
795
|
+
const outDirPath = join(resolve(cwd()), outDir);
|
|
796
|
+
const entries = readdirSync(sourceDir);
|
|
797
|
+
for (const entry of entries) {
|
|
798
|
+
const sourcePath = join(sourceDir, entry);
|
|
799
|
+
if (statSync(sourcePath).isDirectory()) {
|
|
800
|
+
const newTargetDir = join(targetDir, entry);
|
|
801
|
+
if (!existsSync(newTargetDir)) mkdirSync(newTargetDir, { recursive: true });
|
|
802
|
+
copyAndSanitizeMarkdownFiles(outDir, logger, sourcePath, newTargetDir, false);
|
|
803
|
+
} else if (entry.endsWith(".md")) {
|
|
804
|
+
let targetPath;
|
|
805
|
+
if (entry === "index.md" && !isRoot) {
|
|
806
|
+
const parentDirName = basename(sourceDir);
|
|
807
|
+
targetPath = join(dirname(targetDir), `${parentDirName}.md`);
|
|
808
|
+
} else targetPath = join(targetDir, entry);
|
|
809
|
+
const targetDirPath = dirname(targetPath);
|
|
810
|
+
if (!existsSync(targetDirPath)) mkdirSync(targetDirPath, { recursive: true });
|
|
811
|
+
const { data, content } = matter(readFileSync(sourcePath, "utf-8"));
|
|
812
|
+
const sanitizedContent = sanitizeMarkdown(content, data.title);
|
|
813
|
+
writeFileSync(targetPath, sanitizedContent, "utf-8");
|
|
814
|
+
logger.info(`${dim(`${outDir}/`)}${cyan(targetPath.replace(`${outDirPath}/`, ""))}`);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
669
817
|
}
|
|
670
818
|
//#endregion
|
|
671
|
-
//#region src/
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
*/
|
|
675
|
-
function webpage(id, structuredData, properties, options) {
|
|
676
|
-
const { title, description, datePublished, keywords } = properties;
|
|
677
|
-
const canonicalUrl = getCanonicalUrl(id, options.hostname);
|
|
678
|
-
const data = {
|
|
679
|
-
"@type": "WebPage",
|
|
680
|
-
"@id": canonicalUrl,
|
|
681
|
-
"url": canonicalUrl,
|
|
682
|
-
"name": title,
|
|
683
|
-
"description": description,
|
|
684
|
-
"isPartOf": { "@id": structuredData.website.data["@id"] },
|
|
685
|
-
"inLanguage": "en-US",
|
|
686
|
-
"potentialAction": [{
|
|
687
|
-
"@type": "ReadAction",
|
|
688
|
-
"target": [canonicalUrl]
|
|
689
|
-
}],
|
|
690
|
-
...datePublished ? { datePublished: datePublished.toISOString() } : {},
|
|
691
|
-
...keywords ? { keywords } : {}
|
|
692
|
-
};
|
|
819
|
+
//#region src/plugins/raw-markdown.ts
|
|
820
|
+
function raw_markdown_default() {
|
|
821
|
+
let config;
|
|
693
822
|
return {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
823
|
+
name: "markdown",
|
|
824
|
+
configResolved(resolvedConfig) {
|
|
825
|
+
config = resolvedConfig;
|
|
697
826
|
},
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
827
|
+
closeBundle() {
|
|
828
|
+
if (this.environment.name !== "client") return;
|
|
829
|
+
const pagesDir = resolve(cwd(), "pages");
|
|
830
|
+
const distDir = resolve(cwd(), config.build.outDir);
|
|
831
|
+
const time = Date.now();
|
|
832
|
+
config.logger.info(yellow("Copy and Sanitize Markdown"));
|
|
833
|
+
copyAndSanitizeMarkdownFiles(config.build.outDir, config.logger, pagesDir, distDir);
|
|
834
|
+
config.logger.info(green(`✓ copied in ${Date.now() - time}ms`));
|
|
701
835
|
}
|
|
702
836
|
};
|
|
703
837
|
}
|
|
704
838
|
//#endregion
|
|
705
|
-
//#region src/
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
"@id": joinURL(options.url, "#", "schema", "WebSite", "1"),
|
|
713
|
-
"url": options.url,
|
|
714
|
-
"name": options.name,
|
|
715
|
-
"inLanguage": ["en-US"],
|
|
716
|
-
"publisher": { "@id": structuredData.person.data["@id"] }
|
|
717
|
-
} };
|
|
839
|
+
//#region src/domain/sitemap.ts
|
|
840
|
+
function generateSitemap(outDir, hostname, routes) {
|
|
841
|
+
const sitemapStream = new SitemapStream({ hostname: `https://${hostname}` });
|
|
842
|
+
const writeStream = createWriteStream(join(outDir, "sitemap.xml"));
|
|
843
|
+
sitemapStream.pipe(writeStream);
|
|
844
|
+
routes.forEach((item) => sitemapStream.write(item));
|
|
845
|
+
sitemapStream.end();
|
|
718
846
|
}
|
|
719
847
|
//#endregion
|
|
720
|
-
//#region src/
|
|
721
|
-
function
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
"
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
848
|
+
//#region src/plugins/sitemap.ts
|
|
849
|
+
function sitemap_default(hostname) {
|
|
850
|
+
const routes = /* @__PURE__ */ new Set();
|
|
851
|
+
let config;
|
|
852
|
+
return {
|
|
853
|
+
name: "soubiran:sitemap",
|
|
854
|
+
config() {
|
|
855
|
+
return { ssgOptions: {
|
|
856
|
+
onPageRendered(route, renderedHTML) {
|
|
857
|
+
routes.add(route);
|
|
858
|
+
return renderedHTML;
|
|
859
|
+
},
|
|
860
|
+
onFinished() {
|
|
861
|
+
generateSitemap(config.build.outDir, hostname, Array.from(routes));
|
|
862
|
+
}
|
|
863
|
+
} };
|
|
864
|
+
},
|
|
865
|
+
configResolved(resolvedConfig) {
|
|
866
|
+
config = resolvedConfig;
|
|
867
|
+
}
|
|
731
868
|
};
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
const page = extractPage(id);
|
|
741
|
-
const pageConfig = getPageConfig?.(page, frontmatter);
|
|
742
|
-
if (pageConfig?.type === "article") {
|
|
743
|
-
const articleData = article(id, {
|
|
744
|
-
person: personData,
|
|
745
|
-
webpage: webpageData
|
|
746
|
-
}, {
|
|
747
|
-
title: frontmatter.title,
|
|
748
|
-
description: frontmatter.description
|
|
749
|
-
}, structuredDataOptions);
|
|
750
|
-
graph["@graph"].push(articleData.data);
|
|
751
|
-
if (pageConfig.breadcrumbItems) {
|
|
752
|
-
const breadcrumbData = breadcrumb(id, pageConfig.breadcrumbItems, structuredDataOptions);
|
|
753
|
-
graph["@graph"].push(breadcrumbData.data);
|
|
754
|
-
webpageData.setBreadcrumb(breadcrumbData);
|
|
869
|
+
}
|
|
870
|
+
//#endregion
|
|
871
|
+
//#region src/plugins/ssg.ts
|
|
872
|
+
function ssg_default() {
|
|
873
|
+
return {
|
|
874
|
+
name: "soubiran:ssg",
|
|
875
|
+
config() {
|
|
876
|
+
return { ssgOptions: { formatting: "minify" } };
|
|
755
877
|
}
|
|
756
|
-
}
|
|
757
|
-
graph["@graph"].push(personData.data, websiteData.data, webpageData.data);
|
|
758
|
-
frontmatter.script ??= [];
|
|
759
|
-
frontmatter.script.push({
|
|
760
|
-
type: "application/ld+json",
|
|
761
|
-
innerHTML: JSON.stringify(graph)
|
|
762
|
-
});
|
|
878
|
+
};
|
|
763
879
|
}
|
|
764
880
|
//#endregion
|
|
765
|
-
//#region
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
"
|
|
771
|
-
"
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
} };
|
|
780
|
-
return mergeConfig(defineConfig({
|
|
781
|
-
plugins: [
|
|
782
|
-
vueRouter({
|
|
783
|
-
extensions: [".vue", ".md"],
|
|
784
|
-
routesFolder: "pages",
|
|
785
|
-
dts: "src/typed-router.d.ts",
|
|
786
|
-
extendRoute(route) {
|
|
787
|
-
const path = route.components.get("default");
|
|
788
|
-
if (!path) return;
|
|
789
|
-
if (path.endsWith(".vue")) route.addToMeta({ frontmatter: { page: options.extractPage(path) } });
|
|
790
|
-
if (path.endsWith(".md")) {
|
|
791
|
-
const { data } = matter(readFileSync(path, "utf-8"));
|
|
792
|
-
route.addToMeta({ frontmatter: data });
|
|
793
|
-
}
|
|
881
|
+
//#region src/index.ts
|
|
882
|
+
function soubiran(title, hostname, options) {
|
|
883
|
+
return [
|
|
884
|
+
vueRouter({
|
|
885
|
+
extensions: [".vue", ".md"],
|
|
886
|
+
routesFolder: "pages",
|
|
887
|
+
dts: "src/typed-router.d.ts",
|
|
888
|
+
extendRoute(route) {
|
|
889
|
+
const path = route.components.get("default");
|
|
890
|
+
if (!path) return;
|
|
891
|
+
if (path.endsWith(".vue")) route.addToMeta({ frontmatter: { page: options.extractPage(path) } });
|
|
892
|
+
if (path.endsWith(".md")) {
|
|
893
|
+
const { data } = matter(readFileSync(path, "utf-8"));
|
|
894
|
+
route.addToMeta({ frontmatter: data });
|
|
794
895
|
}
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
markdown
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
"prose-figure:bg-neutral-100 dark:prose-figure:bg-neutral-800 prose-figure:rounded-lg",
|
|
837
|
-
"prose-img:rounded-lg prose-img:border prose-img:border-accented prose-img:shadow-md",
|
|
838
|
-
"prose-video:rounded-lg prose-video:border prose-video:border-accented prose-video:shadow-md",
|
|
839
|
-
"prose-figcaption:text-center prose-figcaption:py-1 prose-figcaption:m-0",
|
|
840
|
-
"[&_:first-child]:mt-0 [&_:last-child]:mb-0"
|
|
841
|
-
],
|
|
842
|
-
transforms: options.markdown?.transforms ?? {},
|
|
843
|
-
wrapperComponent: options.markdown?.wrapperComponent,
|
|
844
|
-
async markdownItSetup(md) {
|
|
845
|
-
githubAlerts(md);
|
|
846
|
-
implicitFiguresRule(md);
|
|
847
|
-
linkAttributesRule(md);
|
|
848
|
-
tableOfContentsRule(md);
|
|
849
|
-
customLink(md, hostname);
|
|
850
|
-
customImage(md, hostname);
|
|
851
|
-
await shikiHighlight(md);
|
|
852
|
-
},
|
|
853
|
-
frontmatterPreprocess(frontmatter, frontmatterOptions, id, defaults) {
|
|
854
|
-
createAssert(options.seo?.assert?.rules)(id, frontmatter);
|
|
855
|
-
og(id, frontmatter, hostname);
|
|
856
|
-
canonical(id, frontmatter, hostname);
|
|
857
|
-
structuredData(id, frontmatter, {
|
|
858
|
-
name: title,
|
|
859
|
-
hostname,
|
|
860
|
-
person: options.seo?.person ?? seo.person,
|
|
861
|
-
extractPage: options.extractPage,
|
|
862
|
-
getPageConfig: options.seo?.structuredData?.pageConfig
|
|
863
|
-
});
|
|
864
|
-
frontmatter.page = options.extractPage(id);
|
|
865
|
-
return {
|
|
866
|
-
head: defaults(frontmatter, frontmatterOptions),
|
|
867
|
-
frontmatter
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
}),
|
|
871
|
-
fonts({ google: { families: [
|
|
872
|
-
{
|
|
873
|
-
name: "DM Sans",
|
|
874
|
-
styles: "ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000"
|
|
875
|
-
},
|
|
876
|
-
{
|
|
877
|
-
name: "DM Mono",
|
|
878
|
-
styles: "ital,wght@0,300;0,400;0,500;1,300;1,400;1,500"
|
|
879
|
-
},
|
|
880
|
-
{
|
|
881
|
-
name: "Sofia Sans",
|
|
882
|
-
styles: "ital,wght@0,1..1000;1,1..1000"
|
|
883
|
-
}
|
|
884
|
-
] } }),
|
|
885
|
-
icons({ autoInstall: true }),
|
|
886
|
-
apiPlugin(options.apiCategories),
|
|
887
|
-
markdownPlugin(),
|
|
888
|
-
metaPlugin(hostname),
|
|
896
|
+
}
|
|
897
|
+
}),
|
|
898
|
+
vue({ include: vueIncludePatterns }),
|
|
899
|
+
ui({
|
|
900
|
+
autoImport: {
|
|
901
|
+
dts: "src/auto-imports.d.ts",
|
|
902
|
+
dirs: ["src/composables"],
|
|
903
|
+
imports: [
|
|
904
|
+
"vue",
|
|
905
|
+
"vue-router",
|
|
906
|
+
"@vueuse/core",
|
|
907
|
+
unheadVueComposablesImports,
|
|
908
|
+
{
|
|
909
|
+
from: "tailwind-variants",
|
|
910
|
+
imports: ["tv"]
|
|
911
|
+
},
|
|
912
|
+
soubiranComposablesImports
|
|
913
|
+
]
|
|
914
|
+
},
|
|
915
|
+
components: {
|
|
916
|
+
include: componentIncludePatterns,
|
|
917
|
+
dts: "src/components.d.ts",
|
|
918
|
+
resolvers: [soubiranResolver()]
|
|
919
|
+
},
|
|
920
|
+
ui: { colors: { neutral: "neutral" } }
|
|
921
|
+
}),
|
|
922
|
+
markdown({
|
|
923
|
+
headEnabled: true,
|
|
924
|
+
wrapperClasses: soubiranWrapperClasses,
|
|
925
|
+
transforms: options.markdown?.transforms ?? {},
|
|
926
|
+
wrapperComponent: options.markdown?.wrapperComponent,
|
|
927
|
+
markdownItSetup: markdownRulesFactory(hostname),
|
|
928
|
+
frontmatterPreprocess: markdownFrontmatterFactory({
|
|
929
|
+
title,
|
|
930
|
+
hostname,
|
|
931
|
+
extractPage: options.extractPage,
|
|
932
|
+
assertRules: options.seo?.assert?.rules,
|
|
933
|
+
getPageConfig: options.seo?.structuredData?.pageConfig
|
|
934
|
+
})
|
|
935
|
+
}),
|
|
936
|
+
fonts({ google: { families: [
|
|
889
937
|
{
|
|
890
|
-
name: "
|
|
891
|
-
|
|
892
|
-
await resolveAll();
|
|
893
|
-
}
|
|
938
|
+
name: "DM Sans",
|
|
939
|
+
styles: "ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000"
|
|
894
940
|
},
|
|
895
941
|
{
|
|
896
|
-
name: "
|
|
897
|
-
|
|
898
|
-
Object.assign(config, resolvedConfig);
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
],
|
|
902
|
-
optimizeDeps: { exclude: ["@soubiran/ui"] },
|
|
903
|
-
resolve: { alias: { "@": resolve("./src") } },
|
|
904
|
-
ssgOptions: {
|
|
905
|
-
formatting: "minify",
|
|
906
|
-
onPageRendered(route, renderedHTML) {
|
|
907
|
-
routes.add(route);
|
|
908
|
-
return renderedHTML;
|
|
942
|
+
name: "DM Mono",
|
|
943
|
+
styles: "ital,wght@0,300;0,400;0,500;1,300;1,400;1,500"
|
|
909
944
|
},
|
|
910
|
-
|
|
911
|
-
|
|
945
|
+
{
|
|
946
|
+
name: "Sofia Sans",
|
|
947
|
+
styles: "ital,wght@0,1..1000;1,1..1000"
|
|
912
948
|
}
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
|
|
949
|
+
] } }),
|
|
950
|
+
icons({ autoInstall: true }),
|
|
951
|
+
config_default(),
|
|
952
|
+
ssg_default(),
|
|
953
|
+
meta_default(hostname),
|
|
954
|
+
api_default(options.api?.categories),
|
|
955
|
+
raw_markdown_default(),
|
|
956
|
+
sitemap_default(hostname),
|
|
957
|
+
promise_default()
|
|
958
|
+
];
|
|
959
|
+
}
|
|
916
960
|
//#endregion
|
|
917
|
-
export {
|
|
961
|
+
export { soubiran as default };
|