@xaendar/compiler 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.
@@ -489,6 +489,7 @@ function consumeInterpolationExpression(cursor, context) {
489
489
  deep--;
490
490
  if (deep === 0) {
491
491
  cursor.advance();
492
+ interpolation = interpolation.trimEnd();
492
493
  const previousState = context.history.pop();
493
494
  let state;
494
495
  switch (previousState) {
@@ -1190,158 +1191,6 @@ var ASTNodeType = /* @__PURE__ */ function(ASTNodeType) {
1190
1191
  return ASTNodeType;
1191
1192
  }({});
1192
1193
  //#endregion
1193
- //#region ../packages/compiler/src/parser/states/parse-const-declaration.state.ts
1194
- /**
1195
- * Parses a CONST_DECLARATION token into a `ConstDeclarationNode`.
1196
- *
1197
- * @param cursor Parser cursor; advanced past the CONST_DECLARATION token.
1198
- * @param _parseNode Unused parser function.
1199
- * @param token The CONST_DECLARATION token containing variable name and expression.
1200
- * @returns The parsed `ConstDeclarationNode`.
1201
- */
1202
- function parseConstDeclaration(cursor, _parseNode, token) {
1203
- cursor.advance();
1204
- return {
1205
- type: ASTNodeType.ConstDeclaration,
1206
- varName: token.parts[0],
1207
- expression: token.parts[1]
1208
- };
1209
- }
1210
- //#endregion
1211
- //#region ../packages/compiler/src/parser/states/parse-interpolation.state.ts
1212
- /**
1213
- * Parses an interpolation expression or literal token into an `InterpolationNode`.
1214
- *
1215
- * @param cursor Parser cursor; advanced past the interpolation token.
1216
- * @param _parseNode Unused parser function.
1217
- * @param token The INTERPOLATION_EXPRESSION or INTERPOLATION_LITERAL token.
1218
- * @returns The parsed `InterpolationNode`.
1219
- */
1220
- function parseInterpolation(cursor, _parseNode, token) {
1221
- cursor.advance();
1222
- return {
1223
- type: ASTNodeType.Interpolation,
1224
- expression: token.parts[0]
1225
- };
1226
- }
1227
- //#endregion
1228
- //#region ../packages/compiler/src/parser/states/parse-attribute.state.ts
1229
- /**
1230
- * Parses an ATTRIBUTE token into an `AttributeNode`.
1231
- * Handles boolean attributes (no `=`), string values, and interpolation values.
1232
- *
1233
- * @param cursor Parser cursor; advanced past the ATTRIBUTE token.
1234
- * @param parseNode Parser node function for recursive parsing.
1235
- * @param token The ATTRIBUTE token to parse.
1236
- * @returns The parsed `AttributeNode`.
1237
- */
1238
- function parseAttribute(cursor, parseNode, token) {
1239
- cursor.advance();
1240
- const raw = token.parts[0];
1241
- if (!raw.includes("=")) return {
1242
- name: raw,
1243
- value: "true"
1244
- };
1245
- const [name, value] = raw.split("=");
1246
- if (!name) throw new Error(`[Parser] Attribute name missing in: ${raw}`);
1247
- const nextToken = cursor.peek();
1248
- if (nextToken.type === TokenType.INTERPOLATION_EXPRESSION || nextToken.type === TokenType.INTERPOLATION_LITERAL) return {
1249
- name,
1250
- value: parseInterpolation(cursor, parseNode, nextToken)
1251
- };
1252
- if (!value) throw new Error(`[Parser] Attribute value missing for ${name} in: ${raw}`);
1253
- return {
1254
- name,
1255
- value: value.replace(/^['']|['']$/g, "")
1256
- };
1257
- }
1258
- //#endregion
1259
- //#region ../packages/compiler/src/parser/states/parse-event.state.ts
1260
- /**
1261
- * Parses an EVENT token into an `EventNode` by splitting the raw
1262
- * `eventName=handler` string.
1263
- *
1264
- * @param cursor Parser cursor; advanced past the EVENT token.
1265
- * @param _parseNode Unused parser function.
1266
- * @param token The EVENT token to parse.
1267
- * @returns The parsed `EventNode`.
1268
- */
1269
- function parseEvent(cursor, _parseNode, token) {
1270
- cursor.advance();
1271
- const raw = token.parts[0];
1272
- const [name, value] = raw.split("=");
1273
- if (!name || !value) throw new Error(`[Parser] Invalid event format: ${raw}`);
1274
- return {
1275
- name,
1276
- handler: value.replace(/^[""]|[""]$/g, "")
1277
- };
1278
- }
1279
- //#endregion
1280
- //#region ../packages/compiler/src/parser/states/parse-element.state.ts
1281
- /**
1282
- * Parses a TAG_OPEN_NAME token and the subsequent attributes, events, and children
1283
- * into an `ElementNode`. Handles both regular and self-closing tags.
1284
- *
1285
- * @param cursor Parser cursor positioned at the TAG_OPEN_NAME token.
1286
- * @param context Parser context for recursive child parsing.
1287
- * @param token The TAG_OPEN_NAME token containing the tag name.
1288
- * @returns The parsed `ElementNode`.
1289
- */
1290
- function parseElement(cursor, parseNode, token) {
1291
- cursor.advance();
1292
- const tagName = token.parts[0];
1293
- const attributes = new Array();
1294
- const events = new Array();
1295
- let read = true;
1296
- while (read) {
1297
- const token = cursor.peek();
1298
- switch (token.type) {
1299
- case TokenType.ATTRIBUTE:
1300
- attributes.push(parseAttribute(cursor, parseNode, token));
1301
- break;
1302
- case TokenType.EVENT:
1303
- events.push(parseEvent(cursor, parseNode, token));
1304
- break;
1305
- default: read = false;
1306
- }
1307
- }
1308
- if (cursor.peek().type === TokenType.TAG_OPEN_END) cursor.advance();
1309
- if (cursor.peek().type === TokenType.TAG_SELF_CLOSE) {
1310
- cursor.advance();
1311
- return {
1312
- type: ASTNodeType.Element,
1313
- tagName,
1314
- attributes,
1315
- events,
1316
- children: []
1317
- };
1318
- }
1319
- const children = new Array();
1320
- while (!isTagClose(cursor, tagName)) {
1321
- const child = parseNode();
1322
- if (child) children.push(child);
1323
- }
1324
- cursor.advance();
1325
- return {
1326
- type: ASTNodeType.Element,
1327
- tagName,
1328
- attributes,
1329
- events,
1330
- children
1331
- };
1332
- }
1333
- /**
1334
- * Returns `true` if the next token in the stream is a closing tag for the given tag name.
1335
- *
1336
- * @param cursor Parser cursor to peek from.
1337
- * @param tagName The expected tag name to match.
1338
- * @returns `true` if the next token is TAG_CLOSE_NAME matching `tagName`.
1339
- */
1340
- function isTagClose(cursor, tagName) {
1341
- const nextToken = cursor.peek();
1342
- return nextToken.type === TokenType.TAG_CLOSE_NAME && nextToken.parts[0] === tagName;
1343
- }
1344
- //#endregion
1345
1194
  //#region ../packages/compiler/src/parser/utils/expression-validator.ts
1346
1195
  /**
1347
1196
  * Validates that a string contains a single expression belonging to the
@@ -1519,6 +1368,158 @@ function isAssignmentOperator(kind) {
1519
1368
  return kind >= ts.SyntaxKind.FirstAssignment && kind <= ts.SyntaxKind.LastAssignment;
1520
1369
  }
1521
1370
  //#endregion
1371
+ //#region ../packages/compiler/src/parser/states/parse-const-declaration.state.ts
1372
+ /**
1373
+ * Parses a CONST_DECLARATION token into a `ConstDeclarationNode`.
1374
+ *
1375
+ * @param cursor Parser cursor; advanced past the CONST_DECLARATION token.
1376
+ * @param _parseNode Unused parser function.
1377
+ * @param token The CONST_DECLARATION token containing variable name and expression.
1378
+ * @returns The parsed `ConstDeclarationNode`.
1379
+ */
1380
+ function parseConstDeclaration(cursor, _parseNode, token) {
1381
+ cursor.advance();
1382
+ return {
1383
+ type: ASTNodeType.ConstDeclaration,
1384
+ varName: token.parts[0],
1385
+ expression: validateExpression(token.parts[1]).node
1386
+ };
1387
+ }
1388
+ //#endregion
1389
+ //#region ../packages/compiler/src/parser/states/parse-interpolation.state.ts
1390
+ /**
1391
+ * Parses an interpolation expression or literal token into an `InterpolationNode`.
1392
+ *
1393
+ * @param cursor Parser cursor; advanced past the interpolation token.
1394
+ * @param _parseNode Unused parser function.
1395
+ * @param token The INTERPOLATION_EXPRESSION or INTERPOLATION_LITERAL token.
1396
+ * @returns The parsed `InterpolationNode`.
1397
+ */
1398
+ function parseInterpolation(cursor, _parseNode, token) {
1399
+ cursor.advance();
1400
+ return {
1401
+ type: ASTNodeType.Interpolation,
1402
+ expression: validateExpression(token.parts[0]).node
1403
+ };
1404
+ }
1405
+ //#endregion
1406
+ //#region ../packages/compiler/src/parser/states/parse-attribute.state.ts
1407
+ /**
1408
+ * Parses an ATTRIBUTE token into an `AttributeNode`.
1409
+ * Handles boolean attributes (no `=`), string values, and interpolation values.
1410
+ *
1411
+ * @param cursor Parser cursor; advanced past the ATTRIBUTE token.
1412
+ * @param parseNode Parser node function for recursive parsing.
1413
+ * @param token The ATTRIBUTE token to parse.
1414
+ * @returns The parsed `AttributeNode`.
1415
+ */
1416
+ function parseAttribute(cursor, parseNode, token) {
1417
+ cursor.advance();
1418
+ const raw = token.parts[0];
1419
+ if (!raw.includes("=")) return {
1420
+ name: raw,
1421
+ value: "true"
1422
+ };
1423
+ const [name, value] = raw.split("=");
1424
+ if (!name) throw new Error(`[Parser] Attribute name missing in: ${raw}`);
1425
+ const nextToken = cursor.peek();
1426
+ if (nextToken.type === TokenType.INTERPOLATION_EXPRESSION || nextToken.type === TokenType.INTERPOLATION_LITERAL) return {
1427
+ name,
1428
+ value: parseInterpolation(cursor, parseNode, nextToken)
1429
+ };
1430
+ if (!value) throw new Error(`[Parser] Attribute value missing for ${name} in: ${raw}`);
1431
+ return {
1432
+ name,
1433
+ value: value.replace(/^['']|['']$/g, "")
1434
+ };
1435
+ }
1436
+ //#endregion
1437
+ //#region ../packages/compiler/src/parser/states/parse-event.state.ts
1438
+ /**
1439
+ * Parses an EVENT token into an `EventNode` by splitting the raw
1440
+ * `eventName=handler` string.
1441
+ *
1442
+ * @param cursor Parser cursor; advanced past the EVENT token.
1443
+ * @param _parseNode Unused parser function.
1444
+ * @param token The EVENT token to parse.
1445
+ * @returns The parsed `EventNode`.
1446
+ */
1447
+ function parseEvent(cursor, _parseNode, token) {
1448
+ cursor.advance();
1449
+ const raw = token.parts[0];
1450
+ const [name, value] = raw.split("=");
1451
+ if (!name || !value) throw new Error(`[Parser] Invalid event format: ${raw}`);
1452
+ return {
1453
+ name,
1454
+ handler: value.replace(/^[""]|[""]$/g, "")
1455
+ };
1456
+ }
1457
+ //#endregion
1458
+ //#region ../packages/compiler/src/parser/states/parse-element.state.ts
1459
+ /**
1460
+ * Parses a TAG_OPEN_NAME token and the subsequent attributes, events, and children
1461
+ * into an `ElementNode`. Handles both regular and self-closing tags.
1462
+ *
1463
+ * @param cursor Parser cursor positioned at the TAG_OPEN_NAME token.
1464
+ * @param context Parser context for recursive child parsing.
1465
+ * @param token The TAG_OPEN_NAME token containing the tag name.
1466
+ * @returns The parsed `ElementNode`.
1467
+ */
1468
+ function parseElement(cursor, parseNode, token) {
1469
+ cursor.advance();
1470
+ const tagName = token.parts[0];
1471
+ const attributes = new Array();
1472
+ const events = new Array();
1473
+ let read = true;
1474
+ while (read) {
1475
+ const token = cursor.peek();
1476
+ switch (token.type) {
1477
+ case TokenType.ATTRIBUTE:
1478
+ attributes.push(parseAttribute(cursor, parseNode, token));
1479
+ break;
1480
+ case TokenType.EVENT:
1481
+ events.push(parseEvent(cursor, parseNode, token));
1482
+ break;
1483
+ default: read = false;
1484
+ }
1485
+ }
1486
+ if (cursor.peek().type === TokenType.TAG_OPEN_END) cursor.advance();
1487
+ if (cursor.peek().type === TokenType.TAG_SELF_CLOSE) {
1488
+ cursor.advance();
1489
+ return {
1490
+ type: ASTNodeType.Element,
1491
+ tagName,
1492
+ attributes,
1493
+ events,
1494
+ children: []
1495
+ };
1496
+ }
1497
+ const children = new Array();
1498
+ while (!isTagClose(cursor, tagName)) {
1499
+ const child = parseNode();
1500
+ if (child) children.push(child);
1501
+ }
1502
+ cursor.advance();
1503
+ return {
1504
+ type: ASTNodeType.Element,
1505
+ tagName,
1506
+ attributes,
1507
+ events,
1508
+ children
1509
+ };
1510
+ }
1511
+ /**
1512
+ * Returns `true` if the next token in the stream is a closing tag for the given tag name.
1513
+ *
1514
+ * @param cursor Parser cursor to peek from.
1515
+ * @param tagName The expected tag name to match.
1516
+ * @returns `true` if the next token is TAG_CLOSE_NAME matching `tagName`.
1517
+ */
1518
+ function isTagClose(cursor, tagName) {
1519
+ const nextToken = cursor.peek();
1520
+ return nextToken.type === TokenType.TAG_CLOSE_NAME && nextToken.parts[0] === tagName;
1521
+ }
1522
+ //#endregion
1522
1523
  //#region ../packages/compiler/src/parser/states/parse-block-children.state.ts
1523
1524
  /**
1524
1525
  * Parses child AST nodes inside a flow-control block until a BLOCK_CLOSE token is reached.
@@ -1760,7 +1761,7 @@ function parseSwitchControlFlow(cursor, parseNode, _token) {
1760
1761
  cursor.advance();
1761
1762
  const conditionToken = cursor.peek();
1762
1763
  if (conditionToken.type !== TokenType.CONDITION) throw new Error(`[Parser] Expected CONDITION after SWITCH, got ${TokenType[conditionToken.type]}`);
1763
- const expression = conditionToken.parts[0];
1764
+ const expression = validateExpression(conditionToken.parts[0]).node;
1764
1765
  cursor.advance(2);
1765
1766
  const cases = new Array();
1766
1767
  while (cursor.peek().type !== TokenType.BLOCK_CLOSE) switch (cursor.peek().type) {
@@ -2129,7 +2130,7 @@ var ROOT_NODE = "this._root";
2129
2130
  * // → typeof this.id !== 'boolean' || this.pippo instanceof HTMLElement
2130
2131
  */
2131
2132
  function resolveExpression(expression, context) {
2132
- return typeof expression === "string" ? context.getIdentifier(expression) ?? `this.${expression}` : emitNode(expression, expression, context);
2133
+ return emitNode(expression, expression, context);
2133
2134
  }
2134
2135
  /**
2135
2136
  * Emits the resolved text for a node.
@@ -2295,7 +2296,7 @@ function processFor(node, nodeName, parentNode, parentContext) {
2295
2296
  ],
2296
2297
  args: [itemsName, counterName]
2297
2298
  });
2298
- mainBlock.push("(() => {", ...indent("let localUnwatchFns = [];", "const unwatch = () => {", ...indent("localUnwatchFns?.forEach(fn => fn());", "localUnwatchFns = [];", "unwatchFns = unwatchFns.filter(fn => !localUnwatchFns.includes(fn));"), "};", "unwatchFns.push(", ...indent("effect(() => {", ...indent("unwatch();", `const ${itemsName} = ${iterableExpr};`, "Signal.subtle.untrack(() => {", ...indent(`for (let ${counterName} = 0; ${counterName} < ${itemsName}.length; ${counterName}++) {`, ...indent(`localUnwatchFns.push(...this.for_${nodeName}(${itemsName}, ${counterName}));`, "unwatchFns.push(...localUnwatchFns);"), "}"), "});"), "})"), ");"), "})();");
2299
+ mainBlock.push("(() => {", ...indent("let localUnwatchFns = [];", "const unwatch = () => {", ...indent("unwatchFns = unwatchFns.filter(fn => !localUnwatchFns.includes(fn));", "localUnwatchFns?.forEach(fn => fn());", "localUnwatchFns = [];"), "};", "unwatchFns.push(", ...indent("effect(() => {", ...indent("unwatch();", `const ${itemsName} = ${iterableExpr};`, "Signal.subtle.untrack(() => {", ...indent(`for (let ${counterName} = 0; ${counterName} < ${itemsName}.length; ${counterName}++) {`, ...indent(`localUnwatchFns.push(...this.for_${nodeName}(${itemsName}, ${counterName}));`, "unwatchFns.push(...localUnwatchFns);"), "}"), "});"), "})"), ");"), "})();");
2299
2300
  return {
2300
2301
  mainBlock,
2301
2302
  fns: functionsToProcess
@@ -2355,7 +2356,7 @@ function processIf(node, nodeName, parentNode, context) {
2355
2356
  return {
2356
2357
  mainBlock: [
2357
2358
  "(() => {",
2358
- ...indent("let state;", "let localUnwatchFns = []", "const checkAndUpdateState = (newState, fn) => {", ...indent("if (state === newState) {", ...indent("return;"), "}", "state = newState;", "unwatch();", "localUnwatchFns = Signal.subtle.untrack(fn);", "unwatchFns.push(...localUnwatchFns);"), "};", "const unwatch = () => {", ...indent("localUnwatchFns?.forEach(fn => fn());", "unwatchFns = unwatchFns.filter(fn => !localUnwatchFns.includes(fn));", "localUnwatchFns = [];"), "}", "unwatchFns.push(", ...indent("effect(() => {", ...indent(...mainBlock), "})"), ");"),
2359
+ ...indent("let state;", "let localUnwatchFns = []", "const checkAndUpdateState = (newState, fn) => {", ...indent("if (state === newState) {", ...indent("return;"), "}", "state = newState;", "unwatch();", "localUnwatchFns = Signal.subtle.untrack(fn);", "unwatchFns.push(...localUnwatchFns);"), "};", "const unwatch = () => {", ...indent("unwatchFns = unwatchFns.filter(fn => !localUnwatchFns.includes(fn));", "localUnwatchFns?.forEach(fn => fn());", "localUnwatchFns = [];"), "}", "unwatchFns.push(", ...indent("effect(() => {", ...indent(...mainBlock), "})"), ");"),
2359
2360
  "})();"
2360
2361
  ],
2361
2362
  fns: functionsToProcess
@@ -2391,7 +2392,7 @@ function processSwitch(node, nodeName, parentNode, context) {
2391
2392
  return {
2392
2393
  mainBlock: [
2393
2394
  "(() => {",
2394
- ...indent("let localUnwatchFns = []", "const checkAndUpdateState = (newState, fn) => {", ...indent("unwatch();", "localUnwatchFns = Signal.subtle.untrack(fn);", "unwatchFns.push(...localUnwatchFns);"), "};", "const unwatch = () => {", ...indent("localUnwatchFns?.forEach(fn => fn());", "unwatchFns = unwatchFns.filter(fn => !localUnwatchFns.includes(fn));", "localUnwatchFns = [];"), "}", "unwatchFns.push(", ...indent("effect(() => {", ...indent("unwatch();", ...mainBlock), "})"), ");"),
2395
+ ...indent("let localUnwatchFns = []", "const unwatch = () => {", ...indent("unwatchFns = unwatchFns.filter(fn => !localUnwatchFns.includes(fn));", "localUnwatchFns?.forEach(fn => fn());", "localUnwatchFns = [];"), "}", "unwatchFns.push(", ...indent("effect(() => {", ...indent("unwatch();", ...mainBlock), "})"), ");"),
2395
2396
  "})();"
2396
2397
  ],
2397
2398
  fns: functionsToProcess
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xaendar/compiler",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "A library for transpiling Xaendar Templates into JavaScript code",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -16,8 +16,8 @@
16
16
  }
17
17
  },
18
18
  "dependencies": {
19
- "@xaendar/common": "0.5.1",
20
- "@xaendar/types": "0.5.1",
19
+ "@xaendar/common": "0.5.3",
20
+ "@xaendar/types": "0.5.3",
21
21
  "typescript": "^6.0.3"
22
22
  }
23
23
  }