gt-sanity 2.0.7 → 2.0.8

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 CHANGED
@@ -32,6 +32,9 @@ export default defineConfig({
32
32
  gtPlugin({
33
33
  sourceLocale: 'en',
34
34
  locales: ['es', 'fr'],
35
+ // Initialize translated document slugs with a unique locale suffix,
36
+ // e.g. "hello-world" -> "hello-world-es".
37
+ dedupeFields: [{ fields: [{ property: '$.slug' }] }],
35
38
  }),
36
39
  ],
37
40
  });
package/dist/index.d.mts CHANGED
@@ -86,6 +86,8 @@ export declare const BaseDocumentSerializer: (schemas: Schema) => {
86
86
 
87
87
  export declare const customSerializers: Partial<PortableTextHtmlComponents>;
88
88
 
89
+ declare type DedupeFields = FieldMatcher;
90
+
89
91
  export declare const defaultStopTypes: string[];
90
92
 
91
93
  declare interface Deserializer {
@@ -117,6 +119,14 @@ export declare type ExportForTranslation = (
117
119
  context: TranslationFunctionContext
118
120
  ) => Promise<GTSerializedDocument>;
119
121
 
122
+ declare type FieldMatcher = {
123
+ documentId?: string;
124
+ fields?: {
125
+ property: string;
126
+ type?: string;
127
+ }[];
128
+ };
129
+
120
130
  export declare const findLatestDraft: (
121
131
  documentId: string,
122
132
  client: SanityClient
@@ -153,6 +163,7 @@ export declare type GTPluginConfig = Omit<
153
163
  singletons?: string[];
154
164
  singletonMapping?: (sourceDocumentId: string, locale: string) => string;
155
165
  ignoreFields?: IgnoreFields[];
166
+ dedupeFields?: DedupeFields[];
156
167
  skipFields?: SkipFields[];
157
168
  languageField?: string;
158
169
  translateDocuments?: TranslateDocumentFilter[] | string[];
@@ -166,13 +177,7 @@ export declare type GTPluginConfig = Omit<
166
177
 
167
178
  declare type GTSerializedDocument = Omit<SerializedDocument, 'name'> & GTFile;
168
179
 
169
- declare type IgnoreFields = {
170
- documentId?: string;
171
- fields?: {
172
- property: string;
173
- type?: string;
174
- }[];
175
- };
180
+ declare type IgnoreFields = FieldMatcher;
176
181
 
177
182
  export declare type ImportTranslation = (
178
183
  documentInfo: GTFile,
@@ -220,13 +225,7 @@ export declare type SerializedDocument = {
220
225
  content: string;
221
226
  };
222
227
 
223
- declare type SkipFields = {
224
- documentId?: string;
225
- fields?: {
226
- property: string;
227
- type?: string;
228
- }[];
229
- };
228
+ declare type SkipFields = FieldMatcher;
230
229
 
231
230
  export declare const translateAction: DocumentActionComponent;
232
231
 
package/dist/index.d.ts CHANGED
@@ -86,6 +86,8 @@ export declare const BaseDocumentSerializer: (schemas: Schema) => {
86
86
 
87
87
  export declare const customSerializers: Partial<PortableTextHtmlComponents>;
88
88
 
89
+ declare type DedupeFields = FieldMatcher;
90
+
89
91
  export declare const defaultStopTypes: string[];
90
92
 
91
93
  declare interface Deserializer {
@@ -117,6 +119,14 @@ export declare type ExportForTranslation = (
117
119
  context: TranslationFunctionContext
118
120
  ) => Promise<GTSerializedDocument>;
119
121
 
122
+ declare type FieldMatcher = {
123
+ documentId?: string;
124
+ fields?: {
125
+ property: string;
126
+ type?: string;
127
+ }[];
128
+ };
129
+
120
130
  export declare const findLatestDraft: (
121
131
  documentId: string,
122
132
  client: SanityClient
@@ -153,6 +163,7 @@ export declare type GTPluginConfig = Omit<
153
163
  singletons?: string[];
154
164
  singletonMapping?: (sourceDocumentId: string, locale: string) => string;
155
165
  ignoreFields?: IgnoreFields[];
166
+ dedupeFields?: DedupeFields[];
156
167
  skipFields?: SkipFields[];
157
168
  languageField?: string;
158
169
  translateDocuments?: TranslateDocumentFilter[] | string[];
@@ -166,13 +177,7 @@ export declare type GTPluginConfig = Omit<
166
177
 
167
178
  declare type GTSerializedDocument = Omit<SerializedDocument, 'name'> & GTFile;
168
179
 
169
- declare type IgnoreFields = {
170
- documentId?: string;
171
- fields?: {
172
- property: string;
173
- type?: string;
174
- }[];
175
- };
180
+ declare type IgnoreFields = FieldMatcher;
176
181
 
177
182
  export declare type ImportTranslation = (
178
183
  documentInfo: GTFile,
@@ -220,13 +225,7 @@ export declare type SerializedDocument = {
220
225
  content: string;
221
226
  };
222
227
 
223
- declare type SkipFields = {
224
- documentId?: string;
225
- fields?: {
226
- property: string;
227
- type?: string;
228
- }[];
229
- };
228
+ declare type SkipFields = FieldMatcher;
230
229
 
231
230
  export declare const translateAction: DocumentActionComponent;
232
231
 
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ class GTConfig {
35
35
  singletons;
36
36
  singletonMapping;
37
37
  ignoreFields;
38
+ dedupeFields;
38
39
  skipFields;
39
40
  translateDocuments;
40
41
  additionalStopTypes;
@@ -42,8 +43,8 @@ class GTConfig {
42
43
  additionalDeserializers;
43
44
  additionalBlockDeserializers;
44
45
  static instance;
45
- constructor(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = {}, additionalBlockDeserializers = []) {
46
- this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers;
46
+ constructor(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = {}, additionalBlockDeserializers = []) {
47
+ this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.dedupeFields = dedupeFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers;
47
48
  }
48
49
  static getInstance() {
49
50
  return this.instance || (this.instance = new GTConfig(
@@ -57,13 +58,14 @@ class GTConfig {
57
58
  [],
58
59
  [],
59
60
  [],
61
+ [],
60
62
  {},
61
63
  {},
62
64
  []
63
65
  )), this.instance;
64
66
  }
65
- init(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = {}, additionalBlockDeserializers = []) {
66
- this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers;
67
+ init(secretsNamespace, languageField, sourceLocale, locales, singletons, singletonMapping, ignoreFields, dedupeFields, skipFields, translateDocuments, additionalStopTypes = [], additionalSerializers = {}, additionalDeserializers = {}, additionalBlockDeserializers = []) {
68
+ this.secretsNamespace = secretsNamespace, this.languageField = languageField, this.sourceLocale = sourceLocale, this.locales = locales, this.singletons = singletons, this.singletonMapping = singletonMapping, this.ignoreFields = ignoreFields, this.dedupeFields = dedupeFields, this.skipFields = skipFields, this.translateDocuments = translateDocuments, this.additionalStopTypes = additionalStopTypes, this.additionalSerializers = additionalSerializers, this.additionalDeserializers = additionalDeserializers, this.additionalBlockDeserializers = additionalBlockDeserializers;
67
69
  }
68
70
  getSecretsNamespace() {
69
71
  return this.secretsNamespace;
@@ -86,6 +88,9 @@ class GTConfig {
86
88
  getIgnoreFields() {
87
89
  return this.ignoreFields;
88
90
  }
91
+ getDedupeFields() {
92
+ return this.dedupeFields;
93
+ }
89
94
  getSkipFields() {
90
95
  return this.skipFields;
91
96
  }
@@ -629,14 +634,35 @@ function deleteMatchingFields(documentId, document2, fields) {
629
634
  for (const { parent, index } of arrayRemovals)
630
635
  parent.splice(index, 1);
631
636
  }
632
- function applyDocuments(documentId, sourceDocument, targetDocument, ignore, skip = []) {
637
+ function applyDocuments(documentId, sourceDocument, targetDocument, ignore, skip = [], dedupe = [], localeId) {
633
638
  const mergedDocument = JSON.parse(JSON.stringify(sourceDocument)), clonedTarget = JSON.parse(JSON.stringify(targetDocument));
634
639
  for (const [key, value] of Object.entries(clonedTarget))
635
640
  mergedDocument[key] = value;
636
641
  return forEachMatchingField(documentId, sourceDocument, ignore, (result) => {
637
642
  JSONPointer__default.default.set(mergedDocument, result.pointer, result.value);
643
+ }), forEachMatchingField(documentId, sourceDocument, dedupe, (result) => {
644
+ JSONPointer__default.default.set(
645
+ mergedDocument,
646
+ result.pointer,
647
+ dedupeFieldValue(result.value, localeId)
648
+ );
638
649
  }), deleteMatchingFields(documentId, mergedDocument, skip), mergedDocument;
639
650
  }
651
+ function dedupeFieldValue(value, localeId) {
652
+ return localeId ? typeof value == "string" ? appendLocaleSuffix(value, localeId) : value && typeof value == "object" && !Array.isArray(value) && typeof value.current == "string" ? {
653
+ ...value,
654
+ current: appendLocaleSuffix(value.current, localeId)
655
+ } : value : value;
656
+ }
657
+ function appendLocaleSuffix(value, localeId) {
658
+ if (!value) return value;
659
+ const suffix = createLocaleSuffix(localeId);
660
+ return !suffix || value.endsWith(suffix) ? value : `${value}${suffix}`;
661
+ }
662
+ function createLocaleSuffix(localeId) {
663
+ const normalized = localeId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
664
+ return normalized ? `-${normalized}` : "";
665
+ }
640
666
  function deserializeDocument(document2) {
641
667
  const deserializers = merge__default.default(
642
668
  { types: {} },
@@ -660,7 +686,8 @@ function serializeDocument(document2, schema2, baseLanguage) {
660
686
  pluginConfig.getAdditionalSerializers()
661
687
  ), docToSerialize = stripIgnoredFields(
662
688
  document2,
663
- pluginConfig.getIgnoreFields()
689
+ pluginConfig.getIgnoreFields(),
690
+ pluginConfig.getDedupeFields()
664
691
  );
665
692
  return BaseDocumentSerializer(schema2).serializeDocument(
666
693
  docToSerialize,
@@ -670,13 +697,14 @@ function serializeDocument(document2, schema2, baseLanguage) {
670
697
  serializers
671
698
  );
672
699
  }
673
- function stripIgnoredFields(document2, ignoreFields) {
674
- if (ignoreFields.length === 0) return document2;
700
+ function stripIgnoredFields(document2, ignoreFields, dedupeFields) {
701
+ const fieldsToStrip = [...ignoreFields, ...dedupeFields];
702
+ if (fieldsToStrip.length === 0) return document2;
675
703
  const strippedDoc = JSON.parse(JSON.stringify(document2));
676
704
  return deleteMatchingFields(
677
705
  document2._id.replace("drafts.", ""),
678
706
  strippedDoc,
679
- ignoreFields
707
+ fieldsToStrip
680
708
  ), strippedDoc;
681
709
  }
682
710
  async function uploadFiles(documents, secrets) {
@@ -788,7 +816,9 @@ async function createI18nDocAndPatchMetadata(sourceDocument, translatedDoc, loca
788
816
  sourceDocument,
789
817
  rest,
790
818
  pluginConfig.getIgnoreFields(),
791
- pluginConfig.getSkipFields()
819
+ pluginConfig.getSkipFields(),
820
+ pluginConfig.getDedupeFields(),
821
+ localeId
792
822
  ), isSingleton = pluginConfig.getSingletons().includes(sourceDocumentId);
793
823
  let createDocumentPromise;
794
824
  if (isSingleton) {
@@ -871,7 +901,7 @@ const getOrCreateTranslationMetadata = async (documentId, baseDocument, client,
871
901
  throw error;
872
902
  }
873
903
  }, SYSTEM_FIELDS = ["_id", "_rev", "_updatedAt", "language"], isSystemField = (field) => SYSTEM_FIELDS.includes(field);
874
- async function patchI18nDoc(sourceDocumentId, i18nDocId, sourceDocument, mergedDocument, translatedFields, client) {
904
+ async function patchI18nDoc(sourceDocumentId, i18nDocId, sourceDocument, mergedDocument, translatedFields, client, existingDocument) {
875
905
  const cleanedMerge = {};
876
906
  Object.entries(mergedDocument).forEach(([key, value]) => {
877
907
  key in translatedFields && //don't overwrite any existing system values on the i18n doc
@@ -887,8 +917,15 @@ async function patchI18nDoc(sourceDocumentId, i18nDocId, sourceDocument, mergedD
887
917
  cleanedMerge,
888
918
  pluginConfig.getIgnoreFields(),
889
919
  pluginConfig.getSkipFields()
890
- );
891
- await client.patch(i18nDocId, { set: appliedDocument }).commit();
920
+ ), dedupeFields = pluginConfig.getDedupeFields();
921
+ deleteMatchingFields(sourceDocumentId, appliedDocument, dedupeFields), existingDocument && forEachMatchingField(
922
+ sourceDocumentId,
923
+ existingDocument,
924
+ dedupeFields,
925
+ (result) => {
926
+ JSONPointer__default.default.set(appliedDocument, result.pointer, result.value);
927
+ }
928
+ ), await client.patch(i18nDocId, { set: appliedDocument }).commit();
892
929
  }
893
930
  const documentLevelPatch = async (docInfo, translatedFields, localeId, client, languageField = "language", mergeWithTargetLocale = !1) => {
894
931
  const baseLanguage = pluginConfig.getSourceLocale();
@@ -919,7 +956,8 @@ const documentLevelPatch = async (docInfo, translatedFields, localeId, client, l
919
956
  baseDoc,
920
957
  merged,
921
958
  translatedFields,
922
- client
959
+ client,
960
+ i18nDoc
923
961
  ) : await createI18nDocAndPatchMetadata(
924
962
  baseDoc,
925
963
  merged,
@@ -3697,6 +3735,7 @@ const WrapText = dt(ui.Box)`
3697
3735
  singletons,
3698
3736
  singletonMapping,
3699
3737
  ignoreFields,
3738
+ dedupeFields,
3700
3739
  skipFields,
3701
3740
  translateDocuments,
3702
3741
  secretsNamespace = SECRETS_NAMESPACE,
@@ -3717,6 +3756,7 @@ const WrapText = dt(ui.Box)`
3717
3756
  // singletons is a string array of singleton document ids
3718
3757
  singletonMapping || ((sourceDocumentId, locale) => `${sourceDocumentId}-${locale}`),
3719
3758
  ignoreFields || [],
3759
+ dedupeFields || [],
3720
3760
  skipFields || [],
3721
3761
  normalizedTranslateDocuments || [],
3722
3762
  additionalStopTypes,