@tsonic/emitter 0.0.63 → 0.0.64

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.
Files changed (55) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/core/semantic/boolean-context.d.ts.map +1 -1
  3. package/dist/core/semantic/boolean-context.js +88 -26
  4. package/dist/core/semantic/boolean-context.js.map +1 -1
  5. package/dist/core/semantic/boolean-context.test.js +42 -1
  6. package/dist/core/semantic/boolean-context.test.js.map +1 -1
  7. package/dist/core/semantic/imports.d.ts.map +1 -1
  8. package/dist/core/semantic/imports.js +53 -0
  9. package/dist/core/semantic/imports.js.map +1 -1
  10. package/dist/core/semantic/imports.test.js +150 -0
  11. package/dist/core/semantic/imports.test.js.map +1 -1
  12. package/dist/emitter-types/core.d.ts +2 -0
  13. package/dist/emitter-types/core.d.ts.map +1 -1
  14. package/dist/expression-emitter.d.ts.map +1 -1
  15. package/dist/expression-emitter.js +224 -2
  16. package/dist/expression-emitter.js.map +1 -1
  17. package/dist/expressions/access.d.ts.map +1 -1
  18. package/dist/expressions/access.js +77 -1
  19. package/dist/expressions/access.js.map +1 -1
  20. package/dist/expressions/calls/call-emitter.d.ts.map +1 -1
  21. package/dist/expressions/calls/call-emitter.js +172 -0
  22. package/dist/expressions/calls/call-emitter.js.map +1 -1
  23. package/dist/expressions/collections.d.ts.map +1 -1
  24. package/dist/expressions/collections.js +17 -0
  25. package/dist/expressions/collections.js.map +1 -1
  26. package/dist/expressions/index.test.js +462 -0
  27. package/dist/expressions/index.test.js.map +1 -1
  28. package/dist/integration.test.js +3 -0
  29. package/dist/integration.test.js.map +1 -1
  30. package/dist/specialization/type-aliases.test.js +44 -0
  31. package/dist/specialization/type-aliases.test.js.map +1 -1
  32. package/dist/statements/blocks.d.ts.map +1 -1
  33. package/dist/statements/blocks.js +52 -1
  34. package/dist/statements/blocks.js.map +1 -1
  35. package/dist/statements/classes/members/methods.d.ts.map +1 -1
  36. package/dist/statements/classes/members/methods.js +8 -3
  37. package/dist/statements/classes/members/methods.js.map +1 -1
  38. package/dist/statements/declarations/classes.d.ts.map +1 -1
  39. package/dist/statements/declarations/classes.js +43 -2
  40. package/dist/statements/declarations/classes.js.map +1 -1
  41. package/dist/statements/declarations/functions.d.ts.map +1 -1
  42. package/dist/statements/declarations/functions.js +8 -3
  43. package/dist/statements/declarations/functions.js.map +1 -1
  44. package/dist/statements/declarations/interfaces.d.ts.map +1 -1
  45. package/dist/statements/declarations/interfaces.js +21 -1
  46. package/dist/statements/declarations/interfaces.js.map +1 -1
  47. package/dist/statements/declarations/type-aliases.d.ts.map +1 -1
  48. package/dist/statements/declarations/type-aliases.js +27 -0
  49. package/dist/statements/declarations/type-aliases.js.map +1 -1
  50. package/dist/statements/declarations/variables.d.ts.map +1 -1
  51. package/dist/statements/declarations/variables.js +10 -5
  52. package/dist/statements/declarations/variables.js.map +1 -1
  53. package/dist/statements/index.test.js +307 -0
  54. package/dist/statements/index.test.js.map +1 -1
  55. package/package.json +2 -2
@@ -349,6 +349,126 @@ describe("Expression Emission", () => {
349
349
  expect(result).to.include('global::Tsonic.JSRuntime.String.split(path, "/")');
350
350
  expect(result).not.to.include("path.split");
351
351
  });
