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"