xbintsc 0.3.3 → 0.3.5
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/src/ast/expressions.d.ts +8 -0
- package/dist/src/binder/binder.js +1 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/binder/references.d.ts +1 -1
- package/dist/src/binder/references.js +34 -5
- package/dist/src/binder/references.js.map +1 -1
- package/dist/src/binder/types.d.ts +4 -0
- package/dist/src/binder/types.js.map +1 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.d.ts +9 -1
- package/dist/src/codegen/generator/calls/access.js +146 -13
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/closures.d.ts +1 -0
- package/dist/src/codegen/generator/calls/closures.js +25 -0
- package/dist/src/codegen/generator/calls/closures.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +12 -1
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/calls/literals.js +12 -4
- package/dist/src/codegen/generator/calls/literals.js.map +1 -1
- package/dist/src/codegen/generator/calls/super.js +4 -7
- package/dist/src/codegen/generator/calls/super.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +21 -2
- package/dist/src/codegen/generator/context.js +42 -5
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions/assignment.js +1 -3
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.d.ts +3 -1
- package/dist/src/codegen/generator/expressions/primary.js +99 -1
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.js +25 -15
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.d.ts +15 -0
- package/dist/src/codegen/generator/state.js +28 -1
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.d.ts +3 -1
- package/dist/src/codegen/generator/statements/control-flow.js +85 -0
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +15 -6
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/loops.d.ts +3 -0
- package/dist/src/codegen/generator/statements/loops.js +25 -11
- package/dist/src/codegen/generator/statements/loops.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +7 -0
- package/dist/src/codegen/generator/tables.js +41 -1
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +1 -1
- package/dist/src/driver/compiler.js +33 -1
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +4 -3
- package/dist/src/driver/modules.js +61 -4
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/driver/toolchain.js +1 -1
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/parser/declarations/classes.js +4 -0
- package/dist/src/parser/declarations/classes.js.map +1 -1
- package/dist/src/parser/declarations/type-members.js.map +1 -1
- package/dist/src/parser/expressions.d.ts +2 -1
- package/dist/src/parser/expressions.js +10 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/src/parser/helpers.js +2 -2
- package/dist/src/parser/helpers.js.map +1 -1
- package/dist/src/parser/literals.js +79 -6
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/src/parser/modules.js.map +1 -1
- package/dist/src/parser/parser.js +0 -1
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/statements.d.ts +1 -0
- package/dist/src/parser/statements.js +13 -1
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/src/parser/types.js +2 -2
- package/dist/src/parser/types.js.map +1 -1
- package/dist/tests/ast/factory.test.d.ts +1 -0
- package/dist/tests/ast/factory.test.js +35 -0
- package/dist/tests/ast/factory.test.js.map +1 -0
- package/dist/tests/ast/visitor.test.d.ts +1 -0
- package/dist/tests/ast/visitor.test.js +53 -0
- package/dist/tests/ast/visitor.test.js.map +1 -0
- package/dist/tests/cli/main.test.js +109 -9
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/closures.test.d.ts +1 -0
- package/dist/tests/codegen/closures.test.js +32 -0
- package/dist/tests/codegen/closures.test.js.map +1 -0
- package/dist/tests/codegen/expressions.test.d.ts +1 -0
- package/dist/tests/codegen/expressions.test.js +149 -0
- package/dist/tests/codegen/expressions.test.js.map +1 -0
- package/dist/tests/codegen/llvm.test.js +14 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/codegen/values.test.d.ts +1 -0
- package/dist/tests/codegen/values.test.js +48 -0
- package/dist/tests/codegen/values.test.js.map +1 -0
- package/dist/tests/diagnostics/diagnostic.test.d.ts +1 -0
- package/dist/tests/diagnostics/diagnostic.test.js +117 -0
- package/dist/tests/diagnostics/diagnostic.test.js.map +1 -0
- package/dist/tests/diagnostics/source.test.d.ts +1 -0
- package/dist/tests/diagnostics/source.test.js +58 -0
- package/dist/tests/diagnostics/source.test.js.map +1 -0
- package/dist/tests/driver/modules.test.d.ts +1 -0
- package/dist/tests/driver/modules.test.js +140 -0
- package/dist/tests/driver/modules.test.js.map +1 -0
- package/dist/tests/driver/paths.test.js +17 -1
- package/dist/tests/driver/paths.test.js.map +1 -1
- package/dist/tests/driver/runtime-lib.test.d.ts +1 -0
- package/dist/tests/driver/runtime-lib.test.js +19 -0
- package/dist/tests/driver/runtime-lib.test.js.map +1 -0
- package/dist/tests/driver/toolchain-provider.test.js +91 -0
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +11 -1
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +14 -1
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +465 -2
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/differential.test.d.ts +7 -0
- package/dist/tests/e2e/differential.test.js +581 -0
- package/dist/tests/e2e/differential.test.js.map +1 -0
- package/dist/tests/e2e/harness.d.ts +21 -0
- package/dist/tests/e2e/harness.js +26 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/extensions/node.test.d.ts +1 -0
- package/dist/tests/extensions/node.test.js +32 -0
- package/dist/tests/extensions/node.test.js.map +1 -0
- package/dist/tests/lexer/identifiers.test.d.ts +1 -0
- package/dist/tests/lexer/identifiers.test.js +13 -0
- package/dist/tests/lexer/identifiers.test.js.map +1 -0
- package/dist/tests/lexer/numbers.test.d.ts +1 -0
- package/dist/tests/lexer/numbers.test.js +23 -0
- package/dist/tests/lexer/numbers.test.js.map +1 -0
- package/dist/tests/lexer/strings.test.d.ts +1 -0
- package/dist/tests/lexer/strings.test.js +72 -0
- package/dist/tests/lexer/strings.test.js.map +1 -0
- package/dist/tests/parser/declarations.test.d.ts +1 -0
- package/dist/tests/parser/declarations.test.js +70 -0
- package/dist/tests/parser/declarations.test.js.map +1 -0
- package/dist/tests/parser/expressions.test.d.ts +1 -0
- package/dist/tests/parser/expressions.test.js +47 -0
- package/dist/tests/parser/expressions.test.js.map +1 -0
- package/dist/tests/parser/helpers.test.d.ts +1 -0
- package/dist/tests/parser/helpers.test.js +58 -0
- package/dist/tests/parser/helpers.test.js.map +1 -0
- package/dist/tests/parser/literals.test.d.ts +1 -0
- package/dist/tests/parser/literals.test.js +70 -0
- package/dist/tests/parser/literals.test.js.map +1 -0
- package/dist/tests/parser/module-syntax.test.d.ts +1 -0
- package/dist/tests/parser/module-syntax.test.js +91 -0
- package/dist/tests/parser/module-syntax.test.js.map +1 -0
- package/dist/tests/parser/statements.test.d.ts +1 -0
- package/dist/tests/parser/statements.test.js +85 -0
- package/dist/tests/parser/statements.test.js.map +1 -0
- package/dist/tests/parser/type-members.test.d.ts +1 -0
- package/dist/tests/parser/type-members.test.js +86 -0
- package/dist/tests/parser/type-members.test.js.map +1 -0
- package/dist/tests/parser/types.test.d.ts +1 -0
- package/dist/tests/parser/types.test.js +143 -0
- package/dist/tests/parser/types.test.js.map +1 -0
- package/package.json +6 -1
- package/runtime/ext_node/fs/fs_ops.c +36 -1
- package/runtime/rt.h +17 -0
- package/runtime/rt_internal.h +88 -2
- package/runtime/xt_builtins.c +174 -6
- package/runtime/xt_containers.c +234 -42
- package/runtime/xt_generator.c +304 -0
- package/runtime/xt_io.c +170 -30
- package/runtime/xt_promise.c +6 -1
- package/runtime/xt_regex.h +105 -35
- package/runtime/xt_stdlib/dispatch.inc +255 -4
- package/runtime/xt_stdlib/strings.inc +17 -13
- package/runtime/xt_stdlib2/arrays.inc +126 -19
- package/runtime/xt_stdlib2/date.inc +9 -2
- package/runtime/xt_stdlib2/error.inc +162 -12
- package/runtime/xt_stdlib2/helpers.inc +4 -0
- package/runtime/xt_stdlib2/json.inc +19 -6
- package/runtime/xt_stdlib2/number-string-statics.inc +1 -1
- package/runtime/xt_stdlib2/numbers.inc +4 -5
- package/runtime/xt_stdlib2/objects.inc +29 -0
- package/runtime/xt_stdlib2/regexp-match.inc +293 -51
- package/runtime/xt_stdlib2/set.inc +58 -2
- package/runtime/xt_stdlib2/strings.inc +38 -0
- package/runtime/xt_symbol.c +149 -0
- package/runtime/xt_values.c +221 -15
- package/scripts/build-runtime.ts +1 -1
- package/src/ast/expressions.ts +8 -0
- package/src/binder/binder.ts +1 -1
- package/src/binder/references.ts +47 -4
- package/src/binder/types.ts +4 -0
- package/src/cli/main.ts +1 -2
- package/src/codegen/generator/calls/access.ts +158 -13
- package/src/codegen/generator/calls/closures.ts +24 -0
- package/src/codegen/generator/calls/invocation.ts +11 -1
- package/src/codegen/generator/calls/literals.ts +10 -4
- package/src/codegen/generator/calls/super.ts +6 -9
- package/src/codegen/generator/context.ts +44 -7
- package/src/codegen/generator/expressions/assignment.ts +1 -3
- package/src/codegen/generator/expressions/primary.ts +108 -2
- package/src/codegen/generator/generator.ts +0 -1
- package/src/codegen/generator/module.ts +24 -18
- package/src/codegen/generator/state.ts +44 -1
- package/src/codegen/generator/statements/control-flow.ts +86 -0
- package/src/codegen/generator/statements/dispatch.ts +18 -6
- package/src/codegen/generator/statements/loops.ts +29 -11
- package/src/codegen/generator/tables.ts +44 -1
- package/src/driver/compiler.ts +32 -2
- package/src/driver/modules.ts +64 -4
- package/src/driver/toolchain.ts +1 -1
- package/src/lexer/scanner.ts +0 -1
- package/src/parser/declarations/classes.ts +4 -0
- package/src/parser/declarations/type-members.ts +0 -2
- package/src/parser/expressions.ts +13 -1
- package/src/parser/helpers.ts +2 -2
- package/src/parser/literals.ts +82 -7
- package/src/parser/modules.ts +0 -1
- package/src/parser/parser.ts +0 -1
- package/src/parser/statements.ts +14 -0
- package/src/parser/types.ts +2 -2
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Differential tests: compile a program with xbintsc, run it, and compare the
|
|
3
|
+
* output byte-for-byte with the same program executed by Node. Every program
|
|
4
|
+
* prints canonical `JSON.stringify` lines so console formatting never masks a
|
|
5
|
+
* real semantic difference.
|
|
6
|
+
*/
|
|
7
|
+
import { it } from "vitest";
|
|
8
|
+
import { describeE2E } from "./harness.js";
|
|
9
|
+
describeE2E("differential vs Node", (harness) => {
|
|
10
|
+
/** Register one differential case from a complete program source. */
|
|
11
|
+
const diff = (name, source) => {
|
|
12
|
+
it(name, () => {
|
|
13
|
+
harness.expectSameOutputAsNode(source, { name: `diff_${name.replace(/\W+/g, "_")}` });
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
const printAll = (values) => `const values: unknown[] = [\n${values.map((v) => ` ${v},`).join("\n")}\n];\n` +
|
|
17
|
+
`for (const v of values) console.log(JSON.stringify(v));\n`;
|
|
18
|
+
diff("number formatting and coercion", printAll([
|
|
19
|
+
"0",
|
|
20
|
+
"-0",
|
|
21
|
+
"1.5",
|
|
22
|
+
"0.1",
|
|
23
|
+
"1 / 3",
|
|
24
|
+
"1e21",
|
|
25
|
+
"1e-7",
|
|
26
|
+
"1e-6",
|
|
27
|
+
"123456789012345680000",
|
|
28
|
+
"Number.MAX_SAFE_INTEGER",
|
|
29
|
+
"Number.MIN_SAFE_INTEGER",
|
|
30
|
+
"Number.EPSILON",
|
|
31
|
+
"Infinity",
|
|
32
|
+
"-Infinity",
|
|
33
|
+
"NaN",
|
|
34
|
+
"(1234.5678).toFixed(2)",
|
|
35
|
+
"(0.000001).toFixed(7)",
|
|
36
|
+
"(1e21).toFixed(2)",
|
|
37
|
+
"(2.5).toFixed(0)",
|
|
38
|
+
"(-2.5).toFixed(0)",
|
|
39
|
+
"(123.456).toPrecision(5)",
|
|
40
|
+
"(123.4).toPrecision(10)",
|
|
41
|
+
"(0.0001234).toPrecision(2)",
|
|
42
|
+
"(12345).toExponential()",
|
|
43
|
+
"(12345).toExponential(2)",
|
|
44
|
+
"(0.000123).toExponential(3)",
|
|
45
|
+
"(255).toString(16)",
|
|
46
|
+
"(255).toString(2)",
|
|
47
|
+
"String(42)",
|
|
48
|
+
"String(1e21)",
|
|
49
|
+
"String(1e-7)",
|
|
50
|
+
]));
|
|
51
|
+
diff("number parsing and predicates", printAll([
|
|
52
|
+
'Number(" 42 ")',
|
|
53
|
+
'Number("")',
|
|
54
|
+
'Number("0x1f")',
|
|
55
|
+
'Number("0b101")',
|
|
56
|
+
'Number("0o17")',
|
|
57
|
+
'Number("abc")',
|
|
58
|
+
'parseInt("42px")',
|
|
59
|
+
'parseFloat("3.14abc")',
|
|
60
|
+
"isNaN(NaN)",
|
|
61
|
+
"isFinite(3)",
|
|
62
|
+
"Number.isInteger(3)",
|
|
63
|
+
"Number.isInteger(3.5)",
|
|
64
|
+
"Number.isNaN(NaN)",
|
|
65
|
+
"Number.isFinite(Infinity)",
|
|
66
|
+
"Number.isSafeInteger(2 ** 53)",
|
|
67
|
+
]));
|
|
68
|
+
diff("abstract equality and coercion", printAll([
|
|
69
|
+
'1 == "1"',
|
|
70
|
+
'1 === "1"',
|
|
71
|
+
"0 == false",
|
|
72
|
+
'"" == false',
|
|
73
|
+
"null == undefined",
|
|
74
|
+
"null === undefined",
|
|
75
|
+
"[] == false",
|
|
76
|
+
"[] == 0",
|
|
77
|
+
"[1] == 1",
|
|
78
|
+
'[1, 2] == "1,2"',
|
|
79
|
+
'{} == "[object Object]"',
|
|
80
|
+
"NaN == NaN",
|
|
81
|
+
'"1" == true',
|
|
82
|
+
'"0" == false',
|
|
83
|
+
]));
|
|
84
|
+
diff("arithmetic and relational operators", printAll([
|
|
85
|
+
"[] + []",
|
|
86
|
+
"[1] + [2]",
|
|
87
|
+
"[1, 2] + [3]",
|
|
88
|
+
'{} + ""',
|
|
89
|
+
'1 + "2"',
|
|
90
|
+
'"3" - 1',
|
|
91
|
+
'"3" * "2"',
|
|
92
|
+
"true + 1",
|
|
93
|
+
"null + 1",
|
|
94
|
+
"undefined + 1",
|
|
95
|
+
'+ "42"',
|
|
96
|
+
'+ "0x1f"',
|
|
97
|
+
'+ ""',
|
|
98
|
+
'+ " 12 "',
|
|
99
|
+
'+ "abc"',
|
|
100
|
+
"[2] < 3",
|
|
101
|
+
'[1, 2] < "1,3"',
|
|
102
|
+
'"10" < "9"',
|
|
103
|
+
"10 < 9",
|
|
104
|
+
"2 ** 10",
|
|
105
|
+
"7 % 3",
|
|
106
|
+
"-7 % 3",
|
|
107
|
+
"1 / 0",
|
|
108
|
+
"0 / 0",
|
|
109
|
+
]));
|
|
110
|
+
diff("logical operators, bitwise and typeof", printAll([
|
|
111
|
+
'0 || "x"',
|
|
112
|
+
'"" || "y"',
|
|
113
|
+
'null ?? "z"',
|
|
114
|
+
'undefined ?? "w"',
|
|
115
|
+
"0 ?? 5",
|
|
116
|
+
"5 & 3",
|
|
117
|
+
"5 | 2",
|
|
118
|
+
"5 ^ 1",
|
|
119
|
+
"~5",
|
|
120
|
+
"1 << 4",
|
|
121
|
+
"-8 >> 1",
|
|
122
|
+
"-8 >>> 1",
|
|
123
|
+
"typeof 1",
|
|
124
|
+
'typeof "a"',
|
|
125
|
+
"typeof true",
|
|
126
|
+
"typeof undefined",
|
|
127
|
+
"typeof null",
|
|
128
|
+
"typeof {}",
|
|
129
|
+
"typeof []",
|
|
130
|
+
"typeof (() => 1)",
|
|
131
|
+
]));
|
|
132
|
+
diff("string methods", printAll([
|
|
133
|
+
'"Hello, World".length',
|
|
134
|
+
'"Hello, World".toUpperCase()',
|
|
135
|
+
'"Hello, World".toLowerCase()',
|
|
136
|
+
'"Hello, World".indexOf("o")',
|
|
137
|
+
'"Hello, World".lastIndexOf("o")',
|
|
138
|
+
'"Hello, World".includes("World")',
|
|
139
|
+
'"Hello, World".slice(7)',
|
|
140
|
+
'"Hello, World".slice(-5)',
|
|
141
|
+
'"Hello, World".substring(0, 5)',
|
|
142
|
+
'"Hello, World".substr(7, 3)',
|
|
143
|
+
'"a, b, c".split(", ")',
|
|
144
|
+
'"Hello".replace("l", "L")',
|
|
145
|
+
'"Hello".replaceAll("l", "L")',
|
|
146
|
+
'"ab".repeat(3)',
|
|
147
|
+
'"Hello".charAt(1)',
|
|
148
|
+
'"Hello".charCodeAt(0)',
|
|
149
|
+
'" pad ".trim()',
|
|
150
|
+
'"ab".padStart(5, "-")',
|
|
151
|
+
'"ab".padEnd(5, "-")',
|
|
152
|
+
'"Hello".concat("!" )',
|
|
153
|
+
'"a1b2c3".match(/\\d/g)',
|
|
154
|
+
'"a1b2c3".replace(/\\d/g, "#")',
|
|
155
|
+
'"abc".at(-1)',
|
|
156
|
+
'"Hello".localeCompare("World")',
|
|
157
|
+
]));
|
|
158
|
+
diff("array methods", printAll([
|
|
159
|
+
"[3, 1, 2].sort()",
|
|
160
|
+
'["banana", "apple", "cherry"].sort()',
|
|
161
|
+
"[10, 9, 100].sort()",
|
|
162
|
+
"[3, 1, 2].sort((a, b) => a - b)",
|
|
163
|
+
"[1, 2, 3].map((x) => x * 2)",
|
|
164
|
+
"[1, 2, 3, 4].filter((x) => x % 2 === 0)",
|
|
165
|
+
"[1, 2, 3].reduce((a, b) => a + b, 0)",
|
|
166
|
+
"[1, 2, 3].reverse()",
|
|
167
|
+
'[1, 2, 3].join("-")',
|
|
168
|
+
"[1, [2, [3, [4]]]].flat(2)",
|
|
169
|
+
"[1, 2, 3].flatMap((x) => [x, x])",
|
|
170
|
+
"[1, 2, 3, 4].slice(1, 3)",
|
|
171
|
+
"[1, 2, 3].concat([4, 5])",
|
|
172
|
+
"[1, 2, 3].indexOf(2)",
|
|
173
|
+
"[1, 2, 3].includes(4)",
|
|
174
|
+
"[5, 3, 8].find((x) => x > 4)",
|
|
175
|
+
"[1, 2, 3].some((x) => x > 2)",
|
|
176
|
+
"[1, 2, 3].every((x) => x > 0)",
|
|
177
|
+
"Array.isArray([])",
|
|
178
|
+
"Array.from([1, 2, 3], (x) => x + 1)",
|
|
179
|
+
"Array.of(1, 2, 3)",
|
|
180
|
+
"[1, 2, 3].fill(0, 1)",
|
|
181
|
+
"[1, 2, 3, 4].copyWithin(0, 2)",
|
|
182
|
+
"[1, 2, 3].at(-1)",
|
|
183
|
+
"[1, 2, 3].splice(1, 1)",
|
|
184
|
+
]));
|
|
185
|
+
diff("objects, key order and JSON", `const o = { b: 2, a: 1, "10": "ten", "2": "two", c: 3 };
|
|
186
|
+
${printAll([
|
|
187
|
+
"Object.keys(o)",
|
|
188
|
+
"Object.values(o)",
|
|
189
|
+
"Object.entries(o)",
|
|
190
|
+
"JSON.stringify(o)",
|
|
191
|
+
"JSON.stringify({ x: undefined, y: null, z: () => 1 })",
|
|
192
|
+
"JSON.stringify([undefined, null, () => 1])",
|
|
193
|
+
"JSON.stringify({ a: { b: [1, 2, { c: 3 }] } })",
|
|
194
|
+
'JSON.stringify("hi")',
|
|
195
|
+
"JSON.stringify(5)",
|
|
196
|
+
"JSON.stringify(true)",
|
|
197
|
+
"JSON.stringify(null)",
|
|
198
|
+
"JSON.stringify(NaN)",
|
|
199
|
+
"JSON.stringify(Infinity)",
|
|
200
|
+
"JSON.stringify(undefined)",
|
|
201
|
+
"Object.assign({}, o, { d: 4 })",
|
|
202
|
+
"{ ...o, e: 5 }",
|
|
203
|
+
'"a" in o',
|
|
204
|
+
'o.hasOwnProperty("a")',
|
|
205
|
+
])}`);
|
|
206
|
+
diff("control flow, closures and classes", `function fib(n: number): number { return n < 2 ? n : fib(n - 1) + fib(n - 2); }
|
|
207
|
+
function classify(x: number): string {
|
|
208
|
+
if (x < 0) return "neg";
|
|
209
|
+
else if (x === 0) return "zero";
|
|
210
|
+
return "pos";
|
|
211
|
+
}
|
|
212
|
+
function sum(...xs: number[]): number { return xs.reduce((a, b) => a + b, 0); }
|
|
213
|
+
function defaults(a: number, b = 10, c = a + b): number { return a + b + c; }
|
|
214
|
+
function safeDiv(a: number, b: number): string {
|
|
215
|
+
try {
|
|
216
|
+
if (b === 0) throw new Error("divide by zero");
|
|
217
|
+
return String(a / b);
|
|
218
|
+
} catch (err) {
|
|
219
|
+
return "caught";
|
|
220
|
+
} finally {
|
|
221
|
+
// always runs
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
class Animal {
|
|
225
|
+
name: string;
|
|
226
|
+
constructor(name: string) { this.name = name; }
|
|
227
|
+
speak(): string { return this.name + " makes a sound"; }
|
|
228
|
+
static create(n: string): Animal { return new Animal(n); }
|
|
229
|
+
}
|
|
230
|
+
class Dog extends Animal {
|
|
231
|
+
constructor(name: string) { super(name); }
|
|
232
|
+
speak(): string { return this.name + " barks"; }
|
|
233
|
+
}
|
|
234
|
+
const counter = () => { let c = 0; return () => ++c; };
|
|
235
|
+
const mk = counter();
|
|
236
|
+
const results: unknown[] = [
|
|
237
|
+
fib(10), classify(-5), classify(0), classify(5), sum(1, 2, 3),
|
|
238
|
+
defaults(1), defaults(1, 2), defaults(1, 2, 3),
|
|
239
|
+
mk(), mk(), mk(),
|
|
240
|
+
new Animal("Cat").speak(), new Dog("Rex").speak(), Animal.create("Bird").speak(),
|
|
241
|
+
Dog.prototype instanceof Animal,
|
|
242
|
+
safeDiv(10, 2), safeDiv(1, 0),
|
|
243
|
+
(() => { let s = 0; for (let i = 0; i < 5; i++) s += i; return s; })(),
|
|
244
|
+
(() => { let s = ""; for (const ch of ["a", "b", "c"]) s += ch; return s; })(),
|
|
245
|
+
(() => { let i = 0, s = 0; while (i < 3) { s += i; i++; } return s; })(),
|
|
246
|
+
(() => { let i = 0; do { i++; } while (i < 3); return i; })(),
|
|
247
|
+
(() => { switch (2) { case 1: return "one"; case 2: return "two"; default: return "other"; } })(),
|
|
248
|
+
];
|
|
249
|
+
for (const v of results) console.log(JSON.stringify(v));`);
|
|
250
|
+
diff("Math, collections and dates", printAll([
|
|
251
|
+
"Math.max(1, 2, 3)",
|
|
252
|
+
"Math.min()",
|
|
253
|
+
"Math.abs(-3)",
|
|
254
|
+
"Math.floor(1.9)",
|
|
255
|
+
"Math.ceil(1.1)",
|
|
256
|
+
"Math.round(2.5)",
|
|
257
|
+
"Math.round(-2.5)",
|
|
258
|
+
"Math.trunc(-1.9)",
|
|
259
|
+
"Math.sign(-5)",
|
|
260
|
+
"Math.sqrt(16)",
|
|
261
|
+
"Math.pow(2, 8)",
|
|
262
|
+
"Math.PI",
|
|
263
|
+
"new Date(0).toISOString()",
|
|
264
|
+
"new Date(1000).getTime()",
|
|
265
|
+
"new Date(2020, 0, 1).getUTCFullYear()",
|
|
266
|
+
"new Date(Date.UTC(2020, 5, 15)).toISOString()",
|
|
267
|
+
"[...new Set([1, 2, 2, 3])]",
|
|
268
|
+
"[...new Map([[\"a\", 1], [\"b\", 2]]).keys()]",
|
|
269
|
+
]));
|
|
270
|
+
diff("regexp capture groups and string splitting", printAll([
|
|
271
|
+
'"12-34".match(/(\\d+)-(\\d+)/)',
|
|
272
|
+
'"a1b2c3".split(/(\\d)/)',
|
|
273
|
+
'"a1b2c3".split(/\\d/)',
|
|
274
|
+
'"2020-01-02".replace(/(\\d+)-(\\d+)-(\\d+)/, "$3/$2/$1")',
|
|
275
|
+
'"a1b2".replace(/(\\d)/g, "[$1]")',
|
|
276
|
+
'"a.b.c".search(/\\./)',
|
|
277
|
+
'"abc".split(/(?:)/)',
|
|
278
|
+
'"hello world".match(/(\\w+) (\\w+)/)',
|
|
279
|
+
'"aaa".split("a", 2)',
|
|
280
|
+
'"a,b;c".split(/[,;]/)',
|
|
281
|
+
]));
|
|
282
|
+
diff("immutable array methods and Object statics", printAll([
|
|
283
|
+
"[3, 1, 2].toSorted((x, y) => x - y)",
|
|
284
|
+
"[3, 1, 2].toReversed()",
|
|
285
|
+
"[3, 1, 2].toSpliced(1, 1, 9, 9)",
|
|
286
|
+
"[3, 1, 2].with(1, 9)",
|
|
287
|
+
"[3, 1, 2].with(-1, 9)",
|
|
288
|
+
"Object.getOwnPropertyNames({ b: 1, a: 2 })",
|
|
289
|
+
"Object.getOwnPropertyNames([1, 2])",
|
|
290
|
+
"Object.groupBy([1, 2, 3, 4], (n) => (n % 2 === 0 ? \"even\" : \"odd\"))",
|
|
291
|
+
"Array.from({ length: 3 }, (_v, i) => i * 2)",
|
|
292
|
+
]));
|
|
293
|
+
diff("tagged templates and String.raw", `function tag(strings: TemplateStringsArray, ...values: unknown[]): string {
|
|
294
|
+
let out = strings[0];
|
|
295
|
+
for (let i = 0; i < values.length; i++) out += "[" + String(values[i]) + "]" + strings[i + 1];
|
|
296
|
+
return out;
|
|
297
|
+
}
|
|
298
|
+
const obj = {
|
|
299
|
+
prefix: "<",
|
|
300
|
+
tag(strings: TemplateStringsArray, ...values: unknown[]): string {
|
|
301
|
+
return this.prefix + strings.join("|") + "/" + JSON.stringify(strings.raw) + "/" + values.join(",");
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
const name = "world";
|
|
305
|
+
${printAll([
|
|
306
|
+
'tag`hello ${name} and ${1 + 1}!`',
|
|
307
|
+
'tag`no substitutions`',
|
|
308
|
+
'obj.tag`a\\n${name}b${2}`',
|
|
309
|
+
'String.raw`a\\nb\\t${name}c`',
|
|
310
|
+
'String.raw`plain`',
|
|
311
|
+
'tag`${name}`',
|
|
312
|
+
])}`);
|
|
313
|
+
diff("Function.prototype.call, apply and bind", `function add(a: number, b: number): number { return a + b; }
|
|
314
|
+
const obj = {
|
|
315
|
+
base: 10,
|
|
316
|
+
sum(a: number, b: number): number { return this.base + a + b; },
|
|
317
|
+
};
|
|
318
|
+
class Greeter {
|
|
319
|
+
prefix = "hi";
|
|
320
|
+
greet(name: string): string { return this.prefix + " " + name; }
|
|
321
|
+
}
|
|
322
|
+
const g = new Greeter();
|
|
323
|
+
const bound = obj.sum.bind(obj, 1);
|
|
324
|
+
const bound2 = add.bind(null, 10);
|
|
325
|
+
const mk = (x: number) => x * 2;
|
|
326
|
+
${printAll([
|
|
327
|
+
"add.call(null, 1, 2)",
|
|
328
|
+
"obj.sum.call(obj, 1, 2)",
|
|
329
|
+
"obj.sum.apply(obj, [3, 4])",
|
|
330
|
+
"add.apply(null, [5, 6])",
|
|
331
|
+
"bound(2)",
|
|
332
|
+
"bound2(5)",
|
|
333
|
+
'g.greet.call(g, "ada")',
|
|
334
|
+
'g.greet.apply(g, ["bob"])',
|
|
335
|
+
'g.greet.bind(g)("cy")',
|
|
336
|
+
"mk.call(null, 21)",
|
|
337
|
+
"mk.apply(null, [3])",
|
|
338
|
+
])}`);
|
|
339
|
+
diff("first-class built-in method values", `const array = [1, 2, 3];
|
|
340
|
+
const text = "abc";
|
|
341
|
+
const fn = function (x: number): number { return x; };
|
|
342
|
+
const date = new Date(0);
|
|
343
|
+
const map = new Map<string, number>();
|
|
344
|
+
map.set("a", 1);
|
|
345
|
+
const set = new Set<number>([1, 2]);
|
|
346
|
+
const regexp = /a(b)c/;
|
|
347
|
+
const object = { base: 10, add(n: number): number { return this.base + n; } };
|
|
348
|
+
type Holder = { base: number; go?: (n: number) => number };
|
|
349
|
+
const holder: Holder = { base: 10, go(n: number): number { return this.base + n; } };
|
|
350
|
+
const empty: { go?: (n: number) => number } = {};
|
|
351
|
+
const results: unknown[] = [
|
|
352
|
+
typeof array.map,
|
|
353
|
+
typeof array.push,
|
|
354
|
+
typeof text.slice,
|
|
355
|
+
typeof fn.call,
|
|
356
|
+
typeof date.getTime,
|
|
357
|
+
typeof map.get,
|
|
358
|
+
typeof set.add,
|
|
359
|
+
typeof regexp.test,
|
|
360
|
+
array.map.name,
|
|
361
|
+
array.map.length,
|
|
362
|
+
text.slice.length,
|
|
363
|
+
array.map(function (x: number): number { return x * 2; }),
|
|
364
|
+
array.map?.((x: number): number => x + 1),
|
|
365
|
+
text.split?.(""),
|
|
366
|
+
object.add(5),
|
|
367
|
+
holder.go?.(3),
|
|
368
|
+
empty.go?.(3),
|
|
369
|
+
];
|
|
370
|
+
for (const value of results) console.log(JSON.stringify(value));
|
|
371
|
+
try {
|
|
372
|
+
const detached = array.map;
|
|
373
|
+
(detached as unknown as (cb: (x: number) => number) => number[])((x: number): number => x);
|
|
374
|
+
console.log(JSON.stringify("no throw"));
|
|
375
|
+
} catch (caught) {
|
|
376
|
+
console.log(JSON.stringify("threw"));
|
|
377
|
+
}
|
|
378
|
+
`);
|
|
379
|
+
diff("object-literal getters and setters", `const obj = {
|
|
380
|
+
_x: 0,
|
|
381
|
+
get x(): number { return this._x; },
|
|
382
|
+
set x(v: number) { this._x = v * 2; },
|
|
383
|
+
get double(): number { return this._x * 2; },
|
|
384
|
+
method(a: number, b: number): number { return a + b; },
|
|
385
|
+
};
|
|
386
|
+
obj.x = 5;
|
|
387
|
+
const base = 7;
|
|
388
|
+
const o2 = { base, get next() { return base + 1; } };
|
|
389
|
+
const o3 = { get ["dyn" + "amic"]() { return 42; } };
|
|
390
|
+
${printAll([
|
|
391
|
+
"obj.x",
|
|
392
|
+
"obj.double",
|
|
393
|
+
"obj.method(2, 3)",
|
|
394
|
+
"Object.keys(obj)",
|
|
395
|
+
"o2.base",
|
|
396
|
+
"o2.next",
|
|
397
|
+
"o3.dynamic",
|
|
398
|
+
])}`);
|
|
399
|
+
diff("Error family and AggregateError", `
|
|
400
|
+
const e = new Error("boom");
|
|
401
|
+
class MyErr extends Error {
|
|
402
|
+
constructor(m: string) { super(m); this.name = "MyErr"; }
|
|
403
|
+
}
|
|
404
|
+
class CustomRange extends RangeError {}
|
|
405
|
+
const m = new MyErr("m");
|
|
406
|
+
const r = new CustomRange("too big");
|
|
407
|
+
const agg = new AggregateError([1, 2], "all failed");
|
|
408
|
+
${printAll([
|
|
409
|
+
"typeof Error",
|
|
410
|
+
"typeof TypeError",
|
|
411
|
+
"typeof AggregateError",
|
|
412
|
+
"e instanceof Error",
|
|
413
|
+
"new TypeError('b') instanceof TypeError",
|
|
414
|
+
"new TypeError('b') instanceof Error",
|
|
415
|
+
"new RangeError('c') instanceof TypeError",
|
|
416
|
+
"String(e)",
|
|
417
|
+
"e.toString()",
|
|
418
|
+
"e.name",
|
|
419
|
+
"e.message",
|
|
420
|
+
"m.name",
|
|
421
|
+
"m.message",
|
|
422
|
+
"m instanceof MyErr",
|
|
423
|
+
"m instanceof Error",
|
|
424
|
+
"m instanceof TypeError",
|
|
425
|
+
"r.name",
|
|
426
|
+
"r.message",
|
|
427
|
+
"r instanceof RangeError",
|
|
428
|
+
"r instanceof Error",
|
|
429
|
+
"agg.name",
|
|
430
|
+
"agg.message",
|
|
431
|
+
"agg.errors",
|
|
432
|
+
"agg instanceof AggregateError",
|
|
433
|
+
"agg instanceof Error",
|
|
434
|
+
"String(agg)",
|
|
435
|
+
"typeof e.stack",
|
|
436
|
+
])}`);
|
|
437
|
+
diff("Promise.any rejects with an AggregateError", `
|
|
438
|
+
Promise.any([Promise.reject("a"), Promise.reject("b"), Promise.resolve(42)])
|
|
439
|
+
.then((value) => console.log(JSON.stringify(["fulfilled", value])))
|
|
440
|
+
.catch((reason) => {
|
|
441
|
+
const err = reason as { name: string; message: string; errors: unknown[] };
|
|
442
|
+
console.log(JSON.stringify([err.name, err.message, err.errors]));
|
|
443
|
+
});
|
|
444
|
+
Promise.any([Promise.reject("x"), Promise.reject("y")]).catch((reason) => {
|
|
445
|
+
const err = reason as { name: string; errors: unknown[] };
|
|
446
|
+
console.log(JSON.stringify([err.name, err.errors, reason instanceof AggregateError]));
|
|
447
|
+
});
|
|
448
|
+
`);
|
|
449
|
+
diff("generators", `
|
|
450
|
+
function* nums() { yield 1; yield 2; yield 3; }
|
|
451
|
+
const g = nums();
|
|
452
|
+
console.log(JSON.stringify([typeof nums, g.next(), g.next(), g.next(), g.next()]));
|
|
453
|
+
|
|
454
|
+
function* counter(start: number) {
|
|
455
|
+
let x = start;
|
|
456
|
+
while (true) {
|
|
457
|
+
const step = yield x;
|
|
458
|
+
x = step === undefined ? x + 1 : step;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
const c = counter(10);
|
|
462
|
+
console.log(JSON.stringify([c.next().value, c.next().value, c.next(100).value, c.next().value]));
|
|
463
|
+
|
|
464
|
+
function* inner() { yield "a"; return "done"; }
|
|
465
|
+
function* outer() { const r = yield* inner(); yield r; yield* [1, 2]; }
|
|
466
|
+
console.log(JSON.stringify([...outer()]));
|
|
467
|
+
|
|
468
|
+
function* echo() { const a = yield "first"; const b = yield a; return b; }
|
|
469
|
+
const e = echo();
|
|
470
|
+
console.log(JSON.stringify([e.next(), e.next("A"), e.next("B")]));
|
|
471
|
+
|
|
472
|
+
function* guarded() {
|
|
473
|
+
try { yield "x"; } catch (err) { yield "caught:" + (err as Error).message; }
|
|
474
|
+
yield "end";
|
|
475
|
+
}
|
|
476
|
+
const t = guarded();
|
|
477
|
+
console.log(JSON.stringify([t.next(), t.throw(new Error("E")), t.next(), t.next()]));
|
|
478
|
+
|
|
479
|
+
function* naturals() { let i = 0; while (true) yield i++; }
|
|
480
|
+
const seen: number[] = [];
|
|
481
|
+
for (const v of naturals()) { if (v >= 3) break; seen.push(v); }
|
|
482
|
+
console.log(JSON.stringify(seen));
|
|
483
|
+
|
|
484
|
+
class Box {
|
|
485
|
+
private n = 0;
|
|
486
|
+
*gen(): Generator<number> { yield this.n++; yield this.n++; }
|
|
487
|
+
}
|
|
488
|
+
console.log(JSON.stringify([...new Box().gen()]));
|
|
489
|
+
|
|
490
|
+
const obj = {
|
|
491
|
+
base: 5,
|
|
492
|
+
*gen() { yield this.base; yield this.base * 2; },
|
|
493
|
+
};
|
|
494
|
+
console.log(JSON.stringify([...obj.gen(), Math.max(...obj.gen())]));
|
|
495
|
+
`);
|
|
496
|
+
it("implements symbols and the well-known symbol protocol", () => {
|
|
497
|
+
harness.expectSameOutputAsNode(`const a = Symbol("desc");
|
|
498
|
+
const b = Symbol("desc");
|
|
499
|
+
const lines: unknown[] = [
|
|
500
|
+
typeof Symbol,
|
|
501
|
+
typeof a,
|
|
502
|
+
a === a,
|
|
503
|
+
a === b,
|
|
504
|
+
a.toString(),
|
|
505
|
+
String(a),
|
|
506
|
+
a.description,
|
|
507
|
+
Symbol().description,
|
|
508
|
+
String(Symbol()),
|
|
509
|
+
String(Symbol.iterator),
|
|
510
|
+
Symbol.iterator === Symbol.iterator,
|
|
511
|
+
Symbol.for("k") === Symbol.for("k"),
|
|
512
|
+
Symbol.keyFor(Symbol.for("k")),
|
|
513
|
+
Symbol.keyFor(Symbol("k")),
|
|
514
|
+
];
|
|
515
|
+
for (const line of lines) console.log(JSON.stringify(line));
|
|
516
|
+
|
|
517
|
+
const holder: Record<string, unknown> = {};
|
|
518
|
+
(holder as any)[a] = 1;
|
|
519
|
+
(holder as any)[b] = 2;
|
|
520
|
+
holder.x = 3;
|
|
521
|
+
console.log(JSON.stringify(Object.keys(holder)));
|
|
522
|
+
console.log(JSON.stringify(Object.getOwnPropertySymbols(holder).length));
|
|
523
|
+
console.log(
|
|
524
|
+
JSON.stringify([(holder as any)[a], (holder as any)[b], (holder as any)[Symbol("desc")]]),
|
|
525
|
+
);
|
|
526
|
+
console.log(JSON.stringify(holder));
|
|
527
|
+
console.log(JSON.stringify(a));
|
|
528
|
+
console.log(JSON.stringify([a, 1]));
|
|
529
|
+
|
|
530
|
+
class Range {
|
|
531
|
+
start: number;
|
|
532
|
+
end: number;
|
|
533
|
+
constructor(start: number, end: number) {
|
|
534
|
+
this.start = start;
|
|
535
|
+
this.end = end;
|
|
536
|
+
}
|
|
537
|
+
[Symbol.iterator]() {
|
|
538
|
+
let i = this.start;
|
|
539
|
+
const end = this.end;
|
|
540
|
+
return {
|
|
541
|
+
next() {
|
|
542
|
+
return i < end ? { value: i++, done: false } : { value: undefined, done: true };
|
|
543
|
+
},
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
console.log(JSON.stringify([...new Range(1, 4)]));
|
|
548
|
+
const walked: number[] = [];
|
|
549
|
+
for (const x of new Range(10, 13)) walked.push(x);
|
|
550
|
+
console.log(JSON.stringify(walked));
|
|
551
|
+
|
|
552
|
+
function* delegates() { yield* new Range(5, 7); }
|
|
553
|
+
console.log(JSON.stringify([...delegates()]));
|
|
554
|
+
`, { name: "diff_symbol" });
|
|
555
|
+
});
|
|
556
|
+
it("binds namespace imports to every export", () => {
|
|
557
|
+
harness.expectSameOutputAsNode(`import * as util from "./util.ts";
|
|
558
|
+
import { add } from "./util.ts";
|
|
559
|
+
const values: unknown[] = [
|
|
560
|
+
util.greeting,
|
|
561
|
+
util.add(2, 3),
|
|
562
|
+
add(20, 22),
|
|
563
|
+
new util.default().value,
|
|
564
|
+
Object.keys(util).sort(),
|
|
565
|
+
typeof util.add,
|
|
566
|
+
];
|
|
567
|
+
for (const v of values) console.log(JSON.stringify(v));
|
|
568
|
+
`, {
|
|
569
|
+
name: "diff_namespace_import",
|
|
570
|
+
files: {
|
|
571
|
+
"util.ts": [
|
|
572
|
+
'export const greeting = "hello";',
|
|
573
|
+
"export function add(a: number, b: number): number { return a + b; }",
|
|
574
|
+
"export default class Thing { value = 42; }",
|
|
575
|
+
"export const ignored = 1;\n",
|
|
576
|
+
].join("\n"),
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
//# sourceMappingURL=differential.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"differential.test.js","sourceRoot":"","sources":["../../../tests/e2e/differential.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,12 +12,22 @@ export interface RunOptions {
|
|
|
12
12
|
force?: boolean;
|
|
13
13
|
/** Child-process timeout in milliseconds. */
|
|
14
14
|
timeout?: number;
|
|
15
|
+
/** Extra modules written next to the entry file, keyed by relative path. */
|
|
16
|
+
files?: Record<string, string>;
|
|
15
17
|
}
|
|
16
18
|
export interface RunResult {
|
|
17
19
|
stdout: string;
|
|
18
20
|
stderr: string;
|
|
19
21
|
status: number | null;
|
|
20
22
|
}
|
|
23
|
+
export interface NodeRunOptions {
|
|
24
|
+
/** Entry file base name; defaults to a random name. */
|
|
25
|
+
name?: string;
|
|
26
|
+
/** Child-process timeout in milliseconds. */
|
|
27
|
+
timeout?: number;
|
|
28
|
+
/** Extra modules written next to the entry file, keyed by relative path. */
|
|
29
|
+
files?: Record<string, string>;
|
|
30
|
+
}
|
|
21
31
|
export interface E2EHarness {
|
|
22
32
|
/** Scratch directory shared by the tests in the calling suite. */
|
|
23
33
|
readonly workdir: string;
|
|
@@ -25,6 +35,17 @@ export interface E2EHarness {
|
|
|
25
35
|
runProgram(source: string, options?: RunOptions): string;
|
|
26
36
|
/** Compile and run `source` without asserting the exit status. */
|
|
27
37
|
runProgramFull(source: string, options?: RunOptions): RunResult;
|
|
38
|
+
/**
|
|
39
|
+
* Run `source` under Node (through the `tsx` loader) and return its trimmed
|
|
40
|
+
* stdout. Differential cases should print canonical `JSON.stringify` output
|
|
41
|
+
* so console formatting never masks a real difference.
|
|
42
|
+
*/
|
|
43
|
+
runNodeProgram(source: string, options?: NodeRunOptions): string;
|
|
44
|
+
/**
|
|
45
|
+
* Compile `source` with xbintsc and run it under Node, asserting both produce
|
|
46
|
+
* byte-identical stdout. This is the core differential-testing primitive.
|
|
47
|
+
*/
|
|
48
|
+
expectSameOutputAsNode(source: string, options?: RunOptions): void;
|
|
28
49
|
}
|
|
29
50
|
/**
|
|
30
51
|
* Declare a suite of end-to-end compilation tests. The `define` callback runs
|
|
@@ -31,6 +31,9 @@ export function describeE2E(name, define, options = {}) {
|
|
|
31
31
|
});
|
|
32
32
|
const runProgramFull = (source, runOptions = {}) => {
|
|
33
33
|
const entry = join(workdir, `${runOptions.name ?? `program_${Math.random().toString(36).slice(2)}`}.ts`);
|
|
34
|
+
for (const [relative, contents] of Object.entries(runOptions.files ?? {})) {
|
|
35
|
+
writeFileSync(join(workdir, relative), contents);
|
|
36
|
+
}
|
|
34
37
|
writeFileSync(entry, source);
|
|
35
38
|
const extensions = runOptions.extensions ? createDefaultRegistry().register(nodeExtension) : undefined;
|
|
36
39
|
const result = build(entry, {
|
|
@@ -47,6 +50,19 @@ export function describeE2E(name, define, options = {}) {
|
|
|
47
50
|
});
|
|
48
51
|
return { stdout: executed.stdout ?? "", stderr: executed.stderr ?? "", status: executed.status };
|
|
49
52
|
};
|
|
53
|
+
const runNodeProgram = (source, runOptions = {}) => {
|
|
54
|
+
const entry = join(workdir, `${runOptions.name ?? `node_${Math.random().toString(36).slice(2)}`}.ts`);
|
|
55
|
+
for (const [relative, contents] of Object.entries(runOptions.files ?? {})) {
|
|
56
|
+
writeFileSync(join(workdir, relative), contents);
|
|
57
|
+
}
|
|
58
|
+
writeFileSync(entry, source);
|
|
59
|
+
const executed = spawnSync(process.execPath, ["--import", "tsx", entry], {
|
|
60
|
+
encoding: "utf8",
|
|
61
|
+
...(runOptions.timeout ? { timeout: runOptions.timeout } : {}),
|
|
62
|
+
});
|
|
63
|
+
expect(executed.status).toBe(0);
|
|
64
|
+
return (executed.stdout ?? "").trim();
|
|
65
|
+
};
|
|
50
66
|
const harness = {
|
|
51
67
|
get workdir() {
|
|
52
68
|
return workdir;
|
|
@@ -57,6 +73,16 @@ export function describeE2E(name, define, options = {}) {
|
|
|
57
73
|
return result.stdout.trim();
|
|
58
74
|
},
|
|
59
75
|
runProgramFull,
|
|
76
|
+
runNodeProgram,
|
|
77
|
+
expectSameOutputAsNode(source, runOptions = {}) {
|
|
78
|
+
const ours = harness.runProgram(source, runOptions);
|
|
79
|
+
const theirs = runNodeProgram(source, {
|
|
80
|
+
...(runOptions.name ? { name: runOptions.name } : {}),
|
|
81
|
+
...(runOptions.timeout ? { timeout: runOptions.timeout } : {}),
|
|
82
|
+
...(runOptions.files ? { files: runOptions.files } : {}),
|
|
83
|
+
});
|
|
84
|
+
expect(ours).toBe(theirs);
|
|
85
|
+
},
|
|
60
86
|
};
|
|
61
87
|
define(harness);
|
|
62
88
|
});
|