Я использую Node v8.12.0 на Mac (хотя я видел эту проблему с версиями Node 9.x, а также в Linux).
Я разрабатываю приложение Angular 6 и запускаю dev с --watch
флага --watch
. Часы будут работать и могут перестроить приложение, возможно, 4 или 5 раз, затем Node выйдет со следующим выходом:
<--- 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
Я также попытался добавить --max_old_space_size=12000
, но это, похоже, не имеет никакого значения. Я не уверен, где искать причину проблемы или как начать отладку в узле. Любая помощь была бы чрезвычайно оценена!
Некоторые предпосылки: это приложение Angular 5 с выталкиваемой конфигурацией, и я обновил его до Angular 6 с той же конфигурацией, и само приложение работает должным образом. Только с момента обновления к Angular 6 этот вопрос начал возникать.
Для справки, здесь находятся части зависимости и devDependency для 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"
}
Наконец, здесь используется настраиваемая конфигурация webpack (которая работает безупречно в 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
}