<?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: Kamil Dzieniszewski</title>
    <description>The latest articles on DEV Community by Kamil Dzieniszewski (@dzienisz).</description>
    <link>https://dev.to/dzienisz</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F101658%2F865cff2c-d15b-4f9e-b1d5-da0907d1e7c1.jpeg</url>
      <title>DEV Community: Kamil Dzieniszewski</title>
      <link>https://dev.to/dzienisz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dzienisz"/>
    <language>en</language>
    <item>
      <title>I shipped a Chrome extension that runs an LLM with no server, no build step, and no dependencies. Here's what the Prompt API is really like.</title>
      <dc:creator>Kamil Dzieniszewski</dc:creator>
      <pubDate>Mon, 13 Jul 2026 15:54:35 +0000</pubDate>
      <link>https://dev.to/dzienisz/i-shipped-a-chrome-extension-that-runs-an-llm-with-no-server-no-build-step-and-no-dependencies-2pdg</link>
      <guid>https://dev.to/dzienisz/i-shipped-a-chrome-extension-that-runs-an-llm-with-no-server-no-build-step-and-no-dependencies-2pdg</guid>
      <description>&lt;p&gt;We've all typed a reply, hit post, and regretted the tone ten minutes later. I built &lt;a href="https://chromewebstore.google.com/detail/comment-vibe/kibcnjcipaofjlbbnjdjaobbkoajiejp" rel="noopener noreferrer"&gt;Comment Vibe&lt;/a&gt;, a Chrome extension that shows a small badge next to any comment box (LinkedIn, X, YouTube, generic &lt;code&gt;contenteditable&lt;/code&gt;) with the detected tone of what you're typing — and offers a kinder rewrite when you sound harsh.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2lohn4zjiarsi0e75yoc.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2lohn4zjiarsi0e75yoc.png" alt=" " width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The interesting part isn't the product. It's the constraint: &lt;strong&gt;everything runs on-device via Chrome's built-in Gemini Nano&lt;/strong&gt;. No server, no API key, no analytics, no network requests. The whole extension is a handful of raw MV3 files — no npm, no bundler. When your pitch is "your comments never leave your machine," the best privacy audit is &lt;code&gt;cat content.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This post is everything I wish I'd known about the Prompt API before starting.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0mk9xucr4fhvj5n9yfre.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0mk9xucr4fhvj5n9yfre.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Prompt API is actually stable now
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;LanguageModel&lt;/code&gt; API went &lt;strong&gt;stable for extensions in Chrome 138&lt;/strong&gt; and &lt;strong&gt;stable on the open web in Chrome 148&lt;/strong&gt;. No flags, no origin trial tokens. Chrome downloads Gemini Nano (~2–4 GB) automatically on first use, on eligible hardware (desktop, 16 GB+ RAM, &amp;gt;4 GB VRAM, 22 GB free disk).&lt;/p&gt;

&lt;p&gt;That hardware floor is the real trade-off: you get zero infra cost and perfect privacy, but a chunk of your users simply can't run it. Your UX must degrade gracefully — mine shows an availability check in the popup and otherwise stays silent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 1: You still have to probe two namespaces
&lt;/h2&gt;

&lt;p&gt;Older builds expose the legacy &lt;code&gt;window.ai.languageModel&lt;/code&gt;; newer ones the global &lt;code&gt;LanguageModel&lt;/code&gt;. Different Chrome builds ship different namespaces depending on where they were in the origin-trial timeline, so I probe both — and feature-detect &lt;code&gt;initialPrompts&lt;/code&gt; support with a retry ladder:&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;constructSession&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;LanguageModel&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="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;avail&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;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;availability&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;avail&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unavailable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unavailable&lt;/span&gt;&lt;span class="dl"&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;initialPrompts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;FEW_SHOT&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;try&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;sessionMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;initialPrompts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;LANGUAGE_OPTIONS&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;supportsReducedOptionsRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;try&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;sessionMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;initialPrompts&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducedError&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="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;supportsReducedOptionsRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducedError&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;reducedError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// older builds without initialPrompts support&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sessionMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;systemPrompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="p"&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="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="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&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;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;languageModel&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;available&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;languageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capabilities&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;available&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unavailable&lt;/span&gt;&lt;span class="dl"&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;sessionMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;languageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;systemPrompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="p"&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Chrome AI not found&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;Unsupported options throw &lt;code&gt;TypeError&lt;/code&gt; or &lt;code&gt;NotSupportedError&lt;/code&gt;, so each fallback strips one capability and retries. Ugly, but it's what "works on every Chrome since 127" costs.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg8xqaws7l1jg173iza40.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg8xqaws7l1jg173iza40.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 2: Sessions are stateful — clone, don't reuse
&lt;/h2&gt;

