mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-11-04 04:44:08 +01:00 
			
		
		
		
	Compare commits
	
		
			25 Commits
		
	
	
		
			mermaid@11
			...
			sidv/runTi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b4be53ff04 | ||
| 
						 | 
					93dee55ade | ||
| 
						 | 
					781945325d | ||
| 
						 | 
					dc476233ba | ||
| 
						 | 
					eb6c92b0d9 | ||
| 
						 | 
					c6cf5953a1 | ||
| 
						 | 
					32db724752 | ||
| 
						 | 
					6702d41840 | ||
| 
						 | 
					441deaffc9 | ||
| 
						 | 
					af53a968f6 | ||
| 
						 | 
					3ecb841c1a | ||
| 
						 | 
					1d2450245e | ||
| 
						 | 
					f6c4c9260f | ||
| 
						 | 
					f354d68350 | ||
| 
						 | 
					bea76aa682 | ||
| 
						 | 
					6d4b27aacb | ||
| 
						 | 
					e008b7dae7 | ||
| 
						 | 
					96a3991c56 | ||
| 
						 | 
					8d1d691bc3 | ||
| 
						 | 
					e07608209b | ||
| 
						 | 
					50cdb74d54 | ||
| 
						 | 
					edc091f4d4 | ||
| 
						 | 
					e0448a7b7b | ||
| 
						 | 
					0f02f5ff34 | ||
| 
						 | 
					b2111adef5 | 
@@ -88,6 +88,7 @@ rels
 | 
			
		||||
reqs
 | 
			
		||||
rewritelinks
 | 
			
		||||
rgba
 | 
			
		||||
runtimes
 | 
			
		||||
RIGHTOF
 | 
			
		||||
sankey
 | 
			
		||||
sequencenumber
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										83
									
								
								.github/workflows/e2e.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										83
									
								
								.github/workflows/e2e.yml
									
									
									
									
										vendored
									
									
								
							@@ -15,6 +15,7 @@ on:
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
  pull-requests: write
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  # For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
 | 
			
		||||
@@ -48,15 +49,35 @@ jobs:
 | 
			
		||||
        with:
 | 
			
		||||
          ref: ${{ env.targetHash }}
 | 
			
		||||
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
 | 
			
		||||
        uses: cypress-io/github-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          # just perform install
 | 
			
		||||
          runTests: false
 | 
			
		||||
 | 
			
		||||
      - name: Build
 | 
			
		||||
        if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' && github.event_name == 'pull_request' }}
 | 
			
		||||
        run: |
 | 
			
		||||
          pnpm run build:viz
 | 
			
		||||
          mkdir -p cypress/snapshots/stats/base
 | 
			
		||||
          mv stats cypress/snapshots/stats/base
 | 
			
		||||
 | 
			
		||||
      - name: Cypress run
 | 
			
		||||
        uses: cypress-io/github-action@v4
 | 
			
		||||
        uses: cypress-io/github-action@v6
 | 
			
		||||
        id: cypress-snapshot-gen
 | 
			
		||||
        if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
 | 
			
		||||
        with:
 | 
			
		||||
          install: false
 | 
			
		||||
          start: pnpm run dev
 | 
			
		||||
          wait-on: 'http://localhost:9000'
 | 
			
		||||
          browser: chrome
 | 
			
		||||
 | 
			
		||||
      - name: Move runtime data
 | 
			
		||||
        if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
 | 
			
		||||
        run: |
 | 
			
		||||
          mv cypress/snapshots/runtimes/current cypress/snapshots/runtimes/base
 | 
			
		||||
 | 
			
		||||
  e2e:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    container:
 | 
			
		||||
