Skip to content

Commit

Permalink
Merge pull request #2018 from DivanteLtd/feature/1713_amp_renderer
Browse files Browse the repository at this point in the history
Feature/1713 amp renderer
  • Loading branch information
pkarw authored Nov 22, 2018
2 parents 8b199d3 + 87fa1e5 commit 5c215a4
Show file tree
Hide file tree
Showing 49 changed files with 2,656 additions and 38 deletions.
3 changes: 2 additions & 1 deletion config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"templates": {
"default": "dist/index.html",
"minimal": "dist/index.minimal.html",
"basic": "dist/index.basic.html"
"basic": "dist/index.basic.html",
"amp": "dist/index.amp.html"
},
"executeMixedinAsyncData": true,
"initialStateFilter": ["__DEMO_MODE__", "version", "storeView"],
Expand Down
12 changes: 9 additions & 3 deletions core/build/webpack.base.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const themeCSS = themeRoot + '/css'
const themeApp = themeRoot + '/App.vue'
const themedIndex = path.join(themeRoot, 'index.template.html')
const themedIndexMinimal = path.join(themeRoot, 'index.minimal.template.html')
const themedIndexBasic= path.join(themeRoot, 'index.basic.template.html')
const themedIndexBasic = path.join(themeRoot, 'index.basic.template.html')
const themedIndexAmp = path.join(themeRoot, 'index.amp.template.html')

const translationPreprocessor = require('@vue-storefront/i18n/scripts/translation.preprocessor.js')
translationPreprocessor([
Expand Down Expand Up @@ -57,14 +58,19 @@ module.exports = {
inject: isProd == false // in dev mode we're not using clientManifest therefore renderScripts() is returning empty string and we need to inject scripts using HTMLPlugin
}),
new HTMLPlugin({
template: fs.existsSync(themedIndex) ? themedIndexMinimal : 'src/index.minimal.template.html',
template: fs.existsSync(themedIndexMinimal) ? themedIndexMinimal : 'src/index.minimal.template.html',
filename: 'index.minimal.html',
inject: isProd == false
}),
new HTMLPlugin({
template: fs.existsSync(themedIndex) ? themedIndexBasic: 'src/index.basic.template.html',
template: fs.existsSync(themedIndexBasic) ? themedIndexBasic: 'src/index.basic.template.html',
filename: 'index.basic.html',
inject: isProd == false
}),
new HTMLPlugin({
template: fs.existsSync(themedIndexAmp) ? themedIndexAmp: 'src/index.amp.template.html',
filename: 'index.amp.html',
inject: isProd == false
})
],
devtool: 'source-map',
Expand Down
12 changes: 11 additions & 1 deletion core/pages/Category.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import store from '@vue-storefront/store'
import EventBus from '@vue-storefront/core/compatibility/plugins/event-bus'
import { baseFilterProductsQuery, buildFilterProductsQuery } from '@vue-storefront/store/helpers'
import { htmlDecode } from '@vue-storefront/core/filters/html-decode'

import { localizedRoute } from '@vue-storefront/store/lib/multistore'
import Composite from '@vue-storefront/core/mixins/composite'

