<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kaspars Dambis</title>
    <description>The latest articles on DEV Community by Kaspars Dambis (@kasparsd).</description>
    <link>https://dev.to/kasparsd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F112113%2F8b2496c3-919c-40ec-bca9-7b0ba48aedc8.jpeg</url>
      <title>DEV Community: Kaspars Dambis</title>
      <link>https://dev.to/kasparsd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kasparsd"/>
    <language>en</language>
    <item>
      <title>CSS Minification for Server-Side Rendered Apps</title>
      <dc:creator>Kaspars Dambis</dc:creator>
      <pubDate>Thu, 14 May 2020 08:21:17 +0000</pubDate>
      <link>https://dev.to/kasparsd/css-minification-for-server-side-rendered-apps-5eh0</link>
      <guid>https://dev.to/kasparsd/css-minification-for-server-side-rendered-apps-5eh0</guid>
      <description>&lt;p&gt;With styles moving into components and critical styles being unique for each layout we no longer get the luxury of preparing and optimizing CSS during the build process (especially for server-side rendered applications). Optimizations needs to happen dynamically and have to be as fast even with a caching layer on top of it.&lt;/p&gt;

&lt;p&gt;Most of the HTTP responses these days are compressed using GZIP which takes care of reducing the response body size during the transfer so I’m not completely sure why CSS and JS minification is considered &lt;em&gt;that important&lt;/em&gt; &lt;a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer#minification-preprocessing--context-specific-optimizations"&gt;in Google PageSpeed&lt;/a&gt;, for example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minification vs. Compression
&lt;/h2&gt;

&lt;p&gt;I created &lt;a href="https://github.com/kasparsd/csso-perf"&gt;this experiment&lt;/a&gt; to evaluate the impact of CSS minification over the resulting compressed version and the time it takes to do the minification.&lt;/p&gt;

