map?: RawSourceMap
}
+const enum NewlineType {
+ Start = 0,
+ End = -1,
+ None = -2,
+ Unknown = -3
+}
+
export interface CodegenContext
extends Omit<Required<CodegenOptions>, 'bindingMetadata' | 'inline'> {
source: string
pure: boolean
map?: SourceMapGenerator
helper(key: symbol): string
- push(code: string, node?: CodegenNode): void
+ push(code: string, newlineIndex?: number, node?: CodegenNode): void
indent(): void
deindent(withoutNewLine?: boolean): void
newline(): void
helper(key) {
return `_${helperNameMap[key]}`
},
- push(code, node) {
+ push(code, newlineIndex = NewlineType.None, node) {
context.code += code
if (!__BROWSER__ && context.map) {
if (node) {
}
addMapping(node.loc.start, name)
}
- advancePositionWithMutation(context, code)
+ 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)
}
}
function newline(n: number) {
- context.push('\n' + ` `.repeat(n))
+ context.push('\n' + ` `.repeat(n), NewlineType.Start)
}
function addMapping(loc: Position, name?: string) {
// function mode const declarations should be inside with block
// also they should be renamed to avoid collision with user properties
if (hasHelpers) {
- push(`const { ${helpers.map(aliasHelper).join(', ')} } = _Vue`)
- push(`\n`)
+ push(
+ `const { ${helpers.map(aliasHelper).join(', ')} } = _Vue\n`,
+ NewlineType.End
+ )
newline()
}
}
}
}
if (ast.components.length || ast.directives.length || ast.temps) {
- push(`\n`)
+ push(`\n`, NewlineType.Start)
newline()
}
const helpers = Array.from(ast.helpers)
if (helpers.length > 0) {
if (!__BROWSER__ && prefixIdentifiers) {
- push(`const { ${helpers.map(aliasHelper).join(', ')} } = ${VueBinding}\n`)
+ push(
+ `const { ${helpers.map(aliasHelper).join(', ')} } = ${VueBinding}\n`,
+ NewlineType.End
+ )
} else {
// "with" mode.
// save Vue in a separate variable to avoid collision
- push(`const _Vue = ${VueBinding}\n`)
+ push(`const _Vue = ${VueBinding}\n`, NewlineType.End)
// in "with" mode, helpers are declared inside the with block to avoid
// has check cost, but hoists are lifted out of the function - we need
// to provide the helper here.
.filter(helper => helpers.includes(helper))
.map(aliasHelper)
.join(', ')
- push(`const { ${staticHelpers} } = _Vue\n`)
+ push(`const { ${staticHelpers} } = _Vue\n`, NewlineType.End)
}
}
}
push(
`const { ${ast.ssrHelpers
.map(aliasHelper)
- .join(', ')} } = require("${ssrRuntimeModuleName}")\n`
+ .join(', ')} } = require("${ssrRuntimeModuleName}")\n`,
+ NewlineType.End
)
}
genHoists(ast.hoists, context)
push(
`import { ${helpers
.map(s => helperNameMap[s])
- .join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`
+ .join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`,
+ NewlineType.End
)
push(
`\n// Binding optimization for webpack code-split\nconst ${helpers
.map(s => `_${helperNameMap[s]} = ${helperNameMap[s]}`)
- .join(', ')}\n`
+ .join(', ')}\n`,
+ NewlineType.End
)
} else {
push(
`import { ${helpers
.map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
- .join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`
+ .join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`,
+ NewlineType.End
)
}
}
push(
`import { ${ast.ssrHelpers
.map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
- .join(', ')} } from "${ssrRuntimeModuleName}"\n`
+ .join(', ')} } from "${ssrRuntimeModuleName}"\n`,
+ NewlineType.End
)
}
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
if (isString(node)) {
- push(node)
+ push(node, NewlineType.Unknown)
} else if (isArray(node)) {
genNodeListAsArray(node, context)
} else {
function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
if (isString(node)) {
- context.push(node)
+ context.push(node, NewlineType.Unknown)
return
}
if (isSymbol(node)) {
node: TextNode | SimpleExpressionNode,
context: CodegenContext
) {
- context.push(JSON.stringify(node.content), node)
+ context.push(JSON.stringify(node.content), NewlineType.Unknown, node)
}
function genExpression(node: SimpleExpressionNode, context: CodegenContext) {
const { content, isStatic } = node
- context.push(isStatic ? JSON.stringify(content) : content, node)
+ context.push(
+ isStatic ? JSON.stringify(content) : content,
+ NewlineType.Unknown,
+ node
+ )
}
function genInterpolation(node: InterpolationNode, context: CodegenContext) {
for (let i = 0; i < node.children!.length; i++) {
const child = node.children![i]
if (isString(child)) {
- context.push(child)
+ context.push(child, NewlineType.Unknown)
} else {
genNode(child, context)
}
const text = isSimpleIdentifier(node.content)
? node.content
: JSON.stringify(node.content)
- push(text, node)
+ push(text, NewlineType.None, node)
} else {
- push(`[${node.content}]`, node)
+ push(`[${node.content}]`, NewlineType.Unknown, node)
}
}
if (pure) {
push(PURE_ANNOTATION)
}
- push(`${helper(CREATE_COMMENT)}(${JSON.stringify(node.content)})`, node)
+ push(
+ `${helper(CREATE_COMMENT)}(${JSON.stringify(node.content)})`,
+ NewlineType.Unknown,
+ node
+ )
}
function genVNodeCall(node: VNodeCall, context: CodegenContext) {
const callHelper: symbol = isBlock
? getVNodeBlockHelper(context.inSSR, isComponent)
: getVNodeHelper(context.inSSR, isComponent)
- push(helper(callHelper) + `(`, node)
+ push(helper(callHelper) + `(`, NewlineType.None, node)
genNodeList(
genNullableArgs([tag, props, children, patchFlag, dynamicProps]),
context
if (pure) {
push(PURE_ANNOTATION)
}
- push(callee + `(`, node)
+ push(callee + `(`, NewlineType.None, node)
genNodeList(node.arguments, context)
push(`)`)
}
const { push, indent, deindent, newline } = context
const { properties } = node
if (!properties.length) {
- push(`{}`, node)
+ push(`{}`, NewlineType.None, node)
return
}
const multilines =
// wrap slot functions with owner context
push(`_${helperNameMap[WITH_CTX]}(`)
}
- push(`(`, node)
+ push(`(`, NewlineType.None, node)
if (isArray(params)) {
genNodeList(params, context)
} else if (params) {
for (let i = 0; i < l; i++) {
const e = node.elements[i]
if (isString(e)) {
- push(e.replace(/(`|\$|\\)/g, '\\$1'))
+ push(e.replace(/(`|\$|\\)/g, '\\$1'), NewlineType.Unknown)
} else {
push('${')
if (multilines) indent()