@adobe/helix-html-pipeline 5.0.0 → 5.0.2

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,17 @@
1
+ ## [5.0.2](https://github.com/adobe/helix-html-pipeline/compare/v5.0.1...v5.0.2) (2023-09-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * supress empty non-fallback values ([#399](https://github.com/adobe/helix-html-pipeline/issues/399)) ([1e5629d](https://github.com/adobe/helix-html-pipeline/commit/1e5629d7cb08de210c713654479bd6d5301a905e))
7
+
8
+ ## [5.0.1](https://github.com/adobe/helix-html-pipeline/compare/v5.0.0...v5.0.1) (2023-09-09)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * properly handle empty string ([#395](https://github.com/adobe/helix-html-pipeline/issues/395)) ([daac492](https://github.com/adobe/helix-html-pipeline/commit/daac49224642eeb69951ee68256fcb7f264f4f0c))
14
+
1
15
  # [5.0.0](https://github.com/adobe/helix-html-pipeline/compare/v4.1.5...v5.0.0) (2023-09-09)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-html-pipeline",
3
- "version": "5.0.0",
3
+ "version": "5.0.2",
4
4
  "description": "Helix HTML Pipeline",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -53,7 +53,7 @@
53
53
  "hast-util-to-html": "8.0.4",
54
54
  "hast-util-to-string": "2.0.0",
55
55
  "hastscript": "7.2.0",
56
- "jose": "4.14.5",
56
+ "jose": "4.14.6",
57
57
  "mdast-util-to-hast": "12.3.0",
58
58
  "mdast-util-to-string": "4.0.0",
59
59
  "mime": "3.0.0",
@@ -76,16 +76,16 @@
76
76
  "@semantic-release/git": "10.0.1",
77
77
  "@semantic-release/npm": "10.0.5",
78
78
  "c8": "8.0.1",
79
- "eslint": "8.48.0",
79
+ "eslint": "8.49.0",
80
80
  "eslint-import-resolver-exports": "1.0.0-beta.5",
81
81
  "eslint-plugin-header": "3.1.1",
82
82
  "eslint-plugin-import": "2.28.1",
83
- "esmock": "2.3.8",
83
+ "esmock": "2.4.1",
84
84
  "husky": "8.0.3",
85
85
  "js-yaml": "4.1.0",
86
86
  "jsdom": "22.1.0",
87
87
  "junit-report-builder": "3.0.1",
88
- "lint-staged": "13.3.0",
88
+ "lint-staged": "14.0.1",
89
89
  "mocha": "10.2.0",
90
90
  "mocha-multi-reporters": "1.5.1",
91
91
  "mocha-suppress-logs": "0.3.1",
@@ -142,12 +142,25 @@ function extractDescription(hast) {
142
142
  }
143
143
 
144
144
  /**
145
- * Extracts the metadata and stores it in the content meta
145
+ * Extracts the metadata from config and the metadata block and stores it in the content.meta.page:
146
+ * - all the non-empty values from the config metadata are applied.
147
+ * - config value with an empty string (explicit "") are removed from the result
148
+ * - all the values from the metadata block are applied. empty values remain empty for the
149
+ * properties that can have a default: 'title', 'description', 'image', 'image-alt', 'url'
150
+ * the others are removed.
151
+ * - the defaults for 'title', 'description', 'image', 'image-alt', 'url' are applied if missing
152
+ * - twitter: properties default to their og: counterparts if missing.
153
+ *
146
154
  * @type PipelineStep
147
155
  * @param {PipelineState} state
148
156
  * @param {PipelineRequest} req
149
157
  */
