How do I group functions in ES6 Modules? - javascript

How do you group functions in a ES6 Module package? In CommonJS packages you can do this.
// package.js
const startSession = require("./startSession");
module.exports = {
hooks: {
startSession,
},
};
// client.js
const { hooks: {startSession } } = require("package");
// OR
const { startSession } = require("package").hooks;
It looks like the similar syntax in ES6 code isn't supported.
import { hooks: {startSession } } from "packageX";
On source for that, is this babel issue from 2016. https://github.com/babel/babel/issues/4996
Another is the official documentation, that doesn't really mentioning this kind of syntax.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
Solutions
One solutions in ES6 javascript is this.
// package.js
import startSession from "./hooks/start-session";
export const hooks = {
startSession
};
// client.js
import { hooks } from "package";
const { startSession } = hooks;
But I would like to write the import in one line. Maybe not possible?
This is not what I look for.
import { hooks } from "package"; const { startSession } = hooks;
This kind of syntax would be nice, but how do you set up the packages, if possible?
import { startSession } from "packageX/hooks";
import { startSession } from "packageX".hooks;
Any other suggestions?
Answer
Estus below pushed me in the right direction. Here is a more detailed answer.
// package.json
"main": "src/index.js",
"exports": {
".": "./src/index.js",
"./hooks": "./src/hooks/index.js",
}
// src/hooks/start-session.js
export default () => {}
// src/hooks/index.js
export * as startSession from "./start-session";
// src/index.js
export const doSomething () => {}
// client.js
import { doSomething } from "packageX";
import { startSession } from "packageX/hooks";

ES modules are supposed to be statically analyzed so nested imports that result from expressions are impossible. Import syntax isn't destructuring, just looks similarly. It is strictly specified; if a feature isn't there, it's not supported.
This should be preferably avoided as this prevents hooks properties from being tree-shaken (not applicable to Node currently).
For an entry point or barrel module, prefixes can be used to give an export a scope:
export { default as hookStartSession } from "./hooks/start-session";
And this is a way this is usually done when an import has some scope, at least if a package exposes additional public entry point.
import { startSession } from "packageX/hooks"

Related

How to use vue-toastification

I just migrated my project created in vue 3 to nuxt 3. Previously I used the vue-toastification module but now I don't know how to import it correctly. My code using this module.
import { useToast, POSITION } from 'vue-toastification'
const toast = useToast()
export default {
methods: {
copy(text) {
toast.success('Copied!', {
timeout: 2000,
position: POSITION.BOTTOM_CENTER,
})
navigator.clipboard.writeText(text)
}
}
}
In Vue I had to do app.use(Toast) but Nuxt does not have an index.js file. Adding modules: ['vue-toastification/nuxt'] in nuxt.config.js does not work because I get an error.
Answers suggested by kissu and Ricardo de Paula worked for me while I was using development server (npm run dev).
After building and running the project I encountered error 500:
Named export 'useToast' not found. The requested module 'vue-toastification' is a CommonJS module, which may not support all module.exports as named exports. CommonJS modules can always be imported via the default export, for example using: import pkg from 'vue-toastification';
To fix this, I registered toast as plugin helper (I'm using Nuxt 3.1.1 with Nitro 2.1.1):
Inside vue-toastificaton.client.js:
import { defineNuxtPlugin } from '#app'
import * as vt from 'vue-toastification'
import '#/assets/css/toast.scss'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(vt.default)
return {
provide: {
toast: vt.useToast()
}
}
})
Then in my component script setup:
//throws an error after production build
//import { useToast } from 'vue-toastification'
//const toast = useToast()
//toast.success('Yay!!!')
//works after production build
const { $toast } = useNuxtApp()
$toast.success('Yay!!!')
If you want it to be available globally, you can install it as a Nuxt plugin as explained in the official docs or in this other answer.
vue-toastification is probably a client-side only plugin, hence you would probably want to use it as
/plugins/vue-toastificaton.client.js like this
import { defineNuxtPlugin } from '#app'
import Toast from "vue-toastification"
import "vue-toastification/dist/index.css" // if needed
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.use(Toast)
})
Then, you should be able to use it in your components with either Composition API or Options API (I think).
I was wanting to do the same thing. I read kissu's answer and did the following:
1 - I created a folder for the puglin - plugins
2 - Inside the plugins folder I created a file called vue-toastificaton.client.js
Inside vue-toastificaton.client.js:
import { defineNuxtPlugin } from '#app'
import Toast from 'vue-toastification'
import 'vue-toastification/dist/index.css' // if needed
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(Toast)
})
And I used it that way:
<script setup>
import { useToast } from 'vue-toastification'
const toast = useToast()
const onSubmit = () => {
// use the toast notification plugin to show a success message
toast.success('Hello world!')
}
</script>

