Skip to content

Commit

Permalink
feat: repl (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz authored Jan 21, 2024
1 parent 32604cf commit 51098cf
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 56 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-sfc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
10 changes: 9 additions & 1 deletion packages/compiler-sfc/src/compileTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -55,6 +56,7 @@ export interface SFCTemplateCompileOptions {
scoped?: boolean
slotted?: boolean
isProd?: boolean
vapor?: boolean
ssr?: boolean
ssrCssVars?: string[]
inMap?: RawSourceMap
Expand Down Expand Up @@ -171,6 +173,7 @@ function doCompileTemplate({
source,
ast: inAST,
ssr = false,
vapor = false,
ssrCssVars,
isProd = false,
compiler,
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion packages/sfc-playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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:*"
Expand Down
155 changes: 110 additions & 45 deletions packages/sfc-playground/src/App.vue
Original file line number Diff line number Diff line change
@@ -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>()
Expand All @@ -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() {
Expand All @@ -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
Expand All @@ -135,7 +201,6 @@ onMounted(() => {
:store="store"
:showCompileOutput="true"
:autoResize="true"
:sfcOptions="sfcOptions"
:clearConsole="false"
:preview-options="{
customCode: {
Expand Down
28 changes: 24 additions & 4 deletions packages/sfc-playground/src/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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',
])
Expand All @@ -27,20 +29,20 @@ 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]
}
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}`
}
Expand Down Expand Up @@ -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"
/>
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -202,6 +212,7 @@ h1 img {
}
.toggle-prod span,
.toggle-vapor span,
.toggle-ssr span {
font-size: 12px;
border-radius: 4px;
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions packages/sfc-playground/src/vue-vapor-dev-proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// serve vue/vapor to the iframe sandbox during dev.
export * from 'vue/vapor'
1 change: 1 addition & 0 deletions packages/sfc-playground/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
},
}
}
Loading

0 comments on commit 51098cf

Please sign in to comment.