diff --git a/CHANGELOG.md b/CHANGELOG.md index fc9267c0a3..6f8e7fb795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add `purgeConfig` to default.json and purge-config loader - @gibkigonzo (#4540) +- Separate theme installation and add it as yarn init:theme or as a step in yarn installer. - @gibkigonzo (4534, #4552) ### Fixed diff --git a/core/mixins/onBottomScroll.js b/core/mixins/onBottomScroll.js index b29d8fc184..5f8402791e 100644 --- a/core/mixins/onBottomScroll.js +++ b/core/mixins/onBottomScroll.js @@ -14,7 +14,7 @@ const isBottomVisible = () => { } /** - * By implementing this mixin add "onBottomScroll" mthod in component. + * By implementing this mixin add "onBottomScroll" method in component. * It will be invoked when view reach the bottom. */ export default { diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index aaba692aaf..960faaf56b 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -21,8 +21,8 @@ module.exports = { link: 'https://www.youtube.com/channel/UCkm1F3Cglty3CE1QwKQUhhg', }, { - text: 'Medium', - link: 'https://medium.com/the-vue-storefront-journal', + text: 'Blog', + link: 'https://blog.vuestorefront.io/', }, ], sidebar: { @@ -78,7 +78,7 @@ module.exports = { 'basics/feature-list', 'basics/recipes', 'basics/typescript', - 'basics/graphql', + // 'basics/graphql', 'basics/ssr-cache', 'basics/amp', 'basics/static-generator', @@ -115,18 +115,18 @@ module.exports = { 'data/data-loader' ], }, - { - title: 'Working with Vuex', - collapsable: false, - children: [ - 'vuex/introduction', - 'vuex/vuex-conventions', - 'vuex/product-store', - 'vuex/category-store', - 'vuex/stock-store', - 'vuex/attribute-store', - ], - }, + // { + // title: 'Working with Vuex', + // collapsable: false, + // children: [ + // 'vuex/introduction', + // 'vuex/vuex-conventions', + // 'vuex/product-store', + // 'vuex/category-store', + // 'vuex/stock-store', + // 'vuex/attribute-store', + // ], + // }, { title: 'Integrations', collapsable: false, @@ -156,7 +156,8 @@ module.exports = { children: [ 'archives/modules', 'archives/extensions', - 'archives/components' + 'archives/components', + 'archives/vuex' ], }, ], diff --git a/docs/guide/archives/vuex.md b/docs/guide/archives/vuex.md new file mode 100644 index 0000000000..33e86cc807 --- /dev/null +++ b/docs/guide/archives/vuex.md @@ -0,0 +1,540 @@ +# Vuex + +:::danger REMINDER +This document is _archived_ and _NOT_ relevant with the latest version which is `1.11` at the time of writing. Please keep in mind this document is supposed to help you maintain legacy product, not the fresh installation. +::: + +## Introduction + +All data processing and remote requests should be managed by Vuex data stores. The core modules generally contain `store` folder inside. +You can modify the existing store actions by responding to events. Events are specified in the docs below and can be found in the [core module](https://github.com/DivanteLtd/vue-storefront/tree/master/core), where `EventBus.$emit` has been mostly used for Vuex Actions. + +**You should put all the REST calls, Elasticsearch data queries inside the Vuex Actions.** This is our default design pattern for managing the data. + +### Vuex conventions + +Before you start working with Vuex, it's recommended to get familiar with our [vuex conventions](./vuex-conventions.md) + +### Vuex modules + +- [Product](product-store.md) +- [Category](category-store.md) +- [Cart](Cart%20Store.md) +- [Checkout](Checkout%20Store.md) +- [Order](Order%20Store.md) +- [Stock](stock-store.md) +- [Sync](sync-store.md) +- [User](User%20Store.md) +- [Attribute](attribute-store.md) +- [UI Store]() + +### Override existing core modules + +Existing core modules can be overridden in the themes store. Just import any core store modules and override them using the `extendStore()` utility method like the example given below in `src/modules/ui-store/index.ts`. + +``` +import coreStore from '@vue-storefront/core/store/modules/ui-store' +import { extendStore } from '@vue-storefront/core/lib/themes' + +const state = { + // override state of core ui module... +} + +const mutations = { + // override mutations of core ui module... +} + +const actions = { + // override actions of core ui module... +} + +export default extendStore(coreStore, { + state, + mutations, + actions +}) +``` + +And then import it in `src/modules/index.ts` + +``` +import ui from './ui-store' + +export default { + ui +} +``` + +### Related + +[Working with data](data.md) + + + +## Vuex conventions + +### Module +The Vuex module should be created for a specific set of functionalities. It should also have only absolutely necessary dependencies to other modules. The name of module should be short, quite clear about it’s destination, and have words separated by a dash. + +Good examples: + +- products +- product +- user +- checkout +- compare-products +- notifications +- order + +Bad examples: + +- next-module +- compare (because it’s not saying what it compares) + +### State +State properties should be simple and their structure should not be nested. Their names are written in underscore-case notation and indicate what they contain. We should avoid having more than one instance of an object, even between modules. In the vast majority of cases, they can be referenced by their unique ID property. Example: + +``` +{ + "products_map": { + "WS08": { + "sku": "WS08", + "name": "Minerva LumaTech™ V-Tee" + // other options + }, + "WS12": { + "sku": "WS12", + "name": "Radiant Tee" + // other options + }, + "WS08-XS-Black": { + "sku": "WS08-XS-Black", + "name": "Minerva LumaTech™ V-Tee" + // other options + } + // maaaaaaaany more products + }, + "current_product_id": "WS08-XS-Black", + "wishlist": ["MP01-32-Black", "MSH05-32-Black"], + "cart_items": [ + { + "sku": "WH09-XS-Green", + "qty": 3 + }, + { + "sku": "WH09-S-Red", + "qty": 1 + } + ] +} +``` + +Good examples: + + - categories_map +- current_category_id +- order +- product_parent_id + +Bad examples +- list +- elements + +``` +filters: { + available: {}, + chosen: {} +}, +``` + +### Getters +The Vuex state, except of mutations, should always be accessed by getters, including actions. Getter should: + +* Start from `is` when returns Boolean, or `get` otherwise +* Answer to question `what am I returning?` +* Contain module name to ensure that getter is unique through whole Vuex, but it doesn’t have to start with that name. First, it should have a natural name, so for example we have module `category` and in the state `availableFilters`. So `what am I returning?` -> `available Filters` and these filters are `category filters`. It's not a Boolean, it’s an array or map so we’re starting with `get` -> `getAvailableCategoryFilters` + +Good examples: + +- For state user -> isUserLoggedIn, getUser +- For state availableFilters -> getAvailableCategoryFilters +- For state currentProductId -> getCurrentProduct (because it gets product object from map), getCurrentProductId + +Bad examples: + +- totals +- product +- current +- list + +### Actions + +Every state change from outside of a module should be invoked as an action. Actions are meant to: + +- Fetch something from the server(or cache) — in this case, they have to be asynchronous (return promise). +- Mutate state of current module. +- Dispatch actions from the same module (to avoid repeating logic). +- Dispatch actions from another module (only if it’s absolutely required). +- Their names should be as unique as possible and simply describe what specific action will happen. **Almost every action should return promise.** We allow you to replicate conventions for existing methods like list or single in new modules to have a consistent API. + +Good examples: + +- fetchProduct - Gets product by ID from server or cache, sets it in products map, and returns it by getter. +- findProducts - Fetches products by specific query, sets them in products map, and returns them as array. +- setCurrentProduct - Param could be ID, it could dispatch fetchProduct, mutate it to productsMap, and mutate its ID to currentProductId. Also if productId is null, then it removes currentProduct. +- addCartItem +- toggleMicrocart + +Bad examples: + +- products +- reset + +### Mutations + +Finally we have mutations. Only mutations can change the state of the module. They should be synchronous (never return promise), not contain any logic (be extremely fast), except one needed to keep the state as it should be (for example, sets default value for state). Mutations should be invoked only by actions from the same module. In most cases, it should only be a single action that invokes a specific mutation. Types of mutations: + +- SET_ - The most common type of mutation. It can set an object (or whole array), set default value of object (or maybe clean array), +- ADD_ - It can add a new element to the state property, which is an array or add new element to map. +- REMOVE_ - An opposite to ADD. It can remove the map element or array element by index (or by finding object, which is not recommended on big arrays, as mutation could be slow). + +Good examples: + +- ADD_PRODUCT +- SET_CURRENT_PRODUCT_ID +- ADD_CATEGORY_FILTER +- REMOVE_WISHLIST_PRODUCT_ID + +Bad examples: + +- CATEGORY_UPD_CURRENT_CATEGORY +- TAX_UPDATE_RULES + + + +## Product Vuex Store + +The Product Store is designed to handle all actions related product data. It's responsible for loading the list of products or a single product as well as configuring the configurable products and managing the product attachments. + +This module works pretty tightly with Elasticsearch and operates on the [Product data format](../data/elasticsearch.md) + +### State + +```js +const state = { + breadcrumbs: { routes: [] }, + current: null, // shown product + current_options: { color: [], size: [] }, + current_configuration: {}, + parent: null, + list: [], + original: null, // default, not configured product + related: {}, +}; +``` + +The product state is generally populated by just two methods - [list](https://github.com/DivanteLtd/vue-storefront/blob/bd559f1baad7cd392bc5bae7b935a60484e2e6e5/src/store/modules/product.js#L395) and [single](https://github.com/DivanteLtd/vue-storefront/blob/bd559f1baad7cd392bc5bae7b935a60484e2e6e5/src/store/modules/product.js#L428) - and cleared to the defaults by [reset](https://github.com/DivanteLtd/vue-storefront/blob/bd559f1baad7cd392bc5bae7b935a60484e2e6e5/src/store/modules/product.js#L215). + +The product state data: + +- `breadcrumbs` - This is the list of routes used by the [Breadcrumbs component](https://github.com/DivanteLtd/vue-storefront/blob/master/core/components/Breadcrumbs.js) +- `current` - This is the product object with selected `configurable_children` variant, so it's the base product with attributes overridden by the values from selected `configurable_children` variant; it's used on [Product.vue page](https://github.com/DivanteLtd/vue-storefront/blob/bd559f1baad7cd392bc5bae7b935a60484e2e6e5/src/pages/Product.vue#L203). This is the product which is added to the cart after "Add to cart" +- `current_options` - The list used to populate the variant selector on the [Product.vue page](https://github.com/DivanteLtd/vue-storefront/blob/bd559f1baad7cd392bc5bae7b935a60484e2e6e5/src/themes/default/pages/Product.vue#L56). It contains dictionary of attributes and possible attribute values and labels, and it's populated by [setupVariants](https://github.com/DivanteLtd/vue-storefront/blob/bd559f1baad7cd392bc5bae7b935a60484e2e6e5/src/store/modules/product.js#L344) based on the `configurable_children` property. +- `current_configuration` The current product configuration. A dictionary of selected variant attributes; for example, it contains a dictionary of selected product attributes: + +```json +{ + "color": 123, + "size": 24 +} +``` + +Please note, we're using the Magento-like EAV attributes structure so the values here are attribute value indexes, not the values itself. Please take a look at [Data formats](../data/elasticsearch.md) for a reference. + +- `parent` - If the current product is a `type_id="single"`, then in this variable the parent, `configurable` product is stored. This data is populated only on `Product.vue` by [checkConfigurableParent](https://github.com/DivanteLtd/vue-storefront/blob/bd559f1baad7cd392bc5bae7b935a60484e2e6e5/src/store/modules/product.js#L323) +- `list` - This is an array of products loaded by [list](https://github.com/DivanteLtd/vue-storefront/blob/bd559f1baad7cd392bc5bae7b935a60484e2e6e5/src/store/modules/product.js#L395) +- `original` - Used only for `configurable` products, this is the base product with no selected variant. +- `related` - This is dictionary of related products; set outside this store (for [example here](https://github.com/DivanteLtd/vue-storefront/blob/master/src/themes/default/components/core/blocks/Product/Related.vue)) by calling and [related action](https://github.com/DivanteLtd/vue-storefront/blob/bd559f1baad7cd392bc5bae7b935a60484e2e6e5/src/store/modules/product.js#L528) + +### Events + +The following events are published from `product` store: + +- `EventBus.$emit('product-after-priceupdate', product)` - from [syncProductPrice](https://github.com/DivanteLtd/vue-storefront/blob/bd559f1baad7cd392bc5bae7b935a60484e2e6e5/src/store/modules/product.js#L33) after product price is synced with Magento. +- `EventBus.$emit('product-after-configure', { product: product, configuration: configuration, selectedVariant: selectedVariant })` from `configureProductAsync` (called by `product/configure` action after `product/single`). This event provides the information about selected product variant on the product page. +- `EventBus.$emit('product-after-list', { query: query, start: start, size: size, sort: sort, entityType: entityType, result: resp })` - this event emits the current product list as it's returned by `product/list`providing the current filters, etc. You can mark the specific product list identifier by setting the `meta` property; it's important because on a single page, this event can be executed multiple time for each individual block of products. +- `EventBus.$emit('product-after-single', { key: key, options: options, product: cachedProduct })` - After single product has been loaded (invoked by `product/single` action). + +### Actions + +The product store provides following public actions: + +#### `setupBreadcrumbs (context, { product })` + +This method is in charge of setting `state.breadcrumbs` to be used on the `Product.vue` page. It's called from `Product.vue:fetchData`. The `product` parameter is an [Elasticsearch product object](../data/elasticsearch.md). + +#### `syncPlatformPricesOver(context, { skus })` + +When the config option `products.alwaysSyncPlatformPricesOver` is on, Vue Storefront will request the current product prices each time when `product/single` or `product/list` action is dispatched. It's called exclusively by these actions and shouldn't be called manually. This method calls `vue-storefront-api` proxy to get the current prices from Magento or any other backend CMS. + +`skus` - this is an array with product SKUs to be synchronized. + +#### `setupAssociated (context, { product })` + +This method is called as a subsequent call of `Product.vue:fetchData` or `product/list` action. It's used to get the child products of `grouped` or `bundle` types of products. + +#### `checkConfigurableParent (context, {product})` + +This method is called by `Product.vue:fetchData` to check if current, simple product has got an configurable parent. If so, the redirect is being made to the parent product. It's a fix for [#508](https://github.com/DivanteLtd/vue-storefront/issues/508) + +#### `setupVariants (context, { product })` + +This method is subsequently called by `Product.vue:fetchData` to load all configurable attributes defined in `product.configurable_options` and then to populate `state.current_configuration` and `state.current_options`. The main usage of this action is to prepare a product to be configured by the user on the product page and to display the product configurator UI properly. + +#### `list (context, { query, start = 0, size = 50, entityType = 'product', sort = '', cacheByKey = 'sku', prefetchGroupProducts = true, updateState = true, meta = {} })` + +This is the key method to load the product list. It returns the Promise that contains the product list object. This method should be used everywhere you need to get product data. When `config.tax.calculateServerSide=false` this method runs product taxes calculator and synchronizes prices with Magento if it's required. + +**Events**: this method emits product list as `EventBus.$emit('product-after-list', { query: query, start: start, size: size, sort: sort, entityType: entityType, meta: meta, result: resp })` + +:::warning Important +This method synchronizes products for offline usage by storing the whole query results object into `localForage` and by caching each product individually (to be used on the product page, for example). +::: + +- `query` - This is the `bodybuilder` Elasticsearch query (please check `bodybuilder` package or for example `Home.vue` for a reference how to use it). + +- `start`, `size` - Both parameters are used for paging; start is the starting index; size is a page size. + +- `entityType` - By default it's of course set to `product` and it's mapped to Elasticsearch entity class. + +- `sort` - Product attribute using to sort. This field must be mapped in Elasticsearch as a numeric field, + +- `prefetchGroupProducts` - By default, it's set to true and causes the `setupAssociated` action to be dispatched to get all the associated products + +- `updateState` - If you set this to false, the `state.list` will not be updated, just the products will be returned. + +- `meta` - This is an optional attribute which is returned with the `product-after-list` event; it can be used for example to mark any specific ES call. + +#### `single (context, { options, setCurrentProduct = true, selectDefaultVariant = true, key = 'sku' })` + +This method subsequently dispatches the `product/list` action to get the products and synchronize the taxes/prices. When the product has been recently downloaded via `product/list` this method will return the cached version from `localForage`, but update the cache anyway. + +#### `configure (context, { product = null, configuration, selectDefaultVariant = true })` + +This action is used to configure the `configurable` product with specified attributes. It gets the `configuration` object, which should have the following format: `{ attribute_code: attribute_value_id }` and finds the `product.configurable_children` item which complies with this configuration. Then, it merges this specific `configurable_child` with the product itself - for example, setting the product.price to the configurable price, color, size etc. This method is used on: `Product.vue` page for allowing user to select color, size etc. The second usage for it is on `Category.vue` page after user selects some filters - the resulting products are configured to display the proper images (related to selected color and size) and prices. + +If `selectDefaultVariant` is set to true (default), the `state.current` will be altered with configured product. + +#### `setCurrent (context, productVariant)` + +Auxiliary method just to set `state.current` to productVariant. + +#### `setOriginal (context, originalProduct)` + +Auxiliary method just to set `state.original` to originalProduct. + +#### `related (context, { key = 'related-products', items })` + +Alters `state.related` dictionary to set specific list of related products to be displayed on `Product.vue` page (`RelatedProducts` component is used for this). + +### Getters + +All state members should have been accessed only by getters. Please take a look at the state reference for data formats. + +```js +const getters = { + getParentProduct: state => state.parent, + getCurrentProduct: state => state.current, + getCurrentProductConfiguration: state => state.current_configuration, + getOriginalProduct: state => state.original, + getCurrentProductOptions: state => state.current_options, + breadcrumbs: state => state.breadcrumbs, +}; +``` + + + +## Category Vuex Store + +The Category Store is designed to handle all actions related to category data. + +This module works pretty tightly with Elasticsearch and operates on the [Product data format](../data/elasticsearch.md) + +### State + +```js +const state = { + list: [], + current: {}, + filters: { color: [], size: [], price: [] }, + breadcrumbs: { routes: [] }, + current_path: [], // list of categories from root to current +}; +``` + +The category state is generally populated by just two methods- [list](https://github.com/DivanteLtd/vue-storefront/blob/06fbb89a5a8bc2c607847f65a7bca9ad54ed7146/core/store/modules/category.js#L38) and [single](https://github.com/DivanteLtd/vue-storefront/blob/06fbb89a5a8bc2c607847f65a7bca9ad54ed7146/core/store/modules/category.js#L70) - and cleared to the defaults by [reset](https://github.com/DivanteLtd/vue-storefront/blob/06fbb89a5a8bc2c607847f65a7bca9ad54ed7146/core/store/modules/category.js#L28) + +:::tip Note +The action `category/single` uses `localForage` cache only—no Elasticsearch data store directly. Because of this optimization, download the categories list by dispatching the `category/list` at first. +::: + +The category state data: + +- `breadcrumbs` - This is the list of routes used by the [Breadcrumbs component](https://github.com/DivanteLtd/vue-storefront/blob/master/core/components/Breadcrumbs.js) +- `current` - This is the current category object. +- `filters` is a current state of the category filters, a dictionary of selected variant attributes; for example it contains dictionary of selected product attributes: + +```json +{ + "color": 123, + "size": 24 +} +``` + +Please note, we're using the Magento-like EAV attributes structure so the values here are attribute value indexes, not the values itself. Please take a look at [Data formats](../data/elasticsearch.md) for a reference + +- `current_path` - this is the list of category objects: from current category to the top level root, + +### Events + +The following events are published from `category` store: + +- `EventBus.$emit('category-after-single', { category: mainCategory })` - from [category/single](https://github.com/DivanteLtd/vue-storefront/blob/06fbb89a5a8bc2c607847f65a7bca9ad54ed7146/core/store/modules/category.js#L70) after single category is loaded. +- `EventBus.$emit('category-after-current', { category: category })` - After current category has been changed - this is subsequent call of `category/single` action. +- `EventBus.$emit('category-after-reset', { })` - After the category has been reset (for example, in the process of moving from one category page to another). +- `EventBus.$emit('category-after-list', { query: qrObj, sort: sort, size: size, start: start, list: resp })` - This event emits the current category list as it's returned by `category/list`. + +### Actions + +The cart store provides following public actions: + +#### `list (context, { parent = null, onlyActive = true, onlyNotEmpty = false, size = 4000, start = 0, sort = 'position:asc' })` + +This is the key method to load the category list. It returns the Promise that contains the product list object. This method should be used everywhere you need to get products data. + +#### `single (context, { key, value, setCurrentCategory = true, setCurrentCategoryPath = true })` + +This method gets the single category from `localForage`. + +:::warning Important +To make this method work, you should call `category/list` before. This category works only on localForage and cannot access Elasticsearch directly +::: + +:::warning Important +This method synchronizes products for offline usage by storing the whole query results object into `localForage` and by caching each category individually (to be used on the product page, for example). +::: + +**Events**: this method emits category list as `EventBus.$emit('category-after-list', { query: qrObj, sort: sort, size: size, start: start, list: resp })` + +- `parent` - `category` - Object to load the subcategories only. + +- `start`, `size` - Both parameters are used for paging; start is the starting index; size is a page size. + +- `onlyActive` - (bool) load only the categories marked as active in CMS (for example, in Magento). + +- `sort` - Category attribute used to sort. This field must be mapped in Elasticsearch as a numeric field. + +- `onlyNotEmpty` - (bool) load only the categories that contain any products. + +### Getters + +All state members should be accessed only by getters. Please take a look at the state reference for data formats. + +```js +const getters = { + current: state => state.current, + list: state => state.list, +}; +``` + + + +## Stock Vuex Store + +Stock Store is designed to handle stock-quantity checks. + +### Events + +The following events are published from `stock` store: + +- `stock-after-check` - Emitted just after the stock item has been received from eCommerce backend / Magento. + + +### Actions + +The cart store provides the following public actions: + +#### `check (context, { product, qty = 1 })` + +Check if the `product` can be added to the shopping cart with a given quantity. + +The resulting promise is expanded to the following object: + +```js +{ + qty: 100, + status: 'ok', // another option is: 'out_of_stock' + onlineCheckTaskId: 14241 +} +``` + + +## Attribute Vuex Store + +Attribute Store is designed to handle all actions related to attributes management. + +### State + +```js + state: { + list_by_code: {}, + list_by_id: {}, + labels: {} + }, +``` + +As we're using the attributes dictionary for the product management in a very similar way as Magento ([EAV model](http://www.xpertdeveloper.com/2010/10/what-is-eav-model-in-magento/)), we're operating on the attributes, attribute types, and dictionaries. + +Attributes are **explicitly** loaded by the user by calling the `attribute/list` method. For example, when you're going to work with customizable attributes of the product, or to work on variants, you need to prefetch the attributes metadata: + +```js +this.$store.dispatch('attribute/list', { + filterValues: [true], + filterField: 'is_user_defined', +}); +``` + +This is an example from [product compare feature](https://github.com/DivanteLtd/vue-storefront/blob/c954b96f6633a201e10bed1d2e4c0def1aeb3071/core/pages/Compare.vue). + +The attribute state data: + +- `list_by_code` - This is a dictionary where you can get the specific attribute just by accessing the `list_by_code['color']` etc. +- `list_by_id` - This is a dictionary where you can get the specific attribute just by accessing the `list_by_id[123]` etc. +- `labels` - The preloaded labels of attribute values (the V in EAV). + +### Actions + +The attribute store provides the following public actions: + +#### `list (context, { filterValues = null, filterField = 'attribute_code', size = 150, start = 0 })`` + +This method is used to load the attributes metadata. `filterValues` is an array of multiple values like: `['color', 'size']` and the `filterField` is the attribute field to compare the `filterValues` against. Usually, it is a `attribute_code` or `attribute_id`. The `size` and `start` are just used to limit the list. + +### Helpers + +Attribute module exports one very popular helper method: + +#### `export function optionLabel (state, { attributeKey, searchBy = 'code', optionId })` + +This is used to get the label for specific `optionId`. For example, when the user filters products and uses the 165 attribute_value we can call `optionLabel( { attributeKey: 'color', optionId: 165 })` to get back 'Red' label. + +### Getters + +All state members should have been accessed only by getters. Please take a look at the state reference for data formats + +```js +export default { + attributeListByCode: state => state.list_by_code, + attributeListById: state => state.list_by_id, +}; +``` diff --git a/docs/guide/basics/configuration.md b/docs/guide/basics/configuration.md index 1a471f358e..69fd63a775 100644 --- a/docs/guide/basics/configuration.md +++ b/docs/guide/basics/configuration.md @@ -6,6 +6,10 @@ The Vue Storefront application uses the [node-config](https://github.com/lorenwe - `local.json` is the second configuration file which is .gitignore'd from the repository. This is the place where you should store all instance-specific configuration variables. +:::tip NOTE +Please not that the `config` is bundled into JavaScript files that are returned to the user's browser. Please **NEVER PUT ANY SENSITIVE INFORMATION** into the config file of `vue-storefront`. If your application requires some authorization / tokens /etc - please store them and access via dedicated [`vue-storefront-api`](https://github.com/DivanteLtd/vue-storefront-api) or [`storefront-api`](https://github.com/DivanteLtd/storefront-api) extension that will prevent these sensitive information from being returned to the users. +::: + The structure of these files is exactly the same! Vue Storefront does kind of `Object.assign(default, local)` (but with the deep-merge). This means that the `local.json` overrides the `default.json` properties. :::tip NOTE @@ -13,7 +17,7 @@ Please take a look at the `node-config` docs as the library is open for some oth ::: :::tip NOTE -Currently, the configuration files are being processed by the webpack during the build process. This means that whenever you apply some configuration changes, you shall rebuild the app, even when using the `yarn dev` mode. This limitation can be solved with the VS 1.4 special config variable. Now the config can be reloaded on the fly with each server request if `config.server.dynamicConfigReload`is set to true. However, in that case, the config is added to `window.**INITIAL_STATE**` with the responses. +Currently, the configuration files are being processed by the webpack during the build process. This means that whenever you apply some configuration changes, you shall rebuild the app, even when using the `yarn dev` mode. This limitation can be solved with the VS 1.4 special config variable. Now the config can be reloaded on the fly with each server request if `config.server.dynamicConfigReload`is set to true. However, in that case, the config is added to `window.INITIAL_STATE` with the responses. When you using the `config.server.dynamicConfigReload` plase remember about `config.server.dynamicConfigExclude` and `config.server.dynamicConfigInclude`. ::: diff --git a/docs/guide/cookbook/devops.md b/docs/guide/cookbook/devops.md index 81a52ff825..c6a5d50cf5 100644 --- a/docs/guide/cookbook/devops.md +++ b/docs/guide/cookbook/devops.md @@ -4,11 +4,13 @@ In this chapter, we will cover : [[toc]] ## 0. Introduction -As the industry matures, developers' concerns are poised to move towards dealing with operations of server infrastructure rather than development itself. The reason is simple and clear, it's simply more painful to deal with server operation. I am not saying AI programmer will take away your job, but there are lots of resources out there to help you build a software with such as frameworks, libraries, templates, best practices of all sorts and so on. Not to mention, IDEs on steroid are getting better all the time that guide you for the degree to which is foolproof. It just takes a single fool to build a great software these days. (Forgive me!) +As the industry matures, developers' concerns are poised to move towards dealing with operations of server infrastructure rather than development itself. The reason is clear as fire, it's simply more painful to deal with server operation. I am not saying AI programmer will take away your job, but there are lots of resources out there to help you build a software with such as frameworks, libraries, templates, best practices of all sorts, and so on. Not to mention, IDEs on steroid are getting better all the time which provides you with tools for the degree to which proves foolproof. It just takes a single fool to build a great software these days. (Forgive me!) -However, something people underestimated at first sight turned out to be the most stressful experiences; _Set the servers up and running_. (and maintain it!) Something you thought the last simple step for your development project turned out to be the separate portion of substantial workloads that needs to be taken care of from the beginning of the project. Now, that's where _DevOps_ comes in. +However, something people underestimated at the first sight turned out to be the most stressful experience; _Set the servers up and running_. (and maintain it!) Something you thought the last simple step for your development project turned out to be the separate portion of substantial workloads that needs to be taken care of from the beginning of the project. Now, that's where _DevOps_ comes in. ## 1. Infrastructure design +_Vue Storefront_ approaches the online commerce problem with _MSA (Microservice Architecture)_ methodology. A handful army of technology stacks is positioned in place to bring in the maximum efficiency on top of the PWA concept. + ### 1. Preparation ### 2. Recipe ### 3. Peep into the kitchen (what happens internally) @@ -24,7 +26,15 @@ However, something people underestimated at first sight turned out to be the mos

-## 3. DevOps in VSF context +## 3. Cache Strategy +### 1. Preparation +### 2. Recipe +### 3. Peep into the kitchen (what happens internally) +### 4. Chef's secret (protip) +
+
+ +## 4. DevOps in VSF context ### 1. Preparation ### 2. Recipe ### 3. Peep into the kitchen (what happens internally) @@ -32,7 +42,7 @@ However, something people underestimated at first sight turned out to be the mos

-## 4. In case of StorefrontCloud.io +## 5. In case of StorefrontCloud.io ### 1. Preparation ### 2. Recipe ### 3. Peep into the kitchen (what happens internally) @@ -40,7 +50,7 @@ However, something people underestimated at first sight turned out to be the mos

-## 5. Rooms to improve +## 6. Rooms to improve ### 1. Preparation ### 2. Recipe ### 3. Peep into the kitchen (what happens internally) diff --git a/docs/guide/integrations/integrations.md b/docs/guide/integrations/integrations.md index 48df34e8a6..e2090e2c23 100644 --- a/docs/guide/integrations/integrations.md +++ b/docs/guide/integrations/integrations.md @@ -8,3 +8,4 @@ Here is a short list of existing Vue Storefront integrations with links to their - [Vue Storefront + Pimcore](https://github.com/DivanteLtd/pimcore2vuestorefront) - [Magento2 Product Reviews](https://github.com/DivanteLtd/vue-storefront/blob/develop/doc/Reviews.md) - [Vue Storefront 3rd party platforms integration boilerplate](https://github.com/DivanteLtd/vue-storefront-integration-boilerplate) - This is the API you should implement to integrate a third-party platform. +- [Vue Storefront + Fresh Relevance](https://github.com/TriggeredMessaging/vsf-freshrelevance) diff --git a/docs/guide/vuex/introduction.md b/docs/guide/vuex/introduction.md index 0363144811..0e6d4110fb 100644 --- a/docs/guide/vuex/introduction.md +++ b/docs/guide/vuex/introduction.md @@ -1,6 +1,6 @@ # Introduction -All data processing and remote requests should be managed by Vuex data stores. The core module contains more than [10 default data stores](https://github.com/DivanteLtd/vue-storefront/tree/master/core/store/modules) and can be easily extended by [store extensions](../extensions/extensions.md). +All data processing and remote requests should be managed by Vuex data stores. The core modules generally contain `store` folder inside. You can modify the existing store actions by responding to events. Events are specified in the docs below and can be found in the [core module](https://github.com/DivanteLtd/vue-storefront/tree/master/core), where `EventBus.$emit` has been mostly used for Vuex Actions. **You should put all the REST calls, Elasticsearch data queries inside the Vuex Actions.** This is our default design pattern for managing the data. diff --git a/packages/cli/package.json b/packages/cli/package.json index cb549d0af4..c434143c77 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -13,7 +13,7 @@ "author": "Filip Rakowski (@filrak)", "license": "MIT", "dependencies": { - "execa": "^1.0.0", + "execa": "^4.0.2", "fs-extra": "^8.1.0", "inquirer": "^6.3.1", "listr": "^0.14.3", diff --git a/packages/cli/scripts/install.js b/packages/cli/scripts/install.js index bdab543fce..0c4d8d7c7d 100644 --- a/packages/cli/scripts/install.js +++ b/packages/cli/scripts/install.js @@ -20,12 +20,12 @@ module.exports = function (installationDir) { const tasks = { installDeps: { title: 'Installing dependencies', - task: () => execa.shell('cd ' + installationDir + ' && yarn') + task: () => execa.command('cd ' + installationDir + ' && yarn cache clean && yarn', { shell: true }) }, cloneVersion: { title: 'Copying Vue Storefront files', task: answers => { - return execa.shell(`git clone --quiet --single-branch --branch ${answers.specificVersion} https://github.com/DivanteLtd/vue-storefront.git ${installationDir} && cd ${installationDir}/core/scripts && git remote rm origin`) + return execa.command(`git clone --quiet --single-branch --branch ${answers.specificVersion} https://github.com/DivanteLtd/vue-storefront.git ${installationDir} && cd ${installationDir}/core/scripts && git remote rm origin`, { shell: true }) } }, ...createThemeTasks(installationDir), @@ -35,11 +35,11 @@ module.exports = function (installationDir) { }, getStorefrontVersions: { title: 'Check available versions', - task: () => execa.stdout('git', ['ls-remote', '--tags', 'https://github.com/DivanteLtd/vue-storefront.git']).then(result => { - allTags = result.match(/refs\/tags\/v1.([0-9.]+)(-rc.[0-9])?/gm).map(tag => tag.replace('refs/tags/', '')) + task: () => execa('git', ['ls-remote', '--tags', 'https://github.com/DivanteLtd/vue-storefront.git']).then(({ stdout }) => { + allTags = stdout.match(/refs\/tags\/v1.([0-9.]+)(-rc.[0-9])?/gm).map(tag => tag.replace('refs/tags/', '')) allTags = semverSortDesc(allTags) - execa.stdout('git', ['ls-remote', '--heads', 'https://github.com/DivanteLtd/vue-storefront.git']).then(branches => { - let rcBranches = branches.match(/refs\/heads\/release\/v1.([0-9.x]+)/gm).map(tag => tag.replace('refs/heads/', '')) + execa('git', ['ls-remote', '--heads', 'https://github.com/DivanteLtd/vue-storefront.git']).then(({ stdout }) => { + let rcBranches = stdout.match(/refs\/heads\/release\/v1.([0-9.x]+)/gm).map(tag => tag.replace('refs/heads/', '')) availableBranches = [...rcBranches, ...availableBranches] }) }).catch(e => { diff --git a/packages/cli/themeTasks.js b/packages/cli/themeTasks.js index 607c60e1ce..598c3544f9 100644 --- a/packages/cli/themeTasks.js +++ b/packages/cli/themeTasks.js @@ -13,18 +13,18 @@ const createThemeTasks = (installationDir = 'vue-storefront') => ({ title: 'Installing dependencies', task: (answers) => { const _installationDir = answers.vsf_dir || installationDir - return execa.shell('cd ' + _installationDir + ' && yarn') + return execa.command('cd ' + _installationDir + ' && yarn cache clean && yarn', { shell: true }) } }, cloneTheme: { title: 'Copying Vue Storefront theme', task: answers => { const _installationDir = answers.vsf_dir || installationDir - return execa.shell([ + return execa.command([ `git clone --quiet --single-branch --branch ${answers.themeBranch} https://github.com/DivanteLtd/vsf-${answers.themeName}.git ${_installationDir}/src/themes/${answers.themeName}`, `cd ${_installationDir}/src/themes/${answers.themeName}`, `git remote rm origin` - ].join(' && ')) + ].join(' && '), { shell: true }) }, skip: answers => { const _installationDir = answers.vsf_dir || installationDir @@ -100,7 +100,8 @@ const createThemePrompt = (installationDir = 'vue-storefront') => [ name: themeConfig.label, value: themeName })) - } + }, + default: 'default' }, { type: 'list', @@ -110,7 +111,8 @@ const createThemePrompt = (installationDir = 'vue-storefront') => [ .map(([branchName, branchLabel]) => ({ name: branchLabel, value: branchName - })) + })), + default: 'master' } ] diff --git a/yarn.lock b/yarn.lock index c11e261f92..e4791d9c11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7894,6 +7894,21 @@ execa@^3.2.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.2.tgz#ad87fb7b2d9d564f70d2b62d511bee41d5cbb240" + integrity sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + executable@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c"