Skip to content

Commit

Permalink
Perform a check of which files contribute to BaseRealtime bundle size
Browse files Browse the repository at this point in the history
To give us further confidence that something unexpected won’t sneak in.

This just automates a rather crude process that I’ve been performing
manually from time to time — tweak the moduleReport script to spit out
source file + source map, then run source-map-explorer on the
output and look at which files are making a significant contribution to
the bundle size.

Part of #1497.
  • Loading branch information
lawrence-forooghian committed Nov 27, 2023
1 parent 14153d0 commit 9653e93
Showing 1 changed file with 123 additions and 4 deletions.
127 changes: 123 additions & 4 deletions scripts/moduleReport.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as esbuild from 'esbuild';
import * as path from 'path';
import { explore } from 'source-map-explorer';

// List of all modules accepted in ModulesMap
const moduleNames = [
Expand Down Expand Up @@ -34,8 +36,14 @@ function formatBytes(bytes: number) {
return `${formatted} KiB`;
}

// Gets the bundled size in bytes of an array of named exports from 'ably/modules'
function getImportSize(modules: string[]) {
interface BundleInfo {
byteSize: number;
code: Uint8Array;
sourceMap: Uint8Array;
}

// Uses esbuild to create a bundle containing the named exports from 'ably/modules'
function getBundleInfo(modules: string[]): BundleInfo {
const outfile = modules.join('');
const result = esbuild.buildSync({
stdin: {
Expand All @@ -47,9 +55,36 @@ function getImportSize(modules: string[]) {
bundle: true,
outfile,
write: false,
sourcemap: 'external',
});

return result.metafile.outputs[outfile].bytes;
const pathHasBase = (component: string) => {
return (outputFile: esbuild.OutputFile) => {
return path.parse(outputFile.path).base === component;
};
};

const codeOutputFile = result.outputFiles.find(pathHasBase(outfile))!;
const sourceMapOutputFile = result.outputFiles.find(pathHasBase(`${outfile}.map`))!;

return {
byteSize: result.metafile.outputs[outfile].bytes,
code: codeOutputFile.contents,
sourceMap: sourceMapOutputFile.contents,
};
}

// Gets the bundled size in bytes of an array of named exports from 'ably/modules'
function getImportSize(modules: string[]) {
const bundleInfo = getBundleInfo(modules);
return bundleInfo.byteSize;
}

async function runSourceMapExplorer(bundleInfo: BundleInfo) {
return explore({
code: Buffer.from(bundleInfo.code),
map: Buffer.from(bundleInfo.sourceMap),
});
}

function printAndCheckModuleSizes() {
Expand Down Expand Up @@ -111,11 +146,95 @@ function printAndCheckFunctionSizes() {
return errors;
}

(function run() {
// Performs a sense check that there are no unexpected files making a large contribution to the BaseRealtime bundle size.
async function checkBaseRealtimeFiles() {
const baseRealtimeBundleInfo = getBundleInfo(['BaseRealtime']);
const exploreResult = await runSourceMapExplorer(baseRealtimeBundleInfo);

const files = exploreResult.bundles[0].files;
delete files['[sourceMappingURL]'];
delete files['[unmapped]'];
delete files['[EOLs]'];

const thresholdBytes = 100;
const filesAboveThreshold = Object.entries(files).filter((file) => file[1].size >= thresholdBytes);

// These are the files that are allowed to contribute >= `threshold` bytes to the BaseRealtime bundle.
//
// The threshold is chosen pretty arbitrarily. There are some files (e.g. presencemessage.ts) whose bulk should not be included in the BaseRealtime bundle, but which make a small contribution to the bundle (probably because we make use of one exported constant or something; I haven’t looked into it).
const allowedFiles = new Set([
'src/common/constants/HttpStatusCodes.ts',
'src/common/constants/TransportName.ts',
'src/common/constants/XHRStates.ts',
'src/common/lib/client/auth.ts',
'src/common/lib/client/baseclient.ts',
'src/common/lib/client/baserealtime.ts',
'src/common/lib/client/channelstatechange.ts',
'src/common/lib/client/connection.ts',
'src/common/lib/client/connectionstatechange.ts',
'src/common/lib/client/realtimechannel.ts',
'src/common/lib/transport/connectionerrors.ts',
'src/common/lib/transport/connectionmanager.ts',
'src/common/lib/transport/messagequeue.ts',
'src/common/lib/transport/protocol.ts',
'src/common/lib/transport/transport.ts',
'src/common/lib/types/errorinfo.ts',
'src/common/lib/types/message.ts',
'src/common/lib/types/protocolmessage.ts',
'src/common/lib/types/pushchannelsubscription.ts', // TODO why? https://github.com/ably/ably-js/issues/1506
'src/common/lib/util/defaults.ts',
'src/common/lib/util/eventemitter.ts',
'src/common/lib/util/logger.ts',
'src/common/lib/util/multicaster.ts',
'src/common/lib/util/utils.ts',
'src/platform/web/config.ts',
'src/platform/web/lib/http/http.ts',
'src/platform/web/lib/util/bufferutils.ts',
'src/platform/web/lib/util/defaults.ts',
'src/platform/web/lib/util/hmac-sha256.ts',
'src/platform/web/lib/util/webstorage.ts',
'src/platform/web/modules.ts',
]);

const errors: Error[] = [];

// Check that no files other than those allowed above make a large contribution to the bundle size
for (const file of filesAboveThreshold) {
if (!allowedFiles.has(file[0])) {
errors.push(
new Error(
`Unexpected file ${file[0]}, contributes ${file[1].size}B to BaseRealtime, more than allowed ${thresholdBytes}B`
)
);
}
}

// Check that there are no stale entries in the allowed list
for (const allowedFile of Array.from(allowedFiles)) {
const file = files[allowedFile];

if (file) {
if (file.size < thresholdBytes) {
errors.push(
new Error(
`File ${allowedFile} contributes ${file.size}B, which is less than the expected minimum of ${thresholdBytes}B. Remove it from the \`allowedFiles\` list.`
)
);
}
} else {
errors.push(new Error(`File ${allowedFile} is referenced in \`allowedFiles\` but does not contribute to bundle`));
}
}

return errors;
}

(async function run() {
const errors: Error[] = [];

errors.push(...printAndCheckModuleSizes());
errors.push(...printAndCheckFunctionSizes());
errors.push(...(await checkBaseRealtimeFiles()));

if (errors.length > 0) {
for (const error of errors) {
Expand Down

0 comments on commit 9653e93

Please sign in to comment.