Skip to content

Instantly share code, notes, and snippets.

View benhatsor's full-sized avatar

Ben Hatsor benhatsor

View GitHub Profile
@benhatsor
benhatsor / customElementTyping.ts
Created July 4, 2026 17:58
Strict Custom Element Type
type ElementAttribute = string | null
type StrictArrayTuple<Arr extends readonly unknown[], ArrTuple = Arr[number]> =
Arr[number][] extends Arr ? // widened array — `as const` forgotten
{ missingAsConstOnArr: never }
: Arr[number] extends ArrTuple ?
// brackets compare the types as single units. unbracketed, a naked (eg. non-indexed-access)
// type param like ArrTuple distributes per union member (TS rules). `never` is
// regarded by TypeScript as an empty union, so with no members to distribute over,
@benhatsor
benhatsor / AbortablePromise.ts
Last active July 9, 2026 07:36
Abortable Promise
/**
* Abortable Promise
* @remarks
* Call {@linkcode abort abort(reason?:)} to abort the signal and resolve the promise.
*/
export class AbortablePromise extends AbortController {
promise: Promise<typeof this.signal.reason>
// Bind method to allow destructuring
@benhatsor
benhatsor / PRs-and-CI-branch-ruleset.jsonc
Last active June 1, 2026 13:56
PRs and CI branch ruleset. To use in your repository, go to Settings > Rules > Rulesets > New ruleset > Import a ruleset > Select this file (without the jsonc comments).
{
"name": "PRs and CI",
"target": "branch",
"source_type": "Repository",
"enforcement": "active",
// Target specified branches
"conditions": {
"ref_name": {
"include": [
// Default branch (usually 'main')
@benhatsor
benhatsor / TransformStreamInterface.ts
Last active June 2, 2026 16:15
A simple wrapper for TransformStream.
/**
* A simple wrapper for `TransformStream`.
* @example
* class UpperCaseStream extends TransformStreamInterface<string, string> {
* transform(chunk: string) {
* return chunk.toUpperCase()
* }
* }
*
* // Usage:
@benhatsor
benhatsor / html-include-plugin-vite.js
Last active May 24, 2026 11:59
HTML Include Plugin for Vite
/*
* Tiny HTML include plugin for Vite.
* Usage: `<include src="./partials/header.html"></include>`
*/
import fs from 'fs'
import path from 'path'
function processIncludes(html, dir) {
return html.replaceAll(/<include src="(.+?)"><\/include>/g, (_match, src) => {
@benhatsor
benhatsor / default.tsconfig.json
Last active July 14, 2026 08:29
Useful TypeScript defaults.
{
// Note: This assumes TS6+.
"compilerOptions": {
// Treat all files as ES modules.
"moduleDetection": "force",
// For Node.js:
// "types": ["node"],
// For native TS execution in Node.js without a bundler:
// "module": "nodenext",
@benhatsor
benhatsor / imports.md
Last active May 8, 2026 17:34
Proposal: Granular import with module namespace

Proposal: Granular import with module namespace

Barrel import with module namespace:
Problem: loses granular intent as well as potential tree-shaking benefits.

import * as util from './util'

Granular import without module namespace:
Problem: loses scope, conflicts with other local or imported module functions.

@benhatsor
benhatsor / Task.ts
Last active July 9, 2026 08:00
Task (a-la Swift)
/**
* Task (a-la [Swift](https://developer.apple.com/documentation/swift/task)).
*
* @example
* const task = new Task(async signal => {
* await Task.sleep({ for: 1000, signal })
* return 'hello'
* })
* task.abort()
@benhatsor
benhatsor / simple-mc-decompiler.js
Last active April 4, 2026 20:50
Simple Minecraft Decompiler.
/**
* Simple Minecraft Decompiler
*
* Usage:
* - Decompile latest version: `node simple-mc-decompiler.js`
* - To specify version: `node simple-mc-decompiler.js [version]`
* - version = `latest` or `latest-release` / `latest-snapshot` / [version name]
* - Add `-s` or `--server` to decompile server
*/
@benhatsor
benhatsor / css-standard-native.css
Last active May 24, 2026 00:12
CSS Standard Styles - Native App
body {
margin: 0;
}
html {
/*
* Disable pinch-to-zoom, double-tap to zoom and panning the body (scrolling gestures).
* To enable scrolling: `touch-action: pan-x pan-y`.
*/