diff --git a/packages/global.d.ts b/packages/global.d.ts index 2796f658..52199e5e 100644 --- a/packages/global.d.ts +++ b/packages/global.d.ts @@ -1,3 +1,5 @@ +/// + // Global compile-time constants declare var __DEV__: boolean declare var __TEST__: boolean @@ -25,10 +27,6 @@ declare namespace jest { } declare module '*.vue' {} -declare module '*?raw' { - const content: string - export default content -} declare module 'file-saver' { export function saveAs(blob: any, name: any): void @@ -39,3 +37,10 @@ declare module 'stream/web' { 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 } +} diff --git a/packages/sfc-playground/package.json b/packages/sfc-playground/package.json index f8893f4b..d866cef9 100644 --- a/packages/sfc-playground/package.json +++ b/packages/sfc-playground/package.json @@ -8,14 +8,12 @@ "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" } } diff --git a/packages/sfc-playground/src/App.vue b/packages/sfc-playground/src/App.vue index d2e6f1ed..de4ba074 100644 --- a/packages/sfc-playground/src/App.vue +++ b/packages/sfc-playground/src/App.vue @@ -1,22 +1,24 @@ - - - - - - - - - - - + + diff --git a/packages/sfc-playground/src/SplitPane.vue b/packages/sfc-playground/src/SplitPane.vue deleted file mode 100644 index a8582dd0..00000000 --- a/packages/sfc-playground/src/SplitPane.vue +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/packages/sfc-playground/src/codemirror/CodeMirror.vue b/packages/sfc-playground/src/codemirror/CodeMirror.vue deleted file mode 100644 index f6fd7691..00000000 --- a/packages/sfc-playground/src/codemirror/CodeMirror.vue +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - diff --git a/packages/sfc-playground/src/codemirror/codemirror.css b/packages/sfc-playground/src/codemirror/codemirror.css deleted file mode 100644 index 01fa7624..00000000 --- a/packages/sfc-playground/src/codemirror/codemirror.css +++ /dev/null @@ -1,506 +0,0 @@ -/* BASICS */ - -.CodeMirror { - --base: #545281; - --comment: hsl(210, 25%, 60%); - --keyword: #af4ab1; - --variable: #0055d1; - --function: #c25205; - --string: #2ba46d; - --number: #c25205; - --tags: #dd0000; - --qualifier: #ff6032; - --important: var(--string); - - direction: ltr; - font-family: var(--font-code); - height: auto; -} - -/* PADDING */ - -.CodeMirror-lines { - padding: 4px 0; /* Vertical padding around content */ -} -.CodeMirror pre { - padding: 0 4px; /* Horizontal padding of content */ -} - -.CodeMirror-scrollbar-filler, -.CodeMirror-gutter-filler { - background-color: white; /* The little square between H and V scrollbars */ -} - -/* GUTTER */ - -.CodeMirror-gutters { - border-right: 1px solid #ddd; - background-color: transparent; - white-space: nowrap; -} -.CodeMirror-linenumber { - padding: 0 3px 0 5px; - min-width: 20px; - text-align: right; - color: var(--comment); - white-space: nowrap; - opacity: 0.6; -} - -.CodeMirror-guttermarker { - color: black; -} -.CodeMirror-guttermarker-subtle { - color: #999; -} - -/* FOLD GUTTER */ - -.CodeMirror-foldmarker { - color: #414141; - text-shadow: #ff9966 1px 1px 2px, #ff9966 -1px -1px 2px, #ff9966 1px -1px 2px, - #ff9966 -1px 1px 2px; - font-family: arial; - line-height: 0.3; - cursor: pointer; -} -.CodeMirror-foldgutter { - width: 0.7em; -} -.CodeMirror-foldgutter-open, -.CodeMirror-foldgutter-folded { - cursor: pointer; -} -.CodeMirror-foldgutter-open:after, -.CodeMirror-foldgutter-folded:after { - content: '>'; - font-size: 0.8em; - opacity: 0.8; - transition: transform 0.2s; - display: inline-block; - top: -0.1em; - position: relative; - transform: rotate(90deg); -} -.CodeMirror-foldgutter-folded:after { - transform: none; -} - -/* CURSOR */ - -.CodeMirror-cursor { - border-left: 1px solid black; - border-right: none; - width: 0; -} -/* Shown when moving in bi-directional text */ -.CodeMirror div.CodeMirror-secondarycursor { - border-left: 1px solid silver; -} -.cm-fat-cursor .CodeMirror-cursor { - width: auto; - border: 0 !important; - background: #7e7; -} -.cm-fat-cursor div.CodeMirror-cursors { - z-index: 1; -} -.cm-fat-cursor-mark { - background-color: rgba(20, 255, 20, 0.5); - -webkit-animation: blink 1.06s steps(1) infinite; - -moz-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; -} -.cm-animate-fat-cursor { - width: auto; - border: 0; - -webkit-animation: blink 1.06s steps(1) infinite; - -moz-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; - background-color: #7e7; -} -@-moz-keyframes blink { - 0% { - } - 50% { - background-color: transparent; - } - 100% { - } -} -@-webkit-keyframes blink { - 0% { - } - 50% { - background-color: transparent; - } - 100% { - } -} -@keyframes blink { - 0% { - } - 50% { - background-color: transparent; - } - 100% { - } -} - -.cm-tab { - display: inline-block; - text-decoration: inherit; -} - -.CodeMirror-rulers { - position: absolute; - left: 0; - right: 0; - top: -50px; - bottom: -20px; - overflow: hidden; -} -.CodeMirror-ruler { - border-left: 1px solid #ccc; - top: 0; - bottom: 0; - position: absolute; -} - -/* DEFAULT THEME */ -.cm-s-default.CodeMirror { - background-color: transparent; -} -.cm-s-default .cm-header { - color: blue; -} -.cm-s-default .cm-quote { - color: #090; -} -.cm-negative { - color: #d44; -} -.cm-positive { - color: #292; -} -.cm-header, -.cm-strong { - font-weight: bold; -} -.cm-em { - font-style: italic; -} -.cm-link { - text-decoration: underline; -} -.cm-strikethrough { - text-decoration: line-through; -} - -.cm-s-default .cm-atom, -.cm-s-default .cm-def, -.cm-s-default .cm-property, -.cm-s-default .cm-variable-2, -.cm-s-default .cm-variable-3, -.cm-s-default .cm-punctuation { - color: var(--base); -} -.cm-s-default .cm-hr, -.cm-s-default .cm-comment { - color: var(--comment); -} -.cm-s-default .cm-attribute, -.cm-s-default .cm-keyword { - color: var(--keyword); -} -.cm-s-default .cm-variable { - color: var(--variable); -} -.cm-s-default .cm-bracket, -.cm-s-default .cm-tag { - color: var(--tags); -} -.cm-s-default .cm-number { - color: var(--number); -} -.cm-s-default .cm-string, -.cm-s-default .cm-string-2 { - color: var(--string); -} -.cm-s-default .cm-type { - color: #085; -} -.cm-s-default .cm-meta { - color: #555; -} -.cm-s-default .cm-qualifier { - color: var(--qualifier); -} -.cm-s-default .cm-builtin { - color: #7539ff; -} -.cm-s-default .cm-link { - color: var(--flash); -} -.cm-s-default .cm-error { - color: #ff008c; -} -.cm-invalidchar { - color: #ff008c; -} - -.CodeMirror-composing { - border-bottom: 2px solid; -} - -/* Default styles for common addons */ - -div.CodeMirror span.CodeMirror-matchingbracket { - color: #0b0; -} -div.CodeMirror span.CodeMirror-nonmatchingbracket { - color: #a22; -} -.CodeMirror-matchingtag { - background: rgba(255, 150, 0, 0.3); -} -.CodeMirror-activeline-background { - background: #e8f2ff; -} - -/* STOP */ - -/* The rest of this file contains styles related to the mechanics of - the editor. You probably shouldn't touch them. */ - -.CodeMirror { - position: relative; - overflow: hidden; - background: white; -} - -.CodeMirror-scroll { - overflow: scroll !important; /* Things will break if this is overridden */ - /* 30px is the magic margin used to hide the element's real scrollbars */ - /* See overflow: hidden in .CodeMirror */ - margin-bottom: -30px; - margin-right: -30px; - padding-bottom: 30px; - height: 100%; - outline: none; /* Prevent dragging from highlighting the element */ - position: relative; -} -.CodeMirror-sizer { - position: relative; - border-right: 30px solid transparent; -} - -/* The fake, visible scrollbars. Used to force redraw during scrolling - before actual scrolling happens, thus preventing shaking and - flickering artifacts. */ -.CodeMirror-vscrollbar, -.CodeMirror-hscrollbar, -.CodeMirror-scrollbar-filler, -.CodeMirror-gutter-filler { - position: absolute; - z-index: 6; - display: none; -} -.CodeMirror-vscrollbar { - right: 0; - top: 0; - overflow-x: hidden; - overflow-y: scroll; -} -.CodeMirror-hscrollbar { - bottom: 0; - left: 0; - overflow-y: hidden; - overflow-x: scroll; -} -.CodeMirror-scrollbar-filler { - right: 0; - bottom: 0; -} -.CodeMirror-gutter-filler { - left: 0; - bottom: 0; -} - -.CodeMirror-gutters { - position: absolute; - left: 0; - top: 0; - min-height: 100%; - z-index: 3; -} -.CodeMirror-gutter { - white-space: normal; - height: 100%; - display: inline-block; - vertical-align: top; - margin-bottom: -30px; -} -.CodeMirror-gutter-wrapper { - position: absolute; - z-index: 4; - background: none !important; - border: none !important; -} -.CodeMirror-gutter-background { - position: absolute; - top: 0; - bottom: 0; - z-index: 4; -} -.CodeMirror-gutter-elt { - position: absolute; - cursor: default; - z-index: 4; -} -.CodeMirror-gutter-wrapper ::selection { - background-color: transparent; -} -.CodeMirror-gutter-wrapper ::-moz-selection { - background-color: transparent; -} - -.CodeMirror-lines { - cursor: text; - min-height: 1px; /* prevents collapsing before first draw */ -} -.CodeMirror pre { - /* Reset some styles that the rest of the page might have set */ - -moz-border-radius: 0; - -webkit-border-radius: 0; - border-radius: 0; - border-width: 0; - background: transparent; - font-family: inherit; - font-size: inherit; - margin: 0; - white-space: pre; - word-wrap: normal; - line-height: inherit; - color: inherit; - z-index: 2; - position: relative; - overflow: visible; - -webkit-tap-highlight-color: transparent; - -webkit-font-variant-ligatures: contextual; - font-variant-ligatures: contextual; -} -.CodeMirror-wrap pre { - word-wrap: break-word; - white-space: pre-wrap; - word-break: normal; -} - -.CodeMirror-linebackground { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - z-index: 0; -} - -.CodeMirror-linewidget { - position: relative; - z-index: 2; - padding: 0.1px; /* Force widget margins to stay inside of the container */ -} - -.CodeMirror-rtl pre { - direction: rtl; -} - -.CodeMirror-code { - outline: none; -} - -/* Force content-box sizing for the elements where we expect it */ -.CodeMirror-scroll, -.CodeMirror-sizer, -.CodeMirror-gutter, -.CodeMirror-gutters, -.CodeMirror-linenumber { - -moz-box-sizing: content-box; - box-sizing: content-box; -} - -.CodeMirror-measure { - position: absolute; - width: 100%; - height: 0; - overflow: hidden; - visibility: hidden; -} - -.CodeMirror-cursor { - position: absolute; - pointer-events: none; -} -.CodeMirror-measure pre { - position: static; -} - -div.CodeMirror-cursors { - visibility: hidden; - position: relative; - z-index: 3; -} -div.CodeMirror-dragcursors { - visibility: visible; -} - -.CodeMirror-focused div.CodeMirror-cursors { - visibility: visible; -} - -.CodeMirror-selected { - background: #d9d9d9; -} -.CodeMirror-focused .CodeMirror-selected { - background: #d7d4f0; -} -.CodeMirror-crosshair { - cursor: crosshair; -} -.CodeMirror-line::selection, -.CodeMirror-line > span::selection, -.CodeMirror-line > span > span::selection { - background: #d7d4f0; -} -.CodeMirror-line::-moz-selection, -.CodeMirror-line > span::-moz-selection, -.CodeMirror-line > span > span::-moz-selection { - background: #d7d4f0; -} - -.cm-searching { - background-color: #ffa; - background-color: rgba(255, 255, 0, 0.4); -} - -/* Used to force a border model for a node */ -.cm-force-border { - padding-right: 0.1px; -} - -@media print { - /* Hide the cursor when printing */ - .CodeMirror div.CodeMirror-cursors { - visibility: hidden; - } -} - -/* See issue #2901 */ -.cm-tab-wrap-hack:after { - content: ''; -} - -/* Help users use markselection to safely style text background */ -span.CodeMirror-selectedtext { - background: none; -} diff --git a/packages/sfc-playground/src/codemirror/codemirror.ts b/packages/sfc-playground/src/codemirror/codemirror.ts deleted file mode 100644 index 0ae44dd8..00000000 --- a/packages/sfc-playground/src/codemirror/codemirror.ts +++ /dev/null @@ -1,19 +0,0 @@ -import CodeMirror from 'codemirror' -import './codemirror.css' - -// modes -import 'codemirror/mode/javascript/javascript.js' -import 'codemirror/mode/css/css.js' -import 'codemirror/mode/htmlmixed/htmlmixed.js' - -// addons -import 'codemirror/addon/edit/closebrackets.js' -import 'codemirror/addon/edit/closetag.js' -import 'codemirror/addon/comment/comment.js' -import 'codemirror/addon/fold/foldcode.js' -import 'codemirror/addon/fold/foldgutter.js' -import 'codemirror/addon/fold/brace-fold.js' -import 'codemirror/addon/fold/indent-fold.js' -import 'codemirror/addon/fold/comment-fold.js' - -export default CodeMirror diff --git a/packages/sfc-playground/src/download/download.ts b/packages/sfc-playground/src/download/download.ts index 91cdfd00..6d9634d3 100644 --- a/packages/sfc-playground/src/download/download.ts +++ b/packages/sfc-playground/src/download/download.ts @@ -1,4 +1,3 @@ -import { exportFiles } from '../store' import { saveAs } from 'file-saver' import index from './template/index.html?raw' @@ -7,7 +6,7 @@ import pkg from './template/package.json?raw' import config from './template/vite.config.js?raw' import 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() @@ -21,7 +20,7 @@ export async function downloadProject() { 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]) } diff --git a/packages/sfc-playground/src/editor/Editor.vue b/packages/sfc-playground/src/editor/Editor.vue deleted file mode 100644 index de615369..00000000 --- a/packages/sfc-playground/src/editor/Editor.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - diff --git a/packages/sfc-playground/src/editor/FileSelector.vue b/packages/sfc-playground/src/editor/FileSelector.vue deleted file mode 100644 index aee2309e..00000000 --- a/packages/sfc-playground/src/editor/FileSelector.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - {{ file }} - - - - - - - - - - - + - - - - diff --git a/packages/sfc-playground/src/main.ts b/packages/sfc-playground/src/main.ts index 01433bca..a82d62b9 100644 --- a/packages/sfc-playground/src/main.ts +++ b/packages/sfc-playground/src/main.ts @@ -1,4 +1,6 @@ import { createApp } from 'vue' import App from './App.vue' +import '@vue/repl/style.css' +;(window as any).process = { env: {} } createApp(App).mount('#app') diff --git a/packages/sfc-playground/src/output/Output.vue b/packages/sfc-playground/src/output/Output.vue deleted file mode 100644 index 70f6518d..00000000 --- a/packages/sfc-playground/src/output/Output.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - {{ m }} - - - - - - - - - - diff --git a/packages/sfc-playground/src/output/Preview.vue b/packages/sfc-playground/src/output/Preview.vue deleted file mode 100644 index 04f6860c..00000000 --- a/packages/sfc-playground/src/output/Preview.vue +++ /dev/null @@ -1,216 +0,0 @@ - - - - - - - - - diff --git a/packages/sfc-playground/src/output/PreviewProxy.ts b/packages/sfc-playground/src/output/PreviewProxy.ts deleted file mode 100644 index 8c115e0d..00000000 --- a/packages/sfc-playground/src/output/PreviewProxy.ts +++ /dev/null @@ -1,96 +0,0 @@ -// ReplProxy and srcdoc implementation from Svelte REPL -// MIT License https://github.com/sveltejs/svelte-repl/blob/master/LICENSE - -let uid = 1 - -export class PreviewProxy { - iframe: HTMLIFrameElement - handlers: Record - pending_cmds: Map< - number, - { resolve: (value: unknown) => void; reject: (reason?: any) => void } - > - handle_event: (e: any) => void - - constructor(iframe: HTMLIFrameElement, handlers: Record) { - 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', {}) - } -} diff --git a/packages/sfc-playground/src/output/moduleCompiler.ts b/packages/sfc-playground/src/output/moduleCompiler.ts deleted file mode 100644 index 958fade7..00000000 --- a/packages/sfc-playground/src/output/moduleCompiler.ts +++ /dev/null @@ -1,235 +0,0 @@ -import { store, File } from '../store' -import { MAIN_FILE } from '../transform' -import { - babelParse, - MagicString, - walk, - walkIdentifiers, - extractIdentifiers, - isInDestructureAssignment, - isStaticProperty -} from '@vue/compiler-sfc' -import { babelParserDefaultPlugins } from '@vue/shared' -import { ExportSpecifier, Identifier, Node } from '@babel/types' - -export function compileModulesForPreview() { - return processFile(store.files[MAIN_FILE]).reverse() -} - -const modulesKey = `__modules__` -const exportKey = `__export__` -const dynamicImportKey = `__dynamic_import__` -const moduleKey = `__module__` - -// similar logic with Vite's SSR transform, except this is targeting the browser -function processFile(file: File, seen = new Set()) { - 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() - const declaredConst = new Set() - const importedFiles = new Set() - const importToIdMap = new Map() - - 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 -} diff --git a/packages/sfc-playground/src/output/srcdoc.html b/packages/sfc-playground/src/output/srcdoc.html deleted file mode 100644 index 91dd9e1a..00000000 --- a/packages/sfc-playground/src/output/srcdoc.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/sfc-playground/src/store.ts b/packages/sfc-playground/src/store.ts deleted file mode 100644 index 7a741bdb..00000000 --- a/packages/sfc-playground/src/store.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { reactive, watchEffect } from 'vue' -import { compileFile, MAIN_FILE } from './transform' -import { utoa, atou } from './utils' - -const welcomeCode = ` - - - - {{ msg }} - - -`.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 - 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 = {} - 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] - } -} diff --git a/packages/sfc-playground/src/transform.ts b/packages/sfc-playground/src/transform.ts deleted file mode 100644 index 478744d1..00000000 --- a/packages/sfc-playground/src/transform.ts +++ /dev/null @@ -1,285 +0,0 @@ -import { store, File } from './store' -import { - SFCDescriptor, - BindingMetadata, - shouldTransformRef, - transformRef -} from '@vue/compiler-sfc' -import * as defaultCompiler from '@vue/compiler-sfc' -import { ref } from 'vue' - -export const MAIN_FILE = 'App.vue' -export const COMP_IDENTIFIER = `__sfc__` - -/** - * The default SFC compiler we are using is built from each commit - * but we may swap it out with a version fetched from CDNs - */ -let SFCCompiler: typeof defaultCompiler = defaultCompiler - -// @ts-ignore -const defaultVueUrl = import.meta.env.PROD - ? `${location.origin}/vue.runtime.esm-browser.js` // to be copied on build - : `${location.origin}/src/vue-dev-proxy` - -export const vueRuntimeUrl = ref(defaultVueUrl) - -export async function setVersion(version: string) { - const compilerUrl = `https://unpkg.com/@vue/compiler-sfc@${version}/dist/compiler-sfc.esm-browser.js` - const runtimeUrl = `https://unpkg.com/@vue/runtime-dom@${version}/dist/runtime-dom.esm-browser.js` - const [compiler] = await Promise.all([ - import(/* @vite-ignore */ compilerUrl), - import(/* @vite-ignore */ runtimeUrl) - ]) - SFCCompiler = compiler - vueRuntimeUrl.value = runtimeUrl - console.info(`Now using Vue version: ${version}`) -} - -export function resetVersion() { - SFCCompiler = defaultCompiler - vueRuntimeUrl.value = defaultVueUrl -} - -async function transformTS(src: string) { - return (await import('sucrase')).transform(src, { - transforms: ['typescript'] - }).code -} - -export async function compileFile({ filename, code, compiled }: File) { - if (!code.trim()) { - store.errors = [] - return - } - - if (!filename.endsWith('.vue')) { - if (shouldTransformRef(code)) { - code = transformRef(code, { filename }).code - } - - if (filename.endsWith('.ts')) { - code = await transformTS(code) - } - - compiled.js = compiled.ssr = code - store.errors = [] - return - } - - const id = await hashId(filename) - const { errors, descriptor } = SFCCompiler.parse(code, { - filename, - sourceMap: true - }) - if (errors.length) { - store.errors = errors - return - } - - if ( - descriptor.styles.some(s => s.lang) || - (descriptor.template && descriptor.template.lang) - ) { - store.errors = [ - `lang="x" pre-processors for or