简体   繁体   English

“package.json”不在“rootDir”下

[英]'package.json' is not under 'rootDir'

I'm trying to import package.json in my TypeScript application:我正在尝试在我的 TypeScript 应用程序中导入package.json

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

My tsconfig.json contains the following:我的tsconfig.json包含以下内容:

{
  "compilerOptions": {
    "rootDir": "./src/"
    "outDir": "./dist/",
    "baseUrl": ".",
    "resolveJsonModule": true
  }
}

The problem is that when I compile this, I get问题是当我编译这个时,我得到

error TS6059: File '/path/to/package.json' is not under 'rootDir' '/path/to/app/src/'. 'rootDir' is expected to contain all source files.

I'm not sure I understand the issue, because both ./src/ and /.dist have the same parent .. , so TypeScript could just leave alone the import '../package.json' and it would work from either rootDir or outDir .我不确定我是否理解这个问题,因为./src//.dist都有相同的父级.. ,所以 TypeScript 可以只保留import '../package.json'并且它可以从任rootDir工作或outDir

Anyway, I've tried the following, with unsatisfactory results:无论如何,我尝试了以下方法,但结果并不令人满意:

  • remove rootDir - compilation works, but the dist will contain dist/src , which I don't want删除rootDir - 编译工作,但dist将包含dist/src ,我不想要
  • remove outDir - then src gets polluted with .js files (and .js.map if sourceMap was true)删除outDir - 然后src.js文件污染(如果sourceMap .js.map
  • add @ts-ignore - compilation stops the the file that imports ../package.json添加@ts-ignore - 编译停止导入../package.json的文件

What's the workaround for this limitation, to keep generated files in dist , and allow importing from the parent directory of rootDir ?这个限制的解决方法是什么,将生成的文件保存在dist中,并允许从rootDir的父目录导入?

This is possible , and it turns out, not hard .这是可能的,而且事实证明并不难

The reason the solution is not obvious is because typescript relies on the rootDir to decide the directory structure of the output (see this comment from Typescript's bossman ), and only code included in the output or in package dependencies can be imported.解决方案不明显的原因是因为 typescript 依赖rootDir来决定输出的目录结构(参见Typescript 的 bossman 的这个评论),并且只能导入包含在输出中或包依赖项中的代码。

  • If you set rootDir to the root of your project, package.json gets emitted to the root of outDir and can be imported.如果将rootDir设置为项目的根目录,则package.json会发送到outDir的根目录并可以导入。 But then your compiled src files get written to outDir/src .但是随后您编译的src文件被写入outDir/src
  • If you set rootDir to src , files in there will compile to the root of outDir .如果将rootDir设置为src ,其中的文件将编译到outDir的根目录。 But now the compiler won't have a place to emit package.json , so it issues "an error because the project appears to be misconfigured" (bossman's words).但是现在编译器没有地方发出package.json ,所以它发出“一个错误,因为项目似乎配置错​​误”(老板的话)。

solution: use separate Typescript sub-projects解决方案:使用单独的 Typescript 子项目

A Typescript project is defined by a tsconfig file, is self-contained, and is effectively bounded by its rootDir . Typescript 项目tsconfig文件定义,是自包含的,并且有效地受其rootDir This is a very good thing, as it lines up with principles of encapsulation .这是一件非常好的事情,因为它符合封装的原则

You can have multiple projects (eg a main and a set of libs) each in their own directory and with their own tsconfig.您可以有多个项目(例如,一个主项目和一组库),每个项目都在它们自己的目录中并使用它们自己的 tsconfig。 Dependencies between them are declared in the tsconfig file using Typescript Project References .它们之间的依赖关系是在 tsconfig 文件中使用 Typescript Project References声明的。

I admit, the term "projects" is a poor one, as intuitively it refers to the whole shebang, but "modules" and "packages" are already taken in this context.我承认,“项目”这个词很糟糕,因为直观上它指的是整个 shebang,但是“模块”和“包”已经在这种情况下出现了。 Think of them as "subprojects" and it will make more sense.将它们视为“子项目”,它会更有意义。

We'll treat the src directory and the root directory containing package.json as separate projects.我们将src目录和包含package.json的根目录视为单独的项目。 Each will have its own tsconfig file.每个都有自己的tsconfig文件。

  1. Give the src dir its own project.src目录它自己的项目。

    ./src/tsconfig.json : ./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. Give the root dir its own project.给根目录它自己的项目。

    ./tsconfig.json : ./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. run tsc --build src and voilà!运行tsc --build src和瞧!

    This will build the src project.这将构建src项目。 Because it declares a reference to the root project, it will build that one also, but only if it is out of date.因为它声明了对根项目的引用,所以它也会构建那个,但前提是它已经过时了。 Because the root tsconfig has the same dir as the outDir , tsc will simply do nothing to package.json , the one file it is configured to compile.因为根 tsconfig 与outDir具有相同的目录,所以 tsc 将简单地对package.json (它配置为编译的一个文件)执行任何操作。

this is great for monorepos这对 monorepos 来说很棒

  • You can isolate modules/libraries/sub-projects by putting them in their own subdirectory and giving them their own tsconfig.您可以通过将模块/库/子项目放在它们自己的子目录中并给它们自己的 tsconfig 来隔离它们。

  • You can manage dependencies explicitly using Project References , as well as modularize the build:您可以使用Project References显式管理依赖项,以及模块化构建:

    From the linked doc:从链接的文档:

    • you can greatly improve build times您可以大大缩短构建时间

      A long-awaited feature is smart incremental builds for TypeScript projects.期待已久的功能是 TypeScript 项目的智能增量构建。 In 3.0 you can use the --build flag with tsc .在 3.0 中,您可以将--build标志与tsc一起使用。 This is effectively a new entry point for tsc that behaves more like a build orchestrator than a simple compiler.这实际上是tsc的一个新入口点,它的行为更像是一个构建协调器,而不是一个简单的编译器。

      Running tsc --build ( tsc -b for short) will do the following:运行tsc --build (简称tsc -b )将执行以下操作:

      • Find all referenced projects查找所有引用的项目
      • Detect if they are up-to-date检测它们是否是最新的
      • Build out-of-date projects in the correct order以正确的顺序构建过时的项目

      Don't worry about ordering the files you pass on the commandline - tsc will re-order them if needed so that dependencies are always built first.不必担心对在命令行中传递的文件进行排序 - 如果需要, tsc会重新排序它们,以便始终首先构建依赖项。

    • enforce logical separation between components强制组件之间的逻辑分离

    • organize your code in new and better ways.以新的更好的方式组织您的代码。

It's also very easy:这也很容易:

  1. A root tsconfig for shared options and to build all subprojects with a simple tsc --build command (with --force to build them from scratch)用于共享选项的根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" } ] }
  2. A "common" library that is prevented from importing from the other subprojects because it has no project references由于没有项目引用而无法从其他子项目导入的“通用”库

    src/common/tsconfig.json

     { "extends": "../tsconfig.json", //inherit from root tsconfig // but override these: "compilerOptions": { "rootDir": ".", "outDir": "../../build/common", "resolveJsonModule": true, "composite": true } }
  3. A subproject that can import common because of the declared reference.由于声明的引用,可以导入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" } ] }

We can set resolveJsonModule to false and declare a module for *.json inside typings.d.ts which will require JSON files as modules and it will generate files without any directory structure inside the dist directory.我们可以将resolveJsonModule设置为 false 并在 typings.d.ts 中为*.json typings.d.ts声明一个模块,这将需要 JSON 文件作为模块,它将在dist目录中生成没有任何目录结构的文件。

Monorepo directory structure 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 contents app/package.json内容

{
  "name": "my-app",
  "version": "0.0.1",
  ...
}

lib/package.json contents lib/package.json内容

{
  "name": "my-lib",
  "version": "1.0.1",
  ...
}

Now if we compile the project using tsc , we'll get the following dist directory structure:现在,如果我们使用tsc编译项目,我们将得到以下dist目录结构:

app\
└─ dist\
   ├─ index.d.ts
   └─ index.js

And if we run it using node ./dist , we'll get the output from both app and lib package.json information:如果我们使用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

You can find the project repository here: https://github.com/clytras/typescript-monorepo您可以在此处找到项目存储库: https ://github.com/clytras/typescript-monorepo

It is not possible for now.目前是不可能的。 Typescript compiler try to keep your directory structure. Typescript 编译器尝试保持你的目录结构。

For example, your project look like:例如,您的项目如下所示:

src/
  shared/
    index.ts
  index.ts
package.json
tsconfig.json

Your tsconfig.json contains:您的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/**/*"
  ]
}

