这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export default defineConfig({
tsup index.ts --dts
```

This will emit `./dist/index.js` and `./dist/index.d.ts`.
This will emit `./dist/index.js` and `./dist/index.d.ts`. When emitting multiple [bundle formats](#bundle-formats), one declaration file per bundle format is generated. This is required for consumers to get accurate type checking with TypeScript. Note that declaration files generated by any tool other than `tsc` are not guaranteed to be error-free, so it's a good idea to test the output with `tsc` or a tool like [@arethetypeswrong/cli](https://www.npmjs.com/package/@arethetypeswrong/cli) before publishing.

If you have multiple entry files, each entry will get a corresponding `.d.ts` file. So when you only want to generate declaration file for a single entry, use `--dts <entry>` format, e.g. `--dts src/index.ts`.

Expand Down
25 changes: 1 addition & 24 deletions src/esbuild/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,12 @@ import { externalPlugin } from './external'
import { postcssPlugin } from './postcss'
import { sveltePlugin } from './svelte'
import consola from 'consola'
import { truthy } from '../utils'
import { defaultOutExtension, truthy } from '../utils'
import { swcPlugin } from './swc'
import { nativeNodeModulesPlugin } from './native-node-module'
import { PluginContainer } from '../plugin'
import { OutExtensionFactory } from '../options'

const defaultOutExtension = ({
format,
pkgType,
}: {
format: Format
pkgType?: string
}): { js: string } => {
let jsExtension = '.js'
const isModule = pkgType === 'module'
if (isModule && format === 'cjs') {
jsExtension = '.cjs'
}
if (!isModule && format === 'esm') {
jsExtension = '.mjs'
}
if (format === 'iife') {
jsExtension = '.global.js'
}
return {
js: jsExtension,
}
}

const getOutputExtensionMap = (
options: NormalizedOptions,
format: Format,
Expand Down
2 changes: 1 addition & 1 deletion src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export type ContextForOutPathGeneration = {
pkgType?: string
}

export type OutExtensionObject = { js?: string }
export type OutExtensionObject = { js?: string, dts?: string }

export type OutExtensionFactory = (
ctx: ContextForOutPathGeneration
Expand Down
36 changes: 22 additions & 14 deletions src/rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import ts from 'typescript'
import hashbangPlugin from 'rollup-plugin-hashbang'
import jsonPlugin from '@rollup/plugin-json'
import { handleError } from './errors'
import { removeFiles } from './utils'
import { defaultOutExtension, removeFiles } from './utils'
import { TsResolveOptions, tsResolvePlugin } from './rollup/ts-resolve'
import { createLogger, setSilent } from './log'
import { getProductionDeps } from './load'
import { getProductionDeps, loadPkg } from './load'
import path from 'path'
import { reportSize } from './lib/report-size'
import resolveFrom from 'resolve-from'
Expand All @@ -32,7 +32,7 @@ const dtsPlugin: typeof import('rollup-plugin-dts') = require('rollup-plugin-dts

type RollupConfig = {
inputConfig: InputOptions
outputConfig: OutputOptions
outputConfig: OutputOptions[]
}

const findLowestCommonAncestor = (filepaths: string[]) => {
Expand Down Expand Up @@ -111,6 +111,7 @@ const getRollupConfig = async (
}
}

const pkg = await loadPkg(process.cwd())
const deps = await getProductionDeps(process.cwd())

const tsupCleanPlugin: Plugin = {
Expand Down Expand Up @@ -188,13 +189,19 @@ const getRollupConfig = async (
...(options.external || []),
],
},
outputConfig: {
dir: options.outDir || 'dist',
format: 'esm',
exports: 'named',
banner: dtsOptions.banner,
footer: dtsOptions.footer,
},
outputConfig: options.format.map((format) => {
const outputExtension =
options.outExtension?.({ format, options, pkgType: pkg.type }).dts ||
defaultOutExtension({ format, pkgType: pkg.type }).dts
return {
dir: options.outDir || 'dist',
format: 'esm',
exports: 'named',
banner: dtsOptions.banner,
footer: dtsOptions.footer,
entryFileNames: `[name]${outputExtension}`,
}
}),
}
}

Expand All @@ -207,15 +214,16 @@ async function runRollup(options: RollupConfig) {
}
logger.info('dts', 'Build start')
const bundle = await rollup(options.inputConfig)
const result = await bundle.write(options.outputConfig)
const results = await Promise.all(options.outputConfig.map(bundle.write))
const outputs = results.flatMap((result) => result.output);
logger.success('dts', `⚡️ Build success in ${getDuration()}`)
reportSize(
logger,
'dts',
result.output.reduce((res, info) => {
outputs.reduce((res, info) => {
const name = path.relative(
process.cwd(),
path.join(options.outputConfig.dir || '.', info.fileName)
path.join(options.outputConfig[0].dir || '.', info.fileName)
)
return {
...res,
Expand All @@ -231,7 +239,7 @@ async function runRollup(options: RollupConfig) {

async function watchRollup(options: {
inputConfig: InputOptions
outputConfig: OutputOptions
outputConfig: OutputOptions[]
}) {
const { watch } = await import('rollup')

Expand Down
28 changes: 28 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'fs'
import glob from 'globby'
import resolveFrom from 'resolve-from'
import strip from 'strip-json-comments'
import { Format } from './options'

export type MaybePromise<T> = T | Promise<T>

Expand Down Expand Up @@ -129,3 +130,30 @@ export function jsoncParse(data: string) {
return {}
}
}

export function defaultOutExtension({
format,
pkgType,
}: {
format: Format
pkgType?: string
}): { js: string, dts: string } {
let jsExtension = '.js'
let dtsExtension = '.d.ts'
const isModule = pkgType === 'module'
if (isModule && format === 'cjs') {
jsExtension = '.cjs'
dtsExtension = '.d.cts'
}
if (!isModule && format === 'esm') {
jsExtension = '.mjs'
dtsExtension = '.d.mts'
}
if (format === 'iife') {
jsExtension = '.global.js'
}
return {
js: jsExtension,
dts: dtsExtension,
}
}
35 changes: 32 additions & 3 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,7 @@ test(`custom tsconfig should pass to dts plugin`, async () => {
}
`,
})
expect(outFiles).toEqual(['input.d.ts'])
expect(outFiles).toEqual(['input.d.mts'])
})

