wip(vapor): per-file vapor support in sfc playground

This commit is contained in:
Evan You 2025-02-03 12:22:45 +08:00
parent c2a91a8daf
commit aa84afc199
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
8 changed files with 26 additions and 74 deletions

View File

@ -13,7 +13,7 @@
"vite": "catalog:"
},
"dependencies": {
"@vue/repl": "^4.4.3",
"@vue/repl": "^4.5.0",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"vue": "workspace:*"

View File

@ -5,11 +5,10 @@ import {
type SFCOptions,
useStore,
useVueImportMap,
File,
StoreState,
} from '@vue/repl'
import Monaco from '@vue/repl/monaco-editor'
import { ref, watchEffect, onMounted, computed, watch } from 'vue'
import { ref, watchEffect, onMounted, computed } from 'vue'
const replRef = ref<InstanceType<typeof Repl>>()
@ -20,7 +19,6 @@ window.addEventListener('resize', setVH)
setVH()
const useSSRMode = ref(false)
const useVaporMode = ref(true)
const AUTO_SAVE_STORAGE_KEY = 'vue-sfc-playground-auto-save'
const initAutoSave: boolean = JSON.parse(
@ -31,16 +29,12 @@ const autoSave = ref(initAutoSave)
const { vueVersion, productionMode, importMap } = useVueImportMap({
runtimeDev: () => {
return import.meta.env.PROD
? useVaporMode.value
? `${location.origin}/vue.runtime-with-vapor.esm-browser.js`
: `${location.origin}/vue.runtime.esm-browser.js`
? `${location.origin}/vue.runtime-with-vapor.esm-browser.js`
: `${location.origin}/src/vue-dev-proxy`
},
runtimeProd: () => {
return import.meta.env.PROD
? useVaporMode.value
? `${location.origin}/vue.runtime-with-vapor.esm-browser.prod.js`
: `${location.origin}/vue.runtime.esm-browser.prod.js`
? `${location.origin}/vue.runtime-with-vapor.esm-browser.prod.js`
: `${location.origin}/src/vue-dev-proxy-prod`
},
serverRenderer: import.meta.env.PROD
@ -61,10 +55,6 @@ if (hash.startsWith('__SSR__')) {
hash = hash.slice(7)
useSSRMode.value = true
}
if (hash.startsWith('__VAPOR__')) {
hash = hash.slice(9)
useVaporMode.value = true
}
const files: StoreState['files'] = ref(Object.create(null))
@ -75,13 +65,13 @@ const sfcOptions = computed(
inlineTemplate: productionMode.value,
isProd: productionMode.value,
propsDestructure: true,
vapor: useVaporMode.value,
// vapor: useVaporMode.value,
},
style: {
isProd: productionMode.value,
},
template: {
vapor: useVaporMode.value,
// vapor: useVaporMode.value,
isProd: productionMode.value,
compilerOptions: {
isCustomElement: (tag: string) =>
@ -103,38 +93,10 @@ const store = useStore(
// @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 { createVaporApp } from 'vue'
import App from './App.vue'
createVaporApp(App).mount('#app')` +
'<' +
'/script>' +
`<div id="app"></div>`,
true,
)
store.mainFile = 'src/index.html'
} else if (files.value['src/index.html']?.hidden) {
delete files.value['src/index.html']
store.mainFile = 'src/App.vue'
if (store.activeFile.filename === 'src/index.html') {
store.activeFile = files.value['src/App.vue']
}
}
},
{ immediate: true },
)
// persist state
watchEffect(() => {
const newHash = store
.serialize()
.replace(/^#/, useVaporMode.value ? `#__VAPOR__` : `#`)
.replace(/^#/, useSSRMode.value ? `#__SSR__` : `#`)
.replace(/^#/, productionMode.value ? `#__PROD__` : `#`)
history.replaceState({}, '', newHash)
@ -148,10 +110,6 @@ function toggleSSR() {
useSSRMode.value = !useSSRMode.value
}
function toggleVapor() {
useVaporMode.value = !useVaporMode.value
}
function toggleAutoSave() {
autoSave.value = !autoSave.value
localStorage.setItem(AUTO_SAVE_STORAGE_KEY, String(autoSave.value))
@ -179,14 +137,12 @@ onMounted(() => {
:store="store"
:prod="productionMode"
:ssr="useSSRMode"
:vapor="useVaporMode"
:autoSave="autoSave"
:theme="theme"
@toggle-theme="toggleTheme"
@toggle-prod="toggleProdMode"
@toggle-ssr="toggleSSR"
@toggle-autosave="toggleAutoSave"
@toggle-vapor="toggleVapor"
@reload-page="reloadPage"
/>
<Repl
@ -204,8 +160,10 @@ onMounted(() => {
:clearConsole="false"
:preview-options="{
customCode: {
importCode: `import { initCustomFormatter } from 'vue'`,
useCode: `if (window.devtoolsFormatters) {
importCode: `import { initCustomFormatter, vaporInteropPlugin } from 'vue'`,
useCode: `
app.use(vaporInteropPlugin)
if (window.devtoolsFormatters) {
const index = window.devtoolsFormatters.findIndex((v) => v.__vue_custom_formatter)
window.devtoolsFormatters.splice(index, 1)
initCustomFormatter()

View File

@ -14,7 +14,6 @@ const props = defineProps<{
store: ReplStore
prod: boolean
ssr: boolean
vapor: boolean
autoSave: boolean
theme: 'dark' | 'light'
}>()
@ -105,14 +104,6 @@ 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"

View File

@ -874,6 +874,7 @@ export function compileScript(
scoped: sfc.styles.some(s => s.scoped),
isProd: options.isProd,
ssrCssVars: sfc.cssVars,
vapor,
compilerOptions: {
...(options.templateOptions &&
options.templateOptions.compilerOptions),
@ -942,9 +943,6 @@ export function compileScript(
: `export default`
let runtimeOptions = ``
if (vapor) {
runtimeOptions += `\n __vapor: true,`
}
if (!ctx.hasDefaultExportName && filename && filename !== DEFAULT_FILENAME) {
const match = filename.match(/([^/\\]+)\.\w+$/)
if (match) {
@ -992,6 +990,10 @@ export function compileScript(
)
ctx.s.appendRight(endOffset, `})`)
} else {
// in TS, defineVaporComponent adds the option already
if (vapor) {
runtimeOptions += `\n __vapor: true,`
}
if (defaultExport || definedOptions) {
// without TS, can't rely on rest spread, so we use Object.assign
// export default Object.assign(__default__, { ... })

View File

@ -209,11 +209,10 @@ function doCompileTemplate({
const shortId = id.replace(/^data-v-/, '')
const longId = `data-v-${shortId}`
const defaultCompiler = vapor
? // TODO ssr
(CompilerVapor as TemplateCompiler)
: ssr
? (CompilerSSR as TemplateCompiler)
const defaultCompiler = ssr
? (CompilerSSR as TemplateCompiler)
: vapor
? (CompilerVapor as TemplateCompiler)
: CompilerDOM
compiler = compiler || defaultCompiler

View File

@ -3,5 +3,6 @@ import type { VaporComponent } from './component'
/*! #__NO_SIDE_EFFECTS__ */
export function defineVaporComponent(comp: VaporComponent): VaporComponent {
// TODO type inference
comp.__vapor = true
return comp
}

View File

@ -39,6 +39,7 @@ const vaporInVDOMInterface: VaporInVDOMInterface = {
update(n1: VNode, n2: VNode) {
n2.component = n1.component
// TODO if has patchFlag, do simple diff to skip unnecessary updates
;(n2.component as any as VaporComponentInstance).rawPropsRef!.value =
n2.props
},

View File

@ -229,8 +229,8 @@ importers:
packages-private/sfc-playground:
dependencies:
'@vue/repl':
specifier: ^4.4.3
version: 4.4.3
specifier: ^4.5.0
version: 4.5.0
file-saver:
specifier: ^2.0.5
version: 2.0.5
@ -1531,8 +1531,8 @@ packages:
'@vue/reactivity@3.5.13':
resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==}
'@vue/repl@4.4.3':
resolution: {integrity: sha512-MKIaWgmpaDSfcQrzgsoEFW4jpFbdPYFDn9LBvXFQqEUcosheP9IoUcj/u4omp72oxsecFF5YO4/ssp4aaR8e+g==}
'@vue/repl@4.5.0':
resolution: {integrity: sha512-nWQfTzBePs5zN4qIK+vwEMEDHCuWWx2AY0utun37cSD2Qi4C84dlTtO/OL0xDzBB8Ob7250KYzIzDP3N3l3qLg==}
'@vue/runtime-core@3.5.13':
resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==}
@ -4730,7 +4730,7 @@ snapshots:
dependencies:
'@vue/shared': 3.5.13
'@vue/repl@4.4.3': {}
'@vue/repl@4.5.0': {}
'@vue/runtime-core@3.5.13':
dependencies: