mirror of
https://github.com/egoist/tsup.git
synced 2025-12-08 20:35:58 +00:00
feat: allow passing custom swc configuration to swcPlugin (#1313)
This commit is contained in:
parent
769aa49cae
commit
fdfd59afb2
@ -609,6 +609,64 @@ tsup --tsconfig tsconfig.prod.json
|
|||||||
|
|
||||||
By default, tsup try to find the `tsconfig.json` file in the current directory, if it's not found, it will use the default tsup config.
|
By default, tsup try to find the `tsconfig.json` file in the current directory, if it's not found, it will use the default tsup config.
|
||||||
|
|
||||||
|
### Using custom Swc configuration
|
||||||
|
|
||||||
|
When you use legacy TypeScript decorator by enabling `emitDecoratorMetadata` in your tsconfig, tsup will automatically use [SWC](https://swc.rs) to transpile
|
||||||
|
decorators. In this case, you can give extra swc configuration in the `tsup.config.ts` file.
|
||||||
|
|
||||||
|
For example, if you have to define `useDefineForClassFields`, you can do that as follows:
|
||||||
|
```ts
|
||||||
|
import { defineConfig } from 'tsup'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
entry: ['src/index.ts'],
|
||||||
|
splitting: false,
|
||||||
|
sourcemap: true,
|
||||||
|
clean: true,
|
||||||
|
swc: {
|
||||||
|
jsc: {
|
||||||
|
transform: {
|
||||||
|
useDefineForClassFields: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: some SWC options cannot be configured:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"parser": {
|
||||||
|
"syntax": "typescript",
|
||||||
|
"decorators": true
|
||||||
|
},
|
||||||
|
"transform": {
|
||||||
|
"legacyDecorator": true,
|
||||||
|
"decoratorMetadata": true
|
||||||
|
},
|
||||||
|
"keepClassNames": true,
|
||||||
|
"target": "es2022"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also define a custom `.swcrc` configuration file. Just set `swcrc` to `true`
|
||||||
|
in `tsup.config.ts` to allow SWC plugin to discover automatically your custom swc config file.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { defineConfig } from 'tsup'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
entry: ['src/index.ts'],
|
||||||
|
splitting: false,
|
||||||
|
sourcemap: true,
|
||||||
|
clean: true,
|
||||||
|
swc: {
|
||||||
|
swcrc: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### error: No matching export in "xxx.ts" for import "xxx"
|
### error: No matching export in "xxx.ts" for import "xxx"
|
||||||
|
|||||||
@ -181,6 +181,9 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"swc": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"globalName": {
|
"globalName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -138,7 +138,7 @@ export async function runEsbuild(
|
|||||||
skipNodeModulesBundle: options.skipNodeModulesBundle,
|
skipNodeModulesBundle: options.skipNodeModulesBundle,
|
||||||
tsconfigResolvePaths: options.tsconfigResolvePaths,
|
tsconfigResolvePaths: options.tsconfigResolvePaths,
|
||||||
}),
|
}),
|
||||||
options.tsconfigDecoratorMetadata && swcPlugin({ logger }),
|
options.tsconfigDecoratorMetadata && swcPlugin({ ...options.swc, logger }),
|
||||||
nativeNodeModulesPlugin(),
|
nativeNodeModulesPlugin(),
|
||||||
postcssPlugin({
|
postcssPlugin({
|
||||||
css,
|
css,
|
||||||
|
|||||||
101
src/esbuild/swc.test.ts
Normal file
101
src/esbuild/swc.test.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { describe, expect, test, vi } from 'vitest'
|
||||||
|
import { swcPlugin, type SwcPluginConfig } from './swc'
|
||||||
|
import { localRequire } from '../utils'
|
||||||
|
|
||||||
|
vi.mock('../utils')
|
||||||
|
|
||||||
|
const getFixture = async (opts: Partial<SwcPluginConfig> = {}) => {
|
||||||
|
const swc = {
|
||||||
|
transformFile: vi.fn().mockResolvedValue({
|
||||||
|
code: 'source-code',
|
||||||
|
map: JSON.stringify({
|
||||||
|
sources: ['file:///path/to/file.ts'],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger = {
|
||||||
|
warn: vi.fn(),
|
||||||
|
error: vi.fn(),
|
||||||
|
info: vi.fn(),
|
||||||
|
}
|
||||||
|
|
||||||
|
const build = {
|
||||||
|
initialOptions: {
|
||||||
|
keepNames: true,
|
||||||
|
},
|
||||||
|
onLoad: vi.fn(),
|
||||||
|
}
|
||||||
|
|
||||||
|
vi.mocked(localRequire).mockReturnValue(swc)
|
||||||
|
|
||||||
|
const plugin = swcPlugin({
|
||||||
|
...opts,
|
||||||
|
logger: logger as never,
|
||||||
|
})
|
||||||
|
|
||||||
|
await plugin.setup(build as never)
|
||||||
|
|
||||||
|
const onLoad = build.onLoad.mock.calls[0][1] as Function
|
||||||
|
|
||||||
|
return { swc, onLoad, logger, build }
|
||||||
|
}
|
||||||
|
describe('swcPlugin', () => {
|
||||||
|
test('swcPlugin transforms TypeScript code with decorators and default plugin swc option', async () => {
|
||||||
|
const { swc, onLoad } = await getFixture()
|
||||||
|
|
||||||
|
await onLoad({
|
||||||
|
path: 'file.ts',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(swc.transformFile).toHaveBeenCalledWith('file.ts', {
|
||||||
|
configFile: false,
|
||||||
|
jsc: {
|
||||||
|
keepClassNames: true,
|
||||||
|
parser: {
|
||||||
|
decorators: true,
|
||||||
|
syntax: 'typescript',
|
||||||
|
},
|
||||||
|
target: 'es2022',
|
||||||
|
transform: {
|
||||||
|
decoratorMetadata: true,
|
||||||
|
legacyDecorator: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sourceMaps: true,
|
||||||
|
swcrc: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test('swcPlugin transforms TypeScript code and use given plugin swc option', async () => {
|
||||||
|
const { swc, onLoad } = await getFixture({
|
||||||
|
jsc: {
|
||||||
|
transform: {
|
||||||
|
useDefineForClassFields: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await onLoad({
|
||||||
|
path: 'file.ts',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(swc.transformFile).toHaveBeenCalledWith('file.ts', {
|
||||||
|
configFile: false,
|
||||||
|
jsc: {
|
||||||
|
keepClassNames: true,
|
||||||
|
parser: {
|
||||||
|
decorators: true,
|
||||||
|
syntax: 'typescript',
|
||||||
|
},
|
||||||
|
target: 'es2022',
|
||||||
|
transform: {
|
||||||
|
decoratorMetadata: true,
|
||||||
|
legacyDecorator: true,
|
||||||
|
useDefineForClassFields: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sourceMaps: true,
|
||||||
|
swcrc: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -3,11 +3,13 @@
|
|||||||
*/
|
*/
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { localRequire } from '../utils'
|
import { localRequire } from '../utils'
|
||||||
import type { JscConfig } from '@swc/core'
|
import type { JscConfig, Options } from '@swc/core'
|
||||||
import type { Plugin } from 'esbuild'
|
import type { Plugin } from 'esbuild'
|
||||||
import type { Logger } from '../log'
|
import type { Logger } from '../log'
|
||||||
|
|
||||||
export const swcPlugin = ({ logger }: { logger: Logger }): Plugin => {
|
export type SwcPluginConfig = { logger: Logger } & Options
|
||||||
|
|
||||||
|
export const swcPlugin = ({ logger, ...swcOptions }: SwcPluginConfig): Plugin => {
|
||||||
return {
|
return {
|
||||||
name: 'swc',
|
name: 'swc',
|
||||||
|
|
||||||
@ -29,11 +31,14 @@ export const swcPlugin = ({ logger }: { logger: Logger }): Plugin => {
|
|||||||
const isTs = /\.tsx?$/.test(args.path)
|
const isTs = /\.tsx?$/.test(args.path)
|
||||||
|
|
||||||
const jsc: JscConfig = {
|
const jsc: JscConfig = {
|
||||||
|
...swcOptions.jsc,
|
||||||
parser: {
|
parser: {
|
||||||
|
...swcOptions.jsc?.parser,
|
||||||
syntax: isTs ? 'typescript' : 'ecmascript',
|
syntax: isTs ? 'typescript' : 'ecmascript',
|
||||||
decorators: true,
|
decorators: true,
|
||||||
},
|
},
|
||||||
transform: {
|
transform: {
|
||||||
|
...swcOptions.jsc?.transform,
|
||||||
legacyDecorator: true,
|
legacyDecorator: true,
|
||||||
decoratorMetadata: true,
|
decoratorMetadata: true,
|
||||||
},
|
},
|
||||||
@ -42,10 +47,11 @@ export const swcPlugin = ({ logger }: { logger: Logger }): Plugin => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await swc.transformFile(args.path, {
|
const result = await swc.transformFile(args.path, {
|
||||||
|
...swcOptions,
|
||||||
jsc,
|
jsc,
|
||||||
sourceMaps: true,
|
sourceMaps: true,
|
||||||
configFile: false,
|
configFile: false,
|
||||||
swcrc: false,
|
swcrc: swcOptions.swcrc ?? false,
|
||||||
})
|
})
|
||||||
|
|
||||||
let code = result.code
|
let code = result.code
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import type { MinifyOptions } from 'terser'
|
|||||||
import type { MarkRequired } from 'ts-essentials'
|
import type { MarkRequired } from 'ts-essentials'
|
||||||
import type { Plugin } from './plugin'
|
import type { Plugin } from './plugin'
|
||||||
import type { TreeshakingStrategy } from './plugins/tree-shaking'
|
import type { TreeshakingStrategy } from './plugins/tree-shaking'
|
||||||
|
import type { SwcPluginConfig } from './esbuild/swc.js'
|
||||||
|
|
||||||
export type KILL_SIGNAL = 'SIGKILL' | 'SIGTERM'
|
export type KILL_SIGNAL = 'SIGKILL' | 'SIGTERM'
|
||||||
|
|
||||||
@ -256,6 +257,8 @@ export type Options = {
|
|||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
removeNodeProtocol?: boolean
|
removeNodeProtocol?: boolean
|
||||||
|
|
||||||
|
swc?: SwcPluginConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NormalizedExperimentalDtsConfig {
|
export interface NormalizedExperimentalDtsConfig {
|
||||||
@ -272,4 +275,5 @@ export type NormalizedOptions = Omit<
|
|||||||
tsconfigResolvePaths: Record<string, string[]>
|
tsconfigResolvePaths: Record<string, string[]>
|
||||||
tsconfigDecoratorMetadata?: boolean
|
tsconfigDecoratorMetadata?: boolean
|
||||||
format: Format[]
|
format: Format[]
|
||||||
|
swc?: SwcPluginConfig
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,5 +4,6 @@ export default defineConfig({
|
|||||||
test: {
|
test: {
|
||||||
testTimeout: 50000,
|
testTimeout: 50000,
|
||||||
globalSetup: 'vitest-global.ts',
|
globalSetup: 'vitest-global.ts',
|
||||||
|
include: ["test/*.test.ts", "src/**/*.test.ts"]
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user