test(`should generate export {} when there are no exports in source file`, async () => {
Expand All @@ -1268,8 +1268,8 @@ test(`should generate export {} when there are no exports in source file`, async
}
`,
})
expect(outFiles).toEqual(['input.d.ts', 'input.mjs'])
expect(await getFileContent('dist/input.d.ts')).toContain('export { }')
expect(outFiles).toEqual(['input.d.mts', 'input.mjs'])
expect(await getFileContent('dist/input.d.mts')).toContain('export { }')
})

test('custom inject style function', async () => {
Expand Down Expand Up @@ -1335,3 +1335,32 @@ test('should load postcss esm config', async () => {
expect(outFiles).toEqual(['input.cjs', 'input.css'])
expect(await getFileContent('dist/input.css')).toContain('color: blue;')
})

test('should emit a declaration file per format', async () => {
const { outFiles } = await run(getTestName(), {
'input.ts': `export default 'foo'`,
'tsup.config.ts': `
export default {
entry: ['src/input.ts'],
format: ['esm', 'cjs'],
dts: true
}`,
});
expect(outFiles).toEqual(['input.d.mts', 'input.d.ts', 'input.js', 'input.mjs'])
});

test('should emit a declaration file per format (type: module)', async () => {
const { outFiles } = await run(getTestName(), {
'input.ts': `export default 'foo'`,
'package.json': `{
"type": "module"
}`,
'tsup.config.ts': `
export default {
entry: ['src/input.ts'],
format: ['esm', 'cjs'],
dts: true
}`,
});
expect(outFiles).toEqual(['input.cjs', 'input.d.cts', 'input.d.ts', 'input.js'])
});