Skip to content

Commit df17bfb

Browse files
committed
Perf: Improve performance and production with SWC minimization
1 parent fd0701c commit df17bfb

File tree

10 files changed

+97
-509
lines changed

10 files changed

+97
-509
lines changed

packages/techor/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
"dependencies": {
7070
"@rollup/plugin-commonjs": "^25.0.7",
7171
"@rollup/plugin-node-resolve": "^15.2.3",
72-
"@rollup/plugin-swc": "^0.3.0",
7372
"@swc/core": "^1.3.106",
7473
"@techor/extend": "workspace:^",
7574
"@techor/fs": "workspace:^",
@@ -78,11 +77,11 @@
7877
"@techor/npm": "workspace:^",
7978
"clsx": "^2.0.0",
8079
"commander": "^11.0.0",
81-
"esbuild": "^0.20.1",
8280
"escodegen": "^2.1.0",
8381
"execa": "^7.2.0",
8482
"explore-config": "workspace:^",
8583
"hrtime": "^0.5.0",
84+
"load-tsconfig": "^0.2.5",
8685
"lodash.isequal": "^4.5.0",
8786
"magic-string": "^0.30.7",
8887
"pkg-types": "^1.0.1",

packages/techor/src/commands/build.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@ import { BuildCommonOptions, Config, default as defaultConfig } from '../config'
1919

2020
// core plugins
2121
import esmShim from '../plugins/esm-shim'
22-
import esbuildTransform from '../plugins/esbuild-transform'
2322
import { nodeResolve } from '@rollup/plugin-node-resolve'
2423
import commonjs from '@rollup/plugin-commonjs'
25-
import swc from '@rollup/plugin-swc'
24+
import swc from '../plugins/swc'
2625
import exploreConfig from 'explore-config'
2726

2827
const FORMAT_OF_EXT = {
@@ -119,9 +118,6 @@ export default (program: Command) => program.command('build [entryPaths...]')
119118
}
120119
}
121120
}
122-
if (config.build.esbuildTransform || outputOptions.minify) {
123-
(outputOptions.output.plugins as RollupOutputOptions['plugins'][]).push(esbuildTransform())
124-
}
125121
let buildOptions = buildMap.get(input)
126122
if (buildOptions) {
127123
// 合併同一個 input 來源並對應多個 RollupOutputOptions 避免重新 parse 相同的 input
@@ -135,7 +131,7 @@ export default (program: Command) => program.command('build [entryPaths...]')
135131
buildOptions.input.external = (config.build.external && !isGlobalFile) && getWideExternal(config.build.external || []);
136132
(buildOptions.input.plugins as RollupInputPluginOption[]).unshift(
137133
...[
138-
config.build.swc && swc(config.build.swc),
134+
(config.build.swc || outputOptions.minify) && swc({ ...config.build.swc, minify: outputOptions.minify }),
139135
config.build.commonjs && commonjs(config.build.commonjs),
140136
config.build.nodeResolve && nodeResolve(config.build.nodeResolve),
141137
config.build.esmShim && esmShim(),

packages/techor/src/config.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { InputOptions as RollupInputOptions, OutputOptions as RollupOutputOptions } from 'rollup'
2-
import { TransformOptions as ESBuildTransformOptions } from 'esbuild'
3-
import { Options as SWCOptions } from '@swc/core'
2+
import type { Options as SWCOptions } from './plugins/swc'
43
import { RollupNodeResolveOptions } from '@rollup/plugin-node-resolve'
54
import { RollupCommonJSOptions } from '@rollup/plugin-commonjs'
65

@@ -22,7 +21,15 @@ const config: Config = {
2221
exportConditions: ['node', 'import', 'require', 'default']
2322
},
2423
commonjs: { extensions: ['.js', '.ts'] },
25-
swc: {},
24+
swc: {
25+
include: /\.[jt]sx?$/,
26+
exclude: ['node_modules'],
27+
jsc: {
28+
target: 'esnext',
29+
keepClassNames: true,
30+
externalHelpers: false
31+
}
32+
},
2633
esmShim: true
2734
}
2835
}
@@ -53,8 +60,6 @@ export interface BuildOptions extends BuildCommonOptions {
5360
swc?: SWCOptions | false
5461
// https://github.com/rollup/plugins/tree/master/packages/node-resolve#options
5562
nodeResolve?: RollupNodeResolveOptions | false;
56-
// https://esbuild.github.io/api/#transform
57-
esbuildTransform?: ESBuildTransformOptions | false
5863
// We made `techor-esm-shim` because `@rollup/plugin-esm-shim` breaks the source code.
5964
esmShim?: boolean
6065
// https://github.com/rollup/plugins/tree/master/packages/commonjs#options

packages/techor/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export { default as esmShim } from './plugins/esm-shim'
22
export { default as esbuildTransform } from './plugins/esbuild-transform'
3+
export { default as swc } from './plugins/swc'
34
export { default as rawLoader } from './plugins/raw-loader'
45
export { default as config } from './config'
56

packages/techor/src/plugins/swc.ts

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type { Plugin } from 'rollup'
2+
// @ts-expect-error Cannot find module 'load-tsconfig' or its corresponding type declarations.ts(2307)
3+
import { loadTsConfig } from 'load-tsconfig'
4+
import { Options as SWCOptions, transform } from '@swc/core'
5+
import extend from '@techor/extend'
6+
import { FilterPattern, createFilter } from '@rollup/pluginutils'
7+
8+
export type Options = SWCOptions & {
9+
include?: FilterPattern
10+
exclude?: FilterPattern
11+
tsconfigFile?: string | boolean
12+
}
13+
14+
export default function swc({ tsconfigFile, include, exclude, minify, ...options }: Options = {}): Plugin {
15+
const filter = createFilter(include, exclude)
16+
const compilerOptions = tsconfigFile === false ? {} : loadTsConfig('.', tsconfigFile === true ? undefined : tsconfigFile)?.data?.compilerOptions || {}
17+
let swcOptions = {
18+
jsc: {
19+
target: compilerOptions.target,
20+
parser: {},
21+
transform: {}
22+
}
23+
} as SWCOptions
24+
if (compilerOptions.experimentalDecorators) {
25+
swcOptions.jsc.parser.decorators = true
26+
swcOptions.jsc.transform.legacyDecorator = true
27+
swcOptions.jsc.transform.decoratorMetadata = compilerOptions.emitDecoratorMetadata
28+
}
29+
if (compilerOptions.jsx) {
30+
swcOptions.jsc.transform.react = {
31+
pragma: compilerOptions.jsxFactory,
32+
pragmaFrag: compilerOptions.jsxFragmentFactory,
33+
importSource: compilerOptions.jsxImportSource,
34+
}
35+
}
36+
swcOptions = extend(swcOptions, options)
37+
return {
38+
name: 'techor-swc',
39+
async transform(code, id) {
40+
if (!filter(id)) return null
41+
const transformed = await transform(code, {
42+
filename: id,
43+
sourceMaps: true,
44+
...extend(swcOptions, {
45+
jsc: {
46+
parser: /\.ts?$/.test(id)
47+
? { syntax: 'typescript', tsx: /\.tsx$/.test(id) }
48+
: { syntax: 'ecmascript', jsx: /\.jsx$/.test(id) }
49+
}
50+
} as SWCOptions)
51+
})
52+
return {
53+
code: transformed.code,
54+
map: transformed.map && JSON.parse(transformed.map),
55+
}
56+
},
57+
async renderChunk(code, chunk) {
58+
return minify
59+
? await transform(code, {
60+
...swcOptions,
61+
sourceMaps: true,
62+
minify: true,
63+
filename: chunk.fileName,
64+
})
65+
: null
66+
}
67+
} as Plugin
68+
}

packages/techor/tests/global/test.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ it('should bundle all deps', () => {
1515
})
1616

1717
it('should be minifined', () => {
18-
expect(readFileSync(join(__dirname, './dist/global.min.js'), 'utf-8')).toBe('(()=>{(function(){"use strict";function c(t){const e=typeof t;return t!==null&&(e==="object"||e==="function")}console.log(c({foo:"bar"})),globalThis.effect1="created";function o(){globalThis.effect2="created"}o()})();})();\n')
18+
expect(readFileSync(join(__dirname, './dist/global.min.js'), 'utf-8')).toBe('(function(){"use strict";function isObject(value){const type=typeof value;return value!==null&&(type==="object"||type==="function")}console.log(isObject({foo:"bar"}));globalThis.effect1="created";function effect2(){globalThis.effect2="created"}effect2()})();\n')
1919
})
2020

21+
it('should not contain @swc/helpers', () => {
22+
expect(readFileSync(join(__dirname, './dist/global.min.js'), 'utf-8')).not.toContain('@swc/helpers')
23+
})

packages/techor/tests/minify/test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ import { join } from 'node:path'
44

55
test('main', () => {
66
execSync('tsx ../../src/bin build --minify', { cwd: __dirname })
7-
expect(readFileSync(join(__dirname, './dist/index.mjs'), 'utf-8').toString()).toEqual('function n(){console.log("main")}n();export{n as default};\n')
7+
expect(readFileSync(join(__dirname, './dist/index.mjs'), 'utf-8').toString()).toEqual('function main(){console.log("main")}main();export{main as default};\n')
88
})

0 commit comments

Comments
 (0)