Gerson Goulart
Gerson Goulart

Reputation: 624

Babel failed to compile: .presets[1][0] must be a string, object, function — how to fix?

My project is based on Create React App, but I had to eject for a few reasons. Still, I try my best to keep it up to the current CRA standards manually. My last update is not going so well, though. I'm getting the following error when trying to run the "start" command:

./apps/index.tsx
Error: [BABEL] ./apps/index.tsx: .presets[1][0] must be a string, object, function
    at Array.forEach (<anonymous>)
    at Array.forEach (<anonymous>)

I have manually added some logs to ./node_modules/@babel/core/lib/config/validation/option-assertions.js, which gave me the following info:

LOG "assertPluginItem ERROR inside IF block:"

LOG "Item access(loc, 0)"
{
  type: 'access',
  name: 0,
  parent: {
    type: 'access',
    name: 1,
    parent: { type: 'option', name: 'presets', parent: [Object] }
  }
}

LOG "Item loc"
{
  type: 'access',
  name: 1,
  parent: {
    type: 'option',
    name: 'presets',
    parent: { type: 'root', source: 'preset' }
  }
}

LOG "Item value"
[ undefined, { development: true, runtime: 'automatic' } ]

LOG "pluginLoc"
{
  type: 'option',
  name: 'presets',
  parent: { type: 'root', source: 'preset' }
}

LOG "pluginValue"
[
  [ [Function], { useBuiltIns: 'entry', corejs: 3, exclude: [Array] } ],
  [ undefined, { development: true, runtime: 'automatic' } ],
  [ [Function] ]
]

Clearly, the problem is this "undefined" in [ undefined, { development: true, runtime: 'automatic' } ] but based on the options passed to this failing plugin, the code is not on my setup files, but rather somewhere internal to Babel.

I've tried adding logs to various files within Babel core without success, and I am out of ideas to figure out which plugin is failing and why. Would you have an idea that can help me out here?

Here's what my webpack.config.js looks like:

const path = require('path');
const webpack = require('webpack');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const getTheme = require('../theme');
const paths = require('../paths');
const getClientEnvironment = require('../env/getClientEnvironment');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');

const postcssNormalize = require('postcss-normalize');

const appPackageJson = require(paths.appPackageJson);

const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';

const webpackDevClientEntry = require.resolve('react-dev-utils/webpackHotDevClient');
const reactRefreshOverlayEntry = require.resolve('react-dev-utils/refreshOverlayInterop');

const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';


const cssRegex = /\.css$/;

const hasJsxRuntime = (() => {
    if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
        return false;
    }

    try {
        require.resolve('react/jsx-runtime');
        return true;
    }
    catch (e) {
        return false;
    }
})();

