Home Reference Source Repository

src/roc/builder/index.js

import 'source-map-support/register';

import fs from 'fs';
import path from 'path';
import { getAbsolutePath } from 'roc';
import { createBuilder as createBuilderBase } from 'roc-web';
import { getSettings, appendSettings } from 'roc';

/**
 * Creates a builder.
 *
 * @param {!string} target - a target: should be either "client" or "server"
 * @param {rocBuilder} rocBuilder - A rocBuilder to base everything on.
 * @param {!string} [resolver=roc-web-react/lib/helpers/get-resolve-path] - Path to the resolver for the server side
 * {@link getResolvePath}
 * @returns {rocBuilder}
 */
export default function createBuilder(target, rocBuilder, resolver = 'roc-web-react/lib/helpers/get-resolve-path') {
    const allowedTargets = ['server', 'client'];

    if (allowedTargets.indexOf(target) === -1) {
        throw new Error(`Invalid target, must be one of ${allowedTargets}. Was instead ${target}.`);
    }

    let settings = getSettings('build');

    const DEV = (settings.mode === 'dev');

    const SERVER = (target === 'server');
    const CLIENT = (target === 'client');

    const newEntry = {
        ...settings.entry
    };
    if (!newEntry[target] && CLIENT) {
        newEntry[target] = require.resolve('../../src/default/client');
    } else if (!newEntry[target] && SERVER) {
        newEntry[target] = require.resolve('../../src/default/server');
    }

    appendSettings({build: {entry: newEntry}});
    settings = getSettings('build');

    // Update reference to internal base styles
    if (settings.assets.length > 0) {
        const updatedAssets = settings.assets.map((asset) => {
            const matches = /^roc-web-react\/(.*)/.exec(asset);
            if (matches && matches[1]) {
                return require.resolve(`../../${matches[1]}`);
            }

            return asset;
        });

        appendSettings({build: {assets: updatedAssets}});
    }
    settings = getSettings('build');

    let { buildConfig, builder } = createBuilderBase(target, {}, resolver);

    if (SERVER) {
        buildConfig.externals = [].concat([
            {
                'roc-web-react/lib/helpers/read-stats': true,
                'roc-web-react/lib/helpers/my-path': true
            }
        ], buildConfig.externals);
    }

    if (CLIENT) {
        buildConfig.plugins.push(
            new builder.IgnorePlugin(/^roc$/)
        );
    }

    if (CLIENT && DEV) {
        buildConfig.module.loaders.forEach(loader => {
            if (loader.loader === 'babel-loader') {
                loader.query = {
                    cacheDirectory: true,
                    plugins: [require.resolve('babel-plugin-react-transform')],
                    extra: {
                        'react-transform': {
                            transforms: [{
                                transform: require.resolve('react-transform-hmr'),
                                imports: [require.resolve('react')],
                                locals: ['module']
                            }, {
                                transform: require.resolve('react-transform-catch-errors'),
                                imports: [require.resolve('react'), require.resolve('redbox-react')]
                            }]
                        }
                    }
                };
            }
        });
    }

    buildConfig.resolveLoader.root.push(path.join(__dirname, '../../node_modules'));

    buildConfig.resolve.fallback.push(
        path.join(__dirname, '../../node_modules')
    );

    if (settings.routes) {
        const routes = getAbsolutePath(settings.routes);

        buildConfig.plugins.push(
            new builder.DefinePlugin({
                REACT_ROUTER_ROUTES: JSON.stringify(routes)
            })
        );
    }

    const fileExists = (filepath) => {
        filepath = getAbsolutePath(filepath);
        try {
            return fs.statSync(filepath).isFile();
        } catch (error) {
            return false;
        }
    };

    const hasReducers = !!(settings.reducers && fileExists(settings.reducers));
    const hasMiddlewares = !!(settings.reduxMiddlewares && fileExists(settings.reduxMiddlewares));
    const hasClientLoading = !!(settings.clientLoading && fileExists(settings.clientLoading));

    if (hasReducers) {
        const reducers = getAbsolutePath(settings.reducers);

        buildConfig.plugins.push(
            new builder.DefinePlugin({
                REDUX_REDUCERS: JSON.stringify(reducers)
            })
        );
    }

    if (hasMiddlewares) {
        const middlewares = getAbsolutePath(settings.reduxMiddlewares);

        buildConfig.plugins.push(
            new builder.DefinePlugin({
                REDUX_MIDDLEWARES: JSON.stringify(middlewares)
            })
        );
    }

    if (hasClientLoading) {
        const clientLoading = getAbsolutePath(settings.clientLoading);

        buildConfig.plugins.push(
            new builder.DefinePlugin({
                ROC_CLIENT_LOADING: JSON.stringify(clientLoading)
            })
        );
    }

    buildConfig.plugins.push(
        new builder.DefinePlugin({
            USE_DEFAULT_REDUX_REDUCERS: settings.useDefaultReducers,
            USE_DEFAULT_REDUX_MIDDLEWARES: settings.useDefaultReduxMiddlewares,
            USE_DEFAULT_REACT_ROUTER_ROUTES: settings.useDefaultRoutes,

            HAS_REDUX_REDUCERS: hasReducers,
            HAS_REDUX_MIDDLEWARES: hasMiddlewares,
            HAS_CLIENT_LOADING: hasClientLoading
        })
    );

    return {
        buildConfig,
        builder
    };
}