Cache windows executables (#282265)

fixes #279262
This commit is contained in:
Megan Rogge
2025-12-10 11:59:55 -05:00
committed by GitHub
parent 0c022544b1
commit c05e0b2311
3 changed files with 116 additions and 25 deletions

View File

@@ -6,10 +6,10 @@
import { osIsWindows } from './os';
import * as fs from 'fs/promises';
export function isExecutable(filePath: string, configuredWindowsExecutableExtensions?: { [key: string]: boolean | undefined } | undefined): Promise<boolean> | boolean {
export function isExecutable(filePath: string, windowsExecutableExtensions?: Set<string>): Promise<boolean> | boolean {
if (osIsWindows()) {
const resolvedWindowsExecutableExtensions = resolveWindowsExecutableExtensions(configuredWindowsExecutableExtensions);
return resolvedWindowsExecutableExtensions.find(ext => filePath.endsWith(ext)) !== undefined;
const extensions = windowsExecutableExtensions ?? defaultWindowsExecutableExtensionsSet;
return hasWindowsExecutableExtension(filePath, extensions);
}
return isExecutableUnix(filePath);
}
@@ -25,22 +25,6 @@ export async function isExecutableUnix(filePath: string): Promise<boolean> {
}
}
function resolveWindowsExecutableExtensions(configuredWindowsExecutableExtensions?: { [key: string]: boolean | undefined }): string[] {
const resolvedWindowsExecutableExtensions: string[] = windowsDefaultExecutableExtensions;
const excluded = new Set<string>();
if (configuredWindowsExecutableExtensions) {
for (const [key, value] of Object.entries(configuredWindowsExecutableExtensions)) {
if (value === true) {
resolvedWindowsExecutableExtensions.push(key);
} else {
excluded.add(key);
}
}
}
return Array.from(new Set(resolvedWindowsExecutableExtensions)).filter(ext => !excluded.has(ext));
}
export const windowsDefaultExecutableExtensions: string[] = [
'.exe', // Executable file
'.bat', // Batch file
@@ -59,3 +43,65 @@ export const windowsDefaultExecutableExtensions: string[] = [
'.pl', // Perl script (requires Perl interpreter)
'.sh', // Shell script (via WSL or third-party tools)
];
const defaultWindowsExecutableExtensionsSet = new Set<string>();
for (const ext of windowsDefaultExecutableExtensions) {
defaultWindowsExecutableExtensionsSet.add(ext);
}
export class WindowsExecutableExtensionsCache {
private _rawConfig: { [key: string]: boolean | undefined } | undefined;
private _cachedExtensions: Set<string> | undefined;
constructor(rawConfig?: { [key: string]: boolean | undefined }) {
this._rawConfig = rawConfig;
}
update(rawConfig: { [key: string]: boolean | undefined } | undefined): void {
this._rawConfig = rawConfig;
this._cachedExtensions = undefined;
}
getExtensions(): Set<string> {
if (!this._cachedExtensions) {
this._cachedExtensions = resolveWindowsExecutableExtensions(this._rawConfig);
}
return this._cachedExtensions;
}
}
function hasWindowsExecutableExtension(filePath: string, extensions: Set<string>): boolean {
const fileName = filePath.slice(Math.max(filePath.lastIndexOf('\\'), filePath.lastIndexOf('/')) + 1);
for (const ext of extensions) {
if (fileName.endsWith(ext)) {
return true;
}
}
return false;
}
function resolveWindowsExecutableExtensions(configuredWindowsExecutableExtensions?: { [key: string]: boolean | undefined }): Set<string> {
const extensions = new Set<string>();
const configured = configuredWindowsExecutableExtensions ?? {};
const excluded = new Set<string>();
for (const [ext, value] of Object.entries(configured)) {
if (value !== true) {
excluded.add(ext);
}
}
for (const ext of windowsDefaultExecutableExtensions) {
if (!excluded.has(ext)) {
extensions.add(ext);
}
}
for (const [ext, value] of Object.entries(configured)) {
if (value === true) {
extensions.add(ext);
}
}
return extensions;
}