<?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>Forem: WH yang</title>
    <description>The latest articles on Forem by WH yang (@whyang9701).</description>
    <link>https://forem.com/whyang9701</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%2F3480158%2F2e5c12f0-932a-4ce9-97ff-275db0afb4c7.png</url>
      <title>Forem: WH yang</title>
      <link>https://forem.com/whyang9701</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/whyang9701"/>
    <language>en</language>
    <item>
      <title>My Solution to Deno Watch Mode Signal Handling</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Sun, 14 Dec 2025 06:13:01 +0000</pubDate>
      <link>https://forem.com/whyang9701/my-solution-of-deno-watch-mode-signal-handling-4ejp</link>
      <guid>https://forem.com/whyang9701/my-solution-of-deno-watch-mode-signal-handling-4ejp</guid>
      <description>&lt;p&gt;To fix the Deno watch mode &lt;a href="https://github.com/denoland/deno/issues/30912" rel="noopener noreferrer"&gt;issue&lt;/a&gt;, I &lt;a href="https://dev.to/whyang9701/progress-update-of-deno-watch-mode-issue-464e"&gt;researched&lt;/a&gt; both Deno and Node.js. I want to use Node.js's approach but implement it in Rust for Deno.&lt;/p&gt;

&lt;p&gt;Rust doesn't have a built-in &lt;a href="https://nodejs.org/api/process.html#processkillpid-signal" rel="noopener noreferrer"&gt;&lt;code&gt;process.kill()&lt;/code&gt;&lt;/a&gt; function like JavaScript to send signals, but we can simulate this using channels. When creating a &lt;a href="https://tokio.rs/tokio/tutorial/channels" rel="noopener noreferrer"&gt;channel&lt;/a&gt;, we get a sender (Tx) and a receiver (Rx) that work together, which helps avoid race conditions.&lt;/p&gt;

&lt;p&gt;I created a &lt;a href="https://github.com/denoland/deno/pull/31602" rel="noopener noreferrer"&gt;pull request (PR)&lt;/a&gt; that uses a channel to allow the worker process to inform the watcher process when the INT signal is handled, instead of letting the watcher process handle and ignore it.&lt;/p&gt;

&lt;p&gt;Here’s how I set it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a channel before starting the worker process&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctrl_c_tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ctrl_c_rx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;mpsc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;unbounded_channel&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;watcher_communicator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;WatcherCommunicator&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WatcherCommunicatorOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Pass the sender to the worker&lt;/span&gt;
      &lt;span class="n"&gt;ctrl_c_tx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ctrl_c_tx&lt;/span&gt;&lt;span class="nf"&gt;.clone&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;WatcherCommunicator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Public function to inform the watcher process&lt;/span&gt;
   &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;handle_ctrl_c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;SendError&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.ctrl_c_tx&lt;/span&gt;&lt;span class="nf"&gt;.send&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;I also removed the INT handler from the watcher process and replaced it with the channel's receive handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// _ = deno_signals::ctrl_c() =&amp;gt; {&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctrl_c_rx&lt;/span&gt;&lt;span class="nf"&gt;.recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&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;This approach requires a better understanding of how the signal API is implemented in Deno. The &lt;a href="https://github.com/denoland/deno/blob/d17ae8ca9b0777c64934ae332b83ff0996a9fae0/ext/os/ops/signal.rs#L4" rel="noopener noreferrer"&gt;Signal.rs&lt;/a&gt; file shows how the &lt;a href="https://docs.deno.com/examples/os_signals_tutorial/" rel="noopener noreferrer"&gt;Deno.addSignalListener() or process.on(signalName, callback)&lt;/a&gt; API works. I'm still looking for the right place to trigger the channel sender without breaking existing behavior.&lt;/p&gt;

&lt;p&gt;Implementing this feature has been the most challenging task I've faced recently. Three months ago, I had never written a line of Rust, but now I'm working on something significant written in Rust. I can't believe how far I've come since starting my open-source journey. I want to thank my professor, &lt;a href="https://github.com/humphd" rel="noopener noreferrer"&gt;humphd&lt;/a&gt;, for encouraging everyone in class to get involved in the open-source community. Everyone has grown so much this semester; this has been the best course I've taken.&lt;/p&gt;

</description>
      <category>deno</category>
    </item>
    <item>
      <title>Update on Deno Watch Mode Signal Issue.</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Sun, 14 Dec 2025 04:34:05 +0000</pubDate>
      <link>https://forem.com/whyang9701/progress-update-of-deno-watch-mode-issue-464e</link>
      <guid>https://forem.com/whyang9701/progress-update-of-deno-watch-mode-issue-464e</guid>
      <description>&lt;p&gt;There is an &lt;a href="https://github.com/denoland/deno/issues/30912" rel="noopener noreferrer"&gt;issue&lt;/a&gt; in Deno where the signal handler doesn't work in watch mode. I believe this happens because the Deno file watcher doesn't send the INT signal to the worker, causing it to just stop instead. I'm confident about this now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/denoland/deno/blob/d17ae8ca9b0777c64934ae332b83ff0996a9fae0/cli/util/file_watcher.rs#L383C4-L392C38" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt; &lt;span class="nd"&gt;select!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;receiver_future&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;deno_signals&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ctrl_c&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&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="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(());&lt;/span&gt; &lt;span class="c1"&gt;// this line handled the INT signal and fulfilled select!&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;restart_rx&lt;/span&gt;&lt;span class="nf"&gt;.recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print_after_restart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;operation_future&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Deno code, the &lt;code&gt;select!&lt;/code&gt; statement acts like JavaScript's Promise.any. It completes as soon as the INT signal is ignored. The &lt;code&gt;operation_future&lt;/code&gt; runs the user's JavaScript or TypeScript files, but it doesn't get a chance to activate the user-defined listener.&lt;/p&gt;

&lt;p&gt;I also looked at how Node.js handles signals in watch mode. It sends the signal to the worker and waits for the worker to finish.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nodejs/node/blob/4f24aff94ad9160bceaf9dcc9cf5235a65f01029/lib/internal/main/watch_mode.js#L121" rel="noopener noreferrer"&gt;source&lt;/a&gt;&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;killAndWait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kKillSignal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;force&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;removeAllListeners&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;child&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="p"&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;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;killed&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;exited&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;force&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="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onExit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;exitCode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;onExit&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;exitCode&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;I plan to use a similar approach: send the signal to the worker to trigger the listener and then wait for it to complete.&lt;/p&gt;

</description>
      <category>deno</category>
    </item>
    <item>
      <title>The Difference of Deno and Node Handle Signals in Watch Mode</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Sat, 13 Dec 2025 05:15:29 +0000</pubDate>
      <link>https://forem.com/whyang9701/the-difference-of-deno-and-node-handle-signals-in-watch-mode-kcm</link>
      <guid>https://forem.com/whyang9701/the-difference-of-deno-and-node-handle-signals-in-watch-mode-kcm</guid>
      <description>&lt;p&gt;I recently started learning Rust and tried to contribute to some projects that use it, specifically &lt;a href="https://github.com/tryandromeda/andromeda" rel="noopener noreferrer"&gt;Andromeda&lt;/a&gt; and &lt;a href="https://github.com/denoland/deno" rel="noopener noreferrer"&gt;Deno&lt;/a&gt;. Both projects are JavaScript/TypeScript runtimes and have some problems with their watch mode.&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://github.com/denoland/deno/issues/30912" rel="noopener noreferrer"&gt;issue&lt;/a&gt; shows how the two runtimes behave differently. When using Node.js in watch mode, it handles the INT signal correctly, but Deno just exits without doing that. This can lead to some resources not being released properly. I plan to submit a pull request (PR) to fix this issue because I have worked on related PRs before.&lt;/p&gt;

&lt;p&gt;I think the problem is that Deno doesn’t pass the INT signal to the process running the JavaScript/TypeScript code, so it just stops immediately.&lt;/p&gt;

&lt;p&gt;I found some &lt;a href="https://github.com/denoland/deno/blob/ee7c483df4bdc271edd0264c66246cc3297c6351/cli/tools/run/mod.rs#L165C1-L167C25" rel="noopener noreferrer"&gt;notes&lt;/a&gt; left by developers about three years ago while exploring the repo. While exit codes and signal handling aren’t the same, both should be managed properly for the JavaScript/Rust processes. I will keep trying to figure this out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TODO(bartlomieju): this function is not handling `exit_code` set by the runtime&lt;/span&gt;
&lt;span class="c1"&gt;// code properly.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_with_watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>deno</category>
    </item>
    <item>
      <title>Enhance Deno.watchFs</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Fri, 12 Dec 2025 15:37:59 +0000</pubDate>
      <link>https://forem.com/whyang9701/enhance-denowatchfs-1lo6</link>
      <guid>https://forem.com/whyang9701/enhance-denowatchfs-1lo6</guid>
      <description>&lt;p&gt;Deno has a built-in API called &lt;a href="https://docs.deno.com/api/deno/~/Deno.watchFs" rel="noopener noreferrer"&gt;watchFs&lt;/a&gt; that tracks changes in files and folders. It can also watch subfolders automatically. However, some files, like those in the .git folder, change a lot. It would be helpful to have an "ignore" option that skips events from .git and its subfolders. I found this &lt;a href="https://github.com/denoland/deno/issues/26272" rel="noopener noreferrer"&gt;issue&lt;/a&gt; suggesting this idea.&lt;/p&gt;

