securemark 0.253.2 → 0.254.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 +4 -0
- package/README.md +1 -1
- package/design.md +6 -0
- package/dist/index.js +16 -12
- package/markdown.d.ts +2 -2
- package/package.json +1 -1
- package/src/parser/block/extension/table.ts +5 -8
- package/src/parser/inline/emphasis.test.ts +1 -1
- package/src/parser/inline/html.test.ts +58 -58
- package/src/parser/inline/html.ts +152 -9
- package/src/parser/inline/strong.test.ts +1 -1
- package/src/parser/inline.test.ts +4 -5
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ https://falsandtru.github.io/securemark/
|
|
|
43
43
|
- Preformattedtext (```)
|
|
44
44
|
- HorizontalRule (---)
|
|
45
45
|
- Inline markups (*, `, []{}, {}, ![]{}, !{}, \[](), ++, ~~, (()), ...)
|
|
46
|
-
- Inline HTML tags (\<
|
|
46
|
+
- Inline HTML tags (\<bdi>, \<bdo>)
|
|
47
47
|
- Autolink (https://host, user@host, @user)
|
|
48
48
|
- Shortmedia (!https://host/image.png, !https://youtu.be/...)
|
|
49
49
|
- Syntex highlight (```lang filename)
|
package/design.md
CHANGED
|
@@ -307,3 +307,9 @@ MarkdownはGFMのように最初から高機能で完成度の高い拡張不要
|
|
|
307
307
|
|
|
308
308
|
Data URIは保存および転送容量削減ならびにユーザーおよび管理者双方の集約的管理のためサポートしない。
|
|
309
309
|
特に規制および公開レベルの媒体別設定の実現のためテキストとメディアの分離が必須となる。
|
|
310
|
+
|
|
311
|
+
### HTML
|
|
312
|
+
|
|
313
|
+
- \<small>: 法的表記を縮小表示すべきでないため削除。
|
|
314
|
+
- \<sub>: \<small>の代わりに使用されないよう削除。他の構文との相性も悪い。
|
|
315
|
+
- \<sup>: 同上。
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! securemark v0.
|
|
1
|
+
/*! securemark v0.254.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"));
|
|
@@ -4514,7 +4514,8 @@ function format(rows) {
|
|
|
4514
4514
|
let lHeadCellIdx;
|
|
4515
4515
|
let rHeadCellIdx;
|
|
4516
4516
|
|
|
4517
|
-
for (let j = 0
|
|
4517
|
+
for (let j = 0; j < cells.length; ++j) {
|
|
4518
|
+
const jn = (0, global_1.BigInt)(j);
|
|
4518
4519
|
const isVirtual = !!ranges[i]?.[j];
|
|
4519
4520
|
const cell = isVirtual ? (0, array_1.splice)(cells, j, 0, global_1.undefined) && ranges[i][j] : cells[j];
|
|
4520
4521
|
const isHeadCell = cell.tagName === 'TH';
|
|
@@ -4545,8 +4546,8 @@ function format(rows) {
|
|
|
4545
4546
|
|
|
4546
4547
|
if (colSpan > 1) {
|
|
4547
4548
|
(0, array_1.splice)(cells, j + 1, 0, ...(0, global_1.Array)(colSpan - 1));
|
|
4548
|
-
heads |= heads & 1n << jn
|
|
4549
|
-
highlights |= highlights & 1n << jn
|
|
4549
|
+
heads |= heads & 1n << jn && ~(~0n << (0, global_1.BigInt)(colSpan)) << jn;
|
|
4550
|
+
highlights |= highlights & 1n << jn && ~(~0n << (0, global_1.BigInt)(colSpan)) << jn;
|
|
4550
4551
|
j += colSpan - 1;
|
|
4551
4552
|
}
|
|
4552
4553
|
|
|
@@ -6209,21 +6210,24 @@ const cache_1 = __webpack_require__(9210);
|
|
|
6209
6210
|
|
|
6210
6211
|
const array_1 = __webpack_require__(8112);
|
|
6211
6212
|
|
|
6212
|
-
const tags = global_1.Object.freeze(['
|
|
6213
|
-
const
|
|
6213
|
+
const tags = global_1.Object.freeze(['bdo', 'bdi']);
|
|
6214
|
+
const attrspecs = {
|
|
6214
6215
|
bdo: {
|
|
6215
6216
|
dir: global_1.Object.freeze(['ltr', 'rtl'])
|
|
6216
6217
|
}
|
|
6217
6218
|
};
|
|
6218
|
-
global_1.Object.setPrototypeOf(
|
|
6219
|
-
global_1.Object.values(
|
|
6219
|
+
global_1.Object.setPrototypeOf(attrspecs, null);
|
|
6220
|
+
global_1.Object.values(attrspecs).forEach(o => global_1.Object.setPrototypeOf(o, null));
|
|
6220
6221
|
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
|
|
6221
|
-
/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/, source => [[source], '']), (0, combinator_1.match)(
|
|
6222
|
-
exports.attribute = (0, combinator_1.union)([(0, source_1.str)(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/)]);
|
|
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, util_1.startLoose)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.open)(/^\n?/, (0, combinator_1.some)(inline_1.inline, (0, util_1.blankWith)('\n', `</${tag}>`)), true)])), `</${tag}>`), (0, source_1.str)(`</${tag}>`), false, ([as, bs, cs], rest) => [[elem(tag, as, bs, cs)], 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, util_1.startLoose)((0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.open)(/^\n?/, (0, combinator_1.some)(inline_1.inline, (0, util_1.blankWith)('\n', `</${tag}>`)), true)])), `</${tag}>`), (0, source_1.str)(`</${tag}>`), false, ([as, bs, cs], rest) => [[elem(tag, as, bs, cs)], rest]), ([, tag]) => tag, new cache_1.Cache(10000)))])))));
|
|
6223
|
+
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
|
+
// [...document.querySelectorAll('tbody > tr > td:first-child')].map(el => el.textContent.slice(1, -1))
|
|
6225
|
+
|
|
6226
|
+
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"]);
|
|
6223
6227
|
|
|
6224
6228
|
function elem(tag, as, bs, cs) {
|
|
6225
6229
|
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag <${tag}>`, as, bs, cs);
|
|
6226
|
-
const attrs = attributes('html', [],
|
|
6230
|
+
const attrs = attributes('html', [], attrspecs[tag], as.slice(1, -1));
|
|
6227
6231
|
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));
|
|
6228
6232
|
}
|
|
6229
6233
|
|
|
@@ -6247,7 +6251,7 @@ function attributes(syntax, classes, spec, params) {
|
|
|
6247
6251
|
const name = param.split('=', 1)[0];
|
|
6248
6252
|
const value = param !== name ? param.slice(name.length + 2, -1).replace(/\\(.?)/g, '$1') : global_1.undefined;
|
|
6249
6253
|
invalid ||= !spec || name in attrs;
|
|
6250
|
-
if (spec &&
|
|
6254
|
+
if (spec && name in spec && !spec[name]) continue;
|
|
6251
6255
|
spec?.[name]?.includes(value) || value !== global_1.undefined && spec?.[name]?.length === 0 ? attrs[name] = value ?? '' : invalid ||= !!spec;
|
|
6252
6256
|
(0, array_1.splice)(params, i--, 1);
|
|
6253
6257
|
}
|
package/markdown.d.ts
CHANGED
|
@@ -964,8 +964,8 @@ export namespace MarkdownParser {
|
|
|
964
964
|
}
|
|
965
965
|
}
|
|
966
966
|
export interface HTMLParser extends
|
|
967
|
-
// Allow: wbr,
|
|
968
|
-
// <
|
|
967
|
+
// Allow: wbr, bdo, bdi
|
|
968
|
+
// <bdi>abc</bdi>
|
|
969
969
|
Inline<'html'>,
|
|
970
970
|
Parser<HTMLElement | string, Context, [
|
|
971
971
|
HTMLParser.OpenTagParser,
|
package/package.json
CHANGED
|
@@ -220,7 +220,8 @@ function format(rows: Tree<RowParser>[]): HTMLTableSectionElement[] {
|
|
|
220
220
|
let hasDataCell = false;
|
|
221
221
|
let lHeadCellIdx: bigint;
|
|
222
222
|
let rHeadCellIdx: bigint;
|
|
223
|
-
for (let j = 0
|
|
223
|
+
for (let j = 0; j < cells.length; ++j) {
|
|
224
|
+
const jn = BigInt(j);
|
|
224
225
|
const isVirtual = !!ranges[i]?.[j];
|
|
225
226
|
const cell = isVirtual
|
|
226
227
|
? splice(cells, j, 0, undefined) && ranges[i][j]
|
|
@@ -248,12 +249,8 @@ function format(rows: Tree<RowParser>[]): HTMLTableSectionElement[] {
|
|
|
248
249
|
assert(colSpan > 0);
|
|
249
250
|
if (colSpan > 1) {
|
|
250
251
|
splice(cells, j + 1, 0, ...Array(colSpan - 1));
|
|
251
|
-
heads |= heads & 1n << jn
|
|
252
|
-
|
|
253
|
-
: 0n;
|
|
254
|
-
highlights |= highlights & 1n << jn
|
|
255
|
-
? ~(~0n << BigInt(colSpan)) << jn
|
|
256
|
-
: 0n;
|
|
252
|
+
heads |= heads & 1n << jn && ~(~0n << BigInt(colSpan)) << jn;
|
|
253
|
+
highlights |= highlights & 1n << jn && ~(~0n << BigInt(colSpan)) << jn;
|
|
257
254
|
j += colSpan - 1;
|
|
258
255
|
}
|
|
259
256
|
if (target === thead) {
|
|
@@ -315,7 +312,7 @@ function format(rows: Tree<RowParser>[]): HTMLTableSectionElement[] {
|
|
|
315
312
|
'v c',
|
|
316
313
|
'v h',
|
|
317
314
|
'v h c',
|
|
318
|
-
][+!!(highlights & m)
|
|
315
|
+
][+!!(highlights & m) | +!!(lHighlight | rHighlight) << 1 | +!!(tHighlights & m) << 2]));
|
|
319
316
|
}
|
|
320
317
|
continue;
|
|
321
318
|
case tfoot:
|
|
@@ -46,7 +46,7 @@ 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('*<
|
|
49
|
+
assert.deepStrictEqual(inspect(parser('*<bdi>*')), [['<em><bdi></em>'], '']);
|
|
50
50
|
assert.deepStrictEqual(inspect(parser('*(*a*)*')), [['<em><span class="paren">(<em>a</em>)</span></em>'], '']);
|
|
51
51
|
assert.deepStrictEqual(inspect(parser('*(**a**)*')), [['<em><span class="paren">(<strong>a</strong>)</span></em>'], '']);
|
|
52
52
|
});
|
|
@@ -11,10 +11,10 @@ describe('Unit: parser/inline/html', () => {
|
|
|
11
11
|
assert.deepStrictEqual(inspect(parser('<script>alert()<script>')), undefined);
|
|
12
12
|
assert.deepStrictEqual(inspect(parser('<script>alert()</script>')), [['<span class="invalid"><script>alert<span class="paren">()</span></script></span>'], '']);
|
|
13
13
|
assert.deepStrictEqual(inspect(parser('<script src="\\""></script>')), undefined);
|
|
14
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
15
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
16
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
17
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
14
|
+
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()">')), undefined);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()"></bdi>')), undefined);
|
|
16
|
+
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()">a</bdi>')), [['<span class="invalid"><bdi onclick="alert()">a</bdi></span>'], '']);
|
|
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>'], '']);
|
|
19
19
|
});
|
|
20
20
|
|
|
@@ -25,80 +25,80 @@ 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('<
|
|
29
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
30
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
31
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
32
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
33
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
34
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
35
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
36
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
37
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
38
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
39
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
40
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
41
|
-
assert.deepStrictEqual(inspect(parser('</
|
|
42
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
28
|
+
assert.deepStrictEqual(inspect(parser('<bdi>z')), undefined);
|
|
29
|
+
assert.deepStrictEqual(inspect(parser('<bdi></bdi>')), undefined);
|
|
30
|
+
assert.deepStrictEqual(inspect(parser('<bdi> </bdi>')), undefined);
|
|
31
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\\ </bdi>')), undefined);
|
|
32
|
+
assert.deepStrictEqual(inspect(parser('<bdi>	</bdi>')), undefined);
|
|
33
|
+
assert.deepStrictEqual(inspect(parser('<bdi><wbr></bdi>')), undefined);
|
|
34
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\n</bdi>')), undefined);
|
|
35
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\na</bdi>')), undefined);
|
|
36
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\\\na</bdi>')), undefined);
|
|
37
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a')), undefined);
|
|
38
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a</BDO>')), undefined);
|
|
39
|
+
assert.deepStrictEqual(inspect(parser('<BDI>a</BDI>')), undefined);
|
|
40
|
+
assert.deepStrictEqual(inspect(parser('<BDI>a</bdo>')), undefined);
|
|
41
|
+
assert.deepStrictEqual(inspect(parser('</bdi>')), undefined);
|
|
42
|
+
assert.deepStrictEqual(inspect(parser('<bdi/>')), undefined);
|
|
43
43
|
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
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
44
|
+
assert.deepStrictEqual(inspect(parser('<bdi><bdi><bdi>a</bdi></bdi></bdi>')), [['<bdi><bdi><bdi>a</bdi></bdi></bdi>'], '']);
|
|
45
45
|
assert.deepStrictEqual(inspect(parser('<x a="*b*"')), undefined);
|
|
46
46
|
assert.deepStrictEqual(inspect(parser('<x a="*b*">')), undefined);
|
|
47
47
|
assert.deepStrictEqual(inspect(parser('<x a="*b*">c')), undefined);
|
|
48
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
49
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
50
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
51
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
52
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
53
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
54
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
55
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
56
|
-
assert.deepStrictEqual(inspect(parser(' <
|
|
48
|
+
assert.deepStrictEqual(inspect(parser('<bdi a="*b*"')), undefined);
|
|
49
|
+
assert.deepStrictEqual(inspect(parser('<bdi a="*b*">')), undefined);
|
|
50
|
+
assert.deepStrictEqual(inspect(parser('<bdi a="*b*">c')), undefined);
|
|
51
|
+
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *c*')), undefined);
|
|
52
|
+
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *c*>')), undefined);
|
|
53
|
+
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *c*>d</bdi>')), undefined);
|
|
54
|
+
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *>*c*')), undefined);
|
|
55
|
+
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *>*c*</bdi>')), undefined);
|
|
56
|
+
assert.deepStrictEqual(inspect(parser(' <bdi>a</bdi>')), undefined);
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
it('basic', () => {
|
|
60
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
61
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
62
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
63
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
64
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
65
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
66
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
67
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
68
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
69
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
70
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
71
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
72
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
60
|
+
assert.deepStrictEqual(inspect(parser('<bdi> a</bdi>')), [['<bdi> a</bdi>'], '']);
|
|
61
|
+
assert.deepStrictEqual(inspect(parser('<bdi> a </bdi>')), [['<bdi> a </bdi>'], '']);
|
|
62
|
+
assert.deepStrictEqual(inspect(parser('<bdi> a </bdi>')), [['<bdi> a </bdi>'], '']);
|
|
63
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\\ a</bdi>')), [['<bdi> a</bdi>'], '']);
|
|
64
|
+
assert.deepStrictEqual(inspect(parser('<bdi><wbr>a</bdi>')), [['<bdi><wbr>a</bdi>'], '']);
|
|
65
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a</bdi>')), [['<bdi>a</bdi>'], '']);
|
|
66
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a</bdi>a')), [['<bdi>a</bdi>'], 'a']);
|
|
67
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a </bdi>')), [['<bdi>a </bdi>'], '']);
|
|
68
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a \n </bdi>')), [['<bdi>a </bdi>'], '']);
|
|
69
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a\n</bdi>')), [['<bdi>a</bdi>'], '']);
|
|
70
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a\n </bdi>')), [['<bdi>a </bdi>'], '']);
|
|
71
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a\n<wbr></bdi>')), [['<bdi>a<wbr></bdi>'], '']);
|
|
72
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a\nb</bdi>')), [['<bdi>a<br>b</bdi>'], '']);
|
|
73
73
|
assert.deepStrictEqual(inspect(parser('<wbr>a')), [['<wbr>'], 'a']);
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
it('nest', () => {
|
|
77
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
78
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
79
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
77
|
+
assert.deepStrictEqual(inspect(parser('<bdi><bdi>a</bdi></bdi>')), [['<bdi><bdi>a</bdi></bdi>'], '']);
|
|
78
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a<bdi>b</bdi>c</bdi>')), [['<bdi>a<bdi>b</bdi>c</bdi>'], '']);
|
|
79
|
+
assert.deepStrictEqual(inspect(parser('<bdi>`a`</bdi>')), [['<bdi><code data-src="`a`">a</code></bdi>'], '']);
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
it('escape', () => {
|
|
83
83
|
assert.deepStrictEqual(inspect(parser('<a>')), undefined);
|
|
84
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
85
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
84
|
+
assert.deepStrictEqual(inspect(parser('<bdi><a>a</a></bdi>')), [['<bdi><span class="invalid"><a>a</a></span></bdi>'], '']);
|
|
85
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a<a>b</a>c</bdi>')), [['<bdi>a<span class="invalid"><a>b</a></span>c</bdi>'], '']);
|
|
86
86
|
assert.deepStrictEqual(inspect(parser('<img>')), [['<img'], '>']);
|
|
87
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
87
|
+
assert.deepStrictEqual(inspect(parser('<bdi><img></bdi>')), [['<bdi><img></bdi>'], '']);
|
|
88
88
|
assert.deepStrictEqual(inspect(parser('<img />')), [['<img'], ' />']);
|
|
89
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
89
|
+
assert.deepStrictEqual(inspect(parser('<bdi><img /></bdi>')), [['<bdi><img /></bdi>'], '']);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
92
|
it('attribute', () => {
|
|
93
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
94
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
95
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
96
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
97
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
98
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
99
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
100
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
101
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
93
|
+
assert.deepStrictEqual(inspect(parser('<bdi\n>a</bdi>')), undefined);
|
|
94
|
+
assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>')), [['<bdi>a</bdi>'], '']);
|
|
95
|
+
assert.deepStrictEqual(inspect(parser('<bdi \n>a</bdi>')), undefined);
|
|
96
|
+
assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>')), [['<bdi>a</bdi>'], '']);
|
|
97
|
+
assert.deepStrictEqual(inspect(parser('<bdi __proto__>a</bdi>')), undefined);
|
|
98
|
+
assert.deepStrictEqual(inspect(parser('<bdi constructor>a</bdi>')), [['<span class="invalid"><bdi constructor>a</bdi></span>'], '']);
|
|
99
|
+
assert.deepStrictEqual(inspect(parser('<bdi toString>a</bdi>')), undefined);
|
|
100
|
+
assert.deepStrictEqual(inspect(parser('<bdi X>a</bdi>')), undefined);
|
|
101
|
+
assert.deepStrictEqual(inspect(parser('<bdi x>a</bdi>')), [['<span class="invalid"><bdi x>a</bdi></span>'], '']);
|
|
102
102
|
assert.deepStrictEqual(inspect(parser('<bdo>a</bdo>')), [['<span class="invalid"><bdo>a</bdo></span>'], '']);
|
|
103
103
|
assert.deepStrictEqual(inspect(parser('<bdo >a</bdo>')), [['<span class="invalid"><bdo >a</bdo></span>'], '']);
|
|
104
104
|
assert.deepStrictEqual(inspect(parser('<bdo __proto__>a</bdo>')), undefined);
|
|
@@ -9,14 +9,14 @@ import { memoize } from 'spica/memoize';
|
|
|
9
9
|
import { Cache } from 'spica/cache';
|
|
10
10
|
import { unshift, push, splice } from 'spica/array';
|
|
11
11
|
|
|
12
|
-
const tags = Object.freeze(['
|
|
13
|
-
const
|
|
12
|
+
const tags = Object.freeze(['bdo', 'bdi']);
|
|
13
|
+
const attrspecs = {
|
|
14
14
|
bdo: {
|
|
15
|
-
dir: Object.freeze(['ltr', 'rtl']
|
|
15
|
+
dir: Object.freeze(['ltr', 'rtl']),
|
|
16
16
|
},
|
|
17
17
|
} as const;
|
|
18
|
-
Object.setPrototypeOf(
|
|
19
|
-
Object.values(
|
|
18
|
+
Object.setPrototypeOf(attrspecs, null);
|
|
19
|
+
Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
|
|
20
20
|
|
|
21
21
|
export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)/, union([
|
|
22
22
|
focus(
|
|
@@ -27,7 +27,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
|
|
|
27
27
|
/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/,
|
|
28
28
|
source => [[source], '']),
|
|
29
29
|
match(
|
|
30
|
-
|
|
30
|
+
new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`),
|
|
31
31
|
memoize(
|
|
32
32
|
([, tag]) =>
|
|
33
33
|
surround<HTMLParser.TagParser, string>(surround(
|
|
@@ -38,7 +38,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
|
|
|
38
38
|
str(`</${tag}>`), false,
|
|
39
39
|
([as, bs, cs], rest) =>
|
|
40
40
|
[[elem(tag, as, bs, cs)], rest]),
|
|
41
|
-
([, tag]) =>
|
|
41
|
+
([, tag]) => TAGS.indexOf(tag), [])),
|
|
42
42
|
match(
|
|
43
43
|
/^<([a-z]+)(?=[^\S\n]|>)/,
|
|
44
44
|
memoize(
|
|
@@ -59,16 +59,159 @@ export const attribute: HTMLParser.TagParser.AttributeParser = union([
|
|
|
59
59
|
str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/),
|
|
60
60
|
]);
|
|
61
61
|
|
|
62
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
|
|
63
|
+
// [...document.querySelectorAll('tbody > tr > td:first-child')].map(el => el.textContent.slice(1, -1))
|
|
64
|
+
const TAGS = Object.freeze([
|
|
65
|
+
"html",
|
|
66
|
+
"base",
|
|
67
|
+
"head",
|
|
68
|
+
"link",
|
|
69
|
+
"meta",
|
|
70
|
+
"style",
|
|
71
|
+
"title",
|
|
72
|
+
"body",
|
|
73
|
+
"address",
|
|
74
|
+
"article",
|
|
75
|
+
"aside",
|
|
76
|
+
"footer",
|
|
77
|
+
"header",
|
|
78
|
+
"h1", "h2", "h3", "h4", "h5", "h6",
|
|
79
|
+
"main",
|
|
80
|
+
"nav",
|
|
81
|
+
"section",
|
|
82
|
+
"blockquote",
|
|
83
|
+
"dd",
|
|
84
|
+
"div",
|
|
85
|
+
"dl",
|
|
86
|
+
"dt",
|
|
87
|
+
"figcaption",
|
|
88
|
+
"figure",
|
|
89
|
+
"hr",
|
|
90
|
+
"li",
|
|
91
|
+
"menu",
|
|
92
|
+
"ol",
|
|
93
|
+
"p",
|
|
94
|
+
"pre",
|
|
95
|
+
"ul",
|
|
96
|
+
"a",
|
|
97
|
+
"abbr",
|
|
98
|
+
"b",
|
|
99
|
+
"bdi",
|
|
100
|
+
"bdo",
|
|
101
|
+
"br",
|
|
102
|
+
"cite",
|
|
103
|
+
"code",
|
|
104
|
+
"data",
|
|
105
|
+
"dfn",
|
|
106
|
+
"em",
|
|
107
|
+
"i",
|
|
108
|
+
"kbd",
|
|
109
|
+
"mark",
|
|
110
|
+
"q",
|
|
111
|
+
"rp",
|
|
112
|
+
"rt",
|
|
113
|
+
"ruby",
|
|
114
|
+
"s",
|
|
115
|
+
"samp",
|
|
116
|
+
"small",
|
|
117
|
+
"span",
|
|
118
|
+
"strong",
|
|
119
|
+
"sub",
|
|
120
|
+
"sup",
|
|
121
|
+
"time",
|
|
122
|
+
"u",
|
|
123
|
+
"var",
|
|
124
|
+
"wbr",
|
|
125
|
+
"area",
|
|
126
|
+
"audio",
|
|
127
|
+
"img",
|
|
128
|
+
"map",
|
|
129
|
+
"track",
|
|
130
|
+
"video",
|
|
131
|
+
"embed",
|
|
132
|
+
"iframe",
|
|
133
|
+
"object",
|
|
134
|
+
"picture",
|
|
135
|
+
"portal",
|
|
136
|
+
"source",
|
|
137
|
+
"svg",
|
|
138
|
+
"math",
|
|
139
|
+
"canvas",
|
|
140
|
+
"noscript",
|
|
141
|
+
"script",
|
|
142
|
+
"del",
|
|
143
|
+
"ins",
|
|
144
|
+
"caption",
|
|
145
|
+
"col",
|
|
146
|
+
"colgroup",
|
|
147
|
+
"table",
|
|
148
|
+
"tbody",
|
|
149
|
+
"td",
|
|
150
|
+
"tfoot",
|
|
151
|
+
"th",
|
|
152
|
+
"thead",
|
|
153
|
+
"tr",
|
|
154
|
+
"button",
|
|
155
|
+
"datalist",
|
|
156
|
+
"fieldset",
|
|
157
|
+
"form",
|
|
158
|
+
"input",
|
|
159
|
+
"label",
|
|
160
|
+
"legend",
|
|
161
|
+
"meter",
|
|
162
|
+
"optgroup",
|
|
163
|
+
"option",
|
|
164
|
+
"output",
|
|
165
|
+
"progress",
|
|
166
|
+
"select",
|
|
167
|
+
"textarea",
|
|
168
|
+
"details",
|
|
169
|
+
"dialog",
|
|
170
|
+
"summary",
|
|
171
|
+
"slot",
|
|
172
|
+
"template",
|
|
173
|
+
"acronym",
|
|
174
|
+
"applet",
|
|
175
|
+
"basefont",
|
|
176
|
+
"bgsound",
|
|
177
|
+
"big",
|
|
178
|
+
"blink",
|
|
179
|
+
"center",
|
|
180
|
+
"content",
|
|
181
|
+
"dir",
|
|
182
|
+
"font",
|
|
183
|
+
"frame",
|
|
184
|
+
"frameset",
|
|
185
|
+
"hgroup",
|
|
186
|
+
"image",
|
|
187
|
+
"keygen",
|
|
188
|
+
"marquee",
|
|
189
|
+
"menuitem",
|
|
190
|
+
"nobr",
|
|
191
|
+
"noembed",
|
|
192
|
+
"noframes",
|
|
193
|
+
"param",
|
|
194
|
+
"plaintext",
|
|
195
|
+
"rb",
|
|
196
|
+
"rtc",
|
|
197
|
+
"shadow",
|
|
198
|
+
"spacer",
|
|
199
|
+
"strike",
|
|
200
|
+
"tt",
|
|
201
|
+
"xmp",
|
|
202
|
+
]);
|
|
203
|
+
|
|
62
204
|
function elem(tag: string, as: string[], bs: (HTMLElement | string)[], cs: string[]): HTMLElement {
|
|
63
205
|
assert(as.length > 0);
|
|
64
206
|
assert(as[0][0] === '<' && as[as.length - 1].slice(-1) === '>');
|
|
65
207
|
assert(cs.length === 1);
|
|
66
208
|
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag <${tag}>`, as, bs, cs);
|
|
67
|
-
const attrs = attributes('html', [],
|
|
209
|
+
const attrs = attributes('html', [], attrspecs[tag], as.slice(1, -1));
|
|
68
210
|
return 'data-invalid-syntax' in attrs
|
|
69
211
|
? invalid('attribute', 'Invalid HTML attribute', as, bs, cs)
|
|
70
212
|
: h(tag as 'span', attrs, defrag(bs));
|
|
71
213
|
}
|
|
214
|
+
|
|
72
215
|
function invalid(type: string, message: string, as: (HTMLElement | string)[], bs: (HTMLElement | string)[], cs: (HTMLElement | string)[]): HTMLElement {
|
|
73
216
|
return h('span', {
|
|
74
217
|
class: 'invalid',
|
|
@@ -101,7 +244,7 @@ export function attributes(
|
|
|
101
244
|
? param.slice(name.length + 2, -1).replace(/\\(.?)/g, '$1')
|
|
102
245
|
: undefined;
|
|
103
246
|
invalid ||= !spec || name in attrs;
|
|
104
|
-
if (spec &&
|
|
247
|
+
if (spec && name in spec && !spec[name]) continue;
|
|
105
248
|
spec?.[name]?.includes(value) || value !== undefined && spec?.[name]?.length === 0
|
|
106
249
|
? attrs[name] = value ?? ''
|
|
107
250
|
: invalid ||= !!spec;
|
|
@@ -43,7 +43,7 @@ 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('**<
|
|
46
|
+
assert.deepStrictEqual(inspect(parser('**<bdi>**')), [['<strong><bdi></strong>'], '']);
|
|
47
47
|
assert.deepStrictEqual(inspect(parser('**(*a*)**')), [['<strong><span class="paren">(<em>a</em>)</span></strong>'], '']);
|
|
48
48
|
assert.deepStrictEqual(inspect(parser('**(**a**)**')), [['<strong><span class="paren">(<strong>a</strong>)</span></strong>'], '']);
|
|
49
49
|
});
|
|
@@ -99,8 +99,8 @@ describe('Unit: parser/inline', () => {
|
|
|
99
99
|
assert.deepStrictEqual(inspect(parser('*++ ++*')), [['<em><ins> </ins></em>'], '']);
|
|
100
100
|
assert.deepStrictEqual(inspect(parser('*++ a ++*')), [['<em><ins> a </ins></em>'], '']);
|
|
101
101
|
assert.deepStrictEqual(inspect(parser('*++ a ++*')), [['<em><ins> a </ins></em>'], '']);
|
|
102
|
-
assert.deepStrictEqual(inspect(parser('*<
|
|
103
|
-
assert.deepStrictEqual(inspect(parser('<
|
|
102
|
+
assert.deepStrictEqual(inspect(parser('*<bdi>`a`</bdi>*')), [['<em><bdi><code data-src="`a`">a</code></bdi></em>'], '']);
|
|
103
|
+
assert.deepStrictEqual(inspect(parser('<bdi>*<bdi>a</bdi>*</bdi>')), [['<bdi><em><bdi>a</bdi></em></bdi>'], '']);
|
|
104
104
|
assert.deepStrictEqual(inspect(parser('<bdi>((<bdi>((a))</bdi>))</bdi>')), [['<bdi><sup class="annotation"><span><bdi><span class="paren">((a))</span></bdi></span></sup></bdi>'], '']);
|
|
105
105
|
assert.deepStrictEqual(inspect(parser('<bdi>[[<bdi>[[a]]</bdi>]]</bdi>')), [['<bdi><sup class="reference"><span><bdi>[[a]]</bdi></span></sup></bdi>'], '']);
|
|
106
106
|
assert.deepStrictEqual(inspect(parser('*[*]')), [['*', '[', '*', ']'], '']);
|
|
@@ -155,9 +155,8 @@ 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('<<
|
|
159
|
-
assert.deepStrictEqual(inspect(parser('
|
|
160
|
-
assert.deepStrictEqual(inspect(parser('*<small>*`</small>`')), [['<em><small></em>', '<code data-src="`</small>`"></small></code>'], '']);
|
|
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>'], '']);
|
|
161
160
|
assert.deepStrictEqual(inspect(parser('[~http://host')), [['[', '~', '<a href="http://host" target="_blank">http://host</a>'], '']);
|
|
162
161
|
assert.deepStrictEqual(inspect(parser('[~a@b')), [['[', '~', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
|
|
163
162
|
assert.deepStrictEqual(inspect(parser('[~~a~~]')), [['[', '<del>a</del>', ']'], '']);
|