Webpack per-entry 模块解析别名规则

Kri*_* A. 5 webpack

这里有一个很长的。尽管我对答案或解决方案没有太多(或任何)期望,但仍然感觉像乐趣 分享/发泄的有趣问题。

我拥有的

我有一个有 2 个入口点的应用程序。它们都导入了相同的文件,main.ts而后者又导入了一个 Handlebars 模板main.hbs

entry1.ts
? main.ts
  ? main.hbs
  ? …
entry2.ts
? main.ts
  ? main.hbs
  ? …
Run Code Online (Sandbox Code Playgroud)

main.ts还导入了其他 TS 类(数千个),这些类导入了其他 HBS 模板(数百个),但几乎所有这些都在两个条目之间共享。这些条目只是main.ts通过一些特定于条目的选项调用 Main 类。

我需要的

我被要求为 HBS 模板创建一个特定于条目的“变体”,以便entry1加载main.v1.hbsentry2加载main.v2.hbs(如果存在此类文件)。如果任何其他导入的.ts文件导入任何其他.hbs文件,它们也会返回相应的*.v1.hbs/*.v2.hbs变体。

由于我希望它尽可能自动化并且对实际源代码的更改尽可能少,我决定要走的路是让 Webpack “重定向”导入:

  • entry1.ts导入实际加载的main.ts导入main.hbsmain.v1.hbs
    • main.ts进口menu.ts实际上负载menu.v1.hbs
    • 等等…
  • entry2.ts导入实际加载的main.ts导入main.hbsmain.v2.hbs
    • main.ts进口menu.ts实际上负载menu.v2.hbs
    • 等等…

我认为这种方法会给我带来多种优势:

  1. 不必更改所需文件(实际上是数百个)中的源代码

    如果我不关心这一点,我可以使用带有表达式require,然后使用摇树来只留下每个条目中使用的模板。然而…

    • 这会增加编译时间和复杂性
    • 如果不能正确地进行 tree-shake 并且不得不将所有变体实际存储在一个包中,这样我就可以在运行时访问正确的一个,则可能会大大增加包的大小(eww)
    • 我的怪胎不太热衷于处理更多的东西只是为了把它们扔掉
    • 该方法似乎只适用于require()语法而不适用于 ES 风格的静态import——所以不向前兼容
  2. 可以在“重定向”之前检查变体文件是否存在,如果不存在则回退到默认文件。

    重要的是,这允许我逐步进行此转换,因为创建了更多新“v2”变体的文件,而无需保留哪些导入映射到哪些文件或其他内容的静态列表。

  3. 也许可以在单个编译运行中完成此转换。

    如果我不关心这个,我可以单独编译每个条目,在每次运行时设置适当的选项。然而,应用程序相当大,需要几分钟的时间来构建,甚至需要增加节点内存限制。因此,按顺序或并行单独构建每个条目并不太好,尽管如果没有其他方法,这是我的最后手段。无论如何,鉴于 Webpack 的功能,我觉得这是我应该能够在单个构建中完成的事情。

我试过的

NormalModuleReplacementPlugin

这是一个内置于 Webpack 的插件,乍一看似乎满足了我的需求:拦截require对特定模块的调用并将它们更改为其他模块,它甚至还支持正则表达式和谓词函数。但是我很快放弃了这个,因为在编译过程中映射不能改变。这意味着我不能为每个条目设置不同的替换规则。

编写我自己的加载器

现在我们深陷战壕。我想,为什么不编写自己的加载程序来解决这个问题?加载程序能够读取其根条目,因此理论上我应该能够使用该信息来代替main.hbs加载main.v1.hbsforentry1main.v2.hbsfor entry2

虽然最初这似乎有效(尽管我讨厌这种方法的非无状态性),但我发现 Webpack 似乎在第一次执行它时缓存了 require/resolution:while processing main.ts. 因此,即使使用我拥有的所有逻辑,我的加载器每个文件只调用一次,而不是每个调用一次require,我无法实现我想要的。

我研究了告诉 Webpack“不要缓存它,下次再读一遍”的方法,但我没有运气。由于这两个条目进口main.ts其进口main.hbs只有一次,治疗的WebPack既TS和HBS为每个只有1模块,不管事实,他们被导入到多个项文件。我想这是一个必不可少的优化,但对于这种特定情况,我没有找到解决方法。

编写我自己的插件

由于 Loader 没有得到我需要的东西,我尝试编写一个插件。我浏览了详细的文档并尝试连接到compilercompilation但并没有走得太远。我查看了NormalModuleReplacementPlugin源代码NormalModuleFactory以相同的方式使用(文档似乎没有涵盖它)。最终通过更改适当资源的请求以包含每个条目的“变体”,或多或少地重新实现了我之前在插件系统中尝试加载器的功能——正是我需要的。然而,遗憾的是,我遇到了与 Loader 相同的障碍——文件(和我的代码)只被访问一次。

我还尝试“从外到内”——查看结果块,其中每个条目在其树中都有 HBS 文件的模块,但这些模块已经过处理和编译,这似乎不是通往成功的道路。

编写我自己的解析插件

最后,与常规插件类似,我决定尝试使用解析插件。我一直在想,“我只需require要将这些调用解析到其他地方,这应该不难!”

但这并没有奏效,有相同的“每个文件只调用一次钩子”问题,现在我什至无法从所需文件中获得入口点,因为它们甚至还没有得到正确解决。

TL; 博士

因此,如果我能让我的插件或加载程序告诉 Webpack“嘿,这个文件下次我require时会有所不同,所以请再次检查它”,我认为这可以解决所有问题并使一切按照我想要的方式工作。

如果这是不可能的,那么我可能只会恢复到顺序构建:

  1. 建立entry1.js*.hbs解析为*.v1.hbs
  2. 建立entry2.js*.hbs解析为*.v2.hbs

并燃烧时间和内存。但你能做什么呢。

感谢您的阅读。

小智 -1

我也有同样的问题。唯一对我有帮助的想法是Webpack Virtual Modules。我根据某些模板动态生成了多个入口模块实例。因此这些模块具有不同的 ID,并且在条目之间不共享。