Skip to content

Instantly share code, notes, and snippets.

@Grawl
Created March 11, 2020 10:33
Show Gist options
  • Save Grawl/ee90f967e7104142cc36516c7026ea39 to your computer and use it in GitHub Desktop.
Save Grawl/ee90f967e7104142cc36516c7026ea39 to your computer and use it in GitHub Desktop.
import posthtml from 'posthtml'
export default {
async rendered(response, context, app) {
// TODO try to use route meta instead?
if (context.url.endsWith('/amp')) {
const html = response.body
const transform = new TransformHTML(html)
response.body = await transform.transform()
}
},
}
class TransformHTML {
constructor(html) {
this.html = html
// TODO move this to separate files
this.AMPScript = `<script async src='https://cdn.ampproject.org/v0.js'></script>`
this.AMPStyle = `
<style amp-boilerplate>
body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}
</style>
<noscript>
<style amp-boilerplate>
body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}
</style>
</noscript>
`
this.plugin = new TransformHTMLPlugin()
}
async transform() {
return posthtml([
require('posthtml-minifier')({
removeComments: true,
minifyJS: true,
minifyCSS: true,
}),
require('posthtml-collect-styles')(),
this.plugin.transform(),
require('posthtml-insert-at').default([
{
selector: 'title',
append: this.AMPScript,
behavior: 'outside',
},
{
selector: 'title',
append: this.AMPStyle,
behavior: 'outside',
},
]),
])
.process(this.html)
.then(result => result.html)
.catch(error => {
console.log({ error })
})
}
}
class TransformHTMLPlugin {
constructor() {
this.attrsToDelete = [
'data-vue-meta',
'data-server-rendered',
]
}
transform() {
return tree => {
this.rootAttrs(tree)
this.removeAttrs(tree)
this.removeLinks(tree)
this.removeScripts(tree)
this.AMP(tree)
return tree
}
}
rootAttrs(tree) {
tree.match({ tag: 'html' }, html => {
delete html.attrs['data-vue-meta-server-rendered']
delete html.attrs['data-vue-meta']
html.attrs['amp'] = ''
return html
})
}
removeAttrs(tree) {
tree.walk(node => {
if (node && node.attrs) {
this.attrsToDelete.forEach(attr => {
if (this.attrsToDelete.some(attrToDelete => attrToDelete === attr)) {
delete node.attrs[attr]
}
})
}
return node
})
}
removeLinks(tree) {
tree.match({ tag: 'link' }, link => {
if (
link.attrs &&
(
link.attrs['rel'] === 'preload' ||
link.attrs['rel'] === 'prefetch'
)
) {
return ''
}
return link
})
}
removeScripts(tree) {
tree.match({ tag: 'body' }, body => {
body.content = body.content.map(child => {
if (
typeof child === 'object' &&
child.tag === 'script'
) {
return ''
}
return child
})
return body
})
}
AMP(tree) {
tree.match({ tag: 'style' }, style => {
if (!style.attrs) {
style.attrs = {}
}
style.attrs['amp-custom'] = ''
return style
})
// TODO use amp-img instead of <img> and/or <picture>
tree.match({ tag: 'img' }, img => {
img.tag = 'amp-img'
return img
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment