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

@memoized-dom/compiler

v0.0.8

Published

Binding-aware TypeScript and JSX compiler for memoized-dom

Readme

Compiler

Requires Node.js 24.11 or newer. Standard JavaScript and TypeScript modules are parsed by Yuku, while all compiler analysis and emission operates on ESTree.

Public compilation APIs:

  • compile(source, options) returns JavaScript and keeps the allocation-light legacy path.
  • compileDetailed(source, options) returns { code, map, css? } with authored source in sourcesContent.
  • compileModulesDetailed(modules, options) returns linked output, one map per module in maps, optional extracted css, component metadata, and root metadata derived from an ordinary entry module's top-level mount(target, Component) call.

The top-level files are orchestration and whole-program passes:

| Path | Responsibility | |---|---| | src/analysis.ts | Stable analysis facade and ordered pass orchestration | | src/analysis/prepare.ts | Shared normalization and analysis preparation for manifests and final emission | | src/context.ts | Stable facade for shared context types and AST utilities | | src/effects.ts | Stable facade for effect discovery and emission | | src/emit.ts | Host JSX DOM emission and structural-region dispatch | | src/handlers.ts | Handler resolution and callback instrumentation | | src/linker.ts | Connected module-graph compilation | | src/module-control-flow.ts | Pure module if/switch computed classification | | src/plugin.ts | Parser-neutral pass ordering and final module rewrite | | src/router.ts | Linked JSX route graph, directive validation, and navigation lowering |

Domain folders keep related implementation details discoverable:

| Folder | Responsibility | |---|---| | src/analysis/ | Module discovery, component validation, read attribution, computeds, instance preludes, and access tables | | src/components/ | Prop contracts, content/ref slots, and linker manifests | | src/context/ | Compiler data model/context construction and raw AST helpers | | src/effects/ | Effect ownership, dependency discovery, invalidation, registration, and rewriting | | src/emission/ | Component factories and generated list/conditional/route regions | | src/handlers/ | Mutation traversal and commit-routing analysis | | src/jsx/ | Ordered attributes, child classification, refs, and namespaces | | src/linking/ | Linker graph contracts, module resolution, and cross-module import metadata | | src/features/data-sources/ | Transparent-source discovery, analysis, policy, subscriptions, and read lowering |

The top-level analysis, context, emitter, and handler modules intentionally remain stable facades. Cross-domain callers use those facades; implementation modules within a domain import their siblings directly.

Architecture and evolution rules

Model compiler work as an explicit pipeline of domain passes. A pass receives the compiler context and AST, performs one named responsibility, and leaves the context in a documented state for the next pass. Top-level modules coordinate passes and provide stable compatibility exports; they must not grow a second implementation of domain behavior.

Use a hybrid functional model:

  • Keep AST analysis and rewriting as focused functions with explicit inputs.
  • Use interfaces and named types for stable data exchanged between phases.
  • Use a small class only when an object owns real mutable state and a lifecycle.
  • Do not introduce inheritance merely to group helper functions.
  • Keep policy decisions separate from AST construction when they can vary independently.

Every rule has one owner. Before adding a helper or pass, search the compiler for the same operation, AST shape, diagnostic, or runtime-helper construction. Extend or move the existing implementation when it already owns that rule. Do not copy it into a new domain, facade, frontend, or linker path.

The following are required for current and future development:

  • Facades re-export or orchestrate; they do not duplicate implementations.
  • Domain modules may import sibling internals, while external callers use the domain index or an established top-level facade.
  • Shared contracts get one named type instead of repeated inline object shapes.
  • Generic AST access and construction belong in src/ast/; do not create local variants of identifier, literal, field-access, or cloning helpers.
  • A new specialized helper stays in its owning domain until a second genuine consumer proves a broader abstraction is needed.
  • When moving code, move its tests and documentation responsibility with it; do not leave a compatibility copy behind.
  • Any architectural change must update this README in the same milestone.

Code review should reject duplicated rules even when both copies currently produce the same output. Parallel implementations drift in diagnostics, source-location behavior, and runtime semantics.

Shared compiler contracts have canonical owners:

| Contract | Owner | |---|---| | CompilerPath, ProgramPath, ComponentPath, and HelperPath | src/context/model.ts | | Binding resolution and declaration lookup, including variableDeclaratorFor | src/context/ast.ts |

Import these contracts through the stable src/context.ts facade. Do not derive local path aliases from Ctx or repeat parent-walking binding helpers.

Module-list content targeting preserves static item indices through the access table into each reader's pending dirty reasons. Direct list readers can replay only those rows; an ordinary/full reason or a different source forces ordinary reconciliation. Reasons are not a shared consumable mutation set, and generic object-prop equality is unchanged.

The proof currently requires a non-exported, unreassigned module array literal of flat scalar records, non-escaping item references, and immutable scalar-field or identity keys. Structural operations, dynamic indices, nested/aliased objects, unknown consumers, async handlers, and execution-aware effect writes retain conservative routing. Authored assignments are neither wrapped nor evaluated again. After initial reconciliation, compiler-proven content updates pass the fixed-position guarantee to refreshIndices, avoiding a collection-wide identity scan: list refresh reads only the targeted positions. Initial mounting, hydration, and length mismatches still reconcile. Calls retain content-safe reconciliation: a method property name cannot prove that retained rows were not mutated. This bounds a targeted list refresh by the number of targets without turning method recognition into a language or purity whitelist.

List-method optimization candidates

src/lists/mutation-shapes.ts centralizes method names considered for list optimization: map, push, pop, unshift, shift, splice, reverse, sort, fill, and copyWithin. This is not a whitelist of supported JavaScript methods. An absent name does not produce an unsupported-method error; it retains existing conservative receiver-effect routing. Existing independent language restrictions, such as writes to read-only state, still apply.

The list analysis imports this table for compiler-owned JSX map and for recognizing push candidates while proving that targeted writes remain valid after fresh flat records are appended. A recognized call is still routed as a content-capable receiver effect. JavaScript permits an own or inherited method to replace Array.prototype.push, so spelling alone cannot authorize skipping retained-prefix validation or row replay.

Structural labels remain candidates, not implemented specializations. Adding a name alone cannot authorize an optimization. Receiver provenance, overrides, callbacks, argument effects, and mixed writes must be proven safe. Names are neither purity guarantees nor permission to skip invalidation. Unknown calls can mutate retained content; the table introduces no new errors and does not change the existing fallback implementation.

Analysis pipeline

src/analysis.ts is the shallow coordinator for the first compiler pass. It defines ordering because several analyses consume metadata or normalized AST produced by earlier stages; it does not own their implementations.

| Module | Responsibility | |---|---| | module-scan.ts | Linked-import validation and discovery of module state, helpers, and components | | render-props.ts | JSX-carrying component prop discovery, including scalar type exclusions | | component-validation.ts | Component composition edges and structural JSX diagnostics | | read-collection.ts | Module-state reads, list/conditional ownership, and helper-read attribution | | module-list-targets.ts | Closed module-array proof for static-index content invalidation | | computed.ts | Module computed-state discovery and dependency analysis | | instance.ts | Component-local state and ordered derivation discovery | | instance-control-flow.ts | Replay planning for component-local control flow | | component-reads.ts | Read propagation across render-callback subtrees | | component-graph.ts | Component path expansion and lightweight-list classification | | access-table.ts | Final route table from writes to affected component entities |

Keep dependencies directed from focused discovery/validation modules into the coordinator. A domain module may record facts on Ctx, but pass ordering stays in runAnalysis; do not create a second partial pipeline in a caller.

Both linker manifest analysis and final emission call prepareProgramAnalysis before consuming analysis facts. Conditional directives and external reactive imports must therefore be normalized identically in metadata and generated code. Cached module inputs retain comments alongside the AST, source, and extracted CSS.

JavaScript semantics and regression coverage

Successful compilation emits JavaScript, including when TypeScript assertions are nested. Ambient declarations are erased. Runtime namespaces, enums, import assignments, export assignments, and constructor parameter properties require lowering that is not currently implemented and receive explicit diagnostics. The same applies to runtime decorators and TypeScript auto-accessor properties.

