commit
d999140b44
4 changed files with 122 additions and 4 deletions
|
@ -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}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
14
src/test/fixtures/readme/readme.github.expected.md
vendored
Normal file
14
src/test/fixtures/readme/readme.github.expected.md
vendored
Normal 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/)
|
14
src/test/fixtures/readme/readme.github.md
vendored
Normal file
14
src/test/fixtures/readme/readme.github.md
vendored
Normal 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/)
|
|
@ -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)`;
|
||||
|
|
Loading…
Add table
Reference in a new issue