26

我有一个库,其工作区包含两个项目,一个用于库本身,一个用于测试应用程序。

├── projects
    ├── midi-app
    └── midi-lib

在工作区tsconfig.json文件中,我配置了一些@app@lib路径:

"paths": {
  "@app/*": ["projects/midi-app/src/app/*"],
  "@lib/*": ["projects/midi-lib/src/lib/*"],
  "midi-lib": [
    "dist/midi-lib"
  ],
  "midi-lib/*": [
    "dist/midi-lib/*"
  ]
}

有一个projects/midi-lib/tsconfig.lib.json文件扩展了上述tsconfig.json文件:

"extends": "../../tsconfig.json",

有一个public-api.ts文件包含:

export * from './lib/midi-lib.module';

我可以将此库与测试应用程序一起使用。

但是当我尝试在另一个客户端应用程序中使用它时,在另一个工作区中,作为 Node 模块导入时,我在未知路径上遇到很多错误Can't resolve '@lib/...'

如何表达库路径以便它们在客户端应用程序中公开?或者打包库时如何翻译库路径?

作为一个附带问题,我想知道为什么不以相反的方式进行扩展。为什么不是在tsconfig.json文件上扩展的projects/midi-lib/tsconfig.lib.json文件?

这是我打包然后使用库的方式:

package.json打包库,在父文件的scripts数组中添加以下脚本

"copy-license": "cp ./LICENSE.md ./dist/midi-lib",
"copy-readme": "cp ./README.md ./dist/midi-lib",
"copy-files": "npm run copy-license && npm run copy-readme",
"build-lib": "ng build midi-lib",
"npm-pack": "cd dist/midi-lib && npm pack",
"package": "npm run build-lib && npm run copy-files && npm run npm-pack",

并运行命令:npm run package

然后安装依赖

npm install ../midi-lib/dist/midi-lib/midi-lib-0.0.1.tgz

并在应用程序模块中导入模块app.module.ts文件中有:

