]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: push codegen
author三咲智子 Kevin Deng <sxzz@sxzz.moe>
Fri, 1 Dec 2023 14:12:19 +0000 (22:12 +0800)
committer三咲智子 Kevin Deng <sxzz@sxzz.moe>
Fri, 1 Dec 2023 14:12:19 +0000 (22:12 +0800)
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/index.ts
packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap
packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap
packages/compiler-vapor/package.json
packages/compiler-vapor/src/generate.ts
packages/compiler-vapor/src/ir.ts
packages/compiler-vapor/src/transform.ts
packages/template-explorer/src/index.ts
packages/template-explorer/src/options.ts
pnpm-lock.yaml

index 890ef9bfda4211a8ecc58c163d0e1ae40f239a71..6ab8a998704761c55454202a3f3bf64bec12ee7d 100644 (file)
@@ -69,7 +69,7 @@ export interface CodegenResult {
   map?: RawSourceMap
 }
 
-enum NewlineType {
+export enum NewlineType {
   Start = 0,
   End = -1,
   None = -2,
index 25a446e7f466d87f5f2ea7ac450140a67ae4db57..d4f2f3d12bfea50c4597a76714fd249fa0fe6c43 100644 (file)
@@ -21,7 +21,12 @@ export {
   type StructuralDirectiveTransform,
   type DirectiveTransform
 } from './transform'
-export { generate, type CodegenContext, type CodegenResult } from './codegen'
+export {
+  generate,
+  NewlineType,
+  type CodegenContext,
+  type CodegenResult
+} from './codegen'
 export {
   ErrorCodes,
   errorMessages,
index 74932f20d894f0661865b88e9ece33e49eea0c97..22883caa72ddab4036f13278f5784f479a1b2e67 100644 (file)
@@ -1,9 +1,8 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compile > bindings 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, insert as _insert, effect as _effect, setText as _setText } from 'vue/vapor';
-const t0 = _template('<div>count is <!>.</div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div>count is <!>.</div>');
   const n0 = t0();
   const {
     0: [
@@ -20,13 +19,13 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, createTextNode as _createTextNode, insert as _insert, effect as _effect, setText as _setText } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-bind > simple expression 1`] = `
-"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
-const t0 = _template('<div></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div></div>');
   const n0 = t0();
   const {
     0: [n1],
@@ -36,13 +35,13 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-html > no expression 1`] = `
-"import { template as _template, children as _children, effect as _effect, setHtml as _setHtml } from 'vue/vapor';
-const t0 = _template('<div></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div></div>');
   const n0 = t0();
   const {
     0: [n1],
@@ -52,13 +51,13 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, effect as _effect, setHtml as _setHtml } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-html > simple expression 1`] = `
-"import { template as _template, children as _children, effect as _effect, setHtml as _setHtml } from 'vue/vapor';
-const t0 = _template('<div></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div></div>');
   const n0 = t0();
   const {
     0: [n1],
@@ -68,13 +67,13 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, effect as _effect, setHtml as _setHtml } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-on > event modifier 1`] = `
-"import { template as _template, children as _children, effect as _effect, withModifiers as _withModifiers, on as _on } from 'vue/vapor';
-const t0 = _template('<div></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div></div>');
   const n0 = t0();
   const {
     0: [n1],
@@ -84,13 +83,13 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, effect as _effect, withModifiers as _withModifiers, on as _on } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-on > simple expression 1`] = `
-"import { template as _template, children as _children, effect as _effect, on as _on } from 'vue/vapor';
-const t0 = _template('<div></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div></div>');
   const n0 = t0();
   const {
     0: [n1],
@@ -100,13 +99,13 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, effect as _effect, on as _on } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-once > as root node 1`] = `
-"import { template as _template, children as _children, setAttr as _setAttr } from 'vue/vapor';
-const t0 = _template('<div></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div></div>');
   const n0 = t0();
   const {
     0: [n1],
@@ -114,13 +113,13 @@ export function render(_ctx) {
   _setAttr(n1, 'id', undefined, foo);
   return n0;
 }
+import { template as _template, children as _children, setAttr as _setAttr } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-once > basic 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setAttr as _setAttr, prepend as _prepend } from 'vue/vapor';
-const t0 = _template('<div> <span></span></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div> <span></span></div>');
   const n0 = t0();
   const {
     0: [
@@ -136,23 +135,23 @@ export function render(_ctx) {
   _prepend(n3, n1);
   return n0;
 }
+import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setAttr as _setAttr, prepend as _prepend } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-pre > basic 1`] = `
-"import { template as _template } from 'vue/vapor';
-const t0 = _template('<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>');
   const n0 = t0();
   return n0;
 }
+import { template as _template } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-pre > self-closing v-pre 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
-const t0 = _template('<div></div><div><Comp></Comp></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div></div><div><Comp></Comp></div>');
   const n0 = t0();
   const {
     1: [n1],
@@ -167,13 +166,13 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-pre > should not affect siblings after it 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
-const t0 = _template('<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div><div><Comp></Comp></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div><div><Comp></Comp></div>');
   const n0 = t0();
   const {
     1: [n1],
@@ -188,13 +187,13 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-text > no expression 1`] = `
-"import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor';
-const t0 = _template('<div></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div></div>');
   const n0 = t0();
   const {
     0: [n1],
@@ -204,13 +203,13 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor';
 "
 `;
 
 exports[`compile > directives > v-text > simple expression 1`] = `
-"import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor';
-const t0 = _template('<div></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div></div>');
   const n0 = t0();
   const {
     0: [n1],
@@ -220,12 +219,12 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor';
 "
 `;
 
 exports[`compile > dynamic root 1`] = `
-"import { fragment as _fragment, createTextNode as _createTextNode, append as _append, effect as _effect, setText as _setText } from 'vue/vapor';
-export function render(_ctx) {
+"export function render(_ctx) {
   const t0 = _fragment();
   const n0 = t0();
   const n1 = _createTextNode(1);
@@ -239,13 +238,13 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { fragment as _fragment, createTextNode as _createTextNode, append as _append, effect as _effect, setText as _setText } from 'vue/vapor';
 "
 `;
 
 exports[`compile > dynamic root nodes and interpolation 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, effect as _effect, on as _on, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
-const t0 = _template('<button>foo<!>foo</button>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<button>foo<!>foo</button>');
   const n0 = t0();
   const {
     0: [
@@ -272,23 +271,23 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, effect as _effect, on as _on, setAttr as _setAttr, setText as _setText } from 'vue/vapor';
 "
 `;
 
 exports[`compile > fragment 1`] = `
-"import { template as _template } from 'vue/vapor';
-const t0 = _template('<p></p><span></span><div></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<p></p><span></span><div></div>');
   const n0 = t0();
   return n0;
 }
+import { template as _template } from 'vue/vapor';
 "
 `;
 
 exports[`compile > static + dynamic root 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, effect as _effect, setText as _setText } from 'vue/vapor';
-const t0 = _template('3<!>6<!>9');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('3<!>6<!>9');
   const n0 = t0();
   const {
     1: [n9],
@@ -332,15 +331,16 @@ export function render(_ctx) {
   });
   return n0;
 }
+import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, effect as _effect, setText as _setText } from 'vue/vapor';
 "
 `;
 
 exports[`compile > static template 1`] = `
-"import { template as _template } from 'vue/vapor';
-const t0 = _template('<div><p>hello</p><input><span></span></div>');
-export function render(_ctx) {
+"export function render(_ctx) {
+  const t0 = _template('<div><p>hello</p><input><span></span></div>');
   const n0 = t0();
   return n0;
 }
+import { template as _template } from 'vue/vapor';
 "
 `;
index a4585b155d5b1779ea82a6106cdd9671dd136e3d..99a13914585c264662a5e964236897f9a5a37457 100644 (file)
@@ -2,8 +2,6 @@
 
 exports[`fixtures 1`] = `
 "import { defineComponent as _defineComponent } from 'vue'
-import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, setText as _setText, effect as _effect, on as _on, setHtml as _setHtml } from 'vue/vapor'
-const t0 = _template(\\"<h1 id=\\\\\\"title\\\\\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button><div></div><input type=\\\\\\"text\\\\\\"><p>once: </p><p>{{ count }}</p>\\")
 import { ref, computed } from 'vue'
 
 const html = '<b>HTML</b>'
@@ -18,6 +16,7 @@ const increment = () => count.value++
 
 
 return (() => {
+const t0 = _template(\\"<h1 id=\\\\\\"title\\\\\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button><div></div><input type=\\\\\\"text\\\\\\"><p>once: </p><p>{{ count }}</p>\\")
 const n0 = t0()
 const { 1: [n2], 2: [n4], 3: [n5], 4: [n6], 6: [n8],} = _children(n0)
 const n1 = _createTextNode(count.value)
@@ -40,8 +39,9 @@ _effect(() => {
 _setHtml(n6, undefined, html)
 })
 return n0
+})()
+import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, setText as _setText, effect as _effect, on as _on, setHtml as _setHtml } from 'vue/vapor'
 
-})();
 }
 
 })"
index 1b6387150909cab5e9b5c04f9d728e67d0d6b04a..2f57f3777fc497a7e0037661d52e333157359ef1 100644 (file)
@@ -37,7 +37,8 @@
   },
   "homepage": "https://github.com/vuejs/core-vapor/tree/main/packages/compiler-vapor#readme",
   "dependencies": {
+    "@vue/compiler-dom": "3.3.8",
     "@vue/shared": "3.3.8",
-    "@vue/compiler-dom": "3.3.8"
+    "source-map-js": "^1.0.2"
   }
 }
