Skip to content

Commit 562937a

Browse files
committed
fix: rewrite emitted declaration specifiers
1 parent ade463d commit 562937a

2 files changed

Lines changed: 87 additions & 1 deletion

File tree

packages/dtsx/src/generator.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,44 @@ function getOutputPath(inputPath: string, config: DtsGenerationConfig): string {
684684
}
685685
}
686686

687+
function getDeclarationSpecifier(fromOutputPath: string, toOutputPath: string): string {
688+
let specifier = relative(dirname(fromOutputPath), toOutputPath).replace(/\\/g, '/')
689+
specifier = specifier
690+
.replace(/\.d\.mts$/, '')
691+
.replace(/\.d\.cts$/, '')
692+
.replace(/\.d\.ts$/, '')
693+
694+
if (!specifier.startsWith('.')) {
695+
specifier = `./${specifier}`
696+
}
697+
698+
return specifier
699+
}
700+
701+
function rewriteRelativeDeclarationSpecifiers(content: string, filePath: string, config: DtsGenerationConfig): string {
702+
const outputPath = getOutputPath(filePath, config)
703+
704+
const rewriteSpecifier = (specifier: string): string => {
705+
const resolved = resolveRelativeSpecifier(specifier, filePath)
706+
if (!resolved.isRelative || !resolved.resolved) {
707+
return specifier
708+
}
709+
710+
return getDeclarationSpecifier(outputPath, getOutputPath(resolved.resolved, config))
711+
}
712+
713+
return content
714+
.replace(/\b(from\s*)(['"])(\.{1,2}\/[^'"]+)\2/g, (_match, prefix: string, quote: string, specifier: string) => {
715+
return `${prefix}${quote}${rewriteSpecifier(specifier)}${quote}`
716+
})
717+
.replace(/\b(import\s*)(['"])(\.{1,2}\/[^'"]+)\2/g, (_match, prefix: string, quote: string, specifier: string) => {
718+
return `${prefix}${quote}${rewriteSpecifier(specifier)}${quote}`
719+
})
720+
.replace(/\b(import\s*\(\s*)(['"])(\.{1,2}\/[^'"]+)\2/g, (_match, prefix: string, quote: string, specifier: string) => {
721+
return `${prefix}${quote}${rewriteSpecifier(specifier)}${quote}`
722+
})
723+
}
724+
687725
/**
688726
* Process a single TypeScript file and generate its DTS
689727
*/
@@ -755,6 +793,7 @@ async function processFileWithStatsFromSource(
755793

756794
// Process declarations to generate DTS
757795
let dtsContent = processDeclarations(declarations, context, config.keepComments, config.importOrder)
796+
dtsContent = rewriteRelativeDeclarationSpecifiers(dtsContent, filePath, config)
758797

759798
// Run onAfterFile hooks (may modify output)
760799
if (pluginManager) {

packages/dtsx/test/generator.test.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
import type { DtsGenerationConfig } from '../src/types'
22
import { afterEach, describe, expect, it } from 'bun:test'
3-
import { mkdir, rm, writeFile } from 'node:fs/promises'
3+
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
4+
import { tmpdir } from 'node:os'
45
import { join } from 'node:path'
56
import { generate, processSource } from '../src/generator'
67
import { extractDeclarations } from '../src/extractor'
78
import { processCode } from './test-utils'
89

10+
const tempDirs: string[] = []
11+
12+
async function createTempDir(): Promise<string> {
13+
const tempDir = await mkdtemp(join(tmpdir(), 'dtsx-generator-'))
14+
tempDirs.push(tempDir)
15+
return tempDir
16+
}
17+
18+
afterEach(async () => {
19+
await Promise.all(tempDirs.splice(0).map(dir => rm(dir, { recursive: true, force: true })))
20+
})
21+
922
describe('processSource (stdin support)', () => {
1023
it('should process simple variable declaration', () => {
1124
const source = `export const foo: string = 'bar';`
@@ -97,6 +110,40 @@ describe('processSource (stdin support)', () => {
97110
})
98111
})
99112

113+
describe('generate relative declaration specifiers', () => {
114+
it('rewrites relative imports and exports from the emitted declaration path', async () => {
115+
const tempDir = await createTempDir()
116+
const srcDir = join(tempDir, 'src')
117+
const outDir = join(tempDir, 'dist')
118+
119+
await mkdir(join(srcDir, 'nested'), { recursive: true })
120+
await writeFile(join(tempDir, 'index.ts'), [
121+
`export * from './src/request'`,
122+
`export { Thing } from './src/nested/thing'`,
123+
`export interface Wrapper { request: import('./src/request').RequestInstance }`,
124+
].join('\n'))
125+
await writeFile(join(srcDir, 'request.ts'), `export interface RequestInstance { id: string }\n`)
126+
await writeFile(join(srcDir, 'nested', 'thing.ts'), `export interface Thing { name: string }\n`)
127+
128+
await generate({
129+
cwd: tempDir,
130+
root: '.',
131+
outdir: outDir,
132+
entrypoints: ['index.ts'],
133+
outputStructure: 'mirror',
134+
clean: false,
135+
keepComments: true,
136+
logLevel: 'error',
137+
})
138+
139+
const output = await readFile(join(outDir, 'index.d.ts'), 'utf8')
140+
141+
expect(output).toContain(`export * from './src/request';`)
142+
expect(output).toContain(`export { Thing } from './src/nested/thing';`)
143+
expect(output).toContain(`import('./src/request').RequestInstance`)
144+
})
145+
})
146+
100147
describe('generate with parallel processing', () => {
101148
const tempDir = join(__dirname, 'temp-parallel-test')
102149
const inputDir = join(tempDir, 'input')

0 commit comments

Comments
 (0)