如何在库项目中具有绝对导入路径?

时间:2019-07-09 09:30:01

标签: angular typescript tsconfig angular-library tsconfig-paths

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

├── 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';

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

但是,当我尝试在另一个客户端应用程序,另一个工作空间(作为节点模块导入)中使用它时,我在未知路径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值。

3 个答案:

答案 0 :(得分:2)

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


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

我假设midi-app是一个不打算分发的测试应用程序。您应该可以继续使用paths映射,而不会出现任何问题。 (您没有提到运行此应用程序时遇到任何问题。因此,看来您的工具已经解决了运行时问题。)

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

此外,所需的配置可能会变得复杂,具体取决于使用库的环境。只要您的库是唯一需要将@lib映射到某个路径的 ,必须添加到Webpack(或SystemJS等)的映射可以是 global < / em>。模块捆绑器或模块加载器将始终用您的路径替换@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/*": ...

基本上,这里的目标是设置一个映射,该映射允许您以与项目使用者从项目中导入各个模块完全相同的方式在项目中导入模块。如果模块的使用者将ParseServiceimport { 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编译元数据的处理方式有关。

答案 1 :(得分:0)

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

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

对于其余的汇总配置,您可能需要rollup-plugin-node-resolverollup-plugin-commonjs,以及用于处理打字稿(rollup-plugin-typescript)的插件,否则您可以使用更新的插件使用通天塔的途径。搜索一些指南,因为有流行的用打字稿编写的库,这些库使用汇总来准备要打包的代码(反应一个)。

快乐的编码。

答案 2 :(得分:-3)

我想找到了解决方法。

删除src文件夹,您会发现一个新世界!