Instrumented callbacks retain a straight-line write; notify hot path. Return expressions are evaluated into a hygienic temporary before notification. More complex early returns lower through a compiler-generated completion label so authored finally blocks finish before notification without wrapping callbacks in exception-handling control flow. As elsewhere in the R5 contract, an exceptional exit does not notify. Direct effect writes retain per-site execution flags. Equality checks may read only plain bindings or proven, non-escaping object literals' own data properties; unknown member assignments preserve setter/proxy behavior and conservatively invalidate the root because setters may mutate other state.

tests/compiler-semantic-regressions.test.ts executes emitted JavaScript without a TypeScript loader and checks completion order, lexical scopes, accessors, proxies, comments, and metadata parity. Extend semantic tests alongside codegen snapshots when adding a new lowering or optimization.

Handler analysis model

src/handlers.ts owns handler discovery and is the stable entry point used by components, effects, and callbacks. Mutation analysis is split by concern:

| Module | Responsibility | |---|---| | handlers/analyze.ts | Callback cloning, origin setup, AST visitor dispatch, and finalization orchestration | | handlers/write-routing.ts | Stateful per-handler origin classification and scoped write-routing decisions | | handlers/traversal.ts | Scope-aware handler paths and visitor dispatch | | handlers/mutation-targets.ts | Keyed-list mutation keys and cross-list visibility decisions | | handlers/execution-sites.ts | Execution-aware write guards and final commit insertion | | handlers/local-calls.ts | Reachable local-helper call constraints |

HandlerPath is intentionally a class: each path owns its node, parent chain, scope lookup, and mutable traversal skip state for one walk. This is the kind of lifecycle-bearing object allowed by the hybrid model. Write policy and AST rewrites remain functions; do not move them onto the class or create parallel path wrappers in handler consumers.

Effect pipeline

The compiler intrinsic effect() crosses analysis and emission, but each rule still has one owner:

| Module | Responsibility | |---|---| | effects/discovery.ts | Callback resolution, ownership, dependency reads, and effect-site metadata | | effects/emission.ts | Local invalidation conditions, runtime registration, module rewriting, and final ownership rejection | | effects.ts | Compatibility exports only |

Discovery produces EffectSite and ModuleEffectSite records on Ctx; emission consumes those records. Keep that dependency one-way. Callback resolution and intrinsic recognition must not be reimplemented in emitters or handler analysis.

List emission

List emission separates region/row construction from update scheduling:

| Module | Responsibility | |---|---| | emission/authored-slots.ts | Authored children/render-value slot construction through injected node and region emitters | | emission/component-call.ts | Nested component props, render slots/callbacks, child identity, and prop replay | | emission/host-element.ts | Intrinsic element creation, attributes/events/refs, direct children, and dynamic DOM writes | | emission/text-node.ts | Text normalization, DOM creation, guarded writes, and transparent-source registration | | emission/list-region.ts | Region setup and selection of callback, component, or inline row strategy | | emission/list-component-row.ts | Component-row factory ABI, prop projection, and lightweight-row updates | | emission/list-inline-row.ts | Inline-row scopes, bindings, registration, and update closure construction | | emission/list-update.ts | Full reconcile versus targeted keyed refresh decisions and emitted update statements | | lists/map-site.ts | Map source/callback validation and deterministic list-site identity | | lists/row-derivations.ts | Row-local derivation discovery, shadowing checks, and AST substitution | | lists/targeted-refresh.ts | Analysis-time dependency discovery consumed by list emission |

list-update.ts is the sole owner of reason matching and keyed refresh selection. Row factories supply metadata to it; they must not construct a second version of targeted-versus-structural update policy.

Module linking

| Module | Responsibility | |---|---| | linking/model.ts | Canonical public options plus internal manifest, export, import, usage, and module-entry contracts | | linking/options.ts | Translation from linker options into per-module compiler options | | linking/discovery.ts | AST manifest discovery, component usage analysis, and linked re-analysis | | linking/resolution.ts | Module ID normalization/resolution and conversion of manifest exports into linked imports | | linker.ts | Fixed-point orchestration, application-root validation, and final connected compilation |

Keep host resolution and alias behavior in linking/resolution.ts. Discovery and compilation consume its resolved entries; they must not grow separate extension probing or alias matching rules.