&lt;p&gt;This one bit me. A Prompt API session keeps its conversation history. If you prompt the same session repeatedly, every analysis appends to its context: each call gets slower, and eventually you overflow the session quota.&lt;/p&gt;

&lt;p&gt;The fix: keep one warm base session (creating a session is expensive — it can load the model), and &lt;code&gt;clone()&lt;/code&gt; a throwaway per analysis. A clone starts fresh from the initial prompts and is cheap:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acquired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;          &lt;span class="c1"&gt;// cached, warm base session&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;acquired&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneCapable&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acquired&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;            &lt;span class="c1"&gt;// fresh context, no model reload&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;acquired&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ... prompt on the clone&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroy&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;Bonus: baking the system prompt and few-shot examples into &lt;code&gt;initialPrompts&lt;/code&gt; means they survive the clone — you don't re-send them with every request.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyppdsk1djqgk7thnzynz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyppdsk1djqgk7thnzynz.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 3: Prefill the assistant response to force JSON
&lt;/h2&gt;

&lt;p&gt;Gemini Nano is a small model. Ask it politely for JSON and you'll get JSON &lt;em&gt;most&lt;/em&gt; of the time, wrapped in markdown fences &lt;em&gt;some&lt;/em&gt; of the time, and a friendly paragraph on bad days. Two tricks got me to reliable structured output:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Few-shot examples&lt;/strong&gt; in the session's initial prompts, plus an &lt;strong&gt;assistant prefill&lt;/strong&gt; — you hand the model the beginning of its own answer and it continues from there:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Classify the tone of this comment:\n"&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assistant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{"sentiment":&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;// ← prefill&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;raw&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{"sentiment":&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;prefix: true&lt;/code&gt; flag means the model &lt;em&gt;must&lt;/em&gt; start its response mid-JSON. As a side effect, &lt;code&gt;sentiment&lt;/code&gt; is guaranteed to be the first field — which enables the streaming trick below.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0dmpofglmmewfx23o9jc.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0dmpofglmmewfx23o9jc.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 4: Stream, and act on the first parseable field
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;promptStreaming()&lt;/code&gt; the badge colors in as soon as the sentiment value is readable, while the reason and rewrite are still generating:&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;streamPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onEarlySentiment&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;signaled&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="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &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;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;callPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;promptStreaming&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messages&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="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// older builds stream the cumulative text, newer ones stream deltas&lt;/span&gt;
    &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;chunk&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;signaled&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;onEarlySentiment&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;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/"&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;positive|neutral|negative|toxic&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&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;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signaled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nf"&gt;onEarlySentiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&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;return&lt;/span&gt; &lt;span class="nx"&gt;raw&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;Note the chunk handling: older Chrome builds stream the &lt;em&gt;cumulative&lt;/em&gt; text, newer ones stream &lt;em&gt;deltas&lt;/em&gt;. That one line handles both.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1zd2qj9wd0nvzk48j10.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1zd2qj9wd0nvzk48j10.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 5: Never trust the schema — normalize everything
&lt;/h2&gt;

&lt;p&gt;Even with prefill, a small model improvises key names. Ask for &lt;code&gt;reason&lt;/code&gt;, receive &lt;code&gt;tone&lt;/code&gt;, &lt;code&gt;analysis&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, or &lt;code&gt;explanation&lt;/code&gt;. So the parser maps every synonym the model has ever invented back to the expected shape:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reasonRaw&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tone&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;analysis&lt;/span&gt;
                &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;explanation&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&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;rewriteRaw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rewrite&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;suggestion&lt;/span&gt;
                &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alternative&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;improved_version&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when JSON parsing fails entirely, there's a last-ditch fallback: regex the raw text for sentiment keywords. A wrong-but-plausible badge beats a broken one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 6: Debounce like you mean it
&lt;/h2&gt;

&lt;p&gt;On-device inference is free but not fast. I wait &lt;strong&gt;900 ms&lt;/strong&gt; after the last keystroke before prompting, wire an &lt;code&gt;AbortController&lt;/code&gt; through so a new keystroke cancels the in-flight analysis, and tag requests with an id so a slow stale response can't overwrite a newer result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 7: The API zoo is uneven — check before you depend
&lt;/h2&gt;

