UNPKG

56.6 kBJavaScriptView Raw
1"use strict";
2var __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}));
9var __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});
14var __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};
21var __importDefault = (this && this.__importDefault) || function (mod) {
22 return (mod && mod.__esModule) ? mod : { "default": mod };
23};
24Object.defineProperty(exports, "__esModule", { value: true });
25exports.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;
26const fs = __importStar(require("fs"));
27const path = __importStar(require("path"));
28const util_1 = require("util");
29const cp = __importStar(require("child_process"));
30const yazl = __importStar(require("yazl"));
31const nls_1 = require("./nls");
32const util = __importStar(require("./util"));
33const glob_1 = __importDefault(require("glob"));
34const minimatch_1 = __importDefault(require("minimatch"));
35const markdown_it_1 = __importDefault(require("markdown-it"));
36const cheerio = __importStar(require("cheerio"));
37const url = __importStar(require("url"));
38const mime_1 = __importDefault(require("mime"));
39const semver = __importStar(require("semver"));
40const url_join_1 = __importDefault(require("url-join"));
41const validation_1 = require("./validation");
42const npm_1 = require("./npm");
43const GitHost = __importStar(require("hosted-git-info"));
44const parse_semver_1 = __importDefault(require("parse-semver"));
45const MinimatchOptions = { dot: true };
46function isInMemoryFile(file) {
47 return !!file.contents;
48}
49function 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}
57exports.read = read;
58class 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}
72exports.BaseProcessor = BaseProcessor;
73// https://github.com/npm/cli/blob/latest/lib/utils/hosted-git-info-from-manifest.js
74function 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
79function 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
97function 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
115function 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
126function escapeRegExp(value) {
127 return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
128}
129function toExtensionTags(extensions) {
130 return extensions
131 .map(s => s.replace(/\W/g, ''))
132 .filter(s => !!s)
133 .map(s => `__ext_${s}`);
134}
135function 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 */
143const 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];
187function isGitHubRepository(repository) {
188 return /^https:\/\/github\.com\/|^git@github\.com:/.test(repository ?? '');
189}
190function isGitLabRepository(repository) {
191 return /^https:\/\/gitlab\.com\/|^git@gitlab\.com:/.test(repository ?? '');
192}
193function isGitHubBadge(href) {
194 return /^https:\/\/github\.com\/[^/]+\/[^/]+\/(actions\/)?workflows\/.*badge\.svg/.test(href || '');
195}
196function isHostTrusted(url) {
197 return (url.host && TrustedSVGSources.indexOf(url.host.toLowerCase()) > -1) || isGitHubBadge(url.href);
198}
199async 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}
241exports.versionBump = versionBump;
242exports.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]);
255class 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}
376exports.ManifestProcessor = ManifestProcessor;
377class 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}
432exports.TagsProcessor = TagsProcessor;
433TagsProcessor.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};
471class 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}
627exports.MarkdownProcessor = MarkdownProcessor;
628class ReadmeProcessor extends MarkdownProcessor {
629 constructor(manifest, options = {}) {
630 super(manifest, 'README.md', /^extension\/readme.md$/i, 'Microsoft.VisualStudio.Services.Content.Details', options);
631 }
632}
633exports.ReadmeProcessor = ReadmeProcessor;
634class ChangelogProcessor extends MarkdownProcessor {
635 constructor(manifest, options = {}) {
636 super(manifest, 'CHANGELOG.md', /^extension\/changelog.md$/i, 'Microsoft.VisualStudio.Services.Content.Changelog', options);
637 }
638}
639exports.ChangelogProcessor = ChangelogProcessor;
640class 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}
680class 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}
708class 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}
730const ValidExtensionKinds = new Set(['ui', 'workspace']);
731function isWebKind(manifest) {
732 const extensionKind = getExtensionKind(manifest);
733 return extensionKind.some(kind => kind === 'web');
734}
735exports.isWebKind = isWebKind;
736const extensionPointExtensionKindsMap = new Map();
737extensionPointExtensionKindsMap.set('jsonValidation', ['workspace', 'web']);
738extensionPointExtensionKindsMap.set('localizations', ['ui', 'workspace']);
739extensionPointExtensionKindsMap.set('debuggers', ['workspace']);
740extensionPointExtensionKindsMap.set('terminal', ['workspace']);
741extensionPointExtensionKindsMap.set('typescriptServerPlugins', ['workspace']);
742extensionPointExtensionKindsMap.set('markdown.previewStyles', ['workspace', 'web']);
743extensionPointExtensionKindsMap.set('markdown.previewScripts', ['workspace', 'web']);
744extensionPointExtensionKindsMap.set('markdown.markdownItPlugins', ['workspace', 'web']);
745extensionPointExtensionKindsMap.set('html.customData', ['workspace', 'web']);
746extensionPointExtensionKindsMap.set('css.customData', ['workspace', 'web']);
747function 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}
764function 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}
791class 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}
825exports.NLSProcessor = NLSProcessor;
826class 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}
859exports.ValidationProcessor = ValidationProcessor;
860function 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}
938exports.validateManifest = validateManifest;
939function 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}
972exports.readManifest = readManifest;
973const escapeChars = new Map([
974 ["'", '&apos;'],
975 ['"', '&quot;'],
976 ['<', '&lt;'],
977 ['>', '&gt;'],
978 ['&', '&amp;'],
979]);
980function escape(value) {
981 return String(value).replace(/(['"<>&])/g, (_, char) => escapeChars.get(char));
982}
983async 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}
1052exports.toVsixManifest = toVsixManifest;
1053const defaultMimetypes = new Map([
1054 ['.json', 'application/json'],
1055 ['.vsixmanifest', 'text/xml'],
1056]);
1057async 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}
1073exports.toContentTypes = toContentTypes;
1074const 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];
1107const notIgnored = ['!package.json', '!README.md'];
1108async 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}
1113function 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}
1140function 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}
1166exports.processFiles = processFiles;
1167function 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}
1180exports.createDefaultProcessors = createDefaultProcessors;
1181function 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}
1194function 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}
1204exports.collect = collect;
1205function 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}
1224function 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}
1234async 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}
1249exports.prepublish = prepublish;
1250async 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}
1267async 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}
1282exports.pack = pack;
1283async 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}
1303exports.packageCommand = packageCommand;
1304/**
1305 * Lists the files included in the extension's package.
1306 */
1307async 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}
1315exports.listFiles = listFiles;
1316/**
1317 * Lists the files included in the extension's package. Runs prepublish.
1318 */
1319async function ls(options = {}) {
1320 const files = await listFiles({ ...options, prepublish: true });
1321 for (const file of files) {
1322 console.log(`${file}`);
1323 }
1324}
1325exports.ls = ls;
1326//# sourceMappingURL=package.js.map
\No newline at end of file