feat: optimize head tags (#40630)
Co-authored-by: moT01 <20648924+moT01@users.noreply.github.com>
This commit is contained in:
committed by
Mrugesh Mohapatra
parent
625469c82f
commit
3ec468be38
@ -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));
|
||||
};
|
||||
|
@ -2,6 +2,7 @@
|
||||
"meta": {
|
||||
"title": "免费学习编程 – 适合忙碌人士的编程课程",
|
||||
"description": "在家学习编程,构建项目,获得认证。自 2014 年以来,已有超过 40,000 名 freeCodeCamp 学员入职谷歌、苹果、亚马逊、微软等科技公司。",
|
||||
"social-description": "Learn to Code — For Free",
|
||||
"keywords": [
|
||||
"javascript",
|
||||
"js",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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',
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -1,3 +0,0 @@
|
||||
const styleSheets = [];
|
||||
|
||||
export default styleSheets;
|
@ -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
85
client/utils/tags.js
Normal 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
115
cypress/integration/tags.js
Normal 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
|
||||
);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user