@@ -86,15 +107,42 @@ jobs:
 | 
			
		||||
          path: ./cypress/snapshots
 | 
			
		||||
          key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
 | 
			
		||||
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        uses: cypress-io/github-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          runTests: false
 | 
			
		||||
 | 
			
		||||
      - name: Build
 | 
			
		||||
        id: size
 | 
			
		||||
        if: ${{ github.event_name == 'pull_request' && matrix.containers == 1 }}
 | 
			
		||||
        run: |
 | 
			
		||||
          pnpm run build:viz
 | 
			
		||||
          mv stats cypress/snapshots/stats/head
 | 
			
		||||
          {
 | 
			
		||||
            echo 'size_diff<<EOF'
 | 
			
		||||
            npx tsx scripts/size.ts
 | 
			
		||||
            echo EOF
 | 
			
		||||
          } >> "$GITHUB_OUTPUT"
 | 
			
		||||
 | 
			
		||||
      # Size diff only needs to be posted from one job, on PRs.
 | 
			
		||||
      - name: Comment PR size difference
 | 
			
		||||
        if: ${{ github.event_name == 'pull_request' && matrix.containers == 1 }}
 | 
			
		||||
        uses: thollander/actions-comment-pull-request@v2
 | 
			
		||||
        with:
 | 
			
		||||
          message: |
 | 
			
		||||
            ${{ steps.size.outputs.size_diff }}
 | 
			
		||||
          comment_tag: size-diff
 | 
			
		||||
 | 
			
		||||
      # Install NPM dependencies, cache them correctly
 | 
			
		||||
      # and run all Cypress tests
 | 
			
		||||
      - name: Cypress run
 | 
			
		||||
        uses: cypress-io/github-action@v4
 | 
			
		||||
        uses: cypress-io/github-action@v6
 | 
			
		||||
        id: cypress
 | 
			
		||||
        # If CYPRESS_RECORD_KEY is set, run in parallel on all containers
 | 
			
		||||
        # Otherwise (e.g. if running from fork), we run on a single container only
 | 
			
		||||
        if: ${{ ( env.CYPRESS_RECORD_KEY != '' ) || ( matrix.containers == 1 ) }}
 | 
			
		||||
        with:
 | 
			
		||||
          install: false
 | 
			
		||||
          start: pnpm run dev:coverage
 | 
			
		||||
          wait-on: 'http://localhost:9000'
 | 
			
		||||
          browser: chrome
 | 
			
		||||
