为什么 tsc 不根据其运行位置输出 json 文件?



情况:您有一个typescript项目,它被配置为还输出JSON文件。您拥有正确的tsconfig.json设置和正确的依赖关系。你也读过这个相关的问答;并确保您的typescript文件导入json文件。然而,当您运行tsc时,您会注意到json文件丢失了。

然后将项目复制到一个单独的位置,使用完全相同的文件布局、完全相同的二进制文件,并运行tsc。现在,生成json文件。WTFNODE?

这是一个最小的repo,使用节点v16.13.2和纱线v1.22.7

参考项目

假设您已准备好安装nodejsyarn。如果使用Nix,则可以通过nix-shell -p yarn nodejs实现。然后:

pwd
# /tmp/foolib
yarn init -y  # blank project
yarn add typescript  # this is all we need for the repro
export PATH=$(yarn bin):$PATH  # make sure we see `tsc` in the path

其余文件:

src/index.ts

import Thing from './moveme.json'
console.log("Hello")

src/moveme.json

{ "foo": "bar" }

tsconfig.json

{
"compilerOptions": {
"module": "commonjs",
"target": "ES2015",
"declarationMap": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"outDir": "./dist",
"skipLibCheck": true,
"declaration": true,
"jsx": "react"
},
"include": [
"src/**/*"
]
}

如果运行rm -rf dist;export PATH=$(yarn bin):$PATH; tsc; find dist,应该会看到以下输出:

dist
dist/index.d.ts
dist/index.d.ts.map
dist/moveme.json
dist/index.js

另一个项目

假设您正在将项目作为库导入其他地方。让我们用这种方式创建一个假的:

mkdir /tmp/fooproject
cd /tmp/fooproject
yarn init -y  # this creates package.json, yarn.lock, node_modules
cp -R /tmp/foolib ./node_modules/  # create a local copy
cd node_modules/foolib
tsc

现在,find dist的输出

dist
dist/index.d.ts
dist/index.d.ts.map
dist/index.js

为什么没有moveme.json

tl;dr

当您从真实路径(解析符号链接后)包含node_modules目录的位置运行tsc时,json文件将不会被复制。

快速修复

根据tsc的工作方式,在node_modules中的某个目录之外运行tsc可能被认为是一种糟糕的做法。但是,如果在某个时候你决定这样做,最快的方法是在不同的位置编译它,node_modules不在路径层次结构中(你甚至可以重命名node_modules并将其重新命名)。

解释

当您调用tsc时,它会启动一组异步Worker,解析源文件并计算代码依赖树。代码中引用的模块(如上例中导入的JSON文件)在这里被发现,然后由这里的工作逻辑解析。

因此,在我们的示例中,processImportedModules()将发现index.ts导入一个名为moveme.json模块。是否将此文件作为输出文件包括在内的决定取决于以下行:

const isFromNodeModulesSearch = resolution.isExternalLibraryImport;

因为它没有得到输出,我们知道tsc认为moveme.json是一个";外部库导入";。该确定由tryResolve()函数进行,特别是该行

return resolved && toSearchResult({ resolved, isExternalLibraryImport: contains(parts, "node_modules") });

最后告诉我们;模块";其路径中某处包含CCD_;"外部库导入";,并且不会被复制到输出文件夹中。

在跟踪源之后,这种行为在逻辑上是有意义的,但非常令人惊讶,因为看似孤立的项目的输出实际上取决于一直到文件系统根的结构。在没有这些知识的情况下,tsc的行为是不一致的;给定:

  • inputs0 --{tsc}--> outputs1来自/tmp/foolib
  • inputs0 --{tsc}--> outputs2来自/tmp/fooproject/node_modules/foolib

此处,outputs1 != outputs2

有了这些知识,tsc的行为是依赖于工作目录的

最新更新