add ssr-counter example demonstrating SSR + hydration end-to-end

Counter.el defines Counter, CounterPair, and App components with {#if}
conditionals, event handlers, and child component composition.

render.js (generated by elc-ui --target=server) is the Node.js render
script the server calls. app.js (generated by elc-ui --target=web) is the
compiled browser module. hydrate.js attaches the runtime to the
server-rendered DOM without replacing it.

index.html shows the integration point with instructions for injecting
server output and the two CLI commands needed to regenerate both targets.
This commit is contained in:
Will Anderson
2026-05-04 12:49:19 -05:00
parent f8e32e7719
commit 143137e2b4
6 changed files with 632 additions and 10 deletions
+21 -10
View File
@@ -218,16 +218,27 @@ function emitComponentClass(comp, components) {
* @returns {string}
*/
function transformMethodBody(body, stateNames) {
let result = body;
for (const name of stateNames) {
// Replace bare assignments: name = expr (not inside ==, !=, <=, >=)
const re = new RegExp(`(?<![=!<>])\\b${name}\\s*=(?!=)\\s*`, 'g');
result = result.replace(re, `__self.setState('${name}', `);
// We need to close the paren — this is tricky without full parsing.
// For now, append ) at end of expression line (works for simple cases).
// Full statement-level transformation requires the parser.
}
return result;
// Process line-by-line: for each line that contains a bare state assignment,
// rewrite: `name = expr` -> `__self.setState('name', expr)`
// This is a line-level transformation that works for the common case where
// each state assignment is on its own line (the el-ui style).
const lines = body.split('\n');
const transformed = lines.map(line => {
let changed = line;
for (const name of stateNames) {
// Match: optional whitespace + name + = (not ==, !=, <=, >=) + rest of line
const re = new RegExp(`^(\\s*)${name}\\s*=(?![=>])\\s*(.+)$`);
const m = changed.match(re);
if (m) {
// m[1] = leading whitespace, m[2] = RHS expression
// Strip trailing newline/whitespace from the RHS
const rhs = m[2].trimEnd();
changed = `${m[1]}__self.setState('${name}', ${rhs})`;
}
}
return changed;
});
return transformed.join('\n');
}
/**