&lt;p&gt;For minfication I used the &lt;a href="https://github.com/css/csso"&gt;csso&lt;/a&gt; library and this “dummify” three rule regular expression string-replace as the fastest way to do &lt;em&gt;some&lt;/em&gt; minification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dummyMinifyCss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;css&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;css&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\n\r\t]&lt;/span&gt;&lt;span class="sr"&gt;/gi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Line breaks to spaces.&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/gi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Multiple spaces to single spaces.&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(\s&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;?)?([\(\)&lt;/span&gt;&lt;span class="sr"&gt;:;}{&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;])(\s&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;?)?&lt;/span&gt;&lt;span class="sr"&gt;/ig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Remove spaces before and after :;{}()&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that I haven’t really tested this tiny minifier apart from this site and a few other sites using the Bootstrap CSS library. I’m using a PHP version of the same snippet with the &lt;a href="https://github.com/kasparsd/minit"&gt;Minit plugin&lt;/a&gt; on &lt;a href="https://kaspars.net/blog"&gt;my blog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Here are the results for the Bootstrap CSS library:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Compressed&lt;/th&gt;
&lt;th&gt;Minify Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Original&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;198KB&lt;/td&gt;
&lt;td&gt;26KB&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Minified&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;158KB (-20%)&lt;/td&gt;
&lt;td&gt;24KB&lt;/td&gt;
&lt;td&gt;109ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dummified&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;166KB (-16%)&lt;/td&gt;
&lt;td&gt;24KB&lt;/td&gt;
&lt;td&gt;6ms (-94%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The dummy minifier produced only a 4% larger CSS but &lt;strong&gt;18 times faster&lt;/strong&gt; than &lt;code&gt;csso&lt;/code&gt; with the compressed CSS being the same! Note that different CSS files have different minification potential usually in the range of 5-35%.&lt;/p&gt;

&lt;p&gt;Minifying CSS has very little impact over its compressed size so using a fast minifier that removes only the unnecessary whitespace is the quickest solution for server side rendered apps with dynamically generated CSS.&lt;/p&gt;

</description>
      <category>css</category>
      <category>react</category>
      <category>ssr</category>
      <category>optimization</category>
    </item>
    <item>
      <title>How to Bulk Update the “Tested up to” Version for WordPress Plugins</title>
      <dc:creator>Kaspars Dambis</dc:creator>
      <pubDate>Thu, 02 Apr 2020 14:17:54 +0000</pubDate>
      <link>https://dev.to/kasparsd/how-to-bulk-update-the-tested-up-to-version-for-wordpress-plugins-55h5</link>
      <guid>https://dev.to/kasparsd/how-to-bulk-update-the-tested-up-to-version-for-wordpress-plugins-55h5</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Vl0EbVnHvDs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here is &lt;a href="https://www.youtube.com/watch?v=Vl0EbVnHvDs"&gt;a quick demo video&lt;/a&gt; of &lt;a href="https://github.com/wpsh/batch-mark-as-tested"&gt;a bash script&lt;/a&gt; for updating the “Tested up to” version of multiple WordPress plugins at the same time. This solves the pain of updating the &lt;code&gt;readme.txt&lt;/code&gt; file for all your plugins whenever a new version of WordPress is released.&lt;/p&gt;

&lt;p&gt;p.s. Background music provided by my kids 🥁🥳🤣&lt;/p&gt;

</description>
      <category>wordpress</category>
    </item>
    <item>
      <title>Sensor Pilot: A Progressive Web App (PWA) for Bluetooth Sensors</title>
      <dc:creator>Kaspars Dambis</dc:creator>
      <pubDate>Mon, 30 Dec 2019 12:05:35 +0000</pubDate>
      <link>https://dev.to/kasparsd/sensor-pilot-a-progressive-web-app-pwa-for-bluetooth-sensors-37c6</link>
      <guid>https://dev.to/kasparsd/sensor-pilot-a-progressive-web-app-pwa-for-bluetooth-sensors-37c6</guid>
      <description>&lt;p&gt;Did you know that Chrome-based browsers (including on Android phones) can interact with Bluetooth Low Energy (LE) sensors and devices without any additional software using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API"&gt;Web Bluetooth API&lt;/a&gt;? Bundle that with the off-line features enabled by the &lt;a href="https://developers.google.com/web/progressive-web-apps"&gt;progressive web apps&lt;/a&gt; (PWA) and we get a real application using pure web technologies and standards.&lt;/p&gt;

&lt;p&gt;Visit the &lt;a href="https://kasparsd.github.io/sensor-pilot/"&gt;Sensor Pilot app →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I started playing with the Web Bluetooth API after getting the &lt;a href="https://kaspars.net/go/xiaomi-mijia-clock-bluetooth"&gt;Xiaomi Mijia clock&lt;/a&gt; that also also measures air temperature and relative humidity:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kaspars.net/go/xiaomi-mijia-clock-bluetooth"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QjRiTiEB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kaspars.net/wp-content/uploads/2019/12/xiaomi-mijia-bluetooth-temperature-humidity-sensor.jpg" alt="Xiaomi Mijia clock with temperature and humidity sensor and Bluetooth LE interface"&gt;&lt;/a&gt;Xiaomi Mijia clock with temperature and humidity sensor and Bluetooth LE interface.&lt;/p&gt;

&lt;p&gt;Turns out that reading sensor values and updating the time is relatively simple as long as you can get your head around the Bluetooth LE communication patterns such as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/BluetoothRemoteGATTServer"&gt;GATT services&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/BluetoothRemoteGATTCharacteristic"&gt;characteristics&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Screenshots
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5E5s0BB7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kaspars.net/wp-content/uploads/2019/12/sensor-pilot-desktop-pwa-bluetooth-1024x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5E5s0BB7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kaspars.net/wp-content/uploads/2019/12/sensor-pilot-desktop-pwa-bluetooth-1024x1024.png" alt="Sensor Pilot Progressive Web App for Bluetooth Sensors on Desktop"&gt;&lt;/a&gt;Google Chrome web browser connected to &lt;a href="https://aranet4.com/"&gt;Aranet4&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HIUl0g8T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kaspars.net/wp-content/uploads/2019/12/server-pilot-pwa-screenshot-desktop-2x-1024x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HIUl0g8T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kaspars.net/wp-content/uploads/2019/12/server-pilot-pwa-screenshot-desktop-2x-1024x1024.png" alt="Sensor Pilot Progressive Web App for Bluetooth Sensors as an app"&gt;&lt;/a&gt;Web app view of the progressive web app connected to &lt;a href="https://aranet4.com/"&gt;Aranet4&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>bluetooth</category>
      <category>react</category>
      <category>pwa</category>
      <category>webbluetooth</category>
    </item>
    <item>
      <title>PGP Subkeys for Keybase</title>
      <dc:creator>Kaspars Dambis</dc:creator>
      <pubDate>Fri, 05 Jul 2019 11:37:31 +0000</pubDate>
      <link>https://dev.to/kasparsd/pgp-subkeys-for-keybase-4n0b</link>
      <guid>https://dev.to/kasparsd/pgp-subkeys-for-keybase-4n0b</guid>
      <description>&lt;p&gt;The private parts of PGP keys (including subkeys) stored on Yubikey can’t be exported so you must always use the actual Yubikey to encrypt, decrypt, sign and verify messages. &lt;a href="https://wiki.debian.org/Subkeys"&gt;Subkeys&lt;/a&gt; stored outside the hardware key can simplify the day-to-day encryption and signing operations and can be revoked independently from the master key.&lt;/p&gt;

&lt;p&gt;So I created a new subkey with encrypt and sign capabilities using the master key on the Yubikey and left them on the computer instead of saving to the Yubikey:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpg --expert --edit-key FINGERPRINTOFYOURMASTERKEY
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--expert&lt;/code&gt; flag is required to enable subkeys with both encrypt and sign capabilities. Use &lt;code&gt;gpg --edit-card&lt;/code&gt; to view the fingerprint of your master key with Yubikey plugged in.&lt;/p&gt;

&lt;p&gt;Now create a new subkey:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;addkey
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Choose &lt;code&gt;(8) RSA (set your own capabilities)&lt;/code&gt; and choose &lt;code&gt;(S) Toggle the sign capability&lt;/code&gt; and &lt;code&gt;(E) Toggle the encrypt capability&lt;/code&gt;, and set the key size to 4096 bits.&lt;/p&gt;

&lt;p&gt;Be sure to set the expiration date to something after the expiration date of the encryption subkey already stored on the Yubikey (if there is one) &lt;a href="https://github.com/keybase/keybase-issues/issues/1853"&gt;or Keybase will fail to use the new encryption subkey&lt;/a&gt; when users encrypt messages using the &lt;a href="https://keybase.io/encrypt#kaspars"&gt;online encryption form&lt;/a&gt;. Most PGP software will pick the subkey by its creation time and then by the expiration time.&lt;/p&gt;

&lt;p&gt;Save the changes to your combined public key after creating the subkey by entering &lt;code&gt;save&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now send the updated public key to your preferred PGP key server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gpg --send-keys FINGERPRINTOFYOURMASTERKEY
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and to Keybase using &lt;a href="https://keybase.io/docs/command_line"&gt;their command-line tool&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ keybase pgp update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It is impossible to upload the updated public key using their web UI unless you have another private key associated with the account. All of my private keys before creating this new subkey were stored on Yubikey so I had to use their CLI tool.&lt;/p&gt;

&lt;p&gt;You can now export the private part of your new subkey. Find the fingerprint of the new subkey:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gpg --list-secret-keys --with-subkey-fingerprints
---------------------------------
sec&amp;gt; rsa4096 2015-01-01 [SC] [expires: 2021-01-01]
      A134BA0260D43F8EACC889D994F13532A319EA5D
      Card serial no. = 0001 011111111
uid [ultimate] Kaspars Dambis &amp;lt;hi@kaspars.net&amp;gt;
uid [ultimate] [jpeg image of size 1790]
ssb&amp;gt; rsa4096 2015-01-01 [E] [expires: 2021-01-01]
      D2A5D7D1B4D5A75BB161AC94FD4869EA5538D9E8
      Card serial no. = 0001 011111111
ssb&amp;gt; rsa4096 2015-01-01 [A] [expires: 2021-01-01]
      0A153C055A881B4524C04B3F03142B71D9CDD878
      Card serial no. = 0001 011111111
ssb rsa4096 2019-01-01 [SE] [expires: 2021-01-01]
      A58B88D1220599B82097CBA30C8C9DD50841A889
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The master key is listed at the top with all the subkeys below it. Note that all subkeys stored on Yubikey have the &lt;code&gt;&amp;gt;&lt;/code&gt; character after their &lt;code&gt;ssb&lt;/code&gt; (secret subkey) identifier and the new one doesn’t have that. The fingerprint of the new subkey is &lt;code&gt;A58B88D1220599B82097CBA30C8C9DD50841A889&lt;/code&gt; so I use the following command to export the private key of the new subkey:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpg --armor --export-secret-subkeys FINGERPRINTOFSUBKEY!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note the exclamation mark &lt;code&gt;!&lt;/code&gt; at the end of the fingerprint — it is required to export the private key of just the particular subkey.&lt;/p&gt;

&lt;p&gt;Pipe the output to &lt;code&gt;pbcopy&lt;/code&gt; on macOS to copy it to the clipboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpg --armor --export-secret-subkeys FINGERPRINTOFSUBKEY! | pbcopy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Go to your Keybase profile, click on &lt;code&gt;edit&lt;/code&gt; next to your public key fingerprint and choose “Host an encrypted copy of my private key”, paste in the key and enter your Keybase password to encrypt the key for storage.&lt;/p&gt;

&lt;p&gt;Now you’re able to encrypt and decrypt messages using &lt;a href="https://keybase.io/encrypt#kaspars"&gt;the Keybase web UI&lt;/a&gt; or the command-line tool without having to use Yubikey.&lt;/p&gt;

</description>
      <category>cryptography</category>
      <category>privacy</category>
      <category>identity</category>
      <category>encryption</category>
    </item>
    <item>
      <title>Homebrew for Web Development</title>
      <dc:creator>Kaspars Dambis</dc:creator>
      <pubDate>Tue, 01 Jan 2019 16:45:01 +0000</pubDate>
      <link>https://dev.to/kasparsd/homebrew-for-web-development-23nj</link>
      <guid>https://dev.to/kasparsd/homebrew-for-web-development-23nj</guid>
      <description>&lt;p&gt;&lt;a href="https://brew.sh/"&gt;Homebrew&lt;/a&gt; is an awesome command-line software management solution for macOS (similar to &lt;a href="https://www.macports.org/"&gt;MacPorts&lt;/a&gt;). It can install pretty much all software required for web development and keep it organised (&lt;a href="https://docs.brew.sh/FAQ"&gt;under&lt;/a&gt;&lt;code&gt;/usr/local&lt;/code&gt;) and updated with a single command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is it awesome?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Installs packages quickly without having to download and run the installers via a web browser.&lt;/li&gt;
&lt;li&gt;Builds everything from source (unless installing from cask) which makes it secure. Compares checksums of the download files against known references.&lt;/li&gt;
&lt;li&gt;Places everything under &lt;code&gt;/usr/local&lt;/code&gt; and doesn’t use root privileges for added security and limited system access. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use It
&lt;/h2&gt;

&lt;p&gt;Here is how to install some of the most popular packages. In most cases you just need to run &lt;code&gt;brew install PACKAGENAME&lt;/code&gt; or &lt;code&gt;brew cask install PACKAGENAME&lt;/code&gt; for packages that are distributed as binaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development Tools
&lt;/h2&gt;

&lt;p&gt;Install &lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt; and &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Install &lt;a href="https://getcomposer.org/"&gt;Composer&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install composer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Editors
&lt;/h2&gt;

&lt;p&gt;Install Visual Studio Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew cask install visual-studio-code
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Install Atom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew cask install atom
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Virtual Development Environments
&lt;/h2&gt;

&lt;p&gt;Install VirtualBox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew cask install virtualbox
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Install Vagrant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew cask install vagrant
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Install Docker and Docker Compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install docker docker-compose
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Useful Commands
&lt;/h2&gt;

&lt;p&gt;Update Homebrew and the package information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Update all installed packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew upgrade
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Remove old versions of packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew cleanup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;List all installed packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Laravel Homestead for WordPress Theme and Plugin Development</title>
      <dc:creator>Kaspars Dambis</dc:creator>
      <pubDate>Sun, 09 Dec 2018 12:49:49 +0000</pubDate>
      <link>https://dev.to/kasparsd/laravel-homestead-for-wordpress-theme-and-plugin-development-1ed4</link>
      <guid>https://dev.to/kasparsd/laravel-homestead-for-wordpress-theme-and-plugin-development-1ed4</guid>
      <description>&lt;p&gt;Turns out &lt;a href="https://laravel.com/docs/5.7/homestead"&gt;Laravel Homestead&lt;/a&gt; is almost exactly &lt;a href="https://kaspars.net/blog/wordpress/wordpress-environment-composer"&gt;the development environment I was looking for&lt;/a&gt; — it &lt;a href="https://laravel.com/docs/5.7/homestead#per-project-installation"&gt;can be added as a Composer dependancy&lt;/a&gt; to any PHP project and configured using &lt;a href="https://laravel.com/docs/5.7/homestead#configuring-homestead"&gt;a simple Yaml file&lt;/a&gt;. The host machine needs only &lt;a href="https://www.vagrantup.com/"&gt;Vagrant&lt;/a&gt; and &lt;a href="https://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; See &lt;a href="https://github.com/kasparsd/widget-context-wporg/pull/48"&gt;this pull request&lt;/a&gt; for how I added Homestead to the &lt;a href="https://widgetcontext.com/"&gt;Widget Context plugin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it Works
&lt;/h2&gt;

&lt;p&gt;First, we add a very minimal &lt;code&gt;Vagrantfile&lt;/code&gt; to the project root which reads the Homestead’s configuration from &lt;code&gt;Homestead.yaml&lt;/code&gt; (could be named anything) and triggers the provisioning logic in &lt;code&gt;scripts/homestead.rb&lt;/code&gt; using the supplied configuration.&lt;/p&gt;

&lt;p&gt;We install WordPress a development dependancy in &lt;code&gt;package.json&lt;/code&gt; and configured it from the same &lt;code&gt;Vagrantfile&lt;/code&gt; using an inline shell script (and &lt;a href="https://wp-cli.org/"&gt;WP-CLI&lt;/a&gt; which comes bundled with Homestead):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.vm.provision "shell",
    inline: "wp config create",
    privileged: false
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and use a dedicated &lt;code&gt;wp-cli.yaml&lt;/code&gt; which defines the database access parameters and credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;path: /home/vagrant/code

config create:
  dbname: homestead
  dbuser: homestead
  dbpass: secret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;which are used as defaults during &lt;code&gt;wp config create&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note that &lt;code&gt;wp-cli.yaml&lt;/code&gt; lives within our theme directory so we specify the &lt;code&gt;WP_CLI_CONFIG_PATH&lt;/code&gt; environment variable in &lt;code&gt;Homestead.yaml&lt;/code&gt; which points to &lt;code&gt;wp-cli.yaml&lt;/code&gt; inside the virtual environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;p&gt;Laravel Homestead runs the provision scripts as &lt;code&gt;root&lt;/code&gt; inside the virtual machine so the regular non-privileged &lt;code&gt;vagrant&lt;/code&gt; user can’t write to disk which prevents us from downloading and setting up WordPress from within the virtual environment. This can probably be adjusted with a few additional lines of configuration in &lt;code&gt;Homestead.yaml&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>development</category>
      <category>wordpress</category>
      <category>php</category>
      <category>laraval</category>
    </item>
    <item>
      <title>Composer, Travis and Bash Source</title>
      <dc:creator>Kaspars Dambis</dc:creator>
      <pubDate>Wed, 30 May 2018 11:23:31 +0000</pubDate>
      <link>https://dev.to/kasparsd/composer-travis-and-bash-source-3e2c</link>
      <guid>https://dev.to/kasparsd/composer-travis-and-bash-source-3e2c</guid>
      <description>&lt;p&gt;Today I learned that Composer &lt;a href="https://github.com/composer/composer/blob/ea78712822e2efecf177cdd01f5c001587b47e8e/src/Composer/Util/ProcessExecutor.php#L65-L68"&gt;uses&lt;/a&gt; the &lt;a href="https://github.com/symfony/process"&gt;Symphony Process component&lt;/a&gt; for executing the shell commands defined as &lt;a href="https://getcomposer.org/doc/articles/scripts.md"&gt;Composer scripts&lt;/a&gt;. This &lt;a href="https://github.com/symfony/process/blob/4f10286a128df6b60b3706752e11f6227fcac844/Process.php#L304"&gt;internally calls&lt;/a&gt; &lt;code&gt;proc_open()&lt;/code&gt; &lt;a href="https://www.daemon-systems.org/man/popen.3.html"&gt;which&lt;/a&gt; “opens a process by creating a bidirectional pipe, forking, and invoking the shell”.&lt;/p&gt;

&lt;p&gt;I had this script failing on &lt;a href="https://travis-ci.org/xwp/wp-dev-lib/builds/385632956#L1013"&gt;a Travis build with Ubuntu 14.04.5 LTS and PHP 5.6&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"source scripts/test.sh"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;with the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sh: 1: source: not found
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Turns out that &lt;code&gt;/bin/sh&lt;/code&gt; on &lt;a href="https://travis-ci.org/xwp/wp-dev-lib/jobs/385635734#L1011"&gt;Ubuntu Trusty is a symlink&lt;/a&gt; to &lt;code&gt;/bin/dash&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls -lah /bin/sh lrwxrwxrwx 1 root root 4 Feb 19 2014 /bin/sh -\&amp;gt; dash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;source&lt;/code&gt; command is &lt;a href="https://ss64.com/bash/source.html"&gt;a bash built-in&lt;/a&gt; which simply isn’t available in dash. The solution is to replace all instances of &lt;code&gt;source something.sh&lt;/code&gt; with &lt;code&gt;. something.sh&lt;/code&gt; (note the space between the dot and the script filename).&lt;/p&gt;

</description>
      <category>development</category>
      <category>linux</category>
      <category>php</category>
    </item>
    <item>
      <title>Notes on Webpack Hot Module Replacement</title>
      <dc:creator>Kaspars Dambis</dc:creator>
      <pubDate>Tue, 27 Mar 2018 14:57:54 +0000</pubDate>
      <link>https://dev.to/kasparsd/notes-on-webpack-hot-module-replacement-551j</link>
      <guid>https://dev.to/kasparsd/notes-on-webpack-hot-module-replacement-551j</guid>
      <description>&lt;p&gt;It took me a few days to read through the relevant parts of the &lt;a href="https://github.com/webpack/webpack/tree/master/hot"&gt;webpack&lt;/a&gt; and &lt;a href="https://github.com/webpack/webpack-dev-server/tree/master/client-src/default"&gt;webpack-dev-server&lt;/a&gt; source code and figure out how to enable &lt;a href="https://webpack.js.org/concepts/hot-module-replacement/"&gt;Webpack Hot Module Replacement&lt;/a&gt; for any project that doesn’t use the &lt;code&gt;webpack-dev-server&lt;/code&gt; for serving the JS bundle. I wanted to use HMR with Vagrant running Docker for a local WordPress theme development workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does It Work?
&lt;/h2&gt;

&lt;p&gt;The main requirement for HMR is for the browser to receive notifications when a new bundle file is built as you edit the source files. Unfortunately, the default notification logic in &lt;code&gt;webpack/dev-server.js&lt;/code&gt; relies &lt;a href="https://github.com/webpack/webpack/blob/7f11210cffc58820b4cdd086934398306882c338/hot/dev-server.js#L51-L57"&gt;on the native Node.js events&lt;/a&gt; instead of the websocket messages sent by the &lt;code&gt;webpack-dev-server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Therefore, HMR will never work if the bundle file is not server by the same &lt;code&gt;webpack-dev-server&lt;/code&gt; which also watches for the file changes and triggers the builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Native Node Events and Websockets
&lt;/h2&gt;

&lt;p&gt;Fortunately, the &lt;code&gt;webpack-dev-server&lt;/code&gt; also &lt;a href="https://github.com/webpack/webpack-dev-server/blob/ef5598440ebf6847ac02a2d44d7fec70efee0aa1/lib/Server.js#L569-L623"&gt;starts a websocket server&lt;/a&gt; at &lt;code&gt;/sockjs-node&lt;/code&gt; and &lt;a href="https://github.com/webpack/webpack-dev-server/blob/ef5598440ebf6847ac02a2d44d7fec70efee0aa1/lib/Server.js#L601-L607"&gt;sends out a few messages to the listening clients&lt;/a&gt; to indicate that it will report changes related to HMR.&lt;/p&gt;

&lt;p&gt;So your bundled script needs only two things to work with the data coming from the websocket server:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a websocket client that acts on the incoming messages, and&lt;/li&gt;
&lt;li&gt;a hot module replacement logic that can apply the incoming code patches.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;webpack-dev-server&lt;/code&gt; comes with &lt;a href="https://github.com/webpack/webpack-dev-server/blob/ef5598440ebf6847ac02a2d44d7fec70efee0aa1/client-src/default/index.js"&gt;a websocket client&lt;/a&gt; &lt;code&gt;webpack-dev-server/client&lt;/code&gt; which is &lt;a href="https://github.com/webpack/webpack-dev-server/blob/3a7f7d543889707725e5d60fc21fe2de975d627d/bin/webpack-dev-server.js#L375"&gt;injected into your bundle automatically&lt;/a&gt; if you enable &lt;code&gt;--hot&lt;/code&gt; and &lt;a href="https://github.com/webpack/webpack-dev-server/blob/3a7f7d543889707725e5d60fc21fe2de975d627d/lib/util/addDevServerEntrypoints.js#L8-L39"&gt;don’t explicitly set&lt;/a&gt; &lt;code&gt;--inline&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;. The websocket client parses all incoming messages from the dev server and &lt;a href="https://github.com/webpack/webpack-dev-server/blob/ab4eeb007283fdf3e8950ff1f3ff8150a4812061/client-src/default/index.js#L216"&gt;triggers a native Node.js event&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hotEmitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack/hot/emitter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;hotEmitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpackHotUpdate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and also &lt;a href="https://github.com/webpack/webpack-dev-server/blob/ab4eeb007283fdf3e8950ff1f3ff8150a4812061/client-src/default/index.js#L219"&gt;sends out a websocket message&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// broadcast update to window&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpackHotUpdate${currentHash}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;However, only the native Node.js event &lt;code&gt;webpackHotUpdate&lt;/code&gt; is used by the &lt;a href="https://github.com/webpack/webpack/blob/7f11210cffc58820b4cdd086934398306882c338/hot/dev-server.js"&gt;HMR client side logic&lt;/a&gt; in &lt;code&gt;webpack/hot/dev-server&lt;/code&gt; to apply the patch. This is why your bundle will never receive notifications about HMR events if it’s not served by the &lt;code&gt;webpack-dev-server&lt;/code&gt; because native Node events are not supported outside the Node environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Fix It?
&lt;/h2&gt;

&lt;p&gt;Clone the HMR client side logic in &lt;code&gt;webpack/hot/dev-server&lt;/code&gt; and make it listen for &lt;code&gt;webpackHotUpdate&lt;/code&gt; messages on a websocket instead of the native node events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&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;e&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpackHotUpdate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;hmrHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpackHotUpdate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Run check() from webpack/hot/dev-server.js&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>webpack</category>
      <category>node</category>
    </item>
    <item>
      <title>Poker 3 Mechanical Keyboard for Mac</title>
      <dc:creator>Kaspars Dambis</dc:creator>
      <pubDate>Tue, 15 Aug 2017 07:20:07 +0000</pubDate>
      <link>https://dev.to/kasparsd/poker-3-mechanical-keyboard-for-mac-5f9b</link>
      <guid>https://dev.to/kasparsd/poker-3-mechanical-keyboard-for-mac-5f9b</guid>
      <description>&lt;p&gt;Recently I got my first mechanical keyboard &lt;a href="https://kaspars.net/go/poker3-keyboard" rel="noopener noreferrer"&gt;Poker 3 with the Cherry MX Brown switches&lt;/a&gt;. Here is how I configured the keyboard to match the familiar Mac layout:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.keyboard-layout-editor.com/#/gists/a7fa3da73ef9361789d91a34bd8cb4d4" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsy6ge8slvi2xg3grlq80.png" alt="Mac Layout for Poker 3 Mechanical Keyboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;FN&lt;/code&gt;, &lt;code&gt;PN&lt;/code&gt;, &lt;code&gt;Ctrl&lt;/code&gt; and &lt;code&gt;Shift&lt;/code&gt; as arrow keys. You’ll have to learn to use the left &lt;code&gt;Shift&lt;/code&gt; for capitalising letters which I’m still getting used to.&lt;/li&gt;
&lt;li&gt;Map &lt;code&gt;CapsLock&lt;/code&gt; to &lt;code&gt;FN&lt;/code&gt; because &lt;code&gt;FN&lt;/code&gt; is now the left arrow key.&lt;/li&gt;
&lt;li&gt;Map left &lt;code&gt;Alt&lt;/code&gt; to &lt;code&gt;Command ⌘&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Map left &lt;code&gt;Win&lt;/code&gt; to &lt;code&gt;Option ⌥&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keyboard Layers
&lt;/h2&gt;

&lt;p&gt;The keyboard has three programmable layouts or layers. I’m using layer 3 (red LED) to store this configuration. Use &lt;code&gt;FN&lt;/code&gt; + &lt;code&gt;&amp;gt;&lt;/code&gt; to switch to layer 3. Note that the default layer 1 can’t be configured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable the Arrow Keys
&lt;/h2&gt;

&lt;p&gt;Next we want to turn &lt;code&gt;CapsLock&lt;/code&gt; into &lt;code&gt;FN&lt;/code&gt; because &lt;code&gt;FN&lt;/code&gt; will become the left arrow key. Switch the 3rd dip switch on the back of the keyboard to enable this.&lt;/p&gt;

&lt;p&gt;Press left &lt;code&gt;Win&lt;/code&gt;, &lt;code&gt;Space&lt;/code&gt; + right &lt;code&gt;Alt&lt;/code&gt; to turn right &lt;code&gt;FN&lt;/code&gt; (left), &lt;code&gt;PN&lt;/code&gt; (down), &lt;code&gt;Shift&lt;/code&gt; (up) and &lt;code&gt;Ctrl&lt;/code&gt; (right) into permanent arrow keys. Please note that this setting isn’t preserved between keyboard reboots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Swap Win/Cmd and Alt
&lt;/h2&gt;

&lt;p&gt;Finally we need to switch the left &lt;code&gt;Win&lt;/code&gt; and &lt;code&gt;Alt&lt;/code&gt; keys to match the Mac layout where the &lt;code&gt;Win&lt;/code&gt; or &lt;code&gt;Command&lt;/code&gt; keys are right next to the spacebar. Enter the programming mode by pressing &lt;code&gt;FN&lt;/code&gt; + right &lt;code&gt;Ctrl&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Press &lt;code&gt;Alt&lt;/code&gt; then &lt;code&gt;Win&lt;/code&gt; then &lt;code&gt;PN&lt;/code&gt; to set the &lt;code&gt;Command&lt;/code&gt; key.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Win&lt;/code&gt; then &lt;code&gt;Alt&lt;/code&gt; then &lt;code&gt;PN&lt;/code&gt; to set the &lt;code&gt;Option&lt;/code&gt; key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used this &lt;a href="https://github.com/davidjenni/pok3r-layouts" rel="noopener noreferrer"&gt;Pok3r layout guide&lt;/a&gt; for reference.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>tools</category>
      <category>mechanicalkeyboard</category>
    </item>
  </channel>
</rss>
