@dylandepass/helix-product-pipeline 2.2.0 → 2.4.0
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/CHANGELOG.md +14 -0
- package/package.json +2 -2
- package/src/product-html-pipe.js +2 -0
- package/src/product-index-pipe.js +25 -5
- package/src/product-json-pipe.js +3 -0
- package/src/steps/create-pictures.js +2 -3
- package/src/steps/transform-images.js +34 -0
- package/src/steps/utils.js +33 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [2.4.0](https://github.com/adobe-rnd/helix-product-pipeline/compare/v2.3.0...v2.4.0) (2026-03-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* append optional image filenames in HTML and JSON pipeline output ([#72](https://github.com/adobe-rnd/helix-product-pipeline/issues/72)) ([c87e79c](https://github.com/adobe-rnd/helix-product-pipeline/commit/c87e79c9d74cf045135fd3b8049e7c31e1c9e175))
|
|
7
|
+
|
|
8
|
+
# [2.3.0](https://github.com/adobe-rnd/helix-product-pipeline/compare/v2.2.0...v2.3.0) (2026-02-12)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add limit/offset pagination and deterministic sort to product index ([#71](https://github.com/adobe-rnd/helix-product-pipeline/issues/71)) ([28499dd](https://github.com/adobe-rnd/helix-product-pipeline/commit/28499dd2140c830ed46ecec03bad4cee1fdd1ba5))
|
|
14
|
+
|
|
1
15
|
# [2.2.0](https://github.com/adobe-rnd/helix-product-pipeline/compare/v2.1.0...v2.2.0) (2026-02-04)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dylandepass/helix-product-pipeline",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Helix Product Pipeline",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@adobe/helix-html-pipeline": "6.27.9",
|
|
49
49
|
"@adobe/helix-shared-utils": "3.0.2",
|
|
50
|
-
"@dylandepass/helix-product-shared": "1.
|
|
50
|
+
"@dylandepass/helix-product-shared": "1.6.0",
|
|
51
51
|
"dayjs": "1.11.19",
|
|
52
52
|
"github-slugger": "2.0.0",
|
|
53
53
|
"hast-util-from-html": "^2.0.3",
|
package/src/product-html-pipe.js
CHANGED
|
@@ -16,6 +16,7 @@ import addHeadingIds from './steps/add-heading-ids.js';
|
|
|
16
16
|
import { getPathInfo, validatePathInfo } from './utils/path.js';
|
|
17
17
|
import initConfig from './steps/init-config.js';
|
|
18
18
|
import fetchProductBusContent from './steps/fetch-productbus.js';
|
|
19
|
+
import transformImages from './steps/transform-images.js';
|
|
19
20
|
import fetchEdgeContent from './steps/fetch-edge-product.js';
|
|
20
21
|
import { setLastModified } from './utils/last-modified.js';
|
|
21
22
|
import html from './steps/make-html.js';
|
|
@@ -71,6 +72,7 @@ export async function productHTMLPipe(state, req) {
|
|
|
71
72
|
return res;
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
transformImages(state);
|
|
74
76
|
state.timer?.update('render');
|
|
75
77
|
await html(state);
|
|
76
78
|
|
|
@@ -17,22 +17,33 @@ import initConfig from './steps/init-config.js';
|
|
|
17
17
|
import fetchProductBusContent from './steps/fetch-productbus.js';
|
|
18
18
|
import { setLastModified } from './utils/last-modified.js';
|
|
19
19
|
import { set404CacheHeaders } from './steps/set-cache-headers.js';
|
|
20
|
-
import { getIncludes } from './steps/utils.js';
|
|
20
|
+
import { getIncludes, getPaginationParams } from './steps/utils.js';
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Returns index as spreadsheet
|
|
23
|
+
* Returns index as spreadsheet with pagination support.
|
|
24
24
|
* @param {SharedTypes.StoredIndex} index
|
|
25
25
|
* @param {Record<string, boolean>} includes
|
|
26
|
+
* @param {{ hasParams?: boolean, limit?: number, offset?: number }} [pagination={}]
|
|
27
|
+
* - Pagination options
|
|
26
28
|
* @returns {{
|
|
27
29
|
* ':type': 'sheet',
|
|
30
|
+
* total: number,
|
|
31
|
+
* offset: number,
|
|
32
|
+
* limit: number,
|
|
28
33
|
* columns: string[],
|
|
29
34
|
* data: Record<string, string>[]
|
|
30
35
|
* }}
|
|
31
36
|
*/
|
|
32
|
-
export function toSpreadsheet(index, includes) {
|
|
37
|
+
export function toSpreadsheet(index, includes, pagination = {}) {
|
|
38
|
+
const {
|
|
39
|
+
limit = pagination.hasParams ? 1000 : Infinity,
|
|
40
|
+
offset = 0,
|
|
41
|
+
} = pagination;
|
|
42
|
+
|
|
33
43
|
const columns = new Set(['sku']);
|
|
34
44
|
|
|
35
45
|
const products = Object.entries(index)
|
|
46
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
36
47
|
.filter(([_, entry]) => includes.all || !entry.filters?.noindex || includes.noindex)
|
|
37
48
|
.reduce((acc, [sluggedSku, entry]) => {
|
|
38
49
|
const product = entry.data;
|
|
@@ -80,10 +91,17 @@ export function toSpreadsheet(index, includes) {
|
|
|
80
91
|
return acc;
|
|
81
92
|
}, []);
|
|
82
93
|
|
|
94
|
+
const total = products.length;
|
|
95
|
+
const len = Math.min(limit, total - offset);
|
|
96
|
+
const paginatedData = products.slice(offset, offset + len);
|
|
97
|
+
|
|
83
98
|
return {
|
|
84
99
|
':type': 'sheet',
|
|
100
|
+
total,
|
|
101
|
+
offset,
|
|
102
|
+
limit: paginatedData.length,
|
|
85
103
|
columns: Array.from(columns),
|
|
86
|
-
data:
|
|
104
|
+
data: paginatedData,
|
|
87
105
|
};
|
|
88
106
|
}
|
|
89
107
|
|
|
@@ -135,7 +153,9 @@ export async function productIndexPipe(state, req) {
|
|
|
135
153
|
|
|
136
154
|
setLastModified(state, res);
|
|
137
155
|
|
|
138
|
-
|
|
156
|
+
const pagination = getPaginationParams(req);
|
|
157
|
+
const spreadsheet = toSpreadsheet(state.content.data, getIncludes(req), pagination);
|
|
158
|
+
res.body = JSON.stringify(spreadsheet, null, 2);
|
|
139
159
|
} catch (e) {
|
|
140
160
|
const errorRes = new PipelineResponse('', {
|
|
141
161
|
/* c8 ignore next 6 */
|
package/src/product-json-pipe.js
CHANGED
|
@@ -15,6 +15,7 @@ import { cleanupHeaderValue } from '@adobe/helix-shared-utils';
|
|
|
15
15
|
import { validatePathInfo } from './utils/path.js';
|
|
16
16
|
import initConfig from './steps/init-config.js';
|
|
17
17
|
import fetchProductBusContent from './steps/fetch-productbus.js';
|
|
18
|
+
import transformImages from './steps/transform-images.js';
|
|
18
19
|
import { setLastModified } from './utils/last-modified.js';
|
|
19
20
|
import { set404CacheHeaders, setProductCacheHeaders } from './steps/set-cache-headers.js';
|
|
20
21
|
|
|
@@ -60,6 +61,8 @@ export async function productJSONPipe(state, req) {
|
|
|
60
61
|
throw new PipelineStatusError(res.status, res.error);
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
transformImages(state);
|
|
65
|
+
|
|
63
66
|
// set surrogate keys
|
|
64
67
|
await setProductCacheHeaders(state, req, res);
|
|
65
68
|
|
|
@@ -55,9 +55,8 @@ export function createOptimizedPicture(src, alt = '', title = undefined) {
|
|
|
55
55
|
height = props.get('height');
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
//
|
|
59
|
-
const
|
|
60
|
-
const isMediaBusFile = filename.startsWith('media_');
|
|
58
|
+
// Matches media bus URLs: /media_{sha1hash}.ext or /media_{sha1hash}/filename.ext
|
|
59
|
+
const isMediaBusFile = /^\/media_[a-f0-9]+(?:\/[^/]+)?\.[a-z0-9]+$/i.test(pathname);
|
|
61
60
|
|
|
62
61
|
// If not a media file, return a simple picture with just an img tag (no optimization)
|
|
63
62
|
if (!isMediaBusFile) {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { appendFilenameToMediaUrl } from '@dylandepass/helix-product-shared';
|
|
14
|
+
|
|
15
|
+
function transformImage(image) {
|
|
16
|
+
if (!image || typeof image !== 'object') {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
image.url = appendFilenameToMediaUrl(image.url, image.filename);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default function transformImages(state) {
|
|
23
|
+
const data = state?.content?.data;
|
|
24
|
+
if (!data || typeof data !== 'object') {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (Array.isArray(data.images)) {
|
|
29
|
+
data.images.forEach(transformImage);
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(data.variants)) {
|
|
32
|
+
data.variants.forEach((variant) => (variant?.images || []).forEach(transformImage));
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/steps/utils.js
CHANGED
|
@@ -125,3 +125,36 @@ export function getIncludes(req) {
|
|
|
125
125
|
}
|
|
126
126
|
return includes;
|
|
127
127
|
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get pagination params from request URL.
|
|
131
|
+
* @param {PipelineRequest} req
|
|
132
|
+
* @returns {{ hasParams?: boolean, limit?: number, offset?: number }}
|
|
133
|
+
*/
|
|
134
|
+
export function getPaginationParams(req) {
|
|
135
|
+
const { searchParams } = req.url;
|
|
136
|
+
const hasParams = searchParams.has('limit') || searchParams.has('offset');
|
|
137
|
+
if (!hasParams) {
|
|
138
|
+
return {};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const result = { hasParams: true };
|
|
142
|
+
|
|
143
|
+
const limitStr = searchParams.get('limit');
|
|
144
|
+
if (limitStr !== null) {
|
|
145
|
+
const limit = parseInt(limitStr, 10);
|
|
146
|
+
if (!Number.isNaN(limit) && limit >= 0) {
|
|
147
|
+
result.limit = limit;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const offsetStr = searchParams.get('offset');
|
|
152
|
+
if (offsetStr !== null) {
|
|
153
|
+
const offset = parseInt(offsetStr, 10);
|
|
154
|
+
if (!Number.isNaN(offset) && offset >= 0) {
|
|
155
|
+
result.offset = offset;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return result;
|
|
160
|
+
}
|