securemark 0.254.1 → 0.255.1
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 +42 -21
- package/markdown.d.ts +1 -0
- package/package.json +1 -1
- package/src/combinator/control/manipulation/context.ts +1 -7
- package/src/parser/api/bind.test.ts +20 -20
- package/src/parser/api/parse.test.ts +2 -4
- 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/inline/annotation.test.ts +0 -1
- package/src/parser/inline/emphasis.test.ts +0 -1
- package/src/parser/inline/html.test.ts +23 -21
- package/src/parser/inline/html.ts +24 -16
- package/src/parser/inline/media.test.ts +3 -3
- package/src/parser/inline/media.ts +4 -0
- package/src/parser/inline/reference.test.ts +0 -1
- package/src/parser/inline/strong.test.ts +0 -1
- package/src/parser/inline.test.ts +0 -2
- package/src/parser/source/text.ts +1 -1
- package/src/parser/util.ts +13 -0
- package/src/renderer/render/media/video.ts +4 -1
package/CHANGELOG.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! securemark v0.
|
|
1
|
+
/*! securemark v0.255.1 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"));
|
|
@@ -2127,14 +2127,12 @@ function guard(f, parser) {
|
|
|
2127
2127
|
exports.guard = guard;
|
|
2128
2128
|
|
|
2129
2129
|
function reset(base, parser) {
|
|
2130
|
-
if (isEmpty(base)) return parser;
|
|
2131
2130
|
return (source, context) => parser(source, inherit((0, alias_1.ObjectCreate)(context), base));
|
|
2132
2131
|
}
|
|
2133
2132
|
|
|
2134
2133
|
exports.reset = reset;
|
|
2135
2134
|
|
|
2136
2135
|
function context(base, parser) {
|
|
2137
|
-
if (isEmpty(base)) return parser;
|
|
2138
2136
|
const override = (0, memoize_1.memoize)(context => inherit((0, alias_1.ObjectCreate)(context), base), new global_1.WeakMap());
|
|
2139
2137
|
return (source, context) => parser(source, override(context));
|
|
2140
2138
|
}
|
|
@@ -2166,12 +2164,6 @@ const inherit = (0, assign_1.template)((prop, target, source) => {
|
|
|
2166
2164
|
}
|
|
2167
2165
|
});
|
|
2168
2166
|
|
|
2169
|
-
function isEmpty(context) {
|
|
2170
|
-
for (const _ in context) return false;
|
|
2171
|
-
|
|
2172
|
-
return true;
|
|
2173
|
-
}
|
|
2174
|
-
|
|
2175
2167
|
/***/ }),
|
|
2176
2168
|
|
|
2177
2169
|
/***/ 7957:
|
|
@@ -3550,12 +3542,13 @@ function parse(source, opts = {}, context) {
|
|
|
3550
3542
|
const url = (0, header_2.headers)(source).find(field => field.toLowerCase().startsWith('url:'))?.slice(4).trim() ?? '';
|
|
3551
3543
|
source = !context ? (0, normalize_1.normalize)(source) : source;
|
|
3552
3544
|
context = {
|
|
3553
|
-
url: url ? new url_1.ReadonlyURL(url) : context?.url,
|
|
3554
3545
|
host: opts.host ?? context?.host ?? new url_1.ReadonlyURL(global_1.location.pathname, global_1.location.origin),
|
|
3546
|
+
url: url ? new url_1.ReadonlyURL(url) : context?.url,
|
|
3547
|
+
id: opts.id ?? context?.id,
|
|
3555
3548
|
caches: context?.caches,
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3549
|
+
...(context?.resources && {
|
|
3550
|
+
resources: context.resources
|
|
3551
|
+
})
|
|
3559
3552
|
};
|
|
3560
3553
|
if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
|
|
3561
3554
|
const node = (0, dom_1.frag)();
|
|
@@ -3888,7 +3881,7 @@ const parse_1 = __webpack_require__(5013);
|
|
|
3888
3881
|
|
|
3889
3882
|
const dom_1 = __webpack_require__(3252);
|
|
3890
3883
|
|
|
3891
|
-
exports.aside = (0, combinator_1.
|
|
3884
|
+
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
3885
|
([body, overflow, closer, opener, delim, param], _, context) => {
|
|
3893
3886
|
if (!closer || overflow || param.trimStart()) return [(0, dom_1.html)('pre', {
|
|
3894
3887
|
class: 'invalid',
|
|
@@ -3920,7 +3913,7 @@ exports.aside = (0, combinator_1.creator)(100, (0, combinator_1.block)((0, combi
|
|
|
3920
3913
|
id: (0, indexee_1.identity)((0, indexee_1.text)(heading)),
|
|
3921
3914
|
class: 'aside'
|
|
3922
3915
|
}, [document, references])];
|
|
3923
|
-
})))
|
|
3916
|
+
})));
|
|
3924
3917
|
|
|
3925
3918
|
/***/ }),
|
|
3926
3919
|
|
|
@@ -3946,7 +3939,7 @@ const mathblock_1 = __webpack_require__(3754);
|
|
|
3946
3939
|
const dom_1 = __webpack_require__(3252);
|
|
3947
3940
|
|
|
3948
3941
|
const opener = /^(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/;
|
|
3949
|
-
exports.example = (0, combinator_1.
|
|
3942
|
+
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
3943
|
([body, overflow, closer, opener, delim, type = 'markdown', param], _, context) => {
|
|
3951
3944
|
if (!closer || overflow || param.trimStart()) return [(0, dom_1.html)('pre', {
|
|
3952
3945
|
class: 'invalid',
|
|
@@ -3993,7 +3986,7 @@ exports.example = (0, combinator_1.creator)(100, (0, combinator_1.block)((0, com
|
|
|
3993
3986
|
'data-invalid-message': 'Invalid example type'
|
|
3994
3987
|
}, `${opener}${body}${closer}`)];
|
|
3995
3988
|
}
|
|
3996
|
-
})))
|
|
3989
|
+
})));
|
|
3997
3990
|
|
|
3998
3991
|
/***/ }),
|
|
3999
3992
|
|
|
@@ -6219,14 +6212,17 @@ const attrspecs = {
|
|
|
6219
6212
|
global_1.Object.setPrototypeOf(attrspecs, null);
|
|
6220
6213
|
global_1.Object.values(attrspecs).forEach(o => global_1.Object.setPrototypeOf(o, null));
|
|
6221
6214
|
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,
|
|
6215
|
+
/^<(?: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
6216
|
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
6217
|
// [...document.querySelectorAll('tbody > tr > td:first-child')].map(el => el.textContent.slice(1, -1))
|
|
6225
6218
|
|
|
6226
6219
|
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
6220
|
|
|
6228
6221
|
function elem(tag, as, bs, cs) {
|
|
6229
|
-
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag
|
|
6222
|
+
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag name "${tag}"`, as, bs, cs);
|
|
6223
|
+
if (cs.length === 0) return invalid('tag', `Missing the closing HTML tag "</${tag}>"`, as, bs, cs);
|
|
6224
|
+
if (bs.length === 0) return invalid('content', `Missing the content`, as, bs, cs);
|
|
6225
|
+
if (!(0, util_1.isStartLooseNodes)(bs)) return invalid('content', `Missing the visible content in the same line`, as, bs, cs);
|
|
6230
6226
|
const attrs = attributes('html', [], attrspecs[tag], as.slice(1, -1));
|
|
6231
6227
|
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
6228
|
}
|
|
@@ -6588,6 +6584,12 @@ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.creator)(10, (0, c
|
|
|
6588
6584
|
cache?.hasAttribute('alt') && cache?.setAttribute('alt', text);
|
|
6589
6585
|
if (!sanitize(el, url, text)) return [[el], rest];
|
|
6590
6586
|
(0, dom_1.define)(el, (0, html_1.attributes)('media', (0, array_1.push)([], el.classList), optspec, params));
|
|
6587
|
+
|
|
6588
|
+
// Awaiting the generic support for attr().
|
|
6589
|
+
if (el.hasAttribute('aspect-ratio')) {
|
|
6590
|
+
el.style.aspectRatio = el.getAttribute('aspect-ratio');
|
|
6591
|
+
}
|
|
6592
|
+
|
|
6591
6593
|
if (context.syntax?.inline?.link === false || cache && cache.tagName !== 'IMG') return [[el], rest];
|
|
6592
6594
|
return (0, combinator_1.fmap)(link_1.link, ([link]) => [(0, dom_1.define)(link, {
|
|
6593
6595
|
target: '_blank'
|
|
@@ -7637,7 +7639,7 @@ const str_1 = __webpack_require__(2790);
|
|
|
7637
7639
|
|
|
7638
7640
|
const dom_1 = __webpack_require__(3252);
|
|
7639
7641
|
|
|
7640
|
-
exports.delimiter = /[\s\x00-\x7F]|\S#|[
|
|
7642
|
+
exports.delimiter = /[\s\x00-\x7F]|\S#|[()、。!?][^\S\n]*(?=\\\n)/;
|
|
7641
7643
|
exports.nonWhitespace = /[\S\n]|$/;
|
|
7642
7644
|
exports.nonAlphanumeric = /[^0-9A-Za-z]|\S#|$/;
|
|
7643
7645
|
const repeat = (0, str_1.str)(/^(.)\1*/);
|
|
@@ -7767,7 +7769,7 @@ exports.unescsource = (0, combinator_1.creator)(source => {
|
|
|
7767
7769
|
Object.defineProperty(exports, "__esModule", ({
|
|
7768
7770
|
value: true
|
|
7769
7771
|
}));
|
|
7770
|
-
exports.stringify = exports.trimBlankEnd = exports.trimBlankStart = exports.trimBlank = exports.isStartTightNodes = exports.startTight = exports.startLoose = exports.visualize = exports.blankWith = exports.regBlankStart = void 0;
|
|
7772
|
+
exports.stringify = exports.trimBlankEnd = exports.trimBlankStart = exports.trimBlank = exports.isStartTightNodes = exports.isStartLooseNodes = exports.startTight = exports.startLoose = exports.visualize = exports.blankWith = exports.regBlankStart = void 0;
|
|
7771
7773
|
|
|
7772
7774
|
const global_1 = __webpack_require__(4128);
|
|
7773
7775
|
|
|
@@ -7871,6 +7873,24 @@ const isStartTight = (0, memoize_1.reduce)((source, context, except) => {
|
|
|
7871
7873
|
}
|
|
7872
7874
|
}, (source, _, except = '') => `${source}\x1E${except}`);
|
|
7873
7875
|
|
|
7876
|
+
function isStartLooseNodes(nodes) {
|
|
7877
|
+
if (nodes.length === 0) return true;
|
|
7878
|
+
|
|
7879
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
7880
|
+
const node = nodes[i];
|
|
7881
|
+
if (isVisible(node)) return true;
|
|
7882
|
+
|
|
7883
|
+
if (typeof node === 'object') {
|
|
7884
|
+
if (node.tagName === 'BR') break;
|
|
7885
|
+
if (node.className === 'linebreak') break;
|
|
7886
|
+
}
|
|
7887
|
+
}
|
|
7888
|
+
|
|
7889
|
+
return false;
|
|
7890
|
+
}
|
|
7891
|
+
|
|
7892
|
+
exports.isStartLooseNodes = isStartLooseNodes;
|
|
7893
|
+
|
|
7874
7894
|
function isStartTightNodes(nodes) {
|
|
7875
7895
|
if (nodes.length === 0) return true;
|
|
7876
7896
|
return isVisible(nodes[0], 0);
|
|
@@ -8378,6 +8398,7 @@ function video(source, url) {
|
|
|
8378
8398
|
src: source.getAttribute('data-src'),
|
|
8379
8399
|
'data-type': 'video',
|
|
8380
8400
|
...global_1.Object.fromEntries([...source.attributes].map(attr => [attr.name, attr.value])),
|
|
8401
|
+
style: source.hasAttribute('aspect-ratio') ? `aspect-ratio: ${source.getAttribute('aspect-ratio')};` : global_1.undefined,
|
|
8381
8402
|
muted: '',
|
|
8382
8403
|
controls: ''
|
|
8383
8404
|
});
|
package/markdown.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -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
|
-
}
|
|
@@ -27,7 +27,7 @@ describe('Unit: parser/api/bind', () => {
|
|
|
27
27
|
return acc;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
const cfgs = { footnotes: {
|
|
30
|
+
const cfgs = { footnotes: { references: html('ol') } };
|
|
31
31
|
|
|
32
32
|
it('huge input', () => {
|
|
33
33
|
const iter = bind(html('div'), { ...cfgs, id: '' }).parse(`${'\n'.repeat(10 * 1000 ** 2)}`);
|
|
@@ -168,62 +168,62 @@ describe('Unit: parser/api/bind', () => {
|
|
|
168
168
|
const el = html('div');
|
|
169
169
|
const chunk = frag();
|
|
170
170
|
const update = bind(chunk, { ...cfgs, chunk: true }).parse;
|
|
171
|
-
const iter = update([...Array(3)].map((_, i) => `
|
|
171
|
+
const iter = update([...Array(3)].map((_, i) => `[[${i + 1}]]`).join('\n\n'));
|
|
172
172
|
|
|
173
173
|
inspect(iter, 2);
|
|
174
174
|
el.appendChild(chunk);
|
|
175
175
|
assert.deepStrictEqual(
|
|
176
176
|
[...el.children].map(el => el.outerHTML),
|
|
177
177
|
[
|
|
178
|
-
html('p', [html('sup', { class: "
|
|
179
|
-
html('p', [html('sup', { class: "
|
|
178
|
+
html('p', [html('sup', { class: "reference" }, [html('span', '1')]),]).outerHTML,
|
|
179
|
+
html('p', [html('sup', { class: "reference" }, [html('span', '2')]),]).outerHTML,
|
|
180
180
|
]);
|
|
181
181
|
inspect(iter, 1);
|
|
182
182
|
el.appendChild(chunk);
|
|
183
183
|
assert.deepStrictEqual(
|
|
184
184
|
[...el.children].map(el => el.outerHTML),
|
|
185
185
|
[
|
|
186
|
-
html('p', [html('sup', { class: "
|
|
187
|
-
html('p', [html('sup', { class: "
|
|
188
|
-
html('p', [html('sup', { class: "
|
|
186
|
+
html('p', [html('sup', { class: "reference" }, [html('span', '1')]),]).outerHTML,
|
|
187
|
+
html('p', [html('sup', { class: "reference" }, [html('span', '2')]),]).outerHTML,
|
|
188
|
+
html('p', [html('sup', { class: "reference" }, [html('span', '3')]),]).outerHTML,
|
|
189
189
|
]);
|
|
190
190
|
inspect(iter);
|
|
191
191
|
assert.deepStrictEqual(
|
|
192
192
|
[...el.children].map(el => el.outerHTML),
|
|
193
193
|
[
|
|
194
194
|
html('p', [
|
|
195
|
-
html('sup', { class: "
|
|
195
|
+
html('sup', { class: "reference", id: "reference:ref:1", title: "1" }, [
|
|
196
196
|
html('span', { hidden: '' }, '1'),
|
|
197
|
-
html('a', { href: "#
|
|
197
|
+
html('a', { href: "#reference:def:1" }, '[1]'),
|
|
198
198
|
]),
|
|
199
199
|
]).outerHTML,
|
|
200
200
|
html('p', [
|
|
201
|
-
html('sup', { class: "
|
|
201
|
+
html('sup', { class: "reference", id: "reference:ref:2", title: "2" }, [
|
|
202
202
|
html('span', { hidden: '' }, '2'),
|
|
203
|
-
html('a', { href: "#
|
|
203
|
+
html('a', { href: "#reference:def:2" }, '[2]'),
|
|
204
204
|
]),
|
|
205
205
|
]).outerHTML,
|
|
206
206
|
html('p', [
|
|
207
|
-
html('sup', { class: "
|
|
207
|
+
html('sup', { class: "reference", id: "reference:ref:3", title: "3" }, [
|
|
208
208
|
html('span', { hidden: '' }, '3'),
|
|
209
|
-
html('a', { href: "#
|
|
209
|
+
html('a', { href: "#reference:def:3" }, '[3]'),
|
|
210
210
|
]),
|
|
211
211
|
]).outerHTML,
|
|
212
212
|
]);
|
|
213
213
|
assert.deepStrictEqual(
|
|
214
|
-
cfgs.footnotes.
|
|
214
|
+
cfgs.footnotes.references?.outerHTML,
|
|
215
215
|
html('ol', [
|
|
216
|
-
html('li', { id: '
|
|
216
|
+
html('li', { id: 'reference:def:1' }, [
|
|
217
217
|
'1',
|
|
218
|
-
html('sup', [html('a', { href: '#
|
|
218
|
+
html('sup', [html('a', { href: '#reference:ref:1' }, '^1')]),
|
|
219
219
|
]),
|
|
220
|
-
html('li', { id: '
|
|
220
|
+
html('li', { id: 'reference:def:2' }, [
|
|
221
221
|
'2',
|
|
222
|
-
html('sup', [html('a', { href: '#
|
|
222
|
+
html('sup', [html('a', { href: '#reference:ref:2' }, '^2')]),
|
|
223
223
|
]),
|
|
224
|
-
html('li', { id: '
|
|
224
|
+
html('li', { id: 'reference:def:3' }, [
|
|
225
225
|
'3',
|
|
226
|
-
html('sup', [html('a', { href: '#
|
|
226
|
+
html('sup', [html('a', { href: '#reference:ref:3' }, '^3')]),
|
|
227
227
|
]),
|
|
228
228
|
]).outerHTML);
|
|
229
229
|
assert.throws(() => update('').next());
|
|
@@ -204,16 +204,14 @@ describe('Unit: parser/api/parse', () => {
|
|
|
204
204
|
});
|
|
205
205
|
|
|
206
206
|
it('footnote', () => {
|
|
207
|
-
const footnotes = {
|
|
207
|
+
const footnotes = { references: html('ol') };
|
|
208
208
|
assert.deepStrictEqual(
|
|
209
209
|
[...parse('$-a\n$$\n$$\n\n(($-a[[b]][[c*d*]]))', { footnotes }).children].map(el => el.outerHTML),
|
|
210
210
|
[
|
|
211
211
|
'<figure data-type="math" data-label="$-a" data-group="$" data-number="1" id="label:$-a"><figcaption><span class="figindex">(1)</span><span class="figtext"></span></figcaption><div><div class="math" translate="no">$$\n$$</div></div></figure>',
|
|
212
212
|
'<p><sup class="annotation" id="annotation:ref:1" title="(1)[1][2]"><span hidden=""><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" id="reference:ref:1" title="b"><span hidden="">b</span><a href="#reference:def:1">[1]</a></sup><sup class="reference" id="reference:ref:2" title="cd"><span hidden="">c<em>d</em></span><a href="#reference:def:2">[2]</a></sup></span><a href="#annotation:def:1">*1</a></sup></p>',
|
|
213
|
+
'<ol class="annotations"><li id="annotation:def:1" data-marker="*1"><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" id="reference:ref:1" title="b"><span hidden="">b</span><a href="#reference:def:1">[1]</a></sup><sup class="reference" id="reference:ref:2" title="cd"><span hidden="">c<em>d</em></span><a href="#reference:def:2">[2]</a></sup><sup><a href="#annotation:ref:1">^1</a></sup></li></ol>',
|
|
213
214
|
]);
|
|
214
|
-
assert.deepStrictEqual(
|
|
215
|
-
footnotes.annotations.outerHTML,
|
|
216
|
-
'<ol><li id="annotation:def:1"><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" id="reference:ref:1" title="b"><span hidden="">b</span><a href="#reference:def:1">[1]</a></sup><sup class="reference" id="reference:ref:2" title="cd"><span hidden="">c<em>d</em></span><a href="#reference:def:2">[2]</a></sup><sup><a href="#annotation:ref:1">^1</a></sup></li></ol>');
|
|
217
215
|
assert.deepStrictEqual(
|
|
218
216
|
footnotes.references.outerHTML,
|
|
219
217
|
'<ol><li id="reference:def:1">b<sup><a href="#reference:ref:1">^1</a></sup></li><li id="reference:def:2">c<em>d</em><sup><a href="#reference:ref:2">^2</a></sup></li></ol>');
|
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
|
+
})));
|
|
@@ -46,7 +46,6 @@ describe('Unit: parser/inline/annotation', () => {
|
|
|
46
46
|
assert.deepStrictEqual(inspect(parser('((@a))')), [['<sup class="annotation"><span><a href="/@a" class="account">@a</a></span></sup>'], '']);
|
|
47
47
|
assert.deepStrictEqual(inspect(parser('((http://host))')), [['<sup class="annotation"><span><a href="http://host" target="_blank">http://host</a></span></sup>'], '']);
|
|
48
48
|
assert.deepStrictEqual(inspect(parser('((![]{a}))')), [['<sup class="annotation"><span>!<a href="a">a</a></span></sup>'], '']);
|
|
49
|
-
assert.deepStrictEqual(inspect(parser('((<a>))')), [['<sup class="annotation"><span><a></span></sup>'], '']);
|
|
50
49
|
assert.deepStrictEqual(inspect(parser('(((a)))')), [['<sup class="annotation"><span>(a)</span></sup>'], '']);
|
|
51
50
|
assert.deepStrictEqual(inspect(parser('((((a))))')), [['<sup class="annotation"><span><span class="paren">((a))</span></span></sup>'], '']);
|
|
52
51
|
assert.deepStrictEqual(inspect(parser('(([[a]]))')), [['<sup class="annotation"><span><sup class="reference"><span>a</span></sup></span></sup>'], '']);
|
|
@@ -46,7 +46,6 @@ describe('Unit: parser/inline/emphasis', () => {
|
|
|
46
46
|
assert.deepStrictEqual(inspect(parser('*a**b**c*')), [['<em>a<strong>b</strong>c</em>'], '']);
|
|
47
47
|
assert.deepStrictEqual(inspect(parser('*a**b**c*d')), [['<em>a<strong>b</strong>c</em>'], 'd']);
|
|
48
48
|
assert.deepStrictEqual(inspect(parser('*`a`*')), [['<em><code data-src="`a`">a</code></em>'], '']);
|
|
49
|
-
assert.deepStrictEqual(inspect(parser('*<bdi>*')), [['<em><bdi></em>'], '']);
|
|
50
49
|
assert.deepStrictEqual(inspect(parser('*(*a*)*')), [['<em><span class="paren">(<em>a</em>)</span></em>'], '']);
|
|
51
50
|
assert.deepStrictEqual(inspect(parser('*(**a**)*')), [['<em><span class="paren">(<strong>a</strong>)</span></em>'], '']);
|
|
52
51
|
});
|
|
@@ -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,12 +32,15 @@ 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]|>)/,
|
|
@@ -45,12 +48,15 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
|
|
|
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)
|
|
@@ -106,9 +106,9 @@ describe('Unit: parser/inline/media', () => {
|
|
|
106
106
|
assert.deepStrictEqual(inspect(parser('![]{/ nofollow}')), [['<a href="/" rel="nofollow" target="_blank"><img class="media" data-src="/" alt=""></a>'], '']);
|
|
107
107
|
assert.deepStrictEqual(inspect(parser('![]{/ width="4" height="3"}')), [['<a href="/" target="_blank"><img class="media" data-src="/" alt="" width="4" height="3"></a>'], '']);
|
|
108
108
|
assert.deepStrictEqual(inspect(parser('![]{/ 4x3}')), [['<a href="/" target="_blank"><img class="media" data-src="/" alt="" width="4" height="3"></a>'], '']);
|
|
109
|
-
assert.deepStrictEqual(inspect(parser('![]{/ aspect-ratio="4/3"}')), [['<a href="/" target="_blank"><img class="media" data-src="/" alt="" aspect-ratio="4/3"></a>'], '']);
|
|
110
|
-
assert.deepStrictEqual(inspect(parser('![]{/ aspect-ratio="4/3" nofollow}')), [['<a href="/" rel="nofollow" target="_blank"><img class="media" data-src="/" alt="" aspect-ratio="4/3"></a>'], '']);
|
|
111
|
-
assert.deepStrictEqual(inspect(parser('![]{/ 4:3}')), [['<a href="/" target="_blank"><img class="media" data-src="/" alt="" aspect-ratio="4/3"></a>'], '']);
|
|
109
|
+
assert.deepStrictEqual(inspect(parser('![]{/ aspect-ratio="4/3"}')), [['<a href="/" target="_blank"><img class="media" data-src="/" alt="" aspect-ratio="4/3" style="aspect-ratio: 4 / 3;"></a>'], '']);
|
|
110
|
+
assert.deepStrictEqual(inspect(parser('![]{/ aspect-ratio="4/3" nofollow}')), [['<a href="/" rel="nofollow" target="_blank"><img class="media" data-src="/" alt="" aspect-ratio="4/3" style="aspect-ratio: 4 / 3;"></a>'], '']);
|
|
111
|
+
assert.deepStrictEqual(inspect(parser('![]{/ 4:3}')), [['<a href="/" target="_blank"><img class="media" data-src="/" alt="" aspect-ratio="4/3" style="aspect-ratio: 4 / 3;"></a>'], '']);
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
});
|
|
@@ -48,6 +48,10 @@ export const media: MediaParser = lazy(() => creator(10, validate(['![', '!{'],
|
|
|
48
48
|
assert(!el.matches('.invalid'));
|
|
49
49
|
define(el, attributes('media', push([], el.classList), optspec, params));
|
|
50
50
|
assert(el.matches('img') || !el.matches('.invalid'));
|
|
51
|
+
// Awaiting the generic support for attr().
|
|
52
|
+
if (el.hasAttribute('aspect-ratio')) {
|
|
53
|
+
el.style.aspectRatio = el.getAttribute('aspect-ratio')!;
|
|
54
|
+
}
|
|
51
55
|
if (context.syntax?.inline?.link === false || cache && cache.tagName !== 'IMG') return [[el], rest];
|
|
52
56
|
return fmap(
|
|
53
57
|
link as MediaParser,
|
|
@@ -46,7 +46,6 @@ describe('Unit: parser/inline/reference', () => {
|
|
|
46
46
|
assert.deepStrictEqual(inspect(parser('[[@a]]')), [['<sup class="reference"><span><a href="/@a" class="account">@a</a></span></sup>'], '']);
|
|
47
47
|
assert.deepStrictEqual(inspect(parser('[[http://host]]')), [['<sup class="reference"><span><a href="http://host" target="_blank">http://host</a></span></sup>'], '']);
|
|
48
48
|
assert.deepStrictEqual(inspect(parser('[[![]{a}]]')), [['<sup class="reference"><span>!<a href="a">a</a></span></sup>'], '']);
|
|
49
|
-
assert.deepStrictEqual(inspect(parser('[[<a>]]')), [['<sup class="reference"><span><a></span></sup>'], '']);
|
|
50
49
|
assert.deepStrictEqual(inspect(parser('[[[a]]]')), [['<sup class="reference"><span>[a]</span></sup>'], '']);
|
|
51
50
|
assert.deepStrictEqual(inspect(parser('[[[[a]]]]')), [['<sup class="reference"><span>[[a]]</span></sup>'], '']);
|
|
52
51
|
assert.deepStrictEqual(inspect(parser('[[((a))]]')), [['<sup class="reference"><span><span class="paren">((a))</span></span></sup>'], '']);
|
|
@@ -43,7 +43,6 @@ describe('Unit: parser/inline/strong', () => {
|
|
|
43
43
|
assert.deepStrictEqual(inspect(parser('**a*b*c**')), [['<strong>a<em>b</em>c</strong>'], '']);
|
|
44
44
|
assert.deepStrictEqual(inspect(parser('**a*b*c**d')), [['<strong>a<em>b</em>c</strong>'], 'd']);
|
|
45
45
|
assert.deepStrictEqual(inspect(parser('**`a`**')), [['<strong><code data-src="`a`">a</code></strong>'], '']);
|
|
46
|
-
assert.deepStrictEqual(inspect(parser('**<bdi>**')), [['<strong><bdi></strong>'], '']);
|
|
47
46
|
assert.deepStrictEqual(inspect(parser('**(*a*)**')), [['<strong><span class="paren">(<em>a</em>)</span></strong>'], '']);
|
|
48
47
|
assert.deepStrictEqual(inspect(parser('**(**a**)**')), [['<strong><span class="paren">(<strong>a</strong>)</span></strong>'], '']);
|
|
49
48
|
});
|
|
@@ -155,8 +155,6 @@ describe('Unit: parser/inline', () => {
|
|
|
155
155
|
assert.deepStrictEqual(inspect(parser('[(([a]{#}))]{#}')), [['<a href="#"><span class="paren">(<span class="paren">([a]{#})</span>)</span></a>'], '']);
|
|
156
156
|
assert.deepStrictEqual(inspect(parser('"[[""]]')), [['"', '<sup class="reference"><span>""</span></sup>'], '']);
|
|
157
157
|
assert.deepStrictEqual(inspect(parser('<http://host>')), [['<', '<a href="http://host" target="_blank">http://host</a>', '>'], '']);
|
|
158
|
-
assert.deepStrictEqual(inspect(parser('<<bdi>a<</bdi>')), [['<', '<bdi>a<</bdi>'], '']);
|
|
159
|
-
assert.deepStrictEqual(inspect(parser('*<bdi>*`</bdi>`')), [['<em><bdi></em>', '<code data-src="`</bdi>`"></bdi></code>'], '']);
|
|
160
158
|
assert.deepStrictEqual(inspect(parser('[~http://host')), [['[', '~', '<a href="http://host" target="_blank">http://host</a>'], '']);
|
|
161
159
|
assert.deepStrictEqual(inspect(parser('[~a@b')), [['[', '~', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
|
|
162
160
|
assert.deepStrictEqual(inspect(parser('[~~a~~]')), [['[', '<del>a</del>', ']'], '']);
|
|
@@ -4,7 +4,7 @@ import { union, focus, creator } from '../../combinator';
|
|
|
4
4
|
import { str } from './str';
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
6
|
|
|
7
|
-
export const delimiter = /[\s\x00-\x7F]|\S#|[
|
|
7
|
+
export const delimiter = /[\s\x00-\x7F]|\S#|[()、。!?][^\S\n]*(?=\\\n)/;
|
|
8
8
|
export const nonWhitespace = /[\S\n]|$/;
|
|
9
9
|
export const nonAlphanumeric = /[^0-9A-Za-z]|\S#|$/;
|
|
10
10
|
const repeat = str(/^(.)\1*/);
|
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);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Object } from 'spica/global';
|
|
1
|
+
import { undefined, Object } from 'spica/global';
|
|
2
2
|
import { html } from 'typed-dom/dom';
|
|
3
3
|
|
|
4
4
|
const extensions = [
|
|
@@ -13,6 +13,9 @@ export function video(source: HTMLImageElement, url: URL): HTMLVideoElement | un
|
|
|
13
13
|
'data-type': 'video',
|
|
14
14
|
...Object.fromEntries([...source.attributes]
|
|
15
15
|
.map(attr => [attr.name, attr.value])),
|
|
16
|
+
style: source.hasAttribute('aspect-ratio')
|
|
17
|
+
? `aspect-ratio: ${source.getAttribute('aspect-ratio')};`
|
|
18
|
+
: undefined,
|
|
16
19
|
muted: '',
|
|
17
20
|
controls: '',
|
|
18
21
|
});
|