web-dev-qa-db-fra.com

Est-il possible d'importer des modules de tous les fichiers d'un répertoire à l'aide d'un caractère générique?

Avec ES6, je peux importer plusieurs exportations à partir d'un fichier comme celui-ci:

import {ThingA, ThingB, ThingC} from 'lib/things';

Cependant, j'aime bien avoir un module par fichier. Je me retrouve avec des importations comme ceci:

import ThingA from 'lib/things/ThingA';
import ThingB from 'lib/things/ThingB';
import ThingC from 'lib/things/ThingC';

J'aimerais pouvoir faire ça:

import {ThingA, ThingB, ThingC} from 'lib/things/*';

ou quelque chose de similaire, avec la convention comprise que chaque fichier contient une exportation par défaut et que chaque module porte le même nom que son fichier.

Est-ce possible?

184
Joe Frambach

Je ne pense pas que cela soit possible, mais autant que la résolution des noms de modules revienne aux chargeurs de modules, une implémentation de chargeur qui prend en charge cela pourrait.

Jusque-là, vous pourriez utiliser un "fichier de module" intermédiaire à lib/things/index.js qui ne contient que

export * from 'ThingA';
export * from 'ThingB';
export * from 'ThingC';

et cela vous permettrait de faire

import {ThingA, ThingB, ThingC} from 'lib/things';
173
Bergi

Les réponses actuelles suggèrent une solution de contournement, mais le fait que cela n’existe pas, m’a énervé, j’ai donc créé un plugin babel qui le fait.

Installez-le en utilisant:

npm i --save-dev babel-plugin-wildcard

puis ajoutez-le à votre .babelrc avec:

{
    "plugins": ["wildcard"]
}

voir le repo pour des informations d'installation détaillées


Cela vous permet de faire ceci:

import * as Things from './lib/things';

// Do whatever you want with these :D
Things.ThingA;
Things.ThingB;
Things.ThingC;

là encore, le repo contient des informations supplémentaires sur ce qu’il fait exactement, mais le faire de cette façon évite de créer des fichiers index.js et se produit également au moment de la compilation pour éviter de faire readdirs à l’exécution.

Également avec une version plus récente, vous pouvez faire exactement comme votre exemple:

 import { ThingsA, ThingsB, ThingsC } from './lib/things/*';

fonctionne comme ci-dessus.

55
Downgoat

Je les ai utilisées plusieurs fois (notamment pour construire des objets volumineux fractionnant les données en plusieurs fichiers (par exemple, AST noeuds)), afin de les construire, j’ai créé un petit script (que je viens d’ajouter à npm pour que tout le monde puisse l’utiliser).

Utilisation (actuellement, vous devrez utiliser babel pour utiliser le fichier d'exportation):

$ npm install -g folder-module
$ folder-module my-cool-module/

Génère un fichier contenant:

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

Ensuite, vous pouvez simplement consommer le fichier:

import * as myCoolModule from "my-cool-module.js"
myCoolModule.foo()
2
Jamesernator

Juste une autre approche de la réponse de @ Bergi

// lib/things/index.js
import ThingA from './ThingA';
import ThingB from './ThingB';
import ThingC from './ThingC';

export default {
 ThingA,
 ThingB,
 ThingC
}

Les usages

import {ThingA, ThingB, ThingC} from './lib/things';
2

Super muglys gugly! C'était plus difficile que nécessaire.

Exporter une valeur par défaut

C'est une excellente occasion d'utiliser spread (... dans { ...Matters, ...Contacts } ci-dessous:

// imports/collections/Matters.js
export default {           // default export
  hello: 'World',
  something: 'important',
};
// imports/collections/Contacts.js
export default {           // default export
  hello: 'Moon',
  email: '[email protected]',
};
// 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);

Ensuite, pour exécutez le code compilé par babel à partir de la ligne de commande (à partir de la racine du projet /):

$ 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: '[email protected]' }

Exporter une arborescence par défaut

Si vous préférez ne pas écraser les propriétés, modifiez:

// imports/collections/index.js
import Matters from './Matters';     // import default as 'Matters'
import Contacts from './Contacts';

export default {   // export default
  Matters,
  Contacts,
};

Et le résultat sera:

$ npx babel-node --presets @babel/preset-env imports/test.js
{ Matters: { hello: 'World', something: 'important' },
  Contacts: { hello: 'Moon', email: '[email protected]' } }

Exporter plusieurs exportations nommées sans valeur par défaut

Si vous êtes dédié à DRY , la syntaxe des importations change également:

// imports/collections/index.js

// export default as named export 'Matters'
export { default as Matters } from './Matters';  
export { default as Contacts } from './Contacts'; 

Cela crée 2 exportations nommées avec aucune exportation par défaut. Puis changez:

// imports/test.js
import { Matters, Contacts } from './collections';

console.log(Matters, Contacts);

Et la sortie: 

$ npx babel-node --presets @babel/preset-env imports/test.js
{ hello: 'World', something: 'important' } { hello: 'Moon', email: '[email protected]' }

Importer toutes les exportations nommées

// 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);

Notez le destructuringimport { Matters, Contacts } from './collections'; dans l'exemple précédent.

$ npx babel-node --presets @babel/preset-env imports/test.js
{ Matters: [Getter], Contacts: [Getter] }
{ hello: 'World', something: 'important' } { hello: 'Moon', email: '[email protected]' }

En pratique

Étant donné ces fichiers source:

/myLib/thingA.js
/myLib/thingB.js
/myLib/thingC.js

La création d'un /myLib/index.js pour regrouper tous les fichiers annule le but de l'importation/exportation. En premier lieu, il serait plus facile de tout rendre global que de le faire via import/export via "fichiers wrapper" index.js.

Si vous voulez un fichier particulier, import thingA from './myLib/thingA'; dans vos propres projets.

La création d'un "fichier wrapper" avec des exportations pour le module n'a de sens que si vous empaquetez pour npm ou sur un projet multi-équipes pluriannuel.

Fait ça si loin? Voir le docs pour plus de détails.

Aussi, yay pour Stackoverflow supportant finalement trois `s comme balisage de clôture de code.

1
Michael Cole

Semblable à la question acceptée, mais cela vous permet d’échelle sans avoir à ajouter un nouveau module au fichier d’index chaque fois que vous en créez un:

./ 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'
1
Nicolas

Vous pouvez utiliser async import ():

import fs = require ('fs');

et alors:

fs.readdir('./someDir', (err, files) => {
 files.forEach(file => {
  const module = import('./' + file).then(m =>
    m.callSomeMethod();
  );
  // or const module = await import('file')
  });
});
1
mr_squall

Vous pouvez aussi utiliser require:

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

Puis utilisez votre moduleHolder avec des contrôleurs chargés dynamiquement:

  loadModules(DIR) 
  for (const controller of moduleHolder) {
    controller(app, db)
  }
0
mr_squall

Ce n’est pas exactement ce que vous aviez demandé mais, avec cette méthode, je peux naviguer dans componentsList dans mes autres fichiers et utiliser une fonction telle que componentsList.map(...) que je trouve assez utile!

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;
0
FlyingZipper