首页 > 其他分享 >Molecule 在构建工具中的选择

Molecule 在构建工具中的选择

时间:2023-10-31 12:13:13浏览次数:42  
标签:fs const Molecule tsc 构建 工具 dirname

我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。

本文作者:修能

朝闻道,夕死可矣

何为 Molecule?
轻量级的 Web IDE UI 框架——Molecule
我们开源了一个轻量的 Web IDE UI 框架
Molecule实现数栈至简前端开发新体验

前言

构建通常指的是把源代码转换成发布到线上的可执行 JavaScrip、CSS、HTML 代码。在前端发展的过程中,源代码的模块体系在不断的更新,最终产物也在不断的更新。而随之也使得构建工具也在不断更新换代。

而目前来看,基于前端的细化领域下,针对不同领域下的构建工具也日新月异。来看看 Molecule 该如何选择构建工具呢?

Molecule 的需求

首先,我们需要分析 Molecule 对构建工具的需求有什么?

老版本的问题

  1. 本地开发和 build 的构建工具不同,不得不增加 web 命令来执行一个预览的任务,确保 build 后的产物没问题。
  2. 慢,由于使用 tsc 作为编译,所以编译较慢。
  3. 部分变量无法复用,导致重复定义。

代码编译

由于 Molecule 的代码是用 ESM 的模块书写,且 Molecule 面向的是 Web 应用。通常来说面向 Web 应用的依赖库是需要提供 ESM 的代码实现 tree shaking 的作用的。
所以我们这里需要把 ESM 书写的 Molecule 代码通过构建工具编译成 ESM。

思考:为什么要把 ESM 代码编译成 ESM?

  1. 将 TypeScript 编译成 JavaScript
  2. 将高级语法编译成低级语法

除此之外,由于我们考虑到 Node.js 后续发展以 Pure ESM 为主,且 Molecule 针对 CommonJS 的场景较少,故我们不考虑输出 CommonJS 的产物。

类型

需要支持输出类型。

样式

Molecule 中使用 BEM 作为类名规范,通常情况下使得需要在 Sass 中和 JavaScript 中都定义相同变量名。而类 Sass-in-JS 使得我们可以从 Sass 中导出变量名,在 JS 文件中使用。
这就使得构建工具不仅要支持 Sass 的编译,同时还需要支持插件,允许我们做 Sass-in-JS 的需求。

其他

其他相关文件,例如 JSON,PNG 等文件需要支持拷贝至相关指定目录。

调研构建工具

Webpack

Webpack 是目前构建工具中的老大哥了,作为顶级老牌构建工具,几乎所有场景都能适用。
缺点也仅仅是冗余代码较多,配置项太多,体积较大等。

Rollup

作为面向 JS 类库而出现的构建工具。其和 Webpack 相比,在打包后产生的冗余代码少,体积较小,功能专注。缺点仅仅是不支持 HMR。

Vite

直接排除

Parcel

Parcel 目前看作是面向 Web 应用的零配置,高速度的 Webpack。其有一个致命的弱点是,自定义插件支持不如 Webpack。这会让我们无法实现 Sass-in-JS。
2.0 可能有所改善,我不清楚。不予评价

swc

swc 在某种程度上,是 babel 和 tsc 的竞品,属于比较底层的构建工具。和 esbuild 同类型,只是 esbuild 基于 Go,swc 基于 Rust。

esbuild

extremely fast JavaScript Compiler

babel

很好,就是慢

tsc

很好,就是更慢。有一个优点,只有 tsc 能支持输出类型。

方案实施

由于大多数的构建工具都是 bundler,并不符合 Molecule 的定位。故采取的方案是 esbuild + Sass + tsc 的方案。
esbuild 取其作为 Compiler 的部分,Sass 取其编译 SCSS 文件的部分,tsc 负责编译出类型文件。

tsx 相关文件输出

 transformCtx = await esbuild.context({
        entryPoints,
        bundle: false,
        format: 'esm',
        outdir: dist,
        jsx: 'automatic',
        plugins: [
            {
                name: 'alias',
                setup(build) {
                    build.onLoad({ filter: /.*/ }, async (args) => {
                        const source = await fs.promises.readFile(args.path, 'utf8');
                        const contents = sassLoader(alias(source, args.path));
                        return {
                            contents,
                            loader: args.path.endsWith('.tsx') ? 'tsx' : 'ts',
                        };
                    });
                },
            },
        ],
    });
    await transformCtx.watch();

做两件事

  1. 别名重定位
  2. 将文件中的样式文件改为 css

样式文件输出

/**
 *
 * @param {string} entry
 */
