Skip to content

Commit

Permalink
chore: wip
Browse files Browse the repository at this point in the history
chore: wip

chore: wip

chore: wip

chore: wip

chore: wip

chore: wip
  • Loading branch information
chrisbbreuer committed Oct 22, 2024
1 parent 03e59cb commit ab7b1e3
Showing 1 changed file with 70 additions and 29 deletions.
99 changes: 70 additions & 29 deletions src/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ function generateDtsTypes(sourceCode: string): string {
const lines = sourceCode.split('\n')
const dtsLines: string[] = []
const imports: string[] = []
const exports: string[] = []
const usedTypes: Set<string> = new Set()
const typeSources: Map<string, string> = new Map()
let defaultExport = ''

let isMultiLineDeclaration = false
Expand All @@ -43,7 +44,7 @@ function generateDtsTypes(sourceCode: string): string {
}

if (line.trim().startsWith('import')) {
const processedImport = processImport(line)
const processedImport = processImport(line, typeSources)
imports.push(processedImport)
logDebug(`Processed import: ${processedImport}`)
continue
Expand All @@ -55,7 +56,7 @@ function generateDtsTypes(sourceCode: string): string {
continue
}

if (line.trim().startsWith('export') || isMultiLineDeclaration) {
if (line.trim().startsWith('export') || isMultiLineDeclaration || line.trim().startsWith('const') || line.trim().startsWith('interface') || line.trim().startsWith('type') || line.trim().startsWith('function')) {
currentDeclaration += `${line}\n`
bracketCount += (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length
isMultiLineDeclaration = bracketCount > 0
Expand All @@ -66,7 +67,7 @@ function generateDtsTypes(sourceCode: string): string {
logDebug(`Comment block added to dtsLines: ${lastCommentBlock.trimEnd()}`)
lastCommentBlock = ''
}
const processed = processDeclaration(currentDeclaration.trim())
const processed = processDeclaration(currentDeclaration.trim(), usedTypes)
if (processed) {
dtsLines.push(processed)
logDebug(`Processed declaration added to dtsLines: ${processed}`)
Expand All @@ -77,53 +78,79 @@ function generateDtsTypes(sourceCode: string): string {
}
}

const result = cleanOutput([...imports, '', ...dtsLines, '', ...exports].filter(Boolean).join('\n'))
const dynamicImports = Array.from(usedTypes).map((type) => {
const source = typeSources.get(type)
return source ? `import type { ${type} } from '${source}';` : ''
}).filter(Boolean)

const result = cleanOutput([...imports, ...dynamicImports, '', ...dtsLines].filter(Boolean).join('\n'))
const finalResult = defaultExport ? `${result}\n${defaultExport}` : result

logDebug('Final result:', finalResult)
return finalResult
}

function processImport(importLine: string): string {
function processImport(importLine: string, typeSources: Map<string, string>): string {
logDebug(`Processing import: ${importLine}`)
const importMatch = importLine.match(/import(?: type)? \{([^}]+)\} from ['"]([^'"]+)['"]/)
if (importMatch) {
const types = importMatch[1].split(',').map(type => type.trim())
const source = importMatch[2]
types.forEach(type => typeSources.set(type, source))
}
if (importLine.includes('type')) {
return importLine.replace('import', 'import type').replace('type type', 'type')
}
return importLine
}

function processDeclaration(declaration: string): string {
function processDeclaration(declaration: string, usedTypes: Set<string>): string {
logDebug(`Processing declaration: ${declaration}`)
if (declaration.startsWith('export const')) {
return processConstDeclaration(declaration)
}
else if (declaration.startsWith('const')) {
return processConstDeclaration(declaration, false)
}
else if (declaration.startsWith('export interface')) {
return processInterfaceDeclaration(declaration)
}
else if (declaration.startsWith('interface')) {
return processInterfaceDeclaration(declaration)
return processInterfaceDeclaration(declaration, false)
}
else if (declaration.startsWith('export type {')) {
return processTypeOnlyExport(declaration)
}
else if (declaration.startsWith('type {')) {
return processTypeOnlyExport(declaration, false)
}
else if (declaration.startsWith('export type')) {
return processTypeDeclaration(declaration)
}
else if (declaration.startsWith('type')) {
return processTypeDeclaration(declaration, false)
}
else if (declaration.startsWith('export function') || declaration.startsWith('export async function')) {
return processFunctionDeclaration(declaration)
return processFunctionDeclaration(declaration, usedTypes)
}
else if (declaration.startsWith('function') || declaration.startsWith('async function')) {
return processFunctionDeclaration(declaration, usedTypes, false)
}
else if (declaration.startsWith('export default')) {
return `${declaration};`
}
else if (declaration.startsWith('export')) {
return declaration
}
logDebug(`Declaration not processed: ${declaration}`)
return declaration
return `declare ${declaration}`
}

function processConstDeclaration(declaration: string): string {
function processConstDeclaration(declaration: string, isExported = true): string {
logDebug(`Processing const declaration: ${declaration}`)
const lines = declaration.split('\n')
const firstLine = lines[0]
const name = firstLine.split('export const')[1].split('=')[0].trim().split(':')[0].trim()
const name = firstLine.split('const')[1].split('=')[0].trim().split(':')[0].trim()

const properties = lines.slice(1, -1).map((line) => {
let inString = false
Expand Down Expand Up @@ -166,50 +193,59 @@ function processConstDeclaration(declaration: string): string {
return ` ${key.trim()}: ${value}${comment};`
}).join('\n')

return `export declare const ${name}: {\n${properties}\n};`
return `${isExported ? 'export ' : ''}declare const ${name}: {\n${properties}\n};`
}

function processInterfaceDeclaration(declaration: string): string {
function processInterfaceDeclaration(declaration: string, isExported = true): string {
logDebug(`Processing interface declaration: ${declaration}`)
const lines = declaration.split('\n')
const interfaceName = lines[0].split('interface')[1].split('{')[0].trim()
const interfaceBody = lines.slice(1, -1).map(line => ` ${line.trim()}`).join('\n')
const result = `export declare interface ${interfaceName} {\n${interfaceBody}\n}`
const interfaceBody = lines.slice(1, -1)
.map(line => ` ${line.trim().replace(/;?$/, ';')}`) // Ensure each line ends with a semicolon
.join('\n')
const result = `${isExported ? 'export ' : ''}declare interface ${interfaceName} {\n${interfaceBody}\n}`
logDebug(`Processed interface declaration: ${result}`)
return result
}

function processTypeOnlyExport(declaration: string): string {
function processTypeOnlyExport(declaration: string, isExported = true): string {
logDebug(`Processing type-only export: ${declaration}`)
return declaration.replace('export type', 'export declare type')
return declaration.replace('export type', `${isExported ? 'export ' : ''}declare type`).replace(/;$/, '')
}

function processTypeDeclaration(declaration: string): string {
function processTypeDeclaration(declaration: string, isExported = true): string {
logDebug(`Processing type declaration: ${declaration}`)
const lines = declaration.split('\n')
const firstLine = lines[0]
const typeName = firstLine.split('type')[1].split('=')[0].trim()
const typeBody = firstLine.split('=')[1]?.trim() || lines.slice(1).join('\n').trim().replace(/;$/, '')
const result = `export declare type ${typeName} = ${typeBody};`
const result = `${isExported ? 'export ' : ''}declare type ${typeName} = ${typeBody};`
logDebug(`Processed type declaration: ${result}`)
return result
}

function processFunctionDeclaration(declaration: string): string {
function processFunctionDeclaration(declaration: string, usedTypes: Set<string>, isExported = true): string {
logDebug(`Processing function declaration: ${declaration}`)
const functionSignature = declaration.split('{')[0].trim()
const asyncKeyword = functionSignature.includes('async') ? 'async ' : ''
const functionName = functionSignature.replace('export ', '').replace('async ', '').split('(')[0].trim()
const params = functionSignature.split('(')[1].split(')')[0].trim()
const returnType = getReturnType(declaration)
const result = `export declare ${asyncKeyword}function ${functionName}(${params}): ${returnType};`
const returnType = getReturnType(functionSignature)

// Track used types for dynamic imports
if (returnType && returnType !== 'void') {
usedTypes.add(returnType)
}

// Fix invalid ending `):;` to `;`
const result = `${isExported ? 'export ' : ''}declare ${asyncKeyword}function ${functionName}(${params}): ${returnType};`
logDebug(`Processed function declaration: ${result}`)
return result
}

function getReturnType(declaration: string): string {
const returnTypeMatch = declaration.match(/:\s*([^\s{]+)/)
return returnTypeMatch ? returnTypeMatch[1] : 'void'
function getReturnType(functionSignature: string): string {
const returnTypeMatch = functionSignature.match(/:\s*([^\s{]+)/)
return returnTypeMatch ? returnTypeMatch[1].replace(/;$/, '') : 'void'
}

function cleanOutput(output: string): string {
Expand All @@ -226,13 +262,18 @@ function cleanOutput(output: string): string {
.replace(/\n{3,}/g, '\n\n')
.replace(/;\n(\s*)\}/g, ';\n$1\n$1}')
.replace(/,\n\s*;/g, ';')
.replace(/;\s*\/\/\s*/g, '; // ')
.replace(/,\s*;/g, ';')
.replace(/;[\t\v\f\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*\n\s*\}/g, ';\n}')
.replace(/;\s*\/\/\s*/g, '; // ')
.replace(/;\s*\}/g, ';\n}')
.replace(/;\s*\/\/\s*/g, '; // ')
.replace(/export declare async function/g, 'export declare function')
.replace(/declare function function/g, 'declare function')
.replace(/declare async function async/g, 'declare async function')
.replace(/declare const const/g, 'declare const')
.replace(/declare type \{ ([^}]+) \} = ;/g, 'declare type { $1 };')
.replace(/declare function ([^(]+)\(\): ([^;]+)\);/g, 'declare function $1(): $2;')
.replace(/declare function ([^(]+)\(([^)]+)\): ([^;]+)\);/g, 'declare function $1($2): $3;')
.replace(/declare declare/g, 'declare')
.replace(/\):;/g, ');') // Fix invalid ending
.trim()

logDebug('Cleaned output:', result)
Expand Down

0 comments on commit ab7b1e3

Please sign in to comment.