11

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
}
danwellman
  • 9,068
  • 8
  • 60
  • 88
  • Does this happen on mac only or did you test it on windows too without issues? Out of memory is quite strange. Is your angular project running on webpack or are you using the angular-cli? if you're using webpack, did you try to clean the webpack cache? – briosheje Nov 28 '18 at 12:44
  • It happens on Mac and Linux platforms, I did not test Windows. It's running on Angular 6, which uses Webpack internally. Previously we had Angular 5 with ejected Webpack config, but this is not supported in Angular 6, so I am injecting previous config into Webpack with a custom builder (https://www.npmjs.com/package/@angular-builders/custom-webpack). I did not try clearing cache yet, will try it now – danwellman Nov 28 '18 at 13:11
  • angular-cli uses webpack internally, of course, but many other environments don't rely on angular-cli (we don't use it neither with angular 6, neither with angular 7, we use a custom webpack configuration which builds the angular project, that's why I was asking). Aside from trying to clean the webpack cache, there isn't that much else to do other than opening an issue to angular-cli to me, unless you can find specifically what are some possible causes of CALL_AND_RETRY_LAST. – briosheje Nov 28 '18 at 15:04
  • I may have to open an issue, if only to have Webpack team close it with message about non-standard config being unsupported :) we wanted to use cli to make use of schematics. I looked for info on cleaning cache, but all I can find is about configuring cache-busting... – danwellman Nov 28 '18 at 15:47
  • All I can tell about cache is that, usually, as far as I can see, webpack creates a `webpack-cache` folder, where it stores data to recycle between the builds. If the heap is growing without control between various watched builds, it might be because something is wrong in the recycle process, hence the heap error. Of course it's just a supposition, but clearing that folder surely may help. In my case specifically, **without** using angular-cli, I had some issues related to the heap that were caused by an improper build on angular 5. Clearing webpack-cache solved the issue. – briosheje Nov 28 '18 at 16:09
  • Another thing you can try is updating to angular 7, by updating `@angular-cli`, `@ngtools/webpack`, `@angular-devkit/build-optimizer`, `@angular/compiler-cli` to the latest version (which should be 7.1.x as of today). Switching from angular 6 to 7 should **not** be that hard, since there are not major changes like rxjs updates or whatever, so it should be quite clean, unless you already tried that and reverted to angular 6. – briosheje Nov 28 '18 at 16:13
  • The upgrade docs say not to update more than 1 major version at a time, so I went from 5-6. I could try 7, but it would be nice to understand what is causing the issue if it isn't just some peculiarity with version 6 – danwellman Dec 02 '18 at 17:32
  • You had try this https://stackoverflow.com/questions/48906968/ng-build-prod-have-javascript-heap-out-of-memory-error ? – IftekharDani Dec 03 '18 at 11:20
  • I'm already at TypeScript 2.9.2 – danwellman Dec 03 '18 at 11:39
  • 1
    I have experienced this issue on windows too.... – programoholic Dec 03 '18 at 12:41
  • `--max_old_space_size=12000` to `--max-old-space-size=12000`. Underscore to dashes. – Gonzalo Lorieto Dec 03 '18 at 14:23
  • @GonzaloLorieto thanks. It hasn't fixed the problem, but the app now rebuilds 9/10 times before crashing out with the same error shown above :) – danwellman Dec 03 '18 at 14:51
  • Try to set node options first - `export NODE_OPTIONS=--max_old_space_size=4096` and then run angular commands (e.g. `npm test`) – taras-d Dec 04 '18 at 09:09
  • It's not that the flag wasn't being applied - it was being applied, it just didn't solve the underlying issue. It increased the time in between crashes, but I want to stop the crashes altogether. Call me old-fashioned, but I don't think a TS/SCSS build should need 12Gb of memory :) – danwellman Dec 04 '18 at 16:13
  • There is a similarity to this issue [Build fails with JavaScript heap out of memory](https://github.com/angular/angular-cli/issues/12645). Since you have Angular v6, you could perhaps downgrade Angular CLI to v6 as well. –  Dec 10 '18 at 01:36
  • The CLI doesn't have the same version as Angular itself (no 6.1.10), but I tried a couple of 6.x versions and still have the same issue. – danwellman Dec 10 '18 at 18:31

2 Answers2

4

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).

Amirhossein Mehrvarzi
  • 18,024
  • 7
  • 45
  • 70
  • I'll give the dependency check tool a whirl and see if it sheds any light on things, and will have another go at recording some heap snapshots. I did do the update whilst following the Angular Update Guide. – danwellman Dec 05 '18 at 08:28
  • Dependency-check wasn't any use unfortunately - it just listed every single entry in deps/devDeps as unused, which is definitely not the case – danwellman Dec 05 '18 at 08:42
  • @danwellman Another question which I didn't placed yet, I see 6 webpack deps in your `devDependencies` while I have never used even one of them in my projects & they are working fine. Are they added by yourself? Why do you include them? – Amirhossein Mehrvarzi Dec 05 '18 at 12:51
  • The app had an ejected config previously, so I am merging an updated version of the Webpack config into the CLI's internal Webpack config using `@angular-builders/custom-webpack` – danwellman Dec 05 '18 at 21:58
3

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"
}
Ismoil Shifoev
  • 5,512
  • 3
  • 24
  • 32