Import from subfolder of npm package
Solution 1
One of the possible solutions there is webpack aliasing system.
You can create another project, call it for example 'app-aliases', so your aliases will be reusable.
This project will has one js file with all of your packages paths:
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'),
}
And then add it to the webpack configuration in any project which will be responsible for building/transpiling:
webpack.config.js
const appAliases = require('app-aliases');
const config = {
...
resolve: {
alias: {
...appAlises
}
}
}
In the runtime code you will be able to use it like this:
import {Button} from '@components/Button';
import {Something} from '@another'
If you are using typescript you will need to add the same aliases to the paths
tsconfig property.
So answers to your questions are:
- Yes, you can use any path in aliases
- Yes, it is not necessary to build all of your projects
- I see that now mui uses imports from directi packages (core for example), see https://material-ui.com/components/radio-buttons/ there is
import Radio from '@material-ui/core/Radio';
. But I hope they using re-export that I described below.
Also about node.js
resolution mechanism.
When you import some library it tries to find node_modules/some-library/package.json
and then main
property inside it. This property should lead to your main entry point. Usually it is src/index.js
(you should set it in package.json if it is no there yet). In this file you can re-export anything you want from internals file structure and will be able to use it without the full path.
Please see this repo for some examples.
Solution 2
Can I skip the src/ directory somehow in the import path?
Yes. Using the package.json "exports"
field, which should be supported by Webpack in a near future (see this issue), but has already been supported by Node since Node 12 LTS following the Bare Module Specifier Resolution proposal:
package.json
...
"main": "./src/index.js",
"type": "module",
...
"exports": {
"./Button": "./src/Button/index.js",
"./Header": "./src/Header/index.js"
},
...
Now, the following code:
// This project is responsible for building/transpiling after importing
import { Button, ButtonGroup } from 'components-library/Button';
should be translated to:
import { Button, ButtonGroup } from 'components-library/src/Button/index.js';
which should correctly import the requested modules.
Caveat
Now, it would certainly be tempting to try a simpler version like:
...
"exports": {
"./Button": "./src/Button/",
"./Header": "./src/Header/"
},
...
so as the usual import statement
import { ... } from 'components-library/Button';
gets translated to
import { ... } from 'components-library/src/Button';
This looks nice, but it will not work in this case, because your submodules don't have each their own package.json
file but rely on their index.js
file to be found.
/!\ Unlike in CommonJS, there is no automatic searching for index.js or index.mjs or for file extensions.
src/index.js - Is this file actually necessary if only importing from subdirectories?
I don't think so, but you can keep it if you want.
Can I skip any type of build phase in the package?
Using the "exports"
field does not require you to transpile your code.
Solution 3
The answer may depend on how you installed your components library. If you did it via either npm install <git-host>:<git-user>/<repo-name>
or npm install <git repo url>
,
You should be able to
import {Button} from 'component-library/Button'
as is, according to your first linked question. Similar to Node's require() resolution, Webpack should resolve subdirectories within component-library relative to component-library's entry point. You can find the docs on customizing the resolution behavior via the webpack.config.resolve property. material-ui seems to rely on resolving subdirectory imports from the module entry directory.To distribute an ES module library, there's no need for building before distribution. However, projects such as create-react-app may need a pre-transpiled version.
Alternately, you can write import {Button} from 'components-library'
.
Webpack will trace the dependencies back through each index
without a fuss.
Solution 4
you have to install babel-plugin-module-resolver package
Specify the package relative path in your .babelrc file alias like this
{
"plugins": [
["module-resolver", {
"alias": {
"components-library": "./node_module/components-library"
}
}]
]
}
then you can import subdir of npm package like this
import { Button, ButtonGroup } from 'components-library/Button';
Kendall
BY DAY: I currently work as a full-stack developer at Focus21, helping with both front end and back end tasks. BY NIGHT: I enjoy gaming and reading, and some of my favourite moments have been spent discovering new worlds, both in an RPG or book. FOR FUN: I love reading, computers, and life in general (usually)!
Updated on March 12, 2021Comments
-
Kendall about 3 years
I've been working on creating a small library of React components for use in several other projects. I am publishing the package internally (using a private GitHub repository) and then including in another project. However, when I go to import from a subdirectory of the package I am not able to do so as the paths don't match.
The projects using the package all utilize webpack to bundle/transpile code as I am trying to avoid doing any building in the component library if possible.
Directory Structure
- 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
Is this file actually necessary if only importing from subdirectories?
import Button from './Button'; import ButtonGroup from './Button/ButtonGroup'; import Header from './Header'; export { Button, ButtonGroup, Header };
Other Project
// This project is responsible for building/transpiling after importing import { Button, ButtonGroup } from 'components-library/Button';
Example
Material-UI is a library of React components that is used by requiring in the following fashion:
import { RadioButtonGroup } from 'material-ui/RadioButton
. I've tried to figure out how this works for them but to no avail yet.Similar Questions
-
How would I import a module within an npm package subfolder with webpack?
- This is very nearly the correct approach I require, except that the import path used there involved the
src/
directory, which I am trying to avoid (should becomponent-library/item
, notcomponent-library/src/item
(which does work currently though))
- This is very nearly the correct approach I require, except that the import path used there involved the
-
Publishing Flat NPM Packages
- This is exactly what I want except that I was hoping to not have a "build" phase in the package (rely on importing locations to build/transpile)
Questions
- Can I skip the
src/
directory somehow in the import path? - Can I skip any type of build phase in the package (so developers don't have to build before committing)?
- How does a package similar to material-ui handle this?
-
How would I import a module within an npm package subfolder with webpack?
-
Kendall about 4 yearsI can't take the time to verify this in the context of the original answer (since I no longer have the project); however, it does appear to match what I now know about Webpack aliases, etc.
-
matty about 4 yearsDoes this answer address the user's question? You may want to bring your skills and experience to questions tagged with angular.
-
Hussein Akar about 4 yearsYes, If you follow my answer you could create a project with the same structure as martial-ui project. What they did is a workspace containing multiple packages this is called monorepo project structure. second part of the question where you could import you packages as import { componentName} from '@company/workspaceName' this is done in tsconfig.json and gave him the axcample from material-ui as follow: "paths": { "@material-ui/core": ["./packages/material-ui/src"], "@material-ui/core/*": ["./packages/material-ui/src/*"], .... } this is called creating fake path.
-
Toivo Säwén over 3 yearsGood answer. I found more information here: medium.com/swlh/npm-new-package-json-exports-field-1a7d1f489ccf
-
Toivo Säwén over 3 yearsTypescript users: github.com/microsoft/TypeScript/issues/33079