[ci] format
This commit is contained in:
parent
908c2638cb
commit
5e716e8cd5
8 changed files with 173 additions and 92 deletions
|
@ -51,7 +51,7 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = {
|
||||||
vite: {},
|
vite: {},
|
||||||
experimental: {
|
experimental: {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
integrations: false
|
integrations: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -348,7 +348,7 @@ export async function validateConfig(
|
||||||
};
|
};
|
||||||
if (
|
if (
|
||||||
// TODO: expose @astrojs/mdx package
|
// 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
|
// Enable default JSX integration
|
||||||
const { default: jsxRenderer } = await import('../jsx/renderer.js');
|
const { default: jsxRenderer } = await import('../jsx/renderer.js');
|
||||||
|
|
|
@ -5,7 +5,7 @@ const Empty = Symbol('empty');
|
||||||
|
|
||||||
interface AstroVNode {
|
interface AstroVNode {
|
||||||
[AstroJSX]: boolean;
|
[AstroJSX]: boolean;
|
||||||
type: string|((...args: any) => any)|typeof Fragment;
|
type: string | ((...args: any) => any) | typeof Fragment;
|
||||||
props: Record<string, any>;
|
props: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,24 +19,26 @@ export function transformSlots(vnode: AstroVNode) {
|
||||||
if (typeof vnode.type === 'string') return vnode;
|
if (typeof vnode.type === 'string') return vnode;
|
||||||
if (!Array.isArray(vnode.props.children)) return;
|
if (!Array.isArray(vnode.props.children)) return;
|
||||||
const slots: Record<string, any> = {};
|
const slots: Record<string, any> = {};
|
||||||
vnode.props.children = vnode.props.children.map(child => {
|
vnode.props.children = vnode.props.children
|
||||||
if (!isVNode(child)) return child;
|
.map((child) => {
|
||||||
if (!('slot' in child.props)) return child;
|
if (!isVNode(child)) return child;
|
||||||
const name = toSlotName(child.props.slot)
|
if (!('slot' in child.props)) return child;
|
||||||
if (Array.isArray(slots[name])) {
|
const name = toSlotName(child.props.slot);
|
||||||
slots[name].push(child);
|
if (Array.isArray(slots[name])) {
|
||||||
} else {
|
slots[name].push(child);
|
||||||
slots[name] = [child];
|
} else {
|
||||||
}
|
slots[name] = [child];
|
||||||
delete child.props.slot;
|
}
|
||||||
return Empty;
|
delete child.props.slot;
|
||||||
}).filter(v => v !== Empty);
|
return Empty;
|
||||||
|
})
|
||||||
|
.filter((v) => v !== Empty);
|
||||||
Object.assign(vnode.props, slots);
|
Object.assign(vnode.props, slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
function markRawChildren(child: any): any {
|
function markRawChildren(child: any): any {
|
||||||
if (typeof child === 'string') return markHTMLString(child);
|
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;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +59,7 @@ function transformSetDirectives(vnode: AstroVNode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVNode(type: any, props: Record<string, any>) {
|
function createVNode(type: any, props: Record<string, any>) {
|
||||||
const vnode: AstroVNode = {
|
const vnode: AstroVNode = {
|
||||||
[AstroJSX]: true,
|
[AstroJSX]: true,
|
||||||
type,
|
type,
|
||||||
props: props ?? {},
|
props: props ?? {},
|
||||||
|
@ -67,10 +69,4 @@ function createVNode(type: any, props: Record<string, any>) {
|
||||||
return vnode;
|
return vnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export { AstroJSX, createVNode as jsx, createVNode as jsxs, createVNode as jsxDEV, Fragment };
|
||||||
AstroJSX,
|
|
||||||
createVNode as jsx,
|
|
||||||
createVNode as jsxs,
|
|
||||||
createVNode as jsxDEV,
|
|
||||||
Fragment
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,85 +1,110 @@
|
||||||
import * as t from "@babel/types";
|
|
||||||
import type { PluginObj } from '@babel/core';
|
import type { PluginObj } from '@babel/core';
|
||||||
|
import * as t from '@babel/types';
|
||||||
|
|
||||||
function isComponent(tagName: string) {
|
function isComponent(tagName: string) {
|
||||||
return (
|
return (
|
||||||
(tagName[0] && tagName[0].toLowerCase() !== tagName[0]) ||
|
(tagName[0] && tagName[0].toLowerCase() !== tagName[0]) ||
|
||||||
tagName.includes(".") ||
|
tagName.includes('.') ||
|
||||||
/[^a-zA-Z]/.test(tagName[0])
|
/[^a-zA-Z]/.test(tagName[0])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasClientDirective(node: t.JSXElement) {
|
function hasClientDirective(node: t.JSXElement) {
|
||||||
for (const attr of node.openingElement.attributes) {
|
for (const attr of node.openingElement.attributes) {
|
||||||
if (attr.type === 'JSXAttribute' && attr.name.type === 'JSXNamespacedName') {
|
if (attr.type === 'JSXAttribute' && attr.name.type === 'JSXNamespacedName') {
|
||||||
return attr.name.namespace.name === 'client'
|
return attr.name.namespace.name === 'client';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTagName(tag: t.JSXElement) {
|
function getTagName(tag: t.JSXElement) {
|
||||||
const jsxName = tag.openingElement.name;
|
const jsxName = tag.openingElement.name;
|
||||||
return jsxElementNameToString(jsxName);
|
return jsxElementNameToString(jsxName);
|
||||||
}
|
}
|
||||||
|
|
||||||
function jsxElementNameToString(node: t.JSXOpeningElement['name']): string {
|
function jsxElementNameToString(node: t.JSXOpeningElement['name']): string {
|
||||||
if (t.isJSXMemberExpression(node)) {
|
if (t.isJSXMemberExpression(node)) {
|
||||||
return `${jsxElementNameToString(node.object)}.${node.property.name}`;
|
return `${jsxElementNameToString(node.object)}.${node.property.name}`;
|
||||||
}
|
}
|
||||||
if (t.isJSXIdentifier(node) || t.isIdentifier(node)) {
|
if (t.isJSXIdentifier(node) || t.isIdentifier(node)) {
|
||||||
return node.name;
|
return node.name;
|
||||||
}
|
}
|
||||||
return `${node.namespace.name}:${node.name.name}`;
|
return `${node.namespace.name}:${node.name.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function jsxAttributeToString(attr: t.JSXAttribute): string {
|
function jsxAttributeToString(attr: t.JSXAttribute): string {
|
||||||
if (t.isJSXNamespacedName(attr.name)) {
|
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}`;
|
return `${attr.name.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addClientMetadata(node: t.JSXElement, meta: { path: string, name: string }) {
|
function addClientMetadata(node: t.JSXElement, meta: { path: string; name: string }) {
|
||||||
const existingAttributes = node.openingElement.attributes.map(attr => t.isJSXAttribute(attr) ? jsxAttributeToString(attr) : null);
|
const existingAttributes = node.openingElement.attributes.map((attr) =>
|
||||||
if (!existingAttributes.find(attr => attr === 'client:component-path')) {
|
t.isJSXAttribute(attr) ? jsxAttributeToString(attr) : null
|
||||||
|
);
|
||||||
|
if (!existingAttributes.find((attr) => attr === 'client:component-path')) {
|
||||||
const componentPath = t.jsxAttribute(
|
const componentPath = t.jsxAttribute(
|
||||||
t.jsxNamespacedName(t.jsxIdentifier('client'), t.jsxIdentifier('component-path')),
|
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);
|
node.openingElement.attributes.push(componentPath);
|
||||||
}
|
}
|
||||||
if (!existingAttributes.find(attr => attr === 'client:component-export')) {
|
if (!existingAttributes.find((attr) => attr === 'client:component-export')) {
|
||||||
if (meta.name === '*') {
|
if (meta.name === '*') {
|
||||||
meta.name = getTagName(node).split('.').at(1)!;
|
meta.name = getTagName(node).split('.').at(1)!;
|
||||||
}
|
}
|
||||||
const componentExport = t.jsxAttribute(
|
const componentExport = t.jsxAttribute(
|
||||||
t.jsxNamespacedName(t.jsxIdentifier('client'), t.jsxIdentifier('component-export')),
|
t.jsxNamespacedName(t.jsxIdentifier('client'), t.jsxIdentifier('component-export')),
|
||||||
t.stringLiteral(meta.name),
|
t.stringLiteral(meta.name)
|
||||||
);
|
);
|
||||||
node.openingElement.attributes.push(componentExport);
|
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(
|
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);
|
node.openingElement.attributes.push(staticMarker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function astroJSX(): PluginObj {
|
export default function astroJSX(): PluginObj {
|
||||||
return {
|
return {
|
||||||
visitor: {
|
visitor: {
|
||||||
Program(path) {
|
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) {
|
ImportDeclaration(path, state) {
|
||||||
const source = path.node.source.value;
|
const source = path.node.source.value;
|
||||||
if (source.startsWith('astro/jsx-runtime')) return;
|
if (source.startsWith('astro/jsx-runtime')) return;
|
||||||
const specs = path.node.specifiers.map(spec => {
|
const specs = path.node.specifiers.map((spec) => {
|
||||||
if (t.isImportDefaultSpecifier(spec)) return { local: spec.local.name, imported: 'default' }
|
if (t.isImportDefaultSpecifier(spec))
|
||||||
if (t.isImportNamespaceSpecifier(spec)) return { local: spec.local.name, imported: '*' }
|
return { local: spec.local.name, imported: 'default' };
|
||||||
if (t.isIdentifier(spec.imported)) return { local: spec.local.name, imported: spec.imported.name };
|
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 };
|
return { local: spec.local.name, imported: spec.imported.value };
|
||||||
});
|
});
|
||||||
const imports = state.get('imports') ?? new Map();
|
const imports = state.get('imports') ?? new Map();
|
||||||
|
@ -87,17 +112,17 @@ export default function astroJSX(): PluginObj {
|
||||||
if (imports.has(source)) {
|
if (imports.has(source)) {
|
||||||
const existing = imports.get(source);
|
const existing = imports.get(source);
|
||||||
existing.add(spec);
|
existing.add(spec);
|
||||||
imports.set(source, existing)
|
imports.set(source, existing);
|
||||||
} else {
|
} else {
|
||||||
imports.set(source, new Set([spec]))
|
imports.set(source, new Set([spec]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.set('imports', imports);
|
state.set('imports', imports);
|
||||||
},
|
},
|
||||||
JSXIdentifier(path, state) {
|
JSXIdentifier(path, state) {
|
||||||
const isAttr = path.findParent(n => t.isJSXAttribute(n));
|
const isAttr = path.findParent((n) => t.isJSXAttribute(n));
|
||||||
if (isAttr) return;
|
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 parentNode = parent.node as t.JSXElement;
|
||||||
const tagName = getTagName(parentNode);
|
const tagName = getTagName(parentNode);
|
||||||
if (!isComponent(tagName)) return;
|
if (!isComponent(tagName)) return;
|
||||||
|
@ -121,11 +146,15 @@ export default function astroJSX(): PluginObj {
|
||||||
// TODO: map unmatched identifiers back to imports if possible
|
// TODO: map unmatched identifiers back to imports if possible
|
||||||
const meta = path.getData('import');
|
const meta = path.getData('import');
|
||||||
if (meta) {
|
if (meta) {
|
||||||
addClientMetadata(parentNode, meta)
|
addClientMetadata(parentNode, meta);
|
||||||
} else {
|
} 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!`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
|
@ -4,12 +4,17 @@ const renderer = {
|
||||||
jsxImportSource: 'astro',
|
jsxImportSource: 'astro',
|
||||||
jsxTransformOptions: async () => {
|
jsxTransformOptions: async () => {
|
||||||
// @ts-ignore
|
// @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');
|
const { default: astroJSX } = await import('./babel.js');
|
||||||
return {
|
return {
|
||||||
plugins: [astroJSX(), jsx({}, { throwIfNamespace: false, runtime: 'automatic', importSource: 'astro' })],
|
plugins: [
|
||||||
|
astroJSX(),
|
||||||
|
jsx({}, { throwIfNamespace: false, runtime: 'automatic', importSource: 'astro' }),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export default renderer;
|
export default renderer;
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import { renderJSX } from '../runtime/server/jsx.js';
|
|
||||||
import { AstroJSX, jsx } from '../jsx-runtime/index.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());
|
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;
|
if (typeof Component !== 'function') return false;
|
||||||
const slots: Record<string, any> = {};
|
const slots: Record<string, any> = {};
|
||||||
for (const [key, value] of Object.entries(slotted)) {
|
for (const [key, value] of Object.entries(slotted)) {
|
||||||
|
@ -13,11 +17,16 @@ export async function check(Component: any, props: any, { default: children = nu
|
||||||
try {
|
try {
|
||||||
const result = await Component({ ...props, ...slots, children });
|
const result = await Component({ ...props, ...slots, children });
|
||||||
return result[AstroJSX];
|
return result[AstroJSX];
|
||||||
} catch (e) {};
|
} catch (e) {}
|
||||||
return false;
|
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<string, any> = {};
|
const slots: Record<string, any> = {};
|
||||||
for (const [key, value] of Object.entries(slotted)) {
|
for (const [key, value] of Object.entries(slotted)) {
|
||||||
const name = slotName(key);
|
const name = slotName(key);
|
||||||
|
|
|
@ -22,7 +22,12 @@ import { serializeProps } from './serialize.js';
|
||||||
import { shorthash } from './shorthash.js';
|
import { shorthash } from './shorthash.js';
|
||||||
import { serializeListValue } from './util.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 type { Metadata } from './metadata';
|
||||||
export { createMetadata } from './metadata.js';
|
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
|
// We already know that renderer.ssr.check() has failed
|
||||||
// but this will throw a much more descriptive error!
|
// but this will throw a much more descriptive error!
|
||||||
renderer = matchingRenderers[0];
|
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 {
|
} else {
|
||||||
throw new Error(`Unable to render ${metadata.displayName}!
|
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') {
|
if (metadata.hydrate === 'only') {
|
||||||
html = await renderSlot(result, slots?.fallback);
|
html = await renderSlot(result, slots?.fallback);
|
||||||
} else {
|
} 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) {
|
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
|
// This is a custom element without a renderer. Because of that, render it
|
||||||
|
|
|
@ -1,13 +1,28 @@
|
||||||
import { HTMLString, markHTMLString, escapeHTML, Fragment, renderComponent, spreadAttributes, voidElementNames } from './index.js';
|
|
||||||
import { AstroJSX, isVNode } from '../../jsx-runtime/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<any> {
|
export async function renderJSX(result: any, vnode: any): Promise<any> {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case (vnode instanceof HTMLString): return vnode;
|
case vnode instanceof HTMLString:
|
||||||
case (typeof vnode === 'string'): return markHTMLString(escapeHTML(vnode));
|
return vnode;
|
||||||
case (!vnode && vnode !== 0): return '';
|
case typeof vnode === 'string':
|
||||||
case (vnode.type === Fragment): return renderJSX(result, vnode.props.children);
|
return markHTMLString(escapeHTML(vnode));
|
||||||
case (Array.isArray(vnode)): return markHTMLString((await Promise.all(vnode.map((v: any) => renderJSX(result, v)))).join(''));
|
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[AstroJSX]) {
|
||||||
if (!vnode.type && vnode.type !== 0) return '';
|
if (!vnode.type && vnode.type !== 0) return '';
|
||||||
|
@ -27,17 +42,17 @@ export async function renderJSX(result: any, vnode: any): Promise<any> {
|
||||||
|
|
||||||
const { children = null, ...props } = vnode.props ?? {};
|
const { children = null, ...props } = vnode.props ?? {};
|
||||||
const slots: Record<string, any> = {
|
const slots: Record<string, any> = {
|
||||||
default: []
|
default: [],
|
||||||
}
|
};
|
||||||
function extractSlots(child: any): any {
|
function extractSlots(child: any): any {
|
||||||
if (Array.isArray(child)) {
|
if (Array.isArray(child)) {
|
||||||
return child.map(c => extractSlots(c));
|
return child.map((c) => extractSlots(c));
|
||||||
}
|
}
|
||||||
if (!isVNode(child)) {
|
if (!isVNode(child)) {
|
||||||
return slots.default.push(child);
|
return slots.default.push(child);
|
||||||
}
|
}
|
||||||
if ('slot' in child.props) {
|
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;
|
delete child.props.slot;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -47,17 +62,25 @@ export async function renderJSX(result: any, vnode: any): Promise<any> {
|
||||||
for (const [key, value] of Object.entries(slots)) {
|
for (const [key, value] of Object.entries(slots)) {
|
||||||
slots[key] = () => renderJSX(result, value);
|
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
|
// numbers, plain objects, etc
|
||||||
return markHTMLString(`${vnode}`);
|
return markHTMLString(`${vnode}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function renderElement(result: any, tag: string, { children, ...props }: Record<string, any>) {
|
async function renderElement(
|
||||||
return markHTMLString(`<${tag}${spreadAttributes(props)}${markHTMLString(
|
result: any,
|
||||||
|
tag: string,
|
||||||
|
{ children, ...props }: Record<string, any>
|
||||||
|
) {
|
||||||
|
return markHTMLString(
|
||||||
|
`<${tag}${spreadAttributes(props)}${markHTMLString(
|
||||||
(children == null || children == '') && voidElementNames.test(tag)
|
(children == null || children == '') && voidElementNames.test(tag)
|
||||||
? `/>`
|
? `/>`
|
||||||
: `>${children == null ? '' : await renderJSX(result, children)}</${tag}>`
|
: `>${children == null ? '' : await renderJSX(result, children)}</${tag}>`
|
||||||
)}`);
|
)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ describe('jsx-runtime', () => {
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
fixture = await loadFixture({
|
fixture = await loadFixture({
|
||||||
root: './fixtures/jsx/'
|
root: './fixtures/jsx/',
|
||||||
});
|
});
|
||||||
await fixture.build();
|
await fixture.build();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue