|
|
@@ -69,6 +69,13 @@ export interface CodegenResult {
|
|
|
map?: RawSourceMap
|
|
|
}
|
|
|
|
|
|
+const enum NewlineType {
|
|
|
+ Start = 0,
|
|
|
+ End = -1,
|
|
|
+ None = -2,
|
|
|
+ Unknown = -3
|
|
|
+}
|
|
|
+
|
|
|
export interface CodegenContext
|
|
|
extends Omit<Required<CodegenOptions>, 'bindingMetadata' | 'inline'> {
|
|
|
source: string
|
|
|
@@ -80,7 +87,7 @@ export interface CodegenContext
|
|
|
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
|
|
|
@@ -127,7 +134,7 @@ function createCodegenContext(
|
|
|
helper(key) {
|
|
|
return `_${helperNameMap[key]}`
|
|
|
},
|
|
|
- push(code, node) {
|
|
|
+ push(code, newlineIndex = NewlineType.None, node) {
|
|
|
context.code += code
|
|
|
if (!__BROWSER__ && context.map) {
|
|
|
if (node) {
|
|
|
@@ -140,7 +147,41 @@ function createCodegenContext(
|
|
|
}
|
|
|
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)
|
|
|
}
|
|
|
@@ -162,7 +203,7 @@ function createCodegenContext(
|
|
|
}
|
|
|
|
|
|
function newline(n: number) {
|
|
|
- context.push('\n' + ` `.repeat(n))
|
|
|
+ context.push('\n' + ` `.repeat(n), NewlineType.Start)
|
|
|
}
|
|
|
|
|
|
function addMapping(loc: Position, name?: string) {
|
|
|
@@ -250,8 +291,10 @@ export function generate(
|
|
|
// 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()
|
|
|
}
|
|
|
}
|
|
|
@@ -282,7 +325,7 @@ export function generate(
|
|
|
}
|
|
|
}
|
|
|
if (ast.components.length || ast.directives.length || ast.temps) {
|
|
|
- push(`\n`)
|
|
|
+ push(`\n`, NewlineType.Start)
|
|
|
newline()
|
|
|
}
|
|
|
|
|
|
@@ -334,11 +377,14 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
|
|
|
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.
|
|
|
@@ -353,7 +399,7 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
|
|
|
.filter(helper => helpers.includes(helper))
|
|
|
.map(aliasHelper)
|
|
|
.join(', ')
|
|
|
- push(`const { ${staticHelpers} } = _Vue\n`)
|
|
|
+ push(`const { ${staticHelpers} } = _Vue\n`, NewlineType.End)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -363,7 +409,8 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
|
|
|
push(
|
|
|
`const { ${ast.ssrHelpers
|
|
|
.map(aliasHelper)
|
|
|
- .join(', ')} } = require("${ssrRuntimeModuleName}")\n`
|
|
|
+ .join(', ')} } = require("${ssrRuntimeModuleName}")\n`,
|
|
|
+ NewlineType.End
|
|
|
)
|
|
|
}
|
|
|
genHoists(ast.hoists, context)
|
|
|
@@ -402,18 +449,21 @@ function genModulePreamble(
|
|
|
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
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
@@ -422,7 +472,8 @@ function genModulePreamble(
|
|
|
push(
|
|
|
`import { ${ast.ssrHelpers
|
|
|
.map(s => `${helperNameMap[s]} as _${helperNameMap[s]}`)
|
|
|
- .join(', ')} } from "${ssrRuntimeModuleName}"\n`
|
|
|
+ .join(', ')} } from "${ssrRuntimeModuleName}"\n`,
|
|
|
+ NewlineType.End
|
|
|
)
|
|
|
}
|
|
|
|
|
|
@@ -554,7 +605,7 @@ function genNodeList(
|
|
|
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 {
|
|
|
@@ -573,7 +624,7 @@ function genNodeList(
|
|
|
|
|
|
function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
|
|
|
if (isString(node)) {
|
|
|
- context.push(node)
|
|
|
+ context.push(node, NewlineType.Unknown)
|
|
|
return
|
|
|
}
|
|
|
if (isSymbol(node)) {
|
|
|
@@ -671,12 +722,16 @@ function genText(
|
|
|
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) {
|
|
|
@@ -694,7 +749,7 @@ function genCompoundExpression(
|
|
|
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)
|
|
|
}
|
|
|
@@ -715,9 +770,9 @@ function genExpressionAsPropertyKey(
|
|
|
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)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -726,7 +781,11 @@ function genComment(node: CommentNode, context: CodegenContext) {
|
|
|
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) {
|
|
|
@@ -754,7 +813,7 @@ 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
|
|
|
@@ -785,7 +844,7 @@ function genCallExpression(node: CallExpression, context: CodegenContext) {
|
|
|
if (pure) {
|
|
|
push(PURE_ANNOTATION)
|
|
|
}
|
|
|
- push(callee + `(`, node)
|
|
|
+ push(callee + `(`, NewlineType.None, node)
|
|
|
genNodeList(node.arguments, context)
|
|
|
push(`)`)
|
|
|
}
|
|
|
@@ -794,7 +853,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
|
|
|
const { push, indent, deindent, newline } = context
|
|
|
const { properties } = node
|
|
|
if (!properties.length) {
|
|
|
- push(`{}`, node)
|
|
|
+ push(`{}`, NewlineType.None, node)
|
|
|
return
|
|
|
}
|
|
|
const multilines =
|
|
|
@@ -834,7 +893,7 @@ function genFunctionExpression(
|
|
|
// 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) {
|
|
|
@@ -934,7 +993,7 @@ function genTemplateLiteral(node: TemplateLiteral, context: CodegenContext) {
|
|
|
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()
|