@aeriajs/compiler 0.0.26 → 0.0.28

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/dist/ast.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Property, AccessCondition, CollectionActions, SearchOptions, DescriptionPreset, Icon, OwnershipMode, Layout, LayoutOptions } from '@aeriajs/types';
1
+ import type { Property, AccessCondition, CollectionActions, SearchOptions, DescriptionPreset, Icon, OwnershipMode, Layout, LayoutOptions, FormLayout, Description, FormLayoutField } from '@aeriajs/types';
2
2
  import type { ArrayProperties } from './utils.js';
3
3
  export declare const LOCATION_SYMBOL: unique symbol;
4
4
  export declare const PropertyType: {
@@ -26,6 +26,16 @@ export type LayoutNode = NodeBase<'layout'> & Layout & {
26
26
  };
27
27
  };
28
28
  };
29
+ export type FormLayoutNode = NodeBase<'formLayout'> & FormLayout<Description> & {
30
+ [LOCATION_SYMBOL]: {
31
+ fields: {
32
+ [P in string]: {
33
+ name: symbol;
34
+ field: FormLayoutField<Description>;
35
+ };
36
+ };
37
+ };
38
+ };
29
39
  export type PropertyNode = NodeBase<'property'> & {
30
40
  modifier?: keyof typeof PropertyModifiers;
31
41
  property: Property & {
@@ -61,6 +71,7 @@ export type CollectionNode = NodeBase<'collection'> & {
61
71
  filters?: string[];
62
72
  search?: SearchOptions<any>;
63
73
  layout?: LayoutNode;
74
+ formLayout?: FormLayoutNode;
64
75
  [LOCATION_SYMBOL]: {
65
76
  arrays: {
66
77
  [P in ArrayProperties<CollectionNode>]?: symbol[];
@@ -87,4 +98,3 @@ export type ProgramNode = NodeBase<'program'> & {
87
98
  functionsets: FunctionSetNode[];
88
99
  };
89
100
  export type Node = CollectionNode | ContractNode | FunctionSetNode;
90
- export type NoteKind = Node['kind'];
@@ -58,6 +58,9 @@ const makeJSCollectionSchema = (collectionNode, collectionId) => {
58
58
  };
59
59
  collectionSchema.exposedFunctions = (0, utils_js_1.getExposedFunctions)(collectionNode[key]);
60
60
  break;
61
+ case 'required':
62
+ collectionSchema.description[key] = collectionNode[key];
63
+ break;
61
64
  case 'table':
62
65
  case 'filters':
63
66
  case 'indexes':
@@ -78,10 +81,10 @@ const makeJSCollectionSchema = (collectionNode, collectionId) => {
78
81
  collectionSchema.description[key] = collectionNode[key];
79
82
  break;
80
83
  case 'layout':
81
- collectionSchema.description[key] = collectionNode[key];
84
+ collectionSchema.description[key] = (0, utils_js_1.unwrapNode)(collectionNode[key]);
82
85
  break;
83
- case 'required':
84
- collectionSchema.description[key] = collectionNode[key];
86
+ case 'formLayout':
87
+ collectionSchema.description[key] = (0, utils_js_1.unwrapNode)(collectionNode[key]);
85
88
  break;
86
89
  }
87
90
  }
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- import { makeASTImports, recursivelyUnwrapPropertyNodes, stringify, getExtendName, getCollectionId, UnquotedSymbol, getExposedFunctions, PACKAGE_NAME, DEFAULT_FUNCTIONS } from "./utils.mjs";
2
+ import { unwrapNode, recursivelyUnwrapPropertyNodes, stringify, makeASTImports, getCollectionId, UnquotedSymbol, getExposedFunctions, getExtendName, PACKAGE_NAME, DEFAULT_FUNCTIONS } from "./utils.mjs";
3
3
  const initialImportedFunctions = [
4
4
  "extendCollection",
5
5
  "defineCollection"
@@ -51,6 +51,9 @@ const makeJSCollectionSchema = (collectionNode, collectionId) => {
51
51
  };
52
52
  collectionSchema.exposedFunctions = getExposedFunctions(collectionNode[key]);
53
53
  break;
54
+ case "required":
55
+ collectionSchema.description[key] = collectionNode[key];
56
+ break;
54
57
  case "table":
55
58
  case "filters":
56
59
  case "indexes":
@@ -71,10 +74,10 @@ const makeJSCollectionSchema = (collectionNode, collectionId) => {
71
74
  collectionSchema.description[key] = collectionNode[key];
72
75
  break;
73
76
  case "layout":
74
- collectionSchema.description[key] = collectionNode[key];
77
+ collectionSchema.description[key] = unwrapNode(collectionNode[key]);
75
78
  break;
76
- case "required":
77
- collectionSchema.description[key] = collectionNode[key];
79
+ case "formLayout":
80
+ collectionSchema.description[key] = unwrapNode(collectionNode[key]);
78
81
  break;
79
82
  }
80
83
  }
@@ -68,6 +68,9 @@ const makeTSCollectionSchema = (collectionNode, collectionId) => {
68
68
  collectionSchema.functions = makeTSFunctions(collectionNode[key]);
69
69
  collectionSchema.exposedFunctions = (0, utils_js_1.getExposedFunctions)(collectionNode[key]);
70
70
  break;
71
+ case 'required':
72
+ collectionSchema.description[key] = collectionNode[key];
73
+ break;
71
74
  case 'table':
72
75
  case 'filters':
73
76
  case 'indexes':
@@ -88,10 +91,10 @@ const makeTSCollectionSchema = (collectionNode, collectionId) => {
88
91
  collectionSchema.description[key] = collectionNode[key];
89
92
  break;
90
93
  case 'layout':
91
- collectionSchema.description[key] = collectionNode[key];
94
+ collectionSchema.description[key] = (0, utils_js_1.unwrapNode)(collectionNode[key]);
92
95
  break;
93
- case 'required':
94
- collectionSchema.description[key] = collectionNode[key];
96
+ case 'formLayout':
97
+ collectionSchema.description[key] = (0, utils_js_1.unwrapNode)(collectionNode[key]);
95
98
  break;
96
99
  }
97
100
  }
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- import { recursivelyUnwrapPropertyNodes, stringify, makeASTImports, resizeFirstChar, getCollectionId, UnquotedSymbol, getExposedFunctions, PACKAGE_NAME, DEFAULT_FUNCTIONS } from "./utils.mjs";
2
+ import { unwrapNode, recursivelyUnwrapPropertyNodes, stringify, makeASTImports, resizeFirstChar, getCollectionId, UnquotedSymbol, getExposedFunctions, PACKAGE_NAME, DEFAULT_FUNCTIONS } from "./utils.mjs";
3
3
  const initialImportedTypes = [
4
4
  "Collection",
5
5
  "SchemaWithId",
@@ -63,6 +63,9 @@ const makeTSCollectionSchema = (collectionNode, collectionId) => {
63
63
  collectionSchema.functions = makeTSFunctions(collectionNode[key]);
64
64
  collectionSchema.exposedFunctions = getExposedFunctions(collectionNode[key]);
65
65
  break;
66
+ case "required":
67
+ collectionSchema.description[key] = collectionNode[key];
68
+ break;
66
69
  case "table":
67
70
  case "filters":
68
71
  case "indexes":
@@ -83,10 +86,10 @@ const makeTSCollectionSchema = (collectionNode, collectionId) => {
83
86
  collectionSchema.description[key] = collectionNode[key];
84
87
  break;
85
88
  case "layout":
86
- collectionSchema.description[key] = collectionNode[key];
89
+ collectionSchema.description[key] = unwrapNode(collectionNode[key]);
87
90
  break;
88
- case "required":
89
- collectionSchema.description[key] = collectionNode[key];
91
+ case "formLayout":
92
+ collectionSchema.description[key] = unwrapNode(collectionNode[key]);
90
93
  break;
91
94
  }
92
95
  }
@@ -13,6 +13,9 @@ export declare const makeASTImports: (ast: AST.Node[], initialImports?: Record<s
13
13
  code: string[];
14
14
  modifiedSymbols: Record<string, string>;
15
15
  };
16
+ export declare const unwrapNode: <TNode extends {
17
+ kind: string;
18
+ }>(node: TNode) => Omit<TNode, "kind" | symbol>;
16
19
  export declare const unwrapPropertyNode: ({ property, nestedProperties, nestedAdditionalProperties }: Pick<AST.PropertyNode, "property" | "nestedProperties" | "nestedAdditionalProperties">) => Property;
17
20
  /** Transforms the AST properties to the format of aeria schema properties */
18
21
  export declare const recursivelyUnwrapPropertyNodes: <TProperties extends Record<string, AST.PropertyNode | AST.PropertyNode[]>, TReturnType = TProperties[keyof TProperties] extends Array<unknown> ? Record<string, Property[]> : Record<string, Property>>(properties: TProperties) => TReturnType;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getExtendName = exports.getCollectionId = exports.resizeFirstChar = exports.stringify = exports.UnquotedSymbol = exports.recursivelyUnwrapPropertyNodes = exports.unwrapPropertyNode = exports.makeASTImports = exports.getExposedFunctions = exports.ArraySymbol = exports.DEFAULT_FUNCTIONS = exports.PACKAGE_NAME = void 0;
3
+ exports.getExtendName = exports.getCollectionId = exports.resizeFirstChar = exports.stringify = exports.UnquotedSymbol = exports.recursivelyUnwrapPropertyNodes = exports.unwrapPropertyNode = exports.unwrapNode = exports.makeASTImports = exports.getExposedFunctions = exports.ArraySymbol = exports.DEFAULT_FUNCTIONS = exports.PACKAGE_NAME = void 0;
4
4
  exports.PACKAGE_NAME = 'aeria';
5
5
  exports.DEFAULT_FUNCTIONS = [
6
6
  'count',
@@ -56,6 +56,11 @@ const makeASTImports = (ast, initialImports) => {
56
56
  };
57
57
  };
58
58
  exports.makeASTImports = makeASTImports;
59
+ const unwrapNode = (node) => {
60
+ const { kind, ...unwrappedNode } = Object.fromEntries(Object.entries(node).filter(([key]) => typeof key === 'string'));
61
+ return unwrappedNode;
62
+ };
63
+ exports.unwrapNode = unwrapNode;
59
64
  const unwrapPropertyNode = ({ property, nestedProperties, nestedAdditionalProperties }) => {
60
65
  const propertyOrPropertyItems = 'items' in property
61
66
  ? property.items
@@ -49,6 +49,10 @@ export const makeASTImports = (ast, initialImports) => {
49
49
  modifiedSymbols
50
50
  };
51
51
  };
52
+ export const unwrapNode = (node) => {
53
+ const { kind, ...unwrappedNode } = Object.fromEntries(Object.entries(node).filter(([key]) => typeof key === "string"));
54
+ return unwrappedNode;
55
+ };
52
56
  export const unwrapPropertyNode = ({ property, nestedProperties, nestedAdditionalProperties }) => {
53
57
  const propertyOrPropertyItems = "items" in property ? property.items : property;
54
58
  let unwrappedProperty = propertyOrPropertyItems;
@@ -17,6 +17,9 @@ class Diagnostic extends Error {
17
17
  this.message = message;
18
18
  this.location = location;
19
19
  this.fileLocation = fileLocation;
20
+ if (process.env.NODE_ENV === 'debug') {
21
+ console.error(message, location);
22
+ }
20
23
  }
21
24
  }
22
25
  exports.Diagnostic = Diagnostic;
@@ -11,6 +11,9 @@ export class Diagnostic extends Error {
11
11
  this.message = message;
12
12
  this.location = location;
13
13
  this.fileLocation = fileLocation;
14
+ if (false) {
15
+ console.error(message, location);
16
+ }
14
17
  }
15
18
  static currentFile;
16
19
  }
package/dist/lexer.d.ts CHANGED
@@ -1,15 +1,18 @@
1
1
  import { type Token } from './token.js';
2
2
  import { Diagnostic } from './diagnostic.js';
3
- export declare const COLLECTION_KEYWORDS: readonly ["actions", "additionalProperties", "filters", "form", "functions", "icon", "indexes", "individualActions", "layout", "owned", "presets", "properties", "required", "search", "table"];
3
+ export declare const COLLECTION_KEYWORDS: readonly ["actions", "additionalProperties", "filters", "form", "formLayout", "functions", "icon", "indexes", "individualActions", "layout", "owned", "presets", "properties", "required", "search", "table"];
4
4
  export declare const COLLECTION_ACTIONS_KEYWORDS: readonly ["ask", "button", "clearItem", "effect", "event", "fetchItem", "function", "icon", "label", "params", "query", "requires", "roles", "route", "selection", "setItem", "translate"];
5
5
  export declare const COLLECTION_SEARCH_KEYWORDS: readonly ["indexes", "placeholder", "exactMatches"];
6
6
  export declare const COLLECTION_LAYOUT_KEYWORDS: readonly ["name", "options"];
7
7
  export declare const COLLECTION_LAYOUT_OPTIONS_KEYWORDS: readonly ["title", "picture", "badge", "information", "active", "translateBadge"];
8
+ export declare const COLLECTION_FORM_LAYOUT_KEYWORDS: readonly ["fields", "if", "span", "verticalSpacing", "separator"];
8
9
  export declare const CONTRACT_KEYWORDS: readonly ["roles", "payload", "query", "response"];
9
10
  export declare const TOPLEVEL_KEYWORDS: readonly ["collection", "contract", "functionset"];
10
11
  export declare const MISC_KEYWORDS: readonly ["extends"];
11
- export type Keyword = typeof COLLECTION_KEYWORDS[number] | typeof COLLECTION_ACTIONS_KEYWORDS[number] | typeof COLLECTION_SEARCH_KEYWORDS[number] | typeof COLLECTION_LAYOUT_KEYWORDS[number] | typeof COLLECTION_LAYOUT_OPTIONS_KEYWORDS[number] | typeof CONTRACT_KEYWORDS[number] | typeof TOPLEVEL_KEYWORDS[number] | typeof MISC_KEYWORDS[number];
12
+ export type Keyword = typeof COLLECTION_KEYWORDS[number] | typeof COLLECTION_ACTIONS_KEYWORDS[number] | typeof COLLECTION_SEARCH_KEYWORDS[number] | typeof COLLECTION_LAYOUT_KEYWORDS[number] | typeof COLLECTION_LAYOUT_OPTIONS_KEYWORDS[number] | typeof COLLECTION_FORM_LAYOUT_KEYWORDS[number] | typeof CONTRACT_KEYWORDS[number] | typeof TOPLEVEL_KEYWORDS[number] | typeof MISC_KEYWORDS[number];
12
13
  export declare const KEYWORDS: Keyword[];
14
+ export declare const FINAL_OPERATORS: readonly ["==", "in", ">=", "<=", ">", "<", "!"];
15
+ export declare const LOGICAL_OPERATORS: readonly ["&&", "||"];
13
16
  export declare const tokenize: (rawInput: string) => {
14
17
  tokens: Token[];
15
18
  errors: Diagnostic[];
package/dist/lexer.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.tokenize = exports.KEYWORDS = exports.MISC_KEYWORDS = exports.TOPLEVEL_KEYWORDS = exports.CONTRACT_KEYWORDS = exports.COLLECTION_LAYOUT_OPTIONS_KEYWORDS = exports.COLLECTION_LAYOUT_KEYWORDS = exports.COLLECTION_SEARCH_KEYWORDS = exports.COLLECTION_ACTIONS_KEYWORDS = exports.COLLECTION_KEYWORDS = void 0;
3
+ exports.tokenize = exports.LOGICAL_OPERATORS = exports.FINAL_OPERATORS = exports.KEYWORDS = exports.MISC_KEYWORDS = exports.TOPLEVEL_KEYWORDS = exports.CONTRACT_KEYWORDS = exports.COLLECTION_FORM_LAYOUT_KEYWORDS = exports.COLLECTION_LAYOUT_OPTIONS_KEYWORDS = exports.COLLECTION_LAYOUT_KEYWORDS = exports.COLLECTION_SEARCH_KEYWORDS = exports.COLLECTION_ACTIONS_KEYWORDS = exports.COLLECTION_KEYWORDS = void 0;
4
4
  const token_js_1 = require("./token.js");
5
5
  const diagnostic_js_1 = require("./diagnostic.js");
6
6
  exports.COLLECTION_KEYWORDS = [
@@ -8,6 +8,7 @@ exports.COLLECTION_KEYWORDS = [
8
8
  'additionalProperties',
9
9
  'filters',
10
10
  'form',
11
+ 'formLayout',
11
12
  'functions',
12
13
  'icon',
13
14
  'indexes',
@@ -56,6 +57,13 @@ exports.COLLECTION_LAYOUT_OPTIONS_KEYWORDS = [
56
57
  'active',
57
58
  'translateBadge',
58
59
  ];
60
+ exports.COLLECTION_FORM_LAYOUT_KEYWORDS = [
61
+ 'fields',
62
+ 'if',
63
+ 'span',
64
+ 'verticalSpacing',
65
+ 'separator',
66
+ ];
59
67
  exports.CONTRACT_KEYWORDS = [
60
68
  'roles',
61
69
  'payload',
@@ -68,7 +76,20 @@ exports.TOPLEVEL_KEYWORDS = [
68
76
  'functionset',
69
77
  ];
70
78
  exports.MISC_KEYWORDS = ['extends'];
71
- exports.KEYWORDS = [].concat(exports.COLLECTION_KEYWORDS, exports.COLLECTION_ACTIONS_KEYWORDS, exports.COLLECTION_SEARCH_KEYWORDS, exports.COLLECTION_LAYOUT_KEYWORDS, exports.COLLECTION_LAYOUT_OPTIONS_KEYWORDS, exports.CONTRACT_KEYWORDS, exports.TOPLEVEL_KEYWORDS, exports.MISC_KEYWORDS);
79
+ exports.KEYWORDS = [].concat(exports.COLLECTION_KEYWORDS, exports.COLLECTION_ACTIONS_KEYWORDS, exports.COLLECTION_SEARCH_KEYWORDS, exports.COLLECTION_LAYOUT_KEYWORDS, exports.COLLECTION_LAYOUT_OPTIONS_KEYWORDS, exports.COLLECTION_FORM_LAYOUT_KEYWORDS, exports.CONTRACT_KEYWORDS, exports.TOPLEVEL_KEYWORDS, exports.MISC_KEYWORDS);
80
+ exports.FINAL_OPERATORS = [
81
+ '==',
82
+ 'in',
83
+ '>=',
84
+ '<=',
85
+ '>',
86
+ '<',
87
+ '!',
88
+ ];
89
+ exports.LOGICAL_OPERATORS = [
90
+ '&&',
91
+ '||',
92
+ ];
72
93
  const keywordsSet = new Set();
73
94
  for (const keyword of exports.KEYWORDS) {
74
95
  keywordsSet.add(keyword);
@@ -110,6 +131,10 @@ const TOKENS = [
110
131
  type: token_js_1.TokenType.RightSquareBracket,
111
132
  matcher: ']',
112
133
  },
134
+ {
135
+ type: token_js_1.TokenType.Operator,
136
+ matcher: [].concat(exports.FINAL_OPERATORS, exports.LOGICAL_OPERATORS),
137
+ },
113
138
  {
114
139
  type: token_js_1.TokenType.Pipe,
115
140
  matcher: '|',
@@ -263,6 +288,7 @@ const tokenize = function (rawInput) {
263
288
  let variableScope = false;
264
289
  if (lastToken && lastToken.type === token_js_1.TokenType.Keyword) {
265
290
  switch (lastToken.value) {
291
+ case 'fields':
266
292
  case 'information':
267
293
  case 'form':
268
294
  case 'table':
package/dist/lexer.mjs CHANGED
@@ -6,6 +6,7 @@ export const COLLECTION_KEYWORDS = [
6
6
  "additionalProperties",
7
7
  "filters",
8
8
  "form",
9
+ "formLayout",
9
10
  "functions",
10
11
  "icon",
11
12
  "indexes",
@@ -54,6 +55,13 @@ export const COLLECTION_LAYOUT_OPTIONS_KEYWORDS = [
54
55
  "active",
55
56
  "translateBadge"
56
57
  ];
58
+ export const COLLECTION_FORM_LAYOUT_KEYWORDS = [
59
+ "fields",
60
+ "if",
61
+ "span",
62
+ "verticalSpacing",
63
+ "separator"
64
+ ];
57
65
  export const CONTRACT_KEYWORDS = [
58
66
  "roles",
59
67
  "payload",
@@ -72,10 +80,24 @@ export const KEYWORDS = [].concat(
72
80
  COLLECTION_SEARCH_KEYWORDS,
73
81
  COLLECTION_LAYOUT_KEYWORDS,
74
82
  COLLECTION_LAYOUT_OPTIONS_KEYWORDS,
83
+ COLLECTION_FORM_LAYOUT_KEYWORDS,
75
84
  CONTRACT_KEYWORDS,
76
85
  TOPLEVEL_KEYWORDS,
77
86
  MISC_KEYWORDS
78
87
  );
88
+ export const FINAL_OPERATORS = [
89
+ "==",
90
+ "in",
91
+ ">=",
92
+ "<=",
93
+ ">",
94
+ "<",
95
+ "!"
96
+ ];
97
+ export const LOGICAL_OPERATORS = [
98
+ "&&",
99
+ "||"
100
+ ];
79
101
  const keywordsSet = /* @__PURE__ */ new Set();
80
102
  for (const keyword of KEYWORDS) {
81
103
  keywordsSet.add(keyword);
@@ -117,6 +139,10 @@ const TOKENS = [
117
139
  type: TokenType.RightSquareBracket,
118
140
  matcher: "]"
119
141
  },
142
+ {
143
+ type: TokenType.Operator,
144
+ matcher: [].concat(FINAL_OPERATORS, LOGICAL_OPERATORS)
145
+ },
120
146
  {
121
147
  type: TokenType.Pipe,
122
148
  matcher: "|"
@@ -265,6 +291,7 @@ export const tokenize = function(rawInput) {
265
291
  let variableScope = false;
266
292
  if (lastToken && lastToken.type === TokenType.Keyword) {
267
293
  switch (lastToken.value) {
294
+ case "fields":
268
295
  case "information":
269
296
  case "form":
270
297
  case "table":
package/dist/parser.js CHANGED
@@ -600,6 +600,7 @@ const parse = (tokens) => {
600
600
  }
601
601
  continue;
602
602
  }
603
+ throw err;
603
604
  }
604
605
  }
605
606
  consume(token_js_1.TokenType.RightBracket);
@@ -731,6 +732,10 @@ const parse = (tokens) => {
731
732
  node[keyword] = parseLayoutBlock();
732
733
  break;
733
734
  }
735
+ case 'formLayout': {
736
+ node[keyword] = parseFormLayoutBlock();
737
+ break;
738
+ }
734
739
  }
735
740
  }
736
741
  catch (err) {
@@ -739,6 +744,7 @@ const parse = (tokens) => {
739
744
  recover(lexer.COLLECTION_KEYWORDS);
740
745
  continue;
741
746
  }
747
+ throw err;
742
748
  }
743
749
  }
744
750
  consume(token_js_1.TokenType.RightBracket);
@@ -792,6 +798,7 @@ const parse = (tokens) => {
792
798
  if (match(token_js_1.TokenType.MacroName)) {
793
799
  const { value: macroName } = consume(token_js_1.TokenType.MacroName, ['include']);
794
800
  switch (macroName) {
801
+ /* eslint-disable-next-line */
795
802
  case 'include': {
796
803
  const { value: functionSetName, location } = consume(token_js_1.TokenType.Identifier);
797
804
  const functionset = ast.functionsets.find((node) => node.name === functionSetName);
@@ -835,6 +842,7 @@ const parse = (tokens) => {
835
842
  errors.push(err);
836
843
  continue;
837
844
  }
845
+ throw err;
838
846
  }
839
847
  }
840
848
  consume(token_js_1.TokenType.RightBracket);
@@ -1051,6 +1059,166 @@ const parse = (tokens) => {
1051
1059
  },
1052
1060
  };
1053
1061
  };
1062
+ const parseFormLayoutBlock = () => {
1063
+ const fields = {};
1064
+ const node = {
1065
+ kind: 'formLayout',
1066
+ fields,
1067
+ [AST.LOCATION_SYMBOL]: {
1068
+ fields: {},
1069
+ },
1070
+ };
1071
+ consume(token_js_1.TokenType.LeftBracket);
1072
+ while (!match(token_js_1.TokenType.RightBracket)) {
1073
+ const { value: keyword, location: keywordLocation } = consume(token_js_1.TokenType.Keyword, lexer.COLLECTION_FORM_LAYOUT_KEYWORDS);
1074
+ switch (keyword) {
1075
+ case 'fields': {
1076
+ consume(token_js_1.TokenType.LeftBracket);
1077
+ while (!match(token_js_1.TokenType.RightBracket)) {
1078
+ const { value: identifier, location: identifierLocation } = consume(token_js_1.TokenType.Identifier);
1079
+ const identifierSymbol = Symbol();
1080
+ exports.locationMap.set(identifierSymbol, identifierLocation);
1081
+ fields[identifier] ??= {};
1082
+ node[AST.LOCATION_SYMBOL].fields[identifier] = {
1083
+ name: identifierSymbol,
1084
+ field: {},
1085
+ };
1086
+ consume(token_js_1.TokenType.LeftBracket);
1087
+ while (!match(token_js_1.TokenType.RightBracket)) {
1088
+ const { value: keyword, location: keywordLocation } = consume(token_js_1.TokenType.Keyword, lexer.COLLECTION_FORM_LAYOUT_KEYWORDS);
1089
+ switch (keyword) {
1090
+ case 'if': {
1091
+ fields[identifier].if = parseCondition();
1092
+ break;
1093
+ }
1094
+ case 'span':
1095
+ case 'verticalSpacing': {
1096
+ fields[identifier].span = consume(token_js_1.TokenType.Number).value;
1097
+ break;
1098
+ }
1099
+ case 'separator': {
1100
+ fields[identifier].separator = match(token_js_1.TokenType.Boolean)
1101
+ ? consume(token_js_1.TokenType.Boolean).value
1102
+ : consume(token_js_1.TokenType.QuotedString, [
1103
+ 'top',
1104
+ 'bottom',
1105
+ ]).value;
1106
+ break;
1107
+ }
1108
+ default: {
1109
+ throw new diagnostic_js_1.Diagnostic(`invalid keyword "${keyword}"`, keywordLocation);
1110
+ }
1111
+ }
1112
+ }
1113
+ consume(token_js_1.TokenType.RightBracket);
1114
+ }
1115
+ consume(token_js_1.TokenType.RightBracket);
1116
+ break;
1117
+ }
1118
+ default: {
1119
+ throw new diagnostic_js_1.Diagnostic(`invalid keyword "${keyword}"`, keywordLocation);
1120
+ }
1121
+ }
1122
+ }
1123
+ consume(token_js_1.TokenType.RightBracket);
1124
+ return node;
1125
+ };
1126
+ const parseCondition = () => {
1127
+ if (match(token_js_1.TokenType.LeftParens)) {
1128
+ consume(token_js_1.TokenType.LeftParens);
1129
+ let operatorType, newOp = operatorType;
1130
+ const conditions = [];
1131
+ while (!match(token_js_1.TokenType.RightParens)) {
1132
+ conditions.push(parseCondition());
1133
+ if (match(token_js_1.TokenType.RightParens)) {
1134
+ break;
1135
+ }
1136
+ const { value: operatorSymbol, location } = consume(token_js_1.TokenType.Operator);
1137
+ switch (operatorSymbol) {
1138
+ case '&&':
1139
+ newOp = 'and';
1140
+ break;
1141
+ case '||':
1142
+ newOp = 'or';
1143
+ break;
1144
+ default: {
1145
+ throw new diagnostic_js_1.Diagnostic(`unsupported operator: "${operatorSymbol}"`, location);
1146
+ }
1147
+ }
1148
+ if (operatorType && operatorType !== newOp) {
1149
+ throw new diagnostic_js_1.Diagnostic('having "and" or "or" in the same expression is not supported, please use parenthesis', location);
1150
+ }
1151
+ operatorType = newOp;
1152
+ }
1153
+ consume(token_js_1.TokenType.RightParens);
1154
+ switch (operatorType) {
1155
+ case 'and': {
1156
+ return {
1157
+ and: conditions,
1158
+ };
1159
+ }
1160
+ case 'or': {
1161
+ return {
1162
+ or: conditions,
1163
+ };
1164
+ }
1165
+ default: {
1166
+ return conditions[0];
1167
+ }
1168
+ }
1169
+ }
1170
+ if (match(token_js_1.TokenType.Operator, '!')) {
1171
+ consume(token_js_1.TokenType.Operator);
1172
+ return {
1173
+ not: parseCondition(),
1174
+ };
1175
+ }
1176
+ const { value: term1 } = consume(token_js_1.TokenType.Identifier);
1177
+ if (!match(token_js_1.TokenType.Operator, lexer.FINAL_OPERATORS)) {
1178
+ return {
1179
+ operator: 'truthy',
1180
+ term1: term1,
1181
+ };
1182
+ }
1183
+ const { value: operatorSymbol, location } = consume(token_js_1.TokenType.Operator);
1184
+ let term2;
1185
+ if (match(token_js_1.TokenType.LeftParens)) {
1186
+ term2 = parseCondition();
1187
+ }
1188
+ else {
1189
+ term2 = current().value;
1190
+ advance();
1191
+ }
1192
+ let operator;
1193
+ switch (operatorSymbol) {
1194
+ case '==':
1195
+ operator = 'equal';
1196
+ break;
1197
+ case 'in':
1198
+ operator = 'in';
1199
+ break;
1200
+ case '>=':
1201
+ operator = 'gte';
1202
+ break;
1203
+ case '<=':
1204
+ operator = 'lte';
1205
+ break;
1206
+ case '>':
1207
+ operator = 'gt';
1208
+ break;
1209
+ case '<':
1210
+ operator = 'lt';
1211
+ break;
1212
+ default: {
1213
+ throw new diagnostic_js_1.Diagnostic(`unsuported operator: "${operatorSymbol}"`, location);
1214
+ }
1215
+ }
1216
+ return {
1217
+ operator,
1218
+ term1,
1219
+ term2,
1220
+ };
1221
+ };
1054
1222
  while (index < tokens.length) {
1055
1223
  const { value: declType, location } = current();
1056
1224
  try {
package/dist/parser.mjs CHANGED
@@ -551,6 +551,7 @@ export const parse = (tokens) => {
551
551
  }
552
552
  continue;
553
553
  }
554
+ throw err;
554
555
  }
555
556
  }
556
557
  consume(TokenType.RightBracket);
@@ -676,6 +677,10 @@ export const parse = (tokens) => {
676
677
  node[keyword] = parseLayoutBlock();
677
678
  break;
678
679
  }
680
+ case "formLayout": {
681
+ node[keyword] = parseFormLayoutBlock();
682
+ break;
683
+ }
679
684
  }
680
685
  } catch (err) {
681
686
  if (err instanceof Diagnostic) {
@@ -683,6 +688,7 @@ export const parse = (tokens) => {
683
688
  recover(lexer.COLLECTION_KEYWORDS);
684
689
  continue;
685
690
  }
691
+ throw err;
686
692
  }
687
693
  }
688
694
  consume(TokenType.RightBracket);
@@ -777,6 +783,7 @@ export const parse = (tokens) => {
777
783
  errors.push(err);
778
784
  continue;
779
785
  }
786
+ throw err;
780
787
  }
781
788
  }
782
789
  consume(TokenType.RightBracket);
@@ -991,6 +998,163 @@ export const parse = (tokens) => {
991
998
  }
992
999
  };
993
1000
  };
1001
+ const parseFormLayoutBlock = () => {
1002
+ const fields = {};
1003
+ const node = {
1004
+ kind: "formLayout",
1005
+ fields,
1006
+ [AST.LOCATION_SYMBOL]: {
1007
+ fields: {}
1008
+ }
1009
+ };
1010
+ consume(TokenType.LeftBracket);
1011
+ while (!match(TokenType.RightBracket)) {
1012
+ const { value: keyword, location: keywordLocation } = consume(TokenType.Keyword, lexer.COLLECTION_FORM_LAYOUT_KEYWORDS);
1013
+ switch (keyword) {
1014
+ case "fields": {
1015
+ consume(TokenType.LeftBracket);
1016
+ while (!match(TokenType.RightBracket)) {
1017
+ const { value: identifier, location: identifierLocation } = consume(TokenType.Identifier);
1018
+ const identifierSymbol = Symbol();
1019
+ locationMap.set(identifierSymbol, identifierLocation);
1020
+ fields[identifier] ??= {};
1021
+ node[AST.LOCATION_SYMBOL].fields[identifier] = {
1022
+ name: identifierSymbol,
1023
+ field: {}
1024
+ };
1025
+ consume(TokenType.LeftBracket);
1026
+ while (!match(TokenType.RightBracket)) {
1027
+ const { value: keyword2, location: keywordLocation2 } = consume(TokenType.Keyword, lexer.COLLECTION_FORM_LAYOUT_KEYWORDS);
1028
+ switch (keyword2) {
1029
+ case "if": {
1030
+ fields[identifier].if = parseCondition();
1031
+ break;
1032
+ }
1033
+ case "span":
1034
+ case "verticalSpacing": {
1035
+ fields[identifier].span = consume(TokenType.Number).value;
1036
+ break;
1037
+ }
1038
+ case "separator": {
1039
+ fields[identifier].separator = match(TokenType.Boolean) ? consume(TokenType.Boolean).value : consume(TokenType.QuotedString, [
1040
+ "top",
1041
+ "bottom"
1042
+ ]).value;
1043
+ break;
1044
+ }
1045
+ default: {
1046
+ throw new Diagnostic(`invalid keyword "${keyword2}"`, keywordLocation2);
1047
+ }
1048
+ }
1049
+ }
1050
+ consume(TokenType.RightBracket);
1051
+ }
1052
+ consume(TokenType.RightBracket);
1053
+ break;
1054
+ }
1055
+ default: {
1056
+ throw new Diagnostic(`invalid keyword "${keyword}"`, keywordLocation);
1057
+ }
1058
+ }
1059
+ }
1060
+ consume(TokenType.RightBracket);
1061
+ return node;
1062
+ };
1063
+ const parseCondition = () => {
1064
+ if (match(TokenType.LeftParens)) {
1065
+ consume(TokenType.LeftParens);
1066
+ let operatorType, newOp = operatorType;
1067
+ const conditions = [];
1068
+ while (!match(TokenType.RightParens)) {
1069
+ conditions.push(parseCondition());
1070
+ if (match(TokenType.RightParens)) {
1071
+ break;
1072
+ }
1073
+ const { value: operatorSymbol2, location: location2 } = consume(TokenType.Operator);
1074
+ switch (operatorSymbol2) {
1075
+ case "&&":
1076
+ newOp = "and";
1077
+ break;
1078
+ case "||":
1079
+ newOp = "or";
1080
+ break;
1081
+ default: {
1082
+ throw new Diagnostic(`unsupported operator: "${operatorSymbol2}"`, location2);
1083
+ }
1084
+ }
1085
+ if (operatorType && operatorType !== newOp) {
1086
+ throw new Diagnostic('having "and" or "or" in the same expression is not supported, please use parenthesis', location2);
1087
+ }
1088
+ operatorType = newOp;
1089
+ }
1090
+ consume(TokenType.RightParens);
1091
+ switch (operatorType) {
1092
+ case "and": {
1093
+ return {
1094
+ and: conditions
1095
+ };
1096
+ }
1097
+ case "or": {
1098
+ return {
1099
+ or: conditions
1100
+ };
1101
+ }
1102
+ default: {
1103
+ return conditions[0];
1104
+ }
1105
+ }
1106
+ }
1107
+ if (match(TokenType.Operator, "!")) {
1108
+ consume(TokenType.Operator);
1109
+ return {
1110
+ not: parseCondition()
1111
+ };
1112
+ }
1113
+ const { value: term1 } = consume(TokenType.Identifier);
1114
+ if (!match(TokenType.Operator, lexer.FINAL_OPERATORS)) {
1115
+ return {
1116
+ operator: "truthy",
1117
+ term1
1118
+ };
1119
+ }
1120
+ const { value: operatorSymbol, location } = consume(TokenType.Operator);
1121
+ let term2;
1122
+ if (match(TokenType.LeftParens)) {
1123
+ term2 = parseCondition();
1124
+ } else {
1125
+ term2 = current().value;
1126
+ advance();
1127
+ }
1128
+ let operator;
1129
+ switch (operatorSymbol) {
1130
+ case "==":
1131
+ operator = "equal";
1132
+ break;
1133
+ case "in":
1134
+ operator = "in";
1135
+ break;
1136
+ case ">=":
1137
+ operator = "gte";
1138
+ break;
1139
+ case "<=":
1140
+ operator = "lte";
1141
+ break;
1142
+ case ">":
1143
+ operator = "gt";
1144
+ break;
1145
+ case "<":
1146
+ operator = "lt";
1147
+ break;
1148
+ default: {
1149
+ throw new Diagnostic(`unsuported operator: "${operatorSymbol}"`, location);
1150
+ }
1151
+ }
1152
+ return {
1153
+ operator,
1154
+ term1,
1155
+ term2
1156
+ };
1157
+ };
994
1158
  while (index < tokens.length) {
995
1159
  const { value: declType, location } = current();
996
1160
  try {
package/dist/semantic.js CHANGED
@@ -160,6 +160,16 @@ const analyze = async (ast, options, errors = []) => {
160
160
  }
161
161
  }
162
162
  }
163
+ if (node.formLayout) {
164
+ if (node.formLayout.fields) {
165
+ for (const [name, value] of Object.entries(node.formLayout[AST.LOCATION_SYMBOL].fields)) {
166
+ if (!(name in node.properties)) {
167
+ const location = parser_js_1.locationMap.get(value.name);
168
+ errors.push(new diagnostic_js_1.Diagnostic(`invalid property "${name}"`, location));
169
+ }
170
+ }
171
+ }
172
+ }
163
173
  }
164
174
  for (const node of ast.contracts) {
165
175
  if (node.payload) {
package/dist/semantic.mjs CHANGED
@@ -122,6 +122,16 @@ export const analyze = async (ast, options, errors = []) => {
122
122
  }
123
123
  }
124
124
  }
125
+ if (node.formLayout) {
126
+ if (node.formLayout.fields) {
127
+ for (const [name, value] of Object.entries(node.formLayout[AST.LOCATION_SYMBOL].fields)) {
128
+ if (!(name in node.properties)) {
129
+ const location = locationMap.get(value.name);
130
+ errors.push(new Diagnostic(`invalid property "${name}"`, location));
131
+ }
132
+ }
133
+ }
134
+ }
125
135
  }
126
136
  for (const node of ast.contracts) {
127
137
  if (node.payload) {
package/dist/token.d.ts CHANGED
@@ -19,6 +19,7 @@ export declare const TokenType: {
19
19
  readonly AttributeName: "ATTRIBUTE_NAME";
20
20
  readonly MacroName: "MACRO_NAME";
21
21
  readonly Range: "RANGE";
22
+ readonly Operator: "OPERATOR";
22
23
  };
23
24
  export type TokenType = typeof TokenType[keyof typeof TokenType];
24
25
  export type TypeMap = {
package/dist/token.js CHANGED
@@ -22,4 +22,5 @@ exports.TokenType = {
22
22
  AttributeName: 'ATTRIBUTE_NAME',
23
23
  MacroName: 'MACRO_NAME',
24
24
  Range: 'RANGE',
25
+ Operator: 'OPERATOR',
25
26
  };
package/dist/token.mjs CHANGED
@@ -19,5 +19,6 @@ export const TokenType = {
19
19
  QuotedString: "QUOTED_STRING",
20
20
  AttributeName: "ATTRIBUTE_NAME",
21
21
  MacroName: "MACRO_NAME",
22
- Range: "RANGE"
22
+ Range: "RANGE",
23
+ Operator: "OPERATOR"
23
24
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aeriajs/compiler",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -21,8 +21,8 @@
21
21
  "dist"
22
22
  ],
23
23
  "peerDependencies": {
24
- "@aeriajs/common": "^0.0.141",
25
- "@aeriajs/types": "^0.0.123"
24
+ "@aeriajs/common": "^0.0.142",
25
+ "@aeriajs/types": "^0.0.124"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@aeriajs/common": "link:../common",