ES6 module import group of files with individual exports - javascript

I would like to combine some modules into a single file that can be imported. These are local files and not part of an npm module.
Module Kitten (kitten.js)
export function Feed() {}
export function Play() {}
In my code I can access 'Feed' and 'Play':
// This works but I would like to avoid this due to my paths
import { Feed, Play } from './some/long/path/kitten.js'
// Then use it
Feed()
As I have many 'pets' I can contenate them in a master file - say pets.js
export * as Kitten from './some/long/path/kitten.js'
export * as Puppies from './some/long/path/puppies.js'
...
In my code I can then do:
import { Kitten, Puppies } from './pets'
// Then use it as
Kitten.Feed()
is it possible to have both a) the master pets file and b) call Feed() without doing Kitten.Feed()?
The following doesn't work as it's not a valid path. It's possible it would work as 'pets/Kitten' if it was an npm module - am not sure.
import { Feed, Play } from './pets/Kitten'
I was thinking something along the lines of:
import * as Pets from from './pets'
import { Feed, Play } from Pets.Kitten // or 'Pets/Kitten'
But clearly that doesn't work. I am wondering if it's at all possible.
I am using this in Node with Babel 6 and ES6 module loading. I see a lot of similar questions but they all use default exports which I am not using.

But that doesn't allow me to import selected functions.
Sure it does. The relative-path import works the same as module import. You can destructure the results just the same.
import { Play } from 'Pet/Puppy';
// is identical to
import { Play } from '../node_modules/Pet/Puppy';
If you take a look at the import syntax (s15.2.2), you can see that the from part expects a string. It doesn't care what's in the string, that's up to the module system (the browser, node, etc).

Ah.. object destructuring.. Forgot about that.
import { Kitten, Puppies } from './pets'
const {Feed, Play} = Kitten;
Thanks to https://stackoverflow.com/a/30132149/856498

Related

How are the imports implemented in different React libraries and how can I implement the same pattern in my own library?

For example, the recommended way of importing in React Bootstrap is to go this way:
import Button from 'react-bootstrap/Button' instead of import { Button } from 'react-bootstrap';
The reason is "Doing so pulls in only the specific components that you use, which can significantly reduce the amount of code you end up sending to the client."
source: https://react-bootstrap.github.io/getting-started/introduction/
Same for React MUI components:
import Button from '#mui/material/Button';
source: https://mui.com/material-ui/getting-started/usage/
I want to implement something similar in my React components library, to limit the usage of code in the bundle, but I don't know how they implement this specific pattern. I have looked at their code base, but I don't quite understand.
Basically it is all about modules and module files and their organization. You can have a lot of.. lets call them folders, "compoments/*" for example. "components/button", "components/alert", "component/badge", and other things. All of them will have some index.js or .ts file that will export or declare and export all the functionality that needed in order to make this component work, 'react-bootstrap/Button' for example. Ideally all those subfolders or submodules are independend from each other, no references between them but probably each one will have 1 reference to 1 common/shared submodule like "components/common" which will contain some constants, for example, and no references to other files. At the top level of them you will have another index.js or .ts file that is referencing all of those components, so "components/index.js" will import and reexport all the nested components index files. So in order to import a Button, for example, you can either import "components/index.js" file with all the other imports this file is using, either only 1 single "components/button/index.js" file which is obviously much more easy to fetch. Just imagine a tree data structure, you import root of the tree (root index.js) - you get all the tree nodes. You import one specific Node (components/button/index.js) of the tree - just load all the childs (imports) of that node.
Sorry for a long read but asuming you mentioned webpack - there is a technique called tree-shaking which will cut off all the unused things.
Info about modules: https://www.w3schools.com/js/js_modules.asp
Info about Tree-Shaking: https://webpack.js.org/guides/tree-shaking/
It might not be as complicated as you think. Let's say you write the following library:
// your-library.js
const A = 22
const B = 33
export function getA () { return A }
export function getB () { return B }
export function APlusB () { return A + B }
// a lot of other stuff here
If some consumer of your library wants to make use of the APlusB function, they must do the following:
// their-website.js
import { APlusB } from 'your-library'
const C = APlusB()
However, depending on how the code is bundled, they may or may not wind up with the entire your-library file in their web bundle. Modern bundling tools like Webpack may provide tree shaking to eliminate dead code, but this should be considered an additional optimization that the API consumer can opt into rather than a core behavior of the import spec.
To make your library more flexible, you can split up independent functions or chunks of functionality into their own files while still providing a full bundle for users who prefer that option. For example:
// your-library/constants.js
export const A = 22
export const B = 33
// your-library/aplusb.js
import { A, B } from 'constants'
export default function APlusB () { return A + B }
// your-library/index.js
// instead of declaring everything in one file, export it from each module
export * from 'constants'
export { default as APlusB } from 'aplusb'
// more exports here
For distribution purposes you can package your library like so:
your-library
|__aplusb.js
|__constants.js
|__index.js
You mentioned react-bootstrap and you can see this exact pattern in their file structure:
https://github.com/react-bootstrap/react-bootstrap/tree/master/src
and you can see they aggregate and re-export modules in their index file here:
https://github.com/react-bootstrap/react-bootstrap/blob/master/src/index.tsx
Essentially, what you are asking is:
"How to export react components"
OR
"How are react components exported to be able to use it in a different react project ?"
Now coming to your actual question:
import Button from 'react-bootstrap/Button' instead of import { Button } from 'react-bootstrap';
The reason is 'Button' component is the default export of that file react-bootstrap/Button.tsx. So there is no need for destructuring a specific component.
If you export multiple components/ functions out of a file, only 1 of them can be a default export.
If you have only 1 export in a file you can make it the default export.
Consider the file project/elements.js
export default function Button(){
// Implementation of custom button component
}
export function Link(){
// Implementation of custom Link component
}
function Image(){
// Implementation of custom Image component
}
Notice that the Button component has 'default' as a keyword and the Link component doesn't.
The Image component can't even be imported and can only be used by other functions/components in the same file.
Now in project/index.js
import 'Button', {Link} from './elements.js'
As Button component is the default export its possible to import without destructuring and as Link component is a regular export, I have to destructure it for importing.

