Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/build/lib/builtInExtensions.ts
3520 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import fs from 'fs';
7
import path from 'path';
8
import os from 'os';
9
import rimraf from 'rimraf';
10
import es from 'event-stream';
11
import rename from 'gulp-rename';
12
import vfs from 'vinyl-fs';
13
import * as ext from './extensions';
14
import fancyLog from 'fancy-log';
15
import ansiColors from 'ansi-colors';
16
import { Stream } from 'stream';
17
18
export interface IExtensionDefinition {
19
name: string;
20
version: string;
21
sha256: string;
22
repo: string;
23
platforms?: string[];
24
vsix?: string;
25
metadata: {
26
id: string;
27
publisherId: {
28
publisherId: string;
29
publisherName: string;
30
displayName: string;
31
flags: string;
32
};
33
publisherDisplayName: string;
34
};
35
}
36
37
const root = path.dirname(path.dirname(__dirname));
38
const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8'));
39
const builtInExtensions = <IExtensionDefinition[]>productjson.builtInExtensions || [];
40
const webBuiltInExtensions = <IExtensionDefinition[]>productjson.webBuiltInExtensions || [];
41
const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json');
42
const ENABLE_LOGGING = !process.env['VSCODE_BUILD_BUILTIN_EXTENSIONS_SILENCE_PLEASE'];
43
44
function log(...messages: string[]): void {
45
if (ENABLE_LOGGING) {
46
fancyLog(...messages);
47
}
48
}
49
50
function getExtensionPath(extension: IExtensionDefinition): string {
51
return path.join(root, '.build', 'builtInExtensions', extension.name);
52
}
53
54
function isUpToDate(extension: IExtensionDefinition): boolean {
55
const packagePath = path.join(getExtensionPath(extension), 'package.json');
56
57
if (!fs.existsSync(packagePath)) {
58
return false;
59
}
60
61
const packageContents = fs.readFileSync(packagePath, { encoding: 'utf8' });
62
63
try {
64
const diskVersion = JSON.parse(packageContents).version;
65
return (diskVersion === extension.version);
66
} catch (err) {
67
return false;
68
}
69
}
70
71
function getExtensionDownloadStream(extension: IExtensionDefinition) {
72
let input: Stream;
73
74
if (extension.vsix) {
75
input = ext.fromVsix(path.join(root, extension.vsix), extension);
76
} else if (productjson.extensionsGallery?.serviceUrl) {
77
input = ext.fromMarketplace(productjson.extensionsGallery.serviceUrl, extension);
78
} else {
79
input = ext.fromGithub(extension);
80
}
81
82
return input.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`));
83
}
84
85
export function getExtensionStream(extension: IExtensionDefinition) {
86
// if the extension exists on disk, use those files instead of downloading anew
87
if (isUpToDate(extension)) {
88
log('[extensions]', `${extension.name}@${extension.version} up to date`, ansiColors.green('✔︎'));
89
return vfs.src(['**'], { cwd: getExtensionPath(extension), dot: true })
90
.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`));
91
}
92
93
return getExtensionDownloadStream(extension);
94
}
95
96
function syncMarketplaceExtension(extension: IExtensionDefinition): Stream {
97
const galleryServiceUrl = productjson.extensionsGallery?.serviceUrl;
98
const source = ansiColors.blue(galleryServiceUrl ? '[marketplace]' : '[github]');
99
if (isUpToDate(extension)) {
100
log(source, `${extension.name}@${extension.version}`, ansiColors.green('✔︎'));
101
return es.readArray([]);
102
}
103
104
rimraf.sync(getExtensionPath(extension));
105
106
return getExtensionDownloadStream(extension)
107
.pipe(vfs.dest('.build/builtInExtensions'))
108
.on('end', () => log(source, extension.name, ansiColors.green('✔︎')));
109
}
110
111
function syncExtension(extension: IExtensionDefinition, controlState: 'disabled' | 'marketplace'): Stream {
112
if (extension.platforms) {
113
const platforms = new Set(extension.platforms);
114
115
if (!platforms.has(process.platform)) {
116
log(ansiColors.gray('[skip]'), `${extension.name}@${extension.version}: Platform '${process.platform}' not supported: [${extension.platforms}]`, ansiColors.green('✔︎'));
117
return es.readArray([]);
118
}
119
}
120
121
switch (controlState) {
122
case 'disabled':
123
log(ansiColors.blue('[disabled]'), ansiColors.gray(extension.name));
124
return es.readArray([]);
125
126
case 'marketplace':
127
return syncMarketplaceExtension(extension);
128
129
default:
130
if (!fs.existsSync(controlState)) {
131
log(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`));
132
return es.readArray([]);
133
134
} else if (!fs.existsSync(path.join(controlState, 'package.json'))) {
135
log(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`));
136
return es.readArray([]);
137
}
138
139
log(ansiColors.blue('[local]'), `${extension.name}: ${ansiColors.cyan(controlState)}`, ansiColors.green('✔︎'));
140
return es.readArray([]);
141
}
142
}
143
144
interface IControlFile {
145
[name: string]: 'disabled' | 'marketplace';
146
}
147
148
function readControlFile(): IControlFile {
149
try {
150
return JSON.parse(fs.readFileSync(controlFilePath, 'utf8'));
151
} catch (err) {
152
return {};
153
}
154
}
155
156
function writeControlFile(control: IControlFile): void {
157
fs.mkdirSync(path.dirname(controlFilePath), { recursive: true });
158
fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2));
159
}
160
161
export function getBuiltInExtensions(): Promise<void> {
162
log('Synchronizing built-in extensions...');
163
log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`);
164
165
const control = readControlFile();
166
const streams: Stream[] = [];
167
168
for (const extension of [...builtInExtensions, ...webBuiltInExtensions]) {
169
const controlState = control[extension.name] || 'marketplace';
170
control[extension.name] = controlState;
171
172
streams.push(syncExtension(extension, controlState));
173
}
174
175
writeControlFile(control);
176
177
return new Promise((resolve, reject) => {
178
es.merge(streams)
179
.on('error', reject)
180
.on('end', resolve);
181
});
182
}
183
184
if (require.main === module) {
185
getBuiltInExtensions().then(() => process.exit(0)).catch(err => {
186
console.error(err);
187
process.exit(1);
188
});
189
}
190
191