async function _transform(entry) {
    const res = await sass.compileAsync(entry);
    const regex = /^:export {(\n|.)+}$/m;
    const target = entry.replace(/src\//, 'esm/').replace(/.scss/, '.css');
    const dirname = path.dirname(target);
    if (!fs.existsSync(dirname)) {
        fs.mkdirSync(dirname, { recursive: true });
    }
    const css = res.css.replace(regex, '');
    fs.writeFileSync(target, css);
    if (regex.test(res.css)) {
        const exportModules = res.css.match(regex)[0];
        fs.writeFileSync(
            path.join(dirname, styleVariablesFileName),
            exportModules
                .replace(':export', 'export default')
                .replace(/: .*;/gm, (substring) => {
                    const stringLiteral = /(?<="|')\S+(?="|')/g;
                    if (!stringLiteral.test(substring)) {
                        const startIdx = substring.indexOf(':');
                        const endIdx = substring.indexOf(';');
                        return `:"${substring.substring(startIdx + 1, endIdx).trim()}",`;
                    } else {
                        return substring.replace(';', ',');
                    }
                })
        );
    }
}

做两件事

  1. :export干掉
  2. :export的内容放到当前目录下的style__variables.js的目录中

类型文件输出

类型文件异步输出,防止阻塞

async function transformTyping() {
    typingCtx = spawn('tsc && (concurrently "tsc -w" "tsc-alias -w")', {
        stdio: 'inherit',
        shell: true,
    });
}

其他文件输出

/**
 *
 * @param {string} filePath
 */
function _copyFile(filePath) {
    const dest = filePath.replace(/src\//, 'esm/');
    const dirname = path.dirname(dest);
    if (!fs.existsSync(dirname)) {
        fs.mkdirSync(dirname, { recursive: true });
    }
    fs.createReadStream(filePath, 'utf-8').pipe(fs.createWriteStream(dest));
}

遗留问题

  • 增量编译的问题
  • 代码压缩

欢迎大家就以上问题留言讨论!

最后

欢迎关注【袋鼠云数栈UED团队】~
袋鼠云数栈UED团队持续为广大开发者分享技术成果,相继参与开源了欢迎star

标签:fs,const,Molecule,tsc,构建,工具,dirname
From: https://www.cnblogs.com/dtux/p/17799946.html

相关文章

  • webapi 注解调试工具swaggo 介绍和使用
    swaggo介绍和使用介绍Swag是一个开源项目,用于web框架下接口调试和文档管理,可以将代码中的接口注释转换为文档格式,并提供界面在线调试接口的功能。项目地址:https://github.com/swaggo/swag目前项目可以支持的web框架gin,echo,buffalo,net/http,gorilla/mux,go-chi/chi,flamingo,fi......
  • Gradle8.4构建SpringBoot多模块项目
    Gradle8.4构建SpringBoot多模块项目一、基本1、版本这个版本是Jdk8最后一个SpringBoot版本软件版本Gradle8.4SpringBoot2.7.15JDK82、Gradle基本介绍2.1、使用Wrapper方式构建好处:统一gradle的版本好处:不用安装gradle就可以使用Maven也是一样的......
  • React Native 页面调试工具 react-native-vdebug
    yarnaddreact-native-vdebugimportReactfrom'react'import{createNativeStackNavigator}from'@react-navigation/native-stack'import{getRouter}from'./config'import{ErrorBoundary}from'../component/light......
  • 教育管理工具类 APP 在智慧校园中的应用探究?
    智慧校园是指利用信息化技术,将学校的管理、教育教学等方面进行整合和优化,实现校园运行的智能化和高效化。在智慧校园建设中,教育管理工具类APP发挥着重要的作用。本文将详细探究教育管理工具类APP在智慧校园中的应用,并从以下几个方面进行介绍:一、招生与报名管理教育管理工具类A......
  • Java配置工具:typesafe config使用文档超详解
    文章目录一、typesafeconfig概述1、官网2、优点3、JSON超集特性(HOCON)(1)实例4、版本概述二、typesafeconfig基本使用1、API示例2、更多示例3、不变性4、模式和验证5、配置文件加载6、合并配置树7、处理默认值8、理解Config和ConfigObject9、理解ConfigFactory一、typesafeconfig......
  • 爬虫工具—whistle安装与使用
    参考链接https://mbd.baidu.com/ug_share/mbox/4a83aa9e65/share?product=smartapp&tk=fae2094d0e00d4e4fae484fa554fe802&share_url=https%3A%2F%2Fzoyi14.smartapps.cn%2Fpages%2Fnote%2Findex%3Fslug%3D17c48959be44%26origin%3Dshare%26_swebfr%3D1%26_swebFromHost%......
  • 第十章 数据库连接池与DBUtils工具
    目录一.单选题(共5题,50分)二.判断题(共5题,50分)一.单选题(共5题,50分)(单选题)已知,存在QueryRunner对象runner,SQL语句:Stringsql="select*fromuserwhereid=?";下面操作中,能实现查询指定记录的选项是()A.Useruser=(User)runner.query(sql,newBeanListHandler(User.c......
  • ELASTICSEARCH-监控工具cerebro
    文件摘自https://www.cnblogs.com/hogan0210/p/16279731.html  cerebro下载地址:https://github.com/lmenezes/cerebro/releases/download/v0.9.4/cerebro-0.9.4.tgz1、解压tar-zxvfcerebro-0.9.4.tgz2、配置访问elasticsearch服务(可配置多个,不配置不可访问)修改cereb......
  • 好用的API调试工具推荐:Apipost
    随着数字化转型的加速,API(应用程序接口)已经成为企业间沟通和数据交换的关键。而在API开发和管理过程中,API文档、调试、Mock和测试的协作显得尤为重要。Apipost正是这样一款一体化协作平台,旨在解决这些问题,提高API开发效率和质量。Apipost提供API文档管理功能,让后端开发人员可以在开......
  • 好用的API调试工具推荐:Apipost
    随着数字化转型的加速,API(应用程序接口)已经成为企业间沟通和数据交换的关键。而在API开发和管理过程中,API文档、调试、Mock和测试的协作显得尤为重要。Apipost正是这样一款一体化协作平台,旨在解决这些问题,提高API开发效率和质量。 Apipost提供API文档管理功能,让后端开发人员可......