@evfrenkel/decap-cms-core 3.13.0-image-conversions.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/README.md +9 -0
  2. package/dist/20.decap-cms-core.js +2 -0
  3. package/dist/20.decap-cms-core.js.map +1 -0
  4. package/dist/3802306e7b58a11862fb.wasm +0 -0
  5. package/dist/@evfrenkel/20.decap-cms-core.js +2 -0
  6. package/dist/@evfrenkel/20.decap-cms-core.js.map +1 -0
  7. package/dist/@evfrenkel/decap-cms-core.js +44 -0
  8. package/dist/@evfrenkel/decap-cms-core.js.LICENSE.txt +126 -0
  9. package/dist/@evfrenkel/decap-cms-core.js.map +1 -0
  10. package/dist/decap-cms-core.js +47 -0
  11. package/dist/decap-cms-core.js.LICENSE.txt +126 -0
  12. package/dist/decap-cms-core.js.map +1 -0
  13. package/dist/esm/actions/auth.js +97 -0
  14. package/dist/esm/actions/collections.js +15 -0
  15. package/dist/esm/actions/config.js +503 -0
  16. package/dist/esm/actions/deploys.js +80 -0
  17. package/dist/esm/actions/editorialWorkflow.js +480 -0
  18. package/dist/esm/actions/entries.js +876 -0
  19. package/dist/esm/actions/media.js +147 -0
  20. package/dist/esm/actions/mediaLibrary.js +599 -0
  21. package/dist/esm/actions/notifications.js +21 -0
  22. package/dist/esm/actions/search.js +149 -0
  23. package/dist/esm/actions/status.js +74 -0
  24. package/dist/esm/actions/waitUntil.js +32 -0
  25. package/dist/esm/backend.js +1090 -0
  26. package/dist/esm/bootstrap.js +101 -0
  27. package/dist/esm/components/App/App.js +296 -0
  28. package/dist/esm/components/App/Header.js +172 -0
  29. package/dist/esm/components/App/NotFoundPage.js +19 -0
  30. package/dist/esm/components/Collection/Collection.js +198 -0
  31. package/dist/esm/components/Collection/CollectionControls.js +47 -0
  32. package/dist/esm/components/Collection/CollectionSearch.js +222 -0
  33. package/dist/esm/components/Collection/CollectionTop.js +68 -0
  34. package/dist/esm/components/Collection/ControlButton.js +17 -0
  35. package/dist/esm/components/Collection/Entries/Entries.js +73 -0
  36. package/dist/esm/components/Collection/Entries/EntriesCollection.js +241 -0
  37. package/dist/esm/components/Collection/Entries/EntriesSearch.js +113 -0
  38. package/dist/esm/components/Collection/Entries/EntryCard.js +192 -0
  39. package/dist/esm/components/Collection/Entries/EntryListing.js +143 -0
  40. package/dist/esm/components/Collection/FilterControl.js +33 -0
  41. package/dist/esm/components/Collection/GroupControl.js +33 -0
  42. package/dist/esm/components/Collection/NestedCollection.js +308 -0
  43. package/dist/esm/components/Collection/Sidebar.js +91 -0
  44. package/dist/esm/components/Collection/SortControl.js +59 -0
  45. package/dist/esm/components/Collection/ViewStyleControl.js +41 -0
  46. package/dist/esm/components/Editor/Editor.js +466 -0
  47. package/dist/esm/components/Editor/EditorControlPane/EditorControl.js +398 -0
  48. package/dist/esm/components/Editor/EditorControlPane/EditorControlPane.js +254 -0
  49. package/dist/esm/components/Editor/EditorControlPane/Widget.js +374 -0
  50. package/dist/esm/components/Editor/EditorInterface.js +386 -0
  51. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreview.js +47 -0
  52. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewContent.js +67 -0
  53. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewPane.js +306 -0
  54. package/dist/esm/components/Editor/EditorPreviewPane/PreviewHOC.js +27 -0
  55. package/dist/esm/components/Editor/EditorToolbar.js +554 -0
  56. package/dist/esm/components/Editor/withWorkflow.js +56 -0
  57. package/dist/esm/components/EditorWidgets/Unknown/UnknownControl.js +18 -0
  58. package/dist/esm/components/EditorWidgets/Unknown/UnknownPreview.js +20 -0
  59. package/dist/esm/components/EditorWidgets/index.js +4 -0
  60. package/dist/esm/components/MediaLibrary/EmptyMessage.js +22 -0
  61. package/dist/esm/components/MediaLibrary/MediaLibrary.js +446 -0
  62. package/dist/esm/components/MediaLibrary/MediaLibraryButtons.js +92 -0
  63. package/dist/esm/components/MediaLibrary/MediaLibraryCard.js +99 -0
  64. package/dist/esm/components/MediaLibrary/MediaLibraryCardGrid.js +198 -0
  65. package/dist/esm/components/MediaLibrary/MediaLibraryHeader.js +34 -0
  66. package/dist/esm/components/MediaLibrary/MediaLibraryModal.js +156 -0
  67. package/dist/esm/components/MediaLibrary/MediaLibrarySearch.js +51 -0
  68. package/dist/esm/components/MediaLibrary/MediaLibraryTop.js +124 -0
  69. package/dist/esm/components/UI/DragDrop.js +67 -0
  70. package/dist/esm/components/UI/ErrorBoundary.js +170 -0
  71. package/dist/esm/components/UI/FileUploadButton.js +27 -0
  72. package/dist/esm/components/UI/Modal.js +104 -0
  73. package/dist/esm/components/UI/Notifications.js +62 -0
  74. package/dist/esm/components/UI/SettingsDropdown.js +107 -0
  75. package/dist/esm/components/UI/index.js +6 -0
  76. package/dist/esm/components/Workflow/Workflow.js +133 -0
  77. package/dist/esm/components/Workflow/WorkflowCard.js +128 -0
  78. package/dist/esm/components/Workflow/WorkflowList.js +204 -0
  79. package/dist/esm/constants/collectionTypes.js +2 -0
  80. package/dist/esm/constants/collectionViews.js +2 -0
  81. package/dist/esm/constants/commitProps.js +2 -0
  82. package/dist/esm/constants/configSchema.js +695 -0
  83. package/dist/esm/constants/fieldInference.js +57 -0
  84. package/dist/esm/constants/publishModes.js +18 -0
  85. package/dist/esm/constants/validationErrorTypes.js +6 -0
  86. package/dist/esm/formats/formats.js +83 -0
  87. package/dist/esm/formats/frontmatter.js +146 -0
  88. package/dist/esm/formats/helpers.js +12 -0
  89. package/dist/esm/formats/json.js +8 -0
  90. package/dist/esm/formats/toml.js +32 -0
  91. package/dist/esm/formats/yaml.js +60 -0
  92. package/dist/esm/index.js +7 -0
  93. package/dist/esm/integrations/index.js +28 -0
  94. package/dist/esm/integrations/providers/algolia/implementation.js +174 -0
  95. package/dist/esm/integrations/providers/assetStore/implementation.js +165 -0
  96. package/dist/esm/lib/consoleError.js +3 -0
  97. package/dist/esm/lib/formatters.js +201 -0
  98. package/dist/esm/lib/i18n.js +372 -0
  99. package/dist/esm/lib/imageTransformations.js +143 -0
  100. package/dist/esm/lib/phrases.js +6 -0
  101. package/dist/esm/lib/polyfill.js +8 -0
  102. package/dist/esm/lib/registry.js +332 -0
  103. package/dist/esm/lib/serializeEntryValues.js +67 -0
  104. package/dist/esm/lib/stega.js +142 -0
  105. package/dist/esm/lib/textHelper.js +9 -0
  106. package/dist/esm/lib/urlHelper.js +129 -0
  107. package/dist/esm/mediaLibrary.js +37 -0
  108. package/dist/esm/reducers/auth.js +27 -0
  109. package/dist/esm/reducers/collections.js +428 -0
  110. package/dist/esm/reducers/combinedReducer.js +8 -0
  111. package/dist/esm/reducers/config.js +29 -0
  112. package/dist/esm/reducers/cursors.js +31 -0
  113. package/dist/esm/reducers/deploys.js +50 -0
  114. package/dist/esm/reducers/editorialWorkflow.js +83 -0
  115. package/dist/esm/reducers/entries.js +568 -0
  116. package/dist/esm/reducers/entryDraft.js +211 -0
  117. package/dist/esm/reducers/globalUI.js +25 -0
  118. package/dist/esm/reducers/index.js +66 -0
  119. package/dist/esm/reducers/integrations.js +53 -0
  120. package/dist/esm/reducers/mediaLibrary.js +252 -0
  121. package/dist/esm/reducers/medias.js +68 -0
  122. package/dist/esm/reducers/notifications.js +23 -0
  123. package/dist/esm/reducers/search.js +92 -0
  124. package/dist/esm/reducers/status.js +30 -0
  125. package/dist/esm/redux/index.js +7 -0
  126. package/dist/esm/redux/middleware/waitUntilAction.js +48 -0
  127. package/dist/esm/routing/history.js +12 -0
  128. package/dist/esm/types/diacritics.d.js +0 -0
  129. package/dist/esm/types/global.d.js +1 -0
  130. package/dist/esm/types/immutable.js +7 -0
  131. package/dist/esm/types/redux.js +14 -0
  132. package/dist/esm/types/tomlify-j0.4.d.js +0 -0
  133. package/dist/esm/valueObjects/AssetProxy.js +44 -0
  134. package/dist/esm/valueObjects/EditorComponent.js +34 -0
  135. package/dist/esm/valueObjects/Entry.js +20 -0
  136. package/index.d.ts +662 -0
  137. package/package.json +100 -0
  138. package/src/__tests__/backend.spec.js +1223 -0
  139. package/src/actions/__tests__/config.spec.js +1023 -0
  140. package/src/actions/__tests__/editorialWorkflow.spec.js +216 -0
  141. package/src/actions/__tests__/entries.spec.js +610 -0
  142. package/src/actions/__tests__/media.spec.ts +171 -0
  143. package/src/actions/__tests__/mediaLibrary.spec.js +462 -0
  144. package/src/actions/__tests__/search.spec.js +209 -0
  145. package/src/actions/auth.ts +127 -0
  146. package/src/actions/collections.ts +18 -0
  147. package/src/actions/config.ts +574 -0
  148. package/src/actions/deploys.ts +105 -0
  149. package/src/actions/editorialWorkflow.ts +567 -0
  150. package/src/actions/entries.ts +1070 -0
  151. package/src/actions/media.ts +139 -0
  152. package/src/actions/mediaLibrary.ts +639 -0
  153. package/src/actions/notifications.ts +36 -0
  154. package/src/actions/search.ts +221 -0
  155. package/src/actions/status.ts +99 -0
  156. package/src/actions/waitUntil.ts +49 -0
  157. package/src/backend.ts +1411 -0
  158. package/src/bootstrap.js +104 -0
  159. package/src/components/App/App.js +295 -0
  160. package/src/components/App/Header.js +291 -0
  161. package/src/components/App/NotFoundPage.js +23 -0
  162. package/src/components/Collection/Collection.js +210 -0
  163. package/src/components/Collection/CollectionControls.js +58 -0
  164. package/src/components/Collection/CollectionSearch.js +243 -0
  165. package/src/components/Collection/CollectionTop.js +81 -0
  166. package/src/components/Collection/ControlButton.js +27 -0
  167. package/src/components/Collection/Entries/Entries.js +82 -0
  168. package/src/components/Collection/Entries/EntriesCollection.js +277 -0
  169. package/src/components/Collection/Entries/EntriesSearch.js +102 -0
  170. package/src/components/Collection/Entries/EntryCard.js +256 -0
  171. package/src/components/Collection/Entries/EntryListing.js +151 -0
  172. package/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js +163 -0
  173. package/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap +46 -0
  174. package/src/components/Collection/FilterControl.js +39 -0
  175. package/src/components/Collection/GroupControl.js +39 -0
  176. package/src/components/Collection/NestedCollection.js +330 -0
  177. package/src/components/Collection/Sidebar.js +136 -0
  178. package/src/components/Collection/SortControl.js +68 -0
  179. package/src/components/Collection/ViewStyleControl.js +52 -0
  180. package/src/components/Collection/__tests__/Collection.spec.js +75 -0
  181. package/src/components/Collection/__tests__/NestedCollection.spec.js +445 -0
  182. package/src/components/Collection/__tests__/Sidebar.spec.js +87 -0
  183. package/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap +144 -0
  184. package/src/components/Collection/__tests__/__snapshots__/NestedCollection.spec.js.snap +550 -0
  185. package/src/components/Collection/__tests__/__snapshots__/Sidebar.spec.js.snap +312 -0
  186. package/src/components/Editor/Editor.js +497 -0
  187. package/src/components/Editor/EditorControlPane/EditorControl.js +453 -0
  188. package/src/components/Editor/EditorControlPane/EditorControlPane.js +269 -0
  189. package/src/components/Editor/EditorControlPane/Widget.js +384 -0
  190. package/src/components/Editor/EditorInterface.js +444 -0
  191. package/src/components/Editor/EditorPreviewPane/EditorPreview.js +40 -0
  192. package/src/components/Editor/EditorPreviewPane/EditorPreviewContent.js +75 -0
  193. package/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js +337 -0
  194. package/src/components/Editor/EditorPreviewPane/PreviewHOC.js +33 -0
  195. package/src/components/Editor/EditorToolbar.js +728 -0
  196. package/src/components/Editor/__tests__/Editor.spec.js +221 -0
  197. package/src/components/Editor/__tests__/EditorToolbar.spec.js +166 -0
  198. package/src/components/Editor/__tests__/__snapshots__/Editor.spec.js.snap +45 -0
  199. package/src/components/Editor/__tests__/__snapshots__/EditorToolbar.spec.js.snap +4265 -0
  200. package/src/components/Editor/withWorkflow.js +61 -0
  201. package/src/components/EditorWidgets/Unknown/UnknownControl.js +17 -0
  202. package/src/components/EditorWidgets/Unknown/UnknownPreview.js +19 -0
  203. package/src/components/EditorWidgets/index.js +5 -0
  204. package/src/components/MediaLibrary/EmptyMessage.js +28 -0
  205. package/src/components/MediaLibrary/MediaLibrary.js +411 -0
  206. package/src/components/MediaLibrary/MediaLibraryButtons.js +135 -0
  207. package/src/components/MediaLibrary/MediaLibraryCard.js +128 -0
  208. package/src/components/MediaLibrary/MediaLibraryCardGrid.js +199 -0
  209. package/src/components/MediaLibrary/MediaLibraryHeader.js +48 -0
  210. package/src/components/MediaLibrary/MediaLibraryModal.js +200 -0
  211. package/src/components/MediaLibrary/MediaLibrarySearch.js +61 -0
  212. package/src/components/MediaLibrary/MediaLibraryTop.js +144 -0
  213. package/src/components/MediaLibrary/__tests__/MediaLibraryButtons.spec.js +45 -0
  214. package/src/components/MediaLibrary/__tests__/MediaLibraryCard.spec.js +49 -0
  215. package/src/components/MediaLibrary/__tests__/__snapshots__/MediaLibraryCard.spec.js.snap +264 -0
  216. package/src/components/UI/DragDrop.js +66 -0
  217. package/src/components/UI/ErrorBoundary.js +213 -0
  218. package/src/components/UI/FileUploadButton.js +24 -0
  219. package/src/components/UI/Modal.js +112 -0
  220. package/src/components/UI/Notifications.tsx +83 -0
  221. package/src/components/UI/SettingsDropdown.js +130 -0
  222. package/src/components/UI/__tests__/ErrorBoundary.spec.js +57 -0
  223. package/src/components/UI/index.js +6 -0
  224. package/src/components/Workflow/Workflow.js +169 -0
  225. package/src/components/Workflow/WorkflowCard.js +177 -0
  226. package/src/components/Workflow/WorkflowList.js +272 -0
  227. package/src/constants/__tests__/configSchema.spec.js +644 -0
  228. package/src/constants/collectionTypes.ts +2 -0
  229. package/src/constants/collectionViews.js +2 -0
  230. package/src/constants/commitProps.ts +2 -0
  231. package/src/constants/configSchema.js +489 -0
  232. package/src/constants/fieldInference.tsx +78 -0
  233. package/src/constants/publishModes.ts +22 -0
  234. package/src/constants/validationErrorTypes.js +6 -0
  235. package/src/formats/__tests__/formats.spec.js +87 -0
  236. package/src/formats/__tests__/frontmatter.spec.js +450 -0
  237. package/src/formats/__tests__/toml.spec.js +9 -0
  238. package/src/formats/__tests__/yaml.spec.js +189 -0
  239. package/src/formats/formats.ts +97 -0
  240. package/src/formats/frontmatter.ts +150 -0
  241. package/src/formats/helpers.ts +14 -0
  242. package/src/formats/json.ts +9 -0
  243. package/src/formats/toml.ts +33 -0
  244. package/src/formats/yaml.ts +73 -0
  245. package/src/index.js +8 -0
  246. package/src/integrations/index.js +35 -0
  247. package/src/integrations/providers/algolia/implementation.js +176 -0
  248. package/src/integrations/providers/assetStore/implementation.js +148 -0
  249. package/src/lib/__tests__/formatters.spec.js +844 -0
  250. package/src/lib/__tests__/i18n.spec.js +792 -0
  251. package/src/lib/__tests__/imageTransformations.spec.ts +97 -0
  252. package/src/lib/__tests__/phrases.spec.js +119 -0
  253. package/src/lib/__tests__/registry.spec.js +261 -0
  254. package/src/lib/__tests__/serializeEntryValues.spec.js +22 -0
  255. package/src/lib/__tests__/urlHelper.spec.js +145 -0
  256. package/src/lib/consoleError.js +7 -0
  257. package/src/lib/formatters.ts +297 -0
  258. package/src/lib/i18n.ts +462 -0
  259. package/src/lib/imageTransformations.ts +212 -0
  260. package/src/lib/phrases.js +8 -0
  261. package/src/lib/polyfill.js +9 -0
  262. package/src/lib/registry.js +315 -0
  263. package/src/lib/serializeEntryValues.js +75 -0
  264. package/src/lib/stega.ts +145 -0
  265. package/src/lib/textHelper.js +11 -0
  266. package/src/lib/urlHelper.ts +152 -0
  267. package/src/mediaLibrary.ts +51 -0
  268. package/src/reducers/__tests__/auth.spec.ts +38 -0
  269. package/src/reducers/__tests__/collections.spec.js +610 -0
  270. package/src/reducers/__tests__/config.spec.js +38 -0
  271. package/src/reducers/__tests__/deploys.spec.ts +111 -0
  272. package/src/reducers/__tests__/entries.spec.js +694 -0
  273. package/src/reducers/__tests__/entryDraft.spec.js +315 -0
  274. package/src/reducers/__tests__/globalUI.js +43 -0
  275. package/src/reducers/__tests__/integrations.spec.ts +76 -0
  276. package/src/reducers/__tests__/mediaLibrary.spec.js +154 -0
  277. package/src/reducers/__tests__/medias.spec.ts +49 -0
  278. package/src/reducers/auth.ts +46 -0
  279. package/src/reducers/collections.ts +535 -0
  280. package/src/reducers/combinedReducer.ts +11 -0
  281. package/src/reducers/config.ts +38 -0
  282. package/src/reducers/cursors.js +36 -0
  283. package/src/reducers/deploys.ts +54 -0
  284. package/src/reducers/editorialWorkflow.ts +163 -0
  285. package/src/reducers/entries.ts +819 -0
  286. package/src/reducers/entryDraft.js +259 -0
  287. package/src/reducers/globalUI.ts +45 -0
  288. package/src/reducers/index.ts +82 -0
  289. package/src/reducers/integrations.ts +59 -0
  290. package/src/reducers/mediaLibrary.ts +296 -0
  291. package/src/reducers/medias.ts +66 -0
  292. package/src/reducers/notifications.ts +52 -0
  293. package/src/reducers/search.ts +111 -0
  294. package/src/reducers/status.ts +40 -0
  295. package/src/redux/index.ts +18 -0
  296. package/src/redux/middleware/waitUntilAction.ts +64 -0
  297. package/src/routing/__tests__/history.spec.ts +49 -0
  298. package/src/routing/history.ts +17 -0
  299. package/src/types/diacritics.d.ts +1 -0
  300. package/src/types/global.d.ts +8 -0
  301. package/src/types/immutable.ts +49 -0
  302. package/src/types/redux.ts +875 -0
  303. package/src/types/tomlify-j0.4.d.ts +13 -0
  304. package/src/valueObjects/AssetProxy.ts +48 -0
  305. package/src/valueObjects/EditorComponent.js +38 -0
  306. package/src/valueObjects/Entry.ts +63 -0
