Moved to https://github.com/jsenv/workflow/tree/main/packages/jsenv-file-size-impact
@jsenv/file-size-impact
analyses a pull request impact on specific files size. This analysis is posted in a comment of the pull request on GitHub.
- Helps you to catch size impacts before merging pull requests
- Can be configured to track compressed file size
- Create group of files to create meaningful reports
- Can be added to any automated process (GitHub workflow, Jenkins, ...)
This section shows pull request comment and how to read it.
"remaining files (+4.71%)" is a group summary, it translates into the following sentence:
"There is a group of files named remaining files and pull request has an overall impact of +4.71% on these files."
"21.6 kB (+4.11 kB / +23.55%)" is a size impact, it translates into the following sentence:
"The size after merge is 21.6 kB. Pull request adds 4.11 kB representing an increase of 23.55% of the size before merge."
Unmodified row is the sum of all files in a group that are not impacted by changes in the pull request.
Total row is the sum of all files in a group.
The first thing you need is a script capable to generate a file size report.
npm install --save-dev @jsenv/file-size-impact
generate_file_size_report.mjs
import { generateFileSizeReport } from "@jsenv/file-size-impact"
export const fileSizeReport = await generateFileSizeReport({
log: process.argv.includes("--log"),
projectDirectoryUrl: new URL("./", import.meta.url),
trackingConfig: {
dist: {
"./dist/**/*": true,
"./dist/**/*.map": false,
},
},
})
At this stage, you could generate a file size report on your machine with the following command.
node ./generate_file_size_report.mjs --log
Now it's time to configure a workflow to compare file size reports before and after merging a pull request.
.github/workflows/file_size_impact.yml
# This is a GitHub workflow YAML file
# see https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
#
# For every push on a pull request, it
# - starts a machine on ubuntu
# - clone the git repository
# - install node, install npm deps
# - Executes report_file_size_impact.mjs
name: file size impact
on: pull_request_target
jobs:
file_size_impact:
runs-on: ubuntu-latest
name: file size impact
steps:
- name: Setup git
uses: actions/checkout@v2
- name: Setup node
uses: actions/setup-node@v1
with:
node-version: "16.6.1"
- name: Setup npm
run: npm install
- name: Report file size impact
run: node ./report_file_size_impact.mjs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
report_file_size_impact.mjs
/*
* This file is executed by file_size_impact.yml GitHub workflow.
* - it generates file size report before and after merging a pull request
* - Then, it creates or updates a comment in the pull request
* See https://github.com/jsenv/file-size-impact#how-it-works
*/
import { reportFileSizeImpact, readGitHubWorkflowEnv } from "@jsenv/file-size-impact"
await reportFileSizeImpact({
...readGitHubWorkflowEnv(),
buildCommand: "npm run dist",
fileSizeReportModulePath: "./generate_file_size_report.mjs#fileSizeReport",
})
where fileSizeReport
is the name of the export from generate_file_size_report.mjs.
If you want to use an other tool than GitHub worflow to run the pull request comparison, like Jenkins, there is a few things to do:
- Replicate file_size_impact.yml
- Adjust report_file_size_impact.mjs
- Create a GitHub token (required to post comment on GitHub)
Your workflow must reproduce the state where your git repository has been cloned and you are currently on the pull request branch. Something like the commands below.
git init
git remote add origin $GITHUB_REPOSITORY_URL
git fetch --no-tags --prune origin $PULL_REQUEST_HEAD_REF
git checkout origin/$PULL_REQUEST_HEAD_REF
npm install
node ./report_file_size_impact.mjs
When outside a GitHub workflow, you cannot use readGitHubWorkflowEnv(). It means you must pass several parameters to reportFileSizeImpact. The example below assume code is executed by Travis.
- import { reportFileSizeImpact, readGitHubWorkflowEnv } from "@jsenv/file-size-impact"
+ import { reportFileSizeImpact } from "@jsenv/file-size-impact"
reportFileSizeImpact({
- ...readGitHubWorkflowEnv(),
+ projectDirectoryUrl: process.env.TRAVIS_BUILD_DIR,
+ repositoryOwner: process.env.TRAVIS_REPO_SLUG.split("/")[0],
+ repositoryName: process.env.TRAVIS_REPO_SLUG.split("/")[1],
+ pullRequestNumber: process.env.TRAVIS_PULL_REQUEST,
+ githubToken: process.env.GITHUB_TOKEN, // see next step
buildCommand: "npm run dist",
})
The GitHub token is required to be able to post a commment in the pull request. You need to create a GitHub token with repo
scope at https://github.com/settings/tokens/new. Finally you need to setup this environment variable. The exact way to do this is specific to the tools your are using.
In order to analyse the impact of a pull request on file size the following steps are executed:
- Checkout pull request base branch
- Execute an install command (npm install by default)
- Get a file size report (dynamic import of a js module exporting
generateFileSizeReport
) - Merge pull request into its base
- Execute command to generate files again
- Get a second file size report
- Analyse differences between the two file size reports
- Post or update comment in the pull request
generateFileSizeReport is an async function scanning filesystem to compute a list of file sizes and return these infos into an object.
import { generateFileSizeReport, raw, gzip } from "@jsenv/file-size-impact"
const fileSizeReport = await generateFileSizeReport({
projectDirectoryUrl: new URL("./", import.meta.url),
trackingConfig: {
dist: {
"./dist/**/*.js": true,
},
},
transformations: { raw, gzip },
})
trackingConfig parameter is an object used to configure group of files you want to track. This parameter is optional with a default value exported in src/jsenvTrackingConfig.js
trackingConfig keys are group names that will appear in the generated comment. trackingConfig values are objects associating a pattern to a value. This object is refered as metaValueMap in @jsenv/url-meta#structuredMetaMap.
For example you can create two groups named "critical files" and "remaining files" like this:
import { generateFileSizeReport } from "@jsenv/file-size-impact"
await generateFileSizeReport({
trackingConfig: {
"critical files": {
"./dist/main.js": true,
"./dist/main.css": true,
},
"remaining files": {
"./dist/**/*.js": true,
"./dist/**/*.css": true,
"./dist/main.js": false,
"./dist/main.css": false,
},
},
})
transformations parameter is an object used to transform files content before computing their size. This parameter is optional with a default tracking file size without transformation called raw.
You can use this parameter to track file size after gzip compression.
import { generateFileSizeReport, raw, gzip, brotli } from "@jsenv/file-size-impact"
await generateFileSizeReport({
transformations: { raw, gzip, brotli },
})
raw, gzip and brotli compression can be enabled this way.
It's also possible to control compression level.
import { generateFileSizeReport, raw, gzip } from "@jsenv/file-size-impact"
await generateFileSizeReport({
transformations: {
raw,
gzip7: (buffer) => gzip(buffer, { level: 7 }),
gzip9: (buffer) => gzip(buffer, { level: 9 }),
},
})
Finally transformations can be used to add custom transformations.
import { generateFileSizeReport, raw, gzip, brotli } from "@jsenv/file-size-impact"
await generateFileSizeReport({
transformations: {
raw,
trim: (buffer) => String(buffer).trim(),
},
})
manifestConfig parameter is an object used to configure the location of an optional manifest file. It is used to compare files with dynamic names. This parameter is optional with a default considering "dist/**/manifest.json"
as manifest files.
This parameter reuses the shape of trackingConfig (associating pattern + value).
import { reportFileSizeImpact } from "@jsenv/file-size-impact"
await reportFileSizeImpact({
manifestConfig: {
"./dist/**/manifest.json": true,
},
})
You can disable manifest files handling by passing null
.
import { reportFileSizeImpact } from "@jsenv/file-size-impact"
await reportFileSizeImpact({
manifestConfig: {
"./dist/**/manifest.json": null,
},
})
In that case manifest.json will be handled as a regular file.
reportFileSizeImpact is an async function that will analyse a pull request file size impact and post a comment with the result of this analysis.
import { reportFileSizeImpact, raw } from "@jsenv/file-size-impact"
await reportFileSizeImpact({
logLevel: "info",
projectDirectoryUrl: "file:///directory",
githubToken: "xxx",
repositoryOwner: "jsenv",
repositoryName: "file-size-impact",
pullRequestNumber: 10,
installCommand: "npm install",
buildCommand: "npm run build",
fileSizeReportModulePath: "./generate_file_size_report.mjs#fileSizeReport",
filesOrdering: "size_impact",
})
logLevel parameter controls verbosity of logs during the function execution. This parameter is optional with a default value of "info"
.
You likely don't need to modify this parameter except to get verbose logs using "debug"
. The list of available values for logLevel can be found on @jsenv/logger documentation.
projectDirectoryUrl parameter is a string leading to your project root directory. This parameter is required.
installCommand parameter is a string representing the command to run in order to install things just after a switching to a git branch. This parameter is optional with a default value of "npm install"
. You can pass null
if you don't need to run an install command to run your project.
buildCommand parameter is a string representing the command to run in order to generate files. This parameter is optional with a default value of "npm run-script build"
. You can pass null
if you don't need to run a build command before computing file sizes.
fileSizeModulePath is a string parameter representing an url relative to projectDirectoryUrl leading a module file. This file must contain a generateFileSizeReport export.
filesOrdering parameter is a string used to decide the order of the files displayed in the comment. This parameter is optional with a default value of "size_impact"
.
filesOrdering | Description |
---|---|
"size_impact" | Files are ordered by size impact |
"filesystem" | Files are ordered as they appear on the filesystem |
runLink parameter allow to put a link to the workflow run in the generated comment body. It is used to indicates where file size impact was runned.
This parameter is returned by readGitHubWorkflowEnv meaning it comes for free inside a GitHub workflow.
Inside an other workflow, you can pass your own runLink. As in the example below where it is assumed that script is runned by jenkins.
import { reportFileSizeImpact } from "@jsenv/file-size-impact"
await reportFileSizeImpact({
runLink: {
url: process.env.BUILD_URL,
text: `${process.env.JOB_NAME}#${process.env.BUILD_ID}`,
},
})
commitInGeneratedByInfo parameter is a boolean controlling if a link to the commit where size impact was performed appears in the comment. This parameter is optional and enabled by default.
readGitHubWorkflowEnv is a function meant to be runned inside a GitHub workflow. It returns an object meant to be forwarded to reportFileSizeImpact.
import { reportFileSizeImpact, readGitHubWorkflowEnv } from "@jsenv/file-size-impact"
const gitHubWorkflowEnv = readGitHubWorkflowEnv()
await reportFileSizeImpact({
...gitHubWorkflowEnv,
})
gitHubWorkflowEnv object looks like this:
const gitHubWorkflowEnv = {
projectDirectoryUrl: "/home/runner/work/repo-name/repo-name",
githubToken: "xxx",
repositoryOwner: "jsenv",
repositoryName: "file-size-impact",
pullRequestNumber: 10,
runLink: {
url: "https://github.com/jsenv/file-size-impact/actions/runs/34",
text: "workflow-name#34",
},
}
Manifest file allows to compare file with dynamic names. The content of a manifest file looks like this:
{
"dist/file.js": "dist/file.4798774987w97er984798.js"
}
These files are generated by build tools. For example by webpack-manifest-plugin or rollup-plugin-output-manifest.
Read more in manifestConfig parameter
- @jsenv/performance-impact: Monitor pull requests impacts but on performance metrics
- @jsenv/lighthouse-impact: Monitor pull requests impacts but on lighthouse score
It would be more efficient to enable size impact workflow only if certain file changes (the one that could impact dist/ files). It could be done with on
condition in a workflow.yml.
on:
pull_request:
paths:
- "index.js"
- "src/**"
But in practice humans will wonder why the workflow did not run and think something is wrong.