@payloadcms/plugin-import-export 4.0.0-internal.5b1e7cd → 4.0.0-internal.5e065b3

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 (194) hide show
  1. package/dist/components/CollectionField/index.d.ts +3 -2
  2. package/dist/components/CollectionField/index.d.ts.map +1 -1
  3. package/dist/components/CollectionField/index.js.map +1 -1
  4. package/dist/components/ExportListMenuItem/index.d.ts.map +1 -1
  5. package/dist/components/ExportListMenuItem/index.js +14 -12
  6. package/dist/components/ExportListMenuItem/index.js.map +1 -1
  7. package/dist/components/ExportPreview/index.css +52 -0
  8. package/dist/components/ExportPreview/index.d.ts +1 -1
  9. package/dist/components/ExportPreview/index.d.ts.map +1 -1
  10. package/dist/components/ExportPreview/index.js +12 -2
  11. package/dist/components/ExportPreview/index.js.map +1 -1
  12. package/dist/components/FieldsToExport/index.d.ts +3 -2
  13. package/dist/components/FieldsToExport/index.d.ts.map +1 -1
  14. package/dist/components/FieldsToExport/index.js +18 -15
  15. package/dist/components/FieldsToExport/index.js.map +1 -1
  16. package/dist/components/FieldsToExport/reduceFields.d.ts +6 -0
  17. package/dist/components/FieldsToExport/reduceFields.d.ts.map +1 -1
  18. package/dist/components/FieldsToExport/reduceFields.js +4 -0
  19. package/dist/components/FieldsToExport/reduceFields.js.map +1 -1
  20. package/dist/components/FormatField/index.d.ts +3 -2
  21. package/dist/components/FormatField/index.d.ts.map +1 -1
  22. package/dist/components/FormatField/index.js +6 -3
  23. package/dist/components/FormatField/index.js.map +1 -1
  24. package/dist/components/ImportListMenuItem/index.d.ts.map +1 -1
  25. package/dist/components/ImportListMenuItem/index.js +14 -12
  26. package/dist/components/ImportListMenuItem/index.js.map +1 -1
  27. package/dist/components/ImportPreview/index.css +45 -0
  28. package/dist/components/ImportPreview/index.d.ts +1 -1
  29. package/dist/components/ImportPreview/index.d.ts.map +1 -1
  30. package/dist/components/ImportPreview/index.js +44 -11
  31. package/dist/components/ImportPreview/index.js.map +1 -1
  32. package/dist/components/LimitField/index.d.ts +3 -2
  33. package/dist/components/LimitField/index.d.ts.map +1 -1
  34. package/dist/components/LimitField/index.js.map +1 -1
  35. package/dist/components/LocaleField/index.d.ts +3 -2
  36. package/dist/components/LocaleField/index.d.ts.map +1 -1
  37. package/dist/components/LocaleField/index.js.map +1 -1
  38. package/dist/components/Page/index.d.ts +4 -3
  39. package/dist/components/Page/index.d.ts.map +1 -1
  40. package/dist/components/Page/index.js +1 -1
  41. package/dist/components/Page/index.js.map +1 -1
  42. package/dist/components/RelationshipCell/getRelationshipGroupKey.d.ts +7 -0
  43. package/dist/components/RelationshipCell/getRelationshipGroupKey.d.ts.map +1 -0
  44. package/dist/components/RelationshipCell/getRelationshipGroupKey.js +3 -0
  45. package/dist/components/RelationshipCell/getRelationshipGroupKey.js.map +1 -0
  46. package/dist/components/RelationshipCell/getRelationshipGroups.d.ts +43 -0
  47. package/dist/components/RelationshipCell/getRelationshipGroups.d.ts.map +1 -0
  48. package/dist/components/RelationshipCell/getRelationshipGroups.js +109 -0
  49. package/dist/components/RelationshipCell/getRelationshipGroups.js.map +1 -0
  50. package/dist/components/RelationshipCell/index.css +30 -0
  51. package/dist/components/RelationshipCell/index.d.ts +15 -0
  52. package/dist/components/RelationshipCell/index.d.ts.map +1 -0
  53. package/dist/components/RelationshipCell/index.js +57 -0
  54. package/dist/components/RelationshipCell/index.js.map +1 -0
  55. package/dist/components/SortBy/index.css +9 -0
  56. package/dist/components/SortBy/index.d.ts +4 -3
  57. package/dist/components/SortBy/index.d.ts.map +1 -1
  58. package/dist/components/SortBy/index.js +1 -2
  59. package/dist/components/SortBy/index.js.map +1 -1
  60. package/dist/components/SortOrder/index.css +9 -0
  61. package/dist/components/SortOrder/index.d.ts +4 -3
  62. package/dist/components/SortOrder/index.d.ts.map +1 -1
  63. package/dist/components/SortOrder/index.js +1 -2
  64. package/dist/components/SortOrder/index.js.map +1 -1
  65. package/dist/export/batchProcessor.d.ts +12 -3
  66. package/dist/export/batchProcessor.d.ts.map +1 -1
  67. package/dist/export/batchProcessor.js +61 -15
  68. package/dist/export/batchProcessor.js.map +1 -1
  69. package/dist/export/createExport.d.ts +4 -2
  70. package/dist/export/createExport.d.ts.map +1 -1
  71. package/dist/export/createExport.js +105 -22
  72. package/dist/export/createExport.js.map +1 -1
  73. package/dist/export/getCreateExportCollectionTask.d.ts.map +1 -1
  74. package/dist/export/getCreateExportCollectionTask.js +11 -0
  75. package/dist/export/getCreateExportCollectionTask.js.map +1 -1
  76. package/dist/export/getExportCollection.d.ts.map +1 -1
  77. package/dist/export/getExportCollection.js +9 -4
  78. package/dist/export/getExportCollection.js.map +1 -1
  79. package/dist/export/handleDownload.d.ts.map +1 -1
  80. package/dist/export/handleDownload.js +7 -3
  81. package/dist/export/handleDownload.js.map +1 -1
  82. package/dist/export/handlePreview.d.ts.map +1 -1
  83. package/dist/export/handlePreview.js +60 -16
  84. package/dist/export/handlePreview.js.map +1 -1
  85. package/dist/exports/types.d.ts +1 -1
  86. package/dist/exports/types.d.ts.map +1 -1
  87. package/dist/exports/types.js.map +1 -1
  88. package/dist/import/batchProcessor.d.ts +17 -4
  89. package/dist/import/batchProcessor.d.ts.map +1 -1
  90. package/dist/import/batchProcessor.js +63 -34
  91. package/dist/import/batchProcessor.js.map +1 -1
  92. package/dist/import/createImport.d.ts +3 -11
  93. package/dist/import/createImport.d.ts.map +1 -1
  94. package/dist/import/createImport.js +42 -54
  95. package/dist/import/createImport.js.map +1 -1
  96. package/dist/import/getCreateImportCollectionTask.d.ts.map +1 -1
  97. package/dist/import/getCreateImportCollectionTask.js +5 -2
  98. package/dist/import/getCreateImportCollectionTask.js.map +1 -1
  99. package/dist/import/getImportCollection.d.ts.map +1 -1
  100. package/dist/import/getImportCollection.js +6 -4
  101. package/dist/import/getImportCollection.js.map +1 -1
  102. package/dist/import/handlePreview.d.ts.map +1 -1
  103. package/dist/import/handlePreview.js +37 -7
  104. package/dist/import/handlePreview.js.map +1 -1
  105. package/dist/index.d.ts +51 -7
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +36 -8
  108. package/dist/index.js.map +1 -1
  109. package/dist/translations/languages/en.d.ts.map +1 -1
  110. package/dist/types.d.ts +205 -42
  111. package/dist/types.d.ts.map +1 -1
  112. package/dist/types.js.map +1 -1
  113. package/dist/utilities/applyFieldHooks.d.ts +22 -0
  114. package/dist/utilities/applyFieldHooks.d.ts.map +1 -0
  115. package/dist/utilities/applyFieldHooks.js +117 -0
  116. package/dist/utilities/applyFieldHooks.js.map +1 -0
  117. package/dist/utilities/collectDisabledFieldPaths.d.ts.map +1 -1
  118. package/dist/utilities/collectDisabledFieldPaths.js +1 -1
  119. package/dist/utilities/collectDisabledFieldPaths.js.map +1 -1
  120. package/dist/utilities/flattenObject.d.ts +8 -6
  121. package/dist/utilities/flattenObject.d.ts.map +1 -1
  122. package/dist/utilities/flattenObject.js +85 -75
  123. package/dist/utilities/flattenObject.js.map +1 -1
  124. package/dist/utilities/flattenedFields.d.ts +21 -0
  125. package/dist/utilities/flattenedFields.d.ts.map +1 -0
  126. package/dist/utilities/flattenedFields.js +37 -0
  127. package/dist/utilities/flattenedFields.js.map +1 -0
  128. package/dist/utilities/getExportFieldFunctions.d.ts +5 -5
  129. package/dist/utilities/getExportFieldFunctions.d.ts.map +1 -1
  130. package/dist/utilities/getExportFieldFunctions.js +122 -99
  131. package/dist/utilities/getExportFieldFunctions.js.map +1 -1
  132. package/dist/utilities/getFileFromDoc.js +3 -3
  133. package/dist/utilities/getFileFromDoc.js.map +1 -1
  134. package/dist/utilities/getImportFieldFunctions.d.ts +5 -5
  135. package/dist/utilities/getImportFieldFunctions.d.ts.map +1 -1
  136. package/dist/utilities/getImportFieldFunctions.js +95 -103
  137. package/dist/utilities/getImportFieldFunctions.js.map +1 -1
  138. package/dist/utilities/getSchemaColumns.d.ts +1 -1
  139. package/dist/utilities/getSchemaColumns.js +2 -2
  140. package/dist/utilities/getSchemaColumns.js.map +1 -1
  141. package/dist/utilities/getSelect.d.ts.map +1 -1
  142. package/dist/utilities/getSelect.js +11 -3
  143. package/dist/utilities/getSelect.js.map +1 -1
  144. package/dist/utilities/getSubmittedFormValues.d.ts +12 -0
  145. package/dist/utilities/getSubmittedFormValues.d.ts.map +1 -0
  146. package/dist/utilities/getSubmittedFormValues.js +24 -0
  147. package/dist/utilities/getSubmittedFormValues.js.map +1 -0
  148. package/dist/utilities/isPlainObject.d.ts +2 -0
  149. package/dist/utilities/isPlainObject.d.ts.map +1 -0
  150. package/dist/utilities/isPlainObject.js +3 -0
  151. package/dist/utilities/isPlainObject.js.map +1 -0
  152. package/dist/utilities/polymorphicRel.d.ts +14 -0
  153. package/dist/utilities/polymorphicRel.d.ts.map +1 -0
  154. package/dist/utilities/polymorphicRel.js +17 -0
  155. package/dist/utilities/polymorphicRel.js.map +1 -0
  156. package/dist/utilities/processRichTextField.d.ts.map +1 -1
  157. package/dist/utilities/processRichTextField.js +7 -0
  158. package/dist/utilities/processRichTextField.js.map +1 -1
  159. package/dist/utilities/removeDisabledFields.js.map +1 -1
  160. package/dist/utilities/setNestedValue.d.ts +2 -1
  161. package/dist/utilities/setNestedValue.d.ts.map +1 -1
  162. package/dist/utilities/setNestedValue.js +65 -32
  163. package/dist/utilities/setNestedValue.js.map +1 -1
  164. package/dist/utilities/sortHelpers.d.ts +1 -1
  165. package/dist/utilities/unflattenObject.d.ts +4 -3
  166. package/dist/utilities/unflattenObject.d.ts.map +1 -1
  167. package/dist/utilities/unflattenObject.js +69 -170
  168. package/dist/utilities/unflattenObject.js.map +1 -1
  169. package/dist/utilities/unflattenPostProcess.d.ts +11 -0
  170. package/dist/utilities/unflattenPostProcess.d.ts.map +1 -0
  171. package/dist/utilities/unflattenPostProcess.js +174 -0
  172. package/dist/utilities/unflattenPostProcess.js.map +1 -0
  173. package/license.md +1 -1
  174. package/package.json +9 -9
  175. package/dist/components/ExportPreview/index.scss +0 -48
  176. package/dist/components/FieldsToExport/index.scss +0 -0
  177. package/dist/components/FieldsToExport/reduceFields.spec.js +0 -239
  178. package/dist/components/FieldsToExport/reduceFields.spec.js.map +0 -1
  179. package/dist/components/ImportPreview/index.scss +0 -41
  180. package/dist/components/SortBy/index.scss +0 -3
  181. package/dist/components/SortOrder/index.scss +0 -3
  182. package/dist/utilities/buildDisabledFieldRegex.spec.js +0 -64
  183. package/dist/utilities/buildDisabledFieldRegex.spec.js.map +0 -1
  184. package/dist/utilities/fieldToRegex.spec.js +0 -151
  185. package/dist/utilities/fieldToRegex.spec.js.map +0 -1
  186. package/dist/utilities/filterToSelectedFields.spec.js +0 -204
  187. package/dist/utilities/filterToSelectedFields.spec.js.map +0 -1
  188. package/dist/utilities/getSchemaColumns.spec.js +0 -157
  189. package/dist/utilities/getSchemaColumns.spec.js.map +0 -1
  190. package/dist/utilities/parseCSV.spec.js +0 -195
  191. package/dist/utilities/parseCSV.spec.js.map +0 -1
  192. package/dist/utilities/unflattenObject.spec.js +0 -680
  193. package/dist/utilities/unflattenObject.spec.js.map +0 -1
  194. /package/dist/components/Page/{index.scss → index.css} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"flattenObject.d.ts","sourceRoot":"","sources":["../../src/utilities/flattenObject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAIhD,KAAK,IAAI,GAAG;IACV,GAAG,EAAE,QAAQ,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CAC9C,CAAA;AAED,eAAO,MAAM,aAAa,6CAKvB,IAAI,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAoK/B,CAAA"}
