diff --git a/docs/README.md b/docs/README.md index 9641ef0c..3234b4b8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -205,7 +205,7 @@ They should not be included in a published NPM package and should not be confuse [Tsup is not able to generate those files](https://github.com/egoist/tsup/issues/564). Instead, you should use the TypeScript compiler directly, by running the following command after the build is done: `tsc --emitDeclarationOnly --declaration`. -You can combine this command with Tsup [`onSuccess`](https://tsup.egoist.dev/#onsuccess) callback. +You can combine this command with Tsup [`onSuccess`](https://tsup.egoist.dev/#onsuccess) callback. ### Generate sourcemap file @@ -409,6 +409,9 @@ export default defineConfig({ }) ``` +### onDTSSuccess +`onSuccess` only fires when the JS assets are ready. `onDTSSuccess` fires when the typings are ready, which is a separate task. It functions exactly the same as `onSuccess` + ### Minify output You can also minify the output, resulting into lower bundle sizes by using the `--minify` flag. diff --git a/schema.json b/schema.json index 0402482e..11bd1004 100644 --- a/schema.json +++ b/schema.json @@ -149,6 +149,9 @@ "onSuccess": { "type": "string" }, + "onDTSSuccess": { + "type": "string" + }, "jsxFactory": { "type": "string" }, @@ -264,7 +267,7 @@ }, "silent": { "type": "boolean", - "description": "Suppress non-error logs (excluding \"onSuccess\" process output)" + "description": "Suppress non-error logs (excluding \"onSuccess\" and \"onDTSSuccess\" process output)" }, "skipNodeModulesBundle": { "type": "boolean", diff --git a/src/cli-main.ts b/src/cli-main.ts index b3338b6c..9962dd2b 100644 --- a/src/cli-main.ts +++ b/src/cli-main.ts @@ -51,6 +51,10 @@ export async function main(options: Options = {}) { '--onSuccess ', 'Execute command after successful build, specially useful for watch mode' ) + .option( + '--onDTSSuccess ', + 'Execute command after successful build of the typings, specially useful for watch mode' + ) .option('--env.* ', 'Define compile-time env variables') .option( '--inject ', diff --git a/src/index.ts b/src/index.ts index 0a542f80..5ce8b920 100644 --- a/src/index.ts +++ b/src/index.ts @@ -172,8 +172,27 @@ export async function build(_options: Options) { logger.info('CLI', 'Running in watch mode') } + let onDTSSuccessProcess: ChildProcess | undefined + let onDTSSuccessCleanup: (() => any) | undefined | void + + const doonDTSSuccessCleanup = async () => { + if (onDTSSuccessProcess) { + await killProcess({ + pid: onDTSSuccessProcess.pid, + signal: options.killSignal || 'SIGTERM', + }) + } else if (onDTSSuccessCleanup) { + await onDTSSuccessCleanup() + } + // reset them in all occasions anyway + onDTSSuccessProcess = undefined + onDTSSuccessCleanup = undefined + } + const dtsTask = async () => { if (options.dts) { + doonDTSSuccessCleanup(); + await new Promise((resolve, reject) => { const worker = new Worker(path.join(__dirname, './rollup.js')) worker.postMessage({ @@ -187,6 +206,7 @@ export async function build(_options: Options) { plugins: undefined, treeshake: undefined, onSuccess: undefined, + onDTSSuccess: undefined, outExtension: undefined, }, }) @@ -194,16 +214,32 @@ export async function build(_options: Options) { if (data === 'error') { reject(new Error('error occured in dts build')) } else if (data === 'success') { - resolve() - } else { - const { type, text } = data - if (type === 'log') { - console.log(text) - } else if (type === 'error') { - console.error(text) + + if (options.onDTSSuccess) { + if (typeof options.onDTSSuccess === 'function') { + options.onDTSSuccess().then((v) => onDTSSuccessCleanup = v) + } else { + onDTSSuccessProcess = execa(options.onDTSSuccess, { + shell: true, + stdio: 'inherit', + }) + onDTSSuccessProcess.on('exit', (code) => { + if (code && code !== 0) { + process.exitCode = code + } + }) + } } - } - }) + resolve() + } else { + const { type, text } = data + if (type === 'log') { + console.log(text) + } else if (type === 'error') { + console.error(text) + } + } + }) }) } } diff --git a/src/options.ts b/src/options.ts index 884dfa44..c14a4730 100644 --- a/src/options.ts +++ b/src/options.ts @@ -113,6 +113,9 @@ export type Options = { onSuccess?: | string | (() => Promise void | Promise)>) + onDTSSuccess?: + | string + | (() => Promise void | Promise)>) jsxFactory?: string jsxFragment?: string outDir?: string @@ -139,7 +142,7 @@ export type Options = { /** * Code splitting * Default to `true` for ESM, `false` for CJS. - * + * * You can set it to `true` explicitly, and may want to disable code splitting sometimes: [`#255`](https://github.com/egoist/tsup/issues/255) */ splitting?: boolean diff --git a/test/index.test.ts b/test/index.test.ts index fcbc1782..9b2e792a 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -553,6 +553,89 @@ test('onSuccess: use a function from config file', async () => { expect(logs.includes('world')).toEqual(true) }) +test('onDTSSuccess', async () => { + const { logs } = await run( + getTestName(), + { + 'input.ts': "console.log('test');", + }, + { + flags: [ + '--dts', + '--onDTSSuccess', 'echo foo && echo bar' + ], + } + ) + + expect(logs.includes('foo')).toEqual(true) + expect(logs.includes('bar')).toEqual(true) +}) + +test('onDTSSuccess: use a function from config file', async () => { + const { logs } = await run(getTestName(), { + 'input.ts': "console.log('test');", + 'tsup.config.ts': ` + export default { + onDTSSuccess: async () => { + console.log('hello') + await new Promise((resolve) => { + setTimeout(() => { + console.log('world') + resolve('') + }, 1_000) + }) + } + }`, + }, { + 'flags': ['--dts'] + }) + + expect(logs.includes('hello')).toEqual(true) + expect(logs.includes('world')).toEqual(true) +}) + +test('onSuccess and onDTSSuccess run separately: use a function from config file', async () => { + const { logs } = await run(getTestName(), { + 'input.ts': "console.log('test');", + 'tsup.config.ts': ` + export default { + onSuccess: async () => { + console.log("onSuccess Called"); + }, + onDTSSuccess: async () => { + console.log("onDTSSuccess Called"); + }, + esbuildPlugins: [ + { + name: "delay-success", + setup(build) { + build.onEnd(async (result) => { + console.log("delay plugin start"); + await new Promise((resolve) => { + setTimeout(() => { + console.log("delay plugin end"); + resolve(""); + }, 2_000); + }); + }); + }, + }, + ], + }; + `, + }, { + 'flags': ['--dts'] + }) + + const dtsSuccessIndex = logs.indexOf('onDTSSuccess Called'); + const onSuccessIndex = logs.indexOf('onSuccess Called'); + const delayPluginStartIndex = logs.indexOf('delay plugin start'); + const delayPluginEndIndex = logs.indexOf('delay plugin end'); + expect(dtsSuccessIndex).toBeLessThan(onSuccessIndex) + expect(delayPluginStartIndex).toBeLessThan(delayPluginEndIndex) + expect(delayPluginEndIndex).toBeLessThan(onSuccessIndex) +}) + test('custom tsconfig', async () => { await run( getTestName(),