From 381189da9b2e7c5884456f31f82d450a549e74bc Mon Sep 17 00:00:00 2001 From: Liz Mitchell Date: Wed, 13 Dec 2023 11:23:32 -0800 Subject: [PATCH] chore: add readme link to size and bot --- .github/workflows/update-size-on-main.yml | 43 +++++++ README.md | 1 + docs/size.md | 131 ++++++++++++---------- scripts/size/update-size.ts | 42 ++++--- 4 files changed, 133 insertions(+), 84 deletions(-) create mode 100644 .github/workflows/update-size-on-main.yml diff --git a/.github/workflows/update-size-on-main.yml b/.github/workflows/update-size-on-main.yml new file mode 100644 index 000000000..16bc7dd8a --- /dev/null +++ b/.github/workflows/update-size-on-main.yml @@ -0,0 +1,43 @@ +name: Update size + +on: + push: + branches: main + workflow_dispatch: + # allows triggering from the github UI +jobs: + check-for-doc-changes: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: 20 + cache: npm + - uses: google/wireit@setup-github-actions-caching/v1 + + - name: Install Dependencies + run: npm ci + + - name: Update Size + run: npm run update-size + + - name: Check if update-size produces git diff + id: ifChange + run: git diff --exit-code || echo "::set-output name=changed::yes" + + - name: Create or update PR + if: steps.ifChange.outputs.changed == 'yes' + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.LIT_ROBOT_ACCESS_TOKEN }} + commit-message: 'chore: update sizes' + author: lit-robot + committer: lit-robot + title: 'chore: update sizes' + body: This PR was auto generated by the update-size-on-main GitHub action. + reviewers: e111077,asyncliz + branch: auto-update-size + # Don't automatically add Ready for Google label until we're ready + # since this will be noisy. diff --git a/README.md b/README.md index 5f0794842..71528ee6e 100644 --- a/README.md +++ b/README.md @@ -16,5 +16,6 @@ Google's open-source design system. - [Introduction](docs/intro.md) - [Roadmap](docs/roadmap.md) - [Quick start](docs/quick-start.md) +- [Bundle sizes](docs/size.md) - [Component docs](docs/components/) - [Browser support and FAQ](docs/support.md) diff --git a/docs/size.md b/docs/size.md index aee249d4a..002c71ed7 100644 --- a/docs/size.md +++ b/docs/size.md @@ -4,83 +4,92 @@ This doc tracks important size metrics for Material Web Components. +Sizes are tracked in bundles. A bundle is a single `.js` file for one or more +components that includes all of the JavaScript and CSS needed, minus external +dependencies. We track three metrics: + - **gzip** - minified and compressed. This impacts download size, which can - take longer on slow networks. + take longer over slow networks. - **minified** - minified and unpacked. This impacts the time it takes a page to be interactive, which can take longer on some devices. +- **% CSS** - the amount of CSS compared to JavaScript. The bundle includes + both JS and CSS, so this helps track changes to JS logic and CSS styles + separately. + + -Last updated 2023-12-12. +Last updated 2023-12-13. -Component | gzip | minified | Import ---- | --- | --- | --- -**All** | **70.0kb** | 451.6kb *(66% CSS)* | *@material/web/all.js* -**Common** | **51.4kb** | 281.9kb *(54% CSS)* | *@material/web/common.js* -**Button** | **8.0kb** | 46.3kb *(66% CSS)* | - | 6.7kb | 27.4kb *(49% CSS)* | *@material/web/button/elevated-button.js* - | 6.6kb | 27.3kb *(49% CSS)* | *@material/web/button/filled-button.js* - | 6.7kb | 27.7kb *(49% CSS)* | *@material/web/button/filled-tonal-button.js* - | 6.4kb | 25.7kb *(48% CSS)* | *@material/web/button/outlined-button.js* - | 6.2kb | 24.2kb *(45% CSS)* | *@material/web/button/text-button.js* -**Checkbox** | **7.0kb** | 28.5kb *(43% CSS)* | *@material/web/checkbox/checkbox.js* -**Chips** | **9.9kb** | 60.1kb *(64% CSS)* | - | 4.8kb | 16.4kb *(22% CSS)* | *@material/web/chips/chip-set.js* - | 6.3kb | 26.8kb *(51% CSS)* | *@material/web/chips/assist-chip.js* - | 7.8kb | 36.7kb *(56% CSS)* | *@material/web/chips/filter-chip.js* - | 7.3kb | 33.6kb *(54% CSS)* | *@material/web/chips/input-chip.js* - | 6.4kb | 27.2kb *(51% CSS)* | *@material/web/chips/suggestion-chip.js* -**Dialog** | **4.2kb** | 15.2kb *(36% CSS)* | *@material/web/dialog/dialog.js* -**Divider** | **0.7kb** | 1.4kb *(39% CSS)* | *@material/web/divider/divider.js* -**Elevation** | **0.7kb** | 1.7kb *(62% CSS)* | *@material/web/elevation/elevation.js* -**Fab** | **6.9kb** | 37.1kb *(67% CSS)* | - | 6.6kb | 32.8kb *(64% CSS)* | *@material/web/fab/fab.js* - | 5.8kb | 24.6kb *(51% CSS)* | *@material/web/fab/branded-fab.js* -**Field** | **6.0kb** | 40.5kb *(83% CSS)* | - | 4.6kb | 24.8kb *(75% CSS)* | *@material/web/field/filled-field.js* - | 5.0kb | 27.1kb *(76% CSS)* | *@material/web/field/outlined-field.js* -**Focus** | **1.6kb** | 5.2kb *(46% CSS)* | *@material/web/focus/md-focus-ring.js* -**Icon** | **0.7kb** | 1.3kb *(46% CSS)* | *@material/web/icon/icon.js* -**Icon button** | **7.3kb** | 42.0kb *(65% CSS)* | - | 5.8kb | 23.1kb *(42% CSS)* | *@material/web/iconbutton/icon-button.js* - | 6.0kb | 25.0kb *(45% CSS)* | *@material/web/iconbutton/filled-icon-button.js* - | 6.0kb | 25.5kb *(46% CSS)* | *@material/web/iconbutton/filled-tonal-icon-button.js* - | 6.0kb | 24.6kb *(45% CSS)* | *@material/web/iconbutton/outlined-icon-button.js* -**List** | **6.9kb** | 27.2kb *(35% CSS)* | - | 1.6kb | 4.5kb *(5% CSS)* | *@material/web/list/list.js* - | 5.8kb | 23.0kb *(40% CSS)* | *@material/web/list/list-item.js* -**Menu** | **13.5kb** | 53.9kb *(23% CSS)* | - | 7.9kb | 28.8kb *(17% CSS)* | *@material/web/menu/menu.js* - | 6.5kb | 25.6kb *(37% CSS)* | *@material/web/menu/menu-item.js* - | 8.4kb | 31.9kb *(11% CSS)* | *@material/web/menu/sub-menu.js* -**Progress** | **3.5kb** | 13.9kb *(70% CSS)* | - | 2.6kb | 8.6kb *(64% CSS)* | *@material/web/progress/linear-progress.js* - | 2.2kb | 7.4kb *(57% CSS)* | *@material/web/progress/circular-progress.js* -**Radio** | **6.9kb** | 26.0kb *(31% CSS)* | *@material/web/radio/radio.js* -**Ripple** | **2.8kb** | 7.9kb *(14% CSS)* | *@material/web/ripple/ripple.js* -**Select** | **25.6kb** | 142.5kb *(57% CSS)* | - | 17.8kb | 89.3kb *(48% CSS)* | *@material/web/select/filled-select.js* - | 18.1kb | 89.9kb *(48% CSS)* | *@material/web/select/outlined-select.js* - | 6.6kb | 26.6kb *(36% CSS)* | *@material/web/select/select-option.js* -**Slider** | **9.7kb** | 45.0kb *(49% CSS)* | *@material/web/slider/slider.js* -**Switch** | **7.8kb** | 34.8kb *(53% CSS)* | *@material/web/switch/switch.js* -**Tabs** | **7.9kb** | 35.1kb *(50% CSS)* | - | 6.2kb | 21.9kb *(25% CSS)* | *@material/web/tabs/tabs.js* - | 6.3kb | 25.6kb *(48% CSS)* | *@material/web/tabs/primary-tab.js* - | 6.2kb | 25.2kb *(48% CSS)* | *@material/web/tabs/secondary-tab.js* -**Text field** | **13.7kb** | 93.0kb *(74% CSS)* | - | 10.7kb | 60.8kb *(62% CSS)* | *@material/web/textfield/filled-text-field.js* - | 10.9kb | 61.3kb *(62% CSS)* | *@material/web/textfield/outlined-text-field.js* +Component | gzip | minified | *% CSS* | Import +--- | --- | --- | --- | --- +**All** | **70.0kb** | 451.6kb | *66% CSS* | `@material/web/all.js` +**Common** | **51.4kb** | 281.9kb | *54% CSS* | `@material/web/common.js` +**Button** | **8.0kb** | 46.3kb | *66% CSS* | + | 6.7kb | 27.4kb | *49% CSS* | `@material/web/button/elevated-button.js` + | 6.6kb | 27.3kb | *49% CSS* | `@material/web/button/filled-button.js` + | 6.7kb | 27.7kb | *49% CSS* | `@material/web/button/filled-tonal-button.js` + | 6.4kb | 25.7kb | *48% CSS* | `@material/web/button/outlined-button.js` + | 6.2kb | 24.2kb | *45% CSS* | `@material/web/button/text-button.js` +**Checkbox** | **7.0kb** | 28.5kb | *43% CSS* | `@material/web/checkbox/checkbox.js` +**Chips** | **9.9kb** | 60.1kb | *64% CSS* | + | 4.8kb | 16.4kb | *22% CSS* | `@material/web/chips/chip-set.js` + | 6.3kb | 26.8kb | *51% CSS* | `@material/web/chips/assist-chip.js` + | 7.8kb | 36.7kb | *56% CSS* | `@material/web/chips/filter-chip.js` + | 7.3kb | 33.6kb | *54% CSS* | `@material/web/chips/input-chip.js` + | 6.4kb | 27.2kb | *51% CSS* | `@material/web/chips/suggestion-chip.js` +**Dialog** | **4.2kb** | 15.2kb | *36% CSS* | `@material/web/dialog/dialog.js` +**Divider** | **0.7kb** | 1.4kb | *39% CSS* | `@material/web/divider/divider.js` +**Elevation** | **0.7kb** | 1.7kb | *62% CSS* | `@material/web/elevation/elevation.js` +**Fab** | **6.9kb** | 37.1kb | *67% CSS* | + | 6.6kb | 32.8kb | *64% CSS* | `@material/web/fab/fab.js` + | 5.8kb | 24.6kb | *51% CSS* | `@material/web/fab/branded-fab.js` +**Field** | **6.0kb** | 40.5kb | *83% CSS* | + | 4.6kb | 24.8kb | *75% CSS* | `@material/web/field/filled-field.js` + | 5.0kb | 27.1kb | *76% CSS* | `@material/web/field/outlined-field.js` +**Focus** | **1.6kb** | 5.2kb | *46% CSS* | `@material/web/focus/md-focus-ring.js` +**Icon** | **0.7kb** | 1.3kb | *46% CSS* | `@material/web/icon/icon.js` +**Icon button** | **7.3kb** | 42.0kb | *65% CSS* | + | 5.8kb | 23.1kb | *42% CSS* | `@material/web/iconbutton/icon-button.js` + | 6.0kb | 25.0kb | *45% CSS* | `@material/web/iconbutton/filled-icon-button.js` + | 6.0kb | 25.5kb | *46% CSS* | `@material/web/iconbutton/filled-tonal-icon-button.js` + | 6.0kb | 24.6kb | *45% CSS* | `@material/web/iconbutton/outlined-icon-button.js` +**List** | **6.9kb** | 27.2kb | *35% CSS* | + | 1.6kb | 4.5kb | *5% CSS* | `@material/web/list/list.js` + | 5.8kb | 23.0kb | *40% CSS* | `@material/web/list/list-item.js` +**Menu** | **13.5kb** | 53.9kb | *23% CSS* | + | 7.9kb | 28.8kb | *17% CSS* | `@material/web/menu/menu.js` + | 6.5kb | 25.6kb | *37% CSS* | `@material/web/menu/menu-item.js` + | 8.4kb | 31.9kb | *11% CSS* | `@material/web/menu/sub-menu.js` +**Progress** | **3.5kb** | 13.9kb | *70% CSS* | + | 2.6kb | 8.6kb | *64% CSS* | `@material/web/progress/linear-progress.js` + | 2.2kb | 7.4kb | *57% CSS* | `@material/web/progress/circular-progress.js` +**Radio** | **6.9kb** | 26.0kb | *31% CSS* | `@material/web/radio/radio.js` +**Ripple** | **2.8kb** | 7.9kb | *14% CSS* | `@material/web/ripple/ripple.js` +**Select** | **25.6kb** | 142.5kb | *57% CSS* | + | 17.8kb | 89.3kb | *48% CSS* | `@material/web/select/filled-select.js` + | 18.1kb | 89.9kb | *48% CSS* | `@material/web/select/outlined-select.js` + | 6.6kb | 26.6kb | *36% CSS* | `@material/web/select/select-option.js` +**Slider** | **9.7kb** | 45.0kb | *49% CSS* | `@material/web/slider/slider.js` +**Switch** | **7.8kb** | 34.8kb | *53% CSS* | `@material/web/switch/switch.js` +**Tabs** | **7.9kb** | 35.1kb | *50% CSS* | + | 6.2kb | 21.9kb | *25% CSS* | `@material/web/tabs/tabs.js` + | 6.3kb | 25.6kb | *48% CSS* | `@material/web/tabs/primary-tab.js` + | 6.2kb | 25.2kb | *48% CSS* | `@material/web/tabs/secondary-tab.js` +**Text field** | **13.7kb** | 93.0kb | *74% CSS* | + | 10.7kb | 60.8kb | *62% CSS* | `@material/web/textfield/filled-text-field.js` + | 10.9kb | 61.3kb | *62% CSS* | `@material/web/textfield/outlined-text-field.js` diff --git a/scripts/size/update-size.ts b/scripts/size/update-size.ts index c3e069abd..07f4b4742 100644 --- a/scripts/size/update-size.ts +++ b/scripts/size/update-size.ts @@ -6,9 +6,9 @@ import * as fs from 'fs/promises'; -import {MarkdownTable} from '../analyzer/markdown-tree-builder.js'; -import {COMPONENT_CUSTOM_ELEMENTS} from '../component-custom-elements.js'; -import {Bundle, Size, getBundleSize} from './bundle-size.js'; +import { MarkdownTable } from '../analyzer/markdown-tree-builder.js'; +import { COMPONENT_CUSTOM_ELEMENTS } from '../component-custom-elements.js'; +import { Bundle, Size, getBundleSize } from './bundle-size.js'; // The bundles to track sizes for. @@ -28,7 +28,7 @@ const bundles: Bundle[] = [ ).map((component) => { const tsCustomElementPaths = COMPONENT_CUSTOM_ELEMENTS[component]; const jsCustomElementPaths = tsCustomElementPaths.map((tsPath) => - tsPath.replace(/\.ts$/, '.js'), + tsPath.replace(/\.ts$/, '.js') ); return { @@ -41,19 +41,20 @@ const bundles: Bundle[] = [ // Compute bundle sizes. const bundleSizes = await Promise.all( - bundles.map((bundle) => getBundleSize(bundle)), + bundles.map((bundle) => getBundleSize(bundle)) ); // Create a markdown table with size data. -const columns = ['Component', 'gzip', 'minified', 'Import']; +const columns = ['Component', 'gzip', 'minified', '*% CSS*', 'Import']; const rows: string[][] = []; -for (const {name, size, inputs} of bundleSizes) { +for (const { name, size, inputs } of bundleSizes) { rows.push([ `**${camelToSentenceCase(name)}**`, `**${bytesToString(size.gzip)}**`, - getJsAndCss(size), - inputs.length === 1 ? `*${getImport(inputs[0].input)}*` : '', + bytesToString(size.raw), + getCssPercent(size), + inputs.length === 1 ? getImport(inputs[0].input) : '', ]); if (inputs.length > 1) { @@ -61,11 +62,12 @@ for (const {name, size, inputs} of bundleSizes) { ...inputs.map((input) => { return [ '', - `${bytesToString(input.size.gzip)}`, - `${getJsAndCss(input.size)}`, - `*${getImport(input.input)}*`, + bytesToString(input.size.gzip), + bytesToString(input.size.raw), + getCssPercent(input.size), + getImport(input.input), ]; - }), + }) ); } } @@ -77,7 +79,7 @@ for (const row of rows) { // Update markdown file. -const markdownContent = await fs.readFile('docs/size.md', {encoding: 'utf8'}); +const markdownContent = await fs.readFile('docs/size.md', { encoding: 'utf8' }); const updateTrackingStart = ''; const updateTrackingEnd = ''; @@ -88,7 +90,7 @@ const newMarkdownContent = [ markdownContent.substring(0, markdownContent.indexOf(updateTrackingStart)), updateTrackingStart, '\n\n', - `Last updated ${nowString}.\n\n`, + `Last updated ${nowString}.\n\n`, markdownTable.toString(), '\n\n', markdownContent.substring(markdownContent.indexOf(updateTrackingEnd)), @@ -99,23 +101,17 @@ await fs.writeFile('docs/size.md', newMarkdownContent); // Text formatting functions for markdown table. function getImport(input: string) { - return `@material/web/${input}`; + return `\`@material/web/${input}\``; } function getCssPercent(size: Size) { - return `${Math.round((size.css / size.raw) * 100)}%`; + return `*${Math.round((size.css / size.raw) * 100)}% CSS*`; } function bytesToString(bytes: number) { return `${(Math.round(bytes / 100) / 10).toFixed(1)}kb`; } -function getJsAndCss(size: Size) { - return `${bytesToString(size.raw)} *(${getCssPercent( - size, - )} CSS)*`; -} - function camelToSentenceCase(value: string) { const withSpaces = value.replaceAll(/([a-z])([A-Z])/g, '$1 $2'); return withSpaces[0].toUpperCase() + withSpaces.slice(1).toLowerCase();