Why is it exported this way in the index file?

I'm following a nest tutorial and the instructor creates a folder called dtos and inside it creates two dto's (create-user.dto and edit-user.dto). Then, create an index file (in the same folder) that contains only the following:
index.ts:
export * from './create-user.dto';
export * from './edit-user.dto'
I don't understand two things:
1-why do you export the dtos from there? they already export themselves.
2- because it uses exports the dtos directly. Shouldn't I import them first?
Here is the code of the data:
edit-user.dto:
export class EditUserDto {}
create-user.dto:
export class CreateUserDto {}
1-why do you export the dtos from there? they already export themselves.
It allows for more concise importing. Say your folder structure is:
top
index
dtos
index
create-user
edit-user
If you import create-user and edit-user into dtos/index, and then export them from dtos/index, you can then import them from the top index with:
import { EditUserDto, CreateUserDto } from './dtos';
This is accessing what dtos/index exports.
Without this - yes, the classes are already exported, but importing them elsewhere takes a few more characters, since you have to navigate the folder structure more. From the top's index, you'd need:
import { EditUserDto } from './dtos/edit-user.dto';
import { CreaetUserDto } from './dtos/create-user.dto';
It's ever so slightly more unwieldy. Not a big deal. Some might prefer the extra boilerplate in order to import more concisely, others might prefer to navigate directly to the nested file location without bothering. Either will work just fine.
2- because it uses exports the dtos directly. Shouldn't I import them first?
You can import from a file and export that which you're importing in the same line using that syntax you see. export * from 'path' will take everything path exports, and export it in the current file as well.

How do you find out which functions are exported by a node/npm package?

Following my earlier question, and the Mozilla documentation on import, I now understand that I must do something like the following to use the functionality in a module:
import * as name from "module"; or
import {functionName} from "module";
Coming from using CommonJS, I never thought about which functions were exported by a package because I just used to require them like:
const vueServerRenderer = require('vue-server-renderer') // get the module
vueServerRenderer.createRenderer() // use a function in that module
How can someone find out which functions are being exported by a module such as express or vueServerRenderer so I know how to use the correct import statement like:
import express from 'express' instead of import * as express from 'express'?
You need to read the module source.
Every export statement exports something. It may be a function, an array, a string, a class etc.
Every export statement without default needs to be destructured on import:
import { NonDefaultThing1, NonDefaultThing2 } from 'somewhere'
An export statement with default must be imported directly without the {}:
import DefaultThing from 'somewhere'
Some modules have default export but also non-default exports. You can pick and choose what to import:
import DefaultThing, { NonDefaultThing7 } from 'somewhere'
If you use an IDE that can parse javascript such as Microsoft Visual Studio Code you can get autocompletion/intellisense of the import statement. There are even plugins that does auto-import: just use a class or function or something from a module and it will automatically add the required import statement at the top of your file.
TLDR: The default export.
Say a particular library named "module" has the following code
function functionName() {
// function body
}
export default functionName;
Now, in your code, if you put
import blah from "module";
then blah will point to functionName.

Is there a performance cost if use multiple imports instead one named import

I m wondering if there is a performance cost if we make multiple imports, like so:
import { wrapper } from './components/wrapper';
import { error } from './components/error';
import { products } from './components/products';
In each component folder i have an index.js and export it as named, like so:
export { default as wrapper } from '.wrapper';
Compared to:
Import all the files as named imports from the same source, like so:
import {
wrapper,
error,
products,
} from './components';
In components folder i have an index where i gather and export all the files, like so:
export { wrapper } from '...';
export { error } from '...';
export { products } from '...';
According to the ES262 specification, import and export statements just provide information about dependencies between modules to the engine. How the modules are actually loaded in the end is up to the engine (there are a few constraints though). So whether there is actually a difference between importing from the source vs. importing a reexport depends on the environment.
Whatsoever the differences are probably irrelevant. Choose what works best for you.
I'm a fan of that approach. I like to split some components into folder and only expose what I want to the rest of my application. I really don't think that impact the perf on dev. (Obviously, there is absolutely no difference on prod as the whole project is pack in one file)

