esm.sh icon indicating copy to clipboard operation
esm.sh copied to clipboard

TypeBox: Modules incorrectly caching by path

Open sinclairzx81 opened this issue 2 years ago • 1 comments

Failing module

There appears to be a module "cache by path" issue on esm.sh which is causing runtime functionality to fail in TypeBox. The functionality relates specifically to registering custom types in TypeBox (where a shared stateful registry (TypeRegistry) is defined in one module) which is then shared across multiple other modules.

The expectation is that absolute path "https://esm.sh/@sinclair/typebox" (which contains the TypeRegistry) is cached once and reused, however multiple instances of this module seem to be created when importing sub module paths.

  • GitHub: https://github.com/sinclairzx81/typebox
  • npm: https://www.npmjs.com/package/@sinclair/typebox

The following is repro of the issue.

// using deno npm specifier works ok
import { Kind, TypeRegistry } from "npm:@sinclair/typebox"
import { Value } from "npm:@sinclair/typebox/value"

// esm.sh import seems to generate two instances of TypeRegistry
// import { Kind, TypeRegistry } from "https://esm.sh/@sinclair/typebox"
// import { Value } from "https://esm.sh/@sinclair/typebox/value"

// This line registers the `Foo` type on the TypeRegistry (defined in https://esm.sh/@sinclair/typebox)
TypeRegistry.Set('Foo', (_, value) => value === 'foo')

// This line will internally check the TypeRegistry (via relative path to https://esm.sh/@sinclair/typebox)
const R = Value.Check({ [Kind]: 'Foo' }, 'foo')

console.log(R)

Error message

After running deno run I got this:

# This error message is generated by TypeBox as it cannot find the registered type as
# the Value import is using it's own instance of the TypeRegistry. This only occurs when
# using esm.sh.
error: Uncaught Error: Unknown type

Additional info

I think the issue here is that the path to ./typebox.ts (which is referenced by the Value modules) are being loaded in as distinct modules. Because they are distinct, two versions of the TypeRegistry are being created, one for user level import, and another which is internally imported via the Value (as well as TypeCompiler) modules, but not sure.

  • esm.sh version: n/a (loading via import)
  • Deno version: deno 1.37.1

Happy to provide more detail if needed

sinclairzx81 avatar Oct 12 '23 06:10 sinclairzx81

Related https://github.com/esm-dev/esm.sh/issues/16

sinclairzx81 avatar Oct 12 '23 06:10 sinclairzx81

@ije Any update on this? I just ran into this issue, esm.sh appears to instance the same module multiple times.

1951FDG avatar Apr 22 '24 17:04 1951FDG

i am looking into it...seems typebox and typebox/value do not share the typebox/type module

ije avatar Apr 23 '24 22:04 ije

just found the no-bundle mode works:

import { Kind, type TSchema, TypeRegistry } from "https://esm.sh/@sinclair/[email protected]?no-bundle";
import { Value } from "https://esm.sh/@sinclair/[email protected]/value?no-bundle";

const Foo = { [Kind]: "Foo" } as TSchema;
TypeRegistry.Set("Foo", (_, value) => value === "foo");
assertEquals(Value.Check(Foo, "foo"), true);
assertEquals(Value.Check(Foo, "bar"), false);

ije avatar Apr 25 '24 12:04 ije

@sinclairzx81 can you please add a esm.sh specific field in the pakcage.json?

{
  "name": "@sinclair/typebox",
  "esm.sh": {
    "bundle": false // disables the default bundling behavior
  }
}

check https://github.com/esm-dev/esm.sh#bundling-strategy

ije avatar Apr 25 '24 13:04 ije

@ije Hi, thanks for the follow up on this!

can you please add a esm.sh specific field in the pakcage.json?

Thanks, I'll give this some consideration, but may recommend the ?no-bundle option in the short term as I think I prefer the explicit opt-out on the import path vs package.json configurations, but will give it some thought.

Happy to close off this issue! Cheers S

sinclairzx81 avatar Apr 25 '24 23:04 sinclairzx81

@sinclairzx81 thanks!

ije avatar Apr 26 '24 03:04 ije