Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js crashing during Angular 6 build with --watch

I am using Node v8.12.0 on Mac (although I've seen this issue with Node 9.x versions, and also on Linux).

I am developing Angular 6 app, and am running dev builds with --watch flag. The watch will run and can rebuild the app maybe 4 or 5 times, then Node crashes with the following output:

<--- Last few GCs --->

[34201:0x104000000]   273927 ms: Mark-sweep 1309.4 (1430.5) -> 1309.2 (1431.0) MB, 1296.0 / 0.0 ms  allocation failure GC in old space requested
[34201:0x104000000]   275358 ms: Mark-sweep 1309.2 (1431.0) -> 1309.2 (1424.0) MB, 1430.8 / 0.0 ms  last resort GC in old space requested
[34201:0x104000000]   276946 ms: Mark-sweep 1309.2 (1424.0) -> 1309.2 (1423.5) MB, 1587.7 / 0.0 ms  last resort GC in old space requested


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0x1c5f3a825879 <JSObject>
1: fromString(aka fromString) [buffer.js:~298] [pc=0x2234a1ca140b](this=0x1c5ffcc022d1 <undefined>,string=0x1c5f6f8dffa1 <Very long string[784654]>,encoding=0x1c5ffcc022d1 <undefined>)
2: from [buffer.js:177] [bytecode=0x1c5f43e4aac9 offset=11](this=0x1c5f8a5b5c51 <JSFunction Buffer (sfi = 0x1c5f3a87e159)>,value=0x1c5f6f8dffa1 <Very long string[784654]>,encodingOrOffset=0x1c5ffcc022d1 <u...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
 1: node::Abort() [/usr/local/bin/node]
 2: node::FatalException(v8::Isolate*, v8::Local<v8::Value>, v8::Local<v8::Message>) [/usr/local/bin/node]
 3: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [/usr/local/bin/node]
 4: v8::internal::Factory::NewRawTwoByteString(int, v8::internal::PretenureFlag) [/usr/local/bin/node]
 5: v8::internal::String::SlowFlatten(v8::internal::Handle<v8::internal::ConsString>, v8::internal::PretenureFlag) [/usr/local/bin/node]
 6: v8::String::WriteUtf8(char*, int, int*, int) const [/usr/local/bin/node]
 7: node::StringBytes::Write(v8::Isolate*, char*, unsigned long, v8::Local<v8::Value>, node::encoding, int*) [/usr/local/bin/node]
 8: node::Buffer::New(v8::Isolate*, v8::Local<v8::String>, node::encoding) [/usr/local/bin/node]
 9: node::Buffer::(anonymous namespace)::CreateFromString(v8::FunctionCallbackInfo<v8::Value> const&) [/usr/local/bin/node]
10: 0x2234a02d4067
11: 0x2234a1ca140b
12: 0x2234a023d1d6
13: 0x2234a018535f

I have tried adding --max_old_space_size=12000 also, but it has not seemed to make any difference. I'm not sure where to look for the cause of the issue, or how to even begin debugging in Node. Any assistance would be hugely appreciated!

Some background: it was Angular 5 app with ejected config, and I have updated it to Angular 6, with the same config, and the app itself is working as expected. It is only since updating to Angular 6 that this issue started to arise.

For reference, here are the dependency and devDependency parts of the package.json:

"dependencies": {
  "@angular/animations": "6.1.10",
  "@angular/cdk": "6.4.7",
  "@angular/common": "6.1.10",
  "@angular/compiler": "6.1.10",
  "@angular/core": "6.1.10",
  "@angular/forms": "6.1.10",
  "@angular/http": "6.1.10",
  "@angular/material": "6.4.7",
  "@angular/platform-browser": "6.1.10",
  "@angular/platform-browser-dynamic": "6.1.10",
  "@angular/router": "6.1.10",
  "@ng-idle/core": "6.0.0-beta.3",
  "@ng-idle/keepalive": "6.0.0-beta.3",
  "@ngrx/effects": "6.1.2",
  "@ngrx/entity": "6.1.2",
  "@ngrx/router-store": "6.1.2",
  "@ngrx/store": "6.1.2",
  "@ngrx/store-devtools": "6.1.2",
  "@swimlane/ngx-datatable": "14.0.0",
  "@types/crypto-js": "3.1.37",
  "@types/moment": "2.13.0",
  "angular2-toaster": "6.1.0",
  "angulartics2": "7.2.0",
  "core-js": "2.5.7",
  "crypto-js": "3.1.9-1",
  "hammerjs": "2.0.8",
  "immutable": "3.8.2",
  "jquery": "2.2.4",
  "moment": "2.19.1",
  "ng2-charts": "1.6.0",
  "ngx-zendesk-webwidget": "0.1.3",
  "node-waves": "0.7.6",
  "normalize.css": "3.0.3",
  "rxjs": "6.3.3",
  "sass": "1.15.1",
  "zone.js": "0.8.26"
},
"devDependencies": {
  "@angular-builders/custom-webpack": "7.0.0",
  "@angular-devkit/build-angular": "0.11.0",
  "@angular/cli": "7.0.6",
  "@angular/compiler-cli": "6.1.10",
  "@angular/language-service": "6.1.10",
  "@types/jasmine": "2.5.53",
  "@types/jasminewd2": "2.0.2",
  "@types/node": "6.0.60",
  "autoprefixer": "9.3.1",
  "chromedriver": "2.38.2",
  "clean-webpack-plugin": "1.0.0",
  "codelyzer": "4.5.0",
  "copy-webpack-plugin": "4.6.0",
  "css-loader": "1.0.1",
  "cssnano": "4.1.7",
  "exports-loader": "0.7.0",
  "file-loader": "2.0.0",
  "istanbul-instrumenter-loader": "2.0.0",
  "jasmine-allure-reporter": "1.0.2",
  "jasmine-core": "2.6.2",
  "jasmine-marbles": "0.4.0",
  "jasmine-spec-reporter": "4.2.1",
  "karma": "3.0.0",
  "karma-chrome-launcher": "2.2.0",
  "karma-cli": "1.0.1",
  "karma-coverage-istanbul-reporter": "2.0.1",
  "karma-jasmine": "1.1.2",
  "karma-jasmine-html-reporter": "0.2.2",
  "karma-spec-reporter": "0.0.32",
  "lint-staged": "8.1.0",
  "loader-utils": "1.1.0",
  "mini-css-extract-plugin": "0.4.5",
  "npm-run-all": "4.1.5",
  "postcss-custom-properties": "8.0.9",
  "postcss-loader": "3.0.0",
  "postcss-url": "8.0.0",
  "pre-commit": "1.2.2",
  "process": "0.11.10",
  "protractor": "5.4.1",
  "protractor-console": "3.0.0",
  "protractor-jasmine2-html-reporter": "0.0.7",
  "puppeteer": "1.6.0",
  "raw-loader": "0.5.1",
  "rxjs-tslint": "0.1.5",
  "sass-loader": "7.1.0",
  "selenium-server-standalone-jar": "3.8.1",
  "source-map-loader": "0.2.4",
  "style-loader": "0.23.1",
  "stylelint": "9.6.0",
  "stylelint-config-recommended": "2.1.0",
  "ts-mockito": "2.3.1",
  "ts-node": "3.2.0",
  "tslint": "5.7.0",
  "typescript": "2.9.2",
  "uglifyjs-webpack-plugin": "2.0.1",
  "url-loader": "1.1.2",
  "webpack": "4.24.0",
  "webpack-bundle-analyzer": "3.0.3",
  "webpack-cli": "3.1.2",
  "webpack-concat-plugin": "3.0.0",
  "webpack-dev-server": "3.1.10",
  "webpack-filter-warnings-plugin": "^1.2.1",
  "yargs": "8.0.1"
}

Lastly, here is the custom webpack configuration I am using (which worked flawlessly in Angular 5):

const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const autoprefixer = require('autoprefixer');
const postcssUrl = require('postcss-url');
const cssnano = require('cssnano');
const customProperties = require('postcss-custom-properties');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const { NoEmitOnErrorsPlugin, SourceMapDevToolPlugin, NormalModuleReplacementPlugin } = require('webpack');
const { AngularCompilerPlugin } = require('@ngtools/webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const FilterWarningsPlugin = require('webpack-filter-warnings-plugin');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const ProgressPlugin = require('webpack/lib/ProgressPlugin');

const postcssPlugins = function (env) {

  // safe settings based on: https://github.com/ben-eb/cssnano/issues/358#issuecomment-283696193
  const importantCommentRe = /@preserve|@license|[@#]\s*source(?:Mapping)?URL|^!/i;
  const baseHref = '';
  const deployUrl = '';
  const minimizeOptions = {
    preset: [
      'default',
      {
        mergeLonghand: false,
        discardComments: { remove: (comment) => !importantCommentRe.test(comment) }
      }
    ]
  };
  return [
    postcssUrl({
      url: (URL) => {
        // Only convert root relative URLs, which CSS-Loader won't process into require().
        if (!URL.url.startsWith('/') || URL.url.startsWith('//')) {
           return URL.url;
        }
        if (deployUrl.match(/:\/\//)) {
          // If deployUrl contains a scheme, ignore baseHref use deployUrl as is.
          return `${deployUrl.replace(/\/$/, '')}${URL.url}`;
        }
        else if (baseHref.match(/:\/\//)) {
          // If baseHref contains a scheme, include it as is.
          return baseHref.replace(/\/$/, '') +
        `/${deployUrl}/${URL.url}`.replace(/\/\/+/g, '/');
        }
        else {
          // Join together base-href, deploy-url and the original URL.
          // Also dedupe multiple slashes into single ones.
          return `/${baseHref}/${deployUrl}/${URL.url}`.replace(/\/\/+/g, '/');
        }
      }
    }),
    autoprefixer(),
    customProperties({ preserve: true })
  ].concat(env === 'prod' ? [cssnano(minimizeOptions)] : []);
};

const builder = (customer, prodEnv) => {

let plugins = [
    new ProgressPlugin(),
    new NoEmitOnErrorsPlugin(),
    new FilterWarningsPlugin({
      exclude: /System.import/
    }),
    new CleanWebpackPlugin(['target/classes/static/' + customer]),
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css'
    }),
    new CopyWebpackPlugin([
        {
            context: 'src/main/angular',
            to: '',
            from: {
                glob: 'assets/**/*',
                dot: true
            }
        },
        {
            context: 'src/main/angular',
            to: '',
            from: {
                glob: 'favicon.ico',
                dot: true
            }
        }
    ], {
        ignore: [
            '.gitkeep',
            '**/.DS_Store'
        ],
        debug: 'warning'
    }),
    //Replace the actual environment file with the correct one passed in via env args
    new NormalModuleReplacementPlugin(/(.*)\environments\/environment(\.*)/, function(resource) {
        resource.request = resource.request.replace('environments/environment',
            `environments/${customer}/environment.${prodEnv}`);
    }),
    //Replace the actual chart-colors file with the correct one based on customer
    new NormalModuleReplacementPlugin(/(.*)\environments\/chart-colors.json/, function(resource) {
        resource.request = resource.request.replace('environments/chart-colors.json',
            `environments/${customer}/chart-colors.json`);
    }),
    //Replace the actual lang file with the correct one based on customer
    new NormalModuleReplacementPlugin(/(.*)\environments\/lang.json/, function(resource) {
        resource.request = resource.request.replace('environments/lang.json',
            `environments/${customer}/lang.json`);
    }),
    //Replace the actual scss file with the correct one based on customer
    new NormalModuleReplacementPlugin(/(.*)\environments\/styles.scss/, function(resource) {
        resource.request = resource.request.replace('environments/styles.scss',
            `environments/${customer}/styles.scss`);
    }),
    new AngularCompilerPlugin({
        mainPath: 'main.ts',
        platform: 0,
        sourceMap: (prodEnv === 'dev') ? true : false,
        tsConfigPath: 'src/main/angular/tsconfig.app.json',
        skipCodeGeneration: true,
        compilerOptions: {}
    }),
    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        "window.jQuery": 'jquery',
        Hammer: 'hammerjs/hammer'
    })
];

let devPlugins = [
    new CircularDependencyPlugin({
        exclude: /(\\|\/)node_modules(\\|\/)/,
        failOnError: false
    }),
    new SourceMapDevToolPlugin({
        filename: '[file].map[query]',
        moduleFilenameTemplate: '[resource-path]',
        fallbackModuleFilenameTemplate: '[resource-path]?[hash]',
        sourceRoot: 'webpack:///',
        exclude: ['vendor.js']
    }),
    new BundleAnalyzerPlugin({
        generateStatsFile: true
    })
];

let prodPlugins = [
    new UglifyJsPlugin({
        parallel: true,
        sourceMap: false
    })
];

plugins = prodEnv === 'dev'
    ? plugins.concat(devPlugins)
    : plugins.concat(prodPlugins);

return  {
  resolve: {
    extensions: [
      '.ts',
      '.js'
    ],
    modules: [
      './node_modules'
    ],
    symlinks: true,
    alias: {
      "rxjs/" : './node_modules/rxjs/_esm2015/'
    },
    mainFields: [
      'browser',
      'module',
      'main'
    ]
  },
  resolveLoader: {
    modules: [
      './node_modules'
    ]
  },
  entry: {
    main: [
      './src/main/angular/main.ts'
    ],
    polyfills: [
      './src/main/angular/polyfills.ts'
    ]
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          chunks: 'initial',
          test: path.join(process.cwd(), 'node_modules'),
          name: 'vendor',
          enforce: true,
          filename: 'vendor.chunk.js'
        }
      }
    }
  },
  output: {
    path: path.join(process.cwd(), 'target', 'classes', 'static', customer),
    filename: '[name].bundle.js',
    chunkFilename: '[id].chunk.js',
    crossOriginLoading: false
  },
  module: {
    rules: [
      {
        test: /\.html$/,
        loader: 'raw-loader'
      },
      {
        test: /\.(eot|svg|cur)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[hash:20].[ext]',
          limit: 10000
        }
      },
      {
        test: /\.(jpg|png|webp|gif|otf|ttf|woff|woff2|ani)$/,
        loader: 'url-loader',
        options: {
          name: '[name].[hash:20].[ext]',
          limit: 10000
        }
      },
      {
        test: /\.css$/,
        use: [
          'exports-loader?module.exports.toString()',
          {
            loader: 'css-loader',
            options: {
              sourceMap: false,
              importLoaders: 1
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: postcssPlugins(prodEnv)
            }
          }
        ]
      },
      {
        test: /\.css$/,
        include: [
            path.join(process.cwd(), `src/main/angular/environments/${customer}/styles.scss`)
        ],
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              sourceMap: false,
              importLoaders: 1
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: postcssPlugins(prodEnv)
            }
          }
        ]
      },
      {
        test: /\.scss$/,
        include: [
          path.join(process.cwd(), `src/main/angular/environments/${customer}/styles.scss`)
        ],
        use: [
            MiniCssExtractPlugin.loader,
            {
                loader: 'css-loader',
                options: {
                    sourceMap: false,
                    importLoaders: 1
                }
            },
            {
                loader: 'postcss-loader',
                options: {
                    ident: 'postcss',
                    plugins: postcssPlugins(prodEnv)
                }
            },
            {
                loader: 'sass-loader',
                options: {
                    sourceMap: false,
                    precision: 8,
                    includePaths: [path.join(process.cwd(), 'src', 'main', 'angular')]
                }
            }
          ]
        },
        {
          test: /\.ts$/,
          loader: '@ngtools/webpack'
        }
      ]
    },
    mode: (prodEnv === 'prod') ? 'production' : 'development',
    plugins: plugins,
    node: {
      fs: 'empty',
      global: true,
      crypto: 'empty',
      tls: 'empty',
      net: 'empty',
      process: true,
      module: false,
      clearImmediate: false,
      setImmediate: false
    },
    devServer: {
      historyApiFallback: true
    },
    watchOptions: {
      aggregateTimeout: 500
    }
  };
};

