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

Commit

Permalink
feat(html): rewrite relative asset references to ESI resources
Browse files Browse the repository at this point in the history
The `script.js.esi` or `style.css.esi` resource will be replaced with a long-cacheable URL

implements #267
  • Loading branch information
trieloff committed Apr 23, 2019
1 parent 84f38a2 commit dce696e
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 11 deletions.
28 changes: 17 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"micromatch": "^4.0.0",
"object-hash": "^1.3.1",
"rehype-parse": "^6.0.0",
"rehype-stringify": "^5.0.0",
"remark-parse": "^6.0.0",
"remark-rehype": "^4.0.0",
"request": "^2.87.0",
Expand All @@ -77,6 +78,7 @@
"unist-util-map": "^1.0.4",
"unist-util-select": "^2.0.0",
"uri-js": "^4.2.2",
"url-parse": "^1.4.6",
"winston": "^3.0.0",
"xml2js": "^0.4.19",
"xmlbuilder": "^12.0.0"
Expand Down
79 changes: 79 additions & 0 deletions src/html/static-asset-links.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

const unified = require('unified');
const parse = require('rehype-parse');
const stringify = require('rehype-stringify');
const map = require('unist-util-map');
const Url = require('url-parse');

function scripts() {
return function transformer(tree) {
map(tree, (node) => {
if (node.tagName === 'script'
&& node.properties
&& node.properties.src) {
const src = new Url(node.properties.src);
if (src.host === '' && src.query === '' && src.pathname) {
// eslint-disable-next-line no-param-reassign
node.properties.src = `<esi:include src="${src.pathname}.esi"/><esi:remove>${node.properties.src}</esi:remove>`;
}
}
});
};
}

function links() {
return function transformer(tree) {
map(tree, (node) => {
if (node.tagName === 'link'
&& node.properties
&& node.properties.rel
&& Array.isArray(node.properties.rel)
&& node.properties.rel.indexOf('stylesheet') !== -1
&& node.properties.href) {
const href = new Url(node.properties.href);
if (href.host === '' && href.query === '' && href.pathname) {
// eslint-disable-next-line no-param-reassign
node.properties.href = `<esi:include src="${href.pathname}.esi"/><esi:remove>${node.properties.href}</esi:remove>`;
}
}
});
};
}

function rewrite({ response: { body, headers } }) {
if (headers && headers['Content-Type'] && headers['Content-Type'].match(/html/)) {
const doc = unified()
.use(parse, {
fragment: false,
})
.use(scripts)
.use(links)
.use(stringify, {
allowParseErrors: true,
allowDangerousHTML: true,
allowDangerousCharacters: true,
quoteSmart: true,

})
.processSync(body);
return {
response: {
body: doc.contents,
},
};
}
return {};
}

module.exports = rewrite;
86 changes: 86 additions & 0 deletions test/testRewriteStatic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
/* eslint-env mocha */
const assert = require('assert');
const rewrite = require('../src/html/static-asset-links');

function rw(content) {
return rewrite({
response: {
body: content,
headers: {
'Content-Type': 'text/html',
},
},
}).response.body;
}

describe('Test Static Asset Rewriting', () => {
it('Ignores non-HTML', () => {
assert.deepEqual(rewrite({
response: {
body: '{}',
headers: {
'Content-Type': 'application/json',
},
},
}), {});
});

it('Load simple HTML', async () => {
assert.equal(rw(`<!doctype html><html><head>
<title>Normal</title>
</head>
<body>Just normal things
</body></html>`), `<!doctype html><html><head>
<title>Normal</title>
</head>
<body>Just normal things
</body></html>`);
});

it('ESI include script tags HTML', async () => {
assert.equal(rw(`<!doctype html><html><head>
<title>Normal</title>
<script src="test.js"></script>
<script src="keep.js?cached=false"></script>
<script src="//code.jquery.com/jquery-3.4.0.min.js"></script>
</head>
<body>Just normal things
</body></html>`), `<!doctype html><html><head>
<title>Normal</title>
<script src='<esi:include src="test.js.esi"/><esi:remove>test.js</esi:remove>'></script>
<script src="keep.js?cached=false"></script>
<script src="//code.jquery.com/jquery-3.4.0.min.js"></script>
</head>
<body>Just normal things
</body></html>`);
});

it('ESI include link tags HTML', async () => {
assert.equal(rw(`<!doctype html><html><head>
<title>Normal</title>
<link rel="stylesheet foo" href="test.css">
<link rel="stylesheet" href="keep.css?cached=false">
<link rel="stylesheet" href="//code.jquery.com/jquery-3.4.0.min.css">
</head>
<body>Just normal things
</body></html>`), `<!doctype html><html><head>
<title>Normal</title>
<link rel="stylesheet foo" href='<esi:include src="test.css.esi"/><esi:remove>test.css</esi:remove>'>
<link rel="stylesheet" href="keep.css?cached=false">
<link rel="stylesheet" href="//code.jquery.com/jquery-3.4.0.min.css">
</head>
<body>Just normal things
</body></html>`);
});
});

0 comments on commit dce696e

Please sign in to comment.