Merge pull request #27629 from Microsoft/sourceMapGenerator

Simplify source map generation
This commit is contained in:
Ron Buckton 2018-11-09 14:50:22 -08:00 committed by GitHub
commit 50a0174582
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 1831 additions and 1789 deletions

View file

@ -1,6 +1,7 @@
// @ts-check
const path = require("path");
const child_process = require("child_process");
const fs = require("fs");
const tsc = require("gulp-typescript");
const Vinyl = require("vinyl");
const { Duplex, Readable } = require("stream");
@ -42,7 +43,10 @@ function createProject(tsConfigFileName, settings, options) {
getVinyl(path) { return inputs.get(path); },
getSourceFile(fileName) { return sourceFiles.get(fileName); },
createSourceFile(fileName, text, languageVersion) {
if (text === undefined) throw new Error("File not cached.");
if (text === undefined) {
text = fs.readFileSync(fileName, "utf8");
}
/** @type {protocol.SourceFile} */
let file;
if (options.parse) {

View file

@ -450,7 +450,7 @@ namespace ts {
assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile);
if (!targetSourceFile) {
// Emit and report any errors we ran into.
let sourceMaps: SourceMapData[] = [];
let sourceMaps: SourceMapEmitResult[] = [];
let emitSkipped = false;
let diagnostics: Diagnostic[] | undefined;
let emittedFiles: string[] = [];

View file

@ -3141,7 +3141,7 @@ namespace ts {
const sig = nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName);
const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true });
const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration);
printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, writer); // TODO: GH#18217
printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, getTrailingSemicolonOmittingWriter(writer)); // TODO: GH#18217
return writer;
}
}

View file

@ -1,431 +0,0 @@
/* @internal */
namespace ts {
export interface CommentWriter {
reset(): void;
setSourceFile(sourceFile: SourceFile): void;
setWriter(writer: EmitTextWriter | undefined): void;
emitNodeWithComments(hint: EmitHint, node: Node | undefined, emitCallback: (hint: EmitHint, node: Node) => void): void;
emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void;
emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean): void;
emitLeadingCommentsOfPosition(pos: number): void;
}
export function createCommentWriter(printerOptions: PrinterOptions, emitPos: ((pos: number) => void) | undefined): CommentWriter {
const extendedDiagnostics = printerOptions.extendedDiagnostics;
const newLine = getNewLineCharacter(printerOptions);
let writer: EmitTextWriter;
let containerPos = -1;
let containerEnd = -1;
let declarationListContainerEnd = -1;
let currentSourceFile: SourceFile;
let currentText: string;
let currentLineMap: ReadonlyArray<number>;
let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[] | undefined;
let hasWrittenComment = false;
let disabled: boolean = !!printerOptions.removeComments;
return {
reset,
setWriter,
setSourceFile,
emitNodeWithComments,
emitBodyWithDetachedComments,
emitTrailingCommentsOfPosition,
emitLeadingCommentsOfPosition,
};
function emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (disabled) {
emitCallback(hint, node);
return;
}
if (node) {
hasWrittenComment = false;
const emitNode = node.emitNode;
const emitFlags = emitNode && emitNode.flags || 0;
const { pos, end } = emitNode && emitNode.commentRange || node;
if ((pos < 0 && end < 0) || (pos === end)) {
// Both pos and end are synthesized, so just emit the node without comments.
emitNodeWithSynthesizedComments(hint, node, emitNode, emitFlags, emitCallback);
}
else {
if (extendedDiagnostics) {
performance.mark("preEmitNodeWithComment");
}
const isEmittedNode = node.kind !== SyntaxKind.NotEmittedStatement;
// We have to explicitly check that the node is JsxText because if the compilerOptions.jsx is "preserve" we will not do any transformation.
// It is expensive to walk entire tree just to set one kind of node to have no comments.
const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0 || node.kind === SyntaxKind.JsxText;
const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0 || node.kind === SyntaxKind.JsxText;
// Emit leading comments if the position is not synthesized and the node
// has not opted out from emitting leading comments.
if (!skipLeadingComments) {
emitLeadingComments(pos, isEmittedNode);
}
// Save current container state on the stack.
const savedContainerPos = containerPos;
const savedContainerEnd = containerEnd;
const savedDeclarationListContainerEnd = declarationListContainerEnd;
if (!skipLeadingComments || (pos >= 0 && (emitFlags & EmitFlags.NoLeadingComments) !== 0)) {
// Advance the container position of comments get emitted or if they've been disabled explicitly using NoLeadingComments.
containerPos = pos;
}
if (!skipTrailingComments || (end >= 0 && (emitFlags & EmitFlags.NoTrailingComments) !== 0)) {
// As above.
containerEnd = end;
// To avoid invalid comment emit in a down-level binding pattern, we
// keep track of the last declaration list container's end
if (node.kind === SyntaxKind.VariableDeclarationList) {
declarationListContainerEnd = end;
}
}
if (extendedDiagnostics) {
performance.measure("commentTime", "preEmitNodeWithComment");
}
emitNodeWithSynthesizedComments(hint, node, emitNode, emitFlags, emitCallback);
if (extendedDiagnostics) {
performance.mark("postEmitNodeWithComment");
}
// Restore previous container state.
containerPos = savedContainerPos;
containerEnd = savedContainerEnd;
declarationListContainerEnd = savedDeclarationListContainerEnd;
// Emit trailing comments if the position is not synthesized and the node
// has not opted out from emitting leading comments and is an emitted node.
if (!skipTrailingComments && isEmittedNode) {
emitTrailingComments(end);
}
if (extendedDiagnostics) {
performance.measure("commentTime", "postEmitNodeWithComment");
}
}
}
}
function emitNodeWithSynthesizedComments(hint: EmitHint, node: Node, emitNode: EmitNode | undefined, emitFlags: EmitFlags, emitCallback: (hint: EmitHint, node: Node) => void) {
const leadingComments = emitNode && emitNode.leadingComments;
if (some(leadingComments)) {
if (extendedDiagnostics) {
performance.mark("preEmitNodeWithSynthesizedComments");
}
forEach(leadingComments, emitLeadingSynthesizedComment);
if (extendedDiagnostics) {
performance.measure("commentTime", "preEmitNodeWithSynthesizedComments");
}
}
emitNodeWithNestedComments(hint, node, emitFlags, emitCallback);
const trailingComments = emitNode && emitNode.trailingComments;
if (some(trailingComments)) {
if (extendedDiagnostics) {
performance.mark("postEmitNodeWithSynthesizedComments");
}
forEach(trailingComments, emitTrailingSynthesizedComment);
if (extendedDiagnostics) {
performance.measure("commentTime", "postEmitNodeWithSynthesizedComments");
}
}
}
function emitLeadingSynthesizedComment(comment: SynthesizedComment) {
if (comment.kind === SyntaxKind.SingleLineCommentTrivia) {
writer.writeLine();
}
writeSynthesizedComment(comment);
if (comment.hasTrailingNewLine || comment.kind === SyntaxKind.SingleLineCommentTrivia) {
writer.writeLine();
}
else {
writer.write(" ");
}
}
function emitTrailingSynthesizedComment(comment: SynthesizedComment) {
if (!writer.isAtStartOfLine()) {
writer.write(" ");
}
writeSynthesizedComment(comment);
if (comment.hasTrailingNewLine) {
writer.writeLine();
}
}
function writeSynthesizedComment(comment: SynthesizedComment) {
const text = formatSynthesizedComment(comment);
const lineMap = comment.kind === SyntaxKind.MultiLineCommentTrivia ? computeLineStarts(text) : undefined;
writeCommentRange(text, lineMap!, writer, 0, text.length, newLine);
}
function formatSynthesizedComment(comment: SynthesizedComment) {
return comment.kind === SyntaxKind.MultiLineCommentTrivia
? `/*${comment.text}*/`
: `//${comment.text}`;
}
function emitNodeWithNestedComments(hint: EmitHint, node: Node, emitFlags: EmitFlags, emitCallback: (hint: EmitHint, node: Node) => void) {
if (emitFlags & EmitFlags.NoNestedComments) {
disabled = true;
emitCallback(hint, node);
disabled = false;
}
else {
emitCallback(hint, node);
}
}
function emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) {
if (extendedDiagnostics) {
performance.mark("preEmitBodyWithDetachedComments");
}
const { pos, end } = detachedRange;
const emitFlags = getEmitFlags(node);
const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0;
const skipTrailingComments = disabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0;
if (!skipLeadingComments) {
emitDetachedCommentsAndUpdateCommentsInfo(detachedRange);
}
if (extendedDiagnostics) {
performance.measure("commentTime", "preEmitBodyWithDetachedComments");
}
if (emitFlags & EmitFlags.NoNestedComments && !disabled) {
disabled = true;
emitCallback(node);
disabled = false;
}
else {
emitCallback(node);
}
if (extendedDiagnostics) {
performance.mark("beginEmitBodyWithDetachedComments");
}
if (!skipTrailingComments) {
emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true);
if (hasWrittenComment && !writer.isAtStartOfLine()) {
writer.writeLine();
}
}
if (extendedDiagnostics) {
performance.measure("commentTime", "beginEmitBodyWithDetachedComments");
}
}
function emitLeadingComments(pos: number, isEmittedNode: boolean) {
hasWrittenComment = false;
if (isEmittedNode) {
forEachLeadingCommentToEmit(pos, emitLeadingComment);
}
else if (pos === 0) {
// If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
// unless it is a triple slash comment at the top of the file.
// For Example:
// /// <reference-path ...>
// declare var x;
// /// <reference-path ...>
// interface F {}
// The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
forEachLeadingCommentToEmit(pos, emitTripleSlashLeadingComment);
}
}
function emitTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
if (isTripleSlashComment(commentPos, commentEnd)) {
emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos);
}
}
function shouldWriteComment(text: string, pos: number) {
if (printerOptions.onlyPrintJsDocStyle) {
return (isJSDocLikeText(text, pos) || isPinnedComment(text, pos));
}
return true;
}
function emitLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
if (!shouldWriteComment(currentText, commentPos)) return;
if (!hasWrittenComment) {
emitNewLineBeforeLeadingCommentOfPosition(currentLineMap, writer, rangePos, commentPos);
hasWrittenComment = true;
}
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
if (emitPos) emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
if (emitPos) emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
}
else if (kind === SyntaxKind.MultiLineCommentTrivia) {
writer.write(" ");
}
}
function emitLeadingCommentsOfPosition(pos: number) {
if (disabled || pos === -1) {
return;
}
emitLeadingComments(pos, /*isEmittedNode*/ true);
}
function emitTrailingComments(pos: number) {
forEachTrailingCommentToEmit(pos, emitTrailingComment);
}
function emitTrailingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
if (!shouldWriteComment(currentText, commentPos)) return;
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/
if (!writer.isAtStartOfLine()) {
writer.write(" ");
}
if (emitPos) emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
if (emitPos) emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
}
}
function emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean) {
if (disabled) {
return;
}
if (extendedDiagnostics) {
performance.mark("beforeEmitTrailingCommentsOfPosition");
}
forEachTrailingCommentToEmit(pos, prefixSpace ? emitTrailingComment : emitTrailingCommentOfPosition);
if (extendedDiagnostics) {
performance.measure("commentTime", "beforeEmitTrailingCommentsOfPosition");
}
}
function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) {
// trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
if (emitPos) emitPos(commentPos);
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
if (emitPos) emitPos(commentEnd);
if (hasTrailingNewLine) {
writer.writeLine();
}
else {
writer.write(" ");
}
}
function forEachLeadingCommentToEmit(pos: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
// Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
if (containerPos === -1 || pos !== containerPos) {
if (hasDetachedComments(pos)) {
forEachLeadingCommentWithoutDetachedComments(cb);
}
else {
forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
}
}
}
function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) {
// Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
if (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd)) {
forEachTrailingCommentRange(currentText, end, cb);
}
}
function reset() {
currentSourceFile = undefined!;
currentText = undefined!;
currentLineMap = undefined!;
detachedCommentsInfo = undefined;
}
function setWriter(output: EmitTextWriter): void {
writer = output;
}
function setSourceFile(sourceFile: SourceFile) {
currentSourceFile = sourceFile;
currentText = currentSourceFile.text;
currentLineMap = getLineStarts(currentSourceFile);
detachedCommentsInfo = undefined;
}
function hasDetachedComments(pos: number) {
return detachedCommentsInfo !== undefined && last(detachedCommentsInfo).nodePos === pos;
}
function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
// get the leading comments from detachedPos
const pos = last(detachedCommentsInfo!).detachedCommentEndPos;
if (detachedCommentsInfo!.length - 1) {
detachedCommentsInfo!.pop();
}
else {
detachedCommentsInfo = undefined;
}
forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
}
function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) {
const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, range, newLine, disabled);
if (currentDetachedCommentInfo) {
if (detachedCommentsInfo) {
detachedCommentsInfo.push(currentDetachedCommentInfo);
}
else {
detachedCommentsInfo = [currentDetachedCommentInfo];
}
}
}
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
if (!shouldWriteComment(currentText, commentPos)) return;
if (emitPos) emitPos(commentPos);
writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
if (emitPos) emitPos(commentEnd);
}
/**
* Determine if the given comment is a triple-slash
*
* @return true if the comment is a triple-slash comment else false
*/
function isTripleSlashComment(commentPos: number, commentEnd: number) {
return isRecognizedTripleSlashComment(currentText, commentPos, commentEnd);
}
}
}

