@silicajs/next 0.5.0 → 0.6.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/dist/cache-handlers/filesystem.js +12 -7
- package/dist/cache-handlers/filesystem.js.map +1 -1
- package/dist/generated-app-package-manifest.d.ts +7 -0
- package/dist/generated-app-package-manifest.js +28 -0
- package/dist/generated-app-package-manifest.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/routes/layout.d.ts +12 -0
- package/dist/routes/layout.js +21 -1
- package/dist/routes/layout.js.map +1 -1
- package/dist/routes/opengraph-image-format.d.ts +10 -0
- package/dist/routes/opengraph-image-format.js +28 -0
- package/dist/routes/opengraph-image-format.js.map +1 -0
- package/dist/routes/opengraph-image.d.ts +14 -0
- package/dist/routes/opengraph-image.js +171 -0
- package/dist/routes/opengraph-image.js.map +1 -0
- package/dist/routes/page.d.ts +20 -0
- package/dist/routes/page.js +29 -11
- package/dist/routes/page.js.map +1 -1
- package/dist/runtime-paths.d.ts +10 -0
- package/dist/runtime-paths.js +98 -0
- package/dist/runtime-paths.js.map +1 -0
- package/dist/server-data.js +8 -8
- package/dist/server-data.js.map +1 -1
- package/dist/template-files/generated-app/app/api/silica/og/[[...slug]]/route.ts +1 -0
- package/dist/template-files/next.config.ts +35 -9
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +20 -3
- package/dist/templates.js.map +1 -1
- package/package.json +20 -9
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { ReadableStream } from "node:stream/web";
|
|
4
4
|
import Database from "better-sqlite3";
|
|
5
5
|
import fs from "fs-extra";
|
|
6
|
+
import { tryResolveDataRoot } from "../runtime-paths.js";
|
|
6
7
|
function createFilesystemCacheHandler(options = {}) {
|
|
7
8
|
const root = options.root ?? resolveCacheRoot();
|
|
8
9
|
const entriesRoot = path.join(root, "entries");
|
|
@@ -79,16 +80,17 @@ async function ensureCacheRoot(entriesRoot) {
|
|
|
79
80
|
}
|
|
80
81
|
function resolveCacheRoot() {
|
|
81
82
|
if (process.env.SILICA_CACHE_DIR) return process.env.SILICA_CACHE_DIR;
|
|
82
|
-
const
|
|
83
|
-
const
|
|
83
|
+
const dataRoot = tryResolveDataRoot();
|
|
84
|
+
const appRoot = dataRoot ? path.dirname(dataRoot) : process.cwd();
|
|
85
|
+
const config = dataRoot ? readConfigFromVaultDb(dataRoot) : void 0;
|
|
84
86
|
const configured = config?.render?.cache?.directory;
|
|
85
87
|
if (configured) {
|
|
86
|
-
return path.isAbsolute(configured) ? configured : path.join(
|
|
88
|
+
return path.isAbsolute(configured) ? configured : path.join(appRoot, configured);
|
|
87
89
|
}
|
|
88
|
-
return path.join(
|
|
90
|
+
return path.join(appRoot, "data/cache/next");
|
|
89
91
|
}
|
|
90
|
-
function readConfigFromVaultDb(
|
|
91
|
-
const databasePath = path.join(
|
|
92
|
+
function readConfigFromVaultDb(dataRoot) {
|
|
93
|
+
const databasePath = path.join(dataRoot, "vault.db");
|
|
92
94
|
if (!fs.existsSync(databasePath)) return;
|
|
93
95
|
const db = new Database(databasePath, {
|
|
94
96
|
fileMustExist: true,
|
|
@@ -103,9 +105,12 @@ function readConfigFromVaultDb(projectRoot) {
|
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
function getEntryPath(entriesRoot, cacheKey) {
|
|
106
|
-
const digest =
|
|
108
|
+
const digest = getCacheKeyDigest(cacheKey);
|
|
107
109
|
return path.join(entriesRoot, digest.slice(0, 2), `${digest}.json`);
|
|
108
110
|
}
|
|
111
|
+
function getCacheKeyDigest(cacheKey) {
|
|
112
|
+
return crypto.createHash("sha256").update(cacheKey).digest("hex");
|
|
113
|
+
}
|
|
109
114
|
async function readStoredEntry(filePath) {
|
|
110
115
|
try {
|
|
111
116
|
return await fs.readJson(filePath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cache-handlers/filesystem.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport path from \"node:path\";\nimport { ReadableStream } from \"node:stream/web\";\nimport Database from \"better-sqlite3\";\nimport fs from \"fs-extra\";\n\nexport type CacheEntry = {\n value: ReadableStream<Uint8Array>;\n tags: string[];\n stale: number;\n timestamp: number;\n expire: number;\n revalidate: number;\n};\n\ntype StoredCacheEntry = Omit<CacheEntry, \"value\"> & {\n value: string;\n};\n\ntype TagState = {\n version: 1;\n tags: Record<string, number>;\n};\n\nexport type FilesystemCacheHandlerOptions = {\n root?: string;\n};\n\nexport function createFilesystemCacheHandler(\n options: FilesystemCacheHandlerOptions = {},\n) {\n const root = options.root ?? resolveCacheRoot();\n const entriesRoot = path.join(root, \"entries\");\n const tagsPath = path.join(root, \"tags.json\");\n let tagState: TagState | undefined;\n\n return {\n async get(\n cacheKey: string,\n softTags: string[] = [],\n ): Promise<CacheEntry | undefined> {\n await ensureCacheRoot(entriesRoot);\n await loadTagState();\n const stored = await readStoredEntry(getEntryPath(entriesRoot, cacheKey));\n if (!stored) return undefined;\n const now = Date.now();\n if (\n Number.isFinite(stored.expire) &&\n stored.expire > 0 &&\n stored.timestamp + stored.expire * 1000 <= now\n ) {\n return undefined;\n }\n const expiration = getExpirationFromState([\n ...(stored.tags ?? []),\n ...softTags,\n ]);\n if (expiration > stored.timestamp) return undefined;\n return {\n ...stored,\n value: streamFromBuffer(Buffer.from(stored.value, \"base64\")),\n };\n },\n\n async set(\n cacheKey: string,\n pendingEntry: Promise<CacheEntry>,\n ): Promise<void> {\n await ensureCacheRoot(entriesRoot);\n const entry = await pendingEntry;\n const [storedStream, returnedStream] = entry.value.tee();\n entry.value = returnedStream;\n const stored: StoredCacheEntry = {\n ...entry,\n value: (await bufferFromStream(storedStream)).toString(\"base64\"),\n };\n const destination = getEntryPath(entriesRoot, cacheKey);\n await fs.ensureDir(path.dirname(destination));\n await writeJsonAtomic(destination, stored);\n },\n\n async refreshTags(): Promise<void> {\n await loadTagState();\n },\n\n async getExpiration(tags: string[]): Promise<number> {\n await loadTagState();\n return getExpirationFromState(tags);\n },\n\n async updateTags(\n tags: string[],\n durations?: { expire?: number },\n ): Promise<void> {\n await ensureCacheRoot(entriesRoot);\n await loadTagState();\n const now = Date.now();\n const uniqueTags = [...new Set(tags)];\n tagState ??= { version: 1, tags: {} };\n for (const tag of uniqueTags) {\n tagState.tags[tag] = now;\n }\n await writeJsonAtomic(tagsPath, tagState);\n if (durations?.expire === 0) {\n await deleteEntriesWithTags(entriesRoot, uniqueTags);\n }\n },\n };\n\n async function loadTagState(): Promise<void> {\n tagState = ((await fs.readJson(tagsPath).catch(() => undefined)) ?? {\n version: 1,\n tags: {},\n }) as TagState;\n }\n\n function getExpirationFromState(tags: string[]): number {\n const state = tagState ?? { version: 1, tags: {} };\n return Math.max(0, ...tags.map((tag) => state.tags[tag] ?? 0));\n }\n}\n\nasync function ensureCacheRoot(entriesRoot: string): Promise<void> {\n await fs.ensureDir(entriesRoot);\n}\n\nfunction resolveCacheRoot(): string {\n if (process.env.SILICA_CACHE_DIR) return process.env.SILICA_CACHE_DIR;\n const projectRoot = process.env.SILICA_PROJECT_ROOT ?? process.cwd();\n const config = readConfigFromVaultDb(projectRoot);\n const configured = config?.render?.cache?.directory;\n if (configured) {\n return path.isAbsolute(configured)\n ? configured\n : path.join(projectRoot, configured);\n }\n return path.join(projectRoot, \".silica/cache/next\");\n}\n\nfunction readConfigFromVaultDb(projectRoot: string):\n | {\n render?: { cache?: { directory?: string } };\n }\n | undefined {\n const databasePath = path.join(projectRoot, \".silica/vault.db\");\n if (!fs.existsSync(databasePath)) return;\n const db = new Database(databasePath, {\n fileMustExist: true,\n readonly: true,\n });\n try {\n db.pragma(\"query_only = ON\");\n const row = db\n .prepare(\"SELECT value FROM vault_metadata WHERE key = 'configJson'\")\n .get() as { value: string } | undefined;\n return row ? JSON.parse(row.value) : undefined;\n } finally {\n db.close();\n }\n}\n\nfunction getEntryPath(entriesRoot: string, cacheKey: string): string {\n const digest = crypto.createHash(\"sha256\").update(cacheKey).digest(\"hex\");\n return path.join(entriesRoot, digest.slice(0, 2), `${digest}.json`);\n}\n\nasync function readStoredEntry(\n filePath: string,\n): Promise<StoredCacheEntry | undefined> {\n try {\n return (await fs.readJson(filePath)) as StoredCacheEntry;\n } catch {\n return undefined;\n }\n}\n\nasync function writeJsonAtomic(\n filePath: string,\n value: unknown,\n): Promise<void> {\n const temporary = `${filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;\n await fs.ensureDir(path.dirname(filePath));\n await fs.writeJson(temporary, value);\n await fs.rename(temporary, filePath);\n}\n\nasync function deleteEntriesWithTags(\n entriesRoot: string,\n tags: string[],\n): Promise<void> {\n if (!(await fs.pathExists(entriesRoot))) return;\n const wanted = new Set(tags);\n for (const filePath of await listJsonFiles(entriesRoot)) {\n const entry = await readStoredEntry(filePath);\n if (entry?.tags?.some((tag) => wanted.has(tag))) {\n await fs.remove(filePath);\n }\n }\n}\n\nasync function listJsonFiles(root: string): Promise<string[]> {\n const entries = await fs.readdir(root, { withFileTypes: true });\n const files: string[] = [];\n for (const entry of entries) {\n const absolutePath = path.join(root, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await listJsonFiles(absolutePath)));\n } else if (entry.isFile() && entry.name.endsWith(\".json\")) {\n files.push(absolutePath);\n }\n }\n return files;\n}\n\nasync function bufferFromStream(\n stream: ReadableStream<Uint8Array>,\n): Promise<Buffer> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n return Buffer.concat(chunks);\n}\n\nfunction streamFromBuffer(buffer: Buffer): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(buffer);\n controller.close();\n },\n });\n}\n\nexport default createFilesystemCacheHandler();\n"],"mappings":"AAAA,OAAO,YAAY;AACnB,OAAO,UAAU;AACjB,SAAS,sBAAsB;AAC/B,OAAO,cAAc;AACrB,OAAO,QAAQ;AAwBR,SAAS,6BACd,UAAyC,CAAC,GAC1C;AACA,QAAM,OAAO,QAAQ,QAAQ,iBAAiB;AAC9C,QAAM,cAAc,KAAK,KAAK,MAAM,SAAS;AAC7C,QAAM,WAAW,KAAK,KAAK,MAAM,WAAW;AAC5C,MAAI;AAEJ,SAAO;AAAA,IACL,MAAM,IACJ,UACA,WAAqB,CAAC,GACW;AACjC,YAAM,gBAAgB,WAAW;AACjC,YAAM,aAAa;AACnB,YAAM,SAAS,MAAM,gBAAgB,aAAa,aAAa,QAAQ,CAAC;AACxE,UAAI,CAAC,OAAQ,QAAO;AACpB,YAAM,MAAM,KAAK,IAAI;AACrB,UACE,OAAO,SAAS,OAAO,MAAM,KAC7B,OAAO,SAAS,KAChB,OAAO,YAAY,OAAO,SAAS,OAAQ,KAC3C;AACA,eAAO;AAAA,MACT;AACA,YAAM,aAAa,uBAAuB;AAAA,QACxC,GAAI,OAAO,QAAQ,CAAC;AAAA,QACpB,GAAG;AAAA,MACL,CAAC;AACD,UAAI,aAAa,OAAO,UAAW,QAAO;AAC1C,aAAO;AAAA,QACL,GAAG;AAAA,QACH,OAAO,iBAAiB,OAAO,KAAK,OAAO,OAAO,QAAQ,CAAC;AAAA,MAC7D;AAAA,IACF;AAAA,IAEA,MAAM,IACJ,UACA,cACe;AACf,YAAM,gBAAgB,WAAW;AACjC,YAAM,QAAQ,MAAM;AACpB,YAAM,CAAC,cAAc,cAAc,IAAI,MAAM,MAAM,IAAI;AACvD,YAAM,QAAQ;AACd,YAAM,SAA2B;AAAA,QAC/B,GAAG;AAAA,QACH,QAAQ,MAAM,iBAAiB,YAAY,GAAG,SAAS,QAAQ;AAAA,MACjE;AACA,YAAM,cAAc,aAAa,aAAa,QAAQ;AACtD,YAAM,GAAG,UAAU,KAAK,QAAQ,WAAW,CAAC;AAC5C,YAAM,gBAAgB,aAAa,MAAM;AAAA,IAC3C;AAAA,IAEA,MAAM,cAA6B;AACjC,YAAM,aAAa;AAAA,IACrB;AAAA,IAEA,MAAM,cAAc,MAAiC;AACnD,YAAM,aAAa;AACnB,aAAO,uBAAuB,IAAI;AAAA,IACpC;AAAA,IAEA,MAAM,WACJ,MACA,WACe;AACf,YAAM,gBAAgB,WAAW;AACjC,YAAM,aAAa;AACnB,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,aAAa,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC;AACpC,mBAAa,EAAE,SAAS,GAAG,MAAM,CAAC,EAAE;AACpC,iBAAW,OAAO,YAAY;AAC5B,iBAAS,KAAK,GAAG,IAAI;AAAA,MACvB;AACA,YAAM,gBAAgB,UAAU,QAAQ;AACxC,UAAI,WAAW,WAAW,GAAG;AAC3B,cAAM,sBAAsB,aAAa,UAAU;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAEA,iBAAe,eAA8B;AAC3C,eAAa,MAAM,GAAG,SAAS,QAAQ,EAAE,MAAM,MAAM,MAAS,KAAM;AAAA,MAClE,SAAS;AAAA,MACT,MAAM,CAAC;AAAA,IACT;AAAA,EACF;AAEA,WAAS,uBAAuB,MAAwB;AACtD,UAAM,QAAQ,YAAY,EAAE,SAAS,GAAG,MAAM,CAAC,EAAE;AACjD,WAAO,KAAK,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC,QAAQ,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC;AAAA,EAC/D;AACF;AAEA,eAAe,gBAAgB,aAAoC;AACjE,QAAM,GAAG,UAAU,WAAW;AAChC;AAEA,SAAS,mBAA2B;AAClC,MAAI,QAAQ,IAAI,iBAAkB,QAAO,QAAQ,IAAI;AACrD,QAAM,cAAc,QAAQ,IAAI,uBAAuB,QAAQ,IAAI;AACnE,QAAM,SAAS,sBAAsB,WAAW;AAChD,QAAM,aAAa,QAAQ,QAAQ,OAAO;AAC1C,MAAI,YAAY;AACd,WAAO,KAAK,WAAW,UAAU,IAC7B,aACA,KAAK,KAAK,aAAa,UAAU;AAAA,EACvC;AACA,SAAO,KAAK,KAAK,aAAa,oBAAoB;AACpD;AAEA,SAAS,sBAAsB,aAIjB;AACZ,QAAM,eAAe,KAAK,KAAK,aAAa,kBAAkB;AAC9D,MAAI,CAAC,GAAG,WAAW,YAAY,EAAG;AAClC,QAAM,KAAK,IAAI,SAAS,cAAc;AAAA,IACpC,eAAe;AAAA,IACf,UAAU;AAAA,EACZ,CAAC;AACD,MAAI;AACF,OAAG,OAAO,iBAAiB;AAC3B,UAAM,MAAM,GACT,QAAQ,2DAA2D,EACnE,IAAI;AACP,WAAO,MAAM,KAAK,MAAM,IAAI,KAAK,IAAI;AAAA,EACvC,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,SAAS,aAAa,aAAqB,UAA0B;AACnE,QAAM,SAAS,OAAO,WAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAK;AACxE,SAAO,KAAK,KAAK,aAAa,OAAO,MAAM,GAAG,CAAC,GAAG,GAAG,MAAM,OAAO;AACpE;AAEA,eAAe,gBACb,UACuC;AACvC,MAAI;AACF,WAAQ,MAAM,GAAG,SAAS,QAAQ;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,gBACb,UACA,OACe;AACf,QAAM,YAAY,GAAG,QAAQ,IAAI,QAAQ,GAAG,IAAI,OAAO,WAAW,CAAC;AACnE,QAAM,GAAG,UAAU,KAAK,QAAQ,QAAQ,CAAC;AACzC,QAAM,GAAG,UAAU,WAAW,KAAK;AACnC,QAAM,GAAG,OAAO,WAAW,QAAQ;AACrC;AAEA,eAAe,sBACb,aACA,MACe;AACf,MAAI,CAAE,MAAM,GAAG,WAAW,WAAW,EAAI;AACzC,QAAM,SAAS,IAAI,IAAI,IAAI;AAC3B,aAAW,YAAY,MAAM,cAAc,WAAW,GAAG;AACvD,UAAM,QAAQ,MAAM,gBAAgB,QAAQ;AAC5C,QAAI,OAAO,MAAM,KAAK,CAAC,QAAQ,OAAO,IAAI,GAAG,CAAC,GAAG;AAC/C,YAAM,GAAG,OAAO,QAAQ;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,eAAe,cAAc,MAAiC;AAC5D,QAAM,UAAU,MAAM,GAAG,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;AAC9D,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,SAAS;AAC3B,UAAM,eAAe,KAAK,KAAK,MAAM,MAAM,IAAI;AAC/C,QAAI,MAAM,YAAY,GAAG;AACvB,YAAM,KAAK,GAAI,MAAM,cAAc,YAAY,CAAE;AAAA,IACnD,WAAW,MAAM,OAAO,KAAK,MAAM,KAAK,SAAS,OAAO,GAAG;AACzD,YAAM,KAAK,YAAY;AAAA,IACzB;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,iBACb,QACiB;AACjB,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,SAAuB,CAAC;AAC9B,aAAS;AACP,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,QAAI,KAAM;AACV,WAAO,KAAK,KAAK;AAAA,EACnB;AACA,SAAO,OAAO,OAAO,MAAM;AAC7B;AAEA,SAAS,iBAAiB,QAA4C;AACpE,SAAO,IAAI,eAA2B;AAAA,IACpC,MAAM,YAAY;AAChB,iBAAW,QAAQ,MAAM;AACzB,iBAAW,MAAM;AAAA,IACnB;AAAA,EACF,CAAC;AACH;AAEA,IAAO,qBAAQ,6BAA6B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/cache-handlers/filesystem.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport path from \"node:path\";\nimport { ReadableStream } from \"node:stream/web\";\nimport Database from \"better-sqlite3\";\nimport fs from \"fs-extra\";\nimport { tryResolveDataRoot } from \"../runtime-paths.js\";\n\nexport type CacheEntry = {\n value: ReadableStream<Uint8Array>;\n tags: string[];\n stale: number;\n timestamp: number;\n expire: number;\n revalidate: number;\n};\n\ntype StoredCacheEntry = Omit<CacheEntry, \"value\"> & {\n value: string;\n};\n\ntype TagState = {\n version: 1;\n tags: Record<string, number>;\n};\n\nexport type FilesystemCacheHandlerOptions = {\n root?: string;\n};\n\nexport function createFilesystemCacheHandler(\n options: FilesystemCacheHandlerOptions = {},\n) {\n const root = options.root ?? resolveCacheRoot();\n const entriesRoot = path.join(root, \"entries\");\n const tagsPath = path.join(root, \"tags.json\");\n let tagState: TagState | undefined;\n\n return {\n async get(\n cacheKey: string,\n softTags: string[] = [],\n ): Promise<CacheEntry | undefined> {\n await ensureCacheRoot(entriesRoot);\n await loadTagState();\n const stored = await readStoredEntry(getEntryPath(entriesRoot, cacheKey));\n if (!stored) return undefined;\n const now = Date.now();\n if (\n Number.isFinite(stored.expire) &&\n stored.expire > 0 &&\n stored.timestamp + stored.expire * 1000 <= now\n ) {\n return undefined;\n }\n const expiration = getExpirationFromState([\n ...(stored.tags ?? []),\n ...softTags,\n ]);\n if (expiration > stored.timestamp) return undefined;\n return {\n ...stored,\n value: streamFromBuffer(Buffer.from(stored.value, \"base64\")),\n };\n },\n\n async set(\n cacheKey: string,\n pendingEntry: Promise<CacheEntry>,\n ): Promise<void> {\n await ensureCacheRoot(entriesRoot);\n const entry = await pendingEntry;\n const [storedStream, returnedStream] = entry.value.tee();\n entry.value = returnedStream;\n const stored: StoredCacheEntry = {\n ...entry,\n value: (await bufferFromStream(storedStream)).toString(\"base64\"),\n };\n const destination = getEntryPath(entriesRoot, cacheKey);\n await fs.ensureDir(path.dirname(destination));\n await writeJsonAtomic(destination, stored);\n },\n\n async refreshTags(): Promise<void> {\n await loadTagState();\n },\n\n async getExpiration(tags: string[]): Promise<number> {\n await loadTagState();\n return getExpirationFromState(tags);\n },\n\n async updateTags(\n tags: string[],\n durations?: { expire?: number },\n ): Promise<void> {\n await ensureCacheRoot(entriesRoot);\n await loadTagState();\n const now = Date.now();\n const uniqueTags = [...new Set(tags)];\n tagState ??= { version: 1, tags: {} };\n for (const tag of uniqueTags) {\n tagState.tags[tag] = now;\n }\n await writeJsonAtomic(tagsPath, tagState);\n if (durations?.expire === 0) {\n await deleteEntriesWithTags(entriesRoot, uniqueTags);\n }\n },\n };\n\n async function loadTagState(): Promise<void> {\n tagState = ((await fs.readJson(tagsPath).catch(() => undefined)) ?? {\n version: 1,\n tags: {},\n }) as TagState;\n }\n\n function getExpirationFromState(tags: string[]): number {\n const state = tagState ?? { version: 1, tags: {} };\n return Math.max(0, ...tags.map((tag) => state.tags[tag] ?? 0));\n }\n}\n\nasync function ensureCacheRoot(entriesRoot: string): Promise<void> {\n await fs.ensureDir(entriesRoot);\n}\n\nfunction resolveCacheRoot(): string {\n if (process.env.SILICA_CACHE_DIR) return process.env.SILICA_CACHE_DIR;\n\n const dataRoot = tryResolveDataRoot();\n const appRoot = dataRoot ? path.dirname(dataRoot) : process.cwd();\n const config = dataRoot ? readConfigFromVaultDb(dataRoot) : undefined;\n const configured = config?.render?.cache?.directory;\n if (configured) {\n return path.isAbsolute(configured)\n ? configured\n : path.join(appRoot, configured);\n }\n return path.join(appRoot, \"data/cache/next\");\n}\n\nfunction readConfigFromVaultDb(dataRoot: string):\n | {\n render?: { cache?: { directory?: string } };\n }\n | undefined {\n const databasePath = path.join(dataRoot, \"vault.db\");\n if (!fs.existsSync(databasePath)) return;\n const db = new Database(databasePath, {\n fileMustExist: true,\n readonly: true,\n });\n try {\n db.pragma(\"query_only = ON\");\n const row = db\n .prepare(\"SELECT value FROM vault_metadata WHERE key = 'configJson'\")\n .get() as { value: string } | undefined;\n return row ? JSON.parse(row.value) : undefined;\n } finally {\n db.close();\n }\n}\n\nfunction getEntryPath(entriesRoot: string, cacheKey: string): string {\n const digest = getCacheKeyDigest(cacheKey);\n return path.join(entriesRoot, digest.slice(0, 2), `${digest}.json`);\n}\n\nfunction getCacheKeyDigest(cacheKey: string): string {\n return crypto.createHash(\"sha256\").update(cacheKey).digest(\"hex\");\n}\n\nasync function readStoredEntry(\n filePath: string,\n): Promise<StoredCacheEntry | undefined> {\n try {\n return (await fs.readJson(filePath)) as StoredCacheEntry;\n } catch {\n return undefined;\n }\n}\n\nasync function writeJsonAtomic(\n filePath: string,\n value: unknown,\n): Promise<void> {\n const temporary = `${filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;\n await fs.ensureDir(path.dirname(filePath));\n await fs.writeJson(temporary, value);\n await fs.rename(temporary, filePath);\n}\n\nasync function deleteEntriesWithTags(\n entriesRoot: string,\n tags: string[],\n): Promise<void> {\n if (!(await fs.pathExists(entriesRoot))) return;\n const wanted = new Set(tags);\n for (const filePath of await listJsonFiles(entriesRoot)) {\n const entry = await readStoredEntry(filePath);\n if (entry?.tags?.some((tag) => wanted.has(tag))) {\n await fs.remove(filePath);\n }\n }\n}\n\nasync function listJsonFiles(root: string): Promise<string[]> {\n const entries = await fs.readdir(root, { withFileTypes: true });\n const files: string[] = [];\n for (const entry of entries) {\n const absolutePath = path.join(root, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await listJsonFiles(absolutePath)));\n } else if (entry.isFile() && entry.name.endsWith(\".json\")) {\n files.push(absolutePath);\n }\n }\n return files;\n}\n\nasync function bufferFromStream(\n stream: ReadableStream<Uint8Array>,\n): Promise<Buffer> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n return Buffer.concat(chunks);\n}\n\nfunction streamFromBuffer(buffer: Buffer): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(buffer);\n controller.close();\n },\n });\n}\n\nexport default createFilesystemCacheHandler();\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type GeneratedAppPackageManifest = {
|
|
2
|
+
dependencies: Record<string, string>;
|
|
3
|
+
devDependencies: Record<string, string>;
|
|
4
|
+
};
|
|
5
|
+
declare const generatedAppPackageManifest: GeneratedAppPackageManifest;
|
|
6
|
+
|
|
7
|
+
export { type GeneratedAppPackageManifest, generatedAppPackageManifest };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const generatedAppPackageManifest = {
|
|
2
|
+
dependencies: {
|
|
3
|
+
"@silicajs/auth": "^0.1.3",
|
|
4
|
+
"@silicajs/components": "^0.4.1",
|
|
5
|
+
"@silicajs/core": "^0.9.0",
|
|
6
|
+
"@silicajs/next": "^0.6.0",
|
|
7
|
+
"@silicajs/remark-obsidian": "^0.1.0",
|
|
8
|
+
"@silicajs/search": "^0.3.2",
|
|
9
|
+
"@silicajs/theme-amethyst": "^0.5.1",
|
|
10
|
+
"@silicajs/ui": "^0.2.1",
|
|
11
|
+
"@tailwindcss/postcss": "^4.3.1",
|
|
12
|
+
"better-sqlite3": "^12.11.1",
|
|
13
|
+
jiti: "^2.7.0",
|
|
14
|
+
next: "^16.2.0",
|
|
15
|
+
react: "^19.2.0",
|
|
16
|
+
"react-dom": "^19.2.0",
|
|
17
|
+
tailwindcss: "^4.1.18"
|
|
18
|
+
},
|
|
19
|
+
devDependencies: {
|
|
20
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
21
|
+
"@types/node": "^25.9.3",
|
|
22
|
+
typescript: "^6.0.3"
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
export {
|
|
26
|
+
generatedAppPackageManifest
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=generated-app-package-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/generated-app-package-manifest.ts"],"sourcesContent":["// This file is generated by scripts/sync-generated-app-manifest.mjs.\n// Run npm run generated-app-manifest:update after generated app dependency changes.\n\nexport type GeneratedAppPackageManifest = {\n dependencies: Record<string, string>;\n devDependencies: Record<string, string>;\n};\n\nexport const generatedAppPackageManifest: GeneratedAppPackageManifest = {\n dependencies: {\n \"@silicajs/auth\": \"^0.1.3\",\n \"@silicajs/components\": \"^0.4.1\",\n \"@silicajs/core\": \"^0.9.0\",\n \"@silicajs/next\": \"^0.6.0\",\n \"@silicajs/remark-obsidian\": \"^0.1.0\",\n \"@silicajs/search\": \"^0.3.2\",\n \"@silicajs/theme-amethyst\": \"^0.5.1\",\n \"@silicajs/ui\": \"^0.2.1\",\n \"@tailwindcss/postcss\": \"^4.3.1\",\n \"better-sqlite3\": \"^12.11.1\",\n jiti: \"^2.7.0\",\n next: \"^16.2.0\",\n react: \"^19.2.0\",\n \"react-dom\": \"^19.2.0\",\n tailwindcss: \"^4.1.18\",\n },\n devDependencies: {\n \"@types/better-sqlite3\": \"^7.6.13\",\n \"@types/node\": \"^25.9.3\",\n typescript: \"^6.0.3\",\n },\n};\n"],"mappings":"AAQO,MAAM,8BAA2D;AAAA,EACtE,cAAc;AAAA,IACZ,kBAAkB;AAAA,IAClB,wBAAwB;AAAA,IACxB,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,IAClB,6BAA6B;AAAA,IAC7B,oBAAoB;AAAA,IACpB,4BAA4B;AAAA,IAC5B,gBAAgB;AAAA,IAChB,wBAAwB;AAAA,IACxB,kBAAkB;AAAA,IAClB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,aAAa;AAAA,EACf;AAAA,EACA,iBAAiB;AAAA,IACf,yBAAyB;AAAA,IACzB,eAAe;AAAA,IACf,YAAY;AAAA,EACd;AACF;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { TemplateFile, assistantModuleTemplate, assistantRouteTemplate, getSilicaTemplates, nextConfigTemplate, packageJsonTemplate, proxyTemplate, themeModuleTemplate, tsconfigTemplate } from './templates.js';
|
|
2
|
+
export { GeneratedAppPackageManifest, generatedAppPackageManifest } from './generated-app-package-manifest.js';
|
|
2
3
|
export { LoadedVaultDb, getAllSlugs, getBacklinks, getBreadcrumbs, getCacheState, getConfig, getEntriesForTag, getNavigation, getPage, getPageBySourcePath, getPageRuntimeData, getPrerenderSlugs, getProjectRoot, getRelatedTagsForEntries, getRenderKey, getSilicaRoot, getTagSlugs, getVaultDatabasePath, loadRenderEnvironmentHash, loadSearchIndex, loadVaultDb, normalizeRouteSlug, resolveAssetFromDb, resolveWikiLinkFromDb } from './server-data.js';
|
|
3
4
|
export { SilicaNextRoutingProvider } from './routing-provider.js';
|
|
4
5
|
import '@silicajs/core/runtime';
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
themeModuleTemplate,
|
|
9
9
|
tsconfigTemplate
|
|
10
10
|
} from "./templates.js";
|
|
11
|
+
import {
|
|
12
|
+
generatedAppPackageManifest
|
|
13
|
+
} from "./generated-app-package-manifest.js";
|
|
11
14
|
import {
|
|
12
15
|
getAllSlugs,
|
|
13
16
|
getBacklinks,
|
|
@@ -38,6 +41,7 @@ export {
|
|
|
38
41
|
SilicaNextRoutingProvider,
|
|
39
42
|
assistantModuleTemplate,
|
|
40
43
|
assistantRouteTemplate,
|
|
44
|
+
generatedAppPackageManifest,
|
|
41
45
|
getAllSlugs,
|
|
42
46
|
getBacklinks,
|
|
43
47
|
getBreadcrumbs,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n assistantModuleTemplate,\n assistantRouteTemplate,\n getSilicaTemplates,\n nextConfigTemplate,\n packageJsonTemplate,\n proxyTemplate,\n themeModuleTemplate,\n tsconfigTemplate,\n type TemplateFile,\n} from \"./templates.js\";\nexport {\n getAllSlugs,\n getBacklinks,\n getBreadcrumbs,\n getCacheState,\n getConfig,\n getEntriesForTag,\n getNavigation,\n getPage,\n getPageBySourcePath,\n getPageRuntimeData,\n getProjectRoot,\n getPrerenderSlugs,\n getRenderKey,\n getRelatedTagsForEntries,\n getSilicaRoot,\n getTagSlugs,\n getVaultDatabasePath,\n loadSearchIndex,\n loadRenderEnvironmentHash,\n loadVaultDb,\n normalizeRouteSlug,\n resolveAssetFromDb,\n resolveWikiLinkFromDb,\n type LoadedVaultDb,\n} from \"./server-data.js\";\nexport { SilicaNextRoutingProvider } from \"./routing-provider.js\";\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,iCAAiC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n assistantModuleTemplate,\n assistantRouteTemplate,\n getSilicaTemplates,\n nextConfigTemplate,\n packageJsonTemplate,\n proxyTemplate,\n themeModuleTemplate,\n tsconfigTemplate,\n type TemplateFile,\n} from \"./templates.js\";\nexport {\n generatedAppPackageManifest,\n type GeneratedAppPackageManifest,\n} from \"./generated-app-package-manifest.js\";\nexport {\n getAllSlugs,\n getBacklinks,\n getBreadcrumbs,\n getCacheState,\n getConfig,\n getEntriesForTag,\n getNavigation,\n getPage,\n getPageBySourcePath,\n getPageRuntimeData,\n getProjectRoot,\n getPrerenderSlugs,\n getRenderKey,\n getRelatedTagsForEntries,\n getSilicaRoot,\n getTagSlugs,\n getVaultDatabasePath,\n loadSearchIndex,\n loadRenderEnvironmentHash,\n loadVaultDb,\n normalizeRouteSlug,\n resolveAssetFromDb,\n resolveWikiLinkFromDb,\n type LoadedVaultDb,\n} from \"./server-data.js\";\nexport { SilicaNextRoutingProvider } from \"./routing-provider.js\";\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,iCAAiC;","names":[]}
|
package/dist/routes/layout.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
declare function generateMetadata(): Promise<{
|
|
2
|
+
metadataBase: URL | undefined;
|
|
2
3
|
title: {
|
|
3
4
|
default: string;
|
|
4
5
|
template: string;
|
|
5
6
|
};
|
|
6
7
|
description: string;
|
|
8
|
+
openGraph: {
|
|
9
|
+
type: string;
|
|
10
|
+
siteName: string;
|
|
11
|
+
title: string;
|
|
12
|
+
description: string;
|
|
13
|
+
};
|
|
14
|
+
twitter: {
|
|
15
|
+
card: string;
|
|
16
|
+
title: string;
|
|
17
|
+
description: string;
|
|
18
|
+
};
|
|
7
19
|
}>;
|
|
8
20
|
declare function getLayoutProps(): Promise<{
|
|
9
21
|
navigationEndpoint: string;
|
package/dist/routes/layout.js
CHANGED
|
@@ -4,13 +4,33 @@ import { getCacheState, getConfig } from "../server-data.js";
|
|
|
4
4
|
async function generateMetadata() {
|
|
5
5
|
const { config } = await getLayoutProps();
|
|
6
6
|
return {
|
|
7
|
+
metadataBase: resolveMetadataBase(config.baseUrl),
|
|
7
8
|
title: {
|
|
8
9
|
default: config.title,
|
|
9
10
|
template: `%s \xB7 ${config.title}`
|
|
10
11
|
},
|
|
11
|
-
description: config.description
|
|
12
|
+
description: config.description,
|
|
13
|
+
openGraph: {
|
|
14
|
+
type: "website",
|
|
15
|
+
siteName: config.title,
|
|
16
|
+
title: config.title,
|
|
17
|
+
description: config.description
|
|
18
|
+
},
|
|
19
|
+
twitter: {
|
|
20
|
+
card: "summary_large_image",
|
|
21
|
+
title: config.title,
|
|
22
|
+
description: config.description
|
|
23
|
+
}
|
|
12
24
|
};
|
|
13
25
|
}
|
|
26
|
+
function resolveMetadataBase(baseUrl) {
|
|
27
|
+
if (!baseUrl) return void 0;
|
|
28
|
+
try {
|
|
29
|
+
return new URL(baseUrl);
|
|
30
|
+
} catch {
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
14
34
|
async function getLayoutProps() {
|
|
15
35
|
const cacheState = getCacheState();
|
|
16
36
|
return getCachedLayoutProps(cacheState.renderEnvironmentHash);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/routes/layout.tsx"],"sourcesContent":["import { cacheLife, cacheTag } from \"next/cache\";\nimport { resolveRuntimeAuthConfig } from \"../auth-config.js\";\nimport { getCacheState, getConfig } from \"../server-data.js\";\n\nexport async function generateMetadata() {\n const { config } = await getLayoutProps();\n return {\n title: {\n default: config.title,\n template: `%s · ${config.title}`,\n },\n description: config.description,\n };\n}\n\nexport async function getLayoutProps() {\n const cacheState = getCacheState();\n return getCachedLayoutProps(cacheState.renderEnvironmentHash);\n}\n\nasync function getCachedLayoutProps(renderEnvironmentHash: string) {\n \"use cache\";\n cacheLife(\"max\");\n cacheTag(`environment:${renderEnvironmentHash}`);\n const config = getConfig();\n const auth = resolveRuntimeAuthConfig(config);\n return {\n navigationEndpoint: `/api/navigation?build=${encodeURIComponent(\n renderEnvironmentHash,\n )}`,\n config: {\n title: config.title,\n description: config.description,\n logo: config.logo,\n baseUrl: config.baseUrl,\n authEnabled: auth.authEnabled,\n assistantEnabled: Boolean(config.assistant),\n },\n };\n}\n"],"mappings":"AAAA,SAAS,WAAW,gBAAgB;AACpC,SAAS,gCAAgC;AACzC,SAAS,eAAe,iBAAiB;AAEzC,eAAsB,mBAAmB;AACvC,QAAM,EAAE,OAAO,IAAI,MAAM,eAAe;AACxC,SAAO;AAAA,IACL,OAAO;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,UAAU,WAAQ,OAAO,KAAK;AAAA,IAChC;AAAA,IACA,aAAa,OAAO;AAAA,
|
|
1
|
+
{"version":3,"sources":["../../src/routes/layout.tsx"],"sourcesContent":["import { cacheLife, cacheTag } from \"next/cache\";\nimport { resolveRuntimeAuthConfig } from \"../auth-config.js\";\nimport { getCacheState, getConfig } from \"../server-data.js\";\n\nexport async function generateMetadata() {\n const { config } = await getLayoutProps();\n return {\n metadataBase: resolveMetadataBase(config.baseUrl),\n title: {\n default: config.title,\n template: `%s · ${config.title}`,\n },\n description: config.description,\n openGraph: {\n type: \"website\",\n siteName: config.title,\n title: config.title,\n description: config.description,\n },\n twitter: {\n card: \"summary_large_image\",\n title: config.title,\n description: config.description,\n },\n };\n}\n\nfunction resolveMetadataBase(baseUrl?: string): URL | undefined {\n if (!baseUrl) return undefined;\n try {\n return new URL(baseUrl);\n } catch {\n return undefined;\n }\n}\n\nexport async function getLayoutProps() {\n const cacheState = getCacheState();\n return getCachedLayoutProps(cacheState.renderEnvironmentHash);\n}\n\nasync function getCachedLayoutProps(renderEnvironmentHash: string) {\n \"use cache\";\n cacheLife(\"max\");\n cacheTag(`environment:${renderEnvironmentHash}`);\n const config = getConfig();\n const auth = resolveRuntimeAuthConfig(config);\n return {\n navigationEndpoint: `/api/navigation?build=${encodeURIComponent(\n renderEnvironmentHash,\n )}`,\n config: {\n title: config.title,\n description: config.description,\n logo: config.logo,\n baseUrl: config.baseUrl,\n authEnabled: auth.authEnabled,\n assistantEnabled: Boolean(config.assistant),\n },\n };\n}\n"],"mappings":"AAAA,SAAS,WAAW,gBAAgB;AACpC,SAAS,gCAAgC;AACzC,SAAS,eAAe,iBAAiB;AAEzC,eAAsB,mBAAmB;AACvC,QAAM,EAAE,OAAO,IAAI,MAAM,eAAe;AACxC,SAAO;AAAA,IACL,cAAc,oBAAoB,OAAO,OAAO;AAAA,IAChD,OAAO;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,UAAU,WAAQ,OAAO,KAAK;AAAA,IAChC;AAAA,IACA,aAAa,OAAO;AAAA,IACpB,WAAW;AAAA,MACT,MAAM;AAAA,MACN,UAAU,OAAO;AAAA,MACjB,OAAO,OAAO;AAAA,MACd,aAAa,OAAO;AAAA,IACtB;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,OAAO,OAAO;AAAA,MACd,aAAa,OAAO;AAAA,IACtB;AAAA,EACF;AACF;AAEA,SAAS,oBAAoB,SAAmC;AAC9D,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACF,WAAO,IAAI,IAAI,OAAO;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,iBAAiB;AACrC,QAAM,aAAa,cAAc;AACjC,SAAO,qBAAqB,WAAW,qBAAqB;AAC9D;AAEA,eAAe,qBAAqB,uBAA+B;AACjE;AACA,YAAU,KAAK;AACf,WAAS,eAAe,qBAAqB,EAAE;AAC/C,QAAM,SAAS,UAAU;AACzB,QAAM,OAAO,yBAAyB,MAAM;AAC5C,SAAO;AAAA,IACL,oBAAoB,yBAAyB;AAAA,MAC3C;AAAA,IACF,CAAC;AAAA,IACD,QAAQ;AAAA,MACN,OAAO,OAAO;AAAA,MACd,aAAa,OAAO;AAAA,MACpB,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,MAChB,aAAa,KAAK;AAAA,MAClB,kBAAkB,QAAQ,OAAO,SAAS;AAAA,IAC5C;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public path of the auto-generated Open Graph image for a note slug. Mirrors
|
|
3
|
+
* the `app/api/silica/og/[[...slug]]/route` handler that renders it.
|
|
4
|
+
*/
|
|
5
|
+
declare function opengraphImagePath(slug: string): string;
|
|
6
|
+
declare function titleFontSize(title: string): number;
|
|
7
|
+
declare function clampText(text: string, maxLength: number): string;
|
|
8
|
+
declare function hostnameFromBaseUrl(baseUrl?: string): string | undefined;
|
|
9
|
+
|
|
10
|
+
export { clampText, hostnameFromBaseUrl, opengraphImagePath, titleFontSize };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
function opengraphImagePath(slug) {
|
|
2
|
+
if (!slug || slug === "index") return "/api/silica/og";
|
|
3
|
+
return `/api/silica/og/${slug.split("/").map(encodeURIComponent).join("/")}`;
|
|
4
|
+
}
|
|
5
|
+
function titleFontSize(title) {
|
|
6
|
+
if (title.length <= 28) return 84;
|
|
7
|
+
if (title.length <= 55) return 64;
|
|
8
|
+
return 48;
|
|
9
|
+
}
|
|
10
|
+
function clampText(text, maxLength) {
|
|
11
|
+
if (text.length <= maxLength) return text;
|
|
12
|
+
return `${text.slice(0, maxLength - 1).trimEnd()}\u2026`;
|
|
13
|
+
}
|
|
14
|
+
function hostnameFromBaseUrl(baseUrl) {
|
|
15
|
+
if (!baseUrl) return void 0;
|
|
16
|
+
try {
|
|
17
|
+
return new URL(baseUrl).hostname;
|
|
18
|
+
} catch {
|
|
19
|
+
return baseUrl.replace(/^https?:\/\//, "").replace(/\/+$/, "") || void 0;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
clampText,
|
|
24
|
+
hostnameFromBaseUrl,
|
|
25
|
+
opengraphImagePath,
|
|
26
|
+
titleFontSize
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=opengraph-image-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/opengraph-image-format.ts"],"sourcesContent":["/**\n * Public path of the auto-generated Open Graph image for a note slug. Mirrors\n * the `app/api/silica/og/[[...slug]]/route` handler that renders it.\n */\nexport function opengraphImagePath(slug: string): string {\n if (!slug || slug === \"index\") return \"/api/silica/og\";\n return `/api/silica/og/${slug.split(\"/\").map(encodeURIComponent).join(\"/\")}`;\n}\n\nexport function titleFontSize(title: string): number {\n if (title.length <= 28) return 84;\n if (title.length <= 55) return 64;\n return 48;\n}\n\nexport function clampText(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return `${text.slice(0, maxLength - 1).trimEnd()}…`;\n}\n\nexport function hostnameFromBaseUrl(baseUrl?: string): string | undefined {\n if (!baseUrl) return undefined;\n try {\n return new URL(baseUrl).hostname;\n } catch {\n return baseUrl.replace(/^https?:\\/\\//, \"\").replace(/\\/+$/, \"\") || undefined;\n }\n}\n"],"mappings":"AAIO,SAAS,mBAAmB,MAAsB;AACvD,MAAI,CAAC,QAAQ,SAAS,QAAS,QAAO;AACtC,SAAO,kBAAkB,KAAK,MAAM,GAAG,EAAE,IAAI,kBAAkB,EAAE,KAAK,GAAG,CAAC;AAC5E;AAEO,SAAS,cAAc,OAAuB;AACnD,MAAI,MAAM,UAAU,GAAI,QAAO;AAC/B,MAAI,MAAM,UAAU,GAAI,QAAO;AAC/B,SAAO;AACT;AAEO,SAAS,UAAU,MAAc,WAA2B;AACjE,MAAI,KAAK,UAAU,UAAW,QAAO;AACrC,SAAO,GAAG,KAAK,MAAM,GAAG,YAAY,CAAC,EAAE,QAAQ,CAAC;AAClD;AAEO,SAAS,oBAAoB,SAAsC;AACxE,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACF,WAAO,IAAI,IAAI,OAAO,EAAE;AAAA,EAC1B,QAAQ;AACN,WAAO,QAAQ,QAAQ,gBAAgB,EAAE,EAAE,QAAQ,QAAQ,EAAE,KAAK;AAAA,EACpE;AACF;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ImageResponse } from 'next/og';
|
|
2
|
+
|
|
3
|
+
declare const OG_IMAGE_WIDTH = 1200;
|
|
4
|
+
declare const OG_IMAGE_HEIGHT = 630;
|
|
5
|
+
type RouteContext = {
|
|
6
|
+
params: Promise<{
|
|
7
|
+
slug?: string[];
|
|
8
|
+
}> | {
|
|
9
|
+
slug?: string[];
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
declare function GET(_request: Request, context: RouteContext): Promise<ImageResponse>;
|
|
13
|
+
|
|
14
|
+
export { GET, OG_IMAGE_HEIGHT, OG_IMAGE_WIDTH };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ImageResponse } from "next/og";
|
|
3
|
+
import { cacheLife, cacheTag } from "next/cache";
|
|
4
|
+
import { getMetaDescription } from "@silicajs/core/runtime";
|
|
5
|
+
import {
|
|
6
|
+
getConfig,
|
|
7
|
+
getPage,
|
|
8
|
+
getRenderKey,
|
|
9
|
+
normalizeRouteSlug
|
|
10
|
+
} from "../server-data.js";
|
|
11
|
+
import {
|
|
12
|
+
clampText,
|
|
13
|
+
hostnameFromBaseUrl,
|
|
14
|
+
titleFontSize
|
|
15
|
+
} from "./opengraph-image-format.js";
|
|
16
|
+
const OG_IMAGE_WIDTH = 1200;
|
|
17
|
+
const OG_IMAGE_HEIGHT = 630;
|
|
18
|
+
const ACCENT = "#8b5cf6";
|
|
19
|
+
const BACKGROUND_TOP = "#1c1330";
|
|
20
|
+
const BACKGROUND_BOTTOM = "#0c0717";
|
|
21
|
+
async function GET(_request, context) {
|
|
22
|
+
const params = await context.params;
|
|
23
|
+
const slug = normalizeRouteSlug(params?.slug);
|
|
24
|
+
const renderKey = getRenderKey(slug);
|
|
25
|
+
const data = await getOpengraphImageData(
|
|
26
|
+
slug,
|
|
27
|
+
renderKey.renderHash,
|
|
28
|
+
renderKey.renderEnvironmentHash
|
|
29
|
+
);
|
|
30
|
+
return new ImageResponse(/* @__PURE__ */ jsx(OpengraphImageCard, { ...data }), {
|
|
31
|
+
width: OG_IMAGE_WIDTH,
|
|
32
|
+
height: OG_IMAGE_HEIGHT
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async function getOpengraphImageData(slug, renderHash, renderEnvironmentHash) {
|
|
36
|
+
"use cache";
|
|
37
|
+
cacheLife("max");
|
|
38
|
+
cacheTag(
|
|
39
|
+
`environment:${renderEnvironmentHash}`,
|
|
40
|
+
`page:${slug}`,
|
|
41
|
+
`render:${renderHash}`
|
|
42
|
+
);
|
|
43
|
+
const config = getConfig();
|
|
44
|
+
const entry = getPage(slug);
|
|
45
|
+
return {
|
|
46
|
+
siteTitle: config.title,
|
|
47
|
+
title: entry?.title ?? config.title,
|
|
48
|
+
description: entry ? getMetaDescription(entry) : config.description,
|
|
49
|
+
tags: entry?.tags?.slice(0, 4) ?? [],
|
|
50
|
+
domain: hostnameFromBaseUrl(config.baseUrl)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function OpengraphImageCard({
|
|
54
|
+
siteTitle,
|
|
55
|
+
title,
|
|
56
|
+
description,
|
|
57
|
+
tags,
|
|
58
|
+
domain
|
|
59
|
+
}) {
|
|
60
|
+
return /* @__PURE__ */ jsxs(
|
|
61
|
+
"div",
|
|
62
|
+
{
|
|
63
|
+
style: {
|
|
64
|
+
display: "flex",
|
|
65
|
+
flexDirection: "column",
|
|
66
|
+
justifyContent: "space-between",
|
|
67
|
+
width: "100%",
|
|
68
|
+
height: "100%",
|
|
69
|
+
padding: "80px",
|
|
70
|
+
color: "#f8fafc",
|
|
71
|
+
backgroundColor: BACKGROUND_BOTTOM,
|
|
72
|
+
backgroundImage: `linear-gradient(135deg, ${BACKGROUND_TOP} 0%, ${BACKGROUND_BOTTOM} 100%)`,
|
|
73
|
+
fontFamily: "sans-serif"
|
|
74
|
+
},
|
|
75
|
+
children: [
|
|
76
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
|
|
77
|
+
/* @__PURE__ */ jsx(
|
|
78
|
+
"div",
|
|
79
|
+
{
|
|
80
|
+
style: {
|
|
81
|
+
width: "20px",
|
|
82
|
+
height: "44px",
|
|
83
|
+
borderRadius: "6px",
|
|
84
|
+
backgroundColor: ACCENT,
|
|
85
|
+
marginRight: "24px"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
),
|
|
89
|
+
/* @__PURE__ */ jsx(
|
|
90
|
+
"div",
|
|
91
|
+
{
|
|
92
|
+
style: {
|
|
93
|
+
fontSize: "34px",
|
|
94
|
+
fontWeight: 600,
|
|
95
|
+
color: "#cbb6ff",
|
|
96
|
+
letterSpacing: "-0.01em"
|
|
97
|
+
},
|
|
98
|
+
children: siteTitle
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
] }),
|
|
102
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column" }, children: [
|
|
103
|
+
/* @__PURE__ */ jsx(
|
|
104
|
+
"div",
|
|
105
|
+
{
|
|
106
|
+
style: {
|
|
107
|
+
display: "flex",
|
|
108
|
+
fontSize: titleFontSize(title),
|
|
109
|
+
fontWeight: 800,
|
|
110
|
+
lineHeight: 1.05,
|
|
111
|
+
letterSpacing: "-0.02em"
|
|
112
|
+
},
|
|
113
|
+
children: clampText(title, 90)
|
|
114
|
+
}
|
|
115
|
+
),
|
|
116
|
+
description ? /* @__PURE__ */ jsx(
|
|
117
|
+
"div",
|
|
118
|
+
{
|
|
119
|
+
style: {
|
|
120
|
+
display: "flex",
|
|
121
|
+
marginTop: "28px",
|
|
122
|
+
fontSize: "32px",
|
|
123
|
+
lineHeight: 1.35,
|
|
124
|
+
color: "#c7c9d9"
|
|
125
|
+
},
|
|
126
|
+
children: clampText(description, 160)
|
|
127
|
+
}
|
|
128
|
+
) : null
|
|
129
|
+
] }),
|
|
130
|
+
/* @__PURE__ */ jsxs(
|
|
131
|
+
"div",
|
|
132
|
+
{
|
|
133
|
+
style: {
|
|
134
|
+
display: "flex",
|
|
135
|
+
alignItems: "center",
|
|
136
|
+
justifyContent: "space-between"
|
|
137
|
+
},
|
|
138
|
+
children: [
|
|
139
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center" }, children: tags.map((tag) => /* @__PURE__ */ jsxs(
|
|
140
|
+
"div",
|
|
141
|
+
{
|
|
142
|
+
style: {
|
|
143
|
+
display: "flex",
|
|
144
|
+
marginRight: "16px",
|
|
145
|
+
padding: "8px 20px",
|
|
146
|
+
borderRadius: "999px",
|
|
147
|
+
border: "1px solid rgba(139, 92, 246, 0.5)",
|
|
148
|
+
color: "#cbb6ff",
|
|
149
|
+
fontSize: "24px"
|
|
150
|
+
},
|
|
151
|
+
children: [
|
|
152
|
+
"#",
|
|
153
|
+
tag
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
tag
|
|
157
|
+
)) }),
|
|
158
|
+
domain ? /* @__PURE__ */ jsx("div", { style: { display: "flex", fontSize: "26px", color: "#8c8fa3" }, children: domain }) : null
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
export {
|
|
167
|
+
GET,
|
|
168
|
+
OG_IMAGE_HEIGHT,
|
|
169
|
+
OG_IMAGE_WIDTH
|
|
170
|
+
};
|
|
171
|
+
//# sourceMappingURL=opengraph-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/routes/opengraph-image.tsx"],"sourcesContent":["import { ImageResponse } from \"next/og\";\nimport { cacheLife, cacheTag } from \"next/cache\";\nimport { getMetaDescription } from \"@silicajs/core/runtime\";\nimport {\n getConfig,\n getPage,\n getRenderKey,\n normalizeRouteSlug,\n} from \"../server-data.js\";\nimport {\n clampText,\n hostnameFromBaseUrl,\n titleFontSize,\n} from \"./opengraph-image-format.js\";\n\nexport const OG_IMAGE_WIDTH = 1200;\nexport const OG_IMAGE_HEIGHT = 630;\n\nconst ACCENT = \"#8b5cf6\";\nconst BACKGROUND_TOP = \"#1c1330\";\nconst BACKGROUND_BOTTOM = \"#0c0717\";\n\ntype RouteContext = {\n params: Promise<{ slug?: string[] }> | { slug?: string[] };\n};\n\ntype OpengraphImageData = {\n siteTitle: string;\n title: string;\n description?: string;\n tags: string[];\n domain?: string;\n};\n\nexport async function GET(_request: Request, context: RouteContext) {\n const params = await context.params;\n const slug = normalizeRouteSlug(params?.slug);\n const renderKey = getRenderKey(slug);\n const data = await getOpengraphImageData(\n slug,\n renderKey.renderHash,\n renderKey.renderEnvironmentHash,\n );\n\n return new ImageResponse(<OpengraphImageCard {...data} />, {\n width: OG_IMAGE_WIDTH,\n height: OG_IMAGE_HEIGHT,\n });\n}\n\nasync function getOpengraphImageData(\n slug: string,\n renderHash: string,\n renderEnvironmentHash: string,\n): Promise<OpengraphImageData> {\n \"use cache\";\n cacheLife(\"max\");\n cacheTag(\n `environment:${renderEnvironmentHash}`,\n `page:${slug}`,\n `render:${renderHash}`,\n );\n const config = getConfig();\n const entry = getPage(slug);\n return {\n siteTitle: config.title,\n title: entry?.title ?? config.title,\n description: entry ? getMetaDescription(entry) : config.description,\n tags: entry?.tags?.slice(0, 4) ?? [],\n domain: hostnameFromBaseUrl(config.baseUrl),\n };\n}\n\nfunction OpengraphImageCard({\n siteTitle,\n title,\n description,\n tags,\n domain,\n}: OpengraphImageData) {\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"space-between\",\n width: \"100%\",\n height: \"100%\",\n padding: \"80px\",\n color: \"#f8fafc\",\n backgroundColor: BACKGROUND_BOTTOM,\n backgroundImage: `linear-gradient(135deg, ${BACKGROUND_TOP} 0%, ${BACKGROUND_BOTTOM} 100%)`,\n fontFamily: \"sans-serif\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\" }}>\n <div\n style={{\n width: \"20px\",\n height: \"44px\",\n borderRadius: \"6px\",\n backgroundColor: ACCENT,\n marginRight: \"24px\",\n }}\n />\n <div\n style={{\n fontSize: \"34px\",\n fontWeight: 600,\n color: \"#cbb6ff\",\n letterSpacing: \"-0.01em\",\n }}\n >\n {siteTitle}\n </div>\n </div>\n\n <div style={{ display: \"flex\", flexDirection: \"column\" }}>\n <div\n style={{\n display: \"flex\",\n fontSize: titleFontSize(title),\n fontWeight: 800,\n lineHeight: 1.05,\n letterSpacing: \"-0.02em\",\n }}\n >\n {clampText(title, 90)}\n </div>\n {description ? (\n <div\n style={{\n display: \"flex\",\n marginTop: \"28px\",\n fontSize: \"32px\",\n lineHeight: 1.35,\n color: \"#c7c9d9\",\n }}\n >\n {clampText(description, 160)}\n </div>\n ) : null}\n </div>\n\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\" }}>\n {tags.map((tag) => (\n <div\n key={tag}\n style={{\n display: \"flex\",\n marginRight: \"16px\",\n padding: \"8px 20px\",\n borderRadius: \"999px\",\n border: \"1px solid rgba(139, 92, 246, 0.5)\",\n color: \"#cbb6ff\",\n fontSize: \"24px\",\n }}\n >\n #{tag}\n </div>\n ))}\n </div>\n {domain ? (\n <div style={{ display: \"flex\", fontSize: \"26px\", color: \"#8c8fa3\" }}>\n {domain}\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/routes/page.d.ts
CHANGED
|
@@ -7,9 +7,29 @@ declare function generateStaticParams(): Promise<{
|
|
|
7
7
|
declare function generateMetadata({ params }: PageProps): Promise<{
|
|
8
8
|
title?: undefined;
|
|
9
9
|
description?: undefined;
|
|
10
|
+
openGraph?: undefined;
|
|
11
|
+
twitter?: undefined;
|
|
10
12
|
} | {
|
|
11
13
|
title: string;
|
|
12
14
|
description: string | undefined;
|
|
15
|
+
openGraph: {
|
|
16
|
+
type: string;
|
|
17
|
+
siteName: string;
|
|
18
|
+
title: string;
|
|
19
|
+
description: string | undefined;
|
|
20
|
+
images: {
|
|
21
|
+
url: string;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
alt: string;
|
|
25
|
+
}[];
|
|
26
|
+
};
|
|
27
|
+
twitter: {
|
|
28
|
+
card: string;
|
|
29
|
+
title: string;
|
|
30
|
+
description: string | undefined;
|
|
31
|
+
images: string[];
|
|
32
|
+
};
|
|
13
33
|
}>;
|
|
14
34
|
type PageProps = {
|
|
15
35
|
params: Promise<{
|
package/dist/routes/page.js
CHANGED
|
@@ -12,6 +12,7 @@ import { SilicaLink } from "@silicajs/components/routing";
|
|
|
12
12
|
import {
|
|
13
13
|
getBacklinks,
|
|
14
14
|
getBreadcrumbs,
|
|
15
|
+
getConfig,
|
|
15
16
|
getPage,
|
|
16
17
|
getPageRuntimeData,
|
|
17
18
|
getPrerenderSlugs,
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
resolveWikiLinkFromDb,
|
|
21
22
|
normalizeRouteSlug
|
|
22
23
|
} from "../server-data.js";
|
|
24
|
+
import { opengraphImagePath } from "./opengraph-image-format.js";
|
|
23
25
|
function MarkdownLink({
|
|
24
26
|
href,
|
|
25
27
|
...props
|
|
@@ -46,9 +48,25 @@ async function generateMetadata({ params }) {
|
|
|
46
48
|
renderKey.renderEnvironmentHash
|
|
47
49
|
);
|
|
48
50
|
if (!entry) return {};
|
|
51
|
+
const description = getMetaDescription(entry);
|
|
52
|
+
const config = getConfig();
|
|
53
|
+
const imageUrl = opengraphImagePath(slug);
|
|
49
54
|
return {
|
|
50
55
|
title: entry.title,
|
|
51
|
-
description
|
|
56
|
+
description,
|
|
57
|
+
openGraph: {
|
|
58
|
+
type: "article",
|
|
59
|
+
siteName: config.title,
|
|
60
|
+
title: entry.title,
|
|
61
|
+
description,
|
|
62
|
+
images: [{ url: imageUrl, width: 1200, height: 630, alt: entry.title }]
|
|
63
|
+
},
|
|
64
|
+
twitter: {
|
|
65
|
+
card: "summary_large_image",
|
|
66
|
+
title: entry.title,
|
|
67
|
+
description,
|
|
68
|
+
images: [imageUrl]
|
|
69
|
+
}
|
|
52
70
|
};
|
|
53
71
|
}
|
|
54
72
|
async function getPageMetadata(slug, renderHash, renderEnvironmentHash) {
|
|
@@ -80,6 +98,16 @@ async function VaultContent({
|
|
|
80
98
|
const renderContext = (currentSlug, currentSourcePath, embedDepth = 0) => ({
|
|
81
99
|
slug: currentSlug,
|
|
82
100
|
sourcePath: currentSourcePath,
|
|
101
|
+
assetBaseUrl: "/silica",
|
|
102
|
+
wikilinkStrategy: config.wikilinks.strategy,
|
|
103
|
+
tags: config.tags,
|
|
104
|
+
ordering: config.ordering,
|
|
105
|
+
embedDepth,
|
|
106
|
+
maxEmbedDepth: 3,
|
|
107
|
+
components: {
|
|
108
|
+
...theme.components,
|
|
109
|
+
a: MarkdownLink
|
|
110
|
+
},
|
|
83
111
|
resolveWikiLink: (_currentSlug, target) => resolveWikiLinkFromDb(
|
|
84
112
|
currentSlug,
|
|
85
113
|
target,
|
|
@@ -92,16 +120,6 @@ async function VaultContent({
|
|
|
92
120
|
config.wikilinks.strategy,
|
|
93
121
|
config.ordering
|
|
94
122
|
),
|
|
95
|
-
assetBaseUrl: "/silica",
|
|
96
|
-
wikilinkStrategy: config.wikilinks.strategy,
|
|
97
|
-
tags: config.tags,
|
|
98
|
-
ordering: config.ordering,
|
|
99
|
-
embedDepth,
|
|
100
|
-
maxEmbedDepth: 3,
|
|
101
|
-
components: {
|
|
102
|
-
...theme.components,
|
|
103
|
-
a: MarkdownLink
|
|
104
|
-
},
|
|
105
123
|
resolveEmbed: async (target) => {
|
|
106
124
|
const resolved = resolveWikiLinkFromDb(
|
|
107
125
|
currentSlug,
|
package/dist/routes/page.js.map
CHANGED
|
@@ -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 getResolvedPageProperties,\n renderMarkdown,\n renderMarkdownHtml,\n type RenderContext,\n} from \"@silicajs/core/runtime\";\nimport { SilicaLink } from \"@silicajs/components/routing\";\nimport {\n getBacklinks,\n getBreadcrumbs,\n getPage,\n getPageRuntimeData,\n getPrerenderSlugs,\n getRenderKey,\n resolveAssetFromDb,\n resolveWikiLinkFromDb,\n normalizeRouteSlug,\n} from \"../server-data.js\";\nimport type { SilicaTheme } 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 prerenderSlugs = getPrerenderSlugs();\n const slugs = prerenderSlugs.length\n ? prerenderSlugs\n : [\"__silica_prerender_placeholder__\"];\n return slugs.map((slug) => ({\n slug: slug === \"index\" ? [] : slug.split(\"/\"),\n }));\n}\n\nexport async function generateMetadata({ params }: PageProps) {\n const resolvedParams = await params;\n const slug = normalizeRouteSlug(resolvedParams?.slug);\n const renderKey = getRenderKey(slug);\n const entry = await getPageMetadata(\n slug,\n renderKey.renderHash,\n renderKey.renderEnvironmentHash,\n );\n if (!entry) return {};\n return {\n title: entry.title,\n description: getMetaDescription(entry),\n };\n}\n\nasync function getPageMetadata(\n slug: string,\n renderHash: string,\n renderEnvironmentHash: string,\n) {\n \"use cache\";\n cacheLife(\"max\");\n cacheTag(\n `environment:${renderEnvironmentHash}`,\n `page:${slug}`,\n `render:${renderHash}`,\n );\n return getPage(slug);\n}\n\nexport type PageProps = {\n params: Promise<{ slug?: string[] }> | { slug?: string[] };\n};\n\nexport async function VaultContent({\n slug,\n renderHash,\n renderEnvironmentHash,\n theme,\n}: {\n slug: string;\n renderHash: string;\n renderEnvironmentHash: string;\n theme: SilicaTheme;\n}) {\n \"use cache\";\n cacheLife(\"max\");\n const data = getPageRuntimeData(slug);\n if (!data) notFound();\n const { entry, config } = data;\n cacheTag(\n `environment:${renderEnvironmentHash}`,\n `page:${slug}`,\n `render:${renderHash}`,\n );\n\n const renderContext = (\n currentSlug: string,\n currentSourcePath: string,\n embedDepth = 0,\n ): RenderContext => ({\n slug: currentSlug,\n sourcePath: currentSourcePath,\n resolveWikiLink: (_currentSlug, target) =>\n resolveWikiLinkFromDb(\n currentSlug,\n target,\n config.wikilinks.strategy,\n config.ordering,\n ),\n resolveAsset: (_currentSourcePath, target) =>\n resolveAssetFromDb(\n currentSourcePath,\n target,\n config.wikilinks.strategy,\n config.ordering,\n ),\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 = resolveWikiLinkFromDb(\n currentSlug,\n target.path || currentSlug,\n config.wikilinks.strategy,\n config.ordering,\n );\n if (!resolved || embedDepth >= 3) return;\n const embeddedEntry = getPage(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, embeddedEntry.sourcePath, embedDepth + 1),\n );\n },\n });\n\n const raw = await fs.readFile(entry.file, \"utf8\");\n const pageRenderContext = renderContext(slug, entry.sourcePath);\n const rendered = await renderMarkdown(raw, pageRenderContext);\n\n return (\n <theme.PageRenderer\n config={config}\n breadcrumbs={getBreadcrumbs(slug)}\n backlinks={getBacklinks(slug)}\n page={{\n slug,\n title: entry.title,\n description: entry.description,\n content: rendered.content,\n frontmatter: entry.frontmatter,\n pageProperties: getResolvedPageProperties(\n entry.frontmatter,\n pageRenderContext,\n ),\n toc: rendered.toc,\n tags: entry.tags,\n entry,\n }}\n />\n );\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 getResolvedPageProperties,\n renderMarkdown,\n renderMarkdownHtml,\n type RenderContext,\n} from \"@silicajs/core/runtime\";\nimport { SilicaLink } from \"@silicajs/components/routing\";\nimport {\n getBacklinks,\n getBreadcrumbs,\n getConfig,\n getPage,\n getPageRuntimeData,\n getPrerenderSlugs,\n getRenderKey,\n resolveAssetFromDb,\n resolveWikiLinkFromDb,\n normalizeRouteSlug,\n} from \"../server-data.js\";\nimport { opengraphImagePath } from \"./opengraph-image-format.js\";\nimport type { SilicaTheme } 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 prerenderSlugs = getPrerenderSlugs();\n const slugs = prerenderSlugs.length\n ? prerenderSlugs\n : [\"__silica_prerender_placeholder__\"];\n return slugs.map((slug) => ({\n slug: slug === \"index\" ? [] : slug.split(\"/\"),\n }));\n}\n\nexport async function generateMetadata({ params }: PageProps) {\n const resolvedParams = await params;\n const slug = normalizeRouteSlug(resolvedParams?.slug);\n const renderKey = getRenderKey(slug);\n const entry = await getPageMetadata(\n slug,\n renderKey.renderHash,\n renderKey.renderEnvironmentHash,\n );\n if (!entry) return {};\n const description = getMetaDescription(entry);\n const config = getConfig();\n const imageUrl = opengraphImagePath(slug);\n return {\n title: entry.title,\n description,\n openGraph: {\n type: \"article\",\n siteName: config.title,\n title: entry.title,\n description,\n images: [{ url: imageUrl, width: 1200, height: 630, alt: entry.title }],\n },\n twitter: {\n card: \"summary_large_image\",\n title: entry.title,\n description,\n images: [imageUrl],\n },\n };\n}\n\nasync function getPageMetadata(\n slug: string,\n renderHash: string,\n renderEnvironmentHash: string,\n) {\n \"use cache\";\n cacheLife(\"max\");\n cacheTag(\n `environment:${renderEnvironmentHash}`,\n `page:${slug}`,\n `render:${renderHash}`,\n );\n return getPage(slug);\n}\n\nexport type PageProps = {\n params: Promise<{ slug?: string[] }> | { slug?: string[] };\n};\n\nexport async function VaultContent({\n slug,\n renderHash,\n renderEnvironmentHash,\n theme,\n}: {\n slug: string;\n renderHash: string;\n renderEnvironmentHash: string;\n theme: SilicaTheme;\n}) {\n \"use cache\";\n cacheLife(\"max\");\n const data = getPageRuntimeData(slug);\n if (!data) notFound();\n const { entry, config } = data;\n cacheTag(\n `environment:${renderEnvironmentHash}`,\n `page:${slug}`,\n `render:${renderHash}`,\n );\n\n const renderContext = (\n currentSlug: string,\n currentSourcePath: string,\n embedDepth = 0,\n ): RenderContext => ({\n slug: currentSlug,\n sourcePath: currentSourcePath,\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 resolveWikiLink: (_currentSlug, target) =>\n resolveWikiLinkFromDb(\n currentSlug,\n target,\n config.wikilinks.strategy,\n config.ordering,\n ),\n resolveAsset: (_currentSourcePath, target) =>\n resolveAssetFromDb(\n currentSourcePath,\n target,\n config.wikilinks.strategy,\n config.ordering,\n ),\n resolveEmbed: async (target) => {\n const resolved = resolveWikiLinkFromDb(\n currentSlug,\n target.path || currentSlug,\n config.wikilinks.strategy,\n config.ordering,\n );\n if (!resolved || embedDepth >= 3) return;\n const embeddedEntry = getPage(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, embeddedEntry.sourcePath, embedDepth + 1),\n );\n },\n });\n\n const raw = await fs.readFile(entry.file, \"utf8\");\n const pageRenderContext = renderContext(slug, entry.sourcePath);\n const rendered = await renderMarkdown(raw, pageRenderContext);\n\n return (\n <theme.PageRenderer\n config={config}\n breadcrumbs={getBreadcrumbs(slug)}\n backlinks={getBacklinks(slug)}\n page={{\n slug,\n title: entry.title,\n description: entry.description,\n content: rendered.content,\n frontmatter: entry.frontmatter,\n pageProperties: getResolvedPageProperties(\n entry.frontmatter,\n pageRenderContext,\n ),\n toc: rendered.toc,\n tags: entry.tags,\n entry,\n }}\n />\n );\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":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const VAULT_DATABASE_FILENAME = "vault.db";
|
|
2
|
+
declare const SILICA_DATA_DIRNAME = "data";
|
|
3
|
+
declare function resolveProjectRoot(): string;
|
|
4
|
+
declare function tryResolveProjectRoot(): string | undefined;
|
|
5
|
+
declare function resolveDataRoot(cwd?: string): string;
|
|
6
|
+
declare function tryResolveDataRoot(): string | undefined;
|
|
7
|
+
declare function resolveGeneratedRuntimeProjectRoot(cwd?: string): string | undefined;
|
|
8
|
+
declare function resolveVaultDatabasePath(cwd?: string): string;
|
|
9
|
+
|
|
10
|
+
export { SILICA_DATA_DIRNAME, VAULT_DATABASE_FILENAME, resolveDataRoot, resolveGeneratedRuntimeProjectRoot, resolveProjectRoot, resolveVaultDatabasePath, tryResolveDataRoot, tryResolveProjectRoot };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const VAULT_DATABASE_FILENAME = "vault.db";
|
|
4
|
+
const SILICA_DATA_DIRNAME = "data";
|
|
5
|
+
function resolveProjectRoot() {
|
|
6
|
+
const cwd = (
|
|
7
|
+
/* turbopackIgnore: true */
|
|
8
|
+
process.cwd()
|
|
9
|
+
);
|
|
10
|
+
return resolveAppRoot(cwd);
|
|
11
|
+
}
|
|
12
|
+
function tryResolveProjectRoot() {
|
|
13
|
+
try {
|
|
14
|
+
return resolveProjectRoot();
|
|
15
|
+
} catch {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function resolveDataRoot(cwd = (
|
|
20
|
+
/* turbopackIgnore: true */
|
|
21
|
+
process.cwd()
|
|
22
|
+
)) {
|
|
23
|
+
const configured = process.env.SILICA_DATA_ROOT?.trim();
|
|
24
|
+
if (configured) return resolveConfiguredPath(configured, cwd);
|
|
25
|
+
const configuredProjectRoot = process.env.SILICA_PROJECT_ROOT?.trim();
|
|
26
|
+
if (configuredProjectRoot) {
|
|
27
|
+
const root = resolveConfiguredPath(configuredProjectRoot, cwd);
|
|
28
|
+
const generatedDataRoot2 = path.join(root, ".silica/next/data");
|
|
29
|
+
if (isSilicaDataRoot(generatedDataRoot2)) return generatedDataRoot2;
|
|
30
|
+
const legacyDataRoot = path.join(root, ".silica");
|
|
31
|
+
if (isSilicaDataRoot(legacyDataRoot)) return legacyDataRoot;
|
|
32
|
+
}
|
|
33
|
+
const generatedDataRoot = resolveGeneratedRuntimeDataRoot(cwd);
|
|
34
|
+
if (generatedDataRoot) return generatedDataRoot;
|
|
35
|
+
throw new Error("Silica runtime data could not be found.");
|
|
36
|
+
}
|
|
37
|
+
function tryResolveDataRoot() {
|
|
38
|
+
try {
|
|
39
|
+
return resolveDataRoot();
|
|
40
|
+
} catch {
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function resolveConfiguredPath(value, cwd) {
|
|
45
|
+
return path.isAbsolute(value) ? value : path.resolve(cwd, value);
|
|
46
|
+
}
|
|
47
|
+
function resolveGeneratedRuntimeProjectRoot(cwd = (
|
|
48
|
+
/* turbopackIgnore: true */
|
|
49
|
+
process.cwd()
|
|
50
|
+
)) {
|
|
51
|
+
const dataRoot = resolveGeneratedRuntimeDataRoot(cwd);
|
|
52
|
+
return dataRoot ? path.dirname(dataRoot) : void 0;
|
|
53
|
+
}
|
|
54
|
+
function resolveVaultDatabasePath(cwd = (
|
|
55
|
+
/* turbopackIgnore: true */
|
|
56
|
+
process.cwd()
|
|
57
|
+
)) {
|
|
58
|
+
return path.join(resolveDataRoot(cwd), VAULT_DATABASE_FILENAME);
|
|
59
|
+
}
|
|
60
|
+
function resolveAppRoot(cwd) {
|
|
61
|
+
const dataRoot = resolveDataRoot(cwd);
|
|
62
|
+
return path.dirname(dataRoot);
|
|
63
|
+
}
|
|
64
|
+
function resolveGeneratedRuntimeDataRoot(cwd) {
|
|
65
|
+
const directDataRoot = path.join(cwd, SILICA_DATA_DIRNAME);
|
|
66
|
+
if (isSilicaDataRoot(directDataRoot)) return directDataRoot;
|
|
67
|
+
const projectGeneratedDataRoot = path.join(cwd, ".silica/next/data");
|
|
68
|
+
if (isSilicaDataRoot(projectGeneratedDataRoot))
|
|
69
|
+
return projectGeneratedDataRoot;
|
|
70
|
+
const ancestorDataRoot = findAncestorDataRoot(cwd);
|
|
71
|
+
if (ancestorDataRoot) return ancestorDataRoot;
|
|
72
|
+
const legacyDataRoot = path.join(cwd, ".silica");
|
|
73
|
+
if (isSilicaDataRoot(legacyDataRoot)) return legacyDataRoot;
|
|
74
|
+
}
|
|
75
|
+
function findAncestorDataRoot(cwd) {
|
|
76
|
+
let current = path.resolve(cwd);
|
|
77
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
78
|
+
const dataRoot = path.join(current, SILICA_DATA_DIRNAME);
|
|
79
|
+
if (isSilicaDataRoot(dataRoot)) return dataRoot;
|
|
80
|
+
const parent = path.dirname(current);
|
|
81
|
+
if (parent === current) return void 0;
|
|
82
|
+
current = parent;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function isSilicaDataRoot(dataRoot) {
|
|
86
|
+
return fs.existsSync(path.join(dataRoot, VAULT_DATABASE_FILENAME));
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
SILICA_DATA_DIRNAME,
|
|
90
|
+
VAULT_DATABASE_FILENAME,
|
|
91
|
+
resolveDataRoot,
|
|
92
|
+
resolveGeneratedRuntimeProjectRoot,
|
|
93
|
+
resolveProjectRoot,
|
|
94
|
+
resolveVaultDatabasePath,
|
|
95
|
+
tryResolveDataRoot,
|
|
96
|
+
tryResolveProjectRoot
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=runtime-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime-paths.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport const VAULT_DATABASE_FILENAME = \"vault.db\";\nexport const SILICA_DATA_DIRNAME = \"data\";\n\nexport function resolveProjectRoot(): string {\n const cwd = /* turbopackIgnore: true */ process.cwd();\n return resolveAppRoot(cwd);\n}\n\nexport function tryResolveProjectRoot(): string | undefined {\n try {\n return resolveProjectRoot();\n } catch {\n return undefined;\n }\n}\n\nexport function resolveDataRoot(\n cwd = /* turbopackIgnore: true */ process.cwd(),\n): string {\n const configured = process.env.SILICA_DATA_ROOT?.trim();\n if (configured) return resolveConfiguredPath(configured, cwd);\n\n const configuredProjectRoot = process.env.SILICA_PROJECT_ROOT?.trim();\n if (configuredProjectRoot) {\n const root = resolveConfiguredPath(configuredProjectRoot, cwd);\n const generatedDataRoot = path.join(root, \".silica/next/data\");\n if (isSilicaDataRoot(generatedDataRoot)) return generatedDataRoot;\n\n const legacyDataRoot = path.join(root, \".silica\");\n if (isSilicaDataRoot(legacyDataRoot)) return legacyDataRoot;\n }\n\n const generatedDataRoot = resolveGeneratedRuntimeDataRoot(cwd);\n if (generatedDataRoot) return generatedDataRoot;\n\n throw new Error(\"Silica runtime data could not be found.\");\n}\n\nexport function tryResolveDataRoot(): string | undefined {\n try {\n return resolveDataRoot();\n } catch {\n return undefined;\n }\n}\n\nfunction resolveConfiguredPath(value: string, cwd: string): string {\n return path.isAbsolute(value) ? value : path.resolve(cwd, value);\n}\n\nexport function resolveGeneratedRuntimeProjectRoot(\n cwd = /* turbopackIgnore: true */ process.cwd(),\n): string | undefined {\n const dataRoot = resolveGeneratedRuntimeDataRoot(cwd);\n return dataRoot ? path.dirname(dataRoot) : undefined;\n}\n\nexport function resolveVaultDatabasePath(\n cwd = /* turbopackIgnore: true */ process.cwd(),\n): string {\n return path.join(resolveDataRoot(cwd), VAULT_DATABASE_FILENAME);\n}\n\nfunction resolveAppRoot(cwd: string): string {\n const dataRoot = resolveDataRoot(cwd);\n return path.dirname(dataRoot);\n}\n\nfunction resolveGeneratedRuntimeDataRoot(cwd: string): string | undefined {\n const directDataRoot = path.join(cwd, SILICA_DATA_DIRNAME);\n if (isSilicaDataRoot(directDataRoot)) return directDataRoot;\n\n const projectGeneratedDataRoot = path.join(cwd, \".silica/next/data\");\n if (isSilicaDataRoot(projectGeneratedDataRoot))\n return projectGeneratedDataRoot;\n\n const ancestorDataRoot = findAncestorDataRoot(cwd);\n if (ancestorDataRoot) return ancestorDataRoot;\n\n const legacyDataRoot = path.join(cwd, \".silica\");\n if (isSilicaDataRoot(legacyDataRoot)) return legacyDataRoot;\n}\n\nfunction findAncestorDataRoot(cwd: string): string | undefined {\n let current = path.resolve(cwd);\n for (let depth = 0; depth < 8; depth += 1) {\n const dataRoot = path.join(current, SILICA_DATA_DIRNAME);\n if (isSilicaDataRoot(dataRoot)) return dataRoot;\n\n const parent = path.dirname(current);\n if (parent === current) return undefined;\n current = parent;\n }\n}\n\nfunction isSilicaDataRoot(dataRoot: string): boolean {\n return fs.existsSync(path.join(dataRoot, VAULT_DATABASE_FILENAME));\n}\n"],"mappings":"AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEV,MAAM,0BAA0B;AAChC,MAAM,sBAAsB;AAE5B,SAAS,qBAA6B;AAC3C,QAAM;AAAA;AAAA,IAAkC,QAAQ,IAAI;AAAA;AACpD,SAAO,eAAe,GAAG;AAC3B;AAEO,SAAS,wBAA4C;AAC1D,MAAI;AACF,WAAO,mBAAmB;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,gBACd;AAAA;AAAA,EAAkC,QAAQ,IAAI;AAAA,GACtC;AACR,QAAM,aAAa,QAAQ,IAAI,kBAAkB,KAAK;AACtD,MAAI,WAAY,QAAO,sBAAsB,YAAY,GAAG;AAE5D,QAAM,wBAAwB,QAAQ,IAAI,qBAAqB,KAAK;AACpE,MAAI,uBAAuB;AACzB,UAAM,OAAO,sBAAsB,uBAAuB,GAAG;AAC7D,UAAMA,qBAAoB,KAAK,KAAK,MAAM,mBAAmB;AAC7D,QAAI,iBAAiBA,kBAAiB,EAAG,QAAOA;AAEhD,UAAM,iBAAiB,KAAK,KAAK,MAAM,SAAS;AAChD,QAAI,iBAAiB,cAAc,EAAG,QAAO;AAAA,EAC/C;AAEA,QAAM,oBAAoB,gCAAgC,GAAG;AAC7D,MAAI,kBAAmB,QAAO;AAE9B,QAAM,IAAI,MAAM,yCAAyC;AAC3D;AAEO,SAAS,qBAAyC;AACvD,MAAI;AACF,WAAO,gBAAgB;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,sBAAsB,OAAe,KAAqB;AACjE,SAAO,KAAK,WAAW,KAAK,IAAI,QAAQ,KAAK,QAAQ,KAAK,KAAK;AACjE;AAEO,SAAS,mCACd;AAAA;AAAA,EAAkC,QAAQ,IAAI;AAAA,GAC1B;AACpB,QAAM,WAAW,gCAAgC,GAAG;AACpD,SAAO,WAAW,KAAK,QAAQ,QAAQ,IAAI;AAC7C;AAEO,SAAS,yBACd;AAAA;AAAA,EAAkC,QAAQ,IAAI;AAAA,GACtC;AACR,SAAO,KAAK,KAAK,gBAAgB,GAAG,GAAG,uBAAuB;AAChE;AAEA,SAAS,eAAe,KAAqB;AAC3C,QAAM,WAAW,gBAAgB,GAAG;AACpC,SAAO,KAAK,QAAQ,QAAQ;AAC9B;AAEA,SAAS,gCAAgC,KAAiC;AACxE,QAAM,iBAAiB,KAAK,KAAK,KAAK,mBAAmB;AACzD,MAAI,iBAAiB,cAAc,EAAG,QAAO;AAE7C,QAAM,2BAA2B,KAAK,KAAK,KAAK,mBAAmB;AACnE,MAAI,iBAAiB,wBAAwB;AAC3C,WAAO;AAET,QAAM,mBAAmB,qBAAqB,GAAG;AACjD,MAAI,iBAAkB,QAAO;AAE7B,QAAM,iBAAiB,KAAK,KAAK,KAAK,SAAS;AAC/C,MAAI,iBAAiB,cAAc,EAAG,QAAO;AAC/C;AAEA,SAAS,qBAAqB,KAAiC;AAC7D,MAAI,UAAU,KAAK,QAAQ,GAAG;AAC9B,WAAS,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG;AACzC,UAAM,WAAW,KAAK,KAAK,SAAS,mBAAmB;AACvD,QAAI,iBAAiB,QAAQ,EAAG,QAAO;AAEvC,UAAM,SAAS,KAAK,QAAQ,OAAO;AACnC,QAAI,WAAW,QAAS,QAAO;AAC/B,cAAU;AAAA,EACZ;AACF;AAEA,SAAS,iBAAiB,UAA2B;AACnD,SAAO,GAAG,WAAW,KAAK,KAAK,UAAU,uBAAuB,CAAC;AACnE;","names":["generatedDataRoot"]}
|
package/dist/server-data.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import Database from "better-sqlite3";
|
|
4
|
+
import {
|
|
5
|
+
resolveDataRoot,
|
|
6
|
+
resolveProjectRoot,
|
|
7
|
+
resolveVaultDatabasePath
|
|
8
|
+
} from "./runtime-paths.js";
|
|
4
9
|
import {
|
|
5
10
|
asFullSlug,
|
|
6
11
|
normalizeAssetReference,
|
|
@@ -9,20 +14,15 @@ import {
|
|
|
9
14
|
resolveRelative,
|
|
10
15
|
slugToHref
|
|
11
16
|
} from "@silicajs/core/runtime";
|
|
12
|
-
const VAULT_DATABASE_FILENAME = "vault.db";
|
|
13
17
|
let loadedVaultDb;
|
|
14
18
|
function getProjectRoot() {
|
|
15
|
-
|
|
16
|
-
if (!projectRoot) {
|
|
17
|
-
throw new Error("SILICA_PROJECT_ROOT must be set by the Silica CLI.");
|
|
18
|
-
}
|
|
19
|
-
return projectRoot;
|
|
19
|
+
return resolveProjectRoot();
|
|
20
20
|
}
|
|
21
21
|
function getSilicaRoot() {
|
|
22
|
-
return
|
|
22
|
+
return resolveDataRoot();
|
|
23
23
|
}
|
|
24
24
|
function getVaultDatabasePath() {
|
|
25
|
-
return
|
|
25
|
+
return resolveVaultDatabasePath();
|
|
26
26
|
}
|
|
27
27
|
function loadVaultDb() {
|
|
28
28
|
const databasePath = getVaultDatabasePath();
|
package/dist/server-data.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server-data.ts"],"sourcesContent":["import path from \"node:path\";\nimport fs from \"node:fs\";\nimport Database from \"better-sqlite3\";\nimport type { LoadedSearchIndex } from \"@silicajs/search\";\nimport type {\n Navigation,\n ManifestEntry,\n RenderCacheState,\n ResolvedSilicaConfig,\n} from \"@silicajs/core/runtime\";\nimport {\n asFullSlug,\n normalizeAssetReference,\n normalizeSlug,\n resolveRelativeAsset,\n resolveRelative,\n slugToHref,\n} from \"@silicajs/core/runtime\";\n\nconst VAULT_DATABASE_FILENAME = \"vault.db\";\n\nexport type LoadedVaultDb = {\n databasePath: string;\n generatedAt: string;\n renderEnvironmentHash: string;\n config: ResolvedSilicaConfig;\n cacheState: RenderCacheState;\n mtimeMs: number;\n db: Database.Database;\n close(): void;\n};\n\nexport type VaultPageData = {\n cacheState: RenderCacheState;\n config: ResolvedSilicaConfig;\n entry: ManifestEntry;\n};\n\ntype MetadataRows = {\n generatedAt: string;\n renderEnvironmentHash: string;\n config: ResolvedSilicaConfig;\n cacheState: RenderCacheState;\n};\n\ntype NoteRow = {\n slug: string;\n file: string;\n source_path: string;\n title: string;\n menu_label: string;\n description: string | null;\n generated_description: string | null;\n frontmatter_json: string;\n tags_json: string;\n created: string | null;\n modified: string | null;\n sort_key: string | null;\n listed: 0 | 1;\n content_hash: string;\n render_hash: string;\n prerender: 0 | 1;\n};\n\ntype NavigationRow = {\n slug: string;\n title: string;\n sort_key: string | null;\n};\n\ntype BacklinkRow = {\n slug: string;\n title: string;\n};\n\nlet loadedVaultDb: LoadedVaultDb | 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 function getVaultDatabasePath(): string {\n return path.join(getSilicaRoot(), VAULT_DATABASE_FILENAME);\n}\n\nexport function loadVaultDb(): LoadedVaultDb {\n const databasePath = getVaultDatabasePath();\n const stat = fs.statSync(databasePath);\n if (\n loadedVaultDb?.databasePath === databasePath &&\n loadedVaultDb.mtimeMs === stat.mtimeMs\n ) {\n return loadedVaultDb;\n }\n\n loadedVaultDb?.close();\n\n const db = new Database(databasePath, {\n fileMustExist: true,\n readonly: true,\n });\n db.pragma(\"query_only = ON\");\n const metadata = readMetadata(db);\n loadedVaultDb = {\n databasePath,\n generatedAt: metadata.generatedAt,\n renderEnvironmentHash: metadata.renderEnvironmentHash,\n config: metadata.config,\n cacheState: metadata.cacheState,\n mtimeMs: stat.mtimeMs,\n db,\n close: () => {\n db.close();\n if (loadedVaultDb?.db === db) loadedVaultDb = undefined;\n },\n };\n return loadedVaultDb;\n}\n\nexport function getPage(slug: string): ManifestEntry | undefined {\n const row = loadVaultDb()\n .db.prepare(\"SELECT * FROM notes WHERE slug = ?\")\n .get(slug) as NoteRow | undefined;\n return row ? noteRowToEntry(row) : undefined;\n}\n\nexport function getPageBySourcePath(\n sourcePath: string,\n): ManifestEntry | undefined {\n const row = loadVaultDb()\n .db.prepare(\"SELECT * FROM notes WHERE source_path = ?\")\n .get(normalizeDbSourcePath(sourcePath)) as NoteRow | undefined;\n return row ? noteRowToEntry(row) : undefined;\n}\n\nexport function getPageRuntimeData(slug: string): VaultPageData | undefined {\n const entry = getPage(slug);\n if (!entry) return undefined;\n return {\n entry,\n config: getConfig(),\n cacheState: getCacheState(),\n };\n}\n\nexport function getRenderKey(slug: string): {\n renderHash: string;\n renderEnvironmentHash: string;\n} {\n const loaded = loadVaultDb();\n const row = loaded.db\n .prepare(\"SELECT render_hash FROM notes WHERE slug = ?\")\n .get(slug) as { render_hash: string } | undefined;\n return {\n renderHash: row?.render_hash ?? \"missing\",\n renderEnvironmentHash: loaded.renderEnvironmentHash,\n };\n}\n\nexport function loadRenderEnvironmentHash(): string {\n return loadVaultDb().renderEnvironmentHash;\n}\n\nexport function getPrerenderSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\n \"SELECT slug FROM notes WHERE prerender = 1 ORDER BY COALESCE(sort_key, slug), slug\",\n )\n .all()\n .map((row) => (row as { slug: string }).slug);\n}\n\nexport function getAllSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\"SELECT slug FROM notes ORDER BY slug\")\n .all()\n .map((row) => (row as { slug: string }).slug);\n}\n\nexport function getNavigation(): Navigation {\n const entries = loadVaultDb()\n .db.prepare(\n `\n SELECT slug, menu_label AS title, sort_key\n FROM notes\n WHERE listed = 1\n ORDER BY COALESCE(sort_key, slug), slug\n `,\n )\n .all() as NavigationRow[];\n return {\n version: 1,\n entries: entries.map((entry) => ({\n slug: entry.slug,\n title: entry.title,\n sortKey: entry.sort_key ?? undefined,\n })),\n };\n}\n\nexport function getBacklinks(slug: string): BacklinkRow[] {\n return loadVaultDb()\n .db.prepare(\n `\n SELECT n.slug, n.title\n FROM links l\n JOIN notes n ON n.slug = l.source_slug\n WHERE l.target_slug = ?\n AND l.kind = 'link'\n ORDER BY n.title COLLATE NOCASE ASC, n.slug\n `,\n )\n .all(slug) as BacklinkRow[];\n}\n\nexport function getConfig(): ResolvedSilicaConfig {\n return loadVaultDb().config;\n}\n\nexport function getCacheState(): RenderCacheState {\n return loadVaultDb().cacheState;\n}\n\nexport function getTagSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\"SELECT DISTINCT tag FROM note_tags ORDER BY tag\")\n .all()\n .map((row) => (row as { tag: string }).tag);\n}\n\nexport function getEntriesForTag(tag: string): ManifestEntry[] {\n return loadVaultDb()\n .db.prepare(\n `\n SELECT n.*\n FROM notes n\n WHERE n.listed = 1\n AND EXISTS (\n SELECT 1\n FROM note_tags nt\n WHERE nt.slug = n.slug\n AND nt.tag = ?\n )\n ORDER BY n.title COLLATE NOCASE ASC, n.slug\n `,\n )\n .all(tag)\n .map((row) => noteRowToEntry(row as NoteRow));\n}\n\nexport function getRelatedTagsForEntries(\n slugs: string[],\n tag: string,\n): string[] {\n if (slugs.length === 0) return [];\n return loadVaultDb()\n .db.prepare(\n `\n SELECT nt.tag, COUNT(*) AS count\n FROM note_tags nt\n WHERE nt.slug IN (${slugs.map(() => \"?\").join(\", \")})\n AND nt.tag != ?\n GROUP BY nt.tag\n ORDER BY count DESC, nt.tag ASC\n LIMIT 12\n `,\n )\n .all(...slugs, tag)\n .map((row) => (row as { tag: string }).tag);\n}\n\nexport function resolveWikiLinkFromDb(\n currentSlug: string,\n target: string,\n strategy: \"absolute\" | \"relative\" | \"shortest\",\n ordering?: { numericPrefixes?: boolean },\n): string | undefined {\n const [rawPath] = target.split(\"#\");\n const slugOptions = ordering ?? getConfig().ordering;\n const normalizedTarget = normalizeSlug(rawPath ?? target, slugOptions);\n const db = loadVaultDb().db;\n\n if (strategy === \"absolute\") {\n return lookupAlias(db, \"absolute\", normalizedTarget);\n }\n\n if (strategy === \"relative\") {\n const relative = resolveRelative(\n asFullSlug(currentSlug),\n normalizedTarget,\n slugOptions,\n );\n const resolved = lookupAlias(db, \"absolute\", relative);\n if (resolved) return resolved;\n }\n\n return (\n lookupAlias(db, \"absolute\", normalizedTarget) ??\n lookupAlias(db, \"absolute\", `${normalizedTarget}/index`) ??\n lookupClosestAlias(\n db,\n currentSlug,\n normalizedTarget.split(\"/\").at(-1) ?? \"\",\n )\n );\n}\n\nexport function resolveAssetFromDb(\n currentSourcePath: string,\n target: string,\n strategy: \"absolute\" | \"relative\" | \"shortest\",\n ordering?: { numericPrefixes?: boolean },\n): string | undefined {\n const assetOptions = ordering ?? getConfig().ordering;\n const normalizedTarget = normalizeAssetReference(target, assetOptions);\n if (!normalizedTarget) return undefined;\n const db = loadVaultDb().db;\n const isExplicitRelative = isExplicitRelativeAssetReference(target);\n\n if (isExplicitRelative || strategy === \"relative\") {\n const relative = resolveRelativeAsset(\n currentSourcePath,\n target,\n assetOptions,\n );\n const resolved = lookupAssetAlias(db, \"absolute\", relative);\n if (resolved || isExplicitRelative) return resolved;\n }\n\n if (strategy === \"absolute\") {\n return lookupAssetAlias(db, \"absolute\", normalizedTarget);\n }\n\n return (\n lookupAssetAlias(db, \"absolute\", normalizedTarget) ??\n lookupAssetAlias(db, \"shortest\", normalizedTarget.split(\"/\").at(-1) ?? \"\")\n );\n}\n\nexport function getBreadcrumbs(slug: string) {\n if (slug === \"index\" || !slug.includes(\"/\")) return [];\n\n const breadcrumbs: Array<{ label: string; href?: string }> = [\n { label: \"Home\", href: \"/\" },\n ];\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),\n });\n }\n return breadcrumbs;\n}\n\nexport function loadSearchIndex(): LoadedSearchIndex {\n const loaded = loadVaultDb();\n return {\n databasePath: loaded.databasePath,\n db: loaded.db,\n close: () => undefined,\n };\n}\n\nexport function normalizeRouteSlug(slug?: string[]): string {\n return slug?.length ? slug.join(\"/\") : \"index\";\n}\n\nfunction readMetadata(db: Database.Database): MetadataRows {\n const rows = db\n .prepare(\"SELECT key, value FROM vault_metadata\")\n .all() as Array<{ key: string; value: string }>;\n const metadata = Object.fromEntries(rows.map((row) => [row.key, row.value]));\n return {\n generatedAt: metadata.generatedAt ?? \"\",\n renderEnvironmentHash: metadata.renderEnvironmentHash ?? \"silica\",\n config: parseConfigMetadata(metadata.configJson),\n cacheState: JSON.parse(metadata.cacheStateJson ?? \"{}\") as RenderCacheState,\n };\n}\n\nfunction parseConfigMetadata(value: string | undefined): ResolvedSilicaConfig {\n if (!value) throw new Error(\"vault.db is missing configJson metadata.\");\n return JSON.parse(value) as ResolvedSilicaConfig;\n}\n\nfunction lookupAlias(\n db: Database.Database,\n strategy: string,\n alias: string,\n): string | undefined {\n const row = db\n .prepare(\n `\n SELECT slug\n FROM slug_aliases\n WHERE strategy_key = ?\n AND alias = ?\n ORDER BY sort_key, slug\n LIMIT 2\n `,\n )\n .all(strategy, alias) as Array<{ slug: string }>;\n return row.length === 1 ? row[0]?.slug : undefined;\n}\n\nfunction lookupClosestAlias(\n db: Database.Database,\n currentSlug: string,\n alias: string,\n): string | undefined {\n const rows = db\n .prepare(\n `\n SELECT slug\n FROM slug_aliases\n WHERE strategy_key = 'shortest'\n AND alias = ?\n ORDER BY slug\n `,\n )\n .all(alias) as Array<{ slug: string }>;\n\n return closestWikiLinkCandidate(\n currentSlug,\n rows.map((row) => row.slug),\n );\n}\n\nfunction lookupAssetAlias(\n db: Database.Database,\n strategy: string,\n alias: string,\n): string | undefined {\n const row = db\n .prepare(\n `\n SELECT asset_path\n FROM asset_aliases\n WHERE strategy_key = ?\n AND alias = ?\n ORDER BY sort_key, asset_path\n LIMIT 2\n `,\n )\n .all(strategy, alias) as Array<{ asset_path: string }>;\n return row.length === 1 ? row[0]?.asset_path : undefined;\n}\n\nfunction closestWikiLinkCandidate(\n currentSlug: string,\n candidates: readonly string[],\n): string | undefined {\n if (candidates.length === 0) return undefined;\n\n const currentDirectory = normalizeSlug(currentSlug, {\n numericPrefixes: false,\n })\n .split(\"/\")\n .slice(0, -1);\n const [bestCandidate] = [...candidates].sort((left, right) => {\n const leftScore = wikiLinkCandidateScore(currentDirectory, left);\n const rightScore = wikiLinkCandidateScore(currentDirectory, right);\n\n return (\n rightScore.sharedPrefixLength - leftScore.sharedPrefixLength ||\n leftScore.depth - rightScore.depth ||\n compareSlugs(left, right)\n );\n });\n\n return bestCandidate;\n}\n\nfunction wikiLinkCandidateScore(currentDirectory: string[], slug: string) {\n const simplified = slug === \"index\" ? \"\" : slug.replace(/\\/index$/, \"\");\n const segments = simplified ? simplified.split(\"/\") : [];\n\n return {\n sharedPrefixLength: sharedPrefixLength(\n currentDirectory,\n segments.slice(0, -1),\n ),\n depth: segments.length,\n };\n}\n\nfunction sharedPrefixLength(left: readonly string[], right: readonly string[]) {\n let length = 0;\n while (left[length] !== undefined && left[length] === right[length]) {\n length += 1;\n }\n return length;\n}\n\nfunction compareSlugs(left: string, right: string) {\n if (left < right) return -1;\n if (left > right) return 1;\n return 0;\n}\n\nfunction isExplicitRelativeAssetReference(value: string): boolean {\n const withoutSuffix = value.split(/[?#]/)[0] ?? \"\";\n const normalized = withoutSuffix\n .replace(/\\\\/g, \"/\")\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\");\n return /^\\.{1,2}\\//.test(normalized);\n}\n\nfunction noteRowToEntry(row: NoteRow): ManifestEntry {\n return {\n slug: row.slug,\n title: row.title,\n menuLabel: row.menu_label,\n description: row.description ?? undefined,\n generatedDescription: row.generated_description ?? undefined,\n tags: parseJsonArray(row.tags_json),\n file: path.isAbsolute(row.file)\n ? row.file\n : path.join(getProjectRoot(), row.file),\n sourcePath: row.source_path,\n sortKey: row.sort_key ?? undefined,\n created: row.created ?? undefined,\n modified: row.modified ?? undefined,\n frontmatter: parseObject(row.frontmatter_json),\n contentHash: row.content_hash,\n embeds: getEmbeds(row.slug),\n };\n}\n\nfunction normalizeDbSourcePath(value: string): string {\n return value\n .replace(/\\\\/g, \"/\")\n .replace(/^\\/+/, \"\")\n .replace(/^content\\//, \"\");\n}\n\nfunction getEmbeds(slug: string): string[] {\n return loadVaultDb()\n .db.prepare(\n \"SELECT target_slug FROM links WHERE source_slug = ? AND kind = 'embed' ORDER BY target_slug\",\n )\n .all(slug)\n .map((row) => (row as { target_slug: string }).target_slug);\n}\n\nfunction breadcrumbSegmentHref(segmentPath: string): string | undefined {\n if (getPage(segmentPath)) return slugToHref(segmentPath);\n const indexSlug = `${segmentPath}/index`;\n if (getPage(indexSlug)) return slugToHref(indexSlug);\n return undefined;\n}\n\nfunction prettySegment(segment: string): string {\n return segment\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction parseObject(value: string): Record<string, unknown> {\n return JSON.parse(value) as Record<string, unknown>;\n}\n\nfunction parseJsonArray(value: string): string[] {\n return JSON.parse(value) as string[];\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server-data.ts"],"sourcesContent":["import path from \"node:path\";\nimport fs from \"node:fs\";\nimport Database from \"better-sqlite3\";\nimport type { LoadedSearchIndex } from \"@silicajs/search\";\nimport {\n resolveDataRoot,\n resolveProjectRoot,\n resolveVaultDatabasePath,\n} from \"./runtime-paths.js\";\nimport type {\n Navigation,\n ManifestEntry,\n RenderCacheState,\n ResolvedSilicaConfig,\n} from \"@silicajs/core/runtime\";\nimport {\n asFullSlug,\n normalizeAssetReference,\n normalizeSlug,\n resolveRelativeAsset,\n resolveRelative,\n slugToHref,\n} from \"@silicajs/core/runtime\";\n\nexport type LoadedVaultDb = {\n databasePath: string;\n generatedAt: string;\n renderEnvironmentHash: string;\n config: ResolvedSilicaConfig;\n cacheState: RenderCacheState;\n mtimeMs: number;\n db: Database.Database;\n close(): void;\n};\n\nexport type VaultPageData = {\n cacheState: RenderCacheState;\n config: ResolvedSilicaConfig;\n entry: ManifestEntry;\n};\n\ntype MetadataRows = {\n generatedAt: string;\n renderEnvironmentHash: string;\n config: ResolvedSilicaConfig;\n cacheState: RenderCacheState;\n};\n\ntype NoteRow = {\n slug: string;\n file: string;\n source_path: string;\n title: string;\n menu_label: string;\n description: string | null;\n generated_description: string | null;\n frontmatter_json: string;\n tags_json: string;\n created: string | null;\n modified: string | null;\n sort_key: string | null;\n listed: 0 | 1;\n content_hash: string;\n render_hash: string;\n prerender: 0 | 1;\n};\n\ntype NavigationRow = {\n slug: string;\n title: string;\n sort_key: string | null;\n};\n\ntype BacklinkRow = {\n slug: string;\n title: string;\n};\n\nlet loadedVaultDb: LoadedVaultDb | undefined;\n\nexport function getProjectRoot(): string {\n return resolveProjectRoot();\n}\n\nexport function getSilicaRoot(): string {\n return resolveDataRoot();\n}\n\nexport function getVaultDatabasePath(): string {\n return resolveVaultDatabasePath();\n}\n\nexport function loadVaultDb(): LoadedVaultDb {\n const databasePath = getVaultDatabasePath();\n const stat = fs.statSync(databasePath);\n if (\n loadedVaultDb?.databasePath === databasePath &&\n loadedVaultDb.mtimeMs === stat.mtimeMs\n ) {\n return loadedVaultDb;\n }\n\n loadedVaultDb?.close();\n\n const db = new Database(databasePath, {\n fileMustExist: true,\n readonly: true,\n });\n db.pragma(\"query_only = ON\");\n const metadata = readMetadata(db);\n loadedVaultDb = {\n databasePath,\n generatedAt: metadata.generatedAt,\n renderEnvironmentHash: metadata.renderEnvironmentHash,\n config: metadata.config,\n cacheState: metadata.cacheState,\n mtimeMs: stat.mtimeMs,\n db,\n close: () => {\n db.close();\n if (loadedVaultDb?.db === db) loadedVaultDb = undefined;\n },\n };\n return loadedVaultDb;\n}\n\nexport function getPage(slug: string): ManifestEntry | undefined {\n const row = loadVaultDb()\n .db.prepare(\"SELECT * FROM notes WHERE slug = ?\")\n .get(slug) as NoteRow | undefined;\n return row ? noteRowToEntry(row) : undefined;\n}\n\nexport function getPageBySourcePath(\n sourcePath: string,\n): ManifestEntry | undefined {\n const row = loadVaultDb()\n .db.prepare(\"SELECT * FROM notes WHERE source_path = ?\")\n .get(normalizeDbSourcePath(sourcePath)) as NoteRow | undefined;\n return row ? noteRowToEntry(row) : undefined;\n}\n\nexport function getPageRuntimeData(slug: string): VaultPageData | undefined {\n const entry = getPage(slug);\n if (!entry) return undefined;\n return {\n entry,\n config: getConfig(),\n cacheState: getCacheState(),\n };\n}\n\nexport function getRenderKey(slug: string): {\n renderHash: string;\n renderEnvironmentHash: string;\n} {\n const loaded = loadVaultDb();\n const row = loaded.db\n .prepare(\"SELECT render_hash FROM notes WHERE slug = ?\")\n .get(slug) as { render_hash: string } | undefined;\n return {\n renderHash: row?.render_hash ?? \"missing\",\n renderEnvironmentHash: loaded.renderEnvironmentHash,\n };\n}\n\nexport function loadRenderEnvironmentHash(): string {\n return loadVaultDb().renderEnvironmentHash;\n}\n\nexport function getPrerenderSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\n \"SELECT slug FROM notes WHERE prerender = 1 ORDER BY COALESCE(sort_key, slug), slug\",\n )\n .all()\n .map((row) => (row as { slug: string }).slug);\n}\n\nexport function getAllSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\"SELECT slug FROM notes ORDER BY slug\")\n .all()\n .map((row) => (row as { slug: string }).slug);\n}\n\nexport function getNavigation(): Navigation {\n const entries = loadVaultDb()\n .db.prepare(\n `\n SELECT slug, menu_label AS title, sort_key\n FROM notes\n WHERE listed = 1\n ORDER BY COALESCE(sort_key, slug), slug\n `,\n )\n .all() as NavigationRow[];\n return {\n version: 1,\n entries: entries.map((entry) => ({\n slug: entry.slug,\n title: entry.title,\n sortKey: entry.sort_key ?? undefined,\n })),\n };\n}\n\nexport function getBacklinks(slug: string): BacklinkRow[] {\n return loadVaultDb()\n .db.prepare(\n `\n SELECT n.slug, n.title\n FROM links l\n JOIN notes n ON n.slug = l.source_slug\n WHERE l.target_slug = ?\n AND l.kind = 'link'\n ORDER BY n.title COLLATE NOCASE ASC, n.slug\n `,\n )\n .all(slug) as BacklinkRow[];\n}\n\nexport function getConfig(): ResolvedSilicaConfig {\n return loadVaultDb().config;\n}\n\nexport function getCacheState(): RenderCacheState {\n return loadVaultDb().cacheState;\n}\n\nexport function getTagSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\"SELECT DISTINCT tag FROM note_tags ORDER BY tag\")\n .all()\n .map((row) => (row as { tag: string }).tag);\n}\n\nexport function getEntriesForTag(tag: string): ManifestEntry[] {\n return loadVaultDb()\n .db.prepare(\n `\n SELECT n.*\n FROM notes n\n WHERE n.listed = 1\n AND EXISTS (\n SELECT 1\n FROM note_tags nt\n WHERE nt.slug = n.slug\n AND nt.tag = ?\n )\n ORDER BY n.title COLLATE NOCASE ASC, n.slug\n `,\n )\n .all(tag)\n .map((row) => noteRowToEntry(row as NoteRow));\n}\n\nexport function getRelatedTagsForEntries(\n slugs: string[],\n tag: string,\n): string[] {\n if (slugs.length === 0) return [];\n return loadVaultDb()\n .db.prepare(\n `\n SELECT nt.tag, COUNT(*) AS count\n FROM note_tags nt\n WHERE nt.slug IN (${slugs.map(() => \"?\").join(\", \")})\n AND nt.tag != ?\n GROUP BY nt.tag\n ORDER BY count DESC, nt.tag ASC\n LIMIT 12\n `,\n )\n .all(...slugs, tag)\n .map((row) => (row as { tag: string }).tag);\n}\n\nexport function resolveWikiLinkFromDb(\n currentSlug: string,\n target: string,\n strategy: \"absolute\" | \"relative\" | \"shortest\",\n ordering?: { numericPrefixes?: boolean },\n): string | undefined {\n const [rawPath] = target.split(\"#\");\n const slugOptions = ordering ?? getConfig().ordering;\n const normalizedTarget = normalizeSlug(rawPath ?? target, slugOptions);\n const db = loadVaultDb().db;\n\n if (strategy === \"absolute\") {\n return lookupAlias(db, \"absolute\", normalizedTarget);\n }\n\n if (strategy === \"relative\") {\n const relative = resolveRelative(\n asFullSlug(currentSlug),\n normalizedTarget,\n slugOptions,\n );\n const resolved = lookupAlias(db, \"absolute\", relative);\n if (resolved) return resolved;\n }\n\n return (\n lookupAlias(db, \"absolute\", normalizedTarget) ??\n lookupAlias(db, \"absolute\", `${normalizedTarget}/index`) ??\n lookupClosestAlias(\n db,\n currentSlug,\n normalizedTarget.split(\"/\").at(-1) ?? \"\",\n )\n );\n}\n\nexport function resolveAssetFromDb(\n currentSourcePath: string,\n target: string,\n strategy: \"absolute\" | \"relative\" | \"shortest\",\n ordering?: { numericPrefixes?: boolean },\n): string | undefined {\n const assetOptions = ordering ?? getConfig().ordering;\n const normalizedTarget = normalizeAssetReference(target, assetOptions);\n if (!normalizedTarget) return undefined;\n const db = loadVaultDb().db;\n const isExplicitRelative = isExplicitRelativeAssetReference(target);\n\n if (isExplicitRelative || strategy === \"relative\") {\n const relative = resolveRelativeAsset(\n currentSourcePath,\n target,\n assetOptions,\n );\n const resolved = lookupAssetAlias(db, \"absolute\", relative);\n if (resolved || isExplicitRelative) return resolved;\n }\n\n if (strategy === \"absolute\") {\n return lookupAssetAlias(db, \"absolute\", normalizedTarget);\n }\n\n return (\n lookupAssetAlias(db, \"absolute\", normalizedTarget) ??\n lookupAssetAlias(db, \"shortest\", normalizedTarget.split(\"/\").at(-1) ?? \"\")\n );\n}\n\nexport function getBreadcrumbs(slug: string) {\n if (slug === \"index\" || !slug.includes(\"/\")) return [];\n\n const breadcrumbs: Array<{ label: string; href?: string }> = [\n { label: \"Home\", href: \"/\" },\n ];\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),\n });\n }\n return breadcrumbs;\n}\n\nexport function loadSearchIndex(): LoadedSearchIndex {\n const loaded = loadVaultDb();\n return {\n databasePath: loaded.databasePath,\n db: loaded.db,\n close: () => undefined,\n };\n}\n\nexport function normalizeRouteSlug(slug?: string[]): string {\n return slug?.length ? slug.join(\"/\") : \"index\";\n}\n\nfunction readMetadata(db: Database.Database): MetadataRows {\n const rows = db\n .prepare(\"SELECT key, value FROM vault_metadata\")\n .all() as Array<{ key: string; value: string }>;\n const metadata = Object.fromEntries(rows.map((row) => [row.key, row.value]));\n return {\n generatedAt: metadata.generatedAt ?? \"\",\n renderEnvironmentHash: metadata.renderEnvironmentHash ?? \"silica\",\n config: parseConfigMetadata(metadata.configJson),\n cacheState: JSON.parse(metadata.cacheStateJson ?? \"{}\") as RenderCacheState,\n };\n}\n\nfunction parseConfigMetadata(value: string | undefined): ResolvedSilicaConfig {\n if (!value) throw new Error(\"vault.db is missing configJson metadata.\");\n return JSON.parse(value) as ResolvedSilicaConfig;\n}\n\nfunction lookupAlias(\n db: Database.Database,\n strategy: string,\n alias: string,\n): string | undefined {\n const row = db\n .prepare(\n `\n SELECT slug\n FROM slug_aliases\n WHERE strategy_key = ?\n AND alias = ?\n ORDER BY sort_key, slug\n LIMIT 2\n `,\n )\n .all(strategy, alias) as Array<{ slug: string }>;\n return row.length === 1 ? row[0]?.slug : undefined;\n}\n\nfunction lookupClosestAlias(\n db: Database.Database,\n currentSlug: string,\n alias: string,\n): string | undefined {\n const rows = db\n .prepare(\n `\n SELECT slug\n FROM slug_aliases\n WHERE strategy_key = 'shortest'\n AND alias = ?\n ORDER BY slug\n `,\n )\n .all(alias) as Array<{ slug: string }>;\n\n return closestWikiLinkCandidate(\n currentSlug,\n rows.map((row) => row.slug),\n );\n}\n\nfunction lookupAssetAlias(\n db: Database.Database,\n strategy: string,\n alias: string,\n): string | undefined {\n const row = db\n .prepare(\n `\n SELECT asset_path\n FROM asset_aliases\n WHERE strategy_key = ?\n AND alias = ?\n ORDER BY sort_key, asset_path\n LIMIT 2\n `,\n )\n .all(strategy, alias) as Array<{ asset_path: string }>;\n return row.length === 1 ? row[0]?.asset_path : undefined;\n}\n\nfunction closestWikiLinkCandidate(\n currentSlug: string,\n candidates: readonly string[],\n): string | undefined {\n if (candidates.length === 0) return undefined;\n\n const currentDirectory = normalizeSlug(currentSlug, {\n numericPrefixes: false,\n })\n .split(\"/\")\n .slice(0, -1);\n const [bestCandidate] = [...candidates].sort((left, right) => {\n const leftScore = wikiLinkCandidateScore(currentDirectory, left);\n const rightScore = wikiLinkCandidateScore(currentDirectory, right);\n\n return (\n rightScore.sharedPrefixLength - leftScore.sharedPrefixLength ||\n leftScore.depth - rightScore.depth ||\n compareSlugs(left, right)\n );\n });\n\n return bestCandidate;\n}\n\nfunction wikiLinkCandidateScore(currentDirectory: string[], slug: string) {\n const simplified = slug === \"index\" ? \"\" : slug.replace(/\\/index$/, \"\");\n const segments = simplified ? simplified.split(\"/\") : [];\n\n return {\n sharedPrefixLength: sharedPrefixLength(\n currentDirectory,\n segments.slice(0, -1),\n ),\n depth: segments.length,\n };\n}\n\nfunction sharedPrefixLength(left: readonly string[], right: readonly string[]) {\n let length = 0;\n while (left[length] !== undefined && left[length] === right[length]) {\n length += 1;\n }\n return length;\n}\n\nfunction compareSlugs(left: string, right: string) {\n if (left < right) return -1;\n if (left > right) return 1;\n return 0;\n}\n\nfunction isExplicitRelativeAssetReference(value: string): boolean {\n const withoutSuffix = value.split(/[?#]/)[0] ?? \"\";\n const normalized = withoutSuffix\n .replace(/\\\\/g, \"/\")\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\");\n return /^\\.{1,2}\\//.test(normalized);\n}\n\nfunction noteRowToEntry(row: NoteRow): ManifestEntry {\n return {\n slug: row.slug,\n title: row.title,\n menuLabel: row.menu_label,\n description: row.description ?? undefined,\n generatedDescription: row.generated_description ?? undefined,\n tags: parseJsonArray(row.tags_json),\n file: path.isAbsolute(row.file)\n ? row.file\n : path.join(getProjectRoot(), row.file),\n sourcePath: row.source_path,\n sortKey: row.sort_key ?? undefined,\n created: row.created ?? undefined,\n modified: row.modified ?? undefined,\n frontmatter: parseObject(row.frontmatter_json),\n contentHash: row.content_hash,\n embeds: getEmbeds(row.slug),\n };\n}\n\nfunction normalizeDbSourcePath(value: string): string {\n return value\n .replace(/\\\\/g, \"/\")\n .replace(/^\\/+/, \"\")\n .replace(/^content\\//, \"\");\n}\n\nfunction getEmbeds(slug: string): string[] {\n return loadVaultDb()\n .db.prepare(\n \"SELECT target_slug FROM links WHERE source_slug = ? AND kind = 'embed' ORDER BY target_slug\",\n )\n .all(slug)\n .map((row) => (row as { target_slug: string }).target_slug);\n}\n\nfunction breadcrumbSegmentHref(segmentPath: string): string | undefined {\n if (getPage(segmentPath)) return slugToHref(segmentPath);\n const indexSlug = `${segmentPath}/index`;\n if (getPage(indexSlug)) return slugToHref(indexSlug);\n return undefined;\n}\n\nfunction prettySegment(segment: string): string {\n return segment\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction parseObject(value: string): Record<string, unknown> {\n return JSON.parse(value) as Record<string, unknown>;\n}\n\nfunction parseJsonArray(value: string): string[] {\n return JSON.parse(value) as string[];\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { GET } from "@silicajs/next/routes/opengraph-image";
|
|
@@ -7,14 +7,16 @@ import type { NextConfig } from "next";
|
|
|
7
7
|
|
|
8
8
|
const require = createRequire(import.meta.url);
|
|
9
9
|
const nextRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const dataRoot = path.join(nextRoot, "data");
|
|
11
|
+
const turbopackRoot = findTurbopackRoot(nextRoot);
|
|
12
|
+
const tracedDataGlob = `${relativePosixPath(turbopackRoot, dataRoot)}/**/*`;
|
|
13
|
+
const vaultMetadata = readVaultMetadata(path.join(dataRoot, "vault.db"));
|
|
12
14
|
type VaultConfig = {
|
|
13
15
|
assistant?: { provider?: { package?: string } };
|
|
14
|
-
render?: {
|
|
16
|
+
render?: { output?: "standalone" | "default" };
|
|
15
17
|
};
|
|
16
18
|
const resolvedConfig = parseJson<VaultConfig>(vaultMetadata.configJson);
|
|
17
|
-
const
|
|
19
|
+
const standaloneOutput = resolvedConfig?.render?.output === "standalone";
|
|
18
20
|
const serverExternalPackages = [
|
|
19
21
|
"better-sqlite3",
|
|
20
22
|
"just-bash",
|
|
@@ -25,8 +27,13 @@ const serverExternalPackages = [
|
|
|
25
27
|
|
|
26
28
|
const baseNextConfig: NextConfig = {
|
|
27
29
|
cacheComponents: true,
|
|
28
|
-
|
|
30
|
+
// Standalone output and the filesystem cache handler are both self-hosting
|
|
31
|
+
// concerns, enabled together by `render.output: "standalone"`. The default
|
|
32
|
+
// (`"default"`) emits a regular build so the deployment platform's adapter
|
|
33
|
+
// bundles the server and manages its own durable Cache Components handler.
|
|
34
|
+
...(standaloneOutput
|
|
29
35
|
? {
|
|
36
|
+
output: "standalone" as const,
|
|
30
37
|
cacheHandlers: {
|
|
31
38
|
default: require.resolve("./cache-handlers/filesystem-cache.js"),
|
|
32
39
|
remote: require.resolve("./cache-handlers/filesystem-cache.js"),
|
|
@@ -35,7 +42,6 @@ const baseNextConfig: NextConfig = {
|
|
|
35
42
|
: {}),
|
|
36
43
|
generateBuildId: async () => vaultMetadata.renderEnvironmentHash ?? "silica",
|
|
37
44
|
deploymentId: process.env.SILICA_DEPLOYMENT_ID,
|
|
38
|
-
output: "standalone",
|
|
39
45
|
transpilePackages: [
|
|
40
46
|
"@silicajs/core",
|
|
41
47
|
"@silicajs/next",
|
|
@@ -46,11 +52,14 @@ const baseNextConfig: NextConfig = {
|
|
|
46
52
|
"@silicajs/theme-amethyst",
|
|
47
53
|
],
|
|
48
54
|
serverExternalPackages,
|
|
55
|
+
outputFileTracingRoot: turbopackRoot,
|
|
49
56
|
outputFileTracingIncludes: {
|
|
50
|
-
"/*": [
|
|
57
|
+
"/*": [tracedDataGlob],
|
|
58
|
+
},
|
|
59
|
+
turbopack: {
|
|
60
|
+
root: turbopackRoot,
|
|
51
61
|
},
|
|
52
62
|
experimental: {
|
|
53
|
-
externalDir: true,
|
|
54
63
|
serverSourceMaps: process.env.NODE_ENV !== "production",
|
|
55
64
|
},
|
|
56
65
|
};
|
|
@@ -79,6 +88,23 @@ function parseJson<T>(value: string | undefined): T | undefined {
|
|
|
79
88
|
return value ? (JSON.parse(value) as T) : undefined;
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
|
|
91
|
+
function findTurbopackRoot(start: string): string {
|
|
92
|
+
let current = start;
|
|
93
|
+
while (true) {
|
|
94
|
+
if (fs.existsSync(path.join(current, "node_modules/next/package.json"))) {
|
|
95
|
+
return current;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const parent = path.dirname(current);
|
|
99
|
+
if (parent === current) return start;
|
|
100
|
+
current = parent;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function relativePosixPath(from: string, to: string): string {
|
|
105
|
+
return path.relative(from, to).split(path.sep).join("/") || ".";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const nextConfig = baseNextConfig;
|
|
83
109
|
|
|
84
110
|
export default nextConfig;
|
package/dist/templates.d.ts
CHANGED
|
@@ -11,6 +11,6 @@ declare function assistantModuleTemplate(assistantEnabled: boolean): string;
|
|
|
11
11
|
declare function assistantRouteTemplate(assistant: ResolvedSilicaAssistantConfig): string;
|
|
12
12
|
declare function proxyTemplate(config: ResolvedSilicaConfig): string;
|
|
13
13
|
declare function tsconfigTemplate(hasUserTsconfig: boolean): string;
|
|
14
|
-
declare function packageJsonTemplate(): string;
|
|
14
|
+
declare function packageJsonTemplate(dependencies?: Record<string, string>, devDependencies?: Record<string, string>): string;
|
|
15
15
|
|
|
16
16
|
export { type TemplateFile, assistantModuleTemplate, assistantRouteTemplate, getSilicaTemplates, nextConfigTemplate, packageJsonTemplate, proxyTemplate, themeModuleTemplate, tsconfigTemplate };
|
package/dist/templates.js
CHANGED
|
@@ -13,7 +13,7 @@ function nextConfigTemplate(userConfigImport) {
|
|
|
13
13
|
"/* __SILICA_CONFIG_IMPORT__ */",
|
|
14
14
|
userConfigImport ? `import { createJiti } from "jiti";` : ""
|
|
15
15
|
).replace(
|
|
16
|
-
"
|
|
16
|
+
"const nextConfig = baseNextConfig;",
|
|
17
17
|
nextConfigOverride(userConfigImport)
|
|
18
18
|
);
|
|
19
19
|
}
|
|
@@ -82,8 +82,25 @@ function tsconfigTemplate(hasUserTsconfig) {
|
|
|
82
82
|
const rendered = hasUserTsconfig ? template.replaceAll("{{extends}}", "../../tsconfig.json") : template.replace(' "extends": "{{extends}}",\n', "");
|
|
83
83
|
return rendered.trimEnd();
|
|
84
84
|
}
|
|
85
|
-
function packageJsonTemplate() {
|
|
86
|
-
return
|
|
85
|
+
function packageJsonTemplate(dependencies = {}, devDependencies = {}) {
|
|
86
|
+
return `${JSON.stringify(
|
|
87
|
+
{
|
|
88
|
+
private: true,
|
|
89
|
+
name: ".silica-next",
|
|
90
|
+
version: "0.0.0",
|
|
91
|
+
type: "module",
|
|
92
|
+
scripts: {
|
|
93
|
+
dev: "next dev",
|
|
94
|
+
build: "next build",
|
|
95
|
+
start: "next start"
|
|
96
|
+
},
|
|
97
|
+
dependencies,
|
|
98
|
+
devDependencies
|
|
99
|
+
},
|
|
100
|
+
null,
|
|
101
|
+
2
|
|
102
|
+
)}
|
|
103
|
+
`;
|
|
87
104
|
}
|
|
88
105
|
function readTemplateFile(filename) {
|
|
89
106
|
return fs.readFileSync(path.join(templateFilesRoot, filename), "utf8");
|
package/dist/templates.js.map
CHANGED
|
@@ -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 {\n ResolvedSilicaAssistantConfig,\n ResolvedSilicaConfig,\n} 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 \"
|
|
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 {\n ResolvedSilicaAssistantConfig,\n ResolvedSilicaConfig,\n} 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 \"const nextConfig = baseNextConfig;\",\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 assistantModuleTemplate(assistantEnabled: boolean): string {\n if (!assistantEnabled) {\n return `import type { ThemeAssistantSlots } from \"@silicajs/core/theme\";\n\nexport const assistant: ThemeAssistantSlots | undefined = undefined;\n`;\n }\n\n return `import {\n AssistantPanel,\n AssistantProvider,\n AssistantTrigger,\n} from \"@silicajs/assistant/ui\";\nimport type { ThemeAssistantSlots } from \"@silicajs/core/theme\";\n\nexport const assistant: ThemeAssistantSlots | undefined = {\n Provider: AssistantProvider,\n Trigger: AssistantTrigger,\n Panel: AssistantPanel,\n};\n`;\n}\n\nexport function assistantRouteTemplate(\n assistant: ResolvedSilicaAssistantConfig,\n): string {\n return `import * as assistantProvider from ${JSON.stringify(assistant.provider.package)};\nimport { createAssistantRouteHandler } from \"@silicajs/assistant/next\";\n\nexport const POST = createAssistantRouteHandler({\n providerModule: assistantProvider,\n});\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(\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {},\n): string {\n return `${JSON.stringify(\n {\n private: true,\n name: \".silica-next\",\n version: \"0.0.0\",\n type: \"module\",\n scripts: {\n dev: \"next dev\",\n build: \"next build\",\n start: \"next start\",\n },\n dependencies,\n devDependencies,\n },\n null,\n 2,\n )}\\n`;\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.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Next.js runtime, routes, templates, and proxy for Silica.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
"types": "./dist/routes/tags-page.d.ts",
|
|
21
21
|
"import": "./dist/routes/tags-page.js"
|
|
22
22
|
},
|
|
23
|
+
"./routes/opengraph-image": {
|
|
24
|
+
"types": "./dist/routes/opengraph-image.d.ts",
|
|
25
|
+
"import": "./dist/routes/opengraph-image.js"
|
|
26
|
+
},
|
|
23
27
|
"./routes/layout": {
|
|
24
28
|
"types": "./dist/routes/layout.d.ts",
|
|
25
29
|
"import": "./dist/routes/layout.js"
|
|
@@ -96,19 +100,23 @@
|
|
|
96
100
|
"dist"
|
|
97
101
|
],
|
|
98
102
|
"scripts": {
|
|
103
|
+
"prebuild": "node ../../scripts/sync-generated-app-manifest.mjs --write",
|
|
104
|
+
"pretest": "node ../../scripts/sync-generated-app-manifest.mjs --write",
|
|
105
|
+
"pretypecheck": "node ../../scripts/sync-generated-app-manifest.mjs --write",
|
|
106
|
+
"prelint": "node ../../scripts/sync-generated-app-manifest.mjs --write",
|
|
99
107
|
"build": "tsup",
|
|
100
108
|
"test": "vitest run --passWithNoTests",
|
|
101
109
|
"typecheck": "tsc --noEmit",
|
|
102
110
|
"lint": "tsc --noEmit"
|
|
103
111
|
},
|
|
104
112
|
"dependencies": {
|
|
105
|
-
"@silicajs/auth": "^0.1.
|
|
106
|
-
"@silicajs/components": "^0.4.
|
|
107
|
-
"@silicajs/core": "^0.
|
|
113
|
+
"@silicajs/auth": "^0.1.3",
|
|
114
|
+
"@silicajs/components": "^0.4.1",
|
|
115
|
+
"@silicajs/core": "^0.9.0",
|
|
108
116
|
"@silicajs/remark-obsidian": "^0.1.0",
|
|
109
|
-
"@silicajs/search": "^0.3.
|
|
110
|
-
"better-auth": "1.6.
|
|
111
|
-
"better-sqlite3": "^12.
|
|
117
|
+
"@silicajs/search": "^0.3.2",
|
|
118
|
+
"better-auth": "1.6.19",
|
|
119
|
+
"better-sqlite3": "^12.11.1",
|
|
112
120
|
"jiti": "^2.7.0",
|
|
113
121
|
"katex": "^0.17.0"
|
|
114
122
|
},
|
|
@@ -118,9 +126,12 @@
|
|
|
118
126
|
"react-dom": "^19.2.0"
|
|
119
127
|
},
|
|
120
128
|
"devDependencies": {
|
|
121
|
-
"
|
|
129
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
130
|
+
"@types/node": "^25.9.3",
|
|
131
|
+
"next": "^16.2.9",
|
|
122
132
|
"react": "^19.2.6",
|
|
123
|
-
"react-dom": "^19.2.7"
|
|
133
|
+
"react-dom": "^19.2.7",
|
|
134
|
+
"typescript": "^6.0.3"
|
|
124
135
|
},
|
|
125
136
|
"homepage": "https://github.com/agdevhq/silica/tree/main/packages/next#readme",
|
|
126
137
|
"repository": {
|