@swell/apps-sdk 1.0.126 → 1.0.128

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/dist/index.mjs CHANGED
@@ -62,10 +62,151 @@ import { cloneDeep as cloneDeep3, reduce } from "lodash-es";
62
62
  import JSON52 from "json5";
63
63
 
64
64
  // src/resources.ts
65
- import { cloneDeep } from "lodash-es";
65
+ import { cloneDeep as cloneDeep2 } from "lodash-es";
66
66
 
67
67
  // src/liquid/utils.ts
68
68
  import { Drop } from "liquidjs";
69
+
70
+ // src/compatibility/shopify-objects/resource.ts
71
+ import { cloneDeep, noop } from "lodash-es";
72
+ var ShopifyResource = class _ShopifyResource {
73
+ props;
74
+ stringProp;
75
+ linkProps;
76
+ constructor(props, stringProp, linkProps) {
77
+ this.props = props;
78
+ this.stringProp = stringProp;
79
+ this.linkProps = linkProps;
80
+ return new Proxy(props, {
81
+ get(target, prop, receiver) {
82
+ const instance = target;
83
+ switch (prop) {
84
+ case "toJSON":
85
+ return props;
86
+ case "clone":
87
+ return () => {
88
+ return new _ShopifyResource(
89
+ cloneDeep(props),
90
+ cloneDeep(stringProp),
91
+ cloneDeep(linkProps)
92
+ );
93
+ };
94
+ case "toString": {
95
+ if (stringProp) {
96
+ return () => {
97
+ return props[stringProp];
98
+ };
99
+ }
100
+ break;
101
+ }
102
+ case Symbol.toPrimitive: {
103
+ return () => {
104
+ let prop2 = stringProp;
105
+ if (typeof prop2 === "number") {
106
+ prop2 = prop2.toString();
107
+ }
108
+ return this.get?.(instance, prop2 || "handle", receiver);
109
+ };
110
+ }
111
+ default:
112
+ break;
113
+ }
114
+ const value = instance[prop];
115
+ if (value instanceof DeferredShopifyResource) {
116
+ const promise = value.resolve().then(
117
+ (value2) => {
118
+ instance[prop] = value2;
119
+ return value2;
120
+ },
121
+ (err) => {
122
+ console.log(err);
123
+ instance[prop] = null;
124
+ return null;
125
+ }
126
+ );
127
+ instance[prop] = promise;
128
+ return promise;
129
+ }
130
+ return value;
131
+ },
132
+ getPrototypeOf() {
133
+ return _ShopifyResource.prototype;
134
+ }
135
+ });
136
+ }
137
+ valueOf() {
138
+ if (this.stringProp) {
139
+ return this.props[this.stringProp];
140
+ }
141
+ return this;
142
+ }
143
+ // For typescript
144
+ clone() {
145
+ return new _ShopifyResource(this.props);
146
+ }
147
+ };
148
+ var DeferredShopifyResource = class {
149
+ handler;
150
+ result;
151
+ constructor(handler) {
152
+ this.result = void 0;
153
+ this.handler = handler;
154
+ }
155
+ async resolve() {
156
+ if (this.handler !== noop) {
157
+ const handler = this.handler;
158
+ this.handler = noop;
159
+ this.result = Promise.resolve().then(handler).then((value) => {
160
+ this.result = value;
161
+ return value;
162
+ });
163
+ }
164
+ return this.result;
165
+ }
166
+ };
167
+ function defer(handler) {
168
+ return new DeferredShopifyResource(handler);
169
+ }
170
+ function isResolvable(asyncProp) {
171
+ return isObject(asyncProp) && (isLikePromise(asyncProp) || typeof asyncProp._resolve === "function");
172
+ }
173
+ function isStorefrontResource(resource) {
174
+ return isObject(resource) && typeof resource._resolve === "function";
175
+ }
176
+ async function resolveAsyncProp(asyncProp) {
177
+ if (Array.isArray(asyncProp)) {
178
+ return Promise.all(
179
+ asyncProp.map(
180
+ (prop) => isStorefrontResource(prop) ? prop._resolve() : prop
181
+ )
182
+ );
183
+ }
184
+ if (isStorefrontResource(asyncProp)) {
185
+ return asyncProp._resolve();
186
+ }
187
+ return asyncProp;
188
+ }
189
+ function handleDeferredProp(asyncProp, handler) {
190
+ return resolveAsyncProp(asyncProp).then((value) => {
191
+ if (Array.isArray(asyncProp) && Array.isArray(value)) {
192
+ return handler(...value.map((prop) => prop || {}));
193
+ }
194
+ if (isResolvable(value)) {
195
+ return handleDeferredProp(value, handler);
196
+ }
197
+ return handler(value || {});
198
+ }).catch((err) => {
199
+ console.log(err);
200
+ return null;
201
+ });
202
+ }
203
+ function deferWith(asyncProp, handler) {
204
+ return new DeferredShopifyResource(
205
+ () => handleDeferredProp(asyncProp, handler)
206
+ );
207
+ }
208
+
209
+ // src/liquid/utils.ts
69
210
  var ForloopDrop = class extends Drop {
70
211
  i;
71
212
  length;
@@ -232,18 +373,24 @@ async function jsonStringifyAsync(input, space = 0) {
232
373
  async function resolveAllKeys(value, references = /* @__PURE__ */ new WeakSet()) {
233
374
  await forEachKeyDeep(value, async (key, value2) => {
234
375
  if (!isObject(value2)) {
235
- return;
376
+ return true;
236
377
  }
237
378
  const val = value2[key];
238
379
  if (isLikePromise(val)) {
239
380
  value2[key] = await val;
240
381
  await resolveAllKeys(value2[key], references);
241
382
  } else if (isObject(val)) {
383
+ if (val instanceof DeferredShopifyResource) {
384
+ value2[key] = await val.resolve();
385
+ await resolveAllKeys(value2[key], references);
386
+ return true;
387
+ }
242
388
  if (references.has(val)) {
243
389
  return false;
244
390
  }
245
391
  references.add(val);
246
392
  }
393
+ return true;
247
394
  });
248
395
  }
249
396
  async function forEachKeyDeep(obj, fn) {
@@ -569,7 +716,7 @@ var SwellStorefrontCollection = class _SwellStorefrontCollection extends SwellSt
569
716
  this._getter
570
717
  );
571
718
  if (this._isResultResolved()) {
572
- cloned._result = cloneDeep(this._result);
719
+ cloned._result = cloneDeep2(this._result);
573
720
  }
574
721
  if (this._compatibilityProps) {
575
722
  cloned.setCompatibilityProps(this._compatibilityProps);
@@ -598,7 +745,7 @@ var SwellStorefrontCollection = class _SwellStorefrontCollection extends SwellSt
598
745
  }
599
746
  });
