“package.json”不在“rootDir”下

Kou*_*sha 41 javascript typescript

我正在尝试package.json在我的 TypeScript 应用程序中导入:

import packageJson from '../package.json';
Run Code Online (Sandbox Code Playgroud)

我的tsconfig.json包含以下内容:

{
  "compilerOptions": {
    "rootDir": "./src/"
    "outDir": "./dist/",
    "baseUrl": ".",
    "resolveJsonModule": true
  }
}
Run Code Online (Sandbox Code Playgroud)

问题是当我编译这个时,我得到

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具有相同的父..,所以打字稿可能只是独自离开import '../package.json'它的工作无论从rootDiroutDir

无论如何,我尝试了以下方法,但结果并不令人满意:

  • 删除rootDir- 编译有效,但dist将包含dist/src我不想要的
  • 删除outDir- 然后src.js文件污染(.js.map如果sourceMap是真的)
  • add @ts-ignore- 编译停止导入的文件../package.json

有什么办法解决这个限制,以保持生成的文件中dist,并允许从父目录中导入rootDir

vas*_*vas 42

这是可能的,事实证明,并不难

解决方案不明显的原因是因为 typescript 依赖rootDir来决定输出的目录结构(请参阅来自 Typescript 的 bossman 的评论),并且只能导入包含在输出或包依赖项中的代码。

  • 如果您设置rootDir为项目package.json的根目录,outDir则会发送到根目录并可以导入。但是随后您编译的src文件被写入outDir/src.
  • 如果设置rootDirsrc,则其中的文件将编译为outDir. 但是现在编译器没有地方发出package.json,所以它发出“一个错误,因为项目似乎配置错​​误”(老板的话)。

解决方案:使用单独的 Typescript 子项目

一个打字稿项目被规定tsconfig文件,是自包含的,并有效地通过它的边界rootDir。这是一件非常好的事情,因为它符合封装原则

您可以拥有多个项目(例如一个主项目和一组库),每个项目都在它们自己的目录中并具有自己的 tsconfig。它们之间的依赖关系在 tsconfig 文件中使用 Typescript Project References 声明

我承认,术语“项目”是一个糟糕的词,因为它直观地指的是整个shebang,但是在这种情况下已经采用了“模块”和“包”。将它们视为“子项目”,这样会更有意义。

我们将src目录和包含的根目录package.json视为单独的项目。每个人都有自己的tsconfig文件。

  1. 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`
      ]
    }   
    
    Run Code Online (Sandbox Code Playgroud)
  2. 给根目录一个自己的项目。

    ./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, TS won't automatically
        "package.json"   // include all source below root, which is the default.
      ]
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 奔跑tsc --build src 吧!

    这将构建src项目。因为它声明了对根项目的引用,所以它也会构建那个,但前提是它已经过时了。因为根 tsconfig 与 具有相同的目录,所以outDirtsc 不会package.json对它配置编译的一个文件做任何事情。

这对 monorepos 来说很棒

  • 您可以通过将模块/库/子项目放在自己的子目录中并为其提供自己的 tsconfig 来隔离模块/库/子项目。

  • 您可以使用Project References显式管理依赖项,以及模块化构建:

    从链接的文档:

    • 您可以大大缩短构建时间

    期待已久的功能是针对 TypeScript 项目的智能增量构建。在 3.0 中,您可以将--build标志与tsc. 这实际上是一个新的入口点,tsc它的行为更像是一个构建协调器,而不是一个简单的编译器。

    运行tsc --buildtsc -b简称)将执行以下操作:

    • 查找所有引用的项目
    • 检测它们是否是最新的
    • 以正确的顺序构建过时的项目

    不要担心对您在命令行上传递的文件进行排序 -tsc如果需要,将重新排序它们,以便始终首先构建依赖项。

    • 强制组件之间的逻辑分离

    • 以新的更好的方式组织您的代码。

这也很容易:

  • src/tsconfig.json

    即使您在根目录中没有代码,此 tsconfig 也可以是所有通用设置的所在(其他设置将从它继承),并且它可以轻松tsc --build src构建整个项目(并--force从头开始构建它)。

    {
      "compilerOptions": {
        "rootDir": ".",
        "outDir": "../build/",
        "resolveJsonModule": true,
        "composite": true
      },
      // this root project has no source of its own
      "files": [],
      // but building this project will build all of the following:
      "references": [
        { "path": "./common" }
        { "path": "./projectA" }
        // include all other sub-projects here  
      ]
    }
    
    Run Code Online (Sandbox Code Playgroud)
    • src/common/tsconfig.json

      因为common没有引用,所以导入仅限于其目录和npm_modules. 我相信,您甚至可以通过给它自己的package.json.

          {
           "compilerOptions": {
              "rootDir": ".",
              "outDir": "../../build/common",
              "resolveJsonModule": true,
              "composite": true
            }
          }
      
      Run Code Online (Sandbox Code Playgroud)
    • src/projectA/tsconfig.json

      由于声明的引用,projectA可以导入common

          {
            "compilerOptions": {
              "rootDir": ".",
              "outDir": "../../build/libA",
              "resolveJsonModule": true,
              "composite": true
            },
            "references": [
              { "path": "../common" }
            ]
          }
      
      Run Code Online (Sandbox Code Playgroud)

  • 非常感谢你。我什至在寻找暗示这些概念的文档时都遇到了问题。你把事情说清楚了,做得很好。 (3认同)
  • 这是一个非常有用且清晰的答案 - 它应该包含在 Typescript 文档中,而 Typescript 文档并不像这样清晰。 (2认同)
  • 首先,感谢您的精彩回答!其次,我尝试使用具有服务器、客户端和公共“子项目”的 TypeScript 项目来遵循您的 monorepo 示例(目前我只使用公共项目和服务器)。我的设置与您的示例非常相似(尽管每个项目都有自己的 src 文件夹),但是当我运行 `tsc --build client/tsconfig.json` 时,我收到如下错误:“无法写入文件 '/build/tsconfig .tsbuildinfo',因为它将覆盖引用项目'/common'生成的'.tsbuildinfo'文件。” 有什么线索吗? (2认同)

Chr*_*ras 17

我们可以设置resolveJsonModule为 false 并*.json在内部声明一个模块typings.d.ts,该模块需要 JSON 文件作为模块,并且它将生成dist目录内没有任何目录结构的文件。

Monorepo目录结构

monorepo\
?? app\
?  ?? src\
?  ?  ?? index.ts
?  ?? package.json
?  ?? tsconfig.json
?  ?? typings.d.ts
?? lib\
   ?? package.json
Run Code Online (Sandbox Code Playgroud)

app/typings.d.ts

monorepo\
?? app\
?  ?? src\
?  ?  ?? index.ts
?  ?? package.json
?  ?? tsconfig.json
?  ?? typings.d.ts
?? lib\
   ?? package.json
Run Code Online (Sandbox Code Playgroud)

app/src/index.ts

declare module "*.json";
Run Code Online (Sandbox Code Playgroud)

app/package.json内容

// 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();
Run Code Online (Sandbox Code Playgroud)

lib/package.json内容

{
  "name": "my-app",
  "version": "0.0.1",
  ...
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我们使用 编译项目tsc,我们将得到以下dist目录结构:

app\
?? dist\
   ?? index.d.ts
   ?? index.js
Run Code Online (Sandbox Code Playgroud)

如果我们使用 运行它,我们将从和信息node ./dist中获得输出:applib package.json

$ node ./dist
App name "my-app" with version 0.0.1
Lib name "my-lib" with version 1.0.1
Run Code Online (Sandbox Code Playgroud)

您可以在这里找到项目存储库:https ://github.com/clytras/typescript-monorepo


ide*_*ide 10

对于使用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"
  }
}
Run Code Online (Sandbox Code Playgroud)

在tsconfig.json中启用对 ES 模块和 JSON 导入的TypeScript 支持:

{
  "compilerOptions": {
    "module": "nodenext",
    // "moduleResolution" defaults to "nodenext", just made explicit here
    "moduleResolution": "nodenext",
    "resolveJsonModule": true,
  }
}
Run Code Online (Sandbox Code Playgroud)

最后,从 TypeScript 模块导入:#package.json

import packageJson from '#package.json' assert { type: 'json' };

console.log(packageJson);
Run Code Online (Sandbox Code Playgroud)


小智 1

这取决于您阅读“package.json”的方式和时间。您可以在运行时使用 NodeJS“fs”模块将其读取为文件,或者只需输入 const package = require(“package.json”)。

在第二种情况下,Typescript 将在编译时在根目录中搜索它(请参阅Typescript 模块解析文档)。

您还可以使用“rootDirs”属性而不是“rootDir”来指定根文件夹数组。

  • const package = require("../package.json") 为我解决了这个问题,谢谢! (2认同)