feat(apps-vtex): let the legacy PLP shrink its payload - #517
feat(apps-vtex): let the legacy PLP shrink its payload#517aka-sacci-ccr wants to merge 2 commits into
Conversation
`leanVariants` existed on `toProduct` and was already exposed by `productDetailsPage` (intelligent search), but the legacy PLP never plumbed it through — so a listing always paid the full nested `toProduct` per SKU. Measured on a real store (36 products, 26 variants each): ~9.6 MB per page, 96% of each product sitting in `isVariantOf.hasVariant`, because every variant carries the whole 48-entry payment ladder plus a copy of the parent `description` — none of which a listing card reads. Forwards `leanVariants`, `variantPropertyNames`, `variantIncludeImage` and `variantIncludeInventory` from `LegacyPLPOptions` into `toProduct`, and adds two new `ProductOptions` knobs: - `displayedVariantId(items) => itemId | undefined` — keep ONE variant on the full shape. `leanVariants` assumes the card renders the root sku, so `buildOfferVariant` empties `priceSpecification` on every entry; cards that instead pick a representative variant out of `hasVariant` (e.g. "cheapest in stock") read that variant's own offer for list price and installments, and render blank with the ladder emptied. This lets such a caller keep exactly the entry it displays. - `maxImages` — cap `image[]` to the first N entries. Note it truncates by POSITION: on a real listing the `vira` (hover) image sits at index 1 in only 5 of the 19 products that have one, index 2 in 12 of them, so a cap of 2 drops the hover image on most cards that use one. Callers selecting by name should keep the named entries instead. Every option is opt-in — undefined preserves current behaviour byte for byte. Originates as a local patch on a live site (miess); the fix belongs here, not in a per-site patch file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review follow-up on the two new toProduct options. maxImages was unvalidated. `slice(0, 0)` returns [] rather than null, so `maxImages: 0` slipped past the `?? [DEFAULT_IMAGE]` fallback and emitted `image: []` — breaking any card that reads `image[0].url`. A negative value dropped the LAST images instead of capping the first N, silently contradicting the documented contract. Both now fall back to keeping every image, matching how `undefined` behaves; documented as `>= 1`. Also replaced the IIFE around the hasVariant map with a hoisted const. The `level < 1 && options.leanVariants` guard preserves the original call count — the callback still runs exactly once per product, and not at all when leanVariants is off. Documented that maxImages is honoured by toProduct only: toProductShelf and toProductVariant build image[] through their own paths and ignore it, so passing it via the intelligent-search shelf is a silent no-op. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Review follow-up pushed in 6f49cb3 — one correctness fix plus a readability pass. No new tests (deliberate, per request). 🔴
|
Upstreams a patch that currently lives as a
patches/file on a live site (miess-01-tanstack). The fix belongs inblocks, not in a per-site patch.The problem
leanVariantsalready existed ontoProduct, andproductDetailsPage(intelligent search) already exposed it — but the legacy PLP never plumbed it through. So a listing always paid a full nestedtoProductper SKU.Measured on a real store (36 products, 26 variants each): ~9.6 MB per page, 96% of each product sitting in
isVariantOf.hasVariant, because every variant carries the whole 48-entry payment ladder plus a copy of the parentdescription— none of which a listing card reads.What changed
LegacyPLPOptionsnow forwardsleanVariants,variantPropertyNames,variantIncludeImageandvariantIncludeInventoryintotoProduct, andProductOptionsgains two knobs (and is nowexported):displayedVariantId?: (items) => string | undefined— keep ONE variant on the fulltoProductshape.Why it's needed:
leanVariantsassumes the card renders the root sku, sobuildOfferVariantemptiespriceSpecificationon every entry. Cards that instead pick a representative variant out ofhasVariant(e.g. "cheapest in stock") read that variant's own offer for list price and installments — with the ladder emptied, those render blank. This lets such a caller keep exactly the one entry it displays.maxImages?: number— capimage[]to the first N entries. Listings render at most a couple of images per card, while the Catalog API returns every asset on the SKU (3 on average, up to 5 measured).vira(hover) image sits at index 1 in only 5 of the 19 products that have one — index 2 in 12 of them, index 3 in 2. A cap of 2 therefore drops the hover image on most cards that use one. Callers that select images by name should keep the named entries instead of using this option. This is documented on the option itself, not just here.Compatibility
Every option is opt-in;
undefinedpreserves current behaviour byte for byte. No existing caller passes any of them.Verification
bun run typecheck— cleanbun run test— 268 tests, 25 files, all passingbiome checkon both touched files — back to the same 6 pre-existing diagnostics asmain(these two files are tab-indented whilebiome.jsonformats with spaces; that drift predates this PR)Left out deliberately
legacyProductListingPageis a CMS-exposed loader (vtex/loaders/legacy/legacyProductListingPage, schemas24). Runningbun run generate:schemasto pick up the new props produced 2230 lines of diff — almost all of it pre-existing drift unrelated to this change:schemas.gen.tsis stale againstsrcand is missing whole loaders (e.g. the shelf'scompleteVariants,orderFormId/itemIndex,freeShippingTarget). I reverted the file rather than bury this change in that churn.Consequence: the new props don't show up in the admin schema. That mirrors the site patch, which didn't touch
schemas.gen.tseither, and the load-bearing one (displayedVariantId) is a function, so it isn't serializable regardless. Regeneratingschemas.gen.tsdeserves its own PR.🤖 Generated with Claude Code
Summary by cubic
Legacy PLP now forwards
leanVariantsand related options intotoProduct, adding two optional knobs to shrink listing payloads without breaking existing behavior.LegacyPLPOptionsgainsleanVariants,variantPropertyNames,variantIncludeImage,variantIncludeInventory,displayedVariantId, andmaxImages; all default to undefined and preserve current output.displayedVariantIdkeeps one variant on the full shape for cards that read that variant's own offer (list price/installments).maxImagescapsimage[]to the first N entries by position, ignoring values below 1; onlytoProducthonors it, and it's not for callers selecting images by name.schemas.gen.ts) intentionally left out; regenerating it produces large unrelated drift and should be a separate PR.Written for commit 6f49cb3. Summary will update on new commits.