@elurjs/kit 2.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +874 -0
- package/LICENSE +21 -0
- package/README.md +1330 -0
- package/bin/elur-kit.js +54 -0
- package/dist/lib/action/define.d.cts +72 -0
- package/dist/lib/action/define.d.ts +72 -0
- package/dist/lib/action/error-store.d.cts +29 -0
- package/dist/lib/action/error-store.d.ts +29 -0
- package/dist/lib/action/index.cjs +1 -0
- package/dist/lib/action/index.d.cts +64 -0
- package/dist/lib/action/index.d.ts +64 -0
- package/dist/lib/action/index.js +2 -0
- package/dist/lib/action/origin.d.cts +20 -0
- package/dist/lib/action/origin.d.ts +20 -0
- package/dist/lib/action/scan.d.cts +28 -0
- package/dist/lib/action/scan.d.ts +28 -0
- package/dist/lib/action/server.d.cts +30 -0
- package/dist/lib/action/server.d.ts +30 -0
- package/dist/lib/action-BB6N5yD-.js +111 -0
- package/dist/lib/action-BB6N5yD-.js.map +1 -0
- package/dist/lib/action-DZHihU3Z.cjs +2 -0
- package/dist/lib/action-DZHihU3Z.cjs.map +1 -0
- package/dist/lib/adapters/bun.cjs +30 -0
- package/dist/lib/adapters/bun.cjs.map +1 -0
- package/dist/lib/adapters/bun.d.cts +13 -0
- package/dist/lib/adapters/bun.d.ts +13 -0
- package/dist/lib/adapters/bun.js +65 -0
- package/dist/lib/adapters/bun.js.map +1 -0
- package/dist/lib/adapters/index.cjs +0 -0
- package/dist/lib/adapters/index.d.cts +30 -0
- package/dist/lib/adapters/index.d.ts +30 -0
- package/dist/lib/adapters/index.js +0 -0
- package/dist/lib/adapters/netlify.cjs +10 -0
- package/dist/lib/adapters/netlify.cjs.map +1 -0
- package/dist/lib/adapters/netlify.d.cts +13 -0
- package/dist/lib/adapters/netlify.d.ts +13 -0
- package/dist/lib/adapters/netlify.js +51 -0
- package/dist/lib/adapters/netlify.js.map +1 -0
- package/dist/lib/adapters/node.cjs +46 -0
- package/dist/lib/adapters/node.cjs.map +1 -0
- package/dist/lib/adapters/node.d.cts +13 -0
- package/dist/lib/adapters/node.d.ts +13 -0
- package/dist/lib/adapters/node.js +105 -0
- package/dist/lib/adapters/node.js.map +1 -0
- package/dist/lib/adapters/shared.d.cts +17 -0
- package/dist/lib/adapters/shared.d.ts +17 -0
- package/dist/lib/adapters/vercel.cjs +2 -0
- package/dist/lib/adapters/vercel.cjs.map +1 -0
- package/dist/lib/adapters/vercel.d.cts +9 -0
- package/dist/lib/adapters/vercel.d.ts +9 -0
- package/dist/lib/adapters/vercel.js +62 -0
- package/dist/lib/adapters/vercel.js.map +1 -0
- package/dist/lib/build/build.d.cts +83 -0
- package/dist/lib/build/build.d.ts +83 -0
- package/dist/lib/build/document-shell.d.cts +48 -0
- package/dist/lib/build/document-shell.d.ts +48 -0
- package/dist/lib/build/transform-source.d.cts +26 -0
- package/dist/lib/build/transform-source.d.ts +26 -0
- package/dist/lib/build/vite-build.d.cts +78 -0
- package/dist/lib/build/vite-build.d.ts +78 -0
- package/dist/lib/cache/policy.d.cts +25 -0
- package/dist/lib/cache/policy.d.ts +25 -0
- package/dist/lib/cache.d.cts +13 -0
- package/dist/lib/cache.d.ts +13 -0
- package/dist/lib/capabilities-C0TDQoMY.cjs +2 -0
- package/dist/lib/capabilities-C0TDQoMY.cjs.map +1 -0
- package/dist/lib/capabilities-C7txofmY.js +45 -0
- package/dist/lib/capabilities-C7txofmY.js.map +1 -0
- package/dist/lib/cli/commands.d.cts +15 -0
- package/dist/lib/cli/commands.d.ts +15 -0
- package/dist/lib/cli.cjs +5132 -0
- package/dist/lib/cli.cjs.map +1 -0
- package/dist/lib/cli.d.cts +30 -0
- package/dist/lib/cli.d.ts +30 -0
- package/dist/lib/cli.js +5131 -0
- package/dist/lib/cli.js.map +1 -0
- package/dist/lib/collections-Dbc8aRLf.js +233 -0
- package/dist/lib/collections-Dbc8aRLf.js.map +1 -0
- package/dist/lib/collections-VDy5g9p8.cjs +13 -0
- package/dist/lib/collections-VDy5g9p8.cjs.map +1 -0
- package/dist/lib/config/index.cjs +2 -0
- package/dist/lib/config/index.cjs.map +1 -0
- package/dist/lib/config/index.d.cts +100 -0
- package/dist/lib/config/index.d.ts +100 -0
- package/dist/lib/config/index.js +122 -0
- package/dist/lib/config/index.js.map +1 -0
- package/dist/lib/content/collections.d.cts +69 -0
- package/dist/lib/content/collections.d.ts +69 -0
- package/dist/lib/content/frontmatter.d.cts +20 -0
- package/dist/lib/content/frontmatter.d.ts +20 -0
- package/dist/lib/content/index.cjs +2 -0
- package/dist/lib/content/index.cjs.map +1 -0
- package/dist/lib/content/index.d.cts +5 -0
- package/dist/lib/content/index.d.ts +5 -0
- package/dist/lib/content/index.js +28 -0
- package/dist/lib/content/index.js.map +1 -0
- package/dist/lib/content/markdown.d.cts +6 -0
- package/dist/lib/content/markdown.d.ts +6 -0
- package/dist/lib/content/raw.d.cts +9 -0
- package/dist/lib/content/raw.d.ts +9 -0
- package/dist/lib/content/schema.d.cts +14 -0
- package/dist/lib/content/schema.d.ts +14 -0
- package/dist/lib/errors-0X7pCFNU.cjs +2 -0
- package/dist/lib/errors-0X7pCFNU.cjs.map +1 -0
- package/dist/lib/errors-ePYxDlhU.js +70 -0
- package/dist/lib/errors-ePYxDlhU.js.map +1 -0
- package/dist/lib/errors.d.cts +84 -0
- package/dist/lib/errors.d.ts +84 -0
- package/dist/lib/hydrate-8a3TbQbs.cjs +2 -0
- package/dist/lib/hydrate-8a3TbQbs.cjs.map +1 -0
- package/dist/lib/hydrate-CgZGrTxF.js +126 -0
- package/dist/lib/hydrate-CgZGrTxF.js.map +1 -0
- package/dist/lib/image/index.cjs +2 -0
- package/dist/lib/image/index.cjs.map +1 -0
- package/dist/lib/image/index.d.cts +37 -0
- package/dist/lib/image/index.d.ts +37 -0
- package/dist/lib/image/index.js +49 -0
- package/dist/lib/image/index.js.map +1 -0
- package/dist/lib/image/pipeline.d.cts +37 -0
- package/dist/lib/image/pipeline.d.ts +37 -0
- package/dist/lib/image/registry.cjs +2 -0
- package/dist/lib/image/registry.cjs.map +1 -0
- package/dist/lib/image/registry.d.cts +26 -0
- package/dist/lib/image/registry.d.ts +26 -0
- package/dist/lib/image/registry.js +23 -0
- package/dist/lib/image/registry.js.map +1 -0
- package/dist/lib/image/service.cjs +2 -0
- package/dist/lib/image/service.cjs.map +1 -0
- package/dist/lib/image/service.d.cts +164 -0
- package/dist/lib/image/service.d.ts +164 -0
- package/dist/lib/image/service.js +308 -0
- package/dist/lib/image/service.js.map +1 -0
- package/dist/lib/index.cjs +1 -0
- package/dist/lib/index.cjs.map +1 -0
- package/dist/lib/index.d.cts +38 -0
- package/dist/lib/index.d.ts +38 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/integrations/hooks.d.cts +58 -0
- package/dist/lib/integrations/hooks.d.ts +58 -0
- package/dist/lib/integrations/index.cjs +1 -0
- package/dist/lib/integrations/index.d.cts +18 -0
- package/dist/lib/integrations/index.d.ts +18 -0
- package/dist/lib/integrations/index.js +2 -0
- package/dist/lib/integrations-7dpN1RYB.js +35 -0
- package/dist/lib/integrations-7dpN1RYB.js.map +1 -0
- package/dist/lib/integrations-DDqfWjvH.cjs +2 -0
- package/dist/lib/integrations-DDqfWjvH.cjs.map +1 -0
- package/dist/lib/interpolation-plugin-Bt-KRKVY.js +365 -0
- package/dist/lib/interpolation-plugin-Bt-KRKVY.js.map +1 -0
- package/dist/lib/interpolation-plugin-CMPCDlVJ.cjs +31 -0
- package/dist/lib/interpolation-plugin-CMPCDlVJ.cjs.map +1 -0
- package/dist/lib/island/generate-entry.d.cts +27 -0
- package/dist/lib/island/generate-entry.d.ts +27 -0
- package/dist/lib/island/hydrate.d.cts +33 -0
- package/dist/lib/island/hydrate.d.ts +33 -0
- package/dist/lib/island/index.cjs +1 -0
- package/dist/lib/island/index.d.cts +2 -0
- package/dist/lib/island/index.d.ts +2 -0
- package/dist/lib/island/index.js +1 -0
- package/dist/lib/island/island.d.cts +32 -0
- package/dist/lib/island/island.d.ts +32 -0
- package/dist/lib/island/scan.d.cts +14 -0
- package/dist/lib/island/scan.d.ts +14 -0
- package/dist/lib/manifest/index.cjs +3 -0
- package/dist/lib/manifest/index.cjs.map +1 -0
- package/dist/lib/manifest/index.d.cts +18 -0
- package/dist/lib/manifest/index.d.ts +18 -0
- package/dist/lib/manifest/index.js +110 -0
- package/dist/lib/manifest/index.js.map +1 -0
- package/dist/lib/middleware/index.d.cts +56 -0
- package/dist/lib/middleware/index.d.ts +56 -0
- package/dist/lib/middleware/stream-boundary.d.cts +53 -0
- package/dist/lib/middleware/stream-boundary.d.ts +53 -0
- package/dist/lib/node-http-DRAUhO0c.js +617 -0
- package/dist/lib/node-http-DRAUhO0c.js.map +1 -0
- package/dist/lib/node-http-DhxguYyz.cjs +13 -0
- package/dist/lib/node-http-DhxguYyz.cjs.map +1 -0
- package/dist/lib/render/render-to-string.d.cts +12 -0
- package/dist/lib/render/render-to-string.d.ts +12 -0
- package/dist/lib/render/ssr-flag.d.cts +4 -0
- package/dist/lib/render/ssr-flag.d.ts +4 -0
- package/dist/lib/route-scanner-JY6YrNH5.cjs +2 -0
- package/dist/lib/route-scanner-JY6YrNH5.cjs.map +1 -0
- package/dist/lib/route-scanner-XcLId7lK.js +100 -0
- package/dist/lib/route-scanner-XcLId7lK.js.map +1 -0
- package/dist/lib/router/client.cjs +2 -0
- package/dist/lib/router/client.cjs.map +1 -0
- package/dist/lib/router/client.d.cts +47 -0
- package/dist/lib/router/client.d.ts +47 -0
- package/dist/lib/router/client.js +298 -0
- package/dist/lib/router/client.js.map +1 -0
- package/dist/lib/router/route-scanner.d.cts +50 -0
- package/dist/lib/router/route-scanner.d.ts +50 -0
- package/dist/lib/runtime/capabilities.d.cts +47 -0
- package/dist/lib/runtime/capabilities.d.ts +47 -0
- package/dist/lib/runtime/context.d.cts +140 -0
- package/dist/lib/runtime/context.d.ts +140 -0
- package/dist/lib/runtime/handler.d.cts +39 -0
- package/dist/lib/runtime/handler.d.ts +39 -0
- package/dist/lib/runtime/index.cjs +1 -0
- package/dist/lib/runtime/index.d.cts +6 -0
- package/dist/lib/runtime/index.d.ts +6 -0
- package/dist/lib/runtime/index.js +4 -0
- package/dist/lib/runtime/node-http.d.cts +2 -0
- package/dist/lib/runtime/node-http.d.ts +2 -0
- package/dist/lib/runtime/security-headers.d.cts +13 -0
- package/dist/lib/runtime/security-headers.d.ts +13 -0
- package/dist/lib/runtime/static.d.cts +1 -0
- package/dist/lib/runtime/static.d.ts +1 -0
- package/dist/lib/runtime-CrH5X-Es.cjs +2 -0
- package/dist/lib/runtime-CrH5X-Es.cjs.map +1 -0
- package/dist/lib/runtime-Ds2-wH6P.js +520 -0
- package/dist/lib/runtime-Ds2-wH6P.js.map +1 -0
- package/dist/lib/scan-CY2O9ty6.cjs +2 -0
- package/dist/lib/scan-CY2O9ty6.cjs.map +1 -0
- package/dist/lib/scan-CjMUgnsd.js +22 -0
- package/dist/lib/scan-CjMUgnsd.js.map +1 -0
- package/dist/lib/scan-DXzYtmRQ.cjs +2 -0
- package/dist/lib/scan-DXzYtmRQ.cjs.map +1 -0
- package/dist/lib/scan-Dn93ycXG.js +33 -0
- package/dist/lib/scan-Dn93ycXG.js.map +1 -0
- package/dist/lib/seo/index.cjs +10 -0
- package/dist/lib/seo/index.cjs.map +1 -0
- package/dist/lib/seo/index.d.cts +99 -0
- package/dist/lib/seo/index.d.ts +99 -0
- package/dist/lib/seo/index.js +41 -0
- package/dist/lib/seo/index.js.map +1 -0
- package/dist/lib/shared-CfqOLsKj.cjs +183 -0
- package/dist/lib/shared-CfqOLsKj.cjs.map +1 -0
- package/dist/lib/shared-DU3iQgr7.js +236 -0
- package/dist/lib/shared-DU3iQgr7.js.map +1 -0
- package/dist/lib/ssr/match.d.cts +25 -0
- package/dist/lib/ssr/match.d.ts +25 -0
- package/dist/lib/ssr/render.d.cts +62 -0
- package/dist/lib/ssr/render.d.ts +62 -0
- package/dist/lib/ssr/server.d.cts +41 -0
- package/dist/lib/ssr/server.d.ts +41 -0
- package/dist/lib/ssr/stream.d.cts +48 -0
- package/dist/lib/ssr/stream.d.ts +48 -0
- package/dist/lib/stream-CpOG052V.js +104 -0
- package/dist/lib/stream-CpOG052V.js.map +1 -0
- package/dist/lib/stream-DmZjYqHm.cjs +17 -0
- package/dist/lib/stream-DmZjYqHm.cjs.map +1 -0
- package/dist/lib/types.d.cts +97 -0
- package/dist/lib/types.d.ts +97 -0
- package/dist/lib/vite/index.cjs +2 -0
- package/dist/lib/vite/index.cjs.map +1 -0
- package/dist/lib/vite/index.d.cts +41 -0
- package/dist/lib/vite/index.d.ts +41 -0
- package/dist/lib/vite/index.js +229 -0
- package/dist/lib/vite/index.js.map +1 -0
- package/dist/lib/vite/interpolation-plugin.d.cts +64 -0
- package/dist/lib/vite/interpolation-plugin.d.ts +64 -0
- package/package.json +292 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { t as e } from "./route-scanner-XcLId7lK.js";
|
|
2
|
+
import { resolve as t } from "node:path";
|
|
3
|
+
//#region src/action/scan.ts
|
|
4
|
+
async function n(n) {
|
|
5
|
+
let r = await e(n), i = {};
|
|
6
|
+
for (let e of r.pages) {
|
|
7
|
+
if (!e.actionPath) continue;
|
|
8
|
+
let n = t(e.actionPath), r = await import(n), a = {};
|
|
9
|
+
for (let [e, t] of Object.entries(r)) e !== "default" && typeof t == "function" && (a[e] = n);
|
|
10
|
+
Object.keys(a).length > 0 && (i[e.path] = a);
|
|
11
|
+
}
|
|
12
|
+
return i;
|
|
13
|
+
}
|
|
14
|
+
function r(e) {
|
|
15
|
+
let t = {};
|
|
16
|
+
for (let [n, r] of Object.entries(e)) t[n] = Object.keys(r);
|
|
17
|
+
return t;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { n, r as t };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=scan-CjMUgnsd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-CjMUgnsd.js","names":[],"sources":["../../src/action/scan.ts"],"sourcesContent":["import { resolve, relative } from \"node:path\";\nimport { scanRoutes } from \"../router/route-scanner.js\";\n\n/**\n * Registry of server actions grouped by page path.\n *\n * The outer key is the page URL path (e.g. \"/contact\"). The inner object maps\n * each exported action name to the absolute file path of the `page.action.ts`\n * module that defines it.\n */\nexport type ActionRegistry = Record<string, Record<string, string>>;\n\n/**\n * Scans `page.action.ts` modules and returns a per-page registry of server actions.\n *\n * Only named function exports are collected; default exports are ignored. The\n * registry is keyed by page URL path so the client can resolve actions scoped\n * to a specific page and avoid name collisions between different routes.\n */\nexport async function scanActions(appDir: string): Promise<ActionRegistry> {\n const routes = await scanRoutes(appDir);\n const actions: ActionRegistry = {};\n\n for (const page of routes.pages) {\n if (!page.actionPath) continue;\n const actionPath = resolve(page.actionPath);\n const mod = (await import(actionPath)) as Record<string, unknown>;\n const pageActions: Record<string, string> = {};\n for (const [name, value] of Object.entries(mod)) {\n if (name === \"default\") continue;\n if (typeof value === \"function\") {\n pageActions[name] = actionPath;\n }\n }\n if (Object.keys(pageActions).length > 0) {\n actions[page.path] = pageActions;\n }\n }\n\n return actions;\n}\n\n/**\n * Return a copy of the action registry where every file path is made relative to\n * the given project root. Useful for serializing actions into the HTML shell\n * without exposing absolute server paths.\n */\nexport function relativeActions(actions: ActionRegistry, root: string): ActionRegistry {\n const result: ActionRegistry = {};\n for (const [page, pageActions] of Object.entries(actions)) {\n const entries: Record<string, string> = {};\n for (const [name, actionPath] of Object.entries(pageActions)) {\n entries[name] = relative(root, actionPath);\n }\n result[page] = entries;\n }\n return result;\n}\n\n/**\n * Return only the names of available actions per page, without file paths.\n * This is the safe format to serialize into the HTML shell: the client only\n * needs to know which actions exist, never where they are implemented.\n */\nexport function actionNames(actions: ActionRegistry): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n for (const [page, pageActions] of Object.entries(actions)) {\n result[page] = Object.keys(pageActions);\n }\n return result;\n}\n"],"mappings":";;;AAmBA,eAAsB,EAAY,GAAyC;CACzE,IAAM,IAAS,MAAM,EAAW,CAAM,GAChC,IAA0B,CAAC;CAEjC,KAAK,IAAM,KAAQ,EAAO,OAAO;EAC/B,IAAI,CAAC,EAAK,YAAY;EACtB,IAAM,IAAa,EAAQ,EAAK,UAAU,GACpC,IAAO,MAAM,OAAO,IACpB,IAAsC,CAAC;EAC7C,KAAK,IAAM,CAAC,GAAM,MAAU,OAAO,QAAQ,CAAG,GACxC,MAAS,aACT,OAAO,KAAU,eACnB,EAAY,KAAQ;EAGxB,AAAI,OAAO,KAAK,CAAW,CAAC,CAAC,SAAS,MACpC,EAAQ,EAAK,QAAQ;CAEzB;CAEA,OAAO;AACT;AAwBA,SAAgB,EAAY,GAAmD;CAC7E,IAAM,IAAmC,CAAC;CAC1C,KAAK,IAAM,CAAC,GAAM,MAAgB,OAAO,QAAQ,CAAO,GACtD,EAAO,KAAQ,OAAO,KAAK,CAAW;CAExC,OAAO;AACT"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./route-scanner-JY6YrNH5.cjs");let t=require("node:path");async function n(n){let r=await e.t(n),i={};for(let e of r.pages){if(!e.actionPath)continue;let n=(0,t.resolve)(e.actionPath),r=await import(n),a={};for(let[e,t]of Object.entries(r))e!=="default"&&typeof t==`function`&&(a[e]=n);Object.keys(a).length>0&&(i[e.path]=a)}return i}function r(e){let t={};for(let[n,r]of Object.entries(e))t[n]=Object.keys(r);return t}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return r}});
|
|
2
|
+
//# sourceMappingURL=scan-DXzYtmRQ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-DXzYtmRQ.cjs","names":[],"sources":["../../src/action/scan.ts"],"sourcesContent":["import { resolve, relative } from \"node:path\";\nimport { scanRoutes } from \"../router/route-scanner.js\";\n\n/**\n * Registry of server actions grouped by page path.\n *\n * The outer key is the page URL path (e.g. \"/contact\"). The inner object maps\n * each exported action name to the absolute file path of the `page.action.ts`\n * module that defines it.\n */\nexport type ActionRegistry = Record<string, Record<string, string>>;\n\n/**\n * Scans `page.action.ts` modules and returns a per-page registry of server actions.\n *\n * Only named function exports are collected; default exports are ignored. The\n * registry is keyed by page URL path so the client can resolve actions scoped\n * to a specific page and avoid name collisions between different routes.\n */\nexport async function scanActions(appDir: string): Promise<ActionRegistry> {\n const routes = await scanRoutes(appDir);\n const actions: ActionRegistry = {};\n\n for (const page of routes.pages) {\n if (!page.actionPath) continue;\n const actionPath = resolve(page.actionPath);\n const mod = (await import(actionPath)) as Record<string, unknown>;\n const pageActions: Record<string, string> = {};\n for (const [name, value] of Object.entries(mod)) {\n if (name === \"default\") continue;\n if (typeof value === \"function\") {\n pageActions[name] = actionPath;\n }\n }\n if (Object.keys(pageActions).length > 0) {\n actions[page.path] = pageActions;\n }\n }\n\n return actions;\n}\n\n/**\n * Return a copy of the action registry where every file path is made relative to\n * the given project root. Useful for serializing actions into the HTML shell\n * without exposing absolute server paths.\n */\nexport function relativeActions(actions: ActionRegistry, root: string): ActionRegistry {\n const result: ActionRegistry = {};\n for (const [page, pageActions] of Object.entries(actions)) {\n const entries: Record<string, string> = {};\n for (const [name, actionPath] of Object.entries(pageActions)) {\n entries[name] = relative(root, actionPath);\n }\n result[page] = entries;\n }\n return result;\n}\n\n/**\n * Return only the names of available actions per page, without file paths.\n * This is the safe format to serialize into the HTML shell: the client only\n * needs to know which actions exist, never where they are implemented.\n */\nexport function actionNames(actions: ActionRegistry): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n for (const [page, pageActions] of Object.entries(actions)) {\n result[page] = Object.keys(pageActions);\n }\n return result;\n}\n"],"mappings":"2EAmBA,eAAsB,EAAY,EAAyC,CACzE,IAAM,EAAS,MAAM,EAAA,EAAW,CAAM,EAChC,EAA0B,CAAC,EAEjC,IAAK,IAAM,KAAQ,EAAO,MAAO,CAC/B,GAAI,CAAC,EAAK,WAAY,SACtB,IAAM,GAAA,EAAa,EAAA,QAAA,CAAQ,EAAK,UAAU,EACpC,EAAO,MAAM,OAAO,GACpB,EAAsC,CAAC,EAC7C,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAG,EACxC,IAAS,WACT,OAAO,GAAU,aACnB,EAAY,GAAQ,GAGpB,OAAO,KAAK,CAAW,CAAC,CAAC,OAAS,IACpC,EAAQ,EAAK,MAAQ,EAEzB,CAEA,OAAO,CACT,CAwBA,SAAgB,EAAY,EAAmD,CAC7E,IAAM,EAAmC,CAAC,EAC1C,IAAK,GAAM,CAAC,EAAM,KAAgB,OAAO,QAAQ,CAAO,EACtD,EAAO,GAAQ,OAAO,KAAK,CAAW,EAExC,OAAO,CACT"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readdir as e } from "node:fs/promises";
|
|
2
|
+
import { join as t, relative as n, sep as r } from "node:path";
|
|
3
|
+
//#region src/island/scan.ts
|
|
4
|
+
async function i(n) {
|
|
5
|
+
let r;
|
|
6
|
+
try {
|
|
7
|
+
r = await e(n, {
|
|
8
|
+
withFileTypes: !0,
|
|
9
|
+
encoding: "utf8"
|
|
10
|
+
});
|
|
11
|
+
} catch {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
let a = [];
|
|
15
|
+
for (let e of r) {
|
|
16
|
+
let r = t(n, e.name);
|
|
17
|
+
e.isDirectory() ? a.push(...await i(r)) : e.isFile() && e.name.endsWith(".ts") && !e.name.endsWith(".d.ts") && !e.name.endsWith(".test.ts") && a.push(r);
|
|
18
|
+
}
|
|
19
|
+
return a;
|
|
20
|
+
}
|
|
21
|
+
function a(e, t) {
|
|
22
|
+
return n(e, t).replace(/\.ts$/, "").split(r).join("/");
|
|
23
|
+
}
|
|
24
|
+
async function o(e) {
|
|
25
|
+
return (await i(e)).map((t) => ({
|
|
26
|
+
name: a(e, t),
|
|
27
|
+
filePath: t
|
|
28
|
+
})).sort((e, t) => e.name.localeCompare(t.name));
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { o as t };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=scan-Dn93ycXG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-Dn93ycXG.js","names":[],"sources":["../../src/island/scan.ts"],"sourcesContent":["import { readdir } from \"node:fs/promises\";\nimport type { Dirent } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\n\n// --- Island scanner ---\n//\n// Walks `src/islands/` and lists every island component module. Each `.ts`\n// file (recursively) is treated as one island whose name is derived from its\n// path relative to the islands root:\n//\n// src/islands/LikeButton.ts -> \"LikeButton\"\n// src/islands/nav/MobileMenu.ts -> \"nav/MobileMenu\"\n//\n// The name must match the first argument passed to `island(name, ...)` on the\n// server so the client registry can look the component up during hydration.\n\n/** A single island component discovered by the scanner. */\nexport interface IslandModule {\n /** Registry name, derived from the path relative to the islands dir. */\n name: string;\n /** Absolute file system path to the island module. */\n filePath: string;\n}\n\nasync function walk(dir: string): Promise<string[]> {\n let entries: Dirent<string>[];\n try {\n entries = (await readdir(dir, {\n withFileTypes: true,\n encoding: \"utf8\",\n })) as Dirent<string>[];\n } catch {\n return [];\n }\n\n const files: string[] = [];\n for (const entry of entries) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await walk(full)));\n } else if (\n entry.isFile() &&\n entry.name.endsWith(\".ts\") &&\n !entry.name.endsWith(\".d.ts\") &&\n !entry.name.endsWith(\".test.ts\")\n ) {\n files.push(full);\n }\n }\n return files;\n}\n\nfunction toIslandName(islandsDir: string, filePath: string): string {\n return relative(islandsDir, filePath)\n .replace(/\\.ts$/, \"\")\n .split(sep)\n .join(\"/\");\n}\n\n/**\n * Scans an islands directory for island component modules.\n *\n * @param islandsDir Absolute path to the islands directory (e.g. \"src/islands\").\n * @returns Discovered island modules, sorted by name.\n */\nexport async function scanIslands(islandsDir: string): Promise<IslandModule[]> {\n const files = await walk(islandsDir);\n return files\n .map((filePath) => ({ name: toIslandName(islandsDir, filePath), filePath }))\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n"],"mappings":";;;AAwBA,eAAe,EAAK,GAAgC;CAClD,IAAI;CACJ,IAAI;EACF,IAAW,MAAM,EAAQ,GAAK;GAC5B,eAAe;GACf,UAAU;EACZ,CAAC;CACH,QAAQ;EACN,OAAO,CAAC;CACV;CAEA,IAAM,IAAkB,CAAC;CACzB,KAAK,IAAM,KAAS,GAAS;EAC3B,IAAM,IAAO,EAAK,GAAK,EAAM,IAAI;EACjC,AAAI,EAAM,YAAY,IACpB,EAAM,KAAK,GAAI,MAAM,EAAK,CAAI,CAAE,IAEhC,EAAM,OAAO,KACb,EAAM,KAAK,SAAS,KAAK,KACzB,CAAC,EAAM,KAAK,SAAS,OAAO,KAC5B,CAAC,EAAM,KAAK,SAAS,UAAU,KAE/B,EAAM,KAAK,CAAI;CAEnB;CACA,OAAO;AACT;AAEA,SAAS,EAAa,GAAoB,GAA0B;CAClE,OAAO,EAAS,GAAY,CAAQ,CAAC,CAClC,QAAQ,SAAS,EAAE,CAAC,CACpB,MAAM,CAAG,CAAC,CACV,KAAK,GAAG;AACb;AAQA,eAAsB,EAAY,GAA6C;CAE7E,QAAO,MADa,EAAK,CAAU,EAAA,CAEhC,KAAK,OAAc;EAAE,MAAM,EAAa,GAAY,CAAQ;EAAG;CAAS,EAAE,CAAC,CAC3E,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAChD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("node:fs/promises"),t=require("node:path");async function n(n){let{siteUrl:r,urls:i,outDir:o}=n,s=r.replace(/\/$/,``),c=`<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
3
|
+
${i.map(e=>{let t=typeof e==`string`?{url:e}:e,n=[` <url>`,` <loc>${a(`${s}${t.url.startsWith(`/`)?``:`/`}${t.url}`)}</loc>`];return t.lastmod&&n.push(` <lastmod>${t.lastmod}</lastmod>`),t.changefreq&&n.push(` <changefreq>${t.changefreq}</changefreq>`),t.priority!==void 0&&n.push(` <priority>${t.priority.toFixed(1)}</priority>`),n.push(` </url>`),n.join(`
|
|
4
|
+
`)}).join(`
|
|
5
|
+
`)}
|
|
6
|
+
</urlset>
|
|
7
|
+
`,l=(0,t.join)(o,`sitemap.xml`);return await(0,e.mkdir)((0,t.dirname)(l),{recursive:!0}),await(0,e.writeFile)(l,c,`utf8`),l}async function r(n){let{siteUrl:r,outDir:i,rules:a,disallow:o,sitemapUrl:s}=n,c=r.replace(/\/$/,``),l=[];if(a&&a.length>0)for(let e of a){if(l.push(`User-agent: ${e.userAgent}`),e.allow)for(let t of e.allow)l.push(`Allow: ${t}`);if(e.disallow)for(let t of e.disallow)l.push(`Disallow: ${t}`);e.crawlDelay!==void 0&&l.push(`Crawl-delay: ${e.crawlDelay}`),l.push(``)}else{if(l.push(`User-agent: *`),o&&o.length>0)for(let e of o)l.push(`Disallow: ${e}`);else l.push(`Disallow:`);l.push(``)}let u=s??`${c}/sitemap.xml`;l.push(`Sitemap: ${u}`);let d=l.join(`
|
|
8
|
+
`)+`
|
|
9
|
+
`,f=(0,t.join)(i,`robots.txt`);return await(0,e.mkdir)((0,t.dirname)(f),{recursive:!0}),await(0,e.writeFile)(f,d,`utf8`),f}function i(e){return`<script type="application/ld+json">${JSON.stringify(e).replace(/</g,`\\u003c`).replace(/>/g,`\\u003e`).replace(/&/g,`\\u0026`).replace(/\u2028/g,`\\u2028`).replace(/\u2029/g,`\\u2029`)}<\/script>`}function a(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`)}exports.generateRobots=r,exports.generateSitemap=n,exports.jsonLd=i;
|
|
10
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/seo/index.ts"],"sourcesContent":["/**\n * SEO utilities — sitemap.xml and robots.txt generation.\n *\n * @module\n */\n\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\n\n// Types\n\nexport interface SitemapEntry {\n /** URL path, e.g. \"/docs/getting-started/introduction\". */\n url: string;\n /** Last modification date (ISO 8601 or YYYY-MM-DD). */\n lastmod?: string;\n /** Change frequency: always, hourly, daily, weekly, monthly, yearly, never. */\n changefreq?: \"always\" | \"hourly\" | \"daily\" | \"weekly\" | \"monthly\" | \"yearly\" | \"never\";\n /** Priority 0.0–1.0. */\n priority?: number;\n}\n\nexport interface SitemapConfig {\n /** Base URL of the site, e.g. \"https://example.com\". */\n siteUrl: string;\n /** List of URL entries to include in the sitemap. */\n urls: (SitemapEntry | string)[];\n /** Output directory where sitemap.xml will be written. */\n outDir: string;\n}\n\nexport interface RobotsConfig {\n /** Base URL of the site, e.g. \"https://example.com\". */\n siteUrl: string;\n /** Output directory where robots.txt will be written. */\n outDir: string;\n /** Rules for specific user agents. */\n rules?: RobotsRule[];\n /** Paths to disallow for all crawlers (shorthand for rules). */\n disallow?: string[];\n /** Sitemap URL override. If not set, defaults to `${siteUrl}/sitemap.xml`. */\n sitemapUrl?: string;\n}\n\nexport interface RobotsRule {\n /** User-agent, e.g. \"Googlebot\" or \"*\" for all. */\n userAgent: string;\n /** Paths to disallow. */\n disallow?: string[];\n /** Paths to allow. */\n allow?: string[];\n /** Crawl delay in seconds. */\n crawlDelay?: number;\n}\n\n// Sitemap generation\n\n/**\n * Generates a `sitemap.xml` file from a list of URLs.\n *\n * @example\n * ```ts\n * import { generateSitemap } from \"@elurjs/kit/seo\";\n *\n * await generateSitemap({\n * siteUrl: \"https://elur-kit.dev\",\n * outDir: \"./dist\",\n * urls: [\n * \"/\",\n * \"/docs/introduction\",\n * { url: \"/docs/routing\", changefreq: \"weekly\", priority: 0.8 },\n * ],\n * });\n * ```\n */\nexport async function generateSitemap(config: SitemapConfig): Promise<string> {\n const { siteUrl, urls, outDir } = config;\n const base = siteUrl.replace(/\\/$/, \"\");\n\n const entries: string[] = urls.map((entry) => {\n const e = typeof entry === \"string\" ? { url: entry } : entry;\n const loc = `${base}${e.url.startsWith(\"/\") ? \"\" : \"/\"}${e.url}`;\n const lines = [` <url>`, ` <loc>${escapeXml(loc)}</loc>`];\n if (e.lastmod) lines.push(` <lastmod>${e.lastmod}</lastmod>`);\n if (e.changefreq) lines.push(` <changefreq>${e.changefreq}</changefreq>`);\n if (e.priority !== undefined) lines.push(` <priority>${e.priority.toFixed(1)}</priority>`);\n lines.push(` </url>`);\n return lines.join(\"\\n\");\n });\n\n const xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${entries.join(\"\\n\")}\n</urlset>\n`;\n\n const filePath = join(outDir, \"sitemap.xml\");\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, xml, \"utf8\");\n return filePath;\n}\n\n// Robots.txt generation\n\n/**\n * Generates a `robots.txt` file.\n *\n * @example\n * ```ts\n * import { generateRobots } from \"@elurjs/kit/seo\";\n *\n * await generateRobots({\n * siteUrl: \"https://elur-kit.dev\",\n * outDir: \"./dist\",\n * disallow: [\"/api/\", \"/_elur/\"],\n * });\n * ```\n */\nexport async function generateRobots(config: RobotsConfig): Promise<string> {\n const { siteUrl, outDir, rules, disallow, sitemapUrl } = config;\n const base = siteUrl.replace(/\\/$/, \"\");\n const lines: string[] = [];\n\n if (rules && rules.length > 0) {\n for (const rule of rules) {\n lines.push(`User-agent: ${rule.userAgent}`);\n if (rule.allow) {\n for (const path of rule.allow) lines.push(`Allow: ${path}`);\n }\n if (rule.disallow) {\n for (const path of rule.disallow) lines.push(`Disallow: ${path}`);\n }\n if (rule.crawlDelay !== undefined) {\n lines.push(`Crawl-delay: ${rule.crawlDelay}`);\n }\n lines.push(\"\");\n }\n } else {\n lines.push(\"User-agent: *\");\n if (disallow && disallow.length > 0) {\n for (const path of disallow) lines.push(`Disallow: ${path}`);\n } else {\n lines.push(\"Disallow:\");\n }\n lines.push(\"\");\n }\n\n const sitemap = sitemapUrl ?? `${base}/sitemap.xml`;\n lines.push(`Sitemap: ${sitemap}`);\n\n const content = lines.join(\"\\n\") + \"\\n\";\n const filePath = join(outDir, \"robots.txt\");\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, content, \"utf8\");\n return filePath;\n}\n\n// JSON-LD / Structured data\n\nexport interface JsonLdSchema {\n [key: string]: unknown;\n}\n\n/**\n * Serializes a JSON-LD structured data object into a `<script type=\"application/ld+json\">` tag.\n *\n * @example\n * ```ts\n * import { jsonLd } from \"@elurjs/kit/seo\";\n *\n * const schema = jsonLd({\n * \"@context\": \"https://schema.org\",\n * \"@type\": \"TechArticle\",\n * headline: \"Routing\",\n * author: { \"@type\": \"Person\", name: \"Deiver Vasquez\" },\n * });\n * // Returns: <script type=\"application/ld+json\">{...}</script>\n * ```\n */\nexport function jsonLd(schema: JsonLdSchema | JsonLdSchema[]): string {\n const data = JSON.stringify(Array.isArray(schema) ? schema : schema);\n // Escape sequences that could close the <script> tag or introduce markup.\n // Per the HTML spec, inside a <script> block the only dangerous sequence\n // is \"</script\" (case-insensitive). We also escape \"<\" more broadly to\n // prevent any interpreter from seeing markup-like content, and escape\n // \"<!--\" to prevent HTML comment-based escapes.\n const safe = data\n .replace(/</g, \"\\\\u003c\")\n .replace(/>/g, \"\\\\u003e\")\n .replace(/&/g, \"\\\\u0026\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\n return `<script type=\"application/ld+json\">${safe}</script>`;\n}\n\n// Helpers\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n"],"mappings":"4HA2EA,eAAsB,EAAgB,EAAwC,CAC5E,GAAM,CAAE,UAAS,OAAM,UAAW,EAC5B,EAAO,EAAQ,QAAQ,MAAO,EAAE,EAahC,EAAM;;EAXc,EAAK,IAAK,GAAU,CAC5C,IAAM,EAAI,OAAO,GAAU,SAAW,CAAE,IAAK,CAAM,EAAI,EAEjD,EAAQ,CAAC,UAAW,YAAY,EAAU,GADjC,IAAO,EAAE,IAAI,WAAW,GAAG,EAAI,GAAK,MAAM,EAAE,KACR,EAAE,OAAO,EAK5D,OAJI,EAAE,SAAS,EAAM,KAAK,gBAAgB,EAAE,QAAQ,WAAW,EAC3D,EAAE,YAAY,EAAM,KAAK,mBAAmB,EAAE,WAAW,cAAc,EACvE,EAAE,WAAa,IAAA,IAAW,EAAM,KAAK,iBAAiB,EAAE,SAAS,QAAQ,CAAC,EAAE,YAAY,EAC5F,EAAM,KAAK,UAAU,EACd,EAAM,KAAK;CAAI,CACxB,CAIA,CAAA,CAAQ,KAAK;CAAI,EAAE;;EAIb,GAAA,EAAW,EAAA,KAAA,CAAK,EAAQ,aAAa,EAG3C,OAFA,MAAA,EAAM,EAAA,MAAA,EAAA,EAAM,EAAA,QAAA,CAAQ,CAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,EAClD,MAAA,EAAM,EAAA,UAAA,CAAU,EAAU,EAAK,MAAM,EAC9B,CACT,CAkBA,eAAsB,EAAe,EAAuC,CAC1E,GAAM,CAAE,UAAS,SAAQ,QAAO,WAAU,cAAe,EACnD,EAAO,EAAQ,QAAQ,MAAO,EAAE,EAChC,EAAkB,CAAC,EAEzB,GAAI,GAAS,EAAM,OAAS,EAC1B,IAAK,IAAM,KAAQ,EAAO,CAExB,GADA,EAAM,KAAK,eAAe,EAAK,WAAW,EACtC,EAAK,MACP,IAAK,IAAM,KAAQ,EAAK,MAAO,EAAM,KAAK,UAAU,GAAM,EAE5D,GAAI,EAAK,SACP,IAAK,IAAM,KAAQ,EAAK,SAAU,EAAM,KAAK,aAAa,GAAM,EAE9D,EAAK,aAAe,IAAA,IACtB,EAAM,KAAK,gBAAgB,EAAK,YAAY,EAE9C,EAAM,KAAK,EAAE,CACf,KACK,CAEL,GADA,EAAM,KAAK,eAAe,EACtB,GAAY,EAAS,OAAS,EAChC,IAAK,IAAM,KAAQ,EAAU,EAAM,KAAK,aAAa,GAAM,OAE3D,EAAM,KAAK,WAAW,EAExB,EAAM,KAAK,EAAE,CACf,CAEA,IAAM,EAAU,GAAc,GAAG,EAAK,cACtC,EAAM,KAAK,YAAY,GAAS,EAEhC,IAAM,EAAU,EAAM,KAAK;CAAI,EAAI;EAC7B,GAAA,EAAW,EAAA,KAAA,CAAK,EAAQ,YAAY,EAG1C,OAFA,MAAA,EAAM,EAAA,MAAA,EAAA,EAAM,EAAA,QAAA,CAAQ,CAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,EAClD,MAAA,EAAM,EAAA,UAAA,CAAU,EAAU,EAAS,MAAM,EAClC,CACT,CAwBA,SAAgB,EAAO,EAA+C,CAapE,MAAO,sCAZM,KAAK,UAAkC,CAMvC,CAAA,CACV,QAAQ,KAAM,SAAS,CAAC,CACxB,QAAQ,KAAM,SAAS,CAAC,CACxB,QAAQ,KAAM,SAAS,CAAC,CACxB,QAAQ,UAAW,SAAS,CAAC,CAC7B,QAAQ,UAAW,SACuB,EAAK,WACpD,CAIA,SAAS,EAAU,EAAqB,CACtC,OAAO,EACJ,QAAQ,KAAM,OAAO,CAAC,CACtB,QAAQ,KAAM,MAAM,CAAC,CACrB,QAAQ,KAAM,MAAM,CAAC,CACrB,QAAQ,KAAM,QAAQ,CAAC,CACvB,QAAQ,KAAM,QAAQ,CAC3B"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO utilities — sitemap.xml and robots.txt generation.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
export interface SitemapEntry {
|
|
7
|
+
/** URL path, e.g. "/docs/getting-started/introduction". */
|
|
8
|
+
url: string;
|
|
9
|
+
/** Last modification date (ISO 8601 or YYYY-MM-DD). */
|
|
10
|
+
lastmod?: string;
|
|
11
|
+
/** Change frequency: always, hourly, daily, weekly, monthly, yearly, never. */
|
|
12
|
+
changefreq?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
13
|
+
/** Priority 0.0–1.0. */
|
|
14
|
+
priority?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface SitemapConfig {
|
|
17
|
+
/** Base URL of the site, e.g. "https://example.com". */
|
|
18
|
+
siteUrl: string;
|
|
19
|
+
/** List of URL entries to include in the sitemap. */
|
|
20
|
+
urls: (SitemapEntry | string)[];
|
|
21
|
+
/** Output directory where sitemap.xml will be written. */
|
|
22
|
+
outDir: string;
|
|
23
|
+
}
|
|
24
|
+
export interface RobotsConfig {
|
|
25
|
+
/** Base URL of the site, e.g. "https://example.com". */
|
|
26
|
+
siteUrl: string;
|
|
27
|
+
/** Output directory where robots.txt will be written. */
|
|
28
|
+
outDir: string;
|
|
29
|
+
/** Rules for specific user agents. */
|
|
30
|
+
rules?: RobotsRule[];
|
|
31
|
+
/** Paths to disallow for all crawlers (shorthand for rules). */
|
|
32
|
+
disallow?: string[];
|
|
33
|
+
/** Sitemap URL override. If not set, defaults to `${siteUrl}/sitemap.xml`. */
|
|
34
|
+
sitemapUrl?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface RobotsRule {
|
|
37
|
+
/** User-agent, e.g. "Googlebot" or "*" for all. */
|
|
38
|
+
userAgent: string;
|
|
39
|
+
/** Paths to disallow. */
|
|
40
|
+
disallow?: string[];
|
|
41
|
+
/** Paths to allow. */
|
|
42
|
+
allow?: string[];
|
|
43
|
+
/** Crawl delay in seconds. */
|
|
44
|
+
crawlDelay?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Generates a `sitemap.xml` file from a list of URLs.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { generateSitemap } from "@elurjs/kit/seo";
|
|
52
|
+
*
|
|
53
|
+
* await generateSitemap({
|
|
54
|
+
* siteUrl: "https://elur-kit.dev",
|
|
55
|
+
* outDir: "./dist",
|
|
56
|
+
* urls: [
|
|
57
|
+
* "/",
|
|
58
|
+
* "/docs/introduction",
|
|
59
|
+
* { url: "/docs/routing", changefreq: "weekly", priority: 0.8 },
|
|
60
|
+
* ],
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function generateSitemap(config: SitemapConfig): Promise<string>;
|
|
65
|
+
/**
|
|
66
|
+
* Generates a `robots.txt` file.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { generateRobots } from "@elurjs/kit/seo";
|
|
71
|
+
*
|
|
72
|
+
* await generateRobots({
|
|
73
|
+
* siteUrl: "https://elur-kit.dev",
|
|
74
|
+
* outDir: "./dist",
|
|
75
|
+
* disallow: ["/api/", "/_elur/"],
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare function generateRobots(config: RobotsConfig): Promise<string>;
|
|
80
|
+
export interface JsonLdSchema {
|
|
81
|
+
[key: string]: unknown;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Serializes a JSON-LD structured data object into a `<script type="application/ld+json">` tag.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* import { jsonLd } from "@elurjs/kit/seo";
|
|
89
|
+
*
|
|
90
|
+
* const schema = jsonLd({
|
|
91
|
+
* "@context": "https://schema.org",
|
|
92
|
+
* "@type": "TechArticle",
|
|
93
|
+
* headline: "Routing",
|
|
94
|
+
* author: { "@type": "Person", name: "Deiver Vasquez" },
|
|
95
|
+
* });
|
|
96
|
+
* // Returns: <script type="application/ld+json">{...}</script>
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export declare function jsonLd(schema: JsonLdSchema | JsonLdSchema[]): string;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO utilities — sitemap.xml and robots.txt generation.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
export interface SitemapEntry {
|
|
7
|
+
/** URL path, e.g. "/docs/getting-started/introduction". */
|
|
8
|
+
url: string;
|
|
9
|
+
/** Last modification date (ISO 8601 or YYYY-MM-DD). */
|
|
10
|
+
lastmod?: string;
|
|
11
|
+
/** Change frequency: always, hourly, daily, weekly, monthly, yearly, never. */
|
|
12
|
+
changefreq?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
13
|
+
/** Priority 0.0–1.0. */
|
|
14
|
+
priority?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface SitemapConfig {
|
|
17
|
+
/** Base URL of the site, e.g. "https://example.com". */
|
|
18
|
+
siteUrl: string;
|
|
19
|
+
/** List of URL entries to include in the sitemap. */
|
|
20
|
+
urls: (SitemapEntry | string)[];
|
|
21
|
+
/** Output directory where sitemap.xml will be written. */
|
|
22
|
+
outDir: string;
|
|
23
|
+
}
|
|
24
|
+
export interface RobotsConfig {
|
|
25
|
+
/** Base URL of the site, e.g. "https://example.com". */
|
|
26
|
+
siteUrl: string;
|
|
27
|
+
/** Output directory where robots.txt will be written. */
|
|
28
|
+
outDir: string;
|
|
29
|
+
/** Rules for specific user agents. */
|
|
30
|
+
rules?: RobotsRule[];
|
|
31
|
+
/** Paths to disallow for all crawlers (shorthand for rules). */
|
|
32
|
+
disallow?: string[];
|
|
33
|
+
/** Sitemap URL override. If not set, defaults to `${siteUrl}/sitemap.xml`. */
|
|
34
|
+
sitemapUrl?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface RobotsRule {
|
|
37
|
+
/** User-agent, e.g. "Googlebot" or "*" for all. */
|
|
38
|
+
userAgent: string;
|
|
39
|
+
/** Paths to disallow. */
|
|
40
|
+
disallow?: string[];
|
|
41
|
+
/** Paths to allow. */
|
|
42
|
+
allow?: string[];
|
|
43
|
+
/** Crawl delay in seconds. */
|
|
44
|
+
crawlDelay?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Generates a `sitemap.xml` file from a list of URLs.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { generateSitemap } from "@elurjs/kit/seo";
|
|
52
|
+
*
|
|
53
|
+
* await generateSitemap({
|
|
54
|
+
* siteUrl: "https://elur-kit.dev",
|
|
55
|
+
* outDir: "./dist",
|
|
56
|
+
* urls: [
|
|
57
|
+
* "/",
|
|
58
|
+
* "/docs/introduction",
|
|
59
|
+
* { url: "/docs/routing", changefreq: "weekly", priority: 0.8 },
|
|
60
|
+
* ],
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function generateSitemap(config: SitemapConfig): Promise<string>;
|
|
65
|
+
/**
|
|
66
|
+
* Generates a `robots.txt` file.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { generateRobots } from "@elurjs/kit/seo";
|
|
71
|
+
*
|
|
72
|
+
* await generateRobots({
|
|
73
|
+
* siteUrl: "https://elur-kit.dev",
|
|
74
|
+
* outDir: "./dist",
|
|
75
|
+
* disallow: ["/api/", "/_elur/"],
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare function generateRobots(config: RobotsConfig): Promise<string>;
|
|
80
|
+
export interface JsonLdSchema {
|
|
81
|
+
[key: string]: unknown;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Serializes a JSON-LD structured data object into a `<script type="application/ld+json">` tag.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* import { jsonLd } from "@elurjs/kit/seo";
|
|
89
|
+
*
|
|
90
|
+
* const schema = jsonLd({
|
|
91
|
+
* "@context": "https://schema.org",
|
|
92
|
+
* "@type": "TechArticle",
|
|
93
|
+
* headline: "Routing",
|
|
94
|
+
* author: { "@type": "Person", name: "Deiver Vasquez" },
|
|
95
|
+
* });
|
|
96
|
+
* // Returns: <script type="application/ld+json">{...}</script>
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export declare function jsonLd(schema: JsonLdSchema | JsonLdSchema[]): string;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { mkdir as e, writeFile as t } from "node:fs/promises";
|
|
2
|
+
import { dirname as n, join as r } from "node:path";
|
|
3
|
+
//#region src/seo/index.ts
|
|
4
|
+
async function i(i) {
|
|
5
|
+
let { siteUrl: a, urls: o, outDir: c } = i, l = a.replace(/\/$/, ""), u = `<?xml version="1.0" encoding="UTF-8"?>
|
|
6
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
7
|
+
${o.map((e) => {
|
|
8
|
+
let t = typeof e == "string" ? { url: e } : e, n = [" <url>", ` <loc>${s(`${l}${t.url.startsWith("/") ? "" : "/"}${t.url}`)}</loc>`];
|
|
9
|
+
return t.lastmod && n.push(` <lastmod>${t.lastmod}</lastmod>`), t.changefreq && n.push(` <changefreq>${t.changefreq}</changefreq>`), t.priority !== void 0 && n.push(` <priority>${t.priority.toFixed(1)}</priority>`), n.push(" </url>"), n.join("\n");
|
|
10
|
+
}).join("\n")}
|
|
11
|
+
</urlset>
|
|
12
|
+
`, d = r(c, "sitemap.xml");
|
|
13
|
+
return await e(n(d), { recursive: !0 }), await t(d, u, "utf8"), d;
|
|
14
|
+
}
|
|
15
|
+
async function a(i) {
|
|
16
|
+
let { siteUrl: a, outDir: o, rules: s, disallow: c, sitemapUrl: l } = i, u = a.replace(/\/$/, ""), d = [];
|
|
17
|
+
if (s && s.length > 0) for (let e of s) {
|
|
18
|
+
if (d.push(`User-agent: ${e.userAgent}`), e.allow) for (let t of e.allow) d.push(`Allow: ${t}`);
|
|
19
|
+
if (e.disallow) for (let t of e.disallow) d.push(`Disallow: ${t}`);
|
|
20
|
+
e.crawlDelay !== void 0 && d.push(`Crawl-delay: ${e.crawlDelay}`), d.push("");
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
if (d.push("User-agent: *"), c && c.length > 0) for (let e of c) d.push(`Disallow: ${e}`);
|
|
24
|
+
else d.push("Disallow:");
|
|
25
|
+
d.push("");
|
|
26
|
+
}
|
|
27
|
+
let f = l ?? `${u}/sitemap.xml`;
|
|
28
|
+
d.push(`Sitemap: ${f}`);
|
|
29
|
+
let p = d.join("\n") + "\n", m = r(o, "robots.txt");
|
|
30
|
+
return await e(n(m), { recursive: !0 }), await t(m, p, "utf8"), m;
|
|
31
|
+
}
|
|
32
|
+
function o(e) {
|
|
33
|
+
return `<script type="application/ld+json">${JSON.stringify(e).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029")}<\/script>`;
|
|
34
|
+
}
|
|
35
|
+
function s(e) {
|
|
36
|
+
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { a as generateRobots, i as generateSitemap, o as jsonLd };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/seo/index.ts"],"sourcesContent":["/**\n * SEO utilities — sitemap.xml and robots.txt generation.\n *\n * @module\n */\n\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\n\n// Types\n\nexport interface SitemapEntry {\n /** URL path, e.g. \"/docs/getting-started/introduction\". */\n url: string;\n /** Last modification date (ISO 8601 or YYYY-MM-DD). */\n lastmod?: string;\n /** Change frequency: always, hourly, daily, weekly, monthly, yearly, never. */\n changefreq?: \"always\" | \"hourly\" | \"daily\" | \"weekly\" | \"monthly\" | \"yearly\" | \"never\";\n /** Priority 0.0–1.0. */\n priority?: number;\n}\n\nexport interface SitemapConfig {\n /** Base URL of the site, e.g. \"https://example.com\". */\n siteUrl: string;\n /** List of URL entries to include in the sitemap. */\n urls: (SitemapEntry | string)[];\n /** Output directory where sitemap.xml will be written. */\n outDir: string;\n}\n\nexport interface RobotsConfig {\n /** Base URL of the site, e.g. \"https://example.com\". */\n siteUrl: string;\n /** Output directory where robots.txt will be written. */\n outDir: string;\n /** Rules for specific user agents. */\n rules?: RobotsRule[];\n /** Paths to disallow for all crawlers (shorthand for rules). */\n disallow?: string[];\n /** Sitemap URL override. If not set, defaults to `${siteUrl}/sitemap.xml`. */\n sitemapUrl?: string;\n}\n\nexport interface RobotsRule {\n /** User-agent, e.g. \"Googlebot\" or \"*\" for all. */\n userAgent: string;\n /** Paths to disallow. */\n disallow?: string[];\n /** Paths to allow. */\n allow?: string[];\n /** Crawl delay in seconds. */\n crawlDelay?: number;\n}\n\n// Sitemap generation\n\n/**\n * Generates a `sitemap.xml` file from a list of URLs.\n *\n * @example\n * ```ts\n * import { generateSitemap } from \"@elurjs/kit/seo\";\n *\n * await generateSitemap({\n * siteUrl: \"https://elur-kit.dev\",\n * outDir: \"./dist\",\n * urls: [\n * \"/\",\n * \"/docs/introduction\",\n * { url: \"/docs/routing\", changefreq: \"weekly\", priority: 0.8 },\n * ],\n * });\n * ```\n */\nexport async function generateSitemap(config: SitemapConfig): Promise<string> {\n const { siteUrl, urls, outDir } = config;\n const base = siteUrl.replace(/\\/$/, \"\");\n\n const entries: string[] = urls.map((entry) => {\n const e = typeof entry === \"string\" ? { url: entry } : entry;\n const loc = `${base}${e.url.startsWith(\"/\") ? \"\" : \"/\"}${e.url}`;\n const lines = [` <url>`, ` <loc>${escapeXml(loc)}</loc>`];\n if (e.lastmod) lines.push(` <lastmod>${e.lastmod}</lastmod>`);\n if (e.changefreq) lines.push(` <changefreq>${e.changefreq}</changefreq>`);\n if (e.priority !== undefined) lines.push(` <priority>${e.priority.toFixed(1)}</priority>`);\n lines.push(` </url>`);\n return lines.join(\"\\n\");\n });\n\n const xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${entries.join(\"\\n\")}\n</urlset>\n`;\n\n const filePath = join(outDir, \"sitemap.xml\");\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, xml, \"utf8\");\n return filePath;\n}\n\n// Robots.txt generation\n\n/**\n * Generates a `robots.txt` file.\n *\n * @example\n * ```ts\n * import { generateRobots } from \"@elurjs/kit/seo\";\n *\n * await generateRobots({\n * siteUrl: \"https://elur-kit.dev\",\n * outDir: \"./dist\",\n * disallow: [\"/api/\", \"/_elur/\"],\n * });\n * ```\n */\nexport async function generateRobots(config: RobotsConfig): Promise<string> {\n const { siteUrl, outDir, rules, disallow, sitemapUrl } = config;\n const base = siteUrl.replace(/\\/$/, \"\");\n const lines: string[] = [];\n\n if (rules && rules.length > 0) {\n for (const rule of rules) {\n lines.push(`User-agent: ${rule.userAgent}`);\n if (rule.allow) {\n for (const path of rule.allow) lines.push(`Allow: ${path}`);\n }\n if (rule.disallow) {\n for (const path of rule.disallow) lines.push(`Disallow: ${path}`);\n }\n if (rule.crawlDelay !== undefined) {\n lines.push(`Crawl-delay: ${rule.crawlDelay}`);\n }\n lines.push(\"\");\n }\n } else {\n lines.push(\"User-agent: *\");\n if (disallow && disallow.length > 0) {\n for (const path of disallow) lines.push(`Disallow: ${path}`);\n } else {\n lines.push(\"Disallow:\");\n }\n lines.push(\"\");\n }\n\n const sitemap = sitemapUrl ?? `${base}/sitemap.xml`;\n lines.push(`Sitemap: ${sitemap}`);\n\n const content = lines.join(\"\\n\") + \"\\n\";\n const filePath = join(outDir, \"robots.txt\");\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, content, \"utf8\");\n return filePath;\n}\n\n// JSON-LD / Structured data\n\nexport interface JsonLdSchema {\n [key: string]: unknown;\n}\n\n/**\n * Serializes a JSON-LD structured data object into a `<script type=\"application/ld+json\">` tag.\n *\n * @example\n * ```ts\n * import { jsonLd } from \"@elurjs/kit/seo\";\n *\n * const schema = jsonLd({\n * \"@context\": \"https://schema.org\",\n * \"@type\": \"TechArticle\",\n * headline: \"Routing\",\n * author: { \"@type\": \"Person\", name: \"Deiver Vasquez\" },\n * });\n * // Returns: <script type=\"application/ld+json\">{...}</script>\n * ```\n */\nexport function jsonLd(schema: JsonLdSchema | JsonLdSchema[]): string {\n const data = JSON.stringify(Array.isArray(schema) ? schema : schema);\n // Escape sequences that could close the <script> tag or introduce markup.\n // Per the HTML spec, inside a <script> block the only dangerous sequence\n // is \"</script\" (case-insensitive). We also escape \"<\" more broadly to\n // prevent any interpreter from seeing markup-like content, and escape\n // \"<!--\" to prevent HTML comment-based escapes.\n const safe = data\n .replace(/</g, \"\\\\u003c\")\n .replace(/>/g, \"\\\\u003e\")\n .replace(/&/g, \"\\\\u0026\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\n return `<script type=\"application/ld+json\">${safe}</script>`;\n}\n\n// Helpers\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n"],"mappings":";;;AA2EA,eAAsB,EAAgB,GAAwC;CAC5E,IAAM,EAAE,YAAS,SAAM,cAAW,GAC5B,IAAO,EAAQ,QAAQ,OAAO,EAAE,GAahC,IAAM;;EAXc,EAAK,KAAK,MAAU;EAC5C,IAAM,IAAI,OAAO,KAAU,WAAW,EAAE,KAAK,EAAM,IAAI,GAEjD,IAAQ,CAAC,WAAW,YAAY,EAAU,GADjC,IAAO,EAAE,IAAI,WAAW,GAAG,IAAI,KAAK,MAAM,EAAE,KACR,EAAE,OAAO;EAK5D,OAJI,EAAE,WAAS,EAAM,KAAK,gBAAgB,EAAE,QAAQ,WAAW,GAC3D,EAAE,cAAY,EAAM,KAAK,mBAAmB,EAAE,WAAW,cAAc,GACvE,EAAE,aAAa,KAAA,KAAW,EAAM,KAAK,iBAAiB,EAAE,SAAS,QAAQ,CAAC,EAAE,YAAY,GAC5F,EAAM,KAAK,UAAU,GACd,EAAM,KAAK,IAAI;CACxB,CAIA,CAAA,CAAQ,KAAK,IAAI,EAAE;;GAIb,IAAW,EAAK,GAAQ,aAAa;CAG3C,OAFA,MAAM,EAAM,EAAQ,CAAQ,GAAG,EAAE,WAAW,GAAK,CAAC,GAClD,MAAM,EAAU,GAAU,GAAK,MAAM,GAC9B;AACT;AAkBA,eAAsB,EAAe,GAAuC;CAC1E,IAAM,EAAE,YAAS,WAAQ,UAAO,aAAU,kBAAe,GACnD,IAAO,EAAQ,QAAQ,OAAO,EAAE,GAChC,IAAkB,CAAC;CAEzB,IAAI,KAAS,EAAM,SAAS,GAC1B,KAAK,IAAM,KAAQ,GAAO;EAExB,IADA,EAAM,KAAK,eAAe,EAAK,WAAW,GACtC,EAAK,OACP,KAAK,IAAM,KAAQ,EAAK,OAAO,EAAM,KAAK,UAAU,GAAM;EAE5D,IAAI,EAAK,UACP,KAAK,IAAM,KAAQ,EAAK,UAAU,EAAM,KAAK,aAAa,GAAM;EAKlE,AAHI,EAAK,eAAe,KAAA,KACtB,EAAM,KAAK,gBAAgB,EAAK,YAAY,GAE9C,EAAM,KAAK,EAAE;CACf;MACK;EAEL,IADA,EAAM,KAAK,eAAe,GACtB,KAAY,EAAS,SAAS,GAChC,KAAK,IAAM,KAAQ,GAAU,EAAM,KAAK,aAAa,GAAM;OAE3D,EAAM,KAAK,WAAW;EAExB,EAAM,KAAK,EAAE;CACf;CAEA,IAAM,IAAU,KAAc,GAAG,EAAK;CACtC,EAAM,KAAK,YAAY,GAAS;CAEhC,IAAM,IAAU,EAAM,KAAK,IAAI,IAAI,MAC7B,IAAW,EAAK,GAAQ,YAAY;CAG1C,OAFA,MAAM,EAAM,EAAQ,CAAQ,GAAG,EAAE,WAAW,GAAK,CAAC,GAClD,MAAM,EAAU,GAAU,GAAS,MAAM,GAClC;AACT;AAwBA,SAAgB,EAAO,GAA+C;CAapE,OAAO,sCAZM,KAAK,UAAkC,CAMvC,CAAA,CACV,QAAQ,MAAM,SAAS,CAAC,CACxB,QAAQ,MAAM,SAAS,CAAC,CACxB,QAAQ,MAAM,SAAS,CAAC,CACxB,QAAQ,WAAW,SAAS,CAAC,CAC7B,QAAQ,WAAW,SACuB,EAAK;AACpD;AAIA,SAAS,EAAU,GAAqB;CACtC,OAAO,EACJ,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,QAAQ,CAAC,CACvB,QAAQ,MAAM,QAAQ;AAC3B"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
let e=require("node:fs/promises"),t=require("node:path"),n=require("node:fs");async function r(n,i){await(0,e.mkdir)(i,{recursive:!0});let a=await(0,e.readdir)(n,{withFileTypes:!0});for(let o of a){let a=(0,t.join)(n,o.name),s=(0,t.join)(i,o.name);o.isDirectory()?await r(a,s):await(0,e.copyFile)(a,s)}}function i(e,t,r){t.add(e.pagePath),e.dataPath&&t.add(e.dataPath),e.loadingPath&&t.add(e.loadingPath);for(let r of e.layouts){t.add(r);let e=r.replace(/layout\.ts$/,`layout.data.ts`);e!==r&&(0,n.existsSync)(e)&&t.add(e)}if(e.actionPath){t.add(e.actionPath);let n=r.get(e.path);n||(n=new Set,r.set(e.path,n)),n.add(e.actionPath)}}async function a(e,t,n){let r=new Set,a=new Map;for(let t of e.pages)i(t,r,a);e.error404&&i(e.error404,r,a),e.error500&&i(e.error500,r,a);for(let t of e.api)r.add(t.routePath);let s=Array.from(r),c=new Map(s.map((e,t)=>[e,t])),l=s.map((e,t)=>{let r=o(n,e);return`import * as m_${t} from ${JSON.stringify(r)};`}).join(`
|
|
2
|
+
`),u=e=>`{
|
|
3
|
+
path: ${JSON.stringify(e.path)},
|
|
4
|
+
pagePath: ${JSON.stringify(e.pagePath)},
|
|
5
|
+
dataPath: ${JSON.stringify(e.dataPath??null)},
|
|
6
|
+
actionPath: ${JSON.stringify(e.actionPath??null)},
|
|
7
|
+
loadingPath: ${JSON.stringify(e.loadingPath??null)},
|
|
8
|
+
layouts: ${JSON.stringify(e.layouts)},
|
|
9
|
+
params: ${JSON.stringify(e.params)},
|
|
10
|
+
}`,d=e.pages.map(u).join(`,
|
|
11
|
+
`),f=e.api.map(e=>{let t=c.get(e.routePath);return` { path: ${JSON.stringify(e.path)}, routePath: m_${t} },`}).join(`
|
|
12
|
+
`),p=Array.from(a.entries()).map(([e,t])=>{let n=Array.from(t).map(e=>{let t=c.get(e);return` [${JSON.stringify(e)}, m_${t}],`}).join(`
|
|
13
|
+
`);return` [${JSON.stringify(e)}, new Map([\n${n}\n ])],`}).join(`
|
|
14
|
+
`),m={};for(let t of e.pages){if(!t.actionPath)continue;let e=await import(t.actionPath),n=[];for(let[t,r]of Object.entries(e))t!=="default"&&typeof r==`function`&&n.push(t);n.length>0&&(m[t.path]=n)}return`// AUTO-GENERATED by @elurjs/kit. Do not edit.
|
|
15
|
+
import { handleActionRequest, matchApiRoute, matchRoute, renderPage, renderErrorPage } from "@elurjs/kit";
|
|
16
|
+
${l}
|
|
17
|
+
|
|
18
|
+
const registry = new Map<string, unknown>([
|
|
19
|
+
${s.map((e,t)=>` [${JSON.stringify(e)}, m_${t}],`).join(`
|
|
20
|
+
`)}
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const pages = [
|
|
24
|
+
${d},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const apiRoutes = [
|
|
28
|
+
${f}
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const actionModules = new Map<string, Map<string, unknown>>([
|
|
32
|
+
${p}
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const actions = ${JSON.stringify(m)};
|
|
36
|
+
|
|
37
|
+
const routes = {
|
|
38
|
+
pages,
|
|
39
|
+
api: apiRoutes,
|
|
40
|
+
error404: ${e.error404?u(e.error404):`undefined`},
|
|
41
|
+
error500: ${e.error500?u(e.error500):`undefined`},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const clientEntry = ${JSON.stringify(t.clientEntry)};
|
|
45
|
+
const lang = ${JSON.stringify(t.lang)};
|
|
46
|
+
|
|
47
|
+
function loadModule(path: string) {
|
|
48
|
+
const mod = registry.get(path);
|
|
49
|
+
if (mod) return mod;
|
|
50
|
+
throw new Error(\`Module not found in registry: \${path}\`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function resolveAction(name: string, page?: string) {
|
|
54
|
+
// Match concrete page paths (e.g. /movies/inception) to their route pattern
|
|
55
|
+
// (/movies/:slug) so actions on dynamic routes resolve by scope.
|
|
56
|
+
let pageKey: string | undefined;
|
|
57
|
+
if (page) {
|
|
58
|
+
pageKey = routes.pages.some((route) => route.path === page)
|
|
59
|
+
? page
|
|
60
|
+
: (matchRoute(page, routes.pages)?.route.path ?? page);
|
|
61
|
+
}
|
|
62
|
+
const pageModules = pageKey ? actionModules.get(pageKey) : undefined;
|
|
63
|
+
const candidates = pageModules ? [...pageModules.values()] : [];
|
|
64
|
+
if (!pageModules) {
|
|
65
|
+
for (const mods of actionModules.values()) {
|
|
66
|
+
for (const mod of mods.values()) {
|
|
67
|
+
const action = (mod as Record<string, unknown>)[name];
|
|
68
|
+
if (typeof action === "function") return action;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const mod of candidates) {
|
|
73
|
+
const action = (mod as Record<string, unknown>)[name];
|
|
74
|
+
if (typeof action === "function") {
|
|
75
|
+
return action as (...args: unknown[]) => unknown;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default async function handler(request: Request): Promise<Response> {
|
|
82
|
+
const url = new URL(request.url);
|
|
83
|
+
|
|
84
|
+
if (url.pathname === "/__elur-js/actions") {
|
|
85
|
+
return handleActionRequest(request, resolveAction);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Render endpoint used by the SPA router and streaming boundaries.
|
|
89
|
+
if (url.pathname === "/__elur-js/render") {
|
|
90
|
+
const page = url.searchParams.get("page") ?? "/";
|
|
91
|
+
const search = url.searchParams.get("search") ?? "";
|
|
92
|
+
const wantsJson = (request.headers.get("Accept") ?? "").includes("application/json");
|
|
93
|
+
try {
|
|
94
|
+
const match = matchRoute(page, routes.pages);
|
|
95
|
+
if (!match) throw new Error(\`No route found for \${page}\`);
|
|
96
|
+
const { html } = await renderPage({
|
|
97
|
+
route: match.route,
|
|
98
|
+
params: match.params,
|
|
99
|
+
searchParams: new URLSearchParams(search),
|
|
100
|
+
config: { lang, clientEntry },
|
|
101
|
+
importer: loadModule,
|
|
102
|
+
actions,
|
|
103
|
+
request,
|
|
104
|
+
});
|
|
105
|
+
const bodyStart = html.indexOf('<div id="app">');
|
|
106
|
+
const bodyEnd = html.lastIndexOf("</div>");
|
|
107
|
+
const body = bodyStart >= 0 && bodyEnd > bodyStart
|
|
108
|
+
? html.slice(bodyStart + 13, bodyEnd).trim()
|
|
109
|
+
: html;
|
|
110
|
+
const titleStart = html.indexOf("<title>");
|
|
111
|
+
const titleEnd = html.indexOf("</title>");
|
|
112
|
+
const title = titleStart >= 0 && titleEnd > titleStart
|
|
113
|
+
? html.slice(titleStart + 7, titleEnd)
|
|
114
|
+
: "";
|
|
115
|
+
if (wantsJson) {
|
|
116
|
+
return new Response(JSON.stringify({ title, body }), {
|
|
117
|
+
status: 200,
|
|
118
|
+
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return new Response(body, {
|
|
122
|
+
status: 200,
|
|
123
|
+
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
124
|
+
});
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if ((err as { name?: string }).name === "RouteNotFoundError") {
|
|
127
|
+
return new Response("Not Found", {
|
|
128
|
+
status: 404,
|
|
129
|
+
headers: { "Content-Type": "text/plain" },
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
console.error("[elur-kit] render endpoint error:", err);
|
|
133
|
+
return new Response("Internal Server Error", {
|
|
134
|
+
status: 500,
|
|
135
|
+
headers: { "Content-Type": "text/plain" },
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const apiMatch = matchApiRoute(url.pathname, apiRoutes);
|
|
141
|
+
if (apiMatch) {
|
|
142
|
+
const mod = apiMatch.route.routePath as Record<
|
|
143
|
+
string,
|
|
144
|
+
(request: Request, context?: { params: Record<string, string | string[]> }) => unknown
|
|
145
|
+
>;
|
|
146
|
+
const handler = mod[request.method ?? "GET"];
|
|
147
|
+
if (typeof handler !== "function") {
|
|
148
|
+
return new Response("Method not allowed: " + request.method, { status: 405, headers: { "Content-Type": "text/plain" } });
|
|
149
|
+
}
|
|
150
|
+
return (await handler(request, { params: apiMatch.params })) as Response;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const match = matchRoute(url.pathname, routes.pages);
|
|
154
|
+
if (!match) {
|
|
155
|
+
const errorResult = await renderErrorPage({ routes, status: 404, config: { lang, clientEntry }, actions, importer: loadModule });
|
|
156
|
+
if (errorResult) {
|
|
157
|
+
return new Response(errorResult.html, { status: errorResult.status, headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
158
|
+
}
|
|
159
|
+
return new Response("Not Found", { status: 404, headers: { "Content-Type": "text/plain" } });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const { html } = await renderPage({
|
|
164
|
+
route: match.route,
|
|
165
|
+
params: match.params,
|
|
166
|
+
searchParams: new URLSearchParams(url.search),
|
|
167
|
+
config: { lang, clientEntry },
|
|
168
|
+
importer: loadModule,
|
|
169
|
+
actions,
|
|
170
|
+
request,
|
|
171
|
+
});
|
|
172
|
+
return new Response(html, { status: 200, headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error("[elur-kit] SSR render error:", err);
|
|
175
|
+
const errorResult = await renderErrorPage({ routes, status: 500, error: err, config: { lang, clientEntry }, actions, importer: loadModule });
|
|
176
|
+
if (errorResult) {
|
|
177
|
+
return new Response(errorResult.html, { status: errorResult.status, headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
178
|
+
}
|
|
179
|
+
return new Response("Internal Server Error", { status: 500, headers: { "Content-Type": "text/plain; charset=utf-8" } });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
`}function o(e,n){return(0,t.relative)(e,n).split(`\\`).join(`/`)}async function s(n,r,i){await(0,e.writeFile)(n,await a(r,i,(0,t.dirname)(n)),`utf8`)}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return r}});
|
|
183
|
+
//# sourceMappingURL=shared-CfqOLsKj.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-CfqOLsKj.cjs","names":[],"sources":["../../src/adapters/shared.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, readdir, copyFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative } from \"node:path\";\nimport { scanRoutes } from \"../router/route-scanner.js\";\nimport type { AdapterOptions } from \"./index.js\";\nimport type { PageRoute } from \"../router/route-scanner.js\";\n\n/**\n * Shared helper: copy a directory recursively.\n */\nexport async function copyStatic(from: string, to: string): Promise<void> {\n await mkdir(to, { recursive: true });\n const entries = await readdir(from, { withFileTypes: true });\n for (const entry of entries) {\n const src = join(from, entry.name);\n const dest = join(to, entry.name);\n if (entry.isDirectory()) {\n await copyStatic(src, dest);\n } else {\n await copyFile(src, dest);\n }\n }\n}\n\n/** Adds every module the SSR runtime may import for a page to the registry. */\nfunction collectPageModules(\n page: PageRoute,\n moduleSet: Set<string>,\n actionPathsByPage: Map<string, Set<string>>,\n): void {\n moduleSet.add(page.pagePath);\n if (page.dataPath) moduleSet.add(page.dataPath);\n if (page.loadingPath) moduleSet.add(page.loadingPath);\n for (const layout of page.layouts) {\n moduleSet.add(layout);\n const layoutDataPath = layout.replace(/layout\\.ts$/, \"layout.data.ts\");\n if (layoutDataPath !== layout && existsSync(layoutDataPath)) {\n moduleSet.add(layoutDataPath);\n }\n }\n if (page.actionPath) {\n moduleSet.add(page.actionPath);\n let set = actionPathsByPage.get(page.path);\n if (!set) {\n set = new Set<string>();\n actionPathsByPage.set(page.path, set);\n }\n set.add(page.actionPath);\n }\n}\n\n/**\n * Build a self-contained SSR entry file for a platform adapter.\n * The generated module exports a default `handler(request: Request): Response`\n * and embeds the full route table plus a registry of all page/layout/data/\n * action modules so the runtime never touches the file system.\n */\nexport async function buildSsrEntry(\n routes: Awaited<ReturnType<typeof scanRoutes>>,\n options: AdapterOptions,\n entryDir: string,\n): Promise<string> {\n // Collect all module paths that the SSR runtime may need to import.\n const moduleSet = new Set<string>();\n const actionPathsByPage = new Map<string, Set<string>>();\n for (const page of routes.pages) {\n collectPageModules(page, moduleSet, actionPathsByPage);\n }\n if (routes.error404) collectPageModules(routes.error404, moduleSet, actionPathsByPage);\n if (routes.error500) collectPageModules(routes.error500, moduleSet, actionPathsByPage);\n for (const api of routes.api) {\n moduleSet.add(api.routePath);\n }\n const modules = Array.from(moduleSet);\n const moduleIndex = new Map(modules.map((path, index) => [path, index]));\n\n const imports = modules\n .map((path, index) => {\n const rel = relativeToPosix(entryDir, path);\n return `import * as m_${index} from ${JSON.stringify(rel)};`;\n })\n .join(\"\\n\");\n\n const renderPageRecord = (page: PageRoute): string => `{\n path: ${JSON.stringify(page.path)},\n pagePath: ${JSON.stringify(page.pagePath)},\n dataPath: ${JSON.stringify(page.dataPath ?? null)},\n actionPath: ${JSON.stringify(page.actionPath ?? null)},\n loadingPath: ${JSON.stringify(page.loadingPath ?? null)},\n layouts: ${JSON.stringify(page.layouts)},\n params: ${JSON.stringify(page.params)},\n }`;\n\n const pages = routes.pages.map(renderPageRecord).join(\",\\n\");\n\n const apiRoutes = routes.api\n .map((api) => {\n const index = moduleIndex.get(api.routePath);\n return ` { path: ${JSON.stringify(api.path)}, routePath: m_${index} },`;\n })\n .join(\"\\n\");\n\n const actionModules = Array.from(actionPathsByPage.entries())\n .map(([pagePath, paths]) => {\n const entries = Array.from(paths)\n .map((path) => {\n const index = moduleIndex.get(path);\n return ` [${JSON.stringify(path)}, m_${index}],`;\n })\n .join(\"\\n\");\n return ` [${JSON.stringify(pagePath)}, new Map([\\n${entries}\\n ])],`;\n })\n .join(\"\\n\");\n\n const actionsRegistry: Record<string, string[]> = {};\n for (const page of routes.pages) {\n if (!page.actionPath) continue;\n const mod = (await import(page.actionPath)) as Record<string, unknown>;\n const names: string[] = [];\n for (const [name, value] of Object.entries(mod)) {\n if (name === \"default\") continue;\n if (typeof value === \"function\") {\n names.push(name);\n }\n }\n if (names.length > 0) {\n actionsRegistry[page.path] = names;\n }\n }\n\n return `// AUTO-GENERATED by @elurjs/kit. Do not edit.\nimport { handleActionRequest, matchApiRoute, matchRoute, renderPage, renderErrorPage } from \"@elurjs/kit\";\n${imports}\n\nconst registry = new Map<string, unknown>([\n${modules.map((path, index) => ` [${JSON.stringify(path)}, m_${index}],`).join(\"\\n\")}\n]);\n\nconst pages = [\n${pages},\n];\n\nconst apiRoutes = [\n${apiRoutes}\n];\n\nconst actionModules = new Map<string, Map<string, unknown>>([\n${actionModules}\n]);\n\nconst actions = ${JSON.stringify(actionsRegistry)};\n\nconst routes = {\n pages,\n api: apiRoutes,\n error404: ${routes.error404 ? renderPageRecord(routes.error404) : \"undefined\"},\n error500: ${routes.error500 ? renderPageRecord(routes.error500) : \"undefined\"},\n};\n\nconst clientEntry = ${JSON.stringify(options.clientEntry)};\nconst lang = ${JSON.stringify(options.lang)};\n\nfunction loadModule(path: string) {\n const mod = registry.get(path);\n if (mod) return mod;\n throw new Error(\\`Module not found in registry: \\${path}\\`);\n}\n\nasync function resolveAction(name: string, page?: string) {\n // Match concrete page paths (e.g. /movies/inception) to their route pattern\n // (/movies/:slug) so actions on dynamic routes resolve by scope.\n let pageKey: string | undefined;\n if (page) {\n pageKey = routes.pages.some((route) => route.path === page)\n ? page\n : (matchRoute(page, routes.pages)?.route.path ?? page);\n }\n const pageModules = pageKey ? actionModules.get(pageKey) : undefined;\n const candidates = pageModules ? [...pageModules.values()] : [];\n if (!pageModules) {\n for (const mods of actionModules.values()) {\n for (const mod of mods.values()) {\n const action = (mod as Record<string, unknown>)[name];\n if (typeof action === \"function\") return action;\n }\n }\n }\n for (const mod of candidates) {\n const action = (mod as Record<string, unknown>)[name];\n if (typeof action === \"function\") {\n return action as (...args: unknown[]) => unknown;\n }\n }\n return undefined;\n}\n\nexport default async function handler(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n if (url.pathname === \"/__elur-js/actions\") {\n return handleActionRequest(request, resolveAction);\n }\n\n // Render endpoint used by the SPA router and streaming boundaries.\n if (url.pathname === \"/__elur-js/render\") {\n const page = url.searchParams.get(\"page\") ?? \"/\";\n const search = url.searchParams.get(\"search\") ?? \"\";\n const wantsJson = (request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n try {\n const match = matchRoute(page, routes.pages);\n if (!match) throw new Error(\\`No route found for \\${page}\\`);\n const { html } = await renderPage({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(search),\n config: { lang, clientEntry },\n importer: loadModule,\n actions,\n request,\n });\n const bodyStart = html.indexOf('<div id=\"app\">');\n const bodyEnd = html.lastIndexOf(\"</div>\");\n const body = bodyStart >= 0 && bodyEnd > bodyStart\n ? html.slice(bodyStart + 13, bodyEnd).trim()\n : html;\n const titleStart = html.indexOf(\"<title>\");\n const titleEnd = html.indexOf(\"</title>\");\n const title = titleStart >= 0 && titleEnd > titleStart\n ? html.slice(titleStart + 7, titleEnd)\n : \"\";\n if (wantsJson) {\n return new Response(JSON.stringify({ title, body }), {\n status: 200,\n headers: { \"Content-Type\": \"application/json; charset=utf-8\" },\n });\n }\n return new Response(body, {\n status: 200,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n } catch (err) {\n if ((err as { name?: string }).name === \"RouteNotFoundError\") {\n return new Response(\"Not Found\", {\n status: 404,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n console.error(\"[elur-kit] render endpoint error:\", err);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n }\n\n const apiMatch = matchApiRoute(url.pathname, apiRoutes);\n if (apiMatch) {\n const mod = apiMatch.route.routePath as Record<\n string,\n (request: Request, context?: { params: Record<string, string | string[]> }) => unknown\n >;\n const handler = mod[request.method ?? \"GET\"];\n if (typeof handler !== \"function\") {\n return new Response(\"Method not allowed: \" + request.method, { status: 405, headers: { \"Content-Type\": \"text/plain\" } });\n }\n return (await handler(request, { params: apiMatch.params })) as Response;\n }\n\n const match = matchRoute(url.pathname, routes.pages);\n if (!match) {\n const errorResult = await renderErrorPage({ routes, status: 404, config: { lang, clientEntry }, actions, importer: loadModule });\n if (errorResult) {\n return new Response(errorResult.html, { status: errorResult.status, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n }\n return new Response(\"Not Found\", { status: 404, headers: { \"Content-Type\": \"text/plain\" } });\n }\n\n try {\n const { html } = await renderPage({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(url.search),\n config: { lang, clientEntry },\n importer: loadModule,\n actions,\n request,\n });\n return new Response(html, { status: 200, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n } catch (err) {\n console.error(\"[elur-kit] SSR render error:\", err);\n const errorResult = await renderErrorPage({ routes, status: 500, error: err, config: { lang, clientEntry }, actions, importer: loadModule });\n if (errorResult) {\n return new Response(errorResult.html, { status: errorResult.status, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n }\n return new Response(\"Internal Server Error\", { status: 500, headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n}\n`;\n}\n\nfunction relativeToPosix(from: string, to: string): string {\n return relative(from, to).split(\"\\\\\").join(\"/\");\n}\n\n/**\n * Write a generated SSR entry file for an adapter.\n */\nexport async function writeSsrEntry(\n entryPath: string,\n routes: Awaited<ReturnType<typeof scanRoutes>>,\n options: AdapterOptions,\n): Promise<void> {\n await writeFile(\n entryPath,\n await buildSsrEntry(routes, options, dirname(entryPath)),\n \"utf8\",\n );\n}\n"],"mappings":"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"}
|