@vscode/vsce 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/README.md +105 -0
- package/ThirdPartyNotices.txt +292 -0
- package/out/api.d.ts +59 -0
- package/out/api.js +48 -0
- package/out/main.js +229 -0
- package/out/manifest.js +3 -0
- package/out/nls.js +22 -0
- package/out/npm.js +190 -0
- package/out/package.js +1326 -0
- package/out/publicgalleryapi.js +39 -0
- package/out/publish.js +186 -0
- package/out/search.js +65 -0
- package/out/show.js +71 -0
- package/out/store.js +225 -0
- package/out/util.js +165 -0
- package/out/validation.js +120 -0
- package/out/viewutils.js +73 -0
- package/out/xml.js +11 -0
- package/out/zip.js +57 -0
- package/package.json +99 -0
- package/vsce +2 -0
package/out/package.js
ADDED
|
@@ -0,0 +1,1326 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.ls = exports.listFiles = exports.packageCommand = exports.pack = exports.prepublish = exports.collect = exports.createDefaultProcessors = exports.processFiles = exports.toContentTypes = exports.toVsixManifest = exports.readManifest = exports.validateManifest = exports.ValidationProcessor = exports.NLSProcessor = exports.isWebKind = exports.ChangelogProcessor = exports.ReadmeProcessor = exports.MarkdownProcessor = exports.TagsProcessor = exports.ManifestProcessor = exports.Targets = exports.versionBump = exports.BaseProcessor = exports.read = void 0;
|
|
26
|
+
const fs = __importStar(require("fs"));
|
|
27
|
+
const path = __importStar(require("path"));
|
|
28
|
+
const util_1 = require("util");
|
|
29
|
+
const cp = __importStar(require("child_process"));
|
|
30
|
+
const yazl = __importStar(require("yazl"));
|
|
31
|
+
const nls_1 = require("./nls");
|
|
32
|
+
const util = __importStar(require("./util"));
|
|
33
|
+
const glob_1 = __importDefault(require("glob"));
|
|
34
|
+
const minimatch_1 = __importDefault(require("minimatch"));
|
|
35
|
+
const markdown_it_1 = __importDefault(require("markdown-it"));
|
|
36
|
+
const cheerio = __importStar(require("cheerio"));
|
|
37
|
+
const url = __importStar(require("url"));
|
|
38
|
+
const mime_1 = __importDefault(require("mime"));
|
|
39
|
+
const semver = __importStar(require("semver"));
|
|
40
|
+
const url_join_1 = __importDefault(require("url-join"));
|
|
41
|
+
const validation_1 = require("./validation");
|
|
42
|
+
const npm_1 = require("./npm");
|
|
43
|
+
const GitHost = __importStar(require("hosted-git-info"));
|
|
44
|
+
const parse_semver_1 = __importDefault(require("parse-semver"));
|
|
45
|
+
const MinimatchOptions = { dot: true };
|
|
46
|
+
function isInMemoryFile(file) {
|
|
47
|
+
return !!file.contents;
|
|
48
|
+
}
|
|
49
|
+
function read(file) {
|
|
50
|
+
if (isInMemoryFile(file)) {
|
|
51
|
+
return Promise.resolve(file.contents).then(b => (typeof b === 'string' ? b : b.toString('utf8')));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return fs.promises.readFile(file.localPath, 'utf8');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.read = read;
|
|
58
|
+
class BaseProcessor {
|
|
59
|
+
constructor(manifest) {
|
|
60
|
+
this.manifest = manifest;
|
|
61
|
+
this.assets = [];
|
|
62
|
+
this.tags = [];
|
|
63
|
+
this.vsix = Object.create(null);
|
|
64
|
+
}
|
|
65
|
+
async onFile(file) {
|
|
66
|
+
return file;
|
|
67
|
+
}
|
|
68
|
+
async onEnd() {
|
|
69
|
+
// noop
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.BaseProcessor = BaseProcessor;
|
|
73
|
+
// https://github.com/npm/cli/blob/latest/lib/utils/hosted-git-info-from-manifest.js
|
|
74
|
+
function getGitHost(manifest) {
|
|
75
|
+
const url = getRepositoryUrl(manifest);
|
|
76
|
+
return url ? GitHost.fromUrl(url, { noGitPlus: true }) : undefined;
|
|
77
|
+
}
|
|
78
|
+
// https://github.com/npm/cli/blob/latest/lib/repo.js
|
|
79
|
+
function getRepositoryUrl(manifest, gitHost) {
|
|
80
|
+
if (gitHost) {
|
|
81
|
+
return gitHost.https();
|
|
82
|
+
}
|
|
83
|
+
let url = undefined;
|
|
84
|
+
if (manifest.repository) {
|
|
85
|
+
if (typeof manifest.repository === 'string') {
|
|
86
|
+
url = manifest.repository;
|
|
87
|
+
}
|
|
88
|
+
else if (typeof manifest.repository === 'object' &&
|
|
89
|
+
manifest.repository.url &&
|
|
90
|
+
typeof manifest.repository.url === 'string') {
|
|
91
|
+
url = manifest.repository.url;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return url;
|
|
95
|
+
}
|
|
96
|
+
// https://github.com/npm/cli/blob/latest/lib/bugs.js
|
|
97
|
+
function getBugsUrl(manifest, gitHost) {
|
|
98
|
+
if (manifest.bugs) {
|
|
99
|
+
if (typeof manifest.bugs === 'string') {
|
|
100
|
+
return manifest.bugs;
|
|
101
|
+
}
|
|
102
|
+
if (typeof manifest.bugs === 'object' && manifest.bugs.url) {
|
|
103
|
+
return manifest.bugs.url;
|
|
104
|
+
}
|
|
105
|
+
if (typeof manifest.bugs === 'object' && manifest.bugs.email) {
|
|
106
|
+
return `mailto:${manifest.bugs.email}`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (gitHost) {
|
|
110
|
+
return gitHost.bugs();
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
// https://github.com/npm/cli/blob/latest/lib/docs.js
|
|
115
|
+
function getHomepageUrl(manifest, gitHost) {
|
|
116
|
+
if (manifest.homepage) {
|
|
117
|
+
return manifest.homepage;
|
|
118
|
+
}
|
|
119
|
+
if (gitHost) {
|
|
120
|
+
return gitHost.docs();
|
|
121
|
+
}
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
// Contributed by Mozilla developer authors
|
|
125
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
|
|
126
|
+
function escapeRegExp(value) {
|
|
127
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
|
128
|
+
}
|
|
129
|
+
function toExtensionTags(extensions) {
|
|
130
|
+
return extensions
|
|
131
|
+
.map(s => s.replace(/\W/g, ''))
|
|
132
|
+
.filter(s => !!s)
|
|
133
|
+
.map(s => `__ext_${s}`);
|
|
134
|
+
}
|
|
135
|
+
function toLanguagePackTags(translations, languageId) {
|
|
136
|
+
return (translations ?? [])
|
|
137
|
+
.map(({ id }) => [`__lp_${id}`, `__lp-${languageId}_${id}`])
|
|
138
|
+
.reduce((r, t) => [...r, ...t], []);
|
|
139
|
+
}
|
|
140
|
+
/* This list is also maintained by the Marketplace team.
|
|
141
|
+
* Remember to reach out to them when adding new domains.
|
|
142
|
+
*/
|
|
143
|
+
const TrustedSVGSources = [
|
|
144
|
+
'api.bintray.com',
|
|
145
|
+
'api.travis-ci.com',
|
|
146
|
+
'api.travis-ci.org',
|
|
147
|
+
'app.fossa.io',
|
|
148
|
+
'badge.buildkite.com',
|
|
149
|
+
'badge.fury.io',
|
|
150
|
+
'badge.waffle.io',
|
|
151
|
+
'badgen.net',
|
|
152
|
+
'badges.frapsoft.com',
|
|
153
|
+
'badges.gitter.im',
|
|
154
|
+
'badges.greenkeeper.io',
|
|
155
|
+
'cdn.travis-ci.com',
|
|
156
|
+
'cdn.travis-ci.org',
|
|
157
|
+
'ci.appveyor.com',
|
|
158
|
+
'circleci.com',
|
|
159
|
+
'cla.opensource.microsoft.com',
|
|
160
|
+
'codacy.com',
|
|
161
|
+
'codeclimate.com',
|
|
162
|
+
'codecov.io',
|
|
163
|
+
'coveralls.io',
|
|
164
|
+
'david-dm.org',
|
|
165
|
+
'deepscan.io',
|
|
166
|
+
'dev.azure.com',
|
|
167
|
+
'docs.rs',
|
|
168
|
+
'flat.badgen.net',
|
|
169
|
+
'gemnasium.com',
|
|
170
|
+
'githost.io',
|
|
171
|
+
'gitlab.com',
|
|
172
|
+
'godoc.org',
|
|
173
|
+
'goreportcard.com',
|
|
174
|
+
'img.shields.io',
|
|
175
|
+
'isitmaintained.com',
|
|
176
|
+
'marketplace.visualstudio.com',
|
|
177
|
+
'nodesecurity.io',
|
|
178
|
+
'opencollective.com',
|
|
179
|
+
'snyk.io',
|
|
180
|
+
'travis-ci.com',
|
|
181
|
+
'travis-ci.org',
|
|
182
|
+
'visualstudio.com',
|
|
183
|
+
'vsmarketplacebadge.apphb.com',
|
|
184
|
+
'www.bithound.io',
|
|
185
|
+
'www.versioneye.com',
|
|
186
|
+
];
|
|
187
|
+
function isGitHubRepository(repository) {
|
|
188
|
+
return /^https:\/\/github\.com\/|^git@github\.com:/.test(repository ?? '');
|
|
189
|
+
}
|
|
190
|
+
function isGitLabRepository(repository) {
|
|
191
|
+
return /^https:\/\/gitlab\.com\/|^git@gitlab\.com:/.test(repository ?? '');
|
|
192
|
+
}
|
|
193
|
+
function isGitHubBadge(href) {
|
|
194
|
+
return /^https:\/\/github\.com\/[^/]+\/[^/]+\/(actions\/)?workflows\/.*badge\.svg/.test(href || '');
|
|
195
|
+
}
|
|
196
|
+
function isHostTrusted(url) {
|
|
197
|
+
return (url.host && TrustedSVGSources.indexOf(url.host.toLowerCase()) > -1) || isGitHubBadge(url.href);
|
|
198
|
+
}
|
|
199
|
+
async function versionBump(options) {
|
|
200
|
+
if (!options.version) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (!(options.updatePackageJson ?? true)) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const cwd = options.cwd ?? process.cwd();
|
|
207
|
+
const manifest = await readManifest(cwd);
|
|
208
|
+
if (manifest.version === options.version) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
switch (options.version) {
|
|
212
|
+
case 'major':
|
|
213
|
+
case 'minor':
|
|
214
|
+
case 'patch':
|
|
215
|
+
break;
|
|
216
|
+
case 'premajor':
|
|
217
|
+
case 'preminor':
|
|
218
|
+
case 'prepatch':
|
|
219
|
+
case 'prerelease':
|
|
220
|
+
case 'from-git':
|
|
221
|
+
return Promise.reject(`Not supported: ${options.version}`);
|
|
222
|
+
default:
|
|
223
|
+
if (!semver.valid(options.version)) {
|
|
224
|
+
return Promise.reject(`Invalid version ${options.version}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
let command = `npm version ${options.version}`;
|
|
228
|
+
if (options.commitMessage) {
|
|
229
|
+
command = `${command} -m "${options.commitMessage}"`;
|
|
230
|
+
}
|
|
231
|
+
if (!(options.gitTagVersion ?? true)) {
|
|
232
|
+
command = `${command} --no-git-tag-version`;
|
|
233
|
+
}
|
|
234
|
+
// call `npm version` to do our dirty work
|
|
235
|
+
const { stdout, stderr } = await (0, util_1.promisify)(cp.exec)(command, { cwd });
|
|
236
|
+
if (!process.env['VSCE_TESTS']) {
|
|
237
|
+
process.stdout.write(stdout);
|
|
238
|
+
process.stderr.write(stderr);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
exports.versionBump = versionBump;
|
|
242
|
+
exports.Targets = new Set([
|
|
243
|
+
'win32-x64',
|
|
244
|
+
'win32-ia32',
|
|
245
|
+
'win32-arm64',
|
|
246
|
+
'linux-x64',
|
|
247
|
+
'linux-arm64',
|
|
248
|
+
'linux-armhf',
|
|
249
|
+
'darwin-x64',
|
|
250
|
+
'darwin-arm64',
|
|
251
|
+
'alpine-x64',
|
|
252
|
+
'alpine-arm64',
|
|
253
|
+
'web',
|
|
254
|
+
]);
|
|
255
|
+
class ManifestProcessor extends BaseProcessor {
|
|
256
|
+
constructor(manifest, options = {}) {
|
|
257
|
+
super(manifest);
|
|
258
|
+
this.options = options;
|
|
259
|
+
const flags = ['Public'];
|
|
260
|
+
if (manifest.preview) {
|
|
261
|
+
flags.push('Preview');
|
|
262
|
+
}
|
|
263
|
+
const gitHost = getGitHost(manifest);
|
|
264
|
+
const repository = getRepositoryUrl(manifest, gitHost);
|
|
265
|
+
const isGitHub = isGitHubRepository(repository);
|
|
266
|
+
let enableMarketplaceQnA;
|
|
267
|
+
let customerQnALink;
|
|
268
|
+
if (manifest.qna === 'marketplace') {
|
|
269
|
+
enableMarketplaceQnA = true;
|
|
270
|
+
}
|
|
271
|
+
else if (typeof manifest.qna === 'string') {
|
|
272
|
+
customerQnALink = manifest.qna;
|
|
273
|
+
}
|
|
274
|
+
else if (manifest.qna === false) {
|
|
275
|
+
enableMarketplaceQnA = false;
|
|
276
|
+
}
|
|
277
|
+
const extensionKind = getExtensionKind(manifest);
|
|
278
|
+
const target = options.target;
|
|
279
|
+
const preRelease = options.preRelease;
|
|
280
|
+
if (target || preRelease) {
|
|
281
|
+
let engineVersion;
|
|
282
|
+
try {
|
|
283
|
+
const engineSemver = (0, parse_semver_1.default)(`vscode@${manifest.engines['vscode']}`);
|
|
284
|
+
engineVersion = engineSemver.version;
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
throw new Error('Failed to parse semver of engines.vscode');
|
|
288
|
+
}
|
|
289
|
+
if (target) {
|
|
290
|
+
if (engineVersion !== 'latest' && !semver.satisfies(engineVersion, '>=1.61', { includePrerelease: true })) {
|
|
291
|
+
throw new Error(`Platform specific extension is supported by VS Code >=1.61. Current 'engines.vscode' is '${manifest.engines['vscode']}'.`);
|
|
292
|
+
}
|
|
293
|
+
if (!exports.Targets.has(target)) {
|
|
294
|
+
throw new Error(`'${target}' is not a valid VS Code target. Valid targets: ${[...exports.Targets].join(', ')}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (preRelease) {
|
|
298
|
+
if (engineVersion !== 'latest' && !semver.satisfies(engineVersion, '>=1.63', { includePrerelease: true })) {
|
|
299
|
+
throw new Error(`Pre-release versions are supported by VS Code >=1.63. Current 'engines.vscode' is '${manifest.engines['vscode']}'.`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
this.vsix = {
|
|
304
|
+
...this.vsix,
|
|
305
|
+
id: manifest.name,
|
|
306
|
+
displayName: manifest.displayName ?? manifest.name,
|
|
307
|
+
version: options.version && !(options.updatePackageJson ?? true) ? options.version : manifest.version,
|
|
308
|
+
publisher: manifest.publisher,
|
|
309
|
+
target,
|
|
310
|
+
engine: manifest.engines['vscode'],
|
|
311
|
+
description: manifest.description ?? '',
|
|
312
|
+
pricing: manifest.pricing ?? 'Free',
|
|
313
|
+
categories: (manifest.categories ?? []).join(','),
|
|
314
|
+
flags: flags.join(' '),
|
|
315
|
+
links: {
|
|
316
|
+
repository,
|
|
317
|
+
bugs: getBugsUrl(manifest, gitHost),
|
|
318
|
+
homepage: getHomepageUrl(manifest, gitHost),
|
|
319
|
+
},
|
|
320
|
+
galleryBanner: manifest.galleryBanner ?? {},
|
|
321
|
+
badges: manifest.badges,
|
|
322
|
+
githubMarkdown: manifest.markdown !== 'standard',
|
|
323
|
+
enableMarketplaceQnA,
|
|
324
|
+
customerQnALink,
|
|
325
|
+
extensionDependencies: [...new Set(manifest.extensionDependencies ?? [])].join(','),
|
|
326
|
+
extensionPack: [...new Set(manifest.extensionPack ?? [])].join(','),
|
|
327
|
+
extensionKind: extensionKind.join(','),
|
|
328
|
+
localizedLanguages: manifest.contributes && manifest.contributes.localizations
|
|
329
|
+
? manifest.contributes.localizations
|
|
330
|
+
.map(loc => loc.localizedLanguageName ?? loc.languageName ?? loc.languageId)
|
|
331
|
+
.join(',')
|
|
332
|
+
: '',
|
|
333
|
+
preRelease: !!this.options.preRelease,
|
|
334
|
+
sponsorLink: manifest.sponsor?.url || '',
|
|
335
|
+
};
|
|
336
|
+
if (isGitHub) {
|
|
337
|
+
this.vsix.links.github = repository;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
async onFile(file) {
|
|
341
|
+
const path = util.normalize(file.path);
|
|
342
|
+
if (!/^extension\/package.json$/i.test(path)) {
|
|
343
|
+
return Promise.resolve(file);
|
|
344
|
+
}
|
|
345
|
+
if (this.options.version && !(this.options.updatePackageJson ?? true)) {
|
|
346
|
+
const contents = await read(file);
|
|
347
|
+
const packageJson = JSON.parse(contents);
|
|
348
|
+
packageJson.version = this.options.version;
|
|
349
|
+
file = { ...file, contents: JSON.stringify(packageJson, undefined, 2) };
|
|
350
|
+
}
|
|
351
|
+
// Ensure that package.json is writable as VS Code needs to
|
|
352
|
+
// store metadata in the extracted file.
|
|
353
|
+
return { ...file, mode: 0o100644 };
|
|
354
|
+
}
|
|
355
|
+
async onEnd() {
|
|
356
|
+
if (typeof this.manifest.extensionKind === 'string') {
|
|
357
|
+
util.log.warn(`The 'extensionKind' property should be of type 'string[]'. Learn more at: https://aka.ms/vscode/api/incorrect-execution-location`);
|
|
358
|
+
}
|
|
359
|
+
if (this.manifest.publisher === 'vscode-samples') {
|
|
360
|
+
throw new Error("It's not allowed to use the 'vscode-samples' publisher. Learn more at: https://code.visualstudio.com/api/working-with-extensions/publishing-extension.");
|
|
361
|
+
}
|
|
362
|
+
if (!this.options.allowMissingRepository && !this.manifest.repository) {
|
|
363
|
+
util.log.warn(`A 'repository' field is missing from the 'package.json' manifest file.`);
|
|
364
|
+
if (!/^y$/i.test(await util.read('Do you want to continue? [y/N] '))) {
|
|
365
|
+
throw new Error('Aborted');
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (!this.options.allowStarActivation && this.manifest.activationEvents?.some(e => e === '*')) {
|
|
369
|
+
util.log.warn(`Using '*' activation is usually a bad idea as it impacts performance.\nMore info: https://code.visualstudio.com/api/references/activation-events#Start-up`);
|
|
370
|
+
if (!/^y$/i.test(await util.read('Do you want to continue? [y/N] '))) {
|
|
371
|
+
throw new Error('Aborted');
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
exports.ManifestProcessor = ManifestProcessor;
|
|
377
|
+
class TagsProcessor extends BaseProcessor {
|
|
378
|
+
async onEnd() {
|
|
379
|
+
const keywords = this.manifest.keywords ?? [];
|
|
380
|
+
const contributes = this.manifest.contributes;
|
|
381
|
+
const activationEvents = this.manifest.activationEvents ?? [];
|
|
382
|
+
const doesContribute = (...properties) => {
|
|
383
|
+
let obj = contributes;
|
|
384
|
+
for (const property of properties) {
|
|
385
|
+
if (!obj) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
obj = obj[property];
|
|
389
|
+
}
|
|
390
|
+
return obj && obj.length > 0;
|
|
391
|
+
};
|
|
392
|
+
const colorThemes = doesContribute('themes') ? ['theme', 'color-theme'] : [];
|
|
393
|
+
const iconThemes = doesContribute('iconThemes') ? ['theme', 'icon-theme'] : [];
|
|
394
|
+
const productIconThemes = doesContribute('productIconThemes') ? ['theme', 'product-icon-theme'] : [];
|
|
395
|
+
const snippets = doesContribute('snippets') ? ['snippet'] : [];
|
|
396
|
+
const keybindings = doesContribute('keybindings') ? ['keybindings'] : [];
|
|
397
|
+
const debuggers = doesContribute('debuggers') ? ['debuggers'] : [];
|
|
398
|
+
const json = doesContribute('jsonValidation') ? ['json'] : [];
|
|
399
|
+
const remoteMenu = doesContribute('menus', 'statusBar/remoteIndicator') ? ['remote-menu'] : [];
|
|
400
|
+
const localizationContributions = ((contributes && contributes['localizations']) ?? []).reduce((r, l) => [...r, `lp-${l.languageId}`, ...toLanguagePackTags(l.translations, l.languageId)], []);
|
|
401
|
+
const languageContributions = ((contributes && contributes['languages']) ?? []).reduce((r, l) => [...r, l.id, ...(l.aliases ?? []), ...toExtensionTags(l.extensions ?? [])], []);
|
|
402
|
+
const languageActivations = activationEvents
|
|
403
|
+
.map(e => /^onLanguage:(.*)$/.exec(e))
|
|
404
|
+
.filter(util.nonnull)
|
|
405
|
+
.map(r => r[1]);
|
|
406
|
+
const grammars = ((contributes && contributes['grammars']) ?? []).map(g => g.language);
|
|
407
|
+
const description = this.manifest.description || '';
|
|
408
|
+
const descriptionKeywords = Object.keys(TagsProcessor.Keywords).reduce((r, k) => r.concat(new RegExp('\\b(?:' + escapeRegExp(k) + ')(?!\\w)', 'gi').test(description) ? TagsProcessor.Keywords[k] : []), []);
|
|
409
|
+
const webExtensionTags = isWebKind(this.manifest) ? ['__web_extension'] : [];
|
|
410
|
+
const sponsorTags = this.manifest.sponsor?.url ? ['__sponsor_extension'] : [];
|
|
411
|
+
const tags = new Set([
|
|
412
|
+
...keywords,
|
|
413
|
+
...colorThemes,
|
|
414
|
+
...iconThemes,
|
|
415
|
+
...productIconThemes,
|
|
416
|
+
...snippets,
|
|
417
|
+
...keybindings,
|
|
418
|
+
...debuggers,
|
|
419
|
+
...json,
|
|
420
|
+
...remoteMenu,
|
|
421
|
+
...localizationContributions,
|
|
422
|
+
...languageContributions,
|
|
423
|
+
...languageActivations,
|
|
424
|
+
...grammars,
|
|
425
|
+
...descriptionKeywords,
|
|
426
|
+
...webExtensionTags,
|
|
427
|
+
...sponsorTags,
|
|
428
|
+
]);
|
|
429
|
+
this.tags = [...tags].filter(tag => !!tag);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
exports.TagsProcessor = TagsProcessor;
|
|
433
|
+
TagsProcessor.Keywords = {
|
|
434
|
+
git: ['git'],
|
|
435
|
+
npm: ['node'],
|
|
436
|
+
spell: ['markdown'],
|
|
437
|
+
bootstrap: ['bootstrap'],
|
|
438
|
+
lint: ['linters'],
|
|
439
|
+
linting: ['linters'],
|
|
440
|
+
react: ['javascript'],
|
|
441
|
+
js: ['javascript'],
|
|
442
|
+
node: ['javascript', 'node'],
|
|
443
|
+
'c++': ['c++'],
|
|
444
|
+
Cplusplus: ['c++'],
|
|
445
|
+
xml: ['xml'],
|
|
446
|
+
angular: ['javascript'],
|
|
447
|
+
jquery: ['javascript'],
|
|
448
|
+
php: ['php'],
|
|
449
|
+
python: ['python'],
|
|
450
|
+
latex: ['latex'],
|
|
451
|
+
ruby: ['ruby'],
|
|
452
|
+
java: ['java'],
|
|
453
|
+
erlang: ['erlang'],
|
|
454
|
+
sql: ['sql'],
|
|
455
|
+
nodejs: ['node'],
|
|
456
|
+
'c#': ['c#'],
|
|
457
|
+
css: ['css'],
|
|
458
|
+
javascript: ['javascript'],
|
|
459
|
+
ftp: ['ftp'],
|
|
460
|
+
haskell: ['haskell'],
|
|
461
|
+
unity: ['unity'],
|
|
462
|
+
terminal: ['terminal'],
|
|
463
|
+
powershell: ['powershell'],
|
|
464
|
+
laravel: ['laravel'],
|
|
465
|
+
meteor: ['meteor'],
|
|
466
|
+
emmet: ['emmet'],
|
|
467
|
+
eslint: ['linters'],
|
|
468
|
+
tfs: ['tfs'],
|
|
469
|
+
rust: ['rust'],
|
|
470
|
+
};
|
|
471
|
+
class MarkdownProcessor extends BaseProcessor {
|
|
472
|
+
constructor(manifest, name, regexp, assetType, options = {}) {
|
|
473
|
+
super(manifest);
|
|
474
|
+
this.name = name;
|
|
475
|
+
this.regexp = regexp;
|
|
476
|
+
this.assetType = assetType;
|
|
477
|
+
const guess = this.guessBaseUrls(options.githubBranch || options.gitlabBranch);
|
|
478
|
+
this.baseContentUrl = options.baseContentUrl || (guess && guess.content);
|
|
479
|
+
this.baseImagesUrl = options.baseImagesUrl || options.baseContentUrl || (guess && guess.images);
|
|
480
|
+
this.rewriteRelativeLinks = options.rewriteRelativeLinks ?? true;
|
|
481
|
+
this.repositoryUrl = guess && guess.repository;
|
|
482
|
+
this.isGitHub = isGitHubRepository(this.repositoryUrl);
|
|
483
|
+
this.isGitLab = isGitLabRepository(this.repositoryUrl);
|
|
484
|
+
this.gitHubIssueLinking = typeof options.gitHubIssueLinking === 'boolean' ? options.gitHubIssueLinking : true;
|
|
485
|
+
this.gitLabIssueLinking = typeof options.gitLabIssueLinking === 'boolean' ? options.gitLabIssueLinking : true;
|
|
486
|
+
}
|
|
487
|
+
async onFile(file) {
|
|
488
|
+
const filePath = util.normalize(file.path);
|
|
489
|
+
if (!this.regexp.test(filePath)) {
|
|
490
|
+
return Promise.resolve(file);
|
|
491
|
+
}
|
|
492
|
+
this.assets.push({ type: this.assetType, path: filePath });
|
|
493
|
+
let contents = await read(file);
|
|
494
|
+
if (/This is the README for your extension /.test(contents)) {
|
|
495
|
+
throw new Error(`Make sure to edit the README.md file before you package or publish your extension.`);
|
|
496
|
+
}
|
|
497
|
+
if (this.rewriteRelativeLinks) {
|
|
498
|
+
const markdownPathRegex = /(!?)\[([^\]\[]*|!\[[^\]\[]*]\([^\)]+\))\]\(([^\)]+)\)/g;
|
|
499
|
+
const urlReplace = (_, isImage, title, link) => {
|
|
500
|
+
if (/^mailto:/i.test(link)) {
|
|
501
|
+
return `${isImage}[${title}](${link})`;
|
|
502
|
+
}
|
|
503
|
+
const isLinkRelative = !/^\w+:\/\//.test(link) && link[0] !== '#';
|
|
504
|
+
if (!this.baseContentUrl && !this.baseImagesUrl) {
|
|
505
|
+
const asset = isImage ? 'image' : 'link';
|
|
506
|
+
if (isLinkRelative) {
|
|
507
|
+
throw new Error(`Couldn't detect the repository where this extension is published. The ${asset} '${link}' will be broken in ${this.name}. GitHub/GitLab repositories will be automatically detected. Otherwise, please provide the repository URL in package.json or use the --baseContentUrl and --baseImagesUrl options.`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
title = title.replace(markdownPathRegex, urlReplace);
|
|
511
|
+
const prefix = isImage ? this.baseImagesUrl : this.baseContentUrl;
|
|
512
|
+
if (!prefix || !isLinkRelative) {
|
|
513
|
+
return `${isImage}[${title}](${link})`;
|
|
514
|
+
}
|
|
515
|
+
return `${isImage}[${title}](${(0, url_join_1.default)(prefix, path.posix.normalize(link))})`;
|
|
516
|
+
};
|
|
517
|
+
// Replace Markdown links with urls
|
|
518
|
+
contents = contents.replace(markdownPathRegex, urlReplace);
|
|
519
|
+
// Replace <img> links with urls
|
|
520
|
+
contents = contents.replace(/<img.+?src=["']([/.\w\s#-]+)['"].*?>/g, (all, link) => {
|
|
521
|
+
const isLinkRelative = !/^\w+:\/\//.test(link) && link[0] !== '#';
|
|
522
|
+
if (!this.baseImagesUrl && isLinkRelative) {
|
|
523
|
+
throw new Error(`Couldn't detect the repository where this extension is published. The image will be broken in ${this.name}. GitHub/GitLab repositories will be automatically detected. Otherwise, please provide the repository URL in package.json or use the --baseContentUrl and --baseImagesUrl options.`);
|
|
524
|
+
}
|
|
525
|
+
const prefix = this.baseImagesUrl;
|
|
526
|
+
if (!prefix || !isLinkRelative) {
|
|
527
|
+
return all;
|
|
528
|
+
}
|
|
529
|
+
return all.replace(link, (0, url_join_1.default)(prefix, path.posix.normalize(link)));
|
|
530
|
+
});
|
|
531
|
+
if ((this.gitHubIssueLinking && this.isGitHub) || (this.gitLabIssueLinking && this.isGitLab)) {
|
|
532
|
+
const markdownIssueRegex = /(\s|\n)([\w\d_-]+\/[\w\d_-]+)?#(\d+)\b/g;
|
|
533
|
+
const issueReplace = (all, prefix, ownerAndRepositoryName, issueNumber) => {
|
|
534
|
+
let result = all;
|
|
535
|
+
let owner;
|
|
536
|
+
let repositoryName;
|
|
537
|
+
if (ownerAndRepositoryName) {
|
|
538
|
+
[owner, repositoryName] = ownerAndRepositoryName.split('/', 2);
|
|
539
|
+
}
|
|
540
|
+
if (owner && repositoryName && issueNumber) {
|
|
541
|
+
// Issue in external repository
|
|
542
|
+
const issueUrl = this.isGitHub
|
|
543
|
+
? (0, url_join_1.default)('https://github.com', owner, repositoryName, 'issues', issueNumber)
|
|
544
|
+
: (0, url_join_1.default)('https://gitlab.com', owner, repositoryName, '-', 'issues', issueNumber);
|
|
545
|
+
result = prefix + `[${owner}/${repositoryName}#${issueNumber}](${issueUrl})`;
|
|
546
|
+
}
|
|
547
|
+
else if (!owner && !repositoryName && issueNumber && this.repositoryUrl) {
|
|
548
|
+
// Issue in own repository
|
|
549
|
+
result =
|
|
550
|
+
prefix +
|
|
551
|
+
`[#${issueNumber}](${this.isGitHub
|
|
552
|
+
? (0, url_join_1.default)(this.repositoryUrl, 'issues', issueNumber)
|
|
553
|
+
: (0, url_join_1.default)(this.repositoryUrl, '-', 'issues', issueNumber)})`;
|
|
554
|
+
}
|
|
555
|
+
return result;
|
|
556
|
+
};
|
|
557
|
+
// Replace Markdown issue references with urls
|
|
558
|
+
contents = contents.replace(markdownIssueRegex, issueReplace);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const html = (0, markdown_it_1.default)({ html: true }).render(contents);
|
|
562
|
+
const $ = cheerio.load(html);
|
|
563
|
+
if (this.rewriteRelativeLinks) {
|
|
564
|
+
$('img').each((_, img) => {
|
|
565
|
+
const rawSrc = $(img).attr('src');
|
|
566
|
+
if (!rawSrc) {
|
|
567
|
+
throw new Error(`Images in ${this.name} must have a source.`);
|
|
568
|
+
}
|
|
569
|
+
const src = decodeURI(rawSrc);
|
|
570
|
+
const srcUrl = new url.URL(src);
|
|
571
|
+
if (/^data:$/i.test(srcUrl.protocol) && /^image$/i.test(srcUrl.host) && /\/svg/i.test(srcUrl.pathname)) {
|
|
572
|
+
throw new Error(`SVG data URLs are not allowed in ${this.name}: ${src}`);
|
|
573
|
+
}
|
|
574
|
+
if (!/^https:$/i.test(srcUrl.protocol)) {
|
|
575
|
+
throw new Error(`Images in ${this.name} must come from an HTTPS source: ${src}`);
|
|
576
|
+
}
|
|
577
|
+
if (/\.svg$/i.test(srcUrl.pathname) && !isHostTrusted(srcUrl)) {
|
|
578
|
+
throw new Error(`SVGs are restricted in ${this.name}; please use other file image formats, such as PNG: ${src}`);
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
$('svg').each(() => {
|
|
583
|
+
throw new Error(`SVG tags are not allowed in ${this.name}.`);
|
|
584
|
+
});
|
|
585
|
+
return {
|
|
586
|
+
path: file.path,
|
|
587
|
+
contents: Buffer.from(contents, 'utf8'),
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
// GitHub heuristics
|
|
591
|
+
guessBaseUrls(githostBranch) {
|
|
592
|
+
let repository = null;
|
|
593
|
+
if (typeof this.manifest.repository === 'string') {
|
|
594
|
+
repository = this.manifest.repository;
|
|
595
|
+
}
|
|
596
|
+
else if (this.manifest.repository && typeof this.manifest.repository['url'] === 'string') {
|
|
597
|
+
repository = this.manifest.repository['url'];
|
|
598
|
+
}
|
|
599
|
+
if (!repository) {
|
|
600
|
+
return undefined;
|
|
601
|
+
}
|
|
602
|
+
const gitHubRegex = /(?<domain>github(\.com\/|:))(?<project>(?:[^/]+)\/(?:[^/]+))(\/|$)/;
|
|
603
|
+
const gitLabRegex = /(?<domain>gitlab(\.com\/|:))(?<project>(?:[^/]+)(\/(?:[^/]+))+)(\/|$)/;
|
|
604
|
+
const match = (gitHubRegex.exec(repository) || gitLabRegex.exec(repository));
|
|
605
|
+
if (!match) {
|
|
606
|
+
return undefined;
|
|
607
|
+
}
|
|
608
|
+
const project = match.groups.project.replace(/\.git$/i, '');
|
|
609
|
+
const branchName = githostBranch ? githostBranch : 'HEAD';
|
|
610
|
+
if (/^github/.test(match.groups.domain)) {
|
|
611
|
+
return {
|
|
612
|
+
content: `https://github.com/${project}/blob/${branchName}`,
|
|
613
|
+
images: `https://github.com/${project}/raw/${branchName}`,
|
|
614
|
+
repository: `https://github.com/${project}`,
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
else if (/^gitlab/.test(match.groups.domain)) {
|
|
618
|
+
return {
|
|
619
|
+
content: `https://gitlab.com/${project}/-/blob/${branchName}`,
|
|
620
|
+
images: `https://gitlab.com/${project}/-/raw/${branchName}`,
|
|
621
|
+
repository: `https://gitlab.com/${project}`,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
return undefined;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
exports.MarkdownProcessor = MarkdownProcessor;
|
|
628
|
+
class ReadmeProcessor extends MarkdownProcessor {
|
|
629
|
+
constructor(manifest, options = {}) {
|
|
630
|
+
super(manifest, 'README.md', /^extension\/readme.md$/i, 'Microsoft.VisualStudio.Services.Content.Details', options);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
exports.ReadmeProcessor = ReadmeProcessor;
|
|
634
|
+
class ChangelogProcessor extends MarkdownProcessor {
|
|
635
|
+
constructor(manifest, options = {}) {
|
|
636
|
+
super(manifest, 'CHANGELOG.md', /^extension\/changelog.md$/i, 'Microsoft.VisualStudio.Services.Content.Changelog', options);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
exports.ChangelogProcessor = ChangelogProcessor;
|
|
640
|
+
class LicenseProcessor extends BaseProcessor {
|
|
641
|
+
constructor(manifest) {
|
|
642
|
+
super(manifest);
|
|
643
|
+
this.didFindLicense = false;
|
|
644
|
+
const match = /^SEE LICENSE IN (.*)$/.exec(manifest.license || '');
|
|
645
|
+
if (!match || !match[1]) {
|
|
646
|
+
this.expectedLicenseName = 'LICENSE.md, LICENSE.txt or LICENSE';
|
|
647
|
+
this.filter = name => /^extension\/license(\.(md|txt))?$/i.test(name);
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
this.expectedLicenseName = match[1];
|
|
651
|
+
const regexp = new RegExp('^extension/' + match[1] + '$');
|
|
652
|
+
this.filter = regexp.test.bind(regexp);
|
|
653
|
+
}
|
|
654
|
+
delete this.vsix.license;
|
|
655
|
+
}
|
|
656
|
+
onFile(file) {
|
|
657
|
+
if (!this.didFindLicense) {
|
|
658
|
+
let normalizedPath = util.normalize(file.path);
|
|
659
|
+
if (this.filter(normalizedPath)) {
|
|
660
|
+
if (!path.extname(normalizedPath)) {
|
|
661
|
+
file.path += '.txt';
|
|
662
|
+
normalizedPath += '.txt';
|
|
663
|
+
}
|
|
664
|
+
this.assets.push({ type: 'Microsoft.VisualStudio.Services.Content.License', path: normalizedPath });
|
|
665
|
+
this.vsix.license = normalizedPath;
|
|
666
|
+
this.didFindLicense = true;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return Promise.resolve(file);
|
|
670
|
+
}
|
|
671
|
+
async onEnd() {
|
|
672
|
+
if (!this.didFindLicense) {
|
|
673
|
+
util.log.warn(`${this.expectedLicenseName} not found`);
|
|
674
|
+
if (!/^y$/i.test(await util.read('Do you want to continue? [y/N] '))) {
|
|
675
|
+
throw new Error('Aborted');
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
class LaunchEntryPointProcessor extends BaseProcessor {
|
|
681
|
+
constructor(manifest) {
|
|
682
|
+
super(manifest);
|
|
683
|
+
this.entryPoints = new Set();
|
|
684
|
+
if (manifest.main) {
|
|
685
|
+
this.entryPoints.add(util.normalize(path.join('extension', this.appendJSExt(manifest.main))));
|
|
686
|
+
}
|
|
687
|
+
if (manifest.browser) {
|
|
688
|
+
this.entryPoints.add(util.normalize(path.join('extension', this.appendJSExt(manifest.browser))));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
appendJSExt(filePath) {
|
|
692
|
+
if (filePath.endsWith('.js')) {
|
|
693
|
+
return filePath;
|
|
694
|
+
}
|
|
695
|
+
return filePath + '.js';
|
|
696
|
+
}
|
|
697
|
+
onFile(file) {
|
|
698
|
+
this.entryPoints.delete(util.normalize(file.path));
|
|
699
|
+
return Promise.resolve(file);
|
|
700
|
+
}
|
|
701
|
+
async onEnd() {
|
|
702
|
+
if (this.entryPoints.size > 0) {
|
|
703
|
+
const files = [...this.entryPoints].join(',\n ');
|
|
704
|
+
throw new Error(`Extension entrypoint(s) missing. Make sure these files exist and aren't ignored by '.vscodeignore':\n ${files}`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
class IconProcessor extends BaseProcessor {
|
|
709
|
+
constructor(manifest) {
|
|
710
|
+
super(manifest);
|
|
711
|
+
this.didFindIcon = false;
|
|
712
|
+
this.icon = manifest.icon && `extension/${manifest.icon}`;
|
|
713
|
+
delete this.vsix.icon;
|
|
714
|
+
}
|
|
715
|
+
onFile(file) {
|
|
716
|
+
const normalizedPath = util.normalize(file.path);
|
|
717
|
+
if (normalizedPath === this.icon) {
|
|
718
|
+
this.didFindIcon = true;
|
|
719
|
+
this.assets.push({ type: 'Microsoft.VisualStudio.Services.Icons.Default', path: normalizedPath });
|
|
720
|
+
this.vsix.icon = this.icon;
|
|
721
|
+
}
|
|
722
|
+
return Promise.resolve(file);
|
|
723
|
+
}
|
|
724
|
+
async onEnd() {
|
|
725
|
+
if (this.icon && !this.didFindIcon) {
|
|
726
|
+
return Promise.reject(new Error(`The specified icon '${this.icon}' wasn't found in the extension.`));
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
const ValidExtensionKinds = new Set(['ui', 'workspace']);
|
|
731
|
+
function isWebKind(manifest) {
|
|
732
|
+
const extensionKind = getExtensionKind(manifest);
|
|
733
|
+
return extensionKind.some(kind => kind === 'web');
|
|
734
|
+
}
|
|
735
|
+
exports.isWebKind = isWebKind;
|
|
736
|
+
const extensionPointExtensionKindsMap = new Map();
|
|
737
|
+
extensionPointExtensionKindsMap.set('jsonValidation', ['workspace', 'web']);
|
|
738
|
+
extensionPointExtensionKindsMap.set('localizations', ['ui', 'workspace']);
|
|
739
|
+
extensionPointExtensionKindsMap.set('debuggers', ['workspace']);
|
|
740
|
+
extensionPointExtensionKindsMap.set('terminal', ['workspace']);
|
|
741
|
+
extensionPointExtensionKindsMap.set('typescriptServerPlugins', ['workspace']);
|
|
742
|
+
extensionPointExtensionKindsMap.set('markdown.previewStyles', ['workspace', 'web']);
|
|
743
|
+
extensionPointExtensionKindsMap.set('markdown.previewScripts', ['workspace', 'web']);
|
|
744
|
+
extensionPointExtensionKindsMap.set('markdown.markdownItPlugins', ['workspace', 'web']);
|
|
745
|
+
extensionPointExtensionKindsMap.set('html.customData', ['workspace', 'web']);
|
|
746
|
+
extensionPointExtensionKindsMap.set('css.customData', ['workspace', 'web']);
|
|
747
|
+
function getExtensionKind(manifest) {
|
|
748
|
+
const deduced = deduceExtensionKinds(manifest);
|
|
749
|
+
// check the manifest
|
|
750
|
+
if (manifest.extensionKind) {
|
|
751
|
+
const result = Array.isArray(manifest.extensionKind)
|
|
752
|
+
? manifest.extensionKind
|
|
753
|
+
: manifest.extensionKind === 'ui'
|
|
754
|
+
? ['ui', 'workspace']
|
|
755
|
+
: [manifest.extensionKind];
|
|
756
|
+
// Add web kind if the extension can run as web extension
|
|
757
|
+
if (deduced.includes('web') && !result.includes('web')) {
|
|
758
|
+
result.push('web');
|
|
759
|
+
}
|
|
760
|
+
return result;
|
|
761
|
+
}
|
|
762
|
+
return deduced;
|
|
763
|
+
}
|
|
764
|
+
function deduceExtensionKinds(manifest) {
|
|
765
|
+
// Not an UI extension if it has main
|
|
766
|
+
if (manifest.main) {
|
|
767
|
+
if (manifest.browser) {
|
|
768
|
+
return ['workspace', 'web'];
|
|
769
|
+
}
|
|
770
|
+
return ['workspace'];
|
|
771
|
+
}
|
|
772
|
+
if (manifest.browser) {
|
|
773
|
+
return ['web'];
|
|
774
|
+
}
|
|
775
|
+
let result = ['ui', 'workspace', 'web'];
|
|
776
|
+
const isNonEmptyArray = (obj) => Array.isArray(obj) && obj.length > 0;
|
|
777
|
+
// Extension pack defaults to workspace,web extensionKind
|
|
778
|
+
if (isNonEmptyArray(manifest.extensionPack) || isNonEmptyArray(manifest.extensionDependencies)) {
|
|
779
|
+
result = ['workspace', 'web'];
|
|
780
|
+
}
|
|
781
|
+
if (manifest.contributes) {
|
|
782
|
+
for (const contribution of Object.keys(manifest.contributes)) {
|
|
783
|
+
const supportedExtensionKinds = extensionPointExtensionKindsMap.get(contribution);
|
|
784
|
+
if (supportedExtensionKinds) {
|
|
785
|
+
result = result.filter(extensionKind => supportedExtensionKinds.indexOf(extensionKind) !== -1);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return result;
|
|
790
|
+
}
|
|
791
|
+
class NLSProcessor extends BaseProcessor {
|
|
792
|
+
constructor(manifest) {
|
|
793
|
+
super(manifest);
|
|
794
|
+
this.translations = Object.create(null);
|
|
795
|
+
if (!manifest.contributes ||
|
|
796
|
+
!manifest.contributes.localizations ||
|
|
797
|
+
manifest.contributes.localizations.length === 0) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
const localizations = manifest.contributes.localizations;
|
|
801
|
+
const translations = Object.create(null);
|
|
802
|
+
// take last reference in the manifest for any given language
|
|
803
|
+
for (const localization of localizations) {
|
|
804
|
+
for (const translation of localization.translations) {
|
|
805
|
+
if (translation.id === 'vscode' && !!translation.path) {
|
|
806
|
+
const translationPath = util.normalize(translation.path.replace(/^\.[\/\\]/, ''));
|
|
807
|
+
translations[localization.languageId.toUpperCase()] = `extension/${translationPath}`;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
// invert the map for later easier retrieval
|
|
812
|
+
for (const languageId of Object.keys(translations)) {
|
|
813
|
+
this.translations[translations[languageId]] = languageId;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
onFile(file) {
|
|
817
|
+
const normalizedPath = util.normalize(file.path);
|
|
818
|
+
const language = this.translations[normalizedPath];
|
|
819
|
+
if (language) {
|
|
820
|
+
this.assets.push({ type: `Microsoft.VisualStudio.Code.Translation.${language}`, path: normalizedPath });
|
|
821
|
+
}
|
|
822
|
+
return Promise.resolve(file);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
exports.NLSProcessor = NLSProcessor;
|
|
826
|
+
class ValidationProcessor extends BaseProcessor {
|
|
827
|
+
constructor() {
|
|
828
|
+
super(...arguments);
|
|
829
|
+
this.files = new Map();
|
|
830
|
+
this.duplicates = new Set();
|
|
831
|
+
}
|
|
832
|
+
async onFile(file) {
|
|
833
|
+
const lower = file.path.toLowerCase();
|
|
834
|
+
const existing = this.files.get(lower);
|
|
835
|
+
if (existing) {
|
|
836
|
+
this.duplicates.add(lower);
|
|
837
|
+
existing.push(file.path);
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
this.files.set(lower, [file.path]);
|
|
841
|
+
}
|
|
842
|
+
return file;
|
|
843
|
+
}
|
|
844
|
+
async onEnd() {
|
|
845
|
+
if (this.duplicates.size === 0) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const messages = [
|
|
849
|
+
`The following files have the same case insensitive path, which isn't supported by the VSIX format:`,
|
|
850
|
+
];
|
|
851
|
+
for (const lower of this.duplicates) {
|
|
852
|
+
for (const filePath of this.files.get(lower)) {
|
|
853
|
+
messages.push(` - ${filePath}`);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
throw new Error(messages.join('\n'));
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
exports.ValidationProcessor = ValidationProcessor;
|
|
860
|
+
function validateManifest(manifest) {
|
|
861
|
+
(0, validation_1.validateExtensionName)(manifest.name);
|
|
862
|
+
if (!manifest.version) {
|
|
863
|
+
throw new Error('Manifest missing field: version');
|
|
864
|
+
}
|
|
865
|
+
if (manifest.pricing && !['Free', 'Trial'].includes(manifest.pricing)) {
|
|
866
|
+
throw new Error('Pricing should be Free or Trial');
|
|
867
|
+
}
|
|
868
|
+
(0, validation_1.validateVersion)(manifest.version);
|
|
869
|
+
if (!manifest.engines) {
|
|
870
|
+
throw new Error('Manifest missing field: engines');
|
|
871
|
+
}
|
|
872
|
+
if (!manifest.engines['vscode']) {
|
|
873
|
+
throw new Error('Manifest missing field: engines.vscode');
|
|
874
|
+
}
|
|
875
|
+
(0, validation_1.validateEngineCompatibility)(manifest.engines['vscode']);
|
|
876
|
+
const hasActivationEvents = !!manifest.activationEvents;
|
|
877
|
+
const hasMain = !!manifest.main;
|
|
878
|
+
const hasBrowser = !!manifest.browser;
|
|
879
|
+
if (hasActivationEvents) {
|
|
880
|
+
if (!hasMain && !hasBrowser) {
|
|
881
|
+
throw new Error("Manifest needs either a 'main' or 'browser' property, given it has a 'activationEvents' property.");
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
else if (hasMain) {
|
|
885
|
+
throw new Error("Manifest needs the 'activationEvents' property, given it has a 'main' property.");
|
|
886
|
+
}
|
|
887
|
+
else if (hasBrowser) {
|
|
888
|
+
throw new Error("Manifest needs the 'activationEvents' property, given it has a 'browser' property.");
|
|
889
|
+
}
|
|
890
|
+
if (manifest.devDependencies && manifest.devDependencies['@types/vscode']) {
|
|
891
|
+
(0, validation_1.validateVSCodeTypesCompatibility)(manifest.engines['vscode'], manifest.devDependencies['@types/vscode']);
|
|
892
|
+
}
|
|
893
|
+
if (/\.svg$/i.test(manifest.icon || '')) {
|
|
894
|
+
throw new Error(`SVGs can't be used as icons: ${manifest.icon}`);
|
|
895
|
+
}
|
|
896
|
+
(manifest.badges ?? []).forEach(badge => {
|
|
897
|
+
const decodedUrl = decodeURI(badge.url);
|
|
898
|
+
const srcUrl = new url.URL(decodedUrl);
|
|
899
|
+
if (!/^https:$/i.test(srcUrl.protocol)) {
|
|
900
|
+
throw new Error(`Badge URLs must come from an HTTPS source: ${badge.url}`);
|
|
901
|
+
}
|
|
902
|
+
if (/\.svg$/i.test(srcUrl.pathname) && !isHostTrusted(srcUrl)) {
|
|
903
|
+
throw new Error(`Badge SVGs are restricted. Please use other file image formats, such as PNG: ${badge.url}`);
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
Object.keys(manifest.dependencies || {}).forEach(dep => {
|
|
907
|
+
if (dep === 'vscode') {
|
|
908
|
+
throw new Error(`You should not depend on 'vscode' in your 'dependencies'. Did you mean to add it to 'devDependencies'?`);
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
if (manifest.extensionKind) {
|
|
912
|
+
const extensionKinds = Array.isArray(manifest.extensionKind) ? manifest.extensionKind : [manifest.extensionKind];
|
|
913
|
+
for (const kind of extensionKinds) {
|
|
914
|
+
if (!ValidExtensionKinds.has(kind)) {
|
|
915
|
+
throw new Error(`Manifest contains invalid value '${kind}' in the 'extensionKind' property. Allowed values are ${[
|
|
916
|
+
...ValidExtensionKinds,
|
|
917
|
+
]
|
|
918
|
+
.map(k => `'${k}'`)
|
|
919
|
+
.join(', ')}.`);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
if (manifest.sponsor) {
|
|
924
|
+
let isValidSponsorUrl = true;
|
|
925
|
+
try {
|
|
926
|
+
const sponsorUrl = new url.URL(manifest.sponsor.url);
|
|
927
|
+
isValidSponsorUrl = /^(https|http):$/i.test(sponsorUrl.protocol);
|
|
928
|
+
}
|
|
929
|
+
catch (error) {
|
|
930
|
+
isValidSponsorUrl = false;
|
|
931
|
+
}
|
|
932
|
+
if (!isValidSponsorUrl) {
|
|
933
|
+
throw new Error(`Manifest contains invalid value '${manifest.sponsor.url}' in the 'sponsor' property. It must be a valid URL with a HTTP or HTTPS protocol.`);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
return manifest;
|
|
937
|
+
}
|
|
938
|
+
exports.validateManifest = validateManifest;
|
|
939
|
+
function readManifest(cwd = process.cwd(), nls = true) {
|
|
940
|
+
const manifestPath = path.join(cwd, 'package.json');
|
|
941
|
+
const manifestNLSPath = path.join(cwd, 'package.nls.json');
|
|
942
|
+
const manifest = fs.promises
|
|
943
|
+
.readFile(manifestPath, 'utf8')
|
|
944
|
+
.catch(() => Promise.reject(`Extension manifest not found: ${manifestPath}`))
|
|
945
|
+
.then(manifestStr => {
|
|
946
|
+
try {
|
|
947
|
+
return Promise.resolve(JSON.parse(manifestStr));
|
|
948
|
+
}
|
|
949
|
+
catch (e) {
|
|
950
|
+
return Promise.reject(`Error parsing 'package.json' manifest file: not a valid JSON file.`);
|
|
951
|
+
}
|
|
952
|
+
})
|
|
953
|
+
.then(validateManifest);
|
|
954
|
+
if (!nls) {
|
|
955
|
+
return manifest;
|
|
956
|
+
}
|
|
957
|
+
const manifestNLS = fs.promises
|
|
958
|
+
.readFile(manifestNLSPath, 'utf8')
|
|
959
|
+
.catch(err => (err.code !== 'ENOENT' ? Promise.reject(err) : Promise.resolve('{}')))
|
|
960
|
+
.then(raw => {
|
|
961
|
+
try {
|
|
962
|
+
return Promise.resolve(JSON.parse(raw));
|
|
963
|
+
}
|
|
964
|
+
catch (e) {
|
|
965
|
+
return Promise.reject(`Error parsing JSON manifest translations file: ${manifestNLSPath}`);
|
|
966
|
+
}
|
|
967
|
+
});
|
|
968
|
+
return Promise.all([manifest, manifestNLS]).then(([manifest, translations]) => {
|
|
969
|
+
return (0, nls_1.patchNLS)(manifest, translations);
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
exports.readManifest = readManifest;
|
|
973
|
+
const escapeChars = new Map([
|
|
974
|
+
["'", '''],
|
|
975
|
+
['"', '"'],
|
|
976
|
+
['<', '<'],
|
|
977
|
+
['>', '>'],
|
|
978
|
+
['&', '&'],
|
|
979
|
+
]);
|
|
980
|
+
function escape(value) {
|
|
981
|
+
return String(value).replace(/(['"<>&])/g, (_, char) => escapeChars.get(char));
|
|
982
|
+
}
|
|
983
|
+
async function toVsixManifest(vsix) {
|
|
984
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
985
|
+
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
|
|
986
|
+
<Metadata>
|
|
987
|
+
<Identity Language="en-US" Id="${escape(vsix.id)}" Version="${escape(vsix.version)}" Publisher="${escape(vsix.publisher)}" ${vsix.target ? `TargetPlatform="${escape(vsix.target)}"` : ''}/>
|
|
988
|
+
<DisplayName>${escape(vsix.displayName)}</DisplayName>
|
|
989
|
+
<Description xml:space="preserve">${escape(vsix.description)}</Description>
|
|
990
|
+
<Tags>${escape(vsix.tags)}</Tags>
|
|
991
|
+
<Categories>${escape(vsix.categories)}</Categories>
|
|
992
|
+
<GalleryFlags>${escape(vsix.flags)}</GalleryFlags>
|
|
993
|
+
${!vsix.badges
|
|
994
|
+
? ''
|
|
995
|
+
: `<Badges>${vsix.badges
|
|
996
|
+
.map(badge => `<Badge Link="${escape(badge.href)}" ImgUri="${escape(badge.url)}" Description="${escape(badge.description)}" />`)
|
|
997
|
+
.join('\n')}</Badges>`}
|
|
998
|
+
<Properties>
|
|
999
|
+
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="${escape(vsix.engine)}" />
|
|
1000
|
+
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="${escape(vsix.extensionDependencies)}" />
|
|
1001
|
+
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="${escape(vsix.extensionPack)}" />
|
|
1002
|
+
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="${escape(vsix.extensionKind)}" />
|
|
1003
|
+
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="${escape(vsix.localizedLanguages)}" />
|
|
1004
|
+
${vsix.preRelease ? `<Property Id="Microsoft.VisualStudio.Code.PreRelease" Value="${escape(vsix.preRelease)}" />` : ''}
|
|
1005
|
+
${vsix.sponsorLink
|
|
1006
|
+
? `<Property Id="Microsoft.VisualStudio.Code.SponsorLink" Value="${escape(vsix.sponsorLink)}" />`
|
|
1007
|
+
: ''}
|
|
1008
|
+
${!vsix.links.repository
|
|
1009
|
+
? ''
|
|
1010
|
+
: `<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="${escape(vsix.links.repository)}" />
|
|
1011
|
+
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="${escape(vsix.links.repository)}" />
|
|
1012
|
+
${vsix.links.github
|
|
1013
|
+
? `<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="${escape(vsix.links.github)}" />`
|
|
1014
|
+
: `<Property Id="Microsoft.VisualStudio.Services.Links.Repository" Value="${escape(vsix.links.repository)}" />`}`}
|
|
1015
|
+
${vsix.links.bugs
|
|
1016
|
+
? `<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="${escape(vsix.links.bugs)}" />`
|
|
1017
|
+
: ''}
|
|
1018
|
+
${vsix.links.homepage
|
|
1019
|
+
? `<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="${escape(vsix.links.homepage)}" />`
|
|
1020
|
+
: ''}
|
|
1021
|
+
${vsix.galleryBanner.color
|
|
1022
|
+
? `<Property Id="Microsoft.VisualStudio.Services.Branding.Color" Value="${escape(vsix.galleryBanner.color)}" />`
|
|
1023
|
+
: ''}
|
|
1024
|
+
${vsix.galleryBanner.theme
|
|
1025
|
+
? `<Property Id="Microsoft.VisualStudio.Services.Branding.Theme" Value="${escape(vsix.galleryBanner.theme)}" />`
|
|
1026
|
+
: ''}
|
|
1027
|
+
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="${escape(vsix.githubMarkdown)}" />
|
|
1028
|
+
<Property Id="Microsoft.VisualStudio.Services.Content.Pricing" Value="${escape(vsix.pricing)}"/>
|
|
1029
|
+
|
|
1030
|
+
${vsix.enableMarketplaceQnA !== undefined
|
|
1031
|
+
? `<Property Id="Microsoft.VisualStudio.Services.EnableMarketplaceQnA" Value="${escape(vsix.enableMarketplaceQnA)}" />`
|
|
1032
|
+
: ''}
|
|
1033
|
+
${vsix.customerQnALink !== undefined
|
|
1034
|
+
? `<Property Id="Microsoft.VisualStudio.Services.CustomerQnALink" Value="${escape(vsix.customerQnALink)}" />`
|
|
1035
|
+
: ''}
|
|
1036
|
+
</Properties>
|
|
1037
|
+
${vsix.license ? `<License>${escape(vsix.license)}</License>` : ''}
|
|
1038
|
+
${vsix.icon ? `<Icon>${escape(vsix.icon)}</Icon>` : ''}
|
|
1039
|
+
</Metadata>
|
|
1040
|
+
<Installation>
|
|
1041
|
+
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
|
|
1042
|
+
</Installation>
|
|
1043
|
+
<Dependencies/>
|
|
1044
|
+
<Assets>
|
|
1045
|
+
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
|
|
1046
|
+
${vsix.assets
|
|
1047
|
+
.map(asset => `<Asset Type="${escape(asset.type)}" Path="${escape(asset.path)}" Addressable="true" />`)
|
|
1048
|
+
.join('\n')}
|
|
1049
|
+
</Assets>
|
|
1050
|
+
</PackageManifest>`;
|
|
1051
|
+
}
|
|
1052
|
+
exports.toVsixManifest = toVsixManifest;
|
|
1053
|
+
const defaultMimetypes = new Map([
|
|
1054
|
+
['.json', 'application/json'],
|
|
1055
|
+
['.vsixmanifest', 'text/xml'],
|
|
1056
|
+
]);
|
|
1057
|
+
async function toContentTypes(files) {
|
|
1058
|
+
const mimetypes = new Map(defaultMimetypes);
|
|
1059
|
+
for (const file of files) {
|
|
1060
|
+
const ext = path.extname(file.path).toLowerCase();
|
|
1061
|
+
if (ext) {
|
|
1062
|
+
mimetypes.set(ext, mime_1.default.lookup(ext));
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
const contentTypes = [];
|
|
1066
|
+
for (const [extension, contentType] of mimetypes) {
|
|
1067
|
+
contentTypes.push(`<Default Extension="${extension}" ContentType="${contentType}"/>`);
|
|
1068
|
+
}
|
|
1069
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
1070
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">${contentTypes.join('')}</Types>
|
|
1071
|
+
`;
|
|
1072
|
+
}
|
|
1073
|
+
exports.toContentTypes = toContentTypes;
|
|
1074
|
+
const defaultIgnore = [
|
|
1075
|
+
'.vscodeignore',
|
|
1076
|
+
'package-lock.json',
|
|
1077
|
+
'npm-debug.log',
|
|
1078
|
+
'yarn.lock',
|
|
1079
|
+
'yarn-error.log',
|
|
1080
|
+
'npm-shrinkwrap.json',
|
|
1081
|
+
'.editorconfig',
|
|
1082
|
+
'.npmrc',
|
|
1083
|
+
'.yarnrc',
|
|
1084
|
+
'.gitattributes',
|
|
1085
|
+
'*.todo',
|
|
1086
|
+
'tslint.yaml',
|
|
1087
|
+
'.eslintrc*',
|
|
1088
|
+
'.babelrc*',
|
|
1089
|
+
'.prettierrc*',
|
|
1090
|
+
'.cz-config.js',
|
|
1091
|
+
'.commitlintrc*',
|
|
1092
|
+
'webpack.config.js',
|
|
1093
|
+
'ISSUE_TEMPLATE.md',
|
|
1094
|
+
'CONTRIBUTING.md',
|
|
1095
|
+
'PULL_REQUEST_TEMPLATE.md',
|
|
1096
|
+
'CODE_OF_CONDUCT.md',
|
|
1097
|
+
'.github',
|
|
1098
|
+
'.travis.yml',
|
|
1099
|
+
'appveyor.yml',
|
|
1100
|
+
'**/.git/**',
|
|
1101
|
+
'**/*.vsix',
|
|
1102
|
+
'**/.DS_Store',
|
|
1103
|
+
'**/*.vsixmanifest',
|
|
1104
|
+
'**/.vscode-test/**',
|
|
1105
|
+
'**/.vscode-test-web/**',
|
|
1106
|
+
];
|
|
1107
|
+
const notIgnored = ['!package.json', '!README.md'];
|
|
1108
|
+
async function collectAllFiles(cwd, dependencies, dependencyEntryPoints) {
|
|
1109
|
+
const deps = await (0, npm_1.getDependencies)(cwd, dependencies, dependencyEntryPoints);
|
|
1110
|
+
const promises = deps.map(dep => (0, util_1.promisify)(glob_1.default)('**', { cwd: dep, nodir: true, dot: true, ignore: 'node_modules/**' }).then(files => files.map(f => path.relative(cwd, path.join(dep, f))).map(f => f.replace(/\\/g, '/'))));
|
|
1111
|
+
return Promise.all(promises).then(util.flatten);
|
|
1112
|
+
}
|
|
1113
|
+
function collectFiles(cwd, dependencies, dependencyEntryPoints, ignoreFile) {
|
|
1114
|
+
return collectAllFiles(cwd, dependencies, dependencyEntryPoints).then(files => {
|
|
1115
|
+
files = files.filter(f => !/\r$/m.test(f));
|
|
1116
|
+
return (fs.promises
|
|
1117
|
+
.readFile(ignoreFile ? ignoreFile : path.join(cwd, '.vscodeignore'), 'utf8')
|
|
1118
|
+
.catch(err => err.code !== 'ENOENT' ? Promise.reject(err) : ignoreFile ? Promise.reject(err) : Promise.resolve(''))
|
|
1119
|
+
// Parse raw ignore by splitting output into lines and filtering out empty lines and comments
|
|
1120
|
+
.then(rawIgnore => rawIgnore
|
|
1121
|
+
.split(/[\n\r]/)
|
|
1122
|
+
.map(s => s.trim())
|
|
1123
|
+
.filter(s => !!s)
|
|
1124
|
+
.filter(i => !/^\s*#/.test(i)))
|
|
1125
|
+
// Add '/**' to possible folder names
|
|
1126
|
+
.then(ignore => [
|
|
1127
|
+
...ignore,
|
|
1128
|
+
...ignore.filter(i => !/(^|\/)[^/]*\*[^/]*$/.test(i)).map(i => (/\/$/.test(i) ? `${i}**` : `${i}/**`)),
|
|
1129
|
+
])
|
|
1130
|
+
// Combine with default ignore list
|
|
1131
|
+
.then(ignore => [...defaultIgnore, ...ignore, ...notIgnored])
|
|
1132
|
+
// Split into ignore and negate list
|
|
1133
|
+
.then(ignore => ignore.reduce((r, e) => (!/^\s*!/.test(e) ? [[...r[0], e], r[1]] : [r[0], [...r[1], e]]), [[], []]))
|
|
1134
|
+
.then(r => ({ ignore: r[0], negate: r[1] }))
|
|
1135
|
+
// Filter out files
|
|
1136
|
+
.then(({ ignore, negate }) => files.filter(f => !ignore.some(i => (0, minimatch_1.default)(f, i, MinimatchOptions)) ||
|
|
1137
|
+
negate.some(i => (0, minimatch_1.default)(f, i.substr(1), MinimatchOptions)))));
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
function processFiles(processors, files) {
|
|
1141
|
+
const processedFiles = files.map(file => util.chain(file, processors, (file, processor) => processor.onFile(file)));
|
|
1142
|
+
return Promise.all(processedFiles).then(files => {
|
|
1143
|
+
return util.sequence(processors.map(p => () => p.onEnd())).then(() => {
|
|
1144
|
+
const assets = processors.reduce((r, p) => [...r, ...p.assets], []);
|
|
1145
|
+
const tags = [
|
|
1146
|
+
...processors.reduce((r, p) => {
|
|
1147
|
+
for (const tag of p.tags) {
|
|
1148
|
+
if (tag) {
|
|
1149
|
+
r.add(tag);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return r;
|
|
1153
|
+
}, new Set()),
|
|
1154
|
+
].join(',');
|
|
1155
|
+
const vsix = processors.reduce((r, p) => ({ ...r, ...p.vsix }), { assets, tags });
|
|
1156
|
+
return Promise.all([toVsixManifest(vsix), toContentTypes(files)]).then(result => {
|
|
1157
|
+
return [
|
|
1158
|
+
{ path: 'extension.vsixmanifest', contents: Buffer.from(result[0], 'utf8') },
|
|
1159
|
+
{ path: '[Content_Types].xml', contents: Buffer.from(result[1], 'utf8') },
|
|
1160
|
+
...files,
|
|
1161
|
+
];
|
|
1162
|
+
});
|
|
1163
|
+
});
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
exports.processFiles = processFiles;
|
|
1167
|
+
function createDefaultProcessors(manifest, options = {}) {
|
|
1168
|
+
return [
|
|
1169
|
+
new ManifestProcessor(manifest, options),
|
|
1170
|
+
new TagsProcessor(manifest),
|
|
1171
|
+
new ReadmeProcessor(manifest, options),
|
|
1172
|
+
new ChangelogProcessor(manifest, options),
|
|
1173
|
+
new LaunchEntryPointProcessor(manifest),
|
|
1174
|
+
new LicenseProcessor(manifest),
|
|
1175
|
+
new IconProcessor(manifest),
|
|
1176
|
+
new NLSProcessor(manifest),
|
|
1177
|
+
new ValidationProcessor(manifest),
|
|
1178
|
+
];
|
|
1179
|
+
}
|
|
1180
|
+
exports.createDefaultProcessors = createDefaultProcessors;
|
|
1181
|
+
function getDependenciesOption(options) {
|
|
1182
|
+
if (options.dependencies === false) {
|
|
1183
|
+
return 'none';
|
|
1184
|
+
}
|
|
1185
|
+
switch (options.useYarn) {
|
|
1186
|
+
case true:
|
|
1187
|
+
return 'yarn';
|
|
1188
|
+
case false:
|
|
1189
|
+
return 'npm';
|
|
1190
|
+
default:
|
|
1191
|
+
return undefined;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
function collect(manifest, options = {}) {
|
|
1195
|
+
const cwd = options.cwd || process.cwd();
|
|
1196
|
+
const packagedDependencies = options.dependencyEntryPoints || undefined;
|
|
1197
|
+
const ignoreFile = options.ignoreFile || undefined;
|
|
1198
|
+
const processors = createDefaultProcessors(manifest, options);
|
|
1199
|
+
return collectFiles(cwd, getDependenciesOption(options), packagedDependencies, ignoreFile).then(fileNames => {
|
|
1200
|
+
const files = fileNames.map(f => ({ path: `extension/${f}`, localPath: path.join(cwd, f) }));
|
|
1201
|
+
return processFiles(processors, files);
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
exports.collect = collect;
|
|
1205
|
+
function writeVsix(files, packagePath) {
|
|
1206
|
+
return fs.promises
|
|
1207
|
+
.unlink(packagePath)
|
|
1208
|
+
.catch(err => (err.code !== 'ENOENT' ? Promise.reject(err) : Promise.resolve(null)))
|
|
1209
|
+
.then(() => new Promise((c, e) => {
|
|
1210
|
+
const zip = new yazl.ZipFile();
|
|
1211
|
+
files.forEach(f => isInMemoryFile(f)
|
|
1212
|
+
? zip.addBuffer(typeof f.contents === 'string' ? Buffer.from(f.contents, 'utf8') : f.contents, f.path, {
|
|
1213
|
+
mode: f.mode,
|
|
1214
|
+
})
|
|
1215
|
+
: zip.addFile(f.localPath, f.path, { mode: f.mode }));
|
|
1216
|
+
zip.end();
|
|
1217
|
+
const zipStream = fs.createWriteStream(packagePath);
|
|
1218
|
+
zip.outputStream.pipe(zipStream);
|
|
1219
|
+
zip.outputStream.once('error', e);
|
|
1220
|
+
zipStream.once('error', e);
|
|
1221
|
+
zipStream.once('finish', () => c());
|
|
1222
|
+
}));
|
|
1223
|
+
}
|
|
1224
|
+
function getDefaultPackageName(manifest, options) {
|
|
1225
|
+
let version = manifest.version;
|
|
1226
|
+
if (options.version && !(options.updatePackageJson ?? true)) {
|
|
1227
|
+
version = options.version;
|
|
1228
|
+
}
|
|
1229
|
+
if (options.target) {
|
|
1230
|
+
return `${manifest.name}-${options.target}-${version}.vsix`;
|
|
1231
|
+
}
|
|
1232
|
+
return `${manifest.name}-${version}.vsix`;
|
|
1233
|
+
}
|
|
1234
|
+
async function prepublish(cwd, manifest, useYarn) {
|
|
1235
|
+
if (!manifest.scripts || !manifest.scripts['vscode:prepublish']) {
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
if (useYarn === undefined) {
|
|
1239
|
+
useYarn = await (0, npm_1.detectYarn)(cwd);
|
|
1240
|
+
}
|
|
1241
|
+
console.log(`Executing prepublish script '${useYarn ? 'yarn' : 'npm'} run vscode:prepublish'...`);
|
|
1242
|
+
await new Promise((c, e) => {
|
|
1243
|
+
const tool = useYarn ? 'yarn' : 'npm';
|
|
1244
|
+
const child = cp.spawn(tool, ['run', 'vscode:prepublish'], { cwd, shell: true, stdio: 'inherit' });
|
|
1245
|
+
child.on('exit', code => (code === 0 ? c() : e(`${tool} failed with exit code ${code}`)));
|
|
1246
|
+
child.on('error', e);
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
exports.prepublish = prepublish;
|
|
1250
|
+
async function getPackagePath(cwd, manifest, options = {}) {
|
|
1251
|
+
if (!options.packagePath) {
|
|
1252
|
+
return path.join(cwd, getDefaultPackageName(manifest, options));
|
|
1253
|
+
}
|
|
1254
|
+
try {
|
|
1255
|
+
const _stat = await fs.promises.stat(options.packagePath);
|
|
1256
|
+
if (_stat.isDirectory()) {
|
|
1257
|
+
return path.join(options.packagePath, getDefaultPackageName(manifest, options));
|
|
1258
|
+
}
|
|
1259
|
+
else {
|
|
1260
|
+
return options.packagePath;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
catch {
|
|
1264
|
+
return options.packagePath;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
async function pack(options = {}) {
|
|
1268
|
+
const cwd = options.cwd || process.cwd();
|
|
1269
|
+
const manifest = await readManifest(cwd);
|
|
1270
|
+
const files = await collect(manifest, options);
|
|
1271
|
+
const jsFiles = files.filter(f => /\.js$/i.test(f.path));
|
|
1272
|
+
if (files.length > 5000 || jsFiles.length > 100) {
|
|
1273
|
+
console.log(`This extension consists of ${files.length} files, out of which ${jsFiles.length} are JavaScript files. For performance reasons, you should bundle your extension: https://aka.ms/vscode-bundle-extension . You should also exclude unnecessary files by adding them to your .vscodeignore: https://aka.ms/vscode-vscodeignore`);
|
|
1274
|
+
}
|
|
1275
|
+
if (options.version) {
|
|
1276
|
+
manifest.version = options.version;
|
|
1277
|
+
}
|
|
1278
|
+
const packagePath = await getPackagePath(cwd, manifest, options);
|
|
1279
|
+
await writeVsix(files, path.resolve(packagePath));
|
|
1280
|
+
return { manifest, packagePath, files };
|
|
1281
|
+
}
|
|
1282
|
+
exports.pack = pack;
|
|
1283
|
+
async function packageCommand(options = {}) {
|
|
1284
|
+
const cwd = options.cwd || process.cwd();
|
|
1285
|
+
const manifest = await readManifest(cwd);
|
|
1286
|
+
util.patchOptionsWithManifest(options, manifest);
|
|
1287
|
+
await prepublish(cwd, manifest, options.useYarn);
|
|
1288
|
+
await versionBump(options);
|
|
1289
|
+
const { packagePath, files } = await pack(options);
|
|
1290
|
+
const stats = await fs.promises.stat(packagePath);
|
|
1291
|
+
let size = 0;
|
|
1292
|
+
let unit = '';
|
|
1293
|
+
if (stats.size > 1048576) {
|
|
1294
|
+
size = Math.round(stats.size / 10485.76) / 100;
|
|
1295
|
+
unit = 'MB';
|
|
1296
|
+
}
|
|
1297
|
+
else {
|
|
1298
|
+
size = Math.round(stats.size / 10.24) / 100;
|
|
1299
|
+
unit = 'KB';
|
|
1300
|
+
}
|
|
1301
|
+
util.log.done(`Packaged: ${packagePath} (${files.length} files, ${size}${unit})`);
|
|
1302
|
+
}
|
|
1303
|
+
exports.packageCommand = packageCommand;
|
|
1304
|
+
/**
|
|
1305
|
+
* Lists the files included in the extension's package.
|
|
1306
|
+
*/
|
|
1307
|
+
async function listFiles(options = {}) {
|
|
1308
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1309
|
+
const manifest = await readManifest(cwd);
|
|
1310
|
+
if (options.prepublish) {
|
|
1311
|
+
await prepublish(cwd, manifest, options.useYarn);
|
|
1312
|
+
}
|
|
1313
|
+
return await collectFiles(cwd, getDependenciesOption(options), options.packagedDependencies, options.ignoreFile);
|
|
1314
|
+
}
|
|
1315
|
+
exports.listFiles = listFiles;
|
|
1316
|
+
/**
|
|
1317
|
+
* Lists the files included in the extension's package. Runs prepublish.
|
|
1318
|
+
*/
|
|
1319
|
+
async function ls(options = {}) {
|
|
1320
|
+
const files = await listFiles({ ...options, prepublish: true });
|
|
1321
|
+
for (const file of files) {
|
|
1322
|
+
console.log(`${file}`);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
exports.ls = ls;
|
|
1326
|
+
//# sourceMappingURL=package.js.map
|