如何为库设置TypeScript编译器,以便Webpack将依赖项目中未使用的模块截断?
学科库初步说明
我很抱歉让您阅读本文而耽误您的时间。我写它是为了回答诸如“你在做什么?”之类的问题。和“你为什么要这样做?”。
该库由大量辅助函数和类组成。在这方面它类似于 lodash(检查lodash的结构),但与 lodash 不同的是,源代码是由多级目录组织的。这对开发人员来说很舒服,但对用户来说可能不舒服:要将所需的功能导入到项目中,用户必须知道它在哪里,例如:
import {
computeFirstItemNumberForSpecificPaginationPage
} from "@yamato-daiwa/es-extensions/Number/Pagination";
为了解决这个问题,大部分功能已经导入index.ts并从那里再次导出。现在用户可以获得所需的功能:
import {
computeFirstItemNumberForSpecificPaginationPage
} from "@yamato-daiwa/es-extensions";
请注意index.ts(将由 TypeScript 编译为index.js)中的所有函数均适用于 BrowserJS 和 NodeJS。尤其是针对 BrowserJS 的功能在BrowserJS.ts,尤其是针对 NodeJS 的功能NodeJS.ts(目前几乎是空的,但重新导出方法是相同的)。
此外,在这个问题得到解决之前,我将编译后的 JavaScript 包含到库存储库(Distributable目录)中。
问题
从现在开始,@yamato-daiwa/es-extensions 是图书馆,任何依赖它的项目都是消费项目。
我预计消费项目的所有未使用的函数/类都将被Webpack 优化切断。例如,在下面的情况下,我预计该isUndefined功能只会留在 Webpack 包中:
import { isUndefined } from "@yamato-daiwa/es-extensions"
const test: string | undefined = "ALPHA";
console.log(isUndefined(test));
但实际上,Webpack 离开index.js了库中的所有内容。我美化了 Webpack 构建的缩小版 JavaScript;它像是:
(() => {
"use strict";
var e = {
5272: (e, t) => {
Object.defineProperty(t, "__esModule", {
value: !0
}), t.default = function(e, t) {
for (const [a, n] of e.entries())
if (t(n)) return a;
return null
}
},
7684: (e, t) => {
Object.defineProperty(t, "__esModule", {
value: !0
}), t.default = function(e, t) {
const a = [];
return e.forEach(((e, n) => {
t(e) && a.push(n)
})), a
}
},
// ...
我想每个人都明白这是不可接受的,尤其是对于每千字节计数的浏览器应用程序。
如何解决这个问题呢?理想的解决方案(如果存在)不会触及源文件组织,只需更改 TypeScript 配置即可。
再现
我创建了另一个存储库(repro),您可以在其中尝试上面的示例。
实验流程
- 通过 VCS 获取此存储库
- 像往常一样安装依赖项(
npm i命令)。 - 检查
src/index.ts. 它isUndefined从库中导入函数并使用它。 - 跑
npm run ProductionBuild - 使用 beautifier.io
index.js等工具美化输出。您将看到整个库都已捆绑,而您只需要inUndefined捆绑即可。
关于原因的沉思
第一个候选原因是使用reexportint模式,确切地说是Source/index.ts、Source/BrowserJS.ts和Source/ NodeJS。编译后的index.js样子:
import { isUndefined } from "@yamato-daiwa/es-extensions"
const test: string | undefined = "ALPHA";
console.log(isUndefined(test));
(检查完整文件)
如果从它的单个模块中导入每个函数,import isUndefined from "@yamato-daiwa/es-extensions/TypeGuards/isUndefined"而不是import { isUndefined } from "@yamato-daiwa/es-extensions",则不会输出冗余代码。但正如我已经说过的,这个解决方案是不可接受的,因为图书馆用户必须知道在哪里isUndefined组织了其他功能。
另一个原因可能是输出模块类型。目前它是一个CommonJS. 这是
tsconfig.json图书馆的:
(() => {
"use strict";
var e = {
5272: (e, t) => {
Object.defineProperty(t, "__esModule", {
value: !0
}), t.default = function(e, t) {
for (const [a, n] of e.entries())
if (t(n)) return a;
return null
}
},
7684: (e, t) => {
Object.defineProperty(t, "__esModule", {
value: !0
}), t.default = function(e, t) {
const a = [];
return e.forEach(((e, n) => {
t(e) && a.push(n)
})), a
}
},
// ...
根据假设,根据特定的模块类型,Webpack 可以将代码捆绑到单体结构中,即使这些模块没有被使用,也无法分解和过滤掉一些模块。
现在所有这些(AMD、UMD、CommonJS)都慢慢成为历史的一部分,但我们仍然可以在旧脚本中找到它们。
javascript.info
顺便说一句,消费项目中的 TypeScript 配置也可能会影响(包含在 repro 中)。目前是:
const isStringifiedNonNegativeIntegerOfRegularNotation_1 = require("./Numbers/isStringifiedNonNegativeIntegerOfRegularNotation");
exports.isStringifiedNonNegativeIntegerOfRegularNotation = isStringifiedNonNegativeIntegerOfRegularNotation_1.default;
const separateEach3DigitsGroupWithComma_1 = require("./Numbers/separateEach3DigitsGroupWithComma");
exports.separateEach3DigitsGroupWithComma = separateEach3DigitsGroupWithComma_1.default;