Symbolk / somanyconflicts

No more headache on So Many Conflicts after merging!
https://marketplace.visualstudio.com/items?itemName=symbolk.somanyconflicts
MIT License
7 stars 3 forks source link

build: bundle extension with esbuild #27

Open Symbolk opened 3 years ago

Symbolk commented 3 years ago

目标:使用esbuild压缩与发布somanyconflicts release版本。

分支:build

参考:https://code.visualstudio.com/api/working-with-extensions/bundling-extension

Symbolk commented 3 years ago

目前写了一个配置,但是貌似TreeSitter会有问题,你们碰到过或知道如何解决吗? @Shigma @TsukimiRini image

image

shigma commented 3 years ago

试试看这个?

import TreeSitter from 'tree-sitter'
Symbolk commented 3 years ago

试试看这个?

import TreeSitter from 'tree-sitter'

提示需要在tsconfig中设置"esModuleInterop": true,

image

但设置之后会在编译test/index.ts(使用yo初始化项目时自动产生的test suite)时出现错误:

image

移除index.ts之后可以正常运行,vsce package不再警告,但独立安装后仍有 Error: Cannot find module './build/Release/tree_sitter_runtime_binding'错误

shigma commented 3 years ago

刚刚重新看了一下 dts 文件,之前我说的有误。我先说一下 ts 中模块导出和引入的三种方法:

  1. 使用 export { xxx } 导出(export const / function / class / namespace / type xxx 同理),此时应该使用 import { xxx } from 导入。
  2. 使用 export default xxx 导出,此时应该使用 import xxx from 导入。
  3. 使用 export = xxx 导出(这种写法是 ts 为了 node 兼容性引入的,非 es 标准语法),此时有三种解决方案:
    • 使用 import xxx from 导入
    • 使用 import { xxx } from 导入,同时配置 esModuleInterop
    • 使用 import xxx = require() 导入(这种写法同样也非 es 标准语法,导入的模块同样会有类型标注不用担心)

然后 tree-sitter 和 mocha 的类型定义都是 export = xxx 形式的,所以还是应该直接 import xxx from(同时也不需要配置 esModuleInterop)。

那么为啥 esbuid 会报 warning 呢?这里牵扯到 esmodule 标准执行的问题。

cjs 在 esm 标准被推广之前就已经大行其道,在 cjs 中模块的导出无非就是一个对象,其他模块 require 这个模块的时候,返回的就是这个对象本身,因此这个对象的所有属性都是可以被修改的。

// 1.js
module.exports = { foo: 'bar' }

// 2.js
const ns = require('1.js')
ns.foo = 'baz'

// 3.js
const ns = require('1.js')
require('2.js')
console.log(ns.foo) // baz

然而根据 esm 规范

9.4.6.8 [[Set]] ( P, V, Receiver )

When the [[Set]] internal method of a module namespace exotic object O is called with property key P, value V, and ECMAScript language value Receiver, the following steps are taken:

Return false.

因此,将上面的写法换成 esm,得到的结果就会不同:

// 1.js
export const foo = 'bar'

// 2.js
import * as ns from '1.js'
ns.foo = 'baz'

// 3.js
import * as ns from '1.js'
import '2.js'
console.log(ns.foo) // bar

回到上面列举的三种导出方法,为什么 ts 要创造一种有别于 esm 的新语法呢?这是因为 cjs 没有 export default,在 esModuleInterop 的帮助下,ts 用户可以使用任何一种方式导入上面的文件,非常方便:

// 源文件 1.ts
const ns = { foo: 'bar' }
export = ns

// 编译后的 1.d.ts
declare ns: { foo: string }
export = ns

// 编译后的 1.js,简化了各种 helper
const ns = { foo: 'bar' }
Object.defineProperty(ns, 'esModule', { value: true })
Object.defineProperty(ns, 'default', { value: ns })
module.exports = ns

// some-file.ts
import { foo } from '1.js'
import ns from '1.js'

这对于 ts 自身的生态没有问题,但是当有了 esbuild 就不一样了。esbuild 作者坚持使用符合标准的实现,导致所有 import xxx from 都会被打包一层 helper,将其中的所有属性都变成 getter,这会导致许多库都没法正常导入。在这种情况下使用上文中提及的第三种方法导入才是最稳妥的选择。

如果要在测试环境也使用 esbuild,同样需要把 mocha 的导入也改掉。