-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3be1fcf
commit f8caae7
Showing
23 changed files
with
424 additions
and
6 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import type { DtsGenerationConfig } from './types' | ||
import { existsSync } from 'node:fs' | ||
import { resolve } from 'node:path' | ||
import process from 'node:process' | ||
import { deepMerge } from './utils' | ||
|
||
interface Options<T> { | ||
name: string | ||
cwd?: string | ||
defaultConfig: T | ||
} | ||
|
||
export async function loadConfig<T extends Record<string, unknown>>({ name, cwd, defaultConfig }: Options<T>): Promise<T> { | ||
const c = cwd ?? process.cwd() | ||
const configPath = resolve(c, `${name}.config`) | ||
|
||
if (existsSync(configPath)) { | ||
try { | ||
const importedConfig = await import(configPath) | ||
const loadedConfig = importedConfig.default || importedConfig | ||
return deepMerge(defaultConfig, loadedConfig) | ||
} | ||
catch (error) { | ||
console.error(`Error loading config from ${configPath}:`, error) | ||
} | ||
} | ||
|
||
return defaultConfig | ||
} | ||
|
||
// Get loaded config | ||
// eslint-disable-next-line antfu/no-top-level-await | ||
export const config: DtsGenerationConfig = await loadConfig({ | ||
name: 'dts', | ||
cwd: process.cwd(), | ||
defaultConfig: { | ||
cwd: process.cwd(), | ||
root: './src', | ||
entrypoints: ['**/*.ts'], | ||
outdir: './dist', | ||
keepComments: true, | ||
clean: true, | ||
tsconfigPath: './tsconfig.json', | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import type { DtsGenerationConfig, DtsGenerationOption } from './types' | ||
import { mkdir, rm } from 'node:fs/promises' | ||
import { dirname, join, parse, relative } from 'node:path' | ||
import { glob } from 'tinyglobby' | ||
import { config } from './config' | ||
import { extractTypeFromSource } from './extract' | ||
import { checkIsolatedDeclarations, getAllTypeScriptFiles, writeToFile } from './utils' | ||
|
||
export async function generateDeclarationsFromFiles(options?: DtsGenerationConfig): Promise<void> { | ||
// console.log('Generating declaration files...', options) | ||
try { | ||
// Check for isolatedModules setting | ||
const isIsolatedDeclarations = await checkIsolatedDeclarations(options) | ||
if (!isIsolatedDeclarations) { | ||
console.error('Error: isolatedModules must be set to true in your tsconfig.json. Ensure `tsc --noEmit` does not output any errors.') | ||
return | ||
} | ||
|
||
if (options?.clean) { | ||
// console.log('Cleaning output directory...') | ||
await rm(options.outdir, { recursive: true, force: true }) | ||
} | ||
|
||
let files: string[] | ||
if (options?.entrypoints) { | ||
files = await glob(options.entrypoints, { cwd: options.root ?? options.cwd, absolute: true }) | ||
} | ||
else { | ||
files = await getAllTypeScriptFiles(options?.root) | ||
} | ||
|
||
// console.log('Found the following TypeScript files:', files) | ||
|
||
for (const file of files) { | ||
// console.log(`Processing file: ${file}`) | ||
const fileDeclarations = await extractTypeFromSource(file) | ||
|
||
if (fileDeclarations) { | ||
const relativePath = relative(options?.root ?? './src', file) | ||
const parsedPath = parse(relativePath) | ||
const outputPath = join(options?.outdir ?? './dist', `${parsedPath.name}.d.ts`) | ||
|
||
// Ensure the directory exists | ||
await mkdir(dirname(outputPath), { recursive: true }) | ||
|
||
// Write the declarations without additional formatting | ||
await writeToFile(outputPath, fileDeclarations) | ||
|
||
// console.log(`Generated ${outputPath}`) | ||
} | ||
else { | ||
console.warn(`No declarations extracted for ${file}`) | ||
} | ||
} | ||
|
||
// console.log('Declaration file generation complete') | ||
} | ||
catch (error) { | ||
console.error('Error generating declarations:', error) | ||
} | ||
} | ||
|
||
export async function generate(options?: DtsGenerationOption): Promise<void> { | ||
await generateDeclarationsFromFiles({ ...config, ...options }) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export { config } from './config' | ||
export * from './extract' | ||
export * from './generate' | ||
export * from './types' | ||
export * from './utils' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/** | ||
* DtsGenerationConfig | ||
* | ||
* This is the configuration object for the DTS generation process. | ||
*/ | ||
export interface DtsGenerationConfig { | ||
cwd: string | ||
root: string | ||
entrypoints: string[] | ||
outdir: string | ||
keepComments: boolean | ||
clean: boolean | ||
tsconfigPath: string | ||
} | ||
|
||
/** | ||
* DtsGenerationOption | ||
* | ||
* This is the configuration object for the DTS generation process. | ||
*/ | ||
export type DtsGenerationOption = Partial<DtsGenerationConfig> | ||
|
||
/** | ||
* DtsGenerationOptions | ||
* | ||
* This is the configuration object for the DTS generation process. | ||
*/ | ||
export type DtsGenerationOptions = DtsGenerationOption | DtsGenerationOption[] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import type { DtsGenerationConfig } from './types' | ||
import { readdir, readFile } from 'node:fs/promises' | ||
import { extname, join } from 'node:path' | ||
import { config } from './config' | ||
|
||
export async function writeToFile(filePath: string, content: string): Promise<void> { | ||
await Bun.write(filePath, content) | ||
} | ||
|
||
export async function getAllTypeScriptFiles(directory?: string): Promise<string[]> { | ||
const dir = directory ?? config.root | ||
const entries = await readdir(dir, { withFileTypes: true }) | ||
|
||
const files = await Promise.all(entries.map((entry) => { | ||
const res = join(dir, entry.name) | ||
return entry.isDirectory() ? getAllTypeScriptFiles(res) : res | ||
})) | ||
|
||
return Array.prototype.concat(...files).filter(file => extname(file) === '.ts') | ||
} | ||
|
||
export async function checkIsolatedDeclarations(options?: DtsGenerationConfig): Promise<boolean> { | ||
try { | ||
const tsconfigPath = options?.tsconfigPath || join(options?.root ?? process.cwd(), 'tsconfig.json') | ||
const tsconfigContent = await readFile(tsconfigPath, 'utf-8') | ||
const tsconfig = JSON.parse(tsconfigContent) | ||
|
||
return tsconfig.compilerOptions?.isolatedDeclarations === true | ||
} | ||
catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.log('Error reading tsconfig.json:', error) | ||
return false | ||
} | ||
} | ||
|
||
export function formatDeclarations(declarations: string): string { | ||
const lines = declarations.split('\n') | ||
const formattedLines = lines.map((line) => { | ||
// Trim trailing spaces | ||
line = line.trimEnd() | ||
|
||
// Handle interface and type declarations | ||
if (line.startsWith('export interface') || line.startsWith('export type')) { | ||
const parts = line.split('{') | ||
if (parts.length > 1) { | ||
return `${parts[0].trim()} {${parts[1]}` | ||
} | ||
} | ||
|
||
// Remove semicolons from the end of lines | ||
if (line.endsWith(';')) { | ||
line = line.slice(0, -1) | ||
} | ||
|
||
return line | ||
}) | ||
|
||
// Join lines and ensure only one blank line between declarations | ||
let result = formattedLines.join('\n') | ||
result = result.replace(/\n{3,}/g, '\n\n') | ||
|
||
// Format comments | ||
result = result.replace(/\/\*\*\n([^*]*)(\n \*\/)/g, (match, content) => { | ||
const formattedContent = content | ||
.split('\n') | ||
.map((line: string) => ` *${line.trim() ? ` ${line.trim()}` : ''}`) | ||
.join('\n') | ||
return `/**\n${formattedContent}\n */` | ||
}) | ||
|
||
return `${result.trim()}\n` | ||
} | ||
|
||
export function formatComment(comment: string): string { | ||
const lines = comment.split('\n') | ||
return lines | ||
.map((line, index) => { | ||
if (index === 0) | ||
return '/**' | ||
if (index === lines.length - 1) | ||
return ' */' | ||
const trimmedLine = line.replace(/^\s*\*?\s?/, '').trim() | ||
return ` * ${trimmedLine}` | ||
}) | ||
.join('\n') | ||
} | ||
|
||
export function deepMerge<T extends object>(target: T, ...sources: Array<Partial<T>>): T { | ||
if (!sources.length) | ||
return target | ||
|
||
const source = sources.shift() | ||
|
||
if (isObject(target) && isObject(source)) { | ||
for (const key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
const sourceValue = source[key] | ||
if (isObject(sourceValue) && isObject(target[key])) { | ||
target[key] = deepMerge(target[key] as any, sourceValue as any) | ||
} | ||
else { | ||
(target as any)[key] = sourceValue | ||
} | ||
} | ||
} | ||
} | ||
|
||
return deepMerge(target, ...sources) | ||
} | ||
|
||
function isObject(item: unknown): item is Record<string, unknown> { | ||
return (item && typeof item === 'object' && !Array.isArray(item)) as boolean | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import type { DtsGenerationConfig } from './types' | ||
|
||
export declare function loadConfig<T extends Record<string, unknown>>({ name, cwd, defaultConfig }: Options<T>): Promise<T> | ||
|
||
export declare const config: DtsGenerationConfig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import type { DtsGenerationConfig, DtsGenerationOption } from './types' | ||
|
||
export declare function generateDeclarationsFromFiles(options?: DtsGenerationConfig): Promise<void> | ||
|
||
export declare function generate(options?: DtsGenerationOption): Promise<void> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export { config } from './config' | ||
export * from './extract' | ||
export * from './generate' | ||
export * from './types' | ||
export * from './utils' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/** | ||
* DtsGenerationConfig | ||
* | ||
* This is the configuration object for the DTS generation process. | ||
*/ | ||
export interface DtsGenerationConfig { | ||
cwd: string | ||
root: string | ||
entrypoints: string[] | ||
outdir: string | ||
keepComments: boolean | ||
clean: boolean | ||
tsconfigPath: string | ||
} | ||
|
||
/** | ||
* DtsGenerationOption | ||
* | ||
* This is the configuration object for the DTS generation process. | ||
*/ | ||
export type DtsGenerationOption = Partial<DtsGenerationConfig> | ||
|
||
/** | ||
* DtsGenerationOptions | ||
* | ||
* This is the configuration object for the DTS generation process. | ||
*/ | ||
export type DtsGenerationOptions = DtsGenerationOption | DtsGenerationOption[] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import type { DtsGenerationConfig } from './types' | ||
|
||
export declare function writeToFile(filePath: string, content: string): Promise<void> | ||
|
||
export declare function getAllTypeScriptFiles(directory?: string): Promise<string[]> | ||
|
||
export declare function checkIsolatedDeclarations(options?: DtsGenerationConfig): Promise<boolean> | ||
|
||
export declare function formatDeclarations(declarations: string): string | ||
|
||
export declare function formatComment(comment: string): string | ||
|
||
export declare function deepMerge<T extends object>(target: T, ...sources: Array<Partial<T>>): T |
Oops, something went wrong.