| author | |
| committer | |
| log | 18dc72c445e5feaed852e15d649d21e28992cb81 |
| tree | 42a9ebbfb428acada18737904008ad47a6d3db7c |
| parent | b2d921bae3440a3ea449d551a78cf81e4b13bd78 |
| signature | Commit is signed but in an unrecognized format. |
this is not React and i want to make it extremely clear. i don't think
`children` is a great word for the property. this now also lets you do
this with meta tags:
<meta name="description>
enter your long description here
</meta>
but i find this not as compelling due to `meta.ts`17 files changed, 83 insertions(+), 54 deletions(-)
framework/engine/marko-runtime.ts+8-2| ... | ... | @@ -7,12 +7,18 @@ export const _template: typeof marko._template = ( |
| 7 | 7 | renderer, |
| 8 | 8 | ) => { |
| 9 | 9 | const { render: renderFn } = marko._template(templateId, renderer); |
| 10 | function wrap(props: Record<string, unknown>, n: number) { | |
| 10 | function wrap({ content, ...attrs }: Record<string, unknown>, n: number) { | |
| 11 | 11 | // Marko Custom Tags |
| 12 | 12 | const cloverAsyncMarker = { isAsync: false }; |
| 13 | 13 | const r = render.current; |
| 14 | 14 | // Support using Marko outside of Clover SSR |
| 15 | if (!r) return renderer(props, n); | |
| 15 | const props = { | |
| 16 | content: content ? () => content : undefined, | |
| 17 | ...attrs, | |
| 18 | }; | |
| 19 | if (!r) { | |
| 20 | return renderer(props, n); | |
| 21 | } | |
| 16 | 22 | render.setCurrent(null); |
| 17 | 23 | const markoResult = renderFn.call(renderer, { |
| 18 | 24 | ...props, |
framework/generate.ts+1-1| ... | ... | @@ -286,7 +286,7 @@ export async function preparePage( |
| 286 | 286 | // -- html -- |
| 287 | 287 | let page = render.element(Page); |
| 288 | 288 | if (layout?.default) { |
| 289 | page = render.element(layout.default, { children: page }); | |
| 289 | page = render.element(layout.default, { content: page }); | |
| 290 | 290 | } |
| 291 | 291 | |
| 292 | 292 | const sitegen = sg.context.init(); |
framework/global.d.ts+11| ... | ... | @@ -1,2 +1,13 @@ |
| 1 | 1 | type Timer = ReturnType<typeof setTimeout>; |
| 2 | 2 | type Awaitable<T> = T | Promise<T>; |
| 3 | ||
| 4 | declare global { | |
| 5 | namespace JSX { | |
| 6 | export type IntrinsicElements = jsx.ts.IntrinsicElements; | |
| 7 | export type ElementChildrenAttribute = jsx.ts.ElementChildrenAttribute; | |
| 8 | export type Element = jsx.ts.Element; | |
| 9 | export type ElementType = jsx.ts.ElementType; | |
| 10 | export type ElementClass = jsx.ts.ElementClass; | |
| 11 | } | |
| 12 | } | |
| 13 | import * as jsx from "@clo/lib/render/jsx-runtime.ts"; |
framework/lib/view.ts+1-1| ... | ... | @@ -72,7 +72,7 @@ export async function renderToString<K extends Key>( |
| 72 | 72 | |
| 73 | 73 | // -- html -- |
| 74 | 74 | let page: render.Element = render.element(component, props); |
| 75 | if (layout) page = render.element(layout, { children: page }); | |
| 75 | if (layout) page = render.element(layout, { content: page }); | |
| 76 | 76 | const sitegen = sg.context.init(); |
| 77 | 77 | const { text: body } = await render.async(page, [sitegen]); |
| 78 | 78 |
framework/mdx-hack.tsx+5-5| ... | ... | @@ -1,15 +1,15 @@ |
| 1 | 1 | export function useMDXComponents() { |
| 2 | 2 | return { |
| 3 | 3 | pre: async ( |
| 4 | { filename, whitespace, tint, children: [, , { className, children }] }, | |
| 4 | { filename, whitespace, tint, content: [, , { className, content }] }, | |
| 5 | 5 | ) => { |
| 6 | 6 | const lang = className.split("-")?.[1] ?? null; |
| 7 | 7 | const code = ["tsx", "jsx", "json", "html", "ts", "js"].includes(lang) |
| 8 | 8 | ? await highlight.highlightCode( |
| 9 | children.trim(), | |
| 9 | content.trim(), | |
| 10 | 10 | lang, |
| 11 | 11 | ) |
| 12 | : children.trim(); | |
| 12 | : content.trim(); | |
| 13 | 13 | return ( |
| 14 | 14 | <figure class={["code", tint]}> |
| 15 | 15 | {filename && <figcaption>{filename}</figcaption>} |
| ... | ... | @@ -19,10 +19,10 @@ export function useMDXComponents() { |
| 19 | 19 | </figure> |
| 20 | 20 | ); |
| 21 | 21 | }, |
| 22 | code: ({ children }) => { | |
| 22 | code: ({ content }) => { | |
| 23 | 23 | return ( |
| 24 | 24 | <code> |
| 25 | {children} | |
| 25 | {content} | |
| 26 | 26 | </code> |
| 27 | 27 | ); |
| 28 | 28 | }, |
lib/render.ts+18-9| ... | ... | @@ -14,7 +14,7 @@ |
| 14 | 14 | * import * as render from "@clo/lib/render.ts"; |
| 15 | 15 | * console.info(render.sync(<h1>good morning</h1>)); |
| 16 | 16 | * // jsx is equivilent to: |
| 17 | * console.info(render.sync(render.element("h1", { children: "good morning" }))); | |
| 17 | * console.info(render.sync(render.element("h1", { content: "good morning" }))); | |
| 18 | 18 | * ``` |
| 19 | 19 | * |
| 20 | 20 | * NOTE: This module is not production ready, but it is in use at https://paperclover.net |
| ... | ... | @@ -154,9 +154,9 @@ export function element( |
| 154 | 154 | export function resolvedElement( |
| 155 | 155 | type: ResolvedElement[1], |
| 156 | 156 | attrs: ResolvedElement[2], |
| 157 | children: ResolvedElement[3], | |
| 157 | content: ResolvedElement[3], | |
| 158 | 158 | ): ResolvedElement { |
| 159 | return [kElement, type, attrs, children]; | |
| 159 | return [kElement, type, attrs, content]; | |
| 160 | 160 | } |
| 161 | 161 | |
| 162 | 162 | /** internal global state while rendering */ |
| ... | ... | @@ -259,8 +259,8 @@ export function resolveNode(r: State, node: unknown): ResolvedNode { |
| 259 | 259 | } |
| 260 | 260 | } |
| 261 | 261 | if (typeof tag !== "string") throw new Error("Unexpected " + inspect(type)); |
| 262 | const children = attrs?.children; | |
| 263 | if (children) return [kElement, tag, attrs, resolveNode(r, children)]; | |
| 262 | const content = attrs?.content; | |
| 263 | if (content) return [kElement, tag, attrs, resolveNode(r, content)]; | |
| 264 | 264 | return node; |
| 265 | 265 | } |
| 266 | 266 | if (type === kRaw) return node[1]; |
| ... | ... | @@ -275,7 +275,7 @@ export type ResolvedElement = [ |
| 275 | 275 | tag: typeof kElement, |
| 276 | 276 | type: string, |
| 277 | 277 | attrs: Record<string, unknown>, |
| 278 | children: ResolvedNode, | |
| 278 | content: ResolvedNode, | |
| 279 | 279 | ]; |
| 280 | 280 | /** |
| 281 | 281 | * async rendering is done by creating an array of one item, which is already a |
| ... | ... | @@ -307,7 +307,7 @@ export function stringifyNode(node: ResolvedNode): string { |
| 307 | 307 | } |
| 308 | 308 | |
| 309 | 309 | function stringifyElement(element: ResolvedElement) { |
| 310 | const { 1: tag, 2: attrs, 3: children } = element; | |
| 310 | const { 1: tag, 2: attrs, 3: content } = element; | |
| 311 | 311 | let out = "<" + tag; |
| 312 | 312 | for (const prop in attrs) { |
| 313 | 313 | const value = attrs[prop]; |
| ... | ... | @@ -336,8 +336,17 @@ function stringifyElement(element: ResolvedElement) { |
| 336 | 336 | throw new Error( |
| 337 | 337 | "Do not use the `dangerouslySetInnerHTML` attribute. Use `render.raw()`", |
| 338 | 338 | ); |
| 339 | // Do not process these | |
| 340 | 339 | case "children": |
| 340 | throw new Error( | |
| 341 | "Do not set the `children` attribute to set element contents. Use `content`", | |
| 342 | ); | |
| 343 | // Do not process these | |
| 344 | case "content": | |
| 345 | if (tag === "meta") { | |
| 346 | attr = `${prop}=${quoteIfNeeded(escapeAttribute(String(value)))}`; | |
| 347 | break; | |
| 348 | } | |
| 349 | continue; | |
| 341 | 350 | case "ref": |
| 342 | 351 | case "key": |
| 343 | 352 | continue; |
| ... | ... | @@ -346,7 +355,7 @@ function stringifyElement(element: ResolvedElement) { |
| 346 | 355 | out += attr; |
| 347 | 356 | } |
| 348 | 357 | out = out.trimEnd() + ">"; |
| 349 | if (children) out += stringifyNode(children); | |
| 358 | if (content) out += stringifyNode(content); | |
| 350 | 359 | if ( |
| 351 | 360 | tag !== "br" && |
| 352 | 361 | tag !== "img" && |
lib/render/jsx-runtime.ts+22-20| ... | ... | @@ -6,11 +6,11 @@ |
| 6 | 6 | * |
| 7 | 7 | * declare global { |
| 8 | 8 | * namespace JSX { |
| 9 | * export type IntrinsicElements = jsx.IntrinsicElements; | |
| 10 | * export type ElementChildrenAttribute = jsx.ElementChildrenAttribute; | |
| 11 | * export type Element = jsx.Element; | |
| 12 | * export type ElementType = jsx.ElementType; | |
| 13 | * export type ElementClass = jsx.ElementClass; | |
| 9 | * export type IntrinsicElements = jsx.ts.IntrinsicElements; | |
| 10 | * export type ElementChildrenAttribute = jsx.ts.ElementChildrenAttribute; | |
| 11 | * export type Element = jsx.ts.Element; | |
| 12 | * export type ElementType = jsx.ts.ElementType; | |
| 13 | * export type ElementClass = jsx.ts.ElementClass; | |
| 14 | 14 | * } |
| 15 | 15 | * } |
| 16 | 16 | * import * as jsx from "@clo/lib/render/jsx-runtime.ts"; |
| ... | ... | @@ -18,30 +18,30 @@ |
| 18 | 18 | |
| 19 | 19 | /** this element returns just its contents, and is used by `<>` fragment syntax. */ |
| 20 | 20 | export function Fragment( |
| 21 | { children }: { children: render.Node[] }, | |
| 21 | { content }: { content: render.Node[] }, | |
| 22 | 22 | ): render.Node[] { |
| 23 | return children; | |
| 23 | return content; | |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | /** construct a JSX element in production mode. */ |
| 27 | 27 | export function jsx( |
| 28 | 28 | type: string | render.Component, |
| 29 | attrs: Record<string, unknown>, | |
| 29 | { children, ...attrs }: Record<string, unknown>, | |
| 30 | 30 | ): render.Element { |
| 31 | 31 | if (typeof type !== "function" && typeof type !== "string") { |
| 32 | 32 | throw new Error("Invalid component type: " + render.inspect(type)); |
| 33 | 33 | } |
| 34 | return render.element(type, attrs); | |
| 34 | return render.element(type, { content: children, ...attrs }); | |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | 37 | /** construct a JSX element in development mode. */ |
| 38 | 38 | export function jsxDEV( |
| 39 | 39 | type: string | render.Component, |
| 40 | attrs: Record<string, unknown>, | |
| 40 | { children, ...attrs }: Record<string, unknown>, | |
| 41 | 41 | // Unused with the clover engine |
| 42 | 42 | _key: string, |
| 43 | 43 | // Unused with the clover engine |
| 44 | _isStaticChildren: boolean, | |
| 44 | _staticContent: boolean, | |
| 45 | 45 | source: render.SrcLoc, |
| 46 | 46 | ): render.Element { |
| 47 | 47 | ASSERT( |
| ... | ... | @@ -59,21 +59,23 @@ export function jsxDEV( |
| 59 | 59 | ); |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | return render.element(type, attrs, source); | |
| 62 | return render.element(type, { content: children, ...attrs }, source); | |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | 65 | // Clover SSR engine does not yet distinguish between static and non-static JSX. |
| 66 | 66 | export { jsx as jsxs }; |
| 67 | 67 | |
| 68 | export interface IntrinsicElements { | |
| 69 | [name: string]: Record<string, unknown>; | |
| 70 | } | |
| 71 | export interface ElementChildrenAttribute { | |
| 72 | children: render.Node; | |
| 68 | export declare namespace ts { | |
| 69 | export interface IntrinsicElements { | |
| 70 | [name: string]: Record<string, unknown>; | |
| 71 | } | |
| 72 | export interface ElementChildrenAttribute { | |
| 73 | content: render.Node; | |
| 74 | } | |
| 75 | export type Element = render.Element; | |
| 76 | export type ElementType = keyof IntrinsicElements | render.Component; | |
| 77 | export type ElementClass = ReturnType<render.Component>; | |
| 73 | 78 | } |
| 74 | export type Element = render.Element; | |
| 75 | export type ElementType = keyof IntrinsicElements | render.Component; | |
| 76 | export type ElementClass = ReturnType<render.Component>; | |
| 77 | 79 | |
| 78 | 80 | import * as render from "../render.ts"; |
| 79 | 81 | import { ASSERT } from "../assert.ts"; |
run.js+1| ... | ... | @@ -49,6 +49,7 @@ console.debug = log.debug; |
| 49 | 49 | console["log"] = log.log; |
| 50 | 50 | |
| 51 | 51 | process.on("uncaughtException", (error) => { |
| 52 | console.error("Uncaught Exception"); | |
| 52 | 53 | console.error(error); |
| 53 | 54 | process.exit(1); |
| 54 | 55 | }); |
src/blog/pages/community-translations.mdx+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | export const meta = { title: "about clover's community translations" }; |
| 2 | export const layout = { default: ({ children }) => <main>{children}</main> }; | |
| 2 | export const layout = { default: ({ content }) => <main>{content}</main> }; | |
| 3 | 3 | |
| 4 | 4 | <a href="/">back to the home page</a> |
| 5 | 5 |
src/blog/tags/heading.tsx+2-2| ... | ... | @@ -1,9 +1,9 @@ |
| 1 | export default function ({ level: Level, slug, children }) { | |
| 1 | export default function ({ level: Level, slug, content }) { | |
| 2 | 2 | return ( |
| 3 | 3 | <> |
| 4 | 4 | <Level id={slug}> |
| 5 | 5 | <a class="return" href="#toc">§</a> |
| 6 | {children} | |
| 6 | {content} | |
| 7 | 7 | </Level> |
| 8 | 8 | </> |
| 9 | 9 | ); |
src/blog/tags/layout.tsx+3-3| ... | ... | @@ -11,7 +11,7 @@ interface LayoutProps { |
| 11 | 11 | meta: { title: string; description: string; keywords: string[] }; |
| 12 | 12 | date: string; |
| 13 | 13 | slug: string; |
| 14 | children: render.Node; | |
| 14 | content: render.Node; | |
| 15 | 15 | translation?: { |
| 16 | 16 | lang: string; |
| 17 | 17 | |
| ... | ... | @@ -26,7 +26,7 @@ export async function Layout({ |
| 26 | 26 | meta: { title, description, keywords }, |
| 27 | 27 | date, |
| 28 | 28 | slug, |
| 29 | children, | |
| 29 | content, | |
| 30 | 30 | translation, |
| 31 | 31 | }: LayoutProps) { |
| 32 | 32 | const translations = |
| ... | ... | @@ -95,7 +95,7 @@ export async function Layout({ |
| 95 | 95 | </div> |
| 96 | 96 | <hr /> |
| 97 | 97 | </header> |
| 98 | {children} | |
| 98 | {content} | |
| 99 | 99 | </main> |
| 100 | 100 | </> |
| 101 | 101 | ); |
src/blog/tags/table-of-contents.tsx+2-2| ... | ... | @@ -1,10 +1,10 @@ |
| 1 | 1 | import "./table-of-contents.css"; |
| 2 | 2 | |
| 3 | export default function ({ children }) { | |
| 3 | export default function ({ content }) { | |
| 4 | 4 | return ( |
| 5 | 5 | <div id="toc"> |
| 6 | 6 | <h2>Contents:</h2> |
| 7 | {children} | |
| 7 | {content} | |
| 8 | 8 | </div> |
| 9 | 9 | ); |
| 10 | 10 | } |
src/file-viewer/backend.tsx+1-1| ... | ... | @@ -249,7 +249,7 @@ function getPartialPage(c: Context, rawFilePath: string) { |
| 249 | 249 | if (!checkCotyledonCookie(c)) { |
| 250 | 250 | let root = Speedbump(); |
| 251 | 251 | // Remove the root element, it's created client side! |
| 252 | root = root[2].children as render.Element; | |
| 252 | root = root[2].content as render.Element; | |
| 253 | 253 | |
| 254 | 254 | const html = render.sync(root).text; |
| 255 | 255 | c.header("X-Cotyledon", "true"); |
src/pages/license.mdx+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | export const CurrentYear = () => new Date().getFullYear(); |
| 2 | export default ({children}) => <main>{children}</main>; | |
| 2 | export default ({ content }) => <main>{content}</main>; | |
| 3 | 3 | export const theme = { |
| 4 | 4 | fg: '#ffe', |
| 5 | 5 | bg: '#434', |
src/q+a/layout.tsx+4-5| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | import "./questions.css"; |
| 2 | 2 | |
| 3 | export default function QuestionLayout({ children }: { children: any }) { | |
| 3 | export default function QuestionLayout({ content }: render.Node) { | |
| 4 | 4 | addScript("./header.client.ts"); |
| 5 | 5 | return ( |
| 6 | 6 | <main class="qa"> |
| ... | ... | @@ -9,7 +9,7 @@ export default function QuestionLayout({ children }: { children: any }) { |
| 9 | 9 | <span class="n" style="--s:#999;margin:0 5px">&</span> |
| 10 | 10 | <span class="q" style="color:var(--b);--s:#994253">questions</span> |
| 11 | 11 | </h1> |
| 12 | {children} | |
| 12 | {content} | |
| 13 | 13 | </main> |
| 14 | 14 | ); |
| 15 | 15 | } |
| ... | ... | @@ -21,10 +21,9 @@ export const theme = { |
| 21 | 21 | }; |
| 22 | 22 | |
| 23 | 23 | export const minimal = { |
| 24 | default: ({ children }: { children: any }) => ( | |
| 25 | <main class="qa">{children}</main> | |
| 26 | ), | |
| 24 | default: ({ content }: { content: any }) => <main class="qa">{content}</main>, | |
| 27 | 25 | theme, |
| 28 | 26 | }; |
| 29 | 27 | |
| 30 | 28 | import { addScript } from "#sitegen"; |
| 29 | import * as render from "lib/render.ts"; |
src/q+a/tags/clickable-links.client.ts+1| ... | ... | @@ -11,6 +11,7 @@ async function qaDate(el: HTMLAnchorElement) { |
| 11 | 11 | time.textContent = "copied permalink to question"; |
| 12 | 12 | time.classList.add("winner"); |
| 13 | 13 | } catch (error) { |
| 14 | console.error(error); | |
| 14 | 15 | time.textContent = "failed to copy :("; |
| 15 | 16 | } |
| 16 | 17 | setTimeout(() => { |
tsconfig.json+1-1| ... | ... | @@ -18,5 +18,5 @@ |
| 18 | 18 | "noUncheckedIndexedAccess": true, |
| 19 | 19 | "resolveJsonModule": true |
| 20 | 20 | }, |
| 21 | "include": ["framework/**/*", "src/**/*", "lib/**/*"] | |
| 21 | "include": ["framework/**/*", "src/**/*", "lib/**/*", "framework/global.d.ts"] | |
| 22 | 22 | } |