npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@scratch-code/diff

v0.1.0

Published

Deterministic semantic diffs for Scratch AST scripts

Downloads

30

Readme

@scratch-code/diff

Deterministic semantic diffs for @scratch-code/ast Script collections.

The package compares AST meaning rather than SB3 encoding or scratchblocks presentation. It has no block-registry or codec dependency, so open extension opcodes are supported without a catalog.

Installation

npm install @scratch-code/diff

This package is ESM-only and requires Node.js 22 or newer.

import { diffScripts } from '@scratch-code/diff';

const result = diffScripts(beforeScripts, afterScripts);

for (const change of result.changes) {
  console.log(change.type);
}

Result model

diffScripts() returns a versioned, JSON-safe result with three collections:

  • pairs maps matched before/after entities and records whether they matched by unique Scratch ID, ordered equality, conservative similarity, or a named AST key.
  • changes contains the renderer-independent add, remove, and modify operations.
  • relations currently contains optional move annotations.

Locations use structured paths rooted at scripts, matching AST validation paths. A location may include metadata.scratch.id as an identity hint, but the path is the location for this diff. Diff-local pair and change IDs are deterministic and never become Scratch IDs.

The result references the caller's before/after ASTs by path instead of copying whole subtrees. Keep those inputs with the result when a consumer needs to render source content.

Semantic boundary

The following state is compared:

  • Script and stack order.
  • Block opcode and shadow role.
  • Named inputs, input types and literal values, nested blocks and substacks, and obscured shadows.
  • Field types, JSON values, and variable/list/broadcast IDs.
  • Modeled semantic mutations.

All AST metadata is excluded from semantic equality. A unique, non-empty metadata.scratch.id may help match Blocks, but changing or removing that ID alone is not a semantic change. Script coordinates, numeric shadow kinds, and codec namespaces are likewise ignored.

Both inputs are structurally validated without a registry. Invalid ASTs throw InvalidDiffInputError, whose inputs property identifies the before or after side and retains the AST diagnostics. The diff never repairs nodes, materializes defaults, or generates IDs.

Matching

The default matching pipeline is ID-first, then ordered, then similarity:

diffScripts(before, after, {
  matching: [{ kind: 'scratch-id' }, { kind: 'ordered' }, { kind: 'similarity' }],
});

The array order is significant, and strategies may be omitted. Ordered matching uses canonical semantic fingerprints with sorted record keys. Similarity is deliberately conservative: Blocks require the same opcode and structural shape, and ambiguous duplicate candidates remain additions and removals.

Moves

A move is an extra relation, never a fourth base change status. A moved Script or Block still has a linked removal and addition in changes. Consumers that ignore relations therefore retain a complete basic diff, while richer consumers may collapse the pair into a move presentation.

The first version does not project changes into metadata.diff. The canonical result remains independent of AST metadata; a future projection helper can derive annotations immutably without becoming another source of truth.