gt-sanity 0.0.6 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/index.d.mts +95 -73
  3. package/dist/index.d.ts +95 -73
  4. package/dist/index.js +6233 -1162
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +6292 -1193
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +9 -4
  9. package/src/adapter/core.ts +41 -4
  10. package/src/adapter/getLocales.ts +2 -2
  11. package/src/components/TranslationsProvider.tsx +942 -0
  12. package/src/components/page/BatchProgress.tsx +27 -0
  13. package/src/components/page/ImportAllDialog.tsx +51 -0
  14. package/src/components/page/ImportMissingDialog.tsx +55 -0
  15. package/src/components/page/TranslateAllDialog.tsx +55 -0
  16. package/src/components/page/TranslationsTable.tsx +81 -0
  17. package/src/components/page/TranslationsTool.tsx +299 -837
  18. package/src/components/shared/BaseTranslationWrapper.tsx +82 -0
  19. package/src/components/shared/LocaleCheckbox.tsx +47 -0
  20. package/src/components/shared/SingleDocumentView.tsx +108 -0
  21. package/src/components/tab/TranslationView.tsx +379 -0
  22. package/src/components/tab/TranslationsTab.tsx +25 -0
  23. package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +6 -9
  24. package/src/configuration/baseDocumentLevelConfig/helpers/createI18nDocAndPatchMetadata.ts +5 -24
  25. package/src/configuration/baseDocumentLevelConfig/helpers/patchI18nDoc.ts +3 -23
  26. package/src/configuration/baseDocumentLevelConfig/index.ts +16 -68
  27. package/src/configuration/baseFieldLevelConfig.ts +15 -50
  28. package/src/index.ts +29 -43
  29. package/src/sanity-api/findDocuments.ts +44 -0
  30. package/src/sanity-api/publishDocuments.ts +49 -0
  31. package/src/sanity-api/resolveRefs.ts +146 -0
  32. package/src/serialization/BaseDocumentMerger.ts +138 -0
  33. package/src/serialization/BaseSerializationConfig.ts +220 -0
  34. package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/documentLevelDeserialization.test.ts.snap +189 -0
  35. package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/fieldLevelDeserialization.test.ts.snap +107 -0
  36. package/src/serialization/__tests__/BaseDocumentDeserializer/baseDeserialization.test.ts +397 -0
  37. package/src/serialization/__tests__/BaseDocumentDeserializer/documentLevelDeserialization.test.ts +107 -0
  38. package/src/serialization/__tests__/BaseDocumentDeserializer/fieldLevelDeserialization.test.ts +107 -0
  39. package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/documentLevelMerge.test.ts.snap +193 -0
  40. package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/fieldLevelMerge.test.ts.snap +97 -0
  41. package/src/serialization/__tests__/BaseDocumentMerger/baseMerge.test.ts +36 -0
  42. package/src/serialization/__tests__/BaseDocumentMerger/documentLevelMerge.test.ts +96 -0
  43. package/src/serialization/__tests__/BaseDocumentMerger/fieldLevelMerge.test.ts +142 -0
  44. package/src/serialization/__tests__/BaseDocumentMerger/utils.ts +52 -0
  45. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentInlineMarks.test.ts.snap +39 -0
  46. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentLevelSerialization.test.ts.snap +8 -0
  47. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/fieldLevelSerialization.test.ts.snap +8 -0
  48. package/src/serialization/__tests__/BaseDocumentSerializer/baseSerialization.test.ts +345 -0
  49. package/src/serialization/__tests__/BaseDocumentSerializer/documentInlineMarks.test.ts +53 -0
  50. package/src/serialization/__tests__/BaseDocumentSerializer/documentLevelSerialization.test.ts +120 -0
  51. package/src/serialization/__tests__/BaseDocumentSerializer/fieldLevelSerialization.test.ts +153 -0
  52. package/src/serialization/__tests__/BaseDocumentSerializer/utils.ts +27 -0
  53. package/src/serialization/__tests__/README +2 -0
  54. package/src/serialization/__tests__/__fixtures__/annotationAndInlineBlocks.json +140 -0
  55. package/src/serialization/__tests__/__fixtures__/customStyles.json +62 -0
  56. package/src/serialization/__tests__/__fixtures__/documentInlineMarks.json +70 -0
  57. package/src/serialization/__tests__/__fixtures__/documentLevelArticle.json +185 -0
  58. package/src/serialization/__tests__/__fixtures__/fieldLevelArticle.json +107 -0
  59. package/src/serialization/__tests__/__fixtures__/inlineDocumentLevelArticle.json +134 -0
  60. package/src/serialization/__tests__/__fixtures__/inlineSchema.ts +270 -0
  61. package/src/serialization/__tests__/__fixtures__/messy-html.html +26 -0
  62. package/src/serialization/__tests__/__fixtures__/nestedLanguageFields.json +54 -0
  63. package/src/serialization/__tests__/__fixtures__/schema.ts +310 -0
  64. package/src/serialization/__tests__/global.setup.ts +40 -0
  65. package/src/serialization/__tests__/helpers.ts +132 -0
  66. package/src/serialization/data.ts +82 -0
  67. package/src/serialization/deserialize/BaseDocumentDeserializer.ts +171 -0
  68. package/src/serialization/deserialize/helpers.ts +42 -0
  69. package/src/serialization/helpers.ts +18 -0
  70. package/src/serialization/index.ts +11 -0
  71. package/src/serialization/serialize/fieldFilters.ts +124 -0
  72. package/src/serialization/serialize/index.ts +284 -0
  73. package/src/serialization/types.ts +41 -0
  74. package/src/translation/importDocument.ts +4 -5
  75. package/src/translation/uploadFiles.ts +1 -1
  76. package/src/types.ts +3 -19
  77. package/src/utils/batchProcessor.ts +111 -0
  78. package/src/utils/importUtils.ts +95 -0
  79. package/src/utils/serialize.ts +25 -5
  80. package/src/utils/shared.ts +1 -1
  81. package/src/adapter/index.ts +0 -13
  82. package/src/components/NewTask.tsx +0 -251
  83. package/src/components/TaskView.tsx +0 -257
  84. package/src/components/TranslationContext.tsx +0 -24
  85. package/src/components/TranslationView.tsx +0 -114
  86. package/src/components/TranslationsTab.tsx +0 -181
  87. /package/src/components/{LanguageStatus.tsx → shared/LanguageStatus.tsx} +0 -0
  88. /package/src/components/{ProgressBar.tsx → shared/ProgressBar.tsx} +0 -0
