@constela/compiler 0.9.1 → 0.10.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/README.md CHANGED
@@ -50,6 +50,8 @@ The compiler transforms JSON programs through three passes:
50
50
  - **setPath** - Compiles to efficient path-based state updates
51
51
  - **Key-based each** - Compiles key expressions for list diffing
52
52
  - **WebSocket connections** - Compiles connection definitions and send/close actions
53
+ - **concat expression** - Compiles string concatenation expressions
54
+ - **Object payloads** - Supports object-shaped event handler payloads with expression fields
53
55
 
54
56
  ## CompiledProgram Structure
55
57
 
package/dist/index.d.ts CHANGED
@@ -217,7 +217,26 @@ interface CompiledCloseStep {
217
217
  do: 'close';
218
218
  connection: string;
219
219
  }
220
- type CompiledNode = CompiledElementNode | CompiledTextNode | CompiledIfNode | CompiledEachNode | CompiledMarkdownNode | CompiledCodeNode | CompiledSlotNode;
220
+ /**
221
+ * Compiled local action
222
+ */
223
+ interface CompiledLocalAction {
224
+ name: string;
225
+ steps: CompiledActionStep[];
226
+ }
227
+ /**
228
+ * Compiled local state node - wraps component with local state
229
+ */
230
+ interface CompiledLocalStateNode {
231
+ kind: 'localState';
232
+ state: Record<string, {
233
+ type: string;
234
+ initial: unknown;
235
+ }>;
236
+ actions: Record<string, CompiledLocalAction>;
237
+ child: CompiledNode;
238
+ }
239
+ type CompiledNode = CompiledElementNode | CompiledTextNode | CompiledIfNode | CompiledEachNode | CompiledMarkdownNode | CompiledCodeNode | CompiledSlotNode | CompiledLocalStateNode;
221
240
  interface CompiledElementNode {
222
241
  kind: 'element';
223
242
  tag: string;
@@ -459,4 +478,4 @@ declare function transformLayoutPass(layout: LayoutProgram, _context: LayoutAnal
459
478
  */
460
479
  declare function composeLayoutWithPage(layout: CompiledProgram, page: CompiledProgram, layoutParams?: Record<string, Expression>, slots?: Record<string, ViewNode>): CompiledProgram;
461
480
 
462
- export { type AnalysisContext, type AnalyzePassFailure, type AnalyzePassResult, type AnalyzePassSuccess, type CompileFailure, type CompileResult, type CompileSuccess, type CompiledAction, type CompiledActionStep, type CompiledBinExpr, type CompiledCallStep, type CompiledClipboardStep, type CompiledCloseStep, type CompiledCodeNode, type CompiledConcatExpr, type CompiledCondExpr, type CompiledDataExpr, type CompiledDisposeStep, type CompiledDomStep, type CompiledEachNode, type CompiledElementNode, type CompiledEventHandler, type CompiledExpression, type CompiledFetchStep, type CompiledGetExpr, type CompiledIfNode, type CompiledIfStep, type CompiledImportExpr, type CompiledImportStep, type CompiledIndexExpr, type CompiledLayoutProgram, type CompiledLifecycleHooks, type CompiledLitExpr, type CompiledMarkdownNode, type CompiledNavigateStep, type CompiledNode, type CompiledNotExpr, type CompiledParamExpr, type CompiledProgram, type CompiledRefExpr, type CompiledRouteDefinition, type CompiledRouteExpr, type CompiledSendStep, type CompiledSetPathStep, type CompiledSetStep, type CompiledSlotNode, type CompiledStateExpr, type CompiledStorageStep, type CompiledStyleExpr, type CompiledSubscribeStep, type CompiledTextNode, type CompiledUpdateStep, type CompiledVarExpr, type LayoutAnalysisContext, type LayoutAnalysisFailure, type LayoutAnalysisResult, type LayoutAnalysisSuccess, type ValidatePassFailure, type ValidatePassResult, type ValidatePassSuccess, analyzeLayoutPass, analyzePass, compile, composeLayoutWithPage, transformLayoutPass, transformPass, validatePass };
481
+ export { type AnalysisContext, type AnalyzePassFailure, type AnalyzePassResult, type AnalyzePassSuccess, type CompileFailure, type CompileResult, type CompileSuccess, type CompiledAction, type CompiledActionStep, type CompiledBinExpr, type CompiledCallStep, type CompiledClipboardStep, type CompiledCloseStep, type CompiledCodeNode, type CompiledConcatExpr, type CompiledCondExpr, type CompiledDataExpr, type CompiledDisposeStep, type CompiledDomStep, type CompiledEachNode, type CompiledElementNode, type CompiledEventHandler, type CompiledExpression, type CompiledFetchStep, type CompiledGetExpr, type CompiledIfNode, type CompiledIfStep, type CompiledImportExpr, type CompiledImportStep, type CompiledIndexExpr, type CompiledLayoutProgram, type CompiledLifecycleHooks, type CompiledLitExpr, type CompiledLocalAction, type CompiledLocalStateNode, type CompiledMarkdownNode, type CompiledNavigateStep, type CompiledNode, type CompiledNotExpr, type CompiledParamExpr, type CompiledProgram, type CompiledRefExpr, type CompiledRouteDefinition, type CompiledRouteExpr, type CompiledSendStep, type CompiledSetPathStep, type CompiledSetStep, type CompiledSlotNode, type CompiledStateExpr, type CompiledStorageStep, type CompiledStyleExpr, type CompiledSubscribeStep, type CompiledTextNode, type CompiledUpdateStep, type CompiledVarExpr, type LayoutAnalysisContext, type LayoutAnalysisFailure, type LayoutAnalysisResult, type LayoutAnalysisSuccess, type ValidatePassFailure, type ValidatePassResult, type ValidatePassSuccess, analyzeLayoutPass, analyzePass, compile, composeLayoutWithPage, transformLayoutPass, transformPass, validatePass };
package/dist/index.js CHANGED
@@ -44,6 +44,8 @@ import {
44
44
  createUndefinedRefError,
45
45
  createUndefinedStyleError,
46
46
  createUndefinedVariantError,
47
+ createUndefinedLocalStateError,
48
+ createLocalActionInvalidStepError,
47
49
  findSimilarNames,
48
50
  isEventHandler,
49
51
  DATA_SOURCE_TYPES,
@@ -157,12 +159,19 @@ function checkDuplicateActions(ast2) {
157
159
  function validateExpression(expr, path, context, scope, paramScope) {
158
160
  const errors = [];
159
161
  switch (expr.expr) {
160
- case "state":
161
- if (!context.stateNames.has(expr.name)) {
162
- const errorOptions = createErrorOptionsWithSuggestion(expr.name, context.stateNames);
162
+ case "state": {
163
+ const isGlobalState = context.stateNames.has(expr.name);
164
+ const isLocalState = paramScope?.localStateNames?.has(expr.name) ?? false;
165
+ if (!isGlobalState && !isLocalState) {
166
+ const availableNames = /* @__PURE__ */ new Set([
167
+ ...context.stateNames,
168
+ ...paramScope?.localStateNames ?? []
169
+ ]);
170
+ const errorOptions = createErrorOptionsWithSuggestion(expr.name, availableNames);
163
171
  errors.push(createUndefinedStateError(expr.name, path, errorOptions));
164
172
  }
165
173
  break;
174
+ }
166
175
  case "var":
167
176
  if (!scope.has(expr.name)) {
168
177
  errors.push(createUndefinedVarError(expr.name, path));
@@ -706,8 +715,14 @@ function validateViewNode(node, path, context, scope, options = { insideComponen
706
715
  for (const [propName, propValue] of Object.entries(node.props)) {
707
716
  const propPath = buildPath(path, "props", propName);
708
717
  if (isEventHandler(propValue)) {
709
- if (!context.actionNames.has(propValue.action)) {
710
- const errorOptions = createErrorOptionsWithSuggestion(propValue.action, context.actionNames);
718
+ const isGlobalAction = context.actionNames.has(propValue.action);
719
+ const isLocalAction = paramScope?.localActionNames?.has(propValue.action) ?? false;
720
+ if (!isGlobalAction && !isLocalAction) {
721
+ const availableNames = /* @__PURE__ */ new Set([
722
+ ...context.actionNames,
723
+ ...paramScope?.localActionNames ?? []
724
+ ]);
725
+ const errorOptions = createErrorOptionsWithSuggestion(propValue.action, availableNames);
711
726
  errors.push(createUndefinedActionError(propValue.action, propPath, errorOptions));
712
727
  }
713
728
  if (propValue.payload) {
@@ -910,21 +925,58 @@ function detectComponentCycles(programAst, context) {
910
925
  }
911
926
  return errors;
912
927
  }
928
+ function validateLocalActions(localActions, localStateNames, componentPath) {
929
+ const errors = [];
930
+ for (let i = 0; i < localActions.length; i++) {
931
+ const action = localActions[i];
932
+ if (!action) continue;
933
+ const actionPath = buildPath(componentPath, "localActions", i);
934
+ for (let j = 0; j < action.steps.length; j++) {
935
+ const step = action.steps[j];
936
+ if (!step) continue;
937
+ const stepPath = buildPath(actionPath, "steps", j);
938
+ const stepDo = step.do;
939
+ if (stepDo !== "set" && stepDo !== "update" && stepDo !== "setPath") {
940
+ errors.push(createLocalActionInvalidStepError(stepDo, stepPath));
941
+ continue;
942
+ }
943
+ if (!localStateNames.has(step.target)) {
944
+ const errorOptions = createErrorOptionsWithSuggestion(step.target, localStateNames);
945
+ errors.push(createUndefinedLocalStateError(step.target, buildPath(stepPath, "target"), errorOptions));
946
+ }
947
+ }
948
+ }
949
+ return errors;
950
+ }
913
951
  function validateComponents(programAst, context) {
914
952
  const errors = [];
915
953
  if (!programAst.components) return errors;
916
954
  for (const [name, def] of Object.entries(programAst.components)) {
955
+ const componentPath = buildPath("", "components", name);
956
+ const localStateNames = new Set(
957
+ def.localState ? Object.keys(def.localState) : []
958
+ );
959
+ const localActionNames = new Set(
960
+ def.localActions ? def.localActions.map((a) => a.name) : []
961
+ );
917
962
  const paramNames = new Set(
918
963
  def.params ? Object.keys(def.params) : []
919
964
  );
920
965
  const paramScope = {
921
966
  params: paramNames,
922
- componentName: name
967
+ componentName: name,
968
+ localStateNames,
969
+ localActionNames
923
970
  };
971
+ if (def.localActions && def.localActions.length > 0) {
972
+ errors.push(
973
+ ...validateLocalActions(def.localActions, localStateNames, componentPath)
974
+ );
975
+ }
924
976
  errors.push(
925
977
  ...validateViewNode(
926
978
  def.view,
927
- buildPath("", "components", name, "view"),
979
+ buildPath(componentPath, "view"),
928
980
  context,
929
981
  /* @__PURE__ */ new Set(),
930
982
  { insideComponent: true, paramScope }
@@ -1480,7 +1532,16 @@ function transformViewNode(node, ctx) {
1480
1532
  currentParams: params,
1481
1533
  currentChildren: children
1482
1534
  };
1483
- return transformViewNode(def.view, newCtx);
1535
+ const expandedView = transformViewNode(def.view, newCtx);
1536
+ if (def.localState && Object.keys(def.localState).length > 0) {
1537
+ return {
1538
+ kind: "localState",
1539
+ state: transformLocalState(def.localState),
1540
+ actions: transformLocalActions(def.localActions ?? []),
1541
+ child: expandedView
1542
+ };
1543
+ }
1544
+ return expandedView;
1484
1545
  }
1485
1546
  case "markdown":
1486
1547
  return {
@@ -1519,6 +1580,23 @@ function transformState(state) {
1519
1580
  }
1520
1581
  return compiledState;
1521
1582
  }
1583
+ function transformLocalState(localState) {
1584
+ const result = {};
1585
+ for (const [name, field] of Object.entries(localState)) {
1586
+ result[name] = { type: field.type, initial: field.initial };
1587
+ }
1588
+ return result;
1589
+ }
1590
+ function transformLocalActions(localActions) {
1591
+ const result = {};
1592
+ for (const action of localActions) {
1593
+ result[action.name] = {
1594
+ name: action.name,
1595
+ steps: action.steps.map(transformActionStep)
1596
+ };
1597
+ }
1598
+ return result;
1599
+ }
1522
1600
  function transformActions(actions) {
1523
1601
  const compiledActions = {};
1524
1602
  for (const action of actions) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/compiler",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Compiler for Constela UI framework - AST to Program transformation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "dist"
16
16
  ],
17
17
  "dependencies": {
18
- "@constela/core": "0.9.1"
18
+ "@constela/core": "0.10.0"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/node": "^20.10.0",