&lt;p&gt;I created a &lt;a href="https://github.com/denoland/deno/pull/31582" rel="noopener noreferrer"&gt;pull request&lt;/a&gt; for this new ignore option. This update changes &lt;a href="https://github.com/denoland/deno/blob/3a6157a4aadac56d13cff76da4d8078ac5924699/runtime/ops/fs_events.rs" rel="noopener noreferrer"&gt;fs_events.rs&lt;/a&gt;, which is the Rust code behind the Deno.watchFs API. My update checks if an event belongs to any ignored paths. If it does, it won’t send that event back to the JavaScript layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ignore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ignore&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.any&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="py"&gt;.paths&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.any&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;event_path&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nn"&gt;same_file&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;is_same_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;starts_with_canonicalized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&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;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ignore&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.any&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;path&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original proposal also mentioned that watching a folder with many nested subfolders can be slow. I tried a different method that manually walk each folder and creates watchers but skips ignored paths. However, this method was slower than the simple filter I implemented. Managing multiple file watchers is less efficient than one that watches everything at once for OS. Also, new folders won't be detected if they weren't included in the initial walk.&lt;/p&gt;

</description>
      <category>deno</category>
    </item>
    <item>
      <title>Deno CLI Not Parsing Options After Target Files</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Tue, 25 Nov 2025 20:17:32 +0000</pubDate>
      <link>https://forem.com/whyang9701/deno-cli-not-parsing-options-after-target-files-4k5e</link>
      <guid>https://forem.com/whyang9701/deno-cli-not-parsing-options-after-target-files-4k5e</guid>
      <description>&lt;p&gt;&lt;strong&gt;Deno Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deno is a JavaScript and TypeScript runtime built with Rust. It offers a better development experience with built-in tools like a linter, formatter, and test runner. Deno is also compatible with Node.js and npm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Breaking Changes in Deno 3.0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In version 3.0, Deno introduced a change for command-line arguments. Users now need to put &lt;code&gt;--&lt;/code&gt; before their arguments to avoid confusion. You can find more details in these issues: &lt;a href="https://github.com/denoland/deno/issues/31295" rel="noopener noreferrer"&gt;Issue 31295&lt;/a&gt; and &lt;a href="https://github.com/denoland/deno/issues/30334" rel="noopener noreferrer"&gt;Issue 30334&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example from Version 2.5.6&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s how it worked in version 2.5.6:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Command with &lt;code&gt;--output&lt;/code&gt; ignored:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ❯ deno compile &lt;span class="nt"&gt;--output&lt;/span&gt; output tests/testdata/compile/args.ts &lt;span class="nt"&gt;--&lt;/span&gt; abcd
   Compile file:///Users/phillips/Repo/deno/tests/testdata/compile/args.ts to output

   Embedded Files

   output
   └── args.ts &lt;span class="o"&gt;(&lt;/span&gt;224B&lt;span class="o"&gt;)&lt;/span&gt;

   Files: 1.61KB
   Metadata: 1.29KB
   Remote modules: 12B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Command with &lt;code&gt;--&lt;/code&gt; correctly parsed:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ❯ deno compile tests/testdata/compile/args.ts &lt;span class="nt"&gt;--output&lt;/span&gt; output &lt;span class="nt"&gt;--&lt;/span&gt; abcd
   Compile file:///Users/phillips/Repo/deno/tests/testdata/compile/args.ts to args

   Embedded Files

   args
   └── args.ts &lt;span class="o"&gt;(&lt;/span&gt;224B&lt;span class="o"&gt;)&lt;/span&gt;

   Files: 1.63KB
   Metadata: 1.31KB
   Remote modules: 12B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the second example, the &lt;code&gt;--output&lt;/code&gt; option was ignored and treated as an argument, which was not what users expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixing the Issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://github.com/denoland/deno/pull/31296/files" rel="noopener noreferrer"&gt;PR&lt;/a&gt; was made to fix this issue in version 2.5.6. The change removed a line of code that caused everything after the &lt;code&gt;.ts&lt;/code&gt; or &lt;code&gt;.js&lt;/code&gt; file to be considered an argument. Instead, it added a new rule to make sure anything after &lt;code&gt;--&lt;/code&gt; is treated as an argument for the script.&lt;/p&gt;

