【问题标题】:'package.json' is not under 'rootDir'“package.json”不在“rootDir”下
【发布时间】:2019-09-09 05:05:39
【问题描述】:

我正在尝试在我的 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'它会工作 来自rootDiroutDir

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

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

这个限制的解决方法是什么,将生成的文件保存在dist,并允许从rootDir的父目录导入?

【问题讨论】:

  • // @ts-ignore 有帮助吗?
  • 我遇到了同样的问题。添加// @ts-ignore 没有帮助。
  • 给定的import 语句与给定的tsconfig 不一致。产生错误。请参阅我建议的编辑。
  • 您是否尝试设置baseUrl 而不是rootDir
  • 我发现如果你只使用require 而不是import,这可以工作。当然,这假设您在节点平台上。并且在分发包时,您不是分发 dist,而是分发包含 package.json 的整个目录。

标签: javascript typescript


【解决方案1】:

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

解决方案不明显的原因是因为typescript依赖rootDir来决定输出的目录结构(见this comment from Typescript's bossman),并且只有代码包含在输出中或可以导入包依赖。

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

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

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`
      ]
    }   
    
  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.
      ]
    }
    
  3. 运行tsc --build src,然后瞧!

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

这对 monorepos 来说很棒

  • 您可以隔离模块/库/子项目,方法是将它们放在自己的子目录中并为它们提供自己的 tsconfig。

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

    来自链接的文档:

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

    期待已久的功能是 TypeScript 项目的智能增量构建。在 3.0 中,您可以将 --buildflag 与 tsc 一起使用。这实际上是 tsc 的新入口点,其行为更像是构建协调器,而不是简单的编译器。

    运行tsc --build(简称tsc -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  
      ]
    }
    
    • src/common/tsconfig.json

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

          {
           "compilerOptions": {
              "rootDir": ".",
              "outDir": "../../build/common",
              "resolveJsonModule": true,
              "composite": true
            }
          }
      
    • src/projectA/tsconfig.json

      projectA 可以导入 common 因为声明的引用。

          {
            "compilerOptions": {
              "rootDir": ".",
              "outDir": "../../build/libA",
              "resolveJsonModule": true,
              "composite": true
            },
            "references": [
              { "path": "../common" }
            ]
          }
      

【讨论】:

  • @DanDascalescu 如果我回答了你关于 monorepos 的问题,请告诉我。
  • 感谢您的出色回答!另一个对我的用例来说稍微简单一些,所以我奖励了赏金,但感谢你提醒我monorepo granular access solution with subtrees - 接受!
  • @DanDascalescu 我很高兴。无论如何,我需要自己弄清楚这些东西。您当然比我更了解您的需求,但至于“更简单”……请考虑 tsc --build 提供的您可能会错过的优势。请参阅the bottom of this answer(顶部大致相同,虽然 tsconfig 是正确的......我错过了上面的一些细节,我会为未来的读者修复)。
  • 这是一个超级有用且清晰的答案 - 它应该包含在 Typescript 文档中,但没有这么清晰。
  • 非常感谢您。即使找到暗示这些概念的文档,我也遇到了问题。你把事情说清楚了。
【解决方案2】:

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

Monorepo 目录结构

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 运行它,我们将从applib 得到输出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

【讨论】:

  • 赞成。在mine 和你的之间,我们有两个合法的解决方案:)
  • 最简单的答案,所以我会奖励它。谢谢!我看到的唯一非常小的问题是valuetypings.d.ts 中的警告:A default export can only be used in an ECMAScript-style module
  • @DanDascalescu 感谢您的赏金。当您尝试导入模块时,或者使用ts-node 运行时,您在哪里收到警告?如果您可以 PR 一个导致该警告的示例到存储库,这将非常有帮助,以便我可以进一步调查它。
  • 可能来自WebStorm
  • 是的,它可能,我没有在使用TS 3.8.3 的最新 VSCode 中看到该警告。我会检查一下,看看它是否与 WebStorm 或 WS 正在使用的 TS 版本有关。
【解决方案3】:

暂时不可能。 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 设置。例如:

  1. ./lib01 文件夹中,lib 在其代码中导入./lib02。 Tsconfig 文件会是这样的:
{
  "compilerOptions": {
    "declarationDir": "dist",
    "rootDir": "src"
  },
  "include": ["src/**/*"],
  "references": [ // here
    {
      "path": "../lib02"
    }
  ]
}
  1. lib02 的tsconfig.json
 {
   "compilerOptions": {
    "declarationDir": "dist",
    "rootDir": "src",
    "composite": true // importance.
  }
 }

【讨论】:

  • 我在想,既然src/index.ts 导入../package.json,TypeScript 可以简单地保持相同的路径,不做任何更改,并生成具有相同import '../package.json'dest/index.js。 (我使用esnext 模块,但这没关系。)。 srcdest 都具有相同的父级,因此 .. 将解析到同一个目录。
【解决方案4】:

当在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 之外导入时抛出警告/错误。但您可以忽略这些警告。

【讨论】:

    【解决方案5】:

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

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

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

    【讨论】:

    • const package = require("../package.json") 为我解决了这个问题,谢谢!
    【解决方案6】:

    我通过使用符号链接解决了这个问题: 在窗口中:

    cd src
    mklink package.json ..\package.json
    

    或者在linux中:

    cd src
    ln -s package.json ../package.json
    

    【讨论】:

      猜你喜欢
      • 2022-10-02
      • 1970-01-01
      • 2019-12-16
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 2022-07-15
      • 2018-06-19
      • 2021-09-16
      相关资源
      最近更新 更多