Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .changeset/easy-flowers-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fluentui-react-native/components": patch
"@fluentui-react-native/design": patch
---

Add sideEffects: false to package manifests
46 changes: 46 additions & 0 deletions .github/workflows/pr-bundle-size-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Bundle Size PR Comment

on:
workflow_run:
workflows: [PR]
types: [completed]

permissions:
actions: read
contents: read
pull-requests: write

concurrency:
group: bundle-size-comment-${{ github.event.workflow_run.head_repository.id }}-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: true

jobs:
comment:
name: Publish trusted bundle-size report
if: ${{ github.event.workflow_run.event == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- name: Checkout trusted reporter
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false

- name: Download bundle-size report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: bundle-size-analysis
path: bundle-size-analysis
github-token: ${{ github.token }}
repository: ${{ github.repository }}
run-id: ${{ github.event.workflow_run.id }}

- name: Publish bundle size PR comment
env:
EXPECTED_HEAD_REPOSITORY: ${{ github.event.workflow_run.head_repository.full_name }}
EXPECTED_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
GITHUB_TOKEN: ${{ github.token }}
PR_NUMBER_PATH: bundle-size-analysis/pull-request-number.txt
REPORT_PATH: bundle-size-analysis/report.md
SOURCE_RUN_ID: ${{ github.event.workflow_run.id }}
run: node apps/bundle-size/scripts/post-pr-comment.mjs
19 changes: 19 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ jobs:
name: Bundle Size PR
runs-on: windows-latest
timeout-minutes: 30
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
Expand Down Expand Up @@ -72,6 +75,22 @@ jobs:
- name: Publish bundle size summary
run: Get-Content apps\bundle-size\dist\bundle-size\report.md -Raw | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append

- name: Publish bundle size PR comment
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.login != 'dependabot[bot]' }}
env:
EXPECTED_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
GITHUB_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPORT_PATH: apps/bundle-size/dist/bundle-size/report.md
SOURCE_RUN_ID: ${{ github.run_id }}
run: node apps/bundle-size/scripts/post-pr-comment.mjs

- name: Add pull request metadata
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: Set-Content apps\bundle-size\dist\bundle-size\pull-request-number.txt $env:PR_NUMBER -NoNewline

