Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8f6592c
chore: integrate new bundle size tool
layershifter Apr 30, 2021
3687430
move getChangedEntriesInReport() to a separate file, reuse it in both…
layershifter Jul 8, 2021
d440307
Update packages/bundle-size/src/commands/compareReports.js
layershifter Jul 8, 2021
942a4aa
Update packages/bundle-size/src/reporters/markdownReporter.js
layershifter Jul 8, 2021
dcb1e97
fix snapshot
layershifter Jul 8, 2021
3dcca65
do not mock chalk
layershifter Jul 8, 2021
86bb6b8
add comment
layershifter Jul 8, 2021
307083b
update test
layershifter Jul 8, 2021
fbd16bc
use Object.freeze, add tests for getChangedEntriesInReport
layershifter Jul 8, 2021
6eeaf81
update cliReporter to exit early
layershifter Jul 8, 2021
17d9a86
refactor, do not use tuple, extract const
layershifter Jul 8, 2021
9b30a82
use snapshot serializer and strip-ansi
layershifter Jul 8, 2021
217031c
rename variables to clarify usage
layershifter Jul 8, 2021
e150032
refactor getRemoteReport
layershifter Jul 8, 2021
859b054
move sampleReport to __fixture__
layershifter Jul 8, 2021
bd561fd
add comment to fix lint error
layershifter Jul 8, 2021
1cc78db
refactor getDirectionSymbol
layershifter Jul 8, 2021
ddd0ec5
refactor calculateDiffByMetric
layershifter Jul 8, 2021
1017421
use toEqual instead of snapshots
layershifter Jul 8, 2021
d6161d5
handle zero values
layershifter Jul 8, 2021
6bdf2cd
move url to const
layershifter Jul 8, 2021
0f0b498
add tests for compareReports & getRemoteReport
layershifter Jul 8, 2021
190e052
exclude tests from commands
layershifter Jul 8, 2021
e30d276
Merge branch 'master' of https://github.com/microsoft/fluentui into c…
layershifter Jul 9, 2021
a3257ae
fix regex
layershifter Jul 12, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions azure-pipelines.bundlesize.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ jobs:
yarn lage bundle-size --no-cache --verbose $(sinceArg)
Copy link
Member Author

@layershifter layershifter Jul 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should run bundle-size command while build is only a dependency (I also modified lage.config.js) otherwise it could cause build failures due scoped builds.

For example:

  • yarn build --no-cache --since=origin/master will run build for react-northstar only
  • yarn lage bundle-size --no-cache will run bundle-size for all packages (react-menu, react-image, etc.)
  • As required artifacts are not present (for react-image), build will fail

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yarn build --no-cache will run build for react-menu only

not sure this is correct -> yarn build from root will run lage build so all packages will be build (with dependant tasks )

Copy link
Member Author

@layershifter layershifter Jul 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, it's already an issue in other PRs, see #18876 😐 (I modified initial message to make an example more obvious).

This could be solved without merging into one command:

      - script: |
-         yarn lage bundle-size --no-cache
+         yarn lage bundle-size --no-cache $(sinceArg)
        displayName: create reports for packages

But merging will give us better concurrency as lage will run bundle-size commands immediately once all required builds are completed.

displayName: build packages & create reports

- script: |
yarn bundle-size compare-reports --branch=$(System.PullRequest.TargetBranch) --output=markdown --verbose
displayName: compare bundle size with base (PR only)
condition: variables.isPR

- task: GithubPRComment@0
displayName: Post results to PR (PR only)
condition: eq(variables.isPR, true)
inputs:
githubOwner: microsoft
githubRepo: 'fluentui'
blobFilePath: 'packages/bundle-size/dist/bundle-size.md'
status: 'success'
uniqueId: 'bundleSizeComment9423'

