Skip to content

Commit 3a94354

Browse files
authored
Add bundle definitions and its measurement script. (#5706)
1 parent cf40fb8 commit 3a94354

17 files changed

+1238
-50
lines changed

.github/workflows/health-metrics-test.yml

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ name: Health Metrics
33
on: [push, pull_request]
44

55
env:
6-
METRICS_SERVICE_URL: ${{ secrets.METRICS_SERVICE_URL }}
76
GITHUB_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
7+
# TODO(yifany): parse from git commit history directly
8+
# Reason: actions/checkout@v2 does not always honor ${{ github.event.pull_request.base.sha }},
9+
# therefore "base.sha" sometimes is not the commit that actually gets merged with the
10+
# pull request head commit for CI test.
11+
# See:
12+
# - https://github.com/actions/checkout/issues/27
13+
# - https://github.com/actions/checkout/issues/237
814
GITHUB_PULL_REQUEST_BASE_SHA: ${{ github.event.pull_request.base.sha }}
915
NODE_OPTIONS: "--max-old-space-size=4096"
1016

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import * as fs from 'fs';
19+
import * as path from 'path';
20+
import * as tmp from 'tmp';
21+
22+
import { Bundler, Mode, run as runBundleAnalysis } from './bundle-analysis';
23+
import { Report } from '../../scripts/size_report/report_binary_size';
24+
25+
/**
26+
* Runs bundle analysis for all bundle definition files under:
27+
*
28+
* `firebase-js-sdk/repo-scripts/size-analysis/bundle-definitions`
29+
*
30+
* The method accepts an optional parameter `version`:
31+
* 1. when presented (for example, `version` = '9.0.0'), this method measures the bundle size by
32+
* building test bundles with dependencies at that specific version downloaded from npm
33+
* 2. when omitted (in this case, `version` = null), this method measures the bundle size by
34+
* building test bundles with dependencies from local artifacts (e.g. produced by `yarn build`)
35+
*
36+
* #1 is intended only for manual runs for the purpose of back-filling historical size data. #2 is
37+
* intended for CI runs that measure size for the current commit.
38+
*
39+
* More details on how a test bundle is built can be found in `bundle-analysis.ts`.
40+
*
41+
* @param version - If present, the SDK version to run measurement against
42+
* @returns A list of bundle size measurements
43+
*/
44+
export async function generateReportForBundles(
45+
version?: string
46+
): Promise<Report[]> {
47+
const definitionDir = `${__dirname}/bundle-definitions`;
48+
const outputDir = tmp.dirSync().name;
49+
console.log(`Bundle definitions are located at "${definitionDir}".`);
50+
console.log(`Analysis output are located at "${outputDir}".`);
51+
52+
const bundles = fs.readdirSync(definitionDir);
53+
const results: Report[] = [];
54+
for (const bundle of bundles) {
55+
const product = path.basename(bundle, '.json');
56+
const output = `${outputDir}/${product}.analysis.json`;
57+
if (version) {
58+
overwriteVersion(definitionDir, bundle, outputDir, version);
59+
}
60+
const option = {
61+
input: version ? `${outputDir}/${bundle}` : `${definitionDir}/${bundle}`,
62+
bundler: Bundler.Rollup,
63+
mode: version ? Mode.Npm : Mode.Local,
64+
output: output,
65+
debug: true
66+
};
67+
console.log(`Running for bundle "${bundle}" with mode "${option.mode}".`);
68+
await runBundleAnalysis(option);
69+
const measurements = parseAnalysisOutput(product, output);
70+
results.push(...measurements);
71+
}
72+
console.log(results);
73+
return results;
74+
}
75+
76+
function overwriteVersion(
77+
definitionDir: string,
78+
bundle: string,
79+
temp: string,
80+
version: string
81+
) {
82+
const definitions = JSON.parse(
83+
fs.readFileSync(`${definitionDir}/${bundle}`, { encoding: 'utf-8' })
84+
);
85+
for (const definition of definitions) {
86+
const dependencies = definition.dependencies;
87+
for (const dependency of dependencies) {
88+
dependency.versionOrTag = version;
89+
}
90+
}
91+
fs.writeFileSync(`${temp}/${bundle}`, JSON.stringify(definitions, null, 2), {
92+
encoding: 'utf-8'
93+
});
94+
}
95+
96+
function parseAnalysisOutput(product: string, output: string) {
97+
const analyses = JSON.parse(fs.readFileSync(output, { encoding: 'utf-8' }));
98+
const results: Report[] = [];
99+
for (const analysis of analyses) {
100+
// The API of the backend for persisting size measurements currently requires data to be
101+
// organized strictly in the below json format:
102+
//
103+
// {
104+
// sdk: <some-string>,
105+
// type: <some-string>,
106+
// value: <some-integer>
107+
// }
108+
//
109+
// We are reusing this API here, although its semantics does not make sense in the context of
110+
// bundle-analysis.
111+
const sdk = 'bundle'; // to accommodate above API syntax, can be any string
112+
const value = analysis.results[0].size;
113+
const type = `${product} (${analysis.name})`;
114+
results.push({ sdk, type, value });
115+
}
116+
return results;
117+
}

repo-scripts/size-analysis/bundle-analysis.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ interface SubModuleImport {
6464
imports: string[];
6565
}
6666

67-
enum Bundler {
67+
export enum Bundler {
6868
Rollup = 'rollup',
6969
Webpack = 'webpack',
7070
Both = 'both'
7171
}
7272

73-
enum Mode {
73+
export enum Mode {
7474
Npm = 'npm',
7575
Local = 'local'
7676
}

repo-scripts/size-analysis/bundle-definition-examples/bundle-definition-1.json

-44
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[
2+
{
3+
"name": "logEvent",
4+
"dependencies": [
5+
{
6+
"packageName": "firebase",
7+
"versionOrTag": "latest",
8+
"imports": [
9+
{
10+
"path": "app",
11+
"imports": [
12+
"initializeApp"
13+
]
14+
}
15+
]
16+
},
17+
{
18+
"packageName": "firebase",
19+
"versionOrTag": "latest",
20+
"imports": [
21+
{
22+
"path": "analytics",
23+
"imports": [
24+
"getAnalytics",
25+
"logEvent"
26+
]
27+
}
28+
]
29+
}
30+
]
31+
}
32+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
[
2+
{
3+
"name": "ReCaptchaV3Provider",
4+
"dependencies": [
5+
{
6+
"packageName": "firebase",
7+
"versionOrTag": "latest",
8+
"imports": [
9+
{
10+
"path": "app",
11+
"imports": [
12+
"initializeApp"
13+
]
14+
}
15+
]
16+
},
17+
{
18+
"packageName": "firebase",
19+
"versionOrTag": "latest",
20+
"imports": [
21+
{
22+
"path": "app-check",
23+
"imports": [
24+
"initializeAppCheck",
25+
"ReCaptchaV3Provider",
26+
"getToken"
27+
]
28+
}
29+
]
30+
}
31+
]
32+
},
33+
{
34+
"name": "ReCaptchaEnterpriseProvider",
35+
"dependencies": [
36+
{
37+
"packageName": "firebase",
38+
"versionOrTag": "latest",
39+
"imports": [
40+
{
41+
"path": "app",
42+
"imports": [
43+
"initializeApp"
44+
]
45+
}
46+
]
47+
},
48+
{
49+
"packageName": "firebase",
50+
"versionOrTag": "latest",
51+
"imports": [
52+
{
53+
"path": "app-check",
54+
"imports": [
55+
"initializeAppCheck",
56+
"ReCaptchaEnterpriseProvider",
57+
"getToken"
58+
]
59+
}
60+
]
61+
}
62+
]
63+
},
64+
{
65+
"name": "CustomProvider",
66+
"dependencies": [
67+
{
68+
"packageName": "firebase",
69+
"versionOrTag": "latest",
70+
"imports": [
71+
{
72+
"path": "app",
73+
"imports": [
74+
"initializeApp"
75+
]
76+
}
77+
]
78+
},
79+
{
80+
"packageName": "firebase",
81+
"versionOrTag": "latest",
82+
"imports": [
83+
{
84+
"path": "app-check",
85+
"imports": [
86+
"initializeAppCheck",
87+
"CustomProvider",
88+
"getToken"
89+
]
90+
}
91+
]
92+
}
93+
]
94+
}
95+
]

0 commit comments

Comments
 (0)