&lt;p&gt;A test case was also created to check that arguments after &lt;code&gt;--&lt;/code&gt; are parsed correctly. However, it didn’t test if options after the target file would affect execution, as seen in &lt;a href="https://github.com/denoland/deno/issues/30334" rel="noopener noreferrer"&gt;Issue 30334&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno compile main.ts &lt;span class="nt"&gt;--output&lt;/span&gt; output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To address this, I submitted another &lt;a href="https://github.com/denoland/deno/pull/31337/files" rel="noopener noreferrer"&gt;PR&lt;/a&gt; to add a test case ensuring that the order of arguments doesn’t affect the execution result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My journey through these issues and pull requests was quite interesting. When I joined the project, &lt;a href="https://github.com/denoland/deno/issues/31295" rel="noopener noreferrer"&gt;Issue 31295&lt;/a&gt; was already open. I tried to help, but my question led the maintainer to realize the issue had been solved. I discovered another related issue that lacked a test case, which allowed me to contribute in a way I hadn’t planned, but it was still enjoyable!&lt;/p&gt;

</description>
      <category>deno</category>
    </item>
    <item>
      <title>Version 1.0.0 Released! My Repo Extraction Tool is now available on NPM!</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Sat, 22 Nov 2025 22:53:28 +0000</pubDate>
      <link>https://forem.com/whyang9701/100-published-my-repo-extraction-tool-is-on-npm-now-2fbk</link>
      <guid>https://forem.com/whyang9701/100-published-my-repo-extraction-tool-is-on-npm-now-2fbk</guid>
      <description>&lt;p&gt;🎉 &lt;strong&gt;Exciting News!&lt;/strong&gt; My project, &lt;a href="https://github.com/whyang9701/repopal" rel="noopener noreferrer"&gt;&lt;strong&gt;repoal&lt;/strong&gt;&lt;/a&gt;, has just launched its &lt;a href="https://www.npmjs.com/package/@whyang9701/repopal" rel="noopener noreferrer"&gt;first version on NPM&lt;/a&gt;! You can install it using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @whyang9701/repopal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or run it directly with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @whyang9701/repopal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How I Published on NPM
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choosing a Name&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I decided to use a &lt;strong&gt;scoped name&lt;/strong&gt; for my project. This means it will be associated with my username. &lt;/li&gt;
&lt;li&gt;For example, an unscoped package like &lt;a href="https://www.npmjs.com/package/vue" rel="noopener noreferrer"&gt;vue&lt;/a&gt; is just a project name and can't be reused. In contrast, a scoped package like &lt;a href="https://www.npmjs.com/package/@vue/shared" rel="noopener noreferrer"&gt;@vue/shared&lt;/a&gt; is linked to the Vue organization. &lt;/li&gt;
&lt;li&gt;I went with a scoped name to avoid confusion and connect it to me.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automating the Publish Process&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I used &lt;strong&gt;GitHub Actions&lt;/strong&gt; to automate my publishing. GitHub has a &lt;a href="https://docs.github.com/en/actions/tutorials/publish-packages/publish-nodejs-packages" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt; that shows how to set this up.&lt;/li&gt;
&lt;li&gt;I modified the YAML file to trigger the publish process when I create a new git tag:
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Node.js Package&lt;/span&gt;

   &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v*'&lt;/span&gt;
   &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To update the version and push the tag, I run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   npm version 1.0.0
   git push &amp;amp;&amp;amp; git push --tags
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This automation will kick in every time I update the version.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Adding Provenance Statements&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I also added a feature for provenance statements, which help verify where the package was built and who published it. This can enhance security.&lt;/li&gt;
&lt;li&gt;NPM provides a &lt;a href="https://docs.npmjs.com/generating-provenance-statements" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt; on how to do this.&lt;/li&gt;
&lt;li&gt;In my GitHub Actions YAML, I made two updates:

&lt;ul&gt;
&lt;li&gt;Allow the runner to read the repo and use my ID token for verification:
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="s"&gt;...&lt;/span&gt;
     &lt;span class="s"&gt;runs-on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
     &lt;span class="s"&gt;permissions&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
       &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
     &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="s"&gt;...&lt;/span&gt;
       &lt;span class="s"&gt;- run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm publish --provenance --access public&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Visual Confirmation
&lt;/h3&gt;

&lt;p&gt;Here's a green check mark that shows my publish comes with provenance statements! &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%2F073xur3s66u2s8y21077.png" 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%2F073xur3s66u2s8y21077.png" alt="green check mark" width="268" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This check mark indicates that my package has verified information, improving supply-chain security.&lt;/p&gt;

</description>
      <category>npm</category>
      <category>node</category>
      <category>tooling</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Meet GitHub Actions</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Sat, 15 Nov 2025 18:03:39 +0000</pubDate>
      <link>https://forem.com/whyang9701/meet-github-actions-1kop</link>
      <guid>https://forem.com/whyang9701/meet-github-actions-1kop</guid>
      <description>&lt;h3&gt;
  
  
  Continuous Integration (CI) with GitHub Actions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What is CI?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Continuous Integration (CI) is a process that automatically tests the latest code to ensure everything works as expected. There are many ways to set up CI, but I find &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; the easiest to use when hosting a project on GitHub.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why Use GitHub Actions?
