Reduce bundle size & check it in CI (#7395)

This commit is contained in:
Pascal Birchler 2025-09-04 23:00:27 +02:00 committed by GitHub
parent 35a841f71a
commit c38247ed5c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 184 additions and 432 deletions

View file

@ -371,3 +371,24 @@ jobs:
- name: 'Perform CodeQL Analysis'
uses: 'github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2' # ratchet:github/codeql-action/analyze@v3
# Check for changes in bundle size.
bundle_size:
name: 'Check Bundle Size'
runs-on: 'ubuntu-latest'
permissions:
contents: 'read' # For checkout
pull-requests: 'write' # For commenting
steps:
- name: 'Checkout'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
with:
fetch-depth: 1
- uses: 'preactjs/compressed-size-action@946a292cd35bd1088e0d7eb92b69d1a8d5b5d76a'
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
pattern: './bundle/**/*.{js,sb}'
minimum-change-threshold: '1000'
compression: 'none'

View file

@ -8,12 +8,24 @@ import esbuild from 'esbuild';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import { writeFileSync } from 'node:fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const require = createRequire(import.meta.url);
const pkg = require(path.resolve(__dirname, 'package.json'));
const external = [
'@lydell/node-pty',
'node-pty',
'@lydell/node-pty-darwin-arm64',
'@lydell/node-pty-darwin-x64',
'@lydell/node-pty-linux-x64',
'@lydell/node-pty-win32-arm64',
'@lydell/node-pty-win32-x64',
'node-fetch',
];
esbuild
.build({
entryPoints: ['packages/cli/index.ts'],
@ -21,15 +33,7 @@ esbuild
outfile: 'bundle/gemini.js',
platform: 'node',
format: 'esm',
external: [
'@lydell/node-pty',
'node-pty',
'@lydell/node-pty-darwin-arm64',
'@lydell/node-pty-darwin-x64',
'@lydell/node-pty-linux-x64',
'@lydell/node-pty-win32-arm64',
'@lydell/node-pty-win32-x64',
],
external,
alias: {
'is-in-ci': path.resolve(
__dirname,
@ -43,5 +47,12 @@ esbuild
js: `import { createRequire } from 'module'; const require = createRequire(import.meta.url); globalThis.__filename = require('url').fileURLToPath(import.meta.url); globalThis.__dirname = require('path').dirname(globalThis.__filename);`,
},
loader: { '.node': 'file' },
metafile: true,
write: true,
})
.then(({ metafile }) => {
if (process.env.DEV === 'true') {
writeFileSync('./bundle/esbuild.json', JSON.stringify(metafile, null, 2));
}
})
.catch(() => process.exit(1));

497
package-lock.json generated
View file

@ -12,8 +12,7 @@
],
"dependencies": {
"@lvce-editor/ripgrep": "^1.6.0",
"simple-git": "^3.28.0",
"strip-ansi": "^7.1.0"
"simple-git": "^3.28.0"
},
"bin": {
"gemini": "bundle/gemini.js"
@ -26,7 +25,6 @@
"@types/shell-quote": "^1.7.5",
"@vitest/coverage-v8": "^3.1.1",
"@vitest/eslint-plugin": "^1.3.4",
"concurrently": "^9.2.0",
"cross-env": "^7.0.3",
"esbuild": "^0.25.0",
"eslint": "^9.24.0",
@ -38,7 +36,6 @@
"glob": "^10.4.5",
"globals": "^16.0.0",
"json": "^11.0.0",
"lodash": "^4.17.21",
"memfs": "^4.17.2",
"mnemonist": "^0.40.3",
"mock-fs": "^5.5.0",
@ -3831,23 +3828,6 @@
"@types/node": "*"
}
},
"node_modules/@types/lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/marked": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz",
@ -6361,32 +6341,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/concurrently": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.0.tgz",
"integrity": "sha512-IsB/fiXTupmagMW4MNp2lx2cdSN2FfZq78vF90LBB+zZHArbIQZjQtzXCiXnvTxCZSvXanTqFLWBjw2UkLx1SQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2",
"lodash": "^4.17.21",
"rxjs": "^7.8.1",
"shell-quote": "^1.8.1",
"supports-color": "^8.1.1",
"tree-kill": "^1.2.2",
"yargs": "^17.7.2"
},
"bin": {
"conc": "dist/bin/concurrently.js",
"concurrently": "dist/bin/concurrently.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/config-chain": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
@ -7167,16 +7121,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.2.1"
}
},
"node_modules/es-abstract": {
"version": "1.24.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
@ -9635,13 +9579,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
"dev": true,
"license": "MIT"
},
"node_modules/is-async-function": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
@ -10400,12 +10337,15 @@
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"license": "MIT"
},
"node_modules/json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"node_modules/json-parse-even-better-errors": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz",
"integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==",
"dev": true,
"license": "MIT"
"license": "MIT",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
@ -10655,36 +10595,6 @@
"uc.micro": "^2.0.0"
}
},
"node_modules/load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
"integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.1.2",
"parse-json": "^4.0.0",
"pify": "^3.0.0",
"strip-bom": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/load-json-file/node_modules/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
"dev": true,
"license": "MIT",
"dependencies": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
},
"engines": {
"node": ">=4"
}
},
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@ -10708,12 +10618,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT"
},
"node_modules/lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
@ -11331,13 +11235,6 @@
"node": ">= 0.6"
}
},
"node_modules/nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true,
"license": "MIT"
},
"node_modules/node-abi": {
"version": "3.75.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz",
@ -11479,216 +11376,106 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/npm-run-all": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
"integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
"node_modules/npm-normalize-package-bin": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz",
"integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/npm-run-all2": {
"version": "8.0.4",
"resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz",
"integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^3.2.1",
"chalk": "^2.4.1",
"cross-spawn": "^6.0.5",
"ansi-styles": "^6.2.1",
"cross-spawn": "^7.0.6",
"memorystream": "^0.3.1",
"minimatch": "^3.0.4",
"pidtree": "^0.3.0",
"read-pkg": "^3.0.0",
"shell-quote": "^1.6.1",
"string.prototype.padend": "^3.0.0"
"picomatch": "^4.0.2",
"pidtree": "^0.6.0",
"read-package-json-fast": "^4.0.0",
"shell-quote": "^1.7.3",
"which": "^5.0.0"
},
"bin": {
"npm-run-all": "bin/npm-run-all/index.js",
"npm-run-all2": "bin/npm-run-all/index.js",
"run-p": "bin/run-p/index.js",
"run-s": "bin/run-s/index.js"
},
"engines": {
"node": ">= 4"
"node": "^20.5.0 || >=22.0.0",
"npm": ">= 10"
}
},
"node_modules/npm-run-all/node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"node_modules/npm-run-all2/node_modules/ansi-styles": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^1.9.0"
"engines": {
"node": ">=12"
},
"engines": {
"node": ">=4"
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/npm-run-all/node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/npm-run-all/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/npm-run-all/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true,
"license": "MIT"
},
"node_modules/npm-run-all/node_modules/cross-spawn": {
"version": "6.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz",
"integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==",
"dev": true,
"license": "MIT",
"dependencies": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
},
"engines": {
"node": ">=4.8"
}
},
"node_modules/npm-run-all/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/npm-run-all/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/npm-run-all/node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true,
"license": "ISC"
},
"node_modules/npm-run-all/node_modules/normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"hosted-git-info": "^2.1.4",
"resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
}
},
"node_modules/npm-run-all/node_modules/path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
"integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/npm-run-all/node_modules/read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
"integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==",
"dev": true,
"license": "MIT",
"dependencies": {
"load-json-file": "^4.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/npm-run-all/node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"node_modules/npm-run-all2/node_modules/isexe": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
"integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=16"
}
},
"node_modules/npm-run-all2/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/npm-run-all2/node_modules/pidtree": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
"dev": true,
"license": "MIT",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/npm-run-all/node_modules/shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
"dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^1.0.0"
"pidtree": "bin/pidtree.js"
},
"engines": {
"node": ">=0.10.0"
"node": ">=0.10"
}
},
"node_modules/npm-run-all/node_modules/shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/npm-run-all/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/npm-run-all/node_modules/which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"node_modules/npm-run-all2/node_modules/which": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
"integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
"isexe": "^3.1.1"
},
"bin": {
"which": "bin/which"
"node-which": "bin/which.js"
},
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/npm-run-path": {
@ -12279,19 +12066,6 @@
"license": "MIT",
"peer": true
},
"node_modules/path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
"integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dev": true,
"license": "MIT",
"dependencies": {
"pify": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@ -12343,29 +12117,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pidtree": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
"integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
"dev": true,
"license": "MIT",
"bin": {
"pidtree": "bin/pidtree.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/pkce-challenge": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
@ -12863,6 +12614,20 @@
"node": ">=0.8"
}
},
"node_modules/read-package-json-fast": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz",
"integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==",
"dev": true,
"license": "ISC",
"dependencies": {
"json-parse-even-better-errors": "^4.0.0",
"npm-normalize-package-bin": "^4.0.0"
},
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/read-package-up": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
@ -13289,16 +13054,6 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rxjs": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/safe-array-concat": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
@ -14098,25 +13853,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/string.prototype.padend": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz",
"integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"define-properties": "^1.2.1",
"es-abstract": "^1.23.2",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/string.prototype.repeat": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
@ -14352,22 +14088,6 @@
"node": ">=14.18.0"
}
},
"node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/supports-hyperlinks": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz",
@ -14972,16 +14692,6 @@
"tslib": "2"
}
},
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true,
"license": "MIT",
"bin": {
"tree-kill": "cli.js"
}
},
"node_modules/triple-beam": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
@ -16684,9 +16394,7 @@
"ink": "^6.2.3",
"ink-gradient": "^3.0.0",
"ink-spinner": "^5.0.0",
"lodash-es": "^4.17.21",
"lowlight": "^3.3.0",
"mime-types": "^3.0.1",
"open": "^10.1.2",
"react": "^19.1.0",
"read-package-up": "^11.0.0",
@ -16710,7 +16418,6 @@
"@types/command-exists": "^1.2.3",
"@types/diff": "^7.0.2",
"@types/dotenv": "^6.1.1",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.11.24",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
@ -16894,6 +16601,7 @@
"https-proxy-agent": "^7.0.6",
"ignore": "^7.0.0",
"marked": "^15.0.12",
"mime": "4.0.7",
"mnemonist": "^0.40.3",
"open": "^10.1.2",
"picomatch": "^4.0.1",
@ -16972,6 +16680,21 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"packages/core/node_modules/mime": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz",
"integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==",
"funding": [
"https://github.com/sponsors/broofa"
],
"license": "MIT",
"bin": {
"mime": "bin/cli.js"
},
"engines": {
"node": ">=16"
}
},
"packages/core/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
@ -17015,7 +16738,7 @@
"@vscode/vsce": "^3.6.0",
"esbuild": "^0.25.3",
"eslint": "^9.25.1",
"npm-run-all": "^4.1.5",
"npm-run-all2": "^8.0.2",
"typescript": "^5.8.3",
"vitest": "^3.2.4"
},

