feat: push codegen
This commit is contained in:
parent
fe1780d9ff
commit
9843175c2c
|
@ -69,7 +69,7 @@ export interface CodegenResult {
|
||||||
map?: RawSourceMap
|
map?: RawSourceMap
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NewlineType {
|
export enum NewlineType {
|
||||||
Start = 0,
|
Start = 0,
|
||||||
End = -1,
|
End = -1,
|
||||||
None = -2,
|
None = -2,
|
||||||
|
|
|
@ -21,7 +21,12 @@ export {
|
||||||
type StructuralDirectiveTransform,
|
type StructuralDirectiveTransform,
|
||||||
type DirectiveTransform
|
type DirectiveTransform
|
||||||
} from './transform'
|
} from './transform'
|
||||||
export { generate, type CodegenContext, type CodegenResult } from './codegen'
|
export {
|
||||||
|
generate,
|
||||||
|
NewlineType,
|
||||||
|
type CodegenContext,
|
||||||
|
type CodegenResult
|
||||||
|
} from './codegen'
|
||||||
export {
|
export {
|
||||||
ErrorCodes,
|
ErrorCodes,
|
||||||
errorMessages,
|
errorMessages,
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
exports[`compile > bindings 1`] = `
|
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';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div>count is <!>.</div>');
|
const t0 = _template('<div>count is <!>.</div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [
|
0: [
|
||||||
|
@ -20,13 +19,13 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
exports[`compile > directives > v-bind > simple expression 1`] = `
|
||||||
"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div></div>');
|
const t0 = _template('<div></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [n1],
|
0: [n1],
|
||||||
|
@ -36,13 +35,13 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
exports[`compile > directives > v-html > no expression 1`] = `
|
||||||
"import { template as _template, children as _children, effect as _effect, setHtml as _setHtml } from 'vue/vapor';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div></div>');
|
const t0 = _template('<div></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [n1],
|
0: [n1],
|
||||||
|
@ -52,13 +51,13 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
exports[`compile > directives > v-html > simple expression 1`] = `
|
||||||
"import { template as _template, children as _children, effect as _effect, setHtml as _setHtml } from 'vue/vapor';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div></div>');
|
const t0 = _template('<div></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [n1],
|
0: [n1],
|
||||||
|
@ -68,13 +67,13 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
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';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div></div>');
|
const t0 = _template('<div></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [n1],
|
0: [n1],
|
||||||
|
@ -84,13 +83,13 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
exports[`compile > directives > v-on > simple expression 1`] = `
|
||||||
"import { template as _template, children as _children, effect as _effect, on as _on } from 'vue/vapor';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div></div>');
|
const t0 = _template('<div></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [n1],
|
0: [n1],
|
||||||
|
@ -100,13 +99,13 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
exports[`compile > directives > v-once > as root node 1`] = `
|
||||||
"import { template as _template, children as _children, setAttr as _setAttr } from 'vue/vapor';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div></div>');
|
const t0 = _template('<div></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [n1],
|
0: [n1],
|
||||||
|
@ -114,13 +113,13 @@ export function render(_ctx) {
|
||||||
_setAttr(n1, 'id', undefined, foo);
|
_setAttr(n1, 'id', undefined, foo);
|
||||||
return n0;
|
return n0;
|
||||||
}
|
}
|
||||||
|
import { template as _template, children as _children, setAttr as _setAttr } from 'vue/vapor';
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compile > directives > v-once > basic 1`] = `
|
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';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div> <span></span></div>');
|
const t0 = _template('<div> <span></span></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [
|
0: [
|
||||||
|
@ -136,23 +135,23 @@ export function render(_ctx) {
|
||||||
_prepend(n3, n1);
|
_prepend(n3, n1);
|
||||||
return n0;
|
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`] = `
|
exports[`compile > directives > v-pre > basic 1`] = `
|
||||||
"import { template as _template } from 'vue/vapor';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>');
|
const t0 = _template('<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
return n0;
|
return n0;
|
||||||
}
|
}
|
||||||
|
import { template as _template } from 'vue/vapor';
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compile > directives > v-pre > self-closing v-pre 1`] = `
|
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';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div></div><div><Comp></Comp></div>');
|
const t0 = _template('<div></div><div><Comp></Comp></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
1: [n1],
|
1: [n1],
|
||||||
|
@ -167,13 +166,13 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
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';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div><div><Comp></Comp></div>');
|
const t0 = _template('<div :id=\\"foo\\"><Comp></Comp>{{ bar }}</div><div><Comp></Comp></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
1: [n1],
|
1: [n1],
|
||||||
|
@ -188,13 +187,13 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
exports[`compile > directives > v-text > no expression 1`] = `
|
||||||
"import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div></div>');
|
const t0 = _template('<div></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [n1],
|
0: [n1],
|
||||||
|
@ -204,13 +203,13 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
exports[`compile > directives > v-text > simple expression 1`] = `
|
||||||
"import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div></div>');
|
const t0 = _template('<div></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [n1],
|
0: [n1],
|
||||||
|
@ -220,12 +219,12 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
return n0;
|
||||||
}
|
}
|
||||||
|
import { template as _template, children as _children, effect as _effect, setText as _setText } from 'vue/vapor';
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compile > dynamic root 1`] = `
|
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 t0 = _fragment();
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const n1 = _createTextNode(1);
|
const n1 = _createTextNode(1);
|
||||||
|
@ -239,13 +238,13 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
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';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<button>foo<!>foo</button>');
|
const t0 = _template('<button>foo<!>foo</button>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
0: [
|
0: [
|
||||||
|
@ -272,23 +271,23 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
exports[`compile > fragment 1`] = `
|
||||||
"import { template as _template } from 'vue/vapor';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<p></p><span></span><div></div>');
|
const t0 = _template('<p></p><span></span><div></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
return n0;
|
return n0;
|
||||||
}
|
}
|
||||||
|
import { template as _template } from 'vue/vapor';
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`compile > static + dynamic root 1`] = `
|
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';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('3<!>6<!>9');
|
const t0 = _template('3<!>6<!>9');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
const {
|
const {
|
||||||
1: [n9],
|
1: [n9],
|
||||||
|
@ -332,15 +331,16 @@ export function render(_ctx) {
|
||||||
});
|
});
|
||||||
return n0;
|
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`] = `
|
exports[`compile > static template 1`] = `
|
||||||
"import { template as _template } from 'vue/vapor';
|
"export function render(_ctx) {
|
||||||
const t0 = _template('<div><p>hello</p><input><span></span></div>');
|
const t0 = _template('<div><p>hello</p><input><span></span></div>');
|
||||||
export function render(_ctx) {
|
|
||||||
const n0 = t0();
|
const n0 = t0();
|
||||||
return n0;
|
return n0;
|
||||||
}
|
}
|
||||||
|
import { template as _template } from 'vue/vapor';
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
exports[`fixtures 1`] = `
|
exports[`fixtures 1`] = `
|
||||||
"import { defineComponent as _defineComponent } from 'vue'
|
"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'
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
const html = '<b>HTML</b>'
|
const html = '<b>HTML</b>'
|
||||||
|
@ -18,6 +16,7 @@ const increment = () => count.value++
|
||||||
|
|
||||||
|
|
||||||
return (() => {
|
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 n0 = t0()
|
||||||
const { 1: [n2], 2: [n4], 3: [n5], 4: [n6], 6: [n8],} = _children(n0)
|
const { 1: [n2], 2: [n4], 3: [n5], 4: [n6], 6: [n8],} = _children(n0)
|
||||||
const n1 = _createTextNode(count.value)
|
const n1 = _createTextNode(count.value)
|
||||||
|
@ -40,8 +39,9 @@ _effect(() => {
|
||||||
_setHtml(n6, undefined, html)
|
_setHtml(n6, undefined, html)
|
||||||
})
|
})
|
||||||
return n0
|
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",
|
"homepage": "https://github.com/vuejs/core-vapor/tree/main/packages/compiler-vapor#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vue/compiler-dom": "3.3.8",
|
||||||
"@vue/shared": "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 {
|
import {
|
||||||
type DynamicChildren,
|
type DynamicChildren,
|
||||||
type RootIRNode,
|
type RootIRNode,
|
||||||
IRNodeTypes,
|
IRNodeTypes,
|
||||||
OperationNode,
|
OperationNode,
|
||||||
VaporHelper,
|
VaporHelper,
|
||||||
|
IRNode,
|
||||||
} from './ir'
|
} from './ir'
|
||||||
|
import { SourceMapGenerator } from 'source-map-js'
|
||||||
|
|
||||||
// remove when stable
|
// 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>
|
helpers: Set<string>
|
||||||
vaporHelpers: Set<string>
|
vaporHelpers: Set<string>
|
||||||
helper(name: string): string
|
helper(name: string): string
|
||||||
vaporHelper(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
|
const { helpers, vaporHelpers } = ir
|
||||||
return {
|
const context: CodegenContext = {
|
||||||
options,
|
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,
|
helpers,
|
||||||
vaporHelpers,
|
vaporHelpers,
|
||||||
helper(name: string) {
|
helper(name: string) {
|
||||||
|
@ -32,7 +90,104 @@ function createCodegenContext(ir: RootIRNode, options: CodegenOptions) {
|
||||||
vaporHelpers.add(name)
|
vaporHelpers.add(name)
|
||||||
return `_${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
|
// IR -> JS codegen
|
||||||
|
@ -43,86 +198,112 @@ export function generate(
|
||||||
const ctx = createCodegenContext(ir, options)
|
const ctx = createCodegenContext(ir, options)
|
||||||
const { vaporHelper, helpers, vaporHelpers } = ctx
|
const { vaporHelper, helpers, vaporHelpers } = ctx
|
||||||
|
|
||||||
let code = ''
|
const functionName = 'render'
|
||||||
let preamble = ''
|
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) => {
|
ir.template.forEach((template, i) => {
|
||||||
if (template.type === IRNodeTypes.TEMPLATE_FACTORY) {
|
if (template.type === IRNodeTypes.TEMPLATE_FACTORY) {
|
||||||
preamble += `const t${i} = ${vaporHelper('template')}(${JSON.stringify(
|
// TODO source map?
|
||||||
template.template,
|
ctx.push(
|
||||||
)})\n`
|
`const t${i} = ${vaporHelper('template')}(${JSON.stringify(
|
||||||
|
template.template,
|
||||||
|
)})\n`,
|
||||||
|
NewlineType.End,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// fragment
|
// 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)
|
const children = genChildren(ir.dynamic.children)
|
||||||
if (children) {
|
if (children) {
|
||||||
code += `const ${children} = ${vaporHelper('children')}(n${
|
ctx.push(
|
||||||
ir.dynamic.id
|
`const ${children} = ${vaporHelper('children')}(n${ir.dynamic.id})\n`,
|
||||||
})\n`
|
NewlineType.End,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const operation of ir.operation) {
|
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)) {
|
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) {
|
for (const operation of operations) {
|
||||||
scope += genOperation(operation, ctx)
|
genOperation(operation, ctx).forEach((args) => ctx.push(...args))
|
||||||
}
|
}
|
||||||
scope += '})\n'
|
ctx.push('})\n', NewlineType.End)
|
||||||
code += scope
|
|
||||||
}
|
}
|
||||||
// TODO multiple-template
|
// TODO multiple-template
|
||||||
// TODO return statement in IR
|
// 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)
|
if (vaporHelpers.size)
|
||||||
// TODO: extract
|
// TODO: extract
|
||||||
preamble =
|
ctx.push(
|
||||||
`import { ${[...vaporHelpers]
|
`import { ${[...vaporHelpers]
|
||||||
.map((h) => `${h} as _${h}`)
|
.map((h) => `${h} as _${h}`)
|
||||||
.join(', ')} } from 'vue/vapor'\n` + preamble
|
.join(', ')} } from 'vue/vapor'\n`,
|
||||||
|
NewlineType.End,
|
||||||
|
)
|
||||||
if (helpers.size)
|
if (helpers.size)
|
||||||
preamble =
|
ctx.push(
|
||||||
`import { ${[...helpers]
|
`import { ${[...helpers]
|
||||||
.map((h) => `${h} as _${h}`)
|
.map((h) => `${h} as _${h}`)
|
||||||
.join(', ')} } from 'vue'\n` + preamble
|
.join(', ')} } from 'vue'\n`,
|
||||||
|
NewlineType.End,
|
||||||
const functionName = 'render'
|
)
|
||||||
const isSetupInlined = !!options.inline
|
|
||||||
if (isSetupInlined) {
|
|
||||||
code = `(() => {\n${code}\n})();`
|
|
||||||
} else {
|
|
||||||
code = `${preamble}export function ${functionName}(_ctx) {\n${code}\n}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code,
|
code: ctx.code,
|
||||||
ast: ir as any,
|
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
|
// TODO: cache old value
|
||||||
switch (oper.type) {
|
switch (oper.type) {
|
||||||
case IRNodeTypes.SET_PROP: {
|
case IRNodeTypes.SET_PROP: {
|
||||||
return `${vaporHelper('setAttr')}(n${oper.element}, ${JSON.stringify(
|
return [
|
||||||
oper.name,
|
[
|
||||||
)}, undefined, ${oper.value})\n`
|
`${vaporHelper('setAttr')}(n${oper.element}, ${JSON.stringify(
|
||||||
|
oper.name,
|
||||||
|
)}, undefined, ${oper.value})\n`,
|
||||||
|
NewlineType.End,
|
||||||
|
],
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
case IRNodeTypes.SET_TEXT: {
|
case IRNodeTypes.SET_TEXT: {
|
||||||
return `${vaporHelper('setText')}(n${oper.element}, undefined, ${
|
return [
|
||||||
oper.value
|
[
|
||||||
})\n`
|
`${vaporHelper('setText')}(n${oper.element}, undefined, ${
|
||||||
|
oper.value
|
||||||
|
})\n`,
|
||||||
|
NewlineType.End,
|
||||||
|
],
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
case IRNodeTypes.SET_EVENT: {
|
case IRNodeTypes.SET_EVENT: {
|
||||||
|
@ -132,43 +313,73 @@ function genOperation(oper: OperationNode, { vaporHelper }: CodegenContext) {
|
||||||
oper.modifiers,
|
oper.modifiers,
|
||||||
)})`
|
)})`
|
||||||
}
|
}
|
||||||
return `${vaporHelper('on')}(n${oper.element}, ${JSON.stringify(
|
return [
|
||||||
oper.name,
|
[
|
||||||
)}, ${value})\n`
|
`${vaporHelper('on')}(n${oper.element}, ${JSON.stringify(
|
||||||
|
oper.name,
|
||||||
|
)}, ${value})\n`,
|
||||||
|
NewlineType.End,
|
||||||
|
],
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
case IRNodeTypes.SET_HTML: {
|
case IRNodeTypes.SET_HTML: {
|
||||||
return `${vaporHelper('setHtml')}(n${oper.element}, undefined, ${
|
return [
|
||||||
oper.value
|
[
|
||||||
})\n`
|
`${vaporHelper('setHtml')}(n${oper.element}, undefined, ${
|
||||||
|
oper.value
|
||||||
|
})\n`,
|
||||||
|
NewlineType.End,
|
||||||
|
],
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
case IRNodeTypes.CREATE_TEXT_NODE: {
|
case IRNodeTypes.CREATE_TEXT_NODE: {
|
||||||
return `const n${oper.id} = ${vaporHelper('createTextNode')}(${
|
return [
|
||||||
oper.value
|
[
|
||||||
})\n`
|
`const n${oper.id} = ${vaporHelper('createTextNode')}(${
|
||||||
|
oper.value
|
||||||
|
})\n`,
|
||||||
|
NewlineType.End,
|
||||||
|
],
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
case IRNodeTypes.INSERT_NODE: {
|
case IRNodeTypes.INSERT_NODE: {
|
||||||
const elements = ([] as number[]).concat(oper.element)
|
const elements = ([] as number[]).concat(oper.element)
|
||||||
let element = elements.map((el) => `n${el}`).join(', ')
|
let element = elements.map((el) => `n${el}`).join(', ')
|
||||||
if (elements.length > 1) element = `[${element}]`
|
if (elements.length > 1) element = `[${element}]`
|
||||||
return `${vaporHelper('insert')}(${element}, n${
|
return [
|
||||||
oper.parent
|
[
|
||||||
}${`, n${oper.anchor}`})\n`
|
`${vaporHelper('insert')}(${element}, n${
|
||||||
|
oper.parent
|
||||||
|
}${`, n${oper.anchor}`})\n`,
|
||||||
|
NewlineType.End,
|
||||||
|
],
|
||||||
|
]
|
||||||
}
|
}
|
||||||
case IRNodeTypes.PREPEND_NODE: {
|
case IRNodeTypes.PREPEND_NODE: {
|
||||||
return `${vaporHelper('prepend')}(n${oper.parent}, ${oper.elements
|
return [
|
||||||
.map((el) => `n${el}`)
|
[
|
||||||
.join(', ')})\n`
|
`${vaporHelper('prepend')}(n${oper.parent}, ${oper.elements
|
||||||
|
.map((el) => `n${el}`)
|
||||||
|
.join(', ')})\n`,
|
||||||
|
NewlineType.End,
|
||||||
|
],
|
||||||
|
]
|
||||||
}
|
}
|
||||||
case IRNodeTypes.APPEND_NODE: {
|
case IRNodeTypes.APPEND_NODE: {
|
||||||
return `${vaporHelper('append')}(n${oper.parent}, ${oper.elements
|
return [
|
||||||
.map((el) => `n${el}`)
|
[
|
||||||
.join(', ')})\n`
|
`${vaporHelper('append')}(n${oper.parent}, ${oper.elements
|
||||||
|
.map((el) => `n${el}`)
|
||||||
|
.join(', ')})\n`,
|
||||||
|
NewlineType.End,
|
||||||
|
],
|
||||||
|
]
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
checkNever(oper)
|
return checkNever(oper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ export type VaporHelper = keyof typeof import('../../runtime-vapor/src')
|
||||||
|
|
||||||
export interface RootIRNode extends IRNode {
|
export interface RootIRNode extends IRNode {
|
||||||
type: IRNodeTypes.ROOT
|
type: IRNodeTypes.ROOT
|
||||||
|
source: string
|
||||||
template: Array<TemplateFactoryIRNode | FragmentFactoryIRNode>
|
template: Array<TemplateFactoryIRNode | FragmentFactoryIRNode>
|
||||||
dynamic: DynamicInfo
|
dynamic: DynamicInfo
|
||||||
// TODO multi-expression effect
|
// TODO multi-expression effect
|
||||||
|
|
|
@ -175,6 +175,7 @@ export function transform(
|
||||||
|
|
||||||
const ir: RootIRNode = {
|
const ir: RootIRNode = {
|
||||||
type: IRNodeTypes.ROOT,
|
type: IRNodeTypes.ROOT,
|
||||||
|
source: root.source,
|
||||||
loc: root.loc,
|
loc: root.loc,
|
||||||
template: [],
|
template: [],
|
||||||
dynamic: {
|
dynamic: {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as m from 'monaco-editor'
|
import * as m from 'monaco-editor'
|
||||||
import { CompilerError, CompilerOptions } from '@vue/compiler-dom'
|
import { CompilerError } from '@vue/compiler-dom'
|
||||||
import { compile } from '@vue/compiler-vapor'
|
import { compile, CompilerOptions } from '@vue/compiler-vapor'
|
||||||
import { compile as ssrCompile } from '@vue/compiler-ssr'
|
// import { compile as ssrCompile } from '@vue/compiler-ssr'
|
||||||
import {
|
import {
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
compilerOptions,
|
compilerOptions,
|
||||||
|
@ -74,7 +74,7 @@ window.init = () => {
|
||||||
console.clear()
|
console.clear()
|
||||||
try {
|
try {
|
||||||
const errors: CompilerError[] = []
|
const errors: CompilerError[] = []
|
||||||
const compileFn = ssrMode.value ? ssrCompile : compile
|
const compileFn = /* ssrMode.value ? ssrCompile : */ compile
|
||||||
const start = performance.now()
|
const start = performance.now()
|
||||||
const { code, ast, map } = compileFn(source, {
|
const { code, ast, map } = compileFn(source, {
|
||||||
...compilerOptions,
|
...compilerOptions,
|
||||||
|
@ -93,8 +93,8 @@ window.init = () => {
|
||||||
console.log(`AST: `, ast)
|
console.log(`AST: `, ast)
|
||||||
console.log(`Options: `, toRaw(compilerOptions))
|
console.log(`Options: `, toRaw(compilerOptions))
|
||||||
lastSuccessfulCode = code + `\n\n// Check the console for the AST`
|
lastSuccessfulCode = code + `\n\n// Check the console for the AST`
|
||||||
// lastSuccessfulMap = new SourceMapConsumer(map!)
|
lastSuccessfulMap = new SourceMapConsumer(map!)
|
||||||
// lastSuccessfulMap!.computeColumnSpans()
|
lastSuccessfulMap!.computeColumnSpans()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
lastSuccessfulCode = `/* ERROR: ${e.message} (see console for more info) */`
|
lastSuccessfulCode = `/* ERROR: ${e.message} (see console for more info) */`
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { h, reactive, createApp, ref } from 'vue'
|
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'
|
import { BindingTypes } from '@vue/compiler-core'
|
||||||
|
|
||||||
export const ssrMode = ref(false)
|
export const ssrMode = ref(false)
|
||||||
|
|
|
@ -274,6 +274,9 @@ importers:
|
||||||
'@vue/shared':
|
'@vue/shared':
|
||||||
specifier: 3.3.8
|
specifier: 3.3.8
|
||||||
version: 3.3.8
|
version: 3.3.8
|
||||||
|
source-map-js:
|
||||||
|
specifier: ^1.0.2
|
||||||
|
version: 1.0.2
|
||||||
|
|
||||||
packages/dts-built-test:
|
packages/dts-built-test:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
Loading…
Reference in New Issue