Added source maps to printer

This commit is contained in:
Ron Buckton
2015-10-28 17:24:24 -07:00
parent 2a3f802841
commit 21574cd8a6
8 changed files with 593 additions and 49 deletions

View File

@@ -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",

View File

@@ -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 () => {

View File

@@ -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);
}
}
}
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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
View 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)}`;
}
}

View File

@@ -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",