- name: Upload bundle analysis
if: ${{ always() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down
10 changes: 7 additions & 3 deletions apps/bundle-size/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ Results, source maps, and esbuild metafiles are written to the ignored `dist/bun
directory. The runner passes `--tree-shake true` and `--metafile <scenario>.meta.json` to
`rnx-cli bundle`. The JSON report contains raw bytes, gzip bytes, contributing esbuild input
counts, Metro source-map counts, workspace package contribution bytes, and shell deltas. Raw
bytes are the primary comparison; gzip and module attribution are diagnostic signals.
bytes are the primary comparison; gzip and module attribution are diagnostic signals. The
terminal summary shows the shell's absolute cost and each package scenario's incremental module
and byte cost above its platform shell, plus signed deltas from the baseline.

The Babel configuration preserves ESM for the serializer while explicitly lowering JSX. The
explicit JSX transform is required because the desktop React Native packages publish JSX in
Expand All @@ -40,8 +42,10 @@ yarn bundle-size:update
```

Review the baseline diff together with the implementation that caused it. The PR workflow adds
the Markdown comparison to its job summary and uploads the complete `dist/bundle-size` directory,
including the esbuild metafiles, for investigation.
the Markdown comparison to its job summary, updates one persistent PR comment, and uploads the
complete `dist/bundle-size` directory, including the esbuild metafiles, for investigation.
Same-repository PRs publish directly; fork and Dependabot reports are strictly validated and
published by a separate trusted completion workflow.

## Adding a package or submodule

Expand Down
90 changes: 45 additions & 45 deletions apps/bundle-size/baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,38 @@
{
"platform": "macos",
"scenario": "components-button",
"rawBytes": 1088756,
"gzipBytes": 301160,
"moduleCount": 753,
"metroModuleCount": 748,
"rawBytes": 868023,
"gzipBytes": 248334,
"moduleCount": 550,
"metroModuleCount": 546,
"metafileInputCount": 753,
"workspaceModules": {
"@fluentui-react-native/components": 98,
"@fluentui-react-native/design": 20,
"@fluentui-react-native/components": 10,
"@fluentui-react-native/design": 17,
"@fluentui-react-native/framework-base": 26
},
"workspaceBytes": {
"@fluentui-react-native/components": 120566,
"@fluentui-react-native/design": 26777,
"@fluentui-react-native/framework-base": 12813
"@fluentui-react-native/components": 9480,
"@fluentui-react-native/design": 26458,
"@fluentui-react-native/framework-base": 11146
}
},
{
"platform": "macos",
"scenario": "components-catalog",
"rawBytes": 1090777,
"gzipBytes": 301702,
"moduleCount": 753,
"metroModuleCount": 748,
"rawBytes": 1090635,
"gzipBytes": 301676,
"moduleCount": 750,
"metroModuleCount": 745,
"metafileInputCount": 753,
"workspaceModules": {
"@fluentui-react-native/components": 98,
"@fluentui-react-native/design": 20,
"@fluentui-react-native/design": 17,
"@fluentui-react-native/framework-base": 26
},
"workspaceBytes": {
"@fluentui-react-native/components": 122538,
"@fluentui-react-native/design": 26777,
"@fluentui-react-native/design": 26642,
"@fluentui-react-native/framework-base": 12813
}
},
Expand All @@ -64,38 +64,38 @@
{
"platform": "win32",
"scenario": "components-button",
"rawBytes": 1094123,
"gzipBytes": 301643,
"moduleCount": 754,
"metroModuleCount": 749,
"rawBytes": 873381,
"gzipBytes": 248809,
"moduleCount": 551,
"metroModuleCount": 547,
"metafileInputCount": 754,
"workspaceModules": {
"@fluentui-react-native/components": 98,
"@fluentui-react-native/design": 21,
"@fluentui-react-native/components": 10,
"@fluentui-react-native/design": 18,
"@fluentui-react-native/framework-base": 26
},
"workspaceBytes": {
"@fluentui-react-native/components": 120582,
"@fluentui-react-native/design": 26814,
"@fluentui-react-native/framework-base": 12813
"@fluentui-react-native/components": 9482,
"@fluentui-react-native/design": 26495,
"@fluentui-react-native/framework-base": 11146
}
},
{
"platform": "win32",
"scenario": "components-catalog",
"rawBytes": 1096141,
"gzipBytes": 302170,
"moduleCount": 754,
"metroModuleCount": 749,
"rawBytes": 1096000,
"gzipBytes": 302161,
"moduleCount": 751,
"metroModuleCount": 746,
"metafileInputCount": 754,
"workspaceModules": {
"@fluentui-react-native/components": 98,
"@fluentui-react-native/design": 21,
"@fluentui-react-native/design": 18,
"@fluentui-react-native/framework-base": 26
},
"workspaceBytes": {
"@fluentui-react-native/components": 122554,
"@fluentui-react-native/design": 26814,
"@fluentui-react-native/design": 26679,
"@fluentui-react-native/framework-base": 12813
}
},
Expand All @@ -113,38 +113,38 @@
{
"platform": "windows",
"scenario": "components-button",
"rawBytes": 1097727,
"gzipBytes": 302564,
"moduleCount": 759,
"metroModuleCount": 754,
"rawBytes": 877232,
"gzipBytes": 249752,
"moduleCount": 556,
"metroModuleCount": 552,
"metafileInputCount": 759,
"workspaceModules": {
"@fluentui-react-native/components": 98,
"@fluentui-react-native/design": 21,
"@fluentui-react-native/components": 10,
"@fluentui-react-native/design": 18,
"@fluentui-react-native/framework-base": 26
},
"workspaceBytes": {
"@fluentui-react-native/components": 120529,
"@fluentui-react-native/design": 26849,
"@fluentui-react-native/framework-base": 12813
"@fluentui-react-native/components": 9478,
"@fluentui-react-native/design": 26530,
"@fluentui-react-native/framework-base": 11146
}
},
{
"platform": "windows",
"scenario": "components-catalog",
"rawBytes": 1099748,
"gzipBytes": 303151,
"moduleCount": 759,
"metroModuleCount": 754,
"rawBytes": 1099607,
"gzipBytes": 303049,
"moduleCount": 756,
"metroModuleCount": 751,
"metafileInputCount": 759,
"workspaceModules": {
"@fluentui-react-native/components": 98,
"@fluentui-react-native/design": 21,
"@fluentui-react-native/design": 18,
"@fluentui-react-native/framework-base": 26
},
"workspaceBytes": {
"@fluentui-react-native/components": 122501,
"@fluentui-react-native/design": 26849,
"@fluentui-react-native/design": 26714,
"@fluentui-react-native/framework-base": 12813
}
}
Expand Down
2 changes: 2 additions & 0 deletions apps/bundle-size/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"format": "fluentui-scripts format",
"lint": "fluentui-scripts lint",
"measure": "node scripts/measure.mjs",
"test": "node --test scripts/*.test.mjs",
"update-baseline": "node scripts/measure.mjs --update-baseline"
},
"dependencies": {
Expand Down Expand Up @@ -35,6 +36,7 @@
"@rnx-kit/metro-resolver-symlinks": "catalog:",
"@rnx-kit/metro-serializer-esbuild": "catalog:",
"@rnx-kit/tools-filesystem": "^0.2.0",
"@rnx-kit/tools-formatting": "^0.1.0",
"metro": "^0.83.8",
"oxc-resolver": "catalog:"
},
Expand Down
45 changes: 45 additions & 0 deletions apps/bundle-size/scripts/format.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { formatAsTable } from '@rnx-kit/tools-formatting';

const kilobyteFormatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});

const integerFormatter = new Intl.NumberFormat('en-US', {
maximumFractionDigits: 0,
});

export function formatSize(bytes) {
const absoluteBytes = Math.abs(bytes);
if (absoluteBytes < 1000) {
return `${bytes}b`;
}

const sign = bytes < 0 ? '-' : '';
return `${sign}${kilobyteFormatter.format(absoluteBytes / 1000)}k`;
}

function formatSigned(value, formatter) {
const sign = value < 0 ? '-' : '+';
return `${sign}${formatter(Math.abs(value))}`;
}

export function formatBundleSizeTable(results) {
const rows = results.map(({ platform, scenario, comparison }) => [
`${platform}: ${scenario}`,
comparison.currentModuleCost,
comparison.status === 'new' ? 'New' : formatSigned(comparison.moduleCostDelta, integerFormatter.format),
formatSize(comparison.currentCost),
comparison.status === 'new' ? 'New' : formatSigned(comparison.costDelta, formatSize),
]);

return formatAsTable(rows, {
columns: [
{ label: 'Platform: scenario' },
{ label: 'New modules', align: 'right', format: integerFormatter.format },
{ label: 'Module delta', align: 'right' },
{ label: 'New size', align: 'right' },
{ label: 'Size delta', align: 'right' },
],
});
}
Loading
Loading