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.
- package/CHANGELOG.md +15 -0
- package/README.md +51 -0
- package/default.css +239 -0
- package/dist/client-runtime-D7MhMWCo.d.mts +45 -0
- package/dist/components/doc.d.mts +2 -0
- package/dist/components/doc.mjs +2 -0
- package/dist/components/icons.d.mts +23 -0
- package/dist/components/icons.mjs +40 -0
- package/dist/components/index.d.mts +33 -0
- package/dist/components/index.mjs +253 -0
- package/dist/core/client-runtime.d.mts +2 -0
- package/dist/core/client-runtime.mjs +121 -0
- package/dist/core/prerender-core.d.mts +2 -0
- package/dist/core/prerender-core.mjs +2 -0
- package/dist/core/runtime.d.mts +3 -0
- package/dist/core/runtime.mjs +3 -0
- package/dist/core/shiki-build.d.mts +9 -0
- package/dist/core/shiki-build.mjs +34 -0
- package/dist/doc-pager-D-YhwEQN.d.mts +27 -0
- package/dist/doc-toolbar-DUQS2gnK.mjs +460 -0
- package/dist/docs/config.d.mts +13 -0
- package/dist/docs/config.mjs +10 -0
- package/dist/docs/landing-shiki.d.mts +7 -0
- package/dist/docs/landing-shiki.mjs +7 -0
- package/dist/docs/mdx.d.mts +79 -0
- package/dist/docs/mdx.mjs +293 -0
- package/dist/docs/rehype.d.mts +25 -0
- package/dist/docs/rehype.mjs +2 -0
- package/dist/frontmatter-DVneGjCO.mjs +16 -0
- package/dist/global-search-Dfv8DYN3.mjs +310 -0
- package/dist/index.d.mts +41 -0
- package/dist/index.mjs +668 -0
- package/dist/prerender-core-D4Li--RS.mjs +172 -0
- package/dist/prerender-core-DBi9ntWW.d.mts +48 -0
- package/dist/rehype-BWpGaBql.mjs +182 -0
- package/dist/search-store-DDGHRAKl.mjs +64 -0
- package/dist/shiki-gFey7C-z.d.mts +3289 -0
- package/dist/sidebar-layout-DsEhSkJS.mjs +43 -0
- package/dist/socials-BIszPk-A.d.mts +8 -0
- package/docs/architecture.md +26 -0
- package/docs/integration-notes.md +6 -0
- package/index.d.ts +49 -0
- package/package.json +128 -0
- 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 };
|