export default {
Expand Down Expand Up @@ -262,6 +262,16 @@ export default {
},
metaInfo () {
return {
link: [
{ rel: 'amphtml',
href: this.$router.resolve(localizedRoute({
name: 'category-amp',
params: {
slug: this.category.slug
}
})).href
}
],
title: htmlDecode(this.$route.meta.title || this.categoryName),
meta: this.$route.meta.description ? [{ vmid: 'description', description: htmlDecode(this.$route.meta.description) }] : []
}
Expand Down
14 changes: 13 additions & 1 deletion core/pages/Product.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { mapGetters } from 'vuex'
import store from '@vue-storefront/store'
import EventBus from '@vue-storefront/core/compatibility/plugins/event-bus'
import { htmlDecode, stripHTML } from '@vue-storefront/core/filters'
import { currentStoreView } from '@vue-storefront/store/lib/multistore'
import { currentStoreView, localizedRoute } from '@vue-storefront/store/lib/multistore'
import { CompareProduct } from '@vue-storefront/core/modules/compare/components/Product.ts'
import { AddToCompare } from '@vue-storefront/core/modules/compare/components/AddToCompare.ts'
import { isOptionAvailableAsync } from '@vue-storefront/core/modules/catalog/helpers/index'
Expand Down Expand Up @@ -227,6 +227,18 @@ export default {
metaInfo () {
return {
title: htmlDecode(this.$route.meta.title || this.productName),
link: [
{ rel: 'amphtml',
href: this.$router.resolve(localizedRoute({
name: this.product.type_id + '-product-amp',
params: {
parentSku: this.product.parentSku ? this.product.parentSku : this.product.sku,
slug: this.product.slug,
childSku: this.product.sku
}
})).href
}
],
meta: [{ vmid: 'description', description: this.product.short_description ? stripHTML(htmlDecode(this.product.short_description)) : htmlDecode(stripHTML(this.product.description)) }]
}
}
Expand Down
2 changes: 2 additions & 0 deletions core/scripts/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ app.get('*', (req, res, next) => {
output: {
prepend: (context) => { return '' }, // these functions can be replaced in the Vue components to append or prepend some content AFTER all other things are rendered. So in this function You may call: output.prepend() { return context.renderStyles() } to attach styles
append: (context) => { return '' },
appendHead: (context) => { return '' },
template: 'default',
cacheTags: null
},
Expand Down Expand Up @@ -185,6 +186,7 @@ app.get('*', (req, res, next) => {

output = contentPrepend + output + contentAppend
if (context.output.template) { // case when we've got the template name back from vue app
if (!isProd) context.output.template = 'default' // in dev mode we can not use pre-rendered HTML templates
if (templatesCache[context.output.template]) { // please look at: https://github.com/vuejs/vue/blob/79cabadeace0e01fb63aa9f220f41193c0ca93af/src/server/template-renderer/index.js#L87 for reference
output = templatesCache[context.output.template](context).replace('<!--vue-ssr-outlet-->', output)
} else {
Expand Down
13 changes: 13 additions & 0 deletions src/index.amp.template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html data-vue-meta-server-rendered {{{ meta.inject().htmlAttrs.text() }}} amp>
<head><!-- index for default theme -->
{{{ meta.inject().title.text() }}}
{{{ meta.inject().meta.text() }}}
{{{ meta.inject().link.text() }}}
{{{ output.appendHead() }}}
{{{ renderStyles().replace("<style", "<style amp-custom") }}}
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
3 changes: 2 additions & 1 deletion src/index.basic.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
s.parentNode.insertBefore(wf, s);
})();
</script>
{{{ renderStyles() }}}
{{{ renderStyles() }}}
{{{ output.appendHead() }}}
</head>
<body>
<!--vue-ssr-outlet-->
Expand Down
1 change: 1 addition & 0 deletions src/index.minimal.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
{{{ meta.inject().title.text() }}}
{{{ meta.inject().meta.text() }}}
{{{ meta.inject().link.text() }}}
{{{ output.appendHead() }}}
</head>
<body>
<!--vue-ssr-outlet-->
Expand Down
3 changes: 2 additions & 1 deletion src/index.template.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
})();
</script>
{{{ renderResourceHints() }}}
{{{ renderStyles() }}}
{{{ renderStyles() }}}
{{{ output.appendHead() }}}
</head>
<body>
<!--vue-ssr-outlet-->
Expand Down
19 changes: 19 additions & 0 deletions src/modules/amp-renderer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { VueStorefrontModule, VueStorefrontModuleConfig } from '@vue-storefront/module'
import moduleRoutes from './router'

const store = {
namespaced: true,
state: {
key: null
}
}

const KEY = 'amp-renderer'

const moduleConfig: VueStorefrontModuleConfig = {
key: KEY,
router: { routes: moduleRoutes },
store: { modules: [{ key: KEY, module: store }] }
}

export const AmpRenderer = new VueStorefrontModule(moduleConfig)
13 changes: 13 additions & 0 deletions src/modules/amp-renderer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "@vue-storefront/module-amp-renderer",
"version": "1.0.0",
"description": "Google AMP Renderer for Vue Storefront",
"license": "MIT",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"publishConfig": {
"access": "public"
}
}
2 changes: 2 additions & 0 deletions src/modules/amp-renderer/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import AmpThemeRouting from 'src/themes/default-amp/router'
export default AmpThemeRouting
6 changes: 6 additions & 0 deletions src/modules/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Vue from 'vue'
import { VueStorefrontModule } from '@vue-storefront/module'
import { Catalog } from "@vue-storefront/core/modules/catalog"
import { Cart } from '@vue-storefront/core/modules/cart'
Expand All @@ -14,6 +15,7 @@ import { Claims } from './claims'
import { PromotedOffers } from './promoted-offers'
import { Ui } from './ui-store'
import { GoogleAnalytics } from './google-analytics';
import { AmpRenderer } from './amp-renderer';

// Some modules that still needs API refactoring are temporary registered in core
// This is how you can adjust any module with application-specific behavior
Expand Down Expand Up @@ -43,3 +45,7 @@ export const registerModules: VueStorefrontModule[] = [
GoogleAnalytics
// Example
]

if (Vue.prototype.$isServer) { // extensions that are not required in the SSR mode
registerModules.push(AmpRenderer)
}
170 changes: 170 additions & 0 deletions src/themes/default-amp/components/core/Header.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<template>
<div class="header">
<header
class="fixed w-100 brdr-bottom-1 bg-cl-primary brdr-cl-secondary"
:class="{ 'is-visible': navVisible }"
>
<div class="container px15">
<div class="row between-xs middle-xs" v-if="!isCheckoutPage">
<div class="col-sm-4 col-xs-2 middle-xs" />
<div class="col-xs-2 visible-xs">
<search-icon class="p15 icon pointer" />
</div>
<div class="col-sm-4 col-xs-4 center-xs pt5">
<div>
<logo width="36px" height="41px"/>
</div>
</div>
<div class="col-xs-2 visible-xs">
<wishlist-icon class="p15 icon pointer" />
</div>
<div class="col-sm-4 col-xs-2 end-xs" />
</div>
<div class="row between-xs middle-xs px15 py5" v-if="isCheckoutPage">
<div class="col-xs-5 col-md-3 middle-xs">
<div>
<router-link :to="localizedRoute('/')" class="cl-tertiary links">
{{ $t('Return to shopping') }}
</router-link>
</div>
</div>
<div class="col-xs-2 col-md-6 center-xs">
<logo width="36px" height="41px"/>
</div>
<div class="col-xs-5 col-md-3 end-xs">
<div>
<a v-if="!currentUser" href="#" @click.prevent="gotoAccount" class="cl-tertiary links">
{{ $t('Login to your account') }}
</a>
<span v-else>
{{ $t('You are logged in as') }} {{ currentUser.firstname }}
</span>
</div>
</div>
</div>
</div>
</header>
<div class="header-placeholder"/>
</div>
</template>

<script>
import { mapState } from 'vuex'
import CurrentPage from 'theme/mixins/currentPage'
import Header from 'theme/components/core//blocks/Header/Header'
import AccountIcon from 'theme/components/core/blocks/Header/AccountIcon'
import CompareIcon from 'theme/components/core/blocks/Header/CompareIcon'
import HamburgerIcon from 'theme/components/core/blocks/Header/HamburgerIcon'
import Logo from 'theme/components/core/Logo'
import MicrocartIcon from 'theme/components/core/blocks/Header/MicrocartIcon'
import ReturnIcon from 'theme/components/core/blocks/Header/ReturnIcon'
import SearchIcon from 'theme/components/core/blocks/Header/SearchIcon'
import WishlistIcon from 'theme/components/core/blocks/Header/WishlistIcon'
export default {
components: {
AccountIcon,
CompareIcon,
HamburgerIcon,
Logo,
MicrocartIcon,
ReturnIcon,
SearchIcon,
WishlistIcon
},
mixins: [Header, CurrentPage],
data () {
return {
navVisible: true,
isScrolling: false,
scrollTop: 0,
lastScrollTop: 0,
navbarHeight: 54
}
},
computed: {
...mapState({
isOpenLogin: state => state.ui.signUp,
currentUser: state => state.user.current
})
},
beforeMount () {
window.addEventListener('scroll', () => {
this.isScrolling = true
})
setInterval(() => {
if (this.isScrolling) {
this.hasScrolled()
this.isScrolling = false
}
}, 250)
},
methods: {
gotoAccount () {
this.$bus.$emit('modal-toggle', 'modal-signup')
},
hasScrolled () {
this.scrollTop = window.scrollY
if (this.scrollTop > this.lastScrollTop && this.scrollTop > this.navbarHeight) {
this.navVisible = false
} else {
this.navVisible = true
}
this.lastScrollTop = this.scrollTop
}
}
}
</script>

<style lang="scss" scoped>
@import '~theme/css/variables/colors';
@import '~theme/css/helpers/functions/color';
$color-icon-hover: color(secondary, $colors-background);
header {
height: 54px;
top: -55px;
z-index: 2;
transition: top 0.2s ease-in-out;
&.is-visible {
top: 0;
}
}
.icon {
opacity: 0.6;
&:hover,
&:focus {
background-color: $color-icon-hover;
opacity: 1;
}
}
.right-icons {
//for edge
float: right;
}
.header-placeholder {
height: 54px;
}
.links {
text-decoration: underline;
}
@media (max-width: 767px) {
.row.middle-xs {
margin: 0 -15px;
&.py5 {
margin: 0;
}
}
.col-xs-2:first-of-type {
padding-left: 0;
}
.col-xs-2:last-of-type {
padding-right: 0;
}
a, span {
font-size: 12px;
}
}
</style>
Loading

0 comments on commit 5c215a4

Please sign in to comment.