diff --git a/packages/typescript/src/index.ts b/packages/typescript/src/index.ts index be243d23b..293e341d9 100644 --- a/packages/typescript/src/index.ts +++ b/packages/typescript/src/index.ts @@ -12,7 +12,13 @@ import createModuleResolver from './moduleResolution'; import { getPluginOptions } from './options/plugin'; import { emitParsedOptionsErrors, parseTypescriptConfig } from './options/tsconfig'; import { validatePaths, validateSourceMap } from './options/validate'; -import findTypescriptOutput, { getEmittedFile, normalizePath, emitFile } from './outputFile'; +import findTypescriptOutput, { + getEmittedFile, + normalizePath, + emitFile, + isDeclarationOutputFile, + isMapOutputFile +} from './outputFile'; import { preflight } from './preflight'; import createWatchProgram, { WatchProgramHelper } from './watchProgram'; import TSCache from './tscache'; @@ -150,40 +156,41 @@ export default function typescript(options: RollupTypescriptOptions = {}): Plugi }, async generateBundle(outputOptions) { - parsedOptions.fileNames.forEach((fileName) => { - const output = findTypescriptOutput(ts, parsedOptions, fileName, emittedFiles, tsCache); - output.declarations.forEach((id) => { - const code = getEmittedFile(id, emittedFiles, tsCache); - if (!code || !parsedOptions.options.declaration) { - return; - } - - let baseDir: string | undefined; - if (outputOptions.dir) { - baseDir = outputOptions.dir; - } else if (outputOptions.file) { - // find common path of output.file and configured declation output - const outputDir = path.dirname(outputOptions.file); - const configured = path.resolve( - parsedOptions.options.declarationDir || - parsedOptions.options.outDir || - tsconfig || - process.cwd() - ); - const backwards = path - .relative(outputDir, configured) - .split(path.sep) - .filter((v) => v === '..') - .join(path.sep); - baseDir = path.normalize(`${outputDir}/${backwards}`); - } - if (!baseDir) return; - - this.emitFile({ - type: 'asset', - fileName: normalizePath(path.relative(baseDir, id)), - source: code - }); + const declarationAndMapFiles = [...emittedFiles.keys()].filter( + (fileName) => isDeclarationOutputFile(fileName) || isMapOutputFile(fileName) + ); + + declarationAndMapFiles.forEach((id) => { + const code = getEmittedFile(id, emittedFiles, tsCache); + if (!code || !parsedOptions.options.declaration) { + return; + } + + let baseDir: string | undefined; + if (outputOptions.dir) { + baseDir = outputOptions.dir; + } else if (outputOptions.file) { + // find common path of output.file and configured declation output + const outputDir = path.dirname(outputOptions.file); + const configured = path.resolve( + parsedOptions.options.declarationDir || + parsedOptions.options.outDir || + tsconfig || + process.cwd() + ); + const backwards = path + .relative(outputDir, configured) + .split(path.sep) + .filter((v) => v === '..') + .join(path.sep); + baseDir = path.normalize(`${outputDir}/${backwards}`); + } + if (!baseDir) return; + + this.emitFile({ + type: 'asset', + fileName: normalizePath(path.relative(baseDir, id)), + source: code }); }); diff --git a/packages/typescript/src/outputFile.ts b/packages/typescript/src/outputFile.ts index e2ff67220..4dc0de04d 100644 --- a/packages/typescript/src/outputFile.ts +++ b/packages/typescript/src/outputFile.ts @@ -16,15 +16,22 @@ export interface TypescriptSourceDescription extends Partial /** * Checks if the given OutputFile represents some code */ -function isCodeOutputFile(name: string): boolean { - return !isMapOutputFile(name) && !name.endsWith('.d.ts'); +export function isCodeOutputFile(name: string): boolean { + return !isMapOutputFile(name) && !isDeclarationOutputFile(name); } /** * Checks if the given OutputFile represents some source map */ -function isMapOutputFile(name: string): boolean { - return name.endsWith('.map'); +export function isMapOutputFile(name: string): boolean { + return name.endsWith('ts.map'); +} + +/** + * Checks if the given OutputFile represents some declaration + */ +export function isDeclarationOutputFile(name: string): boolean { + return /\.d\.[cm]?ts$/.test(name); } /** diff --git a/packages/typescript/test/declarations.ts b/packages/typescript/test/declarations.ts index 36ccdc85c..6b24d00c1 100644 --- a/packages/typescript/test/declarations.ts +++ b/packages/typescript/test/declarations.ts @@ -48,11 +48,11 @@ test.serial('supports creating declaration files in subfolder', async (t) => { onwarn }); const output = await getCode(bundle, { format: 'es', dir: 'fixtures/basic/dist' }, true); - const declaration = output[1].source as string; + const declaration = output[2].source as string; t.deepEqual( output.map((out) => out.fileName), - ['main.js', 'types/main.d.ts', 'types/main.d.ts.map'] + ['main.js', 'types/main.d.ts.map', 'types/main.d.ts'] ); t.true(declaration.includes('declare const answer = 42;'), declaration); @@ -100,16 +100,16 @@ test.serial('supports creating declaration files for interface only source file' { format: 'es', dir: 'fixtures/export-interface-only/dist' }, true ); - const declaration = output[1].source as string; + const declaration = output[2].source as string; t.deepEqual( output.map((out) => out.fileName), [ 'main.js', - 'types/interface.d.ts', 'types/interface.d.ts.map', - 'types/main.d.ts', - 'types/main.d.ts.map' + 'types/interface.d.ts', + 'types/main.d.ts.map', + 'types/main.d.ts' ] ); @@ -117,6 +117,37 @@ test.serial('supports creating declaration files for interface only source file' t.true(declaration.includes('//# sourceMappingURL=interface.d.ts.map'), declaration); }); +test.serial( + 'supports creating declaration files for type-only source files that are implicitly included', + async (t) => { + const bundle = await rollup({ + input: 'fixtures/implicitly-included-type-only-file/main.ts', + plugins: [ + typescript({ + tsconfig: 'fixtures/implicitly-included-type-only-file/tsconfig.json', + declarationDir: 'fixtures/implicitly-included-type-only-file/dist/types', + declaration: true + }), + onwarn + ] + }); + const output = await getCode( + bundle, + { format: 'es', dir: 'fixtures/implicitly-included-type-only-file/dist' }, + true + ); + const declaration = output[1].source as string; + + t.deepEqual( + output.map((out) => out.fileName), + // 'types/should-not-be-emitted-types.d.ts' should not be emitted because 'main.ts' does not import/export from it. + ['main.js', 'types/should-be-emitted-types.d.ts', 'types/main.d.ts'] + ); + + t.true(declaration.includes('export declare type MyNumber = number;'), declaration); + } +); + test.serial('supports creating declaration files in declarationDir', async (t) => { const bundle = await rollup({ input: 'fixtures/basic/main.ts', diff --git a/packages/typescript/test/fixtures/implicitly-included-type-only-file/main.ts b/packages/typescript/test/fixtures/implicitly-included-type-only-file/main.ts new file mode 100644 index 000000000..fc12ba62f --- /dev/null +++ b/packages/typescript/test/fixtures/implicitly-included-type-only-file/main.ts @@ -0,0 +1 @@ +export { MyNumber } from './should-be-emitted-types'; diff --git a/packages/typescript/test/fixtures/implicitly-included-type-only-file/should-be-emitted-types.ts b/packages/typescript/test/fixtures/implicitly-included-type-only-file/should-be-emitted-types.ts new file mode 100644 index 000000000..f6a07df9b --- /dev/null +++ b/packages/typescript/test/fixtures/implicitly-included-type-only-file/should-be-emitted-types.ts @@ -0,0 +1 @@ +export type MyNumber = number; diff --git a/packages/typescript/test/fixtures/implicitly-included-type-only-file/should-not-be-emitted-types.ts b/packages/typescript/test/fixtures/implicitly-included-type-only-file/should-not-be-emitted-types.ts new file mode 100644 index 000000000..9a173ddd5 --- /dev/null +++ b/packages/typescript/test/fixtures/implicitly-included-type-only-file/should-not-be-emitted-types.ts @@ -0,0 +1,2 @@ +// This file is intentionally not imported from. +export type MyString = string; diff --git a/packages/typescript/test/fixtures/implicitly-included-type-only-file/tsconfig.json b/packages/typescript/test/fixtures/implicitly-included-type-only-file/tsconfig.json new file mode 100644 index 000000000..346e6cea9 --- /dev/null +++ b/packages/typescript/test/fixtures/implicitly-included-type-only-file/tsconfig.json @@ -0,0 +1,3 @@ +{ + "include": ["main.ts"] +}