import { MidiLibModule } from 'midi-lib';
@NgModule({
  imports: [
    MidiLibModule

最后将组件插入模板

<midi-midi-lib></midi-midi-lib>

当库安装在客户端应用程序中时,目录.d.ts中有很多文件node_modules/midi-lib

├── bundles
├── esm2015
│   └── lib
│       ├── device
│       ├── keyboard
│       ├── model
│       │   ├── measure
│       │   └── note
│       │       ├── duration
│       │       └── pitch
│       ├── service
│       ├── sheet
│       ├── soundtrack
│       ├── store
│       ├── synth
│       └── upload
├── esm5
│   └── lib
│       ├── device
│       ├── keyboard
│       ├── model
│       │   ├── measure
│       │   └── note
│       │       ├── duration
│       │       └── pitch
│       ├── service
│       ├── sheet
│       ├── soundtrack
│       ├── store
│       ├── synth
│       └── upload
├── fesm2015
├── fesm5
└── lib
    ├── device
    ├── keyboard
    ├── model
    │   ├── measure
    │   └── note
    │       ├── duration
    │       └── pitch
    ├── service
    ├── sheet
    ├── soundtrack
    ├── store
    ├── synth
    └── upload

喜欢这个lib/service/melody.service.d.ts文件

import { SoundtrackStore } from '@lib/store/soundtrack-store';
import { ParseService } from '@lib/service/parse.service';
import { CommonService } from './common.service';
export declare class MelodyService {
    private soundtrackStore;
    private parseService;
    private commonService;
    constructor(soundtrackStore: SoundtrackStore, parseService: ParseService, commonService: CommonService);
    addSomeMelodies(): void;
    private addSoundtrack;
    private generateNotes;
}

可以看出,它包含对@lib路径映射的引用,这在客户端应用程序中是未知的。

我也尝试使用该baseUrl属性作为解决方法,但这也无济于事,因为在安装库时,baseUrl未指定此值。

为什么用命令打包库npm run package不能解析paths映射?

4

2 回答 2

17

paths您建立的映射tsconfig.json纯粹是编译时映射。它对 TypeScript 编译器生成的代码没有影响。这就是为什么您在运行时失败的原因。这是已向TypeScript 项目报告的内容,建议tsc应该在发出的代码中自动转换模块路径,以符合paths. TS 开发人员回应说tsc正在按预期工作,解决方案是配置一个模块加载器,该模块加载器在运行时执行类似于paths.


根据您对案件的描述,我认为您应该这样做。

我假设这midi-app是一个不打算分发的测试应用程序。您应该能够继续使用paths您拥有的映射而不会出现任何问题。(您没有提到运行此应用程序的任何问题。因此,您的工具似乎已经处理了运行时问题。)

对于midi-lib,我将不再依赖由建立的映射,paths而只使用相对路径。这是一个图书馆,旨在供其他人使用。因此,任何会在运行时(或在捆绑时)修复模块名称映射的配置都必须由库的使用者处理。使用 Webpack 的消费者必须将配置添加到他们的 Webpack 配置中以提供正确的映射。使用 Rollup 的消费者必须对 Rollup 做同样的事情。使用 SystemJS 的消费者必须对 SystemJS 等做同样的事情。

此外,根据使用库的上下文,所需的配置可能会变得复杂。只要您的库是唯一需要映射@lib到某个路径的库,必须添加到 Webpack(或 SystemJS 等)的映射可以是global。模块捆绑器或模块加载器将始终替换@lib为您的路径,这很好,因为您的包是唯一需要@lib替换的包。但是,假设另一个库作者完全按照您所做的那样做,并且您的库的使用者也使用该其他库。现在您遇到一种情况,@lib在某些情况下必须映射到一条路径,而在其他情况下必须映射到另一条路径。这可以可以配置,但需要更复杂的配置。

我专注于在捆绑期间或在运行时加载模块时解析模块的问题,但还有另一个问题。消费者还需要tsc使用特殊配置来配置他们的编译,因为.d.ts文件

如果您只是在代码中使用相对路径,那么您的库的使用者将不必担心添加特殊配置来满足您的库的特殊需求。


有一个特殊情况可能恰好适合您的情况。如果您的图书馆将要发布,midi-lib那么您可以更改您的paths地图,以便@lib/*您拥有以下地图midi-lib/*

"midi-lib/*": ["projects/midi-lib/src/*"],

(请注意,@就 TypeScript 而言,该符号没有特殊含义。另请注意,如果您的包打算安装一个范围,@midi-project/midi-lib那么您也需要tsconfig.json映射中的范围"@midi-project/midi-lib/*": ...:)

基本上,这里的目标是设置一个映射,允许您在项目中导入模块,其方式与项目使用者从中导入单个模块的方式完全相同。如果您的模块的使用者将导入ParseServicewith import { ParseService } from "midi-lib/lib/service/parse.service",那么在您自己的代码中,import当您想使用该模块时,您将使用相同的代码。(请注意,您是否告诉消费者直接导入此模块并不重要。如果消费者要直接导入模块,那么他们将使用什么路径?)所以相同的路径在编译时和运行时(或捆绑时)都有效)。在编译时,tsc转换路径。在运行时或捆绑时,Node 的模块解析算法(或可以遵循相同算法的工具,如 Webpack 或 Rollup)会转换路径。

您将节省多少打字很大程度上取决于您选择的名称以及您如何构建库。


从理论上讲,您可以在运行后执行一个步骤,该步骤ng build将检查生成的文件ng build并将@lib模块名称替换为它应该指向的实际路径。这样做的问题:

  1. 这不仅仅是运行单个工具或在配置选项中翻转标志的问题。也许像这样的工具rollup可以转换 JS 文件,但您现在需要了解它的工作原理并为其编写配置。

  2. AFAIK 没有现成的工具可以.d.ts根据需要转换文件。您很可能必须编写自己的工具。

  3. 您还需要修补由 Angular AOT 编译器生成的 AOT 编译元数据,因为它还包含模块引用,并且这些引用由您的库的使用者使用。AFAIK,不存在这样的工具。所以在这里你也必须自己动手。

  4. 如果新的 Angular 版本更改了 AOT 编译元数据的格式或添加了需要修补的不同类型的元数据文件,您的构建过程可能会中断。我从经验中知道这一点:我有几个包是高度实验性的 Angular 应用程序。由于历史原因,他们完全绕过使用 Angular CLI 进行构建。从版本 4 开始的每次 Angular 升级都会破坏这些应用程序的构建过程。它通常与 AOT 编译元数据的处理方式有关。

于 2019-07-31T12:37:45.867 回答
1

正如其他人所说,打字稿不会修改您的@app@lib导入。我在尝试在库包中使用绝对路径时遇到了同样的问题。您需要准备您的库以使用汇总或类似的方式发布。

Rollup 有各种插件,我不会介绍完整的设置,但您需要的是一个可以重写您的 imports的插件。这个插件看起来像它:https ://github.com/bitshiftza/rollup-plugin-ts-paths

对于其余的汇总配置,您可能需要rollup-plugin-node-resolverollup-plugin-commonjs,以及处理 typescript 的插件 ( rollup-plugin-typescript ),或者您可以采用更新的路线使用 babel。搜索一些指南,因为有一些流行的用 typescript 编写的库,它们使用汇总来准备打包代码(React 就是其中之一)。

快乐编码。

于 2019-08-01T18:42:35.757 回答