<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><description>Articles from the &quot;Development&quot; feed</description><atom:link href="https://faiwer.ru/feed/rss/1" rel="self" type="application/rss+xml"></atom:link><link>https://faiwer.ru/it</link><title>Development. Zubashev Stepan's blog</title><language>en</language><item><title>Why do I hate 2FA?</title><pubDate>Sat, 17 Jan 2026 17:51:00 +0000</pubDate><link>https://faiwer.ru/content/214-why-do-i-hate-2fa</link><guid isPermaLink="false">article-214</guid><description><![CDATA[<p>Because it’s too tightly bound to a mobile device. It seems I’ve lost access to one of my email accounts for an indefinite amount of time. I’ll skip the name of the service, it’s not very important. Today I tried to log in and failed because of their 2FA.</p>
<ul>
<li>Their mobile app somehow lost my account. That sucks.</li>
<li>I still know the password, I can’t log in without going through a recovery process.</li>
<li>Their recovery process requires access to my old SIM card.</li>
<li>Luckily I have it and got the SMS</li>
<li>Then it asked for a PIN. I don’t have one. I assume they introduced PINs after I set up my 2FA account. Fine, I can skip this step.</li>
<li>Next, I had to answer a default security question like “What’s the name of the street …”. I know the answer, but I don’t know whether it’s case-sensitive.</li>
<li>Anyway,The system refuses to accept any of my attempts.</li>
<li>Now I have to go through a long process of convincing their security team that it’s actually me.</li>
<li>I’m not sure it’ll work out. I hope as the last hope I’ll be able to send them my passport data</li>
</ul>
<p>So, why do I hate 2FA? Because it’s supposed to be a security measure. But instead it’s almost always a suffering measure.</p>
]]></description></item><item><title>Lazy &amp; Suspense in React</title><pubDate>Fri, 16 Jan 2026 10:35:00 +0000</pubDate><link>https://faiwer.ru/content/213-lazy-suspense-in-react</link><guid isPermaLink="false">article-213</guid><description><![CDATA[<p>React documentation says:</p>
<blockquote>
<p>Now that your component’s code loads on demand, you also <strong>need to</strong> specify what should be displayed while it is loading. You can do this by wrapping the lazy component or any of its parents into a boundary</p>
<ul>
<li><a target="_blank" rel="noopener noreferrer" href="https://react.dev/reference/react/lazy">Lazy - React</a></li>
</ul>
</blockquote>
<p>“Need to”? Or “must to”?</p>
<p>If you try to use <code>lazy</code> without <code>&lt;Suspense/&gt;</code> it… It works. Wow. So, is it optional?</p>
<p><strong>HELL NO</strong>! You must wrap your lazy components with <code>&lt;Suspense/&gt;</code>.</p>
<p>Why? Because such non-connected components can put React’s reconciliation engine into a semi-broken, hung state where behavior becomes unpredictable. It gets auto-healed only when the lazy promise is fulfilled.</p>
<p>So, if you don’t want to play hide &amp; seek in a mine field — do it. Wrap it with <code>&lt;Suspense/&gt;</code> even if you don’t plan to render any fallback JSX.</p>
]]></description></item><item><title>Math namespace in HTML</title><pubDate>Mon, 24 Nov 2025 21:48:00 +0000</pubDate><link>https://faiwer.ru/content/212-math-namespace-in-html</link><guid isPermaLink="false">article-212</guid><description><![CDATA[<p>TIL: HTML partially supports math:</p>
<!-- HTML --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">math</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">  &lt;</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">    &lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;a&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">    &lt;</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;=&lt;/</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">    &lt;</span><span style="color:#f2777a;">mfrac</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">      &lt;</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">        &lt;</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;-&lt;/</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">        &lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;b&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">        &lt;</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;±&lt;/</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">        &lt;</span><span style="color:#f2777a;">msqrt</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">          &lt;</span><span style="color:#f2777a;">msup</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;b&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;2&lt;/</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;&lt;/</span><span style="color:#f2777a;">msup</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">          &lt;</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;-&lt;/</span><span style="color:#f2777a;">mo</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">          &lt;</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;4&lt;/</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;a&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;c&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">        &lt;/</span><span style="color:#f2777a;">msqrt</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">      &lt;/</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">      &lt;</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;2&lt;/</span><span style="color:#f2777a;">mn</span><span style="color:#d3d0c8;">&gt;&lt;</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;a&lt;/</span><span style="color:#f2777a;">mi</span><span style="color:#d3d0c8;">&gt;&lt;/</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">    &lt;/</span><span style="color:#f2777a;">mfrac</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">  &lt;/</span><span style="color:#f2777a;">mrow</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">&lt;/</span><span style="color:#f2777a;">math</span><span style="color:#d3d0c8;">&gt;
</span></pre>
<p>^ It’s a valid HTML code. It renders this way:</p>
<p><img src="https://faiwer.ru/static/upload/images/004.webp" alt=""></p>
<p>No 3rd party libs needed.</p>
]]></description></item><item><title>Parametrized var-functions</title><pubDate>Tue, 11 Nov 2025 18:49:00 +0000</pubDate><link>https://faiwer.ru/content/211-parametrized-var-functions</link><guid isPermaLink="false">article-211</guid><description><![CDATA[<!-- TypeScriptReact --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">type </span><span style="color:#d3d0c8;">Fn = &lt;T&gt;(</span><span style="color:#f2777a;">a</span><span style="color:#d3d0c8;">: T) </span><span style="color:#cc99cc;">=&gt; </span><span style="color:#d3d0c8;">T;
</span><span style="color:#cc99cc;">declare const </span><span style="color:#f2777a;">fn</span><span style="color:#d3d0c8;">: Fn;
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">a </span><span style="color:#d3d0c8;">= </span><span style="color:#f2777a;">fn</span><span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">number</span><span style="color:#d3d0c8;">&gt;; </span><span style="color:#747369;">// no &quot;()&quot;
</span><span style="color:#f2777a;">a</span><span style="color:#d3d0c8;">; </span><span style="color:#747369;">// const a: (a: number) =&gt; number
</span></pre>
<p>It was on the surface all along, but it eluded me.</p>
]]></description></item><item><title>Pure functions</title><pubDate>Thu, 06 Nov 2025 01:38:00 +0000</pubDate><link>https://faiwer.ru/content/210-pure-functions</link><guid isPermaLink="false">article-210</guid><description><![CDATA[<p>A little rant about functional programming and pure functions. There is a common delusion about pure functions. People often think that:</p>
<ul>
<li>Pure functions cannot use variables. Only constants.</li>
<li>Pure functions cannot run impure functions. Only pure.</li>
</ul>
<p>These statements make some point. But they are not entirely true. Let me clarify this. The only restrictions that pure functions have are:</p>
<ul>
<li>It must be deterministic, meaning it doesn’t have random behavior. No matter how many times you run it with the same set of arguments — you’ll get the same result.</li>
<li>It shouldn’t have any side effects.</li>
</ul>
<p>What is a side effect? Basically, it’s any change in the world outside of the function. E.g.:</p>
<ul>
<li>Changing the given argument</li>
<li>Changing a variable defined outside of the function</li>
<li>Mutating any value reachable from outside the function (including parameters) or any value that escapes the function</li>
<li>Making a query (database, network, etc.)</li>
<li>Reading a file, writing to a file, removing a file, etc.</li>
<li>Reading external mutable data</li>
<li>etc.</li>
</ul>
<p>I think you got the concept. Now let’s focus on the word “side” in “side effects”. “Side” means it’s outside of the given function. We don’t have these restrictions for internal state. Yep, a pure function may have mutable internal state.</p>
<!-- TypeScriptReact --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">function </span><span style="color:#6699cc;">pure</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">a</span><span style="color:#d3d0c8;">: number, </span><span style="color:#f2777a;">b</span><span style="color:#d3d0c8;">: number): number {
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">let </span><span style="color:#f2777a;">c </span><span style="color:#d3d0c8;">= </span><span style="color:#f2777a;">a </span><span style="color:#d3d0c8;">** </span><span style="color:#f2777a;">b</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">c </span><span style="color:#d3d0c8;">&gt; </span><span style="color:#f99157;">100</span><span style="color:#d3d0c8;">) </span><span style="color:#f2777a;">c </span><span style="color:#d3d0c8;">%= </span><span style="color:#f99157;">100</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">return </span><span style="color:#f2777a;">c </span><span style="color:#d3d0c8;">- </span><span style="color:#f99157;">14</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">}
</span></pre>
<p>^ this one is pure, even though it mutates <code>c</code>. Just because <code>c</code> is internal to the function — it doesn’t affect or depend on anything outside. We don’t mutate it after we return the value.</p>
<p>Ok, what about running impure functions? It’s a dangerous zone. Because it is very dependent on how exactly impure the impure function. If it’s not deterministic, then we have no chance. So the question is about: what side effects an impure function may have while still allowing us to use it inside a pure function? The answer is: the only side effect it may have is mutating the given arguments (only if they are local to the pure function):</p>
<!-- TypeScriptReact --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">function </span><span style="color:#6699cc;">impure</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">: Packet): void {
</span><span style="color:#d3d0c8;">  </span><span style="color:#747369;">// deterministic and based only on packet
</span><span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">.hash = </span><span style="color:#6699cc;">computeHash</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">); 
</span><span style="color:#d3d0c8;">}
</span><span style="color:#d3d0c8;">
</span><span style="color:#cc99cc;">function </span><span style="color:#6699cc;">pure</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">id</span><span style="color:#d3d0c8;">: number, </span><span style="color:#f2777a;">data</span><span style="color:#d3d0c8;">: Data): Packet {
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">: Packet = { </span><span style="color:#f2777a;">id</span><span style="color:#d3d0c8;">, </span><span style="color:#f2777a;">data </span><span style="color:#d3d0c8;">};
</span><span style="color:#d3d0c8;">  </span><span style="color:#6699cc;">impure</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">return </span><span style="color:#f2777a;">packet</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">}
</span></pre>
<p>Here we clearly run <code>impure</code> in <code>pure</code>. But since <code>impure</code> changes only <code>packet</code>, which is entirely managed by <code>pure</code>, we’re safe.</p>
<p>That’s it.</p>
]]><p><a link="https://faiwer.ru/content/210-pure-functions">Read more</a></p></description></item><item><title>TIL: HTMLCollection is alive</title><pubDate>Sun, 19 Oct 2025 14:52:00 +0000</pubDate><link>https://faiwer.ru/content/209-til-htmlcollection-is-alive</link><guid isPermaLink="false">article-209</guid><description><![CDATA[<p>TIL: <code>HTMLCollection</code>s are live-collections. What does it mean? Look. Imagine you have HTML like this:</p>
<!-- HTML --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">div</span><span style="color:#d3d0c8;">&gt;
</span><span style="color:#d3d0c8;">  &lt;</span><span style="color:#f2777a;">br</span><span style="color:#d3d0c8;">/&gt;
</span><span style="color:#d3d0c8;">  &lt;</span><span style="color:#f2777a;">br</span><span style="color:#d3d0c8;">/&gt;
</span><span style="color:#d3d0c8;">&lt;/</span><span style="color:#f2777a;">div</span><span style="color:#d3d0c8;">&gt;
</span></pre>
<p>Then you want to find all its <code>&lt;br/&gt;</code>s:</p>
<!-- TypeScriptReact --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">const </span><span style="color:#f2777a;">collection  </span><span style="color:#d3d0c8;">= </span><span style="color:#f2777a;">$0</span><span style="color:#d3d0c8;">.</span><span style="color:#66cccc;">getElementsByTagName</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">br</span><span style="color:#d3d0c8;">&#39;);
</span></pre>
<p><img src="https://faiwer.ru/static/upload/images/002.webp" alt=""></p>
<p>Thus far, everything is obvious, right? Ok, let’s add one more <code>&lt;br/&gt;</code>:</p>
<!-- TypeScriptReact --><pre style="background-color:#2d2d2d;">
<span style="color:#f2777a;">$0</span><span style="color:#d3d0c8;">.</span><span style="color:#66cccc;">append</span><span style="color:#d3d0c8;">(document.</span><span style="color:#66cccc;">createElement</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">br</span><span style="color:#d3d0c8;">&#39;));
</span></pre>
<p>So, now have three of them. Let’s read the same <code>collection</code> element again:
<img src="https://faiwer.ru/static/upload/images/003.webp" alt=""></p>
<p>Whoa… It was updated too. Why? <code>HTMLCollection</code> is a “live” collection:</p>
<blockquote>
<p>An HTMLCollection in the HTML DOM is live; it is automatically updated when the underlying document is changed. For this reason it is a good idea to make a copy (e.g., using Array.from) to iterate over if adding, moving, or removing nodes.</p>
<ul>
<li><a target="_blank" rel="noopener noreferrer" href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection">Mozilla</a></li>
</ul>
</blockquote>
]]></description></item><item><title>CSS color filters &amp; black</title><pubDate>Mon, 29 Sep 2025 16:05:09 +0000</pubDate><link>https://faiwer.ru/content/208-css-color-filters-black</link><guid isPermaLink="false">article-208</guid><description><![CDATA[<p>I ran into an issue. Code like this:</p>
<!-- CSS --><pre style="background-color:#2d2d2d;">
<span style="color:#6699cc;">.</span><span style="color:#f99157;">link</span><span style="color:#6699cc;">:</span><span style="color:#cc99cc;">hover </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">  filter: </span><span style="color:#66cccc;">brightness</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">1.5</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">}
</span></pre>
<p>… worked perfectly on a colored icon but had no effect on a text label. It turned out that the label used the color <code>#000</code>. <code>brightness()</code> is a purely mathematical function that multiplies the R, G, and B values by the given factor. This makes no difference for pure black, since <code>0 * anything = 0</code>.</p>
]]></description></item><item><title>Evict password managers</title><pubDate>Sun, 28 Sep 2025 06:11:42 +0000</pubDate><link>https://faiwer.ru/content/207-evict-password-managers</link><guid isPermaLink="false">article-207</guid><description><![CDATA[<p>There are multiple password managers like 1pass, KeePass, and LastPass. Most of them have browser extensions. They are great. But they alter the page behavior, including HTML. Sometimes we need to prevent them from doing that.</p>
<p>Many years ago, people just did this:</p>
<!-- HTML --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">input </span><span style="color:#f99157;">autocomplete</span><span style="color:#d3d0c8;">=&quot;</span><span style="color:#99cc99;">off</span><span style="color:#d3d0c8;">&quot;
</span></pre>
<p>Now it doesn’t work. These extensions don’t respect this attribute. You might have seen this one:</p>
<!-- HTML --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">input </span><span style="color:#f99157;">data-lpignore</span><span style="color:#d3d0c8;">=&quot;</span><span style="color:#99cc99;">true</span><span style="color:#d3d0c8;">&quot;
</span></pre>
<p>It doesn’t work either (at least not with KeePass)</p>
<p>I came up with a simple and pretty silly solution:</p>
<!-- HTML --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&lt;</span><span style="color:#f2777a;">input </span><span style="color:#f99157;">type</span><span style="color:#d3d0c8;">=&quot;</span><span style="color:#99cc99;">text</span><span style="color:#d3d0c8;">&quot; </span><span style="color:#f99157;">onfocus</span><span style="color:#d3d0c8;">=&quot;</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.target.type=&#39;</span><span style="color:#99cc99;">password</span><span style="color:#d3d0c8;">&#39;&quot;
</span></pre>
<p>And it works ;-) The extensions don’t care about text-inputs. And they are not smart enough to detect new controls. At least for now.</p>
<p><strong>upd</strong>: It helped with <code>KeePass</code>, but didn’t work out with <code>1Password</code>.</p>
]]></description></item><item><title>New blog</title><pubDate>Fri, 26 Sep 2025 09:36:44 +0000</pubDate><link>https://faiwer.ru/content/198-new-blog</link><guid isPermaLink="false">article-198</guid><description><![CDATA[<h2>The idea</h2>
<p>In June, I started learning <code>Rust</code>. Why? Just out of curiosity. A system-level language with a lot of syntax sugar, new patterns, and approaches. So, I started with the Rust Book, got through half of it, and decided to build something real.</p>
<p>My previous blog was made with <code>PHP 5.5</code> more than ~8 years ago. It wasn’t bad, but I completely quit PHP. I didn’t want to touch that old codebase. So I came to the conclusion: why not write a new one, this time in Rust?</p>
<p>Here it is. You’re welcome ;)</p>
<p><img src="https://faiwer.ru/static/upload/images/001.jpg" alt=""></p>
<h2>AI</h2>
<p>I didn’t want to vibe-code it. However, I didn’t want to face all the obstacles by myself. So I used <code>ChatGPT</code> a lot, for almost every aspect of the development. I turned off <code>Copilot</code> and didn’t use <code>Cursor</code>. Because I wanted to write the blog “myself”. To feel it. At least in some merit. GTP was always in a separate browser tab. More like a helper, not an author.</p>
<h2>Rust. First impression</h2>
<p>Rust is wonderful, but it’s a system-level language. I had no real experience in this area before. Just a few simple <code>C++</code> <code>LeetCode</code> problems, which gave me no real <code>C++</code> knowledge. So it was a real challenge. But I wasn’t completely unarmed. I still remembered something about pointers, the heap, allocation, and other important system-level stuff. Though it was purely theoretical in my mind.</p>
]]><p><a link="https://faiwer.ru/content/198-new-blog">Read more</a></p></description></item><item><title>&quot;Native code&quot; label</title><pubDate>Thu, 31 Jul 2025 01:14:00 +0000</pubDate><link>https://faiwer.ru/content/181-native-code-label</link><guid isPermaLink="false">article-181</guid><description><![CDATA[<p>How to detect if a function is a core part of browser API? This way:</p>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">a </span><span style="color:#d3d0c8;">= () </span><span style="color:#cc99cc;">=&gt; </span><span style="color:#d3d0c8;">{};
</span><span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">a</span><span style="color:#d3d0c8;">.</span><span style="color:#66cccc;">toString</span><span style="color:#d3d0c8;">(); </span><span style="color:#747369;">// () =&gt; {}
</span><span style="color:#d3d0c8;">  console.</span><span style="color:#66cccc;">log</span><span style="color:#d3d0c8;">.</span><span style="color:#66cccc;">toString</span><span style="color:#d3d0c8;">(); </span><span style="color:#747369;">// function log() { [native code] }
</span></pre>
<p>You see? <code>[native code]</code> label. Sounds good, right?</p>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">a </span><span style="color:#d3d0c8;">= () </span><span style="color:#cc99cc;">=&gt; </span><span style="color:#d3d0c8;">{};
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">b </span><span style="color:#d3d0c8;">= </span><span style="color:#f2777a;">a</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">bind</span><span style="color:#d3d0c8;">(</span><span style="color:#f99157;">null</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">b</span><span style="color:#d3d0c8;">.</span><span style="color:#66cccc;">toString</span><span style="color:#d3d0c8;">(); </span><span style="color:#747369;">// function () { [native code] }
</span></pre>
<p>Oops. But we still have this <code>log</code> name in the string snapshot.</p>
]]></description></item><item><title>Babel to EsBuild migration for Webpack</title><pubDate>Sun, 09 Feb 2025 13:00:00 +0000</pubDate><link>https://faiwer.ru/content/180-babel-to-esbuild-migration-for-webpack</link><guid isPermaLink="false">article-180</guid><description><![CDATA[<p>Recently I had some time to take a look at the performance of my pet project. One thing bothered me: the project is relatively small, but the bundle size was too big: <code>780 KiB</code>. Whereas the project had only a few dependencies: <code>react</code> (~10 KiB), <code>react-router</code> (~70 KiB), <code>react-dom</code> (~90 KiB), <code>dayjs</code> (~3 KiB), <code>lodash</code> (~70 KiB). The bundle should not be that big for so few dependencies.</p>
<p>So I ran <code>webpack-dev-analyzer</code> and found a weird thing: the sum of the libs is 3 times smaller than the final bundle size. Then I decided to take a look at <code>@statoscope/webpack-plugin</code>. It was a good tool but it gave me the same result. The bundle has way too much trash.</p>
<p>Ok. Probably it’s just webpack wrappers over 300+ small modules. %cut% So I asked <code>Perplexity</code> - what can I do with it? Turned out all the proper options were already activated. So, I made a decision - it’s time to try esbuild. It made a huge impact on my work project, so I knew it could work out.</p>
<p>I knew that <code>esbuild</code> didn’t support a lot of stuff that babel supports. So I expected that it would take a day to migrate the codebase to <code>esbuild</code>. But surprisingly once I replaced <code>babel-loader</code> with <code>esbuild-loader</code> the build command worked out. The build didn’t work but the bundle was assembled without any errors.</p>
<p>Did it help? Nope. Instead of <code>780 KiB</code>, I got <code>~1 MiB</code>. Luckily I found out that there’s also <code>EsbuildPlugin</code>, which replaces <code>TerserPlugin</code> and works better. Added this line, it worked like a charm, and… Whoa! <code>280 KiB</code>. That’s huuuge.</p>
<p>So I figured out - it worked. Now I have to find a way to fix the app (spoiler: it didn’t work at all).</p>
<h3>Implicit React imports</h3>
<p>React 17+ supports the possibility of avoiding adding <code>import React from ‘react’</code> to every TSX-file. <code>esbuild</code> supports it too, but somehow it didn’t work out of the box. Solution:</p>
<!-- Git Diff --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">  const esbuildLoader: RuleSetRule = {
</span><span style="color:#d3d0c8;">    test: /\.(js|ts|tsx)$/,
</span><span style="color:#d3d0c8;">    loader: &#39;esbuild-loader&#39;,
</span><span style="color:#d3d0c8;">    exclude: /node_modules/,
</span><span style="color:#d3d0c8;">    options: {
</span><span style="color:#d3d0c8;">       target: &#39;es2020&#39;,
</span><span style="color:#d3d0c8;">       minify: cfg.PROD,
</span><span style="color:#99cc99;">+      // Support implicit React imports:
</span><span style="color:#99cc99;">+      jsx: &#39;automatic&#39;,
</span><span style="color:#99cc99;">+      jsxFactory: &#39;React.createElement&#39;,
</span><span style="color:#99cc99;">+      jsxImportSource: &#39;react&#39;,
</span><span style="color:#d3d0c8;">    } satisfies LoaderOptions,
</span><span style="color:#d3d0c8;">  };
</span></pre>
<h3>TSX-Control-Statements</h3>
<p>I just removed the packages and all their <code>&lt;If/&gt;</code>s. I liked them though. There’s a plugin for <code>Vite</code> that adds their support. But I still use <code>Webpack</code> and the plugin works via string replacements, not via AST.</p>
<h3>SourceMaps</h3>
<p>To enable sourcemaps I had to disable <code>EsbuildPlugin</code> for development. Theoretically, it supports sourcemaps, but no matter what I did it didn’t work. I even debugged the package itself. Didn’t help. So I surrendered and enabled it only for the prod build.</p>
<h3>TypeScript</h3>
<p>EsBuild supports TypeScript syntax out of the box but it doesn’t check types. So it builds extremely fast but I need to know my errors. Solution:</p>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">webpackConfig</span><span style="color:#d3d0c8;">.plugins!.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(
</span><span style="color:#d3d0c8;">    new </span><span style="color:#f2777a;">ForkTsCheckerWebpackPlugin</span><span style="color:#d3d0c8;">({
</span><span style="color:#d3d0c8;">      typescript: {
</span><span style="color:#d3d0c8;">        configFile: </span><span style="color:#f2777a;">cfg</span><span style="color:#d3d0c8;">.paths.TS_CONFIG,
</span><span style="color:#d3d0c8;">      },
</span><span style="color:#d3d0c8;">    })
</span><span style="color:#d3d0c8;">  );
</span></pre>
<p>How does it work? The build and the type-checking processes are run in parallel. The 2nd doesn’t linger the 1st. You still can see the errors in the browser (and CLI) console. And visually too. Nice.</p>
<h3>ES-Lint</h3>
<p>For some reason, it stopped working. The solution was pretty simple:</p>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">webpackConfig</span><span style="color:#d3d0c8;">.plugins!.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(
</span><span style="color:#d3d0c8;">    new </span><span style="color:#f2777a;">ESLintPlugin</span><span style="color:#d3d0c8;">({
</span><span style="color:#d3d0c8;">      extensions: [&#39;</span><span style="color:#99cc99;">ts</span><span style="color:#d3d0c8;">&#39;, &#39;</span><span style="color:#99cc99;">tsx</span><span style="color:#d3d0c8;">&#39;],
</span></pre>
<h3>Final results</h3>
<ul>
<li>File sizes
<ul>
<li>Before: 641 KiB + 234 KiB + 29.5 KiB (css) = <code>904.5 KiB</code></li>
<li>After: 221 KiB + 55 KiB + 8.1 KiB (o_O, css) = <code>284.1 KiB</code></li>
<li>After <code>minifyIdentifiers: true</code> (scary thing) &amp; <code>minifyWhitespace: true</code>:  138 KiB + 44.2 KiB + 8.1 KiB = <code>190.1 KiB</code></li>
</ul>
</li>
<li>DOMContentLoaded:
<ul>
<li>Before: <code>737ms</code></li>
<li>After: <code>231ms</code></li>
<li>After-2: <code>131ms</code></li>
</ul>
</li>
<li>Build time: <code>esbuild</code> was ~3x faster</li>
</ul>
<h3>Afterthought</h3>
<p>Taking a deeper look I found:</p>
<ul>
<li>The main issue wasn’t in <code>babel</code>. The main issue was that I used <code>inline-source-map</code> for prod. I didn’t want to do it. Just a mistake. And both bundle analyzers didn’t notice that. Even when I manually looked at the file I barely found it (the horisontal scroll was too big). Fixing this issue dropped the file size 3x times even with <code>babel</code>.</li>
<li><code>esbuild</code> has a better wrapper logic for a ton of tiny files than <code>babel</code> does.</li>
<li>Whitespace minification is quite important. <code>GZip</code> doesn’t eliminate the difference.</li>
<li>Name mangling is a beast. But be careful, it can easily break your app in runtime.</li>
</ul>
]]><p><a link="https://faiwer.ru/content/180-babel-to-esbuild-migration-for-webpack">Read more</a></p></description></item><item><title>How to migrate Javascript webpack configuration to Typescript</title><pubDate>Sat, 25 Mar 2023 14:00:00 +0000</pubDate><link>https://faiwer.ru/content/179-how-to-migrate-javascript-webpack-configuration-to-typescript</link><guid isPermaLink="false">article-179</guid><description><![CDATA[<p>It turned out that the newest versions (at least 5+) of webpack support Typescript out of the box. So the algorithm is next:</p>
<ul>
<li>Create <code>tsconfig.json</code> at the root level. Content:<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">  &quot;</span><span style="color:#99cc99;">compilerOptions</span><span style="color:#d3d0c8;">&quot;: {
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">module</span><span style="color:#d3d0c8;">&quot;: &quot;</span><span style="color:#99cc99;">CommonJS</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">target</span><span style="color:#d3d0c8;">&quot;: &quot;</span><span style="color:#99cc99;">ES5</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">esModuleInterop</span><span style="color:#d3d0c8;">&quot;: </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">checkJs</span><span style="color:#d3d0c8;">&quot;: </span><span style="color:#f99157;">false</span><span style="color:#d3d0c8;">,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">strict</span><span style="color:#d3d0c8;">&quot;: </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">,
</span><span style="color:#d3d0c8;">  }
</span><span style="color:#d3d0c8;">}
</span></pre>
</li>
<li>Rename all *<code>.js</code> files to <code>*.ts</code></li>
<li>Type all of them:
<ul>
<li>no more ugly <code>require</code>, use <code>import</code>.</li>
<li><code>webpack</code> package has typings out of the box.
<ul>
<li>You may find these types useful: <code>Configuration</code> &amp; <code>RuleSetRule</code></li>
<li>To enable <code>devServer</code> write this: <!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#f2777a;">interface </span><span style="color:#f99157;">Configuration </span><span style="color:#f2777a;">extends </span><span style="color:#f99157;">WebpackConfiguration </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">  </span><span style="color:#f2777a;">devServer</span><span style="color:#d3d0c8;">?: </span><span style="color:#f99157;">WebpackDevServerConfiguration</span><span style="color:#d3d0c8;">;
</span><span style="color:#d3d0c8;">}
</span></pre>
</li>
</ul>
</li>
<li>Some of the popular plugins have types too.</li>
<li>Some of them don’t have types at all:
<ul>
<li>Create a <code>*.d.ts</code> file</li>
<li>Put there something like this:<!-- TypeScript --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">declare module </span><span style="color:#d3d0c8;">&#39;</span><span style="color:#99cc99;">postcss-assets</span><span style="color:#d3d0c8;">&#39; {
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">export default function </span><span style="color:#6699cc;">postcssAssets</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">opts</span><span style="color:#d3d0c8;">: {
</span><span style="color:#d3d0c8;">    </span><span style="color:#f2777a;">basePath</span><span style="color:#d3d0c8;">: string;
</span><span style="color:#d3d0c8;">    </span><span style="color:#f2777a;">relative</span><span style="color:#d3d0c8;">: boolean;
</span><span style="color:#d3d0c8;">  }): unknown;
</span><span style="color:#d3d0c8;">}
</span></pre>
</li>
</ul>
</li>
</ul>
</li>
<li>Make sure your <code>webpack.config.ts</code> file is placed at the root level. I mean exactly at the same spot where <code>node_modules</code> is. Otherwise, you won’t be able to build it. No <code>compilerOptions</code> helped me. </li>
<li>Run webpack. It should work.</li>
</ul>
]]></description></item><item><title>How to make a simple system.d service for a node.js server</title><pubDate>Tue, 21 Feb 2023 17:00:00 +0000</pubDate><link>https://faiwer.ru/content/178-how-to-make-a-simple-system-d-service-for-a-node-js-server</link><guid isPermaLink="false">article-178</guid><description><![CDATA[<p>Put this content:</p>
<!-- INI --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">[Unit]
</span><span style="color:#cc99cc;">Description</span><span style="color:#d3d0c8;">=</span><span style="color:#99cc99;">{name}
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">[Service]
</span><span style="color:#cc99cc;">Type</span><span style="color:#d3d0c8;">=</span><span style="color:#99cc99;">simple
</span><span style="color:#cc99cc;">User</span><span style="color:#d3d0c8;">=</span><span style="color:#99cc99;">{user}
</span><span style="color:#cc99cc;">ExecStart</span><span style="color:#d3d0c8;">=</span><span style="color:#99cc99;">/usr/bin/node {full-path-to-script}.js
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">[Install]
</span><span style="color:#cc99cc;">WantedBy</span><span style="color:#d3d0c8;">=</span><span style="color:#99cc99;">multi-user.target
</span></pre>
<p>… somewhere as <code>{name}.service</code>, where:</p>
<ul>
<li><code>{name}</code> is the name of the service</li>
<li><code>{user}</code> is the name of the user to run the script (optional)</li>
</ul>
<p>… then:</p>
<ul>
<li>run: <code>this sudo ln -s /{full_path}/{name}.service /lib/systemd/system/{name}.service</code> </li>
<li>then this: <code>sudo systemctl daemon-reload</code></li>
<li>then this: <code>sudo systemctl enable {name}.service</code></li>
<li>and finally this: <code>sudo systemctl start {name}</code></li>
</ul>
<p>How does it work?</p>
<ul>
<li>It starts the service on boot (see <code>WantedBy</code> section).</li>
<li>Using <code>ExecStart</code> command. Important: we specify the full path to <code>node</code></li>
<li><code>SystemD</code> remembers the <code>PID</code> of the new process and considers the service is ongoing until the process is died.</li>
<li>So any subsequent <code>systemctrl start {name}</code> won’t do anything if the previous process is alive.</li>
<li>This behavior is determined by <code>Type=Simple</code></li>
</ul>
]]></description></item><item><title>Multilayout Keybinds in a Browser</title><pubDate>Wed, 09 Feb 2022 13:00:00 +0000</pubDate><link>https://faiwer.ru/content/177-multilayout-keybinds-in-a-browser</link><guid isPermaLink="false">article-177</guid><description><![CDATA[<p>Imagine that you want to add support of some keybinds in your web application. Let it be <code>Ctrl+L</code> for liking\unliking something. What kind of issues can you face in such a scenario?</p>
<h3>CMD or Ctrl?</h3>
<p>At first look at <code>Ctrl</code>. Probably on <code>MacOS</code> you’d like to replace it with <code>CMD</code>.  You can check it by <code>event.metaKey</code>.</p>
<h3>Extra modificators</h3>
<p>Probably you wouldn’t like to consider <code>Ctrl+Alt+S</code> as <code>Ctrl+S</code>. So don’t forget to handle this case.</p>
<h3>Different layouts</h3>
<p>Not every language that uses the Latin alphabet has the <code>L</code> button at the same position as in a typical English keyboard layout. You need to decide what is more important to you ― a real key position on a keyboard or a letter upon of it. I’d guess that the 2nd case is preferable for most applications. </p>
<p>To get a real key position you can use <code>which</code>, <code>code</code>, <code>codeKey</code> properties. To get a letter on the key use <code>key</code> property.</p>
<h3>Different alphabets</h3>
<p>What’s about Greek or Russian alphabets? Or any other possible alphabets? Or not even alphabets? There’re different strategies. And one of them is to use a key from a typical English keyboard layout. So it leads us again to <code>code</code> and <code>codeKey</code> properties.</p>
<h3>Example</h3>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#cc99cc;">const </span><span style="color:#f2777a;">getEventKeyBind </span><span style="color:#d3d0c8;">= event </span><span style="color:#cc99cc;">=&gt; </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">keybind </span><span style="color:#d3d0c8;">= [];
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.metaKey) </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">cmd</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.ctrlKey) </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">ctrl</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.shiftKey) </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">shift</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.altKey) </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">alt</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.key === &#39; &#39;) </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">space</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">else </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">    </span><span style="color:#cc99cc;">const </span><span style="color:#f2777a;">key </span><span style="color:#d3d0c8;">= </span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.key.</span><span style="color:#f2777a;">toLowerCase</span><span style="color:#d3d0c8;">();
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">    </span><span style="color:#cc99cc;">if </span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">key</span><span style="color:#d3d0c8;">.length !== </span><span style="color:#f99157;">1 </span><span style="color:#d3d0c8;">|| </span><span style="color:#f2777a;">key</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">match</span><span style="color:#d3d0c8;">(/</span><span style="color:#66cccc;">^[a-z]$</span><span style="color:#d3d0c8;">/)) {
</span><span style="color:#d3d0c8;">      </span><span style="color:#747369;">// latin key or a special key
</span><span style="color:#d3d0c8;">      </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">key</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">    } </span><span style="color:#cc99cc;">else </span><span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">      </span><span style="color:#747369;">// extra-latin or non-latin key
</span><span style="color:#d3d0c8;">      </span><span style="color:#cc99cc;">const </span><span style="color:#d3d0c8;">[, </span><span style="color:#f2777a;">enSymbol</span><span style="color:#d3d0c8;">] = </span><span style="color:#f2777a;">event</span><span style="color:#d3d0c8;">.code.</span><span style="color:#f2777a;">match</span><span style="color:#d3d0c8;">(/</span><span style="color:#66cccc;">^Key(\w)$</span><span style="color:#d3d0c8;">/) || [];
</span><span style="color:#d3d0c8;">      </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">push</span><span style="color:#d3d0c8;">(</span><span style="color:#f2777a;">enSymbol </span><span style="color:#d3d0c8;">? </span><span style="color:#f2777a;">enSymbol</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">toLowerCase</span><span style="color:#d3d0c8;">() : </span><span style="color:#f2777a;">key</span><span style="color:#d3d0c8;">);
</span><span style="color:#d3d0c8;">    }
</span><span style="color:#d3d0c8;">  }
</span><span style="color:#d3d0c8;">
</span><span style="color:#d3d0c8;">  </span><span style="color:#cc99cc;">return </span><span style="color:#f2777a;">keybind</span><span style="color:#d3d0c8;">.</span><span style="color:#f2777a;">join</span><span style="color:#d3d0c8;">(&#39;</span><span style="color:#99cc99;">+</span><span style="color:#d3d0c8;">&#39;);
</span><span style="color:#d3d0c8;">};
</span></pre>
]]></description></item><item><title>Custom English-German layout for Ubuntu</title><pubDate>Sun, 24 Nov 2019 16:00:00 +0000</pubDate><link>https://faiwer.ru/content/175-custom-english-german-layout-for-ubuntu</link><guid isPermaLink="false">article-175</guid><description><![CDATA[<p>German language contains some extra latin symbols that English language doesn’t: <code>ä, ö, ü, ß</code>. But if you choose German keyboard layout instead of English you get some keyboard keys moved to unusual positions. If it’s okay for you then you don’t need to do anything with it. Just get used to the new layout! But if you wanna stay with the English version of latin keys positions you need to find some convenient way to type German specific letters.</p>
<p>Under the cut 3 ways to handle it:</p>
<ul>
<li>Compose key</li>
<li>International layout with &quot;dead keys&quot;</li>
<li>Write your own EN-layout</li>
</ul>
]]><p><a link="https://faiwer.ru/content/175-custom-english-german-layout-for-ubuntu">Read more</a></p></description></item><item><title>How to use babel-module-resolver with vsCode and ESLint</title><pubDate>Tue, 04 Dec 2018 05:43:00 +0000</pubDate><link>https://faiwer.ru/content/174-how-to-use-babel-module-resolver-with-vscode-and-eslint</link><guid isPermaLink="false">article-174</guid><description><![CDATA[<p>This article is a small guide about how to configure an enviroment for <code>babel-plugin-module-resolver</code>. This plugin allows you use custom prefixes for <code>import/export</code> declarations in JS files. E.g. <code>import A from ‘com/A’  </code>can be treated as <code>import A from …/…/components/A</code>.</p>
<ul>
<li>At first you should install the package <code>npm i -D babel-plugin-module-resolver</code>. </li>
<li>Then add your <code>map-config</code> to your <code>.babelrc</code> file into the <code>plugins</code> section. E.g.:<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">plugins:
</span><span style="color:#d3d0c8;">[
</span><span style="color:#d3d0c8;">  </span><span style="color:#747369;">// ...
</span><span style="color:#d3d0c8;">  [
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">babel-plugin-module-resolver</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;">    {
</span><span style="color:#d3d0c8;">        &quot;</span><span style="color:#99cc99;">alias</span><span style="color:#d3d0c8;">&quot;: {
</span><span style="color:#d3d0c8;">          &quot;</span><span style="color:#99cc99;">^com(.+)</span><span style="color:#d3d0c8;">&quot;: &quot;</span><span style="color:#99cc99;">./src/com/</span><span style="color:#66cccc;">\\</span><span style="color:#99cc99;">1</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#d3d0c8;">        }
</span><span style="color:#d3d0c8;">    }
</span><span style="color:#d3d0c8;">  ],
</span><span style="color:#d3d0c8;">  </span><span style="color:#747369;">// ...
</span><span style="color:#d3d0c8;">]
</span></pre>
</li>
<li>Then install <code>npm i -D eslint-import-resolver-babel-module</code> package for <code>ESLint</code>. It allows <code>ESLint</code> to check your rewrited imports, It assumes that you already use <code>eslint-plugin-import</code> package. If you don’t I recommend you to start using it. This package checks your <code>import/export</code> declarations, it’s very convinient.</li>
<li>Then change in your <code>.eslint</code> file:<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">  &quot;</span><span style="color:#99cc99;">parser</span><span style="color:#d3d0c8;">&quot;: &quot;</span><span style="color:#99cc99;">babel-eslint</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;">  &quot;</span><span style="color:#99cc99;">parserOptions</span><span style="color:#d3d0c8;">&quot;: {
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">ecmaVersion</span><span style="color:#d3d0c8;">&quot;: </span><span style="color:#f99157;">6</span><span style="color:#d3d0c8;">,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">sourceType</span><span style="color:#d3d0c8;">&quot;: &quot;</span><span style="color:#99cc99;">module</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">ecmaFeatures</span><span style="color:#d3d0c8;">&quot;: { &quot;</span><span style="color:#99cc99;">jsx</span><span style="color:#d3d0c8;">&quot;: </span><span style="color:#f99157;">true </span><span style="color:#d3d0c8;">}
</span><span style="color:#d3d0c8;">  },
</span><span style="color:#d3d0c8;">  &quot;</span><span style="color:#99cc99;">settings</span><span style="color:#d3d0c8;">&quot;: {
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">import/resolver</span><span style="color:#d3d0c8;">&quot;: { &quot;</span><span style="color:#99cc99;">babel-module</span><span style="color:#d3d0c8;">&quot;: {} }
</span><span style="color:#d3d0c8;">  },
</span><span style="color:#d3d0c8;">  &quot;</span><span style="color:#99cc99;">plugins</span><span style="color:#d3d0c8;">&quot;: [
</span><span style="color:#d3d0c8;">    </span><span style="color:#747369;">// ...
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">import</span><span style="color:#d3d0c8;">&quot;
</span><span style="color:#d3d0c8;">  ],
</span><span style="color:#d3d0c8;">  </span><span style="color:#747369;">// ...
</span><span style="color:#d3d0c8;">}
</span></pre>
</li>
<li>You also need to install <code>babel-eslint</code> if you didn’t do it yet. And move its declaration from <code>parseOptions</code> to the root of the <code>.eslintrc</code>.</li>
<li><code>babel-module</code> part in <code>.babelrc</code> section is just <code>{}</code>, it’s okay.</li>
<li>Install <code>ESLint</code> plugin in <code>vsCode</code></li>
<li>Probably you’ll need to write in your <code>.vcode/settings.json</code> something like next lines. They force <code>vscode</code> to change <code>CWD</code> (current directory) in the deamon of <code>eslint plugin</code>:<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">&quot;</span><span style="color:#99cc99;">eslint.workingDirectories</span><span style="color:#d3d0c8;">&quot;: [
</span><span style="color:#d3d0c8;">  {
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">directory</span><span style="color:#d3d0c8;">&quot;: &quot;</span><span style="color:#99cc99;">./client</span><span style="color:#d3d0c8;">&quot;,
</span><span style="color:#d3d0c8;">    &quot;</span><span style="color:#99cc99;">changeProcessCWD</span><span style="color:#d3d0c8;">&quot;: </span><span style="color:#f99157;">true
</span><span style="color:#d3d0c8;">  }
</span><span style="color:#d3d0c8;">],
</span></pre>
</li>
<li>If you use <code>path-intellisense</code> plugin, you should consider configurate your mapping in <code>.vscode/settings.json</code> too. </li>
<li>If you have any troubles, something doesn’t work properly or doesn’t work at all: <code>Use force, Luke</code>. Set vscode setting <code>“eslint.trace.server”: “verbose”</code> and debug, debug, debug. I’ve spent on it several hours :(</li>
</ul>
]]></description></item><item><title>Отладка NodeJS приложений при помощи ndb</title><pubDate>Fri, 03 Aug 2018 08:19:00 +0000</pubDate><link>https://faiwer.ru/content/173-otladka-nodejs-prilozhenij-pri-pomoshhi-ndb</link><guid isPermaLink="false">article-173</guid><description><![CDATA[<p>Пост-заметка об <code>ndb</code>. Всем кто пишет для <code>nodejs</code> периодически приходится отлаживать своё приложение. Да даже тем, кто использует <code>mocha</code> или <code>webpack</code> бывает нет-нет да удобнее отладить по-человечески проблему, нежели тыкать повсюду <code>console.log</code>-и. <code>NodeJS</code> издавна предоставляет нам для этого браузерный инструмент.</p>
<p>Работает оно так:</p>
<ul>
<li>мы запускаем наше приложение из консоли с нужным флагом</li>
<li>NodeJS в консоли нам сообщает ссылку с нужным портом</li>
<li>Которую мы открываем в браузере и видим перед собой копию <code>chrome-dev-tools</code>-ов.</li>
</ul>
<p>Если надо перезагрузить приложение ― повторяем всё с нуля. С одной стороны сами инструменты весьма удобные. С другой стороны вся эта мышиная возня с портами и перезапусками <code>очень</code> неудобна.</p>
<p>И тут на помощь к нам приходит <a target="_blank" rel="noopener noreferrer" href="https://github.com/GoogleChromeLabs/ndb">ndb</a>. Просто перед командой запуска приложения добавляем <code>ndb</code>. <code>Dev-tools</code>-ы открываются прямо в своём отдельном окне. Перезагрузить приложение можно нажав <code>ctrl+R</code>. Все <code>breakpoint</code>-ы и прочая муть при этом сохраняется. </p>
<p>Выглядит это всё примерно так:</p>
<p><img src="https://faiwer.ru/static/upload/old/086.png" alt=""></p>
]]></description></item><item><title>Webpack и SCSS импорты в купе с module-resolver</title><pubDate>Fri, 03 Aug 2018 01:00:00 +0000</pubDate><link>https://faiwer.ru/content/172-webpack-i-scss-importy-v-kupe-s-module-resolver</link><guid isPermaLink="false">article-172</guid><description><![CDATA[<p>С приходоим <code>ES7</code> в <code>JS</code> пришли <code>import</code>-ы и <code>export</code>-ы. Жить стало веселее, и… сложнее. Появилось много заморочек и вообще новых проблем. Одна из таких проблем выглядит так: <code>import some from …/…/…/…/…/some</code>. Знакомая ситуация?</p>
<p>Обычно её в случае <code>webpack</code>-а решают при помощи модуля <a target="_blank" rel="noopener noreferrer" href="https://github.com/tleunen/babel-plugin-module-resolver">module-resolver</a>. Он позволяет в <code>.babelrc </code>(или где вы держите конфигурацию) указывать регулярные выражения для автозамены, а также добавляет поддержку <code>root</code>-записей. Скажем с ним можно указать <code>import actions from ‘@actions/header`’, где </code>@actions`` будет алиасом к какому-нибудь пути в вашем проекте. </p>
<p>Дальше встаёт вопрос инструментов. А как на это должен реагировать редактор? Как он узнает, что теперь пути в <code>import</code>-ах устроены хитрее. Что делать с линтингом? К счастью, для <code>eslint</code> есть такой плагин:  <code>eslint-plugin-import</code>. С редакторами ситуация может быть сложнее.</p>
<p>Хорошо, а что делать с <code>SCSS\Sass</code>? Нормальных рабочих решений с наскоку мне найти не удалось. Но как оказалось, всё можно решить относительно просто. В настройках <code>webpack</code> для <code>sass-loader</code>-а можно указать настройки. Они будут переданы как есть в пакет <code>node-sass</code>. А вот он, оказывается, достаточно гибкий. Если там указать метод <code>importer</code>, то можно навязать свою логику обработки <code>@import</code>-ов. Пример:</p>
<!-- JavaScript --><pre style="background-color:#2d2d2d;">
<span style="color:#d3d0c8;">{
</span><span style="color:#d3d0c8;">	loader: &#39;</span><span style="color:#99cc99;">sass-loader</span><span style="color:#d3d0c8;">&#39;,
</span><span style="color:#d3d0c8;">	</span><span style="color:#f2777a;">options</span><span style="color:#d3d0c8;">:
</span><span style="color:#d3d0c8;">	{
</span><span style="color:#d3d0c8;">		sourceMap: </span><span style="color:#f99157;">true</span><span style="color:#d3d0c8;">,
</span><span style="color:#d3d0c8;">		</span><span style="color:#f2777a;">url</span><span style="color:#d3d0c8;">: </span><span style="color:#f99157;">false</span><span style="color:#d3d0c8;">,
</span><span style="color:#d3d0c8;">		</span><span style="color:#f2777a;">importer</span><span style="color:#d3d0c8;">: url </span><span style="color:#cc99cc;">=&gt; </span><span style="color:#d3d0c8;">(
</span><span style="color:#d3d0c8;">		{
</span><span style="color:#d3d0c8;">			file: </span><span style="color:#f2777a;">url
</span><span style="color:#d3d0c8;">				.</span><span style="color:#f2777a;">replace</span><span style="color:#d3d0c8;">(...)
</span><span style="color:#d3d0c8;">				.</span><span style="color:#f2777a;">replace</span><span style="color:#d3d0c8;">(...)
</span><span style="color:#d3d0c8;">				.</span><span style="color:#f2777a;">replace</span><span style="color:#d3d0c8;">(...),
</span><span style="color:#d3d0c8;">		}),
</span><span style="color:#d3d0c8;">	},
</span><span style="color:#d3d0c8;">}
</span></pre>
<p>Либо даже выцепить ваши настройки прямо из настроек в <code>.babelrc</code>.</p>
]]></description></item><item><title>Google Calendar и спам от Казино в мобильном приложении на Android</title><pubDate>Thu, 19 Jul 2018 08:58:00 +0000</pubDate><link>https://faiwer.ru/content/171-google-calendar-i-spam-ot-kazino-v-mobil-nom-prilozhenii-na-android</link><guid isPermaLink="false">article-171</guid><description><![CDATA[<p>В который раз наткнулся на странную вещь. Телефон издаёт странный нестандартный непривычный звук. Подхожу - смотрю - реклама казино в <code>google-календаре</code>. В этот раз решил разобраться, как так. Стал рыться-копаться. Нет, таких записей в календаре у меня нет. Нет - к календарю ни у какого приложения доступа нет. WTF?</p>
<p>Разгадка - <code>недоработка Gmail</code>. Оказывается что если сформировать письмо правильным образом, то “умный” Google автоматически формирует в календаре Event под это письмо. И часть писем успевают сформировать такие спам-уведомления из календаря ещё до того момента, пока Google не определяет, что это спам и event-ы с письмами сносит. Однако диалог “пойду-не-пойду на мероприятие” на мобильнике при этом не тухнет.</p>
<p>Лечится принудительным отключением в настройках Google Calendar опции: <code>Automatically add events from Gmail to my calendar</code>. Да, она у всех включена по-умолчанию.</p>
]]></description></item><item><title>Embedded webpack application</title><pubDate>Thu, 19 Apr 2018 10:48:00 +0000</pubDate><link>https://faiwer.ru/content/169-embedded-webpack-application</link><guid isPermaLink="false">article-169</guid><description><![CDATA[<p>Возникла необходимость внедрять готовый <code>SPA</code> в другой проект на страницу. Проект сразу писался под это дело, но всё равно возник ряд проблем, которые нужно было решать. Напишу про 2 из них сюда как шпаргалку.</p>
<h3>webpack &amp; dynamic import</h3>
<p>Что если ваш проект использует <code>динамический импорт</code>? Например <code>System.import</code>. В этом случае <code>webpack</code> сделает для подгружаемых файлов <code>отдельный chunk</code>. И грузить он его будет исходя из <code>publicPath</code> в настройках webpack. Но ведь embedded решения могут быть внедрены по любому пути. Как быть? Всё просто, пишем в точке входа что-нибудь типа: <code>__webpack_public_path__ = window.myPredefinedPath</code> и дело в шляпе. Магия. До загрузки приложения нужно определить этот <code>window.myPredefinedPath</code>, webpack его увидит и будет использовать. Сразу отмечу, что надо писать как есть, не <code>window.__webpack…=</code>, а как-будто эта переменная уже определена в коде. Если на неё ругается <code>eslint</code> не беда, пишем: <code>// eslint-disable-line</code> и он более не ругается.</p>
<h3>webpack css &amp; url</h3>
<p>Возникла проблема с тем, что в стилях указаны пути к шрифтам так, что webpack их найти не может (отдельная тема почему так). Да и не должен, него его это забота. Однако он пытается от-<code>revolve</code>-ить все пути, что находит в css. Лечится установкой <code>url: false</code> в <code>cssLoader</code>-е. После этой настройки пути отдаются как есть без каких-либо проверок.</p>
]]></description></item></channel></rss>