mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-02 08:15:56 +01:00
Add vscode-extras extension with npm up-to-date feature and related configurations (#298295)
This commit is contained in:
committed by
GitHub
parent
98ad6b67c2
commit
5b7dafcb12
16
.vscode/extensions/vscode-extras/package-lock.json
generated
vendored
Normal file
16
.vscode/extensions/vscode-extras/package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "vscode-extras",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "vscode-extras",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"vscode": "^1.88.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
.vscode/extensions/vscode-extras/package.json
vendored
Normal file
38
.vscode/extensions/vscode-extras/package.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "vscode-extras",
|
||||
"displayName": "VS Code Extras",
|
||||
"description": "Extra utility features for the VS Code selfhost workspace",
|
||||
"engines": {
|
||||
"vscode": "^1.88.0"
|
||||
},
|
||||
"version": "0.0.1",
|
||||
"publisher": "ms-vscode",
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"workspaceContains:src/vscode-dts/vscode.d.ts"
|
||||
],
|
||||
"main": "./out/extension.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/microsoft/vscode.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"compile": "gulp compile-extension:vscode-extras",
|
||||
"watch": "gulp watch-extension:vscode-extras"
|
||||
},
|
||||
"contributes": {
|
||||
"configuration": {
|
||||
"title": "VS Code Extras",
|
||||
"properties": {
|
||||
"vscode-extras.npmUpToDateFeature.enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Show a status bar warning when npm dependencies are out of date."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
.vscode/extensions/vscode-extras/src/extension.ts
vendored
Normal file
51
.vscode/extensions/vscode-extras/src/extension.ts
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { NpmUpToDateFeature } from './npmUpToDateFeature';
|
||||
|
||||
export class Extension extends vscode.Disposable {
|
||||
private readonly _output: vscode.LogOutputChannel;
|
||||
private _npmFeature: NpmUpToDateFeature | undefined;
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
super(() => disposables.forEach(d => d.dispose()));
|
||||
|
||||
this._output = vscode.window.createOutputChannel('VS Code Extras', { log: true });
|
||||
disposables.push(this._output);
|
||||
|
||||
this._updateNpmFeature();
|
||||
|
||||
disposables.push(
|
||||
vscode.workspace.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('vscode-extras.npmUpToDateFeature.enabled')) {
|
||||
this._updateNpmFeature();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _updateNpmFeature(): void {
|
||||
const enabled = vscode.workspace.getConfiguration('vscode-extras').get<boolean>('npmUpToDateFeature.enabled', true);
|
||||
if (enabled && !this._npmFeature) {
|
||||
this._npmFeature = new NpmUpToDateFeature(this._output);
|
||||
} else if (!enabled && this._npmFeature) {
|
||||
this._npmFeature.dispose();
|
||||
this._npmFeature = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let extension: Extension | undefined;
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
extension = new Extension(context);
|
||||
context.subscriptions.push(extension);
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
extension = undefined;
|
||||
}
|
||||
176
.vscode/extensions/vscode-extras/src/npmUpToDateFeature.ts
vendored
Normal file
176
.vscode/extensions/vscode-extras/src/npmUpToDateFeature.ts
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
interface FileHashes {
|
||||
readonly [relativePath: string]: string;
|
||||
}
|
||||
|
||||
interface PostinstallState {
|
||||
readonly nodeVersion: string;
|
||||
readonly fileHashes: FileHashes;
|
||||
}
|
||||
|
||||
interface InstallState {
|
||||
readonly root: string;
|
||||
readonly current: PostinstallState;
|
||||
readonly saved: PostinstallState | undefined;
|
||||
readonly files: readonly string[];
|
||||
}
|
||||
|
||||
export class NpmUpToDateFeature extends vscode.Disposable {
|
||||
private readonly _statusBarItem: vscode.StatusBarItem;
|
||||
private readonly _disposables: vscode.Disposable[] = [];
|
||||
private _watchers: fs.FSWatcher[] = [];
|
||||
private _terminal: vscode.Terminal | undefined;
|
||||
|
||||
constructor(private readonly _output: vscode.LogOutputChannel) {
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
super(() => {
|
||||
disposables.forEach(d => d.dispose());
|
||||
for (const w of this._watchers) {
|
||||
w.close();
|
||||
}
|
||||
});
|
||||
this._disposables = disposables;
|
||||
|
||||
this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10000);
|
||||
this._statusBarItem.name = 'npm Install State';
|
||||
this._statusBarItem.text = '$(warning) node_modules is stale - run npm i';
|
||||
this._statusBarItem.tooltip = 'Dependencies are out of date. Click to run npm install.';
|
||||
this._statusBarItem.command = 'vscode-extras.runNpmInstall';
|
||||
this._statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
|
||||
this._disposables.push(this._statusBarItem);
|
||||
|
||||
this._disposables.push(
|
||||
vscode.commands.registerCommand('vscode-extras.runNpmInstall', () => this._runNpmInstall())
|
||||
);
|
||||
|
||||
this._disposables.push(
|
||||
vscode.window.onDidCloseTerminal(t => {
|
||||
if (t === this._terminal) {
|
||||
this._terminal = undefined;
|
||||
this._check();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this._check();
|
||||
}
|
||||
|
||||
private _runNpmInstall(): void {
|
||||
if (this._terminal) {
|
||||
this._terminal.show();
|
||||
return;
|
||||
}
|
||||
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri;
|
||||
if (!workspaceRoot) {
|
||||
return;
|
||||
}
|
||||
this._terminal = vscode.window.createTerminal({ name: 'npm install', cwd: workspaceRoot });
|
||||
this._terminal.sendText('node build/npm/fast-install.ts --force');
|
||||
this._terminal.show();
|
||||
|
||||
this._statusBarItem.text = '$(loading~spin) npm i';
|
||||
this._statusBarItem.tooltip = 'npm install is running...';
|
||||
this._statusBarItem.backgroundColor = undefined;
|
||||
this._statusBarItem.command = 'vscode-extras.runNpmInstall';
|
||||
}
|
||||
|
||||
private _queryState(): InstallState | undefined {
|
||||
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
|
||||
if (!workspaceRoot) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const script = path.join(workspaceRoot, 'build', 'npm', 'installStateHash.ts');
|
||||
const output = cp.execFileSync(process.execPath, [script], {
|
||||
cwd: workspaceRoot,
|
||||
timeout: 10_000,
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const parsed = JSON.parse(output.trim());
|
||||
this._output.trace('raw output:', output.trim());
|
||||
return parsed;
|
||||
} catch (e) {
|
||||
this._output.error('_queryState error:', e as any);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _check(): void {
|
||||
const state = this._queryState();
|
||||
this._output.trace('state:', JSON.stringify(state, null, 2));
|
||||
if (!state) {
|
||||
this._output.trace('no state, hiding');
|
||||
this._statusBarItem.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
this._setupWatcher(state);
|
||||
|
||||
const changedFiles = this._getChangedFiles(state);
|
||||
this._output.trace('changedFiles:', JSON.stringify(changedFiles));
|
||||
|
||||
if (changedFiles.length === 0) {
|
||||
this._statusBarItem.hide();
|
||||
} else {
|
||||
this._statusBarItem.text = '$(warning) node_modules is stale - run npm i';
|
||||
const tooltip = new vscode.MarkdownString();
|
||||
tooltip.appendText('Dependencies are out of date. Click to run npm install.\n\nChanged files:\n');
|
||||
for (const file of changedFiles) {
|
||||
tooltip.appendText(` • ${file}\n`);
|
||||
}
|
||||
this._statusBarItem.tooltip = tooltip;
|
||||
this._statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
|
||||
this._statusBarItem.show();
|
||||
}
|
||||
}
|
||||
|
||||
private _getChangedFiles(state: InstallState): string[] {
|
||||
if (!state.saved) {
|
||||
return ['(no postinstall state found)'];
|
||||
}
|
||||
const changed: string[] = [];
|
||||
if (state.saved.nodeVersion !== state.current.nodeVersion) {
|
||||
changed.push(`Node.js version (${state.saved.nodeVersion} → ${state.current.nodeVersion})`);
|
||||
}
|
||||
const allKeys = new Set([...Object.keys(state.current.fileHashes), ...Object.keys(state.saved.fileHashes)]);
|
||||
for (const key of allKeys) {
|
||||
if (state.current.fileHashes[key] !== state.saved.fileHashes[key]) {
|
||||
changed.push(key);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private _setupWatcher(state: InstallState): void {
|
||||
for (const w of this._watchers) {
|
||||
w.close();
|
||||
}
|
||||
this._watchers = [];
|
||||
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
const scheduleCheck = () => {
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer);
|
||||
}
|
||||
debounceTimer = setTimeout(() => this._check(), 500);
|
||||
};
|
||||
|
||||
for (const file of state.files) {
|
||||
try {
|
||||
const watcher = fs.watch(file, scheduleCheck);
|
||||
this._watchers.push(watcher);
|
||||
} catch {
|
||||
// file may not exist yet
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
.vscode/extensions/vscode-extras/tsconfig.json
vendored
Normal file
14
.vscode/extensions/vscode-extras/tsconfig.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "../../../extensions/tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./out",
|
||||
"types": [
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"../../../src/vscode-dts/vscode.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -6,6 +6,9 @@
|
||||
"testObserver",
|
||||
"testRelatedCode"
|
||||
],
|
||||
"extensionDependencies": [
|
||||
"ms-vscode.vscode-extras"
|
||||
],
|
||||
"engines": {
|
||||
"vscode": "^1.88.0"
|
||||
},
|
||||
|
||||
@@ -95,6 +95,7 @@ const compilations = [
|
||||
|
||||
'.vscode/extensions/vscode-selfhost-test-provider/tsconfig.json',
|
||||
'.vscode/extensions/vscode-selfhost-import-aid/tsconfig.json',
|
||||
'.vscode/extensions/vscode-extras/tsconfig.json',
|
||||
];
|
||||
|
||||
const getBaseUrl = (out: string) => `https://main.vscode-cdn.net/sourcemaps/${commit}/${out}`;
|
||||
|
||||
@@ -60,6 +60,7 @@ export const dirs = [
|
||||
'test/mcp',
|
||||
'.vscode/extensions/vscode-selfhost-import-aid',
|
||||
'.vscode/extensions/vscode-selfhost-test-provider',
|
||||
'.vscode/extensions/vscode-extras',
|
||||
];
|
||||
|
||||
if (existsSync(`${import.meta.dirname}/../../.build/distro/npm`)) {
|
||||
|
||||
22
build/npm/fast-install.ts
Normal file
22
build/npm/fast-install.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as child_process from 'child_process';
|
||||
import { root, isUpToDate, forceInstallMessage } from './installStateHash.ts';
|
||||
|
||||
if (!process.argv.includes('--force') && isUpToDate()) {
|
||||
console.log(`\x1b[32mAll dependencies up to date.\x1b[0m ${forceInstallMessage}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
const result = child_process.spawnSync(npm, ['install'], {
|
||||
cwd: root,
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
env: { ...process.env, VSCODE_FORCE_INSTALL: '1' },
|
||||
});
|
||||
|
||||
process.exit(result.status ?? 1);
|
||||
78
build/npm/installStateHash.ts
Normal file
78
build/npm/installStateHash.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import * as fs from 'fs';
|
||||
import path from 'path';
|
||||
import { dirs } from './dirs.ts';
|
||||
|
||||
export const root = fs.realpathSync.native(path.dirname(path.dirname(import.meta.dirname)));
|
||||
export const stateFile = path.join(root, 'node_modules', '.postinstall-state');
|
||||
export const forceInstallMessage = 'Run \x1b[36mnode build/npm/fast-install.ts --force\x1b[0m to force a full install.';
|
||||
|
||||
export function collectInputFiles(): string[] {
|
||||
const files: string[] = [];
|
||||
|
||||
for (const dir of dirs) {
|
||||
const base = dir === '' ? root : path.join(root, dir);
|
||||
for (const file of ['package.json', '.npmrc']) {
|
||||
const filePath = path.join(base, file);
|
||||
if (fs.existsSync(filePath)) {
|
||||
files.push(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
files.push(path.join(root, '.nvmrc'));
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
export interface PostinstallState {
|
||||
readonly nodeVersion: string;
|
||||
readonly fileHashes: Record<string, string>;
|
||||
}
|
||||
|
||||
function hashFileContent(filePath: string): string {
|
||||
const hash = crypto.createHash('sha256');
|
||||
hash.update(fs.readFileSync(filePath));
|
||||
return hash.digest('hex');
|
||||
}
|
||||
|
||||
export function computeState(): PostinstallState {
|
||||
const fileHashes: Record<string, string> = {};
|
||||
for (const filePath of collectInputFiles()) {
|
||||
fileHashes[path.relative(root, filePath)] = hashFileContent(filePath);
|
||||
}
|
||||
return { nodeVersion: process.versions.node, fileHashes };
|
||||
}
|
||||
|
||||
export function readSavedState(): PostinstallState | undefined {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function isUpToDate(): boolean {
|
||||
const saved = readSavedState();
|
||||
if (!saved) {
|
||||
return false;
|
||||
}
|
||||
const current = computeState();
|
||||
return saved.nodeVersion === current.nodeVersion
|
||||
&& JSON.stringify(saved.fileHashes) === JSON.stringify(current.fileHashes);
|
||||
}
|
||||
|
||||
// When run directly, output state as JSON for tooling (e.g. the vscode-extras extension).
|
||||
if (import.meta.filename === process.argv[1]) {
|
||||
console.log(JSON.stringify({
|
||||
root,
|
||||
current: computeState(),
|
||||
saved: readSavedState(),
|
||||
files: [...collectInputFiles(), stateFile],
|
||||
}));
|
||||
}
|
||||
@@ -8,9 +8,9 @@ import path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as child_process from 'child_process';
|
||||
import { dirs } from './dirs.ts';
|
||||
import { root, stateFile, computeState, isUpToDate } from './installStateHash.ts';
|
||||
|
||||
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
const root = path.dirname(path.dirname(import.meta.dirname));
|
||||
const rootNpmrcConfigKeys = getNpmrcConfigKeys(path.join(root, '.npmrc'));
|
||||
|
||||
function log(dir: string, message: string) {
|
||||
@@ -35,24 +35,45 @@ function run(command: string, args: string[], opts: child_process.SpawnSyncOptio
|
||||
}
|
||||
}
|
||||
|
||||
function npmInstall(dir: string, opts?: child_process.SpawnSyncOptions) {
|
||||
opts = {
|
||||
function spawnAsync(command: string, args: string[], opts: child_process.SpawnOptions): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = child_process.spawn(command, args, { ...opts, stdio: ['ignore', 'pipe', 'pipe'] });
|
||||
let output = '';
|
||||
child.stdout?.on('data', (data: Buffer) => { output += data.toString(); });
|
||||
child.stderr?.on('data', (data: Buffer) => { output += data.toString(); });
|
||||
child.on('error', reject);
|
||||
child.on('close', (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(`Process exited with code: ${code}\n${output}`));
|
||||
} else {
|
||||
resolve(output);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function npmInstallAsync(dir: string, opts?: child_process.SpawnOptions): Promise<void> {
|
||||
const finalOpts: child_process.SpawnOptions = {
|
||||
env: { ...process.env },
|
||||
...(opts ?? {}),
|
||||
cwd: dir,
|
||||
stdio: 'inherit',
|
||||
shell: true
|
||||
cwd: path.join(root, dir),
|
||||
shell: true,
|
||||
};
|
||||
|
||||
const command = process.env['npm_command'] || 'install';
|
||||
|
||||
if (process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'] && /^(.build\/distro\/npm\/)?remote$/.test(dir)) {
|
||||
const syncOpts: child_process.SpawnSyncOptions = {
|
||||
env: finalOpts.env,
|
||||
cwd: root,
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
};
|
||||
const userinfo = os.userInfo();
|
||||
log(dir, `Installing dependencies inside container ${process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME']}...`);
|
||||
|
||||
opts.cwd = root;
|
||||
if (process.env['npm_config_arch'] === 'arm64') {
|
||||
run('sudo', ['docker', 'run', '--rm', '--privileged', 'multiarch/qemu-user-static', '--reset', '-p', 'yes'], opts);
|
||||
run('sudo', ['docker', 'run', '--rm', '--privileged', 'multiarch/qemu-user-static', '--reset', '-p', 'yes'], syncOpts);
|
||||
}
|
||||
run('sudo', [
|
||||
'docker', 'run',
|
||||
@@ -63,11 +84,16 @@ function npmInstall(dir: string, opts?: child_process.SpawnSyncOptions) {
|
||||
'-w', path.resolve('/root/vscode', dir),
|
||||
process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'],
|
||||
'sh', '-c', `\"chown -R root:root ${path.resolve('/root/vscode', dir)} && export PATH="/root/vscode/.build/nodejs-musl/usr/local/bin:$PATH" && npm i -g node-gyp-build && npm ci\"`
|
||||
], opts);
|
||||
run('sudo', ['chown', '-R', `${userinfo.uid}:${userinfo.gid}`, `${path.resolve(root, dir)}`], opts);
|
||||
], syncOpts);
|
||||
run('sudo', ['chown', '-R', `${userinfo.uid}:${userinfo.gid}`, `${path.resolve(root, dir)}`], syncOpts);
|
||||
} else {
|
||||
log(dir, 'Installing dependencies...');
|
||||
run(npm, command.split(' '), opts);
|
||||
const output = await spawnAsync(npm, command.split(' '), finalOpts);
|
||||
if (output.trim()) {
|
||||
for (const line of output.trim().split('\n')) {
|
||||
log(dir, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
removeParcelWatcherPrebuild(dir);
|
||||
}
|
||||
@@ -156,65 +182,114 @@ function clearInheritedNpmrcConfig(dir: string, env: NodeJS.ProcessEnv): void {
|
||||
}
|
||||
}
|
||||
|
||||
for (const dir of dirs) {
|
||||
async function runWithConcurrency(tasks: (() => Promise<void>)[], concurrency: number): Promise<void> {
|
||||
const errors: Error[] = [];
|
||||
let index = 0;
|
||||
|
||||
if (dir === '') {
|
||||
removeParcelWatcherPrebuild(dir);
|
||||
continue; // already executed in root
|
||||
}
|
||||
|
||||
let opts: child_process.SpawnSyncOptions | undefined;
|
||||
|
||||
if (dir === 'build') {
|
||||
opts = {
|
||||
env: {
|
||||
...process.env
|
||||
},
|
||||
};
|
||||
if (process.env['CC']) { opts.env!['CC'] = 'gcc'; }
|
||||
if (process.env['CXX']) { opts.env!['CXX'] = 'g++'; }
|
||||
if (process.env['CXXFLAGS']) { opts.env!['CXXFLAGS'] = ''; }
|
||||
if (process.env['LDFLAGS']) { opts.env!['LDFLAGS'] = ''; }
|
||||
|
||||
setNpmrcConfig('build', opts.env!);
|
||||
npmInstall('build', opts);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/^(.build\/distro\/npm\/)?remote$/.test(dir)) {
|
||||
// node modules used by vscode server
|
||||
opts = {
|
||||
env: {
|
||||
...process.env
|
||||
},
|
||||
};
|
||||
if (process.env['VSCODE_REMOTE_CC']) {
|
||||
opts.env!['CC'] = process.env['VSCODE_REMOTE_CC'];
|
||||
} else {
|
||||
delete opts.env!['CC'];
|
||||
async function worker() {
|
||||
while (index < tasks.length) {
|
||||
const i = index++;
|
||||
try {
|
||||
await tasks[i]();
|
||||
} catch (err) {
|
||||
errors.push(err as Error);
|
||||
}
|
||||
}
|
||||
if (process.env['VSCODE_REMOTE_CXX']) {
|
||||
opts.env!['CXX'] = process.env['VSCODE_REMOTE_CXX'];
|
||||
} else {
|
||||
delete opts.env!['CXX'];
|
||||
}
|
||||
if (process.env['CXXFLAGS']) { delete opts.env!['CXXFLAGS']; }
|
||||
if (process.env['CFLAGS']) { delete opts.env!['CFLAGS']; }
|
||||
if (process.env['LDFLAGS']) { delete opts.env!['LDFLAGS']; }
|
||||
if (process.env['VSCODE_REMOTE_CXXFLAGS']) { opts.env!['CXXFLAGS'] = process.env['VSCODE_REMOTE_CXXFLAGS']; }
|
||||
if (process.env['VSCODE_REMOTE_LDFLAGS']) { opts.env!['LDFLAGS'] = process.env['VSCODE_REMOTE_LDFLAGS']; }
|
||||
if (process.env['VSCODE_REMOTE_NODE_GYP']) { opts.env!['npm_config_node_gyp'] = process.env['VSCODE_REMOTE_NODE_GYP']; }
|
||||
|
||||
setNpmrcConfig('remote', opts.env!);
|
||||
npmInstall(dir, opts);
|
||||
continue;
|
||||
}
|
||||
|
||||
// For directories that don't define their own .npmrc, clear inherited config
|
||||
const env = { ...process.env };
|
||||
clearInheritedNpmrcConfig(dir, env);
|
||||
npmInstall(dir, { env });
|
||||
await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, () => worker()));
|
||||
|
||||
if (errors.length > 0) {
|
||||
for (const err of errors) {
|
||||
console.error(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
child_process.execSync('git config pull.rebase merges');
|
||||
child_process.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs');
|
||||
async function main() {
|
||||
if (!process.env['VSCODE_FORCE_INSTALL'] && isUpToDate()) {
|
||||
log('.', 'All dependencies up to date, skipping postinstall.');
|
||||
child_process.execSync('git config pull.rebase merges');
|
||||
child_process.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs');
|
||||
return;
|
||||
}
|
||||
|
||||
const _state = computeState();
|
||||
|
||||
const nativeTasks: (() => Promise<void>)[] = [];
|
||||
const parallelTasks: (() => Promise<void>)[] = [];
|
||||
|
||||
for (const dir of dirs) {
|
||||
if (dir === '') {
|
||||
removeParcelWatcherPrebuild(dir);
|
||||
continue; // already executed in root
|
||||
}
|
||||
|
||||
if (dir === 'build') {
|
||||
nativeTasks.push(() => {
|
||||
const env: NodeJS.ProcessEnv = { ...process.env };
|
||||
if (process.env['CC']) { env['CC'] = 'gcc'; }
|
||||
if (process.env['CXX']) { env['CXX'] = 'g++'; }
|
||||
if (process.env['CXXFLAGS']) { env['CXXFLAGS'] = ''; }
|
||||
if (process.env['LDFLAGS']) { env['LDFLAGS'] = ''; }
|
||||
setNpmrcConfig('build', env);
|
||||
return npmInstallAsync('build', { env });
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/^(.build\/distro\/npm\/)?remote$/.test(dir)) {
|
||||
const remoteDir = dir;
|
||||
nativeTasks.push(() => {
|
||||
const env: NodeJS.ProcessEnv = { ...process.env };
|
||||
if (process.env['VSCODE_REMOTE_CC']) {
|
||||
env['CC'] = process.env['VSCODE_REMOTE_CC'];
|
||||
} else {
|
||||
delete env['CC'];
|
||||
}
|
||||
if (process.env['VSCODE_REMOTE_CXX']) {
|
||||
env['CXX'] = process.env['VSCODE_REMOTE_CXX'];
|
||||
} else {
|
||||
delete env['CXX'];
|
||||
}
|
||||
if (process.env['CXXFLAGS']) { delete env['CXXFLAGS']; }
|
||||
if (process.env['CFLAGS']) { delete env['CFLAGS']; }
|
||||
if (process.env['LDFLAGS']) { delete env['LDFLAGS']; }
|
||||
if (process.env['VSCODE_REMOTE_CXXFLAGS']) { env['CXXFLAGS'] = process.env['VSCODE_REMOTE_CXXFLAGS']; }
|
||||
if (process.env['VSCODE_REMOTE_LDFLAGS']) { env['LDFLAGS'] = process.env['VSCODE_REMOTE_LDFLAGS']; }
|
||||
if (process.env['VSCODE_REMOTE_NODE_GYP']) { env['npm_config_node_gyp'] = process.env['VSCODE_REMOTE_NODE_GYP']; }
|
||||
setNpmrcConfig('remote', env);
|
||||
return npmInstallAsync(remoteDir, { env });
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const taskDir = dir;
|
||||
parallelTasks.push(() => {
|
||||
const env = { ...process.env };
|
||||
clearInheritedNpmrcConfig(taskDir, env);
|
||||
return npmInstallAsync(taskDir, { env });
|
||||
});
|
||||
}
|
||||
|
||||
// Native dirs (build, remote) run sequentially to avoid node-gyp conflicts
|
||||
for (const task of nativeTasks) {
|
||||
await task();
|
||||
}
|
||||
|
||||
// JS-only dirs run in parallel
|
||||
const concurrency = Math.min(os.cpus().length, 8);
|
||||
log('.', `Running ${parallelTasks.length} npm installs with concurrency ${concurrency}...`);
|
||||
await runWithConcurrency(parallelTasks, concurrency);
|
||||
|
||||
child_process.execSync('git config pull.rebase merges');
|
||||
child_process.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs');
|
||||
|
||||
fs.writeFileSync(stateFile, JSON.stringify(_state));
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as child_process from 'child_process';
|
||||
import * as os from 'os';
|
||||
import { isUpToDate, forceInstallMessage } from './installStateHash.ts';
|
||||
|
||||
if (!process.env['VSCODE_SKIP_NODE_VERSION_CHECK']) {
|
||||
// Get the running Node.js version
|
||||
@@ -41,6 +42,13 @@ if (process.env.npm_execpath?.includes('yarn')) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
// Fast path: if nothing changed since last successful install, skip everything.
|
||||
// This makes `npm i` near-instant when dependencies haven't changed.
|
||||
if (!process.env['VSCODE_FORCE_INSTALL'] && isUpToDate()) {
|
||||
console.log(`\x1b[32mAll dependencies up to date.\x1b[0m ${forceInstallMessage}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
if (!hasSupportedVisualStudioVersion()) {
|
||||
console.error('\x1b[1;31m*** Invalid C/C++ Compiler Toolchain. Please check https://github.com/microsoft/vscode/wiki/How-to-Contribute#prerequisites.\x1b[0;0m');
|
||||
|
||||
Reference in New Issue
Block a user