As you see, the file does not include rootDir property, but when you call tsc command to compile the project, the output will look like:如您所见,该文件不包含rootDir属性,但是当您调用tsc命令编译项目时,输出将如下所示:

build/
  shared/
    index.js
  index.js

The output does not contain src folder, because in my code, I just import and use inside src folder, like:输出不包含src文件夹,因为在我的代码中,我只是在src文件夹中导入和使用,例如:

src/index.ts

import someName from './shared';

then, build/index.js will look like:然后, build/index.js将如下所示:

...
const shared_1 = __importDefault(require("./shared"));
...

as you see - require("./shared") , this mean it working fine with build folder structure.如您所见 - require("./shared") ,这意味着它可以在build文件夹结构中正常工作。

Your "issue" appeared when you import a "outside" module导入“外部”模块时出现“问题”

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

So, what happen with "back" action - '../'?那么,“后退”动作 - '../' 会发生什么? If you hope your output structure will be:如果你希望你的输出结构是:

build/
  package.json
  index.js

then, how do they work with const packageJson = __importDefault(require("../package.json"));那么,它们如何与const packageJson = __importDefault(require("../package.json")); . . Then Typescript compiler try to keep project structure:然后 Typescript 编译器尝试保持项目结构:

build/
  package.json
  src/
    index.js

