Adopt esbuild instead of webpack for a few more extensions

Adopting for configuration-editing, emmet, grunt, jake, and npm
This commit is contained in:
Matt Bierner
2026-02-12 12:31:13 -08:00
parent c13b565ab7
commit 2e81391ad3
35 changed files with 275 additions and 212 deletions

View File

@@ -1,9 +1,8 @@
test/**
src/**
tsconfig.json
tsconfig*.json
out/**
extension.webpack.config.js
extension-browser.webpack.config.js
esbuild*
package-lock.json
build/**
schemas/devContainer.codespaces.schema.json

View File

@@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'node:path';
import type { Plugin } from 'esbuild';
import { run } from '../esbuild-extension-common.mts';
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
/**
* Plugin to redirect `./node/net` imports to `./browser/net` for the browser build.
*/
const browserNetPlugin: Plugin = {
name: 'browser-net-redirect',
setup(build) {
build.onResolve({ filter: /\/node\/net$/ }, args => {
return { path: path.join(path.dirname(args.resolveDir), 'src', 'browser', 'net.ts') };
});
},
};
run({
platform: 'browser',
entryPoints: {
'configurationEditingMain': path.join(srcDir, 'configurationEditingMain.ts'),
},
srcDir,
outdir: outDir,
additionalOptions: {
plugins: [browserNetPlugin],
tsconfig: path.join(import.meta.dirname, 'tsconfig.browser.json'),
},
}, process.argv);

View File

@@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'node:path';
import { run } from '../esbuild-extension-common.mts';
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist');
run({
platform: 'node',
entryPoints: {
'configurationEditingMain': path.join(srcDir, 'configurationEditingMain.ts'),
},
srcDir,
outdir: outDir,
}, process.argv);

View File

@@ -1,23 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import path from 'path';
import { browser as withBrowserDefaults } from '../shared.webpack.config.mjs';
export default withBrowserDefaults({
context: import.meta.dirname,
entry: {
extension: './src/configurationEditingMain.ts'
},
output: {
filename: 'configurationEditingMain.js'
},
resolve: {
alias: {
'./node/net': path.resolve(import.meta.dirname, 'src', 'browser', 'net'),
}
}
});

View File

@@ -22,7 +22,13 @@
"browser": "./dist/browser/configurationEditingMain",
"scripts": {
"compile": "gulp compile-extension:configuration-editing",
"watch": "gulp watch-extension:configuration-editing"
"watch": "gulp watch-extension:configuration-editing",
"compile-web": "npm-run-all2 -lp bundle-web typecheck-web",
"bundle-web": "node ./esbuild.browser.mts",
"typecheck-web": "tsgo --project ./tsconfig.browser.json --noEmit",
"watch-web": "npm-run-all2 -lp watch-bundle-web watch-typecheck-web",
"watch-bundle-web": "node ./esbuild.browser.mts --watch",
"watch-typecheck-web": "tsgo --project ./tsconfig.browser.json --noEmit --watch"
},
"dependencies": {
"@octokit/rest": "^21.1.1",

View File

@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {},
"exclude": [
"./src/test/**"
],
"files": [
"./src/configurationEditingMain.ts"
]
}

View File