what's the difference between import "name" from "module" and import * as "name" from "module"?

The code on my app is like this.
import { HttpExceptionFilter } from './common/exceptions/http-exception.filter';
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '#nestjs/common';
import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '#nestjs/swagger';
//1.
import * as expressBasicAuth from 'express-basic-auth';
//2.
//import expressBasicAuth from 'express-basic-auth'; => promise unhandled error
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
app.useGlobalFilters(new HttpExceptionFilter());
app.use(
['/docs', '/docs-json'],
expressBasicAuth({
challenge: true,
users: {
[process.env.SWAGGER_USER]: process.env.SWAGGER_PASSWORD,
},
}),
);
};
I want to know the difference between 1 and 2.
Because When I run my app using 2(9 lines), there was promise unhandled error.
I want to know how those two ways work. I referred to the official document of mozilla, but I couldn't understand it well.
I'd appreciate it if you could answer me.
First let's just understand how imports works
so consider i have a file with the following exports
// file name -> modules
export const add = (x, y) => x + y;
export const subtract = (x, y) => x - y;
export default (x, y) => x * y;
now i can import all these functions on another file like this
import defaultFunction, { add, subtract } from './modules.js';
console.log(add(1, 2)) // 3
console.log(subtract(2, 1)) // 1
console.log(defaultFunction(2, 2)) // 2 * 2 = 4
or i can use import * as <name> from <file/package> to import all the exports inside a file.
like this
import * as myModules from './modules.js';
// NOTE: this will only give you add and subtract functions but not the default one
// now to actually access the default function you have to use
// import * as <name> from <file/package>
// <name>.default to access the default export
// or simply
// import default, { } from <file/package>
console.log(myModules.add(1, 2)) // 3
console.log(myModules.subtract(2, 1)) // 1
console.log(myModules.default(2, 2)) // 2 * 2 = 4
// or i can destructure the "myModules" import
const { add, subtract } = myModules;
Conclusion
I think the problem is that the library that you're using is not providing a default export to actually use import expressBasicAuth from 'express-basic-auth'; or it might be exporting different functions for named exports and default exports
As per your code:
import * as expressBasicAuth from ...
Imports all export const variable1...; export const variable2...; items as a single item.
import expressBasicAuth from ...
Imports a single export default item if there was one defined inside the library.
Depending on the library implementation, there might be totally different functions exported in those two ways.
Most of the time, the module exports multiple things
There are two great ways to import from another module when the module exports an object with properties. This is the common case.
Import the whole module, giving it a name:
import * as child_process from "child_process";
// then later...
child_process.spawn(...);
or pick the names you want to import:
import { spawn } from "child_process";
// then later
spawn(...);
sometimes the module exports just one thing. typescript has the concept of export default
If a module declares a default export, then you must bring it in like this:
import thing from "thing";
Now you have a function or a class (whatever its default export is) in the thing.
More commonly in JavaScript (CommonJS?) modules, a module author will override module.exports to a function or class instead of adding properties to the exports object like a ES-module would.
so in common js we have
const expressBasicAuth = require(express-basic-auth);
and in typescript :
import * as expressBasicAuth from 'express-basic-auth';

Mixing default and named exports with Rollup