352
+ it("should rewrite js-surface array functional calls via LINQ", () => {
353
+ const module = {
354
+ kind: "module",
355
+ filePath: "/src/test.ts",
356
+ namespace: "MyApp",
357
+ className: "test",
358
+ isStaticContainer: true,
359
+ imports: [],
360
+ body: [
361
+ {
362
+ kind: "expressionStatement",
363
+ expression: {
364
+ kind: "call",
365
+ callee: {
366
+ kind: "memberAccess",
367
+ object: {
368
+ kind: "identifier",
369
+ name: "nums",
370
+ inferredType: {
371
+ kind: "arrayType",
372
+ elementType: { kind: "primitiveType", name: "int" },
373
+ },
374
+ },
375
+ property: "map",
376
+ isComputed: false,
377
+ isOptional: false,
378
+ },
379
+ arguments: [{ kind: "identifier", name: "doubleIt" }],
380
+ isOptional: false,
381
+ inferredType: {
382
+ kind: "arrayType",
383
+ elementType: { kind: "primitiveType", name: "int" },
384
+ },
385
+ },
386
+ },
387
+ ],
388
+ exports: [],
389
+ };
390
+ const result = emitModule(module, { surface: "js" });
391
+ expect(result).to.include("global::System.Linq.Enumerable.Select(nums, doubleIt)");
392
+ expect(result).to.include("global::System.Linq.Enumerable.ToArray(");
393
+ expect(result).not.to.include("new global::Tsonic.JSRuntime.JSArray");
394
+ });
395
+ it("should rewrite js-surface reduce/reduceRight/join via deterministic CLR calls", () => {
396
+ const numsExpr = {
397
+ kind: "identifier",
398
+ name: "nums",
399
+ inferredType: {
400
+ kind: "arrayType",
401
+ elementType: { kind: "primitiveType", name: "int" },
402
+ },
403
+ };
404
+ const module = {
405
+ kind: "module",
406
+ filePath: "/src/test.ts",
407
+ namespace: "MyApp",
408
+ className: "test",
409
+ isStaticContainer: true,
410
+ imports: [],
411
+ body: [
412
+ {
413
+ kind: "expressionStatement",
414
+ expression: {
415
+ kind: "call",
416
+ callee: {
417
+ kind: "memberAccess",
418
+ object: numsExpr,
419
+ property: "reduce",
420
+ isComputed: false,
421
+ isOptional: false,
422
+ },
423
+ arguments: [
424
+ { kind: "identifier", name: "sum" },
425
+ { kind: "literal", value: 0 },
426
+ ],
427
+ isOptional: false,
428
+ },
429
+ },
430
+ {
431
+ kind: "expressionStatement",
432
+ expression: {
433
+ kind: "call",
434
+ callee: {
435
+ kind: "memberAccess",
436
+ object: numsExpr,
437
+ property: "reduceRight",
438
+ isComputed: false,
439
+ isOptional: false,
440
+ },
441
+ arguments: [
442
+ { kind: "identifier", name: "sum" },
443
+ { kind: "literal", value: 0 },
444
+ ],
445
+ isOptional: false,
446
+ },
447
+ },
448
+ {
449
+ kind: "expressionStatement",
450
+ expression: {
451
+ kind: "call",
452
+ callee: {
453
+ kind: "memberAccess",
454
+ object: numsExpr,
455
+ property: "join",
456
+ isComputed: false,
457
+ isOptional: false,
458
+ },
459
+ arguments: [],
460
+ isOptional: false,
461
+ },
462
+ },
463
+ ],
464
+ exports: [],
465
+ };
466
+ const result = emitModule(module, { surface: "js" });
467
+ expect(result).to.include("global::System.Linq.Enumerable.Aggregate(nums, 0, sum)");
468
+ expect(result).to.include("global::System.Linq.Enumerable.Aggregate(global::System.Linq.Enumerable.Reverse(nums), 0, sum)");
469
+ expect(result).to.include('global::System.String.Join(",", nums)');
470
+ expect(result).not.to.include("new global::Tsonic.JSRuntime.JSArray");
471
+ });
352
472
  it("should emit fluent LINQ extension method calls (required for EF query precompilation)", () => {
353
473
  const module = {
354
474
  kind: "module",
@@ -965,6 +1085,162 @@ describe("Expression Emission", () => {
965
1085
  // Should emit regular property access
966
1086
  expect(result).to.include("obj.property");
967
1087
  });
1088
+ it("should emit array Length for js/node surfaces when binding provides lowercase length", () => {
1089
+ const module = {
1090
+ kind: "module",
1091
+ filePath: "/src/test.ts",
1092
+ namespace: "MyApp",
1093
+ className: "test",
1094
+ isStaticContainer: true,
1095
+ imports: [],
1096
+ body: [
1097
+ {
1098
+ kind: "expressionStatement",
1099
+ expression: {
1100
+ kind: "memberAccess",
1101
+ object: {
1102
+ kind: "identifier",
1103
+ name: "nums",
1104
+ inferredType: {
1105
+ kind: "arrayType",
1106
+ elementType: { kind: "primitiveType", name: "int" },
1107
+ },
1108
+ },
1109
+ property: "length",
1110
+ isComputed: false,
1111
+ isOptional: false,
1112
+ memberBinding: {
1113
+ assembly: "Tsonic.JSRuntime",
1114
+ type: "Tsonic.JSRuntime.JSArray",
1115
+ member: "length",
1116
+ },
1117
+ },
1118
+ },
1119
+ ],
1120
+ exports: [],
1121
+ };
1122
+ const jsResult = emitModule(module, { surface: "js" });
1123
+ expect(jsResult).to.include("nums.Length");
1124
+ expect(jsResult).not.to.include("nums.length");
1125
+ const nodeResult = emitModule(module, { surface: "nodejs" });
1126
+ expect(nodeResult).to.include("nums.Length");
1127
+ expect(nodeResult).not.to.include("nums.length");
1128
+ });
1129
+ it("should emit array Length for js/node surfaces without member binding", () => {
1130
+ const module = {
1131
+ kind: "module",
1132
+ filePath: "/src/test.ts",
1133
+ namespace: "MyApp",
1134
+ className: "test",
1135
+ isStaticContainer: true,
1136
+ imports: [],
1137
+ body: [
1138
+ {
1139
+ kind: "expressionStatement",
1140
+ expression: {
1141
+ kind: "memberAccess",
1142
+ object: {
1143
+ kind: "identifier",
1144
+ name: "nums",
1145
+ inferredType: {
1146
+ kind: "arrayType",
1147
+ elementType: { kind: "primitiveType", name: "int" },
1148
+ },
1149
+ },
1150
+ property: "length",
1151
+ isComputed: false,
1152
+ isOptional: false,
1153
+ },
1154
+ },
1155
+ ],
1156
+ exports: [],
1157
+ };
1158
+ const jsResult = emitModule(module, { surface: "js" });
1159
+ expect(jsResult).to.include("nums.Length");
1160
+ expect(jsResult).not.to.include("nums.length");
1161
+ const nodeResult = emitModule(module, { surface: "nodejs" });
1162
+ expect(nodeResult).to.include("nums.Length");
1163
+ expect(nodeResult).not.to.include("nums.length");
1164
+ });
1165
+ it("should emit array Length for js/node surfaces when array type is referenceType", () => {
1166
+ const module = {
1167
+ kind: "module",
1168
+ filePath: "/src/test.ts",
1169
+ namespace: "MyApp",
1170
+ className: "test",
1171
+ isStaticContainer: true,
1172
+ imports: [],
1173
+ body: [
1174
+ {
1175
+ kind: "expressionStatement",
1176
+ expression: {
1177
+ kind: "memberAccess",
1178
+ object: {
1179
+ kind: "identifier",
1180
+ name: "nums",
1181
+ inferredType: {
1182
+ kind: "referenceType",
1183
+ name: "Array",
1184
+ typeArguments: [{ kind: "primitiveType", name: "int" }],
1185
+ },
1186
+ },
1187
+ property: "length",
1188
+ isComputed: false,
1189
+ isOptional: false,
1190
+ },
1191
+ },
1192
+ ],
1193
+ exports: [],
1194
+ };
1195
+ const jsResult = emitModule(module, { surface: "js" });
1196
+ expect(jsResult).to.include("nums.Length");
1197
+ expect(jsResult).not.to.include("nums.length");
1198
+ const nodeResult = emitModule(module, { surface: "nodejs" });
1199
+ expect(nodeResult).to.include("nums.Length");
1200
+ expect(nodeResult).not.to.include("nums.length");
1201
+ });
1202
+ it("should emit array Length for js/node surfaces with member binding and ReadonlyArray referenceType", () => {
1203
+ const module = {
1204
+ kind: "module",
1205
+ filePath: "/src/test.ts",
1206
+ namespace: "MyApp",
1207
+ className: "test",
1208
+ isStaticContainer: true,
1209
+ imports: [],
1210
+ body: [
1211
+ {
1212
+ kind: "expressionStatement",
1213
+ expression: {
1214
+ kind: "memberAccess",
1215
+ object: {
1216
+ kind: "identifier",
1217
+ name: "nums",
1218
+ inferredType: {
1219
+ kind: "referenceType",
1220
+ name: "ReadonlyArray",
1221
+ typeArguments: [{ kind: "primitiveType", name: "int" }],
1222
+ },
1223
+ },
1224
+ property: "length",
1225
+ isComputed: false,
1226
+ isOptional: false,
1227
+ memberBinding: {
1228
+ assembly: "Tsonic.JSRuntime",
1229
+ type: "Tsonic.JSRuntime.JSArray",
1230
+ member: "length",
1231
+ },
1232
+ },
1233
+ },
1234
+ ],
1235
+ exports: [],
1236
+ };
1237
+ const jsResult = emitModule(module, { surface: "js" });
1238
+ expect(jsResult).to.include("nums.Length");
1239
+ expect(jsResult).not.to.include("nums.length");
1240
+ const nodeResult = emitModule(module, { surface: "nodejs" });
1241
+ expect(nodeResult).to.include("nums.Length");
1242
+ expect(nodeResult).not.to.include("nums.length");
1243
+ });
968
1244
  it("should project CLR Union_n member access deterministically", () => {
969
1245
  const unionReference = {
970
1246
  kind: "referenceType",
@@ -1269,6 +1545,192 @@ describe("Expression Emission", () => {
1269
1545
  expect(result).to.include('(dict).ContainsKey("x")');
1270
1546
  expect(result).to.not.include('dict["x"] != null');
1271
1547
  });
1548
+ it("should lower dictionary.Keys to a materialized key array", () => {
1549
+ const dictType = {
1550
+ kind: "dictionaryType",
1551
+ keyType: { kind: "primitiveType", name: "string" },
1552
+ valueType: { kind: "primitiveType", name: "number" },
1553
+ };
1554
+ const module = {
1555
+ kind: "module",
1556
+ filePath: "/src/test.ts",
1557
+ namespace: "MyApp",
1558
+ className: "test",
1559
+ isStaticContainer: true,
1560
+ imports: [],
1561
+ body: [
1562
+ {
1563
+ kind: "variableDeclaration",
1564
+ declarationKind: "const",
1565
+ isExported: false,
1566
+ declarations: [
1567
+ {
1568
+ kind: "variableDeclarator",
1569
+ name: { kind: "identifierPattern", name: "keys" },
1570
+ initializer: {
1571
+ kind: "memberAccess",
1572
+ object: {
1573
+ kind: "identifier",
1574
+ name: "dict",
1575
+ inferredType: dictType,
1576
+ },
1577
+ property: "Keys",
1578
+ isComputed: false,
1579
+ isOptional: false,
1580
+ inferredType: {
1581
+ kind: "arrayType",
1582
+ elementType: { kind: "primitiveType", name: "string" },
1583
+ },
1584
+ },
1585
+ },
1586
+ ],
1587
+ },
1588
+ ],
1589
+ exports: [],
1590
+ };
1591
+ const result = emitModule(module);
1592
+ expect(result).to.include("new global::System.Collections.Generic.List<string>(dict.Keys).ToArray()");
1593
+ });
1594
+ it("should lower dictionary.Values to a materialized value array", () => {
1595
+ const dictType = {
1596
+ kind: "dictionaryType",
1597
+ keyType: { kind: "primitiveType", name: "string" },
1598
+ valueType: { kind: "referenceType", name: "long" },
1599
+ };
1600
+ const module = {
1601
+ kind: "module",
1602
+ filePath: "/src/test.ts",
1603
+ namespace: "MyApp",
1604
+ className: "test",
1605
+ isStaticContainer: true,
1606
+ imports: [],
1607
+ body: [
1608
+ {
1609
+ kind: "variableDeclaration",
1610
+ declarationKind: "const",
1611
+ isExported: false,
1612
+ declarations: [
1613
+ {
1614
+ kind: "variableDeclarator",
1615
+ name: { kind: "identifierPattern", name: "values" },
1616
+ initializer: {
1617
+ kind: "memberAccess",
1618
+ object: {
1619
+ kind: "identifier",
1620
+ name: "dict",
1621
+ inferredType: dictType,
1622
+ },
1623
+ property: "Values",
1624
+ isComputed: false,
1625
+ isOptional: false,
1626
+ inferredType: {
1627
+ kind: "arrayType",
1628
+ elementType: { kind: "referenceType", name: "long" },
1629
+ },
1630
+ },
1631
+ },
1632
+ ],
1633
+ },
1634
+ ],
1635
+ exports: [],
1636
+ };
1637
+ const result = emitModule(module);
1638
+ expect(result).to.include("new global::System.Collections.Generic.List<long>(dict.Values).ToArray()");
1639
+ });
1640
+ it("should upcast dictionary values into union wrappers for expected dictionary union types", () => {
1641
+ const module = {
1642
+ kind: "module",
1643
+ filePath: "/src/test.ts",
1644
+ namespace: "MyApp",
1645
+ className: "test",
1646
+ isStaticContainer: true,
1647
+ imports: [],
1648
+ body: [
1649
+ {
1650
+ kind: "variableDeclaration",
1651
+ declarationKind: "const",
1652
+ isExported: false,
1653
+ declarations: [
1654
+ {
1655
+ kind: "variableDeclarator",
1656
+ name: { kind: "identifierPattern", name: "widened" },
1657
+ type: {
1658
+ kind: "dictionaryType",
1659
+ keyType: { kind: "primitiveType", name: "string" },
1660
+ valueType: {
1661
+ kind: "unionType",
1662
+ types: [
1663
+ { kind: "referenceType", name: "int" },
1664
+ { kind: "primitiveType", name: "string" },
1665
+ ],
1666
+ },
1667
+ },
1668
+ initializer: {
1669
+ kind: "identifier",
1670
+ name: "raw",
1671
+ inferredType: {
1672
+ kind: "dictionaryType",
1673
+ keyType: { kind: "primitiveType", name: "string" },
1674
+ valueType: { kind: "referenceType", name: "int" },
1675
+ },
1676
+ },
1677
+ },
1678
+ ],
1679
+ },
1680
+ ],
1681
+ exports: [],
1682
+ };
1683
+ const result = emitModule(module);
1684
+ expect(result).to.include("global::System.Linq.Enumerable.ToDictionary");
1685
+ expect(result).to.include("global::Tsonic.Runtime.Union<int, string>.From1");
1686
+ });
1687
+ it("should not upcast when dictionary value type already matches union runtime type", () => {
1688
+ const unionType = {
1689
+ kind: "unionType",
1690
+ types: [
1691
+ { kind: "referenceType", name: "int" },
1692
+ { kind: "primitiveType", name: "string" },
1693
+ ],
1694
+ };
1695
+ const module = {
1696
+ kind: "module",
1697
+ filePath: "/src/test.ts",
1698
+ namespace: "MyApp",
1699
+ className: "test",
1700
+ isStaticContainer: true,
1701
+ imports: [],
1702
+ body: [
1703
+ {
1704
+ kind: "variableDeclaration",
1705
+ declarationKind: "const",
1706
+ isExported: false,
1707
+ declarations: [
1708
+ {
1709
+ kind: "variableDeclarator",
1710
+ name: { kind: "identifierPattern", name: "alreadyWide" },
1711
+ type: {
1712
+ kind: "dictionaryType",
1713
+ keyType: { kind: "primitiveType", name: "string" },
1714
+ valueType: unionType,
1715
+ },
1716
+ initializer: {
1717
+ kind: "identifier",
1718
+ name: "input",
1719
+ inferredType: {
1720
+ kind: "dictionaryType",
1721
+ keyType: { kind: "primitiveType", name: "string" },
1722
+ valueType: unionType,
1723
+ },
1724
+ },
1725
+ },
1726
+ ],
1727
+ },
1728
+ ],
1729
+ exports: [],
1730
+ };
1731
+ const result = emitModule(module);
1732
+ expect(result).not.to.include("global::System.Linq.Enumerable.ToDictionary");
1733
+ });
1272
1734
  it("should lower symbol-key dictionary undefined checks to ContainsKey", () => {
1273
1735
  const dictType = {
1274
1736
  kind: "dictionaryType",