600
747
  if (this._isResultResolved()) {
601
- const result = cloneDeep(this._result);
748
+ const result = cloneDeep2(this._result);
602
749
  if (result) {
603
750
  const compatibilityProps = compatibilityGetter(result);
604
751
  Object.assign(cloned, result);
@@ -762,145 +909,6 @@ var SwellStorefrontPagination = class {
762
909
  }
763
910
  };
764
911
 
765
- // src/compatibility/shopify-objects/resource.ts
766
- import { cloneDeep as cloneDeep2, noop } from "lodash-es";
767
- var ShopifyResource = class _ShopifyResource {
768
- props;
769
- stringProp;
770
- linkProps;
771
- constructor(props, stringProp, linkProps) {
772
- this.props = props;
773
- this.stringProp = stringProp;
774
- this.linkProps = linkProps;
775
- return new Proxy(props, {
776
- get(target, prop, receiver) {
777
- const instance = target;
778
- switch (prop) {
779
- case "toJSON":
780
- return props;
781
- case "clone":
782
- return () => {
783
- return new _ShopifyResource(
784
- cloneDeep2(props),
785
- cloneDeep2(stringProp),
786
- cloneDeep2(linkProps)
787
- );
788
- };
789
- case "toString": {
790
- if (stringProp) {
791
- return () => {
792
- return props[stringProp];
793
- };
794
- }
795
- break;
796
- }
797
- case Symbol.toPrimitive: {
798
- return () => {
799
- let prop2 = stringProp;
800
- if (typeof prop2 === "number") {
801
- prop2 = prop2.toString();
802
- }
803
- return this.get?.(instance, prop2 || "handle", receiver);
804
- };
805
- }
806
- default:
807
- break;
808
- }
809
- const value = instance[prop];
810
- if (value instanceof DeferredShopifyResource) {
811
- const promise = value.resolve().then(
812
- (value2) => {
813
- instance[prop] = value2;
814
- return value2;
815
- },
816
- (err) => {
817
- console.log(err);
818
- instance[prop] = null;
819
- return null;
820
- }
821
- );
822
- instance[prop] = promise;
823
- return promise;
824
- }
825
- return value;
826
- },
827
- getPrototypeOf() {
828
- return _ShopifyResource.prototype;
829
- }
830
- });
831
- }
832
- valueOf() {
833
- if (this.stringProp) {
834
- return this.props[this.stringProp];
835
- }
836
- return this;
837
- }
838
- // For typescript
839
- clone() {
840
- return new _ShopifyResource(this.props);
841
- }
842
- };
843
- var DeferredShopifyResource = class {
844
- handler;
845
- result;
846
- constructor(handler) {
847
- this.result = void 0;
848
- this.handler = handler;
849
- }
850
- async resolve() {
851
- if (this.handler !== noop) {
852
- const handler = this.handler;
853
- this.handler = noop;
854
- this.result = Promise.resolve().then(handler).then((value) => {
855
- this.result = value;
856
- return value;
857
- });
858
- }
859
- return this.result;
860
- }
861
- };
862
- function defer(handler) {
863
- return new DeferredShopifyResource(handler);
864
- }
865
- function isResolvable(asyncProp) {
866
- return isObject(asyncProp) && (isLikePromise(asyncProp) || typeof asyncProp._resolve === "function");
867
- }
868
- function isStorefrontResource(resource) {
869
- return isObject(resource) && typeof resource._resolve === "function";
870
- }
871
- async function resolveAsyncProp(asyncProp) {
872
- if (Array.isArray(asyncProp)) {
873
- return Promise.all(
874
- asyncProp.map(
875
- (prop) => isStorefrontResource(prop) ? prop._resolve() : prop
876
- )
877
- );
878
- }
879
- if (isStorefrontResource(asyncProp)) {
880
- return asyncProp._resolve();
881
- }
882
- return asyncProp;
883
- }
884
- function handleDeferredProp(asyncProp, handler) {
885
- return resolveAsyncProp(asyncProp).then((value) => {
886
- if (Array.isArray(asyncProp) && Array.isArray(value)) {
887
- return handler(...value.map((prop) => prop || {}));
888
- }
889
- if (isResolvable(value)) {
890
- return handleDeferredProp(value, handler);
891
- }
892
- return handler(value || {});
893
- }).catch((err) => {
894
- console.log(err);
895
- return null;
896
- });
897
- }
898
- function deferWith(asyncProp, handler) {
899
- return new DeferredShopifyResource(
900
- () => handleDeferredProp(asyncProp, handler)
901
- );
902
- }
903
-
904
912
  // src/constants.ts
905
913
  var FILE_DATA_INCLUDE_QUERY = {
906
914
  url: "/:themes:configs/{id}/file/data",
@@ -13610,6 +13618,9 @@ function ShopifyArticle(instance, blog, blogCategory) {
13610
13618
  "moderated?": false
13611
13619
  });
13612
13620
  }
