Merge remote-tracking branch 'origin/main' into joh/esbuild-the-things

This commit is contained in:
Johannes
2026-02-11 09:52:59 +01:00
153 changed files with 5660 additions and 4085 deletions

View File

@@ -273,11 +273,33 @@ gulp.task(compileWebExtensionsTask);
export const watchWebExtensionsTask = task.define('watch-web', () => buildWebExtensions(true));
gulp.task(watchWebExtensionsTask);
async function buildWebExtensions(isWatch: boolean) {
async function buildWebExtensions(isWatch: boolean): Promise<void> {
const extensionsPath = path.join(root, 'extensions');
const webpackConfigLocations = await nodeUtil.promisify(glob)(
path.join(extensionsPath, '**', 'extension-browser.webpack.config.js'),
// Find all esbuild-browser.ts files
const esbuildConfigLocations = await nodeUtil.promisify(glob)(
path.join(extensionsPath, '**', 'esbuild-browser.ts'),
{ ignore: ['**/node_modules'] }
);
return ext.webpackExtensions('packaging web extension', isWatch, webpackConfigLocations.map(configPath => ({ configPath })));
// Find all webpack configs, excluding those that will be esbuilt
const esbuildExtensionDirs = new Set(esbuildConfigLocations.map(p => path.dirname(p)));
const webpackConfigLocations = (await nodeUtil.promisify(glob)(
path.join(extensionsPath, '**', 'extension-browser.webpack.config.js'),
{ ignore: ['**/node_modules'] }
)).filter(configPath => !esbuildExtensionDirs.has(path.dirname(configPath)));
const promises: Promise<unknown>[] = [];
// Esbuild for extensions
if (esbuildConfigLocations.length > 0) {
promises.push(ext.esbuildExtensions('packaging web extension (esbuild)', isWatch, esbuildConfigLocations.map(script => ({ script }))));
}
// Run webpack for remaining extensions
if (webpackConfigLocations.length > 0) {
promises.push(ext.webpackExtensions('packaging web extension', isWatch, webpackConfigLocations.map(configPath => ({ configPath }))));
}
await Promise.all(promises);
}

View File

@@ -66,16 +66,31 @@ function updateExtensionPackageJSON(input: Stream, update: (data: any) => any):
function fromLocal(extensionPath: string, forWeb: boolean, disableMangle: boolean): Stream {
const esbuildConfigFileName = forWeb
? 'esbuild-browser.ts'
: 'esbuild.ts';
const webpackConfigFileName = forWeb
? `extension-browser.webpack.config.js`
: `extension.webpack.config.js`;
const hasEsbuild = fs.existsSync(path.join(extensionPath, esbuildConfigFileName));
const isWebPacked = fs.existsSync(path.join(extensionPath, webpackConfigFileName));
let input = isWebPacked
? fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle)
: fromLocalNormal(extensionPath);
if (isWebPacked) {
let input: Stream;
let isBundled = false;
if (hasEsbuild) {
input = fromLocalEsbuild(extensionPath, esbuildConfigFileName);
isBundled = true;
} else if (isWebPacked) {
input = fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle);
isBundled = true;
} else {
input = fromLocalNormal(extensionPath);
}
if (isBundled) {
input = updateExtensionPackageJSON(input, (data: any) => {
delete data.scripts;
delete data.dependencies;
@@ -240,6 +255,51 @@ function fromLocalNormal(extensionPath: string): Stream {
return result.pipe(createStatsStream(path.basename(extensionPath)));
}
function fromLocalEsbuild(extensionPath: string, esbuildConfigFileName: string): Stream {
const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce');
const result = es.through();
const esbuildScript = path.join(extensionPath, esbuildConfigFileName);
// Run esbuild, then collect the files
new Promise<void>((resolve, reject) => {
const proc = cp.execFile(process.argv[0], [esbuildScript], {}, (error, _stdout, stderr) => {
if (error) {
return reject(error);
}
const matches = (stderr || '').match(/\> (.+): error: (.+)?/g);
fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), esbuildConfigFileName))} with ${matches ? matches.length : 0} errors.`);
for (const match of matches || []) {
fancyLog.error(match);
}
return resolve();
});
proc.stdout!.on('data', (data) => {
fancyLog(`${ansiColors.green('esbuilding')}: ${data.toString('utf8')}`);
});
}).then(() => {
// After esbuild completes, collect all files using vsce
return vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.None });
}).then(fileNames => {
const files = fileNames
.map(fileName => path.join(extensionPath, fileName))
.map(filePath => new File({
path: filePath,
stat: fs.statSync(filePath),
base: extensionPath,
contents: fs.createReadStream(filePath)
}));
es.readArray(files).pipe(result);
}).catch(err => {
console.error(extensionPath);
result.emit('error', err);
});
return result.pipe(createStatsStream(path.basename(extensionPath)));
}
const userAgent = 'VSCode Build';
const baseHeaders = {
'X-Market-Client-Id': 'VSCode Build',
@@ -647,7 +707,7 @@ export async function webpackExtensions(taskName: string, isWatch: boolean, webp
});
}
async function esbuildExtensions(taskName: string, isWatch: boolean, scripts: { script: string; outputRoot?: string }[]) {
export async function esbuildExtensions(taskName: string, isWatch: boolean, scripts: { script: string; outputRoot?: string }[]): Promise<void> {
function reporter(stdError: string, script: string) {
const matches = (stdError || '').match(/\> (.+): error: (.+)?/g);
fancyLog(`Finished ${ansiColors.green(taskName)} ${script} with ${matches ? matches.length : 0} errors.`);
@@ -678,10 +738,11 @@ async function esbuildExtensions(taskName: string, isWatch: boolean, scripts: {
});
});
});
return Promise.all(tasks);
await Promise.all(tasks);
}
export async function buildExtensionMedia(isWatch: boolean, outputRoot?: string) {
export function buildExtensionMedia(isWatch: boolean, outputRoot?: string): Promise<void> {
return esbuildExtensions('esbuilding extension media', isWatch, esbuildMediaScripts.map(p => ({
script: path.join(extensionsPath, p),
outputRoot: outputRoot ? path.join(root, outputRoot, path.dirname(p)) : undefined