@adobe/helix-html-pipeline 4.1.4 → 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 CHANGED
@@ -1,3 +1,22 @@
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
+
13
+ ## [4.1.5](https://github.com/adobe/helix-html-pipeline/compare/v4.1.4...v4.1.5) (2023-09-02)
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * **deps:** update dependency jose to v4.14.5 ([a6eafdb](https://github.com/adobe/helix-html-pipeline/commit/a6eafdb053814ff58d06572464a8d773c0b389d5))
19
+
1
20
  ## [4.1.4](https://github.com/adobe/helix-html-pipeline/compare/v4.1.3...v4.1.4) (2023-08-24)
2
21
 
3
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-html-pipeline",
3
- "version": "4.1.4",
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": "test/setup-env.js",
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.4",
56
+ "jose": "4.14.5",
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,7 +76,7 @@
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.47.0",
79
+ "eslint": "8.48.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",
@@ -85,7 +88,8 @@
85
88
  "lint-staged": "13.3.0",
86
89
  "mocha": "10.2.0",
87
90
  "mocha-multi-reporters": "1.5.1",
88
- "semantic-release": "21.0.7"
91
+ "mocha-suppress-logs": "0.3.1",
92
+ "semantic-release": "21.1.1"
89
93
  },
90
94
  "lint-staged": {
91
95
  "*.js": "eslint",
@@ -82,10 +82,7 @@ function readBlockConfig($block) {
82
82
  value = $img.properties.src;
83
83
  }
84
84
  }
85
- if (value) {
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[name]) {
183
+ if (name in metaConfig) {
177
184
  meta[name] = metaConfig[name];
178
185
  delete metaConfig[name];
179
186
  }
180
187
  });
181
- // default value for twitter:card (mandatory for rendering URLs as cards in tweets)
182
- meta['twitter:card'] = metaConfig['twitter:card'] || 'summary_large_image';
183
- delete metaConfig['twitter:card'];
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.title) {
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.description) {
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.canonical) {
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
- meta.image = getAbsoluteUrl(
226
- state,
227
- optimizeMetaImage(state.info.path, meta.image || content.image || '/default-meta-image.png'),
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'] || content.imageAlt;
241
+ meta.imageAlt = meta['image-alt'] ?? content.imageAlt;
231
242
  }
@@ -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 (!value) {
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
- h('title', meta.title),
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': meta.title,
73
- 'twitter:description': meta.description,
74
- 'twitter:image': meta.image,
73
+ 'twitter:title': '',
74
+ 'twitter:description': '',
75
+ 'twitter:image': '',
75
76
  };
76
- // remove meta with no values
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
- // remove og:url with explicit removal marker
86
- if (metadata['og:url'] === '""') {
87
- delete metadata['og:url'];
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', content.meta.canonical));
91
- appendElement($head, createElement('meta', 'name', 'description', 'content', content.meta.description));
92
- appendElement($head, createElement('meta', 'name', 'keywords', 'content', content.meta.keywords));
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';