150
158
  export default function extractMetaData(state, req) {
159
+ const FIXED = new Set([
160
+ 'title', 'description', 'image', 'image-alt', 'url',
161
+ 'twitter:card', 'og:url', 'canonical',
162
+ ]);
163
+
151
164
  const { content } = state;
152
165
  const { meta, hast } = content;
153
166
 
@@ -160,13 +173,19 @@ export default function extractMetaData(state, req) {
160
173
 
161
174
  // prune empty values and explicit "" strings from sheet based metadata
162
175
  Object.entries(metaConfig).forEach(([name, value]) => {
163
- if (!value || value === '""') {
176
+ if (!value) {
164
177
  delete metaConfig[name];
178
+ } else if (value === '""') {
179
+ metaConfig[name] = undefined;
165
180
  }
166
181
  });
167
182
 
168
183
  // apply document local overrides
169
- Object.assign(metaConfig, getLocalMetadata(hast));
184
+ Object.entries(getLocalMetadata(hast)).forEach(([name, value]) => {
185
+ if (value || FIXED.has(name)) {
186
+ metaConfig[name] = value;
187
+ }
188
+ });
170
189
 
171
190
  // first process supported metadata properties
172
191
  [
@@ -191,9 +210,6 @@ export default function extractMetaData(state, req) {
191
210
  meta['twitter:card'] = 'summary_large_image';
192
211
  }
193
212
 
194
- // add rest to meta.custom
195
- meta.custom = metaConfig;
196
-
197
213
  if (meta.keywords) {
198
214
  meta.keywords = toList(meta.keywords).join(', ');
199
215
  }
@@ -234,9 +250,55 @@ export default function extractMetaData(state, req) {
234
250
  if (!('image' in meta)) {
235
251
  meta.image = getAbsoluteUrl(
236
252
  state,
237
- optimizeMetaImage(state.info.path, meta.image || content.image || '/default-meta-image.png'),
253
+ optimizeMetaImage(state.info.path, content.image || '/default-meta-image.png'),
238
254
  );
239
255
  }
240
256
 
241
257
  meta.imageAlt = meta['image-alt'] ?? content.imageAlt;
258
+
259
+ // compute the final page metadata
260
+ const metadata = {
261
+ description: meta.description,
262
+ keywords: meta.keywords,
263
+ 'og:title': meta.title,
264
+ 'og:description': meta.description,
265
+ 'og:url': meta.url,
266
+ 'og:image': meta.image,
267
+ 'og:image:secure_url': meta.image,
268
+ 'og:image:alt': meta.imageAlt,
269
+ 'og:updated_time': meta.modified_time,
270
+ 'article:tag': meta.tags || [],
271
+ 'article:section': meta.section,
272
+ 'article:published_time': meta.published_time,
273
+ 'article:modified_time': meta.modified_time,
274
+ 'twitter:card': meta['twitter:card'],
275
+ 'twitter:title': '',
276
+ 'twitter:description': '',
277
+ 'twitter:image': '',
278
+ };
279
+
280
+ // append custom metadata
281
+ Object.assign(metadata, metaConfig);
282
+
283
+ // fallback for twitter
284
+ const FALLBACKS = [
285
+ ['twitter:title', 'og:title'],
286
+ ['twitter:description', 'og:description'],
287
+ ['twitter:image', 'og:image'],
288
+ ];
289
+
290
+ for (const [from, to] of FALLBACKS) {
291
+ if (!(from in metaConfig)) {
292
+ metadata[from] = metadata[to];
293
+ }
294
+ }
295
+
296
+ // remove undefined metadata
297
+ for (const name of Object.keys(metadata)) {
298
+ if (metadata[name] === undefined) {
299
+ delete metadata[name];
300
+ }
301
+ }
302
+
303
+ meta.page = metadata;
242
304
  }
@@ -55,54 +55,9 @@ export default async function render(state, req, res) {
55
55
  $head.children.push(h('title', meta.title));
56
56
  }
57
57
 
58
- // add meta
59
- // (this is so complicated to keep the order backward compatible to make the diff tests happy)
60
- const metadata = {
61
- 'og:title': meta.title,
62
- 'og:description': meta.description,
63
- 'og:url': meta.url,
64
- 'og:image': meta.image,
65
- 'og:image:secure_url': meta.image,
66
- 'og:image:alt': meta.imageAlt,
67
- 'og:updated_time': meta.modified_time,
68
- 'article:tag': meta.tags || [],
69
- 'article:section': meta.section,
70
- 'article:published_time': meta.published_time,
71
- 'article:modified_time': meta.modified_time,
72
- 'twitter:card': meta['twitter:card'],
73
- 'twitter:title': '',
74
- 'twitter:description': '',
75
- 'twitter:image': '',
76
- };
77
-
78
- // append custom metadata
79
- Object.assign(metadata, meta.custom);
80
-
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
- }
99
- }
100
-
101
58
  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));
104
59
 
105
- for (const [name, value] of Object.entries(metadata)) {
60
+ for (const [name, value] of Object.entries(meta.page)) {
106
61
  const attr = name.includes(':') && !name.startsWith('twitter:') ? 'property' : 'name';
107
62
  if (Array.isArray(value)) {
108
63
  for (const v of value) {