<?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: tetsu</title>
    <description>The latest articles on DEV Community by tetsu (@wtetsu).</description>
    <link>https://dev.to/wtetsu</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%2F42940%2F271d925f-17c8-41b5-bb38-f632325c3b3e.png</url>
      <title>DEV Community: tetsu</title>
      <link>https://dev.to/wtetsu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wtetsu"/>
    <language>en</language>
    <item>
      <title>Lessons learned from my Chrome extension migration to Manifest V3</title>
      <dc:creator>tetsu</dc:creator>
      <pubDate>Sat, 14 May 2022 17:18:41 +0000</pubDate>
      <link>https://dev.to/wtetsu/lessons-learned-from-my-chrome-extension-migration-to-manifest-v3-76j</link>
      <guid>https://dev.to/wtetsu/lessons-learned-from-my-chrome-extension-migration-to-manifest-v3-76j</guid>
      <description>&lt;p&gt;This is a record of the Manifest V3 migration of &lt;a href="https://mouse-dictionary.netlify.app/en/"&gt;Mouse Dictionary&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Manifest V3?
&lt;/h3&gt;

&lt;p&gt;Existing Chrome extensions will stop working in 2023, unless migrated to Manifest V3.&lt;/p&gt;

&lt;p&gt;(&lt;a href="https://developer.chrome.com/docs/extensions/mv3/mv2-sunset/"&gt;Manifest V2 support timeline&lt;/a&gt;)&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R3mXBa5v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cqm705raooz51ppmph7k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R3mXBa5v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cqm705raooz51ppmph7k.png" alt="Manifest V2 support timeline" width="880" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is not the main topic here, so please refer to &lt;a href="https://developer.chrome.com/docs/extensions/mv3/intro/"&gt;official documentation&lt;/a&gt; and &lt;a href="https://dev.to/search?q=manifest%20v3"&gt;existing articles&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;Mouse Dictionary is an Manifest V2 extension released in 2018, so sooner or later it had to be migrated to V3.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cross-browser issue
&lt;/h3&gt;

&lt;p&gt;As of May 2022, Firefox does not yet support Manifest V3, so the Firefox version must remain V2. This means that cross-browser extensions need to support both V2 and V3, which is quite a hassle.&lt;/p&gt;

&lt;p&gt;Mouse Dictionary has tried to maintain Firefox support while migrating the Chrome version to V3, so please refer to this page if you are in a similar situation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Actual migration
&lt;/h2&gt;

&lt;p&gt;There is an official migration guide available.&lt;br&gt;
&lt;a href="https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/"&gt;https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following is a record of the actual mundane migration work.&lt;/p&gt;
&lt;h3&gt;
  
  
  Preparation 1: Eliminate eval
&lt;/h3&gt;

&lt;p&gt;Manifest V3 has more restrictions on &lt;a href="https://developer.chrome.com/docs/extensions/mv3/mv3-migration-checklist/#:~:text=Are%20you%20executing%20remote%20code%20or%20arbitrary%20strings%3F"&gt;eval() and new Function()&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Mouse Dictionary was using &lt;a href="https://github.com/twitter/hogan.js/"&gt;Hogan.js&lt;/a&gt; as its Mustache engine, but because it was using new Function() internally, it was an obstacle in the V3 migration process.&lt;/p&gt;

&lt;p&gt;Therefore, I eliminated &lt;code&gt;new Function()&lt;/code&gt; by moving from Hogan.js to &lt;a href="https://github.com/janl/mustache.js/"&gt;mustache.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hogan.js, mustache.js, and Handlebars were originally considered as candidates for the Mustache engine, and Hogan.js was the fastest for the Mouse Dictionary usage.&lt;/p&gt;

&lt;p&gt;I verified that mustache.js is compatible and fast enough so that it's capable of being the extension's new Mustache engine.&lt;/p&gt;
&lt;h3&gt;
  
  
  Preparation 2: Build for each browser
&lt;/h3&gt;

&lt;p&gt;If you want to migrate the Chrome version to V3 and keep the Firefox version, it is not possible to prepare a single manifest.json for all browsers. package at build time.&lt;/p&gt;

&lt;p&gt;In the case of the Mouse Dictionary, the appropriate manifest.json is included in the package at build time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oMRYx5No--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8y33p06acp20lvrx31lf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oMRYx5No--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8y33p06acp20lvrx31lf.png" alt="manifest" width="373" height="239"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Change code and configurations
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/wtetsu/mouse-dictionary/pull/75/files"&gt;View changes on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;
&lt;h4&gt;
  
  
  chrome.browserAction -&amp;gt; chrome.action
&lt;/h4&gt;

