feat: make a SWC plugin to emit decorator metadata (#48)

This commit is contained in:
alex8088 2022-11-02 01:34:19 +08:00
parent 6bae6ac6b4
commit 97ad5fa726
4 changed files with 284 additions and 0 deletions

View file

@ -50,12 +50,19 @@
]
},
"peerDependencies": {
"@swc/core": "^1.0.0",
"vite": "^3.0.0"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
}
},
"devDependencies": {
"@microsoft/api-extractor": "^7.33.5",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-typescript": "^9.0.2",
"@swc/core": "^1.3.11",
"@types/node": "16.18.3",
"@typescript-eslint/eslint-plugin": "^5.42.0",
"@typescript-eslint/parser": "^5.42.0",

View file

@ -6,6 +6,7 @@ specifiers:
'@microsoft/api-extractor': ^7.33.5
'@rollup/plugin-node-resolve': ^15.0.1
'@rollup/plugin-typescript': ^9.0.2
'@swc/core': ^1.3.11
'@types/node': 16.18.3
'@typescript-eslint/eslint-plugin': ^5.42.0
'@typescript-eslint/parser': ^5.42.0
@ -35,6 +36,7 @@ devDependencies:
'@microsoft/api-extractor': 7.33.5
'@rollup/plugin-node-resolve': 15.0.1_rollup@2.79.1
'@rollup/plugin-typescript': 9.0.2_ee2lsxs7bbggrcawnn34dqpc4e
'@swc/core': 1.3.11
'@types/node': 16.18.3
'@typescript-eslint/eslint-plugin': 5.42.0_6xw5wg2354iw4zujk2f3vyfrzu
'@typescript-eslint/parser': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
@ -501,6 +503,168 @@ packages:
string-argv: 0.3.1
dev: true
/@swc/core-android-arm-eabi/1.3.11:
resolution: {integrity: sha512-LC9JlMcdFmTU94KKmQkJKaPSeVmYTfVm2rKGESMiFrgIjopXL/Zeg+XHA97ucnh5iUMkWIpXAMzSOaVRs33K5g==}
engines: {node: '>=10'}
cpu: [arm]
os: [android]
requiresBuild: true
dependencies:
'@swc/wasm': 1.2.122
dev: true
optional: true
/@swc/core-android-arm64/1.3.11:
resolution: {integrity: sha512-M7FamR3kFpVTyTw73FzKcOZmS7/TWHX75eqtwBTaU9fW4shf0KTLr/h9DnMxNKAnwUMeub/lqlINUe5EKFIKwQ==}
engines: {node: '>=10'}
cpu: [arm64]
os: [android]
requiresBuild: true
dependencies:
'@swc/wasm': 1.2.130
dev: true
optional: true
/@swc/core-darwin-arm64/1.3.11:
resolution: {integrity: sha512-LOoiw3uQDuoKBLW3Mn8p6wIccpYjAoDI3ROdto4MksLQSraHMufXY8bqqncfVuy1750XZmC1qnU39RC3yihPfA==}
engines: {node: '>=10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@swc/core-darwin-x64/1.3.11:
resolution: {integrity: sha512-ycjrEbWmpU8MTDdVLdf76ClxQCSTfNqSoP59hieLzhmXpXUa7Oy4sN/v6WSQgp4I1euGs1Wp5kPU5hH5f7XBJQ==}
engines: {node: '>=10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@swc/core-freebsd-x64/1.3.11:
resolution: {integrity: sha512-02uqYktPp6WmZfZ2Crc/yIVOcgANtjo8ciHcT7yLHvz7v+S7gx1I2tyNGUFtTX5hcR2IFNGrL8Yj4DvpTABFHg==}
engines: {node: '>=10'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dependencies:
'@swc/wasm': 1.2.130
dev: true
optional: true
/@swc/core-linux-arm-gnueabihf/1.3.11:
resolution: {integrity: sha512-nZ2T/gPFncsIiFGhVeVY9vRCTX0hTdqso8OEvFhSwRfRvcEYOpb/rhMG09are7YoB44GMiku5tSzEiNmvT3GuQ==}
engines: {node: '>=10'}
cpu: [arm]
os: [linux]
requiresBuild: true
dependencies:
'@swc/wasm': 1.2.130
dev: true
optional: true
/@swc/core-linux-arm64-gnu/1.3.11:
resolution: {integrity: sha512-EWEd8NnGwhykEDFIet/r4Fcfr6805ecnBniHZWmG8UVYUp5tz7LYEMZesxCxa0+aGVpCmxHL5/Cdk1uEIrVIzg==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@swc/core-linux-arm64-musl/1.3.11:
resolution: {integrity: sha512-ms7CLj2+8sfTM2QnnwqDheYRky9rgPpX2hXmc0KltX+AiSAs7WURjn2JwXWkaICDzXL+djennfswxSspJ53knw==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@swc/core-linux-x64-gnu/1.3.11:
resolution: {integrity: sha512-Fq5/QEY0IbzpIrqlvQT59aMf+nLk//esL3Aj4nvZdsvF4iZaD2oHtXW+/MBi0i1HV9OBWApGZMygYNVS0uVZkQ==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@swc/core-linux-x64-musl/1.3.11:
resolution: {integrity: sha512-S/KoEgRHwGhs7VunHiz4jLrnFOJvqZe391j2MiYN1p2EThoGI3rvwcUoHkoxLCXVuDbi4E91qodOheaMGetWNA==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@swc/core-win32-arm64-msvc/1.3.11:
resolution: {integrity: sha512-fFby7KOQIxolR6w4Gie8MSkgQ3ee6j3r7A6PX4ekzu+509QsZogLPZnWFTJ8WFo7ui0tx/ocA5X8BV4ZNBVlKQ==}
engines: {node: '>=10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dependencies:
'@swc/wasm': 1.2.130
dev: true
optional: true
/@swc/core-win32-ia32-msvc/1.3.11:
resolution: {integrity: sha512-fii7Y33S9Z5oZ/BTsXa8Ou/RZ4T/ZsAOFpG7mMvifpdAP6LVWdhLoNy7SeeEHTEAWGcWNA/FslA6p2WETvEEfA==}
engines: {node: '>=10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dependencies:
'@swc/wasm': 1.2.130
dev: true
optional: true
/@swc/core-win32-x64-msvc/1.3.11:
resolution: {integrity: sha512-cDF4qBQLf3U0KypnLn3T1HtE12QmEplPGjWs2Xd/hzVkjsPixVR8XIgLlczsj7Pk7Of7VDnUDLIl52aIdgTm8w==}
engines: {node: '>=10'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@swc/core/1.3.11:
resolution: {integrity: sha512-lnCnnnNCsnbrhW/gwkoN0sAeIqOyoHLS4ZB20xmPJjKVfvTnJrAcNnLSiwlYdcoUSFqT2GYZjUAG6usEzRQASA==}
engines: {node: '>=10'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@swc/core-android-arm-eabi': 1.3.11
'@swc/core-android-arm64': 1.3.11
'@swc/core-darwin-arm64': 1.3.11
'@swc/core-darwin-x64': 1.3.11
'@swc/core-freebsd-x64': 1.3.11
'@swc/core-linux-arm-gnueabihf': 1.3.11
'@swc/core-linux-arm64-gnu': 1.3.11
'@swc/core-linux-arm64-musl': 1.3.11
'@swc/core-linux-x64-gnu': 1.3.11
'@swc/core-linux-x64-musl': 1.3.11
'@swc/core-win32-arm64-msvc': 1.3.11
'@swc/core-win32-ia32-msvc': 1.3.11
'@swc/core-win32-x64-msvc': 1.3.11
dev: true
/@swc/wasm/1.2.122:
resolution: {integrity: sha512-sM1VCWQxmNhFtdxME+8UXNyPNhxNu7zdb6ikWpz0YKAQQFRGT5ThZgJrubEpah335SUToNg8pkdDF7ibVCjxbQ==}
requiresBuild: true
dev: true
optional: true
/@swc/wasm/1.2.130:
resolution: {integrity: sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==}
requiresBuild: true
dev: true
optional: true
/@types/argparse/1.0.38:
resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
dev: true

View file

@ -5,3 +5,4 @@ export { build } from './build'
export { preview } from './preview'
export * from './plugin'
export { compileToBytecode } from './bytecode'
export * from './swc'

112
src/swc.ts Normal file
View file

@ -0,0 +1,112 @@
import { createRequire } from 'node:module'
import type { SourceMap } from 'rollup'
import type { TransformConfig, Output as TransformResult, JscConfig } from '@swc/core'
import type { Plugin, UserConfig, FilterPattern } from 'vite'
import { createFilter } from 'vite'
type SwcTransformResult = Omit<TransformResult, 'map'> & {
map: SourceMap
}
type SwcTransformOptions = {
sourcemap?: boolean | 'inline' | undefined
minify?: boolean
} & TransformConfig
async function transformWithSWC(code: string, id: string, options: SwcTransformOptions): Promise<SwcTransformResult> {
const { sourcemap = false, minify = false } = options
delete options.sourcemap
delete options.minify
const isTs = /\.tsx?$/.test(id)
const require = createRequire(import.meta.url)
const swc: typeof import('@swc/core') = require('@swc/core')
if (!swc) {
throw new Error('swc plugin require @swc/core, you need to install it.')
}
const jsc: JscConfig = {
parser: {
syntax: isTs ? 'typescript' : 'ecmascript',
decorators: true
},
transform: {
legacyDecorator: true,
decoratorMetadata: true,
...options
},
keepClassNames: true,
target: 'es2022',
minify: {
format: {
comments: false
}
}
}
const result = await swc.transform(code, {
jsc,
sourceMaps: sourcemap,
minify,
configFile: false,
swcrc: false
})
const map: SourceMap = sourcemap && result.map ? JSON.parse(result.map) : { mappings: '' }
return {
code: result.code,
map
}
}
export type SwcOptions = {
include?: FilterPattern
exclude?: FilterPattern
transformOptions?: TransformConfig
}
export function swcPlugin(options: SwcOptions = {}): Plugin {
const filter = createFilter(options.include || /\.(m?ts|[jt]sx)$/, options.exclude || /\.js$/)
let sourcemap: boolean | 'inline' = false
let minify: boolean | 'esbuild' | 'terser' = false
return {
name: 'vite:swc',
config(): UserConfig {
return {
esbuild: false
}
},
async configResolved(resolvedConfig): Promise<void> {
sourcemap = resolvedConfig.build?.sourcemap === 'inline' ? 'inline' : !!resolvedConfig.build?.sourcemap
minify = resolvedConfig.build?.minify
},
async transform(code, id): Promise<void | { code: string; map: SourceMap }> {
if (filter(id)) {
const result = await transformWithSWC(code, id, { sourcemap, ...(options.transformOptions || {}) })
return {
code: result.code,
map: result.map
}
}
},
async renderChunk(code, chunk): Promise<null | { code: string; map: SourceMap }> {
if (!minify || minify === 'terser') {
return null
}
const result = await transformWithSWC(code, chunk.fileName, {
sourcemap,
minify: true,
...(options.transformOptions || {})
})
return {
code: result.code,
map: result.map
}
}
}
}