Skip to content
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ You can find some tutorials and explainations on our [YouTube channel](https://w

### Vue Storefront core and themes
* [Working with themes](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/themes/Working%20with%20themes.md)
* [Layouts and advanced output operations](https://github.com/DivanteLtd/vue-storefront/blob/develop/doc/Layouts%20and%20advanced%20output%20operations.md)
* [Working with Vue Storefront core components](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/components/Working%20with%20components.md)
* [Working with UI Store (interface state)](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/Working%20with%20UI%20Store%20(interface%20state).md)
* [Working with translations](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/i18n/Working%20with%20translations.md)
Expand All @@ -153,6 +154,7 @@ Tutorial series on creating themes for Vue Storefront:
* [Working with extensions](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/extensions/Working%20with%20extensions.md)
* [Adding custom Server API methods](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/Extending%20vue-storefront-api.md)
* [Extending UI from extensions](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/components/Extending%20UI%20from%20extensions.md)
* [Adding Express routes and middlewares](https://github.com/DivanteLtd/vue-storefront/blob/master/doc/Extending%20Express.js%20server%20side%20routes.md)

### Integrations
* [Vue Storefront + Magento](https://github.com/DivanteLtd/mage2vuestorefront)
Expand Down
5 changes: 5 additions & 0 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
"ssrTimeout": 1000
},
"ssr": {
"templates": {
"default": "dist/index.html",
"minimal": "dist/index.minimal.html",
"basic": "dist/index.basic.html"
},
"executeMixedinAsyncData": true,
"initialStateFilter": ["config", "__DEMO_MODE__", "version", "storeView"],
"useInitialStateFilter": true
Expand Down
1 change: 0 additions & 1 deletion core/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ export function createApp (serverContext = null): { app: Vue, router: any, store
provide: apolloProvider,
render: h => h(App)
})

registerExtensions(
union(extensionEntryPoints, themeExtensionEntryPoints),
app,
Expand Down
18 changes: 17 additions & 1 deletion core/build/webpack.base.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const fs = require('fs')
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const autoprefixer = require('autoprefixer')
const HTMLPlugin = require('html-webpack-plugin')

fs.writeFileSync(
path.resolve(__dirname, './config.json'),
Expand All @@ -17,6 +18,9 @@ const themeRoot = require('./theme-path')
const themeResources = themeRoot + '/resource'
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 translationPreprocessor = require('@vue-storefront/i18n/scripts/translation.preprocessor.js')
translationPreprocessor([
Expand All @@ -40,7 +44,19 @@ const postcssConfig = {
module.exports = {
plugins: [
new CaseSensitivePathsPlugin(),
new VueLoaderPlugin()
new VueLoaderPlugin(),
// generate output HTML
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this files should be provided in config or exported from some place that develoeprs can reach (not from core)?

new HTMLPlugin({
template: fs.existsSync(themedIndex) ? themedIndex : 'src/index.template.html'
}),
new HTMLPlugin({
template: fs.existsSync(themedIndex) ? themedIndexMinimal : 'src/index.minimal.template.html',
filename: 'index.minimal.html'
}),
new HTMLPlugin({
template: fs.existsSync(themedIndex) ? themedIndexBasic: 'src/index.basic.template.html',
filename: 'index.basic.html'
})
],
devtool: 'source-map',
entry: {
Expand Down
8 changes: 0 additions & 8 deletions core/build/webpack.client.config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
const webpack = require('webpack')
const merge = require('webpack-merge')
const base = require('./webpack.base.config')
const HTMLPlugin = require('html-webpack-plugin')
const path = require('path')
const fs = require('fs')
const themeRoot = require('./theme-path')
const themedIndex = path.join(themeRoot, 'index.template.html')

const config = merge(base, {
output: {
Expand Down Expand Up @@ -35,10 +31,6 @@ const config = merge(base, {
// strip dev-only code in Vue source
new webpack.DefinePlugin({
'process.env.VUE_ENV': '"client"'
}),
// generate output HTML
new HTMLPlugin({
template: fs.existsSync(themedIndex) ? themedIndex : 'src/index.template.html'
})
]
})
Expand Down
2 changes: 1 addition & 1 deletion core/components/blocks/Microcart/Product.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default {
},
methods: {
removeItem () {
this.$store.dispatch('cart/removeItem', this.product)
this.$store.dispatch('cart/removeItem', { product: this.product })
},
onProductChanged (event) {
if (event.item.sku === this.product.sku) {
Expand Down
2 changes: 1 addition & 1 deletion core/modules/cart/features/removeFromCart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import CartItem from '../types/CartItem'
export const removeFromCart = {
methods: {
removeFromCart (item: CartItem) {
this.$store.dispatch('cart/removeItem', item)
this.$store.dispatch('cart/removeItem', { product: item })
}
}
}
92 changes: 65 additions & 27 deletions core/scripts/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ const resolve = file => path.resolve(rootPath, file)
const config = require('config')
const TagCache = require('redis-tag-cache').default
const utils = require('./server/utils')

const compile = require('lodash.template')
const compileOptions = {
escape: /{{([^{][\s\S]+?[^}])}}/g,
interpolate: /{{{([\s\S]+?)}}}/g
}
const isProd = process.env.NODE_ENV === 'production'
process.noDeprecation = true

Expand All @@ -21,28 +25,37 @@ if (config.server.useOutputCache) {
console.log('Redis cache set', config.redis)
}

const templatesCache = {}
let renderer
for (const tplName of Object.keys(config.ssr.templates)) {
const fileName = resolve(config.ssr.templates[tplName])
if (fs.existsSync(fileName)) {
const template = fs.readFileSync(fileName, 'utf-8')
templatesCache[tplName] = compile(template, compileOptions)
}
}

if (isProd) {
// In production: create server renderer using server bundle and index HTML
// template from real fs.
// The server bundle is generated by vue-ssr-webpack-plugin.
const bundle = require(resolve('dist/vue-ssr-bundle.json'))
// src/index.template.html is processed by html-webpack-plugin to inject
// build assets and output as dist/index.html.
const template = fs.readFileSync(resolve('dist/index.html'), 'utf-8')
renderer = createRenderer(bundle, template)
// TODO: Add dynamic templates loading from (config based?) list
renderer = createRenderer(bundle)
} else {
// In development: setup the dev server with watch and hot-reload,
// and create a new renderer on bundle / index template update.
require(resolve('core/build/dev-server'))(app, (bundle, template) => {
renderer = createRenderer(bundle, template)
templatesCache['default'] = compile(template, compileOptions) // Important Notice: template switching doesn't work with dev server because of the HMR
renderer = createRenderer(bundle)
})
}

function createRenderer (bundle, template) {
// https://github.com/vuejs/vue/blob/dev/packages/vue-server-renderer/README.md#why-use-bundlerenderer
return require('vue-server-renderer').createBundleRenderer(bundle, {
template,
cache: require('lru-cache')({
max: 1000,
maxAge: 1000 * 60 * 15
Expand Down Expand Up @@ -115,7 +128,7 @@ app.get('*', (req, res, next) => {
} else {
// Render Error Page or Redirect
// TODO: Add error page handler
res.status(500).end('500 | Internal Server Error')
res.status(500).end('500 | Internal Server Error. Check Server console for details.')
console.error(`Error during render : ${req.url}`)
console.error(err)
next()
Expand All @@ -137,44 +150,69 @@ app.get('*', (req, res, next) => {
' </html>')
return next()
}
const context = { url: req.url, storeCode: req.header('x-vs-store-code') ? req.header('x-vs-store-code') : process.env.STORE_CODE, server: { app: app, res: res, req: req } }
if (config.server.useOutputCacheTagging) {
renderer.renderToString(context).then(output => {
const context = {
url: req.url,
renderPrepend: (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: renderPrepend() { return context.renderStyles() } to attach styles
renderAppend: (context) => { return '' },
serverOutputTemplate: 'default',
meta: null,
currentRoute: null/** will be set by Vue */,
storeCode: req.header('x-vs-store-code') ? req.header('x-vs-store-code') : process.env.STORE_CODE,
app: app,
response: res,
request: req
}
renderer.renderToString(context).then(output => {
if (!res.get('content-type')) {
res.setHeader('Content-Type', 'text/html')
}
if (config.server.useOutputCacheTagging) {
const tagsArray = Array.from(context.state.requestContext.outputCacheTags)
const cacheTags = tagsArray.join(' ')
res.setHeader('Content-Type', 'text/html')
res.setHeader('X-VS-Cache-Tags', cacheTags)
res.end(output)
console.log(`cache tags for the request: ${cacheTags}`)
console.log(`whole request [${req.url}]: ${Date.now() - s}ms`)
const contentPrepend = (typeof context.renderPrepend === 'function') ? context.renderPrepend(context) : ''
const contentAppend = (typeof context.renderAppend === 'function') ? context.renderAppend(context) : ''

output = contentPrepend + output + contentAppend
if (context.serverOutputTemplate) { // case when we've got the template name back from vue app
if (templatesCache[context.serverOutputTemplate]) { // please look at: https://github.com/vuejs/vue/blob/79cabadeace0e01fb63aa9f220f41193c0ca93af/src/server/template-renderer/index.js#L87 for reference
output = templatesCache[context.serverOutputTemplate](context).replace('<!--vue-ssr-outlet-->', output)
} else {
throw new Error(`The given template name ${context.serverOutputTemplate} does not exist`)
}
}
if (config.server.useOutputCache && cache) {
cache.set(
'page:' + req.url,
output,
{ headers: res.getHeaders(), body: output },
tagsArray
).catch(errorHandler)
}
next()
}).catch(errorHandler)
} else {
res.setHeader('Content-Type', 'text/html')
renderer.renderToStream(context) // TODO: pass the store code from the headers
.on('error', errorHandler)
.on('end', () => {
console.log(`whole request: ${Date.now() - s}ms`)
next()
})
.pipe(res)
}
console.log(`cache tags for the request: ${cacheTags}`)
}
res.end(output)
console.log(`whole request [${req.url}]: ${Date.now() - s}ms`)
next()
}).catch(errorHandler)
}

if (config.server.useOutputCache && cache) {
cache.get(
'page:' + req.url
).then(output => {
if (output !== null) {
res.setHeader('Content-Type', 'text/html')
if (output.headers) {
for (const header of Object.keys(output.headers)) {
res.setHeader(header, output.headers[header])
}
}
res.setHeader('X-VS-Cache', 'Hit')
if (output.body) {
res.end(output.body)
} else {
res.setHeader('Content-Type', 'text/html')
res.end(output.body)
}
res.end(output)
console.log(`cache hit [${req.url}], cached request: ${Date.now() - s}ms`)
next()
Expand Down
6 changes: 4 additions & 2 deletions core/server-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ function _ssrHydrateSubcomponents (components, store, router, resolve, reject, a
if (SubComponent.asyncData) {
return SubComponent.asyncData({
store,
route: router.currentRoute
route: router.currentRoute,
context
})
}
})).then(() => {
Expand All @@ -46,6 +47,7 @@ export default context => {
if (store.state.config.storeViews.multistore === true) {
let storeCode = context.storeCode // this is from http header or env variable
if (router.currentRoute) { // this is from url
context.currentRoute = router.currentRoute
storeCode = storeCodeFromRoute(router.currentRoute)
}
if (storeCode !== '' && storeCode !== null) {
Expand All @@ -64,7 +66,7 @@ export default context => {
}
})
if (Component.asyncData) {
Component.asyncData({ store, route: router.currentRoute }).then((result) => { // always execute the asyncData() from the top most component first
Component.asyncData({ store, route: router.currentRoute, context: context }).then((result) => { // always execute the asyncData() from the top most component first
console.debug('Top-most asyncData executed')
_ssrHydrateSubcomponents(components, store, router, resolve, reject, app, context)
}).catch((err) => {
Expand Down
12 changes: 4 additions & 8 deletions core/store/modules/cart/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,9 @@ const actions: ActionTree<CartState, RootState> = {
})
}
},
removeItem ({ commit, dispatch }, product) {
commit(types.CART_DEL_ITEM, { product })
removeItem ({ commit, dispatch }, { product, removeByParentSku = true }) {
commit(types.CART_DEL_ITEM, { product, removeByParentSku })
if (rootStore.state.config.cart.synchronize && product.server_item_id) {
/* dispatch('serverDeleteItem', {
sku: product.sku,
item_id: product.server_item_id
}) */
dispatch('serverPull', { forceClientState: true })
}
},
Expand Down Expand Up @@ -615,13 +611,13 @@ const actions: ActionTree<CartState, RootState> = {
rootStore.dispatch('cart/updateItem', { product: { qty: cartItem.prev_qty } }, { root: true }) // update the server_id reference
Vue.prototype.$bus.$emit('cart-after-itemchanged', { item: cartItem })
} else {
rootStore.dispatch('cart/removeItem', { product: cartItem }, { root: true }) // update the server_id reference
rootStore.dispatch('cart/removeItem', { product: cartItem, removeByParentSku: false }, { root: true }) // update the server_id reference
}
}
})
} else {
console.log('Removing product from the cart', originalCartItem)
rootStore.commit('cart/' + types.CART_DEL_ITEM, { product: originalCartItem }, {root: true})
rootStore.commit('cart/' + types.CART_DEL_ITEM, { product: originalCartItem, removeByParentSku: false }, {root: true})
}
} else {
const isThisNewItemAddedToTheCart = (!originalCartItem || !originalCartItem.item_id)
Expand Down
4 changes: 2 additions & 2 deletions core/store/modules/cart/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ const mutations: MutationTree<CartState> = {
Vue.prototype.$bus.$emit('cart-before-save', { items: state.cartItems })
state.cartSavedAt = Date.now()
},
[types.CART_DEL_ITEM] (state, { product }) {
[types.CART_DEL_ITEM] (state, { product, removeByParentSku = true }) {
Vue.prototype.$bus.$emit('cart-before-delete', { items: state.cartItems })
state.cartItems = state.cartItems.filter(p => p.sku !== product.sku && p.parentSku !== product.sku)
state.cartItems = state.cartItems.filter(p => p.sku !== product.sku && (p.parentSku !== product.sku || removeByParentSku === false))
Vue.prototype.$bus.$emit('cart-after-delete', { items: state.cartItems })
state.cartSavedAt = Date.now()
},
Expand Down
Loading