<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Ихрь постит в уютный бложик: заметки с тегом javascript</title>
<link>https://ifedyukin.ru/blog/tags/javascript/</link>
<description>Фрирайтинг, заметки, шитпостинг и все подряд</description>
<author></author>
<language>ru</language>
<generator>Aegea 11.1 (v4098)</generator>

<itunes:subtitle>Фрирайтинг, заметки, шитпостинг и все подряд</itunes:subtitle>
<itunes:image href="" />
<itunes:explicit></itunes:explicit>

<item>
<title>How to inline TailwindCSS styles to SSR NextJS</title>
<guid isPermaLink="false">77</guid>
<link>https://ifedyukin.ru/blog/all/how-to-inline-tailwindcss-styles-to-ssr-nextjs/</link>
<pubDate>Wed, 04 Oct 2023 11:54:42 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/how-to-inline-tailwindcss-styles-to-ssr-nextjs/</comments>
<description>
&lt;p&gt;By default, NextJS may not include Tailwind styles in the SSR build, and with disabled JavaScript, your site may look ugly like pure HTML.&lt;/p&gt;
&lt;p&gt;To fix this, you need to install the &lt;i&gt;critters&lt;/i&gt; package.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;npm install critters&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And enable &lt;i&gt;optimizeCss&lt;/i&gt; rule in &lt;i&gt;next.config.js&lt;/i&gt;.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;experimental: {
    optimizeCss: true,
},&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Source: &lt;a href="https://github.com/vercel/next.js/discussions/12868"&gt;next.js#12868&lt;/a&gt;.&lt;/p&gt;
</description>
</item>

<item>
<title>How to fix unknown fragments types in graphql-codegen?</title>
<guid isPermaLink="false">73</guid>
<link>https://ifedyukin.ru/blog/all/how-to-fix-unknown-fragments-types-in-graphql-codegen/</link>
<pubDate>Wed, 15 Feb 2023 23:05:44 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/how-to-fix-unknown-fragments-types-in-graphql-codegen/</comments>
<description>
&lt;p&gt;I spent half of the day setting up graphql-codegen to generate the correct types for fragment queries.&lt;/p&gt;
&lt;p&gt;At first, my code looked like this:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;// file1
export const DIALOG_FIELDS = gql(`
  fragment DialogFields on Dialog {
    id
    name
  }
`);

