shelving 1.86.0 → 1.86.2
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.d.ts +2 -2
- package/constraint/Constraints.js +7 -6
- package/constraint/FilterConstraint.d.ts +4 -4
- package/constraint/FilterConstraint.js +17 -17
- package/constraint/QueryConstraints.d.ts +1 -1
- package/constraint/QueryConstraints.js +8 -8
- package/constraint/SortConstraint.d.ts +4 -4
- package/constraint/SortConstraint.js +3 -3
- package/db/Change.d.ts +4 -4
- package/db/Item.d.ts +4 -4
- package/error/ThroughError.js +1 -1
- package/markup/index.d.ts +1 -0
- package/markup/index.js +1 -0
- package/markup/options.d.ts +2 -2
- package/markup/regexp.d.ts +2 -2
- package/markup/render.js +19 -20
- package/markup/rule.d.ts +80 -0
- package/markup/rule.js +63 -0
- package/markup/rules.d.ts +17 -67
- package/markup/rules.js +88 -160
- package/package.json +17 -17
- package/react/useItem.js +10 -10
- package/react/useQuery.js +29 -29
- package/schema/AllowSchema.d.ts +4 -4
- package/schema/AllowSchema.js +3 -3
- package/schema/ArraySchema.d.ts +1 -1
- package/schema/BooleanSchema.d.ts +1 -1
- package/schema/DataSchema.d.ts +2 -2
- package/schema/DateSchema.d.ts +1 -1
- package/schema/DictionarySchema.d.ts +1 -1
- package/schema/LinkSchema.d.ts +1 -1
- package/schema/NumberSchema.d.ts +1 -1
- package/schema/NumberSchema.js +4 -4
- package/schema/Schema.d.ts +1 -1
- package/schema/StringSchema.d.ts +3 -3
- package/schema/ThroughSchema.d.ts +1 -1
- package/schema/TimeSchema.d.ts +1 -1
- package/state/State.d.ts +1 -1
- package/state/State.js +6 -6
- package/test/basics.d.ts +2 -2
- package/test/index.d.ts +1 -1
- package/test/people.d.ts +2 -2
- package/update/ArrayUpdate.d.ts +2 -1
- package/update/ArrayUpdate.js +9 -8
- package/update/DataUpdate.d.ts +1 -1
- package/update/DataUpdate.js +7 -6
- package/update/DictionaryUpdate.d.ts +1 -1
- package/update/DictionaryUpdate.js +8 -7
- package/util/array.d.ts +4 -4
- package/util/async.js +8 -8
- package/util/class.d.ts +3 -3
- package/util/clone.js +4 -3
- package/util/color.d.ts +2 -2
- package/util/color.js +6 -6
- package/util/data.d.ts +7 -14
- package/util/data.js +1 -19
- package/util/date.d.ts +3 -3
- package/util/debug.d.ts +1 -0
- package/util/debug.js +5 -5
- package/util/dictionary.d.ts +5 -5
- package/util/duration.d.ts +17 -0
- package/util/duration.js +52 -0
- package/util/entry.d.ts +3 -3
- package/util/equal.js +6 -3
- package/util/function.d.ts +8 -8
- package/util/hydrate.d.ts +3 -3
- package/util/hydrate.js +2 -2
- package/util/index.d.ts +1 -0
- package/util/index.js +1 -0
- package/util/iterate.d.ts +1 -1
- package/util/jsx.d.ts +3 -3
- package/util/lazy.d.ts +1 -1
- package/util/map.d.ts +15 -19
- package/util/map.js +11 -13
- package/util/match.d.ts +2 -2
- package/util/merge.d.ts +1 -1
- package/util/null.d.ts +1 -1
- package/util/number.d.ts +4 -2
- package/util/number.js +8 -6
- package/util/object.d.ts +24 -11
- package/util/object.js +30 -4
- package/util/regexp.d.ts +2 -3
- package/util/serialise.js +2 -2
- package/util/set.d.ts +4 -4
- package/util/sort.d.ts +2 -2
- package/util/source.js +2 -2
- package/util/string.d.ts +1 -1
- package/util/string.js +3 -3
- package/util/template.d.ts +2 -2
- package/util/template.js +9 -9
- package/util/time.d.ts +2 -2
- package/util/time.js +3 -3
- package/util/transform.d.ts +5 -5
- package/util/units.d.ts +39 -50
- package/util/units.js +36 -74
- package/util/url.d.ts +2 -2
- package/util/validate.d.ts +5 -5
package/api/Resource.d.ts
CHANGED
|
@@ -27,9 +27,9 @@ export declare class Resource<P = unknown, R = void> implements Validatable<R> {
|
|
|
27
27
|
validate(unsafeResult: unknown): R;
|
|
28
28
|
}
|
|
29
29
|
/** Extract the payload type from a `Resource`. */
|
|
30
|
-
export
|
|
30
|
+
export type PayloadType<X extends Resource> = X extends Resource<infer Y, unknown> ? Y : never;
|
|
31
31
|
/** Extract the result type from a `Resource`. */
|
|
32
|
-
export
|
|
32
|
+
export type ResourceType<X extends Resource> = X extends Resource<unknown, infer Y> ? Y : never;
|
|
33
33
|
/**
|
|
34
34
|
* Shortcut to create a new `Resource` (consistent with `Schema` shortcuts.
|
|
35
35
|
* - Sets `undefined` as the default type for payload and result.
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
+
import { getPrototype } from "../util/object.js";
|
|
1
2
|
import { withArrayItems, omitArrayItems } from "../util/array.js";
|
|
2
3
|
import { Constraint } from "./Constraint.js";
|
|
3
4
|
/** Type of Rule that is powered by several sub-constraints (e.g. `Filters` and `Sorts` and `Query` itself extend this). */
|
|
4
5
|
export class Constraints extends Constraint {
|
|
5
|
-
constructor(...constraints) {
|
|
6
|
-
super();
|
|
7
|
-
this._constraints = constraints;
|
|
8
|
-
}
|
|
9
6
|
/** Get the first constraint. */
|
|
10
7
|
get first() {
|
|
11
8
|
return this._constraints[0];
|
|
@@ -18,15 +15,19 @@ export class Constraints extends Constraint {
|
|
|
18
15
|
get size() {
|
|
19
16
|
return this._constraints.length;
|
|
20
17
|
}
|
|
18
|
+
constructor(...constraints) {
|
|
19
|
+
super();
|
|
20
|
+
this._constraints = constraints;
|
|
21
|
+
}
|
|
21
22
|
/** Clone this set of constraints but add additional constraints. */
|
|
22
23
|
with(...constraints) {
|
|
23
24
|
const _constraints = withArrayItems(this._constraints, ...constraints);
|
|
24
|
-
return _constraints !== this._constraints ? { __proto__:
|
|
25
|
+
return _constraints !== this._constraints ? { __proto__: getPrototype(this), ...this, _constraints: _constraints } : this;
|
|
25
26
|
}
|
|
26
27
|
/** Clone this set of constraints but remove specific constraints. */
|
|
27
28
|
omit(...constraints) {
|
|
28
29
|
const _constraints = omitArrayItems(this._constraints, ...constraints);
|
|
29
|
-
return _constraints !== this._constraints ? { __proto__:
|
|
30
|
+
return _constraints !== this._constraints ? { __proto__: getPrototype(this), ...this, _constraints: _constraints } : this;
|
|
30
31
|
}
|
|
31
32
|
/** Iterate over the constraints. */
|
|
32
33
|
[Symbol.iterator]() {
|
|
@@ -4,11 +4,11 @@ import { ImmutableArray } from "../util/array.js";
|
|
|
4
4
|
import { Matchable } from "../util/match.js";
|
|
5
5
|
import type { Constraint } from "./Constraint.js";
|
|
6
6
|
/** Possible operator references. */
|
|
7
|
-
export
|
|
7
|
+
export type FilterOperator = "IS" | "NOT" | "IN" | "OUT" | "CONTAINS" | "LT" | "LTE" | "GT" | "GTE";
|
|
8
8
|
/** Format that allows filters to be specified as a string, e.g. `!name` means `name is not` and `age>` means `age is more than` and `tags[]` means `tags array contains` */
|
|
9
|
-
export
|
|
9
|
+
export type FilterKey<T extends Data> = DataKey<T> | `${DataKey<T>}` | `!${DataKey<T>}` | `${DataKey<T>}[]` | `${DataKey<T>}<` | `${DataKey<T>}<=` | `${DataKey<T>}>` | `${DataKey<T>}>=`;
|
|
10
10
|
/** Format that allows multiple filters to be specified as a plain object. */
|
|
11
|
-
export
|
|
11
|
+
export type FilterProps<T extends Data> = {
|
|
12
12
|
[K in DataKey<T> as `${K}` | `!${K}`]?: T[K] | ImmutableArray<T[K]>;
|
|
13
13
|
} & {
|
|
14
14
|
[K in DataKey<T> as `${K}[]`]?: Required<T>[K] extends ImmutableArray<infer X> ? X : never;
|
|
@@ -16,7 +16,7 @@ export declare type FilterProps<T extends Data> = {
|
|
|
16
16
|
[K in DataKey<T> as `${K}<` | `${K}<=` | `${K}>` | `${K}>=`]?: T[K];
|
|
17
17
|
};
|
|
18
18
|
/** List of filters in a flexible format. */
|
|
19
|
-
export
|
|
19
|
+
export type FilterList<T extends Data> = Nullish<FilterProps<T> | FilterConstraint<T>> | Iterable<FilterList<T>>;
|
|
20
20
|
/**
|
|
21
21
|
* Filter: filters a list of data.
|
|
22
22
|
*
|
|
@@ -21,6 +21,23 @@ const MATCHERS = {
|
|
|
21
21
|
* @param value Value the specified property should be matched against.
|
|
22
22
|
*/
|
|
23
23
|
export class FilterConstraint {
|
|
24
|
+
get filterKey() {
|
|
25
|
+
const { operator, key } = this;
|
|
26
|
+
if (operator === "NOT" || operator === "OUT")
|
|
27
|
+
return `!${key}`;
|
|
28
|
+
else if (operator === "CONTAINS")
|
|
29
|
+
return `${key}[]`;
|
|
30
|
+
else if (operator === "LT")
|
|
31
|
+
return `${key}<`;
|
|
32
|
+
else if (operator === "LTE")
|
|
33
|
+
return `${key}<=`;
|
|
34
|
+
else if (operator === "GT")
|
|
35
|
+
return `${key}>`;
|
|
36
|
+
else if (operator === "GTE")
|
|
37
|
+
return `${key}>=`;
|
|
38
|
+
else
|
|
39
|
+
return key;
|
|
40
|
+
}
|
|
24
41
|
constructor(filterKey, value) {
|
|
25
42
|
if (filterKey.startsWith("!")) {
|
|
26
43
|
this.key = filterKey.slice(1);
|
|
@@ -52,23 +69,6 @@ export class FilterConstraint {
|
|
|
52
69
|
}
|
|
53
70
|
this.value = value;
|
|
54
71
|
}
|
|
55
|
-
get filterKey() {
|
|
56
|
-
const { operator, key } = this;
|
|
57
|
-
if (operator === "NOT" || operator === "OUT")
|
|
58
|
-
return `!${key}`;
|
|
59
|
-
else if (operator === "CONTAINS")
|
|
60
|
-
return `${key}[]`;
|
|
61
|
-
else if (operator === "LT")
|
|
62
|
-
return `${key}<`;
|
|
63
|
-
else if (operator === "LTE")
|
|
64
|
-
return `${key}<=`;
|
|
65
|
-
else if (operator === "GT")
|
|
66
|
-
return `${key}>`;
|
|
67
|
-
else if (operator === "GTE")
|
|
68
|
-
return `${key}>=`;
|
|
69
|
-
else
|
|
70
|
-
return key;
|
|
71
|
-
}
|
|
72
72
|
match(item) {
|
|
73
73
|
return MATCHERS[this.operator](item[this.key], this.value);
|
|
74
74
|
}
|
|
@@ -31,7 +31,7 @@ export declare class QueryConstraints<T extends Data = Data> extends Constraint<
|
|
|
31
31
|
readonly filters: FilterConstraints<T>;
|
|
32
32
|
readonly sorts: SortConstraints<T>;
|
|
33
33
|
readonly limit: number | null;
|
|
34
|
-
constructor(filters?: FilterList<Partial<T
|
|
34
|
+
constructor(filters?: FilterList<Partial<T>> | FilterConstraints<T>, sorts?: SortList<Partial<T>> | SortConstraints<T>, limit?: number | null);
|
|
35
35
|
filter(...filters: FilterList<Partial<T>>[]): this;
|
|
36
36
|
get unfilter(): this;
|
|
37
37
|
match(item: T): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getProp } from "../util/object.js";
|
|
1
|
+
import { getProp, getPrototype } from "../util/object.js";
|
|
2
2
|
import { assert } from "../util/assert.js";
|
|
3
3
|
import { limitArray } from "../util/array.js";
|
|
4
4
|
import { FilterConstraints } from "./FilterConstraints.js";
|
|
@@ -19,7 +19,7 @@ export class QueryConstraints extends Constraint {
|
|
|
19
19
|
// Implement `Filterable`
|
|
20
20
|
filter(...filters) {
|
|
21
21
|
return {
|
|
22
|
-
__proto__:
|
|
22
|
+
__proto__: getPrototype(this),
|
|
23
23
|
...this,
|
|
24
24
|
filters: this.filters.filter(...filters),
|
|
25
25
|
};
|
|
@@ -28,7 +28,7 @@ export class QueryConstraints extends Constraint {
|
|
|
28
28
|
if (!this.filters.size)
|
|
29
29
|
return this;
|
|
30
30
|
return {
|
|
31
|
-
__proto__:
|
|
31
|
+
__proto__: getPrototype(this),
|
|
32
32
|
...this,
|
|
33
33
|
filters: EMPTY_FILTERS,
|
|
34
34
|
};
|
|
@@ -39,7 +39,7 @@ export class QueryConstraints extends Constraint {
|
|
|
39
39
|
// Implement `Sortable`
|
|
40
40
|
sort(...sorts) {
|
|
41
41
|
return {
|
|
42
|
-
__proto__:
|
|
42
|
+
__proto__: getPrototype(this),
|
|
43
43
|
...this,
|
|
44
44
|
sorts: this.sorts.sort(...sorts),
|
|
45
45
|
};
|
|
@@ -48,7 +48,7 @@ export class QueryConstraints extends Constraint {
|
|
|
48
48
|
if (!this.sorts.size)
|
|
49
49
|
return this;
|
|
50
50
|
return {
|
|
51
|
-
__proto__:
|
|
51
|
+
__proto__: getPrototype(this),
|
|
52
52
|
...this,
|
|
53
53
|
sorts: EMPTY_SORTS,
|
|
54
54
|
};
|
|
@@ -59,14 +59,14 @@ export class QueryConstraints extends Constraint {
|
|
|
59
59
|
// Implement `Queryable`
|
|
60
60
|
after(item) {
|
|
61
61
|
return {
|
|
62
|
-
__proto__:
|
|
62
|
+
__proto__: getPrototype(this),
|
|
63
63
|
...this,
|
|
64
64
|
filters: this.filters.with(..._getAfterFilters(this.sorts, item)),
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
before(item) {
|
|
68
68
|
return {
|
|
69
|
-
__proto__:
|
|
69
|
+
__proto__: getPrototype(this),
|
|
70
70
|
...this,
|
|
71
71
|
filters: this.filters.with(..._getBeforeFilters(this.sorts, item)),
|
|
72
72
|
};
|
|
@@ -75,7 +75,7 @@ export class QueryConstraints extends Constraint {
|
|
|
75
75
|
if (this.limit === limit)
|
|
76
76
|
return this;
|
|
77
77
|
return {
|
|
78
|
-
__proto__:
|
|
78
|
+
__proto__: getPrototype(this),
|
|
79
79
|
...this,
|
|
80
80
|
limit,
|
|
81
81
|
};
|
|
@@ -4,13 +4,13 @@ import type { Nullish } from "../util/null.js";
|
|
|
4
4
|
import { Rankable } from "../util/sort.js";
|
|
5
5
|
import type { Constraint } from "./Constraint.js";
|
|
6
6
|
/** Format that allows sorts to be set as a plain string, e.g. `name` sorts by name in ascending order and `!date` sorts by date in descending order. */
|
|
7
|
-
export
|
|
7
|
+
export type SortKey<T extends Data> = DataKey<T> | `${DataKey<T>}` | `!${DataKey<T>}`;
|
|
8
8
|
/** One or more sort keys. */
|
|
9
|
-
export
|
|
9
|
+
export type SortKeys<T extends Data> = SortKey<T> | ImmutableArray<SortKey<T>>;
|
|
10
10
|
/** Possible operator references. */
|
|
11
|
-
export
|
|
11
|
+
export type SortDirection = "ASC" | "DESC";
|
|
12
12
|
/** List of sorts in a flexible format. */
|
|
13
|
-
export
|
|
13
|
+
export type SortList<T extends Data> = Nullish<SortKeys<T> | SortConstraint<T> | Iterable<SortList<T>>>;
|
|
14
14
|
/** Sort a list of values. */
|
|
15
15
|
export declare class SortConstraint<T extends Data = Data> implements Constraint<T>, Rankable<T> {
|
|
16
16
|
readonly key: string;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { rank, rankAsc, rankDesc, sortItems } from "../util/sort.js";
|
|
2
2
|
/** Sort a list of values. */
|
|
3
3
|
export class SortConstraint {
|
|
4
|
+
get sortKey() {
|
|
5
|
+
return `"${this.direction === "DESC" ? "!" : ""}${this.key}"`;
|
|
6
|
+
}
|
|
4
7
|
constructor(sortKey) {
|
|
5
8
|
if (sortKey.startsWith("!")) {
|
|
6
9
|
this.key = sortKey.slice(1);
|
|
@@ -11,9 +14,6 @@ export class SortConstraint {
|
|
|
11
14
|
this.direction = "ASC";
|
|
12
15
|
}
|
|
13
16
|
}
|
|
14
|
-
get sortKey() {
|
|
15
|
-
return `"${this.direction === "DESC" ? "!" : ""}${this.key}"`;
|
|
16
|
-
}
|
|
17
17
|
rank(left, right) {
|
|
18
18
|
return rank(left[this.key], this.direction === "ASC" ? rankAsc : rankDesc, right[this.key]);
|
|
19
19
|
}
|
package/db/Change.d.ts
CHANGED
|
@@ -33,13 +33,13 @@ export interface DeleteChange<T extends Datas, K extends DataKey<T> = DataKey<T>
|
|
|
33
33
|
readonly id: string;
|
|
34
34
|
}
|
|
35
35
|
/** Set, update, or delete change on an item. */
|
|
36
|
-
export
|
|
36
|
+
export type ItemChange<T extends Datas, K extends DataKey<T> = DataKey<T>> = SetChange<T, K> | UpdateChange<T, K> | DeleteChange<T, K>;
|
|
37
37
|
/** Array of item changes. */
|
|
38
|
-
export
|
|
38
|
+
export type ItemChanges<T extends Datas, K extends DataKey<T> = DataKey<T>> = ImmutableArray<ItemChange<T, K>>;
|
|
39
39
|
/** Write change on an item. */
|
|
40
|
-
export
|
|
40
|
+
export type WriteChange<T extends Datas, K extends DataKey<T> = DataKey<T>> = ItemChange<T, K> | AddChange<T, K>;
|
|
41
41
|
/** Array of write changes. */
|
|
42
|
-
export
|
|
42
|
+
export type WriteChanges<T extends Datas, K extends DataKey<T> = DataKey<T>> = ImmutableArray<WriteChange<T, K>>;
|
|
43
43
|
/** Apply a set of changes to a synchronous provider. */
|
|
44
44
|
export declare function changeProvider<T extends Datas, K extends DataKey<T>>(provider: Provider<T>, ...changes: DeepIterable<Nullish<WriteChange<T, K>>>[]): ItemChanges<T, K>;
|
|
45
45
|
/** Apply a set of changes to an asynchronous provider. */
|
package/db/Item.d.ts
CHANGED
|
@@ -7,15 +7,15 @@ import type { DeleteChange, SetChange, UpdateChange } from "./Change.js";
|
|
|
7
7
|
import type { AsyncQuery, Query } from "./Query.js";
|
|
8
8
|
import type { AsyncDatabase, Database } from "./Database.js";
|
|
9
9
|
/** Item data with a string ID that uniquely identifies it. */
|
|
10
|
-
export
|
|
10
|
+
export type ItemData<T extends Data = Data> = T & {
|
|
11
11
|
id: string;
|
|
12
12
|
};
|
|
13
13
|
/** Entity or `null` to indicate the item doesn't exist. */
|
|
14
|
-
export
|
|
14
|
+
export type ItemValue<T extends Data = Data> = ItemData<T> | null;
|
|
15
15
|
/** An array of item data. */
|
|
16
|
-
export
|
|
16
|
+
export type ItemArray<T extends Data = Data> = ImmutableArray<ItemData<T>>;
|
|
17
17
|
/** A set of query constraints for item data. */
|
|
18
|
-
export
|
|
18
|
+
export type ItemConstraints<T extends Data = Data> = QueryConstraints<ItemData<T>>;
|
|
19
19
|
/** Reference to an item in a synchronous or asynchronous database. */
|
|
20
20
|
declare abstract class BaseItem<T extends Datas = Datas, K extends DataKey<T> = DataKey<T>> implements AsyncIterable<ItemValue<T[K]>> {
|
|
21
21
|
abstract readonly db: Database<T> | AsyncDatabase<T>;
|
package/error/ThroughError.js
CHANGED
|
@@ -7,7 +7,7 @@ export class ThroughError extends Error {
|
|
|
7
7
|
constructor(message, cause) {
|
|
8
8
|
super(message);
|
|
9
9
|
this.cause = cause;
|
|
10
|
-
this.stack = `${this.stack}\nCause: ${cause instanceof Error ? cause.stack : debug(cause)}`;
|
|
10
|
+
this.stack = `${this.stack || ""}\nCause: ${cause instanceof Error ? cause.stack || "" : debug(cause)}`;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
ThroughError.prototype.name = "ThroughError";
|
package/markup/index.d.ts
CHANGED
package/markup/index.js
CHANGED
package/markup/options.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { MarkupRules } from "./
|
|
1
|
+
import type { MarkupRules } from "./rule.js";
|
|
2
2
|
/** The current parsing options (represents the current state of the parsing). */
|
|
3
|
-
export
|
|
3
|
+
export type MarkupOptions = {
|
|
4
4
|
/** The active list of parsing rules. */
|
|
5
5
|
readonly rules: MarkupRules;
|
|
6
6
|
/** The initial context to start parsing in (rules may render their children with a different context). */
|
package/markup/regexp.d.ts
CHANGED
|
@@ -2,13 +2,13 @@ import type { Data } from "../util/data.js";
|
|
|
2
2
|
import { NamedRegExp, NamedRegExpData, PossibleRegExp } from "../util/regexp.js";
|
|
3
3
|
import type { MarkupOptions } from "./options.js";
|
|
4
4
|
/** Subset of `NamedRegExpArray<T>` that are the only things we're required return from a `MarkupMatcher` function. */
|
|
5
|
-
export
|
|
5
|
+
export type MarkupMatch<T extends Data | undefined> = {
|
|
6
6
|
0: string;
|
|
7
7
|
index: number;
|
|
8
8
|
groups: T;
|
|
9
9
|
};
|
|
10
10
|
/** Function that matches a string and returns a `MarkupMatch` or `null` or `void` */
|
|
11
|
-
export
|
|
11
|
+
export type MarkupMatcher<T extends Data> = (input: string, options: MarkupOptions) => MarkupMatch<T> | null | void;
|
|
12
12
|
export declare const LINE_REGEXP: RegExp;
|
|
13
13
|
export declare const LINE_START_REGEXP: RegExp;
|
|
14
14
|
export declare const LINE_END_REGEXP: RegExp;
|
package/markup/render.js
CHANGED
|
@@ -77,49 +77,48 @@ function _renderElement(element, options, context) {
|
|
|
77
77
|
*/
|
|
78
78
|
function* _parseString(input, options, context, offset = 0) {
|
|
79
79
|
let matchedRule = undefined;
|
|
80
|
-
let
|
|
81
|
-
let
|
|
82
|
-
let
|
|
83
|
-
let matchedGroups = undefined;
|
|
80
|
+
let matchedResult = undefined;
|
|
81
|
+
let highPriority = Number.MIN_SAFE_INTEGER;
|
|
82
|
+
let lowIndex = Number.MIN_SAFE_INTEGER;
|
|
84
83
|
// Loop through all rules in the list and see if any match.
|
|
85
84
|
for (const rule of options.rules) {
|
|
86
85
|
// Only apply this rule if both:
|
|
87
86
|
// 1. The priority is equal or higher to the current priority.
|
|
88
87
|
// 2. The rule is allowed in the current context.
|
|
89
|
-
const { priority
|
|
90
|
-
if (priority >=
|
|
91
|
-
const result =
|
|
88
|
+
const { priority } = rule;
|
|
89
|
+
if (rule.priority >= highPriority && rule.contexts.includes(context)) {
|
|
90
|
+
const result = rule.match(input, options);
|
|
92
91
|
if (result) {
|
|
93
92
|
// Use the match if it has length and is earlier in the string or is higher priority.
|
|
94
|
-
const { 0: { length } = "", index
|
|
95
|
-
if (length && (index <
|
|
93
|
+
const { 0: { length } = "", index } = result;
|
|
94
|
+
if (length && (index < lowIndex || rule.priority > highPriority)) {
|
|
96
95
|
matchedRule = rule;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
matchedGroups = groups;
|
|
96
|
+
matchedResult = result;
|
|
97
|
+
highPriority = priority;
|
|
98
|
+
lowIndex = index;
|
|
101
99
|
}
|
|
102
100
|
}
|
|
103
101
|
}
|
|
104
102
|
}
|
|
105
103
|
// Did at least one rule match?
|
|
106
|
-
if (matchedRule &&
|
|
104
|
+
if (matchedRule && matchedResult) {
|
|
107
105
|
// 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,
|
|
106
|
+
const prefix = input.slice(0, lowIndex);
|
|
109
107
|
if (prefix.length)
|
|
110
108
|
yield* _parseString(prefix, options, context, offset);
|
|
111
109
|
// Call the rule's `render()` function to generate the node.
|
|
112
110
|
// React gets annoyed if we don't set a `key:` property on lists of elements.
|
|
113
111
|
// 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
112
|
// Trying to generate an incrementing number would require tracking the number and passing it back and forth through `_parseString()`
|
|
115
|
-
const {
|
|
116
|
-
const element = render(
|
|
117
|
-
element.key = offset +
|
|
113
|
+
const { 0: { length } = "", groups, index } = matchedResult;
|
|
114
|
+
const element = matchedRule.render(groups, options);
|
|
115
|
+
element.key = offset + index;
|
|
116
|
+
const { subcontext } = matchedRule;
|
|
118
117
|
yield subcontext ? _renderElement(element, options, subcontext) : element;
|
|
119
118
|
// Decrement the content.
|
|
120
|
-
const suffix = input.slice(
|
|
119
|
+
const suffix = input.slice(index + length);
|
|
121
120
|
if (suffix.length)
|
|
122
|
-
yield* _parseString(suffix, options, context, offset +
|
|
121
|
+
yield* _parseString(suffix, options, context, offset + index + length);
|
|
123
122
|
}
|
|
124
123
|
else {
|
|
125
124
|
// If nothing matched return the entire string..
|
package/markup/rule.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NamedRegExp, NamedRegExpArray, NamedRegExpData } from "../util/regexp.js";
|
|
2
|
+
import { Data } from "../util/data.js";
|
|
3
|
+
import { JSXElement } from "../util/jsx.js";
|
|
4
|
+
import { ImmutableArray } from "../util/array.js";
|
|
5
|
+
import { MarkupOptions } from "./options.js";
|
|
6
|
+
export type MarkupRuleMatch<T extends Data = Data> = {
|
|
7
|
+
0: string;
|
|
8
|
+
index: number;
|
|
9
|
+
groups?: T;
|
|
10
|
+
};
|
|
11
|
+
export declare abstract class MarkupRule {
|
|
12
|
+
/**
|
|
13
|
+
* Contexts this rule should be applied in,
|
|
14
|
+
*
|
|
15
|
+
* @example `["block", "inline", "list"]`
|
|
16
|
+
*/
|
|
17
|
+
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
|
+
/**
|
|
25
|
+
* Priority for this rule (defaults to zero).
|
|
26
|
+
*
|
|
27
|
+
* @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
|
+
* @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
|
+
*/
|
|
30
|
+
readonly priority: number;
|
|
31
|
+
constructor(contexts: ImmutableArray<string>, //
|
|
32
|
+
subcontext?: string | null, priority?: number);
|
|
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;
|
|
37
|
+
}
|
|
38
|
+
export declare class RegExpMarkupRule extends MarkupRule {
|
|
39
|
+
private readonly _regexp;
|
|
40
|
+
private readonly _render;
|
|
41
|
+
constructor(_regexp: RegExp, //
|
|
42
|
+
_render: (props: Data, options: MarkupOptions) => JSXElement, contexts: ImmutableArray<string>, subcontext?: string | null, priority?: number);
|
|
43
|
+
match(input: string): MarkupRuleMatch | null;
|
|
44
|
+
render(props: Data, options: MarkupOptions): JSXElement;
|
|
45
|
+
}
|
|
46
|
+
export declare class NamedRegExpMarkupRule<T extends NamedRegExpData> extends MarkupRule {
|
|
47
|
+
private readonly _regexp;
|
|
48
|
+
private readonly _render;
|
|
49
|
+
constructor(_regexp: NamedRegExp<T>, //
|
|
50
|
+
_render: (props: T, options: MarkupOptions) => JSXElement, contexts: ImmutableArray<string>, subcontext?: string | null, priority?: number);
|
|
51
|
+
match(input: string): NamedRegExpArray<T> | null;
|
|
52
|
+
render(props: T, options: MarkupOptions): JSXElement;
|
|
53
|
+
}
|
|
54
|
+
export declare class LinkRegExpMarkupRule extends MarkupRule {
|
|
55
|
+
readonly _regexp: NamedRegExp<{
|
|
56
|
+
title?: string;
|
|
57
|
+
href: string;
|
|
58
|
+
}>;
|
|
59
|
+
readonly _render: (props: {
|
|
60
|
+
title: string;
|
|
61
|
+
href: string;
|
|
62
|
+
}, options: MarkupOptions) => JSXElement;
|
|
63
|
+
constructor(_regexp: NamedRegExp<{
|
|
64
|
+
title?: string;
|
|
65
|
+
href: string;
|
|
66
|
+
}>, //
|
|
67
|
+
_render: (props: {
|
|
68
|
+
title: string;
|
|
69
|
+
href: string;
|
|
70
|
+
}, options: MarkupOptions) => JSXElement, contexts: ImmutableArray<string>, subcontext?: string | null, priority?: number);
|
|
71
|
+
match(input: string, { schemes, url: base }: MarkupOptions): MarkupRuleMatch<{
|
|
72
|
+
title: string;
|
|
73
|
+
href: string;
|
|
74
|
+
}> | null;
|
|
75
|
+
render(props: {
|
|
76
|
+
title: string;
|
|
77
|
+
href: string;
|
|
78
|
+
}, options: MarkupOptions): JSXElement<import("../util/jsx.js").JSXProps>;
|
|
79
|
+
}
|
|
80
|
+
export type MarkupRules = MarkupRule[];
|
package/markup/rule.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { formatURL, getOptionalURL } from "../util/url.js";
|
|
2
|
+
export class MarkupRule {
|
|
3
|
+
constructor(contexts, //
|
|
4
|
+
subcontext = null, priority = 0) {
|
|
5
|
+
this.contexts = contexts;
|
|
6
|
+
this.subcontext = subcontext;
|
|
7
|
+
this.priority = priority;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class RegExpMarkupRule extends MarkupRule {
|
|
11
|
+
constructor(_regexp, //
|
|
12
|
+
_render, contexts, subcontext, priority) {
|
|
13
|
+
super(contexts, subcontext, priority);
|
|
14
|
+
this._regexp = _regexp;
|
|
15
|
+
this._render = _render;
|
|
16
|
+
}
|
|
17
|
+
match(input) {
|
|
18
|
+
return this._regexp.exec(input);
|
|
19
|
+
}
|
|
20
|
+
render(props, options) {
|
|
21
|
+
return this._render(props, options);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class NamedRegExpMarkupRule extends MarkupRule {
|
|
25
|
+
constructor(_regexp, //
|
|
26
|
+
_render, contexts, subcontext, priority) {
|
|
27
|
+
super(contexts, subcontext, priority);
|
|
28
|
+
this._regexp = _regexp;
|
|
29
|
+
this._render = _render;
|
|
30
|
+
}
|
|
31
|
+
match(input) {
|
|
32
|
+
return this._regexp.exec(input);
|
|
33
|
+
}
|
|
34
|
+
render(props, options) {
|
|
35
|
+
return this._render(props, options);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class LinkRegExpMarkupRule extends MarkupRule {
|
|
39
|
+
constructor(_regexp, //
|
|
40
|
+
_render, contexts, subcontext, priority) {
|
|
41
|
+
super(contexts, subcontext, priority);
|
|
42
|
+
this._regexp = _regexp;
|
|
43
|
+
this._render = _render;
|
|
44
|
+
}
|
|
45
|
+
// Validates that the link is a valid URL (using `getOptionalURL()` to resolve relative links relative to `options.url`).
|
|
46
|
+
// Validates that the link's URL scheme is in the `options.schemes` whitelist (defaults to `http` and `https`).
|
|
47
|
+
// Generates a default title for the link using `formatURL()` (e.g. `shax.com/my/dir`).
|
|
48
|
+
match(input, { schemes, url: base }) {
|
|
49
|
+
const match = this._regexp.exec(input);
|
|
50
|
+
if (match) {
|
|
51
|
+
const { 0: first, index, groups } = match;
|
|
52
|
+
const { href, title } = groups;
|
|
53
|
+
const url = getOptionalURL(href, base);
|
|
54
|
+
if (url && schemes.includes(url.protocol)) {
|
|
55
|
+
return { 0: first, index, groups: { href: url.href, title: (title === null || title === void 0 ? void 0 : title.trim()) || formatURL(url) } };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
render(props, options) {
|
|
61
|
+
return this._render(props, options);
|
|
62
|
+
}
|
|
63
|
+
}
|