View file

@ -1061,10 +1061,10 @@ namespace ts {
/**
* Stable sort of an array. Elements equal to each other maintain their relative position in the array.
*/
export function stableSort<T>(array: ReadonlyArray<T>, comparer: Comparer<T>) {
export function stableSort<T>(array: ReadonlyArray<T>, comparer: Comparer<T>): SortedReadonlyArray<T> {
const indices = array.map((_, i) => i);
stableSortIndices(array, indices, comparer);
return indices.map(i => array[i]);
return indices.map(i => array[i]) as SortedArray<T> as SortedReadonlyArray<T>;
}
export function rangeEquals<T>(array1: ReadonlyArray<T>, array2: ReadonlyArray<T>, pos: number, end: number) {
@ -1156,13 +1156,26 @@ namespace ts {
* @param offset An offset into `array` at which to start the search.
*/
export function binarySearch<T, U>(array: ReadonlyArray<T>, value: T, keySelector: (v: T) => U, keyComparer: Comparer<U>, offset?: number): number {
if (!array || array.length === 0) {
return binarySearchKey(array, keySelector(value), keySelector, keyComparer, offset);
}
/**
* Performs a binary search, finding the index at which an object with `key` occurs in `array`.
* If no such index is found, returns the 2's-complement of first index at which
* `array[index]` exceeds `key`.
* @param array A sorted array whose first element must be no larger than number
* @param key The key to be searched for in the array.
* @param keySelector A callback used to select the search key from each element of `array`.
* @param keyComparer A callback used to compare two keys in a sorted array.
* @param offset An offset into `array` at which to start the search.
*/
export function binarySearchKey<T, U>(array: ReadonlyArray<T>, key: U, keySelector: (v: T) => U, keyComparer: Comparer<U>, offset?: number): number {
if (!some(array)) {
return -1;
}
let low = offset || 0;
let high = array.length - 1;
const key = keySelector(value);
while (low <= high) {
const middle = low + ((high - low) >> 1);
const midKey = keySelector(array[middle]);

File diff suppressed because it is too large Load diff

View file

@ -19,6 +19,41 @@ namespace ts.performance {
let marks: Map<number>;
let measures: Map<number>;
export interface Timer {
enter(): void;
exit(): void;
}
export function createTimerIf(condition: boolean, measureName: string, startMarkName: string, endMarkName: string) {
return condition ? createTimer(measureName, startMarkName, endMarkName) : nullTimer;
}
export function createTimer(measureName: string, startMarkName: string, endMarkName: string): Timer {
let enterCount = 0;
return {
enter,
exit
};
function enter() {
if (++enterCount === 1) {
mark(startMarkName);
}
}
function exit() {
if (--enterCount === 0) {
mark(endMarkName);
measure(measureName, startMarkName, endMarkName);
}
else if (enterCount < 0) {
Debug.fail("enter/exit count does not match.");
}
}
}
export const nullTimer: Timer = { enter: noop, exit: noop };
/**
* Marks a performance event.
*

File diff suppressed because it is too large Load diff

View file

@ -1,376 +0,0 @@
/* @internal */
namespace ts {
export interface SourceFileLikeCache {
get(path: Path): SourceFileLike | undefined;
}
export function createSourceFileLikeCache(host: { readFile?: (path: string) => string | undefined, fileExists?: (path: string) => boolean }): SourceFileLikeCache {
const cached = createMap<SourceFileLike>();
return {
get(path: Path) {
if (cached.has(path)) {
return cached.get(path);
}
if (!host.fileExists || !host.readFile || !host.fileExists(path)) return;
// And failing that, check the disk
const text = host.readFile(path)!; // TODO: GH#18217
const file = {
text,
lineMap: undefined,
getLineAndCharacterOfPosition(pos: number) {
return computeLineAndCharacterOfPosition(getLineStarts(this), pos);
}
} as SourceFileLike;
cached.set(path, file);
return file;
}
};
}
}
/* @internal */
namespace ts.sourcemaps {
export interface SourceMapData {
version?: number;
file?: string;
sourceRoot?: string;
sources: string[];
sourcesContent?: (string | null)[];
names?: string[];
mappings: string;
}
export interface SourceMappableLocation {
fileName: string;
position: number;
}
export interface SourceMapper {
getOriginalPosition(input: SourceMappableLocation): SourceMappableLocation;
getGeneratedPosition(input: SourceMappableLocation): SourceMappableLocation;
}
export const identitySourceMapper = { getOriginalPosition: identity, getGeneratedPosition: identity };
export interface SourceMapDecodeHost {
readFile(path: string): string | undefined;
fileExists(path: string): boolean;
getCanonicalFileName(path: string): string;
log(text: string): void;
useCaseSensitiveFileNames: boolean;
}
export function decode(host: SourceMapDecodeHost, mapPath: string, map: SourceMapData, program?: Program, fallbackCache = createSourceFileLikeCache(host)): SourceMapper {
const currentDirectory = getDirectoryPath(mapPath);
const sourceRoot = map.sourceRoot ? getNormalizedAbsolutePath(map.sourceRoot, currentDirectory) : currentDirectory;
let decodedMappings: ProcessedSourceMapPosition[];
let generatedOrderedMappings: ProcessedSourceMapPosition[];
let sourceOrderedMappings: ProcessedSourceMapPosition[];
return {
getOriginalPosition,
getGeneratedPosition
};
function getGeneratedPosition(loc: SourceMappableLocation): SourceMappableLocation {
const maps = getSourceOrderedMappings();
if (!length(maps)) return loc;
let targetIndex = binarySearch(maps, { sourcePath: loc.fileName, sourcePosition: loc.position }, identity, compareProcessedPositionSourcePositions);
if (targetIndex < 0 && maps.length > 0) {
// if no exact match, closest is 2's compliment of result
targetIndex = ~targetIndex;
}
if (!maps[targetIndex] || comparePaths(loc.fileName, maps[targetIndex].sourcePath, sourceRoot, !host.useCaseSensitiveFileNames) !== 0) {
return loc;
}
return { fileName: getNormalizedAbsolutePath(map.file!, sourceRoot), position: maps[targetIndex].emittedPosition }; // Closest pos
}
function getOriginalPosition(loc: SourceMappableLocation): SourceMappableLocation {
const maps = getGeneratedOrderedMappings();
if (!length(maps)) return loc;
let targetIndex = binarySearch(maps, { emittedPosition: loc.position }, identity, compareProcessedPositionEmittedPositions);
if (targetIndex < 0 && maps.length > 0) {
// if no exact match, closest is 2's compliment of result
targetIndex = ~targetIndex;
}
return { fileName: getNormalizedAbsolutePath(maps[targetIndex].sourcePath, sourceRoot), position: maps[targetIndex].sourcePosition }; // Closest pos
}
function getSourceFileLike(fileName: string, location: string): SourceFileLike | undefined {
// Lookup file in program, if provided
const path = toPath(fileName, location, host.getCanonicalFileName);
const file = program && program.getSourceFileByPath(path);
// file returned here could be .d.ts when asked for .ts file if projectReferences and module resolution created this source file
if (!file || file.resolvedPath !== path) {
// Otherwise check the cache (which may hit disk)
return fallbackCache.get(path);
}
return file;
}
function getPositionOfLineAndCharacterUsingName(fileName: string, directory: string, line: number, character: number) {
const file = getSourceFileLike(fileName, directory);
if (!file) {
return -1;
}
return getPositionOfLineAndCharacter(file, line, character);
}
function getDecodedMappings() {
return decodedMappings || (decodedMappings = calculateDecodedMappings(map, processPosition, host));
}
function getSourceOrderedMappings() {
return sourceOrderedMappings || (sourceOrderedMappings = getDecodedMappings().slice().sort(compareProcessedPositionSourcePositions));
}
function getGeneratedOrderedMappings() {
return generatedOrderedMappings || (generatedOrderedMappings = getDecodedMappings().slice().sort(compareProcessedPositionEmittedPositions));
}
function compareProcessedPositionSourcePositions(a: ProcessedSourceMapPosition, b: ProcessedSourceMapPosition) {
return comparePaths(a.sourcePath, b.sourcePath, sourceRoot, !host.useCaseSensitiveFileNames) ||
compareValues(a.sourcePosition, b.sourcePosition);
}
function compareProcessedPositionEmittedPositions(a: ProcessedSourceMapPosition, b: ProcessedSourceMapPosition) {
return compareValues(a.emittedPosition, b.emittedPosition);
}
function processPosition(position: RawSourceMapPosition): ProcessedSourceMapPosition {
const sourcePath = map.sources[position.sourceIndex];
return {
emittedPosition: getPositionOfLineAndCharacterUsingName(map.file!, currentDirectory, position.emittedLine, position.emittedColumn),
sourcePosition: getPositionOfLineAndCharacterUsingName(sourcePath, sourceRoot, position.sourceLine, position.sourceColumn),
sourcePath,
// TODO: Consider using `name` field to remap the expected identifier to scan for renames to handle another tool renaming oout output
// name: position.nameIndex ? map.names[position.nameIndex] : undefined
};
}
}
export interface MappingsDecoder extends Iterator<SourceMapSpan> {
readonly decodingIndex: number;
readonly error: string | undefined;
readonly lastSpan: SourceMapSpan;
}
export function decodeMappings(map: SourceMapData): MappingsDecoder {
const state: DecoderState = {
encodedText: map.mappings,
currentNameIndex: undefined,
sourceMapNamesLength: map.names ? map.names.length : undefined,
currentEmittedColumn: 0,
currentEmittedLine: 0,
currentSourceColumn: 0,
currentSourceLine: 0,
currentSourceIndex: 0,
decodingIndex: 0
};
function captureSpan(): SourceMapSpan {
return {
emittedColumn: state.currentEmittedColumn,
emittedLine: state.currentEmittedLine,
sourceColumn: state.currentSourceColumn,
sourceIndex: state.currentSourceIndex,
sourceLine: state.currentSourceLine,
nameIndex: state.currentNameIndex
};
}
return {
get decodingIndex() { return state.decodingIndex; },
get error() { return state.error; },
get lastSpan() { return captureSpan(); },
next() {
if (hasCompletedDecoding(state) || state.error) return { done: true, value: undefined as never };
if (!decodeSinglePosition(state)) return { done: true, value: undefined as never };
return { done: false, value: captureSpan() };
}
};
}
function calculateDecodedMappings<T>(map: SourceMapData, processPosition: (position: RawSourceMapPosition) => T, host?: { log?(s: string): void }): T[] {
const decoder = decodeMappings(map);
const positions = arrayFrom(decoder, processPosition);
if (decoder.error) {
if (host && host.log) {
host.log(`Encountered error while decoding sourcemap: ${decoder.error}`);
}
return [];
}
return positions;
}
interface ProcessedSourceMapPosition {
emittedPosition: number;
sourcePosition: number;
sourcePath: string;
}
interface RawSourceMapPosition {
emittedLine: number;
emittedColumn: number;
sourceLine: number;
sourceColumn: number;
sourceIndex: number;
nameIndex?: number;
}
interface DecoderState {
decodingIndex: number;
currentEmittedLine: number;
currentEmittedColumn: number;
currentSourceLine: number;
currentSourceColumn: number;
currentSourceIndex: number;
currentNameIndex: number | undefined;
encodedText: string;
sourceMapNamesLength?: number;
error?: string;
}
function hasCompletedDecoding(state: DecoderState) {
return state.decodingIndex === state.encodedText.length;
}
function decodeSinglePosition(state: DecoderState): boolean {
while (state.decodingIndex < state.encodedText.length) {
const char = state.encodedText.charCodeAt(state.decodingIndex);
if (char === CharacterCodes.semicolon) {
// New line
state.currentEmittedLine++;
state.currentEmittedColumn = 0;
state.decodingIndex++;
continue;
}
if (char === CharacterCodes.comma) {
// Next entry is on same line - no action needed
state.decodingIndex++;
continue;
}
// Read the current position
// 1. Column offset from prev read jsColumn
state.currentEmittedColumn += base64VLQFormatDecode();
// Incorrect emittedColumn dont support this map
if (createErrorIfCondition(state.currentEmittedColumn < 0, "Invalid emittedColumn found")) {
return false;
}
// Dont support reading mappings that dont have information about original source and its line numbers
if (createErrorIfCondition(isSourceMappingSegmentEnd(state.encodedText, state.decodingIndex), "Unsupported Error Format: No entries after emitted column")) {
return false;
}
// 2. Relative sourceIndex
state.currentSourceIndex += base64VLQFormatDecode();
// Incorrect sourceIndex dont support this map
if (createErrorIfCondition(state.currentSourceIndex < 0, "Invalid sourceIndex found")) {
return false;
}
// Dont support reading mappings that dont have information about original source position
if (createErrorIfCondition(isSourceMappingSegmentEnd(state.encodedText, state.decodingIndex), "Unsupported Error Format: No entries after sourceIndex")) {
return false;
}
// 3. Relative sourceLine 0 based
state.currentSourceLine += base64VLQFormatDecode();
// Incorrect sourceLine dont support this map
if (createErrorIfCondition(state.currentSourceLine < 0, "Invalid sourceLine found")) {
return false;
}
// Dont support reading mappings that dont have information about original source and its line numbers
if (createErrorIfCondition(isSourceMappingSegmentEnd(state.encodedText, state.decodingIndex), "Unsupported Error Format: No entries after emitted Line")) {
return false;
}
// 4. Relative sourceColumn 0 based
state.currentSourceColumn += base64VLQFormatDecode();
// Incorrect sourceColumn dont support this map
if (createErrorIfCondition(state.currentSourceColumn < 0, "Invalid sourceLine found")) {
return false;
}
// 5. Check if there is name:
if (!isSourceMappingSegmentEnd(state.encodedText, state.decodingIndex)) {
if (state.currentNameIndex === undefined) {
state.currentNameIndex = 0;
}
state.currentNameIndex += base64VLQFormatDecode();
// Incorrect nameIndex dont support this map
// TODO: If we start using `name`s, issue errors when they aren't correct in the sourcemap
// if (createErrorIfCondition(state.currentNameIndex < 0 || state.currentNameIndex >= state.sourceMapNamesLength, "Invalid name index for the source map entry")) {
// return;
// }
}
// Dont support reading mappings that dont have information about original source and its line numbers
if (createErrorIfCondition(!isSourceMappingSegmentEnd(state.encodedText, state.decodingIndex), "Unsupported Error Format: There are more entries after " + (state.currentNameIndex === undefined ? "sourceColumn" : "nameIndex"))) {
return false;
}
// Entry should be complete
return true;
}
createErrorIfCondition(/*condition*/ true, "No encoded entry found");
return false;
function createErrorIfCondition(condition: boolean, errormsg: string) {
if (state.error) {
// An error was already reported
return true;
}
if (condition) {
state.error = errormsg;
}
return condition;
}
function base64VLQFormatDecode(): number {
let moreDigits = true;
let shiftCount = 0;
let value = 0;
for (; moreDigits; state.decodingIndex++) {
if (createErrorIfCondition(state.decodingIndex >= state.encodedText.length, "Error in decoding base64VLQFormatDecode, past the mapping string")) {
return undefined!; // TODO: GH#18217
}
// 6 digit number
const currentByte = base64FormatDecode(state.encodedText.charAt(state.decodingIndex));
// If msb is set, we still have more bits to continue
moreDigits = (currentByte & 32) !== 0;
// least significant 5 bits are the next msbs in the final value.
value = value | ((currentByte & 31) << shiftCount);
shiftCount += 5;
}
// Least significant bit if 1 represents negative and rest of the msb is actual absolute value
if ((value & 1) === 0) {
// + number
value = value >> 1;
}
else {
// - number
value = value >> 1;
value = -value;
}
return value;
}
}
function base64FormatDecode(char: string) {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(char);
}
function isSourceMappingSegmentEnd(encodedText: string, pos: number) {
return (pos === encodedText.length ||
encodedText.charCodeAt(pos) === CharacterCodes.comma ||
encodedText.charCodeAt(pos) === CharacterCodes.semicolon);
}
}

View file

@ -68,6 +68,14 @@ namespace ts {
return transformers;
}
export function noEmitSubstitution(_hint: EmitHint, node: Node) {
return node;
}
export function noEmitNotification(hint: EmitHint, node: Node, callback: (hint: EmitHint, node: Node) => void) {
callback(hint, node);
}
/**
* Transforms an array of SourceFiles by passing them through each transformer.
*
@ -87,8 +95,8 @@ namespace ts {
let lexicalEnvironmentStackOffset = 0;
let lexicalEnvironmentSuspended = false;
let emitHelpers: EmitHelper[] | undefined;
let onSubstituteNode: TransformationContext["onSubstituteNode"] = (_, node) => node;
let onEmitNode: TransformationContext["onEmitNode"] = (hint, node, callback) => callback(hint, node);
let onSubstituteNode: TransformationContext["onSubstituteNode"] = noEmitSubstitution;
let onEmitNode: TransformationContext["onEmitNode"] = noEmitNotification;
let state = TransformationState.Uninitialized;
const diagnostics: DiagnosticWithLocation[] = [];

View file

@ -25,7 +25,7 @@
"checker.ts",
"factory.ts",
"visitor.ts",
"sourcemapDecoder.ts",
"sourcemap.ts",
"transformers/utilities.ts",
"transformers/destructuring.ts",
"transformers/ts.ts",
@ -42,8 +42,6 @@
"transformers/declarations/diagnostics.ts",
"transformers/declarations.ts",
"transformer.ts",
"sourcemap.ts",
"comments.ts",
"emitter.ts",
"watchUtilities.ts",
"program.ts",

View file

@ -2963,16 +2963,10 @@ namespace ts {
sourceIndex: number;
}
export interface SourceMapData {
sourceMapFilePath: string; // Where the sourcemap file is written
jsSourceMappingURL: string; // source map URL written in the .js file
sourceMapFile: string; // Source map's file field - .js file name
sourceMapSourceRoot: string; // Source map's sourceRoot field - location where the sources will be present if not ""
sourceMapSources: string[]; // Source map's sources field - list of sources that can be indexed in this source map
sourceMapSourcesContent?: (string | null)[]; // Source map's sourcesContent field - list of the sources' text to be embedded in the source map
inputSourceFileNames: string[]; // Input source file (which one can use on program to get the file), 1:1 mapping with the sourceMapSources list
sourceMapNames?: string[]; // Source map's names field - list of names that can be indexed in this source map
sourceMapMappings: string; // Source map's mapping field - encoded source map spans
/* @internal */
export interface SourceMapEmitResult {
inputSourceFileNames: ReadonlyArray<string>; // Input source file (which one can use on program to get the file), 1:1 mapping with the sourceMap.sources list
sourceMap: RawSourceMap;
}
/** Return code used by getEmitOutput function to indicate status of the function */
@ -2994,7 +2988,7 @@ namespace ts {
/** Contains declaration emit diagnostics */
diagnostics: ReadonlyArray<Diagnostic>;
emittedFiles?: string[]; // Array of files the compiler wrote to disk
/* @internal */ sourceMaps?: SourceMapData[]; // Array of sourceMapData if compiler emitted sourcemaps
/* @internal */ sourceMaps?: SourceMapEmitResult[]; // Array of sourceMapData if compiler emitted sourcemaps
/* @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit;
}
@ -5217,6 +5211,7 @@ namespace ts {
IdentifierName, // Emitting an IdentifierName
MappedTypeParameter, // Emitting a TypeParameterDeclaration inside of a MappedTypeNode
Unspecified, // Emitting an otherwise unspecified node
EmbeddedStatement, // Emitting an embedded statement
}
/* @internal */
@ -5386,8 +5381,8 @@ namespace ts {
printBundle(bundle: Bundle): string;
/*@internal*/ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void;
/*@internal*/ writeList<T extends Node>(format: ListFormat, list: NodeArray<T> | undefined, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void;
/*@internal*/ writeFile(sourceFile: SourceFile, writer: EmitTextWriter): void;
/*@internal*/ writeBundle(bundle: Bundle, writer: EmitTextWriter, info?: BundleInfo): void;
/*@internal*/ writeFile(sourceFile: SourceFile, writer: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined): void;
/*@internal*/ writeBundle(bundle: Bundle, info: BundleInfo | undefined, writer: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined): void;
}
/**
@ -5467,15 +5462,90 @@ namespace ts {
/*@internal*/ target?: CompilerOptions["target"];
/*@internal*/ sourceMap?: boolean;
/*@internal*/ inlineSourceMap?: boolean;
/*@internal*/ inlineSources?: boolean;
/*@internal*/ extendedDiagnostics?: boolean;
/*@internal*/ onlyPrintJsDocStyle?: boolean;
/*@internal*/ neverAsciiEscape?: boolean;
}
/* @internal */
export interface RawSourceMap {
version: 3;
file: string;
sourceRoot?: string | null;
sources: string[];
sourcesContent?: (string | null)[] | null;
mappings: string;
names?: string[] | null;
}
/**
* Generates a source map.
*/
/* @internal */
export interface SourceMapGenerator {
getSources(): ReadonlyArray<string>;
/**
* Adds a source to the source map.
*/
addSource(fileName: string): number;
/**
* Set the content for a source.
*/
setSourceContent(sourceIndex: number, content: string | null): void;
/**
* Adds a name.
*/
addName(name: string): number;
/**
* Adds a mapping without source information.
*/
addMapping(generatedLine: number, generatedCharacter: number): void;
/**
* Adds a mapping with source information.
*/
addMapping(generatedLine: number, generatedCharacter: number, sourceIndex: number, sourceLine: number, sourceCharacter: number, nameIndex?: number): void;
/**
* Appends a source map.
*/
appendSourceMap(generatedLine: number, generatedCharacter: number, sourceMap: RawSourceMap, sourceMapPath: string): void;
/**
* Gets the source map as a `RawSourceMap` object.
*/
toJSON(): RawSourceMap;
/**
* Gets the string representation of the source map.
*/
toString(): string;
}
/* @internal */
export interface DocumentPositionMapperHost {
getSourceFileLike(path: Path): SourceFileLike | undefined;
getCanonicalFileName(path: string): string;
log?(text: string): void;
}
/**
* Maps positions between source and generated files.
*/
/* @internal */
export interface DocumentPositionMapper {
getSourcePosition(input: DocumentPosition): DocumentPosition;
getGeneratedPosition(input: DocumentPosition): DocumentPosition;
}
/* @internal */
export interface DocumentPosition {
fileName: string;
pos: number;
}
/* @internal */
export interface EmitTextWriter extends SymbolWriter {
write(s: string): void;
writeTextOfNode(text: string, node: Node): void;
writeTrailingSemicolon(text: string): void;
writeComment(text: string): void;
getText(): string;
rawWrite(s: string): void;
writeLiteral(s: string): void;

View file

@ -64,7 +64,6 @@ namespace ts {
getText: () => str,
write: writeText,
rawWrite: writeText,
writeTextOfNode: writeText,
writeKeyword: writeText,
writeOperator: writeText,
writePunctuation: writeText,
@ -73,7 +72,9 @@ namespace ts {
writeLiteral: writeText,
writeParameter: writeText,
writeProperty: writeText,
writeSymbol: writeText,
writeSymbol: (s, _) => writeText(s),
writeTrailingSemicolon: writeText,
writeComment: writeText,
getTextPos: () => str.length,
getLine: () => 0,
getColumn: () => 0,
@ -3201,18 +3202,11 @@ namespace ts {
}
}
function writeTextOfNode(text: string, node: Node) {
const s = getTextOfNodeFromSourceText(text, node);
write(s);
updateLineCountAndPosFor(s);
}
reset();
return {
write,
rawWrite,
writeTextOfNode,
writeLiteral,
writeLine,
increaseIndent: () => { indent++; },
@ -3235,7 +3229,79 @@ namespace ts {
writePunctuation: write,
writeSpace: write,
writeStringLiteral: write,
writeSymbol: write
writeSymbol: (s, _) => write(s),
writeTrailingSemicolon: write,
writeComment: write
};
}
export function getTrailingSemicolonOmittingWriter(writer: EmitTextWriter): EmitTextWriter {
let pendingTrailingSemicolon = false;
function commitPendingTrailingSemicolon() {
if (pendingTrailingSemicolon) {
writer.writeTrailingSemicolon(";");
pendingTrailingSemicolon = false;
}
}
return {
...writer,
writeTrailingSemicolon() {
pendingTrailingSemicolon = true;
},
writeLiteral(s) {
commitPendingTrailingSemicolon();
writer.writeLiteral(s);
},
writeStringLiteral(s) {
commitPendingTrailingSemicolon();
writer.writeStringLiteral(s);
},
writeSymbol(s, sym) {
commitPendingTrailingSemicolon();
writer.writeSymbol(s, sym);
},
writePunctuation(s) {
commitPendingTrailingSemicolon();
writer.writePunctuation(s);
},
writeKeyword(s) {
commitPendingTrailingSemicolon();
writer.writeKeyword(s);
},
writeOperator(s) {
commitPendingTrailingSemicolon();
writer.writeOperator(s);
},
writeParameter(s) {
commitPendingTrailingSemicolon();
writer.writeParameter(s);
},
writeSpace(s) {
commitPendingTrailingSemicolon();
writer.writeSpace(s);
},
writeProperty(s) {
commitPendingTrailingSemicolon();
writer.writeProperty(s);
},
writeComment(s) {
commitPendingTrailingSemicolon();
writer.writeComment(s);
},
writeLine() {
commitPendingTrailingSemicolon();
writer.writeLine();
},
increaseIndent() {
commitPendingTrailingSemicolon();
writer.increaseIndent();
},
decreaseIndent() {
commitPendingTrailingSemicolon();
writer.decreaseIndent();
}
};
}
@ -3515,13 +3581,13 @@ namespace ts {
writeComment: (text: string, lineMap: ReadonlyArray<number>, writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) => void) {
if (comments && comments.length > 0) {
if (leadingSeparator) {
writer.write(" ");
writer.writeSpace(" ");
}
let emitInterveningSeparator = false;
for (const comment of comments) {
if (emitInterveningSeparator) {
writer.write(" ");
writer.writeSpace(" ");
emitInterveningSeparator = false;
}
@ -3535,7 +3601,7 @@ namespace ts {
}
if (emitInterveningSeparator && trailingSeparator) {
writer.write(" ");
writer.writeSpace(" ");
}
}
}
@ -3669,7 +3735,7 @@ namespace ts {
}
else {
// Single line comment of style //....
writer.write(text.substring(commentPos, commentEnd));
writer.writeComment(text.substring(commentPos, commentEnd));
}
}
@ -3678,14 +3744,14 @@ namespace ts {
const currentLineText = text.substring(pos, end).replace(/^\s+|\s+$/g, "");
if (currentLineText) {
// trimmed forward and ending spaces text
writer.write(currentLineText);
writer.writeComment(currentLineText);
if (end !== commentEnd) {
writer.writeLine();
}
}
else {
// Empty string - make sure we write empty line
writer.writeLiteral(newLine);
writer.rawWrite(newLine);
}
}

View file

@ -1646,6 +1646,7 @@ namespace Harness {
else {
sourceMapCode = "";
result.maps.forEach(sourceMap => {
if (sourceMapCode) sourceMapCode += "\r\n";
sourceMapCode += fileOutput(sourceMap, harnessSettings);
});
}
@ -1671,6 +1672,9 @@ namespace Harness {
let jsCode = "";
result.js.forEach(file => {
if (jsCode.length && jsCode.charCodeAt(jsCode.length - 1) !== ts.CharacterCodes.lineFeed) {
jsCode += "\r\n";
}
jsCode += fileOutput(file, harnessSettings);
});

View file

@ -1,44 +1,36 @@
namespace Harness.SourceMapRecorder {
interface SourceMapSpanWithDecodeErrors {
sourceMapSpan: ts.SourceMapSpan;
sourceMapSpan: ts.Mapping;
decodeErrors: string[] | undefined;
}
namespace SourceMapDecoder {
let sourceMapMappings: string;
let decodingIndex: number;
let mappings: ts.sourcemaps.MappingsDecoder | undefined;
let mappings: ts.MappingsDecoder | undefined;
export interface DecodedMapping {
sourceMapSpan: ts.SourceMapSpan;
sourceMapSpan: ts.Mapping;
error?: string;
}
export function initializeSourceMapDecoding(sourceMapData: ts.SourceMapData) {
export function initializeSourceMapDecoding(sourceMapData: ts.SourceMapEmitResult) {
decodingIndex = 0;
sourceMapMappings = sourceMapData.sourceMapMappings;
mappings = ts.sourcemaps.decodeMappings({
version: 3,
file: sourceMapData.sourceMapFile,
sources: sourceMapData.sourceMapSources,
sourceRoot: sourceMapData.sourceMapSourceRoot,
sourcesContent: sourceMapData.sourceMapSourcesContent,
mappings: sourceMapData.sourceMapMappings,
names: sourceMapData.sourceMapNames
});
sourceMapMappings = sourceMapData.sourceMap.mappings;
mappings = ts.decodeMappings(sourceMapData.sourceMap.mappings);
}
export function decodeNextEncodedSourceMapSpan(): DecodedMapping {
if (!mappings) return ts.Debug.fail("not initialized");
const result = mappings.next();
if (result.done) return { error: mappings.error || "No encoded entry found", sourceMapSpan: mappings.lastSpan };
if (result.done) return { error: mappings.error || "No encoded entry found", sourceMapSpan: mappings.state };
return { sourceMapSpan: result.value };
}
export function hasCompletedDecoding() {
if (!mappings) return ts.Debug.fail("not initialized");
return mappings.decodingIndex === sourceMapMappings.length;
return mappings.pos === sourceMapMappings.length;
}
export function getRemainingDecodeString() {
@ -49,7 +41,7 @@ namespace Harness.SourceMapRecorder {
namespace SourceMapSpanWriter {
let sourceMapRecorder: Compiler.WriterAggregator;
let sourceMapSources: string[];
let sourceMapNames: string[] | undefined;
let sourceMapNames: string[] | null | undefined;
let jsFile: documents.TextDocument;
let jsLineMap: ReadonlyArray<number>;
@ -61,10 +53,10 @@ namespace Harness.SourceMapRecorder {
let nextJsLineToWrite: number;
let spanMarkerContinues: boolean;
export function initializeSourceMapSpanWriter(sourceMapRecordWriter: Compiler.WriterAggregator, sourceMapData: ts.SourceMapData, currentJsFile: documents.TextDocument) {
export function initializeSourceMapSpanWriter(sourceMapRecordWriter: Compiler.WriterAggregator, sourceMapData: ts.SourceMapEmitResult, currentJsFile: documents.TextDocument) {
sourceMapRecorder = sourceMapRecordWriter;
sourceMapSources = sourceMapData.sourceMapSources;
sourceMapNames = sourceMapData.sourceMapNames;
sourceMapSources = sourceMapData.sourceMap.sources;
sourceMapNames = sourceMapData.sourceMap.names;
jsFile = currentJsFile;
jsLineMap = jsFile.lineStarts;
@ -75,43 +67,39 @@ namespace Harness.SourceMapRecorder {
spanMarkerContinues = false;
SourceMapDecoder.initializeSourceMapDecoding(sourceMapData);
sourceMapRecorder.WriteLine("===================================================================");
sourceMapRecorder.WriteLine("JsFile: " + sourceMapData.sourceMapFile);
sourceMapRecorder.WriteLine("mapUrl: " + sourceMapData.jsSourceMappingURL);
sourceMapRecorder.WriteLine("sourceRoot: " + sourceMapData.sourceMapSourceRoot);
sourceMapRecorder.WriteLine("sources: " + sourceMapData.sourceMapSources);
if (sourceMapData.sourceMapSourcesContent) {
sourceMapRecorder.WriteLine("sourcesContent: " + JSON.stringify(sourceMapData.sourceMapSourcesContent));
sourceMapRecorder.WriteLine("JsFile: " + sourceMapData.sourceMap.file);
sourceMapRecorder.WriteLine("mapUrl: " + ts.tryGetSourceMappingURL(jsFile.text, jsLineMap));
sourceMapRecorder.WriteLine("sourceRoot: " + sourceMapData.sourceMap.sourceRoot);
sourceMapRecorder.WriteLine("sources: " + sourceMapData.sourceMap.sources);
if (sourceMapData.sourceMap.sourcesContent) {
sourceMapRecorder.WriteLine("sourcesContent: " + JSON.stringify(sourceMapData.sourceMap.sourcesContent));
}
sourceMapRecorder.WriteLine("===================================================================");
}
function getSourceMapSpanString(mapEntry: ts.SourceMapSpan, getAbsentNameIndex?: boolean) {
let mapString = "Emitted(" + (mapEntry.emittedLine + 1) + ", " + (mapEntry.emittedColumn + 1) + ") Source(" + (mapEntry.sourceLine + 1) + ", " + (mapEntry.sourceColumn + 1) + ") + SourceIndex(" + mapEntry.sourceIndex + ")";
if (mapEntry.nameIndex! >= 0 && mapEntry.nameIndex! < sourceMapNames!.length) {
mapString += " name (" + sourceMapNames![mapEntry.nameIndex!] + ")";
}
else {
if ((mapEntry.nameIndex && mapEntry.nameIndex !== -1) || getAbsentNameIndex) {
mapString += " nameIndex (" + mapEntry.nameIndex + ")";
function getSourceMapSpanString(mapEntry: ts.Mapping, getAbsentNameIndex?: boolean) {
let mapString = "Emitted(" + (mapEntry.generatedLine + 1) + ", " + (mapEntry.generatedCharacter + 1) + ")";
if (ts.isSourceMapping(mapEntry)) {
mapString += " Source(" + (mapEntry.sourceLine + 1) + ", " + (mapEntry.sourceCharacter + 1) + ") + SourceIndex(" + mapEntry.sourceIndex + ")";
if (mapEntry.nameIndex! >= 0 && mapEntry.nameIndex! < sourceMapNames!.length) {
mapString += " name (" + sourceMapNames![mapEntry.nameIndex!] + ")";
}
else {
if ((mapEntry.nameIndex && mapEntry.nameIndex !== -1) || getAbsentNameIndex) {
mapString += " nameIndex (" + mapEntry.nameIndex + ")";
}
}
}
return mapString;
}
export function recordSourceMapSpan(sourceMapSpan: ts.SourceMapSpan) {
export function recordSourceMapSpan(sourceMapSpan: ts.Mapping) {
// verify the decoded span is same as the new span
const decodeResult = SourceMapDecoder.decodeNextEncodedSourceMapSpan();
let decodeErrors: string[] | undefined;
if (typeof decodeResult.error === "string"
|| decodeResult.sourceMapSpan.emittedLine !== sourceMapSpan.emittedLine
|| decodeResult.sourceMapSpan.emittedColumn !== sourceMapSpan.emittedColumn
|| decodeResult.sourceMapSpan.sourceLine !== sourceMapSpan.sourceLine
|| decodeResult.sourceMapSpan.sourceColumn !== sourceMapSpan.sourceColumn
|| decodeResult.sourceMapSpan.sourceIndex !== sourceMapSpan.sourceIndex
|| decodeResult.sourceMapSpan.nameIndex !== sourceMapSpan.nameIndex) {
if (typeof decodeResult.error === "string" || !ts.sameMapping(decodeResult.sourceMapSpan, sourceMapSpan)) {
if (decodeResult.error) {
decodeErrors = ["!!^^ !!^^ There was decoding error in the sourcemap at this location: " + decodeResult.error];
}
@ -121,7 +109,7 @@ namespace Harness.SourceMapRecorder {
decodeErrors.push("!!^^ !!^^ Decoded span from sourcemap's mappings entry: " + getSourceMapSpanString(decodeResult.sourceMapSpan, /*getAbsentNameIndex*/ true) + " Span encoded by the emitter:" + getSourceMapSpanString(sourceMapSpan, /*getAbsentNameIndex*/ true));
}
if (spansOnSingleLine.length && spansOnSingleLine[0].sourceMapSpan.emittedLine !== sourceMapSpan.emittedLine) {
if (spansOnSingleLine.length && spansOnSingleLine[0].sourceMapSpan.generatedLine !== sourceMapSpan.generatedLine) {
// On different line from the one that we have been recording till now,
writeRecordedSpans();
spansOnSingleLine = [];
@ -129,14 +117,21 @@ namespace Harness.SourceMapRecorder {
spansOnSingleLine.push({ sourceMapSpan, decodeErrors });
}
export function recordNewSourceFileSpan(sourceMapSpan: ts.SourceMapSpan, newSourceFileCode: string) {
assert.isTrue(spansOnSingleLine.length === 0 || spansOnSingleLine[0].sourceMapSpan.emittedLine !== sourceMapSpan.emittedLine, "new file source map span should be on new line. We currently handle only that scenario");
export function recordNewSourceFileSpan(sourceMapSpan: ts.Mapping, newSourceFileCode: string) {
let continuesLine = false;
if (spansOnSingleLine.length > 0 && spansOnSingleLine[0].sourceMapSpan.generatedCharacter === sourceMapSpan.generatedLine) {
writeRecordedSpans();
spansOnSingleLine = [];
nextJsLineToWrite--; // walk back one line to reprint the line
continuesLine = true;
}
recordSourceMapSpan(sourceMapSpan);
assert.isTrue(spansOnSingleLine.length === 1);
sourceMapRecorder.WriteLine("-------------------------------------------------------------------");
sourceMapRecorder.WriteLine("emittedFile:" + jsFile.file);
sourceMapRecorder.WriteLine("sourceFile:" + sourceMapSources[spansOnSingleLine[0].sourceMapSpan.sourceIndex]);
sourceMapRecorder.WriteLine("emittedFile:" + jsFile.file + (continuesLine ? ` (${sourceMapSpan.generatedLine + 1}, ${sourceMapSpan.generatedCharacter + 1})` : ""));
sourceMapRecorder.WriteLine("sourceFile:" + sourceMapSources[spansOnSingleLine[0].sourceMapSpan.sourceIndex!]);
sourceMapRecorder.WriteLine("-------------------------------------------------------------------");
tsLineMap = ts.computeLineStarts(newSourceFileCode);
@ -195,7 +190,7 @@ namespace Harness.SourceMapRecorder {
prevEmittedCol = 0;
for (let i = 0; i < spansOnSingleLine.length; i++) {
fn(spansOnSingleLine[i], i);
prevEmittedCol = spansOnSingleLine[i].sourceMapSpan.emittedColumn;
prevEmittedCol = spansOnSingleLine[i].sourceMapSpan.generatedCharacter;
}
}
@ -206,7 +201,7 @@ namespace Harness.SourceMapRecorder {
}
}
function writeSourceMapMarker(currentSpan: SourceMapSpanWithDecodeErrors, index: number, endColumn = currentSpan.sourceMapSpan.emittedColumn, endContinues = false) {
function writeSourceMapMarker(currentSpan: SourceMapSpanWithDecodeErrors, index: number, endColumn = currentSpan.sourceMapSpan.generatedCharacter, endContinues = false) {
const markerId = getMarkerId(index);
markerIds.push(markerId);
@ -223,7 +218,7 @@ namespace Harness.SourceMapRecorder {
}
function writeSourceMapSourceText(currentSpan: SourceMapSpanWithDecodeErrors, index: number) {
const sourcePos = tsLineMap[currentSpan.sourceMapSpan.sourceLine] + (currentSpan.sourceMapSpan.sourceColumn);
const sourcePos = tsLineMap[currentSpan.sourceMapSpan.sourceLine!] + (currentSpan.sourceMapSpan.sourceCharacter!);
let sourceText = "";
if (prevWrittenSourcePos < sourcePos) {
// Position that goes forward, get text
@ -255,7 +250,7 @@ namespace Harness.SourceMapRecorder {
}
if (spansOnSingleLine.length) {
const currentJsLine = spansOnSingleLine[0].sourceMapSpan.emittedLine;
const currentJsLine = spansOnSingleLine[0].sourceMapSpan.generatedLine;
// Write js line
writeJsFileLines(currentJsLine + 1);
@ -280,14 +275,14 @@ namespace Harness.SourceMapRecorder {
}
}
export function getSourceMapRecord(sourceMapDataList: ReadonlyArray<ts.SourceMapData>, program: ts.Program, jsFiles: ReadonlyArray<documents.TextDocument>, declarationFiles: ReadonlyArray<documents.TextDocument>) {
export function getSourceMapRecord(sourceMapDataList: ReadonlyArray<ts.SourceMapEmitResult>, program: ts.Program, jsFiles: ReadonlyArray<documents.TextDocument>, declarationFiles: ReadonlyArray<documents.TextDocument>) {
const sourceMapRecorder = new Compiler.WriterAggregator();
for (let i = 0; i < sourceMapDataList.length; i++) {
const sourceMapData = sourceMapDataList[i];
let prevSourceFile: ts.SourceFile | undefined;
let currentFile: documents.TextDocument;
if (ts.endsWith(sourceMapData.sourceMapFile, ts.Extension.Dts)) {
if (ts.endsWith(sourceMapData.sourceMap.file, ts.Extension.Dts)) {
if (sourceMapDataList.length > jsFiles.length) {
currentFile = declarationFiles[Math.floor(i / 2)]; // When both kinds of source map are present, they alternate js/dts
}
@ -305,11 +300,15 @@ namespace Harness.SourceMapRecorder {
}
SourceMapSpanWriter.initializeSourceMapSpanWriter(sourceMapRecorder, sourceMapData, currentFile);
const mapper = ts.sourcemaps.decodeMappings({ mappings: sourceMapData.sourceMapMappings, sources: sourceMapData.sourceMapSources });
const mapper = ts.decodeMappings(sourceMapData.sourceMap.mappings);
for (let { value: decodedSourceMapping, done } = mapper.next(); !done; { value: decodedSourceMapping, done } = mapper.next()) {
const currentSourceFile = program.getSourceFile(sourceMapData.inputSourceFileNames[decodedSourceMapping.sourceIndex])!;
const currentSourceFile = ts.isSourceMapping(decodedSourceMapping)
? program.getSourceFile(sourceMapData.inputSourceFileNames[decodedSourceMapping.sourceIndex])
: undefined;
if (currentSourceFile !== prevSourceFile) {
SourceMapSpanWriter.recordNewSourceFileSpan(decodedSourceMapping, currentSourceFile.text);
if (currentSourceFile) {
SourceMapSpanWriter.recordNewSourceFileSpan(decodedSourceMapping, currentSourceFile.text);
}
prevSourceFile = currentSourceFile;
}
else {

View file

@ -2367,8 +2367,8 @@ namespace ts.server {
}
/*@internal*/
getOriginalLocationEnsuringConfiguredProject(project: Project, location: sourcemaps.SourceMappableLocation): sourcemaps.SourceMappableLocation | undefined {
const originalLocation = project.getSourceMapper().tryGetOriginalLocation(location);
getOriginalLocationEnsuringConfiguredProject(project: Project, location: DocumentPosition): DocumentPosition | undefined {
const originalLocation = project.getSourceMapper().tryGetSourcePosition(location);
if (!originalLocation) return undefined;
const { fileName } = originalLocation;

View file

@ -290,7 +290,7 @@ namespace ts.server {
projects: Projects,
defaultProject: Project,
action: (project: Project) => ReadonlyArray<T>,
getLocation: (t: T) => sourcemaps.SourceMappableLocation,
getLocation: (t: T) => DocumentPosition,
resultsEqual: (a: T, b: T) => boolean,
): T[] {
const outputs: T[] = [];
@ -312,18 +312,18 @@ namespace ts.server {
function combineProjectOutputForRenameLocations(
projects: Projects,
defaultProject: Project,
initialLocation: sourcemaps.SourceMappableLocation,
initialLocation: DocumentPosition,
findInStrings: boolean,
findInComments: boolean
): ReadonlyArray<RenameLocation> {
const outputs: RenameLocation[] = [];
combineProjectOutputWorker<sourcemaps.SourceMappableLocation>(
combineProjectOutputWorker<DocumentPosition>(
projects,
defaultProject,
initialLocation,
({ project, location }, tryAddToTodo) => {
for (const output of project.getLanguageService().findRenameLocations(location.fileName, location.position, findInStrings, findInComments) || emptyArray) {
for (const output of project.getLanguageService().findRenameLocations(location.fileName, location.pos, findInStrings, findInComments) || emptyArray) {
if (!contains(outputs, output, documentSpansEqual) && !tryAddToTodo(project, documentSpanLocation(output))) {
outputs.push(output);
}
@ -335,29 +335,29 @@ namespace ts.server {
return outputs;
}
function getDefinitionLocation(defaultProject: Project, initialLocation: sourcemaps.SourceMappableLocation): sourcemaps.SourceMappableLocation | undefined {
const infos = defaultProject.getLanguageService().getDefinitionAtPosition(initialLocation.fileName, initialLocation.position);
function getDefinitionLocation(defaultProject: Project, initialLocation: DocumentPosition): DocumentPosition | undefined {
const infos = defaultProject.getLanguageService().getDefinitionAtPosition(initialLocation.fileName, initialLocation.pos);
const info = infos && firstOrUndefined(infos);
return info && { fileName: info.fileName, position: info.textSpan.start };
return info && { fileName: info.fileName, pos: info.textSpan.start };
}
function combineProjectOutputForReferences(
projects: Projects,
defaultProject: Project,
initialLocation: sourcemaps.SourceMappableLocation
initialLocation: DocumentPosition
): ReadonlyArray<ReferencedSymbol> {
const outputs: ReferencedSymbol[] = [];
combineProjectOutputWorker<sourcemaps.SourceMappableLocation>(
combineProjectOutputWorker<DocumentPosition>(
projects,
defaultProject,
initialLocation,
({ project, location }, getMappedLocation) => {
for (const outputReferencedSymbol of project.getLanguageService().findReferences(location.fileName, location.position) || emptyArray) {
for (const outputReferencedSymbol of project.getLanguageService().findReferences(location.fileName, location.pos) || emptyArray) {
const mappedDefinitionFile = getMappedLocation(project, documentSpanLocation(outputReferencedSymbol.definition));
const definition: ReferencedSymbolDefinitionInfo = mappedDefinitionFile === undefined ? outputReferencedSymbol.definition : {
...outputReferencedSymbol.definition,
textSpan: createTextSpan(mappedDefinitionFile.position, outputReferencedSymbol.definition.textSpan.length),
textSpan: createTextSpan(mappedDefinitionFile.pos, outputReferencedSymbol.definition.textSpan.length),
fileName: mappedDefinitionFile.fileName,
};
let symbolToAddTo = find(outputs, o => documentSpansEqual(o.definition, definition));
@ -380,7 +380,7 @@ namespace ts.server {
return outputs.filter(o => o.references.length !== 0);
}
interface ProjectAndLocation<TLocation extends sourcemaps.SourceMappableLocation | undefined> {
interface ProjectAndLocation<TLocation extends DocumentPosition | undefined> {
readonly project: Project;
readonly location: TLocation;
}
@ -398,24 +398,24 @@ namespace ts.server {
}
}
type CombineProjectOutputCallback<TLocation extends sourcemaps.SourceMappableLocation | undefined> = (
type CombineProjectOutputCallback<TLocation extends DocumentPosition | undefined> = (
where: ProjectAndLocation<TLocation>,
getMappedLocation: (project: Project, location: sourcemaps.SourceMappableLocation) => sourcemaps.SourceMappableLocation | undefined,
getMappedLocation: (project: Project, location: DocumentPosition) => DocumentPosition | undefined,
) => void;
function combineProjectOutputWorker<TLocation extends sourcemaps.SourceMappableLocation | undefined>(
function combineProjectOutputWorker<TLocation extends DocumentPosition | undefined>(
projects: Projects,
defaultProject: Project,
initialLocation: TLocation,
cb: CombineProjectOutputCallback<TLocation>,
getDefinition: (() => sourcemaps.SourceMappableLocation | undefined) | undefined,
getDefinition: (() => DocumentPosition | undefined) | undefined,
): void {
const projectService = defaultProject.projectService;
let toDo: ProjectAndLocation<TLocation>[] | undefined;
const seenProjects = createMap<true>();
forEachProjectInProjects(projects, initialLocation && initialLocation.fileName, (project, path) => {
// TLocation shoud be either `sourcemaps.SourceMappableLocation` or `undefined`. Since `initialLocation` is `TLocation` this cast should be valid.
const location = (initialLocation ? { fileName: path, position: initialLocation.position } : undefined) as TLocation;
// TLocation shoud be either `DocumentPosition` or `undefined`. Since `initialLocation` is `TLocation` this cast should be valid.
const location = (initialLocation ? { fileName: path, pos: initialLocation.pos } : undefined) as TLocation;
toDo = callbackProjectAndLocation({ project, location }, projectService, toDo, seenProjects, cb);
});
@ -436,13 +436,13 @@ namespace ts.server {
}
}
function getDefinitionInProject(definition: sourcemaps.SourceMappableLocation | undefined, definingProject: Project, project: Project): sourcemaps.SourceMappableLocation | undefined {
function getDefinitionInProject(definition: DocumentPosition | undefined, definingProject: Project, project: Project): DocumentPosition | undefined {
if (!definition || project.containsFile(toNormalizedPath(definition.fileName))) return definition;
const mappedDefinition = definingProject.getLanguageService().getSourceMapper().tryGetGeneratedLocation(definition);
const mappedDefinition = definingProject.getLanguageService().getSourceMapper().tryGetGeneratedPosition(definition);
return mappedDefinition && project.containsFile(toNormalizedPath(mappedDefinition.fileName)) ? mappedDefinition : undefined;
}
function callbackProjectAndLocation<TLocation extends sourcemaps.SourceMappableLocation | undefined>(
function callbackProjectAndLocation<TLocation extends DocumentPosition | undefined>(
projectAndLocation: ProjectAndLocation<TLocation>,
projectService: ProjectService,
toDo: ProjectAndLocation<TLocation>[] | undefined,
@ -472,16 +472,16 @@ namespace ts.server {
return toDo;
}
function addToTodo<TLocation extends sourcemaps.SourceMappableLocation | undefined>(projectAndLocation: ProjectAndLocation<TLocation>, toDo: Push<ProjectAndLocation<TLocation>>, seenProjects: Map<true>): void {
function addToTodo<TLocation extends DocumentPosition | undefined>(projectAndLocation: ProjectAndLocation<TLocation>, toDo: Push<ProjectAndLocation<TLocation>>, seenProjects: Map<true>): void {
if (addToSeen(seenProjects, projectAndLocation.project.projectName)) toDo.push(projectAndLocation);
}
function documentSpanLocation({ fileName, textSpan }: DocumentSpan): sourcemaps.SourceMappableLocation {
return { fileName, position: textSpan.start };
function documentSpanLocation({ fileName, textSpan }: DocumentSpan): DocumentPosition {
return { fileName, pos: textSpan.start };
}
function getMappedLocation(location: sourcemaps.SourceMappableLocation, projectService: ProjectService, project: Project): sourcemaps.SourceMappableLocation | undefined {
const mapsTo = project.getSourceMapper().tryGetOriginalLocation(location);
function getMappedLocation(location: DocumentPosition, projectService: ProjectService, project: Project): DocumentPosition | undefined {
const mapsTo = project.getSourceMapper().tryGetSourcePosition(location);
return mapsTo && projectService.fileExists(toNormalizedPath(mapsTo.fileName)) ? mapsTo : undefined;
}
@ -941,7 +941,7 @@ namespace ts.server {
kind: info.kind,
name: info.name,
textSpan: {
start: newLoc.position,
start: newLoc.pos,
length: info.textSpan.length
},
originalFileName: info.fileName,
@ -1038,7 +1038,7 @@ namespace ts.server {
kind: info.kind,
displayParts: info.displayParts,
textSpan: {
start: newLoc.position,
start: newLoc.pos,
length: info.textSpan.length
},
originalFileName: info.fileName,
@ -1229,7 +1229,7 @@ namespace ts.server {
const locations = combineProjectOutputForRenameLocations(
projects,
this.getDefaultProject(args),
{ fileName: args.file, position },
{ fileName: args.file, pos: position },
!!args.findInStrings,
!!args.findInComments
);
@ -1269,7 +1269,7 @@ namespace ts.server {
const references = combineProjectOutputForReferences(
projects,
this.getDefaultProject(args),
{ fileName: args.file, position },
{ fileName: args.file, pos: position },
);
if (simplifiedResult) {

View file

@ -25,7 +25,7 @@ namespace ts {
export function getPathUpdater(oldFileOrDirPath: string, newFileOrDirPath: string, getCanonicalFileName: GetCanonicalFileName, sourceMapper: SourceMapper | undefined): PathUpdater {
const canonicalOldPath = getCanonicalFileName(oldFileOrDirPath);
return path => {
const originalPath = sourceMapper && sourceMapper.tryGetOriginalLocation({ fileName: path, position: 0 });
const originalPath = sourceMapper && sourceMapper.tryGetSourcePosition({ fileName: path, pos: 0 });
const updatedPath = getUpdatedPath(originalPath ? originalPath.fileName : path);
return originalPath
? updatedPath === undefined ? undefined : makeCorrespondingRelativeChange(originalPath.fileName, updatedPath, path, getCanonicalFileName)

View file

@ -1,14 +1,11 @@
/* @internal */
namespace ts {
// Sometimes tools can sometimes see the following line as a source mapping url comment, so we mangle it a bit (the [M])
const sourceMapCommentRegExp = /^\/\/[@#] source[M]appingURL=(.+)\s*$/;
const whitespaceOrMapCommentRegExp = /^\s*(\/\/[@#] .*)?$/;
const base64UrlRegExp = /^data:(?:application\/json(?:;charset=[uU][tT][fF]-8);base64,([A-Za-z0-9+\/=]+)$)?/;
export interface SourceMapper {
toLineColumnOffset(fileName: string, position: number): LineAndCharacter;
tryGetOriginalLocation(info: sourcemaps.SourceMappableLocation): sourcemaps.SourceMappableLocation | undefined;
tryGetGeneratedLocation(info: sourcemaps.SourceMappableLocation): sourcemaps.SourceMappableLocation | undefined;
tryGetSourcePosition(info: DocumentPosition): DocumentPosition | undefined;
tryGetGeneratedPosition(info: DocumentPosition): DocumentPosition | undefined;
clearCache(): void;
}
@ -21,7 +18,7 @@ namespace ts {
): SourceMapper {
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
let sourcemappedFileCache: SourceFileLikeCache;
return { tryGetOriginalLocation, tryGetGeneratedLocation, toLineColumnOffset, clearCache };
return { tryGetSourcePosition, tryGetGeneratedPosition, toLineColumnOffset, clearCache };
function toPath(fileName: string) {
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
@ -32,44 +29,36 @@ namespace ts {
if (!mappedFile) {
return;
}
const starts = getLineStarts(mappedFile);
for (let index = starts.length - 1; index >= 0; index--) {
const lineText = mappedFile.text.substring(starts[index], starts[index + 1]);
const comment = sourceMapCommentRegExp.exec(lineText);
if (comment) {
return comment[1];
}
// If we see a non-whitespace/map comment-like line, break, to avoid scanning up the entire file
else if (!lineText.match(whitespaceOrMapCommentRegExp)) {
break;
}
}
return tryGetSourceMappingURL(mappedFile.text, getLineStarts(mappedFile));
}
function convertDocumentToSourceMapper(file: { sourceMapper?: sourcemaps.SourceMapper }, contents: string, mapFileName: string) {
let maps: sourcemaps.SourceMapData | undefined;
try {
maps = JSON.parse(contents);
}
catch {
// swallow error
}
if (!maps || !maps.sources || !maps.file || !maps.mappings) {
function convertDocumentToSourceMapper(file: { sourceMapper?: DocumentPositionMapper }, contents: string, mapFileName: string) {
const map = tryParseRawSourceMap(contents);
if (!map || !map.sources || !map.file || !map.mappings) {
// obviously invalid map
return file.sourceMapper = sourcemaps.identitySourceMapper;
return file.sourceMapper = identitySourceMapConsumer;
}
return file.sourceMapper = sourcemaps.decode({
readFile: s => host.readFile!(s), // TODO: GH#18217
fileExists: s => host.fileExists!(s), // TODO: GH#18217
useCaseSensitiveFileNames,
const program = getProgram();
return file.sourceMapper = createDocumentPositionMapper({
getSourceFileLike: s => {
// Lookup file in program, if provided
const file = program && program.getSourceFileByPath(s);
// file returned here could be .d.ts when asked for .ts file if projectReferences and module resolution created this source file
if (file === undefined || file.resolvedPath !== s) {
// Otherwise check the cache (which may hit disk)
return sourcemappedFileCache.get(s);
}
return file;
},
getCanonicalFileName,
log,
}, mapFileName, maps, getProgram(), sourcemappedFileCache);
}, map, mapFileName);
}
function getSourceMapper(fileName: string, file: SourceFileLike): sourcemaps.SourceMapper {
function getSourceMapper(fileName: string, file: SourceFileLike): DocumentPositionMapper {
if (!host.readFile || !host.fileExists) {
return file.sourceMapper = sourcemaps.identitySourceMapper;
return file.sourceMapper = identitySourceMapConsumer;
}
if (file.sourceMapper) {
return file.sourceMapper;
@ -97,19 +86,19 @@ namespace ts {
return convertDocumentToSourceMapper(file, host.readFile(mapPath)!, mapPath); // TODO: GH#18217
}
}
return file.sourceMapper = sourcemaps.identitySourceMapper;
return file.sourceMapper = identitySourceMapConsumer;
}
function tryGetOriginalLocation(info: sourcemaps.SourceMappableLocation): sourcemaps.SourceMappableLocation | undefined {
function tryGetSourcePosition(info: DocumentPosition): DocumentPosition | undefined {
if (!isDeclarationFileName(info.fileName)) return undefined;
const file = getFile(info.fileName);
if (!file) return undefined;
const newLoc = getSourceMapper(info.fileName, file).getOriginalPosition(info);
return newLoc === info ? undefined : tryGetOriginalLocation(newLoc) || newLoc;
const newLoc = getSourceMapper(info.fileName, file).getSourcePosition(info);
return newLoc === info ? undefined : tryGetSourcePosition(newLoc) || newLoc;
}
function tryGetGeneratedLocation(info: sourcemaps.SourceMappableLocation): sourcemaps.SourceMappableLocation | undefined {
function tryGetGeneratedPosition(info: DocumentPosition): DocumentPosition | undefined {
const program = getProgram();
const options = program.getCompilerOptions();
const outPath = options.outFile || options.out;
@ -141,4 +130,31 @@ namespace ts {
sourcemappedFileCache = createSourceFileLikeCache(host);
}
}
interface SourceFileLikeCache {
get(path: Path): SourceFileLike | undefined;
}
function createSourceFileLikeCache(host: { readFile?: (path: string) => string | undefined, fileExists?: (path: string) => boolean }): SourceFileLikeCache {
const cached = createMap<SourceFileLike>();
return {
get(path: Path) {
if (cached.has(path)) {
return cached.get(path);
}
if (!host.fileExists || !host.readFile || !host.fileExists(path)) return;
// And failing that, check the disk
const text = host.readFile(path)!; // TODO: GH#18217
const file = {
text,
lineMap: undefined,
getLineAndCharacterOfPosition(pos: number) {
return computeLineAndCharacterOfPosition(getLineStarts(this), pos);
}
} as SourceFileLike;
cached.set(path, file);
return file;
}
};
}
}

View file

@ -925,6 +925,9 @@ namespace ts.textChanges {
this.writer.write(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
}
writeComment(s: string): void {
this.writer.writeComment(s);
}
writeKeyword(s: string): void {
this.writer.writeKeyword(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
@ -937,6 +940,10 @@ namespace ts.textChanges {
this.writer.writePunctuation(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
}
writeTrailingSemicolon(s: string): void {
this.writer.writeTrailingSemicolon(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
}
writeParameter(s: string): void {
this.writer.writeParameter(s);
this.setLastNonTriviaPosition(s, /*force*/ false);
@ -957,9 +964,6 @@ namespace ts.textChanges {
this.writer.writeSymbol(s, sym);
this.setLastNonTriviaPosition(s, /*force*/ false);
}
writeTextOfNode(text: string, node: Node): void {
this.writer.writeTextOfNode(text, node);
}
writeLine(): void {
this.writer.writeLine();
}

View file

@ -86,12 +86,12 @@ namespace ts {
getPositionOfLineAndCharacter(line: number, character: number): number;
update(newText: string, textChangeRange: TextChangeRange): SourceFile;
/* @internal */ sourceMapper?: sourcemaps.SourceMapper;
/* @internal */ sourceMapper?: DocumentPositionMapper;
}
export interface SourceFileLike {
getLineAndCharacterOfPosition(pos: number): LineAndCharacter;
/*@internal*/ sourceMapper?: sourcemaps.SourceMapper;
/*@internal*/ sourceMapper?: DocumentPositionMapper;
}
export interface SourceMapSource {

View file

@ -1459,6 +1459,7 @@ namespace ts {
writeKeyword: text => writeKind(text, SymbolDisplayPartKind.keyword),
writeOperator: text => writeKind(text, SymbolDisplayPartKind.operator),
writePunctuation: text => writeKind(text, SymbolDisplayPartKind.punctuation),
writeTrailingSemicolon: text => writeKind(text, SymbolDisplayPartKind.punctuation),
writeSpace: text => writeKind(text, SymbolDisplayPartKind.space),
writeStringLiteral: text => writeKind(text, SymbolDisplayPartKind.stringLiteral),
writeParameter: text => writeKind(text, SymbolDisplayPartKind.parameterName),
@ -1467,7 +1468,7 @@ namespace ts {
writeSymbol,
writeLine,
write: unknownWrite,
writeTextOfNode: unknownWrite,
writeComment: unknownWrite,
getText: () => "",
getTextPos: () => 0,
getColumn: () => 0,

View file

@ -23,7 +23,7 @@ namespace project {
program?: ts.Program;
compilerOptions?: ts.CompilerOptions;
errors: ReadonlyArray<ts.Diagnostic>;
sourceMapData?: ReadonlyArray<ts.SourceMapData>;
sourceMapData?: ReadonlyArray<ts.SourceMapEmitResult>;
}
interface BatchCompileProjectTestCaseResult extends CompileProjectFilesResult {
@ -315,11 +315,11 @@ namespace project {
// Clean up source map data that will be used in baselining
if (sourceMapData) {
for (const data of sourceMapData) {
for (let j = 0; j < data.sourceMapSources.length; j++) {
data.sourceMapSources[j] = this.cleanProjectUrl(data.sourceMapSources[j]);
}
data.jsSourceMappingURL = this.cleanProjectUrl(data.jsSourceMappingURL);
data.sourceMapSourceRoot = this.cleanProjectUrl(data.sourceMapSourceRoot);
data.sourceMap = {
...data.sourceMap,
sources: data.sourceMap.sources.map(source => this.cleanProjectUrl(source)),
sourceRoot: data.sourceMap.sourceRoot && this.cleanProjectUrl(data.sourceMap.sourceRoot)
};
}
}
@ -425,6 +425,7 @@ namespace project {
skipDefaultLibCheck: false,
moduleResolution: ts.ModuleResolutionKind.Classic,
module: moduleKind,
newLine: ts.NewLineKind.CarriageReturnLineFeed,
mapRoot: testCase.resolveMapRoot && testCase.mapRoot
? vpath.resolve(vfs.srcFolder, testCase.mapRoot)
: testCase.mapRoot,

View file

@ -18,7 +18,7 @@ namespace ts {
writeFile: (fileName, text) => outputs.set(fileName, text),
};
const program = createProgram(arrayFrom(fileMap.keys()), options, host);
const program = createProgram(arrayFrom(fileMap.keys()), { newLine: NewLineKind.LineFeed, ...options }, host);
program.emit(/*targetSourceFile*/ undefined, host.writeFile, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ false, customTransformers);
let content = "";
for (const [file, text] of arrayFrom(outputs.entries())) {

View file

@ -10058,7 +10058,7 @@ declare class TestLib {
const configContent = JSON.stringify({ compilerOptions });
const aTsconfig: File = { path: "/a/tsconfig.json", content: configContent };
const aDtsMapContent: SourceMapSection = {
const aDtsMapContent: RawSourceMap = {
version: 3,
file: "a.d.ts",
sourceRoot: "",
@ -10082,7 +10082,7 @@ declare class TestLib {
};
const bTsconfig: File = { path: "/b/tsconfig.json", content: configContent };
const bDtsMapContent: SourceMapSection = {
const bDtsMapContent: RawSourceMap = {
version: 3,
file: "b.d.ts",
sourceRoot: "",

View file

@ -1849,17 +1849,6 @@ declare namespace ts {
/** .ts file (index into sources array) associated with this span */
sourceIndex: number;
}
interface SourceMapData {
sourceMapFilePath: string;
jsSourceMappingURL: string;
sourceMapFile: string;
sourceMapSourceRoot: string;
sourceMapSources: string[];
sourceMapSourcesContent?: (string | null)[];
inputSourceFileNames: string[];
sourceMapNames?: string[];
sourceMapMappings: string;
}
/** Return code used by getEmitOutput function to indicate status of the function */
enum ExitStatus {
Success = 0,
@ -2764,7 +2753,8 @@ declare namespace ts {
Expression = 1,
IdentifierName = 2,
MappedTypeParameter = 3,
Unspecified = 4
Unspecified = 4,
EmbeddedStatement = 5
}
interface TransformationContext {
/** Gets the compiler options supplied to the transformer. */

View file

@ -1849,17 +1849,6 @@ declare namespace ts {
/** .ts file (index into sources array) associated with this span */
sourceIndex: number;
}
interface SourceMapData {
sourceMapFilePath: string;
jsSourceMappingURL: string;
sourceMapFile: string;
sourceMapSourceRoot: string;
sourceMapSources: string[];
sourceMapSourcesContent?: (string | null)[];
inputSourceFileNames: string[];
sourceMapNames?: string[];
sourceMapMappings: string;
}
/** Return code used by getEmitOutput function to indicate status of the function */
enum ExitStatus {
Success = 0,
@ -2764,7 +2753,8 @@ declare namespace ts {
Expression = 1,
IdentifierName = 2,
MappedTypeParameter = 3,
Unspecified = 4
Unspecified = 4,
EmbeddedStatement = 5
}
interface TransformationContext {
/** Gets the compiler options supplied to the transformer. */

View file

@ -1,3 +1,4 @@
//// [a.d.ts.map]
{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["a.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,OAAO,CAAC,CAAC,EAAE;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd"}//// [index.d.ts.map]
{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["a.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,OAAO,CAAC,CAAC,EAAE;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd"}
//// [index.d.ts.map]
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,eAAO,IAAI,CAAC;;CAAqB,CAAC;AAClC,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC"}

View file

@ -1,3 +1,4 @@
//// [bundle.js.map]
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IAAA;IAOA,CAAC;IANG,qBAAO,GAAP,UAAQ,CAAc;QAClB,OAAO,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,CAAC;IACpB,CAAC;IACM,QAAI,GAAX;QACI,OAAO,IAAI,GAAG,EAAE,CAAC;IACrB,CAAC;IACL,UAAC;AAAD,CAAC,AAPD,IAOC;ACPD,IAAM,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;AACpB,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC;AAEnB,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC"}//// [bundle.d.ts.map]
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA;IAAA;IAOA,CAAC;IANG,qBAAO,GAAP,UAAQ,CAAc;QAClB,OAAO,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,CAAC;IACpB,CAAC;IACM,QAAI,GAAX;QACI,OAAO,IAAI,GAAG,EAAE,CAAC;IACrB,CAAC;IACL,UAAC;AAAD,CAAC,AAPD,IAOC;ACPD,IAAM,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;AACpB,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC;AAEnB,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAC"}
//// [bundle.d.ts.map]
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["tests/cases/compiler/a.ts","tests/cases/compiler/index.ts"],"names":[],"mappings":"AAAA,cAAM,GAAG;IACL,OAAO,CAAC,GAAG;QAAC,CAAC,EAAE,MAAM,CAAA;KAAC;;;IAGtB,MAAM,CAAC,IAAI;CAGd;ACPD,QAAA,MAAM,CAAC,KAAY,CAAC;AAGpB,QAAA,IAAI,CAAC;;CAAqB,CAAC"}

View file

@ -68,7 +68,8 @@ exports.createElement = Element.createElement;
function toCamelCase(text) {
return text[0].toLowerCase() + text.substring(1);
}
//# sourceMappingURL=Element.js.map//// [test.js]
//# sourceMappingURL=Element.js.map
//// [test.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Element_1 = require("./Element");

View file

@ -1,3 +1,4 @@
//// [Element.js.map]
{"version":3,"file":"Element.js","sourceRoot":"","sources":["Element.ts"],"names":[],"mappings":";;AAYA,IAAiB,OAAO,CAUvB;AAVD,WAAiB,OAAO;IACpB,SAAgB,SAAS,CAAC,EAAO;QAC7B,OAAO,EAAE,CAAC,wBAAwB,KAAK,SAAS,CAAC;IACrD,CAAC;IAFe,iBAAS,YAExB,CAAA;IAED,SAAgB,aAAa,CAAC,IAAW;QAErC,OAAO,EACN,CAAA;IACL,CAAC;IAJe,qBAAa,gBAI5B,CAAA;AACL,CAAC,EAVgB,OAAO,GAAP,eAAO,KAAP,eAAO,QAUvB;AAEU,QAAA,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAEjD,SAAS,WAAW,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"}//// [test.js.map]
{"version":3,"file":"Element.js","sourceRoot":"","sources":["Element.ts"],"names":[],"mappings":";;AAYA,IAAiB,OAAO,CAUvB;AAVD,WAAiB,OAAO;IACpB,SAAgB,SAAS,CAAC,EAAO;QAC7B,OAAO,EAAE,CAAC,wBAAwB,KAAK,SAAS,CAAC;IACrD,CAAC;IAFe,iBAAS,YAExB,CAAA;IAED,SAAgB,aAAa,CAAC,IAAW;QAErC,OAAO,EACN,CAAA;IACL,CAAC;IAJe,qBAAa,gBAI5B,CAAA;AACL,CAAC,EAVgB,OAAO,GAAP,eAAO,KAAP,eAAO,QAUvB;AAEU,QAAA,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAEjD,SAAS,WAAW,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"}
//// [test.js.map]
{"version":3,"file":"test.js","sourceRoot":"","sources":["test.tsx"],"names":[],"mappings":";;AAAA,uCAAmC;AACnC,IAAI,aAAa,GAAG,iBAAO,CAAC,aAAa,CAAC;AAC1C,IAAI,CAIH,CAAC;AAEF,MAAM,CAAC;IACN,IAAI;QACH,OAAO;YACN,wBAAM,OAAO,EAAC,YAAY,GAAQ;YAClC,wBAAM,OAAO,EAAE,CAAC,CAAC,CAAE,CAAC,CAAC,GAAS;SAC9B,CAAC;IACH,CAAC;CACD"}

View file

@ -67,7 +67,8 @@ exports.createElement = Element.createElement;
function toCamelCase(text) {
return text[0].toLowerCase() + text.substring(1);
}
//# sourceMappingURL=Element.js.map//// [test.js]
//# sourceMappingURL=Element.js.map
//// [test.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Element_1 = require("./Element");

View file

@ -1,3 +1,4 @@
//// [Element.js.map]
{"version":3,"file":"Element.js","sourceRoot":"","sources":["Element.ts"],"names":[],"mappings":";;AAYA,IAAiB,OAAO,CAUvB;AAVD,WAAiB,OAAO;IACpB,SAAgB,SAAS,CAAC,EAAO;QAC7B,OAAO,EAAE,CAAC,wBAAwB,KAAK,SAAS,CAAC;IACrD,CAAC;IAFe,iBAAS,YAExB,CAAA;IAED,SAAgB,aAAa,CAAC,IAAW;QAErC,OAAO,EACN,CAAA;IACL,CAAC;IAJe,qBAAa,gBAI5B,CAAA;AACL,CAAC,EAVgB,OAAO,GAAP,eAAO,KAAP,eAAO,QAUvB;AAEU,QAAA,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAEjD,SAAS,WAAW,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"}//// [test.js.map]
{"version":3,"file":"Element.js","sourceRoot":"","sources":["Element.ts"],"names":[],"mappings":";;AAYA,IAAiB,OAAO,CAUvB;AAVD,WAAiB,OAAO;IACpB,SAAgB,SAAS,CAAC,EAAO;QAC7B,OAAO,EAAE,CAAC,wBAAwB,KAAK,SAAS,CAAC;IACrD,CAAC;IAFe,iBAAS,YAExB,CAAA;IAED,SAAgB,aAAa,CAAC,IAAW;QAErC,OAAO,EACN,CAAA;IACL,CAAC;IAJe,qBAAa,gBAI5B,CAAA;AACL,CAAC,EAVgB,OAAO,GAAP,eAAO,KAAP,eAAO,QAUvB;AAEU,QAAA,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;AAEjD,SAAS,WAAW,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"}
//// [test.js.map]
{"version":3,"file":"test.js","sourceRoot":"","sources":["test.tsx"],"names":[],"mappings":";;AAAA,uCAAmC;AAEnC,IAAI,CAIH,CAAC;AAEF,MAAM,CAAC;IACN,IAAI;QACH,OAAO;YACN,0CAAM,OAAO,EAAC,YAAY,GAAQ;YAClC,0CAAM,OAAO,EAAE,CAAC,CAAC,CAAE,CAAC,CAAC,GAAS;SAC9B,CAAC;IACH,CAAC;CACD"}

View file

@ -18,7 +18,8 @@ var c = /** @class */ (function () {
}
return c;
}());
//# sourceMappingURL=app.js.map//// [app2.js]
//# sourceMappingURL=app.js.map
//// [app2.js]
var d = /** @class */ (function () {
function d() {
}

View file

@ -1,3 +1,4 @@
//// [app.js.map]
{"version":3,"file":"app.js","sourceRoot":"","sources":["../testFiles/app.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,wIAAwI;AACxI;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"}//// [app2.js.map]
{"version":3,"file":"app.js","sourceRoot":"","sources":["../testFiles/app.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,wIAAwI;AACxI;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"}
//// [app2.js.map]
{"version":3,"file":"app2.js","sourceRoot":"","sources":["../testFiles/app2.ts"],"names":[],"mappings":"AAAA;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"}

View file

@ -18,7 +18,8 @@ var c = /** @class */ (function () {
}
return c;
}());
//# sourceMappingURL=app.js.map//// [app2.js]
//# sourceMappingURL=app.js.map
//// [app2.js]
var d = /** @class */ (function () {
function d() {
}

View file

@ -1,3 +1,4 @@
//// [app.js.map]
{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,0GAA0G;AAC1G;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"}//// [app2.js.map]
{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,0GAA0G;AAC1G;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"}
//// [app2.js.map]
{"version":3,"file":"app2.js","sourceRoot":"","sources":["app2.ts"],"names":[],"mappings":"AAAA;IAAA;IACA,CAAC;IAAD,QAAC;AAAD,CAAC,AADD,IACC"}