Sign in

David Sherret

@dsherret.dev
1.5K followers 80 following 153 posts

Software developer. Prev @deno.land. Working on ts-morph, dprint, and more (github.com/dsherret)

PostsRepliesMedia
David Sherret @dsherret.dev · 30/07/2026
Also, support for multiple files was added by Gerrit Birkeland.
Example showing multiple tabs representing multiple files on ts-ast-viewer.com
030
David Sherret @dsherret.dev · 30/07/2026
ts-ast-viewer.com now supports TypeScript 7 along with nightly builds of the typescript-go repository. It's not selected by default, but available in the dropdown.
Dropdown menu showing a TypeScript 7.0.2 selection on ts-ast-viewer.com
2295
David Sherret @dsherret.dev · 30/06/2026
Released dprint 0.55—a pluggable and configurable code formatting platform that unifies all your formatters behind a single command. This substantial release lets you specify formatting plugins from npm.
A dprint.json configuration file with npm specifiers in the plugins array.
2101
David Sherret @dsherret.dev · 26/05/2026
People should be aware of the practices of the critical infrastructure they rely on. pnpm is maintained by volunteers, but bun has a billion dollar company behind them and was acquired for many millions. Zero published CVEs is inexcusable and irresponsible.
140
David Sherret @dsherret.dev · 25/05/2026
Reporting the same security issue to pnpm vs bun: pnpm acknowledged it quickly, shipped a fix, backported it, and published an advisory. bun never acknowledged, silently fixed this and another issue I reported, has not published advisories. One takes security more seriously.
Prompt response from pnpm. Fixed and published.No response on github from bun.No response from bun on email.
41116
David Sherret @dsherret.dev · 04/05/2026
Or alternatively, use the new `$.all` function.
110
David Sherret @dsherret.dev · 04/05/2026
It works well with progress bars, selection, and other features that dax provides.
120
David Sherret @dsherret.dev · 04/05/2026
The latest version of dax (dax.land) can show a scrolling view of the last N lines when executing a command. This is especially useful when running multiple commands in parallel.
2141
David Sherret @dsherret.dev · 23/04/2026
Released gagen 0.5, which simplifies the output to match dependabot.
160
David Sherret @dsherret.dev · 02/04/2026
I've found maintaining GitHub Actions workflow files is much easier when it's generated from Typescript. The latest realization is that the output yaml file can act like a lockfile. So I maintain `actions/checkout@v6`, but the generated output gets locked to a hash.
1492
David Sherret @dsherret.dev · 20/03/2026
Released jsonc-parser 0.32, which allows deserializing via serde to a concrete type. It's also much faster.
160
David Sherret @dsherret.dev · 25/02/2026
dprint's incremental formatter is incredibly fast and happens without needing an opt-in. You can use both oxc and biome via dprint for faster performance.
150
David Sherret @dsherret.dev · 13/02/2026
It's good npmx.dev shows git and https dependencies. I still think it's crazy npmjs.org doesn't.
1281
David Sherret @dsherret.dev · 12/02/2026
I didn't want a terminal based solution, so I vibe coded a desktop app to give the status of all my AI sessions regardless of where they're running and I can click to quickly jump to them.
040
David Sherret @dsherret.dev · 04/02/2026
Several popular npm packages transitively have this problem, including a recent popular one. It's only a matter of time before someone takes advantage of this.
120
David Sherret @dsherret.dev · 04/02/2026
Both of these are also possible using https dependencies in an npm package. Worst part is in some cases the package wouldn't easily be able to be taken down. npm, pnpm, and bun all happily install this kind of stuff—`deno install` errors.
140
David Sherret @dsherret.dev · 22/01/2026
The deno and dprint vscode extensions now prompt when a custom executable path is set in the workspace configuration. I believe this should help reduce the chance of malicious config launching an executable in a trusted workspace.
040
David Sherret @dsherret.dev · 21/01/2026
Vibe coded a website to give a summary of our frequently failing and flaky tests on the CI.
290
David Sherret @dsherret.dev · 17/12/2025
In the Deno repo, we have a custom Rust test runner for our tests in order to support file/directory-based tests and improve stability on the CI. I just refactored the code to have a pty reporter that surfaces some useful information. It seems better than having failures whizz by.
1130
David Sherret @dsherret.dev · 05/12/2025
The next Deno release will include Node type declarations out of the box.
Using the `module` global in a cts file with type completions.
1141
David Sherret @dsherret.dev · 25/06/2025
Type checking text and bytes imports is now working.
Example code snippet that type checks text and bytes imports.
190
David Sherret @dsherret.dev · 20/06/2025
Yup.
Importing a module as bytes and text with a dynamic import.
020
David Sherret @dsherret.dev · 20/06/2025
Should have importing files as text and bytes ready for Deno 2.4 -- will be unstable because it hasn't been standardized.
Importing a file as text and bytes with an import attribute.
4252
David Sherret @dsherret.dev · 04/06/2025
It's very untested and probably won't work in many scenarios, but it's a start. Bugs can be reported here: github.com/denoland/rol...
010
David Sherret @dsherret.dev · 31/05/2025
Got bundling JSR/Deno code with Rolldown working. This is using Rolldown's and Deno's crates respecting Deno's lockfile, config discovery, and resolution. Finally Deno's crates have been refactored enough for this to be feasible (~500 LOC). I'll update with more details later.
Terminal output showing a JSR specifier being bundled to a single file then executed.

