@zenithbuild/core 0.4.6 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/discovery/componentDiscovery.ts +4 -0
- package/compiler/discovery/layouts.ts +11 -2
- package/compiler/index.ts +1 -0
- package/compiler/ir/types.ts +12 -0
- package/compiler/parse/parseTemplate.ts +10 -2
- package/compiler/parse/scriptAnalysis.ts +8 -0
- package/compiler/runtime/transformIR.ts +22 -2
- package/compiler/transform/componentResolver.ts +36 -13
- package/compiler/transform/componentScriptTransformer.ts +147 -0
- package/dist/cli.js +6 -0
- package/dist/zen-build.js +256 -11
- package/dist/zen-dev.js +256 -11
- package/dist/zen-preview.js +256 -11
- package/dist/zenith.js +256 -11
- package/package.json +1 -1
- package/runtime/bundle-generator.ts +144 -1
package/dist/zen-preview.js
CHANGED
|
@@ -8441,7 +8441,12 @@ function generateExpressionId() {
|
|
|
8441
8441
|
return `expr_${expressionIdCounter++}`;
|
|
8442
8442
|
}
|
|
8443
8443
|
function stripBlocks(html) {
|
|
8444
|
-
let stripped = html.replace(/<script[^>]
|
|
8444
|
+
let stripped = html.replace(/<script([^>]*)>([\s\S]*?)<\/script>/gi, (match, attrs, content) => {
|
|
8445
|
+
if (attrs.includes("src=")) {
|
|
8446
|
+
return match;
|
|
8447
|
+
}
|
|
8448
|
+
return "";
|
|
8449
|
+
});
|
|
8445
8450
|
stripped = stripped.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "");
|
|
8446
8451
|
return stripped;
|
|
8447
8452
|
}
|
|
@@ -8942,6 +8947,8 @@ function parseComponentFile(filePath) {
|
|
|
8942
8947
|
slots,
|
|
8943
8948
|
props,
|
|
8944
8949
|
styles,
|
|
8950
|
+
script: ir.script?.raw || null,
|
|
8951
|
+
scriptAttributes: ir.script?.attributes || null,
|
|
8945
8952
|
hasScript: ir.script !== null,
|
|
8946
8953
|
hasStyles: ir.styles.length > 0
|
|
8947
8954
|
};
|
|
@@ -9133,13 +9140,20 @@ function resolveComponentsInIR(ir, components) {
|
|
|
9133
9140
|
usedComponents.clear();
|
|
9134
9141
|
const resolvedNodes = resolveComponentsInNodes(ir.template.nodes, components);
|
|
9135
9142
|
const componentStyles = Array.from(usedComponents).map((name) => components.get(name)).filter((meta) => meta !== undefined && meta.styles.length > 0).flatMap((meta) => meta.styles.map((raw) => ({ raw })));
|
|
9143
|
+
const componentScripts = Array.from(usedComponents).map((name) => components.get(name)).filter((meta) => meta !== undefined && meta.script !== null).map((meta) => ({
|
|
9144
|
+
name: meta.name,
|
|
9145
|
+
script: meta.script,
|
|
9146
|
+
props: meta.props,
|
|
9147
|
+
scriptAttributes: meta.scriptAttributes || {}
|
|
9148
|
+
}));
|
|
9136
9149
|
return {
|
|
9137
9150
|
...ir,
|
|
9138
9151
|
template: {
|
|
9139
9152
|
...ir.template,
|
|
9140
9153
|
nodes: resolvedNodes
|
|
9141
9154
|
},
|
|
9142
|
-
styles: [...ir.styles, ...componentStyles]
|
|
9155
|
+
styles: [...ir.styles, ...componentStyles],
|
|
9156
|
+
componentScripts: [...ir.componentScripts || [], ...componentScripts]
|
|
9143
9157
|
};
|
|
9144
9158
|
}
|
|
9145
9159
|
function resolveComponentsInNodes(nodes, components, depth = 0) {
|
|
@@ -9199,20 +9213,24 @@ function resolveComponent(componentNode, components, depth = 0) {
|
|
|
9199
9213
|
}
|
|
9200
9214
|
const templateNodes = JSON.parse(JSON.stringify(componentMeta.nodes));
|
|
9201
9215
|
const resolvedTemplate = resolveSlots(templateNodes, resolvedSlots);
|
|
9202
|
-
const forwardedTemplate = forwardAttributesToRoot(resolvedTemplate, componentNode.attributes, componentNode.loopContext);
|
|
9216
|
+
const forwardedTemplate = forwardAttributesToRoot(resolvedTemplate, componentNode.attributes, componentNode.loopContext, componentMeta.hasScript ? componentName : undefined);
|
|
9203
9217
|
const fullyResolved = resolveComponentsInNodes(forwardedTemplate, components, depth + 1);
|
|
9204
9218
|
return fullyResolved;
|
|
9205
9219
|
}
|
|
9206
|
-
function forwardAttributesToRoot(nodes, attributes, loopContext) {
|
|
9207
|
-
if (attributes.length === 0) {
|
|
9208
|
-
return nodes;
|
|
9209
|
-
}
|
|
9220
|
+
function forwardAttributesToRoot(nodes, attributes, loopContext, componentName) {
|
|
9210
9221
|
const rootIndex = nodes.findIndex((n) => n.type === "element");
|
|
9211
9222
|
if (rootIndex === -1) {
|
|
9212
9223
|
return nodes;
|
|
9213
9224
|
}
|
|
9214
9225
|
const root = nodes[rootIndex];
|
|
9215
9226
|
const mergedAttributes = [...root.attributes];
|
|
9227
|
+
if (componentName) {
|
|
9228
|
+
mergedAttributes.push({
|
|
9229
|
+
name: "data-zen-component",
|
|
9230
|
+
value: componentName,
|
|
9231
|
+
location: { line: 0, column: 0 }
|
|
9232
|
+
});
|
|
9233
|
+
}
|
|
9216
9234
|
for (const attr of attributes) {
|
|
9217
9235
|
const existingIndex = mergedAttributes.findIndex((a) => a.name === attr.name);
|
|
9218
9236
|
const forwardedAttr = {
|
|
@@ -10877,10 +10895,73 @@ function transformStateDeclarations(script) {
|
|
|
10877
10895
|
transformed = transformed.replace(/export\s+let\s+props(?:\s*:\s*([^;]+))?\s*;?[ \t]*/g, "");
|
|
10878
10896
|
transformed = transformed.replace(/(?:type|interface)\s+Props\s*=?\s*\{[^}]*(?:\{[^}]*\}[^}]*)*\}[ \t]*;?/gs, "");
|
|
10879
10897
|
transformed = transformed.replace(/import\s+{[^}]+}\s+from\s+['"]zenith\/runtime['"]\s*;?[ \t]*/g, "");
|
|
10898
|
+
transformed = transformed.replace(/import\s+\w+\s+from\s+['"][^'"]*\.zen['"];?\s*/g, "");
|
|
10899
|
+
transformed = transformed.replace(/import\s+{[^}]*}\s+from\s+['"][^'"]+\.zen['"];?\s*/g, "");
|
|
10880
10900
|
transformed = transformed.replace(/import\s*{\s*([^}]+)\s*}\s*from\s*['"]zenith:content['"]\s*;?/g, (_, imports) => `const { ${imports.trim()} } = window.__zenith;`);
|
|
10881
10901
|
return transformed.trim();
|
|
10882
10902
|
}
|
|
10883
10903
|
|
|
10904
|
+
// compiler/transform/componentScriptTransformer.ts
|
|
10905
|
+
var NAMESPACE_BINDINGS = `const {
|
|
10906
|
+
signal, state, memo, effect, ref,
|
|
10907
|
+
batch, untrack, onMount, onUnmount
|
|
10908
|
+
} = __inst;`;
|
|
10909
|
+
var ZEN_PREFIX_MAPPINGS = {
|
|
10910
|
+
zenSignal: "signal",
|
|
10911
|
+
zenState: "state",
|
|
10912
|
+
zenMemo: "memo",
|
|
10913
|
+
zenEffect: "effect",
|
|
10914
|
+
zenRef: "ref",
|
|
10915
|
+
zenBatch: "batch",
|
|
10916
|
+
zenUntrack: "untrack",
|
|
10917
|
+
zenOnMount: "onMount",
|
|
10918
|
+
zenOnUnmount: "onUnmount"
|
|
10919
|
+
};
|
|
10920
|
+
function transformComponentScript(componentName, scriptContent, props) {
|
|
10921
|
+
let transformed = scriptContent;
|
|
10922
|
+
transformed = transformed.replace(/import\s+\w+\s+from\s+['"][^'"]*\.zen['"];?\s*/g, "");
|
|
10923
|
+
transformed = transformed.replace(/import\s+{[^}]*}\s+from\s+['"][^'"]+['"];?\s*/g, "");
|
|
10924
|
+
for (const [zenName, unprefixedName] of Object.entries(ZEN_PREFIX_MAPPINGS)) {
|
|
10925
|
+
const regex = new RegExp(`(?<!\\w)${zenName}\\s*\\(`, "g");
|
|
10926
|
+
transformed = transformed.replace(regex, `${unprefixedName}(`);
|
|
10927
|
+
}
|
|
10928
|
+
return transformed.trim();
|
|
10929
|
+
}
|
|
10930
|
+
function generateComponentFactory(componentName, transformedScript, propNames) {
|
|
10931
|
+
const propsDestructure = propNames.length > 0 ? `const { ${propNames.join(", ")} } = props || {};` : "";
|
|
10932
|
+
return `
|
|
10933
|
+
// Component Factory: ${componentName}
|
|
10934
|
+
// Instantiation is driven by hydrator, not by bundle load
|
|
10935
|
+
__zenith.defineComponent('${componentName}', function(props, rootElement) {
|
|
10936
|
+
const __inst = __zenith.createInstance('${componentName}', rootElement);
|
|
10937
|
+
|
|
10938
|
+
// Namespace bindings (instance-scoped primitives)
|
|
10939
|
+
${NAMESPACE_BINDINGS}
|
|
10940
|
+
|
|
10941
|
+
${propsDestructure}
|
|
10942
|
+
|
|
10943
|
+
// Component script (instance-scoped)
|
|
10944
|
+
${transformedScript}
|
|
10945
|
+
|
|
10946
|
+
// Execute mount lifecycle (rootElement is already in DOM)
|
|
10947
|
+
__inst.mount();
|
|
10948
|
+
|
|
10949
|
+
return __inst;
|
|
10950
|
+
});
|
|
10951
|
+
`;
|
|
10952
|
+
}
|
|
10953
|
+
function transformAllComponentScripts(componentScripts) {
|
|
10954
|
+
if (!componentScripts || componentScripts.length === 0) {
|
|
10955
|
+
return "";
|
|
10956
|
+
}
|
|
10957
|
+
const factories = componentScripts.filter((comp) => comp.script && comp.script.trim().length > 0).map((comp) => {
|
|
10958
|
+
const transformed = transformComponentScript(comp.name, comp.script, comp.props);
|
|
10959
|
+
return generateComponentFactory(comp.name, transformed, comp.props);
|
|
10960
|
+
});
|
|
10961
|
+
return factories.join(`
|
|
10962
|
+
`);
|
|
10963
|
+
}
|
|
10964
|
+
|
|
10884
10965
|
// compiler/runtime/transformIR.ts
|
|
10885
10966
|
function transformIR(ir) {
|
|
10886
10967
|
const expressionDependencies = analyzeAllExpressions(ir.template.expressions, ir.filePath, [], ir.script?.attributes["props"] ? ir.script.attributes["props"].split(",") : [], []);
|
|
@@ -10897,6 +10978,7 @@ function transformIR(ir) {
|
|
|
10897
10978
|
const propDeclarations = extractProps(scriptContent);
|
|
10898
10979
|
const stateInitCode = generateStateInitialization(stateDeclarations, [...propDeclarations, ...propKeys]);
|
|
10899
10980
|
const scriptCode = transformStateDeclarations(scriptContent);
|
|
10981
|
+
const componentScriptCode = transformAllComponentScripts(ir.componentScripts || []);
|
|
10900
10982
|
const bundle = generateRuntimeBundle({
|
|
10901
10983
|
expressions,
|
|
10902
10984
|
expressionRegistry,
|
|
@@ -10904,7 +10986,8 @@ function transformIR(ir) {
|
|
|
10904
10986
|
navigationRuntime,
|
|
10905
10987
|
stylesCode,
|
|
10906
10988
|
scriptCode,
|
|
10907
|
-
stateInitCode
|
|
10989
|
+
stateInitCode,
|
|
10990
|
+
componentScriptCode
|
|
10908
10991
|
});
|
|
10909
10992
|
return {
|
|
10910
10993
|
expressions,
|
|
@@ -10940,6 +11023,9 @@ ${functionRegistrations}
|
|
|
10940
11023
|
${parts.stateInitCode ? `// State initialization
|
|
10941
11024
|
${parts.stateInitCode}` : ""}
|
|
10942
11025
|
|
|
11026
|
+
${parts.componentScriptCode ? `// Component factories (instance-scoped)
|
|
11027
|
+
${parts.componentScriptCode}` : ""}
|
|
11028
|
+
|
|
10943
11029
|
// Export hydration functions
|
|
10944
11030
|
if (typeof window !== 'undefined') {
|
|
10945
11031
|
window.zenithHydrate = window.__zenith_hydrate || function(state, container) {
|
|
@@ -10998,10 +11084,21 @@ if (typeof window !== 'undefined') {
|
|
|
10998
11084
|
// Get the router outlet or body
|
|
10999
11085
|
const container = document.querySelector('#app') || document.body;
|
|
11000
11086
|
|
|
11001
|
-
// Hydrate with state
|
|
11087
|
+
// Hydrate with state (expressions, bindings)
|
|
11002
11088
|
if (window.__zenith_hydrate) {
|
|
11003
11089
|
window.__zenith_hydrate(state, {}, {}, {}, container);
|
|
11004
11090
|
}
|
|
11091
|
+
|
|
11092
|
+
// Hydrate components by discovering data-zen-component markers
|
|
11093
|
+
// This is the ONLY place component instantiation happens - driven by DOM markers
|
|
11094
|
+
if (window.__zenith && window.__zenith.hydrateComponents) {
|
|
11095
|
+
window.__zenith.hydrateComponents(container);
|
|
11096
|
+
}
|
|
11097
|
+
|
|
11098
|
+
// Trigger page-level mount lifecycle
|
|
11099
|
+
if (window.__zenith && window.__zenith.triggerMount) {
|
|
11100
|
+
window.__zenith.triggerMount();
|
|
11101
|
+
}
|
|
11005
11102
|
}
|
|
11006
11103
|
|
|
11007
11104
|
// Run on DOM ready
|
|
@@ -11333,7 +11430,12 @@ function discoverLayouts(layoutsDir) {
|
|
|
11333
11430
|
if (match[1])
|
|
11334
11431
|
styles.push(match[1].trim());
|
|
11335
11432
|
}
|
|
11336
|
-
let html = source.replace(/<script[^>]
|
|
11433
|
+
let html = source.replace(/<script([^>]*)>([\s\S]*?)<\/script>/gi, (match2, attrs, content) => {
|
|
11434
|
+
if (attrs.includes("src=")) {
|
|
11435
|
+
return match2;
|
|
11436
|
+
}
|
|
11437
|
+
return "";
|
|
11438
|
+
});
|
|
11337
11439
|
html = html.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").trim();
|
|
11338
11440
|
layouts.set(name, {
|
|
11339
11441
|
name,
|
|
@@ -11835,6 +11937,144 @@ function generateBundleJS() {
|
|
|
11835
11937
|
unmountCallbacks.length = 0;
|
|
11836
11938
|
}
|
|
11837
11939
|
|
|
11940
|
+
// ============================================
|
|
11941
|
+
// Component Instance System
|
|
11942
|
+
// ============================================
|
|
11943
|
+
// Each component instance gets isolated state, effects, and lifecycles
|
|
11944
|
+
// Instances are tied to DOM elements via hydration markers
|
|
11945
|
+
|
|
11946
|
+
const componentRegistry = {};
|
|
11947
|
+
|
|
11948
|
+
function createComponentInstance(componentName, rootElement) {
|
|
11949
|
+
const instanceMountCallbacks = [];
|
|
11950
|
+
const instanceUnmountCallbacks = [];
|
|
11951
|
+
const instanceEffects = [];
|
|
11952
|
+
let instanceMounted = false;
|
|
11953
|
+
|
|
11954
|
+
return {
|
|
11955
|
+
// DOM reference
|
|
11956
|
+
root: rootElement,
|
|
11957
|
+
|
|
11958
|
+
// Lifecycle hooks (instance-scoped)
|
|
11959
|
+
onMount: function(fn) {
|
|
11960
|
+
if (instanceMounted) {
|
|
11961
|
+
const cleanup = fn();
|
|
11962
|
+
if (typeof cleanup === 'function') {
|
|
11963
|
+
instanceUnmountCallbacks.push(cleanup);
|
|
11964
|
+
}
|
|
11965
|
+
} else {
|
|
11966
|
+
instanceMountCallbacks.push(fn);
|
|
11967
|
+
}
|
|
11968
|
+
},
|
|
11969
|
+
onUnmount: function(fn) {
|
|
11970
|
+
instanceUnmountCallbacks.push(fn);
|
|
11971
|
+
},
|
|
11972
|
+
|
|
11973
|
+
// Reactivity (uses global primitives but tracks for cleanup)
|
|
11974
|
+
signal: function(initial) {
|
|
11975
|
+
return zenSignal(initial);
|
|
11976
|
+
},
|
|
11977
|
+
state: function(initial) {
|
|
11978
|
+
return zenState(initial);
|
|
11979
|
+
},
|
|
11980
|
+
ref: function(initial) {
|
|
11981
|
+
return zenRef(initial);
|
|
11982
|
+
},
|
|
11983
|
+
effect: function(fn) {
|
|
11984
|
+
const cleanup = zenEffect(fn);
|
|
11985
|
+
instanceEffects.push(cleanup);
|
|
11986
|
+
return cleanup;
|
|
11987
|
+
},
|
|
11988
|
+
memo: function(fn) {
|
|
11989
|
+
return zenMemo(fn);
|
|
11990
|
+
},
|
|
11991
|
+
batch: function(fn) {
|
|
11992
|
+
zenBatch(fn);
|
|
11993
|
+
},
|
|
11994
|
+
untrack: function(fn) {
|
|
11995
|
+
return zenUntrack(fn);
|
|
11996
|
+
},
|
|
11997
|
+
|
|
11998
|
+
// Lifecycle execution
|
|
11999
|
+
mount: function() {
|
|
12000
|
+
instanceMounted = true;
|
|
12001
|
+
for (let i = 0; i < instanceMountCallbacks.length; i++) {
|
|
12002
|
+
try {
|
|
12003
|
+
const cleanup = instanceMountCallbacks[i]();
|
|
12004
|
+
if (typeof cleanup === 'function') {
|
|
12005
|
+
instanceUnmountCallbacks.push(cleanup);
|
|
12006
|
+
}
|
|
12007
|
+
} catch(e) {
|
|
12008
|
+
console.error('[Zenith] Component mount error:', componentName, e);
|
|
12009
|
+
}
|
|
12010
|
+
}
|
|
12011
|
+
instanceMountCallbacks.length = 0;
|
|
12012
|
+
},
|
|
12013
|
+
unmount: function() {
|
|
12014
|
+
instanceMounted = false;
|
|
12015
|
+
// Cleanup effects
|
|
12016
|
+
for (let i = 0; i < instanceEffects.length; i++) {
|
|
12017
|
+
try {
|
|
12018
|
+
if (typeof instanceEffects[i] === 'function') instanceEffects[i]();
|
|
12019
|
+
} catch(e) {
|
|
12020
|
+
console.error('[Zenith] Effect cleanup error:', e);
|
|
12021
|
+
}
|
|
12022
|
+
}
|
|
12023
|
+
instanceEffects.length = 0;
|
|
12024
|
+
// Run unmount callbacks
|
|
12025
|
+
for (let i = 0; i < instanceUnmountCallbacks.length; i++) {
|
|
12026
|
+
try { instanceUnmountCallbacks[i](); } catch(e) { console.error('[Zenith] Unmount error:', e); }
|
|
12027
|
+
}
|
|
12028
|
+
instanceUnmountCallbacks.length = 0;
|
|
12029
|
+
}
|
|
12030
|
+
};
|
|
12031
|
+
}
|
|
12032
|
+
|
|
12033
|
+
function defineComponent(name, factory) {
|
|
12034
|
+
componentRegistry[name] = factory;
|
|
12035
|
+
}
|
|
12036
|
+
|
|
12037
|
+
function instantiateComponent(name, props, rootElement) {
|
|
12038
|
+
const factory = componentRegistry[name];
|
|
12039
|
+
if (!factory) {
|
|
12040
|
+
console.warn('[Zenith] Component not found:', name);
|
|
12041
|
+
return null;
|
|
12042
|
+
}
|
|
12043
|
+
return factory(props, rootElement);
|
|
12044
|
+
}
|
|
12045
|
+
|
|
12046
|
+
/**
|
|
12047
|
+
* Hydrate components by discovering data-zen-component markers
|
|
12048
|
+
* This is the ONLY place component instantiation should happen
|
|
12049
|
+
*/
|
|
12050
|
+
function hydrateComponents(container) {
|
|
12051
|
+
const componentElements = container.querySelectorAll('[data-zen-component]');
|
|
12052
|
+
|
|
12053
|
+
for (let i = 0; i < componentElements.length; i++) {
|
|
12054
|
+
const el = componentElements[i];
|
|
12055
|
+
const componentName = el.getAttribute('data-zen-component');
|
|
12056
|
+
|
|
12057
|
+
// Skip if already hydrated
|
|
12058
|
+
if (el.__zenith_instance) continue;
|
|
12059
|
+
|
|
12060
|
+
// Parse props from data attribute if present
|
|
12061
|
+
const propsJson = el.getAttribute('data-zen-props') || '{}';
|
|
12062
|
+
let props = {};
|
|
12063
|
+
try {
|
|
12064
|
+
props = JSON.parse(propsJson);
|
|
12065
|
+
} catch(e) {
|
|
12066
|
+
console.warn('[Zenith] Invalid props JSON for', componentName);
|
|
12067
|
+
}
|
|
12068
|
+
|
|
12069
|
+
// Instantiate component and bind to DOM element
|
|
12070
|
+
const instance = instantiateComponent(componentName, props, el);
|
|
12071
|
+
|
|
12072
|
+
if (instance) {
|
|
12073
|
+
el.__zenith_instance = instance;
|
|
12074
|
+
}
|
|
12075
|
+
}
|
|
12076
|
+
}
|
|
12077
|
+
|
|
11838
12078
|
// ============================================
|
|
11839
12079
|
// Expression Registry & Hydration
|
|
11840
12080
|
// ============================================
|
|
@@ -12254,8 +12494,13 @@ function generateBundleJS() {
|
|
|
12254
12494
|
triggerUnmount: triggerUnmount,
|
|
12255
12495
|
// Hydration
|
|
12256
12496
|
hydrate: zenithHydrate,
|
|
12497
|
+
hydrateComponents: hydrateComponents, // Marker-driven component instantiation
|
|
12257
12498
|
registerExpression: registerExpression,
|
|
12258
|
-
getExpression: getExpression
|
|
12499
|
+
getExpression: getExpression,
|
|
12500
|
+
// Component instance system
|
|
12501
|
+
createInstance: createComponentInstance,
|
|
12502
|
+
defineComponent: defineComponent,
|
|
12503
|
+
instantiate: instantiateComponent
|
|
12259
12504
|
};
|
|
12260
12505
|
|
|
12261
12506
|
// Expose with zen* prefix for direct usage
|