npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

babel-plugin-glob-import

v0.0.9-1

Published

Babel plugin to use glob patterns in import and require statements.

Downloads

294

Readme

Babel Glob Import

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

npm npm

Installation

npm i --save-dev babel-plugin-glob-import

Setup

// Import the plugin
import BabelGlobImport from 'babel-plugin-glob-import';

// Initialize the glob import Babel plugin
const babelGlobImport = BabelGlobImport({ 
    // (Optional): You can set it to true if you need debugging
    //  It will help you to better understand the different transformation steps, 
    //      and will print the output code in the logs
    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/
    • index.ts
    • settings.ts

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:

  • Wildcards:

    • *.ts matches all .ts files in the @server/routes directory.
      import routes from '@server/routes/*.ts';
      // Matches: index.ts, file1.ts, file2.ts
    • **/*.ts matches all .ts files in @server/routes and its subdirectories.
      import routes from '@server/routes/**/*.ts';
      // Matches: index.ts, file1.ts, file2.ts, users/index.ts, users/user1.ts, users/auth/google.ts, users/auth/facebook.ts, users/auth/file-a1.ts, users/auth/file-a2.ts, admin/index.ts, admin/settings.ts
  • Negation:

    • !users/*.ts matches all .ts files except those in the users directory.
      import routes from '@server/routes/!users/*.ts';
      // Matches: index.ts, file1.ts, file2.ts, admin/index.ts, admin/settings.ts
    • *!(index).ts matches all .ts files except index.ts.
      import routes from '@server/routes/*!(index).ts';
      // Matches: file1.ts, file2.ts
  • Extglobs (extended globbing):

    • +(users|admin) matches the users and admin directories.
      import routes from '@server/routes/+(users|admin)/**/*.ts';
      // Matches: users/index.ts, users/user1.ts, users/auth/google.ts, users/auth/facebook.ts, users/auth/file-a1.ts, users/auth/file-a2.ts, admin/index.ts, admin/settings.ts
    • !(auth) matches everything except the auth directory.
      import routes from '@server/routes/users/!(auth)/**/*.ts';
      // Matches: users/index.ts, users/user1.ts
  • POSIX character classes:

    • [[:alpha:][:digit:]] matches any alphabetic or numeric character. For example, to match files with alphabetic or numeric names.
      import routes from '@server/routes/[[:alpha:][:digit:]]/**/*.ts';
      // Matches: index.ts, file1.ts, file2.ts, users/index.ts, users/user1.ts, admin/index.ts, admin/settings.ts
  • Brace expansion:

    • Example: foo/{1..5}.ts matches foo/1.ts, foo/2.ts, foo/3.ts, foo/4.ts, and foo/5.ts.
    • Example: bar/{a,b,c}.ts matches bar/a.ts, bar/b.ts, and bar/c.ts.
      import routes from '@server/routes/{index,users/auth/google}.ts';
      // Matches: index.ts, users/auth/google.ts
  • Regex character classes:

    • Example: foo-[1-5].ts matches foo-1.ts, foo-2.ts, foo-3.ts, foo-4.ts, and foo-5.ts.
      import routes from '@server/routes/users/auth/file-[a1-a2].ts';
      // Matches: users/auth/file-a1.ts, users/auth/file-a2.ts
  • Regex logical "or":

    • Example: foo/(abc|xyz).ts matches foo/abc.ts and foo/xyz.ts.
      import routes from '@server/routes/users/auth/(google|facebook).ts';
      // Matches: users/auth/google.ts, users/auth/facebook.ts

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 the plugin
import BabelGlobImport from 'babel-plugin-glob-import';

// Define rules for Javascript files
const JavascriptRules = {
    test: /\.(ts|tsx|js|jsx)$/,
    rules: [{
        // Configure Babel
        loader: 'babel-loader',
        options: { 
            plugins: [
                
                // Add the glob import Babel plugin
                BabelGlobImport({ 
                    debug: true,
                })
            ]
        }
    }]
}

// Export Webpack compilation configuration
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,
}, [{
    // A function where you put the conditions to test for matching the 
    test: (request: TUnresolvedRequest) => boolean,
    // A function where you return by which statements 
    replace: (
        // The importation request (ex: ./src/server/models/**.ts)
        request: TRequest,
        // The files

 that were matched by the importation request
        matches: FileMatch[],
        // Access to babel's methods to create new statements
        t: typeof types
    ) => types.Statement[] | void
}])

Example

This example replaces import models from "@models" by import models from "@server/models/index.ts";

BabelGlobImport({ }, [{

    // (Optional) Giving a name to your transformer will make debugging easier
    name: 'Models import shortcut',

    // Match the import we want to replace 
    test: (request) => (
        // import
        request.type === 'import' 
        && 
        // models
        request.imported.type === 'default'
        && 
        // from '@models';
        request.source === '@models' 
    ),

    // If matched, replace it
    replace: (request, matches, t) => {
        // import
        return t.importDeclaration(
            // models
            [t.importDefaultSpecifier( t.identifier( request.imported.name ))],
            // from "@server/models/index.ts";
            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:
// @babel-debug
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