shelving 1.69.0 → 1.71.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/api/Resource.js +2 -2
- package/error/ValidationError.js +2 -1
- package/feedback/Feedback.d.ts +4 -16
- package/feedback/Feedback.js +8 -31
- package/markup/index.d.ts +1 -1
- package/markup/index.js +1 -1
- package/markup/options.d.ts +16 -0
- package/markup/options.js +9 -0
- package/markup/render.d.ts +4 -4
- package/markup/render.js +95 -123
- package/markup/rules.d.ts +113 -33
- package/markup/rules.js +195 -130
- package/observe/Subject.d.ts +1 -1
- package/package.json +2 -2
- package/query/Filter.d.ts +1 -1
- package/query/Filter.js +3 -2
- package/react/useDocument.js +3 -4
- package/react/useQuery.js +3 -4
- package/schema/AllowSchema.js +4 -4
- package/schema/ArraySchema.js +2 -2
- package/schema/LinkSchema.js +2 -2
- package/schema/index.d.ts +0 -1
- package/schema/index.js +0 -1
- package/test/util.d.ts +3 -2
- package/update/DataUpdate.js +4 -6
- package/util/array.d.ts +1 -2
- package/util/array.js +7 -5
- package/util/clone.js +2 -2
- package/util/color.js +3 -2
- package/util/data.d.ts +13 -2
- package/util/data.js +19 -0
- package/util/date.d.ts +17 -16
- package/util/date.js +4 -3
- package/util/debug.d.ts +15 -1
- package/util/debug.js +62 -22
- package/util/equal.d.ts +2 -2
- package/util/equal.js +12 -7
- package/util/filter.d.ts +0 -4
- package/util/filter.js +0 -6
- package/util/function.d.ts +2 -0
- package/util/hydrate.js +8 -7
- package/util/index.d.ts +1 -1
- package/util/index.js +1 -1
- package/util/iterate.d.ts +1 -1
- package/util/jsx.d.ts +7 -10
- package/util/jsx.js +15 -13
- package/util/map.d.ts +12 -21
- package/util/map.js +13 -9
- package/util/match.js +4 -3
- package/util/number.d.ts +2 -2
- package/util/number.js +5 -1
- package/util/object.d.ts +12 -8
- package/util/object.js +13 -12
- package/util/regexp.d.ts +56 -0
- package/util/regexp.js +60 -0
- package/util/set.d.ts +14 -0
- package/util/set.js +17 -0
- package/util/sort.d.ts +1 -1
- package/util/string.d.ts +43 -34
- package/util/string.js +83 -77
- package/util/template.d.ts +9 -4
- package/util/template.js +14 -20
- package/util/timeout.js +3 -3
- package/util/transform.d.ts +10 -6
- package/util/transform.js +5 -1
- package/util/units.d.ts +8 -8
- package/util/url.d.ts +7 -5
- package/util/url.js +17 -14
- package/util/validate.js +6 -8
- package/markup/types.d.ts +0 -44
- package/markup/types.js +0 -1
- package/schema/MapSchema.d.ts +0 -19
- package/schema/MapSchema.js +0 -29
- package/util/search.d.ts +0 -71
- package/util/search.js +0 -97
package/api/Resource.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { validate } from "../util/validate.js";
|
|
2
2
|
import { getUndefined } from "../util/undefined.js";
|
|
3
|
-
import {
|
|
3
|
+
import { isFeedback } from "../feedback/Feedback.js";
|
|
4
4
|
import { ValidationError } from "../error/ValidationError.js";
|
|
5
5
|
/**
|
|
6
6
|
* An abstract API resource definition, used to specify types for e.g. serverless functions..
|
|
@@ -34,7 +34,7 @@ export class Resource {
|
|
|
34
34
|
return validate(unsafeResult, this.result);
|
|
35
35
|
}
|
|
36
36
|
catch (thrown) {
|
|
37
|
-
throw thrown
|
|
37
|
+
throw isFeedback(thrown) ? new ValidationError(`Invalid result for resource`, thrown) : thrown;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
package/error/ValidationError.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { debug } from "../util/debug.js";
|
|
1
2
|
/** Thrown if a value isn't valid. */
|
|
2
3
|
export class ValidationError extends Error {
|
|
3
4
|
constructor(message, feedback) {
|
|
4
|
-
super(`${message}:\n${feedback.
|
|
5
|
+
super(`${message}:\n${feedback.message} (received ${debug(feedback.details)})`);
|
|
5
6
|
this.feedback = feedback;
|
|
6
7
|
}
|
|
7
8
|
}
|
package/feedback/Feedback.d.ts
CHANGED
|
@@ -12,28 +12,16 @@ import type { ImmutableObject } from "../util/object.js";
|
|
|
12
12
|
*/
|
|
13
13
|
export declare class Feedback {
|
|
14
14
|
/** String feedback message that is safe to show to a user. */
|
|
15
|
-
readonly
|
|
15
|
+
readonly message: string;
|
|
16
16
|
/** Nested details providing deeper feedback. */
|
|
17
17
|
readonly details: ImmutableObject;
|
|
18
18
|
constructor(feedback: string, details?: ImmutableObject);
|
|
19
19
|
/**
|
|
20
20
|
* Map details to a set of string messages.
|
|
21
|
-
* - If a detail is another `Feedback` instance, return its
|
|
21
|
+
* - If a detail is another `Feedback` instance, return its `.message` string.
|
|
22
22
|
* - If a detail is anything else, convert it to string using `toString()`
|
|
23
23
|
*/
|
|
24
24
|
get messages(): ImmutableObject<string>;
|
|
25
|
-
/**
|
|
26
|
-
* Convert to string (equivalent to `message.details`).
|
|
27
|
-
* Returns a string including the main message string and a deeply nested list of child message strings.
|
|
28
|
-
*
|
|
29
|
-
* > Invalid format
|
|
30
|
-
* > - name: Invalid format
|
|
31
|
-
* > - first: Must be string
|
|
32
|
-
* > - value: 123
|
|
33
|
-
* > - last: Must be string
|
|
34
|
-
* > - value: true
|
|
35
|
-
* > - age: Must be number
|
|
36
|
-
* > - value: "abc"
|
|
37
|
-
*/
|
|
38
|
-
toString(): string;
|
|
39
25
|
}
|
|
26
|
+
/** Is an unknown value a `Feedback` instance? */
|
|
27
|
+
export declare const isFeedback: <T extends Feedback>(v: unknown) => v is Feedback;
|
package/feedback/Feedback.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { getString } from "../util/string.js";
|
|
2
|
+
import { mapObject } from "../util/transform.js";
|
|
3
3
|
/**
|
|
4
4
|
* The `Feedback` class represents a feedback message that should be shown to the user.
|
|
5
5
|
* - Basic `Feedback` is neither good nor bad, `SuccessFeedback` indicates good news, and `ErrorFeedback` indicates bad news.
|
|
@@ -13,41 +13,18 @@ import { getTitle } from "../util/string.js";
|
|
|
13
13
|
*/
|
|
14
14
|
export class Feedback {
|
|
15
15
|
constructor(feedback, details = {}) {
|
|
16
|
-
this.
|
|
16
|
+
this.message = feedback;
|
|
17
17
|
this.details = details;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Map details to a set of string messages.
|
|
21
|
-
* - If a detail is another `Feedback` instance, return its
|
|
21
|
+
* - If a detail is another `Feedback` instance, return its `.message` string.
|
|
22
22
|
* - If a detail is anything else, convert it to string using `toString()`
|
|
23
23
|
*/
|
|
24
24
|
get messages() {
|
|
25
|
-
|
|
26
|
-
for (const [k, v] of Object.entries(this.details)) {
|
|
27
|
-
if (v instanceof Feedback)
|
|
28
|
-
messages[k] = v.feedback;
|
|
29
|
-
else
|
|
30
|
-
messages[k] = getTitle(v);
|
|
31
|
-
}
|
|
32
|
-
return messages;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Convert to string (equivalent to `message.details`).
|
|
36
|
-
* Returns a string including the main message string and a deeply nested list of child message strings.
|
|
37
|
-
*
|
|
38
|
-
* > Invalid format
|
|
39
|
-
* > - name: Invalid format
|
|
40
|
-
* > - first: Must be string
|
|
41
|
-
* > - value: 123
|
|
42
|
-
* > - last: Must be string
|
|
43
|
-
* > - value: true
|
|
44
|
-
* > - age: Must be number
|
|
45
|
-
* > - value: "abc"
|
|
46
|
-
*/
|
|
47
|
-
toString() {
|
|
48
|
-
let output = this.feedback;
|
|
49
|
-
for (const [k, v] of Object.entries(this.details))
|
|
50
|
-
output += `\n- ${k}: ${v instanceof Feedback ? v.toString().replace(/\n/g, "\n ") : debug(v)}`;
|
|
51
|
-
return output;
|
|
25
|
+
return mapObject(this.details, _getMessage);
|
|
52
26
|
}
|
|
53
27
|
}
|
|
28
|
+
const _getMessage = (v) => (isFeedback(v) ? v.message : getString(v));
|
|
29
|
+
/** Is an unknown value a `Feedback` instance? */
|
|
30
|
+
export const isFeedback = (v) => v instanceof Feedback;
|
package/markup/index.d.ts
CHANGED
package/markup/index.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { MarkupRules } from "./rules.js";
|
|
2
|
+
/** The current parsing options (represents the current state of the parsing). */
|
|
3
|
+
export declare type MarkupOptions = {
|
|
4
|
+
/** The active list of parsing rules. */
|
|
5
|
+
readonly rules: MarkupRules;
|
|
6
|
+
/** The initial context to start parsing in (rules may render their children with a different context). */
|
|
7
|
+
readonly context: string;
|
|
8
|
+
/** Set the base URL that any relative links will be relative to (defaults to `window.location.href`, if undefined then relative links won't work). */
|
|
9
|
+
readonly url: string | undefined;
|
|
10
|
+
/** Set the `rel=""` property used for any links (e.g. `rel="nofollow ugc"`). */
|
|
11
|
+
readonly rel: string | undefined;
|
|
12
|
+
/** Valid URL schemes/protocols for links (including trailing commas), defaults to `[`http:`, `https:`]` */
|
|
13
|
+
readonly schemes: string[];
|
|
14
|
+
};
|
|
15
|
+
/** Default options */
|
|
16
|
+
export declare const MARKUP_OPTIONS: MarkupOptions;
|
package/markup/render.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSXNode } from "../util/jsx.js";
|
|
2
|
-
import
|
|
2
|
+
import { MarkupOptions } from "./options.js";
|
|
3
3
|
/**
|
|
4
4
|
* Parse a text string as Markdownish syntax and render it as a JSX node.
|
|
5
5
|
* - Syntax is not defined by this code, but by the rules supplied to it.
|
|
@@ -28,9 +28,9 @@ import type { MarkupOptions } from "./types.js";
|
|
|
28
28
|
* - If the first thing in the definition is a URL, then it's recognised as a link reference (and produces an `<a href=""></a>`)
|
|
29
29
|
* - If the first thing in the definition isn't a URL, then it's recognised as a sidenote/footnote and tapping it will scroll you to that point (and popup the definition like Marco Arment's Bigfoot code).
|
|
30
30
|
*
|
|
31
|
-
* @param
|
|
31
|
+
* @param input The string content possibly containing markup syntax, e.g. "This is a *bold* string.
|
|
32
32
|
* @param options An options object for the render.
|
|
33
33
|
*
|
|
34
|
-
* @returns
|
|
34
|
+
* @returns JSXNode, i.e. either a complete `JSXElement`, `null`, `undefined`, `string`, or an array of zero or more of those.
|
|
35
35
|
*/
|
|
36
|
-
export declare function renderMarkup(
|
|
36
|
+
export declare function renderMarkup(input: string, options?: Partial<MarkupOptions>): JSXNode;
|
package/markup/render.js
CHANGED
|
@@ -1,116 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
/** Convert a string into an array of React nodes using a set of rules. */
|
|
5
|
-
function renderString(content, options) {
|
|
6
|
-
// If there's no context return the unmodified string.
|
|
7
|
-
if (!options.context)
|
|
8
|
-
return content;
|
|
9
|
-
const nodes = [];
|
|
10
|
-
// Loop until we've parsed the entire string.
|
|
11
|
-
while (content.length) {
|
|
12
|
-
// Loop through all rules in the list and see if any match.
|
|
13
|
-
let matchedPriority = Number.MIN_SAFE_INTEGER;
|
|
14
|
-
let matchedIndex = Number.MAX_SAFE_INTEGER;
|
|
15
|
-
let matchedRule = undefined;
|
|
16
|
-
let matchedResult = undefined;
|
|
17
|
-
for (const rule of options.rules) {
|
|
18
|
-
const { priority = 0, match, regexp, contexts } = rule;
|
|
19
|
-
// Only apply this rule if both:
|
|
20
|
-
// 1. The priority is equal or higher to the current priority.
|
|
21
|
-
// 2. The rule is allowed in the current context.
|
|
22
|
-
if (priority >= matchedPriority && contexts.includes(options.context)) {
|
|
23
|
-
const result = match ? match(content, options) : regexp ? content.match(regexp) : null;
|
|
24
|
-
// If this matched and has an index (it might not if it's a `/g` global RegExp, which would be a mistake).
|
|
25
|
-
if (result && typeof result.index === "number") {
|
|
26
|
-
const index = result.index;
|
|
27
|
-
// Only match the rule if either:
|
|
28
|
-
// 1. The index is lower than the previous index then this rule takes priority.
|
|
29
|
-
// 2. The priority is higher than the previous match then this rule takes priority.
|
|
30
|
-
if (index < matchedIndex || priority > matchedPriority) {
|
|
31
|
-
matchedRule = rule;
|
|
32
|
-
matchedResult = result;
|
|
33
|
-
matchedIndex = index;
|
|
34
|
-
matchedPriority = priority;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
// Did at least one rule match?
|
|
40
|
-
if (matchedRule && matchedResult && matchedResult[0]) {
|
|
41
|
-
// If index is more than zero, then add a string node before this one.
|
|
42
|
-
if (matchedIndex) {
|
|
43
|
-
const prefix = content.slice(0, matchedIndex);
|
|
44
|
-
appendNode(nodes, renderString(prefix, options));
|
|
45
|
-
}
|
|
46
|
-
// Call the rule's `render()` function to generate the node.
|
|
47
|
-
const childOptions = { ...options, context: matchedRule.childContext };
|
|
48
|
-
const element = matchedRule.render(matchedResult, childOptions);
|
|
49
|
-
appendNode(nodes, renderNode(element, childOptions));
|
|
50
|
-
// Decrement the content.
|
|
51
|
-
content = content.slice(matchedIndex + matchedResult[0].length);
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
// If nothing else matched add the rest of the string as a node.
|
|
55
|
-
// Don't need to push the string through `renderNode()` because we already know it doesn't match any rules in the current context.
|
|
56
|
-
nodes.push(content);
|
|
57
|
-
content = "";
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
// If there's only one node return the single node (otherwise return the entire array).
|
|
61
|
-
return !nodes.length ? null : nodes.length === 1 ? nodes[0] : nodes;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Append a JSX node to a list of JSX nodes.
|
|
65
|
-
* - Sets a generated `element.key` for JSX elements (based on `nodes.length`)
|
|
66
|
-
* - JSX nodes can be arrays of nodes — these will be flattened directly into the nodes list.
|
|
67
|
-
* - Nodes array is modified in place (not returned).
|
|
68
|
-
*/
|
|
69
|
-
function appendNode(nodes, node) {
|
|
70
|
-
if (!node) {
|
|
71
|
-
// No need to append null, undefined, or empty string.
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
else if (typeof node === "string") {
|
|
75
|
-
// String nodes should be merged into the last node (if there are several in a row).
|
|
76
|
-
const i = nodes.length - 1;
|
|
77
|
-
if (typeof nodes[i] === "string")
|
|
78
|
-
nodes[i] += node;
|
|
79
|
-
else
|
|
80
|
-
nodes.push(node);
|
|
81
|
-
}
|
|
82
|
-
else if (node instanceof Array) {
|
|
83
|
-
// Nested arrays of nodes are flattened.
|
|
84
|
-
for (const n of node)
|
|
85
|
-
appendNode(nodes, n);
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
// Generate `key` property using a numeric incrementor (if it's a string let it pass — there's probably a reason it's a string).
|
|
89
|
-
if (typeof node.key !== "string")
|
|
90
|
-
node.key = nodes.length;
|
|
91
|
-
// Append the node.
|
|
92
|
-
nodes.push(node);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Render a JSX node
|
|
97
|
-
* - Recursively renders the children of the node using the current options.
|
|
98
|
-
*/
|
|
99
|
-
function renderNode(node, options) {
|
|
100
|
-
if (typeof node === "string")
|
|
101
|
-
return renderString(node, options);
|
|
102
|
-
if (node instanceof Array)
|
|
103
|
-
return node.map(n => renderNode(n, options));
|
|
104
|
-
if (typeof node === "object" && node) {
|
|
105
|
-
return {
|
|
106
|
-
...node,
|
|
107
|
-
$$typeof: REACT_SECURITY_SYMBOL,
|
|
108
|
-
props: node.props.children ? { ...node.props, children: renderNode(node.props.children, options) } : node.props,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
return node;
|
|
112
|
-
}
|
|
113
|
-
const REACT_SECURITY_SYMBOL = Symbol.for("react.element");
|
|
2
|
+
import { isArray } from "../util/array.js";
|
|
3
|
+
import { MARKUP_OPTIONS } from "./options.js";
|
|
114
4
|
/**
|
|
115
5
|
* Parse a text string as Markdownish syntax and render it as a JSX node.
|
|
116
6
|
* - Syntax is not defined by this code, but by the rules supplied to it.
|
|
@@ -139,18 +29,100 @@ const REACT_SECURITY_SYMBOL = Symbol.for("react.element");
|
|
|
139
29
|
* - If the first thing in the definition is a URL, then it's recognised as a link reference (and produces an `<a href=""></a>`)
|
|
140
30
|
* - If the first thing in the definition isn't a URL, then it's recognised as a sidenote/footnote and tapping it will scroll you to that point (and popup the definition like Marco Arment's Bigfoot code).
|
|
141
31
|
*
|
|
142
|
-
* @param
|
|
32
|
+
* @param input The string content possibly containing markup syntax, e.g. "This is a *bold* string.
|
|
143
33
|
* @param options An options object for the render.
|
|
144
34
|
*
|
|
145
|
-
* @returns
|
|
35
|
+
* @returns JSXNode, i.e. either a complete `JSXElement`, `null`, `undefined`, `string`, or an array of zero or more of those.
|
|
146
36
|
*/
|
|
147
|
-
export function renderMarkup(
|
|
148
|
-
|
|
37
|
+
export function renderMarkup(input, options) {
|
|
38
|
+
if (!input)
|
|
39
|
+
return null;
|
|
40
|
+
const combined = options ? { ...MARKUP_OPTIONS, ...options } : MARKUP_OPTIONS;
|
|
41
|
+
return _renderString(input, combined, combined.context);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Render a string to its corresponding JSX node in a given context.
|
|
45
|
+
*/
|
|
46
|
+
function _renderString(input, options, context) {
|
|
47
|
+
const nodes = Array.from(_parseString(input, options, context));
|
|
48
|
+
return !nodes.length ? null : nodes.length === 1 ? nodes[0] : nodes;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Render a JSX node in a given context.
|
|
52
|
+
*/
|
|
53
|
+
function _renderNode(node, options, context) {
|
|
54
|
+
if (!node)
|
|
55
|
+
return node;
|
|
56
|
+
if (typeof node === "string")
|
|
57
|
+
return _renderString(node, options, context);
|
|
58
|
+
if (isArray(node)) {
|
|
59
|
+
for (let i = 0; i <= node.length - 1; i++)
|
|
60
|
+
node[i] = _renderNode(node[i], options, context);
|
|
61
|
+
return node;
|
|
62
|
+
}
|
|
63
|
+
return _renderElement(node, options, context);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Render a JSX element in a given context.
|
|
67
|
+
*/
|
|
68
|
+
function _renderElement(element, options, context) {
|
|
69
|
+
if (element.props.children)
|
|
70
|
+
element.props.children = _renderNode(element.props.children, options, context);
|
|
71
|
+
return element;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Parse a string to its corresponding JSX nodes in a given context.
|
|
75
|
+
*
|
|
76
|
+
* @param offset Keeps track of where we are within the wider string we're parsing when we're several calls deep.
|
|
77
|
+
*/
|
|
78
|
+
function* _parseString(input, options, context, offset = 0) {
|
|
79
|
+
let matchedRule = undefined;
|
|
80
|
+
let matchedPriority = Number.MIN_SAFE_INTEGER;
|
|
81
|
+
let matchedIndex = Number.MIN_SAFE_INTEGER;
|
|
82
|
+
let matchedLength = 0;
|
|
83
|
+
let matchedGroups = undefined;
|
|
84
|
+
// Loop through all rules in the list and see if any match.
|
|
85
|
+
for (const rule of options.rules) {
|
|
86
|
+
// Only apply this rule if both:
|
|
87
|
+
// 1. The priority is equal or higher to the current priority.
|
|
88
|
+
// 2. The rule is allowed in the current context.
|
|
89
|
+
const { priority = 0, match, contexts } = rule;
|
|
90
|
+
if (priority >= matchedPriority && contexts.includes(context)) {
|
|
91
|
+
const result = typeof match === "function" ? match(input, options) : match.exec(input);
|
|
92
|
+
if (result) {
|
|
93
|
+
// Use the match if it has length and is earlier in the string or is higher priority.
|
|
94
|
+
const { 0: { length } = "", index, groups } = result;
|
|
95
|
+
if (length && (index < matchedIndex || priority > matchedPriority)) {
|
|
96
|
+
matchedRule = rule;
|
|
97
|
+
matchedPriority = priority;
|
|
98
|
+
matchedIndex = index;
|
|
99
|
+
matchedLength = length;
|
|
100
|
+
matchedGroups = groups;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Did at least one rule match?
|
|
106
|
+
if (matchedRule && matchedLength) {
|
|
107
|
+
// If index is more than zero, then the string before the match may match another rule at lower priority.
|
|
108
|
+
const prefix = input.slice(0, matchedIndex);
|
|
109
|
+
if (prefix.length)
|
|
110
|
+
yield* _parseString(prefix, options, context, offset);
|
|
111
|
+
// Call the rule's `render()` function to generate the node.
|
|
112
|
+
// React gets annoyed if we don't set a `key:` property on lists of elements.
|
|
113
|
+
// 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.
|
|
114
|
+
// Trying to generate an incrementing number would require tracking the number and passing it back and forth through `_parseString()`
|
|
115
|
+
const { render, subcontext } = matchedRule;
|
|
116
|
+
const element = render(matchedGroups, options);
|
|
117
|
+
element.key = offset + matchedIndex;
|
|
118
|
+
yield subcontext ? _renderElement(element, options, subcontext) : element;
|
|
119
|
+
// Decrement the content.
|
|
120
|
+
const suffix = input.slice(matchedIndex + matchedLength);
|
|
121
|
+
if (suffix.length)
|
|
122
|
+
yield* _parseString(suffix, options, context, offset + matchedIndex + matchedLength);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// If nothing matched return the entire string..
|
|
126
|
+
yield input;
|
|
127
|
+
}
|
|
149
128
|
}
|
|
150
|
-
const defaults = {
|
|
151
|
-
rules: MARKUP_RULES,
|
|
152
|
-
context: "block",
|
|
153
|
-
url: undefined,
|
|
154
|
-
rel: undefined,
|
|
155
|
-
schemes: ["http:", "https:"],
|
|
156
|
-
};
|
package/markup/rules.d.ts
CHANGED
|
@@ -1,28 +1,78 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Data } from "../util/data.js";
|
|
2
|
+
import type { JSXElement } from "../util/jsx.js";
|
|
3
|
+
import { NamedRegExp, NamedRegExpData } from "../util/regexp.js";
|
|
4
|
+
import type { MarkupOptions } from "./options.js";
|
|
5
|
+
/** Subset of `NamedRegExpArray<T>` that are the only things we're required return from `match()` (because ) */
|
|
6
|
+
export declare type MarkupMatch<T extends Data | undefined> = {
|
|
7
|
+
0: string;
|
|
8
|
+
index: number;
|
|
9
|
+
groups: T;
|
|
10
|
+
};
|
|
11
|
+
/** Rule for parsing string markup into a JSX element. */
|
|
12
|
+
export interface MarkupRule<T extends Data | undefined = Data | undefined> {
|
|
13
|
+
/**
|
|
14
|
+
* Regular expression or custom matching function.
|
|
15
|
+
*/
|
|
16
|
+
readonly match: (T extends undefined ? RegExp : T extends NamedRegExpData ? NamedRegExp<T> : never) | ((input: string, options: MarkupOptions) => MarkupMatch<T> | null | void);
|
|
17
|
+
/**
|
|
18
|
+
* Render the JSX element for this rule using the props matched by
|
|
19
|
+
*/
|
|
20
|
+
readonly render: (orops: T, options: MarkupOptions) => JSXElement;
|
|
21
|
+
/**
|
|
22
|
+
* Contexts this rule should be applied in,
|
|
23
|
+
*
|
|
24
|
+
* @example `["block", "inline", "list"]`
|
|
25
|
+
*/
|
|
26
|
+
readonly contexts: string[];
|
|
27
|
+
/**
|
|
28
|
+
* Context that children should be rendered with.
|
|
29
|
+
*
|
|
30
|
+
* @example `"inline"` // Children of the element rendered by this rule will be parsed against markup rules applied in the "inline" context.
|
|
31
|
+
*/
|
|
32
|
+
readonly subcontext?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Priority for this rule (defaults to zero).
|
|
35
|
+
*
|
|
36
|
+
* @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>`.
|
|
37
|
+
* @example e.g. `<code>` rule is higher priority than other inlines so e.g. `<strong>` or `<em>` don't match inside a code block.
|
|
38
|
+
*/
|
|
39
|
+
readonly priority?: number;
|
|
40
|
+
}
|
|
41
|
+
/** Any markup rule. */
|
|
42
|
+
export declare type AnyMarkupRule = MarkupRule<any>;
|
|
43
|
+
/** A set of markup rules (as an object or array). */
|
|
44
|
+
export declare type MarkupRules = AnyMarkupRule[];
|
|
2
45
|
/**
|
|
3
46
|
* Headings are single line only (don't allow multiline).
|
|
4
47
|
* - 1-6 hashes then 1+ spaces, then the title.
|
|
5
48
|
* - Same as Markdown syntax.
|
|
6
49
|
* - Markdown's underline syntax is not supported (for simplification).
|
|
7
50
|
*/
|
|
8
|
-
export declare const
|
|
51
|
+
export declare const MATCH_HEADING: NamedRegExp<{
|
|
52
|
+
prefix: string;
|
|
53
|
+
heading: string;
|
|
54
|
+
}>;
|
|
55
|
+
export declare const HEADING_RULE: MarkupRule<{
|
|
56
|
+
level: number;
|
|
57
|
+
heading: string;
|
|
58
|
+
}>;
|
|
9
59
|
/**
|
|
10
60
|
* Horizontal rules
|
|
11
|
-
* - Same as Markdown syntax but also allows `•` bullet character (in addition to `-` dash, `+` plus, `*` asterisk,
|
|
61
|
+
* - Same as Markdown syntax but also allows `•` bullet character (in addition to `-` dash, `+` plus, `*` asterisk, `_` underscore).
|
|
12
62
|
* - Character must be repeated three (or more) times.
|
|
13
63
|
* - Character must be the same every time (can't mix)
|
|
14
64
|
* - Might have infinite number of spaces between the characters.
|
|
15
65
|
*/
|
|
16
66
|
export declare const HORIZONTAL_RULE: MarkupRule;
|
|
17
|
-
export declare const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
67
|
+
export declare const UNORDERED_RULE: MarkupRule<{
|
|
68
|
+
list: string;
|
|
69
|
+
}>;
|
|
70
|
+
export declare const ORDERED_RULE: MarkupRule<{
|
|
71
|
+
list: string;
|
|
72
|
+
}>;
|
|
73
|
+
export declare const BLOCKQUOTE_RULE: MarkupRule<{
|
|
74
|
+
quote: string;
|
|
75
|
+
}>;
|
|
26
76
|
/**
|
|
27
77
|
* Fenced code blocks
|
|
28
78
|
* - Same as Markdown syntax.
|
|
@@ -30,29 +80,49 @@ export declare const BLOCKQUOTE_RULE: MarkupRule;
|
|
|
30
80
|
* - If there's no closing fence the code block will run to the end of the current string.
|
|
31
81
|
* - Markdown-style four-space indent syntax is not supported (only fenced code, since it's easier to use).
|
|
32
82
|
*/
|
|
33
|
-
export declare const FENCED_CODE_RULE: MarkupRule
|
|
83
|
+
export declare const FENCED_CODE_RULE: MarkupRule<{
|
|
84
|
+
title?: string;
|
|
85
|
+
code: string;
|
|
86
|
+
}>;
|
|
34
87
|
/**
|
|
35
88
|
* Paragraph.
|
|
36
89
|
* - When ordering rules, paragraph should go after other "block" context elements (because it has a very generous capture).
|
|
37
90
|
*/
|
|
38
|
-
export declare const PARAGRAPH_RULE: MarkupRule
|
|
91
|
+
export declare const PARAGRAPH_RULE: MarkupRule<{
|
|
92
|
+
paragraph: string;
|
|
93
|
+
}>;
|
|
39
94
|
/**
|
|
40
|
-
*
|
|
41
|
-
* -
|
|
95
|
+
* Autolinked URL starts with `http:` or `https:` or `mailto:` (any scheme in `options.schemes`) and matches an unlimited number of non-space characters.
|
|
96
|
+
* - 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).
|
|
42
97
|
* - If no title is specified a cleaned up version of the URL will be used, e.g. `google.com/maps`
|
|
43
|
-
* - Does not need space before/after the link.
|
|
44
98
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
45
|
-
* - For security only
|
|
99
|
+
* - For security only schemes that appear in `options.schemes` will match (defaults to `http:` and `https:`).
|
|
46
100
|
*/
|
|
47
|
-
export declare const
|
|
101
|
+
export declare const URL_CHAR = "[-$_@.&!*,=;/#?:%a-zA-Z0-9]";
|
|
102
|
+
export declare const URL_MATCH: NamedRegExp<{
|
|
103
|
+
title?: string;
|
|
104
|
+
href: string;
|
|
105
|
+
}>;
|
|
106
|
+
export declare const URL_RULE: MarkupRule<{
|
|
107
|
+
title: string;
|
|
108
|
+
href: string;
|
|
109
|
+
}>;
|
|
48
110
|
/**
|
|
49
|
-
*
|
|
50
|
-
* -
|
|
111
|
+
* Markdown-style link.
|
|
112
|
+
* - Link in standard Markdown format, e.g. `[Google Maps](http://google.com/maps)`
|
|
51
113
|
* - If no title is specified a cleaned up version of the URL will be used, e.g. `google.com/maps`
|
|
114
|
+
* - Does not need space before/after the link.
|
|
52
115
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
53
|
-
* - For security only
|
|
116
|
+
* - For security only `http://` or `https://` links will work (if invalid the unparsed text will be returned).
|
|
54
117
|
*/
|
|
55
|
-
export declare const
|
|
118
|
+
export declare const LINK_MATCH: NamedRegExp<{
|
|
119
|
+
title: string;
|
|
120
|
+
href: string;
|
|
121
|
+
}>;
|
|
122
|
+
export declare const LINK_RULE: MarkupRule<{
|
|
123
|
+
title: string;
|
|
124
|
+
href: string;
|
|
125
|
+
}>;
|
|
56
126
|
/**
|
|
57
127
|
* Inline code.
|
|
58
128
|
* - Text surrounded by one or more "`" backtick tilde characters.
|
|
@@ -60,7 +130,9 @@ export declare const AUTOLINK_RULE: MarkupRule;
|
|
|
60
130
|
* - Closing characters must exactly match opening characters.
|
|
61
131
|
* - Same as Markdown syntax.
|
|
62
132
|
*/
|
|
63
|
-
export declare const CODE_RULE: MarkupRule
|
|
133
|
+
export declare const CODE_RULE: MarkupRule<{
|
|
134
|
+
text: string;
|
|
135
|
+
}>;
|
|
64
136
|
/**
|
|
65
137
|
* Inline strong.
|
|
66
138
|
* - Inline text wrapped in one or more `*` asterisks.
|
|
@@ -69,7 +141,9 @@ export declare const CODE_RULE: MarkupRule;
|
|
|
69
141
|
* - Closing characters must exactly match opening characters.
|
|
70
142
|
* - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
|
|
71
143
|
*/
|
|
72
|
-
export declare const STRONG_RULE: MarkupRule
|
|
144
|
+
export declare const STRONG_RULE: MarkupRule<{
|
|
145
|
+
text: string;
|
|
146
|
+
}>;
|
|
73
147
|
/**
|
|
74
148
|
* Inline emphasis.
|
|
75
149
|
* - Inline text wrapped in one or more `_` underscore symbols.
|
|
@@ -78,16 +152,20 @@ export declare const STRONG_RULE: MarkupRule;
|
|
|
78
152
|
* - Closing characters must exactly match opening characters.
|
|
79
153
|
* - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
|
|
80
154
|
*/
|
|
81
|
-
export declare const EMPHASIS_RULE: MarkupRule
|
|
155
|
+
export declare const EMPHASIS_RULE: MarkupRule<{
|
|
156
|
+
text: string;
|
|
157
|
+
}>;
|
|
82
158
|
/**
|
|
83
159
|
* Inserted text (`<ins>` tag),
|
|
84
160
|
* - Inline text wrapped in two or more `++` pluses.
|
|
85
|
-
* - Works inside words (e.g. `magi
|
|
161
|
+
* - Works inside words (e.g. `magi++karp++carp`).
|
|
86
162
|
* - Whitespace cannot be the first or last character of the element (e.g. `+ abc +` will not work).
|
|
87
163
|
* - Closing characters must exactly match opening characters.
|
|
88
164
|
* - Markdown doesn't have this.
|
|
89
165
|
*/
|
|
90
|
-
export declare const INSERT_RULE: MarkupRule
|
|
166
|
+
export declare const INSERT_RULE: MarkupRule<{
|
|
167
|
+
text: string;
|
|
168
|
+
}>;
|
|
91
169
|
/**
|
|
92
170
|
* Deleted text (`<del>` tag),
|
|
93
171
|
* - Inline text wrapped in two or more `--` hyphens or `~~` tildes.
|
|
@@ -96,7 +174,9 @@ export declare const INSERT_RULE: MarkupRule;
|
|
|
96
174
|
* - Closing characters must exactly match opening characters.
|
|
97
175
|
* - Markdown doesn't have this.
|
|
98
176
|
*/
|
|
99
|
-
export declare const DELETE_RULE: MarkupRule
|
|
177
|
+
export declare const DELETE_RULE: MarkupRule<{
|
|
178
|
+
text: string;
|
|
179
|
+
}>;
|
|
100
180
|
/**
|
|
101
181
|
* Hard linebreak (`<br />` tag).
|
|
102
182
|
* - Any line break in a paragraph will become a hard `<br />` tag.
|
|
@@ -115,10 +195,10 @@ export declare const LINEBREAK_RULE: MarkupRule;
|
|
|
115
195
|
* 3. more aligned with smaller textboxes and editors that have line wrapping
|
|
116
196
|
* - HTML tags and character entities are never allowed (our use cases generally require a locked-down subset of syntax).
|
|
117
197
|
*/
|
|
118
|
-
export declare const MARKUP_RULES:
|
|
198
|
+
export declare const MARKUP_RULES: MarkupRules;
|
|
119
199
|
/** Subset of markup rules that work in a block context. */
|
|
120
|
-
export declare const MARKUP_RULES_BLOCK:
|
|
200
|
+
export declare const MARKUP_RULES_BLOCK: MarkupRules;
|
|
121
201
|
/** Subset of markup rules that work in an inline context. */
|
|
122
|
-
export declare const MARKUP_RULES_INLINE:
|
|
202
|
+
export declare const MARKUP_RULES_INLINE: MarkupRules;
|
|
123
203
|
/** Subset of markup rules that are relevant for collapsed shortform content. */
|
|
124
|
-
export declare const MARKUP_RULES_SHORTFORM:
|
|
204
|
+
export declare const MARKUP_RULES_SHORTFORM: MarkupRules;
|