从 npm 包的子文件夹导入

Posted

技术标签:

【中文标题】从 npm 包的子文件夹导入【英文标题】:Import from subfolder of npm package 【发布时间】:2017-11-04 19:53:39 【问题描述】:

我一直致力于创建一个小型 React 组件库,以用于其他几个项目。我在内部发布包(使用私有 GitHub 存储库),然后包含在另一个项目中。但是,当我从包的子目录导入时,由于路径不匹配,我无法这样做。

使用该包的项目都使用 webpack 来捆绑/转译代码,因为我尽量避免在组件库中进行任何构建。

目录结构

- package.json
- src/
  - index.js
  - Button/
    - index.js
    - Button.jsx
    - ButtonGroup.jsx
  - Header/
    - index.js
    - Header.jsx (default export)

package.json

...
"main": "./src/index.js",
"scripts": "",
...

src/Button/index.js

import Button from './Button';
import ButtonGroup from './ButtonGroup';

export default Button;

export  Button, ButtonGroup;

src/index.js

如果只从子目录导入,这个文件真的有必要吗?

import Button from './Button';
import ButtonGroup from './Button/ButtonGroup';
import Header from './Header';

export  Button, ButtonGroup, Header ;

其他项目

// This project is responsible for building/transpiling after importing
import  Button, ButtonGroup  from 'components-library/Button';

示例

Material-UI 是一个 React 组件库,通过以下方式使用:import RadioButtonGroup from 'material-ui/RadioButton。我试图弄清楚这对他们是如何工作的,但还没有成功。

类似问题

How would I import a module within an npm package subfolder with webpack? 这几乎是我需要的正确方法,除了那里使用的导入路径涉及src/ 目录,我试图避免(应该是component-library/item,而不是component-library/src/item(虽然目前确实有效) )) Publishing Flat NPM Packages 这正是我想要的,只是我希望包中没有“构建”阶段(依赖于导入位置来构建/转换)

问题

    我可以在导入路径中以某种方式跳过src/ 目录吗? 我可以跳过包中的任何类型的构建阶段(这样开发人员就不必在提交之前构建)吗? 类似于 material-ui 的包如何处理这个问题?

【问题讨论】:

您是否发现Material-UI 是如何实现import npm-package/subfolder/Component 的方式来导入src/subfolder/Component 组件的? 我最终从这个转向其他选择,抱歉。 您能分享一下您的经验吗?我目前正在寻找类似的东西,但找不到有用的资源。现在我正在打包一个我正在编写的没有src 目录(github.com/tonix-tuft/react-js-utl)的库。我在项目的根目录中使用 ES6 源代码创建子文件夹并将这些目录发布到 NPM,但我想这不是最佳的(例如 ES6 导出在 Node 中的服务器端不起作用)......不过,我可以使用react-js-utl/subfolder 语法导入所需的文件。顺便说一句,祝你新年快乐!谢谢。 不幸的是我不记得我最后做了什么,因为它已经快一年半了:(。我确实希望有这方面的资源,但请记住我不能很快就弄清楚了,不得不继续前进。 你可以这个我的anwser ***.com/a/61829655/8079868 【参考方案1】:

答案可能取决于您安装组件库的方式。如果您是通过npm install <git-host>:<git-user>/<repo-name>npm install <git repo url> 完成的,

    根据your first linked question,您应该可以按原样使用import Button from 'component-library/Button'。与Node's require() resolution 类似,Webpack 应该解析组件库中相对于组件库入口点的子目录。您可以通过 webpack.config.resolve 属性找到有关自定义解析行为的文档。 material-ui 似乎依赖于从模块入口目录解析子目录导入。

    要分发 ES 模块库,无需在分发之前进行构建。但是,create-react-app 等项目可能需要预编译版本。

或者,您可以写import Button from 'components-library'。 Webpack 会毫不费力地通过每个 index 追溯依赖关系。

【讨论】:

【参考方案2】:

其中一个可能的解决方案是 webpack 别名系统。 您可以创建另一个项目,例如将其命名为“app-aliases”,这样您的别名就可以重复使用。 这个项目将有一个包含所有包路径的 js 文件:

const path = require('path');

module.exports = 
'@components': path.resolve(__dirname, 'node_modules/components-library/src'),
'@another': path.resolve(__dirname, 'node_modules/any/path/you/want'),

然后将其添加到任何负责构建/转译的项目的 webpack 配置中: webpack.config.js

const appAliases = require('app-aliases');

const config = 
...
  resolve: 
    alias: 
      ...appAlises
    
  

在运行时代码中你可以这样使用它:

import Button from '@components/Button';
import Something from '@another'

如果您使用的是 typescript,则需要将相同的别名添加到 paths tsconfig 属性。 所以你的问题的答案是:

    是的,您可以在别名中使用任何路径 是的,不必构建所有项目 我看到现在 mui 使用来自 directi 包(例如核心)的导入,请参阅 https://material-ui.com/components/radio-buttons/ 有 import Radio from '@material-ui/core/Radio';。但我希望他们使用我在下面描述的再出口。

还有关于node.js的解析机制。 当您导入某个库时,它会尝试在其中找到 node_modules/some-library/package.jsonmain 属性。此属性应指向您的主要入口点。通常是src/index.js(如果还没有,你应该在 package.json 中设置它)。在此文件中,您可以从内部文件结构中重新导出您想要的任何内容,并且可以在没有完整路径的情况下使用它。 有关示例,请参阅此repo。

【讨论】:

我不能花时间在原始答案的上下文中验证这一点(因为我不再拥有该项目);但是,它似乎与我现在对 Webpack 别名等的了解相匹配。【参考方案3】:

我是一名 Angular 开发人员,从未使用过 react,但我可以说 material-ui 正在使用 monorepo,其中相同的概念存在于 angular 中,我们创建一个工作区,并且该工作区包含多个项目/包,如 react 中命名的那样。欲了解更多信息Workspaces with Yarn

Material-ui 在 tsconfig 中使用假路径使其看起来像 src 文件夹在您提供的 git 中不存在:tsconfig.json

【讨论】:

这个答案是否解决了用户的问题?您可能希望将您的技能和经验带到带有 angular 标记的问题中。 是的,如果你按照我的回答,你可以创建一个与军事-ui 项目结构相同的项目。他们所做的是一个包含多个包的工作区,这被称为 monorepo 项目结构。问题的第二部分,您可以将包作为 import componentName from '@company/workspaceName' 导入,这是在 tsconfig.json 中完成的,并给了他来自 material-ui 的 axcample,如下所示:“paths”:“@material -ui/core": ["./packages/material-ui/src"], "@material-ui/core/*": ["./packages/material-ui/src/*"], ... . 这称为创建假路径。【参考方案4】:

我可以在导入路径中以某种方式跳过 src/ 目录吗?

是的。使用 package.json "exports" 字段,Webpack 应该会在不久的将来支持该字段(请参阅此 issue),但自 Node 12 LTS 之后的 Bare Module Specifier Resolution proposal 之后,Node 已经支持:

package.json

...
"main": "./src/index.js",
"type": "module",
...
"exports": 
  "./Button": "./src/Button/index.js",
  "./Header": "./src/Header/index.js"
,
...

现在,下面的代码:

// This project is responsible for building/transpiling after importing
import  Button, ButtonGroup  from 'components-library/Button';

应该翻译成:

import  Button, ButtonGroup  from 'components-library/src/Button/index.js';

应该正确导入请求的模块。

警告

现在,尝试更简单的版本肯定会很诱人:

...
"exports": 
  "./Button": "./src/Button/",
  "./Header": "./src/Header/"
,
...

所以和通常的导入语句一样

import  ...  from 'components-library/Button';

被翻译成

import  ...  from 'components-library/src/Button';

这看起来不错,但在这种情况下它不起作用,因为您的子模块没有各自的 package.json 文件,而是依赖于它们的 index.js 文件才能找到。

/!\ 与 CommonJS 不同,没有自动搜索 index.js 或 index.mjs 或文件扩展名。


src/index.js - 如果只从子目录导入,这个文件真的有必要吗?

我不这么认为,但如果你愿意,你可以保留它。


我可以跳过包中的任何类型的构建阶段吗?

使用"exports" 字段不需要您转译代码。

【讨论】:

好答案。我在这里找到了更多信息:medium.com/swlh/npm-new-package-json-exports-field-1a7d1f489ccf 打字稿用户:github.com/microsoft/TypeScript/issues/33079【参考方案5】:

你必须安装babel-plugin-module-resolver

像这样在 .babelrc 文件别名中指定包的相对路径


  "plugins": [
    ["module-resolver", 
      "alias": 
        "components-library": "./node_module/components-library"
      
    ]
  ]

然后你可以像这样导入npm包的子目录

import  Button, ButtonGroup  from 'components-library/Button';

【讨论】:

【参考方案6】:

这是可能的,但需要发布一个精选的 dist 文件夹,而不是项目的根目录。

如果您了解模块解析的工作原理,整个事情就相当简单了,您只需要一个小脚本来准备您的分发。

为了避免我在这里重复所有细节,请参阅我对Importing from subfolders for a javascript package的回答。

【讨论】:

以上是关于从 npm 包的子文件夹导入的主要内容,如果未能解决你的问题,请参考以下文章

从 Github 安装时自动构建 NPM 模块

如何从 C# 中的子文件夹导入 .dll 库

如何从 Python 中的子文件夹导入模块或文件?

从不同子文件夹的子文件夹相对导入python模块

Laravel如何导入通过NPM安装的vuejs包

__init__.py文件的作用