This repository has been archived by the owner on Nov 28, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refine interface a little bit, remove `configure()` function. Allow passing in `opts` as a second parameter. Possible options: - `correctnewlines` - comes from `project.flags.correctnewlines` - `stripHtml` - passed in in certain cases Outstanding issues: https://github.com/readmeio/api-explorer/issues/29 https://github.com/readmeio/api-explorer/issues/28
- Loading branch information
Dom Harrington
committed
Sep 13, 2017
1 parent
9639c07
commit fab9103
Showing
8 changed files
with
326 additions
and
235 deletions.
There are no files selected for viewing
41 changes: 41 additions & 0 deletions
41
packages/api-explorer-ui/__tests__/lib/__snapshots__/marked.test.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`should render markdown 1`] = ` | ||
"<p><img src=\\"http://example.com/image.png\\" alt=\\"Image\\"></p> | ||
<ul> | ||
<li >listitem1</li><li style=\\"list-style: none\\" class=\\"checklist\\"><input type=\\"checkbox\\" disabled> checklistitem1</li></ul> | ||
<div class=\\"marked-table\\"><table> | ||
<thead> | ||
<tr> | ||
<th>Tables</th> | ||
<th style=\\"text-align:center\\">Are</th> | ||
<th style=\\"text-align:right\\">Cool</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>col 3 is</td> | ||
<td style=\\"text-align:center\\">right-aligned</td> | ||
<td style=\\"text-align:right\\">$1600</td> | ||
</tr> | ||
<tr> | ||
<td>col 2 is</td> | ||
<td style=\\"text-align:center\\">centered</td> | ||
<td style=\\"text-align:right\\">$12</td> | ||
</tr> | ||
<tr> | ||
<td>zebra stripes</td> | ||
<td style=\\"text-align:center\\">are neat</td> | ||
<td style=\\"text-align:right\\">$1</td> | ||
</tr> | ||
</tbody> | ||
</table></div> | ||
<h1 class=\\"header-scroll\\"><div class=\\"anchor waypoint\\" id=\\"section-h1\\"></div>h1<a class=\\"fa fa-anchor\\" href=\\"#section-h1\\"></a></h1> | ||
<h2 class=\\"header-scroll\\"><div class=\\"anchor waypoint\\" id=\\"section-h2\\"></div>h2<a class=\\"fa fa-anchor\\" href=\\"#section-h2\\"></a></h2> | ||
<h3 class=\\"header-scroll\\"><div class=\\"anchor waypoint\\" id=\\"section-h3\\"></div>h3<a class=\\"fa fa-anchor\\" href=\\"#section-h3\\"></a></h3> | ||
<p><a href=\\"http://example.com\\" target=\\"_self\\">link</a></p> | ||
<p><img src=\\"/img/emojis/joy.png\\" alt=\\":joy+:\\" title=\\":joy:\\" class=\\"emoji\\" align=\\"absmiddle\\" height=\\"20\\" width=\\"20\\"><br><i class=\\"fa fa-lock\\"></i><br>:unknown-emoji:</p> | ||
<pre><code class=\\"lang-js\\"><span class=\\"cm-s-tomorrow-night\\">var a = 1;</span> | ||
</code></pre> | ||
" | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
![Image](http://example.com/image.png) | ||
|
||
- listitem1 | ||
- [ ] checklistitem1 | ||
|
||
| Tables | Are | Cool | | ||
| ------------- |:-------------:| -----:| | ||
| col 3 is | right-aligned | $1600 | | ||
| col 2 is | centered | $12 | | ||
| zebra stripes | are neat | $1 | | ||
|
||
# h1 | ||
## h2 | ||
### h3 | ||
|
||
[link](http://example.com) | ||
|
||
:joy: | ||
:fa-lock: | ||
:unknown-emoji: | ||
|
||
```js | ||
var a = 1; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const fs = require('fs'); | ||
const markdown = require('../../src/lib/markdown'); | ||
|
||
const fixture = fs.readFileSync(`${__dirname}/markdown.txt`, 'utf8'); | ||
|
||
test('should render markdown', () => { | ||
expect(markdown(fixture)).toMatchSnapshot(); | ||
}); | ||
|
||
test('should render empty string if nothing passed in', () => { | ||
expect(markdown('')).toBe(''); | ||
}); | ||
|
||
test('`correctnewlines` option', () => { | ||
expect(markdown('test\ntest\ntest', { correctnewlines: true })).toBe('<p>test\ntest\ntest</p>\n'); | ||
expect(markdown('test\ntest\ntest', { correctnewlines: false })).toBe('<p>test<br>test<br>test</p>\n'); | ||
}); | ||
|
||
test('`stripHtml` option', () => { | ||
expect(markdown('<p>Test</p>')).toBe('<p><p>Test</p></p>\n'); | ||
expect(markdown('<p>Test</p>', { stripHtml: false })).toBe('<p><p>Test</p></p>\n'); | ||
expect(markdown('<p>Test</p>', { stripHtml: true })).toBe('<p><p>Test</p></p>\n'); | ||
}); |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
const marked = require('marked'); | ||
const Emoji = require('./emojis.js').emoji; | ||
const syntaxHighlighter = require('../../../../readme-syntax-highlighter'); | ||
const sanitizer = require('./sanitizer'); | ||
const renderer = require('./renderer'); | ||
|
||
const emojis = new Emoji(); | ||
|
||
module.exports = function markdown(text, opts = {}) { | ||
marked.setOptions({ | ||
sanitize: true, | ||
preserveNumbering: true, | ||
renderer, | ||
emoji(emojiText) { | ||
const emoji = emojiText.replace(/[^-_+a-zA-Z0-9]/g, '').toLowerCase(); | ||
if (emoji.substr(0, 3) === 'fa-') { | ||
return `<i class="fa ${emoji}"></i>`; | ||
} | ||
if (emojis.is(emoji)) { | ||
return `<img src="/img/emojis/${emoji}.png" alt=":${emoji}+:" title=":${emoji}:" class="emoji" align="absmiddle" height="20" width="20">`; | ||
} | ||
return `:${emoji}:`; | ||
}, | ||
highlight(code, language) { | ||
if (!language) return undefined; | ||
return syntaxHighlighter(code, language); | ||
}, | ||
gfm: true, | ||
breaks: !opts.correctnewlines, | ||
sanitizer: opts.stripHtml ? undefined : sanitizer, | ||
}); | ||
return marked(text); | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
const marked = require('marked'); | ||
|
||
const renderer = new marked.Renderer(); | ||
|
||
renderer.image = function image(href, title, text) { | ||
let out = `<img src="${href}" alt="${text}"`; | ||
if (title && title.substr(0, 6) === 'style|') { | ||
out += ` style="${title.substr(6).replace(/"/g, "'")}"`; | ||
} else if (title && title.substr(0, 5) === 'right') { | ||
out += ' style="float: right; margin 0 0 15px 15px;"'; | ||
} else if (title && title.substr(0, 4) === 'left') { | ||
out += ' style="float: left; margin 0 15px 15px 0;"'; | ||
} else if (title) { | ||
out += ` title="${title}"`; | ||
} | ||
|
||
out += this.options.xhtml ? '/>' : '>'; | ||
return out; | ||
}; | ||
|
||
renderer.listitem = function listitem(text, val) { | ||
const valAttr = val ? ` value="${val}"` : ''; | ||
if (/^\s*\[[x ]\]\s*/.test(text)) { | ||
text = text | ||
.replace(/^\s*\[ \]\s*/, '<input type="checkbox" disabled> ') | ||
.replace(/^\s*\[x\]\s*/, '<input type="checkbox" checked disabled> '); | ||
return `<li style="list-style: none" class="checklist">${text}</li>`; | ||
} | ||
|
||
return `<li ${valAttr}>${text}</li>`; | ||
}; | ||
|
||
renderer.table = function table(header, body) { | ||
return ( | ||
`${'<div class="marked-table"><table>\n<thead>\n'}${header}</thead>\n` + | ||
`<tbody>\n${body}</tbody>\n</table></div>\n` | ||
); | ||
}; | ||
|
||
renderer.heading = function heading(text, level, raw) { | ||
const id = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w]+/g, '-'); | ||
|
||
return ( | ||
`<h${level} class="header-scroll">` + | ||
`<div class="anchor waypoint" id="section-${id}"></div>${text}<a class="fa fa-anchor" href="#section-${id}"></a>` + | ||
`</h${level}>\n` | ||
); | ||
}; | ||
|
||
renderer.link = function link(href, title, text) { | ||
/* eslint no-param-reassign: 0 */ | ||
const doc = href.match(/^doc:([-_a-zA-Z0-9#]*)$/); | ||
let isDoc = false; | ||
let uiSref = false; | ||
|
||
if (href.match(/^(data|javascript)[^a-zA-Z0-9/_-]/i)) { | ||
// Avoid XSS | ||
href = ''; | ||
} | ||
|
||
if (doc) { | ||
uiSref = `docs.show({'doc': '${doc[1]}'})`; | ||
href = ''; | ||
isDoc = doc[1]; | ||
} | ||
|
||
const ref = href.match(/^ref:([-_a-zA-Z0-9#]*)$/); | ||
if (ref) { | ||
const cat = ''; | ||
// TODO https://github.com/readmeio/api-explorer/issues/28 | ||
// if (req && req.project.appearance.categoriesAsDropdown) { | ||
// cat = `/${req._referenceCategoryMap[ref[1]]}`; | ||
// } | ||
href = `/reference${cat}#${ref[1]}`; | ||
} | ||
|
||
const blog = href.match(/^blog:([-_a-zA-Z0-9#]*)$/); | ||
if (blog) { | ||
uiSref = `blog.show({'blog': '${blog[1]}'})`; | ||
href = ''; | ||
} | ||
|
||
const custompage = href.match(/^page:([-_a-zA-Z0-9#]*)$/); | ||
if (custompage) { | ||
uiSref = `custompages.show({'custompage': '${custompage[1]}'})`; | ||
} | ||
|
||
if (this.options.sanitize) { | ||
let prot; | ||
try { | ||
prot = decodeURIComponent(unescape(href)) | ||
.replace(/[^\w:]/g, '') | ||
.toLowerCase(); | ||
} catch (e) { | ||
return ''; | ||
} | ||
// eslint-disable-next-line no-script-url | ||
if (prot.indexOf('javascript:') === 0) { | ||
return ''; | ||
} | ||
} | ||
|
||
let out = '<a'; | ||
|
||
out += ` href="${href}"`; | ||
|
||
if (uiSref) { | ||
out += ` ui-sref="${uiSref}"`; | ||
} else { | ||
// This prevents full links from getting | ||
// into a weird AJAX state | ||
out += ' target="_self"'; | ||
} | ||
|
||
if (title) { | ||
out += ` title="${title}"`; | ||
} | ||
if (isDoc) { | ||
out += ` class="doc-link" data-sidebar="${isDoc}"`; | ||
} | ||
out += `>${text}</a>`; | ||
return out; | ||
}; | ||
|
||
module.exports = renderer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
function sanitizer(tag) { | ||
// TODO: This is probably not secure enough; use html-sanatize when we move to the backend in hub2. | ||
const tagName = tag.match(/<\/?([^>\s]+)/); | ||
|
||
const allowedTags = [ | ||
'img', | ||
'h1', | ||
'h2', | ||
'h3', | ||
'h4', | ||
'h5', | ||
'h6', | ||
'span', | ||
'blockquote', | ||
'p', | ||
'a', | ||
'ul', | ||
'ol', | ||
'nl', | ||
'li', | ||
'b', | ||
'i', | ||
'strong', | ||
'em', | ||
'strike', | ||
'code', | ||
'hr', | ||
'br', | ||
'div', | ||
'table', | ||
'thead', | ||
'caption', | ||
'tbody', | ||
'tr', | ||
'th', | ||
'td', | ||
'pre', | ||
'dl', | ||
'dd', | ||
'dt', | ||
'sub', | ||
'sup', | ||
'section', | ||
]; | ||
|
||
const allowedAttrs = [ | ||
'class', | ||
'id', | ||
'style', | ||
'cellpadding', | ||
'cellspacing', | ||
'width', | ||
'align', | ||
'height', | ||
'colspan', | ||
'href', | ||
'name', | ||
'target', | ||
'src', | ||
'title', | ||
'alt', | ||
]; | ||
|
||
if (allowedTags.indexOf(tagName[1]) <= -1) { | ||
return tag.replace('<', '<').replace('>', '>'); | ||
} | ||
|
||
let tagClean = tagName[0]; // add the tag here | ||
tag.replace(/\s+([a-zA-Z0-9]+)=('.*?'|".*?")/g, (full, attr) => { | ||
if (allowedAttrs.indexOf(attr) > -1) { | ||
tagClean += full; | ||
} | ||
return ''; | ||
}); | ||
|
||
return `${tagClean}>`; | ||
}; | ||
|
||
module.exports = sanitizer; |
Oops, something went wrong.