&lt;/h3&gt;

&lt;p&gt;GitHub Actions runs your project's tests automatically whenever you need it. For example, it can trigger tests when you:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Push a new commit
&lt;/li&gt;
&lt;li&gt;Open a pull request
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures that any new changes won’t break existing features. It gives me confidence before merging anything into the main branch.&lt;/p&gt;




&lt;h3&gt;
  
  
  Is It Free?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For public repositories&lt;/strong&gt;: GitHub Actions is &lt;a href="https://docs.github.com/en/billing/concepts/product-billing/github-actions#:~:text=GitHub%20Actions%20usage%20is%20free%20for%20self%2Dhosted%20runners%20and%20for%20public%20repositories%20that%20use%20standard%20GitHub%2Dhosted%20runners" rel="noopener noreferrer"&gt;completely free&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For private repositories&lt;/strong&gt;: You can still configure self-hosted runners and use them for free.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  How to Set It Up?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enable GitHub Actions&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Go to your repository, click the "Actions" tab, and enable it.&lt;br&gt;&lt;br&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%2F4ywj2mfi8b63xqzfbznd.png" 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%2F4ywj2mfi8b63xqzfbznd.png" alt="GitHub Actions button" width="800" height="119"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add a YAML Configuration File&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Create a &lt;code&gt;.yml&lt;/code&gt; file inside the &lt;code&gt;.github/workflows&lt;/code&gt; folder. Here's an example from my &lt;a href="https://github.com/whyang9701/repopal/blob/main/.github/workflows/nodejs.yml" rel="noopener noreferrer"&gt;Repopal project&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Node.js CI&lt;/span&gt;

   &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
     &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

   &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

       &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;volta-cli/action@v4&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci --no-audit&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run lint --if-present&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run prettier:check --if-present&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build --if-present&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimize Your Workflow&lt;/strong&gt;
Use options like &lt;code&gt;--silent&lt;/code&gt; or &lt;code&gt;--no-color&lt;/code&gt; in your commands to speed things up and reduce unnecessary output. If any command fails (returns a non-zero exit code), the job will be marked as failed.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub also provides &lt;a href="https://docs.github.com/en/actions/how-tos/write-workflows/use-workflow-templates" rel="noopener noreferrer"&gt;step-by-step tutorials&lt;/a&gt; to help you get started.&lt;/p&gt;




&lt;h3&gt;
  
  
  What’s Next?
&lt;/h3&gt;

