“package.json”不在“rootDir”下

IT技术 javascript typescript
2021-01-26 09:22:08

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

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文件污染.js.map如果sourceMap是真的)
  • add @ts-ignore- 编译停止导入的文件../package.json

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

6个回答

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

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

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

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

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

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

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

我们将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 与 具有相同的目录,所以outDirtsc 不会package.json对它配置编译的一个文件做任何事情

这对 monorepos 来说很棒

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

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

    从链接的文档:

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

    期待已久的功能是针对 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  
      ]
    }
    
    • 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" }
            ]
          }
      
非常感谢你。我什至在寻找暗示这些概念的文档时也遇到了问题。你在把事情弄清楚方面做得很好。
2021-03-25 09:22:08
@DanDascalescu 我的荣幸。无论如何,我需要自己弄清楚这些东西。您当然比我更了解您的需求,但至于“更简单”……请考虑tsc --build您会错过的优惠。请参阅此答案的底部(顶部大致相同,但 tsconfigs 是正确的......我错过了上面的一些细节,我将为未来的读者修复)。
2021-03-26 09:22:08
@DanDascalescu 如果我回答了您关于 monorepos 的问题,请告诉我。
2021-03-27 09:22:08
感谢您的出色回答!另一个对我的用例来说稍微简单一些,所以我授予了赏金,但感谢你提醒我关于带有子树monorepo 粒度访问解决方案- 接受了!
2021-04-02 09:22:08
这是一个超级有用且清晰的答案 - 它应该包含在 Typescript 文档中,但没有这么清楚。
2021-04-14 09:22:08

我们可以设置resolveJsonModule为 false 并为*.jsoninside声明一个moduletypings.d.ts,它将需要 JSON 文件作为module,它将生成目录中没有任何目录结构的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

可能来自WebStorm
2021-03-21 09:22:08
赞成。我的和你的之间,我们有两个合法的解决方案:)
2021-03-26 09:22:08
最简单的答案,所以我会给它赏金。谢谢!我看到的唯一非常小的问题是valuein typings.d.ts:的警告A default export can only be used in an ECMAScript-style module
2021-03-27 09:22:08
@DanDascalescu 感谢您的赏金。当您尝试导入module或运行 using 时,您在哪里收到警告ts-node如果您可以将导致该警告的示例公诸于存储库,这将非常有帮助,以便我可以进一步调查它。
2021-04-03 09:22:08
是的,可能,我在使用TS 3.8.3. 我会检查一下,看看它是否与 WebStorm 或 WS 使用的 TS 版本有关。
2021-04-11 09:22:08

目前是不可能的。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属性,但是当您调用tsccommand 编译项目时,输出将如下所示:

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文件夹结构一起正常工作

导入“外部”module时出现“问题”

import packageJson from '../package.json';

那么,“后退”动作会发生什么 - '../'?如果你希望你的输出结构是:

build/
  package.json
  index.js

那么,它们如何与const packageJson = __importDefault(require("../package.json"));. 然后typescript编译器尝试保持项目结构:

build/
  package.json
  src/
    index.js

对于 monorepo 项目,我认为您需要为每个库创建声明文件,然后references在 tsconfig 文件中使用设置。前任:

  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.tsimport ../package.json,TypeScript 可以简单地保持相同的路径,不做任何更改,并dest/index.js使用相同的import '../package.json'. (我使用esnextmodule,但这并不重要。)。双方srcdest具有相同的父,所以..将解析到同一目录中。
2021-03-17 09:22:08
2021-03-27 09:22:08

通话// @ts-ignore之上使用import并进行设置时,"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. 但是您可以忽略这些警告。

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

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

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

const package = require("../package.json") 为我解决了这个问题,谢谢!
2021-03-15 09:22:08