Correct way to import lodash

I had a pull request feedback below, just wondering which way is the correct way to import lodash?
You'd better do import has from 'lodash/has'.. For the earlier version
of lodash (v3) which by itself is pretty heavy, we should only import
a specidic module/function rather than importing the whole lodash
library. Not sure about the newer version (v4).
import has from 'lodash/has';
vs
import { has } from 'lodash';
Thanks
import has from 'lodash/has'; is better because lodash holds all it's functions in a single file, so rather than import the whole 'lodash' library at 100k, it's better to just import lodash's has function which is maybe 2k.
If you are using webpack 4, the following code is tree shakable.
import { has } from 'lodash-es';
The points to note;
CommonJS modules are not tree shakable so you should definitely use lodash-es, which is the Lodash library exported as ES Modules, rather than lodash (CommonJS).
lodash-es's package.json contains "sideEffects": false, which notifies webpack 4 that all the files inside the package are side effect free (see https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free).
This information is crucial for tree shaking since module bundlers do not tree shake files which possibly contain side effects even if their exported members are not used in anywhere.
Edit
As of version 1.9.0, Parcel also supports "sideEffects": false, threrefore import { has } from 'lodash-es'; is also tree shakable with Parcel.
It also supports tree shaking CommonJS modules, though it is likely tree shaking of ES Modules is more efficient than CommonJS according to my experiment.
Import specific methods inside of curly brackets
import { map, tail, times, uniq } from 'lodash';
Pros:
Only one import line(for a decent amount of functions)
More readable usage: map() instead of _.map() later in the javascript code.
Cons:
Every time we want to use a new function or stop using another - it needs to be maintained and managed
Copied from:The Correct Way to Import Lodash Libraries - A Benchmark article written by Alexander Chertkov.
You can import them as
import {concat, filter, orderBy} from 'lodash';
or as
import concat from 'lodash/concat';
import orderBy from 'lodash/orderBy';
import filter from 'lodash/filter';
the second one is much optimized than the first because it only loads the needed modules
then use like this
pendingArray: concat(
orderBy(
filter(payload, obj => obj.flag),
['flag'],
['desc'],
),
filter(payload, obj => !obj.flag),
If you are using babel, you should check out babel-plugin-lodash, it will cherry-pick the parts of lodash you are using for you, less hassle and a smaller bundle.
It has a few limitations:
You must use ES2015 imports to load Lodash
Babel < 6 & Node.js < 4 aren’t supported
Chain sequences aren’t supported. See this blog post for alternatives.
Modularized method packages aren’t supported
I just put them in their own file and export it for node and webpack:
// lodash-cherries.js
module.exports = {
defaults: require('lodash/defaults'),
isNil: require('lodash/isNil'),
isObject: require('lodash/isObject'),
isArray: require('lodash/isArray'),
isFunction: require('lodash/isFunction'),
isInteger: require('lodash/isInteger'),
isBoolean: require('lodash/isBoolean'),
keys: require('lodash/keys'),
set: require('lodash/set'),
get: require('lodash/get'),
}
I think this answer can be used in any project easily and brings the best result with less effort.
For Typescript users, use as following :
// lodash.utils.ts
export { default as get } from 'lodash/get';
export { default as isEmpty } from 'lodash/isEmpty';
export { default as isNil } from 'lodash/isNil';
...
And can be used the same way as importing lodash :
//some-code.ts
import { get } from './path/to/lodash.utils'
export static function getSomething(thing: any): any {
return get(thing, 'someSubField', 'someDefaultValue')
}
Or if you prefer to keep the _ to avoid conflicts (ex. map from rxjs vs lodash)
//some-other-code.ts
import * as _ from './path/to/lodash.utils'
export static function getSomething(thing: any): any {
return _.get(thing, 'someSubField', 'someDefaultValue')
}
UPDATE :
Seems like the right way to export is :
export * as get from 'lodash/get';
export * as isEmpty from 'lodash/isEmpty';
export * as isNil from 'lodash/isNil';
...
But there is a weird collision with #types/lodash, I've removed this type package because I would get this error :
Module '"/../project/node_modules/#types/lodash/cloneDeep"' uses
'export =' and cannot be used with 'export *'.ts(2498)
UPDATE :
After some digging, I've turned tsconfig.json feature esModuleInterop to true, and it allows me to do the following :
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import isNil from 'lodash/isNil';
...
export { get, isEmpty, isNil, ... };
Note that this affects all your imports in your projects that has been defined as import * as lib from 'lib'. Follow the documentation to be sure it's suitable for you.
import { cloneDeep, groupBy } from 'lodash';
I think this is simpler when you don't need to convert array to lodash object by using _.
const groupData = groupBy(expandedData, (x) => x.room.name);
For those who want to keep using _ , then just import them like this:
import groupBy from 'lodash/groupBy';
import filter from 'lodash/filter';
import get from 'lodash/get';
window._ = {groupBy, filter, get};
I think the more cleaner way of importing lodash is just like this:-
import _ from 'lodash'
then you can use what ever you want just by using this underscore just like this:-
_.has()

Categories