Skip to content

Commit f38ec3d

Browse files
committed
Extract React preview helpers to shared module
1 parent 34b4b85 commit f38ec3d

4 files changed

Lines changed: 486 additions & 428 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { fromJS } from 'immutable';
2+
3+
import { getAssetByPath } from '$lib/services/assets';
4+
import { AssetProxy } from '$lib/services/contents/api/asset-proxy';
5+
import { getEntriesByCollection } from '$lib/services/contents/collection/entries';
6+
import { getCollectionFileEntry } from '$lib/services/contents/collection/files';
7+
import { getField } from '$lib/services/contents/entry/fields';
8+
9+
/**
10+
* @import { Entry, GetFieldArgs, InternalLocaleCode } from '$lib/types/private';
11+
* @import { ApiAsset, FieldKeyPath } from '$lib/types/public';
12+
*/
13+
14+
/**
15+
* Create an asset getter function for React components (preview templates and custom widgets).
16+
* @param {object} args Arguments.
17+
* @param {Entry} args.entry Entry object.
18+
* @param {string} args.collectionName Collection name.
19+
* @param {string} [args.fileName] File name.
20+
* @returns {(path: string) => ApiAsset | undefined} Function that gets asset URLs.
21+
*/
22+
export const createGetAsset =
23+
({ entry, collectionName, fileName }) =>
24+
/**
25+
* Get the asset URL for a given asset path.
26+
* @param {string} path Path to the asset.
27+
* @returns {ApiAsset | undefined} Asset item.
28+
*/
29+
(path) => {
30+
const asset = getAssetByPath({ value: path, entry, collectionName, fileName });
31+
32+
if (asset) {
33+
return new AssetProxy(asset);
34+
}
35+
36+
return undefined;
37+
};
38+
39+
/**
40+
* Get metadata for fields. For relation fields, looks up and stores the referenced entry content
41+
* keyed by collection name and value, matching the `fieldsMetaData` structure expected by
42+
* Netlify/Decap CMS preview templates and custom widgets.
43+
* @param {object} args Arguments.
44+
* @param {InternalLocaleCode} args.locale Current locale.
45+
* @param {Omit<GetFieldArgs, 'keyPath'>} args.getFieldArgs Arguments for getField function.
46+
* @returns {import('immutable').MapOf<any>} Immutable Map of entry metadata.
47+
*/
48+
export const getMetaData = ({ locale, getFieldArgs }) => {
49+
const { valueMap = {} } = getFieldArgs;
50+
/** @type {Record<string, any>} */
51+
const metaData = {};
52+
/** @type {Map<string, Entry[]>} */
53+
const refEntriesCache = new Map();
54+
55+
Object.entries(valueMap).forEach(([key, value]) => {
56+
const keyPath = /** @type {FieldKeyPath} */ (key.replace(/\.\d+$/, ''));
57+
const field = getField({ ...getFieldArgs, keyPath });
58+
59+
// Populate metadata for relation fields by looking up referenced entries
60+
if (field?.widget === 'relation') {
61+
const {
62+
value_field: valueField = '{{slug}}',
63+
collection: refCollection,
64+
file: refFile,
65+
} = field;
66+
67+
const refEntries = (() => {
68+
const cacheKey = `${refCollection}:${refFile ?? ''}`;
69+
const cache = refEntriesCache.get(cacheKey);
70+
71+
if (cache) {
72+
return cache;
73+
}
74+
75+
const entries = (
76+
refFile
77+
? [getCollectionFileEntry(refCollection, refFile)]
78+
: getEntriesByCollection(refCollection)
79+
).filter((entry) => !!entry);
80+
81+
refEntriesCache.set(cacheKey, entries);
82+
83+
return entries;
84+
})();
85+
86+
metaData[keyPath] ??= {};
87+
metaData[keyPath][refCollection] ??= {};
88+
metaData[keyPath][refCollection][value] = refEntries.find((entry) =>
89+
valueField === '{{slug}}'
90+
? entry.slug === value
91+
: entry.locales[locale]?.content?.[valueField] === value,
92+
)?.locales[locale]?.content;
93+
}
94+
});
95+
96+
return /** @type {import('immutable').MapOf<any>} */ (fromJS(metaData));
97+
};

0 commit comments

Comments
 (0)