@adobe/helix-html-pipeline 4.1.5 → 5.0.1
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 +19 -0
- package/package.json +10 -6
- package/src/steps/extract-metadata.js +31 -18
- package/src/steps/render.js +30 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## [5.0.1](https://github.com/adobe/helix-html-pipeline/compare/v5.0.0...v5.0.1) (2023-09-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* properly handle empty string ([#395](https://github.com/adobe/helix-html-pipeline/issues/395)) ([daac492](https://github.com/adobe/helix-html-pipeline/commit/daac49224642eeb69951ee68256fcb7f264f4f0c))
|
|
7
|
+
|
|
8
|
+
# [5.0.0](https://github.com/adobe/helix-html-pipeline/compare/v4.1.5...v5.0.0) (2023-09-09)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* better metadata fallback ([2c7481c](https://github.com/adobe/helix-html-pipeline/commit/2c7481c19c52cce38559a50360c8c5eabc201b27)), closes [#382](https://github.com/adobe/helix-html-pipeline/issues/382)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### BREAKING CHANGES
|
|
17
|
+
|
|
18
|
+
* metadata fallback is different for empty values.
|
|
19
|
+
|
|
1
20
|
## [4.1.5](https://github.com/adobe/helix-html-pipeline/compare/v4.1.4...v4.1.5) (2023-09-02)
|
|
2
21
|
|
|
3
22
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-html-pipeline",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Helix HTML Pipeline",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -29,7 +29,10 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/adobe/helix-html-pipeline#readme",
|
|
31
31
|
"mocha": {
|
|
32
|
-
"require":
|
|
32
|
+
"require": [
|
|
33
|
+
"test/setup-env.js",
|
|
34
|
+
"mocha-suppress-logs"
|
|
35
|
+
],
|
|
33
36
|
"recursive": "true",
|
|
34
37
|
"reporter": "mocha-multi-reporters",
|
|
35
38
|
"reporter-options": "configFile=.mocha-multi.json",
|
|
@@ -50,7 +53,7 @@
|
|
|
50
53
|
"hast-util-to-html": "8.0.4",
|
|
51
54
|
"hast-util-to-string": "2.0.0",
|
|
52
55
|
"hastscript": "7.2.0",
|
|
53
|
-
"jose": "4.14.
|
|
56
|
+
"jose": "4.14.6",
|
|
54
57
|
"mdast-util-to-hast": "12.3.0",
|
|
55
58
|
"mdast-util-to-string": "4.0.0",
|
|
56
59
|
"mime": "3.0.0",
|
|
@@ -73,18 +76,19 @@
|
|
|
73
76
|
"@semantic-release/git": "10.0.1",
|
|
74
77
|
"@semantic-release/npm": "10.0.5",
|
|
75
78
|
"c8": "8.0.1",
|
|
76
|
-
"eslint": "8.
|
|
79
|
+
"eslint": "8.49.0",
|
|
77
80
|
"eslint-import-resolver-exports": "1.0.0-beta.5",
|
|
78
81
|
"eslint-plugin-header": "3.1.1",
|
|
79
82
|
"eslint-plugin-import": "2.28.1",
|
|
80
|
-
"esmock": "2.
|
|
83
|
+
"esmock": "2.4.1",
|
|
81
84
|
"husky": "8.0.3",
|
|
82
85
|
"js-yaml": "4.1.0",
|
|
83
86
|
"jsdom": "22.1.0",
|
|
84
87
|
"junit-report-builder": "3.0.1",
|
|
85
|
-
"lint-staged": "
|
|
88
|
+
"lint-staged": "14.0.1",
|
|
86
89
|
"mocha": "10.2.0",
|
|
87
90
|
"mocha-multi-reporters": "1.5.1",
|
|
91
|
+
"mocha-suppress-logs": "0.3.1",
|
|
88
92
|
"semantic-release": "21.1.1"
|
|
89
93
|
},
|
|
90
94
|
"lint-staged": {
|
|
@@ -82,10 +82,7 @@ function readBlockConfig($block) {
|
|
|
82
82
|
value = $img.properties.src;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
// only keep non-empty value
|
|
87
|
-
config[name] = value;
|
|
88
|
-
}
|
|
85
|
+
config[name] = value;
|
|
89
86
|
}
|
|
90
87
|
}
|
|
91
88
|
});
|
|
@@ -159,9 +156,20 @@ export default function extractMetaData(state, req) {
|
|
|
159
156
|
const metaConfig = Object.assign(
|
|
160
157
|
state.metadata.getModifiers(state.info.unmappedPath || state.info.path),
|
|
161
158
|
state.mappedMetadata.getModifiers(state.info.unmappedPath),
|
|
162
|
-
getLocalMetadata(hast),
|
|
163
159
|
);
|
|
164
160
|
|
|
161
|
+
// prune empty values and explicit "" strings from sheet based metadata
|
|
162
|
+
Object.entries(metaConfig).forEach(([name, value]) => {
|
|
163
|
+
if (!value) {
|
|
164
|
+
delete metaConfig[name];
|
|
165
|
+
} else if (value === '""') {
|
|
166
|
+
metaConfig[name] = undefined;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// apply document local overrides
|
|
171
|
+
Object.assign(metaConfig, getLocalMetadata(hast));
|
|
172
|
+
|
|
165
173
|
// first process supported metadata properties
|
|
166
174
|
[
|
|
167
175
|
'title',
|
|
@@ -172,15 +180,18 @@ export default function extractMetaData(state, req) {
|
|
|
172
180
|
'image-alt',
|
|
173
181
|
'canonical',
|
|
174
182
|
'feed',
|
|
183
|
+
'twitter:card',
|
|
175
184
|
].forEach((name) => {
|
|
176
|
-
if (metaConfig
|
|
185
|
+
if (name in metaConfig) {
|
|
177
186
|
meta[name] = metaConfig[name];
|
|
178
187
|
delete metaConfig[name];
|
|
179
188
|
}
|
|
180
189
|
});
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
190
|
+
|
|
191
|
+
if (!('twitter:card' in meta)) {
|
|
192
|
+
// default value for twitter:card (mandatory for rendering URLs as cards in tweets)
|
|
193
|
+
meta['twitter:card'] = 'summary_large_image';
|
|
194
|
+
}
|
|
184
195
|
|
|
185
196
|
// add rest to meta.custom
|
|
186
197
|
meta.custom = metaConfig;
|
|
@@ -193,7 +204,7 @@ export default function extractMetaData(state, req) {
|
|
|
193
204
|
}
|
|
194
205
|
|
|
195
206
|
// complete metadata with insights from content
|
|
196
|
-
if (!meta
|
|
207
|
+
if (!('title' in meta)) {
|
|
197
208
|
// content.title is not correct if the h1 is in a page-block since the pipeline
|
|
198
209
|
// only respects the heading nodes in the mdast
|
|
199
210
|
const $title = select('div h1', hast);
|
|
@@ -202,13 +213,13 @@ export default function extractMetaData(state, req) {
|
|
|
202
213
|
}
|
|
203
214
|
meta.title = content.title;
|
|
204
215
|
}
|
|
205
|
-
if (!meta
|
|
206
|
-
meta.description = extractDescription(hast);
|
|
216
|
+
if (!('description' in meta)) {
|
|
217
|
+
meta.description = extractDescription(hast) || undefined;
|
|
207
218
|
}
|
|
208
219
|
|
|
209
220
|
// use the req.url and not the state.info.path in case of folder mapping
|
|
210
221
|
meta.url = makeCanonicalHtmlUrl(getAbsoluteUrl(state, req.url.pathname));
|
|
211
|
-
if (!meta
|
|
222
|
+
if (!('canonical' in meta)) {
|
|
212
223
|
meta.canonical = meta.url;
|
|
213
224
|
}
|
|
214
225
|
|
|
@@ -222,10 +233,12 @@ export default function extractMetaData(state, req) {
|
|
|
222
233
|
}
|
|
223
234
|
}
|
|
224
235
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
236
|
+
if (!('image' in meta)) {
|
|
237
|
+
meta.image = getAbsoluteUrl(
|
|
238
|
+
state,
|
|
239
|
+
optimizeMetaImage(state.info.path, meta.image || content.image || '/default-meta-image.png'),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
229
242
|
|
|
230
|
-
meta.imageAlt = meta['image-alt']
|
|
243
|
+
meta.imageAlt = meta['image-alt'] ?? content.imageAlt;
|
|
231
244
|
}
|
package/src/steps/render.js
CHANGED
|
@@ -26,7 +26,7 @@ function createElement(name, ...attrs) {
|
|
|
26
26
|
const properties = {};
|
|
27
27
|
for (let i = 0; i < attrs.length; i += 2) {
|
|
28
28
|
const value = attrs[i + 1];
|
|
29
|
-
if (
|
|
29
|
+
if (value === undefined) {
|
|
30
30
|
return null;
|
|
31
31
|
}
|
|
32
32
|
properties[attrs[i]] = value;
|
|
@@ -50,9 +50,10 @@ export default async function render(state, req, res) {
|
|
|
50
50
|
res.document = hast;
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
|
-
const $head = h('head'
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const $head = h('head');
|
|
54
|
+
if (meta.title !== undefined) {
|
|
55
|
+
$head.children.push(h('title', meta.title));
|
|
56
|
+
}
|
|
56
57
|
|
|
57
58
|
// add meta
|
|
58
59
|
// (this is so complicated to keep the order backward compatible to make the diff tests happy)
|
|
@@ -69,27 +70,37 @@ export default async function render(state, req, res) {
|
|
|
69
70
|
'article:published_time': meta.published_time,
|
|
70
71
|
'article:modified_time': meta.modified_time,
|
|
71
72
|
'twitter:card': meta['twitter:card'],
|
|
72
|
-
'twitter:title':
|
|
73
|
-
'twitter:description':
|
|
74
|
-
'twitter:image':
|
|
73
|
+
'twitter:title': '',
|
|
74
|
+
'twitter:description': '',
|
|
75
|
+
'twitter:image': '',
|
|
75
76
|
};
|
|
76
|
-
|
|
77
|
-
for (const name of Object.keys(metadata)) {
|
|
78
|
-
if (!metadata[name]) {
|
|
79
|
-
delete metadata[name];
|
|
80
|
-
}
|
|
81
|
-
}
|
|
77
|
+
|
|
82
78
|
// append custom metadata
|
|
83
79
|
Object.assign(metadata, meta.custom);
|
|
84
80
|
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
// fallback for twitter
|
|
82
|
+
const FALLBACKS = [
|
|
83
|
+
['twitter:title', 'og:title'],
|
|
84
|
+
['twitter:description', 'og:description'],
|
|
85
|
+
['twitter:image', 'og:image'],
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
for (const [from, to] of FALLBACKS) {
|
|
89
|
+
if (!(from in meta.custom)) {
|
|
90
|
+
metadata[from] = metadata[to];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// remove undefined metadata
|
|
95
|
+
for (const name of Object.keys(metadata)) {
|
|
96
|
+
if (metadata[name] === undefined) {
|
|
97
|
+
delete metadata[name];
|
|
98
|
+
}
|
|
88
99
|
}
|
|
89
100
|
|
|
90
|
-
appendElement($head, createElement('link', 'rel', 'canonical', 'href',
|
|
91
|
-
appendElement($head, createElement('meta', 'name', 'description', 'content',
|
|
92
|
-
appendElement($head, createElement('meta', 'name', 'keywords', 'content',
|
|
101
|
+
appendElement($head, createElement('link', 'rel', 'canonical', 'href', meta.canonical));
|
|
102
|
+
appendElement($head, createElement('meta', 'name', 'description', 'content', meta.description));
|
|
103
|
+
appendElement($head, createElement('meta', 'name', 'keywords', 'content', meta.keywords));
|
|
93
104
|
|
|
94
105
|
for (const [name, value] of Object.entries(metadata)) {
|
|
95
106
|
const attr = name.includes(':') && !name.startsWith('twitter:') ? 'property' : 'name';
|