From 5e716e8cd5c848b487055db1868c96e8f4ac85ac Mon Sep 17 00:00:00 2001 From: natemoo-re Date: Fri, 24 Jun 2022 20:13:12 +0000 Subject: [PATCH] [ci] format --- packages/astro/src/core/config.ts | 4 +- packages/astro/src/jsx-runtime/index.ts | 40 ++++---- packages/astro/src/jsx/babel.ts | 113 +++++++++++++-------- packages/astro/src/jsx/renderer.ts | 11 +- packages/astro/src/jsx/server.ts | 17 +++- packages/astro/src/runtime/server/index.ts | 27 ++++- packages/astro/src/runtime/server/jsx.ts | 51 +++++++--- packages/astro/test/jsx.test.js | 2 +- 8 files changed, 173 insertions(+), 92 deletions(-) diff --git a/packages/astro/src/core/config.ts b/packages/astro/src/core/config.ts index f4e097ff6..4ad099e1f 100644 --- a/packages/astro/src/core/config.ts +++ b/packages/astro/src/core/config.ts @@ -51,7 +51,7 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = { vite: {}, experimental: { ssr: false, - integrations: false + integrations: false, }, }; @@ -348,7 +348,7 @@ export async function validateConfig( }; if ( // TODO: expose @astrojs/mdx package - result.integrations.find(integration => integration.name === '@astrojs/mdx') + result.integrations.find((integration) => integration.name === '@astrojs/mdx') ) { // Enable default JSX integration const { default: jsxRenderer } = await import('../jsx/renderer.js'); diff --git a/packages/astro/src/jsx-runtime/index.ts b/packages/astro/src/jsx-runtime/index.ts index ee8660742..37d79a673 100644 --- a/packages/astro/src/jsx-runtime/index.ts +++ b/packages/astro/src/jsx-runtime/index.ts @@ -5,7 +5,7 @@ const Empty = Symbol('empty'); interface AstroVNode { [AstroJSX]: boolean; - type: string|((...args: any) => any)|typeof Fragment; + type: string | ((...args: any) => any) | typeof Fragment; props: Record; } @@ -19,24 +19,26 @@ export function transformSlots(vnode: AstroVNode) { if (typeof vnode.type === 'string') return vnode; if (!Array.isArray(vnode.props.children)) return; const slots: Record = {}; - vnode.props.children = vnode.props.children.map(child => { - if (!isVNode(child)) return child; - if (!('slot' in child.props)) return child; - const name = toSlotName(child.props.slot) - if (Array.isArray(slots[name])) { - slots[name].push(child); - } else { - slots[name] = [child]; - } - delete child.props.slot; - return Empty; - }).filter(v => v !== Empty); + vnode.props.children = vnode.props.children + .map((child) => { + if (!isVNode(child)) return child; + if (!('slot' in child.props)) return child; + const name = toSlotName(child.props.slot); + if (Array.isArray(slots[name])) { + slots[name].push(child); + } else { + slots[name] = [child]; + } + delete child.props.slot; + return Empty; + }) + .filter((v) => v !== Empty); Object.assign(vnode.props, slots); } function markRawChildren(child: any): any { if (typeof child === 'string') return markHTMLString(child); - if (Array.isArray(child)) return child.map(c => markRawChildren(c)); + if (Array.isArray(child)) return child.map((c) => markRawChildren(c)); return child; } @@ -57,7 +59,7 @@ function transformSetDirectives(vnode: AstroVNode) { } function createVNode(type: any, props: Record) { - const vnode: AstroVNode = { + const vnode: AstroVNode = { [AstroJSX]: true, type, props: props ?? {}, @@ -67,10 +69,4 @@ function createVNode(type: any, props: Record) { return vnode; } -export { - AstroJSX, - createVNode as jsx, - createVNode as jsxs, - createVNode as jsxDEV, - Fragment -} +export { AstroJSX, createVNode as jsx, createVNode as jsxs, createVNode as jsxDEV, Fragment }; diff --git a/packages/astro/src/jsx/babel.ts b/packages/astro/src/jsx/babel.ts index 33bd8652b..0e529115d 100644 --- a/packages/astro/src/jsx/babel.ts +++ b/packages/astro/src/jsx/babel.ts @@ -1,85 +1,110 @@ -import * as t from "@babel/types"; import type { PluginObj } from '@babel/core'; +import * as t from '@babel/types'; function isComponent(tagName: string) { - return ( - (tagName[0] && tagName[0].toLowerCase() !== tagName[0]) || - tagName.includes(".") || - /[^a-zA-Z]/.test(tagName[0]) - ); + return ( + (tagName[0] && tagName[0].toLowerCase() !== tagName[0]) || + tagName.includes('.') || + /[^a-zA-Z]/.test(tagName[0]) + ); } function hasClientDirective(node: t.JSXElement) { for (const attr of node.openingElement.attributes) { if (attr.type === 'JSXAttribute' && attr.name.type === 'JSXNamespacedName') { - return attr.name.namespace.name === 'client' + return attr.name.namespace.name === 'client'; } } return false; } function getTagName(tag: t.JSXElement) { - const jsxName = tag.openingElement.name; - return jsxElementNameToString(jsxName); + const jsxName = tag.openingElement.name; + return jsxElementNameToString(jsxName); } function jsxElementNameToString(node: t.JSXOpeningElement['name']): string { - if (t.isJSXMemberExpression(node)) { - return `${jsxElementNameToString(node.object)}.${node.property.name}`; - } - if (t.isJSXIdentifier(node) || t.isIdentifier(node)) { - return node.name; - } - return `${node.namespace.name}:${node.name.name}`; + if (t.isJSXMemberExpression(node)) { + return `${jsxElementNameToString(node.object)}.${node.property.name}`; + } + if (t.isJSXIdentifier(node) || t.isIdentifier(node)) { + return node.name; + } + return `${node.namespace.name}:${node.name.name}`; } function jsxAttributeToString(attr: t.JSXAttribute): string { if (t.isJSXNamespacedName(attr.name)) { - return `${attr.name.namespace.name}:${attr.name.name.name}` + return `${attr.name.namespace.name}:${attr.name.name.name}`; } return `${attr.name.name}`; } -function addClientMetadata(node: t.JSXElement, meta: { path: string, name: string }) { - const existingAttributes = node.openingElement.attributes.map(attr => t.isJSXAttribute(attr) ? jsxAttributeToString(attr) : null); - if (!existingAttributes.find(attr => attr === 'client:component-path')) { +function addClientMetadata(node: t.JSXElement, meta: { path: string; name: string }) { + const existingAttributes = node.openingElement.attributes.map((attr) => + t.isJSXAttribute(attr) ? jsxAttributeToString(attr) : null + ); + if (!existingAttributes.find((attr) => attr === 'client:component-path')) { const componentPath = t.jsxAttribute( t.jsxNamespacedName(t.jsxIdentifier('client'), t.jsxIdentifier('component-path')), - !meta.path.startsWith('.') ? t.stringLiteral(meta.path) : t.jsxExpressionContainer(t.binaryExpression("+", t.stringLiteral('/@fs'), t.memberExpression(t.newExpression(t.identifier('URL'), [t.stringLiteral(meta.path), t.identifier('import.meta.url')]), t.identifier('pathname')))), + !meta.path.startsWith('.') + ? t.stringLiteral(meta.path) + : t.jsxExpressionContainer( + t.binaryExpression( + '+', + t.stringLiteral('/@fs'), + t.memberExpression( + t.newExpression(t.identifier('URL'), [ + t.stringLiteral(meta.path), + t.identifier('import.meta.url'), + ]), + t.identifier('pathname') + ) + ) + ) ); node.openingElement.attributes.push(componentPath); } - if (!existingAttributes.find(attr => attr === 'client:component-export')) { + if (!existingAttributes.find((attr) => attr === 'client:component-export')) { if (meta.name === '*') { meta.name = getTagName(node).split('.').at(1)!; } const componentExport = t.jsxAttribute( t.jsxNamespacedName(t.jsxIdentifier('client'), t.jsxIdentifier('component-export')), - t.stringLiteral(meta.name), + t.stringLiteral(meta.name) ); node.openingElement.attributes.push(componentExport); } - if (!existingAttributes.find(attr => attr === 'client:component-hydration')) { + if (!existingAttributes.find((attr) => attr === 'client:component-hydration')) { const staticMarker = t.jsxAttribute( - t.jsxNamespacedName(t.jsxIdentifier('client'), t.jsxIdentifier('component-hydration')), - ) + t.jsxNamespacedName(t.jsxIdentifier('client'), t.jsxIdentifier('component-hydration')) + ); node.openingElement.attributes.push(staticMarker); } } export default function astroJSX(): PluginObj { - return { - visitor: { + return { + visitor: { Program(path) { - path.node.body.splice(0, 0, (t.importDeclaration([t.importSpecifier(t.identifier('Fragment'), t.identifier('Fragment'))], t.stringLiteral('astro/jsx-runtime')))); + path.node.body.splice( + 0, + 0, + t.importDeclaration( + [t.importSpecifier(t.identifier('Fragment'), t.identifier('Fragment'))], + t.stringLiteral('astro/jsx-runtime') + ) + ); }, ImportDeclaration(path, state) { const source = path.node.source.value; if (source.startsWith('astro/jsx-runtime')) return; - const specs = path.node.specifiers.map(spec => { - if (t.isImportDefaultSpecifier(spec)) return { local: spec.local.name, imported: 'default' } - if (t.isImportNamespaceSpecifier(spec)) return { local: spec.local.name, imported: '*' } - if (t.isIdentifier(spec.imported)) return { local: spec.local.name, imported: spec.imported.name }; + const specs = path.node.specifiers.map((spec) => { + if (t.isImportDefaultSpecifier(spec)) + return { local: spec.local.name, imported: 'default' }; + if (t.isImportNamespaceSpecifier(spec)) return { local: spec.local.name, imported: '*' }; + if (t.isIdentifier(spec.imported)) + return { local: spec.local.name, imported: spec.imported.name }; return { local: spec.local.name, imported: spec.imported.value }; }); const imports = state.get('imports') ?? new Map(); @@ -87,17 +112,17 @@ export default function astroJSX(): PluginObj { if (imports.has(source)) { const existing = imports.get(source); existing.add(spec); - imports.set(source, existing) + imports.set(source, existing); } else { - imports.set(source, new Set([spec])) + imports.set(source, new Set([spec])); } } state.set('imports', imports); }, JSXIdentifier(path, state) { - const isAttr = path.findParent(n => t.isJSXAttribute(n)); + const isAttr = path.findParent((n) => t.isJSXAttribute(n)); if (isAttr) return; - const parent = path.findParent(n => t.isJSXElement(n))!; + const parent = path.findParent((n) => t.isJSXElement(n))!; const parentNode = parent.node as t.JSXElement; const tagName = getTagName(parentNode); if (!isComponent(tagName)) return; @@ -121,11 +146,15 @@ export default function astroJSX(): PluginObj { // TODO: map unmatched identifiers back to imports if possible const meta = path.getData('import'); if (meta) { - addClientMetadata(parentNode, meta) + addClientMetadata(parentNode, meta); } else { - throw new Error(`Unable to match <${getTagName(parentNode)}> with client:* directive to an import statement!`); + throw new Error( + `Unable to match <${getTagName( + parentNode + )}> with client:* directive to an import statement!` + ); } }, - } - }; -}; + }, + }; +} diff --git a/packages/astro/src/jsx/renderer.ts b/packages/astro/src/jsx/renderer.ts index 94a63b5fe..54f4d6a3d 100644 --- a/packages/astro/src/jsx/renderer.ts +++ b/packages/astro/src/jsx/renderer.ts @@ -4,12 +4,17 @@ const renderer = { jsxImportSource: 'astro', jsxTransformOptions: async () => { // @ts-ignore - const { default: { default: jsx } } = await import('@babel/plugin-transform-react-jsx'); + const { + default: { default: jsx }, + } = await import('@babel/plugin-transform-react-jsx'); const { default: astroJSX } = await import('./babel.js'); return { - plugins: [astroJSX(), jsx({}, { throwIfNamespace: false, runtime: 'automatic', importSource: 'astro' })], + plugins: [ + astroJSX(), + jsx({}, { throwIfNamespace: false, runtime: 'automatic', importSource: 'astro' }), + ], }; }, -} +}; export default renderer; diff --git a/packages/astro/src/jsx/server.ts b/packages/astro/src/jsx/server.ts index c75135b90..1f7ff850c 100644 --- a/packages/astro/src/jsx/server.ts +++ b/packages/astro/src/jsx/server.ts @@ -1,9 +1,13 @@ -import { renderJSX } from '../runtime/server/jsx.js'; import { AstroJSX, jsx } from '../jsx-runtime/index.js'; +import { renderJSX } from '../runtime/server/jsx.js'; const slotName = (str: string) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase()); -export async function check(Component: any, props: any, { default: children = null, ...slotted } = {}) { +export async function check( + Component: any, + props: any, + { default: children = null, ...slotted } = {} +) { if (typeof Component !== 'function') return false; const slots: Record = {}; for (const [key, value] of Object.entries(slotted)) { @@ -13,11 +17,16 @@ export async function check(Component: any, props: any, { default: children = nu try { const result = await Component({ ...props, ...slots, children }); return result[AstroJSX]; - } catch (e) {}; + } catch (e) {} return false; } -export async function renderToStaticMarkup(this: any, Component: any, props = {}, { default: children = null, ...slotted } = {}) { +export async function renderToStaticMarkup( + this: any, + Component: any, + props = {}, + { default: children = null, ...slotted } = {} +) { const slots: Record = {}; for (const [key, value] of Object.entries(slotted)) { const name = slotName(key); diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts index 33d41ef5d..e1ffd69e5 100644 --- a/packages/astro/src/runtime/server/index.ts +++ b/packages/astro/src/runtime/server/index.ts @@ -22,7 +22,12 @@ import { serializeProps } from './serialize.js'; import { shorthash } from './shorthash.js'; import { serializeListValue } from './util.js'; -export { markHTMLString, markHTMLString as unescapeHTML, HTMLString, escapeHTML } from './escape.js'; +export { + escapeHTML, + HTMLString, + markHTMLString, + markHTMLString as unescapeHTML, +} from './escape.js'; export type { Metadata } from './metadata'; export { createMetadata } from './metadata.js'; @@ -299,7 +304,13 @@ Did you mean to enable ${formatList(probableRendererNames.map((r) => '`' + r + ' // We already know that renderer.ssr.check() has failed // but this will throw a much more descriptive error! renderer = matchingRenderers[0]; - ({ html } = await renderer.ssr.renderToStaticMarkup.call({ result }, Component, props, children, metadata)); + ({ html } = await renderer.ssr.renderToStaticMarkup.call( + { result }, + Component, + props, + children, + metadata + )); } else { throw new Error(`Unable to render ${metadata.displayName}! @@ -318,12 +329,20 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr if (metadata.hydrate === 'only') { html = await renderSlot(result, slots?.fallback); } else { - ({ html } = await renderer.ssr.renderToStaticMarkup.call({ result }, Component, props, children, metadata)); + ({ html } = await renderer.ssr.renderToStaticMarkup.call( + { result }, + Component, + props, + children, + metadata + )); } } if (renderer && !renderer.clientEntrypoint && metadata.hydrate) { - throw new Error(`${metadata.displayName} component has a \`client:${metadata.hydrate}\` directive, but no client entrypoint was provided by ${renderer.name}!`); + throw new Error( + `${metadata.displayName} component has a \`client:${metadata.hydrate}\` directive, but no client entrypoint was provided by ${renderer.name}!` + ); } // This is a custom element without a renderer. Because of that, render it diff --git a/packages/astro/src/runtime/server/jsx.ts b/packages/astro/src/runtime/server/jsx.ts index dc4df923a..015378797 100644 --- a/packages/astro/src/runtime/server/jsx.ts +++ b/packages/astro/src/runtime/server/jsx.ts @@ -1,13 +1,28 @@ -import { HTMLString, markHTMLString, escapeHTML, Fragment, renderComponent, spreadAttributes, voidElementNames } from './index.js'; import { AstroJSX, isVNode } from '../../jsx-runtime/index.js'; +import { + escapeHTML, + Fragment, + HTMLString, + markHTMLString, + renderComponent, + spreadAttributes, + voidElementNames, +} from './index.js'; export async function renderJSX(result: any, vnode: any): Promise { switch (true) { - case (vnode instanceof HTMLString): return vnode; - case (typeof vnode === 'string'): return markHTMLString(escapeHTML(vnode)); - case (!vnode && vnode !== 0): return ''; - case (vnode.type === Fragment): return renderJSX(result, vnode.props.children); - case (Array.isArray(vnode)): return markHTMLString((await Promise.all(vnode.map((v: any) => renderJSX(result, v)))).join('')); + case vnode instanceof HTMLString: + return vnode; + case typeof vnode === 'string': + return markHTMLString(escapeHTML(vnode)); + case !vnode && vnode !== 0: + return ''; + case vnode.type === Fragment: + return renderJSX(result, vnode.props.children); + case Array.isArray(vnode): + return markHTMLString( + (await Promise.all(vnode.map((v: any) => renderJSX(result, v)))).join('') + ); } if (vnode[AstroJSX]) { if (!vnode.type && vnode.type !== 0) return ''; @@ -27,17 +42,17 @@ export async function renderJSX(result: any, vnode: any): Promise { const { children = null, ...props } = vnode.props ?? {}; const slots: Record = { - default: [] - } + default: [], + }; function extractSlots(child: any): any { if (Array.isArray(child)) { - return child.map(c => extractSlots(c)); + return child.map((c) => extractSlots(c)); } if (!isVNode(child)) { return slots.default.push(child); } if ('slot' in child.props) { - slots[child.props.slot] = [...(slots[child.props.slot] ?? []), child] + slots[child.props.slot] = [...(slots[child.props.slot] ?? []), child]; delete child.props.slot; return; } @@ -47,17 +62,25 @@ export async function renderJSX(result: any, vnode: any): Promise { for (const [key, value] of Object.entries(slots)) { slots[key] = () => renderJSX(result, value); } - return markHTMLString(await renderComponent(result, vnode.type.name, vnode.type, props, slots)); + return markHTMLString( + await renderComponent(result, vnode.type.name, vnode.type, props, slots) + ); } } // numbers, plain objects, etc return markHTMLString(`${vnode}`); } -async function renderElement(result: any, tag: string, { children, ...props }: Record) { - return markHTMLString(`<${tag}${spreadAttributes(props)}${markHTMLString( +async function renderElement( + result: any, + tag: string, + { children, ...props }: Record +) { + return markHTMLString( + `<${tag}${spreadAttributes(props)}${markHTMLString( (children == null || children == '') && voidElementNames.test(tag) ? `/>` : `>${children == null ? '' : await renderJSX(result, children)}` - )}`); + )}` + ); } diff --git a/packages/astro/test/jsx.test.js b/packages/astro/test/jsx.test.js index bf05d35c0..41671699c 100644 --- a/packages/astro/test/jsx.test.js +++ b/packages/astro/test/jsx.test.js @@ -7,7 +7,7 @@ describe('jsx-runtime', () => { before(async () => { fixture = await loadFixture({ - root: './fixtures/jsx/' + root: './fixtures/jsx/', }); await fixture.build(); });