chore(sfc-playground): use standalone version of @vue/repl
This commit is contained in:
parent
4178d5d7d9
commit
d80d40a9c1
13
packages/global.d.ts
vendored
13
packages/global.d.ts
vendored
@ -1,3 +1,5 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
// Global compile-time constants
|
// Global compile-time constants
|
||||||
declare var __DEV__: boolean
|
declare var __DEV__: boolean
|
||||||
declare var __TEST__: boolean
|
declare var __TEST__: boolean
|
||||||
@ -25,10 +27,6 @@ declare namespace jest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.vue' {}
|
declare module '*.vue' {}
|
||||||
declare module '*?raw' {
|
|
||||||
const content: string
|
|
||||||
export default content
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module 'file-saver' {
|
declare module 'file-saver' {
|
||||||
export function saveAs(blob: any, name: any): void
|
export function saveAs(blob: any, name: any): void
|
||||||
@ -39,3 +37,10 @@ declare module 'stream/web' {
|
|||||||
const t: typeof TransformStream
|
const t: typeof TransformStream
|
||||||
export { r as ReadableStream, t as TransformStream }
|
export { r as ReadableStream, t as TransformStream }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '@vue/repl' {
|
||||||
|
import { ComponentOptions } from '@vue/runtime-core'
|
||||||
|
const Repl: ComponentOptions
|
||||||
|
const ReplStore: any
|
||||||
|
export { Repl, ReplStore }
|
||||||
|
}
|
||||||
|
@ -8,14 +8,12 @@
|
|||||||
"serve": "vite preview"
|
"serve": "vite preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/codemirror": "^0.0.108",
|
|
||||||
"@vitejs/plugin-vue": "^1.2.0",
|
"@vitejs/plugin-vue": "^1.2.0",
|
||||||
"codemirror": "^5.60.0",
|
|
||||||
"vite": "^2.4.0"
|
"vite": "^2.4.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vue/repl": "^0.1.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"jszip": "^3.6.0",
|
"jszip": "^3.6.0"
|
||||||
"sucrase": "^3.19.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,24 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Header from './Header.vue'
|
import Header from './Header.vue'
|
||||||
import SplitPane from './SplitPane.vue'
|
import { Repl, ReplStore } from '@vue/repl'
|
||||||
import Editor from './editor/Editor.vue'
|
import { watchEffect } from 'vue'
|
||||||
import Output from './output/Output.vue'
|
|
||||||
|
document.documentElement.style.setProperty('--vh', window.innerHeight + `px`)
|
||||||
|
|
||||||
|
const store = new ReplStore({
|
||||||
|
serializedState: location.hash.slice(1),
|
||||||
|
defaultVueRuntimeURL: import.meta.env.PROD
|
||||||
|
? `${location.origin}/vue.runtime.esm-browser.js`
|
||||||
|
: `${location.origin}/src/vue-dev-proxy`
|
||||||
|
})
|
||||||
|
|
||||||
|
// persist state
|
||||||
|
watchEffect(() => history.replaceState({}, '', store.serialize()))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Header />
|
<Header :store="store" />
|
||||||
<div class="wrapper">
|
<Repl :store="store" :showCompileOutput="true" />
|
||||||
<SplitPane>
|
|
||||||
<template #left>
|
|
||||||
<Editor />
|
|
||||||
</template>
|
|
||||||
<template #right>
|
|
||||||
<Output />
|
|
||||||
</template>
|
|
||||||
</SplitPane>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -24,18 +26,13 @@ body {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||||
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
color: var(--base);
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: #f8f8f8;
|
|
||||||
--base: #444;
|
--base: #444;
|
||||||
--nav-height: 50px;
|
--nav-height: 50px;
|
||||||
--font-code: 'Source Code Pro', monospace;
|
|
||||||
--color-branding: #3ca877;
|
|
||||||
--color-branding-dark: #416f9c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.vue-repl {
|
||||||
height: calc(100vh - var(--nav-height));
|
height: calc(var(--vh) - var(--nav-height));
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { downloadProject } from './download/download'
|
import { downloadProject } from './download/download'
|
||||||
import { setVersion, resetVersion } from './transform'
|
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const { store } = defineProps(['store'])
|
||||||
|
|
||||||
const currentCommit = __COMMIT__
|
const currentCommit = __COMMIT__
|
||||||
const activeVersion = ref(`@${currentCommit}`)
|
const activeVersion = ref(`@${currentCommit}`)
|
||||||
const publishedVersions = ref<string[]>()
|
const publishedVersions = ref<string[]>()
|
||||||
@ -17,13 +19,13 @@ async function toggle() {
|
|||||||
|
|
||||||
async function setVueVersion(v: string) {
|
async function setVueVersion(v: string) {
|
||||||
activeVersion.value = `loading...`
|
activeVersion.value = `loading...`
|
||||||
await setVersion(v)
|
await store.setVueVersion(v)
|
||||||
activeVersion.value = `v${v}`
|
activeVersion.value = `v${v}`
|
||||||
expanded.value = false
|
expanded.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetVueVersion() {
|
function resetVueVersion() {
|
||||||
resetVersion()
|
store.resetVueVersion()
|
||||||
activeVersion.value = `@${currentCommit}`
|
activeVersion.value = `@${currentCommit}`
|
||||||
expanded.value = false
|
expanded.value = false
|
||||||
}
|
}
|
||||||
@ -113,7 +115,7 @@ async function fetchVersions(): Promise<string[]> {
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button class="download" @click="downloadProject">
|
<button class="download" @click="downloadProject(store)">
|
||||||
<svg width="1.7em" height="1.7em" viewBox="0 0 24 24">
|
<svg width="1.7em" height="1.7em" viewBox="0 0 24 24">
|
||||||
<g fill="#626262">
|
<g fill="#626262">
|
||||||
<rect x="4" y="18" width="16" height="2" rx="1" ry="1" />
|
<rect x="4" y="18" width="16" height="2" rx="1" ry="1" />
|
||||||
@ -241,7 +243,7 @@ h1 img {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.versions a:hover {
|
.versions a:hover {
|
||||||
color: var(--color-branding);
|
color: #3ca877;
|
||||||
}
|
}
|
||||||
|
|
||||||
.versions.expanded {
|
.versions.expanded {
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref, watch } from 'vue'
|
|
||||||
import { CompilerError } from '@vue/compiler-sfc'
|
|
||||||
|
|
||||||
const props = defineProps(['err', 'warn'])
|
|
||||||
|
|
||||||
const dismissed = ref(false)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => [props.err, props.warn],
|
|
||||||
() => {
|
|
||||||
dismissed.value = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
function formatMessage(err: string | Error): string {
|
|
||||||
if (typeof err === 'string') {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
let msg = err.message
|
|
||||||
const loc = (err as CompilerError).loc
|
|
||||||
if (loc && loc.start) {
|
|
||||||
msg = `(${loc.start.line}:${loc.start.column}) ` + msg
|
|
||||||
}
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Transition name="fade">
|
|
||||||
<pre
|
|
||||||
v-if="!dismissed && (err || warn)"
|
|
||||||
class="msg"
|
|
||||||
:class="err ? 'err' : 'warn'"
|
|
||||||
@click="dismissed = true"
|
|
||||||
>{{ formatMessage(err || warn) }}</pre
|
|
||||||
>
|
|
||||||
</Transition>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.msg {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 8px;
|
|
||||||
right: 8px;
|
|
||||||
z-index: 10;
|
|
||||||
padding: 14px 20px;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-family: var(--font-code);
|
|
||||||
white-space: pre-wrap;
|
|
||||||
max-height: calc(100% - 50px);
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
.msg.err {
|
|
||||||
color: red;
|
|
||||||
border-color: red;
|
|
||||||
background-color: #ffd7d7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.msg.warn {
|
|
||||||
--color: rgb(105, 95, 27);
|
|
||||||
color: var(--color);
|
|
||||||
border-color: var(--color);
|
|
||||||
background-color: rgb(247, 240, 205);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-active,
|
|
||||||
.fade-leave-active {
|
|
||||||
transition: all 0.15s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-from,
|
|
||||||
.fade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translate(0, 10px);
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,87 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref, reactive } from 'vue'
|
|
||||||
|
|
||||||
const container = ref()
|
|
||||||
|
|
||||||
const state = reactive({
|
|
||||||
dragging: false,
|
|
||||||
split: 50
|
|
||||||
})
|
|
||||||
|
|
||||||
function boundSplit() {
|
|
||||||
const { split } = state
|
|
||||||
return split < 20 ? 20 : split > 80 ? 80 : split
|
|
||||||
}
|
|
||||||
|
|
||||||
let startPosition = 0
|
|
||||||
let startSplit = 0
|
|
||||||
|
|
||||||
function dragStart(e: MouseEvent) {
|
|
||||||
state.dragging = true
|
|
||||||
startPosition = e.pageX
|
|
||||||
startSplit = boundSplit()
|
|
||||||
}
|
|
||||||
|
|
||||||
function dragMove(e: MouseEvent) {
|
|
||||||
if (state.dragging) {
|
|
||||||
const position = e.pageX
|
|
||||||
const totalSize = container.value.offsetWidth
|
|
||||||
const dp = position - startPosition
|
|
||||||
state.split = startSplit + ~~((dp / totalSize) * 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dragEnd() {
|
|
||||||
state.dragging = false
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
ref="container"
|
|
||||||
class="split-pane"
|
|
||||||
:class="{ dragging: state.dragging }"
|
|
||||||
@mousemove="dragMove"
|
|
||||||
@mouseup="dragEnd"
|
|
||||||
@mouseleave="dragEnd"
|
|
||||||
>
|
|
||||||
<div class="left" :style="{ width: boundSplit() + '%' }">
|
|
||||||
<slot name="left" />
|
|
||||||
<div class="dragger" @mousedown.prevent="dragStart" />
|
|
||||||
</div>
|
|
||||||
<div class="right" :style="{ width: 100 - boundSplit() + '%' }">
|
|
||||||
<slot name="right" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.split-pane {
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.split-pane.dragging {
|
|
||||||
cursor: ew-resize;
|
|
||||||
}
|
|
||||||
.dragging .left,
|
|
||||||
.dragging .right {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
.left,
|
|
||||||
.right {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.left {
|
|
||||||
border-right: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
.dragger {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 99;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: -5px;
|
|
||||||
width: 10px;
|
|
||||||
cursor: ew-resize;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,83 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="editor" ref="el"></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, onMounted, watchEffect } from 'vue'
|
|
||||||
import { debounce } from '../utils'
|
|
||||||
import CodeMirror from './codemirror'
|
|
||||||
|
|
||||||
const el = ref()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
mode: {
|
|
||||||
type: String,
|
|
||||||
default: 'htmlmixed'
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
readonly: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits<(e: 'change', value: string) => void>()
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
const addonOptions = {
|
|
||||||
autoCloseBrackets: true,
|
|
||||||
autoCloseTags: true,
|
|
||||||
foldGutter: true,
|
|
||||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
|
|
||||||
}
|
|
||||||
|
|
||||||
const editor = CodeMirror(el.value!, {
|
|
||||||
value: '',
|
|
||||||
mode: props.mode,
|
|
||||||
readOnly: props.readonly,
|
|
||||||
tabSize: 2,
|
|
||||||
lineWrapping: true,
|
|
||||||
lineNumbers: true,
|
|
||||||
...addonOptions
|
|
||||||
})
|
|
||||||
|
|
||||||
editor.on('change', () => {
|
|
||||||
emit('change', editor.getValue())
|
|
||||||
})
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
editor.setValue(props.value)
|
|
||||||
})
|
|
||||||
watchEffect(() => {
|
|
||||||
editor.setOption('mode', props.mode)
|
|
||||||
})
|
|
||||||
|
|
||||||
window.addEventListener(
|
|
||||||
'resize',
|
|
||||||
debounce(() => {
|
|
||||||
editor.refresh()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
editor.refresh()
|
|
||||||
}, 50)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.editor {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
font-family: 'Source Code Pro', monospace;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,506 +0,0 @@
|
|||||||
/* BASICS */
|
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
--base: #545281;
|
|
||||||
--comment: hsl(210, 25%, 60%);
|
|
||||||
--keyword: #af4ab1;
|
|
||||||
--variable: #0055d1;
|
|
||||||
--function: #c25205;
|
|
||||||
--string: #2ba46d;
|
|
||||||
--number: #c25205;
|
|
||||||
--tags: #dd0000;
|
|
||||||
--qualifier: #ff6032;
|
|
||||||
--important: var(--string);
|
|
||||||
|
|
||||||
direction: ltr;
|
|
||||||
font-family: var(--font-code);
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PADDING */
|
|
||||||
|
|
||||||
.CodeMirror-lines {
|
|
||||||
padding: 4px 0; /* Vertical padding around content */
|
|
||||||
}
|
|
||||||
.CodeMirror pre {
|
|
||||||
padding: 0 4px; /* Horizontal padding of content */
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-scrollbar-filler,
|
|
||||||
.CodeMirror-gutter-filler {
|
|
||||||
background-color: white; /* The little square between H and V scrollbars */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* GUTTER */
|
|
||||||
|
|
||||||
.CodeMirror-gutters {
|
|
||||||
border-right: 1px solid #ddd;
|
|
||||||
background-color: transparent;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.CodeMirror-linenumber {
|
|
||||||
padding: 0 3px 0 5px;
|
|
||||||
min-width: 20px;
|
|
||||||
text-align: right;
|
|
||||||
color: var(--comment);
|
|
||||||
white-space: nowrap;
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-guttermarker {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
.CodeMirror-guttermarker-subtle {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FOLD GUTTER */
|
|
||||||
|
|
||||||
.CodeMirror-foldmarker {
|
|
||||||
color: #414141;
|
|
||||||
text-shadow: #ff9966 1px 1px 2px, #ff9966 -1px -1px 2px, #ff9966 1px -1px 2px,
|
|
||||||
#ff9966 -1px 1px 2px;
|
|
||||||
font-family: arial;
|
|
||||||
line-height: 0.3;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.CodeMirror-foldgutter {
|
|
||||||
width: 0.7em;
|
|
||||||
}
|
|
||||||
.CodeMirror-foldgutter-open,
|
|
||||||
.CodeMirror-foldgutter-folded {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.CodeMirror-foldgutter-open:after,
|
|
||||||
.CodeMirror-foldgutter-folded:after {
|
|
||||||
content: '>';
|
|
||||||
font-size: 0.8em;
|
|
||||||
opacity: 0.8;
|
|
||||||
transition: transform 0.2s;
|
|
||||||
display: inline-block;
|
|
||||||
top: -0.1em;
|
|
||||||
position: relative;
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
.CodeMirror-foldgutter-folded:after {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CURSOR */
|
|
||||||
|
|
||||||
.CodeMirror-cursor {
|
|
||||||
border-left: 1px solid black;
|
|
||||||
border-right: none;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
/* Shown when moving in bi-directional text */
|
|
||||||
.CodeMirror div.CodeMirror-secondarycursor {
|
|
||||||
border-left: 1px solid silver;
|
|
||||||
}
|
|
||||||
.cm-fat-cursor .CodeMirror-cursor {
|
|
||||||
width: auto;
|
|
||||||
border: 0 !important;
|
|
||||||
background: #7e7;
|
|
||||||
}
|
|
||||||
.cm-fat-cursor div.CodeMirror-cursors {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
.cm-fat-cursor-mark {
|
|
||||||
background-color: rgba(20, 255, 20, 0.5);
|
|
||||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
|
||||||
-moz-animation: blink 1.06s steps(1) infinite;
|
|
||||||
animation: blink 1.06s steps(1) infinite;
|
|
||||||
}
|
|
||||||
.cm-animate-fat-cursor {
|
|
||||||
width: auto;
|
|
||||||
border: 0;
|
|
||||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
|
||||||
-moz-animation: blink 1.06s steps(1) infinite;
|
|
||||||
animation: blink 1.06s steps(1) infinite;
|
|
||||||
background-color: #7e7;
|
|
||||||
}
|
|
||||||
@-moz-keyframes blink {
|
|
||||||
0% {
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-webkit-keyframes blink {
|
|
||||||
0% {
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes blink {
|
|
||||||
0% {
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-tab {
|
|
||||||
display: inline-block;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-rulers {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: -50px;
|
|
||||||
bottom: -20px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.CodeMirror-ruler {
|
|
||||||
border-left: 1px solid #ccc;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* DEFAULT THEME */
|
|
||||||
.cm-s-default.CodeMirror {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-header {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-quote {
|
|
||||||
color: #090;
|
|
||||||
}
|
|
||||||
.cm-negative {
|
|
||||||
color: #d44;
|
|
||||||
}
|
|
||||||
.cm-positive {
|
|
||||||
color: #292;
|
|
||||||
}
|
|
||||||
.cm-header,
|
|
||||||
.cm-strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.cm-em {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
.cm-link {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
.cm-strikethrough {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-s-default .cm-atom,
|
|
||||||
.cm-s-default .cm-def,
|
|
||||||
.cm-s-default .cm-property,
|
|
||||||
.cm-s-default .cm-variable-2,
|
|
||||||
.cm-s-default .cm-variable-3,
|
|
||||||
.cm-s-default .cm-punctuation {
|
|
||||||
color: var(--base);
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-hr,
|
|
||||||
.cm-s-default .cm-comment {
|
|
||||||
color: var(--comment);
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-attribute,
|
|
||||||
.cm-s-default .cm-keyword {
|
|
||||||
color: var(--keyword);
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-variable {
|
|
||||||
color: var(--variable);
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-bracket,
|
|
||||||
.cm-s-default .cm-tag {
|
|
||||||
color: var(--tags);
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-number {
|
|
||||||
color: var(--number);
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-string,
|
|
||||||
.cm-s-default .cm-string-2 {
|
|
||||||
color: var(--string);
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-type {
|
|
||||||
color: #085;
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-meta {
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-qualifier {
|
|
||||||
color: var(--qualifier);
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-builtin {
|
|
||||||
color: #7539ff;
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-link {
|
|
||||||
color: var(--flash);
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-error {
|
|
||||||
color: #ff008c;
|
|
||||||
}
|
|
||||||
.cm-invalidchar {
|
|
||||||
color: #ff008c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-composing {
|
|
||||||
border-bottom: 2px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Default styles for common addons */
|
|
||||||
|
|
||||||
div.CodeMirror span.CodeMirror-matchingbracket {
|
|
||||||
color: #0b0;
|
|
||||||
}
|
|
||||||
div.CodeMirror span.CodeMirror-nonmatchingbracket {
|
|
||||||
color: #a22;
|
|
||||||
}
|
|
||||||
.CodeMirror-matchingtag {
|
|
||||||
background: rgba(255, 150, 0, 0.3);
|
|
||||||
}
|
|
||||||
.CodeMirror-activeline-background {
|
|
||||||
background: #e8f2ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* STOP */
|
|
||||||
|
|
||||||
/* The rest of this file contains styles related to the mechanics of
|
|
||||||
the editor. You probably shouldn't touch them. */
|
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-scroll {
|
|
||||||
overflow: scroll !important; /* Things will break if this is overridden */
|
|
||||||
/* 30px is the magic margin used to hide the element's real scrollbars */
|
|
||||||
/* See overflow: hidden in .CodeMirror */
|
|
||||||
margin-bottom: -30px;
|
|
||||||
margin-right: -30px;
|
|
||||||
padding-bottom: 30px;
|
|
||||||
height: 100%;
|
|
||||||
outline: none; /* Prevent dragging from highlighting the element */
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.CodeMirror-sizer {
|
|
||||||
position: relative;
|
|
||||||
border-right: 30px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
|
||||||
before actual scrolling happens, thus preventing shaking and
|
|
||||||
flickering artifacts. */
|
|
||||||
.CodeMirror-vscrollbar,
|
|
||||||
.CodeMirror-hscrollbar,
|
|
||||||
.CodeMirror-scrollbar-filler,
|
|
||||||
.CodeMirror-gutter-filler {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 6;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.CodeMirror-vscrollbar {
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
.CodeMirror-hscrollbar {
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
overflow-y: hidden;
|
|
||||||
overflow-x: scroll;
|
|
||||||
}
|
|
||||||
.CodeMirror-scrollbar-filler {
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-filler {
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-gutters {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
min-height: 100%;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter {
|
|
||||||
white-space: normal;
|
|
||||||
height: 100%;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
margin-bottom: -30px;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-wrapper {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 4;
|
|
||||||
background: none !important;
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-background {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 4;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-elt {
|
|
||||||
position: absolute;
|
|
||||||
cursor: default;
|
|
||||||
z-index: 4;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-wrapper ::selection {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-wrapper ::-moz-selection {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-lines {
|
|
||||||
cursor: text;
|
|
||||||
min-height: 1px; /* prevents collapsing before first draw */
|
|
||||||
}
|
|
||||||
.CodeMirror pre {
|
|
||||||
/* Reset some styles that the rest of the page might have set */
|
|
||||||
-moz-border-radius: 0;
|
|
||||||
-webkit-border-radius: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
border-width: 0;
|
|
||||||
background: transparent;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
margin: 0;
|
|
||||||
white-space: pre;
|
|
||||||
word-wrap: normal;
|
|
||||||
line-height: inherit;
|
|
||||||
color: inherit;
|
|
||||||
z-index: 2;
|
|
||||||
position: relative;
|
|
||||||
overflow: visible;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
-webkit-font-variant-ligatures: contextual;
|
|
||||||
font-variant-ligatures: contextual;
|
|
||||||
}
|
|
||||||
.CodeMirror-wrap pre {
|
|
||||||
word-wrap: break-word;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-linebackground {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-linewidget {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
padding: 0.1px; /* Force widget margins to stay inside of the container */
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-rtl pre {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-code {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Force content-box sizing for the elements where we expect it */
|
|
||||||
.CodeMirror-scroll,
|
|
||||||
.CodeMirror-sizer,
|
|
||||||
.CodeMirror-gutter,
|
|
||||||
.CodeMirror-gutters,
|
|
||||||
.CodeMirror-linenumber {
|
|
||||||
-moz-box-sizing: content-box;
|
|
||||||
box-sizing: content-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-measure {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-cursor {
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
.CodeMirror-measure pre {
|
|
||||||
position: static;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.CodeMirror-cursors {
|
|
||||||
visibility: hidden;
|
|
||||||
position: relative;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
div.CodeMirror-dragcursors {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-focused div.CodeMirror-cursors {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-selected {
|
|
||||||
background: #d9d9d9;
|
|
||||||
}
|
|
||||||
.CodeMirror-focused .CodeMirror-selected {
|
|
||||||
background: #d7d4f0;
|
|
||||||
}
|
|
||||||
.CodeMirror-crosshair {
|
|
||||||
cursor: crosshair;
|
|
||||||
}
|
|
||||||
.CodeMirror-line::selection,
|
|
||||||
.CodeMirror-line > span::selection,
|
|
||||||
.CodeMirror-line > span > span::selection {
|
|
||||||
background: #d7d4f0;
|
|
||||||
}
|
|
||||||
.CodeMirror-line::-moz-selection,
|
|
||||||
.CodeMirror-line > span::-moz-selection,
|
|
||||||
.CodeMirror-line > span > span::-moz-selection {
|
|
||||||
background: #d7d4f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-searching {
|
|
||||||
background-color: #ffa;
|
|
||||||
background-color: rgba(255, 255, 0, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Used to force a border model for a node */
|
|
||||||
.cm-force-border {
|
|
||||||
padding-right: 0.1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
/* Hide the cursor when printing */
|
|
||||||
.CodeMirror div.CodeMirror-cursors {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See issue #2901 */
|
|
||||||
.cm-tab-wrap-hack:after {
|
|
||||||
content: '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Help users use markselection to safely style text background */
|
|
||||||
span.CodeMirror-selectedtext {
|
|
||||||
background: none;
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import CodeMirror from 'codemirror'
|
|
||||||
import './codemirror.css'
|
|
||||||
|
|
||||||
// modes
|
|
||||||
import 'codemirror/mode/javascript/javascript.js'
|
|
||||||
import 'codemirror/mode/css/css.js'
|
|
||||||
import 'codemirror/mode/htmlmixed/htmlmixed.js'
|
|
||||||
|
|
||||||
// addons
|
|
||||||
import 'codemirror/addon/edit/closebrackets.js'
|
|
||||||
import 'codemirror/addon/edit/closetag.js'
|
|
||||||
import 'codemirror/addon/comment/comment.js'
|
|
||||||
import 'codemirror/addon/fold/foldcode.js'
|
|
||||||
import 'codemirror/addon/fold/foldgutter.js'
|
|
||||||
import 'codemirror/addon/fold/brace-fold.js'
|
|
||||||
import 'codemirror/addon/fold/indent-fold.js'
|
|
||||||
import 'codemirror/addon/fold/comment-fold.js'
|
|
||||||
|
|
||||||
export default CodeMirror
|
|
@ -1,4 +1,3 @@
|
|||||||
import { exportFiles } from '../store'
|
|
||||||
import { saveAs } from 'file-saver'
|
import { saveAs } from 'file-saver'
|
||||||
|
|
||||||
import index from './template/index.html?raw'
|
import index from './template/index.html?raw'
|
||||||
@ -7,7 +6,7 @@ import pkg from './template/package.json?raw'
|
|||||||
import config from './template/vite.config.js?raw'
|
import config from './template/vite.config.js?raw'
|
||||||
import readme from './template/README.md?raw'
|
import readme from './template/README.md?raw'
|
||||||
|
|
||||||
export async function downloadProject() {
|
export async function downloadProject(store: any) {
|
||||||
const { default: JSZip } = await import('jszip')
|
const { default: JSZip } = await import('jszip')
|
||||||
const zip = new JSZip()
|
const zip = new JSZip()
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ export async function downloadProject() {
|
|||||||
const src = zip.folder('src')!
|
const src = zip.folder('src')!
|
||||||
src.file('main.js', main)
|
src.file('main.js', main)
|
||||||
|
|
||||||
const files = exportFiles()
|
const files = store.getFiles()
|
||||||
for (const file in files) {
|
for (const file in files) {
|
||||||
src.file(file, files[file])
|
src.file(file, files[file])
|
||||||
}
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import FileSelector from './FileSelector.vue'
|
|
||||||
import CodeMirror from '../codemirror/CodeMirror.vue'
|
|
||||||
import Message from '../Message.vue'
|
|
||||||
import { store } from '../store'
|
|
||||||
import { debounce } from '../utils'
|
|
||||||
import { ref, watch, computed } from 'vue'
|
|
||||||
|
|
||||||
const onChange = debounce((code: string) => {
|
|
||||||
store.activeFile.code = code
|
|
||||||
}, 250)
|
|
||||||
|
|
||||||
const activeCode = ref(store.activeFile.code)
|
|
||||||
const activeMode = computed(() =>
|
|
||||||
store.activeFilename.endsWith('.vue') ? 'htmlmixed' : 'javascript'
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => store.activeFilename,
|
|
||||||
() => {
|
|
||||||
activeCode.value = store.activeFile.code
|
|
||||||
}
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<FileSelector />
|
|
||||||
<div class="editor-container">
|
|
||||||
<CodeMirror @change="onChange" :value="activeCode" :mode="activeMode" />
|
|
||||||
<Message :err="store.errors[0]" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.editor-container {
|
|
||||||
height: calc(100% - 35px);
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,119 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { store, addFile, deleteFile, setActive } from '../store'
|
|
||||||
import { ref, VNode } from 'vue'
|
|
||||||
|
|
||||||
const pending = ref(false)
|
|
||||||
const pendingFilename = ref('Comp.vue')
|
|
||||||
|
|
||||||
function startAddFile() {
|
|
||||||
pending.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancelAddFile() {
|
|
||||||
pending.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
function focus({ el }: VNode) {
|
|
||||||
;(el as HTMLInputElement).focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
function doneAddFile() {
|
|
||||||
const filename = pendingFilename.value
|
|
||||||
|
|
||||||
if (!/\.(vue|js|ts)$/.test(filename) && filename !== 'import-map.json') {
|
|
||||||
store.errors = [
|
|
||||||
`Playground only supports *.vue, *.js, *.ts files or import-map.json.`
|
|
||||||
]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filename in store.files) {
|
|
||||||
store.errors = [`File "${filename}" already exists.`]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
store.errors = []
|
|
||||||
pending.value = false
|
|
||||||
addFile(filename)
|
|
||||||
pendingFilename.value = 'Comp.vue'
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="file-selector">
|
|
||||||
<div
|
|
||||||
v-for="(file, i) in Object.keys(store.files)"
|
|
||||||
class="file"
|
|
||||||
:class="{ active: store.activeFilename === file }"
|
|
||||||
@click="setActive(file)"
|
|
||||||
>
|
|
||||||
<span class="label">{{ file }}</span>
|
|
||||||
<span v-if="i > 0" class="remove" @click.stop="deleteFile(file)">
|
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" class="svelte-cghqrp">
|
|
||||||
<line stroke="#999" x1="18" y1="6" x2="6" y2="18"></line>
|
|
||||||
<line stroke="#999" x1="6" y1="6" x2="18" y2="18"></line>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="pending" class="file">
|
|
||||||
<input
|
|
||||||
v-model="pendingFilename"
|
|
||||||
spellcheck="false"
|
|
||||||
@keyup.enter="doneAddFile"
|
|
||||||
@keyup.esc="cancelAddFile"
|
|
||||||
@vnodeMounted="focus"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button class="add" @click="startAddFile">+</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.file-selector {
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
.file {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 13px;
|
|
||||||
font-family: var(--font-code);
|
|
||||||
cursor: pointer;
|
|
||||||
color: #999;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.file.active {
|
|
||||||
color: var(--color-branding);
|
|
||||||
border-bottom: 3px solid var(--color-branding);
|
|
||||||
cursor: text;
|
|
||||||
}
|
|
||||||
.file span {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 8px 10px 6px;
|
|
||||||
}
|
|
||||||
.file input {
|
|
||||||
width: 80px;
|
|
||||||
outline: none;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 4px 6px;
|
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
.file .remove {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
line-height: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
.add {
|
|
||||||
font-size: 20px;
|
|
||||||
font-family: var(--font-code);
|
|
||||||
color: #999;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
.add:hover {
|
|
||||||
color: var(--color-branding);
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,4 +1,6 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import '@vue/repl/style.css'
|
||||||
|
;(window as any).process = { env: {} }
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
createApp(App).mount('#app')
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import Preview from './Preview.vue'
|
|
||||||
import CodeMirror from '../codemirror/CodeMirror.vue'
|
|
||||||
import { store } from '../store'
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const modes = ['preview', 'js', 'css', 'ssr'] as const
|
|
||||||
|
|
||||||
type Modes = typeof modes[number]
|
|
||||||
const mode = ref<Modes>('preview')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="tab-buttons">
|
|
||||||
<button
|
|
||||||
v-for="m of modes"
|
|
||||||
:class="{ active: mode === m }"
|
|
||||||
@click="mode = m"
|
|
||||||
>
|
|
||||||
{{ m }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="output-container">
|
|
||||||
<Preview v-if="mode === 'preview'" />
|
|
||||||
<CodeMirror
|
|
||||||
v-else
|
|
||||||
readonly
|
|
||||||
:mode="mode === 'css' ? 'css' : 'javascript'"
|
|
||||||
:value="store.activeFile.compiled[mode]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.output-container {
|
|
||||||
height: calc(100% - 35px);
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.tab-buttons {
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
.tab-buttons button {
|
|
||||||
font-size: 13px;
|
|
||||||
font-family: var(--font-code);
|
|
||||||
padding: 8px 16px 6px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: #999;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.active {
|
|
||||||
color: var(--color-branding-dark);
|
|
||||||
border-bottom: 3px solid var(--color-branding-dark);
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,216 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import Message from '../Message.vue'
|
|
||||||
import {
|
|
||||||
ref,
|
|
||||||
onMounted,
|
|
||||||
onUnmounted,
|
|
||||||
watchEffect,
|
|
||||||
watch,
|
|
||||||
WatchStopHandle
|
|
||||||
} from 'vue'
|
|
||||||
import srcdoc from './srcdoc.html?raw'
|
|
||||||
import { PreviewProxy } from './PreviewProxy'
|
|
||||||
import { MAIN_FILE, vueRuntimeUrl } from '../transform'
|
|
||||||
import { compileModulesForPreview } from './moduleCompiler'
|
|
||||||
import { store } from '../store'
|
|
||||||
|
|
||||||
const container = ref()
|
|
||||||
const runtimeError = ref()
|
|
||||||
const runtimeWarning = ref()
|
|
||||||
|
|
||||||
let sandbox: HTMLIFrameElement
|
|
||||||
let proxy: PreviewProxy
|
|
||||||
let stopUpdateWatcher: WatchStopHandle
|
|
||||||
|
|
||||||
// create sandbox on mount
|
|
||||||
onMounted(createSandbox)
|
|
||||||
|
|
||||||
// reset sandbox when import map changes
|
|
||||||
watch(
|
|
||||||
() => store.importMap,
|
|
||||||
(importMap, prev) => {
|
|
||||||
if (!importMap) {
|
|
||||||
if (prev) {
|
|
||||||
// import-map.json deleted
|
|
||||||
createSandbox()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const map = JSON.parse(importMap)
|
|
||||||
if (!map.imports) {
|
|
||||||
store.errors = [`import-map.json is missing "imports" field.`]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (map.imports.vue) {
|
|
||||||
store.errors = [
|
|
||||||
'Select Vue versions using the top-right dropdown.\n' +
|
|
||||||
'Specifying it in the import map has no effect.'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
createSandbox()
|
|
||||||
} catch (e: any) {
|
|
||||||
store.errors = [e as Error]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// reset sandbox when version changes
|
|
||||||
watch(vueRuntimeUrl, createSandbox)
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
proxy.destroy()
|
|
||||||
stopUpdateWatcher && stopUpdateWatcher()
|
|
||||||
})
|
|
||||||
|
|
||||||
function createSandbox() {
|
|
||||||
if (sandbox) {
|
|
||||||
// clear prev sandbox
|
|
||||||
proxy.destroy()
|
|
||||||
stopUpdateWatcher()
|
|
||||||
container.value.removeChild(sandbox)
|
|
||||||
}
|
|
||||||
|
|
||||||
sandbox = document.createElement('iframe')
|
|
||||||
sandbox.setAttribute(
|
|
||||||
'sandbox',
|
|
||||||
[
|
|
||||||
'allow-forms',
|
|
||||||
'allow-modals',
|
|
||||||
'allow-pointer-lock',
|
|
||||||
'allow-popups',
|
|
||||||
'allow-same-origin',
|
|
||||||
'allow-scripts',
|
|
||||||
'allow-top-navigation-by-user-activation'
|
|
||||||
].join(' ')
|
|
||||||
)
|
|
||||||
|
|
||||||
let importMap: Record<string, any>
|
|
||||||
try {
|
|
||||||
importMap = JSON.parse(store.importMap || `{}`)
|
|
||||||
} catch (e: any) {
|
|
||||||
store.errors = [`Syntax error in import-map.json: ${(e as Error).message}`]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!importMap.imports) {
|
|
||||||
importMap.imports = {}
|
|
||||||
}
|
|
||||||
importMap.imports.vue = vueRuntimeUrl.value
|
|
||||||
const sandboxSrc = srcdoc.replace(
|
|
||||||
/<!--IMPORT_MAP-->/,
|
|
||||||
JSON.stringify(importMap)
|
|
||||||
)
|
|
||||||
sandbox.srcdoc = sandboxSrc
|
|
||||||
container.value.appendChild(sandbox)
|
|
||||||
|
|
||||||
proxy = new PreviewProxy(sandbox, {
|
|
||||||
on_fetch_progress: (progress: any) => {
|
|
||||||
// pending_imports = progress;
|
|
||||||
},
|
|
||||||
on_error: (event: any) => {
|
|
||||||
const msg =
|
|
||||||
event.value instanceof Error ? event.value.message : event.value
|
|
||||||
if (
|
|
||||||
msg.includes('Failed to resolve module specifier') ||
|
|
||||||
msg.includes('Error resolving module specifier')
|
|
||||||
) {
|
|
||||||
runtimeError.value =
|
|
||||||
msg.replace(/\. Relative references must.*$/, '') +
|
|
||||||
`.\nTip: add an "import-map.json" file to specify import paths for dependencies.`
|
|
||||||
} else {
|
|
||||||
runtimeError.value = event.value
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_unhandled_rejection: (event: any) => {
|
|
||||||
let error = event.value
|
|
||||||
if (typeof error === 'string') {
|
|
||||||
error = { message: error }
|
|
||||||
}
|
|
||||||
runtimeError.value = 'Uncaught (in promise): ' + error.message
|
|
||||||
},
|
|
||||||
on_console: (log: any) => {
|
|
||||||
if (log.duplicate) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (log.level === 'error') {
|
|
||||||
if (log.args[0] instanceof Error) {
|
|
||||||
runtimeError.value = log.args[0].message
|
|
||||||
} else {
|
|
||||||
runtimeError.value = log.args[0]
|
|
||||||
}
|
|
||||||
} else if (log.level === 'warn') {
|
|
||||||
if (log.args[0].toString().includes('[Vue warn]')) {
|
|
||||||
runtimeWarning.value = log.args
|
|
||||||
.join('')
|
|
||||||
.replace(/\[Vue warn\]:/, '')
|
|
||||||
.trim()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_console_group: (action: any) => {
|
|
||||||
// group_logs(action.label, false);
|
|
||||||
},
|
|
||||||
on_console_group_end: () => {
|
|
||||||
// ungroup_logs();
|
|
||||||
},
|
|
||||||
on_console_group_collapsed: (action: any) => {
|
|
||||||
// group_logs(action.label, true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
sandbox.addEventListener('load', () => {
|
|
||||||
proxy.handle_links()
|
|
||||||
stopUpdateWatcher = watchEffect(updatePreview)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updatePreview() {
|
|
||||||
// @ts-ignore
|
|
||||||
if (import.meta.env.PROD) {
|
|
||||||
console.clear()
|
|
||||||
}
|
|
||||||
runtimeError.value = null
|
|
||||||
runtimeWarning.value = null
|
|
||||||
try {
|
|
||||||
const modules = compileModulesForPreview()
|
|
||||||
console.log(`successfully compiled ${modules.length} modules.`)
|
|
||||||
// reset modules
|
|
||||||
await proxy.eval([
|
|
||||||
`window.__modules__ = {};window.__css__ = ''`,
|
|
||||||
...modules,
|
|
||||||
`
|
|
||||||
import { createApp as _createApp } from "vue"
|
|
||||||
|
|
||||||
if (window.__app__) {
|
|
||||||
window.__app__.unmount()
|
|
||||||
document.getElementById('app').innerHTML = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('__sfc-styles').innerHTML = window.__css__
|
|
||||||
const app = window.__app__ = _createApp(__modules__["${MAIN_FILE}"].default)
|
|
||||||
app.config.errorHandler = e => console.error(e)
|
|
||||||
app.mount('#app')`.trim()
|
|
||||||
])
|
|
||||||
} catch (e: any) {
|
|
||||||
runtimeError.value = (e as Error).message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="preview-container" ref="container"></div>
|
|
||||||
<Message :err="runtimeError" />
|
|
||||||
<Message v-if="!runtimeError" :warn="runtimeWarning" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.preview-container,
|
|
||||||
iframe {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: none;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,96 +0,0 @@
|
|||||||
// ReplProxy and srcdoc implementation from Svelte REPL
|
|
||||||
// MIT License https://github.com/sveltejs/svelte-repl/blob/master/LICENSE
|
|
||||||
|
|
||||||
let uid = 1
|
|
||||||
|
|
||||||
export class PreviewProxy {
|
|
||||||
iframe: HTMLIFrameElement
|
|
||||||
handlers: Record<string, Function>
|
|
||||||
pending_cmds: Map<
|
|
||||||
number,
|
|
||||||
{ resolve: (value: unknown) => void; reject: (reason?: any) => void }
|
|
||||||
>
|
|
||||||
handle_event: (e: any) => void
|
|
||||||
|
|
||||||
constructor(iframe: HTMLIFrameElement, handlers: Record<string, Function>) {
|
|
||||||
this.iframe = iframe
|
|
||||||
this.handlers = handlers
|
|
||||||
|
|
||||||
this.pending_cmds = new Map()
|
|
||||||
|
|
||||||
this.handle_event = e => this.handle_repl_message(e)
|
|
||||||
window.addEventListener('message', this.handle_event, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
window.removeEventListener('message', this.handle_event)
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe_command(action: string, args: any) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const cmd_id = uid++
|
|
||||||
|
|
||||||
this.pending_cmds.set(cmd_id, { resolve, reject })
|
|
||||||
|
|
||||||
this.iframe.contentWindow!.postMessage({ action, cmd_id, args }, '*')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_command_message(cmd_data: any) {
|
|
||||||
let action = cmd_data.action
|
|
||||||
let id = cmd_data.cmd_id
|
|
||||||
let handler = this.pending_cmds.get(id)
|
|
||||||
|
|
||||||
if (handler) {
|
|
||||||
this.pending_cmds.delete(id)
|
|
||||||
if (action === 'cmd_error') {
|
|
||||||
let { message, stack } = cmd_data
|
|
||||||
let e = new Error(message)
|
|
||||||
e.stack = stack
|
|
||||||
handler.reject(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action === 'cmd_ok') {
|
|
||||||
handler.resolve(cmd_data.args)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error('command not found', id, cmd_data, [
|
|
||||||
...this.pending_cmds.keys()
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_repl_message(event: any) {
|
|
||||||
if (event.source !== this.iframe.contentWindow) return
|
|
||||||
|
|
||||||
const { action, args } = event.data
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case 'cmd_error':
|
|
||||||
case 'cmd_ok':
|
|
||||||
return this.handle_command_message(event.data)
|
|
||||||
case 'fetch_progress':
|
|
||||||
return this.handlers.on_fetch_progress(args.remaining)
|
|
||||||
case 'error':
|
|
||||||
return this.handlers.on_error(event.data)
|
|
||||||
case 'unhandledrejection':
|
|
||||||
return this.handlers.on_unhandled_rejection(event.data)
|
|
||||||
case 'console':
|
|
||||||
return this.handlers.on_console(event.data)
|
|
||||||
case 'console_group':
|
|
||||||
return this.handlers.on_console_group(event.data)
|
|
||||||
case 'console_group_collapsed':
|
|
||||||
return this.handlers.on_console_group_collapsed(event.data)
|
|
||||||
case 'console_group_end':
|
|
||||||
return this.handlers.on_console_group_end(event.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eval(script: string | string[]) {
|
|
||||||
return this.iframe_command('eval', { script })
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_links() {
|
|
||||||
return this.iframe_command('catch_clicks', {})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,235 +0,0 @@
|
|||||||
import { store, File } from '../store'
|
|
||||||
import { MAIN_FILE } from '../transform'
|
|
||||||
import {
|
|
||||||
babelParse,
|
|
||||||
MagicString,
|
|
||||||
walk,
|
|
||||||
walkIdentifiers,
|
|
||||||
extractIdentifiers,
|
|
||||||
isInDestructureAssignment,
|
|
||||||
isStaticProperty
|
|
||||||
} from '@vue/compiler-sfc'
|
|
||||||
import { babelParserDefaultPlugins } from '@vue/shared'
|
|
||||||
import { ExportSpecifier, Identifier, Node } from '@babel/types'
|
|
||||||
|
|
||||||
export function compileModulesForPreview() {
|
|
||||||
return processFile(store.files[MAIN_FILE]).reverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
const modulesKey = `__modules__`
|
|
||||||
const exportKey = `__export__`
|
|
||||||
const dynamicImportKey = `__dynamic_import__`
|
|
||||||
const moduleKey = `__module__`
|
|
||||||
|
|
||||||
// similar logic with Vite's SSR transform, except this is targeting the browser
|
|
||||||
function processFile(file: File, seen = new Set<File>()) {
|
|
||||||
if (seen.has(file)) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
seen.add(file)
|
|
||||||
|
|
||||||
const { js, css } = file.compiled
|
|
||||||
|
|
||||||
const s = new MagicString(js)
|
|
||||||
|
|
||||||
const ast = babelParse(js, {
|
|
||||||
sourceFilename: file.filename,
|
|
||||||
sourceType: 'module',
|
|
||||||
plugins: [...babelParserDefaultPlugins]
|
|
||||||
}).program.body
|
|
||||||
|
|
||||||
const idToImportMap = new Map<string, string>()
|
|
||||||
const declaredConst = new Set<string>()
|
|
||||||
const importedFiles = new Set<string>()
|
|
||||||
const importToIdMap = new Map<string, string>()
|
|
||||||
|
|
||||||
function defineImport(node: Node, source: string) {
|
|
||||||
const filename = source.replace(/^\.\/+/, '')
|
|
||||||
if (!(filename in store.files)) {
|
|
||||||
throw new Error(`File "${filename}" does not exist.`)
|
|
||||||
}
|
|
||||||
if (importedFiles.has(filename)) {
|
|
||||||
return importToIdMap.get(filename)!
|
|
||||||
}
|
|
||||||
importedFiles.add(filename)
|
|
||||||
const id = `__import_${importedFiles.size}__`
|
|
||||||
importToIdMap.set(filename, id)
|
|
||||||
s.appendLeft(
|
|
||||||
node.start!,
|
|
||||||
`const ${id} = ${modulesKey}[${JSON.stringify(filename)}]\n`
|
|
||||||
)
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineExport(name: string, local = name) {
|
|
||||||
s.append(`\n${exportKey}(${moduleKey}, "${name}", () => ${local})`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0. instantiate module
|
|
||||||
s.prepend(
|
|
||||||
`const ${moduleKey} = __modules__[${JSON.stringify(
|
|
||||||
file.filename
|
|
||||||
)}] = { [Symbol.toStringTag]: "Module" }\n\n`
|
|
||||||
)
|
|
||||||
|
|
||||||
// 1. check all import statements and record id -> importName map
|
|
||||||
for (const node of ast) {
|
|
||||||
// import foo from 'foo' --> foo -> __import_foo__.default
|
|
||||||
// import { baz } from 'foo' --> baz -> __import_foo__.baz
|
|
||||||
// import * as ok from 'foo' --> ok -> __import_foo__
|
|
||||||
if (node.type === 'ImportDeclaration') {
|
|
||||||
const source = node.source.value
|
|
||||||
if (source.startsWith('./')) {
|
|
||||||
const importId = defineImport(node, node.source.value)
|
|
||||||
for (const spec of node.specifiers) {
|
|
||||||
if (spec.type === 'ImportSpecifier') {
|
|
||||||
idToImportMap.set(
|
|
||||||
spec.local.name,
|
|
||||||
`${importId}.${(spec.imported as Identifier).name}`
|
|
||||||
)
|
|
||||||
} else if (spec.type === 'ImportDefaultSpecifier') {
|
|
||||||
idToImportMap.set(spec.local.name, `${importId}.default`)
|
|
||||||
} else {
|
|
||||||
// namespace specifier
|
|
||||||
idToImportMap.set(spec.local.name, importId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.remove(node.start!, node.end!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. check all export statements and define exports
|
|
||||||
for (const node of ast) {
|
|
||||||
// named exports
|
|
||||||
if (node.type === 'ExportNamedDeclaration') {
|
|
||||||
if (node.declaration) {
|
|
||||||
if (
|
|
||||||
node.declaration.type === 'FunctionDeclaration' ||
|
|
||||||
node.declaration.type === 'ClassDeclaration'
|
|
||||||
) {
|
|
||||||
// export function foo() {}
|
|
||||||
defineExport(node.declaration.id!.name)
|
|
||||||
} else if (node.declaration.type === 'VariableDeclaration') {
|
|
||||||
// export const foo = 1, bar = 2
|
|
||||||
for (const decl of node.declaration.declarations) {
|
|
||||||
for (const id of extractIdentifiers(decl.id)) {
|
|
||||||
defineExport(id.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.remove(node.start!, node.declaration.start!)
|
|
||||||
} else if (node.source) {
|
|
||||||
// export { foo, bar } from './foo'
|
|
||||||
const importId = defineImport(node, node.source.value)
|
|
||||||
for (const spec of node.specifiers) {
|
|
||||||
defineExport(
|
|
||||||
(spec.exported as Identifier).name,
|
|
||||||
`${importId}.${(spec as ExportSpecifier).local.name}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
s.remove(node.start!, node.end!)
|
|
||||||
} else {
|
|
||||||
// export { foo, bar }
|
|
||||||
for (const spec of node.specifiers) {
|
|
||||||
const local = (spec as ExportSpecifier).local.name
|
|
||||||
const binding = idToImportMap.get(local)
|
|
||||||
defineExport((spec.exported as Identifier).name, binding || local)
|
|
||||||
}
|
|
||||||
s.remove(node.start!, node.end!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// default export
|
|
||||||
if (node.type === 'ExportDefaultDeclaration') {
|
|
||||||
if ('id' in node.declaration && node.declaration.id) {
|
|
||||||
// named hoistable/class exports
|
|
||||||
// export default function foo() {}
|
|
||||||
// export default class A {}
|
|
||||||
const { name } = node.declaration.id
|
|
||||||
s.remove(node.start!, node.start! + 15)
|
|
||||||
s.append(`\n${exportKey}(${moduleKey}, "default", () => ${name})`)
|
|
||||||
} else {
|
|
||||||
// anonymous default exports
|
|
||||||
s.overwrite(node.start!, node.start! + 14, `${moduleKey}.default =`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// export * from './foo'
|
|
||||||
if (node.type === 'ExportAllDeclaration') {
|
|
||||||
const importId = defineImport(node, node.source.value)
|
|
||||||
s.remove(node.start!, node.end!)
|
|
||||||
s.append(`\nfor (const key in ${importId}) {
|
|
||||||
if (key !== 'default') {
|
|
||||||
${exportKey}(${moduleKey}, key, () => ${importId}[key])
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. convert references to import bindings
|
|
||||||
for (const node of ast) {
|
|
||||||
if (node.type === 'ImportDeclaration') continue
|
|
||||||
walkIdentifiers(node, (id, parent, parentStack) => {
|
|
||||||
const binding = idToImportMap.get(id.name)
|
|
||||||
if (!binding) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (isStaticProperty(parent) && parent.shorthand) {
|
|
||||||
// let binding used in a property shorthand
|
|
||||||
// { foo } -> { foo: __import_x__.foo }
|
|
||||||
// skip for destructure patterns
|
|
||||||
if (
|
|
||||||
!(parent as any).inPattern ||
|
|
||||||
isInDestructureAssignment(parent, parentStack)
|
|
||||||
) {
|
|
||||||
s.appendLeft(id.end!, `: ${binding}`)
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
parent.type === 'ClassDeclaration' &&
|
|
||||||
id === parent.superClass
|
|
||||||
) {
|
|
||||||
if (!declaredConst.has(id.name)) {
|
|
||||||
declaredConst.add(id.name)
|
|
||||||
// locate the top-most node containing the class declaration
|
|
||||||
const topNode = parentStack[1]
|
|
||||||
s.prependRight(topNode.start!, `const ${id.name} = ${binding};\n`)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.overwrite(id.start!, id.end!, binding)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. convert dynamic imports
|
|
||||||
;(walk as any)(ast, {
|
|
||||||
enter(node: Node, parent: Node) {
|
|
||||||
if (node.type === 'Import' && parent.type === 'CallExpression') {
|
|
||||||
const arg = parent.arguments[0]
|
|
||||||
if (arg.type === 'StringLiteral' && arg.value.startsWith('./')) {
|
|
||||||
s.overwrite(node.start!, node.start! + 6, dynamicImportKey)
|
|
||||||
s.overwrite(
|
|
||||||
arg.start!,
|
|
||||||
arg.end!,
|
|
||||||
JSON.stringify(arg.value.replace(/^\.\/+/, ''))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// append CSS injection code
|
|
||||||
if (css) {
|
|
||||||
s.append(`\nwindow.__css__ += ${JSON.stringify(css)}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const processed = [s.toString()]
|
|
||||||
if (importedFiles.size) {
|
|
||||||
for (const imported of importedFiles) {
|
|
||||||
processed.push(...processFile(store.files[imported], seen))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return a list of files to further process
|
|
||||||
return processed
|
|
||||||
}
|
|
@ -1,259 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
||||||
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<style id="__sfc-styles"></style>
|
|
||||||
<script>
|
|
||||||
(() => {
|
|
||||||
let scriptEls = []
|
|
||||||
|
|
||||||
window.__modules__ = {}
|
|
||||||
|
|
||||||
window.__export__ = (mod, key, get) => {
|
|
||||||
Object.defineProperty(mod, key, {
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
get
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__dynamic_import__ = key => {
|
|
||||||
return Promise.resolve(window.__modules__[key])
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handle_message(ev) {
|
|
||||||
let { action, cmd_id } = ev.data;
|
|
||||||
const send_message = (payload) => parent.postMessage( { ...payload }, ev.origin);
|
|
||||||
const send_reply = (payload) => send_message({ ...payload, cmd_id });
|
|
||||||
const send_ok = () => send_reply({ action: 'cmd_ok' });
|
|
||||||
const send_error = (message, stack) => send_reply({ action: 'cmd_error', message, stack });
|
|
||||||
|
|
||||||
if (action === 'eval') {
|
|
||||||
try {
|
|
||||||
if (scriptEls.length) {
|
|
||||||
scriptEls.forEach(el => {
|
|
||||||
document.head.removeChild(el)
|
|
||||||
})
|
|
||||||
scriptEls.length = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
let { script: scripts } = ev.data.args
|
|
||||||
if (typeof scripts === 'string') scripts = [scripts]
|
|
||||||
|
|
||||||
for (const script of scripts) {
|
|
||||||
const scriptEl = document.createElement('script')
|
|
||||||
scriptEl.setAttribute('type', 'module')
|
|
||||||
// send ok in the module script to ensure sequential evaluation
|
|
||||||
// of multiple proxy.eval() calls
|
|
||||||
const done = new Promise((resolve) => {
|
|
||||||
window.__next__ = resolve
|
|
||||||
})
|
|
||||||
scriptEl.innerHTML = script + `\nwindow.__next__()`
|
|
||||||
document.head.appendChild(scriptEl)
|
|
||||||
scriptEl.onrror = err => send_error(err.message, err.stack)
|
|
||||||
scriptEls.push(scriptEl)
|
|
||||||
await done
|
|
||||||
}
|
|
||||||
window.__next__ = undefined
|
|
||||||
send_ok()
|
|
||||||
} catch (e) {
|
|
||||||
send_error(e.message, e.stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action === 'catch_clicks') {
|
|
||||||
try {
|
|
||||||
const top_origin = ev.origin;
|
|
||||||
document.body.addEventListener('click', event => {
|
|
||||||
if (event.which !== 1) return;
|
|
||||||
if (event.metaKey || event.ctrlKey || event.shiftKey) return;
|
|
||||||
if (event.defaultPrevented) return;
|
|
||||||
|
|
||||||
// ensure target is a link
|
|
||||||
let el = event.target;
|
|
||||||
while (el && el.nodeName !== 'A') el = el.parentNode;
|
|
||||||
if (!el || el.nodeName !== 'A') return;
|
|
||||||
|
|
||||||
if (el.hasAttribute('download') || el.getAttribute('rel') === 'external' || el.target) return;
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (el.href.startsWith(top_origin)) {
|
|
||||||
const url = new URL(el.href);
|
|
||||||
if (url.hash[0] === '#') {
|
|
||||||
window.location.hash = url.hash;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.open(el.href, '_blank');
|
|
||||||
});
|
|
||||||
send_ok();
|
|
||||||
} catch(e) {
|
|
||||||
send_error(e.message, e.stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('message', handle_message, false);
|
|
||||||
|
|
||||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
|
||||||
if (msg.includes('module specifier “vue”')) {
|
|
||||||
// firefox only error, ignore
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
parent.postMessage({ action: 'error', value: error }, '*');
|
|
||||||
} catch (e) {
|
|
||||||
parent.postMessage({ action: 'error', value: msg }, '*');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("unhandledrejection", event => {
|
|
||||||
if (event.reason.message.includes('Cross-origin')) {
|
|
||||||
event.preventDefault()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
parent.postMessage({ action: 'unhandledrejection', value: event.reason }, '*');
|
|
||||||
} catch (e) {
|
|
||||||
parent.postMessage({ action: 'unhandledrejection', value: event.reason.message }, '*');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let previous = { level: null, args: null };
|
|
||||||
|
|
||||||
['clear', 'log', 'info', 'dir', 'warn', 'error', 'table'].forEach((level) => {
|
|
||||||
const original = console[level];
|
|
||||||
console[level] = (...args) => {
|
|
||||||
const msg = String(args[0])
|
|
||||||
if (
|
|
||||||
msg.includes('You are running a development build of Vue') ||
|
|
||||||
msg.includes('You are running the esm-bundler build of Vue')
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const stringifiedArgs = stringify(args);
|
|
||||||
if (
|
|
||||||
previous.level === level &&
|
|
||||||
previous.args &&
|
|
||||||
previous.args === stringifiedArgs
|
|
||||||
) {
|
|
||||||
parent.postMessage({ action: 'console', level, duplicate: true }, '*');
|
|
||||||
} else {
|
|
||||||
previous = { level, args: stringifiedArgs };
|
|
||||||
|
|
||||||
try {
|
|
||||||
parent.postMessage({ action: 'console', level, args }, '*');
|
|
||||||
} catch (err) {
|
|
||||||
parent.postMessage({ action: 'console', level, args: args.map(a => {
|
|
||||||
return a instanceof Error ? a.message : String(a)
|
|
||||||
}) }, '*');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
original(...args);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
[
|
|
||||||
{ method: 'group', action: 'console_group' },
|
|
||||||
{ method: 'groupEnd', action: 'console_group_end' },
|
|
||||||
{ method: 'groupCollapsed', action: 'console_group_collapsed' },
|
|
||||||
].forEach((group_action) => {
|
|
||||||
const original = console[group_action.method];
|
|
||||||
console[group_action.method] = (label) => {
|
|
||||||
parent.postMessage({ action: group_action.action, label }, '*');
|
|
||||||
|
|
||||||
original(label);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const timers = new Map();
|
|
||||||
const original_time = console.time;
|
|
||||||
const original_timelog = console.timeLog;
|
|
||||||
const original_timeend = console.timeEnd;
|
|
||||||
|
|
||||||
console.time = (label = 'default') => {
|
|
||||||
original_time(label);
|
|
||||||
timers.set(label, performance.now());
|
|
||||||
}
|
|
||||||
console.timeLog = (label = 'default') => {
|
|
||||||
original_timelog(label);
|
|
||||||
const now = performance.now();
|
|
||||||
if (timers.has(label)) {
|
|
||||||
parent.postMessage({ action: 'console', level: 'system-log', args: [`${label}: ${now - timers.get(label)}ms`] }, '*');
|
|
||||||
} else {
|
|
||||||
parent.postMessage({ action: 'console', level: 'system-warn', args: [`Timer '${label}' does not exist`] }, '*');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.timeEnd = (label = 'default') => {
|
|
||||||
original_timeend(label);
|
|
||||||
const now = performance.now();
|
|
||||||
if (timers.has(label)) {
|
|
||||||
parent.postMessage({ action: 'console', level: 'system-log', args: [`${label}: ${now - timers.get(label)}ms`] }, '*');
|
|
||||||
} else {
|
|
||||||
parent.postMessage({ action: 'console', level: 'system-warn', args: [`Timer '${label}' does not exist`] }, '*');
|
|
||||||
}
|
|
||||||
timers.delete(label);
|
|
||||||
};
|
|
||||||
|
|
||||||
const original_assert = console.assert;
|
|
||||||
console.assert = (condition, ...args) => {
|
|
||||||
if (condition) {
|
|
||||||
const stack = new Error().stack;
|
|
||||||
parent.postMessage({ action: 'console', level: 'assert', args, stack }, '*');
|
|
||||||
}
|
|
||||||
original_assert(condition, ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const counter = new Map();
|
|
||||||
const original_count = console.count;
|
|
||||||
const original_countreset = console.countReset;
|
|
||||||
|
|
||||||
console.count = (label = 'default') => {
|
|
||||||
counter.set(label, (counter.get(label) || 0) + 1);
|
|
||||||
parent.postMessage({ action: 'console', level: 'system-log', args: `${label}: ${counter.get(label)}` }, '*');
|
|
||||||
original_count(label);
|
|
||||||
};
|
|
||||||
|
|
||||||
console.countReset = (label = 'default') => {
|
|
||||||
if (counter.has(label)) {
|
|
||||||
counter.set(label, 0);
|
|
||||||
} else {
|
|
||||||
parent.postMessage({ action: 'console', level: 'system-warn', args: `Count for '${label}' does not exist` }, '*');
|
|
||||||
}
|
|
||||||
original_countreset(label);
|
|
||||||
};
|
|
||||||
|
|
||||||
const original_trace = console.trace;
|
|
||||||
|
|
||||||
console.trace = (...args) => {
|
|
||||||
const stack = new Error().stack;
|
|
||||||
parent.postMessage({ action: 'console', level: 'trace', args, stack }, '*');
|
|
||||||
original_trace(...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
function stringify(args) {
|
|
||||||
try {
|
|
||||||
return JSON.stringify(args);
|
|
||||||
} catch (error) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- ES Module Shims: Import maps polyfill for modules browsers without import maps support (all except Chrome 89+) -->
|
|
||||||
<script async src="https://unpkg.com/es-module-shims@0.10.1/dist/es-module-shims.js"></script>
|
|
||||||
<script type="importmap"><!--IMPORT_MAP--></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,114 +0,0 @@
|
|||||||
import { reactive, watchEffect } from 'vue'
|
|
||||||
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>
|
|
||||||
`.trim()
|
|
||||||
|
|
||||||
export class File {
|
|
||||||
filename: string
|
|
||||||
code: string
|
|
||||||
compiled = {
|
|
||||||
js: '',
|
|
||||||
css: '',
|
|
||||||
ssr: ''
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(filename: string, code = '') {
|
|
||||||
this.filename = filename
|
|
||||||
this.code = code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Store {
|
|
||||||
files: Record<string, File>
|
|
||||||
activeFilename: string
|
|
||||||
readonly activeFile: File
|
|
||||||
readonly importMap: string | undefined
|
|
||||||
errors: (string | Error)[]
|
|
||||||
}
|
|
||||||
|
|
||||||
let files: Store['files'] = {}
|
|
||||||
|
|
||||||
const savedFiles = location.hash.slice(1)
|
|
||||||
if (savedFiles) {
|
|
||||||
const saved = JSON.parse(atou(savedFiles))
|
|
||||||
for (const filename in saved) {
|
|
||||||
files[filename] = new File(filename, saved[filename])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
files = {
|
|
||||||
'App.vue': new File(MAIN_FILE, welcomeCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const store: Store = reactive({
|
|
||||||
files,
|
|
||||||
activeFilename: MAIN_FILE,
|
|
||||||
get activeFile() {
|
|
||||||
return store.files[store.activeFilename]
|
|
||||||
},
|
|
||||||
get importMap() {
|
|
||||||
const file = store.files['import-map.json']
|
|
||||||
return file && file.code
|
|
||||||
},
|
|
||||||
errors: []
|
|
||||||
})
|
|
||||||
|
|
||||||
watchEffect(() => compileFile(store.activeFile))
|
|
||||||
|
|
||||||
for (const file in store.files) {
|
|
||||||
if (file !== MAIN_FILE) {
|
|
||||||
compileFile(store.files[file])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
history.replaceState({}, '', '#' + utoa(JSON.stringify(exportFiles())))
|
|
||||||
})
|
|
||||||
|
|
||||||
export function exportFiles() {
|
|
||||||
const exported: Record<string, string> = {}
|
|
||||||
for (const filename in store.files) {
|
|
||||||
exported[filename] = store.files[filename].code
|
|
||||||
}
|
|
||||||
return exported
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setActive(filename: string) {
|
|
||||||
store.activeFilename = filename
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addFile(filename: string) {
|
|
||||||
const file = (store.files[filename] = new File(filename))
|
|
||||||
|
|
||||||
if (filename === 'import-map.json') {
|
|
||||||
file.code = `
|
|
||||||
{
|
|
||||||
"imports": {
|
|
||||||
|
|
||||||
}
|
|
||||||
}`.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
setActive(filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteFile(filename: string) {
|
|
||||||
if (confirm(`Are you sure you want to delete ${filename}?`)) {
|
|
||||||
if (store.activeFilename === filename) {
|
|
||||||
store.activeFilename = MAIN_FILE
|
|
||||||
}
|
|
||||||
delete store.files[filename]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,285 +0,0 @@
|
|||||||
import { store, File } from './store'
|
|
||||||
import {
|
|
||||||
SFCDescriptor,
|
|
||||||
BindingMetadata,
|
|
||||||
shouldTransformRef,
|
|
||||||
transformRef
|
|
||||||
} from '@vue/compiler-sfc'
|
|
||||||
import * as defaultCompiler from '@vue/compiler-sfc'
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
export const MAIN_FILE = 'App.vue'
|
|
||||||
export const COMP_IDENTIFIER = `__sfc__`
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default SFC compiler we are using is built from each commit
|
|
||||||
* but we may swap it out with a version fetched from CDNs
|
|
||||||
*/
|
|
||||||
let SFCCompiler: typeof defaultCompiler = defaultCompiler
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const defaultVueUrl = import.meta.env.PROD
|
|
||||||
? `${location.origin}/vue.runtime.esm-browser.js` // to be copied on build
|
|
||||||
: `${location.origin}/src/vue-dev-proxy`
|
|
||||||
|
|
||||||
export const vueRuntimeUrl = ref(defaultVueUrl)
|
|
||||||
|
|
||||||
export async function setVersion(version: string) {
|
|
||||||
const compilerUrl = `https://unpkg.com/@vue/compiler-sfc@${version}/dist/compiler-sfc.esm-browser.js`
|
|
||||||
const runtimeUrl = `https://unpkg.com/@vue/runtime-dom@${version}/dist/runtime-dom.esm-browser.js`
|
|
||||||
const [compiler] = await Promise.all([
|
|
||||||
import(/* @vite-ignore */ compilerUrl),
|
|
||||||
import(/* @vite-ignore */ runtimeUrl)
|
|
||||||
])
|
|
||||||
SFCCompiler = compiler
|
|
||||||
vueRuntimeUrl.value = runtimeUrl
|
|
||||||
console.info(`Now using Vue version: ${version}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resetVersion() {
|
|
||||||
SFCCompiler = defaultCompiler
|
|
||||||
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 = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = await hashId(filename)
|
|
||||||
const { errors, descriptor } = SFCCompiler.parse(code, {
|
|
||||||
filename,
|
|
||||||
sourceMap: true
|
|
||||||
})
|
|
||||||
if (errors.length) {
|
|
||||||
store.errors = errors
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
descriptor.styles.some(s => s.lang) ||
|
|
||||||
(descriptor.template && descriptor.template.lang)
|
|
||||||
) {
|
|
||||||
store.errors = [
|
|
||||||
`lang="x" pre-processors for <template> or <style> are currently not ` +
|
|
||||||
`supported.`
|
|
||||||
]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const scriptLang =
|
|
||||||
(descriptor.script && descriptor.script.lang) ||
|
|
||||||
(descriptor.scriptSetup && descriptor.scriptSetup.lang)
|
|
||||||
if (scriptLang && scriptLang !== 'ts') {
|
|
||||||
store.errors = [`Only lang="ts" is supported for <script> blocks.`]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasScoped = descriptor.styles.some(s => s.scoped)
|
|
||||||
let clientCode = ''
|
|
||||||
let ssrCode = ''
|
|
||||||
|
|
||||||
const appendSharedCode = (code: string) => {
|
|
||||||
clientCode += code
|
|
||||||
ssrCode += code
|
|
||||||
}
|
|
||||||
|
|
||||||
const clientScriptResult = await doCompileScript(descriptor, id, false)
|
|
||||||
if (!clientScriptResult) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const [clientScript, bindings] = clientScriptResult
|
|
||||||
clientCode += clientScript
|
|
||||||
|
|
||||||
// script ssr only needs to be performed if using <script setup> where
|
|
||||||
// the render fn is inlined.
|
|
||||||
if (descriptor.scriptSetup) {
|
|
||||||
const ssrScriptResult = await doCompileScript(descriptor, id, true)
|
|
||||||
if (ssrScriptResult) {
|
|
||||||
ssrCode += ssrScriptResult[0]
|
|
||||||
} else {
|
|
||||||
ssrCode = `/* SSR compile error: ${store.errors[0]} */`
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// when no <script setup> is used, the script result will be identical.
|
|
||||||
ssrCode += clientScript
|
|
||||||
}
|
|
||||||
|
|
||||||
// template
|
|
||||||
// only need dedicated compilation if not using <script setup>
|
|
||||||
if (descriptor.template && !descriptor.scriptSetup) {
|
|
||||||
const clientTemplateResult = doCompileTemplate(
|
|
||||||
descriptor,
|
|
||||||
id,
|
|
||||||
bindings,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
if (!clientTemplateResult) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
clientCode += clientTemplateResult
|
|
||||||
|
|
||||||
const ssrTemplateResult = doCompileTemplate(descriptor, id, bindings, true)
|
|
||||||
if (ssrTemplateResult) {
|
|
||||||
// ssr compile failure is fine
|
|
||||||
ssrCode += ssrTemplateResult
|
|
||||||
} else {
|
|
||||||
ssrCode = `/* SSR compile error: ${store.errors[0]} */`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasScoped) {
|
|
||||||
appendSharedCode(
|
|
||||||
`\n${COMP_IDENTIFIER}.__scopeId = ${JSON.stringify(`data-v-${id}`)}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clientCode || ssrCode) {
|
|
||||||
appendSharedCode(
|
|
||||||
`\n${COMP_IDENTIFIER}.__file = ${JSON.stringify(filename)}` +
|
|
||||||
`\nexport default ${COMP_IDENTIFIER}`
|
|
||||||
)
|
|
||||||
compiled.js = clientCode.trimStart()
|
|
||||||
compiled.ssr = ssrCode.trimStart()
|
|
||||||
}
|
|
||||||
|
|
||||||
// styles
|
|
||||||
let css = ''
|
|
||||||
for (const style of descriptor.styles) {
|
|
||||||
if (style.module) {
|
|
||||||
store.errors = [`<style module> is not supported in the playground.`]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const styleResult = await SFCCompiler.compileStyleAsync({
|
|
||||||
source: style.content,
|
|
||||||
filename,
|
|
||||||
id,
|
|
||||||
scoped: style.scoped,
|
|
||||||
modules: !!style.module
|
|
||||||
})
|
|
||||||
if (styleResult.errors.length) {
|
|
||||||
// postcss uses pathToFileURL which isn't polyfilled in the browser
|
|
||||||
// ignore these errors for now
|
|
||||||
if (!styleResult.errors[0].message.includes('pathToFileURL')) {
|
|
||||||
store.errors = styleResult.errors
|
|
||||||
}
|
|
||||||
// proceed even if css compile errors
|
|
||||||
} else {
|
|
||||||
css += styleResult.code + '\n'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (css) {
|
|
||||||
compiled.css = css.trim()
|
|
||||||
} else {
|
|
||||||
compiled.css = '/* No <style> tags present */'
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear errors
|
|
||||||
store.errors = []
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doCompileScript(
|
|
||||||
descriptor: SFCDescriptor,
|
|
||||||
id: string,
|
|
||||||
ssr: boolean
|
|
||||||
): Promise<[string, BindingMetadata | undefined] | undefined> {
|
|
||||||
if (descriptor.script || descriptor.scriptSetup) {
|
|
||||||
try {
|
|
||||||
const compiledScript = SFCCompiler.compileScript(descriptor, {
|
|
||||||
id,
|
|
||||||
refTransform: true,
|
|
||||||
inlineTemplate: true,
|
|
||||||
templateOptions: {
|
|
||||||
ssr,
|
|
||||||
ssrCssVars: descriptor.cssVars
|
|
||||||
}
|
|
||||||
})
|
|
||||||
let code = ''
|
|
||||||
if (compiledScript.bindings) {
|
|
||||||
code += `\n/* Analyzed bindings: ${JSON.stringify(
|
|
||||||
compiledScript.bindings,
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)} */`
|
|
||||||
}
|
|
||||||
code +=
|
|
||||||
`\n` +
|
|
||||||
SFCCompiler.rewriteDefault(compiledScript.content, COMP_IDENTIFIER)
|
|
||||||
|
|
||||||
if ((descriptor.script || descriptor.scriptSetup)!.lang === 'ts') {
|
|
||||||
code = await transformTS(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
return [code, compiledScript.bindings]
|
|
||||||
} catch (e: any) {
|
|
||||||
store.errors = [e.stack.split('\n').slice(0, 12).join('\n')]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return [`\nconst ${COMP_IDENTIFIER} = {}`, undefined]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function doCompileTemplate(
|
|
||||||
descriptor: SFCDescriptor,
|
|
||||||
id: string,
|
|
||||||
bindingMetadata: BindingMetadata | undefined,
|
|
||||||
ssr: boolean
|
|
||||||
) {
|
|
||||||
const templateResult = SFCCompiler.compileTemplate({
|
|
||||||
source: descriptor.template!.content,
|
|
||||||
filename: descriptor.filename,
|
|
||||||
id,
|
|
||||||
scoped: descriptor.styles.some(s => s.scoped),
|
|
||||||
slotted: descriptor.slotted,
|
|
||||||
ssr,
|
|
||||||
ssrCssVars: descriptor.cssVars,
|
|
||||||
isProd: false,
|
|
||||||
compilerOptions: {
|
|
||||||
bindingMetadata
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (templateResult.errors.length) {
|
|
||||||
store.errors = templateResult.errors
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const fnName = ssr ? `ssrRender` : `render`
|
|
||||||
|
|
||||||
return (
|
|
||||||
`\n${templateResult.code.replace(
|
|
||||||
/\nexport (function|const) (render|ssrRender)/,
|
|
||||||
`$1 ${fnName}`
|
|
||||||
)}` + `\n${COMP_IDENTIFIER}.${fnName} = ${fnName}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function hashId(filename: string) {
|
|
||||||
const msgUint8 = new TextEncoder().encode(filename) // encode as (utf-8) Uint8Array
|
|
||||||
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8) // hash the message
|
|
||||||
const hashArray = Array.from(new Uint8Array(hashBuffer)) // convert buffer to byte array
|
|
||||||
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('') // convert bytes to hex string
|
|
||||||
return hashHex.slice(0, 8)
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
export function debounce(fn: Function, n = 100) {
|
|
||||||
let handle: any
|
|
||||||
return (...args: any[]) => {
|
|
||||||
if (handle) clearTimeout(handle)
|
|
||||||
handle = setTimeout(() => {
|
|
||||||
fn(...args)
|
|
||||||
}, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// prefer old unicode hacks for backward compatibility
|
|
||||||
// https://base64.guru/developers/javascript/examples/unicode-strings
|
|
||||||
export function utoa(data: string): string {
|
|
||||||
return btoa(unescape(encodeURIComponent(data)))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function atou(base64: string): string {
|
|
||||||
return decodeURIComponent(escape(atob(base64)))
|
|
||||||
}
|
|
@ -11,13 +11,8 @@ export default defineConfig({
|
|||||||
define: {
|
define: {
|
||||||
__COMMIT__: JSON.stringify(commit)
|
__COMMIT__: JSON.stringify(commit)
|
||||||
},
|
},
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@vue/compiler-sfc': '@vue/compiler-sfc/dist/compiler-sfc.esm-browser.js'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
exclude: ['consolidate']
|
exclude: ['@vue/repl']
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
88
yarn.lock
88
yarn.lock
@ -744,13 +744,6 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.36.tgz#00d9301d4dc35c2f6465a8aec634bb533674c652"
|
resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.36.tgz#00d9301d4dc35c2f6465a8aec634bb533674c652"
|
||||||
integrity sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==
|
integrity sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==
|
||||||
|
|
||||||
"@types/codemirror@^0.0.108":
|
|
||||||
version "0.0.108"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-0.0.108.tgz#e640422b666bf49251b384c390cdeb2362585bde"
|
|
||||||
integrity sha512-3FGFcus0P7C2UOGCNUVENqObEb4SFk+S8Dnxq7K6aIsLVs/vDtlangl3PEO0ykaKXyK56swVF6Nho7VsA44uhw==
|
|
||||||
dependencies:
|
|
||||||
"@types/tern" "*"
|
|
||||||
|
|
||||||
"@types/consolidate@^0.14.0":
|
"@types/consolidate@^0.14.0":
|
||||||
version "0.14.1"
|
version "0.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/consolidate/-/consolidate-0.14.1.tgz#78f01b1ed747d945dea9969581fcc1d0cb59bad8"
|
resolved "https://registry.yarnpkg.com/@types/consolidate/-/consolidate-0.14.1.tgz#78f01b1ed747d945dea9969581fcc1d0cb59bad8"
|
||||||
@ -867,13 +860,6 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||||
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
||||||
|
|
||||||
"@types/tern@*":
|
|
||||||
version "0.23.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.4.tgz#03926eb13dbeaf3ae0d390caf706b2643a0127fb"
|
|
||||||
integrity sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==
|
|
||||||
dependencies:
|
|
||||||
"@types/estree" "*"
|
|
||||||
|
|
||||||
"@types/yargs-parser@*":
|
"@types/yargs-parser@*":
|
||||||
version "20.2.1"
|
version "20.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
|
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
|
||||||
@ -942,6 +928,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-1.6.0.tgz#e5558e20c20e9098cd5bd65b9901fdcd2c354983"
|
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-1.6.0.tgz#e5558e20c20e9098cd5bd65b9901fdcd2c354983"
|
||||||
integrity sha512-n3i8htn8pTg9M+kM3cnEfsPZx/6ngInlTroth6fA1LQTJq5aTVQ8ggaE5pPoAy9vCgHPtcaXMzwpldhqRAkebQ==
|
integrity sha512-n3i8htn8pTg9M+kM3cnEfsPZx/6ngInlTroth6fA1LQTJq5aTVQ8ggaE5pPoAy9vCgHPtcaXMzwpldhqRAkebQ==
|
||||||
|
|
||||||
|
"@vue/repl@^0.1.0":
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/repl/-/repl-0.1.0.tgz#9d37f0e8458b18737286785ea86456b8a60e3ee8"
|
||||||
|
integrity sha512-C1o7ZRJFDKjF7Hz94DVCg9XQL1gRG7+Iigu9HduNmS80LvBb6SJ6JiwOpPSV0UU/gDCt1r+Am9bUGEVj2D/fzw==
|
||||||
|
|
||||||
"@zeit/schemas@2.6.0":
|
"@zeit/schemas@2.6.0":
|
||||||
version "2.6.0"
|
version "2.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3"
|
resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3"
|
||||||
@ -1096,11 +1087,6 @@ ansi-styles@^5.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
|
||||||
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
|
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
|
||||||
|
|
||||||
any-promise@^1.0.0:
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
|
|
||||||
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
|
|
||||||
|
|
||||||
anymatch@^3.0.3, anymatch@~3.1.2:
|
anymatch@^3.0.3, anymatch@~3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
|
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
|
||||||
@ -1642,11 +1628,6 @@ co@^4.6.0:
|
|||||||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||||
integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
|
integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
|
||||||
|
|
||||||
codemirror@^5.60.0:
|
|
||||||
version "5.62.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.62.3.tgz#5cfdee6931c8b2d1b39ae773aaaaec2cc6b5558e"
|
|
||||||
integrity sha512-zZAyOfN8TU67ngqrxhOgtkSAGV9jSpN1snbl8elPtnh9Z5A11daR405+dhLzLnuXrwX0WCShWlybxPN3QC/9Pg==
|
|
||||||
|
|
||||||
collect-v8-coverage@^1.0.0:
|
collect-v8-coverage@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
|
resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
|
||||||
@ -1698,11 +1679,6 @@ commander@^2.20.0, commander@^2.7.1:
|
|||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||||
|
|
||||||
commander@^4.0.0:
|
|
||||||
version "4.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
|
||||||
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
|
|
||||||
|
|
||||||
commander@^6.2.0:
|
commander@^6.2.0:
|
||||||
version "6.2.1"
|
version "6.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
||||||
@ -2890,18 +2866,6 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
|
|
||||||
glob@7.1.6:
|
|
||||||
version "7.1.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
|
||||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
|
||||||
dependencies:
|
|
||||||
fs.realpath "^1.0.0"
|
|
||||||
inflight "^1.0.4"
|
|
||||||
inherits "2"
|
|
||||||
minimatch "^3.0.4"
|
|
||||||
once "^1.3.0"
|
|
||||||
path-is-absolute "^1.0.0"
|
|
||||||
|
|
||||||
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||||
version "7.1.7"
|
version "7.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
|
||||||
@ -4533,15 +4497,6 @@ ms@2.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
mz@^2.7.0:
|
|
||||||
version "2.7.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
|
|
||||||
integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
|
|
||||||
dependencies:
|
|
||||||
any-promise "^1.0.0"
|
|
||||||
object-assign "^4.0.1"
|
|
||||||
thenify-all "^1.0.0"
|
|
||||||
|
|
||||||
nanoid@^3.1.23:
|
nanoid@^3.1.23:
|
||||||
version "3.1.25"
|
version "3.1.25"
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
|
||||||
@ -4651,7 +4606,7 @@ nwsapi@^2.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
|
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
|
||||||
integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
|
integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
|
||||||
|
|
||||||
object-assign@^4.0.1, object-assign@^4.1.1:
|
object-assign@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||||
@ -5984,18 +5939,6 @@ strip-json-comments@~2.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||||
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
||||||
|
|
||||||
sucrase@^3.19.0:
|
|
||||||
version "3.20.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.20.1.tgz#1c055e97d0fab2f9857f02461364075b3a4ab226"
|
|
||||||
integrity sha512-BIG59HaJOxNct9Va6KvT5yzBA/rcMGetzvZyTx0ZdCcspIbpJTPS64zuAfYlJuOj+3WaI5JOdA+F0bJQQi8ZiQ==
|
|
||||||
dependencies:
|
|
||||||
commander "^4.0.0"
|
|
||||||
glob "7.1.6"
|
|
||||||
lines-and-columns "^1.1.6"
|
|
||||||
mz "^2.7.0"
|
|
||||||
pirates "^4.0.1"
|
|
||||||
ts-interface-checker "^0.1.9"
|
|
||||||
|
|
||||||
supports-color@^5.3.0:
|
supports-color@^5.3.0:
|
||||||
version "5.5.0"
|
version "5.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||||
@ -6119,20 +6062,6 @@ text-table@^0.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||||
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
||||||
|
|
||||||
thenify-all@^1.0.0:
|
|
||||||
version "1.6.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
|
|
||||||
integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=
|
|
||||||
dependencies:
|
|
||||||
thenify ">= 3.1.0 < 4"
|
|
||||||
|
|
||||||
"thenify@>= 3.1.0 < 4":
|
|
||||||
version "3.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
|
|
||||||
integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
|
|
||||||
dependencies:
|
|
||||||
any-promise "^1.0.0"
|
|
||||||
|
|
||||||
throat@^6.0.1:
|
throat@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375"
|
resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375"
|
||||||
@ -6216,11 +6145,6 @@ trim-off-newlines@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3"
|
resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3"
|
||||||
integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM=
|
integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM=
|
||||||
|
|
||||||
ts-interface-checker@^0.1.9:
|
|
||||||
version "0.1.13"
|
|
||||||
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
|
|
||||||
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
|
|
||||||
|
|
||||||
ts-jest@^27.0.5:
|
ts-jest@^27.0.5:
|
||||||
version "27.0.5"
|
version "27.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.5.tgz#0b0604e2271167ec43c12a69770f0bb65ad1b750"
|
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.5.tgz#0b0604e2271167ec43c12a69770f0bb65ad1b750"
|
||||||
|
Loading…
Reference in New Issue
Block a user