&lt;p&gt;Once your CI is up and running, you can focus on improving it:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Increase Test Coverage&lt;/strong&gt;: Add more tests to cover different scenarios.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static Code Scanning&lt;/strong&gt;: Use tools like GitHub &lt;a href="https://docs.github.com/en/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning" rel="noopener noreferrer"&gt;CodeQL&lt;/a&gt; (built-in) or third-party options like &lt;a href="https://www.sonarsource.com/products/sonarqube/?s_campaign=SQ-NA-Canada-All-Brand&amp;amp;s_content=SonarQubeServer&amp;amp;s_term=sonarqube&amp;amp;s_category=Paid&amp;amp;s_source=Paid%20Search&amp;amp;s_origin=Google&amp;amp;cq_src=google_ads&amp;amp;cq_cmp=21484744306&amp;amp;cq_con=167801151271&amp;amp;cq_term=sonarqube&amp;amp;cq_med=&amp;amp;cq_plac=&amp;amp;cq_net=g&amp;amp;cq_pos=&amp;amp;cq_plt=gp&amp;amp;gad_source=1&amp;amp;gad_campaignid=21484744306&amp;amp;gbraid=0AAAAAC0fKmqc80tt9NeHqVm1cyl_llnsx&amp;amp;gclid=Cj0KCQiA5uDIBhDAARIsAOxj0CHSSXuY2i4fUqf8Oxciemvmv-UoY03Y8l2PXadYUNBD8PyZshnSM9oaAiPjEALw_wcB" rel="noopener noreferrer"&gt;SonarQube&lt;/a&gt; to scan your code for issues.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security is just as important as functionality, so integrating automated security checks is a great next step.&lt;br&gt;&lt;br&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%2F0tsd22gj02111urdcyg4.png" 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%2F0tsd22gj02111urdcyg4.png" alt="GitHub CodeQL" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Hacktoberfest 2025</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Mon, 10 Nov 2025 18:01:04 +0000</pubDate>
      <link>https://forem.com/whyang9701/hacktoberfest-2025-n3k</link>
      <guid>https://forem.com/whyang9701/hacktoberfest-2025-n3k</guid>
      <description>&lt;p&gt;&lt;a href="https://hacktoberfest.com/about/" rel="noopener noreferrer"&gt;Hacktoberfest&lt;/a&gt; is a yearly event by &lt;a href="https://www.digitalocean.com/" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt; that supports the open source community. Many things we use every day rely on open &lt;a href="https://en.wikipedia.org/wiki/Open_source" rel="noopener noreferrer"&gt;source software&lt;/a&gt;. For example, &lt;a href="https://source.android.com/docs/core/architecture/kernel" rel="noopener noreferrer"&gt;Android&lt;/a&gt; and cloud servers use Linux, while &lt;a href="https://en.wikipedia.org/wiki/MacOS" rel="noopener noreferrer"&gt;Macs&lt;/a&gt; is part of BSD family. &lt;a href="https://pulse.microsoft.com/nl-nl/transform-nl-nl/na/fa1-microsoft-loves-open-source/" rel="noopener noreferrer"&gt;GitHub, owned by Microsoft&lt;/a&gt;, is the most popular site for storing open source projects and has many features for collaboration.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Pull Requests (PRs) in Hacktoberfest
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/soybeanjs/soybean-admin/pull/845" rel="noopener noreferrer"&gt;Scrollbar color issue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tylerapear/NESY-Engine/pull/76" rel="noopener noreferrer"&gt;Added a document for a class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lxc/incus/pull/2595" rel="noopener noreferrer"&gt;Fixed some typos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tryandromeda/andromeda/pull/188" rel="noopener noreferrer"&gt;Added a new option for a CLI tool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When I made my first contribution to open source, I felt nervous. I worried about making mistakes, wasting someone’s time with my pull request (PR), or not following the community rules. I spent a lot of time looking for issues I felt confident I could solve. However, I found that I needed to understand the projects better before I could tackle any issues. Even the &lt;a href="https://github.com/soybeanjs/soybean-admin/pull/845" rel="noopener noreferrer"&gt;scrollbar issue&lt;/a&gt;, which seemed simple, felt daunting when I first asked the maintainer to assign it to me. Now, I realize my anxiety was unnecessary. Most things become clearer once you dive into a project.&lt;/p&gt;

&lt;p&gt;While working on my &lt;a href="https://github.com/tryandromeda/andromeda/pull/188" rel="noopener noreferrer"&gt;PR&lt;/a&gt; for improving the Incus documentation, the maintainer asked me to fix my commit to follow the community rules. Even though the fix was minor and didn’t affect the code or the CI/CD pipeline, he was polite and insisted on the no-anonymous contribution rule. It took me about 15 minutes to update the files and another 15 minutes to learn how to build the documentation site on my local machine. However, it took me 2 hours to understand the command &lt;a href="https://git-scm.com/docs/git-commit" rel="noopener noreferrer"&gt;&lt;code&gt;git commit --sign-commits&lt;/code&gt;&lt;/a&gt; and why it was important.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Productive October
&lt;/h3&gt;

&lt;p&gt;This October has been my most productive and satisfying month of the year. I learned a lot by contributing to different open source projects. My motivation made a big difference. By choosing issues to work on, I set personal goals that felt like promises to the community.&lt;/p&gt;

&lt;p&gt;I learned how to implement the &lt;code&gt;--watch&lt;/code&gt; option for CLI tools by studying a Node.js project. Then, I recreated that feature in another project using Rust. I started learning Rust from scratch and was able to contribute within two weeks. I also discovered that most maintainers online are friendly, and the community is there to help, not criticize.&lt;/p&gt;

&lt;p&gt;If you want to join Hacktoberfest 2026, don’t spend too much time searching for issues you feel fully confident about. Choose issues that interest you and think through them as you go. You don’t need to know every detail before you start; having a rough idea is enough.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>Build A Watch Option for TypeScript Runtime in Rust</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Sat, 08 Nov 2025 03:11:09 +0000</pubDate>
      <link>https://forem.com/whyang9701/build-a-watch-option-for-typescript-runtime-in-rust-1if1</link>
      <guid>https://forem.com/whyang9701/build-a-watch-option-for-typescript-runtime-in-rust-1if1</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/tryandromeda/andromeda" rel="noopener noreferrer"&gt;Andromeda&lt;/a&gt; is a new TypeScript runtime that uses the &lt;a href="https://trynova.dev/" rel="noopener noreferrer"&gt;Nova&lt;/a&gt; Engine and &lt;a href="https://oxc.rs/" rel="noopener noreferrer"&gt;Oxc&lt;/a&gt;, which is written in Rust.&lt;/p&gt;

