<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>README — javascript</title>
<link>https://readme.news/tags/javascript/</link>
<atom:link href="https://readme.news/tags/javascript/feed.xml" rel="self" type="application/rss+xml"/>
<description>README pieces tagged javascript.</description>
<language>en-us</language>
<lastBuildDate>Thu, 01 Oct 2026 13:20:31 +0000</lastBuildDate>
<item><title>Node 26 and the build step that finally disappeared</title><link>https://readme.news/node-26-and-the-build-step-that-finally-disappeared/</link><guid isPermaLink="true">https://readme.news/node-26-and-the-build-step-that-finally-disappeared/</guid><pubDate>Fri, 03 Apr 2026 09:00:00 +0000</pubDate><description>Type stripping, a mature test runner, and a standard library that absorbed the ecosystem. You can ship TypeScript with no toolchain.</description><content:encoded><![CDATA[<p>Node.js 26 landed this week, and the accumulated effect of the last several major versions is worth stating plainly: <strong>you can now write a TypeScript server application, test it, and run it in production, with zero build tooling.</strong></p>
<p>That was not true three years ago and it is a meaningful simplification.</p>
<h2 id="the-workflow">the workflow<a class="anchor" href="#the-workflow" aria-label="link to this section">#</a></h2>
<div class="code"><span class="code-lang">json</span><pre><code class="lang-json">{
  "compilerOptions": {
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true,
    "module": "nodenext",
    "strict": true,
    "noEmit": true
  }
}</code></pre></div>
<div class="code"><span class="code-lang">bash</span><pre><code class="lang-bash">node --test          # test runner, built in
node --watch app.ts  # dev, with reload, running TypeScript directly
npx tsc              # typecheck only, in CI
node app.ts          # production</code></pre></div>
<p>No bundler. No transpiler in the runtime path. No <code>ts-node</code>. No <code>nodemon</code>. No <code>dotenv</code>. No test framework. No <code>node-fetch</code>.</p>
<p>The <code>erasableSyntaxOnly</code> flag is what makes it safe: it rejects any TypeScript syntax that cannot be removed by whitespace substitution — enums, namespaces with runtime values, parameter properties — so your source is guaranteed to run under type stripping.</p>
<h2 id="what-got-absorbed-cumulatively">what got absorbed, cumulatively<a class="anchor" href="#what-got-absorbed-cumulatively" aria-label="link to this section">#</a></h2>
<p>Over the last several major versions Node has taken into the runtime:</p>
<div class="table-wrap"><table><thead><tr><th style="text-align:left">capability</th><th style="text-align:left">the package it replaced</th></tr></thead><tbody><tr><td style="text-align:left">test runner</td><td style="text-align:left">jest, mocha, ava</td></tr><tr><td style="text-align:left">watch mode</td><td style="text-align:left">nodemon</td></tr><tr><td style="text-align:left"><code>.env</code> loading</td><td style="text-align:left">dotenv</td></tr><tr><td style="text-align:left">TypeScript execution</td><td style="text-align:left">ts-node, tsx</td></tr><tr><td style="text-align:left">SQLite</td><td style="text-align:left">better-sqlite3</td></tr><tr><td style="text-align:left"><code>fetch</code>, WebSocket</td><td style="text-align:left">node-fetch, ws, axios</td></tr><tr><td style="text-align:left"><a class="xref" href="/nodejs-24-and-the-slow-reinvention-of-the-runtime/" title="Node.js 24 and the slow reinvention of the runtime">permission model</a></td><td style="text-align:left">(nothing existed)</td></tr><tr><td style="text-align:left"><code>using</code> / disposal</td><td style="text-align:left">various</td></tr></tbody></table></div>
<p>Every one of those is a dependency removed, which is a supply chain surface removed, an upgrade treadmill removed, and a <code>postinstall</code> script removed.</p>
<p>For a small service, a fresh project's dependency count is now dramatically lower than it would have been in 2022. That is the most important consequence and it is rarely framed that way.</p>
<h2 id="the-division-of-labor-that-makes-sense">the division of labor that makes sense<a class="anchor" href="#the-division-of-labor-that-makes-sense" aria-label="link to this section">#</a></h2>
<p><strong>Typechecking belongs in CI and your editor.</strong> Not in the production runtime hot path. <code>tsc --noEmit</code> in CI, language server in your editor, type stripping at runtime.</p>
<p>A type error will happily run under stripping and fail at runtime. That is the correct trade — you check types where checking is cheap and run where running should be fast — and it requires that CI actually gates on the typecheck. If it does not, you have given up types.</p>
<p><strong>Bundling is still right for some things.</strong> If you deploy to a serverless platform where cold start scales with file count, bundling helps. If you ship to browsers, obviously. For a long-running server process, it buys you nothing.</p>
<h2 id="what-is-still-missing">what is still missing<a class="anchor" href="#what-is-still-missing" aria-label="link to this section">#</a></h2>
<p><strong>Decorators.</strong> They are not erasable — they generate runtime code. If your framework depends on decorator-based dependency injection or routing, you need a transform. Several major frameworks are in this category, and it is the single most common blocker for adopting the no-build workflow.</p>
<p><strong>Path aliases.</strong> <code>@/components/foo</code> requires resolution mapping. Node supports <code>imports</code> in <code>package.json</code> with the <code>#</code> prefix, which works and requires changing your import style.</p>
<p><strong>Anything in your build that is not TypeScript.</strong> CSS modules, GraphQL documents, asset imports. If your build does more than strip types, you still have a build.</p>
<h2 id="the-migration-advice">the migration advice<a class="anchor" href="#the-migration-advice" aria-label="link to this section">#</a></h2>
<p>If you are on Node 22 or earlier, plan the move. Check:</p>
<ul><li>Base image compatibility — minimum glibc and macOS versions have moved.</li><li>Deprecated APIs that now throw rather than warn.</li><li>Native modules, which need a rebuild.</li></ul>
<p>Run the full suite on the new version in CI before switching the default. The upgrade is usually uneventful and "usually" is load-bearing.</p>
<h2 id="the-broader-observation">the broader observation<a class="anchor" href="#the-broader-observation" aria-label="link to this section">#</a></h2>
<p>Node's response to competitive pressure from Deno and Bun was to absorb their best ideas rather than to argue about philosophy.</p>
<p>That was correct, it took about four years, and the ecosystem is meaningfully better for it. Competition in developer tooling works, and the incumbent that responds by improving rather than by defending is the one that keeps the position.</p>]]></content:encoded></item><item><title>Node.js 24 goes LTS and the runtime wars settle into a truce</title><link>https://readme.news/nodejs-24-goes-lts-and-the-runtime-wars-settle-into-a-truce/</link><guid isPermaLink="true">https://readme.news/nodejs-24-goes-lts-and-the-runtime-wars-settle-into-a-truce/</guid><pubDate>Tue, 28 Oct 2025 09:00:00 +0000</pubDate><description>Long-term support for the batteries-included Node, plus a look at where Deno and Bun actually landed.</description><content:encoded><![CDATA[<p>Node.js 24 entered long-term support this week. That makes it the version most teams will run for the next two years, and it is a good moment to take stock of the three-way runtime competition that has defined server-side JavaScript since 2022.</p>
<h2 id="what-you-get-in-24-lts">what you get in 24 LTS<a class="anchor" href="#what-you-get-in-24-lts" aria-label="link to this section">#</a></h2>
<p>The accumulated result of the last two years of Node absorbing its own ecosystem:</p>
<ul><li><strong>A test runner</strong> (<code>node --test</code>) with coverage, mocking, and watch mode.</li><li><strong>TypeScript type stripping</strong>, no build step.</li><li><strong>A <a class="xref" href="/nodejs-24-and-the-slow-reinvention-of-the-runtime/" title="Node.js 24 and the slow reinvention of the runtime">permission model</a></strong> for filesystem, network, and child process access.</li><li><strong><code>.env</code> file loading</strong> built in.</li><li><strong>A SQLite module</strong> in the standard library.</li><li><strong><code>fetch</code>, <code>WebSocket</code>, <code>URLPattern</code></strong> as globals.</li><li><strong><code>using</code> declarations</strong> for deterministic resource cleanup.</li><li><strong>A watch mode</strong> that does not require nodemon.</li></ul>
<p>Every one of those replaced a dependency. That is the whole story of Node's last three years, and it was the correct response to competitive pressure.</p>
<h2 id="where-the-three-landed">where the three landed<a class="anchor" href="#where-the-three-landed" aria-label="link to this section">#</a></h2>
<p><strong>Node</strong> won by absorbing. It has the ecosystem, the deployment targets, the enterprise support, and now most of the convenience. Its remaining weaknesses are startup time and the accumulated weight of API decisions from 2010 that cannot be changed.</p>
<p><strong>Deno</strong> built the best <a class="xref" href="/the-component-model-and-the-plugin-problem/" title="The component model and the plugin problem">security model</a> and the most coherent standard library, then spent years walking back the decisions that made adoption hard — first adding npm compatibility, then <code>node_modules</code>, then <code>package.json</code>. It is excellent and it is a niche, and the trademark dispute over "JavaScript" was an unusual way to spend a year.</p>
<p><strong>Bun</strong> won on speed and on the package manager. <code>bun install</code> is dramatically faster than npm and a very large number of teams use it for exactly that while running Node in production. That is a real and defensible position — being the best tool for one step of the workflow.</p>
<h2 id="what-actually-changed-for-developers">what actually changed for developers<a class="anchor" href="#what-actually-changed-for-developers" aria-label="link to this section">#</a></h2>
<p>The competition worked. All three runtimes are much better than Node was in 2021, and Node specifically improved in ways it had resisted for a decade.</p>
<p>That is the argument for competition in developer tooling, and it is worth remembering when a dominant tool seems permanent.</p>
<h2 id="practical-guidance">practical guidance<a class="anchor" href="#practical-guidance" aria-label="link to this section">#</a></h2>
<p><strong>For a new production service:</strong> Node 24 LTS. The ecosystem compatibility and operational maturity dominate, and the convenience gap has closed.</p>
<p><strong>For a CLI or a script:</strong> Bun, probably. Startup time and the single-binary story are genuinely better, and the compatibility risk is low for a program you control end to end.</p>
<p><strong>For anything where sandboxing matters:</strong> Deno's permission model is the most mature, though Node's is now credible.</p>
<p><strong>For your package manager:</strong> Bun or pnpm. npm is fine and it is slower, and the difference on a large install is minutes rather than seconds.</p>
<h2 id="the-migration-note">the migration note<a class="anchor" href="#the-migration-note" aria-label="link to this section">#</a></h2>
<p>If you are on Node 20, plan the move to 24. Node 22 is a reasonable interim stop and there is no strong reason to linger there.</p>
<p>Things to check:</p>
<ul><li><strong>Base image compatibility.</strong> Node 24 raises minimum glibc and macOS versions.</li><li><strong>Deprecated APIs that now throw</strong> rather than warning.</li><li><strong>Native modules.</strong> Anything with a compiled component needs a rebuild and possibly an upgrade.</li></ul>
<p>Run your full test suite on 24 in CI before you switch the default. The upgrade is usually uneventful and "usually" is doing work in that sentence.</p>]]></content:encoded></item><item><title>Node.js 24 and the slow reinvention of the runtime</title><link>https://readme.news/nodejs-24-and-the-slow-reinvention-of-the-runtime/</link><guid isPermaLink="true">https://readme.news/nodejs-24-and-the-slow-reinvention-of-the-runtime/</guid><pubDate>Tue, 06 May 2025 09:00:00 +0000</pubDate><description>V8 13.6, npm 11, `fetch` no longer experimental, and the permission model losing its dashes.</description><content:encoded><![CDATA[<p>Node.js 24 is out. It becomes LTS in October. The individual items are small; the direction they add up to is not.</p>
<h2 id="whats-in-it">what's in it<a class="anchor" href="#whats-in-it" aria-label="link to this section">#</a></h2>
<p><strong>V8 13.6.</strong> Brings <code>RegExp.escape</code> (finally), <code>Float16Array</code>, <code>Atomics.pause</code>, and explicit resource management — the <code>using</code> declaration:</p>
<div class="code"><span class="code-lang">js</span><pre><code class="lang-js">{
  using file = await open("data.txt");
  // ...
}  // file[Symbol.asyncDispose]() called automatically</code></pre></div>
<p>That last one is a genuinely useful addition to the language. Deterministic cleanup without try/finally pyramids, and it composes with async.</p>
<p><strong>npm 11.</strong> Faster, stricter, and with better handling of the lifecycle-script security surface.</p>
<p><strong>AsyncLocalStorage</strong> now defaults to <code>AsyncContextFrame</code>, which is a substantially faster implementation. If you use context propagation for request tracing — and if you have an observability stack, you do — this is a real performance improvement you get for free.</p>
<p><strong>The permission model drops the experimental dashes.</strong> <code>--permission</code> instead of <code>--experimental-permission</code>.</p>
<div class="code"><span class="code-lang">bash</span><pre><code class="lang-bash">node --permission --allow-fs-read=./data --allow-net=api.example.com app.js</code></pre></div>
<p><strong>URLPattern</strong> is global. <strong>Undici updated</strong>, so <code>fetch</code> behavior tracks the platform more closely.</p>
<h2 id="the-direction">the direction<a class="anchor" href="#the-direction" aria-label="link to this section">#</a></h2>
<p>Look at what Node has added over the last three major versions: a test runner, a watch mode, <code>.env</code> file support, a permission model, TypeScript type stripping, a SQLite module, and a full fetch stack.</p>
<p>Every one of those was previously a dependency. <code>jest</code> or <code>mocha</code>. <code>nodemon</code>. <code>dotenv</code>. Nothing, because there was no sandbox. <code>ts-node</code>. <code>better-sqlite3</code>. <code>node-fetch</code> or <code>axios</code>.</p>
<p>Node is absorbing its own ecosystem's most common packages into the runtime. This is a direct response to Deno and Bun, both of which shipped batteries-included from day one and made Node's "small core" philosophy look like an excuse.</p>
<p>I think this is correct and overdue. "Small core, rich ecosystem" was a reasonable position in 2012, when the ecosystem was small and trustworthy. In 2025, when a fresh Express app pulls three hundred transitive dependencies and each one is a supply chain risk, every capability moved into the runtime is one fewer package with a <code>postinstall</code> script.</p>
<h2 id="the-migration-notes">the migration notes<a class="anchor" href="#the-migration-notes" aria-label="link to this section">#</a></h2>
<ul><li>Node 24 requires a newer minimum glibc and macOS version. Check your base images.</li><li>Some deprecated APIs finally throw instead of warning. Run your test suite before you upgrade, not after.</li><li>If you are on 20, plan to go to 24 when it hits LTS in October rather than stopping at 22.</li></ul>
<h2 id="the-type-stripping-question">the type stripping question<a class="anchor" href="#the-type-stripping-question" aria-label="link to this section">#</a></h2>
<p>Node can now run <code>.ts</code> files by erasing types. It does not typecheck. That is the correct division of labor — typechecking belongs in your editor and your CI, not in your production runtime hot path — but it does mean a type error will happily run and fail at runtime.</p>
<p>The workflow that makes sense: <code>tsc --noEmit</code> in CI, <code>node app.ts</code> in production, <code>erasableSyntaxOnly</code> set so you cannot accidentally use syntax that requires a transform.</p>
<p>No build step. That is a real ergonomic win and it has been a long time coming.</p>]]></content:encoded></item><item><title>TypeScript 5.8 quietly bets on type stripping</title><link>https://readme.news/typescript-58-quietly-bets-on-type-stripping/</link><guid isPermaLink="true">https://readme.news/typescript-58-quietly-bets-on-type-stripping/</guid><pubDate>Sat, 01 Mar 2025 09:00:00 +0000</pubDate><description>`--erasableSyntaxOnly` exists because Node can now run TypeScript, and enums can&#x27;t be erased.</description><content:encoded><![CDATA[<p>TypeScript 5.8 shipped with a flag that looks like a footnote and is actually a statement about where the language is going: <code>--erasableSyntaxOnly</code>.</p>
<h2 id="the-context">the context<a class="anchor" href="#the-context" aria-label="link to this section">#</a></h2>
<p>Node.js can now strip TypeScript types and run the resulting JavaScript directly. Deno and Bun have done this for a while. The approach is deliberately dumb — it does not typecheck, it does not transform, it replaces type annotations with whitespace and runs what is left.</p>
<p>That works beautifully for the ninety-five percent of TypeScript that is JavaScript plus annotations. It breaks completely for the parts of TypeScript that <em>generate code</em>:</p>
<div class="code"><span class="code-lang">ts</span><pre><code class="lang-ts">enum Color { Red, Green }           // emits an object at runtime
namespace Utils { export const x = 1 }  // emits an IIFE
class Point {
  constructor(private x: number) {}  // parameter properties emit assignments
}
declare module "foo" {}              // fine, erasable</code></pre></div>
<p>You cannot erase an enum. There is nothing to erase to; the enum <em>is</em> runtime code wearing type-shaped syntax.</p>
<h2 id="what-the-flag-does">what the flag does<a class="anchor" href="#what-the-flag-does" aria-label="link to this section">#</a></h2>
<p><code>erasableSyntaxOnly: true</code> makes the compiler reject any construct that cannot be removed by whitespace substitution. Enums, namespaces with runtime values, parameter properties, and old-style <code>import =</code> are all errors.</p>
<div class="code"><span class="code-lang">json</span><pre><code class="lang-json">{
  "compilerOptions": {
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true,
    "module": "nodenext"
  }
}</code></pre></div>
<p>Turn it on and your TypeScript is guaranteed to run under any type-stripping runtime with no build step at all.</p>
<h2 id="the-alternatives-to-what-it-bans">the alternatives to what it bans<a class="anchor" href="#the-alternatives-to-what-it-bans" aria-label="link to this section">#</a></h2>
<p>You are losing very little.</p>
<p>Instead of an enum, a const object with a derived union type — which most codebases already prefer because it produces cleaner types and does not have <code>enum</code>'s bizarre bidirectional-mapping behavior:</p>
<div class="code"><span class="code-lang">ts</span><pre><code class="lang-ts">const Color = { Red: "red", Green: "green" } as const;
type Color = typeof Color[keyof typeof Color];  // "red" | "green"</code></pre></div>
<p>Instead of parameter properties, an explicit assignment. Two extra lines, and the ES class fields proposal made the ergonomics fine anyway.</p>
<p>Instead of namespaces, modules. You should have done this in 2019.</p>
<p><code>const enum</code> remains available with <code>preserveConstEnums</code>, but the honest advice is to stop using it — it has never played well with isolated module compilation and it never will.</p>
<h2 id="also-in-58">also in 5.8<a class="anchor" href="#also-in-58" aria-label="link to this section">#</a></h2>
<ul><li><strong>Checked returns for conditional expressions.</strong> Return statements whose type is a conditional now get checked against each branch individually rather than against the collapsed union, which catches a real class of bug.</li><li><strong><code>--module nodenext</code> supports <code>require()</code> of ESM</strong>, tracking Node's own change.</li><li><strong>Faster program updates</strong> on the editor path, which you will feel in a large monorepo more than any feature.</li></ul>
<h2 id="the-direction">the direction<a class="anchor" href="#the-direction" aria-label="link to this section">#</a></h2>
<p>The build step for TypeScript is dissolving. Between type stripping in runtimes, <code>tsc --noEmit</code> as a pure typechecker in CI, and bundlers that handle TypeScript natively, the days of TypeScript-as-a-compile-target are ending.</p>
<p>What remains is TypeScript as a <a class="xref" href="/type-systems-and-the-cost-of-being-right/" title="Type systems and the cost of being right">type system</a> layered on JavaScript — which is what it always claimed to be and only recently became true. Set the flag, delete your enums, and stop thinking about the build.</p>]]></content:encoded></item><item><title>Bun 1.2 stops being a runtime and starts being a platform</title><link>https://readme.news/bun-12-stops-being-a-runtime-and-starts-being-a-platform/</link><guid isPermaLink="true">https://readme.news/bun-12-stops-being-a-runtime-and-starts-being-a-platform/</guid><pubDate>Fri, 17 Jan 2025 09:00:00 +0000</pubDate><description>Native S3 and Postgres clients, a real Node compatibility push, and a bundled package manager that keeps getting faster.</description><content:encoded><![CDATA[<p>Bun 1.2 landed this week and the release is a good moment to notice what the project has actually become. It started as "a fast JavaScript runtime." It is now attempting to be the entire server-side JavaScript toolchain in one binary, and the 1.2 feature list is unsubtle about it.</p>
<h2 id="whats-new">what's new<a class="anchor" href="#whats-new" aria-label="link to this section">#</a></h2>
<p><strong>A built-in S3 client.</strong> <code>Bun.s3</code> gives you presigned URLs, streaming reads and writes, and it works against any S3-compatible endpoint — R2, MinIO, Backblaze, the actual thing.</p>
<div class="code"><span class="code-lang">js</span><pre><code class="lang-js">import { s3 } from "bun";

