Importing CommonJS modules to an ESM syntax - javascript

I'm struggling to understand how to import CommonJS modules into an ESM syntax. I'm currently trying to work with the library url-metadata. url-metadata exposes a top-level export as a callable (which does not really conform to CommonJS, AFAIK):
const urlMetadata = require('url-metadata')
urlMetadata(URL, ...)
It's not possible to write:
import urlMetadata from 'urlMetadata'
since no default export is defined.
Instead, I have to write:
import * as urlMetadata from 'url-metadata'
Or:
import urlMetadata = require("url-metadata")
I tried to read up on module loading in Node but I'm still somewhat confused as to what is the correct way to do this and why.

import urlMetadata from 'url-metadata';
is syntactic sugar for
import { default as urlMetadata } from 'url-metadata';
Either will work fine.
The value assigned to module.exports in a CommonJS module is the default export.
See the Node.js docs.

Related

module export in ts file is not a module error

I have a config.ts and I do
module.exports = { something: 123 }
when I import like import { something } from './config.ts' I got error of config.ts' is not a module, what's the issue? my typescript is configured rightly and it's working in other places.
If you're using import { something } from './config.ts', you're using JavaScript modules, but your code in config.ts is using CommonJS modules. Some bundlers and such may let you mix them, but it's best not to.
To make config.ts a JavaScript module compatible with that import declaration (which expects a named export called something), change it to:
export const something = 123;
Or, of course, to use config.ts via CommonJS, your code using it would be:
const { something } = require("./config.ts");
...but given the error you're getting, I think your project is set up to use JavaScript modules (import/export), which here in almost 2021 is probably best (now that we have dynamic import to handle the cases where static modules don't quite do the job).

How do you find out which functions are exported by a node/npm package?

Following my earlier question, and the Mozilla documentation on import, I now understand that I must do something like the following to use the functionality in a module:
import * as name from "module"; or
import {functionName} from "module";
Coming from using CommonJS, I never thought about which functions were exported by a package because I just used to require them like:
const vueServerRenderer = require('vue-server-renderer') // get the module
vueServerRenderer.createRenderer() // use a function in that module
How can someone find out which functions are being exported by a module such as express or vueServerRenderer so I know how to use the correct import statement like:
import express from 'express' instead of import * as express from 'express'?
You need to read the module source.
Every export statement exports something. It may be a function, an array, a string, a class etc.
Every export statement without default needs to be destructured on import:
import { NonDefaultThing1, NonDefaultThing2 } from 'somewhere'
An export statement with default must be imported directly without the {}:
import DefaultThing from 'somewhere'
Some modules have default export but also non-default exports. You can pick and choose what to import:
import DefaultThing, { NonDefaultThing7 } from 'somewhere'
If you use an IDE that can parse javascript such as Microsoft Visual Studio Code you can get autocompletion/intellisense of the import statement. There are even plugins that does auto-import: just use a class or function or something from a module and it will automatically add the required import statement at the top of your file.
TLDR: The default export.
Say a particular library named "module" has the following code
function functionName() {
// function body
}
export default functionName;
Now, in your code, if you put
import blah from "module";
then blah will point to functionName.

When do we use typescript import * as?

Trying to developer a mental model for what import * as Blah does. For example:
import * as StackTrace from 'stacktrace-js';
How does that work and when do we do import *?
Not really an answer, but a usage: Consider you have a few constant strings to be used in your application, you can define them in a single file and export
export const name = "NAME";
export const someOtherString = "SOME_CONST_STRING";
Then you can import them in a single variable using:
import * as CONST_STRINGS from './yourFilePath';
and use as
CONST_STRINGS.name
CONST_STRINGS.someOtherString
From the TypeScript doc:
Import the entire module into a single variable, and use it to access the module exports
The example code imports all exports of the stacktrace-js module into a variable called StackTrace.
Any named exports will be available as properties of the same name.
If the module has a default export it will be available as the default property.
Note also from the TypeScript Module doc:
Starting with ECMAScript 2015, JavaScript has a concept of modules. TypeScript shares this concept.
So TypeScript modules behave in the same way as ES6 JavaScript modules.
You would use import * as in either TypeScript or JavaScript when you want access to all of the module exports in a single variable.

using the ... spread syntax in javascript es6 named exports

I am attempting to import everything from a library as a hash, modify it, and re-export the modified hash, without knowing all of the named exports in a library. For example:
import * as reactBootstrap from 'react-bootstrap';
wrappedReactBootstrap = doFunnyThingsTo(reactBootstrap);
export {
...wrappedReactBootstrap
};
// or
export wrappedReactBootstrap;
My understanding of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export is that the following is not allowed by the spec. Could someone confirm?
Obviously, looping is out of the question, since export and import statements must be top level.
Object rest spread is stage 3 proposal and not a part of any spec (will probably be included in ES2018).
More importantly, export has syntax that mimics existing JS syntax but doesn't interpret { ... } as an expression. export syntax was strictly defined because ES2015 modules are supposed to be statically analyzed. This is one of their benefits, but it requires the developer to specify exports and imports explicitly.
Since { ...wrappedReactBootstrap } introduces dynamic export (it was used here exactly for this purpose), it isn't supported by ES2015 module export and it is very unlikely that it will be.
If it is necessary to provide dynamic behaviour for the export, it can be exported and imported as named or default object.
import * as reactBootstrap from 'react-bootstrap';
export default doFunnyThingsTo(reactBootstrap);
And used like
import wrappedReactBootstrap from '...';
const { funny, thing } = wrappedReactBootstrap;
Obviously, wrappedReactBootstrap object won't get the benefits of ES2015 modules this way, e.g. tree-shaking.

How do i write this require as an es6 import statement

Problem
I have this require statement
require('smoothscroll-polyfill').polyfill();
But I would like to write it as an es6 import statement
I have tried
import 'smoothscroll-polyfill';
and
import * as 'smoothscrollPolyfill' from 'smoothscroll-polyfill';
But cant get it to work correctly, so what is the correct way to import a package like this?
You'd do it in two parts, first the import, then the function call:
If polyfill itself is a named export and it doesn't care what this is when it's called:
import {polyfill} from 'smoothscroll-polyfill';
polyfill();
(And you've now confirmed that was the case.)
For completeness, before the above was confirmed, I also listed these other possibilities which may be useful for others in future:
If polyfill is a property on the default export (rather than its own named export), then we import the default (no {} in the import statement) and then use its property:
import smoothScrollPolyFill from 'smoothscroll-polyfill';
const polyfill = smoothScrollPolyFill.polyfill();
If the smoothScrollPolyFill part is a named export and polyfill is a property on it, then we'd use the {} on import:
import {smoothScrollPolyFill} from 'smoothscroll-polyfill';
const polyfill = smoothScrollPolyFill.polyfill();
using import {} from '...' instead.
import {polyfill} from 'smoothscroll-polyfill';//ref polyfill from 'smotthscroll-polyfill'
polyfill();//ref a method,so you must call it after imported.
Assuming you're using Babel or something similar to provide compatibility between CommonJS modules and ES6 modules, the syntax would look something like this:
import smoothscrollPolyfill from "smoothscroll-polyfill";
smoothscrollPolyfill.polyfill();

Categories