From e05fb32a76c7ef309f17ada1ed4bc340886d768b Mon Sep 17 00:00:00 2001 From: wanchun <445436867@qq.com> Date: Wed, 1 Mar 2023 16:29:42 +0800 Subject: [PATCH] feat: plugin-swc --- README.en-US.md | 1 + README.md | 2 +- build.config.js | 1 + docs/.vuepress/configs/sidebar/zh.ts | 1 + docs/guide/builder.md | 1 - docs/reference/config/README.md | 7 - docs/reference/plugin/README.md | 3 +- docs/reference/plugin/plugins/swc.md | 30 ++++ packages/fes-builder-webpack/package.json | 2 - packages/fes-builder-webpack/src/index.js | 1 - .../src/plugins/commands/webpackConfig/css.js | 3 +- .../plugins/commands/webpackConfig/index.js | 128 +++++------------- .../commands/webpackConfig/minimizer.js | 21 +-- .../src/plugins/features/swcLoader.js | 10 -- packages/fes-plugin-swc/LICENSE | 21 +++ packages/fes-plugin-swc/README.md | 126 +++++++++++++++++ packages/fes-plugin-swc/package.json | 41 ++++++ packages/fes-plugin-swc/src/index.js | 126 +++++++++++++++++ .../src}/swcOptions.js | 15 +- packages/fes-plugin-swc/types.d.ts | 7 + .../src/plugins/features/targets.js | 1 - packages/fes-template/.fes.js | 8 +- packages/fes-template/package.json | 3 +- 23 files changed, 418 insertions(+), 141 deletions(-) create mode 100644 docs/reference/plugin/plugins/swc.md delete mode 100644 packages/fes-builder-webpack/src/plugins/features/swcLoader.js create mode 100644 packages/fes-plugin-swc/LICENSE create mode 100644 packages/fes-plugin-swc/README.md create mode 100644 packages/fes-plugin-swc/package.json create mode 100644 packages/fes-plugin-swc/src/index.js rename packages/{fes-builder-webpack/src/plugins/commands/webpackConfig => fes-plugin-swc/src}/swcOptions.js (63%) create mode 100644 packages/fes-plugin-swc/types.d.ts diff --git a/README.en-US.md b/README.en-US.md index 36033479..f963c950 100644 --- a/README.en-US.md +++ b/README.en-US.md @@ -66,6 +66,7 @@ It mainly has the following functions: | [@fesjs/plugin-monaco-editor](http://fesjs.mumblefe.cn/reference/plugin/plugins/editor.html#%E4%BB%8B%E7%BB%8D) | Provide code editor capability, based on `monaco-editor` (code editor used by VS Code) | | [@fesjs/plugin-pinia](http://fesjs.mumblefe.cn/reference/plugin/plugins/pinia.html) | state manager | | [@fesjs/plugin-watermark](http://fesjs.mumblefe.cn/reference/plugin/plugins/watermark.html) | watermark | +| [@fesjs/plugin-swc](http://fesjs.mumblefe.cn/reference/plugin/plugins/swc.html) | use swc-loader | ## As easy as counting 1, 2, 3 use `yarn`: diff --git a/README.md b/README.md index dff37b69..b6b56174 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Fes.js 是一个好用的前端应用解决方案。提供覆盖编译构建到 | [@fesjs/plugin-windicss](http://fesjs.mumblefe.cn/reference/plugin/plugins/windicss.html) | 基于 `windicss`,提供原子化 CSS 能力 | | [@fesjs/plugin-pinia](http://fesjs.mumblefe.cn/reference/plugin/plugins/pinia.html) | pinia,状态处理 | | [@fesjs/plugin-watermark](http://fesjs.mumblefe.cn/reference/plugin/plugins/watermark.html) | 水印 | - +| [@fesjs/plugin-swc](http://fesjs.mumblefe.cn/reference/plugin/plugins/swc.html) | 使用swc-loader构建 | ## 像数 1, 2, 3 一样容易 使用 `yarn`: diff --git a/build.config.js b/build.config.js index 58b16ef4..d3952f64 100644 --- a/build.config.js +++ b/build.config.js @@ -23,6 +23,7 @@ module.exports = { 'fes-plugin-pinia', 'fes-plugin-windicss', 'fes-plugin-watermark', + 'fes-plugin-swc', ], copy: [], }; diff --git a/docs/.vuepress/configs/sidebar/zh.ts b/docs/.vuepress/configs/sidebar/zh.ts index 71d364a7..bafbc83b 100644 --- a/docs/.vuepress/configs/sidebar/zh.ts +++ b/docs/.vuepress/configs/sidebar/zh.ts @@ -65,6 +65,7 @@ export const zh: SidebarConfig = { '/reference/plugin/plugins/editor.md', '/reference/plugin/plugins/pinia.md', '/reference/plugin/plugins/watermark.md', + '/reference/plugin/plugins/swc.md', ], }, { diff --git a/docs/guide/builder.md b/docs/guide/builder.md index a0773538..67d5fb86 100644 --- a/docs/guide/builder.md +++ b/docs/guide/builder.md @@ -4,7 +4,6 @@ - 选用 Vite 构建,安装 `npm i @fesjs/builder-vite` 依赖即可。 - 选用 Webpack 构建,安装 `npm i @fesjs/builder-webpack` 依赖即可。 -- Webpack构建支持用babel+terser和swc两种编译方式,如选用swc,安装`npm i @swc/core` 同时配置额外传`swcLoader:{}`,具体可以查看[配置](../reference/config)。 ## 使用差异 diff --git a/docs/reference/config/README.md b/docs/reference/config/README.md index 3f169a65..ff71b4e7 100644 --- a/docs/reference/config/README.md +++ b/docs/reference/config/README.md @@ -459,13 +459,6 @@ export default { 配置额外的 `babel` 插件集。 -### swcLoader -- 类型: `object` -- 默认值: `undefined` -- 详情: - -传对象时使用swc进行编译和压缩,[swc配置](https://swc.rs/docs/configuration/swcrc) -默认usage模式 ### extraPostCSSPlugins - 类型: `array` diff --git a/docs/reference/plugin/README.md b/docs/reference/plugin/README.md index a9cccbfb..25be0d87 100644 --- a/docs/reference/plugin/README.md +++ b/docs/reference/plugin/README.md @@ -7,7 +7,7 @@ | [@fesjs/plugin-enums](./plugins/enums.md) | 提供统一的枚举存取及丰富的函数来处理枚举 | | [@fesjs/plugin-icon](./plugins/icon.md) | svg 文件自动注册为组件 | | [@fesjs/plugin-jest](./plugins/jest.md) | 基于 `Jest`,提供单元测试、覆盖测试能力 | -| [ @fesjs/plugin-layout](./plugins/layout.md) | 简单的配置即可拥有布局,包括导航以及侧边栏 | +| [@fesjs/plugin-layout](./plugins/layout.md) | 简单的配置即可拥有布局,包括导航以及侧边栏 | | [@fesjs/plugin-locale](./plugins/locale.md) | 基于 `Vue I18n`,提供国际化能力 | | [@fesjs/plugin-model](./plugins/model.md) | 简易的数据管理方案 | | [@fesjs/plugin-request](./plugins/request.md) | 基于 `Axios` 封装的 request,内置防止重复请求、请求节流、错误处理等功能 | @@ -18,6 +18,7 @@ | [@fesjs/plugin-windicss](./plugins/windicss.md) | 基于 `windicss`,提供原子化 CSS 能力 | | [@fesjs/plugin-pinia](./plugins/pinia.md) | 基于 `pinia`,提供状态管理 | | [@fesjs/plugin-watermark](./plugins/watermark.md) | 水印 | +| [@fesjs/plugin-swc](./plugins/swc.md) | swc | ## 架构 diff --git a/docs/reference/plugin/plugins/swc.md b/docs/reference/plugin/plugins/swc.md new file mode 100644 index 00000000..108191ec --- /dev/null +++ b/docs/reference/plugin/plugins/swc.md @@ -0,0 +1,30 @@ +# @fesjs/plugin-swc + +## 介绍 +webpack 启用 swc,构建速度更快! + + +## 启用方式 +在 `package.json` 中引入依赖: +```json +{ + "dependencies": { + "@fesjs/fes": "^3.0.0", + "@fesjs/plugin-swc": "^3.0.0" + }, +} +``` + +## 编译时配置 +传对象时使用swc进行编译和压缩,[loader配置](https://swc.rs/docs/configuration/swcrc),默认usage模式。 +```js +export default { + swc: { + loader: { + env: { + coreJs: '3.27', + }, + } + }, +} +``` diff --git a/packages/fes-builder-webpack/package.json b/packages/fes-builder-webpack/package.json index 146bf96d..bdb40c4c 100644 --- a/packages/fes-builder-webpack/package.json +++ b/packages/fes-builder-webpack/package.json @@ -35,7 +35,6 @@ "@babel/preset-env": "^7.16.4", "@babel/preset-typescript": "^7.15.0", "@fesjs/utils": "3.0.0-rc.2", - "@swc/css": "^0.0.18", "@vue/babel-plugin-jsx": "^1.1.1", "autoprefixer": "^10.2.4", "babel-loader": "^8.2.2", @@ -56,7 +55,6 @@ "postcss-loader": "^4.2.0", "postcss-safe-parser": "^6.0.0", "style-loader": "^2.0.0", - "swc-loader": "^0.2.3", "terser-webpack-plugin": "^5.3.6", "vue-loader": "^16.1.2", "webpack": "^5.69.0", diff --git a/packages/fes-builder-webpack/src/index.js b/packages/fes-builder-webpack/src/index.js index d4455dcc..e9e1b703 100644 --- a/packages/fes-builder-webpack/src/index.js +++ b/packages/fes-builder-webpack/src/index.js @@ -24,7 +24,6 @@ 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/css.js b/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/css.js index ca8ab814..5fad5ec9 100644 --- a/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/css.js +++ b/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/css.js @@ -10,7 +10,6 @@ // 根据 entry 将文件输出到不同的文件夹 import { deepmerge } from '@fesjs/utils'; -import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; function createRules({ isDev, webpackConfig, config, lang, test, loader, options, browserslist, styleLoaderOption }) { function applyLoaders(rule, cssLoaderOption = {}) { @@ -98,7 +97,7 @@ export default function createCssWebpackConfig({ isDev, config, webpackConfig, b chunkFilename: '[id].[contenthash:8].css', }, ]); - webpackConfig.optimization.minimizer('css').use(require.resolve('css-minimizer-webpack-plugin'), [{ minify: CssMinimizerPlugin.swcMinify }]); + webpackConfig.optimization.minimizer('css').use(require.resolve('css-minimizer-webpack-plugin'), [{}]); } return (options) => { 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 429a8009..6aa22f4d 100644 --- a/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/index.js +++ b/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/index.js @@ -8,7 +8,6 @@ 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', @@ -53,7 +52,6 @@ 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'); @@ -114,6 +112,13 @@ 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 @@ -122,106 +127,43 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod .test(/\.m?jsx?$/) .resolve.set('fullySpecified', false); - if (useSwc) { - webpackConfig.module - .rule('js') - .test(/\.(js|mjs)$/) + 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 - .exclude.add(/node_modules/) - .end() - .use('swc-loader') - .loader(require.resolve('swc-loader')) - .options(buildSwcOptions(targets, config, false, false)); - webpackConfig.module - .rule('jsx') - .test(/\.jsx$/) - .exclude.add(/node_modules/) - .end() - .use('swc-loader') - .loader(require.resolve('swc-loader')) - .options(buildSwcOptions(targets, config, true, false)); + 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('ts') - .test(/\.ts$/) - .exclude.add(/node_modules/) + .rule('js-in-node_modules') + .test(/\.(js|mjs)$/) + .include.add(/node_modules/) .end() - .use('swc-loader') - .loader(require.resolve('swc-loader')) - .options(buildSwcOptions(targets, config, false, true)); - webpackConfig.module - .rule('tsx') - .test(/\.tsx$/) - .exclude.add(/node_modules/) - .end() - .use('swc-loader') - .loader(require.resolve('swc-loader')) - .options(buildSwcOptions(targets, config, true, true)); - // 为了避免第三方依赖包编译不充分导致线上问题,默认对 node_modules 也进行全编译,只在生产构建的时候进行 - if (isProd) { - // const cjsReg = [/css-loader/, /vue-loader/].concat(config.swcLoader?.cjsPkg || []); - const transpileDepRegex = genTranspileDepRegex(config.nodeModulesTransform.exclude); - webpackConfig.module - .rule('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(targets, 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 (/(\.tsx|\.ts|\.jsx)$/.test(filepath)) { - return false; + if (transpileDepRegex && transpileDepRegex.test(filepath)) { + return true; } - // Don't transpile node_modules - return /node_modules/.test(filepath); + + return false; }) .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, @@ -319,13 +261,15 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod isProd, config, webpackConfig, - swcOptions: useSwc ? buildSwcOptions(targets, config, false, false, true) : undefined, }); // --------------- chainwebpack ----------- if (chainWebpack) { await chainWebpack(webpackConfig, { createCSSRule, + env, + targets, + browserslist, webpack, }); } @@ -334,6 +278,8 @@ export default async function getConfig({ api, cwd, config, env, entry = {}, mod await config.chainWebpack(webpackConfig, { createCSSRule, env, + targets, + browserslist, webpack, }); } diff --git a/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/minimizer.js b/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/minimizer.js index bf9343ea..507b8c52 100644 --- a/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/minimizer.js +++ b/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/minimizer.js @@ -1,5 +1,4 @@ import { deepmerge } from '@fesjs/utils'; -import TerserPlugin from 'terser-webpack-plugin'; const defaultTerserOptions = { compress: { @@ -38,22 +37,14 @@ const defaultTerserOptions = { }, }; -const terserOptions = (config, swcOptions) => { - if (swcOptions) { - return { - terserOptions: swcOptions.jsc?.minify, - minify: TerserPlugin.swcMinify, - }; - } - return { - terserOptions: deepmerge(defaultTerserOptions, config.terserOptions || {}), - extractComments: false, - }; -}; +const terserOptions = (config) => ({ + terserOptions: deepmerge(defaultTerserOptions, config.terserOptions || {}), + extractComments: false, +}); -export default function createMinimizerWebpackConfig({ isProd, config, webpackConfig, swcOptions }) { +export default function createMinimizerWebpackConfig({ isProd, config, webpackConfig }) { if (isProd) { - webpackConfig.optimization.minimizer('terser').use(require.resolve('terser-webpack-plugin'), [terserOptions(config, swcOptions)]); + webpackConfig.optimization.minimizer('terser').use(require.resolve('terser-webpack-plugin'), [terserOptions(config)]); } if (process.env.FES_ENV === 'test') { webpackConfig.optimization.minimize(false); diff --git a/packages/fes-builder-webpack/src/plugins/features/swcLoader.js b/packages/fes-builder-webpack/src/plugins/features/swcLoader.js deleted file mode 100644 index 99f57eca..00000000 --- a/packages/fes-builder-webpack/src/plugins/features/swcLoader.js +++ /dev/null @@ -1,10 +0,0 @@ -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-plugin-swc/LICENSE b/packages/fes-plugin-swc/LICENSE new file mode 100644 index 00000000..0978fbf7 --- /dev/null +++ b/packages/fes-plugin-swc/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-present webank + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/fes-plugin-swc/README.md b/packages/fes-plugin-swc/README.md new file mode 100644 index 00000000..b6b56174 --- /dev/null +++ b/packages/fes-plugin-swc/README.md @@ -0,0 +1,126 @@ +简体中文 | [English](./README.en-US.md) + +

+ + fes.js + +

+ +
+ +一个优秀的前端解决方案 + +[![GitHub issues](https://img.shields.io/github/issues/WeBankFinTech/fes.js.svg?style=flat-square)](../../issues) +[![MIT](https://img.shields.io/dub/l/vibe-d.svg?style=flat-square)](http://opensource.org/licenses/MIT) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](../../pulls) +[![Page Views Count](https://badges.toozhao.com/badges/01G7TRNN1PH9PMSCYWDF3EK4QT/green.svg)](https://badges.toozhao.com/stats/01G7TRNN1PH9PMSCYWDF3EK4QT "Get your own page views count badge on badges.toozhao.com") + +
+ +- 使用文档 - [http://fesjs.mumblefe.cn/](http://fesjs.mumblefe.cn/) +- 更新日志 - [CHANGELOG.md](./CHANGELOG.md) + +# 痛点 +在开发一个前端项目之前,我们可能需要做如下准备工作: +- 搭建开发环境 +- 约定代码规范 +- 封装API请求 +- 配置路由 +- 实现布局、菜单、导航 +- 实现登录 +- 权限管理 +- ... + +除了准备工作之外,还会遇到很多相似的业务类型,比如中后台应用大多都是工作台、增删改查、权限、图表等。如果每次项目都完全手动处理一遍,不仅耗费时间,久而久之可能会存在多种技术栈、开发规范,导致开发流程不统一,历史项目越来越难维护。所以我们需要一套完整的解决方案,管理开发到部署整个流程。 + + +## Fes.js 是什么? +Fes.js 是一个好用的前端应用解决方案。提供覆盖编译构建到代码运行的每个生命周期的插件体系,支持各种功能扩展和业务需求。以 路由为基础,同时支持配置式路由和约定式路由,保证路由的功能完备。整体上以约定、配置化、组件化的设计思想,让用户仅仅关心用组件搭建页面内容。基于Vue.js3.0,充分利用Vue丰富的生态。技术曲线平缓,上手也简单。在经过多个项目中打磨后趋于稳定。 + +它主要具备以下功能: +- 🚀 __快速__ ,内置了路由、开发、构建等,并且提供测试、布局、权限、国际化、状态管理、API请求、数据字典、SvgIcon等插件,可以满足大部分日常开发需求。 + +- 🧨 __简单__ ,基于Vue.js 3.0,上手简单。贯彻“约定优于配置”思想,设计插件上尽可能用约定替代配置,同时提供统一的插件配置入口,简单简洁又不失灵活。提供一致性的API入口,一致化的体验,学习起来更轻松。 + +- 💪 __健壮__ ,只需要关心页面内容,减少写BUG的机会!提供单元测试、覆盖测试能力保障项目质量。 + +- 📦 __可扩展__ ,借鉴Umi实现了完整的生命周期和插件化机制,插件可以管理项目的编译时和运行时,能力均可以通过插件封装进来,在 Fes.js 中协调有序的运行。 + +- 📡 __面向未来__ ,在满足需求的同时,我们也不会停止对新技术的探索。已使用Vue3.0来提升应用性能,已使用webpack5 和 vite提升构建性能和实现微服务。 + +## 插件 + +| 插件 | 介绍 | +| ---- | ---- | +| [@fesjs/plugin-access](http://fesjs.mumblefe.cn/reference/plugin/plugins/access.html) | 提供对页面资源的权限控制能力 | +| [@fesjs/plugin-enums](http://fesjs.mumblefe.cn/reference/plugin/plugins/enums.html#%E4%BB%8B%E7%BB%8D) | 提供统一的枚举存取及丰富的函数来处理枚举 | +| [@fesjs/plugin-icon](http://fesjs.mumblefe.cn/reference/plugin/plugins/icon.html#%E4%BB%8B%E7%BB%8D) | svg 文件自动注册为组件 | +| [@fesjs/plugin-jest](http://fesjs.mumblefe.cn/reference/plugin/plugins/jest.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | 基于 `Jest`,提供单元测试、覆盖测试能力 | +| [ @fesjs/plugin-layout](http://fesjs.mumblefe.cn/reference/plugin/plugins/layout.html) | 简单的配置即可拥有布局,包括导航以及侧边栏 | +| [@fesjs/plugin-locale](http://fesjs.mumblefe.cn/reference/plugin/plugins/locale.html#%E4%BB%8B%E7%BB%8D) | 基于 `Vue I18n`,提供国际化能力 | +| [@fesjs/plugin-model](http://fesjs.mumblefe.cn/reference/plugin/plugins/model.html#%E4%BB%8B%E7%BB%8D) | 简易的数据管理方案 | +| [@fesjs/plugin-request](http://fesjs.mumblefe.cn/reference/plugin/plugins/request.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | 基于 `Axios` 封装的 request,内置防止重复请求、请求节流、错误处理等功能 | +| [@fesjs/plugin-vuex](http://fesjs.mumblefe.cn/reference/plugin/plugins/vuex.html#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F) | 基于 `Vuex`, 提供状态管理能力 | +| [@fesjs/plugin-qiankun](http://fesjs.mumblefe.cn/reference/plugin/plugins/qiankun.html#%E4%BB%8B%E7%BB%8D) | 基于 `qiankun`,提供微服务能力 | +| [@fesjs/plugin-sass](http://fesjs.mumblefe.cn/reference/plugin/plugins/sass.html#%E4%BB%8B%E7%BB%8D) | 样式支持sass | +| [@fesjs/plugin-monaco-editor](http://fesjs.mumblefe.cn/reference/plugin/plugins/editor.html#%E4%BB%8B%E7%BB%8D) | 提供代码编辑器能力, 基于`monaco-editor`(VS Code使用的代码编辑器) | +| [@fesjs/plugin-windicss](http://fesjs.mumblefe.cn/reference/plugin/plugins/windicss.html) | 基于 `windicss`,提供原子化 CSS 能力 | +| [@fesjs/plugin-pinia](http://fesjs.mumblefe.cn/reference/plugin/plugins/pinia.html) | pinia,状态处理 | +| [@fesjs/plugin-watermark](http://fesjs.mumblefe.cn/reference/plugin/plugins/watermark.html) | 水印 | +| [@fesjs/plugin-swc](http://fesjs.mumblefe.cn/reference/plugin/plugins/swc.html) | 使用swc-loader构建 | + +## 像数 1, 2, 3 一样容易 +使用 `yarn`: +```bash +# 创建模板 +yarn create @fesjs/fes-app myapp + +# 安装依赖 +yarn + +# 运行 +yarn dev +``` + +使用 `npm`: +```bash +# 创建模板 +npx @fesjs/create-fes-app myapp + +# 安装依赖 +npm install + +# 运行 +npm run dev +``` + +## 反馈 + +| Github Issue | Fes.js开源运营小助手 | +| --- | --- | +| [@fesjs/fes.js/issues](../../issues) | | + + +## 参与共建 + +我们非常欢迎社区同学能提交PR: + +1. fork项目! +2. 创建你的功能分支: `git checkout -b my-new-feature` +3. 本地提交新代码: `git commit -am 'Add some feature'` +4. 推送本地到服务器分支: `git push origin my-new-feature` +5. 创建一个PR + +如果是发现Bug或者期望添加新功能,请提交[issue](../../issues)。 + +## 社区活动 + +### Fesjs 社区有奖征文活动 + +为了 Fes.js 开源项目更好的运转,同时回馈开源社区,社区推出有奖征文活动!欢迎大家投递实践经验,给社区用户,更广泛的开发者提供借鉴。 + +经验输出也可以帮助到你系统沉淀自有项目,梳理工作思路,也能够帮助你的技术博客做宣传。优秀的实践案例将有机会邀请参与项目社区技术会议分享,赶快来参与吧。 +请戳:https://mp.weixin.qq.com/s/nV4NG_OUUrdgtft8g_IW4g + + + diff --git a/packages/fes-plugin-swc/package.json b/packages/fes-plugin-swc/package.json new file mode 100644 index 00000000..0ca6879a --- /dev/null +++ b/packages/fes-plugin-swc/package.json @@ -0,0 +1,41 @@ +{ + "name": "@fesjs/plugin-swc", + "version": "3.0.0-rc.0", + "description": "@fesjs/plugin-swc", + "main": "lib/index.js", + "types": "types.d.ts", + "files": [ + "lib", + "types.d.ts" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/WeBankFinTech/fes.js.git", + "directory": "packages/fes-plugin-swc" + }, + "keywords": [ + "fes" + ], + "author": "RiESAEX", + "license": "MIT", + "bugs": { + "url": "https://github.com/WeBankFinTech/fes.js/issues" + }, + "homepage": "https://github.com/WeBankFinTech/fes.js#readme", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@swc/core": "^1.3.32", + "@swc/css": "^0.0.18", + "swc-plugin-vue-jsx": "^0.2.0", + "swc-loader": "^0.2.3", + "@fesjs/utils": "^3.0.0-rc.2", + "terser-webpack-plugin": "^5.3.6", + "css-minimizer-webpack-plugin": "^4.0.0" + }, + "peerDependencies": { + "@fesjs/fes": "^3.0.0-rc.7", + "@fesjs/builder-webpack": "^3.0.0-rc.5" + } +} diff --git a/packages/fes-plugin-swc/src/index.js b/packages/fes-plugin-swc/src/index.js new file mode 100644 index 00000000..e6fb7a55 --- /dev/null +++ b/packages/fes-plugin-swc/src/index.js @@ -0,0 +1,126 @@ +import { join } from 'path'; +import TerserPlugin from 'terser-webpack-plugin'; +import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; +import { buildSwcOptions } from './swcOptions'; + +const DEFAULT_EXCLUDE_NODE_MODULES = [ + 'vue', + 'vuex', + 'vue-router', + 'core-js', + 'echarts', + '@babel/runtime', + 'lodash-es', + 'webpack-dev-server', + 'ansi-html', + 'html-entities', +]; + +function genTranspileDepRegex(exclude) { + exclude = exclude.concat(DEFAULT_EXCLUDE_NODE_MODULES); + const deps = exclude.map((dep) => { + if (typeof dep === 'string') { + const depPath = join('node_modules', dep, '/'); + return /^win/.test(require('os').platform()) ? depPath.replace(/\\/g, '\\\\') : depPath; + } + if (dep instanceof RegExp) { + return dep.source; + } + + throw new Error('exclude only accepts an array of string or regular expressions'); + }); + return deps.length ? new RegExp(deps.join('|')) : null; +} + +export default (api) => { + api.describe({ + key: 'swc', + config: { + schema(joi) { + return joi.object({ + loader: joi.object().description('more swc options see https://github.com/swc-project/swc-loader'), + }); + }, + }, + }); + + if (api.builder.name === 'vite') { + // vite 不需要处理 + } else { + api.chainWebpack((webpackConfig, { targets, env }) => { + const isProd = env === 'production'; + const config = api.config; + + //清除babel配置 + webpackConfig.module.rules.delete('js'); + + webpackConfig.module + .rule('js') + .test(/\.(js|mjs)$/) + // Don't transpile node_modules + .exclude.add(/node_modules/) + .end() + .use('swc-loader') + .loader(require.resolve('swc-loader')) + .options(buildSwcOptions(targets, config, false, false)); + + webpackConfig.module + .rule('jsx') + .test(/\.jsx$/) + .exclude.add(/node_modules/) + .end() + .use('swc-loader') + .loader(require.resolve('swc-loader')) + .options(buildSwcOptions(targets, config, true, false)); + + webpackConfig.module + .rule('ts') + .test(/\.ts$/) + .exclude.add(/node_modules/) + .end() + .use('swc-loader') + .loader(require.resolve('swc-loader')) + .options(buildSwcOptions(targets, config, false, true)); + + webpackConfig.module + .rule('tsx') + .test(/\.tsx$/) + .exclude.add(/node_modules/) + .end() + .use('swc-loader') + .loader(require.resolve('swc-loader')) + .options(buildSwcOptions(targets, config, true, true)); + + // 为了避免第三方依赖包编译不充分导致线上问题,默认对 node_modules 也进行全编译,只在生产构建的时候进行 + if (isProd) { + webpackConfig.module.rules.delete('js-in-node_modules'); + + // const cjsReg = [/css-loader/, /vue-loader/].concat(config.swcLoader?.cjsPkg || []); + 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(targets, config, false, false)); + + const swcOptions = buildSwcOptions(targets, config, false, false, true); + webpackConfig.optimization.minimizer('css').tap((args) => [...args, { minify: CssMinimizerPlugin.swcMinify }]); + webpackConfig.optimization + .minimizer('terser') + .tap((args) => [...args, { terserOptions: swcOptions.jsc?.minify, minify: TerserPlugin.swcMinify }]); + } + + return webpackConfig; + }); + } +}; diff --git a/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/swcOptions.js b/packages/fes-plugin-swc/src/swcOptions.js similarity index 63% rename from packages/fes-builder-webpack/src/plugins/commands/webpackConfig/swcOptions.js rename to packages/fes-plugin-swc/src/swcOptions.js index e5654e36..b4e14ad5 100644 --- a/packages/fes-builder-webpack/src/plugins/commands/webpackConfig/swcOptions.js +++ b/packages/fes-plugin-swc/src/swcOptions.js @@ -1,14 +1,21 @@ import { deepmerge } from '@fesjs/utils'; +const Supported = ['chrome', 'opera', 'edge', 'firefox', 'safari', 'ie', 'ios', 'android', 'node', 'electron']; + export function buildSwcOptions(targets, config, isJsx, isTs, minify = false) { - if (config.swcLoader?.cjsPkg) { - delete config.swcLoader.cjsPkg; + if (config.swc?.loader?.cjsPkg) { + delete config.swc.loader.cjsPkg; } return deepmerge( { // sync: true, env: { - targets, + targets: Object.keys(targets) + .filter((key) => Supported.includes(key)) + .reduce((memo, key) => { + memo[key] = targets[key]; + return memo; + }, {}), mode: 'usage', coreJs: '3', }, @@ -28,6 +35,6 @@ export function buildSwcOptions(targets, config, isJsx, isTs, minify = false) { isModule: 'unknown', minify: minify ? {} : false, }, - config.swcLoader || {}, + config.swc?.loader || {}, ); } diff --git a/packages/fes-plugin-swc/types.d.ts b/packages/fes-plugin-swc/types.d.ts new file mode 100644 index 00000000..dcba1f52 --- /dev/null +++ b/packages/fes-plugin-swc/types.d.ts @@ -0,0 +1,7 @@ +declare module "@fesjs/fes" { + interface PluginBuildConfig { + swc?: { + loader?: object; + }; + } +} diff --git a/packages/fes-preset-built-in/src/plugins/features/targets.js b/packages/fes-preset-built-in/src/plugins/features/targets.js index 592d97ec..ef3fb03b 100644 --- a/packages/fes-preset-built-in/src/plugins/features/targets.js +++ b/packages/fes-preset-built-in/src/plugins/features/targets.js @@ -5,7 +5,6 @@ export default (api) => { default: { chrome: '64', ios: '11', - browsers: ['defaults and not chrome < 61'], }, schema(joi) { return joi.object(); diff --git a/packages/fes-template/.fes.js b/packages/fes-template/.fes.js index be3cdbbc..a1dee26f 100644 --- a/packages/fes-template/.fes.js +++ b/packages/fes-template/.fes.js @@ -1,9 +1,11 @@ import { defineBuildConfig } from '@fesjs/fes'; export default defineBuildConfig({ - swcLoader: { - env: { - coreJs: '3.27', + swc: { + loader: { + env: { + coreJs: '3.27', + }, }, }, targets: { diff --git a/packages/fes-template/package.json b/packages/fes-template/package.json index 8b5ebda2..259d021f 100644 --- a/packages/fes-template/package.json +++ b/packages/fes-template/package.json @@ -60,10 +60,9 @@ "@fesjs/plugin-vuex": "^3.0.0-rc.0", "@fesjs/plugin-watermark": "^3.0.0-rc.0", "@fesjs/plugin-windicss": "^3.0.0-rc.0", - "@swc/core": "^1.3.32", + "@fesjs/plugin-swc": "^3.0.0-rc.0", "core-js": "3.27.0", "pinia": "^2.0.11", - "swc-plugin-vue-jsx": "^0.2.0", "vue": "^3.2.37", "vuex": "^4.0.0" },