├── tmp └── .gitkeep ├── .gitattributes ├── doc └── comment.png ├── biome.json ├── __tests__ ├── __fixtures__ │ ├── issues │ │ ├── issue-87 │ │ │ ├── src │ │ │ │ ├── decreased.ts │ │ │ │ ├── bit-increased.ts │ │ │ │ ├── much-increased.ts │ │ │ │ ├── new-outfile.ts │ │ │ │ ├── file-imported.ts │ │ │ │ └── no-change.ts │ │ │ ├── tsconfig.json │ │ │ ├── package.json │ │ │ ├── esbuild.mjs │ │ │ └── .analyzer │ │ │ │ └── base │ │ │ │ └── bundle │ │ │ │ └── bundle_analysis.json │ │ └── issue-90 │ │ │ ├── src │ │ │ ├── decreased.ts │ │ │ ├── bit-increased.ts │ │ │ ├── much-increased.ts │ │ │ ├── new-outfile.ts │ │ │ ├── file-imported.ts │ │ │ └── no-change.ts │ │ │ ├── tsconfig.json │ │ │ ├── package.json │ │ │ ├── esbuild.mjs │ │ │ └── .analyzer │ │ │ └── base │ │ │ └── bundle │ │ │ └── bundle_analysis.json │ ├── examples-with-base │ │ ├── multi-output │ │ │ ├── src │ │ │ │ ├── decreased.ts │ │ │ │ ├── bit-increased.ts │ │ │ │ ├── much-increased.ts │ │ │ │ ├── new-outfile.ts │ │ │ │ ├── file-imported.ts │ │ │ │ └── no-change.ts │ │ │ ├── tsconfig.json │ │ │ ├── package.json │ │ │ ├── esbuild.mjs │ │ │ └── .analyzer │ │ │ │ └── base │ │ │ │ └── bundle │ │ │ │ └── bundle_analysis.json │ │ └── multi-metafiles │ │ │ ├── src │ │ │ ├── decreased.ts │ │ │ ├── bit-increased.ts │ │ │ ├── much-increased.ts │ │ │ ├── new-outfile.ts │ │ │ └── no-change.ts │ │ │ ├── package.json │ │ │ ├── esbuild1.mjs │ │ │ ├── tsconfig.json │ │ │ ├── esbuild2.mjs │ │ │ └── .analyzer │ │ │ └── base │ │ │ └── bundle │ │ │ └── bundle_analysis.json │ └── examples │ │ ├── multi-outputs │ │ ├── src │ │ │ ├── beta.ts │ │ │ └── alpha.ts │ │ ├── package.json │ │ ├── tsconfig.json │ │ ├── esbuild.mjs │ │ └── package-lock.json │ │ └── basic │ │ ├── package.json │ │ ├── tsconfig.json │ │ ├── esbuild.mjs │ │ ├── src │ │ └── index.ts │ │ └── package-lock.json ├── helper.ts ├── no-base.test.ts ├── issue-87.test.ts ├── issue-90.test.ts ├── with-base.test.ts └── compare.test.ts ├── .gitignore ├── .github ├── dependabot.yml ├── release.yaml └── workflows │ ├── bump.yml │ ├── ci-from-fork.yml │ └── ci.yml ├── vitest.config.ts ├── tsconfig.json ├── src ├── types.ts ├── utils.ts ├── report.ts ├── index.ts └── compare.ts ├── LICENSE ├── esbuild.mjs ├── scripts └── validate-yaml.ts ├── package.json ├── action.yaml └── README.md /tmp/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /dist/ linguist-generated linguist-vendored 2 | -------------------------------------------------------------------------------- /doc/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exoego/esbuild-bundle-analyzer/HEAD/doc/comment.png -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", 3 | "files": { 4 | "includes": ["src", "__tests__", "!__tests__/__fixtures__"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-87/src/decreased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-90/src/decreased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | node_modules 4 | out 5 | .analyzer 6 | tmp/meta.json 7 | 8 | # Allow-list 9 | !__tests__/__fixtures__/**/.analyzer/base/bundle/bundle_analysis.json 10 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-87/src/bit-increased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-87/src/much-increased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-90/src/bit-increased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-90/src/much-increased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-output/src/decreased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/multi-outputs/src/beta.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises"; 2 | 3 | export const handler2 = async (event: any) => { 4 | return fs.readFile("file.txt", "utf-8"); 5 | }; 6 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-87/src/new-outfile.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises"; 2 | 3 | export const handler2 = async (event: any) => { 4 | return fs.readFile("file.txt", "utf-8"); 5 | }; 6 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-90/src/new-outfile.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises"; 2 | 3 | export const handler2 = async (event: any) => { 4 | return fs.readFile("file.txt", "utf-8"); 5 | }; 6 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-metafiles/src/decreased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-output/src/bit-increased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-output/src/much-increased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-metafiles/src/bit-increased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-metafiles/src/much-increased.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | const app = new Hono(); 3 | 4 | app.get("/", (c) => c.text("Hono!")); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-output/src/new-outfile.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises"; 2 | 3 | export const handler2 = async (event: any) => { 4 | return fs.readFile("file.txt", "utf-8"); 5 | }; 6 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-metafiles/src/new-outfile.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises"; 2 | 3 | export const handler2 = async (event: any) => { 4 | return fs.readFile("file.txt", "utf-8"); 5 | }; 6 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-87/src/file-imported.ts: -------------------------------------------------------------------------------- 1 | import { newQuickJSWASMModuleFromVariant } from "quickjs-emscripten-core" 2 | import releaseVariant from "@jitl/quickjs-wasmfile-release-sync" 3 | export const QuickJS = await newQuickJSWASMModuleFromVariant(releaseVariant) 4 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-90/src/file-imported.ts: -------------------------------------------------------------------------------- 1 | import { newQuickJSWASMModuleFromVariant } from "quickjs-emscripten-core" 2 | import releaseVariant from "@jitl/quickjs-wasmfile-release-sync" 3 | export const QuickJS = await newQuickJSWASMModuleFromVariant(releaseVariant) 4 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-output/src/file-imported.ts: -------------------------------------------------------------------------------- 1 | import { newQuickJSWASMModuleFromVariant } from "quickjs-emscripten-core" 2 | import releaseVariant from "@jitl/quickjs-wasmfile-release-sync" 3 | export const QuickJS = await newQuickJSWASMModuleFromVariant(releaseVariant) 4 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-bundle-analysis-test-fixture", 3 | "version": "1.0.0", 4 | "description": "test", 5 | "dependencies": { 6 | "rambda": "^9.2.0" 7 | }, 8 | "devDependencies": { 9 | "esbuild": "^0.20.2", 10 | "typescript": "^5.4.5" 11 | }, 12 | "scripts": { 13 | "build": "node esbuild.mjs" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/multi-outputs/src/alpha.ts: -------------------------------------------------------------------------------- 1 | import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; 2 | 3 | const s3 = new S3Client(); 4 | 5 | export const handler = async (event: any) => { 6 | const result = await s3.send( 7 | new PutObjectCommand({ 8 | Bucket: "my-bucket", 9 | Key: "my-key", 10 | }), 11 | ); 12 | return result; 13 | }; 14 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-87/src/no-change.ts: -------------------------------------------------------------------------------- 1 | import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; 2 | 3 | const s3 = new S3Client(); 4 | 5 | export const handler = async (event: any) => { 6 | const result = await s3.send( 7 | new PutObjectCommand({ 8 | Bucket: "my-bucket", 9 | Key: "my-key", 10 | }), 11 | ); 12 | return result; 13 | }; 14 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-90/src/no-change.ts: -------------------------------------------------------------------------------- 1 | import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; 2 | 3 | const s3 = new S3Client(); 4 | 5 | export const handler = async (event: any) => { 6 | const result = await s3.send( 7 | new PutObjectCommand({ 8 | Bucket: "my-bucket", 9 | Key: "my-key", 10 | }), 11 | ); 12 | return result; 13 | }; 14 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-metafiles/src/no-change.ts: -------------------------------------------------------------------------------- 1 | import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; 2 | 3 | const s3 = new S3Client(); 4 | 5 | export const handler = async (event: any) => { 6 | const result = await s3.send( 7 | new PutObjectCommand({ 8 | Bucket: "my-bucket", 9 | Key: "my-key", 10 | }), 11 | ); 12 | return result; 13 | }; 14 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-output/src/no-change.ts: -------------------------------------------------------------------------------- 1 | import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; 2 | 3 | const s3 = new S3Client(); 4 | 5 | export const handler = async (event: any) => { 6 | const result = await s3.send( 7 | new PutObjectCommand({ 8 | Bucket: "my-bucket", 9 | Key: "my-key", 10 | }), 11 | ); 12 | return result; 13 | }; 14 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/multi-outputs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-bundle-analysis-test-fixture", 3 | "version": "1.0.0", 4 | "description": "test", 5 | "main": "index.js", 6 | "author": "", 7 | "dependencies": { 8 | "@aws-sdk/client-s3": "3.556.0" 9 | }, 10 | "devDependencies": { 11 | "esbuild": "0.20.2", 12 | "typescript": "5.4.5" 13 | }, 14 | "scripts": { 15 | "build": "node esbuild.mjs" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-metafiles/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-bundle-analysis-test-fixture", 3 | "version": "1.0.0", 4 | "description": "test", 5 | "dependencies": { 6 | "hono": "^4.2.7", 7 | "@aws-sdk/client-s3": "^3.556.0" 8 | }, 9 | "devDependencies": { 10 | "esbuild": "^0.20.2", 11 | "typescript": "^5.4.5" 12 | }, 13 | "scripts": { 14 | "build": "node esbuild1.mjs && node esbuild2.mjs" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | day: "monday" 8 | labels: 9 | - "dependencies" 10 | 11 | - package-ecosystem: "npm" 12 | directory: "/" 13 | schedule: 14 | interval: "monthly" 15 | day: "monday" 16 | labels: 17 | - "dependencies" 18 | groups: 19 | vitest: 20 | patterns: 21 | - "*vitest*" 22 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/basic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2022"], 4 | "module": "es2022", 5 | "target": "es2022", 6 | "allowJs": true, 7 | "noImplicitAny": true, 8 | "esModuleInterop": true, 9 | "strict": true, 10 | "moduleResolution": "Bundler", 11 | "sourceMap": true, 12 | "noEmit": true, 13 | "skipLibCheck": true, 14 | "experimentalDecorators": true, 15 | "types": ["node"], 16 | "baseUrl": "./" 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-87/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2022"], 4 | "module": "es2022", 5 | "target": "es2022", 6 | "allowJs": true, 7 | "noImplicitAny": true, 8 | "esModuleInterop": true, 9 | "strict": true, 10 | "moduleResolution": "Bundler", 11 | "sourceMap": true, 12 | "noEmit": true, 13 | "skipLibCheck": true, 14 | "experimentalDecorators": true, 15 | "types": ["node"], 16 | "baseUrl": "./" 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-90/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2022"], 4 | "module": "es2022", 5 | "target": "es2022", 6 | "allowJs": true, 7 | "noImplicitAny": true, 8 | "esModuleInterop": true, 9 | "strict": true, 10 | "moduleResolution": "Bundler", 11 | "sourceMap": true, 12 | "noEmit": true, 13 | "skipLibCheck": true, 14 | "experimentalDecorators": true, 15 | "types": ["node"], 16 | "baseUrl": "./" 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-metafiles/esbuild1.mjs: -------------------------------------------------------------------------------- 1 | import { writeFileSync } from "node:fs"; 2 | import { build } from "esbuild"; 3 | 4 | const result = await build({ 5 | entryPoints: [`./src/no-change.ts`], 6 | outdir: `out`, 7 | format: "esm", 8 | metafile: true, 9 | mainFields: ["module", "main"], 10 | platform: "node", 11 | target: "node20.9", 12 | bundle: true, 13 | plugins: [], 14 | }); 15 | 16 | writeFileSync(`out/meta1.json`, JSON.stringify(result.metafile, null, 2)); 17 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/multi-outputs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2022"], 4 | "module": "es2022", 5 | "target": "es2022", 6 | "allowJs": true, 7 | "noImplicitAny": true, 8 | "esModuleInterop": true, 9 | "strict": true, 10 | "moduleResolution": "Bundler", 11 | "sourceMap": true, 12 | "noEmit": true, 13 | "skipLibCheck": true, 14 | "experimentalDecorators": true, 15 | "types": ["node"], 16 | "baseUrl": "./" 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-output/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2022"], 4 | "module": "es2022", 5 | "target": "es2022", 6 | "allowJs": true, 7 | "noImplicitAny": true, 8 | "esModuleInterop": true, 9 | "strict": true, 10 | "moduleResolution": "Bundler", 11 | "sourceMap": true, 12 | "noEmit": true, 13 | "skipLibCheck": true, 14 | "experimentalDecorators": true, 15 | "types": ["node"], 16 | "baseUrl": "./" 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | // vitest.config.ts 2 | import { defineConfig, configDefaults } from 'vitest/config' 3 | 4 | // https://vitest.dev/config/ 5 | export default defineConfig({ 6 | test: { 7 | pool: "forks", 8 | clearMocks: true, 9 | globals: true, 10 | reporters: ['verbose'], 11 | coverage: { 12 | provider: 'v8', 13 | reporter: ['lcov'], 14 | }, 15 | watch: true, 16 | exclude: [ 17 | ...configDefaults.exclude, 18 | '**/__fixtures__/**' 19 | ] 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-metafiles/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2022"], 4 | "module": "es2022", 5 | "target": "es2022", 6 | "allowJs": true, 7 | "noImplicitAny": true, 8 | "esModuleInterop": true, 9 | "strict": true, 10 | "moduleResolution": "Bundler", 11 | "sourceMap": true, 12 | "noEmit": true, 13 | "skipLibCheck": true, 14 | "experimentalDecorators": true, 15 | "types": ["node"], 16 | "baseUrl": "./" 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2023"], 4 | "module": "es2022", 5 | "target": "es2022", 6 | "allowJs": true, 7 | "noImplicitAny": true, 8 | "esModuleInterop": true, 9 | "strict": true, 10 | "moduleResolution": "Bundler", 11 | "sourceMap": true, 12 | "noEmit": true, 13 | "skipLibCheck": true, 14 | "experimentalDecorators": true, 15 | "jsx": "preserve", 16 | "types": ["vitest/globals", "node"], 17 | "baseUrl": "." 18 | }, 19 | "include": [ 20 | "src", 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-87/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-bundle-analysis-test-fixture", 3 | "version": "1.0.0", 4 | "description": "test", 5 | "dependencies": { 6 | "@jitl/quickjs-wasmfile-release-sync": "^0.29.1", 7 | "quickjs-emscripten-core": "^0.29.1", 8 | "hono": "4.2.7", 9 | "@aws-sdk/client-s3": "3.556.0" 10 | }, 11 | "devDependencies": { 12 | "@chialab/esbuild-plugin-meta-url": "^0.18.2", 13 | "esbuild": "0.20.2", 14 | "typescript": "^5.4.5" 15 | }, 16 | "scripts": { 17 | "build": "node esbuild.mjs" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-90/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-bundle-analysis-test-fixture", 3 | "version": "1.0.0", 4 | "description": "test", 5 | "dependencies": { 6 | "@jitl/quickjs-wasmfile-release-sync": "^0.29.1", 7 | "quickjs-emscripten-core": "^0.29.1", 8 | "hono": "4.2.7", 9 | "@aws-sdk/client-s3": "3.556.0" 10 | }, 11 | "devDependencies": { 12 | "@chialab/esbuild-plugin-meta-url": "^0.18.2", 13 | "esbuild": "0.20.2", 14 | "typescript": "^5.4.5" 15 | }, 16 | "scripts": { 17 | "build": "node esbuild.mjs" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-output/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-bundle-analysis-test-fixture", 3 | "version": "1.0.0", 4 | "description": "test", 5 | "dependencies": { 6 | "@jitl/quickjs-wasmfile-release-sync": "^0.29.1", 7 | "quickjs-emscripten-core": "^0.29.1", 8 | "hono": "4.2.7", 9 | "@aws-sdk/client-s3": "3.556.0" 10 | }, 11 | "devDependencies": { 12 | "@chialab/esbuild-plugin-meta-url": "^0.18.2", 13 | "esbuild": "0.20.2", 14 | "typescript": "^5.4.5" 15 | }, 16 | "scripts": { 17 | "build": "node esbuild.mjs" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-metafiles/esbuild2.mjs: -------------------------------------------------------------------------------- 1 | import { writeFileSync } from "node:fs"; 2 | import { build } from "esbuild"; 3 | 4 | const result = await build({ 5 | entryPoints: [ 6 | `./src/bit-increased.ts`, 7 | `./src/much-increased.ts`, 8 | `./src/decreased.ts`, 9 | `./src/new-outfile.ts`, 10 | ], 11 | outdir: `out`, 12 | format: "esm", 13 | metafile: true, 14 | mainFields: ["module", "main"], 15 | platform: "node", 16 | target: "node20.9", 17 | bundle: true, 18 | plugins: [], 19 | }); 20 | 21 | writeFileSync(`out/meta2.json`, JSON.stringify(result.metafile, null, 2)); 22 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/basic/esbuild.mjs: -------------------------------------------------------------------------------- 1 | import {mkdirSync, writeFileSync} from "node:fs"; 2 | import { build } from "esbuild"; 3 | 4 | const result = await build({ 5 | entryPoints: [`./src/index.ts`], 6 | outfile: `out/index.mjs`, 7 | format: "esm", 8 | metafile: true, 9 | mainFields: ["module", "main"], 10 | platform: "node", 11 | target: "node20.9", 12 | bundle: true, 13 | plugins: [], 14 | loader: { 15 | ".node": "copy", 16 | }, 17 | assetNames: "resources/[name]-[hash]", 18 | }); 19 | 20 | mkdirSync(`out/foo`, { recursive: true }); 21 | writeFileSync(`out/foo/meta.json`, JSON.stringify(result.metafile, null, 2)); 22 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/multi-outputs/esbuild.mjs: -------------------------------------------------------------------------------- 1 | import { writeFileSync, mkdirSync } from "node:fs"; 2 | import { build } from "esbuild"; 3 | 4 | const result = await build({ 5 | entryPoints: [`./src/alpha.ts`, `./src/beta.ts`], 6 | outdir: `out`, 7 | format: "esm", 8 | metafile: true, 9 | mainFields: ["module", "main"], 10 | platform: "node", 11 | target: "node20.9", 12 | bundle: true, 13 | plugins: [], 14 | loader: { 15 | ".node": "copy", 16 | }, 17 | assetNames: "resources/[name]-[hash]", 18 | }); 19 | 20 | mkdirSync(`out/bar`, { recursive: true }); 21 | writeFileSync(`out/bar/meta.json`, JSON.stringify(result.metafile, null, 2)); 22 | -------------------------------------------------------------------------------- /.github/release.yaml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - ignore-for-release 5 | authors: 6 | - octocat 7 | categories: 8 | - title: 🛠 Breaking Changes 9 | labels: 10 | - semver-major 11 | - breaking-change 12 | - title: 🎉 Exciting New Features 13 | labels: 14 | - semver-minor 15 | - enhancement 16 | - title: 🐞 Bugfixes 17 | labels: 18 | - bug 19 | - title: 📄 Documentation 20 | labels: 21 | - documentation 22 | - title: 📦 Dependencies 23 | labels: 24 | - dependencies 25 | - title: 🗂️ Other Changes 26 | labels: 27 | - "*" 28 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-87/esbuild.mjs: -------------------------------------------------------------------------------- 1 | import { writeFileSync } from "node:fs"; 2 | import { build } from "esbuild"; 3 | import metaUrlPlugin from '@chialab/esbuild-plugin-meta-url'; 4 | 5 | const result = await build({ 6 | entryPoints: [ 7 | `./src/no-change.ts`, 8 | `./src/bit-increased.ts`, 9 | `./src/much-increased.ts`, 10 | `./src/decreased.ts`, 11 | `./src/new-outfile.ts`, 12 | `./src/file-imported.ts`, 13 | ], 14 | outdir: `out`, 15 | format: "esm", 16 | metafile: true, 17 | mainFields: ["module", "main"], 18 | platform: "node", 19 | target: "node20.9", 20 | bundle: true, 21 | plugins: [ 22 | metaUrlPlugin(), 23 | ], 24 | sourcemap: true, 25 | }); 26 | 27 | writeFileSync(`out/meta.json`, JSON.stringify(result.metafile, null, 2)); 28 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-90/esbuild.mjs: -------------------------------------------------------------------------------- 1 | import { writeFileSync } from "node:fs"; 2 | import { build } from "esbuild"; 3 | import metaUrlPlugin from '@chialab/esbuild-plugin-meta-url'; 4 | 5 | const result = await build({ 6 | entryPoints: [ 7 | `./src/no-change.ts`, 8 | `./src/bit-increased.ts`, 9 | `./src/much-increased.ts`, 10 | `./src/decreased.ts`, 11 | `./src/new-outfile.ts`, 12 | `./src/file-imported.ts`, 13 | ], 14 | outdir: `out`, 15 | format: "esm", 16 | metafile: true, 17 | mainFields: ["module", "main"], 18 | platform: "node", 19 | target: "node20.9", 20 | bundle: true, 21 | plugins: [ 22 | metaUrlPlugin(), 23 | ], 24 | sourcemap: true, 25 | }); 26 | 27 | writeFileSync(`out/meta.json`, JSON.stringify(result.metafile, null, 2)); 28 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-output/esbuild.mjs: -------------------------------------------------------------------------------- 1 | import { writeFileSync } from "node:fs"; 2 | import { build } from "esbuild"; 3 | import metaUrlPlugin from '@chialab/esbuild-plugin-meta-url'; 4 | 5 | const result = await build({ 6 | entryPoints: [ 7 | `./src/no-change.ts`, 8 | `./src/bit-increased.ts`, 9 | `./src/much-increased.ts`, 10 | `./src/decreased.ts`, 11 | `./src/new-outfile.ts`, 12 | `./src/file-imported.ts`, 13 | ], 14 | outdir: `out`, 15 | format: "esm", 16 | metafile: true, 17 | mainFields: ["module", "main"], 18 | platform: "node", 19 | target: "node20.9", 20 | bundle: true, 21 | plugins: [ 22 | metaUrlPlugin(), 23 | ], 24 | sourcemap: true, 25 | }); 26 | 27 | writeFileSync(`out/meta.json`, JSON.stringify(result.metafile, null, 2)); 28 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-87/.analyzer/base/bundle/bundle_analysis.json: -------------------------------------------------------------------------------- 1 | { 2 | "out/meta.json -> out/no-change.js": { 3 | "metafile": "out/meta.json", 4 | "outfile": "out/no-change.js", 5 | "bytes": 733190 6 | }, 7 | "out/meta.json -> out/decreased.js": { 8 | "metafile": "out/meta.json", 9 | "outfile": "out/decreased.js", 10 | "bytes": 50000 11 | }, 12 | "out/meta.json -> out/bit-increased.js": { 13 | "metafile": "out/meta.json", 14 | "outfile": "out/bit-increased.js", 15 | "bytes": 44000 16 | }, 17 | "out/meta.json -> out/much-increased.js": { 18 | "metafile": "out/meta.json", 19 | "outfile": "out/much-increased.js", 20 | "bytes": 20000 21 | }, 22 | "out/meta.json -> out/missing.js": { 23 | "metafile": "out/meta.json", 24 | "outfile": "out/missing.js", 25 | "bytes": 12345 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-output/.analyzer/base/bundle/bundle_analysis.json: -------------------------------------------------------------------------------- 1 | { 2 | "out/meta.json -> out/no-change.js": { 3 | "metafile": "out/meta.json", 4 | "outfile": "out/no-change.js", 5 | "bytes": 733190 6 | }, 7 | "out/meta.json -> out/decreased.js": { 8 | "metafile": "out/meta.json", 9 | "outfile": "out/decreased.js", 10 | "bytes": 50000 11 | }, 12 | "out/meta.json -> out/bit-increased.js": { 13 | "metafile": "out/meta.json", 14 | "outfile": "out/bit-increased.js", 15 | "bytes": 44000 16 | }, 17 | "out/meta.json -> out/much-increased.js": { 18 | "metafile": "out/meta.json", 19 | "outfile": "out/much-increased.js", 20 | "bytes": 20000 21 | }, 22 | "out/meta.json -> out/missing.js": { 23 | "metafile": "out/meta.json", 24 | "outfile": "out/missing.js", 25 | "bytes": 12345 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples-with-base/multi-metafiles/.analyzer/base/bundle/bundle_analysis.json: -------------------------------------------------------------------------------- 1 | { 2 | "out/meta1.json -> out/no-change.js": { 3 | "metafile": "out/meta1.json", 4 | "outfile": "out/no-change.js", 5 | "bytes": 732985 6 | }, 7 | "out/meta2.json -> out/decreased.js": { 8 | "metafile": "out/meta2.json", 9 | "outfile": "out/decreased.js", 10 | "bytes": 50000 11 | }, 12 | "out/meta2.json -> out/bit-increased.js": { 13 | "metafile": "out/meta2.json", 14 | "outfile": "out/bit-increased.js", 15 | "bytes": 44000 16 | }, 17 | "out/meta2.json -> out/much-increased.js": { 18 | "metafile": "out/meta2.json", 19 | "outfile": "out/much-increased.js", 20 | "bytes": 20000 21 | }, 22 | "out/meta2.json -> out/missing.js": { 23 | "metafile": "out/meta2.json", 24 | "outfile": "out/missing.js", 25 | "bytes": 12345 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/bump.yml: -------------------------------------------------------------------------------- 1 | name: Bump major version 2 | on: 3 | push: 4 | tags: 5 | - 'v*.*.*' 6 | 7 | jobs: 8 | tag: 9 | permissions: 10 | contents: write 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v6 14 | with: 15 | ref: main 16 | token: ${{ secrets.GITHUB_TOKEN }} 17 | fetch-depth: 0 18 | - name: Git config 19 | run: | 20 | git config user.name actions-bot 21 | git config user.email actions-bot@users.noreply.github.com 22 | - name: Tag new target 23 | run: | 24 | major_version=$(echo "${{ github.ref_name }}" | cut -d'.' -f1) 25 | git tag -f $major_version ${{ github.ref_name }} 26 | - name: Push new tag 27 | run: | 28 | major_version=$(echo "${{ github.ref_name }}" | cut -d'.' -f1) 29 | git push origin $major_version --force 30 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | interface ReportEntry { 2 | metafile: string; 3 | outfile: string; 4 | bytes: number; 5 | } 6 | export type Report = Record; 7 | 8 | export interface CompareResult { 9 | metafile: string; 10 | outfile: string; 11 | bytes: number; 12 | baseBytes: number; 13 | remark: "added" | "deleted" | "increased" | "decreased" | "no-change"; 14 | tree: TreeMapNode | undefined; 15 | } 16 | 17 | export type SizeComparisonFilter = CompareResult["remark"] | "total"; 18 | 19 | export interface Input { 20 | name: string; 21 | metafiles: Array; 22 | includeExtensions: Array; 23 | includeSizeComparison: Set; 24 | topNLargestPaths: number; 25 | analyzerDirectory: string; 26 | percentExtraAttention: number; 27 | showDetails: boolean; 28 | } 29 | 30 | export interface TreeMapNode { 31 | value: number; 32 | name: string; 33 | color?: Array; 34 | path: string; 35 | children: TreeMapNode[]; 36 | } 37 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/issues/issue-90/.analyzer/base/bundle/bundle_analysis.json: -------------------------------------------------------------------------------- 1 | { 2 | "out/meta.json -> out/no-change-old.js": { 3 | "bytes": 733190, 4 | "metafile": "out/meta.json", 5 | "outfile": "out/no-change-old.js" 6 | }, 7 | "out/meta.json -> out/bit-increased-old.js": { 8 | "bytes": 44674, 9 | "metafile": "out/meta.json", 10 | "outfile": "out/bit-increased-old.js" 11 | }, 12 | "out/meta.json -> out/much-increased-old.js": { 13 | "bytes": 44678, 14 | "metafile": "out/meta.json", 15 | "outfile": "out/much-increased-old.js" 16 | }, 17 | "out/meta.json -> out/decreased-old.js": { 18 | "bytes": 44658, 19 | "metafile": "out/meta.json", 20 | "outfile": "out/decreased-old.js" 21 | }, 22 | "out/meta.json -> out/new-outfile-old.js": { 23 | "bytes": 200, 24 | "metafile": "out/meta.json", 25 | "outfile": "out/new-outfile-old.js" 26 | }, 27 | "out/meta.json -> out/file-imported-old.js": { 28 | "bytes": 68068, 29 | "metafile": "out/meta.json", 30 | "outfile": "out/file-imported-old.js" 31 | } 32 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | 3 | import type { Metafile } from "esbuild"; 4 | import type { Report } from "./types"; 5 | 6 | function loadJsonFile(path: string) { 7 | return JSON.parse(fs.readFileSync(path).toString("utf-8")); 8 | } 9 | 10 | export function loadMetaFile(path: string): Metafile { 11 | return loadJsonFile(path) as Metafile; 12 | } 13 | 14 | export function loadAnalysisJson(path: string): Report { 15 | return loadJsonFile(path) as Report; 16 | } 17 | 18 | // https://github.com/actions/toolkit/blob/81a73aba8bedd532f6eddcc41ed3a0fad8b1cfeb/packages/core/src/core.ts#L126 19 | export function getSingleInput(name: string): string { 20 | const val = process.env[`INPUT_${name.toUpperCase()}`] || ""; 21 | return val.trim(); 22 | } 23 | 24 | export function getBooleanInput( 25 | name: string, 26 | fallback: "true" | "false", 27 | ): boolean { 28 | return ["true", "True", "TRUE"].includes(getSingleInput(name) || fallback); 29 | } 30 | 31 | export function getNumberInput(name: string, fallback: number): number { 32 | const raw = getSingleInput(name); 33 | return raw === "" ? fallback : Number.parseInt(raw, 10); 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 TATSUNO Yasuhiro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /esbuild.mjs: -------------------------------------------------------------------------------- 1 | import * as esbuild from 'esbuild' 2 | import { readFileSync, writeFileSync } from 'node:fs' 3 | 4 | // https://github.com/evanw/esbuild/issues/1685#issuecomment-944916409 5 | const excludeNodeModulesFromSourceMap = { 6 | name: 'excludeNodeModulesFromSourceMap', 7 | setup(build) { 8 | build.onLoad({filter: /node_modules/}, args => { 9 | if (args.path.endsWith('.js')) { 10 | return { 11 | contents: 12 | readFileSync(args.path, 'utf8') + 13 | '\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJtYXBwaW5ncyI6IkEifQ==', 14 | loader: 'default', 15 | }; 16 | } 17 | }) 18 | }, 19 | }; 20 | 21 | const result = await esbuild.build({ 22 | entryPoints: [`./src/index.ts`], 23 | outfile: `dist/index.mjs`, 24 | format: 'esm', 25 | metafile: true, 26 | minify: true, 27 | mainFields: ['module', 'main'], 28 | platform: 'node', 29 | target: 'node18', 30 | sourcemap: "inline", 31 | bundle: true, 32 | plugins: [excludeNodeModulesFromSourceMap] 33 | }) 34 | 35 | writeFileSync(`tmp/meta.json`, JSON.stringify(result.metafile, null, 2)); 36 | -------------------------------------------------------------------------------- /scripts/validate-yaml.ts: -------------------------------------------------------------------------------- 1 | import YAML from 'yaml'; 2 | import fs from 'node:fs'; 3 | 4 | const actionYaml = YAML.parse(fs.readFileSync("action.yaml", "utf-8")); 5 | 6 | const inputKeys = Object.keys(actionYaml.inputs) 7 | const envKeys = new Set(); 8 | actionYaml.runs.steps.forEach((step: any) => { 9 | if (step.name !== "Compare with base branch bundle") return; 10 | Object.keys(step.env).forEach((key: string) => { 11 | envKeys.add(key.replace(/^INPUT_/, "").toLowerCase()) 12 | }) 13 | }) 14 | if (envKeys.size === 0) { 15 | throw new Error("No environment variables found. Did you forget to add `env` to the step?") 16 | } 17 | 18 | const missingKeys = inputKeys.filter((key: string) => !envKeys.has(key)) 19 | if (missingKeys.length > 0) { 20 | const s = missingKeys.map((key: string) => `INPUT_${key.toUpperCase()}: \${{ inputs.${key} }}`).join("\n") 21 | 22 | throw new Error(` 23 | Missing environment variables for ${missingKeys.join(", ")}. 24 | Add the following envs to the step "Compare with base branch bundle" in action.yaml: 25 | 26 | ${s} 27 | `) 28 | } else { 29 | console.log("All environment variables are present") 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-bundle-analyzer", 3 | "version": "0.5.0", 4 | "description": "Analyzes each PR's impact on esbuild bundle size", 5 | "type": "module", 6 | "scripts": { 7 | "test": "vitest", 8 | "test:run": "vitest --run --coverage", 9 | "build": "node esbuild.mjs", 10 | "check": "tsc && biome check --write ." 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/esbio;d/nextjs-bundle-analysis.git" 15 | }, 16 | "keywords": [ 17 | "lambda", 18 | "esbuild", 19 | "bundle", 20 | "analysis", 21 | "github", 22 | "action" 23 | ], 24 | "author": "TATSUNO Yasuhiro", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/exoego/esbuild-bundle-analysis/issues" 28 | }, 29 | "homepage": "https://github.com/exoego/esbuild-bundle-analysis#readme", 30 | "dependencies": { 31 | "glob": "^11.0.3" 32 | }, 33 | "devDependencies": { 34 | "@biomejs/biome": "^2.3.8", 35 | "@types/node": "^24.10.1", 36 | "@vitest/coverage-v8": "4.0.14", 37 | "esbuild": "^0.27.0", 38 | "typescript": "^5.9.3", 39 | "vitest": "4.0.14", 40 | "yaml": "^2.8.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/ci-from-fork.yml: -------------------------------------------------------------------------------- 1 | name: CI (from fork) 2 | on: 3 | push: 4 | branches: [main] 5 | paths-ignore: 6 | - '*.md' 7 | pull_request_target: 8 | branches: [main] 9 | 10 | 11 | jobs: 12 | bundle-analysis-from-fork: 13 | permissions: 14 | contents: read # for checkout repository 15 | actions: read # for fetching base branch bundle stats 16 | pull-requests: write # for comments 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 5 19 | if: github.event.pull_request.head.repo.fork == true 20 | steps: 21 | - uses: actions/checkout@v6 22 | - uses: actions/setup-node@v6 23 | with: 24 | node-version: 22.x 25 | cache: npm 26 | - run: npm ci 27 | - run: npm run build 28 | - name: Analyze esbuild bundle size 29 | uses: jenseng/dynamic-uses@26a7fa196ecfc98e02d08d65a09d03ab999683ae 30 | with: 31 | uses: exoego/esbuild-bundle-analyzer@${{ github.sha }} 32 | with: '{"metafiles": "tmp/meta.json"}' 33 | - uses: actions/upload-artifact@v5 34 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 35 | with: 36 | name: distribution 37 | path: | 38 | dist/ 39 | action.yaml 40 | -------------------------------------------------------------------------------- /__tests__/helper.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | 4 | interface Example { 5 | name: string; 6 | path: string; 7 | } 8 | 9 | export function getExampleDirectories(baseDirectory: string): Example[] { 10 | return fs 11 | .readdirSync(baseDirectory, { withFileTypes: true }) 12 | .filter((dirent) => dirent.isDirectory()) 13 | .map((dirent) => { 14 | return { 15 | name: dirent.name, 16 | path: path.join(baseDirectory, dirent.name), 17 | }; 18 | }); 19 | } 20 | 21 | export function readCurrentAnalysis(analyzeDirectory: string) { 22 | return fs.readFileSync( 23 | path.join(process.cwd(), analyzeDirectory, "bundle_analysis.json"), 24 | "utf8", 25 | ); 26 | } 27 | 28 | export function createDummyBaseAnalysis( 29 | analyzeDirectory: string, 30 | dummyContent: string, 31 | ) { 32 | // In the real world, this would pull from GitHub as part of the action flow 33 | fs.mkdirSync(path.join(process.cwd(), analyzeDirectory, "base/bundle"), { 34 | recursive: true, 35 | }); 36 | fs.writeFileSync( 37 | path.join( 38 | process.cwd(), 39 | analyzeDirectory, 40 | "base/bundle/bundle_analysis.json", 41 | ), 42 | dummyContent, 43 | ); 44 | } 45 | 46 | export function readAnalysisComment(analyzeDirectory: string) { 47 | return fs.readFileSync( 48 | path.join(process.cwd(), analyzeDirectory, "bundle_analysis_comment.txt"), 49 | "utf8", 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /__tests__/no-base.test.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "node:child_process"; 2 | import path from "node:path"; 3 | import { compare } from "../src/compare"; 4 | import { report } from "../src/report"; 5 | import type { Input } from "../src/types"; 6 | import { 7 | createDummyBaseAnalysis, 8 | getExampleDirectories, 9 | readAnalysisComment, 10 | readCurrentAnalysis, 11 | } from "./helper"; 12 | 13 | describe("examples w/o base analysis", () => { 14 | const fixturesPath = path.join(__dirname, "__fixtures__", "examples"); 15 | for (const example of getExampleDirectories(fixturesPath)) { 16 | describe(`example ${example.name}`, () => { 17 | const input: Input = { 18 | analyzerDirectory: ".analyzer", 19 | percentExtraAttention: 20, 20 | includeExtensions: [".js", ".mjs", ".cjs"], 21 | includeSizeComparison: new Set([ 22 | "added", 23 | "deleted", 24 | "increased", 25 | "decreased", 26 | "no-change", 27 | ]), 28 | metafiles: ["out/**/meta.json"], 29 | name: "test", 30 | showDetails: false, 31 | topNLargestPaths: 10, 32 | }; 33 | 34 | beforeEach(() => { 35 | process.chdir(example.path); 36 | execSync("npm ci"); 37 | execSync("npm run build"); 38 | }); 39 | 40 | test(`bundle analysis action generates report and compares artifacts correctly ${example.name}`, () => { 41 | report(input); 42 | const bundleAnalysis = readCurrentAnalysis(input.analyzerDirectory); 43 | expect(bundleAnalysis.length).toBeGreaterThan(1); 44 | 45 | createDummyBaseAnalysis(input.analyzerDirectory, bundleAnalysis); 46 | 47 | compare(input); 48 | const comment = readAnalysisComment(input.analyzerDirectory); 49 | expect(comment).toMatch(/no changes to the esbuild bundle/i); 50 | }); 51 | }); 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /__tests__/issue-87.test.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "node:child_process"; 2 | import path from "node:path"; 3 | 4 | import { run } from "../src"; 5 | import type { Input, SizeComparisonFilter } from "../src/types"; 6 | import { readAnalysisComment, readCurrentAnalysis } from "./helper"; 7 | 8 | describe("issues", () => { 9 | const fixturesPath = path.join(__dirname, "__fixtures__", "issues"); 10 | 11 | const example = { 12 | name: "issue-87", 13 | path: path.join(fixturesPath, "issue-87"), 14 | }; 15 | 16 | describe(`example ${example.name}`, () => { 17 | const input: Input = { 18 | analyzerDirectory: ".analyzer", 19 | percentExtraAttention: 20, 20 | includeExtensions: [".js", ".mjs", ".cjs"], 21 | metafiles: ["out/meta.json"], 22 | name: "test", 23 | showDetails: false, 24 | topNLargestPaths: 0, 25 | includeSizeComparison: new Set(["total"]), 26 | }; 27 | 28 | beforeEach(() => { 29 | process.chdir(example.path); 30 | execSync("npm ci"); 31 | execSync("npm run build"); 32 | }); 33 | 34 | test(`bundle analysis action generates report and compares artifacts correctly ${example.name}`, () => { 35 | run(input); 36 | 37 | const bundleAnalysis = readCurrentAnalysis(input.analyzerDirectory); 38 | expect(bundleAnalysis.length).toBeGreaterThan(1); 39 | 40 | const comment = readAnalysisComment(input.analyzerDirectory); 41 | expect(comment).toMatch(/\(Total\)/i); 42 | expect(comment).toMatch(/\d bundles are hidden./i); 43 | expect(comment).toMatch(/‼️ \+\d+.*/); 44 | expect(comment).toMatch(/⚠️ \+\d+.*/); 45 | expect(comment).toMatch(/✅ {2}-\d+.*/); 46 | expect(comment).toMatch(/✅ {2}No change.*/i); 47 | expect(comment).toMatch(/🆕 Added.*/i); 48 | expect(comment).toMatch(/🗑️ Deleted.*/i); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /__tests__/issue-90.test.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "node:child_process"; 2 | import path from "node:path"; 3 | 4 | import { run } from "../src"; 5 | import type { Input, SizeComparisonFilter } from "../src/types"; 6 | import { readAnalysisComment, readCurrentAnalysis } from "./helper"; 7 | 8 | describe("issues", () => { 9 | const fixturesPath = path.join(__dirname, "__fixtures__", "issues"); 10 | 11 | const example = { 12 | name: "issue-90", 13 | path: path.join(fixturesPath, "issue-90"), 14 | }; 15 | 16 | describe(`example ${example.name} should return no-change Total`, () => { 17 | const input: Input = { 18 | analyzerDirectory: ".analyzer", 19 | percentExtraAttention: 20, 20 | includeExtensions: [".js", ".mjs", ".cjs"], 21 | metafiles: ["out/meta.json"], 22 | name: "test", 23 | showDetails: false, 24 | topNLargestPaths: 0, 25 | includeSizeComparison: new Set(["total"]), 26 | }; 27 | 28 | beforeEach(() => { 29 | process.chdir(example.path); 30 | execSync("npm ci"); 31 | execSync("npm run build"); 32 | }); 33 | 34 | test(`bundle analysis action generates report and compares artifacts correctly ${example.name}`, () => { 35 | run(input); 36 | 37 | const bundleAnalysis = readCurrentAnalysis(input.analyzerDirectory); 38 | expect(bundleAnalysis.length).toBeGreaterThan(1); 39 | 40 | const comment = readAnalysisComment(input.analyzerDirectory); 41 | expect(comment).toMatch(/\(Total\).+✅ {2}No change/i); 42 | expect(comment).toMatch(/\d bundles are hidden./i); 43 | expect(comment).not.toMatch(/‼️ \+\d+.*/); 44 | expect(comment).not.toMatch(/⚠️ \+\d+.*/); 45 | expect(comment).not.toMatch(/✅ {2}-\d+.*/); 46 | expect(comment).not.toMatch(/✅ {2}No change.*/i); 47 | expect(comment).toMatch(/🆕 Added.*/i); 48 | expect(comment).toMatch(/🗑️ Deleted.*/i); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /src/report.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | import process from "node:process"; 4 | 5 | import * as console from "node:console"; 6 | import { globSync } from "glob"; 7 | import type { Input, Report } from "./types"; 8 | import { loadMetaFile } from "./utils"; 9 | 10 | export function report(input: Input): void { 11 | const allPageSizes = getAllPageSizes(input); 12 | fs.mkdirSync(path.join(process.cwd(), input.analyzerDirectory), { 13 | recursive: true, 14 | }); 15 | const resultJsonPath = path.join( 16 | process.cwd(), 17 | input.analyzerDirectory, 18 | "bundle_analysis.json", 19 | ); 20 | fs.writeFileSync(resultJsonPath, JSON.stringify(allPageSizes, null, 2)); 21 | console.log(`Wrote ${resultJsonPath}`, allPageSizes); 22 | } 23 | 24 | interface MetafilePath { 25 | readonly relativePath: string; 26 | readonly absolutePath: string; 27 | } 28 | 29 | export function findMetafiles(input: Input): MetafilePath[] { 30 | return input.metafiles.flatMap((metafile) => { 31 | return globSync(path.join(process.cwd(), metafile), { 32 | nodir: true, 33 | }).map((metaFilePath) => { 34 | return { 35 | relativePath: path.relative(process.cwd(), metaFilePath), 36 | absolutePath: metaFilePath, 37 | }; 38 | }); 39 | }); 40 | } 41 | 42 | function getAllPageSizes(input: Input): Report { 43 | const acc: Report = {}; 44 | 45 | const metafiles = findMetafiles(input); 46 | const result = metafiles.reduce((acc, { relativePath, absolutePath }) => { 47 | try { 48 | fs.accessSync(absolutePath, fs.constants.R_OK); 49 | } catch (err) { 50 | console.error( 51 | `No meta file found at "${absolutePath}" - a path to meta file may be wrong, or esbuild is not executed.`, 52 | ); 53 | process.exit(1); 54 | } 55 | 56 | const metaFileJson = loadMetaFile(absolutePath); 57 | Object.entries(metaFileJson.outputs).reduce((acc, output) => { 58 | const [outfile, buildMeta] = output; 59 | if ( 60 | !input.includeExtensions.some((ext) => 61 | outfile.toLowerCase().endsWith(ext), 62 | ) 63 | ) { 64 | return acc; 65 | } 66 | acc[`${relativePath} -> ${outfile}`] = { 67 | bytes: buildMeta.bytes, 68 | metafile: relativePath, 69 | outfile, 70 | }; 71 | return acc; 72 | }, acc); 73 | return acc; 74 | }, acc); 75 | console.log("Found metafiles", metafiles); 76 | console.log("Found result", result); 77 | return result; 78 | } 79 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import console from "node:console"; 2 | import { pathToFileURL } from "node:url"; 3 | import { compare } from "./compare"; 4 | import { report } from "./report"; 5 | import type { Input, SizeComparisonFilter } from "./types"; 6 | import { getBooleanInput, getNumberInput, getSingleInput } from "./utils"; 7 | 8 | function getInput(): Input { 9 | const rawMetafiles = getSingleInput("metafiles"); 10 | if (!rawMetafiles) { 11 | throw new Error("metafiles is not specified"); 12 | } 13 | const name = getSingleInput("name"); 14 | if (!name) { 15 | throw new Error("name is not specified"); 16 | } 17 | const filters = new Set( 18 | ( 19 | getSingleInput("include_size_comparison") || 20 | "added, deleted, increased, decreased, no-change" 21 | ) 22 | .split(",") 23 | .map((s) => { 24 | switch (s.trim()) { 25 | case "added": 26 | case "deleted": 27 | case "increased": 28 | case "decreased": 29 | case "total": 30 | case "no-change": 31 | return s.trim() as SizeComparisonFilter; 32 | default: 33 | throw new Error(`Unknown size comparison filter: ${s}`); 34 | } 35 | }), 36 | ); 37 | const rawShowNoChange = getSingleInput("show_no_change"); 38 | if (rawShowNoChange !== "") { 39 | if (getBooleanInput("show_no_change", "true")) { 40 | filters.add("no-change"); 41 | console.log( 42 | "`show_no_change: true` is deprecated. Instead, remove `no-change` from the `include_size_comparison` list.", 43 | ); 44 | } else { 45 | filters.delete("no-change"); 46 | console.log( 47 | "`show_no_change: false` is deprecated. Instead, add `no-change` to the `include_size_comparison` list.", 48 | ); 49 | } 50 | } 51 | return { 52 | percentExtraAttention: getNumberInput("percent_extra_attention", 20), 53 | showDetails: getBooleanInput("show_details", "true"), 54 | topNLargestPaths: getNumberInput("top_n_largest_paths", 20), 55 | includeExtensions: ( 56 | getSingleInput("include_extensions") || ".js,.mjs,.cjs" 57 | ).split(","), 58 | includeSizeComparison: filters, 59 | name, 60 | analyzerDirectory: getSingleInput("analyze_directory") || ".analyzer", 61 | metafiles: rawMetafiles.split(","), 62 | }; 63 | } 64 | 65 | export function run(input: Input = getInput()): void { 66 | report(input); 67 | compare(input); 68 | } 69 | 70 | if (import.meta.url === pathToFileURL(process.argv[1]).href) { 71 | run(); 72 | } 73 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [main] 5 | paths-ignore: 6 | - '*.md' 7 | pull_request: 8 | branches: [main] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 5 14 | steps: 15 | - uses: actions/checkout@v6 16 | - uses: actions/setup-node@v6 17 | with: 18 | node-version: 22.x 19 | cache: npm 20 | - run: npm ci 21 | - run: npm run check 22 | - run: npm run test:run 23 | - name: Upload coverage reports to Codecov 24 | uses: codecov/codecov-action@v5 25 | with: 26 | token: ${{ secrets.CODECOV_TOKEN }} 27 | - run: npm run build 28 | - name: Check uncommited file 29 | run: | 30 | git diff 31 | git_diff_stat=$(git diff --stat) 32 | if [[ $git_diff_stat != '' ]]; then 33 | echo "Uncommitted file found. Ensure you have run tests and committed all changes." 34 | echo "" 35 | echo $git_diff_stat 36 | exit 1 37 | else 38 | echo 'clean' 39 | fi 40 | 41 | bundle-analysis: 42 | permissions: 43 | contents: read # for checkout repository 44 | actions: read # for fetching base branch bundle stats 45 | pull-requests: write # for comments 46 | runs-on: ubuntu-latest 47 | timeout-minutes: 5 48 | if: github.event.pull_request.head.repo.fork == false 49 | steps: 50 | - uses: actions/checkout@v6 51 | - uses: actions/setup-node@v6 52 | with: 53 | node-version: 22.x 54 | cache: npm 55 | - run: npm ci 56 | - run: npm run build 57 | - name: Analyze esbuild bundle size 58 | uses: jenseng/dynamic-uses@26a7fa196ecfc98e02d08d65a09d03ab999683ae 59 | with: 60 | uses: exoego/esbuild-bundle-analyzer@${{ github.sha }} 61 | with: '{"metafiles": "tmp/meta.json"}' 62 | - uses: actions/upload-artifact@v5 63 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 64 | with: 65 | name: distribution 66 | path: | 67 | dist/ 68 | action.yaml 69 | 70 | validate-action-yaml: 71 | runs-on: ubuntu-latest 72 | timeout-minutes: 1 73 | steps: 74 | - uses: actions/checkout@v6 75 | - uses: oven-sh/setup-bun@v2 76 | - run: bun install 77 | - run: bun scripts/validate-yaml.ts 78 | -------------------------------------------------------------------------------- /__tests__/with-base.test.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "node:child_process"; 2 | import path from "node:path"; 3 | 4 | import { run } from "../src/index"; 5 | import type { Input, SizeComparisonFilter } from "../src/types"; 6 | import { 7 | getExampleDirectories, 8 | readAnalysisComment, 9 | readCurrentAnalysis, 10 | } from "./helper"; 11 | 12 | describe("examples w/ base analysis", () => { 13 | const fixturesPath = path.join( 14 | __dirname, 15 | "__fixtures__", 16 | "examples-with-base", 17 | ); 18 | 19 | for (const [index, example] of getExampleDirectories( 20 | fixturesPath, 21 | ).entries()) { 22 | describe(`example ${example.name}`, () => { 23 | const metafiles = 24 | example.name === "multi-metafiles" 25 | ? ["out/meta1.json", "out/meta2.json"] 26 | : ["out/meta.json"]; 27 | 28 | const isEven = index % 2 === 0; 29 | 30 | const includeSizeComparison = new Set([ 31 | "total", 32 | "added", 33 | "deleted", 34 | "increased", 35 | "decreased", 36 | "no-change", 37 | ]); 38 | if (!isEven) { 39 | includeSizeComparison.delete("total"); 40 | includeSizeComparison.delete("no-change"); 41 | } 42 | const input: Input = { 43 | analyzerDirectory: ".analyzer", 44 | percentExtraAttention: 20, 45 | includeExtensions: [".js", ".mjs", ".cjs"], 46 | metafiles, 47 | name: "test", 48 | showDetails: false, 49 | topNLargestPaths: 10, 50 | includeSizeComparison, 51 | }; 52 | 53 | beforeEach(() => { 54 | process.chdir(example.path); 55 | execSync("npm ci"); 56 | execSync("npm run build"); 57 | }); 58 | 59 | test(`bundle analysis action generates report and compares artifacts correctly ${example.name}`, () => { 60 | run(input); 61 | 62 | const bundleAnalysis = readCurrentAnalysis(input.analyzerDirectory); 63 | expect(bundleAnalysis.length).toBeGreaterThan(1); 64 | 65 | const comment = readAnalysisComment(input.analyzerDirectory); 66 | expect(comment).not.toMatch(/no changes to the esbuild bundle/i); 67 | expect(comment).not.include(".js.map"); 68 | expect(comment).not.include(".wasm"); 69 | expect(comment).toMatch(/\.[cm]?js /i); 70 | expect(comment).toMatch(/‼️ \+\d+/); 71 | expect(comment).toMatch(/⚠️ \+\d+/); 72 | expect(comment).toMatch(/✅ {2}-\d+/); 73 | expect(comment).toMatch(/✅ {2}No change/i); 74 | if (isEven) { 75 | expect(comment).not.toMatch(/\d bundles are hidden./i); 76 | expect(comment).toMatch(/\(Total\) \| - \|.+⚠️/i); 77 | } else { 78 | expect(comment).toMatch(/\d bundles are hidden./i); 79 | expect(comment).not.toMatch(/\(Total\) \| - \|.+⚠️/i); 80 | } 81 | expect(comment).toMatch(/🆕 Added/i); 82 | expect(comment).toMatch(/🗑️ Deleted/i); 83 | }); 84 | }); 85 | } 86 | }); 87 | -------------------------------------------------------------------------------- /__tests__/compare.test.ts: -------------------------------------------------------------------------------- 1 | import { calculateTotalRow } from "../src/compare"; 2 | 3 | describe("calculateTotalRow", () => { 4 | test("empty returns zero", () => { 5 | expect(calculateTotalRow([])).toEqual({ 6 | baseBytes: 0, 7 | bytes: 0, 8 | metafile: "(Total)", 9 | outfile: "-", 10 | remark: "no-change", 11 | tree: undefined, 12 | }); 13 | }); 14 | 15 | describe("single row", () => { 16 | test("given decreased, return decreased", () => { 17 | expect( 18 | calculateTotalRow([ 19 | { 20 | baseBytes: 100, 21 | bytes: 50, 22 | metafile: "", 23 | outfile: "", 24 | remark: "decreased", 25 | tree: undefined, 26 | }, 27 | ]), 28 | ).toEqual({ 29 | baseBytes: 100, 30 | bytes: 50, 31 | metafile: "(Total)", 32 | outfile: "-", 33 | remark: "decreased", 34 | tree: undefined, 35 | }); 36 | }); 37 | test("given increased, return increased", () => { 38 | expect( 39 | calculateTotalRow([ 40 | { 41 | baseBytes: 50, 42 | bytes: 100, 43 | metafile: "", 44 | outfile: "", 45 | remark: "increased", 46 | tree: undefined, 47 | }, 48 | ]), 49 | ).toEqual({ 50 | baseBytes: 50, 51 | bytes: 100, 52 | metafile: "(Total)", 53 | outfile: "-", 54 | remark: "increased", 55 | tree: undefined, 56 | }); 57 | }); 58 | test("given no-change, return no-change", () => { 59 | expect( 60 | calculateTotalRow([ 61 | { 62 | baseBytes: 50, 63 | bytes: 50, 64 | metafile: "", 65 | outfile: "", 66 | remark: "no-change", 67 | tree: undefined, 68 | }, 69 | ]), 70 | ).toEqual({ 71 | baseBytes: 50, 72 | bytes: 50, 73 | metafile: "(Total)", 74 | outfile: "-", 75 | remark: "no-change", 76 | tree: undefined, 77 | }); 78 | }); 79 | test("given added, return increased", () => { 80 | expect( 81 | calculateTotalRow([ 82 | { 83 | baseBytes: 0, 84 | bytes: 50, 85 | metafile: "", 86 | outfile: "", 87 | remark: "added", 88 | tree: undefined, 89 | }, 90 | ]), 91 | ).toEqual({ 92 | baseBytes: 0, 93 | bytes: 50, 94 | metafile: "(Total)", 95 | outfile: "-", 96 | remark: "increased", 97 | tree: undefined, 98 | }); 99 | }); 100 | test("given deleted, return decreased", () => { 101 | expect( 102 | calculateTotalRow([ 103 | { 104 | baseBytes: 50, 105 | bytes: 0, 106 | metafile: "", 107 | outfile: "", 108 | remark: "deleted", 109 | tree: undefined, 110 | }, 111 | ]), 112 | ).toEqual({ 113 | baseBytes: 50, 114 | bytes: 0, 115 | metafile: "(Total)", 116 | outfile: "-", 117 | remark: "decreased", 118 | tree: undefined, 119 | }); 120 | }); 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /action.yaml: -------------------------------------------------------------------------------- 1 | name: esbuild-bundle-analyzer 2 | description: Analyzes each PR's impact on esbuild bundle size 3 | inputs: 4 | name: 5 | required: false 6 | default: ${{ github.event.repository.name }} 7 | description: | 8 | The name of your project. This will be used in the comment header. 9 | metafiles: 10 | required: true 11 | description: | 12 | A comma-separated list of paths to [esbuild's meta file]([https://esbuild.github.io/api/#metafile]). 13 | Must be non-empty. 14 | Glob (`dist/**/meta.json`) is supported. 15 | 16 | As of esbuild v0.20.0, you need to write the meta file by yourself after build, something like this: 17 | 18 | ```javascript 19 | import * as esbuild from 'esbuild' 20 | import fs from 'node:fs' 21 | 22 | let result = await esbuild.build({ 23 | entryPoints: ['src/app1.js', 'src/app2.js'], 24 | bundle: true, 25 | metafile: true, 26 | outdir: 'dist', 27 | }) 28 | 29 | fs.writeFileSync('dist/meta.json', JSON.stringify(result.metafile)) 30 | ``` 31 | 32 | In this case, the `metafiles` config should be `"dist/meta.json"`. 33 | 34 | Typically, you only need one meta file since one meta file can contain multiple out files information. 35 | Multiple meta files may be useful for more complex scenarios. 36 | analyze_directory: 37 | required: false 38 | default: ".analyzer" 39 | description: | 40 | A path to working directory where bundle analysis are stored. 41 | include_extensions: 42 | required: false 43 | default: ".js,.cjs,.mjs" 44 | description: | 45 | A comma-separated list of file extension to be included in the analysis table. 46 | percent_extra_attention: 47 | required: false 48 | default: "20" 49 | description: | 50 | If an out file size has increased more than this percent, display a "‼️" to draw attention 51 | to the change. 52 | include_size_comparison: 53 | required: false 54 | default: "added, deleted, increased, decreased, no-change" 55 | description: | 56 | A comma-separated list of size comparison items to be displayed. 57 | Available filter are `total`, `added`, `deleted`, `increased`, `decreased` and `no-change`. 58 | If you are not interested in some of them, you can remove them from the list. 59 | show_details: 60 | required: false 61 | default: "true" 62 | description: | 63 | If `true`, a collapsed "details" section is rendered. It explains the details of the numbers provided and icons. 64 | show_no_change: 65 | required: false 66 | deprecationMessage: | 67 | Use the `include_size_comparison` list to show/hide `no-change`. 68 | description: | 69 | If `true`, all bundles are shown in the analysis regardless of size change. If `false`, only bundles with size changes are shown. 70 | top_n_largest_paths: 71 | required: false 72 | default: "20" 73 | description: | 74 | The number of largest paths (e.g.) `node_modules/foo`) to be collected. 75 | If 0 or lower, skipped. 76 | 77 | runs: 78 | using: composite 79 | steps: 80 | - name: Download base branch bundle stats 81 | uses: dawidd6/action-download-artifact@v11 82 | # Ok to continue on error since the base branch has no bundle analysis artifact for the first setup 83 | continue-on-error: true 84 | if: success() && github.event.number 85 | with: 86 | branch: ${{ github.event.pull_request.base.ref }} 87 | path: ${{ inputs.analyze_directory }}/base 88 | 89 | - name: Compare with base branch bundle 90 | shell: bash 91 | if: success() 92 | env: 93 | INPUT_METAFILES: ${{ inputs.metafiles }} 94 | INPUT_NAME: ${{ inputs.name }} 95 | INPUT_ANALYZE_DIRECTORY: ${{ inputs.analyze_directory }} 96 | INPUT_INCLUDE_EXTENSIONS: ${{ inputs.include_extensions }} 97 | INPUT_INCLUDE_SIZE_COMPARISON: ${{ inputs.include_size_comparison }} 98 | INPUT_PERCENT_EXTRA_ATTENTION: ${{ inputs.percent_extra_attention }} 99 | INPUT_SHOW_DETAILS: ${{ inputs.show_details }} 100 | INPUT_SHOW_NO_CHANGE: ${{ inputs.show_no_change }} 101 | INPUT_TOP_N_LARGEST_PATHS: ${{ inputs.top_n_largest_paths }} 102 | run: | 103 | node ${{ github.action_path }}/dist/index.mjs 104 | 105 | - name: Upload bundle analysis on default branch 106 | uses: actions/upload-artifact@v5 107 | if: success() && github.ref_name == github.event.repository.default_branch 108 | with: 109 | name: bundle 110 | path: ${{ inputs.analyze_directory }}/bundle_analysis.json 111 | 112 | - name: Find Comment 113 | uses: peter-evans/find-comment@v4 114 | if: success() && github.event.number 115 | id: fc 116 | with: 117 | issue-number: ${{ github.event.number }} 118 | body-includes: '' 119 | 120 | - name: Create Comment 121 | uses: peter-evans/create-or-update-comment@v5 122 | if: success() && github.event.number && steps.fc.outputs.comment-id == 0 123 | with: 124 | issue-number: ${{ github.event.number }} 125 | body-path: ${{ inputs.analyze_directory }}/bundle_analysis_comment.txt 126 | 127 | - name: Update Comment 128 | uses: peter-evans/create-or-update-comment@v5 129 | if: success() && github.event.number && steps.fc.outputs.comment-id != 0 130 | with: 131 | issue-number: ${{ github.event.number }} 132 | body-path: ${{ inputs.analyze_directory }}/bundle_analysis_comment.txt 133 | comment-id: ${{ steps.fc.outputs.comment-id }} 134 | edit-mode: replace 135 | branding: 136 | icon: 'package' 137 | color: 'yellow' 138 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/basic/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as console from "node:console"; 2 | 3 | import { compose, map, filter } from 'rambda' 4 | 5 | const composed = compose( 6 | map((x: number) => x * 2), 7 | filter((x: number) => x > 2) 8 | ); 9 | export const handler = async (event: any) => { 10 | console.log(composed([1, 2, 3, 4])); 11 | console.log([ 12 | { 13 | "_id": "6636e04374026d875e3a14f6", 14 | "index": 0, 15 | "guid": "fbdf37d0-aaad-4b0f-b143-28666927fa56", 16 | "isActive": true, 17 | "balance": "$2,337.22", 18 | "picture": "http://placehold.it/32x32", 19 | "age": 40, 20 | "eyeColor": "blue", 21 | "name": "Moody Duncan", 22 | "gender": "male", 23 | "company": "SULTRAXIN", 24 | "email": "moodyduncan@sultraxin.com", 25 | "phone": "+1 (948) 509-3889", 26 | "address": "100 Clermont Avenue, Glenbrook, Florida, 3611", 27 | "about": "Sunt minim sunt qui irure laborum pariatur nostrud laborum aliquip. Qui quis in laboris eu non enim nisi magna. Excepteur est ipsum nostrud pariatur. Officia esse consectetur est culpa reprehenderit aute et veniam sit.\r\n", 28 | "registered": "2015-02-14T11:49:26 -09:00", 29 | "latitude": 54.979732, 30 | "longitude": 86.310027, 31 | "tags": [ 32 | "irure", 33 | "cupidatat", 34 | "sunt", 35 | "sunt", 36 | "dolore", 37 | "incididunt", 38 | "voluptate" 39 | ], 40 | "friends": [ 41 | { 42 | "id": 0, 43 | "name": "Blanca Blake" 44 | }, 45 | { 46 | "id": 1, 47 | "name": "Stafford Gallagher" 48 | }, 49 | { 50 | "id": 2, 51 | "name": "Odessa Figueroa" 52 | } 53 | ], 54 | "greeting": "Hello, Moody Duncan! You have 9 unread messages.", 55 | "favoriteFruit": "banana" 56 | }, 57 | { 58 | "_id": "6636e0438aa0e021cfd86611", 59 | "index": 2, 60 | "guid": "1d6e9bda-0a31-4864-ad70-2fed16999380", 61 | "isActive": true, 62 | "balance": "$1,910.08", 63 | "picture": "http://placehold.it/32x32", 64 | "age": 39, 65 | "eyeColor": "blue", 66 | "name": "Carole Wyatt", 67 | "gender": "female", 68 | "company": "SNACKTION", 69 | "email": "carolewyatt@snacktion.com", 70 | "phone": "+1 (904) 474-3559", 71 | "address": "383 Nixon Court, Belvoir, Illinois, 7525", 72 | "about": "Non cupidatat culpa duis incididunt mollit. Est irure duis ut quis officia ea sit reprehenderit proident ad nulla. Tempor nisi laborum quis non eu occaecat aliquip. Dolore anim consequat ea laboris do dolore laboris ex dolore officia tempor nisi irure do.\r\n", 73 | "registered": "2017-12-13T02:18:04 -09:00", 74 | "latitude": -19.106743, 75 | "longitude": -15.102964, 76 | "tags": [ 77 | "voluptate", 78 | "cillum", 79 | "ullamco", 80 | "elit", 81 | "eiusmod", 82 | "quis", 83 | "labore" 84 | ], 85 | "friends": [ 86 | { 87 | "id": 0, 88 | "name": "Patterson Callahan" 89 | }, 90 | { 91 | "id": 1, 92 | "name": "Tanisha Key" 93 | }, 94 | { 95 | "id": 2, 96 | "name": "Kathie Carroll" 97 | } 98 | ], 99 | "greeting": "Hello, Carole Wyatt! You have 1 unread messages.", 100 | "favoriteFruit": "banana" 101 | }, 102 | { 103 | "_id": "6636e04313862a26fc0407fb", 104 | "index": 4, 105 | "guid": "dacacda2-4a4a-496d-9b5a-fabd5c0c600d", 106 | "isActive": true, 107 | "balance": "$2,754.82", 108 | "picture": "http://placehold.it/32x32", 109 | "age": 30, 110 | "eyeColor": "brown", 111 | "name": "Castro Stevenson", 112 | "gender": "male", 113 | "company": "PANZENT", 114 | "email": "castrostevenson@panzent.com", 115 | "phone": "+1 (972) 542-2441", 116 | "address": "516 Aster Court, Taycheedah, North Dakota, 9377", 117 | "about": "Et nostrud tempor id et occaecat commodo ad fugiat enim magna velit qui ex nostrud. Minim commodo in voluptate consequat duis occaecat velit consequat laboris et. Sunt sit ullamco dolor enim dolore esse deserunt voluptate eiusmod aliqua. Aliqua dolor consequat consequat et commodo aute amet cillum proident velit magna. Labore esse officia proident et ullamco nulla. Dolore proident velit labore ad cupidatat. Adipisicing velit aliqua velit cillum excepteur ullamco eiusmod laborum.\r\n", 118 | "registered": "2023-04-25T09:50:40 -09:00", 119 | "latitude": 3.775112, 120 | "longitude": -170.248202, 121 | "tags": [ 122 | "dolore", 123 | "in", 124 | "id", 125 | "id", 126 | "laboris", 127 | "in", 128 | "consequat" 129 | ], 130 | "friends": [ 131 | { 132 | "id": 0, 133 | "name": "Elaine Roach" 134 | }, 135 | { 136 | "id": 1, 137 | "name": "Allyson Whitaker" 138 | }, 139 | { 140 | "id": 2, 141 | "name": "Juanita Solomon" 142 | } 143 | ], 144 | "greeting": "Hello, Castro Stevenson! You have 10 unread messages.", 145 | "favoriteFruit": "apple" 146 | }, 147 | { 148 | "_id": "6636e043240e1670126a03b6", 149 | "index": 5, 150 | "guid": "3a20ab87-df6c-49a7-ab44-dd299439d733", 151 | "isActive": false, 152 | "balance": "$3,457.86", 153 | "picture": "http://placehold.it/32x32", 154 | "age": 35, 155 | "eyeColor": "brown", 156 | "name": "Suarez Nunez", 157 | "gender": "male", 158 | "company": "INRT", 159 | "email": "suareznunez@inrt.com", 160 | "phone": "+1 (984) 555-3946", 161 | "address": "874 Cumberland Walk, Dunbar, Minnesota, 8376", 162 | "about": "Cupidatat cillum enim velit mollit non cupidatat ullamco ex voluptate est officia. Ea id voluptate excepteur irure deserunt magna sit ipsum nulla. Id in cillum anim minim duis laboris proident. Excepteur cupidatat ea adipisicing consectetur in non voluptate eu. Reprehenderit anim consequat eu pariatur consectetur aliqua ipsum ad pariatur proident minim aliqua amet ullamco. Officia magna anim minim esse mollit minim deserunt.\r\n", 163 | "registered": "2021-09-17T08:54:45 -09:00", 164 | "latitude": 16.307274, 165 | "longitude": 121.547327, 166 | "tags": [ 167 | "amet", 168 | "irure", 169 | "ipsum", 170 | "cillum", 171 | "ad", 172 | "eu", 173 | "aliqua" 174 | ], 175 | "friends": [ 176 | { 177 | "id": 0, 178 | "name": "Robbie Mckenzie" 179 | }, 180 | { 181 | "id": 1, 182 | "name": "Bryant Barry" 183 | }, 184 | { 185 | "id": 2, 186 | "name": "Eve Hatfield" 187 | } 188 | ], 189 | "greeting": "Hello, Suarez Nunez! You have 6 unread messages.", 190 | "favoriteFruit": "strawberry" 191 | } 192 | ]) 193 | return { 194 | 195 | }; 196 | }; 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esbuild-bundle-analyzer GitHub Action 2 | 3 | [![test](https://github.com/exoego/esbuild-bundle-analyzer/actions/workflows/ci.yml/badge.svg)](https://github.com/exoego/esbuild-bundle-analyzer/actions/workflows/ci.yml) 4 | [![codecov](https://codecov.io/github/exoego/esbuild-bundle-analyzer/graph/badge.svg?token=DzLqKXHJNC)](https://codecov.io/github/exoego/esbuild-bundle-analyzer) 5 | 6 | Analyzes each PR's impact on esbuild bundle size 7 | 8 | ![comment-example](/doc/comment.png "Comment Example") 9 | 10 | ## Usage 11 | 12 | ### GitHub Action setup for 🔐private repositories 13 | 14 | ```yaml 15 | name: esbuild-bundle-analyzer 16 | 17 | on: 18 | push: 19 | branches: [main] 20 | pull_request: 21 | branches: [main] 22 | 23 | jobs: 24 | analyze: 25 | runs-on: ubuntu-latest 26 | timeout-minutes: 5 27 | permissions: 28 | contents: read # for checkout repository 29 | actions: read # for fetching base branch bundle stats 30 | pull-requests: write # for comments 31 | steps: 32 | # Ensure you build your project before running this action 33 | # For example, 34 | - uses: actions/checkout@v4 35 | - uses: actions/setup-node@v4 36 | with: 37 | node-version: 20 38 | cache: 'npm' 39 | cache-dependency-path: subdir/package-lock.json 40 | - run: npm ci 41 | - name: Run esbuild 42 | run: npm run build 43 | 44 | # Call this action after the build 45 | - name: Analyze esbuild bundle size 46 | # uses: exoego/esbuild-bundle-analyzer@main # If you prefer nightly! 47 | uses: exoego/esbuild-bundle-analyzer@v1 48 | with: 49 | metafiles: "out/meta.json" 50 | ``` 51 | 52 | ### GitHub Action setup for public repositories 53 | 54 | If your repository is public and you want to run this action on PRs from forks, you may need to use `pull_request_target` event. 55 | By using `pull_request_target` event, GitHub grant GitHub Actions to modify pull requests even on PRs from forks. 56 | 57 | > [!WARNING] 58 | > Please refer [Permissions for the GITHUB_TOKEN](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token) and use this carefully. 59 | 60 | ```yaml 61 | name: esbuild-bundle-analyzer 62 | 63 | on: 64 | push: 65 | branches: [main] 66 | pull_request_target: 67 | branches: [main] 68 | 69 | jobs: 70 | # PLEASE AVOID ADDING OTHER JOBS IN THIS FILE 71 | # BECAUSE THIS ACTION USE `pull_request_target` EVENT that grants write permissions to GitHub Actions running on PRs from forks. 72 | analyze: 73 | runs-on: ubuntu-latest 74 | timeout-minutes: 5 75 | permissions: 76 | contents: read # for checkout repository 77 | actions: read # for fetching base branch bundle stats 78 | pull-requests: write # for comments 79 | steps: 80 | # Ensure you build your project before running this action 81 | # For example, 82 | - uses: actions/checkout@v4 83 | with: 84 | # This is required to fetch the commit SHA of the forked PR 85 | ref: "${{ github.event.pull_request.merge_commit_sha }}" 86 | - uses: actions/setup-node@v4 87 | with: 88 | node-version: 20 89 | cache: 'npm' 90 | cache-dependency-path: subdir/package-lock.json 91 | - run: npm ci 92 | - name: Run esbuild 93 | run: npm run build 94 | 95 | # Call this action after the build 96 | - name: Analyze esbuild bundle size 97 | # uses: exoego/esbuild-bundle-analyzer@main # If you prefer nightly! 98 | uses: exoego/esbuild-bundle-analyzer@v1 99 | with: 100 | metafiles: "out/meta.json" 101 | ``` 102 | 103 | ### esbuild setup 104 | 105 | You need to [write ***meta file*** yourself after build](https://esbuild.github.io/api/#metafile). 106 | 107 | If you use esbuild CLI, your build script in package.json should have `--metafile=out/meta.json` or such, something like this: 108 | 109 | ```json5 110 | { 111 | "scripts": { 112 | "build": "esbuild ./src/lambda.ts --bundle --metafile=out/meta.json ......." 113 | }, 114 | // ... 115 | } 116 | ``` 117 | 118 | If you use esbuild API, something like this: 119 | 120 | ```javascript 121 | // esbuild.mjs 122 | import * as esbuild from 'esbuild' 123 | import fs from 'node:fs' 124 | 125 | let result = await esbuild.build({ 126 | entryPoints: ['src/app1.ts', 'src/app2.ts'], 127 | bundle: true, 128 | metafile: true, 129 | outdir: 'dist', 130 | }) 131 | 132 | fs.writeFileSync('dist/meta.json', JSON.stringify(result.metafile)) 133 | ``` 134 | 135 | For both cases, the `metafiles` input for GitHub Action will be `"dist/meta.json"`. 136 | 137 | If you have multiple meta files, you can specify them like this `"dist/meta1.json,dist/meta2.json"` or `"dist/meta*.json`. 138 | 139 | ## Permissions 140 | 141 | This action requires the following permissions: 142 | 143 | ```yaml 144 | permissions: 145 | contents: read # for checkout repository 146 | actions: read # for fetching base branch bundle stats 147 | pull-requests: write # for comments 148 | ``` 149 | 150 | This action uses the `GITHUB_TOKEN` provided by GitHub Actions. 151 | Due to security limitation, `GITHUB_TOKEN` is not granted to write comments on PRs from forks on `pull_request` event. 152 | Instead, [`pull_request_target` event should be used on PRs from forks to overcome this limitation](https://docs.github.com/en/actions/security-guides/automatic-token-authentication). 153 | Please check the above setup example to use this action with `pull_request_target`. 154 | 155 | ## Action inputs 156 | 157 | | Name | Default | Description | 158 | |---------------------------|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------| 159 | | `metafiles` | - | A required comma-separated list of paths to [esbuild's meta file]([https://esbuild.github.io/api/#metafile]). Glob (`dist/**/meta.json`) is supported. | 160 | | `name` | ${{ github.event.
repository.name }} | The name of your project. This will be used in the comment header. | 161 | | `analyze_directory` | `.analyzer` | A path to working directory where bundle analysis are stored. | 162 | | `include_extensions` | `.js,.cjs,.mjs` | A comma-separated list of file extension to be included in the analysis table. | 163 | | `percent_extra_attention` | `20` | If an out file size has increased more than this percent, display a "‼️" to draw extra attention to the change. | 164 | | `include_size_comparison` | `added, deleted, increased, decreased, no-change` | A comma-separated list of size comparison items to be displayed.
Available filter are `total`, `added`, `deleted`, `increased`, `decreased` and `no-change`.
If you are not interested in some of them, you can remove them from the list. | 165 | | `show_details` | `true` | If `true`, a collapsed "details" section is rendered. It explains the details of the numbers provided and icons. | 166 | | `show_no_change` | `true` | [DEPRECATED] Use the `include_size_comparison` list to show/hide `no-change`.
If `true`, all bundles are shown in the analysis regardless of size change. If `false`, only bundles with size changes are shown. | 167 | | `top_n_largest_paths` | `20` | The number of largest paths (e.g.) `node_modules/foo`) to be collected. If 0 or lower, skipped. | 168 | 169 | ## Action outputs 170 | 171 | No outputs are provided by this action at this time. 172 | 173 | ## Acknowledgements 174 | 175 | - Highly inspired by [hashicorp/nextjs-bundle-analysis](https://github.com/hashicorp/nextjs-bundle-analysis) 176 | 177 | ## License 178 | 179 | [MIT](LICENSE) 180 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/basic/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-bundle-analysis-test-fixture", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "esbuild-bundle-analysis-test-fixture", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "rambda": "^9.2.0" 12 | }, 13 | "devDependencies": { 14 | "esbuild": "^0.20.2", 15 | "typescript": "^5.4.5" 16 | } 17 | }, 18 | "node_modules/@esbuild/aix-ppc64": { 19 | "version": "0.20.2", 20 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", 21 | "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", 22 | "cpu": [ 23 | "ppc64" 24 | ], 25 | "dev": true, 26 | "optional": true, 27 | "os": [ 28 | "aix" 29 | ], 30 | "engines": { 31 | "node": ">=12" 32 | } 33 | }, 34 | "node_modules/@esbuild/android-arm": { 35 | "version": "0.20.2", 36 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", 37 | "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", 38 | "cpu": [ 39 | "arm" 40 | ], 41 | "dev": true, 42 | "optional": true, 43 | "os": [ 44 | "android" 45 | ], 46 | "engines": { 47 | "node": ">=12" 48 | } 49 | }, 50 | "node_modules/@esbuild/android-arm64": { 51 | "version": "0.20.2", 52 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", 53 | "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", 54 | "cpu": [ 55 | "arm64" 56 | ], 57 | "dev": true, 58 | "optional": true, 59 | "os": [ 60 | "android" 61 | ], 62 | "engines": { 63 | "node": ">=12" 64 | } 65 | }, 66 | "node_modules/@esbuild/android-x64": { 67 | "version": "0.20.2", 68 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", 69 | "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", 70 | "cpu": [ 71 | "x64" 72 | ], 73 | "dev": true, 74 | "optional": true, 75 | "os": [ 76 | "android" 77 | ], 78 | "engines": { 79 | "node": ">=12" 80 | } 81 | }, 82 | "node_modules/@esbuild/darwin-arm64": { 83 | "version": "0.20.2", 84 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", 85 | "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", 86 | "cpu": [ 87 | "arm64" 88 | ], 89 | "dev": true, 90 | "optional": true, 91 | "os": [ 92 | "darwin" 93 | ], 94 | "engines": { 95 | "node": ">=12" 96 | } 97 | }, 98 | "node_modules/@esbuild/darwin-x64": { 99 | "version": "0.20.2", 100 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", 101 | "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", 102 | "cpu": [ 103 | "x64" 104 | ], 105 | "dev": true, 106 | "optional": true, 107 | "os": [ 108 | "darwin" 109 | ], 110 | "engines": { 111 | "node": ">=12" 112 | } 113 | }, 114 | "node_modules/@esbuild/freebsd-arm64": { 115 | "version": "0.20.2", 116 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", 117 | "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", 118 | "cpu": [ 119 | "arm64" 120 | ], 121 | "dev": true, 122 | "optional": true, 123 | "os": [ 124 | "freebsd" 125 | ], 126 | "engines": { 127 | "node": ">=12" 128 | } 129 | }, 130 | "node_modules/@esbuild/freebsd-x64": { 131 | "version": "0.20.2", 132 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", 133 | "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", 134 | "cpu": [ 135 | "x64" 136 | ], 137 | "dev": true, 138 | "optional": true, 139 | "os": [ 140 | "freebsd" 141 | ], 142 | "engines": { 143 | "node": ">=12" 144 | } 145 | }, 146 | "node_modules/@esbuild/linux-arm": { 147 | "version": "0.20.2", 148 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", 149 | "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", 150 | "cpu": [ 151 | "arm" 152 | ], 153 | "dev": true, 154 | "optional": true, 155 | "os": [ 156 | "linux" 157 | ], 158 | "engines": { 159 | "node": ">=12" 160 | } 161 | }, 162 | "node_modules/@esbuild/linux-arm64": { 163 | "version": "0.20.2", 164 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", 165 | "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", 166 | "cpu": [ 167 | "arm64" 168 | ], 169 | "dev": true, 170 | "optional": true, 171 | "os": [ 172 | "linux" 173 | ], 174 | "engines": { 175 | "node": ">=12" 176 | } 177 | }, 178 | "node_modules/@esbuild/linux-ia32": { 179 | "version": "0.20.2", 180 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", 181 | "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", 182 | "cpu": [ 183 | "ia32" 184 | ], 185 | "dev": true, 186 | "optional": true, 187 | "os": [ 188 | "linux" 189 | ], 190 | "engines": { 191 | "node": ">=12" 192 | } 193 | }, 194 | "node_modules/@esbuild/linux-loong64": { 195 | "version": "0.20.2", 196 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", 197 | "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", 198 | "cpu": [ 199 | "loong64" 200 | ], 201 | "dev": true, 202 | "optional": true, 203 | "os": [ 204 | "linux" 205 | ], 206 | "engines": { 207 | "node": ">=12" 208 | } 209 | }, 210 | "node_modules/@esbuild/linux-mips64el": { 211 | "version": "0.20.2", 212 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", 213 | "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", 214 | "cpu": [ 215 | "mips64el" 216 | ], 217 | "dev": true, 218 | "optional": true, 219 | "os": [ 220 | "linux" 221 | ], 222 | "engines": { 223 | "node": ">=12" 224 | } 225 | }, 226 | "node_modules/@esbuild/linux-ppc64": { 227 | "version": "0.20.2", 228 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", 229 | "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", 230 | "cpu": [ 231 | "ppc64" 232 | ], 233 | "dev": true, 234 | "optional": true, 235 | "os": [ 236 | "linux" 237 | ], 238 | "engines": { 239 | "node": ">=12" 240 | } 241 | }, 242 | "node_modules/@esbuild/linux-riscv64": { 243 | "version": "0.20.2", 244 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", 245 | "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", 246 | "cpu": [ 247 | "riscv64" 248 | ], 249 | "dev": true, 250 | "optional": true, 251 | "os": [ 252 | "linux" 253 | ], 254 | "engines": { 255 | "node": ">=12" 256 | } 257 | }, 258 | "node_modules/@esbuild/linux-s390x": { 259 | "version": "0.20.2", 260 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", 261 | "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", 262 | "cpu": [ 263 | "s390x" 264 | ], 265 | "dev": true, 266 | "optional": true, 267 | "os": [ 268 | "linux" 269 | ], 270 | "engines": { 271 | "node": ">=12" 272 | } 273 | }, 274 | "node_modules/@esbuild/linux-x64": { 275 | "version": "0.20.2", 276 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", 277 | "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", 278 | "cpu": [ 279 | "x64" 280 | ], 281 | "dev": true, 282 | "optional": true, 283 | "os": [ 284 | "linux" 285 | ], 286 | "engines": { 287 | "node": ">=12" 288 | } 289 | }, 290 | "node_modules/@esbuild/netbsd-x64": { 291 | "version": "0.20.2", 292 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", 293 | "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", 294 | "cpu": [ 295 | "x64" 296 | ], 297 | "dev": true, 298 | "optional": true, 299 | "os": [ 300 | "netbsd" 301 | ], 302 | "engines": { 303 | "node": ">=12" 304 | } 305 | }, 306 | "node_modules/@esbuild/openbsd-x64": { 307 | "version": "0.20.2", 308 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", 309 | "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", 310 | "cpu": [ 311 | "x64" 312 | ], 313 | "dev": true, 314 | "optional": true, 315 | "os": [ 316 | "openbsd" 317 | ], 318 | "engines": { 319 | "node": ">=12" 320 | } 321 | }, 322 | "node_modules/@esbuild/sunos-x64": { 323 | "version": "0.20.2", 324 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", 325 | "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", 326 | "cpu": [ 327 | "x64" 328 | ], 329 | "dev": true, 330 | "optional": true, 331 | "os": [ 332 | "sunos" 333 | ], 334 | "engines": { 335 | "node": ">=12" 336 | } 337 | }, 338 | "node_modules/@esbuild/win32-arm64": { 339 | "version": "0.20.2", 340 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", 341 | "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", 342 | "cpu": [ 343 | "arm64" 344 | ], 345 | "dev": true, 346 | "optional": true, 347 | "os": [ 348 | "win32" 349 | ], 350 | "engines": { 351 | "node": ">=12" 352 | } 353 | }, 354 | "node_modules/@esbuild/win32-ia32": { 355 | "version": "0.20.2", 356 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", 357 | "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", 358 | "cpu": [ 359 | "ia32" 360 | ], 361 | "dev": true, 362 | "optional": true, 363 | "os": [ 364 | "win32" 365 | ], 366 | "engines": { 367 | "node": ">=12" 368 | } 369 | }, 370 | "node_modules/@esbuild/win32-x64": { 371 | "version": "0.20.2", 372 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", 373 | "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", 374 | "cpu": [ 375 | "x64" 376 | ], 377 | "dev": true, 378 | "optional": true, 379 | "os": [ 380 | "win32" 381 | ], 382 | "engines": { 383 | "node": ">=12" 384 | } 385 | }, 386 | "node_modules/esbuild": { 387 | "version": "0.20.2", 388 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", 389 | "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", 390 | "dev": true, 391 | "hasInstallScript": true, 392 | "bin": { 393 | "esbuild": "bin/esbuild" 394 | }, 395 | "engines": { 396 | "node": ">=12" 397 | }, 398 | "optionalDependencies": { 399 | "@esbuild/aix-ppc64": "0.20.2", 400 | "@esbuild/android-arm": "0.20.2", 401 | "@esbuild/android-arm64": "0.20.2", 402 | "@esbuild/android-x64": "0.20.2", 403 | "@esbuild/darwin-arm64": "0.20.2", 404 | "@esbuild/darwin-x64": "0.20.2", 405 | "@esbuild/freebsd-arm64": "0.20.2", 406 | "@esbuild/freebsd-x64": "0.20.2", 407 | "@esbuild/linux-arm": "0.20.2", 408 | "@esbuild/linux-arm64": "0.20.2", 409 | "@esbuild/linux-ia32": "0.20.2", 410 | "@esbuild/linux-loong64": "0.20.2", 411 | "@esbuild/linux-mips64el": "0.20.2", 412 | "@esbuild/linux-ppc64": "0.20.2", 413 | "@esbuild/linux-riscv64": "0.20.2", 414 | "@esbuild/linux-s390x": "0.20.2", 415 | "@esbuild/linux-x64": "0.20.2", 416 | "@esbuild/netbsd-x64": "0.20.2", 417 | "@esbuild/openbsd-x64": "0.20.2", 418 | "@esbuild/sunos-x64": "0.20.2", 419 | "@esbuild/win32-arm64": "0.20.2", 420 | "@esbuild/win32-ia32": "0.20.2", 421 | "@esbuild/win32-x64": "0.20.2" 422 | } 423 | }, 424 | "node_modules/rambda": { 425 | "version": "9.2.0", 426 | "resolved": "https://registry.npmjs.org/rambda/-/rambda-9.2.0.tgz", 427 | "integrity": "sha512-RjM8TBNPR+iSvWLqbBpFveDfEf2RPRKHuwBHjQdXsYFDwn3MIvgmJiqVVC1CIQKnOwzeDQd44zqDFgSKQ7RT1Q==" 428 | }, 429 | "node_modules/typescript": { 430 | "version": "5.4.5", 431 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", 432 | "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", 433 | "dev": true, 434 | "bin": { 435 | "tsc": "bin/tsc", 436 | "tsserver": "bin/tsserver" 437 | }, 438 | "engines": { 439 | "node": ">=14.17" 440 | } 441 | } 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /src/compare.ts: -------------------------------------------------------------------------------- 1 | import * as console from "node:console"; 2 | import fs from "node:fs"; 3 | import path from "node:path"; 4 | import { findMetafiles } from "./report"; 5 | import type { 6 | CompareResult, 7 | Input, 8 | Report, 9 | SizeComparisonFilter, 10 | TreeMapNode, 11 | } from "./types"; 12 | import { loadAnalysisJson, loadMetaFile } from "./utils"; 13 | 14 | export function compare(input: Input): void { 15 | let hasAnyChange = false; 16 | let output = `## 📦 esbuild Bundle Analysis for ${input.name} 17 | 18 | This analysis was generated by [esbuild-bundle-analyzer](https://github.com/exoego/esbuild-bundle-analyzer). 🤖 19 | `; 20 | 21 | const currentAnalysisPath = path.join( 22 | process.cwd(), 23 | input.analyzerDirectory, 24 | "bundle_analysis.json", 25 | ); 26 | const current = loadAnalysisJson(currentAnalysisPath); 27 | console.log(`Current analysis found in ${currentAnalysisPath}`, current); 28 | const base = loadBaseAnalysisJson(input); 29 | 30 | const fileTree = buildFileTree(input); 31 | 32 | const allOutFiles: string[] = [ 33 | ...new Set([...Object.keys(current), ...Object.keys(base)]), 34 | ].sort(); 35 | 36 | const comparison: Array = allOutFiles.map((outfile) => { 37 | const currentStats = current[outfile]; 38 | const baseStats = base[outfile]; 39 | console.log("Comparing", outfile, currentStats, baseStats); 40 | 41 | if (!currentStats) { 42 | console.log("Deleted file", outfile); 43 | hasAnyChange = true; 44 | return { 45 | ...baseStats, 46 | baseBytes: 0, 47 | remark: "deleted", 48 | tree: undefined, 49 | }; 50 | } 51 | 52 | const tree = fileTree.get( 53 | treeKey(currentStats.metafile, currentStats.outfile), 54 | ); 55 | if (!baseStats) { 56 | console.log("New file", outfile); 57 | hasAnyChange = true; 58 | return { ...currentStats, baseBytes: 0, remark: "added", tree }; 59 | } 60 | 61 | const diff = currentStats.bytes - baseStats.bytes; 62 | if (diff !== 0) { 63 | console.log("Changed file", outfile, diff); 64 | hasAnyChange = true; 65 | } else { 66 | console.log("No change", outfile); 67 | } 68 | return { 69 | ...currentStats, 70 | baseBytes: baseStats.bytes, 71 | tree, 72 | remark: 73 | diff === 0 ? "no-change" : Math.sign(diff) ? "increased" : "decreased", 74 | }; 75 | }); 76 | console.log("Comparison done.", comparison); 77 | 78 | if (hasAnyChange) { 79 | output += markdownTable( 80 | comparison, 81 | input.includeSizeComparison, 82 | input.percentExtraAttention, 83 | ); 84 | output += hiddenTable( 85 | comparison, 86 | input.includeSizeComparison, 87 | input.percentExtraAttention, 88 | ); 89 | output += fileSizeTable(comparison, input.topNLargestPaths); 90 | output += detail(input); 91 | } else { 92 | output += "This PR introduced no changes to the esbuild bundle! 🙌"; 93 | } 94 | 95 | // we add this tag so that our action can be able to easily and 96 | // consistently find the right comment to edit as more commits are pushed. 97 | // Tag is added to the top of the comment to avoid the truncation. 98 | output = ` 99 | ${output}`; 100 | 101 | writeComment(input, output); 102 | } 103 | 104 | function treeKey(metafile: string, outfile: string): string { 105 | return `${metafile} -> ${outfile}`; 106 | } 107 | 108 | // Write the output to a file which is later read in 109 | // as comment contents by the actions workflow. 110 | function writeComment(input: Input, output: string): void { 111 | console.log("Writing comment to file.", output); 112 | fs.mkdirSync(path.join(process.cwd(), input.analyzerDirectory), { 113 | recursive: true, 114 | }); 115 | fs.writeFileSync( 116 | path.join( 117 | process.cwd(), 118 | input.analyzerDirectory, 119 | "bundle_analysis_comment.txt", 120 | ), 121 | output.trim(), 122 | ); 123 | } 124 | 125 | function detail(input: Input): string { 126 | if (!input.showDetails) { 127 | return ""; 128 | } 129 | return `\n
130 | Details 131 |

