initial commit

This commit is contained in:
2026-04-05 16:08:59 +03:00
commit 75ab9bec9f
1117 changed files with 789034 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025-PRESENT Joaquín Sánchez <https://github.com/userquin>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,79 @@
# Fix TypeScript Declarations for default CJS exports
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![License][license-src]][license-href]
This utility will allow you to fix the TypeScript declaration in CommonJS modules when using default exports.
Check the CJS fixtures in the test folder and the [CJS](./CJS.md) document for further details when using `rollup-plugin-dts`.
## Features
- 🚀 Fix default exports in CommonJS modules via API or Rollup plugin
- ✨ Generate CommonJS `d.ts` and `d.cts` files from `d.mts` files
- 💥 Use it with custom builders like [unbuild](https://github.com/unjs/unbuild), [tsup](https://github.com/egoist/tsup) or [pkgroll](https://github.com/privatenumber/pkgroll) (right now only `unbuild` supported, `tsup` and `pkgroll` don't allow adding Rollup plugins)
## unbuild
> [!NOTE]
>
> [unbuild](https://github.com/unjs/unbuild) `v3.5.0` uses the Rollup plugin from this package to fix the default exports in CommonJS modules.
For older `unbuild` versions, you can add the Rollup plugin from this package using the `rollup:dts:options` hook.
> [!WARNING]
>
> You should register the plugin directly when enabling `rollup.emitCJS = true` option, otherwise you can get wrong transformations.
>
> The plugin exposed here is just a helper to fix the default exports in CommonJS modules, it cannot control what files are being generated, check the [declaration](https://github.com/unjs/unbuild?tab=readme-ov-file#configuration) option in the readme file.
You will need to remove its current internal plugin adding the one provided by this package:
```ts
// build.config.ts
import { FixDtsDefaultCjsExportsPlugin } from 'fix-dts-default-cjs-exports/rollup'
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
entries: ['<your-entry-points>'],
declaration: true,
clean: true,
rollup: { emitCJS: true },
hooks: {
'rollup:dts:options': (ctx, options) => {
/* uncomment this block if you want to remove the unbuild internal plugin
options.plugins = plugins.filter((p) => {
if (!p || typeof p === 'string' || Array.isArray(p) || !('name' in p))
return true
return p.name !== 'unbuild-fix-cjs-export-type'
})
*/
options.plugins.push(FixDtsDefaultCjsExportsPlugin({
warn: message => ctx.warnings.add(message)
}))
}
}
})
```
## tsup
Since [tsup](https://github.com/egoist/tsup) doesn't expose any hook to allow change internal configuration, we need a change in the package to include the Rollup plugin from this package instead its built-in one.
## pkgroll
[pkgroll](https://github.com/privatenumber/pkgroll) is exposing only the `cli`, we need a change in the package to include the Rollup plugin from this package instead its built-in one.
## License
[MIT](./LICENSE) License © 2025-PRESENT [Joaquín Sánchez](https://github.com/userquin)
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/fix-dts-default-cjs-exports?style=flat&colorA=18181B&colorB=F0DB4F
[npm-version-href]: https://npmjs.com/package/fix-dts-default-cjs-exports
[npm-downloads-src]: https://img.shields.io/npm/dm/fix-dts-default-cjs-exports?style=flat&colorA=18181B&colorB=F0DB4F
[npm-downloads-href]: https://npmjs.com/package/fix-dts-default-cjs-exports
[license-src]: https://img.shields.io/github/license/userquin/fix-dts-default-cjs-exports.svg?style=flat&colorA=18181B&colorB=F0DB4F
[license-href]: https://github.com/userquin/fix-dts-default-cjs-exports/blob/main/LICENSE

View File

@@ -0,0 +1,60 @@
'use strict';
var fs = require('node:fs/promises');
var utils = require('./utils-CylcaoNQ.cjs');
require('magic-string');
require('mlly');
function transformDtsDefaultCJSExports(code, fileName, options = {}) {
return utils.internalFixDefaultCJSExports(code, {
fileName,
// we don't need the imports (used only for exporting types optimization)
imports: []
}, options);
}
async function fixDtsFileDefaultCJSExports(dtsPath, options = {}) {
const result = transformDtsDefaultCJSExports(
await fs.readFile(dtsPath, "utf-8"),
dtsPath,
options
);
if (result) {
await fs.writeFile(dtsPath, result, "utf8");
}
return !!result;
}
async function transformESMDtsToCJSDts(dtsPath, dtsDestPath, options = {}) {
if (dtsPath === dtsDestPath) {
throw new Error(`dtsPath and dtsDestPath should be different: ${dtsPath}`);
}
const code = await fs.readFile(dtsPath, "utf-8");
const result = transformDtsDefaultCJSExports(
code,
dtsPath,
options
) ?? code;
const { transformLocalImports = defaultLocalImportsTransformer } = options;
await fs.writeFile(
dtsDestPath,
transformLocalImports(result, dtsPath, dtsDestPath),
"utf8"
);
}
function defaultLocalImportsTransformer(code, dtsPath, dtsDestPath) {
const from = dtsPath.endsWith(".d.mts") ? /\s+(from\s+["'].\.?\/.*(\.mjs)["'];?)\s+/g : /\s+(from\s+["'].\.?\/.*(\.js)["'];?)\s+/g;
let matcher = from.exec(code);
if (!matcher) {
return code;
}
const extension = dtsDestPath.endsWith("d.ts") ? ".js" : ".cjs";
while (matcher) {
code = code.replaceAll(matcher[1], matcher[1].replace(matcher[2], extension));
matcher = from.exec(code);
}
return code;
}
exports.defaultLocalImportsTransformer = defaultLocalImportsTransformer;
exports.fixDtsFileDefaultCJSExports = fixDtsFileDefaultCJSExports;
exports.transformDtsDefaultCJSExports = transformDtsDefaultCJSExports;
exports.transformESMDtsToCJSDts = transformESMDtsToCJSDts;

View File

@@ -0,0 +1,51 @@
interface Options {
warn?: (message: string) => void;
}
/**
* Fix default exports.
*
* **WARNING**: this function doesn't handle local imports/exports transformations.
*
* @param code The code to transform.
* @param fileName The file name.
* @param options The options to be used.
* @return The transformed code or `undefined` if no transformation was needed.
*/
declare function transformDtsDefaultCJSExports(code: string, fileName: string, options?: Options): string | undefined;
/**
* Fix default exports in the file and writes the changes to the file when needed, otherwise the files remains untouched.
*
* @param dtsPath The path to the file to fix.
* @param options The path
*/
declare function fixDtsFileDefaultCJSExports(dtsPath: string, options?: Options): Promise<boolean>;
interface TransformOptions {
warn?: (message: string) => void;
transformLocalImports?: (code: string, dtsPath: string, dtsDestPath: string) => string;
}
/**
* Given an `ESM` dts file, transform it to a `d.ts` or `d.cts` file fixing CJS default exports changing the import/exports when needed.
*
* @param dtsPath The source `ESM` (`d.ts` or `.d.mts`) file path.
* @param dtsDestPath The destination `.d.ts` or `.d.cts` file path.
* @param options The options to use.
* @see {@link defaultLocalImportsTransformer}
*/
declare function transformESMDtsToCJSDts(dtsPath: string, dtsDestPath: string, options?: TransformOptions): Promise<void>;
/**
* Given an `ESM` dts file, transform it to a `d.ts` or `d.cts` file fixing CJS default exports.
*
* **NOTE**: local imports/exports will be replaced with the corresponding extension using source and destination files:
* - when `dtsPath` is a `.d.ts` and `dtsDestPath` is a `d.cts`: `import { foo } from './foo.js'` -> `import { foo } from './foo.cjs'`
* - when `dtsPath` is a `.d.mts` and `dtsDestPath` is a `d.ts`: `import { foo } from './foo.mjs'` -> `import { foo } from './foo.js'`
* - when `dtsPath` is a `.d.mts` and `dtsDestPath` is a `d.cts`: `import { foo } from './foo.mjs'` -> `import { foo } from './foo.cjs'`
*
* @param code The code to transform.
* @param dtsPath The source `ESM` (`d.ts` or `.d.mts`) file path.
* @param dtsDestPath The destination `.d.ts` or `.d.cts` file path.
* @return The transformed code.
*/
declare function defaultLocalImportsTransformer(code: string, dtsPath: string, dtsDestPath: string): string;
export { type Options, type TransformOptions, defaultLocalImportsTransformer, fixDtsFileDefaultCJSExports, transformDtsDefaultCJSExports, transformESMDtsToCJSDts };

View File

@@ -0,0 +1,51 @@
interface Options {
warn?: (message: string) => void;
}
/**
* Fix default exports.
*
* **WARNING**: this function doesn't handle local imports/exports transformations.
*
* @param code The code to transform.
* @param fileName The file name.
* @param options The options to be used.
* @return The transformed code or `undefined` if no transformation was needed.
*/
declare function transformDtsDefaultCJSExports(code: string, fileName: string, options?: Options): string | undefined;
/**
* Fix default exports in the file and writes the changes to the file when needed, otherwise the files remains untouched.
*
* @param dtsPath The path to the file to fix.
* @param options The path
*/
declare function fixDtsFileDefaultCJSExports(dtsPath: string, options?: Options): Promise<boolean>;
interface TransformOptions {
warn?: (message: string) => void;
transformLocalImports?: (code: string, dtsPath: string, dtsDestPath: string) => string;
}
/**
* Given an `ESM` dts file, transform it to a `d.ts` or `d.cts` file fixing CJS default exports changing the import/exports when needed.
*
* @param dtsPath The source `ESM` (`d.ts` or `.d.mts`) file path.
* @param dtsDestPath The destination `.d.ts` or `.d.cts` file path.
* @param options The options to use.
* @see {@link defaultLocalImportsTransformer}
*/
declare function transformESMDtsToCJSDts(dtsPath: string, dtsDestPath: string, options?: TransformOptions): Promise<void>;
/**
* Given an `ESM` dts file, transform it to a `d.ts` or `d.cts` file fixing CJS default exports.
*
* **NOTE**: local imports/exports will be replaced with the corresponding extension using source and destination files:
* - when `dtsPath` is a `.d.ts` and `dtsDestPath` is a `d.cts`: `import { foo } from './foo.js'` -> `import { foo } from './foo.cjs'`
* - when `dtsPath` is a `.d.mts` and `dtsDestPath` is a `d.ts`: `import { foo } from './foo.mjs'` -> `import { foo } from './foo.js'`
* - when `dtsPath` is a `.d.mts` and `dtsDestPath` is a `d.cts`: `import { foo } from './foo.mjs'` -> `import { foo } from './foo.cjs'`
*
* @param code The code to transform.
* @param dtsPath The source `ESM` (`d.ts` or `.d.mts`) file path.
* @param dtsDestPath The destination `.d.ts` or `.d.cts` file path.
* @return The transformed code.
*/
declare function defaultLocalImportsTransformer(code: string, dtsPath: string, dtsDestPath: string): string;
export { type Options, type TransformOptions, defaultLocalImportsTransformer, fixDtsFileDefaultCJSExports, transformDtsDefaultCJSExports, transformESMDtsToCJSDts };

View File

@@ -0,0 +1,51 @@
interface Options {
warn?: (message: string) => void;
}
/**
* Fix default exports.
*
* **WARNING**: this function doesn't handle local imports/exports transformations.
*
* @param code The code to transform.
* @param fileName The file name.
* @param options The options to be used.
* @return The transformed code or `undefined` if no transformation was needed.
*/
declare function transformDtsDefaultCJSExports(code: string, fileName: string, options?: Options): string | undefined;
/**
* Fix default exports in the file and writes the changes to the file when needed, otherwise the files remains untouched.
*
* @param dtsPath The path to the file to fix.
* @param options The path
*/
declare function fixDtsFileDefaultCJSExports(dtsPath: string, options?: Options): Promise<boolean>;
interface TransformOptions {
warn?: (message: string) => void;
transformLocalImports?: (code: string, dtsPath: string, dtsDestPath: string) => string;
}
/**
* Given an `ESM` dts file, transform it to a `d.ts` or `d.cts` file fixing CJS default exports changing the import/exports when needed.
*
* @param dtsPath The source `ESM` (`d.ts` or `.d.mts`) file path.
* @param dtsDestPath The destination `.d.ts` or `.d.cts` file path.
* @param options The options to use.
* @see {@link defaultLocalImportsTransformer}
*/
declare function transformESMDtsToCJSDts(dtsPath: string, dtsDestPath: string, options?: TransformOptions): Promise<void>;
/**
* Given an `ESM` dts file, transform it to a `d.ts` or `d.cts` file fixing CJS default exports.
*
* **NOTE**: local imports/exports will be replaced with the corresponding extension using source and destination files:
* - when `dtsPath` is a `.d.ts` and `dtsDestPath` is a `d.cts`: `import { foo } from './foo.js'` -> `import { foo } from './foo.cjs'`
* - when `dtsPath` is a `.d.mts` and `dtsDestPath` is a `d.ts`: `import { foo } from './foo.mjs'` -> `import { foo } from './foo.js'`
* - when `dtsPath` is a `.d.mts` and `dtsDestPath` is a `d.cts`: `import { foo } from './foo.mjs'` -> `import { foo } from './foo.cjs'`
*
* @param code The code to transform.
* @param dtsPath The source `ESM` (`d.ts` or `.d.mts`) file path.
* @param dtsDestPath The destination `.d.ts` or `.d.cts` file path.
* @return The transformed code.
*/
declare function defaultLocalImportsTransformer(code: string, dtsPath: string, dtsDestPath: string): string;
export { type Options, type TransformOptions, defaultLocalImportsTransformer, fixDtsFileDefaultCJSExports, transformDtsDefaultCJSExports, transformESMDtsToCJSDts };

View File

@@ -0,0 +1,55 @@
import fs from 'node:fs/promises';
import { i as internalFixDefaultCJSExports } from './utils-DwzdDEfz.js';
import 'magic-string';
import 'mlly';
function transformDtsDefaultCJSExports(code, fileName, options = {}) {
return internalFixDefaultCJSExports(code, {
fileName,
// we don't need the imports (used only for exporting types optimization)
imports: []
}, options);
}
async function fixDtsFileDefaultCJSExports(dtsPath, options = {}) {
const result = transformDtsDefaultCJSExports(
await fs.readFile(dtsPath, "utf-8"),
dtsPath,
options
);
if (result) {
await fs.writeFile(dtsPath, result, "utf8");
}
return !!result;
}
async function transformESMDtsToCJSDts(dtsPath, dtsDestPath, options = {}) {
if (dtsPath === dtsDestPath) {
throw new Error(`dtsPath and dtsDestPath should be different: ${dtsPath}`);
}
const code = await fs.readFile(dtsPath, "utf-8");
const result = transformDtsDefaultCJSExports(
code,
dtsPath,
options
) ?? code;
const { transformLocalImports = defaultLocalImportsTransformer } = options;
await fs.writeFile(
dtsDestPath,
transformLocalImports(result, dtsPath, dtsDestPath),
"utf8"
);
}
function defaultLocalImportsTransformer(code, dtsPath, dtsDestPath) {
const from = dtsPath.endsWith(".d.mts") ? /\s+(from\s+["'].\.?\/.*(\.mjs)["'];?)\s+/g : /\s+(from\s+["'].\.?\/.*(\.js)["'];?)\s+/g;
let matcher = from.exec(code);
if (!matcher) {
return code;
}
const extension = dtsDestPath.endsWith("d.ts") ? ".js" : ".cjs";
while (matcher) {
code = code.replaceAll(matcher[1], matcher[1].replace(matcher[2], extension));
matcher = from.exec(code);
}
return code;
}
export { defaultLocalImportsTransformer, fixDtsFileDefaultCJSExports, transformDtsDefaultCJSExports, transformESMDtsToCJSDts };

View File

@@ -0,0 +1,55 @@
import fs from 'node:fs/promises';
import { i as internalFixDefaultCJSExports } from './utils-DwzdDEfz.mjs';
import 'magic-string';
import 'mlly';
function transformDtsDefaultCJSExports(code, fileName, options = {}) {
return internalFixDefaultCJSExports(code, {
fileName,
// we don't need the imports (used only for exporting types optimization)
imports: []
}, options);
}
async function fixDtsFileDefaultCJSExports(dtsPath, options = {}) {
const result = transformDtsDefaultCJSExports(
await fs.readFile(dtsPath, "utf-8"),
dtsPath,
options
);
if (result) {
await fs.writeFile(dtsPath, result, "utf8");
}
return !!result;
}
async function transformESMDtsToCJSDts(dtsPath, dtsDestPath, options = {}) {
if (dtsPath === dtsDestPath) {
throw new Error(`dtsPath and dtsDestPath should be different: ${dtsPath}`);
}
const code = await fs.readFile(dtsPath, "utf-8");
const result = transformDtsDefaultCJSExports(
code,
dtsPath,
options
) ?? code;
const { transformLocalImports = defaultLocalImportsTransformer } = options;
await fs.writeFile(
dtsDestPath,
transformLocalImports(result, dtsPath, dtsDestPath),
"utf8"
);
}
function defaultLocalImportsTransformer(code, dtsPath, dtsDestPath) {
const from = dtsPath.endsWith(".d.mts") ? /\s+(from\s+["'].\.?\/.*(\.mjs)["'];?)\s+/g : /\s+(from\s+["'].\.?\/.*(\.js)["'];?)\s+/g;
let matcher = from.exec(code);
if (!matcher) {
return code;
}
const extension = dtsDestPath.endsWith("d.ts") ? ".js" : ".cjs";
while (matcher) {
code = code.replaceAll(matcher[1], matcher[1].replace(matcher[2], extension));
matcher = from.exec(code);
}
return code;
}
export { defaultLocalImportsTransformer, fixDtsFileDefaultCJSExports, transformDtsDefaultCJSExports, transformESMDtsToCJSDts };

View File

@@ -0,0 +1,25 @@
'use strict';
var utils = require('./utils-CylcaoNQ.cjs');
require('magic-string');
require('mlly');
function cjsExportsDtsMatcher(info) {
return info.type === "chunk" && info.exports?.length > 0 && info.exports.includes("default") && /\.d\.c?ts$/.test(info.fileName);
}
function defaultCjsExportsDtsMatcher(info) {
return cjsExportsDtsMatcher(info) && info.isEntry;
}
function FixDtsDefaultCjsExportsPlugin(options = {}) {
const { matcher = defaultCjsExportsDtsMatcher } = options;
return {
name: "fix-dts-default-cjs-exports-plugin",
renderChunk(code, info) {
return matcher(info) ? utils.internalFixDefaultCJSExports(code, info, options) : void 0;
}
};
}
exports.FixDtsDefaultCjsExportsPlugin = FixDtsDefaultCjsExportsPlugin;
exports.cjsExportsDtsMatcher = cjsExportsDtsMatcher;
exports.defaultCjsExportsDtsMatcher = defaultCjsExportsDtsMatcher;

View File

@@ -0,0 +1,11 @@
import { RenderedChunk, Plugin } from 'rollup';
interface PluginOptions {
warn?: (message: string) => void;
matcher?: (info: RenderedChunk) => boolean;
}
declare function cjsExportsDtsMatcher(info: RenderedChunk): boolean;
declare function defaultCjsExportsDtsMatcher(info: RenderedChunk): boolean;
declare function FixDtsDefaultCjsExportsPlugin(options?: PluginOptions): Plugin;
export { FixDtsDefaultCjsExportsPlugin, type PluginOptions, cjsExportsDtsMatcher, defaultCjsExportsDtsMatcher };

View File

@@ -0,0 +1,11 @@
import { RenderedChunk, Plugin } from 'rollup';
interface PluginOptions {
warn?: (message: string) => void;
matcher?: (info: RenderedChunk) => boolean;
}
declare function cjsExportsDtsMatcher(info: RenderedChunk): boolean;
declare function defaultCjsExportsDtsMatcher(info: RenderedChunk): boolean;
declare function FixDtsDefaultCjsExportsPlugin(options?: PluginOptions): Plugin;
export { FixDtsDefaultCjsExportsPlugin, type PluginOptions, cjsExportsDtsMatcher, defaultCjsExportsDtsMatcher };

View File

@@ -0,0 +1,11 @@
import { RenderedChunk, Plugin } from 'rollup';
interface PluginOptions {
warn?: (message: string) => void;
matcher?: (info: RenderedChunk) => boolean;
}
declare function cjsExportsDtsMatcher(info: RenderedChunk): boolean;
declare function defaultCjsExportsDtsMatcher(info: RenderedChunk): boolean;
declare function FixDtsDefaultCjsExportsPlugin(options?: PluginOptions): Plugin;
export { FixDtsDefaultCjsExportsPlugin, type PluginOptions, cjsExportsDtsMatcher, defaultCjsExportsDtsMatcher };

View File

@@ -0,0 +1,21 @@
import { i as internalFixDefaultCJSExports } from './utils-DwzdDEfz.js';
import 'magic-string';
import 'mlly';
function cjsExportsDtsMatcher(info) {
return info.type === "chunk" && info.exports?.length > 0 && info.exports.includes("default") && /\.d\.c?ts$/.test(info.fileName);
}
function defaultCjsExportsDtsMatcher(info) {
return cjsExportsDtsMatcher(info) && info.isEntry;
}
function FixDtsDefaultCjsExportsPlugin(options = {}) {
const { matcher = defaultCjsExportsDtsMatcher } = options;
return {
name: "fix-dts-default-cjs-exports-plugin",
renderChunk(code, info) {
return matcher(info) ? internalFixDefaultCJSExports(code, info, options) : void 0;
}
};
}
export { FixDtsDefaultCjsExportsPlugin, cjsExportsDtsMatcher, defaultCjsExportsDtsMatcher };

View File

@@ -0,0 +1,21 @@
import { i as internalFixDefaultCJSExports } from './utils-DwzdDEfz.mjs';
import 'magic-string';
import 'mlly';
function cjsExportsDtsMatcher(info) {
return info.type === "chunk" && info.exports?.length > 0 && info.exports.includes("default") && /\.d\.c?ts$/.test(info.fileName);
}
function defaultCjsExportsDtsMatcher(info) {
return cjsExportsDtsMatcher(info) && info.isEntry;
}
function FixDtsDefaultCjsExportsPlugin(options = {}) {
const { matcher = defaultCjsExportsDtsMatcher } = options;
return {
name: "fix-dts-default-cjs-exports-plugin",
renderChunk(code, info) {
return matcher(info) ? internalFixDefaultCJSExports(code, info, options) : void 0;
}
};
}
export { FixDtsDefaultCjsExportsPlugin, cjsExportsDtsMatcher, defaultCjsExportsDtsMatcher };

View File

@@ -0,0 +1,201 @@
'use strict';
var MagicString = require('magic-string');
var mlly = require('mlly');
function internalFixDefaultCJSExports(code, info, options) {
const parsedExports = extractExports(code, info, options);
if (!parsedExports) {
return;
}
if (parsedExports.defaultExport.specifier) {
const imports = [];
for (const imp of mlly.findStaticImports(code)) {
if (!imp.imports) {
continue;
}
imports.push(mlly.parseStaticImport(imp));
}
const specifier = parsedExports.defaultExport.specifier;
const defaultImport = imports.find((i) => i.specifier === specifier);
return parsedExports.defaultExport._type === "named" ? handleDefaultNamedCJSExport(
code,
info,
parsedExports,
imports,
options,
defaultImport
) : handleDefaultCJSExportAsDefault(
code,
parsedExports,
imports,
defaultImport
) || handleNoSpecifierDefaultCJSExport(code, info, parsedExports);
}
return handleNoSpecifierDefaultCJSExport(code, info, parsedExports);
}
function extractExports(code, info, options) {
const defaultExport = mlly.findExports(code).find(
(e) => e.names.includes("default")
);
if (!defaultExport) {
options.warn?.(
/* c8 ignore next */
`No default export found in ${info.fileName}, it contains default export but cannot be parsed.`
);
return;
}
const match = defaultExport.code.match(/export\s*\{([^}]*)\}/);
if (!match?.length) {
options.warn?.(
`No default export found in ${info.fileName}, it contains default export but cannot be parsed.`
);
return;
}
let defaultAlias;
const exportsEntries = [];
for (const exp of match[1].split(",").map((e) => e.trim())) {
if (exp === "default") {
defaultAlias = exp;
continue;
}
const m = exp.match(/\s*as\s+default\s*/);
if (m) {
defaultAlias = exp.replace(m[0], "");
} else {
exportsEntries.push(exp);
}
}
if (!defaultAlias) {
options.warn?.(
`No default export found in ${info.fileName}, it contains default export but cannot be parsed.`
);
return;
}
return {
defaultExport,
defaultAlias,
exports: exportsEntries
};
}
function handleDefaultCJSExportAsDefault(code, { defaultExport, exports }, imports, defaultImport) {
if (defaultImport) {
return exports.length === 0 ? code.replace(
defaultExport.code,
`export = ${defaultImport.defaultImport}`
) : code.replace(
defaultExport.code,
`// @ts-ignore
export = ${defaultImport.defaultImport};
export { ${exports.join(", ")} } from '${defaultExport.specifier}'`
);
} else {
const magicString = new MagicString(code);
const lastImportPosition = imports.length > 0 ? imports.at(-1)?.end || 0 : 0;
if (lastImportPosition > 0) {
magicString.appendRight(
lastImportPosition,
`
import _default from '${defaultExport.specifier}';
`
);
} else {
magicString.prepend(
`import _default from '${defaultExport.specifier}';
`
);
}
return exports.length > 0 ? magicString.replace(
defaultExport.code,
`// @ts-ignore
export = _default;
export { ${exports.join(", ")} } from '${defaultExport.specifier}'`
).toString() : magicString.replace(defaultExport.code, "export = _default").toString();
}
}
function handleDefaultNamedCJSExport(code, info, parsedExports, imports, options, defaultImport) {
const { defaultAlias, defaultExport, exports } = parsedExports;
if (defaultAlias === "default") {
if (defaultImport && !defaultImport.defaultImport) {
options.warn?.(
`Cannot parse default export name from ${defaultImport.specifier} import at ${info.fileName}!.`
);
return;
}
return handleDefaultCJSExportAsDefault(
code,
parsedExports,
imports,
defaultImport
);
}
if (defaultImport) {
const namedExports = defaultImport.namedImports;
if (namedExports?.[defaultAlias] === defaultAlias) {
return exports.length === 0 ? code.replace(defaultExport.code, `export = ${defaultAlias}`) : code.replace(
defaultExport.code,
`// @ts-ignore
export = ${defaultAlias};
export { ${exports.join(", ")} }`
);
} else {
options.warn?.(
`Cannot parse "${defaultAlias}" named export from ${defaultImport.specifier} import at ${info.fileName}!.`
);
return void 0;
}
}
const magicString = new MagicString(code);
const lastImportPosition = imports.length > 0 ? imports.at(-1)?.end || 0 : 0;
if (lastImportPosition > 0) {
magicString.appendRight(
lastImportPosition,
`
import { ${defaultAlias} } from '${defaultExport.specifier}';
`
);
} else {
magicString.prepend(
`import { ${defaultAlias} } from '${defaultExport.specifier}';
`
);
}
return exports.length > 0 ? magicString.replace(
defaultExport.code,
`// @ts-ignore
export = ${defaultAlias};
export { ${exports.join(", ")} } from '${defaultExport.specifier}'`
).toString() : magicString.replace(defaultExport.code, `export = ${defaultAlias}`).toString();
}
function handleNoSpecifierDefaultCJSExport(code, info, { defaultAlias, defaultExport, exports }) {
let exportStatement = exports.length > 0 ? void 0 : "";
if (exportStatement === void 0) {
let someExternalExport = false;
const typeExportRegexp = /\s*type\s+/;
const allRemainingExports = exports.map((exp) => {
if (someExternalExport) {
return [exp, ""];
}
if (!info.imports.includes(exp)) {
const m = exp.match(typeExportRegexp);
if (m) {
const name = exp.replace(m[0], "").trim();
if (!info.imports.includes(name)) {
return [exp, name];
}
}
}
someExternalExport = true;
return [exp, ""];
});
exportStatement = someExternalExport ? `;
export { ${allRemainingExports.map(([e, _]) => e).join(", ")} }` : `;
export type { ${allRemainingExports.map(([_, t]) => t).join(", ")} }`;
}
return code.replace(
defaultExport.code,
`${exportStatement.length > 0 ? "// @ts-ignore\n" : ""}export = ${defaultAlias}${exportStatement}`
);
}
exports.internalFixDefaultCJSExports = internalFixDefaultCJSExports;

View File

@@ -0,0 +1,199 @@
import MagicString from 'magic-string';
import { findStaticImports, parseStaticImport, findExports } from 'mlly';
function internalFixDefaultCJSExports(code, info, options) {
const parsedExports = extractExports(code, info, options);
if (!parsedExports) {
return;
}
if (parsedExports.defaultExport.specifier) {
const imports = [];
for (const imp of findStaticImports(code)) {
if (!imp.imports) {
continue;
}
imports.push(parseStaticImport(imp));
}
const specifier = parsedExports.defaultExport.specifier;
const defaultImport = imports.find((i) => i.specifier === specifier);
return parsedExports.defaultExport._type === "named" ? handleDefaultNamedCJSExport(
code,
info,
parsedExports,
imports,
options,
defaultImport
) : handleDefaultCJSExportAsDefault(
code,
parsedExports,
imports,
defaultImport
) || handleNoSpecifierDefaultCJSExport(code, info, parsedExports);
}
return handleNoSpecifierDefaultCJSExport(code, info, parsedExports);
}
function extractExports(code, info, options) {
const defaultExport = findExports(code).find(
(e) => e.names.includes("default")
);
if (!defaultExport) {
options.warn?.(
/* c8 ignore next */
`No default export found in ${info.fileName}, it contains default export but cannot be parsed.`
);
return;
}
const match = defaultExport.code.match(/export\s*\{([^}]*)\}/);
if (!match?.length) {
options.warn?.(
`No default export found in ${info.fileName}, it contains default export but cannot be parsed.`
);
return;
}
let defaultAlias;
const exportsEntries = [];
for (const exp of match[1].split(",").map((e) => e.trim())) {
if (exp === "default") {
defaultAlias = exp;
continue;
}
const m = exp.match(/\s*as\s+default\s*/);
if (m) {
defaultAlias = exp.replace(m[0], "");
} else {
exportsEntries.push(exp);
}
}
if (!defaultAlias) {
options.warn?.(
`No default export found in ${info.fileName}, it contains default export but cannot be parsed.`
);
return;
}
return {
defaultExport,
defaultAlias,
exports: exportsEntries
};
}
function handleDefaultCJSExportAsDefault(code, { defaultExport, exports }, imports, defaultImport) {
if (defaultImport) {
return exports.length === 0 ? code.replace(
defaultExport.code,
`export = ${defaultImport.defaultImport}`
) : code.replace(
defaultExport.code,
`// @ts-ignore
export = ${defaultImport.defaultImport};
export { ${exports.join(", ")} } from '${defaultExport.specifier}'`
);
} else {
const magicString = new MagicString(code);
const lastImportPosition = imports.length > 0 ? imports.at(-1)?.end || 0 : 0;
if (lastImportPosition > 0) {
magicString.appendRight(
lastImportPosition,
`
import _default from '${defaultExport.specifier}';
`
);
} else {
magicString.prepend(
`import _default from '${defaultExport.specifier}';
`
);
}
return exports.length > 0 ? magicString.replace(
defaultExport.code,
`// @ts-ignore
export = _default;
export { ${exports.join(", ")} } from '${defaultExport.specifier}'`
).toString() : magicString.replace(defaultExport.code, "export = _default").toString();
}
}
function handleDefaultNamedCJSExport(code, info, parsedExports, imports, options, defaultImport) {
const { defaultAlias, defaultExport, exports } = parsedExports;
if (defaultAlias === "default") {
if (defaultImport && !defaultImport.defaultImport) {
options.warn?.(
`Cannot parse default export name from ${defaultImport.specifier} import at ${info.fileName}!.`
);
return;
}
return handleDefaultCJSExportAsDefault(
code,
parsedExports,
imports,
defaultImport
);
}
if (defaultImport) {
const namedExports = defaultImport.namedImports;
if (namedExports?.[defaultAlias] === defaultAlias) {
return exports.length === 0 ? code.replace(defaultExport.code, `export = ${defaultAlias}`) : code.replace(
defaultExport.code,
`// @ts-ignore
export = ${defaultAlias};
export { ${exports.join(", ")} }`
);
} else {
options.warn?.(
`Cannot parse "${defaultAlias}" named export from ${defaultImport.specifier} import at ${info.fileName}!.`
);
return void 0;
}
}
const magicString = new MagicString(code);
const lastImportPosition = imports.length > 0 ? imports.at(-1)?.end || 0 : 0;
if (lastImportPosition > 0) {
magicString.appendRight(
lastImportPosition,
`
import { ${defaultAlias} } from '${defaultExport.specifier}';
`
);
} else {
magicString.prepend(
`import { ${defaultAlias} } from '${defaultExport.specifier}';
`
);
}
return exports.length > 0 ? magicString.replace(
defaultExport.code,
`// @ts-ignore
export = ${defaultAlias};
export { ${exports.join(", ")} } from '${defaultExport.specifier}'`
).toString() : magicString.replace(defaultExport.code, `export = ${defaultAlias}`).toString();
}
function handleNoSpecifierDefaultCJSExport(code, info, { defaultAlias, defaultExport, exports }) {
let exportStatement = exports.length > 0 ? void 0 : "";
if (exportStatement === void 0) {
let someExternalExport = false;
const typeExportRegexp = /\s*type\s+/;
const allRemainingExports = exports.map((exp) => {
if (someExternalExport) {
return [exp, ""];
}
if (!info.imports.includes(exp)) {
const m = exp.match(typeExportRegexp);
if (m) {
const name = exp.replace(m[0], "").trim();
if (!info.imports.includes(name)) {
return [exp, name];
}
}
}
someExternalExport = true;
return [exp, ""];
});
exportStatement = someExternalExport ? `;
export { ${allRemainingExports.map(([e, _]) => e).join(", ")} }` : `;
export type { ${allRemainingExports.map(([_, t]) => t).join(", ")} }`;
}
return code.replace(
defaultExport.code,
`${exportStatement.length > 0 ? "// @ts-ignore\n" : ""}export = ${defaultAlias}${exportStatement}`
);
}
export { internalFixDefaultCJSExports as i };

View File

@@ -0,0 +1,199 @@
import MagicString from 'magic-string';
import { findStaticImports, parseStaticImport, findExports } from 'mlly';
function internalFixDefaultCJSExports(code, info, options) {
const parsedExports = extractExports(code, info, options);
if (!parsedExports) {
return;
}
if (parsedExports.defaultExport.specifier) {
const imports = [];
for (const imp of findStaticImports(code)) {
if (!imp.imports) {
continue;
}
imports.push(parseStaticImport(imp));
}
const specifier = parsedExports.defaultExport.specifier;
const defaultImport = imports.find((i) => i.specifier === specifier);
return parsedExports.defaultExport._type === "named" ? handleDefaultNamedCJSExport(
code,
info,
parsedExports,
imports,
options,
defaultImport
) : handleDefaultCJSExportAsDefault(
code,
parsedExports,
imports,
defaultImport
) || handleNoSpecifierDefaultCJSExport(code, info, parsedExports);
}
return handleNoSpecifierDefaultCJSExport(code, info, parsedExports);
}
function extractExports(code, info, options) {
const defaultExport = findExports(code).find(
(e) => e.names.includes("default")
);
if (!defaultExport) {
options.warn?.(
/* c8 ignore next */
`No default export found in ${info.fileName}, it contains default export but cannot be parsed.`
);
return;
}
const match = defaultExport.code.match(/export\s*\{([^}]*)\}/);
if (!match?.length) {
options.warn?.(
`No default export found in ${info.fileName}, it contains default export but cannot be parsed.`
);
return;
}
let defaultAlias;
const exportsEntries = [];
for (const exp of match[1].split(",").map((e) => e.trim())) {
if (exp === "default") {
defaultAlias = exp;
continue;
}
const m = exp.match(/\s*as\s+default\s*/);
if (m) {
defaultAlias = exp.replace(m[0], "");
} else {
exportsEntries.push(exp);
}
}
if (!defaultAlias) {
options.warn?.(
`No default export found in ${info.fileName}, it contains default export but cannot be parsed.`
);
return;
}
return {
defaultExport,
defaultAlias,
exports: exportsEntries
};
}
function handleDefaultCJSExportAsDefault(code, { defaultExport, exports }, imports, defaultImport) {
if (defaultImport) {
return exports.length === 0 ? code.replace(
defaultExport.code,
`export = ${defaultImport.defaultImport}`
) : code.replace(
defaultExport.code,
`// @ts-ignore
export = ${defaultImport.defaultImport};
export { ${exports.join(", ")} } from '${defaultExport.specifier}'`
);
} else {
const magicString = new MagicString(code);
const lastImportPosition = imports.length > 0 ? imports.at(-1)?.end || 0 : 0;
if (lastImportPosition > 0) {
magicString.appendRight(
lastImportPosition,
`
import _default from '${defaultExport.specifier}';
`
);
} else {
magicString.prepend(
`import _default from '${defaultExport.specifier}';
`
);
}
return exports.length > 0 ? magicString.replace(
defaultExport.code,
`// @ts-ignore
export = _default;
export { ${exports.join(", ")} } from '${defaultExport.specifier}'`
).toString() : magicString.replace(defaultExport.code, "export = _default").toString();
}
}
function handleDefaultNamedCJSExport(code, info, parsedExports, imports, options, defaultImport) {
const { defaultAlias, defaultExport, exports } = parsedExports;
if (defaultAlias === "default") {
if (defaultImport && !defaultImport.defaultImport) {
options.warn?.(
`Cannot parse default export name from ${defaultImport.specifier} import at ${info.fileName}!.`
);
return;
}
return handleDefaultCJSExportAsDefault(
code,
parsedExports,
imports,
defaultImport
);
}
if (defaultImport) {
const namedExports = defaultImport.namedImports;
if (namedExports?.[defaultAlias] === defaultAlias) {
return exports.length === 0 ? code.replace(defaultExport.code, `export = ${defaultAlias}`) : code.replace(
defaultExport.code,
`// @ts-ignore
export = ${defaultAlias};
export { ${exports.join(", ")} }`
);
} else {
options.warn?.(
`Cannot parse "${defaultAlias}" named export from ${defaultImport.specifier} import at ${info.fileName}!.`
);
return void 0;
}
}
const magicString = new MagicString(code);
const lastImportPosition = imports.length > 0 ? imports.at(-1)?.end || 0 : 0;
if (lastImportPosition > 0) {
magicString.appendRight(
lastImportPosition,
`
import { ${defaultAlias} } from '${defaultExport.specifier}';
`
);
} else {
magicString.prepend(
`import { ${defaultAlias} } from '${defaultExport.specifier}';
`
);
}
return exports.length > 0 ? magicString.replace(
defaultExport.code,
`// @ts-ignore
export = ${defaultAlias};
export { ${exports.join(", ")} } from '${defaultExport.specifier}'`
).toString() : magicString.replace(defaultExport.code, `export = ${defaultAlias}`).toString();
}
function handleNoSpecifierDefaultCJSExport(code, info, { defaultAlias, defaultExport, exports }) {
let exportStatement = exports.length > 0 ? void 0 : "";
if (exportStatement === void 0) {
let someExternalExport = false;
const typeExportRegexp = /\s*type\s+/;
const allRemainingExports = exports.map((exp) => {
if (someExternalExport) {
return [exp, ""];
}
if (!info.imports.includes(exp)) {
const m = exp.match(typeExportRegexp);
if (m) {
const name = exp.replace(m[0], "").trim();
if (!info.imports.includes(name)) {
return [exp, name];
}
}
}
someExternalExport = true;
return [exp, ""];
});
exportStatement = someExternalExport ? `;
export { ${allRemainingExports.map(([e, _]) => e).join(", ")} }` : `;
export type { ${allRemainingExports.map(([_, t]) => t).join(", ")} }`;
}
return code.replace(
defaultExport.code,
`${exportStatement.length > 0 ? "// @ts-ignore\n" : ""}export = ${defaultAlias}${exportStatement}`
);
}
export { internalFixDefaultCJSExports as i };

View File

@@ -0,0 +1,87 @@
{
"name": "fix-dts-default-cjs-exports",
"type": "module",
"version": "1.0.1",
"packageManager": "pnpm@10.6.3",
"description": "Utility to fix TypeScript declarations when using default exports in CommonJS.",
"author": "userquin <userquin@gmail.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/userquin/fix-dts-default-cjs-exports.git"
},
"bugs": "https://github.com/userquin/fix-dts-default-cjs-exports/issues",
"keywords": [
"rollup",
"cjs",
"typescript",
"default exports"
],
"sideEffects": false,
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./rollup": {
"import": {
"types": "./dist/rollup.d.mts",
"default": "./dist/rollup.mjs"
},
"require": {
"types": "./dist/rollup.d.cts",
"default": "./dist/rollup.cjs"
}
}
},
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"typesVersions": {
"*": {
"rollup": [
"./dist/rollup.d.ts"
]
}
},
"files": [
"dist"
],
"scripts": {
"prepack": "pkgroll --clean-dist",
"test:full": "pnpm test:coverage && pnpm test:attw",
"test:coverage": "vitest run --coverage",
"test": "vitest",
"lint": "eslint .",
"lint:fix": "pnpm lint --fix",
"release": "bumpp && npm publish",
"test:attw": "pnpm test:reexport-types && pnpm test:reexport-default && pnpm test:mixed-declarations",
"test:reexport-types": "attw --pack test/fixtures/reexport-types",
"test:reexport-default": "attw --pack test/fixtures/reexport-default",
"test:mixed-declarations": "attw --pack test/fixtures/mixed-declarations"
},
"dependencies": {
"magic-string": "^0.30.17",
"mlly": "^1.7.4",
"rollup": "^4.34.8"
},
"devDependencies": {
"@antfu/eslint-config": "^4.3.0",
"@arethetypeswrong/cli": "^0.17.4",
"@types/node": "^20.14.8",
"@vitest/coverage-v8": "^3.0.6",
"bumpp": "^10.0.3",
"eslint": "^9.21.0",
"pathe": "^2.0.3",
"pkgroll": "^2.11.0",
"typescript": "^5.7.3",
"unbuild": "3.3.1",
"vitest": "^3.0.6"
}
}