Skip to content

Commit 78288db

Browse files
committed
fix: warn for CommonJS dts inputs
Closes #240
1 parent 7776dfa commit 78288db

4 files changed

Lines changed: 106 additions & 1 deletion

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ Determines how the default export is emitted.
109109
If set to `true`, and you are only exporting a single item using `export default ...`,
110110
the output will use `export = ...` instead of the standard ES module syntax.
111111
This is useful for compatibility with CommonJS.
112+
This option only controls the output format and does not enable support for
113+
CommonJS-style `.d.ts` input.
112114

113115
#### `sideEffects`
114116

@@ -236,6 +238,11 @@ However, this functionality is limited to ESM output format. Consequently,
236238
and its corresponding type definition files (`.d.cts`).
237239
In such cases, the `emitDtsOnly` option can be particularly helpful.
238240

241+
The plugin expects ESM-style `.d.ts` input. CommonJS-style declaration syntax
242+
such as `export =` or `import x = require("x")` will produce a warning and is
243+
not guaranteed to bundle correctly. If this syntax comes from a dependency in
244+
`node_modules`, mark that dependency as external in your Rolldown config.
245+
239246
## Credits
240247

241248
The project is inspired by [rollup-plugin-dts](https://github.com/Swatinem/rollup-plugin-dts)

src/fake-js.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
filename_js_to_dts,
1414
RE_DTS,
1515
RE_DTS_MAP,
16+
RE_NODE_MODULES,
1617
replaceTemplateName,
1718
resolveTemplateFn,
1819
} from './filename.ts'
@@ -70,6 +71,7 @@ export function createFakeJsPlugin({
7071
const declarationMap = new Map<number /* declaration id */, DeclarationInfo>()
7172
const commentsMap = new Map<string /* filename */, t.Comment[]>()
7273
const typeOnlyMap = new Map<string /* filename */, string[]>()
74+
const warnedCjsDtsInputs = new Set<string>()
7375

7476
return {
7577
name: 'rolldown-plugin-dts:fake-js',
@@ -161,6 +163,15 @@ export function createFakeJsPlugin({
161163
const typeOnlyIds: string[] = []
162164
const identifierMap: Record<string, number> = Object.create(null)
163165

166+
if (!warnedCjsDtsInputs.has(id) && program.body.some(isCjsDtsInputSyntax)) {
167+
warnedCjsDtsInputs.add(id)
168+
this.warn(
169+
RE_NODE_MODULES.test(id)
170+
? `${id} uses CommonJS dts syntax. CommonJS dts modules cannot be reliably bundled by rolldown-plugin-dts. Please mark this module as external in your Rolldown config.`
171+
: `${id} uses CommonJS dts syntax. rolldown-plugin-dts does not support reliably bundling CommonJS dts input.`,
172+
)
173+
}
174+
164175
if (comments) {
165176
const directives = collectReferenceDirectives(comments)
166177
commentsMap.set(id, directives)
@@ -822,6 +833,14 @@ function collectReferenceDirectives(comment: t.Comment[], negative = false) {
822833
return comment.filter((c) => REFERENCE_RE.test(c.value) !== negative)
823834
}
824835

836+
function isCjsDtsInputSyntax(node: t.Statement): boolean {
837+
return (
838+
node.type === 'TSExportAssignment' ||
839+
(node.type === 'TSImportEqualsDeclaration' &&
840+
node.moduleReference.type === 'TSExternalModuleReference')
841+
)
842+
}
843+
825844
//#region Runtime binding variable
826845

827846
/**

src/options.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ export interface GeneralOptions {
8989
* If set to `true`, and you are only exporting a single item using `export default ...`,
9090
* the output will use `export = ...` instead of the standard ES module syntax.
9191
* This is useful for compatibility with CommonJS.
92+
* This only controls the output format and does not enable support for
93+
* CommonJS-style `.d.ts` input.
9294
*/
9395
cjsDefault?: boolean
9496

tests/index.test.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
2+
import { tmpdir } from 'node:os'
13
import path from 'node:path'
24
import { fileURLToPath } from 'node:url'
3-
import { rolldownBuild } from '@sxzz/test-utils'
5+
import { normalizePath, rolldownBuild } from '@sxzz/test-utils'
46
import { describe, expect, test } from 'vitest'
57
import { dts } from '../src/index.ts'
68
import { getTsgoPathFromNodeModules } from '../src/tsgo.ts'
@@ -157,6 +159,81 @@ describe('dts input', () => {
157159
expect(snapshot).toMatchSnapshot()
158160
})
159161

162+
test('warns for CommonJS dts input syntax', async () => {
163+
const warnings: string[] = []
164+
165+
await rolldownBuild(
166+
[
167+
path.resolve(
168+
dirname,
169+
'rollup-plugin-dts/issue-89-import-equals/index.d.ts',
170+
),
171+
],
172+
[dts({ dtsInput: true })],
173+
{
174+
onwarn(warning) {
175+
warnings.push(warning.message)
176+
},
177+
},
178+
)
179+
180+
expect(warnings).toHaveLength(2)
181+
expect(warnings).toEqual(
182+
expect.arrayContaining([
183+
expect.stringContaining('index.d.ts uses CommonJS dts syntax'),
184+
expect.stringContaining('bar.d.ts uses CommonJS dts syntax'),
185+
]),
186+
)
187+
expect(warnings.join('\n')).toContain(
188+
'rolldown-plugin-dts does not support reliably bundling CommonJS dts input',
189+
)
190+
})
191+
192+
test('warns to externalize CommonJS dts dependencies', async () => {
193+
const root = await mkdtemp(path.join(tmpdir(), 'rolldown-plugin-dts-'))
194+
const dep = path.join(root, 'node_modules/cjs-dts-dep')
195+
const warnings: string[] = []
196+
197+
try {
198+
await mkdir(dep, { recursive: true })
199+
await Promise.all([
200+
writeFile(
201+
path.join(root, 'index.d.ts'),
202+
`export interface Wrapped {\n value: import("cjs-dts-dep").Value\n}\n`,
203+
),
204+
writeFile(
205+
path.join(dep, 'package.json'),
206+
`{"name":"cjs-dts-dep","types":"index.d.ts"}`,
207+
),
208+
writeFile(
209+
path.join(dep, 'index.d.ts'),
210+
`declare namespace CjsDtsDep {\n export interface Value {\n foo: string\n }\n}\nexport = CjsDtsDep\n`,
211+
),
212+
])
213+
214+
await rolldownBuild(
215+
[path.join(root, 'index.d.ts')],
216+
[dts({ dtsInput: true })],
217+
{
218+
cwd: root,
219+
onwarn(warning) {
220+
warnings.push(warning.message)
221+
},
222+
},
223+
)
224+
} finally {
225+
await rm(root, { force: true, recursive: true })
226+
}
227+
228+
const cjsWarning =
229+
warnings.find((warning) =>
230+
normalizePath(warning).includes('node_modules/cjs-dts-dep/index.d.ts'),
231+
) ?? ''
232+
233+
expect(cjsWarning).toContain('uses CommonJS dts syntax')
234+
expect(cjsWarning).toContain('Please mark this module as external')
235+
})
236+
160237
test('input object', async () => {
161238
const { snapshot, chunks } = await rolldownBuild(
162239
{ index: path.resolve(dirname, 'fixtures/dts-input.d.ts') },

0 commit comments

Comments
 (0)