mirror of https://github.com/vuejs/core.git
feat: repl (#94)
This commit is contained in:
parent
32604cf91c
commit
51098cff94
|
@ -38,7 +38,7 @@
|
|||
"build-all-cjs": "node scripts/build.js vue runtime compiler reactivity shared -af cjs",
|
||||
"build-runtime-esm": "node scripts/build.js runtime reactivity shared -af esm-bundler && node scripts/build.js vue -f esm-bundler-runtime && node scripts/build.js vue -f esm-browser-runtime",
|
||||
"build-browser-esm": "node scripts/build.js runtime reactivity shared -af esm-bundler && node scripts/build.js vue -f esm-bundler && node scripts/build.js vue -f esm-browser",
|
||||
"build-ssr-esm": "node scripts/build.js compiler-sfc server-renderer -f esm-browser",
|
||||
"build-ssr-esm": "node scripts/build.js compiler-sfc server-renderer vue-vapor -f esm-browser",
|
||||
"build-sfc-playground-self": "cd packages/sfc-playground && npm run build",
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"postinstall": "simple-git-hooks"
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
"@vue/compiler-core": "workspace:*",
|
||||
"@vue/compiler-dom": "workspace:*",
|
||||
"@vue/compiler-ssr": "workspace:*",
|
||||
"@vue/compiler-vapor": "workspace:*",
|
||||
"@vue/shared": "workspace:*",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.5",
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from './template/transformSrcset'
|
||||
import { generateCodeFrame, isObject } from '@vue/shared'
|
||||
import * as CompilerDOM from '@vue/compiler-dom'
|
||||
import * as CompilerVapor from '@vue/compiler-vapor'
|
||||
import * as CompilerSSR from '@vue/compiler-ssr'
|
||||
import consolidate from '@vue/consolidate'
|
||||
import { warnOnce } from './warn'
|
||||
|
@ -55,6 +56,7 @@ export interface SFCTemplateCompileOptions {
|
|||
scoped?: boolean
|
||||
slotted?: boolean
|
||||
isProd?: boolean
|
||||
vapor?: boolean
|
||||
ssr?: boolean
|
||||
ssrCssVars?: string[]
|
||||
inMap?: RawSourceMap
|
||||
|
@ -171,6 +173,7 @@ function doCompileTemplate({
|
|||
source,
|
||||
ast: inAST,
|
||||
ssr = false,
|
||||
vapor = false,
|
||||
ssrCssVars,
|
||||
isProd = false,
|
||||
compiler,
|
||||
|
@ -205,7 +208,12 @@ function doCompileTemplate({
|
|||
const shortId = id.replace(/^data-v-/, '')
|
||||
const longId = `data-v-${shortId}`
|
||||
|
||||
const defaultCompiler = ssr ? (CompilerSSR as TemplateCompiler) : CompilerDOM
|
||||
const defaultCompiler = vapor
|
||||
? // TODO ssr
|
||||
(CompilerVapor as TemplateCompiler)
|
||||
: ssr
|
||||
? (CompilerSSR as TemplateCompiler)
|
||||
: CompilerDOM
|
||||
compiler = compiler || defaultCompiler
|
||||
|
||||
if (compiler !== defaultCompiler) {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"vite": "^5.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/repl": "^3.1.1",
|
||||
"@vue/repl": "4.0.0-alpha.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"jszip": "^3.10.1",
|
||||
"vue": "workspace:*"
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import Header from './Header.vue'
|
||||
import { Repl, ReplStore, SFCOptions } from '@vue/repl'
|
||||
import {
|
||||
Repl,
|
||||
type SFCOptions,
|
||||
useStore,
|
||||
useVueImportMap,
|
||||
mergeImportMap,
|
||||
File,
|
||||
StoreState,
|
||||
} from '@vue/repl'
|
||||
import type Monaco from '@vue/repl/monaco-editor'
|
||||
import type CodeMirror from '@vue/repl/codemirror-editor'
|
||||
import { ref, watchEffect, onMounted } from 'vue'
|
||||
import { shallowRef } from 'vue'
|
||||
import { ref, watchEffect, onMounted, computed, shallowRef, watch } from 'vue'
|
||||
|
||||
const EditorComponent = shallowRef<typeof Monaco | typeof CodeMirror>()
|
||||
|
||||
|
@ -26,78 +33,135 @@ const setVH = () => {
|
|||
window.addEventListener('resize', setVH)
|
||||
setVH()
|
||||
|
||||
const useProdMode = ref(false)
|
||||
const useSSRMode = ref(false)
|
||||
const useVaporMode = ref(false)
|
||||
|
||||
const {
|
||||
vueVersion,
|
||||
productionMode,
|
||||
importMap: vueImportMap,
|
||||
} = useVueImportMap({
|
||||
runtimeDev: import.meta.env.PROD
|
||||
? `${location.origin}/vue.runtime.esm-browser.js`
|
||||
: `${location.origin}/src/vue-dev-proxy`,
|
||||
runtimeProd: import.meta.env.PROD
|
||||
? `${location.origin}/vue.runtime.esm-browser.prod.js`
|
||||
: `${location.origin}/src/vue-dev-proxy-prod`,
|
||||
serverRenderer: import.meta.env.PROD
|
||||
? `${location.origin}/server-renderer.esm-browser.js`
|
||||
: `${location.origin}/src/vue-server-renderer-dev-proxy`,
|
||||
})
|
||||
|
||||
const importMap = computed(() =>
|
||||
mergeImportMap(vueImportMap.value, {
|
||||
imports: {
|
||||
'vue/vapor': import.meta.env.PROD
|
||||
? `${location.origin}/vue-vapor.esm-browser.js`
|
||||
: `${location.origin}/src/vue-vapor-dev-proxy`,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
let hash = location.hash.slice(1)
|
||||
if (hash.startsWith('__DEV__')) {
|
||||
hash = hash.slice(7)
|
||||
useProdMode.value = false
|
||||
productionMode.value = false
|
||||
}
|
||||
if (hash.startsWith('__PROD__')) {
|
||||
hash = hash.slice(8)
|
||||
useProdMode.value = true
|
||||
productionMode.value = true
|
||||
}
|
||||
if (hash.startsWith('__SSR__')) {
|
||||
hash = hash.slice(7)
|
||||
useSSRMode.value = true
|
||||
}
|
||||
if (hash.startsWith('__VAPOR__')) {
|
||||
hash = hash.slice(9)
|
||||
useVaporMode.value = true
|
||||
}
|
||||
|
||||
const store = new ReplStore({
|
||||
serializedState: hash,
|
||||
productionMode: useProdMode.value,
|
||||
defaultVueRuntimeURL: import.meta.env.PROD
|
||||
? `${location.origin}/vue.runtime.esm-browser.js`
|
||||
: `${location.origin}/src/vue-dev-proxy`,
|
||||
defaultVueRuntimeProdURL: import.meta.env.PROD
|
||||
? `${location.origin}/vue.runtime.esm-browser.prod.js`
|
||||
: `${location.origin}/src/vue-dev-proxy-prod`,
|
||||
defaultVueServerRendererURL: import.meta.env.PROD
|
||||
? `${location.origin}/server-renderer.esm-browser.js`
|
||||
: `${location.origin}/src/vue-server-renderer-dev-proxy`,
|
||||
})
|
||||
const files: StoreState['files'] = ref(Object.create(null))
|
||||
const mainFile = ref('src/App.vue')
|
||||
|
||||
// enable experimental features
|
||||
const sfcOptions: SFCOptions = {
|
||||
script: {
|
||||
inlineTemplate: useProdMode.value,
|
||||
isProd: useProdMode.value,
|
||||
propsDestructure: true,
|
||||
},
|
||||
style: {
|
||||
isProd: useProdMode.value,
|
||||
},
|
||||
template: {
|
||||
isProd: useProdMode.value,
|
||||
compilerOptions: {
|
||||
isCustomElement: (tag: string) => tag === 'mjx-container',
|
||||
const sfcOptions = computed(
|
||||
(): SFCOptions => ({
|
||||
script: {
|
||||
inlineTemplate: productionMode.value,
|
||||
isProd: productionMode.value,
|
||||
propsDestructure: true,
|
||||
},
|
||||
style: {
|
||||
isProd: productionMode.value,
|
||||
},
|
||||
template: {
|
||||
vapor: useVaporMode.value,
|
||||
isProd: productionMode.value,
|
||||
compilerOptions: {
|
||||
isCustomElement: (tag: string) => tag === 'mjx-container',
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
const store = useStore(
|
||||
{
|
||||
files,
|
||||
vueVersion,
|
||||
builtinImportMap: importMap,
|
||||
sfcOptions,
|
||||
mainFile,
|
||||
},
|
||||
}
|
||||
hash,
|
||||
)
|
||||
// @ts-expect-error
|
||||
globalThis.store = store
|
||||
|
||||
watch(
|
||||
useVaporMode,
|
||||
() => {
|
||||
if (useVaporMode.value) {
|
||||
files.value['src/index.html'] = new File(
|
||||
'src/index.html',
|
||||
`<script type="module">
|
||||
import { render } from 'vue/vapor'
|
||||
import App from './App.vue'
|
||||
render(App, {}, '#app')` +
|
||||
'<' +
|
||||
'/script>' +
|
||||
`<div id="app"></div>`,
|
||||
true,
|
||||
)
|
||||
mainFile.value = 'src/index.html'
|
||||
store.activeFile = files.value['src/App.vue']
|
||||
} else if (files.value['src/index.html']?.hidden) {
|
||||
delete files.value['src/index.html']
|
||||
mainFile.value = 'src/App.vue'
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// persist state
|
||||
watchEffect(() => {
|
||||
const newHash = store
|
||||
.serialize()
|
||||
.replace(/^#/, useVaporMode.value ? `#__VAPOR__` : `#`)
|
||||
.replace(/^#/, useSSRMode.value ? `#__SSR__` : `#`)
|
||||
.replace(/^#/, useProdMode.value ? `#__PROD__` : `#`)
|
||||
.replace(/^#/, productionMode.value ? `#__PROD__` : `#`)
|
||||
history.replaceState({}, '', newHash)
|
||||
})
|
||||
|
||||
function toggleProdMode() {
|
||||
const isProd = (useProdMode.value = !useProdMode.value)
|
||||
sfcOptions.script!.inlineTemplate =
|
||||
sfcOptions.script!.isProd =
|
||||
sfcOptions.template!.isProd =
|
||||
sfcOptions.style!.isProd =
|
||||
isProd
|
||||
store.toggleProduction()
|
||||
store.setFiles(store.getFiles())
|
||||
productionMode.value = !productionMode.value
|
||||
}
|
||||
|
||||
function toggleSSR() {
|
||||
useSSRMode.value = !useSSRMode.value
|
||||
store.setFiles(store.getFiles())
|
||||
}
|
||||
|
||||
function toggleVapor() {
|
||||
useVaporMode.value = !useVaporMode.value
|
||||
}
|
||||
|
||||
function reloadPage() {
|
||||
|
@ -117,11 +181,13 @@ onMounted(() => {
|
|||
<template>
|
||||
<Header
|
||||
:store="store"
|
||||
:prod="useProdMode"
|
||||
:prod="productionMode"
|
||||
:ssr="useSSRMode"
|
||||
:vapor="useVaporMode"
|
||||
@toggle-theme="toggleTheme"
|
||||
@toggle-prod="toggleProdMode"
|
||||
@toggle-ssr="toggleSSR"
|
||||
@toggle-vapor="toggleVapor"
|
||||
@reload-page="reloadPage"
|
||||
/>
|
||||
<Repl
|
||||
|
@ -135,7 +201,6 @@ onMounted(() => {
|
|||
:store="store"
|
||||
:showCompileOutput="true"
|
||||
:autoResize="true"
|
||||
:sfcOptions="sfcOptions"
|
||||
:clearConsole="false"
|
||||
:preview-options="{
|
||||
customCode: {
|
||||
|
|
|
@ -14,11 +14,13 @@ const props = defineProps<{
|
|||
store: ReplStore
|
||||
prod: boolean
|
||||
ssr: boolean
|
||||
vapor: boolean
|
||||
}>()
|
||||
const emit = defineEmits([
|
||||
'toggle-theme',
|
||||
'toggle-ssr',
|
||||
'toggle-prod',
|
||||
'toggle-vapor',
|
||||
'reload-page',
|
||||
])
|
||||
|
||||
|
@ -27,7 +29,7 @@ const { store } = props
|
|||
const currentCommit = __COMMIT__
|
||||
const vueVersion = ref(`@${currentCommit}`)
|
||||
|
||||
const vueURL = store.getImportMap().imports.vue
|
||||
const vueURL = store.getImportMap().imports?.vue
|
||||
if (vueURL && !vueURL.startsWith(location.origin)) {
|
||||
const versionMatch = vueURL.match(/runtime-dom@([^/]+)/)
|
||||
if (versionMatch) vueVersion.value = versionMatch[1]
|
||||
|
@ -35,12 +37,12 @@ if (vueURL && !vueURL.startsWith(location.origin)) {
|
|||
|
||||
async function setVueVersion(v: string) {
|
||||
vueVersion.value = `loading...`
|
||||
await store.setVueVersion(v)
|
||||
store.vueVersion = v
|
||||
vueVersion.value = v
|
||||
}
|
||||
|
||||
function resetVueVersion() {
|
||||
store.resetVueVersion()
|
||||
store.vueVersion = undefined
|
||||
vueVersion.value = `@${currentCommit}`
|
||||
}
|
||||
|
||||
|
@ -73,7 +75,7 @@ function toggleDark() {
|
|||
</h1>
|
||||
<div class="links">
|
||||
<VersionSelect
|
||||
v-model="store.state.typescriptVersion"
|
||||
v-model="store.typescriptVersion"
|
||||
pkg="typescript"
|
||||
label="TypeScript Version"
|
||||
/>
|
||||
|
@ -102,6 +104,14 @@ function toggleDark() {
|
|||
>
|
||||
<span>{{ prod ? 'PROD' : 'DEV' }}</span>
|
||||
</button>
|
||||
<button
|
||||
title="Toggle vapor mode"
|
||||
class="toggle-vapor"
|
||||
:class="{ enabled: vapor }"
|
||||
@click="$emit('toggle-vapor')"
|
||||
>
|
||||
<span>{{ vapor ? 'VAPOR ON' : 'VAPOR OFF' }}</span>
|
||||
</button>
|
||||
<button
|
||||
title="Toggle server rendering mode"
|
||||
class="toggle-ssr"
|
||||
|
@ -202,6 +212,7 @@ h1 img {
|
|||
}
|
||||
|
||||
.toggle-prod span,
|
||||
.toggle-vapor span,
|
||||
.toggle-ssr span {
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
|
@ -226,6 +237,15 @@ h1 img {
|
|||
background-color: var(--green);
|
||||
}
|
||||
|
||||
.toggle-vapor span {
|
||||
background-color: var(--btn-bg);
|
||||
}
|
||||
|
||||
.toggle-vapor.enabled span {
|
||||
color: #fff;
|
||||
background-color: var(--green);
|
||||
}
|
||||
|
||||
.toggle-dark svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
// serve vue/vapor to the iframe sandbox during dev.
|
||||
export * from 'vue/vapor'
|
|
@ -52,6 +52,7 @@ function copyVuePlugin(): Plugin {
|
|||
copyFile(`../vue/dist/vue.runtime.esm-browser.js`)
|
||||
copyFile(`../vue/dist/vue.runtime.esm-browser.prod.js`)
|
||||
copyFile(`../server-renderer/dist/server-renderer.esm-browser.js`)
|
||||
copyFile(`../vue-vapor/dist/vue-vapor.esm-browser.js`)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -219,6 +219,9 @@ importers:
|
|||
'@vue/compiler-ssr':
|
||||
specifier: workspace:*
|
||||
version: link:../compiler-ssr
|
||||
'@vue/compiler-vapor':
|
||||
specifier: workspace:*
|
||||
version: link:../compiler-vapor
|
||||
'@vue/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../shared
|
||||
|
@ -368,8 +371,8 @@ importers:
|
|||
packages/sfc-playground:
|
||||
dependencies:
|
||||
'@vue/repl':
|
||||
specifier: ^3.1.1
|
||||
version: 3.3.0
|
||||
specifier: 4.0.0-alpha.0
|
||||
version: 4.0.0-alpha.0
|
||||
file-saver:
|
||||
specifier: ^2.0.5
|
||||
version: 2.0.5
|
||||
|
@ -1634,8 +1637,8 @@ packages:
|
|||
engines: {node: '>= 0.12.0'}
|
||||
dev: true
|
||||
|
||||
/@vue/repl@3.3.0:
|
||||
resolution: {integrity: sha512-A9tdO7obt/kpFUHdgGoRnan6bZjfz/WAJ5+DpPkvgNEc960W+bJraURv8MUVtH2Id/byWotKbUve2jTakiccSw==}
|
||||
/@vue/repl@4.0.0-alpha.0:
|
||||
resolution: {integrity: sha512-kGgnon2yV1y0eKeWatys4by32XXCDSdq31Rwx0cd8xXAIK0GIL0AeSMCvVUrNE2ke8rFVYe6xMmpOd1iCcM0Zg==}
|
||||
dev: false
|
||||
|
||||
/@zeit/schemas@2.29.0:
|
||||
|
|
Loading…
Reference in New Issue