ES6 import all files in a folder? [duplicate] - javascript

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()

Related

How do I group functions in ES6 Modules?

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"

How to export multiple components

I have a shared folder that I expose as an npm module.
inside that share folder I have a components folder with index.js that exports them as default:
export { default as AqMegaMenu } from "./megaMenu/AqMegaMenu.vue";
export { default as AqDetailsCard } from "./AqDetailsCard.vue";
export { default as AqDoughnutChart } from "./charts/AqDoughnutChart.vue";
...
My entry file for the library is main.js and i'd like to expose my entire components through it, so I import them in other projects like this:
import {Comp1, Comp2} from "#my/shared"
Currently I export them one by one in main.js:
export { default as AqDetailsCard } from "./components/AqDetailsCard.vue";
...
But i was wondering if there is a way to export the entire components directory using it's index.js
and still be able to import it in other projects like i showed above.
You may try something like this
import camelCase from 'lodash/camelCase'
const requireModule = require.context('./components', true, /\.vue$/)
const modules = {}
requireModule.keys().forEach(filename =>
{
const moduleName = camelCase(fileName.match(/([^\\\/]+)\.vue$/)[1]);
modules[moduleName] = requireModule(fileName)
})
export default modules

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('...');

Javascript es6 re-exports

EDIT
Maybe I found the problem, would like if someone could confirm the situation: It seems like test.js is importing index.js which is importing test.js, and export has not an stop infinite-loop inclusion... Is it right? Is there any workaround like Don't include this file if it's the calling one?
I'm facing a strange problem. I'm trying to import some objects from a re-export (tcomb-react-native is not relevant here as the problem is with import/export).
|-- index.js
|-- simpleTypes.js
|-- userType.js
index.js:
export { UserType, UserTypeBase } from './test';
export { TextMax9Type } from './simpleTypes';
simpleTypes.js:
import t from 'tcomb-form-native';
export const TextMax9Type = t.refinement(t.String, s => s.length <= 9);
test.js:
import t from 'tcomb-form-native';
// import { TextMax9Type } from './'; // <----- NOT WORKING!
import { TextMax9Type } from './simpleTypes'; // <----- OK no error
export const UserTypeBase = {
Name: TextMax9Type,
Surname: TextMax9Type,
};
export const UserType = t.struct(UserTypeBase);
NOT WORKING error:
Invalid argument props {} supplied to struct(props, [name]) combinator (expected a dictionary String-> Type)
So which is the problem with re-export that is exporting an empty object?

ES6 exporting/importing in index file

I am currently using ES6 in an React app via webpack/babel.
I am using index files to gather all components of a module and export them. Unfortunately, that looks like this:
import Comp1_ from './Comp1.jsx';
import Comp2_ from './Comp2.jsx';
import Comp3_ from './Comp3.jsx';
export const Comp1 = Comp1_;
export const Comp2 = Comp2_;
export const Comp3 = Comp3_;
So I can nicely import it from other places like this:
import { Comp1, Comp2, Comp3 } from './components';
Obviously that isn't a very nice solution, so I was wondering, if there was any other way. I don't seem to able to export the imported component directly.
You can easily re-export the default import:
export {default as Comp1} from './Comp1.jsx';
export {default as Comp2} from './Comp2.jsx';
export {default as Comp3} from './Comp3.jsx';
There also is a proposal for ES7 ES8 that will let you write export Comp1 from '…';.
Also, bear in mind that if you need to export multiple functions at once, like actions you can use
export * from './XThingActions';
Too late but I want to share the way that I resolve it.
Having model file which has two named export:
export { Schema, Model };
and having controller file which has the default export:
export default Controller;
I exposed in the index file in this way:
import { Schema, Model } from './model';
import Controller from './controller';
export { Schema, Model, Controller };
and assuming that I want import all of them:
import { Schema, Model, Controller } from '../../path/';
default export
// Default export (recommended)
export {default} from './module'
// Default export with alias
export {default as d1} from './module'
all export
// In >ES7, it could be
export * from './module'
// In >ES7, with alias
export * as d1 from './module'
function name export
// export by function names
export { funcName1, funcName2, …} from './module'
// export by aliases
export { funcName1 as f1, funcName2 as f2, …} from './module'
destructured object export
// export destructured object
export const { myVar, myFunction } = myObjectWithEverything
// export destructured object, with renaming
export const { v1: myVar, f1: myFunction } = myBigObject
with array
// it works with array as well
export const [ funcName1, funcName2 ] = myBigArray
More infos: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
Folder structure:
components|
|_ Nave.js
|_Another.js
|_index.js
Nav.js comp inside components folder
export {Nav}
index.js in component folder
export {Nav} from './Nav';
export {Another} from './Another';
import anywhere
import {Nav, Another} from './components'
I've been searching for years how to export modules as both, named exports, and default exports in modular JavaScript. After tons of experimenting, the solution I found is quite simple and efficient.
// index.js
export { default as Client } from "./client/Client.js";
export { default as Events } from "./utils/Events.js";
// Or export named exports
export { Client } from "./client/Client.js";
export { Events } from "./utils/Events.js";
export * as default from "./index.js";
This would allow each exported module to be imported in two ways:
// script.js
import { Client } from "index.js";
new Client();
import module from "index.js";
new module.Client();
// You could also do this if you prefer to do so:
const { Client } = module;
You can mess around with this to have it suit your needs, but it works for me. Hope it helps!
What worked for me was adding the type keyword:
export type { Comp1, Comp2 } from './somewhere';
Install #babel/plugin-proposal-export-default-from via:
yarn add -D #babel/plugin-proposal-export-default-from
In your .babelrc.json or any of the Configuration File Types
module.exports = {
//...
plugins: [
'#babel/plugin-proposal-export-default-from'
]
//...
}
Now you can export directly from a file-path:
export Foo from './components/Foo'
export Bar from './components/Bar'
Good Luck...

Categories

Resources