@adobe/helix-html-pipeline 3.7.5 → 3.7.7
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 +7 -7
- package/src/steps/extract-metadata.js +4 -13
- package/src/steps/render.js +41 -19
- package/src/steps/set-custom-response-headers.js +44 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [3.7.7](https://github.com/adobe/helix-html-pipeline/compare/v3.7.6...v3.7.7) (2022-12-08)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* remove og:url meta tag if overridden with "" ([d605625](https://github.com/adobe/helix-html-pipeline/commit/d6056258ab5188362cd6afd94cd860a5dca6880a)), closes [#214](https://github.com/adobe/helix-html-pipeline/issues/214)
|
|
7
|
+
|
|
8
|
+
## [3.7.6](https://github.com/adobe/helix-html-pipeline/compare/v3.7.5...v3.7.6) (2022-11-29)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* improve custom header handling ([#205](https://github.com/adobe/helix-html-pipeline/issues/205)) ([3867fef](https://github.com/adobe/helix-html-pipeline/commit/3867fef601735ed8dc6921c0391bd0de148f0245)), closes [#204](https://github.com/adobe/helix-html-pipeline/issues/204) [#202](https://github.com/adobe/helix-html-pipeline/issues/202) [#199](https://github.com/adobe/helix-html-pipeline/issues/199)
|
|
14
|
+
|
|
1
15
|
## [3.7.5](https://github.com/adobe/helix-html-pipeline/compare/v3.7.4...v3.7.5) (2022-11-18)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-html-pipeline",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.7",
|
|
4
4
|
"description": "Helix HTML Pipeline",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"hast-util-to-html": "8.0.3",
|
|
51
51
|
"hast-util-to-string": "2.0.0",
|
|
52
52
|
"hastscript": "7.1.0",
|
|
53
|
-
"jose": "4.11.
|
|
53
|
+
"jose": "4.11.1",
|
|
54
54
|
"mdast-util-gfm-footnote": "1.0.1",
|
|
55
55
|
"mdast-util-gfm-strikethrough": "1.0.2",
|
|
56
56
|
"mdast-util-gfm-table": "1.0.6",
|
|
@@ -79,20 +79,20 @@
|
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@adobe/eslint-config-helix": "1.3.2",
|
|
81
81
|
"@markedjs/html-differ": "4.0.2",
|
|
82
|
-
"@semantic-release/changelog": "6.0.
|
|
82
|
+
"@semantic-release/changelog": "6.0.2",
|
|
83
83
|
"@semantic-release/git": "10.0.1",
|
|
84
84
|
"@semantic-release/npm": "9.0.1",
|
|
85
85
|
"c8": "7.12.0",
|
|
86
|
-
"eslint": "8.
|
|
86
|
+
"eslint": "8.29.0",
|
|
87
87
|
"eslint-import-resolver-exports": "1.0.0-beta.3",
|
|
88
88
|
"eslint-plugin-header": "3.1.1",
|
|
89
89
|
"eslint-plugin-import": "2.26.0",
|
|
90
|
-
"esmock": "2.0
|
|
90
|
+
"esmock": "2.1.0",
|
|
91
91
|
"husky": "8.0.2",
|
|
92
92
|
"js-yaml": "4.1.0",
|
|
93
|
-
"jsdom": "20.0.
|
|
93
|
+
"jsdom": "20.0.3",
|
|
94
94
|
"junit-report-builder": "3.0.1",
|
|
95
|
-
"lint-staged": "13.0.
|
|
95
|
+
"lint-staged": "13.0.4",
|
|
96
96
|
"mocha": "10.1.0",
|
|
97
97
|
"mocha-multi-reporters": "1.5.1",
|
|
98
98
|
"remark-gfm": "3.0.1",
|
|
@@ -161,8 +161,6 @@ export default function extractMetaData(state, req) {
|
|
|
161
161
|
getLocalMetadata(hast),
|
|
162
162
|
);
|
|
163
163
|
|
|
164
|
-
const IGNORED_CUSTOM_META = ['twitter:card'];
|
|
165
|
-
|
|
166
164
|
// first process supported metadata properties
|
|
167
165
|
[
|
|
168
166
|
'title',
|
|
@@ -179,19 +177,12 @@ export default function extractMetaData(state, req) {
|
|
|
179
177
|
delete metaConfig[name];
|
|
180
178
|
}
|
|
181
179
|
});
|
|
182
|
-
if (Object.keys(metaConfig).length > 0) {
|
|
183
|
-
// add rest to meta.custom
|
|
184
|
-
meta.custom = Object.entries(metaConfig)
|
|
185
|
-
.filter(([name]) => !IGNORED_CUSTOM_META.includes(name))
|
|
186
|
-
.map(([name, value]) => ({
|
|
187
|
-
name,
|
|
188
|
-
value,
|
|
189
|
-
property: name.includes(':'),
|
|
190
|
-
}));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
180
|
// default value for twitter:card (mandatory for rendering URLs as cards in tweets)
|
|
194
181
|
meta['twitter:card'] = metaConfig['twitter:card'] || 'summary_large_image';
|
|
182
|
+
delete metaConfig['twitter:card'];
|
|
183
|
+
|
|
184
|
+
// add rest to meta.custom
|
|
185
|
+
meta.custom = metaConfig;
|
|
195
186
|
|
|
196
187
|
if (meta.keywords) {
|
|
197
188
|
meta.keywords = toList(meta.keywords).join(', ');
|
package/src/steps/render.js
CHANGED
|
@@ -55,29 +55,51 @@ export default async function render(state, req, res) {
|
|
|
55
55
|
]);
|
|
56
56
|
|
|
57
57
|
// add meta
|
|
58
|
+
// (this is so complicated to keep the order backward compatible to make the diff tests happy)
|
|
59
|
+
const metadata = {
|
|
60
|
+
'og:title': meta.title,
|
|
61
|
+
'og:description': meta.description,
|
|
62
|
+
'og:url': meta.url,
|
|
63
|
+
'og:image': meta.image,
|
|
64
|
+
'og:image:secure_url': meta.image,
|
|
65
|
+
'og:image:alt': meta.imageAlt,
|
|
66
|
+
'og:updated_time': meta.modified_time,
|
|
67
|
+
'article:tag': meta.tags || [],
|
|
68
|
+
'article:section': meta.section,
|
|
69
|
+
'article:published_time': meta.published_time,
|
|
70
|
+
'article:modified_time': meta.modified_time,
|
|
71
|
+
'twitter:card': meta['twitter:card'],
|
|
72
|
+
'twitter:title': meta.title,
|
|
73
|
+
'twitter:description': meta.description,
|
|
74
|
+
'twitter:image': meta.image,
|
|
75
|
+
};
|
|
76
|
+
// remove meta with no values
|
|
77
|
+
for (const name of Object.keys(metadata)) {
|
|
78
|
+
if (!metadata[name]) {
|
|
79
|
+
delete metadata[name];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// append custom metadata
|
|
83
|
+
Object.assign(metadata, meta.custom);
|
|
84
|
+
|
|
85
|
+
// remove og:url with explicit removal marker
|
|
86
|
+
if (metadata['og:url'] === '""') {
|
|
87
|
+
delete metadata['og:url'];
|
|
88
|
+
}
|
|
89
|
+
|
|
58
90
|
appendElement($head, createElement('link', 'rel', 'canonical', 'href', content.meta.canonical));
|
|
59
91
|
appendElement($head, createElement('meta', 'name', 'description', 'content', content.meta.description));
|
|
60
92
|
appendElement($head, createElement('meta', 'name', 'keywords', 'content', content.meta.keywords));
|
|
61
|
-
appendElement($head, createElement('meta', 'property', 'og:title', 'content', content.meta.title));
|
|
62
|
-
appendElement($head, createElement('meta', 'property', 'og:description', 'content', content.meta.description));
|
|
63
|
-
appendElement($head, createElement('meta', 'property', 'og:url', 'content', content.meta.url));
|
|
64
|
-
appendElement($head, createElement('meta', 'property', 'og:image', 'content', content.meta.image));
|
|
65
|
-
appendElement($head, createElement('meta', 'property', 'og:image:secure_url', 'content', content.meta.image));
|
|
66
|
-
appendElement($head, createElement('meta', 'property', 'og:image:alt', 'content', content.meta.imageAlt));
|
|
67
|
-
appendElement($head, createElement('meta', 'property', 'og:updated_time', 'content', content.meta.modified_time));
|
|
68
|
-
for (const tag of (meta.tags || [])) {
|
|
69
|
-
appendElement($head, createElement('meta', 'property', 'article:tag', 'content', tag));
|
|
70
|
-
}
|
|
71
|
-
appendElement($head, createElement('meta', 'property', 'article:section', 'content', content.meta.section));
|
|
72
|
-
appendElement($head, createElement('meta', 'property', 'article:published_time', 'content', content.meta.published_time));
|
|
73
|
-
appendElement($head, createElement('meta', 'property', 'article:modified_time', 'content', content.meta.modified_time));
|
|
74
|
-
appendElement($head, createElement('meta', 'name', 'twitter:card', 'content', content.meta['twitter:card']));
|
|
75
|
-
appendElement($head, createElement('meta', 'name', 'twitter:title', 'content', content.meta.title));
|
|
76
|
-
appendElement($head, createElement('meta', 'name', 'twitter:description', 'content', content.meta.description));
|
|
77
|
-
appendElement($head, createElement('meta', 'name', 'twitter:image', 'content', content.meta.image));
|
|
78
93
|
|
|
79
|
-
for (const
|
|
80
|
-
|
|
94
|
+
for (const [name, value] of Object.entries(metadata)) {
|
|
95
|
+
const attr = name.includes(':') && !name.startsWith('twitter:') ? 'property' : 'name';
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
for (const v of value) {
|
|
98
|
+
appendElement($head, createElement('meta', attr, name, 'content', v));
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
appendElement($head, createElement('meta', attr, name, 'content', value));
|
|
102
|
+
}
|
|
81
103
|
}
|
|
82
104
|
appendElement($head, createElement('link', 'rel', 'alternate', 'type', 'application/xml+atom', 'href', meta.feed, 'title', `${meta.title} feed`));
|
|
83
105
|
|
|
@@ -9,7 +9,43 @@
|
|
|
9
9
|
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
function cleanupHeaderValue(value) {
|
|
13
|
+
return value
|
|
14
|
+
.replace(/[^\t\u0020-\u007E\u0080-\u00FF]/g, '')
|
|
15
|
+
.substring(0, 1024 * 64);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Computes the access-control-allow-origin header value.
|
|
20
|
+
* The value can either be a single value or a comma separated list of origin names or patterns.
|
|
21
|
+
* If only a single static value is given (eg `*` or `https://www.adobe.com`), it is used verbatim.
|
|
22
|
+
* If multiple values are given, the one matching the origin request header is used.
|
|
23
|
+
* If any of the values is a regexp, the origin request header is used, if any match is given.
|
|
24
|
+
*
|
|
25
|
+
* @param {PipelineRequest} req
|
|
26
|
+
* @param {string} value
|
|
27
|
+
* @return {string} the access-control-allow-origin header value.
|
|
28
|
+
*/
|
|
29
|
+
function getACAOriginValue(req, value) {
|
|
30
|
+
/** @type string */
|
|
31
|
+
const origin = req.headers.get('origin') || '*';
|
|
32
|
+
const values = value.split(',')
|
|
33
|
+
.map((v) => v.trim());
|
|
34
|
+
|
|
35
|
+
if (values.length === 1 && !values[0].startsWith('/')) {
|
|
36
|
+
return values[0];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const v of values) {
|
|
40
|
+
if (v.startsWith('/') && v.endsWith('/') && new RegExp(v.substring(1, v.length - 1)).test(origin)) {
|
|
41
|
+
return origin;
|
|
42
|
+
}
|
|
43
|
+
if (v === origin) {
|
|
44
|
+
return origin;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
13
49
|
|
|
14
50
|
/**
|
|
15
51
|
* Decorates the pipeline response object with the headers defined in metadata.json.
|
|
@@ -23,7 +59,13 @@ export default function setCustomResponseHeaders(state, req, res) {
|
|
|
23
59
|
Object.entries(state.headers.getModifiers(state.info.path)).forEach(([name, value]) => {
|
|
24
60
|
// only use `link` header for extensionless pipeline
|
|
25
61
|
if (name !== 'link' || (state.type === 'html' && state.info.selector === '')) {
|
|
26
|
-
|
|
62
|
+
let val = cleanupHeaderValue(value);
|
|
63
|
+
if (name === 'access-control-allow-origin') {
|
|
64
|
+
val = getACAOriginValue(req, val);
|
|
65
|
+
}
|
|
66
|
+
if (val) {
|
|
67
|
+
res.headers.set(name, val);
|
|
68
|
+
}
|
|
27
69
|
}
|
|
28
70
|
});
|
|
29
71
|
}
|