imprensa 0.1.0 → 0.1.2
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/dist/index.mjs +31 -12
- package/package.json +4 -1
- package/src/docs/frontmatter.ts +31 -0
- package/src/docs/mdx/document-text.ts +96 -0
- package/src/docs/mdx/index.ts +26 -0
- package/src/docs/mdx/render.ts +91 -0
- package/src/docs/mdx/routes.ts +121 -0
- package/src/docs/mdx/runtime-config.ts +15 -0
- package/src/docs/mdx/source-links.ts +56 -0
- package/src/docs/mdx/types.ts +77 -0
- package/src/docs/mdx.ts +20 -0
package/dist/index.mjs
CHANGED
|
@@ -462,17 +462,32 @@ export {
|
|
|
462
462
|
`;
|
|
463
463
|
//#endregion
|
|
464
464
|
//#region src/docs/plugin/create-plugins.ts
|
|
465
|
-
/**
|
|
465
|
+
/**
|
|
466
|
+
* MDX runtime config is transformed by this Vite plugin, so it needs the packaged source.
|
|
467
|
+
* UI subpaths can use published dist files; consumers should not need the full source tree.
|
|
468
|
+
*/
|
|
466
469
|
const MDX_SOURCE = fileURLToPath(new URL("../src/docs/mdx.ts", import.meta.url));
|
|
467
470
|
const MDX_RUNTIME_CONFIG = fileURLToPath(new URL("../src/docs/mdx/runtime-config.ts", import.meta.url));
|
|
468
|
-
const COMPONENTS_INDEX = fileURLToPath(new URL("
|
|
469
|
-
const DOC_ENTRY = fileURLToPath(new URL("
|
|
470
|
-
const CONFIG_STUB = fileURLToPath(new URL("
|
|
471
|
-
const ICONS_ENTRY = fileURLToPath(new URL("
|
|
471
|
+
const COMPONENTS_INDEX = fileURLToPath(new URL("./components/index.mjs", import.meta.url));
|
|
472
|
+
const DOC_ENTRY = fileURLToPath(new URL("./components/doc.mjs", import.meta.url));
|
|
473
|
+
const CONFIG_STUB = fileURLToPath(new URL("./docs/config.mjs", import.meta.url));
|
|
474
|
+
const ICONS_ENTRY = fileURLToPath(new URL("./components/icons.mjs", import.meta.url));
|
|
472
475
|
const IMPRENSA_PRERENDER_ENTRY = path.resolve(fileURLToPath(new URL("./core/prerender-core.mjs", import.meta.url)));
|
|
473
476
|
const IMPRENSA_CLIENT_RUNTIME = path.resolve(fileURLToPath(new URL("./core/client-runtime.mjs", import.meta.url)));
|
|
477
|
+
/** Published bundle still references __IMPRENSA_* until the Vite plugin rewrites them. */
|
|
478
|
+
const MDX_DIST_BUNDLE = path.join(fileURLToPath(new URL("../../..", import.meta.url)), "dist/docs/mdx.mjs");
|
|
474
479
|
function isMdxConfigTarget(id) {
|
|
475
|
-
|
|
480
|
+
const normalized = id.split("?")[0] ?? id;
|
|
481
|
+
return normalized === MDX_RUNTIME_CONFIG || normalized.endsWith("/imprensa/src/docs/mdx/runtime-config.ts") || normalized === MDX_SOURCE || normalized.endsWith("/imprensa/src/docs/mdx.ts") || normalized === MDX_DIST_BUNDLE || normalized.endsWith("/imprensa/dist/docs/mdx.mjs");
|
|
482
|
+
}
|
|
483
|
+
function injectedMdxRuntimeConfig(options) {
|
|
484
|
+
const { contentDir, repo, repoBranch, repoPath, headDefaults } = options;
|
|
485
|
+
return `export const contentDir = ${JSON.stringify(normalizeContentDir(contentDir))};
|
|
486
|
+
export const imprensaRepo = ${JSON.stringify(repo)};
|
|
487
|
+
export const imprensaRepoBranch = ${JSON.stringify(repoBranch)};
|
|
488
|
+
export const imprensaRepoPath = ${JSON.stringify(repoPath)};
|
|
489
|
+
export const mdxRawSources = ${JSON.stringify(collectRawMdxSources(process.cwd(), contentDir))};
|
|
490
|
+
export const headDefaults = ${JSON.stringify(headDefaults ?? null)};`;
|
|
476
491
|
}
|
|
477
492
|
function isAppPageFile(file, root) {
|
|
478
493
|
const relative = path.relative(path.join(root, "src/pages"), file).replace(/\\/g, "/");
|
|
@@ -594,12 +609,15 @@ export const shikiThemes = ${JSON.stringify(shikiThemes)};`;
|
|
|
594
609
|
};
|
|
595
610
|
}
|
|
596
611
|
if (!isMdxConfigTarget(id)) return;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
612
|
+
const injected = injectedMdxRuntimeConfig({
|
|
613
|
+
contentDir,
|
|
614
|
+
repo,
|
|
615
|
+
repoBranch,
|
|
616
|
+
repoPath,
|
|
617
|
+
headDefaults
|
|
618
|
+
});
|
|
619
|
+
if (code.includes(MDX_CONFIG_MARKER)) return code.replace(MDX_CONFIG_MARKER, injected);
|
|
620
|
+
if (/__IMPRENSA_CONTENT_DIR__/.test(code)) return code.replace(/\/\/#region src\/docs\/mdx\/runtime-config\.ts[\s\S]*?\/\/#endregion/, `//#region src/docs/mdx/runtime-config.ts\n${injected}\n//#endregion`);
|
|
603
621
|
},
|
|
604
622
|
handleHotUpdate(ctx) {
|
|
605
623
|
if (!isAppPageFile(ctx.file, ctx.server.config.root)) return;
|
|
@@ -618,6 +636,7 @@ export const headDefaults = ${JSON.stringify(headDefaults ?? null)} as import("u
|
|
|
618
636
|
sonner = createRequire(path.join(root, "package.json")).resolve("sonner");
|
|
619
637
|
} catch {}
|
|
620
638
|
return {
|
|
639
|
+
optimizeDeps: { exclude: ["imprensa/mdx"] },
|
|
621
640
|
server: { watch: { usePolling: true } },
|
|
622
641
|
build: { rolldownOptions: { output: { codeSplitting: { groups: [{
|
|
623
642
|
name: "imprensa-search",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imprensa",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Vite plugin and runtime for Ilha + Areia documentation sites (MDX, prerender, search)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"areia",
|
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
30
|
"dist",
|
|
31
|
+
"src/docs/frontmatter.ts",
|
|
32
|
+
"src/docs/mdx.ts",
|
|
33
|
+
"src/docs/mdx/",
|
|
31
34
|
"default.css",
|
|
32
35
|
"index.d.ts",
|
|
33
36
|
"README.md",
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Parsed YAML scalar from minimal frontmatter parser. */
|
|
2
|
+
export type FrontmatterScalar = string | number | boolean | string[];
|
|
3
|
+
|
|
4
|
+
export function parseScalar(value: string): FrontmatterScalar {
|
|
5
|
+
const trimmed = value.trim();
|
|
6
|
+
if (trimmed === "true") return true;
|
|
7
|
+
if (trimmed === "false") return false;
|
|
8
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) return Number(trimmed);
|
|
9
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
10
|
+
return trimmed
|
|
11
|
+
.slice(1, -1)
|
|
12
|
+
.split(",")
|
|
13
|
+
.map((item) => item.trim().replace(/^['"]|['"]$/g, ""))
|
|
14
|
+
.filter(Boolean);
|
|
15
|
+
}
|
|
16
|
+
return trimmed.replace(/^['"]|['"]$/g, "");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function toStringArray(
|
|
20
|
+
value: FrontmatterScalar | FrontmatterScalar[] | undefined,
|
|
21
|
+
): string[] {
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
return value.flatMap((item) => (typeof item === "string" ? [item] : []));
|
|
24
|
+
}
|
|
25
|
+
if (typeof value === "string")
|
|
26
|
+
return value
|
|
27
|
+
.split(",")
|
|
28
|
+
.map((item) => item.trim())
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { parseScalar, toStringArray, type FrontmatterScalar } from "../frontmatter";
|
|
2
|
+
import type { ContentMeta } from "./types";
|
|
3
|
+
|
|
4
|
+
export function titleize(value: string) {
|
|
5
|
+
return value.replace(/[-_]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function stripFrontmatter(source: string) {
|
|
9
|
+
if (!source.startsWith("---")) return source;
|
|
10
|
+
const end = source.indexOf("\n---", 3);
|
|
11
|
+
return end === -1 ? source : source.slice(end + 4).replace(/^\s+/, "");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function titleFromDocument(filePath: string, rawSource: string) {
|
|
15
|
+
const documentSource = stripFrontmatter(rawSource);
|
|
16
|
+
const heading = documentSource.match(/^#\s+(.+)$/m);
|
|
17
|
+
const title = heading?.[1]?.trim();
|
|
18
|
+
if (title) return title;
|
|
19
|
+
|
|
20
|
+
const name = filePath
|
|
21
|
+
.replace(/\.mdx?$/, "")
|
|
22
|
+
.split("/")
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
.at(-1);
|
|
25
|
+
|
|
26
|
+
return titleize(name === "index" ? "overview" : (name ?? "Untitled"));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function textFromRawDocument(source: string) {
|
|
30
|
+
return stripFrontmatter(source)
|
|
31
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
32
|
+
.replace(/`([^`]+)`/g, "$1")
|
|
33
|
+
.replace(/<[^>]+>/g, " ")
|
|
34
|
+
.replace(/!\[[^\]]*\]\([^)]*\)/g, " ")
|
|
35
|
+
.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
|
|
36
|
+
.replace(/^#{1,6}\s+/gm, "")
|
|
37
|
+
.replace(/[>*_~#-]/g, " ")
|
|
38
|
+
.replace(/\s+/g, " ")
|
|
39
|
+
.trim();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseFrontmatter(source: string): Record<string, FrontmatterScalar | FrontmatterScalar[]> {
|
|
43
|
+
if (!source.startsWith("---")) return {};
|
|
44
|
+
const end = source.indexOf("\n---", 3);
|
|
45
|
+
if (end === -1) return {};
|
|
46
|
+
const yaml = source.slice(4, end);
|
|
47
|
+
const entries: Record<string, FrontmatterScalar | FrontmatterScalar[]> = {};
|
|
48
|
+
let listKey: string | undefined;
|
|
49
|
+
|
|
50
|
+
for (const line of yaml.split("\n")) {
|
|
51
|
+
const listItem = line.match(/^\s*-\s*(.+)$/);
|
|
52
|
+
if (listItem && listKey) {
|
|
53
|
+
const existing = entries[listKey];
|
|
54
|
+
const list: FrontmatterScalar[] = Array.isArray(existing) ? [...existing] : [];
|
|
55
|
+
list.push(parseScalar(listItem[1]));
|
|
56
|
+
entries[listKey] = list;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const m = line.match(/^([\w-]+):\s*(.*)$/);
|
|
61
|
+
if (!m) continue;
|
|
62
|
+
listKey = undefined;
|
|
63
|
+
if (!m[2].trim()) {
|
|
64
|
+
entries[m[1]] = [];
|
|
65
|
+
listKey = m[1];
|
|
66
|
+
} else {
|
|
67
|
+
entries[m[1]] = parseScalar(m[2]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return entries;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function metaFromDocument(filePath: string, rawSource: string): ContentMeta {
|
|
75
|
+
const fm = parseFrontmatter(rawSource);
|
|
76
|
+
const title = typeof fm.title === "string" ? fm.title : titleFromDocument(filePath, rawSource);
|
|
77
|
+
const order = typeof fm.order === "number" ? fm.order : undefined;
|
|
78
|
+
const priority = typeof fm.priority === "number" ? fm.priority : order !== undefined ? -order : 0;
|
|
79
|
+
|
|
80
|
+
const type = fm.type === "custom" || fm.type === "link" ? fm.type : "doc";
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
title,
|
|
84
|
+
description: typeof fm.description === "string" ? fm.description : undefined,
|
|
85
|
+
order,
|
|
86
|
+
priority,
|
|
87
|
+
type,
|
|
88
|
+
link: typeof fm.link === "string" ? fm.link : undefined,
|
|
89
|
+
external: fm.external === true,
|
|
90
|
+
section: typeof fm.section === "string" ? fm.section : undefined,
|
|
91
|
+
badge: typeof fm.badge === "string" ? fm.badge : undefined,
|
|
92
|
+
draft: fm.draft === true,
|
|
93
|
+
hidden: fm.hidden === true || fm.sidebar === false,
|
|
94
|
+
tags: toStringArray(fm.tags),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export { headDefaults } from "./runtime-config";
|
|
2
|
+
|
|
3
|
+
export type {
|
|
4
|
+
ContentMeta,
|
|
5
|
+
ContentPageType,
|
|
6
|
+
ContentTreeNode,
|
|
7
|
+
MdxContent,
|
|
8
|
+
MdxModule,
|
|
9
|
+
SearchDocument,
|
|
10
|
+
} from "./types";
|
|
11
|
+
export { articleClass } from "./types";
|
|
12
|
+
|
|
13
|
+
export { contentMeta, contentTree, mdxRoutes, searchDocuments } from "./routes";
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
getClientPrerenderedMdxHtml,
|
|
17
|
+
getMdxContent,
|
|
18
|
+
getMdxHead,
|
|
19
|
+
getPrerenderedMdxHtml,
|
|
20
|
+
loadMdxHtml,
|
|
21
|
+
renderMdx,
|
|
22
|
+
renderMdxContent,
|
|
23
|
+
setPrerenderedMdxHtml,
|
|
24
|
+
} from "./render";
|
|
25
|
+
|
|
26
|
+
export { getDocLinks, getMdxSourceForRoute } from "./source-links";
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { raw } from "ilha";
|
|
2
|
+
import type { ResolvableHead as Head } from "unhead/types";
|
|
3
|
+
import type { MdxContent } from "./types";
|
|
4
|
+
import { mdxPages } from "./routes";
|
|
5
|
+
|
|
6
|
+
let prerenderedMdxHtml: string | undefined;
|
|
7
|
+
|
|
8
|
+
function toHtml(content: MdxContent) {
|
|
9
|
+
return typeof content === "string" ? content : content.value;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function decodeBase64(value: string) {
|
|
13
|
+
const binary = atob(value);
|
|
14
|
+
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
15
|
+
return new TextDecoder().decode(bytes);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type PrerenderDataPayload = {
|
|
19
|
+
mdxHtml?: string;
|
|
20
|
+
mdxHtmlBase64?: string;
|
|
21
|
+
mdxPath?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function setPrerenderedMdxHtml(html: string | undefined) {
|
|
25
|
+
prerenderedMdxHtml = html;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getPrerenderedMdxHtml() {
|
|
29
|
+
return prerenderedMdxHtml;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getClientPrerenderedMdxHtml(path: string) {
|
|
33
|
+
if (typeof document === "undefined") return undefined;
|
|
34
|
+
|
|
35
|
+
const data = document.getElementById("prerender-data")?.textContent;
|
|
36
|
+
if (!data) return undefined;
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(data) as PrerenderDataPayload;
|
|
40
|
+
const normalizedPath = path.replace(/\/$/, "") || "/";
|
|
41
|
+
if (parsed.mdxPath !== normalizedPath) return undefined;
|
|
42
|
+
if (typeof parsed.mdxHtmlBase64 === "string") return decodeBase64(parsed.mdxHtmlBase64);
|
|
43
|
+
return typeof parsed.mdxHtml === "string" ? parsed.mdxHtml : undefined;
|
|
44
|
+
} catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizePath(url: string) {
|
|
50
|
+
return new URL(url, "http://localhost").pathname.replace(/\/$/, "") || "/";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function loadMdxModule(url: string) {
|
|
54
|
+
const pathname = normalizePath(url);
|
|
55
|
+
const loader = mdxPages.get(pathname);
|
|
56
|
+
return loader ? { pathname, mod: await loader() } : undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function renderMdxContent(url: string) {
|
|
60
|
+
return (await loadMdxModule(url))?.mod.default({});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function getMdxHead(url: string): Promise<Head | undefined> {
|
|
64
|
+
return (await loadMdxModule(url))?.mod.head;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function escapeUnsafeHtmlScriptTags(html: string) {
|
|
68
|
+
return html.replace(/<script/gi, "<script").replace(/<\/script>/gi, "</script>");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function renderMdx(url: string) {
|
|
72
|
+
const loaded = await loadMdxModule(url);
|
|
73
|
+
if (!loaded) return undefined;
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
html: escapeUnsafeHtmlScriptTags(toHtml(loaded.mod.default({}))),
|
|
77
|
+
path: loaded.pathname,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function loadMdxHtml(path: string) {
|
|
82
|
+
const cached = getPrerenderedMdxHtml() ?? getClientPrerenderedMdxHtml(path);
|
|
83
|
+
if (cached) return raw(cached);
|
|
84
|
+
const content = await renderMdxContent(path);
|
|
85
|
+
return content ? raw(escapeUnsafeHtmlScriptTags(toHtml(content))) : null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function getMdxContent(path: string) {
|
|
89
|
+
const cached = getPrerenderedMdxHtml() ?? getClientPrerenderedMdxHtml(path);
|
|
90
|
+
return cached ? raw(cached) : undefined;
|
|
91
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { MdxModule } from "./types";
|
|
2
|
+
import { metaFromDocument, textFromRawDocument, titleize } from "./document-text";
|
|
3
|
+
import type { ContentMeta, ContentTreeNode, SearchDocument } from "./types";
|
|
4
|
+
import { contentDir, mdxRawSources } from "./runtime-config";
|
|
5
|
+
|
|
6
|
+
const allMdxModules = {
|
|
7
|
+
...import.meta.glob("/src/pages/**/*.md"),
|
|
8
|
+
...import.meta.glob("/src/pages/**/*.mdx"),
|
|
9
|
+
} as Record<string, () => Promise<MdxModule>>;
|
|
10
|
+
|
|
11
|
+
export const mdxModules = Object.fromEntries(
|
|
12
|
+
Object.entries(allMdxModules).filter(([filePath]) => filePath.startsWith(contentDir)),
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export function filePathToRoutePath(filePath: string) {
|
|
16
|
+
const routePath = filePath
|
|
17
|
+
.replace(/^\/src\/pages/, "")
|
|
18
|
+
.replace(/\.mdx?$/, "")
|
|
19
|
+
.replace(/\/index$/, "")
|
|
20
|
+
.split("/")
|
|
21
|
+
.filter((segment) => !/^\(.+\)$/.test(segment))
|
|
22
|
+
.join("/");
|
|
23
|
+
|
|
24
|
+
return routePath || "/";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function insertTreeNode(tree: ContentTreeNode[], routePath: string, meta: ContentMeta) {
|
|
28
|
+
const segments = routePath.split("/").filter(Boolean);
|
|
29
|
+
let level = tree;
|
|
30
|
+
|
|
31
|
+
segments.forEach((segment, index) => {
|
|
32
|
+
const path = `/${segments.slice(0, index + 1).join("/")}`;
|
|
33
|
+
const isLeaf = index === segments.length - 1;
|
|
34
|
+
let node = level.find((item) => item.path === path || item.title === titleize(segment));
|
|
35
|
+
|
|
36
|
+
if (!node) {
|
|
37
|
+
node = {
|
|
38
|
+
title: isLeaf ? meta.title : titleize(segment),
|
|
39
|
+
path: isLeaf ? path : undefined,
|
|
40
|
+
type: isLeaf ? meta.type : "doc",
|
|
41
|
+
link: isLeaf ? meta.link : undefined,
|
|
42
|
+
external: isLeaf ? meta.external : undefined,
|
|
43
|
+
priority: isLeaf ? meta.priority : 0,
|
|
44
|
+
order: isLeaf ? meta.order : undefined,
|
|
45
|
+
description: isLeaf ? meta.description : undefined,
|
|
46
|
+
badge: isLeaf ? meta.badge : undefined,
|
|
47
|
+
children: [],
|
|
48
|
+
};
|
|
49
|
+
level.push(node);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (isLeaf) {
|
|
53
|
+
node.title = meta.title;
|
|
54
|
+
node.path = path;
|
|
55
|
+
node.type = meta.type;
|
|
56
|
+
node.link = meta.link;
|
|
57
|
+
node.external = meta.external;
|
|
58
|
+
node.priority = meta.priority;
|
|
59
|
+
node.order = meta.order;
|
|
60
|
+
node.description = meta.description;
|
|
61
|
+
node.badge = meta.badge;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
level = node.children;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const sort = (nodes: ContentTreeNode[]) => {
|
|
68
|
+
nodes.sort((a, b) => {
|
|
69
|
+
if (a.order !== undefined || b.order !== undefined)
|
|
70
|
+
return (a.order ?? 9999) - (b.order ?? 9999);
|
|
71
|
+
return b.priority - a.priority || a.title.localeCompare(b.title);
|
|
72
|
+
});
|
|
73
|
+
nodes.forEach((n) => sort(n.children));
|
|
74
|
+
};
|
|
75
|
+
sort(tree);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const contentMeta = Object.fromEntries(
|
|
79
|
+
Object.entries(mdxModules).map(([filePath]) => {
|
|
80
|
+
const path = filePathToRoutePath(filePath);
|
|
81
|
+
return [path, metaFromDocument(filePath, mdxRawSources[path] ?? "")];
|
|
82
|
+
}),
|
|
83
|
+
) as Record<string, ContentMeta>;
|
|
84
|
+
|
|
85
|
+
export const mdxPages = new Map(
|
|
86
|
+
Object.entries(mdxModules)
|
|
87
|
+
.map(([filePath, loader]) => [filePathToRoutePath(filePath), loader] as const)
|
|
88
|
+
.filter(([path]) => contentMeta[path]?.type !== "link"),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
export const contentTree = Object.entries(mdxModules).reduce<ContentTreeNode[]>(
|
|
92
|
+
(tree, [filePath]) => {
|
|
93
|
+
const routePath = filePathToRoutePath(filePath);
|
|
94
|
+
const meta = contentMeta[routePath];
|
|
95
|
+
if (meta.draft || meta.hidden) return tree;
|
|
96
|
+
insertTreeNode(tree, routePath, meta);
|
|
97
|
+
return tree;
|
|
98
|
+
},
|
|
99
|
+
[],
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
export const searchDocuments = Object.entries(mdxModules)
|
|
103
|
+
.map<SearchDocument | undefined>(([filePath]) => {
|
|
104
|
+
const path = filePathToRoutePath(filePath);
|
|
105
|
+
const meta = contentMeta[path];
|
|
106
|
+
if (meta.draft || meta.type === "link") return undefined;
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
id: path,
|
|
110
|
+
title: meta.title,
|
|
111
|
+
path,
|
|
112
|
+
description: meta.description,
|
|
113
|
+
tags: meta.tags,
|
|
114
|
+
text: [meta.description, meta.tags.join(" "), textFromRawDocument(mdxRawSources[path] ?? "")]
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
.join(" "),
|
|
117
|
+
};
|
|
118
|
+
})
|
|
119
|
+
.filter((doc): doc is SearchDocument => Boolean(doc));
|
|
120
|
+
|
|
121
|
+
export const mdxRoutes = [...mdxPages.keys()];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ResolvableHead as Head } from "unhead/types";
|
|
2
|
+
|
|
3
|
+
declare const __IMPRENSA_CONTENT_DIR__: string;
|
|
4
|
+
declare const __IMPRENSA_REPO__: string;
|
|
5
|
+
declare const __IMPRENSA_REPO_BRANCH__: string;
|
|
6
|
+
declare const __IMPRENSA_REPO_PATH__: string;
|
|
7
|
+
declare const __IMPRENSA_RAW_SOURCES__: Record<string, string>;
|
|
8
|
+
declare const __IMPRENSA_HEAD_DEFAULTS__: Head | null;
|
|
9
|
+
|
|
10
|
+
export const contentDir = __IMPRENSA_CONTENT_DIR__;
|
|
11
|
+
export const imprensaRepo = __IMPRENSA_REPO__;
|
|
12
|
+
export const imprensaRepoBranch = __IMPRENSA_REPO_BRANCH__;
|
|
13
|
+
export const imprensaRepoPath = __IMPRENSA_REPO_PATH__;
|
|
14
|
+
export const mdxRawSources: Record<string, string> = __IMPRENSA_RAW_SOURCES__;
|
|
15
|
+
export const headDefaults: Head | null = __IMPRENSA_HEAD_DEFAULTS__;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { filePathToRoutePath, mdxModules } from "./routes";
|
|
2
|
+
import {
|
|
3
|
+
imprensaRepo,
|
|
4
|
+
imprensaRepoBranch,
|
|
5
|
+
imprensaRepoPath,
|
|
6
|
+
mdxRawSources,
|
|
7
|
+
} from "./runtime-config";
|
|
8
|
+
|
|
9
|
+
function routeToDistMarkdown(routePath: string) {
|
|
10
|
+
if (routePath === "/") return "/index.md";
|
|
11
|
+
return `${routePath}/index.md`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function normalizeRepoUrl(repo: string) {
|
|
15
|
+
const trimmed = repo.trim();
|
|
16
|
+
if (!trimmed) return "";
|
|
17
|
+
if (/^https?:\/\//.test(trimmed)) return trimmed.replace(/\/$/, "");
|
|
18
|
+
return `https://${trimmed.replace(/\/$/, "")}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getMdxSourceForRoute(routePath: string) {
|
|
22
|
+
const normalized = routePath.replace(/\/$/, "") || "/";
|
|
23
|
+
|
|
24
|
+
for (const filePath of Object.keys(mdxModules)) {
|
|
25
|
+
if (filePathToRoutePath(filePath) !== normalized) continue;
|
|
26
|
+
|
|
27
|
+
const ext = filePath.match(/\.mdx?$/)?.[0] ?? ".mdx";
|
|
28
|
+
const sourceFile = filePath.replace(/^\//, "");
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
routePath: normalized,
|
|
32
|
+
sourceFile,
|
|
33
|
+
markdownUrl: routeToDistMarkdown(normalized),
|
|
34
|
+
ext,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getDocLinks(routePath: string) {
|
|
42
|
+
const source = getMdxSourceForRoute(routePath);
|
|
43
|
+
if (!source) return undefined;
|
|
44
|
+
|
|
45
|
+
const repo = normalizeRepoUrl(imprensaRepo);
|
|
46
|
+
const branch = imprensaRepoBranch || "main";
|
|
47
|
+
const repoPath = (imprensaRepoPath ?? "").replace(/^\/+|\/+$/g, "");
|
|
48
|
+
const githubFile = repoPath ? `${repoPath}/${source.sourceFile}` : source.sourceFile;
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
routePath: source.routePath,
|
|
52
|
+
markdownUrl: source.markdownUrl,
|
|
53
|
+
githubUrl: repo ? `${repo}/blob/${branch}/${githubFile}` : undefined,
|
|
54
|
+
rawMarkdown: mdxRawSources[source.routePath],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { RawHtml } from "ilha";
|
|
2
|
+
import type { ResolvableHead as Head } from "unhead/types";
|
|
3
|
+
|
|
4
|
+
export type MdxContent = string | RawHtml;
|
|
5
|
+
|
|
6
|
+
export type MdxModule = {
|
|
7
|
+
default: (props: Record<string, string | number | boolean | undefined>) => MdxContent;
|
|
8
|
+
head?: Head;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type PrerenderDataPayload = {
|
|
12
|
+
mdxHtml?: string;
|
|
13
|
+
mdxHtmlBase64?: string;
|
|
14
|
+
mdxPath?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ContentPageType = "doc" | "custom" | "link";
|
|
18
|
+
|
|
19
|
+
export type ContentMeta = {
|
|
20
|
+
title: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
order?: number;
|
|
23
|
+
priority: number;
|
|
24
|
+
type: ContentPageType;
|
|
25
|
+
link?: string;
|
|
26
|
+
external?: boolean;
|
|
27
|
+
section?: string;
|
|
28
|
+
badge?: string;
|
|
29
|
+
draft?: boolean;
|
|
30
|
+
hidden?: boolean;
|
|
31
|
+
tags: string[];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type ContentTreeNode = {
|
|
35
|
+
title: string;
|
|
36
|
+
path?: string;
|
|
37
|
+
type: ContentPageType;
|
|
38
|
+
link?: string;
|
|
39
|
+
external?: boolean;
|
|
40
|
+
priority: number;
|
|
41
|
+
order?: number;
|
|
42
|
+
description?: string;
|
|
43
|
+
badge?: string;
|
|
44
|
+
children: ContentTreeNode[];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type SearchDocument = {
|
|
48
|
+
id: string;
|
|
49
|
+
title: string;
|
|
50
|
+
path: string;
|
|
51
|
+
text: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
tags: string[];
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const articleClass = [
|
|
57
|
+
"flex-1 prose w-full max-w-none",
|
|
58
|
+
"text-areia-default",
|
|
59
|
+
"[--tw-prose-body:var(--areia-text-default)]",
|
|
60
|
+
"[--tw-prose-headings:var(--areia-text-strong)]",
|
|
61
|
+
"[--tw-prose-lead:var(--areia-text-subtle)]",
|
|
62
|
+
"[--tw-prose-links:var(--areia-primary)]",
|
|
63
|
+
"[--tw-prose-bold:var(--areia-text-strong)]",
|
|
64
|
+
"[--tw-prose-counters:var(--areia-text-muted)]",
|
|
65
|
+
"[--tw-prose-bullets:var(--areia-text-muted)]",
|
|
66
|
+
"[--tw-prose-hr:var(--areia-divider)]",
|
|
67
|
+
"[--tw-prose-quotes:var(--areia-text-strong)]",
|
|
68
|
+
"[--tw-prose-quote-borders:var(--areia-border)]",
|
|
69
|
+
"[--tw-prose-captions:var(--areia-text-muted)]",
|
|
70
|
+
"[--tw-prose-code:var(--areia-text-strong)]",
|
|
71
|
+
"[--tw-prose-pre-code:var(--areia-text-default)]",
|
|
72
|
+
"[--tw-prose-pre-bg:var(--areia-surface-muted)]",
|
|
73
|
+
"[--tw-prose-th-borders:var(--areia-border)]",
|
|
74
|
+
"[--tw-prose-td-borders:var(--areia-divider)]",
|
|
75
|
+
"prose-a:underline-offset-4",
|
|
76
|
+
"prose-code:before:content-none prose-code:after:content-none",
|
|
77
|
+
].join(" ");
|
package/src/docs/mdx.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export {
|
|
2
|
+
articleClass,
|
|
3
|
+
contentMeta,
|
|
4
|
+
contentTree,
|
|
5
|
+
getClientPrerenderedMdxHtml,
|
|
6
|
+
getDocLinks,
|
|
7
|
+
getMdxContent,
|
|
8
|
+
getMdxHead,
|
|
9
|
+
getMdxSourceForRoute,
|
|
10
|
+
getPrerenderedMdxHtml,
|
|
11
|
+
headDefaults,
|
|
12
|
+
loadMdxHtml,
|
|
13
|
+
mdxRoutes,
|
|
14
|
+
renderMdx,
|
|
15
|
+
renderMdxContent,
|
|
16
|
+
searchDocuments,
|
|
17
|
+
setPrerenderedMdxHtml,
|
|
18
|
+
} from "./mdx/index";
|
|
19
|
+
|
|
20
|
+
export type { ContentMeta, ContentPageType, ContentTreeNode, SearchDocument } from "./mdx/index";
|