- script: |
yarn bundle-size upload-report --branch=$(Build.SourceBranchName) --commit-sha $(Build.SourceVersion)
displayName: upload a report (base only)
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
"@types/jest-axe": "3.2.2",
"@types/jju": "1.4.1",
"@types/node": "10.17.55",
"@types/node-fetch": "2.5.7",
"@types/prettier": "2.2.3",
"@types/react": "16.9.42",
"@types/react-dom": "16.9.10",
Expand Down Expand Up @@ -164,6 +165,7 @@
"lage": "0.27.0",
"lerna": "^3.21.0",
"lint-staged": "^10.2.9",
"node-fetch": "2.6.1",
"postcss": "8.2.10",
"postcss-loader": "4.1.0",
"postcss-modules": "4.1.3",
Expand All @@ -175,6 +177,7 @@
"satisfied": "^1.1.1",
"semver": "^6.2.0",
"storybook-addon-performance": "0.14.0",
"strip-ansi": "6.0.0",
"style-loader": "2.0.0",
"syncpack": "5.6.10",
"tsconfig-paths": "3.9.0",
Expand Down
8 changes: 8 additions & 0 deletions packages/bundle-size/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ export default {

### Commands

#### `compare-reports`

Compares local (requires call of `bundle-size measure` first) and remote results, provides output to CLI or to a Markdown file.

```sh
yarn bundle-size compare-reports --branch=main --output=["cli"|"markdown"] [--quiet]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we plan to support any commit not just branch ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's not planned yet. Currently we store data only for latest build for a branch

```

#### `measure`

```sh
Expand Down
41 changes: 41 additions & 0 deletions packages/bundle-size/__fixture__/sampleComparedReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const { emptyDiff } = require('../src/utils/compareResultsInReports');

/** @type {import('../src/utils/compareResultsInReports').ComparedReport} */
const sampleComparedReport = [
{
packageName: 'foo-package',
name: 'New entry',
path: 'foo.fixture.js',
minifiedSize: 1000,
gzippedSize: 100,
diff: emptyDiff,
},
{
packageName: 'bar-package',
name: 'An entry without diff',
path: 'bar.fixture.js',
minifiedSize: 1000,
gzippedSize: 100,
diff: {
empty: false,

minified: { delta: 0, percent: '0%' },
gzip: { delta: 0, percent: '0%' },
},
},
{
packageName: 'baz-package',
name: 'An entry with diff',
path: 'baz.fixture.js',
minifiedSize: 1000,
gzippedSize: 100,
diff: {
empty: false,

minified: { delta: 1000, percent: '100%' },
gzip: { delta: 100, percent: '100%' },
},
},
];

module.exports = sampleComparedReport;
26 changes: 26 additions & 0 deletions packages/bundle-size/__fixture__/sampleReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/** @type {import('../src/utils/collectLocalReport').BundleSizeReport} */
const sampleReport = [
{
packageName: 'foo-package',
name: 'New entry',
path: 'foo.fixture.js',
minifiedSize: 1000,
gzippedSize: 100,
},
{
packageName: 'bar-package',
name: 'An entry without diff',
path: 'bar.fixture.js',
minifiedSize: 1000,
gzippedSize: 100,
},
{
packageName: 'baz-package',
name: 'An entry with diff',
path: 'baz.fixture.js',
minifiedSize: 1000,
gzippedSize: 100,
},
];

module.exports = sampleReport;
82 changes: 82 additions & 0 deletions packages/bundle-size/src/commands/compareReports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const chalk = require('chalk');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have quit a lot untested code (other commands as well), can you add some for this new command, add coverage for other existing later?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added tests for this command in 0f0b498.


const cliReporter = require('../reporters/cliReporter');
const markdownReporter = require('../reporters/markdownReporter');
const collectLocalReport = require('../utils/collectLocalReport');
const { compareResultsInReports } = require('../utils/compareResultsInReports');
const getRemoteReport = require('../utils/getRemoteReport');
const { hrToSeconds } = require('../utils/helpers');

/**
* @param {typeof import('../index') & { branch: string, output: 'cli' | 'markdown' }} options
*/
async function compareReports(options) {
const { branch, output, quiet } = options;
const startTime = process.hrtime();

const localReportStartTime = process.hrtime();
const localReport = await collectLocalReport();

if (!quiet) {
console.log(
[chalk.blue('[i]'), `Local report prepared in ${hrToSeconds(process.hrtime(localReportStartTime))}`].join(' '),
);
}

const remoteReportStartTime = process.hrtime();
const { commitSHA, remoteReport } = await getRemoteReport(branch);

if (!quiet) {
if (commitSHA === '') {
console.log([chalk.blue('[i]'), `Remote report for "${branch}" branch was not found`].join(' '));
} else {
console.log(
[
chalk.blue('[i]'),
`Remote report for "${commitSHA}" commit fetched in ${hrToSeconds(process.hrtime(remoteReportStartTime))}`,
].join(' '),
);
}
}

const result = compareResultsInReports(localReport, remoteReport);

switch (output) {
case 'cli':
await cliReporter(result);
break;
case 'markdown':
await markdownReporter(result, commitSHA, quiet);
break;
}

if (!quiet) {
console.log(`Completed in ${hrToSeconds(process.hrtime(startTime))}`);
}
}

// ---

/** @type {import('yargs').CommandModule} */
const api = {
command: 'compare-reports',
describe: 'compares local and remote results',
builder: {
branch: {
alias: 'b',
type: 'string',
description: 'A branch to compare against',
default: 'main',
},
output: {
alias: 'o',
type: 'string',
choices: ['cli', 'markdown'],
description: 'Defines a reporter to produce output',
default: 'cli',
},
},
handler: compareReports,
};

module.exports = api;
29 changes: 29 additions & 0 deletions packages/bundle-size/src/commands/compareReports.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const getRemoteReport = jest.fn();
const collectLocalReport = jest.fn();
const compareResultsInReports = jest.fn();
const cliReporter = jest.fn();

jest.mock('../reporters/cliReporter', () => cliReporter);
jest.mock('../utils/collectLocalReport', () => collectLocalReport);
jest.mock('../utils/compareResultsInReports', () => ({ compareResultsInReports }));
jest.mock('../utils/getRemoteReport', () => getRemoteReport);

const sampleReport = require('../../__fixture__/sampleReport');
const sampleComparedReport = require('../../__fixture__/sampleComparedReport');
const { handler } = require('./compareReports');

describe('compareReports', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to come up with some more sophisticated e2e testing for our CLI tools, while this is definitely better than nothing, we are testing too much implementation details via mocking etc.

anyways thanks you!

it('fetches remote report and compares it with a local data', async () => {
const branchName = 'master';

getRemoteReport.mockImplementation(() => ({ commitSHA: 'test', remoteReport: sampleReport }));
collectLocalReport.mockImplementation(() => sampleReport);
compareResultsInReports.mockImplementation(() => sampleComparedReport);

await handler({ quiet: true, branch: branchName, output: 'cli' });

expect(getRemoteReport).toHaveBeenCalledWith(branchName);
expect(compareResultsInReports).toHaveBeenCalledWith(sampleReport, sampleReport);
expect(cliReporter).toHaveBeenCalledWith(sampleComparedReport);
});
});
5 changes: 2 additions & 3 deletions packages/bundle-size/src/commands/measure.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ const fs = require('fs').promises;
const del = require('del');
const glob = require('glob');
const path = require('path');
const prettyBytes = require('pretty-bytes');

const buildFixture = require('../utils/buildFixture');
const { hrToSeconds } = require('../utils/helpers');
const { formatBytes, hrToSeconds } = require('../utils/helpers');
const prepareFixture = require('../utils/prepareFixture');

/**
Expand Down Expand Up @@ -49,7 +48,7 @@ async function measure(options) {
const sortedMeasurements = [...measurements].sort((a, b) => a.path.localeCompare(b.path));

sortedMeasurements.forEach(r => {
table.push([r.name, prettyBytes(r.minifiedSize), prettyBytes(r.gzippedSize)]);
table.push([r.name, formatBytes(r.minifiedSize), formatBytes(r.gzippedSize)]);
});

console.log(table.toString());
Expand Down
3 changes: 2 additions & 1 deletion packages/bundle-size/src/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
const yargs = require('yargs');

const cliSetup = yargs
.commandDir('commands')
.commandDir('commands', { exclude: /.test\.js$/ })
.option('quiet', {
alias: 'q',
type: 'boolean',
description: 'Suppress verbose build output',
default: false,
})
.scriptName('bundle-size')
.version(false).argv;

module.exports = cliSetup;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`markdownReporter renders a report to a file 1`] = `
"## 📊 Bundle size report

| Package & Exports | Baseline (minified/GZIP) | PR | Change |
| :------------------------------------------------------------------------------------- | -----------------------: | ------------------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| <samp>baz-package</samp> <br /> <abbr title='baz.fixture.js'>An entry with diff</abbr> | \`2 kB\`<br />\`200 B\` | \`1 kB\`<br />\`100 B\` | \`1 kB\` <img aria-hidden=\\"true\\" src=\\"https://microsoft.github.io/sizeAuditor-website/images/icons/IncreaseYellow.svg\\" /><br />\`100 B\` <img aria-hidden=\\"true\\" src=\\"https://microsoft.github.io/sizeAuditor-website/images/icons/IncreaseYellow.svg\\" /> |
| <samp>foo-package</samp> <br /> <abbr title='foo.fixture.js'>New entry</abbr> | \`0 B\`<br />\`0 B\` | \`1 kB\`<br />\`100 B\` | 🆕 New entry |

<details>
<summary>Unchanged fixtures</summary>

| Package & Exports | Size (minified/GZIP) |
| ----------------------------------------------------------------------------------------- | -------------------: |
| <samp>bar-package</samp> <br /> <abbr title='bar.fixture.js'>An entry without diff</abbr> | \`1 kB\`<br />\`100 B\` |

</details>
<sub>🤖 This report was generated against <a href='https://github.com/microsoft/fluentui/commit/commit-hash'>commit-hash</a></sub>
"
`;
72 changes: 72 additions & 0 deletions packages/bundle-size/src/reporters/cliReporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const chalk = require('chalk');
const Table = require('cli-table3');

const getChangedEntriesInReport = require('../utils/getChangedEntriesInReport');
const { formatBytes } = require('../utils/helpers');

/**
* @param {number} value
*
* @return {string}
*/
function getDirectionSymbol(value) {
if (value < 0) {
return '↓';
}

if (value > 0) {
return '↑';
}

return '';
}

/**
* @param {import('../utils/calculateDiffByMetric').DiffByMetric} diff
*
* @return {string}
*/
function formatDelta({ delta, percent }) {
if (delta === 0) {
return '';
}

const colorFn = delta > 0 ? chalk.red : chalk.green;

return colorFn(percent + getDirectionSymbol(delta));
}

/**
* @param {import('../utils/compareResultsInReports').ComparedReport} report
*/
module.exports = async function cliReporter(report) {
const result = new Table({
colAligns: ['left', 'right', 'right'],
head: ['Fixture', 'Before', 'After (minified/GZIP)'],
});
const { changedEntries } = getChangedEntriesInReport(report);

changedEntries.forEach(entry => {
const { diff, gzippedSize, minifiedSize, name, packageName } = entry;
const fixtureColumn = chalk.bold(packageName) + '\n' + name + (diff.empty ? chalk.cyan(' (new)') : '');

const minifiedBefore = diff.empty ? 'N/A' : formatBytes(minifiedSize + diff.minified.delta);
const gzippedBefore = diff.empty ? 'N/A' : formatBytes(gzippedSize + diff.gzip.delta);

const minifiedAfter = formatBytes(minifiedSize);
const gzippedAfter = formatBytes(gzippedSize);

const beforeColumn = minifiedBefore + '\n' + gzippedBefore;
const afterColumn =
formatDelta(diff.minified) + ' ' + minifiedAfter + '\n' + formatDelta(diff.gzip) + ' ' + gzippedAfter;

result.push([fixtureColumn, beforeColumn, afterColumn]);
});

if (result.length > 0) {
console.log(result.toString());
return;
}

console.log(`${chalk.green('[✔]')} No changes found`);
};
Loading