Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .eslint-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
**/extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts
**/extensions/terminal-suggest/third_party/**
**/extensions/typescript-language-features/test-workspace/**
**/extensions/typescript-language-features/extension.webpack.config.js
**/extensions/typescript-language-features/extension-browser.webpack.config.js
**/extensions/typescript-language-features/package-manager/node-maintainer/**
**/extensions/vscode-api-tests/testWorkspace/**
**/extensions/vscode-api-tests/testWorkspace2/**
Expand Down
12 changes: 0 additions & 12 deletions build/gulpfile.extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,6 @@ async function buildWebExtensions(isWatch: boolean): Promise<void> {
{ ignore: ['**/node_modules'] }
);

// 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
Expand All @@ -330,10 +323,5 @@ async function buildWebExtensions(isWatch: boolean): Promise<void> {
);
}

// 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);
}
208 changes: 4 additions & 204 deletions build/lib/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ import fancyLog from 'fancy-log';
import ansiColors from 'ansi-colors';
import buffer from 'gulp-buffer';
import * as jsoncParser from 'jsonc-parser';
import webpack from 'webpack';
import { getProductionDependencies } from './dependencies.ts';
import { type IExtensionDefinition, getExtensionStream } from './builtInExtensions.ts';
import { getVersion } from './getVersion.ts';
import { fetchUrls, fetchGithub } from './fetch.ts';
import { createTsgoStream, spawnTsgo } from './tsgo.ts';
import vzip from 'gulp-vinyl-zip';
Expand All @@ -32,8 +30,8 @@ import { createRequire } from 'module';
const require = createRequire(import.meta.url);

const root = path.dirname(path.dirname(import.meta.dirname));
const commit = getVersion(root);
const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`;
// const commit = getVersion(root);
// const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`;

function minifyExtensionResources(input: Stream): Stream {
const jsonFilter = filter(['**/*.json', '**/*.code-snippets'], { restore: true });
Expand Down Expand Up @@ -65,32 +63,24 @@ function updateExtensionPackageJSON(input: Stream, update: (data: any) => any):
.pipe(packageJsonFilter.restore);
}

function fromLocal(extensionPath: string, forWeb: boolean, disableMangle: boolean): Stream {
function fromLocal(extensionPath: string, forWeb: boolean, _disableMangle: boolean): Stream {

const esbuildConfigFileName = forWeb
? 'esbuild.browser.mts'
: 'esbuild.mts';

const webpackConfigFileName = forWeb
? `extension-browser.webpack.config.js`
: `extension.webpack.config.js`;

const hasEsbuild = fs.existsSync(path.join(extensionPath, esbuildConfigFileName));
const hasWebpack = fs.existsSync(path.join(extensionPath, webpackConfigFileName));

let input: Stream;
let isBundled = false;

if (hasEsbuild) {
// Unlike webpack, esbuild only does bundling so we still want to run a separate type check step
// Esbuild only does bundling so we still want to run a separate type check step
input = es.merge(
fromLocalEsbuild(extensionPath, esbuildConfigFileName),
...getBuildRootsForExtension(extensionPath).map(root => typeCheckExtensionStream(root, forWeb)),
);
isBundled = true;
} else if (hasWebpack) {
input = fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle);
isBundled = true;
} else {
input = fromLocalNormal(extensionPath);
}
Expand Down Expand Up @@ -122,132 +112,6 @@ export function typeCheckExtensionStream(extensionPath: string, forWeb: boolean)
return createTsgoStream(tsconfigPath, { taskName: 'typechecking extension (tsgo)', noEmit: true });
}

function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string, disableMangle: boolean): Stream {
const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce');
const webpack = require('webpack');
const webpackGulp = require('webpack-stream');
const result = es.through();

const packagedDependencies: string[] = [];
const stripOutSourceMaps: string[] = [];
const packageJsonConfig = require(path.join(extensionPath, 'package.json'));
if (packageJsonConfig.dependencies) {
const webpackConfig = require(path.join(extensionPath, webpackConfigFileName));
const webpackRootConfig = webpackConfig.default;
for (const key in webpackRootConfig.externals) {
if (key in packageJsonConfig.dependencies) {
packagedDependencies.push(key);
}
}

if (webpackConfig.StripOutSourceMaps) {
for (const filePath of webpackConfig.StripOutSourceMaps) {
stripOutSourceMaps.push(filePath);
}
}
}

// TODO: add prune support based on packagedDependencies to vsce.PackageManager.Npm similar
// to vsce.PackageManager.Yarn.
// A static analysis showed there are no webpack externals that are dependencies of the current
// local extensions so we can use the vsce.PackageManager.None config to ignore dependencies list
// as a temporary workaround.
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.None, packagedDependencies }).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)
}));