> ../rolldown/target/debug/rolldown_deno.exe jsr:@david/dax@0.43
Bundle jsr:@david/dax@0.43
Writing: dax.bundle.js
Writing: dax.bundle.js.map
> deno
Deno 2.3.5
exit using ctrl+d, ctrl+c, or close()
REPL is running with all permissions allowed.
To specify permissions, run `deno repl` with allow flags.
> import $ from "./dax.bundle.js"
undefined
> await $`echo 'Hello there!' && echo 'This is from a bundled copy.'`
Hello there!
This is from a bundled copy.
CommandResult { code: 0 }
2151
David Sherret @dsherret.dev · 27/03/2025
I've come across so many bugs caused by using wildcard matches in Rust. Even though it can be verbose, I think it's better to avoid them in most cases so that adding a new enum variant causes compiler errors, forcing you to re-evaluate each match.
1161
David Sherret @dsherret.dev · 20/03/2025
It was getting difficult to debug Deno's npm resolution so I wrote a tool to help visualize each step it makes.
190
David Sherret @dsherret.dev · 25/02/2025
Neat to see software I used to work on and features I helped implement in Severance.
Neurosurgical planning software seen in the show Severance.
1251
David Sherret @dsherret.dev · 07/02/2025
Beware: npm doesn't show non-npm dependencies in the dependencies tab. Check out this package—npm lists it as having zero dependencies, but if you look at the package.json, it definitely has dependencies.
The @dsherret/outside-npm package having zero dependencies.The package.json showing two dependencies.
5364
David Sherret @dsherret.dev · 07/02/2025
Did some work on the url Rust crate the past two days and got it parsing a 400 character URL more than twice as fast (still have a few PRs not merged).
Mean: 1903ns to 841ns
0113
David Sherret @dsherret.dev · 05/02/2025
When it's only that they're all the same, but when there's __esModule = true then Bun behaves incompatibly with Node.
All the same for just module.exports.defaultBun making the `default` property the default export once there's __esModule = true
020
David Sherret @dsherret.dev · 05/02/2025
It's a confusing topic. Just to be clear, Deno used to do that, but decided to align with Node for better Node compatibility. Here's the output of that today.
Deno and Node where "module.exports" is the default export and in Bun where the defualt export is "module.exports.default"
101
David Sherret @dsherret.dev · 05/02/2025
I feel like making node resolution "just work" leaves the JS ecosystem in a worse place.
Bun checking the __esModule property to inform the behaviour of a default import. Bun errors, but Node and Deno don't.
170
David Sherret @dsherret.dev · 30/01/2025
@kt3k.org thought of a clever way to get a Uint8Array<ArrayBuffer> type in TS 5.7 and Uint8Array type in <TS 5.7. Much better to rely on this than the above.
```ts
export type Uint8Array_ = ReturnType<Uint8Array["slice"]>;
```
030
David Sherret @dsherret.dev · 29/01/2025
This seems to work in all 5.x versions with all script targets.
```ts
// @ts-ignore typed arrays are not generic in TS < 5.7
type NewUint8Array<T> = Uint8Array<T>;

interface Child {
  __test_property__: string;
}
interface Parent extends Uint8Array, Child {
}

// Uint8Array#reverse() started returning `this` in TS 5.7
type IsNew = ReturnType<Parent["reverse"]> extends Child ? true : false;
type LocalUint8Array<T> = IsNew extends true ? NewUint8Array<T> : Uint8Array;

export type { LocalUint8Array as Uint8Array };
```
120
David Sherret @dsherret.dev · 28/01/2025
TypeScript <5.7 on left, TypeScript 5.7 on right. Glad this seems to work lol. Anyone have a better solution for dealing with Uint8Array being generic now?
buffer property on Uint8Array going to ArrayBufferLike in TS 5.6