@@ -0,0 +1,146 @@
1
+ import { SanityClient, SanityDocument } from 'sanity';
2
+ import { pluginConfig } from '../adapter/core';
3
+
4
+ interface Reference {
5
+ _type: 'reference';
6
+ _ref: string;
7
+ }
8
+
9
+ interface TranslationMetadata {
10
+ _id: string;
11
+ _type: 'translation.metadata';
12
+ translations: {
13
+ _key: string;
14
+ value: Reference;
15
+ }[];
16
+ }
17
+
18
+ /**
19
+ * Function that:
20
+ * 1. Finds all references in the document
21
+ * 2. Fetches the referenced documents, looks for the translation.metadata file for each reference
22
+ * 3. Updates the document reference with the other translated document reference
23
+ * 4. Returns the document with the updated references
24
+ */
25
+ export async function resolveRefs(
26
+ doc: SanityDocument,
27
+ locale: string,
28
+ client: SanityClient
29
+ ) {
30
+ const references = findReferences(doc);
31
+
32
+ if (references.length === 0) {
33
+ return doc;
34
+ }
35
+
36
+ const translatedRefs = await resolveTranslatedReferences(
37
+ references,
38
+ locale,
39
+ client
40
+ );
41
+ return updateDocumentReferences(doc, translatedRefs);
42
+ }
43
+
44
+ /**
45
+ * Recursively finds all references in a document or object
46
+ */
47
+ function findReferences(
48
+ obj: any,
49
+ path: string[] = []
50
+ ): { ref: Reference; path: string[] }[] {
51
+ if (!obj || typeof obj !== 'object') {
52
+ return [];
53
+ }
54
+
55
+ const references: { ref: Reference; path: string[] }[] = [];
56
+
57
+ if (obj._ref) {
58
+ references.push({ ref: obj as Reference, path });
59
+ }
60
+
61
+ if (Array.isArray(obj)) {
62
+ obj.forEach((item, index) => {
63
+ references.push(...findReferences(item, [...path, index.toString()]));
64
+ });
65
+ } else {
66
+ Object.keys(obj).forEach((key) => {
67
+ references.push(...findReferences(obj[key], [...path, key]));
68
+ });
69
+ }
70
+
71
+ return references;
72
+ }
73
+
74
+ /**
75
+ * Fetches translation metadata and resolves translated references
76
+ */
77
+ async function resolveTranslatedReferences(
78
+ references: { ref: Reference; path: string[] }[],
79
+ locale: string,
80
+ client: SanityClient
81
+ ): Promise<Map<string, string>> {
82
+ const refIds = references.map((r) => r.ref._ref);
83
+ const translatedRefs = new Map<string, string>();
84
+
85
+ if (refIds.length === 0) {
86
+ return translatedRefs;
87
+ }
88
+
89
+ const sourceLocale = pluginConfig.getSourceLocale();
90
+
91
+ // Optimized GROQ query that directly returns only the needed translation pairs
92
+ const query = `*[_type == "translation.metadata" && count(translations[_key == $sourceLocale && value._ref in $refIds]) > 0] {
93
+ "originalRef": translations[_key == $sourceLocale][0].value._ref,
94
+ "translatedRef": translations[_key == $locale][0].value._ref
95
+ }[defined(originalRef) && defined(translatedRef)]`;
96
+
97
+ const translationPairs: { originalRef: string; translatedRef: string }[] =
98
+ await client.fetch(query, { refIds, sourceLocale, locale });
99
+
100
+ // Build the translation map
101
+ for (const { originalRef, translatedRef } of translationPairs) {
102
+ translatedRefs.set(originalRef, translatedRef);
103
+ }
104
+
105
+ return translatedRefs;
106
+ }
107
+
108
+ /**
109
+ * Updates document references with their translated versions
110
+ */
111
+ function updateDocumentReferences(
112
+ doc: SanityDocument,
113
+ translatedRefs: Map<string, string>
114
+ ): SanityDocument {
115
+ return updateReferencesRecursive(doc, translatedRefs);
116
+ }
117
+
118
+ /**
119
+ * Recursively updates references in an object
120
+ */
121
+ function updateReferencesRecursive(
122
+ obj: any,
123
+ translatedRefs: Map<string, string>
124
+ ): any {
125
+ if (!obj || typeof obj !== 'object') {
126
+ return obj;
127
+ }
128
+
129
+ if (obj._ref && translatedRefs.has(obj._ref)) {
130
+ return {
131
+ ...obj,
132
+ _ref: translatedRefs.get(obj._ref),
133
+ };
134
+ }
135
+
136
+ if (Array.isArray(obj)) {
137
+ return obj.map((item) => updateReferencesRecursive(item, translatedRefs));
138
+ }
139
+
140
+ const updated: any = {};
141
+ Object.keys(obj).forEach((key) => {
142
+ updated[key] = updateReferencesRecursive(obj[key], translatedRefs);
143
+ });
144
+
145
+ return updated;
146
+ }
@@ -0,0 +1,138 @@
1
+ // Adapted from https://github.com/sanity-io/sanity-naive-html-serializer
2
+
3
+ import { Merger } from './types';
4
+ import { SanityDocument } from 'sanity';
5
+ import { extractWithPath, arrayToJSONMatchPath } from '@sanity/mutator';
6
+
7
+ const reconcileArray = (origArray: any[], translatedArray: any[]): any[] => {
8
+ //arrays of strings don't have keys, so just replace the array and return
9
+ if (translatedArray && translatedArray.some((el) => typeof el === 'string')) {
10
+ return translatedArray;
11
+ }
12
+
13
+ //deep copy needed for field level patching
14
+ const combined = JSON.parse(JSON.stringify(origArray));
15
+
16
+ translatedArray.forEach((block) => {
17
+ if (!block._key) {
18
+ return;
19
+ }
20
+ const foundBlockIdx = origArray.findIndex(
21
+ (origBlock) => origBlock._key === block._key
22
+ );
23
+ if (foundBlockIdx < 0) {
24
+ //eslint-disable-next-line no-console
25
+ console.warn(
26
+ `This block no longer exists on the original document. Was it removed? ${JSON.stringify(
27
+ block
28
+ )}`
29
+ );
30
+ } else if (
31
+ origArray[foundBlockIdx]._type === 'block' ||
32
+ origArray[foundBlockIdx]._type === 'span'
33
+ ) {
34
+ combined[foundBlockIdx] = block;
35
+ } else if (Array.isArray(origArray[foundBlockIdx])) {
36
+ combined[foundBlockIdx] = reconcileArray(origArray[foundBlockIdx], block);
37
+ } else {
38
+ combined[foundBlockIdx] = reconcileObject(
39
+ origArray[foundBlockIdx],
40
+ block
41
+ );
42
+ }
43
+ });
44
+ return combined;
45
+ };
46
+
47
+ const reconcileObject = (
48
+ origObject: Record<string, any>,
49
+ translatedObject: Record<string, any>
50
+ ): Record<string, any> => {
51
+ if (
52
+ typeof translatedObject !== 'object' ||
53
+ !Object.keys(translatedObject).length
54
+ ) {
55
+ return origObject;
56
+ }
57
+
58
+ const updatedObj = JSON.parse(JSON.stringify(origObject));
59
+ Object.entries(translatedObject).forEach(([key, value]) => {
60
+ if (!value || key[0] === '_') {
61
+ return;
62
+ }
63
+ if (typeof value === 'string') {
64
+ updatedObj[key] = value;
65
+ } else if (Array.isArray(value)) {
66
+ updatedObj[key] = reconcileArray(origObject[key] ?? [], value);
67
+ } else {
68
+ updatedObj[key] = reconcileObject(origObject[key] ?? {}, value);
69
+ }
70
+ });
71
+ return updatedObj;
72
+ };
73
+
74
+ const fieldLevelMerge = (
75
+ translatedFields: Record<string, any>,
76
+ //should be fetched according to the revision and id of the translated obj above
77
+ baseDoc: SanityDocument,
78
+ localeId: string,
79
+ baseLang: string = 'en'
80
+ ): Record<string, any> => {
81
+ const merged: Record<string, any> = {};
82
+ const metaKeys = ['_rev', '_id', '_type'];
83
+ metaKeys.forEach((metaKey) => {
84
+ if (translatedFields[metaKey]) {
85
+ merged[metaKey] = translatedFields[metaKey];
86
+ }
87
+ });
88
+
89
+ //get any field that matches the base language, because it's been translated
90
+ const originPaths = extractWithPath(`..${baseLang}`, translatedFields);
91
+ originPaths.forEach((match) => {
92
+ const origVal = extractWithPath(
93
+ arrayToJSONMatchPath(match.path),
94
+ baseDoc
95
+ )[0].value;
96
+ const translatedVal = extractWithPath(
97
+ arrayToJSONMatchPath(match.path),
98
+ translatedFields
99
+ )[0].value;
100
+ let valToPatch;
101
+ if (typeof translatedVal === 'string') {
102
+ valToPatch = translatedVal;
103
+ } else if (Array.isArray(translatedVal) && translatedVal.length) {
104
+ valToPatch = reconcileArray((origVal as Array<any>) ?? [], translatedVal);
105
+ } else if (
106
+ typeof translatedVal === 'object' &&
107
+ Object.keys(translatedVal as Record<string, any>).length
108
+ ) {
109
+ valToPatch = reconcileObject(
110
+ origVal ?? {},
111
+ translatedVal as Record<string, any>
112
+ );
113
+ }
114
+ const destinationPath = [
115
+ ...match.path.slice(0, match.path.length - 1), //cut off the "en"
116
+ localeId.replace('-', '_'), // replace it with our locale
117
+ ];
118
+
119
+ merged[arrayToJSONMatchPath(destinationPath)] = valToPatch;
120
+ });
121
+
122
+ return merged;
123
+ };
124
+
125
+ const documentLevelMerge = (
126
+ translatedFields: Record<string, any>,
127
+ //should be fetched according to the revision and id of the translated obj above
128
+ baseDoc: SanityDocument
129
+ ): Record<string, any> => {
130
+ return reconcileObject(baseDoc, translatedFields);
131
+ };
132
+
133
+ export const BaseDocumentMerger: Merger = {
134
+ fieldLevelMerge,
135
+ documentLevelMerge,
136
+ reconcileArray,
137
+ reconcileObject,
138
+ };
@@ -0,0 +1,220 @@
1
+ // Adapted from https://github.com/sanity-io/sanity-naive-html-serializer
2
+
3
+ import { PortableTextBlockStyle } from '@portabletext/types';
4
+
5
+ import {
6
+ PortableTextBlockComponent,
7
+ PortableTextListComponent,
8
+ PortableTextListItemComponent,
9
+ PortableTextMarkComponent,
10
+ PortableTextHtmlComponents,
11
+ } from '@portabletext/to-html';
12
+
13
+ import { htmlToBlocks } from '@portabletext/block-tools';
14
+ import { blockContentType } from './deserialize/helpers';
15
+ import { PortableTextObject, PortableTextTextBlock, TypedObject } from 'sanity';
16
+ import { attachGTData, detachGTData } from './data';
17
+
18
+ export const defaultStopTypes = [
19
+ 'reference',
20
+ 'date',
21
+ 'datetime',
22
+ 'file',
23
+ 'geopoint',
24
+ 'image',
25
+ 'number',
26
+ 'crop',
27
+ 'hotspot',
28
+ 'boolean',
29
+ 'url',
30
+ 'color',
31
+ 'code',
32
+ ];
33
+
34
+ export const defaultMarks: Record<string, PortableTextMarkComponent> = {};
35
+
36
+ export const defaultPortableTextBlockStyles: Record<
37
+ PortableTextBlockStyle,
38
+ PortableTextBlockComponent | undefined
39
+ > = {
40
+ normal: ({ value, children }) => `<p id="${value._key}">${children}</p>`,
41
+ blockquote: ({ value, children }) =>
42
+ `<blockquote id="${value._key}">${children}</blockquote>`,
43
+ h1: ({ value, children }) => `<h1 id="${value._key}">${children}</h1>`,
44
+ h2: ({ value, children }) => `<h2 id="${value._key}">${children}</h2>`,
45
+ h3: ({ value, children }) => `<h3 id="${value._key}">${children}</h3>`,
46
+ h4: ({ value, children }) => `<h4 id="${value._key}">${children}</h4>`,
47
+ h5: ({ value, children }) => `<h5 id="${value._key}">${children}</h5>`,
48
+ h6: ({ value, children }) => `<h6 id="${value._key}">${children}</h6>`,
49
+ };
50
+
51
+ const defaultLists: Record<'number' | 'bullet', PortableTextListComponent> = {
52
+ number: ({ value, children }) =>
53
+ `<ol id="${value._key.replace('-parent', '')}">${children}</ol>`,
54
+ bullet: ({ value, children }) =>
55
+ `<ul id="${value._key.replace('-parent', '')}">${children}</ul>`,
56
+ };
57
+
58
+ const defaultListItem: PortableTextListItemComponent = ({
59
+ value,
60
+ children,
61
+ }) => {
62
+ const { _key, level } = value;
63
+ return `<li id="${(_key || '').replace('-parent', '')}" data-level="${level}">${children}</li>`;
64
+ };
65
+
66
+ const unknownBlockFunc: PortableTextBlockComponent = ({ value, children }) =>
67
+ `<p id="${value._key}" data-type="unknown-block-style" data-style="${value.style}">${children}</p>`;
68
+
69
+ export const customSerializers: Partial<PortableTextHtmlComponents> = {
70
+ unknownType: ({ value }: { value: Record<string, any> }) =>
71
+ `<div class="${value._type}"></div>`,
72
+ types: {},
73
+ marks: defaultMarks,
74
+ block: defaultPortableTextBlockStyles,
75
+ list: defaultLists,
76
+ listItem: defaultListItem,
77
+ unknownBlockStyle: unknownBlockFunc,
78
+ };
79
+
80
+ export const customDeserializers: Record<string, any> = { types: {} };
81
+
82
+ export const customBlockDeserializers: Array<any> = [
83
+ // handle marks with data-gt-internal
84
+ {
85
+ deserialize(
86
+ el: HTMLParagraphElement,
87
+ next: (
88
+ elements: Node | Node[] | NodeList
89
+ ) => TypedObject | TypedObject[] | undefined
90
+ ): PortableTextTextBlock | TypedObject | undefined {
91
+ if (!el.hasChildNodes()) {
92
+ return undefined;
93
+ }
94
+
95
+ if (!el.getAttribute('data-gt-internal')) {
96
+ return undefined;
97
+ }
98
+
99
+ const { html, data } = detachGTData(el.outerHTML);
100
+ const block = htmlToBlocks(html, blockContentType)[0];
101
+
102
+ const children = next(el.childNodes);
103
+
104
+ let markDefs: PortableTextObject[] = [];
105
+ if ('markDefs' in block) {
106
+ markDefs = (block.markDefs as PortableTextObject[]) ?? [];
107
+ }
108
+ if (data?.markDef) {
109
+ markDefs.push(data.markDef as PortableTextObject);
110
+ }
111
+ if (children) {
112
+ (children as any).forEach((child: any) => {
113
+ child.marks = data?.markDef?._key
114
+ ? [...(child.marks || []), data.markDef._key]
115
+ : [...(child.marks || [])];
116
+ });
117
+ }
118
+ // Resolve marks in the child nodes
119
+ const output = {
120
+ ...block,
121
+ markDefs,
122
+ children,
123
+ };
124
+ return output;
125
+ },
126
+ },
127
+ //handle undeclared styles
128
+ {
129
+ deserialize(
130
+ el: HTMLParagraphElement,
131
+ next: (
132
+ elements: Node | Node[] | NodeList
133
+ ) => TypedObject | TypedObject[] | undefined
134
+ ): PortableTextTextBlock | TypedObject | undefined {
135
+ if (!el.hasChildNodes()) {
136
+ return undefined;
137
+ }
138
+
139
+ if (el.getAttribute('data-type') !== 'unknown-block-style') {
140
+ return undefined;
141
+ }
142
+
143
+ const style = el.getAttribute('data-style') ?? '';
144
+ const block = htmlToBlocks(el.outerHTML, blockContentType)[0];
145
+
146
+ return {
147
+ ...block,
148
+ style,
149
+ children: next(el.childNodes),
150
+ };
151
+ },
152
+ },
153
+ //handle list items
154
+ {
155
+ deserialize(
156
+ el: HTMLParagraphElement,
157
+ next: (
158
+ elements: Node | Node[] | NodeList
159
+ ) => TypedObject | TypedObject[] | undefined
160
+ ): PortableTextTextBlock | TypedObject | undefined {
161
+ if (!el.hasChildNodes()) {
162
+ return undefined;
163
+ }
164
+
165
+ if (el.tagName.toLowerCase() !== 'li') {
166
+ return undefined;
167
+ }
168
+
169
+ const tagsToStyle: Record<string, string> = {
170
+ ul: 'bullet',
171
+ ol: 'number',
172
+ };
173
+
174
+ const parent = el.parentNode as HTMLUListElement | HTMLOListElement;
175
+ if (!parent || !parent.tagName) {
176
+ return undefined;
177
+ }
178
+
179
+ const listItem = tagsToStyle[parent.tagName.toLowerCase()];
180
+ if (!listItem) {
181
+ return undefined;
182
+ }
183
+
184
+ const level =
185
+ el.getAttribute('data-level') &&
186
+ parseInt(el.getAttribute('data-level') || '0', 10);
187
+ const _key = el.id;
188
+ let block = htmlToBlocks(parent.outerHTML, blockContentType)[0];
189
+ const customStyle = el.children?.[0]?.getAttribute('data-style');
190
+
191
+ //check if the object inside is also serialized -- that means it has a style
192
+ //or custom annotation and we should use childNode serialization
193
+ const regex = new RegExp(/<("[^"]*"|'[^']*'|[^'">])*>/);
194
+ if (regex.test(el.innerHTML)) {
195
+ const newBlock = htmlToBlocks(el.innerHTML, blockContentType)[0];
196
+ if (newBlock) {
197
+ block = {
198
+ ...block,
199
+ ...newBlock,
200
+ // @ts-ignore
201
+ style: customStyle ?? (newBlock as PortableTextTextBlock).style,
202
+ };
203
+
204
+ //next(childNodes) plays poorly with custom styles, issue to be filed.
205
+ if (customStyle) {
206
+ return block as PortableTextTextBlock;
207
+ }
208
+ }
209
+ }
210
+
211
+ return {
212
+ ...block,
213
+ level,
214
+ _key,
215
+ listItem,
216
+ children: next(el.childNodes),
217
+ };
218
+ },
219
+ },
220
+ ];
@@ -0,0 +1,189 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Global test of working doc-level functionality and snapshot match 1`] = `
4
+ {
5
+ "_id": "drafts.d8ffc675-ce86-4f60-9ac8-da164cde3b0a",
6
+ "_rev": "fxcnqj-uew-gr7-v6r-z965tfm69",
7
+ "_type": "documentLevelArticle",
8
+ "config": {
9
+ "_type": "objectField",
10
+ "nestedArrayField": [
11
+ {
12
+ "_key": "4a58adc7c507",
13
+ "_type": "block",
14
+ "children": [
15
+ {
16
+ "_key": "randomKey-16",
17
+ "_type": "span",
18
+ "marks": [],
19
+ "text": "This is block text 1 level deep",
20
+ },
21
+ ],
22
+ "markDefs": [],
23
+ "style": "normal",
24
+ },
25
+ ],
26
+ "objectAsField": {
27
+ "_type": "childObjectField",
28
+ "content": [
29
+ {
30
+ "_key": "1aa228ac848b",
31
+ "_type": "block",
32
+ "children": [
33
+ {
34
+ "_key": "randomKey-14",
35
+ "_type": "span",
36
+ "marks": [],
37
+ "text": "This is a block text 2 levels deep",
38
+ },
39
+ ],
40
+ "markDefs": [],
41
+ "style": "normal",
42
+ },
43
+ ],
44
+ "title": "This is one level deeper",
45
+ },
46
+ "title": "This is an object nested in a document",
47
+ },
48
+ "content": [
49
+ {
50
+ "_key": "a4d177e92666",
51
+ "_type": "block",
52
+ "children": [
53
+ {
54
+ "_key": "randomKey-18",
55
+ "_type": "span",
56
+ "marks": [],
57
+ "text": "This is block text at the top level.",
58
+ },
59
+ ],
60
+ "markDefs": [],
61
+ "style": "normal",
62
+ },
63
+ {
64
+ "_key": "c0313627775e",
65
+ "_type": "objectField",
66
+ "objectAsField": {
67
+ "_type": "childObjectField",
68
+ "content": [
69
+ {
70
+ "_key": "4f24f6fbfae7",
71
+ "_type": "block",
72
+ "children": [
73
+ {
74
+ "_key": "randomKey-24",
75
+ "_type": "span",
76
+ "marks": [],
77
+ "text": "This is block text in a nested object in an object in top-level block text.",
78
+ },
79
+ ],
80
+ "markDefs": [],
81
+ "style": "normal",
82
+ },
83
+ ],
84
+ "title": "This is a nested object in an object in top-level block text.",
85
+ },
86
+ "title": "This is an object in top-level block text.",
87
+ },
88
+ {
89
+ "_key": "9e2ab13c6d63",
90
+ "_type": "block",
91
+ "children": [
92
+ {
93
+ "_key": "randomKey-26",
94
+ "_type": "span",
95
+ "marks": [],
96
+ "text": "This is h1 text",
97
+ },
98
+ ],
99
+ "markDefs": [],
100
+ "style": "h1",
101
+ },
102
+ {
103
+ "_key": "297142fbaf21",
104
+ "_type": "block",
105
+ "children": [
106
+ {
107
+ "_key": "randomKey-28",
108
+ "_type": "span",
109
+ "marks": [],
110
+ "text": "This is h2 text",
111
+ },
112
+ ],
113
+ "markDefs": [],
114
+ "style": "h2",
115
+ },
116
+ {
117
+ "_key": "76e648fc3845",
118
+ "_type": "block",
119
+ "children": [
120
+ {
121
+ "_key": "randomKey-32",
122
+ "_type": "span",
123
+ "marks": [],
124
+ "text": "Bullet 1",
125
+ },
126
+ ],
127
+ "level": 1,
128
+ "listItem": "bullet",
129
+ "markDefs": [],
130
+ "style": "normal",
131
+ },
132
+ {
133
+ "_key": "d090cd8b27d2",
134
+ "_type": "block",
135
+ "children": [
136
+ {
137
+ "_key": "randomKey-36",
138
+ "_type": "span",
139
+ "marks": [],
140
+ "text": "nested bullet a",
141
+ },
142
+ ],
143
+ "level": 2,
144
+ "listItem": "bullet",
145
+ "markDefs": [],
146
+ "style": "normal",
147
+ },
148
+ {
149
+ "_key": "1cdffa5d50f5",
150
+ "_type": "block",
151
+ "children": [
152
+ {
153
+ "_key": "randomKey-42",
154
+ "_type": "span",
155
+ "marks": [],
156
+ "text": "Styled bullet 2",
157
+ },
158
+ ],
159
+ "level": 1,
160
+ "listItem": "bullet",
161
+ "markDefs": [],
162
+ "style": "h2",
163
+ },
164
+ {
165
+ "_key": "da29f5063059",
166
+ "_type": "block",
167
+ "children": [
168
+ {
169
+ "_key": "randomKey-48",
170
+ "_type": "span",
171
+ "marks": [],
172
+ "text": "Number 1",
173
+ },
174
+ ],
175
+ "level": 1,
176
+ "listItem": "number",
177
+ "markDefs": [],
178
+ "style": "h3",
179
+ },
180
+ ],
181
+ "snippet": "This is text in my text field.",
182
+ "tags": [
183
+ "tag 1",
184
+ "tag 2",
185
+ "tag 3",
186
+ ],
187
+ "title": "My Document-Level Article",
188
+ }
189
+ `;