release: WRNexusJS 0.2.32
This commit is contained in:
@@ -289,6 +289,8 @@ function parse(source) {
|
||||
const functions = [];
|
||||
const dataApis = [];
|
||||
const modeFunctions = [];
|
||||
const lifecycle = {};
|
||||
const watches = [];
|
||||
const apis = [];
|
||||
const realtimes = [];
|
||||
while (lx.peek().type !== "rbrace") {
|
||||
@@ -413,6 +415,40 @@ function parse(source) {
|
||||
styles.push(lx.readBalancedBraces());
|
||||
break;
|
||||
}
|
||||
case "lifecycle": {
|
||||
lx.next();
|
||||
expect("lbrace");
|
||||
while (lx.peek().type !== "rbrace") {
|
||||
const hook = lx.peek();
|
||||
if (hook.type === "eof") {
|
||||
throw new ParseError("Unexpected end of input inside lifecycle block");
|
||||
}
|
||||
if (hook.type !== "ident") {
|
||||
throw new ParseError(`Expected a lifecycle hook at offset ${hook.pos}`);
|
||||
}
|
||||
if (hook.value !== "mount" && hook.value !== "update" && hook.value !== "unmount") {
|
||||
throw new ParseError(`Unknown lifecycle hook '${hook.value}' at offset ${hook.pos}`);
|
||||
}
|
||||
const hookName = hook.value;
|
||||
lx.next();
|
||||
if (lifecycle[hookName] !== undefined) {
|
||||
throw new ParseError(`Duplicate lifecycle hook '${hookName}' at offset ${hook.pos}`);
|
||||
}
|
||||
lifecycle[hookName] = lx.readBalancedBraces();
|
||||
}
|
||||
expect("rbrace");
|
||||
break;
|
||||
}
|
||||
case "watch": {
|
||||
lx.next();
|
||||
const stateName = expect("ident").value;
|
||||
const body = lx.readBalancedBraces();
|
||||
watches.push({
|
||||
state: stateName,
|
||||
body
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "functions": {
|
||||
lx.next();
|
||||
functions.push(lx.readBalancedBraces());
|
||||
@@ -423,6 +459,12 @@ function parse(source) {
|
||||
}
|
||||
}
|
||||
expect("rbrace");
|
||||
const declaredStates = new Set(states.map((state) => state.name));
|
||||
for (const watcher of watches) {
|
||||
if (!declaredStates.has(watcher.state)) {
|
||||
throw new ParseError(`Cannot watch undeclared state '${watcher.state}'`);
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: "page",
|
||||
kind,
|
||||
@@ -436,6 +478,8 @@ function parse(source) {
|
||||
functions,
|
||||
dataApis,
|
||||
modeFunctions,
|
||||
lifecycle,
|
||||
watches,
|
||||
apis,
|
||||
realtimes
|
||||
};
|
||||
@@ -716,10 +760,18 @@ function renderAttr(attr) {
|
||||
}
|
||||
}
|
||||
function eventAttribute(name) {
|
||||
if (name.startsWith("browser-"))
|
||||
if (name.startsWith("window:")) {
|
||||
return `data-on-window-${name.slice("window:".length)}`;
|
||||
}
|
||||
if (name.startsWith("document:")) {
|
||||
return `data-on-document-${name.slice("document:".length)}`;
|
||||
}
|
||||
if (name.startsWith("browser-")) {
|
||||
return `data-on-wrnexus-browser-${name.slice(8)}`;
|
||||
if (name.startsWith("mobile-"))
|
||||
}
|
||||
if (name.startsWith("mobile-")) {
|
||||
return `data-on-wrnexus-mobile-${name.slice(7)}`;
|
||||
}
|
||||
return `data-on-${name}`;
|
||||
}
|
||||
function reactiveAttrValue(raw, reactive) {
|
||||
@@ -924,7 +976,7 @@ function renderNestedComponentInvocation(node, ctx) {
|
||||
let bindIndex = 0;
|
||||
const attrs = node.attrs.filter((attr) => attr.name !== "data-component").map((attr) => {
|
||||
if (attr.event) {
|
||||
return ` ${eventAttribute(attr.name)}="${compileAttrValue(attr.value, ctx)}"`;
|
||||
return escLit(` ${eventAttribute(attr.name)}="`) + escLit(attrEscape(attr.value)) + escLit(`"`);
|
||||
}
|
||||
if (attr.boolean) {
|
||||
return ` ${attr.name}`;
|
||||
@@ -1262,6 +1314,34 @@ function safeRef(name) {
|
||||
function escLit(s) {
|
||||
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
||||
}
|
||||
function componentBehavior(ast) {
|
||||
const functions = ast.functions.map((body) => body.trim()).filter(Boolean).join(`
|
||||
|
||||
`);
|
||||
const lifecycle = {
|
||||
...ast.lifecycle.mount?.trim() ? { mount: ast.lifecycle.mount.trim() } : {},
|
||||
...ast.lifecycle.update?.trim() ? { update: ast.lifecycle.update.trim() } : {},
|
||||
...ast.lifecycle.unmount?.trim() ? { unmount: ast.lifecycle.unmount.trim() } : {}
|
||||
};
|
||||
const watches = ast.watches.map((watch) => ({
|
||||
state: watch.state,
|
||||
body: watch.body.trim()
|
||||
}));
|
||||
if (!functions && Object.keys(lifecycle).length === 0 && watches.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
functions,
|
||||
lifecycle,
|
||||
watches
|
||||
};
|
||||
}
|
||||
function behaviorAttribute(behavior) {
|
||||
if (!behavior) {
|
||||
return "";
|
||||
}
|
||||
return ` data-wrn-behavior="${attrEscape(JSON.stringify(behavior))}"`;
|
||||
}
|
||||
var INTERP_RE = /\{([^{}]+)\}/g;
|
||||
function exprRefsState(expr, stateNames) {
|
||||
for (const name of stateNames) {
|
||||
@@ -1340,7 +1420,7 @@ function renderComponentNode(node, ctx) {
|
||||
}
|
||||
const attrs = node.attrs.filter((a) => a.name !== "class" && !a.name.startsWith("class:")).map((a) => {
|
||||
if (a.event) {
|
||||
return ` ${eventAttribute(a.name)}="${compileAttrValue(a.value, ctx)}"`;
|
||||
return ` ${eventAttribute(a.name)}="${escLit(attrEscape(a.value))}"`;
|
||||
}
|
||||
if (a.boolean) {
|
||||
return ` ${a.name}`;
|
||||
@@ -1400,11 +1480,13 @@ function generateComponent(ast) {
|
||||
${styles.map(styleEscape).join(`
|
||||
`)}
|
||||
</style>`) : "";
|
||||
const needsScope = ast.states.length > 0 || viewHasEvents(ast.view);
|
||||
const behavior = componentBehavior(ast);
|
||||
const needsScope = ast.states.length > 0 || viewHasEvents(ast.view) || behavior !== null;
|
||||
const scopeKeys = [
|
||||
...effectiveProps.map((prop) => prop.name),
|
||||
...ast.states.map((state) => state.name)
|
||||
];
|
||||
const behaviorAttr = behaviorAttribute(behavior);
|
||||
const decls = [];
|
||||
for (const prop of effectiveProps) {
|
||||
decls.push(` const ${nameRefs.get(prop.name)} = __coerce(__p[${JSON.stringify(prop.name)}], (${resolveExpr(prop.default)}));`);
|
||||
@@ -1412,7 +1494,7 @@ ${styles.map(styleEscape).join(`
|
||||
for (const state of ast.states) {
|
||||
decls.push(` const ${nameRefs.get(state.name)} = (${resolveExpr(state.expr)});`);
|
||||
}
|
||||
const returnExpr = needsScope ? "`" + styleTag + '<div data-scope="${__scope}">' + viewCode + "</div>`" : "`" + styleTag + viewCode + "`";
|
||||
const returnExpr = needsScope ? "`" + styleTag + `<div data-scope="\${__scope}"${behaviorAttr}>` + viewCode + "</div>`" : "`" + styleTag + viewCode + "`";
|
||||
const scopeLine = needsScope && scopeKeys.length > 0 ? ` const __scope = __wrnexusScopeDecl({ ${scopeKeys.map((k) => `${JSON.stringify(k)}: ${nameRefs.get(k)}`).join(", ")} });
|
||||
` : needsScope ? ` const __scope = "";
|
||||
` : "";
|
||||
@@ -1421,6 +1503,9 @@ ${styles.map(styleEscape).join(`
|
||||
} else {
|
||||
out.push(`export const __wrnexusComponent = ${JSON.stringify(ast.name)};`);
|
||||
}
|
||||
if (behavior) {
|
||||
out.push(`export const __wrnexusBehavior = ${JSON.stringify(behavior, null, 2)};`);
|
||||
}
|
||||
out.push(`function __coerce(v: any, def: any): any {
|
||||
if (v === undefined || v === null) return def;
|
||||
if (typeof def === "number") return Number(v);
|
||||
|
||||
Reference in New Issue
Block a user