| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- import * as m from 'monaco-editor'
- import { compile, CompilerError, CompilerOptions } from '@vue/compiler-dom'
- import { compile as ssrCompile } from '@vue/compiler-ssr'
- import { compilerOptions, initOptions, ssrMode } from './options'
- import { watch } from '@vue/runtime-dom'
- import { SourceMapConsumer } from 'source-map'
- declare global {
- interface Window {
- monaco: typeof m
- _deps: any
- init: () => void
- }
- }
- interface PersistedState {
- src: string
- ssr: boolean
- options: CompilerOptions
- }
- window.init = () => {
- const monaco = window.monaco
- const persistedState: PersistedState = JSON.parse(
- decodeURIComponent(window.location.hash.slice(1)) ||
- localStorage.getItem('state') ||
- `{}`
- )
- ssrMode.value = persistedState.ssr
- Object.assign(compilerOptions, persistedState.options)
- let lastSuccessfulCode: string = `/* See console for error */`
- let lastSuccessfulMap: SourceMapConsumer | undefined = undefined
- function compileCode(source: string): string {
- console.clear()
- try {
- const errors: CompilerError[] = []
- const compileFn = ssrMode.value ? ssrCompile : compile
- const start = performance.now()
- const { code, ast, map } = compileFn(source, {
- filename: 'template.vue',
- ...compilerOptions,
- sourceMap: true,
- onError: err => {
- errors.push(err)
- }
- })
- console.log(`Compiled in ${(performance.now() - start).toFixed(2)}ms.`)
- monaco.editor.setModelMarkers(
- editor.getModel()!,
- `@vue/compiler-dom`,
- errors.filter(e => e.loc).map(formatError)
- )
- console.log(`AST: `, ast)
- lastSuccessfulCode = code + `\n\n// Check the console for the AST`
- lastSuccessfulMap = new window._deps['source-map'].SourceMapConsumer(map)
- lastSuccessfulMap!.computeColumnSpans()
- } catch (e) {
- console.error(e)
- }
- return lastSuccessfulCode
- }
- function formatError(err: CompilerError) {
- const loc = err.loc!
- return {
- severity: monaco.MarkerSeverity.Error,
- startLineNumber: loc.start.line,
- startColumn: loc.start.column,
- endLineNumber: loc.end.line,
- endColumn: loc.end.column,
- message: `Vue template compilation error: ${err.message}`,
- code: String(err.code)
- }
- }
- function reCompile() {
- const src = editor.getValue()
- // every time we re-compile, persist current state
- const state = JSON.stringify({
- src,
- ssr: ssrMode.value,
- options: compilerOptions
- } as PersistedState)
- localStorage.setItem('state', state)
- window.location.hash = encodeURIComponent(state)
- const res = compileCode(src)
- if (res) {
- output.setValue(res)
- }
- }
- const sharedEditorOptions: m.editor.IEditorConstructionOptions = {
- theme: 'vs-dark',
- fontSize: 14,
- wordWrap: 'on',
- scrollBeyondLastLine: false,
- renderWhitespace: 'selection',
- contextmenu: false,
- minimap: {
- enabled: false
- }
- }
- const editor = monaco.editor.create(document.getElementById('source')!, {
- value: persistedState.src || `<div>Hello World!</div>`,
- language: 'html',
- ...sharedEditorOptions
- })
- editor.getModel()!.updateOptions({
- tabSize: 2
- })
- const output = monaco.editor.create(document.getElementById('output')!, {
- value: '',
- language: 'javascript',
- readOnly: true,
- ...sharedEditorOptions
- })
- output.getModel()!.updateOptions({
- tabSize: 2
- })
- // handle resize
- window.addEventListener('resize', () => {
- editor.layout()
- output.layout()
- })
- // update compile output when input changes
- editor.onDidChangeModelContent(debounce(reCompile))
- // highlight output code
- let prevOutputDecos: string[] = []
- function clearOutputDecos() {
- prevOutputDecos = output.deltaDecorations(prevOutputDecos, [])
- }
- editor.onDidChangeCursorPosition(
- debounce(e => {
- clearEditorDecos()
- if (lastSuccessfulMap) {
- const pos = lastSuccessfulMap.generatedPositionFor({
- source: 'template.vue',
- line: e.position.lineNumber,
- column: e.position.column - 1
- })
- if (pos.line != null && pos.column != null) {
- prevOutputDecos = output.deltaDecorations(prevOutputDecos, [
- {
- range: new monaco.Range(
- pos.line,
- pos.column + 1,
- pos.line,
- pos.lastColumn ? pos.lastColumn + 2 : pos.column + 2
- ),
- options: {
- inlineClassName: `highlight`
- }
- }
- ])
- output.revealPositionInCenter({
- lineNumber: pos.line,
- column: pos.column + 1
- })
- } else {
- clearOutputDecos()
- }
- }
- }, 100)
- )
- let previousEditorDecos: string[] = []
- function clearEditorDecos() {
- previousEditorDecos = editor.deltaDecorations(previousEditorDecos, [])
- }
- output.onDidChangeCursorPosition(
- debounce(e => {
- clearOutputDecos()
- if (lastSuccessfulMap) {
- const pos = lastSuccessfulMap.originalPositionFor({
- line: e.position.lineNumber,
- column: e.position.column - 1
- })
- if (
- pos.line != null &&
- pos.column != null &&
- !// ignore mock location
- (pos.line === 1 && pos.column === 0)
- ) {
- const translatedPos = {
- column: pos.column + 1,
- lineNumber: pos.line
- }
- previousEditorDecos = editor.deltaDecorations(previousEditorDecos, [
- {
- range: new monaco.Range(
- pos.line,
- pos.column + 1,
- pos.line,
- pos.column + 1
- ),
- options: {
- isWholeLine: true,
- className: `highlight`
- }
- }
- ])
- editor.revealPositionInCenter(translatedPos)
- } else {
- clearEditorDecos()
- }
- }
- }, 100)
- )
- initOptions()
- watch(reCompile)
- }
- function debounce<T extends (...args: any[]) => any>(
- fn: T,
- delay: number = 300
- ): T {
- let prevTimer: number | null = null
- return ((...args: any[]) => {
- if (prevTimer) {
- clearTimeout(prevTimer)
- }
- prevTimer = window.setTimeout(() => {
- fn(...args)
- prevTimer = null
- }, delay)
- }) as any
- }
|