1
+ {"version":3,"file":"flattenObject.d.ts","sourceRoot":"","sources":["../../src/utilities/flattenObject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAMvD,KAAK,IAAI,GAAG;IACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;IACtD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,CAAA;IACtC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,cAAc,CAAA;CACpB,CAAA;AAED,eAAO,MAAM,aAAa,2DAOvB,IAAI,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CA2K/B,CAAA"}
@@ -1,119 +1,129 @@
1
1
  import { fieldToRegex } from './fieldToRegex.js';
2
- export const flattenObject = ({ doc, fields, prefix, toCSVFunctions })=>{
2
+ import { isPlainObject } from './isPlainObject.js';
3
+ import { getPolymorphicRelId, isPolymorphicRelValue } from './polymorphicRel.js';
4
+ export const flattenObject = ({ data, exportFieldHooks, fields, format, path, req })=>{
3
5
  const row = {};
4
- // Helper to get toCSV function by full path or base field name
5
- // This allows functions registered for field names (e.g., 'richText') to work
6
- // even when the field is nested in arrays/blocks (e.g., 'blocks_0_content_richText')
7
- const getToCSVFunction = (fullPath, baseFieldName)=>{
8
- return toCSVFunctions?.[fullPath] ?? toCSVFunctions?.[baseFieldName];
6
+ const invokeHook = (entry, columnName, value, siblingSource)=>{
7
+ return entry.fn({
8
+ columnName,
9
+ data,
10
+ format,
11
+ siblingData: row,
12
+ siblingDoc: siblingSource,
13
+ value
14
+ });
9
15
  };
10
- // When fields are selected, build a set of top-level document keys to process.
11
- // This prevents sibling fields with similar prefixes from being included
12
- // (e.g. selecting 'dateWithTimezone' won't pull in 'dateWithTimezone_tz')
13
16
  const selectedTopLevelKeys = Array.isArray(fields) && fields.length > 0 ? new Set(fields.map((f)=>f.split('.')[0])) : undefined;
14
- const flattenWithFilter = (siblingDoc, currentPrefix)=>{
15
- Object.entries(siblingDoc).forEach(([key, value])=>{
16
- // At the document root, skip keys that don't match any selected field
17
- if (!currentPrefix && selectedTopLevelKeys && !selectedTopLevelKeys.has(key)) {
17
+ const flattenWithFilter = (siblingSource, currentPath, currentSchemaPath)=>{
18
+ Object.entries(siblingSource).forEach(([key, value])=>{
19
+ if (!currentPath && selectedTopLevelKeys && !selectedTopLevelKeys.has(key)) {
18
20
  return;
19
21
  }
20
- const newKey = currentPrefix ? `${currentPrefix}_${key}` : key;
21
- const toCSVFn = getToCSVFunction(newKey, key);
22
+ const fieldPath = currentPath ? `${currentPath}_${key}` : key;
23
+ const fieldSchemaPath = currentSchemaPath ? `${currentSchemaPath}_${key}` : key;
24
+ const hookEntry = exportFieldHooks?.[fieldSchemaPath];
25
+ const flattenArray = (items, arrayPath, arraySchemaPath)=>{
26
+ items.forEach((item, index)=>{
27
+ if (!isPlainObject(item)) {
28
+ row[`${arrayPath}_${index}`] = item;
29
+ return;
30
+ }
31
+ const blockType = typeof item.blockType === 'string' ? item.blockType : undefined;
32
+ const itemPath = blockType ? `${arrayPath}_${index}_${blockType}` : `${arrayPath}_${index}`;
33
+ const itemSchemaPath = blockType ? `${arraySchemaPath}_${blockType}` : arraySchemaPath;
34
+ if (isPolymorphicRelValue(item) && isPlainObject(item.value)) {
35
+ const id = getPolymorphicRelId(item);
36
+ if (id !== undefined) {
37
+ row[`${itemPath}_relationTo`] = item.relationTo;
38
+ row[`${itemPath}_id`] = id;
39
+ return;
40
+ }
41
+ }
42
+ flattenWithFilter(item, itemPath, itemSchemaPath);
43
+ });
44
+ };
22
45
  if (Array.isArray(value)) {
23
- if (toCSVFn) {
46
+ if (hookEntry) {
24
47
  try {
25
- const result = toCSVFn({
26
- columnName: newKey,
27
- data: row,
28
- doc,
29
- row,
30
- siblingDoc,
31
- value
32
- });
33
- if (typeof result !== 'undefined') {
34
- row[newKey] = result;
48
+ const result = invokeHook(hookEntry, fieldPath, value, siblingSource);
49
+ if (result === null) {
35
50
  return;
36
51
  }
37
- for(const k in row){
38
- if (k === newKey || k.startsWith(`${newKey}_`)) {
39
- return;
40
- }
52
+ if (Array.isArray(result)) {
53
+ flattenArray(result, fieldPath, fieldSchemaPath);
54
+ return;
41
55
  }
42
- } catch (error) {
43
- throw new Error(`Error in toCSVFunction for array "${newKey}": ${JSON.stringify(value)}\n${error.message}`);
44
- }
45
- }
46
- value.forEach((item, index)=>{
47
- if (typeof item === 'object' && item !== null) {
48
- const blockType = typeof item.blockType === 'string' ? item.blockType : undefined;
49
- const itemPrefix = blockType ? `${newKey}_${index}_${blockType}` : `${newKey}_${index}`;
50
- if ('relationTo' in item && 'value' in item && typeof item.value === 'object' && item.value !== null) {
51
- row[`${itemPrefix}_relationTo`] = item.relationTo;
52
- row[`${itemPrefix}_id`] = item.value.id;
56
+ if (typeof result !== 'undefined') {
57
+ row[fieldPath] = result;
53
58
  return;
54
59
  }
55
- flattenWithFilter(item, itemPrefix);
56
- } else {
57
- row[`${newKey}_${index}`] = item;
60
+ } catch (error) {
61
+ req.payload.logger.error({
62
+ err: error,
63
+ msg: `[plugin-import-export] Field-level beforeExport hook for "${fieldPath}" threw — falling back to default flattening`
64
+ });
58
65
  }
59
- });
66
+ }
67
+ flattenArray(value, fieldPath, fieldSchemaPath);
60
68
  } else if (typeof value === 'object' && value !== null) {
61
- if (!toCSVFn) {
62
- flattenWithFilter(value, newKey);
69
+ if (!hookEntry) {
70
+ flattenWithFilter(value, fieldPath, fieldSchemaPath);
63
71
  } else {
72
+ const keysBeforeHook = new Set(Object.keys(row));
64
73
  try {
65
- const result = toCSVFn({
66
- columnName: newKey,
67
- data: row,
68
- doc,
69
- row,
70
- siblingDoc,
71
- value
72
- });
73
- if (typeof result !== 'undefined') {
74
- row[newKey] = result;
74
+ const result = invokeHook(hookEntry, fieldPath, value, siblingSource);
75
+ if (result === null) {
76
+ return;
77
+ }
78
+ if (typeof result === 'undefined') {
79
+ const hookWroteForField = Object.keys(row).some((k)=>!keysBeforeHook.has(k) && (k === fieldPath || k.startsWith(`${fieldPath}_`)));
80
+ if (hookWroteForField) {
81
+ return;
82
+ }
83
+ flattenWithFilter(value, fieldPath, fieldSchemaPath);
84
+ } else if (typeof result === 'object' && !Array.isArray(result)) {
85
+ flattenWithFilter(result, fieldPath, fieldSchemaPath);
86
+ } else {
87
+ row[fieldPath] = result;
75
88
  }
76
89
  } catch (error) {
77
- throw new Error(`Error in toCSVFunction for nested object "${newKey}": ${JSON.stringify(value)}\n${error.message}`);
90
+ req.payload.logger.error({
91
+ err: error,
92
+ msg: `[plugin-import-export] Field-level beforeExport hook for "${fieldPath}" threw — falling back to default flattening`
93
+ });
94
+ flattenWithFilter(value, fieldPath, fieldSchemaPath);
78
95
  }
79
96
  }
80
97
  } else {
81
- if (toCSVFn) {
98
+ if (hookEntry) {
82
99
  try {
83
- const result = toCSVFn({
84
- columnName: newKey,
85
- data: row,
86
- doc,
87
- row,
88
- siblingDoc,
89
- value
90
- });
100
+ const result = invokeHook(hookEntry, fieldPath, value, siblingSource);
91
101
  if (typeof result !== 'undefined') {
92
- row[newKey] = result;
102
+ row[fieldPath] = result;
93
103
  }
94
104
  } catch (error) {
95
- throw new Error(`Error in toCSVFunction for field "${newKey}": ${JSON.stringify(value)}\n${error.message}`);
105
+ req.payload.logger.error({
106
+ err: error,
107
+ msg: `[plugin-import-export] Field-level beforeExport hook for "${fieldPath}" threw — falling back to original value`
108
+ });
109
+ row[fieldPath] = value;
96
110
  }
97
111
  } else {
98
- row[newKey] = value;
112
+ row[fieldPath] = value;
99
113
  }
100
114
  }
101
115
  });
102
116
  };
103
- flattenWithFilter(doc, prefix);
117
+ flattenWithFilter(data, path, path);
104
118
  if (Array.isArray(fields) && fields.length > 0) {
105
119
  const orderedResult = {};
106
- // Build all field regexes once
107
120
  const fieldPatterns = fields.map((field)=>({
108
121
  field,
109
122
  regex: fieldToRegex(field)
110
123
  }));
111
- // Single pass through row keys - O(keys * fields) regex tests but only one iteration
112
124
  const rowKeys = Object.keys(row);
113
- // Process in field order to maintain user's specified ordering
114
125
  for (const { regex } of fieldPatterns){
115
126
  for (const key of rowKeys){
116
- // Skip if already added (a key might match multiple field patterns)
117
127
  if (key in orderedResult) {
118
128
  continue;
119
129
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/flattenObject.ts"],"sourcesContent":["import type { Document } from 'payload'\n\nimport type { ToCSVFunction } from '../types.js'\n\nimport { fieldToRegex } from './fieldToRegex.js'\n\ntype Args = {\n doc: Document\n fields?: string[]\n prefix?: string\n toCSVFunctions: Record<string, ToCSVFunction>\n}\n\nexport const flattenObject = ({\n doc,\n fields,\n prefix,\n toCSVFunctions,\n}: Args): Record<string, unknown> => {\n const row: Record<string, unknown> = {}\n\n // Helper to get toCSV function by full path or base field name\n // This allows functions registered for field names (e.g., 'richText') to work\n // even when the field is nested in arrays/blocks (e.g., 'blocks_0_content_richText')\n const getToCSVFunction = (fullPath: string, baseFieldName: string): ToCSVFunction | undefined => {\n return toCSVFunctions?.[fullPath] ?? toCSVFunctions?.[baseFieldName]\n }\n\n // When fields are selected, build a set of top-level document keys to process.\n // This prevents sibling fields with similar prefixes from being included\n // (e.g. selecting 'dateWithTimezone' won't pull in 'dateWithTimezone_tz')\n const selectedTopLevelKeys =\n Array.isArray(fields) && fields.length > 0\n ? new Set(fields.map((f) => f.split('.')[0]))\n : undefined\n\n const flattenWithFilter = (siblingDoc: Document, currentPrefix?: string) => {\n Object.entries(siblingDoc).forEach(([key, value]) => {\n // At the document root, skip keys that don't match any selected field\n if (!currentPrefix && selectedTopLevelKeys && !selectedTopLevelKeys.has(key)) {\n return\n }\n\n const newKey = currentPrefix ? `${currentPrefix}_${key}` : key\n const toCSVFn = getToCSVFunction(newKey, key)\n\n if (Array.isArray(value)) {\n if (toCSVFn) {\n try {\n const result = toCSVFn({\n columnName: newKey,\n data: row,\n doc,\n row,\n siblingDoc,\n value,\n })\n\n if (typeof result !== 'undefined') {\n row[newKey] = result\n return\n }\n\n for (const k in row) {\n if (k === newKey || k.startsWith(`${newKey}_`)) {\n return\n }\n }\n } catch (error) {\n throw new Error(\n `Error in toCSVFunction for array \"${newKey}\": ${JSON.stringify(value)}\\n${\n (error as Error).message\n }`,\n )\n }\n }\n\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n const blockType = typeof item.blockType === 'string' ? item.blockType : undefined\n const itemPrefix = blockType ? `${newKey}_${index}_${blockType}` : `${newKey}_${index}`\n\n if (\n 'relationTo' in item &&\n 'value' in item &&\n typeof item.value === 'object' &&\n item.value !== null\n ) {\n row[`${itemPrefix}_relationTo`] = item.relationTo\n row[`${itemPrefix}_id`] = item.value.id\n return\n }\n\n flattenWithFilter(item, itemPrefix)\n } else {\n row[`${newKey}_${index}`] = item\n }\n })\n } else if (typeof value === 'object' && value !== null) {\n if (!toCSVFn) {\n flattenWithFilter(value, newKey)\n } else {\n try {\n const result = toCSVFn({\n columnName: newKey,\n data: row,\n doc,\n row,\n siblingDoc,\n value,\n })\n if (typeof result !== 'undefined') {\n row[newKey] = result\n }\n } catch (error) {\n throw new Error(\n `Error in toCSVFunction for nested object \"${newKey}\": ${JSON.stringify(value)}\\n${\n (error as Error).message\n }`,\n )\n }\n }\n } else {\n if (toCSVFn) {\n try {\n const result = toCSVFn({\n columnName: newKey,\n data: row,\n doc,\n row,\n siblingDoc,\n value,\n })\n if (typeof result !== 'undefined') {\n row[newKey] = result\n }\n } catch (error) {\n throw new Error(\n `Error in toCSVFunction for field \"${newKey}\": ${JSON.stringify(value)}\\n${\n (error as Error).message\n }`,\n )\n }\n } else {\n row[newKey] = value\n }\n }\n })\n }\n\n flattenWithFilter(doc, prefix)\n\n if (Array.isArray(fields) && fields.length > 0) {\n const orderedResult: Record<string, unknown> = {}\n\n // Build all field regexes once\n const fieldPatterns = fields.map((field) => ({\n field,\n regex: fieldToRegex(field),\n }))\n\n // Single pass through row keys - O(keys * fields) regex tests but only one iteration\n const rowKeys = Object.keys(row)\n\n // Process in field order to maintain user's specified ordering\n for (const { regex } of fieldPatterns) {\n for (const key of rowKeys) {\n // Skip if already added (a key might match multiple field patterns)\n if (key in orderedResult) {\n continue\n }\n\n if (regex.test(key)) {\n orderedResult[key] = row[key]\n }\n }\n }\n\n return orderedResult\n }\n\n return row\n}\n"],"names":["fieldToRegex","flattenObject","doc","fields","prefix","toCSVFunctions","row","getToCSVFunction","fullPath","baseFieldName","selectedTopLevelKeys","Array","isArray","length","Set","map","f","split","undefined","flattenWithFilter","siblingDoc","currentPrefix","Object","entries","forEach","key","value","has","newKey","toCSVFn","result","columnName","data","k","startsWith","error","Error","JSON","stringify","message","item","index","blockType","itemPrefix","relationTo","id","orderedResult","fieldPatterns","field","regex","rowKeys","keys","test"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/utilities/flattenObject.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport type { ExportFieldHookEntry } from '../types.js'\n\nimport { fieldToRegex } from './fieldToRegex.js'\nimport { isPlainObject } from './isPlainObject.js'\nimport { getPolymorphicRelId, isPolymorphicRelValue } from './polymorphicRel.js'\n\ntype Args = {\n data: Record<string, unknown>\n exportFieldHooks: Record<string, ExportFieldHookEntry>\n fields?: string[]\n format: 'csv' | 'json' | ({} & string)\n path?: string\n req: PayloadRequest\n}\n\nexport const flattenObject = ({\n data,\n exportFieldHooks,\n fields,\n format,\n path,\n req,\n}: Args): Record<string, unknown> => {\n const row: Record<string, unknown> = {}\n\n const invokeHook = (\n entry: ExportFieldHookEntry,\n columnName: string,\n value: unknown,\n siblingSource: Record<string, unknown>,\n ): unknown => {\n return entry.fn({\n columnName,\n data,\n format,\n siblingData: row,\n siblingDoc: siblingSource,\n value,\n })\n }\n\n const selectedTopLevelKeys =\n Array.isArray(fields) && fields.length > 0\n ? new Set(fields.map((f) => f.split('.')[0]))\n : undefined\n\n const flattenWithFilter = (\n siblingSource: Record<string, unknown>,\n currentPath: string | undefined,\n currentSchemaPath: string | undefined,\n ) => {\n Object.entries(siblingSource).forEach(([key, value]) => {\n if (!currentPath && selectedTopLevelKeys && !selectedTopLevelKeys.has(key)) {\n return\n }\n\n const fieldPath = currentPath ? `${currentPath}_${key}` : key\n const fieldSchemaPath = currentSchemaPath ? `${currentSchemaPath}_${key}` : key\n const hookEntry = exportFieldHooks?.[fieldSchemaPath]\n\n const flattenArray = (items: unknown[], arrayPath: string, arraySchemaPath: string): void => {\n items.forEach((item, index) => {\n if (!isPlainObject(item)) {\n row[`${arrayPath}_${index}`] = item\n return\n }\n\n const blockType = typeof item.blockType === 'string' ? item.blockType : undefined\n const itemPath = blockType\n ? `${arrayPath}_${index}_${blockType}`\n : `${arrayPath}_${index}`\n const itemSchemaPath = blockType ? `${arraySchemaPath}_${blockType}` : arraySchemaPath\n\n if (isPolymorphicRelValue(item) && isPlainObject(item.value)) {\n const id = getPolymorphicRelId(item)\n if (id !== undefined) {\n row[`${itemPath}_relationTo`] = item.relationTo\n row[`${itemPath}_id`] = id\n return\n }\n }\n\n flattenWithFilter(item, itemPath, itemSchemaPath)\n })\n }\n\n if (Array.isArray(value)) {\n if (hookEntry) {\n try {\n const result = invokeHook(hookEntry, fieldPath, value, siblingSource)\n\n if (result === null) {\n return\n }\n\n if (Array.isArray(result)) {\n flattenArray(result, fieldPath, fieldSchemaPath)\n return\n }\n\n if (typeof result !== 'undefined') {\n row[fieldPath] = result\n return\n }\n } catch (error) {\n req.payload.logger.error({\n err: error,\n msg: `[plugin-import-export] Field-level beforeExport hook for \"${fieldPath}\" threw — falling back to default flattening`,\n })\n }\n }\n\n flattenArray(value, fieldPath, fieldSchemaPath)\n } else if (typeof value === 'object' && value !== null) {\n if (!hookEntry) {\n flattenWithFilter(value as Record<string, unknown>, fieldPath, fieldSchemaPath)\n } else {\n const keysBeforeHook = new Set(Object.keys(row))\n try {\n const result = invokeHook(hookEntry, fieldPath, value, siblingSource)\n if (result === null) {\n return\n }\n if (typeof result === 'undefined') {\n const hookWroteForField = Object.keys(row).some(\n (k) => !keysBeforeHook.has(k) && (k === fieldPath || k.startsWith(`${fieldPath}_`)),\n )\n if (hookWroteForField) {\n return\n }\n flattenWithFilter(value as Record<string, unknown>, fieldPath, fieldSchemaPath)\n } else if (typeof result === 'object' && !Array.isArray(result)) {\n flattenWithFilter(result as Record<string, unknown>, fieldPath, fieldSchemaPath)\n } else {\n row[fieldPath] = result\n }\n } catch (error) {\n req.payload.logger.error({\n err: error,\n msg: `[plugin-import-export] Field-level beforeExport hook for \"${fieldPath}\" threw — falling back to default flattening`,\n })\n flattenWithFilter(value as Record<string, unknown>, fieldPath, fieldSchemaPath)\n }\n }\n } else {\n if (hookEntry) {\n try {\n const result = invokeHook(hookEntry, fieldPath, value, siblingSource)\n if (typeof result !== 'undefined') {\n row[fieldPath] = result\n }\n } catch (error) {\n req.payload.logger.error({\n err: error,\n msg: `[plugin-import-export] Field-level beforeExport hook for \"${fieldPath}\" threw — falling back to original value`,\n })\n row[fieldPath] = value\n }\n } else {\n row[fieldPath] = value\n }\n }\n })\n }\n\n flattenWithFilter(data, path, path)\n\n if (Array.isArray(fields) && fields.length > 0) {\n const orderedResult: Record<string, unknown> = {}\n\n const fieldPatterns = fields.map((field) => ({\n field,\n regex: fieldToRegex(field),\n }))\n\n const rowKeys = Object.keys(row)\n\n for (const { regex } of fieldPatterns) {\n for (const key of rowKeys) {\n if (key in orderedResult) {\n continue\n }\n\n if (regex.test(key)) {\n orderedResult[key] = row[key]\n }\n }\n }\n\n return orderedResult\n }\n\n return row\n}\n"],"names":["fieldToRegex","isPlainObject","getPolymorphicRelId","isPolymorphicRelValue","flattenObject","data","exportFieldHooks","fields","format","path","req","row","invokeHook","entry","columnName","value","siblingSource","fn","siblingData","siblingDoc","selectedTopLevelKeys","Array","isArray","length","Set","map","f","split","undefined","flattenWithFilter","currentPath","currentSchemaPath","Object","entries","forEach","key","has","fieldPath","fieldSchemaPath","hookEntry","flattenArray","items","arrayPath","arraySchemaPath","item","index","blockType","itemPath","itemSchemaPath","id","relationTo","result","error","payload","logger","err","msg","keysBeforeHook","keys","hookWroteForField","some","k","startsWith","orderedResult","fieldPatterns","field","regex","rowKeys","test"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { FlattenedField } from 'payload';
2
+ /**
3
+ * Returns the `flattenedFields` of a group/tab/array field, or undefined for
4
+ * field types that don't carry nested fields. Concentrates the cast for the
5
+ * Payload core typing oversight (`FlattenedField` doesn't expose
6
+ * `flattenedFields` on every variant).
7
+ */
8
+ export declare const getNestedFlattenedFields: (field: FlattenedField) => FlattenedField[] | undefined;
9
+ /**
10
+ * Returns the `flattenedFields` of a `BlocksField` block (always an array).
11
+ */
12
+ export declare const getBlockFlattenedFields: (block: {
13
+ flattenedFields?: FlattenedField[];
14
+ }) => FlattenedField[];
15
+ /**
16
+ * Traverses a flattened field schema and calls `registerHandler` for each
17
+ * named field. Handles blocks (keyed by block slug), groups, tabs, and arrays.
18
+ * Shared by `getExportFieldFunctions` and `getImportFieldFunctions`.
19
+ */
20
+ export declare const registerFieldHooks: <TEntry>(fields: FlattenedField[], parentPath: string, result: Record<string, TEntry>, registerHandler: (field: FlattenedField, fullKey: string, result: Record<string, TEntry>) => void) => void;
21
+ //# sourceMappingURL=flattenedFields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flattenedFields.d.ts","sourceRoot":"","sources":["../../src/utilities/flattenedFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,UAAW,cAAc,KAAG,cAAc,EAAE,GAAG,SACjB,CAAA;AAEnE;;GAEG;AACH,eAAO,MAAM,uBAAuB,UAAW;IAC7C,eAAe,CAAC,EAAE,cAAc,EAAE,CAAA;CACnC,KAAG,cAAc,EAAiC,CAAA;AAEnD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,UAC/B,cAAc,EAAE,cACZ,MAAM,UACV,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,mBACb,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,KAChG,IA6BF,CAAA"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Returns the `flattenedFields` of a group/tab/array field, or undefined for
3
+ * field types that don't carry nested fields. Concentrates the cast for the
4
+ * Payload core typing oversight (`FlattenedField` doesn't expose
5
+ * `flattenedFields` on every variant).
6
+ */ export const getNestedFlattenedFields = (field)=>field.flattenedFields;
7
+ /**
8
+ * Returns the `flattenedFields` of a `BlocksField` block (always an array).
9
+ */ export const getBlockFlattenedFields = (block)=>block.flattenedFields ?? [];
10
+ /**
11
+ * Traverses a flattened field schema and calls `registerHandler` for each
12
+ * named field. Handles blocks (keyed by block slug), groups, tabs, and arrays.
13
+ * Shared by `getExportFieldFunctions` and `getImportFieldFunctions`.
14
+ */ export const registerFieldHooks = (fields, parentPath, result, registerHandler)=>{
15
+ for (const field of fields){
16
+ if (!('name' in field) || !field.name) {
17
+ continue;
18
+ }
19
+ if (field.type === 'blocks') {
20
+ const base = parentPath ? `${parentPath}_${field.name}` : field.name;
21
+ for (const block of field.blocks ?? []){
22
+ if (typeof block === 'string') {
23
+ continue;
24
+ }
25
+ registerFieldHooks(getBlockFlattenedFields(block), `${base}_${block.slug}`, result, registerHandler);
26
+ }
27
+ continue;
28
+ }
29
+ const fullKey = parentPath ? `${parentPath}_${field.name}` : field.name;
30
+ registerHandler(field, fullKey, result);
31
+ if (field.type === 'group' || field.type === 'tab' || field.type === 'array') {
32
+ registerFieldHooks(getNestedFlattenedFields(field) ?? [], fullKey, result, registerHandler);
33
+ }
34
+ }
35
+ };
36
+
37
+ //# sourceMappingURL=flattenedFields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/flattenedFields.ts"],"sourcesContent":["import type { FlattenedField } from 'payload'\n\n/**\n * Returns the `flattenedFields` of a group/tab/array field, or undefined for\n * field types that don't carry nested fields. Concentrates the cast for the\n * Payload core typing oversight (`FlattenedField` doesn't expose\n * `flattenedFields` on every variant).\n */\nexport const getNestedFlattenedFields = (field: FlattenedField): FlattenedField[] | undefined =>\n (field as { flattenedFields?: FlattenedField[] }).flattenedFields\n\n/**\n * Returns the `flattenedFields` of a `BlocksField` block (always an array).\n */\nexport const getBlockFlattenedFields = (block: {\n flattenedFields?: FlattenedField[]\n}): FlattenedField[] => block.flattenedFields ?? []\n\n/**\n * Traverses a flattened field schema and calls `registerHandler` for each\n * named field. Handles blocks (keyed by block slug), groups, tabs, and arrays.\n * Shared by `getExportFieldFunctions` and `getImportFieldFunctions`.\n */\nexport const registerFieldHooks = <TEntry>(\n fields: FlattenedField[],\n parentPath: string,\n result: Record<string, TEntry>,\n registerHandler: (field: FlattenedField, fullKey: string, result: Record<string, TEntry>) => void,\n): void => {\n for (const field of fields) {\n if (!('name' in field) || !field.name) {\n continue\n }\n\n if (field.type === 'blocks') {\n const base = parentPath ? `${parentPath}_${field.name}` : field.name\n for (const block of field.blocks ?? []) {\n if (typeof block === 'string') {\n continue\n }\n registerFieldHooks(\n getBlockFlattenedFields(block),\n `${base}_${block.slug}`,\n result,\n registerHandler,\n )\n }\n continue\n }\n\n const fullKey = parentPath ? `${parentPath}_${field.name}` : field.name\n registerHandler(field, fullKey, result)\n\n if (field.type === 'group' || field.type === 'tab' || field.type === 'array') {\n registerFieldHooks(getNestedFlattenedFields(field) ?? [], fullKey, result, registerHandler)\n }\n }\n}\n"],"names":["getNestedFlattenedFields","field","flattenedFields","getBlockFlattenedFields","block","registerFieldHooks","fields","parentPath","result","registerHandler","name","type","base","blocks","slug","fullKey"],"mappings":"AAEA;;;;;CAKC,GACD,OAAO,MAAMA,2BAA2B,CAACC,QACvC,AAACA,MAAiDC,eAAe,CAAA;AAEnE;;CAEC,GACD,OAAO,MAAMC,0BAA0B,CAACC,QAEhBA,MAAMF,eAAe,IAAI,EAAE,CAAA;AAEnD;;;;CAIC,GACD,OAAO,MAAMG,qBAAqB,CAChCC,QACAC,YACAC,QACAC;IAEA,KAAK,MAAMR,SAASK,OAAQ;QAC1B,IAAI,CAAE,CAAA,UAAUL,KAAI,KAAM,CAACA,MAAMS,IAAI,EAAE;YACrC;QACF;QAEA,IAAIT,MAAMU,IAAI,KAAK,UAAU;YAC3B,MAAMC,OAAOL,aAAa,GAAGA,WAAW,CAAC,EAAEN,MAAMS,IAAI,EAAE,GAAGT,MAAMS,IAAI;YACpE,KAAK,MAAMN,SAASH,MAAMY,MAAM,IAAI,EAAE,CAAE;gBACtC,IAAI,OAAOT,UAAU,UAAU;oBAC7B;gBACF;gBACAC,mBACEF,wBAAwBC,QACxB,GAAGQ,KAAK,CAAC,EAAER,MAAMU,IAAI,EAAE,EACvBN,QACAC;YAEJ;YACA;QACF;QAEA,MAAMM,UAAUR,aAAa,GAAGA,WAAW,CAAC,EAAEN,MAAMS,IAAI,EAAE,GAAGT,MAAMS,IAAI;QACvED,gBAAgBR,OAAOc,SAASP;QAEhC,IAAIP,MAAMU,IAAI,KAAK,WAAWV,MAAMU,IAAI,KAAK,SAASV,MAAMU,IAAI,KAAK,SAAS;YAC5EN,mBAAmBL,yBAAyBC,UAAU,EAAE,EAAEc,SAASP,QAAQC;QAC7E;IACF;AACF,EAAC"}
@@ -1,12 +1,12 @@
1
- import { type FlattenedField } from 'payload';
2
- import type { ToCSVFunction } from '../types.js';
1
+ import type { FlattenedField } from 'payload';
2
+ import type { ExportFieldHookEntry } from '../types.js';
3
3
  type Args = {
4
4
  fields: FlattenedField[];
5
5
  };
6
6
  /**
7
- * Gets custom toCSV field functions for export.
8
- * These functions transform field values when flattening documents for CSV export.
7
+ * Builds a map from logical field path (e.g. `content_textBlock_body`) to
8
+ * the export hook entry. Paths include block slugs but never array indices.
9
9
  */
10
- export declare const getExportFieldFunctions: ({ fields }: Args) => Record<string, ToCSVFunction>;
10
+ export declare const getExportFieldFunctions: ({ fields }: Args) => Record<string, ExportFieldHookEntry>;
11
11
  export {};
12
12
  //# sourceMappingURL=getExportFieldFunctions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getExportFieldFunctions.d.ts","sourceRoot":"","sources":["../../src/utilities/getExportFieldFunctions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAA+C,MAAM,SAAS,CAAA;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,cAAc,EAAE,CAAA;CACzB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,eAAgB,IAAI,KAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAqHtF,CAAA"}
1
+ {"version":3,"file":"getExportFieldFunctions.d.ts","sourceRoot":"","sources":["../../src/utilities/getExportFieldFunctions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,KAAK,EAAE,oBAAoB,EAAyB,MAAM,aAAa,CAAA;AAK9E,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,cAAc,EAAE,CAAA;CACzB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,eAAgB,IAAI,KAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAI7F,CAAA"}
@@ -1,109 +1,132 @@
1
- import { traverseFields } from 'payload';
1
+ import { registerFieldHooks } from './flattenedFields.js';
2
+ import { getPolymorphicRelId, isPolymorphicRelValue } from './polymorphicRel.js';
2
3
  /**
3
- * Gets custom toCSV field functions for export.
4
- * These functions transform field values when flattening documents for CSV export.
4
+ * Builds a map from logical field path (e.g. `content_textBlock_body`) to
5
+ * the export hook entry. Paths include block slugs but never array indices.
5
6
  */ export const getExportFieldFunctions = ({ fields })=>{
6
7
  const result = {};
7
- const buildCustomFunctions = ({ field, parentRef, ref })=>{
8
- // @ts-expect-error ref is untyped
9
- ref.prefix = parentRef.prefix || '';
10
- if (field.type === 'group' || field.type === 'tab') {
11
- // @ts-expect-error ref is untyped
12
- const parentPrefix = parentRef?.prefix ? `${parentRef.prefix}_` : '';
13
- // @ts-expect-error ref is untyped
14
- ref.prefix = `${parentPrefix}${field.name}_`;
15
- }
16
- if (typeof field.custom?.['plugin-import-export']?.toCSV === 'function') {
17
- // @ts-expect-error ref is untyped
18
- result[`${ref.prefix}${field.name}`] = field.custom['plugin-import-export']?.toCSV;
19
- } else if (field.type === 'json' || field.type === 'richText') {
20
- // Serialize JSON and richText fields as JSON strings in a single column
21
- // This prevents them from being flattened into multiple columns
22
- // @ts-expect-error ref is untyped
23
- result[`${ref.prefix}${field.name}`] = ({ value })=>{
24
- if (value === null || value === undefined) {
25
- return value;
26
- }
27
- if (typeof value === 'object') {
28
- return JSON.stringify(value);
29
- }
8
+ registerFieldHooks(fields, '', result, registerExportHandler);
9
+ return result;
10
+ };
11
+ const registerExportHandler = (field, fullKey, result)=>{
12
+ const beforeExport = field.custom?.['plugin-import-export']?.hooks?.beforeExport;
13
+ if (typeof beforeExport === 'function') {
14
+ result[fullKey] = {
15
+ type: 'beforeExport',
16
+ fn: beforeExport
17
+ };
18
+ return;
19
+ }
20
+ const registerHandler = (handler)=>{
21
+ result[fullKey] = {
22
+ type: 'beforeExport',
23
+ fn: handler
24
+ };
25
+ };
26
+ if (field.type === 'json' || field.type === 'richText') {
27
+ registerHandler(({ format, value })=>{
28
+ if (format === 'json') {
30
29
  return value;
31
- };
32
- } else if (field.type === 'date') {
33
- // Handle date fields - return value as-is (ISO string format)
34
- // This prevents the flattener from treating the document as having
35
- // nested sibling properties like _tz that shouldn't be auto-included
36
- // @ts-expect-error ref is untyped
37
- const fieldKey = `${ref.prefix}${field.name}`;
38
- result[fieldKey] = ({ value })=>value;
39
- } else if (field.type === 'relationship' || field.type === 'upload') {
40
- if (field.hasMany !== true) {
41
- if (!Array.isArray(field.relationTo)) {
42
- // monomorphic single
43
- // @ts-expect-error ref is untyped
44
- result[`${ref.prefix}${field.name}`] = ({ value })=>typeof value === 'object' && value && 'id' in value ? value.id : value;
45
- } else {
46
- // polymorphic single
47
- // @ts-expect-error ref is untyped
48
- result[`${ref.prefix}${field.name}`] = ({ data, value })=>{
49
- if (value && typeof value === 'object' && 'relationTo' in value && 'value' in value) {
50
- const relationTo = value.relationTo;
51
- const relatedDoc = value.value;
52
- if (relatedDoc && typeof relatedDoc === 'object') {
53
- // @ts-expect-error ref is untyped
54
- data[`${ref.prefix}${field.name}_id`] = relatedDoc.id;
55
- // @ts-expect-error ref is untyped
56
- data[`${ref.prefix}${field.name}_relationTo`] = relationTo;
57
- }
58
- }
59
- return undefined // prevents further flattening
60
- ;
61
- };
62
- }
63
- } else {
64
- if (!Array.isArray(field.relationTo)) {
65
- // monomorphic many
66
- // @ts-expect-error ref is untyped
67
- result[`${ref.prefix}${field.name}`] = ({ data, value })=>{
68
- if (Array.isArray(value)) {
69
- value.forEach((val, i)=>{
70
- const id = typeof val === 'object' && val ? val.id : val;
71
- // @ts-expect-error ref is untyped
72
- data[`${ref.prefix}${field.name}_${i}_id`] = id;
73
- });
74
- }
75
- return undefined // prevents further flattening
76
- ;
77
- };
78
- } else {
79
- // polymorphic many
80
- // @ts-expect-error ref is untyped
81
- result[`${ref.prefix}${field.name}`] = ({ data, value })=>{
82
- if (Array.isArray(value)) {
83
- value.forEach((val, i)=>{
84
- if (val && typeof val === 'object') {
85
- const relationTo = val.relationTo;
86
- const relatedDoc = val.value;
87
- if (relationTo && relatedDoc && typeof relatedDoc === 'object') {
88
- // @ts-expect-error ref is untyped
89
- data[`${ref.prefix}${field.name}_${i}_id`] = relatedDoc.id;
90
- // @ts-expect-error ref is untyped
91
- data[`${ref.prefix}${field.name}_${i}_relationTo`] = relationTo;
92
- }
93
- }
94
- });
95
- }
96
- return undefined;
97
- };
98
- }
99
30
  }
31
+ if (value === null || value === undefined) {
32
+ return value;
33
+ }
34
+ if (typeof value === 'object') {
35
+ return JSON.stringify(value);
36
+ }
37
+ return value;
38
+ });
39
+ return;
40
+ }
41
+ if (field.type === 'date') {
42
+ registerHandler(({ value })=>value);
43
+ return;
44
+ }
45
+ if (field.type !== 'relationship' && field.type !== 'upload') {
46
+ return;
47
+ }
48
+ if (field.hasMany !== true) {
49
+ if (!Array.isArray(field.relationTo)) {
50
+ registerHandler(({ value })=>typeof value === 'object' && value && 'id' in value ? value.id : value);
51
+ return;
100
52
  }
101
- };
102
- traverseFields({
103
- callback: buildCustomFunctions,
104
- fields
53
+ registerHandler(({ format, siblingData, value })=>{
54
+ // A shape this handler does not recognize is left untouched for JSON, which holds the
55
+ // source value verbatim. CSV still clears it, since its id/relationTo would otherwise
56
+ // land in sibling columns.
57
+ if (!isPolymorphicRelValue(value)) {
58
+ return format === 'json' ? undefined : null;
59
+ }
60
+ // A recognized reference that resolves to no id is dangling, and a dangling reference
61
+ // cannot be imported back — clear it for both formats.
62
+ const id = getPolymorphicRelId(value);
63
+ if (id === undefined) {
64
+ return null;
65
+ }
66
+ if (format === 'json') {
67
+ return {
68
+ relationTo: value.relationTo,
69
+ value: id
70
+ };
71
+ }
72
+ siblingData[`${fullKey}_id`] = id;
73
+ siblingData[`${fullKey}_relationTo`] = value.relationTo;
74
+ return null;
75
+ });
76
+ return;
77
+ }
78
+ if (!Array.isArray(field.relationTo)) {
79
+ registerHandler(({ format, siblingData, value })=>{
80
+ if (!Array.isArray(value)) {
81
+ return undefined;
82
+ }
83
+ const ids = value.map((val)=>typeof val === 'object' && val ? val.id : val);
84
+ // Bare null entries fail relationship validation when the JSON is imported, so drop
85
+ // references that could not be resolved. CSV keeps source indexes in its column names;
86
+ // unflattening those columns collapses any gaps on import.
87
+ if (format === 'json') {
88
+ return ids.filter((id)=>id !== null && id !== undefined);
89
+ }
90
+ ids.forEach((id, i)=>{
91
+ siblingData[`${fullKey}_${i}_id`] = id;
92
+ });
93
+ return null;
94
+ });
95
+ return;
96
+ }
97
+ registerHandler(({ format, siblingData, value })=>{
98
+ if (!Array.isArray(value)) {
99
+ return undefined;
100
+ }
101
+ // `index` is carried so CSV columns stay pinned to the source position: an entry
102
+ // that cannot be resolved to an id leaves a gap rather than shifting its siblings.
103
+ const rels = value.flatMap((val, index)=>{
104
+ if (!isPolymorphicRelValue(val)) {
105
+ return [];
106
+ }
107
+ const id = getPolymorphicRelId(val);
108
+ return id === undefined ? [] : [
109
+ {
110
+ id,
111
+ index,
112
+ relationTo: val.relationTo
113
+ }
114
+ ];
115
+ });
116
+ if (format === 'json') {
117
+ // Bare null entries fail relationship validation when the JSON is imported, so return
118
+ // only references that resolved to an id.
119
+ return rels.map(({ id, relationTo })=>({
120
+ relationTo,
121
+ value: id
122
+ }));
123
+ }
124
+ rels.forEach(({ id, index, relationTo })=>{
125
+ siblingData[`${fullKey}_${index}_id`] = id;
126
+ siblingData[`${fullKey}_${index}_relationTo`] = relationTo;
127
+ });
128
+ return null;
105
129
  });
106
- return result;
107
130
  };
108
131
 
109
132
  //# sourceMappingURL=getExportFieldFunctions.js.map