const file = s3.file("uploads/report.pdf");
await file.write(pdfBytes, { type: "application/pdf" });
const url = file.presign({ expiresIn: 3600 });</code></pre></div>
<p><strong>A built-in Postgres client.</strong> <code>Bun.sql</code> is a tagged-template SQL <a class="xref" href="/the-interface-is-the-product/" title="The interface is the product">interface</a> with <a class="xref" href="/connection-pooling-explained-properly/" title="Connection pooling, explained properly">connection pooling</a>, prepared statements, and parameter binding that does not require you to think about <code>$1</code> ordering.</p>
<div class="code"><span class="code-lang">js</span><pre><code class="lang-js">import { sql } from "bun";
const users = await sql`select * from users where team = ${teamId} limit 20`;</code></pre></div>
<p><strong>Node compatibility as a first-class metric.</strong> Bun now publishes its pass rate against Node's own test suite and treats regressions as bugs. That is a much more honest signal than a feature checklist, and the number went up substantially in this cycle.</p>
<p><strong>Text-based lockfile.</strong> <code>bun.lock</code> replaces the binary <code>bun.lockb</code> as the default. Reviewable in a pull request, diffable, mergeable. This was the single most common complaint about adopting Bun in a team setting and it is now gone.</p>
<h2 id="the-strategic-read">the strategic read<a class="anchor" href="#the-strategic-read" aria-label="link to this section">#</a></h2>
<p>Node's answer to the same problem has been to add capabilities to the runtime incrementally and carefully: a built-in test runner, <code>--watch</code>, a <a class="xref" href="/nodejs-24-and-the-slow-reinvention-of-the-runtime/" title="Node.js 24 and the slow reinvention of the runtime">permission model</a>, and now stable-ish TypeScript stripping. Deno's answer was to bundle everything from the start and then spend years walking back the parts of its opinionated design that made adoption hard.</p>
<p>Bun's answer is to bundle everything <em>and</em> be compatible with Node's ecosystem rather than replacing it. That is a harder engineering problem and a much easier sales problem. Nobody has to rewrite anything. You point <code>bun</code> at an existing Express app and it mostly runs.</p>
<h2 id="the-honest-caveats">the honest caveats<a class="anchor" href="#the-honest-caveats" aria-label="link to this section">#</a></h2>
<p>Bun's ecosystem edge cases still bite. Native modules that assume V8 internals will not work, because Bun is JavaScriptCore. Some observability agents assume Node. Long-tail packages that reach into <code>process.binding</code> or undocumented internals will surprise you.</p>
<p>The pattern for adoption in 2025 looks like: use Bun as the package manager and test runner immediately, because those are drop-in and dramatically faster; use the runtime in production when your dependency tree is one you actually understand.</p>
<p>That is not a hedge. That is just what shipping looks like.</p>]]></content:encoded></item>
</channel>
</rss>
