securemark 0.266.0 → 0.267.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/dist/index.js +29 -28
- package/index.d.ts +7 -8
- package/markdown.d.ts +4 -4
- package/package.json +1 -1
- package/src/parser/api/bind.ts +1 -1
- package/src/parser/api/parse.test.ts +0 -2
- package/src/parser/api/parse.ts +1 -1
- package/src/parser/block/pagebreak.test.ts +31 -0
- package/src/parser/block/pagebreak.ts +7 -0
- package/src/parser/block.ts +3 -3
- package/src/parser/inline/autolink/account.ts +3 -5
- package/src/parser/inline/autolink/anchor.test.ts +2 -2
- package/src/parser/inline/autolink/anchor.ts +4 -3
- package/src/parser/inline/autolink/channel.test.ts +0 -2
- package/src/parser/inline/autolink/channel.ts +0 -1
- package/src/parser/inline/autolink/hashnum.test.ts +1 -1
- package/src/parser/inline/autolink/hashtag.test.ts +2 -6
- package/src/parser/inline/autolink/hashtag.ts +7 -20
- package/src/parser/inline/extension/indexee.ts +1 -0
- package/src/parser/inline/link.ts +1 -0
- package/src/parser/inline.test.ts +5 -2
- package/src/parser/block/horizontalrule.test.ts +0 -31
- package/src/parser/block/horizontalrule.ts +0 -7
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ https://falsandtru.github.io/securemark/
|
|
|
50
50
|
- Table (| |)
|
|
51
51
|
- Blockquote (>, !>)
|
|
52
52
|
- Preformattedtext (```)
|
|
53
|
-
-
|
|
53
|
+
- Pagebreak (===)
|
|
54
54
|
- Inline markups (_, *, `, []{}, {}, ![]{}, !{}, \[](), ++, ~~, (()), ...)
|
|
55
55
|
- Inline HTML tags (\<bdi>, \<bdo>)
|
|
56
56
|
- Autolink (https://host, user@host, @user)
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! securemark v0.
|
|
1
|
+
/*! securemark v0.267.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("Prism"), require("DOMPurify"));
|
|
@@ -3908,7 +3908,7 @@ function bind(target, settings) {
|
|
|
3908
3908
|
})
|
|
3909
3909
|
};
|
|
3910
3910
|
|
|
3911
|
-
if (context.id?.
|
|
3911
|
+
if (context.id?.match(/[^0-9a-z-]/i)) throw new Error('Invalid ID: ID must be alphanumeric');
|
|
3912
3912
|
if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
|
|
3913
3913
|
const blocks = [];
|
|
3914
3914
|
const adds = [];
|
|
@@ -4226,7 +4226,6 @@ const url_1 = __webpack_require__(2261);
|
|
|
4226
4226
|
const dom_1 = __webpack_require__(3252);
|
|
4227
4227
|
function parse(source, opts = {}, context) {
|
|
4228
4228
|
if (!(0, segment_1.validate)(source, segment_1.MAX_SEGMENT_SIZE)) throw new Error(`Too large input over ${segment_1.MAX_SEGMENT_SIZE.toLocaleString('en')} bytes`);
|
|
4229
|
-
if (context?.id?.includes(':')) throw new Error('ID must not contain ":"');
|
|
4230
4229
|
const url = (0, header_2.headers)(source).find(field => field.toLowerCase().startsWith('url:'))?.slice(4).trim() ?? '';
|
|
4231
4230
|
source = !context ? (0, normalize_1.normalize)(source) : source;
|
|
4232
4231
|
context = {
|
|
@@ -4242,6 +4241,7 @@ function parse(source, opts = {}, context) {
|
|
|
4242
4241
|
})
|
|
4243
4242
|
};
|
|
4244
4243
|
|
|
4244
|
+
if (context.id?.match(/[^0-9a-z-]/i)) throw new Error('Invalid ID: ID must be alphanumeric');
|
|
4245
4245
|
if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
|
|
4246
4246
|
const node = (0, dom_1.frag)();
|
|
4247
4247
|
let index = 0;
|
|
@@ -4314,7 +4314,7 @@ Object.defineProperty(exports, "__esModule", ({
|
|
|
4314
4314
|
exports.block = void 0;
|
|
4315
4315
|
const combinator_1 = __webpack_require__(2087);
|
|
4316
4316
|
const source_1 = __webpack_require__(6743);
|
|
4317
|
-
const
|
|
4317
|
+
const pagebreak_1 = __webpack_require__(4107);
|
|
4318
4318
|
const heading_1 = __webpack_require__(4623);
|
|
4319
4319
|
const ulist_1 = __webpack_require__(5425);
|
|
4320
4320
|
const olist_1 = __webpack_require__(7471);
|
|
@@ -4335,7 +4335,7 @@ exports.block = (0, combinator_1.creation)(1, false, error((0, combinator_1.rese
|
|
|
4335
4335
|
clock: 50 * 1000,
|
|
4336
4336
|
recursion: 20
|
|
4337
4337
|
}
|
|
4338
|
-
}, (0, combinator_1.union)([source_1.emptyline,
|
|
4338
|
+
}, (0, combinator_1.union)([source_1.emptyline, pagebreak_1.pagebreak, 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]))));
|
|
4339
4339
|
function error(parser) {
|
|
4340
4340
|
return (0, combinator_1.recover)((0, combinator_1.fallback)((0, combinator_1.open)('\x07', ({
|
|
4341
4341
|
source
|
|
@@ -5168,22 +5168,6 @@ exports.heading = (0, combinator_1.block)((0, combinator_1.rewrite)(exports.segm
|
|
|
5168
5168
|
|
|
5169
5169
|
/***/ }),
|
|
5170
5170
|
|
|
5171
|
-
/***/ 9967:
|
|
5172
|
-
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
5173
|
-
|
|
5174
|
-
"use strict";
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
Object.defineProperty(exports, "__esModule", ({
|
|
5178
|
-
value: true
|
|
5179
|
-
}));
|
|
5180
|
-
exports.horizontalrule = void 0;
|
|
5181
|
-
const combinator_1 = __webpack_require__(2087);
|
|
5182
|
-
const dom_1 = __webpack_require__(3252);
|
|
5183
|
-
exports.horizontalrule = (0, combinator_1.block)((0, combinator_1.line)((0, combinator_1.focus)(/^-{3,}[^\S\n]*(?:$|\n)/, () => [[(0, dom_1.html)('hr')], ''])));
|
|
5184
|
-
|
|
5185
|
-
/***/ }),
|
|
5186
|
-
|
|
5187
5171
|
/***/ 238:
|
|
5188
5172
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
5189
5173
|
|
|
@@ -5356,6 +5340,22 @@ function format(el, type, form) {
|
|
|
5356
5340
|
|
|
5357
5341
|
/***/ }),
|
|
5358
5342
|
|
|
5343
|
+
/***/ 4107:
|
|
5344
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
5345
|
+
|
|
5346
|
+
"use strict";
|
|
5347
|
+
|
|
5348
|
+
|
|
5349
|
+
Object.defineProperty(exports, "__esModule", ({
|
|
5350
|
+
value: true
|
|
5351
|
+
}));
|
|
5352
|
+
exports.pagebreak = void 0;
|
|
5353
|
+
const combinator_1 = __webpack_require__(2087);
|
|
5354
|
+
const dom_1 = __webpack_require__(3252);
|
|
5355
|
+
exports.pagebreak = (0, combinator_1.block)((0, combinator_1.line)((0, combinator_1.focus)(/^={3,}[^\S\n]*(?:$|\n)/, () => [[(0, dom_1.html)('hr')], ''])));
|
|
5356
|
+
|
|
5357
|
+
/***/ }),
|
|
5358
|
+
|
|
5359
5359
|
/***/ 6457:
|
|
5360
5360
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
5361
5361
|
|
|
@@ -5805,7 +5805,7 @@ const link_1 = __webpack_require__(9628);
|
|
|
5805
5805
|
const source_1 = __webpack_require__(6743);
|
|
5806
5806
|
const dom_1 = __webpack_require__(3252);
|
|
5807
5807
|
// https://example/@user must be a user page or a redirect page going there.
|
|
5808
|
-
exports.account = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.constraint)(1 /* State.shortcut */, false, (0, combinator_1.open)('@', (0, combinator_1.tails)([(0,
|
|
5808
|
+
exports.account = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.constraint)(1 /* State.shortcut */, false, (0, combinator_1.open)('@', (0, combinator_1.tails)([(0, source_1.str)(/^[0-9a-z](?:(?:[0-9a-z]|-(?=\w)){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=\w)){0,61}[0-9a-z])?)*\//i), (0, source_1.str)(/^[a-z][0-9a-z]*(?:-[0-9a-z]+)*/i)]))), (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/@')}` : `/${source}`} }`, (0, combinator_1.union)([link_1.unsafelink]))), ([el]) => [(0, dom_1.define)(el, {
|
|
5809
5809
|
class: 'account'
|
|
5810
5810
|
})]));
|
|
5811
5811
|
|
|
@@ -5826,11 +5826,12 @@ const link_1 = __webpack_require__(9628);
|
|
|
5826
5826
|
const dom_1 = __webpack_require__(3252);
|
|
5827
5827
|
// Timeline(pseudonym): user/tid
|
|
5828
5828
|
// Thread(anonymous): cid
|
|
5829
|
-
//
|
|
5830
|
-
//
|
|
5829
|
+
// UTC
|
|
5830
|
+
// tid: YYYY-MMDD-HHMM-SS
|
|
5831
|
+
// cid: YYYY-MMDD-HHMM-SSmmm
|
|
5831
5832
|
// 内部表現はUnixTimeに統一する(時系列順)
|
|
5832
5833
|
// 外部表現は投稿ごとに投稿者の投稿時のタイムゾーンに統一する(非時系列順)
|
|
5833
|
-
exports.anchor = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('>>', (0, combinator_1.fmap)((0, combinator_1.constraint)(1 /* State.shortcut */, false, (0, combinator_1.focus)(/^>>(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*\/)?[0-9a-z]+(?:-[0-9a-z]+)*(?![0-9a-z@#:])/i, (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `/@${source.slice(2).replace('/', '/timeline
|
|
5834
|
+
exports.anchor = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('>>', (0, combinator_1.fmap)((0, combinator_1.constraint)(1 /* State.shortcut */, false, (0, combinator_1.focus)(/^>>(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*\/)?[0-9a-z]+(?:-[0-9a-z]+)*(?![0-9a-z@#:])/i, (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `/@${source.slice(2).replace('/', '/timeline?at=')}` : `?at=${source.slice(2)}`} }`, (0, combinator_1.union)([link_1.unsafelink])))), ([el]) => [(0, dom_1.define)(el, {
|
|
5834
5835
|
class: 'anchor'
|
|
5835
5836
|
})])));
|
|
5836
5837
|
|
|
@@ -5854,7 +5855,6 @@ const dom_1 = __webpack_require__(3252);
|
|
|
5854
5855
|
// https://example/@user?ch=a+b must be a user channel page or a redirect page going there.
|
|
5855
5856
|
exports.channel = (0, combinator_1.validate)('@', (0, combinator_1.bind)((0, combinator_1.sequence)([account_1.account, (0, combinator_1.some)(hashtag_1.hashtag)]), (es, rest) => {
|
|
5856
5857
|
const source = (0, util_1.stringify)(es);
|
|
5857
|
-
if (source.includes('/', source.indexOf('#'))) return;
|
|
5858
5858
|
const el = es[0];
|
|
5859
5859
|
const url = `${el.getAttribute('href')}?ch=${source.slice(source.indexOf('#') + 1).replace(/#/g, '+')}`;
|
|
5860
5860
|
return [[(0, dom_1.define)(el, {
|
|
@@ -5927,9 +5927,9 @@ const dom_1 = __webpack_require__(3252);
|
|
|
5927
5927
|
// https://example/hashtags/a must be a hashtag page or a redirect page going there.
|
|
5928
5928
|
// https://github.com/tc39/proposal-regexp-unicode-property-escapes#matching-emoji
|
|
5929
5929
|
exports.emoji = String.raw`\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F`;
|
|
5930
|
-
exports.hashtag = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.constraint)(1 /* State.shortcut */, false, (0, combinator_1.open)('#', (0,
|
|
5930
|
+
exports.hashtag = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.constraint)(1 /* State.shortcut */, false, (0, combinator_1.open)('#', (0, source_1.str)(new RegExp([/^(?=(?:[0-9]{1,15})?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'))/u.source, /(?:[^\p{C}\p{S}\p{P}\s]|emoji|(?<!')'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|'))+/u.source].join('').replace(/emoji/g, exports.emoji), 'u')))), (0, combinator_1.convert)(source => `[${source}]{ ${`/hashtags/${source.slice(1)}`} }`, (0, combinator_1.union)([link_1.unsafelink]))), ([el]) => [(0, dom_1.define)(el, {
|
|
5931
5931
|
class: 'hashtag'
|
|
5932
|
-
}
|
|
5932
|
+
})]));
|
|
5933
5933
|
|
|
5934
5934
|
/***/ }),
|
|
5935
5935
|
|
|
@@ -6489,6 +6489,7 @@ function resolve(uri, host, source) {
|
|
|
6489
6489
|
case uri.slice(0, 2) === '^/':
|
|
6490
6490
|
const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
|
|
6491
6491
|
return last.includes('.') // isFile
|
|
6492
|
+
// Exclude ISO 6709.
|
|
6492
6493
|
&& /^[0-9]*[a-z][0-9a-z]*$/i.test(last.slice(last.lastIndexOf('.') + 1)) ? `${host.pathname.slice(0, -last.length)}${uri.slice(2)}` : `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
|
|
6493
6494
|
case host.origin === source.origin && host.pathname === source.pathname:
|
|
6494
6495
|
case uri.slice(0, 2) === '//':
|
package/index.d.ts
CHANGED
|
@@ -28,8 +28,7 @@ export type ParserOptions = Omit<Partial<ParserSettings>, 'chunk'>;
|
|
|
28
28
|
export interface ParserSettings {
|
|
29
29
|
// Host URL.
|
|
30
30
|
readonly host?: URL;
|
|
31
|
-
// ID of comments and timelines.
|
|
32
|
-
// Must not contain ":".
|
|
31
|
+
// Alphanumeric ID of comments and timelines.
|
|
33
32
|
readonly id?: string;
|
|
34
33
|
// For editing.
|
|
35
34
|
readonly caches?: Partial<Caches>;
|
|
@@ -40,12 +39,12 @@ export interface ParserSettings {
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
export type Progress =
|
|
43
|
-
| { type: 'segment'
|
|
44
|
-
| { type: 'block'
|
|
45
|
-
| { type: 'figure'
|
|
46
|
-
| { type: 'footnote'
|
|
47
|
-
| { type: 'break' }
|
|
48
|
-
| { type: 'cancel' };
|
|
42
|
+
| { readonly type: 'segment'; readonly value: string; }
|
|
43
|
+
| { readonly type: 'block'; readonly value: HTMLElement; }
|
|
44
|
+
| { readonly type: 'figure'; readonly value: HTMLAnchorElement; }
|
|
45
|
+
| { readonly type: 'footnote'; readonly value: HTMLLIElement | HTMLElement; }
|
|
46
|
+
| { readonly type: 'break'; }
|
|
47
|
+
| { readonly type: 'cancel'; };
|
|
49
48
|
|
|
50
49
|
export interface RenderingOptions {
|
|
51
50
|
readonly code?: (target: HTMLElement, cache?: Dict<string, HTMLElement>) => void;
|
package/markdown.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export namespace MarkdownParser {
|
|
|
59
59
|
Markdown<'block'>,
|
|
60
60
|
Parser<HTMLElement, Context, [
|
|
61
61
|
SourceParser.EmptyLineParser,
|
|
62
|
-
BlockParser.
|
|
62
|
+
BlockParser.PagebreakParser,
|
|
63
63
|
BlockParser.HeadingParser,
|
|
64
64
|
BlockParser.UListParser,
|
|
65
65
|
BlockParser.OListParser,
|
|
@@ -77,9 +77,9 @@ export namespace MarkdownParser {
|
|
|
77
77
|
}
|
|
78
78
|
export namespace BlockParser {
|
|
79
79
|
interface Block<T extends string> extends Markdown<`block/${T}`> { }
|
|
80
|
-
export interface
|
|
81
|
-
//
|
|
82
|
-
Block<'
|
|
80
|
+
export interface PagebreakParser extends
|
|
81
|
+
// ===
|
|
82
|
+
Block<'pagebreak'>,
|
|
83
83
|
Parser<HTMLHRElement, Context, [
|
|
84
84
|
SourceParser.StrParser,
|
|
85
85
|
]> {
|
package/package.json
CHANGED
package/src/parser/api/bind.ts
CHANGED
|
@@ -26,7 +26,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
|
|
|
26
26
|
host: settings.host ?? new ReadonlyURL(location.pathname, location.origin),
|
|
27
27
|
memo: new Memo({ targets: State.backtrackers }),
|
|
28
28
|
};
|
|
29
|
-
if (context.id?.
|
|
29
|
+
if (context.id?.match(/[^0-9a-z-]/i)) throw new Error('Invalid ID: ID must be alphanumeric');
|
|
30
30
|
if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
|
|
31
31
|
assert(!settings.id);
|
|
32
32
|
type Block = readonly [segment: string, blocks: readonly HTMLElement[], url: string];
|
|
@@ -99,7 +99,6 @@ describe('Unit: parser/api/parse', () => {
|
|
|
99
99
|
'@a#b',
|
|
100
100
|
'@domain/a#b',
|
|
101
101
|
'#a',
|
|
102
|
-
'#domain/a',
|
|
103
102
|
'[#a]',
|
|
104
103
|
'$-a\n$$\n$$',
|
|
105
104
|
'$-a',
|
|
@@ -122,7 +121,6 @@ describe('Unit: parser/api/parse', () => {
|
|
|
122
121
|
'<p><a class="channel" href="https://source/@a?ch=b" target="_blank">@a#b</a></p>',
|
|
123
122
|
'<p><a class="channel" href="https://domain/@a?ch=b" target="_blank">@domain/a#b</a></p>',
|
|
124
123
|
'<p><a class="hashtag" href="https://source/hashtags/a" target="_blank">#a</a></p>',
|
|
125
|
-
'<p><a class="hashtag" href="https://domain/hashtags/a" target="_blank">#domain/a</a></p>',
|
|
126
124
|
'<p><a class="index" href="#index::a">a</a></p>',
|
|
127
125
|
'<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>',
|
|
128
126
|
'<p><a class="label" data-label="$-a" href="#label:$-a">(1)</a></p>',
|
package/src/parser/api/parse.ts
CHANGED
|
@@ -19,7 +19,6 @@ interface Options extends ParserOptions {
|
|
|
19
19
|
|
|
20
20
|
export function parse(source: string, opts: Options = {}, context?: MarkdownParser.Context): DocumentFragment {
|
|
21
21
|
if (!validate(source, MAX_SEGMENT_SIZE)) throw new Error(`Too large input over ${MAX_SEGMENT_SIZE.toLocaleString('en')} bytes`);
|
|
22
|
-
if (context?.id?.includes(':')) throw new Error('ID must not contain ":"');
|
|
23
22
|
const url = headers(source).find(field => field.toLowerCase().startsWith('url:'))?.slice(4).trim() ?? '';
|
|
24
23
|
source = !context ? normalize(source) : source;
|
|
25
24
|
assert(!context?.delimiters);
|
|
@@ -33,6 +32,7 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
|
|
|
33
32
|
},
|
|
34
33
|
memo: new Memo({ targets: State.backtrackers }),
|
|
35
34
|
};
|
|
35
|
+
if (context.id?.match(/[^0-9a-z-]/i)) throw new Error('Invalid ID: ID must be alphanumeric');
|
|
36
36
|
if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
|
|
37
37
|
const node = frag();
|
|
38
38
|
let index = 0;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { pagebreak } from './pagebreak';
|
|
2
|
+
import { some } from '../../combinator';
|
|
3
|
+
import { inspect } from '../../debug.test';
|
|
4
|
+
|
|
5
|
+
describe('Unit: parser/block/pagebreak', () => {
|
|
6
|
+
describe('pagebreak', () => {
|
|
7
|
+
const parser = (source: string) => some(pagebreak)({ source, context: {} });
|
|
8
|
+
|
|
9
|
+
it('invalid', () => {
|
|
10
|
+
assert.deepStrictEqual(inspect(parser('')), undefined);
|
|
11
|
+
assert.deepStrictEqual(inspect(parser('\n')), undefined);
|
|
12
|
+
assert.deepStrictEqual(inspect(parser('=')), undefined);
|
|
13
|
+
assert.deepStrictEqual(inspect(parser('==')), undefined);
|
|
14
|
+
assert.deepStrictEqual(inspect(parser('==\n=')), undefined);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser('===a')), undefined);
|
|
16
|
+
assert.deepStrictEqual(inspect(parser('===\na')), undefined);
|
|
17
|
+
assert.deepStrictEqual(inspect(parser('= = =')), undefined);
|
|
18
|
+
assert.deepStrictEqual(inspect(parser(' ===')), undefined);
|
|
19
|
+
assert.deepStrictEqual(inspect(parser('---')), undefined);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('valid', () => {
|
|
23
|
+
assert.deepStrictEqual(inspect(parser('===')), [['<hr>'], '']);
|
|
24
|
+
assert.deepStrictEqual(inspect(parser('=== ')), [['<hr>'], '']);
|
|
25
|
+
assert.deepStrictEqual(inspect(parser('===\n')), [['<hr>'], '']);
|
|
26
|
+
assert.deepStrictEqual(inspect(parser('====')), [['<hr>'], '']);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
});
|
package/src/parser/block.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MarkdownParser } from '../../markdown';
|
|
2
2
|
import { union, reset, creation, open, fallback, recover } from '../combinator';
|
|
3
3
|
import { emptyline } from './source';
|
|
4
|
-
import {
|
|
4
|
+
import { pagebreak } from './block/pagebreak';
|
|
5
5
|
import { heading } from './block/heading';
|
|
6
6
|
import { ulist } from './block/ulist';
|
|
7
7
|
import { olist } from './block/olist';
|
|
@@ -19,7 +19,7 @@ import { rnd0Z } from 'spica/random';
|
|
|
19
19
|
import { html } from 'typed-dom/dom';
|
|
20
20
|
|
|
21
21
|
export import BlockParser = MarkdownParser.BlockParser;
|
|
22
|
-
export import
|
|
22
|
+
export import PagebreakParser = BlockParser.PagebreakParser;
|
|
23
23
|
export import HeadingParser = BlockParser.HeadingParser;
|
|
24
24
|
export import UListParser = BlockParser.UListParser;
|
|
25
25
|
export import OListParser = BlockParser.OListParser;
|
|
@@ -38,7 +38,7 @@ export const block: BlockParser = creation(1, false, error(
|
|
|
38
38
|
reset({ resources: { clock: 50 * 1000, recursion: 20 } },
|
|
39
39
|
union([
|
|
40
40
|
emptyline,
|
|
41
|
-
|
|
41
|
+
pagebreak,
|
|
42
42
|
heading,
|
|
43
43
|
ulist,
|
|
44
44
|
olist,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
|
-
import { union, constraint, tails,
|
|
2
|
+
import { union, constraint, tails, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
3
3
|
import { unsafelink } from '../link';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { State } from '../../context';
|
|
@@ -12,10 +12,8 @@ export const account: AutolinkParser.AccountParser = lazy(() => fmap(rewrite(
|
|
|
12
12
|
open(
|
|
13
13
|
'@',
|
|
14
14
|
tails([
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
([source]) => source.length <= 253 + 1),
|
|
18
|
-
str(/^[a-z](?:-(?=[0-9a-z])|[0-9a-z]){0,63}/i),
|
|
15
|
+
str(/^[0-9a-z](?:(?:[0-9a-z]|-(?=\w)){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=\w)){0,61}[0-9a-z])?)*\//i),
|
|
16
|
+
str(/^[a-z][0-9a-z]*(?:-[0-9a-z]+)*/i),
|
|
19
17
|
]))),
|
|
20
18
|
convert(
|
|
21
19
|
source =>
|
|
@@ -28,8 +28,8 @@ describe('Unit: parser/inline/autolink/anchor', () => {
|
|
|
28
28
|
assert.deepStrictEqual(inspect(parser('>>0-a')), [['<a class="anchor" href="?at=0-a">>>0-a</a>'], '']);
|
|
29
29
|
assert.deepStrictEqual(inspect(parser('>>0-A')), [['<a class="anchor" href="?at=0-A">>>0-A</a>'], '']);
|
|
30
30
|
assert.deepStrictEqual(inspect(parser('>>0--a')), [['<a class="anchor" href="?at=0">>>0</a>'], '--a']);
|
|
31
|
-
assert.deepStrictEqual(inspect(parser('>>2000-
|
|
32
|
-
assert.deepStrictEqual(inspect(parser('>>A/2000-
|
|
31
|
+
assert.deepStrictEqual(inspect(parser('>>2000-0131-2359-59999')), [['<a class="anchor" href="?at=2000-0131-2359-59999">>>2000-0131-2359-59999</a>'], '']);
|
|
32
|
+
assert.deepStrictEqual(inspect(parser('>>A/2000-0131-2359-59')), [['<a class="anchor" href="/@A/timeline?at=2000-0131-2359-59">>>A/2000-0131-2359-59</a>'], '']);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
});
|
|
@@ -7,8 +7,9 @@ import { define } from 'typed-dom/dom';
|
|
|
7
7
|
// Timeline(pseudonym): user/tid
|
|
8
8
|
// Thread(anonymous): cid
|
|
9
9
|
|
|
10
|
-
//
|
|
11
|
-
//
|
|
10
|
+
// UTC
|
|
11
|
+
// tid: YYYY-MMDD-HHMM-SS
|
|
12
|
+
// cid: YYYY-MMDD-HHMM-SSmmm
|
|
12
13
|
|
|
13
14
|
// 内部表現はUnixTimeに統一する(時系列順)
|
|
14
15
|
// 外部表現は投稿ごとに投稿者の投稿時のタイムゾーンに統一する(非時系列順)
|
|
@@ -21,7 +22,7 @@ export const anchor: AutolinkParser.AnchorParser = lazy(() => validate('>>', fma
|
|
|
21
22
|
source =>
|
|
22
23
|
`[${source}]{ ${
|
|
23
24
|
source.includes('/')
|
|
24
|
-
? `/@${source.slice(2).replace('/', '/timeline
|
|
25
|
+
? `/@${source.slice(2).replace('/', '/timeline?at=')}`
|
|
25
26
|
: `?at=${source.slice(2)}`
|
|
26
27
|
} }`,
|
|
27
28
|
union([unsafelink])))),
|
|
@@ -16,8 +16,6 @@ describe('Unit: parser/inline/autolink/channel', () => {
|
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('@a#1@b')), [['@a#1@b'], '']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('@a#b#')), [['@a#b#'], '']);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('@a#b#1')), [['@a#b#1'], '']);
|
|
19
|
-
assert.deepStrictEqual(inspect(parser('@a#domain/b')), [['@a#domain/b'], '']);
|
|
20
|
-
assert.deepStrictEqual(inspect(parser('@domain/a#domain/b')), [['@domain/a#domain/b'], '']);
|
|
21
19
|
assert.deepStrictEqual(inspect(parser(' @a#b')), undefined);
|
|
22
20
|
});
|
|
23
21
|
|
|
@@ -14,7 +14,6 @@ export const channel: AutolinkParser.ChannelParser = validate('@', bind(
|
|
|
14
14
|
]),
|
|
15
15
|
(es, rest) => {
|
|
16
16
|
const source = stringify(es);
|
|
17
|
-
if (source.includes('/', source.indexOf('#'))) return;
|
|
18
17
|
const el = es[0];
|
|
19
18
|
const url = `${el.getAttribute('href')}?ch=${source.slice(source.indexOf('#') + 1).replace(/#/g, '+')}`;
|
|
20
19
|
return [[define(el, { class: 'channel', href: url }, source)], rest];
|
|
@@ -33,7 +33,7 @@ describe('Unit: parser/inline/autolink/hashnum', () => {
|
|
|
33
33
|
assert.deepStrictEqual(inspect(parser('あ#1')), [['あ#1'], '']);
|
|
34
34
|
assert.deepStrictEqual(inspect(parser(' #1')), undefined);
|
|
35
35
|
assert.deepStrictEqual(inspect(parser('#12345678901234567')), [['#12345678901234567'], '']);
|
|
36
|
-
assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(
|
|
36
|
+
assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(16)}a`)), [[`#${'1'.repeat(16)}a`], '']);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it('valid', () => {
|
|
@@ -34,9 +34,6 @@ describe('Unit: parser/inline/autolink/hashtag', () => {
|
|
|
34
34
|
assert.deepStrictEqual(inspect(parser('a##1')), [['a##1'], '']);
|
|
35
35
|
assert.deepStrictEqual(inspect(parser('a##b')), [['a##b'], '']);
|
|
36
36
|
assert.deepStrictEqual(inspect(parser('あ#b')), [['あ#b'], '']);
|
|
37
|
-
assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(127)}_`)), [[`#${'1'.repeat(127)}`], '_']);
|
|
38
|
-
assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(127)}_a`)), [[`#${'1'.repeat(127)}`], '_a']);
|
|
39
|
-
assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(127)}_'a`)), [[`#${'1'.repeat(127)}`], `_'a`]);
|
|
40
37
|
assert.deepStrictEqual(inspect(parser(' #a')), undefined);
|
|
41
38
|
});
|
|
42
39
|
|
|
@@ -51,15 +48,13 @@ describe('Unit: parser/inline/autolink/hashtag', () => {
|
|
|
51
48
|
assert.deepStrictEqual(inspect(parser('#a(b')), [['<a class="hashtag" href="/hashtags/a">#a</a>'], '(b']);
|
|
52
49
|
assert.deepStrictEqual(inspect(parser('#a(b)')), [['<a class="hashtag" href="/hashtags/a">#a</a>'], '(b)']);
|
|
53
50
|
assert.deepStrictEqual(inspect(parser('#a_')), [['<a class="hashtag" href="/hashtags/a">#a</a>'], '_']);
|
|
54
|
-
assert.deepStrictEqual(inspect(parser('#a__b')), [['<a class="hashtag" href="/hashtags/a">#a</a>'], '__b']);
|
|
55
51
|
assert.deepStrictEqual(inspect(parser('#a_b')), [['<a class="hashtag" href="/hashtags/a_b">#a_b</a>'], '']);
|
|
52
|
+
assert.deepStrictEqual(inspect(parser('#a__b')), [['<a class="hashtag" href="/hashtags/a">#a</a>'], '__b']);
|
|
56
53
|
assert.deepStrictEqual(inspect(parser('#あ')), [['<a class="hashtag" href="/hashtags/あ">#あ</a>'], '']);
|
|
57
54
|
assert.deepStrictEqual(inspect(parser('#👩')), [['<a class="hashtag" href="/hashtags/👩">#👩</a>'], '']);
|
|
58
55
|
assert.deepStrictEqual(inspect(parser('#1a')), [['<a class="hashtag" href="/hashtags/1a">#1a</a>'], '']);
|
|
59
56
|
assert.deepStrictEqual(inspect(parser('#1あ')), [['<a class="hashtag" href="/hashtags/1あ">#1あ</a>'], '']);
|
|
60
57
|
assert.deepStrictEqual(inspect(parser('#1👩')), [['<a class="hashtag" href="/hashtags/1👩">#1👩</a>'], '']);
|
|
61
|
-
assert.deepStrictEqual(inspect(parser('#domain/a')), [['<a class="hashtag" href="https://domain/hashtags/a" target="_blank">#domain/a</a>'], '']);
|
|
62
|
-
assert.deepStrictEqual(inspect(parser('#domain.co.jp/a')), [['<a class="hashtag" href="https://domain.co.jp/hashtags/a" target="_blank">#domain.co.jp/a</a>'], '']);
|
|
63
58
|
assert.deepStrictEqual(inspect(parser(`#'0`)), [[`<a class="hashtag" href="/hashtags/'0">#'0</a>`], '']);
|
|
64
59
|
assert.deepStrictEqual(inspect(parser(`#'00`)), [[`<a class="hashtag" href="/hashtags/'00">#'00</a>`], '']);
|
|
65
60
|
assert.deepStrictEqual(inspect(parser(`#1'`)), [[`<a class="hashtag" href="/hashtags/1'">#1'</a>`], '']);
|
|
@@ -69,6 +64,7 @@ describe('Unit: parser/inline/autolink/hashtag', () => {
|
|
|
69
64
|
assert.deepStrictEqual(inspect(parser(`#a'b`)), [[`<a class="hashtag" href="/hashtags/a'b">#a'b</a>`], '']);
|
|
70
65
|
assert.deepStrictEqual(inspect(parser(`#a'_b`)), [[`<a class="hashtag" href="/hashtags/a'_b">#a'_b</a>`], '']);
|
|
71
66
|
assert.deepStrictEqual(inspect(parser(`#a_'b`)), [[`<a class="hashtag" href="/hashtags/a_'b">#a_'b</a>`], '']);
|
|
67
|
+
assert.deepStrictEqual(inspect(parser(`#${'1'.repeat(15)}a`)), [[`<a class="hashtag" href="/hashtags/${'1'.repeat(15)}a">#${'1'.repeat(15)}a</a>`], '']);
|
|
72
68
|
});
|
|
73
69
|
|
|
74
70
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
|
-
import { union,
|
|
2
|
+
import { union, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
|
|
3
3
|
import { unsafelink } from '../link';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { State } from '../../context';
|
|
@@ -14,24 +14,11 @@ export const hashtag: AutolinkParser.HashtagParser = lazy(() => fmap(rewrite(
|
|
|
14
14
|
constraint(State.shortcut, false,
|
|
15
15
|
open(
|
|
16
16
|
'#',
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
verify(
|
|
22
|
-
str(new RegExp([
|
|
23
|
-
/^(?=(?:[0-9]{1,127}_?)?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'))/u.source,
|
|
24
|
-
/(?:[^\p{C}\p{S}\p{P}\s]|emoji|(?<!')'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|')){1,128}/u.source,
|
|
25
|
-
/(?!_?(?:[^\p{C}\p{S}\p{P}\s]|emoji|(?<!')'))/u.source,
|
|
26
|
-
].join('').replace(/emoji/g, emoji), 'u')),
|
|
27
|
-
([source]) => source.length <= 128),
|
|
28
|
-
]))),
|
|
17
|
+
str(new RegExp([
|
|
18
|
+
/^(?=(?:[0-9]{1,15})?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'))/u.source,
|
|
19
|
+
/(?:[^\p{C}\p{S}\p{P}\s]|emoji|(?<!')'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|'))+/u.source,
|
|
20
|
+
].join('').replace(/emoji/g, emoji), 'u')))),
|
|
29
21
|
convert(
|
|
30
|
-
source =>
|
|
31
|
-
`[${source}]{ ${
|
|
32
|
-
source.includes('/')
|
|
33
|
-
? `https://${source.slice(1).replace('/', '/hashtags/')}`
|
|
34
|
-
: `/hashtags/${source.slice(1)}`
|
|
35
|
-
} }`,
|
|
22
|
+
source => `[${source}]{ ${`/hashtags/${source.slice(1)}`} }`,
|
|
36
23
|
union([unsafelink]))),
|
|
37
|
-
([el]) => [define(el, { class: 'hashtag' }
|
|
24
|
+
([el]) => [define(el, { class: 'hashtag' })]));
|
|
@@ -9,6 +9,7 @@ export function indexee(parser: Parser<HTMLElement, MarkdownParser.Context>, opt
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function identity(id: string | undefined, text: string, name: 'index' | 'mark' = 'index'): string | undefined {
|
|
12
|
+
assert(!id?.match(/[^0-9a-z-]/i));
|
|
12
13
|
assert(!text.includes('\n'));
|
|
13
14
|
if (id === '') return undefined;
|
|
14
15
|
text &&= text.trim().replace(/\s+/g, '_');
|
|
@@ -180,6 +180,7 @@ export function resolve(uri: string, host: URL | Location, source: URL | Locatio
|
|
|
180
180
|
case uri.slice(0, 2) === '^/':
|
|
181
181
|
const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
|
|
182
182
|
return last.includes('.') // isFile
|
|
183
|
+
// Exclude ISO 6709.
|
|
183
184
|
&& /^[0-9]*[a-z][0-9a-z]*$/i.test(last.slice(last.lastIndexOf('.') + 1))
|
|
184
185
|
? `${host.pathname.slice(0, -last.length)}${uri.slice(2)}`
|
|
185
186
|
: `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
|
|
@@ -122,6 +122,7 @@ describe('Unit: parser/inline', () => {
|
|
|
122
122
|
assert.deepStrictEqual(inspect(parser('a@b')), [['<a class="email" href="mailto:a@b">a@b</a>'], '']);
|
|
123
123
|
assert.deepStrictEqual(inspect(parser('_a@b')), [['_', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
|
|
124
124
|
assert.deepStrictEqual(inspect(parser('_a@b_')), [['<em><a class="email" href="mailto:a@b">a@b</a></em>'], '']);
|
|
125
|
+
assert.deepStrictEqual(inspect(parser('_a_b@c_')), [['<em><a class="email" href="mailto:a_b@c">a_b@c</a></em>'], '']);
|
|
125
126
|
assert.deepStrictEqual(inspect(parser('*a@b*')), [['<strong><a class="email" href="mailto:a@b">a@b</a></strong>'], '']);
|
|
126
127
|
assert.deepStrictEqual(inspect(parser('(a@b)')), [['<span class="paren">(<a class="email" href="mailto:a@b">a@b</a>)</span>'], '']);
|
|
127
128
|
assert.deepStrictEqual(inspect(parser(' a@b')), [[' ', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
|
|
@@ -130,9 +131,7 @@ describe('Unit: parser/inline', () => {
|
|
|
130
131
|
|
|
131
132
|
it('channel', () => {
|
|
132
133
|
assert.deepStrictEqual(inspect(parser('@a#b')), [['<a class="channel" href="/@a?ch=b">@a#b</a>'], '']);
|
|
133
|
-
assert.deepStrictEqual(inspect(parser('@a#domain/b')), [['@a#domain/b'], '']);
|
|
134
134
|
assert.deepStrictEqual(inspect(parser('@domain/a#b')), [['<a class="channel" href="https://domain/@a?ch=b" target="_blank">@domain/a#b</a>'], '']);
|
|
135
|
-
assert.deepStrictEqual(inspect(parser('@domain/a#domain/b')), [['@domain/a#domain/b'], '']);
|
|
136
135
|
assert.deepStrictEqual(inspect(parser('_@a#b')), [['_', '<a class="channel" href="/@a?ch=b">@a#b</a>'], '']);
|
|
137
136
|
assert.deepStrictEqual(inspect(parser(' @a#b')), [[' ', '<a class="channel" href="/@a?ch=b">@a#b</a>'], '']);
|
|
138
137
|
});
|
|
@@ -152,6 +151,8 @@ describe('Unit: parser/inline', () => {
|
|
|
152
151
|
assert.deepStrictEqual(inspect(parser('#a')), [['<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
|
|
153
152
|
assert.deepStrictEqual(inspect(parser('#a\nb\n#c\n[#d]')), [['<a class="hashtag" href="/hashtags/a">#a</a>', '<br>', 'b', '<br>', '<a class="hashtag" href="/hashtags/c">#c</a>', '<br>', '<a class="index" href="#index::d">d</a>'], '']);
|
|
154
153
|
assert.deepStrictEqual(inspect(parser('##a')), [['##a'], '']);
|
|
154
|
+
assert.deepStrictEqual(inspect(parser('_#a')), [['_', '<a class="hashtag" href="/hashtags/a">#a</a>'], '']);
|
|
155
|
+
assert.deepStrictEqual(inspect(parser('_#a_')), [['<em><a class="hashtag" href="/hashtags/a">#a</a></em>'], '']);
|
|
155
156
|
assert.deepStrictEqual(inspect(parser('a#b')), [['a#b'], '']);
|
|
156
157
|
assert.deepStrictEqual(inspect(parser('0a#b')), [['0a#b'], '']);
|
|
157
158
|
assert.deepStrictEqual(inspect(parser('あ#b')), [['あ#b'], '']);
|
|
@@ -173,6 +174,8 @@ describe('Unit: parser/inline', () => {
|
|
|
173
174
|
it('hashnum', () => {
|
|
174
175
|
assert.deepStrictEqual(inspect(parser('#1')), [['<a class="hashnum">#1</a>'], '']);
|
|
175
176
|
assert.deepStrictEqual(inspect(parser('#12345678901234567@a')), [['#12345678901234567@a'], '']);
|
|
177
|
+
assert.deepStrictEqual(inspect(parser('_#1_')), [['<em><a class="hashnum">#1</a></em>'], '']);
|
|
178
|
+
assert.deepStrictEqual(inspect(parser('_#1_0')), [['<em><a class="hashnum">#1</a></em>', '0'], '']);
|
|
176
179
|
assert.deepStrictEqual(inspect(parser('「#1」')), [['「', '<a class="hashnum">#1</a>', '」'], '']);
|
|
177
180
|
});
|
|
178
181
|
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { horizontalrule } from './horizontalrule';
|
|
2
|
-
import { some } from '../../combinator';
|
|
3
|
-
import { inspect } from '../../debug.test';
|
|
4
|
-
|
|
5
|
-
describe('Unit: parser/block/horizontalrule', () => {
|
|
6
|
-
describe('horizontalrule', () => {
|
|
7
|
-
const parser = (source: string) => some(horizontalrule)({ source, context: {} });
|
|
8
|
-
|
|
9
|
-
it('invalid', () => {
|
|
10
|
-
assert.deepStrictEqual(inspect(parser('')), undefined);
|
|
11
|
-
assert.deepStrictEqual(inspect(parser('\n')), undefined);
|
|
12
|
-
assert.deepStrictEqual(inspect(parser('-')), undefined);
|
|
13
|
-
assert.deepStrictEqual(inspect(parser('--')), undefined);
|
|
14
|
-
assert.deepStrictEqual(inspect(parser('--\n-')), undefined);
|
|
15
|
-
assert.deepStrictEqual(inspect(parser('---a')), undefined);
|
|
16
|
-
assert.deepStrictEqual(inspect(parser('---\na')), undefined);
|
|
17
|
-
assert.deepStrictEqual(inspect(parser('- - -')), undefined);
|
|
18
|
-
assert.deepStrictEqual(inspect(parser(' ---')), undefined);
|
|
19
|
-
assert.deepStrictEqual(inspect(parser('***')), undefined);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('valid', () => {
|
|
23
|
-
assert.deepStrictEqual(inspect(parser('---')), [['<hr>'], '']);
|
|
24
|
-
assert.deepStrictEqual(inspect(parser('--- ')), [['<hr>'], '']);
|
|
25
|
-
assert.deepStrictEqual(inspect(parser('---\n')), [['<hr>'], '']);
|
|
26
|
-
assert.deepStrictEqual(inspect(parser('----')), [['<hr>'], '']);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
});
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { HorizontalRuleParser } from '../block';
|
|
2
|
-
import { block, line, focus } from '../../combinator';
|
|
3
|
-
import { html } from 'typed-dom/dom';
|
|
4
|
-
|
|
5
|
-
export const horizontalrule: HorizontalRuleParser = block(line(focus(
|
|
6
|
-
/^-{3,}[^\S\n]*(?:$|\n)/,
|
|
7
|
-
() => [[html('hr')], ''])));
|