前一阵子二开一个浏览器插件,给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,两种方案:

等等,既然都自定义了,为啥不再看看其他的库呢?比如https://github.com/ThomasAribart/json-schema-to-ts

哈哈哈到时候再说吧。

神奇,browserify居然能把nodejs环境相关的引用给polyfill掉,使得浏览器能正常运行nodejs相关的库。