&lt;p&gt;Chrome ships seven built-in AI APIs, at very different maturity levels:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Worth using?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prompt (&lt;code&gt;LanguageModel&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;✅ Stable&lt;/td&gt;
&lt;td&gt;Yes — the workhorse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Translator / Language Detector&lt;/td&gt;
&lt;td&gt;✅ Stable&lt;/td&gt;
&lt;td&gt;Yes — they compose beautifully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Summarizer&lt;/td&gt;
&lt;td&gt;✅ Stable&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rewriter&lt;/td&gt;
&lt;td&gt;🧪 Dev trial&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;No&lt;/strong&gt; — ran a full origin trial, then went &lt;em&gt;back&lt;/em&gt; to a flag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Writer&lt;/td&gt;
&lt;td&gt;🧪 Dev trial&lt;/td&gt;
&lt;td&gt;Not yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proofreader&lt;/td&gt;
&lt;td&gt;🧪 Origin trial&lt;/td&gt;
&lt;td&gt;Not yet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Rewriter story is the cautionary tale: an API can complete an origin trial and still not graduate. Don't build a hard dependency on anything pre-stable — I do rewrites with the Prompt API instead.&lt;/p&gt;

&lt;p&gt;The stable trio composes nicely though: Language Detector identifies what language you're typing in, Translator localizes the feedback and the rewrite into it (cached per language pair, falling back to English), so the suggestion is directly pasteable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was "no build tooling" worth it?
&lt;/h2&gt;

&lt;p&gt;Honestly: yes, and more than I expected. No supply chain, nothing to audit but the shipped files, &lt;code&gt;Load unpacked&lt;/code&gt; straight from the repo, and the Web Store zip is just &lt;code&gt;zip -r&lt;/code&gt; with excludes. The cost is no TypeScript and no bundler-era conveniences — for ~1000 lines of content script, that trade is easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extension:&lt;/strong&gt; &lt;a href="https://chromewebstore.google.com/detail/comment-vibe/kibcnjcipaofjlbbnjdjaobbkoajiejp" rel="noopener noreferrer"&gt;Comment Vibe on the Chrome Web Store&lt;/a&gt; (Chrome 138+, desktop)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Playground for all seven built-in AI APIs&lt;/strong&gt; — live availability check for &lt;em&gt;your&lt;/em&gt; browser plus an interactive demo of each: &lt;a href="https://dzienko.dev/comment-vibe/" rel="noopener noreferrer"&gt;https://dzienko.dev/comment-vibe/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source (MIT):&lt;/strong&gt; [your repo link]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building on the Prompt API and hitting weirdness I didn't cover, drop a comment — I've probably seen it.&lt;/p&gt;

</description>
      <category>chrome</category>
      <category>ai</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I hope not! #meetjs</title>
      <dc:creator>Kamil Dzieniszewski</dc:creator>
      <pubDate>Mon, 26 Jan 2026 14:16:16 +0000</pubDate>
      <link>https://dev.to/dzienisz/i-hope-not-meetjs-5fc</link>
      <guid>https://dev.to/dzienisz/i-hope-not-meetjs-5fc</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/naugtur" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F247817%2Ff7fb04ee-1431-41aa-a06e-52aa5c8a1e47.jpeg" alt="naugtur"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/naugtur/is-this-my-last-meetjs-summit-1o5k" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Is this my last meet.js Summit?&lt;/h2&gt;
      &lt;h3&gt;naugtur ・ Jan 26&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>I hope not! #meetjs</title>
      <dc:creator>Kamil Dzieniszewski</dc:creator>
      <pubDate>Mon, 26 Jan 2026 14:16:16 +0000</pubDate>
      <link>https://dev.to/dzienisz/i-hope-not-meetjs-3b6m</link>
      <guid>https://dev.to/dzienisz/i-hope-not-meetjs-3b6m</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/naugtur" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F247817%2Ff7fb04ee-1431-41aa-a06e-52aa5c8a1e47.jpeg" alt="naugtur"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/naugtur/is-this-my-last-meetjs-summit-1o5k" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Is this my last meet.js Summit?&lt;/h2&gt;
      &lt;h3&gt;naugtur ・ Jan 26&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Planning a conference? Check this list!</title>
      <dc:creator>Kamil Dzieniszewski</dc:creator>
      <pubDate>Fri, 03 Oct 2025 13:23:32 +0000</pubDate>
      <link>https://dev.to/dzienisz/planning-a-conference-check-this-list-358b</link>
      <guid>https://dev.to/dzienisz/planning-a-conference-check-this-list-358b</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/naugtur/community-conference-organizer-checklist-5d45" class="crayons-story__hidden-navigation-link"&gt;Community Conference Organizer Checklist&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/naugtur" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.dev.to%2Fcdn-cgi%2Fimage%2Fwidth%3D90%2Cheight%3D90%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto%2Fhttps%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F247817%252Ff7fb04ee-1431-41aa-a06e-52aa5c8a1e47.jpeg" alt="naugtur profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/naugtur" class="crayons-story__secondary fw-medium m:hidden"&gt;
              naugtur
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                naugtur
                
              
              &lt;div id="story-author-preview-content-187883" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/naugtur" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.dev.to%2Fcdn-cgi%2Fimage%2Fwidth%3D90%2Cheight%3D90%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto%2Fhttps%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F247817%252Ff7fb04ee-1431-41aa-a06e-52aa5c8a1e47.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;naugtur&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/naugtur/community-conference-organizer-checklist-5d45" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Dec 20 '19&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/naugtur/community-conference-organizer-checklist-5d45" id="article-link-187883"&gt;
          Community Conference Organizer Checklist
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/naugtur/community-conference-organizer-checklist-5d45" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;9&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/naugtur/community-conference-organizer-checklist-5d45#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            10 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>management</category>
      <category>productivity</category>
      <category>resources</category>
    </item>
    <item>
      <title>When 'env' Isn't Just Environment Variables: My Journey Through JavaScript's Module Shorthand Rabbit Hole</title>
      <dc:creator>Kamil Dzieniszewski</dc:creator>
      <pubDate>Tue, 26 Aug 2025 14:42:03 +0000</pubDate>
      <link>https://dev.to/dzienisz/when-env-isnt-just-environment-variables-my-journey-through-javascripts-module-shorthand-3mnf</link>
      <guid>https://dev.to/dzienisz/when-env-isnt-just-environment-variables-my-journey-through-javascripts-module-shorthand-3mnf</guid>
      <description>&lt;p&gt;&lt;em&gt;Or: How I spent 3 hours debugging a single word in my config&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Docusaurus Confusion Story
&lt;/h2&gt;

&lt;p&gt;Picture this: I'm upgrading the documentation site for &lt;a href="https://tolgee.io" rel="noopener noreferrer"&gt;Tolgee&lt;/a&gt; (a localization platform I work on), following the Docusaurus migration guide, and I see this line:&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="nx"&gt;presets&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="s1"&gt;classic&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="cm"&gt;/* options */&lt;/span&gt; &lt;span class="p"&gt;}]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fi.imgflip.com%2F7kqc8j.jpg" 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%2Fi.imgflip.com%2F7kqc8j.jpg" alt="Confused developer meme" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Me staring at 'classic' like it personally offended me&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;"What the hell is 'classic'?" I thought. "Is this some built-in JavaScript thing I missed? A global variable? Magic?"&lt;/p&gt;

&lt;p&gt;I spent the next hour:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔍 Searching my &lt;code&gt;node_modules&lt;/code&gt; for anything named "classic"&lt;/li&gt;
&lt;li&gt;📖 Reading Docusaurus docs trying to find where this comes from&lt;/li&gt;
&lt;li&gt;🤔 Questioning my understanding of JavaScript imports&lt;/li&gt;
&lt;li&gt;😤 Getting increasingly frustrated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I discovered the truth: &lt;code&gt;'classic'&lt;/code&gt; is actually &lt;code&gt;@docusaurus/preset-classic&lt;/code&gt; in disguise. Mind = blown.&lt;/p&gt;

&lt;p&gt;But here's the kicker - this "magic" isn't unique to Docusaurus. It's EVERYWHERE in the JavaScript ecosystem.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Are Module Shorthands?
&lt;/h2&gt;

&lt;p&gt;Module shorthands are a convenience feature that lets you use abbreviated names instead of full package names in configuration files. When a tool encounters a shorthand, it automatically resolves it to the full package name using predefined rules.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Pattern
&lt;/h3&gt;

&lt;p&gt;Most JavaScript tools follow a similar resolution strategy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Try exact name first&lt;/strong&gt;: &lt;code&gt;classic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try official scoped package&lt;/strong&gt;: &lt;code&gt;@toolname/type-classic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try community convention&lt;/strong&gt;: &lt;code&gt;toolname-type-classic&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first match wins.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Plot Twist: It's Everywhere!
&lt;/h2&gt;

&lt;p&gt;Once I understood what was happening with Docusaurus, I started seeing this pattern EVERYWHERE. Let me blow your mind with some examples:&lt;/p&gt;
&lt;h3&gt;
  
  
  Babel - The OG Shorthand Master
&lt;/h3&gt;

&lt;p&gt;Remember that innocent &lt;code&gt;"env"&lt;/code&gt; in your Babel config? Yeah, that's not what you think it is.&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="c1"&gt;// babel.config.js&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;presets&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// → @babel/preset-env (SURPRISE!)&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// → @babel/preset-react&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typescript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;     &lt;span class="c1"&gt;// → @babel/preset-typescript&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plugins&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transform-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// → @babel/plugin-transform-runtime&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;proposal-decorators&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;// → @babel/plugin-proposal-decorators&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;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7kqc8k.jpg" 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%2Fi.imgflip.com%2F7kqc8k.jpg" alt="Drake meme" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Drake pointing: Writing &lt;a class="mentioned-user" href="https://dev.to/babel"&gt;@babel&lt;/a&gt;/preset-env&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Drake approving: Writing "env"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Babel has the most sophisticated &lt;a href="https://babeljs.io/docs/options#name-normalization" rel="noopener noreferrer"&gt;name normalization system&lt;/a&gt; with detailed rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unscoped packages: &lt;code&gt;"mod"&lt;/code&gt; → &lt;code&gt;"babel-plugin-mod"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Scoped packages: &lt;code&gt;"@babel/mod"&lt;/code&gt; → &lt;code&gt;"@babel/plugin-mod"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Custom scopes: &lt;code&gt;"@scope/mod"&lt;/code&gt; → &lt;code&gt;"@scope/babel-plugin-mod"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  ESLint - The "Obvious" One (Once You Know)
&lt;/h3&gt;

&lt;p&gt;ESLint actually has the clearest naming convention, but it still trips people up:&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="c1"&gt;// .eslintrc.js&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;extends&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;airbnb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// → eslint-config-airbnb&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prettier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// → eslint-config-prettier&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;      &lt;span class="c1"&gt;// → eslint-config-react-app&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plugins&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// → eslint-plugin-react&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;import&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// → eslint-plugin-import&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jsx-a11y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;       &lt;span class="c1"&gt;// → eslint-plugin-jsx-a11y&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;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7kqc8l.jpg" 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%2Fi.imgflip.com%2F7kqc8l.jpg" alt="This is fine meme" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Me realizing every config I've ever written is full of hidden magic&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;ESLint's &lt;a href="https://eslint.org/docs/latest/use/configure/plugins#configuring-plugins" rel="noopener noreferrer"&gt;plugin configuration rules&lt;/a&gt; are actually predictable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configs: &lt;code&gt;"name"&lt;/code&gt; → &lt;code&gt;"eslint-config-name"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Plugins: &lt;code&gt;"name"&lt;/code&gt; → &lt;code&gt;"eslint-plugin-name"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Scoped: &lt;code&gt;"@scope/name"&lt;/code&gt; → &lt;code&gt;"@scope/eslint-plugin-name"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Back to My Docusaurus Nightmare
&lt;/h3&gt;

&lt;p&gt;Now that I know the pattern, let's revisit my original confusion:&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="c1"&gt;// docusaurus.config.js - The scene of the crime&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;presets&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;classic&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="c1"&gt;// → @docusaurus/preset-classic (THE REVEAL!)&lt;/span&gt;
      &lt;span class="na"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* */&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&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="nx"&gt;plugins&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="s1"&gt;sitemap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// → @docusaurus/plugin-sitemap&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content-pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;   &lt;span class="c1"&gt;// → @docusaurus/plugin-content-pages&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;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7kqc8m.jpg" 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%2Fi.imgflip.com%2F7kqc8m.jpg" alt="Expanding brain meme" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Brain 1: "classic" is magic&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Brain 2: "classic" is a shorthand&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Brain 3: All JS tools use shorthands&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Brain 4: I've been using shorthands everywhere without knowing&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Docusaurus has &lt;a href="https://docusaurus.io/docs/using-plugins#module-shorthands" rel="noopener noreferrer"&gt;comprehensive documentation&lt;/a&gt; about this, but finding it when you're confused? Good luck!&lt;/p&gt;
&lt;h3&gt;
  
  
  PostCSS - Simple Resolution
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// postcss.config.js&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;plugins&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;autoprefixer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// → autoprefixer (exact match)&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cssnano&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// → cssnano (exact match)&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;    &lt;span class="c1"&gt;// → tailwindcss (exact match)&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;h3&gt;
  
  
  Rollup - Scoped Packages
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// rollup.config.js&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;plugins&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;resolve&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// → @rollup/plugin-node-resolve&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;commonjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// → @rollup/plugin-commonjs&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typescript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;     &lt;span class="c1"&gt;// → @rollup/plugin-typescript&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;h3&gt;
  
  
  Vite - Following the Pattern
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vite.config.js&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;plugins&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;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// → @vitejs/plugin-react&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;legacy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;         &lt;span class="c1"&gt;// → @vitejs/plugin-legacy&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;h2&gt;
  
  
  Why Does This Pattern Even Exist?
&lt;/h2&gt;

&lt;p&gt;After my 3-hour debugging session, I had to ask: WHY do JavaScript tools do this to us?&lt;/p&gt;
&lt;h3&gt;
  
  
  Convention Over Configuration (The Philosophy)
&lt;/h3&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%2Fi.imgflip.com%2F7kqc8n.jpg" 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%2Fi.imgflip.com%2F7kqc8n.jpg" alt="Philosophy Raptor meme" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;If a shorthand resolves in a config file and no developer understands it, does it make a sound?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The JavaScript ecosystem LOVES "convention over configuration":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Less typing&lt;/strong&gt;: &lt;code&gt;'env'&lt;/code&gt; vs &lt;code&gt;'@babel/preset-env'&lt;/code&gt; (saved 15 characters!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner configs&lt;/strong&gt;: Your babel.config.js doesn't look like XML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable patterns&lt;/strong&gt;: Once you know the rules, you can guess package names&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: Both &lt;code&gt;'env'&lt;/code&gt; and &lt;code&gt;'@babel/preset-env'&lt;/code&gt; work&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The Ecosystem Benefits (When It Works)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Learn it once, recognize it everywhere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discoverability&lt;/strong&gt;: "I bet there's an eslint-plugin-react"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability&lt;/strong&gt;: Shorter configs are easier to scan&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer happiness&lt;/strong&gt;: Less boilerplate = more time for actual coding&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  The Dark Side: Why It's Absolutely Confusing
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Hidden Magic Everywhere
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What you see&lt;/span&gt;
&lt;span class="nx"&gt;presets&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="s1"&gt;classic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="c1"&gt;// What actually happens (behind the scenes)&lt;/span&gt;
&lt;span class="nx"&gt;presets&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="s1"&gt;@docusaurus/preset-classic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fi.imgflip.com%2F7kqc8o.jpg" 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%2Fi.imgflip.com%2F7kqc8o.jpg" alt="Iceberg meme" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Tip of iceberg: Your config&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Underwater: All the module resolution happening&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There's literally NO visual indication that &lt;code&gt;'classic'&lt;/code&gt; becomes something else entirely.&lt;/p&gt;
&lt;h3&gt;
  
  
  Documentation Roulette
&lt;/h3&gt;

&lt;p&gt;The docs are inconsistent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tutorial shows: &lt;code&gt;'classic'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;API docs show: &lt;code&gt;'@docusaurus/preset-classic'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Stack Overflow shows: Both, with no explanation&lt;/li&gt;
&lt;li&gt;Your brain: 🤯&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Debugging Hell
&lt;/h3&gt;

&lt;p&gt;When things break, you get errors like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cannot resolve module 'classic'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fi.imgflip.com%2F7kqc8p.jpg" 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%2Fi.imgflip.com%2F7kqc8p.jpg" alt="Confused Travolta meme" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Me trying to figure out which of the 3 resolution attempts failed&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The error doesn't tell you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which resolution step failed&lt;/li&gt;
&lt;li&gt;What was actually attempted&lt;/li&gt;
&lt;li&gt;Whether the package is missing or the name is wrong&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Every Tool Is Special
&lt;/h3&gt;

&lt;p&gt;Each tool has its own special snowflake implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Babel: Most complex rules&lt;/li&gt;
&lt;li&gt;ESLint: Clear patterns but different prefixes&lt;/li&gt;
&lt;li&gt;Docusaurus: Scoped packages only&lt;/li&gt;
&lt;li&gt;PostCSS: Mostly exact matches&lt;/li&gt;
&lt;li&gt;Rollup: Different scope handling&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Implementation Deep Dive
&lt;/h2&gt;
&lt;h3&gt;
  
  
  How Resolution Actually Works
&lt;/h3&gt;

&lt;p&gt;Most tools implement something like this:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resolveModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moduleType&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;attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                    &lt;span class="c1"&gt;// exact&lt;/span&gt;
    &lt;span class="s2"&gt;`@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;toolName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// official&lt;/span&gt;
    &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;toolName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;     &lt;span class="c1"&gt;// community&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;for &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;attempt&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&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;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &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="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="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Cannot resolve &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;h3&gt;
  
  
  Scoped Package Handling
&lt;/h3&gt;

&lt;p&gt;For scoped packages like &lt;code&gt;@my-company/awesome&lt;/code&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resolveScopedModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moduleType&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;packageName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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="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;packageName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// @scope only → @scope/docusaurus-plugin&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;toolName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// @scope/name → try both forms&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                           &lt;span class="c1"&gt;// @scope/name&lt;/span&gt;
    &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;toolName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;packageName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="c1"&gt;// @scope/docusaurus-plugin-name&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// ... resolution logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Survive the Shorthand Apocalypse
&lt;/h2&gt;

&lt;h3&gt;
  
  
  For Tool Authors (Please Listen!)
&lt;/h3&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%2Fi.imgflip.com%2F7kqc8q.jpg" 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%2Fi.imgflip.com%2F7kqc8q.jpg" alt="Drake meme" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Drake rejecting: Showing only shorthands in examples&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Drake approving: Showing both shorthand AND full names&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Document both forms&lt;/strong&gt;: Show &lt;code&gt;'classic'&lt;/code&gt; AND &lt;code&gt;@docusaurus/preset-classic&lt;/code&gt; in examples&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better error messages&lt;/strong&gt;: Instead of "Cannot resolve 'classic'", show "Tried: classic, @docusaurus/preset-classic, docusaurus-preset-classic"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-reference docs&lt;/strong&gt;: Link to your shorthand explanation from EVERY config example&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent patterns&lt;/strong&gt;: Don't reinvent the wheel - follow Babel/ESLint conventions&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  For Developers (My Hard-Learned Lessons)
&lt;/h3&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%2Fi.imgflip.com%2F7kqc8r.jpg" 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%2Fi.imgflip.com%2F7kqc8r.jpg" alt="Disaster Girl meme" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Me after learning about shorthands: Everything is fine&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;When confused, use full names&lt;/strong&gt;: &lt;code&gt;@docusaurus/preset-classic&lt;/code&gt; never lies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check your package.json&lt;/strong&gt;: See what's actually installed vs what you're referencing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use IDE superpowers&lt;/strong&gt;: Many editors show resolved names on hover&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debug systematically&lt;/strong&gt;: Replace shorthands with full names when things break&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn the patterns&lt;/strong&gt;: Once you know Babel's rules, you can guess others&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Debugging Shorthand Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Common Problems
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Package not installed&lt;/strong&gt;: Shorthand resolves but package missing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong resolution&lt;/strong&gt;: Resolves to unexpected package&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typos&lt;/strong&gt;: Small mistakes in shorthand names&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version conflicts&lt;/strong&gt;: Multiple packages match pattern&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Debugging Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Try the full name&lt;/strong&gt;: Replace shorthand with full package name&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check installation&lt;/strong&gt;: Verify package exists in &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check resolution&lt;/strong&gt;: Use tool's debug mode if available&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify naming&lt;/strong&gt;: Double-check official package names&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Debug Tools
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check what's installed&lt;/span&gt;
npm list | &lt;span class="nb"&gt;grep &lt;/span&gt;preset

&lt;span class="c"&gt;# Try resolving manually&lt;/span&gt;
node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"console.log(require.resolve('@docusaurus/preset-classic'))"&lt;/span&gt;

&lt;span class="c"&gt;# Enable debug mode (tool-specific)&lt;/span&gt;
&lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docusaurus&lt;span class="k"&gt;*&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Future
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Trends
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;More tools adopting&lt;/strong&gt;: Pattern spreading to newer tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better error messages&lt;/strong&gt;: Tools improving debugging experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IDE integration&lt;/strong&gt;: Better autocomplete and resolution hints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standardization&lt;/strong&gt;: Some movement toward common patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Recommendations
&lt;/h3&gt;

&lt;p&gt;For the ecosystem to improve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Better documentation&lt;/strong&gt;: Always show both forms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved tooling&lt;/strong&gt;: Better IDE support for resolution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error message standards&lt;/strong&gt;: Show resolution attempts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community education&lt;/strong&gt;: More articles like this one!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Final Revelation
&lt;/h2&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%2Fi.imgflip.com%2F7kqc8s.jpg" 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%2Fi.imgflip.com%2F7kqc8s.jpg" alt="Galaxy brain meme" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Small brain: Getting confused by 'classic'&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Medium brain: Understanding it's a shorthand&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Large brain: Recognizing the pattern everywhere&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Galaxy brain: Writing an article to help other confused developers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After my 3-hour journey down the shorthand rabbit hole, I've learned that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's not magic&lt;/strong&gt;: Just very well-hidden conventions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's everywhere&lt;/strong&gt;: Once you see it, you can't unsee it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's actually helpful&lt;/strong&gt;: When you understand the rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's poorly documented&lt;/strong&gt;: The connection between examples and explanations is often missing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What This Means for You
&lt;/h3&gt;

&lt;p&gt;Next time you see &lt;code&gt;'env'&lt;/code&gt;, &lt;code&gt;'classic'&lt;/code&gt;, &lt;code&gt;'airbnb'&lt;/code&gt;, or any other mysterious string in a JavaScript config:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Don't panic&lt;/strong&gt;: It's probably a shorthand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the tool's docs&lt;/strong&gt;: Look for "module resolution" or "name normalization"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try the full name&lt;/strong&gt;: When in doubt, be explicit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn the pattern&lt;/strong&gt;: Each tool follows similar rules&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The JavaScript ecosystem loves its conventions, but sometimes forgets to explain them to newcomers. Now you're in on the secret!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you been confused by JavaScript module shorthands? Share your war stories in the comments - let's make the ecosystem more welcoming for everyone! 🚀&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;: I work on &lt;a href="https://tolgee.io" rel="noopener noreferrer"&gt;Tolgee&lt;/a&gt;, an open-source localization platform that makes i18n simple for developers. If you're building multilingual apps and tired of complex translation workflows, check us out!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docusaurus.io/docs/using-plugins#module-shorthands" rel="noopener noreferrer"&gt;Docusaurus Module Shorthands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://babeljs.io/docs/options#name-normalization" rel="noopener noreferrer"&gt;Babel Name Normalization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://eslint.org/docs/latest/use/configure/plugins#configuring-plugins" rel="noopener noreferrer"&gt;ESLint Plugin Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Convention_over_configuration" rel="noopener noreferrer"&gt;Convention over Configuration - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>babel</category>
      <category>beginners</category>
      <category>tooling</category>
    </item>
    <item>
      <title>🚀 React Compiler is now in RC! No more manual memoization — just write React, and the compiler optimizes it for you. Still experimental, but a big leap forward. 🔗 https://react.dev/blog/2025/04/21/react-compiler-rc #react #javascript #webdev</title>
      <dc:creator>Kamil Dzieniszewski</dc:creator>
      <pubDate>Sat, 10 May 2025 18:49:51 +0000</pubDate>
      <link>https://dev.to/dzienisz/react-compiler-is-now-in-rc-no-more-manual-memoization-just-write-react-and-the-compiler-1inm</link>
      <guid>https://dev.to/dzienisz/react-compiler-is-now-in-rc-no-more-manual-memoization-just-write-react-and-the-compiler-1inm</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://react.dev/blog/2025/10/07/react-compiler-1" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Freact.dev%2Fimages%2Fog-blog.png" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://react.dev/blog/2025/10/07/react-compiler-1" rel="noopener noreferrer" class="c-link"&gt;
            React Compiler v1.0 – React
          &lt;/a&gt;
        &lt;/h2&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Freact.dev%2Ffavicon-32x32.png"&gt;
          react.dev
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>experimental</category>
    </item>
  </channel>
</rss>