&lt;p&gt;Node.js has had a &lt;code&gt;--watch&lt;/code&gt; feature since 2022, and you can find the &lt;a href="https://github.com/nodejs/node/pull/44366/files#diff-30ce5ad0c669a2ea6eef90f95d1f70cbf58f4b3063ff61d2498e6db30b30881b" rel="noopener noreferrer"&gt;pull request (PR)&lt;/a&gt; for it on GitHub. This watch mode, or hot reload, is very popular for frontend frameworks, testing frameworks, and even in Node.js itself. It automatically reruns the code whenever you change a file in the working directory, making life easier for developers today.&lt;/p&gt;

&lt;p&gt;I had never tried Rust before sending this PR, and it was a bit reckless to ask the maintainer to assign the issue to me. However, I think it was the best decision I made during Hacktoberfest.&lt;/p&gt;

&lt;p&gt;I didn’t think much about how watch mode was implemented, but I used it a lot. My approach was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the Node.js &lt;a href="https://nodejs.org/en/blog/announcements/v22-release-announce" rel="noopener noreferrer"&gt;release page&lt;/a&gt; to find out when the watch option was announced.&lt;/li&gt;
&lt;li&gt;Locate the PR related to the &lt;code&gt;--watch&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;Read the &lt;a href="https://github.com/nodejs/node/blob/main/lib/internal/watch_mode/files_watcher.js#L117" rel="noopener noreferrer"&gt;core implementation&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, I needed to use Rust to create a similar function. I did the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I read &lt;a href="https://doc.rust-lang.org/book/" rel="noopener noreferrer"&gt;The Rust Programming Language&lt;/a&gt; to get a basic understanding of Rust and its tools.&lt;/li&gt;
&lt;li&gt;I used AI to help explain each package in the &lt;code&gt;Cargo.toml&lt;/code&gt; file, which is like &lt;code&gt;package.json&lt;/code&gt; for JavaScript projects.&lt;/li&gt;
&lt;li&gt;I looked at the main entry point of the project, even though I didn’t fully understand it. The function names and variables gave me clues about the watch option.&lt;/li&gt;
&lt;li&gt;I wrote a folder watch program in a test project and then copied that code to Andromeda.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It was a long journey to solve all the problems I faced. I made some progress but had many doubts over the past two weeks. However, I felt more satisfied than I expected after sending the PR. It reminded me of the first time I compiled and ran my "Hello World" program when I started learning to code.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>rust</category>
    </item>
    <item>
      <title>Vitest HTML Report</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Fri, 07 Nov 2025 22:48:06 +0000</pubDate>
      <link>https://forem.com/whyang9701/vitest-html-report-333o</link>
      <guid>https://forem.com/whyang9701/vitest-html-report-333o</guid>
      <description>&lt;p&gt;Testing is very important for software projects. It helps developers find problems that might occur after changes to the code, before those changes are added to the main branch. Software code is updated frequently, and it’s hard to test every possible issue manually each time. Testing frameworks help &lt;a href="https://en.wikipedia.org/wiki/CI/CD" rel="noopener noreferrer"&gt;automate&lt;/a&gt; this process, either on the developer's computer or in cloud services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vitest.dev/" rel="noopener noreferrer"&gt;Vitest&lt;/a&gt; is a testing framework made for the Vite ecosystem. &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; is another well-known framework, and Vitest is designed to work similarly, so developers can easily switch between them. I can use the &lt;code&gt;Expect&lt;/code&gt; &lt;a href="https://jestjs.io/docs/expect" rel="noopener noreferrer"&gt;API&lt;/a&gt;, which I'm already familiar with, originally from Jest.&lt;/p&gt;

&lt;p&gt;Vitest also has a &lt;code&gt;--ui&lt;/code&gt; option that opens a web page showing the test results and coverage reports while monitoring project files. The HTML report helps me understand which parts of the code are not covered by tests better than just seeing it in the command line.&lt;br&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%2Fards6v35qeefbmah933v.png" 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%2Fards6v35qeefbmah933v.png" alt="CLI output of code coverage" width="800" height="98"&gt;&lt;/a&gt;&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%2Fcslvrdux80dbzv6h97op.png" 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%2Fcslvrdux80dbzv6h97op.png" alt="Browser report of code coverage" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vitest can integrate with IDEs like &lt;a href="https://vitest.dev/guide/ide.html" rel="noopener noreferrer"&gt;VS Code and JetBrains&lt;/a&gt;, but I use Neovim with lazyVim for my development. There’s a third-party tool called &lt;a href="https://github.com/nvim-neotest/neotest-jest" rel="noopener noreferrer"&gt;Neotest&lt;/a&gt; that works with Neovim and Vitest, but I haven’t tried it yet.&lt;/p&gt;

