Skip to content
This repository was archived by the owner on Feb 28, 2022. It is now read-only.

Commit 224c665

Browse files
committed
feat(html): enable HAST parsing and serialization of HTML responses
HTML response bodies will now be parsed and then serialized before the final response is created. This creates the opportunity of changing the HTML AST for HTML-post-processing Fixes #285
1 parent aa2538f commit 224c665

File tree

12 files changed

+361
-24
lines changed

12 files changed

+361
-24
lines changed

docs/response.schema.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/response.schema.md

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
"micromatch": "^4.0.0",
6767
"object-hash": "^1.3.1",
6868
"rehype-parse": "^6.0.0",
69-
"rehype-stringify": "^5.0.0",
7069
"remark-parse": "^6.0.0",
7170
"remark-rehype": "^4.0.0",
7271
"request": "^2.87.0",

src/defaults/html.pipe.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const { cache, uncached } = require('../html/shared-cache');
3232
const embeds = require('../html/find-embeds');
3333
const parseFrontmatter = require('../html/parse-frontmatter');
3434
const rewriteLinks = require('../html/static-asset-links');
35+
const tohast = require('../html/html-to-hast');
36+
const tohtml = require('../html/stringify-hast');
3537

3638
/* eslint no-param-reassign: off */
3739
/* eslint newline-per-chained-call: off */
@@ -63,7 +65,9 @@ const htmlpipe = (cont, payload, action) => {
6365
.after(cache).when(uncached)
6466
.after(key)
6567
.after(debug)
68+
.after(tohast) // start HTML post-processing
6669
.after(rewriteLinks).when(production)
70+
.after(tohtml) // end HTML post-processing
6771
.after(flag).expose('esi').when(esi) // flag ESI when there is ESI in the response
6872
.error(selectStatus(production()));
6973

src/html/html-to-hast.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2019 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
const unified = require('unified');
13+
const parse = require('rehype-parse');
14+
15+
function tohast({ response: { body } }) {
16+
const fragment = !body.match(/<html/i);
17+
const hast = unified().use(parse, { fragment }).parse(body);
18+
return {
19+
response: {
20+
hast,
21+
},
22+
};
23+
}
24+
25+
module.exports = tohast;

src/html/static-asset-links.js

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
const unified = require('unified');
14-
const parse = require('rehype-parse');
15-
const stringify = require('rehype-stringify');
1613
const map = require('unist-util-map');
1714
const Url = require('url-parse');
1815

@@ -51,25 +48,14 @@ function links() {
5148
};
5249
}
5350

54-
function rewrite({ response: { body, headers } }) {
55-
if (headers && headers['Content-Type'] && headers['Content-Type'].match(/html/)) {
56-
const doc = unified()
57-
.use(parse, {
58-
fragment: false,
59-
})
60-
.use(scripts)
61-
.use(links)
62-
.use(stringify, {
63-
allowParseErrors: true,
64-
allowDangerousHTML: true,
65-
allowDangerousCharacters: true,
66-
quoteSmart: true,
51+
function rewrite({ response: { hast, headers } }) {
52+
if (headers && headers['Content-Type'] && headers['Content-Type'].match(/html/) && hast) {
53+
links()(hast);
54+
scripts()(hast);
6755

68-
})
69-
.processSync(body);
7056
return {
7157
response: {
72-
body: doc.contents,
58+
hast,
7359
},
7460
};
7561
}

src/html/stringify-hast.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2019 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
const serialize = require('hast-util-to-html');
13+
14+
function stringify({ response: { hast } }) {
15+
const body = serialize(hast, {
16+
allowParseErrors: true,
17+
allowDangerousHTML: true,
18+
allowDangerousCharacters: true,
19+
quoteSmart: true,
20+
entities: {
21+
useNamedReferences: true,
22+
},
23+
});
24+
25+
return {
26+
response: {
27+
body,
28+
},
29+
};
30+
}
31+
32+
module.exports = stringify;

src/schemas/response.schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
}
3535
]
3636
},
37+
"hast": {
38+
"type": "object",
39+
"description": "The Hypertext AST of the reponse body"
40+
},
3741
"headers": {
3842
"description": "The HTTP headers of the response",
3943
"additionalProperties": {

test/testEmbedHandler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,6 @@ https://www.youtube.com/watch?v=KOxbO0EI4MA
115115
assert.equal(result.response.body, `<p>Hello World
116116
Here comes an embed.</p>
117117
<esi:include src="https://example-embed-service.com/https://www.youtube.com/watch?v=KOxbO0EI4MA"></esi:include>
118-
<p><img src="easy.png" alt="Easy!" srcset="easy.png?width=480&amp;auto=webp 480w,easy.png?width=1384&amp;auto=webp 1384w,easy.png?width=2288&amp;auto=webp 2288w,easy.png?width=3192&amp;auto=webp 3192w,easy.png?width=4096&amp;auto=webp 4096w" sizes="100vw"></p>`);
118+
<p><img src="easy.png" alt="Easy!" srcset="easy.png?width=480&amp;auto=webp 480w, easy.png?width=1384&amp;auto=webp 1384w, easy.png?width=2288&amp;auto=webp 2288w, easy.png?width=3192&amp;auto=webp 3192w, easy.png?width=4096&amp;auto=webp 4096w" sizes="100vw"></p>`);
119119
});
120120
});

test/testRewriteStatic.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,36 @@
1313
const assert = require('assert');
1414
const { Logger } = require('@adobe/helix-shared');
1515
const rewrite = require('../src/html/static-asset-links');
16+
const tohast = require('../src/html/html-to-hast');
17+
const stringify = require('../src/html/stringify-hast');
1618
const { pipe } = require('../src/defaults/html.pipe.js');
1719

18-
1920
const logger = Logger.getTestLogger({
2021
// tune this for debugging
2122
level: 'info',
2223
});
2324

2425
function rw(content) {
25-
return rewrite({
26+
const hastcontext = tohast({
2627
response: {
2728
body: content,
2829
headers: {
2930
'Content-Type': 'text/html',
3031
},
3132
},
32-
}).response.body;
33+
});
34+
35+
const rewritecontext = rewrite({
36+
response: {
37+
body: content,
38+
hast: hastcontext.response.hast,
39+
headers: {
40+
'Content-Type': 'text/html',
41+
},
42+
},
43+
});
44+
45+
return stringify(rewritecontext).response.body;
3346
}
3447

3548
describe('Integration Test Static Asset Rewriting', () => {

0 commit comments

Comments
 (0)