Skip to content

Commit

Permalink
πŸ—πŸš€ Mangle values of specific enum objects (#36935)
Browse files Browse the repository at this point in the history
Reduces compressed size of `amp-story` bundles by ~0.6K
  • Loading branch information
alanorozco authored Nov 17, 2021
1 parent 2eeeb31 commit 5f27973
Show file tree
Hide file tree
Showing 9 changed files with 336 additions and 6 deletions.
1 change: 1 addition & 0 deletions build-system/babel-config/minified-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions build-system/babel-config/pre-closure-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
},
},
};
};
Original file line number Diff line number Diff line change
@@ -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',
});
Original file line number Diff line number Diff line change
@@ -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
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"plugins": [
"../../.."
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const runner = require('@babel/helper-plugin-test-runner').default;

runner(__dirname);
17 changes: 11 additions & 6 deletions extensions/amp-story/1.0/amp-story-store-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions src/core/types/enum.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

0 comments on commit 5f27973

Please sign in to comment.