Socket
Socket
Sign inDemoInstall

tsickle

Package Overview
Dependencies
Maintainers
2
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tsickle - npm Package Compare versions

Comparing version 0.2.4 to 0.21.0

2

build/definitions/tsickle.d.ts

@@ -12,4 +12,2 @@ /**

export { processES5 } from './es5processor';
export { FileMap, ModulesManifest } from './modules_manifest';
export { Pass, TsickleCompilerHost, TsickleCompilerHostOptions, TsickleEnvironment } from './tsickle_compiler_host';
export interface Options {

@@ -16,0 +14,0 @@ /**

2

build/definitions/type-translator.d.ts

@@ -44,2 +44,4 @@ /**

translate(type: ts.Type): string;
private translateUnion(type);
private translateObject(type);
/**

@@ -46,0 +48,0 @@ * translateTypeLiteral translates a ts.SymbolFlags.TypeLiteral type, which

@@ -1,2 +0,1 @@

import * as ts from 'typescript';
/**

@@ -10,15 +9,1 @@ * @license

export declare function toArray<T>(iterator: Iterator<T>): T[];
/**
* Constructs a new ts.CompilerHost that overlays sources in substituteSource
* over another ts.CompilerHost.
*
* @param substituteSource A map of source file name -> overlay source text.
*/
export declare function createSourceReplacingCompilerHost(substituteSource: Map<string, string>, delegate: ts.CompilerHost): ts.CompilerHost;
/**
* Constructs a new ts.CompilerHost that overlays sources in substituteSource
* over another ts.CompilerHost.
*
* @param outputFiles map to fill with source file name -> output text.
*/
export declare function createOutputRetainingCompilerHost(outputFiles: Map<string, string>, delegate: ts.CompilerHost): ts.CompilerHost;

@@ -9,3 +9,3 @@ /**

"use strict";
var path = require('path');
var path = require("path");
// Postprocess generated JS.

@@ -12,0 +12,0 @@ function pathToModuleName(context, fileName) {

@@ -14,6 +14,6 @@ /**

};
var ts = require('typescript');
var rewriter_1 = require('./rewriter');
var type_translator_1 = require('./type-translator');
var util_1 = require('./util');
var ts = require("typescript");
var rewriter_1 = require("./rewriter");
var type_translator_1 = require("./type-translator");
var util_1 = require("./util");
exports.ANNOTATION_SUPPORT_CODE = "\ninterface DecoratorInvocation {\n type: Function;\n args?: any[];\n}\n";

@@ -26,4 +26,5 @@ // ClassRewriter rewrites a single "class Foo {...}" declaration.

function ClassRewriter(typeChecker, sourceFile) {
_super.call(this, sourceFile);
this.typeChecker = typeChecker;
var _this = _super.call(this, sourceFile) || this;
_this.typeChecker = typeChecker;
return _this;
}

@@ -44,2 +45,5 @@ /**

break;
// PropertyAccessExpression is intentionally missing here,
// because the rest of the rewriter does not handle such
// expressions.
default:

@@ -53,3 +57,35 @@ return false;

}
return decSym.getDocumentationComment().some(function (c) { return c.text.indexOf('@Annotation') >= 0; });
for (var _i = 0, _a = decSym.getDeclarations(); _i < _a.length; _i++) {
var d = _a[_i];
// Switch to the TS JSDoc parser in the future to avoid false positives here.
// For example using '@Annotation' in a true comment.
// However, a new TS API would be needed, track at
// https://github.com/Microsoft/TypeScript/issues/7393.
var commentNode = d;
// Not handling PropertyAccess expressions here, because they are
// filtered earlier.
if (commentNode.kind === ts.SyntaxKind.VariableDeclaration) {
if (!commentNode.parent)
return false;
commentNode = commentNode.parent;
}
// Go up one more level to VariableDeclarationStatement, where usually
// the comment lives. If the declaration has an 'export', the
// VDList.getFullText will not contain the comment.
if (commentNode.kind === ts.SyntaxKind.VariableDeclarationList) {
if (!commentNode.parent)
return false;
commentNode = commentNode.parent;
}
var range = ts.getLeadingCommentRanges(commentNode.getFullText(), 0);
if (!range)
return;
for (var _b = 0, range_1 = range; _b < range_1.length; _b++) {
var _c = range_1[_b], pos = _c.pos, end = _c.end;
var jsDocText = commentNode.getFullText().substring(pos, end);
if (jsDocText.includes('@Annotation'))
return true;
}
}
return false;
};

@@ -273,4 +309,5 @@ ClassRewriter.prototype.decoratorsToLower = function (n) {

function DecoratorRewriter(typeChecker, sourceFile) {
_super.call(this, sourceFile);
this.typeChecker = typeChecker;
var _this = _super.call(this, sourceFile) || this;
_this.typeChecker = typeChecker;
return _this;
}

@@ -277,0 +314,0 @@ DecoratorRewriter.prototype.process = function () {

@@ -14,5 +14,5 @@ /**

};
var ts = require('typescript');
var rewriter_1 = require('./rewriter');
var util_1 = require('./util');
var ts = require("typescript");
var rewriter_1 = require("./rewriter");
var util_1 = require("./util");
/**

@@ -26,5 +26,5 @@ * ES5Processor postprocesses TypeScript compilation output JS, to rewrite commonjs require()s into

function ES5Processor(file, pathToModuleName, prelude) {
_super.call(this, file);
this.pathToModuleName = pathToModuleName;
this.prelude = prelude;
var _this = _super.call(this, file) || this;
_this.pathToModuleName = pathToModuleName;
_this.prelude = prelude;
/**

@@ -41,3 +41,3 @@ * namespaceImports collects the variables for imported goog.modules.

*/
this.namespaceImports = new Set();
_this.namespaceImports = new Set();
/**

@@ -47,7 +47,8 @@ * moduleVariables maps from module names to the variables they're assigned to.

*/
this.moduleVariables = new Map();
_this.moduleVariables = new Map();
/** strippedStrict is true once we've stripped a "use strict"; from the input. */
this.strippedStrict = false;
_this.strippedStrict = false;
/** unusedIndex is used to generate fresh symbols for unnamed imports. */
this.unusedIndex = 0;
_this.unusedIndex = 0;
return _this;
}