module.exports = {
  build: builder
}
like image 643
danwellman Avatar asked Nov 28 '18 12:11

danwellman


People also ask

Can we use Angular and NodeJS together?

One way to build Angular application is with NodeJS or Java, and the other method is in which we first build Angular, serve the static content with NGINX. If we are using it with NodeJS, then we also need to deal with server code. For example, the index. html page must be loaded with node.

Why Nodemon is crashed?

Maybe your PC running several processes in the Background. So you need to stop all the node process that are running. and then restart nodemon.

Is npm is necessary for Angular?

Angular, the Angular CLI, and Angular applications depend on npm packages for many features and functions. To download and install npm packages, you need an npm package manager.

Do I need to install NodeJS for Angular?

Once you build your app for production you won't need Node. js because the final bundles are just static HTML, CSS, and JavaScript that can be served by any server or a CDN. That being said, if you are building a full-stack web application with Angular, you may need Node.


2 Answers

This is known as memory leak which says you try to reserve a huge memory!

Note that Allocating more memory (--max_new_space_size and/or --max_old_space_size) won't solve the main problem, although may help to continue working on a memory consuming application.

Background

As you know, in javascript applications, build is a process to export minified bundles where they come from dependencies and your individual codes. In some cases, compatibility issues among dependencies (mismatch versions) may reserve more memory! For example a user found out lodash v4.14.70 is not compatible with TS 2.7 out of the box. Although you don't use lodash, such this issue can be expected.

