Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 76d66ea

Browse filesBrowse files
committed
feat: add playground package
1 parent 3b6758b commit 76d66ea
Copy full SHA for 76d66ea

File tree

Expand file treeCollapse file tree

9 files changed

+676
-1
lines changed
Filter options
Expand file treeCollapse file tree

9 files changed

+676
-1
lines changed

‎packages/playground/.gitignore

Copy file name to clipboard
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist

‎packages/playground/index.html

Copy file name to clipboard
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8+
<title>Vue 2 JSX Playground</title>
9+
10+
<link rel="stylesheet" href="./style.css">
11+
<script type="module" src="./index.ts"></script>
12+
</head>
13+
14+
<body>
15+
<div id="header"></div>
16+
<div id="source" class="editor"></div>
17+
<div id="output" class="editor"></div>
18+
</body>
19+
20+
</html>

‎packages/playground/index.ts

Copy file name to clipboard
+76Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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 = () => <div>Hello World</div>',
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<T extends (...args: any[]) => 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+
}

‎packages/playground/options.ts

Copy file name to clipboard
+246Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
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', 'Vue 2 JSX Explorer'),
26+
h(
27+
'a',
28+
{
29+
// FIXME:
30+
href: 'https://github.com/vuejs/jsx-vue2',
31+
// href: 'https://app.netlify.com/sites/vue-next-jsx-explorer/deploys',
32+
target: '_blank',
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+
}

‎packages/playground/package.json

Copy file name to clipboard
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
"test": "echo \"Error: no test specified\" && exit 1",
8+
"dev": "vite",
9+
"build": "vite build"
10+
},
11+
"repository": {
12+
"type": "git",
13+
"url": "git+https://github.com/vuejs/jsx-vue2.git"
14+
},
15+
"author": "Haoqun Jiang <haoqunjiang+npm@gmail.com>",
16+
"license": "MIT",
17+
"bugs": {
18+
"url": "https://github.com/vuejs/jsx-vue2/issues"
19+
},
20+
"homepage": "https://github.com/vuejs/jsx-vue2#readme",
21+
"devDependencies": {
22+
"@vue/tsconfig": "^0.1.3",
23+
"vite": "^3.0.4",
24+
"vite-plugin-monaco-editor": "^1.1.0"
25+
},
26+
"dependencies": {
27+
"@babel/standalone": "^7.19.3",
28+
"@vue/babel-preset-jsx": "*",
29+
"monaco-editor": "^0.34.0",
30+
"vue": "^2.7.10"
31+
}
32+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.