UNPKG

tsickle

Version:

Transpile TypeScript code to JavaScript with Closure annotations.

176 lines (174 loc) 6.99 kB
/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ "use strict"; var source_map_1 = require("source-map"); var ts = require("typescript"); /** * A Rewriter manages iterating through a ts.SourceFile, copying input * to output while letting the subclass potentially alter some nodes * along the way by implementing maybeProcess(). */ var Rewriter = (function () { function Rewriter(file) { this.file = file; this.output = []; /** Errors found while examining the code. */ this.diagnostics = []; /** Current position in the output. */ this.position = { line: 1, column: 1 }; /** * The current level of recursion through TypeScript Nodes. Used in formatting internal debug * print statements. */ this.indent = 0; this.sourceMap = new source_map_1.SourceMapGenerator({ file: file.fileName }); this.sourceMap.addMapping({ original: this.position, generated: this.position, source: file.fileName, }); } Rewriter.prototype.getOutput = function () { if (this.indent !== 0) { throw new Error('visit() failed to track nesting'); } return { output: this.output.join(''), diagnostics: this.diagnostics, sourceMap: this.sourceMap, }; }; /** * visit traverses a Node, recursively writing all nodes not handled by this.maybeProcess. */ Rewriter.prototype.visit = function (node) { // this.logWithIndent('node: ' + ts.SyntaxKind[node.kind]); this.indent++; if (!this.maybeProcess(node)) { this.writeNode(node); } this.indent--; }; /** * maybeProcess lets subclasses optionally processes a node. * * @return True if the node has been handled and doesn't need to be traversed; * false to have the node written and its children recursively visited. */ Rewriter.prototype.maybeProcess = function (node) { return false; }; /** writeNode writes a ts.Node, calling this.visit() on its children. */ Rewriter.prototype.writeNode = function (node, skipComments) { var _this = this; if (skipComments === void 0) { skipComments = false; } var pos = node.getFullStart(); if (skipComments) { // To skip comments, we skip all whitespace/comments preceding // the node. But if there was anything skipped we should emit // a newline in its place so that the node remains separated // from the previous node. TODO: don't skip anything here if // there wasn't any comment. if (node.getFullStart() < node.getStart()) { this.emit('\n'); } pos = node.getStart(); } ts.forEachChild(node, function (child) { _this.writeRange(pos, child.getFullStart()); _this.visit(child); pos = child.getEnd(); }); this.writeRange(pos, node.getEnd()); }; // Write a span of the input file as expressed by absolute offsets. // These offsets are found in attributes like node.getFullStart() and // node.getEnd(). Rewriter.prototype.writeRange = function (from, to) { // getSourceFile().getText() is wrong here because it has the text of // the SourceFile node of the AST, which doesn't contain the comments // preceding that node. Semantically these ranges are just offsets // into the original source file text, so slice from that. var text = this.file.text.slice(from, to); if (text) { // Add a source mapping. writeRange(from, to) always corresponds to // original source code, so add a mapping at the current location that // points back to the location at `from`. The additional code generated // by tsickle will then be considered part of the last mapped code // section preceding it. That's arguably incorrect (e.g. for the fake // methods defining properties), but is good enough for stack traces. var pos = this.file.getLineAndCharacterOfPosition(from); this.sourceMap.addMapping({ original: { line: pos.line + 1, column: pos.character + 1 }, generated: this.position, source: this.file.fileName, }); this.emit(text); } }; Rewriter.prototype.emit = function (str) { this.output.push(str); for (var _i = 0, str_1 = str; _i < str_1.length; _i++) { var c = str_1[_i]; this.position.column++; if (c === '\n') { this.position.line++; this.position.column = 1; } } }; /** Removes comment metacharacters from a string, to make it safe to embed in a comment. */ Rewriter.prototype.escapeForComment = function (str) { return str.replace(/\/\*/g, '__').replace(/\*\//g, '__'); }; /* tslint:disable: no-unused-variable */ Rewriter.prototype.logWithIndent = function (message) { /* tslint:enable: no-unused-variable */ var prefix = new Array(this.indent + 1).join('| '); console.log(prefix + message); }; /** * Produces a compiler error that references the Node's kind. This is useful for the "else" * branch of code that is attempting to handle all possible input Node types, to ensure all cases * covered. */ Rewriter.prototype.errorUnimplementedKind = function (node, where) { this.error(node, ts.SyntaxKind[node.kind] + " not implemented in " + where); }; Rewriter.prototype.error = function (node, messageText) { this.diagnostics.push({ file: this.file, start: node.getStart(), length: node.getEnd() - node.getStart(), messageText: messageText, category: ts.DiagnosticCategory.Error, code: 0, }); }; return Rewriter; }()); exports.Rewriter = Rewriter; /** Returns the string contents of a ts.Identifier. */ function getIdentifierText(identifier) { // NOTE: the 'text' property on an Identifier may be escaped if it starts // with '__', so just use getText(). return identifier.getText(); } exports.getIdentifierText = getIdentifierText; /** * Converts an escaped TypeScript name into the original source name. * Prefer getIdentifierText() instead if possible. */ function unescapeName(name) { // See the private function unescapeIdentifier in TypeScript's utilities.ts. if (name.match(/^___/)) return name.substr(1); return name; } exports.unescapeName = unescapeName; //# sourceMappingURL=rewriter.js.map