+/// <reference types="vite/client" />
+
// Global compile-time constants
declare var __DEV__: boolean
declare var __TEST__: boolean
}
declare module '*.vue' {}
-declare module '*?raw' {
- const content: string
- export default content
-}
declare module 'file-saver' {
export function saveAs(blob: any, name: any): void
const t: typeof 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 }
+}
"serve": "vite preview"
},
"devDependencies": {
- "@types/codemirror": "^0.0.108",
"@vitejs/plugin-vue": "^1.2.0",
- "codemirror": "^5.60.0",
"vite": "^2.4.0"
},
"dependencies": {
+ "@vue/repl": "^0.1.0",
"file-saver": "^2.0.5",
- "jszip": "^3.6.0",
- "sucrase": "^3.19.0"
+ "jszip": "^3.6.0"
}
}
<script setup lang="ts">
import Header from './Header.vue'
-import SplitPane from './SplitPane.vue'
-import Editor from './editor/Editor.vue'
-import Output from './output/Output.vue'
+import { Repl, ReplStore } from '@vue/repl'
+import { watchEffect } from '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>
<template>
- <Header />
- <div class="wrapper">
- <SplitPane>
- <template #left>
- <Editor />
- </template>
- <template #right>
- <Output />
- </template>
- </SplitPane>
- </div>
+ <Header :store="store" />
+ <Repl :store="store" :showCompileOutput="true" />
</template>
<style>
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
- color: var(--base);
margin: 0;
- background-color: #f8f8f8;
--base: #444;
--nav-height: 50px;
- --font-code: 'Source Code Pro', monospace;
- --color-branding: #3ca877;
- --color-branding-dark: #416f9c;
}
-.wrapper {
- height: calc(100vh - var(--nav-height));
+.vue-repl {
+ height: calc(var(--vh) - var(--nav-height));
}
button {
<script setup lang="ts">
import { downloadProject } from './download/download'
-import { setVersion, resetVersion } from './transform'
import { ref, onMounted } from 'vue'
+// @ts-ignore
+const { store } = defineProps(['store'])
+
const currentCommit = __COMMIT__
const activeVersion = ref(`@${currentCommit}`)
const publishedVersions = ref<string[]>()
async function setVueVersion(v: string) {
activeVersion.value = `loading...`
- await setVersion(v)
+ await store.setVueVersion(v)
activeVersion.value = `v${v}`
expanded.value = false
}
function resetVueVersion() {
- resetVersion()
+ store.resetVueVersion()
activeVersion.value = `@${currentCommit}`
expanded.value = false
}
</g>
</svg>
</button>
- <button class="download" @click="downloadProject">
+ <button class="download" @click="downloadProject(store)">
<svg width="1.7em" height="1.7em" viewBox="0 0 24 24">
<g fill="#626262">
<rect x="4" y="18" width="16" height="2" rx="1" ry="1" />
}
.versions a:hover {
- color: var(--color-branding);
+ color: #3ca877;
}
.versions.expanded {
+++ /dev/null
-<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>
+++ /dev/null
-<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>
+++ /dev/null
-<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>
+++ /dev/null
-/* 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;
-}
+++ /dev/null
-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
-import { exportFiles } from '../store'
import { saveAs } from 'file-saver'
import index from './template/index.html?raw'
import config from './template/vite.config.js?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 zip = new JSZip()
const src = zip.folder('src')!
src.file('main.js', main)
- const files = exportFiles()
+ const files = store.getFiles()
for (const file in files) {
src.file(file, files[file])
}
+++ /dev/null
-<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>
+++ /dev/null
-<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>
import { createApp } from 'vue'
import App from './App.vue'
+import '@vue/repl/style.css'
+;(window as any).process = { env: {} }
createApp(App).mount('#app')
+++ /dev/null
-<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>
+++ /dev/null
-<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>
+++ /dev/null
-// 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', {})
- }
-}
+++ /dev/null
-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
-}
+++ /dev/null
-<!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>
\ No newline at end of file
+++ /dev/null
-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]
- }
-}
+++ /dev/null
-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)
-}
+++ /dev/null
-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)))
-}
define: {
__COMMIT__: JSON.stringify(commit)
},
- resolve: {
- alias: {
- '@vue/compiler-sfc': '@vue/compiler-sfc/dist/compiler-sfc.esm-browser.js'
- }
- },
optimizeDeps: {
- exclude: ['consolidate']
+ exclude: ['@vue/repl']
}
})
resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.36.tgz#00d9301d4dc35c2f6465a8aec634bb533674c652"
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":
version "0.14.1"
resolved "https://registry.yarnpkg.com/@types/consolidate/-/consolidate-0.14.1.tgz#78f01b1ed747d945dea9969581fcc1d0cb59bad8"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
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@*":
version "20.2.1"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-1.6.0.tgz#e5558e20c20e9098cd5bd65b9901fdcd2c354983"
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":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
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:
version "3.1.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
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:
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/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
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:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
dependencies:
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:
version "7.1.7"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
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:
version "3.1.25"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
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"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
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:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
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:
version "6.0.1"
resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375"
resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3"
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:
version "27.0.5"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.5.tgz#0b0604e2271167ec43c12a69770f0bb65ad1b750"