Transparent data-source model

The transparent data-source feature is split by compiler phase:

| Module | Responsibility | |---|---| | discovery.ts | Imported API recognition and authored source discovery | | module-sources.ts | Module-scope source registration and declaration lowering | | component-sources.ts | Component-local sources, event assignments, and validation | | policy-arguments.ts | Runtime policy arguments and source mount construction | | subscriptions.ts | Dependency metadata and emitted invalidation subscriptions | | read-analysis.ts | Binding identity, site classification, and dependency analysis | | read-transforms.ts | Reusable AST mutations for resolved-value reads and effects | | module-read-lowering.ts | Materializing reads for imported module source references | | automatic-sites.ts | Automatic pending/error render-policy sites and renderer metadata | | read-rewriting.ts | Ordered orchestration of component read lowering | | group-analysis.ts | Shared source origins and component-prop analysis for Group and TSRX | | group-policy-components.ts | Captured-variable analysis and generated presentation components | | suspend-directive.ts | Shared validation and consumption of the compiler-owned suspend directive | | tsrx-boundaries.ts | TSRX try/pending/catch metadata and boundary lowering | | group-lowering.ts | JSX Group validation, render-policy lowering, and pass orchestration | | index.ts | Stable public surface for the feature |

The dependency direction is discovery/analysis → transforms → orchestration. Emission metadata and policy construction are shared domain services. Avoid imports from a lower-level module back into the coordinator or top-level facade.

Experimental TSRX frontend

Files ending in .tsrx are parsed by the official @tsrx/core parser and lowered directly to the ordinary TS-ESTree/JSX consumed by the compiler. The .js, .jsx, .ts, .tsx, and .d.ts extensions explicitly select Yuku; the TSRX path is isolated from the standard parser. Unknown extensions must provide a frontend explicitly instead of being interpreted through a fallback parser.

The experimental lowering currently supports statement-container function bodies, @if, @for (... of ...) with index, key, and @empty, root @switch, finite dynamic intrinsic/component choices, and scoped style extraction. The implementation is isolated under src/ast/tsrx so extension nodes never enter the compiler's analysis or emission passes. See the repository tsrx.md for conceptual JSX equivalents and the exact host-profile matrix.

Lazy destructuring, @try/@pending/@catch, nested statement containers, branch-local setup, style expressions, and dynamic selectors without provable finite candidates currently produce explicit diagnostics. They need Memoized DOM-specific reactivity or runtime semantics before they can be lowered safely.

The public createExtensionEstreeFrontend() utility creates a strict extension map for specialized frontends. Virtual or extensionless modules must select a frontend explicitly; TSRX callers can use experimentalTsrxEstreeFrontend.

Compiler-owned routing

route and route-to are compiler properties, similar to key: they are available on JSX elements but are not ordinary runtime props. Route declarations must be static slash-prefixed fragments. The compiler composes each declaration with its nearest route-bearing JSX ancestor, validates the connected module graph, emits one manifest, and lowers each declaration to an anchored route-selected DOM region.

Because the compiler sees the whole linked graph, it rejects undeclared destinations, non-terminal catch-alls, ambiguous patterns, and missing, extra, or duplicate route parameters. These same diagnostics are exposed through diagnoseModules, Vite, and @memoized-dom/language-service. The structured diagnostic includes an authored start and end location when the frontend supplies a node range, allowing editor integrations to highlight the offending expression or JSX attribute precisely.

Projects that do not already include the compiler's global JSX declarations can load the published directive types with "types": ["@memoized-dom/compiler/jsx"] in tsconfig.json. TypeScript then checks the slash-prefixed surface shape; the compiler/language service performs the stronger application-graph checks.

Compiler-owned conditional branches

if, else-if, and else are compiler properties for readable sibling branches. Formatting whitespace and JSX comments do not break a chain, more than one else-if is allowed, and else is optional:

<p if={status === 'pending'}>Loading...</p>
<p else-if={status === 'error'}>Could not load.</p>
<StoryList else />

The compiler removes the properties and lowers the chain to one stable conditional region. An else-if or else without a preceding branch is a compile error. The conditions stay reactive; switching branches updates only the owned region rather than rerunning the component factory.