@liiift-studio/sanity-font-manager 2.3.18 → 2.4.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.
- package/README.md +437 -437
- package/dist/index.js +103 -48
- package/dist/index.mjs +103 -48
- package/package.json +85 -85
- package/src/components/BatchUploadFonts.jsx +640 -639
- package/src/components/FontScriptUploaderComponent.jsx +463 -463
- package/src/components/GenerateCollectionsPairsComponent.jsx +259 -259
- package/src/components/KeyValueInput.jsx +95 -95
- package/src/components/KeyValueReferenceInput.jsx +254 -254
- package/src/components/NestedObjectArraySelector.jsx +146 -146
- package/src/components/PriceInput.jsx +26 -26
- package/src/components/PrimaryCollectionGeneratorTypeface.jsx +116 -116
- package/src/components/RegenerateSubfamiliesComponent.jsx +185 -185
- package/src/components/SetOTF.jsx +87 -87
- package/src/components/SingleUploaderTool.jsx +673 -673
- package/src/components/StatusDisplay.jsx +26 -26
- package/src/components/StyleCountInput.jsx +16 -16
- package/src/components/UpdateScriptsComponent.jsx +76 -76
- package/src/components/UploadButton.jsx +43 -43
- package/src/components/UploadScriptsComponent.jsx +537 -537
- package/src/components/VariableInstanceReferencesInput.jsx +190 -190
- package/src/hooks/useNestedObjects.js +92 -92
- package/src/hooks/useSanityClient.js +9 -9
- package/src/index.js +70 -70
- package/src/schema/openTypeField.js +1945 -1945
- package/src/schema/styleCountField.js +12 -12
- package/src/schema/stylesField.js +268 -268
- package/src/schema/stylisticSetField.js +301 -301
- package/src/utils/generateCssFile.js +205 -205
- package/src/utils/generateFontData.js +145 -145
- package/src/utils/generateFontFile.js +38 -38
- package/src/utils/generateKeywords.js +185 -185
- package/src/utils/generateSubset.js +45 -45
- package/src/utils/getEmptyFontKit.js +99 -99
- package/src/utils/parseVariableFontInstances.js +211 -211
- package/src/utils/processFontFiles.js +487 -477
- package/src/utils/regenerateFontData.js +146 -146
- package/src/utils/sanitizeForSanityId.js +65 -65
- package/src/utils/updateFontPrices.js +94 -94
- package/src/utils/updateTypefaceDocument.js +149 -160
- package/src/utils/uploadFontFiles.js +115 -26
- package/src/utils/utils.js +24 -24
|
@@ -1,160 +1,149 @@
|
|
|
1
|
-
// Patches the parent typeface document's styles.fonts array with newly uploaded font references
|
|
2
|
-
|
|
3
|
-
import { nanoid } from 'nanoid';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Patches a typeface document (draft and published) with the new font references,
|
|
7
|
-
* subfamily structure, and preferred style derived from the upload batch.
|
|
8
|
-
*
|
|
9
|
-
* @param {string} doc_id - The Sanity document ID (may be a draft)
|
|
10
|
-
* @param {Object[]} fontRefs - New regular font references
|
|
11
|
-
* @param {Object[]} variableRefs - New variable font references
|
|
12
|
-
* @param {Object} subfamilies - Map of font ID → subfamily name
|
|
13
|
-
* @param {string[]} uniqueSubfamilies
|
|
14
|
-
* @param {Object[]} subfamiliesArray - Existing subfamilies array from the typeface
|
|
15
|
-
* @param {Object} preferredStyleRef - Existing preferred style reference
|
|
16
|
-
* @param {Object} newPreferredStyle - Candidate preferred style from the upload
|
|
17
|
-
* @param {Object} stylesObject - Existing typeface styles object
|
|
18
|
-
* @param {Object} client - Sanity client
|
|
19
|
-
* @param {Function} setStatus
|
|
20
|
-
* @param {Function} setError
|
|
21
|
-
*/
|
|
22
|
-
export const updateTypefaceDocument = async (
|
|
23
|
-
doc_id,
|
|
24
|
-
fontRefs,
|
|
25
|
-
variableRefs,
|
|
26
|
-
subfamilies,
|
|
27
|
-
uniqueSubfamilies,
|
|
28
|
-
subfamiliesArray,
|
|
29
|
-
preferredStyleRef,
|
|
30
|
-
newPreferredStyle,
|
|
31
|
-
stylesObject,
|
|
32
|
-
client,
|
|
33
|
-
setStatus,
|
|
34
|
-
setError,
|
|
35
|
-
) => {
|
|
36
|
-
console.log('Updating typeface document with new fonts:', { fontRefs, variableRefs, subfamilies, uniqueSubfamilies });
|
|
37
|
-
setStatus('Updating typeface references...');
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
console.log('
|
|
93
|
-
console.log('
|
|
94
|
-
console.log('
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
*
|
|
113
|
-
* @param {string} doc_id
|
|
114
|
-
* @param {Object} preferredStyleRef
|
|
115
|
-
* @param {Object} newPreferredStyle
|
|
116
|
-
* @param {Object} patch
|
|
117
|
-
* @param {Object} client
|
|
118
|
-
*/
|
|
119
|
-
const updatePreferredStyle = async (doc_id, preferredStyleRef, newPreferredStyle, patch, client) => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const publishedId = doc_id.replace('drafts.', '');
|
|
151
|
-
// Parameterized to prevent injection from any draft ID edge cases
|
|
152
|
-
const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then(res => res[0]);
|
|
153
|
-
|
|
154
|
-
if (publishedDoc) {
|
|
155
|
-
await client.patch(publishedId).set(patch).commit();
|
|
156
|
-
console.log(`Updated published document: ${publishedId}`);
|
|
157
|
-
} else {
|
|
158
|
-
console.log(`No published document found for ${publishedId}, skipping`);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
1
|
+
// Patches the parent typeface document's styles.fonts array with newly uploaded font references
|
|
2
|
+
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Patches a typeface document (draft and published) with the new font references,
|
|
7
|
+
* subfamily structure, and preferred style derived from the upload batch.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} doc_id - The Sanity document ID (may be a draft)
|
|
10
|
+
* @param {Object[]} fontRefs - New regular font references
|
|
11
|
+
* @param {Object[]} variableRefs - New variable font references
|
|
12
|
+
* @param {Object} subfamilies - Map of font ID → subfamily name
|
|
13
|
+
* @param {string[]} uniqueSubfamilies
|
|
14
|
+
* @param {Object[]} subfamiliesArray - Existing subfamilies array from the typeface
|
|
15
|
+
* @param {Object} preferredStyleRef - Existing preferred style reference
|
|
16
|
+
* @param {Object} newPreferredStyle - Candidate preferred style from the upload
|
|
17
|
+
* @param {Object} stylesObject - Existing typeface styles object
|
|
18
|
+
* @param {Object} client - Sanity client
|
|
19
|
+
* @param {Function} setStatus
|
|
20
|
+
* @param {Function} setError
|
|
21
|
+
*/
|
|
22
|
+
export const updateTypefaceDocument = async (
|
|
23
|
+
doc_id,
|
|
24
|
+
fontRefs,
|
|
25
|
+
variableRefs,
|
|
26
|
+
subfamilies,
|
|
27
|
+
uniqueSubfamilies,
|
|
28
|
+
subfamiliesArray,
|
|
29
|
+
preferredStyleRef,
|
|
30
|
+
newPreferredStyle,
|
|
31
|
+
stylesObject,
|
|
32
|
+
client,
|
|
33
|
+
setStatus,
|
|
34
|
+
setError,
|
|
35
|
+
) => {
|
|
36
|
+
console.log('Updating typeface document with new fonts:', { fontRefs, variableRefs, subfamilies, uniqueSubfamilies });
|
|
37
|
+
setStatus('Updating typeface references...');
|
|
38
|
+
|
|
39
|
+
// Use dot-path keys so .set() does not clobber sibling fields
|
|
40
|
+
// (styles.collections, styles.pairs, styles.free, styles.displayStyles)
|
|
41
|
+
let patch = {
|
|
42
|
+
'styles.fonts': stylesObject.fonts ? [...stylesObject.fonts, ...fontRefs] : [...fontRefs],
|
|
43
|
+
'styles.variableFont': stylesObject?.variableFont ? [...stylesObject.variableFont, ...variableRefs] : [...variableRefs],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
setStatus('Organising font subfamilies...');
|
|
47
|
+
subfamiliesArray = subfamiliesArray || [];
|
|
48
|
+
|
|
49
|
+
// Create any missing subfamily groups
|
|
50
|
+
uniqueSubfamilies.forEach(subfamilyName => {
|
|
51
|
+
if (!subfamiliesArray.find(sf => sf.title === subfamilyName)) {
|
|
52
|
+
subfamiliesArray.push({
|
|
53
|
+
title: subfamilyName,
|
|
54
|
+
_key: nanoid(),
|
|
55
|
+
_type: 'object',
|
|
56
|
+
fonts: [],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Associate fonts with their subfamily groups (skip VF fonts)
|
|
62
|
+
if (subfamiliesArray.length > 0) {
|
|
63
|
+
Object.entries(subfamilies).forEach(([id, subfamilyName]) => {
|
|
64
|
+
if (id.toLowerCase().includes('vf')) return;
|
|
65
|
+
|
|
66
|
+
const subfamilyIndex = subfamiliesArray.findIndex(sf => sf.title === subfamilyName);
|
|
67
|
+
if (subfamilyIndex !== -1) {
|
|
68
|
+
subfamiliesArray[subfamilyIndex].fonts.push({
|
|
69
|
+
_ref: id,
|
|
70
|
+
_key: nanoid(),
|
|
71
|
+
_type: 'reference',
|
|
72
|
+
_weak: true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Deduplicate references within each subfamily
|
|
78
|
+
subfamiliesArray = subfamiliesArray.map(subfamily => ({
|
|
79
|
+
...subfamily,
|
|
80
|
+
fonts: subfamily.fonts.filter((font, index, self) =>
|
|
81
|
+
index === self.findIndex(f => f._ref === font._ref)
|
|
82
|
+
),
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
patch['styles.subfamilies'] = subfamiliesArray;
|
|
87
|
+
|
|
88
|
+
// Optionally update preferred style
|
|
89
|
+
await updatePreferredStyle(doc_id, preferredStyleRef, newPreferredStyle, patch, client);
|
|
90
|
+
|
|
91
|
+
console.log('doc_id: ', doc_id);
|
|
92
|
+
console.log('Typeface patch: ', patch);
|
|
93
|
+
console.log('New preferred style: ', newPreferredStyle);
|
|
94
|
+
console.log('SubfamiliesArray:', subfamiliesArray);
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
await client.patch(doc_id).set(patch).commit();
|
|
98
|
+
console.log(`Updated document: ${doc_id}`);
|
|
99
|
+
|
|
100
|
+
if (doc_id.startsWith('drafts.')) {
|
|
101
|
+
await updatePublishedDocument(doc_id, patch, client);
|
|
102
|
+
}
|
|
103
|
+
} catch (err) {
|
|
104
|
+
console.error('Error updating document:', err.message);
|
|
105
|
+
setStatus('Error updating typeface');
|
|
106
|
+
setError(true);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Sets preferredStyle on the patch only when currently empty.
|
|
112
|
+
* Does not overwrite an existing preferredStyle — the user's choice is sticky.
|
|
113
|
+
* @param {string} doc_id
|
|
114
|
+
* @param {Object} preferredStyleRef
|
|
115
|
+
* @param {Object} newPreferredStyle
|
|
116
|
+
* @param {Object} patch
|
|
117
|
+
* @param {Object} client
|
|
118
|
+
*/
|
|
119
|
+
const updatePreferredStyle = async (doc_id, preferredStyleRef, newPreferredStyle, patch, client) => {
|
|
120
|
+
const isCurrentlyEmpty = !preferredStyleRef?._ref || preferredStyleRef._ref === '' || preferredStyleRef._ref === null;
|
|
121
|
+
const hasCandidate = newPreferredStyle?._ref && newPreferredStyle._ref !== '';
|
|
122
|
+
|
|
123
|
+
if (isCurrentlyEmpty && hasCandidate) {
|
|
124
|
+
patch.preferredStyle = {
|
|
125
|
+
_type: 'reference',
|
|
126
|
+
_ref: newPreferredStyle._ref,
|
|
127
|
+
_weak: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Applies the same patch to the published document if it exists.
|
|
134
|
+
* @param {string} doc_id - Draft document ID
|
|
135
|
+
* @param {Object} patch
|
|
136
|
+
* @param {Object} client
|
|
137
|
+
*/
|
|
138
|
+
const updatePublishedDocument = async (doc_id, patch, client) => {
|
|
139
|
+
const publishedId = doc_id.replace('drafts.', '');
|
|
140
|
+
// Parameterized to prevent injection from any draft ID edge cases
|
|
141
|
+
const publishedDoc = await client.fetch(`*[_id == $publishedId]`, { publishedId }).then(res => res[0]);
|
|
142
|
+
|
|
143
|
+
if (publishedDoc) {
|
|
144
|
+
await client.patch(publishedId).set(patch).commit();
|
|
145
|
+
console.log(`Updated published document: ${publishedId}`);
|
|
146
|
+
} else {
|
|
147
|
+
console.log(`No published document found for ${publishedId}, skipping`);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Core batch upload orchestrator — uploads each format to Sanity, generates CSS and metadata, then creates or updates font documents
|
|
1
|
+
// Core batch upload orchestrator — uploads each format to Sanity, generates CSS and metadata, resolves existing documents, then creates or updates font documents
|
|
2
2
|
|
|
3
3
|
import { nanoid } from 'nanoid';
|
|
4
4
|
import generateCssFile from './generateCssFile';
|
|
@@ -14,6 +14,7 @@ import { parseVariableFontInstances } from './parseVariableFontInstances';
|
|
|
14
14
|
* @param {Object} stylesObject - Existing typeface styles object
|
|
15
15
|
* @param {Function} setStatus
|
|
16
16
|
* @param {Function} setError
|
|
17
|
+
* @param {boolean} preserveFileNames - Use original filenames for asset naming
|
|
17
18
|
* @returns {Promise<Object>} fontRefs, variableRefs, failedFiles
|
|
18
19
|
*/
|
|
19
20
|
export const uploadFontFiles = async (
|
|
@@ -24,6 +25,7 @@ export const uploadFontFiles = async (
|
|
|
24
25
|
stylesObject,
|
|
25
26
|
setStatus,
|
|
26
27
|
setError,
|
|
28
|
+
preserveFileNames = false,
|
|
27
29
|
) => {
|
|
28
30
|
let fontRefs = [];
|
|
29
31
|
let variableRefs = [];
|
|
@@ -47,11 +49,27 @@ export const uploadFontFiles = async (
|
|
|
47
49
|
const file = files[j];
|
|
48
50
|
const fileType = determineFileType(file);
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
// Use original filename for asset naming when preserveFileNames is enabled
|
|
53
|
+
const assetFilename = preserveFileNames && fontObject.originalFilename
|
|
54
|
+
? `${fontObject.originalFilename}.${fileType}`
|
|
55
|
+
: `${fontObject._id}.${fileType}`;
|
|
56
|
+
|
|
57
|
+
console.log(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Uploading font file: ${assetFilename}`);
|
|
58
|
+
setStatus(`[${i + 1}/${fontObjectKeys.length}][${j + 1}/${files.length}] Uploading font file: ${assetFilename}`);
|
|
52
59
|
|
|
53
60
|
try {
|
|
54
|
-
const baseAsset = await client.assets.upload('file', file, { filename:
|
|
61
|
+
const baseAsset = await client.assets.upload('file', file, { filename: assetFilename });
|
|
62
|
+
|
|
63
|
+
// Sanity deduplicates assets by SHA1 hash and reuses the existing asset doc,
|
|
64
|
+
// which means originalFilename may reflect a previous upload. Patch it to
|
|
65
|
+
// match the intended filename so downstream consumers see the correct name.
|
|
66
|
+
if (preserveFileNames && baseAsset.originalFilename !== assetFilename) {
|
|
67
|
+
try {
|
|
68
|
+
await client.patch(baseAsset._id).set({ originalFilename: assetFilename }).commit();
|
|
69
|
+
} catch (renameErr) {
|
|
70
|
+
console.warn('Could not rename asset — permissions may be restricted:', renameErr.message);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
55
73
|
|
|
56
74
|
newFileInput[fileType] = {
|
|
57
75
|
_type: 'file',
|
|
@@ -86,7 +104,7 @@ export const uploadFontFiles = async (
|
|
|
86
104
|
Object.assign(fontObject, metadata);
|
|
87
105
|
}
|
|
88
106
|
} catch (err) {
|
|
89
|
-
console.error('Error uploading font:
|
|
107
|
+
console.error('Error uploading font:', fontObject.title, err.message);
|
|
90
108
|
setStatus('Error uploading font: ' + err.message);
|
|
91
109
|
setError(true);
|
|
92
110
|
failedFiles.push({ name: file.name, fk: fontKit });
|
|
@@ -136,8 +154,87 @@ const determineFileType = (file) => {
|
|
|
136
154
|
return '';
|
|
137
155
|
};
|
|
138
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Resolves whether a font document already exists in Sanity, returning match details
|
|
159
|
+
* and a recommendation for how to proceed.
|
|
160
|
+
*
|
|
161
|
+
* Resolution strategies (in priority order):
|
|
162
|
+
* 1. Exact _id match or draft _id match or slug.current match
|
|
163
|
+
* 2. Content match by typefaceName + weightName + style + subfamily + variableFont
|
|
164
|
+
*
|
|
165
|
+
* @param {Object} font - The font object with _id, typefaceName, weightName, style, subfamily, variableFont
|
|
166
|
+
* @param {Object} client - Sanity client (parameterized queries only)
|
|
167
|
+
* @returns {Promise<{ exact: Object|null, candidates: Object[], recommendation: string }>}
|
|
168
|
+
*/
|
|
169
|
+
export const resolveExistingFont = async (font, client) => {
|
|
170
|
+
const result = { exact: null, candidates: [], recommendation: 'create' };
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
// Strategy 1: ID / slug match
|
|
174
|
+
const idMatches = await client.fetch(
|
|
175
|
+
`*[_type == 'font' && (_id == $id || _id == $draftId || slug.current == $id)]{
|
|
176
|
+
_id, title, weight, style, weightName, typefaceName, subfamily, variableFont,
|
|
177
|
+
fileInput, description, metaData, metrics, opentypeFeatures, characterSet,
|
|
178
|
+
scriptFileInput, variableInstanceReferences
|
|
179
|
+
}`,
|
|
180
|
+
{ id: font._id, draftId: `drafts.${font._id}` }
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
if (idMatches.length > 0) {
|
|
184
|
+
result.exact = idMatches[0];
|
|
185
|
+
result.recommendation = 'use-exact';
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Strategy 2: Content match (only when ID query returns nothing)
|
|
190
|
+
const subfamily = font.subfamily || '';
|
|
191
|
+
const contentMatches = await client.fetch(
|
|
192
|
+
`*[_type == 'font'
|
|
193
|
+
&& lower(typefaceName) == lower($typefaceName)
|
|
194
|
+
&& lower(weightName) == lower($weightName)
|
|
195
|
+
&& lower(style) == lower($style)
|
|
196
|
+
&& (variableFont == $variableFont || (!defined(variableFont) && $variableFont == false))
|
|
197
|
+
&& (
|
|
198
|
+
lower(coalesce(subfamily, '')) == lower($subfamily)
|
|
199
|
+
|| (lower(coalesce(subfamily, '')) in ['', 'regular'] && lower($subfamily) in ['', 'regular'])
|
|
200
|
+
)
|
|
201
|
+
]{
|
|
202
|
+
_id, title, weight, style, weightName, typefaceName, subfamily, variableFont,
|
|
203
|
+
fileInput, description, metaData, metrics, opentypeFeatures, characterSet,
|
|
204
|
+
scriptFileInput, variableInstanceReferences
|
|
205
|
+
}`,
|
|
206
|
+
{
|
|
207
|
+
typefaceName: font.typefaceName,
|
|
208
|
+
weightName: font.weightName || '',
|
|
209
|
+
style: font.style || 'Regular',
|
|
210
|
+
variableFont: font.variableFont || false,
|
|
211
|
+
subfamily: subfamily === '' ? 'regular' : subfamily,
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
if (contentMatches.length === 1) {
|
|
216
|
+
result.candidates = contentMatches;
|
|
217
|
+
result.recommendation = 'use-candidate';
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (contentMatches.length > 1) {
|
|
222
|
+
result.candidates = contentMatches;
|
|
223
|
+
result.recommendation = 'ambiguous';
|
|
224
|
+
console.warn(`Ambiguous font match for "${font.title}" — ${contentMatches.length} candidates found:`,
|
|
225
|
+
contentMatches.map(c => c._id));
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
} catch (err) {
|
|
229
|
+
console.error('Error resolving existing font:', font._id, err.message);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return result;
|
|
233
|
+
};
|
|
234
|
+
|
|
139
235
|
/**
|
|
140
236
|
* Creates a new font document or updates an existing one, returning its reference.
|
|
237
|
+
* Uses resolveExistingFont to determine whether to create or update.
|
|
141
238
|
* @param {Object} font
|
|
142
239
|
* @param {Object} client
|
|
143
240
|
* @param {Function} setError
|
|
@@ -145,21 +242,7 @@ const determineFileType = (file) => {
|
|
|
145
242
|
*/
|
|
146
243
|
const createOrUpdateFontDocument = async (font, client, setError) => {
|
|
147
244
|
try {
|
|
148
|
-
|
|
149
|
-
const existingFont = await client.fetch(
|
|
150
|
-
`*[_type == 'font' && _id == $fontId]{
|
|
151
|
-
fileInput,
|
|
152
|
-
description,
|
|
153
|
-
metaData,
|
|
154
|
-
metrics,
|
|
155
|
-
opentypeFeatures,
|
|
156
|
-
characterSet,
|
|
157
|
-
subfamily,
|
|
158
|
-
scriptFileInput,
|
|
159
|
-
variableInstanceReferences
|
|
160
|
-
}`,
|
|
161
|
-
{ fontId: font._id }
|
|
162
|
-
).then(res => res[0]);
|
|
245
|
+
const { exact, candidates, recommendation } = await resolveExistingFont(font, client);
|
|
163
246
|
|
|
164
247
|
const { files, fontKit } = font;
|
|
165
248
|
delete font.files;
|
|
@@ -175,9 +258,15 @@ const createOrUpdateFontDocument = async (font, client, setError) => {
|
|
|
175
258
|
}
|
|
176
259
|
|
|
177
260
|
let fontResponse;
|
|
178
|
-
if (
|
|
179
|
-
fontResponse = await updateExistingFont(font,
|
|
261
|
+
if (recommendation === 'use-exact' && exact) {
|
|
262
|
+
fontResponse = await updateExistingFont(font, exact, client);
|
|
263
|
+
} else if (recommendation === 'use-candidate' && candidates.length === 1) {
|
|
264
|
+
// Reassign font._id to match the existing document so inbound references resolve
|
|
265
|
+
console.log(`Content-match: reassigning "${font._id}" → "${candidates[0]._id}"`);
|
|
266
|
+
font._id = candidates[0]._id;
|
|
267
|
+
fontResponse = await updateExistingFont(font, candidates[0], client);
|
|
180
268
|
} else {
|
|
269
|
+
// 'ambiguous' or 'create' — create a new document
|
|
181
270
|
fontResponse = await createNewFont(font, client);
|
|
182
271
|
}
|
|
183
272
|
|
|
@@ -188,7 +277,7 @@ const createOrUpdateFontDocument = async (font, client, setError) => {
|
|
|
188
277
|
_weak: true,
|
|
189
278
|
};
|
|
190
279
|
} catch (e) {
|
|
191
|
-
console.error('Error creating font:
|
|
280
|
+
console.error('Error creating font:', font.title, font.subfamily, e);
|
|
192
281
|
setError(true);
|
|
193
282
|
return null;
|
|
194
283
|
}
|
|
@@ -231,7 +320,7 @@ const updateExistingFont = async (font, existingFont, client) => {
|
|
|
231
320
|
font.variableInstanceReferences = existingFont.variableInstanceReferences;
|
|
232
321
|
}
|
|
233
322
|
|
|
234
|
-
console.log('Updating existing font:
|
|
323
|
+
console.log('Updating existing font:', font._id, font.title);
|
|
235
324
|
|
|
236
325
|
const patchObject = {
|
|
237
326
|
fileInput: font.fileInput,
|
|
@@ -253,7 +342,7 @@ const updateExistingFont = async (font, existingFont, client) => {
|
|
|
253
342
|
* @returns {Promise<Object>}
|
|
254
343
|
*/
|
|
255
344
|
const createNewFont = async (font, client) => {
|
|
256
|
-
console.log('Creating new font:
|
|
345
|
+
console.log('Creating new font:', font._id, font.title);
|
|
257
346
|
if (font.metaData) cleanMetadataValues(font);
|
|
258
347
|
|
|
259
348
|
const newDocument = {
|
|
@@ -276,7 +365,7 @@ const cleanMetadataValues = (font) => {
|
|
|
276
365
|
if (font.metaData[key] == null) {
|
|
277
366
|
font.metaData[key] = '';
|
|
278
367
|
} else {
|
|
279
|
-
font.metaData[key] = font.metaData[key].replace(/[
|
|
368
|
+
font.metaData[key] = font.metaData[key].replace(/[\x00-\x1f]/g, '');
|
|
280
369
|
}
|
|
281
370
|
});
|
|
282
371
|
};
|
package/src/utils/utils.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
// Script variant constants (SCRIPTS, SCRIPTS_OBJECT) and HtmlDescription component for the supported script list
|
|
2
|
-
|
|
3
|
-
import React from 'react'
|
|
4
|
-
|
|
5
|
-
/** Renders children as-is; used for rich-text fields that return HTML strings */
|
|
6
|
-
export const HtmlDescription = ({ children }) => {
|
|
7
|
-
return children || ''
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/** Script variants available for this studio instance — comma-separated SANITY_STUDIO_SCRIPTS env var */
|
|
11
|
-
export const SCRIPTS = (process.env.SANITY_STUDIO_SCRIPTS || '').split(',').map((script) => script.trim()).filter(Boolean);
|
|
12
|
-
|
|
13
|
-
/** SCRIPTS as Sanity select option objects */
|
|
14
|
-
export const SCRIPTS_OBJECT = SCRIPTS.map((script) => {
|
|
15
|
-
return {title: script[0].toUpperCase() + script.slice(1), value: script}
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
/** Discount requirement types — comma-separated SANITY_STUDIO_DISCOUNT_REQ_TYPES env var */
|
|
19
|
-
export const DISCOUNT_REQUIREMENT_TYPES = (process.env.SANITY_STUDIO_DISCOUNT_REQ_TYPES || '').split(',').map((type) => type.trim()).filter(Boolean);
|
|
20
|
-
|
|
21
|
-
/** DISCOUNT_REQUIREMENT_TYPES as Sanity select option objects */
|
|
22
|
-
export const DISCOUNT_REQUIREMENT_TYPES_OBJECT = DISCOUNT_REQUIREMENT_TYPES.map((type) => {
|
|
23
|
-
return {title: type[0].toUpperCase() + type.slice(1), value: type}
|
|
24
|
-
});
|
|
1
|
+
// Script variant constants (SCRIPTS, SCRIPTS_OBJECT) and HtmlDescription component for the supported script list
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
/** Renders children as-is; used for rich-text fields that return HTML strings */
|
|
6
|
+
export const HtmlDescription = ({ children }) => {
|
|
7
|
+
return children || ''
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Script variants available for this studio instance — comma-separated SANITY_STUDIO_SCRIPTS env var */
|
|
11
|
+
export const SCRIPTS = (process.env.SANITY_STUDIO_SCRIPTS || '').split(',').map((script) => script.trim()).filter(Boolean);
|
|
12
|
+
|
|
13
|
+
/** SCRIPTS as Sanity select option objects */
|
|
14
|
+
export const SCRIPTS_OBJECT = SCRIPTS.map((script) => {
|
|
15
|
+
return {title: script[0].toUpperCase() + script.slice(1), value: script}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/** Discount requirement types — comma-separated SANITY_STUDIO_DISCOUNT_REQ_TYPES env var */
|
|
19
|
+
export const DISCOUNT_REQUIREMENT_TYPES = (process.env.SANITY_STUDIO_DISCOUNT_REQ_TYPES || '').split(',').map((type) => type.trim()).filter(Boolean);
|
|
20
|
+
|
|
21
|
+
/** DISCOUNT_REQUIREMENT_TYPES as Sanity select option objects */
|
|
22
|
+
export const DISCOUNT_REQUIREMENT_TYPES_OBJECT = DISCOUNT_REQUIREMENT_TYPES.map((type) => {
|
|
23
|
+
return {title: type[0].toUpperCase() + type.slice(1), value: type}
|
|
24
|
+
});
|