Apakah mungkin mengimpor modul dari semua file dalam direktori, menggunakan wildcard?

256

Dengan ES6, saya dapat mengimpor beberapa ekspor dari file seperti ini:

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

Namun, saya suka organisasi memiliki satu modul per file. Saya berakhir dengan impor seperti ini:

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

Saya ingin sekali dapat melakukan ini:

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

atau sesuatu yang serupa, dengan konvensi yang dipahami bahwa setiap file berisi satu ekspor default, dan setiap modul dinamai sama dengan file-nya.

Apakah ini mungkin?

Frambot
sumber
Ini mungkin. Silakan lihat dokumentasi modul untuk babel babeljs.io/docs/learn-es2015 ... yang diterjemahkan "import {sum, pi} dari" lib / math ";". Jawaban yang diterima tidak lagi valid. Harap perbarui.
Eduard Jacko
6
@ kresli Saya tidak berpikir Anda mengerti pertanyaannya. Dalam dokumen, lib/mathadalah file yang berisi banyak ekspor. Dalam pertanyaan saya, lib/math/adalah direktori yang berisi beberapa file, masing-masing berisi satu ekspor.
Frambot
2
Ok aku paham. Dalam hal itu Bergi benar. Maaf
Eduard Jacko

Jawaban:

231

Saya tidak berpikir ini mungkin, tetapi afaik resolusi nama modul hingga modul loader sehingga mungkin ada implementasi loader yang mendukung ini.

Sampai saat itu, Anda dapat menggunakan "file modul" antara lib/things/index.jsyang baru saja berisi

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

dan itu akan memungkinkan Anda untuk melakukannya

import {ThingA, ThingB, ThingC} from 'lib/things';
Bergi
sumber
6
Terima kasih untuk bantuannya. Saya bisa mendapatkan kerja ini dengan index.jstampak seperti: import ThingA from 'things/ThingA'; export {ThingA as ThingA}; import ThingB from 'things/ThingB'; export {ThingB as ThingB};. Mantra lain di index.jstidak mau mengalah.
Frambot
2
Hm, export * fromharusnya bekerja. Apakah Anda mencoba …from './ThingA'atau export ThingA from …? Apa pemuat modul yang Anda gunakan?
Bergi
7
Ya, jawaban asli Anda berfungsi jika setiap ThingA.js, ThingB.js, masing-masing diekspor dengan nama ekspor. Spot on.
Frambot
1
Apakah Anda harus menentukan file indeks atau Anda dapat menentukan hanya folder dan index.js yang akan dimuat?
Zorgatone
1
@Zorgatone: Itu tergantung pada pemuat modul yang Anda gunakan, tapi ya biasanya path folder akan cukup.
Bergi
128

Hanya variasi pada tema yang sudah disediakan dalam jawaban, tetapi bagaimana dengan ini:

Dalam Thing,

export default function ThingA () {}

Di things/index.js,

export {default as ThingA} from './ThingA'
export {default as ThingB} from './ThingB'
export {default as ThingC} from './ThingC'

Kemudian untuk mengkonsumsi semua hal di tempat lain,

import * as things from './things'
things.ThingA()

Atau hanya mengkonsumsi beberapa hal,

import {ThingA,ThingB} from './things'
Jed Richards
sumber
Ingin melihat jawaban @ wolfbiter? Tidak yakin mengapa ia mengklaim tanda kurung tidak berfungsi.
Bergi
@Bergi Ya setuju, saya tidak berpikir wolfbiter adalah ES6 yang valid. Mungkin dia menggunakan Babel versi lama, atau transpiler lain?
Jed Richards
Bagaimana ini dipindahkan? Mengimpor direktori tidak cocok index.jsuntuk saya. Saya menggunakan SystemJs + Babel
jasonzhao
2
Tidak bisakah Anda mengetik export ThingA from './ThingA'alih-alihexport {default as ThingA} from './ThingA'
Petr Peller
1
Apakah ini memanfaatkan tiga goncangan? jika saya mengimpor {ThingA} dari './things' apakah ThingB dan ThingC akan ditambahkan ke bundel?
Giorgio
75