13621
+ function isLikeShopifyArticle(value) {
13622
+ return typeof value === "object" && value !== null && Object.hasOwn(value, "title") && Object.hasOwn(value, "content") && Object.hasOwn(value, "comments") && Object.hasOwn(value, "moderated?") && Object.hasOwn(value, "published_at") && Object.hasOwn(value, "excerpt_or_content");
13623
+ }
13613
13624
 
13614
13625
  // src/compatibility/shopify-objects/blog.ts
13615
13626
  function ShopifyBlog(instance, blogCategory) {
@@ -13907,7 +13918,7 @@ function ShopifyProduct(instance, product, depth = 0) {
13907
13918
  // not used
13908
13919
  first_available_variant: deferWith(product, (product2) => {
13909
13920
  const variant = getSelectedVariant(product2, {});
13910
- return ShopifyVariant(instance, variant || product2, product2, depth + 1);
13921
+ return variant ? ShopifyVariant(instance, variant, product2, depth + 1) : void 0;
13911
13922
  }),
13912
13923
  "gift_card?": deferWith(product, (product2) => product2.type === "giftcard"),
13913
13924
  handle: defer(() => product.slug),
@@ -14020,26 +14031,32 @@ function ShopifyProduct(instance, product, depth = 0) {
14020
14031
  product,
14021
14032
  (product2) => calculateAddOptionsPrice(product2, instance.swell.queryParams)
14022
14033
  ),
14023
- price_max: deferWith(
14024
- product,
14025
- (product2) => product2.variants?.results?.reduce(
14034
+ price_max: deferWith(product, (product2) => {
14035
+ if (!Array.isArray(product2.variants?.results)) {
14036
+ return product2.price;
14037
+ }
14038
+ return product2.variants.results.reduce(
14026
14039
  (max, variant) => Math.max(max, variant.price),
14027
14040
  0
14028
- )
14029
- ),
14030
- price_min: deferWith(
14031
- product,
14032
- (product2) => product2.variants?.results?.reduce(
14041
+ );
14042
+ }),
14043
+ price_min: deferWith(product, (product2) => {
14044
+ if (!Array.isArray(product2.variants?.results)) {
14045
+ return product2.price;
14046
+ }
14047
+ return product2.variants.results.reduce(
14033
14048
  (min, variant) => Math.min(min, variant.price),
14034
14049
  Infinity
14035
- )
14036
- ),
14037
- price_varies: deferWith(
14038
- product,
14039
- (product2) => product2.variants?.results?.some(
14050
+ );
14051
+ }),
14052
+ price_varies: deferWith(product, (product2) => {
14053
+ if (!Array.isArray(product2.variants?.results)) {
14054
+ return false;
14055
+ }
14056
+ return product2.variants.results.some(
14040
14057
  (variant) => variant.price !== product2.price
14041
- )
14042
- ),
14058
+ );
14059
+ }),
14043
14060
  published_at: deferWith(
14044
14061
  product,
14045
14062
  (product2) => product2.date_updated || product2.date_created
@@ -14075,11 +14092,8 @@ function ShopifyProduct(instance, product, depth = 0) {
14075
14092
  selected_or_first_available_variant: deferWith(
14076
14093
  product,
14077
14094
  (product2) => {
14078
- let variant = getSelectedVariant(product2, instance.swell.queryParams);
14079
- if (!variant) {
14080
- variant = product2;
14081
- }
14082
- return ShopifyVariant(instance, variant, product2, depth + 1);
14095
+ const variant = getSelectedVariant(product2, instance.swell.queryParams);
14096
+ return variant ? ShopifyVariant(instance, variant, product2, depth + 1) : void 0;
14083
14097
  }
14084
14098
  ),
14085
14099
  selected_selling_plan: void 0,
@@ -14100,6 +14114,9 @@ function ShopifyProduct(instance, product, depth = 0) {
14100
14114
  ).reverse();
14101
14115
  return variants;
14102
14116
  }),
14117
+ variants_count: deferWith(product, (product2) => {
14118
+ return product2.variants?.count || 0;
14119
+ }),
14103
14120
  vendor: void 0
14104
14121
  });
