从 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.json
和 main
属性。此属性应指向您的主要入口点。通常是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 包的子文件夹导入的主要内容,如果未能解决你的问题,请参考以下文章