// check for a webpack configuration files, then invoke webpack
// and merge its output with the files stream.
const webpackConfigLocations = (glob.sync(
path.join(extensionPath, '**', webpackConfigFileName),
{ ignore: ['**/node_modules'] }
) as string[]);
const webpackStreams = webpackConfigLocations.flatMap(webpackConfigPath => {

const webpackDone = (err: Error | undefined, stats: any) => {
fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`);
if (err) {
result.emit('error', err);
}
const { compilation } = stats;
if (compilation.errors.length > 0) {
result.emit('error', compilation.errors.join('\n'));
}
if (compilation.warnings.length > 0) {
result.emit('error', compilation.warnings.join('\n'));
}
};

const exportedConfig = require(webpackConfigPath).default;
return (Array.isArray(exportedConfig) ? exportedConfig : [exportedConfig]).map(config => {
const webpackConfig = {
...config,
...{ mode: 'production' }
};
if (disableMangle) {
if (Array.isArray(config.module.rules)) {
for (const rule of config.module.rules) {
if (Array.isArray(rule.use)) {
for (const use of rule.use) {
if (String(use.loader).endsWith('mangle-loader.js')) {
use.options.disabled = true;
}
}
}
}
}
}
const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path);

return webpackGulp(webpackConfig, webpack, webpackDone)
.pipe(es.through(function (data) {
data.stat = data.stat || {};
data.base = extensionPath;
this.emit('data', data);
}))
.pipe(es.through(function (data: File) {
// source map handling:
// * rewrite sourceMappingURL
// * save to disk so that upload-task picks this up
if (path.extname(data.basename) === '.js') {
if (stripOutSourceMaps.indexOf(data.relative) >= 0) { // remove source map
const contents = (data.contents as Buffer).toString('utf8');
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8');
} else {
const contents = (data.contents as Buffer).toString('utf8');
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) {
return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`;
}), 'utf8');
}
}

this.emit('data', data);
}));
});
});

es.merge(...webpackStreams, es.readArray(files))
// .pipe(es.through(function (data) {
// // debug
// console.log('out', data.path, data.contents.length);
// this.emit('data', data);
// }))
.pipe(result);

}).catch(err => {
console.error(extensionPath);
console.error(packagedDependencies);
result.emit('error', err);
});

return result.pipe(createStatsStream(path.basename(extensionPath)));
}