&lt;p&gt;When it comes to writing tests, I often feel less motivated than when I’m developing new features. So, I need to create a nice interface and an easy-to-use pipeline for myself to stay motivated with testing tasks.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>vitest</category>
    </item>
    <item>
      <title>Browser scrollbar color in the Dark mode.</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Sun, 02 Nov 2025 01:08:18 +0000</pubDate>
      <link>https://forem.com/whyang9701/browser-scrollbar-color-in-the-dark-mode-29db</link>
      <guid>https://forem.com/whyang9701/browser-scrollbar-color-in-the-dark-mode-29db</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/soybeanjs/soybean-admin" rel="noopener noreferrer"&gt;SoybeenAdmin&lt;/a&gt; is a dashboard template built with Vue 3, Pinia, and TypeScript. With 13.3k stars and 2.3k forks, it is a popular project.&lt;/p&gt;

&lt;p&gt;I fixed a &lt;a href="https://github.com/soybeanjs/soybean-admin/issues/834" rel="noopener noreferrer"&gt;bug&lt;/a&gt; where the scrollbar color didn't switch to light when dark mode was enabled. The fix for Firefox and Chrome involved updating the CSS property &lt;code&gt;scrollbar-color&lt;/code&gt;, but this property doesn’t apply to Safari. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/scrollbar-color" rel="noopener noreferrer"&gt;Safari doesn't support this property&lt;/a&gt;, yet I found several websites that displayed different color scrollbars while dark mode was active.&lt;/p&gt;

&lt;p&gt;The solution for Safari is the &lt;code&gt;color-scheme&lt;/code&gt; property. This property informs Safari that the element is dark, allowing it to use a light color scrollbar by default.&lt;/p&gt;

&lt;p&gt;Although this issue wasn’t difficult, I initially had no idea what was causing it. Chrome, Firefox, and Safari each support different CSS properties, which made it challenging for me to get into frontend development. After checking line by line on MDN, I discovered &lt;a href="https://github.com/stylelint/stylelint" rel="noopener noreferrer"&gt;stylelint&lt;/a&gt;. I believe this CSS linter can help alleviate my headaches in the frontend projects I'm involved in.&lt;/p&gt;

</description>
      <category>css</category>
      <category>tutorial</category>
      <category>ui</category>
    </item>
    <item>
      <title>Be a contributor of Incus</title>
      <dc:creator>WH yang</dc:creator>
      <pubDate>Sat, 01 Nov 2025 20:53:30 +0000</pubDate>
      <link>https://forem.com/whyang9701/be-a-contributor-of-incus-36p6</link>
      <guid>https://forem.com/whyang9701/be-a-contributor-of-incus-36p6</guid>
      <description>&lt;p&gt;&lt;a href="https://linuxcontainers.org/incus/" rel="noopener noreferrer"&gt;Incus&lt;/a&gt; is a next-generation system container, application container, and virtual machine manager. It aims to provide a similar approach to public clouds for managing containers and VMs on private machines, suitable for anything from a single laptop to a whole server rack. It is an alternative to &lt;a href="https://canonical.com/lxd" rel="noopener noreferrer"&gt;Canonical's LXD&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Incus is a large project, and I'm not sure when I will use it. While exploring the project documentation, I noticed some small problems that hadn’t been reported. I took about half an hour to understand how to build the static documentation page and submitted a pull request (PR) to the Incus main repository. However, things became more complicated after submitting the PR.&lt;/p&gt;

&lt;p&gt;I didn’t realize they had specific instructions for pull requests, including a required commit format and the need to use &lt;code&gt;git commit -s&lt;/code&gt; to add a sign-off line to my commit message. By adding the sign-off line, I agreed to the terms outlined in the Developer Certificate of Origin.&lt;/p&gt;

&lt;p&gt;This was new to me. This project don’t accept anonymous commits. This policy helps avoid legal issues if someone copies code from a company project to this one.&lt;/p&gt;

&lt;p&gt;Even though I made a mistake in my PR, the maintainer kindly asked me to add the sign-off, and he merged it a few hours later after the CI pipeline ran. It was my first time having a conversation online with someone reviewing my contribution. Honestly, this process was more strict than any project I’ve worked on before. I spent a lot of time writing issues, creating the PR, updating my commits, and reading the contribution guidelines, even though my PR only included a small documentation update.&lt;/p&gt;

&lt;p&gt;A huge thank you to &lt;a href="https://github.com/stgraber" rel="noopener noreferrer"&gt;Stéphane Graber&lt;/a&gt;, and I’m happy to see my contribution included in the &lt;a href="https://github.com/lxc/incus/releases/tag/v6.18.0" rel="noopener noreferrer"&gt;6.18 release&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>container</category>
      <category>lxc</category>
      <category>incus</category>
    </item>
  </channel>
</rss>
