@sprlab/wccompiler 0.5.1 → 0.5.2
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/lib/codegen.js +44 -44
- package/lib/reactive-runtime.js +3 -0
- package/package.json +1 -1
package/lib/codegen.js
CHANGED
|
@@ -541,8 +541,6 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
541
541
|
const usedRuntime = new Set(['__signal']); // always need __signal
|
|
542
542
|
if (computeds.length > 0) usedRuntime.add('__computed');
|
|
543
543
|
if (effects.length > 0 || bindings.length > 0 || showBindings.length > 0 || modelBindings.length > 0 || attrBindings.length > 0 || ifBlocks.length > 0 || forBlocks.length > 0 || watchers.length > 0 || childComponents.length > 0 || slots.some(s => s.slotProps.length > 0)) usedRuntime.add('__effect');
|
|
544
|
-
// __batch is available but only needed if user code calls it explicitly — always include for safety
|
|
545
|
-
usedRuntime.add('__batch');
|
|
546
544
|
const imports = [...usedRuntime].join(', ');
|
|
547
545
|
lines.push(`import { ${imports} } from '${options.runtimeImportPath}';`);
|
|
548
546
|
} else {
|
|
@@ -745,27 +743,28 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
745
743
|
lines.push(' if (this.__connected) return;');
|
|
746
744
|
lines.push(' this.__connected = true;');
|
|
747
745
|
lines.push(' this.__ac = new AbortController();');
|
|
746
|
+
lines.push(' this.__disposers = [];');
|
|
748
747
|
lines.push('');
|
|
749
748
|
|
|
750
749
|
// Binding effects — one __effect per binding
|
|
751
750
|
for (const b of bindings) {
|
|
752
751
|
if (b.type === 'prop') {
|
|
753
|
-
lines.push(' __effect(() => {');
|
|
752
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
754
753
|
lines.push(` this.${b.varName}.textContent = this._s_${b.name}() ?? '';`);
|
|
755
|
-
lines.push(' });');
|
|
754
|
+
lines.push(' }));');
|
|
756
755
|
} else if (b.type === 'signal') {
|
|
757
|
-
lines.push(' __effect(() => {');
|
|
756
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
758
757
|
lines.push(` this.${b.varName}.textContent = this._${b.name}() ?? '';`);
|
|
759
|
-
lines.push(' });');
|
|
758
|
+
lines.push(' }));');
|
|
760
759
|
} else if (b.type === 'computed') {
|
|
761
|
-
lines.push(' __effect(() => {');
|
|
760
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
762
761
|
lines.push(` this.${b.varName}.textContent = this._c_${b.name}() ?? '';`);
|
|
763
|
-
lines.push(' });');
|
|
762
|
+
lines.push(' }));');
|
|
764
763
|
} else {
|
|
765
764
|
// method type — call the method
|
|
766
|
-
lines.push(' __effect(() => {');
|
|
765
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
767
766
|
lines.push(` this.${b.varName}.textContent = this._${b.name}() ?? '';`);
|
|
768
|
-
lines.push(' });');
|
|
767
|
+
lines.push(' }));');
|
|
769
768
|
}
|
|
770
769
|
}
|
|
771
770
|
|
|
@@ -804,22 +803,22 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
804
803
|
} else {
|
|
805
804
|
ref = `this._${pb.expr}()`;
|
|
806
805
|
}
|
|
807
|
-
lines.push(' __effect(() => {');
|
|
806
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
808
807
|
lines.push(` this.${cc.varName}.setAttribute('${pb.attr}', ${ref} ?? '');`);
|
|
809
|
-
lines.push(' });');
|
|
808
|
+
lines.push(' }));');
|
|
810
809
|
}
|
|
811
810
|
}
|
|
812
811
|
|
|
813
812
|
// User effects
|
|
814
813
|
for (const eff of effects) {
|
|
815
814
|
const body = transformMethodBody(eff.body, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, refVarNames, constantNames);
|
|
816
|
-
lines.push(' __effect(() => {');
|
|
815
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
817
816
|
// Indent each line of the body
|
|
818
817
|
const bodyLines = body.split('\n');
|
|
819
818
|
for (const line of bodyLines) {
|
|
820
819
|
lines.push(` ${line}`);
|
|
821
820
|
}
|
|
822
|
-
lines.push(' });');
|
|
821
|
+
lines.push(' }));');
|
|
823
822
|
}
|
|
824
823
|
|
|
825
824
|
// Watcher effects
|
|
@@ -837,7 +836,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
837
836
|
} else {
|
|
838
837
|
watchRef = `this._${w.target}()`;
|
|
839
838
|
}
|
|
840
|
-
lines.push(' __effect(() => {');
|
|
839
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
841
840
|
lines.push(` const ${w.newParam} = ${watchRef};`);
|
|
842
841
|
lines.push(` if (this.__prev_${w.target} !== undefined) {`);
|
|
843
842
|
lines.push(` const ${w.oldParam} = this.__prev_${w.target};`);
|
|
@@ -847,12 +846,12 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
847
846
|
}
|
|
848
847
|
lines.push(' }');
|
|
849
848
|
lines.push(` this.__prev_${w.target} = ${w.newParam};`);
|
|
850
|
-
lines.push(' });');
|
|
849
|
+
lines.push(' }));');
|
|
851
850
|
} else {
|
|
852
851
|
// kind === 'getter' — transform the getter expression and use it directly
|
|
853
852
|
const getterExpr = transformMethodBody(w.target, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, refVarNames, constantNames);
|
|
854
853
|
const prevName = `__prev_watch${idx}`;
|
|
855
|
-
lines.push(' __effect(() => {');
|
|
854
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
856
855
|
lines.push(` const ${w.newParam} = ${getterExpr};`);
|
|
857
856
|
lines.push(` if (this.${prevName} !== undefined) {`);
|
|
858
857
|
lines.push(` const ${w.oldParam} = this.${prevName};`);
|
|
@@ -862,7 +861,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
862
861
|
}
|
|
863
862
|
lines.push(' }');
|
|
864
863
|
lines.push(` this.${prevName} = ${w.newParam};`);
|
|
865
|
-
lines.push(' });');
|
|
864
|
+
lines.push(' }));');
|
|
866
865
|
}
|
|
867
866
|
}
|
|
868
867
|
|
|
@@ -875,28 +874,28 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
875
874
|
// Show effects — one __effect per ShowBinding
|
|
876
875
|
for (const sb of showBindings) {
|
|
877
876
|
const expr = transformExpr(sb.expression, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, constantNames, methodNames);
|
|
878
|
-
lines.push(' __effect(() => {');
|
|
877
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
879
878
|
lines.push(` this.${sb.varName}.style.display = (${expr}) ? '' : 'none';`);
|
|
880
|
-
lines.push(' });');
|
|
879
|
+
lines.push(' }));');
|
|
881
880
|
}
|
|
882
881
|
|
|
883
882
|
// Model effects — signal → DOM (one __effect per ModelBinding)
|
|
884
883
|
for (const mb of modelBindings) {
|
|
885
884
|
if (mb.prop === 'checked' && mb.radioValue !== null) {
|
|
886
885
|
// Radio: compare signal value to radioValue
|
|
887
|
-
lines.push(' __effect(() => {');
|
|
886
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
888
887
|
lines.push(` this.${mb.varName}.checked = (this._${mb.signal}() === '${mb.radioValue}');`);
|
|
889
|
-
lines.push(' });');
|
|
888
|
+
lines.push(' }));');
|
|
890
889
|
} else if (mb.prop === 'checked') {
|
|
891
890
|
// Checkbox: coerce to boolean
|
|
892
|
-
lines.push(' __effect(() => {');
|
|
891
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
893
892
|
lines.push(` this.${mb.varName}.checked = !!this._${mb.signal}();`);
|
|
894
|
-
lines.push(' });');
|
|
893
|
+
lines.push(' }));');
|
|
895
894
|
} else {
|
|
896
895
|
// Value-based (text, number, textarea, select): nullish coalesce to ''
|
|
897
|
-
lines.push(' __effect(() => {');
|
|
896
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
898
897
|
lines.push(` this.${mb.varName}.value = this._${mb.signal}() ?? '';`);
|
|
899
|
-
lines.push(' });');
|
|
898
|
+
lines.push(' }));');
|
|
900
899
|
}
|
|
901
900
|
}
|
|
902
901
|
|
|
@@ -918,44 +917,44 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
918
917
|
for (const ab of attrBindings) {
|
|
919
918
|
const expr = transformExpr(ab.expression, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, constantNames, methodNames);
|
|
920
919
|
if (ab.kind === 'attr') {
|
|
921
|
-
lines.push(' __effect(() => {');
|
|
920
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
922
921
|
lines.push(` const __v = ${expr};`);
|
|
923
922
|
lines.push(` if (__v || __v === '') { this.${ab.varName}.setAttribute('${ab.attr}', __v); }`);
|
|
924
923
|
lines.push(` else { this.${ab.varName}.removeAttribute('${ab.attr}'); }`);
|
|
925
|
-
lines.push(' });');
|
|
924
|
+
lines.push(' }));');
|
|
926
925
|
} else if (ab.kind === 'bool') {
|
|
927
|
-
lines.push(' __effect(() => {');
|
|
926
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
928
927
|
lines.push(` this.${ab.varName}.${ab.attr} = !!(${expr});`);
|
|
929
|
-
lines.push(' });');
|
|
928
|
+
lines.push(' }));');
|
|
930
929
|
} else if (ab.kind === 'class') {
|
|
931
930
|
if (ab.expression.trimStart().startsWith('{')) {
|
|
932
931
|
// Object expression: iterate entries, classList.add/remove
|
|
933
|
-
lines.push(' __effect(() => {');
|
|
932
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
934
933
|
lines.push(` const __obj = ${expr};`);
|
|
935
934
|
lines.push(' for (const [__k, __val] of Object.entries(__obj)) {');
|
|
936
935
|
lines.push(` __val ? this.${ab.varName}.classList.add(__k) : this.${ab.varName}.classList.remove(__k);`);
|
|
937
936
|
lines.push(' }');
|
|
938
|
-
lines.push(' });');
|
|
937
|
+
lines.push(' }));');
|
|
939
938
|
} else {
|
|
940
939
|
// String expression: set className
|
|
941
|
-
lines.push(' __effect(() => {');
|
|
940
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
942
941
|
lines.push(` this.${ab.varName}.className = ${expr};`);
|
|
943
|
-
lines.push(' });');
|
|
942
|
+
lines.push(' }));');
|
|
944
943
|
}
|
|
945
944
|
} else if (ab.kind === 'style') {
|
|
946
945
|
if (ab.expression.trimStart().startsWith('{')) {
|
|
947
946
|
// Object expression: iterate entries, set style[key]
|
|
948
|
-
lines.push(' __effect(() => {');
|
|
947
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
949
948
|
lines.push(` const __obj = ${expr};`);
|
|
950
949
|
lines.push(' for (const [__k, __val] of Object.entries(__obj)) {');
|
|
951
950
|
lines.push(` this.${ab.varName}.style[__k] = __val;`);
|
|
952
951
|
lines.push(' }');
|
|
953
|
-
lines.push(' });');
|
|
952
|
+
lines.push(' }));');
|
|
954
953
|
} else {
|
|
955
954
|
// String expression: set cssText
|
|
956
|
-
lines.push(' __effect(() => {');
|
|
955
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
957
956
|
lines.push(` this.${ab.varName}.style.cssText = ${expr};`);
|
|
958
|
-
lines.push(' });');
|
|
957
|
+
lines.push(' }));');
|
|
959
958
|
}
|
|
960
959
|
}
|
|
961
960
|
}
|
|
@@ -963,7 +962,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
963
962
|
// ── if effects ──
|
|
964
963
|
for (const ifBlock of ifBlocks) {
|
|
965
964
|
const vn = ifBlock.varName;
|
|
966
|
-
lines.push(' __effect(() => {');
|
|
965
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
967
966
|
lines.push(' let __branch = null;');
|
|
968
967
|
for (let i = 0; i < ifBlock.branches.length; i++) {
|
|
969
968
|
const branch = ifBlock.branches[i];
|
|
@@ -1002,7 +1001,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
1002
1001
|
}
|
|
1003
1002
|
lines.push(' }');
|
|
1004
1003
|
lines.push(` this.${vn}_active = __branch;`);
|
|
1005
|
-
lines.push(' });');
|
|
1004
|
+
lines.push(' }));');
|
|
1006
1005
|
}
|
|
1007
1006
|
|
|
1008
1007
|
// ── each effects ──
|
|
@@ -1016,7 +1015,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
1016
1015
|
// Transform the source expression
|
|
1017
1016
|
const sourceExpr = transformForExpr(source, itemVar, indexVar, propNames, signalNamesSet, computedNamesSet);
|
|
1018
1017
|
|
|
1019
|
-
lines.push(' __effect(() => {');
|
|
1018
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
1020
1019
|
lines.push(` const __source = ${sourceExpr};`);
|
|
1021
1020
|
lines.push('');
|
|
1022
1021
|
lines.push(" const __iter = typeof __source === 'number'");
|
|
@@ -1058,7 +1057,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
1058
1057
|
lines.push('');
|
|
1059
1058
|
lines.push(` this.${vn}_nodes = __newNodes;`);
|
|
1060
1059
|
lines.push(` this.${vn}_keyMap = __newMap;`);
|
|
1061
|
-
lines.push(' });');
|
|
1060
|
+
lines.push(' }));');
|
|
1062
1061
|
} else {
|
|
1063
1062
|
// ── Non-keyed: destroy all and recreate (original behavior) ──
|
|
1064
1063
|
lines.push(` for (const n of this.${vn}_nodes) n.remove();`);
|
|
@@ -1073,7 +1072,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
1073
1072
|
lines.push(` this.${vn}_anchor.parentNode.insertBefore(node, this.${vn}_anchor);`);
|
|
1074
1073
|
lines.push(` this.${vn}_nodes.push(node);`);
|
|
1075
1074
|
lines.push(' });');
|
|
1076
|
-
lines.push(' });');
|
|
1075
|
+
lines.push(' }));');
|
|
1077
1076
|
}
|
|
1078
1077
|
}
|
|
1079
1078
|
|
|
@@ -1098,10 +1097,11 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
1098
1097
|
lines.push(' }');
|
|
1099
1098
|
lines.push('');
|
|
1100
1099
|
|
|
1101
|
-
// disconnectedCallback (cleanup: abort listeners + user hooks)
|
|
1100
|
+
// disconnectedCallback (cleanup: abort listeners + dispose effects + user hooks)
|
|
1102
1101
|
lines.push(' disconnectedCallback() {');
|
|
1103
1102
|
lines.push(' this.__connected = false;');
|
|
1104
1103
|
lines.push(' this.__ac.abort();');
|
|
1104
|
+
lines.push(' this.__disposers.forEach(d => d());');
|
|
1105
1105
|
if (onDestroyHooks.length > 0) {
|
|
1106
1106
|
for (const hook of onDestroyHooks) {
|
|
1107
1107
|
const body = transformMethodBody(hook.body, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, refVarNames, constantNames);
|
package/lib/reactive-runtime.js
CHANGED
|
@@ -64,7 +64,9 @@ function __computed(fn) {
|
|
|
64
64
|
|
|
65
65
|
function __effect(fn) {
|
|
66
66
|
let _cleanup = null;
|
|
67
|
+
let _active = true;
|
|
67
68
|
const run = () => {
|
|
69
|
+
if (!_active) return;
|
|
68
70
|
if (typeof _cleanup === 'function') _cleanup();
|
|
69
71
|
const prev = __currentEffect;
|
|
70
72
|
__currentEffect = run;
|
|
@@ -72,6 +74,7 @@ function __effect(fn) {
|
|
|
72
74
|
__currentEffect = prev;
|
|
73
75
|
};
|
|
74
76
|
run();
|
|
77
|
+
return () => { _active = false; if (typeof _cleanup === 'function') _cleanup(); };
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
function __batch(fn) {
|
package/package.json
CHANGED