&lt;p&gt;In V3, &lt;code&gt;chrome.browserAction&lt;/code&gt; needs to be changed to &lt;code&gt;chrome.action&lt;/code&gt;. Conversely, in V2 (Firefox), there is no &lt;code&gt;chrome.action&lt;/code&gt; and &lt;code&gt;chrome.browserAction&lt;/code&gt; needs to be used. In order to keep it cross-browser, some kind of device is needed.&lt;/p&gt;

&lt;p&gt;There are many ways to realize this, but since this is the only code where cross-browser compatibility was necessary, I simply used a webpack constant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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="nx"&gt;BROWSER&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CHROME&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClicked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tab&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scripting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main.js&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="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="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClicked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;. /main.js&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="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;By the way, the code below also works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClicked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tab&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scripting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main.js&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="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="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClicked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;. /main.js&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="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;The former is a bit more economical because unneeded processing disappears at build time.&lt;/p&gt;

&lt;h3&gt;
  
  
  chrome.extension -&amp;gt; chrome.runtime
&lt;/h3&gt;

&lt;p&gt;Since both &lt;code&gt;chrome.extension&lt;/code&gt; and &lt;code&gt;chrome.runtime&lt;/code&gt; work in V2 (Firefox), I simply replaced them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- const url = chrome.extension.getURL(fname);
&lt;/span&gt;&lt;span class="gi"&gt;+ const url = chrome.runtime.getURL(fname);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  manifest.json
&lt;/h3&gt;

