From 5f279739b26f082ddb15d207b61e9c574888d8e1 Mon Sep 17 00:00:00 2001 From: Alan Orozco Date: Wed, 17 Nov 2021 07:49:42 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8F=97=F0=9F=9A=80=20Mangle=20values=20of?= =?UTF-8?q?=20specific=20enum=20objects=20(#36935)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduces compressed size of `amp-story` bundles by ~0.6K --- build-system/babel-config/minified-config.js | 1 + .../babel-config/pre-closure-config.js | 1 + .../index.js | 45 ++++++ .../test/fixtures/transform/mangle/input.js | 132 ++++++++++++++++++ .../test/fixtures/transform/mangle/output.js | 128 +++++++++++++++++ .../test/fixtures/transform/options.json | 5 + .../test/index.js | 3 + .../amp-story/1.0/amp-story-store-service.js | 17 ++- src/core/types/enum.js | 10 ++ 9 files changed, 336 insertions(+), 6 deletions(-) create mode 100644 build-system/babel-plugins/babel-plugin-mangle-object-values/index.js create mode 100644 build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/mangle/input.js create mode 100644 build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/mangle/output.js create mode 100644 build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/options.json create mode 100644 build-system/babel-plugins/babel-plugin-mangle-object-values/test/index.js diff --git a/build-system/babel-config/minified-config.js b/build-system/babel-config/minified-config.js index 7ba1d21ebe87..ef773adad991 100644 --- a/build-system/babel-config/minified-config.js +++ b/build-system/babel-config/minified-config.js @@ -25,6 +25,7 @@ function getMinifiedConfig() { const plugins = [ 'optimize-objstr', + isProd && './build-system/babel-plugins/babel-plugin-mangle-object-values', './build-system/babel-plugins/babel-plugin-jsx-style-object', getImportResolverPlugin(), argv.coverage ? 'babel-plugin-istanbul' : null, diff --git a/build-system/babel-config/pre-closure-config.js b/build-system/babel-config/pre-closure-config.js index 27a725b97e15..4fdd62dc6257 100644 --- a/build-system/babel-config/pre-closure-config.js +++ b/build-system/babel-config/pre-closure-config.js @@ -25,6 +25,7 @@ function getPreClosureConfig() { const replacePlugin = getReplacePlugin(); const preClosurePlugins = [ 'optimize-objstr', + isProd && './build-system/babel-plugins/babel-plugin-mangle-object-values', './build-system/babel-plugins/babel-plugin-jsx-style-object', getImportResolverPlugin(), argv.coverage ? 'babel-plugin-istanbul' : null, diff --git a/build-system/babel-plugins/babel-plugin-mangle-object-values/index.js b/build-system/babel-plugins/babel-plugin-mangle-object-values/index.js new file mode 100644 index 000000000000..e2a91b357fd5 --- /dev/null +++ b/build-system/babel-plugins/babel-plugin-mangle-object-values/index.js @@ -0,0 +1,45 @@ +/** + * @fileoverview Mangles the values of an object expression into their numeric + * indices + 1. + * This is useful when defining large enums, as it allows us to use descriptive + * values during development, but use short mangled values during production. + */ + +const fnName = 'mangleObjectValues'; + +module.exports = function (babel) { + const {types: t} = babel; + + return { + name: 'mangle-object-values', + visitor: { + CallExpression(path) { + const callee = path.get('callee'); + if (!callee.isIdentifier({name: fnName})) { + return; + } + const {name} = callee.node.name; + const args = path.node.arguments; + if (args.length !== 1 || !t.isObjectExpression(args[0])) { + throw path.buildCodeFrameError( + `${name}() should take a single argument that's an object expression.` + ); + } + const objectExpression = args[0]; + const seen = {}; + for (const [i, prop] of objectExpression.properties.entries()) { + if (!t.isStringLiteral(prop.value)) { + throw path.buildCodeFrameError( + `${name}() should only be used on object expressions with string values.` + ); + } + const {value} = prop.value; + // Skip 0 because it's falsy + const mangled = (seen[value] = seen[value] || i + 1); + prop.value = t.valueToNode(mangled); + } + path.replaceWith(objectExpression); + }, + }, + }; +}; diff --git a/build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/mangle/input.js b/build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/mangle/input.js new file mode 100644 index 000000000000..0928f9ae4a5f --- /dev/null +++ b/build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/mangle/input.js @@ -0,0 +1,132 @@ +// not mangled: +const x = { + foo: 'bar', + bar: 'qux', + [x]: 'y', +}; + +// not mangled: +const y = notMangled({ + foo: 'bar', + bar: 'qux', + [x]: 'y', +}); + +// mangled: +const z = mangleObjectValues({ + foo: 'bar', + bar: 'qux', + [x]: 'y', +}); + +// dupes: +const a = mangleObjectValues({ + foo: 'bar', + baz: 'qux', + // should be same as foo: + bar: 'bar', +}); + +// 100 elements: +const b = mangleObjectValues({ + k0: 'v_0', + k1: 'v_1', + k2: 'v_2', + k3: 'v_3', + k4: 'v_4', + k5: 'v_5', + k6: 'v_6', + k7: 'v_7', + k8: 'v_8', + k9: 'v_9', + k10: 'v_10', + k11: 'v_11', + k12: 'v_12', + k13: 'v_13', + k14: 'v_14', + k15: 'v_15', + k16: 'v_16', + k17: 'v_17', + k18: 'v_18', + k19: 'v_19', + k20: 'v_20', + k21: 'v_21', + k22: 'v_22', + k23: 'v_23', + k24: 'v_24', + k25: 'v_25', + k26: 'v_26', + k27: 'v_27', + k28: 'v_28', + k29: 'v_29', + k30: 'v_30', + k31: 'v_31', + k32: 'v_32', + k33: 'v_33', + k34: 'v_34', + k35: 'v_35', + k36: 'v_36', + k37: 'v_37', + k38: 'v_38', + k39: 'v_39', + k40: 'v_40', + k41: 'v_41', + k42: 'v_42', + k43: 'v_43', + k44: 'v_44', + k45: 'v_45', + k46: 'v_46', + k47: 'v_47', + k48: 'v_48', + k49: 'v_49', + k50: 'v_50', + k51: 'v_51', + k52: 'v_52', + k53: 'v_53', + k54: 'v_54', + k55: 'v_55', + k56: 'v_56', + k57: 'v_57', + k58: 'v_58', + k59: 'v_59', + k60: 'v_60', + k61: 'v_61', + k62: 'v_62', + k63: 'v_63', + k64: 'v_64', + k65: 'v_65', + k66: 'v_66', + k67: 'v_67', + k68: 'v_68', + k69: 'v_69', + k70: 'v_70', + k71: 'v_71', + k72: 'v_72', + k73: 'v_73', + k74: 'v_74', + k75: 'v_75', + k76: 'v_76', + k77: 'v_77', + k78: 'v_78', + k79: 'v_79', + k80: 'v_80', + k81: 'v_81', + k82: 'v_82', + k83: 'v_83', + k84: 'v_84', + k85: 'v_85', + k86: 'v_86', + k87: 'v_87', + k88: 'v_88', + k89: 'v_89', + k90: 'v_90', + k91: 'v_91', + k92: 'v_92', + k93: 'v_93', + k94: 'v_94', + k95: 'v_95', + k96: 'v_96', + k97: 'v_97', + k98: 'v_98', + k99: 'v_99', +}); diff --git a/build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/mangle/output.js b/build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/mangle/output.js new file mode 100644 index 000000000000..deb34a065a1e --- /dev/null +++ b/build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/mangle/output.js @@ -0,0 +1,128 @@ +// not mangled: +const x = { + foo: 'bar', + bar: 'qux', + [x]: 'y' +}; // not mangled: + +const y = notMangled({ + foo: 'bar', + bar: 'qux', + [x]: 'y' +}); // mangled: + +const z = { + foo: 1, + bar: 2, + [x]: 3 +}; // dupes: + +const a = { + foo: 1, + baz: 2, + // should be same as foo: + bar: 1 +}; // 100 elements: + +const b = { + k0: 1, + k1: 2, + k2: 3, + k3: 4, + k4: 5, + k5: 6, + k6: 7, + k7: 8, + k8: 9, + k9: 10, + k10: 11, + k11: 12, + k12: 13, + k13: 14, + k14: 15, + k15: 16, + k16: 17, + k17: 18, + k18: 19, + k19: 20, + k20: 21, + k21: 22, + k22: 23, + k23: 24, + k24: 25, + k25: 26, + k26: 27, + k27: 28, + k28: 29, + k29: 30, + k30: 31, + k31: 32, + k32: 33, + k33: 34, + k34: 35, + k35: 36, + k36: 37, + k37: 38, + k38: 39, + k39: 40, + k40: 41, + k41: 42, + k42: 43, + k43: 44, + k44: 45, + k45: 46, + k46: 47, + k47: 48, + k48: 49, + k49: 50, + k50: 51, + k51: 52, + k52: 53, + k53: 54, + k54: 55, + k55: 56, + k56: 57, + k57: 58, + k58: 59, + k59: 60, + k60: 61, + k61: 62, + k62: 63, + k63: 64, + k64: 65, + k65: 66, + k66: 67, + k67: 68, + k68: 69, + k69: 70, + k70: 71, + k71: 72, + k72: 73, + k73: 74, + k74: 75, + k75: 76, + k76: 77, + k77: 78, + k78: 79, + k79: 80, + k80: 81, + k81: 82, + k82: 83, + k83: 84, + k84: 85, + k85: 86, + k86: 87, + k87: 88, + k88: 89, + k89: 90, + k90: 91, + k91: 92, + k92: 93, + k93: 94, + k94: 95, + k95: 96, + k96: 97, + k97: 98, + k98: 99, + k99: 100 +}; diff --git a/build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/options.json b/build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/options.json new file mode 100644 index 000000000000..dc8441ad032a --- /dev/null +++ b/build-system/babel-plugins/babel-plugin-mangle-object-values/test/fixtures/transform/options.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + "../../.." + ] +} diff --git a/build-system/babel-plugins/babel-plugin-mangle-object-values/test/index.js b/build-system/babel-plugins/babel-plugin-mangle-object-values/test/index.js new file mode 100644 index 000000000000..23251129fae9 --- /dev/null +++ b/build-system/babel-plugins/babel-plugin-mangle-object-values/test/index.js @@ -0,0 +1,3 @@ +const runner = require('@babel/helper-plugin-test-runner').default; + +runner(__dirname); diff --git a/extensions/amp-story/1.0/amp-story-store-service.js b/extensions/amp-story/1.0/amp-story-store-service.js index f7690220f4d2..a686ca2abf89 100644 --- a/extensions/amp-story/1.0/amp-story-store-service.js +++ b/extensions/amp-story/1.0/amp-story-store-service.js @@ -5,6 +5,7 @@ import {deepEquals} from '#core/types/object/json'; import {dev} from '#utils/log'; import {hasOwn} from '#core/types/object'; import {registerServiceBuilder} from '../../../src/service-helpers'; +import {mangleObjectValues} from '#core/types/enum'; /** @type {string} */ const TAG = 'amp-story'; @@ -116,8 +117,8 @@ export let InteractiveReactData; */ export let State; -/** @const @enum {string} */ -export const StateProperty = { +/** @const @enum {string|number} */ +const StateProperty = mangleObjectValues({ // Embed options. CAN_INSERT_AUTOMATIC_AD: 'canInsertAutomaticAd', CAN_SHOW_AUDIO_UI: 'canShowAudioUi', @@ -170,10 +171,12 @@ export const StateProperty = { NEW_PAGE_AVAILABLE_ID: 'newPageAvailableId', PAGE_IDS: 'pageIds', PAGE_SIZE: 'pageSize', -}; +}); + +export {StateProperty}; -/** @const @enum {string} */ -export const Action = { +/** @const @enum {string|number} */ +const Action = mangleObjectValues({ ADD_INTERACTIVE_REACT: 'addInteractiveReact', ADD_TO_ACTIONS_ALLOWLIST: 'addToActionsAllowlist', CHANGE_PAGE: 'setCurrentPageId', @@ -206,7 +209,9 @@ export const Action = { SET_PAGE_SIZE: 'updatePageSize', ADD_PANNING_MEDIA_STATE: 'addPanningMediaState', SET_VIEWER_CUSTOM_CONTROLS: 'setCustomControls', -}; +}); + +export {Action}; /** * Functions to compare a data structure from the previous to the new state and diff --git a/src/core/types/enum.js b/src/core/types/enum.js index 0beffe3e0ee8..2c4a2e2db109 100644 --- a/src/core/types/enum.js +++ b/src/core/types/enum.js @@ -30,3 +30,13 @@ export function enumValues(enumObj) { } return Object.freeze(Object.values(enumObj)); } + +/** + * No effect on runtime. Merely an annotation for the compiler to shorten the + * property values of large, common enums during production. + * See babel-plugin-mangle-object-values. + * @param {T} obj + * @return {T} + * @template T + */ +export const mangleObjectValues = (obj) => obj;