imprensa 0.1.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +51 -0
  3. package/default.css +239 -0
  4. package/dist/client-runtime-D7MhMWCo.d.mts +45 -0
  5. package/dist/components/doc.d.mts +2 -0
  6. package/dist/components/doc.mjs +2 -0
  7. package/dist/components/icons.d.mts +23 -0
  8. package/dist/components/icons.mjs +40 -0
  9. package/dist/components/index.d.mts +33 -0
  10. package/dist/components/index.mjs +253 -0
  11. package/dist/core/client-runtime.d.mts +2 -0
  12. package/dist/core/client-runtime.mjs +121 -0
  13. package/dist/core/prerender-core.d.mts +2 -0
  14. package/dist/core/prerender-core.mjs +2 -0
  15. package/dist/core/runtime.d.mts +3 -0
  16. package/dist/core/runtime.mjs +3 -0
  17. package/dist/core/shiki-build.d.mts +9 -0
  18. package/dist/core/shiki-build.mjs +34 -0
  19. package/dist/doc-pager-D-YhwEQN.d.mts +27 -0
  20. package/dist/doc-toolbar-DUQS2gnK.mjs +460 -0
  21. package/dist/docs/config.d.mts +13 -0
  22. package/dist/docs/config.mjs +10 -0
  23. package/dist/docs/landing-shiki.d.mts +7 -0
  24. package/dist/docs/landing-shiki.mjs +7 -0
  25. package/dist/docs/mdx.d.mts +79 -0
  26. package/dist/docs/mdx.mjs +293 -0
  27. package/dist/docs/rehype.d.mts +25 -0
  28. package/dist/docs/rehype.mjs +2 -0
  29. package/dist/frontmatter-DVneGjCO.mjs +16 -0
  30. package/dist/global-search-Dfv8DYN3.mjs +310 -0
  31. package/dist/index.d.mts +41 -0
  32. package/dist/index.mjs +668 -0
  33. package/dist/prerender-core-D4Li--RS.mjs +172 -0
  34. package/dist/prerender-core-DBi9ntWW.d.mts +48 -0
  35. package/dist/rehype-BWpGaBql.mjs +182 -0
  36. package/dist/search-store-DDGHRAKl.mjs +64 -0
  37. package/dist/shiki-gFey7C-z.d.mts +3289 -0
  38. package/dist/sidebar-layout-DsEhSkJS.mjs +43 -0
  39. package/dist/socials-BIszPk-A.d.mts +8 -0
  40. package/docs/architecture.md +26 -0
  41. package/docs/integration-notes.md +6 -0
  42. package/index.d.ts +49 -0
  43. package/package.json +128 -0
  44. package/tsconfig.json +28 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,668 @@
