feat: optimize head tags (#40630)

Co-authored-by: moT01 <20648924+moT01@users.noreply.github.com>
This commit is contained in:
Ahmad Abdolsaheb
2021-01-13 16:12:32 +03:00
committed by Mrugesh Mohapatra
parent 625469c82f
commit 3ec468be38
12 changed files with 210 additions and 133 deletions

View File

@ -4,10 +4,9 @@ import { Provider } from 'react-redux';
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n/config';
import headComponents from './src/head';
import { createStore } from './src/redux/createStore';
import layoutSelector from './utils/gatsby/layoutSelector';
import { getheadTagComponents, getPostBodyComponents } from './utils/tags';
const store = createStore();
@ -23,26 +22,6 @@ wrapRootElement.propTypes = {
element: PropTypes.any
};
// TODO: put these in a common utils file.
const mathJaxCdn = {
address:
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/' +
'2.7.4/MathJax.js?config=TeX-AMS_HTML',
id: 'mathjax',
key: 'mathjax',
type: 'text/javascript'
};
const stripeScript = {
address: 'https://js.stripe.com/v3/',
id: 'stripe-js',
key: 'stripe-js',
type: 'text/javascript'
};
const challengeRE = new RegExp('/learn/[^/]+/[^/]+/[^/]+/?$');
const donateRE = new RegExp('/donate/?$');
export const wrapPageElement = layoutSelector;
export const onRenderBody = ({
@ -50,52 +29,6 @@ export const onRenderBody = ({
setHeadComponents,
setPostBodyComponents
}) => {
setHeadComponents([...headComponents]);
const scripts = [
<script
async={true}
key='gtag-script'
src='https://www.googletagmanager.com/gtag/js?id=AW-795617839'
/>,
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'AW-795617839');
`
}}
key='gtag-dataLayer'
/>
];
if (
pathname.includes('/learn/coding-interview-prep/rosetta-code') ||
pathname.includes('/learn/coding-interview-prep/project-euler')
) {
scripts.push(
<script
async={false}
id={mathJaxCdn.id}
key={mathJaxCdn.key}
src={mathJaxCdn.address}
type={mathJaxCdn.type}
/>
);
}
if (challengeRE.test(pathname) || donateRE.test(pathname)) {
scripts.push(
<script
async={true}
id={stripeScript.id}
key={stripeScript.key}
src={stripeScript.address}
type={stripeScript.type}
/>
);
}
setPostBodyComponents(scripts.filter(Boolean));
setHeadComponents(getheadTagComponents());
setPostBodyComponents(getPostBodyComponents(pathname));
};

View File

@ -2,6 +2,7 @@
"meta": {
"title": "免费学习编程 适合忙碌人士的编程课程",
"description": "在家学习编程,构建项目,获得认证。自 2014 年以来,已有超过 40,000 名 freeCodeCamp 学员入职谷歌、苹果、亚马逊、微软等科技公司。",
"social-description": "Learn to Code — For Free",
"keywords": [
"javascript",
"js",

View File

@ -1,7 +1,8 @@
{
"meta": {
"title": "Learn to Code — For Free — Coding Courses for Busy People",
"description": "Learn to code — for free.",
"description": "Learn to Code — For Free",
"social-description": "Learn to Code — For Free",
"keywords": [
"javascript",
"js",

View File

@ -2,6 +2,7 @@
"meta": {
"title": "Aprende a codificar gratis: cursos de programación para personas ocupadas",
"description": "Aprende a codificar en casa. Construye proyectos. Obtén certificaciones. Desde el 2014, más de 40,000 graduados de freeCodeCamp.org han conseguido trabajos en empresas de tecnología como Google, Apple, Amazon y Microsoft.",
"social-description": "Learn to Code — For Free",
"keywords": [
"javascript",
"js",

View File

@ -4,9 +4,9 @@
*/
const translationsSchema = {
meta: {
title: 'Learn to Code for Free Coding Courses for Busy People',
description:
'Learn to code at home. Build projects. Earn certifications. Since 2014, more than 40,000 freeCodeCamp.org graduates have gotten jobs at tech companies including Google, Apple, Amazon, and Microsoft.',
title: 'Learn to Code — For Free Coding Courses for Busy People',
description: 'Learn to Code — For Free',
'social-description': 'Learn to Code — For Free',
keywords: [
'javascript',
'js',

View File

@ -1,9 +0,0 @@
import meta from './meta';
const metaAndStyleSheets = meta.map((element, i) => ({
...element,
key: `meta-stylesheet-${i}`,
props: { ...element.props, key: `meta-stylesheet-${i}` }
}));
export default metaAndStyleSheets;

View File

@ -1,34 +0,0 @@
import React from 'react';
const meta = [
<meta content='freeCodeCamp.org' name='og:title' />,
<meta
content={
'Learn to code. Build projects. Earn certifications.' +
'Since 2015, 40,000 graduates have gotten jobs at tech ' +
'companies including Google, Apple, Amazon, and Microsoft.'
}
name='og:description'
/>,
<meta
content='https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920X1080-indigo.png'
property='og:image'
/>,
<meta content='summary_large_image' key='twitter:card' name='twitter:card' />,
<meta
content='https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920X1080-indigo.png'
name='twitter:image:src'
/>,
<meta content='freeCodeCamp.org' name='twitter:title' />,
<meta
content={
'Learn to code. Build projects. Earn certifications.' +
'Since 2015, 40,000 graduates have gotten jobs at tech ' +
'companies including Google, Apple, Amazon, and Microsoft.'
}
name='twitter:description'
/>,
<meta content='$ilp.uphold.com/LJmbPn7WD4JB' name='monetization' />
];
export default meta;

View File

@ -1,11 +0,0 @@
import React from 'react';
import styleSheets from './styleSheets';
const preloads = styleSheets.map(styleSheet => (
<React.Fragment key={`preload-${styleSheet.props.href}`}>
<link as='style' href={styleSheet.props.href} rel='preload' />
{styleSheet}
</React.Fragment>
));
export default preloads;

View File

@ -1,3 +0,0 @@
const styleSheets = [];
export default styleSheets;

View File

@ -12,8 +12,6 @@ export default class HTML extends React.Component {
content='width=device-width, initial-scale=1.0, shrink-to-fit=no'
name='viewport'
/>
<link as='style' href='/css/bootstrap.min.css' rel='preload' />
<link href='/css/bootstrap.min.css' rel='stylesheet' />
{this.props.headComponents}
</head>
<body {...this.props.bodyAttributes}>

85
client/utils/tags.js Normal file
View File

@ -0,0 +1,85 @@
import React from 'react';
import { withPrefix } from 'gatsby';
import i18next from 'i18next';
export const getheadTagComponents = () => {
const socialImage =
'https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920X1080-indigo.png';
const pathToBootstrap = withPrefix('/css/bootstrap.min.css');
return [
<link
as='style'
href={pathToBootstrap}
key='boostrap-min-preload'
rel='preload'
/>,
<link href={pathToBootstrap} key='boostrap-min' rel='stylesheet' />,
<meta content='freeCodeCamp.org' key='og:title' name='og:title' />,
<meta
content={i18next.t('meta.social-description')}
key='og:description'
name='og:description'
/>,
<meta content={socialImage} key='og:image' property='og:image' />,
<meta
content='summary_large_image'
key='twitter:card'
name='twitter:card'
/>,
<meta
content={socialImage}
key='twitter:image:src'
name='twitter:image:src'
/>,
<meta
content='freeCodeCamp.org'
key='twitter:title'
name='twitter:title'
/>,
<meta
content={i18next.t('meta.social-description')}
key='twitter:description'
name='twitter:description'
/>,
<meta
content='$ilp.uphold.com/LJmbPn7WD4JB'
key='monetization'
name='monetization'
/>
];
};
export const getPostBodyComponents = pathname => {
let scripts = [];
const challengesPathRE = new RegExp('/learn/[^/]+/[^/]+/[^/]+/?$');
const donatePathRE = new RegExp('/donate/?$');
const mathJaxScriptElement = (
<script
async={false}
id='mathjax'
key='mathjax'
src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-AMS_HTML'
type='text/javascript'
/>
);
const stripeScriptElement = (
<script
async={true}
id='stripe-js'
key='stripe-js'
src='https://js.stripe.com/v3/'
type='text/javascript'
/>
);
if (
pathname.includes('/learn/coding-interview-prep/rosetta-code') ||
pathname.includes('/learn/coding-interview-prep/project-euler')
) {
scripts.push(mathJaxScriptElement);
}
if (challengesPathRE.test(pathname) || donatePathRE.test(pathname)) {
scripts.push(stripeScriptElement);
}
return scripts.filter(Boolean);
};

115
cypress/integration/tags.js Normal file
View File

@ -0,0 +1,115 @@
/* global cy */
describe('The Document Metadata', () => {
before(() => {
cy.visit('/');
cy.document();
});
const social = {
description: 'Learn to Code — For Free'
};
const challengs = {
responsiveWebDesign:
'/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements',
rosetaCode: '/learn/coding-interview-prep/rosetta-code/100-doors',
projectEuler:
'/learn/coding-interview-prep/project-euler/problem-1-multiples-of-3-and-5'
};
const scripts = {
stripe: {
selector: 'body script[id="stripe-js"]',
src: 'https://js.stripe.com/v3/'
},
mathjax: {
selector: 'body script[id="mathjax"]',
src:
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-AMS_HTML'
}
};
it('landing page has correct <meta> for description', () => {
cy.get('head meta[name="description"]').should(
'have.attr',
'content',
'Learn to Code — For Free'
);
});
it('landing page has correct <meta> for og title', () => {
cy.get('head meta[name="og:title"]').should(
'have.attr',
'content',
'freeCodeCamp.org'
);
});
it('landing page has correct <meta> for og description', () => {
cy.get('head meta[name="og:description"]').should(
'have.attr',
'content',
social.description
);
});
it('landing page has correct <meta> for twitter title', () => {
cy.get('head meta[name="twitter:title"]').should(
'have.attr',
'content',
'freeCodeCamp.org'
);
});
it('landing page has correct <meta>for twitter description', () => {
cy.get('head meta[name="twitter:description"]').should(
'have.attr',
'content',
social.description
);
});
it('landing page should not have stripe body script', () => {
cy.reload();
cy.get(scripts.stripe.selector).should('not.exist');
});
it('landing page should not have mathjax body script', () => {
cy.reload();
cy.get(scripts.mathjax.selector).should('not.exist');
});
it('responsive webdesign challenges should not have mathjax body script', () => {
cy.visit(challengs.responsiveWebDesign);
cy.reload();
cy.get(scripts.mathjax.selector).should('not.exist');
});
it('donate page should have stripe body script', () => {
cy.visit('/donate');
cy.reload();
cy.get(scripts.stripe.selector).should(
'have.attr',
'src',
scripts.stripe.src
);
});
it('responsive webdesign challenges should have stripe body script', () => {
cy.visit(challengs.responsiveWebDesign);
cy.reload();
cy.get(scripts.stripe.selector).should(
'have.attr',
'src',
scripts.stripe.src
);
});
it('project euler challenges should have mathjax body script', () => {
cy.visit(challengs.projectEuler);
cy.reload();
cy.get(scripts.mathjax.selector).should(
'have.attr',
'src',
scripts.mathjax.src
);
});
it('rosetta code challenges should have mathjax body script', () => {
cy.visit(challengs.projectEuler);
cy.reload();
cy.get(scripts.mathjax.selector).should(
'have.attr',
'src',
scripts.mathjax.src
);
});
});