@soubiran/vite 0.3.1 → 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.
@@ -1,9 +1,9 @@
1
- import { n as toUrl, t as getUri } from "./utils-CgX7pSaU.mjs";
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,15 +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
-
31
- //#region src/assert.ts
30
+ //#region src/markdown/assert.ts
32
31
  function createAssert(customAssert) {
33
32
  return (id, frontmatter) => {
34
33
  if (frontmatter.description) {
@@ -38,9 +37,8 @@ function createAssert(customAssert) {
38
37
  customAssert?.(id, frontmatter);
39
38
  };
40
39
  }
41
-
42
40
  //#endregion
43
- //#region src/canonical.ts
41
+ //#region src/markdown/canonical.ts
44
42
  function getCanonicalUrl(id, hostname) {
45
43
  return joinURL(toUrl(hostname), getUri(id));
46
44
  }
@@ -57,13 +55,232 @@ function canonical(id, frontmatter, hostname) {
57
55
  href: url
58
56
  });
59
57
  }
60
-
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
+ }
61
278
  //#endregion
62
279
  //#region src/markdown-it/custom-image.ts
63
280
  function customImage(md, hostname) {
64
- md.use((md$1) => {
65
- const imageRule = md$1.renderer.rules.image;
66
- md$1.renderer.rules.image = async (tokens, idx, options, env, self) => {
281
+ md.use((md) => {
282
+ const imageRule = md.renderer.rules.image;
283
+ md.renderer.rules.image = async (tokens, idx, options, env, self) => {
67
284
  const token = tokens[idx];
68
285
  const src = token.attrGet("src");
69
286
  if (src) {
@@ -88,16 +305,16 @@ function customImage(md, hostname) {
88
305
  };
89
306
  });
90
307
  }
91
-
92
308
  //#endregion
93
309
  //#region src/markdown-it/custom-link.ts
310
+ const internalLinkRE$1 = /^https?:\/\/(?:[a-z0-9-]+\.)?soubiran\.dev(?:[/?#]|$)/;
94
311
  function customLink(md, hostname) {
95
- md.use((md$1) => {
96
- const linkRule = md$1.renderer.rules.link_open;
97
- md$1.renderer.rules.link_open = (tokens, idx, options, env, self) => {
312
+ md.use((md) => {
313
+ const linkRule = md.renderer.rules.link_open;
314
+ md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
98
315
  const token = tokens[idx];
99
316
  const href = token.attrGet("href");
100
- if (href && /^https?:\/\/(?:[a-z0-9-]+\.)?soubiran\.dev(?:[/?#]|$)/.test(href)) {
317
+ if (href && internalLinkRE$1.test(href)) {
101
318
  let linkText = "";
102
319
  let nextIdx = idx + 1;
103
320
  while (nextIdx < tokens.length && tokens[nextIdx].type !== "link_close") {
@@ -118,34 +335,32 @@ function customLink(md, hostname) {
118
335
  };
119
336
  });
120
337
  }
121
-
122
338
  //#endregion
123
339
  //#region src/markdown-it/github-alerts.ts
124
340
  function githubAlerts(md) {
125
341
  md.use(MarkdownItGitHubAlerts);
126
342
  }
127
-
128
343
  //#endregion
129
344
  //#region src/markdown-it/implicit-figures.ts
130
345
  function implicitFiguresRule(md) {
131
346
  md.use(implicitFigures, { figcaption: "alt" });
132
347
  }
133
-
134
348
  //#endregion
135
349
  //#region src/markdown-it/link-attributes.ts
350
+ const internalLinkRE = /^https?:\/\/(?:[a-z0-9-]+\.)?soubiran\.dev(?:[/?#]|$)/;
351
+ const externalLinkRE = /^https?:\/\//;
136
352
  function linkAttributesRule(md) {
137
353
  md.use(linkAttributes, [{
138
- matcher: (link) => /^https?:\/\/(?:[a-z0-9-]+\.)?soubiran\.dev(?:[/?#]|$)/.test(link),
354
+ matcher: (link) => internalLinkRE.test(link),
139
355
  attrs: { target: "_blank" }
140
356
  }, {
141
- matcher: (link) => /^https?:\/\//.test(link),
357
+ matcher: (link) => externalLinkRE.test(link),
142
358
  attrs: {
143
359
  target: "_blank",
144
360
  rel: "noopener"
145
361
  }
146
362
  }]);
147
363
  }
148
-
149
364
  //#endregion
150
365
  //#region src/markdown-it/shiki-highlight.ts
151
366
  async function shikiHighlight(md) {
@@ -157,7 +372,6 @@ async function shikiHighlight(md) {
157
372
  }
158
373
  }));
159
374
  }
160
-
161
375
  //#endregion
162
376
  //#region ../../node_modules/.pnpm/markdown-it-table-of-contents@1.1.0_patch_hash=bf17e463c2a0f62d05f1686e2d4c458136845ea3b2c32121b585ceededa45171/node_modules/markdown-it-table-of-contents/index.mjs
163
377
  /**
@@ -349,12 +563,11 @@ function flatHeadlineItemsToNestedTree(headlineItems) {
349
563
  });
350
564
  return toc;
351
565
  }
352
-
353
566
  //#endregion
354
567
  //#region src/markdown-it/table-of-contents.ts
355
568
  function tableOfContentsRule(md) {
356
- md.use((md$1) => {
357
- md$1.renderer.rules.heading_open = (tokens, idx, options, _env, self) => {
569
+ md.use((md) => {
570
+ md.renderer.rules.heading_open = (tokens, idx, options, _env, self) => {
358
571
  const token = tokens[idx];
359
572
  if (token.tag === "h2" || token.tag === "h3") {
360
573
  const inlineToken = tokens[idx + 1];
@@ -364,14 +577,14 @@ function tableOfContentsRule(md) {
364
577
  }
365
578
  return self.renderToken(tokens, idx, options);
366
579
  };
367
- md$1.renderer.rules.heading_close = (tokens, idx, options, _env, self) => {
580
+ md.renderer.rules.heading_close = (tokens, idx, options, _env, self) => {
368
581
  const token = tokens[idx];
369
582
  if (token.tag === "h2" || token.tag === "h3") return "</Heading>";
370
583
  return self.renderToken(tokens, idx, options);
371
584
  };
372
585
  });
373
- md.use((md$1) => {
374
- md$1.core.ruler.push("toc_to_env", (state) => {
586
+ md.use((md) => {
587
+ md.core.ruler.push("toc_to_env", (state) => {
375
588
  const options = defaultOptions;
376
589
  const tocTree = flatHeadlineItemsToNestedTree(findHeadlineElements(options.includeLevel, state.tokens, options));
377
590
  state.env.frontmatter = state.env.frontmatter || {};
@@ -380,56 +593,31 @@ function tableOfContentsRule(md) {
380
593
  });
381
594
  });
382
595
  }
383
-
384
- //#endregion
385
- //#region src/promise.ts
386
- const promises = [];
387
- async function resolveAll() {
388
- await Promise.all(promises);
389
- }
390
-
391
596
  //#endregion
392
- //#region src/og.ts
393
- const ogSVG = fs.readFileSync(new URL("./og-template.svg", import.meta.url), "utf-8");
394
- async function generate(title, hostname, output) {
395
- if (fs.existsSync(output)) return;
396
- await fs.mkdir(dirname(output), { recursive: true });
397
- const lines = title.trim().split(/(.{0,30})(?:\s|$)/g).filter(Boolean);
398
- const data = {
399
- line1: lines[0],
400
- line2: lines[1],
401
- line3: lines[2],
402
- headline: "",
403
- 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);
404
607
  };
405
- const svg = ogSVG.replace(/\{\{([^}]+)\}\}/g, (_, name) => data[name] || "");
406
- console.log(`Generating ${output}`);
407
- try {
408
- await sharp(Buffer.from(svg)).resize(1200 * 1.1, 630 * 1.1).png().toFile(output);
409
- } catch (e) {
410
- console.error("Failed to generate og image", e);
411
- }
412
- }
413
- function og(id, frontmatter, hostname) {
414
- (() => {
415
- const path = `og/${basename(id, ".md")}.png`;
416
- promises.push(generate(frontmatter.title.replace(/\s-\s.*$/, "").trim(), hostname, `public/${path}`));
417
- frontmatter.image = `https://${hostname}/${path}`;
418
- })();
419
608
  }
420
-
421
609
  //#endregion
422
- //#region src/plugins/api.ts
610
+ //#region src/domain/api.ts
423
611
  /**
424
612
  * Recursively scan a directory for markdown files
425
613
  */
426
- function scanMarkdownFiles(dir, baseDir) {
614
+ function scanMarkdownFiles(dir) {
427
615
  const files = [];
428
616
  try {
429
617
  const entries = readdirSync(dir, { withFileTypes: true });
430
618
  for (const entry of entries) {
431
619
  const fullPath = join(dir, entry.name);
432
- if (entry.isDirectory()) files.push(...scanMarkdownFiles(fullPath, baseDir));
620
+ if (entry.isDirectory()) files.push(...scanMarkdownFiles(fullPath));
433
621
  else if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "index.md") files.push(fullPath);
434
622
  }
435
623
  } catch {}
@@ -441,26 +629,28 @@ function scanMarkdownFiles(dir, baseDir) {
441
629
  function processMarkdownFile(filePath, category) {
442
630
  const { data } = matter(readFileSync(filePath, "utf-8"));
443
631
  return {
444
- path: `/${category}/${filePath.split("/").pop()?.replace(/\.md$/, "") || ""}`,
632
+ path: `/${category}/${filePath.split("/").pop()?.replace(markdownExtensionRE, "") || ""}`,
445
633
  ...data
446
634
  };
447
635
  }
448
636
  /**
449
637
  * Generates the pages API JSON files in dist/api directory
450
638
  */
451
- async function api(config, categories) {
639
+ function generateJsonApi(outDir, categories, logger) {
452
640
  const pagesDir = resolve(cwd(), "pages");
453
- const distDir = resolve(cwd(), config.build.outDir);
641
+ const distDir = resolve(cwd(), outDir);
454
642
  for (const name of categories) {
455
643
  const processedFiles = scanMarkdownFiles(join(pagesDir, name)).map((file) => processMarkdownFile(file, name));
456
644
  const apiDir = join(distDir, "api");
457
645
  const path = join(apiDir, `${name}.json`);
458
646
  mkdirSync(apiDir, { recursive: true });
459
647
  writeFileSync(path, JSON.stringify(processedFiles, null, 2));
460
- config.logger.info(`${dim(`${config.build.outDir}/`)}${cyan(path.replace(`${distDir}/`, ""))}`);
648
+ logger.info(`${dim(`${outDir}/`)}${cyan(path.replace(`${distDir}/`, ""))}`);
461
649
  }
462
650
  }
463
- function apiPlugin(categories = []) {
651
+ //#endregion
652
+ //#region src/plugins/api.ts
653
+ function api_default(categories = []) {
464
654
  let config;
465
655
  return {
466
656
  name: "api",
@@ -470,85 +660,28 @@ function apiPlugin(categories = []) {
470
660
  closeBundle() {
471
661
  if (this.environment.name !== "client") return;
472
662
  if (categories.length === 0) return;
473
- const time = /* @__PURE__ */ new Date();
663
+ const time = Date.now();
474
664
  config.logger.info(yellow("Generate API files"));
475
- api(config, categories);
476
- config.logger.info(green(`✓ generated in ${(/* @__PURE__ */ new Date()).getTime() - time.getTime()}ms`));
665
+ generateJsonApi(config.build.outDir, categories, config.logger);
666
+ config.logger.info(green(`✓ generated in ${Date.now() - time}ms`));
477
667
  }
478
668
  };
479
669
  }
480
-
481
670
  //#endregion
482
- //#region src/plugins/markdown.ts
483
- /**
484
- * Sanitize markdown content for LLM consumption
485
- * - Removes HTML tags while preserving content inside paired tags
486
- * - Adds title as H1 heading at the top
487
- * - Safe for build-time processing
488
- */
489
- function sanitizeMarkdown(content, title) {
490
- let sanitized = content;
491
- let prevSanitized = "";
492
- while (sanitized !== prevSanitized) {
493
- prevSanitized = sanitized;
494
- sanitized = sanitized.replace(/<[^>]+>([^<]*)<\/[^>]+>/g, "$1");
495
- sanitized = sanitized.replace(/<[^>]*>/g, "");
496
- }
497
- if (title) sanitized = `# ${title}\n\n${sanitized}`;
498
- return sanitized.trim();
499
- }
500
- /**
501
- * Recursively copy and sanitize markdown files from source to target directory
502
- * - Parses frontmatter and removes it
503
- * - Sanitizes HTML tags
504
- * - Preserves directory structure
505
- * - Converts /<something>/index.md to /<something>.md (except for root /index.md)
506
- */
507
- function copyAndSanitizeMarkdownFiles(config, sourceDir, targetDir, isRoot = true) {
508
- const outDir = join(resolve(cwd()), config.build.outDir);
509
- const entries = readdirSync(sourceDir);
510
- for (const entry of entries) {
511
- const sourcePath = join(sourceDir, entry);
512
- if (statSync(sourcePath).isDirectory()) {
513
- const newTargetDir = join(targetDir, entry);
514
- if (!existsSync(newTargetDir)) mkdirSync(newTargetDir, { recursive: true });
515
- copyAndSanitizeMarkdownFiles(config, sourcePath, newTargetDir, false);
516
- } else if (entry.endsWith(".md")) {
517
- let targetPath;
518
- if (entry === "index.md" && !isRoot) {
519
- const parentDirName = basename(sourceDir);
520
- targetPath = join(dirname(targetDir), `${parentDirName}.md`);
521
- } else targetPath = join(targetDir, entry);
522
- const targetDirPath = dirname(targetPath);
523
- if (!existsSync(targetDirPath)) mkdirSync(targetDirPath, { recursive: true });
524
- const { data, content } = matter(readFileSync(sourcePath, "utf-8"));
525
- const sanitizedContent = sanitizeMarkdown(content, data.title);
526
- writeFileSync(targetPath, sanitizedContent, "utf-8");
527
- config.logger.info(`${dim(`${config.build.outDir}/`)}${cyan(targetPath.replace(`${outDir}/`, ""))}`);
528
- }
529
- }
530
- }
531
- function markdownPlugin() {
532
- let config;
671
+ //#region src/plugins/config.ts
672
+ function config_default() {
533
673
  return {
534
- name: "markdown",
535
- configResolved(resolvedConfig) {
536
- config = resolvedConfig;
537
- },
538
- closeBundle() {
539
- if (this.environment.name !== "client") return;
540
- const pagesDir = resolve(cwd(), "pages");
541
- const distDir = resolve(cwd(), config.build.outDir);
542
- const time = /* @__PURE__ */ new Date();
543
- config.logger.info(yellow("Copy and Sanitize Markdown"));
544
- copyAndSanitizeMarkdownFiles(config, pagesDir, distDir);
545
- 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
+ };
546
680
  }
547
681
  };
548
682
  }
549
-
550
683
  //#endregion
551
- //#region src/plugins/meta.ts
684
+ //#region src/domain/meta.ts
552
685
  /**
553
686
  * Generate a hash of the content for change detection
554
687
  */
@@ -570,15 +703,14 @@ function scanPagesForMeta(pagesDir, baseUri = "") {
570
703
  } else if (entry.endsWith(".md")) {
571
704
  const parsed = matter(readFileSync(fullPath, "utf-8"));
572
705
  let uri = baseUri;
573
- if (entry !== "index.md") uri = joinURL(baseUri, entry.replace(/\.md$/, ""));
706
+ if (entry !== "index.md") uri = joinURL(baseUri, entry.replace(markdownExtensionRE, ""));
574
707
  uri = withoutTrailingSlash(uri);
575
708
  pages.push({
576
709
  uri,
577
710
  title: parsed.data.title,
578
711
  description: parsed.data.description,
579
712
  content: parsed.content,
580
- id: parsed.data.id,
581
- filePath: fullPath
713
+ id: parsed.data.id
582
714
  });
583
715
  }
584
716
  }
@@ -587,9 +719,9 @@ function scanPagesForMeta(pagesDir, baseUri = "") {
587
719
  /**
588
720
  * Generate meta.json file with all pages metadata
589
721
  */
590
- async function generateMeta(config, hostname) {
722
+ function generateMeta(outDir, hostname, logger) {
591
723
  const pagesDir = resolve(cwd(), "pages");
592
- const distDir = resolve(cwd(), config.build.outDir);
724
+ const distDir = resolve(cwd(), outDir);
593
725
  const pages = scanPagesForMeta(pagesDir).filter((page) => page.title).map((page) => ({
594
726
  id: page.id,
595
727
  title: page.title,
@@ -601,9 +733,11 @@ async function generateMeta(config, hostname) {
601
733
  pages.sort((a, b) => a.uri.localeCompare(b.uri));
602
734
  const metaPath = join(distDir, "meta.json");
603
735
  writeFileSync(metaPath, JSON.stringify(pages, null, 2));
604
- config.logger.info(`${dim(`${config.build.outDir}/`)}${cyan(metaPath.replace(`${distDir}/`, ""))}`);
736
+ logger.info(`${dim(`${outDir}/`)}${cyan(metaPath.replace(`${distDir}/`, ""))}`);
605
737
  }
606
- function metaPlugin(hostname) {
738
+ //#endregion
739
+ //#region src/plugins/meta.ts
740
+ function meta_default(hostname) {
607
741
  let config;
608
742
  return {
609
743
  name: "meta",
@@ -612,330 +746,216 @@ function metaPlugin(hostname) {
612
746
  },
613
747
  closeBundle() {
614
748
  if (this.environment.name !== "client") return;
615
- const time = /* @__PURE__ */ new Date();
749
+ const time = Date.now();
616
750
  config.logger.info(yellow("Generate meta.json"));
617
- generateMeta(config, hostname);
618
- config.logger.info(green(`✓ generated in ${(/* @__PURE__ */ new Date()).getTime() - time.getTime()}ms`));
751
+ generateMeta(config.build.outDir, hostname, config.logger);
752
+ config.logger.info(green(`✓ generated in ${Date.now() - time}ms`));
619
753
  }
620
754
  };
621
755
  }
622
-
623
- //#endregion
624
- //#region src/sitemap.ts
625
- const routes = /* @__PURE__ */ new Set();
626
- function sitemap(config, hostname, routes$1) {
627
- const sitemapStream = new SitemapStream({ hostname: `https://${hostname}` });
628
- const writeStream = createWriteStream(join(config.build.outDir, "sitemap.xml"));
629
- sitemapStream.pipe(writeStream);
630
- routes$1.forEach((item) => sitemapStream.write(item));
631
- sitemapStream.end();
632
- }
633
-
634
756
  //#endregion
635
- //#region src/structured-data/article.ts
636
- /**
637
- * @see https://developer.yoast.com/features/schema/pieces/article/
638
- */
639
- function article(id, structuredData$1, properties, options) {
640
- const { title, description } = properties;
641
- return { data: {
642
- "@type": "Article",
643
- "@id": joinURL(toUrl(options.hostname), "#", "schema", "Article", getUri(id)),
644
- "headline": title,
645
- "description": description,
646
- "isPartOf": { "@id": structuredData$1.webpage.data["@id"] },
647
- "mainEntityOfPage": { "@id": structuredData$1.webpage.data["@id"] },
648
- "datePublished": structuredData$1.webpage.data.datePublished ? structuredData$1.webpage.data.datePublished : void 0,
649
- "author": { "@id": structuredData$1.person.data["@id"] },
650
- "publisher": { "@id": structuredData$1.person.data["@id"] },
651
- "inLanguage": structuredData$1.webpage.data.inLanguage
652
- } };
757
+ //#region src/plugins/promise.ts
758
+ function promise_default() {
759
+ return {
760
+ name: "soubiran:promise",
761
+ async closeBundle() {
762
+ await resolveAll();
763
+ }
764
+ };
653
765
  }
654
-
655
766
  //#endregion
656
- //#region src/structured-data/breadcrumb.ts
767
+ //#region src/domain/raw-markdown.ts
768
+ const pairedHtmlTagRE = /<[^>]+>([^<]*)<\/[^>]+>/g;
769
+ const htmlTagRE = /<[^>]*>/g;
657
770
  /**
658
- * @see https://developer.yoast.com/features/schema/pieces/breadcrumb/
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
659
775
  */
660
- function breadcrumb(id, items, options) {
661
- return { data: {
662
- "@type": "BreadcrumbList",
663
- "@id": joinURL(toUrl(options.hostname), "#", "schema", "BreadcrumbList", getUri(id)),
664
- "itemListElement": items.map((item, index) => ({
665
- "@type": "ListItem",
666
- "position": index + 1,
667
- "name": item.title,
668
- ...item.type && item.url ? { item: {
669
- "@type": item.type,
670
- "@id": item.url
671
- } } : {}
672
- }))
673
- } };
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();
674
786
  }
675
-
676
- //#endregion
677
- //#region src/structured-data/person.ts
678
787
  /**
679
- * @see https://developer.yoast.com/features/schema/pieces/person/
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)
680
793
  */
681
- function person(options, personOptions) {
682
- return { data: {
683
- "@type": "Person",
684
- "@id": joinURL(options.url, "#", "schema", "Person", "1"),
685
- "name": personOptions.name,
686
- "sameAs": personOptions.sameAs
687
- } };
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
+ }
688
817
  }
689
-
690
818
  //#endregion
691
- //#region src/structured-data/webpage.ts
692
- /**
693
- * @see https://developer.yoast.com/features/schema/pieces/webpage/
694
- */
695
- function webpage(id, structuredData$1, properties, options) {
696
- const { title, description, datePublished, keywords } = properties;
697
- const canonicalUrl = getCanonicalUrl(id, options.hostname);
698
- const data = {
699
- "@type": "WebPage",
700
- "@id": canonicalUrl,
701
- "url": canonicalUrl,
702
- "name": title,
703
- "description": description,
704
- "isPartOf": { "@id": structuredData$1.website.data["@id"] },
705
- "inLanguage": "en-US",
706
- "potentialAction": [{
707
- "@type": "ReadAction",
708
- "target": [canonicalUrl]
709
- }],
710
- ...datePublished ? { datePublished: datePublished.toISOString() } : {},
711
- ...keywords ? { keywords } : {}
712
- };
819
+ //#region src/plugins/raw-markdown.ts
820
+ function raw_markdown_default() {
821
+ let config;
713
822
  return {
714
- data,
715
- setBreadcrumb(breadcrumbData) {
716
- data.breadcrumb = { "@id": breadcrumbData.data["@id"] };
823
+ name: "markdown",
824
+ configResolved(resolvedConfig) {
825
+ config = resolvedConfig;
717
826
  },
718
- setCollection() {
719
- data["@type"] = "CollectionPage";
720
- delete data.potentialAction;
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`));
721
835
  }
722
836
  };
723
837
  }
724
-
725
838
  //#endregion
726
- //#region src/structured-data/website.ts
727
- /**
728
- * @see https://developer.yoast.com/features/schema/pieces/website/
729
- */
730
- function website(structuredData$1, options) {
731
- return { data: {
732
- "@type": "WebSite",
733
- "@id": joinURL(options.url, "#", "schema", "WebSite", "1"),
734
- "url": options.url,
735
- "name": options.name,
736
- "inLanguage": ["en-US"],
737
- "publisher": { "@id": structuredData$1.person.data["@id"] }
738
- } };
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();
739
846
  }
740
-
741
847
  //#endregion
742
- //#region src/structured-data/index.ts
743
- function structuredData(id, frontmatter, options) {
744
- const { name, hostname, extractPage, getPageConfig } = options;
745
- const graph = {
746
- "@context": "https://schema.org",
747
- "@graph": []
748
- };
749
- const structuredDataOptions = {
750
- name,
751
- hostname,
752
- url: toUrl(hostname)
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
+ }
753
868
  };
754
- const personData = person(structuredDataOptions, options.person);
755
- const websiteData = website({ person: personData }, structuredDataOptions);
756
- const webpageData = webpage(id, { website: websiteData }, {
757
- title: frontmatter.title,
758
- description: frontmatter.description,
759
- datePublished: frontmatter.date ? new Date(frontmatter.date) : void 0,
760
- keywords: frontmatter.tags
761
- }, structuredDataOptions);
762
- const page = extractPage(id);
763
- const pageConfig = getPageConfig?.(page, frontmatter);
764
- if (pageConfig?.type === "article") {
765
- const articleData = article(id, {
766
- person: personData,
767
- webpage: webpageData
768
- }, {
769
- title: frontmatter.title,
770
- description: frontmatter.description
771
- }, structuredDataOptions);
772
- graph["@graph"].push(articleData.data);
773
- if (pageConfig.breadcrumbItems) {
774
- const breadcrumbData = breadcrumb(id, pageConfig.breadcrumbItems, structuredDataOptions);
775
- graph["@graph"].push(breadcrumbData.data);
776
- 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" } };
777
877
  }
778
- } else if (pageConfig?.type === "collection") webpageData.setCollection();
779
- graph["@graph"].push(personData.data, websiteData.data, webpageData.data);
780
- frontmatter.script ??= [];
781
- frontmatter.script.push({
782
- type: "application/ld+json",
783
- innerHTML: JSON.stringify(graph)
784
- });
878
+ };
785
879
  }
786
-
787
- //#endregion
788
- //#region vite.config.ts
789
- var vite_config_default = (title, hostname, options, config = {}) => {
790
- const seo = { person: {
791
- name: "Estéban Soubiran",
792
- sameAs: [
793
- "https://x.com/soubiran_",
794
- "https://www.linkedin.com/in/esteban25",
795
- "https://www.twitch.tv/barbapapazes",
796
- "https://www.youtube.com/@barbapapazes",
797
- "https://github.com/barbapapazes",
798
- "https://soubiran.dev",
799
- "https://esteban-soubiran.site",
800
- "https://barbapapazes.dev"
801
- ]
802
- } };
803
- return mergeConfig(defineConfig({
804
- plugins: [
805
- vueRouter({
806
- extensions: [".vue", ".md"],
807
- routesFolder: "pages",
808
- dts: "src/typed-router.d.ts",
809
- extendRoute(route) {
810
- const path = route.components.get("default");
811
- if (!path) return;
812
- if (path.endsWith(".vue")) route.addToMeta({ frontmatter: { page: options.extractPage(path) } });
813
- if (path.endsWith(".md")) {
814
- const { data } = matter(readFileSync(path, "utf-8"));
815
- route.addToMeta({ frontmatter: data });
816
- }
817
- }
818
- }),
819
- vue({ include: [/\.vue$/, /\.md$/] }),
820
- ui({
821
- autoImport: {
822
- dts: "src/auto-imports.d.ts",
823
- dirs: ["src/composables"],
824
- imports: [
825
- "vue",
826
- "vue-router",
827
- "@vueuse/core",
828
- unheadVueComposablesImports,
829
- {
830
- from: "tailwind-variants",
831
- imports: ["tv"]
832
- },
833
- soubiranComposablesImports
834
- ]
835
- },
836
- components: {
837
- include: [
838
- /\.vue$/,
839
- /\.vue\?vue/,
840
- /\.md$/
841
- ],
842
- dts: "src/components.d.ts",
843
- resolvers: [soubiranResolver()]
844
- },
845
- ui: { colors: { neutral: "neutral" } }
846
- }),
847
- markdown({
848
- headEnabled: true,
849
- wrapperClasses: [
850
- "slide-enter-content",
851
- "max-w-none",
852
- "prose prose-neutral dark:prose-invert",
853
- "prose-headings:text-default prose-h2:text-[1.125em] prose-h2:mb-[0.5em] prose-h3:text-[1em]",
854
- "prose-p:my-[1em] dark:prose-p:text-muted",
855
- "dark:prose-ul:text-muted dark:prose-ol:text-muted",
856
- "dark:prose-strong:text-default",
857
- "dark:prose-a:text-muted prose-a:font-semibold prose-a:no-underline prose-a:border-b prose-a:border-muted prose-a:transition-colors prose-a:duration-300 prose-a:ease-out prose-a:hover:border-[var(--ui-text-dimmed)]",
858
- "prose-hr:max-w-1/2 prose-hr:mx-auto prose-hr:my-[2em]",
859
- "prose-figure:bg-neutral-100 dark:prose-figure:bg-neutral-800 prose-figure:rounded-lg",
860
- "prose-img:rounded-lg prose-img:border prose-img:border-accented prose-img:shadow-md",
861
- "prose-video:rounded-lg prose-video:border prose-video:border-accented prose-video:shadow-md",
862
- "prose-figcaption:text-center prose-figcaption:py-1 prose-figcaption:m-0",
863
- "[&_:first-child]:mt-0 [&_:last-child]:mb-0"
864
- ],
865
- transforms: options.markdown?.transforms ?? {},
866
- wrapperComponent: options.markdown?.wrapperComponent,
867
- async markdownItSetup(md) {
868
- githubAlerts(md);
869
- implicitFiguresRule(md);
870
- linkAttributesRule(md);
871
- tableOfContentsRule(md);
872
- customLink(md, hostname);
873
- customImage(md, hostname);
874
- await shikiHighlight(md);
875
- },
876
- frontmatterPreprocess(frontmatter, frontmatterOptions, id, defaults) {
877
- createAssert(options.seo?.assert?.rules)(id, frontmatter);
878
- og(id, frontmatter, hostname);
879
- canonical(id, frontmatter, hostname);
880
- structuredData(id, frontmatter, {
881
- name: title,
882
- hostname,
883
- person: options.seo?.person ?? seo.person,
884
- extractPage: options.extractPage,
885
- getPageConfig: options.seo?.structuredData?.pageConfig
886
- });
887
- frontmatter.page = options.extractPage(id);
888
- return {
889
- head: defaults(frontmatter, frontmatterOptions),
890
- frontmatter
891
- };
892
- }
893
- }),
894
- fonts({ google: { families: [
895
- {
896
- name: "DM Sans",
897
- styles: "ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000"
898
- },
899
- {
900
- name: "DM Mono",
901
- styles: "ital,wght@0,300;0,400;0,500;1,300;1,400;1,500"
902
- },
903
- {
904
- name: "Sofia Sans",
905
- styles: "ital,wght@0,1..1000;1,1..1000"
880
+ //#endregion
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 });
906
895
  }
907
- ] } }),
908
- icons({ autoInstall: true }),
909
- apiPlugin(options.apiCategories),
910
- markdownPlugin(),
911
- 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: [
912
937
  {
913
- name: "await",
914
- async closeBundle() {
915
- await resolveAll();
916
- }
938
+ name: "DM Sans",
939
+ styles: "ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000"
917
940
  },
918
941
  {
919
- name: "extract-config",
920
- configResolved(resolvedConfig) {
921
- Object.assign(config, resolvedConfig);
922
- }
923
- }
924
- ],
925
- optimizeDeps: { exclude: ["@soubiran/ui"] },
926
- resolve: { alias: { "@": resolve("./src") } },
927
- ssgOptions: {
928
- formatting: "minify",
929
- onPageRendered(route, renderedHTML) {
930
- routes.add(route);
931
- return renderedHTML;
942
+ name: "DM Mono",
943
+ styles: "ital,wght@0,300;0,400;0,500;1,300;1,400;1,500"
932
944
  },
933
- onFinished() {
934
- sitemap(config, hostname, Array.from(routes));
945
+ {
946
+ name: "Sofia Sans",
947
+ styles: "ital,wght@0,1..1000;1,1..1000"
935
948
  }
936
- }
937
- }), config);
938
- };
939
-
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
+ }
940
960
  //#endregion
941
- export { vite_config_default as default };
961
+ export { soubiran as default };