bogeychan
bogeychan8mo ago

Help with npm peerDependencies

Hi 👋 I am using an npm library that has @sinclair/typebox listed under peerDependencies. It gives the following runtime error whenever I import/use the library:
Uncaught Error: Cannot find module '@sinclair/typebox/value'
Uncaught Error: Cannot find module '@sinclair/typebox/value'
I tried to add this peer to my imports in deno.jsonc, but the error persists. I've read some comments claiming that peer dependencies work. am I missing something?
deno 1.38.3 (release, x86_64-unknown-linux-gnu)
v8 12.0.267.1
typescript 5.2.2
deno 1.38.3 (release, x86_64-unknown-linux-gnu)
v8 12.0.267.1
typescript 5.2.2
10 Replies
Deno AI Helper
Deno AI Helper8mo ago
Based on the documentation, Deno has added support for npm packages, including those with peer dependencies. However, the error you're encountering suggests that the module '@sinclair/typebox/value' cannot be found. Here are a few things you could try: 1. Check the version of the peer dependency: Ensure that the version of '@sinclair/typebox' listed under peerDependencies in the npm library's package.json file is compatible with the version you're using. 2. Check the import specifier: Make sure you're using the correct import specifier for '@sinclair/typebox'. In Deno, you should use the npm specifier format, which looks like this: npm:@sinclair/typebox@version. 3. Check your deno.jsonc file: Ensure that you've correctly added '@sinclair/typebox' to the imports field in your deno.jsonc file. The entry should look something like this:
{
"imports": {
"@sinclair/typebox/": "npm:@sinclair/typebox@version/"
}
}
{
"imports": {
"@sinclair/typebox/": "npm:@sinclair/typebox@version/"
}
}
4. Check for type errors: If the npm library doesn't distribute types, you might need to specify the corresponding @types package using a @deno-types directive above the import. However, this is usually not necessary for runtime errors. If you've checked all of these and the issue persists, it might be a limitation or bug in Deno's npm package support. In that case, I would recommend reporting the issue to the Deno team for further assistance.
Danielduel
Danielduel7mo ago
Hi @bogeychan, could you check if issue persists if you add import "npm:@sinclair/typebox@versionfrompeer" At the very start of your app? Imports field in deno.json is more like a mapping for aliases for things that will be imported (import map), they aren't importing anything alone
marvinh.
marvinh.7mo ago
@bogeychan Can you share which dependency uses @sinclair/typebox as a peerDependency?
bogeychan
bogeychan7mo ago
Hi all 👋 , @Danielduel, that makes it kinda worse... @marvinh., the dependency is npm:elysia@0.7.29 --- you can reproduce it like:
// main.ts
import 'npm:@bogeychan/elysia-polyfills/deno/index.js';

import 'npm:@sinclair/typebox@0.31.17';

import { cors } from 'npm:@elysiajs/cors@0.7.2';
import { html } from 'npm:@elysiajs/html@0.7.3';
import { bearer } from 'npm:@elysiajs/bearer@0.7.0';
import { swagger } from 'npm:@elysiajs/swagger@0.7.4';

import { Elysia } from 'npm:elysia@0.7.29';

new Elysia()
.use(cors())
.use(html())
.use(bearer())
.use(swagger())
.get('/', () => ({ hello: 'Deno👋' }))
.listen(8080);
// main.ts
import 'npm:@bogeychan/elysia-polyfills/deno/index.js';

import 'npm:@sinclair/typebox@0.31.17';

import { cors } from 'npm:@elysiajs/cors@0.7.2';
import { html } from 'npm:@elysiajs/html@0.7.3';
import { bearer } from 'npm:@elysiajs/bearer@0.7.0';
import { swagger } from 'npm:@elysiajs/swagger@0.7.4';

import { Elysia } from 'npm:elysia@0.7.29';

