mirror of https://github.com/vuejs/core.git
feat: binding
This commit is contained in:
parent
717aad275d
commit
1d2f66e111
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all"
|
||||||
|
}
|
|
@ -1,27 +1,79 @@
|
||||||
import {
|
import {
|
||||||
CodegenContext,
|
CodegenContext,
|
||||||
CodegenOptions,
|
CodegenOptions,
|
||||||
CodegenResult
|
CodegenResult,
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { RootIRNode } from './transform'
|
import { DynamicChildren, IRNodeTypes, RootIRNode } from './transform'
|
||||||
|
|
||||||
// IR -> JS codegen
|
// IR -> JS codegen
|
||||||
export function generate(
|
export function generate(
|
||||||
ast: RootIRNode,
|
ir: RootIRNode,
|
||||||
options: CodegenOptions & {
|
options: CodegenOptions & {
|
||||||
onContextCreated?: (context: CodegenContext) => void
|
onContextCreated?: (context: CodegenContext) => void
|
||||||
} = {}
|
} = {},
|
||||||
): CodegenResult {
|
): CodegenResult {
|
||||||
let code = ''
|
let code = ''
|
||||||
let preamble = "import { template } from 'vue/vapor'\n"
|
let preamble = `import { watchEffect } from 'vue'
|
||||||
|
import { template, setAttr, setText, children, on, insert } from 'vue/vapor'\n`
|
||||||
|
|
||||||
const isSetupInlined = !!options.inline
|
const isSetupInlined = !!options.inline
|
||||||
|
|
||||||
preamble += ast.template
|
preamble += ir.template
|
||||||
.map((template, i) => `const t${i} = template(\`${template.template}\`)\n`)
|
.map((template, i) => `const t${i} = template(\`${template.template}\`)\n`)
|
||||||
.join('')
|
.join('')
|
||||||
|
|
||||||
code += 'const root = t0()\n'
|
code += `const root = t0()\n`
|
||||||
|
|
||||||
|
if (ir.children[0]) {
|
||||||
|
code += `const {${genChildrens(
|
||||||
|
ir.children[0].children,
|
||||||
|
)}} = children(root)\n`
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const opration of ir.opration) {
|
||||||
|
switch (opration.type) {
|
||||||
|
case IRNodeTypes.TEXT_NODE: {
|
||||||
|
code += `const n${opration.id} = document.createTextNode(${opration.content})\n`
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case IRNodeTypes.INSERT_NODE:
|
||||||
|
{
|
||||||
|
let anchor = ''
|
||||||
|
if (typeof opration.anchor === 'number') {
|
||||||
|
anchor = `, n${opration.anchor}`
|
||||||
|
} else if (opration.anchor === 'first') {
|
||||||
|
anchor = `, 0 /* InsertPosition.FIRST */`
|
||||||
|
}
|
||||||
|
code += `insert(n${opration.element}, n${opration.parent}${anchor})\n`
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [expr, effects] of Object.entries(ir.effect)) {
|
||||||
|
let scope = `watchEffect(() => {\n`
|
||||||
|
for (const effect of effects) {
|
||||||
|
switch (effect.type) {
|
||||||
|
case IRNodeTypes.SET_PROP:
|
||||||
|
scope += `setAttr(n${effect.element}, ${JSON.stringify(
|
||||||
|
effect.name,
|
||||||
|
)}, undefined, ${expr})\n`
|
||||||
|
break
|
||||||
|
case IRNodeTypes.SET_TEXT:
|
||||||
|
scope += `setText(n${effect.element}, undefined, ${expr})\n`
|
||||||
|
break
|
||||||
|
case IRNodeTypes.SET_EVENT:
|
||||||
|
scope += `on(n${effect.element}, ${JSON.stringify(
|
||||||
|
effect.name,
|
||||||
|
)}, ${expr})\n`
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope += '})\n'
|
||||||
|
code += scope
|
||||||
|
}
|
||||||
|
|
||||||
code += 'return root'
|
code += 'return root'
|
||||||
|
|
||||||
const functionName = options.ssr ? `ssrRender` : `render`
|
const functionName = options.ssr ? `ssrRender` : `render`
|
||||||
|
@ -33,7 +85,22 @@ export function generate(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code,
|
code,
|
||||||
ast: ast as any,
|
ast: ir as any,
|
||||||
preamble
|
preamble,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function genChildrens(children: DynamicChildren) {
|
||||||
|
let str = ''
|
||||||
|
for (const [index, child] of Object.entries(children)) {
|
||||||
|
str += ` ${index}: [`
|
||||||
|
if (child.store) {
|
||||||
|
str += `n${child.id}`
|
||||||
|
}
|
||||||
|
if (Object.keys(child.children).length) {
|
||||||
|
str += `, {${genChildrens(child.children)}}`
|
||||||
|
}
|
||||||
|
str += '],'
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
import {
|
import {
|
||||||
|
type NodeTypes,
|
||||||
RootNode,
|
RootNode,
|
||||||
|
Node,
|
||||||
TemplateChildNode,
|
TemplateChildNode,
|
||||||
ElementNode,
|
ElementNode,
|
||||||
AttributeNode,
|
AttributeNode,
|
||||||
SourceLocation,
|
SourceLocation,
|
||||||
NodeTypes,
|
|
||||||
InterpolationNode,
|
InterpolationNode,
|
||||||
TransformOptions
|
TransformOptions,
|
||||||
|
DirectiveNode,
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
|
|
||||||
export const enum IRNodeTypes {
|
export const enum IRNodeTypes {
|
||||||
ROOT,
|
ROOT,
|
||||||
TEMPLATE_GENERATOR
|
TEMPLATE_GENERATOR,
|
||||||
|
SET_PROP,
|
||||||
|
SET_TEXT,
|
||||||
|
SET_EVENT,
|
||||||
|
|
||||||
|
INSERT_NODE,
|
||||||
|
TEXT_NODE,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRNode {
|
export interface IRNode {
|
||||||
|
@ -22,6 +30,9 @@ export interface IRNode {
|
||||||
export interface RootIRNode extends IRNode {
|
export interface RootIRNode extends IRNode {
|
||||||
type: IRNodeTypes.ROOT
|
type: IRNodeTypes.ROOT
|
||||||
template: Array<TemplateGeneratorIRNode>
|
template: Array<TemplateGeneratorIRNode>
|
||||||
|
children: DynamicChildren
|
||||||
|
effect: Record<string, EffectNode[]>
|
||||||
|
opration: OprationNode[]
|
||||||
helpers: Set<string>
|
helpers: Set<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,89 +41,349 @@ export interface TemplateGeneratorIRNode extends IRNode {
|
||||||
template: string
|
template: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SetPropIRNode extends IRNode {
|
||||||
|
type: IRNodeTypes.SET_PROP
|
||||||
|
element: number
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SetTextIRNode extends IRNode {
|
||||||
|
type: IRNodeTypes.SET_TEXT
|
||||||
|
element: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SetEventIRNode extends IRNode {
|
||||||
|
type: IRNodeTypes.SET_EVENT
|
||||||
|
element: number
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TextNodeIRNode extends IRNode {
|
||||||
|
type: IRNodeTypes.TEXT_NODE
|
||||||
|
id: number
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InsertNodeIRNode extends IRNode {
|
||||||
|
type: IRNodeTypes.INSERT_NODE
|
||||||
|
element: number
|
||||||
|
parent: number
|
||||||
|
anchor: number | 'first' | 'last'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EffectNode = SetPropIRNode | SetTextIRNode | SetEventIRNode
|
||||||
|
export type OprationNode = TextNodeIRNode | InsertNodeIRNode
|
||||||
|
|
||||||
|
export interface DynamicChild {
|
||||||
|
id: number | null
|
||||||
|
store: boolean
|
||||||
|
children: DynamicChildren
|
||||||
|
}
|
||||||
|
export type DynamicChildren = Record<number, DynamicChild>
|
||||||
|
|
||||||
|
export interface TransformContext<T extends Node = Node> {
|
||||||
|
node: T
|
||||||
|
parent: TransformContext | null
|
||||||
|
root: TransformContext<RootNode>
|
||||||
|
index: number
|
||||||
|
options: TransformOptions
|
||||||
|
ir: RootIRNode
|
||||||
|
template: string
|
||||||
|
children: DynamicChildren
|
||||||
|
store: boolean
|
||||||
|
ghost: boolean
|
||||||
|
|
||||||
|
getElementId(): number
|
||||||
|
registerEffect(expr: string, effectNode: EffectNode): void
|
||||||
|
registerTemplate(): number
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRootContext(
|
||||||
|
ir: RootIRNode,
|
||||||
|
node: RootNode,
|
||||||
|
options: TransformOptions,
|
||||||
|
): TransformContext<RootNode> {
|
||||||
|
let i = 0
|
||||||
|
const { effect: bindings } = ir
|
||||||
|
|
||||||
|
const ctx: TransformContext<RootNode> = {
|
||||||
|
node,
|
||||||
|
parent: null,
|
||||||
|
index: 0,
|
||||||
|
root: undefined as any, // set later
|
||||||
|
options,
|
||||||
|
ir,
|
||||||
|
children: {},
|
||||||
|
store: false,
|
||||||
|
ghost: false,
|
||||||
|
|
||||||
|
getElementId: () => i++,
|
||||||
|
registerEffect(expr, effectNode) {
|
||||||
|
if (!bindings[expr]) bindings[expr] = []
|
||||||
|
bindings[expr].push(effectNode)
|
||||||
|
},
|
||||||
|
|
||||||
|
template: '',
|
||||||
|
registerTemplate() {
|
||||||
|
if (!ctx.template) return -1
|
||||||
|
|
||||||
|
const idx = ir.template.findIndex((t) => t.template === ctx.template)
|
||||||
|
if (idx !== -1) return idx
|
||||||
|
|
||||||
|
ir.template.push({
|
||||||
|
type: IRNodeTypes.TEMPLATE_GENERATOR,
|
||||||
|
template: ctx.template,
|
||||||
|
loc: node.loc,
|
||||||
|
})
|
||||||
|
return ir.template.length - 1
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ctx.root = ctx
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
function createContext<T extends TemplateChildNode>(
|
||||||
|
node: T,
|
||||||
|
parent: TransformContext,
|
||||||
|
index: number,
|
||||||
|
): TransformContext<T> {
|
||||||
|
let id: number | undefined
|
||||||
|
const getElementId = () => {
|
||||||
|
if (id !== undefined) return id
|
||||||
|
return (id = parent.root.getElementId())
|
||||||
|
}
|
||||||
|
const children = {}
|
||||||
|
|
||||||
|
const ctx: TransformContext<T> = {
|
||||||
|
...parent,
|
||||||
|
node,
|
||||||
|
parent,
|
||||||
|
index,
|
||||||
|
get template() {
|
||||||
|
return parent.template
|
||||||
|
},
|
||||||
|
set template(t) {
|
||||||
|
parent.template = t
|
||||||
|
},
|
||||||
|
getElementId,
|
||||||
|
|
||||||
|
children,
|
||||||
|
store: false,
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
// AST -> IR
|
// AST -> IR
|
||||||
export function transform(
|
export function transform(
|
||||||
root: RootNode,
|
root: RootNode,
|
||||||
options: TransformOptions = {}
|
options: TransformOptions = {},
|
||||||
): RootIRNode {
|
): RootIRNode {
|
||||||
const template = transformChildren(root.children)
|
// {
|
||||||
|
// type: IRNodeTypes.TEMPLATE_GENERATOR,
|
||||||
|
// template,
|
||||||
|
// loc: root.loc
|
||||||
|
// }
|
||||||
|
|
||||||
return {
|
const ir: RootIRNode = {
|
||||||
type: IRNodeTypes.ROOT,
|
type: IRNodeTypes.ROOT,
|
||||||
loc: root.loc,
|
loc: root.loc,
|
||||||
template: [
|
template: [],
|
||||||
{
|
children: {},
|
||||||
type: IRNodeTypes.TEMPLATE_GENERATOR,
|
effect: Object.create(null),
|
||||||
template,
|
opration: [],
|
||||||
loc: root.loc
|
helpers: new Set(['template']),
|
||||||
}
|
|
||||||
],
|
|
||||||
helpers: new Set(['template'])
|
|
||||||
}
|
}
|
||||||
|
const ctx = createRootContext(ir, root, options)
|
||||||
|
transformChildren(ctx, true)
|
||||||
|
ctx.registerTemplate()
|
||||||
|
ir.children = ctx.children
|
||||||
|
|
||||||
|
console.log(JSON.stringify(ir, undefined, 2))
|
||||||
|
|
||||||
|
return ir
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformChildren(children: TemplateChildNode[]) {
|
function transformChildren(
|
||||||
let template: string = ''
|
ctx: TransformContext<RootNode | ElementNode>,
|
||||||
children.forEach((child, i) => walkNode(child))
|
root?: boolean,
|
||||||
return template
|
) {
|
||||||
|
const {
|
||||||
|
node: { children },
|
||||||
|
} = ctx
|
||||||
|
let index = 0
|
||||||
|
children.forEach((child, i) => walkNode(child, i))
|
||||||
|
|
||||||
|
function walkNode(node: TemplateChildNode, i: number) {
|
||||||
|
const child = createContext(node, ctx, index)
|
||||||
|
const isFirst = i === 0
|
||||||
|
const isLast = i === children.length - 1
|
||||||
|
|
||||||
function walkNode(node: TemplateChildNode) {
|
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case 1 satisfies NodeTypes.ELEMENT: {
|
case 1 satisfies NodeTypes.ELEMENT: {
|
||||||
template += transformElement(node)
|
transformElement(child as TransformContext<ElementNode>)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 2 satisfies NodeTypes.TEXT:
|
case 2 satisfies NodeTypes.TEXT: {
|
||||||
template += node.content
|
ctx.template += node.content
|
||||||
break
|
break
|
||||||
case 3 satisfies NodeTypes.COMMENT:
|
}
|
||||||
template += `<!--${node.content}-->`
|
case 3 satisfies NodeTypes.COMMENT: {
|
||||||
|
ctx.template += `<!--${node.content}-->`
|
||||||
break
|
break
|
||||||
case 5 satisfies NodeTypes.INTERPOLATION:
|
}
|
||||||
template += transformInterpolation(node)
|
case 5 satisfies NodeTypes.INTERPOLATION: {
|
||||||
|
transformInterpolation(
|
||||||
|
child as TransformContext<InterpolationNode>,
|
||||||
|
isFirst,
|
||||||
|
isLast,
|
||||||
|
)
|
||||||
break
|
break
|
||||||
// case 12 satisfies NodeTypes.TEXT_CALL:
|
}
|
||||||
// template += node.content
|
default: {
|
||||||
default:
|
ctx.template += `[type: ${node.type}]`
|
||||||
template += `[${node.type}]`
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Object.keys(child.children).length > 0 || child.store)
|
||||||
|
ctx.children[index] = {
|
||||||
|
id: child.store ? child.getElementId() : null,
|
||||||
|
store: child.store,
|
||||||
|
children: child.children,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!child.ghost) index++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformInterpolation(node: InterpolationNode) {
|
function transformElement(ctx: TransformContext<ElementNode>) {
|
||||||
// TODO
|
const { node } = ctx
|
||||||
if (node.content.type === (4 satisfies NodeTypes.SIMPLE_EXPRESSION)) {
|
|
||||||
return `{{ ${node.content.content} }}`
|
|
||||||
}
|
|
||||||
return '[EXP]'
|
|
||||||
// return `{{${node.content.content}}}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function transformElement(node: ElementNode) {
|
|
||||||
const { tag, props, children } = node
|
const { tag, props, children } = node
|
||||||
let template = `<${tag}`
|
|
||||||
const propsTemplate = props
|
|
||||||
.filter(
|
|
||||||
(prop): prop is AttributeNode =>
|
|
||||||
prop.type === (6 satisfies NodeTypes.ATTRIBUTE)
|
|
||||||
)
|
|
||||||
.map(prop => transformProp(prop))
|
|
||||||
.join(' ')
|
|
||||||
|
|
||||||
if (propsTemplate) template += ' ' + propsTemplate
|
ctx.template += `<${tag}`
|
||||||
template += `>`
|
|
||||||
|
props.forEach((prop) => transformProp(prop, ctx))
|
||||||
|
ctx.template += node.isSelfClosing ? '/>' : `>`
|
||||||
|
|
||||||
if (children.length > 0) {
|
if (children.length > 0) {
|
||||||
template += transformChildren(children)
|
transformChildren(ctx)
|
||||||
|
}
|
||||||
|
if (!node.isSelfClosing) ctx.template += `</${tag}>`
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformInterpolation(
|
||||||
|
ctx: TransformContext<InterpolationNode>,
|
||||||
|
isFirst: boolean,
|
||||||
|
isLast: boolean,
|
||||||
|
) {
|
||||||
|
const { node } = ctx
|
||||||
|
|
||||||
|
if (node.content.type === (4 satisfies NodeTypes.SIMPLE_EXPRESSION)) {
|
||||||
|
const expr = processExpression(ctx, node.content.content)
|
||||||
|
|
||||||
|
const parent = ctx.parent!
|
||||||
|
const parentId = parent.getElementId()
|
||||||
|
parent.store = true
|
||||||
|
|
||||||
|
if (isFirst && isLast) {
|
||||||
|
ctx.registerEffect(expr, {
|
||||||
|
type: IRNodeTypes.SET_TEXT,
|
||||||
|
loc: node.loc,
|
||||||
|
element: parentId,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let id: number
|
||||||
|
let anchor: number | 'first' | 'last'
|
||||||
|
|
||||||
|
if (!isFirst && !isLast) {
|
||||||
|
id = ctx.root.getElementId()
|
||||||
|
anchor = ctx.getElementId()
|
||||||
|
ctx.template += '<!>'
|
||||||
|
ctx.store = true
|
||||||
|
} else {
|
||||||
|
id = ctx.getElementId()
|
||||||
|
ctx.ghost = true
|
||||||
|
anchor = isFirst ? 'first' : 'last'
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.ir.opration.push(
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.TEXT_NODE,
|
||||||
|
loc: node.loc,
|
||||||
|
id,
|
||||||
|
content: expr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.INSERT_NODE,
|
||||||
|
loc: node.loc,
|
||||||
|
element: id,
|
||||||
|
parent: parentId,
|
||||||
|
anchor,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.registerEffect(expr, {
|
||||||
|
type: IRNodeTypes.SET_TEXT,
|
||||||
|
loc: node.loc,
|
||||||
|
element: id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformProp(
|
||||||
|
node: DirectiveNode | AttributeNode,
|
||||||
|
ctx: TransformContext<ElementNode>,
|
||||||
|
): void {
|
||||||
|
const { name } = node
|
||||||
|
|
||||||
|
if (node.type === (6 satisfies NodeTypes.ATTRIBUTE)) {
|
||||||
|
if (node.value) {
|
||||||
|
ctx.template += ` ${name}="${node.value.content}"`
|
||||||
|
} else {
|
||||||
|
ctx.template += ` ${name}`
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
template += `</${tag}>`
|
if (!node.exp) {
|
||||||
|
// TODO
|
||||||
|
return
|
||||||
|
} else if (node.exp.type === (8 satisfies NodeTypes.COMPOUND_EXPRESSION)) {
|
||||||
|
// TODO
|
||||||
|
return
|
||||||
|
} else if (
|
||||||
|
!node.arg ||
|
||||||
|
node.arg.type === (8 satisfies NodeTypes.COMPOUND_EXPRESSION)
|
||||||
|
) {
|
||||||
|
// TODO
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return template
|
const expr = processExpression(ctx, node.exp.content)
|
||||||
|
ctx.store = true
|
||||||
|
if (name === 'bind') {
|
||||||
|
ctx.registerEffect(expr, {
|
||||||
|
type: IRNodeTypes.SET_PROP,
|
||||||
|
loc: node.loc,
|
||||||
|
element: ctx.getElementId(),
|
||||||
|
name: node.arg.content,
|
||||||
|
})
|
||||||
|
} else if (name === 'on') {
|
||||||
|
ctx.registerEffect(expr, {
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
loc: node.loc,
|
||||||
|
element: ctx.getElementId(),
|
||||||
|
name: node.arg.content,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformProp(prop: AttributeNode) {
|
function processExpression(ctx: TransformContext, expr: string) {
|
||||||
const { name, value } = prop
|
if (ctx.options.bindingMetadata?.[expr] === 'setup-ref') {
|
||||||
if (value) return `${name}="${value.content}"`
|
expr += '.value'
|
||||||
return name
|
}
|
||||||
|
return expr
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,8 @@
|
||||||
export { template } from './template'
|
export { template } from './template'
|
||||||
export { render } from './render'
|
export * from './render'
|
||||||
|
export * from './on'
|
||||||
|
|
||||||
|
type Children = Record<number, [ChildNode, Children]>
|
||||||
|
export function children(n: ChildNode): Children {
|
||||||
|
return { ...Array.from(n.childNodes).map(n => [n, children(n)]) }
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const on = (
|
||||||
|
el: any,
|
||||||
|
event: string,
|
||||||
|
handler: () => any,
|
||||||
|
options?: EventListenerOptions
|
||||||
|
) => {
|
||||||
|
el.addEventListener(event, handler, options)
|
||||||
|
}
|
|
@ -29,11 +29,22 @@ export function normalizeContainer(container: string | ParentNode): ParentNode {
|
||||||
: container
|
: container
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum InsertPosition {
|
||||||
|
FIRST,
|
||||||
|
LAST
|
||||||
|
}
|
||||||
|
|
||||||
export function insert(
|
export function insert(
|
||||||
block: Block,
|
block: Block,
|
||||||
parent: ParentNode,
|
parent: ParentNode,
|
||||||
anchor: Node | null = null
|
anchor: Node | InsertPosition | null = null
|
||||||
) {
|
) {
|
||||||
|
anchor =
|
||||||
|
typeof anchor === 'number'
|
||||||
|
? anchor === InsertPosition.FIRST
|
||||||
|
? parent.firstChild
|
||||||
|
: null
|
||||||
|
: anchor
|
||||||
// if (!isHydrating) {
|
// if (!isHydrating) {
|
||||||
if (block instanceof Node) {
|
if (block instanceof Node) {
|
||||||
parent.insertBefore(block, anchor)
|
parent.insertBefore(block, anchor)
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"enableNonBrowserBranches": true
|
"enableNonBrowserBranches": true
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vue/compiler-vapor": "workspace:^",
|
||||||
"monaco-editor": "^0.44.0",
|
"monaco-editor": "^0.44.0",
|
||||||
"source-map-js": "^1.0.2"
|
"source-map-js": "^1.0.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as m from 'monaco-editor'
|
import * as m from 'monaco-editor'
|
||||||
import { compile, CompilerError, CompilerOptions } from '@vue/compiler-dom'
|
import { CompilerError, CompilerOptions } from '@vue/compiler-dom'
|
||||||
|
import { compile } from '@vue/compiler-vapor'
|
||||||
import { compile as ssrCompile } from '@vue/compiler-ssr'
|
import { compile as ssrCompile } from '@vue/compiler-ssr'
|
||||||
import {
|
import {
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
|
@ -92,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)
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
"vue": "workspace:*"
|
"vue": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.4.0",
|
"@vitejs/plugin-vue": "link:/Users/kevin/Developer/open-source/vite-plugin-vue/packages/plugin-vue",
|
||||||
"vite": "^4.5.0",
|
"vite": "^5.0.2",
|
||||||
"vite-plugin-inspect": "^0.7.42"
|
"vite-plugin-inspect": "^0.7.42"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,31 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
const count = ref(0)
|
const count = ref(0)
|
||||||
|
const double = computed(() => count.value * 2)
|
||||||
|
|
||||||
|
const inc = () => count.value++
|
||||||
|
const dec = () => count.value--
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
globalThis.count = count
|
||||||
|
// @ts-expect-error
|
||||||
|
globalThis.double = double
|
||||||
|
// @ts-expect-error
|
||||||
|
globalThis.inc = inc
|
||||||
|
// @ts-expect-error
|
||||||
|
globalThis.dec = dec
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="red">Hello world</h1>
|
<h1 class="red">Counter</h1>
|
||||||
<!-- {{ count }} -->
|
<div>The number is {{ count }}.</div>
|
||||||
<button style="font-weight: bold">Inc</button>
|
<div>{{ count }} * 2 = {{ double }}</div>
|
||||||
|
<div style="display: flex; gap: 8px">
|
||||||
|
<button @click="inc">inc</button>
|
||||||
|
<button @click="dec">dec</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -16,4 +33,10 @@ const count = ref(0)
|
||||||
.red {
|
.red {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
color-scheme: dark;
|
||||||
|
background-color: #000;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import { render } from 'vue/vapor'
|
import { render } from 'vue/vapor'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
// @ts-expect-error
|
render(() => {
|
||||||
render(App.render, '#app')
|
// @ts-expect-error
|
||||||
|
const returned = App.setup({}, { expose() {} })
|
||||||
|
return App.render(returned)
|
||||||
|
}, '#app')
|
||||||
|
|
|
@ -7,8 +7,10 @@ export default defineConfig({
|
||||||
build: {
|
build: {
|
||||||
target: 'esnext'
|
target: 'esnext'
|
||||||
},
|
},
|
||||||
|
clearScreen: false,
|
||||||
plugins: [
|
plugins: [
|
||||||
Vue({
|
Vue({
|
||||||
|
isProduction: true,
|
||||||
template: {
|
template: {
|
||||||
compiler: CompilerVapor
|
compiler: CompilerVapor
|
||||||
}
|
}
|
||||||
|
|
|
@ -395,6 +395,9 @@ importers:
|
||||||
|
|
||||||
packages/template-explorer:
|
packages/template-explorer:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@vue/compiler-vapor':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../compiler-vapor
|
||||||
monaco-editor:
|
monaco-editor:
|
||||||
specifier: ^0.44.0
|
specifier: ^0.44.0
|
||||||
version: 0.44.0
|
version: 0.44.0
|
||||||
|
@ -451,14 +454,14 @@ importers:
|
||||||
version: link:../packages/vue
|
version: link:../packages/vue
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@vitejs/plugin-vue':
|
'@vitejs/plugin-vue':
|
||||||
specifier: ^4.4.0
|
specifier: link:/Users/kevin/Developer/open-source/vite-plugin-vue/packages/plugin-vue
|
||||||
version: 4.4.0(vite@4.5.0)(vue@packages+vue)
|
version: link:../../../vite-plugin-vue/packages/plugin-vue
|
||||||
vite:
|
vite:
|
||||||
specifier: ^4.5.0
|
specifier: ^5.0.2
|
||||||
version: 4.5.0(@types/node@20.9.0)(terser@5.22.0)
|
version: 5.0.2(@types/node@20.9.0)(terser@5.22.0)
|
||||||
vite-plugin-inspect:
|
vite-plugin-inspect:
|
||||||
specifier: ^0.7.42
|
specifier: ^0.7.42
|
||||||
version: 0.7.42(rollup@4.1.4)(vite@4.5.0)
|
version: 0.7.42(rollup@4.1.4)(vite@5.0.2)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
@ -1728,17 +1731,6 @@ packages:
|
||||||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vitejs/plugin-vue@4.4.0(vite@4.5.0)(vue@packages+vue):
|
|
||||||
resolution: {integrity: sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==}
|
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
vite: ^4.0.0
|
|
||||||
vue: ^3.2.25
|
|
||||||
dependencies:
|
|
||||||
vite: 4.5.0(@types/node@20.9.0)(terser@5.22.0)
|
|
||||||
vue: link:packages/vue
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@vitejs/plugin-vue@4.4.0(vite@5.0.0)(vue@packages+vue):
|
/@vitejs/plugin-vue@4.4.0(vite@5.0.0)(vue@packages+vue):
|
||||||
resolution: {integrity: sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==}
|
resolution: {integrity: sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==}
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
|
@ -5337,14 +5329,6 @@ packages:
|
||||||
rollup: 4.1.4
|
rollup: 4.1.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/rollup@3.29.4:
|
|
||||||
resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==}
|
|
||||||
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
|
|
||||||
hasBin: true
|
|
||||||
optionalDependencies:
|
|
||||||
fsevents: 2.3.3
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/rollup@4.1.4:
|
/rollup@4.1.4:
|
||||||
resolution: {integrity: sha512-U8Yk1lQRKqCkDBip/pMYT+IKaN7b7UesK3fLSTuHBoBJacCE+oBqo/dfG/gkUdQNNB2OBmRP98cn2C2bkYZkyw==}
|
resolution: {integrity: sha512-U8Yk1lQRKqCkDBip/pMYT+IKaN7b7UesK3fLSTuHBoBJacCE+oBqo/dfG/gkUdQNNB2OBmRP98cn2C2bkYZkyw==}
|
||||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
|
@ -6187,7 +6171,7 @@ packages:
|
||||||
mlly: 1.4.2
|
mlly: 1.4.2
|
||||||
pathe: 1.1.1
|
pathe: 1.1.1
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
vite: 5.0.0(@types/node@20.9.0)(terser@5.22.0)
|
vite: 5.0.2(@types/node@20.9.0)(terser@5.22.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- less
|
- less
|
||||||
|
@ -6199,7 +6183,7 @@ packages:
|
||||||
- terser
|
- terser
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite-plugin-inspect@0.7.42(rollup@4.1.4)(vite@4.5.0):
|
/vite-plugin-inspect@0.7.42(rollup@4.1.4)(vite@5.0.2):
|
||||||
resolution: {integrity: sha512-JCyX86wr3siQc+p9Kd0t8VkFHAJag0RaQVIpdFGSv5FEaePEVB6+V/RGtz2dQkkGSXQzRWrPs4cU3dRKg32bXw==}
|
resolution: {integrity: sha512-JCyX86wr3siQc+p9Kd0t8VkFHAJag0RaQVIpdFGSv5FEaePEVB6+V/RGtz2dQkkGSXQzRWrPs4cU3dRKg32bXw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -6217,18 +6201,18 @@ packages:
|
||||||
open: 9.1.0
|
open: 9.1.0
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
sirv: 2.0.3
|
sirv: 2.0.3
|
||||||
vite: 4.5.0(@types/node@20.9.0)(terser@5.22.0)
|
vite: 5.0.2(@types/node@20.9.0)(terser@5.22.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- rollup
|
- rollup
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite@4.5.0(@types/node@20.9.0)(terser@5.22.0):
|
/vite@5.0.0(@types/node@20.9.0)(terser@5.22.0):
|
||||||
resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==}
|
resolution: {integrity: sha512-ESJVM59mdyGpsiNAeHQOR/0fqNoOyWPYesFto8FFZugfmhdHx8Fzd8sF3Q/xkVhZsyOxHfdM7ieiVAorI9RjFw==}
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/node': '>= 14'
|
'@types/node': ^18.0.0 || >=20.0.0
|
||||||
less: '*'
|
less: '*'
|
||||||
lightningcss: ^1.21.0
|
lightningcss: ^1.21.0
|
||||||
sass: '*'
|
sass: '*'
|
||||||
|
@ -6252,16 +6236,16 @@ packages:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.9.0
|
'@types/node': 20.9.0
|
||||||
esbuild: 0.18.20
|
esbuild: 0.19.5
|
||||||
postcss: 8.4.31
|
postcss: 8.4.31
|
||||||
rollup: 3.29.4
|
rollup: 4.4.1
|
||||||
terser: 5.22.0
|
terser: 5.22.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite@5.0.0(@types/node@20.9.0)(terser@5.22.0):
|
/vite@5.0.2(@types/node@20.9.0)(terser@5.22.0):
|
||||||
resolution: {integrity: sha512-ESJVM59mdyGpsiNAeHQOR/0fqNoOyWPYesFto8FFZugfmhdHx8Fzd8sF3Q/xkVhZsyOxHfdM7ieiVAorI9RjFw==}
|
resolution: {integrity: sha512-6CCq1CAJCNM1ya2ZZA7+jS2KgnhbzvxakmlIjN24cF/PXhRMzpM/z8QgsVJA/Dm5fWUWnVEsmtBoMhmerPxT0g==}
|
||||||
engines: {node: ^18.0.0 || >=20.0.0}
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
Loading…
Reference in New Issue