From 9ece71bc56a6e359c9ee822a58a203daffcada7c Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Wed, 17 Jan 2024 12:43:46 +0530 Subject: [PATCH 1/3] feat: add using-cached flag to bench build - add using-cache flag to node esbuild call The flag is to be used by bench get-app if its is using cache. Flag causes building of assets to not run and instead updates assets.json files from built assets. Linking of assets in the sites folder also occurs. --- esbuild/esbuild.js | 72 +++++++++++++++++++++++++++++++++++----- frappe/build.py | 6 +++- frappe/commands/utils.py | 11 ++++++ 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js index e22613e0e9..581a4ed2b0 100644 --- a/esbuild/esbuild.js +++ b/esbuild/esbuild.js @@ -64,6 +64,11 @@ const argv = yargs description: "Saves esbuild metafiles for built assets. Useful for analyzing bundle size. More info: https://esbuild.github.io/api/#metafile", }) + .option("using-cached", { + type: "boolean", + description: + "Skips build and uses cached build artifacts to update assets.json (used by Bench)", + }) .example("node esbuild --apps frappe,erpnext", "Run build only for frappe and erpnext") .example( "node esbuild --files frappe/website.bundle.js,frappe/desk.bundle.js", @@ -88,6 +93,7 @@ const NODE_PATHS = [].concat( // import js file of any app if you provide the full path app_list.map((app) => path.resolve(get_app_path(app), "..")).filter(fs.existsSync) ); +const USING_CACHED = Boolean(argv["using-cached"]); execute().catch((e) => { console.error(e); @@ -101,6 +107,12 @@ if (WATCH_MODE) { async function execute() { console.time(TOTAL_BUILD_TIME); + if (USING_CACHED) { + await update_assets_json_from_built_assets(APPS); + await update_assets_json_in_cache(); + console.timeEnd(TOTAL_BUILD_TIME); + process.exit(0); + } let results; try { @@ -131,6 +143,44 @@ async function execute() { } } +async function update_assets_json_from_built_assets(apps) { + const assets = await get_assets_json_path_and_obj(false); + const assets_rtl = await get_assets_json_path_and_obj(true); + + for (const app in apps) { + await update_assets_obj(app, assets.obj, assets_rtl.obj); + } + + for (const { obj, path } of [assets, assets_rtl]) { + const data = JSON.stringify(obj, null, 4); + await fs.promises.writeFile(path, data); + } +} + +async function update_assets_obj(app, assets, assets_rtl) { + const app_path = get_app_path(app); + const dist_path = path.join(app_path, "public, dist"); + const files = await glob("**/*.bundle.*.{js,css}", { cwd: dist_path }); + const prefix = path.join("/", "assets", app, "dist"); + + // eg: "js/marketplace.bundle.6SCSPSGQ.js" + for (const file of files) { + // eg: [ "marketplace", "bundle", "6SCSPSGQ", "js" ] + const parts = path.parse(file).base.split("."); + + // eg: "marketplace.bundle.js" + const key = [...parts.slice(0, -2), parts.at(-1)].join("."); + + // eg: "js/marketplace.bundle.6SCSPSGQ.js" + const value = path.join(prefix, file); + if (file.includes("-rtl")) { + assets_rtl[`rtl_${key}`] = value; + } else { + assets[key] = value; + } + } +} + function build_assets_for_apps(apps, files) { let { include_patterns, ignore_patterns } = files.length ? get_files_to_build(files) @@ -393,14 +443,7 @@ async function write_assets_json(metafile) { } } - let assets_json_path = path.resolve(assets_path, `assets${rtl ? "-rtl" : ""}.json`); - let assets_json; - try { - assets_json = await fs.promises.readFile(assets_json_path, "utf-8"); - } catch (error) { - assets_json = "{}"; - } - assets_json = JSON.parse(assets_json); + let { obj: assets_json, path: assets_json_path } = await get_assets_json_path_and_obj(rtl); // update with new values let new_assets_json = Object.assign({}, assets_json, out); curr_assets_json = new_assets_json; @@ -434,6 +477,19 @@ async function update_assets_json_in_cache() { }); } +async function get_assets_json_path_and_obj(is_rtl) { + const file_name = is_rtl ? "assets-rtl.json" : "assets.json"; + const assets_json_path = path.resolve(assets_path, file_name); + let assets_json; + try { + assets_json = await fs.promises.readFile(assets_json_path, "utf-8"); + } catch (error) { + assets_json = "{}"; + } + assets_json = JSON.parse(assets_json); + return { obj: assets_json, path: assets_json_path }; +} + function run_build_command_for_apps(apps) { let cwd = process.cwd(); let { execSync } = require("child_process"); diff --git a/frappe/build.py b/frappe/build.py index 03b830f0cb..49021ae6bb 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -229,6 +229,7 @@ def bundle( skip_frappe=False, files=None, save_metafiles=False, + using_cached=False, ): """concat / minify js files""" setup() @@ -246,7 +247,10 @@ def bundle( if files: command += " --files {files}".format(files=",".join(files)) - command += " --run-build-command" + if using_cached: + command += " --using-cached" + else: + command += " --run-build-command" if save_metafiles: command += " --save-metafiles" diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 8f6cb70d17..7d997e012d 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -36,6 +36,12 @@ EXTRA_ARGS_CTX = {"ignore_unknown_options": True, "allow_extra_args": True} default=False, help="Saves esbuild metafiles for built assets. Useful for analyzing bundle size. More info: https://esbuild.github.io/api/#metafile", ) +@click.option( + "--using-cached", + is_flag=True, + default=False, + help="Skips build and uses cached build artifacts (cache is set by Bench). Ignored if developer_mode enabled.", +) def build( app=None, apps=None, @@ -44,6 +50,7 @@ def build( verbose=False, force=False, save_metafiles=False, + using_cached=False, ): "Compile JS and CSS source files" from frappe.build import bundle, download_frappe_assets @@ -69,6 +76,9 @@ def build( if production: mode = "production" + if development: + using_cached = False + bundle( mode, apps=apps, @@ -76,6 +86,7 @@ def build( verbose=verbose, skip_frappe=skip_frappe, save_metafiles=save_metafiles, + using_cached=using_cached, ) if apps and isinstance(apps, str): From 9a87ec11e794d99336fee6efded1769bb3968adb Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Thu, 18 Jan 2024 13:11:41 +0530 Subject: [PATCH 2/3] fix: add command to indicate use-cached support --- frappe/commands/utils.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 7d997e012d..e8f420d97b 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -1147,8 +1147,21 @@ def rebuild_global_search(context, static_pages=False): raise SiteNotSpecifiedError +@click.command("can-use-cached") +def can_use_cached() -> None: + """ + Used by bench to check if the installed version of Frappe supports + installing apps from cached get-app artifacts. + + Bench just checks the exit code for this command, if it is not + present then the call to this command will error out. + """ + click.secho("yes", fg="green") + + commands = [ build, + can_use_cached, clear_cache, clear_website_cache, database, From 2da12b27cd054a11bcb8e657764bb27524656ace Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Thu, 18 Jan 2024 16:25:14 +0530 Subject: [PATCH 3/3] refactor: set using cached with envvar --- frappe/commands/utils.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index e8f420d97b..8abbf9ed9c 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -40,6 +40,7 @@ EXTRA_ARGS_CTX = {"ignore_unknown_options": True, "allow_extra_args": True} "--using-cached", is_flag=True, default=False, + envvar="USING_CACHED", help="Skips build and uses cached build artifacts (cache is set by Bench). Ignored if developer_mode enabled.", ) def build( @@ -1147,21 +1148,8 @@ def rebuild_global_search(context, static_pages=False): raise SiteNotSpecifiedError -@click.command("can-use-cached") -def can_use_cached() -> None: - """ - Used by bench to check if the installed version of Frappe supports - installing apps from cached get-app artifacts. - - Bench just checks the exit code for this command, if it is not - present then the call to this command will error out. - """ - click.secho("yes", fg="green") - - commands = [ build, - can_use_cached, clear_cache, clear_website_cache, database,