1
+ import { THEME_STORAGE_KEY, applyInitialTheme, applyThemeToHtml, createImprensa, getStoredTheme, mountOrHydrate, setStoredTheme, shiki, shikiThemes } from "./core/client-runtime.mjs";
2
+ import { i as resolveShikiThemeIds, n as codeToSnippetHtml, r as resolveShikiLangs, t as createPrerender } from "./prerender-core-D4Li--RS.mjs";
3
+ import { createConfiguredHighlighterCore, resolveShikiLangModuleHref, resolveShikiThemeModuleHref } from "./core/shiki-build.mjs";
4
+ import { n as toStringArray, t as parseScalar } from "./frontmatter-DVneGjCO.mjs";
5
+ import { n as SIDEBAR_LAYOUT_BOOT_SCRIPT } from "./sidebar-layout-DsEhSkJS.mjs";
6
+ import { t as rehypeDeadLinks } from "./rehype-BWpGaBql.mjs";
7
+ import { createRequire } from "node:module";
8
+ import path from "node:path";
9
+ import { fileURLToPath, pathToFileURL } from "node:url";
10
+ import { pages } from "@ilha/router/vite";
11
+ import mdx from "@mdx-js/rollup";
12
+ import tailwindcss from "@tailwindcss/vite";
13
+ import rehypeAutolinkHeadings from "rehype-autolink-headings";
14
+ import rehypeSlug from "rehype-slug";
15
+ import { vitePrerenderPlugin } from "vite-prerender-plugin";
16
+ import sitemap from "vite-plugin-sitemap";
17
+ import fs, { existsSync } from "node:fs";
18
+ import rehypeShikiFromHighlighter from "@shikijs/rehype/core";
19
+ import { transformerTwoslash } from "@shikijs/twoslash";
20
+ //#region src/core/routes.ts
21
+ function normalizeContentDirPhysical$1(root, dir) {
22
+ const trimmed = dir.replace(/^\/+|\/+$/g, "").replace(/^src\//, "");
23
+ return path.join(root, "src", trimmed);
24
+ }
25
+ function normalizeContentDir(dir) {
26
+ return `/src/${dir.replace(/^\/+|\/+$/g, "").replace(/^src\//, "")}/`;
27
+ }
28
+ function filePathToRoutePathFromDisk(filePath, contentDirPhysical) {
29
+ const relative = path.relative(contentDirPhysical, filePath).replace(/\\/g, "/").replace(/\.mdx?$/, "").replace(/\/index$/, "");
30
+ if (!relative || relative === "index") return "/";
31
+ return `/${relative}`;
32
+ }
33
+ function collectRawMdxSources(root, contentDirOption) {
34
+ const contentDirPhysical = normalizeContentDirPhysical$1(root, contentDirOption);
35
+ if (!fs.existsSync(contentDirPhysical)) return {};
36
+ const sources = {};
37
+ function walk(dir) {
38
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
39
+ const entryPath = path.join(dir, entry.name);
40
+ if (entry.isDirectory()) {
41
+ walk(entryPath);
42
+ continue;
43
+ }
44
+ if (!/\.mdx?$/.test(entry.name)) continue;
45
+ sources[filePathToRoutePathFromDisk(entryPath, contentDirPhysical)] = fs.readFileSync(entryPath, "utf8");
46
+ }
47
+ }
48
+ walk(contentDirPhysical);
49
+ return sources;
50
+ }
51
+ function collectMdxRoutes(contentDirOption, root = process.cwd()) {
52
+ const contentDirPhysical = normalizeContentDirPhysical$1(root, contentDirOption);
53
+ if (!fs.existsSync(contentDirPhysical)) return [];
54
+ const routes = [];
55
+ function walk(dir) {
56
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
57
+ const entryPath = path.join(dir, entry.name);
58
+ if (entry.isDirectory()) {
59
+ walk(entryPath);
60
+ continue;
61
+ }
62
+ if (!/\.mdx?$/.test(entry.name)) continue;
63
+ routes.push(filePathToRoutePathFromDisk(entryPath, contentDirPhysical));
64
+ }
65
+ }
66
+ walk(contentDirPhysical);
67
+ return routes;
68
+ }
69
+ //#endregion
70
+ //#region src/core/shiki.ts
71
+ const require = createRequire(import.meta.url);
72
+ function resolveImportSpecifier(id) {
73
+ return pathToFileURL(require.resolve(id)).href;
74
+ }
75
+ function getShikiHighlighterOptions(options) {
76
+ if (options === false) return {
77
+ themes: ["night-owl-light", "houston"],
78
+ langs: ["typescript"],
79
+ clientShiki: false
80
+ };
81
+ const shiki = options ?? {};
82
+ return {
83
+ themes: resolveShikiThemeIds(shiki),
84
+ langs: resolveShikiLangs(shiki),
85
+ clientShiki: shiki.clientShiki !== false
86
+ };
87
+ }
88
+ function shikiPlugin(options) {
89
+ if (options === false) return [];
90
+ const { twoslash = true, transformers = [], themes, langs: _langs, clientShiki: _client, ...rest } = options ?? {};
91
+ const themeIds = resolveShikiThemeIds(options);
92
+ const langIds = resolveShikiLangs(options);
93
+ const themeRecord = themes ?? {
94
+ light: "night-owl-light",
95
+ dark: "houston"
96
+ };
97
+ return [function rehypeShikiConfigured() {
98
+ return async (tree) => {
99
+ return rehypeShikiFromHighlighter(await createConfiguredHighlighterCore(themeIds, langIds), {
100
+ themes: themeRecord,
101
+ langs: langIds,
102
+ ...rest,
103
+ transformers: twoslash ? [transformerTwoslash({ explicitTrigger: true }), ...transformers] : transformers
104
+ })(tree);
105
+ };
106
+ }];
107
+ }
108
+ /**
109
+ * Browser virtual module: static imports for configured themes only;
110
+ * each configured lang is a separate dynamic import (no Shiki language catalog).
111
+ */
112
+ function shikiFineGrainedRuntime(options) {
113
+ const themes = [...new Set(options.themes)];
114
+ const langs = [...new Set(options.langs.map((l) => l === "ts" ? "typescript" : l))];
115
+ const themeImports = themes.map((theme, index) => {
116
+ const href = resolveShikiThemeModuleHref(theme);
117
+ return `import theme${index} from ${JSON.stringify(href)};`;
118
+ });
119
+ const langCases = langs.map((lang) => {
120
+ const href = resolveShikiLangModuleHref(lang);
121
+ return `case ${JSON.stringify(lang)}: return import(${JSON.stringify(href)});`;
122
+ }).join("\n ");
123
+ const shikiCore = resolveImportSpecifier("shiki/core");
124
+ const shikiEngine = resolveImportSpecifier("shiki/engine/javascript");
125
+ const allowedJson = JSON.stringify(langs);
126
+ return `
127
+ import { createHighlighterCore } from ${JSON.stringify(shikiCore)};
128
+ import { createJavaScriptRegexEngine } from ${JSON.stringify(shikiEngine)};
129
+ ${themeImports.join("\n ")}
130
+ const __allowedLangs = new Set(${allowedJson});
131
+ async function __importLang(lang) {
132
+ const id = lang === "ts" ? "typescript" : lang;
133
+ if (!__allowedLangs.has(id)) {
134
+ throw new Error(
135
+ \`imprensa/shiki: language "\${lang}" is not in shiki.langs. Add it under imprensa({ shiki: { langs: [...] } }) in vite.config.\`,
136
+ );
137
+ }
138
+ switch (id) {
139
+ ${langCases || `default: throw new Error(\`shiki.langs is empty\`);`}
140
+ default:
141
+ throw new Error(\`imprensa/shiki: missing lazy import for "\${id}"\`);
142
+ }
143
+ }
144
+ const __core = await createHighlighterCore({
145
+ themes: [${themes.map((_, index) => `theme${index}`).join(", ")}],
146
+ langs: [],
147
+ engine: createJavaScriptRegexEngine(),
148
+ });
149
+ const __loaded = new Set();
150
+ export const shiki = {
151
+ loadLanguage: async (lang) => {
152
+ const id = lang === "ts" ? "typescript" : lang;
153
+ if (__loaded.has(id)) return;
154
+ const mod = await __importLang(lang);
155
+ await __core.loadLanguage(mod.default ?? mod);
156
+ __loaded.add(id);
157
+ },
158
+ codeToHtml: (code, opts) => __core.codeToHtml(code, opts),
159
+ };`;
160
+ }
161
+ //#endregion
162
+ //#region src/docs/llms.ts
163
+ function normalizeContentDirPhysical(root, contentDir) {
164
+ const trimmed = contentDir.replace(/^\/+|\/+$/g, "").replace(/^src\//, "");
165
+ return path.join(root, "src", trimmed);
166
+ }
167
+ function filePathToRoutePath(filePath, contentDirPhysical) {
168
+ const relative = path.relative(contentDirPhysical, filePath).replace(/\\/g, "/").replace(/\.mdx?$/, "").replace(/\/index$/, "");
169
+ if (!relative || relative === "index") return "/";
170
+ return `/${relative}`;
171
+ }
172
+ function titleize(value) {
173
+ return value.replace(/[-_]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
174
+ }
175
+ function parseFrontmatter(content) {
176
+ if (!content.startsWith("---")) return {};
177
+ const end = content.indexOf("\n---", 3);
178
+ if (end === -1) return {};
179
+ const yaml = content.slice(4, end);
180
+ const meta = {};
181
+ let listKey;
182
+ for (const line of yaml.split("\n")) {
183
+ const listItem = line.match(/^\s*-\s*(.+)$/);
184
+ if (listItem && listKey) {
185
+ const existing = meta[listKey];
186
+ const list = Array.isArray(existing) ? [...existing] : [];
187
+ list.push(parseScalar(listItem[1]));
188
+ meta[listKey] = list;
189
+ continue;
190
+ }
191
+ const m = line.match(/^([\w-]+):\s*(.*)$/);
192
+ if (!m) continue;
193
+ listKey = void 0;
194
+ if (!m[2].trim()) {
195
+ meta[m[1]] = [];
196
+ listKey = m[1];
197
+ } else meta[m[1]] = parseScalar(m[2]);
198
+ }
199
+ return {
200
+ title: typeof meta.title === "string" ? meta.title : void 0,
201
+ description: typeof meta.description === "string" ? meta.description : void 0,
202
+ order: typeof meta.order === "number" ? meta.order : void 0,
203
+ priority: typeof meta.priority === "number" ? meta.priority : void 0,
204
+ type: meta.type === "custom" || meta.type === "link" ? meta.type : "doc",
205
+ draft: meta.draft === true,
206
+ hidden: meta.hidden === true || meta.sidebar === false,
207
+ tags: toStringArray(meta.tags)
208
+ };
209
+ }
210
+ function stripFrontmatter(content) {
211
+ if (!content.startsWith("---")) return content;
212
+ const end = content.indexOf("\n---", 3);
213
+ return end === -1 ? content : content.slice(end + 4).replace(/^\s+/, "");
214
+ }
215
+ function titleFromSource(content, filePath, meta) {
216
+ if (meta.title) return meta.title;
217
+ const heading = stripFrontmatter(content).match(/^#\s+(.+)$/m);
218
+ if (heading) return heading[1].trim();
219
+ const name = path.basename(filePath).replace(/\.mdx?$/, "");
220
+ return titleize(name === "index" ? "overview" : name);
221
+ }
222
+ function routeToDistRelative(routePath) {
223
+ if (routePath === "/") return "index.md";
224
+ return `${routePath.slice(1)}/index.md`;
225
+ }
226
+ function collectContentFiles(contentDirPhysical) {
227
+ if (!fs.existsSync(contentDirPhysical)) return [];
228
+ const files = [];
229
+ function walk(dir) {
230
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
231
+ const entryPath = path.join(dir, entry.name);
232
+ if (entry.isDirectory()) {
233
+ walk(entryPath);
234
+ continue;
235
+ }
236
+ if (!/\.mdx?$/.test(entry.name)) continue;
237
+ const content = fs.readFileSync(entryPath, "utf8");
238
+ const meta = parseFrontmatter(content);
239
+ if (meta.draft || meta.hidden || meta.type === "link") continue;
240
+ const routePath = filePathToRoutePath(entryPath, contentDirPhysical);
241
+ files.push({
242
+ sourcePath: entryPath,
243
+ routePath,
244
+ distRelative: routeToDistRelative(routePath),
245
+ title: titleFromSource(content, entryPath, meta),
246
+ description: meta.description,
247
+ order: meta.order,
248
+ content: stripFrontmatter(content),
249
+ ext: ".md",
250
+ tags: meta.tags ?? []
251
+ });
252
+ }
253
+ }
254
+ walk(contentDirPhysical);
255
+ return files.sort((a, b) => {
256
+ if (a.order !== void 0 || b.order !== void 0) return (a.order ?? 9999) - (b.order ?? 9999);
257
+ return a.routePath.localeCompare(b.routePath);
258
+ });
259
+ }
260
+ function renderLlmsOutline(files, options) {
261
+ const lines = [
262
+ `# ${options.siteName}`,
263
+ "",
264
+ `> ${options.summary}`,
265
+ "",
266
+ `## ${options.section}`,
267
+ ""
268
+ ];
269
+ for (const file of files) {
270
+ const description = file.description ? `: ${file.description}` : "";
271
+ lines.push(`- [${file.title}](/${file.distRelative})${description}`);
272
+ }
273
+ lines.push("", "## Full text", "", "- [llms-full.txt](/llms-full.txt): Complete documentation dump");
274
+ return `${lines.join("\n")}\n`;
275
+ }
276
+ function renderLlmsFull(files) {
277
+ return files.map((file) => {
278
+ return [
279
+ `# ${file.title}`,
280
+ "",
281
+ `Route: ${file.routePath}`,
282
+ `Source: /${file.distRelative}`,
283
+ "",
284
+ file.content
285
+ ].join("\n");
286
+ }).join("\n\n---\n\n").concat("\n");
287
+ }
288
+ function readSiteName(root) {
289
+ try {
290
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
291
+ if (pkg.name) return titleize(pkg.name.replace(/^@[^/]+\//, "").replace(/-/g, " "));
292
+ } catch {}
293
+ return "Documentation";
294
+ }
295
+ function generateLlmsArtifacts(options) {
296
+ if (options.llms === false) return;
297
+ const root = options.root ?? process.cwd();
298
+ const files = collectContentFiles(normalizeContentDirPhysical(root, options.contentDir));
299
+ if (files.length === 0) return;
300
+ const llmsOptions = typeof options.llms === "object" ? options.llms : {};
301
+ const resolved = {
302
+ siteName: llmsOptions.siteName ?? readSiteName(root),
303
+ summary: llmsOptions.summary ?? "Documentation site generated by imprensa.",
304
+ section: llmsOptions.section ?? "Docs"
305
+ };
306
+ fs.mkdirSync(options.outDir, { recursive: true });
307
+ for (const file of files) {
308
+ const distPath = path.join(options.outDir, file.distRelative);
309
+ fs.mkdirSync(path.dirname(distPath), { recursive: true });
310
+ fs.copyFileSync(file.sourcePath, distPath);
311
+ }
312
+ fs.writeFileSync(path.join(options.outDir, "llms.txt"), renderLlmsOutline(files, resolved));
313
+ fs.writeFileSync(path.join(options.outDir, "llms-full.txt"), renderLlmsFull(files));
314
+ fs.writeFileSync(path.join(options.outDir, "llms.json"), `${JSON.stringify({
315
+ siteName: resolved.siteName,
316
+ summary: resolved.summary,
317
+ section: resolved.section,
318
+ pages: files.map(({ content: _content, sourcePath: _sourcePath, ...file }) => file)
319
+ }, null, 2)}\n`);
320
+ }
321
+ //#endregion
322
+ //#region src/docs/mdx-config.ts
323
+ const MDX_CONFIG_MARKER = `declare const __IMPRENSA_CONTENT_DIR__: string;
324
+ declare const __IMPRENSA_REPO__: string;
325
+ declare const __IMPRENSA_REPO_BRANCH__: string;
326
+ declare const __IMPRENSA_REPO_PATH__: string;
327
+ declare const __IMPRENSA_RAW_SOURCES__: Record<string, string>;
328
+ declare const __IMPRENSA_HEAD_DEFAULTS__: Head | null;
329
+
330
+ export const contentDir = __IMPRENSA_CONTENT_DIR__;
331
+ export const imprensaRepo = __IMPRENSA_REPO__;
332
+ export const imprensaRepoBranch = __IMPRENSA_REPO_BRANCH__;
333
+ export const imprensaRepoPath = __IMPRENSA_REPO_PATH__;
334
+ export const mdxRawSources: Record<string, string> = __IMPRENSA_RAW_SOURCES__;
335
+ export const headDefaults: Head | null = __IMPRENSA_HEAD_DEFAULTS__;`;
336
+ //#endregion
337
+ //#region src/docs/remark.ts
338
+ function isCode(node) {
339
+ return node.type === "code";
340
+ }
341
+ function remarkPreview() {
342
+ return (tree) => {
343
+ let hasPreview = false;
344
+ tree.children = tree.children.map((node) => {
345
+ if (!isCode(node) || !(node.meta ?? "").includes("preview")) return node;
346
+ hasPreview = true;
347
+ return {
348
+ type: "mdxJsxFlowElement",
349
+ name: "Preview",
350
+ attributes: [{
351
+ type: "mdxJsxAttribute",
352
+ name: "code64",
353
+ value: {
354
+ type: "mdxJsxAttributeValueExpression",
355
+ value: JSON.stringify(Buffer.from(node.value).toString("base64")),
356
+ data: { estree: {
357
+ type: "Program",
358
+ body: [{
359
+ type: "ExpressionStatement",
360
+ expression: {
361
+ type: "Literal",
362
+ value: Buffer.from(node.value).toString("base64")
363
+ }
364
+ }],
365
+ sourceType: "module"
366
+ } }
367
+ }
368
+ }],
369
+ children: []
370
+ };
371
+ });
372
+ if (!hasPreview) return;
373
+ tree.children.unshift({
374
+ type: "mdxjsEsm",
375
+ value: "import { Preview } from 'imprensa/components';",
376
+ data: { estree: {
377
+ type: "Program",
378
+ body: [{
379
+ type: "ImportDeclaration",
380
+ specifiers: [{
381
+ type: "ImportSpecifier",
382
+ imported: {
383
+ type: "Identifier",
384
+ name: "Preview"
385
+ },
386
+ local: {
387
+ type: "Identifier",
388
+ name: "Preview"
389
+ }
390
+ }],
391
+ source: {
392
+ type: "Literal",
393
+ value: "imprensa/components"
394
+ }
395
+ }],
396
+ sourceType: "module"
397
+ } }
398
+ });
399
+ };
400
+ }
401
+ //#endregion
402
+ //#region src/docs/plugin/landing-shiki.ts
403
+ const LANDING_SNIPPETS_FILE = "src/lib/landing-snippets.ts";
404
+ async function loadAppLandingSnippets(root) {
405
+ const file = path.join(root, LANDING_SNIPPETS_FILE);
406
+ if (!existsSync(file)) return null;
407
+ return (await import(pathToFileURL(file).href)).landingSnippets ?? null;
408
+ }
409
+ async function buildLandingShikiModule(root, shiki) {
410
+ if (shiki === false) return "export {};";
411
+ const landingSnippets = await loadAppLandingSnippets(root);
412
+ if (!landingSnippets) return "export {};";
413
+ const lines = [];
414
+ for (const [key, { code, lang }] of Object.entries(landingSnippets)) {
415
+ const html = await codeToSnippetHtml(code, lang, shiki);
416
+ lines.push(`export const ${key}Html = ${JSON.stringify(html)};`);
417
+ }
418
+ return lines.join("\n") || "export {};";
419
+ }
420
+ //#endregion
421
+ //#region src/docs/plugin/virtual-runtime.ts
422
+ /** Re-export surface for the `imprensa` virtual runtime entry (dev ergonomics). */
423
+ const IMPRENSA_VIRTUAL_RUNTIME = `
424
+ export {
425
+ THEME_STORAGE_KEY,
426
+ applyInitialTheme,
427
+ applyThemeToHtml,
428
+ createImprensa,
429
+ getStoredTheme,
430
+ mountOrHydrate,
431
+ setStoredTheme,
432
+ } from "imprensa/runtime";
433
+ export {
434
+ LogoButton,
435
+ SearchOverlay,
436
+ ThemeToggle,
437
+ } from "imprensa/components";
438
+ export const shiki = import("imprensa/shiki").then((m) => m.shiki);
439
+ export const shikiThemes = __SHIKI_THEMES__;
440
+ export {
441
+ DocArticle,
442
+ DocPager,
443
+ DocToolbar,
444
+ getAdjacentDocs,
445
+ } from "imprensa/doc";
446
+ export {
447
+ articleClass,
448
+ contentTree,
449
+ getClientPrerenderedMdxHtml,
450
+ getDocLinks,
451
+ getMdxContent,
452
+ getMdxHead,
453
+ getMdxSourceForRoute,
454
+ getPrerenderedMdxHtml,
455
+ headDefaults,
456
+ mdxRoutes,
457
+ renderMdx,
458
+ renderMdxContent,
459
+ searchDocuments,
460
+ setPrerenderedMdxHtml,
461
+ } from "imprensa/mdx";
462
+ `;
463
+ //#endregion
464
+ //#region src/docs/plugin/create-plugins.ts
465
+ /** Resolved from published `dist/index.mjs` → `src/...`. */
466
+ const MDX_SOURCE = fileURLToPath(new URL("../src/docs/mdx.ts", import.meta.url));
467
+ const MDX_RUNTIME_CONFIG = fileURLToPath(new URL("../src/docs/mdx/runtime-config.ts", import.meta.url));
468
+ const COMPONENTS_INDEX = fileURLToPath(new URL("../src/components/index.tsx", import.meta.url));
469
+ const DOC_ENTRY = fileURLToPath(new URL("../src/components/doc.tsx", import.meta.url));
470
+ const CONFIG_STUB = fileURLToPath(new URL("../src/docs/config.ts", import.meta.url));
471
+ const ICONS_ENTRY = fileURLToPath(new URL("../src/components/icons.tsx", import.meta.url));
472
+ const IMPRENSA_PRERENDER_ENTRY = path.resolve(fileURLToPath(new URL("./core/prerender-core.mjs", import.meta.url)));
473
+ const IMPRENSA_CLIENT_RUNTIME = path.resolve(fileURLToPath(new URL("./core/client-runtime.mjs", import.meta.url)));
474
+ function isMdxConfigTarget(id) {
475
+ return id === MDX_RUNTIME_CONFIG || id.endsWith("/imprensa/src/docs/mdx/runtime-config.ts") || id === MDX_SOURCE || id.endsWith("/imprensa/src/docs/mdx.ts");
476
+ }
477
+ function isAppPageFile(file, root) {
478
+ const relative = path.relative(path.join(root, "src/pages"), file).replace(/\\/g, "/");
479
+ return !relative.startsWith("..") && !path.isAbsolute(relative);
480
+ }
481
+ function createImprensaVitePlugins(options = {}) {
482
+ const { shiki, mdx: mdxOptions = {}, pages: pagesOptions = {}, contentDir = "src/pages/(content)", detectDeadLink = true, llms = true, repo = "", repoBranch = "main", repoPath = "", hostname, head: headDefaults, socials = [], preview = {} } = options;
483
+ const { rehypePlugins, remarkPlugins, ...restMdxOptions } = mdxOptions;
484
+ const resolvedRemarkPlugins = remarkPlugins ?? [];
485
+ const resolvedRehypePlugins = rehypePlugins ?? [];
486
+ const coreRehypePlugins = [
487
+ rehypeSlug,
488
+ [rehypeAutolinkHeadings, {
489
+ behavior: "wrap",
490
+ properties: { className: ["heading-anchor"] }
491
+ }],
492
+ ...detectDeadLink ? [rehypeDeadLinks] : []
493
+ ];
494
+ const highlighterOptions = getShikiHighlighterOptions(shiki);
495
+ const shikiThemes = shiki === false || !shiki?.themes ? {
496
+ light: "night-owl-light",
497
+ dark: "houston"
498
+ } : shiki.themes;
499
+ let isBuild = false;
500
+ const ilhaPagesOptions = {
501
+ interceptLinks: false,
502
+ ...pagesOptions,
503
+ mode: pagesOptions.mode ?? "static"
504
+ };
505
+ const plugins = [
506
+ mdx({
507
+ jsxImportSource: "ilha",
508
+ ...restMdxOptions,
509
+ remarkPlugins: [remarkPreview, ...resolvedRemarkPlugins],
510
+ rehypePlugins: [
511
+ ...shikiPlugin(shiki),
512
+ ...coreRehypePlugins,
513
+ ...resolvedRehypePlugins
514
+ ]
515
+ }),
516
+ {
517
+ name: "imprensa:ilha-pages",
518
+ enforce: "pre",
519
+ config(_config, { command }) {
520
+ ilhaPagesOptions.mode = pagesOptions.mode ?? (command === "serve" ? "spa" : "static");
521
+ }
522
+ },
523
+ pages(ilhaPagesOptions)
524
+ ];
525
+ plugins.push(tailwindcss());
526
+ plugins.push(vitePrerenderPlugin({
527
+ renderTarget: "#app",
528
+ prerenderScript: path.join(process.cwd(), "src/main.ts")
529
+ }));
530
+ plugins.push(sitemap({
531
+ hostname,
532
+ dynamicRoutes: collectMdxRoutes(contentDir)
533
+ }));
534
+ plugins.push({
535
+ name: "imprensa:html",
536
+ transformIndexHtml: {
537
+ order: "pre",
538
+ handler(html) {
539
+ if (!html.includes("imprensa-sidebar-layout-boot")) html = html.replace("</head>", ` ${SIDEBAR_LAYOUT_BOOT_SCRIPT}\n </head>`);
540
+ return html;
541
+ }
542
+ }
543
+ });
544
+ plugins.push({
545
+ name: "imprensa:html-post",
546
+ apply: "build",
547
+ transformIndexHtml: {
548
+ order: "post",
549
+ handler(html) {
550
+ return html.replace(/<link rel="stylesheet" crossorigin href="(\/assets\/[^"]+\.css)">/g, "<link rel=\"preload\" as=\"style\" href=\"$1\" crossorigin />\n $&");
551
+ }
552
+ }
553
+ });
554
+ plugins.push({
555
+ name: "imprensa:config",
556
+ enforce: "pre",
557
+ resolveId(id) {
558
+ if (id === "imprensa") return "\0imprensa:runtime";
559
+ if (id === "imprensa/shiki") return "\0imprensa:shiki";
560
+ if (id === "imprensa/config") return "\0imprensa:config";
561
+ if (id === CONFIG_STUB || id.endsWith("/imprensa/src/docs/config.ts")) return "\0imprensa:config";
562
+ if (id === "imprensa/mdx") return MDX_SOURCE;
563
+ if (id === "imprensa/components") return COMPONENTS_INDEX;
564
+ if (id === "imprensa/doc") return DOC_ENTRY;
565
+ if (id === "imprensa/icons") return ICONS_ENTRY;
566
+ if (id === "imprensa/landing-shiki") return "\0imprensa:landing-shiki";
567
+ },
568
+ async load(id) {
569
+ if (id === "\0imprensa:landing-shiki") return buildLandingShikiModule(process.cwd(), shiki);
570
+ if (id === "\0imprensa:config") return `export const socials = ${JSON.stringify(socials)};
571
+ export const preview = ${JSON.stringify(preview)};
572
+ export const shiki = ${JSON.stringify(shiki === false ? false : shiki ?? {})};
573
+ export const hostname = ${JSON.stringify(hostname ?? "")};
574
+ export const shikiThemes = ${JSON.stringify(shikiThemes)};`;
575
+ if (id === "\0imprensa:shiki") {
576
+ if (!highlighterOptions.clientShiki || highlighterOptions.langs.length === 0) return `export const shiki = {
577
+ loadLanguage: async () => {},
578
+ codeToHtml: () => "",
579
+ };`;
580
+ return shikiFineGrainedRuntime({
581
+ themes: highlighterOptions.themes,
582
+ langs: highlighterOptions.langs
583
+ });
584
+ }
585
+ if (id !== "\0imprensa:runtime") return;
586
+ return IMPRENSA_VIRTUAL_RUNTIME.replace("__SHIKI_THEMES__", JSON.stringify(shikiThemes));
587
+ },
588
+ transform(code, id) {
589
+ if (/\.mdx?$/.test(id) && code.startsWith("---")) {
590
+ const end = code.indexOf("\n---", 3);
591
+ if (end !== -1) return {
592
+ code: code.slice(end + 4),
593
+ map: null
594
+ };
595
+ }
596
+ if (!isMdxConfigTarget(id)) return;
597
+ return code.replace(MDX_CONFIG_MARKER, `export const contentDir = ${JSON.stringify(normalizeContentDir(contentDir))};
598
+ export const imprensaRepo = ${JSON.stringify(repo)};
599
+ export const imprensaRepoBranch = ${JSON.stringify(repoBranch)};
600
+ export const imprensaRepoPath = ${JSON.stringify(repoPath)};
601
+ export const mdxRawSources = ${JSON.stringify(collectRawMdxSources(process.cwd(), contentDir))} as Record<string, string>;
602
+ export const headDefaults = ${JSON.stringify(headDefaults ?? null)} as import("unhead/types").ResolvableHead | null;`);
603
+ },
604
+ handleHotUpdate(ctx) {
605
+ if (!isAppPageFile(ctx.file, ctx.server.config.root)) return;
606
+ for (const module of ctx.server.moduleGraph.getModulesByFile(MDX_RUNTIME_CONFIG) ?? []) ctx.server.moduleGraph.invalidateModule(module);
607
+ for (const module of ctx.server.moduleGraph.getModulesByFile(MDX_SOURCE) ?? []) ctx.server.moduleGraph.invalidateModule(module);
608
+ ctx.server.ws.send({
609
+ type: "full-reload",
610
+ path: "*"
611
+ });
612
+ return [];
613
+ },
614
+ config() {
615
+ const root = process.cwd();
616
+ let sonner = "sonner";
617
+ try {
618
+ sonner = createRequire(path.join(root, "package.json")).resolve("sonner");
619
+ } catch {}
620
+ return {
621
+ server: { watch: { usePolling: true } },
622
+ build: { rolldownOptions: { output: { codeSplitting: { groups: [{
623
+ name: "imprensa-search",
624
+ test: /minisearch/
625
+ }] } } } },
626
+ resolve: {
627
+ alias: {
628
+ $lib: path.resolve(root, "src", "lib"),
629
+ sonner,
630
+ "imprensa/prerender": IMPRENSA_PRERENDER_ENTRY,
631
+ "imprensa/runtime": IMPRENSA_CLIENT_RUNTIME
632
+ },
633
+ dedupe: [
634
+ "@areia/slots",
635
+ "@ilha/router",
636
+ "areia",
637
+ "ilha",
638
+ "lucide",
639
+ "minisearch",
640
+ "quando",
641
+ "sonner"
642
+ ]
643
+ }
644
+ };
645
+ },
646
+ configResolved(config) {
647
+ isBuild = config.command === "build";
648
+ },
649
+ closeBundle() {
650
+ if (!isBuild) return;
651
+ generateLlmsArtifacts({
652
+ root: process.cwd(),
653
+ outDir: path.resolve(process.cwd(), "dist"),
654
+ contentDir,
655
+ llms
656
+ });
657
+ }
658
+ });
659
+ return plugins;
660
+ }
661
+ //#endregion
662
+ //#region src/docs/vite-plugin.ts
663
+ /** Vite meta-plugin preset for Imprensa documentation sites. */
664
+ function imprensa(options = {}) {
665
+ return createImprensaVitePlugins(options);
666
+ }
667
+ //#endregion
668
+ export { THEME_STORAGE_KEY, applyInitialTheme, applyThemeToHtml, createImprensa, createPrerender, getStoredTheme, imprensa, mountOrHydrate, setStoredTheme, shiki, shikiThemes };