dtsc
Version:
Generate bundled TypeScript declaration files
74 lines (65 loc) • 2.48 kB
text/typescript
import fs from 'fs/promises'
import { compile, load, TsConfig } from 'tsconfig-utils'
import { createRequire } from 'module'
import { join, resolve } from 'path'
import { bundle } from './bundle.js'
declare module 'tsconfig-utils' {
interface TsConfig {
dtsc: Config
}
}
export interface Config {
inline?: string[]
exclude?: string[]
}
async function compileToFile(filename: string, config: TsConfig) {
filename = filename.replace(/\.d\.ts$/, '') + '.tmp.d.ts'
config.set('project', '.', false)
config.set('outFile', filename)
config.set('composite', 'false')
config.set('incremental', 'false')
const code = await compile(config.args, { cwd: config.cwd })
if (code) process.exit(code)
const content = await fs.readFile(filename, 'utf8')
await fs.rm(filename)
return content
}
async function getModules(path: string, prefix = ''): Promise<string[]> {
const files = await fs.readdir(path, { withFileTypes: true })
return ([] as string[]).concat(...await Promise.all(files.map(async (file) => {
if (file.isDirectory()) {
return getModules(join(path, file.name), `${prefix}${file.name}/`)
} else if (file.name.endsWith('.ts')) {
return [prefix + file.name.slice(0, -3)]
} else {
return []
}
})))
}
export async function build(cwd: string, args: string[] = []) {
const require = createRequire(cwd + '/')
const config = await load(cwd, args)
const outFile = config.get('outFile')
if (!outFile) throw new Error('outFile is required')
const rootDir = config.get('rootDir')
if (!rootDir) throw new Error('rootDir is required')
const srcpath = `${cwd.replace(/\\/g, '/')}/${rootDir}`
const destpath = resolve(cwd, outFile)
const [files, input] = await Promise.all([
getModules(srcpath),
compileToFile(destpath, config),
])
let source = input
const { inline = [], exclude = [] } = config.dtsc || {}
files.push(...inline)
for (const extra of inline) {
const manifest = require(extra + '/package.json')
const filename = join(extra, manifest.typings || manifest.types)
const content = await fs.readFile(require.resolve(filename), 'utf8')
source += [`declare module "${extra}" {`, ...content.split('\n')].join('\n ') + '\n}\n'
}
const manifest = require(cwd + '/package.json')
const dependencies = Object.keys(manifest.dependencies || {})
const output = await bundle({ files, source, exclude, dependencies })
await fs.writeFile(destpath, output)
}