前一阵子二开一个浏览器插件,给yapi的web页面添加一些额外的功能,是基于katavii/yapi-to-typescript实现的。可以参见chrome插件开发。
哪里来的bundle.js
期间遇到个问题,旧项目是cjs的,工具也比较老旧。十分恶心人。想尝试切换到更现代化的esm,尝试到一半,发现引入了一个本地的bundle.js,仔细一看,居然是个iife规范的包!

再一看,心凉了,4万多行打包后的代码,怕是没办法更换成esm了:

粗略查询jstt也没有查询到相关的内容,于是在cjs模式下完成了这次二开。不过一直在心里有个疙瘩。
改造
动态import
过了一段时间,2024年5月17日,还是忍不了,还是想试试,于是在esm工程里把bundle拷进去了,试了一下靠浏览器支持的动态import语法:
// esm工程
import('./bundle.js').then((module) => {
console.log(module);
console.log('jstt', jstt);
});正常返回:

但是,这是异步的,所有对jstt里方法的调用都要放到回调里。
查库
不想动态import,那就找找源库吧。之前看过bundle.js里的代码,发现最后一行里写了库的名字:“json-schema-to-typescript”(后文简称jstt),于是去github上搜,果然搜到了:https://github.com/bcherny/json-schema-to-typescript。(虽然很多类似的库,但是通过对比代码里的引用路径确定了就是它)
但是,为啥这是一个命令行脚本,一个nodejs环境的库?

我懵了。看了下src下的目录结构,和bundle.js内相关的引用路径又是匹配的。是这个库没错。
于是我尝试直接使用ESM规范直接import引入这个库:
// esm
import { compile, compileFromFile } from 'json-schema-to-typescript'
console.log(compile, compileFromFile)然后它就报错了:

好吧,果然是个纯nodejs的库。查了下issue,2022年也有人提了这个问题process.cwd is not a function,作者的回复是True. I made up my mind I managed to generate the interfaces at build time. Thanks。…emm。
好吧,那我找的这个浏览器插件里bundle.js哪来的?
查库2
仔细看文档,发现jstt库有个浏览器demo,对应的github仓库是https://github.com/bcherny/json-schema-to-typescript-browser。在仓库中,我发现了bundle.js的身影,dist目录下有bundle.js,居然5天前还有更新!难道就是它?
不是。
这个是整个demo站点的rollup打包产物,包含了业务流程,不是单纯的库打包,大致代码如下:
// index.js 包含demo站点的业务逻辑
const { compile } = require('json-schema-to-typescript');
...
//rollup.config.js 打包配置
...
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife',
globals: {
prettier: 'prettierWithOptions'
},
name: 'jstt' // 名字倒是对上了
},
external: ['prettier'],
plugins: [resolve(), commonjs(), nodePolyfills()]
};
...
// index.html 页面引入
<script src="./dist/bundle.js"></script>
<script>
window.__dirname = '';
// standalone requires parsers be explicitly loaded
window.prettierWithOptions = {
format(str, options) {
return prettier.format(
str,
Object.assign({}, options, {
plugins: Object.assign({}, prettierPlugins, options.plugins)
})
);
}
};
</script>总之,此bundle.js非彼bundle.js。
查库3
那么,会不会是djstt demo库的历史版本呢?为什么这么想,因为我发现jstt的作者更新很勤快,而且还有一些github的机器人提交,可能已经和浏览器插件作者使用的版本有比较大的出入了。
于是:
- 先去查看浏览器插件作者最后一次提交的时间:2021年7月。
- clone
json-schema-to-typescript-browser。查看git提交历史,寻找蛛丝马迹。
本以为会有很多很多提交记录,会很难找。没想到很快就找到了:

上图框出来的这几个提交的时间和2021年7月很接近,按时间升序依次查看,然后就找到了!
bundle.js的来源
commit1
在bump deps这个commit内,看到了静态资源引入,此时作者是Boris Cherny。

更新也很简单:

此时终于发现了bundle.js的身影,直接访问代码里的这个资源https://unpkg.com/json-schema-to-typescript@9/dist/bundle.js,确认了它就是我在找的静态资源,但是无法确认这个版本是否就是浏览器插件用的。另外此时我在想,jstt的第9个版本是不是本来就支持浏览器的呢?
不。
commit2
再看第二个commit,发现静态资源放到本地了!?此时作者是Gareth Jones

新增的bundle.js就是将cdn的资源改为本地服务的了,那这个本地bundle.js怎么来的呢,查阅package.json我就发现端倪。

作者把json-schema-to-typescript整个库通过browserify打包了,并命名变量为jstt(所以commit1里的bundle.js可能就是作者用自己电脑打包后加进仓库的,然后又把打包的命令也加到仓库了)。
- 在之后的某一次提交里,作者将
"json-schema-to-typescript": "^10.1.2",改成了"json-schema-to-typescript": "*",。 - 所以,
jstt官方一直都不支持浏览器。只不过是作者为了在浏览器演示demo,强行将jstt通过browserify打包成了浏览器支持的包。
相信浏览器插件的作者,就是这个时候找到了json-schema-to-typescript,并从它的仓库里拷贝了bundle.js。
commit3
再看一下这条神来一笔的commit,move from browserify -> rollup:

也就是5天前…2024年5月12日,作者移除了demo网站项目的browserify打包,并把整个项目添加了打包器rollup、也就是说从仅打包json-schema-to-typescript库变成了打包整个项目。
这就是为什么在查库2我没有找到正确的bundle.js的原因。这也太巧了。
自此,github官网上再也没有纯jstt的浏览器包了(当然,我们可以自己打包)。
后文
json-schema-to-typescript的作者不愿意把这个库浏览器化,那我也不强求。至于让我去改源码提mr,也是大可不必的,没那精力也没那水平。
那除了改造就只有一条路了,恢复静态资源化(html里直接引用script资源),继续使用全局变量jstt。但是至少我知道了jstt的来源,万一真有了什么问题,也不至于只能在打包产物上改了。
如果改造,如果有空,我会把cjs的浏览器插件给改成esm,两种方案:
- 加上commit2里的代码,使用
browserify本地构建最新版的json-schema-to-typescript,然后作为静态资源加载到项目里。很简单,不再赘述。 - 解决:esm模式web项目里nodejs模块polyfill问题。思路见esm模式web项目里对nodejs模块进行polyfill处理。
等等,既然都自定义了,为啥不再看看其他的库呢?比如https://github.com/ThomasAribart/json-schema-to-ts。
哈哈哈到时候再说吧。
神奇,browserify居然能把nodejs环境相关的引用给polyfill掉,使得浏览器能正常运行nodejs相关的库。