Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace size-limit action with own metrics-report action #433

Merged
merged 42 commits into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b761e5e
add empty metrics-report-action
dcastil Jun 22, 2024
a53f898
fix job name in metrics-report workflow
dcastil Jun 22, 2024
0ff7bf0
add @actions/core to metrics-report-action
dcastil Jun 22, 2024
1cd7650
improve readability of rollup config types
dcastil Jun 22, 2024
ca3c5e0
Update caniuse-lite dependency to version 1.0.30001636
dcastil Jun 22, 2024
b277375
move yarn install to action code
dcastil Jun 22, 2024
90f6a9b
Log local bundle sizes in metrics-report-action
dcastil Jun 22, 2024
7e15bbb
also log PR base bundle sizes
dcastil Jun 22, 2024
fdce053
restore and save cache in metrics-report workflow manually
dcastil Jun 22, 2024
f27d6e1
move checkout of branch to main file
dcastil Jun 22, 2024
da8bff5
use normal cache for metrics-report-action
dcastil Jun 22, 2024
25a34a2
set a comment on PR
dcastil Jun 22, 2024
d629f4d
improve comment detection
dcastil Jun 22, 2024
5ee0b6b
add some logging to comment creation
dcastil Jun 22, 2024
ad0cc56
log pullRequest payload in action
dcastil Jun 22, 2024
3af7a8f
improve comment mentadata
dcastil Jun 22, 2024
71bcd32
stop logging github context playload
dcastil Jun 22, 2024
36fa8fe
add bundle size table to comment
dcastil Jun 22, 2024
1504079
improve table styling
dcastil Jun 22, 2024
cc10e7b
makde heading in comment smaller
dcastil Jun 22, 2024
461ea4c
fix potential problem with comment ID matching
dcastil Jun 22, 2024
92fa175
temporarily modify library size
dcastil Jun 22, 2024
c7964af
fix incorrect branch being checked out as base
dcastil Jun 22, 2024
aa99754
improve change indicators
dcastil Jun 22, 2024
c66168a
remove module from package.json again
dcastil Jun 23, 2024
6b3f5d7
improve commentIdComment in setComment
dcastil Jun 23, 2024
3094bc1
render table in HTML instead of markdown for nicer look
dcastil Jun 23, 2024
ef2bb7a
improve table UI
dcastil Jun 23, 2024
899b706
improve table UI a bit
dcastil Jun 23, 2024
deab3a8
move code into own files
dcastil Jun 23, 2024
ba08da3
switch to px widths in table
dcastil Jun 23, 2024
b0afb74
rename table-html util file
dcastil Jun 23, 2024
c6075be
more util renaming
dcastil Jun 23, 2024
196407b
add getDetails function to html utils
dcastil Jun 23, 2024
bb0f266
rename top-level bundle size object to EntryPointSize
dcastil Jun 23, 2024
5d8b334
only report changes and put rest into collapsible element
dcastil Jun 23, 2024
8781595
forgot to return mainBundleRow in getTableHtmlForSizeMetrics if no si…
dcastil Jun 23, 2024
6271e6b
remove size-limit code from repo
dcastil Jun 23, 2024
a5b49a9
create entry point config dynamically and report single export sizes …
dcastil Jun 23, 2024
a496d56
weird bug with content being available under "default" key in dynamic…
dcastil Jun 23, 2024
a25b6a1
fix incorrect entrypoint labels
dcastil Jun 23, 2024
2b01a89
remove test in default config
dcastil Jun 23, 2024
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
1 change: 1 addition & 0 deletions .github/actions/metrics-report/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
temp
11 changes: 11 additions & 0 deletions .github/actions/metrics-report/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: 'metrics-report-action'
author: 'Dany Castillo'
description: 'Posts a comment with a report about changes in important metrics related to tailwind-merge'
inputs:
github_token:
description: 'Github token of the repository (automatically created by Github)'
default: '${{ github.token }}'
required: false
runs:
using: 'node20'
main: 'src/main.mjs'
17 changes: 17 additions & 0 deletions .github/actions/metrics-report/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "metrics-report-action",
"version": "0.1.0",
"private": true,
"author": "Dany Castillo",
"scripts": {
"start": "node src/main.mjs"
},
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0",
"@octokit/types": "^13.5.0",
"esbuild": "^0.21.5",
"rollup": "^4.18.0"
}
}
235 changes: 235 additions & 0 deletions .github/actions/metrics-report/src/get-package-size.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
// @ts-check

import fs from 'fs/promises'
import path from 'path'
import { promisify } from 'util'
import zlib from 'zlib'

import core from '@actions/core'
import { exec } from '@actions/exec'
import { transform } from 'esbuild'
import { rollup } from 'rollup'

import { actionRootPath, repoRootPath } from './utils/path.mjs'

/**
* @typedef {object} GetPackageSizeOptions
* @property {boolean=} shouldOmitFailures
*/

/**
* @param {GetPackageSizeOptions=} options
*/
export async function getPackageSize(options = {}) {
await buildPackage()

return getEntryPointSizes(options)
}

async function buildPackage() {
core.info('Installing dependencies')
await exec('yarn install --frozen-lockfile', [], { cwd: repoRootPath })

core.info('Building package')
await exec('yarn build', [], { cwd: repoRootPath })
}

