From d25be61fdb94c3f5ecd96090975ae6398ed99ecd Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 21 Sep 2021 17:18:10 +0200 Subject: [PATCH] init --- .eslintrc.js | 10 ++ .gitignore | 15 +++ README.md | 83 ++++++++++++++- babel.config.json | 10 ++ package.json | 51 +++++++++ prettier.config.js | 7 ++ src/components/Seo/Canonical.js | 34 ++++++ src/components/Seo/Hreflangs.js | 26 +++++ src/components/Seo/Markup.js | 100 ++++++++++++++++++ src/components/Seo/Seo.js | 41 +++++++ src/components/Seo/features/index.js | 31 ++++++ src/components/Seo/index.js | 1 + src/documentation/local-intercept.js | 48 +++++++++ src/hooks/CMS/useCmsPageSeo.js | 56 ++++++++++ src/hooks/Category/useCategorySeo.js | 56 ++++++++++ src/hooks/Product/useProductSeo.js | 72 +++++++++++++ src/index.js | 7 ++ src/intercept.js | 36 +++++++ src/targets/CMS/wrapUseCmsPage.js | 23 ++++ .../Category/wrapUseCategoryContent.js | 23 ++++ .../Product/wrapUseProductFullDetails.js | 26 +++++ src/urls.congif.js | 14 +++ 22 files changed, 769 insertions(+), 1 deletion(-) create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 babel.config.json create mode 100644 package.json create mode 100644 prettier.config.js create mode 100644 src/components/Seo/Canonical.js create mode 100644 src/components/Seo/Hreflangs.js create mode 100644 src/components/Seo/Markup.js create mode 100644 src/components/Seo/Seo.js create mode 100644 src/components/Seo/features/index.js create mode 100644 src/components/Seo/index.js create mode 100755 src/documentation/local-intercept.js create mode 100644 src/hooks/CMS/useCmsPageSeo.js create mode 100644 src/hooks/Category/useCategorySeo.js create mode 100644 src/hooks/Product/useProductSeo.js create mode 100644 src/index.js create mode 100644 src/intercept.js create mode 100644 src/targets/CMS/wrapUseCmsPage.js create mode 100644 src/targets/Category/wrapUseCategoryContent.js create mode 100644 src/targets/Product/wrapUseProductFullDetails.js create mode 100644 src/urls.congif.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..446cece --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,10 @@ +const config = { + parser: 'babel-eslint', + extends: ['@magento'], + rules: { + 'no-undef': 'off', + 'no-useless-escape': 'off' + } +}; + +module.exports = config; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e1513c --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.idea +.vscode +coverage +node_modules +storybook-dist +test-results +dist +.DS_Store +.env +build-stats.json +npm-debug.log +lastCachedGraphQLSchema.json +test-report.xml +test-results.json +yarn-error.log diff --git a/README.md b/README.md index acc58c3..85fa07b 100644 --- a/README.md +++ b/README.md @@ -1 +1,82 @@ -# mageworx-seo-veniapwa \ No newline at end of file +# MageWorx SEO Suite Ultimate extension for Magento Venia PWA +This add-on integrates [SEO Suite Ultimate extension for Magento 2](https://www.mageworx.com/magento2-extensions/seo-tools-services.html) using [MageWorx SeoBase GraphQl extension](https://github.com/mageworx/MageWorx_SeoBaseGraphQl) with [Magento 2 Venia PWA storefront](https://magento.github.io/pwa-studio/venia-pwa-concept/). + +## Features +- Canonical URLs to eliminate duplicate content +- Alternate URLs +- Meta robots +- Rich snippets +- Seller & Website markup + +## Upload the extension +1. Create directory `@mageworx/seo-veniapwa` in the root of your project +2. Copy this project to `@mageworx/seo-veniapwa` +3. Run `yarn add file:./@mageworx/seo-veniapwa` in the root of your project +4. Open `local-intercept.js` in the root of your project and put this code into `function localIntercept`. Pay attention, `function localIntercept` must have `targets` as parameter (you can see example of `local-intercept.js` in `@mageworx/seo-veniapwa/documentation`). +``` +/* MageWorx seo-veniapwa veniapwa start */ +const seoTargetables = Targetables.using(targets); + +// product +const ProductDetails_seo = seoTargetables.reactComponent( + '@magento/venia-ui/lib/components/ProductFullDetail/productFullDetail.js' +); +const SeoProductDetails = ProductDetails_seo.addImport("{Seo} from '../../../../../../@mageworx/seo-veniapwa/src/components/Seo'"); +ProductDetails_seo.insertAfterJSX( + '
', + `<${SeoProductDetails} seoData={productDetails.seoAttributes}/>` +); + +// category +const CategoryContent_seo = seoTargetables.reactComponent( + '@magento/venia-ui/lib/RootComponents/Category/categoryContent.js' +); +const SeoCategoryContent = CategoryContent_seo.addImport("{Seo} from '../../../../../../@mageworx/seo-veniapwa/src/components/Seo'"); +CategoryContent_seo.insertAfterJSX( + '
', + `<${SeoCategoryContent} seoData={talonProps.seoAttributes} />` +); + +// cms page +const CmsPage_seo = seoTargetables.reactComponent( + '@magento/venia-ui/lib/RootComponents/CMS/cms.js' +); +const SeoCmsPage = CmsPage_seo.addImport("{Seo} from '../../../../../../@mageworx/seo-veniapwa/src/components/Seo'"); +CmsPage_seo.surroundJSX( + '', + `
` +); +CmsPage_seo.insertAfterJSX( + '', + `<${SeoCmsPage} seoData={talonProps.seoAttributes} />` +); +/* MageWorx seo-veniapwa veniapwa end */ +``` +5. Check that your `local-intercept` has this code before `module.exports`, if don't have you should add them (you can see example of `local-intercept.js` in `@mageworx/seo-veniapwa/documentation`) +``` +const { Targetables } = require('@magento/pwa-buildpack'); +``` +6. Let's run your project +``` +yarn watch +``` + +## Urls config +You can change or add your custom urls for hreflang in `@mageworx/seo-veniapwa/src/hreflangs.config.js`, for exmaple: +``` +const hreflangs_config = { + ... + {type: "store", code: "default", url: "https://magento-store.com/"}, + {type: "store", code: "toys", url: "https://magento-toys.com/"}, + {type: "lang", code: "de-DE", url: "https://magento-store.com/de/"}, + {type: "lang", code: "en-US", url: "https://magento-store.com/en/"}, + ... +}; +``` +On frontend it will be look like: +``` + + +``` + + diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 0000000..6f51179 --- /dev/null +++ b/babel.config.json @@ -0,0 +1,10 @@ +{ + "presets": [ + "@babel/preset-react", + "@babel/preset-env" + ], + "plugins": [ + "@babel/plugin-transform-react-jsx", + "@babel/plugin-proposal-class-properties" + ] +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..3269945 --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "@mageworx/seo-veniapwa", + "author": "MageWorx", + "version": "1.0.0", + "main": "src/index.js", + "pwa-studio": { + "targets": { + "intercept": "src/intercept.js" + } + }, + "scripts": { + "format": "prettier --ignore-path .gitignore \"src/**/*.+(ts|js|tsx)\" --write", + "lint": "eslint --ignore-path .gitignore 'src/**/{*.js,package.json}'", + "prepare": "install-peers" + }, + "lint-staged": { + "./src/**/*.{ts,js,jsx,tsx}": [ + "yarn lint --fix", + "yarn format" + ] + }, + "peerDependencies": { + "@apollo/client": "~3.1.2", + "@magento/peregrine": "~7.0.0", + "@magento/pwa-buildpack": ">=6.0.0", + "@magento/venia-ui": "~4.0.0", + "react": "~16.9.0", + "react-dom": "^16.12.0", + "graphql-tag": "~2.10.1", + "webpack": "~4.38.0" + }, + "devDependencies": { + "@babel/core": "^7.11.6", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-syntax-class-properties": "^7.12.1", + "babel-eslint": "~10.0.1", + "babel-preset-env": "^1.7.0", + "babel-preset-react": "^6.24.1", + "eslint": "^7.32.0", + "eslint-config-prettier": "^6.15.0", + "eslint-plugin-jsx-a11y": "^6.0.3", + "eslint-plugin-package-json": "^0.1.4", + "eslint-plugin-react": "^7.9.1", + "eslint-plugin-react-hooks": "^1.6.0", + "identity-obj-proxy": "^3.0.0", + "install-peers-cli": "^2.2.0", + "lint-staged": "^10.0.8", + "prettier": "^1.9.2", + "prettier-check": "^2.0.0" + } +} diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..7d36ec7 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,7 @@ +const config = { + singleQuote: true, + tabWidth: 4, + trailingComma: 'none' +}; + +module.exports = config; diff --git a/src/components/Seo/Canonical.js b/src/components/Seo/Canonical.js new file mode 100644 index 0000000..e69d2bf --- /dev/null +++ b/src/components/Seo/Canonical.js @@ -0,0 +1,34 @@ +import React from 'react'; +import { Helmet } from 'react-helmet-async'; + +import {findCodeFromConfig, getCurrentHostname} from "./features"; + +const Canonical = props => { + const {canonical} = props; + if (!canonical) return null; + + let canonical_result + if (canonical.url) { + if (canonical.url.match(/https?:\/\//i)) { + canonical_result = ; + } + else if (canonical.code) { + let elem_from_config = findCodeFromConfig(canonical.code, "store", true); + if (elem_from_config) { + canonical_result = ; + } + else return null; + } + else { + canonical_result = ; + } + return ( + + {canonical_result} + + ); + } + return null; +}; + +export default Canonical; diff --git a/src/components/Seo/Hreflangs.js b/src/components/Seo/Hreflangs.js new file mode 100644 index 0000000..85324b6 --- /dev/null +++ b/src/components/Seo/Hreflangs.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { Helmet } from 'react-helmet-async'; + +import {findCodeFromConfig} from "./features"; + +const Hreflangs = props => { + const {mw_hreflangs} = props; + if (!mw_hreflangs || !mw_hreflangs.items) return null; + + let hreflangs_result = mw_hreflangs.items.map((item, itemNum) => { + let elem_from_config = findCodeFromConfig(item.code, "lang",true); + if (elem_from_config) { + let url = elem_from_config + item.url; + return ; + } + else return null; + }) + return ( + + {hreflangs_result} + + ); + return null; +}; + +export default Hreflangs; diff --git a/src/components/Seo/Markup.js b/src/components/Seo/Markup.js new file mode 100644 index 0000000..901720f --- /dev/null +++ b/src/components/Seo/Markup.js @@ -0,0 +1,100 @@ +import React from 'react'; +import { Helmet } from 'react-helmet-async'; + +import {getCurrentHostname} from "./features"; + +const changeAllUrlsToLocal = (str) => { + str = str.replaceAll(/\\\//g, "/"); + let resultUrl = str; + + let currentHost = getCurrentHostname(); + // for meta content + resultUrl = resultUrl.replaceAll(/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/g, `${currentHost}$2"`); + // for script content (only url and image) (:?"image":")] + resultUrl = resultUrl.replaceAll(/(:?"url":")https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)"/g, `$1${currentHost}$3"`); + resultUrl = resultUrl.replaceAll(/(:?"image":")https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)"/g, `$1${currentHost}$3"`); + return resultUrl; +} + +const getMetaArrayFromStr = (str) => { + return str.match(//gi); +} + +const getScriptsArrayFromStr = (str) => { + return str.match(/.+?<\/script>/gi); +} + +const getContentAndAttributesOfMeta = (str) => { + const metaData = { + content: "", + property: "", + } + let content = str.match(//i); + let property = str.match(//i); + if (content && content.length>1) { + metaData.content = changeAllUrlsToLocal(content[1]); + } + if (property && property.length>1) { + metaData.property = property[1]; + } + return metaData; +} + +const getContentAndAttributesOfScript = (str) => { + const scriptData = { + content: "", + type: "", + } + let content = str.match(/(.+?)<\/script>/i); + let type = str.match(/.+?<\/script>/i); + if (content && content.length>1) { + scriptData.content = changeAllUrlsToLocal(content[1]); + } + if (type && type.length>1) { + scriptData.type = type[1]; + } + return scriptData; +} + +const getMetaResultOfJsxFromStr = (str) => { + let metaArray = getMetaArrayFromStr(str); + let result = metaArray.map((script, num) => { + let attributes = getContentAndAttributesOfMeta(script); + const {content, property} = attributes; + return + }); + return result; +} + +const getScriptsResultOfJsxFromStr = (str) => { + let scriptsArray = getScriptsArrayFromStr(str); + let result = scriptsArray.map((script, num) => { + let attributes = getContentAndAttributesOfScript(script); + const {type, content} = attributes; + return + }); + return result; +} + +const Markup = props => { + const {markup} = props; + if (!markup) return null; + + let markup_result = []; + if (markup.rich_snippets) { + if (markup.rich_snippets.product) markup_result = markup_result.concat(getScriptsResultOfJsxFromStr(markup.rich_snippets.product)); + if (markup.rich_snippets.seller) markup_result = markup_result.concat(getScriptsResultOfJsxFromStr(markup.rich_snippets.seller)); + if (markup.rich_snippets.website) markup_result = markup_result.concat(getScriptsResultOfJsxFromStr(markup.rich_snippets.website)); + if (markup.rich_snippets.webpage) markup_result = markup_result.concat(getScriptsResultOfJsxFromStr(markup.rich_snippets.webpage)); + } + if (markup.social_markup) { + markup_result = markup_result.concat(getMetaResultOfJsxFromStr(markup.social_markup)); + } + return ( + + {markup_result} + + ) +}; + +export default Markup; diff --git a/src/components/Seo/Seo.js b/src/components/Seo/Seo.js new file mode 100644 index 0000000..86a1342 --- /dev/null +++ b/src/components/Seo/Seo.js @@ -0,0 +1,41 @@ +import React from 'react'; +import { Helmet } from 'react-helmet-async'; + +import Hreflangs from "./Hreflangs"; +import Markup from "./Markup"; +import Canonical from "./Canonical"; + +const Seo = props => { + const {seoData} = props; + if (!seoData) return null; + + const { + meta_robots, + mw_canonical_url, + mw_hreflangs, + mw_seo_markup + } = seoData; + + let canonical; + let meta_robots_JSX; + let hreflangs; + let markup; + if (seoData) { + meta_robots_JSX = meta_robots && ; + canonical = + hreflangs = ; + markup = + } + + return ( + <> + + {meta_robots_JSX} + + {hreflangs} + {markup} + {canonical} + + ) +}; +export default Seo; diff --git a/src/components/Seo/features/index.js b/src/components/Seo/features/index.js new file mode 100644 index 0000000..6e4be54 --- /dev/null +++ b/src/components/Seo/features/index.js @@ -0,0 +1,31 @@ +import urls_config from "../../../urls.congif"; + +export const getCurrentHostname = (isNeedSlash) => { + let currentHostname = window.location.protocol + "//" + window.location.hostname; + if (window.location.port.length > 0) currentHostname += ":" + window.location.port; + if (isNeedSlash) currentHostname += "/"; + return currentHostname; +} + +export const findCodeFromConfig = (code, type, isNeedSlash) => { + const elem = urls_config.find((element) => { + if (element.type === type) { + if (element.code === code) { + return true; + } + } + }) + if (elem) { + let url = elem.url; + if (isNeedSlash) { + // "http://demo.com" => "http://demo.com/" + if (url[url.length - 1] !== "/") url += "/"; + } + else { + // "http://demo.com/" => "http://demo.com" + if (url[url.length - 1] === "/") url = url.substring(0, url.length - 1); + } + return url + } + else return null; +} diff --git a/src/components/Seo/index.js b/src/components/Seo/index.js new file mode 100644 index 0000000..631c902 --- /dev/null +++ b/src/components/Seo/index.js @@ -0,0 +1 @@ +export { default as Seo } from "./Seo"; diff --git a/src/documentation/local-intercept.js b/src/documentation/local-intercept.js new file mode 100755 index 0000000..d262823 --- /dev/null +++ b/src/documentation/local-intercept.js @@ -0,0 +1,48 @@ +const { Targetables } = require('@magento/pwa-buildpack'); + +function localIntercept(targets) { + /* MageWorx seo-veniapwa start */ + const seoTargetables = Targetables.using(targets); + + // product + const ProductDetails_seo = seoTargetables.reactComponent( + '@magento/venia-ui/lib/components/ProductFullDetail/productFullDetail.js' + ); + const SeoProductDetails = ProductDetails_seo.addImport("{Seo} from '../../../../../../@mageworx/seo-veniapwa/src/components/Seo'"); + ProductDetails_seo.insertAfterJSX( + '
', + `<${SeoProductDetails} seoData={productDetails.seoAttributes}/>` + ); + + // category + const CategoryContent_seo = seoTargetables.reactComponent( + '@magento/venia-ui/lib/RootComponents/Category/categoryContent.js' + ); + const SeoCategoryContent = CategoryContent_seo.addImport("{Seo} from '../../../../../../@mageworx/seo-veniapwa/src/components/Seo'"); + CategoryContent_seo.insertAfterJSX( + '
', + `<${SeoCategoryContent} seoData={talonProps.seoAttributes} />` + ); + + // cms page + const CmsPage_seo = seoTargetables.reactComponent( + '@magento/venia-ui/lib/RootComponents/CMS/cms.js' + ); + const SeoCmsPage = CmsPage_seo.addImport("{Seo} from '../../../../../../@mageworx/seo-veniapwa/src/components/Seo'"); + CmsPage_seo.surroundJSX( + '', + `
` + ); + CmsPage_seo.insertAfterJSX( + '', + `<${SeoCmsPage} seoData={talonProps.seoAttributes} />` + ); + CmsPage_seo.insertAfterJSX( + '', + `<${SeoCmsPage} seoData={talonProps.seoAttributes} />` + ); + /* MageWorx seo-veniapwa end */ +} + +module.exports = localIntercept; + diff --git a/src/hooks/CMS/useCmsPageSeo.js b/src/hooks/CMS/useCmsPageSeo.js new file mode 100644 index 0000000..1493b71 --- /dev/null +++ b/src/hooks/CMS/useCmsPageSeo.js @@ -0,0 +1,56 @@ +import {useEffect, useMemo} from "react"; +import { useQuery } from "@apollo/client"; +import gql from "graphql-tag"; + +const GET_CMS_SEO = gql` + query GetCmsPageSeo($id: Int!) { + cmsPage(id: $id) { + mw_canonical_url { + url + code + } + meta_robots + mw_hreflangs { + items { + url + code + } + } + mw_seo_markup { + social_markup + rich_snippets { + website + seller + webpage + } + } + } + } +`; + +const useCmsPageSeo = props => { + const { id } = props; + + const { loading, error, data } = useQuery(GET_CMS_SEO, { + fetchPolicy: 'cache-and-network', + nextFetchPolicy: 'cache-first', + variables: { + id: Number(id) + } + }); + + const seoAttributes = useMemo(() => { + if (!data || !data.cmsPage) { + return null; + } + return data.cmsPage; + }, [data]); + + return { + error, + loading, + seoAttributes + }; +}; + +export default useCmsPageSeo; diff --git a/src/hooks/Category/useCategorySeo.js b/src/hooks/Category/useCategorySeo.js new file mode 100644 index 0000000..1eb4cd2 --- /dev/null +++ b/src/hooks/Category/useCategorySeo.js @@ -0,0 +1,56 @@ +import { useMemo } from "react"; +import { useQuery } from "@apollo/client"; +import gql from "graphql-tag"; + +const GET_CATEGORY_SEO = gql` + query getCategorySeo($id: Int!) { + category(id: $id) { + id + mw_canonical_url { + url + code + } + meta_robots + mw_hreflangs { + items { + url + code + } + } + mw_seo_markup { + social_markup + rich_snippets { + website + seller + } + } + } + } +`; + +const useProductAttachments = (props) => { + const { id } = props; + + const { error, loading, data } = useQuery(GET_CATEGORY_SEO, { + fetchPolicy: "cache-and-network", + nextFetchPolicy: "cache-first", + variables: { + id + } + }); + + const seoAttributes = useMemo(() => { + if (!data || !data.category) { + return null; + } + return data.category; + }, [data]); + + return { + error, + loading, + seoAttributes + }; +}; + +export default useProductAttachments; diff --git a/src/hooks/Product/useProductSeo.js b/src/hooks/Product/useProductSeo.js new file mode 100644 index 0000000..b8a1f7a --- /dev/null +++ b/src/hooks/Product/useProductSeo.js @@ -0,0 +1,72 @@ +import { useMemo } from "react"; +import { useQuery } from "@apollo/client"; +import gql from "graphql-tag"; + +const GET_PRODUCT_SEO = gql` + query getProductSeo($urlKey: String!) { + products(filter: { url_key: { eq: $urlKey } }) { + items { + url_key + mw_canonical_url { + url + code + } + meta_robots + mw_hreflangs { + items { + url + code + } + } + mw_seo_markup { + social_markup + rich_snippets { + website + seller + product + } + } + } + } + } +`; + +const useProductSeo = (props) => { + const { urlKey } = props; + + const { error, loading, data } = useQuery(GET_PRODUCT_SEO, { + fetchPolicy: "cache-and-network", + nextFetchPolicy: "cache-first", + variables: { + urlKey: urlKey + } + }); + + const seoAttributes = useMemo(() => { + if (!data) { + // The product isn't in the cache and we don't have a response from GraphQL yet. + return null; + } + + // Note: if a product is out of stock _and_ the backend specifies not to + // display OOS items, the items array will be empty. + + // Only return the product that we queried for. + const product = data.products.items.find( + item => item.url_key === urlKey + ); + + if (!product) { + return null; + } + return product; + }, [data, urlKey]); + + return { + error, + loading, + seoAttributes + }; +}; + +export default useProductSeo; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..3321a04 --- /dev/null +++ b/src/index.js @@ -0,0 +1,7 @@ +/** + * Custom index for the extension attention this file should be not delete! + * It is use as main file but this can be empty by default only need to exits. + * + * A project index.js should contain default exports like: + * export { default } from './components/main'; + */ diff --git a/src/intercept.js b/src/intercept.js new file mode 100644 index 0000000..47dcee8 --- /dev/null +++ b/src/intercept.js @@ -0,0 +1,36 @@ +/** + * Custom intercept file for the extension + * By default you can only use target of @magento/pwa-buildpack. + */ + +module.exports = targets => { + // For extends productFullDetail component in local-intercept + const { Targetables } = require('@magento/pwa-buildpack'); + const targetables = Targetables.using(targets); + targetables.setSpecialFeatures('esModules','cssModules'); + + const peregrineTargets = targets.of("@magento/peregrine"); + const talonsTarget = peregrineTargets.talons; + + // product + talonsTarget.tap((talonWrapperConfig) => { + talonWrapperConfig.ProductFullDetail.useProductFullDetail.wrapWith( + "@mageworx/seo-veniapwa/src/targets/Product/wrapUseProductFullDetails" + ); + }); + + // category + talonsTarget.tap((talonWrapperConfig) => { + talonWrapperConfig.Cms.useCmsPage.wrapWith( + "@mageworx/seo-veniapwa/src/targets/CMS/wrapUseCmsPage" + ); + }); + + // cms + talonsTarget.tap((talonWrapperConfig) => { + talonWrapperConfig.RootComponents.Category.useCategoryContent.wrapWith( + "@mageworx/seo-veniapwa/src/targets/Category/wrapUseCategoryContent" + ); + }); + +}; diff --git a/src/targets/CMS/wrapUseCmsPage.js b/src/targets/CMS/wrapUseCmsPage.js new file mode 100644 index 0000000..bd4ac0f --- /dev/null +++ b/src/targets/CMS/wrapUseCmsPage.js @@ -0,0 +1,23 @@ +import useCmsPageSeo from "../../hooks/CMS/useCmsPageSeo"; + +const wrapUseCmsPage = (original) => { + return function useCmsPage(props, ...restArgs) { + const { id } = props; + + const seoQueryResult = useCmsPageSeo({ + id + }); + + const { ...defaultReturnData } = original( + props, + ...restArgs + ); + + return { + ...defaultReturnData, + seoAttributes: seoQueryResult.seoAttributes, + }; + }; +}; + +export default wrapUseCmsPage; diff --git a/src/targets/Category/wrapUseCategoryContent.js b/src/targets/Category/wrapUseCategoryContent.js new file mode 100644 index 0000000..7a0f3dd --- /dev/null +++ b/src/targets/Category/wrapUseCategoryContent.js @@ -0,0 +1,23 @@ +import useCategorySeo from "../../hooks/Category/useCategorySeo"; + +const wrapUseCategoryContent = (original) => { + return function useCategoryContent(props, ...restArgs) { + const { categoryId } = props; + + const seoQueryResult = useCategorySeo({ + id: categoryId + }); + + const { ...defaultReturnData } = original( + props, + ...restArgs + ); + + return { + ...defaultReturnData, + seoAttributes: seoQueryResult.seoAttributes, + }; + }; +}; + +export default wrapUseCategoryContent; diff --git a/src/targets/Product/wrapUseProductFullDetails.js b/src/targets/Product/wrapUseProductFullDetails.js new file mode 100644 index 0000000..0c4ac83 --- /dev/null +++ b/src/targets/Product/wrapUseProductFullDetails.js @@ -0,0 +1,26 @@ +import useProductSeo from "../../hooks/Product/useProductSeo"; + +const wrapUseProductFullDetails = (original) => { + return function useProductFullDetails(props, ...restArgs) { + const { product } = props; + + const seoQueryResult = useProductSeo({ + urlKey: product.url_key + }); + + const { productDetails, ...defaultReturnData } = original( + props, + ...restArgs + ); + + return { + ...defaultReturnData, + productDetails: { + ...productDetails, + seoAttributes: seoQueryResult.seoAttributes, + } + }; + }; +}; + +export default wrapUseProductFullDetails; diff --git a/src/urls.congif.js b/src/urls.congif.js new file mode 100644 index 0000000..b92b624 --- /dev/null +++ b/src/urls.congif.js @@ -0,0 +1,14 @@ +/* + EXAMPLE: + const hreflangs_config = [ + {type: "store", code: "default", url: "https://magento-store.com/"}, + {type: "store", code: "toys", url: "https://magento-toys.com/"}, + {type: "lang", code: "de-DE", url: "https://magento-store.com/de/"}, + {type: "lang", code: "en-US", url: "https://magento-store.com/en/"}, + ]; + */ + +const urls_config = []; + +module.exports = urls_config; +