feat: push codegen
This commit is contained in:
parent
fe1780d9ff
commit
9843175c2c
|
@ -69,7 +69,7 @@ export interface CodegenResult {
|
|||
map?: RawSourceMap
|
||||
}
|
||||
|
||||
enum NewlineType {
|
||||
export enum NewlineType {
|
||||
Start = 0,
|
||||
End = -1,
|
||||
None = -2,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
"
|
||||
`;
|
||||
|
|
|
@ -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'
|
||||
|
||||
})();
|
||||
}
|
||||
|
||||
})"
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -175,6 +175,7 @@ export function transform(
|
|||
|
||||
const ir: RootIRNode = {
|
||||
type: IRNodeTypes.ROOT,
|
||||
source: root.source,
|
||||
loc: root.loc,
|
||||
template: [],
|
||||
dynamic: {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue