├── test ├── expected │ ├── .gitattributes │ ├── conflicts_dirname │ │ └── foo-mx.d.ts │ ├── commonjs │ │ └── foo-mx.d.ts │ ├── excludeExp │ │ └── foo-mx.d.ts │ ├── excludeFunc │ │ └── foo-mx.d.ts │ ├── includeExclude │ │ └── foo-mx.d.ts │ ├── out │ │ └── fizz │ │ │ └── buzz.d.ts │ ├── remove │ │ └── foo-mx.d.ts │ ├── default │ │ └── foo-mx.d.ts │ ├── seprinnew │ │ └── bar-mx.d.ts │ ├── externals │ │ └── foo-mx.d.ts │ └── es6 │ │ └── foo-mx.d.ts ├── src │ ├── commonjs │ │ ├── sub │ │ │ ├── index.ts │ │ │ ├── sub.service.html │ │ │ └── sub.service.ts │ │ └── index.ts │ ├── conflicts │ │ └── dirname │ │ │ ├── index.ts │ │ │ ├── file1.ts │ │ │ └── file1 │ │ │ └── file2.ts │ ├── typings │ │ └── external.d.ts │ ├── es6 │ │ ├── sub.ts │ │ ├── lib │ │ │ ├── subC.ts │ │ │ ├── subD.ts │ │ │ └── subE.ts │ │ └── index.ts │ └── main │ │ ├── lib │ │ ├── exported-sub.ts │ │ └── only-internal.ts │ │ ├── Foo.ts │ │ └── index.ts ├── seprinnew_cli-config.json └── test.js ├── index.js ├── .gitattributes ├── .gitmodules ├── .travis.yml ├── .npmignore ├── debug.js ├── .gitignore ├── lib ├── tsconfig.json ├── dts-bundle.js └── index.ts ├── LICENSE-MIT ├── package.json ├── Gruntfile.js └── README.md /test/expected/.gitattributes: -------------------------------------------------------------------------------- 1 | *.ts eol=lf 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/index'); 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js text eol=lf 2 | *.ts text eol=lf 3 | -------------------------------------------------------------------------------- /test/src/commonjs/sub/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sub.service'; 2 | -------------------------------------------------------------------------------- /test/src/commonjs/sub/sub.service.html: -------------------------------------------------------------------------------- 1 |

{{getName()}}

