Deno's module resolution and package management currently face a challenge in handling duplicate modules. To illustrate this issue, let's consider an example.
Suppose we are building a single-page application (SPA) with Deno and React, and we want to use the "react-spectrum" component library for React. We would begin by adding these dependencies to our deps.ts file:
export * as React from "https://esm.sh/react@17.0.1";
export * as ReactDOM from "https://esm.sh/react-dom@17.0.1";
export * as ReactSpectrum from "https://esm.sh/react-spectrum@1.2.3";
Running deno info produces the following output:
file:///Users/lucacasonato/projects/github.com/denoland/corp/test.ts (185B)
├─┬ https://esm.sh/react-dom@17.0.1 (192B)
│ ├─┬ https://cdn.esm.sh/v64/@types/react-dom@17.0.11/index.d.ts (4.02KB)
│ │ └─┬ https://cdn.esm.sh/v64/@types/react@17.0.38/index.d.ts (148.67KB)
│ │ ├── https://cdn.esm.sh/v64/@types/react@17.0.38/global.d.ts (7.01KB)
│ │ ├── https://cdn.esm.sh/v64/@types/prop-types@15.7.4/index.d.ts (3.58KB)
│ │ ├── https://cdn.esm.sh/v64/@types/scheduler@0.16.2/tracing.d.ts (4.03KB)
│ │ └── https://cdn.esm.sh/v64/csstype@3.0.10/index.d.ts (844.54KB)
│ ├─┬ https://cdn.esm.sh/v64/react-dom@17.0.1/deno/react-dom.js (118.43KB)
│ │ ├── https://cdn.esm.sh/v64/object-assign@4.1.1/deno/object-assign.js (1.7KB)
│ │ ├─┬ https://cdn.esm.sh/v64/react@17.0.1/deno/react.js (8.13KB)
│ │ │ └── https://cdn.esm.sh/v64/object-assign@4.1.1/deno/object-assign.js *
│ │ └── https://cdn.esm.sh/v64/scheduler@0.20.2/deno/scheduler.js (6.31KB)
├─┬ https://esm.sh/react-spectrum@1.2.3 (214B)
│ ├─┬ https://cdn.esm.sh/v64/react-spectrum@1.2.3/dist/Spectrum.d.ts (1.68KB)
│ │ └── https://cdn.esm.sh/v64/@types/react@17.0.38/index.d.ts *
│ ├─┬ https://cdn.esm.sh/v64/react-spectrum@1.2.3/deno/react-spectrum.js (2.96KB)
│ │ └─┬ https://cdn.esm.sh/v64/react@17.0.2/deno/react.js (8.13KB)
│ │ └── https://cdn.esm.sh/v64/object-assign@4.1.1/deno/object-assign.js *
└─┬ https://esm.sh/react@17.0.1 (172B)
├── https://cdn.esm.sh/v64/@types/react@17.0.38/index.d.ts *
├── https://cdn.esm.sh/v64/react@17.0.1/deno/react.js *
Notice that two different versions of React appear in the output: react@17.0.1 and react@17.0.2. This incompatibility can cause our code to malfunction as the two versions of React are not compatible.
In this case, resolving the issue is relatively simple. We need to update the direct React dependency in our deps.ts file to match the version used by the react-spectrum package:
- export * as React from "https://esm.sh/react@17.0.1";
- export * as ReactDOM from "https://esm.sh/react-dom@17.0.1";
+ export * as React from "https://esm.sh/react@17.0.2";
+ export * as ReactDOM from "https://esm.sh/react-dom@17.0.2";
export * as ReactSpectrum from "https://esm.sh/react-spectrum@1.2.3";
However, in situations where multiple dependencies rely on different versions of React, simply changing the imported version of React is insufficient.
Fortunately, ESM.sh offers a solution for locking the version of a transitive dependency using a query parameter:
export * as React from "https://esm.sh/react@17.0.1";
export * as ReactDOM from "https://esm.sh/react-dom@17.0.1";
- export * as ReactSpectrum from "https://esm.sh/react-spectrum@1.2.3";
+ export * as ReactSpectrum from "https://esm.sh/react-spectrum@1.2.3?deps=react@17.0.1";
However, this manual approach becomes increasingly difficult when applied to each module in a complex project.