Skip to content

Commit

Permalink
Add experimentalDts.only
Browse files Browse the repository at this point in the history
  • Loading branch information
aryaemami59 committed Oct 26, 2024
1 parent 21b1193 commit aa59912
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 6 deletions.
19 changes: 19 additions & 0 deletions src/cli-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export async function main(options: Options = {}) {
'--experimental-dts [entry]',
'Generate declaration file (experimental)',
)
.option(
'--experimental-dts-only',
'Emit declaration files only (experimental)',
)
.alias('--experimentalDts-only')
.alias('--experimentalDtsOnly')
.option(
'--sourcemap [inline]',
'Generate external sourcemap, or inline source: --sourcemap inline',
Expand Down Expand Up @@ -133,6 +139,19 @@ export async function main(options: Options = {}) {
options.dts.only = true
}
}

if (flags.experimentalDts || flags.experimentalDtsOnly) {
options.experimentalDts = {}

if (typeof flags.experimentalDts === 'string') {
options.experimentalDts.entry = flags.experimentalDts
}

if (flags.experimentalDtsOnly) {
options.experimentalDts.only = true
}
}

if (flags.inject) {
const inject = ensureArray(flags.inject)
options.inject = inject
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export async function build(_options: Options) {
}

const mainTasks = async () => {
if (!options.dts?.only) {
if (!options.experimentalDts?.only && !options.dts?.only) {
let onSuccessProcess: ExecChild | undefined
let onSuccessCleanup: (() => any) | undefined | void
/** Files imported by the entry */
Expand Down
37 changes: 36 additions & 1 deletion src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,40 @@ export type DtsConfig = {
}

export type ExperimentalDtsConfig = {
/**
* When set to `true`, this option will emit only
* TypeScript declaration (`.d.ts`) files and skip generating the
* corresponding JavaScript files during the build process.
*
* @example
* <caption>#### Generate only TypeScript declaration files</caption>
*
* ```ts
* import { defineConfig } from 'tsup'
*
* export default defineConfig({
* entry: { index: 'src/index.ts' },
* format: ['esm', 'cjs'],
* experimentalDts: { only: true },
* })
* ```
*
* @default false
*
* @since 8.4.0
*
* #### CLI Equivalent:
* You can use the following CLI commands to achieve the same result:
*
* ```bash
* tsup src/index.ts --experimental-dts-only
* # or
* tsup src/index.ts --experimentalDts-only
* # or
* tsup src/index.ts --experimentalDtsOnly
* ```
*/
only?: boolean
entry?: InputOption
/**
* Overrides `compilerOptions`
Expand Down Expand Up @@ -258,7 +292,8 @@ export type Options = {
removeNodeProtocol?: boolean
}

export interface NormalizedExperimentalDtsConfig {
export interface NormalizedExperimentalDtsConfig extends ExperimentalDtsConfig {
only: boolean
entry: { [entryAlias: string]: string }
compilerOptions?: any
}
Expand Down
33 changes: 29 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,12 @@ const convertArrayEntriesToObjectEntries = (arrayOfEntries: string[]) => {
/**
* Resolves and standardizes entry paths into an object format. If the provided
* entry is a string or an array of strings, it resolves any potential glob
* patterns amd converts the result into an entry object. If the input is
* patterns and converts the result into an entry object. If the input is
* already an object, it is returned as-is.
*
* @param entryPaths - The entry paths to resolve. Can be a string, an array of strings, or an object.
* @returns A {@linkcode Promise | promise} that resolves to the standardized entry paths in object format.
*
* @example
*
* ```ts
Expand Down Expand Up @@ -358,6 +361,7 @@ const resolveEntryPaths = async (entryPaths: InputOption) => {
* @param options - The options containing entry points and experimental DTS
* configuration.
* @param tsconfig - The loaded TypeScript configuration data.
* @returns A {@linkcode Promise | promise} that resolves to the normalized experimental DTS configuration, or `undefined` if no entry or experimental DTS option is provided.
*
* @internal
*/
Expand All @@ -378,6 +382,8 @@ export const resolveExperimentalDtsConfig = async (
: resolvedEntryPaths

const normalizedExperimentalDtsConfig: NormalizedExperimentalDtsConfig = {
only: options.experimentalDts?.only || false,

compilerOptions: {
...(tsconfig.data.compilerOptions || {}),
...(options.experimentalDts?.compilerOptions || {}),
Expand All @@ -390,8 +396,23 @@ export const resolveExperimentalDtsConfig = async (
}

/**
* Resolves the initial experimental DTS configuration into a consistent
* {@link NormalizedExperimentalDtsConfig} object.
* Resolves the initial experimental DTS configuration
* into a consistent
* {@linkcode NormalizedExperimentalDtsConfig | experimentalDts config object}.
*
* This function handles different types of
* {@linkcode NormalizedExperimentalDtsConfig | experimentalDts} inputs:
* - If {@linkcode experimentalDts} is a `boolean`:
* - if `true`, it returns a default object with an empty entry (`{ entry: {} }`).
* - if `false`, it returns `undefined`.
* - If {@linkcode experimentalDts} is a `string`, it treats the string as a glob pattern, resolving it to entry paths and returning an object with the `entry` property.
* - If {@linkcode experimentalDts} is already an object ({@linkcode NormalizedExperimentalDtsConfig}), it resolves {@linkcode NormalizedExperimentalDtsConfig.entry | the entry paths} (if necessary) and returns the object with the updated entries.
*
* The function focuses specifically on normalizing the **initial**
* {@linkcode NormalizedExperimentalDtsConfig | experimentalDts configuration}.
*
* @param experimentalDts - The {@linkcode NormalizedExperimentalDtsConfig | experimentalDts} value, which can be a `boolean`, `string`, `object`, or `undefined`.
* @returns A {@linkcode Promise | promise} that resolves to a normalized {@linkcode NormalizedExperimentalDtsConfig | experimentalDts config object}, or `undefined` if the input was `false` or `undefined`.
*
* @internal
*/
Expand All @@ -403,19 +424,23 @@ export const resolveInitialExperimentalDtsConfig = async (
}

if (typeof experimentalDts === 'boolean')
return experimentalDts ? { entry: {} } : undefined
return experimentalDts ? { only: false, entry: {} } : undefined

if (typeof experimentalDts === 'string') {
// Treats the string as a glob pattern, resolving it to entry paths and
// returning an object with the `entry` property.
return {
only: false,

entry: convertArrayEntriesToObjectEntries(await glob(experimentalDts)),
}
}

return {
...experimentalDts,

only: experimentalDts?.only || false,

entry:
experimentalDts?.entry == null
? {}
Expand Down
103 changes: 103 additions & 0 deletions test/experimental-dts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,3 +604,106 @@ test('experimentalDts.entry can be a string of glob pattern', async ({
),
)
})

test('experimentalDts.only works', async ({ expect, task }) => {
const { outFiles } = await run(
getTestName(),
{
'src/types.ts': `export type Person = { name: string }`,
'src/index.ts': `export const foo = [1, 2, 3]\nexport type { Person } from './types.js'`,
'tsup.config.ts': `export default ${JSON.stringify(
{
name: task.name,
entry: { index: 'src/index.ts' },
format: ['esm', 'cjs'],
experimentalDts: { only: true },
} satisfies Options,
null,
2,
)}`,
'package.json': JSON.stringify(
{
name: 'experimental-dts-only-works',
description: task.name,
type: 'module',
},
null,
2,
),
'tsconfig.json': JSON.stringify(
{
compilerOptions: {
outDir: './dist',
rootDir: './src',
skipLibCheck: true,
strict: true,
},
include: ['src'],
},
null,
2,
),
},
{
entry: [],
},
)

expect(outFiles).toStrictEqual([
'_tsup-dts-rollup.d.cts',
'_tsup-dts-rollup.d.ts',
'index.d.cts',
'index.d.ts',
])
})

test('experimental-dts-only cli option works', async ({ expect, task }) => {
const { outFiles } = await run(
getTestName(),
{
'src/types.ts': `export type Person = { name: string }`,
'src/index.ts': `export const foo = [1, 2, 3]\nexport type { Person } from './types.js'`,
'tsup.config.ts': `export default ${JSON.stringify(
{
name: task.name,
format: ['esm', 'cjs'],
} satisfies Options,
null,
2,
)}`,
'package.json': JSON.stringify(
{
name: 'experimental-dts-only-cli-works',
description: task.name,
type: 'module',
},
null,
2,
),
'tsconfig.json': JSON.stringify(
{
compilerOptions: {
outDir: './dist',
rootDir: './src',
skipLibCheck: true,
strict: true,
},
include: ['src'],
},
null,
2,
),
},
{
entry: ['./src/index.ts'],
flags: ['--experimental-dts-only'],
},
)

expect(outFiles).toStrictEqual([
'_tsup-dts-rollup.d.cts',
'_tsup-dts-rollup.d.ts',
'index.d.cts',
'index.d.ts',
])
})

0 comments on commit aa59912

Please sign in to comment.