Merge pull request #345 from HaaLeo/master

Support GitHub Issue Links
This commit is contained in:
João Moreno 2019-07-11 11:33:21 +02:00 committed by GitHub
commit d999140b44
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 122 additions and 4 deletions

View file

@ -179,6 +179,10 @@ function isHostTrusted(host: string): boolean {
return TrustedSVGSources.indexOf(host.toLowerCase()) > -1;
}
function isGitHubRepository(repository: string): boolean {
return /^https:\/\/github\.com\/|^git@github\.com:/.test(repository || '');
}
class ManifestProcessor extends BaseProcessor {
constructor(manifest: Manifest) {
@ -191,7 +195,7 @@ class ManifestProcessor extends BaseProcessor {
}
const repository = getRepositoryUrl(manifest.repository);
const isGitHub = /^https:\/\/github\.com\/|^git@github\.com:/.test(repository || '');
const isGitHub = isGitHubRepository(repository);
let enableMarketplaceQnA: boolean | undefined;
let customerQnALink: string | undefined;
@ -350,6 +354,8 @@ export class MarkdownProcessor extends BaseProcessor {
private baseContentUrl: string;
private baseImagesUrl: string;
private isGitHub: boolean;
private repositoryUrl: string;
constructor(manifest: Manifest, private name: string, private regexp: RegExp, private assetType: string, options: IPackageOptions = {}) {
super(manifest);
@ -358,6 +364,8 @@ export class MarkdownProcessor extends BaseProcessor {
this.baseContentUrl = options.baseContentUrl || (guess && guess.content);
this.baseImagesUrl = options.baseImagesUrl || options.baseContentUrl || (guess && guess.images);
this.repositoryUrl = (guess && guess.repository);
this.isGitHub = isGitHubRepository(this.repositoryUrl);
}
async onFile(file: IFile): Promise<IFile> {
@ -396,9 +404,36 @@ export class MarkdownProcessor extends BaseProcessor {
return `${isImage}[${title}](${urljoin(prefix, link)})`;
};
// Replace Markdown links with urls
contents = contents.replace(markdownPathRegex, urlReplace);
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;
let repositoryName: string;
if (ownerAndRepositoryName) {
[owner, repositoryName] = ownerAndRepositoryName.split('/', 2);
}
if (this.isGitHub){
if (owner && repositoryName && issueNumber) {
// Issue in external repository
const issueUrl = urljoin('https://github.com', owner, repositoryName, 'issues', issueNumber);
result = prefix + `[${owner}/${repositoryName}#${issueNumber}](${issueUrl})`;
} else if (!owner && !repositoryName && issueNumber) {
// Issue in own repository
result = prefix + `[#${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);
@ -430,7 +465,7 @@ export class MarkdownProcessor extends BaseProcessor {
}
// GitHub heuristics
private guessBaseUrls(): { content: string; images: string; } {
private guessBaseUrls(): { content: string; images: string; repository: string} {
let repository = null;
if (typeof this.manifest.repository === 'string') {
@ -455,7 +490,8 @@ export class MarkdownProcessor extends BaseProcessor {
return {
content: `https://github.com/${account}/${repositoryName}/blob/master`,
images: `https://github.com/${account}/${repositoryName}/raw/master`
images: `https://github.com/${account}/${repositoryName}/raw/master`,
repository: `https://github.com/${account}/${repositoryName}`
};
}
}

View file

@ -0,0 +1,14 @@
# Replace
[#8](https://github.com/username/repository/issues/8)
* Some issue in same repository: [#7](https://github.com/username/repository/issues/7)
* Some issue in other repository: [other/repositoryName#8](https://github.com/other/repositoryName/issues/8)
* Some issue in other repository with fancy name: [my_user-name/my-rep_o12#6](https://github.com/my_user-name/my-rep_o12/issues/6)
# Do not touch this:
* username#4 (no valid github link)
* /#7
* foo/$234/#7
* [#7](http://shouldnottouchthis/)
* [other/repositoryName#8](http://shouldnottouchthis/)

View file

@ -0,0 +1,14 @@
# Replace
#8
* Some issue in same repository: #7
* Some issue in other repository: other/repositoryName#8
* Some issue in other repository with fancy name: my_user-name/my-rep_o12#6
# Do not touch this:
* username#4 (no valid github link)
* /#7
* foo/$234/#7
* [#7](http://shouldnottouchthis/)
* [other/repositoryName#8](http://shouldnottouchthis/)

View file

@ -1500,6 +1500,60 @@ describe('MarkdownProcessor', () => {
});
});
it('should replace issue links with urls if its a github repo.', () => {
const manifest = {
name: 'test',
publisher: 'mocha',
version: '0.0.1',
description: 'test extension',
engines: Object.create(null),
repository: 'https://github.com/username/repository.git'
};
const root = fixture('readme');
const processor = new ReadmeProcessor(manifest, {});
const readme = {
path: 'extension/readme.md',
localPath: path.join(root, 'readme.github.md')
};
return processor.onFile(readme)
.then(file => read(file))
.then(actual => {
return readFile(path.join(root, 'readme.github.expected.md'), 'utf8')
.then(expected => {
assert.equal(actual, expected);
});
});
});
it('should not replace issue links with urls if its not a github repo.', () => {
const manifest = {
name: 'test',
publisher: 'mocha',
version: '0.0.1',
description: 'test extension',
engines: Object.create(null),
repository: 'https://some-other-provider.com/username/repository.git'
};
const root = fixture('readme');
const processor = new ReadmeProcessor(manifest, {});
const readme = {
path: 'extension/readme.md',
localPath: path.join(root, 'readme.github.md')
};
return processor.onFile(readme)
.then(file => read(file))
.then(actual => {
return readFile(path.join(root, 'readme.github.md'), 'utf8')
.then(expected => {
assert.equal(actual, expected);
});
});
});
it('should prevent non-HTTPS images', async () => {
const manifest = { name: 'test', publisher: 'mocha', version: '0.0.1', engines: Object.create(null), repository: 'https://github.com/username/repository' };
const contents = `![title](http://foo.png)`;