Also, you must be familiar with cases in javascript where memory leaks happen, then figure out what is using too much memory in your application. I recommend you to Record Heap Snapshots too. I list some cases when a memory leak may occur (at a glance):

  • Pushing elements to an array, but not freeing or resetting the array
  • Attaching closures as event listener handler functions
  • Storing callbacks into an object which lives even if the callbacks are no longer needed
  • Storing symbols (with the same name) as properties of an object
  • Constantly creating new object properties (each with a different name) without deleting previous ones
  • Adding properties to a Set, a Map, or a WeakMap
  • Storing non-resolved promises in an array
  • parsing a huge JSON object

Debugging

Since your application worked fine before updating to Angular6, the main affection comes from your new dependencies. I think this is necessary to cleanup your project from unused dependencies as first step, perhaps by some tool like dependency-check (I have never tested it). Then try to migrate the project from Angular5 to Angular6 again with Angular Update Guide. Then check the compatibility among dependencies. At the time of writing this answer, I have not found any tool to find compatible versions but as a trick, You can employ versions which are released at the same period of time (concurrent).

like image 122
Amirhossein Mehrvarzi Avatar answered Oct 13 '22 11:10

Amirhossein Mehrvarzi


We've seen this happen on occasion with Angular 5 as well. Our solution (workaround?) is to run ng serve with the max_old_space_size param.

node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng serve --aot

setting --max_old_space_size=8192 works for me, but it is really a good solution for one part developers. For another we can try another solution

This is my solution, it requires people to use git bash as terminal if on windows, but it would be easy to change if needed (just use the cmd file instead):

In my project root I have a folder called scripts and in it a file called ng.sh, which is a copy from node_modules/.bin/ng but with more allowed RAM to be used

#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
    *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  "$basedir/node" --max_old_space_size=8192 "./node_modules/@angular/cli/bin/ng" "$@"
  ret=$?
else
  node --max_old_space_size=8192 "./node_modules/@angular/cli/bin/ng" "$@"
  ret=$?
fi
exit $ret

Then in my package.json I do:

"scripts": {
    "build-prod": "bash ./scripts/ng.sh build --prod --aot --env=prod"
}
like image 3
Ismoil Shifoev Avatar answered Oct 13 '22 10:10

Ismoil Shifoev