require() resolves to CJS

I am trying to import hashids CDN by jsDelivr - A CDN for npm and GitHub and fail. I know that some solution from require debugger will eventually work, but it will be a much better experience if require('hashids@2.2.8') could simply work. But it doesn’t.

image

The https://cdn.jsdelivr.net/npm/hashids@2.2.8/cjs/index.js is a single line script.

module.exports = require('../dist/hashids.js').default

And if I understand it correctly, it is an old CommonJS module format, and d3-require that powers Observable require() only support AMD modules (which are also outdated if I understand it right).

There are at least two more dirs with JS code in hashids CDN by jsDelivr - A CDN for npm and GitHub. There is an explicit ESM dir, which I understand that is the module format of the future.

  1. Why d3-require still resolves to unsupported CJM instead of trying something that might work?
  2. Why d3-require can not support ESM?

The package maintainer should really add a “browser” (and a jsdelivr) item to the package.json, but the URL you are looking for is:

hashids = require("https://cdn.jsdelivr.net/npm/hashids@2.2.8/dist/hashids.min.js")

Found a reference to https://docs.npmjs.com/cli/v7/configuring-npm/package-json#browser but no example how it should look like. Still looking for jsdelivr definition.

Finally found jsdelivr/README.md at master · jsdelivr/jsdelivr · GitHub which finally provides an example of main module that in case of hashids points to CommonJS module. What format is assumed to be default for the main?

“main” normally points to a CommonJS compatible module aimed at the NodeJS environment (CJS).
“browser” normally points to a web browser specific module (one which relies on window, document etc.)

In addition folks may also have “module”, “unpkg”, “jsdelivr” specific entries - see https://cdn.jsdelivr.net/npm/d3/package.json as an example.

Again for hashids to “just work” it should add a browser entry and ideally a jsdlvr entry.

2 Likes

hashids defines module in its package.json, so you can just do:

hashids = (await import('hashids@2.2.8')).default
1 Like

Send PR Add 'browser' field to `package.json` by abitrolly · Pull Request #462 · niieani/hashids.js · GitHub

Interesting that d3 doesn’t define browser entry itself.

It is not an ESM module, right? At least I can not find that .default is needed in other examples. And ESM seems async already, so it doesn’t need the wrapper.

Why Observable can not use ECM modules directly?

.default is needed because this is a dynamic import.

Observable offers some syntactic sugar by wrapping the browser’s native import() function and expanding package names to full unpkg.com URLs:

  1. import("hashids@2.2.8") becomes import("https://unpkg.com/hashids@2.2.8?module")
  2. https://unpkg.com/hashids@2.2.8?module redirects to https://unpkg.com/hashids@2.2.8/esm/index.js?module; unpkg.com knows how to redirect the request because the package.json contains the entry"module": "esm/index.js".

You can, via import().

I still do not understand how can one understand how make it work. The documentation on hashids doesn’t mention any dynamic imports.

Use in ESM-compatible environments (webpack, modern browsers)

import Hashids from 'hashids'
const hashids = new Hashids()

console.log(hashids.encode(1))

Neither MDN page document nor Module require debugger / Observable | Observable shows any .default reference or example (there is no info in Introduction to require / Observable | Observable and How to require stubborn modules | Observable documentation).

I understand that Observable JavaScript / Observable | Observable but I my background is Python, and learning the history of JavaScript imports without any experience in that is hard. That’s why I highly rely on examples when bruteforce experiments do not work.

Thanks to your answers I could finally make it with hashids = (await import('hashids@2.2.8')).default.

image

But I wish I won’t have to resort to forum.

2 Likes