Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/1713 amp renderer #2018

Merged
merged 4 commits into from
Nov 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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