diff --git a/packages/fes-builder-webpack/package.json b/packages/fes-builder-webpack/package.json
index 8dc135b4..5f9aec0d 100644
--- a/packages/fes-builder-webpack/package.json
+++ b/packages/fes-builder-webpack/package.json
@@ -35,6 +35,7 @@
"@babel/preset-env": "^7.16.4",
"@babel/preset-typescript": "^7.15.0",
"@fesjs/utils": "3.0.0-rc.2",
+ "@swc/core": "^1.3.24",
"@vue/babel-plugin-jsx": "^1.1.1",
"autoprefixer": "^10.2.4",
"babel-loader": "^8.2.2",
@@ -55,6 +56,8 @@
"postcss-loader": "^4.2.0",
"postcss-safe-parser": "^6.0.0",
"style-loader": "^2.0.0",
+ "swc-loader": "^0.2.3",
+ "swc-plugin-vue-jsx": "^0.2.0",
"vue-loader": "^16.1.2",
"webpack": "^5.69.0",
"webpack-bundle-analyzer": "^4.4.0",
diff --git a/packages/fes-builder-webpack/src/index.js b/packages/fes-builder-webpack/src/index.js
index e9e1b703..d4455dcc 100644
--- a/packages/fes-builder-webpack/src/index.js
+++ b/packages/fes-builder-webpack/src/index.js
@@ -24,6 +24,7 @@ export default function () {
require.resolve('./plugins/features/postcssLoader'),
require.resolve('./plugins/features/nodeModulesTransform'),
require.resolve('./plugins/features/vueLoader'),
+ require.resolve('./plugins/features/swcLoader'),
// commands
require.resolve('./plugins/commands/build'),
diff --git a/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/index.js b/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/index.js
index 6f552241..09fae9b7 100644
--- a/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/index.js
+++ b/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/index.js
@@ -8,6 +8,7 @@ import createVueWebpackConfig from './vue';
import createDefineWebpackConfig from './define';
import createMinimizerWebpackConfig from './minimizer';
import createHtmlWebpackConfig from './html';
+import { buildSwcOptions } from './swcOptions';
const DEFAULT_EXCLUDE_NODE_MODULES = [
'vue',
@@ -52,6 +53,7 @@ function handleAlias({ api, webpackConfig }) {
export default async function getConfig({ api, cwd, config, env, entry = {}, modifyBabelOpts, modifyBabelPresetOpts, chainWebpack, headScripts, publicPath }) {
const isDev = env === 'development';
const isProd = env === 'production';
+ const useSwc = !!config.swcLoader;
const webpackConfig = new Config();
const absoluteOutput = join(cwd, config.outputPath || 'dist');
@@ -112,13 +114,6 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod
.type('asset/source');
const { targets, browserslist } = api.utils.getTargetsAndBrowsersList({ config });
- const babelOpts = await getBabelOpts({
- cwd,
- config,
- modifyBabelOpts,
- modifyBabelPresetOpts,
- targets,
- });
// --------------- js -----------
// https://webpack.docschina.org/configuration/module/#resolve-fully-specified
@@ -127,43 +122,133 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod
.test(/\.m?jsx?$/)
.resolve.set('fullySpecified', false);
- webpackConfig.module
- .rule('js')
- .test(/\.(js|mjs|jsx|ts|tsx)$/)
- .exclude.add((filepath) => {
- // always transpile js in vue files
- if (/(\.vue|\.jsx)$/.test(filepath)) {
- return false;
- }
- // Don't transpile node_modules
- return /node_modules/.test(filepath);
- })
- .end()
- .use('babel-loader')
- .loader(require.resolve('babel-loader'))
- .options(babelOpts);
-
- // 为了避免第三方依赖包编译不充分导致线上问题,默认对 node_modules 也进行全编译,只在生产构建的时候进行
- if (isProd) {
- const transpileDepRegex = genTranspileDepRegex(config.nodeModulesTransform.exclude);
+ if (useSwc) {
webpackConfig.module
- .rule('js-in-node_modules')
+ .rule('js')
.test(/\.(js|mjs)$/)
- .include.add(/node_modules/)
- .end()
.exclude.add((filepath) => {
- if (transpileDepRegex && transpileDepRegex.test(filepath)) {
- return true;
+ // always transpile js in vue files
+ if (/(\.vue|\.jsx)$/.test(filepath)) {
+ return false;
}
+ // Don't transpile node_modules
+ return /node_modules/.test(filepath);
+ })
+ .end()
+ .use('swc-loader')
+ .loader(require.resolve('swc-loader'))
+ .options(buildSwcOptions(browserslist, config, false, false));
+ webpackConfig.module
+ .rule('jsx')
+ .test(/\.jsx$/)
+ .exclude.add((filepath) => {
+ // always transpile js in vue files
+ if (/(\.vue|\.jsx)$/.test(filepath)) {
+ return false;
+ }
+ // Don't transpile node_modules
+ return /node_modules/.test(filepath);
+ })
+ .end()
+ .use('swc-loader')
+ .loader(require.resolve('swc-loader'))
+ .options(buildSwcOptions(browserslist, config, true, false));
- return false;
+ webpackConfig.module
+ .rule('ts')
+ .test(/\.ts$/)
+ .exclude.add((filepath) => {
+ // always transpile js in vue files
+ if (/(\.vue|\.tsx)$/.test(filepath)) {
+ return false;
+ }
+ // Don't transpile node_modules
+ return /node_modules/.test(filepath);
+ })
+ .end()
+ .use('swc-loader')
+ .loader(require.resolve('swc-loader'))
+ .options(buildSwcOptions(browserslist, config, false, true));
+ webpackConfig.module
+ .rule('tsx')
+ .test(/\.tsx$/)
+ .exclude.add((filepath) => {
+ // always transpile js in vue files
+ if (/(\.vue|\.tsx)$/.test(filepath)) {
+ return false;
+ }
+ // Don't transpile node_modules
+ return /node_modules/.test(filepath);
+ })
+ .end()
+ .use('swc-loader')
+ .loader(require.resolve('swc-loader'))
+ .options(buildSwcOptions(browserslist, config, true, true));
+ // 为了避免第三方依赖包编译不充分导致线上问题,默认对 node_modules 也进行全编译,只在生产构建的时候进行
+ if (isProd) {
+ const transpileDepRegex = genTranspileDepRegex(config.nodeModulesTransform.exclude);
+ webpackConfig.module
+ .rule('js-in-node_modules')
+ .test(/\.(js|mjs)$/)
+ .include.add(/node_modules/)
+ .end()
+ .exclude.add((filepath) => {
+ if (transpileDepRegex && transpileDepRegex.test(filepath)) {
+ return true;
+ }
+
+ return false;
+ })
+ .end()
+ .use('swc-loader')
+ .loader(require.resolve('swc-loader'))
+ .options(buildSwcOptions(browserslist, config, false, false));
+ }
+ } else {
+ const babelOpts = await getBabelOpts({
+ cwd,
+ config,
+ modifyBabelOpts,
+ modifyBabelPresetOpts,
+ targets,
+ });
+ webpackConfig.module
+ .rule('js')
+ .test(/\.(js|mjs|jsx|ts|tsx)$/)
+ .exclude.add((filepath) => {
+ // always transpile js in vue files
+ if (/(\.vue|\.jsx)$/.test(filepath)) {
+ return false;
+ }
+ // Don't transpile node_modules
+ return /node_modules/.test(filepath);
})
.end()
.use('babel-loader')
.loader(require.resolve('babel-loader'))
.options(babelOpts);
- }
+ // 为了避免第三方依赖包编译不充分导致线上问题,默认对 node_modules 也进行全编译,只在生产构建的时候进行
+ if (isProd) {
+ const transpileDepRegex = genTranspileDepRegex(config.nodeModulesTransform.exclude);
+ webpackConfig.module
+ .rule('js-in-node_modules')
+ .test(/\.(js|mjs)$/)
+ .include.add(/node_modules/)
+ .end()
+ .exclude.add((filepath) => {
+ if (transpileDepRegex && transpileDepRegex.test(filepath)) {
+ return true;
+ }
+
+ return false;
+ })
+ .end()
+ .use('babel-loader')
+ .loader(require.resolve('babel-loader'))
+ .options(babelOpts);
+ }
+ }
// --------------- css -----------
const createCSSRule = createCssWebpackConfig({
isDev,
@@ -257,12 +342,13 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod
}
// --------------- 压缩 -----------
- createMinimizerWebpackConfig({
- isProd,
- config,
- webpackConfig,
- });
-
+ if (!useSwc) {
+ createMinimizerWebpackConfig({
+ isProd,
+ config,
+ webpackConfig,
+ });
+ }
// --------------- chainwebpack -----------
if (chainWebpack) {
await chainWebpack(webpackConfig, {
diff --git a/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/swcOptions.js b/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/swcOptions.js
new file mode 100644
index 00000000..fcdd8245
--- /dev/null
+++ b/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/swcOptions.js
@@ -0,0 +1,20 @@
+export function buildSwcOptions(browserslist, config, isJsx, isTs) {
+ return {
+ env: {
+ targets: browserslist,
+ mode: 'entry',
+ coreJs: '3',
+ },
+ jsc: {
+ parser: {
+ syntax: isTs ? 'typescript' : 'ecmascript',
+ jsx: isJsx,
+ },
+ experimental: {
+ plugins: [['swc-plugin-vue-jsx', {}]],
+ },
+ },
+ minify: true,
+ ...config.swcLoader,
+ };
+}
diff --git a/packages/fes-builder-webpack/src/plugins/features/swcLoader.js b/packages/fes-builder-webpack/src/plugins/features/swcLoader.js
new file mode 100644
index 00000000..99f57eca
--- /dev/null
+++ b/packages/fes-builder-webpack/src/plugins/features/swcLoader.js
@@ -0,0 +1,10 @@
+export default (api) => {
+ api.describe({
+ key: 'swcLoader',
+ config: {
+ schema(joi) {
+ return joi.object().description('more swc options see https://github.com/swc-project/swc-loader');
+ },
+ },
+ });
+};
diff --git a/packages/fes-template/.fes.js b/packages/fes-template/.fes.js
index d457db32..91e89578 100644
--- a/packages/fes-template/.fes.js
+++ b/packages/fes-template/.fes.js
@@ -1,31 +1,33 @@
-import { defineBuildConfig } from '@fesjs/fes'
+import { defineBuildConfig } from '@fesjs/fes';
+
export default defineBuildConfig({
+ swcLoader: {},
define: {
- __DEV__: false
+ __DEV__: false,
},
html: {
- title: '海贼王'
+ title: '海贼王',
},
router: {
- mode: 'hash'
+ mode: 'hash',
},
watermark: {
- disabled: false
+ disabled: false,
},
access: {
roles: {
admin: ['*'],
- menuTest: ['/', '/menuTest']
- }
+ menuTest: ['/', '/menuTest'],
+ },
},
mock: {
- prefix: '/v2'
+ prefix: '/v2',
},
proxy: {
'/v2': {
target: 'https://api.douban.com/',
- changeOrigin: true
- }
+ changeOrigin: true,
+ },
},
layout: {
title: 'Fes.js',
@@ -37,60 +39,58 @@ export default defineBuildConfig({
{
name: 'index',
icon: '/wine-outline.svg',
- match: ['/route/*']
+ match: ['/route/*'],
},
{
- name: 'store'
+ name: 'store',
},
{
name: 'editor',
- icon: '/wine-outline.svg'
+ icon: '/wine-outline.svg',
},
{
title: '$externalLink',
icon: 'UserOutlined',
- path: 'https://www.baidu.com'
+ path: 'https://www.baidu.com',
},
{
- name: 'mock'
+ name: 'mock',
},
{
title: '菜单权限测试',
children: [
{
title: '子菜单',
- path: '/menuTest'
+ path: '/menuTest',
},
- ]
+ ],
},
{
- name: 'cssModule'
+ name: 'cssModule',
},
{
- name: 'pinia'
+ name: 'pinia',
},
],
menuProps: {
- defaultExpandAll: false
- }
+ defaultExpandAll: false,
+ },
},
devServer: {
- port: 8080
+ port: 8080,
},
enums: {
status: [
['0', '无效的'],
- ['1', '有效的']
- ]
+ ['1', '有效的'],
+ ],
},
vuex: {
- strict: true
+ strict: true,
},
dynamicImport: true,
monacoEditor: {
- languages: ['javascript', 'typescript', 'html', 'json']
+ languages: ['javascript', 'typescript', 'html', 'json'],
},
- presets: [
- require.resolve('../fes-builder-webpack/lib'),
- ]
+ presets: [require.resolve('../fes-builder-webpack/lib')],
});
diff --git a/packages/fes-template/.gitignore b/packages/fes-template/.gitignore
index df903b98..59cb1b8e 100644
--- a/packages/fes-template/.gitignore
+++ b/packages/fes-template/.gitignore
@@ -9,3 +9,6 @@
/src/.fes-production
/src/.fes-test
/.env.local
+
+# swc wasm cache
+/.swc
\ No newline at end of file
diff --git a/packages/fes-template/src/app.js b/packages/fes-template/src/app.jsx
similarity index 100%
rename from packages/fes-template/src/app.js
rename to packages/fes-template/src/app.jsx
diff --git a/packages/fes-template/src/pages/@id/add.vue b/packages/fes-template/src/pages/@id/add.vue
index e69de29b..662b7a4a 100644
--- a/packages/fes-template/src/pages/@id/add.vue
+++ b/packages/fes-template/src/pages/@id/add.vue
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/yarn.lock b/yarn.lock
index 8feaf7ea..187d84f4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2217,6 +2217,72 @@
magic-string "^0.25.0"
string.prototype.matchall "^4.0.6"
+"@swc/core-darwin-arm64@1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.24.tgz#d41fc574cb5049def9001903680fdd924f065052"
+ integrity sha512-rR+9UpWm+fGXcipsjCst2hIL1GYIbo0YTLhJZWdIpQD6KRHHJMFXiydMgQQkDj2Ml7HpqUVgxj6m4ZWYL8b0OA==
+
+"@swc/core-darwin-x64@1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.24.tgz#0f7a3960b91cbd7f95f25542b29d0e08bde4f59d"
+ integrity sha512-px+5vkGtgPH0m3FkkTBHynlRdS5rRz+lK+wiXIuBZFJSySWFl6RkKbvwkD+sf0MpazQlqwlv/rTOGJBw6oDffg==
+
+"@swc/core-linux-arm-gnueabihf@1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.24.tgz#a0fdd97b8341806b57290217830a5d1ab7d0b193"
+ integrity sha512-jLs8ZOdTV4UW4J12E143QJ4mOMONQtqgAnuhBbRuWFzQ3ny1dfoC3P1jNWAJ2Xi59XdxAIXn0PggPNH4Kh34kw==
+
+"@swc/core-linux-arm64-gnu@1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.24.tgz#0536d03e12dd471ebafc180599488404aebb65cf"
+ integrity sha512-A/v0h70BekrwGpp1DlzIFGcHQ3QQ2PexXcnnuIBZeMc9gNmHlcZmg3EcwAnaUDiokhNuSUFA/wV94yk1OqmSkw==
+
+"@swc/core-linux-arm64-musl@1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.24.tgz#54f46ffea1bf6ffcbe7c62037efaefdfb5115214"
+ integrity sha512-pbc9eArWPTiMrbpS/pJo0IiQNAKAQBcBIDjWBGP1tcw2iDXYLw4bruwz9kI/VjakbshWb8MoE4T5ClkeuULvSw==
+
+"@swc/core-linux-x64-gnu@1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.24.tgz#c2b5cef83f8afd2a57d0eafbac083562d50cd0e6"
+ integrity sha512-pP5pOLlY1xd352qo7rTlpVPUI9/9VhOd4b3Lk+LzfZDq9bTL2NDlGfyrPiwa5DGHMSzrugH56K2J68eutkxYVA==
+
+"@swc/core-linux-x64-musl@1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.24.tgz#3459d01f9bf745568a4196c1993987f3d4a98303"
+ integrity sha512-phNbP7zGp+Wcyxq1Qxlpe5KkxO7WLT2kVQUC7aDFGlVdCr+xdXsfH1MzheHtnr0kqTVQX1aiM8XXXHfFxR0oNA==
+
+"@swc/core-win32-arm64-msvc@1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.24.tgz#85a18c844c00d66bf46db99d9c98e9550b4d28f5"
+ integrity sha512-qhbiJTWAOqyR+K9xnGmCkOWSz2EmWpDBstEJCEOTc6FZiEdbiTscDmqTcMbCKaTHGu8t+6erVA4t65/Eg6uWPA==
+
+"@swc/core-win32-ia32-msvc@1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.24.tgz#18318199ba06cab4ead8f6122b9f30b3f452b1e7"
+ integrity sha512-JfghIlscE4Rz+Lc08lSoDh+R0cWxrISed5biogFfE6vZqhaDnw3E5Qshqw7O3pIaiq8L2u1nmzuyP581ZmpbRA==
+
+"@swc/core-win32-x64-msvc@1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.24.tgz#b53746787e5af021787134d393fd67b0431f90d9"
+ integrity sha512-3AmJRr0hwciwDBbzUNqaftvppzS8v9X/iv/Wl7YaVLBVpPfQvaZzfqLycvNMGLZb5vIKXR/u58txg3dRBGsJtw==
+
+"@swc/core@^1.3.24":
+ version "1.3.24"
+ resolved "https://registry.npmmirror.com/@swc/core/-/core-1.3.24.tgz#ef6b30267c1bbd48af62cbc91370fe9b3f5d6a23"
+ integrity sha512-QMOTd0AgiUT3K1crxLRqd3gw0f3FC8hhH1vvlIlryvYqU4c+FJ/T2G4ZhMKLxQlZ/jX6Rhk0gKINZRBxy2GFyQ==
+ optionalDependencies:
+ "@swc/core-darwin-arm64" "1.3.24"
+ "@swc/core-darwin-x64" "1.3.24"
+ "@swc/core-linux-arm-gnueabihf" "1.3.24"
+ "@swc/core-linux-arm64-gnu" "1.3.24"
+ "@swc/core-linux-arm64-musl" "1.3.24"
+ "@swc/core-linux-x64-gnu" "1.3.24"
+ "@swc/core-linux-x64-musl" "1.3.24"
+ "@swc/core-win32-arm64-msvc" "1.3.24"
+ "@swc/core-win32-ia32-msvc" "1.3.24"
+ "@swc/core-win32-x64-msvc" "1.3.24"
+
"@tootallnate/once@1":
version "1.1.2"
resolved "https://registry.npmmirror.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
@@ -10527,6 +10593,16 @@ svgo@^2.3.1, svgo@^2.7.0:
picocolors "^1.0.0"
stable "^0.1.8"
+swc-loader@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.npmmirror.com/swc-loader/-/swc-loader-0.2.3.tgz#6792f1c2e4c9ae9bf9b933b3e010210e270c186d"
+ integrity sha512-D1p6XXURfSPleZZA/Lipb3A8pZ17fP4NObZvFCDjK/OKljroqDpPmsBdTraWhVBqUNpcWBQY1imWdoPScRlQ7A==
+
+swc-plugin-vue-jsx@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.npmmirror.com/swc-plugin-vue-jsx/-/swc-plugin-vue-jsx-0.2.0.tgz#22cd06409aac9a81070f84812a0e0e9bf4acd01b"
+ integrity sha512-2atLA8DXUtCHZqpc/0wCDIhx2LXJ9ZV7IMDlEG+eWVb0p5u/5jff8iEf+Mv7RumSM2cPrb3FTszbDDGSCNXoqQ==
+
symbol-tree@^3.2.4:
version "3.2.4"
resolved "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"