|
1 | 1 | import * as ts from "typescript"; |
2 | 2 | import * as util from "../util"; |
3 | 3 |
|
4 | | -describe("module import/export elision", () => { |
5 | | - const moduleDeclaration = ` |
6 | | - declare module "module" { |
7 | | - export type Type = string; |
8 | | - export declare const value: string; |
9 | | - } |
10 | | - `; |
11 | | - |
12 | | - const expectToElideImport: util.TapCallback = builder => { |
13 | | - builder.addExtraFile("module.d.ts", moduleDeclaration).setOptions({ module: ts.ModuleKind.CommonJS }); |
14 | | - expect(builder.getLuaExecutionResult()).not.toBeInstanceOf(util.ExecutionError); |
15 | | - }; |
16 | | - |
17 | | - test("should elide named type imports", () => { |
18 | | - util.testModule` |
19 | | - import { Type } from "module"; |
20 | | - const foo: Type = "bar"; |
21 | | - `.tap(expectToElideImport); |
22 | | - }); |
23 | | - |
24 | | - test("should elide named value imports used only as a type", () => { |
| 4 | +describe("export default", () => { |
| 5 | + test("literal", () => { |
25 | 6 | util.testModule` |
26 | | - import { value } from "module"; |
27 | | - const foo: typeof value = "bar"; |
28 | | - `.tap(expectToElideImport); |
29 | | - }); |
30 | | - |
31 | | - test("should elide namespace imports with unused values", () => { |
32 | | - util.testModule` |
33 | | - import * as module from "module"; |
34 | | - const foo: module.Type = "bar"; |
35 | | - `.tap(expectToElideImport); |
| 7 | + export default true; |
| 8 | + `.expectToEqual({ default: true }); |
36 | 9 | }); |
37 | 10 |
|
38 | | - test("should elide `import =` declarations", () => { |
| 11 | + test("class", () => { |
39 | 12 | util.testModule` |
40 | | - import module = require("module"); |
41 | | - const foo: module.Type = "bar"; |
42 | | - `.tap(expectToElideImport); |
| 13 | + export default class Default {} |
| 14 | + const d = new Default(); |
| 15 | + export const result = d.constructor.name; |
| 16 | + ` |
| 17 | + .setReturnExport("result") |
| 18 | + .expectToMatchJsResult(); |
43 | 19 | }); |
44 | 20 |
|
45 | | - test("should elide type exports", () => { |
| 21 | + test("function", () => { |
46 | 22 | util.testModule` |
47 | | - (globalThis as any).foo = true; |
48 | | - type foo = boolean; |
49 | | - export { foo }; |
50 | | - `.expectToEqual([]); |
| 23 | + export default function defaultFunction() { |
| 24 | + return true; |
| 25 | + } |
| 26 | + export const result = defaultFunction(); |
| 27 | + ` |
| 28 | + .setReturnExport("result") |
| 29 | + .expectToMatchJsResult(); |
51 | 30 | }); |
52 | 31 | }); |
53 | 32 |
|
54 | | -test.each(["ke-bab", "dollar$", "singlequote'", "hash#", "s p a c e", "ɥɣɎɌͼƛಠ", "_̀ः٠‿"])( |
55 | | - "Import module names with invalid lua identifier characters (%p)", |
56 | | - name => { |
57 | | - util.testBundle` |
58 | | - import { foo } from "./${name}"; |
59 | | - export { foo }; |
60 | | - ` |
61 | | - .addExtraFile(`${name}.ts`, "export const foo = true;") |
62 | | - .expectToEqual({ foo: true }) |
63 | | - .tap(builder => { |
64 | | - const identifier = builder.getMainLuaCodeChunk().match(/local (.+) = require\(/)?.[1]; |
65 | | - expect(identifier).toMatchSnapshot("local identifier"); |
66 | | - }); |
67 | | - } |
68 | | -); |
69 | | - |
70 | | -test.each(["export default value;", "export { value as default };"])("Export Default From (%p)", exportStatement => { |
| 33 | +test("export { value as default }", () => { |
71 | 34 | util.testBundle` |
72 | | - export { default } from "./module"; |
73 | | - ` |
74 | | - .addExtraFile( |
75 | | - "module.ts", |
76 | | - ` |
77 | | - export const value = true; |
78 | | - ${exportStatement}; |
79 | | - ` |
80 | | - ) |
81 | | - .expectToEqual({ default: true }); |
| 35 | + const value = true; |
| 36 | + export { value as default }; |
| 37 | + `.expectToEqual({ default: true }); |
82 | 38 | }); |
83 | 39 |
|
84 | | -test("Default Import and Export Expression", () => { |
| 40 | +test("export { default } from '...'", () => { |
85 | 41 | util.testBundle` |
86 | | - import defaultExport from "./module"; |
87 | | - export const value = defaultExport; |
| 42 | + export { default } from "./module"; |
88 | 43 | ` |
89 | | - .addExtraFile("module.ts", "export default 1 + 2 + 3;") |
90 | | - .expectToEqual({ value: 6 }); |
| 44 | + .addExtraFile("module.ts", "export default true;") |
| 45 | + .expectToEqual({ default: true }); |
91 | 46 | }); |
92 | 47 |
|
93 | | -test("Import and Export Assignment", () => { |
| 48 | +test("import = and export =", () => { |
94 | 49 | util.testBundle` |
95 | 50 | import m = require("./module"); |
96 | 51 | export const value = m; |
@@ -134,95 +89,142 @@ test("Mixed Exports, Default and Namespace Import", () => { |
134 | 89 | .expectToEqual({ value: 6 }); |
135 | 90 | }); |
136 | 91 |
|
137 | | -test("Export Default Function", () => { |
138 | | - util.testBundle` |
139 | | - import defaultExport from "./module"; |
140 | | - export const value = defaultExport(); |
141 | | - ` |
142 | | - .addExtraFile("module.ts", "export default function() { return true; }") |
143 | | - .expectToEqual({ value: true }); |
144 | | -}); |
145 | | - |
146 | | -const reassignmentTestCases = [ |
147 | | - "x = 1", |
148 | | - "x++", |
149 | | - "(x = 1)", |
150 | | - "[x] = [1]", |
151 | | - "[[x]] = [[1]]", |
152 | | - "({ x } = { x: 1 })", |
153 | | - "({ y: x } = { y: 1 })", |
154 | | - "({ x = 1 } = { x: undefined })", |
155 | | -]; |
156 | | - |
157 | | -test.each(reassignmentTestCases)("export specifier with reassignment afterwards (%p)", reassignment => { |
158 | | - util.testModule` |
159 | | - let x = 0; |
160 | | - export { x }; |
161 | | - ${reassignment}; |
162 | | - `.expectToMatchJsResult(); |
163 | | -}); |
| 92 | +describe("export live bindings", () => { |
| 93 | + const reassignmentTestCases = [ |
| 94 | + "x = 1", |
| 95 | + "x++", |
| 96 | + "(x = 1)", |
| 97 | + "[x] = [1]", |
| 98 | + "[[x]] = [[1]]", |
| 99 | + "({ x } = { x: 1 })", |
| 100 | + "({ y: x } = { y: 1 })", |
| 101 | + "({ x = 1 } = { x: undefined })", |
| 102 | + ]; |
| 103 | + |
| 104 | + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/926 |
| 105 | + test.each(reassignmentTestCases.filter(c => !c.includes(" } = { x: ")))("export variable (%p)", reassignment => { |
| 106 | + util.testModule` |
| 107 | + export let x = 0; |
| 108 | + ${reassignment}; |
| 109 | + `.expectToMatchJsResult(); |
| 110 | + }); |
164 | 111 |
|
165 | | -test.each(reassignmentTestCases)("export specifier fork (%p)", reassignment => { |
166 | | - util.testModule` |
167 | | - let x = 0; |
168 | | - export { x as a }; |
169 | | - export { x as b }; |
170 | | - ${reassignment}; |
171 | | - `.expectToMatchJsResult(); |
172 | | -}); |
| 112 | + test.each(reassignmentTestCases)("export variable as a binding (%p)", reassignment => { |
| 113 | + util.testModule` |
| 114 | + let x = 0; |
| 115 | + export { x }; |
| 116 | + ${reassignment}; |
| 117 | + `.expectToMatchJsResult(); |
| 118 | + }); |
173 | 119 |
|
174 | | -test("does not export shadowed identifiers", () => { |
175 | | - util.testModule` |
176 | | - export let a = 1; |
177 | | - { let a = 2; a = 3 }; |
178 | | - `.expectToMatchJsResult(); |
179 | | -}); |
| 120 | + test.each(reassignmentTestCases)("export variable with multiple bindings (%p)", reassignment => { |
| 121 | + util.testModule` |
| 122 | + let x = 0; |
| 123 | + export { x as a }; |
| 124 | + export { x as b }; |
| 125 | + ${reassignment}; |
| 126 | + `.expectToMatchJsResult(); |
| 127 | + }); |
180 | 128 |
|
181 | | -test("export as specifier shouldn't effect local vars", () => { |
182 | | - util.testModule` |
183 | | - let x = false; |
184 | | - export { x as a }; |
185 | | - let a = 5; |
186 | | - a = 6; |
187 | | - `.expectToMatchJsResult(); |
188 | | -}); |
| 129 | + // Can't be added to reassignmentTestCases because of https://github.com/microsoft/TypeScript/issues/35881 |
| 130 | + test("export variable (for in loop)", () => { |
| 131 | + util.testModule` |
| 132 | + export let foo = ''; |
| 133 | + for (foo in { x: true }) {} |
| 134 | + ` |
| 135 | + .setReturnExport("foo") |
| 136 | + .expectToMatchJsResult(); |
| 137 | + }); |
189 | 138 |
|
190 | | -test("export modified in for in loop", () => { |
191 | | - util.testModule` |
192 | | - export let foo = ''; |
193 | | - for (foo in { x: true }) {} |
194 | | - ` |
195 | | - .setReturnExport("foo") |
196 | | - .expectToMatchJsResult(); |
197 | | -}); |
| 139 | + test("export variable as a binding (for in loop)", () => { |
| 140 | + util.testModule` |
| 141 | + let foo = ''; |
| 142 | + export { foo as bar }; |
| 143 | + for (foo in { x: true }) {} |
| 144 | + ` |
| 145 | + .setReturnExport("bar") |
| 146 | + .expectToEqual("x"); |
| 147 | + }); |
198 | 148 |
|
199 | | -test("export dependency modified in for in loop", () => { |
200 | | - util.testModule` |
201 | | - let foo = ''; |
202 | | - export { foo as bar }; |
203 | | - for (foo in { x: true }) {} |
204 | | - ` |
205 | | - .setReturnExport("bar") |
206 | | - .expectToEqual("x"); |
207 | | -}); |
| 149 | + test("does not update shadowed names", () => { |
| 150 | + util.testModule` |
| 151 | + export let a = 1; |
| 152 | + { let a = 2; a = 3 }; |
| 153 | + `.expectToMatchJsResult(); |
| 154 | + }); |
208 | 155 |
|
209 | | -test("export default class with future reference", () => { |
210 | | - util.testModule` |
211 | | - export default class Default {} |
212 | | - const d = new Default(); |
213 | | - export const result = d.constructor.name; |
214 | | - ` |
215 | | - .setReturnExport("result") |
216 | | - .expectToMatchJsResult(); |
| 156 | + test("renamed export specifier shouldn't effect local vars", () => { |
| 157 | + util.testModule` |
| 158 | + let x = false; |
| 159 | + export { x as a }; |
| 160 | + let a = 5; |
| 161 | + a = 6; |
| 162 | + `.expectToMatchJsResult(); |
| 163 | + }); |
217 | 164 | }); |
218 | 165 |
|
219 | | -test("export default function with future reference", () => { |
220 | | - util.testModule` |
221 | | - export default function defaultFunction() { |
222 | | - return true; |
| 166 | +describe("import and export elision", () => { |
| 167 | + const moduleDeclaration = ` |
| 168 | + declare module "module" { |
| 169 | + export type Type = string; |
| 170 | + export declare const value: string; |
223 | 171 | } |
224 | | - export const result = defaultFunction(); |
225 | | - ` |
226 | | - .setReturnExport("result") |
227 | | - .expectToMatchJsResult(); |
| 172 | + `; |
| 173 | + |
| 174 | + const expectToElideImport: util.TapCallback = builder => { |
| 175 | + builder.addExtraFile("module.d.ts", moduleDeclaration).setOptions({ module: ts.ModuleKind.CommonJS }); |
| 176 | + expect(builder.getLuaExecutionResult()).not.toBeInstanceOf(util.ExecutionError); |
| 177 | + }; |
| 178 | + |
| 179 | + test("should elide named type imports", () => { |
| 180 | + util.testModule` |
| 181 | + import { Type } from "module"; |
| 182 | + const foo: Type = "bar"; |
| 183 | + `.tap(expectToElideImport); |
| 184 | + }); |
| 185 | + |
| 186 | + test("should elide named value imports used only as a type", () => { |
| 187 | + util.testModule` |
| 188 | + import { value } from "module"; |
| 189 | + const foo: typeof value = "bar"; |
| 190 | + `.tap(expectToElideImport); |
| 191 | + }); |
| 192 | + |
| 193 | + test("should elide namespace imports with unused values", () => { |
| 194 | + util.testModule` |
| 195 | + import * as module from "module"; |
| 196 | + const foo: module.Type = "bar"; |
| 197 | + `.tap(expectToElideImport); |
| 198 | + }); |
| 199 | + |
| 200 | + test("should elide `import =` declarations", () => { |
| 201 | + util.testModule` |
| 202 | + import module = require("module"); |
| 203 | + const foo: module.Type = "bar"; |
| 204 | + `.tap(expectToElideImport); |
| 205 | + }); |
| 206 | + |
| 207 | + test("should elide type exports", () => { |
| 208 | + util.testModule` |
| 209 | + (globalThis as any).foo = true; |
| 210 | + type foo = boolean; |
| 211 | + export { foo }; |
| 212 | + `.expectToEqual([]); |
| 213 | + }); |
228 | 214 | }); |
| 215 | + |
| 216 | +test.each(["ke-bab", "dollar$", "singlequote'", "hash#", "s p a c e", "ɥɣɎɌͼƛಠ", "_̀ः٠‿", ".dot"])( |
| 217 | + "import local identifier generation (%p)", |
| 218 | + name => { |
| 219 | + util.testBundle` |
| 220 | + import { foo } from "./${name}"; |
| 221 | + export { foo }; |
| 222 | + ` |
| 223 | + .addExtraFile(`${name}.ts`, "export const foo = true;") |
| 224 | + .expectToEqual({ foo: true }) |
| 225 | + .tap(builder => { |
| 226 | + const identifier = builder.getMainLuaCodeChunk().match(/local (.+) = require\(/)?.[1]; |
| 227 | + expect(identifier).toMatchSnapshot("local identifier"); |
| 228 | + }); |
| 229 | + } |
| 230 | +); |
0 commit comments