@adobe/helix-html-pipeline 4.1.5 → 5.0.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 +12 -0
- package/package.json +6 -2
- package/src/steps/extract-metadata.js +29 -18
- package/src/steps/render.js +30 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# [5.0.0](https://github.com/adobe/helix-html-pipeline/compare/v4.1.5...v5.0.0) (2023-09-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* better metadata fallback ([2c7481c](https://github.com/adobe/helix-html-pipeline/commit/2c7481c19c52cce38559a50360c8c5eabc201b27)), closes [#382](https://github.com/adobe/helix-html-pipeline/issues/382)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### BREAKING CHANGES
|
|
10
|
+
|
|
11
|
+
* metadata fallback is different for empty values.
|
|
12
|
+
|
|
1
13
|
## [4.1.5](https://github.com/adobe/helix-html-pipeline/compare/v4.1.4...v4.1.5) (2023-09-02)
|
|
2
14
|
|
|
3
15
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-html-pipeline",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
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",
|
|
@@ -85,6 +88,7 @@
|
|
|
85
88
|
"lint-staged": "13.3.0",
|
|
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,18 @@ 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 || value === '""') {
|
|
164
|
+
delete metaConfig[name];
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// apply document local overrides
|
|
169
|
+
Object.assign(metaConfig, getLocalMetadata(hast));
|
|
170
|
+
|
|
165
171
|
// first process supported metadata properties
|
|
166
172
|
[
|
|
167
173
|
'title',
|
|
@@ -172,15 +178,18 @@ export default function extractMetaData(state, req) {
|
|
|
172
178
|
'image-alt',
|
|
173
179
|
'canonical',
|
|
174
180
|
'feed',
|
|
181
|
+
'twitter:card',
|
|
175
182
|
].forEach((name) => {
|
|
176
|
-
if (metaConfig
|
|
183
|
+
if (name in metaConfig) {
|
|
177
184
|
meta[name] = metaConfig[name];
|
|
178
185
|
delete metaConfig[name];
|
|
179
186
|
}
|
|
180
187
|
});
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
188
|
+
|
|
189
|
+
if (!('twitter:card' in meta)) {
|
|
190
|
+
// default value for twitter:card (mandatory for rendering URLs as cards in tweets)
|
|
191
|
+
meta['twitter:card'] = 'summary_large_image';
|
|
192
|
+
}
|
|
184
193
|
|
|
185
194
|
// add rest to meta.custom
|
|
186
195
|
meta.custom = metaConfig;
|
|
@@ -193,7 +202,7 @@ export default function extractMetaData(state, req) {
|
|
|
193
202
|
}
|
|
194
203
|
|
|
195
204
|
// complete metadata with insights from content
|
|
196
|
-
if (!meta
|
|
205
|
+
if (!('title' in meta)) {
|
|
197
206
|
// content.title is not correct if the h1 is in a page-block since the pipeline
|
|
198
207
|
// only respects the heading nodes in the mdast
|
|
199
208
|
const $title = select('div h1', hast);
|
|
@@ -202,13 +211,13 @@ export default function extractMetaData(state, req) {
|
|
|
202
211
|
}
|
|
203
212
|
meta.title = content.title;
|
|
204
213
|
}
|
|
205
|
-
if (!meta
|
|
206
|
-
meta.description = extractDescription(hast);
|
|
214
|
+
if (!('description' in meta)) {
|
|
215
|
+
meta.description = extractDescription(hast) || undefined;
|
|
207
216
|
}
|
|
208
217
|
|
|
209
218
|
// use the req.url and not the state.info.path in case of folder mapping
|
|
210
219
|
meta.url = makeCanonicalHtmlUrl(getAbsoluteUrl(state, req.url.pathname));
|
|
211
|
-
if (!meta
|
|
220
|
+
if (!('canonical' in meta)) {
|
|
212
221
|
meta.canonical = meta.url;
|
|
213
222
|
}
|
|
214
223
|
|
|
@@ -222,10 +231,12 @@ export default function extractMetaData(state, req) {
|
|
|
222
231
|
}
|
|
223
232
|
}
|
|
224
233
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
234
|
+
if (!('image' in meta)) {
|
|
235
|
+
meta.image = getAbsoluteUrl(
|
|
236
|
+
state,
|
|
237
|
+
optimizeMetaImage(state.info.path, meta.image || content.image || '/default-meta-image.png'),
|
|
238
|
+
);
|
|
239
|
+
}
|
|
229
240
|
|
|
230
|
-
meta.imageAlt = meta['image-alt']
|
|
241
|
+
meta.imageAlt = meta['image-alt'] ?? content.imageAlt;
|
|
231
242
|
}
|
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';
|