I am writing a Bluetooth library for Node.js using TypeScript and Rollup. I want to enable users to import my libraries components in these ways
import Sblendid from "#sblendid/sblendid";
import Sblendid, { Peripheral } from "#sblendid/sblendid";
const Sblendid = require("#sblendid/sblendid");
const { Peripheral } = require("#sblendid/sblendid");
My project structure looks like this:
root
∟ rollup.config.ts
∟ src
∟ index.ts
∟ sblendid.ts
∟ peripheral.ts
And the according code is this:
index.ts
export {
default,
} from "./sblendid";
export {
default as Peripheral,
} from "./peripheral";
sblendid.ts
export default class Sblendid {}
peripheral.ts
export default class Peripheral {}
I am bundling everything with Rollup and my entire config is this:
import typescript from "typescript";
import commonjs from "rollup-plugin-commonjs";
import resolve from "rollup-plugin-node-resolve";
import typescriptPlugin from "rollup-plugin-typescript2";
import autoExternal from "rollup-plugin-auto-external";
import { terser } from "rollup-plugin-terser";
import pkg from "./package.json";
export default {
input: "src/index.ts",
output: [
{
file: pkg.main,
format: "cjs",
sourcemap: true
},
{
file: pkg.module,
format: "es",
sourcemap: true
}
],
plugins: [
autoExternal(),
resolve({ preferBuiltins: true }),
commonjs(),
typescriptPlugin({ typescript, objectHashIgnoreUnknownHack: true }),
terser()
]
};
You can find the entire code here
https://github.com/LukasBombach/sblendid/tree/master/packages/sblendid
Now, this setup does not work. Rollup tells me
$ rollup -c rollup.config.ts
src/index.ts → dist/index.cjs.js, dist/index.es.js...
(!) Mixing named and default exports
Consumers of your bundle will have to use bundle['default'] to access the default export, which may not be what you want. Use `output.exports: 'named'` to disable this warning
which is true. This
const Sblendid = require("#sblendid/sblendid");
simply does not work. What I have to do is this:
const Sblendid = require("#sblendid/sblendid").default;
I can fix this behavior by not mixing named ad default exports, ok, but then I lose the ability to do this:
import Sblendid, { Peripheral } from "#sblendid/sblendid";
So I am wondering. Is there any way, maybe using multiple bundles, I can achieve having users be able to do both:
// This
import Sblendid from "#sblendid/sblendid";
import Sblendid, { Peripheral } from "#sblendid/sblendid";
// And this
const Sblendid = require("#sblendid/sblendid");
const { Peripheral } = require("#sblendid/sblendid");
If you target only nodejs environment you can export like this (index.ts)
import Sblendid from "./sblendid";
import Peripheral from "./peripheral";
Sblendid.Peripheral = Peripheral;
export default Sblendid;
Commonjs does not have the concept of default export. When you are able do:
const Splendid = require("#sblendid/sblendid");
const { Peripheral } = require("#sblendid/sblendid");
It does mean that
assert.equal(Splendid.Peripheral, Peripheral);
That Peripheral is a property of Splendid.
This is basically achieved by
Splendid.Peripheral = /* something */;
module.exports = Splendid;
When cjs code is transpiled from esm code (what rollup does) the only choice is to introduce a default property on the exports object.
If you're not comfortable with adding properties just for the sake of exporting, add a snipped like this to your docs.
const { default: Splendid, Peripheral } = require('...');

Allow export of Client, Server and Common modules independently in a single typescript npm package

I have a project (lets call it FlatEarth) with client-only, server-only and common components. I would like to create a single npm package that allows importing from only the relevant portions.
For example, a client-side only project would be able to do the following:
import { FeCommon, FeClient } from 'FlatEarth'.
The issue that I am facing is that even though I am only importing the FeClient and FeCommon components, the FeServer components (request-promise) will cause errors in the build process (since they are not compatible with client solutions) since they are still being imported by FlatEarth's index.ts file.
The FlatEarth setup is essentially:
index.ts
import * as FeCommonStatic from './FeCommon';
import * as FeServerStatic from './FeServer';
import * as FeClientStatic from './FeClient';
export { FeCommonStatic as FeCommon };
export { FeServerStatic as FeServer };
export { FeClientStatic as FeClient };
FeCommon.ts
import * as stringStatic from './String';
import * as uriStatic from './Uri';
export { stringStatic as string };
export { uriStatic as uri };
FeServer.ts
import { ServerRequestHandler } from './Server/ServerRequestHandler'; // Depends on request-promise.
export { ServerRequestHandler as RequestHandler }
FeClient.ts
import { ClientRequestHandler } from './Client/ClientRequestHandler'; // Depends on jquery
export { ClientRequestHandler as RequestHandler }

ES6 import all files in a folder? [duplicate]