With a monorepo project, I think you need to create declaration files for each library, end then use references setting in the tsconfig file.对于 monorepo 项目,我认为您需要为每个库创建声明文件,然后使用 tsconfig 文件中的references设置。 Ex:前任:

  1. In the ./lib01 folder, the lib import ./lib02 in their code../lib01文件夹中,lib 在其代码中导入./lib02 Tsconfig file will be like: Tsconfig 文件将如下所示:
{
  "compilerOptions": {
    "declarationDir": "dist",
    "rootDir": "src"
  },
  "include": ["src/**/*"],
  "references": [ // here
    {
      "path": "../lib02"
    }
  ]
}
  1. lib02's tsconfig.json lib02 的tsconfig.json
 {
   "compilerOptions": {
    "declarationDir": "dist",
    "rootDir": "src",
    "composite": true // importance.
  }
 }

It depends on how and when you're reading "package.json".这取决于您阅读“package.json”的方式和时间。 You can read it as file with NodeJS "fs" module at runtime, or just type const package = require("package.json").您可以在运行时使用 NodeJS "fs" 模块将其作为文件读取,或者只需键入 const package = require("package.json")。

In 2nd case Typescript will search it in root dir at compile time (refer to Typescript module resolution documentation).在第二种情况下,Typescript 将在编译时在根目录中搜索它(请参阅Typescript 模块解析文档)。

You also can use "rootDirs" property instead of "rootDir" to specify array of root folders.您还可以使用“rootDirs”属性而不是“rootDir”来指定根文件夹数组。

When using // @ts-ignore on top of the import call and setting "rootDir": "./src" it works.import调用之上使用// @ts-ignore并设置"rootDir": "./src"时,它可以工作。 In this case enabling resolveJsonModule will still work, but only for files under the ./src .在这种情况下,启用resolveJsonModule仍然有效,但仅适用于./src下的文件。 See: https://github.com/MatrixAI/TypeScript-Demo-Lib/pull/33 for how I applied it to our template repository.请参阅: https ://github.com/MatrixAI/TypeScript-Demo-Lib/pull/33,了解我如何将其应用于我们的模板存储库。 This way it is possible to import json files from within ./src as normal, but when you import ../package.json , you have to use // @ts-ignore to ensure that TSC ignores it.这样就可以像往常一样从./src中导入 json 文件,但是当你导入../package.json时,你必须使用// @ts-ignore来确保 TSC 忽略它。 It's a one-off special case so this works.这是一次性的特殊情况,所以这是可行的。

The reason it all works is because setting https://www.typescriptlang.org/tsconfig#rootDir will force tsc not to infer the project root dir to be the src.一切正常的原因是因为设置https://www.typescriptlang.org/tsconfig#rootDir将强制 tsc 不将项目根目录推断为 src。 And thus will enforce the expected dist structure, while throwing warnings/errors on importing outside the rootDir .因此将强制执行预期的dist结构,同时在rootDir之外导入时抛出警告/错误。 But you can ignore these warnings.但是您可以忽略这些警告。

There is a tidy three-step solution with Node 16+ LTS and TypeScript 4.7+ for packages that use ES modules instead of CommonJS.对于使用ES 模块而不是 CommonJS 的包,Node 16+ LTS 和 TypeScript 4.7+ 有一个整洁的三步解决方案。

The "imports" field in package.json defines internal pseudo-packages that can be imported only from within your actual package. package.json中的"imports"字段定义了只能从实际包中导入的内部伪包。 Define an internal import specifier, such as #package.json in package.json :定义一个内部导入说明符,例如 package.json 中的#package.json package.json

{
  "type": "module",
  "exports": "./dist/index.js",
  "imports": {
    "#package.json": "./package.json"
  }
}

Enable TypeScript support for ES modules and JSON imports in tsconfig.json :tsconfig.json中启用对 ES 模块和 JSON 导入的 TypeScript 支持

{
  "compilerOptions": {
    "module": "nodenext",
    // "moduleResolution" defaults to "nodenext", just made explicit here
    "moduleResolution": "nodenext",
    "resolveJsonModule": true,
  }
}

Lastly, import #package.json from your TypeScript module:最后,从 TypeScript 模块导入#package.json

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

console.log(packageJson);

I solve this problem by using symlink: in windows:我通过使用符号链接解决了这个问题:在 Windows 中:

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

or in linux:或在linux中:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM