feat(sfc): accept inMap in compileTemplate()
This commit is contained in:
parent
02c6d5c4e3
commit
3a3a24d621
@ -145,7 +145,7 @@ export const errorMessages: { [code: number]: string } = {
|
|||||||
|
|
||||||
// Vue-specific parse errors
|
// Vue-specific parse errors
|
||||||
[ErrorCodes.X_INVALID_END_TAG]: 'Invalid end tag.',
|
[ErrorCodes.X_INVALID_END_TAG]: 'Invalid end tag.',
|
||||||
[ErrorCodes.X_MISSING_END_TAG]: 'End tag was not found.',
|
[ErrorCodes.X_MISSING_END_TAG]: 'Element is missing end tag.',
|
||||||
[ErrorCodes.X_MISSING_INTERPOLATION_END]:
|
[ErrorCodes.X_MISSING_INTERPOLATION_END]:
|
||||||
'Interpolation end sign was not found.',
|
'Interpolation end sign was not found.',
|
||||||
[ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END]:
|
[ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END]:
|
||||||
|
@ -372,7 +372,7 @@ function parseElement(
|
|||||||
if (startsWithEndTagOpen(context.source, element.tag)) {
|
if (startsWithEndTagOpen(context.source, element.tag)) {
|
||||||
parseTag(context, TagType.End, parent)
|
parseTag(context, TagType.End, parent)
|
||||||
} else {
|
} else {
|
||||||
emitError(context, ErrorCodes.X_MISSING_END_TAG)
|
emitError(context, ErrorCodes.X_MISSING_END_TAG, 0, element.loc.start)
|
||||||
if (context.source.length === 0 && element.tag.toLowerCase() === 'script') {
|
if (context.source.length === 0 && element.tag.toLowerCase() === 'script') {
|
||||||
const first = children[0]
|
const first = children[0]
|
||||||
if (first && startsWith(first.loc.source, '<!--')) {
|
if (first && startsWith(first.loc.source, '<!--')) {
|
||||||
@ -963,9 +963,9 @@ function getNewPosition(
|
|||||||
function emitError(
|
function emitError(
|
||||||
context: ParserContext,
|
context: ParserContext,
|
||||||
code: ErrorCodes,
|
code: ErrorCodes,
|
||||||
offset?: number
|
offset?: number,
|
||||||
|
loc: Position = getCursor(context)
|
||||||
): void {
|
): void {
|
||||||
const loc = getCursor(context)
|
|
||||||
if (offset) {
|
if (offset) {
|
||||||
loc.offset += offset
|
loc.offset += offset
|
||||||
loc.column += offset
|
loc.column += offset
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
CompilerError,
|
CompilerError,
|
||||||
NodeTransform
|
NodeTransform
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import { RawSourceMap } from 'source-map'
|
import { SourceMapConsumer, SourceMapGenerator, RawSourceMap } from 'source-map'
|
||||||
import {
|
import {
|
||||||
transformAssetUrl,
|
transformAssetUrl,
|
||||||
AssetURLOptions,
|
AssetURLOptions,
|
||||||
@ -29,6 +29,7 @@ export interface SFCTemplateCompileResults {
|
|||||||
export interface SFCTemplateCompileOptions {
|
export interface SFCTemplateCompileOptions {
|
||||||
source: string
|
source: string
|
||||||
filename: string
|
filename: string
|
||||||
|
inMap?: RawSourceMap
|
||||||
compiler?: TemplateCompiler
|
compiler?: TemplateCompiler
|
||||||
compilerOptions?: CompilerOptions
|
compilerOptions?: CompilerOptions
|
||||||
preprocessLang?: string
|
preprocessLang?: string
|
||||||
@ -100,6 +101,7 @@ export function compileTemplate(
|
|||||||
|
|
||||||
function doCompileTemplate({
|
function doCompileTemplate({
|
||||||
filename,
|
filename,
|
||||||
|
inMap,
|
||||||
source,
|
source,
|
||||||
compiler = require('@vue/compiler-dom'),
|
compiler = require('@vue/compiler-dom'),
|
||||||
compilerOptions = {},
|
compilerOptions = {},
|
||||||
@ -117,7 +119,7 @@ function doCompileTemplate({
|
|||||||
nodeTransforms = [transformAssetUrl, transformSrcset]
|
nodeTransforms = [transformAssetUrl, transformSrcset]
|
||||||
}
|
}
|
||||||
|
|
||||||
const { code, map } = compiler.compile(source, {
|
let { code, map } = compiler.compile(source, {
|
||||||
mode: 'module',
|
mode: 'module',
|
||||||
prefixIdentifiers: true,
|
prefixIdentifiers: true,
|
||||||
hoistStatic: true,
|
hoistStatic: true,
|
||||||
@ -128,5 +130,91 @@ function doCompileTemplate({
|
|||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
onError: e => errors.push(e)
|
onError: e => errors.push(e)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// inMap should be the map produced by ./parse.ts which is a simple line-only
|
||||||
|
// mapping. If it is present, we need to adjust the final map and errors to
|
||||||
|
// reflect the original line numbers.
|
||||||
|
if (inMap) {
|
||||||
|
if (map) {
|
||||||
|
map = mapLines(inMap, map)
|
||||||
|
}
|
||||||
|
if (errors.length) {
|
||||||
|
patchErrors(errors, source, inMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { code, source, errors, tips: [], map }
|
return { code, source, errors, tips: [], map }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapLines(oldMap: RawSourceMap, newMap: RawSourceMap): RawSourceMap {
|
||||||
|
if (!oldMap) return newMap
|
||||||
|
if (!newMap) return oldMap
|
||||||
|
|
||||||
|
const oldMapConsumer = new SourceMapConsumer(oldMap)
|
||||||
|
const newMapConsumer = new SourceMapConsumer(newMap)
|
||||||
|
const mergedMapGenerator = new SourceMapGenerator()
|
||||||
|
|
||||||
|
newMapConsumer.eachMapping(m => {
|
||||||
|
if (m.originalLine == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const origPosInOldMap = oldMapConsumer.originalPositionFor({
|
||||||
|
line: m.originalLine,
|
||||||
|
column: m.originalColumn
|
||||||
|
})
|
||||||
|
|
||||||
|
if (origPosInOldMap.source == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedMapGenerator.addMapping({
|
||||||
|
generated: {
|
||||||
|
line: m.generatedLine,
|
||||||
|
column: m.generatedColumn
|
||||||
|
},
|
||||||
|
original: {
|
||||||
|
line: origPosInOldMap.line, // map line
|
||||||
|
// use current column, since the oldMap produced by @vue/compiler-sfc
|
||||||
|
// does not
|
||||||
|
column: m.originalColumn
|
||||||
|
},
|
||||||
|
source: origPosInOldMap.source,
|
||||||
|
name: origPosInOldMap.name
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// source-map's type definition is incomplete
|
||||||
|
const generator = mergedMapGenerator as any
|
||||||
|
;(oldMapConsumer as any).sources.forEach((sourceFile: string) => {
|
||||||
|
generator._sources.add(sourceFile)
|
||||||
|
const sourceContent = oldMapConsumer.sourceContentFor(sourceFile)
|
||||||
|
if (sourceContent != null) {
|
||||||
|
mergedMapGenerator.setSourceContent(sourceFile, sourceContent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
generator._sourceRoot = oldMap.sourceRoot
|
||||||
|
generator._file = oldMap.file
|
||||||
|
return generator.toJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchErrors(
|
||||||
|
errors: CompilerError[],
|
||||||
|
source: string,
|
||||||
|
inMap: RawSourceMap
|
||||||
|
) {
|
||||||
|
const originalSource = inMap.sourcesContent![0]
|
||||||
|
const offset = originalSource.indexOf(source)
|
||||||
|
const lineOffset = originalSource.slice(0, offset).split(/\r?\n/).length - 1
|
||||||
|
errors.forEach(err => {
|
||||||
|
if (err.loc) {
|
||||||
|
err.loc.start.line += lineOffset
|
||||||
|
err.loc.start.offset += offset
|
||||||
|
if (err.loc.end !== err.loc.start) {
|
||||||
|
err.loc.end.line += lineOffset
|
||||||
|
err.loc.end.offset += offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user