From 6e57c26944d33d6f3091d35fb1cf4573dfe84813 Mon Sep 17 00:00:00 2001 From: pkarw Date: Thu, 27 Sep 2018 21:29:23 +0200 Subject: [PATCH 01/15] README link to Express docs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9426e4cdd1..e5910bf490 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,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) From afe9d269e2b82280e594767a1fbd02e389996d25 Mon Sep 17 00:00:00 2001 From: pkarw Date: Thu, 27 Sep 2018 22:11:40 +0200 Subject: [PATCH 02/15] Templates selector PoC --- core/app.ts | 2 + core/scripts/server.js | 44 +++++++++---------- core/server-entry.ts | 6 ++- src/extensions/index.js | 3 ++ src/extensions/raw-output-example/index.js | 12 +++++ .../raw-output-example/package.json | 13 ++++++ .../pages/RawOutputExample.vue | 31 +++++++++++++ src/extensions/raw-output-example/router.js | 5 +++ src/extensions/raw-output-example/store.js | 20 +++++++++ 9 files changed, 112 insertions(+), 24 deletions(-) create mode 100755 src/extensions/raw-output-example/index.js create mode 100644 src/extensions/raw-output-example/package.json create mode 100755 src/extensions/raw-output-example/pages/RawOutputExample.vue create mode 100644 src/extensions/raw-output-example/router.js create mode 100644 src/extensions/raw-output-example/store.js diff --git a/core/app.ts b/core/app.ts index eb82e47300..0b88fc6081 100755 --- a/core/app.ts +++ b/core/app.ts @@ -110,6 +110,8 @@ export function createApp (serverContext = null): { app: Vue, router: any, store provide: apolloProvider, render: h => h(App) }) + + Vue.prototype.$serverContext = serverContext // this is context passed from Expressjs registerExtensions( union(extensionEntryPoints, themeExtensionEntryPoints), diff --git a/core/scripts/server.js b/core/scripts/server.js index a23f4052d8..7a23d9142d 100755 --- a/core/scripts/server.js +++ b/core/scripts/server.js @@ -21,6 +21,7 @@ if (config.server.useOutputCache) { console.log('Redis cache set', config.redis) } +const templatesCache = {} let renderer if (isProd) { // In production: create server renderer using server bundle and index HTML @@ -30,19 +31,20 @@ if (isProd) { // 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) + templatesCache['default'] = template + 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'] = template + 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 @@ -137,16 +139,15 @@ app.get('*', (req, res, next) => { ' ') 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, serverOutputTemplate: 'default', currentRoute: null /** will be set by Vue */, storeCode: req.header('x-vs-store-code') ? req.header('x-vs-store-code') : process.env.STORE_CODE, server: { app: app, res: res, req: 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`) if (config.server.useOutputCache && cache) { cache.set( 'page:' + req.url, @@ -154,18 +155,17 @@ app.get('*', (req, res, next) => { 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}`) + } + if (context.serverOutputTemplate) { // case when we've got the template name back from vue app + if (templatesCache[context.serverOutputTemplate]) { + output = templatesCache[context.serverOutputTemplate].replace('', output) + } + } + res.end(output) + console.log(`whole request [${req.url}]: ${Date.now() - s}ms`) + next() + }).catch(errorHandler) } if (config.server.useOutputCache && cache) { diff --git a/core/server-entry.ts b/core/server-entry.ts index 6f2eac7d7a..a62371c8ab 100755 --- a/core/server-entry.ts +++ b/core/server-entry.ts @@ -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(() => { @@ -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) { @@ -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) => { diff --git a/src/extensions/index.js b/src/extensions/index.js index 0298bde655..07e2169083 100644 --- a/src/extensions/index.js +++ b/src/extensions/index.js @@ -13,4 +13,7 @@ if (!Vue.prototype.$isServer) { // extensions that are not required in the SSR m extensionList.push(require('@vue-storefront/extension-payment-backend-methods/index.js')) extensionList.push(require('@vue-storefront/extension-payment-cash-on-delivery/index.js')) extensionList.push(require('vsf-payment-stripe/index.js')) +if (Vue.prototype.$isServer) { + extensionList.push(require('@vue-storefront/raw-output-example/index.js')) +} export default extensionList diff --git a/src/extensions/raw-output-example/index.js b/src/extensions/raw-output-example/index.js new file mode 100755 index 0000000000..2e43b60171 --- /dev/null +++ b/src/extensions/raw-output-example/index.js @@ -0,0 +1,12 @@ +import extensionStore from './store' +import extensionRoutes from './router' + +const EXTENSION_KEY = 'raw_content_extension' + +export default function (app, router, store, config, serverContext) { + serverContext.template = '' + router.addRoutes(extensionRoutes) // add custom routes + store.registerModule(EXTENSION_KEY, extensionStore) // add custom store + + return { EXTENSION_KEY, extensionRoutes, extensionStore } +} diff --git a/src/extensions/raw-output-example/package.json b/src/extensions/raw-output-example/package.json new file mode 100644 index 0000000000..9ac1da9082 --- /dev/null +++ b/src/extensions/raw-output-example/package.json @@ -0,0 +1,13 @@ +{ + "name": "@vue-storefront/raw-output-example", + "version": "1.3.0", + "description": "Extension on how to generate raw output for Vue Storefront", + "license": "MIT", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/src/extensions/raw-output-example/pages/RawOutputExample.vue b/src/extensions/raw-output-example/pages/RawOutputExample.vue new file mode 100755 index 0000000000..15a0e4d125 --- /dev/null +++ b/src/extensions/raw-output-example/pages/RawOutputExample.vue @@ -0,0 +1,31 @@ + + + + + + diff --git a/src/extensions/raw-output-example/router.js b/src/extensions/raw-output-example/router.js new file mode 100644 index 0000000000..3a4854afd5 --- /dev/null +++ b/src/extensions/raw-output-example/router.js @@ -0,0 +1,5 @@ +import RawOutputExample from './pages/RawOutputExample.vue' + +export default [ + { path: '/raw-output-example.xml', component: RawOutputExample, serverOutputTemplate: '' } +] diff --git a/src/extensions/raw-output-example/store.js b/src/extensions/raw-output-example/store.js new file mode 100644 index 0000000000..17f957b64d --- /dev/null +++ b/src/extensions/raw-output-example/store.js @@ -0,0 +1,20 @@ +const state = { +} + +const getters = { +} + +// actions +const actions = { +} + +// mutations +const mutations = { +} + +export default { + state, + getters, + actions, + mutations +} From 35d44504adefd3b498173ef63819ddaaec40452f Mon Sep 17 00:00:00 2001 From: pkarw Date: Thu, 27 Sep 2018 23:34:04 +0200 Subject: [PATCH 03/15] Layout switching feature --- core/scripts/server.js | 20 ++-- .../pages/RawOutputExample.vue | 4 +- src/extensions/raw-output-example/router.js | 2 +- src/themes/default/App.vue | 95 ++-------------- src/themes/default/layouts/Default.vue | 106 ++++++++++++++++++ src/themes/default/layouts/Empty.vue | 3 + 6 files changed, 131 insertions(+), 99 deletions(-) create mode 100644 src/themes/default/layouts/Default.vue create mode 100644 src/themes/default/layouts/Empty.vue diff --git a/core/scripts/server.js b/core/scripts/server.js index 7a23d9142d..e480821fad 100755 --- a/core/scripts/server.js +++ b/core/scripts/server.js @@ -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 @@ -31,20 +35,19 @@ if (isProd) { // 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') - templatesCache['default'] = template - renderer = createRenderer(bundle) + renderer = createRenderer(bundle, template) } 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) => { - templatesCache['default'] = template - renderer = createRenderer(bundle) + renderer = createRenderer(bundle, template) }) } 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 @@ -139,7 +142,7 @@ app.get('*', (req, res, next) => { ' ') return next() } - const context = { url: req.url, serverOutputTemplate: 'default', currentRoute: null /** will be set by Vue */, storeCode: req.header('x-vs-store-code') ? req.header('x-vs-store-code') : process.env.STORE_CODE, server: { app: app, res: res, req: req } } + const context = { url: req.url, 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') @@ -157,11 +160,6 @@ app.get('*', (req, res, next) => { } console.log(`cache tags for the request: ${cacheTags}`) } - if (context.serverOutputTemplate) { // case when we've got the template name back from vue app - if (templatesCache[context.serverOutputTemplate]) { - output = templatesCache[context.serverOutputTemplate].replace('', output) - } - } res.end(output) console.log(`whole request [${req.url}]: ${Date.now() - s}ms`) next() diff --git a/src/extensions/raw-output-example/pages/RawOutputExample.vue b/src/extensions/raw-output-example/pages/RawOutputExample.vue index 15a0e4d125..a19c9bc196 100755 --- a/src/extensions/raw-output-example/pages/RawOutputExample.vue +++ b/src/extensions/raw-output-example/pages/RawOutputExample.vue @@ -8,8 +8,8 @@ export default { name: 'RawOutputExample', asyncData ({ store, route, context }) { - context.server.res.setHeader('Content-Type', 'text/xml') - context.serverOutputTemplate = '' + context.response.setHeader('Content-Type', 'text/xml') + context.rawOutput = true return new Promise((resolve, reject) => { resolve() diff --git a/src/extensions/raw-output-example/router.js b/src/extensions/raw-output-example/router.js index 3a4854afd5..932d969829 100644 --- a/src/extensions/raw-output-example/router.js +++ b/src/extensions/raw-output-example/router.js @@ -1,5 +1,5 @@ import RawOutputExample from './pages/RawOutputExample.vue' export default [ - { path: '/raw-output-example.xml', component: RawOutputExample, serverOutputTemplate: '' } + { path: '/raw-output-example.xml', component: RawOutputExample, serverOutputTemplate: '', meta: { layout: 'empty' } } ] diff --git a/src/themes/default/App.vue b/src/themes/default/App.vue index 2826f82acc..0a335f68de 100755 --- a/src/themes/default/App.vue +++ b/src/themes/default/App.vue @@ -1,51 +1,13 @@ - - diff --git a/src/themes/default/layouts/Default.vue b/src/themes/default/layouts/Default.vue new file mode 100644 index 0000000000..ea36090538 --- /dev/null +++ b/src/themes/default/layouts/Default.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/src/themes/default/layouts/Empty.vue b/src/themes/default/layouts/Empty.vue new file mode 100644 index 0000000000..083049bd4e --- /dev/null +++ b/src/themes/default/layouts/Empty.vue @@ -0,0 +1,3 @@ + From 08a37ec4542ad83592903e9285de87f605211304 Mon Sep 17 00:00:00 2001 From: pkarw Date: Fri, 28 Sep 2018 00:24:16 +0200 Subject: [PATCH 04/15] Empty layout example added --- core/scripts/server.js | 10 ++-------- .../index.js | 1 - .../package.json | 2 +- .../pages/RawOutputExample.vue | 8 +++----- src/extensions/empty-layout-example/router.js | 5 +++++ .../store.js | 0 src/extensions/index.js | 4 +--- src/extensions/raw-output-example/router.js | 5 ----- 8 files changed, 12 insertions(+), 23 deletions(-) rename src/extensions/{raw-output-example => empty-layout-example}/index.js (92%) rename src/extensions/{raw-output-example => empty-layout-example}/package.json (84%) rename src/extensions/{raw-output-example => empty-layout-example}/pages/RawOutputExample.vue (82%) create mode 100644 src/extensions/empty-layout-example/router.js rename src/extensions/{raw-output-example => empty-layout-example}/store.js (100%) delete mode 100644 src/extensions/raw-output-example/router.js diff --git a/core/scripts/server.js b/core/scripts/server.js index e480821fad..c95df6d4ed 100755 --- a/core/scripts/server.js +++ b/core/scripts/server.js @@ -6,11 +6,6 @@ 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 @@ -25,7 +20,6 @@ if (config.server.useOutputCache) { console.log('Redis cache set', config.redis) } -const templatesCache = {} let renderer if (isProd) { // In production: create server renderer using server bundle and index HTML @@ -35,12 +29,12 @@ if (isProd) { // 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) + renderer = createRenderer(bundle, process.env.DISABLE_HTML_TEMPLATE ? template : null) } 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) + renderer = createRenderer(bundle, process.env.DISABLE_HTML_TEMPLATE ? template : null) }) } diff --git a/src/extensions/raw-output-example/index.js b/src/extensions/empty-layout-example/index.js similarity index 92% rename from src/extensions/raw-output-example/index.js rename to src/extensions/empty-layout-example/index.js index 2e43b60171..1489fc9b79 100755 --- a/src/extensions/raw-output-example/index.js +++ b/src/extensions/empty-layout-example/index.js @@ -4,7 +4,6 @@ import extensionRoutes from './router' const EXTENSION_KEY = 'raw_content_extension' export default function (app, router, store, config, serverContext) { - serverContext.template = '' router.addRoutes(extensionRoutes) // add custom routes store.registerModule(EXTENSION_KEY, extensionStore) // add custom store diff --git a/src/extensions/raw-output-example/package.json b/src/extensions/empty-layout-example/package.json similarity index 84% rename from src/extensions/raw-output-example/package.json rename to src/extensions/empty-layout-example/package.json index 9ac1da9082..26bc078bdc 100644 --- a/src/extensions/raw-output-example/package.json +++ b/src/extensions/empty-layout-example/package.json @@ -1,5 +1,5 @@ { - "name": "@vue-storefront/raw-output-example", + "name": "@vue-storefront/empty-layout-example", "version": "1.3.0", "description": "Extension on how to generate raw output for Vue Storefront", "license": "MIT", diff --git a/src/extensions/raw-output-example/pages/RawOutputExample.vue b/src/extensions/empty-layout-example/pages/RawOutputExample.vue similarity index 82% rename from src/extensions/raw-output-example/pages/RawOutputExample.vue rename to src/extensions/empty-layout-example/pages/RawOutputExample.vue index a19c9bc196..95d5bf0151 100755 --- a/src/extensions/raw-output-example/pages/RawOutputExample.vue +++ b/src/extensions/empty-layout-example/pages/RawOutputExample.vue @@ -1,7 +1,7 @@ + + + diff --git a/src/extensions/raw-output-example/router.js b/src/extensions/raw-output-example/router.js index 932d969829..0945e1c033 100644 --- a/src/extensions/raw-output-example/router.js +++ b/src/extensions/raw-output-example/router.js @@ -1,5 +1,7 @@ +import NoJSExample from './pages/NoJSExample.vue' import RawOutputExample from './pages/RawOutputExample.vue' export default [ - { path: '/raw-output-example.xml', component: RawOutputExample, serverOutputTemplate: '', meta: { layout: 'empty' } } + { path: '/raw-output-example.xml', component: RawOutputExample, meta: { layout: 'empty' } }, + { path: '/no-js.html', component: NoJSExample, meta: { layout: 'default' } } ] diff --git a/src/index.basic.template.html b/src/index.basic.template.html new file mode 100755 index 0000000000..541312bdcd --- /dev/null +++ b/src/index.basic.template.html @@ -0,0 +1,26 @@ + + + + {{{ meta.inject().title.text() }}} + {{{ meta.inject().meta.text() }}} + {{{ meta.inject().link.text() }}} + + {{{ renderStyles() }}} + + + + + diff --git a/src/index.minimal.template.html b/src/index.minimal.template.html new file mode 100755 index 0000000000..3508f20ede --- /dev/null +++ b/src/index.minimal.template.html @@ -0,0 +1,11 @@ + + + + {{{ meta.inject().title.text() }}} + {{{ meta.inject().meta.text() }}} + {{{ meta.inject().link.text() }}} + + + + + diff --git a/src/themes/default/index.basic.template.html b/src/themes/default/index.basic.template.html new file mode 100755 index 0000000000..541312bdcd --- /dev/null +++ b/src/themes/default/index.basic.template.html @@ -0,0 +1,26 @@ + + + + {{{ meta.inject().title.text() }}} + {{{ meta.inject().meta.text() }}} + {{{ meta.inject().link.text() }}} + + {{{ renderStyles() }}} + + + + + diff --git a/src/themes/default/index.minimal.template.html b/src/themes/default/index.minimal.template.html new file mode 100755 index 0000000000..3508f20ede --- /dev/null +++ b/src/themes/default/index.minimal.template.html @@ -0,0 +1,11 @@ + + + + {{{ meta.inject().title.text() }}} + {{{ meta.inject().meta.text() }}} + {{{ meta.inject().link.text() }}} + + + + + From c95c0768ed6f5607657209028f084f73d1e05a61 Mon Sep 17 00:00:00 2001 From: pkarw Date: Fri, 28 Sep 2018 09:52:06 +0200 Subject: [PATCH 11/15] Missing config added. --- config/default.json | 3 ++- core/scripts/server.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config/default.json b/config/default.json index 4bef1049f6..7fa3a7245c 100644 --- a/config/default.json +++ b/config/default.json @@ -33,7 +33,8 @@ "ssr": { "templates": { "default": "dist/index.html", - "minimal": "dist/index.minimal.html" + "minimal": "dist/index.minimal.html", + "basic": "dist/index.basic.html" }, "executeMixedinAsyncData": true, "initialStateFilter": ["config", "__DEMO_MODE__", "version", "storeView"], diff --git a/core/scripts/server.js b/core/scripts/server.js index 1455468721..1da30d47df 100755 --- a/core/scripts/server.js +++ b/core/scripts/server.js @@ -128,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() From 75ed13725bfbbeb3f42de90e20163c89fb89ce81 Mon Sep 17 00:00:00 2001 From: pkarw Date: Fri, 28 Sep 2018 10:30:06 +0200 Subject: [PATCH 12/15] Docs + append / prepend example --- core/scripts/server.js | 4 +-- doc/Layouts and advanced output operations.md | 10 ++++++ .../pages/NoLayoutAppendPrependExample.vue | 33 +++++++++++++++++++ src/extensions/raw-output-example/router.js | 2 ++ 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 doc/Layouts and advanced output operations.md create mode 100755 src/extensions/raw-output-example/pages/NoLayoutAppendPrependExample.vue diff --git a/core/scripts/server.js b/core/scripts/server.js index 1da30d47df..f203fc0125 100755 --- a/core/scripts/server.js +++ b/core/scripts/server.js @@ -172,8 +172,8 @@ app.get('*', (req, res, next) => { res.setHeader('X-VS-Cache-Tags', cacheTags) 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 - const contentPrepend = (context.renderPrepend instanceof Function) ? context.renderPrepend(context) : '' - const contentAppend = (context.renderAppend instanceof Function) ? context.renderAppend(context) : '' + const contentPrepend = (typeof context.renderPrepend === 'function') ? context.renderPrepend(context) : '' + const contentAppend = (typeof context.renderAppend === 'function') ? context.renderAppend(context) : '' output = contentPrepend + templatesCache[context.serverOutputTemplate](context).replace('', output) + contentAppend } else { throw new Error(`The given template name ${context.serverOutputTemplate} does not exist`) diff --git a/doc/Layouts and advanced output operations.md b/doc/Layouts and advanced output operations.md new file mode 100644 index 0000000000..499ca14ea5 --- /dev/null +++ b/doc/Layouts and advanced output operations.md @@ -0,0 +1,10 @@ +# Layouts and advanced output operations + +Starting from version 1.4.0 Vue Storefront allows You to switch the html templates and layouts dynamically in the SSR mode. + +This feature can be very usefull for non-standard rendering scenarios like: +- generating the XML output, +- generating the AMPHTML pages, +- generating widgets without `` section +... + diff --git a/src/extensions/raw-output-example/pages/NoLayoutAppendPrependExample.vue b/src/extensions/raw-output-example/pages/NoLayoutAppendPrependExample.vue new file mode 100755 index 0000000000..e4c5cc486a --- /dev/null +++ b/src/extensions/raw-output-example/pages/NoLayoutAppendPrependExample.vue @@ -0,0 +1,33 @@ + + + + + + diff --git a/src/extensions/raw-output-example/router.js b/src/extensions/raw-output-example/router.js index 0945e1c033..3e03b8000c 100644 --- a/src/extensions/raw-output-example/router.js +++ b/src/extensions/raw-output-example/router.js @@ -1,7 +1,9 @@ import NoJSExample from './pages/NoJSExample.vue' import RawOutputExample from './pages/RawOutputExample.vue' +import NoLayoutAppendPrependExample from './pages/NoLayoutAppendPrependExample.vue' export default [ { path: '/raw-output-example.xml', component: RawOutputExample, meta: { layout: 'empty' } }, + { path: '/append-prepend.html', component: NoLayoutAppendPrependExample, meta: { layout: 'empty' } }, { path: '/no-js.html', component: NoJSExample, meta: { layout: 'default' } } ] From cdc67fb3c3acc3df1752937c52c8dea6671c00f6 Mon Sep 17 00:00:00 2001 From: pkarw Date: Fri, 28 Sep 2018 11:04:02 +0200 Subject: [PATCH 13/15] ESlint fix --- core/scripts/server.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/scripts/server.js b/core/scripts/server.js index 62c283667d..72642d36fb 100755 --- a/core/scripts/server.js +++ b/core/scripts/server.js @@ -152,7 +152,7 @@ app.get('*', (req, res, next) => { } 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 + 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, @@ -174,7 +174,6 @@ app.get('*', (req, res, next) => { 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('', output) From ca65482904ed12f37589e656773a96a6dbb65789 Mon Sep 17 00:00:00 2001 From: pkarw Date: Fri, 28 Sep 2018 11:07:07 +0200 Subject: [PATCH 14/15] Server context removed from Vue.prototype --- core/app.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/app.ts b/core/app.ts index 0b88fc6081..abe406ec33 100755 --- a/core/app.ts +++ b/core/app.ts @@ -110,9 +110,6 @@ export function createApp (serverContext = null): { app: Vue, router: any, store provide: apolloProvider, render: h => h(App) }) - - Vue.prototype.$serverContext = serverContext // this is context passed from Expressjs - registerExtensions( union(extensionEntryPoints, themeExtensionEntryPoints), app, From 0bc37a0f0c9f198f6fa4bf542f71bb582bab0438 Mon Sep 17 00:00:00 2001 From: pkarw Date: Sat, 29 Sep 2018 22:07:54 +0200 Subject: [PATCH 15/15] Fix for #1795 --- core/components/blocks/Microcart/Product.js | 2 +- core/modules/cart/features/removeFromCart.ts | 2 +- core/store/modules/cart/actions.ts | 12 ++++-------- core/store/modules/cart/mutations.ts | 4 ++-- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/core/components/blocks/Microcart/Product.js b/core/components/blocks/Microcart/Product.js index 13f66e4866..e511259993 100644 --- a/core/components/blocks/Microcart/Product.js +++ b/core/components/blocks/Microcart/Product.js @@ -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) { diff --git a/core/modules/cart/features/removeFromCart.ts b/core/modules/cart/features/removeFromCart.ts index 24337d15cb..e67d229749 100644 --- a/core/modules/cart/features/removeFromCart.ts +++ b/core/modules/cart/features/removeFromCart.ts @@ -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 }) } } } diff --git a/core/store/modules/cart/actions.ts b/core/store/modules/cart/actions.ts index eb703a9037..deb9fe66ac 100644 --- a/core/store/modules/cart/actions.ts +++ b/core/store/modules/cart/actions.ts @@ -291,13 +291,9 @@ const actions: ActionTree = { }) } }, - 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 }) } }, @@ -615,13 +611,13 @@ const actions: ActionTree = { 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) diff --git a/core/store/modules/cart/mutations.ts b/core/store/modules/cart/mutations.ts index 075521744e..294e44e837 100644 --- a/core/store/modules/cart/mutations.ts +++ b/core/store/modules/cart/mutations.ts @@ -26,9 +26,9 @@ const mutations: MutationTree = { 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() },