npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@jsenv/file-size-impact

v14.3.9

Published

Add files size impact into pull requests

Downloads

642

Readme

File size impact

npm package

@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, ...)

Pull request comment

Screenshot + explanation on pull request comment.

legend of pull request comment

| Text | How to read it | | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | | "remaining files (+4.71%)" | 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%)" | 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 (4)" | Sum of files in that group that are not impacted by the pull request. | | Total (5) | Sum of files in that group. |

Installation

The first thing you need is a script capable to generate a file size report.

npm install --save-dev @jsenv/file-size-impact

file_size.mjs

import { generateFileSizeReport } from "@jsenv/file-size-impact";

export const fileSizeReport = await generateFileSizeReport({
  log: process.argv.includes("--log"),
  rootDirectoryUrl: 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 ./file_size.mjs --log

Now it's time to configure a workflow to compare file size reports before and after merging a pull request.

GitHub workflow

.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

jobs:
  file_size_impact:
    runs-on: ubuntu-latest
    name: file size impact
    steps:
      - name: Setup git
        uses: actions/checkout@v3
      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: "18.3.0"
      - 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/workflow/tree/main/packages/file-size-impact#how-it-works
 */

import {
  reportFileSizeImpactInGitHubPullRequest,
  readGitHubWorkflowEnv,
} from "@jsenv/file-size-impact";

await reportFileSizeImpactInGitHubPullRequest({
  ...readGitHubWorkflowEnv(),
  buildCommand: "npm run dist",
  fileSizeReportUrl: new URL("./file_size.mjs#fileSizeReport", import.meta.url),
});

Notes:

  • "#fileSizeReport" is the name of the export from file_size.mjs.

Other tools

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:

  1. Replicate file_size_impact.yml
  2. Adjust report_file_size_impact.mjs
  3. Create a GitHub token (required to post comment on GitHub)

1. Replicate file_size_impact.yml

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

2. Adjust report_file_size_impact.mjs

When outside a GitHub workflow, you cannot use readGitHubWorkflowEnv(). It means you must pass several parameters to reportFileSizeImpactInGitHubPullRequest. The example below assume code is executed by Travis.

- import { reportFileSizeImpactInGitHubPullRequest, readGitHubWorkflowEnv } from "@jsenv/file-size-impact"
+ import { reportFileSizeImpactInGitHubPullRequest } from "@jsenv/file-size-impact"

reportFileSizeImpactInGitHubPullRequest({
- ...readGitHubWorkflowEnv(),
+ rootDirectoryUrl: 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",
})

3. Create a GitHub token

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.

How it works

In order to analyse the impact of a pull request on file size the following steps are executed:

  1. Checkout pull request base branch
  2. Execute an install command (npm install by default)
  3. Run a build command (nom build by default)
  4. Get a file size report (dynamic import of a js module exporting generateFileSizeReport)
  5. Merge pull request into its base
  6. Execute command to generate files again
  7. Get a second file size report
  8. Analyse differences between the two file size reports
  9. Post or update comment in the pull request

generateFileSizeReport

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({
  rootDirectoryUrl: new URL("./", import.meta.url),
  trackingConfig: {
    dist: {
      "./dist/**/*.js": true,
    },
  },
  transformations: { raw, gzip },
});

trackingConfig

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,
    },
  },
});

screenshot of pull request comment where groups are highlighted

transformations

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 },
});

screenshot of pull request comment with gzip and 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

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 { reportFileSizeImpactInGitHubPullRequest } from "@jsenv/file-size-impact";

await reportFileSizeImpactInGitHubPullRequest({
  manifestConfig: {
    "./dist/**/manifest.json": true,
  },
});

You can disable manifest files handling by passing null.

import { reportFileSizeImpactInGitHubPullRequest } from "@jsenv/file-size-impact";

await reportFileSizeImpactInGitHubPullRequest({
  manifestConfig: {
    "./dist/**/manifest.json": null,
  },
});

In that case manifest.json will be handled as a regular file.

reportFileSizeImpactInGitHubPullRequest

reportFileSizeImpactInGitHubPullRequest is an async function that will analyse a pull request file size impact and post a comment with the result of this analysis.

import {
  reportFileSizeImpactInGitHubPullRequest,
  raw,
} from "@jsenv/file-size-impact";

await reportFileSizeImpactInGitHubPullRequest({
  logLevel: "info",

  rootDirectoryUrl: "file:///directory",
  githubToken: "xxx",
  repositoryOwner: "jsenv",
  repositoryName: "file-size-impact",
  pullRequestNumber: 10,

  installCommand: "npm install",
  buildCommand: "npm run build",
  fileSizeReportUrl: new URL("./file_size.mjs#fileSizeReport", import.meta.url),
  filesOrdering: "size_impact",
});

logLevel

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.

rootDirectoryUrl

rootDirectoryUrl parameter is a string leading to your project root directory. This parameter is required.

installCommand

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

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.

fileSizeReportUrl

fileSizeReportUrl is a string or an url parameter representing an url leading a module file. This file must export a fileSizeReport produced by generateFileSizeReport.

filesOrdering

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

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.

screenshot of pull request comment where runlink is highlighted

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 { reportFileSizeImpactInGitHubPullRequest } from "@jsenv/file-size-impact";

await reportFileSizeImpactInGitHubPullRequest({
  runLink: {
    url: process.env.BUILD_URL,
    text: `${process.env.JOB_NAME}#${process.env.BUILD_ID}`,
  },
});

commitInGeneratedByInfo

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.

screenshot of pull request comment where link to commit is highlighted

readGitHubWorkflowEnv

readGitHubWorkflowEnv is a function meant to be runned inside a GitHub workflow. It returns an object meant to be forwarded to reportFileSizeImpactInGitHubPullRequest.

import {
  reportFileSizeImpactInGitHubPullRequest,
  readGitHubWorkflowEnv,
} from "@jsenv/file-size-impact";

const gitHubWorkflowEnv = readGitHubWorkflowEnv();

await reportFileSizeImpactInGitHubPullRequest({
  ...gitHubWorkflowEnv,
});

gitHubWorkflowEnv object looks like this:

const gitHubWorkflowEnv = {
  rootDirectoryUrl: "/home/runner/work/repository-name",
  githubToken: "xxx",
  repositoryOwner: "jsenv",
  repositoryName: "repository-name",
  pullRequestNumber: 10,
  runLink: {
    url: "https://github.com/jsenv/repository-name/actions/runs/34",
    text: "workflow-name#34",
  },
};

File with dynamic names

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

See also

Note about GitHub workflow paths

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.