UNPKG

@kubb/core

Version:

Core functionality for Kubb's plugin-based code generation system, providing the foundation for transforming OpenAPI specifications.

213 lines (176 loc) • 6.03 kB
import path from 'node:path' import type { KubbFile } from '../fs/index.ts' import { getRelativePath } from '../fs/index.ts' import hash from 'object-hash' import { combineExports, combineImports, combineSources } from '../FileManager.ts' import type { Logger } from '../logger.ts' import type { Config } from '../types.ts' /** * Generate a default banner for files created by Kubb * @returns A string with the default banner */ export function getDefaultBanner({ title, description, version, config }: { title?: string; description?: string; version?: string; config: Config }): string { try { let source = '' if ('path' in config.input) { source = path.basename(config.input.path) } else if ('data' in config.input) { source = 'text content' } let banner = '/**\n* Generated by Kubb (https://kubb.dev/).\n* Do not edit manually.\n' if (config.output.defaultBanner === 'simple') { banner += '*/\n' return banner } if (source) { banner += `* Source: ${source}\n` } if (title) { banner += `* Title: ${title}\n` } if (description) { const formattedDescription = description.replace(/\n/gm, '\n* ') banner += `* Description: ${formattedDescription}\n` } if (version) { banner += `* OpenAPI spec version: ${version}\n` } banner += '*/\n' return banner } catch (_error) { // If there's any error in parsing the Oas data, return a simpler banner return '/**\n* Generated by Kubb (https://kubb.dev/).\n* Do not edit manually.\n*/' } } /** * Helper to create a file with name and id set */ export function createFile<TMeta extends object = object>(file: KubbFile.File<TMeta>): KubbFile.ResolvedFile<TMeta> { const extname = path.extname(file.baseName) as KubbFile.Extname if (!extname) { throw new Error(`No extname found for ${file.baseName}`) } const source = file.sources.map((item) => item.value).join('\n\n') const exports = file.exports?.length ? combineExports(file.exports) : [] const imports = file.imports?.length && source ? combineImports(file.imports, exports, source) : [] const sources = file.sources?.length ? combineSources(file.sources) : [] return { ...file, id: hash({ path: file.path }), name: trimExtName(file.baseName), extname, imports: imports.map(createFileImport), exports: exports.map(createFileExport), sources: sources.map(createFileSource), meta: file.meta || ({} as TMeta), } } /** * Helper to create a fileImport with extname set */ function createFileSource(source: KubbFile.Source): KubbFile.Source { return source } /** * Helper to create a fileImport with extname set */ export function createFileImport(imp: KubbFile.Import): KubbFile.ResolvedImport { return { ...imp, } } /** * Helper to create a fileExport with extname set */ export function createFileExport(exp: KubbFile.Export): KubbFile.ResolvedExport { return { ...exp, } } export type ParserModule<TMeta extends object = object> = { format: (source: string) => Promise<string> /** * Convert a file to string */ print: (file: KubbFile.ResolvedFile<TMeta>, options: PrintOptions) => Promise<string> } export function createFileParser<TMeta extends object = object>(parser: ParserModule<TMeta>): ParserModule<TMeta> { return parser } type PrintOptions = { extname?: KubbFile.Extname logger?: Logger } const typeScriptParser = createFileParser({ async format(source) { const module = await import('@kubb/parser-ts') return module.format(source) }, async print(file, options = { extname: '.ts' }) { const module = await import('@kubb/parser-ts') const source = file.sources.map((item) => item.value).join('\n\n') const importNodes = file.imports .map((item) => { const importPath = item.root ? getRelativePath(item.root, item.path) : item.path const hasExtname = !!path.extname(importPath) return module.factory.createImportDeclaration({ name: item.name, path: options.extname && hasExtname ? `${trimExtName(importPath)}${options.extname}` : item.root ? trimExtName(importPath) : importPath, isTypeOnly: item.isTypeOnly, }) }) .filter(Boolean) const exportNodes = file.exports .map((item) => { const exportPath = item.path const hasExtname = !!path.extname(exportPath) return module.factory.createExportDeclaration({ name: item.name, path: options.extname && hasExtname ? `${trimExtName(item.path)}${options.extname}` : trimExtName(item.path), isTypeOnly: item.isTypeOnly, asAlias: item.asAlias, }) }) .filter(Boolean) return [file.banner, module.print([...importNodes, ...exportNodes]), source, file.footer].join('\n') }, }) const tsxParser = createFileParser({ async format(source) { const module = await import('@kubb/parser-ts') //4 = tsx return module.format(source) }, async print(file, options = { extname: '.tsx' }) { return typeScriptParser.print(file, options) }, }) const defaultParser = createFileParser({ async format(source) { return source }, async print(file) { return file.sources.map((item) => item.value).join('\n\n') }, }) const parsers: Record<KubbFile.Extname, ParserModule<any>> = { '.ts': typeScriptParser, '.js': typeScriptParser, '.jsx': tsxParser, '.tsx': tsxParser, '.json': defaultParser, } export async function getFileParser<TMeta extends object = object>(extname: KubbFile.Extname | undefined): Promise<ParserModule<TMeta>> { if (!extname) { return defaultParser } const parser = parsers[extname] if (!parser) { console.warn(`[parser] No parser found for ${extname}, default parser will be used`) } return parser || defaultParser } function trimExtName(text: string): string { const extname = text.split('.').pop() return text.replace(`.${extname}`, '') }