npm Package Compatibility

April 22, 2026 · View on GitHub

Tracks SharpTS compatibility with real-world npm packages. Tests live in SharpTS.Tests/IntegrationTests/RealPackageSmokeTests.cs.

Run: dotnet test --filter "Category=npm"

Results

All 16 smoke tests pass. Tests skip gracefully when npm is not on PATH.

PackageVersionInterpreterCompiledNotes
ms2.1.3PassPass
uuid (CJS)9.0.1PassPassv4 only; deeper v3/v5 APIs blocked by #36
uuid (ESM)9.0.1PassPassNamed imports v4, validate, NIL
debug4.3.4PassPassLoads + instantiates; wire-level output untested
semver7.6.0PassPass
minimatch9.0.4PassPassBasic glob matching; AST class blocked by #37
yaml2.4.1PassPass
lodash4.17.21PassPass**compiled mode asserts only typeof _ — see below

Open gaps surfaced by this work

Tests pass with the API surface exercised above, but the smoke-test investigation surfaced several gaps that would block heavier real-world use. Each has a tracking issue:

  • #36 — Parser: namespace not treated as contextual keyword in assignments. Blocks uuid's v3/v5 hash-function modules.
  • #37 — Parser: uninitialized class field declarations (class Foo { name; }). Blocks minimatch's AST class.
  • lodash compiled behaviorLodash_Compiled currently asserts only typeof _ === "function" because _.chunk(...) and _.flatten(...) return wrong values under compiled mode. Root cause is a separate behavioral bug (not #40/#59, both now closed); to be filed as a follow-up issue. When fixed, the test's behavioral assertions can be strengthened.

Fixes landed during this work

Gaps that were closed:

  • #22 — Compiled mode: timer callbacks can dispatch $PromiseResolveCallback / $PromiseRejectCallback
  • #23 — Parser: automatic semicolon insertion (ASI)
  • #24 — Runtime: Error class available as global variable (unblocks class X extends Error)
  • #25 — Type checker: forward var references in lazy contexts
  • #26 — Built-in module: tty
  • #27 — Compiled mode: String(), Number(), Boolean() callable in emitted IL
  • #28, #29, #30 — Lexer: satisfies as contextual keyword, $ as identifier character, scientific notation
  • #31 — Type checker: index access with any key on object types
  • #32 — Type checker: untyped CJS modules
  • #33 — Module resolver: prefer CJS over ESM for require() calls
  • #34 — Parser: class member syntax patterns
  • #35 — Parser: optional chaining on calls and bracket access
  • #40 — Compiled mode: two-pass inner-function hoist so cross-hoist forward references resolve instead of loading null.
  • #55 — Interpreter: ESM named imports from a CJS module now read accessor-defined exports (Babel's Object.defineProperty(exports, "v4", { get: ... })), unblocking the uuid ESM path.
  • #59 — Compiled mode: ClosureAnalyzer now visits the callee of new expressions, so cross-hoist captures work through constructors.

Additional runtime fixes: contextual keywords as identifiers, lenient parameter binding (missing args default to undefined), package.json exports condition ordering, lenient property access on built-in types, spec-compliant this binding in static methods and accessors, routing hoisted function decls through ArrowScopeDC.