UNPKG

11.9 kBJavaScriptView Raw
1"use strict"
2
3const assert = require("assert")
4const acorn = require("acorn")
5const Parser = acorn.Parser.extend(require(".."))
6
7function test(text, expectedResult, additionalOptions) {
8 it(text, function () {
9 const result = Parser.parse(text, Object.assign({ ecmaVersion: 10 }, additionalOptions))
10 if (expectedResult) {
11 assert.deepStrictEqual(result.body[0], expectedResult)
12 }
13 })
14}
15function testFail(text, expectedResult, additionalOptions) {
16 it(text, function () {
17 let failed = false
18 try {
19 Parser.parse(text, Object.assign({ ecmaVersion: 10 }, additionalOptions))
20 } catch (e) {
21 assert.strictEqual(e.message, expectedResult)
22 failed = true
23 }
24 assert(failed)
25 })
26}
27const newNode = (start, props) => Object.assign(new acorn.Node({options: {}}, start), props)
28
29describe("acorn-static-class-features", function () {
30 test(`class CustomDate {
31 // ...
32 static epoch = new CustomDate(0);
33 }`)
34
35 testFail("class A { static #a; f() { delete A.#a } }", "Private elements may not be deleted (1:27)")
36 testFail("class A { static #a; static #a }", "Duplicate private element (1:21)")
37 testFail("class A { static a = A.#a }", "Usage of undeclared private name (1:23)")
38 testFail("class A { static a = () => arguments }", "A static class field initializer may not contain arguments (1:27)")
39 testFail("class A { static a = () => super() }", "A static class field initializer may not contain super (1:27)")
40 testFail("class A { static # a }", "Unexpected token (1:19)")
41 testFail("class A { static #a; a() { A.# a } }", "Unexpected token (1:31)")
42 test(`class C {
43 static async * #method() {
44 }
45 }`)
46
47 const classes = [
48 { text: "class A { %s }", ast: getBody => {
49 const body = getBody(10)
50 return newNode(0, {
51 type: "ClassDeclaration",
52 end: body.end + 2,
53 id: newNode(6, {
54 type: "Identifier",
55 end: 7,
56 name: "A"
57 }),
58 superClass: null,
59 body: newNode(8, {
60 type: "ClassBody",
61 end: body.end + 2,
62 body: [body]
63 })
64 })
65 } },
66 { text: "class A { %s; }", ast: getBody => {
67 const body = getBody(10)
68 return newNode(0, {
69 type: "ClassDeclaration",
70 end: body.end + 3,
71 id: newNode(6, {
72 type: "Identifier",
73 end: 7,
74 name: "A"
75 }),
76 superClass: null,
77 body: newNode(8, {
78 type: "ClassBody",
79 end: body.end + 3,
80 body: [body]
81 })
82 })
83 } },
84 { text: "class A { %s; static #y }", ast: getBody => {
85 const body = getBody(10)
86 return newNode(0, {
87 type: "ClassDeclaration",
88 end: body.end + 13,
89 id: newNode(6, {
90 type: "Identifier",
91 end: 7,
92 name: "A"
93 }),
94 superClass: null,
95 body: newNode(8, {
96 type: "ClassBody",
97 end: body.end + 13,
98 body: [body, newNode(body.end + 2, {
99 type: "FieldDefinition",
100 end: body.end + 11,
101 key: newNode(body.end + 9, {
102 type: "PrivateName",
103 end: body.end + 11,
104 name: "y"
105 }),
106 value: null,
107 computed: false,
108 static: true
109 }) ]
110 })
111 })
112 } },
113 { text: "class A { %s;a() {} }", ast: getBody => {
114 const body = getBody(10)
115 return newNode(0, {
116 type: "ClassDeclaration",
117 end: body.end + 9,
118 id: newNode(6, {
119 type: "Identifier",
120 end: 7,
121 name: "A"
122 }),
123 superClass: null,
124 body: newNode(8, {
125 type: "ClassBody",
126 end: body.end + 9,
127 body: [ body, newNode(body.end + 1, {
128 type: "MethodDefinition",
129 end: body.end + 7,
130 kind: "method",
131 static: false,
132 computed: false,
133 key: newNode(body.end + 1, {
134 type: "Identifier",
135 end: body.end + 2,
136 name: "a"
137 }),
138 value: newNode(body.end + 2, {
139 type: "FunctionExpression",
140 end: body.end + 7,
141 id: null,
142 generator: false,
143 expression: false,
144 async: false,
145 params: [],
146 body: newNode(body.end + 5, {
147 type: "BlockStatement",
148 end: body.end + 7,
149 body: []
150 })
151 })
152 }) ]
153 })
154 })
155 } },
156 { text: "class A { %s\na() {} }", ast: getBody => {
157 const body = getBody(10)
158 return newNode(0, {
159 type: "ClassDeclaration",
160 end: body.end + 9,
161 id: newNode(6, {
162 type: "Identifier",
163 end: 7,
164 name: "A"
165 }),
166 superClass: null,
167 body: newNode(8, {
168 type: "ClassBody",
169 end: body.end + 9,
170 body: [
171 body,
172 newNode(body.end + 1, {
173 type: "MethodDefinition",
174 end: body.end + 7,
175 kind: "method",
176 static: false,
177 computed: false,
178 key: newNode(body.end + 1, {
179 type: "Identifier",
180 end: body.end + 2,
181 name: "a"
182 }),
183 value: newNode(body.end + 2, {
184 type: "FunctionExpression",
185 end: body.end + 7,
186 id: null,
187 generator: false,
188 expression: false,
189 async: false,
190 params: [],
191 body: newNode(body.end + 5, {
192 type: "BlockStatement",
193 end: body.end + 7,
194 body: []
195 })
196 })
197 })
198 ]
199 })
200 })
201 } },
202 ];
203
204 [
205 { body: "static x", passes: true, ast: start => newNode(start, {
206 type: "FieldDefinition",
207 end: start + 8,
208 key: newNode(start + 7, {
209 type: "Identifier",
210 end: start + 8,
211 name: "x"
212 }),
213 value: null,
214 computed: false,
215 static: true
216 }) },
217 { body: "static x = 0", passes: true, ast: start => newNode(start, {
218 type: "FieldDefinition",
219 end: start + 12,
220 key: newNode(start + 7, {
221 type: "Identifier",
222 end: start + 8,
223 name: "x"
224 }),
225 value: newNode(start + 11, {
226 type: "Literal",
227 end: start + 12,
228 value: 0,
229 raw: "0"
230 }),
231 computed: false,
232 static: true
233 }) },
234 { body: "static [x]", passes: true, ast: start => newNode(start, {
235 type: "FieldDefinition",
236 end: start + 10,
237 computed: true,
238 key: newNode(start + 8, {
239 type: "Identifier",
240 end: start + 9,
241 name: "x"
242 }),
243 value: null,
244 static: true
245 }) },
246 { body: "static [x] = 0", passes: true, ast: start => newNode(start, {
247 type: "FieldDefinition",
248 end: start + 14,
249 computed: true,
250 key: newNode(start + 8, {
251 type: "Identifier",
252 end: start + 9,
253 name: "x"
254 }),
255 value: newNode(start + 13, {
256 type: "Literal",
257 end: start + 14,
258 value: 0,
259 raw: "0"
260 }),
261 static: true
262 }) },
263 { body: "static #x", passes: true, ast: start => newNode(start, {
264 type: "FieldDefinition",
265 end: start + 9,
266 computed: false,
267 key: newNode(start + 7, {
268 type: "PrivateName",
269 end: start + 9,
270 name: "x"
271 }),
272 value: null,
273 static: true
274 }) },
275 { body: "static #x = 0", passes: true, ast: start => newNode(start, {
276 type: "FieldDefinition",
277 end: start + 13,
278 computed: false,
279 key: newNode(start + 7, {
280 type: "PrivateName",
281 end: start + 9,
282 name: "x"
283 }),
284 value: newNode(start + 12, {
285 type: "Literal",
286 end: start + 13,
287 value: 0,
288 raw: "0"
289 }),
290 static: true
291 }) },
292
293 { body: "static async", passes: true, ast: start => newNode(start, {
294 type: "FieldDefinition",
295 end: start + 12,
296 key: newNode(start + 7, {
297 type: "Identifier",
298 end: start + 12,
299 name: "async"
300 }),
301 value: null,
302 computed: false,
303 static: true
304 }) },
305
306 { body: "static async = 5", passes: true, ast: start => newNode(start, {
307 type: "FieldDefinition",
308 end: start + 16,
309 key: newNode(start + 7, {
310 type: "Identifier",
311 end: start + 12,
312 name: "async"
313 }),
314 value: newNode(start + 15, {
315 type: "Literal",
316 end: start + 16,
317 raw: "5",
318 value: 5
319 }),
320 computed: false,
321 static: true
322 }) },
323 ].forEach(bodyInput => {
324 const body = bodyInput.body, passes = bodyInput.passes, bodyAst = bodyInput.ast
325 classes.forEach(input => {
326 const text = input.text, options = input.options || {}, ast = input.ast;
327 (passes ? test : testFail)(text.replace("%s", body), ast(bodyAst), options)
328 })
329 })
330
331 // Private static methods
332 test("class A { a() { A.#a }; static #a() {} }")
333
334 testFail("class A { static #a() {}; f() { delete A.#a } }", "Private elements may not be deleted (1:32)")
335 testFail("class A { static #a() {}; static #a() {} }", "Duplicate private element (1:26)")
336 test("class A { static get #a() {}; static set #a(newA) {} }")
337 testFail("class A { a() { A.#a } }", "Usage of undeclared private name (1:18)")
338 testFail("class A { a() { A.#a } b() { A.#b } }", "Usage of undeclared private name (1:18)")
339 testFail("class A { static #constructor() {} }", "Classes may not have a private static property named constructor (1:17)")
340 testFail("class A { static #[ab]() {} }", "Unexpected token (1:18)")
341 testFail("a = { static #ab() {} }", "Unexpected token (1:13)")
342 testFail("class A { static [{#ab() {}}]() {} }", "Unexpected token (1:19)")
343 testFail("class A{ static # a() {}}", "Unexpected token (1:18)")
344 testFail("class C{ static #method() { super(); } };", "A class method that is not a constructor may not contain a direct super (1:28)")
345 test("class C{ static #method() { super.y(); } };")
346
347 ;[
348 { body: "static #x() {}", passes: true, ast: start => newNode(start, {
349 type: "MethodDefinition",
350 end: start + 14,
351 computed: false,
352 key: newNode(start + 7, {
353 type: "PrivateName",
354 end: start + 9,
355 name: "x"
356 }),
357 kind: "method",
358 static: true,
359 value: newNode(start + 9, {
360 type: "FunctionExpression",
361 end: start + 14,
362 async: false,
363 body: newNode(start + 12, {
364 type: "BlockStatement",
365 body: [],
366 end: start + 14,
367 }),
368 expression: false,
369 generator: false,
370 id: null,
371 params: [],
372 })
373 }) },
374 { body: "static get #x() {}", passes: true, ast: start => newNode(start, {
375 type: "MethodDefinition",
376 end: start + 18,
377 computed: false,
378 key: newNode(start + 11, {
379 type: "PrivateName",
380 end: start + 13,
381 name: "x"
382 }),
383 kind: "get",
384 static: true,
385 value: newNode(start + 13, {
386 type: "FunctionExpression",
387 end: start + 18,
388 async: false,
389 body: newNode(start + 16, {
390 body: [],
391 end: start + 18,
392 type: "BlockStatement"
393 }),
394 expression: false,
395 generator: false,
396 id: null,
397 params: [],
398 })
399 }) },
400
401 ].forEach(bodyInput => {
402 const body = bodyInput.body, passes = bodyInput.passes, bodyAst = bodyInput.ast
403 classes.forEach(input => {
404 const text = input.text, options = input.options || {}, ast = input.ast;
405 (passes ? test : testFail)(text.replace("%s", body), ast(bodyAst), options)
406 })
407 })
408})