Comparing version 0.23.6 to 0.24.0
@@ -14,6 +14,9 @@ import { ModulesManifest } from './modules_manifest'; | ||
fileNameToModuleId(fileName: string): string; | ||
} | ||
export interface Es5ProcessorOptions { | ||
/** Whether to convert CommonJS module syntax to `goog.module` Closure imports. */ | ||
googmodule?: boolean; | ||
/** Whether the emit targets ES5 or ES6+. */ | ||
es5Mode?: boolean; | ||
/** | ||
* An additional prelude to insert in front of the emitted code, e.g. to import a shared library. | ||
*/ | ||
prelude?: string; | ||
@@ -40,6 +43,6 @@ } | ||
*/ | ||
export declare function processES5(host: Es5ProcessorHost, options: Es5ProcessorOptions, fileName: string, content: string): { | ||
export declare function processES5(host: Es5ProcessorHost, fileName: string, content: string): { | ||
output: string; | ||
referencedModules: string[]; | ||
}; | ||
export declare function convertCommonJsToGoogModuleIfNeeded(host: Es5ProcessorHost, options: Es5ProcessorOptions, modulesManifest: ModulesManifest, fileName: string, content: string): string; | ||
export declare function convertCommonJsToGoogModuleIfNeeded(host: Es5ProcessorHost, modulesManifest: ModulesManifest, fileName: string, content: string): string; |
@@ -37,9 +37,15 @@ /** | ||
/** | ||
* Result of parsing a JSDoc comment. Such comments essentially are built of a list of tags. | ||
* In addition to the tags, this might also contain warnings to indicate non-fatal problems | ||
* while finding the tags. | ||
*/ | ||
export interface ParsedJSDocComment { | ||
tags: Tag[]; | ||
warnings?: string[]; | ||
} | ||
/** | ||
* parse parses JSDoc out of a comment string. | ||
* Returns null if comment is not JSDoc. | ||
*/ | ||
export declare function parse(comment: string): { | ||
tags: Tag[]; | ||
warnings?: string[]; | ||
} | null; | ||
export declare function parse(comment: string): ParsedJSDocComment | null; | ||
/** Serializes a Comment out to a string usable in source code. */ | ||
@@ -46,0 +52,0 @@ export declare function toString(tags: Tag[], escapeExtraTags?: Set<string>): string; |
@@ -12,15 +12,5 @@ import * as ts from 'typescript'; | ||
} | ||
export interface ClosureJSOptions { | ||
tsickleCompilerHostOptions: tsickle.Options; | ||
tsickleHost: tsickle.TsickleHost; | ||
files: Map<string, string>; | ||
tsicklePasses: tsickle.Pass[]; | ||
} | ||
/** | ||
* Compiles TypeScript code into Closure-compiler-ready JS. | ||
* Doesn't write any files to disk; all JS content is returned in a map. | ||
*/ | ||
export declare function toClosureJS(options: ts.CompilerOptions, fileNames: string[], settings: Settings, allDiagnostics: ts.Diagnostic[], partialClosureJSOptions?: Partial<ClosureJSOptions>): { | ||
jsFiles: Map<string, string>; | ||
externs: string; | ||
} | null; | ||
export declare function toClosureJS(options: ts.CompilerOptions, fileNames: string[], settings: Settings, writeFile?: ts.WriteFileCallback): tsickle.EmitResult; |
import * as ts from 'typescript'; | ||
import * as es5processor from './es5processor'; | ||
import { ModulesManifest } from './modules_manifest'; | ||
import { SourceMapper } from './source_map_utils'; | ||
export { convertDecorators } from './decorator-annotator'; | ||
export { FileMap, ModulesManifest } from './modules_manifest'; | ||
export { EmitResult, EmitTransformers, emitWithTsickle, mergeEmitResults, TransformerHost, TransformerOptions } from './transformer'; | ||
export { Options, Pass, TsickleCompilerHost, TsickleHost } from './tsickle_compiler_host'; | ||
export interface AnnotatorHost { | ||
@@ -15,4 +15,2 @@ /** | ||
pathToModuleName: (context: string, importPath: string) => string; | ||
} | ||
export interface AnnotatorOptions { | ||
/** | ||
@@ -31,28 +29,2 @@ * If true, convert every type to the Closure {?} type, which means | ||
} | ||
export declare enum AnnotatorFeatures { | ||
LowerDecorators = 1, | ||
/** | ||
* Filter out types when expanding `export * from ...`. | ||
* | ||
* Needed by the transformer version as TypeScript collects symbol information before | ||
* running the transformers, and the generated identifiers therefore have no | ||
* TypeScript symbol information associated. Because of this, Typescript does not | ||
* elide exports for types in this case. | ||
* | ||
* This flag will be removed and always set once we drop the on transformer version of tsickle. | ||
*/ | ||
FilterTypesInExportStart = 2, | ||
/** | ||
* Generated @typedefs for reexported interfaces. | ||
* | ||
* Needed for the transformer version of tsickle as the .d.ts is generated before running tsickle, | ||
* and therefore does no longer contain the reexports for the interfaces, even if | ||
* tsickle changes them into functions. | ||
* | ||
* This flag will be removed and always set once we drop the on transformer version of tsickle. | ||
*/ | ||
TypeDefReexportForInterfaces = 8, | ||
Default = 0, | ||
Transformer = 11, | ||
} | ||
/** | ||
@@ -73,7 +45,7 @@ * The header to be used in generated externs. This is not included in the | ||
export declare function isDtsFileName(fileName: string): boolean; | ||
export declare function annotate(typeChecker: ts.TypeChecker, file: ts.SourceFile, host: AnnotatorHost, options?: AnnotatorOptions, tsHost?: ts.ModuleResolutionHost, tsOpts?: ts.CompilerOptions, sourceMapper?: SourceMapper, features?: AnnotatorFeatures): { | ||
export declare function annotate(typeChecker: ts.TypeChecker, file: ts.SourceFile, host: AnnotatorHost, tsHost?: ts.ModuleResolutionHost, tsOpts?: ts.CompilerOptions, sourceMapper?: SourceMapper): { | ||
output: string; | ||
diagnostics: ts.Diagnostic[]; | ||
}; | ||
export declare function writeExterns(typeChecker: ts.TypeChecker, file: ts.SourceFile, host: AnnotatorHost, options?: AnnotatorOptions): { | ||
export declare function writeExterns(typeChecker: ts.TypeChecker, file: ts.SourceFile, host: AnnotatorHost): { | ||
output: string; | ||
@@ -86,1 +58,35 @@ diagnostics: ts.Diagnostic[]; | ||
}): string; | ||
export interface TsickleHost extends es5processor.Es5ProcessorHost, AnnotatorHost { | ||
/** | ||
* Whether to downlevel decorators | ||
*/ | ||
transformDecorators?: boolean; | ||
/** | ||
* Whether to convers types to closure | ||
*/ | ||
transformTypesToClosure?: boolean; | ||
/** | ||
* If true, tsickle and decorator downlevel processing will be skipped for | ||
* that file. | ||
*/ | ||
shouldSkipTsickleProcessing(fileName: string): boolean; | ||
/** | ||
* Tsickle treats warnings as errors, if true, ignore warnings. This might be | ||
* useful for e.g. third party code. | ||
*/ | ||
shouldIgnoreWarningsForPath(filePath: string): boolean; | ||
} | ||
export declare function mergeEmitResults(emitResults: EmitResult[]): EmitResult; | ||
export interface EmitResult extends ts.EmitResult { | ||
modulesManifest: ModulesManifest; | ||
/** externs.js files produced by tsickle, if any. */ | ||
externs: { | ||
[fileName: string]: string; | ||
}; | ||
} | ||
export interface EmitTransformers { | ||
beforeTsickle?: Array<ts.TransformerFactory<ts.SourceFile>>; | ||
beforeTs?: Array<ts.TransformerFactory<ts.SourceFile>>; | ||
afterTs?: Array<ts.TransformerFactory<ts.SourceFile>>; | ||
} | ||
export declare function emitWithTsickle(program: ts.Program, host: TsickleHost, tsHost: ts.CompilerHost, tsOptions: ts.CompilerOptions, targetSourceFile?: ts.SourceFile, writeFile?: ts.WriteFileCallback, cancellationToken?: ts.CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: EmitTransformers): EmitResult; |
@@ -34,3 +34,3 @@ import * as ts from 'typescript'; | ||
*/ | ||
constructor(typeChecker: ts.TypeChecker, node: ts.Node, pathBlackList?: Set<string>, symbolsToAliasedNames?: Map<ts.Symbol, string>); | ||
constructor(typeChecker: ts.TypeChecker, node: ts.Node, pathBlackList?: Set<string> | undefined, symbolsToAliasedNames?: Map<ts.Symbol, string>); | ||
/** | ||
@@ -50,2 +50,3 @@ * Converts a ts.Symbol to a string. | ||
private translateUnion(type); | ||
private translateEnumLiteral(type); | ||
private translateObject(type); | ||
@@ -52,0 +53,0 @@ /** |
@@ -18,11 +18,4 @@ /** | ||
/** | ||
* 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; | ||
/** | ||
* Returns the input string with line endings normalized to '\n'. | ||
*/ | ||
export declare function normalizeLineEndings(input: string): string; |
@@ -19,2 +19,32 @@ "use strict"; | ||
})(); | ||
var __values = (this && this.__values) || function (o) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; | ||
if (m) return m.call(o); | ||
return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
var __spread = (this && this.__spread) || function () { | ||
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); | ||
return ar; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -45,35 +75,54 @@ var ts = require("typescript"); | ||
DecoratorClassVisitor.prototype.shouldLower = function (decorator) { | ||
for (var _i = 0, _a = decorators_1.getDecoratorDeclarations(decorator, this.typeChecker); _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) | ||
try { | ||
for (var _a = __values(decorators_1.getDecoratorDeclarations(decorator, this.typeChecker)), _b = _a.next(); !_b.done; _b = _a.next()) { | ||
var d = _b.value; | ||
// 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) | ||
continue; | ||
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) | ||
continue; | ||
commentNode = commentNode.parent; | ||
} | ||
var range = ts.getLeadingCommentRanges(commentNode.getFullText(), 0); | ||
if (!range) | ||
continue; | ||
commentNode = commentNode.parent; | ||
try { | ||
for (var range_1 = __values(range), range_1_1 = range_1.next(); !range_1_1.done; range_1_1 = range_1.next()) { | ||
var _c = range_1_1.value, pos = _c.pos, end = _c.end; | ||
var jsDocText = commentNode.getFullText().substring(pos, end); | ||
if (jsDocText.includes('@Annotation')) | ||
return true; | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (range_1_1 && !range_1_1.done && (_d = range_1.return)) _d.call(range_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
} | ||
// 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) | ||
continue; | ||
commentNode = commentNode.parent; | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (_b && !_b.done && (_e = _a.return)) _e.call(_a); | ||
} | ||
var range = ts.getLeadingCommentRanges(commentNode.getFullText(), 0); | ||
if (!range) | ||
continue; | ||
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; | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
return false; | ||
var e_2, _e, e_1, _d; | ||
}; | ||
@@ -94,19 +143,28 @@ DecoratorClassVisitor.prototype.decoratorsToLower = function (n) { | ||
var hasDecoratedParam = false; | ||
for (var _i = 0, _a = ctor.parameters; _i < _a.length; _i++) { | ||
var param = _a[_i]; | ||
var ctorParam = { type: null, decorators: null }; | ||
if (param.decorators) { | ||
ctorParam.decorators = this.decoratorsToLower(param); | ||
hasDecoratedParam = hasDecoratedParam || ctorParam.decorators.length > 0; | ||
} | ||
if (param.type) { | ||
// param has a type provided, e.g. "foo: Bar". | ||
// Verify that "Bar" is a value (e.g. a constructor) and not just a type. | ||
var sym = this.typeChecker.getTypeAtLocation(param.type).getSymbol(); | ||
if (sym && (sym.flags & ts.SymbolFlags.Value)) { | ||
ctorParam.type = param.type; | ||
try { | ||
for (var _a = __values(ctor.parameters), _b = _a.next(); !_b.done; _b = _a.next()) { | ||
var param = _b.value; | ||
var ctorParam = { type: null, decorators: null }; | ||
if (param.decorators) { | ||
ctorParam.decorators = this.decoratorsToLower(param); | ||
hasDecoratedParam = hasDecoratedParam || ctorParam.decorators.length > 0; | ||
} | ||
if (param.type) { | ||
// param has a type provided, e.g. "foo: Bar". | ||
// Verify that "Bar" is a value (e.g. a constructor) and not just a type. | ||
var sym = this.typeChecker.getTypeAtLocation(param.type).getSymbol(); | ||
if (sym && (sym.flags & ts.SymbolFlags.Value)) { | ||
ctorParam.type = param.type; | ||
} | ||
} | ||
ctorParameters.push(ctorParam); | ||
} | ||
ctorParameters.push(ctorParam); | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
try { | ||
if (_b && !_b.done && (_c = _a.return)) _c.call(_a); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
// Use the ctor parameter metadata only if the class or the ctor was decorated. | ||
@@ -116,2 +174,3 @@ if (this.decorators || hasDecoratedParam) { | ||
} | ||
var e_3, _c; | ||
}; | ||
@@ -150,6 +209,6 @@ /** | ||
DecoratorClassVisitor.prototype.getValueIdentifierForType = function (typeSymbol, typeNode) { | ||
if (!typeSymbol.valueDeclaration) { | ||
var valueDeclaration = typeSymbol.valueDeclaration; | ||
if (!valueDeclaration) | ||
return null; | ||
} | ||
var valueName = typeSymbol.valueDeclaration.name; | ||
var valueName = valueDeclaration.name; | ||
if (!valueName || valueName.kind !== ts.SyntaxKind.Identifier) { | ||
@@ -165,11 +224,21 @@ return null; | ||
if (firstIdentifierInType) { | ||
for (var _i = 0, _a = this.importedNames; _i < _a.length; _i++) { | ||
var _b = _a[_i], name_1 = _b.name, declarationNames = _b.declarationNames; | ||
if (firstIdentifierInType.text === name_1.text && | ||
declarationNames.some(function (d) { return d === valueName; })) { | ||
return name_1; | ||
try { | ||
for (var _a = __values(this.importedNames), _b = _a.next(); !_b.done; _b = _a.next()) { | ||
var _c = _b.value, name_1 = _c.name, declarationNames = _c.declarationNames; | ||
if (firstIdentifierInType.text === name_1.text && | ||
declarationNames.some(function (d) { return d === valueName; })) { | ||
return name_1; | ||
} | ||
} | ||
} | ||
catch (e_4_1) { e_4 = { error: e_4_1 }; } | ||
finally { | ||
try { | ||
if (_b && !_b.done && (_d = _a.return)) _d.call(_a); | ||
} | ||
finally { if (e_4) throw e_4.error; } | ||
} | ||
} | ||
return null; | ||
var e_4, _d; | ||
}; | ||
@@ -236,7 +305,16 @@ DecoratorClassVisitor.prototype.beforeProcessNode = function (node) { | ||
this.rewriter.emit("static decorators: " + decoratorInvocations + " = [\n"); | ||
for (var _i = 0, _a = this.decorators; _i < _a.length; _i++) { | ||
var annotation = _a[_i]; | ||
this.emitDecorator(annotation); | ||
this.rewriter.emit(',\n'); | ||
try { | ||
for (var _a = __values(this.decorators), _b = _a.next(); !_b.done; _b = _a.next()) { | ||
var annotation = _b.value; | ||
this.emitDecorator(annotation); | ||
this.rewriter.emit(',\n'); | ||
} | ||
} | ||
catch (e_5_1) { e_5 = { error: e_5_1 }; } | ||
finally { | ||
try { | ||
if (_b && !_b.done && (_c = _a.return)) _c.call(_a); | ||
} | ||
finally { if (e_5) throw e_5.error; } | ||
} | ||
this.rewriter.emit('];\n'); | ||
@@ -250,46 +328,64 @@ } | ||
"}|null)[] = () => [\n"); | ||
for (var _b = 0, _c = this.ctorParameters || []; _b < _c.length; _b++) { | ||
var param = _c[_b]; | ||
if (!param.type && !param.decorators) { | ||
this.rewriter.emit('null,\n'); | ||
continue; | ||
} | ||
this.rewriter.emit("{type: "); | ||
if (!param.type) { | ||
this.rewriter.emit("undefined"); | ||
} | ||
else { | ||
// For transformer mode, tsickle must emit not only the string referring to the type, | ||
// but also create a source mapping, so that TypeScript can later recognize that the | ||
// symbol is used in a value position, so that TypeScript emits an import for the | ||
// symbol. | ||
// The code below and in getValueIdentifierForType finds the value node corresponding to | ||
// the type and emits that symbol if possible. This causes a source mapping to the value, | ||
// which then allows later transformers in the pipeline to do the correct module | ||
// rewriting. Note that we cannot use param.type as the emit node directly (not even just | ||
// for mapping), because that is marked as a type use of the node, not a value use, so it | ||
// doesn't get updated as an export. | ||
var sym = this.typeChecker.getTypeAtLocation(param.type).getSymbol(); | ||
var emitNode = this.getValueIdentifierForType(sym, param.type); | ||
if (emitNode) { | ||
this.rewriter.writeRange(emitNode, emitNode.getStart(), emitNode.getEnd()); | ||
try { | ||
for (var _d = __values(this.ctorParameters || []), _e = _d.next(); !_e.done; _e = _d.next()) { | ||
var param = _e.value; | ||
if (!param.type && !param.decorators) { | ||
this.rewriter.emit('null,\n'); | ||
continue; | ||
} | ||
this.rewriter.emit("{type: "); | ||
if (!param.type) { | ||
this.rewriter.emit("undefined"); | ||
} | ||
else { | ||
var typeStr = new type_translator_1.TypeTranslator(this.typeChecker, param.type) | ||
.symbolToString(sym, /* useFqn */ true); | ||
this.rewriter.emit(typeStr); | ||
// For transformer mode, tsickle must emit not only the string referring to the type, | ||
// but also create a source mapping, so that TypeScript can later recognize that the | ||
// symbol is used in a value position, so that TypeScript emits an import for the | ||
// symbol. | ||
// The code below and in getValueIdentifierForType finds the value node corresponding to | ||
// the type and emits that symbol if possible. This causes a source mapping to the value, | ||
// which then allows later transformers in the pipeline to do the correct module | ||
// rewriting. Note that we cannot use param.type as the emit node directly (not even just | ||
// for mapping), because that is marked as a type use of the node, not a value use, so it | ||
// doesn't get updated as an export. | ||
var sym = this.typeChecker.getTypeAtLocation(param.type).getSymbol(); | ||
var emitNode = this.getValueIdentifierForType(sym, param.type); | ||
if (emitNode) { | ||
this.rewriter.writeRange(emitNode, emitNode.getStart(), emitNode.getEnd()); | ||
} | ||
else { | ||
var typeStr = new type_translator_1.TypeTranslator(this.typeChecker, param.type) | ||
.symbolToString(sym, /* useFqn */ true); | ||
this.rewriter.emit(typeStr); | ||
} | ||
} | ||
} | ||
this.rewriter.emit(", "); | ||
if (param.decorators) { | ||
this.rewriter.emit('decorators: ['); | ||
for (var _d = 0, _e = param.decorators; _d < _e.length; _d++) { | ||
var decorator = _e[_d]; | ||
this.emitDecorator(decorator); | ||
this.rewriter.emit(', '); | ||
this.rewriter.emit(", "); | ||
if (param.decorators) { | ||
this.rewriter.emit('decorators: ['); | ||
try { | ||
for (var _f = __values(param.decorators), _g = _f.next(); !_g.done; _g = _f.next()) { | ||
var decorator = _g.value; | ||
this.emitDecorator(decorator); | ||
this.rewriter.emit(', '); | ||
} | ||
} | ||
catch (e_6_1) { e_6 = { error: e_6_1 }; } | ||
finally { | ||
try { | ||
if (_g && !_g.done && (_h = _f.return)) _h.call(_f); | ||
} | ||
finally { if (e_6) throw e_6.error; } | ||
} | ||
this.rewriter.emit(']'); | ||
} | ||
this.rewriter.emit(']'); | ||
this.rewriter.emit('},\n'); | ||
} | ||
this.rewriter.emit('},\n'); | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (_e && !_e.done && (_j = _d.return)) _j.call(_d); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
this.rewriter.emit("];\n"); | ||
@@ -299,14 +395,33 @@ } | ||
this.rewriter.emit("static propDecorators: {[key: string]: " + decoratorInvocations + "} = {\n"); | ||
for (var _f = 0, _g = util_1.toArray(this.propDecorators.keys()); _f < _g.length; _f++) { | ||
var name_2 = _g[_f]; | ||
this.rewriter.emit("\"" + name_2 + "\": ["); | ||
for (var _h = 0, _j = this.propDecorators.get(name_2); _h < _j.length; _h++) { | ||
var decorator = _j[_h]; | ||
this.emitDecorator(decorator); | ||
this.rewriter.emit(','); | ||
try { | ||
for (var _k = __values(util_1.toArray(this.propDecorators.keys())), _l = _k.next(); !_l.done; _l = _k.next()) { | ||
var name_2 = _l.value; | ||
this.rewriter.emit("\"" + name_2 + "\": ["); | ||
try { | ||
for (var _m = __values(this.propDecorators.get(name_2)), _o = _m.next(); !_o.done; _o = _m.next()) { | ||
var decorator = _o.value; | ||
this.emitDecorator(decorator); | ||
this.rewriter.emit(','); | ||
} | ||
} | ||
catch (e_8_1) { e_8 = { error: e_8_1 }; } | ||
finally { | ||
try { | ||
if (_o && !_o.done && (_p = _m.return)) _p.call(_m); | ||
} | ||
finally { if (e_8) throw e_8.error; } | ||
} | ||
this.rewriter.emit('],\n'); | ||
} | ||
this.rewriter.emit('],\n'); | ||
} | ||
catch (e_9_1) { e_9 = { error: e_9_1 }; } | ||
finally { | ||
try { | ||
if (_l && !_l.done && (_q = _k.return)) _q.call(_k); | ||
} | ||
finally { if (e_9) throw e_9.error; } | ||
} | ||
this.rewriter.emit('};\n'); | ||
} | ||
var e_5, _c, e_7, _j, e_6, _h, e_9, _q, e_8, _p; | ||
}; | ||
@@ -327,7 +442,16 @@ DecoratorClassVisitor.prototype.emitDecorator = function (decorator) { | ||
this.rewriter.emit(', args: ['); | ||
for (var _i = 0, _a = call.arguments; _i < _a.length; _i++) { | ||
var arg = _a[_i]; | ||
this.rewriter.writeNodeFrom(arg, arg.getStart()); | ||
this.rewriter.emit(', '); | ||
try { | ||
for (var _a = __values(call.arguments), _b = _a.next(); !_b.done; _b = _a.next()) { | ||
var arg = _b.value; | ||
this.rewriter.writeNodeFrom(arg, arg.getStart()); | ||
this.rewriter.emit(', '); | ||
} | ||
} | ||
catch (e_10_1) { e_10 = { error: e_10_1 }; } | ||
finally { | ||
try { | ||
if (_b && !_b.done && (_c = _a.return)) _c.call(_a); | ||
} | ||
finally { if (e_10) throw e_10.error; } | ||
} | ||
this.rewriter.emit(']'); | ||
@@ -341,2 +465,3 @@ } | ||
this.rewriter.emit(' }'); | ||
var e_10, _c; | ||
}; | ||
@@ -364,3 +489,3 @@ return DecoratorClassVisitor; | ||
case ts.SyntaxKind.ImportDeclaration: | ||
(_a = this.importedNames).push.apply(_a, collectImportedNames(this.typeChecker, node)); | ||
(_a = this.importedNames).push.apply(_a, __spread(collectImportedNames(this.typeChecker, node))); | ||
return false; | ||
@@ -416,24 +541,44 @@ case ts.SyntaxKind.Decorator: | ||
var namedImports = importClause.namedBindings; | ||
names.push.apply(names, namedImports.elements.map(function (e) { return e.name; })); | ||
names.push.apply(names, __spread(namedImports.elements.map(function (e) { return e.name; }))); | ||
} | ||
for (var _i = 0, names_1 = names; _i < names_1.length; _i++) { | ||
var name_3 = names_1[_i]; | ||
var symbol = typeChecker.getSymbolAtLocation(name_3); | ||
if (symbol.flags & ts.SymbolFlags.Alias) { | ||
symbol = typeChecker.getAliasedSymbol(symbol); | ||
} | ||
var declarationNames = []; | ||
if (symbol.declarations) { | ||
for (var _a = 0, _b = symbol.declarations; _a < _b.length; _a++) { | ||
var d = _b[_a]; | ||
if (d.name && d.name.kind === ts.SyntaxKind.Identifier) { | ||
declarationNames.push(d.name); | ||
try { | ||
for (var names_1 = __values(names), names_1_1 = names_1.next(); !names_1_1.done; names_1_1 = names_1.next()) { | ||
var name_3 = names_1_1.value; | ||
var symbol = typeChecker.getSymbolAtLocation(name_3); | ||
if (symbol.flags & ts.SymbolFlags.Alias) { | ||
symbol = typeChecker.getAliasedSymbol(symbol); | ||
} | ||
var declarationNames = []; | ||
if (symbol.declarations) { | ||
try { | ||
for (var _a = __values(symbol.declarations), _b = _a.next(); !_b.done; _b = _a.next()) { | ||
var d = _b.value; | ||
var decl_1 = d; | ||
if (decl_1.name && decl_1.name.kind === ts.SyntaxKind.Identifier) { | ||
declarationNames.push(decl_1.name); | ||
} | ||
} | ||
} | ||
catch (e_11_1) { e_11 = { error: e_11_1 }; } | ||
finally { | ||
try { | ||
if (_b && !_b.done && (_c = _a.return)) _c.call(_a); | ||
} | ||
finally { if (e_11) throw e_11.error; } | ||
} | ||
} | ||
if (symbol.declarations) { | ||
importedNames.push({ name: name_3, declarationNames: declarationNames }); | ||
} | ||
} | ||
if (symbol.declarations) { | ||
importedNames.push({ name: name_3, declarationNames: declarationNames }); | ||
} | ||
catch (e_12_1) { e_12 = { error: e_12_1 }; } | ||
finally { | ||
try { | ||
if (names_1_1 && !names_1_1.done && (_d = names_1.return)) _d.call(names_1); | ||
} | ||
finally { if (e_12) throw e_12.error; } | ||
} | ||
return importedNames; | ||
var e_12, _d, e_11, _c; | ||
} | ||
@@ -440,0 +585,0 @@ exports.collectImportedNames = collectImportedNames; |
@@ -9,2 +9,12 @@ "use strict"; | ||
*/ | ||
var __values = (this && this.__values) || function (o) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; | ||
if (m) return m.call(o); | ||
return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -54,9 +64,19 @@ var ts = require("typescript"); | ||
} | ||
for (var _i = 0, range_1 = range; _i < range_1.length; _i++) { | ||
var _a = range_1[_i], pos = _a.pos, end = _a.end; | ||
if (/@ExportDecoratedItems\b/.test(declaration.getFullText().substring(pos, end))) { | ||
return true; | ||
try { | ||
for (var range_1 = __values(range), range_1_1 = range_1.next(); !range_1_1.done; range_1_1 = range_1.next()) { | ||
var _a = range_1_1.value, pos = _a.pos, end = _a.end; | ||
if (/@ExportDecoratedItems\b/.test(declaration.getFullText().substring(pos, end))) { | ||
return true; | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (range_1_1 && !range_1_1.done && (_b = range_1.return)) _b.call(range_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
return false; | ||
var e_1, _b; | ||
}); | ||
@@ -63,0 +83,0 @@ } |
@@ -19,2 +19,12 @@ "use strict"; | ||
})(); | ||
var __values = (this && this.__values) || function (o) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; | ||
if (m) return m.call(o); | ||
return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -42,6 +52,5 @@ var ts = require("typescript"); | ||
__extends(ES5Processor, _super); | ||
function ES5Processor(host, options, file) { | ||
function ES5Processor(host, file) { | ||
var _this = _super.call(this, file) || this; | ||
_this.host = host; | ||
_this.options = options; | ||
/** | ||
@@ -77,4 +86,4 @@ * namespaceImports collects the variables for imported goog.modules. | ||
this.emit("goog.module('" + moduleName + "');"); | ||
if (this.options.prelude) | ||
this.emit(this.options.prelude); | ||
if (this.host.prelude) | ||
this.emit(this.host.prelude); | ||
// Allow code to use `module.id` to discover its module URL, e.g. to resolve | ||
@@ -85,3 +94,3 @@ // a template URL against. | ||
// optimizations mode. | ||
if (this.options.es5Mode) { | ||
if (this.host.es5Mode) { | ||
this.emit("var module = module || {id: '" + moduleId + "'};"); | ||
@@ -98,8 +107,17 @@ } | ||
var pos = 0; | ||
for (var _i = 0, _a = this.file.statements; _i < _a.length; _i++) { | ||
var stmt = _a[_i]; | ||
this.writeRange(this.file, pos, stmt.getFullStart()); | ||
this.visitTopLevel(stmt); | ||
pos = stmt.getEnd(); | ||
try { | ||
for (var _a = __values(this.file.statements), _b = _a.next(); !_b.done; _b = _a.next()) { | ||
var stmt = _b.value; | ||
this.writeRange(this.file, pos, stmt.getFullStart()); | ||
this.visitTopLevel(stmt); | ||
pos = stmt.getEnd(); | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (_b && !_b.done && (_c = _a.return)) _c.call(_a); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
this.writeRange(this.file, pos, this.file.getEnd()); | ||
@@ -111,2 +129,3 @@ var referencedModules = util_1.toArray(this.moduleVariables.keys()); | ||
return { output: output, referencedModules: referencedModules }; | ||
var e_1, _c; | ||
}; | ||
@@ -374,19 +393,29 @@ /** | ||
*/ | ||
function processES5(host, options, fileName, content) { | ||
function processES5(host, fileName, content) { | ||
var file = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES5, true); | ||
return new ES5Processor(host, options, file).process(); | ||
return new ES5Processor(host, file).process(); | ||
} | ||
exports.processES5 = processES5; | ||
function convertCommonJsToGoogModuleIfNeeded(host, options, modulesManifest, fileName, content) { | ||
if (!options.googmodule || tsickle_1.isDtsFileName(fileName)) { | ||
function convertCommonJsToGoogModuleIfNeeded(host, modulesManifest, fileName, content) { | ||
if (!host.googmodule || tsickle_1.isDtsFileName(fileName)) { | ||
return content; | ||
} | ||
var _a = processES5(host, options, fileName, content), output = _a.output, referencedModules = _a.referencedModules; | ||
var _a = processES5(host, fileName, content), output = _a.output, referencedModules = _a.referencedModules; | ||
var moduleName = host.pathToModuleName('', fileName); | ||
modulesManifest.addModule(fileName, moduleName); | ||
for (var _i = 0, referencedModules_1 = referencedModules; _i < referencedModules_1.length; _i++) { | ||
var referenced = referencedModules_1[_i]; | ||
modulesManifest.addReferencedModule(fileName, referenced); | ||
try { | ||
for (var referencedModules_1 = __values(referencedModules), referencedModules_1_1 = referencedModules_1.next(); !referencedModules_1_1.done; referencedModules_1_1 = referencedModules_1.next()) { | ||
var referenced = referencedModules_1_1.value; | ||
modulesManifest.addReferencedModule(fileName, referenced); | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (referencedModules_1_1 && !referencedModules_1_1.done && (_b = referencedModules_1.return)) _b.call(referencedModules_1); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
return output; | ||
var e_2, _b; | ||
} | ||
@@ -393,0 +422,0 @@ exports.convertCommonJsToGoogModuleIfNeeded = convertCommonJsToGoogModuleIfNeeded; |
@@ -9,2 +9,28 @@ "use strict"; | ||
*/ | ||
var __values = (this && this.__values) || function (o) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; | ||
if (m) return m.call(o); | ||
return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -103,61 +129,70 @@ var util_1 = require("./util"); | ||
var warnings = []; | ||
for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) { | ||
var line = lines_1[_i]; | ||
match = line.match(/^@(\S+) *(.*)/); | ||
if (match) { | ||
var _ = match[0], tagName = match[1], text = match[2]; | ||
if (tagName === 'returns') { | ||
// A synonym for 'return'. | ||
tagName = 'return'; | ||
try { | ||
for (var lines_1 = __values(lines), lines_1_1 = lines_1.next(); !lines_1_1.done; lines_1_1 = lines_1.next()) { | ||
var line = lines_1_1.value; | ||
match = line.match(/^@(\S+) *(.*)/); | ||
if (match) { | ||
var _a = __read(match, 3), _ = _a[0], tagName = _a[1], text = _a[2]; | ||
if (tagName === 'returns') { | ||
// A synonym for 'return'. | ||
tagName = 'return'; | ||
} | ||
var type = void 0; | ||
if (JSDOC_TAGS_BLACKLIST.has(tagName)) { | ||
warnings.push("@" + tagName + " annotations are redundant with TypeScript equivalents"); | ||
continue; // Drop the tag so Closure won't process it. | ||
} | ||
else if (JSDOC_TAGS_WITH_TYPES.has(tagName) && text[0] === '{') { | ||
warnings.push("the type annotation on @" + tagName + " is redundant with its TypeScript type, " + | ||
"remove the {...} part"); | ||
continue; | ||
} | ||
else if (tagName === 'suppress') { | ||
var suppressMatch = text.match(/^\{(.*)\}(.*)$/); | ||
if (!suppressMatch) { | ||
warnings.push("malformed @suppress tag: \"" + text + "\""); | ||
} | ||
else { | ||
_b = __read(suppressMatch, 3), type = _b[1], text = _b[2]; | ||
} | ||
} | ||
else if (tagName === 'dict') { | ||
warnings.push('use index signatures (`[k: string]: type`) instead of @dict'); | ||
continue; | ||
} | ||
// Grab the parameter name from @param tags. | ||
var parameterName = void 0; | ||
if (tagName === 'param') { | ||
match = text.match(/^(\S+) ?(.*)/); | ||
if (match) | ||
_c = __read(match, 3), _ = _c[0], parameterName = _c[1], text = _c[2]; | ||
} | ||
var tag = { tagName: tagName }; | ||
if (parameterName) | ||
tag.parameterName = parameterName; | ||
if (text) | ||
tag.text = text; | ||
if (type) | ||
tag.type = type; | ||
tags.push(tag); | ||
} | ||
var type = void 0; | ||
if (JSDOC_TAGS_BLACKLIST.has(tagName)) { | ||
warnings.push("@" + tagName + " annotations are redundant with TypeScript equivalents"); | ||
continue; // Drop the tag so Closure won't process it. | ||
} | ||
else if (JSDOC_TAGS_WITH_TYPES.has(tagName) && text[0] === '{') { | ||
warnings.push("the type annotation on @" + tagName + " is redundant with its TypeScript type, " + | ||
"remove the {...} part"); | ||
continue; | ||
} | ||
else if (tagName === 'suppress') { | ||
var suppressMatch = text.match(/^\{(.*)\}(.*)$/); | ||
if (!suppressMatch) { | ||
warnings.push("malformed @suppress tag: \"" + text + "\""); | ||
else { | ||
// Text without a preceding @tag on it is either the plain text | ||
// documentation or a continuation of a previous tag. | ||
if (tags.length === 0) { | ||
tags.push({ tagName: '', text: line }); | ||
} | ||
else { | ||
type = suppressMatch[1], text = suppressMatch[2]; | ||
var lastTag = tags[tags.length - 1]; | ||
lastTag.text = (lastTag.text || '') + '\n' + line; | ||
} | ||
} | ||
else if (tagName === 'dict') { | ||
warnings.push('use index signatures (`[k: string]: type`) instead of @dict'); | ||
continue; | ||
} | ||
// Grab the parameter name from @param tags. | ||
var parameterName = void 0; | ||
if (tagName === 'param') { | ||
match = text.match(/^(\S+) ?(.*)/); | ||
if (match) | ||
_ = match[0], parameterName = match[1], text = match[2]; | ||
} | ||
var tag = { tagName: tagName }; | ||
if (parameterName) | ||
tag.parameterName = parameterName; | ||
if (text) | ||
tag.text = text; | ||
if (type) | ||
tag.type = type; | ||
tags.push(tag); | ||
} | ||
else { | ||
// Text without a preceding @tag on it is either the plain text | ||
// documentation or a continuation of a previous tag. | ||
if (tags.length === 0) { | ||
tags.push({ tagName: '', text: line }); | ||
} | ||
else { | ||
var lastTag = tags[tags.length - 1]; | ||
lastTag.text = (lastTag.text || '') + '\n' + line; | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (lines_1_1 && !lines_1_1.done && (_d = lines_1.return)) _d.call(lines_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
@@ -168,2 +203,3 @@ if (warnings.length > 0) { | ||
return { tags: tags }; | ||
var e_1, _d, _b, _c; | ||
} | ||
@@ -236,15 +272,25 @@ exports.parse = parse; | ||
var emitted = new Set(); | ||
for (var _i = 0, tags_1 = tags; _i < tags_1.length; _i++) { | ||
var tag = tags_1[_i]; | ||
if (emitted.has(tag.tagName) && SINGLETON_TAGS.has(tag.tagName)) { | ||
continue; | ||
try { | ||
for (var tags_1 = __values(tags), tags_1_1 = tags_1.next(); !tags_1_1.done; tags_1_1 = tags_1.next()) { | ||
var tag = tags_1_1.value; | ||
if (emitted.has(tag.tagName) && SINGLETON_TAGS.has(tag.tagName)) { | ||
continue; | ||
} | ||
emitted.add(tag.tagName); | ||
out += ' *'; | ||
// If the tagToString is multi-line, insert " * " prefixes on subsequent lines. | ||
out += tagToString(tag, escapeExtraTags).split('\n').join('\n * '); | ||
out += '\n'; | ||
} | ||
emitted.add(tag.tagName); | ||
out += ' *'; | ||
// If the tagToString is multi-line, insert " * " prefixes on subsequent lines. | ||
out += tagToString(tag, escapeExtraTags).split('\n').join('\n * '); | ||
out += '\n'; | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (tags_1_1 && !tags_1_1.done && (_a = tags_1.return)) _a.call(tags_1); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
out += ' */\n'; | ||
return out; | ||
var e_2, _a; | ||
} | ||
@@ -261,17 +307,26 @@ exports.toString = toString; | ||
var restParam = false; | ||
for (var _i = 0, tags_2 = tags; _i < tags_2.length; _i++) { | ||
var tag_1 = tags_2[_i]; | ||
if (tag_1.tagName) | ||
tagNames.add(tag_1.tagName); | ||
if (tag_1.parameterName) | ||
parameterNames.add(tag_1.parameterName); | ||
if (tag_1.type) | ||
types.add(tag_1.type); | ||
if (tag_1.text) | ||
texts.add(tag_1.text); | ||
if (tag_1.optional) | ||
optional = true; | ||
if (tag_1.restParam) | ||
restParam = true; | ||
try { | ||
for (var tags_2 = __values(tags), tags_2_1 = tags_2.next(); !tags_2_1.done; tags_2_1 = tags_2.next()) { | ||
var tag_1 = tags_2_1.value; | ||
if (tag_1.tagName) | ||
tagNames.add(tag_1.tagName); | ||
if (tag_1.parameterName) | ||
parameterNames.add(tag_1.parameterName); | ||
if (tag_1.type) | ||
types.add(tag_1.type); | ||
if (tag_1.text) | ||
texts.add(tag_1.text); | ||
if (tag_1.optional) | ||
optional = true; | ||
if (tag_1.restParam) | ||
restParam = true; | ||
} | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
try { | ||
if (tags_2_1 && !tags_2_1.done && (_a = tags_2.return)) _a.call(tags_2); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
if (tagNames.size !== 1) { | ||
@@ -290,2 +345,3 @@ throw new Error("cannot merge differing tags: " + JSON.stringify(tags)); | ||
return tag; | ||
var e_3, _a; | ||
} | ||
@@ -292,0 +348,0 @@ exports.merge = merge; |
#!/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 __values = (this && this.__values) || function (o) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; | ||
if (m) return m.call(o); | ||
return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
}; | ||
@@ -19,3 +21,3 @@ Object.defineProperty(exports, "__esModule", { value: true }); | ||
var tsickle = require("./tsickle"); | ||
var util_1 = require("./util"); | ||
var tsickle_1 = require("./tsickle"); | ||
function usage() { | ||
@@ -31,50 +33,54 @@ 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 --typed [experimental] attempt to provide Closure types instead of {?}\n"); | ||
var parsedArgs = minimist(args); | ||
for (var _i = 0, _a = Object.keys(parsedArgs); _i < _a.length; _i++) { | ||
var flag = _a[_i]; | ||
switch (flag) { | ||
case 'h': | ||
case 'help': | ||
usage(); | ||
process.exit(0); | ||
break; | ||
case 'externs': | ||
settings.externsPath = parsedArgs[flag]; | ||
break; | ||
case 'typed': | ||
settings.isTyped = true; | ||
break; | ||
case 'verbose': | ||
settings.verbose = true; | ||
break; | ||
case '_': | ||
// This is part of the minimist API, and holds args after the '--'. | ||
break; | ||
default: | ||
console.error("unknown flag '--" + flag + "'"); | ||
usage(); | ||
process.exit(1); | ||
try { | ||
for (var _a = __values(Object.keys(parsedArgs)), _b = _a.next(); !_b.done; _b = _a.next()) { | ||
var flag = _b.value; | ||
switch (flag) { | ||
case 'h': | ||
case 'help': | ||
usage(); | ||
process.exit(0); | ||
break; | ||
case 'externs': | ||
settings.externsPath = parsedArgs[flag]; | ||
break; | ||
case 'typed': | ||
settings.isTyped = true; | ||
break; | ||
case 'verbose': | ||
settings.verbose = true; | ||
break; | ||
case '_': | ||
// This is part of the minimist API, and holds args after the '--'. | ||
break; | ||
default: | ||
console.error("unknown flag '--" + flag + "'"); | ||
usage(); | ||
process.exit(1); | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (_b && !_b.done && (_c = _a.return)) _c.call(_a); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
// Arguments after the '--' arg are arguments to tsc. | ||
var tscArgs = parsedArgs['_']; | ||
return { settings: settings, tscArgs: tscArgs }; | ||
var e_1, _c; | ||
} | ||
/** | ||
* Loads the tsconfig.json from a directory. | ||
* Unfortunately there's a ton of logic in tsc.ts related to searching | ||
* for tsconfig.json etc. that we don't really want to replicate, e.g. | ||
* tsc appears to allow -p path/to/tsconfig.json while this only works | ||
* with -p path/to/containing/dir. | ||
* | ||
* TODO(martinprobst): use ts.findConfigFile to match tsc behaviour. | ||
* | ||
* @param args tsc command-line arguments. | ||
*/ | ||
function loadTscConfig(args, allDiagnostics) { | ||
function loadTscConfig(args) { | ||
// Gather tsc options/input files from command line. | ||
// Bypass visibilty of parseCommandLine, see | ||
// https://github.com/Microsoft/TypeScript/issues/2620 | ||
// tslint:disable-next-line:no-any | ||
var _a = ts.parseCommandLine(args), options = _a.options, fileNames = _a.fileNames, errors = _a.errors; | ||
if (errors.length > 0) { | ||
allDiagnostics.push.apply(allDiagnostics, errors); | ||
return null; | ||
return { options: {}, fileNames: [], errors: errors }; | ||
} | ||
@@ -88,71 +94,46 @@ // Store file arguments | ||
if (error) { | ||
allDiagnostics.push(error); | ||
return null; | ||
return { options: {}, fileNames: [], errors: [error] }; | ||
} | ||
(_c = ts.parseJsonConfigFileContent(json, ts.sys, projectDir, options, configFileName), options = _c.options, fileNames = _c.fileNames, errors = _c.errors); | ||
if (errors.length > 0) { | ||
allDiagnostics.push.apply(allDiagnostics, errors); | ||
return null; | ||
return { options: {}, fileNames: [], errors: errors }; | ||
} | ||
// if file arguments were given to the typescript transpiler than transpile only those files | ||
// if file arguments were given to the typescript transpiler then transpile only those files | ||
fileNames = tsFileArguments.length > 0 ? tsFileArguments : fileNames; | ||
return { options: options, fileNames: fileNames }; | ||
return { options: options, fileNames: fileNames, errors: [] }; | ||
var _c; | ||
} | ||
function getDefaultClosureJSOptions(fileNames, settings) { | ||
return { | ||
tsickleCompilerHostOptions: { | ||
googmodule: true, | ||
es5Mode: false, | ||
untyped: !settings.isTyped, | ||
}, | ||
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], | ||
}; | ||
} | ||
/** | ||
* Compiles TypeScript code into Closure-compiler-ready JS. | ||
* Doesn't write any files to disk; all JS content is returned in a 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. | ||
// This is so: | ||
// - error messages point at the original source text | ||
// - tsickle can use the result of typechecking for annotation | ||
var jsFiles = new Map(); | ||
var outputRetainingHost = util_1.createOutputRetainingCompilerHost(jsFiles, ts.createCompilerHost(options)); | ||
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); | ||
{ | ||
var diagnostics_1 = ts.getPreEmitDiagnostics(program); | ||
if (diagnostics_1.length > 0) { | ||
allDiagnostics.push.apply(allDiagnostics, diagnostics_1); | ||
return null; | ||
} | ||
} | ||
// Reparse and reload the program, inserting the tsickle output in | ||
// place of the original source. | ||
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; | ||
function toClosureJS(options, fileNames, settings, writeFile) { | ||
var compilerHost = ts.createCompilerHost(options); | ||
var program = ts.createProgram(fileNames, options, compilerHost); | ||
var transformerHost = { | ||
shouldSkipTsickleProcessing: function (fileName) { | ||
return fileNames.indexOf(fileName) === -1; | ||
}, | ||
shouldIgnoreWarningsForPath: function (fileName) { return false; }, | ||
pathToModuleName: cliSupport.pathToModuleName, | ||
fileNameToModuleId: function (fileName) { return fileName; }, | ||
es5Mode: true, | ||
googmodule: true, | ||
prelude: '', | ||
transformDecorators: true, | ||
transformTypesToClosure: true, | ||
typeBlackListPaths: new Set(), | ||
untyped: false, | ||
logWarning: function (warning) { return console.error(tsickle.formatDiagnostics([warning])); }, | ||
}; | ||
var diagnostics = ts.getPreEmitDiagnostics(program); | ||
if (diagnostics.length > 0) { | ||
allDiagnostics.push.apply(allDiagnostics, diagnostics); | ||
return null; | ||
return { | ||
diagnostics: diagnostics, | ||
modulesManifest: new tsickle_1.ModulesManifest(), | ||
externs: {}, | ||
emitSkipped: true, | ||
emittedFiles: [], | ||
}; | ||
} | ||
return { jsFiles: jsFiles, externs: tch.getGeneratedExterns() }; | ||
return tsickle.emitWithTsickle(program, transformerHost, compilerHost, options, undefined, writeFile); | ||
} | ||
@@ -162,6 +143,5 @@ exports.toClosureJS = toClosureJS; | ||
var _a = loadSettingsFromArgs(args), settings = _a.settings, tscArgs = _a.tscArgs; | ||
var diagnostics = []; | ||
var config = loadTscConfig(tscArgs, diagnostics); | ||
if (config === null) { | ||
console.error(tsickle.formatDiagnostics(diagnostics)); | ||
var config = loadTscConfig(tscArgs); | ||
if (config.errors.length) { | ||
console.error(tsickle.formatDiagnostics(config.errors)); | ||
return 1; | ||
@@ -172,19 +152,18 @@ } | ||
// through the diagnostics array mechanism. | ||
console.error('tsickle converts TypeScript modules to Closure modules via CommonJS internally. Set tsconfig.js "module": "commonjs"'); | ||
console.error('tsickle converts TypeScript modules to Closure modules via CommonJS internally. ' + | ||
'Set tsconfig.js "module": "commonjs"'); | ||
return 1; | ||
} | ||
// Run tsickle+TSC to convert inputs to Closure JS files. | ||
var closure = toClosureJS(config.options, config.fileNames, settings, diagnostics); | ||
if (closure === null) { | ||
console.error(tsickle.formatDiagnostics(diagnostics)); | ||
var result = toClosureJS(config.options, config.fileNames, settings, function (filePath, contents) { | ||
mkdirp.sync(path.dirname(filePath)); | ||
fs.writeFileSync(filePath, contents, { encoding: 'utf-8' }); | ||
}); | ||
if (result.diagnostics.length) { | ||
console.error(tsickle.formatDiagnostics(result.diagnostics)); | ||
return 1; | ||
} | ||
for (var _i = 0, _b = util_1.toArray(closure.jsFiles.keys()); _i < _b.length; _i++) { | ||
var fileName = _b[_i]; | ||
mkdirp.sync(path.dirname(fileName)); | ||
fs.writeFileSync(fileName, closure.jsFiles.get(fileName)); | ||
} | ||
if (settings.externsPath) { | ||
mkdirp.sync(path.dirname(settings.externsPath)); | ||
fs.writeFileSync(settings.externsPath, closure.externs); | ||
fs.writeFileSync(settings.externsPath, tsickle.getGeneratedExterns(result.externs)); | ||
} | ||
@@ -191,0 +170,0 @@ return 0; |
@@ -9,2 +9,12 @@ "use strict"; | ||
*/ | ||
var __values = (this && this.__values) || function (o) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; | ||
if (m) return m.call(o); | ||
return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -148,11 +158,21 @@ var ts = require("typescript"); | ||
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 = 0; | ||
try { | ||
for (var str_1 = __values(str), str_1_1 = str_1.next(); !str_1_1.done; str_1_1 = str_1.next()) { | ||
var c = str_1_1.value; | ||
this.position.column++; | ||
if (c === '\n') { | ||
this.position.line++; | ||
this.position.column = 0; | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (str_1_1 && !str_1_1.done && (_a = str_1.return)) _a.call(str_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
this.position.position += str.length; | ||
var e_1, _a; | ||
}; | ||
@@ -159,0 +179,0 @@ /** Removes comment metacharacters from a string, to make it safe to embed in a comment. */ |
@@ -9,2 +9,12 @@ "use strict"; | ||
*/ | ||
var __values = (this && this.__values) || function (o) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; | ||
if (m) return m.call(o); | ||
return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -442,17 +452,26 @@ var ts = require("typescript"); | ||
var lastComment = undefined; | ||
for (var _i = 0, leadingComments_1 = leadingComments; _i < leadingComments_1.length; _i++) { | ||
var comment = leadingComments_1[_i]; | ||
if (lastComment) { | ||
var lastCommentLine = getLineOfPos(sourceFile, lastComment.end); | ||
var commentLine = getLineOfPos(sourceFile, comment.pos); | ||
if (commentLine >= lastCommentLine + 2) { | ||
// There was a blank line between the last comment and this comment. This | ||
// comment is not part of the copyright comments. Return what we have so | ||
// far. | ||
break; | ||
try { | ||
for (var leadingComments_1 = __values(leadingComments), leadingComments_1_1 = leadingComments_1.next(); !leadingComments_1_1.done; leadingComments_1_1 = leadingComments_1.next()) { | ||
var comment = leadingComments_1_1.value; | ||
if (lastComment) { | ||
var lastCommentLine = getLineOfPos(sourceFile, lastComment.end); | ||
var commentLine = getLineOfPos(sourceFile, comment.pos); | ||
if (commentLine >= lastCommentLine + 2) { | ||
// There was a blank line between the last comment and this comment. This | ||
// comment is not part of the copyright comments. Return what we have so | ||
// far. | ||
break; | ||
} | ||
} | ||
detachedComments.push(comment); | ||
lastComment = comment; | ||
} | ||
detachedComments.push(comment); | ||
lastComment = comment; | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (leadingComments_1_1 && !leadingComments_1_1.done && (_a = leadingComments_1.return)) _a.call(leadingComments_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
if (detachedComments.length) { | ||
@@ -470,2 +489,3 @@ // All comments look like they could have been part of the copyright header. Make | ||
return []; | ||
var e_1, _a; | ||
} | ||
@@ -472,0 +492,0 @@ function getLineOfPos(sourceFile, pos) { |
@@ -9,2 +9,12 @@ "use strict"; | ||
*/ | ||
var __values = (this && this.__values) || function (o) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; | ||
if (m) return m.call(o); | ||
return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -44,8 +54,17 @@ var path = require("path"); | ||
]; | ||
for (var _i = 0, basicTypes_1 = basicTypes; _i < basicTypes_1.length; _i++) { | ||
var flag = basicTypes_1[_i]; | ||
if ((type.flags & flag) !== 0) { | ||
debugString += " " + ts.TypeFlags[flag]; | ||
try { | ||
for (var basicTypes_1 = __values(basicTypes), basicTypes_1_1 = basicTypes_1.next(); !basicTypes_1_1.done; basicTypes_1_1 = basicTypes_1.next()) { | ||
var flag = basicTypes_1_1.value; | ||
if ((type.flags & flag) !== 0) { | ||
debugString += " " + ts.TypeFlags[flag]; | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (basicTypes_1_1 && !basicTypes_1_1.done && (_a = basicTypes_1.return)) _a.call(basicTypes_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
if (type.flags === ts.TypeFlags.Object) { | ||
@@ -64,9 +83,19 @@ var objType = type; | ||
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]; | ||
try { | ||
for (var objectFlags_1 = __values(objectFlags), objectFlags_1_1 = objectFlags_1.next(); !objectFlags_1_1.done; objectFlags_1_1 = objectFlags_1.next()) { | ||
var flag = objectFlags_1_1.value; | ||
if ((objType.objectFlags & flag) !== 0) { | ||
debugString += " object:" + ts.ObjectFlags[flag]; | ||
} | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (objectFlags_1_1 && !objectFlags_1_1.done && (_b = objectFlags_1.return)) _b.call(objectFlags_1); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
} | ||
@@ -80,2 +109,3 @@ if (type.symbol && type.symbol.name !== '__type') { | ||
return "{type " + debugString + "}"; | ||
var e_1, _a, e_2, _b; | ||
} | ||
@@ -116,9 +146,19 @@ exports.typeToDebugString = typeToDebugString; | ||
]; | ||
for (var _i = 0, symbolFlags_1 = symbolFlags; _i < symbolFlags_1.length; _i++) { | ||
var flag = symbolFlags_1[_i]; | ||
if ((sym.flags & flag) !== 0) { | ||
debugString += " " + ts.SymbolFlags[flag]; | ||
try { | ||
for (var symbolFlags_1 = __values(symbolFlags), symbolFlags_1_1 = symbolFlags_1.next(); !symbolFlags_1_1.done; symbolFlags_1_1 = symbolFlags_1.next()) { | ||
var flag = symbolFlags_1_1.value; | ||
if ((sym.flags & flag) !== 0) { | ||
debugString += " " + ts.SymbolFlags[flag]; | ||
} | ||
} | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
try { | ||
if (symbolFlags_1_1 && !symbolFlags_1_1.done && (_a = symbolFlags_1.return)) _a.call(symbolFlags_1); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
return debugString; | ||
var e_3, _a; | ||
} | ||
@@ -235,3 +275,3 @@ exports.symbolToDebugString = symbolToDebugString; | ||
reportInaccessibleThisError: doNothing, | ||
reportIllegalExtends: doNothing, | ||
reportPrivateInBaseOfClassExpression: doNothing, | ||
}; | ||
@@ -267,15 +307,24 @@ builder.buildSymbolDisplay(sym, writer, this.node); | ||
if (type.symbol) { | ||
for (var _i = 0, _a = type.symbol.declarations || []; _i < _a.length; _i++) { | ||
var decl = _a[_i]; | ||
if (ts.isExternalModule(decl.getSourceFile())) | ||
isModule = true; | ||
var current = decl; | ||
while (current) { | ||
if (ts.getCombinedModifierFlags(current) & ts.ModifierFlags.Ambient) | ||
isAmbient = true; | ||
if (current.kind === ts.SyntaxKind.ModuleDeclaration) | ||
isNamespace = true; | ||
current = current.parent; | ||
try { | ||
for (var _a = __values(type.symbol.declarations || []), _b = _a.next(); !_b.done; _b = _a.next()) { | ||
var decl = _b.value; | ||
if (ts.isExternalModule(decl.getSourceFile())) | ||
isModule = true; | ||
var current = decl; | ||
while (current) { | ||
if (ts.getCombinedModifierFlags(current) & ts.ModifierFlags.Ambient) | ||
isAmbient = true; | ||
if (current.kind === ts.SyntaxKind.ModuleDeclaration) | ||
isNamespace = true; | ||
current = current.parent; | ||
} | ||
} | ||
} | ||
catch (e_4_1) { e_4 = { error: e_4_1 }; } | ||
finally { | ||
try { | ||
if (_b && !_b.done && (_c = _a.return)) _c.call(_a); | ||
} | ||
finally { if (e_4) throw e_4.error; } | ||
} | ||
} | ||
@@ -311,11 +360,2 @@ // tsickle cannot generate types for non-ambient namespaces. | ||
return this.symbolToString(type.symbol, true); | ||
case ts.TypeFlags.EnumLiteral: | ||
var enumLiteralBaseType = type.baseType; | ||
if (!enumLiteralBaseType.symbol) { | ||
this.warn("EnumLiteralType without a symbol"); | ||
return '?'; | ||
} | ||
// Closure Compiler doesn't support literals in types, so this code must not emit | ||
// "EnumType.MEMBER", but rather "EnumType". The values are de-duplicated in translateUnion. | ||
return this.symbolToString(enumLiteralBaseType.symbol, true); | ||
case ts.TypeFlags.ESSymbol: | ||
@@ -364,5 +404,9 @@ // NOTE: currently this is just a typedef for {?}, shrug. | ||
} | ||
if (type.flags & ts.TypeFlags.EnumLiteral) { | ||
return this.translateEnumLiteral(type); | ||
} | ||
// The switch statement should have been exhaustive. | ||
throw new Error("unknown type flags " + type.flags + " on " + typeToDebugString(type)); | ||
} | ||
var e_4, _c; | ||
}; | ||
@@ -378,2 +422,18 @@ TypeTranslator.prototype.translateUnion = function (type) { | ||
}; | ||
TypeTranslator.prototype.translateEnumLiteral = function (type) { | ||
// Suppose you had: | ||
// enum EnumType { MEMBER } | ||
// then the type of "EnumType.MEMBER" is an enum literal (the thing passed to this function) | ||
// and it has type flags that include | ||
// ts.TypeFlags.NumberLiteral | ts.TypeFlags.EnumLiteral | ||
// | ||
// Closure Compiler doesn't support literals in types, so this code must not emit | ||
// "EnumType.MEMBER", but rather "EnumType". | ||
var enumLiteralBaseType = this.typeChecker.getBaseTypeOfLiteralType(type); | ||
if (!enumLiteralBaseType.symbol) { | ||
this.warn("EnumLiteralType without a symbol"); | ||
return '?'; | ||
} | ||
return this.symbolToString(enumLiteralBaseType.symbol, true); | ||
}; | ||
// translateObject translates a ts.ObjectType, which is the type of all | ||
@@ -519,19 +579,28 @@ // object-like things in TS, such as classes and interfaces. | ||
} | ||
for (var _i = 0, _a = util_1.toArray(type.symbol.members.keys()); _i < _a.length; _i++) { | ||
var field = _a[_i]; | ||
switch (field) { | ||
case '__call': | ||
callable = true; | ||
break; | ||
case '__index': | ||
indexable = true; | ||
break; | ||
default: | ||
var member = type.symbol.members.get(field); | ||
// optional members are handled by the type including |undefined in a union type. | ||
var memberType = this.translate(this.typeChecker.getTypeOfSymbolAtLocation(member, this.node)); | ||
fields.push(field + ": " + memberType); | ||
break; | ||
try { | ||
for (var _a = __values(util_1.toArray(type.symbol.members.keys())), _b = _a.next(); !_b.done; _b = _a.next()) { | ||
var field = _b.value; | ||
switch (field) { | ||
case '__call': | ||
callable = true; | ||
break; | ||
case '__index': | ||
indexable = true; | ||
break; | ||
default: | ||
var member = type.symbol.members.get(field); | ||
// optional members are handled by the type including |undefined in a union type. | ||
var memberType = this.translate(this.typeChecker.getTypeOfSymbolAtLocation(member, this.node)); | ||
fields.push(field + ": " + memberType); | ||
break; | ||
} | ||
} | ||
} | ||
catch (e_5_1) { e_5 = { error: e_5_1 }; } | ||
finally { | ||
try { | ||
if (_b && !_b.done && (_c = _a.return)) _c.call(_a); | ||
} | ||
finally { if (e_5) throw e_5.error; } | ||
} | ||
// Try to special-case plain key-value objects and functions. | ||
@@ -572,2 +641,3 @@ if (fields.length === 0) { | ||
return '?'; | ||
var e_5, _c; | ||
}; | ||
@@ -574,0 +644,0 @@ /** Converts a ts.Signature (function signature) to a Closure function type. */ |
@@ -58,28 +58,2 @@ "use strict"; | ||
/** | ||
* 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; | ||
/** | ||
* Returns the input string with line endings normalized to '\n'. | ||
@@ -86,0 +60,0 @@ */ |
@@ -81,4 +81,4 @@ /** | ||
.on('error', onError) | ||
.js.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: '../..'})) | ||
.pipe(gulp.dest('built/')); // '/test/' comes from base above. | ||
.js.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: '../../test'})) | ||
.pipe(gulp.dest('built/')); | ||
}); | ||
@@ -85,0 +85,0 @@ |
{ | ||
"name": "tsickle", | ||
"version": "0.23.6", | ||
"version": "0.24.0", | ||
"description": "Transpile TypeScript code to JavaScript with Closure annotations.", | ||
@@ -18,3 +18,3 @@ "main": "built/src/tsickle.js", | ||
"peerDependencies": { | ||
"typescript": "2.3.4" | ||
"typescript": "2.4.2" | ||
}, | ||
@@ -34,3 +34,3 @@ "devDependencies": { | ||
"chai": "^3.5.0", | ||
"clang-format": "^1.0.51", | ||
"clang-format": "^1.0.55", | ||
"diff": "^3.2.0", | ||
@@ -51,3 +51,3 @@ "glob": "^7.0.0", | ||
"tslint": "^5.4.2", | ||
"typescript": "~2.3.4" | ||
"typescript": "~2.4.2" | ||
}, | ||
@@ -54,0 +54,0 @@ "scripts": { |
@@ -122,1 +122,9 @@ # Tsickle - TypeScript to Closure Translator [![Linux build](https://travis-ci.org/angular/tsickle.svg?branch=master)](https://travis-ci.org/angular/tsickle) [![Windows build](https://ci.appveyor.com/api/projects/status/puxdblmlqbofqqt1/branch/master?svg=true)](https://ci.appveyor.com/project/alexeagle/tsickle/branch/master) | ||
tests (found in `test_files/...`) run tests with a name matching the regex. | ||
### Releasing | ||
On a new branch, run `npm version <major|minor|patch|...> -m 'rel: %s'` (see | ||
`npm help version` for details) to update the version in `package.json`, commit | ||
the changes, and create a git tag. Push the branch, get it reviewed and merged, | ||
then run `npm publish` from the master branch (you must be logged into the | ||
`angular` shared npm account). |
@@ -41,1 +41,10 @@ /** | ||
var RegExpExecArray; | ||
/** @typedef {!Array} */ | ||
var ReadonlyArray; | ||
/** @typedef {!Map} */ | ||
var ReadonlyMap; | ||
/** @typedef {!Set} */ | ||
var ReadonlySet; |
@@ -126,3 +126,3 @@ /** | ||
*/ | ||
private gatherMethodOrProperty(method: ts.Declaration) { | ||
private gatherMethodOrProperty(method: ts.NamedDeclaration) { | ||
if (!method.decorators) return; | ||
@@ -156,6 +156,5 @@ if (!method.name || method.name.kind !== ts.SyntaxKind.Identifier) { | ||
|null { | ||
if (!typeSymbol.valueDeclaration) { | ||
return null; | ||
} | ||
const valueName = typeSymbol.valueDeclaration.name; | ||
const valueDeclaration = typeSymbol.valueDeclaration as ts.NamedDeclaration; | ||
if (!valueDeclaration) return null; | ||
const valueName = valueDeclaration.name; | ||
if (!valueName || valueName.kind !== ts.SyntaxKind.Identifier) { | ||
@@ -279,3 +278,3 @@ return null; | ||
// doesn't get updated as an export. | ||
const sym = this.typeChecker.getTypeAtLocation(param.type).getSymbol(); | ||
const sym = this.typeChecker.getTypeAtLocation(param.type).getSymbol()!; | ||
const emitNode = this.getValueIdentifierForType(sym, param.type); | ||
@@ -427,3 +426,3 @@ if (emitNode) { | ||
for (const name of names) { | ||
let symbol = typeChecker.getSymbolAtLocation(name); | ||
let symbol = typeChecker.getSymbolAtLocation(name)!; | ||
if (symbol.flags & ts.SymbolFlags.Alias) { | ||
@@ -435,4 +434,5 @@ symbol = typeChecker.getAliasedSymbol(symbol); | ||
for (const d of symbol.declarations) { | ||
if (d.name && d.name.kind === ts.SyntaxKind.Identifier) { | ||
declarationNames.push(d.name as ts.Identifier); | ||
const decl = d as ts.NamedDeclaration; | ||
if (decl.name && decl.name.kind === ts.SyntaxKind.Identifier) { | ||
declarationNames.push(decl.name as ts.Identifier); | ||
} | ||
@@ -439,0 +439,0 @@ } |
@@ -28,7 +28,9 @@ /** | ||
fileNameToModuleId(fileName: string): string; | ||
} | ||
export interface Es5ProcessorOptions { | ||
/** Whether to convert CommonJS module syntax to `goog.module` Closure imports. */ | ||
googmodule?: boolean; | ||
/** Whether the emit targets ES5 or ES6+. */ | ||
es5Mode?: boolean; | ||
/** | ||
* An additional prelude to insert in front of the emitted code, e.g. to import a shared library. | ||
*/ | ||
prelude?: string; | ||
@@ -77,4 +79,3 @@ } | ||
constructor( | ||
private host: Es5ProcessorHost, private options: Es5ProcessorOptions, file: ts.SourceFile) { | ||
constructor(private host: Es5ProcessorHost, file: ts.SourceFile) { | ||
super(file); | ||
@@ -90,3 +91,3 @@ } | ||
this.emit(`goog.module('${moduleName}');`); | ||
if (this.options.prelude) this.emit(this.options.prelude); | ||
if (this.host.prelude) this.emit(this.host.prelude); | ||
// Allow code to use `module.id` to discover its module URL, e.g. to resolve | ||
@@ -97,3 +98,3 @@ // a template URL against. | ||
// optimizations mode. | ||
if (this.options.es5Mode) { | ||
if (this.host.es5Mode) { | ||
this.emit(`var module = module || {id: '${moduleId}'};`); | ||
@@ -379,16 +380,15 @@ } else { | ||
*/ | ||
export function processES5( | ||
host: Es5ProcessorHost, options: Es5ProcessorOptions, fileName: string, | ||
content: string): {output: string, referencedModules: string[]} { | ||
export function processES5(host: Es5ProcessorHost, fileName: string, content: string): | ||
{output: string, referencedModules: string[]} { | ||
const file = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES5, true); | ||
return new ES5Processor(host, options, file).process(); | ||
return new ES5Processor(host, file).process(); | ||
} | ||
export function convertCommonJsToGoogModuleIfNeeded( | ||
host: Es5ProcessorHost, options: Es5ProcessorOptions, modulesManifest: ModulesManifest, | ||
fileName: string, content: string): string { | ||
if (!options.googmodule || isDtsFileName(fileName)) { | ||
host: Es5ProcessorHost, modulesManifest: ModulesManifest, fileName: string, | ||
content: string): string { | ||
if (!host.googmodule || isDtsFileName(fileName)) { | ||
return content; | ||
} | ||
const {output, referencedModules} = processES5(host, options, fileName, content); | ||
const {output, referencedModules} = processES5(host, fileName, content); | ||
@@ -395,0 +395,0 @@ const moduleName = host.pathToModuleName('', fileName); |
@@ -119,2 +119,12 @@ /** | ||
/** | ||
* Result of parsing a JSDoc comment. Such comments essentially are built of a list of tags. | ||
* In addition to the tags, this might also contain warnings to indicate non-fatal problems | ||
* while finding the tags. | ||
*/ | ||
export interface ParsedJSDocComment { | ||
tags: Tag[]; | ||
warnings?: string[]; | ||
} | ||
/** | ||
* parse parses JSDoc out of a comment string. | ||
@@ -127,3 +137,3 @@ * Returns null if comment is not JSDoc. | ||
// strucure (e.g. a Map<TagName, Values[]>). | ||
export function parse(comment: string): {tags: Tag[], warnings?: string[]}|null { | ||
export function parse(comment: string): ParsedJSDocComment|null { | ||
// Make sure we have proper line endings before parsing on Windows. | ||
@@ -130,0 +140,0 @@ comment = normalizeLineEndings(comment); |
159
src/main.ts
@@ -19,3 +19,5 @@ #!/usr/bin/env node | ||
import * as tsickle from './tsickle'; | ||
import {toArray, createOutputRetainingCompilerHost, createSourceReplacingCompilerHost} from './util'; | ||
import {ModulesManifest} from './tsickle'; | ||
import {toArray, createSourceReplacingCompilerHost} from './util'; | ||
/** Tsickle settings passed on the command line. */ | ||
@@ -84,19 +86,13 @@ export interface Settings { | ||
* Loads the tsconfig.json from a directory. | ||
* Unfortunately there's a ton of logic in tsc.ts related to searching | ||
* for tsconfig.json etc. that we don't really want to replicate, e.g. | ||
* tsc appears to allow -p path/to/tsconfig.json while this only works | ||
* with -p path/to/containing/dir. | ||
* | ||
* TODO(martinprobst): use ts.findConfigFile to match tsc behaviour. | ||
* | ||
* @param args tsc command-line arguments. | ||
*/ | ||
function loadTscConfig(args: string[], allDiagnostics: ts.Diagnostic[]): | ||
{options: ts.CompilerOptions, fileNames: string[]}|null { | ||
function loadTscConfig(args: string[]): | ||
{options: ts.CompilerOptions, fileNames: string[], errors: ts.Diagnostic[]} { | ||
// Gather tsc options/input files from command line. | ||
// Bypass visibilty of parseCommandLine, see | ||
// https://github.com/Microsoft/TypeScript/issues/2620 | ||
// tslint:disable-next-line:no-any | ||
let {options, fileNames, errors} = (ts as any).parseCommandLine(args); | ||
let {options, fileNames, errors} = ts.parseCommandLine(args); | ||
if (errors.length > 0) { | ||
allDiagnostics.push(...errors); | ||
return null; | ||
return {options: {}, fileNames: [], errors}; | ||
} | ||
@@ -113,4 +109,3 @@ | ||
if (error) { | ||
allDiagnostics.push(error); | ||
return null; | ||
return {options: {}, fileNames: [], errors: [error]}; | ||
} | ||
@@ -120,92 +115,47 @@ ({options, fileNames, errors} = | ||
if (errors.length > 0) { | ||
allDiagnostics.push(...errors); | ||
return null; | ||
return {options: {}, fileNames: [], errors}; | ||
} | ||
// if file arguments were given to the typescript transpiler than transpile only those files | ||
// if file arguments were given to the typescript transpiler then transpile only those files | ||
fileNames = tsFileArguments.length > 0 ? tsFileArguments : fileNames; | ||
return {options, fileNames}; | ||
return {options, fileNames, errors: []}; | ||
} | ||
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.isTyped, | ||
}, | ||
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], | ||
}; | ||
} | ||
/** | ||
* 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( | ||
options: ts.CompilerOptions, fileNames: string[], settings: Settings, | ||
allDiagnostics: ts.Diagnostic[], partialClosureJSOptions = {} as Partial<ClosureJSOptions>): | ||
{jsFiles: Map<string, string>, externs: string}|null { | ||
const closureJSOptions: ClosureJSOptions = { | ||
...getDefaultClosureJSOptions(fileNames, settings), | ||
...partialClosureJSOptions | ||
writeFile?: ts.WriteFileCallback): tsickle.EmitResult { | ||
const compilerHost = ts.createCompilerHost(options); | ||
const program = ts.createProgram(fileNames, options, compilerHost); | ||
const transformerHost: tsickle.TsickleHost = { | ||
shouldSkipTsickleProcessing: (fileName: string) => { | ||
return fileNames.indexOf(fileName) === -1; | ||
}, | ||
shouldIgnoreWarningsForPath: (fileName: string) => false, | ||
pathToModuleName: cliSupport.pathToModuleName, | ||
fileNameToModuleId: (fileName) => fileName, | ||
es5Mode: true, | ||
googmodule: true, | ||
prelude: '', | ||
transformDecorators: true, | ||
transformTypesToClosure: true, | ||
typeBlackListPaths: new Set(), | ||
untyped: false, | ||
logWarning: (warning) => console.error(tsickle.formatDiagnostics([warning])), | ||
}; | ||
// Parse and load the program without tsickle processing. | ||
// This is so: | ||
// - error messages point at the original source text | ||
// - tsickle can use the result of typechecking for annotation | ||
const jsFiles = new Map<string, string>(); | ||
const outputRetainingHost = | ||
createOutputRetainingCompilerHost(jsFiles, ts.createCompilerHost(options)); | ||
const sourceReplacingHost = | ||
createSourceReplacingCompilerHost(closureJSOptions.files, outputRetainingHost); | ||
const tch = new tsickle.TsickleCompilerHost( | ||
sourceReplacingHost, options, closureJSOptions.tsickleCompilerHostOptions, | ||
closureJSOptions.tsickleHost); | ||
let program = ts.createProgram(fileNames, options, tch); | ||
{ // Scope for the "diagnostics" variable so we can use the name again later. | ||
const diagnostics = ts.getPreEmitDiagnostics(program); | ||
if (diagnostics.length > 0) { | ||
allDiagnostics.push(...diagnostics); | ||
return null; | ||
} | ||
} | ||
// Reparse and reload the program, inserting the tsickle output in | ||
// place of the original source. | ||
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); | ||
} | ||
const {diagnostics} = program.emit(undefined); | ||
const diagnostics = ts.getPreEmitDiagnostics(program); | ||
if (diagnostics.length > 0) { | ||
allDiagnostics.push(...diagnostics); | ||
return null; | ||
return { | ||
diagnostics, | ||
modulesManifest: new ModulesManifest(), | ||
externs: {}, | ||
emitSkipped: true, | ||
emittedFiles: [], | ||
}; | ||
} | ||
return {jsFiles, externs: tch.getGeneratedExterns()}; | ||
return tsickle.emitWithTsickle( | ||
program, transformerHost, compilerHost, options, undefined, writeFile); | ||
} | ||
@@ -215,6 +165,5 @@ | ||
const {settings, tscArgs} = loadSettingsFromArgs(args); | ||
const diagnostics: ts.Diagnostic[] = []; | ||
const config = loadTscConfig(tscArgs, diagnostics); | ||
if (config === null) { | ||
console.error(tsickle.formatDiagnostics(diagnostics)); | ||
const config = loadTscConfig(tscArgs); | ||
if (config.errors.length) { | ||
console.error(tsickle.formatDiagnostics(config.errors)); | ||
return 1; | ||
@@ -227,3 +176,4 @@ } | ||
console.error( | ||
'tsickle converts TypeScript modules to Closure modules via CommonJS internally. Set tsconfig.js "module": "commonjs"'); | ||
'tsickle converts TypeScript modules to Closure modules via CommonJS internally. ' + | ||
'Set tsconfig.js "module": "commonjs"'); | ||
return 1; | ||
@@ -233,16 +183,15 @@ } | ||
// Run tsickle+TSC to convert inputs to Closure JS files. | ||
const closure = toClosureJS(config.options, config.fileNames, settings, diagnostics); | ||
if (closure === null) { | ||
console.error(tsickle.formatDiagnostics(diagnostics)); | ||
const result = toClosureJS( | ||
config.options, config.fileNames, settings, (filePath: string, contents: string) => { | ||
mkdirp.sync(path.dirname(filePath)); | ||
fs.writeFileSync(filePath, contents, {encoding: 'utf-8'}); | ||
}); | ||
if (result.diagnostics.length) { | ||
console.error(tsickle.formatDiagnostics(result.diagnostics)); | ||
return 1; | ||
} | ||
for (const fileName of toArray(closure.jsFiles.keys())) { | ||
mkdirp.sync(path.dirname(fileName)); | ||
fs.writeFileSync(fileName, closure.jsFiles.get(fileName)); | ||
} | ||
if (settings.externsPath) { | ||
mkdirp.sync(path.dirname(settings.externsPath)); | ||
fs.writeFileSync(settings.externsPath, closure.externs); | ||
fs.writeFileSync(settings.externsPath, tsickle.getGeneratedExterns(result.externs)); | ||
} | ||
@@ -249,0 +198,0 @@ return 0; |
@@ -430,3 +430,3 @@ /** | ||
sourceFile: ts.SourceFile, node: ts.Node, statements: ts.NodeArray<ts.Statement>): | ||
{commentStmt: ts.Statement | null, lastCommentEnd: number} { | ||
{commentStmt: ts.Statement|null, lastCommentEnd: number} { | ||
let triviaEnd = statements.end; | ||
@@ -460,3 +460,3 @@ if (statements.length) { | ||
sourceFile: ts.SourceFile, node: ts.Node, statements: ts.NodeArray<ts.Statement>): | ||
{commentStmt: ts.Statement | null, lastCommentEnd: number} { | ||
{commentStmt: ts.Statement|null, lastCommentEnd: number} { | ||
let trailingCommentStart = statements.end; | ||
@@ -463,0 +463,0 @@ if (statements.length) { |
@@ -64,2 +64,3 @@ /** | ||
ts.ObjectFlags.EvolvingArray, | ||
ts.ObjectFlags.ObjectLiteralPatternWithComputedProperties, | ||
]; | ||
@@ -236,3 +237,3 @@ for (const flag of objectFlags) { | ||
reportInaccessibleThisError: doNothing, | ||
reportIllegalExtends: doNothing, | ||
reportPrivateInBaseOfClassExpression: doNothing, | ||
}; | ||
@@ -311,11 +312,2 @@ builder.buildSymbolDisplay(sym, writer, this.node); | ||
return this.symbolToString(type.symbol, true); | ||
case ts.TypeFlags.EnumLiteral: | ||
const enumLiteralBaseType = (type as ts.EnumLiteralType).baseType; | ||
if (!enumLiteralBaseType.symbol) { | ||
this.warn(`EnumLiteralType without a symbol`); | ||
return '?'; | ||
} | ||
// Closure Compiler doesn't support literals in types, so this code must not emit | ||
// "EnumType.MEMBER", but rather "EnumType". The values are de-duplicated in translateUnion. | ||
return this.symbolToString(enumLiteralBaseType.symbol, true); | ||
case ts.TypeFlags.ESSymbol: | ||
@@ -366,2 +358,6 @@ // NOTE: currently this is just a typedef for {?}, shrug. | ||
if (type.flags & ts.TypeFlags.EnumLiteral) { | ||
return this.translateEnumLiteral(type); | ||
} | ||
// The switch statement should have been exhaustive. | ||
@@ -381,2 +377,20 @@ throw new Error(`unknown type flags ${type.flags} on ${typeToDebugString(type)}`); | ||
private translateEnumLiteral(type: ts.Type): string { | ||
// Suppose you had: | ||
// enum EnumType { MEMBER } | ||
// then the type of "EnumType.MEMBER" is an enum literal (the thing passed to this function) | ||
// and it has type flags that include | ||
// ts.TypeFlags.NumberLiteral | ts.TypeFlags.EnumLiteral | ||
// | ||
// Closure Compiler doesn't support literals in types, so this code must not emit | ||
// "EnumType.MEMBER", but rather "EnumType". | ||
const enumLiteralBaseType = this.typeChecker.getBaseTypeOfLiteralType(type); | ||
if (!enumLiteralBaseType.symbol) { | ||
this.warn(`EnumLiteralType without a symbol`); | ||
return '?'; | ||
} | ||
return this.symbolToString(enumLiteralBaseType.symbol, true); | ||
} | ||
// translateObject translates a ts.ObjectType, which is the type of all | ||
@@ -383,0 +397,0 @@ // object-like things in TS, such as classes and interfaces. |
@@ -63,32 +63,2 @@ /** | ||
/** | ||
* 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, | ||
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); | ||
} | ||
} | ||
/** | ||
* Returns the input string with line endings normalized to '\n'. | ||
@@ -95,0 +65,0 @@ */ |
@@ -11,2 +11,3 @@ { | ||
"types": ["node", "mocha"], | ||
"downlevelIteration": true, | ||
@@ -13,0 +14,0 @@ "noImplicitAny": true, |
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 too big to display
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 too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
130
774595
72
11732