refactor(sfc-playground): upgrade `@vue/repl` (#10310)

This commit is contained in:
三咲智子 Kevin Deng 2024-02-20 21:47:09 +08:00 committed by GitHub
parent 3199189901
commit fe5d919b0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 67 additions and 83 deletions

View File

@ -7,8 +7,6 @@
<link rel="icon" type="image/svg" href="/logo.svg" /> <link rel="icon" type="image/svg" href="/logo.svg" />
<title>Vue SFC Playground</title> <title>Vue SFC Playground</title>
<script> <script>
// process shim for old versions of @vue/compiler-sfc dependency
window.process = { env: {} }
const savedPreferDark = localStorage.getItem('vue-sfc-playground-prefer-dark') const savedPreferDark = localStorage.getItem('vue-sfc-playground-prefer-dark')
if ( if (
savedPreferDark === 'true' || savedPreferDark === 'true' ||

View File

@ -13,7 +13,7 @@
"vite": "^5.0.12" "vite": "^5.0.12"
}, },
"dependencies": { "dependencies": {
"@vue/repl": "^3.1.1", "@vue/repl": "^4.1.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"vue": "workspace:*" "vue": "workspace:*"

View File

@ -1,22 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import Header from './Header.vue' import Header from './Header.vue'
import { Repl, ReplStore, SFCOptions } from '@vue/repl' import { Repl, useStore, SFCOptions, useVueImportMap } from '@vue/repl'
import type Monaco from '@vue/repl/monaco-editor' import Monaco from '@vue/repl/monaco-editor'
import type CodeMirror from '@vue/repl/codemirror-editor' import { ref, watchEffect, onMounted, computed } from 'vue'
import { ref, watchEffect, onMounted } from 'vue'
import { shallowRef } from 'vue'
const EditorComponent = shallowRef<typeof Monaco | typeof CodeMirror>()
if (import.meta.env.DEV) {
import('@vue/repl/codemirror-editor').then(
mod => (EditorComponent.value = mod.default),
)
} else {
import('@vue/repl/monaco-editor').then(
mod => (EditorComponent.value = mod.default),
)
}
const replRef = ref<InstanceType<typeof Repl>>() const replRef = ref<InstanceType<typeof Repl>>()
@ -26,78 +12,80 @@ const setVH = () => {
window.addEventListener('resize', setVH) window.addEventListener('resize', setVH)
setVH() setVH()
const useProdMode = ref(false)
const useSSRMode = ref(false) const useSSRMode = ref(false)
const { productionMode, vueVersion, importMap } = 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`,
})
let hash = location.hash.slice(1) let hash = location.hash.slice(1)
if (hash.startsWith('__DEV__')) { if (hash.startsWith('__DEV__')) {
hash = hash.slice(7) hash = hash.slice(7)
useProdMode.value = false productionMode.value = false
} }
if (hash.startsWith('__PROD__')) { if (hash.startsWith('__PROD__')) {
hash = hash.slice(8) hash = hash.slice(8)
useProdMode.value = true productionMode.value = true
} }
if (hash.startsWith('__SSR__')) { if (hash.startsWith('__SSR__')) {
hash = hash.slice(7) hash = hash.slice(7)
useSSRMode.value = true useSSRMode.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`,
})
// enable experimental features // enable experimental features
const sfcOptions: SFCOptions = { const sfcOptions = computed(
script: { (): SFCOptions => ({
inlineTemplate: useProdMode.value, script: {
isProd: useProdMode.value, inlineTemplate: productionMode.value,
propsDestructure: true, isProd: productionMode.value,
}, propsDestructure: true,
style: {
isProd: useProdMode.value,
},
template: {
isProd: useProdMode.value,
compilerOptions: {
isCustomElement: (tag: string) => tag === 'mjx-container',
}, },
style: {
isProd: productionMode.value,
},
template: {
isProd: productionMode.value,
compilerOptions: {
isCustomElement: (tag: string) => tag === 'mjx-container',
},
},
}),
)
const store = useStore(
{
builtinImportMap: importMap,
vueVersion,
sfcOptions,
}, },
} hash,
)
// @ts-expect-error
globalThis.store = store
// persist state // persist state
watchEffect(() => { watchEffect(() => {
const newHash = store const newHash = store
.serialize() .serialize()
.replace(/^#/, useSSRMode.value ? `#__SSR__` : `#`) .replace(/^#/, useSSRMode.value ? `#__SSR__` : `#`)
.replace(/^#/, useProdMode.value ? `#__PROD__` : `#`) .replace(/^#/, productionMode.value ? `#__PROD__` : `#`)
history.replaceState({}, '', newHash) history.replaceState({}, '', newHash)
}) })
function toggleProdMode() { function toggleProdMode() {
const isProd = (useProdMode.value = !useProdMode.value) productionMode.value = !productionMode.value
sfcOptions.script!.inlineTemplate =
sfcOptions.script!.isProd =
sfcOptions.template!.isProd =
sfcOptions.style!.isProd =
isProd
store.toggleProduction()
store.setFiles(store.getFiles())
} }
function toggleSSR() { function toggleSSR() {
useSSRMode.value = !useSSRMode.value useSSRMode.value = !useSSRMode.value
store.setFiles(store.getFiles())
} }
function reloadPage() { function reloadPage() {
@ -111,13 +99,16 @@ function toggleTheme(isDark: boolean) {
onMounted(() => { onMounted(() => {
const cls = document.documentElement.classList const cls = document.documentElement.classList
toggleTheme(cls.contains('dark')) toggleTheme(cls.contains('dark'))
// @ts-expect-error process shim for old versions of @vue/compiler-sfc dependency
window.process = { env: {} }
}) })
</script> </script>
<template> <template>
<Header <Header
:store="store" :store="store"
:prod="useProdMode" :prod="productionMode"
:ssr="useSSRMode" :ssr="useSSRMode"
@toggle-theme="toggleTheme" @toggle-theme="toggleTheme"
@toggle-prod="toggleProdMode" @toggle-prod="toggleProdMode"
@ -125,17 +116,15 @@ onMounted(() => {
@reload-page="reloadPage" @reload-page="reloadPage"
/> />
<Repl <Repl
v-if="EditorComponent"
ref="replRef" ref="replRef"
:theme="theme" :theme="theme"
:editor="EditorComponent" :editor="Monaco"
@keydown.ctrl.s.prevent @keydown.ctrl.s.prevent
@keydown.meta.s.prevent @keydown.meta.s.prevent
:ssr="useSSRMode" :ssr="useSSRMode"
:store="store" :store="store"
:showCompileOutput="true" :showCompileOutput="true"
:autoResize="true" :autoResize="true"
:sfcOptions="sfcOptions"
:clearConsole="false" :clearConsole="false"
:preview-options="{ :preview-options="{
customCode: { customCode: {

View File

@ -1,13 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import type { ReplStore } from '@vue/repl'
import { downloadProject } from './download/download' import { downloadProject } from './download/download'
import { ref } from 'vue'
import Sun from './icons/Sun.vue' import Sun from './icons/Sun.vue'
import Moon from './icons/Moon.vue' import Moon from './icons/Moon.vue'
import Share from './icons/Share.vue' import Share from './icons/Share.vue'
import Download from './icons/Download.vue' import Download from './icons/Download.vue'
import GitHub from './icons/GitHub.vue' import GitHub from './icons/GitHub.vue'
import Reload from './icons/Reload.vue' import Reload from './icons/Reload.vue'
import type { ReplStore } from '@vue/repl'
import VersionSelect from './VersionSelect.vue' import VersionSelect from './VersionSelect.vue'
const props = defineProps<{ const props = defineProps<{
@ -25,23 +25,20 @@ const emit = defineEmits([
const { store } = props const { store } = props
const currentCommit = __COMMIT__ const currentCommit = __COMMIT__
const vueVersion = ref(`@${currentCommit}`)
const vueURL = store.getImportMap().imports.vue const vueVersion = computed(() => {
if (vueURL && !vueURL.startsWith(location.origin)) { if (store.loading) {
const versionMatch = vueURL.match(/runtime-dom@([^/]+)/) return 'loading...'
if (versionMatch) vueVersion.value = versionMatch[1] }
} return store.vueVersion || `@${__COMMIT__}`
})
async function setVueVersion(v: string) { async function setVueVersion(v: string) {
vueVersion.value = `loading...` store.vueVersion = v
await store.setVueVersion(v)
vueVersion.value = v
} }
function resetVueVersion() { function resetVueVersion() {
store.resetVueVersion() store.vueVersion = null
vueVersion.value = `@${currentCommit}`
} }
async function copyLink(e: MouseEvent) { async function copyLink(e: MouseEvent) {
@ -73,7 +70,7 @@ function toggleDark() {
</h1> </h1>
<div class="links"> <div class="links">
<VersionSelect <VersionSelect
v-model="store.state.typescriptVersion" v-model="store.typescriptVersion"
pkg="typescript" pkg="typescript"
label="TypeScript Version" label="TypeScript Version"
/> />

View File

@ -347,8 +347,8 @@ importers:
packages/sfc-playground: packages/sfc-playground:
dependencies: dependencies:
'@vue/repl': '@vue/repl':
specifier: ^3.1.1 specifier: ^4.1.0
version: 3.1.1 version: 4.1.0
file-saver: file-saver:
specifier: ^2.0.5 specifier: ^2.0.5
version: 2.0.5 version: 2.0.5
@ -1774,8 +1774,8 @@ packages:
engines: {node: '>= 0.12.0'} engines: {node: '>= 0.12.0'}
dev: true dev: true
/@vue/repl@3.1.1: /@vue/repl@4.1.0:
resolution: {integrity: sha512-9nJImsUeywU2MTqvzf4ueqWC49UC3LVXVPk1HmoNnLVumfWXv+w5ytuSA//fxb/N/O5vCqE0UV63/dYvnCZpwQ==} resolution: {integrity: sha512-4ZNEQWlLjl1Sq+WFiACm5siMdwUAmmqOES4XDgZRRFYeeW/BfabO9I6fpU+Y0zO9HFzKb8dwUUH0e0LK7mIYeg==}
dev: false dev: false
/@zeit/schemas@2.29.0: /@zeit/schemas@2.29.0: