@valbuild/core 0.17.0 → 0.19.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/dist/declarations/src/index.d.ts +4 -1
- package/dist/declarations/src/initSchema.d.ts +8 -2
- package/dist/declarations/src/patch/deref.d.ts +2 -1
- package/dist/declarations/src/patch/operation.d.ts +8 -0
- package/dist/declarations/src/schema/keyOf.d.ts +2 -2
- package/dist/declarations/src/schema/richtext.d.ts +7 -6
- package/dist/declarations/src/schema/string.d.ts +6 -3
- package/dist/declarations/src/selector/future/index.d.ts +3 -3
- package/dist/declarations/src/selector/index.d.ts +3 -3
- package/dist/declarations/src/source/future/remote.d.ts +2 -2
- package/dist/declarations/src/source/index.d.ts +3 -3
- package/dist/declarations/src/source/richtext.d.ts +70 -54
- package/dist/{index-3e3e839e.esm.js → index-5d1ab97c.esm.js} +1 -1
- package/dist/{index-369caccf.esm.js → index-bccf1907.esm.js} +1 -1
- package/dist/{ops-f3015423.cjs.dev.js → ops-2d7e1742.cjs.dev.js} +106 -33
- package/dist/{ops-23a5abb2.esm.js → ops-7ef32b0a.esm.js} +107 -35
- package/dist/{ops-0d09f8ee.cjs.prod.js → ops-ae089ab2.cjs.prod.js} +106 -33
- package/dist/valbuild-core.cjs.dev.js +311 -15
- package/dist/valbuild-core.cjs.prod.js +311 -15
- package/dist/valbuild-core.esm.js +294 -18
- package/expr/dist/valbuild-core-expr.esm.js +2 -2
- package/package.json +4 -1
- package/patch/dist/valbuild-core-patch.cjs.dev.js +6 -1
- package/patch/dist/valbuild-core-patch.cjs.prod.js +6 -1
- package/patch/dist/valbuild-core-patch.esm.js +8 -3
- package/src/getSha256.ts +8 -0
- package/src/index.ts +21 -5
- package/src/initSchema.ts +6 -0
- package/src/module.ts +33 -2
- package/src/patch/deref.ts +14 -1
- package/src/patch/operation.ts +10 -0
- package/src/patch/parse.ts +1 -0
- package/src/patch/patch.ts +3 -0
- package/src/schema/keyOf.ts +2 -2
- package/src/schema/richtext.ts +19 -73
- package/src/schema/string.ts +14 -4
- package/src/schema/validation.test.ts +2 -2
- package/src/selector/future/index.ts +8 -4
- package/src/selector/index.ts +4 -4
- package/src/source/future/remote.ts +2 -2
- package/src/source/index.ts +2 -2
- package/src/source/richtext.test.ts +178 -0
- package/src/source/richtext.ts +295 -89
- package/tsconfig.json +2 -1
package/src/source/richtext.ts
CHANGED
@@ -1,116 +1,322 @@
|
|
1
|
+
import * as marked from "marked";
|
2
|
+
import { FileSource } from "./file";
|
1
3
|
import { VAL_EXTENSION } from ".";
|
2
|
-
import {
|
4
|
+
import { convertFileSource } from "../schema/image";
|
3
5
|
|
4
|
-
type
|
5
|
-
|
6
|
+
export type RichTextOptions = {
|
7
|
+
headings?: ("h1" | "h2" | "h3" | "h4" | "h5" | "h6")[];
|
8
|
+
img?: boolean;
|
9
|
+
ul?: boolean; // TODO: naming
|
10
|
+
ol?: boolean; // TODO: naming
|
11
|
+
lineThrough?: boolean;
|
12
|
+
bold?: boolean;
|
13
|
+
italic?: boolean;
|
14
|
+
// link?: boolean;
|
15
|
+
// fontFamily?: Record<string, string[]>;
|
16
|
+
// fontSize?: Record<string, string[]>;
|
17
|
+
// blockQuote?: boolean; // TODO: naming
|
6
18
|
};
|
7
19
|
|
8
|
-
type
|
9
|
-
|
10
|
-
|
11
|
-
|
20
|
+
export type ParagraphNode<O extends RichTextOptions> = {
|
21
|
+
tag: "p";
|
22
|
+
children: (string | SpanNode<O> | ImageNode<O>)[];
|
23
|
+
// AnchorNode<O>
|
12
24
|
};
|
13
25
|
|
14
|
-
type
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
indent?: number;
|
33
|
-
};
|
26
|
+
export type LineThrough<O extends RichTextOptions> =
|
27
|
+
O["lineThrough"] extends true ? "line-through" : never;
|
28
|
+
export type Italic<O extends RichTextOptions> = O["italic"] extends true
|
29
|
+
? "italic"
|
30
|
+
: never;
|
31
|
+
export type Bold<O extends RichTextOptions> = O["bold"] extends true
|
32
|
+
? "bold"
|
33
|
+
: never;
|
34
|
+
// export type FontFamily<O extends RichTextOptions> =
|
35
|
+
// O["fontFamily"] extends Record<string, unknown>
|
36
|
+
// ? `font-${keyof O["fontFamily"] & string}`
|
37
|
+
// : never;
|
38
|
+
// export type FontSize<O extends RichTextOptions> = O["fontSize"] extends Record<
|
39
|
+
// string,
|
40
|
+
// unknown
|
41
|
+
// >
|
42
|
+
// ? `text-${keyof O["fontSize"] & string}`
|
43
|
+
// : never;
|
34
44
|
|
35
|
-
export type
|
36
|
-
|
37
|
-
|
38
|
-
|
45
|
+
export type Classes<O extends RichTextOptions> =
|
46
|
+
| LineThrough<O>
|
47
|
+
| Italic<O>
|
48
|
+
| Bold<O>;
|
49
|
+
// | FontFamily<O>
|
50
|
+
// | FontSize<O>;
|
39
51
|
|
40
|
-
export type
|
41
|
-
|
42
|
-
|
43
|
-
|
52
|
+
export type SpanNode<O extends RichTextOptions> = {
|
53
|
+
tag: "span";
|
54
|
+
classes: Classes<O>[];
|
55
|
+
children: [string | SpanNode<O>];
|
44
56
|
};
|
45
57
|
|
46
|
-
export type
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
58
|
+
// export type AnchorNode<O extends RichTextOptions> = never; // TODO:
|
59
|
+
// O["link"] extends true
|
60
|
+
// ? {
|
61
|
+
// tag: "a";
|
62
|
+
// href: string;
|
63
|
+
// children: [string];
|
64
|
+
// }
|
65
|
+
// : never;
|
52
66
|
|
53
|
-
export type
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
67
|
+
export type ImageNode<O extends RichTextOptions> = O["img"] extends true
|
68
|
+
? {
|
69
|
+
tag: "img";
|
70
|
+
src: string;
|
71
|
+
height?: number;
|
72
|
+
width?: number;
|
73
|
+
}
|
74
|
+
: never;
|
75
|
+
|
76
|
+
export type ListItemNode<O extends RichTextOptions> = {
|
77
|
+
tag: "li";
|
78
|
+
children: (
|
79
|
+
| string
|
80
|
+
| SpanNode<O>
|
81
|
+
// | AnchorNode<O>
|
82
|
+
| ImageNode<O>
|
83
|
+
| UnorderedListNode<O>
|
84
|
+
| OrderedListNode<O>
|
85
|
+
)[];
|
59
86
|
};
|
60
87
|
|
61
|
-
type
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
88
|
+
export type UnorderedListNode<O extends RichTextOptions> = O["ul"] extends true
|
89
|
+
? {
|
90
|
+
tag: "ul";
|
91
|
+
dir?: "ltr" | "rtl";
|
92
|
+
children: ListItemNode<O>[];
|
93
|
+
}
|
94
|
+
: never;
|
95
|
+
|
96
|
+
export type OrderedListNode<O extends RichTextOptions> = O["ol"] extends true
|
97
|
+
? {
|
98
|
+
tag: "ol";
|
99
|
+
dir?: "ltr" | "rtl";
|
100
|
+
children: ListItemNode<O>[];
|
101
|
+
}
|
102
|
+
: never;
|
103
|
+
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
105
|
+
export type HeadingNode<O extends RichTextOptions> = O["headings"] extends any[]
|
106
|
+
? {
|
107
|
+
tag: O["headings"][number];
|
108
|
+
children: (string | SpanNode<O>)[];
|
109
|
+
// | AnchorNode<O>
|
110
|
+
}
|
111
|
+
: never;
|
112
|
+
|
113
|
+
// export type BlockQuoteNode<O extends RichTextOptions> =
|
114
|
+
// O["blockQuote"] extends true
|
115
|
+
// ? { tag: "blockquote"; children: [string] }
|
116
|
+
// : never;
|
117
|
+
|
118
|
+
type ImageSource = FileSource<{
|
119
|
+
width: number;
|
120
|
+
height: number;
|
121
|
+
sha256: string;
|
122
|
+
}>;
|
123
|
+
|
124
|
+
export type SourceNode<O extends RichTextOptions> = O["img"] extends true
|
125
|
+
? ImageSource
|
126
|
+
: never;
|
127
|
+
|
128
|
+
export type AnyRichTextOptions = {
|
129
|
+
headings: ("h1" | "h2" | "h3" | "h4" | "h5" | "h6")[];
|
130
|
+
img: true;
|
131
|
+
ul: true;
|
132
|
+
ol: true;
|
133
|
+
lineThrough: true;
|
134
|
+
bold: true;
|
135
|
+
italic: true;
|
136
|
+
// blockQuote: true;
|
137
|
+
// fontFamily: Record<string, string[]>;
|
138
|
+
// fontSize: Record<string, string[]>;
|
68
139
|
};
|
69
140
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
141
|
+
export type RichTextSourceNode<O extends RichTextOptions> =
|
142
|
+
| Exclude<RichTextNode<O>, { tag: "img" }>
|
143
|
+
| ParagraphNode<O>
|
144
|
+
| ListItemNode<O>
|
145
|
+
| ImageNode<O>
|
146
|
+
| SourceNode<O>;
|
147
|
+
|
148
|
+
export type RichTextSource<O extends RichTextOptions> = {
|
149
|
+
[VAL_EXTENSION]: "richtext";
|
150
|
+
children: (
|
151
|
+
| HeadingNode<O>
|
152
|
+
| ParagraphNode<O>
|
153
|
+
| UnorderedListNode<O>
|
154
|
+
| OrderedListNode<O>
|
155
|
+
// | BlockQuoteNode<O>
|
156
|
+
| SourceNode<O>
|
157
|
+
)[];
|
77
158
|
};
|
78
159
|
|
79
|
-
export type
|
80
|
-
|
160
|
+
export type RichTextNode<O extends RichTextOptions> =
|
161
|
+
| string
|
162
|
+
| HeadingNode<O>
|
163
|
+
| ParagraphNode<O>
|
164
|
+
| UnorderedListNode<O>
|
165
|
+
| OrderedListNode<O>
|
166
|
+
| ListItemNode<O>
|
167
|
+
| SpanNode<O>
|
168
|
+
// | BlockQuoteNode<O>
|
169
|
+
| ImageNode<O>;
|
170
|
+
|
171
|
+
export type RootNode<O extends RichTextOptions> =
|
172
|
+
| HeadingNode<O>
|
173
|
+
| ParagraphNode<O>
|
174
|
+
| UnorderedListNode<O>
|
175
|
+
| OrderedListNode<O>
|
176
|
+
// | BlockQuoteNode<O>
|
177
|
+
| ImageNode<O>;
|
178
|
+
|
179
|
+
// TODO: rename to RichTextSelector?
|
180
|
+
export type RichText<O extends RichTextOptions> = {
|
181
|
+
[VAL_EXTENSION]: "richtext";
|
182
|
+
children: RootNode<O>[];
|
81
183
|
};
|
82
184
|
|
83
|
-
|
84
|
-
|
85
|
-
): RichTextSource {
|
86
|
-
|
87
|
-
|
88
|
-
[
|
89
|
-
|
90
|
-
|
185
|
+
function parseTokens<O extends RichTextOptions>(
|
186
|
+
tokens: marked.Token[]
|
187
|
+
): RichTextSource<O>["children"] {
|
188
|
+
return tokens.flatMap((token) => {
|
189
|
+
if (token.type === "heading") {
|
190
|
+
return [
|
191
|
+
{
|
192
|
+
tag: `h${token.depth}`,
|
193
|
+
children: parseTokens(token.tokens ? token.tokens : []),
|
194
|
+
},
|
195
|
+
];
|
196
|
+
}
|
197
|
+
if (token.type === "paragraph") {
|
198
|
+
return [
|
199
|
+
{
|
200
|
+
tag: "p",
|
201
|
+
children: parseTokens(token.tokens ? token.tokens : []),
|
202
|
+
},
|
203
|
+
];
|
204
|
+
}
|
205
|
+
if (token.type === "strong") {
|
206
|
+
return [
|
207
|
+
{
|
208
|
+
tag: "span",
|
209
|
+
classes: ["bold"],
|
210
|
+
children: parseTokens(token.tokens ? token.tokens : []),
|
211
|
+
},
|
212
|
+
];
|
213
|
+
}
|
214
|
+
if (token.type === "em") {
|
215
|
+
return [
|
91
216
|
{
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
type: "text",
|
96
|
-
text: data,
|
97
|
-
},
|
98
|
-
],
|
217
|
+
tag: "span",
|
218
|
+
classes: ["italic"],
|
219
|
+
children: parseTokens(token.tokens ? token.tokens : []),
|
99
220
|
},
|
100
|
-
]
|
101
|
-
}
|
221
|
+
];
|
222
|
+
}
|
223
|
+
if (token.type === "del") {
|
224
|
+
return [
|
225
|
+
{
|
226
|
+
tag: "span",
|
227
|
+
classes: ["line-through"],
|
228
|
+
children: parseTokens(token.tokens ? token.tokens : []),
|
229
|
+
},
|
230
|
+
];
|
231
|
+
}
|
232
|
+
if (token.type === "text") {
|
233
|
+
return [token.text];
|
234
|
+
}
|
235
|
+
if (token.type === "list") {
|
236
|
+
return [
|
237
|
+
{
|
238
|
+
tag: token.ordered ? "ol" : "ul",
|
239
|
+
children: parseTokens(token.items),
|
240
|
+
},
|
241
|
+
];
|
242
|
+
}
|
243
|
+
if (token.type === "list_item") {
|
244
|
+
return [
|
245
|
+
{
|
246
|
+
tag: "li",
|
247
|
+
children: parseTokens(token.tokens ? token.tokens : []),
|
248
|
+
},
|
249
|
+
];
|
250
|
+
}
|
251
|
+
if (token.type === "space") {
|
252
|
+
return [];
|
253
|
+
}
|
254
|
+
|
255
|
+
if (token.type === "code") {
|
256
|
+
return [
|
257
|
+
{
|
258
|
+
tag: "span",
|
259
|
+
classes: [],
|
260
|
+
children: [token.text],
|
261
|
+
},
|
262
|
+
];
|
263
|
+
}
|
264
|
+
console.error(
|
265
|
+
`Could not parse markdown: unsupported token type: ${token.type}. Found: ${token.raw}`
|
266
|
+
);
|
267
|
+
return [token.raw];
|
268
|
+
});
|
269
|
+
}
|
270
|
+
|
271
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
272
|
+
function nodeToTag(node: any): any {
|
273
|
+
if (node[VAL_EXTENSION] === "file") {
|
274
|
+
return node;
|
102
275
|
}
|
276
|
+
throw Error(`Unexpected node: ${JSON.stringify(node)}`);
|
277
|
+
}
|
278
|
+
|
279
|
+
function imgSrcToImgTag<O extends RichTextOptions>(
|
280
|
+
imageSrc: ImageSource
|
281
|
+
): ImageNode<O> {
|
282
|
+
const converted = convertFileSource(imageSrc);
|
283
|
+
return {
|
284
|
+
tag: "img",
|
285
|
+
src: converted.url,
|
286
|
+
width: imageSrc.metadata?.width,
|
287
|
+
height: imageSrc.metadata?.height,
|
288
|
+
} as ImageNode<O>;
|
289
|
+
}
|
290
|
+
|
291
|
+
export function convertRichTextSource<O extends RichTextOptions>(
|
292
|
+
src: RichTextSource<O>
|
293
|
+
): RichText<O> {
|
103
294
|
return {
|
104
|
-
...data,
|
105
295
|
[VAL_EXTENSION]: "richtext",
|
106
|
-
|
296
|
+
children: src.children.map((source) => {
|
297
|
+
if (VAL_EXTENSION in source && source[VAL_EXTENSION] === "file") {
|
298
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
299
|
+
return imgSrcToImgTag(source as any);
|
300
|
+
}
|
301
|
+
return source;
|
302
|
+
}),
|
303
|
+
} as RichText<O>;
|
107
304
|
}
|
108
305
|
|
109
|
-
export function
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
306
|
+
export function richtext<
|
307
|
+
O extends RichTextOptions,
|
308
|
+
Nodes extends never | ImageSource
|
309
|
+
>(templateStrings: TemplateStringsArray, ...expr: Nodes[]): RichTextSource<O> {
|
310
|
+
return {
|
311
|
+
[VAL_EXTENSION]: "richtext",
|
312
|
+
children: templateStrings.flatMap((templateString, i) => {
|
313
|
+
const lex = marked.lexer(templateString, {
|
314
|
+
gfm: true,
|
315
|
+
});
|
316
|
+
if (expr[i]) {
|
317
|
+
return parseTokens(lex).concat(nodeToTag(expr[i]));
|
318
|
+
}
|
319
|
+
return parseTokens(lex);
|
320
|
+
}),
|
321
|
+
};
|
116
322
|
}
|