// file2
export const GET_DIALOG = gql(`
  ${DIALOG_FIELDS}
  query GetDialog($dialogId: ID!) {
    dialog(id: $dialogId) {
      ...DialogFields
    }
  }
`);&lt;/code&gt;&lt;/pre&gt;&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const config: CodegenConfig = {
  schema: CONFIG.apiHost,
  documents: ['src/**/*.{tsx,ts,js,jsx}', '!src/gql/__generated__/*.ts'],
  generates: {
    './src/gql/__generated__/': {
      preset: 'client',
      plugins: [],
      overwrite: true,
      presetConfig: {
        gqlTagName: 'gql'
      }
    }
  },
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, with the &lt;a href="https://www.apollographql.com/docs/react/development-testing/static-typing/"&gt;basic config&lt;/a&gt; I did get an unknown type for both the query and the fragment:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;div class="fotorama" data-width="938" data-ratio="2.1813953488372"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/how-to-fix-unknown-fragments-types-in-graphql-codegen.png" width="938" height="430" alt="" /&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/how-to-fix-unknown-fragments-types-in-graphql-codegen-1.png" width="958" height="542" alt="" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I found a simple solution for this — we need to remove the string interpolation for the fragment literal from the query, because graphql-codegen &lt;a href="https://stackoverflow.com/a/74094939"&gt;automatically includes&lt;/a&gt; fragments.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;export const GET_DIALOG = gql(`
  query GetDialog($dialogId: ID!) {
    dialog(id: $dialogId) {
      ...DialogFields
    }
  }
`);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As a result, I did get an not unknown type, but the type with no data from the fragment for the query fragment.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/how-to-fix-unknown-fragments-types-in-graphql-codegen-3.png" width="1266" height="370" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;I lost the most time at this stage, eventually finding a solution in the &lt;a href="https://github.com/dotansimha/graphql-code-generator/discussions/8859#discussioncomment-4891291"&gt;small GitHub thread&lt;/a&gt;. We need to disable fragment &lt;a href="https://the-guild.dev/graphql/codegen/plugins/presets/gql-tag-operations-preset#fragment-masking"&gt;masking feature&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const config: CodegenConfig = {
  schema: CONFIG.apiHost,
  documents: ['src/**/*.{tsx,ts,js,jsx}', '!src/gql/__generated__/*.ts'],
  generates: {
    './src/gql/__generated__/': {
      preset: 'client',
      plugins: [],
      overwrite: true,
      presetConfig: {
        gqlTagName: 'gql',
        fragmentMasking: false
      }
    }
  },
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally the type contains all the information and it is complete!&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/image-3.png" width="780" height="318" alt="" /&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>Про дебаг в старых Safari</title>
<guid isPermaLink="false">71</guid>
<link>https://ifedyukin.ru/blog/all/pro-debag-v-staryh-safari/</link>
<pubDate>Wed, 01 Feb 2023 18:01:23 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/pro-debag-v-staryh-safari/</comments>
<description>
&lt;p&gt;Знаете, что самое веселое в фронтенд-разработке после похорон IE?&lt;br /&gt;
Дебажить приложения под Safari старых версий — это просто нереально...&lt;/p&gt;
&lt;p&gt;Как я делал с IE? Качал виртуалку с сайта MS с нужной версией IE и спокойно дебажил.&lt;/p&gt;
&lt;p&gt;Как я делаю с Safari? Ищу образ виртуалки, их же официально никто не распространяет, а ушлые ребята распространяют образы только через Patreon, WTF?&lt;/p&gt;
&lt;p&gt;На ноут я не могу поставить виртуалку со старой Mac OS, потому что у меня ARM процессор. На ПК я не могу поставить потому, что у меня там процессор AMD, а почему-то на новых AMD виртуалки как-то криво инструкции транслируют.&lt;/p&gt;
&lt;p&gt;В итоге приходится искать макбуки на Intel, на них ставить виртуалку со старой ОС и там дебажить — во житуха!&lt;/p&gt;
&lt;div class="hr"&gt;&lt;/div&gt;&lt;p&gt;Изначально писал в телеге — &lt;a href="https://t.me/fuckanarium/214"&gt;тык&lt;/a&gt;&lt;/p&gt;
</description>
</item>

<item>
<title>Как babel-плагин замедлил приложение в 2 раза</title>
<guid isPermaLink="false">55</guid>
<link>https://ifedyukin.ru/blog/all/kak-babel-zamedlil-prilozhenie-v-2-raza/</link>
<pubDate>Thu, 04 Mar 2021 19:11:13 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/kak-babel-zamedlil-prilozhenie-v-2-raza/</comments>
<description>
&lt;p&gt;&lt;b&gt;TL;DR:&lt;/b&gt; в &lt;mark&gt;@babel/plugin-transform-classses&lt;/mark&gt; есть &lt;a href="https://github.com/babel/babel/issues/11356"&gt;баг&lt;/a&gt;, который вызывает падение перформанса.&lt;/p&gt;
&lt;p&gt;Вчера всплыла неожиданная проблема, которая изначально звучала как «часть приложения работает медленнее после оптимизаций сборки проекта». Перформанс панель показывала неутешительные результаты, причем, посмотрев на коллстек, найти даже минимальную зацепку я не смог.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/image.png" width="1130" height="293" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Экспериментально удалось выяснить, что проблема возникает чисто из-за каких-то зависимостей.&lt;/p&gt;
&lt;p&gt;С ними в проекте недавно была проведена работа по дедубликации лок-файла и дерева зависимостей, вместе с чем обновились некоторые библиотеки. Минус 7000 строк лока, разбор проблемы обещал быть веселым.&lt;/p&gt;
&lt;p class="note-main"&gt;Проблему искал сначала мой коллега, затем я. Не знаю, сколько времени на это было потрачено, но причиной оказался один плагинчик — &lt;mark&gt;@babel/plugin-transform-classses&lt;/mark&gt;. Откатил версию, зафиксировал ее, и все заработало.&lt;/p&gt;
&lt;p class="note-aside"&gt;Из-за отсутствия зацепок исследование проводилось методом исключения, благо, модули babel были близко к началу очереди.&lt;/p&gt;
&lt;p class="note-main"&gt;Как оказалось, с 1 апреля 2020 года в репозитории есть репорт этой проблемы — &lt;a href="https://github.com/babel/babel/issues/11356"&gt;babel#11356&lt;/a&gt;.&lt;/p&gt;
&lt;p class="note-aside"&gt;Сейчас разбираться, че там внутри плагина поменялось мне лень, конечно, но когда-нибудь обязательно покопаю, когда-нибудь.&lt;/p&gt;
&lt;p&gt;В нашем случае замедление было в среднем в 2+ раза. Некоторым повезло меньше, после обновления Angular можно &lt;a href="https://github.com/angular/angular/issues/41038"&gt;поймать&lt;/a&gt; и более ощутимые просадки.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/kak-babel-zamedlil-prilozhenie-v-2-raza.png" width="1199" height="460" alt="Картинка из обсуждения в репозитории Angular'а" /&gt;
&lt;/div&gt;
&lt;p&gt;В очередной раз поймал себя на мысли, что в 2021 году babel уже не нужен. Пора начать выпиливать его из проектов!&lt;/p&gt;
&lt;p&gt;Сейчас придумал теорию заговора, что babel так с адептами ООП борется просто.&lt;br /&gt;
Как там было? Если хочешь, чтобы перестали использовать deprecated метод, добавь в него sleep.&lt;/p&gt;
</description>
</item>

<item>
<title>Ошибки state-менеджмента React-компонентов</title>
<guid isPermaLink="false">49</guid>
<link>https://ifedyukin.ru/blog/all/react-state-issues/</link>
<pubDate>Mon, 15 Jun 2020 19:01:52 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/react-state-issues/</comments>
<description>
&lt;p&gt;При работе с React у многих разработчиков, у которых с ним менее года опыта (или более, но понимания некоторых аспектов нет) возникает проблема с пониманием состояния компонента.&lt;/p&gt;
&lt;p&gt;Когда были классовые компоненты, многие хранили в стейте вообще все, даже какие-то служебные флаги, которые можно было бы в this записать, и которые на рендер компонента никак не влияют.&lt;/p&gt;
&lt;p&gt;Это было проблемой, но решалась она быстро. С появлением хуков ситуация ухудшилась.&lt;/p&gt;
&lt;p&gt;Если в классовых компонентах каждый метод — какой-то шаг лайфсайкла, то с хуками концепция и подход абсолютно другой. Отсюда вырастает много ляпов, которые не сразу заметны и которые разработчики объяснить не могут, но проблемы, возникающие из-за таких ляпов превращаются в серьезную головную боль.&lt;/p&gt;
&lt;p&gt;Парочка самых распространенных, что я видел: Эффект, который слушает проп и обновляет в след за ним стейт без каких-либо преобразований. Что-то вроде такого:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const component = ({ prop }) =&amp;gt; {
  const [savedProp, setSavedProp] = useState(null);

  useEffect(() =&amp;gt; {
    setSavedProp(prop);
  }, [prop]);
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Больше логики никакой, никаких подводных камней. Мало того, что можно было бы прямо в useState прокидывать проп, так все гораздо проще — можно было бы использовать сам этот проп, вместо его сохранения в стейт! Вероятно, там когда-то был какой-то процессинг, может быть, какие-то условия, но больше этого нет, зато есть несколько ненужных обновлений компонента.&lt;/p&gt;
&lt;p&gt;Эффект, который слушает изменения стейта, чтобы… вызвать коллбэк.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const component = ({ prop, onPropChange }) =&amp;gt; {
  const [savedProp, setSavedProp] = useState(null);

  useEffect(() =&amp;gt; {
    onPropChange(savedProp);
  }, [savedProp]);

  return (
    &amp;lt;Child prop={prop} onPropChange={setSavedProp} /&amp;gt;
  )
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Тут все вроде бы тоже понятно, но стейт не используется ни для чего, кроме триггера коллбэка. Правильнее прокинуть просто коллбэк и… все, а не триггерить несколько апдейтов просто так. Так мало того, часто забывают что-то еще в зависимости эффекта прокинуть, и он работает совсем не так, как должен.&lt;/p&gt;
&lt;p&gt;Почему-то за последние полгода столкнулся с кучей таких ошибок, и не только я — в различных чатиках тоже проскакивало частенько. То ли дело в разработчиках, то ли в хуках.&lt;/p&gt;
</description>
</item>

<item>
<title>Проблема FrontOps’а</title>
<guid isPermaLink="false">48</guid>
<link>https://ifedyukin.ru/blog/all/frontops-trouble/</link>
<pubDate>Sun, 07 Jun 2020 18:59:53 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/frontops-trouble/</comments>
<description>
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Фронтопс — перенос девопс практик на фронтенд, так как у фронтенда есть своя специфика.&lt;/li&gt;
&lt;li&gt;Основная проблема — многие фронтендеры привыкли работать с хорошими инструментами, не зная, как они работают внутри, не понимая, что происходит с кодом после того, как он написан.&lt;/li&gt;
&lt;li&gt;Каждому фронтендеру нужно понимать, как собирается его проект, что происходит после сборки, как и в каком виде код доходит до пользователя, иметь представление об используемых и существующих инструментах&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/frontend-devops.png" width="1600" height="747" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Предыдущие два года на более, чем двух проектах я занимался такой деятельностью, которую обычно называют «FrontOps». Давайте разберемся, какие у фронтопса проблемы и как их решать.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Дисклеймер:&lt;/b&gt; По теме я не раз говорил на митапах &lt;a href="https://webpurple.net"&gt;Webpurple&lt;/a&gt;, но структурировать все в текст так и не вышло нормально. Пост — просто изливание кучи мыслей, не структурирован, хотя планировалось нечто большое на несколько иную тему, в черновиках лежал больше, чем полгода, но, как &lt;a href="https://marinintim.com/2020/publish-anyway/"&gt;говорил Тим&lt;/a&gt;, надо признаться, что черновики не допишутся и публиковать как есть, вот публикую. Ниже все исключительно ИМХО.&lt;/p&gt;
&lt;p&gt;Повествование я начну как обычно — с небольшого отступления в прошлое. Когда я начинал работать как разработчик, я никогда не был против задач не для разработчика (как сейчас считается): сервер настроить, сконфигурировать прокси, сборщик из пачки sh-скриптов подкрутить, еще что-то. Да, эти задачи я решал постольку-поскольку, не использовал всякие крутые штуки и инструменты, до поры до времени ни про вагрант, ни про ансибл не слышал, но задачи решал и мне в целом нравилось.&lt;/p&gt;
&lt;p&gt;Что касается специализаций и развитий навыков, я приверженец T-shape подхода: в основном я фронтендер, но и в DevOps с бэкендом могу. Вот благодаря этому пересечению с девопсом я гордо могу назвать себя FrontOps’ом.&lt;/p&gt;
&lt;h2&gt;Появление FrontOps&lt;/h2&gt;
&lt;p&gt;Вторая часть слова — Ops. Когда говорят про Ops, вспоминается DevOps. Несколько обидно, что почему-то в &lt;b&gt;Dev&lt;/b&gt;Ops не всегда включают FrontEnd, &lt;s&gt;шутка про формошлепов&lt;/s&gt; на это есть свои причины, но начнем с того, что такое DevOps.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;DevOps&lt;/b&gt; — методология взаимодействия специалистов по разработке (Dev) со специалистами по информационно-технологическому обслуживанию (Ops) и интеграцию их рабочих процессов друг в друга для обеспечения качества продукта.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ru.wikipedia.org/wiki/DevOps"&gt;Wikipeadia — DevOps&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;В диалогах я слышал что-то вроде: «Какие еще оперейшены во фронтоне?! Че там оперировать, статика же?», — наверно, поэтому как-то это все в отдельное направление и выделилось местами. Изначально, девопс — это больше про бэкенд и все, что около него, как-то так сложилось исторически, во всяком случая я чаще сталкивался с таким, что в рамках девопс-процессов о фронтенде никто особо не думал, и это как-то само должно было решаться. Ведь что нужно бэкенду? Кластер серверов, лоадбалансинг, умный деплоймент с нулевым даунтаймом, скейлинг и много всего сложного и интересного. А что на фронтенде обычно было? Отдать статику (часто через тот же бэкенд) и все, но сейчас времена другие.&lt;/p&gt;
&lt;p&gt;В итоге, отпочковался фронтопс. &lt;b&gt;Фронтопс&lt;/b&gt; — ничто иное, как &lt;b&gt;перенос девопс-практик на фронтенд&lt;/b&gt;. Те же практики, те же решаемые задачи, просто с некоторой спецификой что ли. Девопс (и фронтопс, выходит, тоже) подразумевает работу по некоторому циклу из определенных шагов с использованием определенных техник и инструментов.&lt;/p&gt;
&lt;h2&gt;Проблема FrontEnd ~ Ops&lt;/h2&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/devops-phase.png" width="1280" height="725" alt="" /&gt;
&lt;/div&gt;
&lt;p class="foot"&gt;Create... а дальше?&lt;/p&gt;
&lt;p&gt;После того, как код написали, с ним еще много интересного происходит до того, как он начнет решать задачи пользователя и бизнеса, да и в процессе выполнения этих задач интересное не заканчивается. На бэкенде, разработчики в большинстве своем представляют, что с кодом происходит, после того, как он скомпилировался, так как по-другому там жить довольно сложно, надо все-таки понимать, кто там jar’ник запустил и какие и куда пакеты летают, бэкендерам даже надо понимать, что такое TCP, например (!!!).&lt;/p&gt;
&lt;p&gt;В мире фронтенда ситуация несколько иная. На один мой комментарий в ревью «этот код не соберется вот по этой причине, вот линк, почитай, интересная штука» мне отвечали «я простой разработчик, это меня не касается» (утрирую, но примерно так было). WTF? В тот момент я понял, что что-то не так.&lt;/p&gt;
&lt;p&gt;Откуда растут ноги этой проблемы? Из времен, когда разработка фронтенда заключалась в запуске сервера статики (и то не всегда) и изменения файликов, которые тут же отображаются в браузере, а деплой заключался в том, чтобы отдать файлики бэкендерам, а они там в jsp перегонят и сами разберутся, или загрузить файлики через FTP на другой сервер. Просто, круто, DX на высоте, код написал и все.&lt;/p&gt;
&lt;p&gt;Мир меняется, требования растут, технологии усложняются. Сначала начали гонять файлы через различные инструменты, которые код бандлят и транспилируют, потом этапы стали сложнее и дольше, а файлов стало больше, добавились дополнительные этапы, куча библиотек, фреймворки. С одной стороны, сейчас ситуация совсем другая, но с другой — для многих разработчиков все остается так же с небольшим усложнением:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;`git clone` — оп, код у меня;&lt;/li&gt;
&lt;li&gt;`npm install` — ну, зависимости у меня, погнали;&lt;/li&gt;
&lt;li&gt;`npm start` — ммм, *клик-клац-клик*, все, написал;&lt;/li&gt;
&lt;li&gt;`git commit -a &amp;&amp; git push` — отлично, таску в резолв.&lt;/li&gt;
&lt;/ul&gt;
&lt;div style="width:100%;height:0;padding-bottom:56%;position:relative;"&gt;&lt;iframe src="https://giphy.com/embed/QNFhOolVeCzPQ2Mx85" width="100%" height="100%" style="position:absolute" frameBorder="0" class="giphy-embed" allowFullScreen&gt;&lt;/iframe&gt;
&lt;/div&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;С точки зрения разработчика ничего особо не поменялось, но появилась какая-то магия, которая где-то там что-то делает. Разработчик не знает, как оно работает внутри и что происходит с кодом после пуша, да даже не после пуша, а после сохранения файлика, кто над этим файликом колдует на его компьютере, во что превращается этот файлик. Рассмотрим выдуманную историю. В один момент было решено переехать из одного облака в другое и допилить пару крупных фич. Для этого надо в зависимоти от инстанса приложения нужно обращаться к различным сервисам, чуть-чуть подпилить сборку проекта, чтобы он собирал все в несколько бандлов и парочку шрифтов подключить.&lt;/p&gt;
&lt;p&gt;Вроде бы задача понятна, но для многих разработчиков фронтенда она нерешаема по одной простой причине: &lt;b&gt;они слишком отдалились от инструментов разработки и не успели подстроиться под их изменения&lt;/b&gt;, да и думать об этом часто не нужно, и «вообще пусть это девопсы делают, оперейшены все-таки». Но откуда девопс-инженерам знать о том, как код должен собираться вебпаком? А тем более о всяких узких местах, типа хранений шрифтов на том же домене, чтобы лишний dns-запрос не делался?&lt;/p&gt;
&lt;p&gt;Много разработчиков на C++ вы знаете, которые не понимают, как линкуются библиотеки и не могут написать make-файл? А фронтендеров таких много, и это часто не проблема фронтендера, да и не проблема это, просто у нас много инструментов, которые легко устанавливаются и все делают сами.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Хорошо, когда инструменты настолько удобны, что легко решают все проблемы за тебя. Плохо, когда ты не представляешь, как они это делают.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Решаем проблемы&lt;/h2&gt;
&lt;p&gt;Фронтопс — это не только девопс + фронтенд, фронтопс — то, что нужно знать фронтенд разработчику, для того, чтобы быть действительно фронтенд разработчиком, ведь закомиченный код — далеко не результат работы. Нужно понимать, что с кодом происходит, куда он потом помещается и как доходит до пользователя.&lt;/p&gt;
&lt;p&gt;Если вы понимаете, что происходит с вашим кодом в вашем проекте, напишите к этому документацию, устройте пару созвонов, где покажете остальным участникам команды, что и как просиходит в процессе сборки, непрерывных интеграций, деплоя.&lt;/p&gt;
&lt;p&gt;Если вы не понимаете этого, изучите проект и инструменты, которые в нем используются, почитайте документацию к ним, спросите более опытных тиммейтов, сделайте собственный стартер или пет-проджект, накрутите там крутой CI/CD, оптимальную сборку.&lt;/p&gt;
&lt;p&gt;Изучение «оперейшенов» на базовом уровне занимает не так много времени, но это очень ценный и полезный опыт, который положительно скажется на кругозоре и понимании того, как вообще работает проект и какую роль в нем занимает фронтенд.&lt;/p&gt;
&lt;h2&gt;Инструменты, о которых нужно иметь представление&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Вебпак, бабел, бандлеры, таск-раннеры (&lt;a href="https://webpack.js.org/guides/getting-started/"&gt;Webpack&lt;/a&gt;, &lt;a href="https://babeljs.io/docs/en/"&gt;Babel&lt;/a&gt;, &lt;a href="https://docs.npmjs.com/misc/scripts"&gt;npm-scripts&lt;/a&gt;, &lt;a href="https://gulpjs.com/"&gt;Gulp&lt;/a&gt;) — изучите принцип работы сборщиков и компиляторов JavaScript, TypeScript, CSS, поймите разницу между Rollup и Webpack, изучите возможности npm-scripts и Gulp (хоть его и мало кто использует сейчас).&lt;/li&gt;
&lt;li&gt;Форматтеры, линтеры, статические анализаторы (&lt;a href="https://eslint.org/"&gt;ESlint&lt;/a&gt;, &lt;a href="https://prettier.io/"&gt;Prettier&lt;/a&gt;, &lt;a href="https://github.com/stylelint/stylelint"&gt;stylelint&lt;/a&gt;, &lt;a href="https://www.sonarqube.org/"&gt;SonarQube&lt;/a&gt;) — хватит спорить на ревью о том, где должна стоять запятая, хватит мутировать иммутабельные значения, автоматизируйте проверку этого.&lt;/li&gt;
&lt;li&gt;Git и хуки (&lt;a href="https://github.com/typicode/husky"&gt;Husky&lt;/a&gt;, &lt;a href="https://github.com/okonet/lint-staged"&gt;lint-staged&lt;/a&gt;, &lt;a href="https://try.github.io/"&gt;Learn Git&lt;/a&gt;) — изучите Git, для вас не должны быть проблемой ребейзы, которые заканчиваются конфликтами, используйте хуки для автоматического запуска проверок.&lt;/li&gt;
&lt;li&gt;Тестирование (&lt;a href="https://jestjs.io/"&gt;Jest&lt;/a&gt;, &lt;a href="https://mochajs.org/"&gt;Mocha&lt;/a&gt;, &lt;a href="https://jasmine.github.io/"&gt;Jasmine&lt;/a&gt;, &lt;a href="https://www.cypress.io/"&gt;Cypress&lt;/a&gt;) — изучите инструменты тестирования, способы их использования, виды тестов.&lt;/li&gt;
&lt;li&gt;Дополнительные инструменты для проверок проекта (&lt;a href="https://github.com/ai/size-limit"&gt;size-limit&lt;/a&gt;, &lt;a href="https://github.com/GoogleChrome/lighthouse-ci"&gt;Lighthouse CI&lt;/a&gt;) — проверяйте перформанс-метрики вашего проекта прямо на этапе непрерывных интеграций.&lt;/li&gt;
&lt;li&gt;CDN, файлы, сервера и прокси (&lt;a href="https://nginx.org/ru/"&gt;nginx&lt;/a&gt;, &lt;a href="https://aws.amazon.com/ru/s3/"&gt;S3&lt;/a&gt;, &lt;a href="https://aws.amazon.com/ru/cloudfront/"&gt;CloudFront&lt;/a&gt;, &lt;a href="https://cloudinary.com/"&gt;Cloudinary&lt;/a&gt;) — изучите, как файлы могут храниться и раздаваться.&lt;/li&gt;
&lt;li&gt;Мониторинг (&lt;a href="https://www.nginx.com/products/nginx-amplify/"&gt;Amplify&lt;/a&gt;, &lt;a href="https://sentry.io/welcome/"&gt;Sentry&lt;/a&gt;) — настройте мониторинги, отслеживайте, что происходит с кодом при использовании.&lt;/li&gt;
&lt;li&gt;Деплой, сервера, докер (&lt;a href="https://github.com/ansible/ansible"&gt;Ansible&lt;/a&gt;, &lt;a href="https://docs.docker.com/get-started/"&gt;Docker&lt;/a&gt;, &lt;a href="https://habr.com/ru/post/122445/"&gt;SSH&lt;/a&gt;) — изучите подходы к деплою, варианты запуска приложений, научитесь работать с SSH и консолью — это действительно нужный навык.&lt;/li&gt;
&lt;li&gt;CI/CD (&lt;a href="https://docs.gitlab.com/ee/ci/"&gt;GitLab CI&lt;/a&gt;) — изучите принципы и подходы к организации CI/CD, сделайте полезный пайплайн для своего проекта или оптимизируйте существующий.&lt;/li&gt;
&lt;/ul&gt;
</description>
</item>

<item>
<title>UI library based on Chakra-UI bundling</title>
<guid isPermaLink="false">47</guid>
<link>https://ifedyukin.ru/blog/all/ui-lib-chakra-bundling/</link>
<pubDate>Thu, 09 Apr 2020 18:49:15 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/ui-lib-chakra-bundling/</comments>
<description>
&lt;p&gt;Recently I tried to build some UI components library that based on &lt;a href="https://github.com/chakra-ui/chakra-ui"&gt;Chakra-UI&lt;/a&gt;. At first my library didn’t contain react components and compiled using typescript and it worked perfectly. But after I added React components based on Chakra-UI and migrated build to webpack, I’ve got the next error from react hooks that were called inside Chakra-UI hooks such as useTheme:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
By the way, Chakra-UI repository has this issue about this error - ((https://github.com/chakra-ui/chakra-ui/issues/495 #495)).&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What is the reason for this error? useTheme hook and Chakra-UI library in whole use react library. As a result library imports react and react-dom. When webpack bundling your library that includes Chakra-UI, it includes react and react-dom to bundle of your library. In brief, you import react to your project, also you import your library with Chakra-UI that contains react too, as a result — you have at least two React copies. And in fact, it’s incorrect hook call.&lt;/p&gt;
&lt;p&gt;How to fix it? Use only single react instance in your app. If you’re using webpack you can mark react and react-dom libraries as external libraries for your components library:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;externals: {
  // mark &amp;quot;react&amp;quot; and react-dom as external packages that will import using commons
  react: 'commonjs react',
  'react-dom': 'commonjs react-dom'
},
output: {
  path: path.resolve('./'),
  libraryTarget: 'commonjs2', // set library target
  filename: '[name].js',
  library: '',
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now bundle of your library doesn’t contain react and react-dom. We marked libraries as external and have pure require calls but doesn’t have library in bundle:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;/***/ &amp;quot;react&amp;quot;:
/*!************************!*\
  !*** external &amp;quot;react&amp;quot; ***!
  \************************/
/*! no static exports found */
/***/ (function(module, exports) {
  module.exports = require(&amp;quot;react&amp;quot;);
/***/ }),

/***/ &amp;quot;react-dom&amp;quot;:
/*!****************************!*\
  !*** external &amp;quot;react-dom&amp;quot; ***!
  \****************************/
/*! no static exports found */
/***/ (function(module, exports) {
  module.exports = require(&amp;quot;react-dom&amp;quot;);
/***/ })&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This way also can help you if you’re using hooks and creating your own UI library without Chakra-UI.&lt;/p&gt;
</description>
</item>

<item>
<title>NextJS, первый взгляд</title>
<guid isPermaLink="false">46</guid>
<link>https://ifedyukin.ru/blog/all/nextjs-begin/</link>
<pubDate>Fri, 28 Feb 2020 18:45:08 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/nextjs-begin/</comments>
<description>
&lt;p&gt;На днях одно (пока) небольшое приложение перевел на NextJS, ибо нужен качественный SSR для SEO, а самостоятельно писать кучу хелперов слишком дорого по времени.&lt;/p&gt;
&lt;p&gt;На первый взгляд все оказалось очень круто, что интересно, почему-то даже некоторые интерактивные элементы начали работать быстрее, не разбирался пока, какая магия их ускорила, но не отметить не мог. Миграция тоже прошла гладко, подключение к проекту было самым крутым из всех инструментов: установить через yarn, удалить конфиг webpack’а, — именно последний пункт больше всего понравился.&lt;/p&gt;
&lt;p&gt;Я сразу подумал, что все слишком хорошо, чтобы быть правдой, в одном чатике отметили, что через недельку я буду бомбить на Next, но пока держусь и странность всего одну заметил — роутинг.&lt;/p&gt;
&lt;p&gt;Казалось бы, роутинг должен быть сильнее всего проработан в фреймворке такого типа, но то ли я не понял, как им правильно пользоваться, то ли оно как-то странно сделано. Ладно, со ссылками пришлось повозиться, ибо в одном случае href — тот url, на который непосредственно идет редирект, а в другом случае — slug страницы, который я определил в роутере, а непосредственно ссылку прокидывать нужно в проп as.&lt;/p&gt;
&lt;p&gt;Но вот если пушить что-то в url с динамическим роутом, то получается что-то странное. Пушу не явный конечный url, а slug описанный в роутинге, нужна квери-стринга? Ок, квери можно прокинуть в отдельный объект, они даже до чилда долетят, но вот в url они не попадут, бери ручками и прокидывай.&lt;/p&gt;
&lt;p&gt;Тут у меня прямо явный диссонанс:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ожидание: сейчас я в метод пуш прокину объект квери и текущий адрес — хоба, квери обновлен, строка адреса хранит состояние, все счастливы.&lt;/li&gt;
&lt;li&gt;Реальность: прокидываю реальный адрес, прокидываю шаблон адреса, прокидываю квери-объект, новый полный адрес вместе с квери, который сам леплю чем-то, — тоже все счастливы, конечно, но большей ценой.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Действий немного, но хотелось бы каких-то изящных абстракций из коробки, а не руками их делать.&lt;/p&gt;
</description>
</item>

<item>
<title>RS Conf 2019</title>
<guid isPermaLink="false">40</guid>
<link>https://ifedyukin.ru/blog/all/rs-conf-2019/</link>
<pubDate>Fri, 13 Sep 2019 18:22:31 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/rs-conf-2019/</comments>
<description>
&lt;p&gt;С 9 по 11 августа в Минске проходила конференция ”&lt;a href="https://rsconf.by/"&gt;The Rolling Scopes Conference 2019&lt;/a&gt;”, на которой я побывал. Отмечу, что это первая зарубежная конференция, на которой я побывал, хоть и Москва-Минск считается внутренним рейсом.&lt;/p&gt;
&lt;p&gt;В Минске я тоже был впервые, о городе много рассказать не могу, ибо большая часть времени все-таки отошла конференции, но и пост не об этом. Если кратко о городе, то он мне понравился, причем гораздо сильнее той же Москвы. За все время общественным транспортом воспользовался один раз, остальные передвижения либо пешком, либо на самокатах, которые днем легко найти, а вот вечером их разбирают другие любители ночных покатушек на электро-транспорте. Вероятно, общее впечатление несколько нерелевантно, ввиду того, что место проведения конференции было выбрано довольно удачно (точнее улица, где это место было).&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/msq-rs-conf-street.jpg" width="1280" height="960" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Доклады были на русском и английском языках, параллельно на трех сценах. Конференция шла три дня, первый день из которых был днем мобильных разработчиков и дизайнеров, причем сначала мне казалось, что этот день точно не для меня, но все же много нового узнал из докладов на общие темы (паттерны, подходы к разработке) и докладов по дизайну (разделяю ту точку зрения, что разработчики отчасти дизайнеры и наоборот). Забавно, что больше всего всем запомнился не какой-то конкретный доклад, а &lt;a href="https://www.youtube.com/watch?v=0k-_4WloY6Y"&gt;один из роликов&lt;/a&gt;, показанный как пример продакшена креативных студий в одном из докладов.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/evoneering/status/1160243552837931008"&gt;https://twitter.com/evoneering/status/1160243552837931008&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Оставшиеся два дня были посвящены теме “JS/FrontEnd”, в эти дни мне было уже более комфортно, что слушать доклады, что участвовать в других активностях.&lt;/p&gt;
&lt;p&gt;PS ClassicВ частности «Evolution Gaming» проводили челлендж по минификации кода на JS (минифицировать нужно было ручками, да). Смело могу заявить, что это был первый подобный челлендж за все время на всех конференциях, в котором мне было действительно интересно участвовать, да и на стенде были люди, с которыми было просто интересно просто пообщаться.&lt;br /&gt;
Интерес к челленджу также подогревался открытой доской с результатами и периодическими сообщениями в чат конференции о том, что кто-то вышел вперед. За первое место полагалась PS Classic, в первый день я ее и забрал, во второй день челлендж был не менее интересным, но я решил уже не участвовать (зачем мне еще одна PS Classic).&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;div class="fotorama" data-width="960" data-ratio="0.75"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/msq-rs-conf-ps.jpg" width="960" height="1280" alt="" /&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/msq-rs-conf-photo.jpg" width="1920" height="2560" alt="" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Тематика докладов была разнообразной, затронули и просто подходы к разработке, и GDPR, и архитектуру. Спикеры в большинстве своем широко охватывали темы и были не против пообщаться после докладов, как-то дополнительно раскрыть свою точку зрения. Новой информации и знаний получил много, на некоторые проблемы и вопросы посмотрел с другой стороны.&lt;/p&gt;
&lt;p&gt;Доклады были интересными, организация отличная, мерч получил, Минск посмотрел, с интересными людьми пообщался. Отличная конференция, по возможности, в следующем году также приеду, а, может быть, и как спикер попробую выступить, если достойную тему найду, не всегда же на локальных мероприятиях только участвовать.&lt;/p&gt;
&lt;p&gt;Под приятными впечатлениями от этой конференции решил посетить “HolyJS”, посмотрим, как оно будет.&lt;/p&gt;
</description>
</item>

<item>
<title>Почему ломаются React-хуки</title>
<guid isPermaLink="false">39</guid>
<link>https://ifedyukin.ru/blog/all/broken-react-hooks/</link>
<pubDate>Fri, 13 Sep 2019 18:18:37 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/broken-react-hooks/</comments>
<description>
&lt;p&gt;В React 16.8 добавили такую крутую фичу, как хуки, все сразу бросились ее юзать, некоторые начали ее юзать еще в бетах, до официального релиза, я же к этой фиче отнесся как-то несколько холодно.&lt;/p&gt;
&lt;p&gt;Без сомнения, идея очень крутая, использовать удобно, можно уходить от классов (я в классах ничего плохого не вижу, но все почему-то это как один из главных пунктов приводят). Но, во-первых, людей надо учить использовать хуки, просто так вот взять и перейти на хуки в production-приложении мне кажется несколько неправильным (какие еще причины, кроме людей, ниже), ибо экспертиза из воздуха не появляется, во-вторых, хуки вносят несколько новых “парадигм”, работу тех же эффектов опять же надо объяснять людям.&lt;/p&gt;
&lt;p&gt;Но пост не о людях, а о самих хуках. На одном из проектиков мы не юзаем хуки, в основном, по причинам, описанным выше, плюс ко всему, основную часть команды составляют малоопытные разработчики, которые только познают все чудеса мира фронтенда, да и сроки слишком сжаты, чтоб в рамках проекта новые техники изучать, но в один момент понадобилось втащить одну библиотеку, которая, как можно догадаться, использует хуки. Да, вот так вот стало сразу модно использовать хуки в библиотеках, не заботясь об обратной совместимости и всяких таких модных важных вещах, при том условии, что хуки там ну чисто для того, чтобы были.&lt;/p&gt;
&lt;p&gt;Втащили библиотеку, заюзали их хуки и… все сломалось, вместо приложения красовалась ошибка:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Конечно, первым делом я перешел по ссылке, проверил версии React’а и всего остального, проверил корректность использования хуков, но все мои исследования пользы не принесли, все было корректно, тогда я пошел шерстить GitHub в поисках решений и источников проблемы.&lt;/p&gt;
&lt;p&gt;Из первого попавшегося &lt;a href="https://github.com/facebook/react/issues/15315"&gt;react#15315&lt;/a&gt;, там виновных нашли сразу кучу, и неправильное использование роутера, и HtmlWebpackPlugin, и вызов в HoC’е, а в &lt;a href="https://ifedyukin.ru/blog/2019/react-dropzone"&gt;react-dropzone#821&lt;/a&gt; вообще оказалось, что все ломали observer’ы из mobx.&lt;/p&gt;
&lt;p&gt;Мой случай похож, хуки ломались из-за использования такой утилиты, как why-did-you-update, которая, к слову, довольно долго жила во многих проектах без проблем, а тут вот всплыло. Соответственно, чтобы поправить работу приложения ни осталось ничего, кроме того, что убрать вызов утилиты.&lt;/p&gt;
&lt;p&gt;Историю свою я рассказал, какой вывод можно сделать? Не знаю, но точно знаю, что хуки с существующими решениями не всегда корректно работают, и что если вдруг хуки сломались надо подчищать HoC’и, несколько часов жизни в никуда...&lt;/p&gt;
</description>
</item>

<item>
<title>circular-dependency-plugin</title>
<guid isPermaLink="false">35</guid>
<link>https://ifedyukin.ru/blog/all/circular-dependency-plugin/</link>
<pubDate>Sun, 02 Jun 2019 18:12:15 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/circular-dependency-plugin/</comments>
<description>
&lt;p&gt;Было ли у вас такое, что при develop-сборке все отлично работает, а при production-сборке приложение падает с несколько пугающей ошибкой?&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;TypeError: Super expression must either be null or a function, not undefined&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Уверен, что было, причем часто проблема возникает из ниоткуда, что очень неприятно, так еще и при исследовании всплывает, что файлики-то все на месте, импорты-экспорты тоже правильные, в чем же дело? А дело в циклических зависимостях.&lt;/p&gt;
&lt;p&gt;О том, в чем вообще проблема и какие есть способы ее решить, можно почитать в посте «&lt;a href="https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de"&gt;How to fix nasty circular dependency issues once and for all in JavaScript &amp; TypeScript&lt;/a&gt;», откуда я и взял картинку ниже.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/circular-dependency-module-loading.png" width="1560" height="780" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Я же просто хочу сказать, что это самая противная проблема, которая возникает обычно очень неожиданно и очень портит жизнь, потому надо бы пресекать или хотя бы отслеживать циклические зависимости, если у вас webpack, то делается это легко, времени много не займет, а профитов даст кучу.&lt;/p&gt;
&lt;p&gt;Резво ставьте &lt;a href="https://www.npmjs.com/package/circular-dependency-plugin"&gt;circular-dependency-plugin&lt;/a&gt; и подключайте его в конфиг, и будет вам счастье.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;// webpack.config.js
const CircularDependencyPlugin = require('circular-dependency-plugin')

module.exports = {
  plugins: [
    new CircularDependencyPlugin({
      exclude: /node_modules/,
      cwd: process.cwd()
    })
  ]
}&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Docker’изация JavaScript-приложений</title>
<guid isPermaLink="false">29</guid>
<link>https://ifedyukin.ru/blog/all/dockerize-js/</link>
<pubDate>Fri, 30 Nov 2018 18:03:02 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/dockerize-js/</comments>
<description>
&lt;p&gt;Поучаствовал в IT-Субботнике, рассказал о Docker’изации JavaScript-приложений.&lt;/p&gt;
&lt;div class="e2-text-video"&gt;
&lt;iframe src="https://www.youtube.com/embed/xC7-HCPEn6k?enablejsapi=1" allow="autoplay" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>WebPurple Telegram Bot</title>
<guid isPermaLink="false">21</guid>
<link>https://ifedyukin.ru/blog/all/webpurple-tg-bot/</link>
<pubDate>Thu, 25 Jan 2018 17:39:46 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/webpurple-tg-bot/</comments>
<description>
&lt;p&gt;В последние пару недель я пытаюсь активно саморазвиваться, изучать что-то новое и делать чисто из интереса, даже диплом писать начал.&lt;br /&gt;
В первую очередь мой интерес направлен на NodeJS и всё вот это серверное, поэтому дальше BackEnd’а дипломного проекта я не дошёл, но разговор не об этом.&lt;/p&gt;
&lt;p&gt;В Рязани наиболее активным и растущим сообществом разработчиков является WebPurple, к которому я себя смело могу отнести, ибо я являюсь активным участником (ну, я так считаю).&lt;/p&gt;
&lt;p&gt;Для обсуждения всякого разного был создан чатик в Telegram, ну и чтобы привнести интерактивности, было принято решение сделать какого-нибудь бота, чтоб прямо круто всё было.&lt;/p&gt;
&lt;p&gt;Изначально бот предполагался в виде типичного бота из IRC-каналов, но в итоге обсуждения он перешёл в разряд «самостоятельных» ботов. Разработкой решил заниматься я, однако в плане различных особенностей на уровне разработки, языка (NodeJS) и его использования мне помогали коллеги, чему я очень рад и благодаря чему я опять же узнал много нового.&lt;/p&gt;
&lt;p&gt;Бот не должен был быть сложным и нагруженным, его задачей было просто джелиться информацией, составленный список функций выглядел как-то так:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;По запросу отправлять последние новости;&lt;/li&gt;
&lt;li&gt;По запросу отправлять информацию о планируемых мероприятиях;&lt;/li&gt;
&lt;li&gt;По запросу отправлять ссылочки на сообщества, репозитории и всякое такое;&lt;/li&gt;
&lt;li&gt;Дать юзеру возможность подписаться на рассылку новостей и как только, так сразу слать ему посты.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Вроде бы всё, изначально информацию планировалось тащить из API нашего сайта, но потом всё-таки передумали, и теперь тащим всё прямиком из группы ВКонтакте, ибо она в большей части кейсов является как бы первоисточником всего и вся.&lt;/p&gt;
&lt;p&gt;Дабы упростить процесс разработки я, недолго думая, ввёл следующую команду в терминал и был рад.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;npm install --save node-telegram-bot-api&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Первые сложности возникли, когда я пытался с Long-Pulling соединения всё на WebHook’и переделать, но это я мануалы плохо читал.&lt;br /&gt;
По итогу первая версия, которая просто отдавала «статику» завелась за пару часов. А всё просто потому, что надо учитывать всякие слэши и вот это всё, ну и то, что у меня не чисто бот, а Express, «внутри» которого живёт бот.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const TelegramBot = require('node-telegram-bot-api');
const botUrl = `/bot${botToken}`;
const bot = new TelegramBot(botToken, {
  onlyFirstMatch: true,
});

app.post(botUrl, (req, res) =&amp;gt; {
  bot.processUpdate(req.body);
  res.sendStatus(200);
});

app.listen(port, () =&amp;gt; {
  console.log(`Express server is listening on ${port}`);
  bot.setWebHook(`https://example.com${botUrl}`);
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Стоит учесть, что мне было лень разбираться с нормальным выстраиванием процессов разработки бота, поэтому всё отлаживал я прямо на Heroku, деплоя туда новую версию, т. к. всё было быстро, неудобств я не испытывал. Но для вероятных контрибьюторов я упростил задачу и сделал Long-Pooling в development-окружении, а WebHook’и только в production:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const TelegramBot = require('node-telegram-bot-api');
const botUrl = `/bot${botToken}`;
const bot = new TelegramBot(botToken, {
  onlyFirstMatch: true,
  polling: !isProduction,
});

app.listen(port, () =&amp;gt; {
  console.log(`Express server is listening on ${port}`);
  if (isProduction) {
    bot.setWebHook(`${hookUrl}${botUrl}`);
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Следющая сложность — многоязычность. Довольно много времени было потрачено на поддержку нескольких языков, чтобы бот, в зависимости от языка, слал то или иное сообщение. Не понятно, зачем оно нужно было, но раз есть, значит есть. Поэтому тексты всех сообщений и регулярных выражений получаются из функции, которая принимает на вход яхык.&lt;br /&gt;
Ну, и пару раз я думал, что всё сломал, а это просто Telegram кэширует (или что-то ещё с ними делает) настройки языка.&lt;/p&gt;
&lt;p&gt;Затем пришла пора работать с API ВКонтакте, обрабатывать хуки от Callback API и вот это всё. Документацию я опять же читал не совсем внимательно, но когда в ответ на запрос нужно отправлять помимо статуса 200 ещё и текст `ok` &lt;b&gt;именно маленькими&lt;/b&gt; буквами было для меня чуть-чуть неожиданно.&lt;br /&gt;
А перед этим на этот же запрос нужно отправлять ответ в виде «сколько-то_там_значного» кода. Чтобы не производить деплой пару раз подряд я вынес эту переменную в ENV.&lt;/p&gt;
&lt;p&gt;Callback API — лучшая часть VK API, потому что... использовать его ну очень просто. Получить текст записи, только что опубликованной в группе — элементарно! Однако, я хотел дать пользователям возможность получать контент по некоторому набору категорий, которые задавались в постах хэш-тэгами. Несколько реализаций «парсера» были испробованы, в итоге остановился на варианте ниже, т. к. проще, удобнее, теги можно обработать перед отправкой:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const formattedText = text.replace(
  regExp,
  (_, type) =&amp;gt; {
    types.push(type);
    return `#${type}`;
  }
);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Плюс ко всему теперь на любой запрос нужен хоть какой-то ключ, из сообщества нельзя вытащить список эвентов, организованных им, поэтому пришлось приделывать костыли в виде поиска по группам от имени пользователя и фильтрации нужных групп-эвентов. Т. к. поиск идёт по названиям, то кто-то левый вполне может попасть в выборку, ведь даже список организаторов эвента получить нельзя. Надеюсь, ВК скоро решат эту проблему.&lt;/p&gt;
&lt;p&gt;Таким образом получение планируемых эвентов выглядит как-то так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;fetch(/* Получаем 5 групп по определённому названию, с типом &amp;quot;event&amp;quot;, старт которых только будет в будущем */)
  .then(r =&amp;gt; r.json())
  .then((r) =&amp;gt; {
    // Для вязкости добавим проверок
    if (r.response &amp;amp;&amp;amp; r.response.items &amp;amp;&amp;amp; r.response.items.length) {
      const groups = r.response.items.map(g =&amp;gt; g.id);
      fetch(/* О каждой группе получим информацию */)
        .then(res =&amp;gt; res.json())
        .then((res) =&amp;gt; {
          if (res.response &amp;amp;&amp;amp; res.response.length) {
            const processEvent = vkEventProcess(uid, bot);
            res.response
              .sort((a, b) =&amp;gt; a.start_date - b.start_date)
              .reduce(
                // Тут &amp;quot;чейним&amp;quot; отправку сообщений, чтобы был точный порядок
                (promise, event) =&amp;gt; promise.then(
                  () =&amp;gt; processEvent(event),
                  err =&amp;gt; console.log(err),
                ),
                Promise.resolve(),
              );
          }
        });
    } else {
      bot.sendMessage(uid, getNoEventsMessage(lang));
    }
  });&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Получение ключа пользователя для сервера — отдельная повесть, в неё я углубляться не буду, просто скажу, что раньше было лучше... и проще с этим всем. Да и с документацией API дела лучше обстояли, во всяком случае не было подобного:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/vk_api-member_status.png" width="580" height="165" alt="" /&gt;
&lt;/div&gt;
&lt;p class="foot"&gt;Описание `member_status`&lt;/p&gt;
&lt;p&gt;Потом были этапы отладки, поиска багов и вот этого всего «предрелизного». Затем проведены интеграции, написаны тесты, небольшой README, добавлена возможность нормальной локальной отладки (был бы localetunnel, была б сказка, но там всё от параметров сети сильно зависит), CI, простой деплой на собственный инстанс Heroku и... «релиз».&lt;/p&gt;
&lt;div id="vk_post_-94098151_899"&gt;&lt;/div&gt;&lt;script type="text/javascript"&gt;
  (function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//vk.com/js/api/openapi.js?151"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'vk_openapi_js'));
  (function() {
    if (!window.VK || !VK.Widgets || !VK.Widgets.Post || !VK.Widgets.Post('vk_post_-94098151_899', -94098151, 899, 'kqc7UUQ3k248p5n5UAQ4ts0q-is')) setTimeout(arguments.callee, 50);
  }());
&lt;/script&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;После релиза я обнаружил, что событие «Репост» у ВКонтакте работает не совсем так, как я ожадал, поэтому пришлось быстренько фиксить, но все подписчики бота тогда уже получили невалидное сообщение.&lt;/p&gt;
&lt;p&gt;Пост изначально планировался каким-то более содержательным и интересным, но из-за своей лени я остановился просто на перечислении сложностей, которые возникли в процессе разработки. Это всё-таки просто мой дневник в стиле «а что я вообще тогда делал».&lt;/p&gt;
&lt;p&gt;Посмотреть на бота можно здесь: &lt;a href="https://t.me/WebPurple_bot/"&gt;Telegram Bot&lt;/a&gt;.&lt;br /&gt;
Поучаствовать в разработке, предложить функционал и сообщить о багах можно здесь: &lt;a href="https://github.com/ifedyukin/WebPurple-tg-bot"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</description>
</item>

<item>
<title>Каррирование в JS</title>
<guid isPermaLink="false">15</guid>
<link>https://ifedyukin.ru/blog/all/js-currying/</link>
<pubDate>Wed, 16 Aug 2017 17:12:37 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/js-currying/</comments>
<description>
&lt;p&gt;Сегодня столкнулся с одной задачкой, которая по своему типу напоминает типичную вайтборд-задачу.&lt;br /&gt;
Есть функция:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const add = (a, b, c, d) =&amp;gt; a + b + c + d;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Для неё нужно реализовать функцию-обёртку возвращающую соответствующие значения при таких вызовах:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const a = getWrap(add, 1)
console.log(a(2)(4));     // 8
console.log(a(1)(2)(3));  // 9

const b = getWrap(add);
console.log(b(5)(5, 5));  // 15
console.log(b(5, 5)(5));  // 15&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Одним из условий является использование каррирования.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D1%80%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"&gt;Карринг&lt;/a&gt; (currying) или каррирование — термин функционального программирования, который означает создание новой функции путём фиксирования аргументов существующей.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;В этом случае функция getWrap является каррирующей функцией, принимающая в качестве аргументов каррируемую функцию и значение, на которое необходимо заменить первый аргумент каррируемой функции (заменять можно сколь угодно много аргументов, но в этом случае я заменяю только первый), причём нужно учесть, что аргумент может отсутствовать.&lt;/p&gt;
&lt;p&gt;Промежуточное значение вычислений будет храниться в переменной count, а каррированная функция задаваться следующим образом:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const curried = val ? fn.bind(null, val) : fn;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Если требуется зафиксировать переменную, то каррируем переменную с помощью метода bind(), в который в качестве контекста вызова передаём null, так как в данном случае он нам не важен, а следующим аргументом — фиксируемый аргумент.&lt;br /&gt;
Если фиксировать переменную не требуется, то каррированная и каррируемая функции будут одинаковы.&lt;/p&gt;
&lt;p&gt;Результатом выполнения каррированной функции будет функция, сохраняющая вычисления в переменную count и возвращающая саму себя, так же следует учесть количество аргументов, передаваемых в эту функцию, так как результатом вызова функции add с аргументами, хотя бы один из которых не задан (является undefined) будет NaN:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;const func = function (...par) {
  const params = [...par];
  for (let i = 0; i &amp;lt; curried.length; i++) {
    params[i] = params[i] || 0;
  }
  count += curried(...params);
  return func;
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Когда в функцию больше не передаётся параметров, нужно возвращать результат, это задачу решим с помощью метода объектов valueOf() (функция является объектом), а так же обнулять сохранённое значение результата, чтобы при следующем вызове вычисления были корректны:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;func.valueOf = function () {
  const result = count;
  count = 0;
  return result;
};&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;В целом моё решение задачи имеет следующий вид:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;function getWrap(fn, val = 0) {
  let count = 0;
  const curried = val ? fn.bind(null, val) : fn;

  const func = function (...par) {
    const params = [...par];
    for (let i = 0; i &amp;lt; curried.length; i++) {
      params[i] = params[i] || 0;
    }
    count += curried(...params);
    return func;
  };

  func.valueOf = function () {
    const result = count;
    count = 0;
    return result;
  };

  return func;
}&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>React Higher-Order Components</title>
<guid isPermaLink="false">14</guid>
<link>https://ifedyukin.ru/blog/all/react-hoc/</link>
<pubDate>Mon, 24 Jul 2017 17:08:24 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/react-hoc/</comments>
<description>
&lt;p&gt;Несколько расстраивает, что сюда ничего не пишу, так что буду вкидывать какие-то заметки и кусочки кода.&lt;br /&gt;
Начнём с простого и понятного примера &lt;a href="https://facebook.github.io/react/docs/higher-order-components.html"&gt;Higher-Order Components&lt;/a&gt; в React.&lt;/p&gt;
&lt;h3&gt;Enhance.jsx&lt;/h3&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;import { Component } from &amp;quot;React&amp;quot;;

export var Enhance = ComposedComponent =&amp;gt; class extends Component {
  constructor() {
    this.state = { data: null };
  }
  componentDidMount() {
    this.setState({ data: 'Hello, World!!!' });
  }
  render() {
    return &amp;lt;ComposedComponent {...this.props} data={this.state.data} /&amp;gt;;
  }
};&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;HigherOrderComponent.jsx&lt;/h3&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;import { Enhance } from &amp;quot;./Enhance&amp;quot;;

class MyComponent {
  render() {
    if (!this.data) return &amp;lt;div&amp;gt;React loading...&amp;lt;/div&amp;gt;;
    return &amp;lt;div&amp;gt;{this.data}&amp;lt;/div&amp;gt;;
  }
}

export default Enhance(MyComponent);&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Запуск Karma в Chrome — Travis-CI</title>
<guid isPermaLink="false">13</guid>
<link>https://ifedyukin.ru/blog/all/travis-ci-chrome/</link>
<pubDate>Mon, 27 Mar 2017 17:05:37 +0300</pubDate>
<author></author>
<comments>https://ifedyukin.ru/blog/all/travis-ci-chrome/</comments>
<description>
&lt;p&gt;Если вы тестируете свой код и используете GitHub, то скорее всего вы уже используете Travis, однако, при тестировании JavaScript в Travis может возникнуть проблема — Travis не умеет запускать тесты в Chrome, но преодолеть её можно всего несколькими строчками config-файлов.&lt;/p&gt;
&lt;p&gt;Для начала нам нужно настроить сам travis, в моём случае `.travis.yml` выглядит следующим образом:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;language: node_js
node_js:
- '6.2'
before_install:
- export CHROME_BIN=chromium-browser
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- sudo apt-get update
- sudo apt-get install -y libappindicator1 fonts-liberation
- wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
- sudo dpkg -i google-chrome*.deb
script:
- npm install
- npm test&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Внимание здесь стоит обратить на блок `before_install`.&lt;/p&gt;
&lt;p&gt;Для Karma тоже нужно несколько изменить файл настроек — создадим лаунчер, который будет использоваться только в окружении Travis:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;module.exports = function (config) {
    var configuration = {
        // configs
        
        browsers: ['Chrome'],
        customLaunchers: {
            Chrome_travis_ci: {
                base: 'Chrome',
                flags: ['--no-sandbox']
            }
        },
      
        // configs
    };

    if (process.env.TRAVIS) {
        configuration.browsers = ['Chrome_travis_ci'];
    }

    config.set(configuration);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;На этом процедура настройки закончена — пушим изменения на Github и можем наблюдать процесс тестирования в “консоли” Travis соответствующего репозитория.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://ifedyukin.ru/blog/pictures/travisLog.png" width="800" height="367" alt="" /&gt;
&lt;/div&gt;
</description>
</item>


</channel>
</rss>