Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add option for interop default in cjs #947

Merged
merged 5 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,12 @@ Provide the following configuration in your `.vscode/settings.json` (or global)
"json.schemas": [
{
"url": "https://cdn.jsdelivr.net/npm/tsup/schema.json",
"fileMatch": [
"package.json",
"tsup.config.json"
]
"fileMatch": ["package.json", "tsup.config.json"]
}
]
}
```


### Multiple entrypoints

Beside using positional arguments `tsup [...files]` to specify multiple entrypoints, you can also use the cli flag `--entry`:
Expand All @@ -164,7 +160,7 @@ Beside using positional arguments `tsup [...files]` to specify multiple entrypoi
tsup --entry src/a.ts --entry src/b.ts
```

The associated output file names can be defined as follows:
The associated output file names can be defined as follows:

```bash
# Outputs `dist/foo.js` and `dist/bar.js`.
Expand Down Expand Up @@ -350,6 +346,14 @@ tsup src/index.ts --env.NODE_ENV production

When an entry file like `src/cli.ts` contains hashbang like `#!/bin/env node` tsup will automatically make the output file executable, so you don't have to run `chmod +x dist/cli.js`.

### Interop with CommonJS

By default, esbuild will transform `export default x` to `module.exports.default = x` in CommonJS, but you can change this behavior by using the `--cjsInterop` flag: If there are only default exports and no named exports, it will be transformed to `module.exports = x` instead.

```bash
tsup src/index.ts --cjsInterop
```

### Watch mode

```bash
Expand Down
1 change: 1 addition & 0 deletions src/cli-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export async function main(options: Options = {}) {
'--killSignal <signal>',
'Signal to kill child process, "SIGTERM" or "SIGKILL"'
)
.option('--cjsInterop', 'Enable cjs interop')
.action(async (files: string[], flags) => {
const { build } = await import('.')
Object.assign(options, {
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { sizeReporter } from './plugins/size-reporter'
import { treeShakingPlugin } from './plugins/tree-shaking'
import { copyPublicDir, isInPublicDir } from './lib/public-dir'
import { terserPlugin } from './plugins/terser'
import { cjsInterop } from './plugins/cjs-interop'

export type { Format, Options, NormalizedOptions }

Expand Down Expand Up @@ -254,6 +255,7 @@ export async function build(_options: Options) {
silent: options.silent,
}),
cjsSplitting(),
cjsInterop(),
es5(),
sizeReporter(),
terserPlugin({
Expand Down
5 changes: 5 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ export type Options = {
*/
publicDir?: string | boolean
killSignal?: KILL_SIGNAL
/**
* Interop default within `module.exports` in cjs
* @default false
*/
cjsInterop?: boolean
}

export type NormalizedOptions = Omit<
Expand Down
26 changes: 26 additions & 0 deletions src/plugins/cjs-interop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Plugin } from '../plugin'

export const cjsInterop = (): Plugin => {
return {
name: 'cjs-interop',

async renderChunk(code, info) {
if (
!this.options.cjsInterop ||
this.format !== 'cjs' ||
info.type !== 'chunk' ||
!/\.(js|cjs)$/.test(info.path) ||
!info.entryPoint ||
info.exports?.length !== 1 ||
info.exports[0] !== 'default'
) {
return
}

return {
code: code + '\nmodule.exports = exports.default',
map: info.map,
}
},
}
}
24 changes: 23 additions & 1 deletion src/rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,25 @@ const getRollupConfig = async (
},
}

const fixCjsExport: Plugin = {
name: 'tsup:fix-cjs-export',
renderChunk(code, info) {
if (
info.type !== 'chunk' ||
!/\.(ts|cts)$/.test(info.fileName) ||
!info.isEntry ||
info.exports?.length !== 1 ||
info.exports[0] !== 'default'
)
return

return code.replace(
/(?<=(?<=[;}]|^)\s*export\s*){\s*([\w$]+)\s*as\s+default\s*}/,
`= $1`
)
},
}

return {
inputConfig: {
input: dtsOptions.entry,
Expand Down Expand Up @@ -179,7 +198,7 @@ const getRollupConfig = async (
...(options.external || []),
],
},
outputConfig: options.format.map((format) => {
outputConfig: options.format.map((format): OutputOptions => {
const outputExtension =
options.outExtension?.({ format, options, pkgType: pkg.type }).dts ||
defaultOutExtension({ format, pkgType: pkg.type }).dts
Expand All @@ -190,6 +209,9 @@ const getRollupConfig = async (
banner: dtsOptions.banner,
footer: dtsOptions.footer,
entryFileNames: `[name]${outputExtension}`,
plugins: [
format === 'cjs' && options.cjsInterop && fixCjsExport,
].filter(Boolean),
}
}),
}
Expand Down