This commit is contained in:
Sergey Yarkov 2020-09-26 14:17:56 +03:00
parent f0b960eb22
commit 5778d12166
20 changed files with 193 additions and 136 deletions

View File

@ -25,14 +25,14 @@ tags:
## Содержание ## Содержание
* [Описание списка зависимостей](#Описание-списка-зависимостей) - [Описание списка зависимостей](#Описание-списка-зависимостей)
* [Структура проекта](#Структура-проекта) - [Структура проекта](#Структура-проекта)
* [Предварительная настройка](#Предварительная-настройка) - [Предварительная настройка](#Предварительная-настройка)
* [Создание Type Definitions](#Создание-Type-Definitions) - [Создание Type Definitions](#Создание-Type-Definitions)
* [Создание Resolvers](#Создание-Resolvers) - [Создание Resolvers](#Создание-Resolvers)
* [Создание схемы](#Создание-схемы) - [Создание схемы](#Создание-схемы)
* [Использование Apollo Server Express](#Использование-Apollo-Server-Express) - [Использование Apollo Server Express](#Использование-Apollo-Server-Express)
* [Заключение](#Заключение) - [Заключение](#Заключение)
💡 Данное руководство расчитано на тех разработчиков, кто уже имел небольшой опыт с использованием технологий **Apollo Server** и **TypeScript**. 💡 Данное руководство расчитано на тех разработчиков, кто уже имел небольшой опыт с использованием технологий **Apollo Server** и **TypeScript**.
@ -56,9 +56,11 @@ npm install @types/graphql-depth-limit apollo-server-express cors dotenv express
// инициализировать файл с настройками typescript // инициализировать файл с настройками typescript
npx tsc --init npx tsc --init
``` ```
Теперь по порядку узнаем для чего нужны эти пакеты: Теперь по порядку узнаем для чего нужны эти пакеты:
**devDependencies:** **devDependencies:**
1. **@typesnode** - устанавливаем type definitions node.js для нашего typescript **~717 kB** 1. **@typesnode** - устанавливаем type definitions node.js для нашего typescript **~717 kB**
2. **prettier** - этот пакет нужен для форматирования вашего кода, вы можете установить его <ins>по желанию</ins> **~9.08 MB** 2. **prettier** - этот пакет нужен для форматирования вашего кода, вы можете установить его <ins>по желанию</ins> **~9.08 MB**
3. **ts-node** - данные пакет предназначен для транспиляции typescript кода в javascript **~377 kB** 3. **ts-node** - данные пакет предназначен для транспиляции typescript кода в javascript **~377 kB**
@ -66,6 +68,7 @@ npx tsc --init
5. **typescript** - сам typescript **~56.8 MB** 5. **typescript** - сам typescript **~56.8 MB**
**dependencies:** **dependencies:**
1. **apollo-server-express** - GraphQL сервер вместе в веб-фреймворком Express.js **~70.1 kB** 1. **apollo-server-express** - GraphQL сервер вместе в веб-фреймворком Express.js **~70.1 kB**
2. **express** - веб-фреймворк Express.js **~208 kB** 2. **express** - веб-фреймворк Express.js **~208 kB**
3. **graphql** - среда выполнения запросов к нашей API **~1.92 MB** 3. **graphql** - среда выполнения запросов к нашей API **~1.92 MB**
@ -78,6 +81,7 @@ npx tsc --init
## Структура проекта ## Структура проекта
Теперь после того как все пакеты установлены, определимся со структурой проекта: Теперь после того как все пакеты установлены, определимся со структурой проекта:
```text ```text
. .
└── typescript-apollo-server └── typescript-apollo-server
@ -103,13 +107,16 @@ npx tsc --init
## Предварительная настройка ## Предварительная настройка
Перед тем как начать разрабатывать наш сервер, зайдем в **package.json** и установим скрипты запуска: Перед тем как начать разрабатывать наш сервер, зайдем в **package.json** и установим скрипты запуска:
```text ```text
"scripts": { "scripts": {
"start": "ts-node index.ts", "start": "ts-node index.ts",
"server": "ts-node-dev --respawn index.ts" "server": "ts-node-dev --respawn index.ts"
}, },
``` ```
Теперь необходимо создать схему, создаем папку **graphql** и в ней также создаем еще две папки **resolvers** и **typeDefs**, также не забываем создать файл **schema.ts**: Теперь необходимо создать схему, создаем папку **graphql** и в ней также создаем еще две папки **resolvers** и **typeDefs**, также не забываем создать файл **schema.ts**:
```bash ```bash
mkdir graphql mkdir graphql
cd ./graphql cd ./graphql
@ -134,6 +141,7 @@ touch Book.graphql
Далее напишем наши типы. Обратите внимание что для удобства и разделения кода мы будем использовать импорты. Возможность использовать импорты нам предоставляет пакет **graphql-tools**. Для того чтобы импортировать файл **.graphql** ставится знак **#** и указывается путь. Далее напишем наши типы. Обратите внимание что для удобства и разделения кода мы будем использовать импорты. Возможность использовать импорты нам предоставляет пакет **graphql-tools**. Для того чтобы импортировать файл **.graphql** ставится знак **#** и указывается путь.
Главный файл **schema.graphql**: Главный файл **schema.graphql**:
```graphql ```graphql
# ./graphql/typeDefs/schema.graphql # ./graphql/typeDefs/schema.graphql
@ -147,6 +155,7 @@ type Query {
``` ```
Файл **Book.graphql**: Файл **Book.graphql**:
```graphql ```graphql
# ./graphql/typeDefs/Book.graphql # ./graphql/typeDefs/Book.graphql
@ -157,6 +166,7 @@ type Book {
``` ```
Файл **User.graphql**: Файл **User.graphql**:
```graphql ```graphql
# ./graphql/typeDefs/User.graphql # ./graphql/typeDefs/User.graphql
@ -191,10 +201,11 @@ cd ..
Наконец прописываем обработчики: Наконец прописываем обработчики:
Файл **Books.ts**: Файл **Books.ts**:
```js ```js
// ./graphql/resolvers/Books/Books.ts // ./graphql/resolvers/Books/Books.ts
import { IResolvers } from 'graphql-tools'; import { IResolvers } from "graphql-tools"
interface BookType { interface BookType {
id: string; id: string;
@ -202,17 +213,17 @@ interface BookType {
} }
const books = [ const books = [
{ id: '1', title: 'First Book' }, { id: "1", title: "First Book" },
{ id: '2', title: 'Second Book' }, { id: "2", title: "Second Book" },
]; ]
const booksResolver: IResolvers = { const booksResolver: IResolvers = {
Query: { Query: {
books: (): BookType[] => books, books: (): BookType[] => books,
}, },
}; }
export default booksResolver; export default booksResolver
``` ```
Файл **Users.ts**: Файл **Users.ts**:
@ -220,7 +231,7 @@ export default booksResolver;
```js ```js
// ./graphql/resolvers/Users/Users.ts // ./graphql/resolvers/Users/Users.ts
import { IResolvers } from 'graphql-tools'; import { IResolvers } from "graphql-tools"
interface UserType { interface UserType {
id: string; id: string;
@ -228,17 +239,17 @@ interface UserType {
} }
const users = [ const users = [
{ id: '1', name: 'John' }, { id: "1", name: "John" },
{ id: '2', name: 'Ivan' }, { id: "2", name: "Ivan" },
]; ]
const usersResolver: IResolvers = { const usersResolver: IResolvers = {
Query: { Query: {
users: (): UserType[] => users, users: (): UserType[] => users,
}, },
}; }
export default usersResolver; export default usersResolver
``` ```
Резолверы получились довольно простые и понятные, теперь нужно собрать их в один массив для дальнейшего экспорта. В файле **index.ts** прописываем следующее: Резолверы получились довольно простые и понятные, теперь нужно собрать их в один массив для дальнейшего экспорта. В файле **index.ts** прописываем следующее:
@ -246,17 +257,18 @@ export default usersResolver;
```js ```js
// ./graphql/resolvers/index.ts // ./graphql/resolvers/index.ts
import { IResolvers } from 'graphql-tools'; import { IResolvers } from "graphql-tools"
import booksResolver from './Books/Books'; import booksResolver from "./Books/Books"
import usersResolver from './Users/Users' import usersResolver from "./Users/Users"
const resolvers: IResolvers[] = [usersResolver, booksResolver]; const resolvers: IResolvers[] = [usersResolver, booksResolver]
export default resolvers; export default resolvers
``` ```
## Создание схемы ## Создание схемы
Как говорилось ранее, пакет **graphql-tools** предназначен для создания схемы. Теперь перейдем в наш файл **schema.ts** и соберем нашу схему. Как говорилось ранее, пакет **graphql-tools** предназначен для создания схемы. Теперь перейдем в наш файл **schema.ts** и соберем нашу схему.
Раньше для того чтобы работали наши graphql импорты мы применяли пакет **graphql-import**, но теперь его не нужно устанавливать так как он уже входит в состав **graphql-tools**. Подробнее об этом можно прочитать [здесь](https://www.graphql-tools.com/docs/migration-from-import). Раньше для того чтобы работали наши graphql импорты мы применяли пакет **graphql-import**, но теперь его не нужно устанавливать так как он уже входит в состав **graphql-tools**. Подробнее об этом можно прочитать [здесь](https://www.graphql-tools.com/docs/migration-from-import).
@ -265,20 +277,26 @@ export default resolvers;
```js ```js
// ./graphql/schema.ts // ./graphql/schema.ts
import { mergeResolvers, loadSchemaSync, loadFilesSync, GraphQLFileLoader, addResolversToSchema } from 'graphql-tools'; import {
import { GraphQLSchema } from 'graphql'; mergeResolvers,
loadSchemaSync,
loadFilesSync,
GraphQLFileLoader,
addResolversToSchema,
} from "graphql-tools"
import { GraphQLSchema } from "graphql"
const schema = loadSchemaSync(`${__dirname}/typeDefs/schema.graphql`, { const schema = loadSchemaSync(`${__dirname}/typeDefs/schema.graphql`, {
loaders: [new GraphQLFileLoader()], loaders: [new GraphQLFileLoader()],
}); })
const resolvers = loadFilesSync(`${__dirname}/resolvers`); const resolvers = loadFilesSync(`${__dirname}/resolvers`)
const schemaWithResolvers: GraphQLSchema = addResolversToSchema({ const schemaWithResolvers: GraphQLSchema = addResolversToSchema({
schema, schema,
resolvers: mergeResolvers(resolvers), resolvers: mergeResolvers(resolvers),
}); })
export default schemaWithResolvers; export default schemaWithResolvers
``` ```
## Использование Apollo Server Express ## Использование Apollo Server Express
@ -294,38 +312,38 @@ touch index.ts
```js ```js
// ./index.ts // ./index.ts
import express, { Application, Request, Response } from 'express'; import express, { Application, Request, Response } from "express"
import cors from 'cors'; import cors from "cors"
import depthLimit from 'graphql-depth-limit'; import depthLimit from "graphql-depth-limit"
import { ApolloServer } from 'apollo-server-express'; import { ApolloServer } from "apollo-server-express"
import schema from './graphql/schema'; import schema from "./graphql/schema"
const PORT = process.env.PORT || 4000; const PORT = process.env.PORT || 4000
const startServer = (): void => { const startServer = (): void => {
try { try {
const app: Application = express(); const app: Application = express()
const server: ApolloServer = new ApolloServer({ const server: ApolloServer = new ApolloServer({
schema, schema,
validationRules: [depthLimit(10)], // выставляем правило deph limit = 10 validationRules: [depthLimit(10)], // выставляем правило deph limit = 10
playground: true, playground: true,
}); })
app.use('*', cors()); app.use("*", cors())
app.get('/', (req: Request, res: Response) => res.send('GraphQL API')); app.get("/", (req: Request, res: Response) => res.send("GraphQL API"))
server.applyMiddleware({ app }); server.applyMiddleware({ app })
app.listen({ port: PORT }, () => app.listen({ port: PORT }, () =>
console.log(`🚀 Server ready on port: ${PORT}`) console.log(`🚀 Server ready on port: ${PORT}`)
); )
} catch (err) { } catch (err) {
console.log(`❌ Something went wrong: \n ${err}`); console.log(`❌ Something went wrong: \n ${err}`)
} }
}; }
startServer(); startServer()
``` ```
Как видим, ничего сложного нет, просто применяем функцию [applyMiddleware](https://www.apollographql.com/docs/apollo-server/api/apollo-server/#applymiddleware) для того чтобы соедениеть Apollo Server с фрэймворком Express.js. Далее можно протестировать наш сервер, запускаем команду **npm run start** и переходим по адресу **/graphql**. Для тестирования можно выполнить вот такой запрос: Как видим, ничего сложного нет, просто применяем функцию [applyMiddleware](https://www.apollographql.com/docs/apollo-server/api/apollo-server/#applymiddleware) для того чтобы соедениеть Apollo Server с фрэймворком Express.js. Далее можно протестировать наш сервер, запускаем команду **npm run start** и переходим по адресу **/graphql**. Для тестирования можно выполнить вот такой запрос:
@ -372,7 +390,6 @@ startServer();
} }
``` ```
## Заключение ## Заключение
Как видим, использование Apollo Server вместе с TypeScript оказалась совсем не сложной задачей, так как и Apollo Server и пакет graphql-import имеют поддержку typescript из коробки. Как видим, использование Apollo Server вместе с TypeScript оказалась совсем не сложной задачей, так как и Apollo Server и пакет graphql-import имеют поддержку typescript из коробки.

View File

@ -3,7 +3,7 @@ import React from "react"
import Layout from "./src/components/Layout/Layout" import Layout from "./src/components/Layout/Layout"
import "prismjs/themes/prism-tomorrow.css" import "prismjs/themes/prism-tomorrow.css"
import 'prismjs/plugins/line-numbers/prism-line-numbers.css' import "prismjs/plugins/line-numbers/prism-line-numbers.css"
import "./src/styles/global.scss" import "./src/styles/global.scss"
import "./src/components/Header/Header.scss" import "./src/components/Header/Header.scss"
@ -30,8 +30,12 @@ export const shouldUpdateScroll = ({ routerProps: { location } }) => {
} }
export const wrapPageElement = ({ element, props }) => { export const wrapPageElement = ({ element, props }) => {
const { location: { pathname } } = props const {
pathname.includes('/amp/article/') && props.pageContext.slug ? props.amp = true : props.amp = false location: { pathname },
} = props
pathname.includes("/amp/article/") && props.pageContext.slug
? (props.amp = true)
: (props.amp = false)
return <Layout {...props}>{element}</Layout> return <Layout {...props}>{element}</Layout>
} }

View File

@ -161,12 +161,12 @@ module.exports = {
{ {
resolve: `gatsby-plugin-amp`, resolve: `gatsby-plugin-amp`,
options: { options: {
canonicalBaseUrl: 'https://devthread.ru/', canonicalBaseUrl: "https://devthread.ru/",
components: ['amp-sidebar', 'amp-accordion', 'amp-form'], components: ["amp-sidebar", "amp-accordion", "amp-form"],
excludedPaths: ['/404*', '/'], excludedPaths: ["/404*", "/"],
pathIdentifier: '/amp/', pathIdentifier: "/amp/",
relAmpHtmlPattern: '{{canonicalBaseUrl}}{{pathIdentifier}}{{pathname}}', relAmpHtmlPattern: "{{canonicalBaseUrl}}{{pathIdentifier}}{{pathname}}",
relCanonicalPattern: '{{canonicalBaseUrl}}{{pathname}}/', relCanonicalPattern: "{{canonicalBaseUrl}}{{pathname}}/",
useAmpClientIdApi: false, useAmpClientIdApi: false,
}, },
}, },

View File

@ -40,7 +40,7 @@ const OnDark = () => {
} }
export const onRenderBody = ({ pathname, setPreBodyComponents }) => { export const onRenderBody = ({ pathname, setPreBodyComponents }) => {
if (pathname.includes('/amp/article/')) { if (pathname.includes("/amp/article/")) {
return return
} }
@ -48,8 +48,12 @@ export const onRenderBody = ({ pathname, setPreBodyComponents }) => {
} }
export const wrapPageElement = ({ element, props }) => { export const wrapPageElement = ({ element, props }) => {
const { location: { pathname } } = props const {
pathname.includes('/amp/article/') && props.pageContext.slug ? props.amp = true : props.amp = false location: { pathname },
} = props
pathname.includes("/amp/article/") && props.pageContext.slug
? (props.amp = true)
: (props.amp = false)
return <Layout {...props}>{element}</Layout> return <Layout {...props}>{element}</Layout>
} }

View File

@ -18,7 +18,7 @@ import FacebookIcon from "../../images/facebook-icon__large.svg"
const Article = ({ const Article = ({
data: { slug, title, image, description, category, tags, time, date }, data: { slug, title, image, description, category, tags, time, date },
html, html,
amp amp,
}) => { }) => {
const { const {
siteMetadata: { siteMetadata: {
@ -40,10 +40,12 @@ const Article = ({
<div className="article-info__time"> <div className="article-info__time">
<span>~ {time} мин</span> <span>~ {time} мин</span>
</div> </div>
{!amp ? <div className="article-info__views"> {!amp ? (
<div className="article-info__views">
<ViewsIcon /> <ViewsIcon />
<ViewCounter id={pathname} /> <ViewCounter id={pathname} />
</div> : null} </div>
) : null}
</div> </div>
<div className="right-info"> <div className="right-info">
<div className="article-info__date"> <div className="article-info__date">
@ -93,19 +95,20 @@ const Article = ({
))} ))}
</div> </div>
<div className="article-image"> <div className="article-image">
{!amp {!amp ? (
? <Img <Img
loading="eager" loading="eager"
fadeIn={false} fadeIn={false}
fluid={image.childImageSharp.fluid} fluid={image.childImageSharp.fluid}
alt={title} alt={title}
/> />
: <img ) : (
<img
src={image.childImageSharp.fluid.src} src={image.childImageSharp.fluid.src}
height='350' height="350"
alt={title} alt={title}
/> />
} )}
</div> </div>
<div className="article-inner inner"> <div className="article-inner inner">
<div dangerouslySetInnerHTML={{ __html: html }}></div> <div dangerouslySetInnerHTML={{ __html: html }}></div>

View File

@ -6,21 +6,26 @@ import { Link } from "gatsby"
const ArticleCard = ({ const ArticleCard = ({
data: { slug, title, image, category, description, tags }, data: { slug, title, image, category, description, tags },
amp amp,
}) => { }) => {
return ( return (
<article className="article-card"> <article className="article-card">
<div className="article-card__image"> <div className="article-card__image">
<Link to={`/article/${slug}/`}> <Link to={`/article/${slug}/`}>
{!amp {!amp ? (
? <Img <Img
loading="eager" loading="eager"
fadeIn={false} fadeIn={false}
fluid={image.childImageSharp.fluid} fluid={image.childImageSharp.fluid}
alt={title} alt={title}
/> />
: <img src={image.childImageSharp.fluid.src} height='380' alt={title} /> ) : (
} <img
src={image.childImageSharp.fluid.src}
height="380"
alt={title}
/>
)}
</Link> </Link>
</div> </div>
<div className="article-card__heading"> <div className="article-card__heading">

View File

@ -90,4 +90,3 @@ export default Footer
Footer.propTypes = { Footer.propTypes = {
amp: PropTypes.bool, amp: PropTypes.bool,
} }

View File

@ -2,10 +2,9 @@ import React from "react"
import Header from "../Header/Header" import Header from "../Header/Header"
import Footer from "../Footer/Footer" import Footer from "../Footer/Footer"
import { HeaderAMP } from '../../lib/amp' import { HeaderAMP } from "../../lib/amp"
const Layout = ({ children, amp }) => { const Layout = ({ children, amp }) => {
/* /*
amp is accelerated mobile pages amp is accelerated mobile pages
*/ */

View File

@ -3,17 +3,21 @@ import React from "react"
const Newsletter = () => { const Newsletter = () => {
const submitHandler = e => { const submitHandler = e => {
e.preventDefault() e.preventDefault()
} }
return ( return (
<div className="newsletter"> <div className="newsletter">
<div className="newsletter-heading"><span role="img" aria-label="hot">📫</span>{" "}Новостная рассылка</div> <div className="newsletter-heading">
<span role="img" aria-label="hot">
📫
</span>{" "}
Новостная рассылка
</div>
<div className="newsletter-content"> <div className="newsletter-content">
<p> <p>
Подпишитесь на новостную рассылку чтобы не пропустить вышедшие статьи Подпишитесь на новостную рассылку чтобы не пропустить вышедшие статьи
</p> </p>
<form onSubmit={submitHandler} action="#" target='_blank'> <form onSubmit={submitHandler} action="#" target="_blank">
<div className="email-field"> <div className="email-field">
<input placeholder="ваш e-mail" type="email" name="email" /> <input placeholder="ваш e-mail" type="email" name="email" />
<button>Подписаться</button> <button>Подписаться</button>

View File

@ -13,7 +13,7 @@ const SEO = ({
type, type,
canonical, canonical,
url, url,
amp amp,
}) => { }) => {
const { pathname } = useLocation() const { pathname } = useLocation()
const { siteMetadata } = useSiteMetadataQuery() const { siteMetadata } = useSiteMetadataQuery()
@ -71,7 +71,8 @@ const SEO = ({
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#191919" /> <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#191919" />
<meta name="msapplication-TileColor" content="#ffffff" /> <meta name="msapplication-TileColor" content="#ffffff" />
<meta name="theme-color" content="#ffffff" /> <meta name="theme-color" content="#ffffff" />
{!amp ? <script type="application/ld+json"> {!amp ? (
<script type="application/ld+json">
{JSON.stringify({ {JSON.stringify({
"@context": "http://schema.org", "@context": "http://schema.org",
"@type": "Organization", "@type": "Organization",
@ -79,7 +80,8 @@ const SEO = ({
url: siteMetadata.siteUrl, url: siteMetadata.siteUrl,
sameAs: [siteMetadata.social.twitter], sameAs: [siteMetadata.social.twitter],
})} })}
</script> : null} </script>
) : null}
</Helmet> </Helmet>
) )
} }

View File

@ -17,7 +17,7 @@ const useMoreArticlesQuery = () => {
title title
image { image {
childImageSharp { childImageSharp {
fluid(maxWidth: 650 quality: 100) { fluid(maxWidth: 650, quality: 100) {
...GatsbyImageSharpFluid_withWebp ...GatsbyImageSharpFluid_withWebp
} }
} }

View File

@ -1,8 +1,8 @@
import React from 'react'; import React from "react"
import { Helmet } from 'react-helmet' import { Helmet } from "react-helmet"
import useSiteMetadataQuery from '../hooks/useSiteMetadataQuery' import useSiteMetadataQuery from "../hooks/useSiteMetadataQuery"
import LogoIcon from '../images/logo-icon.svg' import LogoIcon from "../images/logo-icon.svg"
import TelegramIcon from "../images/telegram-icon.svg" import TelegramIcon from "../images/telegram-icon.svg"
import TwitterIcon from "../images/twitter-icon.svg" import TwitterIcon from "../images/twitter-icon.svg"
import YadzenIcon from "../images/yadzen-icon.svg" import YadzenIcon from "../images/yadzen-icon.svg"
@ -25,7 +25,7 @@ const HeaderAMP = () => {
height: 100%; height: 100%;
padding: 0px 24px; padding: 0px 24px;
padding-top: 25px; padding-top: 25px;
background-color: rgba(7,11,14,.99); background-color: rgba(7, 11, 14, 0.99);
width: 320px; width: 320px;
} }
amp-sidebar nav li { amp-sidebar nav li {
@ -79,9 +79,14 @@ const HeaderAMP = () => {
<div className="header-content amp"> <div className="header-content amp">
<div className="header-content__logo"> <div className="header-content__logo">
<LogoIcon /> <LogoIcon />
<a href='/'>devthread.ru</a> <a href="/">devthread.ru</a>
</div> </div>
<button tabindex="0" on='tap:sidebar-header' aria-label="menu" className="header-content__button mobile"> <button
tabindex="0"
on="tap:sidebar-header"
aria-label="menu"
className="header-content__button mobile"
>
<span /> <span />
<span /> <span />
<span /> <span />
@ -90,7 +95,13 @@ const HeaderAMP = () => {
</header> </header>
<amp-sidebar id="sidebar-header" layout="nodisplay" side="left"> <amp-sidebar id="sidebar-header" layout="nodisplay" side="left">
<nav> <nav>
<div role='button' tabindex="0" on='tap:sidebar-header.toggle' aria-label="menu" className='amp-sidebar__toggleBtn'> <div
role="button"
tabindex="0"
on="tap:sidebar-header.toggle"
aria-label="menu"
className="amp-sidebar__toggleBtn"
>
✕ ✕
</div> </div>
<span>{title}</span> <span>{title}</span>

View File

@ -138,7 +138,7 @@ export const query = graphql`
title title
image { image {
childImageSharp { childImageSharp {
fluid(maxWidth: 650 quality: 100) { fluid(maxWidth: 650, quality: 100) {
...GatsbyImageSharpFluid_withWebp ...GatsbyImageSharpFluid_withWebp
} }
} }

View File

@ -43,7 +43,7 @@ const IndexPage = ({ data: { allMarkdownRemark: edges } }) => {
<Col lg={9} xs={12}> <Col lg={9} xs={12}>
<Articles data={edges} limit={limit} setLimit={setLimit} /> <Articles data={edges} limit={limit} setLimit={setLimit} />
<Snippets> <Snippets>
{snippets.length <= 0 ? null : <h2>Сниппеты</h2> } {snippets.length <= 0 ? null : <h2>Сниппеты</h2>}
{snippets.map(({ node: { frontmatter } }, i) => ( {snippets.map(({ node: { frontmatter } }, i) => (
<SnippetItem <SnippetItem
key={i} key={i}
@ -78,7 +78,7 @@ export const query = graphql`
title title
image { image {
childImageSharp { childImageSharp {
fluid(maxWidth: 650 quality: 100) { fluid(maxWidth: 650, quality: 100) {
...GatsbyImageSharpFluid_withWebp ...GatsbyImageSharpFluid_withWebp
} }
} }

View File

@ -24,7 +24,7 @@ const SnippetsPage = ({
Сниппеты это готовые решения некоторых проблем, которые можно Сниппеты это готовые решения некоторых проблем, которые можно
скопировать и вставить скопировать и вставить
</p> </p>
{snippets.length <= 0 ? <p>Soon...</p> : null } {snippets.length <= 0 ? <p>Soon...</p> : null}
{snippets.map(({ node: { frontmatter } }, i) => ( {snippets.map(({ node: { frontmatter } }, i) => (
<SnippetItem <SnippetItem
key={i} key={i}

View File

@ -1,5 +1,5 @@
import React from "react" import React from "react"
import { parse } from 'node-html-parser' import { parse } from "node-html-parser"
import { Main } from "../ui/ui" import { Main } from "../ui/ui"
import { Grid, Row, Col } from "react-flexbox-grid" import { Grid, Row, Col } from "react-flexbox-grid"
import SEO from "../components/SEO/SEO" import SEO from "../components/SEO/SEO"
@ -16,12 +16,17 @@ const articleTemplateAmp = ({
}, },
}, },
}) => { }) => {
// gatsbyImage to default image // gatsbyImage to default image
const parsedHTML = parse(html, { pre: true }) const parsedHTML = parse(html, { pre: true })
const pictures = [...parsedHTML.querySelectorAll('.gatsby-resp-image-link')] const pictures = [...parsedHTML.querySelectorAll(".gatsby-resp-image-link")]
pictures.forEach(picture => picture.parentNode.parentNode.set_content(`<img src="${picture.getAttribute('href')}" alt="${picture.querySelector('.gatsby-resp-image-image').getAttribute('alt')}" height="350" />`)) pictures.forEach(picture =>
picture.parentNode.parentNode.set_content(
`<img src="${picture.getAttribute("href")}" alt="${picture
.querySelector(".gatsby-resp-image-image")
.getAttribute("alt")}" height="350" />`
)
)
return ( return (
<> <>
@ -40,7 +45,11 @@ const articleTemplateAmp = ({
<Grid fluid style={{ maxWidth: 800 }}> <Grid fluid style={{ maxWidth: 800 }}>
<Row> <Row>
<Col xs={12}> <Col xs={12}>
<Article data={frontmatter} html={parsedHTML.toString().replace(/loading="[^"]*"/, "")} amp={true} /> <Article
data={frontmatter}
html={parsedHTML.toString().replace(/loading="[^"]*"/, "")}
amp={true}
/>
<Newsletter /> <Newsletter />
<MoreArticles amp={true} /> <MoreArticles amp={true} />
</Col> </Col>
@ -61,7 +70,7 @@ export const pageQuery = graphql`
title title
image { image {
childImageSharp { childImageSharp {
fluid(maxWidth: 1400 quality: 100) { fluid(maxWidth: 1400, quality: 100) {
...GatsbyImageSharpFluid_withWebp ...GatsbyImageSharpFluid_withWebp
} }
} }

View File

@ -54,7 +54,7 @@ export const pageQuery = graphql`
title title
image { image {
childImageSharp { childImageSharp {
fluid(maxWidth: 1200 quality: 100) { fluid(maxWidth: 1200, quality: 100) {
...GatsbyImageSharpFluid_withWebp ...GatsbyImageSharpFluid_withWebp
} }
} }

View File

@ -83,7 +83,7 @@ export const pageQuery = graphql`
title title
image { image {
childImageSharp { childImageSharp {
fluid(maxWidth: 650 quality: 100) { fluid(maxWidth: 650, quality: 100) {
...GatsbyImageSharpFluid_withWebp ...GatsbyImageSharpFluid_withWebp
} }
} }

View File

@ -80,7 +80,7 @@ export const pageQuery = graphql`
title title
image { image {
childImageSharp { childImageSharp {
fluid(maxWidth: 650 quality: 100) { fluid(maxWidth: 650, quality: 100) {
...GatsbyImageSharpFluid_withWebp ...GatsbyImageSharpFluid_withWebp
} }
} }