Skip to content

Commit

Permalink
feat(static): add ESI aliasing support for JavaScript modules
Browse files Browse the repository at this point in the history
Same as 396c55b but for JavaScript. For now, only `import` statements are affected

depends on adobe/helix-publish#61 and contributes to adobe/helix-pipeline#224 (comment)
  • Loading branch information
trieloff committed Apr 20, 2019
1 parent 396c55b commit 1fa564d
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 27 deletions.
115 changes: 106 additions & 9 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@adobe/helix-shared": "0.10.3",
"@adobe/helix-simulator": "2.12.2",
"@adobe/parcel-plugin-htl": "2.1.4",
"@babel/core": "^7.4.3",
"@parcel/logger": "1.11.0",
"@snyk/nodejs-runtime-agent": "1.43.0",
"ajv": "^6.10.0",
Expand Down
62 changes: 53 additions & 9 deletions src/openwhisk/static.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const mime = require('mime-types');
const postcss = require('postcss');
const postcssurl = require('postcss-url');
const parser = require('postcss-value-parser');
const babel = require('@babel/core');
const ohash = require('object-hash');

const { space } = postcss.list;
const uri = require('uri-js');
Expand Down Expand Up @@ -47,6 +49,15 @@ function error(message, code = 500) {
};
}


function isCSS(type) {
return type === 'text/css';
}

function isJavaScript(type) {
return type.match(/(text|application)\/(x-)?(javascript|ecmascript)/);
}

function addHeaders(headers, ref, content) {
let cacheheaders = {};
if (ref.match(/[a-f0-9]{40}/)) {
Expand All @@ -59,6 +70,12 @@ function addHeaders(headers, ref, content) {
ETag: `"${hash.digest('base64')}"`,
'Cache-Control': 's-max-age=300',
};
if (headers['Content-Type'] && (
isCSS(headers['Content-Type'])
|| isJavaScript(headers['Content-Type'])
) && content.toString().match(/<esi:include/)) {
cacheheaders['X-ESI'] = true;
}
}
return Object.assign(headers, cacheheaders);
}
Expand All @@ -80,14 +97,6 @@ function isBinary(type) {
return true;
}

function isCSS(type) {
return type === 'text/css';
}

function isJavaScript(type) {
return type.match(/(text|application)\/(x-)?(javascript|ecmascript)/);
}

function rewriteImports(tree) {
tree.walkAtRules('import', (rule) => {
if (rule.name === 'import') {
Expand Down Expand Up @@ -143,7 +152,42 @@ function rewriteCSS(css) {
}

function rewriteJavaScript(javascript) {
return javascript;
const importmap = {};

function rewriteJSImports(bab) {
const t = bab.types;
return {
visitor: {
ImportDeclaration(path) {
if (path
&& path.node
&& path.node.source
&& path.node.source.value
&& !importmap[path.node.source.value]) {
const srcuri = uri.parse(path.node.source.value);
if (srcuri.reference === 'relative' && !srcuri.query) {
const { specifiers } = path.node;
// console.log(srcuri);
const h = ohash(srcuri.path);
importmap[h] = `<esi:include src="${srcuri.path}.esi"/><esi:remove>${path.node.source.value}</esi:remove>`;
path.replaceWith(t.importDeclaration(specifiers, t.stringLiteral(h)));
}
}
return false;
},
},
};
}

try {
const transformed = babel.transformSync(javascript,
{ plugins: [rewriteJSImports], retainLines: true });

return Object.keys(importmap)
.reduce((src, key) => src.replace(key, importmap[key]), transformed.code);
} catch (e) {
return javascript;
}
}

function isJSON(type) {
Expand Down
19 changes: 10 additions & 9 deletions test/testStatic.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,22 +143,23 @@ describe('CSS and JS Rewriting', () => {
}`, true), `.element {
background: url("<esi:include src="sprite.png.esi"/><esi:remove>sprite.png</esi:remove>");
}`);
assert.equal(await index.getBody('text/css',
'@import "fineprint.css" print;', true),
'@import "<esi:include src="fineprint.css.esi"/><esi:remove>fineprint.css</esi:remove>" print;');
assert.equal(await index.getBody('text/css',
'@import \'fineprint.css\' print;', true),
'@import \'<esi:include src="fineprint.css.esi"/><esi:remove>fineprint.css</esi:remove>\' print;');
assert.equal(await index.getBody('text/css',
assert.equal(await index.getBody('text/css',
'@import "fineprint.css" print;', true),
'@import "<esi:include src="fineprint.css.esi"/><esi:remove>fineprint.css</esi:remove>" print;');
assert.equal(await index.getBody('text/css',
'@import \'fineprint.css\' print;', true),
'@import \'<esi:include src="fineprint.css.esi"/><esi:remove>fineprint.css</esi:remove>\' print;');
assert.equal(await index.getBody('text/css',
'@import url(\'fineprint.css\') print;', true),
'@import url(\'<esi:include src="fineprint.css.esi"/><esi:remove>fineprint.css</esi:remove>\') print;');
assert.equal(await index.getBody('text/css',
assert.equal(await index.getBody('text/css',
'@import url("fineprint.css") print;', true),
'@import url("<esi:include src="fineprint.css.esi"/><esi:remove>fineprint.css</esi:remove>") print;');
});

it('Rewrite JS', async () => {
assert.equal(await index.getBody('text/javascript', '', true), '');
assert.equal(await index.getBody('text/javascript', 'import { transform } from "@babel/core";code();', true),
'import { transform } from "<esi:include src="@babel/core.esi"/><esi:remove>@babel/core</esi:remove>";code();');
});
});

Expand Down

0 comments on commit 1fa564d

Please sign in to comment.