Babel Glob Import
Babel plugin to import multiple files from one import statement using glob patterns.

Installation
npm i --save-dev babel-plugin-glob-import
Setup
import BabelGlobImport from 'babel-plugin-glob-import';
const babelGlobImport = BabelGlobImport({
debug: boolean,
})
Usage by examples
Let consider that the folder @server/routes
contains the following files:
index.ts
file1.ts
file2.ts
users/
index.ts
user1.ts
auth/
google.ts
facebook.ts
file-a1.ts
file-a2.ts
admin/
For all the examples below, we assume that these files export a Function
.
How to use glob patterns
Glob patterns are used to match multiple files based on specific rules. Here are the key features and their uses, adapted to the example folder structure:
These patterns can be combined and used to create powerful matching rules for your file import statements, allowing for flexible and efficient module imports.
The different ways to import modules
This plugin provides four different ways to import these files in one shot via glob patterns:
1. Import default
Import the default export of every file into one object.
import routes from '@server/routes/**/*.ts';
> console.log(routes)
{
'index': Function,
'file1': Function,
'file2': Function,
'users/index': Function,
'users/user1': Function,
'users/auth/google': Function,
'users/auth/facebook': Function,
'users/auth/file-a1': Function,
'users/auth/file-a2': Function,
'admin/index': Function,
'admin/settings': Function
}
With metadatas:
In addition of the modules, you can get the metadata of each import by prefixing the glob expression by metas:
.
import routes from 'metas:@server/routes/**/*.ts';
> console.log(routes)
{
'index': {
filename: '/root/server/routes/index.ts',
matches: [undefined, 'index'],
exports: Function
},
'file1': {
filename: '/root/server/routes/file1.ts',
matches: [undefined, 'file1'],
exports: Function
},
'file2': {
filename: '/root/server/routes/file2.ts',
matches: [undefined, 'file2'],
exports: Function
},
'users/index': {
filename: '/root/server/routes/users/index.ts',
matches: ['users', 'index'],
exports: Function
},
'users/user1': {
filename: '/root/server/routes/users/user1.ts',
matches: ['users', 'user1'],
exports: Function
},
'users/auth/google': {
filename: '/root/server/routes/users/auth/google.ts',
matches: ['users', 'auth', 'google'],
exports: Function
},
'users/auth/facebook': {
filename: '/root/server/routes/users/auth/facebook.ts',
matches: ['users', 'auth', 'facebook'],
exports: Function
},
'users/auth/file-a1': {
filename: '/root/server/routes/users/auth/file-a1.ts',
matches: ['users', 'auth', 'file-a1'],
exports: Function
},
'users/auth/file-a2': {
filename: '/root/server/routes/users/auth/file-a2.ts',
matches: ['users', 'auth', 'file-a2'],
exports: Function
},
'admin/index': {
filename: '/root/server/routes/admin/index.ts',
matches: ['admin', 'index'],
exports: Function
},
'admin/settings': {
filename: '/root/server/routes/admin/settings.ts',
matches: ['admin', 'settings'],
exports: Function
}
}
2. Import all (Typescript)
Import all exports of every module into one object.
import * as routes from '@server/routes/**/*.ts';
> console.log(routes)
{
'index': { default: Function },
'file1': { default: Function },
'file2': { default: Function },
'users/index': { default: Function },
'users/user1': { default: Function },
'users/auth/google': { default: Function },
'users/auth/facebook': { default: Function },
'users/auth/file-a1': { default: Function },
'users/auth/file-a2': { default: Function },
'admin/index': { default: Function },
'admin/settings': { default: Function }
}
With metadatas:
import * as routes from 'metas:@server/routes/**/*.ts';
> console.log(routes)
{
'index': {
filename: '/root/server/routes/index
.ts',
matches: [undefined, 'index'],
exports: { default: Function }
},
'file1': {
filename: '/root/server/routes/file1.ts',
matches: [undefined, 'file1'],
exports: { default: Function }
},
'file2': {
filename: '/root/server/routes/file2.ts',
matches: [undefined, 'file2'],
exports: { default: Function }
},
'users/index': {
filename: '/root/server/routes/users/index.ts',
matches: ['users', 'index'],
exports: { default: Function }
},
'users/user1': {
filename: '/root/server/routes/users/user1.ts',
matches: ['users', 'user1'],
exports: { default: Function }
},
'users/auth/google': {
filename: '/root/server/routes/users/auth/google.ts',
matches: ['users', 'auth', 'google'],
exports: { default: Function }
},
'users/auth/facebook': {
filename: '/root/server/routes/users/auth/facebook.ts',
matches: ['users', 'auth', 'facebook'],
exports: { default: Function }
},
'users/auth/file-a1': {
filename: '/root/server/routes/users/auth/file-a1.ts',
matches: ['users', 'auth', 'file-a1'],
exports: { default: Function }
},
'users/auth/file-a2': {
filename: '/root/server/routes/users/auth/file-a2.ts',
matches: ['users', 'auth', 'file-a2'],
exports: { default: Function }
},
'admin/index': {
filename: '/root/server/routes/admin/index.ts',
matches: ['admin', 'index'],
exports: { default: Function }
},
'admin/settings': {
filename: '/root/server/routes/admin/settings.ts',
matches: ['admin', 'settings'],
exports: { default: Function }
}
}
3. Import with destructuration
Import the default export of every module separately.
import { index, file1, file2, users_index, users_user1, users_auth_google, users_auth_facebook, users_auth_file_a1, users_auth_file_a2, admin_index, admin_settings } from '@server/routes/**/*.ts';
> console.log({ index, file1, file2, users_index, users_user1, users_auth_google, users_auth_facebook, users_auth_file_a1, users_auth_file_a2, admin_index, admin_settings })
{
'index': Function,
'file1': Function,
'file2': Function,
'users_index': Function,
'users_user1': Function,
'users_auth_google': Function,
'users_auth_facebook': Function,
'users_auth_file_a1': Function,
'users_auth_file_a2': Function,
'admin_index': Function,
'admin_settings': Function
}
With metadatas:
import { index, file1, file2, users_index, users_user1, users_auth_google, users_auth_facebook, users_auth_file_a1, users_auth_file_a2, admin_index, admin_settings } from 'metas:@server/routes/**/*.ts';
> console.log({ index, file1, file2, users_index, users_user1, users_auth_google, users_auth_facebook, users_auth_file_a1, users_auth_file_a2, admin_index, admin_settings })
{
'index': {
filename: '/root/server/routes/index.ts',
matches: [undefined, 'index'],
exports: Function
},
'file1': {
filename: '/root/server/routes/file1.ts',
matches: [undefined, 'file1'],
exports: Function
},
'file2': {
filename: '/root/server/routes/file2.ts',
matches: [undefined, 'file2'],
exports: Function
},
'users_index': {
filename: '/root/server/routes/users/index.ts',
matches: ['users', 'index'],
exports: Function
},
'users_user1': {
filename: '/root/server/routes/users/user1.ts',
matches: ['users', 'user1'],
exports: Function
},
'users_auth_google': {
filename: '/root/server/routes/users/auth/google.ts',
matches: ['users', 'auth', 'google'],
exports: Function
},
'users_auth_facebook': {
filename: '/root/server/routes/users/auth/facebook.ts',
matches: ['users', 'auth', 'facebook'],
exports: Function
},
'users_auth_file_a1': {
filename: '/root/server/routes/users/auth/file-a1.ts',
matches: ['users', 'auth', 'file-a1'],
exports: Function
},
'users_auth_file_a2': {
filename: '/root/server/routes/users/auth/file-a2.ts',
matches: ['users', 'auth', 'file-a2'],
exports: Function
},
'admin_index': {
filename: '/root/server/routes/admin/index.ts',
matches: ['admin', 'index'],
exports: Function
},
'admin_settings': {
filename: '/root/server/routes/admin/settings.ts',
matches: ['admin', 'settings'],
exports: Function
}
}
4. Require
const routes = require("@server/routes/**/*.ts");
> console.log(routes)
[
Function,
Function,
Function,
Function,
Function,
Function,
Function,
Function,
Function,
Function,
Function
]
With metadatas:
const routes = require("metas:@server/routes/**/*.ts");
> console.log(routes)
[
{
filename: '/root/server/routes/index.ts',
matches: [undefined, 'index'],
exports: Function
},
{
filename: '/root/server/routes/file1.ts',
matches: [undefined, 'file1'],
exports: Function
},
{
filename: '/root/server/routes/file2.ts',
matches: [undefined, 'file2'],
exports: Function
},
{
filename: '/root/server/routes/users/index.ts',
matches: ['users', 'index'],
exports: Function
},
{
filename: '/root/server/routes/users/user1.ts',
matches: ['users', 'user1'],
exports: Function
},
{
filename: '/root/server/routes/users/auth/google.ts',
matches: ['users', 'auth', 'google'],
exports: Function
},
{
filename: '/root/server/routes/users/auth/facebook.ts',
matches: ['users', 'auth', 'facebook'],
exports: Function
},
{
filename: '/root/server/routes/users/auth/file-a1.ts',
matches: ['users', 'auth', 'file-a1'],
exports: Function
},
{
filename: '/root/server/routes/users/auth/file-a2.ts',
matches: ['users', 'auth', 'file-a2'],
exports: Function
},
{
filename: '/root/server/routes/admin/index.ts',
matches: ['admin', 'index'],
exports: Function
},
{
filename: '/root/server/routes/admin/settings.ts',
matches: ['admin', 'settings'],
exports: Function
}
]
Setup example with Webpack
import BabelGlobImport from 'babel-plugin-glob-import';
const JavascriptRules = {
test: /\.(ts|tsx|js|jsx)$/,
rules: [{
loader: 'babel-loader',
options: {
plugins: [
BabelGlobImport({
debug: true,
})
]
}
}]
}
module.exports = {
name: 'server',
target: 'node',
entry: './src/server/index.ts',
...
module: {
rules: [JavascriptRules]
}
}
Fix Typescript errors
Typescript will not recognize your glob importations statements, and will show show you errors about missing files.
To fix that, you have to create a type definitions file (ex: global.d.ts
) and to manually define typings for the imported glob into this file:
declare module "@server/routes/**/*.ts" {
const Route: import("@server/services/router").Route;
export = Route;
}
If you imports metadatas, you can use the GlobImportedWithMetas
generic:
declare module "metas:@server/routes/**/*.ts" {
const Route: import("@server/services/router").Route;
const GlobImportedWithMetas: import('babel-plugin-glob-import').GlobImportedWithMetas;
export = GlobImportedWithMetas<Route>;
}
Importation transformers
This plugin also allows you to transform importation statements.
You can define a list of custom transformation rules while initializing the plugin:
BabelGlobImport({
debug: boolean,
}, [{
test: (request: TUnresolvedRequest) => boolean,
replace: (
request: TRequest,
that were matched by the importation request
matches: FileMatch[],
t: typeof types
) => types.Statement[] | void
}])
Example
This example replaces import models from "@models"
by import models from "@server/models/index.ts";
BabelGlobImport({ }, [{
name: 'Models import shortcut',
test: (request) => (
request.type === 'import'
&&
request.imported.type === 'default'
&&
request.source === '@models'
),
replace: (request, matches, t) => {
return t.importDeclaration(
[t.importDefaultSpecifier( t.identifier( request.imported.name ))],
t.stringLiteral("@server/models/index.ts")
)
}
}])
Changelog
16/07/2024
- Fix conflict between import names
15/07/2024
- Added support for using regex and other shortcuts in glob patterns
28/06/2024
- Fixed potential naming conflict between two imports. An error was triggered when you import two different folders having the same structure.
13/07/2023
- Fixed issue with simple imports without specifiers
import 'my/glob/path/*.ts
was crashing
07/02/2023
- Fixed issue where generated importation statement was invalid (the imported name was empty)
06/01/2023
- Add a comment before an import to debug it. Ex:
import * from 'metas:./**/*.ts';
- Possibility to add a name to each transformer to improve debugging
01/01/2023
- Export the plugin factory via module.exports
- Added possibility to get importation metadata (by prefixing the glob path by
metas:
) - Code cleanup & restructuration
- Fix bad characters in importation names
TODO
- Cleanup the code
- Improve debugging
- Improve the doc with more examples