2 | -------------------------------------------------------------------------------- /test/src/conflicts/dirname/index.ts: -------------------------------------------------------------------------------- 1 | export * from './file1'; 2 | export * from './file1/file2'; -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "repo"] 2 | path = repo 3 | url = https://github.com/Bartvds/DefinitelyTyped.git 4 | -------------------------------------------------------------------------------- /test/src/conflicts/dirname/file1.ts: -------------------------------------------------------------------------------- 1 | export class Foo1 { 2 | public property1: number; 3 | constructor() { } 4 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | 5 | before_install: 6 | - npm install -g grunt-cli 7 | 8 | -------------------------------------------------------------------------------- /test/src/conflicts/dirname/file1/file2.ts: -------------------------------------------------------------------------------- 1 | export class Foo2 { 2 | public property2: string; 3 | constructor() { } 4 | } -------------------------------------------------------------------------------- /test/seprinnew_cli-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "prefix": "--", 3 | "indent": "\t", 4 | "newline": " //$\n", 5 | "separator": "#" 6 | } -------------------------------------------------------------------------------- /test/src/commonjs/sub/sub.service.ts: -------------------------------------------------------------------------------- 1 | import './sub.service.html'; 2 | 3 | export class SubService { 4 | 5 | constructor(public x: string){} 6 | 7 | hello(): string { 8 | return `Hello ${this.x}`; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /test/src/typings/external.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'external1' { 2 | export class SomeType { 3 | foo(): void; 4 | } 5 | } 6 | 7 | declare module 'external2' { 8 | export class AnotherType { 9 | foo(): void; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/src/es6/sub.ts: -------------------------------------------------------------------------------- 1 | export interface A { 2 | name: string; 3 | } 4 | 5 | export class B { 6 | name: string; 7 | } 8 | 9 | export default function test(): A { return null; } 10 | export function foo(): A { return null; } 11 | export function bar(): A { return null; } 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /test 3 | /util 4 | 5 | /Gruntfile.js 6 | /debug.js 7 | /dtsm.json 8 | 9 | .npmignore 10 | .gitignore 11 | .gitmodules 12 | 13 | .travis.yml 14 | .jshintrc 15 | .editorconfig 16 | 17 | /tscommand* 18 | .tscache 19 | tmp/ 20 | 21 | /*.tgz 22 | /*.txt 23 | -------------------------------------------------------------------------------- /test/src/es6/lib/subC.ts: -------------------------------------------------------------------------------- 1 | export interface A { 2 | name: string; 3 | } 4 | 5 | export class B { 6 | name: string; 7 | } 8 | 9 | export default function test(): A { return null; } 10 | export function foo(): A { return null; } 11 | export function bar(): A { return null; } 12 | -------------------------------------------------------------------------------- /test/src/es6/lib/subD.ts: -------------------------------------------------------------------------------- 1 | export interface A { 2 | name: string; 3 | } 4 | 5 | export class B { 6 | name: string; 7 | } 8 | 9 | export default function test(): A { return null; } 10 | export function foo(): A { return null; } 11 | export function bar(): A { return null; } 12 | -------------------------------------------------------------------------------- /test/src/es6/lib/subE.ts: -------------------------------------------------------------------------------- 1 | export interface A { 2 | name: string; 3 | } 4 | 5 | export class B { 6 | name: string; 7 | } 8 | 9 | export default function test(): A { return null; } 10 | export function foo(): A { return null; } 11 | export function bar(): A { return null; } 12 | -------------------------------------------------------------------------------- /debug.js: -------------------------------------------------------------------------------- 1 | var dts = require("./lib"); 2 | var path = require("path"); 3 | var actDir = "test/tmp/includeExclude"; 4 | 5 | dts.bundle({ 6 | name: 'foo-mx', 7 | main: path.join(actDir, 'index.d.ts'), 8 | externals: true, 9 | exclude: /exported\-sub/, 10 | newline: '\n' 11 | }); 12 | -------------------------------------------------------------------------------- /test/src/commonjs/index.ts: -------------------------------------------------------------------------------- 1 | import {SubService} from "./sub"; 2 | export {SubService}; 3 | 4 | export class mymod { 5 | 6 | public sub: SubService; 7 | 8 | constructor(){ 9 | this.sub = new SubService("mymod"); 10 | } 11 | 12 | getName(): string { 13 | return this.sub.hello(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/src/main/lib/exported-sub.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import Foo = require('../Foo'); 4 | import mod2 = require('external2'); 5 | 6 | export class ExternalContainer { 7 | public something: mod2.AnotherType; 8 | } 9 | 10 | export function bar(foo: Foo): string { 11 | return foo.foo + '-bar'; 12 | } 13 | 14 | export function bazz(value: string, option?: boolean): string { 15 | return value + '-bazz'; 16 | } 17 | -------------------------------------------------------------------------------- /test/src/main/lib/only-internal.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import Foo = require('../Foo'); 4 | import mod2 = require('external2'); 5 | 6 | export class ExternalContainer { 7 | public something: mod2.AnotherType; 8 | } 9 | 10 | export function bar(foo: Foo): string { 11 | return foo.foo + '-bar'; 12 | } 13 | 14 | export function bazz(value: string, option?: boolean): string { 15 | return value + '-bazz'; 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | 3 | /.idea 4 | /.vagrant 5 | 6 | /node_modules 7 | /typings 8 | 9 | /lib/**/*.js 10 | !/lib/dts-bundle.js 11 | 12 | /test/tmp 13 | /test/build 14 | !/test/src/typings/*.d.ts 15 | /test/src/**/*.d.ts 16 | /test/src/**/*.js 17 | /test/src/**/*.js.map 18 | 19 | /tscommand* 20 | .tscache 21 | 22 | /*.tgz 23 | tmp/ 24 | dump/ 25 | 26 | .baseDir.ts 27 | .baseDir.d.ts 28 | .baseDir.js 29 | .baseDir.js.map 30 | 31 | _tmp.* 32 | 33 | npm-debug.log 34 | -------------------------------------------------------------------------------- /test/expected/conflicts_dirname/foo-mx.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare module 'foo-mx' { 3 | export * from 'foo-mx/file1'; 4 | export * from 'foo-mx/file1/file2'; 5 | } 6 | 7 | declare module 'foo-mx/file1' { 8 | export class Foo1 { 9 | property1: number; 10 | constructor(); 11 | } 12 | } 13 | 14 | declare module 'foo-mx/file1/file2' { 15 | export class Foo2 { 16 | property2: string; 17 | constructor(); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /test/src/main/Foo.ts: -------------------------------------------------------------------------------- 1 | class Foo { 2 | 3 | foo: string; 4 | private counter: number = 0; 5 | 6 | constructor(private secret?: string) { 7 | 8 | } 9 | 10 | /** 11 | * Bars the foo. 12 | */ 13 | barFoo(): void { 14 | 15 | } 16 | /** 17 | * Foos the bar. 18 | */ 19 | private fooBar(): void { 20 | 21 | } 22 | /** Foos the baz. */ 23 | fooBaz(): void { 24 | 25 | } 26 | } 27 | 28 | export = Foo; 29 | -------------------------------------------------------------------------------- /test/src/es6/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./sub"; 2 | import subA from "./sub"; 3 | import * as subB from "./sub"; 4 | import subC, {A} from "./lib/subC"; 5 | import {bar} from "./lib/subD"; 6 | import {foo as buzz} from "./lib/subE"; 7 | 8 | export function indexA() { 9 | return subA(); 10 | } 11 | 12 | export function indexB() { 13 | return new subB.B(); 14 | } 15 | 16 | export function indexC() { 17 | return subC(); 18 | } 19 | 20 | export function indexD() { 21 | return bar; 22 | } 23 | 24 | export function indexE() { 25 | return buzz; 26 | } 27 | -------------------------------------------------------------------------------- /test/expected/commonjs/foo-mx.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare module 'foo-mx' { 3 | import { SubService } from "foo-mx/sub"; 4 | export { SubService }; 5 | export class mymod { 6 | sub: SubService; 7 | constructor(); 8 | getName(): string; 9 | } 10 | } 11 | 12 | declare module 'foo-mx/sub' { 13 | export * from 'foo-mx/sub/sub.service'; 14 | } 15 | 16 | declare module 'foo-mx/sub/sub.service' { 17 | import './sub.service.html'; 18 | export class SubService { 19 | x: string; 20 | constructor(x: string); 21 | hello(): string; 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /test/src/main/index.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import int = require('./lib/only-internal'); 4 | import exp = require('./lib/exported-sub'); 5 | import mod1 = require('external1'); 6 | 7 | export import Foo = require('./Foo'); 8 | /* 9 | Licence foo module v1.2.3 - MIT 10 | */ 11 | export function run(foo?: Foo): Foo { 12 | var foo = foo || new Foo(); 13 | int.bazz(int.bar(foo)); 14 | return foo; 15 | } 16 | 17 | // flep this 18 | export function flep(): exp.ExternalContainer { 19 | return new exp.ExternalContainer(); 20 | } 21 | 22 | // bar that 23 | export function bar(): mod1.SomeType { 24 | return new mod1.SomeType(); 25 | } 26 | -------------------------------------------------------------------------------- /test/expected/excludeExp/foo-mx.d.ts: -------------------------------------------------------------------------------- 1 | // Dependencies for this module: 2 | // ../../src/typings/external.d.ts 3 | 4 | declare module 'foo-mx' { 5 | import exp = require('foo-mx/lib/exported-sub'); 6 | import mod1 = require('external1'); 7 | export import Foo = require('foo-mx/Foo'); 8 | export function run(foo?: Foo): Foo; 9 | export function flep(): exp.ExternalContainer; 10 | export function bar(): mod1.SomeType; 11 | } 12 | 13 | declare module 'foo-mx/Foo' { 14 | class Foo { 15 | foo: string; 16 | constructor(secret?: string); 17 | /** 18 | * Bars the foo. 19 | */ 20 | barFoo(): void; 21 | /** Foos the baz. */ 22 | fooBaz(): void; 23 | } 24 | export = Foo; 25 | } 26 | -------------------------------------------------------------------------------- /test/expected/excludeFunc/foo-mx.d.ts: -------------------------------------------------------------------------------- 1 | // Dependencies for this module: 2 | // ../../src/typings/external.d.ts 3 | 4 | declare module 'foo-mx' { 5 | import exp = require('foo-mx/lib/exported-sub'); 6 | import mod1 = require('external1'); 7 | export import Foo = require('foo-mx/Foo'); 8 | export function run(foo?: Foo): Foo; 9 | export function flep(): exp.ExternalContainer; 10 | export function bar(): mod1.SomeType; 11 | } 12 | 13 | declare module 'foo-mx/Foo' { 14 | class Foo { 15 | foo: string; 16 | constructor(secret?: string); 17 | /** 18 | * Bars the foo. 19 | */ 20 | barFoo(): void; 21 | /** Foos the baz. */ 22 | fooBaz(): void; 23 | } 24 | export = Foo; 25 | } 26 | -------------------------------------------------------------------------------- /lib/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.5.3", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "module": "commonjs", 6 | "isolatedModules": false, 7 | "experimentalDecorators": true, 8 | "emitDecoratorMetadata": true, 9 | "declaration": false, 10 | "noImplicitAny": true, 11 | "removeComments": true, 12 | "noLib": false, 13 | "preserveConstEnums": false, 14 | "suppressImplicitAnyIndexErrors": false 15 | }, 16 | "filesGlob": [ 17 | "./**/*.ts", 18 | "../typings/**/*.ts", 19 | "./**/*.tsx", 20 | "!./node_modules/**/*" 21 | ], 22 | "files": [ 23 | "./index.ts", 24 | "../typings/bundle.d.ts", 25 | "../typings/detect-indent/detect-indent.d.ts", 26 | "../typings/glob/glob.d.ts", 27 | "../typings/minimatch/minimatch.d.ts", 28 | "../typings/mkdirp/mkdirp.d.ts", 29 | "../typings/node/node.d.ts" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /test/expected/includeExclude/foo-mx.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare module 'foo-mx' { 3 | import exp = require('foo-mx/lib/exported-sub'); 4 | import mod1 = require('foo-mx/index//external1'); 5 | export import Foo = require('foo-mx/Foo'); 6 | export function run(foo?: Foo): Foo; 7 | export function flep(): exp.ExternalContainer; 8 | export function bar(): mod1.SomeType; 9 | } 10 | 11 | declare module 'foo-mx/index//external1' { 12 | export class SomeType { 13 | foo(): void; 14 | } 15 | } 16 | 17 | declare module 'foo-mx/index//external2' { 18 | export class AnotherType { 19 | foo(): void; 20 | } 21 | } 22 | 23 | declare module 'foo-mx/Foo' { 24 | class Foo { 25 | foo: string; 26 | constructor(secret?: string); 27 | /** 28 | * Bars the foo. 29 | */ 30 | barFoo(): void; 31 | /** Foos the baz. */ 32 | fooBaz(): void; 33 | } 34 | export = Foo; 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Bart van der Schoor 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /test/expected/out/fizz/buzz.d.ts: -------------------------------------------------------------------------------- 1 | // Dependencies for this module: 2 | // ../../src/typings/external.d.ts 3 | 4 | declare module 'foo-mx' { 5 | import exp = require('foo-mx/lib/exported-sub'); 6 | import mod1 = require('external1'); 7 | export import Foo = require('foo-mx/Foo'); 8 | export function run(foo?: Foo): Foo; 9 | export function flep(): exp.ExternalContainer; 10 | export function bar(): mod1.SomeType; 11 | } 12 | 13 | declare module 'foo-mx/lib/exported-sub' { 14 | import Foo = require('foo-mx/Foo'); 15 | import mod2 = require('external2'); 16 | export class ExternalContainer { 17 | something: mod2.AnotherType; 18 | } 19 | export function bar(foo: Foo): string; 20 | export function bazz(value: string, option?: boolean): string; 21 | } 22 | 23 | declare module 'foo-mx/Foo' { 24 | class Foo { 25 | foo: string; 26 | constructor(secret?: string); 27 | /** 28 | * Bars the foo. 29 | */ 30 | barFoo(): void; 31 | /** Foos the baz. */ 32 | fooBaz(): void; 33 | } 34 | export = Foo; 35 | } 36 | -------------------------------------------------------------------------------- /test/expected/remove/foo-mx.d.ts: -------------------------------------------------------------------------------- 1 | // Dependencies for this module: 2 | // ../../src/typings/external.d.ts 3 | 4 | declare module 'foo-mx' { 5 | import exp = require('foo-mx/lib/exported-sub'); 6 | import mod1 = require('external1'); 7 | export import Foo = require('foo-mx/Foo'); 8 | export function run(foo?: Foo): Foo; 9 | export function flep(): exp.ExternalContainer; 10 | export function bar(): mod1.SomeType; 11 | } 12 | 13 | declare module 'foo-mx/lib/exported-sub' { 14 | import Foo = require('foo-mx/Foo'); 15 | import mod2 = require('external2'); 16 | export class ExternalContainer { 17 | something: mod2.AnotherType; 18 | } 19 | export function bar(foo: Foo): string; 20 | export function bazz(value: string, option?: boolean): string; 21 | } 22 | 23 | declare module 'foo-mx/Foo' { 24 | class Foo { 25 | foo: string; 26 | constructor(secret?: string); 27 | /** 28 | * Bars the foo. 29 | */ 30 | barFoo(): void; 31 | /** Foos the baz. */ 32 | fooBaz(): void; 33 | } 34 | export = Foo; 35 | } 36 | -------------------------------------------------------------------------------- /test/expected/default/foo-mx.d.ts: -------------------------------------------------------------------------------- 1 | // Dependencies for this module: 2 | // ../../src/typings/external.d.ts 3 | 4 | declare module 'foo-mx' { 5 | import exp = require('foo-mx/lib/exported-sub'); 6 | import mod1 = require('external1'); 7 | export import Foo = require('foo-mx/Foo'); 8 | export function run(foo?: Foo): Foo; 9 | export function flep(): exp.ExternalContainer; 10 | export function bar(): mod1.SomeType; 11 | } 12 | 13 | declare module 'foo-mx/lib/exported-sub' { 14 | import Foo = require('foo-mx/Foo'); 15 | import mod2 = require('external2'); 16 | export class ExternalContainer { 17 | something: mod2.AnotherType; 18 | } 19 | export function bar(foo: Foo): string; 20 | export function bazz(value: string, option?: boolean): string; 21 | } 22 | 23 | declare module 'foo-mx/Foo' { 24 | class Foo { 25 | foo: string; 26 | constructor(secret?: string); 27 | /** 28 | * Bars the foo. 29 | */ 30 | barFoo(): void; 31 | /** Foos the baz. */ 32 | fooBaz(): void; 33 | } 34 | export = Foo; 35 | } 36 | -------------------------------------------------------------------------------- /test/expected/seprinnew/bar-mx.d.ts: -------------------------------------------------------------------------------- 1 | // Dependencies for this module: //$ 2 | // ../../src/typings/external.d.ts //$ 3 | //$ 4 | declare module 'bar-mx' { //$ 5 | import exp = require('--bar-mx#lib#exported-sub'); //$ 6 | import mod1 = require('external1'); //$ 7 | export import Foo = require('--bar-mx#Foo'); //$ 8 | export function run(foo?: Foo): Foo; //$ 9 | export function flep(): exp.ExternalContainer; //$ 10 | export function bar(): mod1.SomeType; //$ 11 | } //$ 12 | //$ 13 | declare module '--bar-mx#lib#exported-sub' { //$ 14 | import Foo = require('--bar-mx#Foo'); //$ 15 | import mod2 = require('external2'); //$ 16 | export class ExternalContainer { //$ 17 | something: mod2.AnotherType; //$ 18 | } //$ 19 | export function bar(foo: Foo): string; //$ 20 | export function bazz(value: string, option?: boolean): string; //$ 21 | } //$ 22 | //$ 23 | declare module '--bar-mx#Foo' { //$ 24 | class Foo { //$ 25 | foo: string; //$ 26 | constructor(secret?: string); //$ 27 | /** //$ 28 | * Bars the foo. //$ 29 | */ //$ 30 | barFoo(): void; //$ 31 | /** Foos the baz. */ //$ 32 | fooBaz(): void; //$ 33 | } //$ 34 | export = Foo; //$ 35 | } //$ 36 | //$ 37 | -------------------------------------------------------------------------------- /test/expected/externals/foo-mx.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare module 'foo-mx' { 3 | import exp = require('foo-mx/lib/exported-sub'); 4 | import mod1 = require('foo-mx/index//external1'); 5 | export import Foo = require('foo-mx/Foo'); 6 | export function run(foo?: Foo): Foo; 7 | export function flep(): exp.ExternalContainer; 8 | export function bar(): mod1.SomeType; 9 | } 10 | 11 | declare module 'foo-mx/index//external1' { 12 | export class SomeType { 13 | foo(): void; 14 | } 15 | } 16 | 17 | declare module 'foo-mx/index//external2' { 18 | export class AnotherType { 19 | foo(): void; 20 | } 21 | } 22 | 23 | declare module 'foo-mx/lib/exported-sub' { 24 | import Foo = require('foo-mx/Foo'); 25 | import mod2 = require('foo-mx/index//external2'); 26 | export class ExternalContainer { 27 | something: mod2.AnotherType; 28 | } 29 | export function bar(foo: Foo): string; 30 | export function bazz(value: string, option?: boolean): string; 31 | } 32 | 33 | declare module 'foo-mx/Foo' { 34 | class Foo { 35 | foo: string; 36 | constructor(secret?: string); 37 | /** 38 | * Bars the foo. 39 | */ 40 | barFoo(): void; 41 | /** Foos the baz. */ 42 | fooBaz(): void; 43 | } 44 | export = Foo; 45 | } 46 | -------------------------------------------------------------------------------- /test/expected/es6/foo-mx.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare module 'foo-mx' { 3 | export * from "foo-mx/sub"; 4 | import * as subB from "foo-mx/sub"; 5 | import { A } from "foo-mx/lib/subC"; 6 | import { bar } from "foo-mx/lib/subD"; 7 | import { foo as buzz } from "foo-mx/lib/subE"; 8 | export function indexA(): subB.A; 9 | export function indexB(): subB.B; 10 | export function indexC(): A; 11 | export function indexD(): typeof bar; 12 | export function indexE(): typeof buzz; 13 | } 14 | 15 | declare module 'foo-mx/sub' { 16 | export interface A { 17 | name: string; 18 | } 19 | export class B { 20 | name: string; 21 | } 22 | export default function test(): A; 23 | export function foo(): A; 24 | export function bar(): A; 25 | } 26 | 27 | declare module 'foo-mx/lib/subC' { 28 | export interface A { 29 | name: string; 30 | } 31 | export class B { 32 | name: string; 33 | } 34 | export default function test(): A; 35 | export function foo(): A; 36 | export function bar(): A; 37 | } 38 | 39 | declare module 'foo-mx/lib/subD' { 40 | export interface A { 41 | name: string; 42 | } 43 | export class B { 44 | name: string; 45 | } 46 | export default function test(): A; 47 | export function foo(): A; 48 | export function bar(): A; 49 | } 50 | 51 | declare module 'foo-mx/lib/subE' { 52 | export interface A { 53 | name: string; 54 | } 55 | export class B { 56 | name: string; 57 | } 58 | export default function test(): A; 59 | export function foo(): A; 60 | export function bar(): A; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dts-bundle", 3 | "version": "0.7.3", 4 | "description": "Export TypeScript .d.ts files as an external module definition", 5 | "keywords": [ 6 | "typescript", 7 | "definition", 8 | "bundle", 9 | "export", 10 | "d.ts" 11 | ], 12 | "bin": { 13 | "dts-bundle": "./lib/dts-bundle.js" 14 | }, 15 | "homepage": "https://github.com/grunt-ts/dts-bundle", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/grunt-ts/dts-bundle.git" 19 | }, 20 | "author": { 21 | "name": "Bart van der Schoor", 22 | "url": "https://github.com/Bartvds" 23 | }, 24 | "licenses": [ 25 | { 26 | "type": "MIT", 27 | "url": "https://raw.github.com/grunt-ts/dts-bundle/master/LICENSE-MIT.txt" 28 | } 29 | ], 30 | "bugs": { 31 | "url": "https://github.com/grunt-ts/dts-bundle/issues" 32 | }, 33 | "engines": { 34 | "node": ">= 0.10.0" 35 | }, 36 | "scripts": { 37 | "test": "grunt test" 38 | }, 39 | "main": "./index.js", 40 | "dependencies": { 41 | "@types/detect-indent": "0.1.30", 42 | "@types/glob": "5.0.30", 43 | "@types/mkdirp": "0.3.29", 44 | "@types/node": "8.0.0", 45 | "commander": "^2.9.0", 46 | "detect-indent": "^0.2.0", 47 | "glob": "^6.0.4", 48 | "mkdirp": "^0.5.0" 49 | }, 50 | "devDependencies": { 51 | "chai": "^1.10.0", 52 | "chai-fs": "0.1.0", 53 | "grunt": "^0.4.5", 54 | "grunt-contrib-clean": "^0.5.0", 55 | "grunt-contrib-jshint": "^0.10.0", 56 | "grunt-dtsm": "1.0.0", 57 | "grunt-mocha-test": "^0.11.0", 58 | "grunt-ts": "^6.0.0-beta.16", 59 | "jshint-path-reporter": "^0.1.3", 60 | "mocha-unfunk-reporter": "^0.4.0", 61 | "ncp": "^0.5.1", 62 | "typescript": "^2.3.4" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 'use strict'; 3 | 4 | grunt.loadNpmTasks('grunt-ts'); 5 | grunt.loadNpmTasks('grunt-mocha-test'); 6 | grunt.loadNpmTasks('grunt-contrib-clean'); 7 | 8 | grunt.initConfig({ 9 | pkg: grunt.file.readJSON('package.json'), 10 | clean: { 11 | cruft: { 12 | option: { 13 | dot: true 14 | }, 15 | src: [ 16 | 'tscommand-*.tmp.txt', 17 | 'test/**/.baseDir*' 18 | ] 19 | }, 20 | tmp: [ 21 | 'tmp/**/*', 22 | 'test/tmp/**/*' 23 | ], 24 | test: [ 25 | 'test/build/**/*' 26 | ] 27 | }, 28 | ts: { 29 | options: { 30 | fast: 'never', 31 | target: 'es5', 32 | module: 'commonjs', 33 | declaration: true, 34 | removeComments: false, 35 | sourceMap: false 36 | }, 37 | main: { 38 | src: [ 39 | './lib/index.ts', 40 | ], 41 | options: { 42 | "target": "es5", 43 | "module": "commonjs", 44 | "isolatedModules": false, 45 | "experimentalDecorators": true, 46 | "emitDecoratorMetadata": true, 47 | "declaration": false, 48 | "noImplicitAny": true, 49 | "removeComments": true, 50 | "noLib": false, 51 | "preserveConstEnums": false, 52 | "suppressImplicitAnyIndexErrors": false 53 | } 54 | }, 55 | test: { 56 | src: ['test/src/main/index.ts'], 57 | outDir: 'test/build/sub/' 58 | }, 59 | testEs6: { 60 | src: ['test/src/es6/index.ts'], 61 | outDir: 'test/build/es6/' 62 | }, 63 | testCommonJs: { 64 | src: ['test/src/commonjs/index.ts'], 65 | outDir: 'test/build/commonjs' 66 | }, 67 | testConflicts: { 68 | src: ['test/src/conflicts/dirname/index.ts'], 69 | outDir: 'test/build/conflicts/dirname' 70 | } 71 | }, 72 | mochaTest: { 73 | options: { 74 | timeout: 5000, 75 | reporter: 'mocha-unfunk-reporter' 76 | }, 77 | all: { 78 | src: 'test/test.js' 79 | } 80 | } 81 | }); 82 | 83 | grunt.registerTask('prep', [ 84 | 'clean:tmp', 85 | 'clean:test', 86 | 'clean:cruft' 87 | ]); 88 | 89 | grunt.registerTask('test', [ 90 | 'prep', 91 | 'ts:test', 92 | 'ts:testEs6', 93 | 'ts:testCommonJs', 94 | 'ts:testConflicts', 95 | 'run' 96 | ]); 97 | 98 | grunt.registerTask('run', [ 99 | 'clean:tmp', 100 | 'ts:main', 101 | 'mochaTest:all', 102 | 'sweep' 103 | ]); 104 | 105 | grunt.registerTask('prepublish', [ 106 | 'build', 107 | 'ts:test', 108 | 'mochaTest:all', 109 | 'sweep' 110 | ]); 111 | 112 | grunt.registerTask('sweep', [ 113 | 'clean:cruft' 114 | ]); 115 | 116 | grunt.registerTask('default', ['test']); 117 | }; 118 | -------------------------------------------------------------------------------- /lib/dts-bundle.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Remember remove \r chars at end of lines. 4 | 5 | var pkg = require('../package'); 6 | var program = require('commander'); 7 | var dts = require("./index"); 8 | var path = require('path'); 9 | var os = require('os'); 10 | 11 | function mapOptions(argObj) { 12 | var result = argObj.configJson ? require(path.resolve(argObj.configJson)) : {}; 13 | 14 | var optList = [ 15 | "main", 16 | "name", 17 | "baseDir", 18 | "out", 19 | //"newline", // Manual 20 | //"indent", // not implemented 21 | "prefix", 22 | // "separator", not implemented 23 | "externals", 24 | //"exclude", // not implemented 25 | "removeSource", 26 | "verbose", 27 | "referenceExternals", 28 | "emitOnIncludedFileNotFound", 29 | "emitOnNoIncludedFileNotFound", 30 | "outputAsModuleFolder", 31 | "headerPath" 32 | ]; 33 | 34 | optList.forEach(function (optName) { 35 | if (argObj.hasOwnProperty(optName)) 36 | result[optName] = argObj[optName]; 37 | }, this); 38 | 39 | if (argObj.hasOwnProperty("newline")) { 40 | switch (argObj.newline) { 41 | case "unix": 42 | result.newline = "\n"; 43 | break; 44 | case "windows": 45 | result.newline = "\r\n"; 46 | break; 47 | case "currentOsDefault": 48 | result.newline = os.EOL; 49 | break; 50 | } 51 | } 52 | return result; 53 | } 54 | 55 | function callBundle(options) { 56 | if (!options.name || !options.main) { 57 | console.log("'name' and 'main' parameters are required. --help for get option list.") 58 | process.exit(1); 59 | } 60 | return dts.bundle(options); 61 | } 62 | 63 | program 64 | .version(pkg.version) 65 | .option('--configJson ', "path to json config file. Load it first and override options with additional parameters") 66 | .option('--name ', 'name of module likein package.json *required') 67 | .option('--main ', 'path to entry-point (see documentation) *required') 68 | .option('--baseDir [value]', 'base directory to be used for discovering type declarations') 69 | .option('--out [value]', 'path of output file. Is relative from baseDir but you can use absolute paths. ') 70 | .option('--externals', 'include typings outside of the "baseDir" (i.e. like node.d.ts)') 71 | .option('--referenceExternals', 'reference external modules as tags *** Experimental, TEST NEEDED') 72 | //.option('--exclude ', 'filter to exclude typings, either a RegExp or a callback. match path relative to opts.baseDir') 73 | .option('--removeSource', 'delete all source typings (i.e. "/**/*.d.ts")') 74 | .option('--newline [style]', 'newline style to use in output file => unix|windows|currentOsDefault', /^(unix|windows|currentOsDefault)$/i) 75 | //.option('--indent', 'indentation to use in output file') 76 | .option('--prefix [value]', 'prefix for rewriting module names') 77 | // .option('--separator [value]', 'separator for rewriting module "path" names') 78 | .option('--verbose', 'enable verbose mode, prints detailed info about all references and includes/excludes') 79 | .option('--emitOnIncludedFileNotFound', 'emit although included files not found. See readme "Files not found" section. ') 80 | .option('--emitOnNoIncludedFileNotFound', 'emit although no included files not found. See readme "Files not found" section. ') 81 | .option('--outputAsModuleFolder', 'output as module folder format (no declare module) . See readme "Module folders" section.') 82 | .option('--headerPath [value]', 'path to file that contains the header') 83 | .parse(process.argv); 84 | 85 | console.log("%s version %s\n%s\n", pkg.name, pkg.version, pkg.description); 86 | 87 | var options = mapOptions(program); 88 | 89 | var result = callBundle(options); 90 | 91 | if (!result.emitted) { 92 | console.log("Result no emitted - use verbose to see details."); 93 | process.exit(1); 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dts-bundle 2 | 3 | [![Build Status](https://travis-ci.org/TypeStrong/dts-bundle.svg)](https://travis-ci.org/TypeStrong/dts-bundle) [![NPM version](https://badge.fury.io/js/dts-bundle.svg)](http://badge.fury.io/js/dts-bundle) [![Dependency Status](https://david-dm.org/TypeStrong/dts-bundle.svg)](https://david-dm.org/TypeStrong/dts-bundle) [![devDependency Status](https://david-dm.org/TypeStrong/dts-bundle/dev-status.svg)](https://david-dm.org/TypeStrong/dts-bundle#info=devDependencies) 4 | 5 | > Export TypeScript .d.ts files as an external module definition 6 | 7 | This module is a naïve string-based approach at generating bundles from the .d.ts declaration files generated by a TypeScript compiler. 8 | 9 | The main use-case is generating definition for npm/bower modules written in TypeScript (commonjs/amd) so the TypeScript code should following the external-module pattern (using `import/export`'s and `--outDir`). 10 | 11 | :warning: Experimental; use with care. 12 | 13 | This module was born out of necessity and frustration. It is a little hacky but at least it seems to work.. 14 | 15 | - Original code was extracted from an ad-hoc Grunt-task so for now it is fully synchronous with no error feedback. 16 | - It works by line-by-line string operations so please [report](https://github.com/grunt-ts/dts-bundle/issues) any edge-cases. 17 | 18 | 19 | ## Usage 20 | 21 | 1) Get it from npm: 22 | 23 | ```` 24 | npm install dts-bundle 25 | ```` 26 | 27 | 2) Compile your modules with the TypeScript compiler of your choice with the `--declaration` and `--outDir` flags (and probably `--module commonjs` too). 28 | 29 | Something like: 30 | 31 | ````shell 32 | tsc src/index.ts --declaration --module commonjs --target es5 --outDir build 33 | ```` 34 | 35 | 3) Run `dts-bundle` (you can run it from cli, see "Command line" section. 36 | 37 | Let's assume the project is called `cool-project` and the main module is `build/index.js` with a `build/index.d.ts`: 38 | 39 | ````js 40 | var dts = require('dts-bundle'); 41 | 42 | dts.bundle({ 43 | name: 'cool-project', 44 | main: 'build/index.d.ts' 45 | }); 46 | ```` 47 | 48 | This will traverse all references and imports for the .d.ts files of your sub-modules and write `build/cool-project.d.ts` with the bundle of all 'local' imports. 49 | 50 | **Optional:** 51 | 52 | 4) Bonus points if you link the generated definition in your package.json's (or bower.json) `typescript` element: 53 | 54 | ````json 55 | { 56 | "name": "cool-project", 57 | "version": "0.1.3", 58 | 59 | "typescript": { 60 | "definition": "build/cool-project.d.ts" 61 | } 62 | } 63 | ```` 64 | 65 | Using this makes the definition findable for tooling, for example the [TypeScript Definitions package manager](https://github.com/DefinitelyTyped/tsd) (from v0.6.x) can auto-link these into `tsd.d.ts` bundle file. 66 | 67 | 68 | ### Wrappers 69 | 70 | There is also a Grunt plugin [grunt-dts-bundle](https://www.github.com/grunt-ts/grunt-dts-bundle) that goes well with Grunt based compilers, like [grunt-ts](https://www.github.com/grunt-ts/grunt-ts) or [grunt-typescript](https://github.com/k-maru/grunt-typescript). 71 | 72 | 73 | ## Options 74 | 75 | Example of all options: 76 | 77 | ````js 78 | var opts = { 79 | 80 | // Required 81 | 82 | // name of module likein package.json 83 | // - used to declare module & import/require 84 | name: 'cool-project', 85 | // path to entry-point (generated .d.ts file for main module) 86 | // if you want to load all .d.ts files from a path recursively you can use "path/project/**/*.d.ts" 87 | // ^ *** Experimental, TEST NEEDED, see "All .d.ts files" section 88 | // - either relative or absolute 89 | main: 'build/index.d.ts', 90 | 91 | // Optional 92 | 93 | // base directory to be used for discovering type declarations (i.e. from this project itself) 94 | // - default: dirname of main 95 | baseDir: 'build', 96 | // path of output file. Is relative from baseDir but you can use absolute paths. 97 | // if starts with "~/" then is relative to current path. See https://github.com/TypeStrong/dts-bundle/issues/26 98 | // ^ *** Experimental, TEST NEEDED 99 | // - default: "/.d.ts" 100 | out: 'dist/cool-project.d.ts', 101 | // include typings outside of the 'baseDir' (i.e. like node.d.ts) 102 | // - default: false 103 | externals: false, 104 | // reference external modules as tags *** Experimental, TEST NEEDED 105 | // - default: false 106 | referenceExternals: false, 107 | // filter to exclude typings, either a RegExp or a callback. match path relative to opts.baseDir 108 | // - RegExp: a match excludes the file 109 | // - function: (file:String, external:Boolean) return true to exclude, false to allow 110 | // - always use forward-slashes (even on Windows) 111 | // - default: *pass* 112 | exclude: /^defs\/$/, 113 | // delete all source typings (i.e. "/**/*.d.ts") 114 | // - default: false 115 | removeSource: false, 116 | // newline to use in output file 117 | newline: os.EOL, 118 | // indentation to use in output file 119 | // - default 4 spaces 120 | indent: ' ', 121 | // prefix for rewriting module names 122 | // - default '' 123 | prefix: '__', 124 | // separator for rewriting module 'path' names 125 | // - default: forward slash (like sub-modules) 126 | separator: '/', 127 | // enable verbose mode, prints detailed info about all references and includes/excludes 128 | // - default: false 129 | verbose: false, 130 | // emit although included files not found. See "Files not found" section. 131 | // *** Experimental, TEST NEEDED 132 | // - default: false 133 | emitOnIncludedFileNotFound: false, 134 | // emit although no included files not found. See "Files not found" section. 135 | // *** Experimental, TEST NEEDED 136 | // - default: false 137 | emitOnNoIncludedFileNotFound: false, 138 | // output d.ts as designed for module folder. (no declare modules) 139 | outputAsModuleFolder: false, 140 | // path to file that contains the header 141 | // // insert a header in output file. i.e.: http://definitelytyped.org/guides/contributing.html#header 142 | // - default: null 143 | headerPath: "path/to/header/file", 144 | // text of the the header 145 | // doesn't work with headerPath 146 | // // insert a header in output file. i.e.: http://definitelytyped.org/guides/contributing.html#header 147 | // - default: '' 148 | headerTex: "" 149 | }; 150 | 151 | // require module 152 | var dts = require('dts-bundle'); 153 | 154 | // run it 155 | dts.bundle(opts); 156 | ```` 157 | 158 | ### All `.d.ts` files 159 | 160 | Experimental - Test needed - https://github.com/TypeStrong/dts-bundle/issues/29 161 | 162 | You can bundle the definitions from for all files contained on a directory, and children directories. 163 | If you set `main` parameter to some path ended with `**/*.d.ts` then `dts-bundle` load all .d.ts files and generate a bundle. 164 | Internally `dts-bundle` builds a temporally file with export of the rest of the files. You can see it on verbose mode: i.e: 165 | 166 | ```` typescript 167 | // ## temporally main file ## 168 | export * from './Core/Bootstrap'; 169 | export * from './Core/ChangeDetection'; 170 | export * from './Core/ControllerDecorator'; 171 | export * from './Core/LifeCycle\LifeCycleHooks'; 172 | export * from './Decorators/Component'; 173 | export * from './Decorators/Input'; 174 | export * from './Decorators/Output'; 175 | export * from './Directives/ngPropertyBinding'; 176 | export * from './Events/EventEmitter'; 177 | export * from './Expressions/ExpressionParser'; 178 | export * from './Expressions/Ng1Lexer\Lexer'; 179 | export * from './Ng2Emulation'; 180 | export * from './Templates/HtmlParser\Parser'; 181 | export * from './Templates/HtmlParser\ParserRule'; 182 | export * from './Templates/HtmlParser\Rules\EventBindingRule'; 183 | export * from './Templates/HtmlParser\Rules\TwoWayBindingRule'; 184 | export * from './Utils/AngularHelpers'; 185 | ```` 186 | 187 | Then `dts-bundle` processes this file. When finish the temporally file is deleted. 188 | 189 | ### Module folders 190 | NPM introduced support for in-package typings, 191 | it is done by adding typings key into package.json file, which should refer to the 192 | typings file. 193 | when this is the case, the d.ts file is threated as module folder, and declare module 194 | is not allowed. 195 | 196 | * `outputAsModuleFolder`. When using this option output d.ts will be compatible with module folder. which means, no declare module are used, 197 | and all internal references are removed as they are merged into the output d.ts. 198 | 199 | ### Files not found. 200 | 201 | Experimental - Test needed - 202 | 203 | `dts-bundle` expects to find all references for all modules. Goes over all files referenced and tries to load each file to get the definitions, 204 | when he loads all files `dts-bundle` determines if include each one in the final bundle. If some file is not found dts-bundle doesn't emit the 205 | result file. You could want to emit the result file although some file are not found. The file not found can be an included or exclued file in 206 | the bundle then you have two options to control these cases: 207 | 208 | * `emitOnIncludedFileNotFound`. When there are files not found and these file should be included in the bundle, 209 | `dts-bundle` writes the output file if `emitOnIncludedFileNotFound` is true. This allow you to have external file 210 | definitions that you will load by other way in your final project. 211 | * `emitOnNoIncludedFileNotFound`. When there are files not found and these file shouldn't be included in the bundle, 212 | `dts-bundle` writes the output file if `emitOnNoIncludedFileNotFound` is true. This allow you don't to include external 213 | typings in your temporally output compile path. 214 | 215 | ## Return value 216 | 217 | Experimental - Test needed - 218 | 219 | `bundle` function return an object that implement this interface: 220 | 221 | ```` typescript 222 | export interface BundleResult { 223 | // a map with parse result per each process module. 224 | fileMap: { [moduleName: string]: Result; }; 225 | // list of files not found that should be included in the bundle. 226 | includeFilesNotFound: string[]; 227 | // list of files not found that shouldn`t be no included in the bundle. 228 | noIncludeFilesNotFound: string[]; 229 | // true if dts-bunlde wrote the result, false otherwise. 230 | emitted?: boolean; 231 | // original options passed to the function. 232 | options: Options; 233 | } 234 | ```` 235 | 236 | You can use the return value to determine if continue your gulp or grunt task or stop and emit an error. 237 | 238 | # Command line 239 | 240 | You can use `dts-bundle` from command line, its allow you use it from npm scripts [ see #13 ](https://github.com/TypeStrong/dts-bundle/issues/13). 241 | You have to install it using -g: 242 | 243 | ```` 244 | npm install dts-bundle -g 245 | ```` 246 | 247 | You can use the following options: 248 | 249 | ```` 250 | Usage: dts-bundle [options] 251 | 252 | Options: 253 | 254 | -h, --help output usage information 255 | -V, --version output the version number 256 | --configJson path to json config file. Load it first and override options with additional parameters 257 | --name name of module likein package.json *required 258 | --main path to entry-point (see documentation) *required 259 | --baseDir [value] base directory to be used for discovering type declarations 260 | --out [value] path of output file. Is relative from baseDir but you can use absolute paths. 261 | --externals include typings outside of the "baseDir" (i.e. like node.d.ts) 262 | --referenceExternals reference external modules as tags 263 | --removeSource delete all source typings (i.e. "/**/*.d.ts") 264 | --newline [style] newline style to use in output file => unix|windows|currentOsDefault 265 | --prefix [value] prefix for rewriting module names 266 | --verbose enable verbose mode, prints detailed info about all references and includes/excludes 267 | --emitOnIncludedFileNotFound emit although included files not found. See readme "Files not found" section. 268 | --emitOnNoIncludedFileNotFound emit although no included files not found. See readme "Files not found" section. 269 | --outputAsModuleFolder output as module folder format (no declare module) . See readme "Module folders" section. 270 | --headerPath [value] path to file that contains the header 271 | --headerText [value] text of the header. Doesn't work with headerPath. 272 | ```` 273 | 274 | For example: 275 | ```` 276 | dts-bundle --name cool-project --main build/output/index.d.ts 277 | ```` 278 | You can load config from a json file: 279 | ```` json 280 | // dts-bundle.json file 281 | { 282 | "name": "cool-project", 283 | "main": "tmp/out/index.d.ts" 284 | } 285 | ```` 286 | Run this way: 287 | ```` 288 | dts-bundle --configJson dts-bundle.json 289 | ```` 290 | And can override properties: 291 | ```` 292 | dts-bundle --configJson dts-bundle.json --name coolest 293 | ```` 294 | Emitting `tmp/out/cooles.d.ts`. 295 | 296 | 297 | ## Todo 298 | 299 | - add feature to create a DefinitelyTyped header (using `definition-header` package) 300 | - add feature to auto-update package.json/bower.json with the definition link 301 | - find time to implement a parser based solution 302 | - run IO asyncronous 303 | 304 | 305 | # History 306 | 307 | * 0.4.x Several features. 308 | * CLI command 309 | * Support not found file (`emitOnIncludedFileNotFound` and `emitOnNoIncludedFileNotFound`) 310 | * Output relative to current path using "~/" 311 | * referenceExternals option 312 | * All files feature using `**/*.d.ts` on `main` option 313 | * Return a object with the result 314 | * Add Header using `headerPath` 315 | * 0.3.x - Support es6 module syntax & rewrite by TypeScript 316 | * 0.2.x - Fixed bugs & added many options (thanks @poelstra) 317 | * 0.1.x - First release 318 | 319 | ## Key People 320 | 321 | * [Javier Ros (tolemaC)](http://github.com/tolemac) : current lead 322 | * [Bart van der Schoor](https://github.com/Bartvds) : original creator 323 | 324 | ## Contributions 325 | 326 | They are very welcome. Beware this module is a quick hack-job so good luck! 327 | 328 | * Martin Poelstra (@poelstra): Exclude 'external' typings, optional debug output, improved configurability. 329 | 330 | ## License 331 | 332 | Licensed under the MIT license. 333 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var glob = require('glob'); 4 | var chai = require('chai'); 5 | var mkdirp = require('mkdirp'); 6 | var ncp = require('ncp'); 7 | 8 | var execSync = require('child_process').execSync; 9 | var util = require("util"); 10 | 11 | chai.use(require('chai-fs')); 12 | chai.config.includeStack = true; 13 | 14 | var assert = chai.assert; 15 | 16 | var dts = require('../index'); 17 | 18 | var baseDir = __dirname; 19 | var buildDir = path.resolve(__dirname, 'build', 'sub'); 20 | var expectDir = path.resolve(__dirname, 'expected'); 21 | var tmpDir = path.resolve(__dirname, 'tmp'); 22 | 23 | var bomOptExp = /^\uFEFF?/; 24 | 25 | function getFile(f) { 26 | return fs.readFileSync(f, 'utf8').replace(bomOptExp, '').replace(/\s*$/, ''); 27 | } 28 | 29 | function fixPaths(arr) { 30 | return arr.map(function(p) { 31 | return p.replace('/', path.sep); 32 | }).sort(); 33 | } 34 | 35 | function assertFiles(base, expHave, expNot) { 36 | var have = fixPaths(glob.sync('**/*.d.ts', {cwd: base})); 37 | 38 | assert.deepEqual(have, fixPaths(expHave), base); 39 | } 40 | 41 | function testit(name, assertion, run) { 42 | var call = function (done) { 43 | var testDir = path.join(tmpDir, name); 44 | var expDir = path.join(expectDir, name); 45 | 46 | mkdirp.sync(testDir); 47 | 48 | ncp.ncp(buildDir, testDir, function (err) { 49 | if (err) { 50 | done(err); 51 | return; 52 | } 53 | assertion(testDir, expDir); 54 | done(); 55 | }); 56 | }; 57 | 58 | var label = 'bundle ' + name; 59 | 60 | if (run === 'skip') { 61 | it.skip(label, call); 62 | } 63 | else if (run === 'only') { 64 | it.only(label, call); 65 | } 66 | else { 67 | it(label, call); 68 | } 69 | } 70 | 71 | describe('dts bundle', function () { 72 | 73 | testit('default', function (actDir, expDir) { 74 | var result = dts.bundle({ 75 | name: 'foo-mx', 76 | main: path.join(actDir, 'index.d.ts'), 77 | newline: '\n', 78 | verbose: true, 79 | headerPath: "none" 80 | }); 81 | var name = 'foo-mx.d.ts'; 82 | var actualFile = path.join(actDir, name); 83 | assert.isTrue(result.emitted, "not emit " + actualFile); 84 | var expectedFile = path.join(expDir, name); 85 | assertFiles(actDir, [ 86 | name, 87 | 'index.d.ts', 88 | 'Foo.d.ts', 89 | 'lib/exported-sub.d.ts', 90 | 'lib/only-internal.d.ts' 91 | ]); 92 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 93 | }); 94 | 95 | testit('default_cli', function (actDir, expDir) { 96 | expDir = expDir.substr(0, expDir.length - 4); // expDir is the same without "_cli" suffix 97 | execSync(util.format("node ./lib/dts-bundle.js --name foo-mx --main %s --newline unix --verbose --headerPath none", path.join(actDir, 'index.d.ts'))); 98 | var name = 'foo-mx.d.ts'; 99 | var actualFile = path.join(actDir, name); 100 | var expectedFile = path.join(expDir, name); 101 | assertFiles(actDir, [ 102 | name, 103 | 'index.d.ts', 104 | 'Foo.d.ts', 105 | 'lib/exported-sub.d.ts', 106 | 'lib/only-internal.d.ts' 107 | ]); 108 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 109 | }); 110 | 111 | testit('remove', function (actDir, expDir) { 112 | var result = dts.bundle({ 113 | name: 'foo-mx', 114 | main: path.join(actDir, 'index.d.ts'), 115 | removeSource: true, 116 | newline: '\n', 117 | verbose: true, 118 | headerPath: "none" 119 | }); 120 | var name = 'foo-mx.d.ts'; 121 | var actualFile = path.join(actDir, name); 122 | assert.isTrue(result.emitted, "not emit " + actualFile); 123 | var expectedFile = path.join(expDir, name); 124 | assertFiles(actDir, [ 125 | name 126 | ]); 127 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 128 | }); 129 | 130 | testit('remove_cli', function (actDir, expDir) { 131 | expDir = expDir.substr(0, expDir.length - 4); // expDir is the same without "_cli" suffix 132 | execSync(util.format("node ./lib/dts-bundle --name foo-mx --main %s --removeSource --newline unix --verbose --headerPath none", path.join(actDir, 'index.d.ts'))); 133 | var name = 'foo-mx.d.ts'; 134 | var actualFile = path.join(actDir, name); 135 | var expectedFile = path.join(expDir, name); 136 | assertFiles(actDir, [ 137 | name 138 | ]); 139 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 140 | }); 141 | 142 | testit('out', function (actDir, expDir) { 143 | var result = dts.bundle({ 144 | name: 'foo-mx', 145 | main: path.join(actDir, 'index.d.ts'), 146 | out: path.join(actDir, 'fizz', 'buzz.d.ts'), 147 | newline: '\n', 148 | verbose: true, 149 | headerPath: "none" 150 | }); 151 | var name = path.join('fizz', 'buzz.d.ts'); 152 | var actualFile = path.join(actDir, name); 153 | assert.isTrue(result.emitted, "not emit " + actualFile); 154 | var expectedFile = path.join(expDir, name); 155 | assertFiles(actDir, [ 156 | name, 157 | 'index.d.ts', 158 | 'Foo.d.ts', 159 | 'lib/exported-sub.d.ts', 160 | 'lib/only-internal.d.ts' 161 | ]); 162 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 163 | }); 164 | 165 | testit('out_cli', function (actDir, expDir) { 166 | expDir = expDir.substr(0, expDir.length - 4); // expDir is the same without "_cli" suffix 167 | execSync(util.format("node ./lib/dts-bundle --name foo-mx --main %s --out %s --newline unix --verbose --headerPath none", 168 | path.join(actDir, 'index.d.ts'), 169 | path.join(actDir, 'fizz', 'buzz.d.ts'))); 170 | var name = path.join('fizz', 'buzz.d.ts'); 171 | var actualFile = path.join(actDir, name); 172 | var expectedFile = path.join(expDir, name); 173 | assertFiles(actDir, [ 174 | name, 175 | 'index.d.ts', 176 | 'Foo.d.ts', 177 | 'lib/exported-sub.d.ts', 178 | 'lib/only-internal.d.ts' 179 | ]); 180 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 181 | }); 182 | 183 | testit('seprinnew', function (actDir, expDir) { 184 | var result = dts.bundle({ 185 | name: 'bar-mx', 186 | main: path.join(actDir, 'index.d.ts'), 187 | removeSource: true, 188 | prefix: '--', 189 | separator: '#', 190 | indent: '\t', 191 | newline: ' //$\n', 192 | verbose: true, 193 | headerPath: "none" 194 | }); 195 | var name = 'bar-mx.d.ts'; 196 | var actualFile = path.join(actDir, name); 197 | assert.isTrue(result.emitted, "not emit " + actualFile); 198 | var expectedFile = path.join(expDir, name); 199 | assertFiles(actDir, [ 200 | name 201 | ]); 202 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 203 | }); 204 | 205 | testit('seprinnew_cli', function (actDir, expDir) { 206 | expDir = expDir.substr(0, expDir.length - 4); // expDir is the same without "_cli" suffix 207 | execSync(util.format("node ./lib/dts-bundle --configJson ./test/seprinnew_cli-config.json --name bar-mx --main %s --removeSource --verbose --headerPath none", 208 | path.join(actDir, 'index.d.ts'), 209 | path.join(actDir, 'fizz', 'buzz.d.ts'))); 210 | var name = 'bar-mx.d.ts'; 211 | var actualFile = path.join(actDir, name); 212 | var expectedFile = path.join(expDir, name); 213 | assertFiles(actDir, [ 214 | name 215 | ]); 216 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 217 | }); 218 | 219 | testit('externals', function (actDir, expDir) { 220 | var result = dts.bundle({ 221 | name: 'foo-mx', 222 | main: path.join(actDir, 'index.d.ts'), 223 | externals: true, 224 | newline: '\n', 225 | verbose: true, 226 | headerPath: "none" 227 | }); 228 | var name = 'foo-mx.d.ts'; 229 | var actualFile = path.join(actDir, name); 230 | assert.isTrue(result.emitted, "not emit " + actualFile); 231 | var expectedFile = path.join(expDir, name); 232 | assertFiles(actDir, [ 233 | name, 234 | 'index.d.ts', 235 | 'Foo.d.ts', 236 | 'lib/exported-sub.d.ts', 237 | 'lib/only-internal.d.ts' 238 | ]); 239 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 240 | }); 241 | 242 | testit('externals_cli', function (actDir, expDir) { 243 | expDir = expDir.substr(0, expDir.length - 4); // expDir is the same without "_cli" suffix 244 | execSync(util.format("node ./lib/dts-bundle --name foo-mx --main %s --externals --newline unix --verbose --headerPath none", 245 | path.join(actDir, 'index.d.ts'))); 246 | var name = 'foo-mx.d.ts'; 247 | var actualFile = path.join(actDir, name); 248 | var expectedFile = path.join(expDir, name); 249 | assertFiles(actDir, [ 250 | name, 251 | 'index.d.ts', 252 | 'Foo.d.ts', 253 | 'lib/exported-sub.d.ts', 254 | 'lib/only-internal.d.ts' 255 | ]); 256 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 257 | }); 258 | 259 | testit('excludeExp', function (actDir, expDir) { 260 | var result = dts.bundle({ 261 | name: 'foo-mx', 262 | main: path.join(actDir, 'index.d.ts'), 263 | exclude: /exported\-sub/, 264 | newline: '\n', 265 | verbose: true, 266 | headerPath: "none" 267 | }); 268 | var name = 'foo-mx.d.ts'; 269 | var actualFile = path.join(actDir, name); 270 | assert.isTrue(result.emitted, "not emit " + actualFile); 271 | var expectedFile = path.join(expDir, name); 272 | assertFiles(actDir, [ 273 | name, 274 | 'index.d.ts', 275 | 'Foo.d.ts', 276 | 'lib/exported-sub.d.ts', 277 | 'lib/only-internal.d.ts' 278 | ]); 279 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 280 | }); 281 | 282 | //testit('excludeExp_cli', function (actDir, expDir) { }); // No exclude options available from CLI. 283 | 284 | testit('excludeFunc', function (actDir, expDir) { 285 | var result = dts.bundle({ 286 | name: 'foo-mx', 287 | main: path.join(actDir, 'index.d.ts'), 288 | exclude: function(file) { 289 | return /exported\-sub/.test(file); 290 | }, 291 | newline: '\n', 292 | verbose: true, 293 | headerPath: "none" 294 | }); 295 | var name = 'foo-mx.d.ts'; 296 | var actualFile = path.join(actDir, name); 297 | assert.isTrue(result.emitted, "not emit " + actualFile); 298 | var expectedFile = path.join(expDir, name); 299 | assertFiles(actDir, [ 300 | name, 301 | 'index.d.ts', 302 | 'Foo.d.ts', 303 | 'lib/exported-sub.d.ts', 304 | 'lib/only-internal.d.ts' 305 | ]); 306 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 307 | }); 308 | 309 | //testit('excludeFunc_cli', function (actDir, expDir) { }); // No exclude options available from CLI. 310 | 311 | testit('includeExclude', function (actDir, expDir) { 312 | var result = dts.bundle({ 313 | name: 'foo-mx', 314 | main: path.join(actDir, 'index.d.ts'), 315 | externals: true, 316 | exclude: /exported\-sub/, 317 | newline: '\n', 318 | verbose: true, 319 | headerPath: "none" 320 | }); 321 | var name = 'foo-mx.d.ts'; 322 | var actualFile = path.join(actDir, name); 323 | assert.isTrue(result.emitted, "not emit " + actualFile); 324 | var expectedFile = path.join(expDir, name); 325 | assertFiles(actDir, [ 326 | name, 327 | 'index.d.ts', 328 | 'Foo.d.ts', 329 | 'lib/exported-sub.d.ts', 330 | 'lib/only-internal.d.ts' 331 | ]); 332 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 333 | }); 334 | 335 | //testit('includeExclude_cli', function (actDir, expDir) { }); // No exclude options available from CLI. 336 | 337 | (function testit(name, assertion, run) { 338 | var buildDir = path.resolve(__dirname, 'build', 'conflicts', 'dirname'); 339 | var call = function (done) { 340 | var testDir = path.join(tmpDir, name); 341 | var expDir = path.join(expectDir, name); 342 | 343 | mkdirp.sync(testDir); 344 | 345 | ncp.ncp(buildDir, testDir, function (err) { 346 | if (err) { 347 | done(err); 348 | return; 349 | } 350 | assertion(testDir, expDir); 351 | done(); 352 | }); 353 | }; 354 | 355 | var label = 'bundle ' + name; 356 | 357 | if (run === 'skip') { 358 | it.skip(label, call); 359 | } 360 | else if (run === 'only') { 361 | it.only(label, call); 362 | } 363 | else { 364 | it(label, call); 365 | } 366 | })('conflicts_dirname', function (actDir, expDir) { 367 | var result = dts.bundle({ 368 | name: 'foo-mx', 369 | main: path.join(actDir, 'index.d.ts'), 370 | newline: '\n', 371 | verbose: true, 372 | headerPath: "none" 373 | }); 374 | var name = 'foo-mx.d.ts'; 375 | var actualFile = path.join(actDir, name); 376 | assert.isTrue(result.emitted, "not emit " + actualFile); 377 | var expectedFile = path.join(expDir, name); 378 | assertFiles(actDir, [ 379 | name, 380 | 'index.d.ts', 381 | 'file1.d.ts', 382 | 'file1/file2.d.ts', 383 | ]); 384 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 385 | }); 386 | 387 | (function testit(name, assertion, run) { 388 | var buildDir = path.resolve(__dirname, 'build', 'es6'); 389 | var call = function (done) { 390 | var testDir = path.join(tmpDir, name); 391 | var expDir = path.join(expectDir, name); 392 | 393 | mkdirp.sync(testDir); 394 | 395 | ncp.ncp(buildDir, testDir, function (err) { 396 | if (err) { 397 | done(err); 398 | return; 399 | } 400 | assertion(testDir, expDir); 401 | done(); 402 | }); 403 | }; 404 | 405 | var label = 'bundle ' + name; 406 | 407 | if (run === 'skip') { 408 | it.skip(label, call); 409 | } 410 | else if (run === 'only') { 411 | it.only(label, call); 412 | } 413 | else { 414 | it(label, call); 415 | } 416 | })('es6', function (actDir, expDir) { 417 | var result = dts.bundle({ 418 | name: 'foo-mx', 419 | main: path.join(actDir, '../es6', 'index.d.ts'), 420 | newline: '\n', 421 | verbose: true, 422 | headerPath: "none" 423 | }); 424 | var name = 'foo-mx.d.ts'; 425 | var actualFile = path.join(actDir, name); 426 | assert.isTrue(result.emitted, "not emit " + actualFile); 427 | var expectedFile = path.join(expDir, name); 428 | assertFiles(actDir, [ 429 | name, 430 | 'index.d.ts', 431 | 'lib/subC.d.ts', 432 | 'lib/subD.d.ts', 433 | 'lib/subE.d.ts', 434 | 'sub.d.ts' 435 | ]); 436 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 437 | }); 438 | 439 | (function testit(name, assertion, run) { 440 | var buildDir = path.resolve(__dirname, 'build', 'es6'); 441 | var call = function (done) { 442 | var testDir = path.join(tmpDir, name); 443 | var expDir = path.join(expectDir, name); 444 | 445 | mkdirp.sync(testDir); 446 | 447 | ncp.ncp(buildDir, testDir, function (err) { 448 | if (err) { 449 | done(err); 450 | return; 451 | } 452 | assertion(testDir, expDir); 453 | done(); 454 | }); 455 | }; 456 | 457 | var label = 'bundle ' + name; 458 | 459 | if (run === 'skip') { 460 | it.skip(label, call); 461 | } 462 | else if (run === 'only') { 463 | it.only(label, call); 464 | } 465 | else { 466 | it(label, call); 467 | } 468 | })('es6_cli', function (actDir, expDir) { 469 | expDir = expDir.substr(0, expDir.length - 4); // expDir is the same without "_cli" suffix 470 | execSync(util.format("node ./lib/dts-bundle --name foo-mx --main %s --newline unix --verbose --headerPath none", 471 | path.join(actDir, '../es6_cli', 'index.d.ts'))); 472 | var name = 'foo-mx.d.ts'; 473 | var actualFile = path.join(actDir, name); 474 | var expectedFile = path.join(expDir, name); 475 | assertFiles(actDir, [ 476 | name, 477 | 'index.d.ts', 478 | 'lib/subC.d.ts', 479 | 'lib/subD.d.ts', 480 | 'lib/subE.d.ts', 481 | 'sub.d.ts' 482 | ]); 483 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 484 | }); 485 | 486 | (function testit(name, assertion, run) { 487 | var buildDir = path.resolve(__dirname, 'build', 'commonjs'); 488 | var call = function (done) { 489 | var testDir = path.join(tmpDir, name); 490 | var expDir = path.join(expectDir, name); 491 | 492 | mkdirp.sync(testDir); 493 | 494 | ncp.ncp(buildDir, testDir, function (err) { 495 | if (err) { 496 | done(err); 497 | return; 498 | } 499 | assertion(testDir, expDir); 500 | done(); 501 | }); 502 | }; 503 | 504 | var label = 'bundle ' + name; 505 | 506 | if (run === 'skip') { 507 | it.skip(label, call); 508 | } 509 | else if (run === 'only') { 510 | it.only(label, call); 511 | } 512 | else { 513 | it(label, call); 514 | } 515 | })('commonjs', function (actDir, expDir) { 516 | var result = dts.bundle({ 517 | name: 'foo-mx', 518 | main: path.join(actDir, '../commonjs', 'index.d.ts'), 519 | newline: '\n', 520 | verbose: true, 521 | headerPath: "none" 522 | }); 523 | var name = 'foo-mx.d.ts'; 524 | var actualFile = path.join(actDir, name); 525 | assert.isTrue(result.emitted, "not emit " + actualFile); 526 | var expectedFile = path.join(expDir, name); 527 | assertFiles(actDir, [ 528 | name, 529 | 'index.d.ts', 530 | 'sub/index.d.ts', 531 | 'sub/sub.service.d.ts' 532 | ]); 533 | assert.strictEqual(getFile(actualFile), getFile(expectedFile)); 534 | }); 535 | }); 536 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as os from 'os'; 4 | import * as fs from 'fs'; 5 | import * as path from 'path'; 6 | import * as util from 'util'; 7 | import * as assert from 'assert'; 8 | import * as glob from 'glob'; 9 | import * as mkdirp from 'mkdirp'; 10 | import * as detectIndent from 'detect-indent'; 11 | 12 | let pkg = require('../package'); 13 | 14 | const dtsExp = /\.d\.ts$/; 15 | const bomOptExp = /^\uFEFF?/; 16 | 17 | const externalExp = /^([ \t]*declare module )(['"])(.+?)(\2[ \t]*{?.*)$/; 18 | const importExp = /^([ \t]*(?:export )?(?:import .+? )= require\()(['"])(.+?)(\2\);.*)$/; 19 | const importEs6Exp = /^([ \t]*(?:export|import) ?(?:(?:\* (?:as [^ ,]+)?)|.*)?,? ?(?:[^ ,]+ ?,?)(?:\{(?:[^ ,]+ ?,?)*\})? ?from )(['"])([^ ,]+)(\2;.*)$/; 20 | const referenceTagExp = /^[ \t]*\/\/\/[ \t]*.*$/; 21 | const identifierExp = /^\w+(?:[\.-]\w+)*$/; 22 | const fileExp = /^([\./].*|.:.*)$/; 23 | const privateExp = /^[ \t]*(?:static )?private (?:static )?/; 24 | const publicExp = /^([ \t]*)(static |)(public |)(static |)(.*)/; 25 | 26 | export interface Options { 27 | main: string; 28 | name: string; 29 | baseDir?: string; 30 | out?: string; 31 | newline?: string; 32 | indent?: string; 33 | outputAsModuleFolder?: boolean; 34 | prefix?: string; 35 | separator?: string; 36 | externals?: boolean; 37 | exclude?: { (file: string): boolean; } | RegExp; 38 | removeSource?: boolean; 39 | verbose?: boolean; 40 | referenceExternals?: boolean; 41 | emitOnIncludedFileNotFound?: boolean; 42 | emitOnNoIncludedFileNotFound?: boolean; 43 | headerPath: string; 44 | headerText: string; 45 | } 46 | 47 | export interface ModLine { 48 | original: string; 49 | modified?: string; 50 | skip?: boolean; 51 | } 52 | 53 | export interface Result { 54 | file: string; 55 | name: string; 56 | indent: string; 57 | exp: string; 58 | refs: string[]; 59 | externalImports: string[]; 60 | relativeImports: string[]; 61 | exports: string[]; 62 | lines: ModLine[]; 63 | importLineRef: ModLine[]; 64 | relativeRef: ModLine[]; 65 | fileExists: boolean; 66 | } 67 | 68 | export interface BundleResult { 69 | fileMap: { [name: string]: Result; }; 70 | includeFilesNotFound: string[]; 71 | noIncludeFilesNotFound: string[]; 72 | emitted?: boolean; 73 | options: Options; 74 | } 75 | 76 | export function bundle(options: Options): BundleResult { 77 | assert(typeof options === 'object' && options, 'options must be an object'); 78 | 79 | // if main ends with **/*.d.ts all .d.ts files will be loaded 80 | const allFiles = stringEndsWith(options.main, "**/*.d.ts"); 81 | 82 | // option parsing & validation 83 | const main = allFiles ? "*.d.ts" : options.main; 84 | const exportName = options.name; 85 | const _baseDir = (() => { 86 | let baseDir = optValue(options.baseDir, path.dirname(options.main)); 87 | if (allFiles) { 88 | baseDir = baseDir.substr(0, baseDir.length - 2); 89 | } 90 | return baseDir; 91 | })(); 92 | const out = optValue(options.out, exportName + '.d.ts').replace(/\//g, path.sep); 93 | 94 | const newline = optValue(options.newline, os.EOL); 95 | const indent = optValue(options.indent, ' '); 96 | const outputAsModuleFolder = optValue(options.outputAsModuleFolder, false); 97 | const prefix = optValue(options.prefix, ''); 98 | const separator = optValue(options.separator, '/'); 99 | 100 | const externals = optValue(options.externals, false); 101 | const exclude = optValue(options.exclude, null); 102 | const removeSource = optValue(options.removeSource, false); 103 | const referenceExternals = optValue(options.referenceExternals, false); 104 | const emitOnIncludedFileNotFound = optValue(options.emitOnIncludedFileNotFound, false); 105 | const emitOnNoIncludedFileNotFound = optValue(options.emitOnNoIncludedFileNotFound, false); 106 | const _headerPath = optValue(options.headerPath, null); 107 | const headerText = optValue(options.headerText, ''); 108 | 109 | // regular (non-jsdoc) comments are not actually supported by declaration compiler 110 | const comments = false; 111 | 112 | const verbose = optValue(options.verbose, false); 113 | 114 | assert.ok(main, 'option "main" must be defined'); 115 | assert.ok(exportName, 'option "name" must be defined'); 116 | 117 | assert(typeof newline === 'string', 'option "newline" must be a string'); 118 | assert(typeof indent === 'string', 'option "indent" must be a string'); 119 | assert(typeof prefix === 'string', 'option "prefix" must be a string'); 120 | assert(separator.length > 0, 'option "separator" must have non-zero length'); 121 | 122 | // turn relative paths into absolute paths 123 | const baseDir = path.resolve(_baseDir); 124 | let mainFile = allFiles ? path.resolve(baseDir, "**/*.d.ts") : path.resolve(main.replace(/\//g, path.sep)); 125 | const outFile = calcOutFilePath(out, baseDir); 126 | let headerData = '// Generated by dts-bundle v' + pkg.version + newline; 127 | const headerPath = _headerPath && _headerPath !== "none" ? path.resolve(_headerPath.replace(/\//g, path.sep)) : _headerPath; 128 | 129 | trace('### settings object passed ###'); 130 | traceObject(options); 131 | 132 | trace('### settings ###'); 133 | trace('main: %s', main); 134 | trace('name: %s', exportName); 135 | trace('out: %s', out); 136 | trace('baseDir: %s', baseDir); 137 | trace('mainFile: %s', mainFile); 138 | trace('outFile: %s', outFile); 139 | trace('externals: %s', externals ? 'yes' : 'no'); 140 | trace('exclude: %s', exclude); 141 | trace('removeSource: %s', removeSource ? 'yes' : 'no'); 142 | trace('comments: %s', comments ? 'yes' : 'no'); 143 | trace('emitOnIncludedFileNotFound: %s', emitOnIncludedFileNotFound ? "yes" : "no"); 144 | trace('emitOnNoIncludedFileNotFound: %s', emitOnNoIncludedFileNotFound ? "yes" : "no"); 145 | trace("headerPath %s", headerPath); 146 | trace("headerText %s", headerText); 147 | 148 | if (!allFiles) { 149 | assert(fs.existsSync(mainFile), 'main does not exist: ' + mainFile); 150 | } 151 | 152 | if (headerPath) { 153 | if (headerPath === "none") { 154 | headerData = ""; 155 | } else { 156 | assert(fs.existsSync(headerPath), 'header does not exist: ' + headerPath); 157 | headerData = fs.readFileSync(headerPath, 'utf8') + headerData; 158 | } 159 | } else if (headerText) { 160 | headerData = '/*' + headerText + '*/\n'; 161 | } 162 | 163 | let isExclude: (file: string, arg?: boolean) => boolean; 164 | if (typeof exclude === 'function') { 165 | isExclude = exclude; 166 | } 167 | else if (exclude instanceof RegExp) { 168 | isExclude = file => exclude.test(file); 169 | } 170 | else { 171 | isExclude = () => false; 172 | } 173 | 174 | const sourceTypings = glob.sync('**/*.d.ts', { cwd: baseDir }).map(file => path.resolve(baseDir, file)); 175 | 176 | // if all files, generate temporally main file 177 | if (allFiles) { 178 | let mainFileContent = ""; 179 | trace("## temporally main file ##"); 180 | sourceTypings.forEach(file => { 181 | let generatedLine = "export * from './" + path.relative(baseDir, file.substr(0, file.length - 5)).replace(path.sep, "/") + "';"; 182 | trace(generatedLine); 183 | mainFileContent += generatedLine + "\n"; 184 | }); 185 | mainFile = path.resolve(baseDir, "dts-bundle.tmp." + exportName + ".d.ts"); 186 | fs.writeFileSync(mainFile, mainFileContent, 'utf8'); 187 | } 188 | 189 | trace('\n### find typings ###'); 190 | 191 | const inSourceTypings = (file: string) => { 192 | return sourceTypings.indexOf(file) !== -1 || sourceTypings.indexOf(path.join(file, 'index.d.ts')) !== -1; 193 | }; // if file reference is a directory assume commonjs index.d.ts 194 | 195 | trace('source typings (will be included in output if actually used)'); 196 | 197 | sourceTypings.forEach(file => trace(' - %s ', file)); 198 | 199 | trace('excluded typings (will always be excluded from output)'); 200 | 201 | let fileMap: { [name: string]: Result; } = Object.create(null); 202 | let globalExternalImports: string[] = []; 203 | let mainParse: Result; // will be parsed result of first parsed file 204 | let externalTypings: string[] = []; 205 | let inExternalTypings = (file: string) => externalTypings.indexOf(file) !== -1; 206 | { 207 | // recursively parse files, starting from main file, 208 | // following all references and imports 209 | trace('\n### parse files ###'); 210 | 211 | let queue: string[] = [mainFile]; 212 | let queueSeen: { [name: string]: boolean; } = Object.create(null); 213 | 214 | while (queue.length > 0) { 215 | let target = queue.shift(); 216 | if (queueSeen[target]) { 217 | continue; 218 | } 219 | queueSeen[target] = true; 220 | 221 | // parse the file 222 | let parse = parseFile(target); 223 | if (!mainParse) { 224 | mainParse = parse; 225 | } 226 | fileMap[parse.file] = parse; 227 | pushUniqueArr(queue, parse.refs, parse.relativeImports); 228 | } 229 | } 230 | 231 | // map all exports to their file 232 | trace('\n### map exports ###'); 233 | 234 | let exportMap = Object.create(null); 235 | Object.keys(fileMap).forEach(file => { 236 | let parse = fileMap[file]; 237 | parse.exports.forEach(name => { 238 | assert(!(name in exportMap), 'already got export for: ' + name); 239 | exportMap[name] = parse; 240 | trace('- %s -> %s', name, parse.file); 241 | }); 242 | }); 243 | 244 | // build list of typings to include in output later 245 | trace('\n### determine typings to include ###'); 246 | 247 | let excludedTypings: string[] = []; 248 | let usedTypings: Result[] = []; 249 | let externalDependencies: string[] = []; // lists all source files that we omit due to !externals 250 | { 251 | let queue = [mainParse]; 252 | let queueSeen: { [name: string]: boolean; } = Object.create(null); 253 | 254 | trace('queue'); 255 | trace(queue); 256 | 257 | while (queue.length > 0) { 258 | let parse = queue.shift(); 259 | if (queueSeen[parse.file]) { 260 | continue; 261 | } 262 | queueSeen[parse.file] = true; 263 | 264 | trace('%s (%s)', parse.name, parse.file); 265 | 266 | usedTypings.push(parse); 267 | 268 | parse.externalImports.forEach(name => { 269 | let p = exportMap[name]; 270 | if (!externals) { 271 | trace(' - exclude external %s', name); 272 | pushUnique(externalDependencies, !p ? name : p.file); 273 | return; 274 | } 275 | if (isExclude(path.relative(baseDir, p.file), true)) { 276 | trace(' - exclude external filter %s', name); 277 | pushUnique(excludedTypings, p.file); 278 | return; 279 | } 280 | trace(' - include external %s', name); 281 | assert(p, name); 282 | queue.push(p); 283 | }); 284 | parse.relativeImports.forEach(file => { 285 | let p = fileMap[file]; 286 | if (isExclude(path.relative(baseDir, p.file), false)) { 287 | trace(' - exclude internal filter %s', file); 288 | pushUnique(excludedTypings, p.file); 289 | return; 290 | } 291 | trace(' - import relative %s', file); 292 | assert(p, file); 293 | queue.push(p); 294 | }); 295 | } 296 | } 297 | 298 | // rewrite global external modules to a unique name 299 | trace('\n### rewrite global external modules ###'); 300 | 301 | usedTypings.forEach(parse => { 302 | trace(parse.name); 303 | 304 | parse.relativeRef.forEach((line, i) => { 305 | line.modified = replaceExternal(line.original, getLibName); 306 | trace(' - %s ==> %s', line.original, line.modified); 307 | }); 308 | 309 | parse.importLineRef.forEach((line, i) => { 310 | if (outputAsModuleFolder) { 311 | trace(' - %s was skipped.', line.original); 312 | line.skip = true; 313 | return; 314 | } 315 | 316 | if (importExp.test(line.original)) { 317 | line.modified = replaceImportExport(line.original, getLibName); 318 | } else { 319 | line.modified = replaceImportExportEs6(line.original, getLibName); 320 | } 321 | trace(' - %s ==> %s', line.original, line.modified); 322 | }); 323 | }); 324 | 325 | // build collected content 326 | trace('\n### build output ###'); 327 | 328 | let content = headerData; 329 | if (externalDependencies.length > 0) { 330 | content += '// Dependencies for this module:' + newline; 331 | externalDependencies.forEach(file => { 332 | if (referenceExternals) { 333 | content += formatReference(path.relative(baseDir, file).replace(/\\/g, '/')) + newline; 334 | } 335 | else { 336 | content += '// ' + path.relative(baseDir, file).replace(/\\/g, '/') + newline; 337 | } 338 | }); 339 | } 340 | 341 | if ( globalExternalImports.length > 0 ) { 342 | content += newline; 343 | content += globalExternalImports.join(newline) + newline; 344 | } 345 | 346 | content += newline; 347 | 348 | // content += header.stringify(header.importer.packageJSON(pkg)).join(lb) + lb; 349 | // content += lb; 350 | 351 | // add wrapped modules to output 352 | content += usedTypings.filter((parse: Result) => { 353 | // Eliminate all the skipped lines 354 | parse.lines = parse.lines.filter((line: ModLine) => { 355 | return (true !== line.skip); 356 | }); 357 | 358 | // filters empty parse objects. 359 | return ( parse.lines.length > 0 ); 360 | }).map((parse: Result) => { 361 | if (inSourceTypings(parse.file)) { 362 | return formatModule(parse.file, parse.lines.map(line => { 363 | return getIndenter(parse.indent, indent)(line); 364 | })); 365 | } 366 | else { 367 | return parse.lines.map(line => { 368 | return getIndenter(parse.indent, indent)(line); 369 | }).join(newline) + newline; 370 | } 371 | }).join(newline) + newline; 372 | 373 | // remove internal typings, except the 'regenerated' main typing 374 | if (removeSource) { 375 | trace('\n### remove source typings ###'); 376 | 377 | sourceTypings.forEach(p => { 378 | // safety check, only delete .d.ts files, leave our outFile intact for now 379 | if (p !== outFile && dtsExp.test(p) && fs.statSync(p).isFile()) { 380 | trace(' - %s', p); 381 | fs.unlinkSync(p); 382 | } 383 | }); 384 | } 385 | 386 | let inUsed = (file: string): boolean => { 387 | return usedTypings.filter(parse => parse.file === file).length !== 0; 388 | }; 389 | 390 | let bundleResult: BundleResult = { 391 | fileMap, 392 | includeFilesNotFound: [], 393 | noIncludeFilesNotFound: [], 394 | options 395 | }; 396 | 397 | trace('## files not found ##'); 398 | for (let p in fileMap) { 399 | let parse = fileMap[p]; 400 | if (!parse.fileExists) { 401 | if (inUsed(parse.file)) { 402 | bundleResult.includeFilesNotFound.push(parse.file); 403 | warning(' X Included file NOT FOUND %s ', parse.file) 404 | } else { 405 | bundleResult.noIncludeFilesNotFound.push(parse.file); 406 | trace(' X Not used file not found %s', parse.file); 407 | } 408 | } 409 | } 410 | 411 | // write main file 412 | trace('\n### write output ###'); 413 | // write only if there aren't not found files or there are and option "emit file not found" is true. 414 | if ((bundleResult.includeFilesNotFound.length == 0 415 | || (bundleResult.includeFilesNotFound.length > 0 && emitOnIncludedFileNotFound)) 416 | && (bundleResult.noIncludeFilesNotFound.length == 0 417 | || (bundleResult.noIncludeFilesNotFound.length > 0 && emitOnNoIncludedFileNotFound))) { 418 | 419 | trace(outFile); 420 | { 421 | let outDir = path.dirname(outFile); 422 | if (!fs.existsSync(outDir)) { 423 | mkdirp.sync(outDir); 424 | } 425 | } 426 | 427 | fs.writeFileSync(outFile, content, 'utf8'); 428 | bundleResult.emitted = true; 429 | } else { 430 | warning(" XXX Not emit due to exist files not found.") 431 | trace("See documentation for emitOnIncludedFileNotFound and emitOnNoIncludedFileNotFound options.") 432 | bundleResult.emitted = false; 433 | } 434 | 435 | // print some debug info 436 | if (verbose) { 437 | trace('\n### statistics ###'); 438 | 439 | trace('used sourceTypings'); 440 | sourceTypings.forEach(p => { 441 | if (inUsed(p)) { 442 | trace(' - %s', p); 443 | } 444 | }); 445 | 446 | trace('unused sourceTypings'); 447 | sourceTypings.forEach(p => { 448 | if (!inUsed(p)) { 449 | trace(' - %s', p); 450 | } 451 | }); 452 | 453 | trace('excludedTypings'); 454 | excludedTypings.forEach(p => { 455 | trace(' - %s', p); 456 | }); 457 | 458 | trace('used external typings'); 459 | externalTypings.forEach(p => { 460 | if (inUsed(p)) { 461 | trace(' - %s', p); 462 | } 463 | }); 464 | 465 | trace('unused external typings'); 466 | externalTypings.forEach(p => { 467 | if (!inUsed(p)) { 468 | trace(' - %s', p); 469 | } 470 | }); 471 | 472 | trace('external dependencies'); 473 | externalDependencies.forEach(p => { 474 | trace(' - %s', p); 475 | }); 476 | } 477 | 478 | trace('\n### done ###\n'); 479 | // remove temporally file. 480 | if (allFiles) { 481 | fs.unlinkSync(mainFile); 482 | } 483 | return bundleResult; 484 | 485 | function stringEndsWith(str: string, suffix: string) { 486 | return str.indexOf(suffix, str.length - suffix.length) !== -1; 487 | } 488 | 489 | function stringStartsWith(str: string, prefix: string) { 490 | return str.slice(0, prefix.length) == prefix; 491 | } 492 | 493 | // Calculate out file path (see #26 https://github.com/TypeStrong/dts-bundle/issues/26) 494 | function calcOutFilePath(out: any, baseDir: any) { 495 | var result = path.resolve(baseDir, out); 496 | // if path start with ~, out parameter is relative from current dir 497 | if (stringStartsWith(out, "~" + path.sep)) { 498 | result = path.resolve(".", out.substr(2)); 499 | } 500 | return result; 501 | } 502 | 503 | function traceObject(obj: any) { 504 | if (verbose) { 505 | console.log(obj); 506 | } 507 | } 508 | 509 | function trace(...args: any[]) { 510 | if (verbose) { 511 | console.log(util.format.apply(null, args)); 512 | } 513 | } 514 | 515 | function warning(...args: any[]) { 516 | console.log(util.format.apply(null, args)); 517 | } 518 | 519 | function getModName(file: string) { 520 | return path.relative(baseDir, path.dirname(file) + path.sep + path.basename(file).replace(/\.d\.ts$/, '')); 521 | } 522 | 523 | function getExpName(file: string) { 524 | if (file === mainFile) { 525 | return exportName; 526 | } 527 | return getExpNameRaw(file); 528 | } 529 | 530 | function getExpNameRaw(file: string) { 531 | return prefix + exportName + separator + cleanupName(getModName(file)); 532 | } 533 | 534 | function getLibName(ref: string) { 535 | return getExpNameRaw(mainFile) + separator + prefix + separator + ref; 536 | } 537 | 538 | function cleanupName(name: string) { 539 | return name.replace(/\.\./g, '--').replace(/[\\\/]/g, separator); 540 | } 541 | 542 | function mergeModulesLines(lines: any) { 543 | var i = (outputAsModuleFolder ? '' : indent); 544 | return (lines.length === 0 ? '' : i + lines.join(newline + i)) + newline; 545 | } 546 | 547 | function formatModule(file: string, lines: string[]) { 548 | let out = ''; 549 | if (outputAsModuleFolder) { 550 | return mergeModulesLines(lines); 551 | } 552 | 553 | out += 'declare module \'' + getExpName(file) + '\' {' + newline; 554 | out += mergeModulesLines(lines); 555 | out += '}' + newline; 556 | return out; 557 | } 558 | 559 | // main info extractor 560 | function parseFile(file: string): Result { 561 | const name = getModName(file); 562 | 563 | trace('%s (%s)', name, file); 564 | 565 | const res: Result = { 566 | file: file, 567 | name: name, 568 | indent: indent, 569 | exp: getExpName(file), 570 | refs: [], // triple-slash references 571 | externalImports: [], // import()'s like "events" 572 | relativeImports: [], // import()'s like "./foo" 573 | exports: [], 574 | lines: [], 575 | fileExists: true, 576 | // the next two properties contain single-element arrays, which reference the same single-element in .lines, 577 | // in order to be able to replace their contents later in the bundling process. 578 | importLineRef: [], 579 | relativeRef: [] 580 | }; 581 | 582 | if (!fs.existsSync(file)) { 583 | trace(' X - File not found: %s', file); 584 | res.fileExists = false; 585 | return res; 586 | } 587 | if (fs.lstatSync(file).isDirectory()) { // if file is a directory then lets assume commonjs convention of an index file in the given folder 588 | file = path.join(file, 'index.d.ts'); 589 | } 590 | const code = fs.readFileSync(file, 'utf8').replace(bomOptExp, '').replace(/\s*$/, ''); 591 | res.indent = detectIndent(code) || indent; 592 | 593 | // buffer multi-line comments, handle JSDoc 594 | let multiComment: string[] = []; 595 | let queuedJSDoc: string[]; 596 | let inBlockComment = false; 597 | const popBlock = () => { 598 | if (multiComment.length > 0) { 599 | // jsdoc 600 | if (/^[ \t]*\/\*\*/.test(multiComment[0])) { 601 | // flush but hold 602 | queuedJSDoc = multiComment; 603 | } 604 | else if (comments) { 605 | // flush it 606 | multiComment.forEach(line => res.lines.push({ original: line })); 607 | } 608 | multiComment = []; 609 | } 610 | inBlockComment = false; 611 | }; 612 | const popJSDoc = () => { 613 | if (queuedJSDoc) { 614 | queuedJSDoc.forEach(line => { 615 | // fix shabby TS JSDoc output 616 | let match = line.match(/^([ \t]*)(\*.*)/); 617 | if (match) { 618 | res.lines.push({ original: match[1] + ' ' + match[2] }); 619 | } 620 | else { 621 | res.lines.push({ original: line }); 622 | } 623 | }); 624 | queuedJSDoc = null; 625 | } 626 | }; 627 | 628 | code.split(/\r?\n/g).forEach((line: any) => { 629 | let match: string[]; 630 | 631 | // block comment end 632 | if (/^[((=====)(=*)) \t]*\*+\//.test(line)) { 633 | multiComment.push(line); 634 | popBlock(); 635 | return; 636 | } 637 | 638 | // block comment start 639 | if (/^[ \t]*\/\*/.test(line)) { 640 | multiComment.push(line); 641 | inBlockComment = true; 642 | 643 | // single line block comment 644 | if (/\*+\/[ \t]*$/.test(line)) { 645 | popBlock(); 646 | } 647 | return; 648 | } 649 | 650 | if (inBlockComment) { 651 | multiComment.push(line); 652 | return; 653 | } 654 | 655 | // blankline 656 | if (/^\s*$/.test(line)) { 657 | res.lines.push({ original: '' }); 658 | return; 659 | } 660 | 661 | // reference tag 662 | if (/^\/\/\//.test(line)) { 663 | let ref = extractReference(line); 664 | if (ref) { 665 | let refPath = path.resolve(path.dirname(file), ref); 666 | if (inSourceTypings(refPath)) { 667 | trace(' - reference source typing %s (%s)', ref, refPath); 668 | } else { 669 | let relPath = path.relative(baseDir, refPath).replace(/\\/g, '/'); 670 | 671 | trace(' - reference external typing %s (%s) (relative: %s)', ref, refPath, relPath); 672 | 673 | if (!inExternalTypings(refPath)) { 674 | externalTypings.push(refPath); 675 | } 676 | } 677 | pushUnique(res.refs, refPath); 678 | return; 679 | } 680 | } 681 | 682 | // line comments 683 | if (/^\/\//.test(line)) { 684 | if (comments) { 685 | res.lines.push({ original: line }); 686 | } 687 | return; 688 | } 689 | 690 | // private member 691 | if (privateExp.test(line)) { 692 | queuedJSDoc = null; 693 | return; 694 | } 695 | popJSDoc(); 696 | 697 | // import() statement or es6 import 698 | if ((line.indexOf("from") >= 0 && (match = line.match(importEs6Exp))) || 699 | (line.indexOf("require") >= 0 && (match = line.match(importExp)))) { 700 | const [_, lead, quote, moduleName, trail] = match; 701 | assert(moduleName); 702 | 703 | const impPath = path.resolve(path.dirname(file), moduleName); 704 | 705 | // filename (i.e. starts with a dot, slash or windows drive letter) 706 | if (fileExp.test(moduleName)) { 707 | // TODO: some module replacing is handled here, whereas the rest is 708 | // done in the "rewrite global external modules" step. It may be 709 | // more clear to do all of it in that step. 710 | let modLine: ModLine = { 711 | original: lead + quote + getExpName(impPath) + trail 712 | }; 713 | res.lines.push(modLine); 714 | 715 | let full = path.resolve(path.dirname(file), impPath); 716 | // If full is not an existing file, then let's assume the extension .d.ts 717 | if(!fs.existsSync(full) || fs.existsSync(full + '.d.ts')) { 718 | full += '.d.ts'; 719 | } 720 | trace(' - import relative %s (%s)', moduleName, full); 721 | 722 | pushUnique(res.relativeImports, full); 723 | res.importLineRef.push(modLine); 724 | } 725 | // identifier 726 | else { 727 | let modLine: ModLine = { 728 | original: line 729 | }; 730 | trace(' - import external %s', moduleName); 731 | 732 | pushUnique(res.externalImports, moduleName); 733 | if (externals) { 734 | res.importLineRef.push(modLine); 735 | } 736 | if (!outputAsModuleFolder) { 737 | res.lines.push(modLine); 738 | } else { 739 | pushUnique(globalExternalImports, line); 740 | } 741 | } 742 | } 743 | 744 | // declaring an external module 745 | // this triggers when we're e.g. parsing external module declarations, such as node.d.ts 746 | else if ((match = line.match(externalExp))) { 747 | let [_, declareModule, lead, moduleName, trail] = match; 748 | assert(moduleName); 749 | 750 | trace(' - declare %s', moduleName); 751 | pushUnique(res.exports, moduleName); 752 | let modLine: ModLine = { 753 | original: line 754 | }; 755 | res.relativeRef.push(modLine); // TODO 756 | res.lines.push(modLine); 757 | } 758 | // clean regular lines 759 | else { 760 | // remove public keyword 761 | if ((match = line.match(publicExp))) { 762 | let [_, sp, static1, pub, static2, ident] = match; 763 | line = sp + static1 + static2 + ident; 764 | } 765 | if (inSourceTypings(file)) { 766 | // for internal typings, remove the 'declare' keyword (but leave 'export' intact) 767 | res.lines.push({ original: line.replace(/^(export )?declare /g, '$1') }); 768 | } 769 | else { 770 | res.lines.push({ original: line }); 771 | } 772 | } 773 | }); 774 | 775 | return res; 776 | } 777 | } 778 | 779 | function pushUnique(arr: T[], value: T) { 780 | if (arr.indexOf(value) < 0) { 781 | arr.push(value); 782 | } 783 | return arr; 784 | } 785 | 786 | function pushUniqueArr(arr: T[], ...values: T[][]) { 787 | values.forEach(vs => vs.forEach(v => pushUnique(arr, v))); 788 | return arr; 789 | } 790 | 791 | function formatReference(file: string) { 792 | return '/// '; 793 | } 794 | 795 | function extractReference(tag: string) { 796 | let match = tag.match(referenceTagExp); 797 | if (match) { 798 | return match[2]; 799 | } 800 | return null; 801 | } 802 | 803 | function replaceImportExport(line: string, replacer: (str: string) => string) { 804 | let match = line.match(importExp); 805 | if (match) { 806 | assert(match[4]); 807 | if (identifierExp.test(match[3])) { 808 | return match[1] + match[2] + replacer(match[3]) + match[4]; 809 | } 810 | } 811 | return line; 812 | } 813 | 814 | function replaceImportExportEs6(line: string, replacer: (str: string) => string) { 815 | if (line.indexOf("from") < 0) { 816 | return line; 817 | } 818 | let match = line.match(importEs6Exp); 819 | if (match) { 820 | assert(match[4]); 821 | if (identifierExp.test(match[3])) { 822 | return match[1] + match[2] + replacer(match[3]) + match[4]; 823 | } 824 | } 825 | return line; 826 | } 827 | 828 | function replaceExternal(line: string, replacer: (str: string) => string) { 829 | let match = line.match(externalExp); 830 | if (match) { 831 | let [_, declareModule, beforeIndent, moduleName, afterIdent] = match; 832 | assert(afterIdent); 833 | if (identifierExp.test(moduleName)) { 834 | return declareModule + beforeIndent + replacer(moduleName) + afterIdent; 835 | } 836 | } 837 | return line; 838 | } 839 | 840 | function getIndenter(actual: string, use: string): (line: ModLine) => string { 841 | if (actual === use || !actual) { 842 | return line => line.modified || line.original; 843 | } 844 | return line => (line.modified || line.original).replace(new RegExp('^' + actual + '+', 'g'), match => match.split(actual).join(use)); 845 | } 846 | 847 | function optValue(passed: T, def: T): T { 848 | if (typeof passed === 'undefined') { 849 | return def; 850 | } 851 | return passed; 852 | } 853 | 854 | function regexEscape(s: string) { 855 | return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 856 | } 857 | --------------------------------------------------------------------------------