Add bundle-size check action (#3454)

* feat: add scripts action, bundle-size checker

* chore: trigger action

* fix: update prefix logic
This commit is contained in:
Nate Moore 2022-05-26 11:49:29 -05:00 committed by GitHub
parent d92d28317f
commit 2f4ee560dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 135 additions and 6 deletions

81
.github/scripts/bundle-size.mjs vendored Normal file
View file

@ -0,0 +1,81 @@
import { build } from 'esbuild';
const CLIENT_RUNTIME_PATH = 'packages/astro/src/runtime/client/';
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 B';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
export default async function checkBundleSize({ github, context }) {
const PR_NUM = context.payload.pull_request.number;
const SHA = context.payload.pull_request.head.sha;
const { data: files } = await github.rest.pulls.listFiles({
...context.repo,
pull_number: PR_NUM,
});
const clientRuntimeFiles = files.filter(({ filename }) => filename.startsWith(CLIENT_RUNTIME_PATH));
if (clientRuntimeFiles.length === 0) return;
const table = [
'| File | Old Size | New Size | Change |',
'| ---- | -------- | -------- | ------ |',
];
const output = await bundle(clientRuntimeFiles);
for (let [filename, { oldSize, newSize, sourceFile }] of Object.entries(output)) {
filename = filename !== 'hmr' ? `client:${filename}` : filename;
const prefix = (newSize - oldSize) === 0 ? '' : (newSize - oldSize) > 0 ? '+ ' : '- ';
const change = `${prefix}${formatBytes(newSize - oldSize)}`;
table.push(`| [\`${filename}\`](https://github.com/${context.repo.owner}/${context.repo.repo}/tree/${context.payload.pull_request.head.ref}/${sourceFile}) | ${formatBytes(oldSize)} | ${formatBytes(newSize)} | ${change} |`);
}
const { data: comments } = await github.rest.issues.listComments({
...context.repo,
issue_number: PR_NUM
})
const comment = comments.find(comment => comment.user.login === 'github-actions[bot]' && comment.body.includes('Bundle Size Check'));
const method = comment ? 'updateComment' : 'createComment';
const payload = comment ? { comment_id: comment.id } : { issue_number: PR_NUM };
await github.rest.issues[method]({
...context.repo,
...payload,
body: `### ⚖️ Bundle Size Check
Latest commit: ${SHA}
${table.join('\n')}`,
});
}
async function bundle(files) {
const { metafile } = await build({
entryPoints: [...files.map(({ filename }) => filename), ...files.map(({ filename }) => `main/${filename}`)],
bundle: true,
minify: true,
sourcemap: false,
target: ['es2018'],
outdir: 'out',
metafile: true,
})
return Object.entries(metafile.outputs).reduce((acc, [filename, info]) => {
filename = filename.slice('out/'.length);
if (filename.startsWith('main/')) {
filename = filename.slice('main/'.length).replace(CLIENT_RUNTIME_PATH, '').replace('.js', '');
const oldSize = info.bytes;
return Object.assign(acc, { [filename]: Object.assign(acc[filename] ?? {}, { oldSize }) });
}
filename = filename.replace(CLIENT_RUNTIME_PATH, '').replace('.js', '');
const newSize = info.bytes;
return Object.assign(acc, { [filename]: Object.assign(acc[filename] ?? {}, { newSize, sourceFile: Object.keys(info.inputs).find(src => src.endsWith('.ts')) }) });
}, {});
}

48
.github/workflows/scripts.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name: Scripts
on:
pull_request:
branches:
- 'main'
# Automatically cancel in-progress actions on the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
bundle:
name: Bundle Size
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Checkout Main into tmp
uses: actions/checkout@v2
with:
ref: main
path: main
- name: Setup PNPM
uses: pnpm/action-setup@v2.2.1
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 16
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Check Bundle Size
uses: actions/github-script@v6
with:
script: |
const { default: script } = await import('${{ github.workspace }}/.github/scripts/bundle-size.mjs')
await script({ github, context })

View file

@ -1,7 +1,7 @@
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
/**
* Hydrate this component as soon as the main thread is free
* Hydrate this component as soon as the main thread is free!
* (or after a short delay, if `requestIdleCallback`) isn't supported
*/
export default async function onIdle(

View file

@ -1,7 +1,7 @@
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
/**
* Hydrate this component immediately
* Hydrate this component immediately!
*/
export default async function onLoad(
astroId: string,

View file

@ -1,7 +1,7 @@
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
/**
* Hydrate this component when a matching media query is found
* Hydrate this component when a matching media query is found!
*/
export default async function onMedia(
astroId: string,

View file

@ -1,9 +1,9 @@
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
/**
* Hydrate this component immediately
* Hydrate this component only on the client
*/
export default async function onLoad(
export default async function onOnly(
astroId: string,
options: HydrateOptions,
getHydrateCallback: GetHydrateCallback

View file

@ -1,7 +1,7 @@
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
/**
* Hydrate this component when one of it's children becomes visible.
* Hydrate this component when one of it's children becomes visible!
* We target the children because `astro-root` is set to `display: contents`
* which doesn't work with IntersectionObserver
*/