@teleporthq/teleport-plugin-next-data-source 0.43.55 → 0.43.59
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/__tests__/array-mapper-cache-wiring.test.ts +425 -0
- package/__tests__/data-source-cache-wrapping.test.ts +152 -0
- package/__tests__/ecommerce-product-allow-backorders.test.ts +156 -0
- package/__tests__/ecommerce-product-model.test.ts +179 -0
- package/__tests__/ecommerce-product-storefront-tax.test.ts +118 -0
- package/dist/cjs/cache/ast.d.ts +71 -0
- package/dist/cjs/cache/ast.d.ts.map +1 -0
- package/dist/cjs/cache/ast.js +229 -0
- package/dist/cjs/cache/ast.js.map +1 -0
- package/dist/cjs/cache/config.d.ts +22 -0
- package/dist/cjs/cache/config.d.ts.map +1 -0
- package/dist/cjs/cache/config.js +67 -0
- package/dist/cjs/cache/config.js.map +1 -0
- package/dist/cjs/cache/types.d.ts +31 -0
- package/dist/cjs/cache/types.d.ts.map +1 -0
- package/dist/cjs/cache/types.js +3 -0
- package/dist/cjs/cache/types.js.map +1 -0
- package/dist/cjs/data-source-fetchers.d.ts +5 -3
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +51 -8
- package/dist/cjs/data-source-fetchers.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts +2 -2
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +3 -2
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +14 -9
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/loading-state.d.ts.map +1 -1
- package/dist/cjs/loading-state.js +74 -2
- package/dist/cjs/loading-state.js.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +204 -64
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts +27 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.js +8 -3
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
- package/dist/cjs/transformations/index.d.ts +14 -2
- package/dist/cjs/transformations/index.d.ts.map +1 -1
- package/dist/cjs/transformations/index.js +37 -3
- package/dist/cjs/transformations/index.js.map +1 -1
- package/dist/cjs/transformations/shared-utils.d.ts.map +1 -1
- package/dist/cjs/transformations/shared-utils.js +1 -1
- package/dist/cjs/transformations/shared-utils.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +4 -3
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +6 -4
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/cache/ast.d.ts +71 -0
- package/dist/esm/cache/ast.d.ts.map +1 -0
- package/dist/esm/cache/ast.js +196 -0
- package/dist/esm/cache/ast.js.map +1 -0
- package/dist/esm/cache/config.d.ts +22 -0
- package/dist/esm/cache/config.d.ts.map +1 -0
- package/dist/esm/cache/config.js +62 -0
- package/dist/esm/cache/config.js.map +1 -0
- package/dist/esm/cache/types.d.ts +31 -0
- package/dist/esm/cache/types.d.ts.map +1 -0
- package/dist/esm/cache/types.js +2 -0
- package/dist/esm/cache/types.js.map +1 -0
- package/dist/esm/data-source-fetchers.d.ts +5 -3
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +51 -8
- package/dist/esm/data-source-fetchers.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts +2 -2
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +3 -2
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +12 -8
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/loading-state.d.ts.map +1 -1
- package/dist/esm/loading-state.js +74 -2
- package/dist/esm/loading-state.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +205 -65
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/transformations/ecommerce-product.d.ts +27 -1
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/esm/transformations/ecommerce-product.js +8 -3
- package/dist/esm/transformations/ecommerce-product.js.map +1 -1
- package/dist/esm/transformations/index.d.ts +14 -2
- package/dist/esm/transformations/index.d.ts.map +1 -1
- package/dist/esm/transformations/index.js +36 -3
- package/dist/esm/transformations/index.js.map +1 -1
- package/dist/esm/transformations/shared-utils.d.ts.map +1 -1
- package/dist/esm/transformations/shared-utils.js +1 -1
- package/dist/esm/transformations/shared-utils.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +4 -3
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +6 -4
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/cache/ast.ts +265 -0
- package/src/cache/config.ts +79 -0
- package/src/cache/types.ts +31 -0
- package/src/data-source-fetchers.ts +66 -12
- package/src/fetchers/teleport.ts +3 -3
- package/src/index.ts +10 -4
- package/src/loading-state.ts +65 -2
- package/src/pagination-plugin.ts +315 -116
- package/src/transformations/ecommerce-product.ts +161 -5
- package/src/transformations/index.ts +43 -4
- package/src/transformations/shared-utils.ts +14 -0
- package/src/utils.ts +10 -5
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
import type { UIDLEcommerceCategory } from '@teleporthq/teleport-types'
|
|
2
|
+
import { StorefrontTax } from '@teleporthq/teleport-shared'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Everything the product transform needs baked in at export time, beyond the
|
|
6
|
+
* row itself. An options OBJECT rather than positional parameters: the list has
|
|
7
|
+
* already grown twice and every caller in the chain has to forward it verbatim.
|
|
8
|
+
*/
|
|
9
|
+
export interface EcommerceProductTransformOptions {
|
|
10
|
+
/** Category taxonomy — lives only in the UIDL, there is no DB table for it. */
|
|
11
|
+
categories?: UIDLEcommerceCategory[]
|
|
12
|
+
/**
|
|
13
|
+
* Percentage to ADD on top of every stored (NET) price for DISPLAY, or 0.
|
|
14
|
+
* Resolved by the caller through `StorefrontTax.resolveStorefrontTaxRate`.
|
|
15
|
+
*/
|
|
16
|
+
storefrontTaxRate?: number
|
|
17
|
+
/**
|
|
18
|
+
* TRUE when stock levels must never gate purchasability in the storefront:
|
|
19
|
+
* the merchant disabled stock management entirely OR opted into backorders
|
|
20
|
+
* (`stockManagementConfig.allowBackorders`). Resolved by the caller through
|
|
21
|
+
* `buildProductTransformOptions`. Baked into the generated transform as
|
|
22
|
+
* `ALLOW_BACKORDERS`: `outOfStock` then always reads 'false' and every
|
|
23
|
+
* EXISTING variant combination counts as in stock — a combination that does
|
|
24
|
+
* not exist at all stays unavailable (there is nothing to backorder).
|
|
25
|
+
* Mirrors the GUI rule in
|
|
26
|
+
* `apps/gui/app/project-page/features/e-commerce/utils/product-variants.ts`.
|
|
27
|
+
*/
|
|
28
|
+
allowBackorders?: boolean
|
|
29
|
+
}
|
|
2
30
|
|
|
3
31
|
/**
|
|
4
32
|
* Flattens the nested category tree (`ecommerceSettings.categories`, baked at
|
|
@@ -47,11 +75,22 @@ const flattenCategoriesById = (
|
|
|
47
75
|
* that UIDL components expect.
|
|
48
76
|
*/
|
|
49
77
|
export const generateEcommerceProductTransformationCode = (
|
|
50
|
-
|
|
78
|
+
options: EcommerceProductTransformOptions = {}
|
|
51
79
|
): string => {
|
|
52
|
-
const categoriesByIdJson = JSON.stringify(flattenCategoriesById(categories))
|
|
80
|
+
const categoriesByIdJson = JSON.stringify(flattenCategoriesById(options.categories))
|
|
81
|
+
const taxHelperCode = StorefrontTax.generateStorefrontTaxHelperCode(
|
|
82
|
+
options.storefrontTaxRate ?? 0
|
|
83
|
+
)
|
|
84
|
+
const allowBackordersLiteral = options.allowBackorders === true ? 'true' : 'false'
|
|
53
85
|
|
|
54
86
|
return `
|
|
87
|
+
${taxHelperCode}
|
|
88
|
+
|
|
89
|
+
// Baked from the merchant's stock settings: TRUE when stock never blocks a
|
|
90
|
+
// purchase (stock management off OR backorders allowed). Regenerating the
|
|
91
|
+
// project refreshes it — do not hand-edit. See EcommerceProductTransformOptions.
|
|
92
|
+
var ALLOW_BACKORDERS = ${allowBackordersLiteral}
|
|
93
|
+
|
|
55
94
|
// Category taxonomy (id -> {name, slug, translations}), baked in at export
|
|
56
95
|
// time — see flattenCategoriesById in ecommerce-product.ts. Empty when the
|
|
57
96
|
// store has no category taxonomy, or products.category_ids simply resolves
|
|
@@ -69,6 +108,50 @@ function resolveCategoryName(categoryInfo, currentLang, mainLang) {
|
|
|
69
108
|
return categoryInfo.name
|
|
70
109
|
}
|
|
71
110
|
|
|
111
|
+
// The merchant's "Show 3D model by default" toggle, as every backend may hand
|
|
112
|
+
// it back: a real boolean, 1/0, 'true'/'false'/'t'/'f' strings — and NULL /
|
|
113
|
+
// absent / '' as ON, because a product that has a model shows it unless the
|
|
114
|
+
// merchant switched it off. MUST mirror normalizeShowModelFlag in the GUI's
|
|
115
|
+
// features/e-commerce/constants/product-model.ts.
|
|
116
|
+
function normalizeShowModelFlag(raw) {
|
|
117
|
+
if (raw === null || raw === undefined || raw === '') return true
|
|
118
|
+
if (typeof raw === 'boolean') return raw
|
|
119
|
+
if (typeof raw === 'number') return raw !== 0
|
|
120
|
+
if (typeof raw === 'string') {
|
|
121
|
+
var token = raw.trim().toLowerCase()
|
|
122
|
+
return !(token === 'false' || token === 'f' || token === '0' || token === 'no' || token === 'off')
|
|
123
|
+
}
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// The product's media gallery in display order, HERO FIRST: the model when it
|
|
128
|
+
// is shown by default, otherwise the images with the model last. Each entry is
|
|
129
|
+
// { kind: 'image' | 'model', src, poster, thumbnail, alt }; 'thumbnail' is what
|
|
130
|
+
// a strip draws and is never empty for an entry that can appear in one. MUST
|
|
131
|
+
// mirror buildProductGalleryMedia in the GUI's
|
|
132
|
+
// features/e-commerce/utils/product-gallery-media.ts.
|
|
133
|
+
function buildProductGalleryMedia(params) {
|
|
134
|
+
var imageItems = []
|
|
135
|
+
for (var gi = 0; gi < params.images.length; gi++) {
|
|
136
|
+
imageItems.push({
|
|
137
|
+
kind: 'image',
|
|
138
|
+
src: params.images[gi],
|
|
139
|
+
poster: '',
|
|
140
|
+
thumbnail: params.images[gi],
|
|
141
|
+
alt: params.imageAlt || params.name,
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
if (!params.modelUrl) return imageItems
|
|
145
|
+
var modelItem = {
|
|
146
|
+
kind: 'model',
|
|
147
|
+
src: params.modelUrl,
|
|
148
|
+
poster: params.modelPoster || '',
|
|
149
|
+
thumbnail: params.modelPoster || params.mainImage || params.images[0] || '',
|
|
150
|
+
alt: params.name,
|
|
151
|
+
}
|
|
152
|
+
return params.showModel === 'true' ? [modelItem].concat(imageItems) : imageItems.concat([modelItem])
|
|
153
|
+
}
|
|
154
|
+
|
|
72
155
|
// ============================================================
|
|
73
156
|
// E-Commerce Product Transformation
|
|
74
157
|
// ============================================================
|
|
@@ -294,6 +377,36 @@ function buildEcommerceProduct(record, options) {
|
|
|
294
377
|
// Step 7: images = full resolved deduplicated array
|
|
295
378
|
var images = resolvedImages
|
|
296
379
|
|
|
380
|
+
// -------------------------------------------------------
|
|
381
|
+
// 3D model (one per product; GUI: constants/product-model.ts)
|
|
382
|
+
// -------------------------------------------------------
|
|
383
|
+
// model_url / model_poster_url hold an asset id OR a raw URL like image_url.
|
|
384
|
+
// A poster the merchant never got (capture failed, plan cap) falls back to
|
|
385
|
+
// the main image so <model-viewer> always has something to paint first.
|
|
386
|
+
var modelUrl = resolveMediaUrl(record.model_url, assetMap)
|
|
387
|
+
var modelPoster = modelUrl ? resolveMediaUrl(record.model_poster_url, assetMap) || mainImage : null
|
|
388
|
+
var hasModel = modelUrl ? 'true' : 'false'
|
|
389
|
+
// STRING gate ('true'/'false') like outOfStock: whether the model replaces the
|
|
390
|
+
// main image on the card / details hero. A product whose only media is the
|
|
391
|
+
// model always shows it. An absent field must read as the IMAGE branch, so the
|
|
392
|
+
// builders gate the viewer on = 'true' and the image on != 'true'.
|
|
393
|
+
var showModel =
|
|
394
|
+
modelUrl && (normalizeShowModelFlag(record.show_model_by_default) || images.length === 0)
|
|
395
|
+
? 'true'
|
|
396
|
+
: 'false'
|
|
397
|
+
var galleryMedia = buildProductGalleryMedia({
|
|
398
|
+
images: images,
|
|
399
|
+
mainImage: mainImage,
|
|
400
|
+
modelUrl: modelUrl,
|
|
401
|
+
modelPoster: modelPoster,
|
|
402
|
+
showModel: showModel,
|
|
403
|
+
imageAlt: imageAlt,
|
|
404
|
+
name: name,
|
|
405
|
+
})
|
|
406
|
+
// Everything that is not the hero — the details thumbnail strip. When the
|
|
407
|
+
// model is the hero the main image moves in here, so the modal stays reachable.
|
|
408
|
+
var galleryThumbnails = galleryMedia.slice(1)
|
|
409
|
+
|
|
297
410
|
// Timestamps
|
|
298
411
|
var rawCreatedAt = pickFirst(record.created_at, record.createdAt, record.created)
|
|
299
412
|
var createdAt = normalizeTimestamp(rawCreatedAt)
|
|
@@ -311,7 +424,9 @@ function buildEcommerceProduct(record, options) {
|
|
|
311
424
|
// string), leaving the Add to Cart button visible on out-of-stock
|
|
312
425
|
// products. NULL/NaN quantity → 'false' (treated as "unlimited
|
|
313
426
|
// stock", same as the upstream cart-availability rewriter).
|
|
314
|
-
|
|
427
|
+
// With ALLOW_BACKORDERS the merchant sells past zero (or does not track
|
|
428
|
+
// stock at all), so nothing is ever presented as out of stock.
|
|
429
|
+
var outOfStock = (!ALLOW_BACKORDERS && quantity !== null && !isNaN(quantity) && quantity <= 0) ? 'true' : 'false'
|
|
315
430
|
|
|
316
431
|
// Per-value availability + default-selection flags, derived from the
|
|
317
432
|
// purchasable combinations, so the storefront picker renders its
|
|
@@ -349,6 +464,32 @@ function buildEcommerceProduct(record, options) {
|
|
|
349
464
|
var hasInStockCombination = !!(firstVariant && isVariantInStock(firstVariant))
|
|
350
465
|
var defaultVariantId = hasInStockCombination ? firstVariant.id : ''
|
|
351
466
|
var defaultVariantPrice = firstVariant ? formatVariantPrice(firstVariant.price, price) : ''
|
|
467
|
+
// Gross (tax-inclusive) counterparts of the three price fields the STOREFRONT
|
|
468
|
+
// renders. \`price\` / \`defaultVariantPrice\` / \`variantsJson\` deliberately stay
|
|
469
|
+
// NET: the same transform feeds the merchant's admin panel, and every write
|
|
470
|
+
// path (add-to-cart, checkout, the invoice route) re-reads the raw row and
|
|
471
|
+
// must keep seeing the price the merchant typed. Only these three are safe to
|
|
472
|
+
// gross, because nothing writes them back.
|
|
473
|
+
//
|
|
474
|
+
// MUST mirror packages/renderer/src/utils/ecommerce-products.ts.
|
|
475
|
+
var displayPrice = grossMoney(price)
|
|
476
|
+
var defaultVariantDisplayPrice = firstVariant
|
|
477
|
+
? grossMoney(formatVariantPrice(firstVariant.price, price))
|
|
478
|
+
: ''
|
|
479
|
+
var variantsDisplay = []
|
|
480
|
+
for (var dv = 0; dv < variants.length; dv++) {
|
|
481
|
+
var dvRow = variants[dv]
|
|
482
|
+
variantsDisplay.push({
|
|
483
|
+
id: dvRow.id,
|
|
484
|
+
options: dvRow.options,
|
|
485
|
+
// A null variant price INHERITS the product price, so gross the RESOLVED
|
|
486
|
+
// value — otherwise an inheriting combination would fall back to the net
|
|
487
|
+
// base price in the picker while an overriding one showed gross.
|
|
488
|
+
price: applyStorefrontTax(dvRow.price == null ? price : dvRow.price),
|
|
489
|
+
quantity: dvRow.quantity,
|
|
490
|
+
image_url: dvRow.image_url,
|
|
491
|
+
})
|
|
492
|
+
}
|
|
352
493
|
// Can this product be added to the cart at all, before the shopper touches the
|
|
353
494
|
// picker? A STRING, same convention as outOfStock/requiresVariantSelection.
|
|
354
495
|
//
|
|
@@ -373,6 +514,7 @@ function buildEcommerceProduct(record, options) {
|
|
|
373
514
|
// combinations + axes from these JSON strings via getAttribute.
|
|
374
515
|
var variantOptionsJson = safeStringifyJson(variantOptions)
|
|
375
516
|
var variantsJson = safeStringifyJson(variants)
|
|
517
|
+
var variantsDisplayJson = safeStringifyJson(variantsDisplay)
|
|
376
518
|
|
|
377
519
|
return {
|
|
378
520
|
id: id,
|
|
@@ -409,6 +551,12 @@ function buildEcommerceProduct(record, options) {
|
|
|
409
551
|
image_url: mainImage,
|
|
410
552
|
galleryImages: galleryImages,
|
|
411
553
|
images: images,
|
|
554
|
+
modelUrl: modelUrl,
|
|
555
|
+
modelPoster: modelPoster,
|
|
556
|
+
hasModel: hasModel,
|
|
557
|
+
showModel: showModel,
|
|
558
|
+
galleryMedia: galleryMedia,
|
|
559
|
+
galleryThumbnails: galleryThumbnails,
|
|
412
560
|
createdAt: createdAt,
|
|
413
561
|
updatedAt: updatedAt,
|
|
414
562
|
created: created,
|
|
@@ -418,16 +566,24 @@ function buildEcommerceProduct(record, options) {
|
|
|
418
566
|
variants: variants,
|
|
419
567
|
variantOptionsJson: variantOptionsJson,
|
|
420
568
|
variantsJson: variantsJson,
|
|
569
|
+
variantsDisplayJson: variantsDisplayJson,
|
|
421
570
|
defaultVariantId: defaultVariantId,
|
|
422
571
|
defaultVariantPrice: defaultVariantPrice,
|
|
572
|
+
displayPrice: displayPrice,
|
|
573
|
+
defaultVariantDisplayPrice: defaultVariantDisplayPrice,
|
|
423
574
|
hasPurchasableVariant: hasPurchasableVariant,
|
|
424
575
|
}
|
|
425
576
|
}
|
|
426
577
|
|
|
427
578
|
// --- Variant availability helpers (mirror packages/renderer ecommerce-products) ---
|
|
428
|
-
// A variant is in stock when its quantity is null (unlimited) or > 0.
|
|
579
|
+
// A variant is in stock when its quantity is null (unlimited) or > 0. With
|
|
580
|
+
// ALLOW_BACKORDERS every EXISTING combination is purchasable regardless of its
|
|
581
|
+
// quantity — but a missing combination (null) is still not in stock: there is
|
|
582
|
+
// no variant row to backorder.
|
|
429
583
|
function isVariantInStock(v) {
|
|
430
|
-
|
|
584
|
+
if (!v) return false
|
|
585
|
+
if (ALLOW_BACKORDERS) return true
|
|
586
|
+
return v.quantity == null || v.quantity > 0
|
|
431
587
|
}
|
|
432
588
|
function variantCoversAllAxes(v, axisKeys) {
|
|
433
589
|
if (!v || !v.options) return false
|
|
@@ -1,7 +1,46 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GeneratorOptions } from '@teleporthq/teleport-types'
|
|
2
|
+
import { StorefrontTax } from '@teleporthq/teleport-shared'
|
|
2
3
|
import { generateSharedTransformationCode } from './shared-utils'
|
|
3
4
|
import { generateBlogPostTransformationCode } from './blog-post'
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
generateEcommerceProductTransformationCode,
|
|
7
|
+
type EcommerceProductTransformOptions,
|
|
8
|
+
} from './ecommerce-product'
|
|
9
|
+
|
|
10
|
+
export type { EcommerceProductTransformOptions }
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Stock never gates purchasability when the merchant disabled stock management
|
|
14
|
+
* entirely or opted into backorders. Mirrors the GUI rule in
|
|
15
|
+
* `apps/gui/app/project-page/features/e-commerce/utils/product-variants.ts`
|
|
16
|
+
* (`isVariantInStock`). A project without e-commerce settings keeps the
|
|
17
|
+
* strict default (stock gates apply) — same behaviour as before the flag.
|
|
18
|
+
*/
|
|
19
|
+
const resolveAllowBackorders = (
|
|
20
|
+
settings: GeneratorOptions['ecommerceSettings'] | undefined
|
|
21
|
+
): boolean => {
|
|
22
|
+
if (!settings) {
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
return !settings.stockManagement || settings.stockManagementConfig?.allowBackorders === true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Collects everything the product transform bakes in from the generator
|
|
30
|
+
* options, in ONE place so every fetcher-emitting call site stays in step.
|
|
31
|
+
*
|
|
32
|
+
* `storefrontTaxRate` is what turns a NET catalogue price into the price the
|
|
33
|
+
* shopper is quoted; `resolveStorefrontTaxRate` returns 0 (a no-op) for
|
|
34
|
+
* tax-inclusive or untaxed stores. `allowBackorders` is the effective
|
|
35
|
+
* "stock never blocks a purchase" flag — see `resolveAllowBackorders`.
|
|
36
|
+
*/
|
|
37
|
+
export const buildProductTransformOptions = (
|
|
38
|
+
options: Pick<GeneratorOptions, 'ecommerceSettings' | 'invoiceSettings'>
|
|
39
|
+
): EcommerceProductTransformOptions => ({
|
|
40
|
+
categories: options.ecommerceSettings?.categories,
|
|
41
|
+
storefrontTaxRate: StorefrontTax.resolveStorefrontTaxRate(options.invoiceSettings),
|
|
42
|
+
allowBackorders: resolveAllowBackorders(options.ecommerceSettings),
|
|
43
|
+
})
|
|
5
44
|
|
|
6
45
|
export type TransformationType = 'blog-post' | 'ecommerce-product' | null
|
|
7
46
|
|
|
@@ -51,7 +90,7 @@ export const detectTransformationType = (tableName: string): TransformationType
|
|
|
51
90
|
*/
|
|
52
91
|
export const getTransformationCode = (
|
|
53
92
|
tableName: string,
|
|
54
|
-
|
|
93
|
+
options: EcommerceProductTransformOptions = {}
|
|
55
94
|
): string => {
|
|
56
95
|
const type = detectTransformationType(tableName)
|
|
57
96
|
if (!type) {
|
|
@@ -64,7 +103,7 @@ export const getTransformationCode = (
|
|
|
64
103
|
case 'blog-post':
|
|
65
104
|
return shared + generateBlogPostTransformationCode()
|
|
66
105
|
case 'ecommerce-product':
|
|
67
|
-
return shared + generateEcommerceProductTransformationCode(
|
|
106
|
+
return shared + generateEcommerceProductTransformationCode(options)
|
|
68
107
|
default:
|
|
69
108
|
return ''
|
|
70
109
|
}
|
|
@@ -234,6 +234,20 @@ function resolveAssetUrl(value, assetMap) {
|
|
|
234
234
|
return value
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
// Whether an asset-id resolution produced a real URL. resolveAssetUrl returns
|
|
238
|
+
// an UNKNOWN id verbatim (asset deleted, teleport_assets not mirrored yet) and
|
|
239
|
+
// a bare 'TQ_...' must never reach a <model-viewer src> or a poster.
|
|
240
|
+
function isResolvedMediaUrl(value) {
|
|
241
|
+
return typeof value === 'string' && (isUrl(value) || value.charAt(0) === '/')
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// resolveAssetUrl for MEDIA that has no safe fallback: a value that did not
|
|
245
|
+
// resolve to a URL is reported as absent (null) instead of passed through.
|
|
246
|
+
function resolveMediaUrl(value, assetMap) {
|
|
247
|
+
var resolved = resolveAssetUrl(value, assetMap)
|
|
248
|
+
return isResolvedMediaUrl(resolved) ? resolved : null
|
|
249
|
+
}
|
|
250
|
+
|
|
237
251
|
function resolveAssetUrls(values, assetMap) {
|
|
238
252
|
if (!Array.isArray(values)) return []
|
|
239
253
|
return values
|
package/src/utils.ts
CHANGED
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
UIDLDataSource,
|
|
8
8
|
DataSourceType,
|
|
9
9
|
ChunkType,
|
|
10
|
-
UIDLEcommerceCategory,
|
|
11
10
|
} from '@teleporthq/teleport-types'
|
|
12
11
|
import * as types from '@babel/types'
|
|
13
12
|
import { ASTUtils } from '@teleporthq/teleport-plugin-common'
|
|
14
13
|
import { StringUtils } from '@teleporthq/teleport-shared'
|
|
15
14
|
import { generateDataSourceFetcherWithCore } from './data-source-fetchers'
|
|
15
|
+
import type { EcommerceProductTransformOptions } from './transformations'
|
|
16
16
|
import { DATA_SOURCE_ISR_REVALIDATE_SECONDS } from './isr'
|
|
17
17
|
|
|
18
18
|
const VALID_DATA_SOURCE_TYPES: DataSourceType[] = [
|
|
@@ -192,7 +192,7 @@ export const extractDataSourceIntoNextAPIFolder = (
|
|
|
192
192
|
dataSources: Record<string, UIDLDataSource>,
|
|
193
193
|
componentChunk: ChunkDefinition,
|
|
194
194
|
extractedResources: GeneratorOptions['extractedResources'],
|
|
195
|
-
|
|
195
|
+
transformOptions: EcommerceProductTransformOptions = {}
|
|
196
196
|
) => {
|
|
197
197
|
try {
|
|
198
198
|
// Validate node content structure
|
|
@@ -446,7 +446,12 @@ export default dataSourceModule.handler
|
|
|
446
446
|
// Generate fetcher code for API route (exports just the handler)
|
|
447
447
|
let fetcherCode: string
|
|
448
448
|
try {
|
|
449
|
-
fetcherCode = generateDataSourceFetcherWithCore(
|
|
449
|
+
fetcherCode = generateDataSourceFetcherWithCore(
|
|
450
|
+
dataSource,
|
|
451
|
+
tableName || '',
|
|
452
|
+
true,
|
|
453
|
+
transformOptions
|
|
454
|
+
)
|
|
450
455
|
} catch (error) {
|
|
451
456
|
return
|
|
452
457
|
}
|
|
@@ -896,7 +901,7 @@ export const extractDataSourceIntoGetStaticProps = (
|
|
|
896
901
|
dependencies: Record<string, any>,
|
|
897
902
|
dynamicRouteAttr?: string,
|
|
898
903
|
folderPath?: string[],
|
|
899
|
-
|
|
904
|
+
transformOptions: EcommerceProductTransformOptions = {}
|
|
900
905
|
): { success: boolean; chunk?: any } => {
|
|
901
906
|
try {
|
|
902
907
|
// Validate node content
|
|
@@ -1224,7 +1229,7 @@ export const extractDataSourceIntoGetStaticProps = (
|
|
|
1224
1229
|
dataSource,
|
|
1225
1230
|
tableName || '',
|
|
1226
1231
|
false,
|
|
1227
|
-
|
|
1232
|
+
transformOptions
|
|
1228
1233
|
)
|
|
1229
1234
|
} catch (error) {
|
|
1230
1235
|
// tslint:disable-next-line:no-console
|