diff --git a/.gitignore b/.gitignore index 2de8774..4175af6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ /test/build !/test/src/typings/*.d.ts /test/src/**/*.d.ts +!/test/src/ambient/*.d.ts /test/src/**/*.js /test/src/**/*.js.map diff --git a/Gruntfile.js b/Gruntfile.js index 5212b2e..3dbbf65 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -67,6 +67,10 @@ module.exports = function (grunt) { testConflicts: { src: ['test/src/conflicts/dirname/index.ts'], outDir: 'test/build/conflicts/dirname' + }, + testAmbient: { + src: ['test/src/ambient/index.ts'], + outDir: 'test/build/ambient' } }, mochaTest: { @@ -92,6 +96,7 @@ module.exports = function (grunt) { 'ts:testEs6', 'ts:testCommonJs', 'ts:testConflicts', + 'ts:testAmbient', 'run' ]); diff --git a/lib/index.ts b/lib/index.ts index 4474d39..88e299e 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -231,12 +231,11 @@ export function bundle(options: Options): BundleResult { // map all exports to their file trace('\n### map exports ###'); - let exportMap = Object.create(null); + let exportMap = Object.create(null); // values are arrays of parsed files Object.keys(fileMap).forEach(file => { let parse = fileMap[file]; parse.exports.forEach(name => { - assert(!(name in exportMap), 'already got export for: ' + name); - exportMap[name] = parse; + (exportMap[name] || (exportMap[name] = [])).push(parse); trace('- %s -> %s', name, parse.file); }); }); @@ -266,20 +265,23 @@ export function bundle(options: Options): BundleResult { usedTypings.push(parse); parse.externalImports.forEach(name => { - let p = exportMap[name]; - if (!externals) { - trace(' - exclude external %s', name); - pushUnique(externalDependencies, !p ? name : p.file); - return; - } - if (isExclude(path.relative(baseDir, p.file), true)) { - trace(' - exclude external filter %s', name); - pushUnique(excludedTypings, p.file); - return; + let parses = exportMap[name]; + + for (let p of parses) { + if (!externals) { + trace(' - exclude external %s', name); + pushUnique(externalDependencies, !p ? name : p.file); + return; + } + if (isExclude(path.relative(baseDir, p.file), true)) { + trace(' - exclude external filter %s', name); + pushUnique(excludedTypings, p.file); + return; + } + trace(' - include external %s', name); + assert(p, name); + queue.push(p); } - trace(' - include external %s', name); - assert(p, name); - queue.push(p); }); parse.relativeImports.forEach(file => { let p = fileMap[file]; @@ -749,6 +751,11 @@ export function bundle(options: Options): BundleResult { trace(' - declare %s', moduleName); pushUnique(res.exports, moduleName); + + // for internal typings, can't have nested declares w/ modules + if (inSourceTypings(file)) { + line = line.replace('declare module', 'module'); + } let modLine: ModLine = { original: line }; diff --git a/test/expected/ambient/foo-mx.d.ts b/test/expected/ambient/foo-mx.d.ts new file mode 100644 index 0000000..863b2be --- /dev/null +++ b/test/expected/ambient/foo-mx.d.ts @@ -0,0 +1,29 @@ +// Dependencies for this module: +// ambient1.d.ts + +declare module 'foo-mx' { + export { MyInterface } from 'mymodule'; + export { Sup1 } from 'foo-mx/ambient1'; + export { Sup2 } from 'foo-mx/ambient2'; +} + +declare module 'foo-mx/ambient1' { + module 'mymodule' { + interface MyInterface { + prop1: string; + } + } + export interface Sup1 { + } +} + +declare module 'foo-mx/ambient2' { + module 'mymodule' { + interface MyInterface { + prop2: string; + } + } + export interface Sup2 { + } +} + diff --git a/test/src/ambient/ambient1.ts b/test/src/ambient/ambient1.ts new file mode 100644 index 0000000..104bb41 --- /dev/null +++ b/test/src/ambient/ambient1.ts @@ -0,0 +1,10 @@ + +/// + +declare module 'mymodule' { + interface MyInterface { + prop1: string + } +} + +export interface Sup1 {} diff --git a/test/src/ambient/ambient2.ts b/test/src/ambient/ambient2.ts new file mode 100644 index 0000000..032346f --- /dev/null +++ b/test/src/ambient/ambient2.ts @@ -0,0 +1,10 @@ + +/// + +declare module 'mymodule' { + interface MyInterface { + prop2: string + } +} + +export interface Sup2 {} diff --git a/test/src/ambient/index.ts b/test/src/ambient/index.ts new file mode 100644 index 0000000..6d375d6 --- /dev/null +++ b/test/src/ambient/index.ts @@ -0,0 +1,3 @@ +export { MyInterface } from 'mymodule'; +export { Sup1 } from './ambient1'; +export { Sup2 } from './ambient2'; diff --git a/test/src/ambient/mymodule.d.ts b/test/src/ambient/mymodule.d.ts new file mode 100644 index 0000000..a3839b1 --- /dev/null +++ b/test/src/ambient/mymodule.d.ts @@ -0,0 +1,6 @@ + +declare module 'mymodule' { + export interface MyInterface { + prop0: string + } +} diff --git a/test/test.js b/test/test.js index 25f95dc..75d31b6 100644 --- a/test/test.js +++ b/test/test.js @@ -384,6 +384,56 @@ describe('dts bundle', function () { assert.strictEqual(getFile(actualFile), getFile(expectedFile)); }); + (function testit(name, assertion, run) { + var buildDir = path.resolve(__dirname, 'build', 'ambient'); + var call = function (done) { + var testDir = path.join(tmpDir, name); + var expDir = path.join(expectDir, name); + + mkdirp.sync(testDir); + + ncp.ncp(buildDir, testDir, function (err) { + if (err) { + done(err); + return; + } + assertion(testDir, expDir); + done(); + }); + }; + + var label = 'bundle ' + name; + + if (run === 'skip') { + it.skip(label, call); + } + else if (run === 'only') { + it.only(label, call); + } + else { + it(label, call); + } + })('ambient', function (actDir, expDir) { + var result = dts.bundle({ + name: 'foo-mx', + main: path.join(actDir, 'index.d.ts'), + newline: '\n', + verbose: true, + headerPath: "none" + }); + var name = 'foo-mx.d.ts'; + var actualFile = path.join(actDir, name); + assert.isTrue(result.emitted, "not emit " + actualFile); + var expectedFile = path.join(expDir, name); + assertFiles(actDir, [ + name, + 'index.d.ts', + 'ambient1.d.ts', + 'ambient2.d.ts', + ]); + assert.strictEqual(getFile(actualFile), getFile(expectedFile)); + }); + (function testit(name, assertion, run) { var buildDir = path.resolve(__dirname, 'build', 'es6'); var call = function (done) {