module.exports = function getConfig(appName, webpackEnv = 'development', isHotModuleReloadEnabled = true) {
    const isEnvDevelopment = webpackEnv === 'development';
    const isEnvProduction = webpackEnv === 'production';

    const isEnvProductionProfile =
        isEnvProduction && process.argv.includes('--profile');

    const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));

    const shouldUseReactRefresh = env.raw.FAST_REFRESH;


    const getStyleLoaders = (cssOptions, preProcessor) => {
        const loaders = [
            isEnvDevelopment && require.resolve('style-loader'),
            isEnvProduction && {
                loader: MiniCssExtractPlugin.loader,
                options: paths.publicUrlOrPath.startsWith('.')
                    ? { publicPath: '../../' }
                    : {},
            },
            {
                loader: require.resolve('css-loader'),
                options: cssOptions,
            },
            {
                loader: require.resolve('postcss-loader'),
                options: {
                    ident: 'postcss',
                    plugins: () => [
                        require('postcss-flexbugs-fixes'),
                        require('postcss-preset-env')({
                            autoprefixer: {
                                flexbox: 'no-2009',
                            },
                            stage: 3,
                        }),
                        postcssNormalize(),
                    ],
                    sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
                },
            },
        ].filter(Boolean);
        if (preProcessor) {
            loaders.push(
                {
                    loader: require.resolve('resolve-url-loader'),
                    options: {
                        sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
                        root: paths.appSrc,
                    },
                },
                {
                    loader: require.resolve(preProcessor),
                    options: {
                        sourceMap: true,
                        globalVars: getTheme(appName),
                    },
                },
            );
        }
        return loaders;
    };

    const alias = {
        ...paths.alias,
        app: paths.getAppPath(appName),
    };

    return {
        stats: 'verbose',
        name: appName,
        mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
        bail: isEnvProduction,
        devtool: isEnvProduction
            ? shouldUseSourceMap
                ? 'source-map'
                : false
            : isEnvDevelopment && 'cheap-module-source-map',
        entry:
            isEnvDevelopment && !shouldUseReactRefresh
                ? [
                    webpackDevClientEntry,
                    paths.appStyles,
                    paths.appIndexJs,
                ]
                : [
                    paths.appStyles,
                    paths.appIndexJs,
                ],
        output: {
            path: isEnvProduction ? paths.appBuild(appName) : undefined,
            pathinfo: isEnvDevelopment,
            filename: isEnvProduction
                ? 'static/js/[name].[contenthash:8].js'
                : isEnvDevelopment && 'static/js/bundle.js',
            futureEmitAssets: true,
            chunkFilename: isEnvProduction
                ? 'static/js/[name].[contenthash:8].chunk.js'
                : isEnvDevelopment && 'static/js/[name].chunk.js',
            publicPath: paths.publicUrlOrPath,
            devtoolModuleFilenameTemplate: isEnvProduction
                ? (info) =>
                    path
                        .relative(paths.appsPath, info.absoluteResourcePath)
                        .replace(/\\/g, '/')
                : isEnvDevelopment &&
                ((info) => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
            jsonpFunction: `webpackJsonp${appPackageJson.name}`,
            globalObject: 'this',
        },
        optimization: {
            minimize: isEnvProduction,
            minimizer: [
                new TerserPlugin({
                    terserOptions: {
                        parse: {
                            ecma: 8,
                        },
                        compress: {
                            ecma: 5,
                            warnings: false,
                            comparisons: false,
                            inline: 2,
                        },
                        mangle: {
                            safari10: true,
                        },
                        keep_classnames: isEnvProductionProfile, // eslint-disable-line camelcase
                        keep_fnames: isEnvProductionProfile, // eslint-disable-line camelcase
                        output: {
                            ecma: 5,
                            comments: false,
                            ascii_only: true,
                        },
                    },
                    sourceMap: shouldUseSourceMap,
                }),
                new OptimizeCSSAssetsPlugin({
                    cssProcessorOptions: {
                        parser: safePostCssParser,
                        map: shouldUseSourceMap
                            ? {
                                inline: false,
                                annotation: true,
                            }
                            : false,
                    },
                    cssProcessorPluginOptions: {
                        preset: ['default', { minifyFontValues: { removeQuotes: false } }],
                    },
                }),
            ],
            splitChunks: {
                cacheGroups: {
                    app: {
                        test: /[\\/]apps[\\/]/,
                        name: 'app',
                        chunks: 'all',
                    },
                    shared: {
                        test: /[\\/]shared[\\/]/,
                        name: 'shared',
                        chunks: 'all',
                    },
                    libs: {
                        test: /[\\/]libs[\\/]/,
                        name: 'libs',
                        chunks: 'all',
                    },
                    vendors: {
                        test: /[\\/]node_modules[\\/]/,
                        name: 'vendors',
                        chunks: 'all',
                    },
                },
            },
            runtimeChunk: {
                name: (entrypoint) => `runtime-${entrypoint.name}`,
            },
        },
        resolve: {
            modules: [
                'node_modules',
                paths.nodeModules,
                paths.sharedSrc,
                paths.libSrc,
            ].concat(
                process.env.NODE_PATH.split(path.delimiter).filter(Boolean),
            ),
            extensions: ['.tsx', '.ts', '.js'],
            alias,
            plugins: [
                PnpWebpackPlugin,
            ],
        },
        resolveLoader: {
            plugins: [
                PnpWebpackPlugin.moduleLoader(module),
            ],
        },
        module: {
            strictExportPresence: true,
            rules: [
                { parser: { requireEnsure: false } },
                {
                    oneOf: [
                        {
                            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
                            loader: require.resolve('url-loader'),
                            options: {
                                limit: 10000,
                                name: 'static/media/[name].[hash:8].[ext]',
                            },
                        },
                        {
                            test: /\.(js|mjs|jsx|ts|tsx)$/,
                            include: [
                                paths.appsPath,
                                paths.sharedSrc,
                                paths.libSrc,
                            ],
                            loader: require.resolve('babel-loader'),
                            options: {
                                customize: require.resolve(
                                    'babel-preset-react-app/webpack-overrides',
                                ),
                                presets: [
                                    [
                                        require.resolve('babel-preset-react-app'),
                                        {
                                            runtime: hasJsxRuntime ? 'automatic' : 'classic',
                                        },
                                    ],
                                ],
                                plugins: [
                                    ['add-module-exports'],
                                    ['@babel/plugin-transform-modules-commonjs'], // only until no more module.exports = *
                                    ['module-resolver', { extensions: ['.js', '.ts', '.tsx'], alias }],
                                    [require.resolve('babel-plugin-named-asset-import'), {
                                        loaderMap: {
                                            svg: {
                                                ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]',
                                            },
                                        },
                                    }],
                                    isEnvDevelopment && shouldUseReactRefresh && require.resolve('react-refresh/babel'),
                                ].filter(Boolean),
                                cacheDirectory: true,
                                cacheCompression: false,
                                compact: isEnvProduction,
                            },
                        },
                        {
                            test: /\.(js|mjs)$/,
                            exclude: /@babel(?:\/|\\{1,2})runtime/,
                            loader: require.resolve('babel-loader'),
                            options: {
                                babelrc: false,
                                configFile: false,
                                compact: false,
                                presets: [
                                    [
                                        require.resolve('babel-preset-react-app/dependencies'),
                                        { helpers: true },
                                    ],
                                ],
                                cacheDirectory: true,
                                cacheCompression: false,

                                sourceMaps: shouldUseSourceMap,
                                inputSourceMap: shouldUseSourceMap,
                            },
                        },

                        {
                            test: cssRegex,
                            use: getStyleLoaders({
                                importLoaders: 1,
                                sourceMap: isEnvProduction
                                    ? shouldUseSourceMap
                                    : isEnvDevelopment,
                            }),
                            sideEffects: true,
                        },
                        {
                            test: /\.(less)$/,
                            use: [
                                isEnvDevelopment && require.resolve('style-loader'),
                                {
                                    loader: require.resolve('css-loader'),
                                    options: {
                                        importLoaders: 0,
                                        sourceMap: isEnvProduction
                                            ? shouldUseSourceMap
                                            : isEnvDevelopment,
                                    },
                                },
                                {
                                    loader: 'less-loader',
                                    options: {
                                        logLevel: 2,
                                        relativeUrls: true,
                                        globalVars: getTheme(appName),
                                        env: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
                                    },
                                },
                                require.resolve('import-glob-loader'),
                            ].filter(Boolean),
                            sideEffects: true,
                        },
                        {
                            loader: require.resolve('file-loader'),
                            exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
                            options: {
                                name: 'static/media/[name].[hash:8].[ext]',
                            },
                        },
                    ],
                },
            ],
        },
        plugins: [
            new HtmlWebpackPlugin(
                Object.assign(
                    {},
                    {
                        inject: true,
                        template: paths.appHtml,
                    },
                    isEnvProduction
                        ? {
                            minify: {
                                removeComments: true,
                                collapseWhitespace: true,
                                removeRedundantAttributes: true,
                                useShortDoctype: true,
                                removeEmptyAttributes: true,
                                removeStyleLinkTypeAttributes: true,
                                keepClosingSlash: true,
                                minifyJS: true,
                                minifyCSS: true,
                                minifyURLs: true,
                            },
                        }
                        : undefined,
                ),
            ),
            isEnvProduction &&
            shouldInlineRuntimeChunk &&
            new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
            new InterpolateHtmlPlugin(
                HtmlWebpackPlugin,
                Object.assign({ FAVICON: `favicon-${appName}.ico?v3` }, env.raw),
            ),
            new ModuleNotFoundPlugin(paths.rootDirectory, paths.yarnLockFile),
            new webpack.DefinePlugin(env.stringified),
            isEnvDevelopment && isHotModuleReloadEnabled && new webpack.HotModuleReplacementPlugin(),
            isEnvDevelopment &&
            shouldUseReactRefresh &&
            new ReactRefreshWebpackPlugin({
                overlay: {
                    entry: webpackDevClientEntry,
                    module: reactRefreshOverlayEntry,
                    sockIntegration: false,
                },
            }),
            isEnvDevelopment && new CaseSensitivePathsPlugin(),
            isEnvDevelopment &&
            new WatchMissingNodeModulesPlugin(paths.nodeModules),
            isEnvProduction &&
            new MiniCssExtractPlugin({
                filename: 'static/css/[name].[contenthash:8].css',
                chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
            }),
            new ManifestPlugin({
                fileName: 'asset-manifest.json',
                publicPath: paths.publicUrlOrPath,
                generate: (seed, files, entrypoints) => {
                    const manifestFiles = files.reduce((manifest, file) => {
                        manifest[file.name] = file.path; // eslint-disable-line immutable/no-mutation
                        return manifest;
                    }, seed);
                    const entrypointFiles = entrypoints.main.filter(
                        (fileName) => !fileName.endsWith('.map'),
                    );

                    return {
                        files: manifestFiles,
                        entrypoints: entrypointFiles,
                    };
                },
            }),
            new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
        ].filter(Boolean),
        node: {
            module: 'empty',
            dgram: 'empty',
            dns: 'mock',
            fs: 'empty',
            net: 'empty',
            tls: 'empty',
            child_process: 'empty',
        },
        performance: false,
    };
};

Upvotes: 0

Views: 4957

Answers (1)

Gerson Goulart
Gerson Goulart

Reputation: 624

Thanks to Logan Smyth I was able to resolve this instance. In case someone else lands here with a similar problem, here is what happened:

On the above webpack config, in the module rule for babel-loader, I had the babel-plugin-add-module-exports that would enable some interoperability with older dependencies I used. After upgrading all dependencies, this module entered in conflict with https://github.com/facebook/create-react-app/blob/22f46a8d5dfc46fe0f613cd7efbc82344823f461/packages/babel-preset-react-app/create.js#L91 which inlines require("@babel/preset-env").default while the default property was removed by add-module-exports causing it to evaluate to undefined.

Upvotes: 1

Related Questions