@@ -2,9 +2,8 @@ test/**
test-workspace/**
src/**
out/**
tsconfig.json
extension.webpack.config.js
extension-browser.webpack.config.js
tsconfig*.json
esbuild*
CONTRIBUTING.md
cgmanifest.json
package-lock.json

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'node:path';
import { run } from '../esbuild-extension-common.mts';
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
run({
platform: 'browser',
entryPoints: {
'emmetBrowserMain': path.join(srcDir, 'browser', 'emmetBrowserMain.ts'),
},
srcDir,
outdir: outDir,
additionalOptions: {
tsconfig: path.join(import.meta.dirname, 'tsconfig.browser.json'),
},
}, process.argv);

View File

@@ -2,22 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import { browser as withBrowserDefaults } from '../shared.webpack.config.mjs';
import * as path from 'node:path';
import { run } from '../esbuild-extension-common.mts';
const config = withBrowserDefaults({
context: import.meta.dirname,
entry: {
extension: './src/npmBrowserMain.ts'
},
output: {
filename: 'npmBrowserMain.js'
},
resolve: {
fallback: {
'child_process': false
}
}
});
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist', 'node');
export default config;
run({
platform: 'node',
entryPoints: {
'emmetNodeMain': path.join(srcDir, 'node', 'emmetNodeMain.ts'),
},
srcDir,
outdir: outDir,
}, process.argv);

View File

@@ -474,8 +474,14 @@
}
},
"scripts": {
"watch": "gulp watch-extension:emmet",
"compile": "gulp compile-extension:emmet",
"watch": "gulp watch-extension:emmet",
"compile-web": "npm-run-all2 -lp bundle-web typecheck-web",
"bundle-web": "node ./esbuild.browser.mts",
"typecheck-web": "tsgo --project ./tsconfig.browser.json --noEmit",
"watch-web": "npm-run-all2 -lp watch-bundle-web watch-typecheck-web",
"watch-bundle-web": "node ./esbuild.browser.mts --watch",
"watch-typecheck-web": "tsgo --project ./tsconfig.browser.json --noEmit --watch",
"deps": "npm install @vscode/emmet-helper"
},
"devDependencies": {

View File

@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {},
"exclude": [
"./src/test/**"
],
"files": [
"./src/browser/emmetBrowserMain.ts"
]
}

View File

@@ -16,17 +16,7 @@ type BuildOptions = Partial<esbuild.BuildOptions> & {
* Build the source code once using esbuild.
*/
async function build(options: BuildOptions, didBuild?: (outDir: string) => unknown): Promise<void> {
await esbuild.build({
bundle: true,
minify: true,
sourcemap: false,
format: 'cjs',
platform: 'node',
target: ['es2024'],
external: ['vscode'],
...options,
});
await esbuild.build(options);
await didBuild?.(options.outdir);
}
@@ -42,10 +32,44 @@ async function tryBuild(options: BuildOptions, didBuild?: (outDir: string) => un
}
interface RunConfig {
srcDir: string;
outdir: string;
entryPoints: string[] | Record<string, string> | { in: string; out: string }[];
additionalOptions?: Partial<esbuild.BuildOptions>;
readonly platform: 'node' | 'browser';
readonly srcDir: string;
readonly outdir: string;
readonly entryPoints: string[] | Record<string, string> | { in: string; out: string }[];
readonly additionalOptions?: Partial<esbuild.BuildOptions>;
}
function resolveOptions(config: RunConfig, outdir: string): BuildOptions {
const options: BuildOptions = {
platform: config.platform,
bundle: true,
minify: true,
sourcemap: true,
target: ['es2024'],
external: ['vscode'],
entryPoints: config.entryPoints,
outdir,
logOverride: {
'import-is-undefined': 'error',
},
...(config.additionalOptions || {}),
};
if (config.platform === 'node') {
options.format = 'cjs';
} else if (config.platform === 'browser') {
options.format = 'iife';
options.alias = {
'path': 'path-browserify',
};
options.define = {
'process.platform': JSON.stringify('web'),
'process.env': JSON.stringify({}),
'process.env.BROWSER_ENV': JSON.stringify('true'),
};
}
return options;
}
export async function run(config: RunConfig, args: string[], didBuild?: (outDir: string) => unknown): Promise<void> {
@@ -57,14 +81,7 @@ export async function run(config: RunConfig, args: string[], didBuild?: (outDir:
outdir = path.join(outputRoot, outputDirName);
}
const resolvedOptions: BuildOptions = {
entryPoints: config.entryPoints,
outdir,
logOverride: {
'import-is-undefined': 'error',
},
...(config.additionalOptions || {}),
};
const resolvedOptions = resolveOptions(config, outdir);
const isWatch = args.indexOf('--watch') >= 0;
if (isWatch) {

View File

@@ -1,6 +1,6 @@
test/**
src/**
tsconfig.json
tsconfig*.json
out/**
extension.webpack.config.js
esbuild*
package-lock.json

View File

@@ -2,16 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import { browser as withBrowserDefaults } from '../shared.webpack.config.mjs';
import * as path from 'node:path';
import { run } from '../esbuild-extension-common.mts';
export default withBrowserDefaults({
context: import.meta.dirname,
entry: {
extension: './src/browser/emmetBrowserMain.ts'
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist');
run({
platform: 'node',
entryPoints: {
'main': path.join(srcDir, 'main.ts'),
},
output: {
filename: 'emmetBrowserMain.js'
}
});
srcDir,
outdir: outDir,
}, process.argv);

View File

@@ -1,5 +1,5 @@
src/**
tsconfig.json
tsconfig*.json
out/**
extension.webpack.config.js
esbuild*
package-lock.json

View File

@@ -2,18 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import path from 'path';
import * as path from 'node:path';
import { run } from '../esbuild-extension-common.mts';
import withDefaults from '../shared.webpack.config.mjs';
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist');
export default withDefaults({
context: import.meta.dirname,
entry: {
extension: './src/node/emmetNodeMain.ts',
run({
platform: 'node',
entryPoints: {
'main': path.join(srcDir, 'main.ts'),
},
output: {
path: path.join(import.meta.dirname, 'dist', 'node'),
filename: 'emmetNodeMain.js'
}
});
srcDir,
outdir: outDir,
}, process.argv);

View File

@@ -1,16 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import withDefaults from '../shared.webpack.config.mjs';
export default withDefaults({
context: import.meta.dirname,
entry: {
main: './src/main.ts',
},
resolve: {
mainFields: ['module', 'main']
}
});

View File

@@ -1,5 +1,5 @@
src/**
tsconfig.json
tsconfig*.json
out/**
extension.webpack.config.js
esbuild*
package-lock.json

View File

@@ -2,15 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import withDefaults from '../shared.webpack.config.mjs';
import * as path from 'node:path';
import { run } from '../esbuild-extension-common.mts';
export default withDefaults({
context: import.meta.dirname,
entry: {
main: './src/main.ts',
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist');
run({
platform: 'node',
entryPoints: {
'main': path.join(srcDir, 'main.ts'),
},
resolve: {
mainFields: ['module', 'main']
}
});
srcDir,
outdir: outDir,
}, process.argv);

View File

@@ -1,16 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import withDefaults from '../shared.webpack.config.mjs';
export default withDefaults({
context: import.meta.dirname,
entry: {
main: './src/main.ts',
},
resolve: {
mainFields: ['module', 'main']
}
});

View File

@@ -19,22 +19,13 @@ async function copyServerWorkerMain(outDir: string): Promise<void> {
}
run({
platform: 'browser',
entryPoints: {
'extension': path.join(srcDir, 'extension.browser.ts'),
},
srcDir,
outdir: outDir,
additionalOptions: {
platform: 'browser',
format: 'cjs',
alias: {
'path': 'path-browserify',
},
define: {
'process.platform': JSON.stringify('web'),
'process.env': JSON.stringify({}),
'process.env.BROWSER_ENV': JSON.stringify('true'),
},
tsconfig: path.join(import.meta.dirname, 'tsconfig.browser.json'),
},
}, process.argv, copyServerWorkerMain);

View File

@@ -19,6 +19,7 @@ async function copyServerWorkerMain(outDir: string): Promise<void> {
}
run({
platform: 'node',
entryPoints: {
'extension': path.join(srcDir, 'extension.ts'),
},

View File

@@ -9,18 +9,13 @@ const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
run({
platform: 'browser',
entryPoints: {
'extension': path.join(srcDir, 'extension.ts'),
},
srcDir,
outdir: outDir,
additionalOptions: {
platform: 'browser',
format: 'cjs',
define: {
'process.platform': JSON.stringify('web'),
'process.env': JSON.stringify({}),
'process.env.BROWSER_ENV': JSON.stringify('true'),
},
tsconfig: path.join(import.meta.dirname, 'tsconfig.browser.json'),
},
}, process.argv);

View File

@@ -9,6 +9,7 @@ const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist');
run({
platform: 'node',
entryPoints: {
'extension': path.join(srcDir, 'extension.ts'),
},

View File

@@ -9,18 +9,13 @@ const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
run({
platform: 'browser',
entryPoints: {
'extension': path.join(srcDir, 'extension.ts'),
},
srcDir,
outdir: outDir,
additionalOptions: {
platform: 'browser',
format: 'cjs',
define: {
'process.platform': JSON.stringify('web'),
'process.env': JSON.stringify({}),
'process.env.BROWSER_ENV': JSON.stringify('true'),
},
tsconfig: path.join(import.meta.dirname, 'tsconfig.browser.json'),
},
}, process.argv);

View File

@@ -9,6 +9,7 @@ const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist');
run({
platform: 'node',
entryPoints: {
'extension': path.join(srcDir, 'extension.ts'),
},

View File

@@ -9,18 +9,13 @@ const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
run({
platform: 'browser',
entryPoints: {
'extension': path.join(srcDir, 'extension.ts'),
},
srcDir,
outdir: outDir,
additionalOptions: {
platform: 'browser',
format: 'cjs',
define: {
'process.platform': JSON.stringify('web'),
'process.env': JSON.stringify({}),
'process.env.BROWSER_ENV': JSON.stringify('true'),
},
tsconfig: path.join(import.meta.dirname, 'tsconfig.browser.json'),
},
}, process.argv);

View File

@@ -9,6 +9,7 @@ const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist');
run({
platform: 'node',
entryPoints: {
'extension': path.join(srcDir, 'extension.ts'),
},

View File

@@ -1,7 +1,6 @@
src/**
out/**
tsconfig.json
tsconfig*.json
.vscode/**
extension.webpack.config.js
extension-browser.webpack.config.js
esbuild*
package-lock.json

View File

@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'node:path';
import { run } from '../esbuild-extension-common.mts';
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
run({
platform: 'browser',
entryPoints: {
'npmBrowserMain': path.join(srcDir, 'npmBrowserMain.ts'),
},
srcDir,
outdir: outDir,
additionalOptions: {
tsconfig: path.join(import.meta.dirname, 'tsconfig.browser.json'),
external: [
'vscode',
'child_process',
]
},
}, process.argv);

View File

@@ -2,18 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import withDefaults from '../shared.webpack.config.mjs';
import * as path from 'node:path';
import { run } from '../esbuild-extension-common.mts';
export default withDefaults({
context: import.meta.dirname,
entry: {
extension: './src/configurationEditingMain.ts',
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist');
run({
platform: 'node',
entryPoints: {
'npmMain': path.join(srcDir, 'npmMain.ts'),
},
output: {
filename: 'configurationEditingMain.js'
},
resolve: {
mainFields: ['module', 'main']
}
});
srcDir,
outdir: outDir,
}, process.argv);

View File

@@ -1,20 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import withDefaults from '../shared.webpack.config.mjs';
export default withDefaults({
context: import.meta.dirname,
entry: {
extension: './src/npmMain.ts',
},
output: {
filename: 'npmMain.js',
},
resolve: {
mainFields: ['module', 'main'],
extensions: ['.ts', '.js'] // support ts-files and js-files
}
});

View File

@@ -18,7 +18,13 @@
],
"scripts": {
"compile": "npx gulp compile-extension:npm",
"watch": "npx gulp watch-extension:npm"
"watch": "npx gulp watch-extension:npm",
"compile-web": "npm-run-all2 -lp bundle-web typecheck-web",
"bundle-web": "node ./esbuild.browser.mts",
"typecheck-web": "tsgo --project ./tsconfig.browser.json --noEmit",
"watch-web": "npm-run-all2 -lp watch-bundle-web watch-typecheck-web",
"watch-bundle-web": "node ./esbuild.browser.mts --watch",
"watch-typecheck-web": "tsgo --project ./tsconfig.browser.json --noEmit --watch"
},
"dependencies": {
"find-up": "^5.0.0",

View File

@@ -8,7 +8,7 @@ import { IJSONContribution, ISuggestionsCollector } from './jsonContributions';
import { XHRRequest } from 'request-light';
import { Location } from 'jsonc-parser';
import * as cp from 'child_process';
import type * as cp from 'child_process';
import { dirname } from 'path';
import { fromNow } from './date';
@@ -283,7 +283,8 @@ export class PackageJSONContribution implements IJSONContribution {
return info;
}
private npmView(npmCommandPath: string, pack: string, resource: Uri | undefined): Promise<ViewPackageInfo | undefined> {
private async npmView(npmCommandPath: string, pack: string, resource: Uri | undefined): Promise<ViewPackageInfo | undefined> {
const cp = await import('child_process');
return new Promise((resolve, _reject) => {
const args = ['view', '--json', '--', pack, 'description', 'dist-tags.latest', 'homepage', 'version', 'time'];
const cwd = resource && resource.scheme === 'file' ? dirname(resource.fsPath) : undefined;

View File

@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {},
"exclude": [
"./src/test/**"
],
"files": [
"./src/npmBrowserMain.ts"
]
}