From dcfea464cf6240d2769e19e502a121ce7621473f Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Fri, 20 Sep 2019 23:30:32 +0200 Subject: [PATCH 1/4] refactoring storeCodeFromRoute, resolve store code on ssr --- core/client-entry.ts | 3 +- core/lib/multistore.ts | 83 +-------------------------- core/lib/storeCodeFromRoute.ts | 49 ++++++++++++++++ core/lib/test/unit/multistore.spec.ts | 4 +- core/lib/types.ts | 43 ++++++++++++++ core/modules/url/helpers/index.ts | 5 +- core/modules/url/router/beforeEach.ts | 7 +-- core/modules/url/store/actions.ts | 6 ++ core/modules/url/types/UrlState.ts | 2 +- core/server-entry.ts | 6 +- 10 files changed, 111 insertions(+), 97 deletions(-) create mode 100644 core/lib/storeCodeFromRoute.ts create mode 100644 core/lib/types.ts diff --git a/core/client-entry.ts b/core/client-entry.ts index b8f9132cc3..37f3745ff7 100755 --- a/core/client-entry.ts +++ b/core/client-entry.ts @@ -5,7 +5,8 @@ import rootStore from '@vue-storefront/core/store' import { registerSyncTaskProcessor } from '@vue-storefront/core/lib/sync/task' import i18n from '@vue-storefront/i18n' import omit from 'lodash-es/omit' -import { prepareStoreView, storeCodeFromRoute, currentStoreView, localizedRoute } from '@vue-storefront/core/lib/multistore' +import storeCodeFromRoute from '@vue-storefront/core/lib/storeCodeFromRoute' +import { prepareStoreView, currentStoreView, localizedRoute } from '@vue-storefront/core/lib/multistore' import { onNetworkStatusChange } from '@vue-storefront/core/modules/offline-order/helpers/onNetworkStatusChange' import '@vue-storefront/core/service-worker/registration' // register the service worker import { AsyncDataLoader } from './lib/async-data-loader' diff --git a/core/lib/multistore.ts b/core/lib/multistore.ts index a1117b685c..2efd1c17f9 100644 --- a/core/lib/multistore.ts +++ b/core/lib/multistore.ts @@ -10,50 +10,8 @@ import VueRouter, { RouteConfig, RawLocation } from 'vue-router' import config from 'config' import { coreHooksExecutors } from '@vue-storefront/core/hooks' import { StorageManager } from '@vue-storefront/core/lib/storage-manager' - -export interface LocalizedRoute { - path?: string, - name?: string, - hash?: string, - params?: { [key: string]: unknown }, - fullPath?: string, - host?: string -} - -export interface StoreView { - storeCode: string, - extend?: string, - disabled?: boolean, - storeId: any, - name?: string, - url?: string, - appendStoreCode?: boolean, - elasticsearch: { - host: string, - index: string - }, - tax: { - sourcePriceIncludesTax: boolean, - defaultCountry: string, - defaultRegion: null | string, - calculateServerSide: boolean, - userGroupId?: number, - useOnlyDefaultUserGroupId: boolean - }, - i18n: { - fullCountryName: string, - fullLanguageName: string, - defaultLanguage: string, - defaultCountry: string, - defaultLocale: string, - currencyCode: string, - currencySign: string, - dateFormat: string - }, - seo: { - defaultTitle: string - } -} +import { LocalizedRoute, StoreView } from './types' +import storeCodeFromRoute from './storeCodeFromRoute' function getExtendedStoreviewConfig (storeView: StoreView): StoreView { if (storeView.extend) { @@ -117,43 +75,6 @@ export function prepareStoreView (storeCode: string): StoreView { return storeView } -export function storeCodeFromRoute (matchedRouteOrUrl: LocalizedRoute | RawLocation | string): string { - if (matchedRouteOrUrl) { - for (let storeCode of config.storeViews.mapStoreUrlsFor) { - const store = config.storeViews[storeCode] - - // handle resolving by path - const matchingPath = typeof matchedRouteOrUrl === 'object' ? matchedRouteOrUrl.path : matchedRouteOrUrl - let normalizedPath = matchingPath // assume that matching string is a path - if (matchingPath.length > 0 && matchingPath[0] !== '/') { - normalizedPath = '/' + matchingPath - } - - if (normalizedPath.startsWith(`${store.url}/`) || normalizedPath === store.url) { - return storeCode - } - - // handle resolving by domain+path - let url = '' - - if (typeof matchedRouteOrUrl === 'object') { - if (matchedRouteOrUrl['host']) { - url = matchedRouteOrUrl['host'] + normalizedPath - } else { - return '' // this route does not have url so there is nothing to do here - } - } else { - url = matchedRouteOrUrl as string - } - - if (url.startsWith(`${store.url}/`) || url === store.url) { - return storeCode - } - } - } - return '' -} - export function removeStoreCodeFromRoute (matchedRouteOrUrl: LocalizedRoute | string): LocalizedRoute | string { const storeCodeInRoute = storeCodeFromRoute(matchedRouteOrUrl) if (storeCodeInRoute !== '') { diff --git a/core/lib/storeCodeFromRoute.ts b/core/lib/storeCodeFromRoute.ts new file mode 100644 index 0000000000..0a74afc31d --- /dev/null +++ b/core/lib/storeCodeFromRoute.ts @@ -0,0 +1,49 @@ +import { RawLocation } from 'vue-router' +import config from 'config' +import { LocalizedRoute } from './types' + +const getNormalizedPath = (matchedRouteOrUrl) => { + const matchingPath = typeof matchedRouteOrUrl === 'object' ? matchedRouteOrUrl.path : matchedRouteOrUrl + + return matchingPath.length > 0 && matchingPath[0] !== '/' ? `/${matchingPath}` : matchingPath +} + +const getUrl = (matchedRouteOrUrl) => { + const normalizedPath = getNormalizedPath(matchedRouteOrUrl) + + if (typeof matchedRouteOrUrl === 'object') { + if (matchedRouteOrUrl['host']) { + return matchedRouteOrUrl['host'] + normalizedPath + } + + return '' + } + + return matchedRouteOrUrl +} + +const isMatchingByPath = (matchedRouteOrUrl, store) => { + const normalizedPath = getNormalizedPath(matchedRouteOrUrl) + return normalizedPath.startsWith(`${store.url}/`) || normalizedPath === store.url +} + +const isMatchingByDomainAndPath = (matchedRouteOrUrl, store) => { + const url = getUrl(matchedRouteOrUrl) + return url.startsWith(`${store.url}/`) || url === store.url +} + +const storeCodeFromRoute = (matchedRouteOrUrl: LocalizedRoute | RawLocation | string): string => { + if (!matchedRouteOrUrl) return '' + + for (let storeCode of config.storeViews.mapStoreUrlsFor) { + const store = config.storeViews[storeCode] + + if (isMatchingByPath(matchedRouteOrUrl, store) || isMatchingByDomainAndPath(matchedRouteOrUrl, store)) { + return storeCode + } + } + + return '' +} + +export default storeCodeFromRoute diff --git a/core/lib/test/unit/multistore.spec.ts b/core/lib/test/unit/multistore.spec.ts index 6f32673902..57af565e76 100644 --- a/core/lib/test/unit/multistore.spec.ts +++ b/core/lib/test/unit/multistore.spec.ts @@ -1,9 +1,9 @@ +import storeCodeFromRoute from '@vue-storefront/core/lib/storeCodeFromRoute' +import { LocalizedRoute } from '@vue-storefront/core/lib/types' import { - storeCodeFromRoute, prepareStoreView, adjustMultistoreApiUrl, localizedDispatcherRoute, - LocalizedRoute, setupMultistoreRoutes, localizedRoutePath, localizedRouteConfig diff --git a/core/lib/types.ts b/core/lib/types.ts new file mode 100644 index 0000000000..507806fab7 --- /dev/null +++ b/core/lib/types.ts @@ -0,0 +1,43 @@ +export interface LocalizedRoute { + path?: string, + name?: string, + hash?: string, + params?: { [key: string]: unknown }, + fullPath?: string, + host?: string +} + +export interface StoreView { + storeCode: string, + extend?: string, + disabled?: boolean, + storeId: any, + name?: string, + url?: string, + appendStoreCode?: boolean, + elasticsearch: { + host: string, + index: string + }, + tax: { + sourcePriceIncludesTax: boolean, + defaultCountry: string, + defaultRegion: null | string, + calculateServerSide: boolean, + userGroupId?: number, + useOnlyDefaultUserGroupId: boolean + }, + i18n: { + fullCountryName: string, + fullLanguageName: string, + defaultLanguage: string, + defaultCountry: string, + defaultLocale: string, + currencyCode: string, + currencySign: string, + dateFormat: string + }, + seo: { + defaultTitle: string + } +} diff --git a/core/modules/url/helpers/index.ts b/core/modules/url/helpers/index.ts index 2d0a704e9a..5f99400567 100644 --- a/core/modules/url/helpers/index.ts +++ b/core/modules/url/helpers/index.ts @@ -1,10 +1,10 @@ import { router } from '@vue-storefront/core/app' import config from 'config' -import { localizedDispatcherRoute, localizedRoute, LocalizedRoute, currentStoreView } from '@vue-storefront/core/lib/multistore' +import { LocalizedRoute } from '@vue-storefront/core/lib/types' +import { localizedDispatcherRoute, localizedRoute, currentStoreView } from '@vue-storefront/core/lib/multistore' import { RouteConfig } from 'vue-router/types/router'; import { RouterManager } from '@vue-storefront/core/lib/router-manager' import { Category } from 'core/modules/catalog-next/types/Category' -import { removeStoreCodeFromRoute } from '@vue-storefront/core/lib/multistore' import { Logger } from '@vue-storefront/core/lib/logger' export function parametrizeRouteData (routeData: LocalizedRoute, query: { [id: string]: any } | string, storeCodeInPath: string): LocalizedRoute { @@ -20,7 +20,6 @@ function prepareDynamicRoute (routeData: LocalizedRoute, path: string): RouteCon const userRoute = RouterManager.findByName(routeData.name) if (userRoute) { const normalizedPath = `${path.startsWith('/') ? '' : '/'}${path}` - const currentStoreCode = currentStoreView().storeCode const dynamicRoute = Object.assign({}, userRoute, routeData, { path: normalizedPath, name: `urldispatcher-${normalizedPath}` }) return dynamicRoute } else { diff --git a/core/modules/url/router/beforeEach.ts b/core/modules/url/router/beforeEach.ts index 86796195e1..85c455edc4 100644 --- a/core/modules/url/router/beforeEach.ts +++ b/core/modules/url/router/beforeEach.ts @@ -5,11 +5,10 @@ import { Route } from 'vue-router' import store from '@vue-storefront/core/store' import { Logger } from '@vue-storefront/core/lib/logger' import { processDynamicRoute, normalizeUrlPath } from '../helpers' -import { isServer } from '@vue-storefront/core/helpers' -import { currentStoreView, LocalizedRoute, localizedRoute, storeCodeFromRoute } from '@vue-storefront/core/lib/multistore' -import config from 'config' +import { currentStoreView, localizedRoute } from '@vue-storefront/core/lib/multistore' +import { LocalizedRoute } from '@vue-storefront/core/lib/types' import { RouterManager } from '@vue-storefront/core/lib/router-manager' -import { router } from '@vue-storefront/core/app' + export const UrlDispatchMapper = async (to) => { const routeData = await store.dispatch('url/mapUrl', { url: to.path, query: to.query }) return Object.assign({}, to, routeData) diff --git a/core/modules/url/store/actions.ts b/core/modules/url/store/actions.ts index 04c92ba909..b302ede0a2 100644 --- a/core/modules/url/store/actions.ts +++ b/core/modules/url/store/actions.ts @@ -6,8 +6,14 @@ import { cacheStorage } from '../' import queryString from 'query-string' import config from 'config' import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' +<<<<<<< HEAD import { preProcessDynamicRoutes, normalizeUrlPath, parametrizeRouteData } from '../helpers' import { storeCodeFromRoute, removeStoreCodeFromRoute, currentStoreView, localizedDispatcherRouteName } from '@vue-storefront/core/lib/multistore' +======= +import { processMultipleDynamicRoutes, normalizeUrlPath, parametrizeRouteData } from '../helpers' +import storeCodeFromRoute from '@vue-storefront/core/lib/storeCodeFromRoute' +import { removeStoreCodeFromRoute } from '@vue-storefront/core/lib/multistore' +>>>>>>> refactoring storeCodeFromRoute, resolve store code on ssr // it's a good practice for all actions to return Promises with effect of their execution export const actions: ActionTree = { diff --git a/core/modules/url/types/UrlState.ts b/core/modules/url/types/UrlState.ts index 2a306ed2d8..fba3babff0 100644 --- a/core/modules/url/types/UrlState.ts +++ b/core/modules/url/types/UrlState.ts @@ -1,4 +1,4 @@ -import { LocalizedRoute } from 'core/lib/multistore' +import { LocalizedRoute } from '@vue-storefront/core/lib/types' // This object should represent structure of your modules Vuex state // It's a good practice is to name this interface accordingly to the KET (for example mailchimpState) diff --git a/core/server-entry.ts b/core/server-entry.ts index 278c47c08c..499c109fd7 100755 --- a/core/server-entry.ts +++ b/core/server-entry.ts @@ -1,7 +1,7 @@ import union from 'lodash-es/union' import { createApp } from '@vue-storefront/core/app' import { HttpError } from '@vue-storefront/core/helpers/internal' -import { storeCodeFromRoute } from '@vue-storefront/core/lib/multistore' +import storeCodeFromRoute from '@vue-storefront/core/lib/storeCodeFromRoute' import omit from 'lodash-es/omit' import pick from 'lodash-es/pick' import buildTimeConfig from 'config' @@ -53,10 +53,6 @@ function _ssrHydrateSubcomponents (components, store, router, resolve, reject, a }) } -function getHostFromHeader (headers: string[]): string { - return headers ? (headers['x-forwarded-host'] !== undefined ? headers['x-forwarded-host'] : headers['host']) : null -} - export default async context => { let storeCode = context.vs.storeCode if (config.storeViews.multistore === true) { From 2b47692ab52b376c9152ae34f61bf6def8e58a9c Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Fri, 20 Sep 2019 23:34:29 +0200 Subject: [PATCH 2/4] refactoring storeCodeFromRoute, resolve store code on ssr --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c06af9693d..828c173016 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,6 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Prevent caching storage instance in plugin module scope - @gibkigonzo (#3571) - Fixed incorrect image sizes in related section on product page - @andrzejewsky (#3590) - Fix typo on default language - @lorenaramonda (#3076) +- Fixed resolving store code on SSR - @andrzejewsky (#3576) ### Changed / Improved From b914fa2469375ff30445a2de592d3a5b22ff26ca Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Sun, 22 Sep 2019 20:22:57 +0200 Subject: [PATCH 3/4] resolve conflict --- core/modules/url/store/actions.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/core/modules/url/store/actions.ts b/core/modules/url/store/actions.ts index b302ede0a2..66568ccd9f 100644 --- a/core/modules/url/store/actions.ts +++ b/core/modules/url/store/actions.ts @@ -6,14 +6,9 @@ import { cacheStorage } from '../' import queryString from 'query-string' import config from 'config' import SearchQuery from '@vue-storefront/core/lib/search/searchQuery' -<<<<<<< HEAD import { preProcessDynamicRoutes, normalizeUrlPath, parametrizeRouteData } from '../helpers' -import { storeCodeFromRoute, removeStoreCodeFromRoute, currentStoreView, localizedDispatcherRouteName } from '@vue-storefront/core/lib/multistore' -======= -import { processMultipleDynamicRoutes, normalizeUrlPath, parametrizeRouteData } from '../helpers' +import { removeStoreCodeFromRoute, currentStoreView, localizedDispatcherRouteName } from '@vue-storefront/core/lib/multistore' import storeCodeFromRoute from '@vue-storefront/core/lib/storeCodeFromRoute' -import { removeStoreCodeFromRoute } from '@vue-storefront/core/lib/multistore' ->>>>>>> refactoring storeCodeFromRoute, resolve store code on ssr // it's a good practice for all actions to return Promises with effect of their execution export const actions: ActionTree = { From e0debde7d743dc8c2d41b64c6e12e8333176d567 Mon Sep 17 00:00:00 2001 From: andrzejewsky Date: Mon, 23 Sep 2019 19:41:00 +0200 Subject: [PATCH 4/4] code review --- core/lib/storeCodeFromRoute.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/lib/storeCodeFromRoute.ts b/core/lib/storeCodeFromRoute.ts index 0a74afc31d..57b9567141 100644 --- a/core/lib/storeCodeFromRoute.ts +++ b/core/lib/storeCodeFromRoute.ts @@ -3,15 +3,15 @@ import config from 'config' import { LocalizedRoute } from './types' const getNormalizedPath = (matchedRouteOrUrl) => { - const matchingPath = typeof matchedRouteOrUrl === 'object' ? matchedRouteOrUrl.path : matchedRouteOrUrl + const matchingPath = matchedRouteOrUrl && (matchedRouteOrUrl.path || matchedRouteOrUrl) - return matchingPath.length > 0 && matchingPath[0] !== '/' ? `/${matchingPath}` : matchingPath + return matchingPath && (matchingPath.length > 0 && matchingPath[0] !== '/') ? `/${matchingPath}` : matchingPath } const getUrl = (matchedRouteOrUrl) => { const normalizedPath = getNormalizedPath(matchedRouteOrUrl) - if (typeof matchedRouteOrUrl === 'object') { + if (matchedRouteOrUrl && typeof matchedRouteOrUrl === 'object') { if (matchedRouteOrUrl['host']) { return matchedRouteOrUrl['host'] + normalizedPath }