mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-14 07:02:44 -05:00
Added source maps to printer
This commit is contained in:
@@ -52,6 +52,7 @@ var compilerSources = [
|
||||
"transforms/jsx.ts",
|
||||
"transforms/es6.ts",
|
||||
"declarationEmitter.ts",
|
||||
"sourcemap.ts",
|
||||
"printer.ts",
|
||||
"emitter.ts",
|
||||
"program.ts",
|
||||
@@ -82,6 +83,7 @@ var servicesSources = [
|
||||
"transforms/jsx.ts",
|
||||
"transforms/es6.ts",
|
||||
"declarationEmitter.ts",
|
||||
"sourcemap.ts",
|
||||
"printer.ts",
|
||||
"emitter.ts",
|
||||
"program.ts",
|
||||
|
||||
@@ -436,6 +436,22 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function reduceProperties<T, U>(map: Map<T>, f: (a: U, v: T, k: string) => U, initial: U): U {
|
||||
let result = initial;
|
||||
if (map) {
|
||||
for (let key in map) {
|
||||
if (hasProperty(map, key)) {
|
||||
result = f(result, map[key], String(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isArray(value: any): value is any[] {
|
||||
return Array.isArray ? Array.isArray(value) : Object.prototype.toString.call(value) === "[object Array]";
|
||||
}
|
||||
|
||||
export function memoize<T>(callback: () => T): () => T {
|
||||
let value: T;
|
||||
return () => {
|
||||
|
||||
@@ -7603,9 +7603,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
||||
|
||||
function emitFile(jsFilePath: string, sourceFile?: SourceFile) {
|
||||
if (compilerOptions.experimentalTransforms) {
|
||||
let writer = createTextWriter(host.getNewLine());
|
||||
let nodes = sourceFile ? [sourceFile] : host.getSourceFiles();
|
||||
let text = printFiles(resolver, host, filter(nodes, isNonDeclarationFile), getTransformationChain(compilerOptions));
|
||||
writeFile(host, diagnostics, jsFilePath, text, compilerOptions.emitBOM);
|
||||
let transformationChain = getTransformationChain(compilerOptions);
|
||||
let sourceMap = compilerOptions.sourceMap || compilerOptions.inlineSourceMap
|
||||
? createSourceMapWriter(host, writer, jsFilePath, sourceFile)
|
||||
: getNullSourceMapWriter();
|
||||
|
||||
printFiles(resolver, host, writer, filter(nodes, isNonDeclarationFile), transformationChain, sourceMap);
|
||||
|
||||
if (compilerOptions.sourceMap) {
|
||||
writeFile(host, diagnostics, jsFilePath + ".map", sourceMap.getText(), /*writeByteOrderMark*/ false);
|
||||
}
|
||||
|
||||
writeFile(host, diagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM);
|
||||
}
|
||||
else {
|
||||
emitJavaScript(jsFilePath, sourceFile);
|
||||
@@ -7616,4 +7627,76 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature
|
||||
// @internal
|
||||
export function emitFilesUsingTransforms(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult {
|
||||
let compilerOptions = host.getCompilerOptions();
|
||||
let sourceMapDataList: SourceMapData[] = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? [] : undefined;
|
||||
let diagnostics: Diagnostic[] = [];
|
||||
let jsxDesugaring = host.getCompilerOptions().jsx !== JsxEmit.Preserve;
|
||||
let shouldEmitJsx = (s: SourceFile) => (s.languageVariant === LanguageVariant.JSX && !jsxDesugaring);
|
||||
|
||||
if (targetSourceFile === undefined) {
|
||||
forEach(host.getSourceFiles(), sourceFile => {
|
||||
if (shouldEmitToOwnFile(sourceFile, compilerOptions)) {
|
||||
let jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, shouldEmitJsx(sourceFile) ? ".jsx" : ".js");
|
||||
printFile(jsFilePath, sourceFile);
|
||||
}
|
||||
});
|
||||
|
||||
if (compilerOptions.outFile || compilerOptions.out) {
|
||||
printFile(compilerOptions.outFile || compilerOptions.out);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// targetSourceFile is specified (e.g calling emitter from language service or calling getSemanticDiagnostic from language service)
|
||||
if (shouldEmitToOwnFile(targetSourceFile, compilerOptions)) {
|
||||
let jsFilePath = getOwnEmitOutputFilePath(targetSourceFile, host, shouldEmitJsx(targetSourceFile) ? ".jsx" : ".js");
|
||||
printFile(jsFilePath, targetSourceFile);
|
||||
}
|
||||
else if (!isDeclarationFile(targetSourceFile) && (compilerOptions.outFile || compilerOptions.out)) {
|
||||
printFile(compilerOptions.outFile || compilerOptions.out);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and make the unique list of diagnostics
|
||||
diagnostics = sortAndDeduplicateDiagnostics(diagnostics);
|
||||
|
||||
return {
|
||||
emitSkipped: false,
|
||||
diagnostics,
|
||||
sourceMaps: sourceMapDataList
|
||||
};
|
||||
|
||||
function printFile(jsFilePath: string, sourceFile?: SourceFile) {
|
||||
let writer = createTextWriter(host.getNewLine());
|
||||
let nodes = sourceFile ? [sourceFile] : host.getSourceFiles();
|
||||
let transformationChain = getTransformationChain(compilerOptions);
|
||||
let sourceMap = compilerOptions.sourceMap || compilerOptions.inlineSourceMap
|
||||
? createSourceMapWriter(host, writer, jsFilePath, sourceFile)
|
||||
: getNullSourceMapWriter();
|
||||
|
||||
// Print each file to the writer after executing the transformation chain.
|
||||
printFiles(resolver, host, writer, filter(nodes, isNonDeclarationFile), transformationChain, sourceMap);
|
||||
|
||||
// Write the output file.
|
||||
writeFile(host, diagnostics, jsFilePath, writer.getText(), compilerOptions.emitBOM);
|
||||
|
||||
// If source maps were requested, write the source map.
|
||||
if (compilerOptions.sourceMap) {
|
||||
writeFile(host, diagnostics, jsFilePath + ".map", sourceMap.getText(), /*writeByteOrderMark*/ false);
|
||||
}
|
||||
|
||||
// If declarations were requested, write the declaration file.
|
||||
if (compilerOptions.declaration) {
|
||||
writeDeclarationFile(jsFilePath, sourceFile, host, resolver, diagnostics);
|
||||
}
|
||||
|
||||
// Record source map data for the test harness.
|
||||
if (sourceMapDataList) {
|
||||
sourceMapDataList.push(sourceMap.sourceMapData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1556,10 +1556,16 @@ namespace ts {
|
||||
export function isSourceFile(node: Node): node is SourceFile {
|
||||
return node && node.kind === SyntaxKind.SourceFile;
|
||||
}
|
||||
export function isFunctionBody(node: Node): node is FunctionBody {
|
||||
export function isLiteralExpression(node: Node): node is LiteralExpression {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Block:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.RegularExpressionLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.TemplateHead:
|
||||
case SyntaxKind.TemplateMiddle:
|
||||
case SyntaxKind.TemplateTail:
|
||||
case SyntaxKind.StringLiteral:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1815,6 +1821,15 @@ namespace ts {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function isFunctionBody(node: Node): node is FunctionBody {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Block:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function isUnaryExpression(node: Node): node is UnaryExpression {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
@@ -1856,21 +1871,6 @@ namespace ts {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function isLiteralExpression(node: Node): node is LiteralExpression {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.RegularExpressionLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.TemplateHead:
|
||||
case SyntaxKind.TemplateMiddle:
|
||||
case SyntaxKind.TemplateTail:
|
||||
case SyntaxKind.StringLiteral:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function isConciseBody(node: Node): node is ConciseBody {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
@@ -2068,6 +2068,37 @@ namespace ts {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.JSDocTypeExpression:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function isJSDocRecordMember(node: Node): node is JSDocRecordMember {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.JSDocRecordMember:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function isJSDocTag(node: Node): node is JSDocTag {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.JSDocTag:
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
case SyntaxKind.JSDocReturnTag:
|
||||
case SyntaxKind.JSDocTypeTag:
|
||||
case SyntaxKind.JSDocParameterTag:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function acceptTransformer(transformer: Transformer, node: Node, visitor: (node: Node, write: (node: Node) => void) => void): Node {
|
||||
if (node) {
|
||||
switch (node.kind) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/// <reference path="checker.ts"/>
|
||||
/// <reference path="transform.ts" />
|
||||
/// <reference path="declarationEmitter.ts"/>
|
||||
/// <reference path="sourcemap.ts"/>
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
const delimiters = createDelimiterMap();
|
||||
|
||||
export function printFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformations?: TransformationChain) {
|
||||
export function printFiles(resolver: EmitResolver, host: EmitHost, writer: EmitTextWriter, sourceFiles: SourceFile[], transformations?: TransformationChain, sourceMap: SourceMapWriter = getNullSourceMapWriter()) {
|
||||
// emit output for the __extends helper function
|
||||
const extendsHelper = `
|
||||
var __extends = (this && this.__extends) || function (d, b) {
|
||||
@@ -70,7 +71,6 @@ function __export(m) {
|
||||
}
|
||||
})`;
|
||||
|
||||
let writer = createTextWriter(host.getNewLine());
|
||||
let {
|
||||
write,
|
||||
writeTextOfNode,
|
||||
@@ -80,6 +80,15 @@ function __export(m) {
|
||||
decreaseIndent
|
||||
} = writer;
|
||||
|
||||
let {
|
||||
setSourceFile,
|
||||
emitStart,
|
||||
emitEnd,
|
||||
emitPos,
|
||||
pushScope,
|
||||
popScope
|
||||
} = sourceMap;
|
||||
|
||||
// Add the pretty printer to the transformation chain.
|
||||
transformations = transformations ? chainTransformationPhases([transformations, createPrinter]) : createPrinter;
|
||||
|
||||
@@ -87,7 +96,11 @@ function __export(m) {
|
||||
transformFiles(resolver, host, sourceFiles, transformations);
|
||||
|
||||
writeLine();
|
||||
return writer.getText();
|
||||
|
||||
let sourceMappingURL = sourceMap.getSourceMappingURL();
|
||||
if (sourceMappingURL) {
|
||||
write(`//# sourceMappingURL=${sourceMappingURL}`);
|
||||
}
|
||||
|
||||
function createPrinter(transformer: Transformer) {
|
||||
let {
|
||||
@@ -120,20 +133,6 @@ function __export(m) {
|
||||
let emitTrailingComments = function (node: Node) { };
|
||||
let emitTrailingCommentsOfPosition = function (pos: number) { };
|
||||
|
||||
/** Called just before starting emit of a node */
|
||||
let emitStart = function (node: Node) { };
|
||||
|
||||
/** Called once the emit of the node is done */
|
||||
let emitEnd = function (node: Node) { };
|
||||
|
||||
/** Called to before starting the lexical scopes as in function/class in the emitted code because of node
|
||||
* @param scopeDeclaration node that starts the lexical scope
|
||||
* @param scopeName Optional name of this scope instead of deducing one from the declaration node */
|
||||
let scopeEmitStart = function(scopeDeclaration: Node, scopeName?: string) { };
|
||||
|
||||
/** Called after coming out of the scope */
|
||||
let scopeEmitEnd = function() { };
|
||||
|
||||
if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) {
|
||||
// initializeEmitterWithSourceMaps();
|
||||
}
|
||||
@@ -1179,7 +1178,7 @@ function __export(m) {
|
||||
else {
|
||||
emitStart(node);
|
||||
write("{");
|
||||
scopeEmitStart(getParentNode());
|
||||
pushScope(getParentNode());
|
||||
if (node.flags & NodeFlags.SingleLine) {
|
||||
emitList(node, node.statements, addHelpers(node, ListFormat.SpaceBetweenBraces | ListFormat.SingleLine | format));
|
||||
}
|
||||
@@ -1187,7 +1186,7 @@ function __export(m) {
|
||||
emitList(node, node.statements, addHelpers(node, ListFormat.Indented | ListFormat.MultiLine | format));
|
||||
}
|
||||
|
||||
scopeEmitEnd();
|
||||
popScope();
|
||||
write("}");
|
||||
emitEnd(node);
|
||||
}
|
||||
@@ -1413,7 +1412,7 @@ function __export(m) {
|
||||
function emitBlockFunctionBody(parentNode: Node, body: Block) {
|
||||
let statements = body.statements;
|
||||
write(" {");
|
||||
scopeEmitStart(parentNode);
|
||||
pushScope(parentNode);
|
||||
|
||||
// Emit all the prologue directives (like "use strict").
|
||||
increaseIndent();
|
||||
@@ -1427,7 +1426,7 @@ function __export(m) {
|
||||
emitList(body, statements, addHelpers(body, ListFormat.MultiLine | ListFormat.Indented | ListFormat.LexicalEnvironment), statementOffset);
|
||||
}
|
||||
|
||||
scopeEmitEnd();
|
||||
popScope();
|
||||
write("}");
|
||||
}
|
||||
|
||||
@@ -1449,9 +1448,9 @@ function __export(m) {
|
||||
tempFlags = 0;
|
||||
|
||||
write(" {");
|
||||
scopeEmitStart(node);
|
||||
pushScope(node);
|
||||
emitList(node, node.members, ListFormat.Indented | ListFormat.MultiLine);
|
||||
scopeEmitEnd();
|
||||
popScope();
|
||||
write("}");
|
||||
|
||||
tempFlags = savedTempFlags;
|
||||
@@ -1802,6 +1801,8 @@ function __export(m) {
|
||||
tempFlags = 0;
|
||||
currentSourceFile = node;
|
||||
|
||||
setSourceFile(node);
|
||||
|
||||
writeLine();
|
||||
emitShebang();
|
||||
emitDetachedComments(node);
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace ts {
|
||||
jsonContent = jsonText ? <{ typings?: string }>JSON.parse(jsonText) : { typings: undefined };
|
||||
}
|
||||
catch (e) {
|
||||
// gracefully handle if readFile fails or returns not JSON
|
||||
// gracefully handle if readFile fails or returns not JSON
|
||||
jsonContent = { typings: undefined };
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace ts {
|
||||
searchName = normalizePath(combinePaths(searchPath, moduleName));
|
||||
referencedSourceFile = forEach(supportedExtensions, extension => {
|
||||
if (extension === ".tsx" && !compilerOptions.jsx) {
|
||||
// resolve .tsx files only if jsx support is enabled
|
||||
// resolve .tsx files only if jsx support is enabled
|
||||
// 'logical not' handles both undefined and None cases
|
||||
return undefined;
|
||||
}
|
||||
@@ -552,7 +552,11 @@ namespace ts {
|
||||
|
||||
let start = new Date().getTime();
|
||||
|
||||
let emitResult = emitFiles(
|
||||
let emitter = options.experimentalTransforms
|
||||
? emitFilesUsingTransforms
|
||||
: emitFiles;
|
||||
|
||||
let emitResult = emitter(
|
||||
emitResolver,
|
||||
getEmitHost(writeFileCallback),
|
||||
sourceFile);
|
||||
@@ -711,13 +715,13 @@ namespace ts {
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
if ((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral && (node.flags & NodeFlags.Ambient || isDeclarationFile(file))) {
|
||||
// TypeScript 1.0 spec (April 2014): 12.1.6
|
||||
// An AmbientExternalModuleDeclaration declares an external module.
|
||||
// An AmbientExternalModuleDeclaration declares an external module.
|
||||
// This type of declaration is permitted only in the global module.
|
||||
// The StringLiteral must specify a top - level external module name.
|
||||
// Relative external module names are not permitted
|
||||
forEachChild((<ModuleDeclaration>node).body, node => {
|
||||
// TypeScript 1.0 spec (April 2014): 12.1.6
|
||||
// An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules
|
||||
// An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules
|
||||
// only through top - level external module names. Relative external module names are not permitted.
|
||||
collect(node, /* allowRelativeModuleNames */ false);
|
||||
});
|
||||
@@ -780,7 +784,7 @@ namespace ts {
|
||||
if (filesByName.contains(normalizedAbsolutePath)) {
|
||||
const file = getSourceFileFromCache(normalizedAbsolutePath, /*useAbsolutePath*/ true);
|
||||
// we don't have resolution for this relative file name but the match was found by absolute file name
|
||||
// store resolution for relative name as well
|
||||
// store resolution for relative name as well
|
||||
filesByName.set(fileName, file);
|
||||
return file;
|
||||
}
|
||||
@@ -1071,7 +1075,7 @@ namespace ts {
|
||||
!options.experimentalDecorators) {
|
||||
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators"));
|
||||
}
|
||||
|
||||
|
||||
if (FORCE_TRANSFORMS) {
|
||||
options.experimentalTransforms = true;
|
||||
}
|
||||
|
||||
404
src/compiler/sourcemap.ts
Normal file
404
src/compiler/sourcemap.ts
Normal file
@@ -0,0 +1,404 @@
|
||||
/// <reference path="checker.ts"/>
|
||||
/// <reference path="transform.ts" />
|
||||
/// <reference path="declarationEmitter.ts"/>
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
export interface SourceMapWriter {
|
||||
sourceMapData?: SourceMapData;
|
||||
setSourceFile(sourceFile: SourceFile): void;
|
||||
emitPos(pos: number, skipTrivia?: boolean): void;
|
||||
emitStart(range: TextRange): void;
|
||||
emitEnd(range: TextRange): void;
|
||||
pushScope(scopeDeclaration: Node, scopeName?: string): void;
|
||||
popScope(): void;
|
||||
getText(): string;
|
||||
getSourceMappingURL(): string;
|
||||
}
|
||||
|
||||
let nop = <(...args: any[]) => any>Function.prototype;
|
||||
let nullSourceMapWriter: SourceMapWriter;
|
||||
|
||||
export function getNullSourceMapWriter(): SourceMapWriter {
|
||||
if (nullSourceMapWriter === undefined) {
|
||||
nullSourceMapWriter = {
|
||||
setSourceFile: nop,
|
||||
emitStart: nop,
|
||||
emitEnd: nop,
|
||||
emitPos: nop,
|
||||
pushScope: nop,
|
||||
popScope: nop,
|
||||
getText: nop,
|
||||
getSourceMappingURL: nop
|
||||
};
|
||||
}
|
||||
|
||||
return nullSourceMapWriter;
|
||||
}
|
||||
|
||||
export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter, filePath: string, root?: SourceFile): SourceMapWriter {
|
||||
let compilerOptions = host.getCompilerOptions();
|
||||
let currentSourceFile: SourceFile;
|
||||
let sourceMapDir: string; // The directory in which sourcemap will be
|
||||
|
||||
// Current source map file and its index in the sources list
|
||||
let sourceMapSourceIndex = -1;
|
||||
|
||||
// Names and its index map
|
||||
let sourceMapNameIndexMap: Map<number> = {};
|
||||
let sourceMapNameIndices: number[] = [];
|
||||
|
||||
// Last recorded and encoded spans
|
||||
let lastRecordedSourceMapSpan: SourceMapSpan;
|
||||
let lastEncodedSourceMapSpan: SourceMapSpan = {
|
||||
emittedLine: 1,
|
||||
emittedColumn: 1,
|
||||
sourceLine: 1,
|
||||
sourceColumn: 1,
|
||||
sourceIndex: 0
|
||||
};
|
||||
let lastEncodedNameIndex = 0;
|
||||
|
||||
// Initialize source map data
|
||||
let sourceMapJsFile = getBaseFileName(normalizeSlashes(filePath));
|
||||
let sourceMapData: SourceMapData = {
|
||||
sourceMapFilePath: filePath + ".map",
|
||||
jsSourceMappingURL: sourceMapJsFile + ".map",
|
||||
sourceMapFile: sourceMapJsFile,
|
||||
sourceMapSourceRoot: compilerOptions.sourceRoot || "",
|
||||
sourceMapSources: [],
|
||||
inputSourceFileNames: [],
|
||||
sourceMapNames: [],
|
||||
sourceMapMappings: "",
|
||||
sourceMapSourcesContent: compilerOptions.inlineSources ? [] : undefined,
|
||||
sourceMapDecodedMappings: []
|
||||
};
|
||||
|
||||
// Normalize source root and make sure it has trailing "/" so that it can be used to combine paths with the
|
||||
// relative paths of the sources list in the sourcemap
|
||||
sourceMapData.sourceMapSourceRoot = ts.normalizeSlashes(sourceMapData.sourceMapSourceRoot);
|
||||
if (sourceMapData.sourceMapSourceRoot.length && sourceMapData.sourceMapSourceRoot.charCodeAt(sourceMapData.sourceMapSourceRoot.length - 1) !== CharacterCodes.slash) {
|
||||
sourceMapData.sourceMapSourceRoot += directorySeparator;
|
||||
}
|
||||
|
||||
if (compilerOptions.mapRoot) {
|
||||
sourceMapDir = normalizeSlashes(compilerOptions.mapRoot);
|
||||
if (root) { // emitting single module file
|
||||
// For modules or multiple emit files the mapRoot will have directory structure like the sources
|
||||
// So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map
|
||||
sourceMapDir = getDirectoryPath(getSourceFilePathInNewDir(root, host, sourceMapDir));
|
||||
}
|
||||
|
||||
if (!isRootedDiskPath(sourceMapDir) && !isUrl(sourceMapDir)) {
|
||||
// The relative paths are relative to the common directory
|
||||
sourceMapDir = combinePaths(host.getCommonSourceDirectory(), sourceMapDir);
|
||||
sourceMapData.jsSourceMappingURL = getRelativePathToDirectoryOrUrl(
|
||||
getDirectoryPath(normalizePath(filePath)), // get the relative sourceMapDir path based on jsFilePath
|
||||
combinePaths(sourceMapDir, sourceMapData.jsSourceMappingURL), // this is where user expects to see sourceMap
|
||||
host.getCurrentDirectory(),
|
||||
host.getCanonicalFileName,
|
||||
/*isAbsolutePathAnUrl*/ true);
|
||||
}
|
||||
else {
|
||||
sourceMapData.jsSourceMappingURL = combinePaths(sourceMapDir, sourceMapData.jsSourceMappingURL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sourceMapDir = getDirectoryPath(normalizePath(filePath));
|
||||
}
|
||||
|
||||
return {
|
||||
sourceMapData,
|
||||
setSourceFile: recordNewSourceFileStart,
|
||||
emitPos: recordSourceMapSpan,
|
||||
emitStart: recordEmitNodeStartSpan,
|
||||
emitEnd: recordEmitNodeEndSpan,
|
||||
pushScope: recordScopeNameOfNode,
|
||||
popScope: recordScopeNameEnd,
|
||||
getText,
|
||||
getSourceMappingURL,
|
||||
};
|
||||
|
||||
function getSourceMapNameIndex() {
|
||||
return sourceMapNameIndices.length ? lastOrUndefined(sourceMapNameIndices) : -1;
|
||||
}
|
||||
|
||||
// Encoding for sourcemap span
|
||||
function encodeLastRecordedSourceMapSpan() {
|
||||
if (!lastRecordedSourceMapSpan || lastRecordedSourceMapSpan === lastEncodedSourceMapSpan) {
|
||||
return;
|
||||
}
|
||||
|
||||
let prevEncodedEmittedColumn = lastEncodedSourceMapSpan.emittedColumn;
|
||||
// Line/Comma delimiters
|
||||
if (lastEncodedSourceMapSpan.emittedLine === lastRecordedSourceMapSpan.emittedLine) {
|
||||
// Emit comma to separate the entry
|
||||
if (sourceMapData.sourceMapMappings) {
|
||||
sourceMapData.sourceMapMappings += ",";
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Emit line delimiters
|
||||
for (let encodedLine = lastEncodedSourceMapSpan.emittedLine; encodedLine < lastRecordedSourceMapSpan.emittedLine; encodedLine++) {
|
||||
sourceMapData.sourceMapMappings += ";";
|
||||
}
|
||||
prevEncodedEmittedColumn = 1;
|
||||
}
|
||||
|
||||
// 1. Relative Column 0 based
|
||||
sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.emittedColumn - prevEncodedEmittedColumn);
|
||||
|
||||
// 2. Relative sourceIndex
|
||||
sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.sourceIndex - lastEncodedSourceMapSpan.sourceIndex);
|
||||
|
||||
// 3. Relative sourceLine 0 based
|
||||
sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.sourceLine - lastEncodedSourceMapSpan.sourceLine);
|
||||
|
||||
// 4. Relative sourceColumn 0 based
|
||||
sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.sourceColumn - lastEncodedSourceMapSpan.sourceColumn);
|
||||
|
||||
// 5. Relative namePosition 0 based
|
||||
if (lastRecordedSourceMapSpan.nameIndex >= 0) {
|
||||
sourceMapData.sourceMapMappings += base64VLQFormatEncode(lastRecordedSourceMapSpan.nameIndex - lastEncodedNameIndex);
|
||||
lastEncodedNameIndex = lastRecordedSourceMapSpan.nameIndex;
|
||||
}
|
||||
|
||||
lastEncodedSourceMapSpan = lastRecordedSourceMapSpan;
|
||||
sourceMapData.sourceMapDecodedMappings.push(lastEncodedSourceMapSpan);
|
||||
}
|
||||
|
||||
function recordSourceMapSpan(pos: number, skipTrivia?: boolean) {
|
||||
if (pos === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (skipTrivia) {
|
||||
pos = ts.skipTrivia(currentSourceFile.text, pos);
|
||||
}
|
||||
|
||||
let sourceLinePos = getLineAndCharacterOfPosition(currentSourceFile, pos);
|
||||
|
||||
// Convert the location to be one-based.
|
||||
sourceLinePos.line++;
|
||||
sourceLinePos.character++;
|
||||
|
||||
let emittedLine = writer.getLine();
|
||||
let emittedColumn = writer.getColumn();
|
||||
|
||||
// If this location wasn't recorded or the location in source is going backwards, record the span
|
||||
if (!lastRecordedSourceMapSpan ||
|
||||
lastRecordedSourceMapSpan.emittedLine !== emittedLine ||
|
||||
lastRecordedSourceMapSpan.emittedColumn !== emittedColumn ||
|
||||
(lastRecordedSourceMapSpan.sourceIndex === sourceMapSourceIndex &&
|
||||
(lastRecordedSourceMapSpan.sourceLine > sourceLinePos.line ||
|
||||
(lastRecordedSourceMapSpan.sourceLine === sourceLinePos.line && lastRecordedSourceMapSpan.sourceColumn > sourceLinePos.character)))) {
|
||||
|
||||
// Encode the last recordedSpan before assigning new
|
||||
encodeLastRecordedSourceMapSpan();
|
||||
|
||||
// New span
|
||||
lastRecordedSourceMapSpan = {
|
||||
emittedLine: emittedLine,
|
||||
emittedColumn: emittedColumn,
|
||||
sourceLine: sourceLinePos.line,
|
||||
sourceColumn: sourceLinePos.character,
|
||||
nameIndex: getSourceMapNameIndex(),
|
||||
sourceIndex: sourceMapSourceIndex
|
||||
};
|
||||
}
|
||||
else {
|
||||
// Take the new pos instead since there is no change in emittedLine and column since last location
|
||||
lastRecordedSourceMapSpan.sourceLine = sourceLinePos.line;
|
||||
lastRecordedSourceMapSpan.sourceColumn = sourceLinePos.character;
|
||||
lastRecordedSourceMapSpan.sourceIndex = sourceMapSourceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
function recordEmitNodeStartSpan(range: TextRange) {
|
||||
recordSourceMapSpan(range.pos, /*skipTrivia*/ true);
|
||||
}
|
||||
|
||||
function recordEmitNodeEndSpan(range: TextRange) {
|
||||
recordSourceMapSpan(range.end, /*skipTrivia*/ false);
|
||||
}
|
||||
|
||||
function recordNewSourceFileStart(sourceFile: SourceFile) {
|
||||
currentSourceFile = getOriginalNodeIf(sourceFile, isSourceFile);
|
||||
|
||||
// Add the file to tsFilePaths
|
||||
// If sourceroot option: Use the relative path corresponding to the common directory path
|
||||
// otherwise source locations relative to map file location
|
||||
let sourcesDirectoryPath = compilerOptions.sourceRoot ? host.getCommonSourceDirectory() : sourceMapDir;
|
||||
|
||||
let source = getRelativePathToDirectoryOrUrl(sourcesDirectoryPath,
|
||||
currentSourceFile.fileName,
|
||||
host.getCurrentDirectory(),
|
||||
host.getCanonicalFileName,
|
||||
/*isAbsolutePathAnUrl*/ true);
|
||||
|
||||
sourceMapSourceIndex = indexOf(sourceMapData.sourceMapSources, source);
|
||||
if (sourceMapSourceIndex === -1) {
|
||||
sourceMapSourceIndex = sourceMapData.sourceMapSources.length;
|
||||
sourceMapData.sourceMapSources.push(source);
|
||||
|
||||
// The one that can be used from program to get the actual source file
|
||||
sourceMapData.inputSourceFileNames.push(sourceFile.fileName);
|
||||
|
||||
if (compilerOptions.inlineSources) {
|
||||
sourceMapData.sourceMapSourcesContent.push(sourceFile.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function recordScopeNameIndex(scopeNameIndex: number) {
|
||||
sourceMapNameIndices.push(scopeNameIndex);
|
||||
}
|
||||
|
||||
function recordScopeNameStart(scopeDeclaration: Node, scopeName: string) {
|
||||
let scopeNameIndex = -1;
|
||||
if (scopeName) {
|
||||
let parentIndex = getSourceMapNameIndex();
|
||||
if (parentIndex !== -1) {
|
||||
// Child scopes are always shown with a dot (even if they have no name),
|
||||
// unless it is a computed property. Then it is shown with brackets,
|
||||
// but the brackets are included in the name.
|
||||
let name = (<Declaration>scopeDeclaration).name;
|
||||
if (!name || name.kind !== SyntaxKind.ComputedPropertyName) {
|
||||
scopeName = "." + scopeName;
|
||||
}
|
||||
scopeName = sourceMapData.sourceMapNames[parentIndex] + scopeName;
|
||||
}
|
||||
|
||||
scopeNameIndex = getProperty(sourceMapNameIndexMap, scopeName);
|
||||
if (scopeNameIndex === undefined) {
|
||||
scopeNameIndex = sourceMapData.sourceMapNames.length;
|
||||
sourceMapData.sourceMapNames.push(scopeName);
|
||||
sourceMapNameIndexMap[scopeName] = scopeNameIndex;
|
||||
}
|
||||
}
|
||||
recordScopeNameIndex(scopeNameIndex);
|
||||
}
|
||||
|
||||
function recordScopeNameOfNode(scopeDeclaration: Node, scopeName?: string) {
|
||||
if (scopeName) {
|
||||
// The scope was already given a name use it
|
||||
recordScopeNameStart(scopeDeclaration, scopeName);
|
||||
}
|
||||
else if (scopeDeclaration.kind === SyntaxKind.FunctionDeclaration ||
|
||||
scopeDeclaration.kind === SyntaxKind.FunctionExpression ||
|
||||
scopeDeclaration.kind === SyntaxKind.MethodDeclaration ||
|
||||
scopeDeclaration.kind === SyntaxKind.MethodSignature ||
|
||||
scopeDeclaration.kind === SyntaxKind.GetAccessor ||
|
||||
scopeDeclaration.kind === SyntaxKind.SetAccessor ||
|
||||
scopeDeclaration.kind === SyntaxKind.ModuleDeclaration ||
|
||||
scopeDeclaration.kind === SyntaxKind.ClassDeclaration ||
|
||||
scopeDeclaration.kind === SyntaxKind.EnumDeclaration) {
|
||||
// Declaration and has associated name use it
|
||||
if ((<Declaration>scopeDeclaration).name) {
|
||||
let name = (<Declaration>scopeDeclaration).name;
|
||||
// For computed property names, the text will include the brackets
|
||||
scopeName = name.kind === SyntaxKind.ComputedPropertyName
|
||||
? getTextOfNode(name)
|
||||
: (<Identifier>(<Declaration>scopeDeclaration).name).text;
|
||||
}
|
||||
|
||||
recordScopeNameStart(scopeDeclaration, scopeName);
|
||||
}
|
||||
else {
|
||||
// Block just use the name from upper level scope
|
||||
recordScopeNameIndex(getSourceMapNameIndex());
|
||||
}
|
||||
}
|
||||
|
||||
function recordScopeNameEnd() {
|
||||
sourceMapNameIndices.pop();
|
||||
}
|
||||
|
||||
function getText() {
|
||||
encodeLastRecordedSourceMapSpan();
|
||||
|
||||
return stringify({
|
||||
version: 3,
|
||||
file: sourceMapData.sourceMapFile,
|
||||
sourceRoot: sourceMapData.sourceMapSourceRoot,
|
||||
sources: sourceMapData.sourceMapSources,
|
||||
sourcesContent: sourceMapData.sourceMapSourcesContent,
|
||||
names: sourceMapData.sourceMapNames,
|
||||
mappings: sourceMapData.sourceMapMappings
|
||||
});
|
||||
}
|
||||
|
||||
function getSourceMappingURL() {
|
||||
if (compilerOptions.inlineSourceMap) {
|
||||
// Encode the sourceMap into the sourceMap url
|
||||
let base64SourceMapText = convertToBase64(getText());
|
||||
return `data:application/json;base64,${base64SourceMapText}`;
|
||||
}
|
||||
else {
|
||||
return sourceMapData.jsSourceMappingURL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
function base64FormatEncode(inValue: number) {
|
||||
if (inValue < 64) {
|
||||
return base64Chars.charAt(inValue);
|
||||
}
|
||||
|
||||
throw TypeError(inValue + ": not a 64 based value");
|
||||
}
|
||||
|
||||
function base64VLQFormatEncode(inValue: number) {
|
||||
// Add a new least significant bit that has the sign of the value.
|
||||
// if negative number the least significant bit that gets added to the number has value 1
|
||||
// else least significant bit value that gets added is 0
|
||||
// eg. -1 changes to binary : 01 [1] => 3
|
||||
// +1 changes to binary : 01 [0] => 2
|
||||
if (inValue < 0) {
|
||||
inValue = ((-inValue) << 1) + 1;
|
||||
}
|
||||
else {
|
||||
inValue = inValue << 1;
|
||||
}
|
||||
|
||||
// Encode 5 bits at a time starting from least significant bits
|
||||
let encodedStr = "";
|
||||
do {
|
||||
let currentDigit = inValue & 31; // 11111
|
||||
inValue = inValue >> 5;
|
||||
if (inValue > 0) {
|
||||
// There are still more digits to decode, set the msb (6th bit)
|
||||
currentDigit = currentDigit | 32;
|
||||
}
|
||||
encodedStr = encodedStr + base64FormatEncode(currentDigit);
|
||||
} while (inValue > 0);
|
||||
|
||||
return encodedStr;
|
||||
}
|
||||
|
||||
var stringify = JSON && JSON.stringify ? JSON.stringify : function stringify(value: any): string {
|
||||
return value == null ? "null"
|
||||
: typeof value === "string" ? `"${escapeString(value)}"`
|
||||
: typeof value === "number" ? String(value)
|
||||
: typeof value === "boolean" ? value ? "true" : "false"
|
||||
: hasToJson(value) ? stringify(value.toJSON())
|
||||
: isArray(value) ? `[${reduceLeft(value, stringifyElement, "")}]`
|
||||
: typeof value === "object" ? `{${reduceProperties(value, stringifyProperty, "")}}`
|
||||
: "null";
|
||||
}
|
||||
|
||||
function hasToJson(value: any) {
|
||||
return typeof value === "object" && typeof value.toJSON === "function";
|
||||
}
|
||||
|
||||
function stringifyElement(memo: string, value: any) {
|
||||
return (memo ? memo + "," : memo) + stringify(value);
|
||||
}
|
||||
|
||||
function stringifyProperty(memo: string, value: any, key: string) {
|
||||
return value === undefined ? memo
|
||||
: (memo ? memo + "," : memo) + `"${escapeString(key)}":${stringify(value)}`;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,9 @@
|
||||
"transforms/module/es6.ts",
|
||||
"transforms/jsx.ts",
|
||||
"transforms/es6.ts",
|
||||
"declarationEmitter.ts",
|
||||
"sourcemap.ts",
|
||||
"printer.ts",
|
||||
"emitter.ts",
|
||||
"program.ts",
|
||||
"commandLineParser.ts",
|
||||
|
||||
Reference in New Issue
Block a user