Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat: add --dts-only flag to emit declarations only
  • Loading branch information
egoist committed Nov 23, 2021
1 parent 3fb9b8e commit 2d4f6b0
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 128 deletions.
6 changes: 5 additions & 1 deletion src/cli-main.ts
Expand Up @@ -38,6 +38,7 @@ export async function main(options: Options = {}) {
)
.option('--dts [entry]', 'Generate declaration file')
.option('--dts-resolve', 'Resolve externals types used for d.ts files')
.option('--dts-only', 'Emit declaration files only')
.option(
'--sourcemap [option]',
'Generate sourcemap, "external", "inline", "both"'
Expand Down Expand Up @@ -99,14 +100,17 @@ export async function main(options: Options = {}) {
const external = ensureArray(flags.external)
options.external = external
}
if (flags.dts || flags.dtsResolve) {
if (flags.dts || flags.dtsResolve || flags.dtsOnly) {
options.dts = {}
if (typeof flags.dts === 'string') {
options.dts.entry = flags.dts
}
if (flags.dtsResolve) {
options.dts.resolve = flags.dtsResolve
}
if (flags.dtsOnly) {
options.dts.only = true
}
}
if (flags.inject) {
const inject = ensureArray(flags.inject)
Expand Down
220 changes: 115 additions & 105 deletions src/index.ts
@@ -1,7 +1,7 @@
import path from 'path'
import fs from 'fs'
import { Worker } from 'worker_threads'
import type { MarkRequired, Buildable } from 'ts-essentials'
import type { MarkRequired } from 'ts-essentials'
import { removeFiles, debouncePromise } from './utils'
import { loadTsupConfig, resolveTsConfig } from './load'
import glob from 'globby'
Expand All @@ -13,15 +13,17 @@ import execa from 'execa'
import kill from 'tree-kill'
import { version } from '../package.json'
import { createLogger, setSilent } from './log'
import { Format, Options } from './options'
import { DtsConfig, Format, Options } from './options'
import { runEsbuild } from './esbuild'

export type { Format, Options }

export type NormalizedOptions = MarkRequired<
Options,
'entryPoints' | 'format' | 'outDir'
>
export type NormalizedOptions = Omit<
MarkRequired<Options, 'entryPoints' | 'format' | 'outDir'>,
'dts'
> & {
dts?: DtsConfig
}

export const defineConfig = (
options:
Expand Down Expand Up @@ -49,7 +51,7 @@ const normalizeOptions = async (
optionsFromConfigFile: Options | undefined,
optionsOverride: Options
) => {
const options: Buildable<NormalizedOptions> = {
const options: Options = {
...optionsFromConfigFile,
...optionsOverride,
}
Expand Down Expand Up @@ -102,6 +104,12 @@ const normalizeOptions = async (
options.target = 'node12'
}

if (options.dts === true) {
options.dts = {}
} else if (typeof options.dts === 'string') {
options.dts = { entry: options.dts }
}

return options as NormalizedOptions
}

Expand All @@ -128,117 +136,119 @@ export async function build(_options: Options) {
logger.info('CLI', 'Running in watch mode')
}

let existingOnSuccess: ChildProcess | undefined
if (!options.dts?.only) {
let existingOnSuccess: ChildProcess | undefined

async function killPreviousProcess() {
if (existingOnSuccess) {
await killProcess({
pid: existingOnSuccess.pid,
})
existingOnSuccess = undefined
async function killPreviousProcess() {
if (existingOnSuccess) {
await killProcess({
pid: existingOnSuccess.pid,
})
existingOnSuccess = undefined
}
}

const debouncedBuildAll = debouncePromise(
() => {
return buildAll()
},
100,
handleError
)

const buildAll = async () => {
const killPromise = killPreviousProcess()

if (options.clean) {
const extraPatterns = Array.isArray(options.clean)
? options.clean
: []
await removeFiles(
['**/*', '!**/*.d.ts', ...extraPatterns],
options.outDir
)
logger.info('CLI', 'Cleaning output folder')
}

const css: Map<string, string> = new Map()
await Promise.all([
...options.format.map((format, index) =>
runEsbuild(options, {
format,
css: index === 0 ? css : undefined,
logger,
})
),
])
await killPromise
if (options.onSuccess) {
const parts = parseArgsStringToArgv(options.onSuccess)
const exec = parts[0]
const args = parts.splice(1)
existingOnSuccess = execa(exec, args, {
stdio: 'inherit',
})
}
}
}

const debouncedBuildAll = debouncePromise(
() => {
return buildAll()
},
100,
handleError
)
const startWatcher = async () => {
if (!options.watch) return

const buildAll = async () => {
const killPromise = killPreviousProcess()
const { watch } = await import('chokidar')

if (options.clean) {
const extraPatterns = Array.isArray(options.clean)
? options.clean
const customIgnores = options.ignoreWatch
? Array.isArray(options.ignoreWatch)
? options.ignoreWatch
: [options.ignoreWatch]
: []
await removeFiles(
['**/*', '!**/*.d.ts', ...extraPatterns],
options.outDir

const ignored = [
'**/{.git,node_modules}/**',
options.outDir,
...customIgnores,
]

const watchPaths =
typeof options.watch === 'boolean'
? '.'
: Array.isArray(options.watch)
? options.watch.filter(
(path): path is string => typeof path === 'string'
)
: options.watch

logger.info(
'CLI',
`Watching for changes in ${
Array.isArray(watchPaths)
? watchPaths.map((v) => '"' + v + '"').join(' | ')
: '"' + watchPaths + '"'
}`
)
logger.info(
'CLI',
`Ignoring changes in ${ignored
.map((v) => '"' + v + '"')
.join(' | ')}`
)
logger.info('CLI', 'Cleaning output folder')
}

const css: Map<string, string> = new Map()
await Promise.all([
...options.format.map((format, index) =>
runEsbuild(options, {
format,
css: index === 0 ? css : undefined,
logger,
})
),
])
await killPromise
if (options.onSuccess) {
const parts = parseArgsStringToArgv(options.onSuccess)
const exec = parts[0]
const args = parts.splice(1)
existingOnSuccess = execa(exec, args, {
stdio: 'inherit',
const watcher = watch(watchPaths, {
ignoreInitial: true,
ignorePermissionErrors: true,
ignored,
})
watcher.on('all', async (type, file) => {
logger.info('CLI', `Change detected: ${type} ${file}`)
debouncedBuildAll()
})
}
}

const startWatcher = async () => {
if (!options.watch) return

const { watch } = await import('chokidar')

const customIgnores = options.ignoreWatch
? Array.isArray(options.ignoreWatch)
? options.ignoreWatch
: [options.ignoreWatch]
: []

const ignored = [
'**/{.git,node_modules}/**',
options.outDir,
...customIgnores,
]

const watchPaths =
typeof options.watch === 'boolean'
? '.'
: Array.isArray(options.watch)
? options.watch.filter(
(path): path is string => typeof path === 'string'
)
: options.watch

logger.info(
'CLI',
`Watching for changes in ${
Array.isArray(watchPaths)
? watchPaths.map((v) => '"' + v + '"').join(' | ')
: '"' + watchPaths + '"'
}`
)
logger.info(
'CLI',
`Ignoring changes in ${ignored
.map((v) => '"' + v + '"')
.join(' | ')}`
)
logger.info('CLI', `Target: ${options.target}`)

const watcher = watch(watchPaths, {
ignoreInitial: true,
ignorePermissionErrors: true,
ignored,
})
watcher.on('all', async (type, file) => {
logger.info('CLI', `Change detected: ${type} ${file}`)
debouncedBuildAll()
})
}
await buildAll()

logger.info('CLI', `Target: ${options.target}`)

await buildAll()

startWatcher()
startWatcher()
}

if (options.dts) {
const hasTypescript = resolveFrom.silent(process.cwd(), 'typescript')
Expand Down
17 changes: 9 additions & 8 deletions src/options.ts
Expand Up @@ -3,6 +3,14 @@ import type { InputOption } from 'rollup'

export type Format = 'cjs' | 'esm' | 'iife'

export type DtsConfig = {
entry?: InputOption
/** Resolve external types used in dts files from node_modules */
resolve?: boolean | (string | RegExp)[]
/** Emit declaration files only */
only?: boolean
}

/**
* The options available in tsup.config.ts
* Not all of them are available from CLI flags
Expand Down Expand Up @@ -40,14 +48,7 @@ export type Options = {
define?: {
[k: string]: string
}
dts?:
| boolean
| string
| {
entry?: InputOption
/** Resolve external types used in dts files from node_modules */
resolve?: boolean | (string | RegExp)[]
}
dts?: boolean | string | DtsConfig
sourcemap?: BuildOptions['sourcemap']
/** Don't bundle these packages */
external?: (string | RegExp)[]
Expand Down
8 changes: 2 additions & 6 deletions src/rollup.ts
Expand Up @@ -83,12 +83,8 @@ const getRollupConfig = async (

const compilerOptions = loadCompilerOptions(options.tsconfig)

const dtsOptions =
typeof options.dts === 'string'
? { entry: options.dts }
: options.dts === true
? { entry: options.entryPoints }
: { entry: options.entryPoints, ...options.dts }
const dtsOptions = options.dts || {}
dtsOptions.entry = dtsOptions.entry || options.entryPoints

if (Array.isArray(dtsOptions.entry) && dtsOptions.entry.length > 1) {
dtsOptions.entry = toObjectEntry(dtsOptions.entry)
Expand Down

0 comments on commit 2d4f6b0

Please sign in to comment.