如何在库项目中具有绝对导入路径? [英] How to have absolute import paths in a library project?

查看:324
本文介绍了如何在库项目中具有绝对导入路径?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a library with a workspace containing two projects, one for the library itself and one for a test application.

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

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

In the workspace tsconfig.json file I configured some @app and @lib paths:

"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 文件上扩展:

There is a projects/midi-lib/tsconfig.lib.json file which extends on the above tsconfig.json file:

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

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

There is a public-api.ts file which contains:

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

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

I can use this library with the test application just fine.

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

But when I try using it in another client application, in another workspace, imported as a Node module, I get many errors on the unknown paths Can't resolve '@lib/...'

如何表达库路径,以便它们在客户端应用程序中公开?还是打包库时如何转换库路径?

How to express the library paths so that they are exposed in a client application ? Or how to translate the library paths when packaging the library ?

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

As a side question, I wonder why the extends is not done the other way around. Why is it not the tsconfig.json file that extends on the projects/midi-lib/tsconfig.lib.json file ?

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

Here is how I package and then use the library:

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

To package the library, add the following scripts in the scripts array of the parent package.json file

"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 文件中具有:

and import the module in the application module In the app.module.ts file have:

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

将组件插入模板中

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

将库安装在客户端应用程序中后,它会包含许多 .d.ts node_modules / midi-lib 目录中的文件:

When the library is installed in a client application, it has lots of .d.ts files in the node_modules/midi-lib directories:

├── 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 路径映射,在客户端应用程序中未知。

As can be seen, it contains references to the @lib path mapping, which is not known in the client application.

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

I also tried to use the baseUrl property as a work around, but that didn't help either, as when installing the library, this baseUrl value was not specified.

为什么用命令 npm run package 打包库而不解析路径映射?

Why is packaging the library with the command npm run package not resolving the paths mappings ?

推荐答案

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

The paths mapping you establish in your tsconfig.json is purely a compile-time mapping. It has no effect on the code generated by the TypeScript compiler. Which is why you have a failure at run time. That's something that has been reported to the TypeScript project, suggesting that tsc should automatically translate module paths in emitted code to conform to the mapping established by paths. The TS devs responded tsc is working as intended and that the solution is to configure a module loader that performs at run time a mapping similar to that established by paths.

这是我认为应该做的,

Here what I think you should do, based on how you described your case.

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

I'm assuming that midi-app is a test application that is not meant to be distributed. You should be able to continue using the paths mapping you have without any issue. (You've not mentioned any issue running this app. So it seems your tooling already takes care of the runtime issue.)

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

For midi-lib, I would stop relying on the mappings established by paths and just use relative paths. This is a library, meant to be consumed by others. Because of this, any configuration that would fix the module name mapping at run time (or at bundling time) would have to be handled by the consumers of your library. Consumers that use Webpack will have to add a configuration to their Webpack configuration to provide the right mapping. Consumers that use Rollup would have to do the same with Rollup. Consumers that use SystemJS would have to do the same with SystemJS, etc.

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

Moreover, the required configuration could get complicated depending on the context in which your library is used. As long as your library is the only one needing to map @lib to some path, the mapping that must be added to Webpack (or SystemJS, etc.) can be global. The module bundler or module loader will always replace @lib with your path, which is fine because your package is the only one that needs @lib replaced. However, suppose another library author does exactly what you did, and a consumer of your library also uses that other library. Now you have a situation where @lib must be mapped to one path in some cases, and must be mapped to another path in other cases. This can be configured, but it requires more complex configuration.

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

I've focused on the issue of resolving modules during bundling or when loading them at runtime, but there's another issue. Consumers would also need to configure their tsc compilation with a special configuration because the .d.ts files

如果仅在代码中使用相对路径,则库的使用者就不必担心添加特殊的配置来满足库的特殊需求。

有一种特殊情况可能恰好适合您的情况。如果您的库将以 midi-lib 的形式发布,则可以更改路径映射,以便代替 @lib / * ,您有一个 midi-lib / * 的地图:

There's a special case that may happen to fit your case. If your library is going to be published as midi-lib then you can change your paths map so that instead of @lib/* you have a map for midi-lib/*:

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

(请注意,对于TypeScript, @ 符号没有特殊含义。另外请注意您的软件包是否意味着要安装一个范围,例如 @ midi-project / midi-lib ,那么您需要在 tsconfig.json 也映射: @ midi-project / midi-lib / *:...

(Note that the @ symbol has no special meaning as far as TypeScript is concerned. Also note if your package is meant to be installed with a scope, like @midi-project/midi-lib then you need the scope in the tsconfig.json mapping too: "@midi-project/midi-lib/*": ...)

基本上,这里的目标是设置一个映射,使您可以以与项目使用者从项目中导入各个模块完全相同的方式在项目中导入模块。如果您的模块的使用者将从 ParseService import {ParseService} ,然后在您自己的代码中使用相同的 import 。 (请注意,是否告诉使用者直接导入该模块无关紧要。如果使用者要直接导入该模块,然后他们将使用什么路径?) >因此相同的路径在编译时和运行时(或捆绑时)都有效。在编译时, tsc 会转换路径。在运行时或捆绑时,Node的模块解析算法(或可以遵循相同算法的工具,例如Webpack或Rollup)将转换路径。

Basically, the goal here is to set a mapping that allows you to import modules in your project in exactly the same way a consumer of your project would import individual modules from it. If a consumer of your module would import the ParseService with import { ParseService } from "midi-lib/lib/service/parse.service", then in your own code you'd use the same import when you want to use that module. (Note that it does not matter whether you tell consumers to import this module directly. If consumers were to import the module directly, then what path would they use?) So the same path works at compile time and at run time (or bundling time). At compile time, tsc converts the path. At run time or bundling time, Node's module resolution algorithm (or a tool which can follow the same algorithm, like Webpack or Rollup) converts the path.

您需要键入多少能否保存,很大程度上取决于您选择的名称以及库的结构。

How much typing you'd save with this highly depends on the names you've chosen and how you structured your library.

理论上,您可以在运行 ng build 之后执行一步,以覆盖 ng build 生成的文件并替换 @lib 在模块名称中应包含其实际指向的路径。问题在于:

In theory, you could have a step after you run ng build that would go over the files produced by ng build and replace @lib in module names with the actual path it is supposed to point to. The problems with this:


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

  1. It's not just a matter of running a single tool or flipping a flag in a configuration option. Maybe a tool like rollup can transform the JS files but you need to now learn how it works and write a configuration for it.

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

AFAIK there's no readily available tool that will transform the .d.ts files as you need them. You'd most likely have to write your own tool.

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

You'd also need to patch the AOT compilation metadata generated by the Angular AOT compiler because it also contains module references, and these references are used by consumers of your library. AFAIK, there's no such tool that exists. So here too you'd have to roll your own.

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

Your build process could break if a new Angular version changes the format of the AOT compilation metadata or adds a different type of metadata file that needs patching. I know this from experience: I have a couple of packages that are highly experimental Angular applications. For historical reasons, they entirely bypass using Angular CLI for building. Every Angular upgrade from version 4 onwards broke something in the build process of these applications. It often had to do with how AOT compilation metadata was handled.

这篇关于如何在库项目中具有绝对导入路径?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