@@ -54,0 +55,0 @@ ES5Processor.prototype.process = function (moduleId, isES5) {

@@ -131,2 +131,16 @@ /**

}
else if (!arrayIncludes(JSDOC_TAGS_WHITELIST, tagName)) {
// Silently drop tags we don't understand. This is a subtle
// compromise between multiple issues.
// 1) If we pass through these non-Closure tags, the user will
// get a warning from Closure, and the point of tsickle is
// to insulate the user from Closure.
// 2) The output of tsickle is for Closure only, so we don't
// care if we drop tags that Closure doesn't undersand.
// 3) Finally, we don't want to warn because users should be
// free to add whichever JSDoc they feel like. If the user
// wants help ensuring they didn't typo a tag, that is the
// responsibility of a linter.
continue;
}
// Grab the parameter name from @param tags.

@@ -153,4 +167,3 @@ var parameterName = void 0;

else {
var lastTag = tags[tags.length - 1];
lastTag.text = (lastTag.text || '') + '\n' + line;
tags[tags.length - 1].text += '\n' + line;
}

@@ -172,19 +185,3 @@ }

if (tag.tagName) {
if (!arrayIncludes(JSDOC_TAGS_WHITELIST, tag.tagName)) {
// Escape tags we don't understand. This is a subtle
// compromise between multiple issues.
// 1) If we pass through these non-Closure tags, the user will
// get a warning from Closure, and the point of tsickle is
// to insulate the user from Closure.
// 2) The output of tsickle is for Closure but also may be read
// by humans, for example non-TypeScript users of Angular.
// 3) Finally, we don't want to warn because users should be
// free to add whichever JSDoc they feel like. If the user
// wants help ensuring they didn't typo a tag, that is the
// responsibility of a linter.
out += " \\@" + tag.tagName;
}
else {
out += " @" + tag.tagName;
}
out += " @" + tag.tagName;
}

@@ -191,0 +188,0 @@ if (tag.type) {

#!/usr/bin/env node
"use strict";
var fs = require('fs');
var minimist = require('minimist');
var mkdirp = require('mkdirp');
var path = require('path');
var ts = require('typescript');
var cliSupport = require('./cli_support');
var tsickle = require('./tsickle');
var util_1 = require('./util');
var fs = require("fs");
var minimist = require("minimist");
var mkdirp = require("mkdirp");
var path = require("path");
var ts = require("typescript");
var cliSupport = require("./cli_support");
var tsickle = require("./tsickle");
var util_1 = require("./util");
function usage() {

@@ -79,3 +79,3 @@ console.error("usage: tsickle [tsickle options] -- [tsc options]\n\nexample:\n tsickle --externs=foo/externs.js -- -p src --noImplicitAny\n\ntsickle flags are:\n --externs=PATH save generated Closure externs.js to PATH\n --untyped convert every type in TypeScript to the Closure {?} type\n");

}
(_c = ts.parseJsonConfigFileContent(json, ts.sys, projectDir, options, configFileName), options = _c.options, fileNames = _c.fileNames, errors = _c.errors, _c);
(_c = ts.parseJsonConfigFileContent(json, ts.sys, projectDir, options, configFileName), options = _c.options, fileNames = _c.fileNames, errors = _c.errors);
if (errors.length > 0) {

@@ -91,2 +91,32 @@ allDiagnostics.push.apply(allDiagnostics, errors);

/**
* Constructs a new ts.CompilerHost that overlays sources in substituteSource
* over another ts.CompilerHost.
*
* @param substituteSource A map of source file name -> overlay source text.
*/
function createSourceReplacingCompilerHost(substituteSource, delegate) {
return {
getSourceFile: getSourceFile,
getCancellationToken: delegate.getCancellationToken,
getDefaultLibFileName: delegate.getDefaultLibFileName,
writeFile: delegate.writeFile,
getCurrentDirectory: delegate.getCurrentDirectory,
getCanonicalFileName: delegate.getCanonicalFileName,
useCaseSensitiveFileNames: delegate.useCaseSensitiveFileNames,
getNewLine: delegate.getNewLine,
fileExists: delegate.fileExists,
readFile: delegate.readFile,
directoryExists: delegate.directoryExists,
getDirectories: delegate.getDirectories,
};
function getSourceFile(fileName, languageVersion, onError) {
var path = ts.sys.resolvePath(fileName);
var sourceText = substituteSource.get(path);
if (sourceText) {
return ts.createSourceFile(path, sourceText, languageVersion);
}
return delegate.getSourceFile(path, languageVersion, onError);
}
}
/**
* Compiles TypeScript code into Closure-compiler-ready JS.

@@ -108,21 +138,35 @@ * Doesn't write any files to disk; all JS content is returned in a map.

}
var tsickleCompilerHostOptions = {
googmodule: true,
es5Mode: false,
tsickleTyped: !settings.isUntyped,
prelude: '',
var tsickleOptions = {
untyped: settings.isUntyped,
logWarning: settings.verbose ?
function (warning) {
console.error(tsickle.formatDiagnostics([warning]));
} :
undefined,
};
var tsickleEnvironment = {
shouldSkipTsickleProcessing: function (fileName) { return fileNames.indexOf(fileName) === -1; },
pathToModuleName: cliSupport.pathToModuleName,
shouldIgnoreWarningsForPath: function (filePath) { return false; },
fileNameToModuleId: function (fileName) { return fileName; },
};
var jsFiles = new Map();
var hostDelegate = util_1.createOutputRetainingCompilerHost(jsFiles, ts.createCompilerHost(options));
// Process each input file with tsickle and save the output.
var tsickleOutput = new Map();
var tsickleExterns = '';
for (var _i = 0, fileNames_1 = fileNames; _i < fileNames_1.length; _i++) {
var fileName = fileNames_1[_i];
var _a = tsickle.annotate(program, program.getSourceFile(fileName), tsickleOptions), output = _a.output, externs = _a.externs, diagnostics_2 = _a.diagnostics;
if (diagnostics_2.length > 0) {
allDiagnostics.push.apply(allDiagnostics, diagnostics_2);
return null;
}
tsickleOutput.set(ts.sys.resolvePath(fileName), output);
if (externs) {
tsickleExterns += externs;
}
}
// Reparse and reload the program, inserting the tsickle output in
// place of the original source.
var host = new tsickle.TsickleCompilerHost(hostDelegate, tsickleCompilerHostOptions, tsickleEnvironment, program, tsickle.Pass.Tsickle);
var host = createSourceReplacingCompilerHost(tsickleOutput, ts.createCompilerHost(options));
program = ts.createProgram(fileNames, options, host);
var diagnostics = program.emit(undefined).diagnostics;
// Emit, creating a map of fileName => generated JS source.
var jsFiles = new Map();
function writeFile(fileName, data) {
jsFiles.set(fileName, data);
}
var diagnostics = program.emit(undefined, writeFile).diagnostics;
if (diagnostics.length > 0) {

@@ -132,5 +176,12 @@ allDiagnostics.push.apply(allDiagnostics, diagnostics);

}
return { jsFiles: jsFiles, externs: host.getGeneratedExterns() };
for (var _b = 0, _c = util_1.toArray(jsFiles.keys()); _b < _c.length; _b++) {
var fileName = _c[_b];
if (path.extname(fileName) !== '.map') {
var moduleId = fileName; // TODO: does anyone use this?
var output = tsickle.processES5(fileName, moduleId, jsFiles.get(fileName), cliSupport.pathToModuleName).output;
jsFiles.set(fileName, output);
}
}
return { jsFiles: jsFiles, externs: tsickleExterns };
}
exports.toClosureJS = toClosureJS;
function main(args) {

@@ -137,0 +188,0 @@ var _a = loadSettingsFromArgs(args), settings = _a.settings, tscArgs = _a.tscArgs;

@@ -9,4 +9,4 @@ /**

"use strict";
var source_map_1 = require('source-map');
var ts = require('typescript');
var source_map_1 = require("source-map");
var ts = require("typescript");
/**

@@ -13,0 +13,0 @@ * A Rewriter manages iterating through a ts.SourceFile, copying input

@@ -14,16 +14,11 @@ /**

};
var ts = require('typescript');
var jsdoc = require('./jsdoc');
var rewriter_1 = require('./rewriter');
var type_translator_1 = require('./type-translator');
var util_1 = require('./util');
var decorator_annotator_1 = require('./decorator-annotator');
var ts = require("typescript");
var jsdoc = require("./jsdoc");
var rewriter_1 = require("./rewriter");
var type_translator_1 = require("./type-translator");
var util_1 = require("./util");
var decorator_annotator_1 = require("./decorator-annotator");
exports.convertDecorators = decorator_annotator_1.convertDecorators;
var es5processor_1 = require('./es5processor');
var es5processor_1 = require("./es5processor");
exports.processES5 = es5processor_1.processES5;
var modules_manifest_1 = require('./modules_manifest');
exports.ModulesManifest = modules_manifest_1.ModulesManifest;
var tsickle_compiler_host_1 = require('./tsickle_compiler_host');
exports.Pass = tsickle_compiler_host_1.Pass;
exports.TsickleCompilerHost = tsickle_compiler_host_1.TsickleCompilerHost;
/**

@@ -63,2 +58,6 @@ * Symbols that are already declared as externs in Closure, that should

exports.formatDiagnostics = formatDiagnostics;
/** @return true if node has the specified modifier flag set. */
function hasModifierFlag(node, flag) {
return (ts.getCombinedModifierFlags(node) & flag) !== 0;
}
/**

@@ -102,7 +101,8 @@ * TypeScript allows you to write identifiers quoted, like:

default:
// The above list of kinds should be exhaustive.
throw new Error("unhandled function parameter kind: " + ts.SyntaxKind[param.name.kind]);
// The above list of kinds is exhaustive. param.name is 'never' at this point.
var paramName = param.name;
throw new Error("unhandled function parameter kind: " + ts.SyntaxKind[paramName.kind]);
}
}
var VISIBILITY_FLAGS = ts.NodeFlags.Private | ts.NodeFlags.Protected | ts.NodeFlags.Public;
var VISIBILITY_FLAGS = ts.ModifierFlags.Private | ts.ModifierFlags.Protected | ts.ModifierFlags.Public;
/**

@@ -117,5 +117,6 @@ * A Rewriter subclass that adds Tsickle-specific (Closure translation) functionality.

function ClosureRewriter(program, file, options) {
_super.call(this, file);
this.program = program;
this.options = options;
var _this = _super.call(this, file) || this;
_this.program = program;
_this.options = options;
return _this;
}

@@ -165,3 +166,3 @@ /**

// Add @abstract on "abstract" declarations.
if (fnDecl.flags & ts.NodeFlags.Abstract) {
if (hasModifierFlag(fnDecl, ts.ModifierFlags.Abstract)) {
newDoc.push({ tagName: 'abstract' });

@@ -329,8 +330,9 @@ }

function Annotator(program, file, options, host, tsOpts) {
_super.call(this, program, file, options);
this.host = host;
this.tsOpts = tsOpts;
var _this = _super.call(this, program, file, options) || this;
_this.host = host;
_this.tsOpts = tsOpts;
/** Exported symbol names that have been generated by expanding an "export * from ...". */
this.generatedExports = new Set();
this.externsWriter = new ExternsWriter(program, file, options);
_this.generatedExports = new Set();
_this.externsWriter = new ExternsWriter(program, file, options);
return _this;
}

