@silicajs/next 0.2.0 → 0.2.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.
@@ -10,13 +10,7 @@ import {
10
10
  slugToHref
11
11
  } from "@silicajs/core/runtime";
12
12
  import { SilicaLink } from "@silicajs/components/routing";
13
- import {
14
- loadBuildId,
15
- loadGraph,
16
- loadManifest,
17
- loadResolvedConfig,
18
- normalizeRouteSlug
19
- } from "../server-data.js";
13
+ import { loadPageRuntimeData, normalizeRouteSlug } from "../server-data.js";
20
14
  function MarkdownLink({
21
15
  href,
22
16
  ...props
@@ -46,9 +40,9 @@ async function generateMetadata({ params }) {
46
40
  async function getPageManifest() {
47
41
  "use cache";
48
42
  cacheLife("max");
49
- const buildId = await loadBuildId();
43
+ const { buildId, manifest } = await loadPageRuntimeData();
50
44
  cacheTag("build", `build:${buildId}`);
51
- return loadManifest();
45
+ return manifest;
52
46
  }
53
47
  async function VaultContent({
54
48
  slug,
@@ -56,18 +50,13 @@ async function VaultContent({
56
50
  }) {
57
51
  "use cache";
58
52
  cacheLife("max");
59
- const buildId = await loadBuildId();
53
+ const { buildId, manifest, graph, config, wikilinkIndex } = await loadPageRuntimeData();
60
54
  cacheTag("build", `build:${buildId}`, `page:${slug}`);
61
- const [manifest, graph, config] = await Promise.all([
62
- loadManifest(),
63
- loadGraph(),
64
- loadResolvedConfig()
65
- ]);
66
55
  const entry = manifest.bySlug[slug];
67
56
  if (!entry) notFound();
68
57
  const renderContext = (currentSlug, embedDepth = 0) => ({
69
58
  slug: currentSlug,
70
- allSlugs: manifest.allSlugs,
59
+ wikilinkIndex,
71
60
  assetBaseUrl: "/silica",
72
61
  wikilinkStrategy: config.wikilinks.strategy,
73
62
  tags: config.tags,
@@ -82,7 +71,7 @@ async function VaultContent({
82
71
  const resolved = resolveWikiLink(
83
72
  currentSlug,
84
73
  target.path || currentSlug,
85
- manifest.allSlugs,
74
+ wikilinkIndex,
86
75
  config.wikilinks.strategy,
87
76
  config.ordering
88
77
  );
@@ -107,10 +96,10 @@ async function VaultContent({
107
96
  backlinks: makeBacklinks(slug, manifest, graph),
108
97
  page: {
109
98
  slug,
110
- title: rendered.title ?? entry.title,
111
- description: rendered.description ?? entry.description,
99
+ title: entry.title,
100
+ description: entry.description,
112
101
  content: rendered.content,
113
- frontmatter: rendered.frontmatter,
102
+ frontmatter: entry.frontmatter,
114
103
  toc: rendered.toc,
115
104
  tags: entry.tags,
116
105
  entry
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/page.tsx"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport type { AnchorHTMLAttributes } from \"react\";\nimport { cacheLife, cacheTag } from \"next/cache\";\nimport { notFound } from \"next/navigation\";\nimport {\n getMetaDescription,\n renderMarkdown,\n renderMarkdownHtml,\n resolveWikiLink,\n slugToHref,\n type RenderContext,\n} from \"@silicajs/core/runtime\";\nimport { SilicaLink } from \"@silicajs/components/routing\";\nimport {\n loadBuildId,\n loadGraph,\n loadManifest,\n loadResolvedConfig,\n normalizeRouteSlug,\n} from \"../server-data.js\";\nimport type {\n SilicaTheme,\n ThemeBacklink,\n ThemeBreadcrumb,\n} from \"@silicajs/core/theme\";\n\nfunction MarkdownLink({\n href,\n ...props\n}: AnchorHTMLAttributes<HTMLAnchorElement>) {\n if (href && href.startsWith(\"/\") && !href.startsWith(\"/silica/\")) {\n return <SilicaLink href={href} {...props} />;\n }\n\n return <a href={href} {...props} />;\n}\n\nexport async function generateStaticParams() {\n const manifest = await getPageManifest();\n return manifest.entries.map((entry) => ({\n slug: entry.slug === \"index\" ? [] : entry.slug.split(\"/\"),\n }));\n}\n\nexport async function generateMetadata({ params }: PageProps) {\n const resolvedParams = await params;\n const slug = normalizeRouteSlug(resolvedParams?.slug);\n const manifest = await getPageManifest();\n const entry = manifest.bySlug[slug];\n if (!entry) return {};\n return {\n title: entry.title,\n description: getMetaDescription(entry),\n };\n}\n\nasync function getPageManifest() {\n \"use cache\";\n cacheLife(\"max\");\n const buildId = await loadBuildId();\n cacheTag(\"build\", `build:${buildId}`);\n return loadManifest();\n}\n\nexport type PageProps = {\n params: Promise<{ slug?: string[] }> | { slug?: string[] };\n};\n\nexport async function VaultContent({\n slug,\n theme,\n}: {\n slug: string;\n theme: SilicaTheme;\n}) {\n \"use cache\";\n cacheLife(\"max\");\n const buildId = await loadBuildId();\n cacheTag(\"build\", `build:${buildId}`, `page:${slug}`);\n\n const [manifest, graph, config] = await Promise.all([\n loadManifest(),\n loadGraph(),\n loadResolvedConfig(),\n ]);\n const entry = manifest.bySlug[slug];\n if (!entry) notFound();\n\n const renderContext = (\n currentSlug: string,\n embedDepth = 0,\n ): RenderContext => ({\n slug: currentSlug,\n allSlugs: manifest.allSlugs,\n assetBaseUrl: \"/silica\",\n wikilinkStrategy: config.wikilinks.strategy,\n tags: config.tags,\n ordering: config.ordering,\n embedDepth,\n maxEmbedDepth: 3,\n components: {\n ...theme.components,\n a: MarkdownLink,\n },\n resolveEmbed: async (target) => {\n const resolved = resolveWikiLink(\n currentSlug,\n target.path || currentSlug,\n manifest.allSlugs,\n config.wikilinks.strategy,\n config.ordering,\n );\n if (!resolved || embedDepth >= 3) return;\n const embeddedEntry = manifest.bySlug[resolved];\n if (!embeddedEntry) return;\n const embeddedRaw = await fs.readFile(embeddedEntry.file, \"utf8\");\n const scopedRaw = scopeEmbedMarkdown(embeddedRaw, target);\n return renderMarkdownHtml(\n scopedRaw,\n renderContext(resolved, embedDepth + 1),\n );\n },\n });\n\n const raw = await fs.readFile(entry.file, \"utf8\");\n const rendered = await renderMarkdown(raw, renderContext(slug));\n\n return (\n <theme.PageRenderer\n config={config}\n breadcrumbs={makeBreadcrumbs(slug, manifest)}\n backlinks={makeBacklinks(slug, manifest, graph)}\n page={{\n slug,\n title: rendered.title ?? entry.title,\n description: rendered.description ?? entry.description,\n content: rendered.content,\n frontmatter: rendered.frontmatter,\n toc: rendered.toc,\n tags: entry.tags,\n entry,\n }}\n />\n );\n}\n\nfunction makeBreadcrumbs(\n slug: string,\n manifest: Awaited<ReturnType<typeof loadManifest>>,\n): ThemeBreadcrumb[] {\n if (slug === \"index\" || !slug.includes(\"/\")) return [];\n\n const breadcrumbs: ThemeBreadcrumb[] = [{ label: \"Home\", href: \"/\" }];\n const segments = slug.split(\"/\").slice(0, -1);\n let acc = \"\";\n for (const segment of segments) {\n acc = acc ? `${acc}/${segment}` : segment;\n breadcrumbs.push({\n label: prettySegment(segment),\n href: breadcrumbSegmentHref(acc, manifest),\n });\n }\n return breadcrumbs;\n}\n\nfunction breadcrumbSegmentHref(\n segmentPath: string,\n manifest: Awaited<ReturnType<typeof loadManifest>>,\n): string | undefined {\n if (manifest.bySlug[segmentPath]) return slugToHref(segmentPath);\n const indexSlug = `${segmentPath}/index`;\n if (manifest.bySlug[indexSlug]) return slugToHref(indexSlug);\n return undefined;\n}\n\nfunction makeBacklinks(\n slug: string,\n manifest: Awaited<ReturnType<typeof loadManifest>>,\n graph: Awaited<ReturnType<typeof loadGraph>>,\n): ThemeBacklink[] {\n return (graph.backlinks[slug] ?? []).map((source) => ({\n slug: source,\n title: manifest.bySlug[source]?.title ?? source,\n }));\n}\n\nfunction prettySegment(segment: string): string {\n return segment\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction scopeEmbedMarkdown(\n raw: string,\n target: Parameters<NonNullable<RenderContext[\"resolveEmbed\"]>>[0],\n): string {\n if (target.blockId) return extractBlock(raw, target.blockId) ?? raw;\n if (target.heading) return extractHeadingSection(raw, target.heading) ?? raw;\n return raw;\n}\n\nfunction extractHeadingSection(\n raw: string,\n heading: string,\n): string | undefined {\n const lines = raw.split(/\\r?\\n/);\n const expected = normalizeHeading(heading);\n const start = lines.findIndex((line) => {\n const parsed = parseHeading(line);\n return parsed ? normalizeHeading(parsed.text) === expected : false;\n });\n if (start === -1) return;\n\n const startHeading = parseHeading(lines[start] ?? \"\");\n if (!startHeading) return;\n let end = lines.length;\n for (let index = start + 1; index < lines.length; index += 1) {\n const nextHeading = parseHeading(lines[index] ?? \"\");\n if (nextHeading && nextHeading.depth <= startHeading.depth) {\n end = index;\n break;\n }\n }\n\n return lines.slice(start, end).join(\"\\n\").trim();\n}\n\nfunction extractBlock(raw: string, blockId: string): string | undefined {\n const lines = raw.split(/\\r?\\n/);\n const blockIdPattern = new RegExp(\n `(^|\\\\s)\\\\^${escapeRegExp(blockId)}(?=\\\\s|$)`,\n );\n const matchIndex = lines.findIndex((line) => blockIdPattern.test(line));\n if (matchIndex === -1) return;\n\n let start = matchIndex;\n while (start > 0 && lines[start - 1]?.trim()) start -= 1;\n\n let end = matchIndex + 1;\n while (end < lines.length && lines[end]?.trim()) end += 1;\n\n return lines.slice(start, end).join(\"\\n\").replace(blockIdPattern, \"\").trim();\n}\n\nfunction parseHeading(\n line: string,\n): { depth: number; text: string } | undefined {\n const match = /^(#{1,6})\\s+(.+?)\\s*#*\\s*$/.exec(line);\n if (!match) return;\n return {\n depth: match[1]!.length,\n text: match[2]!,\n };\n}\n\nfunction normalizeHeading(value: string): string {\n return value.trim().replace(/\\s+/g, \" \").toLowerCase();\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/routes/page.tsx"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport type { AnchorHTMLAttributes } from \"react\";\nimport { cacheLife, cacheTag } from \"next/cache\";\nimport { notFound } from \"next/navigation\";\nimport {\n getMetaDescription,\n renderMarkdown,\n renderMarkdownHtml,\n resolveWikiLink,\n slugToHref,\n type Graph,\n type Manifest,\n type RenderContext,\n} from \"@silicajs/core/runtime\";\nimport { SilicaLink } from \"@silicajs/components/routing\";\nimport { loadPageRuntimeData, normalizeRouteSlug } from \"../server-data.js\";\nimport type {\n SilicaTheme,\n ThemeBacklink,\n ThemeBreadcrumb,\n} from \"@silicajs/core/theme\";\n\nfunction MarkdownLink({\n href,\n ...props\n}: AnchorHTMLAttributes<HTMLAnchorElement>) {\n if (href && href.startsWith(\"/\") && !href.startsWith(\"/silica/\")) {\n return <SilicaLink href={href} {...props} />;\n }\n\n return <a href={href} {...props} />;\n}\n\nexport async function generateStaticParams() {\n const manifest = await getPageManifest();\n return manifest.entries.map((entry) => ({\n slug: entry.slug === \"index\" ? [] : entry.slug.split(\"/\"),\n }));\n}\n\nexport async function generateMetadata({ params }: PageProps) {\n const resolvedParams = await params;\n const slug = normalizeRouteSlug(resolvedParams?.slug);\n const manifest = await getPageManifest();\n const entry = manifest.bySlug[slug];\n if (!entry) return {};\n return {\n title: entry.title,\n description: getMetaDescription(entry),\n };\n}\n\nasync function getPageManifest() {\n \"use cache\";\n cacheLife(\"max\");\n const { buildId, manifest } = await loadPageRuntimeData();\n cacheTag(\"build\", `build:${buildId}`);\n return manifest;\n}\n\nexport type PageProps = {\n params: Promise<{ slug?: string[] }> | { slug?: string[] };\n};\n\nexport async function VaultContent({\n slug,\n theme,\n}: {\n slug: string;\n theme: SilicaTheme;\n}) {\n \"use cache\";\n cacheLife(\"max\");\n const { buildId, manifest, graph, config, wikilinkIndex } =\n await loadPageRuntimeData();\n cacheTag(\"build\", `build:${buildId}`, `page:${slug}`);\n\n const entry = manifest.bySlug[slug];\n if (!entry) notFound();\n\n const renderContext = (\n currentSlug: string,\n embedDepth = 0,\n ): RenderContext => ({\n slug: currentSlug,\n wikilinkIndex,\n assetBaseUrl: \"/silica\",\n wikilinkStrategy: config.wikilinks.strategy,\n tags: config.tags,\n ordering: config.ordering,\n embedDepth,\n maxEmbedDepth: 3,\n components: {\n ...theme.components,\n a: MarkdownLink,\n },\n resolveEmbed: async (target) => {\n const resolved = resolveWikiLink(\n currentSlug,\n target.path || currentSlug,\n wikilinkIndex,\n config.wikilinks.strategy,\n config.ordering,\n );\n if (!resolved || embedDepth >= 3) return;\n const embeddedEntry = manifest.bySlug[resolved];\n if (!embeddedEntry) return;\n const embeddedRaw = await fs.readFile(embeddedEntry.file, \"utf8\");\n const scopedRaw = scopeEmbedMarkdown(embeddedRaw, target);\n return renderMarkdownHtml(\n scopedRaw,\n renderContext(resolved, embedDepth + 1),\n );\n },\n });\n\n const raw = await fs.readFile(entry.file, \"utf8\");\n const rendered = await renderMarkdown(raw, renderContext(slug));\n\n return (\n <theme.PageRenderer\n config={config}\n breadcrumbs={makeBreadcrumbs(slug, manifest)}\n backlinks={makeBacklinks(slug, manifest, graph)}\n page={{\n slug,\n title: entry.title,\n description: entry.description,\n content: rendered.content,\n frontmatter: entry.frontmatter,\n toc: rendered.toc,\n tags: entry.tags,\n entry,\n }}\n />\n );\n}\n\nfunction makeBreadcrumbs(slug: string, manifest: Manifest): ThemeBreadcrumb[] {\n if (slug === \"index\" || !slug.includes(\"/\")) return [];\n\n const breadcrumbs: ThemeBreadcrumb[] = [{ label: \"Home\", href: \"/\" }];\n const segments = slug.split(\"/\").slice(0, -1);\n let acc = \"\";\n for (const segment of segments) {\n acc = acc ? `${acc}/${segment}` : segment;\n breadcrumbs.push({\n label: prettySegment(segment),\n href: breadcrumbSegmentHref(acc, manifest),\n });\n }\n return breadcrumbs;\n}\n\nfunction breadcrumbSegmentHref(\n segmentPath: string,\n manifest: Manifest,\n): string | undefined {\n if (manifest.bySlug[segmentPath]) return slugToHref(segmentPath);\n const indexSlug = `${segmentPath}/index`;\n if (manifest.bySlug[indexSlug]) return slugToHref(indexSlug);\n return undefined;\n}\n\nfunction makeBacklinks(\n slug: string,\n manifest: Manifest,\n graph: Graph,\n): ThemeBacklink[] {\n return (graph.backlinks[slug] ?? []).map((source) => ({\n slug: source,\n title: manifest.bySlug[source]?.title ?? source,\n }));\n}\n\nfunction prettySegment(segment: string): string {\n return segment\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction scopeEmbedMarkdown(\n raw: string,\n target: Parameters<NonNullable<RenderContext[\"resolveEmbed\"]>>[0],\n): string {\n if (target.blockId) return extractBlock(raw, target.blockId) ?? raw;\n if (target.heading) return extractHeadingSection(raw, target.heading) ?? raw;\n return raw;\n}\n\nfunction extractHeadingSection(\n raw: string,\n heading: string,\n): string | undefined {\n const lines = raw.split(/\\r?\\n/);\n const expected = normalizeHeading(heading);\n const start = lines.findIndex((line) => {\n const parsed = parseHeading(line);\n return parsed ? normalizeHeading(parsed.text) === expected : false;\n });\n if (start === -1) return;\n\n const startHeading = parseHeading(lines[start] ?? \"\");\n if (!startHeading) return;\n let end = lines.length;\n for (let index = start + 1; index < lines.length; index += 1) {\n const nextHeading = parseHeading(lines[index] ?? \"\");\n if (nextHeading && nextHeading.depth <= startHeading.depth) {\n end = index;\n break;\n }\n }\n\n return lines.slice(start, end).join(\"\\n\").trim();\n}\n\nfunction extractBlock(raw: string, blockId: string): string | undefined {\n const lines = raw.split(/\\r?\\n/);\n const blockIdPattern = new RegExp(\n `(^|\\\\s)\\\\^${escapeRegExp(blockId)}(?=\\\\s|$)`,\n );\n const matchIndex = lines.findIndex((line) => blockIdPattern.test(line));\n if (matchIndex === -1) return;\n\n let start = matchIndex;\n while (start > 0 && lines[start - 1]?.trim()) start -= 1;\n\n let end = matchIndex + 1;\n while (end < lines.length && lines[end]?.trim()) end += 1;\n\n return lines.slice(start, end).join(\"\\n\").replace(blockIdPattern, \"\").trim();\n}\n\nfunction parseHeading(\n line: string,\n): { depth: number; text: string } | undefined {\n const match = /^(#{1,6})\\s+(.+?)\\s*#*\\s*$/.exec(line);\n if (!match) return;\n return {\n depth: match[1]!.length,\n text: match[2]!,\n };\n}\n\nfunction normalizeHeading(value: string): string {\n return value.trim().replace(/\\s+/g, \" \").toLowerCase();\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import { cacheLife, cacheTag } from "next/cache";
3
3
  import { notFound } from "next/navigation";
4
4
  import { TagsList } from "@silicajs/components";
5
5
  import { getTagHierarchy, tagMatches } from "@silicajs/remark-obsidian";
6
- import { loadBuildId, loadManifest } from "../server-data.js";
6
+ import { loadPageRuntimeData } from "../server-data.js";
7
7
  const EMPTY_TAG_STATIC_PARAM = "__silica_empty_tags__";
8
8
  async function generateStaticParams() {
9
9
  const manifest = await getTagsManifest();
@@ -39,9 +39,9 @@ async function TagsPage({ params }) {
39
39
  async function getTagsManifest() {
40
40
  "use cache";
41
41
  cacheLife("max");
42
- const buildId = await loadBuildId();
42
+ const { buildId, manifest } = await loadPageRuntimeData();
43
43
  cacheTag("build", `build:${buildId}`);
44
- return loadManifest();
44
+ return manifest;
45
45
  }
46
46
  function routeTagToString(tag) {
47
47
  return Array.isArray(tag) ? tag.join("/") : tag;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/tags-page.tsx"],"sourcesContent":["import { cacheLife, cacheTag } from \"next/cache\";\nimport { notFound } from \"next/navigation\";\nimport { TagsList } from \"@silicajs/components\";\nimport { getTagHierarchy, tagMatches } from \"@silicajs/remark-obsidian\";\nimport { loadBuildId, loadManifest } from \"../server-data.js\";\n\nconst EMPTY_TAG_STATIC_PARAM = \"__silica_empty_tags__\";\n\nexport type TagsPageProps = {\n params: Promise<{ tag: string | string[] }> | { tag: string | string[] };\n};\n\nexport async function generateStaticParams() {\n const manifest = await getTagsManifest();\n const tags = new Set(\n manifest.entries\n .filter(isListedEntry)\n .flatMap((entry) => entry.tags.flatMap((tag) => getTagHierarchy(tag))),\n );\n const params = [...tags].map((tag) => ({ tag: tag.split(\"/\") }));\n return params.length > 0 ? params : [{ tag: [EMPTY_TAG_STATIC_PARAM] }];\n}\n\nexport async function generateMetadata({ params }: TagsPageProps) {\n const tag = routeTagToString((await params).tag);\n return {\n title: `#${tag}`,\n };\n}\n\nexport default async function TagsPage({ params }: TagsPageProps) {\n const tag = routeTagToString((await params).tag);\n const manifest = await getTagsManifest();\n if (\n !manifest.entries\n .filter(isListedEntry)\n .some((entry) => entry.tags.some((entryTag) => tagMatches(entryTag, tag)))\n ) {\n notFound();\n }\n return (\n <TagsList\n manifest={{\n ...manifest,\n entries: manifest.entries.filter(isListedEntry),\n }}\n tag={tag}\n />\n );\n}\n\nasync function getTagsManifest() {\n \"use cache\";\n cacheLife(\"max\");\n const buildId = await loadBuildId();\n cacheTag(\"build\", `build:${buildId}`);\n return loadManifest();\n}\n\nfunction routeTagToString(tag: string | string[]): string {\n return Array.isArray(tag) ? tag.join(\"/\") : tag;\n}\n\nfunction isListedEntry(entry: { frontmatter: Record<string, unknown> }) {\n return entry.frontmatter.listed !== false;\n}\n"],"mappings":"AAyCI;AAzCJ,SAAS,WAAW,gBAAgB;AACpC,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB,kBAAkB;AAC5C,SAAS,aAAa,oBAAoB;AAE1C,MAAM,yBAAyB;AAM/B,eAAsB,uBAAuB;AAC3C,QAAM,WAAW,MAAM,gBAAgB;AACvC,QAAM,OAAO,IAAI;AAAA,IACf,SAAS,QACN,OAAO,aAAa,EACpB,QAAQ,CAAC,UAAU,MAAM,KAAK,QAAQ,CAAC,QAAQ,gBAAgB,GAAG,CAAC,CAAC;AAAA,EACzE;AACA,QAAM,SAAS,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,IAAI,MAAM,GAAG,EAAE,EAAE;AAC/D,SAAO,OAAO,SAAS,IAAI,SAAS,CAAC,EAAE,KAAK,CAAC,sBAAsB,EAAE,CAAC;AACxE;AAEA,eAAsB,iBAAiB,EAAE,OAAO,GAAkB;AAChE,QAAM,MAAM,kBAAkB,MAAM,QAAQ,GAAG;AAC/C,SAAO;AAAA,IACL,OAAO,IAAI,GAAG;AAAA,EAChB;AACF;AAEA,eAAO,SAAgC,EAAE,OAAO,GAAkB;AAChE,QAAM,MAAM,kBAAkB,MAAM,QAAQ,GAAG;AAC/C,QAAM,WAAW,MAAM,gBAAgB;AACvC,MACE,CAAC,SAAS,QACP,OAAO,aAAa,EACpB,KAAK,CAAC,UAAU,MAAM,KAAK,KAAK,CAAC,aAAa,WAAW,UAAU,GAAG,CAAC,CAAC,GAC3E;AACA,aAAS;AAAA,EACX;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,QACR,GAAG;AAAA,QACH,SAAS,SAAS,QAAQ,OAAO,aAAa;AAAA,MAChD;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;AAEA,eAAe,kBAAkB;AAC/B;AACA,YAAU,KAAK;AACf,QAAM,UAAU,MAAM,YAAY;AAClC,WAAS,SAAS,SAAS,OAAO,EAAE;AACpC,SAAO,aAAa;AACtB;AAEA,SAAS,iBAAiB,KAAgC;AACxD,SAAO,MAAM,QAAQ,GAAG,IAAI,IAAI,KAAK,GAAG,IAAI;AAC9C;AAEA,SAAS,cAAc,OAAiD;AACtE,SAAO,MAAM,YAAY,WAAW;AACtC;","names":[]}
1
+ {"version":3,"sources":["../../src/routes/tags-page.tsx"],"sourcesContent":["import { cacheLife, cacheTag } from \"next/cache\";\nimport { notFound } from \"next/navigation\";\nimport { TagsList } from \"@silicajs/components\";\nimport { getTagHierarchy, tagMatches } from \"@silicajs/remark-obsidian\";\nimport { loadPageRuntimeData } from \"../server-data.js\";\n\nconst EMPTY_TAG_STATIC_PARAM = \"__silica_empty_tags__\";\n\nexport type TagsPageProps = {\n params: Promise<{ tag: string | string[] }> | { tag: string | string[] };\n};\n\nexport async function generateStaticParams() {\n const manifest = await getTagsManifest();\n const tags = new Set(\n manifest.entries\n .filter(isListedEntry)\n .flatMap((entry) => entry.tags.flatMap((tag) => getTagHierarchy(tag))),\n );\n const params = [...tags].map((tag) => ({ tag: tag.split(\"/\") }));\n return params.length > 0 ? params : [{ tag: [EMPTY_TAG_STATIC_PARAM] }];\n}\n\nexport async function generateMetadata({ params }: TagsPageProps) {\n const tag = routeTagToString((await params).tag);\n return {\n title: `#${tag}`,\n };\n}\n\nexport default async function TagsPage({ params }: TagsPageProps) {\n const tag = routeTagToString((await params).tag);\n const manifest = await getTagsManifest();\n if (\n !manifest.entries\n .filter(isListedEntry)\n .some((entry) => entry.tags.some((entryTag) => tagMatches(entryTag, tag)))\n ) {\n notFound();\n }\n return (\n <TagsList\n manifest={{\n ...manifest,\n entries: manifest.entries.filter(isListedEntry),\n }}\n tag={tag}\n />\n );\n}\n\nasync function getTagsManifest() {\n \"use cache\";\n cacheLife(\"max\");\n const { buildId, manifest } = await loadPageRuntimeData();\n cacheTag(\"build\", `build:${buildId}`);\n return manifest;\n}\n\nfunction routeTagToString(tag: string | string[]): string {\n return Array.isArray(tag) ? tag.join(\"/\") : tag;\n}\n\nfunction isListedEntry(entry: { frontmatter: Record<string, unknown> }) {\n return entry.frontmatter.listed !== false;\n}\n"],"mappings":"AAyCI;AAzCJ,SAAS,WAAW,gBAAgB;AACpC,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB,kBAAkB;AAC5C,SAAS,2BAA2B;AAEpC,MAAM,yBAAyB;AAM/B,eAAsB,uBAAuB;AAC3C,QAAM,WAAW,MAAM,gBAAgB;AACvC,QAAM,OAAO,IAAI;AAAA,IACf,SAAS,QACN,OAAO,aAAa,EACpB,QAAQ,CAAC,UAAU,MAAM,KAAK,QAAQ,CAAC,QAAQ,gBAAgB,GAAG,CAAC,CAAC;AAAA,EACzE;AACA,QAAM,SAAS,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,IAAI,MAAM,GAAG,EAAE,EAAE;AAC/D,SAAO,OAAO,SAAS,IAAI,SAAS,CAAC,EAAE,KAAK,CAAC,sBAAsB,EAAE,CAAC;AACxE;AAEA,eAAsB,iBAAiB,EAAE,OAAO,GAAkB;AAChE,QAAM,MAAM,kBAAkB,MAAM,QAAQ,GAAG;AAC/C,SAAO;AAAA,IACL,OAAO,IAAI,GAAG;AAAA,EAChB;AACF;AAEA,eAAO,SAAgC,EAAE,OAAO,GAAkB;AAChE,QAAM,MAAM,kBAAkB,MAAM,QAAQ,GAAG;AAC/C,QAAM,WAAW,MAAM,gBAAgB;AACvC,MACE,CAAC,SAAS,QACP,OAAO,aAAa,EACpB,KAAK,CAAC,UAAU,MAAM,KAAK,KAAK,CAAC,aAAa,WAAW,UAAU,GAAG,CAAC,CAAC,GAC3E;AACA,aAAS;AAAA,EACX;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,QACR,GAAG;AAAA,QACH,SAAS,SAAS,QAAQ,OAAO,aAAa;AAAA,MAChD;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;AAEA,eAAe,kBAAkB;AAC/B;AACA,YAAU,KAAK;AACf,QAAM,EAAE,SAAS,SAAS,IAAI,MAAM,oBAAoB;AACxD,WAAS,SAAS,SAAS,OAAO,EAAE;AACpC,SAAO;AACT;AAEA,SAAS,iBAAiB,KAAgC;AACxD,SAAO,MAAM,QAAQ,GAAG,IAAI,IAAI,KAAK,GAAG,IAAI;AAC9C;AAEA,SAAS,cAAc,OAAiD;AACtE,SAAO,MAAM,YAAY,WAAW;AACtC;","names":[]}
@@ -1,5 +1,12 @@
1
- import { Graph, Manifest, Navigation, ResolvedSilicaConfig } from '@silicajs/core/runtime';
1
+ import { Graph, Manifest, Navigation, ResolvedSilicaConfig, WikiLinkResolutionIndex } from '@silicajs/core/runtime';
2
2
 
3
+ type PageRuntimeData = {
4
+ buildId: string;
5
+ manifest: Manifest;
6
+ graph: Graph;
7
+ config: ResolvedSilicaConfig;
8
+ wikilinkIndex: WikiLinkResolutionIndex;
9
+ };
3
10
  declare function getProjectRoot(): string;
4
11
  declare function getSilicaRoot(): string;
5
12
  declare function loadManifest(): Promise<Manifest>;
@@ -7,6 +14,7 @@ declare function loadGraph(): Promise<Graph>;
7
14
  declare function loadNavigation(): Promise<Navigation>;
8
15
  declare function loadBuildId(): Promise<string>;
9
16
  declare function loadResolvedConfig(): Promise<ResolvedSilicaConfig>;
17
+ declare function loadPageRuntimeData(): Promise<PageRuntimeData>;
10
18
  declare function normalizeRouteSlug(slug?: string[]): string;
11
19
 
12
- export { getProjectRoot, getSilicaRoot, loadBuildId, loadGraph, loadManifest, loadNavigation, loadResolvedConfig, normalizeRouteSlug };
20
+ export { type PageRuntimeData, getProjectRoot, getSilicaRoot, loadBuildId, loadGraph, loadManifest, loadNavigation, loadPageRuntimeData, loadResolvedConfig, normalizeRouteSlug };
@@ -1,5 +1,7 @@
1
1
  import path from "node:path";
2
2
  import fs from "fs-extra";
3
+ import { createWikiLinkResolutionIndex } from "@silicajs/core/runtime";
4
+ let pageRuntimeDataCache;
3
5
  function getProjectRoot() {
4
6
  const projectRoot = process.env.SILICA_PROJECT_ROOT;
5
7
  if (!projectRoot) {
@@ -43,6 +45,34 @@ async function loadResolvedConfig() {
43
45
  path.join(getSilicaRoot(), "config.json")
44
46
  );
45
47
  }
48
+ async function loadPageRuntimeData() {
49
+ const buildId = await loadBuildId();
50
+ const cacheKey = `${getProjectRoot()}:${buildId}`;
51
+ if (pageRuntimeDataCache?.cacheKey === cacheKey) {
52
+ return pageRuntimeDataCache.promise;
53
+ }
54
+ const promise = Promise.all([
55
+ loadManifest(),
56
+ loadGraph(),
57
+ loadResolvedConfig()
58
+ ]).then(([manifest, graph, config]) => ({
59
+ buildId,
60
+ manifest,
61
+ graph,
62
+ config,
63
+ wikilinkIndex: createWikiLinkResolutionIndex(
64
+ manifest.allSlugs,
65
+ config.ordering
66
+ )
67
+ }));
68
+ pageRuntimeDataCache = { cacheKey, promise };
69
+ promise.catch(() => {
70
+ if (pageRuntimeDataCache?.cacheKey === cacheKey) {
71
+ pageRuntimeDataCache = void 0;
72
+ }
73
+ });
74
+ return promise;
75
+ }
46
76
  function normalizeRouteSlug(slug) {
47
77
  return slug?.length ? slug.join("/") : "index";
48
78
  }
@@ -53,6 +83,7 @@ export {
53
83
  loadGraph,
54
84
  loadManifest,
55
85
  loadNavigation,
86
+ loadPageRuntimeData,
56
87
  loadResolvedConfig,
57
88
  normalizeRouteSlug
58
89
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server-data.ts"],"sourcesContent":["import path from \"node:path\";\nimport fs from \"fs-extra\";\nimport type {\n Graph,\n Manifest,\n Navigation,\n ResolvedSilicaConfig,\n} from \"@silicajs/core/runtime\";\n\nexport function getProjectRoot(): string {\n const projectRoot = process.env.SILICA_PROJECT_ROOT;\n if (!projectRoot) {\n throw new Error(\"SILICA_PROJECT_ROOT must be set by the Silica CLI.\");\n }\n\n return projectRoot;\n}\n\nexport function getSilicaRoot(): string {\n return path.join(getProjectRoot(), \".silica\");\n}\n\nexport async function loadManifest(): Promise<Manifest> {\n const manifest = (await fs.readJson(\n path.join(getSilicaRoot(), \"manifest.json\"),\n )) as Omit<Manifest, \"allSlugs\" | \"bySlug\"> &\n Partial<Pick<Manifest, \"allSlugs\" | \"bySlug\">>;\n const entries = manifest.entries.map((entry) => ({\n ...entry,\n file: path.isAbsolute(entry.file)\n ? entry.file\n : path.join(getProjectRoot(), entry.file),\n }));\n return {\n ...manifest,\n entries,\n allSlugs: manifest.allSlugs ?? entries.map((entry) => entry.slug),\n bySlug:\n manifest.bySlug ??\n Object.fromEntries(entries.map((entry) => [entry.slug, entry])),\n };\n}\n\nexport async function loadGraph(): Promise<Graph> {\n return fs.readJson(\n path.join(getSilicaRoot(), \"graph.json\"),\n ) as Promise<Graph>;\n}\n\nexport async function loadNavigation(): Promise<Navigation> {\n return fs.readJson(\n path.join(getSilicaRoot(), \"navigation.json\"),\n ) as Promise<Navigation>;\n}\n\nexport async function loadBuildId(): Promise<string> {\n return (\n await fs.readFile(path.join(getSilicaRoot(), \"build-id.txt\"), \"utf8\")\n ).trim();\n}\n\nexport async function loadResolvedConfig() {\n return fs.readJson(\n path.join(getSilicaRoot(), \"config.json\"),\n ) as Promise<ResolvedSilicaConfig>;\n}\n\nexport function normalizeRouteSlug(slug?: string[]): string {\n return slug?.length ? slug.join(\"/\") : \"index\";\n}\n"],"mappings":"AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AAQR,SAAS,iBAAyB;AACvC,QAAM,cAAc,QAAQ,IAAI;AAChC,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AAEA,SAAO;AACT;AAEO,SAAS,gBAAwB;AACtC,SAAO,KAAK,KAAK,eAAe,GAAG,SAAS;AAC9C;AAEA,eAAsB,eAAkC;AACtD,QAAM,WAAY,MAAM,GAAG;AAAA,IACzB,KAAK,KAAK,cAAc,GAAG,eAAe;AAAA,EAC5C;AAEA,QAAM,UAAU,SAAS,QAAQ,IAAI,CAAC,WAAW;AAAA,IAC/C,GAAG;AAAA,IACH,MAAM,KAAK,WAAW,MAAM,IAAI,IAC5B,MAAM,OACN,KAAK,KAAK,eAAe,GAAG,MAAM,IAAI;AAAA,EAC5C,EAAE;AACF,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA,UAAU,SAAS,YAAY,QAAQ,IAAI,CAAC,UAAU,MAAM,IAAI;AAAA,IAChE,QACE,SAAS,UACT,OAAO,YAAY,QAAQ,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;AAAA,EAClE;AACF;AAEA,eAAsB,YAA4B;AAChD,SAAO,GAAG;AAAA,IACR,KAAK,KAAK,cAAc,GAAG,YAAY;AAAA,EACzC;AACF;AAEA,eAAsB,iBAAsC;AAC1D,SAAO,GAAG;AAAA,IACR,KAAK,KAAK,cAAc,GAAG,iBAAiB;AAAA,EAC9C;AACF;AAEA,eAAsB,cAA+B;AACnD,UACE,MAAM,GAAG,SAAS,KAAK,KAAK,cAAc,GAAG,cAAc,GAAG,MAAM,GACpE,KAAK;AACT;AAEA,eAAsB,qBAAqB;AACzC,SAAO,GAAG;AAAA,IACR,KAAK,KAAK,cAAc,GAAG,aAAa;AAAA,EAC1C;AACF;AAEO,SAAS,mBAAmB,MAAyB;AAC1D,SAAO,MAAM,SAAS,KAAK,KAAK,GAAG,IAAI;AACzC;","names":[]}
1
+ {"version":3,"sources":["../src/server-data.ts"],"sourcesContent":["import path from \"node:path\";\nimport fs from \"fs-extra\";\nimport type {\n Graph,\n Manifest,\n Navigation,\n ResolvedSilicaConfig,\n WikiLinkResolutionIndex,\n} from \"@silicajs/core/runtime\";\nimport { createWikiLinkResolutionIndex } from \"@silicajs/core/runtime\";\n\nexport type PageRuntimeData = {\n buildId: string;\n manifest: Manifest;\n graph: Graph;\n config: ResolvedSilicaConfig;\n wikilinkIndex: WikiLinkResolutionIndex;\n};\n\nlet pageRuntimeDataCache:\n | { cacheKey: string; promise: Promise<PageRuntimeData> }\n | undefined;\n\nexport function getProjectRoot(): string {\n const projectRoot = process.env.SILICA_PROJECT_ROOT;\n if (!projectRoot) {\n throw new Error(\"SILICA_PROJECT_ROOT must be set by the Silica CLI.\");\n }\n\n return projectRoot;\n}\n\nexport function getSilicaRoot(): string {\n return path.join(getProjectRoot(), \".silica\");\n}\n\nexport async function loadManifest(): Promise<Manifest> {\n const manifest = (await fs.readJson(\n path.join(getSilicaRoot(), \"manifest.json\"),\n )) as Omit<Manifest, \"allSlugs\" | \"bySlug\"> &\n Partial<Pick<Manifest, \"allSlugs\" | \"bySlug\">>;\n const entries = manifest.entries.map((entry) => ({\n ...entry,\n file: path.isAbsolute(entry.file)\n ? entry.file\n : path.join(getProjectRoot(), entry.file),\n }));\n return {\n ...manifest,\n entries,\n allSlugs: manifest.allSlugs ?? entries.map((entry) => entry.slug),\n bySlug:\n manifest.bySlug ??\n Object.fromEntries(entries.map((entry) => [entry.slug, entry])),\n };\n}\n\nexport async function loadGraph(): Promise<Graph> {\n return fs.readJson(\n path.join(getSilicaRoot(), \"graph.json\"),\n ) as Promise<Graph>;\n}\n\nexport async function loadNavigation(): Promise<Navigation> {\n return fs.readJson(\n path.join(getSilicaRoot(), \"navigation.json\"),\n ) as Promise<Navigation>;\n}\n\nexport async function loadBuildId(): Promise<string> {\n return (\n await fs.readFile(path.join(getSilicaRoot(), \"build-id.txt\"), \"utf8\")\n ).trim();\n}\n\nexport async function loadResolvedConfig() {\n return fs.readJson(\n path.join(getSilicaRoot(), \"config.json\"),\n ) as Promise<ResolvedSilicaConfig>;\n}\n\nexport async function loadPageRuntimeData(): Promise<PageRuntimeData> {\n const buildId = await loadBuildId();\n const cacheKey = `${getProjectRoot()}:${buildId}`;\n if (pageRuntimeDataCache?.cacheKey === cacheKey) {\n return pageRuntimeDataCache.promise;\n }\n\n const promise = Promise.all([\n loadManifest(),\n loadGraph(),\n loadResolvedConfig(),\n ]).then(([manifest, graph, config]) => ({\n buildId,\n manifest,\n graph,\n config,\n wikilinkIndex: createWikiLinkResolutionIndex(\n manifest.allSlugs,\n config.ordering,\n ),\n }));\n\n pageRuntimeDataCache = { cacheKey, promise };\n promise.catch(() => {\n if (pageRuntimeDataCache?.cacheKey === cacheKey) {\n pageRuntimeDataCache = undefined;\n }\n });\n return promise;\n}\n\nexport function normalizeRouteSlug(slug?: string[]): string {\n return slug?.length ? slug.join(\"/\") : \"index\";\n}\n"],"mappings":"AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AAQf,SAAS,qCAAqC;AAU9C,IAAI;AAIG,SAAS,iBAAyB;AACvC,QAAM,cAAc,QAAQ,IAAI;AAChC,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AAEA,SAAO;AACT;AAEO,SAAS,gBAAwB;AACtC,SAAO,KAAK,KAAK,eAAe,GAAG,SAAS;AAC9C;AAEA,eAAsB,eAAkC;AACtD,QAAM,WAAY,MAAM,GAAG;AAAA,IACzB,KAAK,KAAK,cAAc,GAAG,eAAe;AAAA,EAC5C;AAEA,QAAM,UAAU,SAAS,QAAQ,IAAI,CAAC,WAAW;AAAA,IAC/C,GAAG;AAAA,IACH,MAAM,KAAK,WAAW,MAAM,IAAI,IAC5B,MAAM,OACN,KAAK,KAAK,eAAe,GAAG,MAAM,IAAI;AAAA,EAC5C,EAAE;AACF,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA,UAAU,SAAS,YAAY,QAAQ,IAAI,CAAC,UAAU,MAAM,IAAI;AAAA,IAChE,QACE,SAAS,UACT,OAAO,YAAY,QAAQ,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;AAAA,EAClE;AACF;AAEA,eAAsB,YAA4B;AAChD,SAAO,GAAG;AAAA,IACR,KAAK,KAAK,cAAc,GAAG,YAAY;AAAA,EACzC;AACF;AAEA,eAAsB,iBAAsC;AAC1D,SAAO,GAAG;AAAA,IACR,KAAK,KAAK,cAAc,GAAG,iBAAiB;AAAA,EAC9C;AACF;AAEA,eAAsB,cAA+B;AACnD,UACE,MAAM,GAAG,SAAS,KAAK,KAAK,cAAc,GAAG,cAAc,GAAG,MAAM,GACpE,KAAK;AACT;AAEA,eAAsB,qBAAqB;AACzC,SAAO,GAAG;AAAA,IACR,KAAK,KAAK,cAAc,GAAG,aAAa;AAAA,EAC1C;AACF;AAEA,eAAsB,sBAAgD;AACpE,QAAM,UAAU,MAAM,YAAY;AAClC,QAAM,WAAW,GAAG,eAAe,CAAC,IAAI,OAAO;AAC/C,MAAI,sBAAsB,aAAa,UAAU;AAC/C,WAAO,qBAAqB;AAAA,EAC9B;AAEA,QAAM,UAAU,QAAQ,IAAI;AAAA,IAC1B,aAAa;AAAA,IACb,UAAU;AAAA,IACV,mBAAmB;AAAA,EACrB,CAAC,EAAE,KAAK,CAAC,CAAC,UAAU,OAAO,MAAM,OAAO;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,MACb,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF,EAAE;AAEF,yBAAuB,EAAE,UAAU,QAAQ;AAC3C,UAAQ,MAAM,MAAM;AAClB,QAAI,sBAAsB,aAAa,UAAU;AAC/C,6BAAuB;AAAA,IACzB;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEO,SAAS,mBAAmB,MAAyB;AAC1D,SAAO,MAAM,SAAS,KAAK,KAAK,GAAG,IAAI;AACzC;","names":[]}
@@ -1,6 +1,7 @@
1
1
  import type { NextConfig } from "next";
2
+ /* __SILICA_CONFIG_IMPORT__ */
2
3
 
3
- const nextConfig: NextConfig = {
4
+ const baseNextConfig: NextConfig = {
4
5
  cacheComponents: true,
5
6
  output: "standalone",
6
7
  transpilePackages: [
@@ -30,4 +31,6 @@ const nextConfig: NextConfig = {
30
31
  },
31
32
  };
32
33
 
34
+ /* __SILICA_CONFIG_OVERRIDE__ */
35
+
33
36
  export default nextConfig;
@@ -5,7 +5,7 @@ type TemplateFile = {
5
5
  content: string;
6
6
  };
7
7
  declare function getSilicaTemplates(): TemplateFile[];
8
- declare function nextConfigTemplate(): string;
8
+ declare function nextConfigTemplate(userConfigImport?: string): string;
9
9
  declare function themeModuleTemplate(themeValue: unknown): string;
10
10
  declare function proxyTemplate(config: ResolvedSilicaConfig): string;
11
11
  declare function tsconfigTemplate(hasUserTsconfig: boolean): string;
package/dist/templates.js CHANGED
@@ -7,8 +7,15 @@ const templateFilesRoot = fileURLToPath(
7
7
  function getSilicaTemplates() {
8
8
  return readTemplateDirectory(path.join(templateFilesRoot, "generated-app"));
9
9
  }
10
- function nextConfigTemplate() {
11
- return readTemplateFile("next.config.ts");
10
+ function nextConfigTemplate(userConfigImport) {
11
+ const template = readTemplateFile("next.config.ts");
12
+ return template.replace(
13
+ "/* __SILICA_CONFIG_IMPORT__ */",
14
+ userConfigImport ? `import { createJiti } from "jiti";` : ""
15
+ ).replace(
16
+ "/* __SILICA_CONFIG_OVERRIDE__ */",
17
+ nextConfigOverride(userConfigImport)
18
+ );
12
19
  }
13
20
  function themeModuleTemplate(themeValue) {
14
21
  return readTemplateFile("silica-theme.ts").replace(
@@ -51,6 +58,65 @@ function packageJsonTemplate() {
51
58
  function readTemplateFile(filename) {
52
59
  return fs.readFileSync(path.join(templateFilesRoot, filename), "utf8");
53
60
  }
61
+ function nextConfigOverride(userConfigImport) {
62
+ if (!userConfigImport) return "const nextConfig = baseNextConfig;";
63
+ return `type SilicaNextConfigOverride =
64
+ | NextConfig
65
+ | ((base: NextConfig) => NextConfig);
66
+
67
+ type SilicaUserConfig = {
68
+ default?: { nextConfig?: SilicaNextConfigOverride };
69
+ nextConfig?: SilicaNextConfigOverride;
70
+ };
71
+
72
+ const silicaUserConfig = loadSilicaUserConfig();
73
+ const silicaNextConfig = silicaUserConfig.nextConfig;
74
+
75
+ const nextConfig =
76
+ typeof silicaNextConfig === "function"
77
+ ? silicaNextConfig(baseNextConfig)
78
+ : mergeNextConfig(baseNextConfig, silicaNextConfig);
79
+
80
+ function loadSilicaUserConfig(): { nextConfig?: SilicaNextConfigOverride } {
81
+ const jiti = createJiti(import.meta.url, { interopDefault: true });
82
+ const loaded = jiti(${JSON.stringify(userConfigImport)}) as SilicaUserConfig;
83
+ return loaded.default ?? loaded;
84
+ }
85
+
86
+ function mergeNextConfig(
87
+ base: NextConfig,
88
+ override: NextConfig | undefined,
89
+ ): NextConfig {
90
+ if (!override) return base;
91
+ return deepMerge(
92
+ base as Record<string, unknown>,
93
+ override as Record<string, unknown>,
94
+ ) as NextConfig;
95
+ }
96
+
97
+ function deepMerge(
98
+ base: Record<string, unknown>,
99
+ override: Record<string, unknown>,
100
+ ): Record<string, unknown> {
101
+ const merged = { ...base };
102
+ for (const [key, value] of Object.entries(override)) {
103
+ const baseValue = merged[key];
104
+ merged[key] =
105
+ isPlainObject(baseValue) && isPlainObject(value)
106
+ ? deepMerge(baseValue, value)
107
+ : value;
108
+ }
109
+ return merged;
110
+ }
111
+
112
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
113
+ return (
114
+ typeof value === "object" &&
115
+ value !== null &&
116
+ !Array.isArray(value)
117
+ );
118
+ }`;
119
+ }
54
120
  function resolveThemeSpecifier(themeValue) {
55
121
  const themeName = typeof themeValue === "object" && themeValue !== null && "name" in themeValue ? String(themeValue.name ?? "default") : typeof themeValue === "string" ? themeValue : "default";
56
122
  if (!themeName || themeName === "default") return "@silicajs/theme-amethyst";
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/templates.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ResolvedSilicaConfig } from \"@silicajs/core/runtime\";\n\nexport type TemplateFile = {\n path: string;\n content: string;\n};\n\nconst templateFilesRoot = fileURLToPath(\n new URL(\"./template-files\", import.meta.url),\n);\n\nexport function getSilicaTemplates(): TemplateFile[] {\n return readTemplateDirectory(path.join(templateFilesRoot, \"generated-app\"));\n}\n\nexport function nextConfigTemplate(): string {\n return readTemplateFile(\"next.config.ts\");\n}\n\nexport function themeModuleTemplate(themeValue: unknown): string {\n return readTemplateFile(\"silica-theme.ts\").replace(\n '\"{{themeSpecifier}}\"',\n JSON.stringify(resolveThemeSpecifier(themeValue)),\n );\n}\n\nexport function proxyTemplate(config: ResolvedSilicaConfig): string {\n return `import type { NextRequest } from \"next/server\";\nimport { silicaProxy } from \"@silicajs/next/proxy\";\n\nconst silicaProxyConfig = ${JSON.stringify(\n {\n authEnabled: Boolean(config.auth),\n allowedDomains: config.auth?.allowedDomains ?? [],\n allowedEmails: config.auth?.allowedEmails ?? [],\n publicPaths: config.logo ? [config.logo] : [],\n },\n null,\n 2,\n )} as const;\n\nexport function proxy(request: NextRequest) {\n return silicaProxy(request, silicaProxyConfig);\n}\n\nexport const config = {\n matcher: [\"/((?!_next/static|_next/image|favicon.ico).*)\"],\n};\n`;\n}\n\nexport function tsconfigTemplate(hasUserTsconfig: boolean): string {\n const template = readTemplateFile(\"tsconfig.json\");\n const rendered = hasUserTsconfig\n ? template.replaceAll(\"{{extends}}\", \"../../tsconfig.json\")\n : template.replace(' \"extends\": \"{{extends}}\",\\n', \"\");\n return rendered.trimEnd();\n}\n\nexport function packageJsonTemplate(): string {\n return readTemplateFile(\"package.json\");\n}\n\nfunction readTemplateFile(filename: string): string {\n return fs.readFileSync(path.join(templateFilesRoot, filename), \"utf8\");\n}\n\nfunction resolveThemeSpecifier(themeValue: unknown): string {\n const themeName =\n typeof themeValue === \"object\" &&\n themeValue !== null &&\n \"name\" in themeValue\n ? String((themeValue as { name?: string }).name ?? \"default\")\n : typeof themeValue === \"string\"\n ? themeValue\n : \"default\";\n\n if (!themeName || themeName === \"default\") return \"@silicajs/theme-amethyst\";\n if (themeName.startsWith(\".\"))\n return `../../${themeName.replace(/^\\.\\//, \"\")}`;\n return themeName;\n}\n\nfunction readTemplateDirectory(root: string, current = root): TemplateFile[] {\n const entries = fs\n .readdirSync(current, { withFileTypes: true })\n .sort((left, right) => left.name.localeCompare(right.name));\n\n return entries.flatMap((entry) => {\n const absolutePath = path.join(current, entry.name);\n if (entry.isDirectory()) return readTemplateDirectory(root, absolutePath);\n if (!entry.isFile()) return [];\n\n return {\n path: path.relative(root, absolutePath).split(path.sep).join(\"/\"),\n content: fs.readFileSync(absolutePath, \"utf8\"),\n };\n });\n}\n"],"mappings":"AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAQ9B,MAAM,oBAAoB;AAAA,EACxB,IAAI,IAAI,oBAAoB,YAAY,GAAG;AAC7C;AAEO,SAAS,qBAAqC;AACnD,SAAO,sBAAsB,KAAK,KAAK,mBAAmB,eAAe,CAAC;AAC5E;AAEO,SAAS,qBAA6B;AAC3C,SAAO,iBAAiB,gBAAgB;AAC1C;AAEO,SAAS,oBAAoB,YAA6B;AAC/D,SAAO,iBAAiB,iBAAiB,EAAE;AAAA,IACzC;AAAA,IACA,KAAK,UAAU,sBAAsB,UAAU,CAAC;AAAA,EAClD;AACF;AAEO,SAAS,cAAc,QAAsC;AAClE,SAAO;AAAA;AAAA;AAAA,4BAGmB,KAAK;AAAA,IAC7B;AAAA,MACE,aAAa,QAAQ,OAAO,IAAI;AAAA,MAChC,gBAAgB,OAAO,MAAM,kBAAkB,CAAC;AAAA,MAChD,eAAe,OAAO,MAAM,iBAAiB,CAAC;AAAA,MAC9C,aAAa,OAAO,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;AAAA,IAC9C;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUH;AAEO,SAAS,iBAAiB,iBAAkC;AACjE,QAAM,WAAW,iBAAiB,eAAe;AACjD,QAAM,WAAW,kBACb,SAAS,WAAW,eAAe,qBAAqB,IACxD,SAAS,QAAQ,iCAAiC,EAAE;AACxD,SAAO,SAAS,QAAQ;AAC1B;AAEO,SAAS,sBAA8B;AAC5C,SAAO,iBAAiB,cAAc;AACxC;AAEA,SAAS,iBAAiB,UAA0B;AAClD,SAAO,GAAG,aAAa,KAAK,KAAK,mBAAmB,QAAQ,GAAG,MAAM;AACvE;AAEA,SAAS,sBAAsB,YAA6B;AAC1D,QAAM,YACJ,OAAO,eAAe,YACtB,eAAe,QACf,UAAU,aACN,OAAQ,WAAiC,QAAQ,SAAS,IAC1D,OAAO,eAAe,WACpB,aACA;AAER,MAAI,CAAC,aAAa,cAAc,UAAW,QAAO;AAClD,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO,SAAS,UAAU,QAAQ,SAAS,EAAE,CAAC;AAChD,SAAO;AACT;AAEA,SAAS,sBAAsB,MAAc,UAAU,MAAsB;AAC3E,QAAM,UAAU,GACb,YAAY,SAAS,EAAE,eAAe,KAAK,CAAC,EAC5C,KAAK,CAAC,MAAM,UAAU,KAAK,KAAK,cAAc,MAAM,IAAI,CAAC;AAE5D,SAAO,QAAQ,QAAQ,CAAC,UAAU;AAChC,UAAM,eAAe,KAAK,KAAK,SAAS,MAAM,IAAI;AAClD,QAAI,MAAM,YAAY,EAAG,QAAO,sBAAsB,MAAM,YAAY;AACxE,QAAI,CAAC,MAAM,OAAO,EAAG,QAAO,CAAC;AAE7B,WAAO;AAAA,MACL,MAAM,KAAK,SAAS,MAAM,YAAY,EAAE,MAAM,KAAK,GAAG,EAAE,KAAK,GAAG;AAAA,MAChE,SAAS,GAAG,aAAa,cAAc,MAAM;AAAA,IAC/C;AAAA,EACF,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../src/templates.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ResolvedSilicaConfig } from \"@silicajs/core/runtime\";\n\nexport type TemplateFile = {\n path: string;\n content: string;\n};\n\nconst templateFilesRoot = fileURLToPath(\n new URL(\"./template-files\", import.meta.url),\n);\n\nexport function getSilicaTemplates(): TemplateFile[] {\n return readTemplateDirectory(path.join(templateFilesRoot, \"generated-app\"));\n}\n\nexport function nextConfigTemplate(userConfigImport?: string): string {\n const template = readTemplateFile(\"next.config.ts\");\n return template\n .replace(\n \"/* __SILICA_CONFIG_IMPORT__ */\",\n userConfigImport ? `import { createJiti } from \"jiti\";` : \"\",\n )\n .replace(\n \"/* __SILICA_CONFIG_OVERRIDE__ */\",\n nextConfigOverride(userConfigImport),\n );\n}\n\nexport function themeModuleTemplate(themeValue: unknown): string {\n return readTemplateFile(\"silica-theme.ts\").replace(\n '\"{{themeSpecifier}}\"',\n JSON.stringify(resolveThemeSpecifier(themeValue)),\n );\n}\n\nexport function proxyTemplate(config: ResolvedSilicaConfig): string {\n return `import type { NextRequest } from \"next/server\";\nimport { silicaProxy } from \"@silicajs/next/proxy\";\n\nconst silicaProxyConfig = ${JSON.stringify(\n {\n authEnabled: Boolean(config.auth),\n allowedDomains: config.auth?.allowedDomains ?? [],\n allowedEmails: config.auth?.allowedEmails ?? [],\n publicPaths: config.logo ? [config.logo] : [],\n },\n null,\n 2,\n )} as const;\n\nexport function proxy(request: NextRequest) {\n return silicaProxy(request, silicaProxyConfig);\n}\n\nexport const config = {\n matcher: [\"/((?!_next/static|_next/image|favicon.ico).*)\"],\n};\n`;\n}\n\nexport function tsconfigTemplate(hasUserTsconfig: boolean): string {\n const template = readTemplateFile(\"tsconfig.json\");\n const rendered = hasUserTsconfig\n ? template.replaceAll(\"{{extends}}\", \"../../tsconfig.json\")\n : template.replace(' \"extends\": \"{{extends}}\",\\n', \"\");\n return rendered.trimEnd();\n}\n\nexport function packageJsonTemplate(): string {\n return readTemplateFile(\"package.json\");\n}\n\nfunction readTemplateFile(filename: string): string {\n return fs.readFileSync(path.join(templateFilesRoot, filename), \"utf8\");\n}\n\nfunction nextConfigOverride(userConfigImport: string | undefined): string {\n if (!userConfigImport) return \"const nextConfig = baseNextConfig;\";\n\n return `type SilicaNextConfigOverride =\n | NextConfig\n | ((base: NextConfig) => NextConfig);\n\ntype SilicaUserConfig = {\n default?: { nextConfig?: SilicaNextConfigOverride };\n nextConfig?: SilicaNextConfigOverride;\n};\n\nconst silicaUserConfig = loadSilicaUserConfig();\nconst silicaNextConfig = silicaUserConfig.nextConfig;\n\nconst nextConfig =\n typeof silicaNextConfig === \"function\"\n ? silicaNextConfig(baseNextConfig)\n : mergeNextConfig(baseNextConfig, silicaNextConfig);\n\nfunction loadSilicaUserConfig(): { nextConfig?: SilicaNextConfigOverride } {\n const jiti = createJiti(import.meta.url, { interopDefault: true });\n const loaded = jiti(${JSON.stringify(userConfigImport)}) as SilicaUserConfig;\n return loaded.default ?? loaded;\n}\n\nfunction mergeNextConfig(\n base: NextConfig,\n override: NextConfig | undefined,\n): NextConfig {\n if (!override) return base;\n return deepMerge(\n base as Record<string, unknown>,\n override as Record<string, unknown>,\n ) as NextConfig;\n}\n\nfunction deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>,\n): Record<string, unknown> {\n const merged = { ...base };\n for (const [key, value] of Object.entries(override)) {\n const baseValue = merged[key];\n merged[key] =\n isPlainObject(baseValue) && isPlainObject(value)\n ? deepMerge(baseValue, value)\n : value;\n }\n return merged;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value)\n );\n}`;\n}\n\nfunction resolveThemeSpecifier(themeValue: unknown): string {\n const themeName =\n typeof themeValue === \"object\" &&\n themeValue !== null &&\n \"name\" in themeValue\n ? String((themeValue as { name?: string }).name ?? \"default\")\n : typeof themeValue === \"string\"\n ? themeValue\n : \"default\";\n\n if (!themeName || themeName === \"default\") return \"@silicajs/theme-amethyst\";\n if (themeName.startsWith(\".\"))\n return `../../${themeName.replace(/^\\.\\//, \"\")}`;\n return themeName;\n}\n\nfunction readTemplateDirectory(root: string, current = root): TemplateFile[] {\n const entries = fs\n .readdirSync(current, { withFileTypes: true })\n .sort((left, right) => left.name.localeCompare(right.name));\n\n return entries.flatMap((entry) => {\n const absolutePath = path.join(current, entry.name);\n if (entry.isDirectory()) return readTemplateDirectory(root, absolutePath);\n if (!entry.isFile()) return [];\n\n return {\n path: path.relative(root, absolutePath).split(path.sep).join(\"/\"),\n content: fs.readFileSync(absolutePath, \"utf8\"),\n };\n });\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silicajs/next",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Next.js runtime, routes, templates, and proxy for Silica.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -88,11 +88,12 @@
88
88
  },
89
89
  "dependencies": {
90
90
  "@silicajs/auth": "^0.1.1",
91
- "@silicajs/components": "^0.2.0",
92
- "@silicajs/core": "^0.2.0",
91
+ "@silicajs/components": "^0.2.2",
92
+ "@silicajs/core": "^0.4.0",
93
93
  "@silicajs/remark-obsidian": "^0.1.0",
94
94
  "@silicajs/search": "^0.2.0",
95
95
  "better-auth": "1.6.11",
96
+ "jiti": "^2.7.0",
96
97
  "katex": "^0.17.0"
97
98
  },
98
99
  "peerDependencies": {