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
}