14105
14122
  }
@@ -14177,6 +14194,9 @@ function getSelectedVariantOptionValues(product, variant, queryParams) {
14177
14194
  }
14178
14195
  return values;
14179
14196
  }
14197
+ function isLikeShopifyProduct(value) {
14198
+ return typeof value === "object" && value !== null && Object.hasOwn(value, "variants") && Object.hasOwn(value, "gift_card?") && Object.hasOwn(value, "price_varies") && Object.hasOwn(value, "has_only_default_variant");
14199
+ }
14180
14200
 
14181
14201
  // src/compatibility/shopify-objects/line_item.ts
14182
14202
  function ShopifyLineItem(instance, item, cart, options = {}) {
@@ -17253,7 +17273,7 @@ var tags = {
17253
17273
  };
17254
17274
  function bindTags(liquidSwell) {
17255
17275
  Object.entries(tags).forEach(
17256
- ([tag, bind61]) => liquidSwell.registerTag(tag, bind61(liquidSwell))
17276
+ ([tag, bind62]) => liquidSwell.registerTag(tag, bind62(liquidSwell))
17257
17277
  );
17258
17278
  }
17259
17279
 
@@ -18075,8 +18095,86 @@ function bind59(_liquidSwell) {
18075
18095
  };
18076
18096
  }
18077
18097
 
18078
- // src/liquid/filters/inline_editable.ts
18098
+ // src/liquid/filters/shopify/structured_data.ts
18079
18099
  function bind60(_liquidSwell) {
18100
+ return async function filterStructuredData(input) {
18101
+ let value = input;
18102
+ if (value instanceof StorefrontResource) {
18103
+ value = await value.resolve();
18104
+ }
18105
+ await resolveAllKeys(value);
18106
+ if (isObject2(value)) {
18107
+ if (isLikeShopifyProduct(value)) {
18108
+ return JSON.stringify(
18109
+ value.variants_count > 0 ? convertToSchemaOrgProductGroup(value) : convertToSchemaOrgProduct(
18110
+ value,
18111
+ value
18112
+ )
18113
+ );
18114
+ }
18115
+ if (isLikeShopifyArticle(value)) {
18116
+ return JSON.stringify(convertToSchemaOrgArticle(value));
18117
+ }
18118
+ }
18119
+ return value;
18120
+ };
18121
+ }
18122
+ function convertToSchemaOrgArticle(article) {
18123
+ const schemaOrgArticle = {
18124
+ "@context": "https://schema.org",
18125
+ "@type": "Article",
18126
+ "@id": article.url,
18127
+ url: article.url,
18128
+ name: article.title,
18129
+ author: article.author,
18130
+ image: article.image?.src.url || void 0,
18131
+ abstract: article.excerpt,
18132
+ keywords: article.tags,
18133
+ articleBody: article.content,
18134
+ dateCreated: article.created_at,
18135
+ dateModified: article.updated_at,
18136
+ datePublished: article.published_at
18137
+ };
18138
+ return schemaOrgArticle;
18139
+ }
18140
+ function convertToSchemaOrgProduct(variant, product) {
18141
+ const schemaOrgProduct = {
18142
+ "@context": "https://schema.org",
18143
+ "@type": "Product",
18144
+ "@id": variant.url,
18145
+ url: variant.url,
18146
+ sku: variant.sku,
18147
+ name: variant.title,
18148
+ image: variant.featured_image?.src.url || void 0,
18149
+ keywords: product.tags,
18150
+ description: product.description,
18151
+ offers: {
18152
+ "@type": "Offer",
18153
+ availability: variant.available ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
18154
+ price: variant.price,
18155
+ priceCurrency: "USD"
18156
+ }
18157
+ };
18158
+ return schemaOrgProduct;
18159
+ }
18160
+ function convertToSchemaOrgProductGroup(product) {
18161
+ const schemaOrgProductGroup = {
18162
+ "@context": "https://schema.org",
18163
+ "@type": "ProductGroup",
18164
+ productGroupID: String(product.id),
18165
+ url: product.url,
18166
+ name: product.title,
18167
+ description: product.description,
18168
+ variesBy: product.options,
18169
+ hasVariant: product.variants.map(
18170
+ (variant) => convertToSchemaOrgProduct(variant, product)
18171
+ )
18172
+ };
18173
+ return schemaOrgProductGroup;
18174
+ }
18175
+
18176
+ // src/liquid/filters/inline_editable.ts
18177
+ function bind61(_liquidSwell) {
18080
18178
  return (value, key) => {
18081
18179
  if (typeof value === "object" && "value" in value) {
18082
18180
  value = value.value;
@@ -18135,8 +18233,9 @@ var filters = {
18135
18233
  payment_terms: bind57,
18136
18234
  placeholder_svg_tag: bind58,
18137
18235
  shopify_asset_url: bind59,
18236
+ structured_data: bind60,
18138
18237
  // Swell only
18139
- inline_editable: bind60
18238
+ inline_editable: bind61
18140
18239
  };
18141
18240
  function bindFilters(liquidSwell) {
18142
18241
  for (const [tag, handler] of Object.entries(filters)) {
@@ -18150,8 +18249,8 @@ function bindFilters(liquidSwell) {
18150
18249
  }
18151
18250
  }
18152
18251
  }
18153
- function bindWithResolvedProps(liquidSwell, bind61, resolve = []) {
18154
- const handler = bind61(liquidSwell);
18252
+ function bindWithResolvedProps(liquidSwell, bind62, resolve = []) {
18253
+ const handler = bind62(liquidSwell);
18155
18254
  if (!Array.isArray(resolve)) {
18156
18255
  return handler;
18157
18256
  }
@@ -19542,14 +19641,36 @@ var SwellTheme3 = class {
19542
19641
  "layouts",
19543
19642
  name
19544
19643
  );
19545
- if (templateConfig) {
19546
- const content = await this.renderThemeTemplate(
19547
- templateConfig.file_path,
19548
- data
19549
- );
19550
- return typeof content === "string" ? content : `<!-- invalid layout: ${name}--> {{ content_for_layout }}`;
19644
+ if (!templateConfig) {
19645
+ throw new Error(`Layout template not found: ${name}`);
19646
+ }
19647
+ let content = await this.renderThemeTemplate(
19648
+ templateConfig.file_path,
19649
+ data
19650
+ );
19651
+ if (typeof content !== "string") {
19652
+ return `<!-- invalid layout: ${name}--> {{ content_for_layout }}`;
19653
+ }
19654
+ if (!this.globals.request.is_editor) {
19655
+ let customCss = this.globals.settings.custom_css || "";
19656
+ if (Array.isArray(customCss)) {
19657
+ customCss = customCss.join("\n").trim();
19658
+ }
19659
+ if (customCss) {
19660
+ let pos = -1;
19661
+ let match = null;
19662
+ const regex = /<\/body\s*?>/gi;
19663
+ while ((match = regex.exec(content)) !== null) {
19664
+ pos = match.index;
19665
+ }
19666
+ if (pos !== -1) {
19667
+ content = `${content.slice(0, pos)}
19668
+ <style>${customCss}</style>
19669
+ ${content.slice(pos)}`;
19670
+ }
19671
+ }
19551
19672
  }
19552
- throw new Error(`Layout template not found: ${name}`);
19673
+ return content;
19553
19674
  }
19554
19675
  async renderPageTemplate(name, data, altTemplateId) {
19555
19676
  let templateConfig = null;