edgerender
Version:
Render at the edge
235 lines • 7.07 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JsxChunk = exports.raw_html = exports.RawHtml = exports.CustomTag = exports.render_jsx = exports.Fragment = void 0;
const utils_1 = require("./utils");
const styles_1 = require("./utils/styles");
const escape_1 = __importDefault(require("./utils/escape"));
class Fragment {
}
exports.Fragment = Fragment;
async function render_jsx(jsx_element) {
const jsx_obj = await Promise.resolve(jsx_element);
return await jsx_obj.render();
}
exports.render_jsx = render_jsx;
const CustomTag = ({ _tag, key, ...props }) => {
return new JsxChunk(_tag, key, props);
};
exports.CustomTag = CustomTag;
class RawHtml {
html;
constructor(html) {
this.html = html;
}
}
exports.RawHtml = RawHtml;
const raw_html = (html) => new RawHtml(html);
exports.raw_html = raw_html;
const EmptyTags = new Set([
'area',
'base',
'br',
'col',
'embed',
'hr',
'img',
'input',
'link',
'meta',
'param',
'source',
'track',
'wbr',
]);
class JsxChunk {
el;
key;
args;
props;
children;
constructor(el, key = null, args) {
this.el = el;
this.key = key;
this.args = args;
const { children, ...props } = args;
this.props = props;
if (Array.isArray(children)) {
this.children = children;
}
else if (typeof children == 'undefined') {
this.children = [];
}
else {
this.children = [children];
}
}
async render() {
const { el } = this;
if (typeof el == 'string') {
if (el == 'html') {
return '<!doctype html>\n' + (await this.render_element(el));
}
else {
return await this.render_element(el);
}
}
else if (el == Fragment) {
return await this.render_children();
}
else {
const f = el;
const jsx_chunk = await Promise.resolve(f(this.args));
return await jsx_chunk.render();
}
}
async render_element(el) {
let attrs = '';
for (const [key, value] of Object.entries(this.props)) {
attrs += render_attr(key, value);
}
if (EmptyTags.has(el)) {
// TODO error if children exist?
return `<${el}${attrs}>`;
}
else {
const children = await this.render_children();
return `<${el}${attrs}>${children}</${el}>`;
}
}
async render_children() {
return await cat_array(this.children);
}
toString() {
return `JsxElement(${this.el}, ${JSON.stringify(this.args)})`;
}
}
exports.JsxChunk = JsxChunk;
function render_attr(name, value) {
let attr_value = null;
const value_type = utils_1.smart_typeof(value);
if (value_type == "Null" /* Null */ || value_type == "Undefined" /* Undefined */) {
attr_value = null;
}
else if (name == 'style') {
attr_value = styles_1.render_styles(value);
}
else if (name == 'className') {
attr_value = render_class(value, value_type);
}
else if (name == 'data' && value_type == "Object" /* Object */) {
return render_data(value);
}
else if (value_type == "Boolean" /* Boolean */) {
if (name.startsWith('hx')) {
// things like hx-boost want the literal value "true" or "false"
attr_value = value ? 'true' : 'false';
}
else if (value === true) {
return ` ${get_tag_name(name)}`;
}
else {
attr_value = null;
}
}
else {
attr_value = render_attr_value(value, value_type);
}
if (attr_value === null) {
return '';
}
else {
const quote = attr_value.includes('"') ? "'" : '"';
return ` ${get_tag_name(name)}=${quote}${attr_value}${quote}`;
}
}
function render_attr_value(value, value_type) {
switch (value_type) {
case "String" /* String */:
return escape_1.default.attr_double(value);
case "RawHtml" /* RawHtml */:
return value.html;
default:
return escape_1.default.attr_single(JSON.stringify(value));
}
}
function get_tag_name(name) {
if (name == 'className') {
return 'class';
}
else if (name == 'htmlFor') {
return 'for';
}
else if (name.startsWith('hx')) {
return styles_1.hyphenate(name);
}
else {
return name.toLowerCase();
}
}
async function render_child(child) {
switch (utils_1.smart_typeof(child)) {
case "String" /* String */:
return escape_1.default.content(child);
case "JsxChunk" /* JsxChunk */:
return await child.render();
case "Array" /* Array */:
return await cat_array(child);
case "RawHtml" /* RawHtml */:
return child.html;
case "Null" /* Null */:
case "Undefined" /* Undefined */:
return '';
case "Number" /* Number */:
return child.toString();
case "Function" /* Function */:
return await render_child(child());
case "Promise" /* Promise */:
return await render_child((await child));
case "RegExp" /* RegExp */:
return escape_1.default.content(child.toString());
default:
return escape_1.default.content(JSON.stringify(child));
}
}
async function cat_array(child) {
const results = await Promise.all(child.map(item => render_child(item)));
return results.join('');
}
function render_class(value, value_type) {
const classNames = [];
switch (value_type) {
case "String" /* String */:
return value;
case "Array" /* Array */:
for (const className of value) {
if (typeof className == 'string') {
classNames.push(className);
}
}
break;
case "Object" /* Object */:
for (const [className, v] of Object.entries(value)) {
if (v) {
classNames.push(className);
}
}
break;
default:
throw new TypeError(`unexpected type passed to className: "${value_type}", should be string, list or object`);
}
return classNames.join(' ');
}
function render_data(data) {
let result = '';
for (const [key, value] of Object.entries(data)) {
const clean_name = key.replace(/[^\w-]/g, '');
const attr_value = render_attr_value(value, utils_1.smart_typeof(value));
const quote = attr_value.includes('"') ? "'" : '"';
result += ` data-${clean_name}=${quote}${attr_value}${quote}`;
}
return result;
}
//# sourceMappingURL=jsx.js.map