UNPKG

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