index 9c90002045da0adf6111cc818b9b9f494e2ca7a1..5eff9a9404b71bf06cd838241370e2cd2619af64 100644 (file)
@@ -1,27 +1,85 @@
-import type { CodegenOptions, CodegenResult } from '@vue/compiler-dom'
+import {
+  type CodegenOptions,
+  type CodegenResult,
+  type Position,
+  NewlineType,
+  advancePositionWithMutation,
+  locStub,
+} from '@vue/compiler-dom'
 import {
   type DynamicChildren,
   type RootIRNode,
   IRNodeTypes,
   OperationNode,
   VaporHelper,
+  IRNode,
 } from './ir'
+import { SourceMapGenerator } from 'source-map-js'
 
 // remove when stable
-function checkNever(x: never): void {}
+// @ts-expect-error
+function checkNever(x: never): never {}
+
+export interface CodegenContext
+  extends Omit<Required<CodegenOptions>, 'bindingMetadata' | 'inline'> {
+  source: string
+  code: string
+  line: number
+  column: number
+  offset: number
+  indentLevel: number
+  map?: SourceMapGenerator
+
+  push(code: string, newlineIndex?: NewlineType, node?: IRNode): void
+  indent(): void
+  deindent(withoutNewLine?: boolean): void
+  newline(): void
 
-export interface CodegenContext {
-  options: CodegenOptions
   helpers: Set<string>
   vaporHelpers: Set<string>
   helper(name: string): string
   vaporHelper(name: string): string
 }
 
-function createCodegenContext(ir: RootIRNode, options: CodegenOptions) {
+function createCodegenContext(
+  ir: RootIRNode,
+  {
+    mode = 'function',
+    prefixIdentifiers = mode === 'module',
+    sourceMap = false,
+    filename = `template.vue.html`,
+    scopeId = null,
+    optimizeImports = false,
+    runtimeGlobalName = `Vue`,
+    runtimeModuleName = `vue`,
+    ssrRuntimeModuleName = 'vue/server-renderer',
+    ssr = false,
+    isTS = false,
+    inSSR = false,
+  }: CodegenOptions,
+) {
   const { helpers, vaporHelpers } = ir
-  return {
-    options,
+  const context: CodegenContext = {
+    mode,
+    prefixIdentifiers,
+    sourceMap,
+    filename,
+    scopeId,
+    optimizeImports,
+    runtimeGlobalName,
+    runtimeModuleName,
+    ssrRuntimeModuleName,
+    ssr,
+    isTS,
+    inSSR,
+
+    source: ir.source,
+    code: ``,
+    column: 1,
+    line: 1,
+    offset: 0,
+    indentLevel: 0,
+
     helpers,
     vaporHelpers,
     helper(name: string) {
@@ -32,7 +90,104 @@ function createCodegenContext(ir: RootIRNode, options: CodegenOptions) {
       vaporHelpers.add(name)
       return `_${name}`
     },
+    push(code, newlineIndex: NewlineType = NewlineType.None, node) {
+      context.code += code
+      if (!__BROWSER__ && context.map) {
+        if (node) {
+          // TODO
+          let name
+          // if (node.type === NodeTypes.SIMPLE_EXPRESSION && !node.isStatic) {
+          //   const content = node.content.replace(/^_ctx\./, '')
+          //   if (content !== node.content && isSimpleIdentifier(content)) {
+          //     name = content
+          //   }
+          // }
+          addMapping(node.loc.start, name)
+        }
+        if (newlineIndex === NewlineType.Unknown) {
+          // multiple newlines, full iteration
+          advancePositionWithMutation(context, code)
+        } else {
+          // fast paths
+          context.offset += code.length
+          if (newlineIndex === NewlineType.None) {
+            // no newlines; fast path to avoid newline detection
+            if (__TEST__ && code.includes('\n')) {
+              throw new Error(
+                `CodegenContext.push() called newlineIndex: none, but contains` +
+                  `newlines: ${code.replace(/\n/g, '\\n')}`,
+              )
+            }
+            context.column += code.length
+          } else {
+            // single newline at known index
+            if (newlineIndex === NewlineType.End) {
+              newlineIndex = code.length - 1
+            }
+            if (
+              __TEST__ &&
+              (code.charAt(newlineIndex) !== '\n' ||
+                code.slice(0, newlineIndex).includes('\n') ||
+                code.slice(newlineIndex + 1).includes('\n'))
+            ) {
+              throw new Error(
+                `CodegenContext.push() called with newlineIndex: ${newlineIndex} ` +
+                  `but does not conform: ${code.replace(/\n/g, '\\n')}`,
+              )
+            }
+            context.line++
+            context.column = code.length - newlineIndex
+          }
+        }
+        if (node && node.loc !== locStub) {
+          addMapping(node.loc.end)
+        }
+      }
+    },
+    indent() {
+      newline(++context.indentLevel)
+    },
+    deindent(withoutNewLine = false) {
+      if (withoutNewLine) {
+        --context.indentLevel
+      } else {
+        newline(--context.indentLevel)
+      }
+    },
+    newline() {
+      newline(context.indentLevel)
+    },
+  }
+
+  function newline(n: number) {
+    context.push(`\n${`  `.repeat(n)}`, NewlineType.Start)
+  }
+
+  function addMapping(loc: Position, name: string | null = null) {
+    // we use the private property to directly add the mapping
+    // because the addMapping() implementation in source-map-js has a bunch of
+    // unnecessary arg and validation checks that are pure overhead in our case.
+    const { _names, _mappings } = context.map!
+    if (name !== null && !_names.has(name)) _names.add(name)
+    _mappings.add({
+      originalLine: loc.line,
+      originalColumn: loc.column - 1, // source-map column is 0 based
+      generatedLine: context.line,
+      generatedColumn: context.column - 1,
+      source: filename,
+      // @ts-ignore it is possible to be null
+      name,
+    })
+  }
+
+  if (!__BROWSER__ && sourceMap) {
+    // lazy require source-map implementation, only in non-browser builds
+    context.map = new SourceMapGenerator()
+    context.map.setSourceContent(filename, context.source)
+    context.map._sources.add(filename)
   }
+
+  return context
 }
 
 // IR -> JS codegen
@@ -43,86 +198,112 @@ export function generate(
   const ctx = createCodegenContext(ir, options)
   const { vaporHelper, helpers, vaporHelpers } = ctx
 
-  let code = ''
-  let preamble = ''
+  const functionName = 'render'
+  const isSetupInlined = !!options.inline
+  if (isSetupInlined) {
+    ctx.push(`(() => {\n`, NewlineType.End)
+  } else {
+    ctx.push(`export function ${functionName}(_ctx) {\n`, NewlineType.End)
+  }
 
   ir.template.forEach((template, i) => {
     if (template.type === IRNodeTypes.TEMPLATE_FACTORY) {
-      preamble += `const t${i} = ${vaporHelper('template')}(${JSON.stringify(
-        template.template,
-      )})\n`
+      // TODO source map?
+      ctx.push(
+        `const t${i} = ${vaporHelper('template')}(${JSON.stringify(
+          template.template,
+        )})\n`,
+        NewlineType.End,
+      )
     } else {
       // fragment
-      code += `const t0 = ${vaporHelper('fragment')}()\n`
+      ctx.push(`const t0 = ${vaporHelper('fragment')}()\n`, NewlineType.End)
     }
   })
 
   {
-    code += `const n${ir.dynamic.id} = t0()\n`
-
+    ctx.push(`const n${ir.dynamic.id} = t0()\n`, NewlineType.End, ir)
     const children = genChildren(ir.dynamic.children)
     if (children) {
-      code += `const ${children} = ${vaporHelper('children')}(n${
-        ir.dynamic.id
-      })\n`
+      ctx.push(
+        `const ${children} = ${vaporHelper('children')}(n${ir.dynamic.id})\n`,
+        NewlineType.End,
+      )
     }
 
     for (const operation of ir.operation) {
-      code += genOperation(operation, ctx)
+      genOperation(operation, ctx).forEach((args) => ctx.push(...args))
     }
     for (const [_expr, operations] of Object.entries(ir.effect)) {
-      let scope = `${vaporHelper('effect')}(() => {\n`
+      ctx.push(`${vaporHelper('effect')}(() => {\n`, NewlineType.End)
       for (const operation of operations) {
-        scope += genOperation(operation, ctx)
+        genOperation(operation, ctx).forEach((args) => ctx.push(...args))
       }
-      scope += '})\n'
-      code += scope
+      ctx.push('})\n', NewlineType.End)
     }
     // TODO multiple-template
     // TODO return statement in IR
-    code += `return n${ir.dynamic.id}\n`
+    ctx.push(`return n${ir.dynamic.id}\n`, NewlineType.End)
   }
 
+  if (isSetupInlined) {
+    ctx.push('})()')
+  } else {
+    ctx.push('}')
+  }
+
+  ctx.newline()
+
   if (vaporHelpers.size)
     // TODO: extract
-    preamble =
+    ctx.push(
       `import { ${[...vaporHelpers]
         .map((h) => `${h} as _${h}`)
-        .join(', ')} } from 'vue/vapor'\n` + preamble
+        .join(', ')} } from 'vue/vapor'\n`,
+      NewlineType.End,
+    )
   if (helpers.size)
-    preamble =
+    ctx.push(
       `import { ${[...helpers]
         .map((h) => `${h} as _${h}`)
-        .join(', ')} } from 'vue'\n` + preamble
-
-  const functionName = 'render'
-  const isSetupInlined = !!options.inline
-  if (isSetupInlined) {
-    code = `(() => {\n${code}\n})();`
-  } else {
-    code = `${preamble}export function ${functionName}(_ctx) {\n${code}\n}`
-  }
+        .join(', ')} } from 'vue'\n`,
+      NewlineType.End,
+    )
 
   return {
-    code,
+    code: ctx.code,
     ast: ir as any,
-    preamble,
+    preamble: '',
+    map: ctx.map ? ctx.map.toJSON() : undefined,
   }
 }
 
-function genOperation(oper: OperationNode, { vaporHelper }: CodegenContext) {
+function genOperation(
+  oper: OperationNode,
+  { vaporHelper }: CodegenContext,
+): Parameters<CodegenContext['push']>[] {
   // TODO: cache old value
   switch (oper.type) {
     case IRNodeTypes.SET_PROP: {
-      return `${vaporHelper('setAttr')}(n${oper.element}, ${JSON.stringify(
-        oper.name,
-      )}, undefined, ${oper.value})\n`
+      return [
+        [
+          `${vaporHelper('setAttr')}(n${oper.element}, ${JSON.stringify(
+            oper.name,
+          )}, undefined, ${oper.value})\n`,
+          NewlineType.End,
+        ],
+      ]
     }
 
     case IRNodeTypes.SET_TEXT: {
-      return `${vaporHelper('setText')}(n${oper.element}, undefined, ${
-        oper.value
-      })\n`
+      return [
+        [
+          `${vaporHelper('setText')}(n${oper.element}, undefined, ${
+            oper.value
+          })\n`,
+          NewlineType.End,
+        ],
+      ]
     }
 
     case IRNodeTypes.SET_EVENT: {
@@ -132,43 +313,73 @@ function genOperation(oper: OperationNode, { vaporHelper }: CodegenContext) {
           oper.modifiers,
         )})`
       }
-      return `${vaporHelper('on')}(n${oper.element}, ${JSON.stringify(
-        oper.name,
-      )}, ${value})\n`
+      return [
+        [
+          `${vaporHelper('on')}(n${oper.element}, ${JSON.stringify(
+            oper.name,
+          )}, ${value})\n`,
+          NewlineType.End,
+        ],
+      ]
     }
 
     case IRNodeTypes.SET_HTML: {
-      return `${vaporHelper('setHtml')}(n${oper.element}, undefined, ${
-        oper.value
-      })\n`
+      return [
+        [
+          `${vaporHelper('setHtml')}(n${oper.element}, undefined, ${
+            oper.value
+          })\n`,
+          NewlineType.End,
+        ],
+      ]
     }
 
     case IRNodeTypes.CREATE_TEXT_NODE: {
-      return `const n${oper.id} = ${vaporHelper('createTextNode')}(${
-        oper.value
-      })\n`
+      return [
+        [
+          `const n${oper.id} = ${vaporHelper('createTextNode')}(${
+            oper.value
+          })\n`,
+          NewlineType.End,
+        ],
+      ]
     }
 
     case IRNodeTypes.INSERT_NODE: {
       const elements = ([] as number[]).concat(oper.element)
       let element = elements.map((el) => `n${el}`).join(', ')
       if (elements.length > 1) element = `[${element}]`
-      return `${vaporHelper('insert')}(${element}, n${
-        oper.parent
-      }${`, n${oper.anchor}`})\n`
+      return [
+        [
+          `${vaporHelper('insert')}(${element}, n${
+            oper.parent
+          }${`, n${oper.anchor}`})\n`,
+          NewlineType.End,
+        ],
+      ]
     }
     case IRNodeTypes.PREPEND_NODE: {
-      return `${vaporHelper('prepend')}(n${oper.parent}, ${oper.elements
-        .map((el) => `n${el}`)
-        .join(', ')})\n`
+      return [
+        [
+          `${vaporHelper('prepend')}(n${oper.parent}, ${oper.elements
+            .map((el) => `n${el}`)
+            .join(', ')})\n`,
+          NewlineType.End,
+        ],
+      ]
     }
     case IRNodeTypes.APPEND_NODE: {
-      return `${vaporHelper('append')}(n${oper.parent}, ${oper.elements
-        .map((el) => `n${el}`)
-        .join(', ')})\n`
+      return [
+        [
+          `${vaporHelper('append')}(n${oper.parent}, ${oper.elements
+            .map((el) => `n${el}`)
+            .join(', ')})\n`,
+          NewlineType.End,
+        ],
+      ]
     }
     default:
-      checkNever(oper)
+      return checkNever(oper)
   }
 }
 
index 8bfbf3863626ac4dec02e503558c55bd9eda4f35..de518c10c31f5b9e8f73bdabfe8f8fe416d21237 100644 (file)
@@ -26,6 +26,7 @@ export type VaporHelper = keyof typeof import('../../runtime-vapor/src')
 
 export interface RootIRNode extends IRNode {
   type: IRNodeTypes.ROOT
+  source: string
   template: Array<TemplateFactoryIRNode | FragmentFactoryIRNode>
   dynamic: DynamicInfo
   // TODO multi-expression effect
index dcc781c2a78423cff33ff53b7a0d87c1d2c72ed1..df7e73d6d9da48420101ba15e5a295fd7a6a638e 100644 (file)
@@ -175,6 +175,7 @@ export function transform(
 
   const ir: RootIRNode = {
     type: IRNodeTypes.ROOT,
+    source: root.source,
     loc: root.loc,
     template: [],
     dynamic: {
index 08258514ea47da34f6d18b8a73b103e2d342337f..ca89addfb40811299d46d247a00a4d64833e259a 100644 (file)
@@ -1,7 +1,7 @@
 import * as m from 'monaco-editor'
-import { CompilerError, CompilerOptions } from '@vue/compiler-dom'
-import { compile } from '@vue/compiler-vapor'
-import { compile as ssrCompile } from '@vue/compiler-ssr'
+import { CompilerError } from '@vue/compiler-dom'
+import { compile, CompilerOptions } from '@vue/compiler-vapor'
+// import { compile as ssrCompile } from '@vue/compiler-ssr'
 import {
   defaultOptions,
   compilerOptions,
@@ -74,7 +74,7 @@ window.init = () => {
     console.clear()
     try {
       const errors: CompilerError[] = []
-      const compileFn = ssrMode.value ? ssrCompile : compile
+      const compileFn = /* ssrMode.value ? ssrCompile : */ compile
       const start = performance.now()
       const { code, ast, map } = compileFn(source, {
         ...compilerOptions,
@@ -93,8 +93,8 @@ window.init = () => {
       console.log(`AST: `, ast)
       console.log(`Options: `, toRaw(compilerOptions))
       lastSuccessfulCode = code + `\n\n// Check the console for the AST`
-      // lastSuccessfulMap = new SourceMapConsumer(map!)
-      // lastSuccessfulMap!.computeColumnSpans()
+      lastSuccessfulMap = new SourceMapConsumer(map!)
+      lastSuccessfulMap!.computeColumnSpans()
     } catch (e: any) {
       lastSuccessfulCode = `/* ERROR: ${e.message} (see console for more info) */`
       console.error(e)
index 73e0a959f79297e097f25a75c4ab44ff6cf0acff..46c0d9263d3479f78bc2fabd3a47409c742dd007 100644 (file)
@@ -1,5 +1,5 @@
 import { h, reactive, createApp, ref } from 'vue'
-import { CompilerOptions } from '@vue/compiler-dom'
+import { CompilerOptions } from '@vue/compiler-vapor'
 import { BindingTypes } from '@vue/compiler-core'
 
 export const ssrMode = ref(false)
index 4a1779f4e43d3428a86167f5906806d0e731e9c0..82ca550ff8c1f5f7ddb5f822f62368a3f0c04991 100644 (file)
@@ -274,6 +274,9 @@ importers:
       '@vue/shared':
         specifier: 3.3.8
         version: 3.3.8
+      source-map-js:
+        specifier: ^1.0.2
+        version: 1.0.2
 
   packages/dts-built-test:
     dependencies: