Current version : 0.24.0
Made in 🇫🇷 ◌ GitHubNPM

Bundle a target

Most of the time, targets that expose a product need to be bundled.

Intro

Server side targets (e.g. node-express, node-hono, etc.) don't need any bundling because they are executed on the server.

However, client side targets (e.g. react-web-pure, react-native-pure) need bundling in order for them to be executed respectively in the browser and in React Native.

Hybrid targets like server-nextjs need "partial" bundling for the client side part.

libmodulor apps need bundling because the use cases define the client and server lifecycles in one central place (*UCD.ts files).

Having lifecycle.client visible to the server is not a big deal. However, even if it does not break - thanks to Dependency Injection - we don't want lifecycle.server (i.e. our server side logic) to be visible client side.

That's why we need plugins & loaders to transform them.

Plugins & Loaders

Babel

Babel is a JavaScript compiler.

It is widely used by React Native via the Metro bundler. Since Metro only understands CommonJS (as of now), and since libmodulor is an ESM-only library, we need a bridge to import the plugin in a babel.config.cjs config file.

[🔖 babel.config.esm-bridge.js]

import { createRequire } from 'node:module';

const require = createRequire(import.meta.url);
const { Plugin } = require('libmodulor/babel');
const bridge = { Plugin };

export default bridge;

[🔖 babel.config.cjs]

const { Plugin } = require('./babel.config.esm-bridge.js').default;

module.exports = (api) => {
    api.cache(true);
    return {
        plugins: [
            // ...
            [Plugin],
        ],
        presets: ['babel-preset-expo'],
    };
};

You can find a full example in the rn target of the Playground example, with the associated code.

Vite

Vite is a blazing fast frontend build tool powering the next generation of web applications.

It is the most modern bundler (as of now). The setup is therefore pretty straightforward.

[🔖 vite.config.ts]

import { defineConfig, type PluginOption } from 'vite';

import { Plugin } from 'libmodulor/vite';

export default defineConfig({
    // ...
    plugins: [Plugin],
});

You can find a full example in the spa target of the Playground example, with the associated code.

Webpack

Webpack is a module bundler.

This is the old guy in town. Even though it's the oldest one here, it's still used a lot. For example in Next.js, in addition to SWC.

Unfortunately, it does not accept inlined loaders, so we need to import it from its path. Hence the somewhat clumsy way of loading it below, in a Next.js project.

[🔖 next.config.js]

import { UC_DEF_FILE_NAME_SUFFIX } from 'libmodulor';

/**
 * @type {import('next').NextConfig}
 */
export default {
    // ...
    webpack: (config, { isServer }) => {
        // ...
        if (!isServer) {
            config.module.rules.push({
                test: new RegExp(``${UC_DEF_FILE_NAME_SUFFIX}$``),
                use: {
                    loader: resolve(
                        join(
                            'node_modules',
                            'libmodulor',
                            'dist',
                            'esm',
                            'index.webpack.js',
                        ),
                    ),
                },
            });
        }
        return config;
    },
};

The example above is when the apps are not already bundled when Next.js does its job. Sometimes, they are already, so you cannot use the webpack loader directly, which relies on the TypeScript AST to do its job.

In such a case, just use the babel plugin shown above.

[🔖 next.config.js]

import { UC_DEF_SUFFIX } from 'libmodulor';
import { Plugin } from 'libmodulor/babel';

/**
 * @type {import('next').NextConfig}
 */
export default {
    // ...
    webpack: (config, { isServer }) => {
        // ...
        if (!isServer) {
            config.module.rules.push({
                exclude: /node_modules/,
                test: new RegExp(``${UC_DEF_SUFFIX}.js$``),
                use: {
                    loader: 'babel-loader',
                    options: {
                        plugins: [[Plugin]],
                    },
                },
            });
        }
        return config;
    },
};

You can find a full example in the server-nextjs target of the Playground example, with the associated code.