<script setup lang="ts">
import { downloadProject } from './download/download'
-import { setVersion, resetVersion } from './sfcCompiler'
+import { setVersion, resetVersion } from './transform'
import { ref, onMounted } from 'vue'
const currentCommit = __COMMIT__
`https://api.github.com/repos/vuejs/vue-next/releases?per_page=100`
)
const releases: any[] = await res.json()
- const versions = releases.map(
- r => (/^v/.test(r.tag_name) ? r.tag_name.substr(1) : r.tag_name)
+ const versions = releases.map(r =>
+ /^v/.test(r.tag_name) ? r.tag_name.substr(1) : r.tag_name
)
const minVersion = versions.findIndex(v => v === '3.0.10')
return versions.slice(0, minVersion + 1)
<template>
<nav>
<h1>
- <img alt="logo" src="/logo.svg">
+ <img alt="logo" src="/logo.svg" />
<span>Vue SFC Playground</span>
</h1>
<div class="links">
<li v-for="version of publishedVersions">
<a @click="setVueVersion(version)">v{{ version }}</a>
</li>
- <li><a @click="resetVueVersion">This Commit ({{ currentCommit }})</a></li>
<li>
- <a href="https://app.netlify.com/sites/vue-sfc-playground/deploys" target="_blank">Commits History</a>
+ <a @click="resetVueVersion">This Commit ({{ currentCommit }})</a>
+ </li>
+ <li>
+ <a
+ href="https://app.netlify.com/sites/vue-sfc-playground/deploys"
+ target="_blank"
+ >Commits History</a
+ >
</li>
</ul>
</div>
<button class="share" @click="copyLink">
<svg width="1.4em" height="1.4em" viewBox="0 0 24 24">
- <g fill="none" stroke="#626262" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
- <circle cx="18" cy="5" r="3"/>
- <circle cx="6" cy="12" r="3"/>
- <circle cx="18" cy="19" r="3"/>
- <path d="M8.59 13.51l6.83 3.98"/>
- <path d="M15.41 6.51l-6.82 3.98"/>
+ <g
+ fill="none"
+ stroke="#626262"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ >
+ <circle cx="18" cy="5" r="3" />
+ <circle cx="6" cy="12" r="3" />
+ <circle cx="18" cy="19" r="3" />
+ <path d="M8.59 13.51l6.83 3.98" />
+ <path d="M15.41 6.51l-6.82 3.98" />
</g>
</svg>
- </button>
+ </button>
<button class="download" @click="downloadProject">
<svg width="1.7em" height="1.7em" viewBox="0 0 24 24">
<g fill="#626262">
- <rect x="4" y="18" width="16" height="2" rx="1" ry="1"/>
- <rect x="3" y="17" width="4" height="2" rx="1" ry="1" transform="rotate(-90 5 18)"/>
- <rect x="17" y="17" width="4" height="2" rx="1" ry="1" transform="rotate(-90 19 18)"/>
- <path d="M12 15a1 1 0 0 1-.58-.18l-4-2.82a1 1 0 0 1-.24-1.39a1 1 0 0 1 1.4-.24L12 12.76l3.4-2.56a1 1 0 0 1 1.2 1.6l-4 3a1 1 0 0 1-.6.2z"/>
- <path d="M12 13a1 1 0 0 1-1-1V4a1 1 0 0 1 2 0v8a1 1 0 0 1-1 1z"/>
+ <rect x="4" y="18" width="16" height="2" rx="1" ry="1" />
+ <rect
+ x="3"
+ y="17"
+ width="4"
+ height="2"
+ rx="1"
+ ry="1"
+ transform="rotate(-90 5 18)"
+ />
+ <rect
+ x="17"
+ y="17"
+ width="4"
+ height="2"
+ rx="1"
+ ry="1"
+ transform="rotate(-90 19 18)"
+ />
+ <path
+ d="M12 15a1 1 0 0 1-.58-.18l-4-2.82a1 1 0 0 1-.24-1.39a1 1 0 0 1 1.4-.24L12 12.76l3.4-2.56a1 1 0 0 1 1.2 1.6l-4 3a1 1 0 0 1-.6.2z"
+ />
+ <path d="M12 13a1 1 0 0 1-1-1V4a1 1 0 0 1 2 0v8a1 1 0 0 1-1 1z" />
</g>
</svg>
</button>
function doneAddFile() {
const filename = pendingFilename.value
- if (
- !filename.endsWith('.vue') &&
- !filename.endsWith('.js') &&
- filename !== 'import-map.json'
- ) {
+ if (!/\.(vue|js|ts)$/.test(filename) && filename !== 'import-map.json') {
store.errors = [
- `Playground only supports *.vue, *.js files or import-map.json.`
+ `Playground only supports *.vue, *.js, *.ts files or import-map.json.`
]
return
}
} from 'vue'
import srcdoc from './srcdoc.html?raw'
import { PreviewProxy } from './PreviewProxy'
-import { MAIN_FILE, vueRuntimeUrl } from '../sfcCompiler'
+import { MAIN_FILE, vueRuntimeUrl } from '../transform'
import { compileModulesForPreview } from './moduleCompiler'
import { store } from '../store'
import { store, File } from '../store'
-import { MAIN_FILE } from '../sfcCompiler'
+import { MAIN_FILE } from '../transform'
+import { babelParse, MagicString, walk } from '@vue/compiler-sfc'
import {
- babelParse,
- MagicString,
- walk,
- walkIdentifiers
-} from '@vue/compiler-sfc'
+ walkIdentifiers,
+ extractIdentifiers,
+ isInDestructureAssignment,
+ isStaticProperty
+} from '@vue/compiler-core'
import { babelParserDefaultPlugins } from '@vue/shared'
-import { ExportSpecifier, Identifier, Node, ObjectProperty } from '@babel/types'
+import { ExportSpecifier, Identifier, Node } from '@babel/types'
export function compileModulesForPreview() {
return processFile(store.files[MAIN_FILE]).reverse()
} else if (node.declaration.type === 'VariableDeclaration') {
// export const foo = 1, bar = 2
for (const decl of node.declaration.declarations) {
- const names = extractNames(decl.id as any)
- for (const name of names) {
- defineExport(name)
+ for (const id of extractIdentifiers(decl.id)) {
+ defineExport(id.name)
}
}
}
// return a list of files to further process
return processed
}
-
-const isStaticProperty = (node: Node): node is ObjectProperty =>
- node.type === 'ObjectProperty' && !node.computed
-
-function extractNames(param: Node): string[] {
- return extractIdentifiers(param).map(id => id.name)
-}
-
-function extractIdentifiers(
- param: Node,
- nodes: Identifier[] = []
-): Identifier[] {
- switch (param.type) {
- case 'Identifier':
- nodes.push(param)
- break
-
- case 'MemberExpression':
- let object: any = param
- while (object.type === 'MemberExpression') {
- object = object.object
- }
- nodes.push(object)
- break
-
- case 'ObjectPattern':
- param.properties.forEach(prop => {
- if (prop.type === 'RestElement') {
- extractIdentifiers(prop.argument, nodes)
- } else {
- extractIdentifiers(prop.value, nodes)
- }
- })
- break
-
- case 'ArrayPattern':
- param.elements.forEach(element => {
- if (element) extractIdentifiers(element, nodes)
- })
- break
-
- case 'RestElement':
- extractIdentifiers(param.argument, nodes)
- break
-
- case 'AssignmentPattern':
- extractIdentifiers(param.left, nodes)
- break
- }
-
- return nodes
-}
-
-function isInDestructureAssignment(parent: Node, parentStack: Node[]): boolean {
- if (
- parent &&
- (parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
- ) {
- let i = parentStack.length
- while (i--) {
- const p = parentStack[i]
- if (p.type === 'AssignmentExpression') {
- return true
- } else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
- break
- }
- }
- }
- return false
-}
import { reactive, watchEffect } from 'vue'
-import { compileFile, MAIN_FILE } from './sfcCompiler'
+import { compileFile, MAIN_FILE } from './transform'
import { utoa, atou } from './utils'
const welcomeCode = `
+<script setup>
+import { ref } from 'vue'
+
+const msg = ref('Hello World!')
+</script>
+
<template>
<h1>{{ msg }}</h1>
+ <input v-model="msg">
</template>
-
-<script setup>
-const msg = 'Hello World!'
-</script>
`.trim()
export class File {
import { store, File } from './store'
-import { SFCDescriptor, BindingMetadata } from '@vue/compiler-sfc'
+import {
+ SFCDescriptor,
+ BindingMetadata,
+ shouldTransformRef,
+ transformRef
+} from '@vue/compiler-sfc'
import * as defaultCompiler from '@vue/compiler-sfc'
import { ref } from 'vue'
vueRuntimeUrl.value = defaultVueUrl
}
+async function transformTS(src: string) {
+ return (await import('sucrase')).transform(src, {
+ transforms: ['typescript']
+ }).code
+}
+
export async function compileFile({ filename, code, compiled }: File) {
if (!code.trim()) {
store.errors = []
}
if (!filename.endsWith('.vue')) {
+ if (shouldTransformRef(code)) {
+ code = transformRef(code, { filename }).code
+ }
+
+ if (filename.endsWith('.ts')) {
+ code = await transformTS(code)
+ }
+
compiled.js = compiled.ssr = code
store.errors = []
return
try {
const compiledScript = SFCCompiler.compileScript(descriptor, {
id,
- refSugar: true,
+ refTransform: true,
inlineTemplate: true,
templateOptions: {
ssr,
SFCCompiler.rewriteDefault(compiledScript.content, COMP_IDENTIFIER)
if ((descriptor.script || descriptor.scriptSetup)!.lang === 'ts') {
- code = (await import('sucrase')).transform(code, {
- transforms: ['typescript']
- }).code
+ code = await transformTS(code)
}
return [code, compiledScript.bindings]