securemark 0.254.2 → 0.256.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.js +53 -45
- package/markdown.d.ts +1 -0
- package/package.json +1 -1
- package/src/combinator/control/constraint/contract.ts +3 -13
- package/src/combinator/control/manipulation/context.ts +1 -7
- package/src/combinator/control/manipulation/resource.ts +1 -1
- package/src/parser/api/parse.test.ts +5 -0
- package/src/parser/api/parse.ts +6 -5
- package/src/parser/block/extension/aside.ts +3 -3
- package/src/parser/block/extension/example.ts +3 -3
- package/src/parser/block.ts +1 -1
- package/src/parser/inline/annotation.ts +4 -4
- package/src/parser/inline/bracket.ts +1 -1
- package/src/parser/inline/extension/index.ts +1 -1
- package/src/parser/inline/extension/placeholder.test.ts +7 -7
- package/src/parser/inline/extension/placeholder.ts +2 -2
- package/src/parser/inline/html.test.ts +23 -21
- package/src/parser/inline/html.ts +25 -17
- package/src/parser/inline/link.ts +4 -3
- package/src/parser/inline/media.ts +1 -1
- package/src/parser/inline/reference.test.ts +4 -4
- package/src/parser/inline/reference.ts +4 -4
- package/src/parser/inline/ruby.ts +1 -1
- package/src/parser/inline.test.ts +2 -2
- package/src/parser/util.ts +14 -1
package/CHANGELOG.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! securemark v0.
|
|
1
|
+
/*! securemark v0.256.0 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
|
|
2
2
|
(function webpackUniversalModuleDefinition(root, factory) {
|
|
3
3
|
if(typeof exports === 'object' && typeof module === 'object')
|
|
4
4
|
module.exports = factory(require("DOMPurify"), require("Prism"));
|
|
@@ -2009,22 +2009,13 @@ const alias_1 = __webpack_require__(5406);
|
|
|
2009
2009
|
|
|
2010
2010
|
const parser_1 = __webpack_require__(6728);
|
|
2011
2011
|
|
|
2012
|
-
function validate(patterns, has,
|
|
2013
|
-
if (typeof has === 'function') return validate(patterns, '',
|
|
2014
|
-
if (
|
|
2015
|
-
if (!(0, alias_1.isArray)(patterns)) return validate([patterns], has, end, parser);
|
|
2012
|
+
function validate(patterns, has, parser) {
|
|
2013
|
+
if (typeof has === 'function') return validate(patterns, '', has);
|
|
2014
|
+
if (!(0, alias_1.isArray)(patterns)) return validate([patterns], has, parser);
|
|
2016
2015
|
const match = (0, global_1.Function)(['"use strict";', 'return source =>', '0', ...patterns.map(pattern => typeof pattern === 'string' ? `|| source.slice(0, ${pattern.length}) === '${pattern}'` : `|| /${pattern.source}/${pattern.flags}.test(source)`)].join(''))();
|
|
2017
|
-
|
|
2018
|
-
const match2 = source => {
|
|
2019
|
-
if (!has) return true;
|
|
2020
|
-
const i = end ? source.indexOf(end, 1) : -1;
|
|
2021
|
-
return i !== -1 ? source.slice(0, i).indexOf(has, 1) !== -1 : source.indexOf(has, 1) !== -1;
|
|
2022
|
-
};
|
|
2023
|
-
|
|
2024
2016
|
return (source, context) => {
|
|
2025
2017
|
if (source === '') return;
|
|
2026
2018
|
if (!match(source)) return;
|
|
2027
|
-
if (!match2(source)) return;
|
|
2028
2019
|
const result = parser(source, context);
|
|
2029
2020
|
if (!result) return;
|
|
2030
2021
|
return (0, parser_1.exec)(result).length < source.length ? result : global_1.undefined;
|
|
@@ -2127,14 +2118,12 @@ function guard(f, parser) {
|
|
|
2127
2118
|
exports.guard = guard;
|
|
2128
2119
|
|
|
2129
2120
|
function reset(base, parser) {
|
|
2130
|
-
if (isEmpty(base)) return parser;
|
|
2131
2121
|
return (source, context) => parser(source, inherit((0, alias_1.ObjectCreate)(context), base));
|
|
2132
2122
|
}
|
|
2133
2123
|
|
|
2134
2124
|
exports.reset = reset;
|
|
2135
2125
|
|
|
2136
2126
|
function context(base, parser) {
|
|
2137
|
-
if (isEmpty(base)) return parser;
|
|
2138
2127
|
const override = (0, memoize_1.memoize)(context => inherit((0, alias_1.ObjectCreate)(context), base), new global_1.WeakMap());
|
|
2139
2128
|
return (source, context) => parser(source, override(context));
|
|
2140
2129
|
}
|
|
@@ -2166,12 +2155,6 @@ const inherit = (0, assign_1.template)((prop, target, source) => {
|
|
|
2166
2155
|
}
|
|
2167
2156
|
});
|
|
2168
2157
|
|
|
2169
|
-
function isEmpty(context) {
|
|
2170
|
-
for (const _ in context) return false;
|
|
2171
|
-
|
|
2172
|
-
return true;
|
|
2173
|
-
}
|
|
2174
|
-
|
|
2175
2158
|
/***/ }),
|
|
2176
2159
|
|
|
2177
2160
|
/***/ 7957:
|
|
@@ -3550,12 +3533,13 @@ function parse(source, opts = {}, context) {
|
|
|
3550
3533
|
const url = (0, header_2.headers)(source).find(field => field.toLowerCase().startsWith('url:'))?.slice(4).trim() ?? '';
|
|
3551
3534
|
source = !context ? (0, normalize_1.normalize)(source) : source;
|
|
3552
3535
|
context = {
|
|
3553
|
-
url: url ? new url_1.ReadonlyURL(url) : context?.url,
|
|
3554
3536
|
host: opts.host ?? context?.host ?? new url_1.ReadonlyURL(global_1.location.pathname, global_1.location.origin),
|
|
3537
|
+
url: url ? new url_1.ReadonlyURL(url) : context?.url,
|
|
3538
|
+
id: opts.id ?? context?.id,
|
|
3555
3539
|
caches: context?.caches,
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3540
|
+
...(context?.resources && {
|
|
3541
|
+
resources: context.resources
|
|
3542
|
+
})
|
|
3559
3543
|
};
|
|
3560
3544
|
if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
|
|
3561
3545
|
const node = (0, dom_1.frag)();
|
|
@@ -3652,7 +3636,7 @@ const random_1 = __webpack_require__(7325);
|
|
|
3652
3636
|
|
|
3653
3637
|
exports.block = (0, combinator_1.creator)(error((0, combinator_1.reset)({
|
|
3654
3638
|
resources: {
|
|
3655
|
-
budget:
|
|
3639
|
+
budget: 50 * 1000,
|
|
3656
3640
|
recursion: 200
|
|
3657
3641
|
}
|
|
3658
3642
|
}, (0, combinator_1.union)([source_1.emptyline, horizontalrule_1.horizontalrule, heading_1.heading, ulist_1.ulist, olist_1.olist, ilist_1.ilist, dlist_1.dlist, table_1.table, codeblock_1.codeblock, mathblock_1.mathblock, extension_1.extension, sidefence_1.sidefence, blockquote_1.blockquote, reply_1.reply, paragraph_1.paragraph]))));
|
|
@@ -3888,7 +3872,7 @@ const parse_1 = __webpack_require__(5013);
|
|
|
3888
3872
|
|
|
3889
3873
|
const dom_1 = __webpack_require__(3252);
|
|
3890
3874
|
|
|
3891
|
-
exports.aside = (0, combinator_1.
|
|
3875
|
+
exports.aside = (0, combinator_1.block)((0, combinator_1.validate)('~~~', (0, combinator_1.fmap)((0, combinator_1.fence)(/^(~{3,})aside(?!\S)([^\n]*)(?:$|\n)/, 300), // Bug: Type mismatch between outer and inner.
|
|
3892
3876
|
([body, overflow, closer, opener, delim, param], _, context) => {
|
|
3893
3877
|
if (!closer || overflow || param.trimStart()) return [(0, dom_1.html)('pre', {
|
|
3894
3878
|
class: 'invalid',
|
|
@@ -3920,7 +3904,7 @@ exports.aside = (0, combinator_1.creator)(100, (0, combinator_1.block)((0, combi
|
|
|
3920
3904
|
id: (0, indexee_1.identity)((0, indexee_1.text)(heading)),
|
|
3921
3905
|
class: 'aside'
|
|
3922
3906
|
}, [document, references])];
|
|
3923
|
-
})))
|
|
3907
|
+
})));
|
|
3924
3908
|
|
|
3925
3909
|
/***/ }),
|
|
3926
3910
|
|
|
@@ -3946,7 +3930,7 @@ const mathblock_1 = __webpack_require__(3754);
|
|
|
3946
3930
|
const dom_1 = __webpack_require__(3252);
|
|
3947
3931
|
|
|
3948
3932
|
const opener = /^(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/;
|
|
3949
|
-
exports.example = (0, combinator_1.
|
|
3933
|
+
exports.example = (0, combinator_1.block)((0, combinator_1.validate)('~~~', (0, combinator_1.fmap)((0, combinator_1.fence)(opener, 300), // Bug: Type mismatch between outer and inner.
|
|
3950
3934
|
([body, overflow, closer, opener, delim, type = 'markdown', param], _, context) => {
|
|
3951
3935
|
if (!closer || overflow || param.trimStart()) return [(0, dom_1.html)('pre', {
|
|
3952
3936
|
class: 'invalid',
|
|
@@ -3993,7 +3977,7 @@ exports.example = (0, combinator_1.creator)(100, (0, combinator_1.block)((0, com
|
|
|
3993
3977
|
'data-invalid-message': 'Invalid example type'
|
|
3994
3978
|
}, `${opener}${body}${closer}`)];
|
|
3995
3979
|
}
|
|
3996
|
-
})))
|
|
3980
|
+
})));
|
|
3997
3981
|
|
|
3998
3982
|
/***/ }),
|
|
3999
3983
|
|
|
@@ -5390,7 +5374,7 @@ const util_1 = __webpack_require__(9437);
|
|
|
5390
5374
|
|
|
5391
5375
|
const dom_1 = __webpack_require__(3252);
|
|
5392
5376
|
|
|
5393
|
-
exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('((',
|
|
5377
|
+
exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('((', (0, combinator_1.fmap)((0, combinator_1.surround)('((', (0, combinator_1.guard)(context => context.syntax?.inline?.annotation ?? true, (0, combinator_1.context)({
|
|
5394
5378
|
syntax: {
|
|
5395
5379
|
inline: {
|
|
5396
5380
|
annotation: false,
|
|
@@ -5405,9 +5389,9 @@ exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0,
|
|
|
5405
5389
|
}
|
|
5406
5390
|
},
|
|
5407
5391
|
delimiters: global_1.undefined
|
|
5408
|
-
}, (0, util_1.
|
|
5392
|
+
}, (0, util_1.trimBlankStart)((0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ')', /^\\?\n/)))), '))'), ns => [(0, dom_1.html)('sup', {
|
|
5409
5393
|
class: 'annotation'
|
|
5410
|
-
}, [(0, dom_1.html)('span', (0, dom_1.defrag)(ns))])]))));
|
|
5394
|
+
}, [(0, dom_1.html)('span', (0, util_1.trimNodeEnd)((0, dom_1.defrag)(ns)))])]))));
|
|
5411
5395
|
|
|
5412
5396
|
/***/ }),
|
|
5413
5397
|
|
|
@@ -5702,7 +5686,7 @@ const dom_1 = __webpack_require__(3252);
|
|
|
5702
5686
|
const array_1 = __webpack_require__(8112);
|
|
5703
5687
|
|
|
5704
5688
|
const index = /^[0-9A-Za-z]+(?:(?:[.-]|, )[0-9A-Za-z]+)*/;
|
|
5705
|
-
exports.bracket = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, source_1.str)(index), (0, source_1.str)(')')), (0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)(inline_1.inline, ')'), (0, source_1.str)(')'), true, ([as, bs = [], cs], rest) => [[(0, dom_1.html)('span', {
|
|
5689
|
+
exports.bracket = (0, combinator_1.lazy)(() => (0, combinator_1.creator)(0, (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, source_1.str)(index), (0, source_1.str)(')')), (0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)(inline_1.inline, ')'), (0, source_1.str)(')'), true, ([as, bs = [], cs], rest) => [[(0, dom_1.html)('span', {
|
|
5706
5690
|
class: 'paren'
|
|
5707
5691
|
}, (0, dom_1.defrag)((0, array_1.push)((0, array_1.unshift)(as, bs), cs)))], rest], ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('('), (0, source_1.str)(new RegExp(index.source.replace(', ', '[,、]').replace(/[09AZaz.]|\-(?!\w)/g, c => c.trimStart() && String.fromCharCode(c.charCodeAt(0) + 0xFEE0)))), (0, source_1.str)(')')), (0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)(inline_1.inline, ')'), (0, source_1.str)(')'), true, ([as, bs = [], cs], rest) => [[(0, dom_1.html)('span', {
|
|
5708
5692
|
class: 'paren'
|
|
@@ -5955,7 +5939,7 @@ const util_1 = __webpack_require__(9437);
|
|
|
5955
5939
|
|
|
5956
5940
|
const dom_1 = __webpack_require__(3252);
|
|
5957
5941
|
|
|
5958
|
-
exports.index = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('[#',
|
|
5942
|
+
exports.index = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('[#', (0, combinator_1.fmap)((0, indexee_1.indexee)((0, combinator_1.fmap)((0, combinator_1.surround)('[#', (0, combinator_1.guard)(context => context.syntax?.inline?.index ?? true, (0, util_1.startTight)((0, combinator_1.context)({
|
|
5959
5943
|
syntax: {
|
|
5960
5944
|
inline: {
|
|
5961
5945
|
annotation: false,
|
|
@@ -6172,7 +6156,7 @@ const array_1 = __webpack_require__(8112); // Don't use the symbols already used
|
|
|
6172
6156
|
// All syntax surrounded by square brackets shouldn't contain line breaks.
|
|
6173
6157
|
|
|
6174
6158
|
|
|
6175
|
-
exports.placeholder = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)(['[:', '[^'],
|
|
6159
|
+
exports.placeholder = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)(['[:', '[^'], (0, combinator_1.surround)((0, source_1.str)(/^\[[:^]/), (0, util_1.startTight)((0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ']', /^\\?\n/)), (0, source_1.str)(']'), false, ([as, bs], rest) => [[(0, dom_1.html)('span', {
|
|
6176
6160
|
class: 'invalid',
|
|
6177
6161
|
'data-invalid-syntax': 'extension',
|
|
6178
6162
|
'data-invalid-type': 'syntax',
|
|
@@ -6219,14 +6203,17 @@ const attrspecs = {
|
|
|
6219
6203
|
global_1.Object.setPrototypeOf(attrspecs, null);
|
|
6220
6204
|
global_1.Object.values(attrspecs).forEach(o => global_1.Object.setPrototypeOf(o, null));
|
|
6221
6205
|
exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('<', (0, combinator_1.validate)(/^<[a-z]+(?=[^\S\n]|>)/, (0, combinator_1.union)([(0, combinator_1.focus)('<wbr>', () => [[(0, dom_1.html)('wbr')], '']), (0, combinator_1.focus)( // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
|
|
6222
|
-
/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/, source => [[source], '']), (0, combinator_1.match)(new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`), (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0,
|
|
6206
|
+
/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/, source => [[source], '']), (0, combinator_1.match)(new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`), (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0, combinator_1.subsequence)([(0, combinator_1.focus)(/^[^\S\n]*\n/, (0, combinator_1.some)(inline_1.inline)), (0, combinator_1.some)((0, combinator_1.open)(/^\n?/, (0, combinator_1.some)(inline_1.inline, (0, util_1.blankWith)('\n', `</${tag}>`)), true))]), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], rest) => [[elem(tag, as, bs, cs)], rest], ([as, bs = []], rest) => [[elem(tag, as, bs, [])], rest]), ([, tag]) => TAGS.indexOf(tag), [])), (0, combinator_1.match)(/^<([a-z]+)(?=[^\S\n]|>)/, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0, combinator_1.subsequence)([(0, combinator_1.focus)(/^[^\S\n]*\n/, (0, combinator_1.some)(inline_1.inline)), (0, combinator_1.some)(inline_1.inline, `</${tag}>`)]), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], rest) => [[elem(tag, as, bs, cs)], rest], ([as, bs = []], rest) => [[elem(tag, as, bs, [])], rest]), ([, tag]) => tag, new cache_1.Cache(10000)))])))));
|
|
6223
6207
|
exports.attribute = (0, combinator_1.union)([(0, source_1.str)(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/)]); // https://developer.mozilla.org/en-US/docs/Web/HTML/Element
|
|
6224
6208
|
// [...document.querySelectorAll('tbody > tr > td:first-child')].map(el => el.textContent.slice(1, -1))
|
|
6225
6209
|
|
|
6226
6210
|
const TAGS = global_1.Object.freeze(["html", "base", "head", "link", "meta", "style", "title", "body", "address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4", "h5", "h6", "main", "nav", "section", "blockquote", "dd", "div", "dl", "dt", "figcaption", "figure", "hr", "li", "menu", "ol", "p", "pre", "ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn", "em", "i", "kbd", "mark", "q", "rp", "rt", "ruby", "s", "samp", "small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "area", "audio", "img", "map", "track", "video", "embed", "iframe", "object", "picture", "portal", "source", "svg", "math", "canvas", "noscript", "script", "del", "ins", "caption", "col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "button", "datalist", "fieldset", "form", "input", "label", "legend", "meter", "optgroup", "option", "output", "progress", "select", "textarea", "details", "dialog", "summary", "slot", "template", "acronym", "applet", "basefont", "bgsound", "big", "blink", "center", "content", "dir", "font", "frame", "frameset", "hgroup", "image", "keygen", "marquee", "menuitem", "nobr", "noembed", "noframes", "param", "plaintext", "rb", "rtc", "shadow", "spacer", "strike", "tt", "xmp"]);
|
|
6227
6211
|
|
|
6228
6212
|
function elem(tag, as, bs, cs) {
|
|
6229
|
-
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag
|
|
6213
|
+
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag name "${tag}"`, as, bs, cs);
|
|
6214
|
+
if (cs.length === 0) return invalid('tag', `Missing the closing HTML tag "</${tag}>"`, as, bs, cs);
|
|
6215
|
+
if (bs.length === 0) return invalid('content', `Missing the content`, as, bs, cs);
|
|
6216
|
+
if (!(0, util_1.isStartLooseNodes)(bs)) return invalid('content', `Missing the visible content in the same line`, as, bs, cs);
|
|
6230
6217
|
const attrs = attributes('html', [], attrspecs[tag], as.slice(1, -1));
|
|
6231
6218
|
return 'data-invalid-syntax' in attrs ? invalid('attribute', 'Invalid HTML attribute', as, bs, cs) : (0, dom_1.html)(tag, attrs, (0, dom_1.defrag)(bs));
|
|
6232
6219
|
}
|
|
@@ -6369,7 +6356,7 @@ const optspec = {
|
|
|
6369
6356
|
rel: ['nofollow']
|
|
6370
6357
|
};
|
|
6371
6358
|
Object.setPrototypeOf(optspec, null);
|
|
6372
|
-
exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.creator)(10, (0, combinator_1.validate)(['[', '{'],
|
|
6359
|
+
exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.creator)(10, (0, combinator_1.validate)(['[', '{'], (0, combinator_1.bind)((0, combinator_1.guard)(context => context.syntax?.inline?.link ?? true, (0, combinator_1.reverse)((0, combinator_1.tails)([(0, combinator_1.context)({
|
|
6373
6360
|
syntax: {
|
|
6374
6361
|
inline: {
|
|
6375
6362
|
link: false
|
|
@@ -6388,7 +6375,8 @@ exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.creator)(10, (0, co
|
|
|
6388
6375
|
autolink: false
|
|
6389
6376
|
}
|
|
6390
6377
|
}
|
|
6391
|
-
}, (0, util_1.
|
|
6378
|
+
}, (0, util_1.trimBlankStart)((0, combinator_1.some)(inline_1.inline, ']', /^\\?\n/))), ']', true)]))), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))]))), ([params, content = []], rest, context) => {
|
|
6379
|
+
content = (0, util_1.trimNodeEnd)(content);
|
|
6392
6380
|
if ((0, parser_1.eval)((0, combinator_1.some)(autolink_1.autolink)((0, util_1.stringify)(content), context))?.some(node => typeof node === 'object')) return;
|
|
6393
6381
|
const INSECURE_URI = params.shift();
|
|
6394
6382
|
const el = elem(INSECURE_URI, (0, dom_1.defrag)(content), new url_1.ReadonlyURL(resolve(INSECURE_URI, context.host ?? global_1.location, context.url ?? context.host ?? global_1.location), context.host?.href || global_1.location.href), context.host?.origin || global_1.location.origin);
|
|
@@ -6576,7 +6564,7 @@ const optspec = {
|
|
|
6576
6564
|
rel: global_1.undefined
|
|
6577
6565
|
};
|
|
6578
6566
|
Object.setPrototypeOf(optspec, null);
|
|
6579
|
-
exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.creator)(10, (0, combinator_1.validate)(['![', '!{'],
|
|
6567
|
+
exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.creator)(10, (0, combinator_1.validate)(['![', '!{'], (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.open)('!', (0, combinator_1.guard)(context => context.syntax?.inline?.media ?? true, (0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)(/^\[(?!\s*\\\s)/, (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']', /^\\?\n/), ']', true)), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([link_1.uri, (0, combinator_1.some)(option)]), /^[^\S\n]*}/))]))), ([as, bs]) => bs ? [[as.join('').trim() || as.join('')], bs] : [[''], as]), ([[text]]) => text === '' || text.trim() !== ''), ([[text], params], rest, context) => {
|
|
6580
6568
|
const INSECURE_URI = params.shift();
|
|
6581
6569
|
const url = new url_1.ReadonlyURL((0, link_1.resolve)(INSECURE_URI, context.host ?? global_1.location, context.url ?? context.host ?? global_1.location), context.host?.href || global_1.location.href);
|
|
6582
6570
|
let cache;
|
|
@@ -6667,7 +6655,7 @@ const util_1 = __webpack_require__(9437);
|
|
|
6667
6655
|
|
|
6668
6656
|
const dom_1 = __webpack_require__(3252);
|
|
6669
6657
|
|
|
6670
|
-
exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('[[',
|
|
6658
|
+
exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('[[', (0, combinator_1.fmap)((0, combinator_1.surround)('[[', (0, combinator_1.guard)(context => context.syntax?.inline?.reference ?? true, (0, combinator_1.context)({
|
|
6671
6659
|
syntax: {
|
|
6672
6660
|
inline: {
|
|
6673
6661
|
annotation: false,
|
|
@@ -6681,7 +6669,7 @@ exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, c
|
|
|
6681
6669
|
}
|
|
6682
6670
|
},
|
|
6683
6671
|
delimiters: global_1.undefined
|
|
6684
|
-
}, (0, combinator_1.subsequence)([abbr, (0, combinator_1.open)((0, source_1.stropt)(/^(?=\^)/), (0, combinator_1.some)(inline_1.inline, ']', /^\\?\n/)), (0, util_1.
|
|
6672
|
+
}, (0, combinator_1.subsequence)([abbr, (0, combinator_1.open)((0, source_1.stropt)(/^(?=\^)/), (0, combinator_1.some)(inline_1.inline, ']', /^\\?\n/)), (0, util_1.trimBlankStart)((0, combinator_1.some)(inline_1.inline, ']', /^\\?\n/))]))), ']]'), ns => [(0, dom_1.html)('sup', attributes(ns), [(0, dom_1.html)('span', (0, util_1.trimNodeEnd)((0, dom_1.defrag)(ns)))])]))));
|
|
6685
6673
|
const abbr = (0, combinator_1.creator)((0, combinator_1.bind)((0, combinator_1.surround)('^', (0, combinator_1.union)([(0, source_1.str)(/^(?![0-9]+\s?[|\]])[0-9A-Za-z]+(?:(?:-|(?=\W)(?!'\d)'?(?!\.\d)\.?(?!,\S),? ?)[0-9A-Za-z]+)*(?:-|'?\.?,? ?)?/)]), /^\|?(?=]])|^\|[^\S\n]*/), ([source], rest) => [[(0, dom_1.html)('abbr', source)], rest.replace(util_1.regBlankStart, '')]));
|
|
6686
6674
|
|
|
6687
6675
|
function attributes(ns) {
|
|
@@ -6727,7 +6715,7 @@ const dom_1 = __webpack_require__(3252);
|
|
|
6727
6715
|
|
|
6728
6716
|
const array_1 = __webpack_require__(8112);
|
|
6729
6717
|
|
|
6730
|
-
exports.ruby = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('[',
|
|
6718
|
+
exports.ruby = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('[', (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.sequence)([(0, combinator_1.surround)('[', (0, combinator_1.focus)(/^(?:\\[^\n]|[^\\\[\]\n])+(?=]\()/, text), ']'), (0, combinator_1.surround)('(', (0, combinator_1.focus)(/^(?:\\[^\n]|[^\\\(\)\n])+(?=\))/, text), ')')]), ([texts]) => (0, util_1.isStartTightNodes)(texts)), ([texts, rubies], rest) => {
|
|
6731
6719
|
const tail = typeof texts[texts.length - 1] === 'object' ? [texts.pop()] : [];
|
|
6732
6720
|
tail.length === 0 && texts[texts.length - 1] === '' && texts.pop();
|
|
6733
6721
|
|
|
@@ -7773,7 +7761,7 @@ exports.unescsource = (0, combinator_1.creator)(source => {
|
|
|
7773
7761
|
Object.defineProperty(exports, "__esModule", ({
|
|
7774
7762
|
value: true
|
|
7775
7763
|
}));
|
|
7776
|
-
exports.stringify = exports.trimBlankEnd = exports.trimBlankStart = exports.trimBlank = exports.isStartTightNodes = exports.startTight = exports.startLoose = exports.visualize = exports.blankWith = exports.regBlankStart = void 0;
|
|
7764
|
+
exports.stringify = exports.trimNodeEnd = exports.trimBlankEnd = exports.trimBlankStart = exports.trimBlank = exports.isStartTightNodes = exports.isStartLooseNodes = exports.startTight = exports.startLoose = exports.visualize = exports.blankWith = exports.regBlankStart = void 0;
|
|
7777
7765
|
|
|
7778
7766
|
const global_1 = __webpack_require__(4128);
|
|
7779
7767
|
|
|
@@ -7877,6 +7865,24 @@ const isStartTight = (0, memoize_1.reduce)((source, context, except) => {
|
|
|
7877
7865
|
}
|
|
7878
7866
|
}, (source, _, except = '') => `${source}\x1E${except}`);
|
|
7879
7867
|
|
|
7868
|
+
function isStartLooseNodes(nodes) {
|
|
7869
|
+
if (nodes.length === 0) return true;
|
|
7870
|
+
|
|
7871
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
7872
|
+
const node = nodes[i];
|
|
7873
|
+
if (isVisible(node)) return true;
|
|
7874
|
+
|
|
7875
|
+
if (typeof node === 'object') {
|
|
7876
|
+
if (node.tagName === 'BR') break;
|
|
7877
|
+
if (node.className === 'linebreak') break;
|
|
7878
|
+
}
|
|
7879
|
+
}
|
|
7880
|
+
|
|
7881
|
+
return false;
|
|
7882
|
+
}
|
|
7883
|
+
|
|
7884
|
+
exports.isStartLooseNodes = isStartLooseNodes;
|
|
7885
|
+
|
|
7880
7886
|
function isStartTightNodes(nodes) {
|
|
7881
7887
|
if (nodes.length === 0) return true;
|
|
7882
7888
|
return isVisible(nodes[0], 0);
|
|
@@ -7972,6 +7978,8 @@ function trimNodeEnd(nodes) {
|
|
|
7972
7978
|
return (0, array_1.push)(nodes, skip);
|
|
7973
7979
|
}
|
|
7974
7980
|
|
|
7981
|
+
exports.trimNodeEnd = trimNodeEnd;
|
|
7982
|
+
|
|
7975
7983
|
function stringify(nodes) {
|
|
7976
7984
|
let acc = '';
|
|
7977
7985
|
|
package/markdown.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -9,11 +9,9 @@ import { Parser, Ctx, Tree, Context, eval, exec, check } from '../../data/parser
|
|
|
9
9
|
|
|
10
10
|
export function validate<P extends Parser<unknown>>(patterns: string | RegExp | (string | RegExp)[], parser: P): P;
|
|
11
11
|
export function validate<P extends Parser<unknown>>(patterns: string | RegExp | (string | RegExp)[], has: string, parser: P): P;
|
|
12
|
-
export function validate<
|
|
13
|
-
|
|
14
|
-
if (
|
|
15
|
-
if (typeof end === 'function') return validate(patterns, has, '', end);
|
|
16
|
-
if (!isArray(patterns)) return validate([patterns], has, end!, parser!);
|
|
12
|
+
export function validate<T>(patterns: string | RegExp | (string | RegExp)[], has: string | Parser<T>, parser?: Parser<T>): Parser<T> {
|
|
13
|
+
if (typeof has === 'function') return validate(patterns, '', has);
|
|
14
|
+
if (!isArray(patterns)) return validate([patterns], has, parser!);
|
|
17
15
|
assert(patterns.length > 0);
|
|
18
16
|
assert(patterns.every(pattern => pattern instanceof RegExp ? !pattern.flags.match(/[gmy]/) && pattern.source.startsWith('^') : true));
|
|
19
17
|
assert(parser);
|
|
@@ -26,17 +24,9 @@ export function validate<T>(patterns: string | RegExp | (string | RegExp)[], has
|
|
|
26
24
|
? `|| source.slice(0, ${pattern.length}) === '${pattern}'`
|
|
27
25
|
: `|| /${pattern.source}/${pattern.flags}.test(source)`),
|
|
28
26
|
].join(''))();
|
|
29
|
-
const match2 = (source: string): boolean => {
|
|
30
|
-
if (!has) return true;
|
|
31
|
-
const i = end ? source.indexOf(end, 1) : -1;
|
|
32
|
-
return i !== -1
|
|
33
|
-
? source.slice(0, i).indexOf(has, 1) !== -1
|
|
34
|
-
: source.indexOf(has, 1) !== -1;
|
|
35
|
-
};
|
|
36
27
|
return (source, context) => {
|
|
37
28
|
if (source === '') return;
|
|
38
29
|
if (!match(source)) return;
|
|
39
|
-
if (!match2(source)) return;
|
|
40
30
|
const result = parser!(source, context);
|
|
41
31
|
assert(check(source, result));
|
|
42
32
|
if (!result) return;
|
|
@@ -17,7 +17,6 @@ export function reset<P extends Parser<unknown>>(context: Context<P>, parser: P)
|
|
|
17
17
|
export function reset<T>(base: Ctx, parser: Parser<T>): Parser<T> {
|
|
18
18
|
assert(Object.getPrototypeOf(base) === Object.prototype);
|
|
19
19
|
assert(Object.freeze(base));
|
|
20
|
-
if (isEmpty(base)) return parser;
|
|
21
20
|
return (source, context) =>
|
|
22
21
|
parser(source, inherit(ObjectCreate(context), base));
|
|
23
22
|
}
|
|
@@ -26,7 +25,6 @@ export function context<P extends Parser<unknown>>(context: Context<P>, parser:
|
|
|
26
25
|
export function context<T>(base: Ctx, parser: Parser<T>): Parser<T> {
|
|
27
26
|
assert(Object.getPrototypeOf(base) === Object.prototype);
|
|
28
27
|
assert(Object.freeze(base));
|
|
29
|
-
if (isEmpty(base)) return parser;
|
|
30
28
|
const override = memoize<Ctx, Ctx>(context => inherit(ObjectCreate(context), base), new WeakMap());
|
|
31
29
|
return (source, context) =>
|
|
32
30
|
parser(source, override(context));
|
|
@@ -40,6 +38,7 @@ const inherit = template((prop, target, source) => {
|
|
|
40
38
|
switch (prop) {
|
|
41
39
|
case 'resources':
|
|
42
40
|
assert(typeof source[prop] === 'object');
|
|
41
|
+
assert(target[prop] || !(prop in target));
|
|
43
42
|
if (prop in target && !hasOwnProperty(target, prop)) return;
|
|
44
43
|
return target[prop] = ObjectCreate(source[prop]);
|
|
45
44
|
}
|
|
@@ -58,8 +57,3 @@ const inherit = template((prop, target, source) => {
|
|
|
58
57
|
return target[prop] = source[prop];
|
|
59
58
|
}
|
|
60
59
|
});
|
|
61
|
-
|
|
62
|
-
function isEmpty(context: Ctx): boolean {
|
|
63
|
-
for (const _ in context) return false;
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
@@ -4,7 +4,7 @@ export function creator<P extends Parser<unknown>>(parser: P): P;
|
|
|
4
4
|
export function creator<P extends Parser<unknown>>(cost: number, parser: P): P;
|
|
5
5
|
export function creator(cost: number | Parser<unknown>, parser?: Parser<unknown>): Parser<unknown> {
|
|
6
6
|
if (typeof cost === 'function') return creator(1, cost);
|
|
7
|
-
assert(cost
|
|
7
|
+
assert(cost >= 0);
|
|
8
8
|
return (source, context) => {
|
|
9
9
|
const { resources = { budget: 1, recursion: 1 } } = context;
|
|
10
10
|
if (resources.budget <= 0) throw new Error('Too many creations.');
|
|
@@ -229,10 +229,15 @@ describe('Unit: parser/api/parse', () => {
|
|
|
229
229
|
[`<p>${'"[% '.repeat(100).trim()}</p>`]);
|
|
230
230
|
});
|
|
231
231
|
|
|
232
|
+
if (!navigator.userAgent.includes('Chrome')) return;
|
|
233
|
+
|
|
232
234
|
it('recursion', () => {
|
|
233
235
|
assert.deepStrictEqual(
|
|
234
236
|
[...parse('('.repeat(199)).children].map(el => el.outerHTML),
|
|
235
237
|
[`<p>${'('.repeat(199)}</p>`]);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('recursion error', () => {
|
|
236
241
|
assert.deepStrictEqual(
|
|
237
242
|
[...parse('('.repeat(200) + '\n\na').children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
238
243
|
[
|
package/src/parser/api/parse.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { location } from 'spica/global';
|
|
2
2
|
import { ParserOptions } from '../../..';
|
|
3
3
|
import { MarkdownParser } from '../../../markdown';
|
|
4
4
|
import { eval } from '../../combinator/data/parser';
|
|
@@ -22,12 +22,13 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
|
|
|
22
22
|
source = !context ? normalize(source) : source;
|
|
23
23
|
assert(!context?.delimiters);
|
|
24
24
|
context = {
|
|
25
|
-
url: url ? new ReadonlyURL(url as ':') : context?.url,
|
|
26
25
|
host: opts.host ?? context?.host ?? new ReadonlyURL(location.pathname, location.origin),
|
|
26
|
+
url: url ? new ReadonlyURL(url as ':') : context?.url,
|
|
27
|
+
id: opts.id ?? context?.id,
|
|
27
28
|
caches: context?.caches,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
...context?.resources && {
|
|
30
|
+
resources: context.resources,
|
|
31
|
+
},
|
|
31
32
|
};
|
|
32
33
|
if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
|
|
33
34
|
const node = frag();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
|
-
import { block, validate, fence,
|
|
2
|
+
import { block, validate, fence, fmap } from '../../../combinator';
|
|
3
3
|
import { identity, text } from '../../inline/extension/indexee';
|
|
4
4
|
import { parse } from '../../api/parse';
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
6
|
|
|
7
|
-
export const aside: ExtensionParser.AsideParser =
|
|
7
|
+
export const aside: ExtensionParser.AsideParser = block(validate('~~~', fmap(
|
|
8
8
|
fence(/^(~{3,})aside(?!\S)([^\n]*)(?:$|\n)/, 300),
|
|
9
9
|
// Bug: Type mismatch between outer and inner.
|
|
10
10
|
([body, overflow, closer, opener, delim, param]: string[], _, context) => {
|
|
@@ -43,4 +43,4 @@ export const aside: ExtensionParser.AsideParser = creator(100, block(validate('~
|
|
|
43
43
|
references,
|
|
44
44
|
]),
|
|
45
45
|
];
|
|
46
|
-
})))
|
|
46
|
+
})));
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
2
|
import { eval } from '../../../combinator/data/parser';
|
|
3
|
-
import { block, validate, fence,
|
|
3
|
+
import { block, validate, fence, fmap } from '../../../combinator';
|
|
4
4
|
import { parse } from '../../api/parse';
|
|
5
5
|
import { mathblock } from '../mathblock';
|
|
6
6
|
import { html } from 'typed-dom/dom';
|
|
7
7
|
|
|
8
8
|
const opener = /^(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/;
|
|
9
9
|
|
|
10
|
-
export const example: ExtensionParser.ExampleParser =
|
|
10
|
+
export const example: ExtensionParser.ExampleParser = block(validate('~~~', fmap(
|
|
11
11
|
fence(opener, 300),
|
|
12
12
|
// Bug: Type mismatch between outer and inner.
|
|
13
13
|
([body, overflow, closer, opener, delim, type = 'markdown', param]: string[], _, context) => {
|
|
@@ -58,4 +58,4 @@ export const example: ExtensionParser.ExampleParser = creator(100, block(validat
|
|
|
58
58
|
}, `${opener}${body}${closer}`),
|
|
59
59
|
];
|
|
60
60
|
}
|
|
61
|
-
})))
|
|
61
|
+
})));
|
package/src/parser/block.ts
CHANGED
|
@@ -36,7 +36,7 @@ export import ReplyParser = BlockParser.ReplyParser;
|
|
|
36
36
|
export import ParagraphParser = BlockParser.ParagraphParser;
|
|
37
37
|
|
|
38
38
|
export const block: BlockParser = creator(error(
|
|
39
|
-
reset({ resources: { budget:
|
|
39
|
+
reset({ resources: { budget: 50 * 1000, recursion: 200 } },
|
|
40
40
|
union([
|
|
41
41
|
emptyline,
|
|
42
42
|
horizontalrule,
|
|
@@ -2,10 +2,10 @@ import { undefined } from 'spica/global';
|
|
|
2
2
|
import { AnnotationParser } from '../inline';
|
|
3
3
|
import { union, some, validate, guard, context, creator, surround, lazy, fmap } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
|
-
import {
|
|
5
|
+
import { trimBlankStart, trimNodeEnd } from '../util';
|
|
6
6
|
import { html, defrag } from 'typed-dom/dom';
|
|
7
7
|
|
|
8
|
-
export const annotation: AnnotationParser = lazy(() => creator(validate('((',
|
|
8
|
+
export const annotation: AnnotationParser = lazy(() => creator(validate('((', fmap(surround(
|
|
9
9
|
'((',
|
|
10
10
|
guard(context => context.syntax?.inline?.annotation ?? true,
|
|
11
11
|
context({ syntax: { inline: {
|
|
@@ -19,6 +19,6 @@ export const annotation: AnnotationParser = lazy(() => creator(validate('((', ')
|
|
|
19
19
|
//link: true,
|
|
20
20
|
//autolink: true,
|
|
21
21
|
}}, delimiters: undefined },
|
|
22
|
-
|
|
22
|
+
trimBlankStart(some(union([inline]), ')', /^\\?\n/)))),
|
|
23
23
|
'))'),
|
|
24
|
-
ns => [html('sup', { class: 'annotation' }, [html('span', defrag(ns))])]))));
|
|
24
|
+
ns => [html('sup', { class: 'annotation' }, [html('span', trimNodeEnd(defrag(ns)))])]))));
|
|
@@ -8,7 +8,7 @@ import { unshift, push } from 'spica/array';
|
|
|
8
8
|
|
|
9
9
|
const index = /^[0-9A-Za-z]+(?:(?:[.-]|, )[0-9A-Za-z]+)*/;
|
|
10
10
|
|
|
11
|
-
export const bracket: BracketParser = lazy(() => creator(union([
|
|
11
|
+
export const bracket: BracketParser = lazy(() => creator(0, union([
|
|
12
12
|
surround(str('('), str(index), str(')')),
|
|
13
13
|
surround(str('('), some(inline, ')'), str(')'), true,
|
|
14
14
|
([as, bs = [], cs], rest) => [[html('span', { class: 'paren' }, defrag(push(unshift(as, bs), cs)))], rest],
|
|
@@ -9,7 +9,7 @@ import { html, define, defrag } from 'typed-dom/dom';
|
|
|
9
9
|
|
|
10
10
|
import IndexParser = ExtensionParser.IndexParser;
|
|
11
11
|
|
|
12
|
-
export const index: IndexParser = lazy(() => creator(validate('[#',
|
|
12
|
+
export const index: IndexParser = lazy(() => creator(validate('[#', fmap(indexee(fmap(surround(
|
|
13
13
|
'[#',
|
|
14
14
|
guard(context => context.syntax?.inline?.index ?? true,
|
|
15
15
|
startTight(
|
|
@@ -21,13 +21,13 @@ describe('Unit: parser/inline/extension/placeholder', () => {
|
|
|
21
21
|
assert.deepStrictEqual(inspect(parser('[^\na]')), undefined);
|
|
22
22
|
assert.deepStrictEqual(inspect(parser('[^\\\na]')), undefined);
|
|
23
23
|
assert.deepStrictEqual(inspect(parser('[^ !http://host]')), undefined);
|
|
24
|
-
assert.deepStrictEqual(inspect(parser('[^a')),
|
|
25
|
-
assert.deepStrictEqual(inspect(parser('[^a\n]')),
|
|
26
|
-
assert.deepStrictEqual(inspect(parser('[^a\n\n]')),
|
|
27
|
-
assert.deepStrictEqual(inspect(parser('[^a\\\n]')),
|
|
28
|
-
assert.deepStrictEqual(inspect(parser('[^a\\\n\\\n]')),
|
|
29
|
-
assert.deepStrictEqual(inspect(parser('[^a\nb]')),
|
|
30
|
-
assert.deepStrictEqual(inspect(parser('[^a\\\nb]')),
|
|
24
|
+
assert.deepStrictEqual(inspect(parser('[^a')), [['[^', 'a'], '']);
|
|
25
|
+
assert.deepStrictEqual(inspect(parser('[^a\n]')), [['[^', 'a'], '\n]']);
|
|
26
|
+
assert.deepStrictEqual(inspect(parser('[^a\n\n]')), [['[^', 'a'], '\n\n]']);
|
|
27
|
+
assert.deepStrictEqual(inspect(parser('[^a\\\n]')), [['[^', 'a'], '\\\n]']);
|
|
28
|
+
assert.deepStrictEqual(inspect(parser('[^a\\\n\\\n]')), [['[^', 'a'], '\\\n\\\n]']);
|
|
29
|
+
assert.deepStrictEqual(inspect(parser('[^a\nb]')), [['[^', 'a'], '\nb]']);
|
|
30
|
+
assert.deepStrictEqual(inspect(parser('[^a\\\nb]')), [['[^', 'a'], '\\\nb]']);
|
|
31
31
|
assert.deepStrictEqual(inspect(parser('[[]')), undefined);
|
|
32
32
|
assert.deepStrictEqual(inspect(parser('[]]')), undefined);
|
|
33
33
|
assert.deepStrictEqual(inspect(parser('[[]]')), undefined);
|
|
@@ -10,9 +10,9 @@ import { unshift } from 'spica/array';
|
|
|
10
10
|
|
|
11
11
|
// All syntax surrounded by square brackets shouldn't contain line breaks.
|
|
12
12
|
|
|
13
|
-
export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => creator(validate(['[:', '[^'],
|
|
13
|
+
export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => creator(validate(['[:', '[^'], surround(
|
|
14
14
|
str(/^\[[:^]/),
|
|
15
|
-
startTight(some(union([inline]), ']')),
|
|
15
|
+
startTight(some(union([inline]), ']', /^\\?\n/)),
|
|
16
16
|
str(']'), false,
|
|
17
17
|
([as, bs], rest) => [[
|
|
18
18
|
html('span', {
|
|
@@ -7,12 +7,12 @@ describe('Unit: parser/inline/html', () => {
|
|
|
7
7
|
const parser = (source: string) => some(html)(source, {});
|
|
8
8
|
|
|
9
9
|
it('xss', () => {
|
|
10
|
-
assert.deepStrictEqual(inspect(parser('<script>')),
|
|
11
|
-
assert.deepStrictEqual(inspect(parser('<script>alert()<script>')),
|
|
10
|
+
assert.deepStrictEqual(inspect(parser('<script>')), [['<span class="invalid"><script></span>'], '']);
|
|
11
|
+
assert.deepStrictEqual(inspect(parser('<script>alert()<script>')), [['<span class="invalid"><script>alert<span class="paren">()</span><span class="invalid"><script></span></span>'], '']);
|
|
12
12
|
assert.deepStrictEqual(inspect(parser('<script>alert()</script>')), [['<span class="invalid"><script>alert<span class="paren">()</span></script></span>'], '']);
|
|
13
|
-
assert.deepStrictEqual(inspect(parser('<script src="\\""></script>')),
|
|
14
|
-
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()">')),
|
|
15
|
-
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()"></bdi>')),
|
|
13
|
+
assert.deepStrictEqual(inspect(parser('<script src="\\""></script>')), [['<span class="invalid"><script src="\\""></script></span>'], '']);
|
|
14
|
+
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()">')), [['<span class="invalid"><bdi onclick="alert()"></span>'], '']);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()"></bdi>')), [['<span class="invalid"><bdi onclick="alert()"></bdi></span>'], '']);
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()">a</bdi>')), [['<span class="invalid"><bdi onclick="alert()">a</bdi></span>'], '']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('<bdi><bdi onclick="alert()">a</bdi></bdi>')), [['<bdi><span class="invalid"><bdi onclick="alert()">a</bdi></span></bdi>'], '']);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl\\"><">a</bdo>')), [['<span class="invalid"><bdo dir="rtl\\"><">a</bdo></span>'], '']);
|
|
@@ -25,17 +25,19 @@ describe('Unit: parser/inline/html', () => {
|
|
|
25
25
|
assert.deepStrictEqual(inspect(parser('<a,b>')), undefined);
|
|
26
26
|
assert.deepStrictEqual(inspect(parser('<a, b>')), undefined);
|
|
27
27
|
assert.deepStrictEqual(inspect(parser('<T>')), undefined);
|
|
28
|
-
assert.deepStrictEqual(inspect(parser('<bdi>z')),
|
|
29
|
-
assert.deepStrictEqual(inspect(parser('<bdi></bdi>')),
|
|
30
|
-
assert.deepStrictEqual(inspect(parser('<bdi> </bdi>')),
|
|
31
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
32
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
33
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
34
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
35
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
36
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
37
|
-
assert.deepStrictEqual(inspect(parser('<bdi>
|
|
38
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
28
|
+
assert.deepStrictEqual(inspect(parser('<bdi>z')), [['<span class="invalid"><bdi>z</span>'], '']);
|
|
29
|
+
assert.deepStrictEqual(inspect(parser('<bdi></bdi>')), [['<span class="invalid"><bdi></bdi></span>'], '']);
|
|
30
|
+
assert.deepStrictEqual(inspect(parser('<bdi> </bdi>')), [['<span class="invalid"><bdi> </bdi></span>'], '']);
|
|
31
|
+
assert.deepStrictEqual(inspect(parser('<bdi> \n</bdi>')), [['<span class="invalid"><bdi><br></bdi></span>'], '']);
|
|
32
|
+
assert.deepStrictEqual(inspect(parser('<bdi> \na</bdi>')), [['<span class="invalid"><bdi><br>a</bdi></span>'], '']);
|
|
33
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\\ </bdi>')), [['<span class="invalid"><bdi> </bdi></span>'], '']);
|
|
34
|
+
assert.deepStrictEqual(inspect(parser('<bdi>	</bdi>')), [['<span class="invalid"><bdi>\t</bdi></span>'], '']);
|
|
35
|
+
assert.deepStrictEqual(inspect(parser('<bdi><wbr></bdi>')), [['<span class="invalid"><bdi><wbr></bdi></span>'], '']);
|
|
36
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\n</bdi>')), [['<span class="invalid"><bdi><br></bdi></span>'], '']);
|
|
37
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\na</bdi>')), [['<span class="invalid"><bdi><br>a</bdi></span>'], '']);
|
|
38
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\\\na</bdi>')), [['<span class="invalid"><bdi><span class="linebreak"> </span>a</bdi></span>'], '']);
|
|
39
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a')), [['<span class="invalid"><bdi>a</span>'], '']);
|
|
40
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a</BDO>')), [['<span class="invalid"><bdi>a</BDO></span>'], '']);
|
|
39
41
|
assert.deepStrictEqual(inspect(parser('<BDI>a</BDI>')), undefined);
|
|
40
42
|
assert.deepStrictEqual(inspect(parser('<BDI>a</bdo>')), undefined);
|
|
41
43
|
assert.deepStrictEqual(inspect(parser('</bdi>')), undefined);
|
|
@@ -43,11 +45,11 @@ describe('Unit: parser/inline/html', () => {
|
|
|
43
45
|
assert.deepStrictEqual(inspect(parser('<b><b><b>a</b></b></b>')), [['<span class="invalid"><b><span class="invalid"><b><span class="invalid"><b>a</b></span></b></span></b></span>'], '']);
|
|
44
46
|
assert.deepStrictEqual(inspect(parser('<bdi><bdi><bdi>a</bdi></bdi></bdi>')), [['<bdi><bdi><bdi>a</bdi></bdi></bdi>'], '']);
|
|
45
47
|
assert.deepStrictEqual(inspect(parser('<x a="*b*"')), undefined);
|
|
46
|
-
assert.deepStrictEqual(inspect(parser('<x a="*b*">')),
|
|
47
|
-
assert.deepStrictEqual(inspect(parser('<x a="*b*">c')),
|
|
48
|
+
assert.deepStrictEqual(inspect(parser('<x a="*b*">')), [['<span class="invalid"><x a="*b*"></span>'], '']);
|
|
49
|
+
assert.deepStrictEqual(inspect(parser('<x a="*b*">c')), [['<span class="invalid"><x a="*b*">c</span>'], '']);
|
|
48
50
|
assert.deepStrictEqual(inspect(parser('<bdi a="*b*"')), undefined);
|
|
49
|
-
assert.deepStrictEqual(inspect(parser('<bdi a="*b*">')),
|
|
50
|
-
assert.deepStrictEqual(inspect(parser('<bdi a="*b*">c')),
|
|
51
|
+
assert.deepStrictEqual(inspect(parser('<bdi a="*b*">')), [['<span class="invalid"><bdi a="*b*"></span>'], '']);
|
|
52
|
+
assert.deepStrictEqual(inspect(parser('<bdi a="*b*">c')), [['<span class="invalid"><bdi a="*b*">c</span>'], '']);
|
|
51
53
|
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *c*')), undefined);
|
|
52
54
|
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *c*>')), undefined);
|
|
53
55
|
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *c*>d</bdi>')), undefined);
|
|
@@ -80,7 +82,7 @@ describe('Unit: parser/inline/html', () => {
|
|
|
80
82
|
});
|
|
81
83
|
|
|
82
84
|
it('escape', () => {
|
|
83
|
-
assert.deepStrictEqual(inspect(parser('<a>')),
|
|
85
|
+
assert.deepStrictEqual(inspect(parser('<a>')), [['<span class="invalid"><a></span>'], '']);
|
|
84
86
|
assert.deepStrictEqual(inspect(parser('<bdi><a>a</a></bdi>')), [['<bdi><span class="invalid"><a>a</a></span></bdi>'], '']);
|
|
85
87
|
assert.deepStrictEqual(inspect(parser('<bdi>a<a>b</a>c</bdi>')), [['<bdi>a<span class="invalid"><a>b</a></span>c</bdi>'], '']);
|
|
86
88
|
assert.deepStrictEqual(inspect(parser('<img>')), [['<img'], '>']);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { undefined, Object } from 'spica/global';
|
|
2
2
|
import { HTMLParser } from '../inline';
|
|
3
|
-
import { union, some, validate, focus, creator, surround, open, match, lazy } from '../../combinator';
|
|
3
|
+
import { union, subsequence, some, validate, focus, creator, surround, open, match, lazy } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
5
|
import { str } from '../source';
|
|
6
|
-
import {
|
|
6
|
+
import { isStartLooseNodes, blankWith } from '../util';
|
|
7
7
|
import { html as h, defrag } from 'typed-dom/dom';
|
|
8
8
|
import { memoize } from 'spica/memoize';
|
|
9
9
|
import { Cache } from 'spica/cache';
|
|
@@ -32,25 +32,31 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
|
|
|
32
32
|
([, tag]) =>
|
|
33
33
|
surround<HTMLParser.TagParser, string>(surround(
|
|
34
34
|
str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
(
|
|
40
|
-
|
|
35
|
+
subsequence([
|
|
36
|
+
focus(/^[^\S\n]*\n/, some(inline)),
|
|
37
|
+
some(open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`)), true)),
|
|
38
|
+
]),
|
|
39
|
+
str(`</${tag}>`), true,
|
|
40
|
+
([as, bs = [], cs], rest) =>
|
|
41
|
+
[[elem(tag, as, bs, cs)], rest],
|
|
42
|
+
([as, bs = []], rest) =>
|
|
43
|
+
[[elem(tag, as, bs, [])], rest]),
|
|
41
44
|
([, tag]) => TAGS.indexOf(tag), [])),
|
|
42
45
|
match(
|
|
43
|
-
|
|
46
|
+
/^<([a-z]+)(?=[^\S\n]|>)/,
|
|
44
47
|
memoize(
|
|
45
48
|
([, tag]) =>
|
|
46
49
|
surround<HTMLParser.TagParser, string>(surround(
|
|
47
50
|
str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
(
|
|
53
|
-
|
|
51
|
+
subsequence([
|
|
52
|
+
focus(/^[^\S\n]*\n/, some(inline)),
|
|
53
|
+
some(inline, `</${tag}>`),
|
|
54
|
+
]),
|
|
55
|
+
str(`</${tag}>`), true,
|
|
56
|
+
([as, bs = [], cs], rest) =>
|
|
57
|
+
[[elem(tag, as, bs, cs)], rest],
|
|
58
|
+
([as, bs = []], rest) =>
|
|
59
|
+
[[elem(tag, as, bs, [])], rest]),
|
|
54
60
|
([, tag]) => tag,
|
|
55
61
|
new Cache(10000))),
|
|
56
62
|
])))));
|
|
@@ -204,8 +210,10 @@ const TAGS = Object.freeze([
|
|
|
204
210
|
function elem(tag: string, as: string[], bs: (HTMLElement | string)[], cs: string[]): HTMLElement {
|
|
205
211
|
assert(as.length > 0);
|
|
206
212
|
assert(as[0][0] === '<' && as[as.length - 1].slice(-1) === '>');
|
|
207
|
-
|
|
208
|
-
if (
|
|
213
|
+
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag name "${tag}"`, as, bs, cs);
|
|
214
|
+
if (cs.length === 0) return invalid('tag', `Missing the closing HTML tag "</${tag}>"`, as, bs, cs);
|
|
215
|
+
if (bs.length === 0) return invalid('content', `Missing the content`, as, bs, cs);
|
|
216
|
+
if (!isStartLooseNodes(bs)) return invalid('content', `Missing the visible content in the same line`, as, bs, cs);
|
|
209
217
|
const attrs = attributes('html', [], attrspecs[tag], as.slice(1, -1));
|
|
210
218
|
return 'data-invalid-syntax' in attrs
|
|
211
219
|
? invalid('attribute', 'Invalid HTML attribute', as, bs, cs)
|
|
@@ -6,7 +6,7 @@ import { inline, media, shortmedia } from '../inline';
|
|
|
6
6
|
import { attributes } from './html';
|
|
7
7
|
import { autolink } from '../autolink';
|
|
8
8
|
import { str } from '../source';
|
|
9
|
-
import {
|
|
9
|
+
import { trimBlankStart, trimNodeEnd, stringify } from '../util';
|
|
10
10
|
import { html, define, defrag } from 'typed-dom/dom';
|
|
11
11
|
import { ReadonlyURL } from 'spica/url';
|
|
12
12
|
|
|
@@ -15,7 +15,7 @@ const optspec = {
|
|
|
15
15
|
} as const;
|
|
16
16
|
Object.setPrototypeOf(optspec, null);
|
|
17
17
|
|
|
18
|
-
export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'],
|
|
18
|
+
export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], bind(
|
|
19
19
|
guard(context => context.syntax?.inline?.link ?? true,
|
|
20
20
|
reverse(tails([
|
|
21
21
|
context({ syntax: { inline: {
|
|
@@ -36,7 +36,7 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
|
|
|
36
36
|
media: false,
|
|
37
37
|
autolink: false,
|
|
38
38
|
}}},
|
|
39
|
-
|
|
39
|
+
trimBlankStart(some(inline, ']', /^\\?\n/))),
|
|
40
40
|
']',
|
|
41
41
|
true),
|
|
42
42
|
]))),
|
|
@@ -44,6 +44,7 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
|
|
|
44
44
|
]))),
|
|
45
45
|
([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) => {
|
|
46
46
|
assert(params.every(p => typeof p === 'string'));
|
|
47
|
+
content = trimNodeEnd(content);
|
|
47
48
|
if (eval(some(autolink)(stringify(content), context))?.some(node => typeof node === 'object')) return;
|
|
48
49
|
assert(!html('div', content).querySelector('a, .media, .annotation, .reference') || (content[0] as HTMLElement).matches('.media'));
|
|
49
50
|
const INSECURE_URI = params.shift()!;
|
|
@@ -17,7 +17,7 @@ const optspec = {
|
|
|
17
17
|
} as const;
|
|
18
18
|
Object.setPrototypeOf(optspec, null);
|
|
19
19
|
|
|
20
|
-
export const media: MediaParser = lazy(() => creator(10, validate(['![', '!{'],
|
|
20
|
+
export const media: MediaParser = lazy(() => creator(10, validate(['![', '!{'], bind(verify(fmap(open(
|
|
21
21
|
'!',
|
|
22
22
|
guard(context => context.syntax?.inline?.media ?? true,
|
|
23
23
|
tails([
|
|
@@ -57,7 +57,7 @@ describe('Unit: parser/inline/reference', () => {
|
|
|
57
57
|
assert.deepStrictEqual(inspect(parser('[[^a,]]')), [['<sup class="reference" data-abbr="a,"><span></span></sup>'], '']);
|
|
58
58
|
assert.deepStrictEqual(inspect(parser('[[^a, ]]')), [['<sup class="reference" data-abbr="a,"><span></span></sup>'], '']);
|
|
59
59
|
assert.deepStrictEqual(inspect(parser('[[^a ]]')), [['<sup class="reference" data-abbr="a"><span></span></sup>'], '']);
|
|
60
|
-
assert.deepStrictEqual(inspect(parser('[[^a ]]')), [['<sup class="invalid"><span>^a
|
|
60
|
+
assert.deepStrictEqual(inspect(parser('[[^a ]]')), [['<sup class="invalid"><span>^a</span></sup>'], '']);
|
|
61
61
|
assert.deepStrictEqual(inspect(parser('[[^a b]]')), [['<sup class="reference" data-abbr="a b"><span></span></sup>'], '']);
|
|
62
62
|
assert.deepStrictEqual(inspect(parser('[[^a b]]')), [['<sup class="invalid"><span>^a b</span></sup>'], '']);
|
|
63
63
|
assert.deepStrictEqual(inspect(parser('[[^a|]]')), [['<sup class="reference" data-abbr="a"><span></span></sup>'], '']);
|
|
@@ -75,7 +75,7 @@ describe('Unit: parser/inline/reference', () => {
|
|
|
75
75
|
assert.deepStrictEqual(inspect(parser('[[^a| ]]')), [['<sup class="reference" data-abbr="a"><span></span></sup>'], '']);
|
|
76
76
|
assert.deepStrictEqual(inspect(parser('[[^1]]')), [['<sup class="invalid"><span>^1</span></sup>'], '']);
|
|
77
77
|
assert.deepStrictEqual(inspect(parser('[[^1a]]')), [['<sup class="reference" data-abbr="1a"><span></span></sup>'], '']);
|
|
78
|
-
assert.deepStrictEqual(inspect(parser('[[^1 ]]')), [['<sup class="invalid"><span>^1
|
|
78
|
+
assert.deepStrictEqual(inspect(parser('[[^1 ]]')), [['<sup class="invalid"><span>^1</span></sup>'], '']);
|
|
79
79
|
assert.deepStrictEqual(inspect(parser('[[^1 a]]')), [['<sup class="reference" data-abbr="1 a"><span></span></sup>'], '']);
|
|
80
80
|
assert.deepStrictEqual(inspect(parser('[[^1|]]')), [['<sup class="invalid"><span>^1|</span></sup>'], '']);
|
|
81
81
|
assert.deepStrictEqual(inspect(parser('[[^1 |]]')), [['<sup class="invalid"><span>^1 |</span></sup>'], '']);
|
|
@@ -86,11 +86,11 @@ describe('Unit: parser/inline/reference', () => {
|
|
|
86
86
|
assert.deepStrictEqual(inspect(parser(`[[^A's, Aces']]`)), [[`<sup class="reference" data-abbr="A's, Aces'"><span></span></sup>`], '']);
|
|
87
87
|
assert.deepStrictEqual(inspect(parser('[[^^]]')), [['<sup class="invalid"><span>^^</span></sup>'], '']);
|
|
88
88
|
assert.deepStrictEqual(inspect(parser('[[\\^]]')), [['<sup class="reference"><span>^</span></sup>'], '']);
|
|
89
|
-
assert.deepStrictEqual(inspect(parser('[[^ ]]')), [['<sup class="invalid"><span
|
|
89
|
+
assert.deepStrictEqual(inspect(parser('[[^ ]]')), [['<sup class="invalid"><span>^</span></sup>'], '']);
|
|
90
90
|
assert.deepStrictEqual(inspect(parser('[[^ a]]')), [['<sup class="invalid"><span>^ a</span></sup>'], '']);
|
|
91
91
|
assert.deepStrictEqual(inspect(parser('[[^ |]]')), [['<sup class="invalid"><span>^ |</span></sup>'], '']);
|
|
92
92
|
assert.deepStrictEqual(inspect(parser('[[^ |b]]')), [['<sup class="invalid"><span>^ |b</span></sup>'], '']);
|
|
93
|
-
assert.deepStrictEqual(inspect(parser('[[^ | ]]')), [['<sup class="invalid"><span>^
|
|
93
|
+
assert.deepStrictEqual(inspect(parser('[[^ | ]]')), [['<sup class="invalid"><span>^ |</span></sup>'], '']);
|
|
94
94
|
assert.deepStrictEqual(inspect(parser('[[^ | b]]')), [['<sup class="invalid"><span>^ | b</span></sup>'], '']);
|
|
95
95
|
});
|
|
96
96
|
|
|
@@ -3,10 +3,10 @@ import { ReferenceParser } from '../inline';
|
|
|
3
3
|
import { union, subsequence, some, validate, guard, context, creator, surround, open, lazy, fmap, bind } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
5
|
import { str, stropt } from '../source';
|
|
6
|
-
import { regBlankStart,
|
|
6
|
+
import { regBlankStart, trimBlankStart, trimNodeEnd, stringify } from '../util';
|
|
7
7
|
import { html, defrag } from 'typed-dom/dom';
|
|
8
8
|
|
|
9
|
-
export const reference: ReferenceParser = lazy(() => creator(validate('[[',
|
|
9
|
+
export const reference: ReferenceParser = lazy(() => creator(validate('[[', fmap(surround(
|
|
10
10
|
'[[',
|
|
11
11
|
guard(context => context.syntax?.inline?.reference ?? true,
|
|
12
12
|
context({ syntax: { inline: {
|
|
@@ -22,10 +22,10 @@ export const reference: ReferenceParser = lazy(() => creator(validate('[[', ']]'
|
|
|
22
22
|
subsequence([
|
|
23
23
|
abbr,
|
|
24
24
|
open(stropt(/^(?=\^)/), some(inline, ']', /^\\?\n/)),
|
|
25
|
-
|
|
25
|
+
trimBlankStart(some(inline, ']', /^\\?\n/)),
|
|
26
26
|
]))),
|
|
27
27
|
']]'),
|
|
28
|
-
ns => [html('sup', attributes(ns), [html('span', defrag(ns))])]))));
|
|
28
|
+
ns => [html('sup', attributes(ns), [html('span', trimNodeEnd(defrag(ns)))])]))));
|
|
29
29
|
|
|
30
30
|
const abbr: ReferenceParser.AbbrParser = creator(bind(surround(
|
|
31
31
|
'^',
|
|
@@ -8,7 +8,7 @@ import { isStartTightNodes } from '../util';
|
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
import { unshift, push } from 'spica/array';
|
|
10
10
|
|
|
11
|
-
export const ruby: RubyParser = lazy(() => creator(validate('[',
|
|
11
|
+
export const ruby: RubyParser = lazy(() => creator(validate('[', bind(verify(
|
|
12
12
|
sequence([
|
|
13
13
|
surround('[', focus(/^(?:\\[^\n]|[^\\\[\]\n])+(?=]\()/, text), ']'),
|
|
14
14
|
surround('(', focus(/^(?:\\[^\n]|[^\\\(\)\n])+(?=\))/, text), ')'),
|
|
@@ -158,8 +158,8 @@ describe('Unit: parser/inline', () => {
|
|
|
158
158
|
assert.deepStrictEqual(inspect(parser('[~http://host')), [['[', '~', '<a href="http://host" target="_blank">http://host</a>'], '']);
|
|
159
159
|
assert.deepStrictEqual(inspect(parser('[~a@b')), [['[', '~', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
|
|
160
160
|
assert.deepStrictEqual(inspect(parser('[~~a~~]')), [['[', '<del>a</del>', ']'], '']);
|
|
161
|
-
assert.deepStrictEqual(inspect(parser('[^http://host')), [['[
|
|
162
|
-
assert.deepStrictEqual(inspect(parser('[^a@b')), [['[
|
|
161
|
+
assert.deepStrictEqual(inspect(parser('[^http://host')), [['[^', '<a href="http://host" target="_blank">http://host</a>'], '']);
|
|
162
|
+
assert.deepStrictEqual(inspect(parser('[^a@b')), [['[^', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
|
|
163
163
|
assert.deepStrictEqual(inspect(parser('[#a*b\nc*]')), [['[', '<a href="/hashtags/a" class="hashtag">#a</a>', '<em>b<br>c</em>', ']'], '']);
|
|
164
164
|
assert.deepStrictEqual(inspect(parser('[*a\nb*]{/}')), [['[', '<em>a<br>b</em>', ']', '<a href="/">/</a>'], '']);
|
|
165
165
|
assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), [['[', '[', '<em>a<br>b</em>', ']', ']'], '']);
|
package/src/parser/util.ts
CHANGED
|
@@ -101,6 +101,19 @@ const isStartTight = reduce((source: string, context: MarkdownParser.Context, ex
|
|
|
101
101
|
return source[0].trimStart() !== '';
|
|
102
102
|
}
|
|
103
103
|
}, (source, _, except = '') => `${source}\x1E${except}`);
|
|
104
|
+
|
|
105
|
+
export function isStartLooseNodes(nodes: readonly (HTMLElement | string)[]): boolean {
|
|
106
|
+
if (nodes.length === 0) return true;
|
|
107
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
108
|
+
const node = nodes[i];
|
|
109
|
+
if (isVisible(node)) return true;
|
|
110
|
+
if (typeof node === 'object') {
|
|
111
|
+
if (node.tagName === 'BR') break;
|
|
112
|
+
if (node.className === 'linebreak') break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
104
117
|
export function isStartTightNodes(nodes: readonly (HTMLElement | string)[]): boolean {
|
|
105
118
|
if (nodes.length === 0) return true;
|
|
106
119
|
return isVisible(nodes[0], 0);
|
|
@@ -170,7 +183,7 @@ export function trimBlankEnd<T extends HTMLElement | string>(parser: Parser<T>):
|
|
|
170
183
|
// }
|
|
171
184
|
// return nodes;
|
|
172
185
|
//}
|
|
173
|
-
function trimNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[] {
|
|
186
|
+
export function trimNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[] {
|
|
174
187
|
const skip = nodes.length > 0 &&
|
|
175
188
|
typeof nodes[nodes.length - 1] === 'object' &&
|
|
176
189
|
nodes[nodes.length - 1]['className'] === 'indexer'
|