我正在尝试在我的 TypeScript 应用程序中导入package.json
:
import packageJson from '../package.json';
我的tsconfig.json
包含以下内容:
{
"compilerOptions": {
"rootDir": "./src/"
"outDir": "./dist/",
"baseUrl": ".",
"resolveJsonModule": true
}
}
问题是当我编译这个时,我得到
error TS6059: File '/path/to/package.json' is not under 'rootDir' '/path/to/app/src/'. 'rootDir' is expected to contain all source files.
我不确定我是否理解这个问题,因为./src/
和/.dist
都有相同的父..
,所以 TypeScript 可以不理会import '../package.json'
,它可以从rootDir
或outDir
工作。
无论如何,我尝试了以下方法,结果不令人满意:
- 删除
rootDir
- 编译有效,但dist
将包含我不想要的dist/src
- 删除
outDir
- 然后src
被.js
文件污染(如果sourceMap
是真的,.js.map
) - 添加
@ts-ignore
- 编译将停止导入../package.json
的文件
此限制的解决方法是什么,将生成的文件保留在dist
中,并允许从rootDir
的父目录导入?
这是可能的,事实证明,并不难。
解决方案不明显的原因是 Typescript 依赖于rootDir
来决定输出的目录结构(参见 Typescript 老板的这条评论),并且只能导入输出或包依赖项中包含的代码。
- 如果将
rootDir
设置为项目的根目录,则package.json
将发出到outDir
根目录,并且可以导入。但是,您编译的src
文件将写入outDir/src
。 - 如果将
rootDir
设置为src
,则其中的文件将编译为outDir
的根目录。 但是现在编译器没有地方发出package.json
,所以它发出"一个错误,因为项目似乎配置错误"(老板的话)。
解决方案:使用单独的 Typescript 子项目
每个 Typescript项目都是自包含的,由自己的tsconfig
定义,具有自己的rootDir
。这与封装原理一致。
您可以有多个项目(例如,一个主库和一组库),每个项目都有自己的rootDir
并具有自己的 tsconfig。 它们之间的依赖关系是使用 Typescript 项目引用在依赖项的tsconfig
中声明的。
我承认,"项目"这个词很糟糕,因为直觉上它指的是整个 shebang,但"模块"和"包"已经被采用。将它们视为子项目,这将更有意义。
为了解决您的特定问题,我们将src
目录和包含package.json
的根目录视为单独的项目。每个人都有自己的tsconfig
。
-
为
src
目录提供自己的项目。./src/tsconfig.json
:{ "compilerOptions": { "rootDir": ".", "outDir": "../dist/", "resolveJsonModule": true }, "references": [ // this is how we declare a dependency from { "path": "../" } // this project to the one at the root dir` ] }
-
为根目录提供自己的项目。
./tsconfig.json
:{ "compilerOptions": { "rootDir": ".", "outDir": ".", // if out path for a file is same as its src path, nothing will be emitted "resolveJsonModule": true, "composite": true // required on the dependency project for references to work }, "files": [ // by whitelisting the files to include, you avoid the default TS behavior, which "package.json" // will include everything, resulting in `src` being included in both projects (bad) ] }
-
跑
tsc --build src
,瞧!这将生成
src
项目。因为它声明了对根项目的引用,所以它也会生成该根项目,但前提是它已过期。因为根 tsconfig 与outDir
具有相同的目录,tsc 不会简单地package.json
它配置编译的一个文件。
这非常适合单存储库
-
您可以通过将模块/库/子项目放在它们自己的子目录中并赋予它们自己的 tsconfig 来隔离它们。
-
您可以使用项目引用显式管理依赖项,以及模块化构建:
从链接的文档:
-
您可以大大缩短构建时间
期待已久的功能是 TypeScript 项目的智能增量构建。在 3.0 中,您可以将
--build
标志与tsc
一起使用。这实际上是tsc
的新入口点,其行为更像是生成业务流程协调程序,而不是简单的编译器。运行
tsc --build
(简称tsc -b
)将执行以下操作:- 查找所有引用的项目
- 检测它们是否为最新
- 以正确的顺序构建过时的项目
不用担心对在命令行上传递的文件进行排序 - 如果需要,
tsc
会重新排序它们,以便始终首先构建依赖项。 -
在组件之间强制实施逻辑分离
-
以新的和更好的方式组织您的代码。
-
这也很容易:
-
共享选项和构建所有选项的根
tsconfig
具有简单tsc --build
命令的子项目 (--force
从头开始构建它们)src/tsconfig.json
{ "compilerOptions": { "outDir": ".", // prevents this tsconfig from compiling any files // we want subprojects to inherit these options: "target": "ES2019", "module": "es2020", "strict": true, ... }, // configure this project to build all of the following: "references": [ { "path": "./common" } { "path": "./projectA" } ] }
-
阻止从 其他子项目,因为它没有项目引用
src/common/tsconfig.json
{ "extends": "../tsconfig.json", //inherit from root tsconfig // but override these: "compilerOptions": { "rootDir": ".", "outDir": "../../build/common", "resolveJsonModule": true, "composite": true } }
-
由于声明的引用而可以导入common的子项目。
src/projectA/tsconfig.json
{ "extends": "../tsconfig.json", //inherit from root tsconfig // but override these: "compilerOptions": { "rootDir": ".", "outDir": "../../build/libA", "resolveJsonModule": true, "composite": true }, "references": [ { "path": "../common" } ] }
我们可以将resolveJsonModule
设置为 false 并在typings.d.ts
中声明一个用于*.json
的模块,这将需要 JSON 文件作为模块,它将生成在dist
目录中没有任何目录结构的文件。
单存储库目录结构
monorepo
├─ app
│ ├─ src
│ │ └─ index.ts
│ ├─ package.json
│ ├─ tsconfig.json
│ └─ typings.d.ts
└─ lib
└─ package.json
app/typings.d.ts
declare module "*.json";
app/src/index.ts
// Import from app/package.json
import appPackageJson from '../package.json';
// Import from lib/package.json
import libPackageJson from '../../lib/package.json';
export function run(): void {
console.log(`App name "${appPackageJson.name}" with version ${appPackageJson.version}`);
console.log(`Lib name "${libPackageJson.name}" with version ${libPackageJson.version}`);
}
run();
app/package.json
内容
{
"name": "my-app",
"version": "0.0.1",
...
}
lib/package.json
内容
{
"name": "my-lib",
"version": "1.0.1",
...
}
现在如果我们使用tsc
编译项目,我们将得到以下dist
目录结构:
app
└─ dist
├─ index.d.ts
└─ index.js
如果我们使用node ./dist
运行它,我们将从app
和lib
package.json
信息中获得输出:
$ node ./dist
App name "my-app" with version 0.0.1
Lib name "my-lib" with version 1.0.1
您可以在此处找到项目存储库:https://github.com/clytras/typescript-monorepo
对于使用 ES 模块而不是 CommonJS 的软件包,有一个整洁的三步解决方案,包括 Node 16+ LTS 和 TypeScript 4.7+。
package.json中的"imports"
字段定义了只能从实际包中导入的内部伪包。定义内部导入说明符,例如package.json中的#package.json
:
{
"type": "module",
"exports": "./dist/index.js",
"imports": {
"#package.json": "./package.json"
}
}
在tsconfig.json中为 ES 模块和 JSON 导入启用 TypeScript 支持:
{
"compilerOptions": {
"module": "nodenext",
// "moduleResolution" defaults to "nodenext", just made explicit here
"moduleResolution": "nodenext",
"resolveJsonModule": true,
}
}
最后,从 TypeScript 模块导入#package.json
:
import packageJson from '#package.json' assert { type: 'json' };
console.log(packageJson);
目前不可能。Typescript 编译器尝试保留您的目录结构。
例如,您的项目如下所示:
src/
shared/
index.ts
index.ts
package.json
tsconfig.json
您的tsconfig.json
包含:
{
"compilerOptions": {
"outDir": "./build",
"module": "commonjs",
"target": "es6",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true
},
"include": [
"src/**/*"
]
}
如您所见,该文件不包含rootDir
属性,但是当您调用tsc
命令来编译项目时,输出将如下所示:
build/
shared/
index.js
index.js
输出不包含src
文件夹,因为在我的代码中,我只是在文件夹中导入和使用src
,例如:
src/index.ts
import someName from './shared';
然后,build/index.js
将如下所示:
...
const shared_1 = __importDefault(require("./shared"));
...
如您所见 -require("./shared")
,这意味着它可以在文件夹结构build
正常工作。
导入"外部"模块时出现"问题">
import packageJson from '../package.json';
那么,"后退"行动会发生什么 - '../'?如果你希望你的输出结构是:
build/
package.json
index.js
然后,他们如何与const packageJson = __importDefault(require("../package.json"));
一起工作.然后 Typescript 编译器尝试保持项目结构:
build/
package.json
src/
index.js
对于 monorepo 项目,我认为您需要为每个库创建声明文件,结束然后在 tsconfig 文件中使用references
设置。前任:
- 在
./lib01
文件夹中,库导入./lib02
其代码中。Tsconfig 文件将如下所示:
{
"compilerOptions": {
"declarationDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*"],
"references": [ // here
{
"path": "../lib02"
}
]
}
- lib02 的
tsconfig.json
{
"compilerOptions": {
"declarationDir": "dist",
"rootDir": "src",
"composite": true // importance.
}
}
这取决于您阅读"package.json"的方式和时间。您可以在运行时使用 NodeJS "fs" 模块将其读取为文件,或者只需键入 const package = require("package.json")。
在第两种情况下,Typescript 将在编译时在根目录中搜索它(请参阅 Typescript 模块解析文档)。
您还可以使用"rootDirs"属性而不是"rootDir"来指定根文件夹数组。
在import
调用之上使用// @ts-ignore
并设置"rootDir": "./src"
它工作时。在这种情况下,启用resolveJsonModule
仍然有效,但仅适用于./src
下的文件。请参阅:https://github.com/MatrixAI/TypeScript-Demo-Lib/pull/33 了解如何将其应用于我们的模板存储库。这样就可以像往常一样从./src
内部导入 json 文件,但是当您导入../package.json
时,您必须使用// @ts-ignore
来确保 TSC 忽略它。这是一个一次性的特例,所以这是有效的。
这一切起作用的原因是因为 设置 https://www.typescriptlang.org/tsconfig#rootDir 将强制 TSC 不推断项目根目录为 SRC。因此将强制执行预期的dist
结构,同时在导入rootDir
外时引发警告/错误。但您可以忽略这些警告。
我通过使用符号链接来解决这个问题: 在窗口中:
cd src
mklink package.json ..package.json
或在 Linux 中:
cd src
ln -s package.json ../package.json