@sprlab/wccompiler 0.5.1 → 0.5.3
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 +53 -46
- 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,35 @@ 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
|
-
// method type —
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
764
|
+
// method type — check if it's a props.x access pattern
|
|
765
|
+
let ref;
|
|
766
|
+
if (propsObjectName && b.name.startsWith(propsObjectName + '.')) {
|
|
767
|
+
const propName = b.name.slice(propsObjectName.length + 1);
|
|
768
|
+
ref = `this._s_${propName}()`;
|
|
769
|
+
} else {
|
|
770
|
+
ref = `this._${b.name}()`;
|
|
771
|
+
}
|
|
772
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
773
|
+
lines.push(` this.${b.varName}.textContent = ${ref} ?? '';`);
|
|
774
|
+
lines.push(' }));');
|
|
769
775
|
}
|
|
770
776
|
}
|
|
771
777
|
|
|
@@ -804,22 +810,22 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
804
810
|
} else {
|
|
805
811
|
ref = `this._${pb.expr}()`;
|
|
806
812
|
}
|
|
807
|
-
lines.push(' __effect(() => {');
|
|
813
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
808
814
|
lines.push(` this.${cc.varName}.setAttribute('${pb.attr}', ${ref} ?? '');`);
|
|
809
|
-
lines.push(' });');
|
|
815
|
+
lines.push(' }));');
|
|
810
816
|
}
|
|
811
817
|
}
|
|
812
818
|
|
|
813
819
|
// User effects
|
|
814
820
|
for (const eff of effects) {
|
|
815
821
|
const body = transformMethodBody(eff.body, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, refVarNames, constantNames);
|
|
816
|
-
lines.push(' __effect(() => {');
|
|
822
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
817
823
|
// Indent each line of the body
|
|
818
824
|
const bodyLines = body.split('\n');
|
|
819
825
|
for (const line of bodyLines) {
|
|
820
826
|
lines.push(` ${line}`);
|
|
821
827
|
}
|
|
822
|
-
lines.push(' });');
|
|
828
|
+
lines.push(' }));');
|
|
823
829
|
}
|
|
824
830
|
|
|
825
831
|
// Watcher effects
|
|
@@ -837,7 +843,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
837
843
|
} else {
|
|
838
844
|
watchRef = `this._${w.target}()`;
|
|
839
845
|
}
|
|
840
|
-
lines.push(' __effect(() => {');
|
|
846
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
841
847
|
lines.push(` const ${w.newParam} = ${watchRef};`);
|
|
842
848
|
lines.push(` if (this.__prev_${w.target} !== undefined) {`);
|
|
843
849
|
lines.push(` const ${w.oldParam} = this.__prev_${w.target};`);
|
|
@@ -847,12 +853,12 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
847
853
|
}
|
|
848
854
|
lines.push(' }');
|
|
849
855
|
lines.push(` this.__prev_${w.target} = ${w.newParam};`);
|
|
850
|
-
lines.push(' });');
|
|
856
|
+
lines.push(' }));');
|
|
851
857
|
} else {
|
|
852
858
|
// kind === 'getter' — transform the getter expression and use it directly
|
|
853
859
|
const getterExpr = transformMethodBody(w.target, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, refVarNames, constantNames);
|
|
854
860
|
const prevName = `__prev_watch${idx}`;
|
|
855
|
-
lines.push(' __effect(() => {');
|
|
861
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
856
862
|
lines.push(` const ${w.newParam} = ${getterExpr};`);
|
|
857
863
|
lines.push(` if (this.${prevName} !== undefined) {`);
|
|
858
864
|
lines.push(` const ${w.oldParam} = this.${prevName};`);
|
|
@@ -862,7 +868,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
862
868
|
}
|
|
863
869
|
lines.push(' }');
|
|
864
870
|
lines.push(` this.${prevName} = ${w.newParam};`);
|
|
865
|
-
lines.push(' });');
|
|
871
|
+
lines.push(' }));');
|
|
866
872
|
}
|
|
867
873
|
}
|
|
868
874
|
|
|
@@ -875,28 +881,28 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
875
881
|
// Show effects — one __effect per ShowBinding
|
|
876
882
|
for (const sb of showBindings) {
|
|
877
883
|
const expr = transformExpr(sb.expression, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, constantNames, methodNames);
|
|
878
|
-
lines.push(' __effect(() => {');
|
|
884
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
879
885
|
lines.push(` this.${sb.varName}.style.display = (${expr}) ? '' : 'none';`);
|
|
880
|
-
lines.push(' });');
|
|
886
|
+
lines.push(' }));');
|
|
881
887
|
}
|
|
882
888
|
|
|
883
889
|
// Model effects — signal → DOM (one __effect per ModelBinding)
|
|
884
890
|
for (const mb of modelBindings) {
|
|
885
891
|
if (mb.prop === 'checked' && mb.radioValue !== null) {
|
|
886
892
|
// Radio: compare signal value to radioValue
|
|
887
|
-
lines.push(' __effect(() => {');
|
|
893
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
888
894
|
lines.push(` this.${mb.varName}.checked = (this._${mb.signal}() === '${mb.radioValue}');`);
|
|
889
|
-
lines.push(' });');
|
|
895
|
+
lines.push(' }));');
|
|
890
896
|
} else if (mb.prop === 'checked') {
|
|
891
897
|
// Checkbox: coerce to boolean
|
|
892
|
-
lines.push(' __effect(() => {');
|
|
898
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
893
899
|
lines.push(` this.${mb.varName}.checked = !!this._${mb.signal}();`);
|
|
894
|
-
lines.push(' });');
|
|
900
|
+
lines.push(' }));');
|
|
895
901
|
} else {
|
|
896
902
|
// Value-based (text, number, textarea, select): nullish coalesce to ''
|
|
897
|
-
lines.push(' __effect(() => {');
|
|
903
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
898
904
|
lines.push(` this.${mb.varName}.value = this._${mb.signal}() ?? '';`);
|
|
899
|
-
lines.push(' });');
|
|
905
|
+
lines.push(' }));');
|
|
900
906
|
}
|
|
901
907
|
}
|
|
902
908
|
|
|
@@ -918,44 +924,44 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
918
924
|
for (const ab of attrBindings) {
|
|
919
925
|
const expr = transformExpr(ab.expression, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, constantNames, methodNames);
|
|
920
926
|
if (ab.kind === 'attr') {
|
|
921
|
-
lines.push(' __effect(() => {');
|
|
927
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
922
928
|
lines.push(` const __v = ${expr};`);
|
|
923
929
|
lines.push(` if (__v || __v === '') { this.${ab.varName}.setAttribute('${ab.attr}', __v); }`);
|
|
924
930
|
lines.push(` else { this.${ab.varName}.removeAttribute('${ab.attr}'); }`);
|
|
925
|
-
lines.push(' });');
|
|
931
|
+
lines.push(' }));');
|
|
926
932
|
} else if (ab.kind === 'bool') {
|
|
927
|
-
lines.push(' __effect(() => {');
|
|
933
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
928
934
|
lines.push(` this.${ab.varName}.${ab.attr} = !!(${expr});`);
|
|
929
|
-
lines.push(' });');
|
|
935
|
+
lines.push(' }));');
|
|
930
936
|
} else if (ab.kind === 'class') {
|
|
931
937
|
if (ab.expression.trimStart().startsWith('{')) {
|
|
932
938
|
// Object expression: iterate entries, classList.add/remove
|
|
933
|
-
lines.push(' __effect(() => {');
|
|
939
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
934
940
|
lines.push(` const __obj = ${expr};`);
|
|
935
941
|
lines.push(' for (const [__k, __val] of Object.entries(__obj)) {');
|
|
936
942
|
lines.push(` __val ? this.${ab.varName}.classList.add(__k) : this.${ab.varName}.classList.remove(__k);`);
|
|
937
943
|
lines.push(' }');
|
|
938
|
-
lines.push(' });');
|
|
944
|
+
lines.push(' }));');
|
|
939
945
|
} else {
|
|
940
946
|
// String expression: set className
|
|
941
|
-
lines.push(' __effect(() => {');
|
|
947
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
942
948
|
lines.push(` this.${ab.varName}.className = ${expr};`);
|
|
943
|
-
lines.push(' });');
|
|
949
|
+
lines.push(' }));');
|
|
944
950
|
}
|
|
945
951
|
} else if (ab.kind === 'style') {
|
|
946
952
|
if (ab.expression.trimStart().startsWith('{')) {
|
|
947
953
|
// Object expression: iterate entries, set style[key]
|
|
948
|
-
lines.push(' __effect(() => {');
|
|
954
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
949
955
|
lines.push(` const __obj = ${expr};`);
|
|
950
956
|
lines.push(' for (const [__k, __val] of Object.entries(__obj)) {');
|
|
951
957
|
lines.push(` this.${ab.varName}.style[__k] = __val;`);
|
|
952
958
|
lines.push(' }');
|
|
953
|
-
lines.push(' });');
|
|
959
|
+
lines.push(' }));');
|
|
954
960
|
} else {
|
|
955
961
|
// String expression: set cssText
|
|
956
|
-
lines.push(' __effect(() => {');
|
|
962
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
957
963
|
lines.push(` this.${ab.varName}.style.cssText = ${expr};`);
|
|
958
|
-
lines.push(' });');
|
|
964
|
+
lines.push(' }));');
|
|
959
965
|
}
|
|
960
966
|
}
|
|
961
967
|
}
|
|
@@ -963,7 +969,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
963
969
|
// ── if effects ──
|
|
964
970
|
for (const ifBlock of ifBlocks) {
|
|
965
971
|
const vn = ifBlock.varName;
|
|
966
|
-
lines.push(' __effect(() => {');
|
|
972
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
967
973
|
lines.push(' let __branch = null;');
|
|
968
974
|
for (let i = 0; i < ifBlock.branches.length; i++) {
|
|
969
975
|
const branch = ifBlock.branches[i];
|
|
@@ -1002,7 +1008,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
1002
1008
|
}
|
|
1003
1009
|
lines.push(' }');
|
|
1004
1010
|
lines.push(` this.${vn}_active = __branch;`);
|
|
1005
|
-
lines.push(' });');
|
|
1011
|
+
lines.push(' }));');
|
|
1006
1012
|
}
|
|
1007
1013
|
|
|
1008
1014
|
// ── each effects ──
|
|
@@ -1016,7 +1022,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
1016
1022
|
// Transform the source expression
|
|
1017
1023
|
const sourceExpr = transformForExpr(source, itemVar, indexVar, propNames, signalNamesSet, computedNamesSet);
|
|
1018
1024
|
|
|
1019
|
-
lines.push(' __effect(() => {');
|
|
1025
|
+
lines.push(' this.__disposers.push(__effect(() => {');
|
|
1020
1026
|
lines.push(` const __source = ${sourceExpr};`);
|
|
1021
1027
|
lines.push('');
|
|
1022
1028
|
lines.push(" const __iter = typeof __source === 'number'");
|
|
@@ -1058,7 +1064,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
1058
1064
|
lines.push('');
|
|
1059
1065
|
lines.push(` this.${vn}_nodes = __newNodes;`);
|
|
1060
1066
|
lines.push(` this.${vn}_keyMap = __newMap;`);
|
|
1061
|
-
lines.push(' });');
|
|
1067
|
+
lines.push(' }));');
|
|
1062
1068
|
} else {
|
|
1063
1069
|
// ── Non-keyed: destroy all and recreate (original behavior) ──
|
|
1064
1070
|
lines.push(` for (const n of this.${vn}_nodes) n.remove();`);
|
|
@@ -1073,7 +1079,7 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
1073
1079
|
lines.push(` this.${vn}_anchor.parentNode.insertBefore(node, this.${vn}_anchor);`);
|
|
1074
1080
|
lines.push(` this.${vn}_nodes.push(node);`);
|
|
1075
1081
|
lines.push(' });');
|
|
1076
|
-
lines.push(' });');
|
|
1082
|
+
lines.push(' }));');
|
|
1077
1083
|
}
|
|
1078
1084
|
}
|
|
1079
1085
|
|
|
@@ -1098,10 +1104,11 @@ export function generateComponent(parseResult, options = {}) {
|
|
|
1098
1104
|
lines.push(' }');
|
|
1099
1105
|
lines.push('');
|
|
1100
1106
|
|
|
1101
|
-
// disconnectedCallback (cleanup: abort listeners + user hooks)
|
|
1107
|
+
// disconnectedCallback (cleanup: abort listeners + dispose effects + user hooks)
|
|
1102
1108
|
lines.push(' disconnectedCallback() {');
|
|
1103
1109
|
lines.push(' this.__connected = false;');
|
|
1104
1110
|
lines.push(' this.__ac.abort();');
|
|
1111
|
+
lines.push(' this.__disposers.forEach(d => d());');
|
|
1105
1112
|
if (onDestroyHooks.length > 0) {
|
|
1106
1113
|
for (const hook of onDestroyHooks) {
|
|
1107
1114
|
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