@@ -133,6 +181,16 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    if: ${{ always() }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - uses: pnpm/action-setup@v2
 | 
			
		||||
        # uses version from "packageManager" field in package.json
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node.js 18.x
 | 
			
		||||
        uses: actions/setup-node@v4
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 18.x
 | 
			
		||||
 | 
			
		||||
      # Download all snapshot artifacts and merge them into a single folder
 | 
			
		||||
      - name: Download All Artifacts
 | 
			
		||||
        uses: actions/download-artifact@v4
 | 
			
		||||
@@ -141,6 +199,27 @@ jobs:
 | 
			
		||||
          pattern: snapshots-*
 | 
			
		||||
          merge-multiple: true
 | 
			
		||||
 | 
			
		||||
      - name: Build
 | 
			
		||||
        id: runtime
 | 
			
		||||
        if: ${{ needs.e2e.result != 'failure' && github.event_name == 'pull_request' }}
 | 
			
		||||
        run: |
 | 
			
		||||
          mv ./snapshots/runtimes/current ./snapshots/runtimes/head
 | 
			
		||||
          npm config set ignore-scripts true
 | 
			
		||||
          pnpm install --frozen-lockfile
 | 
			
		||||
          {
 | 
			
		||||
            echo 'runtime_diff<<EOF'
 | 
			
		||||
            npx tsx scripts/runTime.ts ./snapshots
 | 
			
		||||
            echo EOF
 | 
			
		||||
          } >> "$GITHUB_OUTPUT"
 | 
			
		||||
 | 
			
		||||
      - name: Comment PR runtime difference
 | 
			
		||||
        if: ${{ github.event_name == 'pull_request' }}
 | 
			
		||||
        uses: thollander/actions-comment-pull-request@v2
 | 
			
		||||
        with:
 | 
			
		||||
          message: |
 | 
			
		||||
            ${{ steps.runtime.outputs.runtime_diff }}
 | 
			
		||||
          comment_tag: size-diff
 | 
			
		||||
 | 
			
		||||
      # For successful push events, we save the snapshots cache
 | 
			
		||||
      - name: Save snapshots cache
 | 
			
		||||
        id: cache-upload
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -29,6 +29,7 @@ Gemfile.lock
 | 
			
		||||
 | 
			
		||||
cypress/screenshots/
 | 
			
		||||
cypress/snapshots/
 | 
			
		||||
cypress/runtimes/
 | 
			
		||||
 | 
			
		||||
# eslint --cache file
 | 
			
		||||
.eslintcache
 | 
			
		||||
@@ -50,4 +51,4 @@ demos/dev/**
 | 
			
		||||
tsx-0/**
 | 
			
		||||
 | 
			
		||||
# autogenereated by langium-cli
 | 
			
		||||
generated/
 | 
			
		||||
generated/
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
import { defineConfig } from 'cypress';
 | 
			
		||||
import fs from 'fs';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin';
 | 
			
		||||
import coverage from '@cypress/code-coverage/task';
 | 
			
		||||
import eyesPlugin from '@applitools/eyes-cypress';
 | 
			
		||||
@@ -17,6 +19,19 @@ export default eyesPlugin(
 | 
			
		||||
          }
 | 
			
		||||
          return launchOptions;
 | 
			
		||||
        });
 | 
			
		||||
        on('task', {
 | 
			
		||||
          recordRenderTime({ fileName, testName, timeTaken }) {
 | 
			
		||||
            const resultsPath = path.join('cypress', 'snapshots', 'runtimes', 'current');
 | 
			
		||||
            if (!fs.existsSync(resultsPath)) {
 | 
			
		||||
              fs.mkdirSync(resultsPath, { recursive: true });
 | 
			
		||||
            }
 | 
			
		||||
            fs.appendFileSync(
 | 
			
		||||
              path.join(resultsPath, `${fileName}.csv`),
 | 
			
		||||
              `${testName},${timeTaken}\n`
 | 
			
		||||
            );
 | 
			
		||||
            return true;
 | 
			
		||||
          },
 | 
			
		||||
        });
 | 
			
		||||
        addMatchImageSnapshotPlugin(on, config);
 | 
			
		||||
        // copy any needed variables from process.env to config.env
 | 
			
		||||
        config.env.useAppli = process.env.USE_APPLI ? true : false;
 | 
			
		||||
 
 | 
			
		||||
@@ -110,6 +110,14 @@ export const openURLAndVerifyRendering = (
 | 
			
		||||
 | 
			
		||||
  cy.visit(url);
 | 
			
		||||
  cy.window().should('have.property', 'rendered', true);
 | 
			
		||||
  cy.window().then((win) => {
 | 
			
		||||
    cy.task('recordRenderTime', {
 | 
			
		||||
      fileName: Cypress.spec.name,
 | 
			
		||||
      testName: name,
 | 
			
		||||
      // @ts-ignore Dynamically added property.
 | 
			
		||||
      timeTaken: win.renderTime,
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  cy.get('svg').should('be.visible');
 | 
			
		||||
 | 
			
		||||
  if (validation) {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ function b64ToUtf8(str) {
 | 
			
		||||
 | 
			
		||||
// Adds a rendered flag to window when rendering is done, so cypress can wait for it.
 | 
			
		||||
function markRendered() {
 | 
			
		||||
  window.renderTime = Date.now() - window.loadTime;
 | 
			
		||||
  if (window.Cypress) {
 | 
			
		||||
    window.rendered = true;
 | 
			
		||||
  }
 | 
			
		||||
@@ -131,6 +132,7 @@ if (typeof document !== 'undefined') {
 | 
			
		||||
  window.addEventListener(
 | 
			
		||||
    'load',
 | 
			
		||||
    function () {
 | 
			
		||||
      this.window.loadTime = Date.now();
 | 
			
		||||
      if (this.location.href.match('xss.html')) {
 | 
			
		||||
        this.console.log('Using api');
 | 
			
		||||
        void contentLoadedApi().finally(markRendered);
 | 
			
		||||
 
 | 
			
		||||
@@ -111,6 +111,7 @@
 | 
			
		||||
    "jsdom": "^22.0.0",
 | 
			
		||||
    "langium-cli": "3.0.1",
 | 
			
		||||
    "lint-staged": "^13.2.1",
 | 
			
		||||
    "markdown-table": "^3.0.3",
 | 
			
		||||
    "nyc": "^15.1.0",
 | 
			
		||||
    "path-browserify": "^1.0.1",
 | 
			
		||||
    "pnpm": "^8.6.8",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -163,6 +163,9 @@ importers:
 | 
			
		||||
      lint-staged:
 | 
			
		||||
        specifier: ^13.2.1
 | 
			
		||||
        version: 13.3.0
 | 
			
		||||
      markdown-table:
 | 
			
		||||
        specifier: ^3.0.3
 | 
			
		||||
        version: 3.0.3
 | 
			
		||||
      nyc:
 | 
			
		||||
        specifier: ^15.1.0
 | 
			
		||||
        version: 15.1.0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										135
									
								
								scripts/runTime.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								scripts/runTime.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
			
		||||
/* eslint-disable no-console */
 | 
			
		||||
import { readFile } from 'fs/promises';
 | 
			
		||||
import { globby } from 'globby';
 | 
			
		||||
 | 
			
		||||
interface RunTimes {
 | 
			
		||||
  [key: string]: number;
 | 
			
		||||
}
 | 
			
		||||
interface TestResult {
 | 
			
		||||
  [key: string]: RunTimes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const getRuntimes = (csv: string): RunTimes => {
 | 
			
		||||
  const lines = csv.split('\n');
 | 
			
		||||
  const runtimes: RunTimes = {};
 | 
			
		||||
  for (const line of lines) {
 | 
			
		||||
    const [testName, timeTaken] = line.split(',');
 | 
			
		||||
    if (testName && timeTaken) {
 | 
			
		||||
      runtimes[testName] = Number(timeTaken);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return runtimes;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const readStats = async (path: string): Promise<TestResult> => {
 | 
			
		||||
  const files = await globby(path);
 | 
			
		||||
  const contents = await Promise.all(
 | 
			
		||||
    files.map(async (file) => [file, await readFile(file, 'utf-8')])
 | 
			
		||||
  );
 | 
			
		||||
  const sizes = contents.map(([file, content]) => [file.split('/').pop(), getRuntimes(content)]);
 | 
			
		||||
  return Object.fromEntries(sizes);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const percentChangeThreshold = 5;
 | 
			
		||||
const percentageDifference = (
 | 
			
		||||
  oldValue: number,
 | 
			
		||||
  newValue: number
 | 
			
		||||
): { change: string; crossedThreshold: boolean } => {
 | 
			
		||||
  const difference = Math.abs(newValue - oldValue);
 | 
			
		||||
  const avg = (newValue + oldValue) / 2;
 | 
			
		||||
  const percentage = (difference / avg) * 100;
 | 
			
		||||
  const roundedPercentage = percentage.toFixed(2); // Round to two decimal places
 | 
			
		||||
  if (roundedPercentage === '0.00') {
 | 
			
		||||
    return { change: '0.00%', crossedThreshold: false };
 | 
			
		||||
  }
 | 
			
		||||
  const sign = newValue > oldValue ? '+' : '-';
 | 
			
		||||
  return {
 | 
			
		||||
    change: `${sign}${roundedPercentage}%`,
 | 
			
		||||
    crossedThreshold: percentage > percentChangeThreshold,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const main = async () => {
 | 
			
		||||
  const base = process.argv[2] || './cypress/snapshots';
 | 
			
		||||
  const oldStats = await readStats(`${base}/runtimes/base/**/*.csv`);
 | 
			
		||||
  const newStats = await readStats(`${base}/runtimes/head/**/*.csv`);
 | 
			
		||||
  const fullData: string[][] = [];
 | 
			
		||||
  const changed: string[][] = [];
 | 
			
		||||
  let oldRuntimeSum = 0;
 | 
			
		||||
  let newRuntimeSum = 0;
 | 
			
		||||
  let testCount = 0;
 | 
			
		||||
  for (const [fileName, runtimes] of Object.entries(newStats)) {
 | 
			
		||||
    const oldStat = oldStats[fileName];
 | 
			
		||||
    if (!oldStat) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    for (const [testName, timeTaken] of Object.entries(runtimes)) {
 | 
			
		||||
      const oldTimeTaken = oldStat[testName];
 | 
			
		||||
      if (!oldTimeTaken) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      oldRuntimeSum += oldTimeTaken;
 | 
			
		||||
      newRuntimeSum += timeTaken;
 | 
			
		||||
      testCount++;
 | 
			
		||||
      const delta = timeTaken - oldTimeTaken;
 | 
			
		||||
 | 
			
		||||
      const { change, crossedThreshold } = percentageDifference(oldTimeTaken, timeTaken);
 | 
			
		||||
      const out = [
 | 
			
		||||
        fileName,
 | 
			
		||||
        testName.replace('#', ''),
 | 
			
		||||
        `${oldTimeTaken}/${timeTaken}`,
 | 
			
		||||
        `${delta.toString()}ms ${change}`,
 | 
			
		||||
      ];
 | 
			
		||||
      if (crossedThreshold && Math.abs(delta) > 25) {
 | 
			
		||||
        changed.push(out);
 | 
			
		||||
      }
 | 
			
		||||
      fullData.push(out);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  const oldAverage = oldRuntimeSum / testCount;
 | 
			
		||||
  const newAverage = newRuntimeSum / testCount;
 | 
			
		||||
  const { change, crossedThreshold } = percentageDifference(oldAverage, newAverage);
 | 
			
		||||
 | 
			
		||||
  const headers = ['File', 'Test', 'Time Old/New', 'Change (%)'];
 | 
			
		||||
  console.log(`## Runtime Changes
 | 
			
		||||
Old runtime average: ${oldAverage.toFixed(2)}ms
 | 
			
		||||
New runtime average: ${newAverage.toFixed(2)}ms
 | 
			
		||||
Change: ${change} ${crossedThreshold ? '⚠️' : ''}
 | 
			
		||||
  `);
 | 
			
		||||
  console.log(`
 | 
			
		||||
  <details>
 | 
			
		||||
  <summary>Changed tests</summary>
 | 
			
		||||
  ${htmlTable([headers, ...changed])}
 | 
			
		||||
</details>
 | 
			
		||||
`);
 | 
			
		||||
  console.log(`
 | 
			
		||||
  <details>
 | 
			
		||||
  <summary>Full Data</summary>
 | 
			
		||||
  ${htmlTable([headers, ...fullData])}
 | 
			
		||||
</details>
 | 
			
		||||
`);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const htmlTable = (data: string[][]): string => {
 | 
			
		||||
  let table = `<table border='1' style="border-collapse: collapse">`;
 | 
			
		||||
 | 
			
		||||
  // Generate table header
 | 
			
		||||
  table += `<tr>
 | 
			
		||||
    ${data
 | 
			
		||||
      .shift()!
 | 
			
		||||
      .map((header) => `<th>${header}</th>`)
 | 
			
		||||
      .join('')}
 | 
			
		||||
    </tr>`;
 | 
			
		||||
 | 
			
		||||
  // Generate table rows
 | 
			
		||||
  for (const row of data) {
 | 
			
		||||
    table += `<tr>
 | 
			
		||||
    ${row.map((cell) => `<td>${cell}</td>`).join('')}
 | 
			
		||||
    </tr>`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  table += '</table>';
 | 
			
		||||
  return table;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void main().catch((e) => console.error(e));
 | 
			
		||||
							
								
								
									
										82
									
								
								scripts/size.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								scripts/size.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
/* eslint-disable no-console */
 | 
			
		||||
import type { Metafile } from 'esbuild';
 | 
			
		||||
import { readFile } from 'fs/promises';
 | 
			
		||||
import { globby } from 'globby';
 | 
			
		||||
import { markdownTable } from 'markdown-table';
 | 
			
		||||
export const getSizes = (metafile: Metafile) => {
 | 
			
		||||
  const { outputs } = metafile;
 | 
			
		||||
  const sizes = Object.keys(outputs)
 | 
			
		||||
    .filter((key) => key.endsWith('js') && !key.includes('chunk'))
 | 
			
		||||
    .map((key) => {
 | 
			
		||||
      const { bytes } = outputs[key];
 | 
			
		||||
      return [key.replace('dist/', ''), bytes];
 | 
			
		||||
    });
 | 
			
		||||
  return sizes;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const readStats = async (path: string): Promise<Record<string, number>> => {
 | 
			
		||||
  const files = await globby(path);
 | 
			
		||||
  const contents = await Promise.all(files.map((file) => readFile(file, 'utf-8')));
 | 
			
		||||
  const sizes = contents.flatMap((content) => getSizes(JSON.parse(content)));
 | 
			
		||||
  return Object.fromEntries(sizes);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const formatBytes = (bytes: number): string => {
 | 
			
		||||
  if (bytes == 0) {
 | 
			
		||||
    return '0 Bytes';
 | 
			
		||||
  }
 | 
			
		||||
  const base = 1024;
 | 
			
		||||
  const decimals = 2;
 | 
			
		||||
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
 | 
			
		||||
  const i = Math.floor(Math.log(bytes) / Math.log(base));
 | 
			
		||||
  return parseFloat((bytes / Math.pow(base, i)).toFixed(decimals)) + ' ' + sizes[i];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const formatSize = (bytes: number): string => {
 | 
			
		||||
  const formatted = formatBytes(bytes);
 | 
			
		||||
  if (formatted.includes('Bytes')) {
 | 
			
		||||
    return formatted;
 | 
			
		||||
  }
 | 
			
		||||
  return `${formatBytes(bytes)} (${bytes} Bytes)`;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const percentageDifference = (oldValue: number, newValue: number): string => {
 | 
			
		||||
  const difference = Math.abs(newValue - oldValue);
 | 
			
		||||
  const avg = (newValue + oldValue) / 2;
 | 
			
		||||
  const percentage = (difference / avg) * 100;
 | 
			
		||||
  const roundedPercentage = percentage.toFixed(2); // Round to two decimal places
 | 
			
		||||
  if (roundedPercentage === '0.00') {
 | 
			
		||||
    return '0.00%';
 | 
			
		||||
  }
 | 
			
		||||
  const sign = newValue > oldValue ? '+' : '-';
 | 
			
		||||
  return `${sign}${roundedPercentage}%`;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const main = async () => {
 | 
			
		||||
  const oldStats = await readStats('./cypress/snapshots/stats/base/**/*.json');
 | 
			
		||||
  const newStats = await readStats('./cypress/snapshots/stats/head/**/*.json');
 | 
			
		||||
  const diff = Object.entries(newStats)
 | 
			
		||||
    .filter(([, value]) => value > 2048)
 | 
			
		||||
    .map(([key, value]) => {
 | 
			
		||||
      const oldValue = oldStats[key];
 | 
			
		||||
      const delta = value - oldValue;
 | 
			
		||||
      const output = [
 | 
			
		||||
        key,
 | 
			
		||||
        formatSize(oldValue),
 | 
			
		||||
        formatSize(value),
 | 
			
		||||
        formatSize(delta),
 | 
			
		||||
        percentageDifference(oldValue, value),
 | 
			
		||||
      ];
 | 
			
		||||
      return output;
 | 
			
		||||
    })
 | 
			
		||||
    .filter(([, , , delta]) => delta !== '0 Bytes');
 | 
			
		||||
  if (diff.length === 0) {
 | 
			
		||||
    console.log('No changes in bundle sizes');
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  console.log(
 | 
			
		||||
    markdownTable([['File', 'Previous Size', 'New Size', 'Difference', '% Change'], ...diff])
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void main().catch((e) => console.error(e));
 | 
			
		||||
		Reference in New Issue
	
	Block a user