← back to Hollywood Import
scrub-momentum.js
75 lines
'use strict';
// Momentum → Hollywood private-label scrub. "hollywood" vendor_code in dw_unified
// is the DW private label for Momentum Textiles & Walls. NEVER expose "Momentum"
// (or its sub-brand "Versa") in any customer-facing field. The catalog already
// carries the PL mapping in specs (pl_brand / pl_city_name); this turns a
// vendor_catalog hollywood row into a public-safe product object + a leak check.
//
// Public mapping: vendor = specs.pl_brand ("Hollywood Wallcoverings")
// collection = specs.pl_city_name (CA cities)
// sku = dw_sku (DWHD-)
// Suppress: real_vendor, installation_pdf (momentum URL), grade_collection
// ("VERSA …"), hw/list/cost prices; scrub "Versa's Second-Look".
// Image: image_url passed only as transient push src → Shopify rehosts
// to cdn.shopify.com. Never stored in a text field.
const SOURCE_URL = /https?:\/\/[^\s"']*momentum[^\s"']*/gi;
const VERSA_PROGRAM = /Versa'?s\s+Second[-\s]?Look\s*®?\s*Program/gi;
const SOURCE_BRANDS = /\b(momentum(?:\s+textiles(?:\s+and\s+walls)?)?|versa(?:'s)?(?:\s+designed\s+surfaces)?)\b/gi;
// Spec keys that are internal-only or carry a source-brand value — never push.
const DROP_KEYS = new Set(['real_vendor','installation_pdf','grade_collection','hw_price','list_price','cost']);
function scrubText(s) {
if (s == null) return s;
return String(s)
.replace(SOURCE_URL, '')
.replace(VERSA_PROGRAM, "the manufacturer's take-back program")
.replace(SOURCE_BRANDS, (m) => /momentum/i.test(m) ? 'Hollywood' : '')
.replace(/\s{2,}/g, ' ').replace(/\s+,/g, ',').trim();
}
function scrubMomentum(row) {
const specs = typeof row.specs === 'string' ? JSON.parse(row.specs || '{}') : (row.specs || {});
const pubSpecs = {};
for (const [k, v] of Object.entries(specs)) {
if (DROP_KEYS.has(k)) continue;
pubSpecs[k] = (typeof v === 'string') ? scrubText(v) : v;
}
const tags = scrubText(specs.tags || '').split(',').map(t => t.trim()).filter(Boolean);
// ALL images, not just primary (Steve: "always pull all data and images" —
// every Hollywood/Momentum SKU must carry every image). Parse all_images
// (JSON array or comma/newline list), prepend primary, dedupe.
let imgs = [];
if (row.all_images) {
try { const a = JSON.parse(row.all_images); if (Array.isArray(a)) imgs = a; } catch { imgs = String(row.all_images).split(/[,\n]/); }
}
if (row.image_url) imgs.unshift(row.image_url);
const images = [...new Set(imgs.map(s => String(s || '').trim()).filter(Boolean))];
// completeness gate — importer MUST refuse rows that aren't fully crawled
// (need all images + core data). false → re-crawl the full page first.
const complete = images.length > 0 && !!row.pattern_name && !!row.mfr_sku && !!(specs && Object.keys(specs).length);
return {
vendor: specs.pl_brand || 'Hollywood Wallcoverings',
title: scrubText(row.pattern_name) + (row.color_name ? ', ' + scrubText(row.color_name) : ''),
collection: specs.pl_city_name ? scrubText(specs.pl_city_name) : (row.collection ? scrubText(row.collection) : null),
sku: row.dw_sku,
mfr_sku: row.mfr_sku,
images, // ALL images — TRANSIENT push srcs, Shopify rehosts; excluded from leak check
imageCount: images.length,
complete, // false = NOT import-ready
tags,
specs: pubSpecs,
};
}
// Leak check: scan every customer-facing field EXCEPT the image URLs (their
// momentum CDN host is the sanctioned transient source Shopify launders). Leaks.
function leakCheck(pub) {
const { images, imageSrc, ...rest } = pub;
const hay = JSON.stringify(rest);
const hits = hay.match(/momentum|versa/gi) || [];
return hits;
}
module.exports = { scrubMomentum, scrubText, leakCheck };