diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index db184cd..ac5573c 100644 --- a/shared/static/scripts/sx-browser.js +++ b/shared/static/scripts/sx-browser.js @@ -14,7 +14,7 @@ // ========================================================================= var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); - var SX_VERSION = "2026-03-07T19:36:54Z"; + var SX_VERSION = "2026-03-07T20:18:37Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -565,92 +565,6 @@ return makeThunk(componentBody(comp), local); }; - // ========================================================================= - // Platform: deps module — component dependency analysis - // ========================================================================= - - function componentDeps(c) { - return c.deps ? c.deps.slice() : []; - } - - function componentSetDeps(c, deps) { - c.deps = deps; - } - - function componentCssClasses(c) { - return c.cssClasses ? c.cssClasses.slice() : []; - } - - function envComponents(env) { - var names = []; - for (var k in env) { - var v = env[k]; - if (v && (v._component || v._macro)) names.push(k); - } - return names; - } - - function regexFindAll(pattern, source) { - var re = new RegExp(pattern, "g"); - var results = []; - var m; - while ((m = re.exec(source)) !== null) { - if (m[1] !== undefined) results.push(m[1]); - else results.push(m[0]); - } - return results; - } - - function scanCssClasses(source) { - var classes = {}; - var result = []; - var m; - var re1 = /:class\s+"([^"]*)"/g; - while ((m = re1.exec(source)) !== null) { - var parts = m[1].split(/\s+/); - for (var i = 0; i < parts.length; i++) { - if (parts[i] && !classes[parts[i]]) { - classes[parts[i]] = true; - result.push(parts[i]); - } - } - } - var re2 = /:class\s+\(str\s+((?:"[^"]*"\s*)+)\)/g; - while ((m = re2.exec(source)) !== null) { - var re3 = /"([^"]*)"/g; - var m2; - while ((m2 = re3.exec(m[1])) !== null) { - var parts2 = m2[1].split(/\s+/); - for (var j = 0; j < parts2.length; j++) { - if (parts2[j] && !classes[parts2[j]]) { - classes[parts2[j]] = true; - result.push(parts2[j]); - } - } - } - } - var re4 = /;;\s*@css\s+(.+)/g; - while ((m = re4.exec(source)) !== null) { - var parts3 = m[1].split(/\s+/); - for (var k = 0; k < parts3.length; k++) { - if (parts3[k] && !classes[parts3[k]]) { - classes[parts3[k]] = true; - result.push(parts3[k]); - } - } - } - return result; - } - - function componentIoRefs(c) { - return c.ioRefs ? c.ioRefs.slice() : []; - } - - function componentSetIoRefs(c, refs) { - c.ioRefs = refs; - } - - // ========================================================================= // Platform interface — Parser // ========================================================================= @@ -2093,6 +2007,13 @@ return domAppendToHead(link); }, domQueryAll(container, "link[rel=\"stylesheet\" // page-data-cache-set var pageDataCacheSet = function(cacheKey, data) { return dictSet(_pageDataCache, cacheKey, {"data": data, "ts": nowMs()}); }; + // current-page-layout + var currentPageLayout = function() { return (function() { + var pathname = urlPathname(browserLocationHref()); + var match = findMatchingRoute(pathname, _pageRoutes); + return (isSxTruthy(isNil(match)) ? "" : sxOr(get(match, "layout"), "")); +})(); }; + // swap-rendered-content var swapRenderedContent = function(target, rendered, pathname) { return (domSetTextContent(target, ""), domAppend(target, rendered), hoistHeadElementsFull(target), processElements(target), sxHydrateElements(target), domDispatch(target, "sx:clientRoute", {["pathname"]: pathname}), logInfo((String("sx:route client ") + String(pathname)))); }; @@ -2110,6 +2031,9 @@ return domAppendToHead(link); }, domQueryAll(container, "link[rel=\"stylesheet\" var tryClientRoute = function(pathname, targetSel) { return (function() { var match = findMatchingRoute(pathname, _pageRoutes); return (isSxTruthy(isNil(match)) ? (logInfo((String("sx:route no match (") + String(len(_pageRoutes)) + String(" routes) ") + String(pathname))), false) : (function() { + var targetLayout = sxOr(get(match, "layout"), ""); + var curLayout = currentPageLayout(); + return (isSxTruthy(!isSxTruthy((targetLayout == curLayout))) ? (logInfo((String("sx:route server (layout: ") + String(curLayout) + String(" -> ") + String(targetLayout) + String(") ") + String(pathname))), false) : (function() { var contentSrc = get(match, "content"); var closure = sxOr(get(match, "closure"), {}); var params = get(match, "params"); @@ -2147,6 +2071,7 @@ return (function() { })())); })()); })()); +})()); })(); }; // bind-client-route-link @@ -2584,119 +2509,6 @@ callExpr.push(dictGet(kwargs, k)); } } var bootInit = function() { return (logInfo((String("sx-browser ") + String(SX_VERSION))), initCssTracking(), initStyleDict(), processPageScripts(), processSxScripts(NIL), sxHydrateElements(NIL), processElements(NIL)); }; - // === Transpiled from deps (component dependency analysis) === - - // scan-refs - var scanRefs = function(node) { return (function() { - var refs = []; - scanRefsWalk(node, refs); - return refs; -})(); }; - - // scan-refs-walk - var scanRefsWalk = function(node, refs) { return (isSxTruthy((typeOf(node) == "symbol")) ? (function() { - var name = symbolName(node); - return (isSxTruthy(startsWith(name, "~")) ? (isSxTruthy(!isSxTruthy(contains(refs, name))) ? append_b(refs, name) : NIL) : NIL); -})() : (isSxTruthy((typeOf(node) == "list")) ? forEach(function(item) { return scanRefsWalk(item, refs); }, node) : (isSxTruthy((typeOf(node) == "dict")) ? forEach(function(key) { return scanRefsWalk(dictGet(node, key), refs); }, keys(node)) : NIL))); }; - - // transitive-deps-walk - var transitiveDepsWalk = function(n, seen, env) { return (isSxTruthy(!isSxTruthy(contains(seen, n))) ? (append_b(seen, n), (function() { - var val = envGet(env, n); - return (isSxTruthy((typeOf(val) == "component")) ? forEach(function(ref) { return transitiveDepsWalk(ref, seen, env); }, scanRefs(componentBody(val))) : (isSxTruthy((typeOf(val) == "macro")) ? forEach(function(ref) { return transitiveDepsWalk(ref, seen, env); }, scanRefs(macroBody(val))) : NIL)); -})()) : NIL); }; - - // transitive-deps - var transitiveDeps = function(name, env) { return (function() { - var seen = []; - var key = (isSxTruthy(startsWith(name, "~")) ? name : (String("~") + String(name))); - transitiveDepsWalk(key, seen, env); - return filter(function(x) { return !isSxTruthy((x == key)); }, seen); -})(); }; - - // compute-all-deps - var computeAllDeps = function(env) { return forEach(function(name) { return (function() { - var val = envGet(env, name); - return (isSxTruthy((typeOf(val) == "component")) ? componentSetDeps(val, transitiveDeps(name, env)) : NIL); -})(); }, envComponents(env)); }; - - // scan-components-from-source - var scanComponentsFromSource = function(source) { return (function() { - var matches = regexFindAll("\\(~([a-zA-Z_][a-zA-Z0-9_\\-]*)", source); - return map(function(m) { return (String("~") + String(m)); }, matches); -})(); }; - - // components-needed - var componentsNeeded = function(pageSource, env) { return (function() { - var direct = scanComponentsFromSource(pageSource); - var allNeeded = []; - { var _c = direct; for (var _i = 0; _i < _c.length; _i++) { var name = _c[_i]; if (isSxTruthy(!isSxTruthy(contains(allNeeded, name)))) { - allNeeded.push(name); -} -(function() { - var val = envGet(env, name); - return (function() { - var deps = (isSxTruthy((isSxTruthy((typeOf(val) == "component")) && !isSxTruthy(isEmpty(componentDeps(val))))) ? componentDeps(val) : transitiveDeps(name, env)); - return forEach(function(dep) { return (isSxTruthy(!isSxTruthy(contains(allNeeded, dep))) ? append_b(allNeeded, dep) : NIL); }, deps); -})(); -})(); } } - return allNeeded; -})(); }; - - // page-component-bundle - var pageComponentBundle = function(pageSource, env) { return componentsNeeded(pageSource, env); }; - - // page-css-classes - var pageCssClasses = function(pageSource, env) { return (function() { - var needed = componentsNeeded(pageSource, env); - var classes = []; - { var _c = needed; for (var _i = 0; _i < _c.length; _i++) { var name = _c[_i]; (function() { - var val = envGet(env, name); - return (isSxTruthy((typeOf(val) == "component")) ? forEach(function(cls) { return (isSxTruthy(!isSxTruthy(contains(classes, cls))) ? append_b(classes, cls) : NIL); }, componentCssClasses(val)) : NIL); -})(); } } - { var _c = scanCssClasses(pageSource); for (var _i = 0; _i < _c.length; _i++) { var cls = _c[_i]; if (isSxTruthy(!isSxTruthy(contains(classes, cls)))) { - classes.push(cls); -} } } - return classes; -})(); }; - - // scan-io-refs-walk - var scanIoRefsWalk = function(node, ioNames, refs) { return (isSxTruthy((typeOf(node) == "symbol")) ? (function() { - var name = symbolName(node); - return (isSxTruthy(contains(ioNames, name)) ? (isSxTruthy(!isSxTruthy(contains(refs, name))) ? append_b(refs, name) : NIL) : NIL); -})() : (isSxTruthy((typeOf(node) == "list")) ? forEach(function(item) { return scanIoRefsWalk(item, ioNames, refs); }, node) : (isSxTruthy((typeOf(node) == "dict")) ? forEach(function(key) { return scanIoRefsWalk(dictGet(node, key), ioNames, refs); }, keys(node)) : NIL))); }; - - // scan-io-refs - var scanIoRefs = function(node, ioNames) { return (function() { - var refs = []; - scanIoRefsWalk(node, ioNames, refs); - return refs; -})(); }; - - // transitive-io-refs-walk - var transitiveIoRefsWalk = function(n, seen, allRefs, env, ioNames) { return (isSxTruthy(!isSxTruthy(contains(seen, n))) ? (append_b(seen, n), (function() { - var val = envGet(env, n); - return (isSxTruthy((typeOf(val) == "component")) ? (forEach(function(ref) { return (isSxTruthy(!isSxTruthy(contains(allRefs, ref))) ? append_b(allRefs, ref) : NIL); }, scanIoRefs(componentBody(val), ioNames)), forEach(function(dep) { return transitiveIoRefsWalk(dep, seen, allRefs, env, ioNames); }, scanRefs(componentBody(val)))) : (isSxTruthy((typeOf(val) == "macro")) ? (forEach(function(ref) { return (isSxTruthy(!isSxTruthy(contains(allRefs, ref))) ? append_b(allRefs, ref) : NIL); }, scanIoRefs(macroBody(val), ioNames)), forEach(function(dep) { return transitiveIoRefsWalk(dep, seen, allRefs, env, ioNames); }, scanRefs(macroBody(val)))) : NIL)); -})()) : NIL); }; - - // transitive-io-refs - var transitiveIoRefs = function(name, env, ioNames) { return (function() { - var allRefs = []; - var seen = []; - var key = (isSxTruthy(startsWith(name, "~")) ? name : (String("~") + String(name))); - transitiveIoRefsWalk(key, seen, allRefs, env, ioNames); - return allRefs; -})(); }; - - // compute-all-io-refs - var computeAllIoRefs = function(env, ioNames) { return forEach(function(name) { return (function() { - var val = envGet(env, name); - return (isSxTruthy((typeOf(val) == "component")) ? componentSetIoRefs(val, transitiveIoRefs(name, env, ioNames)) : NIL); -})(); }, envComponents(env)); }; - - // component-pure? - var componentPure_p = function(name, env, ioNames) { return isEmpty(transitiveIoRefs(name, env, ioNames)); }; - - // === Transpiled from router (client-side route matching) === // split-path-segments @@ -2781,7 +2593,7 @@ callExpr.push(dictGet(kwargs, k)); } } function domCreateElement(tag, ns) { if (!_hasDom) return null; - if (ns) return document.createElementNS(ns, tag); + if (ns && ns !== NIL) return document.createElementNS(ns, tag); return document.createElement(tag); } @@ -4714,17 +4526,6 @@ callExpr.push(dictGet(kwargs, k)); } } getEnv: function() { return componentEnv; }, resolveSuspense: typeof resolveSuspense === "function" ? resolveSuspense : null, init: typeof bootInit === "function" ? bootInit : null, - scanRefs: scanRefs, - scanComponentsFromSource: scanComponentsFromSource, - transitiveDeps: transitiveDeps, - computeAllDeps: computeAllDeps, - componentsNeeded: componentsNeeded, - pageComponentBundle: pageComponentBundle, - pageCssClasses: pageCssClasses, - scanIoRefs: scanIoRefs, - transitiveIoRefs: transitiveIoRefs, - computeAllIoRefs: computeAllIoRefs, - componentPure_p: componentPure_p, splitPathSegments: splitPathSegments, parseRoutePattern: parseRoutePattern, matchRoute: matchRoute, diff --git a/shared/sx/ref/bootstrap_js.py b/shared/sx/ref/bootstrap_js.py index 46785a9..d3c3aaa 100644 --- a/shared/sx/ref/bootstrap_js.py +++ b/shared/sx/ref/bootstrap_js.py @@ -2681,7 +2681,7 @@ PLATFORM_DOM_JS = """ function domCreateElement(tag, ns) { if (!_hasDom) return null; - if (ns) return document.createElementNS(ns, tag); + if (ns && ns !== NIL) return document.createElementNS(ns, tag); return document.createElement(tag); } diff --git a/shared/sx/templates/pages.sx b/shared/sx/templates/pages.sx index 4b27f9d..1eedf98 100644 --- a/shared/sx/templates/pages.sx +++ b/shared/sx/templates/pages.sx @@ -27,7 +27,7 @@ (div :id (str "sx-suspense-" id) :data-suspense id :style "display:contents" - (if children children fallback))) + (if (not (empty? children)) children fallback))) (defcomp ~error-page (&key title message image asset-url) (~base-shell :title title :asset-url asset-url