&lt;p&gt;permissions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- "permissions": ["storage", "unlimitedStorage", "activeTab"],
&lt;/span&gt;&lt;span class="gi"&gt;+ "permissions": ["storage", "unlimitedStorage", "activeTab", "scripting"],
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;background&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   "background": {
&lt;span class="gd"&gt;- "scripts": ["background.js"],
- "persistent": false
&lt;/span&gt;&lt;span class="gi"&gt;+ "service_worker": "background.js"
&lt;/span&gt;   },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   "commands": {
&lt;span class="gd"&gt;- "_execute_browser_action": {
&lt;/span&gt;&lt;span class="gi"&gt;+ "_execute_action": {
&lt;/span&gt;       "description": "Activate the extension"
     },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;web_accessible_resources&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- "web_accessible_resources": ["data/*.json"]
&lt;/span&gt;&lt;span class="gi"&gt;+ "web_accessible_resources": [
+ {
+ "resources": ["data/rule.json", "data/dict*.json"],
+ "matches": ["&amp;lt;all_urls&amp;gt;"]
+ }
+ ]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Unresolved issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Doesn't work on local files
&lt;/h3&gt;

&lt;p&gt;Mouse Dictionary used to work on local HTML documents opened in Chrome, but it is no longer working after the V3 migration. I know the cause, &lt;code&gt;web_accessible_resources&lt;/code&gt; does not work in tabs opened with &lt;code&gt;file:///~&lt;/code&gt;, but I can't find any official information about it, so I don't know if this is an intended change in V3 or a bug in Chrome, so I am waiting and seeing.&lt;/p&gt;

&lt;p&gt;(FYI)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/69736887/web-accessible-resources-manifest-key-is-ignored-if-respective-resources-are-i"&gt;https://stackoverflow.com/questions/69736887/web-accessible-resources-manifest-key-is-ignored-if-respective-resources-are-i&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1264366"&gt;https://bugs.chromium.org/p/chromium/issues/detail?id=1264366&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Shortcut key settings behavior is strange
&lt;/h3&gt;

&lt;p&gt;Mouse Dictionary can set shortcut keys, but the behavior is not quite stable after the migration to V3. It seems that shortcut keys are not reflected immediately after they are set, but are reflected after restarting Chrome. There is nothing that can be done on the extension developer side, so we are just waiting to see how it goes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uyDcVKsL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ydzy62gng46qo5fafnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uyDcVKsL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ydzy62gng46qo5fafnf.png" alt="Shortcut key" width="766" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Impressions
&lt;/h2&gt;

&lt;p&gt;This is only a case of Mouse Dictionary, but the amount of code changed was small. However, there is still little information on the web about the experience of Manifest V3 migration, so it is difficult to research, trial and error, and verify how to change the code to make it work as well as V2.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>chrome</category>
      <category>firefox</category>
    </item>
    <item>
      <title>epo: Handy Unix epoch time converter in Rust</title>
      <dc:creator>tetsu</dc:creator>
      <pubDate>Sun, 08 May 2022 06:17:47 +0000</pubDate>
      <link>https://dev.to/wtetsu/epo-handy-unix-epoch-time-converter-in-rust-2ol7</link>
      <guid>https://dev.to/wtetsu/epo-handy-unix-epoch-time-converter-in-rust-2ol7</guid>
      <description>&lt;h2&gt;
  
  
  epo
&lt;/h2&gt;

&lt;p&gt;epo is a handy Unix time &amp;lt;-&amp;gt; date string converter that also taking multiple time zones into account. Written in Rust.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wtetsu/epo"&gt;GitHub repository&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ epo 1648771200 1648771200+86400 greenwich
|      Epoch |                Greenwich |
| ---------- | ------------------------ |
| 1648771200 | 2022-04-01T00:00:00+0000 |
| 1648857600 | 2022-04-02T00:00:00+0000 |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew tap wtetsu/epo
brew install epo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or &lt;a href="https://github.com/wtetsu/epo/releases"&gt;download binaries&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  UNIX time → date string
&lt;/h3&gt;

&lt;p&gt;Basically just specify Unix time and timezone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ epo 1648771200 greenwich
|      Epoch |                Greenwich |
| ---------- | ------------------------ |
| 1648771200 | 2022-04-01T00:00:00+0000 |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Multiple UNIX times x Multiple time zones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ epo 1648771200 1648771200+86400 "1648771200+86400*2" greenwich +0900 new_y
|      Epoch |                Greenwich |                    +0900 |         America/New_York |
| ---------- | ------------------------ | ------------------------ | ------------------------ |
| 1648771200 | 2022-04-01T00:00:00+0000 | 2022-04-01T09:00:00+0900 | 2022-03-31T20:00:00-0400 |
| 1648857600 | 2022-04-02T00:00:00+0000 | 2022-04-02T09:00:00+0900 | 2022-04-01T20:00:00-0400 |
| 1648944000 | 2022-04-03T00:00:00+0000 | 2022-04-03T09:00:00+0900 | 2022-04-02T20:00:00-0400 |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Options can be in any order. You can also write a formula: add 86400 to get one day later.&lt;/p&gt;

&lt;p&gt;When specifying &lt;a href="https://en.wikipedia.org/wiki/Tz_database"&gt;tz database&lt;/a&gt; names, it is OK to use forward matching (here "new_y" is assumed to be America/New_York).&lt;/p&gt;




&lt;p&gt;You can also write JavaScript code (&lt;a href="https://github.com/boa-dev/boa"&gt;boa&lt;/a&gt; is embedded)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;epo "range(10).map(i=&amp;gt;1647165300+i*60)" los_angeles phoenix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prints a Markdown formatted table, so it can be pasted here as is.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Epoch&lt;/th&gt;
&lt;th&gt;America/Los_Angeles&lt;/th&gt;
&lt;th&gt;America/Phoenix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1647165300&lt;/td&gt;
&lt;td&gt;2022-03-13T01:55:00-0800&lt;/td&gt;
&lt;td&gt;2022-03-13T02:55:00-0700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1647165360&lt;/td&gt;
&lt;td&gt;2022-03-13T01:56:00-0800&lt;/td&gt;
&lt;td&gt;2022-03-13T02:56:00-0700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1647165420&lt;/td&gt;
&lt;td&gt;2022-03-13T01:57:00-0800&lt;/td&gt;
&lt;td&gt;2022-03-13T02:57:00-0700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1647165480&lt;/td&gt;
&lt;td&gt;2022-03-13T01:58:00-0800&lt;/td&gt;
&lt;td&gt;2022-03-13T02:58:00-0700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1647165540&lt;/td&gt;
&lt;td&gt;2022-03-13T01:59:00-0800&lt;/td&gt;
&lt;td&gt;2022-03-13T02:59:00-0700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1647165600&lt;/td&gt;
&lt;td&gt;2022-03-13T03:00:00-0700&lt;/td&gt;
&lt;td&gt;2022-03-13T03:00:00-0700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1647165660&lt;/td&gt;
&lt;td&gt;2022-03-13T03:01:00-0700&lt;/td&gt;
&lt;td&gt;2022-03-13T03:01:00-0700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1647165720&lt;/td&gt;
&lt;td&gt;2022-03-13T03:02:00-0700&lt;/td&gt;
&lt;td&gt;2022-03-13T03:02:00-0700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1647165780&lt;/td&gt;
&lt;td&gt;2022-03-13T03:03:00-0700&lt;/td&gt;
&lt;td&gt;2022-03-13T03:03:00-0700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1647165840&lt;/td&gt;
&lt;td&gt;2022-03-13T03:04:00-0700&lt;/td&gt;
&lt;td&gt;2022-03-13T03:04:00-0700&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the above table, you can see the moment when Los Angeles enters daylight saving time: -0800 becomes -0700, and suddenly it is 03:00. Incidentally, Phoenix is known as an area in the U.S. where daylight saving time is not adopted, and it remains at -0700 all the time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Date string → UNIX time
&lt;/h3&gt;

&lt;p&gt;It interprets &lt;a href="https://en.wikipedia.org/wiki/ISO_8601"&gt;ISO 8601&lt;/a&gt; formats appropriately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ epo 2022-04-01 2022-05-01T12:30 2022-06-15T12:30:45 tokyo hawaii gmt
|                Date | Asia/Tokyo |  US/Hawaii |        GMT |
| ------------------- | ---------- | ---------- | ---------- |
| 2022-04-01T00:00:00 | 1648738800 | 1648807200 | 1648771200 |
| 2022-05-01T12:30:00 | 1651375800 | 1651444200 | 1651408200 |
| 2022-06-15T12:30:45 | 1655263845 | 1655332245 | 1655296245 |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>rust</category>
    </item>
    <item>
      <title>👁️Accelerates your coding using Gaze</title>
      <dc:creator>tetsu</dc:creator>
      <pubDate>Fri, 03 Dec 2021 17:09:14 +0000</pubDate>
      <link>https://dev.to/wtetsu/accelerates-your-coding-using-gaze-17p8</link>
      <guid>https://dev.to/wtetsu/accelerates-your-coding-using-gaze-17p8</guid>
      <description>&lt;p&gt;Software development often forces us to manually run the same commands over and over.&lt;/p&gt;

&lt;p&gt;👁️&lt;a href="https://github.com/wtetsu/gaze" rel="noopener noreferrer"&gt;Gaze&lt;/a&gt; runs commands for you, right after you save a file.&lt;/p&gt;

&lt;p&gt;It significantly helps you stay focused on writing code!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fh36f1v8ua1kq0m7xxo68.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fh36f1v8ua1kq0m7xxo68.gif" alt="Gaze" width="792" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gaze is designed as a CLI tool to streamline your coding process.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to use, out-of-the-box experience&lt;/li&gt;
&lt;li&gt;Super quick response time&lt;/li&gt;
&lt;li&gt;Language- and editor-agnostic&lt;/li&gt;
&lt;li&gt;Flexible configuration options&lt;/li&gt;
&lt;li&gt;Useful advanced features&lt;/li&gt;
&lt;li&gt;Multiplatform support (macOS, Windows, Linux)&lt;/li&gt;
&lt;li&gt;Efficient parallel execution handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  👁️Installation
&lt;/h2&gt;

&lt;p&gt;On macOS&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Windows/Linux: download binary &lt;a href="https://github.com/wtetsu/gaze/releases" rel="noopener noreferrer"&gt;https://github.com/wtetsu/gaze/releases&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  👁️How to use
&lt;/h2&gt;

&lt;p&gt;Gaze is incredibly easy to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gaze .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, open your preferred editor in another terminal and start coding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vi a.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  👁️More examples
&lt;/h2&gt;

&lt;p&gt;Gaze at one file. You can just specify file names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gaze a.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Gaze doesn't require complicated file specification options. You can leverage familiar shell wildcards like &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;**&lt;/code&gt;, and &lt;code&gt;?&lt;/code&gt;. &lt;strong&gt;No need to memorize Gaze-specific command-line options!&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gaze "*.py"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Gaze at subdirectories. Runs a modified file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gaze "src/**/*.rb"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Specify an arbitrary command by -c option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gaze "src/**/*.js" -c "eslint {{file}}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Kill the previous one before launching a new process. This is useful if you are writing a server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gaze -r server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Kill an ongoing process after 1000(ms). This is useful if you love infinite loops.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gaze -t 1000 complicated.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Specify multiple commands in quotations, separated by newlines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gaze "*.cpp" -c "gcc {{file}} -o a.out
ls -l a.out
./a.out"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  👁️Motivation
&lt;/h2&gt;

&lt;p&gt;I frequently write Python scripts to automate tasks. I'd create &lt;code&gt;a.py&lt;/code&gt;, write a few lines, and then run &lt;code&gt;python a.py&lt;/code&gt;. Often, the result wasn't perfect, so I'd edit &lt;code&gt;a.py&lt;/code&gt; again and re-run &lt;code&gt;python a.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This cycle repeated endlessly...&lt;/p&gt;

&lt;p&gt;I realized I was constantly switching between my editor and terminal, typing the same command over and over again. It was a huge waste of time and energy!&lt;/p&gt;

&lt;p&gt;That's why I developed Gaze: to automate command execution and eliminate this repetitive, manual process.&lt;/p&gt;

&lt;p&gt;Learn more at: &lt;a href="https://github.com/wtetsu/gaze" rel="noopener noreferrer"&gt;https://github.com/wtetsu/gaze&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
    </item>
  </channel>
</rss>
