├── test ├── units │ ├── test.d.ts │ ├── class-no-namespace.options.json │ ├── collect-global.options.json │ ├── test.options.json │ ├── collect-global.js │ ├── inheretance.logs.json │ ├── type-guessing.options.json │ ├── inheretance.js │ ├── inheretance.d.ts │ ├── global.js │ ├── collect-global.d.ts │ ├── global.d.ts │ ├── constructors.d.ts │ ├── constructors.js │ ├── class-no-namespace.d.ts │ ├── class.js │ ├── class.d.ts │ ├── class-no-namespace.js │ ├── global.logs.json │ ├── type-guessing.d.ts │ ├── collect-global.logs.json │ ├── constructors.logs.json │ ├── type-guessing.js │ ├── class.logs.json │ ├── class-no-namespace.logs.json │ ├── test.js │ ├── type-guessing.logs.json │ └── test.logs.json ├── class-no-namespace.ts └── units.test.ts ├── dist ├── script.d.ts ├── src │ ├── script.d.ts │ ├── generator │ │ ├── tempCodeRunnerFile.d.ts │ │ ├── errorLogger.d.ts │ │ ├── dtsWriter.d.ts │ │ ├── mockupWriter.d.ts │ │ ├── runner.d.ts │ │ ├── propertyCollector.d.ts │ │ ├── classCollector.d.ts │ │ └── typeGuesser.d.ts │ └── utils.d.ts ├── generator │ ├── runner.d.ts │ ├── errorLogger.d.ts │ ├── dtsWriter.d.ts │ ├── propertyCollector.d.ts │ └── classCollector.d.ts ├── utils.d.ts ├── generator.d.ts └── script.js ├── .gitignore ├── .rpt2_cache ├── rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03 │ ├── types │ │ └── cache │ │ │ ├── 0b5c587aedf983cadc469d3e5dc96dfe81ed05fe │ │ │ ├── 0fc858fe703d4a48467f3a1fa428dbcd5e7feb7c │ │ │ ├── 25394251e30e2ac833a5ed11db7442718c1b98e5 │ │ │ ├── 2b6706a8faec0da117f0941de066ac656d3722c7 │ │ │ ├── 5376db2f59c9453d2e9c233c5ae3f264adf424ef │ │ │ ├── 5748ba272f45518d6cb688fffabce12ab9aed032 │ │ │ ├── 7332a4963198ab211f880bc04e7e9c638afa205e │ │ │ ├── 83c42d800e3b880cf781e6765c757b7ce52d77a6 │ │ │ ├── 976679291e3238e04e2ba1ee4a97ff16e89e8748 │ │ │ ├── b446aa4520220f7d68ba6bfe85a7cd87a1ab0b1d │ │ │ ├── e2a201459d658e08fc0ec6ecae45dabf35d731f5 │ │ │ ├── e772ec774f55a1cfead90808d624fca33a564845 │ │ │ └── f7dcb122f8b6fa01fb95f1fb3a3bfaac334f52c1 │ ├── semanticDiagnostics │ │ └── cache │ │ │ ├── 00e542b81fef2b8bef94eee573d7f567ebebaad2 │ │ │ └── ce51734f7014075100857b852c35f76b8e3a9a2b │ ├── syntacticDiagnostics │ │ └── cache │ │ │ ├── 00e542b81fef2b8bef94eee573d7f567ebebaad2 │ │ │ └── ce51734f7014075100857b852c35f76b8e3a9a2b │ └── code │ │ └── cache │ │ ├── 00e542b81fef2b8bef94eee573d7f567ebebaad2 │ │ └── ce51734f7014075100857b852c35f76b8e3a9a2b ├── rpt2_53f8a438f127461b135955be4d38700f869e071d │ ├── types │ │ └── cache │ │ │ ├── 0b5c587aedf983cadc469d3e5dc96dfe81ed05fe │ │ │ ├── 0fc858fe703d4a48467f3a1fa428dbcd5e7feb7c │ │ │ ├── 25394251e30e2ac833a5ed11db7442718c1b98e5 │ │ │ ├── 2b6706a8faec0da117f0941de066ac656d3722c7 │ │ │ ├── 5376db2f59c9453d2e9c233c5ae3f264adf424ef │ │ │ ├── 5748ba272f45518d6cb688fffabce12ab9aed032 │ │ │ ├── 7332a4963198ab211f880bc04e7e9c638afa205e │ │ │ ├── 83c42d800e3b880cf781e6765c757b7ce52d77a6 │ │ │ ├── 976679291e3238e04e2ba1ee4a97ff16e89e8748 │ │ │ ├── b446aa4520220f7d68ba6bfe85a7cd87a1ab0b1d │ │ │ ├── e2a201459d658e08fc0ec6ecae45dabf35d731f5 │ │ │ ├── e772ec774f55a1cfead90808d624fca33a564845 │ │ │ └── f7dcb122f8b6fa01fb95f1fb3a3bfaac334f52c1 │ ├── semanticDiagnostics │ │ └── cache │ │ │ ├── 400dc2724d60301e5b09363e096017caf00665d0 │ │ │ └── ff2473aaba504293d0401f9ac541b0681b110f3b │ ├── syntacticDiagnostics │ │ └── cache │ │ │ ├── 400dc2724d60301e5b09363e096017caf00665d0 │ │ │ └── ff2473aaba504293d0401f9ac541b0681b110f3b │ └── code │ │ └── cache │ │ ├── ff2473aaba504293d0401f9ac541b0681b110f3b │ │ └── 400dc2724d60301e5b09363e096017caf00665d0 └── rpt2_f45e491452289c1f546923ee50e31be5f6dcef06 │ └── types │ └── cache │ ├── 25394251e30e2ac833a5ed11db7442718c1b98e5 │ ├── 976679291e3238e04e2ba1ee4a97ff16e89e8748 │ └── e2a201459d658e08fc0ec6ecae45dabf35d731f5 ├── jest.config.js ├── src ├── generator │ ├── tempCodeRunnerFile.ts │ ├── errorLogger.ts │ ├── mockupWriter.ts │ ├── runner.ts │ ├── dtsWriter.ts │ ├── propertyCollector.ts │ ├── classCollector.ts │ └── typeGuesser.ts ├── script.ts └── utils.ts ├── tsconfig.json ├── rollup.config.js ├── package.json └── README.md /test/units/test.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist/script.d.ts: -------------------------------------------------------------------------------- 1 | export {} -------------------------------------------------------------------------------- /dist/src/script.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .rpt2_cache 3 | -------------------------------------------------------------------------------- /dist/src/generator/tempCodeRunnerFile.d.ts: -------------------------------------------------------------------------------- 1 | declare const node: any; 2 | -------------------------------------------------------------------------------- /test/units/class-no-namespace.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "namespace": null, 3 | "mockupMode": true 4 | } -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/0b5c587aedf983cadc469d3e5dc96dfe81ed05fe: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/0fc858fe703d4a48467f3a1fa428dbcd5e7feb7c: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/25394251e30e2ac833a5ed11db7442718c1b98e5: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/2b6706a8faec0da117f0941de066ac656d3722c7: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/5376db2f59c9453d2e9c233c5ae3f264adf424ef: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/5748ba272f45518d6cb688fffabce12ab9aed032: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/7332a4963198ab211f880bc04e7e9c638afa205e: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/83c42d800e3b880cf781e6765c757b7ce52d77a6: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/976679291e3238e04e2ba1ee4a97ff16e89e8748: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/b446aa4520220f7d68ba6bfe85a7cd87a1ab0b1d: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/e2a201459d658e08fc0ec6ecae45dabf35d731f5: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/e772ec774f55a1cfead90808d624fca33a564845: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/types/cache/f7dcb122f8b6fa01fb95f1fb3a3bfaac334f52c1: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/0b5c587aedf983cadc469d3e5dc96dfe81ed05fe: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/0fc858fe703d4a48467f3a1fa428dbcd5e7feb7c: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/25394251e30e2ac833a5ed11db7442718c1b98e5: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/2b6706a8faec0da117f0941de066ac656d3722c7: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/5376db2f59c9453d2e9c233c5ae3f264adf424ef: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/5748ba272f45518d6cb688fffabce12ab9aed032: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/7332a4963198ab211f880bc04e7e9c638afa205e: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/83c42d800e3b880cf781e6765c757b7ce52d77a6: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/976679291e3238e04e2ba1ee4a97ff16e89e8748: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/b446aa4520220f7d68ba6bfe85a7cd87a1ab0b1d: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/e2a201459d658e08fc0ec6ecae45dabf35d731f5: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/e772ec774f55a1cfead90808d624fca33a564845: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/types/cache/f7dcb122f8b6fa01fb95f1fb3a3bfaac334f52c1: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_f45e491452289c1f546923ee50e31be5f6dcef06/types/cache/25394251e30e2ac833a5ed11db7442718c1b98e5: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_f45e491452289c1f546923ee50e31be5f6dcef06/types/cache/976679291e3238e04e2ba1ee4a97ff16e89e8748: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_f45e491452289c1f546923ee50e31be5f6dcef06/types/cache/e2a201459d658e08fc0ec6ecae45dabf35d731f5: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/units/collect-global.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "namespace": "Test", 3 | "collectRootVariables": true 4 | } -------------------------------------------------------------------------------- /test/units/test.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectRootVariables": true, 3 | "guessTypes": true 4 | } 5 | -------------------------------------------------------------------------------- /test/units/collect-global.js: -------------------------------------------------------------------------------- 1 | SomethingGlobal.changed = true; 2 | 3 | var alsoGlobal; 4 | alsoGlobal.changed = true; -------------------------------------------------------------------------------- /test/units/inheretance.logs.json: -------------------------------------------------------------------------------- 1 | { builtData: { classes: [ [Object], [Object] ], functions: [] }, 2 | properties: [] } -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/semanticDiagnostics/cache/00e542b81fef2b8bef94eee573d7f567ebebaad2: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/semanticDiagnostics/cache/ce51734f7014075100857b852c35f76b8e3a9a2b: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/syntacticDiagnostics/cache/00e542b81fef2b8bef94eee573d7f567ebebaad2: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/syntacticDiagnostics/cache/ce51734f7014075100857b852c35f76b8e3a9a2b: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/semanticDiagnostics/cache/400dc2724d60301e5b09363e096017caf00665d0: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/semanticDiagnostics/cache/ff2473aaba504293d0401f9ac541b0681b110f3b: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/syntacticDiagnostics/cache/400dc2724d60301e5b09363e096017caf00665d0: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/syntacticDiagnostics/cache/ff2473aaba504293d0401f9ac541b0681b110f3b: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | testMatch: ['**/test/*.test.ts'] 5 | }; -------------------------------------------------------------------------------- /test/units/type-guessing.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "namespace": "Test", 3 | "collectRootVariables": true, 4 | "guessTypes": true 5 | } 6 | -------------------------------------------------------------------------------- /src/generator/tempCodeRunnerFile.ts: -------------------------------------------------------------------------------- 1 | const node = ts.generateTypesForGlobal('test', { name: 'hi'}, { 2 | 3 | }); 4 | console.log('node :', node); -------------------------------------------------------------------------------- /test/units/inheretance.js: -------------------------------------------------------------------------------- 1 | function Parent() {} 2 | 3 | function Child() {} 4 | Child.prototype = Object.create(Parent.prototype); 5 | Child.prototype.constructor = Child; -------------------------------------------------------------------------------- /test/units/inheretance.d.ts: -------------------------------------------------------------------------------- 1 | export declare namespace Test { 2 | class Parent { 3 | constructor(); 4 | } 5 | class Child extends Parent { 6 | constructor(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/units/global.js: -------------------------------------------------------------------------------- 1 | Number.prototype.mod = function(n) { 2 | return ((this % n) + n) % n; 3 | }; 4 | 5 | SomethingGlobal.changed = true; 6 | 7 | var isNotGlobal; 8 | isNotGlobal.changed = true; -------------------------------------------------------------------------------- /test/units/collect-global.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface SomethingGlobal { 3 | changed: boolean; 4 | } 5 | } 6 | export declare namespace Test { 7 | var alsoGlobal: any; 8 | } -------------------------------------------------------------------------------- /test/units/global.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Number { 3 | mod(n: any): number; 4 | } 5 | interface SomethingGlobal { 6 | changed: boolean; 7 | } 8 | } 9 | export declare namespace Test { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /test/units/constructors.d.ts: -------------------------------------------------------------------------------- 1 | export declare namespace Test { 2 | class DirectCtr { 3 | constructor(x: any); 4 | x: any; 5 | } 6 | class AltCtr { 7 | constructor(x: any); 8 | x: any; 9 | } 10 | } -------------------------------------------------------------------------------- /test/units/constructors.js: -------------------------------------------------------------------------------- 1 | function DirectCtr(x) { 2 | this.x = x; 3 | } 4 | 5 | function AltCtr() { 6 | this.initialize.apply(this, arguments); 7 | } 8 | 9 | AltCtr.prototype.initialize = function(x) { 10 | this.x = x; 11 | }; 12 | -------------------------------------------------------------------------------- /dist/generator/runner.d.ts: -------------------------------------------------------------------------------- 1 | import { File } from '../utils'; 2 | export declare class Runner { 3 | static run(namespace: string, file: File, fileName: string, callerPath: string, mode?: 'output' | 'write'): string; 4 | static _runPhase(message: string, func: () => T): T; 5 | } 6 | -------------------------------------------------------------------------------- /test/units/class-no-namespace.d.ts: -------------------------------------------------------------------------------- 1 | export class MyClass { 2 | constructor(a: any, b: any); 3 | myMethod(): void; 4 | } 5 | export class MyClass2 { 6 | constructor(a: any, b: any); 7 | myMethod(): void; 8 | } 9 | export class MyClass3 { 10 | constructor(); 11 | myMethod(a: any, b: any): void; 12 | } 13 | export class MyClass4 { 14 | constructor(a: any, b: any); 15 | myMethod(): void; 16 | } 17 | -------------------------------------------------------------------------------- /dist/generator/errorLogger.d.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | export interface ErrorLog { 3 | node: ts.Node; 4 | error: Error; 5 | } 6 | export declare class ErrorLogger { 7 | static logs: ErrorLog[]; 8 | static archive: ErrorLog[]; 9 | static add(node: ts.Node, error: Error): void; 10 | static store(): void; 11 | static display(program: ts.Program): string; 12 | } 13 | -------------------------------------------------------------------------------- /dist/src/generator/errorLogger.d.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | export interface ErrorLog { 3 | node: ts.Node; 4 | error: Error; 5 | } 6 | export declare class ErrorLogger { 7 | static logs: ErrorLog[]; 8 | static archive: ErrorLog[]; 9 | static add(node: ts.Node, error: Error): void; 10 | static store(): void; 11 | static display(program: ts.Program): string; 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "declarationDir": "./dist", 5 | "module": "es2015", 6 | "noImplicitAny": false, 7 | "outDir": "./dist", 8 | "target": "es5", 9 | "lib": ["es7"], 10 | "moduleResolution": "node", 11 | "resolveJsonModule": true 12 | }, 13 | "include": ["src/**/*", "lib/lib.es5.d.ts"], 14 | "exclude": ["node_modules"] 15 | } 16 | -------------------------------------------------------------------------------- /dist/generator/dtsWriter.d.ts: -------------------------------------------------------------------------------- 1 | import { PseudoClass } from './classCollector'; 2 | import { Property } from './propertyCollector'; 3 | export declare class DTSWriter { 4 | static make(classes: PseudoClass[], functions: PseudoClass[]): string; 5 | static propertyToString(property: Property): string; 6 | static classToString(_class: PseudoClass): string; 7 | static functionToString(func: PseudoClass): string; 8 | } 9 | -------------------------------------------------------------------------------- /test/units/class.js: -------------------------------------------------------------------------------- 1 | function MyClass(a, b) {} 2 | MyClass.prototype.myMethod = function() {} 3 | 4 | var MyClass2 = function(a, b) {} 5 | MyClass2.prototype.myMethod = function something() {} 6 | 7 | function MyClass3() {} 8 | function something2(a, b) {} 9 | MyClass3.prototype.myMethod = something2; 10 | 11 | var something3 = function(a, b) {} 12 | var MyClass4 = something3; 13 | MyClass4.prototype.myMethod = function something() {} -------------------------------------------------------------------------------- /test/units/class.d.ts: -------------------------------------------------------------------------------- 1 | export declare namespace Test { 2 | class MyClass { 3 | constructor(a: any, b: any); 4 | myMethod(): void; 5 | } 6 | class MyClass2 { 7 | constructor(a: any, b: any); 8 | myMethod(): void; 9 | } 10 | class MyClass3 { 11 | constructor(); 12 | myMethod(a: any, b: any): void; 13 | } 14 | class MyClass4 { 15 | constructor(a: any, b: any); 16 | myMethod(): void; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/units/class-no-namespace.js: -------------------------------------------------------------------------------- 1 | function MyClass(a, b) {} 2 | MyClass.prototype.myMethod = function() {} 3 | 4 | var MyClass2 = function(a, b) {} 5 | MyClass2.prototype.myMethod = function something() {} 6 | 7 | function MyClass3() {} 8 | function something2(a, b) {} 9 | MyClass3.prototype.myMethod = something2; 10 | 11 | var something3 = function(a, b) {} 12 | var MyClass4 = something3; 13 | MyClass4.prototype.myMethod = function something() {} -------------------------------------------------------------------------------- /test/class-no-namespace.ts: -------------------------------------------------------------------------------- 1 | export class MyClass { 2 | constructor(a: any, b: any) { } 3 | ; 4 | myMethod(): void; 5 | } 6 | export class MyClass2 { 7 | constructor(a: any, b: any) { } 8 | ; 9 | myMethod(): void; 10 | } 11 | export class MyClass3 { 12 | constructor() { } 13 | ; 14 | myMethod(a: any, b: any): void; 15 | } 16 | export class MyClass4 { 17 | constructor(a: any, b: any) { } 18 | ; 19 | myMethod(): void; 20 | } 21 | -------------------------------------------------------------------------------- /test/units/global.logs.json: -------------------------------------------------------------------------------- 1 | { builtData: { classes: [ [Object], [Object] ], functions: [] }, 2 | properties: 3 | [ { name: 'mod', 4 | parentSymbol: 'Number', 5 | static: false, 6 | type: '(n: any) => number', 7 | jsDoc: undefined, 8 | linkedToFunction: null, 9 | rightNode: [NodeObject] }, 10 | { name: 'changed', 11 | parentSymbol: 'SomethingGlobal', 12 | static: true, 13 | type: 'boolean', 14 | jsDoc: undefined, 15 | linkedToFunction: null, 16 | rightNode: [TokenObject] } ] } -------------------------------------------------------------------------------- /test/units/type-guessing.d.ts: -------------------------------------------------------------------------------- 1 | declare type Guess = Partial; 2 | 3 | export declare namespace Test { 4 | var SacredPoint: Guess; 5 | var CursedPoint: Guess; 6 | class Point2D { 7 | constructor(x: any, y: any); 8 | x: Guess; 9 | y: Guess; 10 | add(x: Guess, y: Guess): void; 11 | } 12 | class Circle { 13 | constructor(radius: any, center: any, label: any); 14 | radius: Guess; 15 | center: any; 16 | label: any; 17 | isSame(other: Guess): boolean; 18 | getDiameter(): number; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dist/src/generator/dtsWriter.d.ts: -------------------------------------------------------------------------------- 1 | import { PseudoClass } from './classCollector'; 2 | import { Property } from './propertyCollector'; 3 | export declare class DTSWriter { 4 | static print(dts: string): string; 5 | static make(classes: PseudoClass[], functions: PseudoClass[], properties: Property[]): string; 6 | static propertyToString(property: Property, isMethod?: boolean): string; 7 | static propertyTypeToString(property: Property): string; 8 | static toMethodTypeString(property: Property): string; 9 | static classToString(_class: PseudoClass): string; 10 | static functionToString(func: PseudoClass): string; 11 | } 12 | -------------------------------------------------------------------------------- /dist/src/generator/mockupWriter.d.ts: -------------------------------------------------------------------------------- 1 | import { PseudoClass } from './classCollector'; 2 | import { Property } from './propertyCollector'; 3 | export declare class MockupWriter { 4 | static print(dts: string): string; 5 | static make(classes: PseudoClass[], functions: PseudoClass[], properties: Property[]): string; 6 | static propertyToString(property: Property, isMethod?: boolean): string; 7 | static propertyTypeToString(property: Property): string; 8 | static toMethodTypeString(property: Property): string; 9 | static classToString(_class: PseudoClass): string; 10 | static functionToString(func: PseudoClass): string; 11 | } 12 | -------------------------------------------------------------------------------- /test/units/collect-global.logs.json: -------------------------------------------------------------------------------- 1 | { builtData: { classes: [ [Object] ], functions: [] }, 2 | properties: 3 | [ { name: 'changed', 4 | parentSymbol: 'SomethingGlobal', 5 | static: true, 6 | type: 'boolean', 7 | jsDoc: undefined, 8 | linkedToFunction: null, 9 | rightNode: [TokenObject] }, 10 | { name: 'alsoGlobal', 11 | type: 'any', 12 | rightNode: undefined, 13 | parentSymbol: null }, 14 | { name: 'changed', 15 | parentSymbol: 'alsoGlobal', 16 | static: true, 17 | type: 'boolean', 18 | jsDoc: undefined, 19 | linkedToFunction: null, 20 | rightNode: [TokenObject] } ] } -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2'; 2 | import pkg from './package.json'; 3 | import replace from 'rollup-plugin-re'; 4 | import json from 'rollup-plugin-json'; 5 | 6 | export default { 7 | input: 'src/script.ts', 8 | output: [ 9 | { 10 | file: pkg.main, 11 | format: 'cjs', 12 | banner: '#!/usr/bin/env node' 13 | } 14 | ], 15 | external: [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})], 16 | plugins: [ 17 | json(), 18 | replace({ 19 | replaces: { 20 | '../../lib/lib.es5.d.ts': '../lib/lib.es5.d.ts' 21 | } 22 | }), 23 | typescript({ 24 | typescript: require('typescript') 25 | }) 26 | ] 27 | }; 28 | -------------------------------------------------------------------------------- /test/units/constructors.logs.json: -------------------------------------------------------------------------------- 1 | { builtData: { classes: [ [Object], [Object] ], functions: [] }, 2 | properties: 3 | [ { name: 'x', 4 | parentSymbol: 'DirectCtr', 5 | static: false, 6 | type: 'any', 7 | jsDoc: undefined, 8 | linkedToFunction: 'x', 9 | rightNode: [IdentifierObject] }, 10 | { name: 'initialize', 11 | parentSymbol: 'AltCtr', 12 | static: false, 13 | type: '(x: any) => void', 14 | jsDoc: undefined, 15 | linkedToFunction: null, 16 | rightNode: [NodeObject] }, 17 | { name: 'x', 18 | parentSymbol: 'AltCtr', 19 | static: false, 20 | type: 'any', 21 | jsDoc: undefined, 22 | linkedToFunction: 'x', 23 | rightNode: [IdentifierObject] } ] } -------------------------------------------------------------------------------- /test/units/type-guessing.js: -------------------------------------------------------------------------------- 1 | function Point2D(x, y) { 2 | this.x = x; 3 | this.y = y; 4 | } 5 | 6 | Point2D.prototype.add = function(x, y) { 7 | this.x = Math.round(this.x + x); 8 | this.y = Math.round(this.y + y); 9 | } 10 | 11 | function Circle(radius, center, label) { 12 | this.radius = radius; 13 | this.center = center; 14 | this.label = label.toUpperCase(); 15 | } 16 | 17 | Circle.prototype.isSame = function(other) { 18 | return this.radius === other.radius && this.center.x === other.center.x && this.center.y === other.center.y; 19 | } 20 | 21 | Circle.prototype.getDiameter = function() { 22 | return this.radius * 2; 23 | } 24 | 25 | var SacredPoint = { x: 111, y: 111}; 26 | var CursedPoint = {}; 27 | CursedPoint.x = 666; 28 | CursedPoint.y = 666; -------------------------------------------------------------------------------- /dist/src/generator/runner.d.ts: -------------------------------------------------------------------------------- 1 | import { File } from '../utils'; 2 | import * as ts from 'typescript'; 3 | import { Property } from './propertyCollector'; 4 | import { PseudoClass } from './classCollector'; 5 | export interface Options { 6 | namespace?: string; 7 | allFiles?: boolean; 8 | collectRootVariables?: boolean; 9 | guessTypes?: boolean; 10 | mockupMode?: boolean; 11 | } 12 | export declare class Runner { 13 | static options: Options; 14 | static result: { 15 | properties: Property[]; 16 | builtData: { 17 | classes: PseudoClass[]; 18 | functions: PseudoClass[]; 19 | }; 20 | }; 21 | static makeProgram(files: File[]): ts.Program; 22 | static run(options: Options, files: File[], fileName: string, callerPath: string, mode?: 'output' | 'write'): string; 23 | static _runPhase(message: string, func: () => T): T; 24 | } 25 | -------------------------------------------------------------------------------- /dist/generator/propertyCollector.d.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | export interface Property { 3 | parentSymbol: string; 4 | name: string; 5 | type: string; 6 | static?: boolean; 7 | readonly?: boolean; 8 | jsDoc?: ts.JSDoc; 9 | linkedToFunction?: string; 10 | } 11 | export declare class PropertyCollector { 12 | program: ts.Program; 13 | checker: ts.TypeChecker; 14 | properties: Property[]; 15 | localVariables: string[]; 16 | state: { 17 | parentSymbol?: string; 18 | fromStatic?: boolean; 19 | }; 20 | collect(program: ts.Program): Property[]; 21 | _visit(node: ts.Node): void; 22 | _onVariableStatement(node: ts.VariableStatement): void; 23 | _onFunctionDeclaration(node: ts.FunctionDeclaration): void; 24 | _onExpressionStatement(node: ts.ExpressionStatement, expr: ts.BinaryExpression, left: ts.PropertyAccessExpression): void; 25 | } 26 | -------------------------------------------------------------------------------- /test/units/class.logs.json: -------------------------------------------------------------------------------- 1 | { builtData: 2 | { classes: [ [Object], [Object], [Object], [Object] ], 3 | functions: [] }, 4 | properties: 5 | [ { name: 'myMethod', 6 | parentSymbol: 'MyClass', 7 | static: false, 8 | type: '() => void', 9 | jsDoc: undefined, 10 | linkedToFunction: null, 11 | rightNode: [NodeObject] }, 12 | { name: 'myMethod', 13 | parentSymbol: 'MyClass2', 14 | static: false, 15 | type: '() => void', 16 | jsDoc: undefined, 17 | linkedToFunction: null, 18 | rightNode: [NodeObject] }, 19 | { name: 'myMethod', 20 | parentSymbol: 'MyClass3', 21 | static: false, 22 | type: '(a: any, b: any) => void', 23 | jsDoc: undefined, 24 | linkedToFunction: 'something2', 25 | rightNode: [IdentifierObject] }, 26 | { name: 'myMethod', 27 | parentSymbol: 'MyClass4', 28 | static: false, 29 | type: '() => void', 30 | jsDoc: undefined, 31 | linkedToFunction: null, 32 | rightNode: [NodeObject] } ] } -------------------------------------------------------------------------------- /test/units/class-no-namespace.logs.json: -------------------------------------------------------------------------------- 1 | { builtData: 2 | { classes: [ [Object], [Object], [Object], [Object] ], 3 | functions: [] }, 4 | properties: 5 | [ { name: 'myMethod', 6 | parentSymbol: 'MyClass', 7 | static: false, 8 | type: '() => void', 9 | jsDoc: undefined, 10 | linkedToFunction: null, 11 | rightNode: [NodeObject] }, 12 | { name: 'myMethod', 13 | parentSymbol: 'MyClass2', 14 | static: false, 15 | type: '() => void', 16 | jsDoc: undefined, 17 | linkedToFunction: null, 18 | rightNode: [NodeObject] }, 19 | { name: 'myMethod', 20 | parentSymbol: 'MyClass3', 21 | static: false, 22 | type: '(a: any, b: any) => void', 23 | jsDoc: undefined, 24 | linkedToFunction: 'something2', 25 | rightNode: [IdentifierObject] }, 26 | { name: 'myMethod', 27 | parentSymbol: 'MyClass4', 28 | static: false, 29 | type: '() => void', 30 | jsDoc: undefined, 31 | linkedToFunction: null, 32 | rightNode: [NodeObject] } ] } -------------------------------------------------------------------------------- /src/generator/errorLogger.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as util from 'util'; 3 | import chalk from 'chalk'; 4 | 5 | export interface ErrorLog { 6 | node: ts.Node; 7 | error: Error; 8 | } 9 | 10 | export class ErrorLogger { 11 | static logs: ErrorLog[] = []; 12 | static archive: ErrorLog[] = []; 13 | 14 | static add(node: ts.Node, error: Error) { 15 | this.logs.push({ node, error }); 16 | } 17 | 18 | static store() { 19 | this.archive = [].concat(this.archive, this.logs); 20 | this.logs = []; 21 | } 22 | 23 | static display(program: ts.Program) { 24 | const srcFile = program.getSourceFiles()[0]; 25 | return `${this.archive 26 | .map((l, i) => { 27 | let { line, character } = srcFile.getLineAndCharacterOfPosition(l.node.getStart()); 28 | return `${chalk.bgRed(`Error ${i+1}:`)} ${l.error.message} 29 | Sourcefile ${chalk.yellow(`line ${line}`)}, ${chalk.yellow(`character ${character}`)} 30 | ${chalk.bold('Stacktrace:')} ${l.error.stack} 31 | ${chalk.bold('Node:')} ${util.inspect(l.node)}`; 32 | }) 33 | .join('\n\n')}`; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /dist/src/generator/propertyCollector.d.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Guess } from './typeGuesser'; 3 | export interface Property { 4 | parentSymbol: string; 5 | name: string; 6 | type: string; 7 | static?: boolean; 8 | readonly?: boolean; 9 | jsDoc?: ts.JSDoc; 10 | linkedToFunction?: string; 11 | typeGuessing?: Guess; 12 | guessedType?: string; 13 | rightNode?: ts.Node; 14 | } 15 | export declare class PropertyCollector { 16 | program: ts.Program; 17 | checker: ts.TypeChecker; 18 | properties: Property[]; 19 | localVariables: string[]; 20 | state: { 21 | parentSymbol?: string; 22 | fromStatic?: boolean; 23 | }; 24 | collect(program: ts.Program): Property[]; 25 | _visit(node: ts.Node, parentNode?: ts.Node): void; 26 | _onVariableStatement(node: ts.VariableStatement, parentNode: ts.Node): void; 27 | _onFunctionDeclaration(node: ts.FunctionDeclaration): void; 28 | _onExpressionStatement(node: ts.ExpressionStatement, expr: ts.BinaryExpression, left: ts.PropertyAccessExpression): void; 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "es5-to-dts", 3 | "version": "1.1.2", 4 | "description": "", 5 | "main": "dist/script.js", 6 | "files": [ 7 | "dist", 8 | "lib" 9 | ], 10 | "types": "dist/generator.d.ts", 11 | "bin": { 12 | "es5-to-dts": "dist/script.js" 13 | }, 14 | "preferGlobal": true, 15 | "scripts": { 16 | "build": "rollup -c", 17 | "watch": "rollup -cw", 18 | "test": "jest" 19 | }, 20 | "author": "CyriacBr ", 21 | "repository": { 22 | "type": "github", 23 | "url": "https://github.com/CyriacBr/es5-to-dts.git" 24 | }, 25 | "license": "MIT", 26 | "devDependencies": { 27 | "@types/jest": "^24.0.11", 28 | "chalk": "^2.4.2", 29 | "jest": "^24.5.0", 30 | "rollup": "^1.1.2", 31 | "rollup-plugin-dts": "^0.10.0", 32 | "rollup-plugin-json": "^4.0.0", 33 | "rollup-plugin-re": "^1.0.7", 34 | "rollup-plugin-typescript2": "^0.20.1", 35 | "ts-jest": "^24.0.0", 36 | "tslib": "^1.9.3", 37 | "typescript": "^3.3.3333" 38 | }, 39 | "dependencies": { 40 | "@types/astring": "^1.3.0", 41 | "@types/node": "^11.10.5", 42 | "commander": "^2.19.0", 43 | "dot-object": "^1.7.1", 44 | "ora": "^3.2.0", 45 | "tsutils": "^3.9.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /dist/utils.d.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { PseudoClass } from './generator/classCollector'; 3 | export declare const setNamespace: (name: string) => string; 4 | export declare const getNamespace: () => string; 5 | export interface File { 6 | fileName: string; 7 | content: string; 8 | sourceFile?: ts.SourceFile; 9 | } 10 | export declare function createProgram(files: File[], compilerOptions?: ts.CompilerOptions): ts.Program; 11 | export declare const EQUAL_TOKEN = 59; 12 | export declare const THIS_TOKEN = 100; 13 | export interface Variable { 14 | name: string; 15 | type: string; 16 | } 17 | export declare function makeFunctionDeclarations(classes: PseudoClass[]): PseudoClass[][]; 18 | export declare function makePropertyFromObjectLiteral(checker: ts.TypeChecker, expr: ts.ObjectLiteralExpression, jsDoc: ts.JSDoc): { 19 | readonly: boolean; 20 | type: string; 21 | }; 22 | export declare function makeVariablesFromParameters(checker: ts.TypeChecker, params: ts.ParameterDeclaration[]): Variable[]; 23 | export declare function traverseProgram(program: ts.Program, callback: (node: ts.Node) => any): void; 24 | export declare function getTypeString(checker: ts.TypeChecker, node: ts.Node): string; 25 | export declare function convertJsDocType(type: string): any; 26 | export declare function extractJsDocType(doc: ts.JSDoc, currentType?: string): any; 27 | -------------------------------------------------------------------------------- /test/units/test.js: -------------------------------------------------------------------------------- 1 | 2 | function Game_Temp() { 3 | this.initialize.apply(this, arguments); 4 | } 5 | 6 | Game_Temp.prototype.initialize = function() { 7 | this._isPlaytest = Utils.isOptionValid('test'); 8 | this._commonEventId = 0; 9 | this._destinationX = null; 10 | this._destinationY = null; 11 | }; 12 | 13 | Game_Temp.prototype.isPlaytest = function() { 14 | return this._isPlaytest; 15 | }; 16 | 17 | Game_Temp.prototype.reserveCommonEvent = function(commonEventId) { 18 | this._commonEventId = commonEventId; 19 | }; 20 | 21 | Game_Temp.prototype.clearCommonEvent = function() { 22 | this._commonEventId = 0; 23 | }; 24 | 25 | Game_Temp.prototype.isCommonEventReserved = function() { 26 | return this._commonEventId > 0; 27 | }; 28 | 29 | Game_Temp.prototype.reservedCommonEvent = function() { 30 | return $dataCommonEvents[this._commonEventId]; 31 | }; 32 | 33 | Game_Temp.prototype.setDestination = function(x, y) { 34 | this._destinationX = x; 35 | this._destinationY = y; 36 | }; 37 | 38 | Game_Temp.prototype.clearDestination = function() { 39 | this._destinationX = null; 40 | this._destinationY = null; 41 | }; 42 | 43 | Game_Temp.prototype.isDestinationValid = function() { 44 | return this._destinationX !== null; 45 | }; 46 | 47 | Game_Temp.prototype.destinationX = function() { 48 | return this._destinationX; 49 | }; 50 | 51 | Game_Temp.prototype.destinationY = function() { 52 | return this._destinationY; 53 | }; -------------------------------------------------------------------------------- /dist/src/utils.d.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { PseudoClass } from './generator/classCollector'; 3 | export interface File { 4 | fileName: string; 5 | content: string; 6 | sourceFile?: ts.SourceFile; 7 | } 8 | export declare function createProgram(files: File[], compilerOptions?: ts.CompilerOptions): ts.Program; 9 | export declare const EQUAL_TOKEN = 59; 10 | export declare const THIS_TOKEN = 100; 11 | export interface Variable { 12 | name: string; 13 | type: string; 14 | } 15 | export declare function makeFunctionDeclarations(classes: PseudoClass[]): PseudoClass[][]; 16 | export declare function makePropertyFromObjectLiteral(checker: ts.TypeChecker, expr: ts.ObjectLiteralExpression, jsDoc: ts.JSDoc): { 17 | readonly: boolean; 18 | type: string; 19 | }; 20 | export declare function makeVariablesFromParameters(checker: ts.TypeChecker, params: ts.ParameterDeclaration[]): Variable[]; 21 | export declare function traverseProgram(program: ts.Program, callback: (node: ts.Node) => any): void; 22 | export declare function getTypeString(checker: ts.TypeChecker, node: ts.Node): string; 23 | export declare function convertJsDocType(type: string): any; 24 | export declare function extractJsDocType(doc: ts.JSDoc, currentType?: string): any; 25 | export declare function objectLiteralToObject(expr: ts.ObjectLiteralExpression): {}; 26 | export declare function collectNodesBy(program: ts.Program, constraint: (node: ts.Node) => boolean, startingNode?: ts.Node): any[]; 27 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/code/cache/ff2473aaba504293d0401f9ac541b0681b110f3b: -------------------------------------------------------------------------------- 1 | {"code":"import { createProgram, collectProperties, makePseudoClasses, makeDTS } from './generator';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nvar callerPath = process.cwd();\r\nvar fileName = process.argv[2];\r\ntry {\r\n var file = {\r\n content: fs.readFileSync(path.resolve(callerPath, fileName)).toString(),\r\n fileName: 'file1.ts'\r\n };\r\n var lib = {\r\n content: fs\r\n .readFileSync(path.resolve(__dirname, '../node_modules/typescript/lib/lib.es5.d.ts'))\r\n .toString(),\r\n fileName: 'lib.es2018.d.ts'\r\n };\r\n var program = createProgram([file, lib], {});\r\n console.log(' - TS program created');\r\n console.log(' - Collecting properties');\r\n var properties = collectProperties(program);\r\n console.log(' - Done');\r\n console.log(' - Collecting pseudo classes');\r\n var classes = makePseudoClasses(program, properties);\r\n console.log(' - Done');\r\n console.log(' - Writing result');\r\n var result = makeDTS(classes, 'MV');\r\n var resultFileName = fileName.replace(/\\.(t|j)s/i, '') + '.d.ts';\r\n fs.writeFileSync(path.resolve(__dirname, resultFileName), result);\r\n console.log(' - Done');\r\n}\r\ncatch (error) {\r\n console.log('An error occured.');\r\n throw error;\r\n}\r\n","dts":{"name":"/media/cyriac/0CC5166B0CC5166B/Work/es5-to-dts/src/script.d.ts","writeByteOrderMark":false,"text":"export {};\r\n"}} 2 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/code/cache/00e542b81fef2b8bef94eee573d7f567ebebaad2: -------------------------------------------------------------------------------- 1 | {"code":"import { createProgram, collectProperties, makePseudoClasses, makeDTS, setNamespace } from './generator';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nvar callerPath = process.cwd();\r\nvar fileName = process.argv[2];\r\nvar namespace = process.argv[3];\r\nsetNamespace(namespace || 'UnknownNamespace');\r\ntry {\r\n var file = {\r\n content: fs.readFileSync(path.resolve(callerPath, fileName)).toString(),\r\n fileName: 'file1.ts'\r\n };\r\n var lib = {\r\n content: fs\r\n .readFileSync(path.resolve(__dirname, '../node_modules/typescript/lib/lib.es5.d.ts'))\r\n .toString(),\r\n fileName: 'lib.es2018.d.ts'\r\n };\r\n var program = createProgram([file, lib], {});\r\n console.log(' - TS program created');\r\n console.log(' - Collecting properties');\r\n var properties = collectProperties(program);\r\n console.log(' -> Done');\r\n console.log(' - Collecting pseudo classes');\r\n var classes = makePseudoClasses(program, properties);\r\n console.log(' -> Done');\r\n console.log(' - Writing result');\r\n var result = makeDTS(classes);\r\n var resultFileName = fileName.replace(/\\.(t|j)s/i, '') + '.d.ts';\r\n fs.writeFileSync(path.resolve(callerPath, resultFileName), result);\r\n console.log(' -> Done');\r\n}\r\ncatch (error) {\r\n console.log('An error occured.');\r\n throw error;\r\n}\r\n","dts":{"name":"/media/cyriac/0CC5166B0CC5166B/Work/es5-to-dts/script.d.ts","writeByteOrderMark":false,"text":"export {};\r\n"}} 2 | -------------------------------------------------------------------------------- /dist/generator/classCollector.d.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Variable } from '../utils'; 3 | import { Property } from './propertyCollector'; 4 | export interface PseudoClass { 5 | name: string; 6 | constructorArgs: Variable[]; 7 | properties: Property[]; 8 | global?: boolean; 9 | constructorProperty?: Property; 10 | constructorSignature?: string; 11 | extends?: string; 12 | jsDoc?: ts.JSDoc; 13 | } 14 | export declare class ClassCollector { 15 | program: ts.Program; 16 | checker: ts.TypeChecker; 17 | properties: Property[]; 18 | classes: PseudoClass[]; 19 | localVariables: string[]; 20 | state: { 21 | parentSymbol?: string; 22 | fromStatic?: boolean; 23 | }; 24 | collect(program: ts.Program, properties: Property[]): { 25 | classes: PseudoClass[]; 26 | functions: PseudoClass[]; 27 | }; 28 | _makePureFunctions(classes: PseudoClass[]): PseudoClass[][]; 29 | _visit(node: ts.Node): void; 30 | _onFunctionDeclaration(node: ts.FunctionDeclaration): void; 31 | _onVariableStatement(node: ts.VariableStatement): void; 32 | _onExpressionStatement(node: ts.ExpressionStatement): void; 33 | _checkPrototypeInheritance(node: ts.ExpressionStatement): boolean; 34 | _checkPropertiesDefinition(node: ts.ExpressionStatement): boolean; 35 | _makeConstructorSpecs(node: ts.FunctionDeclaration | ts.VariableStatement, body: ts.Block, name: string, constructorArgs: any): { 36 | constructorSignature: any; 37 | constructorProperty: Property; 38 | jsDoc: ts.JSDoc; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /dist/src/generator/classCollector.d.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Variable } from '../utils'; 3 | import { Property } from './propertyCollector'; 4 | export interface PseudoClass { 5 | name: string; 6 | constructorArgs: Variable[]; 7 | properties: Property[]; 8 | global?: boolean; 9 | constructorProperty?: Property; 10 | constructorSignature?: string; 11 | extends?: string; 12 | jsDoc?: ts.JSDoc; 13 | } 14 | export declare class ClassCollector { 15 | program: ts.Program; 16 | checker: ts.TypeChecker; 17 | properties: Property[]; 18 | classes: PseudoClass[]; 19 | localVariables: string[]; 20 | state: { 21 | parentSymbol?: string; 22 | fromStatic?: boolean; 23 | }; 24 | collect(program: ts.Program, properties: Property[]): { 25 | classes: PseudoClass[]; 26 | functions: PseudoClass[]; 27 | }; 28 | _makePureFunctions(classes: PseudoClass[]): PseudoClass[][]; 29 | _visit(node: ts.Node): void; 30 | _onFunctionDeclaration(node: ts.FunctionDeclaration): void; 31 | _onVariableStatement(node: ts.VariableStatement): void; 32 | _onExpressionStatement(node: ts.ExpressionStatement): void; 33 | _checkPrototypeInheritance(node: ts.ExpressionStatement): boolean; 34 | _checkPropertiesDefinition(node: ts.ExpressionStatement): boolean; 35 | _makeConstructorSpecs(node: ts.FunctionDeclaration | ts.VariableStatement, body: ts.Block, name: string, constructorArgs: any): { 36 | constructorSignature: any; 37 | constructorProperty: Property; 38 | jsDoc: ts.JSDoc; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /src/script.ts: -------------------------------------------------------------------------------- 1 | import { File } from './utils'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | import { Runner } from './generator/runner'; 5 | import * as pkg from '../package.json'; 6 | 7 | const cli = require('commander'); 8 | cli 9 | .version(pkg.version) 10 | .option('-n, --namespace [namespace]', 'Wrap the file into a namespace') 11 | .option('-a, --all-files [outputName]', 'Process all files in the directory and output a single d.ts') 12 | .option('-b, --bundled-output', 'When -a is used, bundle the output into a single file') 13 | .option('-r, --root-variables', 'Collect root variables') 14 | .option('-g, --guess-types', 'Guess types') 15 | .option('-m, --mockup', 'Generate a mockup of the definition file instead of a d.ts') 16 | .parse(process.argv); 17 | const callerPath = process.cwd(); 18 | let fileName; 19 | const namespace = cli.namespace as string; 20 | 21 | const files: File[] = []; 22 | if (cli.allFiles) { 23 | fileName = cli.outputName || 'output'; 24 | let filesName = fs.readdirSync(callerPath); 25 | let i = 1; 26 | for (const fileName of filesName) { 27 | files.push({ 28 | content: fs.readFileSync(path.resolve(callerPath, fileName)).toString(), 29 | fileName: `file${i}.ts` 30 | }); 31 | i++; 32 | } 33 | } else { 34 | fileName = process.argv[2] as string; 35 | files.push({ 36 | content: fs.readFileSync(path.resolve(callerPath, fileName)).toString(), 37 | fileName: 'file1.ts' 38 | }); 39 | } 40 | Runner.run( 41 | { 42 | namespace, 43 | allFiles: cli.allFiles, 44 | collectRootVariables: cli.rootVariables, 45 | guessTypes: cli.guessTypes, 46 | mockupMode: cli.mockup 47 | }, 48 | files, 49 | fileName, 50 | callerPath 51 | ); 52 | -------------------------------------------------------------------------------- /dist/src/generator/typeGuesser.d.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Property } from './propertyCollector'; 3 | import { PseudoClass } from './classCollector'; 4 | export declare class Guess { 5 | value: any[]; 6 | name: string; 7 | constructor(value: any[], name: string); 8 | asTypeSymbol(): string; 9 | asInterfaceSymbol(): string; 10 | toInlineString(): string; 11 | toTypeString(): string; 12 | toInterfaceString(): string; 13 | } 14 | export interface InferData { 15 | assignedTypes: { 16 | [propName: string]: any[]; 17 | }; 18 | typings: { 19 | [propName: string]: any; 20 | }; 21 | } 22 | export declare class TypeGuesser { 23 | static symbol: symbol; 24 | static Dot: any; 25 | static program: ts.Program; 26 | static knownClasses: PseudoClass[]; 27 | static guess(program: ts.Program, properties: Property[], classes: PseudoClass[]): void; 28 | static guessRootPropertiesType(properties: Property[]): void; 29 | static guessPropertiesFunctionType(properties: Property[]): void; 30 | static guessClassPropertiesType(classes: PseudoClass[]): void; 31 | static guessClassConstructorTypes(_class: PseudoClass): void; 32 | static guessParametersType(node: ts.FunctionExpression, props: Property[]): { 33 | [propName: string]: string; 34 | }; 35 | static guessFromBody(body: ts.Node, properties: Property[]): void; 36 | static typingFromAmbientUsage(node: ts.Node, properties: Property[]): {}; 37 | static inferDataFromCallExpressions(node: ts.Node, properties: Property[]): { 38 | assignedTypes: {}; 39 | typings: {}; 40 | }; 41 | static infer(prop: Property, left: ts.Node, right: ts.Node, data: InferData): void; 42 | static inferFromKnownSymbols(typings: any, root?: boolean): any; 43 | } 44 | -------------------------------------------------------------------------------- /dist/generator.d.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | export declare const setNamespace: (name: string) => string; 3 | export interface File { 4 | fileName: string; 5 | content: string; 6 | sourceFile?: ts.SourceFile; 7 | } 8 | export declare function createProgram(files: File[], compilerOptions?: ts.CompilerOptions): ts.Program; 9 | export interface Variable { 10 | name: string; 11 | type: string; 12 | } 13 | export interface Property { 14 | parentSymbol: string; 15 | name: string; 16 | type: string; 17 | static?: boolean; 18 | readonly?: boolean; 19 | jsDoc?: ts.JSDoc; 20 | } 21 | export declare function collectProperties(program: ts.Program): Property[]; 22 | interface PseudoClass { 23 | name: string; 24 | constructorArgs: Variable[]; 25 | properties: Property[]; 26 | global?: boolean; 27 | constructorProperty?: Property; 28 | constructorSignature?: string; 29 | extends?: string; 30 | jsDoc?: ts.JSDoc; 31 | } 32 | export declare function makePseudoClasses(program: ts.Program, properties: Property[]): PseudoClass[]; 33 | export declare function makeFunctionDeclarations(classes: PseudoClass[]): PseudoClass[][]; 34 | export declare function makePropertyFromObjectLiteral(checker: ts.TypeChecker, expr: ts.ObjectLiteralExpression, jsDoc: ts.JSDoc): { 35 | readonly: boolean; 36 | type: string; 37 | }; 38 | export declare function makeVariablesFromParameters(checker: ts.TypeChecker, params: ts.ParameterDeclaration[]): Variable[]; 39 | export declare function traverseProgram(program: ts.Program, callback: (node: ts.Node) => any): void; 40 | export declare function getTypeString(checker: ts.TypeChecker, node: ts.Node): string; 41 | export declare function convertJsDocType(type: string): any; 42 | export declare function extractJsDocType(doc: ts.JSDoc, currentType?: string): any; 43 | export declare function makeDTS(classes: PseudoClass[], functions: PseudoClass[]): any; 44 | export declare function propertyToString(property: Property): string; 45 | export declare function classToString(_class: PseudoClass): string; 46 | export declare function functionToString(func: PseudoClass): string; 47 | export {}; 48 | -------------------------------------------------------------------------------- /test/units.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { Runner, Options } from '../src/generator/runner'; 4 | import * as util from 'util'; 5 | import * as ts from 'typescript'; 6 | import { DTSWriter } from '../src/generator/dtsWriter'; 7 | 8 | const cases: [string, string, string][] = []; 9 | 10 | const files = fs.readdirSync(path.resolve(__dirname, './units')); 11 | 12 | for (const file of files) { 13 | if (file.match(/(.+)\.js$/)) { 14 | const name = RegExp.$1; 15 | const code = fs.readFileSync(path.resolve(__dirname, './units', file)).toString(); 16 | 17 | let optionsRaw; 18 | try { 19 | optionsRaw = fs 20 | .readFileSync(path.resolve(__dirname, './units', name + '.options.json')) 21 | .toString(); 22 | } catch (error) { 23 | optionsRaw = JSON.stringify({ namespace: 'Test' }); 24 | } 25 | let { mockupMode } = optionsRaw; 26 | 27 | let expectedDts; 28 | try { 29 | expectedDts = fs.readFileSync(path.resolve(__dirname, './units', name + '.d.ts')).toString(); 30 | } catch (error) { 31 | expectedDts = `'${name}.d.ts not found';`; 32 | } 33 | 34 | let expectedMockup; 35 | if(mockupMode) { 36 | try { 37 | expectedMockup = fs.readFileSync(path.resolve(__dirname, './units', name + '.mockup.ts')).toString(); 38 | } catch (error) { 39 | expectedMockup = `'${name}.mockup.ts not found';`; 40 | } 41 | } 42 | 43 | const dts = Runner.run( 44 | JSON.parse(optionsRaw), 45 | [ 46 | { 47 | content: code, 48 | fileName: 'file.ts' 49 | } 50 | ], 51 | name, 52 | __dirname, 53 | 'output' 54 | ); 55 | 56 | const log = util.inspect(Runner.result); //JSON.stringify(Runner.result); 57 | fs.writeFileSync(path.resolve(__dirname, './units', name + '.logs.json'), log); 58 | cases.push([name, dts, expectedDts]); 59 | if(mockupMode) { 60 | cases.push([name + ' mockup', dts, expectedMockup]); 61 | } 62 | } 63 | } 64 | 65 | const options = { 66 | preserve_newlines: false 67 | }; 68 | test.each(cases)('%s', (name, value, expected) => { 69 | expect(value).toBe(DTSWriter.print(expected)); 70 | //expect(Beautify.js(value, options)).toBe(Beautify.js(expected, options)); 71 | }); 72 | -------------------------------------------------------------------------------- /test/units/type-guessing.logs.json: -------------------------------------------------------------------------------- 1 | { builtData: { classes: [ [Object], [Object] ], functions: [] }, 2 | properties: 3 | [ { name: 'x', 4 | parentSymbol: 'Point2D', 5 | static: false, 6 | type: 'any', 7 | jsDoc: undefined, 8 | linkedToFunction: 'x', 9 | rightNode: [IdentifierObject], 10 | guessedType: 'Guess', 11 | typeGuessing: [Guess] }, 12 | { name: 'y', 13 | parentSymbol: 'Point2D', 14 | static: false, 15 | type: 'any', 16 | jsDoc: undefined, 17 | linkedToFunction: 'y', 18 | rightNode: [IdentifierObject], 19 | guessedType: 'Guess', 20 | typeGuessing: [Guess] }, 21 | { name: 'add', 22 | parentSymbol: 'Point2D', 23 | static: false, 24 | type: '(x: Guess,y: Guess) => void', 25 | jsDoc: undefined, 26 | linkedToFunction: null, 27 | rightNode: [NodeObject], 28 | guessedType: undefined, 29 | typeGuessing: undefined }, 30 | { name: 'radius', 31 | parentSymbol: 'Circle', 32 | static: false, 33 | type: 'any', 34 | jsDoc: undefined, 35 | linkedToFunction: 'radius', 36 | rightNode: [IdentifierObject], 37 | guessedType: undefined, 38 | typeGuessing: undefined }, 39 | { name: 'center', 40 | parentSymbol: 'Circle', 41 | static: false, 42 | type: 'any', 43 | jsDoc: undefined, 44 | linkedToFunction: 'center', 45 | rightNode: [IdentifierObject], 46 | guessedType: undefined, 47 | typeGuessing: undefined }, 48 | { name: 'label', 49 | parentSymbol: 'Circle', 50 | static: false, 51 | type: 'any', 52 | jsDoc: undefined, 53 | linkedToFunction: null, 54 | rightNode: [NodeObject], 55 | guessedType: undefined, 56 | typeGuessing: undefined }, 57 | { name: 'isSame', 58 | parentSymbol: 'Circle', 59 | static: false, 60 | type: '(other: Guess) => boolean', 61 | jsDoc: undefined, 62 | linkedToFunction: null, 63 | rightNode: [NodeObject], 64 | guessedType: undefined, 65 | typeGuessing: undefined }, 66 | { name: 'getDiameter', 67 | parentSymbol: 'Circle', 68 | static: false, 69 | type: '() => number', 70 | jsDoc: undefined, 71 | linkedToFunction: null, 72 | rightNode: [NodeObject], 73 | guessedType: undefined, 74 | typeGuessing: undefined }, 75 | { name: 'SacredPoint', 76 | type: '{ x: number; y: number; }', 77 | rightNode: [NodeObject], 78 | parentSymbol: null, 79 | typeGuessing: [Guess], 80 | guessedType: 'Guess' }, 81 | { name: 'CursedPoint', 82 | type: '{}', 83 | rightNode: [NodeObject], 84 | parentSymbol: null, 85 | typeGuessing: [Guess], 86 | guessedType: 'Guess' }, 87 | { name: 'x', 88 | parentSymbol: 'CursedPoint', 89 | static: true, 90 | type: 'number', 91 | jsDoc: undefined, 92 | linkedToFunction: null, 93 | rightNode: [TokenObject] }, 94 | { name: 'y', 95 | parentSymbol: 'CursedPoint', 96 | static: true, 97 | type: 'number', 98 | jsDoc: undefined, 99 | linkedToFunction: null, 100 | rightNode: [TokenObject] } ] } -------------------------------------------------------------------------------- /src/generator/mockupWriter.ts: -------------------------------------------------------------------------------- 1 | import { PseudoClass } from './classCollector'; 2 | import { Property } from './propertyCollector'; 3 | import { Runner } from './runner'; 4 | import * as ts from 'typescript'; 5 | 6 | export class MockupWriter { 7 | static print(dts: string) { 8 | const printer: ts.Printer = ts.createPrinter(); 9 | const sourceFile: ts.SourceFile = ts.createSourceFile( 10 | 'test.ts', 11 | dts, 12 | ts.ScriptTarget.ES2017, 13 | true, 14 | ts.ScriptKind.TS 15 | ); 16 | return printer.printFile(sourceFile); 17 | } 18 | 19 | static make(classes: PseudoClass[], functions: PseudoClass[], properties: Property[]): string { 20 | let text = ''; 21 | if (Runner.options.guessTypes) { 22 | text += 'declare type Guess = Partial;\n'; 23 | } 24 | const normal = classes.filter(c => !c.global); 25 | const rootProps = properties.filter(p => !p.parentSymbol); 26 | const namespace = Runner.options.namespace; 27 | if (namespace) { 28 | text += `export declare namespace ${namespace}{ 29 | ${rootProps.map(p => `var ${p.name}: ${this.propertyTypeToString(p)};`).join('\n')} 30 | ${functions.map(f => this.functionToString(f)).join('\n')} 31 | ${normal.map(c => this.classToString(c)).join('\n')} 32 | }`; 33 | } else { 34 | text += ` 35 | ${rootProps 36 | .map(p => `export var ${p.name}: ${this.propertyTypeToString(p)};`) 37 | .join('\n')} 38 | ${functions.map(f => 'export ' + this.functionToString(f)).join('\n')} 39 | ${normal.map(c => 'export ' + this.classToString(c)).join('\n')} 40 | `; 41 | } 42 | return this.print(text); 43 | } 44 | 45 | static propertyToString(property: Property, isMethod = false) { 46 | return `${property.jsDoc && property.jsDoc.getText ? property.jsDoc.getText() + '\n' : ''}${ 47 | property.static ? 'static ' : '' 48 | }${property.readonly ? 'readonly ' : ''}${ 49 | isMethod 50 | ? `${this.toMethodTypeString(property)}` 51 | : `${property.name}: ${this.propertyTypeToString(property)}` 52 | }`.trim(); 53 | } 54 | 55 | static propertyTypeToString(property: Property) { 56 | return `${property.typeGuessing ? property.typeGuessing.toInlineString() : property.type}`; 57 | } 58 | 59 | static toMethodTypeString(property: Property) { 60 | let str = this.propertyTypeToString(property); 61 | if (str.match(/^\((.*)\)\s*\=\>\s*(.+)/i)) { 62 | let returnType = RegExp.$2.trim(); 63 | if (returnType !== 'void') { 64 | return `${property.name}(${RegExp.$1}): ${returnType} { return null; }`; 65 | } 66 | return `${property.name}(${RegExp.$1}): ${returnType} {}`; 67 | } 68 | return `${property.name}: ${this.propertyTypeToString(property)}`; 69 | } 70 | 71 | static classToString(_class: PseudoClass) { 72 | const constructorDoc = _class.jsDoc && _class.jsDoc.getText ? _class.jsDoc.getText() : ''; 73 | return `class ${_class.name}${ 74 | _class.extends ? ` extends ${_class.extends}` : '' 75 | } {${constructorDoc} 76 | ${ 77 | _class.constructorProperty 78 | ? `constructor${_class.constructorProperty.type.replace(/ \=\>.+/i, '')} {}\n` 79 | : `constructor${_class.constructorSignature} {}\n` 80 | }${_class.properties 81 | .filter(p => p !== _class.constructorProperty) 82 | .map(p => this.propertyToString(p, true)) 83 | .join('\n\n')} 84 | }`; 85 | } 86 | 87 | static functionToString(func: PseudoClass) { 88 | const doc = func.jsDoc && func.jsDoc.getText ? func.jsDoc.getText() : ''; 89 | return `${doc}function ${func.name}${func.constructorSignature} { return null; }`; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/generator/runner.ts: -------------------------------------------------------------------------------- 1 | import { File, createProgram, makeFunctionDeclarations } from '../utils'; 2 | import * as ts from 'typescript'; 3 | import * as fs from 'fs'; 4 | import * as path from 'path'; 5 | import { PropertyCollector, Property } from './propertyCollector'; 6 | import { ClassCollector, PseudoClass } from './classCollector'; 7 | import { DTSWriter } from './dtsWriter'; 8 | import ora from 'ora'; 9 | import * as Readline from 'readline'; 10 | import { ErrorLogger } from './errorLogger'; 11 | import { TypeGuesser } from './typeGuesser'; 12 | import { MockupWriter } from './mockupWriter'; 13 | 14 | export interface Options { 15 | namespace?: string; 16 | allFiles?: boolean; 17 | collectRootVariables?: boolean; 18 | guessTypes?: boolean; 19 | mockupMode?: boolean; 20 | } 21 | 22 | export class Runner { 23 | static options: Options = {}; 24 | static result: { 25 | properties: Property[]; 26 | builtData: { 27 | classes: PseudoClass[]; 28 | functions: PseudoClass[]; 29 | }; 30 | }; 31 | 32 | static makeProgram(files: File[]) { 33 | const lib: File = { 34 | content: fs.readFileSync(path.resolve(__dirname, '../../lib/lib.es5.d.ts')).toString(), 35 | fileName: 'lib.es2018.d.ts' 36 | }; 37 | return createProgram([...files, lib], {}); 38 | } 39 | 40 | static run( 41 | options: Options, 42 | files: File[], 43 | fileName: string, 44 | callerPath: string, 45 | mode: 'output' | 'write' = 'write' 46 | ): string { 47 | this.options = options; 48 | 49 | try { 50 | const program = this.makeProgram(files); 51 | 52 | const properties = this._runPhase('Collecting properties', () => 53 | new PropertyCollector().collect(program) 54 | ); 55 | const builtData = this._runPhase('Collecting pseudo classes', () => 56 | new ClassCollector().collect(program, properties) 57 | ); 58 | if (options.guessTypes) { 59 | this._runPhase('Guessing properties typings', () => { 60 | TypeGuesser.guess(program, properties, builtData.classes); 61 | }); 62 | } 63 | const text = this._runPhase('Generating & writing result', () => { 64 | let result; 65 | let resultFileName; 66 | if (options.mockupMode) { 67 | result = MockupWriter.make(builtData.classes, builtData.functions, properties); 68 | resultFileName = fileName.replace(/\.(t|j)s/i, '') + '.ts'; 69 | } else { 70 | result = DTSWriter.make(builtData.classes, builtData.functions, properties); 71 | resultFileName = fileName.replace(/\.(t|j)s/i, '') + '.d.ts'; 72 | } 73 | if (resultFileName && mode === 'write') { 74 | fs.writeFileSync(path.resolve(callerPath, resultFileName), result); 75 | } 76 | return result; 77 | }); 78 | this.result = { 79 | builtData, 80 | properties 81 | }; 82 | 83 | if (mode === 'write') { 84 | const hasError = ErrorLogger.archive.length > 0; 85 | if (hasError) { 86 | const readline = Readline.createInterface({ 87 | input: process.stdin, 88 | output: process.stdout 89 | }); 90 | readline.question(`Show error logs? (y/n): `, value => { 91 | if (value && value[0].toLowerCase().trim() === 'y') { 92 | console.log(ErrorLogger.display(program)); 93 | } 94 | readline.close(); 95 | }); 96 | } 97 | } 98 | 99 | return text; 100 | } catch (error) { 101 | console.log('An unexpected error occurred.'); 102 | throw error; 103 | } 104 | } 105 | 106 | static _runPhase(message: string, func: () => T) { 107 | const spinner = ora(message).start(); 108 | const result = func(); 109 | const hasError = ErrorLogger.logs.length > 0; 110 | if (hasError) { 111 | spinner.fail(`${message} | ${ErrorLogger.logs.length} error(s)`); 112 | } else { 113 | spinner.succeed(); 114 | } 115 | ErrorLogger.store(); 116 | return result; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/generator/dtsWriter.ts: -------------------------------------------------------------------------------- 1 | import { PseudoClass } from './classCollector'; 2 | import { Property } from './propertyCollector'; 3 | import { Runner } from './runner'; 4 | import * as ts from 'typescript'; 5 | 6 | export class DTSWriter { 7 | static print(dts: string) { 8 | const printer: ts.Printer = ts.createPrinter(); 9 | const sourceFile: ts.SourceFile = ts.createSourceFile( 10 | 'test.ts', 11 | dts, 12 | ts.ScriptTarget.ES2017, 13 | true, 14 | ts.ScriptKind.TS 15 | ); 16 | return printer.printFile(sourceFile); 17 | } 18 | 19 | static make(classes: PseudoClass[], functions: PseudoClass[], properties: Property[]): string { 20 | const globals = classes.map(c => (c.global ? c : null)).filter(v => !!v); 21 | let text = ''; 22 | if (Runner.options.guessTypes) { 23 | text += 'declare type Guess = Partial;\n'; 24 | } 25 | if (globals.length > 0) { 26 | text = `declare global { 27 | ${globals 28 | .map( 29 | c => `interface ${c.name} { 30 | ${c.properties 31 | .map(p => this.propertyToString(p, true).replace('static', '')) 32 | .join('\n')} 33 | }` 34 | ) 35 | .join('\n')} 36 | } 37 | `; 38 | } 39 | const normal = classes.filter(c => !c.global); 40 | const rootProps = properties.filter(p => !p.parentSymbol); 41 | const namespace = Runner.options.namespace; 42 | if (namespace) { 43 | text += `export declare namespace ${namespace}{ 44 | ${rootProps.map(p => `var ${p.name}: ${this.propertyTypeToString(p)};`).join('\n')} 45 | ${functions.map(f => this.functionToString(f)).join('\n')} 46 | ${normal.map(c => this.classToString(c)).join('\n')} 47 | }`; 48 | } else { 49 | text += ` 50 | ${rootProps 51 | .map(p => `export var ${p.name}: ${this.propertyTypeToString(p)};`) 52 | .join('\n')} 53 | ${functions.map(f => 'export ' + this.functionToString(f)).join('\n')} 54 | ${normal.map(c => 'export ' + this.classToString(c)).join('\n')} 55 | `; 56 | } 57 | return this.print(text); 58 | } 59 | 60 | static propertyToString(property: Property, isMethod = false) { 61 | return `${property.jsDoc && property.jsDoc.getText ? property.jsDoc.getText() + '\n' : ''}${ 62 | property.static ? 'static ' : '' 63 | }${property.readonly ? 'readonly ' : ''}${ 64 | isMethod 65 | ? `${this.toMethodTypeString(property)}` 66 | : `${property.name}: ${this.propertyTypeToString(property)}` 67 | };`.trim(); 68 | } 69 | 70 | static propertyTypeToString(property: Property) { 71 | return `${property.typeGuessing ? property.typeGuessing.toInlineString() : property.type}`; 72 | } 73 | 74 | static toMethodTypeString(property: Property) { 75 | let str = this.propertyTypeToString(property); 76 | if (str.match(/^\((.*)\)\s*\=\>\s*(.+)/i)) { 77 | return `${property.name}(${RegExp.$1}): ${RegExp.$2}`; 78 | } 79 | return `${property.name}: ${this.propertyTypeToString(property)}`; 80 | } 81 | 82 | static classToString(_class: PseudoClass) { 83 | const constructorDoc = _class.jsDoc && _class.jsDoc.getText ? _class.jsDoc.getText() : ''; 84 | return `class ${_class.name}${ 85 | _class.extends ? ` extends ${_class.extends}` : '' 86 | } {${constructorDoc} 87 | ${ 88 | _class.constructorProperty 89 | ? `constructor${_class.constructorProperty.type.replace(/ \=\>.+/i, '')};\n` 90 | : `constructor${_class.constructorSignature};\n` 91 | }${_class.properties 92 | .filter(p => p !== _class.constructorProperty) 93 | .map(p => this.propertyToString(p, true)) 94 | .join('\n\n')} 95 | }`; 96 | } 97 | 98 | static functionToString(func: PseudoClass) { 99 | const doc = func.jsDoc && func.jsDoc.getText ? func.jsDoc.getText() : ''; 100 | return `${doc}function ${func.name}${func.constructorSignature};`; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /test/units/test.logs.json: -------------------------------------------------------------------------------- 1 | { builtData: { classes: [ [Object] ], functions: [] }, 2 | properties: 3 | [ { name: 'initialize', 4 | parentSymbol: 'Game_Temp', 5 | static: false, 6 | type: '() => void', 7 | jsDoc: undefined, 8 | linkedToFunction: null, 9 | rightNode: [NodeObject], 10 | guessedType: undefined, 11 | typeGuessing: undefined }, 12 | { name: '_isPlaytest', 13 | parentSymbol: 'Game_Temp', 14 | static: false, 15 | type: 'any', 16 | jsDoc: undefined, 17 | linkedToFunction: null, 18 | rightNode: [NodeObject], 19 | guessedType: undefined, 20 | typeGuessing: undefined }, 21 | { name: '_commonEventId', 22 | parentSymbol: 'Game_Temp', 23 | static: false, 24 | type: 'number', 25 | jsDoc: undefined, 26 | linkedToFunction: null, 27 | rightNode: [TokenObject], 28 | guessedType: undefined, 29 | typeGuessing: undefined }, 30 | { name: '_destinationX', 31 | parentSymbol: 'Game_Temp', 32 | static: false, 33 | type: 'any', 34 | jsDoc: undefined, 35 | linkedToFunction: null, 36 | rightNode: [TokenObject], 37 | guessedType: undefined, 38 | typeGuessing: undefined }, 39 | { name: '_destinationY', 40 | parentSymbol: 'Game_Temp', 41 | static: false, 42 | type: 'any', 43 | jsDoc: undefined, 44 | linkedToFunction: null, 45 | rightNode: [TokenObject], 46 | guessedType: undefined, 47 | typeGuessing: undefined }, 48 | { name: 'isPlaytest', 49 | parentSymbol: 'Game_Temp', 50 | static: false, 51 | type: '() => any', 52 | jsDoc: undefined, 53 | linkedToFunction: null, 54 | rightNode: [NodeObject], 55 | guessedType: undefined, 56 | typeGuessing: undefined }, 57 | { name: 'reserveCommonEvent', 58 | parentSymbol: 'Game_Temp', 59 | static: false, 60 | type: '(commonEventId: any) => void', 61 | jsDoc: undefined, 62 | linkedToFunction: null, 63 | rightNode: [NodeObject], 64 | guessedType: undefined, 65 | typeGuessing: undefined }, 66 | { name: 'clearCommonEvent', 67 | parentSymbol: 'Game_Temp', 68 | static: false, 69 | type: '() => void', 70 | jsDoc: undefined, 71 | linkedToFunction: null, 72 | rightNode: [NodeObject], 73 | guessedType: undefined, 74 | typeGuessing: undefined }, 75 | { name: 'isCommonEventReserved', 76 | parentSymbol: 'Game_Temp', 77 | static: false, 78 | type: '() => boolean', 79 | jsDoc: undefined, 80 | linkedToFunction: null, 81 | rightNode: [NodeObject], 82 | guessedType: undefined, 83 | typeGuessing: undefined }, 84 | { name: 'reservedCommonEvent', 85 | parentSymbol: 'Game_Temp', 86 | static: false, 87 | type: '() => any', 88 | jsDoc: undefined, 89 | linkedToFunction: null, 90 | rightNode: [NodeObject], 91 | guessedType: undefined, 92 | typeGuessing: undefined }, 93 | { name: 'setDestination', 94 | parentSymbol: 'Game_Temp', 95 | static: false, 96 | type: '(x: any,y: any) => void', 97 | jsDoc: undefined, 98 | linkedToFunction: null, 99 | rightNode: [NodeObject], 100 | guessedType: undefined, 101 | typeGuessing: undefined }, 102 | { name: 'clearDestination', 103 | parentSymbol: 'Game_Temp', 104 | static: false, 105 | type: '() => void', 106 | jsDoc: undefined, 107 | linkedToFunction: null, 108 | rightNode: [NodeObject], 109 | guessedType: undefined, 110 | typeGuessing: undefined }, 111 | { name: 'isDestinationValid', 112 | parentSymbol: 'Game_Temp', 113 | static: false, 114 | type: '() => boolean', 115 | jsDoc: undefined, 116 | linkedToFunction: null, 117 | rightNode: [NodeObject], 118 | guessedType: undefined, 119 | typeGuessing: undefined }, 120 | { name: 'destinationX', 121 | parentSymbol: 'Game_Temp', 122 | static: false, 123 | type: '() => any', 124 | jsDoc: undefined, 125 | linkedToFunction: null, 126 | rightNode: [NodeObject], 127 | guessedType: undefined, 128 | typeGuessing: undefined }, 129 | { name: 'destinationY', 130 | parentSymbol: 'Game_Temp', 131 | static: false, 132 | type: '() => any', 133 | jsDoc: undefined, 134 | linkedToFunction: null, 135 | rightNode: [NodeObject], 136 | guessedType: undefined, 137 | typeGuessing: undefined } ] } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ES5 To DTS 2 | This tool generates TypeScript declaration files from legacy JS code. 3 | It works by identifying certain patterns from old JS. 4 | 5 | It might not work properly or cover 100% of the possible cases, so you'll probably need to tweak the generated `dts` manually. But it gets the job done and saves a lot of time. It is even better if the legacy code contains JsDoc comments. 6 | 7 | Made using TypeScript compiler API and this AST visualizer: 8 | https://astexplorer.net/ 9 | 10 | 11 | ## Caveats 12 | As I said, the tool may not be able to handle some cases. Errors caught during generation are archived and can be 13 | viewed at the end: 14 | ![Error handling](https://i.imgur.com/ue7EJDu.png) 15 | 16 | Feel free to open an issue with the logs and the concerned part of code. 17 | 18 | Since 1.1.0, es5-to-dts now try to guess types from usage and objects' properties. 19 | (**However it's really unreliable for now**) 20 | 21 | ## Changelog 22 | - 1.0.0: Initial release 23 | - 1.0.3: Fixed lib.es5.d.ts not found 24 | - 1.0.4: Added two more patterns to handle 25 | - 1.0.5: 26 | Restructured code 27 | Added error handling 28 | Added unit testing 29 | Added many more patterns to handle 30 | - 1.1.0: 31 | Added cli options 32 | Added type guessing 33 | - 1.1.1: Added '--all-files' cli flag 34 | - 1.1.2: Added '--mockup' cli flag, in order to generate mockup `ts` files instead of `d.ts`. 35 | Mockup files are simply a `d.ts` version with empty implementation. 36 | 37 | ## TODO 38 | - Remove duplicate properties 39 | - Advanced type guessing 40 | 41 | 42 | ## Installation 43 | `npm -g i es5-to-dts` 44 | Make sure you have the latest TypeScript package installed globally: 45 | `npm -g i typescript` 46 | 47 | To generate a declaration file: 48 | `es5-to-dts oldFile.js` 49 | 50 | ### CLI Options 51 | - `-n namespace` : The definitions will be wrapper inside a namespace. 52 | - `-a` : Process all files inside the directory. 53 | - `-r` : Collect root variables. 54 | - `-g` : Guess types. (WIP) 55 | - `-a outputFileName` : Process all files in the folder and output a single `d.ts` file. 56 | - `-m` : Generate a mockup of the definition file instead of a `d.ts`. 57 | 58 | Example: `es5-to-dts oldFile.js -n MyNamespace` 59 | 60 | 61 | ## Example 62 | Old JS code: 63 | ```javascript 64 | 65 | Number.prototype.mod = function(n) { 66 | return ((this % n) + n) % n; 67 | }; 68 | 69 | SomethingGlobal.changed = true; 70 | var isNotGlobal; 71 | isNotGlobal.a = 5; 72 | 73 | function Animal(name, age, race) { 74 | this.name = name; 75 | this.age = age; 76 | this.race = race; 77 | this._owner = null; 78 | } 79 | 80 | Object.defineProperty(Animal.prototype, 'owner', { 81 | get: function() { 82 | return this._owner; 83 | }, 84 | configurable: true 85 | }); 86 | 87 | /** 88 | * Create a new dog 89 | * 90 | * @param {String} name 91 | * @param {'Pug'|'Shiba Inu'} race 92 | */ 93 | function Dog() { 94 | this.initialize.apply(this, arguments); 95 | } 96 | 97 | Dog.prototype = Object.create(Animal.prototype); 98 | Dog.prototype.constructor = Dog; 99 | 100 | Dog._count = 0; 101 | 102 | Dog.prototype.initialize = function(name, race) { 103 | Animal.prototype.initialize.call(this, name, 0, race); 104 | this.likes = 'bones'; 105 | this.friend = new Animal(); 106 | Dog._count++; 107 | }; 108 | 109 | /** 110 | * Bark for a number of times 111 | * 112 | * @param {Number} times 113 | */ 114 | Dog.prototype.bark = function(times) { 115 | this.barking = true; 116 | }; 117 | 118 | Dog.getCount = function() { 119 | return this._count; 120 | } 121 | ``` 122 | 123 | Result: 124 | ```typescript 125 | declare global { 126 | interface Number { 127 | mod: (n: any) => number; 128 | } 129 | interface SomethingGlobal { 130 | changed: boolean; 131 | } 132 | } 133 | 134 | export declare namespace MyNamespace { 135 | class Animal { 136 | new(name: any, age: any, race: any); 137 | name: any; 138 | 139 | age: any; 140 | 141 | race: any; 142 | 143 | _owner: any; 144 | 145 | readonly owner: any; 146 | } 147 | 148 | class Dog extends Animal { 149 | /** 150 | * Create a new dog 151 | * 152 | * @param {String} name 153 | * @param {'Pug'|'Shiba Inu'} race 154 | */ 155 | new(name: string, race: 'Pug' | 'Shiba Inu'); 156 | static _count: number; 157 | 158 | likes: string; 159 | 160 | friend: Animal; 161 | 162 | /** 163 | * Bark for a number of times 164 | * 165 | * @param {Number} times 166 | */ 167 | bark: (times: number) => any; 168 | 169 | barking: boolean; 170 | 171 | static getCount: () => any; 172 | } 173 | } 174 | ``` 175 | -------------------------------------------------------------------------------- /src/generator/propertyCollector.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { 3 | traverseProgram, 4 | getTypeString, 5 | extractJsDocType, 6 | THIS_TOKEN, 7 | EQUAL_TOKEN 8 | } from '../utils'; 9 | import { ErrorLogger } from './errorLogger'; 10 | import { Runner } from './runner'; 11 | import { Guess } from './typeGuesser'; 12 | 13 | export interface Property { 14 | parentSymbol: string; 15 | name: string; 16 | type: string; 17 | static?: boolean; 18 | readonly?: boolean; 19 | jsDoc?: ts.JSDoc; 20 | linkedToFunction?: string; 21 | typeGuessing?: Guess; 22 | guessedType?: string; 23 | rightNode?: ts.Node; 24 | } 25 | 26 | export class PropertyCollector { 27 | program: ts.Program; 28 | checker: ts.TypeChecker; 29 | properties: Property[] = []; 30 | localVariables: string[] = []; 31 | state: { 32 | parentSymbol?: string; 33 | fromStatic?: boolean; 34 | } = {}; 35 | 36 | collect(program: ts.Program) { 37 | this.program = program; 38 | this.checker = program.getTypeChecker(); 39 | 40 | traverseProgram(program, this._visit.bind(this)); 41 | return this.properties; 42 | } 43 | 44 | _visit(node: ts.Node, parentNode: ts.Node = null) { 45 | try { 46 | if (ts.isVariableStatement(node)) { 47 | this._onVariableStatement(node, parentNode); 48 | } else if (ts.isFunctionDeclaration(node)) { 49 | this._onFunctionDeclaration(node); 50 | } else if ( 51 | ts.isExpressionStatement(node) && 52 | ts.isBinaryExpression(node.expression) && 53 | node.expression.operatorToken.kind === EQUAL_TOKEN 54 | ) { 55 | if (ts.isPropertyAccessExpression(node.expression.left)) { 56 | this._onExpressionStatement(node, node.expression, node.expression.left); 57 | } 58 | } 59 | } catch (error) { 60 | ErrorLogger.add(node, error); 61 | } 62 | } 63 | 64 | _onVariableStatement(node: ts.VariableStatement, parentNode: ts.Node) { 65 | for (const declaration of node.declarationList.declarations) { 66 | if (declaration.initializer) { 67 | if ( 68 | ts.isFunctionExpression(declaration.initializer) || 69 | getTypeString(this.checker, declaration.initializer).match(/^\(.+\)\s*\=\>/) 70 | ) 71 | continue; 72 | } 73 | if (ts.isIdentifier(declaration.name)) { 74 | if(Runner.options.collectRootVariables && !parentNode) { 75 | this.properties.push({ 76 | name: declaration.name.escapedText.toString(), 77 | type: declaration.initializer ? getTypeString(this.checker, declaration.initializer) : 'any', 78 | rightNode: declaration.initializer, 79 | parentSymbol: null 80 | }); 81 | } else { 82 | this.localVariables.push(declaration.name.escapedText.toString()); 83 | } 84 | } 85 | 86 | } 87 | } 88 | 89 | _onFunctionDeclaration(node: ts.FunctionDeclaration) { 90 | const name = node.name.escapedText.toString(); 91 | const { statements } = node.body; 92 | for (const statement of statements) { 93 | this.state.parentSymbol = name; 94 | this.state.fromStatic = false; 95 | this._visit(statement, node); 96 | } 97 | } 98 | 99 | _onExpressionStatement( 100 | node: ts.ExpressionStatement, 101 | expr: ts.BinaryExpression, 102 | left: ts.PropertyAccessExpression 103 | ) { 104 | let symbol: string, name: string, type: string, _static: boolean; 105 | if (left.expression.kind === THIS_TOKEN) { 106 | // this.a = 10 107 | symbol = this.state.parentSymbol; 108 | _static = this.state.fromStatic; 109 | name = left.name.escapedText.toString(); 110 | type = getTypeString(this.checker, expr.right); 111 | } else if (ts.isIdentifier(left.expression)) { 112 | // Global.a = 10 113 | symbol = left.expression.escapedText.toString(); 114 | _static = true; 115 | name = left.name.escapedText.toString(); 116 | type = getTypeString(this.checker, expr.right); 117 | } else if ( 118 | ts.isPropertyAccessExpression(left.expression) && 119 | left.expression.name.kind === 72 /* prototype */ && 120 | ts.isIdentifier(left.expression.expression) 121 | ) { 122 | // Global.prototype.a = 10 123 | symbol = left.expression.expression.escapedText.toString(); 124 | name = left.name.escapedText.toString(); 125 | type = getTypeString(this.checker, expr.right); 126 | _static = false; 127 | } 128 | // Found 129 | if (symbol) { 130 | let doc: ts.JSDoc; 131 | if ((node as any).jsDoc) { 132 | doc = (node as any).jsDoc[(node as any).jsDoc.length - 1]; 133 | type = extractJsDocType(doc); 134 | } 135 | const property: Property = { 136 | name, 137 | parentSymbol: symbol, 138 | static: _static, 139 | type, 140 | jsDoc: doc, 141 | linkedToFunction: ts.isIdentifier(expr.right) ? expr.right.escapedText.toString() : null, 142 | rightNode: expr.right 143 | }; 144 | const exist = this.properties.find( 145 | p => 146 | p.name === property.name && 147 | p.parentSymbol === property.parentSymbol && 148 | p.static === property.static 149 | ); 150 | const forbidden = ['constructor', 'prototype']; 151 | const localCheck = 152 | left.expression.kind === THIS_TOKEN 153 | ? true 154 | : !this.localVariables.includes(name) && !this.localVariables.includes(symbol); 155 | if (!exist && !forbidden.includes(name) && localCheck) { 156 | this.properties.push(property); 157 | if (ts.isFunctionExpression(expr.right)) { 158 | const { statements } = expr.right.body; 159 | for (const statement of statements) { 160 | this.state.parentSymbol = symbol; 161 | this.state.fromStatic = _static; 162 | this._visit(statement); 163 | } 164 | } 165 | } 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { PseudoClass } from './generator/classCollector'; 3 | 4 | export interface File { 5 | fileName: string; 6 | content: string; 7 | sourceFile?: ts.SourceFile; 8 | } 9 | 10 | export function createProgram(files: File[], compilerOptions?: ts.CompilerOptions): ts.Program { 11 | const tsConfigJson = ts.parseConfigFileTextToJson( 12 | 'tsconfig.json', 13 | compilerOptions 14 | ? JSON.stringify(compilerOptions) 15 | : `{ 16 | "compilerOptions": { 17 | "target": "es2018", 18 | "module": "commonjs", 19 | "rootDir": ".", 20 | "strict": false, 21 | "esModuleInterop": true, 22 | "allowJs": true 23 | } 24 | ` 25 | ); 26 | let { options, errors } = ts.convertCompilerOptionsFromJson( 27 | tsConfigJson.config.compilerOptions, 28 | '.' 29 | ); 30 | if (errors.length) { 31 | throw errors; 32 | } 33 | const compilerHost = ts.createCompilerHost(options); 34 | compilerHost.getSourceFile = function( 35 | fileName: string, 36 | languageVersion: ts.ScriptTarget, 37 | onError?: (message: string) => void, 38 | shouldCreateNewSourceFile?: boolean 39 | ): ts.SourceFile | undefined { 40 | const file = files.find(f => f.fileName === fileName); 41 | if (!file) return undefined; 42 | file.sourceFile = 43 | file.sourceFile || ts.createSourceFile(fileName, file.content, ts.ScriptTarget.ES2015, true); 44 | return file.sourceFile; 45 | }; 46 | compilerHost.resolveTypeReferenceDirectives = function( 47 | typeReferenceDirectiveNames: string[], 48 | containingFile: string 49 | ): (ts.ResolvedTypeReferenceDirective | undefined)[] { 50 | return []; 51 | }; 52 | return ts.createProgram(files.map(f => f.fileName), options, compilerHost); 53 | } 54 | 55 | // --------------- 56 | 57 | export const EQUAL_TOKEN = 59; 58 | export const THIS_TOKEN = 100; 59 | 60 | export interface Variable { 61 | name: string; 62 | type: string; 63 | } 64 | 65 | export function makeFunctionDeclarations(classes: PseudoClass[]) { 66 | const functions: PseudoClass[] = []; 67 | // - Remove pseudo classes without properties (= they are not classes then) 68 | classes = classes.filter(c => { 69 | if (c.properties.length > 0) return true; 70 | functions.push(c); 71 | return false; 72 | }); 73 | return [classes, functions]; 74 | } 75 | 76 | export function makePropertyFromObjectLiteral( 77 | checker: ts.TypeChecker, 78 | expr: ts.ObjectLiteralExpression, 79 | jsDoc: ts.JSDoc 80 | ) { 81 | let readonly: boolean = true; 82 | let type: string = 'any'; 83 | for (const prop of expr.properties) { 84 | if (ts.isPropertyAssignment(prop)) { 85 | if ((prop.name as ts.Identifier).escapedText.toString() === 'set') { 86 | readonly = false; 87 | } else if ((prop.name as ts.Identifier).escapedText.toString() === 'get') { 88 | type = getTypeString(checker, prop.initializer); 89 | if (type.match(/\=\>(.+)/i)) { 90 | type = RegExp.$1.trim(); 91 | } 92 | if (jsDoc) { 93 | type = extractJsDocType(jsDoc, type); 94 | } 95 | } else if ((prop.name as ts.Identifier).escapedText.toString() === 'value') { 96 | type = getTypeString(checker, prop.initializer); 97 | if (jsDoc) { 98 | type = extractJsDocType(jsDoc, type); 99 | } 100 | } 101 | } 102 | } 103 | return { 104 | readonly, 105 | type 106 | }; 107 | } 108 | 109 | export function makeVariablesFromParameters( 110 | checker: ts.TypeChecker, 111 | params: ts.ParameterDeclaration[] 112 | ) { 113 | const variables: Variable[] = []; 114 | for (const param of params) { 115 | variables.push({ 116 | name: (param.name as ts.Identifier).escapedText.toString(), 117 | type: getTypeString(checker, param) 118 | }); 119 | } 120 | return variables; 121 | } 122 | 123 | export function traverseProgram(program: ts.Program, callback: (node: ts.Node) => any) { 124 | for (const sourceFile of program.getSourceFiles()) { 125 | if (!sourceFile.isDeclarationFile) { 126 | ts.forEachChild(sourceFile, callback); 127 | } 128 | } 129 | } 130 | 131 | export function getTypeString(checker: ts.TypeChecker, node: ts.Node) { 132 | let type = checker.getTypeAtLocation(node); 133 | if (node.getText().match(/^\s*new\s+(.+?)\(.*\)/i)) { 134 | return RegExp.$1.trim(); 135 | } 136 | if (type.isLiteral()) { 137 | type = checker.getBaseTypeOfLiteralType(type); 138 | } 139 | const value = checker.typeToString(type, node, ts.TypeFormatFlags.NoTruncation); 140 | if (value === 'null') return 'any'; 141 | if (value === 'false' || value === 'true') return 'boolean'; 142 | return value; 143 | } 144 | 145 | export function convertJsDocType(type: string) { 146 | if (type.includes('|')) { 147 | return type 148 | .split('|') 149 | .map(v => convertJsDocType(v)) 150 | .join(' | '); 151 | } 152 | type = 153 | { 154 | String: 'string', 155 | Number: 'number', 156 | Array: 'any[]', 157 | Any: 'any', 158 | Boolean: 'boolean', 159 | '*': 'any' 160 | }[type] || type; 161 | return type; 162 | } 163 | 164 | export function extractJsDocType(doc: ts.JSDoc, currentType: string = '') { 165 | const params: { name: string; type: string }[] = []; 166 | let returnType: string = 'any'; 167 | let absoluteType: string; 168 | 169 | const extractType = (tag: any) => { 170 | let type: string; 171 | if (tag.typeExpression == null) { 172 | type = 'any'; 173 | } else { 174 | type = tag.typeExpression.type.getText(); 175 | // if (!(tag.typeExpression.type as any).typeName) { 176 | // type = tag.typeExpression.type.getText(); 177 | // } else { 178 | // type = (tag.typeExpression.type as any).typeName.escapedText.toString(); 179 | // } 180 | } 181 | return type; 182 | }; 183 | 184 | let autoGeneratedArgs = 0; 185 | for (const tag of Array.from(doc.tags || [])) { 186 | if (ts.isJSDocTypeTag(tag)) { 187 | absoluteType = extractType(tag); 188 | return convertJsDocType(absoluteType); 189 | } else if (ts.isJSDocParameterTag(tag)) { 190 | const type = extractType(tag); 191 | let name = (tag.name as ts.Identifier).escapedText.toString(); 192 | if (!name) { 193 | const commentArg = tag.comment.split(' ')[0]; 194 | if (commentArg.startsWith('...')) { 195 | name = commentArg; 196 | } else { 197 | name = `arg${Number(autoGeneratedArgs++)}`; 198 | } 199 | } 200 | params.push({ name, type: convertJsDocType(type) }); 201 | } else if (ts.isJSDocReturnTag(tag)) { 202 | const type = extractType(tag); 203 | returnType = convertJsDocType(type); 204 | } 205 | } 206 | if (currentType.match(/\(.+\)\s*\=\>\s*(.+)/)) { 207 | returnType = returnType || RegExp.$2.trim(); 208 | } 209 | return `(${params.map(p => `${p.name}: ${p.type}`).join(', ')}) => ${returnType}`; 210 | } 211 | 212 | export function objectLiteralToObject(expr: ts.ObjectLiteralExpression) { 213 | if(expr.getText().length < 2) return {}; 214 | let str = expr.getText().replace(/(^\{|\}$)/ig,''); 215 | const props = str.split(/(;|,)/); 216 | let obj = {}; 217 | for (const p of props) { 218 | if(p.match(/(.+)\:(.+)/)) { 219 | const key = RegExp.$1.trim(); 220 | let value: any = RegExp.$2.trim(); 221 | if (value.match(/\{.+\}/)) { 222 | value = objectLiteralToObject({ 223 | getText: () => value 224 | } as any); 225 | } 226 | obj[key] = value; 227 | } 228 | } 229 | return obj; 230 | } 231 | 232 | export function collectNodesBy(program: ts.Program, constraint: (node: ts.Node) => boolean, startingNode?: ts.Node) { 233 | let nodes = []; 234 | let visit = (node: ts.Node) => { 235 | if(constraint(node)) { 236 | nodes.push(node); 237 | } else { 238 | node.forEachChild(visit); 239 | } 240 | }; 241 | if(startingNode) { 242 | startingNode.forEachChild(visit); 243 | } else { 244 | traverseProgram(program, visit); 245 | } 246 | return nodes; 247 | } 248 | -------------------------------------------------------------------------------- /src/generator/classCollector.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { 3 | traverseProgram, 4 | getTypeString, 5 | extractJsDocType, 6 | makeVariablesFromParameters, 7 | makePropertyFromObjectLiteral, 8 | Variable 9 | } from '../utils'; 10 | import { Property } from './propertyCollector'; 11 | import { ErrorLogger } from './errorLogger'; 12 | 13 | 14 | export interface PseudoClass { 15 | name: string; 16 | constructorArgs: Variable[]; 17 | properties: Property[]; 18 | global?: boolean; 19 | constructorProperty?: Property; 20 | constructorSignature?: string; 21 | extends?: string; 22 | jsDoc?: ts.JSDoc; 23 | } 24 | 25 | export class ClassCollector { 26 | program: ts.Program; 27 | checker: ts.TypeChecker; 28 | properties: Property[] = []; 29 | classes: PseudoClass[] = []; 30 | localVariables: string[] = []; 31 | state: { 32 | parentSymbol?: string; 33 | fromStatic?: boolean; 34 | } = {}; 35 | 36 | collect(program: ts.Program, properties: Property[]) { 37 | this.program = program; 38 | this.checker = program.getTypeChecker(); 39 | this.properties = properties; 40 | 41 | traverseProgram(program, this._visit.bind(this)); 42 | // - Find out global pseudo classes 43 | for (const p of properties) { 44 | if(!p.parentSymbol) continue; 45 | const _class = this.classes.find(c => c.name === p.parentSymbol); 46 | if (!_class) { 47 | const parentAsProp = properties.find(_p => _p.name === p.parentSymbol); 48 | if(parentAsProp && !parentAsProp.parentSymbol) continue; 49 | this.classes.push({ 50 | constructorArgs: [], 51 | constructorProperty: null, 52 | global: true, 53 | name: p.parentSymbol, 54 | properties: properties 55 | .map(_p => (_p.parentSymbol === p.parentSymbol ? _p : null)) 56 | .filter(v => !!v) 57 | }); 58 | } 59 | } 60 | 61 | const result = this._makePureFunctions(this.classes); 62 | this.classes = result[0]; 63 | const functions = result[1]; 64 | return { 65 | classes: this.classes, 66 | functions 67 | }; 68 | } 69 | 70 | _makePureFunctions(classes: PseudoClass[]) { 71 | let functions: PseudoClass[] = []; 72 | /* 73 | Remove pseudo classes who do not have any properties and 74 | 1) are not used as a parent for inheritance 75 | 2) don't inherit anything 76 | */ 77 | classes = classes.filter(c => { 78 | if (c.properties.length > 0) return true; 79 | const child = classes.find(child => child.extends === c.name); 80 | if(!!child) return true; 81 | if(!!c.extends) return true; 82 | functions.push(c); 83 | return false; 84 | }); 85 | // Exclude linked functions 86 | functions = functions.filter(f => !this.properties.find(p => p.linkedToFunction === f.name)); 87 | return [classes, functions]; 88 | } 89 | 90 | _visit(node: ts.Node) { 91 | try { 92 | if (ts.isFunctionDeclaration(node)) { 93 | this._onFunctionDeclaration(node); 94 | } else if (ts.isVariableStatement(node)) { 95 | this._onVariableStatement(node); 96 | } else if (ts.isExpressionStatement(node)) { 97 | this._onExpressionStatement(node); 98 | } 99 | } catch (error) { 100 | ErrorLogger.add(node, error); 101 | } 102 | } 103 | 104 | _onFunctionDeclaration(node: ts.FunctionDeclaration) { 105 | const name = node.name.escapedText.toString(); 106 | const constructorArgs = makeVariablesFromParameters( 107 | this.checker, 108 | Array.from(node.parameters || []) 109 | ); 110 | const { constructorSignature, constructorProperty, jsDoc } = this._makeConstructorSpecs( 111 | node, 112 | node.body, 113 | name, 114 | constructorArgs 115 | ); 116 | this.classes.push({ 117 | name, 118 | constructorArgs, 119 | properties: this.properties.map(p => (p.parentSymbol === name ? p : null)).filter(v => !!v), 120 | constructorProperty, 121 | jsDoc: jsDoc || null, 122 | constructorSignature 123 | }); 124 | } 125 | 126 | _onVariableStatement(node: ts.VariableStatement) { 127 | const declaration = node.declarationList.declarations[0]; 128 | if ( 129 | ts.isVariableDeclaration(declaration) && 130 | declaration.initializer 131 | ) { 132 | if(ts.isFunctionExpression(declaration.initializer)) { 133 | const name = (declaration.name as ts.Identifier).escapedText.toString(); 134 | const constructorArgs = makeVariablesFromParameters( 135 | this.checker, 136 | Array.from(declaration.initializer.parameters || []) 137 | ); 138 | const body = declaration.initializer.body; 139 | const { constructorSignature, constructorProperty, jsDoc } = this._makeConstructorSpecs( 140 | node, 141 | body, 142 | name, 143 | constructorArgs 144 | ); 145 | this.classes.push({ 146 | name, 147 | constructorArgs, 148 | properties: this.properties.map(p => (p.parentSymbol === name ? p : null)).filter(v => !!v), 149 | constructorProperty, 150 | jsDoc: jsDoc || null, 151 | constructorSignature 152 | }); 153 | } else if(ts.isIdentifier(declaration.initializer)) { 154 | const name = (declaration.name as ts.Identifier).escapedText.toString(); 155 | const funcName = declaration.initializer.escapedText.toString(); 156 | let _class: PseudoClass; 157 | this.classes = this.classes.filter(c => { 158 | if(c.name === funcName) { 159 | _class = c; 160 | return false; 161 | } 162 | return true; 163 | }); 164 | if(_class) { 165 | this.classes.push({ 166 | ..._class, 167 | name, 168 | properties: this.properties.map(p => (p.parentSymbol === name ? p : null)).filter(v => !!v) 169 | }); 170 | } 171 | } 172 | } 173 | } 174 | 175 | _onExpressionStatement(node: ts.ExpressionStatement) { 176 | this._checkPrototypeInheritance(node) || this._checkPropertiesDefinition(node); 177 | } 178 | 179 | _checkPrototypeInheritance(node: ts.ExpressionStatement) { 180 | const str = node.getText(); 181 | if (str.match(/(.+)\.prototype\s*\=\s*Object\.create\((.+)\.prototype\)/i)) { 182 | let _class = this.classes.find(c => c.name === RegExp.$1); 183 | if (!_class) { 184 | _class = { 185 | constructorArgs: [], 186 | global: true, 187 | name: RegExp.$1, 188 | properties: [], 189 | extends: RegExp.$2 190 | }; 191 | this.classes.push(_class); 192 | } else { 193 | _class.extends = RegExp.$2; 194 | } 195 | return true; 196 | } 197 | return false; 198 | } 199 | 200 | _checkPropertiesDefinition(node: ts.ExpressionStatement) { 201 | const str = node.getText(); 202 | if (str.match(/Object\.definePropert.+\((.+?),/i) && ts.isCallExpression(node.expression)) { 203 | // - Extract properties from getter/setter 204 | const properties: Property[] = []; 205 | const value = RegExp.$1; 206 | let symbol: string, _static: boolean; 207 | if (value.match(/(.+)\.prototype/)) { 208 | symbol = RegExp.$1; 209 | _static = false; 210 | } else { 211 | symbol = value; 212 | _static = true; 213 | } 214 | const arg = (node.expression as ts.CallExpression).arguments[1]; 215 | if (ts.isStringLiteral(arg)) { 216 | let jsDoc: ts.JSDoc = (node as any).jsDoc || null; 217 | const lastJsDoc: ts.JSDoc = jsDoc ? jsDoc[(jsDoc as any).length - 1] : null; 218 | const objArg = (node.expression as ts.CallExpression).arguments[2]; 219 | if (ts.isObjectLiteralExpression(objArg)) { 220 | const result = makePropertyFromObjectLiteral(this.checker, objArg, lastJsDoc); 221 | properties.push({ 222 | name: arg.text, 223 | parentSymbol: symbol, 224 | static: _static, 225 | type: result.type, 226 | readonly: result.readonly 227 | }); 228 | } 229 | } else if (ts.isObjectLiteralExpression(arg)) { 230 | for (const prop of arg.properties) { 231 | if (ts.isPropertyAssignment(prop)) { 232 | let jsDoc: ts.JSDoc = (prop as any).jsDoc || null; 233 | const lastJsDoc: ts.JSDoc = jsDoc ? jsDoc[(jsDoc as any).length - 1] : null; 234 | const name = (prop.name as ts.Identifier).escapedText.toString(); 235 | if (ts.isObjectLiteralExpression(prop.initializer)) { 236 | const result = makePropertyFromObjectLiteral( 237 | this.checker, 238 | prop.initializer, 239 | lastJsDoc 240 | ); 241 | properties.push({ 242 | name, 243 | parentSymbol: symbol, 244 | readonly: result.readonly, 245 | static: _static, 246 | type: result.type, 247 | jsDoc 248 | }); 249 | } else { 250 | properties.push({ 251 | name, 252 | parentSymbol: symbol, 253 | readonly: true, 254 | static: _static, 255 | type: getTypeString(this.checker, prop.initializer), 256 | jsDoc 257 | }); 258 | } 259 | } 260 | } 261 | } 262 | let _class = this.classes.find(c => c.name === symbol); 263 | if (!_class) { 264 | _class = { 265 | constructorArgs: [], 266 | global: true, 267 | name: symbol, 268 | properties: [...properties] 269 | }; 270 | this.classes.push(_class); 271 | } else { 272 | _class.properties = [].concat(_class.properties, properties); 273 | } 274 | return true; 275 | } 276 | return false; 277 | } 278 | 279 | _makeConstructorSpecs( 280 | node: ts.FunctionDeclaration | ts.VariableStatement, 281 | body: ts.Block, 282 | name: string, 283 | constructorArgs 284 | ) { 285 | const jsDoc: ts.JSDoc = (node as any).jsDoc; 286 | const lastJsDoc: ts.JSDoc = jsDoc ? jsDoc[(jsDoc as any).length - 1] : null; 287 | 288 | if (!body || !body.statements) return; 289 | /* 290 | - Look for call of the true constructor 291 | function Test() { 292 | this.init.apply(this, arguments); 293 | } 294 | */ 295 | let constructorProperty: Property = null; 296 | for (const statement of body.statements) { 297 | if (ts.isExpressionStatement(statement)) { 298 | const str = statement.getText(); 299 | if (str.match(/this\.(.+)\.apply\(this,/i)) { 300 | constructorProperty = this.properties.find( 301 | p => p.parentSymbol === name && p.name === RegExp.$1 302 | ); 303 | if (jsDoc) 304 | constructorProperty.type = extractJsDocType(lastJsDoc, constructorProperty.type); 305 | } 306 | } 307 | } 308 | let constructorSignature = null; 309 | // - Didn't find a call to a pseudo constructor 310 | if (!constructorProperty) { 311 | constructorSignature = jsDoc 312 | ? extractJsDocType(lastJsDoc).replace(/\=\>.+/i, '') 313 | : `(${constructorArgs.map(p => `${p.name}: ${p.type}`).join(', ')})`; 314 | } 315 | return { 316 | constructorSignature, 317 | constructorProperty, 318 | jsDoc: lastJsDoc 319 | }; 320 | } 321 | } 322 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_53f8a438f127461b135955be4d38700f869e071d/code/cache/400dc2724d60301e5b09363e096017caf00665d0: -------------------------------------------------------------------------------- 1 | {"code":"import * as ts from 'typescript';\r\nimport * as Beautify from 'js-beautify';\r\nexport function createProgram(files, compilerOptions) {\r\n var tsConfigJson = ts.parseConfigFileTextToJson('tsconfig.json', compilerOptions\r\n ? JSON.stringify(compilerOptions)\r\n : \"{\\n \\\"compilerOptions\\\": {\\n \\\"target\\\": \\\"es2018\\\", \\n \\\"module\\\": \\\"commonjs\\\", \\n \\\"lib\\\": [\\\"es2018\\\"],\\n \\\"rootDir\\\": \\\".\\\",\\n \\\"strict\\\": false, \\n \\\"esModuleInterop\\\": true,\\n \\\"noImplicitAny\\\": true,\\n \\\"allowJs\\\": true\\n }\\n \");\r\n var _a = ts.convertCompilerOptionsFromJson(tsConfigJson.config.compilerOptions, '.'), options = _a.options, errors = _a.errors;\r\n if (errors.length) {\r\n throw errors;\r\n }\r\n var compilerHost = ts.createCompilerHost(options);\r\n compilerHost.getSourceFile = function (fileName, languageVersion, onError, shouldCreateNewSourceFile) {\r\n var file = files.find(function (f) { return f.fileName === fileName; });\r\n if (!file)\r\n return undefined;\r\n file.sourceFile =\r\n file.sourceFile || ts.createSourceFile(fileName, file.content, ts.ScriptTarget.ES2015, true);\r\n return file.sourceFile;\r\n };\r\n // in order to typechecker to work we need to implement the following method, the following implementation is enough:\r\n compilerHost.resolveTypeReferenceDirectives = function (typeReferenceDirectiveNames, containingFile) {\r\n return [];\r\n };\r\n return ts.createProgram(files.map(function (f) { return f.fileName; }), options, compilerHost);\r\n}\r\n// ---------------\r\nvar EQUAL_TOKEN = 59;\r\nvar THIS_TOKEN = 100;\r\nexport function collectProperties(program) {\r\n var properties = [];\r\n var checker = program.getTypeChecker();\r\n function visit(node, parentSymbol, fromStatic) {\r\n if (parentSymbol === void 0) { parentSymbol = null; }\r\n if (fromStatic === void 0) { fromStatic = null; }\r\n if (ts.isFunctionDeclaration(node)) {\r\n var name = node.name.escapedText.toString();\r\n var statements = node.body.statements;\r\n for (var _i = 0, statements_1 = statements; _i < statements_1.length; _i++) {\r\n var statement = statements_1[_i];\r\n visit(statement, name, false);\r\n }\r\n }\r\n else if (ts.isExpressionStatement(node) &&\r\n ts.isBinaryExpression(node.expression) &&\r\n node.expression.operatorToken.kind === EQUAL_TOKEN) {\r\n if (ts.isPropertyAccessExpression(node.expression.left)) {\r\n var expr = node.expression.left;\r\n var symbol = void 0, name = void 0, type = void 0, _static = void 0;\r\n if (expr.expression.kind === THIS_TOKEN) {\r\n // this.a = 10\r\n symbol = parentSymbol;\r\n _static = fromStatic;\r\n name = expr.name.escapedText.toString();\r\n type = getTypeString(checker, node.expression.right);\r\n }\r\n else if (ts.isIdentifier(expr.expression)) {\r\n // Global.a = 10\r\n symbol = expr.expression.escapedText.toString();\r\n _static = true;\r\n name = expr.name.escapedText.toString();\r\n type = getTypeString(checker, node.expression.right);\r\n }\r\n else if (ts.isPropertyAccessExpression(expr.expression) &&\r\n expr.expression.name.kind === 72 /* prototype */) {\r\n // Global.prototype.a = 10\r\n symbol = expr.expression.expression.escapedText.toString();\r\n name = expr.name.escapedText.toString();\r\n type = getTypeString(checker, node.expression.right);\r\n _static = false;\r\n }\r\n // Found\r\n if (symbol) {\r\n var property_1 = {\r\n name: name,\r\n parentSymbol: symbol,\r\n static: _static,\r\n type: type\r\n };\r\n var exist = properties.find(function (p) {\r\n return p.name === property_1.name &&\r\n p.parentSymbol === property_1.parentSymbol &&\r\n p.static === property_1.static;\r\n });\r\n var forbidden = ['constructor', 'prototype'];\r\n if (!exist && !forbidden.includes(name)) {\r\n properties.push(property_1);\r\n if (ts.isFunctionExpression(node.expression.right)) {\r\n var statements = node.expression.right.body.statements;\r\n for (var _a = 0, statements_2 = statements; _a < statements_2.length; _a++) {\r\n var statement = statements_2[_a];\r\n visit(statement, symbol, _static);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n traverseProgram(program, visit);\r\n return properties;\r\n}\r\nexport function makePseudoClasses(program, properties) {\r\n var classes = [];\r\n var checker = program.getTypeChecker();\r\n function visit(node) {\r\n if (ts.isFunctionDeclaration(node)) {\r\n var name_1 = node.name.escapedText.toString();\r\n var constructorArgs = makeVariablesFromParameters(checker, Array.from(node.parameters || []));\r\n // - Look for call of the true constructor\r\n var constructorProperty = null;\r\n for (var _i = 0, _a = node.body.statements; _i < _a.length; _i++) {\r\n var statement = _a[_i];\r\n if (ts.isExpressionStatement(statement)) {\r\n var str = statement.getText();\r\n if (str.match(/this\\.(.+)\\.apply\\(this,/i)) {\r\n constructorProperty = properties.find(function (p) { return p.parentSymbol === name_1 && p.name === RegExp.$1; });\r\n }\r\n }\r\n }\r\n classes.push({\r\n name: name_1,\r\n constructorArgs: constructorArgs,\r\n properties: properties.map(function (p) { return (p.parentSymbol === name_1 ? p : null); }).filter(function (v) { return !!v; }),\r\n constructorProperty: constructorProperty\r\n });\r\n }\r\n else if (ts.isExpressionStatement(node)) {\r\n var str = node.getText();\r\n if (str.match(/(.+)\\.prototype\\s*\\=\\s*Object\\.create\\((.+)\\.prototype\\)/i)) {\r\n // -\r\n var _class = classes.find(function (c) { return c.name === RegExp.$1; });\r\n _class.extends = RegExp.$2;\r\n }\r\n else if (str.match(/Object\\.defineProperties\\((.+)/i)) {\r\n // - Extract properties from getter/setter\r\n var value = RegExp.$1;\r\n var symbol_1, _static = void 0;\r\n if (value.match(/(.+)\\.prototype/)) {\r\n symbol_1 = RegExp.$1;\r\n _static = false;\r\n }\r\n else {\r\n symbol_1 = value;\r\n _static = true;\r\n }\r\n var arg = node.expression.arguments[1];\r\n if (ts.isObjectLiteralExpression(arg)) {\r\n var properties_2 = [];\r\n for (var _b = 0, _c = arg.properties; _b < _c.length; _b++) {\r\n var prop = _c[_b];\r\n if (ts.isPropertyAssignment(prop)) {\r\n var name = prop.name.escapedText.toString();\r\n var readonly = true;\r\n var type = null;\r\n if (ts.isObjectLiteralExpression(prop.initializer)) {\r\n for (var _d = 0, _e = prop.initializer.properties; _d < _e.length; _d++) {\r\n var subProp = _e[_d];\r\n if (ts.isPropertyAssignment(subProp)) {\r\n if (subProp.name.escapedText.toString() === 'set') {\r\n readonly = false;\r\n }\r\n else if (subProp.name.escapedText.toString() === 'get') {\r\n type = getTypeString(checker, subProp.initializer);\r\n if (type.match(/\\=\\>(.+)/i)) {\r\n type = RegExp.$1.trim();\r\n }\r\n }\r\n }\r\n }\r\n }\r\n properties_2.push({\r\n name: name,\r\n parentSymbol: symbol_1,\r\n readonly: readonly,\r\n static: _static,\r\n type: type\r\n });\r\n }\r\n }\r\n var _class = classes.find(function (c) { return c.name === symbol_1; });\r\n _class.properties = [].concat(_class.properties, properties_2);\r\n }\r\n }\r\n }\r\n //ts.forEachChild(node, visit);\r\n }\r\n traverseProgram(program, visit);\r\n var _loop_1 = function (p) {\r\n var _class = classes.find(function (c) { return c.name === p.parentSymbol; });\r\n if (!_class) {\r\n classes.push({\r\n constructorArgs: [],\r\n constructorProperty: null,\r\n global: true,\r\n name: p.parentSymbol,\r\n properties: properties\r\n .map(function (_p) { return (_p.parentSymbol === p.parentSymbol ? _p : null); })\r\n .filter(function (v) { return !!v; })\r\n });\r\n }\r\n };\r\n // - Find out global pseudo classes\r\n for (var _i = 0, properties_1 = properties; _i < properties_1.length; _i++) {\r\n var p = properties_1[_i];\r\n _loop_1(p);\r\n }\r\n return classes;\r\n}\r\nexport function makeVariablesFromParameters(checker, params) {\r\n var variables = [];\r\n for (var _i = 0, params_1 = params; _i < params_1.length; _i++) {\r\n var param = params_1[_i];\r\n variables.push({\r\n name: param.name.escapedText.toString(),\r\n type: getTypeString(checker, param)\r\n });\r\n }\r\n return variables;\r\n}\r\nexport function traverseProgram(program, callback) {\r\n for (var _i = 0, _a = program.getSourceFiles(); _i < _a.length; _i++) {\r\n var sourceFile = _a[_i];\r\n if (!sourceFile.isDeclarationFile) {\r\n // Walk the tree to search for classes\r\n ts.forEachChild(sourceFile, callback);\r\n }\r\n }\r\n}\r\nexport function getTypeString(checker, node) {\r\n var type = checker.getTypeAtLocation(node);\r\n if (type.isLiteral()) {\r\n type = checker.getBaseTypeOfLiteralType(type);\r\n }\r\n return checker.typeToString(type);\r\n}\r\nexport function makeDTS(classes, namespace) {\r\n var globals = classes.map(function (c) { return (c.global ? c : null); }).filter(function (v) { return !!v; });\r\n var text = '';\r\n if (globals.length > 0) {\r\n text = \"declare global {\\n \" + globals\r\n .map(function (c) { return \"interface \" + c.name + \" {\\n \" + c.properties.map(function (p) { return p.name + \": \" + p.type + \";\"; }).join('\\n') + \"\\n }\"; })\r\n .join('\\n') + \"\\n }\\n \";\r\n }\r\n var normal = classes.filter(function (c) { return !c.global; });\r\n text += \"export declare namespace \" + namespace + \"{\\n \" + normal.map(function (c) { return classToString(c); }).join('\\n') + \"\\n }\";\r\n return Beautify.js(text, {});\r\n}\r\nexport function propertyToString(property) {\r\n return ((property.readonly ? 'readonly' : '') + \" \" + (property.static ? 'static' : '') + \" \" + property.name + \": \" + property.type + \";\").trim();\r\n}\r\nexport function classToString(_class) {\r\n return \"class \" + _class.name + (_class.extends ? \" extends \" + _class.extends : '') + \" {\\n \" + (_class.constructorProperty\r\n ? \"new \" + _class.constructorProperty.type.replace(/ \\=\\>.+/i, '') + \";\\n\"\r\n : '') + _class.properties\r\n .filter(function (p) { return p !== _class.constructorProperty; })\r\n .map(function (p) { return propertyToString(p); })\r\n .join('\\n\\n') + \"\\n }\";\r\n}\r\n","dts":{"name":"/media/cyriac/0CC5166B0CC5166B/Work/es5-to-dts/src/generator.d.ts","writeByteOrderMark":false,"text":"import * as ts from 'typescript';\r\nexport interface File {\r\n fileName: string;\r\n content: string;\r\n sourceFile?: ts.SourceFile;\r\n}\r\nexport declare function createProgram(files: File[], compilerOptions?: ts.CompilerOptions): ts.Program;\r\nexport interface Variable {\r\n name: string;\r\n type: string;\r\n}\r\nexport interface Property {\r\n parentSymbol: string;\r\n name: string;\r\n type: string;\r\n static?: boolean;\r\n readonly?: boolean;\r\n}\r\nexport declare function collectProperties(program: ts.Program): Property[];\r\ninterface PseudoClass {\r\n name: string;\r\n constructorArgs: Variable[];\r\n properties: Property[];\r\n global?: boolean;\r\n constructorProperty?: Property;\r\n extends?: string;\r\n}\r\nexport declare function makePseudoClasses(program: ts.Program, properties: Property[]): PseudoClass[];\r\nexport declare function makeVariablesFromParameters(checker: ts.TypeChecker, params: ts.ParameterDeclaration[]): Variable[];\r\nexport declare function traverseProgram(program: ts.Program, callback: (node: ts.Node) => any): void;\r\nexport declare function getTypeString(checker: ts.TypeChecker, node: ts.Node): string;\r\nexport declare function makeDTS(classes: PseudoClass[], namespace: string): any;\r\nexport declare function propertyToString(property: Property): string;\r\nexport declare function classToString(_class: PseudoClass): string;\r\nexport {};\r\n"}} 2 | -------------------------------------------------------------------------------- /src/generator/typeGuesser.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Property } from './propertyCollector'; 3 | import { getTypeString, objectLiteralToObject, traverseProgram, collectNodesBy } from '../utils'; 4 | import * as DotObject from 'dot-object'; 5 | import * as util from 'util'; 6 | import { PseudoClass } from './classCollector'; 7 | 8 | export class Guess { 9 | value: any[]; 10 | name: string; 11 | constructor(value: any[], name: string) { 12 | this.value = value; 13 | this.name = name; 14 | } 15 | 16 | asTypeSymbol() { 17 | return `T${this.name[0].toUpperCase() + this.name.substr(1, this.name.length - 1)}`; 18 | } 19 | 20 | asInterfaceSymbol() { 21 | return `I${this.name[0].toUpperCase() + this.name.substr(1, this.name.length - 1)}`; 22 | } 23 | 24 | toInlineString() { 25 | return `Guess<${this.value 26 | .map(v => util.inspect(v, true, Infinity)) 27 | .join(' | ') 28 | .replace(/\'(.*?)\'/gi, '$1')}>`; 29 | } 30 | 31 | toTypeString() { 32 | const interfaceStr = this.toInterfaceString(); 33 | const typeSymbol = this.asTypeSymbol(); 34 | const intSymbol = this.asInterfaceSymbol(); 35 | return `${interfaceStr + '\n' || ''}type ${typeSymbol} = Guess<${this.value.map(v => 36 | typeof v === 'object' ? `${intSymbol}` : v 37 | )}>`; 38 | } 39 | 40 | toInterfaceString() { 41 | const typings = this.value.find(v => typeof v === 'object'); 42 | const intSymbol = this.asInterfaceSymbol(); 43 | if (typings) { 44 | return `interface ${intSymbol} { 45 | ${Object.entries(typings) 46 | .map(([key, value]) => `${key}: ${util.inspect(value, true, Infinity)};`) 47 | .join('\n') 48 | .replace(/\'(.*?)\'/gi, '$1')} 49 | }`; 50 | } 51 | return null; 52 | } 53 | } 54 | 55 | export interface InferData { 56 | assignedTypes: { [propName: string]: any[] }; 57 | typings: { [propName: string]: any }; 58 | } 59 | 60 | export class TypeGuesser { 61 | static symbol = Symbol(); 62 | static Dot = new DotObject('.', true); 63 | static program: ts.Program; 64 | static knownClasses: PseudoClass[] = []; 65 | 66 | static guess(program: ts.Program, properties: Property[], classes: PseudoClass[]) { 67 | this.program = program; 68 | this.knownClasses = classes.filter(c => !c.global); 69 | this.guessRootPropertiesType(properties); 70 | this.guessPropertiesFunctionType(properties); 71 | this.guessClassPropertiesType(classes); 72 | } 73 | 74 | static guessRootPropertiesType(properties: Property[]) { 75 | this.guessFromBody(this.program.getSourceFiles()[0], properties.filter(p => !p.parentSymbol)); 76 | } 77 | 78 | static guessPropertiesFunctionType(properties: Property[]) { 79 | for (let prop of properties) { 80 | if (prop.rightNode && ts.isFunctionExpression(prop.rightNode)) { 81 | if (prop.type.match(/\((.+?)\)\s*\=\>\s*(.+)/i)) { 82 | const props: Property[] = []; 83 | const paramsWithType = RegExp.$1.split(','); 84 | const returnType = RegExp.$2.trim(); 85 | for (const p of paramsWithType) { 86 | if (p.match(/(.+)\s*\:\s*(.+)/i)) { 87 | const name = RegExp.$1.trim(); 88 | const type = RegExp.$2.trim(); 89 | props.push({ 90 | name, 91 | type, 92 | parentSymbol: null 93 | }); 94 | } 95 | } 96 | const guessedTypes = this.guessParametersType(prop.rightNode, props); 97 | let resultType = `(${props.map(p => { 98 | return `${p.name}: ${guessedTypes[p.name] || p.type}`; 99 | })}) => ${returnType}`; 100 | prop.type = resultType; 101 | } 102 | } 103 | } 104 | } 105 | 106 | static guessClassPropertiesType(classes: PseudoClass[]) { 107 | for (const _class of classes) { 108 | let props: Property[] = _class.properties.map(prop => { 109 | return { 110 | ...prop, 111 | name: 'this.' + prop.name 112 | }; 113 | }); 114 | for (const prop of _class.properties) { 115 | if (prop.rightNode && ts.isFunctionExpression(prop.rightNode)) { 116 | this.guessFromBody(prop.rightNode, props); 117 | //nodes.push(prop.rightNode); 118 | } 119 | } 120 | //console.log('props :', props); 121 | for (let i = 0; i < props.length; i++) { 122 | _class.properties[i].guessedType = props[i].guessedType; 123 | _class.properties[i].typeGuessing = props[i].typeGuessing; 124 | //console.log(classProp.name, ' ', props[i].typeGuessing); 125 | } 126 | } 127 | } 128 | 129 | static guessClassConstructorTypes(_class: PseudoClass) {} 130 | 131 | static guessParametersType(node: ts.FunctionExpression, props: Property[]) { 132 | this.guessFromBody(node.body, props); 133 | const result: { [propName: string]: string } = {}; 134 | for (const prop of props) { 135 | if (prop.guessedType) { 136 | result[prop.name] = prop.guessedType; 137 | } 138 | } 139 | return result; 140 | } 141 | 142 | static guessFromBody(body: ts.Node, properties: Property[]) { 143 | const data = this.inferDataFromCallExpressions(body, properties); 144 | const assignedTypes = data.assignedTypes; 145 | const typings = { 146 | ...this.typingFromAmbientUsage(body, properties), 147 | ...data.typings 148 | }; 149 | body.forEachChild(node => { 150 | if (ts.isVariableStatement(node)) { 151 | const declaration = node.declarationList.declarations[0]; 152 | const left = declaration.name; 153 | const right = declaration.initializer; 154 | if (!right || !left) return; 155 | const prop = properties.find(p => { 156 | const regex = new RegExp(`^${p.name}`); 157 | return !!left.getText().match(regex); 158 | }); 159 | if (prop) { 160 | this.infer(prop, left, right, { 161 | assignedTypes, 162 | typings 163 | }); 164 | } 165 | } else if (ts.isExpressionStatement(node) && ts.isBinaryExpression(node.expression)) { 166 | const { left, right } = node.expression; 167 | if (!right || !left) return; 168 | const prop = properties.find(p => { 169 | const regex = new RegExp(`^${p.name}`); 170 | return !!left.getText().match(regex); 171 | }); 172 | if (prop) { 173 | this.infer(prop, left, right, { 174 | assignedTypes, 175 | typings 176 | }); 177 | } 178 | } 179 | }); 180 | //console.log('typings :', typings); 181 | for (const prop of properties) { 182 | const typingObj = this.inferFromKnownSymbols(typings[prop.name]); 183 | const usage = {}; 184 | for (const type of assignedTypes[prop.name] || []) { 185 | let typeStr = typeof type === 'string' ? type : JSON.stringify(type); 186 | if (!usage[typeStr]) { 187 | usage[typeStr] = 0; 188 | } 189 | usage[typeStr]++; 190 | } 191 | const mostAssignedTypes = Object.entries(usage) 192 | .sort(([aKey, aValue], [bKey, bValue]) => { 193 | return (bValue as number) - (aValue as number); 194 | }) 195 | .map(([key, value]) => key); 196 | const possibleTypes = []; 197 | for (let i = 0; i < 2; i++) { 198 | if (!mostAssignedTypes[i]) break; 199 | possibleTypes[i] = mostAssignedTypes[i]; 200 | } 201 | const types = [ 202 | ...possibleTypes.filter(v => v !== 'any' && v !== '{}'), 203 | typeof typingObj === 'object' ? this.Dot.object(typingObj) : typingObj 204 | ].filter(v => { 205 | if (typeof v == 'object' && Object.keys(v).length == 0) return false; 206 | return true; 207 | }); 208 | if (types.length === 0) return; 209 | if (types.length === 1 && types[0] === prop.type) return; 210 | prop.typeGuessing = new Guess(types, prop.name); 211 | prop.guessedType = prop.typeGuessing.toInlineString(); 212 | } 213 | } 214 | 215 | static typingFromAmbientUsage(node: ts.Node, properties: Property[]) { 216 | const typings = {}; 217 | const text = node.getFullText(); 218 | for (const prop of properties) { 219 | typings[prop.name] = {}; 220 | const str = `[^\\.](${prop.name}\\..+?)\\)*?[\\s;\\(\\[]`; 221 | const regex = new RegExp(str, 'g'); 222 | while (regex.exec(text)) { 223 | const matchStr = RegExp.$1.trim().replace(prop.name + '.', ''); 224 | typings[prop.name][matchStr] = 'any'; 225 | } 226 | } 227 | return typings; 228 | } 229 | 230 | static inferDataFromCallExpressions(node: ts.Node, properties: Property[]) { 231 | const typings = {}; 232 | const assignedTypes = {}; 233 | const expressions: ts.CallExpression[] = collectNodesBy(this.program, (node: ts.Node) => 234 | ts.isCallExpression(node) 235 | ); 236 | const typeChecker = this.program.getTypeChecker(); 237 | for (const expr of expressions) { 238 | //console.log('expr :', expr); 239 | let propsAsArgs = expr.arguments.map(arg => { 240 | let props: (Property | { propName: string; str: string })[] = []; 241 | let regex = new RegExp('\\b(\\S+)\\b', 'g'); 242 | while (regex.exec(arg.getText())) { 243 | let str = RegExp.$1.trim(); 244 | if (str.match(/^(.+)\..+/i) && RegExp.$1.trim() !== 'this') { 245 | let prop = properties.find(p => str.startsWith(p.name)); 246 | if (prop) props.push({ propName: prop.name, str }); 247 | } else { 248 | let prop = properties.find(p => p.name === str); 249 | if (prop) props.push(prop); 250 | } 251 | } 252 | return props; 253 | /*if (ts.isIdentifier(arg)) { 254 | let prop = properties.find(p => p.name === arg.escapedText.toString()); 255 | return [prop] || null; 256 | } else { 257 | let identifiers = collectNodesBy( 258 | this.program, 259 | (node: ts.Node) => ts.isIdentifier(node), 260 | arg 261 | ) as ts.Identifier[]; 262 | return identifiers.map(i => properties.find(p => p.name === i.escapedText.toString())); 263 | }*/ 264 | }); 265 | let symbol = typeChecker.getSymbolAtLocation(expr.expression); 266 | if (!symbol) { 267 | if (ts.isPropertyAccessExpression(expr.expression)) { 268 | //console.log('need handling'); 269 | //let symbol = tc.getSymbolAtLocation(expr.expression.expression); 270 | continue; 271 | } 272 | continue; 273 | } 274 | let type = typeChecker.getTypeOfSymbolAtLocation(symbol, expr); 275 | let str = typeChecker.typeToString(type); 276 | if (str.match(/^\((.+)\)\s*\=\>/i)) { 277 | let args = RegExp.$1.split(','); 278 | for (let i = 0; i < propsAsArgs.length; i++) { 279 | let props = propsAsArgs[i].filter(v => !!v); 280 | let argStr = args[i]; 281 | if (argStr && argStr.match(/.+\s*\:\s*(.+)/i)) { 282 | let type = RegExp.$1.trim(); 283 | if (type !== 'any') { 284 | props.forEach((prop: any) => { 285 | if (prop.propName) { 286 | if (!typings[prop.propName]) { 287 | typings[prop.propName] = {}; 288 | } 289 | typings[prop.propName][prop.str.replace(prop.propName + '.', '')] = type; 290 | } else { 291 | if (Array.isArray(assignedTypes[prop.name])) { 292 | assignedTypes[prop.name].push(type); 293 | } else { 294 | assignedTypes[prop.name] = [type]; 295 | } 296 | } 297 | }); 298 | } 299 | } 300 | } 301 | } 302 | } 303 | return { 304 | assignedTypes, 305 | typings 306 | }; 307 | } 308 | 309 | static infer(prop: Property, left: ts.Node, right: ts.Node, data: InferData) { 310 | const { assignedTypes, typings } = data; 311 | const leftStr = left.getText(); 312 | let type: any = getTypeString(this.program.getTypeChecker(), right); 313 | /** 314 | * If an object literal is found, it is converted to an object 315 | * so that we can infer the proper type 316 | */ 317 | if (ts.isObjectLiteralExpression(right)) { 318 | type = this.inferFromKnownSymbols(objectLiteralToObject(right)); 319 | } 320 | if (leftStr.match(/^(.+?)\./i)) { 321 | if (!typings[prop.name]) { 322 | typings[prop.name] = {}; 323 | } 324 | typings[prop.name][leftStr.replace(prop.name + '.', '')] = type; 325 | } else { 326 | if (!assignedTypes[prop.name]) { 327 | assignedTypes[prop.name] = []; 328 | } 329 | assignedTypes[prop.name].push(type); 330 | } 331 | } 332 | 333 | static inferFromKnownSymbols(typings: any, root: boolean = true) { 334 | if (root) { 335 | const sym = '_$$$_'; 336 | const inferObj = {}; 337 | inferObj[sym] = typings; 338 | this.inferFromKnownSymbols(inferObj, false); 339 | return inferObj[sym]; 340 | } 341 | for (const [key, value] of Object.entries(typings)) { 342 | if (typeof value === 'object') { 343 | const keys = Object.keys(value); 344 | const matchingSymbols = this.knownClasses 345 | .map(s => { 346 | const matchingProps = s.properties.map(p => keys.includes(p.name)).filter(v => !!v); 347 | return matchingProps.length > 0 ? s : null; 348 | }) 349 | .filter(v => !!v) 350 | .sort((a, b) => { 351 | const nbrPropsA = a.properties.map(p => keys.includes(p.name)).filter(v => !!v).length; 352 | const nbrPropsB = b.properties.map(p => keys.includes(p.name)).filter(v => !!v).length; 353 | return nbrPropsB - nbrPropsA; 354 | }); 355 | if (matchingSymbols.length > 0 && keys.length) { 356 | const symb = matchingSymbols[0]; 357 | if (keys.length <= symb.properties.length) { 358 | //const partial = symb.properties.length > keys.length; 359 | //typings[key] = partial ? `Partial<${symb.name}>` : symb.name; 360 | typings[key] = symb.name; 361 | continue; 362 | } 363 | } 364 | this.inferFromKnownSymbols(value, false); 365 | } 366 | } 367 | } 368 | } 369 | 370 | // const str = ts.generateTypesForGlobal('test', { name: 'hi', val: 1, add: (a, b) => a + b }, {}); 371 | 372 | // const program = Runner.makeProgram({ 373 | // fileName: 'test.ts', 374 | // content: ` 375 | 376 | // var myVar = {}; 377 | // myVar.hello = {}; 378 | // myVar.hello.world = true; 379 | // myVar.hey = "what?"; 380 | // myVar.aVal = 5; 381 | // Math.round(myVar); 382 | 383 | // function MyClass(a, b) { 384 | // this.myProp = {}; 385 | // this.myProp.x = 0; 386 | // this.myProp.y = 0; 387 | // this.myNbr = null; 388 | // } 389 | // MyClass.prototype.myMethod = function(a, b) { 390 | // Math.round(this.myNbr); 391 | // return Math.round(a); 392 | // } 393 | // ` 394 | // }); 395 | // TypeGuesser.program = program; 396 | // const props: Property[] = [ 397 | // { 398 | // name: 'myVar', 399 | // type: 'any', 400 | // parentSymbol: null 401 | // } 402 | // ]; 403 | // //TypeGuesser.guessFromBody(program.getSourceFiles()[0].statements as any, props); 404 | // //console.log('props :', props); 405 | 406 | // TypeGuesser.guessClassPropertiesType([ 407 | // { 408 | // name: 'MyClass', 409 | // constructorArgs: [], 410 | // properties: [ 411 | // { 412 | // name: 'myProp', 413 | // parentSymbol: 'MyClass', 414 | // type: 'any' 415 | // }, 416 | // { 417 | // name: 'myNbr', 418 | // parentSymbol: 'MyClass', 419 | // type: 'any' 420 | // } 421 | // ] 422 | // } 423 | // ]); 424 | 425 | /* 426 | let result = utils.collectVariableUsage(program.getSourceFiles()[0]); 427 | let entries = result.entries(); 428 | let tab; 429 | while(tab = entries.next().value) { 430 | let [key, value]: [ts.Identifier, VariableInfo] = tab; 431 | if(key.escapedText.toString() === 'MyClass') { 432 | console.log("---"); 433 | //console.log('key :', key); 434 | console.log('value :', value); 435 | } 436 | } 437 | */ 438 | -------------------------------------------------------------------------------- /.rpt2_cache/rpt2_3ddc89fd4e92ec9a62f439bf8d222a2bb638ba03/code/cache/ce51734f7014075100857b852c35f76b8e3a9a2b: -------------------------------------------------------------------------------- 1 | {"code":"import * as ts from 'typescript';\r\nimport * as Beautify from 'js-beautify';\r\nvar _namespace = 'MyNamespace';\r\nexport var setNamespace = function (name) { return (_namespace = name); };\r\nexport function createProgram(files, compilerOptions) {\r\n var tsConfigJson = ts.parseConfigFileTextToJson('tsconfig.json', compilerOptions\r\n ? JSON.stringify(compilerOptions)\r\n : \"{\\n \\\"compilerOptions\\\": {\\n \\\"target\\\": \\\"es2018\\\", \\n \\\"module\\\": \\\"commonjs\\\", \\n \\\"lib\\\": [\\\"es2018\\\"],\\n \\\"rootDir\\\": \\\".\\\",\\n \\\"strict\\\": false, \\n \\\"esModuleInterop\\\": true,\\n \\\"noImplicitAny\\\": true,\\n \\\"allowJs\\\": true\\n }\\n \");\r\n var _a = ts.convertCompilerOptionsFromJson(tsConfigJson.config.compilerOptions, '.'), options = _a.options, errors = _a.errors;\r\n if (errors.length) {\r\n throw errors;\r\n }\r\n var compilerHost = ts.createCompilerHost(options);\r\n compilerHost.getSourceFile = function (fileName, languageVersion, onError, shouldCreateNewSourceFile) {\r\n var file = files.find(function (f) { return f.fileName === fileName; });\r\n if (!file)\r\n return undefined;\r\n file.sourceFile =\r\n file.sourceFile || ts.createSourceFile(fileName, file.content, ts.ScriptTarget.ES2015, true);\r\n return file.sourceFile;\r\n };\r\n // in order to typechecker to work we need to implement the following method, the following implementation is enough:\r\n compilerHost.resolveTypeReferenceDirectives = function (typeReferenceDirectiveNames, containingFile) {\r\n return [];\r\n };\r\n return ts.createProgram(files.map(function (f) { return f.fileName; }), options, compilerHost);\r\n}\r\n// ---------------\r\nvar EQUAL_TOKEN = 59;\r\nvar THIS_TOKEN = 100;\r\nexport function collectProperties(program) {\r\n var properties = [];\r\n var localVariables = [];\r\n var checker = program.getTypeChecker();\r\n function visit(node, parentSymbol, fromStatic) {\r\n if (parentSymbol === void 0) { parentSymbol = null; }\r\n if (fromStatic === void 0) { fromStatic = null; }\r\n if (ts.isVariableStatement(node)) {\r\n for (var _i = 0, _a = node.declarationList.declarations; _i < _a.length; _i++) {\r\n var declaration = _a[_i];\r\n if (ts.isIdentifier(declaration.name))\r\n localVariables.push(declaration.name.escapedText.toString());\r\n }\r\n }\r\n else if (ts.isFunctionDeclaration(node)) {\r\n var name = node.name.escapedText.toString();\r\n var statements = node.body.statements;\r\n for (var _b = 0, statements_1 = statements; _b < statements_1.length; _b++) {\r\n var statement = statements_1[_b];\r\n visit(statement, name, false);\r\n }\r\n }\r\n else if (ts.isExpressionStatement(node) &&\r\n ts.isBinaryExpression(node.expression) &&\r\n node.expression.operatorToken.kind === EQUAL_TOKEN) {\r\n if (ts.isPropertyAccessExpression(node.expression.left)) {\r\n var expr = node.expression.left;\r\n var symbol = void 0, name = void 0, type = void 0, _static = void 0;\r\n if (expr.expression.kind === THIS_TOKEN) {\r\n // this.a = 10\r\n symbol = parentSymbol;\r\n _static = fromStatic;\r\n name = expr.name.escapedText.toString();\r\n type = getTypeString(checker, node.expression.right);\r\n }\r\n else if (ts.isIdentifier(expr.expression)) {\r\n // Global.a = 10\r\n symbol = expr.expression.escapedText.toString();\r\n _static = true;\r\n name = expr.name.escapedText.toString();\r\n type = getTypeString(checker, node.expression.right);\r\n }\r\n else if (ts.isPropertyAccessExpression(expr.expression) &&\r\n expr.expression.name.kind === 72 /* prototype */ &&\r\n ts.isIdentifier(expr.expression.expression)) {\r\n // Global.prototype.a = 10\r\n symbol = expr.expression.expression.escapedText.toString();\r\n name = expr.name.escapedText.toString();\r\n type = getTypeString(checker, node.expression.right);\r\n _static = false;\r\n }\r\n // Found\r\n if (symbol) {\r\n var doc = void 0;\r\n if (node.jsDoc) {\r\n doc = node.jsDoc[node.jsDoc.length - 1];\r\n type = extractJsDocType(doc);\r\n }\r\n var property_1 = {\r\n name: name,\r\n parentSymbol: symbol,\r\n static: _static,\r\n type: type,\r\n jsDoc: doc\r\n };\r\n var exist = properties.find(function (p) {\r\n return p.name === property_1.name &&\r\n p.parentSymbol === property_1.parentSymbol &&\r\n p.static === property_1.static;\r\n });\r\n var forbidden = ['constructor', 'prototype'];\r\n var localCheck = expr.expression.kind === THIS_TOKEN\r\n ? true\r\n : !localVariables.includes(name) && !localVariables.includes(symbol);\r\n if (!exist && !forbidden.includes(name) && localCheck) {\r\n properties.push(property_1);\r\n if (ts.isFunctionExpression(node.expression.right)) {\r\n var statements = node.expression.right.body.statements;\r\n for (var _c = 0, statements_2 = statements; _c < statements_2.length; _c++) {\r\n var statement = statements_2[_c];\r\n visit(statement, symbol, _static);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n traverseProgram(program, visit);\r\n return properties;\r\n}\r\nexport function makePseudoClasses(program, properties) {\r\n var classes = [];\r\n var checker = program.getTypeChecker();\r\n function visit(node) {\r\n if (ts.isFunctionDeclaration(node)) {\r\n var name_1 = node.name.escapedText.toString();\r\n var constructorArgs = makeVariablesFromParameters(checker, Array.from(node.parameters || []));\r\n var jsDoc = node.jsDoc;\r\n var lastJsDoc = jsDoc ? jsDoc[jsDoc.length - 1] : null;\r\n // - Look for call of the true constructor\r\n var constructorProperty = null;\r\n for (var _i = 0, _a = node.body.statements; _i < _a.length; _i++) {\r\n var statement = _a[_i];\r\n if (ts.isExpressionStatement(statement)) {\r\n var str = statement.getText();\r\n if (str.match(/this\\.(.+)\\.apply\\(this,/i)) {\r\n constructorProperty = properties.find(function (p) { return p.parentSymbol === name_1 && p.name === RegExp.$1; });\r\n if (jsDoc)\r\n constructorProperty.type = extractJsDocType(lastJsDoc, constructorProperty.type);\r\n }\r\n }\r\n }\r\n var constructorSignature = null;\r\n if (!constructorProperty) {\r\n constructorSignature = jsDoc\r\n ? extractJsDocType(lastJsDoc).replace(/\\=\\>.+/i, '')\r\n : \"(\" + constructorArgs.map(function (p) { return p.name + \": \" + p.type; }).join(', ') + \")\";\r\n }\r\n classes.push({\r\n name: name_1,\r\n constructorArgs: constructorArgs,\r\n properties: properties.map(function (p) { return (p.parentSymbol === name_1 ? p : null); }).filter(function (v) { return !!v; }),\r\n constructorProperty: constructorProperty,\r\n jsDoc: jsDoc ? lastJsDoc : null,\r\n constructorSignature: constructorSignature\r\n });\r\n }\r\n else if (ts.isExpressionStatement(node)) {\r\n var str = node.getText();\r\n if (str.match(/(.+)\\.prototype\\s*\\=\\s*Object\\.create\\((.+)\\.prototype\\)/i)) {\r\n // -\r\n var _class = classes.find(function (c) { return c.name === RegExp.$1; });\r\n if (!_class) {\r\n _class = {\r\n constructorArgs: [],\r\n global: true,\r\n name: RegExp.$1,\r\n properties: [],\r\n extends: RegExp.$2\r\n };\r\n classes.push(_class);\r\n }\r\n else {\r\n _class.extends = RegExp.$2;\r\n }\r\n }\r\n else if (str.match(/Object\\.definePropert.+\\((.+?),/i) && ts.isCallExpression(node.expression)) {\r\n // - Extract properties from getter/setter\r\n var properties_2 = [];\r\n var value = RegExp.$1;\r\n var symbol_1, _static = void 0;\r\n if (value.match(/(.+)\\.prototype/)) {\r\n symbol_1 = RegExp.$1;\r\n _static = false;\r\n }\r\n else {\r\n symbol_1 = value;\r\n _static = true;\r\n }\r\n var arg = node.expression.arguments[1];\r\n if (ts.isStringLiteral(arg)) {\r\n var jsDoc = node.jsDoc || null;\r\n var lastJsDoc = jsDoc ? jsDoc[jsDoc.length - 1] : null;\r\n var objArg = node.expression.arguments[2];\r\n if (ts.isObjectLiteralExpression(objArg)) {\r\n var result = makePropertyFromObjectLiteral(checker, objArg, lastJsDoc);\r\n properties_2.push({\r\n name: arg.text,\r\n parentSymbol: symbol_1,\r\n static: _static,\r\n type: result.type,\r\n readonly: result.readonly\r\n });\r\n }\r\n }\r\n else if (ts.isObjectLiteralExpression(arg)) {\r\n for (var _b = 0, _c = arg.properties; _b < _c.length; _b++) {\r\n var prop = _c[_b];\r\n if (ts.isPropertyAssignment(prop)) {\r\n var jsDoc = prop.jsDoc || null;\r\n var lastJsDoc = jsDoc ? jsDoc[jsDoc.length - 1] : null;\r\n var name = prop.name.escapedText.toString();\r\n if (ts.isObjectLiteralExpression(prop.initializer)) {\r\n var result = makePropertyFromObjectLiteral(checker, prop.initializer, lastJsDoc);\r\n properties_2.push({\r\n name: name,\r\n parentSymbol: symbol_1,\r\n readonly: result.readonly,\r\n static: _static,\r\n type: result.type,\r\n jsDoc: jsDoc\r\n });\r\n }\r\n else {\r\n properties_2.push({\r\n name: name,\r\n parentSymbol: symbol_1,\r\n readonly: true,\r\n static: _static,\r\n type: getTypeString(checker, prop.initializer),\r\n jsDoc: jsDoc\r\n });\r\n }\r\n }\r\n }\r\n }\r\n var _class = classes.find(function (c) { return c.name === symbol_1; });\r\n if (!_class) {\r\n _class = {\r\n constructorArgs: [],\r\n global: true,\r\n name: symbol_1,\r\n properties: properties_2.slice()\r\n };\r\n classes.push(_class);\r\n }\r\n else {\r\n _class.properties = [].concat(_class.properties, properties_2);\r\n }\r\n }\r\n }\r\n //ts.forEachChild(node, visit);\r\n }\r\n traverseProgram(program, visit);\r\n var _loop_1 = function (p) {\r\n var _class = classes.find(function (c) { return c.name === p.parentSymbol; });\r\n if (!_class) {\r\n classes.push({\r\n constructorArgs: [],\r\n constructorProperty: null,\r\n global: true,\r\n name: p.parentSymbol,\r\n properties: properties\r\n .map(function (_p) { return (_p.parentSymbol === p.parentSymbol ? _p : null); })\r\n .filter(function (v) { return !!v; })\r\n });\r\n }\r\n };\r\n // - Find out global pseudo classes\r\n for (var _i = 0, properties_1 = properties; _i < properties_1.length; _i++) {\r\n var p = properties_1[_i];\r\n _loop_1(p);\r\n }\r\n return classes;\r\n}\r\nexport function makePropertyFromObjectLiteral(checker, expr, jsDoc) {\r\n var readonly = true;\r\n var type = 'any';\r\n for (var _i = 0, _a = expr.properties; _i < _a.length; _i++) {\r\n var prop = _a[_i];\r\n if (ts.isPropertyAssignment(prop)) {\r\n if (prop.name.escapedText.toString() === 'set') {\r\n readonly = false;\r\n }\r\n else if (prop.name.escapedText.toString() === 'get') {\r\n type = getTypeString(checker, prop.initializer);\r\n if (type.match(/\\=\\>(.+)/i)) {\r\n type = RegExp.$1.trim();\r\n }\r\n if (jsDoc) {\r\n type = extractJsDocType(jsDoc, type);\r\n }\r\n }\r\n else if (prop.name.escapedText.toString() === 'value') {\r\n type = getTypeString(checker, prop.initializer);\r\n if (jsDoc) {\r\n type = extractJsDocType(jsDoc, type);\r\n }\r\n }\r\n }\r\n }\r\n return {\r\n readonly: readonly,\r\n type: type\r\n };\r\n}\r\nexport function makeVariablesFromParameters(checker, params) {\r\n var variables = [];\r\n for (var _i = 0, params_1 = params; _i < params_1.length; _i++) {\r\n var param = params_1[_i];\r\n variables.push({\r\n name: param.name.escapedText.toString(),\r\n type: getTypeString(checker, param)\r\n });\r\n }\r\n return variables;\r\n}\r\nexport function traverseProgram(program, callback) {\r\n for (var _i = 0, _a = program.getSourceFiles(); _i < _a.length; _i++) {\r\n var sourceFile = _a[_i];\r\n if (!sourceFile.isDeclarationFile) {\r\n // Walk the tree to search for classes\r\n ts.forEachChild(sourceFile, callback);\r\n }\r\n }\r\n}\r\nexport function getTypeString(checker, node) {\r\n var type = checker.getTypeAtLocation(node);\r\n if (node.getText().match(/^\\s*new\\s+(.+?)\\(.*\\)/i)) {\r\n return RegExp.$1.trim();\r\n }\r\n if (type.isLiteral()) {\r\n type = checker.getBaseTypeOfLiteralType(type);\r\n }\r\n var value = checker.typeToString(type, node, ts.TypeFormatFlags.NoTruncation);\r\n if (value === 'null')\r\n return 'any';\r\n if (value === 'false' || value === 'true')\r\n return 'boolean';\r\n return value;\r\n}\r\nexport function convertJsDocType(type) {\r\n if (type.includes('|')) {\r\n return type\r\n .split('|')\r\n .map(function (v) { return convertJsDocType(v); })\r\n .join(' | ');\r\n }\r\n type =\r\n {\r\n String: 'string',\r\n Number: 'number',\r\n Array: 'any[]',\r\n Any: 'any',\r\n Boolean: 'boolean',\r\n '*': 'any'\r\n }[type] || type;\r\n return type;\r\n}\r\nexport function extractJsDocType(doc, currentType) {\r\n if (currentType === void 0) { currentType = ''; }\r\n var params = [];\r\n var returnType = 'any';\r\n var absoluteType;\r\n var extractType = function (tag) {\r\n var type;\r\n if (tag.typeExpression == null) {\r\n type = 'any';\r\n }\r\n else {\r\n type = tag.typeExpression.type.getText();\r\n // if (!(tag.typeExpression.type as any).typeName) {\r\n // type = tag.typeExpression.type.getText();\r\n // } else {\r\n // type = (tag.typeExpression.type as any).typeName.escapedText.toString();\r\n // }\r\n }\r\n return type;\r\n };\r\n var autoGeneratedArgs = 0;\r\n for (var _i = 0, _a = Array.from(doc.tags || []); _i < _a.length; _i++) {\r\n var tag = _a[_i];\r\n if (ts.isJSDocTypeTag(tag)) {\r\n absoluteType = extractType(tag);\r\n return convertJsDocType(absoluteType);\r\n }\r\n else if (ts.isJSDocParameterTag(tag)) {\r\n var type = extractType(tag);\r\n var name = tag.name.escapedText.toString();\r\n if (!name) {\r\n var commentArg = tag.comment.split(' ')[0];\r\n if (commentArg.startsWith('...')) {\r\n name = commentArg;\r\n }\r\n else {\r\n name = \"arg\" + Number(autoGeneratedArgs++);\r\n }\r\n }\r\n params.push({ name: name, type: convertJsDocType(type) });\r\n }\r\n else if (ts.isJSDocReturnTag(tag)) {\r\n var type = extractType(tag);\r\n returnType = convertJsDocType(type);\r\n }\r\n }\r\n if (currentType.match(/\\(.+\\)\\s*\\=\\>\\s*(.+)/)) {\r\n returnType = returnType || RegExp.$2.trim();\r\n }\r\n return \"(\" + params.map(function (p) { return p.name + \": \" + p.type; }).join(', ') + \") => \" + returnType;\r\n}\r\nexport function makeDTS(classes) {\r\n var globals = classes.map(function (c) { return (c.global ? c : null); }).filter(function (v) { return !!v; });\r\n var text = '';\r\n if (globals.length > 0) {\r\n text = \"declare global {\\n \" + globals\r\n .map(function (c) { return \"interface \" + c.name + \" {\\n \" + c.properties.map(function (p) { return propertyToString(p).replace('static', ''); }).join('\\n') + \"\\n }\"; })\r\n .join('\\n') + \"\\n }\\n \";\r\n }\r\n var normal = classes.filter(function (c) { return !c.global; });\r\n text += \"export declare namespace \" + _namespace + \"{\\n \" + normal.map(function (c) { return classToString(c); }).join('\\n') + \"\\n }\";\r\n return Beautify.js(text, {});\r\n}\r\nexport function propertyToString(property) {\r\n return (\"\" + (property.jsDoc && property.jsDoc.getText ? property.jsDoc.getText() + '\\n' : '') + (property.static ? 'static ' : '') + (property.readonly ? 'readonly ' : '') + property.name + \": \" + property.type + \";\").trim();\r\n}\r\nexport function classToString(_class) {\r\n var constructorDoc = _class.jsDoc && _class.jsDoc.getText ? _class.jsDoc.getText() : '';\r\n return \"class \" + _class.name + (_class.extends ? \" extends \" + _class.extends : '') + \" {\" + constructorDoc + \"\\n \" + (_class.constructorProperty\r\n ? \"new \" + _class.constructorProperty.type.replace(/ \\=\\>.+/i, '') + \";\\n\"\r\n : \"new \" + _class.constructorSignature + \";\\n\") + _class.properties\r\n .filter(function (p) { return p !== _class.constructorProperty; })\r\n .map(function (p) { return propertyToString(p); })\r\n .join('\\n\\n') + \"\\n }\";\r\n}\r\n","dts":{"name":"/media/cyriac/0CC5166B0CC5166B/Work/es5-to-dts/generator.d.ts","writeByteOrderMark":false,"text":"import * as ts from 'typescript';\r\nexport declare const setNamespace: (name: string) => string;\r\nexport interface File {\r\n fileName: string;\r\n content: string;\r\n sourceFile?: ts.SourceFile;\r\n}\r\nexport declare function createProgram(files: File[], compilerOptions?: ts.CompilerOptions): ts.Program;\r\nexport interface Variable {\r\n name: string;\r\n type: string;\r\n}\r\nexport interface Property {\r\n parentSymbol: string;\r\n name: string;\r\n type: string;\r\n static?: boolean;\r\n readonly?: boolean;\r\n jsDoc?: ts.JSDoc;\r\n}\r\nexport declare function collectProperties(program: ts.Program): Property[];\r\ninterface PseudoClass {\r\n name: string;\r\n constructorArgs: Variable[];\r\n properties: Property[];\r\n global?: boolean;\r\n constructorProperty?: Property;\r\n constructorSignature?: string;\r\n extends?: string;\r\n jsDoc?: ts.JSDoc;\r\n}\r\nexport declare function makePseudoClasses(program: ts.Program, properties: Property[]): PseudoClass[];\r\nexport declare function makePropertyFromObjectLiteral(checker: ts.TypeChecker, expr: ts.ObjectLiteralExpression, jsDoc: ts.JSDoc): {\r\n readonly: boolean;\r\n type: string;\r\n};\r\nexport declare function makeVariablesFromParameters(checker: ts.TypeChecker, params: ts.ParameterDeclaration[]): Variable[];\r\nexport declare function traverseProgram(program: ts.Program, callback: (node: ts.Node) => any): void;\r\nexport declare function getTypeString(checker: ts.TypeChecker, node: ts.Node): string;\r\nexport declare function convertJsDocType(type: string): any;\r\nexport declare function extractJsDocType(doc: ts.JSDoc, currentType?: string): any;\r\nexport declare function makeDTS(classes: PseudoClass[]): any;\r\nexport declare function propertyToString(property: Property): string;\r\nexport declare function classToString(_class: PseudoClass): string;\r\nexport {};\r\n"}} 2 | -------------------------------------------------------------------------------- /dist/script.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } 5 | 6 | var fs = require('fs'); 7 | var path = require('path'); 8 | var ts = require('typescript'); 9 | var util = require('util'); 10 | var chalk = _interopDefault(require('chalk')); 11 | var ora = _interopDefault(require('ora')); 12 | var Readline = require('readline'); 13 | var DotObject = require('dot-object'); 14 | 15 | function createProgram(files, compilerOptions) { 16 | var tsConfigJson = ts.parseConfigFileTextToJson('tsconfig.json', compilerOptions 17 | ? JSON.stringify(compilerOptions) 18 | : "{\n \"compilerOptions\": {\n \"target\": \"es2018\", \n \"module\": \"commonjs\",\n \"rootDir\": \".\",\n \"strict\": false, \n \"esModuleInterop\": true,\n \"allowJs\": true\n }\n "); 19 | var _a = ts.convertCompilerOptionsFromJson(tsConfigJson.config.compilerOptions, '.'), options = _a.options, errors = _a.errors; 20 | if (errors.length) { 21 | throw errors; 22 | } 23 | var compilerHost = ts.createCompilerHost(options); 24 | compilerHost.getSourceFile = function (fileName, languageVersion, onError, shouldCreateNewSourceFile) { 25 | var file = files.find(function (f) { return f.fileName === fileName; }); 26 | if (!file) 27 | return undefined; 28 | file.sourceFile = 29 | file.sourceFile || ts.createSourceFile(fileName, file.content, ts.ScriptTarget.ES2015, true); 30 | return file.sourceFile; 31 | }; 32 | compilerHost.resolveTypeReferenceDirectives = function (typeReferenceDirectiveNames, containingFile) { 33 | return []; 34 | }; 35 | return ts.createProgram(files.map(function (f) { return f.fileName; }), options, compilerHost); 36 | } 37 | // --------------- 38 | var EQUAL_TOKEN = 59; 39 | var THIS_TOKEN = 100; 40 | function makePropertyFromObjectLiteral(checker, expr, jsDoc) { 41 | var readonly = true; 42 | var type = 'any'; 43 | for (var _i = 0, _a = expr.properties; _i < _a.length; _i++) { 44 | var prop = _a[_i]; 45 | if (ts.isPropertyAssignment(prop)) { 46 | if (prop.name.escapedText.toString() === 'set') { 47 | readonly = false; 48 | } 49 | else if (prop.name.escapedText.toString() === 'get') { 50 | type = getTypeString(checker, prop.initializer); 51 | if (type.match(/\=\>(.+)/i)) { 52 | type = RegExp.$1.trim(); 53 | } 54 | if (jsDoc) { 55 | type = extractJsDocType(jsDoc, type); 56 | } 57 | } 58 | else if (prop.name.escapedText.toString() === 'value') { 59 | type = getTypeString(checker, prop.initializer); 60 | if (jsDoc) { 61 | type = extractJsDocType(jsDoc, type); 62 | } 63 | } 64 | } 65 | } 66 | return { 67 | readonly: readonly, 68 | type: type 69 | }; 70 | } 71 | function makeVariablesFromParameters(checker, params) { 72 | var variables = []; 73 | for (var _i = 0, params_1 = params; _i < params_1.length; _i++) { 74 | var param = params_1[_i]; 75 | variables.push({ 76 | name: param.name.escapedText.toString(), 77 | type: getTypeString(checker, param) 78 | }); 79 | } 80 | return variables; 81 | } 82 | function traverseProgram(program, callback) { 83 | for (var _i = 0, _a = program.getSourceFiles(); _i < _a.length; _i++) { 84 | var sourceFile = _a[_i]; 85 | if (!sourceFile.isDeclarationFile) { 86 | ts.forEachChild(sourceFile, callback); 87 | } 88 | } 89 | } 90 | function getTypeString(checker, node) { 91 | var type = checker.getTypeAtLocation(node); 92 | if (node.getText().match(/^\s*new\s+(.+?)\(.*\)/i)) { 93 | return RegExp.$1.trim(); 94 | } 95 | if (type.isLiteral()) { 96 | type = checker.getBaseTypeOfLiteralType(type); 97 | } 98 | var value = checker.typeToString(type, node, ts.TypeFormatFlags.NoTruncation); 99 | if (value === 'null') 100 | return 'any'; 101 | if (value === 'false' || value === 'true') 102 | return 'boolean'; 103 | return value; 104 | } 105 | function convertJsDocType(type) { 106 | if (type.includes('|')) { 107 | return type 108 | .split('|') 109 | .map(function (v) { return convertJsDocType(v); }) 110 | .join(' | '); 111 | } 112 | type = 113 | { 114 | String: 'string', 115 | Number: 'number', 116 | Array: 'any[]', 117 | Any: 'any', 118 | Boolean: 'boolean', 119 | '*': 'any' 120 | }[type] || type; 121 | return type; 122 | } 123 | function extractJsDocType(doc, currentType) { 124 | if (currentType === void 0) { currentType = ''; } 125 | var params = []; 126 | var returnType = 'any'; 127 | var absoluteType; 128 | var extractType = function (tag) { 129 | var type; 130 | if (tag.typeExpression == null) { 131 | type = 'any'; 132 | } 133 | else { 134 | type = tag.typeExpression.type.getText(); 135 | // if (!(tag.typeExpression.type as any).typeName) { 136 | // type = tag.typeExpression.type.getText(); 137 | // } else { 138 | // type = (tag.typeExpression.type as any).typeName.escapedText.toString(); 139 | // } 140 | } 141 | return type; 142 | }; 143 | var autoGeneratedArgs = 0; 144 | for (var _i = 0, _a = Array.from(doc.tags || []); _i < _a.length; _i++) { 145 | var tag = _a[_i]; 146 | if (ts.isJSDocTypeTag(tag)) { 147 | absoluteType = extractType(tag); 148 | return convertJsDocType(absoluteType); 149 | } 150 | else if (ts.isJSDocParameterTag(tag)) { 151 | var type = extractType(tag); 152 | var name = tag.name.escapedText.toString(); 153 | if (!name) { 154 | var commentArg = tag.comment.split(' ')[0]; 155 | if (commentArg.startsWith('...')) { 156 | name = commentArg; 157 | } 158 | else { 159 | name = "arg" + Number(autoGeneratedArgs++); 160 | } 161 | } 162 | params.push({ name: name, type: convertJsDocType(type) }); 163 | } 164 | else if (ts.isJSDocReturnTag(tag)) { 165 | var type = extractType(tag); 166 | returnType = convertJsDocType(type); 167 | } 168 | } 169 | if (currentType.match(/\(.+\)\s*\=\>\s*(.+)/)) { 170 | returnType = returnType || RegExp.$2.trim(); 171 | } 172 | return "(" + params.map(function (p) { return p.name + ": " + p.type; }).join(', ') + ") => " + returnType; 173 | } 174 | function objectLiteralToObject(expr) { 175 | if (expr.getText().length < 2) 176 | return {}; 177 | var str = expr.getText().replace(/(^\{|\}$)/ig, ''); 178 | var props = str.split(/(;|,)/); 179 | var obj = {}; 180 | var _loop_1 = function (p) { 181 | if (p.match(/(.+)\:(.+)/)) { 182 | var key = RegExp.$1.trim(); 183 | var value_1 = RegExp.$2.trim(); 184 | if (value_1.match(/\{.+\}/)) { 185 | value_1 = objectLiteralToObject({ 186 | getText: function () { return value_1; } 187 | }); 188 | } 189 | obj[key] = value_1; 190 | } 191 | }; 192 | for (var _i = 0, props_1 = props; _i < props_1.length; _i++) { 193 | var p = props_1[_i]; 194 | _loop_1(p); 195 | } 196 | return obj; 197 | } 198 | function collectNodesBy(program, constraint, startingNode) { 199 | var nodes = []; 200 | var visit = function (node) { 201 | if (constraint(node)) { 202 | nodes.push(node); 203 | } 204 | else { 205 | node.forEachChild(visit); 206 | } 207 | }; 208 | if (startingNode) { 209 | startingNode.forEachChild(visit); 210 | } 211 | else { 212 | traverseProgram(program, visit); 213 | } 214 | return nodes; 215 | } 216 | 217 | var ErrorLogger = /** @class */ (function () { 218 | function ErrorLogger() { 219 | } 220 | ErrorLogger.add = function (node, error) { 221 | this.logs.push({ node: node, error: error }); 222 | }; 223 | ErrorLogger.store = function () { 224 | this.archive = [].concat(this.archive, this.logs); 225 | this.logs = []; 226 | }; 227 | ErrorLogger.display = function (program) { 228 | var srcFile = program.getSourceFiles()[0]; 229 | return "" + this.archive 230 | .map(function (l, i) { 231 | var _a = srcFile.getLineAndCharacterOfPosition(l.node.getStart()), line = _a.line, character = _a.character; 232 | return chalk.bgRed("Error " + (i + 1) + ":") + " " + l.error.message + "\nSourcefile " + chalk.yellow("line " + line) + ", " + chalk.yellow("character " + character) + "\n" + chalk.bold('Stacktrace:') + " " + l.error.stack + "\n" + chalk.bold('Node:') + " " + util.inspect(l.node); 233 | }) 234 | .join('\n\n'); 235 | }; 236 | ErrorLogger.logs = []; 237 | ErrorLogger.archive = []; 238 | return ErrorLogger; 239 | }()); 240 | 241 | var PropertyCollector = /** @class */ (function () { 242 | function PropertyCollector() { 243 | this.properties = []; 244 | this.localVariables = []; 245 | this.state = {}; 246 | } 247 | PropertyCollector.prototype.collect = function (program) { 248 | this.program = program; 249 | this.checker = program.getTypeChecker(); 250 | traverseProgram(program, this._visit.bind(this)); 251 | return this.properties; 252 | }; 253 | PropertyCollector.prototype._visit = function (node, parentNode) { 254 | if (parentNode === void 0) { parentNode = null; } 255 | try { 256 | if (ts.isVariableStatement(node)) { 257 | this._onVariableStatement(node, parentNode); 258 | } 259 | else if (ts.isFunctionDeclaration(node)) { 260 | this._onFunctionDeclaration(node); 261 | } 262 | else if (ts.isExpressionStatement(node) && 263 | ts.isBinaryExpression(node.expression) && 264 | node.expression.operatorToken.kind === EQUAL_TOKEN) { 265 | if (ts.isPropertyAccessExpression(node.expression.left)) { 266 | this._onExpressionStatement(node, node.expression, node.expression.left); 267 | } 268 | } 269 | } 270 | catch (error) { 271 | ErrorLogger.add(node, error); 272 | } 273 | }; 274 | PropertyCollector.prototype._onVariableStatement = function (node, parentNode) { 275 | for (var _i = 0, _a = node.declarationList.declarations; _i < _a.length; _i++) { 276 | var declaration = _a[_i]; 277 | if (declaration.initializer) { 278 | if (ts.isFunctionExpression(declaration.initializer) || 279 | getTypeString(this.checker, declaration.initializer).match(/^\(.+\)\s*\=\>/)) 280 | continue; 281 | } 282 | if (ts.isIdentifier(declaration.name)) { 283 | if (Runner.options.collectRootVariables && !parentNode) { 284 | this.properties.push({ 285 | name: declaration.name.escapedText.toString(), 286 | type: declaration.initializer ? getTypeString(this.checker, declaration.initializer) : 'any', 287 | rightNode: declaration.initializer, 288 | parentSymbol: null 289 | }); 290 | } 291 | else { 292 | this.localVariables.push(declaration.name.escapedText.toString()); 293 | } 294 | } 295 | } 296 | }; 297 | PropertyCollector.prototype._onFunctionDeclaration = function (node) { 298 | var name = node.name.escapedText.toString(); 299 | var statements = node.body.statements; 300 | for (var _i = 0, statements_1 = statements; _i < statements_1.length; _i++) { 301 | var statement = statements_1[_i]; 302 | this.state.parentSymbol = name; 303 | this.state.fromStatic = false; 304 | this._visit(statement, node); 305 | } 306 | }; 307 | PropertyCollector.prototype._onExpressionStatement = function (node, expr, left) { 308 | var symbol, name, type, _static; 309 | if (left.expression.kind === THIS_TOKEN) { 310 | // this.a = 10 311 | symbol = this.state.parentSymbol; 312 | _static = this.state.fromStatic; 313 | name = left.name.escapedText.toString(); 314 | type = getTypeString(this.checker, expr.right); 315 | } 316 | else if (ts.isIdentifier(left.expression)) { 317 | // Global.a = 10 318 | symbol = left.expression.escapedText.toString(); 319 | _static = true; 320 | name = left.name.escapedText.toString(); 321 | type = getTypeString(this.checker, expr.right); 322 | } 323 | else if (ts.isPropertyAccessExpression(left.expression) && 324 | left.expression.name.kind === 72 /* prototype */ && 325 | ts.isIdentifier(left.expression.expression)) { 326 | // Global.prototype.a = 10 327 | symbol = left.expression.expression.escapedText.toString(); 328 | name = left.name.escapedText.toString(); 329 | type = getTypeString(this.checker, expr.right); 330 | _static = false; 331 | } 332 | // Found 333 | if (symbol) { 334 | var doc = void 0; 335 | if (node.jsDoc) { 336 | doc = node.jsDoc[node.jsDoc.length - 1]; 337 | type = extractJsDocType(doc); 338 | } 339 | var property_1 = { 340 | name: name, 341 | parentSymbol: symbol, 342 | static: _static, 343 | type: type, 344 | jsDoc: doc, 345 | linkedToFunction: ts.isIdentifier(expr.right) ? expr.right.escapedText.toString() : null, 346 | rightNode: expr.right 347 | }; 348 | var exist = this.properties.find(function (p) { 349 | return p.name === property_1.name && 350 | p.parentSymbol === property_1.parentSymbol && 351 | p.static === property_1.static; 352 | }); 353 | var forbidden = ['constructor', 'prototype']; 354 | var localCheck = left.expression.kind === THIS_TOKEN 355 | ? true 356 | : !this.localVariables.includes(name) && !this.localVariables.includes(symbol); 357 | if (!exist && !forbidden.includes(name) && localCheck) { 358 | this.properties.push(property_1); 359 | if (ts.isFunctionExpression(expr.right)) { 360 | var statements = expr.right.body.statements; 361 | for (var _i = 0, statements_2 = statements; _i < statements_2.length; _i++) { 362 | var statement = statements_2[_i]; 363 | this.state.parentSymbol = symbol; 364 | this.state.fromStatic = _static; 365 | this._visit(statement); 366 | } 367 | } 368 | } 369 | } 370 | }; 371 | return PropertyCollector; 372 | }()); 373 | 374 | /*! ***************************************************************************** 375 | Copyright (c) Microsoft Corporation. All rights reserved. 376 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 377 | this file except in compliance with the License. You may obtain a copy of the 378 | License at http://www.apache.org/licenses/LICENSE-2.0 379 | 380 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 381 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 382 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 383 | MERCHANTABLITY OR NON-INFRINGEMENT. 384 | 385 | See the Apache Version 2.0 License for specific language governing permissions 386 | and limitations under the License. 387 | ***************************************************************************** */ 388 | 389 | var __assign = function() { 390 | __assign = Object.assign || function __assign(t) { 391 | for (var s, i = 1, n = arguments.length; i < n; i++) { 392 | s = arguments[i]; 393 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; 394 | } 395 | return t; 396 | }; 397 | return __assign.apply(this, arguments); 398 | }; 399 | 400 | var ClassCollector = /** @class */ (function () { 401 | function ClassCollector() { 402 | this.properties = []; 403 | this.classes = []; 404 | this.localVariables = []; 405 | this.state = {}; 406 | } 407 | ClassCollector.prototype.collect = function (program, properties) { 408 | this.program = program; 409 | this.checker = program.getTypeChecker(); 410 | this.properties = properties; 411 | traverseProgram(program, this._visit.bind(this)); 412 | var _loop_1 = function (p) { 413 | if (!p.parentSymbol) 414 | return "continue"; 415 | var _class = this_1.classes.find(function (c) { return c.name === p.parentSymbol; }); 416 | if (!_class) { 417 | var parentAsProp = properties.find(function (_p) { return _p.name === p.parentSymbol; }); 418 | if (parentAsProp && !parentAsProp.parentSymbol) 419 | return "continue"; 420 | this_1.classes.push({ 421 | constructorArgs: [], 422 | constructorProperty: null, 423 | global: true, 424 | name: p.parentSymbol, 425 | properties: properties 426 | .map(function (_p) { return (_p.parentSymbol === p.parentSymbol ? _p : null); }) 427 | .filter(function (v) { return !!v; }) 428 | }); 429 | } 430 | }; 431 | var this_1 = this; 432 | // - Find out global pseudo classes 433 | for (var _i = 0, properties_1 = properties; _i < properties_1.length; _i++) { 434 | var p = properties_1[_i]; 435 | _loop_1(p); 436 | } 437 | var result = this._makePureFunctions(this.classes); 438 | this.classes = result[0]; 439 | var functions = result[1]; 440 | return { 441 | classes: this.classes, 442 | functions: functions 443 | }; 444 | }; 445 | ClassCollector.prototype._makePureFunctions = function (classes) { 446 | var _this = this; 447 | var functions = []; 448 | /* 449 | Remove pseudo classes who do not have any properties and 450 | 1) are not used as a parent for inheritance 451 | 2) don't inherit anything 452 | */ 453 | classes = classes.filter(function (c) { 454 | if (c.properties.length > 0) 455 | return true; 456 | var child = classes.find(function (child) { return child.extends === c.name; }); 457 | if (!!child) 458 | return true; 459 | if (!!c.extends) 460 | return true; 461 | functions.push(c); 462 | return false; 463 | }); 464 | // Exclude linked functions 465 | functions = functions.filter(function (f) { return !_this.properties.find(function (p) { return p.linkedToFunction === f.name; }); }); 466 | return [classes, functions]; 467 | }; 468 | ClassCollector.prototype._visit = function (node) { 469 | try { 470 | if (ts.isFunctionDeclaration(node)) { 471 | this._onFunctionDeclaration(node); 472 | } 473 | else if (ts.isVariableStatement(node)) { 474 | this._onVariableStatement(node); 475 | } 476 | else if (ts.isExpressionStatement(node)) { 477 | this._onExpressionStatement(node); 478 | } 479 | } 480 | catch (error) { 481 | ErrorLogger.add(node, error); 482 | } 483 | }; 484 | ClassCollector.prototype._onFunctionDeclaration = function (node) { 485 | var name = node.name.escapedText.toString(); 486 | var constructorArgs = makeVariablesFromParameters(this.checker, Array.from(node.parameters || [])); 487 | var _a = this._makeConstructorSpecs(node, node.body, name, constructorArgs), constructorSignature = _a.constructorSignature, constructorProperty = _a.constructorProperty, jsDoc = _a.jsDoc; 488 | this.classes.push({ 489 | name: name, 490 | constructorArgs: constructorArgs, 491 | properties: this.properties.map(function (p) { return (p.parentSymbol === name ? p : null); }).filter(function (v) { return !!v; }), 492 | constructorProperty: constructorProperty, 493 | jsDoc: jsDoc || null, 494 | constructorSignature: constructorSignature 495 | }); 496 | }; 497 | ClassCollector.prototype._onVariableStatement = function (node) { 498 | var declaration = node.declarationList.declarations[0]; 499 | if (ts.isVariableDeclaration(declaration) && 500 | declaration.initializer) { 501 | if (ts.isFunctionExpression(declaration.initializer)) { 502 | var name_1 = declaration.name.escapedText.toString(); 503 | var constructorArgs = makeVariablesFromParameters(this.checker, Array.from(declaration.initializer.parameters || [])); 504 | var body = declaration.initializer.body; 505 | var _a = this._makeConstructorSpecs(node, body, name_1, constructorArgs), constructorSignature = _a.constructorSignature, constructorProperty = _a.constructorProperty, jsDoc = _a.jsDoc; 506 | this.classes.push({ 507 | name: name_1, 508 | constructorArgs: constructorArgs, 509 | properties: this.properties.map(function (p) { return (p.parentSymbol === name_1 ? p : null); }).filter(function (v) { return !!v; }), 510 | constructorProperty: constructorProperty, 511 | jsDoc: jsDoc || null, 512 | constructorSignature: constructorSignature 513 | }); 514 | } 515 | else if (ts.isIdentifier(declaration.initializer)) { 516 | var name_2 = declaration.name.escapedText.toString(); 517 | var funcName_1 = declaration.initializer.escapedText.toString(); 518 | var _class_1; 519 | this.classes = this.classes.filter(function (c) { 520 | if (c.name === funcName_1) { 521 | _class_1 = c; 522 | return false; 523 | } 524 | return true; 525 | }); 526 | if (_class_1) { 527 | this.classes.push(__assign({}, _class_1, { name: name_2, properties: this.properties.map(function (p) { return (p.parentSymbol === name_2 ? p : null); }).filter(function (v) { return !!v; }) })); 528 | } 529 | } 530 | } 531 | }; 532 | ClassCollector.prototype._onExpressionStatement = function (node) { 533 | this._checkPrototypeInheritance(node) || this._checkPropertiesDefinition(node); 534 | }; 535 | ClassCollector.prototype._checkPrototypeInheritance = function (node) { 536 | var str = node.getText(); 537 | if (str.match(/(.+)\.prototype\s*\=\s*Object\.create\((.+)\.prototype\)/i)) { 538 | var _class = this.classes.find(function (c) { return c.name === RegExp.$1; }); 539 | if (!_class) { 540 | _class = { 541 | constructorArgs: [], 542 | global: true, 543 | name: RegExp.$1, 544 | properties: [], 545 | extends: RegExp.$2 546 | }; 547 | this.classes.push(_class); 548 | } 549 | else { 550 | _class.extends = RegExp.$2; 551 | } 552 | return true; 553 | } 554 | return false; 555 | }; 556 | ClassCollector.prototype._checkPropertiesDefinition = function (node) { 557 | var str = node.getText(); 558 | if (str.match(/Object\.definePropert.+\((.+?),/i) && ts.isCallExpression(node.expression)) { 559 | // - Extract properties from getter/setter 560 | var properties = []; 561 | var value = RegExp.$1; 562 | var symbol_1, _static = void 0; 563 | if (value.match(/(.+)\.prototype/)) { 564 | symbol_1 = RegExp.$1; 565 | _static = false; 566 | } 567 | else { 568 | symbol_1 = value; 569 | _static = true; 570 | } 571 | var arg = node.expression.arguments[1]; 572 | if (ts.isStringLiteral(arg)) { 573 | var jsDoc = node.jsDoc || null; 574 | var lastJsDoc = jsDoc ? jsDoc[jsDoc.length - 1] : null; 575 | var objArg = node.expression.arguments[2]; 576 | if (ts.isObjectLiteralExpression(objArg)) { 577 | var result = makePropertyFromObjectLiteral(this.checker, objArg, lastJsDoc); 578 | properties.push({ 579 | name: arg.text, 580 | parentSymbol: symbol_1, 581 | static: _static, 582 | type: result.type, 583 | readonly: result.readonly 584 | }); 585 | } 586 | } 587 | else if (ts.isObjectLiteralExpression(arg)) { 588 | for (var _i = 0, _a = arg.properties; _i < _a.length; _i++) { 589 | var prop = _a[_i]; 590 | if (ts.isPropertyAssignment(prop)) { 591 | var jsDoc = prop.jsDoc || null; 592 | var lastJsDoc = jsDoc ? jsDoc[jsDoc.length - 1] : null; 593 | var name = prop.name.escapedText.toString(); 594 | if (ts.isObjectLiteralExpression(prop.initializer)) { 595 | var result = makePropertyFromObjectLiteral(this.checker, prop.initializer, lastJsDoc); 596 | properties.push({ 597 | name: name, 598 | parentSymbol: symbol_1, 599 | readonly: result.readonly, 600 | static: _static, 601 | type: result.type, 602 | jsDoc: jsDoc 603 | }); 604 | } 605 | else { 606 | properties.push({ 607 | name: name, 608 | parentSymbol: symbol_1, 609 | readonly: true, 610 | static: _static, 611 | type: getTypeString(this.checker, prop.initializer), 612 | jsDoc: jsDoc 613 | }); 614 | } 615 | } 616 | } 617 | } 618 | var _class = this.classes.find(function (c) { return c.name === symbol_1; }); 619 | if (!_class) { 620 | _class = { 621 | constructorArgs: [], 622 | global: true, 623 | name: symbol_1, 624 | properties: properties.slice() 625 | }; 626 | this.classes.push(_class); 627 | } 628 | else { 629 | _class.properties = [].concat(_class.properties, properties); 630 | } 631 | return true; 632 | } 633 | return false; 634 | }; 635 | ClassCollector.prototype._makeConstructorSpecs = function (node, body, name, constructorArgs) { 636 | var jsDoc = node.jsDoc; 637 | var lastJsDoc = jsDoc ? jsDoc[jsDoc.length - 1] : null; 638 | if (!body || !body.statements) 639 | return; 640 | /* 641 | - Look for call of the true constructor 642 | function Test() { 643 | this.init.apply(this, arguments); 644 | } 645 | */ 646 | var constructorProperty = null; 647 | for (var _i = 0, _a = body.statements; _i < _a.length; _i++) { 648 | var statement = _a[_i]; 649 | if (ts.isExpressionStatement(statement)) { 650 | var str = statement.getText(); 651 | if (str.match(/this\.(.+)\.apply\(this,/i)) { 652 | constructorProperty = this.properties.find(function (p) { return p.parentSymbol === name && p.name === RegExp.$1; }); 653 | if (jsDoc) 654 | constructorProperty.type = extractJsDocType(lastJsDoc, constructorProperty.type); 655 | } 656 | } 657 | } 658 | var constructorSignature = null; 659 | // - Didn't find a call to a pseudo constructor 660 | if (!constructorProperty) { 661 | constructorSignature = jsDoc 662 | ? extractJsDocType(lastJsDoc).replace(/\=\>.+/i, '') 663 | : "(" + constructorArgs.map(function (p) { return p.name + ": " + p.type; }).join(', ') + ")"; 664 | } 665 | return { 666 | constructorSignature: constructorSignature, 667 | constructorProperty: constructorProperty, 668 | jsDoc: lastJsDoc 669 | }; 670 | }; 671 | return ClassCollector; 672 | }()); 673 | 674 | var DTSWriter = /** @class */ (function () { 675 | function DTSWriter() { 676 | } 677 | DTSWriter.print = function (dts) { 678 | var printer = ts.createPrinter(); 679 | var sourceFile = ts.createSourceFile('test.ts', dts, ts.ScriptTarget.ES2017, true, ts.ScriptKind.TS); 680 | return printer.printFile(sourceFile); 681 | }; 682 | DTSWriter.make = function (classes, functions, properties) { 683 | var _this = this; 684 | var globals = classes.map(function (c) { return (c.global ? c : null); }).filter(function (v) { return !!v; }); 685 | var text = ''; 686 | if (Runner.options.guessTypes) { 687 | text += 'declare type Guess = Partial;\n'; 688 | } 689 | if (globals.length > 0) { 690 | text = "declare global {\n " + globals 691 | .map(function (c) { return "interface " + c.name + " {\n " + c.properties 692 | .map(function (p) { return _this.propertyToString(p, true).replace('static', ''); }) 693 | .join('\n') + "\n }"; }) 694 | .join('\n') + "\n }\n "; 695 | } 696 | var normal = classes.filter(function (c) { return !c.global; }); 697 | var rootProps = properties.filter(function (p) { return !p.parentSymbol; }); 698 | var namespace = Runner.options.namespace; 699 | if (namespace) { 700 | text += "export declare namespace " + namespace + "{\n " + rootProps.map(function (p) { return "var " + p.name + ": " + _this.propertyTypeToString(p) + ";"; }).join('\n') + "\n " + functions.map(function (f) { return _this.functionToString(f); }).join('\n') + "\n " + normal.map(function (c) { return _this.classToString(c); }).join('\n') + "\n }"; 701 | } 702 | else { 703 | text += "\n " + rootProps 704 | .map(function (p) { return "export var " + p.name + ": " + _this.propertyTypeToString(p) + ";"; }) 705 | .join('\n') + "\n " + functions.map(function (f) { return 'export ' + _this.functionToString(f); }).join('\n') + "\n " + normal.map(function (c) { return 'export ' + _this.classToString(c); }).join('\n') + "\n "; 706 | } 707 | return this.print(text); 708 | }; 709 | DTSWriter.propertyToString = function (property, isMethod) { 710 | if (isMethod === void 0) { isMethod = false; } 711 | return ("" + (property.jsDoc && property.jsDoc.getText ? property.jsDoc.getText() + '\n' : '') + (property.static ? 'static ' : '') + (property.readonly ? 'readonly ' : '') + (isMethod 712 | ? "" + this.toMethodTypeString(property) 713 | : property.name + ": " + this.propertyTypeToString(property)) + ";").trim(); 714 | }; 715 | DTSWriter.propertyTypeToString = function (property) { 716 | return "" + (property.typeGuessing ? property.typeGuessing.toInlineString() : property.type); 717 | }; 718 | DTSWriter.toMethodTypeString = function (property) { 719 | var str = this.propertyTypeToString(property); 720 | if (str.match(/^\((.*)\)\s*\=\>\s*(.+)/i)) { 721 | return property.name + "(" + RegExp.$1 + "): " + RegExp.$2; 722 | } 723 | return property.name + ": " + this.propertyTypeToString(property); 724 | }; 725 | DTSWriter.classToString = function (_class) { 726 | var _this = this; 727 | var constructorDoc = _class.jsDoc && _class.jsDoc.getText ? _class.jsDoc.getText() : ''; 728 | return "class " + _class.name + (_class.extends ? " extends " + _class.extends : '') + " {" + constructorDoc + "\n " + (_class.constructorProperty 729 | ? "constructor" + _class.constructorProperty.type.replace(/ \=\>.+/i, '') + ";\n" 730 | : "constructor" + _class.constructorSignature + ";\n") + _class.properties 731 | .filter(function (p) { return p !== _class.constructorProperty; }) 732 | .map(function (p) { return _this.propertyToString(p, true); }) 733 | .join('\n\n') + "\n }"; 734 | }; 735 | DTSWriter.functionToString = function (func) { 736 | var doc = func.jsDoc && func.jsDoc.getText ? func.jsDoc.getText() : ''; 737 | return doc + "function " + func.name + func.constructorSignature + ";"; 738 | }; 739 | return DTSWriter; 740 | }()); 741 | 742 | var Guess = /** @class */ (function () { 743 | function Guess(value, name) { 744 | this.value = value; 745 | this.name = name; 746 | } 747 | Guess.prototype.asTypeSymbol = function () { 748 | return "T" + (this.name[0].toUpperCase() + this.name.substr(1, this.name.length - 1)); 749 | }; 750 | Guess.prototype.asInterfaceSymbol = function () { 751 | return "I" + (this.name[0].toUpperCase() + this.name.substr(1, this.name.length - 1)); 752 | }; 753 | Guess.prototype.toInlineString = function () { 754 | return "Guess<" + this.value 755 | .map(function (v) { return util.inspect(v, true, Infinity); }) 756 | .join(' | ') 757 | .replace(/\'(.*?)\'/gi, '$1') + ">"; 758 | }; 759 | Guess.prototype.toTypeString = function () { 760 | var interfaceStr = this.toInterfaceString(); 761 | var typeSymbol = this.asTypeSymbol(); 762 | var intSymbol = this.asInterfaceSymbol(); 763 | return (interfaceStr + '\n' || '') + "type " + typeSymbol + " = Guess<" + this.value.map(function (v) { 764 | return typeof v === 'object' ? "" + intSymbol : v; 765 | }) + ">"; 766 | }; 767 | Guess.prototype.toInterfaceString = function () { 768 | var typings = this.value.find(function (v) { return typeof v === 'object'; }); 769 | var intSymbol = this.asInterfaceSymbol(); 770 | if (typings) { 771 | return "interface " + intSymbol + " {\n " + Object.entries(typings) 772 | .map(function (_a) { 773 | var key = _a[0], value = _a[1]; 774 | return key + ": " + util.inspect(value, true, Infinity) + ";"; 775 | }) 776 | .join('\n') 777 | .replace(/\'(.*?)\'/gi, '$1') + "\n }"; 778 | } 779 | return null; 780 | }; 781 | return Guess; 782 | }()); 783 | var TypeGuesser = /** @class */ (function () { 784 | function TypeGuesser() { 785 | } 786 | TypeGuesser.guess = function (program, properties, classes) { 787 | this.program = program; 788 | this.knownClasses = classes.filter(function (c) { return !c.global; }); 789 | this.guessRootPropertiesType(properties); 790 | this.guessPropertiesFunctionType(properties); 791 | this.guessClassPropertiesType(classes); 792 | }; 793 | TypeGuesser.guessRootPropertiesType = function (properties) { 794 | this.guessFromBody(this.program.getSourceFiles()[0], properties.filter(function (p) { return !p.parentSymbol; })); 795 | }; 796 | TypeGuesser.guessPropertiesFunctionType = function (properties) { 797 | var _loop_1 = function (prop) { 798 | if (prop.rightNode && ts.isFunctionExpression(prop.rightNode)) { 799 | if (prop.type.match(/\((.+?)\)\s*\=\>\s*(.+)/i)) { 800 | var props = []; 801 | var paramsWithType = RegExp.$1.split(','); 802 | var returnType = RegExp.$2.trim(); 803 | for (var _i = 0, paramsWithType_1 = paramsWithType; _i < paramsWithType_1.length; _i++) { 804 | var p = paramsWithType_1[_i]; 805 | if (p.match(/(.+)\s*\:\s*(.+)/i)) { 806 | var name = RegExp.$1.trim(); 807 | var type = RegExp.$2.trim(); 808 | props.push({ 809 | name: name, 810 | type: type, 811 | parentSymbol: null 812 | }); 813 | } 814 | } 815 | var guessedTypes_1 = this_1.guessParametersType(prop.rightNode, props); 816 | var resultType = "(" + props.map(function (p) { 817 | return p.name + ": " + (guessedTypes_1[p.name] || p.type); 818 | }) + ") => " + returnType; 819 | prop.type = resultType; 820 | } 821 | } 822 | }; 823 | var this_1 = this; 824 | for (var _i = 0, properties_1 = properties; _i < properties_1.length; _i++) { 825 | var prop = properties_1[_i]; 826 | _loop_1(prop); 827 | } 828 | }; 829 | TypeGuesser.guessClassPropertiesType = function (classes) { 830 | for (var _i = 0, classes_1 = classes; _i < classes_1.length; _i++) { 831 | var _class = classes_1[_i]; 832 | var props = _class.properties.map(function (prop) { 833 | return __assign({}, prop, { name: 'this.' + prop.name }); 834 | }); 835 | for (var _a = 0, _b = _class.properties; _a < _b.length; _a++) { 836 | var prop = _b[_a]; 837 | if (prop.rightNode && ts.isFunctionExpression(prop.rightNode)) { 838 | this.guessFromBody(prop.rightNode, props); 839 | //nodes.push(prop.rightNode); 840 | } 841 | } 842 | //console.log('props :', props); 843 | for (var i = 0; i < props.length; i++) { 844 | _class.properties[i].guessedType = props[i].guessedType; 845 | _class.properties[i].typeGuessing = props[i].typeGuessing; 846 | //console.log(classProp.name, ' ', props[i].typeGuessing); 847 | } 848 | } 849 | }; 850 | TypeGuesser.guessClassConstructorTypes = function (_class) { }; 851 | TypeGuesser.guessParametersType = function (node, props) { 852 | this.guessFromBody(node.body, props); 853 | var result = {}; 854 | for (var _i = 0, props_1 = props; _i < props_1.length; _i++) { 855 | var prop = props_1[_i]; 856 | if (prop.guessedType) { 857 | result[prop.name] = prop.guessedType; 858 | } 859 | } 860 | return result; 861 | }; 862 | TypeGuesser.guessFromBody = function (body, properties) { 863 | var _this = this; 864 | var data = this.inferDataFromCallExpressions(body, properties); 865 | var assignedTypes = data.assignedTypes; 866 | var typings = __assign({}, this.typingFromAmbientUsage(body, properties), data.typings); 867 | body.forEachChild(function (node) { 868 | if (ts.isVariableStatement(node)) { 869 | var declaration = node.declarationList.declarations[0]; 870 | var left_1 = declaration.name; 871 | var right = declaration.initializer; 872 | if (!right || !left_1) 873 | return; 874 | var prop = properties.find(function (p) { 875 | var regex = new RegExp("^" + p.name); 876 | return !!left_1.getText().match(regex); 877 | }); 878 | if (prop) { 879 | _this.infer(prop, left_1, right, { 880 | assignedTypes: assignedTypes, 881 | typings: typings 882 | }); 883 | } 884 | } 885 | else if (ts.isExpressionStatement(node) && ts.isBinaryExpression(node.expression)) { 886 | var _a = node.expression, left_2 = _a.left, right = _a.right; 887 | if (!right || !left_2) 888 | return; 889 | var prop = properties.find(function (p) { 890 | var regex = new RegExp("^" + p.name); 891 | return !!left_2.getText().match(regex); 892 | }); 893 | if (prop) { 894 | _this.infer(prop, left_2, right, { 895 | assignedTypes: assignedTypes, 896 | typings: typings 897 | }); 898 | } 899 | } 900 | }); 901 | //console.log('typings :', typings); 902 | for (var _i = 0, properties_2 = properties; _i < properties_2.length; _i++) { 903 | var prop = properties_2[_i]; 904 | var typingObj = this.inferFromKnownSymbols(typings[prop.name]); 905 | var usage = {}; 906 | for (var _a = 0, _b = assignedTypes[prop.name] || []; _a < _b.length; _a++) { 907 | var type = _b[_a]; 908 | var typeStr = typeof type === 'string' ? type : JSON.stringify(type); 909 | if (!usage[typeStr]) { 910 | usage[typeStr] = 0; 911 | } 912 | usage[typeStr]++; 913 | } 914 | var mostAssignedTypes = Object.entries(usage) 915 | .sort(function (_a, _b) { 916 | var aKey = _a[0], aValue = _a[1]; 917 | var bKey = _b[0], bValue = _b[1]; 918 | return bValue - aValue; 919 | }) 920 | .map(function (_a) { 921 | var key = _a[0], value = _a[1]; 922 | return key; 923 | }); 924 | var possibleTypes = []; 925 | for (var i = 0; i < 2; i++) { 926 | if (!mostAssignedTypes[i]) 927 | break; 928 | possibleTypes[i] = mostAssignedTypes[i]; 929 | } 930 | var types = possibleTypes.filter(function (v) { return v !== 'any' && v !== '{}'; }).concat([ 931 | typeof typingObj === 'object' ? this.Dot.object(typingObj) : typingObj 932 | ]).filter(function (v) { 933 | if (typeof v == 'object' && Object.keys(v).length == 0) 934 | return false; 935 | return true; 936 | }); 937 | if (types.length === 0) 938 | return; 939 | if (types.length === 1 && types[0] === prop.type) 940 | return; 941 | prop.typeGuessing = new Guess(types, prop.name); 942 | prop.guessedType = prop.typeGuessing.toInlineString(); 943 | } 944 | }; 945 | TypeGuesser.typingFromAmbientUsage = function (node, properties) { 946 | var typings = {}; 947 | var text = node.getFullText(); 948 | for (var _i = 0, properties_3 = properties; _i < properties_3.length; _i++) { 949 | var prop = properties_3[_i]; 950 | typings[prop.name] = {}; 951 | var str = "[^\\.](" + prop.name + "\\..+?)\\)*?[\\s;\\(\\[]"; 952 | var regex = new RegExp(str, 'g'); 953 | while (regex.exec(text)) { 954 | var matchStr = RegExp.$1.trim().replace(prop.name + '.', ''); 955 | typings[prop.name][matchStr] = 'any'; 956 | } 957 | } 958 | return typings; 959 | }; 960 | TypeGuesser.inferDataFromCallExpressions = function (node, properties) { 961 | var typings = {}; 962 | var assignedTypes = {}; 963 | var expressions = collectNodesBy(this.program, function (node) { 964 | return ts.isCallExpression(node); 965 | }); 966 | var typeChecker = this.program.getTypeChecker(); 967 | for (var _i = 0, expressions_1 = expressions; _i < expressions_1.length; _i++) { 968 | var expr = expressions_1[_i]; 969 | //console.log('expr :', expr); 970 | var propsAsArgs = expr.arguments.map(function (arg) { 971 | var props = []; 972 | var regex = new RegExp('\\b(\\S+)\\b', 'g'); 973 | var _loop_3 = function () { 974 | var str_1 = RegExp.$1.trim(); 975 | if (str_1.match(/^(.+)\..+/i) && RegExp.$1.trim() !== 'this') { 976 | var prop = properties.find(function (p) { return str_1.startsWith(p.name); }); 977 | if (prop) 978 | props.push({ propName: prop.name, str: str_1 }); 979 | } 980 | else { 981 | var prop = properties.find(function (p) { return p.name === str_1; }); 982 | if (prop) 983 | props.push(prop); 984 | } 985 | }; 986 | while (regex.exec(arg.getText())) { 987 | _loop_3(); 988 | } 989 | return props; 990 | /*if (ts.isIdentifier(arg)) { 991 | let prop = properties.find(p => p.name === arg.escapedText.toString()); 992 | return [prop] || null; 993 | } else { 994 | let identifiers = collectNodesBy( 995 | this.program, 996 | (node: ts.Node) => ts.isIdentifier(node), 997 | arg 998 | ) as ts.Identifier[]; 999 | return identifiers.map(i => properties.find(p => p.name === i.escapedText.toString())); 1000 | }*/ 1001 | }); 1002 | var symbol = typeChecker.getSymbolAtLocation(expr.expression); 1003 | if (!symbol) { 1004 | if (ts.isPropertyAccessExpression(expr.expression)) { 1005 | //console.log('need handling'); 1006 | //let symbol = tc.getSymbolAtLocation(expr.expression.expression); 1007 | continue; 1008 | } 1009 | continue; 1010 | } 1011 | var type = typeChecker.getTypeOfSymbolAtLocation(symbol, expr); 1012 | var str = typeChecker.typeToString(type); 1013 | if (str.match(/^\((.+)\)\s*\=\>/i)) { 1014 | var args = RegExp.$1.split(','); 1015 | var _loop_2 = function (i) { 1016 | var props = propsAsArgs[i].filter(function (v) { return !!v; }); 1017 | var argStr = args[i]; 1018 | if (argStr && argStr.match(/.+\s*\:\s*(.+)/i)) { 1019 | var type_1 = RegExp.$1.trim(); 1020 | if (type_1 !== 'any') { 1021 | props.forEach(function (prop) { 1022 | if (prop.propName) { 1023 | if (!typings[prop.propName]) { 1024 | typings[prop.propName] = {}; 1025 | } 1026 | typings[prop.propName][prop.str.replace(prop.propName + '.', '')] = type_1; 1027 | } 1028 | else { 1029 | if (Array.isArray(assignedTypes[prop.name])) { 1030 | assignedTypes[prop.name].push(type_1); 1031 | } 1032 | else { 1033 | assignedTypes[prop.name] = [type_1]; 1034 | } 1035 | } 1036 | }); 1037 | } 1038 | } 1039 | }; 1040 | for (var i = 0; i < propsAsArgs.length; i++) { 1041 | _loop_2(i); 1042 | } 1043 | } 1044 | } 1045 | return { 1046 | assignedTypes: assignedTypes, 1047 | typings: typings 1048 | }; 1049 | }; 1050 | TypeGuesser.infer = function (prop, left, right, data) { 1051 | var assignedTypes = data.assignedTypes, typings = data.typings; 1052 | var leftStr = left.getText(); 1053 | var type = getTypeString(this.program.getTypeChecker(), right); 1054 | /** 1055 | * If an object literal is found, it is converted to an object 1056 | * so that we can infer the proper type 1057 | */ 1058 | if (ts.isObjectLiteralExpression(right)) { 1059 | type = this.inferFromKnownSymbols(objectLiteralToObject(right)); 1060 | } 1061 | if (leftStr.match(/^(.+?)\./i)) { 1062 | if (!typings[prop.name]) { 1063 | typings[prop.name] = {}; 1064 | } 1065 | typings[prop.name][leftStr.replace(prop.name + '.', '')] = type; 1066 | } 1067 | else { 1068 | if (!assignedTypes[prop.name]) { 1069 | assignedTypes[prop.name] = []; 1070 | } 1071 | assignedTypes[prop.name].push(type); 1072 | } 1073 | }; 1074 | TypeGuesser.inferFromKnownSymbols = function (typings, root) { 1075 | if (root === void 0) { root = true; } 1076 | if (root) { 1077 | var sym = '_$$$_'; 1078 | var inferObj = {}; 1079 | inferObj[sym] = typings; 1080 | this.inferFromKnownSymbols(inferObj, false); 1081 | return inferObj[sym]; 1082 | } 1083 | var _loop_4 = function (key, value) { 1084 | if (typeof value === 'object') { 1085 | var keys_1 = Object.keys(value); 1086 | var matchingSymbols = this_2.knownClasses 1087 | .map(function (s) { 1088 | var matchingProps = s.properties.map(function (p) { return keys_1.includes(p.name); }).filter(function (v) { return !!v; }); 1089 | return matchingProps.length > 0 ? s : null; 1090 | }) 1091 | .filter(function (v) { return !!v; }) 1092 | .sort(function (a, b) { 1093 | var nbrPropsA = a.properties.map(function (p) { return keys_1.includes(p.name); }).filter(function (v) { return !!v; }).length; 1094 | var nbrPropsB = b.properties.map(function (p) { return keys_1.includes(p.name); }).filter(function (v) { return !!v; }).length; 1095 | return nbrPropsB - nbrPropsA; 1096 | }); 1097 | if (matchingSymbols.length > 0 && keys_1.length) { 1098 | var symb = matchingSymbols[0]; 1099 | if (keys_1.length <= symb.properties.length) { 1100 | //const partial = symb.properties.length > keys.length; 1101 | //typings[key] = partial ? `Partial<${symb.name}>` : symb.name; 1102 | typings[key] = symb.name; 1103 | return "continue"; 1104 | } 1105 | } 1106 | this_2.inferFromKnownSymbols(value, false); 1107 | } 1108 | }; 1109 | var this_2 = this; 1110 | for (var _i = 0, _a = Object.entries(typings); _i < _a.length; _i++) { 1111 | var _b = _a[_i], key = _b[0], value = _b[1]; 1112 | _loop_4(key, value); 1113 | } 1114 | }; 1115 | TypeGuesser.symbol = Symbol(); 1116 | TypeGuesser.Dot = new DotObject('.', true); 1117 | TypeGuesser.knownClasses = []; 1118 | return TypeGuesser; 1119 | }()); 1120 | // const str = ts.generateTypesForGlobal('test', { name: 'hi', val: 1, add: (a, b) => a + b }, {}); 1121 | // const program = Runner.makeProgram({ 1122 | // fileName: 'test.ts', 1123 | // content: ` 1124 | // var myVar = {}; 1125 | // myVar.hello = {}; 1126 | // myVar.hello.world = true; 1127 | // myVar.hey = "what?"; 1128 | // myVar.aVal = 5; 1129 | // Math.round(myVar); 1130 | // function MyClass(a, b) { 1131 | // this.myProp = {}; 1132 | // this.myProp.x = 0; 1133 | // this.myProp.y = 0; 1134 | // this.myNbr = null; 1135 | // } 1136 | // MyClass.prototype.myMethod = function(a, b) { 1137 | // Math.round(this.myNbr); 1138 | // return Math.round(a); 1139 | // } 1140 | // ` 1141 | // }); 1142 | // TypeGuesser.program = program; 1143 | // const props: Property[] = [ 1144 | // { 1145 | // name: 'myVar', 1146 | // type: 'any', 1147 | // parentSymbol: null 1148 | // } 1149 | // ]; 1150 | // //TypeGuesser.guessFromBody(program.getSourceFiles()[0].statements as any, props); 1151 | // //console.log('props :', props); 1152 | // TypeGuesser.guessClassPropertiesType([ 1153 | // { 1154 | // name: 'MyClass', 1155 | // constructorArgs: [], 1156 | // properties: [ 1157 | // { 1158 | // name: 'myProp', 1159 | // parentSymbol: 'MyClass', 1160 | // type: 'any' 1161 | // }, 1162 | // { 1163 | // name: 'myNbr', 1164 | // parentSymbol: 'MyClass', 1165 | // type: 'any' 1166 | // } 1167 | // ] 1168 | // } 1169 | // ]); 1170 | /* 1171 | let result = utils.collectVariableUsage(program.getSourceFiles()[0]); 1172 | let entries = result.entries(); 1173 | let tab; 1174 | while(tab = entries.next().value) { 1175 | let [key, value]: [ts.Identifier, VariableInfo] = tab; 1176 | if(key.escapedText.toString() === 'MyClass') { 1177 | console.log("---"); 1178 | //console.log('key :', key); 1179 | console.log('value :', value); 1180 | } 1181 | } 1182 | */ 1183 | 1184 | var MockupWriter = /** @class */ (function () { 1185 | function MockupWriter() { 1186 | } 1187 | MockupWriter.print = function (dts) { 1188 | var printer = ts.createPrinter(); 1189 | var sourceFile = ts.createSourceFile('test.ts', dts, ts.ScriptTarget.ES2017, true, ts.ScriptKind.TS); 1190 | return printer.printFile(sourceFile); 1191 | }; 1192 | MockupWriter.make = function (classes, functions, properties) { 1193 | var _this = this; 1194 | var text = ''; 1195 | if (Runner.options.guessTypes) { 1196 | text += 'declare type Guess = Partial;\n'; 1197 | } 1198 | var normal = classes.filter(function (c) { return !c.global; }); 1199 | var rootProps = properties.filter(function (p) { return !p.parentSymbol; }); 1200 | var namespace = Runner.options.namespace; 1201 | if (namespace) { 1202 | text += "export declare namespace " + namespace + "{\n " + rootProps.map(function (p) { return "var " + p.name + ": " + _this.propertyTypeToString(p) + ";"; }).join('\n') + "\n " + functions.map(function (f) { return _this.functionToString(f); }).join('\n') + "\n " + normal.map(function (c) { return _this.classToString(c); }).join('\n') + "\n }"; 1203 | } 1204 | else { 1205 | text += "\n " + rootProps 1206 | .map(function (p) { return "export var " + p.name + ": " + _this.propertyTypeToString(p) + ";"; }) 1207 | .join('\n') + "\n " + functions.map(function (f) { return 'export ' + _this.functionToString(f); }).join('\n') + "\n " + normal.map(function (c) { return 'export ' + _this.classToString(c); }).join('\n') + "\n "; 1208 | } 1209 | return this.print(text); 1210 | }; 1211 | MockupWriter.propertyToString = function (property, isMethod) { 1212 | if (isMethod === void 0) { isMethod = false; } 1213 | return ("" + (property.jsDoc && property.jsDoc.getText ? property.jsDoc.getText() + '\n' : '') + (property.static ? 'static ' : '') + (property.readonly ? 'readonly ' : '') + (isMethod 1214 | ? "" + this.toMethodTypeString(property) 1215 | : property.name + ": " + this.propertyTypeToString(property))).trim(); 1216 | }; 1217 | MockupWriter.propertyTypeToString = function (property) { 1218 | return "" + (property.typeGuessing ? property.typeGuessing.toInlineString() : property.type); 1219 | }; 1220 | MockupWriter.toMethodTypeString = function (property) { 1221 | var str = this.propertyTypeToString(property); 1222 | if (str.match(/^\((.*)\)\s*\=\>\s*(.+)/i)) { 1223 | var returnType = RegExp.$2.trim(); 1224 | if (returnType !== 'void') { 1225 | return property.name + "(" + RegExp.$1 + "): " + returnType + " { return null; }"; 1226 | } 1227 | return property.name + "(" + RegExp.$1 + "): " + returnType + " {}"; 1228 | } 1229 | return property.name + ": " + this.propertyTypeToString(property); 1230 | }; 1231 | MockupWriter.classToString = function (_class) { 1232 | var _this = this; 1233 | var constructorDoc = _class.jsDoc && _class.jsDoc.getText ? _class.jsDoc.getText() : ''; 1234 | return "class " + _class.name + (_class.extends ? " extends " + _class.extends : '') + " {" + constructorDoc + "\n " + (_class.constructorProperty 1235 | ? "constructor" + _class.constructorProperty.type.replace(/ \=\>.+/i, '') + " {}\n" 1236 | : "constructor" + _class.constructorSignature + " {}\n") + _class.properties 1237 | .filter(function (p) { return p !== _class.constructorProperty; }) 1238 | .map(function (p) { return _this.propertyToString(p, true); }) 1239 | .join('\n\n') + "\n }"; 1240 | }; 1241 | MockupWriter.functionToString = function (func) { 1242 | var doc = func.jsDoc && func.jsDoc.getText ? func.jsDoc.getText() : ''; 1243 | return doc + "function " + func.name + func.constructorSignature + " { return null; }"; 1244 | }; 1245 | return MockupWriter; 1246 | }()); 1247 | 1248 | var Runner = /** @class */ (function () { 1249 | function Runner() { 1250 | } 1251 | Runner.makeProgram = function (files) { 1252 | var lib = { 1253 | content: fs.readFileSync(path.resolve(__dirname, '../lib/lib.es5.d.ts')).toString(), 1254 | fileName: 'lib.es2018.d.ts' 1255 | }; 1256 | return createProgram(files.concat([lib]), {}); 1257 | }; 1258 | Runner.run = function (options, files, fileName, callerPath, mode) { 1259 | if (mode === void 0) { mode = 'write'; } 1260 | this.options = options; 1261 | try { 1262 | var program_1 = this.makeProgram(files); 1263 | var properties_1 = this._runPhase('Collecting properties', function () { 1264 | return new PropertyCollector().collect(program_1); 1265 | }); 1266 | var builtData_1 = this._runPhase('Collecting pseudo classes', function () { 1267 | return new ClassCollector().collect(program_1, properties_1); 1268 | }); 1269 | if (options.guessTypes) { 1270 | this._runPhase('Guessing properties typings', function () { 1271 | TypeGuesser.guess(program_1, properties_1, builtData_1.classes); 1272 | }); 1273 | } 1274 | var text = this._runPhase('Generating & writing result', function () { 1275 | var result; 1276 | var resultFileName; 1277 | if (options.mockupMode) { 1278 | result = MockupWriter.make(builtData_1.classes, builtData_1.functions, properties_1); 1279 | resultFileName = fileName.replace(/\.(t|j)s/i, '') + '.ts'; 1280 | } 1281 | else { 1282 | result = DTSWriter.make(builtData_1.classes, builtData_1.functions, properties_1); 1283 | resultFileName = fileName.replace(/\.(t|j)s/i, '') + '.d.ts'; 1284 | } 1285 | if (resultFileName && mode === 'write') { 1286 | fs.writeFileSync(path.resolve(callerPath, resultFileName), result); 1287 | } 1288 | return result; 1289 | }); 1290 | this.result = { 1291 | builtData: builtData_1, 1292 | properties: properties_1 1293 | }; 1294 | if (mode === 'write') { 1295 | var hasError = ErrorLogger.archive.length > 0; 1296 | if (hasError) { 1297 | var readline_1 = Readline.createInterface({ 1298 | input: process.stdin, 1299 | output: process.stdout 1300 | }); 1301 | readline_1.question("Show error logs? (y/n): ", function (value) { 1302 | if (value && value[0].toLowerCase().trim() === 'y') { 1303 | console.log(ErrorLogger.display(program_1)); 1304 | } 1305 | readline_1.close(); 1306 | }); 1307 | } 1308 | } 1309 | return text; 1310 | } 1311 | catch (error) { 1312 | console.log('An unexpected error occurred.'); 1313 | throw error; 1314 | } 1315 | }; 1316 | Runner._runPhase = function (message, func) { 1317 | var spinner = ora(message).start(); 1318 | var result = func(); 1319 | var hasError = ErrorLogger.logs.length > 0; 1320 | if (hasError) { 1321 | spinner.fail(message + " | " + ErrorLogger.logs.length + " error(s)"); 1322 | } 1323 | else { 1324 | spinner.succeed(); 1325 | } 1326 | ErrorLogger.store(); 1327 | return result; 1328 | }; 1329 | Runner.options = {}; 1330 | return Runner; 1331 | }()); 1332 | 1333 | var version = "1.1.2"; 1334 | 1335 | var cli = require('commander'); 1336 | cli 1337 | .version(version) 1338 | .option('-n, --namespace [namespace]', 'Wrap the file into a namespace') 1339 | .option('-a, --all-files [outputName]', 'Process all files in the directory and output a single d.ts') 1340 | .option('-b, --bundled-output', 'When -a is used, bundle the output into a single file') 1341 | .option('-r, --root-variables', 'Collect root variables') 1342 | .option('-g, --guess-types', 'Guess types') 1343 | .option('-m, --mockup', 'Generate a mockup of the definition file instead of a d.ts') 1344 | .parse(process.argv); 1345 | var callerPath = process.cwd(); 1346 | var fileName; 1347 | var namespace = cli.namespace; 1348 | var files = []; 1349 | if (cli.allFiles) { 1350 | fileName = cli.outputName || 'output'; 1351 | var filesName = fs.readdirSync(callerPath); 1352 | var i = 1; 1353 | for (var _i = 0, filesName_1 = filesName; _i < filesName_1.length; _i++) { 1354 | var fileName_1 = filesName_1[_i]; 1355 | files.push({ 1356 | content: fs.readFileSync(path.resolve(callerPath, fileName_1)).toString(), 1357 | fileName: "file" + i + ".ts" 1358 | }); 1359 | i++; 1360 | } 1361 | } 1362 | else { 1363 | fileName = process.argv[2]; 1364 | files.push({ 1365 | content: fs.readFileSync(path.resolve(callerPath, fileName)).toString(), 1366 | fileName: 'file1.ts' 1367 | }); 1368 | } 1369 | Runner.run({ 1370 | namespace: namespace, 1371 | allFiles: cli.allFiles, 1372 | collectRootVariables: cli.rootVariables, 1373 | guessTypes: cli.guessTypes, 1374 | mockupMode: cli.mockup 1375 | }, files, fileName, callerPath); 1376 | --------------------------------------------------------------------------------