@artinstack/migrator 0.1.4 → 0.1.5
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/bundle-uAAHehbv.d.ts +23 -0
- package/dist/chunk-CIOYDRY5.js +851 -0
- package/dist/chunk-CIOYDRY5.js.map +1 -0
- package/dist/chunk-S7TRWILI.js +71 -0
- package/dist/chunk-S7TRWILI.js.map +1 -0
- package/dist/{chunk-Q6M5JEL3.js → chunk-XUBCG3IA.js} +90 -2
- package/dist/chunk-XUBCG3IA.js.map +1 -0
- package/dist/{chunk-XKWWXKP3.js → chunk-YLFVYPB3.js} +100 -85
- package/dist/chunk-YLFVYPB3.js.map +1 -0
- package/dist/cli/index.js +3 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +8 -87
- package/dist/index.js +23 -351
- package/dist/index.js.map +1 -1
- package/dist/normalizer/index.d.ts +4 -2
- package/dist/rewrite-inline-images-BckVKPbh.d.ts +21 -0
- package/dist/sinks/index.d.ts +262 -3
- package/dist/sinks/index.js +4 -2
- package/dist/transformers/index.d.ts +157 -0
- package/dist/transformers/index.js +32 -0
- package/dist/transformers/index.js.map +1 -0
- package/dist/{bundle-DfM_jKbq.d.ts → types-DWOP8Dcy.d.ts} +1 -21
- package/package.json +5 -1
- package/dist/chunk-Q6M5JEL3.js.map +0 -1
- package/dist/chunk-XKWWXKP3.js.map +0 -1
- package/dist/index-D88mjcF5.d.ts +0 -279
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
// src/transformers/html-to-grapes/index.ts
|
|
2
|
+
import * as cheerio from "cheerio";
|
|
3
|
+
|
|
4
|
+
// src/transformers/css-to-styles/index.ts
|
|
5
|
+
function stripCssComments(css) {
|
|
6
|
+
return css.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
7
|
+
}
|
|
8
|
+
function parseDeclarations(block) {
|
|
9
|
+
const style = {};
|
|
10
|
+
for (const declaration of block.split(";")) {
|
|
11
|
+
const trimmed = declaration.trim();
|
|
12
|
+
if (!trimmed) continue;
|
|
13
|
+
const separator = trimmed.indexOf(":");
|
|
14
|
+
if (separator === -1) continue;
|
|
15
|
+
const property = trimmed.slice(0, separator).trim();
|
|
16
|
+
const value = trimmed.slice(separator + 1).trim();
|
|
17
|
+
if (!property || !value) continue;
|
|
18
|
+
style[property] = value;
|
|
19
|
+
}
|
|
20
|
+
return style;
|
|
21
|
+
}
|
|
22
|
+
function cssToStyles(css) {
|
|
23
|
+
const cleaned = stripCssComments(css);
|
|
24
|
+
const rules = [];
|
|
25
|
+
const rulePattern = /([^{]+)\{([^}]*)\}/g;
|
|
26
|
+
for (const match of cleaned.matchAll(rulePattern)) {
|
|
27
|
+
const selectorText = match[1]?.trim() ?? "";
|
|
28
|
+
const declarationBlock = match[2] ?? "";
|
|
29
|
+
if (!selectorText || selectorText.startsWith("@")) continue;
|
|
30
|
+
const style = parseDeclarations(declarationBlock);
|
|
31
|
+
if (Object.keys(style).length === 0) continue;
|
|
32
|
+
const selectors = selectorText.split(",").map((selector) => selector.trim()).filter(Boolean);
|
|
33
|
+
if (selectors.length === 0) continue;
|
|
34
|
+
rules.push({ selectors, style });
|
|
35
|
+
}
|
|
36
|
+
return rules;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/transformers/html-to-grapes/walk.ts
|
|
40
|
+
var INLINE_TAGS = /* @__PURE__ */ new Set([
|
|
41
|
+
"a",
|
|
42
|
+
"abbr",
|
|
43
|
+
"b",
|
|
44
|
+
"br",
|
|
45
|
+
"cite",
|
|
46
|
+
"code",
|
|
47
|
+
"del",
|
|
48
|
+
"em",
|
|
49
|
+
"i",
|
|
50
|
+
"img",
|
|
51
|
+
"ins",
|
|
52
|
+
"mark",
|
|
53
|
+
"q",
|
|
54
|
+
"s",
|
|
55
|
+
"small",
|
|
56
|
+
"span",
|
|
57
|
+
"strong",
|
|
58
|
+
"sub",
|
|
59
|
+
"sup",
|
|
60
|
+
"u",
|
|
61
|
+
"wbr"
|
|
62
|
+
]);
|
|
63
|
+
var VOID_TAGS = /* @__PURE__ */ new Set([
|
|
64
|
+
"area",
|
|
65
|
+
"base",
|
|
66
|
+
"br",
|
|
67
|
+
"col",
|
|
68
|
+
"embed",
|
|
69
|
+
"hr",
|
|
70
|
+
"img",
|
|
71
|
+
"input",
|
|
72
|
+
"link",
|
|
73
|
+
"meta",
|
|
74
|
+
"param",
|
|
75
|
+
"source",
|
|
76
|
+
"track",
|
|
77
|
+
"wbr"
|
|
78
|
+
]);
|
|
79
|
+
var TEXT_CONTAINER_TAGS = /* @__PURE__ */ new Set([
|
|
80
|
+
"blockquote",
|
|
81
|
+
"figcaption",
|
|
82
|
+
"h1",
|
|
83
|
+
"h2",
|
|
84
|
+
"h3",
|
|
85
|
+
"h4",
|
|
86
|
+
"h5",
|
|
87
|
+
"h6",
|
|
88
|
+
"label",
|
|
89
|
+
"li",
|
|
90
|
+
"p",
|
|
91
|
+
"pre",
|
|
92
|
+
"td",
|
|
93
|
+
"th"
|
|
94
|
+
]);
|
|
95
|
+
var SKIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "template"]);
|
|
96
|
+
var DEFAULT_TYPES = {
|
|
97
|
+
a: "link",
|
|
98
|
+
img: "image"
|
|
99
|
+
};
|
|
100
|
+
var LAYOUT_DATA_ATTR = "data-layout";
|
|
101
|
+
var DEFAULT_LAYOUT_TYPE_MAP = {
|
|
102
|
+
section: "section",
|
|
103
|
+
row: "row",
|
|
104
|
+
column: "column"
|
|
105
|
+
};
|
|
106
|
+
function parseLayoutKind(attributes) {
|
|
107
|
+
const value = attributes?.[LAYOUT_DATA_ATTR];
|
|
108
|
+
if (value === "section" || value === "row" || value === "column") return value;
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
function resolveLayoutComponentType(kind, options) {
|
|
112
|
+
return options.layoutTypeMap?.[kind] ?? DEFAULT_LAYOUT_TYPE_MAP[kind];
|
|
113
|
+
}
|
|
114
|
+
function layoutAttributesForComponent(attributes) {
|
|
115
|
+
if (!attributes) return void 0;
|
|
116
|
+
const { [LAYOUT_DATA_ATTR]: _layout, ...rest } = attributes;
|
|
117
|
+
return Object.keys(rest).length > 0 ? rest : void 0;
|
|
118
|
+
}
|
|
119
|
+
function tagNameOf($el) {
|
|
120
|
+
const raw = $el.prop("tagName");
|
|
121
|
+
return typeof raw === "string" ? raw.toLowerCase() : void 0;
|
|
122
|
+
}
|
|
123
|
+
function applyElementMeta(component, meta) {
|
|
124
|
+
if (meta.attributes) component.attributes = meta.attributes;
|
|
125
|
+
if (meta.classes) component.classes = meta.classes;
|
|
126
|
+
return component;
|
|
127
|
+
}
|
|
128
|
+
function pickElementMeta($el) {
|
|
129
|
+
const attributes = {};
|
|
130
|
+
const classes = [];
|
|
131
|
+
if (typeof $el.attr() === "object") {
|
|
132
|
+
for (const [key, value] of Object.entries($el.attr() ?? {})) {
|
|
133
|
+
if (key === "class") {
|
|
134
|
+
classes.push(...value.split(/\s+/).filter(Boolean));
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
attributes[key] = value;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
attributes: Object.keys(attributes).length > 0 ? attributes : void 0,
|
|
142
|
+
classes: classes.length > 0 ? classes : void 0
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function resolveComponentType(tagName, classes, options, fallback = "default") {
|
|
146
|
+
if (options.componentMap && classes) {
|
|
147
|
+
for (const className of classes) {
|
|
148
|
+
const mapped = options.componentMap[className];
|
|
149
|
+
if (mapped) return mapped;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const tagMapped = options.tagMap?.[tagName];
|
|
153
|
+
if (tagMapped) return tagMapped;
|
|
154
|
+
return DEFAULT_TYPES[tagName] ?? fallback;
|
|
155
|
+
}
|
|
156
|
+
function hasOnlyInlineContent($, $el) {
|
|
157
|
+
let inlineOnly = true;
|
|
158
|
+
$el.contents().each((_, node) => {
|
|
159
|
+
if (!inlineOnly) return;
|
|
160
|
+
const $child = $(node);
|
|
161
|
+
if ($child.get(0)?.type === "text") return;
|
|
162
|
+
const childTag = tagNameOf($child);
|
|
163
|
+
if (!childTag || !INLINE_TAGS.has(childTag)) {
|
|
164
|
+
inlineOnly = false;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (!hasOnlyInlineContent($, $child)) {
|
|
168
|
+
inlineOnly = false;
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
return inlineOnly;
|
|
172
|
+
}
|
|
173
|
+
function walkChildren($, $el, options) {
|
|
174
|
+
const components = [];
|
|
175
|
+
$el.contents().each((_, node) => {
|
|
176
|
+
const walked = walkNode($, $(node), options);
|
|
177
|
+
if (!walked) return;
|
|
178
|
+
if (Array.isArray(walked)) {
|
|
179
|
+
components.push(...walked);
|
|
180
|
+
} else {
|
|
181
|
+
components.push(walked);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
return components;
|
|
185
|
+
}
|
|
186
|
+
function walkNode($, $el, options) {
|
|
187
|
+
const node = $el.get(0);
|
|
188
|
+
if (!node) return null;
|
|
189
|
+
if (node.type === "text") {
|
|
190
|
+
const text = "data" in node ? String(node.data ?? "") : "";
|
|
191
|
+
if (!text.trim()) return null;
|
|
192
|
+
return { type: "textnode", content: text };
|
|
193
|
+
}
|
|
194
|
+
if (node.type !== "tag") return null;
|
|
195
|
+
const tagName = tagNameOf($el);
|
|
196
|
+
if (!tagName || SKIP_TAGS.has(tagName)) return null;
|
|
197
|
+
const meta = pickElementMeta($el);
|
|
198
|
+
const layoutKind = parseLayoutKind(meta.attributes);
|
|
199
|
+
if (layoutKind) {
|
|
200
|
+
const components2 = walkChildren($, $el, options);
|
|
201
|
+
const component2 = applyElementMeta(
|
|
202
|
+
{
|
|
203
|
+
type: resolveLayoutComponentType(layoutKind, options),
|
|
204
|
+
tagName
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
attributes: layoutAttributesForComponent(meta.attributes),
|
|
208
|
+
classes: meta.classes
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
if (components2.length > 0) {
|
|
212
|
+
component2.components = components2;
|
|
213
|
+
}
|
|
214
|
+
return component2;
|
|
215
|
+
}
|
|
216
|
+
if (VOID_TAGS.has(tagName)) {
|
|
217
|
+
return applyElementMeta(
|
|
218
|
+
{
|
|
219
|
+
type: resolveComponentType(tagName, meta.classes, options),
|
|
220
|
+
tagName,
|
|
221
|
+
void: true
|
|
222
|
+
},
|
|
223
|
+
meta
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
if (TEXT_CONTAINER_TAGS.has(tagName) && hasOnlyInlineContent($, $el)) {
|
|
227
|
+
return applyElementMeta(
|
|
228
|
+
{
|
|
229
|
+
type: resolveComponentType(tagName, meta.classes, options, "text"),
|
|
230
|
+
tagName,
|
|
231
|
+
content: $el.html() ?? ""
|
|
232
|
+
},
|
|
233
|
+
meta
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
if (INLINE_TAGS.has(tagName)) {
|
|
237
|
+
return applyElementMeta(
|
|
238
|
+
{
|
|
239
|
+
type: "text",
|
|
240
|
+
content: $.html($el) ?? ""
|
|
241
|
+
},
|
|
242
|
+
meta
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
const components = walkChildren($, $el, options);
|
|
246
|
+
const component = applyElementMeta(
|
|
247
|
+
{
|
|
248
|
+
type: resolveComponentType(tagName, meta.classes, options),
|
|
249
|
+
tagName
|
|
250
|
+
},
|
|
251
|
+
meta
|
|
252
|
+
);
|
|
253
|
+
if (components.length > 0) {
|
|
254
|
+
component.components = components;
|
|
255
|
+
}
|
|
256
|
+
return component;
|
|
257
|
+
}
|
|
258
|
+
function appendWalked(content, walked) {
|
|
259
|
+
if (!walked) return;
|
|
260
|
+
if (Array.isArray(walked)) {
|
|
261
|
+
content.push(...walked);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
content.push(walked);
|
|
265
|
+
}
|
|
266
|
+
function walkNodes($, $nodes, content, options) {
|
|
267
|
+
$nodes.each((_, node) => {
|
|
268
|
+
appendWalked(content, walkNode($, $(node), options));
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function walkHtmlToComponents($, options = {}) {
|
|
272
|
+
const content = [];
|
|
273
|
+
const body = $("body");
|
|
274
|
+
if (body.length) {
|
|
275
|
+
walkNodes($, body.contents(), content, options);
|
|
276
|
+
return content;
|
|
277
|
+
}
|
|
278
|
+
const children = $.root().children();
|
|
279
|
+
if (children.length) {
|
|
280
|
+
walkNodes($, children, content, options);
|
|
281
|
+
} else {
|
|
282
|
+
walkNodes($, $.root().contents(), content, options);
|
|
283
|
+
}
|
|
284
|
+
return content;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/transformers/html-to-grapes/index.ts
|
|
288
|
+
function htmlToGrapes(html, options = {}) {
|
|
289
|
+
const trimmed = html.trim();
|
|
290
|
+
if (!trimmed) {
|
|
291
|
+
return { content: [], styles: [] };
|
|
292
|
+
}
|
|
293
|
+
const $ = cheerio.load(trimmed, { xml: false });
|
|
294
|
+
const styleBlocks = [];
|
|
295
|
+
$("style").each((_, element) => {
|
|
296
|
+
styleBlocks.push($(element).html() ?? "");
|
|
297
|
+
$(element).remove();
|
|
298
|
+
});
|
|
299
|
+
const contentCss = styleBlocks.join("\n").trim();
|
|
300
|
+
const styles = cssToStyles(contentCss);
|
|
301
|
+
const content = walkHtmlToComponents($, options);
|
|
302
|
+
const contentHtml = serializeContentHtml($);
|
|
303
|
+
return {
|
|
304
|
+
content,
|
|
305
|
+
styles,
|
|
306
|
+
...contentHtml ? { contentHtml } : {},
|
|
307
|
+
...contentCss ? { contentCss } : {}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function serializeContentHtml($) {
|
|
311
|
+
const body = $("body");
|
|
312
|
+
if (body.length) {
|
|
313
|
+
const html = body.html()?.trim();
|
|
314
|
+
return html || void 0;
|
|
315
|
+
}
|
|
316
|
+
const rootHtml = $.root().html()?.trim();
|
|
317
|
+
return rootHtml || void 0;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/transformers/html-to-tiptap/index.ts
|
|
321
|
+
import * as cheerio2 from "cheerio";
|
|
322
|
+
|
|
323
|
+
// src/transformers/html-to-tiptap/walk.ts
|
|
324
|
+
var SKIP_TAGS2 = /* @__PURE__ */ new Set(["script", "style", "noscript", "template"]);
|
|
325
|
+
var HEADING_TAGS = /* @__PURE__ */ new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
|
|
326
|
+
var UNWRAP_TAGS = /* @__PURE__ */ new Set([
|
|
327
|
+
"article",
|
|
328
|
+
"aside",
|
|
329
|
+
"div",
|
|
330
|
+
"figure",
|
|
331
|
+
"footer",
|
|
332
|
+
"header",
|
|
333
|
+
"main",
|
|
334
|
+
"nav",
|
|
335
|
+
"section",
|
|
336
|
+
"span"
|
|
337
|
+
]);
|
|
338
|
+
var INLINE_TAGS2 = /* @__PURE__ */ new Set([
|
|
339
|
+
"a",
|
|
340
|
+
"abbr",
|
|
341
|
+
"b",
|
|
342
|
+
"br",
|
|
343
|
+
"cite",
|
|
344
|
+
"code",
|
|
345
|
+
"del",
|
|
346
|
+
"em",
|
|
347
|
+
"i",
|
|
348
|
+
"ins",
|
|
349
|
+
"mark",
|
|
350
|
+
"q",
|
|
351
|
+
"s",
|
|
352
|
+
"small",
|
|
353
|
+
"strong",
|
|
354
|
+
"sub",
|
|
355
|
+
"sup",
|
|
356
|
+
"u",
|
|
357
|
+
"wbr"
|
|
358
|
+
]);
|
|
359
|
+
var LAYOUT_ATTR = "data-layout";
|
|
360
|
+
function tagNameOf2($el) {
|
|
361
|
+
const raw = $el.prop("tagName");
|
|
362
|
+
return typeof raw === "string" ? raw.toLowerCase() : void 0;
|
|
363
|
+
}
|
|
364
|
+
function headingLevel(tagName) {
|
|
365
|
+
const level = Number.parseInt(tagName.slice(1), 10);
|
|
366
|
+
return Number.isFinite(level) ? level : 1;
|
|
367
|
+
}
|
|
368
|
+
function isLayoutMarker($el, options) {
|
|
369
|
+
if (options.unwrapLayoutMarkers === false) return false;
|
|
370
|
+
return $el.attr(LAYOUT_ATTR) !== void 0;
|
|
371
|
+
}
|
|
372
|
+
function hasOnlyInlineContent2($, $el) {
|
|
373
|
+
let inlineOnly = true;
|
|
374
|
+
$el.contents().each((_, node) => {
|
|
375
|
+
if (!inlineOnly) return;
|
|
376
|
+
const $child = $(node);
|
|
377
|
+
if ($child.get(0)?.type === "text") return;
|
|
378
|
+
const childTag = tagNameOf2($child);
|
|
379
|
+
if (!childTag || childTag === "br" || childTag === "img") return;
|
|
380
|
+
if (!INLINE_TAGS2.has(childTag)) {
|
|
381
|
+
inlineOnly = false;
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (!hasOnlyInlineContent2($, $child)) {
|
|
385
|
+
inlineOnly = false;
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
return inlineOnly;
|
|
389
|
+
}
|
|
390
|
+
function hasBlockChild($, $el) {
|
|
391
|
+
let hasBlock = false;
|
|
392
|
+
$el.contents().each((_, node) => {
|
|
393
|
+
if (hasBlock) return;
|
|
394
|
+
const $child = $(node);
|
|
395
|
+
if ($child.get(0)?.type === "text") return;
|
|
396
|
+
const childTag = tagNameOf2($child);
|
|
397
|
+
if (!childTag) return;
|
|
398
|
+
if (HEADING_TAGS.has(childTag) || childTag === "p" || childTag === "ul" || childTag === "ol" || childTag === "blockquote" || childTag === "pre" || childTag === "hr" || childTag === "img" || childTag === "table" || isLayoutMarker($child, { unwrapLayoutMarkers: true }) || UNWRAP_TAGS.has(childTag) && hasBlockChild($, $child)) {
|
|
399
|
+
hasBlock = true;
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
return hasBlock;
|
|
403
|
+
}
|
|
404
|
+
function textNode(text, marks = []) {
|
|
405
|
+
const node = { type: "text", text };
|
|
406
|
+
if (marks.length > 0) node.marks = marks;
|
|
407
|
+
return node;
|
|
408
|
+
}
|
|
409
|
+
function paragraph(content) {
|
|
410
|
+
return content.length > 0 ? { type: "paragraph", content } : { type: "paragraph" };
|
|
411
|
+
}
|
|
412
|
+
function appendMarks(existing, added) {
|
|
413
|
+
if (existing.some((mark) => mark.type === added.type)) return existing;
|
|
414
|
+
return [...existing, added];
|
|
415
|
+
}
|
|
416
|
+
function marksForTag(tagName, $el) {
|
|
417
|
+
switch (tagName) {
|
|
418
|
+
case "strong":
|
|
419
|
+
case "b":
|
|
420
|
+
return [{ type: "bold" }];
|
|
421
|
+
case "em":
|
|
422
|
+
case "i":
|
|
423
|
+
return [{ type: "italic" }];
|
|
424
|
+
case "s":
|
|
425
|
+
case "del":
|
|
426
|
+
case "strike":
|
|
427
|
+
return [{ type: "strike" }];
|
|
428
|
+
case "u":
|
|
429
|
+
return [{ type: "underline" }];
|
|
430
|
+
case "code":
|
|
431
|
+
return [{ type: "code" }];
|
|
432
|
+
case "a": {
|
|
433
|
+
const href = $el.attr("href");
|
|
434
|
+
if (!href) return [];
|
|
435
|
+
const attrs = { href };
|
|
436
|
+
const target = $el.attr("target");
|
|
437
|
+
if (target) attrs.target = target;
|
|
438
|
+
const rel = $el.attr("rel");
|
|
439
|
+
if (rel) attrs.rel = rel;
|
|
440
|
+
return [{ type: "link", attrs }];
|
|
441
|
+
}
|
|
442
|
+
default:
|
|
443
|
+
return [];
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function imageNode($el) {
|
|
447
|
+
const src = $el.attr("src");
|
|
448
|
+
if (!src) return null;
|
|
449
|
+
const attrs = { src };
|
|
450
|
+
const alt = $el.attr("alt");
|
|
451
|
+
if (alt !== void 0) attrs.alt = alt;
|
|
452
|
+
const title = $el.attr("title");
|
|
453
|
+
if (title) attrs.title = title;
|
|
454
|
+
return { type: "image", attrs };
|
|
455
|
+
}
|
|
456
|
+
function parseInlineContent($, $el, marks = []) {
|
|
457
|
+
const rootTag = tagNameOf2($el);
|
|
458
|
+
if (rootTag && INLINE_TAGS2.has(rootTag) && rootTag !== "br") {
|
|
459
|
+
marks = marksForTag(rootTag, $el).reduce((acc, mark) => appendMarks(acc, mark), marks);
|
|
460
|
+
}
|
|
461
|
+
const nodes = [];
|
|
462
|
+
$el.contents().each((_, node) => {
|
|
463
|
+
if (node.type === "text") {
|
|
464
|
+
const text = String(node.data ?? "");
|
|
465
|
+
if (text) nodes.push(textNode(text, marks));
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (node.type !== "tag") return;
|
|
469
|
+
const $child = $(node);
|
|
470
|
+
const tagName = tagNameOf2($child);
|
|
471
|
+
if (!tagName || SKIP_TAGS2.has(tagName)) return;
|
|
472
|
+
if (tagName === "br") {
|
|
473
|
+
nodes.push({ type: "hardBreak" });
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (tagName === "img") {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const childMarks = marksForTag(tagName, $child);
|
|
480
|
+
const combinedMarks = childMarks.reduce((acc, mark) => appendMarks(acc, mark), [...marks]);
|
|
481
|
+
nodes.push(...parseInlineContent($, $child, combinedMarks));
|
|
482
|
+
});
|
|
483
|
+
return nodes;
|
|
484
|
+
}
|
|
485
|
+
function parseMixedBlockContent($, $el, options) {
|
|
486
|
+
const blocks = [];
|
|
487
|
+
let inlineBuffer = [];
|
|
488
|
+
function flushInline() {
|
|
489
|
+
if (inlineBuffer.length === 0) return;
|
|
490
|
+
blocks.push(paragraph(inlineBuffer));
|
|
491
|
+
inlineBuffer = [];
|
|
492
|
+
}
|
|
493
|
+
$el.contents().each((_, node) => {
|
|
494
|
+
if (node.type === "text") {
|
|
495
|
+
const text = String(node.data ?? "");
|
|
496
|
+
if (text) inlineBuffer.push(textNode(text));
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (node.type !== "tag") return;
|
|
500
|
+
const $child = $(node);
|
|
501
|
+
const tagName = tagNameOf2($child);
|
|
502
|
+
if (!tagName || SKIP_TAGS2.has(tagName)) return;
|
|
503
|
+
if (tagName === "br") {
|
|
504
|
+
inlineBuffer.push({ type: "hardBreak" });
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (tagName === "img") {
|
|
508
|
+
flushInline();
|
|
509
|
+
const image = imageNode($child);
|
|
510
|
+
if (image) blocks.push(image);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (INLINE_TAGS2.has(tagName)) {
|
|
514
|
+
inlineBuffer.push(...parseInlineContent($, $child));
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
flushInline();
|
|
518
|
+
blocks.push(...walkBlockNodes($, $child, options));
|
|
519
|
+
});
|
|
520
|
+
flushInline();
|
|
521
|
+
return blocks;
|
|
522
|
+
}
|
|
523
|
+
function walkListItem($, $el, options) {
|
|
524
|
+
const blocks = walkBlockNodes($, $el, options);
|
|
525
|
+
if (blocks.length === 0) {
|
|
526
|
+
return { type: "listItem", content: [{ type: "paragraph" }] };
|
|
527
|
+
}
|
|
528
|
+
const normalized = [];
|
|
529
|
+
for (const block of blocks) {
|
|
530
|
+
if (block.type === "paragraph" || block.type === "image" || block.type === "blockquote" || block.type === "bulletList" || block.type === "orderedList") {
|
|
531
|
+
normalized.push(block);
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
normalized.push(paragraph(block.content ?? []));
|
|
535
|
+
}
|
|
536
|
+
return { type: "listItem", content: normalized };
|
|
537
|
+
}
|
|
538
|
+
function walkBlockNode($, $el, options) {
|
|
539
|
+
const node = $el.get(0);
|
|
540
|
+
if (!node) return [];
|
|
541
|
+
if (node.type === "text") {
|
|
542
|
+
const text = String(node.data ?? "").trim();
|
|
543
|
+
return text ? [paragraph([textNode(text)])] : [];
|
|
544
|
+
}
|
|
545
|
+
if (node.type !== "tag") return [];
|
|
546
|
+
const tagName = tagNameOf2($el);
|
|
547
|
+
if (!tagName || SKIP_TAGS2.has(tagName)) return [];
|
|
548
|
+
if (isLayoutMarker($el, options)) {
|
|
549
|
+
return walkBlockNodes($, $el, options);
|
|
550
|
+
}
|
|
551
|
+
if (UNWRAP_TAGS.has(tagName)) {
|
|
552
|
+
if (hasBlockChild($, $el)) {
|
|
553
|
+
return walkBlockNodes($, $el, options);
|
|
554
|
+
}
|
|
555
|
+
if (hasOnlyInlineContent2($, $el)) {
|
|
556
|
+
const inline = parseInlineContent($, $el);
|
|
557
|
+
return inline.length > 0 ? [paragraph(inline)] : [];
|
|
558
|
+
}
|
|
559
|
+
return walkBlockNodes($, $el, options);
|
|
560
|
+
}
|
|
561
|
+
if (HEADING_TAGS.has(tagName)) {
|
|
562
|
+
const content = parseInlineContent($, $el);
|
|
563
|
+
if (content.length === 0) return [];
|
|
564
|
+
return [{ type: "heading", attrs: { level: headingLevel(tagName) }, content }];
|
|
565
|
+
}
|
|
566
|
+
if (tagName === "p") {
|
|
567
|
+
return parseMixedBlockContent($, $el, options);
|
|
568
|
+
}
|
|
569
|
+
if (tagName === "ul") {
|
|
570
|
+
const items = [];
|
|
571
|
+
$el.children("li").each((_, li) => {
|
|
572
|
+
items.push(walkListItem($, $(li), options));
|
|
573
|
+
});
|
|
574
|
+
return items.length > 0 ? [{ type: "bulletList", content: items }] : [];
|
|
575
|
+
}
|
|
576
|
+
if (tagName === "ol") {
|
|
577
|
+
const items = [];
|
|
578
|
+
$el.children("li").each((_, li) => {
|
|
579
|
+
items.push(walkListItem($, $(li), options));
|
|
580
|
+
});
|
|
581
|
+
return items.length > 0 ? [{ type: "orderedList", content: items }] : [];
|
|
582
|
+
}
|
|
583
|
+
if (tagName === "li") {
|
|
584
|
+
return [walkListItem($, $el, options)];
|
|
585
|
+
}
|
|
586
|
+
if (tagName === "blockquote") {
|
|
587
|
+
const blocks = walkBlockNodes($, $el, options);
|
|
588
|
+
if (blocks.length === 0) return [];
|
|
589
|
+
return [{ type: "blockquote", content: blocks }];
|
|
590
|
+
}
|
|
591
|
+
if (tagName === "pre") {
|
|
592
|
+
const code = $el.find("code").first();
|
|
593
|
+
const text = (code.length ? code.text() : $el.text()).replace(/\n$/, "");
|
|
594
|
+
if (!text) return [];
|
|
595
|
+
return [{ type: "codeBlock", content: [textNode(text)] }];
|
|
596
|
+
}
|
|
597
|
+
if (tagName === "hr") {
|
|
598
|
+
return [{ type: "horizontalRule" }];
|
|
599
|
+
}
|
|
600
|
+
if (tagName === "img") {
|
|
601
|
+
const image = imageNode($el);
|
|
602
|
+
return image ? [image] : [];
|
|
603
|
+
}
|
|
604
|
+
if (tagName === "table") {
|
|
605
|
+
const rows = [];
|
|
606
|
+
$el.find("tr").each((_, tr) => {
|
|
607
|
+
const cells = [];
|
|
608
|
+
$(tr).find("th, td").each((__, cell) => {
|
|
609
|
+
const text = $(cell).text().trim();
|
|
610
|
+
if (text) cells.push(text);
|
|
611
|
+
});
|
|
612
|
+
if (cells.length > 0) rows.push(cells.join(" | "));
|
|
613
|
+
});
|
|
614
|
+
return rows.map((row) => paragraph([textNode(row)]));
|
|
615
|
+
}
|
|
616
|
+
return walkBlockNodes($, $el, options);
|
|
617
|
+
}
|
|
618
|
+
function walkBlockNodes($, $el, options) {
|
|
619
|
+
const blocks = [];
|
|
620
|
+
$el.contents().each((_, node) => {
|
|
621
|
+
blocks.push(...walkBlockNode($, $(node), options));
|
|
622
|
+
});
|
|
623
|
+
return blocks;
|
|
624
|
+
}
|
|
625
|
+
function normalizeDocContent(content) {
|
|
626
|
+
const normalized = [];
|
|
627
|
+
for (const block of content) {
|
|
628
|
+
if (block.type === "paragraph" && (!block.content || block.content.length === 0)) {
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
normalized.push(block);
|
|
632
|
+
}
|
|
633
|
+
return normalized;
|
|
634
|
+
}
|
|
635
|
+
function walkHtmlToTiptapDoc($, options = {}) {
|
|
636
|
+
const resolved = {
|
|
637
|
+
unwrapLayoutMarkers: options.unwrapLayoutMarkers ?? true
|
|
638
|
+
};
|
|
639
|
+
let content = [];
|
|
640
|
+
const body = $("body");
|
|
641
|
+
if (body.length) {
|
|
642
|
+
content = walkBlockNodes($, body, resolved);
|
|
643
|
+
} else {
|
|
644
|
+
const children = $.root().children();
|
|
645
|
+
if (children.length) {
|
|
646
|
+
content = walkBlockNodes($, children, resolved);
|
|
647
|
+
} else {
|
|
648
|
+
content = walkBlockNodes($, $.root(), resolved);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
content = normalizeDocContent(content);
|
|
652
|
+
if (content.length === 0) {
|
|
653
|
+
content = [{ type: "paragraph" }];
|
|
654
|
+
}
|
|
655
|
+
return { type: "doc", content };
|
|
656
|
+
}
|
|
657
|
+
function prepareHtmlForTiptap($) {
|
|
658
|
+
$("style, script, noscript, template").remove();
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// src/transformers/html-to-tiptap/index.ts
|
|
662
|
+
function htmlToTiptap(html, options = {}) {
|
|
663
|
+
const trimmed = html.trim();
|
|
664
|
+
if (!trimmed) {
|
|
665
|
+
return { type: "doc", content: [{ type: "paragraph" }] };
|
|
666
|
+
}
|
|
667
|
+
const $ = cheerio2.load(trimmed, { xml: false });
|
|
668
|
+
prepareHtmlForTiptap($);
|
|
669
|
+
return walkHtmlToTiptapDoc($, options);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// src/transformers/validate-snapshot.ts
|
|
673
|
+
import { z } from "zod";
|
|
674
|
+
var grapesStyleRuleSchema = z.object({
|
|
675
|
+
selectors: z.array(z.string().min(1)).min(1),
|
|
676
|
+
style: z.record(z.string(), z.string())
|
|
677
|
+
});
|
|
678
|
+
var grapesComponentSchema = z.lazy(
|
|
679
|
+
() => z.object({
|
|
680
|
+
type: z.string().min(1),
|
|
681
|
+
tagName: z.string().optional(),
|
|
682
|
+
attributes: z.record(z.string(), z.string()).optional(),
|
|
683
|
+
classes: z.array(z.string()).optional(),
|
|
684
|
+
components: z.array(grapesComponentSchema).optional(),
|
|
685
|
+
content: z.string().optional(),
|
|
686
|
+
void: z.boolean().optional()
|
|
687
|
+
})
|
|
688
|
+
);
|
|
689
|
+
var grapesProjectSnapshotSchema = z.object({
|
|
690
|
+
content: z.array(grapesComponentSchema),
|
|
691
|
+
styles: z.array(grapesStyleRuleSchema),
|
|
692
|
+
contentHtml: z.string().optional(),
|
|
693
|
+
contentCss: z.string().optional()
|
|
694
|
+
});
|
|
695
|
+
function zodIssuesToValidationIssues(issues) {
|
|
696
|
+
return issues.map((issue) => ({
|
|
697
|
+
code: issue.code,
|
|
698
|
+
message: issue.message,
|
|
699
|
+
path: issue.path.length > 0 ? issue.path.join(".") : void 0
|
|
700
|
+
}));
|
|
701
|
+
}
|
|
702
|
+
function collectComponentTypes(components) {
|
|
703
|
+
const types = [];
|
|
704
|
+
for (const component of components) {
|
|
705
|
+
types.push(component.type);
|
|
706
|
+
if (component.components?.length) {
|
|
707
|
+
types.push(...collectComponentTypes(component.components));
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return types;
|
|
711
|
+
}
|
|
712
|
+
function validateAllowedComponentTypes(snapshot, allowedComponentTypes) {
|
|
713
|
+
const allowlist = new Set(allowedComponentTypes);
|
|
714
|
+
const issues = [];
|
|
715
|
+
for (const componentType of collectComponentTypes(snapshot.content)) {
|
|
716
|
+
if (!allowlist.has(componentType)) {
|
|
717
|
+
issues.push({
|
|
718
|
+
code: "invalid_component_type",
|
|
719
|
+
message: `Component type "${componentType}" is not in allowedComponentTypes`,
|
|
720
|
+
path: "content"
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return issues;
|
|
725
|
+
}
|
|
726
|
+
function validateGrapesProjectSnapshot(snapshot, options = {}) {
|
|
727
|
+
const result = grapesProjectSnapshotSchema.safeParse(snapshot);
|
|
728
|
+
if (!result.success) {
|
|
729
|
+
return { ok: false, issues: zodIssuesToValidationIssues(result.error.issues) };
|
|
730
|
+
}
|
|
731
|
+
if (options.allowedComponentTypes?.length) {
|
|
732
|
+
const typeIssues = validateAllowedComponentTypes(result.data, options.allowedComponentTypes);
|
|
733
|
+
if (typeIssues.length > 0) {
|
|
734
|
+
return { ok: false, issues: typeIssues };
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return { ok: true, issues: [] };
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// src/transformers/validate-tiptap-doc.ts
|
|
741
|
+
import { z as z2 } from "zod";
|
|
742
|
+
var tiptapMarkSchema = z2.object({
|
|
743
|
+
type: z2.string().min(1),
|
|
744
|
+
attrs: z2.record(z2.string(), z2.string()).optional()
|
|
745
|
+
});
|
|
746
|
+
var tiptapNodeSchema = z2.lazy(
|
|
747
|
+
() => z2.union([
|
|
748
|
+
z2.object({
|
|
749
|
+
type: z2.literal("text"),
|
|
750
|
+
text: z2.string(),
|
|
751
|
+
marks: z2.array(tiptapMarkSchema).optional()
|
|
752
|
+
}),
|
|
753
|
+
z2.object({
|
|
754
|
+
type: z2.string().min(1),
|
|
755
|
+
attrs: z2.record(z2.unknown()).optional(),
|
|
756
|
+
content: z2.array(tiptapNodeSchema).optional(),
|
|
757
|
+
marks: z2.array(tiptapMarkSchema).optional()
|
|
758
|
+
})
|
|
759
|
+
])
|
|
760
|
+
);
|
|
761
|
+
var tiptapDocSchema = z2.object({
|
|
762
|
+
type: z2.literal("doc"),
|
|
763
|
+
content: z2.array(tiptapNodeSchema)
|
|
764
|
+
});
|
|
765
|
+
function zodIssuesToValidationIssues2(issues) {
|
|
766
|
+
return issues.map((issue) => ({
|
|
767
|
+
code: issue.code,
|
|
768
|
+
message: issue.message,
|
|
769
|
+
path: issue.path.length > 0 ? issue.path.join(".") : void 0
|
|
770
|
+
}));
|
|
771
|
+
}
|
|
772
|
+
function collectNodeTypes(nodes) {
|
|
773
|
+
const types = [];
|
|
774
|
+
for (const node of nodes) {
|
|
775
|
+
types.push(node.type);
|
|
776
|
+
if (node.content?.length) {
|
|
777
|
+
types.push(...collectNodeTypes(node.content));
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return types;
|
|
781
|
+
}
|
|
782
|
+
function collectMarkTypes(nodes) {
|
|
783
|
+
const types = [];
|
|
784
|
+
for (const node of nodes) {
|
|
785
|
+
if (node.marks?.length) {
|
|
786
|
+
types.push(...node.marks.map((mark) => mark.type));
|
|
787
|
+
}
|
|
788
|
+
if (node.content?.length) {
|
|
789
|
+
types.push(...collectMarkTypes(node.content));
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return types;
|
|
793
|
+
}
|
|
794
|
+
function validateAllowedTypes(doc, allowedNodeTypes, allowedMarkTypes) {
|
|
795
|
+
const issues = [];
|
|
796
|
+
if (allowedNodeTypes) {
|
|
797
|
+
const allowlist = new Set(allowedNodeTypes);
|
|
798
|
+
for (const nodeType of collectNodeTypes(doc.content)) {
|
|
799
|
+
if (!allowlist.has(nodeType)) {
|
|
800
|
+
issues.push({
|
|
801
|
+
code: "invalid_node_type",
|
|
802
|
+
message: `Node type "${nodeType}" is not in allowedNodeTypes`,
|
|
803
|
+
path: "content"
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
if (allowedMarkTypes) {
|
|
809
|
+
const allowlist = new Set(allowedMarkTypes);
|
|
810
|
+
for (const markType of collectMarkTypes(doc.content)) {
|
|
811
|
+
if (!allowlist.has(markType)) {
|
|
812
|
+
issues.push({
|
|
813
|
+
code: "invalid_mark_type",
|
|
814
|
+
message: `Mark type "${markType}" is not in allowedMarkTypes`,
|
|
815
|
+
path: "content"
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return issues;
|
|
821
|
+
}
|
|
822
|
+
function validateTiptapDoc(doc, options = {}) {
|
|
823
|
+
const parsed = tiptapDocSchema.safeParse(doc);
|
|
824
|
+
if (!parsed.success) {
|
|
825
|
+
return { ok: false, issues: zodIssuesToValidationIssues2(parsed.error.issues) };
|
|
826
|
+
}
|
|
827
|
+
const typeIssues = validateAllowedTypes(
|
|
828
|
+
parsed.data,
|
|
829
|
+
options.allowedNodeTypes,
|
|
830
|
+
options.allowedMarkTypes
|
|
831
|
+
);
|
|
832
|
+
if (typeIssues.length > 0) {
|
|
833
|
+
return { ok: false, issues: typeIssues };
|
|
834
|
+
}
|
|
835
|
+
return { ok: true, issues: [] };
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
export {
|
|
839
|
+
cssToStyles,
|
|
840
|
+
htmlToGrapes,
|
|
841
|
+
htmlToTiptap,
|
|
842
|
+
grapesStyleRuleSchema,
|
|
843
|
+
grapesComponentSchema,
|
|
844
|
+
grapesProjectSnapshotSchema,
|
|
845
|
+
validateGrapesProjectSnapshot,
|
|
846
|
+
tiptapMarkSchema,
|
|
847
|
+
tiptapNodeSchema,
|
|
848
|
+
tiptapDocSchema,
|
|
849
|
+
validateTiptapDoc
|
|
850
|
+
};
|
|
851
|
+
//# sourceMappingURL=chunk-CIOYDRY5.js.map
|