this.foo(1, 2, 3, $event)} />
32 | },
33 | })
34 |
35 | wrapper.trigger('click')
36 | t.is(stub.calls.length, 1)
37 |
38 | const args = stub.calls[0].arguments
39 | t.is(args.length, 4)
40 | t.is(args[0], 1)
41 | t.is(args[1], 2)
42 | t.is(args[2], 3)
43 | t.is(args[3].type, 'click')
44 | })
45 |
46 | test('should support stop propagation', t => {
47 | const parentStub = t.context.stub()
48 | const childStub = t.context.stub()
49 | const wrapper = mount({
50 | methods: {
51 | foo: parentStub,
52 | bar: childStub,
53 | },
54 | render(h) {
55 | return (
56 |
57 |
58 |
59 | )
60 | },
61 | })
62 |
63 | wrapper.find('button').trigger('click')
64 | t.is(parentStub.calls.length, 0)
65 | t.is(childStub.calls.length, 1)
66 | })
67 |
68 | test('should support prevent default', t => {
69 | const stub = t.context.stub()
70 | const wrapper = mount({
71 | methods: {
72 | foo($event) {
73 | stub($event.defaultPrevented)
74 | },
75 | },
76 | render(h) {
77 | return
78 | },
79 | })
80 |
81 | wrapper.trigger('click')
82 | t.is(stub.calls.length, 1)
83 | t.is(stub.calls[0].arguments[0], true)
84 | })
85 |
86 | test('should support capture', t => {
87 | const calls = []
88 | const wrapper = mount({
89 | methods: {
90 | foo() {
91 | calls.push(1)
92 | },
93 | bar() {
94 | calls.push(2)
95 | },
96 | },
97 | render(h) {
98 | return (
99 |
100 |
101 |
102 | )
103 | },
104 | })
105 |
106 | wrapper.find('button').trigger('click')
107 | t.deepEqual(calls, [1, 2])
108 | })
109 |
110 | test('should support once', t => {
111 | const stub = t.context.stub()
112 | const wrapper = mount({
113 | methods: {
114 | foo: stub,
115 | },
116 | render(h) {
117 | return
118 | },
119 | })
120 |
121 | t.is(stub.calls.length, 0)
122 | wrapper.trigger('click')
123 | t.is(stub.calls.length, 1)
124 | wrapper.trigger('click')
125 | t.is(stub.calls.length, 1)
126 | })
127 |
128 | test('should handle _once on multiple elements properly', t => {
129 | const stub = t.context.stub()
130 | const wrapper = mount({
131 | methods: {
132 | foo: stub,
133 | },
134 | render(h) {
135 | return (
136 |
137 |
138 |
139 |
140 | )
141 | },
142 | })
143 |
144 | const foo = wrapper.find('#foo')
145 | const bar = wrapper.find('#bar')
146 |
147 | t.is(stub.calls.length, 0)
148 | foo.trigger('click')
149 | t.is(stub.calls.length, 1)
150 | foo.trigger('click')
151 | t.is(stub.calls.length, 1)
152 | bar.trigger('click')
153 | t.is(stub.calls.length, 2)
154 | bar.trigger('click')
155 | t.is(stub.calls.length, 2)
156 | foo.trigger('click')
157 | bar.trigger('click')
158 | t.is(stub.calls.length, 2)
159 | })
160 |
161 | test('should support capture and once', t => {
162 | const calls = []
163 | const wrapper = mount({
164 | methods: {
165 | foo() {
166 | calls.push(1)
167 | },
168 | bar() {
169 | calls.push(2)
170 | },
171 | },
172 | render(h) {
173 | return (
174 |
175 |
176 |
177 | )
178 | },
179 | })
180 |
181 | t.deepEqual(calls, [])
182 | wrapper.find('button').trigger('click')
183 | t.deepEqual(calls, [1, 2])
184 | wrapper.find('button').trigger('click')
185 | t.deepEqual(calls, [1, 2, 2])
186 | })
187 |
188 | test('should support once and other modifiers', t => {
189 | const stub = t.context.stub()
190 | const wrapper = mount({
191 | methods: {
192 | foo: stub,
193 | },
194 | render(h) {
195 | return (
196 |
197 |
198 |
199 | )
200 | },
201 | })
202 |
203 | t.is(stub.calls.length, 0)
204 | wrapper.find('button').trigger('click')
205 | t.is(stub.calls.length, 0)
206 | wrapper.trigger('click')
207 | t.is(stub.calls.length, 1)
208 | wrapper.trigger('click')
209 | t.is(stub.calls.length, 1)
210 | })
211 |
212 | test('should support passive', t => {
213 | const arr = []
214 | const wrapper = mount({
215 | methods: {
216 | foo(e) {
217 | arr.push(e.defaultPrevented) // will be false
218 | e.preventDefault()
219 | arr.push(e.defaultPrevented) // will be true
220 | },
221 | bar(e) {
222 | arr.push(e.defaultPrevented) // will be false
223 | e.preventDefault() // does nothing since the listener is passive
224 | arr.push(e.defaultPrevented) // still false
225 | },
226 | },
227 | render(h) {
228 | return (
229 |
233 | )
234 | },
235 | })
236 | const divs = wrapper.findAll('div')
237 | divs.at(0).trigger('click')
238 | divs.at(1).trigger('click')
239 | t.deepEqual(arr, [false, true, false, false])
240 | })
241 |
242 | test('should support passive and once', t => {
243 | const arr = []
244 | const wrapper = mount({
245 | methods: {
246 | bar(e) {
247 | arr.push(e.defaultPrevented) // will be false
248 | e.preventDefault() // does nothing since the listener is passive
249 | arr.push(e.defaultPrevented) // still false
250 | },
251 | },
252 | render(h) {
253 | return
254 | },
255 | })
256 |
257 | wrapper.trigger('click')
258 | t.deepEqual(arr, [false, false])
259 |
260 | wrapper.trigger('click')
261 | t.deepEqual(arr, [false, false])
262 | })
263 |
264 | test('should support passive and other modifiers', t => {
265 | const arr = []
266 | const wrapper = mount({
267 | methods: {
268 | bar(e) {
269 | arr.push(e.defaultPrevented) // will be false
270 | e.preventDefault() // does nothing since the listener is passive
271 | arr.push(e.defaultPrevented) // still false
272 | },
273 | },
274 | render(h) {
275 | return (
276 |
279 | )
280 | },
281 | })
282 |
283 | wrapper.trigger('click')
284 | t.deepEqual(arr, [false, false])
285 |
286 | wrapper.find('div').trigger('click')
287 | t.deepEqual(arr, [false, false])
288 | })
289 |
290 | test("should respect vue' order on special modifer markers", t => {
291 | // This test is especially for `.passive` working with other modifiers
292 | const fn = t.context.stub()
293 | const FC = h => (
294 |
295 |
296 |
297 |
298 |
299 |
300 | )
301 |
302 | const mockCreateElement = (tag, props, children) => {
303 | if (tag === 'div') {
304 | // `&` is always the first if `.passive` present
305 | t.is(Object.keys(props.on)[0].indexOf('&'), 0)
306 | }
307 | }
308 |
309 | FC(mockCreateElement)
310 | })
311 |
312 | test('should support keyCode', t => {
313 | const stub = t.context.stub()
314 | const wrapper = mount({
315 | methods: {
316 | foo: stub,
317 | },
318 | render(h) {
319 | return
320 | },
321 | })
322 |
323 | t.is(stub.calls.length, 0)
324 | wrapper.trigger('keyup')
325 | t.is(stub.calls.length, 0)
326 | wrapper.trigger('keyup.enter')
327 | t.is(stub.calls.length, 1)
328 | })
329 |
330 | test('should support automatic key name inference', t => {
331 | const stub = t.context.stub()
332 | const wrapper = mount({
333 | methods: {
334 | foo: stub,
335 | },
336 | render(h) {
337 | return
338 | },
339 | })
340 |
341 | t.is(stub.calls.length, 0)
342 | wrapper.trigger('keyup', {
343 | key: 'ArrowLeft',
344 | })
345 | t.is(stub.calls.length, 0)
346 | wrapper.trigger('keyup', {
347 | key: 'ArrowRight',
348 | })
349 | t.is(stub.calls.length, 1)
350 | })
351 |
352 | test('should support system modifers', t => {
353 | const stub = t.context.stub()
354 | const wrapper = mount({
355 | methods: {
356 | foo: stub,
357 | },
358 | render(h) {
359 | return (
360 |
361 |
362 |
363 |
364 |
365 |
366 | )
367 | },
368 | })
369 |
370 | t.is(stub.calls.length, 0)
371 | wrapper.find('#ctrl').trigger('keyup')
372 | t.is(stub.calls.length, 0)
373 | wrapper.find('#ctrl').trigger('keyup', { ctrlKey: true })
374 | t.is(stub.calls.length, 1)
375 |
376 | wrapper.find('#shift').trigger('keyup')
377 | t.is(stub.calls.length, 1)
378 | wrapper.find('#shift').trigger('keyup', { shiftKey: true })
379 | t.is(stub.calls.length, 2)
380 |
381 | wrapper.find('#alt').trigger('keyup')
382 | t.is(stub.calls.length, 2)
383 | wrapper.find('#alt').trigger('keyup', { altKey: true })
384 | t.is(stub.calls.length, 3)
385 |
386 | wrapper.find('#meta').trigger('keyup')
387 | t.is(stub.calls.length, 3)
388 | wrapper.find('#meta').trigger('keyup', { metaKey: true })
389 | t.is(stub.calls.length, 4)
390 | })
391 |
392 | test('should support exact modifer', t => {
393 | const stub = t.context.stub()
394 | const wrapper = mount({
395 | methods: {
396 | foo: stub,
397 | },
398 | render(h) {
399 | return
400 | },
401 | })
402 |
403 | t.is(stub.calls.length, 0)
404 | wrapper.trigger('keyup')
405 | t.is(stub.calls.length, 1)
406 | wrapper.trigger('keyup', { ctrlKey: true })
407 | t.is(stub.calls.length, 1)
408 | })
409 |
410 | test('should support system modifers with exact', t => {
411 | const stub = t.context.stub()
412 | const wrapper = mount({
413 | methods: {
414 | foo: stub,
415 | },
416 | render(h) {
417 | return
418 | },
419 | })
420 |
421 | t.is(stub.calls.length, 0)
422 | wrapper.trigger('keyup')
423 | t.is(stub.calls.length, 0)
424 | wrapper.trigger('keyup', { ctrlKey: true })
425 | t.is(stub.calls.length, 1)
426 | wrapper.trigger('keyup', { ctrlKey: true, altKey: true })
427 | t.is(stub.calls.length, 1)
428 | })
429 |
430 | test('should support number keyCode', t => {
431 | const stub = t.context.stub()
432 | const wrapper = mount({
433 | methods: {
434 | foo: stub,
435 | },
436 | render(h) {
437 | return
438 | },
439 | })
440 |
441 | t.is(stub.calls.length, 0)
442 | wrapper.trigger('keyup')
443 | t.is(stub.calls.length, 0)
444 | wrapper.trigger('keyup.enter')
445 | t.is(stub.calls.length, 1)
446 | })
447 |
448 | test('should support mouse modifier', t => {
449 | const left = 0
450 | const middle = 1
451 | const right = 2
452 | const stubLeft = t.context.stub()
453 | const stubMiddle = t.context.stub()
454 | const stubRight = t.context.stub()
455 |
456 | const wrapper = mount({
457 | methods: {
458 | foo: stubLeft,
459 | foo1: stubRight,
460 | foo2: stubMiddle,
461 | },
462 | render(h) {
463 | return (
464 |
465 |
466 | left
467 |
468 |
469 | right
470 |
471 |
472 | middle
473 |
474 |
475 | )
476 | },
477 | })
478 |
479 | t.is(stubLeft.calls.length, 0)
480 | wrapper.find('#left').trigger('mousedown', { button: left })
481 | t.is(stubLeft.calls.length, 1)
482 | wrapper.find('#left').trigger('mousedown', { button: right })
483 | t.is(stubLeft.calls.length, 1)
484 | wrapper.find('#left').trigger('mousedown', { button: middle })
485 | t.is(stubLeft.calls.length, 1)
486 |
487 | t.is(stubRight.calls.length, 0)
488 | wrapper.find('#right').trigger('mousedown', { button: left })
489 | t.is(stubRight.calls.length, 0)
490 | wrapper.find('#right').trigger('mousedown', { button: right })
491 | t.is(stubRight.calls.length, 1)
492 | wrapper.find('#right').trigger('mousedown', { button: middle })
493 | t.is(stubRight.calls.length, 1)
494 |
495 | t.is(stubMiddle.calls.length, 0)
496 | wrapper.find('#middle').trigger('mousedown', { button: left })
497 | t.is(stubMiddle.calls.length, 0)
498 | wrapper.find('#middle').trigger('mousedown', { button: right })
499 | t.is(stubMiddle.calls.length, 0)
500 | wrapper.find('#middle').trigger('mousedown', { button: middle })
501 | t.is(stubMiddle.calls.length, 1)
502 | })
503 |
504 | test('should support KeyboardEvent.key for built in aliases', t => {
505 | const stub = t.context.stub()
506 | const wrapper = mount({
507 | methods: { foo: stub },
508 | render(h) {
509 | return (
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 | )
518 | },
519 | })
520 |
521 | t.is(stub.calls.length, 0)
522 | wrapper.find('#enter').trigger('keyup', { key: 'Enter' })
523 | t.is(stub.calls.length, 1)
524 | wrapper.find('#space').trigger('keyup', { key: ' ' })
525 | t.is(stub.calls.length, 2)
526 | wrapper.find('#esc').trigger('keyup', { key: 'Escape' })
527 | t.is(stub.calls.length, 3)
528 | wrapper.find('#left').trigger('keyup', { key: 'ArrowLeft' })
529 | t.is(stub.calls.length, 4)
530 | wrapper.find('#delete').trigger('keyup', { key: 'Backspace' })
531 | t.is(stub.calls.length, 5)
532 | wrapper.find('#delete').trigger('keyup', { key: 'Delete' })
533 | t.is(stub.calls.length, 6)
534 | })
535 |
536 | test('should support custom keyCode', t => {
537 | const localVue = createLocalVue()
538 | localVue.config.keyCodes.test = 1
539 | const stub = t.context.stub()
540 | const wrapper = mount(
541 | {
542 | methods: { foo: stub },
543 | render(h) {
544 | return
545 | },
546 | },
547 | { localVue },
548 | )
549 |
550 | t.is(stub.calls.length, 0)
551 | wrapper.trigger('keyup', { keyCode: 1 })
552 | t.is(stub.calls.length, 1)
553 | })
554 |
555 | test('should bind to a child component', t => {
556 | const stub = t.context.stub()
557 | const Bar = {
558 | render(h) {
559 | return
Hello
560 | },
561 | }
562 | const wrapper = mount({
563 | methods: { foo: stub },
564 | render(h) {
565 | return
566 | },
567 | })
568 |
569 | wrapper.vm.$children[0].$emit('custom', 'foo', 'bar')
570 | t.deepEqual(stub.calls[0].arguments, ['foo', 'bar'])
571 | })
572 |
573 | test('should be able to bind native events for a child component', t => {
574 | const stub = t.context.stub()
575 | const Bar = {
576 | render(h) {
577 | return
Hello
578 | },
579 | }
580 | const wrapper = mount({
581 | methods: { foo: stub },
582 | render(h) {
583 | return
584 | },
585 | })
586 |
587 | wrapper.vm.$children[0].$emit('click')
588 | t.is(stub.calls.length, 0)
589 | wrapper.find('span').trigger('click')
590 | t.is(stub.calls.length, 1)
591 | })
592 |
593 | test('_once modifier should work with child components', t => {
594 | const stub = t.context.stub()
595 | const Bar = {
596 | render(h) {
597 | return
Hello
598 | },
599 | }
600 | const wrapper = mount({
601 | methods: { foo: stub },
602 | render(h) {
603 | return
604 | },
605 | })
606 |
607 | t.is(stub.calls.length, 0)
608 | wrapper.vm.$children[0].$emit('custom')
609 | t.is(stub.calls.length, 1)
610 | wrapper.vm.$children[0].$emit('custom')
611 | t.is(stub.calls.length, 1)
612 | })
613 |
614 | test('should support keyboard modifier for direction keys', t => {
615 | const stubLeft = t.context.stub()
616 | const stubRight = t.context.stub()
617 | const stubUp = t.context.stub()
618 | const stubDown = t.context.stub()
619 | const wrapper = mount({
620 | methods: {
621 | foo: stubLeft,
622 | foo1: stubRight,
623 | foo2: stubUp,
624 | foo3: stubDown,
625 | },
626 | render(h) {
627 | return (
628 |
629 |
630 |
631 |
632 |
633 |
634 | )
635 | },
636 | })
637 | wrapper.find('#left').trigger('keydown.left')
638 | wrapper.find('#left').trigger('keydown.right')
639 |
640 | wrapper.find('#right').trigger('keydown.right')
641 | wrapper.find('#right').trigger('keydown.up')
642 |
643 | wrapper.find('#up').trigger('keydown.up')
644 | wrapper.find('#up').trigger('keydown.left')
645 |
646 | wrapper.find('#down').trigger('keydown.down')
647 | wrapper.find('#down').trigger('keydown.right')
648 |
649 | t.is(stubLeft.calls.length, 1)
650 | t.is(stubRight.calls.length, 1)
651 | t.is(stubUp.calls.length, 1)
652 | t.is(stubDown.calls.length, 1)
653 | })
654 |
655 | test('should work with nested components inside arrow functions', t => {
656 | const stubMouseLeft = t.context.stub()
657 | const stubKeyLeft = t.context.stub()
658 | const wrapper = mount({
659 | methods: {
660 | foo: stubMouseLeft,
661 | bar: stubKeyLeft
662 | },
663 | render(h) {
664 | return (
665 |
666 |
667 | {[1].map(() =>
668 |
669 | )}
670 |
671 |
672 | )
673 | }
674 | })
675 |
676 | wrapper.find('#mouse').trigger('mousedown.left')
677 | wrapper.find('#key').trigger('keydown.left')
678 |
679 | t.is(stubMouseLeft.calls.length, 1)
680 | t.is(stubKeyLeft.calls.length, 1)
681 | })
682 |
--------------------------------------------------------------------------------
/packages/babel-sugar-v-on/test/snapshot.js:
--------------------------------------------------------------------------------
1 | import test from 'ava'
2 | import { transform } from '@babel/core'
3 | import plugin from '../dist/plugin.testing'
4 |
5 | const transpile = src =>
6 | new Promise((resolve, reject) => {
7 | transform(
8 | src,
9 | {
10 | plugins: [plugin],
11 | },
12 | (err, result) => {
13 | if (err) {
14 | return reject(err)
15 | }
16 | resolve(result.code)
17 | },
18 | )
19 | })
20 |
21 | const tests = [
22 | {
23 | name: 'Basic v-on:click',
24 | from: `render(h =>
test
)`,
25 | to: `render(h =>
test
);`,
26 | },
27 | {
28 | name: 'Native v-on:click',
29 | from: `render(h =>
test
)`,
30 | to: `render(h =>
test
);`,
31 | },
32 | {
33 | name: 'v-on:click with arrow function expression',
34 | from: `render(h =>
foo(1, 2, 3, $myEvent)}>test
)`,
35 | to: `render(h =>
foo(1, 2, 3, $myEvent)}>test
);`,
36 | },
37 | {
38 | name: 'v-on:click_prevent',
39 | from: `render(h =>
test
)`,
40 | to: `render(h =>
{
41 | $event.preventDefault();
42 | return foo($event);
43 | }}>test
);`,
44 | },
45 | {
46 | name: 'v-on:click_stop',
47 | from: `render(h =>
test
)`,
48 | to: `render(h =>
{
49 | $event.stopPropagation();
50 | return foo($event);
51 | }}>test
);`,
52 | },
53 | {
54 | name: 'v-on:click_capture',
55 | from: `render(h =>
test
)`,
56 | to: `render(h =>
test
);`,
61 | },
62 | {
63 | name: 'v-on:click_once',
64 | from: `render(h =>
test
)`,
65 | to: `render(h =>
test
);`,
70 | },
71 | {
72 | name: 'v-on:click_capture_once',
73 | from: `render(h =>
test
)`,
74 | to: `render(h =>
test
);`,
79 | },
80 | {
81 | name: 'v-on:click_once and other modifier',
82 | from: `render(h =>
test
)`,
83 | to: `render(h =>
{
86 | if ($event.target !== $event.currentTarget) return null;
87 | return foo($event);
88 | }
89 | }
90 | }}>test
);`,
91 | },
92 | {
93 | name: 'keyCode',
94 | from: `render(h =>
)`,
95 | to: `render(h =>
{
96 | if (!("button" in $event) && this._k($event.keyCode, "enter", 13, $event.key, "Enter")) return null;
97 | return foo($event);
98 | }} />);`,
99 | },
100 | {
101 | name: 'automatic key name inference',
102 | from: `render(h =>
)`,
103 | to: `render(h =>
{
104 | if (!("button" in $event) && this._k($event.keyCode, "arrow-right", undefined, $event.key, undefined)) return null;
105 | return foo($event);
106 | }} />);`,
107 | },
108 | {
109 | name: 'system modifers',
110 | from: `render(h =>
111 |
112 |
113 |
114 |
115 |
)`,
116 | to: `render(h =>
117 | {
118 | if (!$event.ctrlKey) return null;
119 | return foo($event);
120 | }} />
121 | {
122 | if (!$event.shiftKey) return null;
123 | return foo($event);
124 | }} />
125 | {
126 | if (!$event.altKey) return null;
127 | return foo($event);
128 | }} />
129 | {
130 | if (!$event.metaKey) return null;
131 | return foo($event);
132 | }} />
133 |
);`,
134 | },
135 | {
136 | name: 'exact modifier',
137 | from: `render(h =>
)`,
138 | to: `render(h =>
{
139 | if ($event.ctrlKey || $event.shiftKey || $event.altKey || $event.metaKey) return null;
140 | return foo($event);
141 | }} />);`,
142 | },
143 | {
144 | name: 'system + exact modifier',
145 | from: `render(h =>
)`,
146 | to: `render(h =>
{
147 | if (!$event.ctrlKey) return null;
148 | if ($event.shiftKey || $event.altKey || $event.metaKey) return null;
149 | return foo($event);
150 | }} />);`,
151 | },
152 | {
153 | name: 'number keyCode',
154 | from: `render(h =>
)`,
155 | to: `render(h =>
{
156 | if (!("button" in $event) && $event.keyCode !== 13) return null;
157 | return foo($event);
158 | }} />);`,
159 | },
160 | {
161 | name: 'mouse modifier',
162 | from: `render(h =>
163 |
left
164 |
right
165 |
right
166 |
)`,
167 | to: `render(h =>
168 |
{
169 | if (!("button" in $event) && this._k($event.keyCode, "left", 37, $event.key, ["Left", "ArrowLeft"])) return null;
170 | if ("button" in $event && $event.button !== 0) return null;
171 | return foo($event);
172 | }}>left
173 |
{
174 | if (!("button" in $event) && this._k($event.keyCode, "right", 39, $event.key, ["Right", "ArrowRight"])) return null;
175 | if ("button" in $event && $event.button !== 2) return null;
176 | return foo1($event);
177 | }}>right
178 |
{
179 | if ("button" in $event && $event.button !== 1) return null;
180 | return foo2($event);
181 | }}>right
182 |
);`,
183 | },
184 | {
185 | name: 'KeyboardEvent.key for built in aliases',
186 | from: `render(h =>
187 |
188 |
189 |
190 |
191 |
192 |
)`,
193 | to: `render(h =>
194 | {
195 | if (!("button" in $event) && this._k($event.keyCode, "enter", 13, $event.key, "Enter")) return null;
196 | return foo($event);
197 | }} />
198 | {
199 | if (!("button" in $event) && this._k($event.keyCode, "space", 32, $event.key, " ")) return null;
200 | return foo($event);
201 | }} />
202 | {
203 | if (!("button" in $event) && this._k($event.keyCode, "esc", 27, $event.key, ["Esc", "Escape"])) return null;
204 | return foo($event);
205 | }} />
206 | {
207 | if (!("button" in $event) && this._k($event.keyCode, "left", 37, $event.key, ["Left", "ArrowLeft"])) return null;
208 | if ("button" in $event && $event.button !== 0) return null;
209 | return foo($event);
210 | }} />
211 | {
212 | if (!("button" in $event) && this._k($event.keyCode, "delete", [8, 46], $event.key, ["Backspace", "Delete"])) return null;
213 | return foo($event);
214 | }} />
215 |
);`,
216 | },
217 | {
218 | name: 'Rightclick',
219 | from: `render(h =>
test
)`,
220 | to: `render(h =>
test
);`,
221 | },
222 | {
223 | name: 'Middle click',
224 | from: `render(h =>
test
)`,
225 | to: `render(h =>
{
226 | if ("button" in $event && $event.button !== 1) return null;
227 | return foo($event);
228 | }}>test
);`,
229 | },
230 | ]
231 |
232 | tests.forEach(({ name, from, to }) => test(name, async t => t.is(await transpile(from), to)))
233 |
--------------------------------------------------------------------------------
/packages/playground/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 |
--------------------------------------------------------------------------------
/packages/playground/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
JSX-Vue2 Playground
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/packages/playground/index.ts:
--------------------------------------------------------------------------------
1 | import * as monaco from 'monaco-editor'
2 | import { watchEffect } from 'vue'
3 | import { transform } from '@babel/standalone'
4 | import babelPresetJsx from '@vue/babel-preset-jsx'
5 | import { compilerOptions, initOptions } from './options'
6 |
7 | const sharedEditorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
8 | theme: 'vs-dark',
9 | fontSize: 14,
10 | wordWrap: 'on',
11 | scrollBeyondLastLine: false,
12 | renderWhitespace: 'selection',
13 | contextmenu: false,
14 | minimap: {
15 | enabled: false,
16 | },
17 | }
18 |
19 | monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
20 | allowJs: true,
21 | allowNonTsExtensions: true,
22 | jsx: monaco.languages.typescript.JsxEmit.Preserve,
23 | target: monaco.languages.typescript.ScriptTarget.Latest,
24 | })
25 |
26 | const editor = monaco.editor.create(document.getElementById('source')!, {
27 | value: decodeURIComponent(window.location.hash.slice(1)) || 'const App = () =>
Hello World
',
28 | language: 'typescript',
29 | tabSize: 2,
30 | ...sharedEditorOptions,
31 | })
32 |
33 | const output = monaco.editor.create(document.getElementById('output')!, {
34 | value: '',
35 | language: 'javascript',
36 | readOnly: true,
37 | tabSize: 2,
38 | ...sharedEditorOptions,
39 | })
40 |
41 | const reCompile = () => {
42 | const src = editor.getValue()
43 | window.location.hash = encodeURIComponent(src)
44 | // console.clear()
45 | try {
46 | const result = transform(src, {
47 | // somehow the transform function won't actually rerun
48 | // if the options are the same object, thus we have to spread it
49 | presets: [[babelPresetJsx, { ...compilerOptions }]],
50 | ast: true,
51 | })
52 | console.log('AST', result.ast!)
53 | output.setValue(result.code!)
54 | } catch (err) {
55 | output.setValue((err as Error).message!)
56 | console.error(err)
57 | }
58 | }
59 |
60 | initOptions()
61 | watchEffect(reCompile)
62 | // update compile output when input changes
63 | editor.onDidChangeModelContent(debounce(reCompile))
64 |
65 | function debounce
any>(fn: T, delay = 300): T {
66 | let prevTimer: number | null = null
67 | return ((...args: any[]) => {
68 | if (prevTimer) {
69 | clearTimeout(prevTimer)
70 | }
71 | prevTimer = window.setTimeout(() => {
72 | fn(...args)
73 | prevTimer = null
74 | }, delay)
75 | }) as any
76 | }
77 |
--------------------------------------------------------------------------------
/packages/playground/options.ts:
--------------------------------------------------------------------------------
1 | import Vue, { h, reactive } from 'vue'
2 |
3 | type VueJSXPresetOptions = {
4 | functional?: boolean
5 | injectH?: boolean
6 | vModel?: boolean
7 | vOn?: boolean
8 | compositionAPI?: true | 'auto' | 'native' | 'plugin' | 'vue-demi' | false | { importSource: string }
9 | }
10 |
11 | export { type VueJSXPresetOptions }
12 |
13 | export const compilerOptions: VueJSXPresetOptions = reactive({
14 | functional: true,
15 | injectH: true,
16 | vModel: true,
17 | vOn: true,
18 | compositionAPI: false,
19 | })
20 |
21 | const App = {
22 | setup() {
23 | return () => [
24 | h('div', { attrs: { id: 'header' } }, [
25 | h('h1', 'JSX-Vue2 Playground'),
26 | h(
27 | 'a',
28 | {
29 | attrs: {
30 | href: 'https://app.netlify.com/sites/jsx-vue2-playground/deploys',
31 | target: '_blank',
32 | },
33 | },
34 | 'History',
35 | ),
36 |
37 | h('div', { attrs: { id: 'options-wrapper' } }, [
38 | h('div', { attrs: { id: 'options-label' } }, 'Options ↘'),
39 | h('ul', { attrs: { id: 'options' } }, [
40 | h('li', [
41 | h('input', {
42 | attrs: {
43 | type: 'checkbox',
44 | id: 'functional',
45 | name: 'functional',
46 | },
47 | domProps: {
48 | checked: compilerOptions.functional,
49 | },
50 | on: {
51 | change: (e: Event) => {
52 | compilerOptions.functional = (e.target as HTMLInputElement).checked
53 | },
54 | },
55 | }),
56 | h('label', { attrs: { for: 'functional' } }, ['functional']),
57 | ]),
58 |
59 | h('li', [
60 | h('input', {
61 | attrs: {
62 | type: 'checkbox',
63 | id: 'injectH',
64 | name: 'injectH',
65 | },
66 | domProps: {
67 | checked: compilerOptions.injectH,
68 | },
69 | on: {
70 | change: (e: Event) => {
71 | compilerOptions.injectH = (e.target as HTMLInputElement).checked
72 | },
73 | },
74 | }),
75 | h('label', { attrs: { for: 'injectH' } }, ['injectH']),
76 | ]),
77 |
78 | h('li', [
79 | h('input', {
80 | attrs: {
81 | type: 'checkbox',
82 | id: 'vModel',
83 | name: 'vModel',
84 | },
85 | domProps: {
86 | checked: compilerOptions.vModel,
87 | },
88 | on: {
89 | change: (e: Event) => {
90 | compilerOptions.vModel = (e.target as HTMLInputElement).checked
91 | },
92 | },
93 | }),
94 | h('label', { attrs: { for: 'vModel' } }, ['vModel']),
95 | ]),
96 |
97 | h('li', [
98 | h('input', {
99 | attrs: {
100 | type: 'checkbox',
101 | id: 'vOn',
102 | name: 'vOn',
103 | },
104 | domProps: {
105 | checked: compilerOptions.vOn,
106 | },
107 | on: {
108 | change: (e: Event) => {
109 | compilerOptions.vOn = (e.target as HTMLInputElement).checked
110 | },
111 | },
112 | }),
113 | h('label', { attrs: { for: 'vOn' } }, ['vOn']),
114 | ]),
115 |
116 | h('li', [
117 | h(
118 | 'span',
119 | {
120 | class: 'label',
121 | },
122 | ['compositionAPI:'],
123 | ),
124 | h('input', {
125 | attrs: {
126 | type: 'radio',
127 | name: 'compositionAPI',
128 | id: 'compositionAPI-false',
129 | },
130 | domProps: {
131 | checked: compilerOptions.compositionAPI === false,
132 | },
133 | on: {
134 | change: () => (compilerOptions.compositionAPI = false),
135 | },
136 | }),
137 | h(
138 | 'label',
139 | {
140 | attrs: {
141 | for: 'compositionAPI-false',
142 | },
143 | },
144 | ['false'],
145 | ),
146 | h('input', {
147 | attrs: {
148 | type: 'radio',
149 | name: 'compositionAPI',
150 | id: 'compositionAPI-auto',
151 | },
152 | domProps: {
153 | checked: compilerOptions.compositionAPI === true || compilerOptions.compositionAPI === 'auto',
154 | },
155 | on: {
156 | change: () => (compilerOptions.compositionAPI = 'auto'),
157 | },
158 | }),
159 | h(
160 | 'label',
161 | {
162 | attrs: {
163 | for: 'compositionAPI-auto',
164 | },
165 | },
166 | ['auto'],
167 | ),
168 | h('input', {
169 | attrs: {
170 | type: 'radio',
171 | name: 'compositionAPI',
172 | id: 'compositionAPI-native',
173 | },
174 | domProps: {
175 | checked: compilerOptions.compositionAPI === 'native',
176 | },
177 | on: {
178 | change: () => (compilerOptions.compositionAPI = 'native'),
179 | },
180 | }),
181 | h(
182 | 'label',
183 | {
184 | attrs: {
185 | for: 'compositionAPI-native',
186 | },
187 | },
188 | ['native'],
189 | ),
190 | h('input', {
191 | attrs: {
192 | type: 'radio',
193 | name: 'compositionAPI',
194 | id: 'compositionAPI-plugin',
195 | },
196 | domProps: {
197 | checked: compilerOptions.compositionAPI === 'plugin',
198 | },
199 | on: {
200 | change: () => (compilerOptions.compositionAPI = 'plugin'),
201 | },
202 | }),
203 | h(
204 | 'label',
205 | {
206 | attrs: {
207 | for: 'compositionAPI-plugin',
208 | },
209 | },
210 | ['plugin'],
211 | ),
212 | h('input', {
213 | attrs: {
214 | type: 'radio',
215 | name: 'compositionAPI',
216 | id: 'compositionAPI-vue-demi',
217 | },
218 | domProps: {
219 | checked: compilerOptions.compositionAPI === 'vue-demi',
220 | },
221 | on: {
222 | change: () => (compilerOptions.compositionAPI = 'vue-demi'),
223 | },
224 | }),
225 | h(
226 | 'label',
227 | {
228 | attrs: {
229 | for: 'compositionAPI-vue-demi',
230 | },
231 | },
232 | ['vue-demi'],
233 | ),
234 | ]),
235 | ]),
236 | ]),
237 | ]),
238 | ]
239 | },
240 | }
241 |
242 | export function initOptions() {
243 | new Vue({
244 | render: h => h(App),
245 | }).$mount(document.getElementById('header')!)
246 | }
247 |
--------------------------------------------------------------------------------
/packages/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vue/jsx-vue2-playground",
3 | "version": "1.0.0",
4 | "description": "Online playground for Vue 2 JSX preset",
5 | "private": true,
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/vuejs/jsx-vue2.git"
13 | },
14 | "author": "Haoqun Jiang ",
15 | "license": "MIT",
16 | "bugs": {
17 | "url": "https://github.com/vuejs/jsx-vue2/issues"
18 | },
19 | "homepage": "https://github.com/vuejs/jsx-vue2#readme",
20 | "devDependencies": {
21 | "@vue/tsconfig": "^0.1.3",
22 | "vite": "^3.0.4",
23 | "vite-plugin-monaco-editor": "^1.1.0"
24 | },
25 | "dependencies": {
26 | "@babel/standalone": "^7.19.3",
27 | "@vue/babel-preset-jsx": "*",
28 | "monaco-editor": "^0.34.0",
29 | "vue": "^2.7.10"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/playground/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
4 | }
5 |
6 | #header {
7 | position: absolute;
8 | top: 0;
9 | left: 0;
10 | right: 0;
11 | height: 60px;
12 | box-sizing: border-box;
13 | background-color: #1e1e1e;
14 | border-bottom: 1px solid #333;
15 | padding: 0.3em 1.6em;
16 | color: #fff;
17 | z-index: 1;
18 | }
19 |
20 | h1 {
21 | font-size: 18px;
22 | display: inline-block;
23 | margin-right: 15px;
24 | }
25 |
26 | #options-wrapper {
27 | position: absolute;
28 | top: 20px;
29 | right: 10px;
30 | }
31 |
32 | #options-wrapper:hover #options {
33 | display: block;
34 | }
35 |
36 | #options-label {
37 | cursor: pointer;
38 | text-align: right;
39 | padding-right: 10px;
40 | font-weight: bold;
41 | }
42 |
43 | #options {
44 | display: none;
45 | margin-top: 15px;
46 | list-style-type: none;
47 | background-color: #1e1e1e;
48 | border: 1px solid #333;
49 | padding: 15px 30px;
50 | }
51 |
52 | #options li {
53 | margin: 8px 0;
54 | }
55 |
56 | #header a {
57 | font-weight: 600;
58 | color: rgb(101, 163, 221);
59 | }
60 |
61 | #header .label {
62 | font-weight: bold;
63 | }
64 |
65 | #header input {
66 | margin-right: 6px;
67 | }
68 |
69 | #header label {
70 | color: #999;
71 | }
72 |
73 | .editor {
74 | position: absolute;
75 | top: 60px;
76 | bottom: 0;
77 | box-sizing: border-box;
78 | }
79 |
80 | #source {
81 | left: 0;
82 | width: 45%;
83 | }
84 |
85 | #output {
86 | left: 45%;
87 | width: 55%;
88 | }
89 |
90 | .highlight {
91 | background-color: rgba(46, 120, 190, 0.5);
92 | }
93 |
--------------------------------------------------------------------------------
/packages/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["./*.ts"],
3 | "extends": "@vue/tsconfig/tsconfig.web.json"
4 | }
5 |
--------------------------------------------------------------------------------
/packages/playground/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import monacoEditorPlugin from 'vite-plugin-monaco-editor'
3 |
4 | export default defineConfig({
5 | plugins: [
6 | monacoEditorPlugin({
7 | languageWorkers: ['editorWorkerService', 'css', 'html', 'json', 'typescript'],
8 | }),
9 | ],
10 | optimizeDeps: {
11 | include: ['@vue/babel-preset-jsx'],
12 | },
13 | build: {
14 | commonjsOptions: {
15 | include: [/packages\/babel-/, /node_modules/],
16 | },
17 | minify: false,
18 | },
19 | define: {
20 | 'process.env': {},
21 | },
22 | })
23 |
--------------------------------------------------------------------------------
/scripts/gen-release-notes.js:
--------------------------------------------------------------------------------
1 | const execa = require('execa')
2 | const cc = require('conventional-changelog')
3 | const config = require('@vue/conventional-changelog')
4 |
5 | const gen = (module.exports = version => {
6 | const fileStream = require('fs').createWriteStream(`CHANGELOG.md`)
7 |
8 | cc({
9 | config,
10 | releaseCount: 0,
11 | pkg: {
12 | transform(pkg) {
13 | pkg.version = `v${version}`
14 | return pkg
15 | },
16 | },
17 | })
18 | .pipe(fileStream)
19 | .on('close', async () => {
20 | delete process.env.PREFIX
21 | await execa('git', ['add', '-A'], { stdio: 'inherit' })
22 | await execa('git', ['commit', '-m', `chore: ${version} changelog [ci skip]`], { stdio: 'inherit' })
23 | })
24 | })
25 |
26 | if (process.argv[2] === 'run') {
27 | const version = require('../lerna.json').version
28 | gen(version)
29 | }
30 |
--------------------------------------------------------------------------------