5

I've written a javascript library as an ES6 module, primarily intended for use in the browser. Now I want to package it for node. I'm ok with restricting to node v 14+. I don't want to transpile.

I'm not sure how to handle the dependencies. One in particular, moment-js, is giving me trouble.

In the browser (tried latest FF + Chrome), I reference the moment library in a <script> tag, and then in my code, I can just use the variable moment, without having had to first import it.

my-module.js:

function testFn() {
  return moment.duration(300).asSeconds();
}

export { testFn }

browser code:

<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.26.0/moment.min.js"></script>
<script type="module" src="my-module.js"></script>
<script>

import { testFn } from 'my-module.js';
let val = testFn();
console.log(val); 

</script>

However, in node, I need the module to include an import in order for it to work:

my-module.js:

import moment from 'moment';

function testFn() {
  return moment.duration(300).asSeconds();
}

export { testFn }

Then I can use my-module in node:

import { testFn } from 'my-module.js';

let val = testFn();
console.log(val); 

But, now the browser code is broken: TypeError: Error resolving module specifier: moment

I've tried different imports (import * as ..., import { moment } from ...) but no luck - the browser still complains.

Moment-js is just an example here - I'd like a generic solution as I have other dependencies too.

This must be an issue that other people have had and solved without resorting to awful workarounds like detecting the environment and conditionally importing. Does anyone have a good solution or reference? Thanks!!

xgdty
  • 161
  • 1
  • 5
  • have you installed the npm to the directory?. it looks like you were using a cdn before, just trying to cover basis – Jon dreisbach Jun 27 '20 at 20:54
  • Yes, the module folder has the dependency (moment-js) installed via npm. Otherwise the import statement would not work at all. The issue is that when using the module in a script running in a browser, having that statement in the module source raises an error, whereas in a node environment, it won't work without it. – xgdty Jun 28 '20 at 21:51
  • I feel like this might be one of those silly things that are so simple but still take a couple of hours to figure out lol. like the order in which certain modules are required – Jon dreisbach Jun 29 '20 at 00:03
  • There are quite good reasons, people uses transpilers. Like this one. There is a dynamic import() that could be helpful for your case: https://v8.dev/features/dynamic-import Alternatively you could register your third party libs in the global scope (self for workers, window for browsers and global in node. Or simply globalThis in a newer enviroment) – Kai Lehmann Jul 01 '20 at 20:15
  • You would need to provide a relative path to the package, since the browser is not able to resolve the node_modules path like nodejs does. Here, check this answer: https://stackoverflow.com/a/52558858/6080889 – dRoyson Jul 02 '20 at 23:39
  • is there a link that would help find all/most of the JS libraries hosted on CDN? That would be helpful. – Shalitha Jayamal Jul 04 '20 at 20:10

3 Answers3

0

Did you try import * as moment from "./moment.js"; (you may need to fiddle with the path based on where you are installing it and how moment handles the file)?

Browsers do not support bare imports and I believe some older libraries like moment are still bare.

Also I would just say, this is exactly what transpiling and build tooling can be great for. It can make dealing with this type of situation much simpler. Certain things can not be conditional checked for and you just have to transpile to the lowest compatibility.

otw
  • 630
  • 4
  • 13
0

This should kind-of work with the caveat below:

const M = typeof moment !== 'undefined' ? moment
  : (await import('moment')).default
  • as of now, top level await only works in chrome, node and firefox canary
  • officially, top level await is still only a stage 3 proposal
Hurelu
  • 1,458
  • 1
  • 14
  • 23
0

Set the main:main.js and browser:browser.js fields in package.json that set-up whatever environment-specific elements before importing and re-exporting the common index.js module.

//main.js fix for nodejs
import {something} from 'nodejsSpecificModule'
global.something = something //or export whatever is needed
export {...} from './index.js'
//browser.js fix for browser
import {something} from 'browserSpecificModule'
window.something = something //or export whatever is needed
export {...} from './index.js'

this avoids dynamic imports with top-level-await

Hurelu
  • 1,458
  • 1
  • 14
  • 23