-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ef5b864
commit d25be61
Showing
22 changed files
with
769 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
const config = { | ||
parser: 'babel-eslint', | ||
extends: ['@magento'], | ||
rules: { | ||
'no-undef': 'off', | ||
'no-useless-escape': 'off' | ||
} | ||
}; | ||
|
||
module.exports = config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,82 @@ | ||
# mageworx-seo-veniapwa | ||
# 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( | ||
'<section className={classes.description} />', | ||
`<${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( | ||
'<article className={classes.root} />', | ||
`<${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( | ||
'<CategoryList />', | ||
`<div>` | ||
); | ||
CmsPage_seo.insertAfterJSX( | ||
'<CategoryList />', | ||
`<${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: | ||
``` | ||
<link rel="alternate" hreflang="de-DE" href="https://magento-store.com/de/gear/bags.html" data-rh="true"> | ||
<link rel="canonical" href="https://magento-store.com/gear/bags.html" data-rh="true"> | ||
``` | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"presets": [ | ||
"@babel/preset-react", | ||
"@babel/preset-env" | ||
], | ||
"plugins": [ | ||
"@babel/plugin-transform-react-jsx", | ||
"@babel/plugin-proposal-class-properties" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
const config = { | ||
singleQuote: true, | ||
tabWidth: 4, | ||
trailingComma: 'none' | ||
}; | ||
|
||
module.exports = config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = <link rel="canonical" href={canonical.url} />; | ||
} | ||
else if (canonical.code) { | ||
let elem_from_config = findCodeFromConfig(canonical.code, "store", true); | ||
if (elem_from_config) { | ||
canonical_result = <link rel="canonical" href={elem_from_config + canonical.url} />; | ||
} | ||
else return null; | ||
} | ||
else { | ||
canonical_result = <link rel="canonical" href={getCurrentHostname(true) + canonical.url} />; | ||
} | ||
return ( | ||
<Helmet> | ||
{canonical_result} | ||
</Helmet> | ||
); | ||
} | ||
return null; | ||
}; | ||
|
||
export default Canonical; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <link key={item.code + itemNum} rel="alternate" hreflang={item.code} href={url} />; | ||
} | ||
else return null; | ||
}) | ||
return ( | ||
<Helmet> | ||
{hreflangs_result} | ||
</Helmet> | ||
); | ||
return null; | ||
}; | ||
|
||
export default Hreflangs; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(/<meta.+?\/>/gi); | ||
} | ||
|
||
const getScriptsArrayFromStr = (str) => { | ||
return str.match(/<script.+?>.+?<\/script>/gi); | ||
} | ||
|
||
const getContentAndAttributesOfMeta = (str) => { | ||
const metaData = { | ||
content: "", | ||
property: "", | ||
} | ||
let content = str.match(/<meta.+?content=\\?"(.+?)\\?".*\/>/i); | ||
let property = str.match(/<meta.+?property=\\?"(.+?)\\?".*\/>/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.+?>(.+?)<\/script>/i); | ||
let type = str.match(/<script.+?type\=\\?"(.+?)\\?".*?>.+?<\/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 <meta key={num} property={property} content={content} /> | ||
}); | ||
return result; | ||
} | ||
|
||
const getScriptsResultOfJsxFromStr = (str) => { | ||
let scriptsArray = getScriptsArrayFromStr(str); | ||
let result = scriptsArray.map((script, num) => { | ||
let attributes = getContentAndAttributesOfScript(script); | ||
const {type, content} = attributes; | ||
return <script key={num} type={type}>{content}</script> | ||
}); | ||
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 ( | ||
<Helmet> | ||
{markup_result} | ||
</Helmet> | ||
) | ||
}; | ||
|
||
export default Markup; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 && <meta name="robots" content={meta_robots} />; | ||
canonical = <Canonical canonical={mw_canonical_url} /> | ||
hreflangs = <Hreflangs mw_hreflangs={mw_hreflangs} />; | ||
markup = <Markup markup={mw_seo_markup} /> | ||
} | ||
|
||
return ( | ||
<> | ||
<Helmet> | ||
{meta_robots_JSX} | ||
</Helmet> | ||
{hreflangs} | ||
{markup} | ||
{canonical} | ||
</> | ||
) | ||
}; | ||
export default Seo; |
Oops, something went wrong.