You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/** * This uses a dynamic import to load a module which may be ESM. * CommonJS code can load ESM code via a dynamic import. Unfortunately, TypeScript * will currently, unconditionally downlevel dynamic import into a require call. * require calls cannot load ESM code and will result in a runtime error. To workaround * this, a Function constructor is used to prevent TypeScript from changing the dynamic import. * Once TypeScript provides support for keeping the dynamic import this workaround can * be dropped. * * @param modulePath The path of the module to load. * @returns A Promise that resolves to the dynamically imported module. */functionloadEsmModule<T>(modulePath: string|URL): Promise<T>{returnnewFunction('modulePath',`return import(modulePath);`)(modulePath)asPromise<T>;}
什么是 native ESM?
Node.js 模块历史简单回顾
ESLint 现状
await import()
变成同步操作eslint-mdx
的选择synckit
由于其异步转同步的能力在eslint-mdx@1.13.0
被引入,当时是为了解决部分remark
插件要求异步执行的问题,而esm
没有能力解决同步调用异步函数的问题,因此显而易见地我们继续使用synckit
来解决 ESM 的问题基于
synckit
的改造思路synckit
的worker
中,并使用await import()
加载,因为我们的 worker 代码也是 commonjs 的tsc
执行完成,而目前 TypeScript 输出 commonjs 时总是会将await import()
转化为类似Promise.resolve().then(() => require())
的结构,这直接导致我们在 commonjs 中使用await import()
加载 ESM 失效了。恰好这两天在@angular-builders/custom-webpack
升级支持 ESM 的 PR(Angular 13 也已经变成 pure ESM 了)看到了相关的 workaround:parse
和process
流程,我们将这两个操作也封装在同一个 worker 中按入参分别进行并返回可以被结构化克隆算法序列化的结构,不出意外的话我们的改造流程就能顺利运行了意外踩坑
v-file-message
定义的VFileMessage
无法被正确序列化(暂时没有去深究原因,可能与它继承了Error
有关),使用JSON.parse(JSON.stringify())
深度克隆一次即可remark-mdx
内部使用acorn
对 ES 和 jsx 语法进行parse
,而acorn
解析出的token
和eslint
默认使用的parser
espree
有差异(实际上espree
内部也是使用的acorn
),因此我们需要在将remark-mdx
调用acorn
解析出的tokens
进行转换,而这个转换的部分espree
内部已经有了相关实现TokenTranslator
,我们『直接复用即可』。TokenTranslator
使用过程中发现TokenTranslator
并没有被导出,导致await import('espree/lib/token-translator')
不可用,我们需要使用绝对路径加载的方式越过这个限制:TokenTranslator#onToken
时我们需要传入acorn
和acorn-jsx
中定义的tokTypes
,而acorn
是一个双格式混合包,acorn-jsx
是一个纯 commonjs 包,这本来无所谓,但是acorn-jsx
里定义tokTypes
的方式是getJsxTokens(require("acorn")).tokTypes
的getter
,这导致acorn-jsx
里引用的acorn
是 commonjs 格式的,而remark-mdx
是纯 ESM 包,引用的acorn
是 ESM 格式的,最终导致两个包定义的TokenType
虽然内容是一样,但引用/指针却不同。因此我们只能查看acorn-jsx
源码后使用如下的方式获取正确的jsxTokTypes
:至此,
eslint-mdx
基本就能完整支持 native ESM 了,剩下的就是一些常规的由于依赖破坏性变更导致的改动,此次踩坑实践详见相关 PR。The text was updated successfully, but these errors were encountered: