fix: fix source map by fixing advancePositionWithMutation
This commit is contained in:
@@ -118,14 +118,12 @@ export interface ExpressionNode extends Node {
|
||||
export interface IfNode extends Node {
|
||||
type: NodeTypes.IF
|
||||
branches: IfBranchNode[]
|
||||
isRoot: boolean
|
||||
}
|
||||
|
||||
export interface IfBranchNode extends Node {
|
||||
type: NodeTypes.IF_BRANCH
|
||||
condition: ExpressionNode | undefined // else
|
||||
children: ChildNode[]
|
||||
isRoot: boolean
|
||||
}
|
||||
|
||||
export interface ForNode extends Node {
|
||||
|
||||
@@ -65,6 +65,7 @@ export interface CodegenContext extends Required<CodegenOptions> {
|
||||
offset: number
|
||||
indentLevel: number
|
||||
map?: SourceMapGenerator
|
||||
helper(name: string): string
|
||||
push(code: string, node?: CodegenNode): void
|
||||
indent(): void
|
||||
deindent(withoutNewLine?: boolean): void
|
||||
@@ -98,11 +99,14 @@ function createCodegenContext(
|
||||
? undefined
|
||||
: new (require('source-map')).SourceMapGenerator(),
|
||||
|
||||
helper(name) {
|
||||
return prefixIdentifiers ? name : `_${name}`
|
||||
},
|
||||
push(code, node?: CodegenNode) {
|
||||
context.code += code
|
||||
if (context.map) {
|
||||
if (node) {
|
||||
context.map.addMapping({
|
||||
const mapping = {
|
||||
source: context.filename,
|
||||
original: {
|
||||
line: node.loc.start.line,
|
||||
@@ -112,9 +116,10 @@ function createCodegenContext(
|
||||
line: context.line,
|
||||
column: context.column - 1
|
||||
}
|
||||
})
|
||||
}
|
||||
context.map.addMapping(mapping)
|
||||
}
|
||||
advancePositionWithMutation(context, code, code.length)
|
||||
advancePositionWithMutation(context, code)
|
||||
}
|
||||
},
|
||||
indent() {
|
||||
@@ -144,7 +149,7 @@ export function generate(
|
||||
): CodegenResult {
|
||||
const context = createCodegenContext(ast, options)
|
||||
const { mode, push, prefixIdentifiers, indent, deindent, newline } = context
|
||||
const imports = ast.imports.join(', ')
|
||||
const hasImports = ast.imports.length
|
||||
|
||||
// preambles
|
||||
if (mode === 'function') {
|
||||
@@ -152,9 +157,9 @@ export function generate(
|
||||
// In prefix mode, we place the const declaration at top so it's done
|
||||
// only once; But if we not prefixing, we place the decalration inside the
|
||||
// with block so it doesn't incur the `in` check cost for every helper access.
|
||||
if (imports) {
|
||||
if (hasImports) {
|
||||
if (prefixIdentifiers) {
|
||||
push(`const { ${imports} } = Vue\n`)
|
||||
push(`const { ${ast.imports.join(', ')} } = Vue\n`)
|
||||
} else {
|
||||
// save Vue in a separate variable to avoid collision
|
||||
push(`const _Vue = Vue`)
|
||||
@@ -164,8 +169,8 @@ export function generate(
|
||||
push(`return `)
|
||||
} else {
|
||||
// generate import statements for helpers
|
||||
if (imports) {
|
||||
push(`import { ${imports} } from 'vue'\n`)
|
||||
if (hasImports) {
|
||||
push(`import { ${ast.imports.join(', ')} } from 'vue'\n`)
|
||||
}
|
||||
genHoists(ast.hoists, context)
|
||||
push(`export default `)
|
||||
@@ -179,8 +184,9 @@ export function generate(
|
||||
push(`with (this) {`)
|
||||
indent()
|
||||
// function mode const declarations should be inside with block
|
||||
if (mode === 'function' && imports) {
|
||||
push(`const { ${imports} } = _Vue`)
|
||||
// also they should be renamed to avoid collision with user properties
|
||||
if (mode === 'function' && hasImports) {
|
||||
push(`const { ${ast.imports.map(n => `${n}: _${n}`).join(', ')} } = _Vue`)
|
||||
newline()
|
||||
}
|
||||
} else {
|
||||
@@ -199,7 +205,7 @@ export function generate(
|
||||
|
||||
// generate the VNode tree expression
|
||||
push(`return `)
|
||||
genChildren(ast.children, context, true /* asRoot */)
|
||||
genChildren(ast.children, context, true)
|
||||
if (!prefixIdentifiers) {
|
||||
deindent()
|
||||
push(`}`)
|
||||
@@ -223,13 +229,12 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) {
|
||||
}
|
||||
|
||||
// This will generate a single vnode call if:
|
||||
// - The list has length === 1, AND:
|
||||
// - This is a root node, OR:
|
||||
// - The only child is a text or expression.
|
||||
// - The target position explicitly allows a single node (root, if, for)
|
||||
// - The list has length === 1, AND The only child is a text or expression.
|
||||
function genChildren(
|
||||
children: ChildNode[],
|
||||
context: CodegenContext,
|
||||
asRoot: boolean = false
|
||||
allowSingle: boolean = false
|
||||
) {
|
||||
if (!children.length) {
|
||||
return context.push(`null`)
|
||||
@@ -237,7 +242,7 @@ function genChildren(
|
||||
const child = children[0]
|
||||
if (
|
||||
children.length === 1 &&
|
||||
(asRoot ||
|
||||
(allowSingle ||
|
||||
child.type === NodeTypes.TEXT ||
|
||||
child.type == NodeTypes.EXPRESSION)
|
||||
) {
|
||||
@@ -336,10 +341,10 @@ function genText(node: TextNode | ExpressionNode, context: CodegenContext) {
|
||||
}
|
||||
|
||||
function genExpression(node: ExpressionNode, context: CodegenContext) {
|
||||
const { push } = context
|
||||
const { push, helper } = context
|
||||
const { content, children, isStatic, isInterpolation } = node
|
||||
if (isInterpolation) {
|
||||
push(`${TO_STRING}(`)
|
||||
push(`${helper(TO_STRING)}(`)
|
||||
}
|
||||
if (children) {
|
||||
genCompoundExpression(node, context)
|
||||
@@ -383,8 +388,11 @@ function genCompoundExpression(node: ExpressionNode, context: CodegenContext) {
|
||||
|
||||
function genComment(node: CommentNode, context: CodegenContext) {
|
||||
if (__DEV__) {
|
||||
context.push(
|
||||
`${CREATE_VNODE}(${COMMENT}, 0, ${JSON.stringify(node.content)})`,
|
||||
const { push, helper } = context
|
||||
push(
|
||||
`${helper(CREATE_VNODE)}(${helper(COMMENT)}, 0, ${JSON.stringify(
|
||||
node.content
|
||||
)})`,
|
||||
node
|
||||
)
|
||||
}
|
||||
@@ -396,7 +404,7 @@ function genIf(node: IfNode, context: CodegenContext) {
|
||||
}
|
||||
|
||||
function genIfBranch(
|
||||
{ condition, children, isRoot }: IfBranchNode,
|
||||
{ condition, children }: IfBranchNode,
|
||||
branches: IfBranchNode[],
|
||||
nextIndex: number,
|
||||
context: CodegenContext
|
||||
@@ -411,7 +419,7 @@ function genIfBranch(
|
||||
indent()
|
||||
context.indentLevel++
|
||||
push(`? `)
|
||||
genChildren(children, context, isRoot)
|
||||
genChildren(children, context, true)
|
||||
context.indentLevel--
|
||||
newline()
|
||||
push(`: `)
|
||||
@@ -424,14 +432,14 @@ function genIfBranch(
|
||||
} else {
|
||||
// v-else
|
||||
__DEV__ && assert(nextIndex === branches.length)
|
||||
genChildren(children, context, isRoot)
|
||||
genChildren(children, context, true)
|
||||
}
|
||||
}
|
||||
|
||||
function genFor(node: ForNode, context: CodegenContext) {
|
||||
const { push } = context
|
||||
const { push, helper, indent, deindent } = context
|
||||
const { source, keyAlias, valueAlias, objectIndexAlias, children } = node
|
||||
push(`${RENDER_LIST}(`, node)
|
||||
push(`${helper(RENDER_LIST)}(`, node)
|
||||
genExpression(source, context)
|
||||
push(`, (`)
|
||||
if (valueAlias) {
|
||||
@@ -455,9 +463,12 @@ function genFor(node: ForNode, context: CodegenContext) {
|
||||
push(`, `)
|
||||
genExpression(objectIndexAlias, context)
|
||||
}
|
||||
push(`) => `)
|
||||
genChildren(children, context)
|
||||
push(`)`)
|
||||
push(`) => {`)
|
||||
indent()
|
||||
push(`return `)
|
||||
genChildren(children, context, true)
|
||||
deindent()
|
||||
push(`})`)
|
||||
}
|
||||
|
||||
// JavaScript
|
||||
|
||||
@@ -479,7 +479,14 @@ function parseAttribute(
|
||||
advanceBy(context, name.length)
|
||||
|
||||
// Value
|
||||
let value: { content: string; loc: SourceLocation } | undefined = undefined
|
||||
let value:
|
||||
| {
|
||||
content: string
|
||||
isQuoted: boolean
|
||||
loc: SourceLocation
|
||||
}
|
||||
| undefined = undefined
|
||||
|
||||
if (/^[\t\r\n\f ]*=/.test(context.source)) {
|
||||
advanceSpaces(context)
|
||||
advanceBy(context, 1)
|
||||
@@ -530,6 +537,13 @@ function parseAttribute(
|
||||
}
|
||||
}
|
||||
|
||||
if (value && value.isQuoted) {
|
||||
const valueLoc = value.loc
|
||||
valueLoc.start.offset++
|
||||
valueLoc.start.column++
|
||||
valueLoc.end = advancePositionWithClone(valueLoc.start, value.content)
|
||||
}
|
||||
|
||||
return {
|
||||
type: NodeTypes.DIRECTIVE,
|
||||
name:
|
||||
@@ -567,13 +581,20 @@ function parseAttribute(
|
||||
|
||||
function parseAttributeValue(
|
||||
context: ParserContext
|
||||
): { content: string; loc: SourceLocation } | undefined {
|
||||
):
|
||||
| {
|
||||
content: string
|
||||
isQuoted: boolean
|
||||
loc: SourceLocation
|
||||
}
|
||||
| undefined {
|
||||
const start = getCursor(context)
|
||||
let content: string
|
||||
|
||||
if (/^["']/.test(context.source)) {
|
||||
const quote = context.source[0]
|
||||
const isQuoted = quote === `"` || quote === `'`
|
||||
if (isQuoted) {
|
||||
// Quoted value.
|
||||
const quote = context.source[0]
|
||||
advanceBy(context, 1)
|
||||
|
||||
const endIndex = context.source.indexOf(quote)
|
||||
@@ -605,7 +626,7 @@ function parseAttributeValue(
|
||||
content = parseTextData(context, match[0].length, TextModes.ATTRIBUTE_VALUE)
|
||||
}
|
||||
|
||||
return { content, loc: getSelection(context, start) }
|
||||
return { content, isQuoted, loc: getSelection(context, start) }
|
||||
}
|
||||
|
||||
function parseInterpolation(
|
||||
|
||||
@@ -59,6 +59,7 @@ export interface TransformContext extends Required<TransformOptions> {
|
||||
parent: ParentNode
|
||||
childIndex: number
|
||||
currentNode: ChildNode | null
|
||||
helper(name: string): string
|
||||
replaceNode(node: ChildNode): void
|
||||
removeNode(node?: ChildNode): void
|
||||
onNodeRemoved: () => void
|
||||
@@ -89,6 +90,10 @@ function createTransformContext(
|
||||
parent: root,
|
||||
childIndex: 0,
|
||||
currentNode: null,
|
||||
helper(name) {
|
||||
context.imports.add(name)
|
||||
return prefixIdentifiers ? name : `_${name}`
|
||||
},
|
||||
replaceNode(node) {
|
||||
/* istanbul ignore if */
|
||||
if (__DEV__ && !context.currentNode) {
|
||||
@@ -195,15 +200,15 @@ export function traverseNode(node: ChildNode, context: TransformContext) {
|
||||
|
||||
switch (node.type) {
|
||||
case NodeTypes.COMMENT:
|
||||
context.imports.add(CREATE_VNODE)
|
||||
context.helper(CREATE_VNODE)
|
||||
// inject import for the Comment symbol, which is needed for creating
|
||||
// comment nodes with `createVNode`
|
||||
context.imports.add(COMMENT)
|
||||
context.helper(COMMENT)
|
||||
break
|
||||
case NodeTypes.EXPRESSION:
|
||||
// no need to traverse, but we need to inject toString helper
|
||||
if (node.isInterpolation) {
|
||||
context.imports.add(TO_STRING)
|
||||
context.helper(TO_STRING)
|
||||
}
|
||||
break
|
||||
|
||||
|
||||
@@ -42,12 +42,11 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
let componentIdentifier: string | undefined
|
||||
|
||||
if (isComponent) {
|
||||
context.imports.add(RESOLVE_COMPONENT)
|
||||
componentIdentifier = `_component_${toValidId(node.tag)}`
|
||||
context.statements.push(
|
||||
`const ${componentIdentifier} = ${RESOLVE_COMPONENT}(${JSON.stringify(
|
||||
node.tag
|
||||
)})`
|
||||
`const ${componentIdentifier} = ${context.helper(
|
||||
RESOLVE_COMPONENT
|
||||
)}(${JSON.stringify(node.tag)})`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -70,13 +69,15 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
}
|
||||
|
||||
const { loc } = node
|
||||
context.imports.add(CREATE_VNODE)
|
||||
const vnode = createCallExpression(CREATE_VNODE, args, loc)
|
||||
const vnode = createCallExpression(
|
||||
context.helper(CREATE_VNODE),
|
||||
args,
|
||||
loc
|
||||
)
|
||||
|
||||
if (runtimeDirectives && runtimeDirectives.length) {
|
||||
context.imports.add(APPLY_DIRECTIVES)
|
||||
node.codegenNode = createCallExpression(
|
||||
APPLY_DIRECTIVES,
|
||||
context.helper(APPLY_DIRECTIVES),
|
||||
[
|
||||
vnode,
|
||||
createArrayExpression(
|
||||
@@ -147,11 +148,10 @@ function buildProps(
|
||||
mergeArgs.push(exp)
|
||||
} else {
|
||||
// v-on="obj" -> toHandlers(obj)
|
||||
context.imports.add(TO_HANDLERS)
|
||||
mergeArgs.push({
|
||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||
loc,
|
||||
callee: TO_HANDLERS,
|
||||
callee: context.helper(TO_HANDLERS),
|
||||
arguments: [exp]
|
||||
})
|
||||
}
|
||||
@@ -197,8 +197,11 @@ function buildProps(
|
||||
)
|
||||
}
|
||||
if (mergeArgs.length > 1) {
|
||||
context.imports.add(MERGE_PROPS)
|
||||
propsExpression = createCallExpression(MERGE_PROPS, mergeArgs, elementLoc)
|
||||
propsExpression = createCallExpression(
|
||||
context.helper(MERGE_PROPS),
|
||||
mergeArgs,
|
||||
elementLoc
|
||||
)
|
||||
} else {
|
||||
// single v-bind with nothing else - no need for a mergeProps call
|
||||
propsExpression = mergeArgs[0]
|
||||
@@ -285,12 +288,12 @@ function createDirectiveArgs(
|
||||
dir: DirectiveNode,
|
||||
context: TransformContext
|
||||
): ArrayExpression {
|
||||
// inject import for `resolveDirective`
|
||||
context.imports.add(RESOLVE_DIRECTIVE)
|
||||
// inject statement for resolving directive
|
||||
const dirIdentifier = `_directive_${toValidId(dir.name)}`
|
||||
context.statements.push(
|
||||
`const ${dirIdentifier} = ${RESOLVE_DIRECTIVE}(${JSON.stringify(dir.name)})`
|
||||
`const ${dirIdentifier} = ${context.helper(
|
||||
RESOLVE_DIRECTIVE
|
||||
)}(${JSON.stringify(dir.name)})`
|
||||
)
|
||||
const dirArgs: ArrayExpression['elements'] = [dirIdentifier]
|
||||
const { loc } = dir
|
||||
|
||||
@@ -24,7 +24,7 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
const parseResult = parseForExpression(dir.exp, context)
|
||||
|
||||
if (parseResult) {
|
||||
context.imports.add(RENDER_LIST)
|
||||
context.helper(RENDER_LIST)
|
||||
const { source, value, key, index } = parseResult
|
||||
|
||||
context.replaceNode({
|
||||
|
||||
@@ -19,14 +19,10 @@ export const transformIf = createStructuralDirectiveTransform(
|
||||
processExpression(dir.exp, context)
|
||||
}
|
||||
if (dir.name === 'if') {
|
||||
// check if this v-if is root - so that in codegen we can avoid generating
|
||||
// arrays for each branch
|
||||
const isRoot = context.parent === context.root
|
||||
context.replaceNode({
|
||||
type: NodeTypes.IF,
|
||||
loc: node.loc,
|
||||
branches: [createIfBranch(node, dir, isRoot)],
|
||||
isRoot
|
||||
branches: [createIfBranch(node, dir)]
|
||||
})
|
||||
} else {
|
||||
// locate the adjacent v-if
|
||||
@@ -43,7 +39,7 @@ export const transformIf = createStructuralDirectiveTransform(
|
||||
if (sibling && sibling.type === NodeTypes.IF) {
|
||||
// move the node to the if node's branches
|
||||
context.removeNode()
|
||||
const branch = createIfBranch(node, dir, sibling.isRoot)
|
||||
const branch = createIfBranch(node, dir)
|
||||
if (__DEV__ && comments.length) {
|
||||
branch.children = [...comments, ...branch.children]
|
||||
}
|
||||
@@ -67,16 +63,11 @@ export const transformIf = createStructuralDirectiveTransform(
|
||||
}
|
||||
)
|
||||
|
||||
function createIfBranch(
|
||||
node: ElementNode,
|
||||
dir: DirectiveNode,
|
||||
isRoot: boolean
|
||||
): IfBranchNode {
|
||||
function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
|
||||
return {
|
||||
type: NodeTypes.IF_BRANCH,
|
||||
loc: node.loc,
|
||||
condition: dir.name === 'else' ? undefined : dir.exp,
|
||||
children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node],
|
||||
isRoot
|
||||
children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export function getInnerRange(
|
||||
export function advancePositionWithClone(
|
||||
pos: Position,
|
||||
source: string,
|
||||
numberOfCharacters: number
|
||||
numberOfCharacters: number = source.length
|
||||
): Position {
|
||||
return advancePositionWithMutation({ ...pos }, source, numberOfCharacters)
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export function advancePositionWithClone(
|
||||
export function advancePositionWithMutation(
|
||||
pos: Position,
|
||||
source: string,
|
||||
numberOfCharacters: number
|
||||
numberOfCharacters: number = source.length
|
||||
): Position {
|
||||
let linesCount = 0
|
||||
let lastNewLinePos = -1
|
||||
@@ -57,7 +57,7 @@ export function advancePositionWithMutation(
|
||||
pos.column =
|
||||
lastNewLinePos === -1
|
||||
? pos.column + numberOfCharacters
|
||||
: Math.max(1, numberOfCharacters - lastNewLinePos - 1)
|
||||
: Math.max(1, numberOfCharacters - lastNewLinePos)
|
||||
|
||||
return pos
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user