function fromLocalNormal(extensionPath: string): Stream {
const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce');
Expand Down Expand Up @@ -649,70 +513,6 @@ export function translatePackageJSON(packageJSON: string, packageNLSPath: string

const extensionsPath = path.join(root, 'extensions');

export async function webpackExtensions(taskName: string, isWatch: boolean, webpackConfigLocations: { configPath: string; outputRoot?: string }[]) {
const webpack = require('webpack') as typeof import('webpack');

const webpackConfigs: webpack.Configuration[] = [];

for (const { configPath, outputRoot } of webpackConfigLocations) {
const configOrFnOrArray = require(configPath).default;
function addConfig(configOrFnOrArray: webpack.Configuration | ((env: unknown, args: unknown) => webpack.Configuration) | webpack.Configuration[]) {
for (const configOrFn of Array.isArray(configOrFnOrArray) ? configOrFnOrArray : [configOrFnOrArray]) {
const config = typeof configOrFn === 'function' ? configOrFn({}, {}) : configOrFn;
if (outputRoot) {
config.output!.path = path.join(outputRoot, path.relative(path.dirname(configPath), config.output!.path!));
}
webpackConfigs.push(config);
}
}
addConfig(configOrFnOrArray);
}

function reporter(fullStats: any) {
if (Array.isArray(fullStats.children)) {
for (const stats of fullStats.children) {
const outputPath = stats.outputPath;
if (outputPath) {
const relativePath = path.relative(extensionsPath, outputPath).replace(/\\/g, '/');
const match = relativePath.match(/[^\/]+(\/server|\/client)?/);
fancyLog(`Finished ${ansiColors.green(taskName)} ${ansiColors.cyan(match![0])} with ${stats.errors.length} errors.`);
}
if (Array.isArray(stats.errors)) {
stats.errors.forEach((error: any) => {
fancyLog.error(error);
});
}
if (Array.isArray(stats.warnings)) {
stats.warnings.forEach((warning: any) => {
fancyLog.warn(warning);
});
}
}
}
}
return new Promise<void>((resolve, reject) => {
if (isWatch) {
webpack(webpackConfigs).watch({}, (err, stats) => {
if (err) {
reject();
} else {
reporter(stats?.toJson());
}
});
} else {
webpack(webpackConfigs).run((err, stats) => {
if (err) {
fancyLog.error(err);
reject();
} else {
reporter(stats?.toJson());
resolve();
}
});
}
});
}

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);
Expand Down
4 changes: 2 additions & 2 deletions extensions/esbuild-extension-common.mts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ async function tryBuild(options: BuildOptions, didBuild?: (outDir: string) => un

interface RunConfig {
readonly platform: 'node' | 'browser';
readonly format?: 'cjs' | 'esm';
readonly srcDir: string;
readonly outdir: string;
readonly entryPoints: string[] | Record<string, string> | { in: string; out: string }[];
Expand All @@ -48,6 +49,7 @@ function resolveOptions(config: RunConfig, outdir: string): BuildOptions {
sourcemap: true,
target: ['es2024'],
external: ['vscode'],
format: config.format ?? 'cjs',
entryPoints: config.entryPoints,
outdir,
logOverride: {
Expand All @@ -57,10 +59,8 @@ function resolveOptions(config: RunConfig, outdir: string): BuildOptions {
};

if (config.platform === 'node') {
options.format = 'cjs';
options.mainFields = ['module', 'main'];
} else if (config.platform === 'browser') {
options.format = 'cjs';
options.mainFields = ['browser', 'module', 'main'];
options.alias = {
'path': 'path-browserify',
Expand Down
2 changes: 1 addition & 1 deletion extensions/github/.vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ src/**
!src/common/config.json
out/**
build/**
extension.webpack.config.js
esbuild*.mts
tsconfig*.json
package-lock.json
testWorkspace/**
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,18 @@
* 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/extension.ts'
},
output: {
libraryTarget: 'module',
chunkFormat: 'module',
},
externals: {
'vscode': 'module vscode',
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist');

run({
platform: 'node',
format: 'esm',
entryPoints: {
'extension': path.join(srcDir, 'extension.ts'),
},
experiments: {
outputModule: true
}
});
srcDir,
outdir: outDir,
}, process.argv);
3 changes: 1 addition & 2 deletions extensions/github/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
"extensionDependencies": [
"vscode.git-base"
],
"main": "./out/extension.js",
"type": "module",
"main": "./dist/extension.js",
"capabilities": {
"virtualWorkspaces": true,
"untrustedWorkspaces": {
Expand Down
2 changes: 1 addition & 1 deletion extensions/github/src/branchProtection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { EventEmitter, LogOutputChannel, Memento, Uri, workspace } from 'vscode';
import { Repository as GitHubRepository, RepositoryRuleset } from '@octokit/graphql-schema';
import { AuthenticationError, OctokitService } from './auth.js';
import { API, BranchProtection, BranchProtectionProvider, BranchProtectionRule, Repository } from './typings/git.js';
import type { API, BranchProtection, BranchProtectionProvider, BranchProtectionRule, Repository } from './typings/git.d.ts';
import { DisposableStore, getRepositoryFromUrl } from './util.js';
import { TelemetryReporter } from '@vscode/extension-telemetry';

Expand Down
2 changes: 1 addition & 1 deletion extensions/github/src/canonicalUriProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { CancellationToken, CanonicalUriProvider, CanonicalUriRequestOptions, Disposable, ProviderResult, Uri, workspace } from 'vscode';
import { API } from './typings/git.js';
import type { API } from './typings/git.d.ts';

const SUPPORTED_SCHEMES = ['ssh', 'https', 'file'];

Expand Down
Loading
Loading