webpackwebpack-encore

webpack (via Encore) compiles code successfully and then, many, many minutes later, throws an error


Good morning

I'm running webpack (v5.74.0), via Symfony Encore (v3.0.0), using Yarn v1.22.17 on Node.js v14.18.0 -- I've tried v16.17.0 as well.

yarn encore apparently hangs after saying it's successfully compiled our JavaScript. ++So it's likely there's a problem compiling our Sass.++ After a couple attempts, I just left it running and eventually it threw an error. I don't know enough to understand what the stack trace is pointing at. It looks as if the problem could be webpack but I can't find reference to the error code. The error code doesn't appear to have anything to do with Yarn. I wonder, have you seen this problem before? Is there any way to see what webpack/Encore is doing? I've read about, and tried, Encore's --profile CLI option but nothing new is displayed and I don't see a log file. I'm not sure where to begin with this. Any pointers will be very welcome, thank you.

Here's what I saw the last time I tried running Encore:

user@dan:~/vhtdocs/s/project$ yarn --verbose encore dev --profile
yarn run v1.22.17
verbose 0.648418183 Checking for configuration file "/home/user/vhtdocs/s/project/.npmrc".
verbose 0.649559572 Checking for configuration file "/home/user/.npmrc".
verbose 0.650454348 Checking for configuration file "/home/user/.nvm/versions/node/v14.18.0/etc/npmrc".
verbose 0.651247908 Checking for configuration file "/home/user/vhtdocs/s/project/.npmrc".
verbose 0.6520772 Checking for configuration file "/home/user/vhtdocs/s/.npmrc".
verbose 0.653013165 Checking for configuration file "/home/user/vhtdocs/.npmrc".
verbose 0.654031989 Checking for configuration file "/home/user/.npmrc".
verbose 0.654781117 Checking for configuration file "/home/.npmrc".
verbose 0.65640441 Checking for configuration file "/home/user/vhtdocs/s/project/.yarnrc".
verbose 0.657429162 Checking for configuration file "/home/user/.yarnrc".
verbose 0.658158477 Found configuration file "/home/user/.yarnrc".
verbose 0.659509905 Checking for configuration file "/home/user/.nvm/versions/node/v14.18.0/etc/yarnrc".
verbose 0.660365284 Checking for configuration file "/home/user/vhtdocs/s/project/.yarnrc".
verbose 0.661126718 Checking for configuration file "/home/user/vhtdocs/s/.yarnrc".
verbose 0.662244781 Checking for configuration file "/home/user/vhtdocs/.yarnrc".
verbose 0.663180621 Checking for configuration file "/home/user/.yarnrc".
verbose 0.664047646 Found configuration file "/home/user/.yarnrc".
verbose 0.679342779 Checking for configuration file "/home/.yarnrc".
verbose 0.693341934 current time: 2022-08-20T08:43:27.996Z
$ /home/user/vhtdocs/s/project/node_modules/.bin/encore dev --profile
Running webpack ...

 DONE  Compiled successfully in 60850ms                                                                                                                                                             08:44:34

135 files written to web/build/js

<--- Last few GCs --->

[21710:0x4a52810]   688427 ms: Mark-sweep (reduce) 1939.5 (1971.4) -> 1938.8 (1971.4) MB, 6465.8 / 0.0 ms  (average mu = 0.079, current mu = 0.002) allocation failure scavenge might not succeed
[21710:0x4a52810]   694602 ms: Mark-sweep (reduce) 1939.8 (1968.4) -> 1938.9 (1969.7) MB, 6133.8 / 0.0 ms  (average mu = 0.044, current mu = 0.007) allocation failure scavenge might not succeed


<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0xa389b0 node::Abort() [webpack]
 2: 0x96e0af node::FatalError(char const*, char const*) [webpack]
 3: 0xbb7a4e v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [webpack]
 4: 0xbb7dc7 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [webpack]
 5: 0xd73fd5  [webpack]
 6: 0xd74b5f  [webpack]
 7: 0xd8299b v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [webpack]
 8: 0xd8655c v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [webpack]
 9: 0xd54c3b v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [webpack]