View file

@ -68,7 +68,6 @@
"@types/shell-quote": "^1.7.5",
"@vitest/coverage-v8": "^3.1.1",
"@vitest/eslint-plugin": "^1.3.4",
"concurrently": "^9.2.0",
"cross-env": "^7.0.3",
"esbuild": "^0.25.0",
"eslint": "^9.24.0",
@ -80,7 +79,6 @@
"glob": "^10.4.5",
"globals": "^16.0.0",
"json": "^11.0.0",
"lodash": "^4.17.21",
"memfs": "^4.17.2",
"mnemonist": "^0.40.3",
"mock-fs": "^5.5.0",
@ -94,8 +92,7 @@
},
"dependencies": {
"@lvce-editor/ripgrep": "^1.6.0",
"simple-git": "^3.28.0",
"strip-ansi": "^7.1.0"
"simple-git": "^3.28.0"
},
"optionalDependencies": {
"@lydell/node-pty": "1.1.0",

View file

@ -42,9 +42,7 @@
"ink": "^6.2.3",
"ink-gradient": "^3.0.0",
"ink-spinner": "^5.0.0",
"lodash-es": "^4.17.21",
"lowlight": "^3.3.0",
"mime-types": "^3.0.1",
"open": "^10.1.2",
"react": "^19.1.0",
"read-package-up": "^11.0.0",
@ -65,7 +63,6 @@
"@types/command-exists": "^1.2.3",
"@types/diff": "^7.0.2",
"@types/dotenv": "^6.1.1",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.11.24",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",

View file

@ -50,6 +50,7 @@
"https-proxy-agent": "^7.0.6",
"ignore": "^7.0.0",
"marked": "^15.0.12",
"mime": "4.0.7",
"mnemonist": "^0.40.3",
"open": "^10.1.2",
"picomatch": "^4.0.1",

View file

@ -24,8 +24,8 @@ import * as glob from 'glob';
vi.mock('glob', { spy: true });
vi.mock('mime-types', () => {
const lookup = (filename: string) => {
vi.mock('mime', () => {
const getType = (filename: string) => {
if (filename.endsWith('.ts') || filename.endsWith('.js')) {
return 'text/plain';
}
@ -45,9 +45,9 @@ vi.mock('mime-types', () => {
};
return {
default: {
lookup,
getType,
},
lookup,
getType,
};
});

View file

@ -18,7 +18,8 @@ import * as actualNodeFs from 'node:fs'; // For setup/teardown
import fsPromises from 'node:fs/promises';
import path from 'node:path';
import os from 'node:os';
import mime from 'mime-types';
// eslint-disable-next-line import/no-internal-modules
import mime from 'mime/lite';
import {
isWithinRoot,
@ -30,12 +31,12 @@ import {
} from './fileUtils.js';
import { StandardFileSystemService } from '../services/fileSystemService.js';
vi.mock('mime-types', () => ({
default: { lookup: vi.fn() },
lookup: vi.fn(),
vi.mock('mime/lite', () => ({
default: { getType: vi.fn() },
getType: vi.fn(),
}));
const mockMimeLookup = mime.lookup as Mock;
const mockMimeGetType = mime.getType as Mock;
describe('fileUtils', () => {
let tempRootDir: string;
@ -49,7 +50,7 @@ describe('fileUtils', () => {
let directoryPath: string;
beforeEach(() => {
vi.resetAllMocks(); // Reset all mocks, including mime.lookup
vi.resetAllMocks(); // Reset all mocks, including mime.getType
tempRootDir = actualNodeFs.mkdtempSync(
path.join(os.tmpdir(), 'fileUtils-test-'),
@ -570,12 +571,12 @@ describe('fileUtils', () => {
});
it('should detect image type by extension (png)', async () => {
mockMimeLookup.mockReturnValueOnce('image/png');
mockMimeGetType.mockReturnValueOnce('image/png');
expect(await detectFileType('file.png')).toBe('image');
});
it('should detect image type by extension (jpeg)', async () => {
mockMimeLookup.mockReturnValueOnce('image/jpeg');
mockMimeGetType.mockReturnValueOnce('image/jpeg');
expect(await detectFileType('file.jpg')).toBe('image');
});
@ -585,31 +586,31 @@ describe('fileUtils', () => {
});
it('should detect pdf type by extension', async () => {
mockMimeLookup.mockReturnValueOnce('application/pdf');
mockMimeGetType.mockReturnValueOnce('application/pdf');
expect(await detectFileType('file.pdf')).toBe('pdf');
});
it('should detect audio type by extension', async () => {
mockMimeLookup.mockReturnValueOnce('audio/mpeg');
mockMimeGetType.mockReturnValueOnce('audio/mpeg');
expect(await detectFileType('song.mp3')).toBe('audio');
});
it('should detect video type by extension', async () => {
mockMimeLookup.mockReturnValueOnce('video/mp4');
mockMimeGetType.mockReturnValueOnce('video/mp4');
expect(await detectFileType('movie.mp4')).toBe('video');
});
it('should detect known binary extensions as binary (e.g. .zip)', async () => {
mockMimeLookup.mockReturnValueOnce('application/zip');
mockMimeGetType.mockReturnValueOnce('application/zip');
expect(await detectFileType('archive.zip')).toBe('binary');
});
it('should detect known binary extensions as binary (e.g. .exe)', async () => {
mockMimeLookup.mockReturnValueOnce('application/octet-stream'); // Common for .exe
mockMimeGetType.mockReturnValueOnce('application/octet-stream'); // Common for .exe
expect(await detectFileType('app.exe')).toBe('binary');
});
it('should use isBinaryFile for unknown extensions and detect as binary', async () => {
mockMimeLookup.mockReturnValueOnce(false); // Unknown mime type
mockMimeGetType.mockReturnValueOnce(false); // Unknown mime type
// Create a file that isBinaryFile will identify as binary
const binaryContent = Buffer.from([
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
@ -619,7 +620,7 @@ describe('fileUtils', () => {
});
it('should default to text if mime type is unknown and content is not binary', async () => {
mockMimeLookup.mockReturnValueOnce(false); // Unknown mime type
mockMimeGetType.mockReturnValueOnce(false); // Unknown mime type
// filePathForDetectTest is already a text file by default from beforeEach
expect(await detectFileType(filePathForDetectTest)).toBe('text');
});
@ -677,7 +678,7 @@ describe('fileUtils', () => {
it('should handle read errors for image/pdf files', async () => {
actualNodeFs.writeFileSync(testImageFilePath, 'content'); // File must exist
mockMimeLookup.mockReturnValue('image/png');
mockMimeGetType.mockReturnValue('image/png');
const readError = new Error('Simulated image read error');
vi.spyOn(fsPromises, 'readFile').mockRejectedValueOnce(readError);
@ -693,7 +694,7 @@ describe('fileUtils', () => {
it('should process an image file', async () => {
const fakePngData = Buffer.from('fake png data');
actualNodeFs.writeFileSync(testImageFilePath, fakePngData);
mockMimeLookup.mockReturnValue('image/png');
mockMimeGetType.mockReturnValue('image/png');
const result = await processSingleFileContent(
testImageFilePath,
tempRootDir,
@ -715,7 +716,7 @@ describe('fileUtils', () => {
it('should process a PDF file', async () => {
const fakePdfData = Buffer.from('fake pdf data');
actualNodeFs.writeFileSync(testPdfFilePath, fakePdfData);
mockMimeLookup.mockReturnValue('application/pdf');
mockMimeGetType.mockReturnValue('application/pdf');
const result = await processSingleFileContent(
testPdfFilePath,
tempRootDir,
@ -743,7 +744,7 @@ describe('fileUtils', () => {
const testSvgFilePath = path.join(tempRootDir, 'test.svg');
actualNodeFs.writeFileSync(testSvgFilePath, svgContent, 'utf-8');
mockMimeLookup.mockReturnValue('image/svg+xml');
mockMimeGetType.mockReturnValue('image/svg+xml');
const result = await processSingleFileContent(
testSvgFilePath,
@ -760,7 +761,7 @@ describe('fileUtils', () => {
testBinaryFilePath,
Buffer.from([0x00, 0x01, 0x02]),
);
mockMimeLookup.mockReturnValueOnce('application/octet-stream');
mockMimeGetType.mockReturnValueOnce('application/octet-stream');
// isBinaryFile will operate on the real file.
const result = await processSingleFileContent(

View file

@ -7,7 +7,8 @@
import fs from 'node:fs';
import path from 'node:path';
import type { PartUnion } from '@google/genai';
import mime from 'mime-types';
// eslint-disable-next-line import/no-internal-modules
import mime from 'mime/lite';
import type { FileSystemService } from '../services/fileSystemService.js';
import { ToolErrorType } from '../tools/tool-error.js';
import { BINARY_EXTENSIONS } from './ignorePatterns.js';
@ -157,7 +158,7 @@ export async function readFileWithEncoding(filePath: string): Promise<string> {
* @returns The specific MIME type string (e.g., 'text/python', 'application/javascript') or undefined if not found or ambiguous.
*/
export function getSpecificMimeType(filePath: string): string | undefined {
const lookedUpMime = mime.lookup(filePath);
const lookedUpMime = mime.getType(filePath);
return typeof lookedUpMime === 'string' ? lookedUpMime : undefined;
}
@ -261,7 +262,7 @@ export async function detectFileType(
return 'svg';
}
const lookedUpMimeType = mime.lookup(filePath); // Returns false if not found, or the mime type string
const lookedUpMimeType = mime.getType(filePath); // Returns null if not found, or the mime type string
if (lookedUpMimeType) {
if (lookedUpMimeType.startsWith('image/')) {
return 'image';
@ -437,7 +438,7 @@ export async function processSingleFileContent(
llmContent: {
inlineData: {
data: base64Data,
mimeType: mime.lookup(filePath) || 'application/octet-stream',
mimeType: mime.getType(filePath) || 'application/octet-stream',
},
},
returnDisplay: `Read ${fileType} file: ${relativePathForDisplay}`,

View file

@ -119,7 +119,7 @@
"@vscode/vsce": "^3.6.0",
"esbuild": "^0.25.3",
"eslint": "^9.25.1",
"npm-run-all": "^4.1.5",
"npm-run-all2": "^8.0.2",
"typescript": "^5.8.3",
"vitest": "^3.2.4"
},