shelving 1.110.0 → 1.110.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/markup/render.js +15 -20
- package/markup/rule.d.ts +27 -53
- package/markup/rule.js +44 -74
- package/markup/rules.d.ts +76 -39
- package/markup/rules.js +32 -26
- package/package.json +1 -1
- package/util/regexp.d.ts +20 -12
package/markup/render.js
CHANGED
|
@@ -66,8 +66,7 @@ function _renderNode(node, options, context) {
|
|
|
66
66
|
* Render a JSX element in a given context.
|
|
67
67
|
*/
|
|
68
68
|
function _renderElement(element, options, context) {
|
|
69
|
-
|
|
70
|
-
element.props.children = _renderNode(element.props.children, options, context);
|
|
69
|
+
element.props.children = _renderNode(element.props.children, options, context);
|
|
71
70
|
return element;
|
|
72
71
|
}
|
|
73
72
|
/**
|
|
@@ -75,9 +74,8 @@ function _renderElement(element, options, context) {
|
|
|
75
74
|
*
|
|
76
75
|
* @param offset Keeps track of where we are within the wider string we're parsing when we're several calls deep.
|
|
77
76
|
*/
|
|
78
|
-
function* _parseString(input, options,
|
|
79
|
-
let
|
|
80
|
-
let matchedResult = undefined;
|
|
77
|
+
function* _parseString(input, options, outerContext, offset = 0) {
|
|
78
|
+
let element = undefined;
|
|
81
79
|
let highPriority = Number.MIN_SAFE_INTEGER;
|
|
82
80
|
let lowIndex = Number.MIN_SAFE_INTEGER;
|
|
83
81
|
// Loop through all rules in the list and see if any match.
|
|
@@ -85,15 +83,14 @@ function* _parseString(input, options, context, offset = 0) {
|
|
|
85
83
|
// Only apply this rule if both:
|
|
86
84
|
// 1. The priority is equal or higher to the current priority.
|
|
87
85
|
// 2. The rule is allowed in the current context.
|
|
88
|
-
const { priority } = rule;
|
|
89
|
-
if (
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
86
|
+
const { priority = 0, contexts } = rule;
|
|
87
|
+
if (priority >= highPriority && contexts.includes(outerContext)) {
|
|
88
|
+
const match = rule.match(input, options);
|
|
89
|
+
if (match) {
|
|
92
90
|
// Use the match if it has length and is earlier in the string or is higher priority.
|
|
93
|
-
const {
|
|
94
|
-
if (length && (index < lowIndex ||
|
|
95
|
-
|
|
96
|
-
matchedResult = result;
|
|
91
|
+
const { index, length } = match;
|
|
92
|
+
if (length && (index < lowIndex || priority > highPriority)) {
|
|
93
|
+
element = match;
|
|
97
94
|
highPriority = priority;
|
|
98
95
|
lowIndex = index;
|
|
99
96
|
}
|
|
@@ -101,24 +98,22 @@ function* _parseString(input, options, context, offset = 0) {
|
|
|
101
98
|
}
|
|
102
99
|
}
|
|
103
100
|
// Did at least one rule match?
|
|
104
|
-
if (
|
|
101
|
+
if (element) {
|
|
105
102
|
// If index is more than zero, then the string before the match may match another rule at lower priority.
|
|
106
103
|
const prefix = input.slice(0, lowIndex);
|
|
107
104
|
if (prefix.length)
|
|
108
|
-
yield* _parseString(prefix, options,
|
|
105
|
+
yield* _parseString(prefix, options, outerContext, offset);
|
|
109
106
|
// Call the rule's `render()` function to generate the node.
|
|
110
107
|
// React gets annoyed if we don't set a `key:` property on lists of elements.
|
|
111
108
|
// We use the string offset as the `.key` property in the element because it's cheap to calculate and guaranteed to be unique within the string.
|
|
112
109
|
// Trying to generate an incrementing number would require tracking the number and passing it back and forth through `_parseString()`
|
|
113
|
-
const {
|
|
114
|
-
const element = matchedRule.render(groups, options);
|
|
110
|
+
const { index, length, context } = element;
|
|
115
111
|
element.key = offset + index;
|
|
116
|
-
|
|
117
|
-
yield subcontext ? _renderElement(element, options, subcontext) : element;
|
|
112
|
+
yield context ? _renderElement(element, options, context) : element;
|
|
118
113
|
// Decrement the content.
|
|
119
114
|
const suffix = input.slice(index + length);
|
|
120
115
|
if (suffix.length)
|
|
121
|
-
yield* _parseString(suffix, options,
|
|
116
|
+
yield* _parseString(suffix, options, outerContext, offset + index + length);
|
|
122
117
|
}
|
|
123
118
|
else {
|
|
124
119
|
// If nothing matched return the entire string..
|
package/markup/rule.d.ts
CHANGED
|
@@ -1,74 +1,48 @@
|
|
|
1
1
|
import type { MarkupOptions } from "./options.js";
|
|
2
2
|
import type { ImmutableArray } from "../util/array.js";
|
|
3
|
-
import type { Data } from "../util/data.js";
|
|
4
3
|
import type { JSXElement } from "../util/jsx.js";
|
|
5
|
-
import type { NamedRegExp,
|
|
6
|
-
export type
|
|
7
|
-
|
|
4
|
+
import type { NamedRegExp, NamedRegExpData, TypedRegExp, TypedRegExpExecArray } from "../util/regexp.js";
|
|
5
|
+
export type MarkupElement = {
|
|
6
|
+
/** String index where this element was matched in the input string. */
|
|
8
7
|
index: number;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
/** String length where this element was matched in the input string. */
|
|
9
|
+
length: number;
|
|
10
|
+
/** Context for this element's children in the input string, e.g. `"inline"` */
|
|
11
|
+
context?: string;
|
|
12
|
+
} & JSXElement;
|
|
13
|
+
export interface MarkupRule {
|
|
12
14
|
/**
|
|
13
15
|
* Contexts this rule should be applied in,
|
|
14
16
|
*
|
|
15
17
|
* @example `["block", "inline", "list"]`
|
|
16
18
|
*/
|
|
17
19
|
readonly contexts: ImmutableArray<string>;
|
|
18
|
-
/**
|
|
19
|
-
* Context that children should be rendered with.
|
|
20
|
-
*
|
|
21
|
-
* @example `"inline"` // Children of the element rendered by this rule will be parsed against markup rules applied in the "inline" context.
|
|
22
|
-
*/
|
|
23
|
-
readonly subcontext: string | null;
|
|
24
20
|
/**
|
|
25
21
|
* Priority for this rule (defaults to zero).
|
|
26
22
|
*
|
|
27
23
|
* @example e.g. `<p>` rule is lower priority than other blocks so it matches last and paragraphs can be interrupted by e.g. `<ul>` and `<blockquote>`.
|
|
28
24
|
* @example e.g. `<code>` rule is higher priority than other inlines so e.g. `<strong>` or `<em>` don't match inside a code block.
|
|
29
25
|
*/
|
|
30
|
-
readonly priority
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/** Match an input string against this */
|
|
34
|
-
abstract match(input: string, options: MarkupOptions): MarkupRuleMatch | null;
|
|
35
|
-
/** Render the JSX element for this rule using the props matched by `.match` */
|
|
36
|
-
abstract render(props: Data | undefined, options: MarkupOptions): JSXElement;
|
|
26
|
+
readonly priority?: number | undefined;
|
|
27
|
+
/** Match an input string against this and return an element if there was a match. */
|
|
28
|
+
match(input: string, options: MarkupOptions): MarkupElement | undefined;
|
|
37
29
|
}
|
|
38
|
-
export declare
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export declare class LinkRegExpMarkupRule extends MarkupRule {
|
|
53
|
-
readonly regexp: NamedRegExp<{
|
|
30
|
+
export declare function getMarkupRule<T extends string = string>(regexp: TypedRegExp<T>, //
|
|
31
|
+
render: (props: TypedRegExpExecArray<T>, options: MarkupOptions) => JSXElement, contexts: ImmutableArray<string>, priority?: number): MarkupRule & {
|
|
32
|
+
regexp: TypedRegExp<T>;
|
|
33
|
+
};
|
|
34
|
+
export declare function getNamedMarkupRule<T extends NamedRegExpData>(regexp: NamedRegExp<T>, //
|
|
35
|
+
render: (data: T, options: MarkupOptions) => JSXElement, contexts: ImmutableArray<string>, priority?: number): MarkupRule & {
|
|
36
|
+
regexp: NamedRegExp<T>;
|
|
37
|
+
};
|
|
38
|
+
export declare function getLinkMarkupRule(regexp: NamedRegExp<{
|
|
39
|
+
title?: string;
|
|
40
|
+
href: string;
|
|
41
|
+
}>, //
|
|
42
|
+
render: (title: string, href: string, options: MarkupOptions) => JSXElement, contexts: ImmutableArray<string>, priority?: number): MarkupRule & {
|
|
43
|
+
regexp: NamedRegExp<{
|
|
54
44
|
title?: string;
|
|
55
45
|
href: string;
|
|
56
46
|
}>;
|
|
57
|
-
|
|
58
|
-
title: string;
|
|
59
|
-
href: string;
|
|
60
|
-
}, options: MarkupOptions) => JSXElement;
|
|
61
|
-
constructor(regexp: NamedRegExp<{
|
|
62
|
-
title?: string;
|
|
63
|
-
href: string;
|
|
64
|
-
}>, //
|
|
65
|
-
render: (props: {
|
|
66
|
-
title: string;
|
|
67
|
-
href: string;
|
|
68
|
-
}, options: MarkupOptions) => JSXElement, contexts: ImmutableArray<string>, subcontext?: string | null, priority?: number);
|
|
69
|
-
match(input: string, { schemes, url: base }: MarkupOptions): MarkupRuleMatch<{
|
|
70
|
-
title: string;
|
|
71
|
-
href: string;
|
|
72
|
-
}> | null;
|
|
73
|
-
}
|
|
47
|
+
};
|
|
74
48
|
export type MarkupRules = MarkupRule[];
|
package/markup/rule.js
CHANGED
|
@@ -1,79 +1,49 @@
|
|
|
1
1
|
import { formatURL, getOptionalURL } from "../util/url.js";
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* Priority for this rule (defaults to zero).
|
|
17
|
-
*
|
|
18
|
-
* @example e.g. `<p>` rule is lower priority than other blocks so it matches last and paragraphs can be interrupted by e.g. `<ul>` and `<blockquote>`.
|
|
19
|
-
* @example e.g. `<code>` rule is higher priority than other inlines so e.g. `<strong>` or `<em>` don't match inside a code block.
|
|
20
|
-
*/
|
|
21
|
-
priority;
|
|
22
|
-
constructor(contexts, //
|
|
23
|
-
subcontext = null, priority = 0) {
|
|
24
|
-
this.contexts = contexts;
|
|
25
|
-
this.subcontext = subcontext;
|
|
26
|
-
this.priority = priority;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export class RegExpMarkupRule extends MarkupRule {
|
|
30
|
-
regexp;
|
|
31
|
-
render;
|
|
32
|
-
constructor(regexp, //
|
|
33
|
-
render, contexts, subcontext, priority) {
|
|
34
|
-
super(contexts, subcontext, priority);
|
|
35
|
-
this.regexp = regexp;
|
|
36
|
-
this.render = render;
|
|
37
|
-
}
|
|
38
|
-
match(input) {
|
|
39
|
-
return this.regexp.exec(input);
|
|
40
|
-
}
|
|
2
|
+
export function getMarkupRule(regexp, //
|
|
3
|
+
render, contexts, priority) {
|
|
4
|
+
return {
|
|
5
|
+
regexp,
|
|
6
|
+
match(input, options) {
|
|
7
|
+
const match = regexp.exec(input);
|
|
8
|
+
if (match) {
|
|
9
|
+
const { index, 0: { length }, } = match;
|
|
10
|
+
return { index, length, ...render(match, options) };
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
contexts,
|
|
14
|
+
priority,
|
|
15
|
+
};
|
|
41
16
|
}
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
17
|
+
export function getNamedMarkupRule(regexp, //
|
|
18
|
+
render, contexts, priority) {
|
|
19
|
+
return {
|
|
20
|
+
regexp,
|
|
21
|
+
match(input, options) {
|
|
22
|
+
const match = regexp.exec(input);
|
|
23
|
+
if (match) {
|
|
24
|
+
const { index, 0: { length }, groups, } = match;
|
|
25
|
+
return { index, length, ...render(groups, options) };
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
contexts,
|
|
29
|
+
priority,
|
|
30
|
+
};
|
|
54
31
|
}
|
|
55
|
-
export
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
match(input, { schemes, url: base }) {
|
|
68
|
-
const match = this.regexp.exec(input);
|
|
69
|
-
if (match) {
|
|
70
|
-
const { 0: first, index, groups } = match;
|
|
71
|
-
const { href, title } = groups;
|
|
72
|
-
const url = getOptionalURL(href, base);
|
|
73
|
-
if (url && schemes.includes(url.protocol)) {
|
|
74
|
-
return { 0: first, index, groups: { href: url.href, title: title?.trim() || formatURL(url) } };
|
|
32
|
+
export function getLinkMarkupRule(regexp, //
|
|
33
|
+
render, contexts, priority) {
|
|
34
|
+
return {
|
|
35
|
+
regexp,
|
|
36
|
+
match(input, options) {
|
|
37
|
+
const match = this.regexp.exec(input);
|
|
38
|
+
if (match) {
|
|
39
|
+
const { schemes, url: base } = options;
|
|
40
|
+
const { 0: { length }, index, groups: { href, title }, } = match;
|
|
41
|
+
const url = getOptionalURL(href, base);
|
|
42
|
+
if (url && schemes.includes(url.protocol))
|
|
43
|
+
return { index, length, ...render(title?.trim() || formatURL(url), url.href, options) };
|
|
75
44
|
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
45
|
+
},
|
|
46
|
+
contexts,
|
|
47
|
+
priority,
|
|
48
|
+
};
|
|
79
49
|
}
|
package/markup/rules.d.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import type { MarkupOptions } from "./options.js";
|
|
2
2
|
import type { MarkupRules } from "./rule.js";
|
|
3
|
-
import type {
|
|
4
|
-
import { LinkRegExpMarkupRule, NamedRegExpMarkupRule, RegExpMarkupRule } from "./rule.js";
|
|
3
|
+
import type { NamedRegExp } from "../util/regexp.js";
|
|
5
4
|
/**
|
|
6
5
|
* Headings are single line only (don't allow multiline).
|
|
7
6
|
* - 1-6 hashes then 1+ spaces, then the title.
|
|
8
7
|
* - Same as Markdown syntax.
|
|
9
8
|
* - Markdown's underline syntax is not supported (for simplification).
|
|
10
9
|
*/
|
|
11
|
-
export declare const HEADING_RULE:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
export declare const HEADING_RULE: import("./rule.js").MarkupRule & {
|
|
11
|
+
regexp: NamedRegExp<{
|
|
12
|
+
prefix: string;
|
|
13
|
+
heading: string;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
15
16
|
/**
|
|
16
17
|
* Separator (horizontal rule / thematic break).
|
|
17
18
|
* - Same as Markdown syntax but also allows `•` bullet character (in addition to `-` dash, `+` plus, `*` asterisk, `_` underscore).
|
|
@@ -19,16 +20,24 @@ export declare const HEADING_RULE: NamedRegExpMarkupRule<{
|
|
|
19
20
|
* - Character must be the same every time (can't mix)
|
|
20
21
|
* - Might have infinite number of spaces between the characters.
|
|
21
22
|
*/
|
|
22
|
-
export declare const SEPARATOR_RULE:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}>;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
export declare const SEPARATOR_RULE: import("./rule.js").MarkupRule & {
|
|
24
|
+
regexp: import("../util/regexp.js").TypedRegExp<string>;
|
|
25
|
+
};
|
|
26
|
+
export declare const UNORDERED_RULE: import("./rule.js").MarkupRule & {
|
|
27
|
+
regexp: NamedRegExp<{
|
|
28
|
+
list: string;
|
|
29
|
+
}>;
|
|
30
|
+
};
|
|
31
|
+
export declare const ORDERED_RULE: import("./rule.js").MarkupRule & {
|
|
32
|
+
regexp: NamedRegExp<{
|
|
33
|
+
list: string;
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
36
|
+
export declare const BLOCKQUOTE_RULE: import("./rule.js").MarkupRule & {
|
|
37
|
+
regexp: NamedRegExp<{
|
|
38
|
+
quote: string;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
32
41
|
/**
|
|
33
42
|
* Fenced code blocks
|
|
34
43
|
* - Same as Markdown syntax.
|
|
@@ -36,23 +45,35 @@ export declare const BLOCKQUOTE_RULE: NamedRegExpMarkupRule<{
|
|
|
36
45
|
* - If there's no closing fence the code block will run to the end of the current string.
|
|
37
46
|
* - Markdown-style four-space indent syntax is not supported (only fenced code, since it's easier to use).
|
|
38
47
|
*/
|
|
39
|
-
export declare const FENCED_CODE_RULE:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
export declare const FENCED_CODE_RULE: import("./rule.js").MarkupRule & {
|
|
49
|
+
regexp: NamedRegExp<{
|
|
50
|
+
wrap: string;
|
|
51
|
+
title?: string;
|
|
52
|
+
code: string;
|
|
53
|
+
}>;
|
|
54
|
+
};
|
|
44
55
|
/**
|
|
45
56
|
* Paragraph.
|
|
46
57
|
* - When ordering rules, paragraph should go after other "block" context elements (because it has a very generous capture).
|
|
47
58
|
*/
|
|
48
|
-
export declare const PARAGRAPH_RULE:
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
export declare const PARAGRAPH_RULE: import("./rule.js").MarkupRule & {
|
|
60
|
+
regexp: NamedRegExp<{
|
|
61
|
+
paragraph: string;
|
|
62
|
+
}>;
|
|
63
|
+
};
|
|
51
64
|
/** Render function for URL and LINK rules. */
|
|
52
|
-
export declare function renderLinkRule(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
export declare function renderLinkRule(title: string, href: string, { rel }: MarkupOptions): {
|
|
66
|
+
type: string;
|
|
67
|
+
key: null;
|
|
68
|
+
ref: null;
|
|
69
|
+
$$typeof: symbol;
|
|
70
|
+
props: {
|
|
71
|
+
children: string;
|
|
72
|
+
href: string;
|
|
73
|
+
rel: string | undefined;
|
|
74
|
+
};
|
|
75
|
+
context: string;
|
|
76
|
+
};
|
|
56
77
|
/**
|
|
57
78
|
* Autolinked URL starts with `http:` or `https:` or `mailto:` (any scheme in `options.schemes`) and matches an unlimited number of non-space characters.
|
|
58
79
|
* - If followed by space and then text in `()` round or `[]` square brackets that will be used as the title, e.g. `http://google.com/maps (Google Maps)` or `http://google.com/maps [Google Maps]` (this syntax is from Todoist and maybe other things too).
|
|
@@ -60,7 +81,12 @@ export declare function renderLinkRule({ href, title }: {
|
|
|
60
81
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
61
82
|
* - For security only schemes that appear in `options.schemes` will match (defaults to `http:` and `https:`).
|
|
62
83
|
*/
|
|
63
|
-
export declare const URL_RULE:
|
|
84
|
+
export declare const URL_RULE: import("./rule.js").MarkupRule & {
|
|
85
|
+
regexp: NamedRegExp<{
|
|
86
|
+
title?: string;
|
|
87
|
+
href: string;
|
|
88
|
+
}>;
|
|
89
|
+
};
|
|
64
90
|
/**
|
|
65
91
|
* Markdown-style link.
|
|
66
92
|
* - Link in standard Markdown format, e.g. `[Google Maps](http://google.com/maps)`
|
|
@@ -69,7 +95,12 @@ export declare const URL_RULE: LinkRegExpMarkupRule;
|
|
|
69
95
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
70
96
|
* - For security only `http://` or `https://` links will work (if invalid the unparsed text will be returned).
|
|
71
97
|
*/
|
|
72
|
-
export declare const LINK_RULE:
|
|
98
|
+
export declare const LINK_RULE: import("./rule.js").MarkupRule & {
|
|
99
|
+
regexp: NamedRegExp<{
|
|
100
|
+
title?: string;
|
|
101
|
+
href: string;
|
|
102
|
+
}>;
|
|
103
|
+
};
|
|
73
104
|
/**
|
|
74
105
|
* Inline code.
|
|
75
106
|
* - Text surrounded by one or more "`" backtick tilde characters.
|
|
@@ -77,9 +108,11 @@ export declare const LINK_RULE: LinkRegExpMarkupRule;
|
|
|
77
108
|
* - Closing characters must exactly match opening characters.
|
|
78
109
|
* - Same as Markdown syntax.
|
|
79
110
|
*/
|
|
80
|
-
export declare const CODE_RULE:
|
|
81
|
-
|
|
82
|
-
|
|
111
|
+
export declare const CODE_RULE: import("./rule.js").MarkupRule & {
|
|
112
|
+
regexp: NamedRegExp<{
|
|
113
|
+
code: string;
|
|
114
|
+
}>;
|
|
115
|
+
};
|
|
83
116
|
/**
|
|
84
117
|
* Inline strong, emphasis, insert, delete, highlight.
|
|
85
118
|
* - Inline strong text wrapped in one or more `*` asterisks.
|
|
@@ -102,11 +135,13 @@ declare const INLINE_CHARS: {
|
|
|
102
135
|
"=": string;
|
|
103
136
|
":": string;
|
|
104
137
|
};
|
|
105
|
-
export declare const INLINE_RULE:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
138
|
+
export declare const INLINE_RULE: import("./rule.js").MarkupRule & {
|
|
139
|
+
regexp: NamedRegExp<{
|
|
140
|
+
char: keyof typeof INLINE_CHARS;
|
|
141
|
+
wrap: string;
|
|
142
|
+
text: string;
|
|
143
|
+
}>;
|
|
144
|
+
};
|
|
110
145
|
/**
|
|
111
146
|
* Hard linebreak (`<br />` tag).
|
|
112
147
|
* - Any line break in a paragraph will become a hard `<br />` tag.
|
|
@@ -115,7 +150,9 @@ export declare const INLINE_RULE: NamedRegExpMarkupRule<{
|
|
|
115
150
|
* - This is more intuitive (a linebreak becomes a linebreak is isn't silently ignored).
|
|
116
151
|
* - This works better with textareas that wrap text (since manually breaking up long lines is no longer necessary).
|
|
117
152
|
*/
|
|
118
|
-
export declare const LINEBREAK_RULE:
|
|
153
|
+
export declare const LINEBREAK_RULE: import("./rule.js").MarkupRule & {
|
|
154
|
+
regexp: import("../util/regexp.js").TypedRegExp<string>;
|
|
155
|
+
};
|
|
119
156
|
/**
|
|
120
157
|
* All markup rules.
|
|
121
158
|
* - Syntax parsed by `renderMarkup()` is defined entirely by the list of rules (i.e. not by code).
|
package/markup/rules.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
/* eslint-disable import/export */
|
|
2
1
|
import { getRegExp } from "../util/regexp.js";
|
|
3
2
|
import { BLOCK_REGEXP, LINE_REGEXP, WordRegExp, getBlockRegExp, getLineRegExp } from "./regexp.js";
|
|
4
|
-
import {
|
|
3
|
+
import { getLinkMarkupRule, getMarkupRule, getNamedMarkupRule } from "./rule.js";
|
|
5
4
|
/** React security symbol — see https://github.com/facebook/react/pull/4832 */
|
|
6
5
|
const $$typeof = Symbol.for("react.element");
|
|
7
6
|
/**
|
|
@@ -10,13 +9,14 @@ const $$typeof = Symbol.for("react.element");
|
|
|
10
9
|
* - Same as Markdown syntax.
|
|
11
10
|
* - Markdown's underline syntax is not supported (for simplification).
|
|
12
11
|
*/
|
|
13
|
-
export const HEADING_RULE =
|
|
12
|
+
export const HEADING_RULE = getNamedMarkupRule(getLineRegExp(`(?<prefix>#{1,6}) +(?<heading>${LINE_REGEXP.source})`), ({ prefix, heading }) => ({
|
|
14
13
|
type: `h${prefix.length}`,
|
|
15
14
|
key: null,
|
|
16
15
|
ref: null,
|
|
17
16
|
$$typeof,
|
|
18
17
|
props: { children: heading.trim() },
|
|
19
|
-
|
|
18
|
+
context: "inline",
|
|
19
|
+
}), ["block"]);
|
|
20
20
|
/**
|
|
21
21
|
* Separator (horizontal rule / thematic break).
|
|
22
22
|
* - Same as Markdown syntax but also allows `•` bullet character (in addition to `-` dash, `+` plus, `*` asterisk, `_` underscore).
|
|
@@ -24,7 +24,7 @@ export const HEADING_RULE = new NamedRegExpMarkupRule(getLineRegExp(`(?<prefix>#
|
|
|
24
24
|
* - Character must be the same every time (can't mix)
|
|
25
25
|
* - Might have infinite number of spaces between the characters.
|
|
26
26
|
*/
|
|
27
|
-
export const SEPARATOR_RULE =
|
|
27
|
+
export const SEPARATOR_RULE = getMarkupRule(getLineRegExp("([-*•+_=])(?: *\\1){2,}"), () => ({
|
|
28
28
|
type: "hr",
|
|
29
29
|
key: null,
|
|
30
30
|
ref: null,
|
|
@@ -41,13 +41,14 @@ export const SEPARATOR_RULE = new RegExpMarkupRule(getLineRegExp("([-*•+_=])(?
|
|
|
41
41
|
const UNORDERED_PREFIX = "[-*•+] +";
|
|
42
42
|
const UNORDERED_SPLIT = new RegExp(`(?:^|\n)+${UNORDERED_PREFIX}`, "g");
|
|
43
43
|
const UNORDERED_INDENT = /^\t/gm;
|
|
44
|
-
export const UNORDERED_RULE =
|
|
44
|
+
export const UNORDERED_RULE = getNamedMarkupRule(getBlockRegExp(`(?<list>${UNORDERED_PREFIX}${BLOCK_REGEXP.source})`), ({ list }) => ({
|
|
45
45
|
type: "ul",
|
|
46
46
|
key: null,
|
|
47
47
|
ref: null,
|
|
48
48
|
$$typeof,
|
|
49
49
|
props: { children: list.split(UNORDERED_SPLIT).filter(Boolean).map(_mapUnordered) },
|
|
50
|
-
|
|
50
|
+
context: "list",
|
|
51
|
+
}), ["block", "list"]);
|
|
51
52
|
const _mapUnordered = (item, key) => ({
|
|
52
53
|
type: "li",
|
|
53
54
|
key,
|
|
@@ -63,13 +64,14 @@ const _mapUnordered = (item, key) => ({
|
|
|
63
64
|
const ORDERED_PREFIX = "[1-9][0-9]{0,8}[.):] +"; // Number for a numbered list, e.g. `1.` or `2)` or `3:`
|
|
64
65
|
const ORDERED_SPLIT = new RegExp(`\n+(?=${ORDERED_PREFIX})`, "g");
|
|
65
66
|
const ORDERED_INDENT = UNORDERED_INDENT;
|
|
66
|
-
export const ORDERED_RULE =
|
|
67
|
+
export const ORDERED_RULE = getNamedMarkupRule(getBlockRegExp(`(?<list>${ORDERED_PREFIX}${BLOCK_REGEXP.source})`), ({ list }) => ({
|
|
67
68
|
type: "ol",
|
|
68
69
|
key: null,
|
|
69
70
|
ref: null,
|
|
70
71
|
$$typeof,
|
|
71
72
|
props: { children: list.split(ORDERED_SPLIT).map(_mapOrdered) },
|
|
72
|
-
|
|
73
|
+
context: "list",
|
|
74
|
+
}), ["block", "list"]);
|
|
73
75
|
const _mapOrdered = (item, key) => ({
|
|
74
76
|
type: "li",
|
|
75
77
|
key,
|
|
@@ -91,13 +93,14 @@ const _mapOrdered = (item, key) => ({
|
|
|
91
93
|
*/
|
|
92
94
|
const BLOCKQUOTE_PREFIX = "> *";
|
|
93
95
|
const BLOCKQUOTE_INDENT = new RegExp(`^${BLOCKQUOTE_PREFIX}`, "gm");
|
|
94
|
-
export const BLOCKQUOTE_RULE =
|
|
96
|
+
export const BLOCKQUOTE_RULE = getNamedMarkupRule(getLineRegExp(`(?<quote>${BLOCKQUOTE_PREFIX}${LINE_REGEXP.source}(?:\n${BLOCKQUOTE_PREFIX}${LINE_REGEXP.source})*)`), ({ quote }) => ({
|
|
95
97
|
type: "blockquote",
|
|
96
98
|
key: null,
|
|
97
99
|
ref: null,
|
|
98
100
|
$$typeof,
|
|
99
101
|
props: { children: quote.replace(BLOCKQUOTE_INDENT, "") },
|
|
100
|
-
|
|
102
|
+
context: "block",
|
|
103
|
+
}), ["block", "list"]);
|
|
101
104
|
/**
|
|
102
105
|
* Fenced code blocks
|
|
103
106
|
* - Same as Markdown syntax.
|
|
@@ -105,7 +108,7 @@ export const BLOCKQUOTE_RULE = new NamedRegExpMarkupRule(getLineRegExp(`(?<quote
|
|
|
105
108
|
* - If there's no closing fence the code block will run to the end of the current string.
|
|
106
109
|
* - Markdown-style four-space indent syntax is not supported (only fenced code, since it's easier to use).
|
|
107
110
|
*/
|
|
108
|
-
export const FENCED_CODE_RULE =
|
|
111
|
+
export const FENCED_CODE_RULE = getNamedMarkupRule(
|
|
109
112
|
// Matcher has its own end that only stops when it reaches a matching closing fence or the end of the string.
|
|
110
113
|
getLineRegExp(`(?<wrap>\`{3,}|~{3,}) *(?<title>${LINE_REGEXP.source})\n(?<code>${BLOCK_REGEXP.source})`, `(?:\n\\k<wrap>|$)`), ({ title, code }) => ({
|
|
111
114
|
type: "pre",
|
|
@@ -121,26 +124,28 @@ getLineRegExp(`(?<wrap>\`{3,}|~{3,}) *(?<title>${LINE_REGEXP.source})\n(?<code>$
|
|
|
121
124
|
props: { title: title?.trim() || undefined, children: code.trim() },
|
|
122
125
|
},
|
|
123
126
|
},
|
|
124
|
-
}), ["block", "list"],
|
|
127
|
+
}), ["block", "list"], 10);
|
|
125
128
|
/**
|
|
126
129
|
* Paragraph.
|
|
127
130
|
* - When ordering rules, paragraph should go after other "block" context elements (because it has a very generous capture).
|
|
128
131
|
*/
|
|
129
|
-
export const PARAGRAPH_RULE =
|
|
132
|
+
export const PARAGRAPH_RULE = getNamedMarkupRule(getBlockRegExp(`(?<paragraph>${BLOCK_REGEXP.source})`), ({ paragraph }) => ({
|
|
130
133
|
type: `p`,
|
|
131
134
|
key: null,
|
|
132
135
|
ref: null,
|
|
133
136
|
$$typeof,
|
|
134
137
|
props: { children: paragraph.trim() },
|
|
135
|
-
|
|
138
|
+
context: "inline",
|
|
139
|
+
}), ["block"], -10);
|
|
136
140
|
/** Render function for URL and LINK rules. */
|
|
137
|
-
export function renderLinkRule(
|
|
141
|
+
export function renderLinkRule(title, href, { rel }) {
|
|
138
142
|
return {
|
|
139
143
|
type: "a",
|
|
140
144
|
key: null,
|
|
141
145
|
ref: null,
|
|
142
146
|
$$typeof,
|
|
143
147
|
props: { children: title, href, rel },
|
|
148
|
+
context: "link",
|
|
144
149
|
};
|
|
145
150
|
}
|
|
146
151
|
/**
|
|
@@ -150,8 +155,8 @@ export function renderLinkRule({ href, title }, { rel }) {
|
|
|
150
155
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
151
156
|
* - For security only schemes that appear in `options.schemes` will match (defaults to `http:` and `https:`).
|
|
152
157
|
*/
|
|
153
|
-
export const URL_RULE =
|
|
154
|
-
renderLinkRule, ["inline", "list"]
|
|
158
|
+
export const URL_RULE = getLinkMarkupRule(getRegExp(/(?<href>[a-z]+:[-$_@.&!*,=;/#?:%a-zA-Z0-9]+)(?: +(?:\((?<title>[^)]*?)\)))?/), //
|
|
159
|
+
renderLinkRule, ["inline", "list"]);
|
|
155
160
|
/**
|
|
156
161
|
* Markdown-style link.
|
|
157
162
|
* - Link in standard Markdown format, e.g. `[Google Maps](http://google.com/maps)`
|
|
@@ -160,8 +165,8 @@ renderLinkRule, ["inline", "list"], "link");
|
|
|
160
165
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
161
166
|
* - For security only `http://` or `https://` links will work (if invalid the unparsed text will be returned).
|
|
162
167
|
*/
|
|
163
|
-
export const LINK_RULE =
|
|
164
|
-
renderLinkRule, ["inline", "list"]
|
|
168
|
+
export const LINK_RULE = getLinkMarkupRule(getRegExp(/\[(?<title>[^\]]*?)\]\((?<href>[^)]*?)\)/), //
|
|
169
|
+
renderLinkRule, ["inline", "list"]);
|
|
165
170
|
/**
|
|
166
171
|
* Inline code.
|
|
167
172
|
* - Text surrounded by one or more "`" backtick tilde characters.
|
|
@@ -169,13 +174,13 @@ renderLinkRule, ["inline", "list"], "link");
|
|
|
169
174
|
* - Closing characters must exactly match opening characters.
|
|
170
175
|
* - Same as Markdown syntax.
|
|
171
176
|
*/
|
|
172
|
-
export const CODE_RULE =
|
|
177
|
+
export const CODE_RULE = getNamedMarkupRule(new RegExp(`(?<wrap>\`+)(?<code>${BLOCK_REGEXP.source})\\k<wrap>`), ({ code }) => ({
|
|
173
178
|
type: "code",
|
|
174
179
|
key: null,
|
|
175
180
|
ref: null,
|
|
176
181
|
$$typeof,
|
|
177
182
|
props: { children: code },
|
|
178
|
-
}), ["inline", "list"],
|
|
183
|
+
}), ["inline", "list"], 10);
|
|
179
184
|
/**
|
|
180
185
|
* Inline strong, emphasis, insert, delete, highlight.
|
|
181
186
|
* - Inline strong text wrapped in one or more `*` asterisks.
|
|
@@ -190,14 +195,15 @@ export const CODE_RULE = new NamedRegExpMarkupRule(new RegExp(`(?<wrap>\`+)(?<co
|
|
|
190
195
|
* - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
|
|
191
196
|
*/
|
|
192
197
|
const INLINE_CHARS = { "-": "del", "~": "del", "+": "ins", "*": "strong", "_": "em", "=": "mark", ":": "mark" }; // Hyphen must be first so it works when we use the keys as a character class.
|
|
193
|
-
export const INLINE_RULE =
|
|
198
|
+
export const INLINE_RULE = getNamedMarkupRule(new WordRegExp(`(?<wrap>(?<char>[${Object.keys(INLINE_CHARS).join("")}])+)(?<text>(?!\\k<char>)\\S|(?!\\k<char>)\\S[\\s\\S]*?(?!\\k<char>)\\S)\\k<wrap>`), // prettier-ignore
|
|
194
199
|
({ char, text }) => ({
|
|
195
200
|
type: INLINE_CHARS[char],
|
|
196
201
|
key: null,
|
|
197
202
|
ref: null,
|
|
198
203
|
$$typeof,
|
|
199
204
|
props: { children: text },
|
|
200
|
-
|
|
205
|
+
context: "inline",
|
|
206
|
+
}), ["inline", "list", "link"]);
|
|
201
207
|
/**
|
|
202
208
|
* Hard linebreak (`<br />` tag).
|
|
203
209
|
* - Any line break in a paragraph will become a hard `<br />` tag.
|
|
@@ -206,13 +212,13 @@ export const INLINE_RULE = new NamedRegExpMarkupRule(new WordRegExp(`(?<wrap>(?<
|
|
|
206
212
|
* - This is more intuitive (a linebreak becomes a linebreak is isn't silently ignored).
|
|
207
213
|
* - This works better with textareas that wrap text (since manually breaking up long lines is no longer necessary).
|
|
208
214
|
*/
|
|
209
|
-
export const LINEBREAK_RULE =
|
|
215
|
+
export const LINEBREAK_RULE = getMarkupRule(/\n/, () => ({
|
|
210
216
|
type: "br",
|
|
211
217
|
key: null,
|
|
212
218
|
ref: null,
|
|
213
219
|
$$typeof,
|
|
214
220
|
props: {},
|
|
215
|
-
}), ["inline", "list", "link"]
|
|
221
|
+
}), ["inline", "list", "link"]);
|
|
216
222
|
/**
|
|
217
223
|
* All markup rules.
|
|
218
224
|
* - Syntax parsed by `renderMarkup()` is defined entirely by the list of rules (i.e. not by code).
|
package/package.json
CHANGED
package/util/regexp.d.ts
CHANGED
|
@@ -6,18 +6,6 @@ export declare const ALWAYS_REGEXP: RegExp;
|
|
|
6
6
|
export declare const NEVER_REGEXP: RegExp;
|
|
7
7
|
/** Things that can be convert to a regular expression. */
|
|
8
8
|
export type PossibleRegExp = string | RegExp;
|
|
9
|
-
/** Set of named match groups from a regular expression. */
|
|
10
|
-
export type NamedRegExpData = {
|
|
11
|
-
[named: string]: string;
|
|
12
|
-
};
|
|
13
|
-
/** Regular expression match array that you've asserted contains the specified named groups. */
|
|
14
|
-
export interface NamedRegExpArray<T extends NamedRegExpData = NamedRegExpData> extends RegExpExecArray {
|
|
15
|
-
groups: T;
|
|
16
|
-
}
|
|
17
|
-
/** Regular expression that you've asserted contains the specified named capture groups. */
|
|
18
|
-
export interface NamedRegExp<T extends NamedRegExpData = NamedRegExpData> extends RegExp {
|
|
19
|
-
exec(input: string): NamedRegExpArray<T> | null;
|
|
20
|
-
}
|
|
21
9
|
/** Is an unknown value a `RegExp` instance? */
|
|
22
10
|
export declare const isRegExp: (value: unknown) => value is RegExp;
|
|
23
11
|
/** Assert that an unknown value is a `RegExp` instance. */
|
|
@@ -39,3 +27,23 @@ export declare function getAllRegExp(patterns: Iterable<PossibleRegExp> & NotStr
|
|
|
39
27
|
export declare const isRegExpMatch: Match<[item: string, target: RegExp]>;
|
|
40
28
|
/** Match function for finding strings that match against regular expressions (use with `filter()` to negatively filter iterable sets of items). */
|
|
41
29
|
export declare const notRegExpMatch: Match<[item: string, target: RegExp]>;
|
|
30
|
+
/** Regular expression match array that you've asserted contains the specified named groups. */
|
|
31
|
+
export interface TypedRegExpExecArray<T extends string = string> extends RegExpExecArray {
|
|
32
|
+
0: T;
|
|
33
|
+
}
|
|
34
|
+
/** Regular expression that you've asserted contains the specified named capture groups. */
|
|
35
|
+
export interface TypedRegExp<T extends string = string> extends RegExp {
|
|
36
|
+
exec(input: string): TypedRegExpExecArray<T> | null;
|
|
37
|
+
}
|
|
38
|
+
/** Set of named match groups from a regular expression. */
|
|
39
|
+
export type NamedRegExpData = {
|
|
40
|
+
[named: string]: string;
|
|
41
|
+
};
|
|
42
|
+
/** Regular expression match array that you've asserted contains the specified named groups. */
|
|
43
|
+
export interface NamedRegExpExecArray<T extends NamedRegExpData = NamedRegExpData> extends RegExpExecArray {
|
|
44
|
+
groups: T;
|
|
45
|
+
}
|
|
46
|
+
/** Regular expression that you've asserted contains the specified named capture groups. */
|
|
47
|
+
export interface NamedRegExp<T extends NamedRegExpData = NamedRegExpData> extends RegExp {
|
|
48
|
+
exec(input: string): NamedRegExpExecArray<T> | null;
|
|
49
|
+
}
|