From abea79edd40179b48c6365a985c06bbed17048a3 Mon Sep 17 00:00:00 2001 From: Ayo Date: Sat, 21 Oct 2023 17:36:34 +0200 Subject: [PATCH] refactor(core): use defineRoute() instead of default export; single object param --- packages/[...index].ts | 262 --------------------------- packages/core/event-handler.mjs | 4 +- site/package.json | 3 +- site/routes/[...index].ts | 4 +- templates/basic/routes/[...index].ts | 4 +- 5 files changed, 8 insertions(+), 269 deletions(-) delete mode 100644 packages/[...index].ts diff --git a/packages/[...index].ts b/packages/[...index].ts deleted file mode 100644 index d5d724c..0000000 --- a/packages/[...index].ts +++ /dev/null @@ -1,262 +0,0 @@ -/** - * McFly SSR logic - */ - -import { ELEMENT_NODE, parse, render, renderSync, walkSync } from "ultrahtml"; -import { parseScript } from "esprima"; -import config from "../app/mcfly.config"; - -const { components: componentType } = config(); - -export default eventHandler(async (event) => { - const { path } = event; - let html = await getHtml(path); - - // transforms - const transforms = [doSetUp, deleteServerScripts]; - if (html) { - for (const transform of transforms) { - html = transform(html.toString()); - } - } - - html = await useFragments(html.toString()); - - if (!!componentType && !!html) { - html = await insertRegistry(html.toString(), componentType); - } - - return html ?? new Response("Not found", { status: 404 }); -}); - -const getHtml = async (path: string) => { - const rawPath = path[path.length - 1] === "/" ? path.slice(0, -1) : path; - const filename = rawPath === "" ? "/index.html" : `${rawPath}.html`; - const fallback = getPath(rawPath + "/index.html"); - const filePath = getPath(filename); - let html = await useStorage().getItem(filePath); - if (!html) html = await useStorage().getItem(fallback); - if (!html) html = await useStorage().getItem(getPath("/404.html")); - - return html; -}; - -function getPath(filename: string) { - return `assets/pages${filename}`; -} - -async function insertRegistry( - html: string, - type: "js" | "ts" -): Promise { - const ast = parse(html); - const componentFiles = await getFiles(type); - const availableComponents = componentFiles.map((key) => - key.replace(`.${type}`, "") - ); - - const usedCustomElements = []; - - walkSync(ast, (node) => { - const usedElement = availableComponents.find((name) => name === node.name); - - if (node.type === ELEMENT_NODE && !!usedElement) { - usedCustomElements.push(usedElement); - } - }); - - // insert registry script to head - if (usedCustomElements.length > 0) { - const registryScript = await buildRegistry(usedCustomElements, type); - walkSync(ast, (node) => { - if (node.type === ELEMENT_NODE && node.name === "head") { - node.children.push(parse(registryScript)); - } - }); - } - - return render(ast); -} - -async function buildRegistry(usedCustomElements: string[], type: "js" | "ts") { - let registryScript = `"; - - return registryScript; -} - -function doSetUp(html: string) { - const ast = parse(html); - const serverScripts = []; - walkSync(ast, (node) => { - const { attributes } = node; - const attributeKeys = Object.keys(attributes ?? {}); - const isServerScript = attributeKeys.some((key) => key.includes("server:")); - if ( - node.type === ELEMENT_NODE && - node.name === "script" && - isServerScript - ) { - const scripts = node.children.map((child) => child.value); - const script = cleanScript(scripts); - serverScripts.push(script); - } - }); - - const setupMap = {}; - serverScripts.forEach((script: string) => { - const { body } = parseScript(script); - const keys = body - .filter((node) => node.type === "VariableDeclaration") - .map((node) => node.declarations[0].id.name); - const constructor = `(function(){}.constructor)(\`${script}; return {${keys.join( - "," - )}}\`);`; - const evalStore = eval(constructor); - Object.assign(setupMap, new evalStore()); - }); - - const regex = /{{(.*?)}}/g; - var match; - - while ((match = regex.exec(html))) { - let [key, value] = match; - value = value.replace(/\s/g, ""); - html = html.replace(key, setupMap[value]); - } - - return html; -} - -function deleteServerScripts(html: string): string { - const ast = parse(html); - walkSync(ast, (node) => { - const { attributes } = node; - const attributeKeys = Object.keys(attributes ?? {}); - const isServerScript = attributeKeys.some((key) => key.includes("server:")); - if (isServerScript) { - node.parent.children.splice(node.parent.children.indexOf(node), 1); - } - }); - - return renderSync(ast); -} - -function cleanScript(scripts: string[]): string { - let script = scripts.map((s) => s.trim()).join(" "); - - script = removeComments(script); - - return script.replace(/\n/g, "").replace(/\s+/g, " "); -} - -function isComment(node) { - return ( - node.type === "Line" || - node.type === "Block" || - node.type === "BlockComment" || - node.type === "LineComment" - ); -} - -function removeComments(script: string) { - const entries = []; - parseScript(script, { comment: true }, function (node, meta) { - if (isComment(node)) { - entries.push({ - start: meta.start.offset, - end: meta.end.offset, - }); - } - }); - - entries - .sort((a, b) => { - return b.end - a.end; - }) - .forEach((n) => { - script = script.slice(0, n.start) + script.slice(n.end); - }); - return script; -} - -async function useFragments(html: string) { - const fragmentFiles = await getFiles("html"); - - const availableFragments = fragmentFiles.reduce((acc, key) => { - return { - ...acc, - [key.replace(".html", "")]: "", - }; - }, {}); - const ast = parse(html); - - for (const key in availableFragments) { - let text: string = await useStorage().getItem( - "assets:components:" + key + ".html" - ); - availableFragments[key] = text.replace(/\n/g, "").replace(/\s+/g, " "); - } - - walkSync(ast, (node) => { - const selector = Object.keys(availableFragments).find( - (name) => name === node.name - ); - - if (node.type === ELEMENT_NODE && !!selector) { - const index = node.parent.children.indexOf(node); - const fragmentNode = parse(availableFragments[selector]); - - replaceSlots(fragmentNode, node); - - node.parent.children[index] = fragmentNode; - } - }); - - return render(ast); -} - -function replaceSlots(fragmentNode, node) { - walkSync(fragmentNode, (n) => { - if (n.type === ELEMENT_NODE && n.name === "slot") { - const index = n.parent.children.indexOf(n); - n.parent.children.splice(index, 1, ...node.children); - } - }); -} - -async function getFiles(type: string) { - return (await useStorage().getKeys("assets:components")) - .map((key) => key.replace("assets:components:", "")) - .filter((key) => key.includes(type)); -} diff --git a/packages/core/event-handler.mjs b/packages/core/event-handler.mjs index bdf5df4..6a190db 100644 --- a/packages/core/event-handler.mjs +++ b/packages/core/event-handler.mjs @@ -4,7 +4,7 @@ import { eventHandler } from "h3"; import { ELEMENT_NODE, parse, render, renderSync, walkSync } from "ultrahtml"; import { parseScript } from "esprima"; -export default (config, storage) => { +export function defineRoute({ config, storage }) { return eventHandler(async (event) => { const { path } = event; const { components: componentType } = config(); @@ -25,7 +25,7 @@ export default (config, storage) => { return html ?? new Response("Not found", { status: 404 }); }); -}; +} const getHtml = async (path, storage) => { const rawPath = path[path.length - 1] === "/" ? path.slice(0, -1) : path; diff --git a/site/package.json b/site/package.json index 0458d5d..7abc911 100644 --- a/site/package.json +++ b/site/package.json @@ -7,7 +7,8 @@ "prepare": "nitropack prepare", "dev": "nitropack dev", "build": "nitropack build", - "preview": "node .output/server/index.mjs" + "preview": "node .output/server/index.mjs", + "build:preview": "npm run build && npm run preview" }, "dependencies": { "esprima": "^4.0.1", diff --git a/site/routes/[...index].ts b/site/routes/[...index].ts index 8973d04..986b716 100644 --- a/site/routes/[...index].ts +++ b/site/routes/[...index].ts @@ -1,6 +1,6 @@ /** * McFly SSR logic */ -import McFly from "@mcflyjs/core/event-handler.mjs"; +import { defineRoute } from "@mcflyjs/core/event-handler.mjs"; import config from "../mcfly.config"; -export default McFly(config, useStorage()); +export default defineRoute({ config, storage: useStorage() }); diff --git a/templates/basic/routes/[...index].ts b/templates/basic/routes/[...index].ts index 8973d04..986b716 100644 --- a/templates/basic/routes/[...index].ts +++ b/templates/basic/routes/[...index].ts @@ -1,6 +1,6 @@ /** * McFly SSR logic */ -import McFly from "@mcflyjs/core/event-handler.mjs"; +import { defineRoute } from "@mcflyjs/core/event-handler.mjs"; import config from "../mcfly.config"; -export default McFly(config, useStorage()); +export default defineRoute({ config, storage: useStorage() });