Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

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.21.3 to 0.21.4

build/definitions/decorators.d.ts

2

build/definitions/jsdoc.d.ts

@@ -32,4 +32,4 @@ /**

/** Serializes a Comment out to a string usable in source code. */
export declare function toString(tags: Tag[]): string;
export declare function toString(tags: Tag[], escapeExtraTags?: string[]): string;
/** Merges multiple tags (of the same tagName type) into a single unified tag. */
export declare function merge(tags: Tag[]): Tag;
import * as ts from 'typescript';
import * as tsickle from './tsickle';
/** Tsickle settings passed on the command line. */

@@ -11,2 +12,8 @@ export interface Settings {

}
export interface ClosureJSOptions {
tsickleCompilerHostOptions: tsickle.Options;
tsickleHost: tsickle.TsickleHost;
files: Map<string, string>;
tsicklePasses: tsickle.Pass[];
}
/**

@@ -16,5 +23,5 @@ * Compiles TypeScript code into Closure-compiler-ready JS.

*/
export declare function toClosureJS(options: ts.CompilerOptions, fileNames: string[], settings: Settings, allDiagnostics: ts.Diagnostic[], files?: Map<string, string>): {
export declare function toClosureJS(options: ts.CompilerOptions, fileNames: string[], settings: Settings, allDiagnostics: ts.Diagnostic[], partialClosureJSOptions?: Partial<ClosureJSOptions>): {
jsFiles: Map<string, string>;
externs: string;
} | null;

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

var ts = require("typescript");
var decorators_1 = require("./decorators");
var rewriter_1 = require("./rewriter");

@@ -34,24 +35,3 @@ var type_translator_1 = require("./type-translator");

ClassRewriter.prototype.shouldLower = function (decorator) {
// Walk down the expression to find the identifier of the decorator function
var node = decorator;
while (node.kind !== ts.SyntaxKind.Identifier) {
switch (node.kind) {
case ts.SyntaxKind.Decorator:
node = node.expression;
break;
case ts.SyntaxKind.CallExpression:
node = node.expression;
break;
// PropertyAccessExpression is intentionally missing here,
// because the rest of the rewriter does not handle such
// expressions.
default:
return false;
}
}
var decSym = this.typeChecker.getSymbolAtLocation(node);
if (decSym.flags & ts.SymbolFlags.Alias) {
decSym = this.typeChecker.getAliasedSymbol(decSym);
}
for (var _i = 0, _a = decSym.getDeclarations(); _i < _a.length; _i++) {
for (var _i = 0, _a = decorators_1.getDecoratorDeclarations(decorator, this.typeChecker); _i < _a.length; _i++) {
var d = _a[_i];

@@ -248,4 +228,4 @@ // Switch to the TS JSDoc parser in the future to avoid false positives here.

this.emit('decorators: [');
for (var _d = 0, decorators_1 = decorators; _d < decorators_1.length; _d++) {
var decorator = decorators_1[_d];
for (var _d = 0, decorators_2 = decorators; _d < decorators_2.length; _d++) {
var decorator = decorators_2[_d];
this.emitDecorator(decorator);

@@ -252,0 +232,0 @@ this.emit(', ');

@@ -167,6 +167,8 @@ /**

*/
function tagToString(tag) {
function tagToString(tag, escapeExtraTags) {
if (escapeExtraTags === void 0) { escapeExtraTags = []; }
var out = '';
if (tag.tagName) {
if (!arrayIncludes(JSDOC_TAGS_WHITELIST, tag.tagName)) {
if (!arrayIncludes(JSDOC_TAGS_WHITELIST, tag.tagName) ||
arrayIncludes(escapeExtraTags, tag.tagName)) {
// Escape tags we don't understand. This is a subtle

@@ -209,3 +211,4 @@ // compromise between multiple issues.

/** Serializes a Comment out to a string usable in source code. */
function toString(tags) {
function toString(tags, escapeExtraTags) {
if (escapeExtraTags === void 0) { escapeExtraTags = []; }
if (tags.length === 0)

@@ -218,3 +221,3 @@ return '';

// /** @type {foo} */
return '/**' + tagToString(tag) + ' */\n';
return '/**' + tagToString(tag, escapeExtraTags) + ' */\n';
}

@@ -228,3 +231,3 @@ }

// If the tagToString is multi-line, insert " * " prefixes on subsequent lines.
out += tagToString(tag).split('\n').join('\n * ');
out += tagToString(tag, escapeExtraTags).split('\n').join('\n * ');
out += '\n';

@@ -231,0 +234,0 @@ }

#!/usr/bin/env node
"use strict";
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
var fs = require("fs");

@@ -89,2 +97,19 @@ var minimist = require("minimist");

}
function getDefaultClosureJSOptions(fileNames, settings) {
return {
tsickleCompilerHostOptions: {
googmodule: true,
es5Mode: false,
untyped: settings.isUntyped,
},
tsickleHost: {
shouldSkipTsickleProcessing: function (fileName) { return fileNames.indexOf(fileName) === -1; },
pathToModuleName: cliSupport.pathToModuleName,
shouldIgnoreWarningsForPath: function (filePath) { return false; },
fileNameToModuleId: function (fileName) { return fileName; },
},
files: new Map(),
tsicklePasses: [tsickle.Pass.CLOSURIZE],
};
}
/**

@@ -94,4 +119,5 @@ * Compiles TypeScript code into Closure-compiler-ready JS.

*/
function toClosureJS(options, fileNames, settings, allDiagnostics, files) {
if (files === void 0) { files = new Map(); }
function toClosureJS(options, fileNames, settings, allDiagnostics, partialClosureJSOptions) {
if (partialClosureJSOptions === void 0) { partialClosureJSOptions = {}; }
var closureJSOptions = __assign({}, getDefaultClosureJSOptions(fileNames, settings), partialClosureJSOptions);
// Parse and load the program without tsickle processing.

@@ -101,17 +127,6 @@ // This is so:

// - tsickle can use the result of typechecking for annotation
var tsickleCompilerHostOptions = {
googmodule: true,
es5Mode: false,
untyped: settings.isUntyped,
};
var tsickleHost = {
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 outputRetainingHost = util_1.createOutputRetainingCompilerHost(jsFiles, ts.createCompilerHost(options));
var sourceReplacingHost = util_1.createSourceReplacingCompilerHost(files, outputRetainingHost);
var tch = new tsickle.TsickleCompilerHost(sourceReplacingHost, options, tsickleCompilerHostOptions, tsickleHost);
var sourceReplacingHost = util_1.createSourceReplacingCompilerHost(closureJSOptions.files, outputRetainingHost);
var tch = new tsickle.TsickleCompilerHost(sourceReplacingHost, options, closureJSOptions.tsickleCompilerHostOptions, closureJSOptions.tsickleHost);
var program = ts.createProgram(fileNames, options, tch);

@@ -127,4 +142,10 @@ {

// place of the original source.
tch.reconfigureForRun(program, tsickle.Pass.CLOSURIZE);
program = ts.createProgram(fileNames, options, tch);
if (closureJSOptions.tsicklePasses.indexOf(tsickle.Pass.DECORATOR_DOWNLEVEL) !== -1) {
tch.reconfigureForRun(program, tsickle.Pass.DECORATOR_DOWNLEVEL);
program = ts.createProgram(fileNames, options, tch);
}
if (closureJSOptions.tsicklePasses.indexOf(tsickle.Pass.CLOSURIZE) !== -1) {
tch.reconfigureForRun(program, tsickle.Pass.CLOSURIZE);
program = ts.createProgram(fileNames, options, tch);
}
var diagnostics = program.emit(undefined).diagnostics;

@@ -131,0 +152,0 @@ if (diagnostics.length > 0) {

@@ -75,8 +75,8 @@ "use strict";

if (path.extname(fileName) !== '.map') {
if (!tsickle_1.isDtsFileName(fileName) && this.tscOptions.inlineSourceMap) {
content = this.combineInlineSourceMaps(fileName, content);
}
if (this.options.googmodule && !tsickle_1.isDtsFileName(fileName)) {
content = this.convertCommonJsToGoogModule(fileName, content);
}
if (!tsickle_1.isDtsFileName(fileName) && this.tscOptions.inlineSourceMap) {
content = this.combineInlineSourceMaps(fileName, content);
}
}

@@ -93,3 +93,3 @@ else {

TsickleCompilerHost.prototype.getSourceMapKeyForSourceFile = function (sourceFile) {
return this.getCanonicalFileName(sourceFile.path);
return this.getCanonicalFileName(path.resolve(sourceFile.path));
};

@@ -147,4 +147,6 @@ TsickleCompilerHost.prototype.stripAndStoreExistingSourceMap = function (sourceFile) {

var preexistingSourceMapGenerator = this.preexistingSourceMaps.get(sourceMapKey);
var preexistingSourceMapConsumer = sourceMapUtils.sourceMapGeneratorToConsumerWithFileName(preexistingSourceMapGenerator, sourceFileName);
tscSourceMapGenerator.applySourceMap(preexistingSourceMapConsumer);
if (preexistingSourceMapGenerator) {
var preexistingSourceMapConsumer = new source_map_1.SourceMapConsumer(preexistingSourceMapGenerator.toJSON());
tscSourceMapGenerator.applySourceMap(preexistingSourceMapConsumer);
}
}

@@ -151,0 +153,0 @@ }

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

var ts = require("typescript");
var decorators_1 = require("./decorators");
var jsdoc = require("./jsdoc");

@@ -223,10 +224,5 @@ var rewriter_1 = require("./rewriter");

// Merge the JSDoc tags for each overloaded parameter.
var foundOptional = false;
for (var i = 0; i < maxArgsCount; i++) {
var paramTag = jsdoc.merge(paramTags[i]);
// If any overload marks this param optional, mark it optional in the
// merged output.
var optional = paramTags[i].find(function (t) { return t.optional === true; }) !== undefined;
if (optional || i >= minArgsCount) {
paramTag.type += '=';
}
// If any overload marks this param as a ..., mark it ... in the

@@ -237,2 +233,11 @@ // merged output.

}
// If any overload marks this param optional, mark it optional in the
// merged output. Also mark parameters following optional as optional,
// even if they are not, since Closure restricts this, see
// https://github.com/google/closure-compiler/issues/2314
var optional = paramTags[i].find(function (t) { return t.optional === true; }) !== undefined || foundOptional;
if (!paramTag.restParam && (optional || i >= minArgsCount)) {
foundOptional = true;
paramTag.type += '=';
}
newDoc.push(paramTag);

@@ -333,2 +338,4 @@ }

_this.generatedExports = new Set();
/** Externs determined by an exporting decorator. */
_this.exportingDecoratorExterns = [];
_this.externsWriter = new ExternsWriter(program, file, options);

@@ -342,4 +349,5 @@ return _this;

var externsSource = null;
if (externs.output) {
externsSource = "/**\n * @externs\n * @suppress {duplicate}\n */\n// NOTE: generated by tsickle, do not edit.\n" + externs.output;
if (externs.output.length > 0 || this.exportingDecoratorExterns.length > 0) {
externsSource = "/**\n * @externs\n * @suppress {duplicate}\n */\n// NOTE: generated by tsickle, do not edit.\n" + externs.output +
this.formatExportingDecoratorExterns();
}

@@ -353,3 +361,68 @@ return {

};
Annotator.prototype.getExportDeclarationNames = function (node) {
var _this = this;
switch (node.kind) {
case ts.SyntaxKind.VariableStatement:
var varDecl = node;
return varDecl.declarationList.declarations.map(function (d) { return _this.getExportDeclarationNames(d)[0]; });
case ts.SyntaxKind.VariableDeclaration:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.ClassDeclaration:
var decl = node;
if (!decl.name || decl.name.kind !== ts.SyntaxKind.Identifier) {
break;
}
return [decl.name];
case ts.SyntaxKind.TypeAliasDeclaration:
var typeAlias = node;
return [typeAlias.name];
default:
break;
}
this.error(node, "unsupported export declaration " + ts.SyntaxKind[node.kind] + ": " + node.getText());
return [];
};
/**
* Emits an ES6 export for the ambient declaration behind node, if it is indeed exported.
*/
Annotator.prototype.maybeEmitAmbientDeclarationExport = function (node) {
// In TypeScript, `export declare` simply generates no code in the exporting module, but does
// generate a regular import in the importing module.
// For Closure Compiler, such declarations must still be exported, so that importing code in
// other modules can reference them. Because tsickle generates global symbols for such types,
// the appropriate semantics are referencing the global name.
if (this.options.untyped || !hasModifierFlag(node, ts.ModifierFlags.Export)) {
return;
}
var declNames = this.getExportDeclarationNames(node);
for (var _i = 0, declNames_1 = declNames; _i < declNames_1.length; _i++) {
var decl = declNames_1[_i];
var sym = this.program.getTypeChecker().getSymbolAtLocation(decl);
var isValue = sym.flags & ts.SymbolFlags.Value;
var declName = rewriter_1.getIdentifierText(decl);
if (node.kind === ts.SyntaxKind.VariableStatement) {
// For variables, TypeScript rewrites every reference to the variable name as an
// "exports." access, to maintain mutable ES6 exports semantics. Indirecting through the
// window object means we reference the correct global symbol. Closure Compiler does
// understand that "var foo" in externs corresponds to "window.foo".
this.emit("\nexports." + declName + " = window." + declName + ";\n");
}
else if (!isValue) {
// Non-value objects do not exist at runtime, so we cannot access the symbol (it only
// exists in externs). Export them as a typedef, which forwards to the type in externs.
this.emit("\n/** @typedef {" + declName + "} */\nexports." + declName + ";\n");
}
else {
this.emit("\nexports." + declName + " = " + declName + ";\n");
}
}
};
Annotator.prototype.formatExportingDecoratorExterns = function () {
if (this.exportingDecoratorExterns.length === 0) {
return '';
}
return '\n' + this.exportingDecoratorExterns.map(function (name) { return "var " + name + ";\n"; }).join('');
};
/**
* Examines a ts.Node and decides whether to do special processing of it for output.

@@ -361,3 +434,3 @@ *

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

@@ -367,4 +440,12 @@ // An ambient declaration declares types for TypeScript's benefit, so we want to skip Tsickle

this.writeRange(node.getFullStart(), node.getEnd());
// ... but it might need to be exported for downstream importing code.
this.maybeEmitAmbientDeclarationExport(node);
return true;
}
if (decorators_1.hasExportingDecorator(node, this.program.getTypeChecker())) {
var name_3 = node.name;
if (name_3) {
this.exportingDecoratorExterns.push(name_3.getText());
}
}
switch (node.kind) {

@@ -514,2 +595,11 @@ case ts.SyntaxKind.ImportDeclaration:

return true;
case ts.SyntaxKind.PropertyDeclaration:
var jsDoc = this.getJSDoc(node);
if (jsDoc && jsDoc.length > 0 && node.getFirstToken()) {
this.emit('\n');
this.emit(jsdoc.toString(jsDoc));
this.writeRange(node.getFirstToken().getStart(), node.getEnd());
return true;
}
break;
default:

@@ -554,4 +644,4 @@ break;

var sym = exports_2[_a];
var name_3 = rewriter_1.unescapeName(sym.name);
if (localSet.has(name_3)) {
var name_4 = rewriter_1.unescapeName(sym.name);
if (localSet.has(name_4)) {
// This name is shadowed by a local definition, such as:

@@ -562,8 +652,8 @@ // - export var foo ...

}
if (this.generatedExports.has(name_3)) {
if (this.generatedExports.has(name_4)) {
// Already exported via an earlier expansion of an "export * from ...".
continue;
}
this.generatedExports.add(name_3);
reexports.add(name_3);
this.generatedExports.add(name_4);
reexports.add(name_4);
}

@@ -644,6 +734,6 @@ return util_1.toArray(reexports.keys());

for (var _i = 0, localNames_1 = localNames; _i < localNames_1.length; _i++) {
var name_4 = localNames_1[_i];
var name_5 = localNames_1[_i];
// This may look like a self-reference but TypeScript will rename the
// right-hand side!
this.emit("\nconst " + name_4 + ": NeverTypeCheckMe = " + name_4 + "; /* local alias for Closure JSDoc */");
this.emit("\nconst " + name_5 + ": NeverTypeCheckMe = " + name_5 + "; /* local alias for Closure JSDoc */");
}

@@ -830,3 +920,5 @@ }

tags.push({ tagName: 'type', type: this.typeToClosure(p) });
this.emit(jsdoc.toString(tags));
// Avoid printing annotations that can conflict with @type
// This avoids Closure's error "type annotation incompatible with other annotations"
this.emit(jsdoc.toString(tags, ['param', 'return']));
namespace = namespace.concat([name]);

@@ -961,22 +1053,30 @@ this.emit(namespace.join('.') + ";\n");

case ts.SyntaxKind.Identifier:
// E.g. "declare namespace foo {"
var name_5 = rewriter_1.getIdentifierText(decl.name);
if (name_5 === undefined)
break;
namespace = namespace.concat(name_5);
if (this.isFirstDeclaration(decl)) {
this.emit('/** @const */\n');
if (namespace.length > 1) {
this.emit(namespace.join('.') + " = {};\n");
{
// E.g. "declare namespace foo {"
var name_6 = rewriter_1.getIdentifierText(decl.name);
if (name_6 === undefined)
break;
if (this.isFirstDeclaration(decl)) {
this.emit('/** @const */\n');
this.writeExternsVariable(name_6, namespace, '{}');
}
else {
this.emit("var " + namespace + " = {};\n");
}
if (decl.body)
this.visit(decl.body, namespace.concat(name_6));
}
if (decl.body)
this.visit(decl.body, namespace);
break;
case ts.SyntaxKind.StringLiteral:
// E.g. "declare module 'foo' {" (note the quotes).
// Skip it.
{
// E.g. "declare module 'foo' {" (note the quotes).
// We still want to emit externs for this module, but
// Closure doesn't really provide a mechanism for
// module-scoped externs. For now, ignore the enclosing
// namespace (because this is declaring a top-level module)
// and emit into a fake namespace.
namespace = ['tsickle_declare_module'];
var name_7 = decl.name.text;
this.emit('/** @const */\n');
this.writeExternsVariable(name_7, namespace, '{}');
if (decl.body)
this.visit(decl.body, namespace.concat(name_7));
}
break;

@@ -1000,4 +1100,4 @@ default:

var fnDecl = node;
var name_6 = fnDecl.name;
if (!name_6) {
var name_8 = fnDecl.name;
if (!name_8) {
this.error(fnDecl, 'anonymous function in externs');

@@ -1007,3 +1107,3 @@ break;

// Gather up all overloads of this function.
var sym = this.program.getTypeChecker().getSymbolAtLocation(name_6);
var sym = this.program.getTypeChecker().getSymbolAtLocation(name_8);
var decls = sym.declarations.filter(function (d) { return d.kind ===

@@ -1015,3 +1115,3 @@ ts.SyntaxKind.FunctionDeclaration; });

var params = this.emitFunctionType(decls);
this.writeExternsFunction(name_6.getText(), params, namespace);
this.writeExternsFunction(name_8.getText(), params, namespace);
break;

@@ -1142,8 +1242,8 @@ case ts.SyntaxKind.VariableStatement:

if (decl.name.kind === ts.SyntaxKind.Identifier) {
var name_7 = rewriter_1.getIdentifierText(decl.name);
if (exports.closureExternsBlacklist.indexOf(name_7) >= 0)
var name_9 = rewriter_1.getIdentifierText(decl.name);
if (exports.closureExternsBlacklist.indexOf(name_9) >= 0)
return;
this.emitJSDocType(decl);
this.emit('\n');
this.writeExternsVariable(name_7, namespace);
this.writeExternsVariable(name_9, namespace);
}

@@ -1150,0 +1250,0 @@ else {

@@ -49,3 +49,3 @@ /**

var sourceText = substituteSource.get(path);
if (sourceText) {
if (sourceText !== undefined) {
return ts.createSourceFile(fileName, sourceText, languageVersion);

@@ -52,0 +52,0 @@ }

{
"name": "tsickle",
"version": "0.21.3",
"version": "0.21.4",
"description": "Transpile TypeScript code to JavaScript with Closure annotations.",

@@ -5,0 +5,0 @@ "main": "build/src/tsickle.js",

@@ -61,2 +61,20 @@ # Tsickle - TypeScript to Closure Annotator [![Build Status](https://travis-ci.org/angular/tsickle.svg?branch=master)](https://travis-ci.org/angular/tsickle)

#### Exporting decorators
An exporting decorator is a decorator that has `@ExportDecoratedItems` in its
JSDoc.
The name of the element that have an exporting decorator are preserved through
the Closure compilation process.
Example:
/** @ExportDecoratedItems */
function myDecorator() {
// ...
}
@myDecorator()
class DoNotRenameThisClass { ... }
## Development

@@ -63,0 +81,0 @@

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

import {getDecoratorDeclarations} from './decorators';
import {Rewriter} from './rewriter';

@@ -43,25 +44,3 @@ import {assertTypeChecked, TypeTranslator} from './type-translator';

private shouldLower(decorator: ts.Decorator) {
// Walk down the expression to find the identifier of the decorator function
let node: ts.Node = decorator;
while (node.kind !== ts.SyntaxKind.Identifier) {
switch (node.kind) {
case ts.SyntaxKind.Decorator:
node = (node as ts.Decorator).expression;
break;
case ts.SyntaxKind.CallExpression:
node = (node as ts.CallExpression).expression;
break;
// PropertyAccessExpression is intentionally missing here,
// because the rest of the rewriter does not handle such
// expressions.
default:
return false;
}
}
let decSym = this.typeChecker.getSymbolAtLocation(node);
if (decSym.flags & ts.SymbolFlags.Alias) {
decSym = this.typeChecker.getAliasedSymbol(decSym);
}
for (let d of decSym.getDeclarations()) {
for (let d of getDecoratorDeclarations(decorator, this.typeChecker)) {
// Switch to the TS JSDoc parser in the future to avoid false positives here.

@@ -68,0 +47,0 @@ // For example using '@Annotation' in a true comment.

@@ -189,6 +189,7 @@ /**

*/
function tagToString(tag: Tag): string {
function tagToString(tag: Tag, escapeExtraTags: string[] = []): string {
let out = '';
if (tag.tagName) {
if (!arrayIncludes(JSDOC_TAGS_WHITELIST, tag.tagName)) {
if (!arrayIncludes(JSDOC_TAGS_WHITELIST, tag.tagName) ||
arrayIncludes(escapeExtraTags, tag.tagName)) {
// Escape tags we don't understand. This is a subtle

@@ -231,3 +232,3 @@ // compromise between multiple issues.

/** Serializes a Comment out to a string usable in source code. */
export function toString(tags: Tag[]): string {
export function toString(tags: Tag[], escapeExtraTags: string[] = []): string {
if (tags.length === 0) return '';

@@ -239,3 +240,3 @@ if (tags.length === 1) {

// /** @type {foo} */
return '/**' + tagToString(tag) + ' */\n';
return '/**' + tagToString(tag, escapeExtraTags) + ' */\n';
}

@@ -250,3 +251,3 @@ // Otherwise, fall through to the multi-line output.

// If the tagToString is multi-line, insert " * " prefixes on subsequent lines.
out += tagToString(tag).split('\n').join('\n * ');
out += tagToString(tag, escapeExtraTags).split('\n').join('\n * ');
out += '\n';

@@ -253,0 +254,0 @@ }

@@ -126,2 +126,27 @@ #!/usr/bin/env node

export interface ClosureJSOptions {
tsickleCompilerHostOptions: tsickle.Options;
tsickleHost: tsickle.TsickleHost;
files: Map<string, string>;
tsicklePasses: tsickle.Pass[];
}
function getDefaultClosureJSOptions(fileNames: string[], settings: Settings): ClosureJSOptions {
return {
tsickleCompilerHostOptions: {
googmodule: true,
es5Mode: false,
untyped: settings.isUntyped,
},
tsickleHost: {
shouldSkipTsickleProcessing: (fileName) => fileNames.indexOf(fileName) === -1,
pathToModuleName: cliSupport.pathToModuleName,
shouldIgnoreWarningsForPath: (filePath) => false,
fileNameToModuleId: (fileName) => fileName,
},
files: new Map<string, string>(),
tsicklePasses: [tsickle.Pass.CLOSURIZE],
};
}
/**

@@ -133,4 +158,6 @@ * Compiles TypeScript code into Closure-compiler-ready JS.

options: ts.CompilerOptions, fileNames: string[], settings: Settings,
allDiagnostics: ts.Diagnostic[],
files = new Map<string, string>()): {jsFiles: Map<string, string>, externs: string}|null {
allDiagnostics: ts.Diagnostic[], partialClosureJSOptions = {} as Partial<ClosureJSOptions>):
{jsFiles: Map<string, string>, externs: string}|null {
const closureJSOptions: ClosureJSOptions = {...getDefaultClosureJSOptions(fileNames, settings),
...partialClosureJSOptions};
// Parse and load the program without tsickle processing.

@@ -140,15 +167,2 @@ // This is so:

// - tsickle can use the result of typechecking for annotation
const tsickleCompilerHostOptions: tsickle.Options = {
googmodule: true,
es5Mode: false,
untyped: settings.isUntyped,
};
const tsickleHost: tsickle.TsickleHost = {
shouldSkipTsickleProcessing: (fileName) => fileNames.indexOf(fileName) === -1,
pathToModuleName: cliSupport.pathToModuleName,
shouldIgnoreWarningsForPath: (filePath) => false,
fileNameToModuleId: (fileName) => fileName,
};
const jsFiles = new Map<string, string>();

@@ -158,6 +172,8 @@ const outputRetainingHost =

const sourceReplacingHost = createSourceReplacingCompilerHost(files, outputRetainingHost);
const sourceReplacingHost =
createSourceReplacingCompilerHost(closureJSOptions.files, outputRetainingHost);
const tch = new tsickle.TsickleCompilerHost(
sourceReplacingHost, options, tsickleCompilerHostOptions, tsickleHost);
sourceReplacingHost, options, closureJSOptions.tsickleCompilerHostOptions,
closureJSOptions.tsickleHost);

@@ -175,5 +191,12 @@ let program = ts.createProgram(fileNames, options, tch);

// place of the original source.
tch.reconfigureForRun(program, tsickle.Pass.CLOSURIZE);
program = ts.createProgram(fileNames, options, tch);
if (closureJSOptions.tsicklePasses.indexOf(tsickle.Pass.DECORATOR_DOWNLEVEL) !== -1) {
tch.reconfigureForRun(program, tsickle.Pass.DECORATOR_DOWNLEVEL);
program = ts.createProgram(fileNames, options, tch);
}
if (closureJSOptions.tsicklePasses.indexOf(tsickle.Pass.CLOSURIZE) !== -1) {
tch.reconfigureForRun(program, tsickle.Pass.CLOSURIZE);
program = ts.createProgram(fileNames, options, tch);
}
let {diagnostics} = program.emit(undefined);

@@ -180,0 +203,0 @@ if (diagnostics.length > 0) {

import * as path from 'path';
import {SourceMapGenerator} from 'source-map';
import {SourceMapConsumer, SourceMapGenerator} from 'source-map';
import * as ts from 'typescript';

@@ -148,8 +148,8 @@

if (path.extname(fileName) !== '.map') {
if (!isDtsFileName(fileName) && this.tscOptions.inlineSourceMap) {
content = this.combineInlineSourceMaps(fileName, content);
}
if (this.options.googmodule && !isDtsFileName(fileName)) {
content = this.convertCommonJsToGoogModule(fileName, content);
}
if (!isDtsFileName(fileName) && this.tscOptions.inlineSourceMap) {
content = this.combineInlineSourceMaps(fileName, content);
}
} else {

@@ -169,3 +169,3 @@ content = this.combineSourceMaps(fileName, content);

getSourceMapKeyForSourceFile(sourceFile: ts.SourceFile): string {
return this.getCanonicalFileName(sourceFile.path);
return this.getCanonicalFileName(path.resolve(sourceFile.path));
}

@@ -229,7 +229,8 @@

const sourceMapKey = this.getSourceMapKeyForPathAndName(filePath, sourceFileName);
const preexistingSourceMapGenerator = this.preexistingSourceMaps.get(sourceMapKey)!;
const preexistingSourceMapConsumer =
sourceMapUtils.sourceMapGeneratorToConsumerWithFileName(
preexistingSourceMapGenerator, sourceFileName);
tscSourceMapGenerator.applySourceMap(preexistingSourceMapConsumer);
const preexistingSourceMapGenerator = this.preexistingSourceMaps.get(sourceMapKey);
if (preexistingSourceMapGenerator) {
const preexistingSourceMapConsumer =
new SourceMapConsumer(preexistingSourceMapGenerator.toJSON());
tscSourceMapGenerator.applySourceMap(preexistingSourceMapConsumer);
}
}

@@ -236,0 +237,0 @@ }

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

import {hasExportingDecorator} from './decorators';
import * as jsdoc from './jsdoc';

@@ -234,10 +235,5 @@ import {getIdentifierText, Rewriter, unescapeName} from './rewriter';

// Merge the JSDoc tags for each overloaded parameter.
let foundOptional = false;
for (let i = 0; i < maxArgsCount; i++) {
let paramTag = jsdoc.merge(paramTags[i]);
// If any overload marks this param optional, mark it optional in the
// merged output.
const optional = paramTags[i].find(t => t.optional === true) !== undefined;
if (optional || i >= minArgsCount) {
paramTag.type += '=';
}
// If any overload marks this param as a ..., mark it ... in the

@@ -248,2 +244,11 @@ // merged output.

}
// If any overload marks this param optional, mark it optional in the
// merged output. Also mark parameters following optional as optional,
// even if they are not, since Closure restricts this, see
// https://github.com/google/closure-compiler/issues/2314
const optional = paramTags[i].find(t => t.optional === true) !== undefined || foundOptional;
if (!paramTag.restParam && (optional || i >= minArgsCount)) {
foundOptional = true;
paramTag.type += '=';
}
newDoc.push(paramTag);

@@ -351,2 +356,5 @@ }

/** Externs determined by an exporting decorator. */
private exportingDecoratorExterns: string[] = [];
constructor(

@@ -366,3 +374,4 @@ program: ts.Program, file: ts.SourceFile, options: Options,

let externsSource: string|null = null;
if (externs.output) {
if (externs.output.length > 0 || this.exportingDecoratorExterns.length > 0) {
externsSource = `/**

@@ -373,3 +382,4 @@ * @externs

// NOTE: generated by tsickle, do not edit.
` + externs.output;
` + externs.output +
this.formatExportingDecoratorExterns();
}

@@ -384,3 +394,70 @@

}
getExportDeclarationNames(node: ts.Node): ts.Identifier[] {
switch (node.kind) {
case ts.SyntaxKind.VariableStatement:
const varDecl = node as ts.VariableStatement;
return varDecl.declarationList.declarations.map(
(d) => this.getExportDeclarationNames(d)[0]);
case ts.SyntaxKind.VariableDeclaration:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.ClassDeclaration:
const decl = node as ts.Declaration;
if (!decl.name || decl.name.kind !== ts.SyntaxKind.Identifier) {
break;
}
return [decl.name];
case ts.SyntaxKind.TypeAliasDeclaration:
const typeAlias = node as ts.TypeAliasDeclaration;
return [typeAlias.name];
default:
break;
}
this.error(
node, `unsupported export declaration ${ts.SyntaxKind[node.kind]}: ${node.getText()}`);
return [];
}
/**
* Emits an ES6 export for the ambient declaration behind node, if it is indeed exported.
*/
maybeEmitAmbientDeclarationExport(node: ts.Node) {
// In TypeScript, `export declare` simply generates no code in the exporting module, but does
// generate a regular import in the importing module.
// For Closure Compiler, such declarations must still be exported, so that importing code in
// other modules can reference them. Because tsickle generates global symbols for such types,
// the appropriate semantics are referencing the global name.
if (this.options.untyped || !hasModifierFlag(node, ts.ModifierFlags.Export)) {
return;
}
const declNames = this.getExportDeclarationNames(node);
for (let decl of declNames) {
const sym = this.program.getTypeChecker().getSymbolAtLocation(decl);
const isValue = sym.flags & ts.SymbolFlags.Value;
const declName = getIdentifierText(decl);
if (node.kind === ts.SyntaxKind.VariableStatement) {
// For variables, TypeScript rewrites every reference to the variable name as an
// "exports." access, to maintain mutable ES6 exports semantics. Indirecting through the
// window object means we reference the correct global symbol. Closure Compiler does
// understand that "var foo" in externs corresponds to "window.foo".
this.emit(`\nexports.${declName} = window.${declName};\n`);
} else if (!isValue) {
// Non-value objects do not exist at runtime, so we cannot access the symbol (it only
// exists in externs). Export them as a typedef, which forwards to the type in externs.
this.emit(`\n/** @typedef {${declName}} */\nexports.${declName};\n`);
} else {
this.emit(`\nexports.${declName} = ${declName};\n`);
}
}
}
private formatExportingDecoratorExterns() {
if (this.exportingDecoratorExterns.length === 0) {
return '';
}
return '\n' + this.exportingDecoratorExterns.map(name => `var ${name};\n`).join('');
}
/**
* Examines a ts.Node and decides whether to do special processing of it for output.

@@ -392,3 +469,3 @@ *

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

@@ -398,5 +475,17 @@ // An ambient declaration declares types for TypeScript's benefit, so we want to skip Tsickle

this.writeRange(node.getFullStart(), node.getEnd());
// ... but it might need to be exported for downstream importing code.
this.maybeEmitAmbientDeclarationExport(node);
return true;
}
if (hasExportingDecorator(node, this.program.getTypeChecker())) {
let {name} = node as (
// If the node has a decorator, it must belong to one of these types.
ts.ClassDeclaration | ts.MethodDeclaration | ts.PropertyDeclaration |
ts.AccessorDeclaration);
if (name) {
this.exportingDecoratorExterns.push(name.getText());
}
}
switch (node.kind) {

@@ -547,2 +636,11 @@ case ts.SyntaxKind.ImportDeclaration:

return true;
case ts.SyntaxKind.PropertyDeclaration:
const jsDoc = this.getJSDoc(node);
if (jsDoc && jsDoc.length > 0 && node.getFirstToken()) {
this.emit('\n');
this.emit(jsdoc.toString(jsDoc));
this.writeRange(node.getFirstToken().getStart(), node.getEnd());
return true;
}
break;
default:

@@ -869,3 +967,5 @@ break;

tags.push({tagName: 'type', type: this.typeToClosure(p)});
this.emit(jsdoc.toString(tags));
// Avoid printing annotations that can conflict with @type
// This avoids Closure's error "type annotation incompatible with other annotations"
this.emit(jsdoc.toString(tags, ['param', 'return']));
namespace = namespace.concat([name]);

@@ -989,21 +1089,25 @@ this.emit(`${namespace.join('.')};\n`);

switch (decl.name.kind) {
case ts.SyntaxKind.Identifier:
case ts.SyntaxKind.Identifier: {
// E.g. "declare namespace foo {"
let name = getIdentifierText(decl.name as ts.Identifier);
if (name === undefined) break;
namespace = namespace.concat(name);
if (this.isFirstDeclaration(decl)) {
this.emit('/** @const */\n');
if (namespace.length > 1) {
this.emit(`${namespace.join('.')} = {};\n`);
} else {
this.emit(`var ${namespace} = {};\n`);
}
this.writeExternsVariable(name, namespace, '{}');
}
if (decl.body) this.visit(decl.body, namespace);
break;
case ts.SyntaxKind.StringLiteral:
if (decl.body) this.visit(decl.body, namespace.concat(name));
} break;
case ts.SyntaxKind.StringLiteral: {
// E.g. "declare module 'foo' {" (note the quotes).
// Skip it.
break;
// We still want to emit externs for this module, but
// Closure doesn't really provide a mechanism for
// module-scoped externs. For now, ignore the enclosing
// namespace (because this is declaring a top-level module)
// and emit into a fake namespace.
namespace = ['tsickle_declare_module'];
let name = (decl.name as ts.StringLiteral).text;
this.emit('/** @const */\n');
this.writeExternsVariable(name, namespace, '{}');
if (decl.body) this.visit(decl.body, namespace.concat(name));
} break;
default:

@@ -1010,0 +1114,0 @@ this.errorUnimplementedKind(decl.name, 'externs generation of namespace');

@@ -55,3 +55,3 @@ /**

let sourceText = substituteSource.get(path);
if (sourceText) {
if (sourceText !== undefined) {
return ts.createSourceFile(fileName, sourceText, languageVersion);

@@ -58,0 +58,0 @@ }

@@ -16,2 +16,3 @@ {

"src/cli_support.ts",
"src/decorators.ts",
"src/decorator-annotator.ts",

@@ -18,0 +19,0 @@ "src/es5processor.ts",

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