diff --git a/frappe/build.py b/frappe/build.py index c1c807c8db..baedb633b6 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -15,6 +15,7 @@ import frappe from frappe.utils.minify import JavascriptMinify import click +import psutil from six import iteritems, text_type from six.moves.urllib.parse import urlparse @@ -226,7 +227,7 @@ def bundle(no_compress, app=None, make_copy=False, restore=False, verbose=False, frappe_app_path = os.path.abspath(os.path.join(app_paths[0], "..")) check_yarn() - frappe.commands.popen(command, cwd=frappe_app_path) + frappe.commands.popen(command, cwd=frappe_app_path, env=get_node_env()) def watch(no_compress): @@ -238,13 +239,32 @@ def watch(no_compress): frappe_app_path = os.path.abspath(os.path.join(app_paths[0], "..")) check_yarn() frappe_app_path = frappe.get_app_path("frappe", "..") - frappe.commands.popen("{pacman} run watch".format(pacman=pacman), cwd=frappe_app_path) + frappe.commands.popen("{pacman} run watch".format(pacman=pacman), + cwd=frappe_app_path, env=get_node_env()) def check_yarn(): if not find_executable("yarn"): print("Please install yarn using below command and try again.\nnpm install -g yarn") +def get_node_env(): + node_env = { + "NODE_OPTIONS": f"--max_old_space_size={get_safe_max_old_space_size()}" + } + return node_env + +def get_safe_max_old_space_size(): + safe_max_old_space_size = 0 + try: + total_memory = psutil.virtual_memory().total / (1024 * 1024) + # reference for the safe limit assumption + # https://nodejs.org/api/cli.html#cli_max_old_space_size_size_in_megabytes + # set minimum value 1GB + safe_max_old_space_size = max(1024, int(total_memory * 0.75)) + except Exception: + pass + + return safe_max_old_space_size def make_asset_dirs(make_copy=False, restore=False): # don't even think of making assets_path absolute - rm -rf ahead. diff --git a/frappe/commands/__init__.py b/frappe/commands/__init__.py index b7294fff77..b9ae02e112 100644 --- a/frappe/commands/__init__.py +++ b/frappe/commands/__init__.py @@ -11,6 +11,7 @@ import frappe.utils import subprocess # nosec from functools import wraps from six import StringIO +from os import environ click.disable_unicode_literals_warning = True @@ -53,16 +54,20 @@ def get_site(context, raise_err=True): return None def popen(command, *args, **kwargs): - output = kwargs.get('output', True) - cwd = kwargs.get('cwd') - shell = kwargs.get('shell', True) + output = kwargs.get('output', True) + cwd = kwargs.get('cwd') + shell = kwargs.get('shell', True) raise_err = kwargs.get('raise_err') + env = kwargs.get('env') + if env: + env = dict(environ, **env) proc = subprocess.Popen(command, - stdout = None if output else subprocess.PIPE, - stderr = None if output else subprocess.PIPE, - shell = shell, - cwd = cwd + stdout=None if output else subprocess.PIPE, + stderr=None if output else subprocess.PIPE, + shell=shell, + cwd=cwd, + env=env ) return_ = proc.wait() diff --git a/requirements.txt b/requirements.txt index 1764834d8e..0f88a48f73 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,6 +38,7 @@ passlib==1.7.3 pdfkit==0.6.1 Pillow>=8.0.0 premailer==3.6.1 +psutil==5.7.2 psycopg2-binary==2.8.4 pyasn1==0.4.8 PyJWT==1.7.1