new Elysia()
.use(cors())
.use(html())
.use(bearer())
.use(swagger())
.get('/', () => ({ hello: 'Deno👋' }))
.listen(8080);
running the following commands works 50% of the time (checkout the screenshots): no change to code
rm -fr ~/.cache/deno/ deno.lock node_modules/
deno run --node-modules-dir --allow-net main.ts
rm -fr ~/.cache/deno/ deno.lock node_modules/
deno run --node-modules-dir --allow-net main.ts
doesn't work with or without --node-modules-dir idk why but sometimes deno decides to download the elysia dependency multiple times (with _1) suffix (checkout the screenshots). therefore some dependencies use/reference elysia_01 as it's peer). that's bad because both elysia's reference the same @sinclair/typebox package and it throws the error Duplicate type kind 'Files' detected because its executed twice, ... https://github.com/elysiajs/elysia/blob/757357f28749674844fc848e4ddd715134f7714f/src/type-system.ts#L144 the dependency tree looks something like this: - main.ts - elysia - @sinclair/typebox (peer) - @elysiajs/cors - elysia (peer) - @elysiajs/html - elysia (peer) - @elysiajs/bearer - elysia (peer) - @elysiajs/swagger - elysia (peer)
GitHub
elysia/src/type-system.ts at 757357f28749674844fc848e4ddd715134f771...
Ergonomic Framework for Humans. Contribute to elysiajs/elysia development by creating an account on GitHub.
No description
No description
marvinh.
marvinh.7mo ago
That sounds like a bug. Filed an issue here https://github.com/denoland/deno/issues/21394
bogeychan
bogeychan7mo ago
ooh, that makes sense, it works! thanks everyone <:hooray_deno:1035517542200004688> <:chun_happy:1076036980334415882>
marvinh.
marvinh.7mo ago
We should file an issue on their tracker to make them aware of the missing depedency declaration
dsherret
dsherret7mo ago
i'm not sure about that in this case. I feel like Deno should just be able to figure it out
bogeychan
bogeychan7mo ago
this dependency resolution should work if deno wanna be fully compatible with node.js. in case you wanna try it out for yourself:
// main.js
import '@bogeychan/elysia-polyfills/node/index.js';

import { cors } from '@elysiajs/cors';
import { html } from '@elysiajs/html';
import { bearer } from '@elysiajs/bearer';
import { swagger } from '@elysiajs/swagger';

import { Elysia } from 'elysia';

const app = new Elysia()
.use(cors())
.use(html())
.use(bearer())
.use(swagger())
.get('/', () => ({ hello: 'Node.js👋' }))
.listen(8080);

console.log('Listening on http://localhost:8080');
// main.js
import '@bogeychan/elysia-polyfills/node/index.js';

import { cors } from '@elysiajs/cors';
import { html } from '@elysiajs/html';
import { bearer } from '@elysiajs/bearer';
import { swagger } from '@elysiajs/swagger';

import { Elysia } from 'elysia';

const app = new Elysia()
.use(cors())
.use(html())
.use(bearer())
.use(swagger())
.get('/', () => ({ hello: 'Node.js👋' }))
.listen(8080);

console.log('Listening on http://localhost:8080');
// package.json
{
"private": true,
"type": "module",
"main": "main.js",
"dependencies": {
"@bogeychan/elysia-polyfills": "0.6.1",
"@elysiajs/cors": "0.7.2",
"@elysiajs/html": "0.7.3",
"@elysiajs/bearer": "0.7.0",
"@elysiajs/swagger": "0.7.4",
"elysia": "0.7.29"
}
}
// package.json
{
"private": true,
"type": "module",
"main": "main.js",
"dependencies": {
"@bogeychan/elysia-polyfills": "0.6.1",
"@elysiajs/cors": "0.7.2",
"@elysiajs/html": "0.7.3",
"@elysiajs/bearer": "0.7.0",
"@elysiajs/swagger": "0.7.4",
"elysia": "0.7.29"
}
}
npm i && node . # v18.16.0
npm i && node . # v18.16.0
I'm fine with the workaround, since the polyfills are experimental anyway