10: 0x109d21f v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [webpack]
11: 0x1446379  [webpack]
Aborted
verbose 701.761060613 Error: Command failed with exit code 134.
    at ProcessTermError.ExtendableBuiltin (/home/user/.nvm/versions/node/v14.18.0/lib/node_modules/yarn/lib/cli.js:721:66)
    at ProcessTermError.MessageError (/home/user/.nvm/versions/node/v14.18.0/lib/node_modules/yarn/lib/cli.js:750:123)
    at new ProcessTermError (/home/user/.nvm/versions/node/v14.18.0/lib/node_modules/yarn/lib/cli.js:790:113)
    at /home/user/.nvm/versions/node/v14.18.0/lib/node_modules/yarn/lib/cli.js:34672:30
    at Generator.throw (<anonymous>)
    at step (/home/user/.nvm/versions/node/v14.18.0/lib/node_modules/yarn/lib/cli.js:310:30)
    at /home/user/.nvm/versions/node/v14.18.0/lib/node_modules/yarn/lib/cli.js:323:13
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
error Command failed with exit code 134.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Here's our webpack.config.js -- which I don't know a great deal about, unfortunately:

const encore = require('@symfony/webpack-encore');
const webpack = require('webpack');
const path = require('path');
const glob = require('glob');

/**
 * CSS/SASS config
 */
encore
    .setOutputPath('web/build/css')
    .setPublicPath('/build/css')

    .addStyleEntry('add_to_basket', './assets/js/css-entrypoints/add_to_basket.js')
    .addStyleEntry('redirect_to_supplier_custom_booking_url', './assets/js/css-entrypoints/redirect_to_supplier_custom_booking_url.js')
    .addStyleEntry('legacy_add_to_basket', './assets/js/css-entrypoints/legacy_add_to_basket.js')
    .addStyleEntry('base', './assets/js/css-entrypoints/base.js')
    .addStyleEntry('hotel_book_now', './assets/js/css-entrypoints/hotel_book_now.js')
    .addStyleEntry('overseer_iframe', './assets/js/css-entrypoints/overseer_iframe.js')
    .addStyleEntry('overseer', './assets/js/css-entrypoints/overseer.js')
    .addStyleEntry('vendor_misc', './assets/js/css-entrypoints/vendor_misc.js')
    .addStyleEntry('email', './assets/js/css-entrypoints/email.js')
    .addStyleEntry('pdf', './assets/js/css-entrypoints/pdf.js')

    // Splits files into smaller pieces for greater optimization
    .splitEntryChunks()

    // Creates a single runtime.js file to be used on all pages, rather than including it in each bundle
    .enableSingleRuntimeChunk()

    // Enable hashed filenames (e.g. app.abc123.css)
    .enableVersioning()

    .enableSourceMaps(!encore.isProduction())

    // enables SASS/SCSS support. Magic importer prevents files from being imported more than once
    .enableSassLoader()

    // We use PostCSS for auto-prefixing
    .enablePostCssLoader((options) => {
        if (options.postcssOptions === undefined) {
            options.postcssOptions = {};
        }

        options.postcssOptions.config = path.resolve(__dirname, 'postcss.config.js');
    })
;

const cssConfig = encore.getWebpackConfig();
cssConfig.name = 'css';

/**
 * JS Config
 */
encore.reset(); // Important - forget everything above!

encore
    .setOutputPath('web/build/js')
    .setPublicPath('/build/js')
    .autoProvidejQuery()
    .splitEntryChunks()
    .enableSingleRuntimeChunk()
    .enableVersioning()
    // .enableSourceMaps(!encore.isProduction())
    // React is used for /book-now/.
    .enableReactPreset()
    // See https://github.com/moment/moment/issues/2979#issuecomment-287675568
    .addPlugin(new webpack.IgnorePlugin({
        resourceRegExp: /\.\/locale$/,
        contextRegExp: /moment.*/,
    }))
;

var jsAssetsPath = path.join(__dirname, 'assets/js');

/**
 * Dynamically create entries, based on files in the entrypoints dir
 *
 * For a JS file assets/js/Bundle/Controller/action.entry.js, we
 * we end up with an entrypoint called Bundle/Controller/action
 */