With ES6, I can import several exports from a file like this:
import {ThingA, ThingB, ThingC} from 'lib/things';
However, I like the organization of having one module per file. I end up with imports like this:
import ThingA from 'lib/things/ThingA';
import ThingB from 'lib/things/ThingB';
import ThingC from 'lib/things/ThingC';
I would love to be able to do this:
import {ThingA, ThingB, ThingC} from 'lib/things/*';
or something similar, with the understood convention that each file contains one default export, and each module is named the same as its file.
Is this possible?
I don't think this is possible, but afaik the resolution of module names is up to module loaders so there might a loader implementation that does support this.
Until then, you could use an intermediate "module file" at lib/things/index.js that just contains
export * from 'ThingA';
export * from 'ThingB';
export * from 'ThingC';
and it would allow you to do
import {ThingA, ThingB, ThingC} from 'lib/things';
Just a variation on the theme already provided in the answer, but how about this:
In a Thing,
export default function ThingA () {}
In things/index.js,
export {default as ThingA} from './ThingA'
export {default as ThingB} from './ThingB'
export {default as ThingC} from './ThingC'
Then to consume all the things elsewhere,
import * as things from './things'
things.ThingA()
Or to consume just some of things,
import {ThingA,ThingB} from './things'
The current answers suggest a workaround but it's bugged me why this doesn't exist, so I've created a babel plugin which does this.
Install it using:
npm i --save-dev babel-plugin-wildcard
then add it to your .babelrc with:
{
"plugins": ["wildcard"]
}
see the repo for detailed install info
This allows you to do this:
import * as Things from './lib/things';
// Do whatever you want with these :D
Things.ThingA;
Things.ThingB;
Things.ThingC;
again, the repo contains further information on what exactly it does, but doing it this way avoids creating index.js files and also happens at compile-time to avoid doing readdirs at runtime.
Also with a newer version you can do exactly like your example:
import { ThingsA, ThingsB, ThingsC } from './lib/things/*';
works the same as the above.
You now can use async import():
import fs = require('fs');
and then:
fs.readdir('./someDir', (err, files) => {
files.forEach(file => {
const module = import('./' + file).then(m =>
m.callSomeMethod();
);
// or const module = await import('file')
});
});
Great gugly muglys! This was harder than it needed to be.
Export one flat default
This is a great opportunity to use spread (... in { ...Matters, ...Contacts } below:
// imports/collections/Matters.js
export default { // default export
hello: 'World',
something: 'important',
};
// imports/collections/Contacts.js
export default { // default export
hello: 'Moon',
email: 'hello#example.com',
};
// imports/collections/index.js
import Matters from './Matters'; // import default export as var 'Matters'
import Contacts from './Contacts';
export default { // default export
...Matters, // spread Matters, overwriting previous properties
...Contacts, // spread Contacts, overwriting previosu properties
};
// imports/test.js
import collections from './collections'; // import default export as 'collections'
console.log(collections);
Then, to run babel compiled code from the command line (from project root /):
$ npm install --save-dev #babel/core #babel/cli #babel/preset-env #babel/node
(trimmed)
$ npx babel-node --presets #babel/preset-env imports/test.js
{ hello: 'Moon',
something: 'important',
email: 'hello#example.com' }
Export one tree-like default
If you'd prefer to not overwrite properties, change:
// imports/collections/index.js
import Matters from './Matters'; // import default as 'Matters'
import Contacts from './Contacts';
export default { // export default
Matters,
Contacts,
};
And the output will be:
$ npx babel-node --presets #babel/preset-env imports/test.js
{ Matters: { hello: 'World', something: 'important' },
Contacts: { hello: 'Moon', email: 'hello#example.com' } }
Export multiple named exports w/ no default
If you're dedicated to DRY, the syntax on the imports changes as well:
// imports/collections/index.js
// export default as named export 'Matters'
export { default as Matters } from './Matters';
export { default as Contacts } from './Contacts';
This creates 2 named exports w/ no default export. Then change:
// imports/test.js
import { Matters, Contacts } from './collections';
console.log(Matters, Contacts);
And the output:
$ npx babel-node --presets #babel/preset-env imports/test.js
{ hello: 'World', something: 'important' } { hello: 'Moon', email: 'hello#example.com' }
Import all named exports
// imports/collections/index.js
// export default as named export 'Matters'
export { default as Matters } from './Matters';
export { default as Contacts } from './Contacts';
// imports/test.js
// Import all named exports as 'collections'
import * as collections from './collections';
console.log(collections); // interesting output
console.log(collections.Matters, collections.Contacts);
Notice the destructuring import { Matters, Contacts } from './collections'; in the previous example.
$ npx babel-node --presets #babel/preset-env imports/test.js
{ Matters: [Getter], Contacts: [Getter] }
{ hello: 'World', something: 'important' } { hello: 'Moon', email: 'hello#example.com' }
In practice
Given these source files:
/myLib/thingA.js
/myLib/thingB.js
/myLib/thingC.js
Creating a /myLib/index.js to bundle up all the files defeats the purpose of import/export. It would be easier to make everything global in the first place, than to make everything global via import/export via index.js "wrapper files".
If you want a particular file, import thingA from './myLib/thingA'; in your own projects.
Creating a "wrapper file" with exports for the module only makes sense if you're packaging for npm or on a multi-year multi-team project.
Made it this far? See the docs for more details.
Also, yay for Stackoverflow finally supporting three `s as code fence markup.
Similar to the accepted answer but it allows you to scale without the need of adding a new module to the index file each time you create one:
./modules/moduleA.js
export const example = 'example';
export const anotherExample = 'anotherExample';
./modules/index.js
// require all modules on the path and with the pattern defined
const req = require.context('./', true, /.js$/);
const modules = req.keys().map(req);
// export all modules
module.exports = modules;
./example.js
import { example, anotherExample } from './modules'
If you are using webpack. This imports files automatically and exports as api namespace.
So no need to update on every file addition.
import camelCase from "lodash-es";
const requireModule = require.context("./", false, /\.js$/); //
const api = {};
requireModule.keys().forEach(fileName => {
if (fileName === "./index.js") return;
const moduleName = camelCase(fileName.replace(/(\.\/|\.js)/g, ""));
api[moduleName] = {
...requireModule(fileName).default
};
});
export default api;
For Typescript users;
import { camelCase } from "lodash-es"
const requireModule = require.context("./folderName", false, /\.ts$/)
interface LooseObject {
[key: string]: any
}
const api: LooseObject = {}
requireModule.keys().forEach(fileName => {
if (fileName === "./index.ts") return
const moduleName = camelCase(fileName.replace(/(\.\/|\.ts)/g, ""))
api[moduleName] = {
...requireModule(fileName).default,
}
})
export default api
I've used them a few times (in particular for building massive objects splitting the data over many files (e.g. AST nodes)), in order to build them I made a tiny script (which I've just added to npm so everyone else can use it).
Usage (currently you'll need to use babel to use the export file):
$ npm install -g folder-module
$ folder-module my-cool-module/
Generates a file containing:
export {default as foo} from "./module/foo.js"
export {default as default} from "./module/default.js"
export {default as bar} from "./module/bar.js"
...etc
Then you can just consume the file:
import * as myCoolModule from "my-cool-module.js"
myCoolModule.foo()
Just an other approach to #Bergi's answer
// lib/things/index.js
import ThingA from './ThingA';
import ThingB from './ThingB';
import ThingC from './ThingC';
export default {
ThingA,
ThingB,
ThingC
}
Uses
import {ThingA, ThingB, ThingC} from './lib/things';
Nodejs ? Do like this:
Create a folder with index.js, in index file, add this:
var GET = require('./GET');
var IS = require('./IS');
var PARSE = require('./PARSE');
module.exports = { ...GET, ...IS, ...PARSE};
And, in file GET.js, or IS.js export as normal:
module.exports = { /* something as you like */}
ANd now, you need only including index.js like:
const Helper = require('./YourFolder');
Helper will include all of function in YourFolder.
Good day!
This is not exactly what you asked for but, with this method I can Iterate throught componentsList in my other files and use function such as componentsList.map(...) which I find pretty usefull !
import StepOne from './StepOne';
import StepTwo from './StepTwo';
import StepThree from './StepThree';
import StepFour from './StepFour';
import StepFive from './StepFive';
import StepSix from './StepSix';
import StepSeven from './StepSeven';
import StepEight from './StepEight';
const componentsList= () => [
{ component: StepOne(), key: 'step1' },
{ component: StepTwo(), key: 'step2' },
{ component: StepThree(), key: 'step3' },
{ component: StepFour(), key: 'step4' },
{ component: StepFive(), key: 'step5' },
{ component: StepSix(), key: 'step6' },
{ component: StepSeven(), key: 'step7' },
{ component: StepEight(), key: 'step8' }
];
export default componentsList;
You can use require as well:
const moduleHolder = []
function loadModules(path) {
let stat = fs.lstatSync(path)
if (stat.isDirectory()) {
// we have a directory: do a tree walk
const files = fs.readdirSync(path)
let f,
l = files.length
for (var i = 0; i < l; i++) {
f = pathModule.join(path, files[i])
loadModules(f)
}
} else {
// we have a file: load it
var controller = require(path)
moduleHolder.push(controller)
}
}
Then use your moduleHolder with dynamically loaded controllers:
loadModules(DIR)
for (const controller of moduleHolder) {
controller(app, db)
}
I was able to take from user atilkan's approach and modify it a bit:
For Typescript users;
require.context('#/folder/with/modules', false, /\.ts$/).keys().forEach((fileName => {
import('#/folder/with/modules' + fileName).then((mod) => {
(window as any)[fileName] = mod[fileName];
const module = new (window as any)[fileName]();
// use module
});
}));
if you don't export default in A, B, C but just export {} then it's possible to do so
// things/A.js
export function A() {}
// things/B.js
export function B() {}
// things/C.js
export function C() {}
// foo.js
import * as Foo from ./thing
Foo.A()
Foo.B()
Foo.C()

Categories

Resources