```ts
type IsAny<T> = 0 extends (1 & T) ? true : false;
// @ts-ignore uint8array is not generic in TS < 5.7
type NewUint8Array<T> = Uint8Array<T>;

type IsOld = IsAny<NewUint8Array<ArrayBuffer>>;
type Uint8Array2<T> =
  IsOld extends true
  ? Uint8Array
  : NewUint8Array<T>;

function test(): Uint8Array2<ArrayBuffer> {
  return new Uint8Array([]);
}

const buffer = test().buffer;
```buffer property on Uint8Array being ArrayBuffer in TS 5.7
181
David Sherret @dsherret.dev · 24/01/2025
Worked on this slowly over 5 months, but Deno's config discovery and module resolution logic has been extracted out of the CLI and into a re-usable crate (not yet published). Also a few days ago this setup code was hundreds of lines, but now it's only a few.
```rs
use std::rc::Rc;

use deno_resolver::factory::ResolverFactory;
use deno_resolver::factory::WorkspaceFactory;
use sys_traits::impls::RealSys;

let sys = RealSys;
let cwd = sys.env_current_dir()?;
let workspace_factory = Rc::new(WorkspaceFactory::new(sys, cwd, Default::default()));
let resolver_factory = ResolverFactory::new(workspace_factory.clone(), Default::default());
let deno_resolver = resolver_factory.deno_resolver().await?;
```
0130
David Sherret @dsherret.dev · 08/01/2025
Wasmbuild is getting simpler thanks to Wasm imports.
Shows a diff of code that was previously complex, but now just requires a single import.
1143
David Sherret @dsherret.dev · 29/11/2024
It's been a few months so let's bring this up again with an example of what other languages do—unawaited async function call detection is standard in other languages, but missing in TypeScript. So many bugs could be prevented if this were built-in and didn't require separate tooling.
```csharp
async Task Example()
{
    SomeAsyncFunction(); // warns, good
    await Task.FromResult(1);
}

async Task ExampleFixed()
{
    await SomeAsyncFunction();
    await Task.FromResult(1);
}

async Task ExampleSuppressedWarning()
{
    _ = SomeAsyncFunction();
    await Task.FromResult(1);
}

async Task SomeAsyncFunction()
{
    // ...
    await Task.CompletedTask;
}
``````rust
async fn example() {
  some_async_function(); // warns, good
}

async fn example_fixed() {
  some_async_function().await;
}

async fn some_async_function() {
  std::future::ready(1).await;
}
``````ts
export async function example() {
  someAsyncFunction(); // please warn/error here
  await Promise.resolve();
}

export async function exampleSuppressed() {
  void someAsyncFunction(); // proposed suppression
  await Promise.resolve();
}

export async function exampleOkFloatable() {
  okToBeFloatable(); // ok because api was defined as floatable
  await Promise.resolve();
}

export async function someAsyncFunction() {
  // ...
  await Promise.resolve();
}

// This does not exist atm, but I think there should be a way for APIs
// to define themselves as floatable given the prevalence of promises
// in return types not expected to be awaited in the JS ecosystem
export async function okToBeFloatable(): Floatable<Promise<void>> {
  // ..
  await Promise.resolve();
}
```
1223
David Sherret @dsherret.dev · 23/11/2024
Another example of waiting to implement the standard vs an aggressive "it just works" It default exports the WebAssembly module instead of exporting the module instance's exports. They'll probably have to do a breaking change for their users.
Code showing Wasm imports where a default export in Node and Deno imports the default export of the instance of the module, but in Bun it exports the module leading this code to error.
110
David Sherret @dsherret.dev · 21/11/2024
Unfortunately, this regression existed since WebGPU was re-introduced almost a year ago. I've been working on better tooling for tracking the performance of Deno across operating systems, which is how this was caught. It should be much harder for something like this to happen in the future.
190
David Sherret @dsherret.dev · 21/11/2024
Fixed a performance regression in Deno's shutdown time on Windows. We were accidentally doing a lot of WebGPU stuff on every run.
Deno 2.0.6: 89.5ms
Deno 2.1.0: 34.7ms
Bun 1.1.36: 37.2ms
Node 22.11.0: 28.3ms
1131
David Sherret @dsherret.dev · 19/11/2024
A CommonJS module importing a Wasm module now works and is type checked.
A commonjs file importing a math.wasm module where an add function is called with incorrect arguments causing a type checking error.
020
David Sherret @dsherret.dev · 15/11/2024
Wasm modules importing JavaScript/TypeScript also now works. (In the screenshot, the Rust code is the source code for math.wasm)
math.ts imports math.wasm, which has an add and subtract function, which calls into math.ts, which have add and subtract exports.
170
David Sherret @dsherret.dev · 15/11/2024
Demo of Wasm modules in the upcoming Deno release. It has type checking and editor support.
2366
David Sherret @dsherret.dev · 13/11/2024
Respecting "type": "commonjs" in a package.json is going to be stabilized in the next Deno minor release next week. When there's a package.json with this, Deno will determine the module kind of ambiguous file extensions (.js, .ts, .jsx, .tsx) by inspecting the contents of the module.
An index.js file requiring an add.js with a package.json with an explicit "type": "commonjs".An index.js file importing (not requiring) an add.js with a package.json with an explicit "type": "commonjs".
121
David Sherret @dsherret.dev · 25/10/2024
The current Deno 2.0.2 outputs 104 MB after 60s, so this is about a 33% reduction from that. The new savings come from more efficient memory management of the module sources.
Deno 2.0.2 using 104.14 MB after 60s.
110
David Sherret @dsherret.dev · 25/10/2024
The upcoming patch release of Deno uses 2.3x less memory than Bun running the compiled executable of this example on Windows (70 MB vs 160MB after 60 seconds).
Code:

```
import { ts as ts1 } from "./typescript1.js";
import { ts as ts2 } from "./typescript2.js";

for (let i = 1; i <= 3; i++) {
  setTimeout(() => {
    console.log(`${(process.memoryUsage().rss / 1024 ** 2).toFixed(2)} MB`);
    if (i === 3) {
      console.log(getVersions().join(", "));
    }
  }, i * 20_000);
}

function getVersions() {
  const versions = [];
  if (process.versions.deno) {
    versions.push(`Deno ${process.versions.deno}`); // upcoming 2.0.3
  }
  if (process.versions.bun) {
    versions.push(`Bun ${process.versions.bun}`); // 1.1.33
  }
  versions.push(`Typescript ${ts1.version}`); // 5.5.2
  versions.push(`Typescript ${ts2.version}`); // 5.6.2
  return versions;
}
```Shows Deno using 69.75 MB after 60s and Bun using 160.29 MB

```shellsession
V:\scratch
> deno compile --quiet --allow-env main.js
V:\scratch
> ./scratch
71.47 MB
69.74 MB
69.75 MB
Deno 2.0.2+e162306, Typescript 5.5.2, Typescript 5.6.2
V:\scratch
> bun build main.js --outfile scratch_bun --compile                 
 [284ms]  bundle  3 modules
  [39ms] compile  scratch_bun.exe
V:\scratch
> ./scratch_bun
192.89 MB
160.29 MB
160.29 MB
Bun 1.1.33, Typescript 5.5.2, Typescript 5.6.2
```
2131
David Sherret @dsherret.dev · 21/10/2024
Here's the code dprint's CLI uses to programmatically add to the "plugins" array in its configuration file when someone runs `dprint config add <plugin/plugin-url>`. It takes into account indentation, comments, newline kind, and respects trailing commas.
Rust code:

use jsonc_parser::cst::CstRootNode;
use jsonc_parser::cst::CstInputValue;
use jsonc_parser::errors::ParseError;
use jsonc_parser::json;

/// Add a plugin url to the dprint config file's plugins array.
///
/// ```jsonc
/// {
///   "plugins": [
///     "https://plugins.dprint.dev/toml-0.6.3.wasm",
///     "<new url goes here>"
///   ]
/// }
/// ```
pub fn add_to_plugins_array(
  file_text: &str,
  url: &str,
) -> Result<String, ParseError> {
  let root_node = CstRootNode::parse(file_text, &Default::default())?;
  let root_obj = root_node.object_value_or_set();
  let plugins = root_obj.array_value_or_set("plugins");

  plugins.ensure_multiline();
  plugins.append(json!(url));

  Ok(root_node.to_string())
}
100