glob
    // Get array of .entrypoint.js(x) files, including in sub-directories
    .sync('**/*.entry.+(js|jsx)', {cwd: jsAssetsPath})
    .forEach(relativeFilePath => {
        let relativeFilePathParts = path.parse(relativeFilePath);

        let entryPointName = path.join(
            relativeFilePathParts.dir,
            relativeFilePathParts.name.replace('.entry', '')
        );

        let fullFilePath = path.join(jsAssetsPath, relativeFilePath);

        encore.addEntry(entryPointName, fullFilePath);
    })
;

const jsConfig = encore.getWebpackConfig();
jsConfig.name = 'js';

// Ensure both node_modules and our AMD modules that haven't been moved over yet are available to Webpack
jsConfig.resolve.modules = ['assets/js/modules', 'node_modules'];

// Make sure Webpack knows browser globals are available
jsConfig.target = 'web';

// We have a downstream dependency which wants the 'fs' module in a web context.
// See https://github.com/webpack-contrib/css-loader/issues/447
// jsConfig.node = { fs: 'empty' };

// @todo now we're off requireJS, gradually get rid of these aliases
jsConfig.resolve.alias = {
    "bower_components": path.join(__dirname, "web/bower_components"),
    "bundles": path.join(__dirname, "web/bundles"),

    //Module names:
    "jquery": "jquery/dist/jquery",
    "jquery.amaran": "AmaranJS/dist/js/jquery.amaran",
    "jquery.select2": "select2/dist/js/select2.full",
    "jquery.keepinsight": path.join(__dirname, "web/bower_components/jquery-keepinsight/jquery.keepinsight"),
    "jquery.backstretch": path.join(__dirname, "web/bower_components/jquery-backstretch/jquery.backstretch"),
    "jquery.zaccordion": "zAccordion/js/jquery.zaccordion",
    "jquery.fancybox": "fancybox/source/jquery.fancybox",
    "slickCarousel": "slick-carousel/slick/slick",
    "InfiniteAjaxScroll": "@webcreate/infinite-ajax-scroll/dist/infinite-ajax-scroll",
    "sweetAlert": "sweetalert/dist/sweetalert.min",
    "momentJs": "moment/min/moment.min",
    "jquery.mmenu": "jquery.mmenu/dist/jquery.mmenu.all",
    "tooltipster": "tooltipster/dist/js/tooltipster.bundle",
    "jquery.worky": "myworld-frontend/js/jquery.worky",
    "window": "myworld-frontend/js/window",
    "MyWorld": "myworld-frontend/js/myworld",
    "MyWorld.Forms": "myworld-frontend/js/forms.js",
    "MyWorld.alerter": "myworld-frontend/js/alerter",
    "MyWorld.translator": "myworld-frontend/js/translator",
    "MyWorld.infiniteScroller": "myworld-frontend/js/infinite_scroller",
    "MyWorld.notifier": "myworld-frontend/js/notifier",
    "MyWorld.dateHelper": "myworld-frontend/js/date_helper",
    "MyWorld.layout": "myworld-frontend/js/layout",
    "MyWorld.dateTimePicker": "myworld-frontend/js/datetime_picker",
    "clipboard": path.join(__dirname, "web/bower_components/clipboard/dist/clipboard"),
    "autosize": path.join(__dirname, "web/bower_components/autosize/dist/autosize"),
};

module.exports = [cssConfig, jsConfig];

Solution

  • I never really got to the bottom of this, but I do know that the script was blowing up in PostCSS, even without plugins enabled. I guess the problem is simply we have a lot of Sass.

    I ended-up increasing memory allocation to Node.js by setting the value of the max_old_space_size option. Here's what our Webpack Encore dev build-script looks like now:

    export NODE_OPTIONS=--max_old_space_size=3072 && yarn encore dev --progress
    

    Earlier:

    In answer to one of my own questions... I just found out, entirely by accident, that it's possible to get more information about what Webpack is doing by specifying stats options in the/a config object exported from webpack.config.js.

    stats: {
      errors: true,
      errorStack: true,
      errorDetails: true,
      children: true
    }
    

    Full details of the stats option here: https://webpack.js.org/configuration/stats/#statserrors