async onFile()

in src/package.ts [658:801]


	async onFile(file: IFile): Promise<IFile> {
		const filePath = util.normalize(file.path);

		if (!this.regexp.test(filePath)) {
			return Promise.resolve(file);
		}

		this.assets.push({ type: this.assetType, path: filePath });

		let contents = await read(file);

		if (/This is the README for your extension /.test(contents)) {
			throw new Error(`Make sure to edit the README.md file before you package or publish your extension.`);
		}

		if (this.rewriteRelativeLinks) {
			const markdownPathRegex = /(!?)\[([^\]\[]*|!\[[^\]\[]*]\([^\)]+\))\]\(([^\)]+)\)/g;
			const urlReplace = (_: string, isImage: string, title: string, link: string) => {
				if (/^mailto:/i.test(link)) {
					return `${isImage}[${title}](${link})`;
				}

				const isLinkRelative = !/^\w+:\/\//.test(link) && link[0] !== '#';

				if (!this.baseContentUrl && !this.baseImagesUrl) {
					const asset = isImage ? 'image' : 'link';

					if (isLinkRelative) {
						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.`
						);
					}
				}

				title = title.replace(markdownPathRegex, urlReplace);
				const prefix = isImage ? this.baseImagesUrl : this.baseContentUrl;

				if (!prefix || !isLinkRelative) {
					return `${isImage}[${title}](${link})`;
				}

				return `${isImage}[${title}](${urljoin(prefix, path.posix.normalize(link))})`;
			};

			// Replace Markdown links with urls
			contents = contents.replace(markdownPathRegex, urlReplace);

			// Replace <img> links with urls
			contents = contents.replace(/<img.+?src=["']([/.\w\s-]+)['"].*?>/g, (all, link) => {
				const isLinkRelative = !/^\w+:\/\//.test(link) && link[0] !== '#';

				if (!this.baseImagesUrl && isLinkRelative) {
					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.`
					);
				}
				const prefix = this.baseImagesUrl;

				if (!prefix || !isLinkRelative) {
					return all;
				}

				return all.replace(link, urljoin(prefix, path.posix.normalize(link)));
			});

			if ((this.gitHubIssueLinking && this.isGitHub) || (this.gitLabIssueLinking && this.isGitLab)) {
				const markdownIssueRegex = /(\s|\n)([\w\d_-]+\/[\w\d_-]+)?#(\d+)\b/g;
				const issueReplace = (
					all: string,
					prefix: string,
					ownerAndRepositoryName: string,
					issueNumber: string
				): string => {
					let result = all;
					let owner: string | undefined;
					let repositoryName: string | undefined;

					if (ownerAndRepositoryName) {
						[owner, repositoryName] = ownerAndRepositoryName.split('/', 2);
					}

					if (owner && repositoryName && issueNumber) {
						// Issue in external repository
						const issueUrl = this.isGitHub
							? urljoin('https://github.com', owner, repositoryName, 'issues', issueNumber)
							: urljoin('https://gitlab.com', owner, repositoryName, '-', 'issues', issueNumber);
						result = prefix + `[${owner}/${repositoryName}#${issueNumber}](${issueUrl})`;
					} else if (!owner && !repositoryName && issueNumber && this.repositoryUrl) {
						// Issue in own repository
						result =
							prefix +
							`[#${issueNumber}](${
								this.isGitHub
									? urljoin(this.repositoryUrl, 'issues', issueNumber)
									: urljoin(this.repositoryUrl, '-', 'issues', issueNumber)
							})`;
					}

					return result;
				};
				// Replace Markdown issue references with urls
				contents = contents.replace(markdownIssueRegex, issueReplace);
			}
		}

		const html = markdownit({ html: true }).render(contents);
		const $ = cheerio.load(html);

		if (this.rewriteRelativeLinks) {
			$('img').each((_, img) => {
				const rawSrc = $(img).attr('src');

				if (!rawSrc) {
					throw new Error(`Images in ${this.name} must have a source.`);
				}

				const src = decodeURI(rawSrc);
				const srcUrl = new url.URL(src);

				if (/^data:$/i.test(srcUrl.protocol) && /^image$/i.test(srcUrl.host) && /\/svg/i.test(srcUrl.pathname)) {
					throw new Error(`SVG data URLs are not allowed in ${this.name}: ${src}`);
				}

				if (!/^https:$/i.test(srcUrl.protocol)) {
					throw new Error(`Images in ${this.name} must come from an HTTPS source: ${src}`);
				}

				if (/\.svg$/i.test(srcUrl.pathname) && !isHostTrusted(srcUrl)) {
					throw new Error(
						`SVGs are restricted in ${this.name}; please use other file image formats, such as PNG: ${src}`
					);
				}
			});
		}

		$('svg').each(() => {
			throw new Error(`SVG tags are not allowed in ${this.name}.`);
		});

		return {
			path: file.path,
			contents: Buffer.from(contents, 'utf8'),
		};
	}