@lifeart/gxt
Version:
<img align="right" width="95" height="95" alt="Philosopher’s stone, logo of PostCSS" src="./public/logo.png">
1,349 lines (1,339 loc) • 47.1 kB
JavaScript
import { Preprocessor } from 'content-tag';
import { builders, preprocess, traverse } from '@glimmer/syntax';
import { transformAsync, transformSync } from '@babel/core';
import { S as SYMBOLS, E as EVENT_TYPE, M as MAIN_IMPORT, C as CONSTANTS } from './symbols-KamXCUHQ.js';
let flags;
let bindings = /* @__PURE__ */ new Set();
function setBindings(b) {
bindings = b;
}
function setFlags(f) {
flags = f;
}
let ctxIndex = 0;
function nextCtxName() {
return `ctx${ctxIndex++}`;
}
function escapeString(str) {
if (typeof str !== "string") {
throw new Error("Not a string");
}
try {
if (typeof JSON.parse(str) !== "string") {
return JSON.stringify(str);
}
return JSON.stringify(JSON.parse(str));
} catch (e) {
return JSON.stringify(str);
}
}
function isPath(str) {
return str.startsWith("$:");
}
function resolvePath(str) {
if (bindings.has(str)) {
return str;
}
if (str === "has-block-params") {
str = str.replace("has-block-params", `${SYMBOLS.$_hasBlockParams}.bind(this, $slots)`);
} else if (str === "has-block") {
str = str.replace("has-block", `${SYMBOLS.$_hasBlock}.bind(this, $slots)`);
} else if (str === "component") {
return SYMBOLS.COMPONENT_HELPER;
} else if (str === "helper") {
return SYMBOLS.HELPER_HELPER;
} else if (str === "modifier") {
return SYMBOLS.MODIFIER_HELPER;
}
return toSafeJSPath(toOptionalChaining(str).replace("$:", "").replace("@", `this[${SYMBOLS.$args}].`));
}
function toSafeJSPath(str) {
if (str.includes("(") || !str.includes(".")) {
return str;
}
const parts = str.split(".");
const result = parts.map((p) => {
if (p.endsWith("?")) {
if (isSafeKey(p.slice(0, -1))) {
return p;
} else if (p.includes("[")) {
return p.slice(0, -1);
} else {
return `["${p.slice(0, -1)}"]?`;
}
} else if (p.includes("[")) {
return p;
} else if (isSafeKey(p)) {
return p;
} else {
return `["${p}"]`;
}
}).reduce((acc, el) => {
if (el.startsWith("[")) {
return acc + el;
} else {
if (acc.length) {
return acc + "." + el;
} else {
return el;
}
}
}, "");
return result;
}
function toOptionalChaining(str) {
if (typeof str !== "string") {
return str;
}
if (str.includes("'") || str.includes('"')) {
return str;
}
if (str.includes("$_")) {
return str;
} else if (str.includes("?.")) {
return str;
} else if (str.split(".").length < 3) {
return str;
}
const result = str.split("...").map((el) => el.split(".").join("?.")).join("...");
if (result.includes("this?.")) {
return result.replace("this?.", "this.");
} else if (result.includes(`this[${SYMBOLS.$args}]?.`)) {
return result.replace(`this[${SYMBOLS.$args}]?.`, `this[${SYMBOLS.$args}].`);
}
return result;
}
function serializePath(p, wrap = flags.IS_GLIMMER_COMPAT_MODE) {
if (typeof p !== "string") {
return p;
}
if (p.includes("...")) {
return p;
}
const isFunction = p.startsWith("$:(") || p.startsWith("$:...(") || p.startsWith("$:function");
if (wrap === false) {
return resolvePath(p);
}
if (isFunction) {
return resolvePath(p);
}
return `() => ${resolvePath(p)}`;
}
function resolvedChildren(els) {
return els.filter((el) => {
if (el.type === "CommentStatement" || el.type === "MustacheCommentStatement") {
return false;
}
if (el.type === "TextNode" && el.chars.trim().length === 0 && el.chars.includes("\n")) {
return false;
}
return true;
});
}
function serializeChildren(children, ctxName) {
if (children.length === 0) {
return "";
}
return `${children.map((child) => {
if (typeof child === "string") {
if (isPath(child)) {
return serializePath(child);
}
return `${SYMBOLS.TEXT}(${SYMBOLS.API}(this),${escapeString(child)})`;
}
return serializeNode(child, ctxName);
}).join(", ")}`;
}
function toChildArray(childs, ctxName = "this") {
if (!childs) {
return "[]";
}
return `[${childs.map((child) => serializeNode(child, ctxName)).filter((el) => el).join(", ")}]`;
}
function toPropName(name) {
let result = name.replace("@", "");
return isSafeKey(result) ? result : JSON.stringify(result);
}
function serializeAttribute(key, value) {
if (typeof value === "boolean") {
return `['${key}', ${String(value)}]`;
} else if (typeof value === "number") {
return `['${key}', ${value}]`;
} else if (value === null) {
return `['${key}', null]`;
} else if (typeof value === "undefined") {
return `['${key}', undefined]`;
}
if (isPath(value)) {
return `['${key}', ${serializePath(value)}]`;
}
return `['${key}', ${escapeString(value)}]`;
}
function serializeProp(attr) {
if (attr[1] === null) {
return `${toPropName(attr[0])}: null`;
} else if (typeof attr[1] === "boolean") {
return `${toPropName(attr[0])}: ${attr[1]}`;
} else if (typeof attr[1] === "number") {
return `${toPropName(attr[0])}: ${attr[1]}`;
} else if (typeof attr[1] === "undefined") {
return `${toPropName(attr[0])}: undefined`;
}
const isScopeValue = isPath(attr[1]);
const key = toPropName(attr[0]);
return `${key}: ${isScopeValue ? serializePath(attr[1]) : escapeString(attr[1])}`;
}
function isSafeKey(key) {
return /^[a-z_$@][a-z0-9_$]*$/i.test(key);
}
function toComponent(ref, args, ctx) {
if (ref.includes(".")) {
return `${SYMBOLS.DYNAMIC_COMPONENT}($:()=>${ref},${args},${ctx})`;
} else {
return `${SYMBOLS.COMPONENT}(${ref},${args},${ctx})`;
}
}
function toObject(args) {
return `{${args.map((attr) => serializeProp(attr)).join(", ")}}`;
}
function toArray(args) {
return `[${args.map((attr) => serializeAttribute(attr[0], attr[1])).join(", ")}]`;
}
function toArgs(args, slots, props) {
if (flags.IS_GLIMMER_COMPAT_MODE === false) {
const extraArgs = [...args];
if (props !== "{}" && !props.includes(SYMBOLS.EMPTY_DOM_PROPS)) {
extraArgs.push([`$:[${SYMBOLS.$PROPS_SYMBOL}]`, `$:${props}`]);
}
if (slots !== "{}") {
extraArgs.push([`$:[${SYMBOLS.$SLOTS_SYMBOL}]`, `$:${slots}`]);
}
const result2 = toObject(extraArgs);
return result2;
}
const result = `${SYMBOLS.ARGS}(${toObject(args)},${slots},${props})`;
return result.replace(`${SYMBOLS.ARGS}({},{},{})`, "{}").replace("$_args({},{},$_edp)", "{}");
}
function hasStableChildsForControlNode(childs) {
if (childs === null) {
return true;
}
let hasStableChild = false;
if (childs.length === 1 && typeof childs[0] === "object") {
const child = childs[0];
if (child === null) {
return true;
}
if ("isControl" in child) {
hasStableChild = false;
} else {
if (child.events.filter(([id]) => id === EVENT_TYPE.ON_CREATED).length) {
return false;
}
hasStableChild = true;
}
}
return hasStableChild;
}
function serializeNode(node, ctxName = "this") {
if (node === null) {
return null;
}
if (typeof node === "object" && "isControl" in node) {
const key = `@${node.type}`;
const arrayName = node.condition;
const paramNames = node.blockParams;
const childs = (node.children || []).filter((el) => el !== null);
const isSync = node.isSync;
const inverses = node.inverse;
let eachKey = node.key;
if (eachKey === "@index") {
console.warn("@index identity not supported");
eachKey = "@identity";
}
const newCtxName = nextCtxName();
if (key === "@yield") {
return `$:${SYMBOLS.SLOT}(${escapeString(eachKey)}, () => [${paramNames.join(",")}], $slots, ${ctxName})`;
} else if (key === "@in-element") {
return `$:${SYMBOLS.$_inElement}(${arrayName}, $:(${newCtxName}) => [${serializeChildren(childs, newCtxName)}], ${ctxName})`;
} else if (key === "@each") {
if (paramNames.length === 0) {
paramNames.push("$noop");
}
if (paramNames.length === 1) {
paramNames.push("$index");
}
let hasStableChild = hasStableChildsForControlNode(childs);
const FN_NAME = isSync ? SYMBOLS.EACH_SYNC : SYMBOLS.EACH;
const EACH_KEY = eachKey ? escapeString(eachKey) : null;
const FN_FN_ARGS = `${paramNames.join(",")},${newCtxName}`;
const indexParamName = paramNames[1];
const paramBounds = new RegExp(`(?<!\\.)\\b${indexParamName}\\b(?!(=|'|"|:)[^ ]*)`, "g");
if (hasStableChild) {
let childText = toChildArray(childs, newCtxName).split(paramBounds).filter(Boolean).join(`${indexParamName}.value`);
if (childs.length === 1) {
const length = childText.length;
childText = childText.slice(1, length - 1);
}
return `${FN_NAME}(${arrayName}, (${FN_FN_ARGS}) => ${childText}, ${EACH_KEY}, ${ctxName})`;
} else {
const extraContextName = nextCtxName();
let childText = toChildArray(childs, extraContextName).split(paramBounds).filter(Boolean).join(`${indexParamName}.value`);
return `${FN_NAME}(${arrayName}, (${FN_FN_ARGS}) => [${SYMBOLS.$_ucw}((${extraContextName}) => ${childText}, ${newCtxName})], ${EACH_KEY}, ${ctxName})`;
}
} else if (key === "@if") {
let hasStableTrueChild = hasStableChildsForControlNode(childs);
let hasStableFalseChild = hasStableChildsForControlNode(inverses);
hasStableTrueChild = false;
hasStableFalseChild = false;
let trueBranch = `(${newCtxName}) => ${toChildArray(childs, newCtxName)}`;
let extraContextName = nextCtxName();
if (!hasStableTrueChild) {
trueBranch = `(${newCtxName}) => ${SYMBOLS.$_ucw}((${extraContextName}) => ${toChildArray(childs, extraContextName)}, ${newCtxName})`;
}
let falseBranch = `(${newCtxName}) => ${toChildArray(inverses, newCtxName)}`;
if (!hasStableFalseChild) {
falseBranch = `(${newCtxName}) => ${SYMBOLS.$_ucw}((${extraContextName}) => ${toChildArray(inverses, extraContextName)}, ${newCtxName})`;
}
return `${SYMBOLS.IF}(${arrayName}, ${trueBranch}, ${falseBranch}, ${ctxName})`;
}
} else if (typeof node === "object" && node.tag && (bindings.has(node.tag) || node.tag.startsWith("$:$_") || node.tag.includes("."))) {
const hasSplatAttrs = node.attributes.find((attr) => {
return attr[0] === "...attributes";
});
const attributes = node.attributes.filter((attr) => {
return attr[0] !== "...attributes";
});
const args = attributes.filter((attr) => {
return attr[0].startsWith("@");
});
const attrs = attributes.filter((attr) => {
return !attr[0].startsWith("@");
});
const props = node.properties;
let secondArg = hasSplatAttrs ? `[[...$fw[0], ...${toArray(props)}],[...$fw[1], ...${toArray(attrs)}],[...$fw[2],...${toArray(node.events)}]]` : `[${toArray(props)},${toArray(attrs)},${toArray(node.events)}]`;
let isSecondArgEmpty = secondArg === "[[],[],[]]";
if (isSecondArgEmpty) {
secondArg = SYMBOLS.EMPTY_DOM_PROPS;
}
if (node.selfClosing) {
return toComponent(node.tag, toArgs(args, "{}", secondArg), ctxName);
} else {
const slots = node.children.filter((child) => {
if (typeof child === "string") {
return false;
} else if ("isControl" in child) {
return false;
} else {
return child.tag.startsWith(":");
}
});
if (slots.length === 0) {
slots.push(node);
}
const serializedSlots = slots.map((slot) => {
const sContext = nextCtxName();
const slotChildren = serializeChildren(slot.children, sContext);
const hasBlockParams = slot.blockParams.length > 0;
const slotName = slot.tag.startsWith(":") ? slot.tag.slice(1) : "default";
return `${slotName}_: ${hasBlockParams},${slotName}: (${[sContext, ...slot.blockParams].join(",")}) => [${slotChildren}]`;
});
const slotsObj = `{${serializedSlots.join(",")}}`;
return toComponent(node.tag, toArgs(args, slotsObj, secondArg), ctxName);
}
} else if (typeof node === "object" && node.tag) {
const hasSplatAttrs = node.attributes.find((attr) => {
return attr[0] === "...attributes";
});
const attributes = node.attributes.filter((attr) => {
return attr[0] !== "...attributes";
});
let tagProps = `[${toArray(node.properties)},${toArray(attributes)},${toArray(node.events)}${hasSplatAttrs ? `,$fw` : ""}]`;
if (tagProps === "[[],[],[]]") {
tagProps = SYMBOLS.EMPTY_DOM_PROPS;
}
return `${SYMBOLS.TAG}('${node.tag}', ${tagProps}, [${serializeChildren(node.children, ctxName)}], ${ctxName})`;
} else {
if (typeof node === "string" || typeof node === "number") {
if (typeof node === "number") {
node = String(node);
}
if (isPath(node)) {
return serializePath(node);
} else {
return `${SYMBOLS.TEXT}(${SYMBOLS.API}(this),${escapeString(node)})`;
}
}
throw new Error("Unknown node type: " + JSON.stringify(node, null, 2));
}
}
function getScopeBindings(path, bindings = /* @__PURE__ */ new Set()) {
Object.keys(path.scope.bindings).forEach((key) => {
bindings.add(key);
});
if (path.parentPath) {
getScopeBindings(path.parentPath, bindings);
}
return bindings;
}
function processTemplate(hbsToProcess, mode) {
return function babelPlugin(babel) {
const {
types: t
} = babel;
return {
name: "ast-transform",
// not required
visitor: {
VariableDeclarator(path, context) {
if (mode !== "development") {
return;
}
if (!context.tokensForHotReload) {
return;
}
const tokensForHotReload = context.tokensForHotReload;
if (path.node.id.type === "Identifier") {
if (path.node.id.name === "existingTokensToReload") {
path.node.init = t.arrayExpression(tokensForHotReload.map((token) => {
return t.stringLiteral(token);
}));
}
}
},
ExportNamedDeclaration(path, context) {
if (mode !== "development") {
return;
}
if (!context.tokensForHotReload) {
context.tokensForHotReload = [];
}
if (path.node.declaration) {
if (path.node.declaration.type === "VariableDeclaration") {
const declarations = path.node.declaration.declarations;
if (declarations.length === 1) {
const declaration = declarations[0];
if (declaration.id.type === "Identifier") {
const existingTokens = context.tokensForHotReload;
existingTokens.push(declaration.id.name);
}
}
} else if (path.node.declaration.type === "ClassDeclaration") {
const declaration = path.node.declaration;
if (declaration.id.type === "Identifier") {
const existingTokens = context.tokensForHotReload;
existingTokens.push(declaration.id.name);
}
}
}
},
ClassBody: {
enter(_, context) {
context.isInsideClassBody = true;
if (_.node.body.length === 1) {
context.isInsideClassBody = false;
}
},
exit(_, context) {
context.isInsideClassBody = false;
}
},
ClassMethod(path) {
if (path.node.key.name === "$static") {
path.replaceWith(t.classProperty(
t.identifier(SYMBOLS.$template),
// hbs literal
t.taggedTemplateExpression(t.identifier("hbs"), path.node.body.body[0].expression.arguments[0]),
null,
null,
true
));
}
},
CallExpression(path) {
if (path.node.callee && path.node.callee.type === "Identifier") {
if (path.node.callee.name === "scope") {
path.remove();
} else if (path.node.callee.name === "template") {
path.replaceWith(t.taggedTemplateExpression(t.identifier("hbs"), path.node.arguments[0]));
} else if (path.node.callee.name === "formula") {
if (mode === "production") {
if (path.node.arguments.length === 2) {
path.node.arguments.pop();
}
}
} else if (path.node.callee.name === "getRenderTargets") {
if (mode === "production") {
if (path.node.arguments.length === 2) {
path.node.arguments.pop();
}
}
}
}
},
ImportDeclaration(path) {
if (path.node.source.value === "@ember/template-compiler") {
path.node.source.value = MAIN_IMPORT;
path.node.specifiers.forEach((specifier) => {
specifier.local.name = "hbs";
specifier.imported.name = "hbs";
});
}
},
Program(path) {
const PUBLIC_API = Object.values(SYMBOLS);
const IMPORTS = PUBLIC_API.map((name) => {
return t.importSpecifier(t.identifier(name), t.identifier(name));
});
path.node.body.unshift(t.importDeclaration(IMPORTS, t.stringLiteral(MAIN_IMPORT)));
},
ReturnStatement: {
enter(_, context) {
context.isInsideReturnStatement = true;
},
exit(_, context) {
context.isInsideReturnStatement = false;
}
},
TaggedTemplateExpression(path, context) {
if (path.node.tag.name === "hbs") {
const template = path.node.quasi.quasis[0].value.raw;
const isInsideClassBody = context.isInsideClassBody === true;
const hasThisInTemplate = template.includes("this");
let hasThisAccess = isInsideClassBody === true || hasThisInTemplate;
if (context.isInsideReturnStatement === true) {
hasThisAccess = true;
}
hbsToProcess.push({
template,
flags: {
hasThisAccess
},
bindings: getScopeBindings(path)
});
path.replaceWith(t.identifier("$placeholder"));
}
}
}
};
};
}
function stripGXTDebug(babel) {
const {
types: t
} = babel;
return {
name: "string-gxt-debug-info-transform",
// not required
visitor: {
BinaryExpression(path) {
if (t.isLiteral(path.node.right)) {
if (path.node.right.value === "/tests.html") {
path.replaceWith(t.booleanLiteral(false));
}
}
},
ClassMethod(path) {
if (path.node.kind === "constructor") {
if (path.node.params.length === 2) {
if (path.node.params[1].name === "debugName") {
path.node.params.pop();
}
}
}
},
ExpressionStatement(path) {
if (path.node.expression && path.node.expression.type === "CallExpression") {
if (path.node.expression.callee.type === "MemberExpression") {
if (path.node.expression.callee.object.name === "console") {
path.remove();
}
}
}
},
ClassProperty(path) {
if (path.node.key.name === "_debugName") {
path.remove();
}
},
FunctionDeclaration(path) {
const nodeName = path.node.id.name;
if (nodeName === "formula" || nodeName === "cell") {
path.node.params.pop();
}
},
AssignmentPattern(path) {
if (path.node.left.name === "debugName") {
path.remove();
}
},
NewExpression(path) {
if (path.node.callee && path.node.callee.type === "Identifier") {
if (path.node.callee.name === "MergedCell" || path.node.callee.name === "Cell") {
path.node.arguments.pop();
}
}
},
CallExpression(path) {
if (path.node.callee && path.node.callee.type === "Identifier") {
const name = path.node.callee.name;
if (name === "addToTree" && path.node.arguments.length === 3) {
path.node.arguments.pop();
} else if (name === "cell" || name === "formula" || name === "resolveRenderable") {
if (path.node.arguments.length === 2) {
path.node.arguments.pop();
}
}
}
}
}
};
}
const booleanAttributes = ["checked", "readonly", "autoplay", "allowfullscreen", "async", "autofocus", "autoplay", "controls", "default", "defer", "disabled", "formnovalidate", "inert", "ismap", "itemscope", "loop", "multiple", "muted", "nomodule", "novalidate", "open", "playsinline", "required", "reversed", "selected"];
const propertyKeys = [
"class",
"shadowrootmode",
// boolean attributes (https://meiert.com/en/blog/boolean-attributes-of-html/)
"checked",
"readonly",
"value",
"autoplay",
"allowfullscreen",
"async",
"autofocus",
"autoplay",
"controls",
"default",
"defer",
"disabled",
"formnovalidate",
"inert",
"ismap",
"itemscope",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"required",
"reversed",
"selected"
];
const COMPILE_TIME_HELPERS = ["has-block-params", "has-block"];
const BUILTIN_HELPERS = {
"or": SYMBOLS.$__or,
"and": SYMBOLS.$__and,
"eq": SYMBOLS.$__eq,
"not": SYMBOLS.$__not,
"if": SYMBOLS.$__if,
"debugger": `${SYMBOLS.$__debugger}.call`,
"log": SYMBOLS.$__log,
"array": SYMBOLS.$__array,
"hash": SYMBOLS.$__hash,
"fn": SYMBOLS.$__fn
};
const SPECIAL_HELPERS = [SYMBOLS.HELPER_HELPER, SYMBOLS.MODIFIER_HELPER, SYMBOLS.COMPONENT_HELPER];
function patchNodePath(node, bindings) {
if (node.path.type !== "PathExpression") {
return;
}
if (bindings.has(node.path.original)) {
return;
}
if (node.path.original === "unless") {
node.path.original = BUILTIN_HELPERS["if"];
if (node.params.length === 3) {
const condTrue = node.params[1];
const condFalse = node.params[2];
node.params[1] = condFalse;
node.params[2] = condTrue;
} else {
node.params.push();
const condTrue = node.params[1];
const condFalse = {
type: "StringLiteral",
value: "",
original: "",
loc: node.loc
};
node.params[1] = condFalse;
node.params[2] = condTrue;
}
} else if (node.path.original === "debugger") {
node.path.original = BUILTIN_HELPERS["debugger"];
node.params.unshift(builders.path("this"));
} else if (node.path.original in BUILTIN_HELPERS) {
node.path.original = BUILTIN_HELPERS[node.path.original];
}
if (node.path.original.includes(".")) {
node.path.original = toSafeJSPath(toOptionalChaining(node.path.original));
}
}
function convert(seenNodes, flags, bindings = /* @__PURE__ */ new Set()) {
setFlags(flags);
setBindings(bindings);
function serializeParam(p) {
if (typeof p !== "string") {
if (typeof p === "object" && p !== null) {
let t = ToJSType(p, false);
if (typeof t !== "string") {
return String(t);
}
return toOptionalChaining(t);
}
return String(p);
}
return isPath(p) ? serializePath(p, false) : escapeString(p);
}
function toModifier(nodePath, params, hash) {
if (flags.WITH_MODIFIER_MANAGER) {
return `${SYMBOLS.$_maybeModifier}($:${resolvePath(nodePath)},$n,[${params.map((p) => serializeParam(p)).join(",")}],${toObject(hash)})`;
} else {
return `$:${resolvePath(nodePath)}($n,${params.map((p) => serializeParam(p)).join(",")})`;
}
}
function hasResolvedBinding(fnPath) {
let hasBinding = fnPath.startsWith("$_") || fnPath.startsWith("this.") || fnPath.startsWith("this[") || bindings.has(fnPath.split(".")[0]?.split("?")[0]);
if (COMPILE_TIME_HELPERS.includes(fnPath)) {
hasBinding = false;
}
return hasBinding;
}
function toHelper(nodePath, params, hash) {
const fnPath = resolvePath(nodePath);
if (SPECIAL_HELPERS.includes(fnPath)) {
return `$:${fnPath}([${params.map((p) => serializeParam(p)).join(",")}],${toObject(hash)})`;
}
let hasBinding = hasResolvedBinding(fnPath);
if ((!hasBinding || flags.WITH_HELPER_MANAGER) && !nodePath.startsWith("$_")) {
if (!hasBinding) {
hash.push([CONSTANTS.SCOPE_KEY, `$:()=>this[${SYMBOLS.$args}]?.${CONSTANTS.SCOPE_KEY}`]);
}
return `$:${SYMBOLS.$_maybeHelper}(${hasBinding ? fnPath : JSON.stringify(fnPath)},[${params.map((p) => serializeParam(p)).join(",")}],${toObject(hash)})`;
} else {
return `$:${fnPath}(${params.map((p) => serializeParam(p)).join(",")})`;
}
}
function ToJSType(node, wrap = true) {
seenNodes.add(node);
if (node.type === "ConcatStatement") {
return `$:() => [${node.parts.map((p) => {
if (p.type === "TextNode") {
seenNodes.add(p);
return escapeString(p.chars);
}
let value = ToJSType(p, false);
return value;
}).join(",")}].join('')`;
} else if (node.type === "UndefinedLiteral") {
return void 0;
} else if (node.type === "NullLiteral") {
return null;
} else if (node.type === "BooleanLiteral") {
return node.value;
} else if (node.type === "SubExpression") {
if (node.path.type !== "PathExpression") {
return null;
}
patchNodePath(node, bindings);
const hashArgs = node.hash.pairs.map((pair) => {
return [pair.key, ToJSType(pair.value, false)];
});
if (node.path.original === "element") {
return `$:function(args){${SYMBOLS.$_GET_ARGS}(this, arguments);const $fw = ${SYMBOLS.$_GET_FW}(this, arguments);const $slots = ${SYMBOLS.$_GET_SLOTS}(this, arguments);return{[${SYMBOLS.$nodes}]:[${SYMBOLS.TAG}(${ToJSType(node.params[0])}, $fw,[()=>${SYMBOLS.SLOT}('default',()=>[],$slots,this)], this)], ctx: this};}`;
} else if (node.path.original === SYMBOLS.$__hash) {
return `$:${SYMBOLS.$__hash}(${toObject(hashArgs)})`;
}
return toHelper(node.path.original, node.params, hashArgs);
} else if (node.type === "NumberLiteral") {
return node.value;
}
if (node.type === "StringLiteral") {
return escapeString(node.value);
} else if (node.type === "TextNode") {
if (node.chars.trim().length === 0 && node.chars.includes("\n")) {
return null;
} else if (node.chars.trim().length === 0 && node.chars.length > 1) {
return null;
}
return node.chars;
} else if (node.type === "ElementNode") {
return ElementToNode(node);
} else if (node.type === "PathExpression") {
return `$:${toOptionalChaining(resolvePath(node.original))}`;
} else if (node.type === "MustacheStatement") {
if (node.path.type !== "PathExpression") {
if (node.path.type === "BooleanLiteral" || node.path.type === "UndefinedLiteral" || node.path.type === "NumberLiteral" || node.path.type === "NullLiteral") {
return node.path.value;
} else if (node.path.type === "SubExpression") {
return `${wrap ? `$:() => ` : ""}${ToJSType(node.path)}`;
} else if (node.path.type === "StringLiteral") {
return escapeString(node.path.value);
}
return null;
}
patchNodePath(node, bindings);
const hashArgs = node.hash.pairs.map((pair) => {
return [pair.key, ToJSType(pair.value)];
});
if (node.path.original === "yield" || node.path.original === "outlet") {
let slotName = node.hash.pairs.find((p) => p.key === "to")?.value || "default";
if (typeof slotName !== "string") {
slotName = ToJSType(slotName);
}
return {
type: "yield",
isControl: true,
blockParams: node.params.map((p) => ToJSType(p)),
children: [],
inverse: [],
key: slotName,
condition: "",
isSync: true
};
}
if (node.params.length === 0) {
if (node.path.original === SYMBOLS.$__hash) {
return `${wrap ? `$:() => ` : ""}${ToJSType(node.path)}(${toObject(hashArgs)})`;
}
if (hashArgs.length === 0) {
const fnPath = resolvePath(node.path.original);
let hasBinding = hasResolvedBinding(fnPath);
if (!hasBinding) {
return toHelper(node.path.original, [], hashArgs);
} else {
return ToJSType(node.path);
}
}
return toHelper(node.path.original, [], hashArgs);
} else {
return `${wrap ? `$:() => ` : ""}${toHelper(node.path.original, node.params, hashArgs)}`;
}
} else if (node.type === "BlockStatement") {
if (!node.params.length) {
return null;
}
node.program.blockParams.forEach((p) => {
bindings.add(p);
});
const childElements = resolvedChildren(node.program.body);
const elseChildElements = node.inverse?.body ? resolvedChildren(node.inverse.body) : void 0;
if (!childElements.length) {
return null;
}
if (node.path.type !== "PathExpression") {
return null;
}
const name = node.path.original;
const keyPair = node.hash.pairs.find((p) => p.key === "key");
const syncPair = node.hash.pairs.find((p) => p.key === "sync");
let keyValue = null;
let syncValue = false;
if (keyPair) {
if (keyPair.value.type === "StringLiteral") {
keyValue = keyPair.value.original;
} else {
keyValue = ToJSType(keyPair.value);
}
}
if (syncPair) {
if (syncPair.value.type === "BooleanLiteral") {
syncValue = syncPair.value.value;
} else {
syncValue = ToJSType(syncPair.value);
}
}
const children = childElements?.map((el) => ToJSType(el)) ?? null;
const inverse = elseChildElements?.map((el) => ToJSType(el)) ?? null;
const cleanupBindings = () => {
node.program.blockParams.forEach((p) => {
bindings.delete(p);
});
};
try {
if (name === "in-element") {
return {
type: "in-element",
isControl: true,
condition: ToJSType(node.params[0]),
blockParams: [],
children,
inverse: [],
isSync: true,
key: ""
};
} else if (name === "unless") {
return {
type: "if",
isControl: true,
condition: serializePath(ToJSType(node.params[0])),
blockParams: node.program.blockParams,
children: inverse,
inverse: children,
isSync: syncValue,
key: keyValue
};
} else if (name === "let") {
let fixChildScopes = function(str) {
Object.keys(namesToReplace).forEach((key) => {
const re = new RegExp(`(?<!\\.)\\b${key}\\b(?!(=|'|"|:)[^ ]*)`, "g");
if (primitives.has(key)) {
str = str.replace(re, namesToReplace[key]);
} else {
str = str.replace(re, `${namesToReplace[key]}()`);
}
});
return str;
};
const varScopeName = Math.random().toString(36).substring(7);
const namesToReplace = {};
const primitives = /* @__PURE__ */ new Set();
const vars = node.params.map((p, index) => {
let isString = p.type === "StringLiteral";
let isBoolean = p.type === "BooleanLiteral";
let isNumber = p.type === "NumberLiteral";
let isNull = p.type === "NullLiteral";
let isUndefined = p.type === "UndefinedLiteral";
let originalName = node.program.blockParams[index];
let newName = `Let_${originalName}_${varScopeName}`;
namesToReplace[originalName] = `${newName}`;
let castToPrimitive = isString || isBoolean || isNull || isUndefined || isNumber;
if (castToPrimitive) {
primitives.add(originalName);
return `let ${newName} = ${ToJSType(p, false)};`;
} else {
return `let ${newName} = $:() => ${ToJSType(p)};`;
}
});
const result = `$:...(() => {let self = this;${vars.join("").split("this.").join("self.")}return [${fixChildScopes(serializeChildren(
children,
"this"
// @todo - fix possible context floating here
))}]})()`;
return result;
}
return {
type: name,
isControl: true,
condition: serializePath(ToJSType(node.params[0])),
blockParams: node.program.blockParams,
isSync: syncValue,
children,
inverse,
key: keyValue
};
} finally {
cleanupBindings();
}
}
}
function hasStableChild(node) {
const childrenWithoutEmptyTextNodes = resolvedChildren(node.children);
if (childrenWithoutEmptyTextNodes.length === 0) {
return true;
}
const firstChild = childrenWithoutEmptyTextNodes[0];
if (firstChild.type === "TextNode") {
return true;
}
if (firstChild.type === "ElementNode" && !firstChild.tag.startsWith(":") && firstChild.tag.toLowerCase() === firstChild.tag) {
return true;
}
return false;
}
const propsToCast = {
class: "",
// className
readonly: "readOnly"
};
function isAttribute(name) {
return !propertyKeys.includes(name);
}
const convertedNodes = /* @__PURE__ */ new WeakSet();
function ElementToNode(element) {
if (element.tag === "math" && !convertedNodes.has(element)) {
convertedNodes.add(element);
const parent = builders.element(`$:${SYMBOLS.MATH_NAMESPACE}`, {
children: [element]
});
return ElementToNode(parent);
} else if (element.tag === "svg" && !convertedNodes.has(element)) {
convertedNodes.add(element);
const parent = builders.element(`$:${SYMBOLS.SVG_NAMESPACE}`, {
children: [element]
});
return ElementToNode(parent);
} else if (element.tag === "foreignObject" && !convertedNodes.has(element)) {
convertedNodes.add(element);
const parent = builders.element(`$:${SYMBOLS.HTML_NAMESPACE}`, {
children: element.children
});
element.children = [parent];
return ElementToNode(parent);
}
element.blockParams.forEach((p) => {
bindings.add(p);
});
const children = resolvedChildren(element.children).map((el) => ToJSType(el)).filter((el) => el !== null);
element.blockParams.forEach((p) => {
bindings.delete(p);
});
const rawStyleEvents = element.attributes.filter((attr) => {
return attr.name.startsWith("style.");
});
element.attributes = element.attributes.filter((attr) => {
return !rawStyleEvents.includes(attr);
});
const styleEvents = rawStyleEvents.map((attr) => {
const propertyName = attr.name.split(".").pop();
const value = attr.value.type === "TextNode" ? escapeString(attr.value.chars) : ToJSType(attr.value);
const isPath2 = typeof value === "string" ? value.includes(".") : false;
return [EVENT_TYPE.ON_CREATED, `$:function($v,$n){$n.style.setProperty('${propertyName}',$v);}.bind(null,${SYMBOLS.$_TO_VALUE}(${isPath2 ? `$:()=>${value}` : value}))`];
});
const extraEvents = [];
const node = {
tag: element.tag,
selfClosing: element.selfClosing,
blockParams: element.blockParams,
hasStableChild: hasStableChild(element),
attributes: element.attributes.filter((el) => isAttribute(el.name)).map((attr) => {
const rawValue = ToJSType(attr.value);
return [attr.name, rawValue];
}),
properties: element.attributes.filter((el) => !isAttribute(el.name)).map((attr) => {
const rawValue = ToJSType(attr.value);
if (booleanAttributes.includes(attr.name) && attr.value.type === "TextNode" && attr.value.chars === "") {
const castedProp2 = propsToCast[attr.name];
return [typeof castedProp2 === "string" ? castedProp2 : attr.name, true];
}
const castedProp = propsToCast[attr.name];
return [typeof castedProp === "string" ? castedProp : attr.name, rawValue];
}),
events: [...extraEvents, ...styleEvents, ...element.modifiers.map((mod) => {
if (mod.path.type !== "PathExpression") {
return null;
}
const hashArgs = mod.hash.pairs.map((pair) => {
return [pair.key, ToJSType(pair.value, false)];
});
if (mod.path.original === "on") {
const firstParam = mod.params[0];
if (firstParam.type === "StringLiteral") {
const tail = mod.params.slice(2).map((p) => ToJSType(p)).join(",");
return [firstParam.original, `$:($e, $n) => ${ToJSType(mod.params[1])}($e, $n${tail.length ? `,${tail}` : ""})`];
} else {
return null;
}
} else {
return [
// @me here
EVENT_TYPE.ON_CREATED,
`$:($n) => ${toModifier(mod.path.original, mod.params, hashArgs)}`
];
}
}).filter((el) => el !== null)],
children
};
if (children.length === 1 && typeof children[0] === "string") {
const v = children[0];
if (!v.includes(SYMBOLS.SLOT) && !node.tag.startsWith(":") && node.tag.toLowerCase() === node.tag && !v.includes("...")) {
node.children = [];
node.events.push([EVENT_TYPE.TEXT_CONTENT, v]);
}
}
return node;
}
return {
ToJSType,
ElementToNode
};
}
function isSimpleElement(element) {
const tag = element.tag;
if (tag.includes(".") || tag.startsWith(":")) {
return false;
}
return tag.toLowerCase() === tag;
}
function isAllChildNodesSimpleElements(children) {
return children.every((child) => {
if (child.type === "ElementNode") {
return isSimpleElement(child) && isAllChildNodesSimpleElements(child.children);
} else if (child.type === "TextNode") {
return true;
} else if (child.type === "MustacheCommentStatement") {
return true;
} else if (child.type === "CommentStatement") {
return true;
} else if (child.type === "MustacheStatement") {
if (child.path.type !== "PathExpression") {
return false;
} else if (child.path.original === "yield" || child.path.original === "outlet") {
return false;
} else if (child.path.data) {
return true;
}
}
return false;
});
}
function processTransformedFiles(babelResult, hbsToProcess, seenNodes, flags, fileName, programs, programResults) {
const txt = babelResult?.code ?? "";
const globalFlags = flags;
hbsToProcess.forEach((content) => {
const flags2 = content.flags;
const bindings = content.bindings;
const {
ToJSType,
ElementToNode
} = convert(seenNodes, globalFlags, bindings);
const ast = preprocess(content.template);
const program = {
meta: flags2,
bindings,
template: []
};
traverse(ast, {
Template(node) {
const isSimple = isAllChildNodesSimpleElements(node.body);
if (!isSimple && flags2.hasThisAccess === false) {
flags2.hasThisAccess = true;
}
},
MustacheStatement(node) {
if (seenNodes.has(node)) {
return;
}
seenNodes.add(node);
program.template.push(ToJSType(node));
},
TextNode(node) {
if (seenNodes.has(node)) {
return;
}
seenNodes.add(node);
if (node.chars.trim().length !== 0) {
program.template.push(ToJSType(node));
}
},
BlockStatement(node) {
if (seenNodes.has(node)) {
return;
}
seenNodes.add(node);
program.template.push(ToJSType(node));
},
ElementNode: {
enter(node) {
if (seenNodes.has(node)) {
return;
}
node.blockParams.forEach((p) => {
bindings.add(p);
});
seenNodes.add(node);
program.template.push(ElementToNode(node));
},
exit(node) {
node.blockParams.forEach((p) => {
bindings.delete(p);
});
}
}
});
programs.push(program);
});
programs.forEach((program) => {
const input = program.template;
setBindings(program.bindings);
const results = input.reduce((acc, node) => {
const serializedNode = serializeNode(node);
if (typeof serializedNode === "string") {
acc.push(serializedNode);
return acc;
}
return acc;
}, []);
const isClass = txt?.includes("template = ") ?? false;
const isTemplateTag = fileName.endsWith(".gts") || fileName.endsWith(".gjs");
let result = "";
let finContext = program.meta.hasThisAccess ? "this" : "this";
const hasFw = results.some((el) => el.includes("$fw"));
const hasSlots = results.some((el) => el.includes("$slots"));
const slotsResolution = `const $slots = ${SYMBOLS.$_GET_SLOTS}(this, arguments);`;
const maybeFw = `${hasFw ? `const $fw = ${SYMBOLS.$_GET_FW}(this, arguments);` : ""}`;
const maybeSlots = `${hasSlots ? slotsResolution : ""}`;
const declareRoots = `const roots = [${results.join(", ")}];`;
const declareReturn = `return ${SYMBOLS.FINALIZE_COMPONENT}(roots, ${finContext});`;
if (isTemplateTag) {
result = `function () {
${maybeFw}
${SYMBOLS.$_GET_ARGS}(this, arguments);
${maybeSlots}
${declareRoots}
${declareReturn}
}`;
} else {
result = isClass ? `() => {
${maybeSlots}
${maybeFw}
${declareRoots}
${declareReturn}
}` : `(() => {
${SYMBOLS.$_GET_ARGS}(this, arguments);
${maybeSlots}
${maybeFw}
${declareRoots}
${declareReturn}
})()`;
}
programResults.push(result);
});
let src = txt ?? "";
programResults.forEach((result) => {
src = src?.replace("$placeholder", result);
});
return src.split("$:").join("");
}
function transform(source, fileName, mode, isLibBuild = false, flags) {
const programs = [];
const seenNodes = /* @__PURE__ */ new Set();
const rawTxt = source;
const hbsToProcess = [];
const programResults = [];
const isAsync = flags.ASYNC_COMPILE_TRANSFORMS;
const plugins = [processTemplate(hbsToProcess, mode)];
if (!isLibBuild) {
plugins.push("module:decorator-transforms");
}
const replacedFileName = fileName.replace(".gts", ".ts").replace(".gjs", ".js");
const babelConfig = {
plugins,
filename: replacedFileName,
presets: [["@babel/preset-typescript", {
allExtensions: true,
onlyRemoveTypeImports: true
}]]
};
if (isAsync) {
return transformAsync(rawTxt, babelConfig).then((babelResult) => {
return processTransformedFiles(babelResult, hbsToProcess, seenNodes, flags, fileName, programs, programResults);
});
} else {
const babelResult = transformSync(rawTxt, babelConfig);
return processTransformedFiles(babelResult, hbsToProcess, seenNodes, flags, fileName, programs, programResults);
}
}
function defaultFlags() {
return {
IS_GLIMMER_COMPAT_MODE: true,
RUN_EVENT_DESTRUCTORS_FOR_SCOPED_NODES: false,
TRY_CATCH_ERROR_HANDLING: true,
SUPPORT_SHADOW_DOM: true,
REACTIVE_MODIFIERS: true,
WITH_HELPER_MANAGER: false,
WITH_MODIFIER_MANAGER: false,
WITH_EMBER_INTEGRATION: false,
WITH_CONTEXT_API: true,
ASYNC_COMPILE_TRANSFORMS: true
};
}
function fixExportsForHMR(code) {
return code.split("export const ").join("export let ");
}
function shouldHotReloadFile(fileName, code) {
const isProperExtension = fileName.endsWith(".gts") || fileName.endsWith(".gjs");
const isNotTest = !fileName.includes("-test");
const hasTemplateTag = code.includes("<template>");
return isProperExtension && isNotTest && hasTemplateTag;
}
const HMR = `
if (import.meta.hot) {
const existingTokensToReload: string[] = [];
import.meta.hot.accept((newModule) => {
if (newModule) {
const moduleTokens = Object.keys(newModule);
const newTokens = moduleTokens.filter(
(token) => !existingTokensToReload.includes(token),
);
if (
newTokens.length ||
moduleTokens.length !== existingTokensToReload.length
) {
import.meta.hot?.invalidate();
} else {
moduleTokens.forEach((token) => {
const oldModule = existingTokensToReload.find((t) => t === token);
if (oldModule) {
window.hotReload(eval(oldModule), newModule[token]);
}
});
existingTokensToReload.length = 0;
existingTokensToReload.push(...moduleTokens);
}
}
});
}
`;
const p = new Preprocessor();
function fixContentTagOutput(code) {
return code.split("static{").join("$static() {");
}
const extensionsToResolve = [".mjs", ".js", ".mts", ".ts", ".jsx", ".tsx", ".json", ".gts", ".gjs"];
const templateFileRegex = /\.(gts|gjs)$/;
const scriptFileRegex = /\.(ts|js)$/;
function compiler(mode, options = {}) {
let isLibBuild = false;
let flags = defaultFlags();
return {
enforce: "pre",
name: "glimmer-next",
config(config, mode2) {
if (options.authorMode === true) {
isLibBuild = config.build?.lib !== void 0;
}
flags = {
...flags,
...options.flags ?? {}
};
let defineValues = flags;
if (!isLibBuild) {
defineValues["IS_DEV_MODE"] = mode2.mode === "development";
} else {
defineValues = {};
}
return {
define: defineValues,
resolve: {
extensions: extensionsToResolve
}
};
},
transform(code, file) {
if (templateFileRegex.test(file)) {
const intermediate = fixContentTagOutput(p.process(code, {
filename: file
}));
if (mode === "development") {
const shouldHotReload = options.disableHMR ? false : shouldHotReloadFile(file, code);
return transform(fixExportsForHMR(intermediate) + (shouldHotReload ? HMR : ""), file, mode, isLibBuild, flags);
} else {
return transform(intermediate, file, mode, isLibBuild, flags);
}
}
if (!code.includes(MAIN_IMPORT)) {
return;
}
let result = void 0;
if (scriptFileRegex.test(file)) {
const source = code;
const result2 = transform(source, file, mode, false, flags);
return result2;
}
return result;
}
};
}
export { compiler, stripGXTDebug };