From 0f4520867b565c1f114293ccda547615fed36ab5 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 8 May 2024 10:40:51 +0100 Subject: [PATCH] DEV: `@babel/plugin-proposal-decorators` -> `decorator-transforms` (#25290) decorator-transforms (https://github.com/ef4/decorator-transforms) is a modern replacement for babel's plugin-proposal-decorators. It provides a decorator implementation using modern browser features, without needing to enable babel's full suite of class feature transformations. This improves the developer experience and performance. In local testing with Google's 'tachometer' tool, this reduces Discourse's 'init-to-render' time by around 3-4% (230ms -> 222ms). It reduces our initial gzip'd JS payloads by 3.2% (2.43MB -> 2.35MB), or 7.5% (14.5MB -> 13.4MB) uncompressed. --- app/assets/javascripts/admin/index.js | 17 +++++++++++++++ app/assets/javascripts/dialog-holder/index.js | 17 +++++++++++++++ .../javascripts/discourse-common/index.js | 15 +++++++++++++ .../javascripts/discourse-plugins/index.js | 11 +++++++++- app/assets/javascripts/discourse/app/app.js | 1 + .../javascripts/discourse/ember-cli-build.js | 12 ++++++++++- .../discourse/tests/setup-tests.js | 2 +- app/assets/javascripts/float-kit/index.js | 17 ++++++++++++++- app/assets/javascripts/pretty-text/index.js | 17 +++++++++++++++ app/assets/javascripts/select-kit/index.js | 17 ++++++++++++++- .../javascripts/theme-transpiler/package.json | 3 ++- .../theme-transpiler/transpiler.js | 3 +++ app/models/theme.rb | 2 +- lib/discourse_js_processor.rb | 4 +--- patches/decorator-transforms+2.0.0.patch | 21 +++++++++++++++++++ spec/lib/discourse_js_processor_spec.rb | 2 +- spec/lib/theme_javascript_compiler_spec.rb | 2 +- 17 files changed, 151 insertions(+), 12 deletions(-) create mode 100644 patches/decorator-transforms+2.0.0.patch diff --git a/app/assets/javascripts/admin/index.js b/app/assets/javascripts/admin/index.js index 8889660fdb5..65548007e09 100644 --- a/app/assets/javascripts/admin/index.js +++ b/app/assets/javascripts/admin/index.js @@ -6,6 +6,23 @@ const path = require("path"); module.exports = { name: require("./package").name, + options: { + babel: { + plugins: [ + [ + require.resolve("decorator-transforms"), + { + runEarly: true, + }, + ], + ], + }, + + "ember-cli-babel": { + disableDecoratorTransforms: true, + }, + }, + // return an empty tree here as we do not want the addon modules to be // included into vendor.js; instead, we will produce a separate bundle // (admin.js) to be included via a script tag as needed diff --git a/app/assets/javascripts/dialog-holder/index.js b/app/assets/javascripts/dialog-holder/index.js index b5b31da4a49..d982c2f690f 100644 --- a/app/assets/javascripts/dialog-holder/index.js +++ b/app/assets/javascripts/dialog-holder/index.js @@ -3,6 +3,23 @@ module.exports = { name: require("./package").name, + options: { + babel: { + plugins: [ + [ + require.resolve("decorator-transforms"), + { + runEarly: true, + }, + ], + ], + }, + + "ember-cli-babel": { + disableDecoratorTransforms: true, + }, + }, + isDevelopingAddon() { return true; }, diff --git a/app/assets/javascripts/discourse-common/index.js b/app/assets/javascripts/discourse-common/index.js index e912e37ba5f..0175dad6b86 100644 --- a/app/assets/javascripts/discourse-common/index.js +++ b/app/assets/javascripts/discourse-common/index.js @@ -8,6 +8,21 @@ module.exports = { handlebars: "handlebars/dist/cjs/handlebars.js", }, }, + + babel: { + plugins: [ + [ + require.resolve("decorator-transforms"), + { + runEarly: true, + }, + ], + ], + }, + + "ember-cli-babel": { + disableDecoratorTransforms: true, + }, }, isDevelopingAddon() { diff --git a/app/assets/javascripts/discourse-plugins/index.js b/app/assets/javascripts/discourse-plugins/index.js index 851a94da7c4..afd9493dc17 100644 --- a/app/assets/javascripts/discourse-plugins/index.js +++ b/app/assets/javascripts/discourse-plugins/index.js @@ -97,11 +97,20 @@ module.exports = { options: { babel: { - plugins: [require.resolve("deprecation-silencer")], + plugins: [ + require.resolve("deprecation-silencer"), + [ + require.resolve("decorator-transforms"), + { + runEarly: true, + }, + ], + ], }, "ember-cli-babel": { throwUnlessParallelizable: true, + disableDecoratorTransforms: true, }, "ember-this-fallback": { diff --git a/app/assets/javascripts/discourse/app/app.js b/app/assets/javascripts/discourse/app/app.js index 2c8dadadd49..803059795c2 100644 --- a/app/assets/javascripts/discourse/app/app.js +++ b/app/assets/javascripts/discourse/app/app.js @@ -1,4 +1,5 @@ /* eslint-disable simple-import-sort/imports */ +import "decorator-transforms/globals"; import "./loader-shims"; import "./global-compat"; /* eslint-enable simple-import-sort/imports */ diff --git a/app/assets/javascripts/discourse/ember-cli-build.js b/app/assets/javascripts/discourse/ember-cli-build.js index 94b0fa5681b..c281b69ce41 100644 --- a/app/assets/javascripts/discourse/ember-cli-build.js +++ b/app/assets/javascripts/discourse/ember-cli-build.js @@ -60,10 +60,20 @@ module.exports = function (defaults) { "ember-cli-babel": { throwUnlessParallelizable: true, + disableDecoratorTransforms: true, }, babel: { - plugins: [require.resolve("deprecation-silencer")], + sourceMaps: false, + plugins: [ + require.resolve("deprecation-silencer"), + [ + require.resolve("decorator-transforms"), + { + runEarly: true, + }, + ], + ], }, vendorFiles: { diff --git a/app/assets/javascripts/discourse/tests/setup-tests.js b/app/assets/javascripts/discourse/tests/setup-tests.js index 63e87609e2b..1fdeb4c0f5d 100644 --- a/app/assets/javascripts/discourse/tests/setup-tests.js +++ b/app/assets/javascripts/discourse/tests/setup-tests.js @@ -1,4 +1,5 @@ /* eslint-disable simple-import-sort/imports */ +import Application from "../app"; import "./loader-shims"; /* eslint-enable simple-import-sort/imports */ @@ -46,7 +47,6 @@ import deprecated from "discourse-common/lib/deprecated"; import { setDefaultOwner } from "discourse-common/lib/get-owner"; import { setupS3CDN, setupURL } from "discourse-common/lib/get-url"; import { buildResolver } from "discourse-common/resolver"; -import Application from "../app"; import { loadSprites } from "../lib/svg-sprite-loader"; import * as FakerModule from "@faker-js/faker"; import { setLoadedFaker } from "discourse/lib/load-faker"; diff --git a/app/assets/javascripts/float-kit/index.js b/app/assets/javascripts/float-kit/index.js index 28c7dca628f..299c6c12847 100644 --- a/app/assets/javascripts/float-kit/index.js +++ b/app/assets/javascripts/float-kit/index.js @@ -2,7 +2,22 @@ module.exports = { name: require("./package").name, - options: {}, + options: { + babel: { + plugins: [ + [ + require.resolve("decorator-transforms"), + { + runEarly: true, + }, + ], + ], + }, + + "ember-cli-babel": { + disableDecoratorTransforms: true, + }, + }, isDevelopingAddon() { return true; }, diff --git a/app/assets/javascripts/pretty-text/index.js b/app/assets/javascripts/pretty-text/index.js index b5b31da4a49..d982c2f690f 100644 --- a/app/assets/javascripts/pretty-text/index.js +++ b/app/assets/javascripts/pretty-text/index.js @@ -3,6 +3,23 @@ module.exports = { name: require("./package").name, + options: { + babel: { + plugins: [ + [ + require.resolve("decorator-transforms"), + { + runEarly: true, + }, + ], + ], + }, + + "ember-cli-babel": { + disableDecoratorTransforms: true, + }, + }, + isDevelopingAddon() { return true; }, diff --git a/app/assets/javascripts/select-kit/index.js b/app/assets/javascripts/select-kit/index.js index 28c7dca628f..299c6c12847 100644 --- a/app/assets/javascripts/select-kit/index.js +++ b/app/assets/javascripts/select-kit/index.js @@ -2,7 +2,22 @@ module.exports = { name: require("./package").name, - options: {}, + options: { + babel: { + plugins: [ + [ + require.resolve("decorator-transforms"), + { + runEarly: true, + }, + ], + ], + }, + + "ember-cli-babel": { + disableDecoratorTransforms: true, + }, + }, isDevelopingAddon() { return true; }, diff --git a/app/assets/javascripts/theme-transpiler/package.json b/app/assets/javascripts/theme-transpiler/package.json index 13dbdde2d9f..17281b863f1 100644 --- a/app/assets/javascripts/theme-transpiler/package.json +++ b/app/assets/javascripts/theme-transpiler/package.json @@ -19,7 +19,8 @@ "handlebars": "^4.7.8", "path-browserify": "^1.0.1", "polyfill-crypto.getrandomvalues": "^1.0.0", - "terser": "^5.31.0" + "terser": "^5.31.0", + "decorator-transforms": "^2.0.0" }, "engines": { "node": ">= 18", diff --git a/app/assets/javascripts/theme-transpiler/transpiler.js b/app/assets/javascripts/theme-transpiler/transpiler.js index 5336a767856..24a23167007 100644 --- a/app/assets/javascripts/theme-transpiler/transpiler.js +++ b/app/assets/javascripts/theme-transpiler/transpiler.js @@ -19,6 +19,7 @@ globalThis.console = { import { transform as babelTransform } from "@babel/standalone"; import HTMLBarsInlinePrecompile from "babel-plugin-ember-template-compilation"; import { Preprocessor } from "content-tag"; +import DecoratorTransforms from "decorator-transforms"; import colocatedBabelPlugin from "ember-cli-htmlbars/lib/colocated-babel-plugin"; import { precompile } from "ember-source/dist/ember-template-compiler"; import EmberThisFallback from "ember-this-fallback"; @@ -138,6 +139,8 @@ globalThis.transpile = function (source, options = {}) { if (moduleId && !skipModule) { plugins.push(["transform-modules-amd", { noInterop: true }]); } + commonPlugins.find((p) => p[0] === "decorator-transforms")[0] = + DecoratorTransforms; plugins.push(...commonPlugins); try { diff --git a/app/models/theme.rb b/app/models/theme.rb index 88f794ad56b..e1c766edd09 100644 --- a/app/models/theme.rb +++ b/app/models/theme.rb @@ -6,7 +6,7 @@ require "json_schemer" class Theme < ActiveRecord::Base include GlobalPath - BASE_COMPILER_VERSION = 81 + BASE_COMPILER_VERSION = 82 class SettingsMigrationError < StandardError end diff --git a/lib/discourse_js_processor.rb b/lib/discourse_js_processor.rb index 26d142fa4d8..de807a69642 100644 --- a/lib/discourse_js_processor.rb +++ b/lib/discourse_js_processor.rb @@ -9,9 +9,7 @@ class DiscourseJsProcessor # To generate a list of babel plugins used by ember-cli, set # babel: { debug: true } in ember-cli-build.js, then run `yarn ember build -prod` DISCOURSE_COMMON_BABEL_PLUGINS = [ - ["proposal-decorators", { legacy: true }], - "proposal-class-properties", - "proposal-private-methods", + ["decorator-transforms", { runEarly: true }], "proposal-class-static-block", "transform-parameters", "proposal-export-namespace-from", diff --git a/patches/decorator-transforms+2.0.0.patch b/patches/decorator-transforms+2.0.0.patch new file mode 100644 index 00000000000..6ecd084a303 --- /dev/null +++ b/patches/decorator-transforms+2.0.0.patch @@ -0,0 +1,21 @@ +diff --git a/node_modules/decorator-transforms/dist/index.js b/node_modules/decorator-transforms/dist/index.js +index fce3aeb..c23d8e4 100644 +--- a/node_modules/decorator-transforms/dist/index.js ++++ b/node_modules/decorator-transforms/dist/index.js +@@ -4,10 +4,13 @@ import { + import "./chunk-CSAU5B4Q.js"; + + // src/index.ts +-import { createRequire } from "module"; ++ + import { ImportUtil } from "babel-import-util"; +-var req = createRequire(import.meta.url); +-var { default: decoratorSyntax } = req("@babel/plugin-syntax-decorators"); ++ ++// https://github.com/ef4/decorator-transforms/pull/27 ++import PluginSyntaxDecorators from "@babel/plugin-syntax-decorators"; ++const decoratorSyntax = PluginSyntaxDecorators.default || PluginSyntaxDecorators; ++ + function makeVisitor(babel) { + const t = babel.types; + return { diff --git a/spec/lib/discourse_js_processor_spec.rb b/spec/lib/discourse_js_processor_spec.rb index 5b0142dfbc5..8c8a8f4c6b7 100644 --- a/spec/lib/discourse_js_processor_spec.rb +++ b/spec/lib/discourse_js_processor_spec.rb @@ -92,7 +92,7 @@ RSpec.describe DiscourseJsProcessor do JS result = DiscourseJsProcessor.transpile(script, "blah", "blah/mymodule") - expect(result).to include("_applyDecoratedDescriptor") + expect(result).to include("static #_ = dt7948.n") end it "correctly transpiles widget hbs" do diff --git a/spec/lib/theme_javascript_compiler_spec.rb b/spec/lib/theme_javascript_compiler_spec.rb index 6ad14c5f2fa..3d0c6b741c6 100644 --- a/spec/lib/theme_javascript_compiler_spec.rb +++ b/spec/lib/theme_javascript_compiler_spec.rb @@ -251,7 +251,7 @@ RSpec.describe ThemeJavascriptCompiler do expect(compiler.raw_content).to include( "define(\"discourse/theme-1/discourse/components/my-component\", [\"exports\",", ) - expect(compiler.raw_content).to include("_defineProperty(this, \"value\", \"foo\");") + expect(compiler.raw_content).to include('value = "foo";') expect(compiler.raw_content).to include("setComponentTemplate") expect(compiler.raw_content).to include("createTemplateFactory") end