@sprlab/wccompiler 0.14.0 → 0.16.0

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 CHANGED
@@ -192,6 +192,9 @@ export function transformMethodBody(body, signalNames, computedNames, propsObjec
192
192
  result = result.replace(emitsRe, 'this._emit(');
193
193
  }
194
194
 
195
+ // 0b. Transform batch() calls: batch( → __batch(
196
+ result = result.replace(/\bbatch\s*\(/g, '__batch(');
197
+
195
198
  // 0b. Transform props.x → this._s_x() BEFORE other transforms
196
199
  if (propsObjectName && propNames.size > 0) {
197
200
  const propsRe = new RegExp(`\\b${propsObjectName}\\.(\\w+)`, 'g');
@@ -878,6 +881,7 @@ export function generateComponent(parseResult, options = {}) {
878
881
  exposeNames = [],
879
882
  modelDefs = [],
880
883
  dynamicComponents = [],
884
+ usesBatch = false,
881
885
  } = parseResult;
882
886
 
883
887
  const signalNames = signals.map(s => s.name);
@@ -907,18 +911,20 @@ export function generateComponent(parseResult, options = {}) {
907
911
  const needsEffect = effects.length > 0 || bindings.length > 0 || showBindings.length > 0 || modelBindings.length > 0 || modelPropBindings.length > 0 || attrBindings.length > 0 || ifBlocks.length > 0 || forBlocks.length > 0 || watchers.length > 0 || childComponents.length > 0 || dynamicComponents.length > 0 || slots.some(s => s.slotProps.length > 0);
908
912
  const needsComputed = computeds.length > 0;
909
913
  const needsUntrack = watchers.length > 0;
914
+ const needsBatch = usesBatch;
910
915
 
911
916
  if (options.runtimeImportPath) {
912
917
  // Tree-shake: only import what this component actually uses
913
918
  const usedRuntime = new Set(['__signal']); // always need __signal
914
919
  if (needsComputed) usedRuntime.add('__computed');
915
920
  if (needsEffect) usedRuntime.add('__effect');
921
+ if (needsBatch) usedRuntime.add('__batch');
916
922
  if (needsUntrack) usedRuntime.add('__untrack');
917
923
  const imports = [...usedRuntime].join(', ');
918
924
  lines.push(`import { ${imports} } from '${options.runtimeImportPath}';`);
919
925
  } else {
920
926
  // Standalone: inline only the runtime functions this component needs
921
- lines.push(buildInlineRuntime({ needsComputed, needsEffect, needsBatch: false, needsUntrack }).trim());
927
+ lines.push(buildInlineRuntime({ needsComputed, needsEffect, needsBatch, needsUntrack }).trim());
922
928
  }
923
929
  lines.push('');
924
930
 
@@ -1314,8 +1320,12 @@ export function generateComponent(parseResult, options = {}) {
1314
1320
  const propName = b.name.slice(propsObjectName.length + 1);
1315
1321
  ref = `this._s_${propName}()`;
1316
1322
  } else {
1323
+ // For method calls, ensure we have parentheses before transforming
1324
+ // b.name might be 'getCount' (from {{getCount()}} where baseName stripped the ())
1325
+ // We need to add () back so transformExpr can match and transform it
1326
+ const exprWithParens = b.name.includes('(') ? b.name : `${b.name}()`;
1317
1327
  // Use transformExpr for complex expressions (e.g. items().length, ternary)
1318
- ref = transformExpr(b.name, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, constantNames, methodNames, modelVarMap);
1328
+ ref = transformExpr(exprWithParens, signalNames, computedNames, propsObjectName, propNames, emitsObjectName, constantNames, methodNames, modelVarMap);
1319
1329
  }
1320
1330
  lines.push(' this.__disposers.push(__effect(() => {');
1321
1331
  lines.push(` this.${b.varName}.textContent = ${ref} ?? '';`);
@@ -1238,3 +1238,15 @@ export function validateNameCollisions(signalNames, computedNames, propNames, me
1238
1238
  }
1239
1239
  }
1240
1240
  }
1241
+
1242
+ /**
1243
+ * Detect if the source code uses the batch() function.
1244
+ *
1245
+ * @param {string} source - The script source code
1246
+ * @returns {boolean} True if batch() is used
1247
+ */
1248
+ export function detectBatchUsage(source) {
1249
+ // Look for batch( pattern (function call)
1250
+ // This will match: batch(() => {...}), batch(function() {...}), etc.
1251
+ return /\bbatch\s*\(/.test(source);
1252
+ }
package/lib/parser.js CHANGED
@@ -50,6 +50,7 @@ import {
50
50
  validateEmitsConflicts,
51
51
  validateUndeclaredEmits,
52
52
  validateNameCollisions,
53
+ detectBatchUsage,
53
54
  } from './parser-extractors.js';
54
55
 
55
56
  // ── Type stripping ──────────────────────────────────────────────────
@@ -195,6 +196,7 @@ export async function parse(filePath) {
195
196
  const methods = extractFunctions(sourceForExtraction);
196
197
  const refs = extractRefs(sourceForExtraction);
197
198
  const constantVars = extractConstants(sourceForExtraction);
199
+ const usesBatch = detectBatchUsage(sourceForExtraction);
198
200
 
199
201
  // 9. Extract props (array form — after type strip, if generic didn't find any)
200
202
  const propsFromArray = propsFromGeneric.length > 0 ? [] : extractPropsArray(source);
@@ -269,5 +271,6 @@ export async function parse(filePath) {
269
271
  onDestroyHooks,
270
272
  onAdoptHooks,
271
273
  refs,
274
+ usesBatch,
272
275
  };
273
276
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sprlab/wccompiler",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Zero-runtime compiler that transforms .wcc single-file components into native web components with signals-based reactivity",
5
5
  "type": "module",
6
6
  "exports": {