@@ -0,0 +1,462 @@
1
+ import { Map, List } from 'immutable';
2
+ import set from 'lodash/set';
3
+ import groupBy from 'lodash/groupBy';
4
+ import escapeRegExp from 'lodash/escapeRegExp';
5
+
6
+ import { selectEntrySlug } from '../reducers/collections';
7
+
8
+ import type { Collection, Entry, EntryDraft, EntryField, EntryMap } from '../types/redux';
9
+ import type { EntryValue } from '../valueObjects/Entry';
10
+
11
+ export const I18N = 'i18n';
12
+
13
+ export enum I18N_STRUCTURE {
14
+ MULTIPLE_FOLDERS = 'multiple_folders',
15
+ MULTIPLE_FILES = 'multiple_files',
16
+ SINGLE_FILE = 'single_file',
17
+ }
18
+
19
+ export enum I18N_FIELD {
20
+ TRANSLATE = 'translate',
21
+ DUPLICATE = 'duplicate',
22
+ NONE = 'none',
23
+ }
24
+
25
+ export function hasI18n(collection: Collection) {
26
+ return collection.has(I18N);
27
+ }
28
+
29
+ export type I18nInfo = {
30
+ locales: string[];
31
+ defaultLocale: string;
32
+ structure: I18N_STRUCTURE;
33
+ };
34
+
35
+ export function getI18nInfo(collection: Collection) {
36
+ if (!hasI18n(collection)) {
37
+ return {};
38
+ }
39
+ const { structure, locales, default_locale: defaultLocale } = collection.get(I18N).toJS();
40
+ return { structure, locales, defaultLocale } as I18nInfo;
41
+ }
42
+
43
+ export function getI18nFilesDepth(collection: Collection, depth: number) {
44
+ const { structure } = getI18nInfo(collection) as I18nInfo;
45
+ if (structure === I18N_STRUCTURE.MULTIPLE_FOLDERS) {
46
+ return depth + 1;
47
+ }
48
+ return depth;
49
+ }
50
+
51
+ export function isFieldTranslatable(field: EntryField, locale: string, defaultLocale: string) {
52
+ const isTranslatable = locale !== defaultLocale && field.get(I18N) === I18N_FIELD.TRANSLATE;
53
+ return isTranslatable;
54
+ }
55
+
56
+ export function isFieldDuplicate(field: EntryField, locale: string, defaultLocale: string) {
57
+ const isDuplicate = locale !== defaultLocale && field.get(I18N) === I18N_FIELD.DUPLICATE;
58
+ return isDuplicate;
59
+ }
60
+
61
+ export function isFieldHidden(field: EntryField, locale: string, defaultLocale: string) {
62
+ const isHidden = locale !== defaultLocale && field.get(I18N) === I18N_FIELD.NONE;
63
+ return isHidden;
64
+ }
65
+
66
+ export function getLocaleDataPath(locale: string) {
67
+ return [I18N, locale, 'data'];
68
+ }
69
+
70
+ export function getDataPath(locale: string, defaultLocale: string) {
71
+ const dataPath = locale !== defaultLocale ? getLocaleDataPath(locale) : ['data'];
72
+ return dataPath;
73
+ }
74
+
75
+ export function getFilePath(
76
+ structure: I18N_STRUCTURE,
77
+ extension: string,
78
+ path: string,
79
+ slug: string,
80
+ locale: string,
81
+ ) {
82
+ switch (structure) {
83
+ case I18N_STRUCTURE.MULTIPLE_FOLDERS:
84
+ return path.replace(`/${slug}`, `/${locale}/${slug}`);
85
+ case I18N_STRUCTURE.MULTIPLE_FILES:
86
+ return path.replace(new RegExp(`${escapeRegExp(extension)}$`), `${locale}.${extension}`);
87
+ case I18N_STRUCTURE.SINGLE_FILE:
88
+ default:
89
+ return path;
90
+ }
91
+ }
92
+
93
+ export function getLocaleFromPath(structure: I18N_STRUCTURE, extension: string, path: string) {
94
+ switch (structure) {
95
+ case I18N_STRUCTURE.MULTIPLE_FOLDERS: {
96
+ const parts = path.split('/');
97
+ // filename
98
+ parts.pop();
99
+ // locale
100
+ return parts.pop();
101
+ }
102
+ case I18N_STRUCTURE.MULTIPLE_FILES: {
103
+ const parts = path.slice(0, -`.${extension}`.length);
104
+ return parts.split('.').pop();
105
+ }
106
+ case I18N_STRUCTURE.SINGLE_FILE:
107
+ default:
108
+ return '';
109
+ }
110
+ }
111
+
112
+ export function getFilePaths(
113
+ collection: Collection,
114
+ extension: string,
115
+ path: string,
116
+ slug: string,
117
+ ) {
118
+ const { structure, locales } = getI18nInfo(collection) as I18nInfo;
119
+
120
+ if (structure === I18N_STRUCTURE.SINGLE_FILE) {
121
+ return [path];
122
+ }
123
+
124
+ const paths = locales.map(locale =>
125
+ getFilePath(structure as I18N_STRUCTURE, extension, path, slug, locale),
126
+ );
127
+
128
+ return paths;
129
+ }
130
+
131
+ export function normalizeFilePath(structure: I18N_STRUCTURE, path: string, locale: string) {
132
+ switch (structure) {
133
+ case I18N_STRUCTURE.MULTIPLE_FOLDERS:
134
+ return path.replace(`${locale}/`, '');
135
+ case I18N_STRUCTURE.MULTIPLE_FILES:
136
+ return path.replace(`.${locale}`, '');
137
+ case I18N_STRUCTURE.SINGLE_FILE:
138
+ default:
139
+ return path;
140
+ }
141
+ }
142
+
143
+ export function getI18nFiles(
144
+ collection: Collection,
145
+ extension: string,
146
+ entryDraft: EntryMap,
147
+ entryToRaw: (entryDraft: EntryMap) => string,
148
+ path: string,
149
+ slug: string,
150
+ newPath?: string,
151
+ ) {
152
+ const { structure, defaultLocale, locales } = getI18nInfo(collection) as I18nInfo;
153
+
154
+ if (structure === I18N_STRUCTURE.SINGLE_FILE) {
155
+ const data = locales.reduce((map, locale) => {
156
+ const dataPath = getDataPath(locale, defaultLocale);
157
+ return map.set(locale, entryDraft.getIn(dataPath));
158
+ }, Map<string, unknown>({}));
159
+ const draft = entryDraft.set('data', data);
160
+
161
+ return [
162
+ {
163
+ path: getFilePath(structure, extension, path, slug, locales[0]),
164
+ slug,
165
+ raw: entryToRaw(draft),
166
+ ...(newPath && {
167
+ newPath: getFilePath(structure, extension, newPath, slug, locales[0]),
168
+ }),
169
+ },
170
+ ];
171
+ }
172
+
173
+ const dataFiles = locales
174
+ .map(locale => {
175
+ const dataPath = getDataPath(locale, defaultLocale);
176
+ const draft = entryDraft.set('data', entryDraft.getIn(dataPath));
177
+ return {
178
+ path: getFilePath(structure, extension, path, slug, locale),
179
+ slug,
180
+ raw: draft.get('data') ? entryToRaw(draft) : '',
181
+ ...(newPath && {
182
+ newPath: getFilePath(structure, extension, newPath, slug, locale),
183
+ }),
184
+ };
185
+ })
186
+ .filter(dataFile => dataFile.raw);
187
+ return dataFiles;
188
+ }
189
+
190
+ export function getI18nBackup(
191
+ collection: Collection,
192
+ entry: EntryMap,
193
+ entryToRaw: (entry: EntryMap) => string,
194
+ ) {
195
+ const { locales, defaultLocale } = getI18nInfo(collection) as I18nInfo;
196
+
197
+ const i18nBackup = locales
198
+ .filter(l => l !== defaultLocale)
199
+ .reduce((acc, locale) => {
200
+ const dataPath = getDataPath(locale, defaultLocale);
201
+ const data = entry.getIn(dataPath);
202
+ if (!data) {
203
+ return acc;
204
+ }
205
+ const draft = entry.set('data', data);
206
+ return { ...acc, [locale]: { raw: entryToRaw(draft) } };
207
+ }, {} as Record<string, { raw: string }>);
208
+
209
+ return i18nBackup;
210
+ }
211
+
212
+ export function formatI18nBackup(
213
+ i18nBackup: Record<string, { raw: string }>,
214
+ formatRawData: (raw: string) => EntryValue,
215
+ ) {
216
+ const i18n = Object.entries(i18nBackup).reduce((acc, [locale, { raw }]) => {
217
+ const entry = formatRawData(raw);
218
+ return { ...acc, [locale]: { data: entry.data } };
219
+ }, {});
220
+
221
+ return i18n;
222
+ }
223
+
224
+ function applyDefaultI18nValues(
225
+ collection: Collection,
226
+ value: EntryValue,
227
+ defaultLocaleValue: EntryValue,
228
+ ) {
229
+ if (collection.get('fields') === undefined) {
230
+ return;
231
+ }
232
+ collection.get('fields').forEach(field => {
233
+ if (field && field.get(I18N) === I18N_FIELD.DUPLICATE) {
234
+ const data = value.data[field.get('name')];
235
+ if (!data) {
236
+ value.data[field.get('name')] = defaultLocaleValue.data[field.get('name')];
237
+ }
238
+ }
239
+ });
240
+ }
241
+
242
+ function mergeValues(
243
+ collection: Collection,
244
+ structure: I18N_STRUCTURE,
245
+ defaultLocale: string,
246
+ values: { locale: string; value: EntryValue }[],
247
+ ) {
248
+ let defaultEntry = values.find(e => e.locale === defaultLocale);
249
+ if (!defaultEntry) {
250
+ defaultEntry = values[0];
251
+ console.warn(`Could not locale entry for default locale '${defaultLocale}'`);
252
+ }
253
+ const i18n = values
254
+ .filter(e => e.locale !== defaultEntry!.locale)
255
+ .reduce((acc, { locale, value }) => {
256
+ const dataPath = getLocaleDataPath(locale);
257
+ if (defaultEntry) {
258
+ applyDefaultI18nValues(collection, value, defaultEntry.value);
259
+ }
260
+ return set(acc, dataPath, value.data);
261
+ }, {});
262
+
263
+ const path = normalizeFilePath(structure, defaultEntry.value.path, defaultLocale);
264
+ const slug = selectEntrySlug(collection, path) as string;
265
+ const entryValue: EntryValue = {
266
+ ...defaultEntry.value,
267
+ raw: '',
268
+ ...i18n,
269
+ path,
270
+ slug,
271
+ };
272
+
273
+ return entryValue;
274
+ }
275
+
276
+ function mergeSingleFileValue(
277
+ entryValue: EntryValue,
278
+ defaultLocale: string,
279
+ locales: string[],
280
+ ): EntryValue {
281
+ const data = entryValue.data[defaultLocale] || {};
282
+ const i18n = locales
283
+ .filter(l => l !== defaultLocale)
284
+ .map(l => ({ locale: l, value: entryValue.data[l] }))
285
+ .filter(e => e.value)
286
+ .reduce((acc, e) => {
287
+ return { ...acc, [e.locale]: { data: e.value } };
288
+ }, {});
289
+
290
+ return {
291
+ ...entryValue,
292
+ data,
293
+ i18n,
294
+ raw: '',
295
+ };
296
+ }
297
+
298
+ export async function getI18nEntry(
299
+ collection: Collection,
300
+ extension: string,
301
+ path: string,
302
+ slug: string,
303
+ getEntryValue: (path: string) => Promise<EntryValue>,
304
+ ) {
305
+ const { structure, locales, defaultLocale } = getI18nInfo(collection) as I18nInfo;
306
+
307
+ let entryValue: EntryValue;
308
+ if (structure === I18N_STRUCTURE.SINGLE_FILE) {
309
+ entryValue = mergeSingleFileValue(await getEntryValue(path), defaultLocale, locales);
310
+ } else {
311
+ const entryValuesResults = await Promise.allSettled(
312
+ locales.map(async locale => {
313
+ const entryPath = getFilePath(structure, extension, path, slug, locale);
314
+ const value = await getEntryValue(entryPath);
315
+ return { value, locale };
316
+ }),
317
+ );
318
+
319
+ const nonNullValues = entryValuesResults
320
+ .map(e => (e.status === 'fulfilled' ? e.value : undefined))
321
+ .filter((e): e is { value: EntryValue; locale: string } => e !== undefined);
322
+
323
+ if (nonNullValues.length === 0) {
324
+ // mergeValues will throw on an empty list, and show the error messages.
325
+ const [error = new Error('No entry values found for any locale')] = entryValuesResults
326
+ .map(e => (e.status === 'rejected' ? e.reason : undefined))
327
+ .filter(e => e !== undefined);
328
+
329
+ throw error;
330
+ }
331
+
332
+ entryValue = mergeValues(collection, structure, defaultLocale, nonNullValues);
333
+ }
334
+
335
+ return entryValue;
336
+ }
337
+
338
+ export function groupEntries(collection: Collection, extension: string, entries: EntryValue[]) {
339
+ const { structure, defaultLocale, locales } = getI18nInfo(collection) as I18nInfo;
340
+ if (structure === I18N_STRUCTURE.SINGLE_FILE) {
341
+ return entries.map(e => mergeSingleFileValue(e, defaultLocale, locales));
342
+ }
343
+
344
+ const grouped = groupBy(
345
+ entries.map(e => ({
346
+ locale: getLocaleFromPath(structure, extension, e.path) as string,
347
+ value: e,
348
+ })),
349
+ ({ locale, value: e }) => {
350
+ return normalizeFilePath(structure, e.path, locale);
351
+ },
352
+ );
353
+
354
+ const groupedEntries = Object.values(grouped).reduce((acc, values) => {
355
+ const entryValue = mergeValues(collection, structure, defaultLocale, values);
356
+ return [...acc, entryValue];
357
+ }, [] as EntryValue[]);
358
+
359
+ return groupedEntries;
360
+ }
361
+
362
+ export function getI18nDataFiles(
363
+ collection: Collection,
364
+ extension: string,
365
+ path: string,
366
+ slug: string,
367
+ diffFiles: { path: string; id: string; newFile: boolean }[],
368
+ ) {
369
+ const { structure } = getI18nInfo(collection) as I18nInfo;
370
+ if (structure === I18N_STRUCTURE.SINGLE_FILE) {
371
+ return diffFiles;
372
+ }
373
+ const paths = getFilePaths(collection, extension, path, slug);
374
+ const dataFiles = paths.reduce((acc, path) => {
375
+ const dataFile = diffFiles.find(file => file.path === path);
376
+ if (dataFile) {
377
+ return [...acc, dataFile];
378
+ } else {
379
+ return [...acc, { path, id: '', newFile: false }];
380
+ }
381
+ }, [] as { path: string; id: string; newFile: boolean }[]);
382
+
383
+ return dataFiles;
384
+ }
385
+
386
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
387
+ export function duplicateDefaultI18nFields(collection: Collection, dataFields: any) {
388
+ const { locales, defaultLocale } = getI18nInfo(collection) as I18nInfo;
389
+
390
+ const i18nFields = Object.fromEntries(
391
+ locales
392
+ .filter(locale => locale !== defaultLocale)
393
+ .map(locale => [locale, { data: dataFields }]),
394
+ );
395
+
396
+ return i18nFields;
397
+ }
398
+
399
+ export function duplicateI18nFields(
400
+ entryDraft: EntryDraft,
401
+ field: EntryField,
402
+ locales: string[],
403
+ defaultLocale: string,
404
+ fieldPath: string[] = [field.get('name')],
405
+ ) {
406
+ const value = entryDraft.getIn(['entry', 'data', ...fieldPath]);
407
+ if (field.get(I18N) === I18N_FIELD.DUPLICATE) {
408
+ locales
409
+ .filter(l => l !== defaultLocale)
410
+ .forEach(l => {
411
+ entryDraft = entryDraft.setIn(
412
+ ['entry', ...getDataPath(l, defaultLocale), ...fieldPath],
413
+ value,
414
+ );
415
+ });
416
+ }
417
+
418
+ if (field.has('field') && !List.isList(value)) {
419
+ const fields = [field.get('field') as EntryField];
420
+ fields.forEach(field => {
421
+ entryDraft = duplicateI18nFields(entryDraft, field, locales, defaultLocale, [
422
+ ...fieldPath,
423
+ field.get('name'),
424
+ ]);
425
+ });
426
+ } else if (field.has('fields') && !List.isList(value)) {
427
+ const fields = field.get('fields')!.toArray() as EntryField[];
428
+ fields.forEach(field => {
429
+ entryDraft = duplicateI18nFields(entryDraft, field, locales, defaultLocale, [
430
+ ...fieldPath,
431
+ field.get('name'),
432
+ ]);
433
+ });
434
+ }
435
+
436
+ return entryDraft;
437
+ }
438
+
439
+ export function getPreviewEntry(entry: EntryMap, locale: string, defaultLocale: string) {
440
+ if (locale === defaultLocale) {
441
+ return entry;
442
+ }
443
+ return entry.set('data', entry.getIn([I18N, locale, 'data']));
444
+ }
445
+
446
+ export function serializeI18n(
447
+ collection: Collection,
448
+ entry: Entry,
449
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
450
+ serializeValues: (data: any) => any,
451
+ ) {
452
+ const { locales, defaultLocale } = getI18nInfo(collection) as I18nInfo;
453
+
454
+ locales
455
+ .filter(locale => locale !== defaultLocale)
456
+ .forEach(locale => {
457
+ const dataPath = getLocaleDataPath(locale);
458
+ entry = entry.setIn(dataPath, serializeValues(entry.getIn(dataPath)));
459
+ });
460
+
461
+ return entry;
462
+ }
@@ -0,0 +1,212 @@
1
+ import { join } from 'path';
2
+ import { basename, fileExtension, fileExtensionWithSeparator } from 'decap-cms-lib-util';
3
+
4
+ import type * as Photon from '@silvia-odwyer/photon';
5
+ import type { CmsImageTransformations, EntryField } from '../types/redux';
6
+
7
+ type PhotonImage = InstanceType<typeof Photon.PhotonImage>;
8
+
9
+ export type ImageTransformationConfig = {
10
+ keepOriginal: boolean;
11
+ variants: ImageTransformationVariant[];
12
+ };
13
+
14
+ type ImageTransformationVariant = {
15
+ name: string;
16
+ width?: number;
17
+ height?: number;
18
+ format?: string;
19
+ quality?: number;
20
+ default?: boolean;
21
+ keep_original_size?: boolean;
22
+ };
23
+
24
+ export type ImageTransformationFile = {
25
+ file: File;
26
+ path: string;
27
+ default?: boolean;
28
+ original?: boolean;
29
+ };
30
+
31
+ const TRANSFORMATIONS_FOLDER = '_transformations';
32
+ const SUPPORTED_OUTPUT_FORMATS = ['jpg', 'jpeg', 'png', 'webp'];
33
+
34
+ function getFieldImageTransformations(field?: EntryField) {
35
+ const imageTransformations = field?.get('image_transformations') as
36
+ | (CmsImageTransformations & { toJS?: () => CmsImageTransformations })
37
+ | undefined;
38
+
39
+ return imageTransformations?.toJS ? imageTransformations.toJS() : imageTransformations;
40
+ }
41
+
42
+ function normalizeVariant(variant: ImageTransformationVariant) {
43
+ const format = variant.format?.toLowerCase();
44
+ return {
45
+ ...variant,
46
+ format: format === 'jpeg' ? 'jpg' : format,
47
+ };
48
+ }
49
+
50
+ export function getImageTransformationsConfig(
51
+ config: { image_transformations?: CmsImageTransformations },
52
+ field?: EntryField,
53
+ ): ImageTransformationConfig | undefined {
54
+ const imageTransformations = getFieldImageTransformations(field) ?? config.image_transformations;
55
+
56
+ if (!imageTransformations) {
57
+ return undefined;
58
+ }
59
+
60
+ if (Array.isArray(imageTransformations)) {
61
+ return {
62
+ keepOriginal: true,
63
+ variants: imageTransformations.map(normalizeVariant),
64
+ };
65
+ }
66
+
67
+ return {
68
+ keepOriginal: imageTransformations.keep_original ?? true,
69
+ variants: (imageTransformations.variants || []).map(normalizeVariant),
70
+ };
71
+ }
72
+
73
+ export function shouldTransformImage(file: File, config: ImageTransformationConfig | undefined) {
74
+ return (
75
+ !!config &&
76
+ config.variants.length > 0 &&
77
+ file.type.startsWith('image/') &&
78
+ file.type !== 'image/svg+xml'
79
+ );
80
+ }
81
+
82
+ function getOutputFormat(fileName: string, variant: ImageTransformationVariant) {
83
+ const inputFormat = fileExtension(fileName).toLowerCase();
84
+ const outputFormat = variant.format || inputFormat;
85
+
86
+ return SUPPORTED_OUTPUT_FORMATS.includes(outputFormat) ? outputFormat : 'png';
87
+ }
88
+
89
+ function getMimeType(format: string) {
90
+ switch (format) {
91
+ case 'jpg':
92
+ case 'jpeg':
93
+ return 'image/jpeg';
94
+ case 'webp':
95
+ return 'image/webp';
96
+ default:
97
+ return 'image/png';
98
+ }
99
+ }
100
+
101
+ function getVariantFileName(fileName: string, format: string) {
102
+ const extension = fileExtensionWithSeparator(fileName);
103
+ const baseName = extension ? basename(fileName, extension) : basename(fileName);
104
+ return `${baseName}.${format === 'jpeg' ? 'jpg' : format}`;
105
+ }
106
+
107
+ function getVariantPath(originalPath: string, variantName: string, variantFileName: string) {
108
+ const originalName = basename(originalPath);
109
+ const parent = originalPath.slice(0, Math.max(0, originalPath.length - originalName.length));
110
+
111
+ return join(parent, TRANSFORMATIONS_FOLDER, variantName, variantFileName);
112
+ }
113
+
114
+ function getResizeDimensions(
115
+ image: { get_width: () => number; get_height: () => number },
116
+ variant: ImageTransformationVariant,
117
+ ) {
118
+ const originalWidth = image.get_width();
119
+ const originalHeight = image.get_height();
120
+
121
+ if (variant.keep_original_size || (!variant.width && !variant.height)) {
122
+ return { width: originalWidth, height: originalHeight };
123
+ }
124
+
125
+ if (variant.width && variant.height) {
126
+ const scale = Math.min(variant.width / originalWidth, variant.height / originalHeight);
127
+ return {
128
+ width: Math.round(originalWidth * scale),
129
+ height: Math.round(originalHeight * scale),
130
+ };
131
+ }
132
+
133
+ if (variant.width) {
134
+ return {
135
+ width: variant.width,
136
+ height: Math.round((originalHeight * variant.width) / originalWidth),
137
+ };
138
+ }
139
+
140
+ const height = variant.height as number;
141
+ return {
142
+ width: Math.round((originalWidth * height) / originalHeight),
143
+ height,
144
+ };
145
+ }
146
+
147
+ function getImageBytes(image: PhotonImage, format: string, quality = 75) {
148
+ if (format === 'jpg' || format === 'jpeg') {
149
+ return image.get_bytes_jpeg(quality);
150
+ }
151
+
152
+ if (format === 'webp') {
153
+ return image.get_bytes_webp();
154
+ }
155
+
156
+ return image.get_bytes();
157
+ }
158
+
159
+ export async function transformImage(
160
+ file: File,
161
+ originalPath: string,
162
+ config: ImageTransformationConfig,
163
+ ): Promise<ImageTransformationFile[]> {
164
+ const photon = await import('@silvia-odwyer/photon');
165
+ const initPhoton = (photon as unknown as { default: () => Promise<unknown> }).default;
166
+ await initPhoton();
167
+
168
+ const originalImage = photon.PhotonImage.new_from_blob(file);
169
+
170
+ try {
171
+ const transformedFiles = config.variants.map(variant => {
172
+ const { width, height } = getResizeDimensions(originalImage, variant);
173
+ const resizedImage =
174
+ width === originalImage.get_width() && height === originalImage.get_height()
175
+ ? originalImage
176
+ : photon.resize(originalImage, width, height, photon.SamplingFilter.Lanczos3);
177
+ const format = getOutputFormat(file.name, variant);
178
+ const variantFileName = getVariantFileName(file.name, format);
179
+ const bytes = getImageBytes(resizedImage, format, variant.quality);
180
+ const transformedFile = new File([bytes], variantFileName, { type: getMimeType(format) });
181
+
182
+ if (resizedImage !== originalImage) {
183
+ resizedImage.free();
184
+ }
185
+
186
+ return {
187
+ file: transformedFile,
188
+ path: getVariantPath(originalPath, variant.name, variantFileName),
189
+ default: variant.default,
190
+ };
191
+ });
192
+
193
+ return config.keepOriginal
194
+ ? [{ file, path: originalPath, original: true }, ...transformedFiles]
195
+ : transformedFiles;
196
+ } finally {
197
+ originalImage.free();
198
+ }
199
+ }
200
+
201
+ export function sortTransformationFilesForSelection(files: ImageTransformationFile[]) {
202
+ const defaultIndex = files.findIndex(file => file.default);
203
+ const originalIndex = files.findIndex(file => file.original);
204
+ const selectedIndex = defaultIndex >= 0 ? defaultIndex : originalIndex;
205
+
206
+ if (selectedIndex < 0) {
207
+ return files;
208
+ }
209
+
210
+ const selectedFile = files[selectedIndex];
211
+ return [...files.slice(0, selectedIndex), ...files.slice(selectedIndex + 1), selectedFile];
212
+ }
@@ -0,0 +1,8 @@
1
+ import merge from 'lodash/merge';
2
+
3
+ import { getLocale } from './registry';
4
+
5
+ export function getPhrases(locale) {
6
+ const phrases = merge({}, getLocale('en'), getLocale(locale));
7
+ return phrases;
8
+ }
@@ -0,0 +1,9 @@
1
+ import { Buffer } from 'buffer';
2
+
3
+ if (typeof window !== 'undefined') {
4
+ // Polyfill global for packages like @iarna/toml
5
+ window.global = window;
6
+
7
+ // Polyfill Buffer for packages like gray-matter
8
+ window.Buffer = Buffer;
9
+ }