Jawaban saat ini menyarankan solusi tetapi disadap saya mengapa ini tidak ada, jadi saya telah membuat sebuah babelplugin yang melakukan ini.

Instal menggunakan:

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

lalu tambahkan ke Anda .babelrcdengan:

{
    "plugins": ["wildcard"]
}

lihat repo untuk info pemasangan terperinci


Ini memungkinkan Anda untuk melakukan ini:

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

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

sekali lagi, repo berisi informasi lebih lanjut tentang apa sebenarnya yang dilakukannya, tetapi melakukannya dengan cara ini menghindari membuat index.jsfile dan juga terjadi pada waktu kompilasi untuk menghindari melakukan readdirs saat runtime.

Juga dengan versi yang lebih baru Anda dapat melakukan persis seperti contoh Anda:

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

kerjanya sama seperti di atas.

Downgoat
sumber
3
Peringatan, saya mengalami masalah serius dengan plugin ini. Masalah mungkin berasal dari caching internal, Anda akan mencabut rambut Anda, ketika kode Anda akan sempurna, tetapi skrip Anda tidak akan berfungsi dengan baik karena Anda menambahkan file ke ./lib/things;dan tidak diambil. Masalah yang ditimbulkannya konyol. Saya baru saja menyaksikan situasi, ketika mengubah file dengan import *membuat babel untuk mengambil file yang ditambahkan, tetapi mengubahnya kembali, membawa masalah kembali, seperti menggunakan kembali cache dari sebelum perubahan. Gunakan dengan hati-hati.
Łukasz Zaroda
@ ŁukaszZaroda babel memiliki cache internal ~/.babel.jsonyang menyebabkan perilaku aneh ini. Juga jika Anda menggunakan seperti watcher atau hot reload Anda harus menyimpan file baru sehingga akan dikompilasi ulang dengan daftar direktori baru
Downgoat
@Downgoat jadi bagaimana cara mengatasinya kecuali untuk menghapus cache babel? Dan btw. Saya kira komentar Anda tidak benar. Saya memiliki caching babel yang dinonaktifkan dan memiliki masalah besar dengan plugin itu.
Sama sekali
1
Btw kepada siapa pun yang memiliki masalah lebih lanjut, tambahkan bpwc clear-cachekarena webpack dan proses pembangunan lainnya masih akan melakukan cache secara diam-diam
Downgoat
Ini adalah ide yang bagus tetapi saya juga tidak bisa membuatnya bekerja. Mungkin konflik dengan kode flowtyped saya, saya tidak yakin, tapi saya mendapatkan `ReferensiError: Foo tidak didefinisikan 'tidak peduli bagaimana saya menyusun impor.
jlewkovich
13

Gugly gugly hebat! Ini lebih sulit daripada yang seharusnya.

Ekspor satu default datar

Ini adalah peluang bagus untuk menggunakan spread ( ...di { ...Matters, ...Contacts }bawah:

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

Kemudian, untuk menjalankan kode yang dikompilasi babel dari baris perintah (dari root proyek /):

$ 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]' }

Ekspor satu default mirip pohon

Jika Anda memilih untuk tidak menimpa properti, ubah:

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

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

Dan hasilnya adalah:

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

Ekspor beberapa nama ekspor tanpa standar

Jika Anda berdedikasi untuk KERING , sintaks pada impor juga berubah:

// imports/collections/index.js

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

Ini menciptakan 2 ekspor bernama tanpa ekspor default. Kemudian ubah:

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

console.log(Matters, Contacts);

Dan hasilnya:

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

Impor semua ekspor yang disebut

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

Perhatikan perusakan import { Matters, Contacts } from './collections'; pada contoh sebelumnya.

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

Dalam praktek

Diberikan file sumber ini:

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

Menciptakan /myLib/index.jsuntuk menggabungkan semua file mengalahkan tujuan impor / ekspor. Akan lebih mudah untuk membuat semuanya mendunia sejak awal, daripada membuat semuanya mendunia melalui impor / ekspor melalui index.js "file pembungkus".

Jika Anda menginginkan file tertentu, import thingA from './myLib/thingA';dalam proyek Anda sendiri.

Membuat "file pembungkus" dengan ekspor untuk modul hanya masuk akal jika Anda mengemas untuk npm atau pada proyek multi-tim multi-tahun.

Sampai sejauh ini? Lihat dokumen untuk detail lebih lanjut.

Juga, yay untuk Stackoverflow akhirnya mendukung tiga `s sebagai markup pagar kode.

Michael Cole
sumber
10

Anda dapat menggunakan impor async ():

import fs = require('fs');

lalu:

fs.readdir('./someDir', (err, files) => {
 files.forEach(file => {
  const module = import('./' + file).then(m =>
    m.callSomeMethod();
  );
  // or const module = await import('file')
  });
});
mr_squall
sumber
2
Impor dinamis bagus seperti itu. Mereka benar-benar tidak ada ketika pertanyaan diajukan. Terima kasih atas jawabannya.
Frambot
6

Mirip dengan pertanyaan yang diterima tetapi memungkinkan Anda untuk menskalakan tanpa perlu menambahkan modul baru ke file indeks setiap kali Anda membuatnya:

./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'
Nicolas
sumber
Ini tidak berfungsi untuk saya ketika mencoba mengimpor sebagai alias di./example.js
tsujp
Aku juga tidak bekerja untukku (webpack 4.41, babel 7.7)
Edwin Joassart
3

Saya telah menggunakannya beberapa kali (khususnya untuk membangun objek besar yang memisahkan data ke banyak file (mis. AST node)), untuk membangunnya saya membuat skrip kecil (yang baru saja saya tambahkan ke npm sehingga semua orang lain dapat menggunakannya).

Penggunaan (saat ini Anda harus menggunakan babel untuk menggunakan file ekspor):

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

Menghasilkan file yang mengandung:

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

Maka Anda cukup mengonsumsi file:

import * as myCoolModule from "my-cool-module.js"
myCoolModule.foo()
Jamesernator
sumber
Tidak berfungsi dengan benar di windows, menghasilkan path sebagai path windows ( \` instead of / - ) also as an improvment you may want to allow two options like --filename` && --destuntuk memungkinkan penyesuaian di mana file yang dibuat harus disimpan dan dengan nama yang juga tidak berfungsi dengan nama file yang mengandung .(seperti user.model.js)
Yuri Scarbaci
2

Hanya pendekatan lain untuk jawaban @ Bergi

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

export default {
 ThingA,
 ThingB,
 ThingC
}

Penggunaan

import {ThingA, ThingB, ThingC} from './lib/things';
Ashok Vishwakarma
sumber
Itu tidak akan berhasil. Saya baru saja mencobanya di aplikasi reaksi dan kembali export '...' was not found in '.....
Hamid Mayeli
1

Anda dapat menggunakan persyaratan juga:

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

Kemudian gunakan modulHolder Anda dengan pengontrol yang dimuat secara dinamis:

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

Ini tidak persis apa yang Anda minta tetapi, dengan metode ini saya dapat beralih componentsListdi file saya yang lain dan menggunakan fungsi seperti componentsList.map(...)yang saya temukan sangat berguna!

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

Jika Anda menggunakan paket web. Ini mengimpor file secara otomatis dan mengekspor sebagai api namespace.

Jadi tidak perlu memperbarui pada setiap penambahan file.

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;

Untuk pengguna naskah;

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
atilkan
sumber
0

Saya dapat mengambil dari pendekatan atilkan pengguna dan memodifikasinya sedikit:

Untuk pengguna naskah;

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

}));
Justin Icenhour
sumber
-9

jika Anda tidak mengekspor default di A, B, C tetapi hanya mengekspor {} maka dimungkinkan untuk melakukannya

// 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()
hjl
sumber
1
Ini bukan javascript yang valid (tidak ada tanda kutip di sekitar ./thing) dan bahkan jika ada, itu tidak akan berhasil. (Saya mencobanya, dan itu tidak berhasil.)
John