/**
* @typedef {object} EntryPointSize
* @property {BundleSize} bundleSize
* @property {BundleSize[]=} singleExportSizes
*/

/**
* @param {GetPackageSizeOptions} param0
* @returns {Promise<EntryPointSize[]>}
*/
async function getEntryPointSizes({ shouldOmitFailures }) {
core.info('Getting entry point configs')
const entryPointConfigs = await getEntryPointConfigs()

core.info('Getting bundle sizes')

const maybeEntryPointSizes = await Promise.all(
entryPointConfigs.map(async (entryPointConfig, entryPointIndex) => {
const entryPointBundlePath = path.resolve(repoRootPath, entryPointConfig.bundlePath)

const bundle = await getBundle(entryPointConfig, entryPointBundlePath).catch(
(error) => {
if (shouldOmitFailures) {
core.info(
`Failed to get bundle for ${entryPointConfig.entryPoint}: ${error.message}`,
)
return
}

throw error
},
)

if (!bundle) {
return
}

const [bundleSize, singleExportSizes] = await Promise.all([
getBundleSize(entryPointConfig.entryPoint, bundle),
getSingleExportBundleSizes(
entryPointConfig,
entryPointIndex,
entryPointBundlePath,
bundle,
),
])

return {
bundleSize,
singleExportSizes,
}
}),
)

/** @type {any} */
const entryPointSizes = maybeEntryPointSizes.filter((bundleSize) => bundleSize !== undefined)

return entryPointSizes
}

/**
* @typedef {object} EntryPointConfiguration
* @property {string} entryPoint
* @property {string} bundlePath
* @property {'esm' | 'cjs'} format
*/

/**
* @returns {Promise<EntryPointConfiguration[]>}
*/
async function getEntryPointConfigs() {
const pkg = (await import('../../../../package.json', { assert: { type: 'json' } })).default

return Object.entries(pkg.exports).flatMap(([relativeEntryPointPath, bundleObject]) => {
const entryPointPath = path.join('tailwind-merge', relativeEntryPointPath)

/** @type {EntryPointConfiguration[]} */
const entryPointConfigs = []

if (bundleObject.import) {
entryPointConfigs.push({
entryPoint: entryPointPath + ' esm',
bundlePath: bundleObject.import,
format: 'esm',
})
}

if (bundleObject.require) {
entryPointConfigs.push({
entryPoint: entryPointPath + ' cjs',
bundlePath: bundleObject.require,
format: 'cjs',
})
}

return entryPointConfigs
})
}

/**
* @param {EntryPointConfiguration} entryPointConfig
* @param {string} entryPoint
*/
async function getBundle(entryPointConfig, entryPoint) {
const rollupBuild = await rollup({ input: entryPoint })
let rollupOutput

try {
rollupOutput = await rollupBuild.generate({
format: entryPointConfig.format,
})
} catch (error) {
await rollupBuild.close()
throw error
}

await rollupBuild.close()

if (rollupOutput.output.length !== 1) {
throw Error(`Expected a single output chunk for bundle ${entryPoint}`)
}

const outputChunk = rollupOutput.output[0]

return outputChunk
}

/**
* @param {EntryPointConfiguration} entryPointConfig
* @param {number} entryPointIndex
* @param {string} bundlePath
* @param {import('rollup').OutputChunk} bundle
*/
async function getSingleExportBundleSizes(entryPointConfig, entryPointIndex, bundlePath, bundle) {
if (entryPointConfig.format === 'esm' && bundle.exports.length !== 0) {
const singleExportBundlesDirPath = path.resolve(
actionRootPath,
`temp/bundle-${entryPointIndex}`,
)

await fs.mkdir(singleExportBundlesDirPath, { recursive: true })

return Promise.all(
bundle.exports.map(async (exportName) => {
const entryPoint = await createEntryPoint(
singleExportBundlesDirPath,
bundlePath,
exportName,
)

const singleExportBundle = await getBundle(entryPointConfig, entryPoint)

return getBundleSize(exportName, singleExportBundle)
}),
)
}
}

/**
* @param {string} singleExportBundlesDirPath
* @param {string} bundlePath
* @param {string} exportName
*/
async function createEntryPoint(singleExportBundlesDirPath, bundlePath, exportName) {
const filePath = path.resolve(singleExportBundlesDirPath, `${exportName}.mjs`)
const fileContent = `export { ${exportName} } from '${bundlePath}'`

await fs.writeFile(filePath, fileContent)

return filePath
}

/**
* @typedef {object} BundleSize
* @property {string} label
* @property {number} size
* @property {number} sizeMinified
* @property {number} sizeBrotliCompressed
*/

/**
* @param {string} label
* @param {import('rollup').OutputChunk} bundle
* @returns {Promise<BundleSize>}
*/
async function getBundleSize(label, bundle) {
const esBuildTransformResult = await transform(bundle.code, { minify: true })
const minifiedCode = esBuildTransformResult.code
const brotliCompressedCode = (await brotliCompress(minifiedCode)).toString()

return {
label,
size: bundle.code.length,
sizeMinified: minifiedCode.length,
sizeBrotliCompressed: brotliCompressedCode.length,
}
}

const brotliCompress = promisify(zlib.brotliCompress)
Loading
Loading