feat(compiler): correct source map generation
This commit is contained in:
parent
2e3a1ff3c3
commit
63b6902bdb
@ -7,9 +7,9 @@ return function render() {
|
||||
const { createVNode: _createVNode, toString: _toString, renderList: _renderList } = _Vue
|
||||
return _createVNode(\\"div\\", {
|
||||
id: \\"foo\\",
|
||||
class: bar
|
||||
class: bar.baz
|
||||
}, [
|
||||
_toString(world),
|
||||
_toString(world.burn()),
|
||||
ok
|
||||
? _createVNode(\\"div\\", 0, \\"yes\\")
|
||||
: \\"no\\",
|
||||
@ -21,25 +21,6 @@ return function render() {
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: integration tests function mode 2`] = `
|
||||
Object {
|
||||
"mappings": ";;;;WAAA,oBAAA;MAAK,IAAG;MAAO,OAAO;;gBACpB;MACW;UAAX,uBAAe;UACE;MACjB,YAA8B,OAAjB,OAAO;eAApB,wBAAoC,kCAAM",
|
||||
"names": Array [],
|
||||
"sources": Array [
|
||||
"foo.vue",
|
||||
],
|
||||
"sourcesContent": Array [
|
||||
"<div id=\\"foo\\" :class=\\"bar\\">
|
||||
{{ world }}
|
||||
<div v-if=\\"ok\\">yes</div>
|
||||
<template v-else>no</template>
|
||||
<div v-for=\\"(value, index) in list\\"><span>{{ value + index }}</span></div>
|
||||
</div>",
|
||||
],
|
||||
"version": 3,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = `
|
||||
"const { createVNode, toString, renderList } = Vue
|
||||
|
||||
@ -47,9 +28,9 @@ return function render() {
|
||||
const _ctx = this
|
||||
return createVNode(\\"div\\", {
|
||||
id: \\"foo\\",
|
||||
class: _ctx.bar
|
||||
class: _ctx.bar.baz
|
||||
}, [
|
||||
toString(_ctx.world),
|
||||
toString(_ctx.world.burn()),
|
||||
(_ctx.ok)
|
||||
? createVNode(\\"div\\", 0, \\"yes\\")
|
||||
: \\"no\\",
|
||||
@ -60,30 +41,6 @@ return function render() {
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 2`] = `
|
||||
Object {
|
||||
"mappings": ";;;;SAAA,mBAAA;IAAK,IAAG;IAAO,OAAOA;;aACpBC;KACWC;QAAX,sBAAe;QACE;IACjB,WAA8BC,YAAjB,OAAO;aAApB,uBAAoC,gCAAS,QAAQ",
|
||||
"names": Array [
|
||||
"bar",
|
||||
"world",
|
||||
"ok",
|
||||
"list",
|
||||
],
|
||||
"sources": Array [
|
||||
"foo.vue",
|
||||
],
|
||||
"sourcesContent": Array [
|
||||
"<div id=\\"foo\\" :class=\\"bar\\">
|
||||
{{ world }}
|
||||
<div v-if=\\"ok\\">yes</div>
|
||||
<template v-else>no</template>
|
||||
<div v-for=\\"(value, index) in list\\"><span>{{ value + index }}</span></div>
|
||||
</div>",
|
||||
],
|
||||
"version": 3,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`compiler: integration tests module mode 1`] = `
|
||||
"import { createVNode, toString, renderList } from \\"vue\\"
|
||||
|
||||
@ -91,9 +48,9 @@ export default function render() {
|
||||
const _ctx = this
|
||||
return createVNode(\\"div\\", {
|
||||
id: \\"foo\\",
|
||||
class: _ctx.bar
|
||||
class: _ctx.bar.baz
|
||||
}, [
|
||||
_toString(_ctx.world),
|
||||
_toString(_ctx.world.burn()),
|
||||
(_ctx.ok)
|
||||
? createVNode(\\"div\\", 0, \\"yes\\")
|
||||
: \\"no\\",
|
||||
|
@ -3889,15 +3889,15 @@ Object {
|
||||
"isStatic": false,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
"column": 20,
|
||||
"column": 18,
|
||||
"line": 1,
|
||||
"offset": 19,
|
||||
"offset": 17,
|
||||
},
|
||||
"source": "{{a < b}}",
|
||||
"source": "a < b",
|
||||
"start": Object {
|
||||
"column": 11,
|
||||
"column": 13,
|
||||
"line": 1,
|
||||
"offset": 10,
|
||||
"offset": 12,
|
||||
},
|
||||
},
|
||||
"type": 4,
|
||||
@ -6965,15 +6965,15 @@ Object {
|
||||
"isStatic": false,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
"column": 23,
|
||||
"column": 21,
|
||||
"line": 1,
|
||||
"offset": 22,
|
||||
"offset": 20,
|
||||
},
|
||||
"source": "{{'</div>'}}",
|
||||
"source": "'</div>'",
|
||||
"start": Object {
|
||||
"column": 11,
|
||||
"column": 13,
|
||||
"line": 1,
|
||||
"offset": 10,
|
||||
"offset": 12,
|
||||
},
|
||||
},
|
||||
"type": 4,
|
||||
@ -7322,15 +7322,15 @@ Object {
|
||||
"isStatic": true,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
"column": 5,
|
||||
"column": 3,
|
||||
"line": 1,
|
||||
"offset": 4,
|
||||
"offset": 2,
|
||||
},
|
||||
"source": "{{}}",
|
||||
"source": "",
|
||||
"start": Object {
|
||||
"column": 1,
|
||||
"column": 3,
|
||||
"line": 1,
|
||||
"offset": 0,
|
||||
"offset": 2,
|
||||
},
|
||||
},
|
||||
"type": 4,
|
||||
@ -7485,7 +7485,7 @@ Object {
|
||||
"line": 1,
|
||||
"offset": 32,
|
||||
},
|
||||
"source": "\\"{ some: condition }\\"",
|
||||
"source": "{ some: condition }",
|
||||
"start": Object {
|
||||
"column": 14,
|
||||
"line": 1,
|
||||
@ -7565,7 +7565,7 @@ Object {
|
||||
"line": 2,
|
||||
"offset": 70,
|
||||
},
|
||||
"source": "\\"{ color: 'red' }\\"",
|
||||
"source": "{ color: 'red' }",
|
||||
"start": Object {
|
||||
"column": 18,
|
||||
"line": 2,
|
||||
@ -7672,7 +7672,7 @@ Object {
|
||||
"line": 2,
|
||||
"offset": 70,
|
||||
},
|
||||
"source": "\\"{ color: 'red' }\\"",
|
||||
"source": "{ color: 'red' }",
|
||||
"start": Object {
|
||||
"column": 20,
|
||||
"line": 2,
|
||||
@ -7771,7 +7771,7 @@ Object {
|
||||
"line": 1,
|
||||
"offset": 32,
|
||||
},
|
||||
"source": "\\"{ some: condition }\\"",
|
||||
"source": "{ some: condition }",
|
||||
"start": Object {
|
||||
"column": 14,
|
||||
"line": 1,
|
||||
|
@ -3,8 +3,8 @@ import { SourceMapConsumer, RawSourceMap } from 'source-map'
|
||||
|
||||
describe('compiler: integration tests', () => {
|
||||
const source = `
|
||||
<div id="foo" :class="bar">
|
||||
{{ world }}
|
||||
<div id="foo" :class="bar.baz">
|
||||
{{ world.burn() }}
|
||||
<div v-if="ok">yes</div>
|
||||
<template v-else>no</template>
|
||||
<div v-for="(value, index) in list"><span>{{ value + index }}</span></div>
|
||||
@ -55,7 +55,6 @@ describe('compiler: integration tests', () => {
|
||||
)
|
||||
|
||||
expect(code).toMatchSnapshot()
|
||||
expect(map).toMatchSnapshot()
|
||||
expect(map!.sources).toEqual([`foo.vue`])
|
||||
expect(map!.sourcesContent).toEqual([source])
|
||||
|
||||
@ -77,9 +76,21 @@ describe('compiler: integration tests', () => {
|
||||
consumer.originalPositionFor(getPositionInCode(code, `bar`))
|
||||
).toMatchObject(getPositionInCode(source, `bar`))
|
||||
|
||||
// without prefixIdentifiers: true, identifiers inside compound expressions
|
||||
// are mapped to closest parent expression.
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `baz`))
|
||||
).toMatchObject(getPositionInCode(source, `bar`))
|
||||
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `world`))
|
||||
).toMatchObject(getPositionInCode(source, `{{ world }}`))
|
||||
).toMatchObject(getPositionInCode(source, `world`))
|
||||
|
||||
// without prefixIdentifiers: true, identifiers inside compound expressions
|
||||
// are mapped to closest parent expression.
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `burn()`))
|
||||
).toMatchObject(getPositionInCode(source, `world`))
|
||||
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `ok`))
|
||||
@ -99,7 +110,7 @@ describe('compiler: integration tests', () => {
|
||||
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `value + index`))
|
||||
).toMatchObject(getPositionInCode(source, `{{ value + index }}`))
|
||||
).toMatchObject(getPositionInCode(source, `value + index`))
|
||||
})
|
||||
|
||||
test('function mode w/ prefixIdentifiers: true', async () => {
|
||||
@ -112,7 +123,6 @@ describe('compiler: integration tests', () => {
|
||||
expect(code).toMatch(`const { createVNode, toString, renderList } = Vue`)
|
||||
|
||||
expect(code).toMatchSnapshot()
|
||||
expect(map).toMatchSnapshot()
|
||||
expect(map!.sources).toEqual([`foo.vue`])
|
||||
expect(map!.sourcesContent).toEqual([source])
|
||||
|
||||
@ -136,15 +146,22 @@ describe('compiler: integration tests', () => {
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `_ctx.bar`, `bar`))
|
||||
).toMatchObject(getPositionInCode(source, `bar`, true))
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `baz`))
|
||||
).toMatchObject(getPositionInCode(source, `baz`))
|
||||
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `world`, true))
|
||||
).toMatchObject(getPositionInCode(source, `{{ world }}`, `world`))
|
||||
).toMatchObject(getPositionInCode(source, `world`, `world`))
|
||||
expect(
|
||||
consumer.originalPositionFor(
|
||||
getPositionInCode(code, `_ctx.world`, `world`)
|
||||
)
|
||||
).toMatchObject(getPositionInCode(source, `{{ world }}`, `world`))
|
||||
).toMatchObject(getPositionInCode(source, `world`, `world`))
|
||||
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `burn()`))
|
||||
).toMatchObject(getPositionInCode(source, `burn()`))
|
||||
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `ok`))
|
||||
@ -208,15 +225,22 @@ describe('compiler: integration tests', () => {
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `_ctx.bar`, `bar`))
|
||||
).toMatchObject(getPositionInCode(source, `bar`, true))
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `baz`))
|
||||
).toMatchObject(getPositionInCode(source, `baz`))
|
||||
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `world`, true))
|
||||
).toMatchObject(getPositionInCode(source, `{{ world }}`, `world`))
|
||||
).toMatchObject(getPositionInCode(source, `world`, `world`))
|
||||
expect(
|
||||
consumer.originalPositionFor(
|
||||
getPositionInCode(code, `_ctx.world`, `world`)
|
||||
)
|
||||
).toMatchObject(getPositionInCode(source, `{{ world }}`, `world`))
|
||||
).toMatchObject(getPositionInCode(source, `world`, `world`))
|
||||
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `burn()`))
|
||||
).toMatchObject(getPositionInCode(source, `burn()`))
|
||||
|
||||
expect(
|
||||
consumer.originalPositionFor(getPositionInCode(code, `ok`))
|
||||
|
@ -298,9 +298,9 @@ describe('compiler: parse', () => {
|
||||
isStatic: false,
|
||||
isInterpolation: true,
|
||||
loc: {
|
||||
start: { offset: 0, line: 1, column: 1 },
|
||||
end: { offset: 11, line: 1, column: 12 },
|
||||
source: '{{message}}'
|
||||
start: { offset: 2, line: 1, column: 3 },
|
||||
end: { offset: 9, line: 1, column: 10 },
|
||||
source: 'message'
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -315,9 +315,9 @@ describe('compiler: parse', () => {
|
||||
isStatic: false,
|
||||
isInterpolation: true,
|
||||
loc: {
|
||||
start: { offset: 0, line: 1, column: 1 },
|
||||
end: { offset: 9, line: 1, column: 10 },
|
||||
source: '{{ a<b }}'
|
||||
start: { offset: 3, line: 1, column: 4 },
|
||||
end: { offset: 6, line: 1, column: 7 },
|
||||
source: 'a<b'
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -333,9 +333,9 @@ describe('compiler: parse', () => {
|
||||
isStatic: false,
|
||||
isInterpolation: true,
|
||||
loc: {
|
||||
start: { offset: 0, line: 1, column: 1 },
|
||||
end: { offset: 9, line: 1, column: 10 },
|
||||
source: '{{ a<b }}'
|
||||
start: { offset: 3, line: 1, column: 4 },
|
||||
end: { offset: 6, line: 1, column: 7 },
|
||||
source: 'a<b'
|
||||
}
|
||||
})
|
||||
expect(interpolation2).toStrictEqual({
|
||||
@ -344,9 +344,9 @@ describe('compiler: parse', () => {
|
||||
isStatic: false,
|
||||
isInterpolation: true,
|
||||
loc: {
|
||||
start: { offset: 9, line: 1, column: 10 },
|
||||
end: { offset: 18, line: 1, column: 19 },
|
||||
source: '{{ c>d }}'
|
||||
start: { offset: 12, line: 1, column: 13 },
|
||||
end: { offset: 15, line: 1, column: 16 },
|
||||
source: 'c>d'
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -362,9 +362,9 @@ describe('compiler: parse', () => {
|
||||
isStatic: false,
|
||||
isInterpolation: true,
|
||||
loc: {
|
||||
start: { offset: 5, line: 1, column: 6 },
|
||||
end: { offset: 19, line: 1, column: 20 },
|
||||
source: '{{ "</div>" }}'
|
||||
start: { offset: 8, line: 1, column: 9 },
|
||||
end: { offset: 16, line: 1, column: 17 },
|
||||
source: '"</div>"'
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -896,7 +896,7 @@ describe('compiler: parse', () => {
|
||||
loc: {
|
||||
start: { offset: 11, line: 1, column: 12 },
|
||||
end: { offset: 12, line: 1, column: 13 },
|
||||
source: '"a"'
|
||||
source: 'a'
|
||||
}
|
||||
},
|
||||
loc: {
|
||||
@ -1313,17 +1313,20 @@ foo
|
||||
offset += foo.loc.source.length
|
||||
expect(foo.loc.end).toEqual({ line: 2, column: 5, offset })
|
||||
|
||||
expect(bar.loc.start).toEqual({ line: 2, column: 5, offset })
|
||||
offset += 3
|
||||
expect(bar.loc.start).toEqual({ line: 2, column: 8, offset })
|
||||
offset += bar.loc.source.length
|
||||
expect(bar.loc.end).toEqual({ line: 2, column: 14, offset })
|
||||
expect(bar.loc.end).toEqual({ line: 2, column: 11, offset })
|
||||
offset += 3
|
||||
|
||||
expect(but.loc.start).toEqual({ line: 2, column: 14, offset })
|
||||
offset += but.loc.source.length
|
||||
expect(but.loc.end).toEqual({ line: 2, column: 19, offset })
|
||||
|
||||
expect(baz.loc.start).toEqual({ line: 2, column: 19, offset })
|
||||
offset += 3
|
||||
expect(baz.loc.start).toEqual({ line: 2, column: 22, offset })
|
||||
offset += baz.loc.source.length
|
||||
expect(baz.loc.end).toEqual({ line: 2, column: 28, offset })
|
||||
expect(baz.loc.end).toEqual({ line: 2, column: 25, offset })
|
||||
})
|
||||
|
||||
describe('namedCharacterReferences option', () => {
|
||||
|
@ -66,7 +66,7 @@ export interface CodegenContext extends Required<CodegenOptions> {
|
||||
indentLevel: number
|
||||
map?: SourceMapGenerator
|
||||
helper(name: string): string
|
||||
push(code: string, node?: CodegenNode): void
|
||||
push(code: string, node?: CodegenNode, openOnly?: boolean): void
|
||||
indent(): void
|
||||
deindent(withoutNewLine?: boolean): void
|
||||
newline(): void
|
||||
@ -102,7 +102,7 @@ function createCodegenContext(
|
||||
helper(name) {
|
||||
return prefixIdentifiers ? name : `_${name}`
|
||||
},
|
||||
push(code, node?: CodegenNode) {
|
||||
push(code, node, openOnly) {
|
||||
context.code += code
|
||||
if (context.map) {
|
||||
if (node) {
|
||||
@ -131,6 +131,19 @@ function createCodegenContext(
|
||||
})
|
||||
}
|
||||
advancePositionWithMutation(context, code)
|
||||
if (node && !openOnly) {
|
||||
context.map.addMapping({
|
||||
source: context.filename,
|
||||
original: {
|
||||
line: node.loc.end.line,
|
||||
column: node.loc.end.column - 1
|
||||
},
|
||||
generated: {
|
||||
line: context.line,
|
||||
column: context.column - 1
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
indent() {
|
||||
@ -453,7 +466,7 @@ function genIfBranch(
|
||||
function genFor(node: ForNode, context: CodegenContext) {
|
||||
const { push, helper, indent, deindent } = context
|
||||
const { source, keyAlias, valueAlias, objectIndexAlias, children } = node
|
||||
push(`${helper(RENDER_LIST)}(`, node)
|
||||
push(`${helper(RENDER_LIST)}(`, node, true)
|
||||
genExpression(source, context)
|
||||
push(`, (`)
|
||||
if (valueAlias) {
|
||||
@ -491,7 +504,7 @@ function genCallExpression(
|
||||
context: CodegenContext,
|
||||
multilines = node.arguments.length > 2
|
||||
) {
|
||||
context.push(node.callee + `(`, node)
|
||||
context.push(node.callee + `(`, node, true)
|
||||
multilines && context.indent()
|
||||
genNodeList(node.arguments, context, multilines)
|
||||
multilines && context.deindent()
|
||||
@ -502,7 +515,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
|
||||
const { push, indent, deindent, newline } = context
|
||||
const { properties } = node
|
||||
const multilines = properties.length > 1
|
||||
push(multilines ? `{` : `{ `, node)
|
||||
push(multilines ? `{` : `{ `)
|
||||
multilines && indent()
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
const { key, value } = properties[i]
|
||||
|
@ -542,6 +542,7 @@ function parseAttribute(
|
||||
valueLoc.start.offset++
|
||||
valueLoc.start.column++
|
||||
valueLoc.end = advancePositionWithClone(valueLoc.start, value.content)
|
||||
valueLoc.source = valueLoc.source.slice(1, -1)
|
||||
}
|
||||
|
||||
return {
|
||||
@ -642,15 +643,26 @@ function parseInterpolation(
|
||||
return undefined
|
||||
}
|
||||
|
||||
const start = getCursor(context)
|
||||
advanceBy(context, open.length)
|
||||
const content = parseTextData(context, closeIndex - open.length, mode).trim()
|
||||
const start = getCursor(context)
|
||||
const end = getCursor(context)
|
||||
const rawContentLength = closeIndex - open.length
|
||||
const rawContent = context.source.slice(0, rawContentLength)
|
||||
const preTrimContent = parseTextData(context, rawContentLength, mode)
|
||||
const content = preTrimContent.trim()
|
||||
const startOffset = preTrimContent.indexOf(content)
|
||||
if (startOffset > 0) {
|
||||
advancePositionWithMutation(start, rawContent, startOffset)
|
||||
}
|
||||
const endOffset =
|
||||
rawContentLength - (preTrimContent.length - content.length - startOffset)
|
||||
advancePositionWithMutation(end, rawContent, endOffset)
|
||||
advanceBy(context, close.length)
|
||||
|
||||
return {
|
||||
type: NodeTypes.EXPRESSION,
|
||||
content,
|
||||
loc: getSelection(context, start),
|
||||
loc: getSelection(context, start, end),
|
||||
isStatic: content === '',
|
||||
isInterpolation: true
|
||||
}
|
||||
|
@ -157,8 +157,8 @@ export function processExpression(
|
||||
children.push(
|
||||
createExpression(id.name, false, {
|
||||
source,
|
||||
start: advancePositionWithClone(node.loc.start, source, id.start + 2),
|
||||
end: advancePositionWithClone(node.loc.start, source, id.end + 2)
|
||||
start: advancePositionWithClone(node.loc.start, source, id.start - 1),
|
||||
end: advancePositionWithClone(node.loc.start, source, id.end - 1)
|
||||
})
|
||||
)
|
||||
if (i === ids.length - 1 && id.end - 1 < full.length) {
|
||||
|
@ -117,9 +117,9 @@ describe('DOM parser', () => {
|
||||
isStatic: false,
|
||||
isInterpolation: true,
|
||||
loc: {
|
||||
start: { offset: 5, line: 1, column: 6 },
|
||||
end: { offset: 19, line: 1, column: 20 },
|
||||
source: '{{ a < b }}'
|
||||
start: { offset: 8, line: 1, column: 9 },
|
||||
end: { offset: 16, line: 1, column: 17 },
|
||||
source: 'a < b'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user