goods-exporter
Version:
A versatile JavaScript library for exporting goods data to various formats such as YML, CSV, and Excel. Simplify data export tasks with ease. Supports streams.
1,091 lines (1,068 loc) • 41.6 kB
JavaScript
;
var events = require('events');
var stream$3 = require('stream');
var pkg = require('exceljs');
var jsonStreamStringify = require('json-stream-stringify');
var fastXmlParser = require('fast-xml-parser');
var fs = require('fs');
const buildCategoryPaths = (categories) => {
const idToCategory = /* @__PURE__ */ new Map();
categories.forEach((category) => {
idToCategory.set(category.id, category);
});
const categoryPaths = /* @__PURE__ */ new Map();
categories.forEach((category) => {
const path = [];
let currentCategory = category;
while (currentCategory) {
path.unshift(currentCategory);
if (currentCategory.parentId !== void 0) {
currentCategory = idToCategory.get(currentCategory.parentId);
} else {
currentCategory = void 0;
}
}
categoryPaths.set(category.id, path);
});
return categoryPaths;
};
const writeWithDrain = (stream) => {
return async (chunk) => {
const canWrite = stream.write(chunk);
if (!canWrite) {
await events.once(stream, "drain");
}
};
};
const delay = async (ms) => await new Promise((resolve) => setTimeout(resolve, ms));
function getRFC3339Date(date) {
const pad = (n) => n.toString().padStart(2, "0");
const year = date.getFullYear();
const month = pad(date.getMonth() + 1);
const day = pad(date.getDate());
const hour = pad(date.getHours());
const min = pad(date.getMinutes());
const tzOffset = -date.getTimezoneOffset();
const sign = tzOffset >= 0 ? "+" : "-";
const absOffset = Math.abs(tzOffset);
const offsetHour = pad(Math.floor(absOffset / 60));
const offsetMin = pad(absOffset % 60);
return `${year}-${month}-${day}T${hour}:${min}${sign}${offsetHour}:${offsetMin}`;
}
const urlQueryEncode = (inputUrl) => {
try {
const url = new URL(inputUrl);
url.search = url.search.replace(/^\?/, "").replace(/,/g, "%2C");
return url.toString();
} catch (error) {
console.error("Invalid URL:", error);
return "";
}
};
var __defProp$b = Object.defineProperty;
var __defNormalProp$b = (obj, key, value) => key in obj ? __defProp$b(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$b = (obj, key, value) => __defNormalProp$b(obj, typeof key !== "symbol" ? key + "" : key, value);
class CSVStream {
constructor({ delimiter, lineSeparator, emptyFieldValue }) {
__publicField$b(this, "stream", new stream$3.PassThrough());
__publicField$b(this, "delimiter", ";");
__publicField$b(this, "lineSeparator", "\n");
__publicField$b(this, "emptyFieldValue", "");
__publicField$b(this, "columns", /* @__PURE__ */ new Set());
__publicField$b(this, "writer", writeWithDrain(this.stream));
if (delimiter !== void 0) this.delimiter = delimiter;
if (lineSeparator !== void 0) this.lineSeparator = lineSeparator;
if (emptyFieldValue !== void 0) this.emptyFieldValue = emptyFieldValue;
}
get writableStream() {
return this.stream;
}
setColumns(columns) {
this.columns = columns;
this.stream.write(
Array.from(this.columns).join(this.delimiter) + this.lineSeparator
);
}
async addRow(items) {
const data = Array.from(this.columns).map(
(key) => items[key] === void 0 ? this.emptyFieldValue : items[key] + ""
).join(this.delimiter) + this.lineSeparator;
await this.writer(data);
}
}
class FormatterAbstract {
}
var Extension = /* @__PURE__ */ ((Extension2) => {
Extension2["CSV"] = "csv";
Extension2["YML"] = "yml";
Extension2["XML"] = "xml";
Extension2["XLSX"] = "xlsx";
Extension2["JSON"] = "json";
return Extension2;
})(Extension || {});
var __defProp$a = Object.defineProperty;
var __defNormalProp$a = (obj, key, value) => key in obj ? __defProp$a(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$a = (obj, key, value) => __defNormalProp$a(obj, typeof key !== "symbol" ? key + "" : key, value);
class CSVFormatter {
constructor() {
__publicField$a(this, "formatterName", "CSV");
__publicField$a(this, "fileExtension", Extension.CSV);
}
async format(writableStream, products, categories, _, __) {
const mappedCategories = {};
categories?.forEach(({ id, name }) => mappedCategories[id] = name);
const csvStream = new CSVStream({
delimiter: ";",
emptyFieldValue: "",
lineSeparator: "\n"
});
csvStream.writableStream.pipe(writableStream);
const columns = /* @__PURE__ */ new Set([
"url",
"productId",
"parentId",
"variantId",
"title",
"description",
"vendor",
"vendorCode",
"category",
"images",
"videos",
"timeDeliveryMin",
"timeDeliveryMax",
"price",
"oldPrice",
"purchasePrice",
"currency",
"saleDate",
"countryOfOrigin",
"tags",
"codesTN",
"params",
"properties",
"sizes",
"keywords",
"relatedProducts"
]);
products.forEach((product) => {
Object.entries(product).forEach(([key, value]) => {
if (value) columns.add(key);
});
});
csvStream.setColumns(columns);
for (const product of products) {
const row = {
...product,
category: mappedCategories[product.categoryId],
images: product.images?.join(","),
videos: product.videos?.join(","),
tags: product.tags?.join(","),
codesTN: product.codesTN?.join(", "),
params: product.params?.map(({ key, value }) => `${key}=${value}`).join(", "),
properties: product.properties?.map(({ key, value }) => `${key}=${value}`).join(", "),
sizes: product.sizes?.map(({ name, value }) => `${name}=${value}`).join(", "),
keywords: product.keywords?.join(","),
relatedProducts: product.relatedProducts?.join(","),
timeDeliveryMin: product.timeDelivery?.min,
timeDeliveryMax: product.timeDelivery?.max
};
await csvStream.addRow(row);
}
csvStream.writableStream.end();
}
}
var __defProp$9 = Object.defineProperty;
var __defNormalProp$9 = (obj, key, value) => key in obj ? __defProp$9(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$9 = (obj, key, value) => __defNormalProp$9(obj, typeof key !== "symbol" ? key + "" : key, value);
const { stream: stream$2 } = pkg;
class ExcelFormatter {
constructor() {
__publicField$9(this, "formatterName", "Excel");
__publicField$9(this, "fileExtension", Extension.XLSX);
}
async format(writableStream, products, categories, _, __) {
const mappedCategories = {};
categories?.forEach(({ id, name }) => mappedCategories[id] = name);
const columns = /* @__PURE__ */ new Set([
"url",
"productId",
"parentId",
"variantId",
"title",
"description",
"vendor",
"vendorCode",
"category",
"images",
"videos",
"timeDeliveryMin",
"timeDeliveryMax",
"price",
"oldPrice",
"purchasePrice",
"currency",
"saleDate",
"countryOfOrigin",
"tags",
"codesTN",
"params",
"properties",
"sizes",
"keywords",
"relatedProducts"
]);
products.forEach((product) => {
Object.entries(product).forEach(([key, value]) => {
if (value) columns.add(key);
});
});
const workbook = new stream$2.xlsx.WorkbookWriter({
stream: writableStream
});
const worksheet = workbook.addWorksheet("products");
worksheet.columns = Array.from(columns).map((column) => ({
key: column,
header: column
}));
products.forEach((product) => {
const row = {
...product,
category: mappedCategories[product.categoryId],
images: product.images?.join(","),
videos: product.videos?.join(","),
tags: product.tags?.join(","),
keywords: product.keywords?.join(","),
relatedProducts: product.relatedProducts?.join(","),
codesTN: product.codesTN?.join(", "),
params: product.params?.map(({ key, value }) => `${key}=${value}`).join(", "),
properties: product.properties?.map(({ key, value }) => `${key}=${value}`).join(", "),
sizes: product.sizes?.map(({ name, value }) => `${name}=${value}`).join(", "),
timeDeliveryMin: product.timeDelivery?.min,
timeDeliveryMax: product.timeDelivery?.max
};
worksheet.addRow(row).commit();
});
worksheet.commit();
await workbook.commit();
}
}
var __defProp$8 = Object.defineProperty;
var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$8 = (obj, key, value) => __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value);
const { stream: stream$1 } = pkg;
class InsalesFormatter {
constructor() {
__publicField$8(this, "formatterName", "Insales");
__publicField$8(this, "fileExtension", Extension.XLSX);
}
async format(writableStream, products, categories, _, __) {
const mappedCategories = {};
categories?.forEach(
(category) => mappedCategories[category.id] = category
);
const getParams = (product) => {
const properties = {};
product.params?.forEach(
(p) => properties[`\u0421\u0432\u043E\u0439\u0441\u0442\u0432\u043E: ${p.key}`] = p.value
);
return properties;
};
const getProperties = (product) => {
const properties = {};
product.properties?.forEach(
(p) => properties[`\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440: ${p.key}`] = p.value
);
return properties;
};
const getCategories = (product) => {
const categories2 = {};
const categoryList = new Array();
function addCategory(categoryId) {
if (categoryId === void 0) return;
const category = mappedCategories[categoryId];
if (category) {
categoryList.push(category.name);
addCategory(category.parentId);
}
}
addCategory(product.categoryId);
categoryList.forEach((name, i) => {
const index = categoryList.length - 1 - i;
const key = index === 0 ? "\u041A\u043E\u0440\u043D\u0435\u0432\u0430\u044F" : `\u041F\u043E\u0434\u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u044F ${index}`;
categories2[key] = name;
});
return categories2;
};
const workbook = new stream$1.xlsx.WorkbookWriter({
stream: writableStream
});
const worksheet = workbook.addWorksheet("products");
const columns = /* @__PURE__ */ new Set([
"\u0412\u043D\u0435\u0448\u043D\u0438\u0439 ID",
"\u0421\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 \u0442\u043E\u0432\u0430\u0440",
"\u0410\u0440\u0442\u0438\u043A\u0443\u043B",
"\u041A\u043E\u0440\u043D\u0435\u0432\u0430\u044F",
"\u041F\u043E\u0434\u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u044F 1",
"\u041F\u043E\u0434\u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u044F 2",
"\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0442\u043E\u0432\u0430\u0440\u0430 \u0438\u043B\u0438 \u0443\u0441\u043B\u0443\u0433\u0438",
"\u0412\u0440\u0435\u043C\u044F \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u0438: \u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435",
"\u0412\u0440\u0435\u043C\u044F \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u0438: \u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435",
"\u0421\u0442\u0430\u0440\u0430\u044F \u0446\u0435\u043D\u0430",
"\u0426\u0435\u043D\u0430 \u043F\u0440\u043E\u0434\u0430\u0436\u0438",
"C\u0435\u0431\u0435\u0441\u0442\u043E\u0438\u043C\u043E\u0441\u0442\u044C",
"\u041A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u0438",
"\u041E\u0441\u0442\u0430\u0442\u043E\u043A",
"\u0428\u0442\u0440\u0438\u0445-\u043A\u043E\u0434",
"\u041A\u0440\u0430\u0442\u043A\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435",
"\u041F\u043E\u043B\u043D\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435",
"\u0413\u0430\u0431\u0430\u0440\u0438\u0442\u044B \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u0430",
"\u0412\u0435\u0441",
"\u0420\u0430\u0437\u043C\u0435\u0449\u0435\u043D\u0438\u0435 \u043D\u0430 \u0441\u0430\u0439\u0442\u0435",
"\u041D\u0414\u0421",
"\u0412\u0430\u043B\u044E\u0442\u0430 \u0441\u043A\u043B\u0430\u0434\u0430",
"\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u0430",
"\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F",
"\u0421\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 \u0432\u0438\u0434\u0435\u043E",
"\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440: \u0410\u0440\u0442\u0438\u043A\u0443\u043B",
"\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B",
"\u0421\u0432\u043E\u0439\u0441\u0442\u0432\u0430",
"\u0420\u0430\u0437\u043C\u0435\u0440\u043D\u0430\u044F \u0441\u0435\u0442\u043A\u0430",
"\u0421\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u0442\u043E\u0432\u0430\u0440\u044B",
"\u041A\u043B\u044E\u0447\u0435\u0432\u044B\u0435 \u0441\u043B\u043E\u0432\u0430"
]);
products.forEach((product) => {
Object.keys({
...getParams(product),
...getProperties(product)
}).forEach((key) => {
columns.add(key);
});
});
worksheet.columns = Array.from(columns).map((column) => ({
header: column,
key: column
}));
for (const product of products) {
const externalId = `${product.productId}-${product.variantId}`;
const row = {
"\u0412\u043D\u0435\u0448\u043D\u0438\u0439 ID": externalId,
"\u0421\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 \u0442\u043E\u0432\u0430\u0440": product.url,
\u0410\u0440\u0442\u0438\u043A\u0443\u043B: externalId,
// TODO: product.vendorCode,
"\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0442\u043E\u0432\u0430\u0440\u0430 \u0438\u043B\u0438 \u0443\u0441\u043B\u0443\u0433\u0438": product.title,
"\u0412\u0440\u0435\u043C\u044F \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u0438: \u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435": product.timeDelivery?.min,
"\u0412\u0440\u0435\u043C\u044F \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u0438: \u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435": product.timeDelivery?.max,
"\u0421\u0442\u0430\u0440\u0430\u044F \u0446\u0435\u043D\u0430": product.oldPrice,
"\u0426\u0435\u043D\u0430 \u043F\u0440\u043E\u0434\u0430\u0436\u0438": product.price,
C\u0435\u0431\u0435\u0441\u0442\u043E\u0438\u043C\u043E\u0441\u0442\u044C: product.purchasePrice,
...getCategories(product),
\u041E\u0441\u0442\u0430\u0442\u043E\u043A: product.count,
"\u0428\u0442\u0440\u0438\u0445-\u043A\u043E\u0434": product.barcode,
"\u041A\u0440\u0430\u0442\u043A\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435": void 0,
"\u041F\u043E\u043B\u043D\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435": product.description,
"\u0413\u0430\u0431\u0430\u0440\u0438\u0442\u044B \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u0430": product.dimensions,
\u0412\u0435\u0441: product.weight,
"\u0420\u0430\u0437\u043C\u0435\u0449\u0435\u043D\u0438\u0435 \u043D\u0430 \u0441\u0430\u0439\u0442\u0435": product.available,
\u041D\u0414\u0421: product.vat?.toString(),
"\u0412\u0430\u043B\u044E\u0442\u0430 \u0441\u043A\u043B\u0430\u0434\u0430": product.currency.toString(),
"\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u0430": product.parentId === void 0 ? product.images?.join(" ") : void 0,
\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F: product.parentId === void 0 ? void 0 : product.images?.join(" "),
"\u0421\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 \u0432\u0438\u0434\u0435\u043E": product.videos ? product.videos[0] : void 0,
"\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440: \u0410\u0440\u0442\u0438\u043A\u0443\u043B": product.vendorCode,
// TODO: брать из обычных параметров
...getParams(product),
...getProperties(product),
"\u0420\u0430\u0437\u043C\u0435\u0440\u043D\u0430\u044F \u0441\u0435\u0442\u043A\u0430": JSON.stringify(product.sizes),
"\u0421\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u0442\u043E\u0432\u0430\u0440\u044B": product.relatedProducts?.join(","),
"\u041A\u043B\u044E\u0447\u0435\u0432\u044B\u0435 \u0441\u043B\u043E\u0432\u0430": product.keywords?.join(",")
};
worksheet.addRow(row).commit();
}
worksheet.commit();
await workbook.commit();
}
}
var __defProp$7 = Object.defineProperty;
var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$7 = (obj, key, value) => __defNormalProp$7(obj, typeof key !== "symbol" ? key + "" : key, value);
class JSONFormatter {
constructor() {
__publicField$7(this, "formatterName", "JSON");
__publicField$7(this, "fileExtension", Extension.JSON);
}
async format(writableStream, products, categories, brands, _) {
const stream = new jsonStreamStringify.JsonStreamStringify({
categories,
brands,
products
});
stream.pipe(writableStream);
}
}
var __defProp$6 = Object.defineProperty;
var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$6 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
class SimpleJSONFormatter {
constructor() {
__publicField$6(this, "formatterName", "JSON");
__publicField$6(this, "fileExtension", Extension.JSON);
}
async format(writableStream, products, categories, brands, _) {
const groupedProduct = /* @__PURE__ */ new Map();
products.forEach((product) => {
if (product.parentId !== void 0) return;
groupedProduct.set(product.variantId, {
...product,
children: []
});
});
products.forEach((product) => {
if (product.parentId === void 0) return;
const parent = groupedProduct.get(product.parentId);
if (!parent) return;
parent.children.push(product);
});
const stream = new jsonStreamStringify.JsonStreamStringify({
categories,
brands,
products: Array.from(groupedProduct.values())
});
stream.pipe(writableStream);
}
}
var __defProp$5 = Object.defineProperty;
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$5 = (obj, key, value) => __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
const { stream } = pkg;
class TgShopFormatter {
constructor() {
__publicField$5(this, "formatterName", "TgShop");
__publicField$5(this, "fileExtension", Extension.XLSX);
}
async format(writableStream, products, categories, _, __) {
const getParameter = (product, key) => product.params?.find((value) => value.key === key);
const convertProduct = (product) => ({
"category id": product.categoryId,
"group id": product.parentId,
"id product": product.variantId,
"name product": product.title,
price: product.price,
picture: product.images?.join(", "),
vendorCode: product.vendorCode,
oldprice: product.oldPrice,
description: product.description,
shortDescription: "",
quantityInStock: product.count,
color: getParameter(product, "color")?.value,
size: getParameter(product, "size")?.value,
priority: void 0
});
const workbook = new stream.xlsx.WorkbookWriter({
stream: writableStream
});
const categoryWorksheet = workbook.addWorksheet("categories");
const productsWorksheet = workbook.addWorksheet("offers");
categoryWorksheet.columns = [
{
header: "id",
key: "id"
},
{
header: "parentId",
key: "parentId"
},
{
header: "name",
key: "name"
}
];
const columns = [
"category id",
"group id",
"id product",
"name product",
"price",
"picture",
"vendorCode",
"oldprice",
"description",
"shortDescription",
"quantityInStock",
"color",
"size",
"priority"
];
productsWorksheet.columns = columns.map((column) => ({
header: column,
key: column
}));
categories?.forEach((category) => {
categoryWorksheet.addRow(category).commit();
});
products.forEach((product) => {
productsWorksheet.addRow(convertProduct(product)).commit();
});
categoryWorksheet.commit();
productsWorksheet.commit();
await workbook.commit();
}
}
var __defProp$4 = Object.defineProperty;
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
class TildaFormatter {
constructor() {
__publicField$4(this, "formatterName", "Tilda");
__publicField$4(this, "fileExtension", Extension.CSV);
}
async format(writableStream, products, categories, _, __) {
const mappedCategories = {};
categories?.forEach(({ id, name }) => mappedCategories[id] = name);
const csvStream = new CSVStream({
delimiter: " ",
emptyFieldValue: "",
lineSeparator: "\n"
});
csvStream.writableStream.pipe(writableStream);
const columns = /* @__PURE__ */ new Set([
"SKU",
"Brand",
"Category",
"Title",
"Text",
"Photo",
"Price",
"Price Old",
"Quantity",
"Editions",
"External ID",
"Parent UID"
]);
const characteristics = /* @__PURE__ */ new Set();
products.forEach((product) => {
product.properties?.forEach(({ key }) => {
characteristics.add(key);
});
});
characteristics.forEach((charKey) => {
columns.add(`Characteristics:${charKey}`);
});
csvStream.setColumns(columns);
for (const product of products) {
const row = {
SKU: product.vendorCode,
Brand: product.vendor,
Category: mappedCategories[product.categoryId],
Title: product.title,
Text: product.description,
Photo: product.images?.map(urlQueryEncode).join(","),
Price: product.price,
"Price Old": product.oldPrice,
Quantity: product.count,
Editions: product.params?.map(({ key, value }) => `${key}:${value}`).join(";"),
"External ID": product.variantId,
"Parent UID": product.parentId
};
product.properties?.forEach(({ key, value }) => {
row[`Characteristics:${key}`] = value;
});
await csvStream.addRow(row);
}
csvStream.writableStream.end();
}
}
var __defProp$3 = Object.defineProperty;
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
class WooCommerceFormatter {
constructor() {
__publicField$3(this, "formatterName", "WooCommerce");
__publicField$3(this, "fileExtension", Extension.CSV);
__publicField$3(this, "DEFAULT_COLUMNS", [
"ID",
"Type",
"SKU",
"Name",
"Parent",
"Short description",
"Description",
"Stock",
"Regular price",
"Position",
"Categories",
"Tags",
"Images",
"SizeGrid"
]);
}
formatKeyAttribute(key) {
const keyWithoutInvalidCharacters = key.replaceAll(",", "").replaceAll(";", "");
return keyWithoutInvalidCharacters.length >= 28 ? keyWithoutInvalidCharacters.slice(0, 24) + "..." : keyWithoutInvalidCharacters;
}
formatValueAttribute(value) {
return value.replaceAll(",", "").replaceAll(";", "");
}
formatProducts(products) {
const formatParams = (params) => {
return params?.map(({ key, value }) => {
const formatedKey = this.formatKeyAttribute(key);
const formatedValue = this.formatValueAttribute(value);
return { key: formatedKey, value: formatedValue };
});
};
return products.map((product) => {
const params = formatParams(product.params);
const properties = formatParams(product.properties);
return { ...product, params, properties };
});
}
createAttribute(data) {
if (!data?.name || data.id === void 0) return;
const attributeStartName = "Attribute";
const attribute = {};
attribute[`${attributeStartName} ${data.id} name`] = data.name;
if (data.values !== void 0)
attribute[`${attributeStartName} ${data.id} value(s)`] = data.values;
if (data.visible !== void 0)
attribute[`${attributeStartName} ${data.id} visible`] = data.visible;
if (data.global !== void 0)
attribute[`${attributeStartName} ${data.id} global`] = data.global;
return attribute;
}
extractAttributes(products) {
const formatedProducts = this.formatProducts(products);
const paramsMap = /* @__PURE__ */ new Map();
const propertiesMap = /* @__PURE__ */ new Map();
const uniqueAttributes = /* @__PURE__ */ new Map();
const genderTitle = "\u041F\u043E\u043B";
formatedProducts.forEach((product) => {
product.params?.forEach(({ key }) => {
if (!uniqueAttributes.has(key)) {
uniqueAttributes.set(key, uniqueAttributes.size);
}
});
uniqueAttributes.set(genderTitle, uniqueAttributes.size);
product.properties?.forEach(({ key }) => {
if (!uniqueAttributes.has(key)) {
uniqueAttributes.set(key, uniqueAttributes.size);
}
});
});
formatedProducts.forEach((product) => {
const paramAttributes = paramsMap.get(product.variantId) ?? {};
const propertyAttributes = propertiesMap.get(product.variantId) ?? {};
product.params?.forEach(({ key, value }) => {
const index = uniqueAttributes.get(key);
if (index === void 0) {
console.error(`\u041D\u0435 \u043D\u0430\u0448\u043B\u043E\u0441\u044C \u0443\u043D\u0438\u043A\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u043B\u044E\u0447\u0430 \u0434\u043B\u044F \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430 - ${key}`);
return;
}
const attribute = this.createAttribute({
name: key,
id: index,
values: value,
visible: 0,
global: 0
});
if (!attribute) return;
Object.entries(attribute).forEach(
([key2, value2]) => paramAttributes[key2] = value2
);
});
const genderIndex = uniqueAttributes.get(genderTitle);
const genderAttribute = this.createAttribute({
name: genderTitle,
id: genderIndex,
values: product.gender,
global: 0
});
if (genderAttribute) {
Object.entries(genderAttribute).forEach(
([key, value]) => propertyAttributes[key] = value
);
}
product.properties?.forEach(({ key, value }) => {
const index = uniqueAttributes.get(key);
if (index === void 0) {
console.error(`\u041D\u0435 \u043D\u0430\u0448\u043B\u043E\u0441\u044C \u0443\u043D\u0438\u043A\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u043B\u044E\u0447\u0430 \u0434\u043B\u044F \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430 - ${key}`);
return;
}
if (paramAttributes[`Attribute ${index} name`]) {
console.warn(`\u0414\u0430\u043D\u043D\u043E\u0435 \u0441\u0432\u043E\u0439\u0441\u0442\u0432\u043E \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0432 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u0445 - ${key}`);
return;
}
const attribute = this.createAttribute({
name: key,
id: index,
values: value,
global: 0
});
if (!attribute) return;
Object.entries(attribute).forEach(
([key2, value2]) => propertyAttributes[key2] = value2
);
});
paramsMap.set(product.variantId, paramAttributes);
propertiesMap.set(product.variantId, propertyAttributes);
});
return { params: paramsMap, properties: propertiesMap };
}
removeVisibleFromAttributes(params) {
Object.entries(params).forEach(([key]) => {
if (key.includes("visible")) params[key] = "";
});
}
async format(writableStream, products, categories, _, __) {
const categoryPaths = buildCategoryPaths(categories ?? []);
const csvStream = new CSVStream({
delimiter: ";",
emptyFieldValue: "",
lineSeparator: "\n"
});
csvStream.writableStream.pipe(writableStream);
const columns = new Set(this.DEFAULT_COLUMNS);
const attributes = this.extractAttributes(products);
const variationsByParentId = /* @__PURE__ */ new Map();
const imagesByParentId = /* @__PURE__ */ new Map();
const sizesByParentId = /* @__PURE__ */ new Map();
const variations = products.map((product, index) => {
const pathsArray = categoryPaths.get(product.categoryId)?.map((category) => category.name);
const price = product.price ? product.price : "";
const images = product.images?.map(urlQueryEncode).join(",");
let row = {
ID: product.variantId,
Type: "variation",
SKU: product.variantId,
Name: product.title,
Parent: product.parentId ?? 0,
"Short description": "",
Description: product.description,
Stock: product.count ?? 0,
"Regular price": price,
Position: index + 1,
Categories: pathsArray?.join(" > "),
Tags: product.keywords?.join(","),
Images: images,
SizeGrid: ""
};
const productParams = attributes.params.get(product.variantId) ?? {};
if (!imagesByParentId.has(row.Parent)) {
imagesByParentId.set(row.Parent, images);
}
if (!sizesByParentId.has(row.Parent)) {
sizesByParentId.set(
row.Parent,
product.sizes ? JSON.stringify(product.sizes) : ""
);
}
this.removeVisibleFromAttributes(productParams);
row = { ...row, ...productParams };
if (variationsByParentId.has(row.Parent)) {
variationsByParentId.get(row.Parent)?.push(row);
} else {
variationsByParentId.set(row.Parent, [row]);
}
return row;
});
const parentProducts = /* @__PURE__ */ new Map();
variations.forEach((product) => {
const currentParent = parentProducts.get(product.Parent);
let row = {
...product,
Type: "variable",
ID: product.Parent,
SKU: product.Parent,
Position: 0,
Parent: "",
"Regular price": "",
Images: imagesByParentId.get(product.Parent) ?? "",
SizeGrid: sizesByParentId.get(product.Parent) ?? ""
};
row.Stock = (currentParent?.Stock || 0) + (product?.Stock || 0);
const productParams = attributes.params.get(product.SKU) ?? {};
const productProperties = attributes.properties.get(product.SKU) ?? {};
Object.entries(productParams).forEach(([key]) => {
if (key.includes("visible")) productParams[key] = 0;
});
if (currentParent) {
Object.entries(productParams).forEach(([key, value]) => {
if (key.includes("value(s)")) {
productParams[key] = currentParent[key] + `, ${value}`;
}
});
}
Object.keys({ ...row, ...productParams, ...productProperties }).forEach(
(item) => columns.add(item)
);
row = { ...row, ...productParams, ...productProperties };
parentProducts.set(product.Parent, row);
});
const variableProducts = Array.from(parentProducts.values());
csvStream.setColumns(columns);
for (const parentProduct of variableProducts) {
await csvStream.addRow(parentProduct);
for (const variationProduct of variationsByParentId.get(
parentProduct.ID
) ?? []) {
await csvStream.addRow(variationProduct);
}
}
csvStream.writableStream.end();
}
}
var __defProp$2 = Object.defineProperty;
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
class YMLFormatter {
constructor() {
__publicField$2(this, "formatterName", "YMl");
__publicField$2(this, "fileExtension", Extension.YML);
}
async format(writableStream, products, categories, brands, options) {
const result = new stream$3.PassThrough();
result.pipe(writableStream);
const builder = new fastXmlParser.XMLBuilder({
ignoreAttributes: false,
cdataPropName: "__cdata",
format: true,
indentBy: " "
});
const date = getRFC3339Date(/* @__PURE__ */ new Date());
result.write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n');
result.write('<yml_catalog date="' + date + '">\n');
result.write("<shop>\n");
const resultWriter = writeWithDrain(result);
if (options?.shopName) {
await resultWriter(builder.build({ name: options.shopName }));
await resultWriter("\n");
}
if (options?.companyName) {
await resultWriter(builder.build({ company: options.companyName }));
await resultWriter("\n");
}
if (categories) {
await resultWriter(
builder.build({
// tagname: "categories",
categories: { category: this.getCategories(categories) }
})
);
await resultWriter("\n");
}
if (brands) {
await resultWriter(
builder.build({ brands: { brand: this.getBrands(brands) } })
);
await resultWriter("\n");
}
await resultWriter("<offers>\n");
const offerStream = new stream$3.PassThrough();
const offerWriter = writeWithDrain(offerStream);
offerStream.pipe(result, { end: false });
for (const product of products) {
if (product.price === 0) continue;
const offer = builder.build({ offer: this.getOffer(product) });
await offerWriter(offer + "\n");
}
offerStream.end();
offerStream.on("end", () => {
result.write("</offers>\n");
result.write("</shop>\n");
result.write("</yml_catalog>\n");
result.end();
});
}
getBrands(brands) {
if (!brands) return [];
return brands.map((brand) => ({
"@_id": brand.id,
"@_url": brand.coverURL ?? "",
"#text": brand.name
}));
}
getCategories(categories) {
if (!categories) return [];
return categories.map((cat) => ({
"@_id": cat.id,
"@_parentId": cat.parentId ?? "",
"#text": cat.name || `\u041A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u044F #${cat.id}`
}));
}
getOffer(product) {
const result = {
"@_id": product.variantId,
name: product.title,
price: product.price,
oldprice: product.oldPrice,
purchase_price: product.purchasePrice,
additional_expenses: product.additionalExpenses,
cofinance_price: product.cofinancePrice,
currencyId: product.currency,
categoryId: product.categoryId,
vendorId: product.vendorId,
vendor: product.vendor,
vendorCode: product.vendorCode,
picture: product.images,
video: product.videos,
available: product.available,
"time-delivery": product.timeDelivery ? {
"@_min": product.timeDelivery.min,
"@_max": product.timeDelivery.max,
"#text": `${product.timeDelivery.min}-${product.timeDelivery.max}`
} : void 0,
series: product.seriesName,
"min-quantity": product.minQuantity,
"step-quantity": product.stepQuantity,
size: product.sizes?.map((size) => ({
"#text": size.value,
"@_name": size.name,
"@_delimiter": size.delimiter
})),
keyword: product.keywords,
saleDate: product.saleDate,
property: product.properties?.map((property) => ({
"#text": property.value,
"@_name": property.key
})),
param: product.params?.map((param) => ({
"#text": param.value,
"@_name": param.key
})),
description: {
__cdata: product.description
},
country_of_origin: product.countryOfOrigin,
barcode: product.barcode,
vat: product.vat,
count: product.count,
"set-ids": product.tags?.join(", "),
adult: product.adult,
downloadable: product.downloadable,
"period-of-validity-days": product.validityPeriod,
"comment-validity-days": product.validityComment,
"service-life-days": product.serviceLifePeriod,
"comment-life-days": product.serviceLifeComment,
"warranty-days": product.warrantyPeriod,
"comment-warranty": product.warrantyComment,
manufacturer_warranty: product.manufacturerWarranty,
certificate: product.certificate,
url: product.url,
weight: product.weight,
dimensions: product.dimensions,
boxCount: product.boxCount,
disabled: product.disabled,
age: product.age ? {
"@_unit": product.age.unit,
"#text": product.age.value
} : void 0,
"tn-ved-codes": product.codesTN?.length ? {
"tn-ved-code": product.codesTN
} : void 0,
relatedProduct: product.relatedProducts,
gender: product.gender
};
if (product.parentId !== void 0) {
return {
...result,
"@_group_id": product.parentId
};
}
return result;
}
}
var __defProp$1 = Object.defineProperty;
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
class XMLFormatter extends YMLFormatter {
constructor() {
super(...arguments);
__publicField$1(this, "formatterName", "XML");
__publicField$1(this, "fileExtension", Extension.XML);
}
}
const Formatters = {
TildaFormatter,
CSVFormatter,
InsalesFormatter,
YMLFormatter,
TgShopFormatter,
ExcelFormatter,
JSONFormatter,
SimpleJSONFormatter,
XMLFormatter,
WooCommerceFormatter
};
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
class GoodsExporter {
constructor(context) {
this.context = context;
__publicField(this, "_context");
__publicField(this, "formatter", new Formatters.YMLFormatter());
__publicField(this, "exporter", () => {
return fs.createWriteStream(
`${this.formatter.formatterName}.output.${this.formatter.fileExtension}`
);
});
__publicField(this, "transformers", new Array());
this._context = context;
}
setContext(context) {
this._context = context;
}
setTransformers(transformers) {
this.transformers = transformers;
}
setFormatter(formatter) {
this.formatter = formatter;
}
setExporter(exporter) {
this.exporter = exporter;
}
async export(products, categories, brands, option) {
let transformedProducts = products;
for (const transformer of this.transformers)
transformedProducts = await transformer(
transformedProducts,
this._context
);
const writableStream = this.exporter();
await this.formatter.format(
writableStream,
transformedProducts,
categories,
brands,
option
);
}
}
var Vat = /* @__PURE__ */ ((Vat2) => {
Vat2["NO_VAT"] = "NO_VAT";
Vat2["VAT_0"] = "VAT_0";
Vat2["VAT_10"] = "VAT_10";
Vat2["VAT_20"] = "VAT_20";
return Vat2;
})(Vat || {});
var Currency = /* @__PURE__ */ ((Currency2) => {
Currency2["RUR"] = "RUR";
Currency2["BYN"] = "BYN";
Currency2["EUR"] = "EUR";
Currency2["USD"] = "USD";
Currency2["UAN"] = "UAN";
Currency2["KZT"] = "KZT";
return Currency2;
})(Currency || {});
exports.Currency = Currency;
exports.Extension = Extension;
exports.FormatterAbstract = FormatterAbstract;
exports.Formatters = Formatters;
exports.GoodsExporter = GoodsExporter;
exports.Vat = Vat;
exports.buildCategoryPaths = buildCategoryPaths;
exports.delay = delay;
exports.getRFC3339Date = getRFC3339Date;
exports.urlQueryEncode = urlQueryEncode;
exports.writeWithDrain = writeWithDrain;
//# sourceMappingURL=index.cjs.map