Next to the size is how much the size has increased or decreased compared with the base branch of this PR.

132 |
    133 |
  • ‼️: Size increased by ${input.percentExtraAttention}% or more. Special attention should be given to this.
  • 134 |
  • ⚠️: Size increased in acceptable range (lower than ${input.percentExtraAttention}%).
  • 135 |
  • ✅: No change or even downsized.
  • 136 |
  • 🗑️: The out file is deleted: not found in base branch.
  • 137 |
  • 🆕: The out file is newly found: will be added to base branch.
  • 138 |
139 |
\n`; 140 | } 141 | 142 | function loadBaseAnalysisJson(input: Input): Report { 143 | try { 144 | const baseAnalysisPath = path.join( 145 | process.cwd(), 146 | input.analyzerDirectory, 147 | "base/bundle/bundle_analysis.json", 148 | ); 149 | const report = loadAnalysisJson(baseAnalysisPath); 150 | console.info(`Base analysis found in ${baseAnalysisPath}`, report); 151 | return report; 152 | } catch (e) { 153 | console.warn( 154 | "No base analysis found. First setup or all artifacts are expired.", 155 | ); 156 | return {}; 157 | } 158 | } 159 | 160 | function buildFileTree(input: Input) { 161 | function buildRoot( 162 | input: Record, 163 | ): TreeMapNode { 164 | const root: TreeMapNode = { name: "", path: "", value: 0, children: [] }; 165 | for (const [filePath, { bytesInOutput }] of Object.entries(input)) { 166 | const directories = filePath.split("/"); 167 | buildNode(root, directories, bytesInOutput); 168 | } 169 | return root; 170 | } 171 | 172 | function buildNode( 173 | node: TreeMapNode, 174 | paths: Array, 175 | value: number, 176 | ): void { 177 | const first = paths.shift(); 178 | if (first === undefined) { 179 | // leaf node (file) 180 | node.value += value; 181 | return; 182 | } 183 | let child = node.children.find((child) => child.name === first); 184 | if (!child) { 185 | child = { 186 | name: first, 187 | path: `${node.path}/${first}`.replace(/^\//, ""), 188 | value: 0, 189 | children: [], 190 | }; 191 | node.children.push(child); 192 | } 193 | node.value += value; 194 | buildNode(child, paths, value); 195 | } 196 | 197 | const trees = new Map(); 198 | if (input.topNLargestPaths <= 0) { 199 | // Skip building tree if we don't need it. 200 | return trees; 201 | } 202 | for (const { relativePath, absolutePath } of findMetafiles(input)) { 203 | const metafileJson = loadMetaFile(absolutePath); 204 | for (const [outfile, buildMeta] of Object.entries(metafileJson.outputs)) { 205 | const tree = buildRoot(buildMeta.inputs); 206 | trees.set(treeKey(relativePath, outfile), tree); 207 | 208 | fs.writeFileSync( 209 | path.join(process.cwd(), input.analyzerDirectory, "tree.json"), 210 | JSON.stringify(tree, null, 2), 211 | ); 212 | } 213 | } 214 | return trees; 215 | } 216 | 217 | const spacer = " "; 218 | 219 | function filesize(bytes: number): string { 220 | const sign = bytes < 0 ? "-" : ""; 221 | const n = Math.abs(bytes); 222 | if (n < 1000) { 223 | return `${sign}${n}${spacer}B`; 224 | } 225 | if (n < 1000 * 1000) { 226 | return `${sign}${(n / 1000).toFixed(2)}${spacer}KB`; 227 | } 228 | if (n < 1000 * 1000 * 1000) { 229 | return `${sign}${(n / 1000 / 1000).toFixed(2)}${spacer}MB`; 230 | } 231 | if (n < 1000 * 1000 * 1000 * 1000) { 232 | return `${sign}${(n / 1000 / 1000 / 1000).toFixed(2)}${spacer}GB`; 233 | } 234 | throw new Error("Too large file size!! Are you sure?"); 235 | } 236 | 237 | export function calculateTotalRow(data: Array): CompareResult { 238 | const totalRow = data.reduce( 239 | (acc, d) => { 240 | const { bytes, baseBytes, ...rest } = acc; 241 | return { 242 | ...rest, 243 | baseBytes: baseBytes + d.baseBytes, 244 | bytes: bytes + d.bytes, 245 | }; 246 | }, 247 | { 248 | baseBytes: 0, 249 | bytes: 0, 250 | metafile: "(Total)", 251 | outfile: "-", 252 | remark: "added", 253 | tree: undefined, 254 | }, 255 | ); 256 | totalRow.remark = 257 | totalRow.bytes === totalRow.baseBytes 258 | ? "no-change" 259 | : totalRow.bytes > totalRow.baseBytes 260 | ? "increased" 261 | : "decreased"; 262 | return totalRow; 263 | } 264 | 265 | function markdownTable( 266 | data: Array, 267 | sizeComparisonFilters: Set, 268 | redThreshold: number, 269 | ): string { 270 | const totalRows: Array = sizeComparisonFilters.has("total") 271 | ? [calculateTotalRow(data)] 272 | : []; 273 | const individualRows = data.filter((d) => 274 | sizeComparisonFilters.has(d.remark), 275 | ); 276 | const rows = [...totalRows, ...individualRows] 277 | .map((d) => { 278 | return `${d.metafile} | ${d.outfile} | ${renderSize(d)} | ${renderNote( 279 | d, 280 | redThreshold, 281 | )}\n`; 282 | }) 283 | .join(""); 284 | 285 | return ` 286 | Meta File | Out File | Size (raw) | Note 287 | ----------|----------|-----------:|------ 288 | ${rows}`; 289 | } 290 | 291 | function hiddenTable( 292 | data: Array, 293 | includeSizeComparison: Set, 294 | redThreshold: number, 295 | ): string { 296 | const hiddenBundles = data.filter( 297 | (d) => !includeSizeComparison.has(d.remark), 298 | ); 299 | const rows = hiddenBundles 300 | .map((d) => { 301 | return `${d.metafile} | ${d.outfile} | ${renderSize(d)} | ${renderNote( 302 | d, 303 | redThreshold, 304 | )}\n`; 305 | }) 306 | .join(""); 307 | if (hiddenBundles.length === 0) { 308 | return ""; 309 | } 310 | return ` 311 |
312 | ${hiddenBundles.length} bundles are hidden since not listed in include_size_comparison. 313 | 314 | Meta File | Out File | Size (raw) | Note 315 | ----------|----------|-----------:|------ 316 | ${rows} 317 | 318 |
319 | `; 320 | } 321 | 322 | /** 323 | * Find the top N largest nodes in root tree. 324 | * Dig nodes until the depth of 3. 325 | */ 326 | function findLargeDirectories(root: TreeMapNode, N: number) { 327 | const nodes: TreeMapNode[] = []; 328 | const queue: Array<{ node: TreeMapNode; depth: number }> = [ 329 | { node: root, depth: 0 }, 330 | ]; 331 | while (queue.length > 0) { 332 | const shift = queue.shift(); 333 | if (!shift) { 334 | break; 335 | } 336 | const { node, depth } = shift; 337 | if (depth === 3) { 338 | nodes.push(node); 339 | continue; 340 | } 341 | if (node.children.length === 0) { 342 | nodes.push(node); 343 | } else { 344 | for (const item of node.children) { 345 | queue.push({ node: item, depth: depth + 1 }); 346 | } 347 | } 348 | } 349 | const largeNodes = nodes.sort((a, b) => b.value - a.value).slice(0, N); 350 | return { 351 | largeNodes, 352 | hasOther: nodes.length > N, 353 | }; 354 | } 355 | 356 | function fixedPercent(n: number, d: number): number { 357 | return Number.parseFloat(((n / d) * 100).toFixed(1)); 358 | } 359 | 360 | function fileSizeTable( 361 | data: Array, 362 | topNLargestPaths: number, 363 | ): string { 364 | if (data.length === 0 || topNLargestPaths <= 0) { 365 | return ""; 366 | } 367 | let output = ""; 368 | output += "
\n"; 369 | output += "Largest paths\n"; 370 | output += `These visualization shows top ${topNLargestPaths} largest paths in the bundle.\n`; 371 | for (const d of data) { 372 | output += "\n"; 373 | output += `## Meta file: ${d.metafile}, Out file: ${d.outfile}\n`; 374 | if (!d.tree) { 375 | output += "️️🗑️Deleted\n"; 376 | continue; 377 | } 378 | output += "| Path | Size |\n"; 379 | output += "|------|-------|\n"; 380 | const totalSize = d.tree.value; 381 | const { largeNodes, hasOther } = findLargeDirectories( 382 | d.tree, 383 | topNLargestPaths, 384 | ); 385 | for (const { path, value } of largeNodes) { 386 | const percent = fixedPercent(value, totalSize); 387 | output += `| ${path} | ${renderBar(percent, value)} |\n`; 388 | } 389 | if (hasOther) { 390 | const otherSize = totalSize - largeNodes[0].value; 391 | const otherPercent = fixedPercent(otherSize, totalSize); 392 | output += `| (other) | ${renderBar(otherPercent, otherSize)} |\n`; 393 | } 394 | } 395 | output += "
\n"; 396 | return output; 397 | } 398 | 399 | function renderBar(percent: number, bytes: number): string { 400 | const bar = progress(percent / 100); 401 | return `\${{\\color{Goldenrod}{ ${bar} }}}\$ ${percent.toFixed( 402 | 1, 403 | )}%, ${filesize(bytes)}`; 404 | } 405 | 406 | // Block progression is 1/8 = 0.125 407 | const blocks = ["", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"]; 408 | const progression = 1 / (blocks.length - 1); 409 | 410 | function progress(value: number, length = 25, vmin = 0.0, vmax = 1.0) { 411 | const v = value * length; 412 | const integerPart = Math.floor(v); 413 | const fractionalPart = v - integerPart; 414 | const i = Math.round( 415 | (progression * Math.floor(fractionalPart / progression)) / progression, 416 | ); 417 | return "█".repeat(integerPart) + blocks[i]; 418 | } 419 | 420 | function renderSize(d: CompareResult): string { 421 | return filesize(d.bytes); 422 | } 423 | 424 | function renderNote(d: CompareResult, redThreshold: number): string { 425 | if (d.remark === "deleted") { 426 | return "🗑️ Deleted"; 427 | } 428 | if (d.remark === "added") { 429 | return "🆕 Added"; 430 | } 431 | if (d.remark === "no-change") { 432 | return "✅ No change"; 433 | } 434 | if (d.baseBytes === 0) { 435 | // Total row will fall here when all bundles are swapped (deleted and added). 436 | // See https://github.com/exoego/esbuild-bundle-analyzer/issues/90 437 | return "✅ No change"; 438 | } 439 | const diff = d.bytes - d.baseBytes; 440 | const percentChange = (diff / d.baseBytes) * 100; 441 | return `${renderStatusIndicator(percentChange, redThreshold)}${filesize( 442 | diff, 443 | )} (${sign(percentChange)}${percentChange.toFixed(1)}%)`; 444 | } 445 | 446 | function sign(num: number): string { 447 | return num < 0 ? "" : "+"; 448 | } 449 | 450 | function renderStatusIndicator( 451 | percentChange: number, 452 | redThreshold: number, 453 | ): string { 454 | let res: string; 455 | if (percentChange > 0 && percentChange < redThreshold) { 456 | res = "⚠️"; 457 | } else if (percentChange >= redThreshold) { 458 | res = "‼️"; 459 | } else { 460 | res = "✅ "; 461 | } 462 | return `${res} ${sign(percentChange)}`; 463 | } 464 | -------------------------------------------------------------------------------- /__tests__/__fixtures__/examples/multi-outputs/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esbuild-bundle-analysis-test-fixture", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "esbuild-bundle-analysis-test-fixture", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "@aws-sdk/client-s3": "3.556.0" 12 | }, 13 | "devDependencies": { 14 | "esbuild": "0.20.2", 15 | "typescript": "5.4.5" 16 | } 17 | }, 18 | "node_modules/@aws-crypto/crc32": { 19 | "version": "3.0.0", 20 | "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", 21 | "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", 22 | "dependencies": { 23 | "@aws-crypto/util": "^3.0.0", 24 | "@aws-sdk/types": "^3.222.0", 25 | "tslib": "^1.11.1" 26 | } 27 | }, 28 | "node_modules/@aws-crypto/crc32/node_modules/tslib": { 29 | "version": "1.14.1", 30 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 31 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 32 | }, 33 | "node_modules/@aws-crypto/crc32c": { 34 | "version": "3.0.0", 35 | "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-3.0.0.tgz", 36 | "integrity": "sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==", 37 | "dependencies": { 38 | "@aws-crypto/util": "^3.0.0", 39 | "@aws-sdk/types": "^3.222.0", 40 | "tslib": "^1.11.1" 41 | } 42 | }, 43 | "node_modules/@aws-crypto/crc32c/node_modules/tslib": { 44 | "version": "1.14.1", 45 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 46 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 47 | }, 48 | "node_modules/@aws-crypto/ie11-detection": { 49 | "version": "3.0.0", 50 | "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", 51 | "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", 52 | "dependencies": { 53 | "tslib": "^1.11.1" 54 | } 55 | }, 56 | "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { 57 | "version": "1.14.1", 58 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 59 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 60 | }, 61 | "node_modules/@aws-crypto/sha1-browser": { 62 | "version": "3.0.0", 63 | "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-3.0.0.tgz", 64 | "integrity": "sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==", 65 | "dependencies": { 66 | "@aws-crypto/ie11-detection": "^3.0.0", 67 | "@aws-crypto/supports-web-crypto": "^3.0.0", 68 | "@aws-crypto/util": "^3.0.0", 69 | "@aws-sdk/types": "^3.222.0", 70 | "@aws-sdk/util-locate-window": "^3.0.0", 71 | "@aws-sdk/util-utf8-browser": "^3.0.0", 72 | "tslib": "^1.11.1" 73 | } 74 | }, 75 | "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": { 76 | "version": "1.14.1", 77 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 78 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 79 | }, 80 | "node_modules/@aws-crypto/sha256-browser": { 81 | "version": "3.0.0", 82 | "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", 83 | "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", 84 | "dependencies": { 85 | "@aws-crypto/ie11-detection": "^3.0.0", 86 | "@aws-crypto/sha256-js": "^3.0.0", 87 | "@aws-crypto/supports-web-crypto": "^3.0.0", 88 | "@aws-crypto/util": "^3.0.0", 89 | "@aws-sdk/types": "^3.222.0", 90 | "@aws-sdk/util-locate-window": "^3.0.0", 91 | "@aws-sdk/util-utf8-browser": "^3.0.0", 92 | "tslib": "^1.11.1" 93 | } 94 | }, 95 | "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { 96 | "version": "1.14.1", 97 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 98 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 99 | }, 100 | "node_modules/@aws-crypto/sha256-js": { 101 | "version": "3.0.0", 102 | "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", 103 | "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", 104 | "dependencies": { 105 | "@aws-crypto/util": "^3.0.0", 106 | "@aws-sdk/types": "^3.222.0", 107 | "tslib": "^1.11.1" 108 | } 109 | }, 110 | "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { 111 | "version": "1.14.1", 112 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 113 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 114 | }, 115 | "node_modules/@aws-crypto/supports-web-crypto": { 116 | "version": "3.0.0", 117 | "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", 118 | "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", 119 | "dependencies": { 120 | "tslib": "^1.11.1" 121 | } 122 | }, 123 | "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { 124 | "version": "1.14.1", 125 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 126 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 127 | }, 128 | "node_modules/@aws-crypto/util": { 129 | "version": "3.0.0", 130 | "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", 131 | "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", 132 | "dependencies": { 133 | "@aws-sdk/types": "^3.222.0", 134 | "@aws-sdk/util-utf8-browser": "^3.0.0", 135 | "tslib": "^1.11.1" 136 | } 137 | }, 138 | "node_modules/@aws-crypto/util/node_modules/tslib": { 139 | "version": "1.14.1", 140 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 141 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 142 | }, 143 | "node_modules/@aws-sdk/client-s3": { 144 | "version": "3.556.0", 145 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.556.0.tgz", 146 | "integrity": "sha512-6WF9Kuzz1/8zqX8hKBpqj9+FYwQ5uTsVcOKpTW94AMX2qtIeVRlwlnNnYyywWo61yqD3g59CMNHcqSsaqAwglg==", 147 | "dependencies": { 148 | "@aws-crypto/sha1-browser": "3.0.0", 149 | "@aws-crypto/sha256-browser": "3.0.0", 150 | "@aws-crypto/sha256-js": "3.0.0", 151 | "@aws-sdk/client-sts": "3.556.0", 152 | "@aws-sdk/core": "3.556.0", 153 | "@aws-sdk/credential-provider-node": "3.556.0", 154 | "@aws-sdk/middleware-bucket-endpoint": "3.535.0", 155 | "@aws-sdk/middleware-expect-continue": "3.535.0", 156 | "@aws-sdk/middleware-flexible-checksums": "3.535.0", 157 | "@aws-sdk/middleware-host-header": "3.535.0", 158 | "@aws-sdk/middleware-location-constraint": "3.535.0", 159 | "@aws-sdk/middleware-logger": "3.535.0", 160 | "@aws-sdk/middleware-recursion-detection": "3.535.0", 161 | "@aws-sdk/middleware-sdk-s3": "3.556.0", 162 | "@aws-sdk/middleware-signing": "3.556.0", 163 | "@aws-sdk/middleware-ssec": "3.537.0", 164 | "@aws-sdk/middleware-user-agent": "3.540.0", 165 | "@aws-sdk/region-config-resolver": "3.535.0", 166 | "@aws-sdk/signature-v4-multi-region": "3.556.0", 167 | "@aws-sdk/types": "3.535.0", 168 | "@aws-sdk/util-endpoints": "3.540.0", 169 | "@aws-sdk/util-user-agent-browser": "3.535.0", 170 | "@aws-sdk/util-user-agent-node": "3.535.0", 171 | "@aws-sdk/xml-builder": "3.535.0", 172 | "@smithy/config-resolver": "^2.2.0", 173 | "@smithy/core": "^1.4.2", 174 | "@smithy/eventstream-serde-browser": "^2.2.0", 175 | "@smithy/eventstream-serde-config-resolver": "^2.2.0", 176 | "@smithy/eventstream-serde-node": "^2.2.0", 177 | "@smithy/fetch-http-handler": "^2.5.0", 178 | "@smithy/hash-blob-browser": "^2.2.0", 179 | "@smithy/hash-node": "^2.2.0", 180 | "@smithy/hash-stream-node": "^2.2.0", 181 | "@smithy/invalid-dependency": "^2.2.0", 182 | "@smithy/md5-js": "^2.2.0", 183 | "@smithy/middleware-content-length": "^2.2.0", 184 | "@smithy/middleware-endpoint": "^2.5.1", 185 | "@smithy/middleware-retry": "^2.3.1", 186 | "@smithy/middleware-serde": "^2.3.0", 187 | "@smithy/middleware-stack": "^2.2.0", 188 | "@smithy/node-config-provider": "^2.3.0", 189 | "@smithy/node-http-handler": "^2.5.0", 190 | "@smithy/protocol-http": "^3.3.0", 191 | "@smithy/smithy-client": "^2.5.1", 192 | "@smithy/types": "^2.12.0", 193 | "@smithy/url-parser": "^2.2.0", 194 | "@smithy/util-base64": "^2.3.0", 195 | "@smithy/util-body-length-browser": "^2.2.0", 196 | "@smithy/util-body-length-node": "^2.3.0", 197 | "@smithy/util-defaults-mode-browser": "^2.2.1", 198 | "@smithy/util-defaults-mode-node": "^2.3.1", 199 | "@smithy/util-endpoints": "^1.2.0", 200 | "@smithy/util-retry": "^2.2.0", 201 | "@smithy/util-stream": "^2.2.0", 202 | "@smithy/util-utf8": "^2.3.0", 203 | "@smithy/util-waiter": "^2.2.0", 204 | "tslib": "^2.6.2" 205 | }, 206 | "engines": { 207 | "node": ">=14.0.0" 208 | } 209 | }, 210 | "node_modules/@aws-sdk/client-sso": { 211 | "version": "3.556.0", 212 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.556.0.tgz", 213 | "integrity": "sha512-unXdWS7uvHqCcOyC1de+Fr8m3F2vMg2m24GPea0bg7rVGTYmiyn9mhUX11VCt+ozydrw+F50FQwL6OqoqPocmw==", 214 | "dependencies": { 215 | "@aws-crypto/sha256-browser": "3.0.0", 216 | "@aws-crypto/sha256-js": "3.0.0", 217 | "@aws-sdk/core": "3.556.0", 218 | "@aws-sdk/middleware-host-header": "3.535.0", 219 | "@aws-sdk/middleware-logger": "3.535.0", 220 | "@aws-sdk/middleware-recursion-detection": "3.535.0", 221 | "@aws-sdk/middleware-user-agent": "3.540.0", 222 | "@aws-sdk/region-config-resolver": "3.535.0", 223 | "@aws-sdk/types": "3.535.0", 224 | "@aws-sdk/util-endpoints": "3.540.0", 225 | "@aws-sdk/util-user-agent-browser": "3.535.0", 226 | "@aws-sdk/util-user-agent-node": "3.535.0", 227 | "@smithy/config-resolver": "^2.2.0", 228 | "@smithy/core": "^1.4.2", 229 | "@smithy/fetch-http-handler": "^2.5.0", 230 | "@smithy/hash-node": "^2.2.0", 231 | "@smithy/invalid-dependency": "^2.2.0", 232 | "@smithy/middleware-content-length": "^2.2.0", 233 | "@smithy/middleware-endpoint": "^2.5.1", 234 | "@smithy/middleware-retry": "^2.3.1", 235 | "@smithy/middleware-serde": "^2.3.0", 236 | "@smithy/middleware-stack": "^2.2.0", 237 | "@smithy/node-config-provider": "^2.3.0", 238 | "@smithy/node-http-handler": "^2.5.0", 239 | "@smithy/protocol-http": "^3.3.0", 240 | "@smithy/smithy-client": "^2.5.1", 241 | "@smithy/types": "^2.12.0", 242 | "@smithy/url-parser": "^2.2.0", 243 | "@smithy/util-base64": "^2.3.0", 244 | "@smithy/util-body-length-browser": "^2.2.0", 245 | "@smithy/util-body-length-node": "^2.3.0", 246 | "@smithy/util-defaults-mode-browser": "^2.2.1", 247 | "@smithy/util-defaults-mode-node": "^2.3.1", 248 | "@smithy/util-endpoints": "^1.2.0", 249 | "@smithy/util-middleware": "^2.2.0", 250 | "@smithy/util-retry": "^2.2.0", 251 | "@smithy/util-utf8": "^2.3.0", 252 | "tslib": "^2.6.2" 253 | }, 254 | "engines": { 255 | "node": ">=14.0.0" 256 | } 257 | }, 258 | "node_modules/@aws-sdk/client-sso-oidc": { 259 | "version": "3.556.0", 260 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.556.0.tgz", 261 | "integrity": "sha512-AXKd2TB6nNrksu+OfmHl8uI07PdgzOo4o8AxoRO8SHlwoMAGvcT9optDGVSYoVfgOKTymCoE7h8/UoUfPc11wQ==", 262 | "dependencies": { 263 | "@aws-crypto/sha256-browser": "3.0.0", 264 | "@aws-crypto/sha256-js": "3.0.0", 265 | "@aws-sdk/client-sts": "3.556.0", 266 | "@aws-sdk/core": "3.556.0", 267 | "@aws-sdk/middleware-host-header": "3.535.0", 268 | "@aws-sdk/middleware-logger": "3.535.0", 269 | "@aws-sdk/middleware-recursion-detection": "3.535.0", 270 | "@aws-sdk/middleware-user-agent": "3.540.0", 271 | "@aws-sdk/region-config-resolver": "3.535.0", 272 | "@aws-sdk/types": "3.535.0", 273 | "@aws-sdk/util-endpoints": "3.540.0", 274 | "@aws-sdk/util-user-agent-browser": "3.535.0", 275 | "@aws-sdk/util-user-agent-node": "3.535.0", 276 | "@smithy/config-resolver": "^2.2.0", 277 | "@smithy/core": "^1.4.2", 278 | "@smithy/fetch-http-handler": "^2.5.0", 279 | "@smithy/hash-node": "^2.2.0", 280 | "@smithy/invalid-dependency": "^2.2.0", 281 | "@smithy/middleware-content-length": "^2.2.0", 282 | "@smithy/middleware-endpoint": "^2.5.1", 283 | "@smithy/middleware-retry": "^2.3.1", 284 | "@smithy/middleware-serde": "^2.3.0", 285 | "@smithy/middleware-stack": "^2.2.0", 286 | "@smithy/node-config-provider": "^2.3.0", 287 | "@smithy/node-http-handler": "^2.5.0", 288 | "@smithy/protocol-http": "^3.3.0", 289 | "@smithy/smithy-client": "^2.5.1", 290 | "@smithy/types": "^2.12.0", 291 | "@smithy/url-parser": "^2.2.0", 292 | "@smithy/util-base64": "^2.3.0", 293 | "@smithy/util-body-length-browser": "^2.2.0", 294 | "@smithy/util-body-length-node": "^2.3.0", 295 | "@smithy/util-defaults-mode-browser": "^2.2.1", 296 | "@smithy/util-defaults-mode-node": "^2.3.1", 297 | "@smithy/util-endpoints": "^1.2.0", 298 | "@smithy/util-middleware": "^2.2.0", 299 | "@smithy/util-retry": "^2.2.0", 300 | "@smithy/util-utf8": "^2.3.0", 301 | "tslib": "^2.6.2" 302 | }, 303 | "engines": { 304 | "node": ">=14.0.0" 305 | }, 306 | "peerDependencies": { 307 | "@aws-sdk/credential-provider-node": "^3.556.0" 308 | } 309 | }, 310 | "node_modules/@aws-sdk/client-sts": { 311 | "version": "3.556.0", 312 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.556.0.tgz", 313 | "integrity": "sha512-TsK3js7Suh9xEmC886aY+bv0KdLLYtzrcmVt6sJ/W6EnDXYQhBuKYFhp03NrN2+vSvMGpqJwR62DyfKe1G0QzQ==", 314 | "dependencies": { 315 | "@aws-crypto/sha256-browser": "3.0.0", 316 | "@aws-crypto/sha256-js": "3.0.0", 317 | "@aws-sdk/core": "3.556.0", 318 | "@aws-sdk/middleware-host-header": "3.535.0", 319 | "@aws-sdk/middleware-logger": "3.535.0", 320 | "@aws-sdk/middleware-recursion-detection": "3.535.0", 321 | "@aws-sdk/middleware-user-agent": "3.540.0", 322 | "@aws-sdk/region-config-resolver": "3.535.0", 323 | "@aws-sdk/types": "3.535.0", 324 | "@aws-sdk/util-endpoints": "3.540.0", 325 | "@aws-sdk/util-user-agent-browser": "3.535.0", 326 | "@aws-sdk/util-user-agent-node": "3.535.0", 327 | "@smithy/config-resolver": "^2.2.0", 328 | "@smithy/core": "^1.4.2", 329 | "@smithy/fetch-http-handler": "^2.5.0", 330 | "@smithy/hash-node": "^2.2.0", 331 | "@smithy/invalid-dependency": "^2.2.0", 332 | "@smithy/middleware-content-length": "^2.2.0", 333 | "@smithy/middleware-endpoint": "^2.5.1", 334 | "@smithy/middleware-retry": "^2.3.1", 335 | "@smithy/middleware-serde": "^2.3.0", 336 | "@smithy/middleware-stack": "^2.2.0", 337 | "@smithy/node-config-provider": "^2.3.0", 338 | "@smithy/node-http-handler": "^2.5.0", 339 | "@smithy/protocol-http": "^3.3.0", 340 | "@smithy/smithy-client": "^2.5.1", 341 | "@smithy/types": "^2.12.0", 342 | "@smithy/url-parser": "^2.2.0", 343 | "@smithy/util-base64": "^2.3.0", 344 | "@smithy/util-body-length-browser": "^2.2.0", 345 | "@smithy/util-body-length-node": "^2.3.0", 346 | "@smithy/util-defaults-mode-browser": "^2.2.1", 347 | "@smithy/util-defaults-mode-node": "^2.3.1", 348 | "@smithy/util-endpoints": "^1.2.0", 349 | "@smithy/util-middleware": "^2.2.0", 350 | "@smithy/util-retry": "^2.2.0", 351 | "@smithy/util-utf8": "^2.3.0", 352 | "tslib": "^2.6.2" 353 | }, 354 | "engines": { 355 | "node": ">=14.0.0" 356 | }, 357 | "peerDependencies": { 358 | "@aws-sdk/credential-provider-node": "^3.556.0" 359 | } 360 | }, 361 | "node_modules/@aws-sdk/core": { 362 | "version": "3.556.0", 363 | "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.556.0.tgz", 364 | "integrity": "sha512-vJaSaHw2kPQlo11j/Rzuz0gk1tEaKdz+2ser0f0qZ5vwFlANjt08m/frU17ctnVKC1s58bxpctO/1P894fHLrA==", 365 | "dependencies": { 366 | "@smithy/core": "^1.4.2", 367 | "@smithy/protocol-http": "^3.3.0", 368 | "@smithy/signature-v4": "^2.3.0", 369 | "@smithy/smithy-client": "^2.5.1", 370 | "@smithy/types": "^2.12.0", 371 | "fast-xml-parser": "4.2.5", 372 | "tslib": "^2.6.2" 373 | }, 374 | "engines": { 375 | "node": ">=14.0.0" 376 | } 377 | }, 378 | "node_modules/@aws-sdk/credential-provider-env": { 379 | "version": "3.535.0", 380 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.535.0.tgz", 381 | "integrity": "sha512-XppwO8c0GCGSAvdzyJOhbtktSEaShg14VJKg8mpMa1XcgqzmcqqHQjtDWbx5rZheY1VdpXZhpEzJkB6LpQejpA==", 382 | "dependencies": { 383 | "@aws-sdk/types": "3.535.0", 384 | "@smithy/property-provider": "^2.2.0", 385 | "@smithy/types": "^2.12.0", 386 | "tslib": "^2.6.2" 387 | }, 388 | "engines": { 389 | "node": ">=14.0.0" 390 | } 391 | }, 392 | "node_modules/@aws-sdk/credential-provider-http": { 393 | "version": "3.552.0", 394 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.552.0.tgz", 395 | "integrity": "sha512-vsmu7Cz1i45pFEqzVb4JcFmAmVnWFNLsGheZc8SCptlqCO5voETrZZILHYIl4cjKkSDk3pblBOf0PhyjqWW6WQ==", 396 | "dependencies": { 397 | "@aws-sdk/types": "3.535.0", 398 | "@smithy/fetch-http-handler": "^2.5.0", 399 | "@smithy/node-http-handler": "^2.5.0", 400 | "@smithy/property-provider": "^2.2.0", 401 | "@smithy/protocol-http": "^3.3.0", 402 | "@smithy/smithy-client": "^2.5.1", 403 | "@smithy/types": "^2.12.0", 404 | "@smithy/util-stream": "^2.2.0", 405 | "tslib": "^2.6.2" 406 | }, 407 | "engines": { 408 | "node": ">=14.0.0" 409 | } 410 | }, 411 | "node_modules/@aws-sdk/credential-provider-ini": { 412 | "version": "3.556.0", 413 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.556.0.tgz", 414 | "integrity": "sha512-0Nz4ErOlXhe3muxWYMbPwRMgfKmVbBp36BAE2uv/z5wTbfdBkcgUwaflEvlKCLUTdHzuZsQk+BFS/gVyaUeOuA==", 415 | "dependencies": { 416 | "@aws-sdk/client-sts": "3.556.0", 417 | "@aws-sdk/credential-provider-env": "3.535.0", 418 | "@aws-sdk/credential-provider-process": "3.535.0", 419 | "@aws-sdk/credential-provider-sso": "3.556.0", 420 | "@aws-sdk/credential-provider-web-identity": "3.556.0", 421 | "@aws-sdk/types": "3.535.0", 422 | "@smithy/credential-provider-imds": "^2.3.0", 423 | "@smithy/property-provider": "^2.2.0", 424 | "@smithy/shared-ini-file-loader": "^2.4.0", 425 | "@smithy/types": "^2.12.0", 426 | "tslib": "^2.6.2" 427 | }, 428 | "engines": { 429 | "node": ">=14.0.0" 430 | } 431 | }, 432 | "node_modules/@aws-sdk/credential-provider-node": { 433 | "version": "3.556.0", 434 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.556.0.tgz", 435 | "integrity": "sha512-s1xVtKjyGc60O8qcNIzS1X3H+pWEwEfZ7TgNznVDNyuXvLrlNWiAcigPWGl2aAkc8tGcsSG0Qpyw2KYC939LFg==", 436 | "dependencies": { 437 | "@aws-sdk/credential-provider-env": "3.535.0", 438 | "@aws-sdk/credential-provider-http": "3.552.0", 439 | "@aws-sdk/credential-provider-ini": "3.556.0", 440 | "@aws-sdk/credential-provider-process": "3.535.0", 441 | "@aws-sdk/credential-provider-sso": "3.556.0", 442 | "@aws-sdk/credential-provider-web-identity": "3.556.0", 443 | "@aws-sdk/types": "3.535.0", 444 | "@smithy/credential-provider-imds": "^2.3.0", 445 | "@smithy/property-provider": "^2.2.0", 446 | "@smithy/shared-ini-file-loader": "^2.4.0", 447 | "@smithy/types": "^2.12.0", 448 | "tslib": "^2.6.2" 449 | }, 450 | "engines": { 451 | "node": ">=14.0.0" 452 | } 453 | }, 454 | "node_modules/@aws-sdk/credential-provider-process": { 455 | "version": "3.535.0", 456 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.535.0.tgz", 457 | "integrity": "sha512-9O1OaprGCnlb/kYl8RwmH7Mlg8JREZctB8r9sa1KhSsWFq/SWO0AuJTyowxD7zL5PkeS4eTvzFFHWCa3OO5epA==", 458 | "dependencies": { 459 | "@aws-sdk/types": "3.535.0", 460 | "@smithy/property-provider": "^2.2.0", 461 | "@smithy/shared-ini-file-loader": "^2.4.0", 462 | "@smithy/types": "^2.12.0", 463 | "tslib": "^2.6.2" 464 | }, 465 | "engines": { 466 | "node": ">=14.0.0" 467 | } 468 | }, 469 | "node_modules/@aws-sdk/credential-provider-sso": { 470 | "version": "3.556.0", 471 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.556.0.tgz", 472 | "integrity": "sha512-ETuBgcnpfxqadEAqhQFWpKoV1C/NAgvs5CbBc5EJbelJ8f4prTdErIHjrRtVT8c02MXj92QwczsiNYd5IoOqyw==", 473 | "dependencies": { 474 | "@aws-sdk/client-sso": "3.556.0", 475 | "@aws-sdk/token-providers": "3.556.0", 476 | "@aws-sdk/types": "3.535.0", 477 | "@smithy/property-provider": "^2.2.0", 478 | "@smithy/shared-ini-file-loader": "^2.4.0", 479 | "@smithy/types": "^2.12.0", 480 | "tslib": "^2.6.2" 481 | }, 482 | "engines": { 483 | "node": ">=14.0.0" 484 | } 485 | }, 486 | "node_modules/@aws-sdk/credential-provider-web-identity": { 487 | "version": "3.556.0", 488 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.556.0.tgz", 489 | "integrity": "sha512-R/YAL8Uh8i+dzVjzMnbcWLIGeeRi2mioHVGnVF+minmaIkCiQMZg2HPrdlKm49El+RljT28Nl5YHRuiqzEIwMA==", 490 | "dependencies": { 491 | "@aws-sdk/client-sts": "3.556.0", 492 | "@aws-sdk/types": "3.535.0", 493 | "@smithy/property-provider": "^2.2.0", 494 | "@smithy/types": "^2.12.0", 495 | "tslib": "^2.6.2" 496 | }, 497 | "engines": { 498 | "node": ">=14.0.0" 499 | } 500 | }, 501 | "node_modules/@aws-sdk/middleware-bucket-endpoint": { 502 | "version": "3.535.0", 503 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.535.0.tgz", 504 | "integrity": "sha512-7sijlfQsc4UO9Fsl11mU26Y5f9E7g6UoNg/iJUBpC5pgvvmdBRO5UEhbB/gnqvOEPsBXyhmfzbstebq23Qdz7A==", 505 | "dependencies": { 506 | "@aws-sdk/types": "3.535.0", 507 | "@aws-sdk/util-arn-parser": "3.535.0", 508 | "@smithy/node-config-provider": "^2.3.0", 509 | "@smithy/protocol-http": "^3.3.0", 510 | "@smithy/types": "^2.12.0", 511 | "@smithy/util-config-provider": "^2.3.0", 512 | "tslib": "^2.6.2" 513 | }, 514 | "engines": { 515 | "node": ">=14.0.0" 516 | } 517 | }, 518 | "node_modules/@aws-sdk/middleware-expect-continue": { 519 | "version": "3.535.0", 520 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.535.0.tgz", 521 | "integrity": "sha512-hFKyqUBky0NWCVku8iZ9+PACehx0p6vuMw5YnZf8FVgHP0fode0b/NwQY6UY7oor/GftvRsAlRUAWGNFEGUpwA==", 522 | "dependencies": { 523 | "@aws-sdk/types": "3.535.0", 524 | "@smithy/protocol-http": "^3.3.0", 525 | "@smithy/types": "^2.12.0", 526 | "tslib": "^2.6.2" 527 | }, 528 | "engines": { 529 | "node": ">=14.0.0" 530 | } 531 | }, 532 | "node_modules/@aws-sdk/middleware-flexible-checksums": { 533 | "version": "3.535.0", 534 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.535.0.tgz", 535 | "integrity": "sha512-rBIzldY9jjRATxICDX7t77aW6ctqmVDgnuAOgbVT5xgHftt4o7PGWKoMvl/45hYqoQgxVFnCBof9bxkqSBebVA==", 536 | "dependencies": { 537 | "@aws-crypto/crc32": "3.0.0", 538 | "@aws-crypto/crc32c": "3.0.0", 539 | "@aws-sdk/types": "3.535.0", 540 | "@smithy/is-array-buffer": "^2.2.0", 541 | "@smithy/protocol-http": "^3.3.0", 542 | "@smithy/types": "^2.12.0", 543 | "@smithy/util-utf8": "^2.3.0", 544 | "tslib": "^2.6.2" 545 | }, 546 | "engines": { 547 | "node": ">=14.0.0" 548 | } 549 | }, 550 | "node_modules/@aws-sdk/middleware-host-header": { 551 | "version": "3.535.0", 552 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.535.0.tgz", 553 | "integrity": "sha512-0h6TWjBWtDaYwHMQJI9ulafeS4lLaw1vIxRjbpH0svFRt6Eve+Sy8NlVhECfTU2hNz/fLubvrUxsXoThaLBIew==", 554 | "dependencies": { 555 | "@aws-sdk/types": "3.535.0", 556 | "@smithy/protocol-http": "^3.3.0", 557 | "@smithy/types": "^2.12.0", 558 | "tslib": "^2.6.2" 559 | }, 560 | "engines": { 561 | "node": ">=14.0.0" 562 | } 563 | }, 564 | "node_modules/@aws-sdk/middleware-location-constraint": { 565 | "version": "3.535.0", 566 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.535.0.tgz", 567 | "integrity": "sha512-SxfS9wfidUZZ+WnlKRTCRn3h+XTsymXRXPJj8VV6hNRNeOwzNweoG3YhQbTowuuNfXf89m9v6meYkBBtkdacKw==", 568 | "dependencies": { 569 | "@aws-sdk/types": "3.535.0", 570 | "@smithy/types": "^2.12.0", 571 | "tslib": "^2.6.2" 572 | }, 573 | "engines": { 574 | "node": ">=14.0.0" 575 | } 576 | }, 577 | "node_modules/@aws-sdk/middleware-logger": { 578 | "version": "3.535.0", 579 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.535.0.tgz", 580 | "integrity": "sha512-huNHpONOrEDrdRTvSQr1cJiRMNf0S52NDXtaPzdxiubTkP+vni2MohmZANMOai/qT0olmEVX01LhZ0ZAOgmg6A==", 581 | "dependencies": { 582 | "@aws-sdk/types": "3.535.0", 583 | "@smithy/types": "^2.12.0", 584 | "tslib": "^2.6.2" 585 | }, 586 | "engines": { 587 | "node": ">=14.0.0" 588 | } 589 | }, 590 | "node_modules/@aws-sdk/middleware-recursion-detection": { 591 | "version": "3.535.0", 592 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.535.0.tgz", 593 | "integrity": "sha512-am2qgGs+gwqmR4wHLWpzlZ8PWhm4ktj5bYSgDrsOfjhdBlWNxvPoID9/pDAz5RWL48+oH7I6SQzMqxXsFDikrw==", 594 | "dependencies": { 595 | "@aws-sdk/types": "3.535.0", 596 | "@smithy/protocol-http": "^3.3.0", 597 | "@smithy/types": "^2.12.0", 598 | "tslib": "^2.6.2" 599 | }, 600 | "engines": { 601 | "node": ">=14.0.0" 602 | } 603 | }, 604 | "node_modules/@aws-sdk/middleware-sdk-s3": { 605 | "version": "3.556.0", 606 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.556.0.tgz", 607 | "integrity": "sha512-4W/dnxqj1B6/uS/5Z+3UHaqDDGjNPgEVlqf5d3ToOFZ31ZfpANwhcCmyX39JklC4aolCEi9renQ5wHnTCC8K8g==", 608 | "dependencies": { 609 | "@aws-sdk/types": "3.535.0", 610 | "@aws-sdk/util-arn-parser": "3.535.0", 611 | "@smithy/node-config-provider": "^2.3.0", 612 | "@smithy/protocol-http": "^3.3.0", 613 | "@smithy/signature-v4": "^2.3.0", 614 | "@smithy/smithy-client": "^2.5.1", 615 | "@smithy/types": "^2.12.0", 616 | "@smithy/util-config-provider": "^2.3.0", 617 | "tslib": "^2.6.2" 618 | }, 619 | "engines": { 620 | "node": ">=14.0.0" 621 | } 622 | }, 623 | "node_modules/@aws-sdk/middleware-signing": { 624 | "version": "3.556.0", 625 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.556.0.tgz", 626 | "integrity": "sha512-kWrPmU8qd3gI5qzpuW9LtWFaH80cOz1ZJDavXx6PRpYZJ5JaKdUHghwfDlVTzzFYAeJmVsWIkPcLT5d5mY5ZTQ==", 627 | "dependencies": { 628 | "@aws-sdk/types": "3.535.0", 629 | "@smithy/property-provider": "^2.2.0", 630 | "@smithy/protocol-http": "^3.3.0", 631 | "@smithy/signature-v4": "^2.3.0", 632 | "@smithy/types": "^2.12.0", 633 | "@smithy/util-middleware": "^2.2.0", 634 | "tslib": "^2.6.2" 635 | }, 636 | "engines": { 637 | "node": ">=14.0.0" 638 | } 639 | }, 640 | "node_modules/@aws-sdk/middleware-ssec": { 641 | "version": "3.537.0", 642 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.537.0.tgz", 643 | "integrity": "sha512-2QWMrbwd5eBy5KCYn9a15JEWBgrK2qFEKQN2lqb/6z0bhtevIOxIRfC99tzvRuPt6nixFQ+ynKuBjcfT4ZFrdQ==", 644 | "dependencies": { 645 | "@aws-sdk/types": "3.535.0", 646 | "@smithy/types": "^2.12.0", 647 | "tslib": "^2.6.2" 648 | }, 649 | "engines": { 650 | "node": ">=14.0.0" 651 | } 652 | }, 653 | "node_modules/@aws-sdk/middleware-user-agent": { 654 | "version": "3.540.0", 655 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.540.0.tgz", 656 | "integrity": "sha512-8Rd6wPeXDnOYzWj1XCmOKcx/Q87L0K1/EHqOBocGjLVbN3gmRxBvpmR1pRTjf7IsWfnnzN5btqtcAkfDPYQUMQ==", 657 | "dependencies": { 658 | "@aws-sdk/types": "3.535.0", 659 | "@aws-sdk/util-endpoints": "3.540.0", 660 | "@smithy/protocol-http": "^3.3.0", 661 | "@smithy/types": "^2.12.0", 662 | "tslib": "^2.6.2" 663 | }, 664 | "engines": { 665 | "node": ">=14.0.0" 666 | } 667 | }, 668 | "node_modules/@aws-sdk/region-config-resolver": { 669 | "version": "3.535.0", 670 | "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.535.0.tgz", 671 | "integrity": "sha512-IXOznDiaItBjsQy4Fil0kzX/J3HxIOknEphqHbOfUf+LpA5ugcsxuQQONrbEQusCBnfJyymrldBvBhFmtlU9Wg==", 672 | "dependencies": { 673 | "@aws-sdk/types": "3.535.0", 674 | "@smithy/node-config-provider": "^2.3.0", 675 | "@smithy/types": "^2.12.0", 676 | "@smithy/util-config-provider": "^2.3.0", 677 | "@smithy/util-middleware": "^2.2.0", 678 | "tslib": "^2.6.2" 679 | }, 680 | "engines": { 681 | "node": ">=14.0.0" 682 | } 683 | }, 684 | "node_modules/@aws-sdk/signature-v4-multi-region": { 685 | "version": "3.556.0", 686 | "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.556.0.tgz", 687 | "integrity": "sha512-bWDSK0ggK7QzAOmPZGv29UAIZocL1MNY7XyOvm3P3P1U3tFMoIBilQQBLabXyHoZ9J3Ik0Vv4n95htUhRQ35ow==", 688 | "dependencies": { 689 | "@aws-sdk/middleware-sdk-s3": "3.556.0", 690 | "@aws-sdk/types": "3.535.0", 691 | "@smithy/protocol-http": "^3.3.0", 692 | "@smithy/signature-v4": "^2.3.0", 693 | "@smithy/types": "^2.12.0", 694 | "tslib": "^2.6.2" 695 | }, 696 | "engines": { 697 | "node": ">=14.0.0" 698 | } 699 | }, 700 | "node_modules/@aws-sdk/token-providers": { 701 | "version": "3.556.0", 702 | "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.556.0.tgz", 703 | "integrity": "sha512-tvIiugNF0/+2wfuImMrpKjXMx4nCnFWQjQvouObny+wrif/PGqqQYrybwxPJDvzbd965bu1I+QuSv85/ug7xsg==", 704 | "dependencies": { 705 | "@aws-sdk/client-sso-oidc": "3.556.0", 706 | "@aws-sdk/types": "3.535.0", 707 | "@smithy/property-provider": "^2.2.0", 708 | "@smithy/shared-ini-file-loader": "^2.4.0", 709 | "@smithy/types": "^2.12.0", 710 | "tslib": "^2.6.2" 711 | }, 712 | "engines": { 713 | "node": ">=14.0.0" 714 | } 715 | }, 716 | "node_modules/@aws-sdk/types": { 717 | "version": "3.535.0", 718 | "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.535.0.tgz", 719 | "integrity": "sha512-aY4MYfduNj+sRR37U7XxYR8wemfbKP6lx00ze2M2uubn7mZotuVrWYAafbMSXrdEMSToE5JDhr28vArSOoLcSg==", 720 | "dependencies": { 721 | "@smithy/types": "^2.12.0", 722 | "tslib": "^2.6.2" 723 | }, 724 | "engines": { 725 | "node": ">=14.0.0" 726 | } 727 | }, 728 | "node_modules/@aws-sdk/util-arn-parser": { 729 | "version": "3.535.0", 730 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.535.0.tgz", 731 | "integrity": "sha512-smVo29nUPAOprp8Z5Y3GHuhiOtw6c8/EtLCm5AVMtRsTPw4V414ZXL2H66tzmb5kEeSzQlbfBSBEdIFZoxO9kg==", 732 | "dependencies": { 733 | "tslib": "^2.6.2" 734 | }, 735 | "engines": { 736 | "node": ">=14.0.0" 737 | } 738 | }, 739 | "node_modules/@aws-sdk/util-endpoints": { 740 | "version": "3.540.0", 741 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.540.0.tgz", 742 | "integrity": "sha512-1kMyQFAWx6f8alaI6UT65/5YW/7pDWAKAdNwL6vuJLea03KrZRX3PMoONOSJpAS5m3Ot7HlWZvf3wZDNTLELZw==", 743 | "dependencies": { 744 | "@aws-sdk/types": "3.535.0", 745 | "@smithy/types": "^2.12.0", 746 | "@smithy/util-endpoints": "^1.2.0", 747 | "tslib": "^2.6.2" 748 | }, 749 | "engines": { 750 | "node": ">=14.0.0" 751 | } 752 | }, 753 | "node_modules/@aws-sdk/util-locate-window": { 754 | "version": "3.535.0", 755 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.535.0.tgz", 756 | "integrity": "sha512-PHJ3SL6d2jpcgbqdgiPxkXpu7Drc2PYViwxSIqvvMKhDwzSB1W3mMvtpzwKM4IE7zLFodZo0GKjJ9AsoXndXhA==", 757 | "dependencies": { 758 | "tslib": "^2.6.2" 759 | }, 760 | "engines": { 761 | "node": ">=14.0.0" 762 | } 763 | }, 764 | "node_modules/@aws-sdk/util-user-agent-browser": { 765 | "version": "3.535.0", 766 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.535.0.tgz", 767 | "integrity": "sha512-RWMcF/xV5n+nhaA/Ff5P3yNP3Kur/I+VNZngog4TEs92oB/nwOdAg/2JL8bVAhUbMrjTjpwm7PItziYFQoqyig==", 768 | "dependencies": { 769 | "@aws-sdk/types": "3.535.0", 770 | "@smithy/types": "^2.12.0", 771 | "bowser": "^2.11.0", 772 | "tslib": "^2.6.2" 773 | } 774 | }, 775 | "node_modules/@aws-sdk/util-user-agent-node": { 776 | "version": "3.535.0", 777 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.535.0.tgz", 778 | "integrity": "sha512-dRek0zUuIT25wOWJlsRm97nTkUlh1NDcLsQZIN2Y8KxhwoXXWtJs5vaDPT+qAg+OpcNj80i1zLR/CirqlFg/TQ==", 779 | "dependencies": { 780 | "@aws-sdk/types": "3.535.0", 781 | "@smithy/node-config-provider": "^2.3.0", 782 | "@smithy/types": "^2.12.0", 783 | "tslib": "^2.6.2" 784 | }, 785 | "engines": { 786 | "node": ">=14.0.0" 787 | }, 788 | "peerDependencies": { 789 | "aws-crt": ">=1.0.0" 790 | }, 791 | "peerDependenciesMeta": { 792 | "aws-crt": { 793 | "optional": true 794 | } 795 | } 796 | }, 797 | "node_modules/@aws-sdk/util-utf8-browser": { 798 | "version": "3.259.0", 799 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", 800 | "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", 801 | "dependencies": { 802 | "tslib": "^2.3.1" 803 | } 804 | }, 805 | "node_modules/@aws-sdk/xml-builder": { 806 | "version": "3.535.0", 807 | "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.535.0.tgz", 808 | "integrity": "sha512-VXAq/Jz8KIrU84+HqsOJhIKZqG0PNTdi6n6PFQ4xJf44ZQHD/5C7ouH4qCFX5XgZXcgbRIcMVVYGC6Jye0dRng==", 809 | "dependencies": { 810 | "@smithy/types": "^2.12.0", 811 | "tslib": "^2.6.2" 812 | }, 813 | "engines": { 814 | "node": ">=14.0.0" 815 | } 816 | }, 817 | "node_modules/@esbuild/aix-ppc64": { 818 | "version": "0.20.2", 819 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", 820 | "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", 821 | "cpu": [ 822 | "ppc64" 823 | ], 824 | "dev": true, 825 | "optional": true, 826 | "os": [ 827 | "aix" 828 | ], 829 | "engines": { 830 | "node": ">=12" 831 | } 832 | }, 833 | "node_modules/@esbuild/android-arm": { 834 | "version": "0.20.2", 835 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", 836 | "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", 837 | "cpu": [ 838 | "arm" 839 | ], 840 | "dev": true, 841 | "optional": true, 842 | "os": [ 843 | "android" 844 | ], 845 | "engines": { 846 | "node": ">=12" 847 | } 848 | }, 849 | "node_modules/@esbuild/android-arm64": { 850 | "version": "0.20.2", 851 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", 852 | "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", 853 | "cpu": [ 854 | "arm64" 855 | ], 856 | "dev": true, 857 | "optional": true, 858 | "os": [ 859 | "android" 860 | ], 861 | "engines": { 862 | "node": ">=12" 863 | } 864 | }, 865 | "node_modules/@esbuild/android-x64": { 866 | "version": "0.20.2", 867 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", 868 | "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", 869 | "cpu": [ 870 | "x64" 871 | ], 872 | "dev": true, 873 | "optional": true, 874 | "os": [ 875 | "android" 876 | ], 877 | "engines": { 878 | "node": ">=12" 879 | } 880 | }, 881 | "node_modules/@esbuild/darwin-arm64": { 882 | "version": "0.20.2", 883 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", 884 | "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", 885 | "cpu": [ 886 | "arm64" 887 | ], 888 | "dev": true, 889 | "optional": true, 890 | "os": [ 891 | "darwin" 892 | ], 893 | "engines": { 894 | "node": ">=12" 895 | } 896 | }, 897 | "node_modules/@esbuild/darwin-x64": { 898 | "version": "0.20.2", 899 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", 900 | "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", 901 | "cpu": [ 902 | "x64" 903 | ], 904 | "dev": true, 905 | "optional": true, 906 | "os": [ 907 | "darwin" 908 | ], 909 | "engines": { 910 | "node": ">=12" 911 | } 912 | }, 913 | "node_modules/@esbuild/freebsd-arm64": { 914 | "version": "0.20.2", 915 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", 916 | "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", 917 | "cpu": [ 918 | "arm64" 919 | ], 920 | "dev": true, 921 | "optional": true, 922 | "os": [ 923 | "freebsd" 924 | ], 925 | "engines": { 926 | "node": ">=12" 927 | } 928 | }, 929 | "node_modules/@esbuild/freebsd-x64": { 930 | "version": "0.20.2", 931 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", 932 | "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", 933 | "cpu": [ 934 | "x64" 935 | ], 936 | "dev": true, 937 | "optional": true, 938 | "os": [ 939 | "freebsd" 940 | ], 941 | "engines": { 942 | "node": ">=12" 943 | } 944 | }, 945 | "node_modules/@esbuild/linux-arm": { 946 | "version": "0.20.2", 947 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", 948 | "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", 949 | "cpu": [ 950 | "arm" 951 | ], 952 | "dev": true, 953 | "optional": true, 954 | "os": [ 955 | "linux" 956 | ], 957 | "engines": { 958 | "node": ">=12" 959 | } 960 | }, 961 | "node_modules/@esbuild/linux-arm64": { 962 | "version": "0.20.2", 963 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", 964 | "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", 965 | "cpu": [ 966 | "arm64" 967 | ], 968 | "dev": true, 969 | "optional": true, 970 | "os": [ 971 | "linux" 972 | ], 973 | "engines": { 974 | "node": ">=12" 975 | } 976 | }, 977 | "node_modules/@esbuild/linux-ia32": { 978 | "version": "0.20.2", 979 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", 980 | "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", 981 | "cpu": [ 982 | "ia32" 983 | ], 984 | "dev": true, 985 | "optional": true, 986 | "os": [ 987 | "linux" 988 | ], 989 | "engines": { 990 | "node": ">=12" 991 | } 992 | }, 993 | "node_modules/@esbuild/linux-loong64": { 994 | "version": "0.20.2", 995 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", 996 | "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", 997 | "cpu": [ 998 | "loong64" 999 | ], 1000 | "dev": true, 1001 | "optional": true, 1002 | "os": [ 1003 | "linux" 1004 | ], 1005 | "engines": { 1006 | "node": ">=12" 1007 | } 1008 | }, 1009 | "node_modules/@esbuild/linux-mips64el": { 1010 | "version": "0.20.2", 1011 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", 1012 | "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", 1013 | "cpu": [ 1014 | "mips64el" 1015 | ], 1016 | "dev": true, 1017 | "optional": true, 1018 | "os": [ 1019 | "linux" 1020 | ], 1021 | "engines": { 1022 | "node": ">=12" 1023 | } 1024 | }, 1025 | "node_modules/@esbuild/linux-ppc64": { 1026 | "version": "0.20.2", 1027 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", 1028 | "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", 1029 | "cpu": [ 1030 | "ppc64" 1031 | ], 1032 | "dev": true, 1033 | "optional": true, 1034 | "os": [ 1035 | "linux" 1036 | ], 1037 | "engines": { 1038 | "node": ">=12" 1039 | } 1040 | }, 1041 | "node_modules/@esbuild/linux-riscv64": { 1042 | "version": "0.20.2", 1043 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", 1044 | "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", 1045 | "cpu": [ 1046 | "riscv64" 1047 | ], 1048 | "dev": true, 1049 | "optional": true, 1050 | "os": [ 1051 | "linux" 1052 | ], 1053 | "engines": { 1054 | "node": ">=12" 1055 | } 1056 | }, 1057 | "node_modules/@esbuild/linux-s390x": { 1058 | "version": "0.20.2", 1059 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", 1060 | "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", 1061 | "cpu": [ 1062 | "s390x" 1063 | ], 1064 | "dev": true, 1065 | "optional": true, 1066 | "os": [ 1067 | "linux" 1068 | ], 1069 | "engines": { 1070 | "node": ">=12" 1071 | } 1072 | }, 1073 | "node_modules/@esbuild/linux-x64": { 1074 | "version": "0.20.2", 1075 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", 1076 | "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", 1077 | "cpu": [ 1078 | "x64" 1079 | ], 1080 | "dev": true, 1081 | "optional": true, 1082 | "os": [ 1083 | "linux" 1084 | ], 1085 | "engines": { 1086 | "node": ">=12" 1087 | } 1088 | }, 1089 | "node_modules/@esbuild/netbsd-x64": { 1090 | "version": "0.20.2", 1091 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", 1092 | "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", 1093 | "cpu": [ 1094 | "x64" 1095 | ], 1096 | "dev": true, 1097 | "optional": true, 1098 | "os": [ 1099 | "netbsd" 1100 | ], 1101 | "engines": { 1102 | "node": ">=12" 1103 | } 1104 | }, 1105 | "node_modules/@esbuild/openbsd-x64": { 1106 | "version": "0.20.2", 1107 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", 1108 | "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", 1109 | "cpu": [ 1110 | "x64" 1111 | ], 1112 | "dev": true, 1113 | "optional": true, 1114 | "os": [ 1115 | "openbsd" 1116 | ], 1117 | "engines": { 1118 | "node": ">=12" 1119 | } 1120 | }, 1121 | "node_modules/@esbuild/sunos-x64": { 1122 | "version": "0.20.2", 1123 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", 1124 | "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", 1125 | "cpu": [ 1126 | "x64" 1127 | ], 1128 | "dev": true, 1129 | "optional": true, 1130 | "os": [ 1131 | "sunos" 1132 | ], 1133 | "engines": { 1134 | "node": ">=12" 1135 | } 1136 | }, 1137 | "node_modules/@esbuild/win32-arm64": { 1138 | "version": "0.20.2", 1139 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", 1140 | "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", 1141 | "cpu": [ 1142 | "arm64" 1143 | ], 1144 | "dev": true, 1145 | "optional": true, 1146 | "os": [ 1147 | "win32" 1148 | ], 1149 | "engines": { 1150 | "node": ">=12" 1151 | } 1152 | }, 1153 | "node_modules/@esbuild/win32-ia32": { 1154 | "version": "0.20.2", 1155 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", 1156 | "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", 1157 | "cpu": [ 1158 | "ia32" 1159 | ], 1160 | "dev": true, 1161 | "optional": true, 1162 | "os": [ 1163 | "win32" 1164 | ], 1165 | "engines": { 1166 | "node": ">=12" 1167 | } 1168 | }, 1169 | "node_modules/@esbuild/win32-x64": { 1170 | "version": "0.20.2", 1171 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", 1172 | "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", 1173 | "cpu": [ 1174 | "x64" 1175 | ], 1176 | "dev": true, 1177 | "optional": true, 1178 | "os": [ 1179 | "win32" 1180 | ], 1181 | "engines": { 1182 | "node": ">=12" 1183 | } 1184 | }, 1185 | "node_modules/@smithy/abort-controller": { 1186 | "version": "2.2.0", 1187 | "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.2.0.tgz", 1188 | "integrity": "sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==", 1189 | "dependencies": { 1190 | "@smithy/types": "^2.12.0", 1191 | "tslib": "^2.6.2" 1192 | }, 1193 | "engines": { 1194 | "node": ">=14.0.0" 1195 | } 1196 | }, 1197 | "node_modules/@smithy/chunked-blob-reader": { 1198 | "version": "2.2.0", 1199 | "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-2.2.0.tgz", 1200 | "integrity": "sha512-3GJNvRwXBGdkDZZOGiziVYzDpn4j6zfyULHMDKAGIUo72yHALpE9CbhfQp/XcLNVoc1byfMpn6uW5H2BqPjgaQ==", 1201 | "dependencies": { 1202 | "tslib": "^2.6.2" 1203 | } 1204 | }, 1205 | "node_modules/@smithy/chunked-blob-reader-native": { 1206 | "version": "2.2.0", 1207 | "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-2.2.0.tgz", 1208 | "integrity": "sha512-VNB5+1oCgX3Fzs072yuRsUoC2N4Zg/LJ11DTxX3+Qu+Paa6AmbIF0E9sc2wthz9Psrk/zcOlTCyuposlIhPjZQ==", 1209 | "dependencies": { 1210 | "@smithy/util-base64": "^2.3.0", 1211 | "tslib": "^2.6.2" 1212 | } 1213 | }, 1214 | "node_modules/@smithy/config-resolver": { 1215 | "version": "2.2.0", 1216 | "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.2.0.tgz", 1217 | "integrity": "sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA==", 1218 | "dependencies": { 1219 | "@smithy/node-config-provider": "^2.3.0", 1220 | "@smithy/types": "^2.12.0", 1221 | "@smithy/util-config-provider": "^2.3.0", 1222 | "@smithy/util-middleware": "^2.2.0", 1223 | "tslib": "^2.6.2" 1224 | }, 1225 | "engines": { 1226 | "node": ">=14.0.0" 1227 | } 1228 | }, 1229 | "node_modules/@smithy/core": { 1230 | "version": "1.4.2", 1231 | "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.4.2.tgz", 1232 | "integrity": "sha512-2fek3I0KZHWJlRLvRTqxTEri+qV0GRHrJIoLFuBMZB4EMg4WgeBGfF0X6abnrNYpq55KJ6R4D6x4f0vLnhzinA==", 1233 | "dependencies": { 1234 | "@smithy/middleware-endpoint": "^2.5.1", 1235 | "@smithy/middleware-retry": "^2.3.1", 1236 | "@smithy/middleware-serde": "^2.3.0", 1237 | "@smithy/protocol-http": "^3.3.0", 1238 | "@smithy/smithy-client": "^2.5.1", 1239 | "@smithy/types": "^2.12.0", 1240 | "@smithy/util-middleware": "^2.2.0", 1241 | "tslib": "^2.6.2" 1242 | }, 1243 | "engines": { 1244 | "node": ">=14.0.0" 1245 | } 1246 | }, 1247 | "node_modules/@smithy/credential-provider-imds": { 1248 | "version": "2.3.0", 1249 | "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.3.0.tgz", 1250 | "integrity": "sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==", 1251 | "dependencies": { 1252 | "@smithy/node-config-provider": "^2.3.0", 1253 | "@smithy/property-provider": "^2.2.0", 1254 | "@smithy/types": "^2.12.0", 1255 | "@smithy/url-parser": "^2.2.0", 1256 | "tslib": "^2.6.2" 1257 | }, 1258 | "engines": { 1259 | "node": ">=14.0.0" 1260 | } 1261 | }, 1262 | "node_modules/@smithy/eventstream-codec": { 1263 | "version": "2.2.0", 1264 | "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.2.0.tgz", 1265 | "integrity": "sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==", 1266 | "dependencies": { 1267 | "@aws-crypto/crc32": "3.0.0", 1268 | "@smithy/types": "^2.12.0", 1269 | "@smithy/util-hex-encoding": "^2.2.0", 1270 | "tslib": "^2.6.2" 1271 | } 1272 | }, 1273 | "node_modules/@smithy/eventstream-serde-browser": { 1274 | "version": "2.2.0", 1275 | "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.2.0.tgz", 1276 | "integrity": "sha512-UaPf8jKbcP71BGiO0CdeLmlg+RhWnlN8ipsMSdwvqBFigl5nil3rHOI/5GE3tfiuX8LvY5Z9N0meuU7Rab7jWw==", 1277 | "dependencies": { 1278 | "@smithy/eventstream-serde-universal": "^2.2.0", 1279 | "@smithy/types": "^2.12.0", 1280 | "tslib": "^2.6.2" 1281 | }, 1282 | "engines": { 1283 | "node": ">=14.0.0" 1284 | } 1285 | }, 1286 | "node_modules/@smithy/eventstream-serde-config-resolver": { 1287 | "version": "2.2.0", 1288 | "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.2.0.tgz", 1289 | "integrity": "sha512-RHhbTw/JW3+r8QQH7PrganjNCiuiEZmpi6fYUAetFfPLfZ6EkiA08uN3EFfcyKubXQxOwTeJRZSQmDDCdUshaA==", 1290 | "dependencies": { 1291 | "@smithy/types": "^2.12.0", 1292 | "tslib": "^2.6.2" 1293 | }, 1294 | "engines": { 1295 | "node": ">=14.0.0" 1296 | } 1297 | }, 1298 | "node_modules/@smithy/eventstream-serde-node": { 1299 | "version": "2.2.0", 1300 | "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.2.0.tgz", 1301 | "integrity": "sha512-zpQMtJVqCUMn+pCSFcl9K/RPNtQE0NuMh8sKpCdEHafhwRsjP50Oq/4kMmvxSRy6d8Jslqd8BLvDngrUtmN9iA==", 1302 | "dependencies": { 1303 | "@smithy/eventstream-serde-universal": "^2.2.0", 1304 | "@smithy/types": "^2.12.0", 1305 | "tslib": "^2.6.2" 1306 | }, 1307 | "engines": { 1308 | "node": ">=14.0.0" 1309 | } 1310 | }, 1311 | "node_modules/@smithy/eventstream-serde-universal": { 1312 | "version": "2.2.0", 1313 | "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.2.0.tgz", 1314 | "integrity": "sha512-pvoe/vvJY0mOpuF84BEtyZoYfbehiFj8KKWk1ds2AT0mTLYFVs+7sBJZmioOFdBXKd48lfrx1vumdPdmGlCLxA==", 1315 | "dependencies": { 1316 | "@smithy/eventstream-codec": "^2.2.0", 1317 | "@smithy/types": "^2.12.0", 1318 | "tslib": "^2.6.2" 1319 | }, 1320 | "engines": { 1321 | "node": ">=14.0.0" 1322 | } 1323 | }, 1324 | "node_modules/@smithy/fetch-http-handler": { 1325 | "version": "2.5.0", 1326 | "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.5.0.tgz", 1327 | "integrity": "sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==", 1328 | "dependencies": { 1329 | "@smithy/protocol-http": "^3.3.0", 1330 | "@smithy/querystring-builder": "^2.2.0", 1331 | "@smithy/types": "^2.12.0", 1332 | "@smithy/util-base64": "^2.3.0", 1333 | "tslib": "^2.6.2" 1334 | } 1335 | }, 1336 | "node_modules/@smithy/hash-blob-browser": { 1337 | "version": "2.2.0", 1338 | "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-2.2.0.tgz", 1339 | "integrity": "sha512-SGPoVH8mdXBqrkVCJ1Hd1X7vh1zDXojNN1yZyZTZsCno99hVue9+IYzWDjq/EQDDXxmITB0gBmuyPh8oAZSTcg==", 1340 | "dependencies": { 1341 | "@smithy/chunked-blob-reader": "^2.2.0", 1342 | "@smithy/chunked-blob-reader-native": "^2.2.0", 1343 | "@smithy/types": "^2.12.0", 1344 | "tslib": "^2.6.2" 1345 | } 1346 | }, 1347 | "node_modules/@smithy/hash-node": { 1348 | "version": "2.2.0", 1349 | "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.2.0.tgz", 1350 | "integrity": "sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g==", 1351 | "dependencies": { 1352 | "@smithy/types": "^2.12.0", 1353 | "@smithy/util-buffer-from": "^2.2.0", 1354 | "@smithy/util-utf8": "^2.3.0", 1355 | "tslib": "^2.6.2" 1356 | }, 1357 | "engines": { 1358 | "node": ">=14.0.0" 1359 | } 1360 | }, 1361 | "node_modules/@smithy/hash-stream-node": { 1362 | "version": "2.2.0", 1363 | "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-2.2.0.tgz", 1364 | "integrity": "sha512-aT+HCATOSRMGpPI7bi7NSsTNVZE/La9IaxLXWoVAYMxHT5hGO3ZOGEMZQg8A6nNL+pdFGtZQtND1eoY084HgHQ==", 1365 | "dependencies": { 1366 | "@smithy/types": "^2.12.0", 1367 | "@smithy/util-utf8": "^2.3.0", 1368 | "tslib": "^2.6.2" 1369 | }, 1370 | "engines": { 1371 | "node": ">=14.0.0" 1372 | } 1373 | }, 1374 | "node_modules/@smithy/invalid-dependency": { 1375 | "version": "2.2.0", 1376 | "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.2.0.tgz", 1377 | "integrity": "sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q==", 1378 | "dependencies": { 1379 | "@smithy/types": "^2.12.0", 1380 | "tslib": "^2.6.2" 1381 | } 1382 | }, 1383 | "node_modules/@smithy/is-array-buffer": { 1384 | "version": "2.2.0", 1385 | "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", 1386 | "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", 1387 | "dependencies": { 1388 | "tslib": "^2.6.2" 1389 | }, 1390 | "engines": { 1391 | "node": ">=14.0.0" 1392 | } 1393 | }, 1394 | "node_modules/@smithy/md5-js": { 1395 | "version": "2.2.0", 1396 | "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-2.2.0.tgz", 1397 | "integrity": "sha512-M26XTtt9IIusVMOWEAhIvFIr9jYj4ISPPGJROqw6vXngO3IYJCnVVSMFn4Tx1rUTG5BiKJNg9u2nxmBiZC5IlQ==", 1398 | "dependencies": { 1399 | "@smithy/types": "^2.12.0", 1400 | "@smithy/util-utf8": "^2.3.0", 1401 | "tslib": "^2.6.2" 1402 | } 1403 | }, 1404 | "node_modules/@smithy/middleware-content-length": { 1405 | "version": "2.2.0", 1406 | "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.2.0.tgz", 1407 | "integrity": "sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ==", 1408 | "dependencies": { 1409 | "@smithy/protocol-http": "^3.3.0", 1410 | "@smithy/types": "^2.12.0", 1411 | "tslib": "^2.6.2" 1412 | }, 1413 | "engines": { 1414 | "node": ">=14.0.0" 1415 | } 1416 | }, 1417 | "node_modules/@smithy/middleware-endpoint": { 1418 | "version": "2.5.1", 1419 | "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.5.1.tgz", 1420 | "integrity": "sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==", 1421 | "dependencies": { 1422 | "@smithy/middleware-serde": "^2.3.0", 1423 | "@smithy/node-config-provider": "^2.3.0", 1424 | "@smithy/shared-ini-file-loader": "^2.4.0", 1425 | "@smithy/types": "^2.12.0", 1426 | "@smithy/url-parser": "^2.2.0", 1427 | "@smithy/util-middleware": "^2.2.0", 1428 | "tslib": "^2.6.2" 1429 | }, 1430 | "engines": { 1431 | "node": ">=14.0.0" 1432 | } 1433 | }, 1434 | "node_modules/@smithy/middleware-retry": { 1435 | "version": "2.3.1", 1436 | "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.3.1.tgz", 1437 | "integrity": "sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==", 1438 | "dependencies": { 1439 | "@smithy/node-config-provider": "^2.3.0", 1440 | "@smithy/protocol-http": "^3.3.0", 1441 | "@smithy/service-error-classification": "^2.1.5", 1442 | "@smithy/smithy-client": "^2.5.1", 1443 | "@smithy/types": "^2.12.0", 1444 | "@smithy/util-middleware": "^2.2.0", 1445 | "@smithy/util-retry": "^2.2.0", 1446 | "tslib": "^2.6.2", 1447 | "uuid": "^9.0.1" 1448 | }, 1449 | "engines": { 1450 | "node": ">=14.0.0" 1451 | } 1452 | }, 1453 | "node_modules/@smithy/middleware-serde": { 1454 | "version": "2.3.0", 1455 | "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.3.0.tgz", 1456 | "integrity": "sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==", 1457 | "dependencies": { 1458 | "@smithy/types": "^2.12.0", 1459 | "tslib": "^2.6.2" 1460 | }, 1461 | "engines": { 1462 | "node": ">=14.0.0" 1463 | } 1464 | }, 1465 | "node_modules/@smithy/middleware-stack": { 1466 | "version": "2.2.0", 1467 | "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.2.0.tgz", 1468 | "integrity": "sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==", 1469 | "dependencies": { 1470 | "@smithy/types": "^2.12.0", 1471 | "tslib": "^2.6.2" 1472 | }, 1473 | "engines": { 1474 | "node": ">=14.0.0" 1475 | } 1476 | }, 1477 | "node_modules/@smithy/node-config-provider": { 1478 | "version": "2.3.0", 1479 | "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.3.0.tgz", 1480 | "integrity": "sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==", 1481 | "dependencies": { 1482 | "@smithy/property-provider": "^2.2.0", 1483 | "@smithy/shared-ini-file-loader": "^2.4.0", 1484 | "@smithy/types": "^2.12.0", 1485 | "tslib": "^2.6.2" 1486 | }, 1487 | "engines": { 1488 | "node": ">=14.0.0" 1489 | } 1490 | }, 1491 | "node_modules/@smithy/node-http-handler": { 1492 | "version": "2.5.0", 1493 | "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.5.0.tgz", 1494 | "integrity": "sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==", 1495 | "dependencies": { 1496 | "@smithy/abort-controller": "^2.2.0", 1497 | "@smithy/protocol-http": "^3.3.0", 1498 | "@smithy/querystring-builder": "^2.2.0", 1499 | "@smithy/types": "^2.12.0", 1500 | "tslib": "^2.6.2" 1501 | }, 1502 | "engines": { 1503 | "node": ">=14.0.0" 1504 | } 1505 | }, 1506 | "node_modules/@smithy/property-provider": { 1507 | "version": "2.2.0", 1508 | "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.2.0.tgz", 1509 | "integrity": "sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==", 1510 | "dependencies": { 1511 | "@smithy/types": "^2.12.0", 1512 | "tslib": "^2.6.2" 1513 | }, 1514 | "engines": { 1515 | "node": ">=14.0.0" 1516 | } 1517 | }, 1518 | "node_modules/@smithy/protocol-http": { 1519 | "version": "3.3.0", 1520 | "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.3.0.tgz", 1521 | "integrity": "sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==", 1522 | "dependencies": { 1523 | "@smithy/types": "^2.12.0", 1524 | "tslib": "^2.6.2" 1525 | }, 1526 | "engines": { 1527 | "node": ">=14.0.0" 1528 | } 1529 | }, 1530 | "node_modules/@smithy/querystring-builder": { 1531 | "version": "2.2.0", 1532 | "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.2.0.tgz", 1533 | "integrity": "sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==", 1534 | "dependencies": { 1535 | "@smithy/types": "^2.12.0", 1536 | "@smithy/util-uri-escape": "^2.2.0", 1537 | "tslib": "^2.6.2" 1538 | }, 1539 | "engines": { 1540 | "node": ">=14.0.0" 1541 | } 1542 | }, 1543 | "node_modules/@smithy/querystring-parser": { 1544 | "version": "2.2.0", 1545 | "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.2.0.tgz", 1546 | "integrity": "sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==", 1547 | "dependencies": { 1548 | "@smithy/types": "^2.12.0", 1549 | "tslib": "^2.6.2" 1550 | }, 1551 | "engines": { 1552 | "node": ">=14.0.0" 1553 | } 1554 | }, 1555 | "node_modules/@smithy/service-error-classification": { 1556 | "version": "2.1.5", 1557 | "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.5.tgz", 1558 | "integrity": "sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==", 1559 | "dependencies": { 1560 | "@smithy/types": "^2.12.0" 1561 | }, 1562 | "engines": { 1563 | "node": ">=14.0.0" 1564 | } 1565 | }, 1566 | "node_modules/@smithy/shared-ini-file-loader": { 1567 | "version": "2.4.0", 1568 | "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.4.0.tgz", 1569 | "integrity": "sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==", 1570 | "dependencies": { 1571 | "@smithy/types": "^2.12.0", 1572 | "tslib": "^2.6.2" 1573 | }, 1574 | "engines": { 1575 | "node": ">=14.0.0" 1576 | } 1577 | }, 1578 | "node_modules/@smithy/signature-v4": { 1579 | "version": "2.3.0", 1580 | "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.3.0.tgz", 1581 | "integrity": "sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==", 1582 | "dependencies": { 1583 | "@smithy/is-array-buffer": "^2.2.0", 1584 | "@smithy/types": "^2.12.0", 1585 | "@smithy/util-hex-encoding": "^2.2.0", 1586 | "@smithy/util-middleware": "^2.2.0", 1587 | "@smithy/util-uri-escape": "^2.2.0", 1588 | "@smithy/util-utf8": "^2.3.0", 1589 | "tslib": "^2.6.2" 1590 | }, 1591 | "engines": { 1592 | "node": ">=14.0.0" 1593 | } 1594 | }, 1595 | "node_modules/@smithy/smithy-client": { 1596 | "version": "2.5.1", 1597 | "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.5.1.tgz", 1598 | "integrity": "sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==", 1599 | "dependencies": { 1600 | "@smithy/middleware-endpoint": "^2.5.1", 1601 | "@smithy/middleware-stack": "^2.2.0", 1602 | "@smithy/protocol-http": "^3.3.0", 1603 | "@smithy/types": "^2.12.0", 1604 | "@smithy/util-stream": "^2.2.0", 1605 | "tslib": "^2.6.2" 1606 | }, 1607 | "engines": { 1608 | "node": ">=14.0.0" 1609 | } 1610 | }, 1611 | "node_modules/@smithy/types": { 1612 | "version": "2.12.0", 1613 | "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", 1614 | "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", 1615 | "dependencies": { 1616 | "tslib": "^2.6.2" 1617 | }, 1618 | "engines": { 1619 | "node": ">=14.0.0" 1620 | } 1621 | }, 1622 | "node_modules/@smithy/url-parser": { 1623 | "version": "2.2.0", 1624 | "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.2.0.tgz", 1625 | "integrity": "sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==", 1626 | "dependencies": { 1627 | "@smithy/querystring-parser": "^2.2.0", 1628 | "@smithy/types": "^2.12.0", 1629 | "tslib": "^2.6.2" 1630 | } 1631 | }, 1632 | "node_modules/@smithy/util-base64": { 1633 | "version": "2.3.0", 1634 | "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.3.0.tgz", 1635 | "integrity": "sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==", 1636 | "dependencies": { 1637 | "@smithy/util-buffer-from": "^2.2.0", 1638 | "@smithy/util-utf8": "^2.3.0", 1639 | "tslib": "^2.6.2" 1640 | }, 1641 | "engines": { 1642 | "node": ">=14.0.0" 1643 | } 1644 | }, 1645 | "node_modules/@smithy/util-body-length-browser": { 1646 | "version": "2.2.0", 1647 | "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.2.0.tgz", 1648 | "integrity": "sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w==", 1649 | "dependencies": { 1650 | "tslib": "^2.6.2" 1651 | } 1652 | }, 1653 | "node_modules/@smithy/util-body-length-node": { 1654 | "version": "2.3.0", 1655 | "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.3.0.tgz", 1656 | "integrity": "sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw==", 1657 | "dependencies": { 1658 | "tslib": "^2.6.2" 1659 | }, 1660 | "engines": { 1661 | "node": ">=14.0.0" 1662 | } 1663 | }, 1664 | "node_modules/@smithy/util-buffer-from": { 1665 | "version": "2.2.0", 1666 | "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", 1667 | "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", 1668 | "dependencies": { 1669 | "@smithy/is-array-buffer": "^2.2.0", 1670 | "tslib": "^2.6.2" 1671 | }, 1672 | "engines": { 1673 | "node": ">=14.0.0" 1674 | } 1675 | }, 1676 | "node_modules/@smithy/util-config-provider": { 1677 | "version": "2.3.0", 1678 | "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.3.0.tgz", 1679 | "integrity": "sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ==", 1680 | "dependencies": { 1681 | "tslib": "^2.6.2" 1682 | }, 1683 | "engines": { 1684 | "node": ">=14.0.0" 1685 | } 1686 | }, 1687 | "node_modules/@smithy/util-defaults-mode-browser": { 1688 | "version": "2.2.1", 1689 | "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.2.1.tgz", 1690 | "integrity": "sha512-RtKW+8j8skk17SYowucwRUjeh4mCtnm5odCL0Lm2NtHQBsYKrNW0od9Rhopu9wF1gHMfHeWF7i90NwBz/U22Kw==", 1691 | "dependencies": { 1692 | "@smithy/property-provider": "^2.2.0", 1693 | "@smithy/smithy-client": "^2.5.1", 1694 | "@smithy/types": "^2.12.0", 1695 | "bowser": "^2.11.0", 1696 | "tslib": "^2.6.2" 1697 | }, 1698 | "engines": { 1699 | "node": ">= 10.0.0" 1700 | } 1701 | }, 1702 | "node_modules/@smithy/util-defaults-mode-node": { 1703 | "version": "2.3.1", 1704 | "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.3.1.tgz", 1705 | "integrity": "sha512-vkMXHQ0BcLFysBMWgSBLSk3+leMpFSyyFj8zQtv5ZyUBx8/owVh1/pPEkzmW/DR/Gy/5c8vjLDD9gZjXNKbrpA==", 1706 | "dependencies": { 1707 | "@smithy/config-resolver": "^2.2.0", 1708 | "@smithy/credential-provider-imds": "^2.3.0", 1709 | "@smithy/node-config-provider": "^2.3.0", 1710 | "@smithy/property-provider": "^2.2.0", 1711 | "@smithy/smithy-client": "^2.5.1", 1712 | "@smithy/types": "^2.12.0", 1713 | "tslib": "^2.6.2" 1714 | }, 1715 | "engines": { 1716 | "node": ">= 10.0.0" 1717 | } 1718 | }, 1719 | "node_modules/@smithy/util-endpoints": { 1720 | "version": "1.2.0", 1721 | "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.2.0.tgz", 1722 | "integrity": "sha512-BuDHv8zRjsE5zXd3PxFXFknzBG3owCpjq8G3FcsXW3CykYXuEqM3nTSsmLzw5q+T12ZYuDlVUZKBdpNbhVtlrQ==", 1723 | "dependencies": { 1724 | "@smithy/node-config-provider": "^2.3.0", 1725 | "@smithy/types": "^2.12.0", 1726 | "tslib": "^2.6.2" 1727 | }, 1728 | "engines": { 1729 | "node": ">= 14.0.0" 1730 | } 1731 | }, 1732 | "node_modules/@smithy/util-hex-encoding": { 1733 | "version": "2.2.0", 1734 | "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.2.0.tgz", 1735 | "integrity": "sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==", 1736 | "dependencies": { 1737 | "tslib": "^2.6.2" 1738 | }, 1739 | "engines": { 1740 | "node": ">=14.0.0" 1741 | } 1742 | }, 1743 | "node_modules/@smithy/util-middleware": { 1744 | "version": "2.2.0", 1745 | "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.2.0.tgz", 1746 | "integrity": "sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==", 1747 | "dependencies": { 1748 | "@smithy/types": "^2.12.0", 1749 | "tslib": "^2.6.2" 1750 | }, 1751 | "engines": { 1752 | "node": ">=14.0.0" 1753 | } 1754 | }, 1755 | "node_modules/@smithy/util-retry": { 1756 | "version": "2.2.0", 1757 | "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.2.0.tgz", 1758 | "integrity": "sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==", 1759 | "dependencies": { 1760 | "@smithy/service-error-classification": "^2.1.5", 1761 | "@smithy/types": "^2.12.0", 1762 | "tslib": "^2.6.2" 1763 | }, 1764 | "engines": { 1765 | "node": ">= 14.0.0" 1766 | } 1767 | }, 1768 | "node_modules/@smithy/util-stream": { 1769 | "version": "2.2.0", 1770 | "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.2.0.tgz", 1771 | "integrity": "sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==", 1772 | "dependencies": { 1773 | "@smithy/fetch-http-handler": "^2.5.0", 1774 | "@smithy/node-http-handler": "^2.5.0", 1775 | "@smithy/types": "^2.12.0", 1776 | "@smithy/util-base64": "^2.3.0", 1777 | "@smithy/util-buffer-from": "^2.2.0", 1778 | "@smithy/util-hex-encoding": "^2.2.0", 1779 | "@smithy/util-utf8": "^2.3.0", 1780 | "tslib": "^2.6.2" 1781 | }, 1782 | "engines": { 1783 | "node": ">=14.0.0" 1784 | } 1785 | }, 1786 | "node_modules/@smithy/util-uri-escape": { 1787 | "version": "2.2.0", 1788 | "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.2.0.tgz", 1789 | "integrity": "sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==", 1790 | "dependencies": { 1791 | "tslib": "^2.6.2" 1792 | }, 1793 | "engines": { 1794 | "node": ">=14.0.0" 1795 | } 1796 | }, 1797 | "node_modules/@smithy/util-utf8": { 1798 | "version": "2.3.0", 1799 | "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", 1800 | "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", 1801 | "dependencies": { 1802 | "@smithy/util-buffer-from": "^2.2.0", 1803 | "tslib": "^2.6.2" 1804 | }, 1805 | "engines": { 1806 | "node": ">=14.0.0" 1807 | } 1808 | }, 1809 | "node_modules/@smithy/util-waiter": { 1810 | "version": "2.2.0", 1811 | "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.2.0.tgz", 1812 | "integrity": "sha512-IHk53BVw6MPMi2Gsn+hCng8rFA3ZmR3Rk7GllxDUW9qFJl/hiSvskn7XldkECapQVkIg/1dHpMAxI9xSTaLLSA==", 1813 | "dependencies": { 1814 | "@smithy/abort-controller": "^2.2.0", 1815 | "@smithy/types": "^2.12.0", 1816 | "tslib": "^2.6.2" 1817 | }, 1818 | "engines": { 1819 | "node": ">=14.0.0" 1820 | } 1821 | }, 1822 | "node_modules/bowser": { 1823 | "version": "2.11.0", 1824 | "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", 1825 | "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" 1826 | }, 1827 | "node_modules/esbuild": { 1828 | "version": "0.20.2", 1829 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", 1830 | "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", 1831 | "dev": true, 1832 | "hasInstallScript": true, 1833 | "bin": { 1834 | "esbuild": "bin/esbuild" 1835 | }, 1836 | "engines": { 1837 | "node": ">=12" 1838 | }, 1839 | "optionalDependencies": { 1840 | "@esbuild/aix-ppc64": "0.20.2", 1841 | "@esbuild/android-arm": "0.20.2", 1842 | "@esbuild/android-arm64": "0.20.2", 1843 | "@esbuild/android-x64": "0.20.2", 1844 | "@esbuild/darwin-arm64": "0.20.2", 1845 | "@esbuild/darwin-x64": "0.20.2", 1846 | "@esbuild/freebsd-arm64": "0.20.2", 1847 | "@esbuild/freebsd-x64": "0.20.2", 1848 | "@esbuild/linux-arm": "0.20.2", 1849 | "@esbuild/linux-arm64": "0.20.2", 1850 | "@esbuild/linux-ia32": "0.20.2", 1851 | "@esbuild/linux-loong64": "0.20.2", 1852 | "@esbuild/linux-mips64el": "0.20.2", 1853 | "@esbuild/linux-ppc64": "0.20.2", 1854 | "@esbuild/linux-riscv64": "0.20.2", 1855 | "@esbuild/linux-s390x": "0.20.2", 1856 | "@esbuild/linux-x64": "0.20.2", 1857 | "@esbuild/netbsd-x64": "0.20.2", 1858 | "@esbuild/openbsd-x64": "0.20.2", 1859 | "@esbuild/sunos-x64": "0.20.2", 1860 | "@esbuild/win32-arm64": "0.20.2", 1861 | "@esbuild/win32-ia32": "0.20.2", 1862 | "@esbuild/win32-x64": "0.20.2" 1863 | } 1864 | }, 1865 | "node_modules/fast-xml-parser": { 1866 | "version": "4.2.5", 1867 | "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", 1868 | "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", 1869 | "funding": [ 1870 | { 1871 | "type": "paypal", 1872 | "url": "https://paypal.me/naturalintelligence" 1873 | }, 1874 | { 1875 | "type": "github", 1876 | "url": "https://github.com/sponsors/NaturalIntelligence" 1877 | } 1878 | ], 1879 | "dependencies": { 1880 | "strnum": "^1.0.5" 1881 | }, 1882 | "bin": { 1883 | "fxparser": "src/cli/cli.js" 1884 | } 1885 | }, 1886 | "node_modules/strnum": { 1887 | "version": "1.0.5", 1888 | "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", 1889 | "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" 1890 | }, 1891 | "node_modules/tslib": { 1892 | "version": "2.6.2", 1893 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", 1894 | "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" 1895 | }, 1896 | "node_modules/typescript": { 1897 | "version": "5.4.5", 1898 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", 1899 | "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", 1900 | "dev": true, 1901 | "bin": { 1902 | "tsc": "bin/tsc", 1903 | "tsserver": "bin/tsserver" 1904 | }, 1905 | "engines": { 1906 | "node": ">=14.17" 1907 | } 1908 | }, 1909 | "node_modules/uuid": { 1910 | "version": "9.0.1", 1911 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", 1912 | "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", 1913 | "funding": [ 1914 | "https://github.com/sponsors/broofa", 1915 | "https://github.com/sponsors/ctavan" 1916 | ], 1917 | "bin": { 1918 | "uuid": "dist/bin/uuid" 1919 | } 1920 | } 1921 | } 1922 | } 1923 | --------------------------------------------------------------------------------