<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <link>https://new.faiwer.ru/it</link>
    <title>IT</title>
    <language>ru</language>
    <generator>KohanaPHP</generator>
    <item>
      <title>"Native code" label</title>
      <pubDate>Fri, 01 Aug 2025 09:14:00 +0000</pubDate>
      <link>https://new.faiwer.ru/content/181-native_code_label</link>
      <guid isPermaLink="false">http://new.faiwer.ru/181</guid>
      <description>&lt;p&gt;How to detect if a function is a core part of browser API? This way:&lt;/p&gt;

&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// () =&amp;gt; {}&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// function log() { [native code] }&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;You see? &lt;var&gt;[native code]&lt;/var&gt; label. Sounds good, right? &lt;/p&gt;

&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// function () { [native code] }&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Oops. But we still have this &lt;var&gt;log&lt;/var&gt; name in the string snapshot.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;</description>
    </item>
    <item>
      <title>Babel to EsBuild migration for Webpack</title>
      <pubDate>Sun, 09 Feb 2025 15:00:00 +0000</pubDate>
      <link>https://new.faiwer.ru/content/180-babel_to_esbuild_migration_for_webpack</link>
      <guid isPermaLink="false">http://new.faiwer.ru/180</guid>
      <description>&lt;p&gt;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: &lt;var&gt;780 KiB&lt;/var&gt;. Whereas the project had only a few dependencies: &lt;var&gt;react&lt;/var&gt; (~10 KiB), &lt;var&gt;react-router&lt;/var&gt; (~70 KiB), &lt;var&gt;react-dom&lt;/var&gt; (~90 KiB), &lt;var&gt;dayjs&lt;/var&gt; (~3 KiB), &lt;var&gt;lodash&lt;/var&gt; (~70 KiB). The bundle should not be that big for so few dependencies.&lt;/p&gt;

&lt;p&gt;So I ran &lt;var&gt;webpack-dev-analyzer&lt;/var&gt; 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 &lt;var&gt;@statoscope/webpack-plugin&lt;/var&gt;. It was a good tool but it gave me the same result. The bundle has way too much trash. &lt;/p&gt;

&lt;p&gt;Ok. Probably it's just webpack wrappers over 300+ small modules. So I asked &lt;var&gt;Perplexity&lt;/var&gt; - 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. &lt;/p&gt;

&lt;p&gt;I knew that &lt;var&gt;esbuild&lt;/var&gt; didn't support a lot of stuff that babel supports. So I expected that it would take a day to migrate the codebase to &lt;var&gt;esbuild&lt;/var&gt;. But surprisingly once I replaced &lt;var&gt;babel-loader&lt;/var&gt; with &lt;var&gt;esbuild-loader&lt;/var&gt; the build command worked out. The build didn't work but the bundle was assembled without any errors.&lt;/p&gt;

&lt;p&gt;Did it help? Nope. Instead of &lt;var&gt;780 KiB&lt;/var&gt;, I got &lt;var&gt;~1 MiB&lt;/var&gt;. Luckily I found out that there's also &lt;var&gt;EsbuildPlugin&lt;/var&gt;, which replaces &lt;var&gt;TerserPlugin&lt;/var&gt; and works better. Added this line, it worked like a charm, and... Whoa! &lt;var&gt;280 KiB&lt;/var&gt;. That's huuuge.&lt;/p&gt;

