From 25fabccd596a7a4b0e5e15752c037dbdb3e092ae Mon Sep 17 00:00:00 2001 From: David Taylor Date: Thu, 9 Feb 2023 16:24:24 +0000 Subject: [PATCH] DEV: Enable parallel babel processing in ember-cli (#20215) Ember CLI will automatically run babel transformations in parallel when the config is 'serializable', and can therefore be applied in multiple processes automatically. If any plugin is defined in an unserializable way, parallelisation will be disabled. Our discourse-widget-hbs transformer was causing parallelisation to be disabled. This commit fixes that, and also enables the throwUnlessParallelizable flag so that we catch this kind of issue more easily in future. This commit also refactors our deprecation silencing system into its own file, and uses a fake babel plugin to ensure deprecations are silenced in babel worker processes. In our GitHub CI jobs, this doubles the speed of ember builds (1m30s -> 45s). It should also improve production deploy times, and cold-start dev builds. --- .../javascripts/discourse-widget-hbs/index.js | 14 ++--- .../lib}/widget-hbs-compiler.js | 2 + .../javascripts/discourse/ember-cli-build.js | 36 ++++-------- .../discourse/lib/deprecation-silencer.js | 56 +++++++++++++++++++ lib/discourse_js_processor.rb | 5 +- 5 files changed, 81 insertions(+), 32 deletions(-) rename {lib/javascripts => app/assets/javascripts/discourse-widget-hbs/lib}/widget-hbs-compiler.js (99%) create mode 100644 app/assets/javascripts/discourse/lib/deprecation-silencer.js diff --git a/app/assets/javascripts/discourse-widget-hbs/index.js b/app/assets/javascripts/discourse-widget-hbs/index.js index 21fe440d27d..d76f936f732 100644 --- a/app/assets/javascripts/discourse-widget-hbs/index.js +++ b/app/assets/javascripts/discourse-widget-hbs/index.js @@ -1,9 +1,6 @@ "use strict"; -const WidgetHbsCompiler = - require("../../../../lib/javascripts/widget-hbs-compiler").WidgetHbsCompiler; - -const glimmer = require("@glimmer/syntax"); +const widgetHbsCompilerPath = require.resolve("./lib/widget-hbs-compiler"); module.exports = { name: require("./package").name, @@ -15,9 +12,12 @@ module.exports = { addonOptions.babel.plugins = addonOptions.babel.plugins || []; let babelPlugins = addonOptions.babel.plugins; - WidgetHbsCompiler.cacheKey = () => "discourse-widget-hbs"; - WidgetHbsCompiler.glimmer = glimmer; - babelPlugins.push(WidgetHbsCompiler); + babelPlugins.push({ + _parallelBabel: { + requireFile: widgetHbsCompilerPath, + useMethod: "WidgetHbsCompiler", + }, + }); }, _getAddonOptions() { diff --git a/lib/javascripts/widget-hbs-compiler.js b/app/assets/javascripts/discourse-widget-hbs/lib/widget-hbs-compiler.js similarity index 99% rename from lib/javascripts/widget-hbs-compiler.js rename to app/assets/javascripts/discourse-widget-hbs/lib/widget-hbs-compiler.js index 0b70f166950..9efebf16ec6 100644 --- a/lib/javascripts/widget-hbs-compiler.js +++ b/app/assets/javascripts/discourse-widget-hbs/lib/widget-hbs-compiler.js @@ -373,4 +373,6 @@ const WidgetHbsCompiler = function (babel) { }; }; +WidgetHbsCompiler.cacheKey = () => "discourse-widget-hbs"; + exports.WidgetHbsCompiler = WidgetHbsCompiler; diff --git a/app/assets/javascripts/discourse/ember-cli-build.js b/app/assets/javascripts/discourse/ember-cli-build.js index 336efc808ca..e273e21ccaa 100644 --- a/app/assets/javascripts/discourse/ember-cli-build.js +++ b/app/assets/javascripts/discourse/ember-cli-build.js @@ -10,36 +10,16 @@ const { parsePluginClientSettings } = require("./lib/site-settings-plugin"); const discourseScss = require("./lib/discourse-scss"); const generateScriptsTree = require("./lib/scripts"); const funnel = require("broccoli-funnel"); - -const SILENCED_WARN_PREFIXES = [ - "Setting the `jquery-integration` optional feature flag", - "The Ember Classic edition has been deprecated", - "Setting the `template-only-glimmer-components` optional feature flag to `false`", - "DEPRECATION: Invoking the `` component with positional arguments is deprecated", -]; +const DeprecationSilencer = require("./lib/deprecation-silencer"); module.exports = function (defaults) { let discourseRoot = resolve("../../../.."); let vendorJs = discourseRoot + "/vendor/assets/javascripts/"; - // Silence the warnings listed in SILENCED_WARN_PREFIXES + // Silence deprecations which we are aware of - see `lib/deprecation-silencer.js` const ui = defaults.project.ui; - const oldWriteWarning = ui.writeWarnLine.bind(ui); - ui.writeWarnLine = (message, ...args) => { - if (!SILENCED_WARN_PREFIXES.some((prefix) => message.startsWith(prefix))) { - return oldWriteWarning(message, ...args); - } - }; - - // Silence warnings which go straight to console.warn (e.g. template compiler deprecations) - /* eslint-disable no-console */ - const oldConsoleWarn = console.warn.bind(console); - console.warn = (message, ...args) => { - if (!SILENCED_WARN_PREFIXES.some((prefix) => message.startsWith(prefix))) { - return oldConsoleWarn(message, ...args); - } - }; - /* eslint-enable no-console */ + DeprecationSilencer.silenceUiWarn(ui); + DeprecationSilencer.silenceConsoleWarn(); const isProduction = EmberApp.env().includes("production"); const isTest = EmberApp.env().includes("test"); @@ -111,6 +91,14 @@ module.exports = function (defaults) { ], }, + "ember-cli-babel": { + throwUnlessParallelizable: true, + }, + + babel: { + plugins: [DeprecationSilencer.generateBabelPlugin()], + }, + // We need to build tests in prod for theme tests tests: true, diff --git a/app/assets/javascripts/discourse/lib/deprecation-silencer.js b/app/assets/javascripts/discourse/lib/deprecation-silencer.js new file mode 100644 index 00000000000..e6ff2d4107f --- /dev/null +++ b/app/assets/javascripts/discourse/lib/deprecation-silencer.js @@ -0,0 +1,56 @@ +const SILENCED_WARN_PREFIXES = [ + "Setting the `jquery-integration` optional feature flag", + "The Ember Classic edition has been deprecated", + "Setting the `template-only-glimmer-components` optional feature flag to `false`", + "DEPRECATION: Invoking the `` component with positional arguments is deprecated", +]; + +let consoleWarnSilenced = false; + +module.exports = class DeprecationSilencer { + static silenceUiWarn(ui) { + const oldWriteWarning = ui.writeWarnLine.bind(ui); + ui.writeWarnLine = (message, ...args) => { + if ( + !SILENCED_WARN_PREFIXES.some((prefix) => message.startsWith(prefix)) + ) { + return oldWriteWarning(message, ...args); + } + }; + } + + static silenceConsoleWarn() { + if (consoleWarnSilenced) { + return; + } + /* eslint-disable no-console */ + const oldConsoleWarn = console.warn.bind(console); + console.warn = (message, ...args) => { + if ( + !SILENCED_WARN_PREFIXES.some((prefix) => message.startsWith(prefix)) + ) { + return oldConsoleWarn(message, ...args); + } + }; + /* eslint-enable no-console */ + consoleWarnSilenced = true; + } + + /** + * Generates a dummy babel plugin which applies the console.warn silences in worker + * processes. Does not actually affect babel output. + */ + static generateBabelPlugin() { + return { + _parallelBabel: { + requireFile: require.resolve("./deprecation-silencer"), + buildUsing: "babelShim", + }, + }; + } + + static babelShim() { + DeprecationSilencer.silenceConsoleWarn(); + return {}; + } +}; diff --git a/lib/discourse_js_processor.rb b/lib/discourse_js_processor.rb index f0452f0f1b1..d7bfb8abf42 100644 --- a/lib/discourse_js_processor.rb +++ b/lib/discourse_js_processor.rb @@ -172,7 +172,10 @@ class DiscourseJsProcessor ) # Widget HBS compiler - widget_hbs_compiler_source = File.read("#{Rails.root}/lib/javascripts/widget-hbs-compiler.js") + widget_hbs_compiler_source = + File.read( + "#{Rails.root}/app/assets/javascripts/discourse-widget-hbs/lib/widget-hbs-compiler.js", + ) widget_hbs_compiler_source = <<~JS define("widget-hbs-compiler", ["exports"], function(exports){ #{widget_hbs_compiler_source}