Making files in sub-folder available directly under npm import root - javascript

I am making an npm module that has this folder structure:
lib/
one-icon.jsx
another-icon.jsx
/* about 100 more */
package.json
I want to be able to import them like so:
import OneIcon from 'icon-package/one-icon';
I don't want to move the files from /lib/* to /*, because that will mess up my repo, and it will make the entire project a maintenance nightmare.
Is there any way I can tell npm that icon-package/some-icon refers to icon-package/lib/some-icon?
I tried setting the files field in package.json, but that only made sure the lib/ folder was included in the node_modules package.
Any ideas?

Maybe you can import like this?
import {OneIcon} from 'icon-package';
And having index.js in the package like this :-
export OneIcon from ‘./lib/OneIcon.jsx’

Related

Export shareable eslint config from a React library's dist folder for other projects

I have a published React library with a dist folder where I export numerous default React components for other projects and I would like to export my eslint config from there.
I've seen people do it in basic npm packages in the main index.js file, but I would like to export everything from one package and not rely on a second package for the eslint config.
I added all my rules to /src/components/eslint/index.js - this then gets translated into /dist/eslint/index.js
Is there any way to import this specific index.js with my eslint rules as eslint config in another project?
In another project I already tried linking it in package.json
and also in the .eslintrc.json file
without luck.
Any ideas?

How to export multiple library variants from a single NPM package

I'm creating a node library that could be partially used in web browsers.
My current structure is something like:
package.json
lib/
index.js
node.js
web.js
modules/
some-function.js
some-node-function.js
some-web-function.js
...
"lib/index.js" is specified as "main" in package.json
I'm using each file inside lib/ to re-export functions from modules/ filtered by target (all/node/web)
And I would like to use it like this:
import fullLibrary from "my-library";
import {someFunction} from "my-library";
import webOnlyLibrary from "my-library/web";
import {someWebFunction} from "my-library/web";
import nodeOnlyLibrary from "my-library/node";
import {someNodeFunction} from "my-library/node";
BUT keeping index.js, node.js and web.js inside /lib!
Currently only the first two import statements work (because index.js is pointed from package.json).
I know that I could place those files in the root of the package and it would work as expected when importing, but I was wondering if there was a way to do the same while keeping the files in /lib for cleanness.
In any case, is this the recommended way to expose multiple variants of a same library from a single NPM package?
Just for anyone that is having similar questions regarding libraries structure and organization.
Back then I started working with monorepos using Lerna and Yarn Workspaces and I haven't regret it. I think this is the most efficient way to go in terms of modularity, readability, scalability and maintainability.
Specifically, the question code would be traduced into this:
import webOnlyLibrary from "#my-library/web";
import {someWebFunction} from "#my-library/web";
import nodeOnlyLibrary from "#my-library/node";
import {someNodeFunction} from "#my-library/node";

How does import statement look for a module from installed node modules in reactJS?

I just want to know one thing,
Consider i have defined a dependency in package.json file like "some-library": "1.0.0"
and installed it using npm install. which will include all dependencies to node_modules folder.
then am importing a Component from that dependency using
import SomeLibrary from 'some-library;
when we do this where this import statement start looking for the component which we are importing ?
can some one explain in a better way. i have googled alot but didn't find any relevant answer. Thanks in advance.
At it's core, the import statement uses the same module resolution method as require().
So for installed modules it goes like this: By calling require(X), it gets a list of all the 'node_modules' directories present in the parent directories. Then, it tries to load the X module from each of those directories (either as a single file, or a directory.)
https://nodejs.org/api/modules.html#modules_all_together

Import from subfolder of npm package

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 be component-library/item, not component-library/src/item (which does work currently though))
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?
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.
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.
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';
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.
I'am an angular developer never used react but what I could tell that material-ui are using monorepo where same concept exists in angular where we create one workspace and this workspace hold multiple project/packages as named in react. for more info Workspaces with Yarn
Material-ui using fake paths in tsconfig to make it appears like src folder doesn't exists this from the git you provided: tsconfig.json
This is possible but requires publishing a curated dist folder rather then the root of your project.
The whole thing is rather simple if you understand how module resolution works, and you just need a small script to prepare your distribution.
Lest I repeat all the details here, please see my answer for Importing from subfolders for a javascript package.

How to require an alternative js file from a npm package without specifiying full node_modules path

I am using the ES2016 import syntax to load the select2 libray from an npm module (via Webpack):
import 'select2';
This works fine and loads the following file from the node_modules directory.
node_modules/select2/dist/js/select2.js
Now within that directory they have a full version of the library which has some extra functionality I need, it is called:
node_modules/select2/dist/js/select2.full.js
Is there a way to import this without providing the full relative path to my node_modules folder?
I tried:
import 'select2.full'
but no luck.
Try this:
import 'select2/dist/js/select2.full.js'
I think this is the best you are going to get.

Categories

Resources