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'),
};
}