@@ -359,3 +361,3 @@ Annotator.prototype.annotate = function () {

Annotator.prototype.maybeProcess = function (node) {
if ((node.flags & ts.NodeFlags.Ambient) || isDtsFileName(this.file.fileName)) {
if ((hasModifierFlag(node, ts.ModifierFlags.Ambient)) || isDtsFileName(this.file.fileName)) {
this.externsWriter.visit(node);

@@ -447,3 +449,3 @@ // An ambient declaration declares types for TypeScript's benefit, so we want to skip Tsickle

if (!fnDecl.body) {
if ((fnDecl.flags & ts.NodeFlags.Abstract) !== 0) {
if (hasModifierFlag(fnDecl, ts.ModifierFlags.Abstract)) {
this.emitFunctionType([fnDecl]);

@@ -663,3 +665,3 @@ // Abstract functions look like

var jsDoc = this.getJSDoc(classDecl) || [];
if ((classDecl.flags & ts.NodeFlags.Abstract) !== 0) {
if (hasModifierFlag(classDecl, ts.ModifierFlags.Abstract)) {
jsDoc.push({ tagName: 'abstract' });

@@ -696,3 +698,3 @@ }

this.emit("\n/** @record */\n");
if (iface.flags & ts.NodeFlags.Export)
if (hasModifierFlag(iface, ts.ModifierFlags.Export))
this.emit('export ');

@@ -733,3 +735,3 @@ var name = rewriter_1.getIdentifierText(iface.name);

var prop = member;
var isStatic = (prop.flags & ts.NodeFlags.Static) !== 0;
var isStatic = hasModifierFlag(prop, ts.ModifierFlags.Static);
if (isStatic) {

@@ -745,3 +747,3 @@ staticProps.push(prop);

var ctor = ctors[0];
paramProps = ctor.parameters.filter(function (p) { return !!(p.flags & VISIBILITY_FLAGS); });
paramProps = ctor.parameters.filter(function (p) { return hasModifierFlag(p, VISIBILITY_FLAGS); });
}

@@ -808,3 +810,3 @@ if (nonStaticProps.length === 0 && paramProps.length === 0 && staticProps.length === 0) {

this.emit("\n/** @typedef {" + this.typeToClosure(node) + "} */\n");
if (node.flags & ts.NodeFlags.Export) {
if (hasModifierFlag(node, ts.ModifierFlags.Export)) {
this.emit('exports.');

@@ -819,3 +821,3 @@ }

Annotator.prototype.maybeProcessEnum = function (node) {
if (node.flags & ts.NodeFlags.Const) {
if (hasModifierFlag(node, ts.ModifierFlags.Const)) {
// const enums disappear after TS compilation and consequently need no

@@ -875,9 +877,8 @@ // help from tsickle.

var name = node.name.getText();
if (node.flags & ts.NodeFlags.Export) {
var isExported = hasModifierFlag(node, ts.ModifierFlags.Export);
if (isExported)
this.emit('export ');
}
this.emit("type " + name + " = number;\n");
if (node.flags & ts.NodeFlags.Export) {
if (isExported)
this.emit('export ');
}
this.emit("let " + name + ": any = {};\n");

@@ -912,3 +913,3 @@ // Emit foo.BAR = 0; lines.

function ExternsWriter() {
_super.apply(this, arguments);
return _super !== null && _super.apply(this, arguments) || this;
}

@@ -915,0 +916,0 @@ /** visit is the main entry point. It generates externs from a ts.Node. */

@@ -9,3 +9,3 @@ /**

"use strict";
var ts = require('typescript');
var ts = require("typescript");
function assertTypeChecked(sourceFile) {

@@ -38,27 +38,9 @@ if (!('resolvedModules' in sourceFile)) {

var basicTypes = [
ts.TypeFlags.Any,
ts.TypeFlags.String,
ts.TypeFlags.Number,
ts.TypeFlags.Boolean,
ts.TypeFlags.Enum,
ts.TypeFlags.StringLiteral,
ts.TypeFlags.NumberLiteral,
ts.TypeFlags.BooleanLiteral,
ts.TypeFlags.EnumLiteral,
ts.TypeFlags.ESSymbol,
ts.TypeFlags.Void,
ts.TypeFlags.Undefined,
ts.TypeFlags.Null,
ts.TypeFlags.Never,
ts.TypeFlags.TypeParameter,
ts.TypeFlags.Class,
ts.TypeFlags.Interface,
ts.TypeFlags.Reference,
ts.TypeFlags.Tuple,
ts.TypeFlags.Union,
ts.TypeFlags.Intersection,
ts.TypeFlags.Anonymous,
ts.TypeFlags.Instantiated,
ts.TypeFlags.ThisType,
ts.TypeFlags.ObjectLiteralPatternWithComputedProperties,
ts.TypeFlags.Any, ts.TypeFlags.String, ts.TypeFlags.Number,
ts.TypeFlags.Boolean, ts.TypeFlags.Enum, ts.TypeFlags.StringLiteral,
ts.TypeFlags.NumberLiteral, ts.TypeFlags.BooleanLiteral, ts.TypeFlags.EnumLiteral,
ts.TypeFlags.ESSymbol, ts.TypeFlags.Void, ts.TypeFlags.Undefined,
ts.TypeFlags.Null, ts.TypeFlags.Never, ts.TypeFlags.TypeParameter,
ts.TypeFlags.Object, ts.TypeFlags.Union, ts.TypeFlags.Intersection,
ts.TypeFlags.Index, ts.TypeFlags.IndexedAccess,
];

@@ -71,2 +53,23 @@ for (var _i = 0, basicTypes_1 = basicTypes; _i < basicTypes_1.length; _i++) {

}
if (type.flags === ts.TypeFlags.Object) {
var objType = type;
var objectFlags = [
ts.ObjectFlags.Class,
ts.ObjectFlags.Interface,
ts.ObjectFlags.Reference,
ts.ObjectFlags.Tuple,
ts.ObjectFlags.Anonymous,
ts.ObjectFlags.Mapped,
ts.ObjectFlags.Instantiated,
ts.ObjectFlags.ObjectLiteral,
ts.ObjectFlags.EvolvingArray,
ts.ObjectFlags.ObjectLiteralPatternWithComputedProperties,
];
for (var _a = 0, objectFlags_1 = objectFlags; _a < objectFlags_1.length; _a++) {
var flag = objectFlags_1[_a];
if ((objType.objectFlags & flag) !== 0) {
debugString += " object:" + ts.ObjectFlags[flag];
}
}
}
if (type.symbol && type.symbol.name !== '__type') {

@@ -166,2 +169,3 @@ debugString += " symbol.name:" + JSON.stringify(type.symbol.name);

writeParameter: writeText,
writeProperty: writeText,
writeSymbol: writeText,

@@ -181,18 +185,31 @@ writeLine: doNothing,

TypeTranslator.prototype.translate = function (type) {
// See the function `buildTypeDisplay` in the TypeScript compiler source
// for guidance on a similar operation.
var _this = this;
// NOTE: type.flags is a single value for primitive types, but sometimes a
// bitwise 'or' of some values for more complex types. We use a switch
// statement for the basics and a series of "if" tests for the complex ones,
// roughly in the same order the flags occur in the TypeFlags enum.
switch (type.flags) {
// NOTE: Though type.flags has the name "flags", it usually can only be one
// of the enum options at a time. This switch handles all the cases in
// the ts.TypeFlags enum in the order they occur.
// NOTE: Some TypeFlags are marked "internal" in the d.ts but still show
// up in the value of type.flags. This mask limits the flag checks to
// the ones in the public API. "lastFlag" here is the last flag handled
// in this switch statement, and should be kept in sync with typescript.d.ts.
var lastFlag = ts.TypeFlags.IndexedAccess;
var mask = (lastFlag << 1) - 1;
switch (type.flags & mask) {
case ts.TypeFlags.Any:
return '?';
case ts.TypeFlags.String:
case ts.TypeFlags.StringLiteral:
return 'string';
case ts.TypeFlags.Number:
case ts.TypeFlags.NumberLiteral:
return 'number';
case ts.TypeFlags.Boolean:
case ts.TypeFlags.BooleanLiteral:
// See the note in translateUnion about booleans.
return 'boolean';
case ts.TypeFlags.Enum:
case ts.TypeFlags.EnumLiteral:
return 'number';
case ts.TypeFlags.ESSymbol:
// NOTE: currently this is just a typedef for {?}, shrug.
// https://github.com/google/closure-compiler/blob/55cf43ee31e80d89d7087af65b5542aa63987874/externs/es3.js#L34
return 'symbol';
case ts.TypeFlags.Void:

@@ -204,18 +221,53 @@ return 'void';

return 'null';
case ts.TypeFlags.EnumLiteral:
case ts.TypeFlags.Enum:
return 'number';
case ts.TypeFlags.StringLiteral:
return 'string';
case ts.TypeFlags.BooleanLiteral:
return 'boolean';
case ts.TypeFlags.NumberLiteral:
return 'number';
case ts.TypeFlags.Never:
this.warn("should not emit a 'never' type");
return '?';
case ts.TypeFlags.TypeParameter:
// This is e.g. the T in a type like Foo<T>.
this.warn("unhandled type flags: " + ts.TypeFlags[type.flags]);
return '?';
case ts.TypeFlags.Object:
return this.translateObject(type);
case ts.TypeFlags.Union:
return this.translateUnion(type);
case ts.TypeFlags.Intersection:
case ts.TypeFlags.Index:
case ts.TypeFlags.IndexedAccess:
// TODO(ts2.1): handle these special types.
this.warn("unhandled type flags: " + ts.TypeFlags[type.flags]);
return '?';
default:
// Continue on to more complex tests below.
break;
// Handle cases where multiple flags are set.
// Booleans are represented as
// ts.TypeFlags.Union | ts.TypeFlags.Boolean
// where the union is a union of true|false.
// Note also that in a more complex union, e.g. boolean|number, then
// it's a union of three things (true|false|number) and
// ts.TypeFlags.Boolean doesn't show up at all.
if (type.flags & ts.TypeFlags.Union) {
return this.translateUnion(type);
}
// The switch statement should have been exhaustive.
throw new Error("unknown type flags: " + type.flags);
}
};
TypeTranslator.prototype.translateUnion = function (type) {
var _this = this;
var parts = type.types.map(function (t) { return _this.translate(t); });
// Union types that include boolean literals and other literals can
// end up repeating the same Closure type. For example: true | boolean
// will be translated to boolean | boolean. Remove duplicates to produce
// types that read better.
parts = parts.filter(function (el, idx) { return parts.indexOf(el) === idx; });
return parts.length === 1 ? parts[0] : "(" + parts.join('|') + ")";
};
// translateObject translates a ts.ObjectType, which is the type of all
// object-like things in TS, such as classes and interfaces.
TypeTranslator.prototype.translateObject = function (type) {
var _this = this;
if (type.symbol && this.isBlackListed(type.symbol))
return '?';
if (type.flags & ts.TypeFlags.Class) {
// NOTE: objectFlags is an enum, but a given type can have multiple flags.
// Array<string> is both ts.ObjectFlags.Reference and ts.ObjectFlags.Interface.
if (type.objectFlags & ts.ObjectFlags.Class) {
if (!type.symbol) {

@@ -227,3 +279,3 @@ this.warn('class has no symbol');

}
else if (type.flags & ts.TypeFlags.Interface) {
else if (type.objectFlags & ts.ObjectFlags.Interface) {
// Note: ts.InterfaceType has a typeParameters field, but that

@@ -251,28 +303,28 @@ // specifies the parameters that the interface type *expects*

}
else if (type.flags & ts.TypeFlags.Reference) {
else if (type.objectFlags & ts.ObjectFlags.Reference) {
// A reference to another type, e.g. Array<number> refers to Array.
// Emit the referenced type and any type arguments.
var referenceType = type;
// A tuple is a ReferenceType where the target is flagged Tuple and the
// typeArguments are the tuple arguments. Just treat it as a mystery
// array, because Closure doesn't understand tuples.
if (referenceType.target.objectFlags & ts.ObjectFlags.Tuple) {
return '!Array<?>';
}
var typeStr = '';
var isTuple = (referenceType.flags & ts.TypeFlags.Tuple) > 0;
// For unknown reasons, tuple types can be reference types containing a
// reference loop. see Destructuring3 in functions.ts.
// TODO(rado): handle tuples in their own branch.
if (!isTuple) {
if (referenceType.target === referenceType) {
// We get into an infinite loop here if the inner reference is
// the same as the outer; this can occur when this function
// fails to translate a more specific type before getting to
// this point.
throw new Error("reference loop in " + typeToDebugString(referenceType) + " " + referenceType.flags);
}
typeStr += this.translate(referenceType.target);
if (referenceType.target === referenceType) {
// We get into an infinite loop here if the inner reference is
// the same as the outer; this can occur when this function
// fails to translate a more specific type before getting to
// this point.
throw new Error("reference loop in " + typeToDebugString(referenceType) + " " + referenceType.flags);
}
typeStr += this.translate(referenceType.target);
if (referenceType.typeArguments) {
var params = referenceType.typeArguments.map(function (t) { return _this.translate(t); });
typeStr += isTuple ? "!Array" : "<" + params.join(', ') + ">";
typeStr += "<" + params.join(', ') + ">";
}
return typeStr;
}
else if (type.flags & ts.TypeFlags.Anonymous) {
else if (type.objectFlags & ts.ObjectFlags.Anonymous) {
if (!type.symbol) {

@@ -299,12 +351,11 @@ // This comes up when generating code for an arrow function as passed

}
else if (type.flags & ts.TypeFlags.Union) {
var unionType = type;
var parts_1 = unionType.types.map(function (t) { return _this.translate(t); });
// In union types that include boolean literal and other literals can
// end up repeating the same closure type. For example: true | boolean
// will be translated to boolean | boolean. Remove duplicates to produce
// types that read better.
parts_1 = parts_1.filter(function (el, idx) { return parts_1.indexOf(el) === idx; });
return parts_1.length === 1 ? parts_1[0] : "(" + parts_1.join('|') + ")";
}
/*
TODO(ts2.1): more unhandled object type flags:
Tuple
Mapped
Instantiated
ObjectLiteral
EvolvingArray
ObjectLiteralPatternWithComputedProperties
*/
this.warn("unhandled type " + typeToDebugString(type));

@@ -311,0 +362,0 @@ return '?';

@@ -1,3 +0,1 @@

"use strict";
var ts = require('typescript');
/**

@@ -10,2 +8,3 @@ * @license

*/
"use strict";
// toArray is a temporary function to help in the use of

@@ -26,60 +25,3 @@ // ES6 maps and sets when running on node 4, which doesn't

exports.toArray = toArray;
/**
* Constructs a new ts.CompilerHost that overlays sources in substituteSource
* over another ts.CompilerHost.
*
* @param substituteSource A map of source file name -> overlay source text.
*/
function createSourceReplacingCompilerHost(substituteSource, delegate) {
return {
getSourceFile: getSourceFile,
getCancellationToken: delegate.getCancellationToken,
getDefaultLibFileName: delegate.getDefaultLibFileName,
writeFile: delegate.writeFile,
getCurrentDirectory: delegate.getCurrentDirectory,
getCanonicalFileName: delegate.getCanonicalFileName,
useCaseSensitiveFileNames: delegate.useCaseSensitiveFileNames,
getNewLine: delegate.getNewLine,
fileExists: delegate.fileExists,
readFile: delegate.readFile,
directoryExists: delegate.directoryExists,
getDirectories: delegate.getDirectories,
};
function getSourceFile(fileName, languageVersion, onError) {
var path = ts.sys.resolvePath(fileName);
var sourceText = substituteSource.get(path);
if (sourceText) {
return ts.createSourceFile(fileName, sourceText, languageVersion);
}
return delegate.getSourceFile(path, languageVersion, onError);
}
}
exports.createSourceReplacingCompilerHost = createSourceReplacingCompilerHost;
/**
* Constructs a new ts.CompilerHost that overlays sources in substituteSource
* over another ts.CompilerHost.
*
* @param outputFiles map to fill with source file name -> output text.
*/
function createOutputRetainingCompilerHost(outputFiles, delegate) {
return {
getSourceFile: delegate.getSourceFile,
getCancellationToken: delegate.getCancellationToken,
getDefaultLibFileName: delegate.getDefaultLibFileName,
writeFile: writeFile,
getCurrentDirectory: delegate.getCurrentDirectory,
getCanonicalFileName: delegate.getCanonicalFileName,
useCaseSensitiveFileNames: delegate.useCaseSensitiveFileNames,
getNewLine: delegate.getNewLine,
fileExists: delegate.fileExists,
readFile: delegate.readFile,
directoryExists: delegate.directoryExists,
getDirectories: delegate.getDirectories,
};
function writeFile(fileName, content, writeByteOrderMark, onError, sourceFiles) {
outputFiles.set(fileName, content);
}
}
exports.createOutputRetainingCompilerHost = createOutputRetainingCompilerHost;
//# sourceMappingURL=util.js.map
{
"name": "tsickle",
"version": "0.2.4",
"version": "0.21.0",
"description": "Transpile TypeScript code to JavaScript with Closure annotations.",

@@ -18,3 +18,3 @@ "main": "build/src/tsickle.js",

"peerDependencies": {
"typescript": "^2.0.0"
"typescript": "^2.1.0"
},

@@ -46,3 +46,3 @@ "devDependencies": {

"tslint": "^3.15.1",
"typescript": "~2.0.0"
"typescript": "2.1.5"
},

@@ -49,0 +49,0 @@ "scripts": {

@@ -52,2 +52,5 @@ /**

break;
// PropertyAccessExpression is intentionally missing here,
// because the rest of the rewriter does not handle such
// expressions.
default:

@@ -62,3 +65,29 @@ return false;

}
return decSym.getDocumentationComment().some(c => c.text.indexOf('@Annotation') >= 0);
for (let d of decSym.getDeclarations()) {
// Switch to the TS JSDoc parser in the future to avoid false positives here.
// For example using '@Annotation' in a true comment.
// However, a new TS API would be needed, track at
// https://github.com/Microsoft/TypeScript/issues/7393.
let commentNode: ts.Node = d;
// Not handling PropertyAccess expressions here, because they are
// filtered earlier.
if (commentNode.kind === ts.SyntaxKind.VariableDeclaration) {
if (!commentNode.parent) return false;
commentNode = commentNode.parent;
}
// Go up one more level to VariableDeclarationStatement, where usually
// the comment lives. If the declaration has an 'export', the
// VDList.getFullText will not contain the comment.
if (commentNode.kind === ts.SyntaxKind.VariableDeclarationList) {
if (!commentNode.parent) return false;
commentNode = commentNode.parent;
}
let range = ts.getLeadingCommentRanges(commentNode.getFullText(), 0);
if (!range) return;
for (let {pos, end} of range) {
let jsDocText = commentNode.getFullText().substring(pos, end);
if (jsDocText.includes('@Annotation')) return true;
}
}
return false;
}

@@ -65,0 +94,0 @@

@@ -155,2 +155,15 @@ /**

continue;
} else if (!arrayIncludes(JSDOC_TAGS_WHITELIST, tagName)) {
// Silently drop tags we don't understand. This is a subtle
// compromise between multiple issues.
// 1) If we pass through these non-Closure tags, the user will
// get a warning from Closure, and the point of tsickle is
// to insulate the user from Closure.
// 2) The output of tsickle is for Closure only, so we don't
// care if we drop tags that Closure doesn't undersand.
// 3) Finally, we don't want to warn because users should be
// free to add whichever JSDoc they feel like. If the user
// wants help ensuring they didn't typo a tag, that is the
// responsibility of a linter.
continue;
}

@@ -175,4 +188,3 @@

} else {
const lastTag = tags[tags.length - 1];
lastTag.text = (lastTag.text || '') + '\n' + line;
tags[tags.length - 1].text += '\n' + line;
}

@@ -194,18 +206,3 @@ }

if (tag.tagName) {
if (!arrayIncludes(JSDOC_TAGS_WHITELIST, tag.tagName)) {
// Escape tags we don't understand. This is a subtle
// compromise between multiple issues.
// 1) If we pass through these non-Closure tags, the user will
// get a warning from Closure, and the point of tsickle is
// to insulate the user from Closure.
// 2) The output of tsickle is for Closure but also may be read
// by humans, for example non-TypeScript users of Angular.
// 3) Finally, we don't want to warn because users should be
// free to add whichever JSDoc they feel like. If the user
// wants help ensuring they didn't typo a tag, that is the
// responsibility of a linter.
out += ` \\@${tag.tagName}`;
} else {
out += ` @${tag.tagName}`;
}
out += ` @${tag.tagName}`;
}

@@ -212,0 +209,0 @@ if (tag.type) {

@@ -19,6 +19,6 @@ #!/usr/bin/env node

import * as tsickle from './tsickle';
import {toArray, createOutputRetainingCompilerHost} from './util';
import {toArray} from './util';
/** Tsickle settings passed on the command line. */
export interface Settings {
interface Settings {
/** If provided, path to save externs to. */

@@ -129,6 +129,41 @@ externsPath?: string;

/**
* Constructs a new ts.CompilerHost that overlays sources in substituteSource
* over another ts.CompilerHost.
*
* @param substituteSource A map of source file name -> overlay source text.
*/
function createSourceReplacingCompilerHost(
substituteSource: Map<string, string>, delegate: ts.CompilerHost): ts.CompilerHost {
return {
getSourceFile,
getCancellationToken: delegate.getCancellationToken,
getDefaultLibFileName: delegate.getDefaultLibFileName,
writeFile: delegate.writeFile,
getCurrentDirectory: delegate.getCurrentDirectory,
getCanonicalFileName: delegate.getCanonicalFileName,
useCaseSensitiveFileNames: delegate.useCaseSensitiveFileNames,
getNewLine: delegate.getNewLine,
fileExists: delegate.fileExists,
readFile: delegate.readFile,
directoryExists: delegate.directoryExists,
getDirectories: delegate.getDirectories,
};
function getSourceFile(
fileName: string, languageVersion: ts.ScriptTarget,
onError?: (message: string) => void): ts.SourceFile {
let path: string = ts.sys.resolvePath(fileName);
let sourceText = substituteSource.get(path);
if (sourceText) {
return ts.createSourceFile(path, sourceText, languageVersion);
}
return delegate.getSourceFile(path, languageVersion, onError);
}
}
/**
* Compiles TypeScript code into Closure-compiler-ready JS.
* Doesn't write any files to disk; all JS content is returned in a map.
*/
export function toClosureJS(
function toClosureJS(
options: ts.CompilerOptions, fileNames: string[], settings: Settings,

@@ -149,26 +184,39 @@ allDiagnostics: ts.Diagnostic[]): {jsFiles: Map<string, string>, externs: string}|null {

const tsickleCompilerHostOptions: tsickle.TsickleCompilerHostOptions = {
googmodule: true,
es5Mode: false,
tsickleTyped: !settings.isUntyped,
prelude: '',
const tsickleOptions: tsickle.Options = {
untyped: settings.isUntyped,
logWarning: settings.verbose ?
(warning: ts.Diagnostic) => {
console.error(tsickle.formatDiagnostics([warning]));
} :
undefined,
};
const tsickleEnvironment: tsickle.TsickleEnvironment = {
shouldSkipTsickleProcessing: (fileName) => fileNames.indexOf(fileName) === -1,
pathToModuleName: cliSupport.pathToModuleName,
shouldIgnoreWarningsForPath: (filePath) => false,
fileNameToModuleId: (fileName) => fileName,
};
// Process each input file with tsickle and save the output.
const tsickleOutput = new Map<string, string>();
let tsickleExterns = '';
for (let fileName of fileNames) {
let {output, externs, diagnostics} =
tsickle.annotate(program, program.getSourceFile(fileName), tsickleOptions);
if (diagnostics.length > 0) {
allDiagnostics.push(...diagnostics);
return null;
}
tsickleOutput.set(ts.sys.resolvePath(fileName), output);
if (externs) {
tsickleExterns += externs;
}
}
const jsFiles = new Map<string, string>();
const hostDelegate = createOutputRetainingCompilerHost(jsFiles, ts.createCompilerHost(options));
// Reparse and reload the program, inserting the tsickle output in
// place of the original source.
let host = new tsickle.TsickleCompilerHost(
hostDelegate, tsickleCompilerHostOptions, tsickleEnvironment, program, tsickle.Pass.Tsickle);
let host = createSourceReplacingCompilerHost(tsickleOutput, ts.createCompilerHost(options));
program = ts.createProgram(fileNames, options, host);
let {diagnostics} = program.emit(undefined);
// Emit, creating a map of fileName => generated JS source.
const jsFiles = new Map<string, string>();
function writeFile(fileName: string, data: string): void {
jsFiles.set(fileName, data);
}
let {diagnostics} = program.emit(undefined, writeFile);
if (diagnostics.length > 0) {

@@ -179,3 +227,12 @@ allDiagnostics.push(...diagnostics);

return {jsFiles, externs: host.getGeneratedExterns()};
for (let fileName of toArray(jsFiles.keys())) {
if (path.extname(fileName) !== '.map') {
const moduleId = fileName; // TODO: does anyone use this?
let {output} = tsickle.processES5(
fileName, moduleId, jsFiles.get(fileName)!, cliSupport.pathToModuleName);
jsFiles.set(fileName, output);
}
}
return {jsFiles, externs: tsickleExterns};
}

@@ -182,0 +239,0 @@

@@ -19,4 +19,2 @@ /**

export {processES5} from './es5processor';
export {FileMap, ModulesManifest} from './modules_manifest';
export {Pass, TsickleCompilerHost, TsickleCompilerHostOptions, TsickleEnvironment} from './tsickle_compiler_host';

@@ -90,2 +88,7 @@ export interface Options {

/** @return true if node has the specified modifier flag set. */
function hasModifierFlag(node: ts.Node, flag: ts.ModifierFlags): boolean {
return (ts.getCombinedModifierFlags(node) & flag) !== 0;
}
/**

@@ -130,8 +133,10 @@ * TypeScript allows you to write identifiers quoted, like:

default:
// The above list of kinds should be exhaustive.
throw new Error(`unhandled function parameter kind: ${ts.SyntaxKind[param.name.kind]}`);
// The above list of kinds is exhaustive. param.name is 'never' at this point.
let paramName = param.name as ts.Node;
throw new Error(`unhandled function parameter kind: ${ts.SyntaxKind[paramName.kind]}`);
}
}
const VISIBILITY_FLAGS = ts.NodeFlags.Private | ts.NodeFlags.Protected | ts.NodeFlags.Public;
const VISIBILITY_FLAGS: ts.ModifierFlags =
ts.ModifierFlags.Private | ts.ModifierFlags.Protected | ts.ModifierFlags.Public;

@@ -191,3 +196,3 @@ /**

// Add @abstract on "abstract" declarations.
if (fnDecl.flags & ts.NodeFlags.Abstract) {
if (hasModifierFlag(fnDecl, ts.ModifierFlags.Abstract)) {
newDoc.push({tagName: 'abstract'});

@@ -408,3 +413,3 @@ }

maybeProcess(node: ts.Node): boolean {
if ((node.flags & ts.NodeFlags.Ambient) || isDtsFileName(this.file.fileName)) {
if ((hasModifierFlag(node, ts.ModifierFlags.Ambient)) || isDtsFileName(this.file.fileName)) {
this.externsWriter.visit(node);

@@ -495,3 +500,3 @@ // An ambient declaration declares types for TypeScript's benefit, so we want to skip Tsickle

if (!fnDecl.body) {
if ((fnDecl.flags & ts.NodeFlags.Abstract) !== 0) {
if (hasModifierFlag(fnDecl, ts.ModifierFlags.Abstract)) {
this.emitFunctionType([fnDecl]);

@@ -723,3 +728,3 @@ // Abstract functions look like

let jsDoc = this.getJSDoc(classDecl) || [];
if ((classDecl.flags & ts.NodeFlags.Abstract) !== 0) {
if (hasModifierFlag(classDecl, ts.ModifierFlags.Abstract)) {
jsDoc.push({tagName: 'abstract'});

@@ -754,3 +759,3 @@ }

this.emit(`\n/** @record */\n`);
if (iface.flags & ts.NodeFlags.Export) this.emit('export ');
if (hasModifierFlag(iface, ts.ModifierFlags.Export)) this.emit('export ');
let name = getIdentifierText(iface.name);

@@ -788,3 +793,3 @@ this.emit(`function ${name}() {}\n`);

let prop = member as ts.PropertyDeclaration;
let isStatic = (prop.flags & ts.NodeFlags.Static) !== 0;
let isStatic = hasModifierFlag(prop, ts.ModifierFlags.Static);
if (isStatic) {

@@ -800,3 +805,3 @@ staticProps.push(prop);

let ctor = ctors[0];
paramProps = ctor.parameters.filter((p) => !!(p.flags & VISIBILITY_FLAGS));
paramProps = ctor.parameters.filter(p => hasModifierFlag(p, VISIBILITY_FLAGS));
}

@@ -866,3 +871,3 @@

this.emit(`\n/** @typedef {${this.typeToClosure(node)}} */\n`);
if (node.flags & ts.NodeFlags.Export) {
if (hasModifierFlag(node, ts.ModifierFlags.Export)) {
this.emit('exports.');

@@ -877,3 +882,3 @@ } else {

private maybeProcessEnum(node: ts.EnumDeclaration): boolean {
if (node.flags & ts.NodeFlags.Const) {
if (hasModifierFlag(node, ts.ModifierFlags.Const)) {
// const enums disappear after TS compilation and consequently need no

@@ -932,9 +937,6 @@ // help from tsickle.

let name = node.name.getText();
if (node.flags & ts.NodeFlags.Export) {
this.emit('export ');
}
const isExported = hasModifierFlag(node, ts.ModifierFlags.Export);
if (isExported) this.emit('export ');
this.emit(`type ${name} = number;\n`);
if (node.flags & ts.NodeFlags.Export) {
this.emit('export ');
}
if (isExported) this.emit('export ');
this.emit(`let ${name}: any = {};\n`);

@@ -941,0 +943,0 @@

@@ -40,27 +40,9 @@ /**

const basicTypes: ts.TypeFlags[] = [
ts.TypeFlags.Any,
ts.TypeFlags.String,
ts.TypeFlags.Number,
ts.TypeFlags.Boolean,
ts.TypeFlags.Enum,
ts.TypeFlags.StringLiteral,
ts.TypeFlags.NumberLiteral,
ts.TypeFlags.BooleanLiteral,
ts.TypeFlags.EnumLiteral,
ts.TypeFlags.ESSymbol,
ts.TypeFlags.Void,
ts.TypeFlags.Undefined,
ts.TypeFlags.Null,
ts.TypeFlags.Never,
ts.TypeFlags.TypeParameter,
ts.TypeFlags.Class,
ts.TypeFlags.Interface,
ts.TypeFlags.Reference,
ts.TypeFlags.Tuple,
ts.TypeFlags.Union,
ts.TypeFlags.Intersection,
ts.TypeFlags.Anonymous,
ts.TypeFlags.Instantiated,
ts.TypeFlags.ThisType,
ts.TypeFlags.ObjectLiteralPatternWithComputedProperties,
ts.TypeFlags.Any, ts.TypeFlags.String, ts.TypeFlags.Number,
ts.TypeFlags.Boolean, ts.TypeFlags.Enum, ts.TypeFlags.StringLiteral,
ts.TypeFlags.NumberLiteral, ts.TypeFlags.BooleanLiteral, ts.TypeFlags.EnumLiteral,
ts.TypeFlags.ESSymbol, ts.TypeFlags.Void, ts.TypeFlags.Undefined,
ts.TypeFlags.Null, ts.TypeFlags.Never, ts.TypeFlags.TypeParameter,
ts.TypeFlags.Object, ts.TypeFlags.Union, ts.TypeFlags.Intersection,
ts.TypeFlags.Index, ts.TypeFlags.IndexedAccess,
];

@@ -73,2 +55,23 @@ for (let flag of basicTypes) {

if (type.flags === ts.TypeFlags.Object) {
const objType = type as ts.ObjectType;
const objectFlags: ts.ObjectFlags[] = [
ts.ObjectFlags.Class,
ts.ObjectFlags.Interface,
ts.ObjectFlags.Reference,
ts.ObjectFlags.Tuple,
ts.ObjectFlags.Anonymous,
ts.ObjectFlags.Mapped,
ts.ObjectFlags.Instantiated,
ts.ObjectFlags.ObjectLiteral,
ts.ObjectFlags.EvolvingArray,
ts.ObjectFlags.ObjectLiteralPatternWithComputedProperties,
];
for (let flag of objectFlags) {
if ((objType.objectFlags & flag) !== 0) {
debugString += ` object:${ts.ObjectFlags[flag]}`;
}
}
}
if (type.symbol && type.symbol.name !== '__type') {

@@ -172,2 +175,3 @@ debugString += ` symbol.name:${JSON.stringify(type.symbol.name)}`;

writeParameter: writeText,
writeProperty: writeText,
writeSymbol: writeText,

@@ -188,18 +192,31 @@ writeLine: doNothing,

translate(type: ts.Type): string {
// See the function `buildTypeDisplay` in the TypeScript compiler source
// for guidance on a similar operation.
// NOTE: type.flags is a single value for primitive types, but sometimes a
// bitwise 'or' of some values for more complex types. We use a switch
// statement for the basics and a series of "if" tests for the complex ones,
// roughly in the same order the flags occur in the TypeFlags enum.
switch (type.flags) {
// NOTE: Though type.flags has the name "flags", it usually can only be one
// of the enum options at a time. This switch handles all the cases in
// the ts.TypeFlags enum in the order they occur.
// NOTE: Some TypeFlags are marked "internal" in the d.ts but still show
// up in the value of type.flags. This mask limits the flag checks to
// the ones in the public API. "lastFlag" here is the last flag handled
// in this switch statement, and should be kept in sync with typescript.d.ts.
let lastFlag = ts.TypeFlags.IndexedAccess;
let mask = (lastFlag << 1) - 1;
switch (type.flags & mask) {
case ts.TypeFlags.Any:
return '?';
case ts.TypeFlags.String:
case ts.TypeFlags.StringLiteral:
return 'string';
case ts.TypeFlags.Number:
case ts.TypeFlags.NumberLiteral:
return 'number';
case ts.TypeFlags.Boolean:
case ts.TypeFlags.BooleanLiteral:
// See the note in translateUnion about booleans.
return 'boolean';
case ts.TypeFlags.Enum:
case ts.TypeFlags.EnumLiteral:
return 'number';
case ts.TypeFlags.ESSymbol:
// NOTE: currently this is just a typedef for {?}, shrug.
// https://github.com/google/closure-compiler/blob/55cf43ee31e80d89d7087af65b5542aa63987874/externs/es3.js#L34
return 'symbol';
case ts.TypeFlags.Void:

@@ -211,19 +228,56 @@ return 'void';

return 'null';
case ts.TypeFlags.EnumLiteral:
case ts.TypeFlags.Enum:
return 'number';
case ts.TypeFlags.StringLiteral:
return 'string';
case ts.TypeFlags.BooleanLiteral:
return 'boolean';
case ts.TypeFlags.NumberLiteral:
return 'number';
case ts.TypeFlags.Never:
this.warn(`should not emit a 'never' type`);
return '?';
case ts.TypeFlags.TypeParameter:
// This is e.g. the T in a type like Foo<T>.
this.warn(`unhandled type flags: ${ts.TypeFlags[type.flags]}`);
return '?';
case ts.TypeFlags.Object:
return this.translateObject(type as ts.ObjectType);
case ts.TypeFlags.Union:
return this.translateUnion(type as ts.UnionType);
case ts.TypeFlags.Intersection:
case ts.TypeFlags.Index:
case ts.TypeFlags.IndexedAccess:
// TODO(ts2.1): handle these special types.
this.warn(`unhandled type flags: ${ts.TypeFlags[type.flags]}`);
return '?';
default:
// Continue on to more complex tests below.
break;
// Handle cases where multiple flags are set.
// Booleans are represented as
// ts.TypeFlags.Union | ts.TypeFlags.Boolean
// where the union is a union of true|false.
// Note also that in a more complex union, e.g. boolean|number, then
// it's a union of three things (true|false|number) and
// ts.TypeFlags.Boolean doesn't show up at all.
if (type.flags & ts.TypeFlags.Union) {
return this.translateUnion(type as ts.UnionType);
}
// The switch statement should have been exhaustive.
throw new Error(`unknown type flags: ${type.flags}`);
}
}
private translateUnion(type: ts.UnionType): string {
let parts = type.types.map(t => this.translate(t));
// Union types that include boolean literals and other literals can
// end up repeating the same Closure type. For example: true | boolean
// will be translated to boolean | boolean. Remove duplicates to produce
// types that read better.
parts = parts.filter((el, idx) => parts.indexOf(el) === idx);
return parts.length === 1 ? parts[0] : `(${parts.join('|')})`;
}
// translateObject translates a ts.ObjectType, which is the type of all
// object-like things in TS, such as classes and interfaces.
private translateObject(type: ts.ObjectType): string {
if (type.symbol && this.isBlackListed(type.symbol)) return '?';
if (type.flags & ts.TypeFlags.Class) {
// NOTE: objectFlags is an enum, but a given type can have multiple flags.
// Array<string> is both ts.ObjectFlags.Reference and ts.ObjectFlags.Interface.
if (type.objectFlags & ts.ObjectFlags.Class) {
if (!type.symbol) {

@@ -234,3 +288,3 @@ this.warn('class has no symbol');

return '!' + this.symbolToString(type.symbol);
} else if (type.flags & ts.TypeFlags.Interface) {
} else if (type.objectFlags & ts.ObjectFlags.Interface) {
// Note: ts.InterfaceType has a typeParameters field, but that

@@ -257,3 +311,3 @@ // specifies the parameters that the interface type *expects*

return '!' + this.symbolToString(type.symbol);
} else if (type.flags & ts.TypeFlags.Reference) {
} else if (type.objectFlags & ts.ObjectFlags.Reference) {
// A reference to another type, e.g. Array<number> refers to Array.

@@ -263,24 +317,25 @@ // Emit the referenced type and any type arguments.

// A tuple is a ReferenceType where the target is flagged Tuple and the
// typeArguments are the tuple arguments. Just treat it as a mystery
// array, because Closure doesn't understand tuples.
if (referenceType.target.objectFlags & ts.ObjectFlags.Tuple) {
return '!Array<?>';
}
let typeStr = '';
let isTuple = (referenceType.flags & ts.TypeFlags.Tuple) > 0;
// For unknown reasons, tuple types can be reference types containing a
// reference loop. see Destructuring3 in functions.ts.
// TODO(rado): handle tuples in their own branch.
if (!isTuple) {
if (referenceType.target === referenceType) {
// We get into an infinite loop here if the inner reference is
// the same as the outer; this can occur when this function
// fails to translate a more specific type before getting to
// this point.
throw new Error(
`reference loop in ${typeToDebugString(referenceType)} ${referenceType.flags}`);
}
typeStr += this.translate(referenceType.target);
if (referenceType.target === referenceType) {
// We get into an infinite loop here if the inner reference is
// the same as the outer; this can occur when this function
// fails to translate a more specific type before getting to
// this point.
throw new Error(
`reference loop in ${typeToDebugString(referenceType)} ${referenceType.flags}`);
}
typeStr += this.translate(referenceType.target);
if (referenceType.typeArguments) {
let params = referenceType.typeArguments.map(t => this.translate(t));
typeStr += isTuple ? `!Array` : `<${params.join(', ')}>`;
typeStr += `<${params.join(', ')}>`;
}
return typeStr;
} else if (type.flags & ts.TypeFlags.Anonymous) {
} else if (type.objectFlags & ts.ObjectFlags.Anonymous) {
if (!type.symbol) {

@@ -307,12 +362,13 @@ // This comes up when generating code for an arrow function as passed

return '?';
} else if (type.flags & ts.TypeFlags.Union) {
let unionType = type as ts.UnionType;
let parts = unionType.types.map(t => this.translate(t));
// In union types that include boolean literal and other literals can
// end up repeating the same closure type. For example: true | boolean
// will be translated to boolean | boolean. Remove duplicates to produce
// types that read better.
parts = parts.filter((el, idx) => parts.indexOf(el) === idx);
return parts.length === 1 ? parts[0] : `(${parts.join('|')})`;
}
/*
TODO(ts2.1): more unhandled object type flags:
Tuple
Mapped
Instantiated
ObjectLiteral
EvolvingArray
ObjectLiteralPatternWithComputedProperties
*/
this.warn(`unhandled type ${typeToDebugString(type)}`);

@@ -319,0 +375,0 @@ return '?';

@@ -1,3 +0,1 @@

import * as ts from 'typescript';
/**

@@ -26,66 +24,1 @@ * @license

}
/**
* Constructs a new ts.CompilerHost that overlays sources in substituteSource
* over another ts.CompilerHost.
*
* @param substituteSource A map of source file name -> overlay source text.
*/
export function createSourceReplacingCompilerHost(
substituteSource: Map<string, string>, delegate: ts.CompilerHost): ts.CompilerHost {
return {
getSourceFile,
getCancellationToken: delegate.getCancellationToken,
getDefaultLibFileName: delegate.getDefaultLibFileName,
writeFile: delegate.writeFile,
getCurrentDirectory: delegate.getCurrentDirectory,
getCanonicalFileName: delegate.getCanonicalFileName,
useCaseSensitiveFileNames: delegate.useCaseSensitiveFileNames,
getNewLine: delegate.getNewLine,
fileExists: delegate.fileExists,
readFile: delegate.readFile,
directoryExists: delegate.directoryExists,
getDirectories: delegate.getDirectories,
};
function getSourceFile(
fileName: string, languageVersion: ts.ScriptTarget,
onError?: (message: string) => void): ts.SourceFile {
let path: string = ts.sys.resolvePath(fileName);
let sourceText = substituteSource.get(path);
if (sourceText) {
return ts.createSourceFile(fileName, sourceText, languageVersion);
}
return delegate.getSourceFile(path, languageVersion, onError);
}
}
/**
* Constructs a new ts.CompilerHost that overlays sources in substituteSource
* over another ts.CompilerHost.
*
* @param outputFiles map to fill with source file name -> output text.
*/
export function createOutputRetainingCompilerHost(
outputFiles: Map<string, string>, delegate: ts.CompilerHost): ts.CompilerHost {
return {
getSourceFile: delegate.getSourceFile,
getCancellationToken: delegate.getCancellationToken,
getDefaultLibFileName: delegate.getDefaultLibFileName,
writeFile: writeFile,
getCurrentDirectory: delegate.getCurrentDirectory,
getCanonicalFileName: delegate.getCanonicalFileName,
useCaseSensitiveFileNames: delegate.useCaseSensitiveFileNames,
getNewLine: delegate.getNewLine,
fileExists: delegate.fileExists,
readFile: delegate.readFile,
directoryExists: delegate.directoryExists,
getDirectories: delegate.getDirectories,
};
function writeFile(
fileName: string, content: string, writeByteOrderMark: boolean,
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]): void {
outputFiles.set(fileName, content);
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc