]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
workflow(sfc-playground): support import map
authorEvan You <yyx990803@gmail.com>
Wed, 31 Mar 2021 17:31:00 +0000 (13:31 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 31 Mar 2021 17:31:00 +0000 (13:31 -0400)
packages/sfc-playground/src/App.vue
packages/sfc-playground/src/Header.vue
packages/sfc-playground/src/editor/Editor.vue
packages/sfc-playground/src/editor/FileSelector.vue
packages/sfc-playground/src/output/Preview.vue
packages/sfc-playground/src/output/moduleCompiler.ts
packages/sfc-playground/src/output/srcdoc.html
packages/sfc-playground/src/sfcCompiler.ts
packages/sfc-playground/src/store.ts

index cf4424ad69443f845db9f144423dced2a7787289..6a0047d74d3f9322429b0386483b4afa2a69cbf6 100644 (file)
@@ -24,7 +24,7 @@ body {
   font-size: 13px;
   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
     Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
-  color:var(--base);
+  color: var(--base);
   margin: 0;
   background-color: #f8f8f8;
   --base: #444;
index a2a9f88439e6d0a8226f1fc96951a4d041e31c0d..1a74ceb248304104fefff14ce4d669da00d2b0dd 100644 (file)
@@ -14,7 +14,7 @@
           <li v-for="version of publishedVersions">
             <a @click="setVueVersion(version)">v{{ version }}</a>
           </li>
-          <li><a @click="resetVueVersion">This Commit ({{ commit }})</a></li>
+          <li><a @click="resetVueVersion">This Commit ({{ currentCommit }})</a></li>
           <li>
             <a href="https://app.netlify.com/sites/vue-sfc-playground/deploys" target="_blank">Commits History</a>
           </li>
@@ -51,8 +51,8 @@ import { downloadProject } from './download/download'
 import { setVersion, resetVersion } from './sfcCompiler'
 import { ref, onMounted } from 'vue'
 
-const commit = __COMMIT__
-const activeVersion = ref(`@${commit}`)
+const currentCommit = __COMMIT__
+const activeVersion = ref(`@${currentCommit}`)
 const publishedVersions = ref<string[]>()
 const expanded = ref(false)
 
@@ -72,7 +72,7 @@ async function setVueVersion(v: string) {
 
 function resetVueVersion() {
   resetVersion()
-  activeVersion.value = `@${commit}`
+  activeVersion.value = `@${currentCommit}`
   expanded.value = false
 }
 
index 86f4f67342562a50967f84d1f2c29a7a9285eace..481450289ec3fb07509c80c0cf19f9dda053cff9 100644 (file)
@@ -20,7 +20,7 @@ const onChange = debounce((code: string) => {
 
 const activeCode = ref(store.activeFile.code)
 const activeMode = computed(
-  () => (store.activeFilename.endsWith('.js') ? 'javascript' : 'htmlmixed')
+  () => (store.activeFilename.endsWith('.vue') ? 'htmlmixed' : 'javascript')
 )
 
 watch(
index 639b0ec186095c19bb1941c975e8930c2c8f5220..e731e7e329bafc9922e11ff4528d7d3337f7f52b 100644 (file)
@@ -45,8 +45,12 @@ function focus({ el }: VNode) {
 function doneAddFile() {
   const filename = pendingFilename.value
 
-  if (!filename.endsWith('.vue') && !filename.endsWith('.js')) {
-    store.errors = [`Playground only supports .vue or .js files.`]
+  if (
+    !filename.endsWith('.vue') &&
+    !filename.endsWith('.js') &&
+    filename !== 'import-map.json'
+  ) {
+    store.errors = [`Playground only supports *.vue, *.js files or import-map.json.`]
     return
   }
 
index 637b618eb9fcc22aec5d15008d1e9a92a1018687..52b35c1fca2717469175b2127fa22c8ef0193cec 100644 (file)
 <template>
-  <iframe
-    id="preview"
-    ref="iframe"
-    sandbox="allow-forms allow-modals allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-top-navigation-by-user-activation"
-    :srcdoc="srcdoc"
-  ></iframe>
+  <div class="preview-container" ref="container">
+</div>
   <Message :err="runtimeError" />
   <Message v-if="!runtimeError" :warn="runtimeWarning" />
 </template>
 
 <script setup lang="ts">
 import Message from '../Message.vue'
-import { ref, onMounted, onUnmounted, watchEffect } from 'vue'
+import { ref, onMounted, onUnmounted, watchEffect, watch } from 'vue'
 import type { WatchStopHandle } from 'vue'
 import srcdoc from './srcdoc.html?raw'
 import { PreviewProxy } from './PreviewProxy'
-import { MAIN_FILE, SANDBOX_VUE_URL } from '../sfcCompiler'
+import { MAIN_FILE, vueRuntimeUrl } from '../sfcCompiler'
 import { compileModulesForPreview } from './moduleCompiler'
+import { store } from '../store'
 
-const iframe = ref()
+const container = ref()
 const runtimeError = ref()
 const runtimeWarning = ref()
 
+let sandbox: HTMLIFrameElement
 let proxy: PreviewProxy
-let updateHandle: WatchStopHandle
+let stopUpdateWatcher: WatchStopHandle
 
-async function updatePreview() {
-  runtimeError.value = null
-  runtimeWarning.value = null
+// 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 modules = compileModulesForPreview()
-    console.log(`successfully compiled ${modules.length} modules.`)
-    // reset modules
-    await proxy.eval([
-      `window.__modules__ = {};window.__css__ = ''`,
-      ...modules,
-      `
-import { createApp as _createApp } from "${SANDBOX_VUE_URL}"
+    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) {
+    store.errors = [e]
+    return
+  }
+})
 
-if (window.__app__) {
-  window.__app__.unmount()
-  document.getElementById('app').innerHTML = ''
-}
+// reset sandbox when version changes
+watch(vueRuntimeUrl, createSandbox)
 
-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()
-    ])
+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) {
-    runtimeError.value = e.stack
+    store.errors = [`Syntax error in import-map.json: ${e.message}`]
+    return
   }
-}
 
-onMounted(() => {
-  proxy = new PreviewProxy(iframe.value, {
+  if (!importMap.imports) {
+    importMap.imports = {}
+  }
+  importMap.imports.vue = vueRuntimeUrl.value
+  const sandboxSrc = srcdoc.replace(/<!--IMPORT_MAP-->/, JSON.stringify(importMap))
+  sandbox.setAttribute('srcdoc', sandboxSrc)
+  container.value.appendChild(sandbox)
+
+  proxy = new PreviewProxy(sandbox, {
     on_fetch_progress: (progress: any) => {
       // pending_imports = progress;
     },
@@ -93,19 +138,43 @@ onMounted(() => {
     }
   })
 
-  iframe.value.addEventListener('load', () => {
+  sandbox.addEventListener('load', () => {
     proxy.handle_links()
-    updateHandle = watchEffect(updatePreview)
+    stopUpdateWatcher = watchEffect(updatePreview)
   })
-})
+}
 
-onUnmounted(() => {
-  proxy.destroy()
-  updateHandle && updateHandle()
-})
+async function updatePreview() {
+  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) {
+    runtimeError.value = e.stack
+  }
+}
 </script>
 
 <style>
+.preview-container,
 iframe {
   width: 100%;
   height: 100%;
index ea22d6b462ba22bc98c7082a41431cf2daf3a9e2..dbb82a52f435c6c16146a32893b656fadc00b4a3 100644 (file)
@@ -1,5 +1,5 @@
 import { store, File } from '../store'
-import { MAIN_FILE, SANDBOX_VUE_URL } from '../sfcCompiler'
+import { MAIN_FILE } from '../sfcCompiler'
 import {
   babelParse,
   MagicString,
@@ -92,15 +92,6 @@ function processFile(file: File, seen = new Set<File>()) {
           }
         }
         s.remove(node.start!, node.end!)
-      } else {
-        if (source === 'vue') {
-          // rewrite Vue imports
-          s.overwrite(
-            node.source.start!,
-            node.source.end!,
-            `"${SANDBOX_VUE_URL}"`
-          )
-        }
       }
     }
   }
index 4446c493247b4db2ab699caf35abb66b2055697d..8890eae0b6db91fb900b215fec856ce00aa7209c 100644 (file)
                        }
                </style>
                <style id="__sfc-styles"></style>
-               <script type="module">
-                       let scriptEls = []
 
-                       window.__modules__ = {}
+               <!-- ES Module Shims: Import maps polyfill for modules browsers without import maps support (all except Chrome 89+) -->
+               <script async src="https://ga.jspm.io/npm:es-module-shims@0.10.1/dist/es-module-shims.min.js"></script>
+               <script id="map" type="importmap"><!--IMPORT_MAP--></script>
 
-                       window.__export__ = (mod, key, get) => {
-                               Object.defineProperty(mod, key, {
-                                       enumerable: true,
-                                       configurable: true,
-                                       get
-                               })
-                       }
+               <script>
+                       (() => {
+                               let scriptEls = []
 
-                       window.__dynamic_import__ = key => {
-                               return Promise.resolve(window.__modules__[key])
-                       }
+                               window.__modules__ = {}
 
-                       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 });
+                               window.__export__ = (mod, key, get) => {
+                                       Object.defineProperty(mod, key, {
+                                               enumerable: true,
+                                               configurable: true,
+                                               get
+                                       })
+                               }
 
-                               if (action === 'eval') {
-                                       try {
-                                               if (scriptEls.length) {
-                                                       scriptEls.forEach(el => {
-                                                               document.head.removeChild(el)
-                                                       })
-                                                       scriptEls.length = 0
-                                               }
+                               window.__dynamic_import__ = key => {
+                                       return Promise.resolve(window.__modules__[key])
+                               }
 
-                                               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, reject) => {
-                                                               window.__next__ = resolve
-                                                               scriptEl.onerror = reject
-                                                       })
-                                                       scriptEl.innerHTML = script + `\nwindow.__next__()`
-                                                       document.head.appendChild(scriptEl)
-                                                       scriptEls.push(scriptEl)
-                                                       await done
+                               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, reject) => {
+                                                                       window.__next__ = resolve
+                                                                       scriptEl.onerror = reject
+                                                               })
+                                                               scriptEl.innerHTML = script + `\nwindow.__next__()`
+                                                               document.head.appendChild(scriptEl)
+                                                               scriptEls.push(scriptEl)
+                                                               await done
+                                                       }
+                                                       window.__next__ = undefined
+                                                       send_ok()
+                                               } catch (e) {
+                                                       send_error(e.message, e.stack);
                                                }
-                                               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;
+                                       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.open(el.href, '_blank');
+                                                       });
+                                                       send_ok();
+                                               } catch(e) {
+                                                       send_error(e.message, e.stack);
+                                               }
                                        }
                                }
-                       }
 
-                       window.addEventListener('message', handle_message, false);
+                               window.addEventListener('message', handle_message, false);
 
-                       window.onerror = function (msg, url, lineNo, columnNo, error) {
-                               parent.postMessage({ action: 'error', value: error }, '*');
-                       }
+                               window.onerror = function (msg, url, lineNo, columnNo, error) {
+                                       parent.postMessage({ action: 'error', value: error }, '*');
+                               }
+
+                               window.addEventListener("unhandledrejection", event => {
+                                       parent.postMessage({ action: 'unhandledrejection', value: event.reason }, '*');
+                               });
 
-                       window.addEventListener("unhandledrejection", event => {
-                               parent.postMessage({ action: 'unhandledrejection', value: event.reason }, '*');
-                       });
+                               let previous = { level: null, args: null };
 
-                       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')) {
+                                                       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: 'unclonable' }, '*');
+                                                       }
+                                               }
 
-                       ['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')) {
-                                               return
+                                               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`] }, '*');
                                        }
-                                       const stringifiedArgs = stringify(args);
-                                       if (
-                                               previous.level === level &&
-                                               previous.args &&
-                                               previous.args === stringifiedArgs
-                                       ) {
-                                               parent.postMessage({ action: 'console', level, duplicate: true }, '*');
+                               }
+                               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 {
-                                               previous = { level, args: stringifiedArgs };
+                                               parent.postMessage({ action: 'console', level: 'system-warn', args: [`Timer '${label}' does not exist`] }, '*');
+                                       }
+                                       timers.delete(label);
+                               };
 
-                                               try {
-                                                       parent.postMessage({ action: 'console', level, args }, '*');
-                                               } catch (err) {
-                                                       parent.postMessage({ action: 'console', level: 'unclonable' }, '*');
-                                               }
+                               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);
+                               };
 
-                                       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 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);
                                };
-                       });
 
-                       const timers = new Map();
-                       const original_time = console.time;
-                       const original_timelog = console.timeLog;
-                       const original_timeend = console.timeEnd;
+                               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);
+                               };
 
-                       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_trace = console.trace;
 
-                       const original_assert = console.assert;
-                       console.assert = (condition, ...args) => {
-                               if (condition) {
+                               console.trace = (...args) => {
                                        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;
+                                       parent.postMessage({ action: 'console', level: 'trace', args, stack }, '*');
+                                       original_trace(...args);
+                               };
+
+                               function stringify(args) {
+                                       try {
+                                               return JSON.stringify(args);
+                                       } catch (error) {
+                                               return null;
+                                       }
                                }
-                       }
+                       })()
                </script>
        </head>
        <body>
index dc586632870fe41bf46d3f44924aabd7f0150b54..d77a6e31c16c7ab7df9ef80272b0204f227944a5 100644 (file)
@@ -1,6 +1,7 @@
 import { store, File } from './store'
 import { SFCDescriptor, BindingMetadata } 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__`
@@ -16,23 +17,23 @@ const defaultVueUrl = import.meta.env.PROD
   ? '/vue.runtime.esm-browser.js' // to be copied on build
   : '/src/vue-dev-proxy'
 
-export let SANDBOX_VUE_URL = defaultVueUrl
+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://cdn.skypack.dev/@vue/runtime-dom@${version}`
+  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
-  SANDBOX_VUE_URL = runtimeUrl
+  vueRuntimeUrl.value = runtimeUrl
   console.info(`Now using Vue version: ${version}`)
 }
 
 export function resetVersion() {
   SFCCompiler = defaultCompiler
-  SANDBOX_VUE_URL = defaultVueUrl
+  vueRuntimeUrl.value = defaultVueUrl
 }
 
 export async function compileFile({ filename, code, compiled }: File) {
@@ -41,7 +42,7 @@ export async function compileFile({ filename, code, compiled }: File) {
     return
   }
 
-  if (filename.endsWith('.js')) {
+  if (!filename.endsWith('.vue')) {
     compiled.js = compiled.ssr = code
     store.errors = []
     return
index 320f6cd304aa0f85e9092826d8d0472f8f4f0041..22080ea107e565aa331ce92252b8d9079f2a165a 100644 (file)
@@ -30,6 +30,7 @@ interface Store {
   files: Record<string, File>
   activeFilename: string
   readonly activeFile: File
+  readonly importMap: string | undefined
   errors: (string | Error)[]
 }
 
@@ -53,6 +54,10 @@ export const store: Store = reactive({
   get activeFile() {
     return store.files[store.activeFilename]
   },
+  get importMap() {
+    const file = store.files['import-map.json']
+    return file && file.code
+  },
   errors: []
 })
 
@@ -81,7 +86,17 @@ export function setActive(filename: string) {
 }
 
 export function addFile(filename: string) {
-  store.files[filename] = new File(filename)
+  const file = (store.files[filename] = new File(filename))
+
+  if (filename === 'import-map.json') {
+    file.code = `
+{
+  "imports": {
+
+  }
+}`.trim()
+  }
+
   setActive(filename)
 }