&lt;p&gt;So I figured out - it worked. Now I have to find a way to fix the app (spoiler: it didn't work at all). &lt;/p&gt;

&lt;h3&gt;Implicit React imports&lt;/h3&gt;

&lt;p&gt;React 17+ supports the possibility of avoiding adding &lt;var&gt;import React from 'react'&lt;/var&gt; to every TSX-file. &lt;var&gt;esbuild&lt;/var&gt; supports it too, but somehow it didn't work out of the box. Solution:&lt;/p&gt;

&lt;div class="site_code_source" data-lang="diff"&gt;&lt;div class="highlighted-source default diff"&gt;&lt;pre&gt;  const esbuildLoader: RuleSetRule = {
    test: /\.(js|ts|tsx)$/,
    loader: &amp;#39;esbuild-loader&amp;#39;,
    exclude: /node_modules/,
    options: {
       target: &amp;#39;es2020&amp;#39;,
       minify: cfg.PROD,
&lt;span class="gi"&gt;+      // Support implicit React imports:&lt;/span&gt;
&lt;span class="gi"&gt;+      jsx: &amp;#39;automatic&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+      jsxFactory: &amp;#39;React.createElement&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+      jsxImportSource: &amp;#39;react&amp;#39;,&lt;/span&gt;
    } satisfies LoaderOptions,
  };
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;TSX-Control-Statements&lt;/h3&gt;

&lt;p&gt;I just removed the packages and all their &lt;var&gt;&amp;lt;If/&amp;gt;&lt;/var&gt;s. I liked them though. There's a plugin for &lt;var&gt;Vite&lt;/var&gt; that adds their support. But I still use &lt;var&gt;Webpack&lt;/var&gt; and the plugin works via string replacements, not via AST. &lt;/p&gt;

&lt;h3&gt;SourceMaps&lt;/h3&gt;

&lt;p&gt;To enable sourcemaps I had to disable &lt;var&gt;EsbuildPlugin&lt;/var&gt; 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.&lt;/p&gt;

&lt;h3&gt;TypeScript&lt;/h3&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;webpackConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ForkTsCheckerWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;typescript&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;configFile&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TS_CONFIG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;ES-Lint&lt;/h3&gt;

&lt;p&gt;For some reason, it stopped working. The solution was pretty simple:&lt;/p&gt;

&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;webpackConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ESLintPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;extensions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;tsx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;Final results&lt;/h3&gt;

&lt;ul&gt;&lt;li&gt;File sizes
	&lt;ul&gt;&lt;li&gt;Before: 641 KiB + 234 KiB + 29.5 KiB (css) = &lt;var&gt;904.5 KiB&lt;/var&gt;&lt;/li&gt;
		&lt;li&gt;After: 221 KiB + 55 KiB + 8.1 KiB (o_O, css) = &lt;var&gt;284.1 KiB&lt;/var&gt;&lt;/li&gt;
		&lt;li&gt;After &lt;var&gt;minifyIdentifiers: true&lt;/var&gt; (scary thing) &amp;amp; &lt;var&gt;minifyWhitespace: true&lt;/var&gt;:  138 KiB + 44.2 KiB + 8.1 KiB = &lt;var&gt;190.1 KiB&lt;/var&gt;&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
	&lt;li&gt;DOMContentLoaded:
	&lt;ul&gt;&lt;li&gt;Before: &lt;var&gt;737ms&lt;/var&gt;&lt;/li&gt;
		&lt;li&gt;After: &lt;var&gt;231ms&lt;/var&gt;&lt;/li&gt;
		&lt;li&gt;After-2: &lt;var&gt;131ms&lt;/var&gt;&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
	&lt;li&gt;Build time: &lt;var&gt;esbuild&lt;/var&gt; was ~3x faster&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;Afterthought&lt;/h3&gt;

&lt;p&gt;Taking a deeper look I found:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;The main issue wasn't in &lt;var&gt;babel&lt;/var&gt;. The main issue was that I used &lt;var&gt;inline-source-map&lt;/var&gt; 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 &lt;var&gt;babel&lt;/var&gt;.&lt;/li&gt;
	&lt;li&gt;&lt;var&gt;esbuild&lt;/var&gt; has a better wrapper logic for a ton of tiny files than &lt;var&gt;babel&lt;/var&gt; does. &lt;/li&gt;
	&lt;li&gt;Whitespace minification is quite important. &lt;var&gt;GZip&lt;/var&gt; doesn't eliminate the difference. &lt;/li&gt;
	&lt;li&gt;Name mangling is a beast. But be careful, it can easily break your app in runtime. &lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    <item>
      <title>How to migrate Javascript webpack configuration to Typescript</title>
      <pubDate>Sat, 25 Mar 2023 16:00:00 +0000</pubDate>
      <link>https://new.faiwer.ru/content/179-how_to_migrate_javascript_webpack_configuration_to_typescript</link>
      <guid isPermaLink="false">http://new.faiwer.ru/179</guid>
      <description>&lt;p&gt;It turned out that the newest versions (at least 5+) of webpack support Typescript out of the box. So the algorithm is next:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Create &lt;var&gt;tsconfig.json&lt;/var&gt; at the root level. Content:

	&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;compilerOptions&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;module&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;CommonJS&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;target&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;ES5&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;esModuleInterop&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;checkJs&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;strict&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
	&lt;/li&gt;
	&lt;li&gt;Rename all *&lt;var&gt;.js&lt;/var&gt; files to &lt;var&gt;*.ts&lt;/var&gt;&lt;/li&gt;
	&lt;li&gt;Type all of them:
	&lt;ul&gt;&lt;li&gt;no more ugly &lt;var&gt;require&lt;/var&gt;, use &lt;var&gt;import&lt;/var&gt;.&lt;/li&gt;
		&lt;li&gt;&lt;var&gt;webpack&lt;/var&gt; package has typings out of the box.
		&lt;ul&gt;&lt;li&gt;You may find these types useful: &lt;var&gt;Configuration&lt;/var&gt; &amp;amp; &lt;var&gt;RuleSetRule&lt;/var&gt;&lt;/li&gt;
			&lt;li&gt;To enable &lt;var&gt;devServer&lt;/var&gt; write this: 
			&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Configuration&lt;/span&gt; &lt;span class="kr"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;WebpackConfiguration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;devServer&lt;/span&gt;&lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;WebpackDevServerConfiguration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
			&lt;/li&gt;
		&lt;/ul&gt;&lt;/li&gt;
		&lt;li&gt;Some of the popular plugins have types too.&lt;/li&gt;
		&lt;li&gt;Some of them don't have types at all:
		&lt;ul&gt;&lt;li&gt;Create a &lt;var&gt;*.d.ts&lt;/var&gt; file&lt;/li&gt;
			&lt;li&gt;Put there something like this:
			&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="nx"&gt;declare&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;postcss-assets&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;postcssAssets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;basePath&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;relative&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
			&lt;/li&gt;
		&lt;/ul&gt;&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
	&lt;li&gt;Make sure your &lt;var&gt;webpack.config.ts&lt;/var&gt; file is placed at the root level. I mean exactly at the same spot where &lt;var&gt;node_modules&lt;/var&gt; is. Otherwise, you won't be able to build it. No &lt;var&gt;compilerOptions&lt;/var&gt; helped me. &lt;/li&gt;
	&lt;li&gt;Run webpack. It should work.&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    <item>
      <title>How to make a simple system.d service for a node.js server</title>
      <pubDate>Tue, 21 Feb 2023 20:00:00 +0000</pubDate>
      <link>https://new.faiwer.ru/content/178-how_to_make_a_simple_systemd_service_for_a_nodejs_server</link>
      <guid isPermaLink="false">http://new.faiwer.ru/178</guid>
      <description>&lt;p&gt;Put this content:&lt;/p&gt;

&lt;div class="site_code_source" data-lang="ini"&gt;&lt;div class="highlighted-source default ini"&gt;&lt;pre&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{name}&lt;/span&gt;

&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{user}&lt;/span&gt;
&lt;span class="na"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/node {full-path-to-script}.js&lt;/span&gt;

&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;... somewhere as &lt;var&gt;{name}.service&lt;/var&gt;, where:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;var&gt;{name}&lt;/var&gt; is the name of the service&lt;/li&gt;
	&lt;li&gt;&lt;var&gt;{user}&lt;/var&gt; is the name of the user to run the script (optional)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;... then:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;run: &lt;var&gt;this sudo ln -s /{full_path}/{name}.service /lib/systemd/system/{name}.service&lt;/var&gt; &lt;/li&gt;
	&lt;li&gt;then this: &lt;var&gt;sudo systemctl daemon-reload&lt;/var&gt;&lt;/li&gt;
	&lt;li&gt;then this: &lt;var&gt;sudo systemctl enable {name}.service&lt;/var&gt;&lt;/li&gt;
	&lt;li&gt;and finally this: &lt;var&gt;sudo systemctl start {name}&lt;/var&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;How does it work?&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;It starts the service on boot (see &lt;var&gt;WantedBy&lt;/var&gt; section).&lt;/li&gt;
	&lt;li&gt;Using &lt;var&gt;ExecStart&lt;/var&gt; command. Important: we specify the full path to &lt;var&gt;node&lt;/var&gt;&lt;/li&gt;
	&lt;li&gt;&lt;var&gt;SystemD&lt;/var&gt; remembers the &lt;var&gt;PID&lt;/var&gt; of the new process and considers the service is ongoing until the process is died.&lt;/li&gt;
	&lt;li&gt;So any subsequent &lt;var&gt;systemctrl start {name}&lt;/var&gt; won't do anything if the previous process is alive.&lt;/li&gt;
	&lt;li&gt;This behavior is determined by &lt;var&gt;Type=Simple&lt;/var&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    <item>
      <title>Multilayout Keybinds in a Browser</title>
      <pubDate>Wed, 09 Feb 2022 16:00:00 +0000</pubDate>
      <link>https://new.faiwer.ru/content/177-multilayout_keybinds_in_a_browser</link>
      <guid isPermaLink="false">http://new.faiwer.ru/177</guid>
      <description>&lt;p&gt;Imagine that you want to add support of some keybinds in your web application. Let it be &lt;var&gt;Ctrl+L&lt;/var&gt; for liking\unliking something. What kind of issues can you face in such a scenario?&lt;/p&gt;

&lt;h3&gt;CMD or Ctrl?&lt;/h3&gt;

&lt;p&gt;At first look at &lt;var&gt;Ctrl&lt;/var&gt;. Probably on &lt;var&gt;MacOS&lt;/var&gt; you'd like to replace it with &lt;var&gt;CMD&lt;/var&gt;.  You can check it by &lt;var&gt;event.metaKey&lt;/var&gt;.&lt;/p&gt;

&lt;h3&gt;Extra modificators&lt;/h3&gt;

&lt;p&gt;Probably you wouldn't like to consider &lt;var&gt;Ctrl+Alt+S&lt;/var&gt; as &lt;var&gt;Ctrl+S&lt;/var&gt;. So don't forget to handle this case.&lt;/p&gt;

&lt;h3&gt;Different layouts&lt;/h3&gt;

&lt;p&gt;Not every language that uses the Latin alphabet has the &lt;var&gt;L&lt;/var&gt; 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. &lt;/p&gt;

&lt;p&gt;To get a real key position you can use &lt;var&gt;which&lt;/var&gt;, &lt;var&gt;code&lt;/var&gt;, &lt;var&gt;codeKey&lt;/var&gt; properties. To get a letter on the key use &lt;var&gt;key&lt;/var&gt; property.&lt;/p&gt;

&lt;h3&gt;Different alphabets&lt;/h3&gt;

&lt;p&gt;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 &lt;var&gt;code&lt;/var&gt; and &lt;var&gt;codeKey&lt;/var&gt; properties.&lt;/p&gt;

&lt;h3&gt;Example&lt;/h3&gt;

&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getEventKeyBind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keybind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metaKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;keybind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cmd&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctrlKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;keybind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ctrl&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;keybind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;shift&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;altKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;keybind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;keybind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;space&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^[a-z]$/&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// latin key or a special key&lt;/span&gt;
      &lt;span class="nx"&gt;keybind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// extra-latin or non-latin key&lt;/span&gt;
      &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[,&lt;/span&gt; &lt;span class="nx"&gt;enSymbol&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^Key(\w)$/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="nx"&gt;keybind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enSymbol&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;enSymbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;keybind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;+&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;</description>
    </item>
    <item>
      <title>Custom English-German layout for Ubuntu</title>
      <pubDate>Sun, 24 Nov 2019 18:00:00 +0000</pubDate>
      <link>https://new.faiwer.ru/content/175-custom_english-german_layout_for_ubuntu</link>
      <guid isPermaLink="false">http://new.faiwer.ru/175</guid>
      <description>&lt;p&gt;German language contains some extra latin symbols that English language doesn't: &lt;var&gt;ä, ö, ü, ß&lt;/var&gt;. 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.&lt;/p&gt;

&lt;p&gt;Under the cut 3 ways to handle it:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Compose key&lt;/li&gt;
	&lt;li&gt;International layout with "dead keys"&lt;/li&gt;
	&lt;li&gt;Write your own EN-layout&lt;/li&gt;
&lt;/ol&gt;</description>
    </item>
    <item>
      <title>How to use babel-module-resolver with vsCode and ESLint</title>
      <pubDate>Tue, 04 Dec 2018 08:43:00 +0000</pubDate>
      <link>https://new.faiwer.ru/content/174-how_to_use_babel-module-resolver_with_vscode_and_eslint</link>
      <guid isPermaLink="false">http://new.faiwer.ru/174</guid>
      <description>&lt;p&gt;This article is a small guide about how to configure an enviroment for &lt;var&gt;babel-plugin-module-resolver&lt;/var&gt;. This plugin allows you use custom prefixes for &lt;var&gt;import/export&lt;/var&gt; declarations in JS files. E.g. &lt;var&gt;import A from 'com/A'  &lt;/var&gt;can be treated as &lt;var&gt;import A from ../../components/A&lt;/var&gt;.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;At first you should install the package &lt;var&gt;npm i -D babel-plugin-module-resolver&lt;/var&gt;. &lt;/li&gt;
	&lt;li&gt;Then add your &lt;var&gt;map-config&lt;/var&gt; to your &lt;var&gt;.babelrc&lt;/var&gt; file into the &lt;var&gt;plugins&lt;/var&gt; section. E.g.:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;babel-plugin-module-resolver&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;alias&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="s2"&gt;&amp;quot;^com(.+)&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;./src/com/\\1&amp;quot;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;&lt;li&gt;Then install &lt;var&gt;npm i -D eslint-import-resolver-babel-module&lt;/var&gt; package for &lt;var&gt;ESLint&lt;/var&gt;. It allows &lt;var&gt;ESLint&lt;/var&gt; to check your rewrited imports, It assumes that you already use &lt;var&gt;eslint-plugin-import&lt;/var&gt; package. If you don't I recommend you to start using it. This package checks your &lt;var&gt;import/export&lt;/var&gt; declarations, it's very convinient.&lt;/li&gt;
	&lt;li&gt;Then change in your &lt;var&gt;.eslint&lt;/var&gt; file:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;parser&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;babel-eslint&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;parserOptions&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;ecmaVersion&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;sourceType&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;module&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;ecmaFeatures&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;jsx&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;settings&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;import/resolver&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;babel-module&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;plugins&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;import&amp;quot;&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;&lt;li&gt;You also need to install &lt;var&gt;babel-eslint&lt;/var&gt; if you didn't do it yet. And move its declaration from &lt;var&gt;parseOptions&lt;/var&gt; to the root of the &lt;var&gt;.eslintrc&lt;/var&gt;.&lt;/li&gt;
	&lt;li&gt;&lt;var&gt;babel-module&lt;/var&gt; part in &lt;var&gt;.babelrc&lt;/var&gt; section is just &lt;var&gt;{}&lt;/var&gt;, it's okay.&lt;/li&gt;
	&lt;li&gt;Install &lt;var&gt;ESLint&lt;/var&gt; plugin in &lt;var&gt;vsCode&lt;/var&gt;&lt;/li&gt;
	&lt;li&gt;Probably you'll need to write in your &lt;var&gt;.vcode/settings.json&lt;/var&gt; something like next lines. They force &lt;var&gt;vscode&lt;/var&gt; to change &lt;var&gt;CWD&lt;/var&gt; (current directory) in the deamon of &lt;var&gt;eslint plugin&lt;/var&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="s2"&gt;&amp;quot;eslint.workingDirectories&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="s2"&gt;&amp;quot;directory&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;./client&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="s2"&gt;&amp;quot;changeProcessCWD&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;ul&gt;&lt;li&gt;If you use &lt;var&gt;path-intellisense&lt;/var&gt; plugin, you should consider configurate your mapping in &lt;var&gt;.vscode/settings.json&lt;/var&gt; too. &lt;/li&gt;
	&lt;li&gt;If you have any troubles, something doesn't work properly or doesn't work at all: &lt;var&gt;Use force, Luke&lt;/var&gt;. Set vscode setting &lt;var&gt;"eslint.trace.server": "verbose"&lt;/var&gt; and debug, debug, debug. I've spent on it several hours :(&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    <item>
      <title>Отладка NodeJS приложений при помощи ndb</title>
      <pubDate>Fri, 03 Aug 2018 10:19:45 +0000</pubDate>
      <link>https://new.faiwer.ru/content/173-otladka_nodejs_prilojeniy_pri_pomoschi_ndb</link>
      <guid isPermaLink="false">http://new.faiwer.ru/173</guid>
      <description>&lt;p&gt;Пост-заметка об &lt;var&gt;ndb&lt;/var&gt;. Всем кто пишет для &lt;var&gt;nodejs&lt;/var&gt; периодически приходится отлаживать своё приложение. Да даже тем, кто использует &lt;var&gt;mocha&lt;/var&gt; или &lt;var&gt;webpack&lt;/var&gt; бывает нет-нет да удобнее отладить по-человечески проблему, нежели тыкать повсюду &lt;var&gt;console.log&lt;/var&gt;-и. &lt;var&gt;NodeJS&lt;/var&gt; издавна предоставляет нам для этого браузерный инструмент.&lt;/p&gt;

&lt;p&gt;Работает оно так:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;мы запускаем наше приложение из консоли с нужным флагом&lt;/li&gt;
	&lt;li&gt;NodeJS в консоли нам сообщает ссылку с нужным портом&lt;/li&gt;
	&lt;li&gt;Которую мы открываем в браузере и видим перед собой копию &lt;var&gt;chrome-dev-tools&lt;/var&gt;-ов.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Если надо перезагрузить приложение ― повторяем всё с нуля. С одной стороны сами инструменты весьма удобные. С другой стороны вся эта мышиная возня с портами и перезапусками &lt;var&gt;очень&lt;/var&gt; неудобна.&lt;/p&gt;

&lt;p&gt;И тут на помощь к нам приходит &lt;a href="https://github.com/GoogleChromeLabs/ndb" target="_blank"&gt;ndb&lt;/a&gt;. Просто перед командой запуска приложения добавляем &lt;var&gt;ndb&lt;/var&gt;. &lt;var&gt;Dev-tools&lt;/var&gt;-ы открываются прямо в своём отдельном окне. Перезагрузить приложение можно нажав &lt;var&gt;ctrl+R&lt;/var&gt;. Все &lt;var&gt;breakpoint&lt;/var&gt;-ы и прочая муть при этом сохраняется. &lt;/p&gt;

&lt;p&gt;Выглядит это всё примерно так:&lt;/p&gt;

&lt;p&gt;&lt;img alt="" src="https://new.faiwer.ru/public/upload/content/images/5b642c274b437.png" style="height:475px;width:800px;"&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Webpack и SCSS импорты в купе с module-resolver</title>
      <pubDate>Fri, 03 Aug 2018 09:00:00 +0000</pubDate>
      <link>https://new.faiwer.ru/content/172-webpack_i_scss_importyi_v_kupe_s_module-resolver</link>
      <guid isPermaLink="false">http://new.faiwer.ru/172</guid>
      <description>&lt;p&gt;С приходоим &lt;var&gt;ES7&lt;/var&gt; в &lt;var&gt;JS&lt;/var&gt; пришли &lt;var&gt;import&lt;/var&gt;-ы и &lt;var&gt;export&lt;/var&gt;-ы. Жить стало веселее, и... сложнее. Появилось много заморочек и вообще новых проблем. Одна из таких проблем выглядит так: `&lt;var&gt;import some from ../../../../../some&lt;/var&gt;`. Знакомая ситуация?&lt;/p&gt;

&lt;p&gt;Обычно её в случае &lt;var&gt;webpack&lt;/var&gt;-а решают при помощи модуля &lt;a href="https://github.com/tleunen/babel-plugin-module-resolver" target="_blank"&gt;module-resolver&lt;/a&gt;. Он позволяет в &lt;var&gt;.babelrc &lt;/var&gt;(или где вы держите конфигурацию) указывать регулярные выражения для автозамены, а также добавляет поддержку &lt;var&gt;root&lt;/var&gt;-записей. Скажем с ним можно указать `&lt;var&gt;import actions from '@actions/header&lt;/var&gt;', где `&lt;var&gt;@actions&lt;/var&gt;` будет алиасом к какому-нибудь пути в вашем проекте. &lt;/p&gt;

&lt;p&gt;Дальше встаёт вопрос инструментов. А как на это должен реагировать редактор? Как он узнает, что теперь пути в &lt;var&gt;import&lt;/var&gt;-ах устроены хитрее. Что делать с линтингом? К счастью, для &lt;var&gt;eslint&lt;/var&gt; есть такой плагин:  &lt;var&gt;eslint-plugin-import&lt;/var&gt;. С редакторами ситуация может быть сложнее.&lt;/p&gt;

&lt;p&gt;Хорошо, а что делать с &lt;var&gt;SCSS\Sass&lt;/var&gt;? Нормальных рабочих решений с наскоку мне найти не удалось. Но как оказалось, всё можно решить относительно просто. В настройках &lt;var&gt;webpack&lt;/var&gt; для &lt;var&gt;sass-loader&lt;/var&gt;-а можно указать настройки. Они будут переданы как есть в пакет &lt;var&gt;node-sass&lt;/var&gt;. А вот он, оказывается, достаточно гибкий. Если там указать метод &lt;var&gt;importer&lt;/var&gt;, то можно навязать свою логику обработки &lt;var&gt;@import&lt;/var&gt;-ов. Пример:&lt;/p&gt;

&lt;div class="site_code_source" data-lang="js"&gt;&lt;div class="highlighted-source default js"&gt;&lt;pre&gt;&lt;span class="p"&gt;{&lt;/span&gt;
	&lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sass-loader&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
	&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
	&lt;span class="p"&gt;{&lt;/span&gt;
		&lt;span class="nx"&gt;sourceMap&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
		&lt;span class="nx"&gt;importer&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
		&lt;span class="p"&gt;{&lt;/span&gt;
			&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;
				&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
				&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
				&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(...),&lt;/span&gt;
		&lt;span class="p"&gt;}),&lt;/span&gt;
	&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Либо даже выцепить ваши настройки прямо из настроек в &lt;var&gt;.babelrc&lt;/var&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Google Calendar и спам от Казино в мобильном приложении на Android</title>
      <pubDate>Thu, 19 Jul 2018 10:58:47 +0000</pubDate>
      <link>https://new.faiwer.ru/content/171-google_calendar_i_spam_ot_kazino_v_mobilnom_prilojenii_na_android</link>
      <guid isPermaLink="false">http://new.faiwer.ru/171</guid>
      <description>&lt;p&gt;В который раз наткнулся на странную вещь. Телефон издаёт странный нестандартный непривычный звук. Подхожу - смотрю - реклама казино в &lt;var&gt;google-календаре&lt;/var&gt;. В этот раз решил разобраться, как так. Стал рыться-копаться. Нет, таких записей в календаре у меня нет. Нет - к календарю ни у какого приложения доступа нет. WTF?&lt;/p&gt;

&lt;p&gt;Разгадка - &lt;var&gt;недоработка Gmail&lt;/var&gt;. Оказывается что если сформировать письмо правильным образом, то "умный" Google автоматически формирует в календаре Event под это письмо. И часть писем успевают сформировать такие спам-уведомления из календаря ещё до того момента, пока Google не определяет, что это спам и event-ы с письмами сносит. Однако диалог "пойду-не-пойду на мероприятие" на мобильнике при этом не тухнет. &lt;/p&gt;

&lt;p&gt;Лечится принудительным отключением в настройках Google Calendar опции: &lt;var&gt;Automatically add events from Gmail to my calendar&lt;/var&gt;. Да, она у всех включена по-умолчанию.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
