Using npm modules with ES6 and Future of npm/bundlers - javascript

I use ES6 for a while and have no experience with npm or node.js.
I want to use some npm modules in my project using ES6.
I tried following and got an error
import {axios} from './axios.js';
The requested module './axios.min.js' does not provide an export named 'axios'
I am trying to use https://github.com/axios/axios/blob/master/dist/axios.js
I do not want to use <script src="..."></script> tag implementation. I want to to load it on demand with ES6 modules.
Is there any helper script to adapt npm modules to ES6 or any solution?

import {axios} from './axios.js';
would require Axios to have a named export like
export function axios(...params) { ... }
Axios does have a default export though, which you import without the curly braces:
import axios from './axios.js';
For imports to work in a browser, you need to declare the importing script with type="module":
<script type="module" src="./js/main.js"></script>
Edit:
Checking that URL you gave it seems Axios does not provide an ECMAScript module at all. Importing the way I described will work as long as you're using something like webpack to bundle your scripts.
Edit 2:
I have just filed an issue on the Axios repository regarding the topic: https://github.com/axios/axios/issues/1879
Edit 3 (April 2021):
Three years forward, even Node.js supports ES Modules, but Axios seems stuck on an old code base that looks unlikely to ever get updated, which causes problems when you want to use Axios along with Vue.js 3/Vite build stack, or Svelte.
A 20% the size but not all the features (e.g. interceptors are missing) rewrite in ES6 including proper exports is available with redaxios.

import { default as axios } from 'axios';

Related

When using the ES Module system in Node.js, can all Node packages be imported using the same import syntax?

This question is not whether Node.js supports the ES Module system.
That question has been answered previously, eg:
How can I use an ES6 import in Node.js?
Node.js - SyntaxError: Unexpected token import
And these answers are supported by the official Node.js docs:
ECMAScript modules are the official standard format to package JavaScript code for reuse...Node.js fully supports ECMAScript modules as they are currently specified and provides interoperability between them and its original module format, CommonJS. Node.js has two module systems: CommonJS modules and ECMAScript modules. Authors can tell Node.js to use the ECMAScript modules loader via the .mjs file extension, the package.json "type" field, or the --input-type flag.
Source
So it is clear that Node.js supports the ES Module system.
This question is:
When using the ES Module system in Node.js, can all Node packages be imported using the same import syntax?
The reason I ask is that a lot of packages' documentation does not specify how to import them using ES Module style, eg:
https://expressjs.com/en/starter/hello-world.html
https://github.com/expressjs/session
I want to avoid the situation where I am trying to figure out the correct syntax to use each time I want to import a library.
This answer seems to suggest this syntax:
import * as [ whatever_was_previously_used_as_the_variable_name ] from [ whatever_was_previously_between_the_quotes_in_the_parenthesis ]
So, using the examples above, this:
const express = require('express');
const app = express();
const session = require('express-session');
could safely be converted to:
import * as express from 'express';
const app = express();
import * as session from 'express-session';
Is that the 'universal' syntax that should be used to import all Node packages?
(Update: that session import above doesn't work, at app.use(session(sessionConfig)); it caused the error TypeError: session is not a function - I needed to define the import like this: import session from 'express-session'. I also had to import axios like this: import axios from 'axios', or i got a similar error. However, to use msal-node i had to use import * as msal from '#azure/msal-node').
Just to confuse things, I have come across other syntax examples, such as these in the Node docs (which includes node:):
import * as fs from 'node:fs/promises';
// OR:
import * as fs from 'node:fs';
Source
I am hoping a definitive answer can be provided that covers the different scenarios that may exist, eg: Node packages that live in the node_modules directory as well as one's own custom JavaScript modules that live outside of node_modules.
In this way, if I ever come across a package that does not specify installation instructions using the ES Modules style, I will still know the correct way to import it.
The import statement is indeed universal and can be used to import a module of either system, whether inside node_modules or out, or built-in to Node. When importing a CommonJS module this way, the value it would normally expose to require becomes its default export, so import name from 'url';, or any other form of import statement that retrieves the default export, will get you what you need.
The import * as name from 'url'; form you mentioned is for retrieving all of a module's named exports. This creates a module namespace object, never a function, though the function or other value that a CommonJS module exposes to require is available on the default property. There's generally no need to use import * with a CommonJS module, since everything that Node detects as a named export will be properties on the default export anyways.
You can also create a require from inside an ES module; this will work exactly the same way as require in CommonJS, but that includes the limitation of importing only CommonJS and built-in modules, not ES modules.

Set VS Code autoImports action to use ES syntax

I'm working on a node project in VS Code with a package.json file that has the "type": "module" set. when I type in an export name from another file the autoImport functionality automatically writes the import statement for me; although I would like it to write the import statement using ES modules syntax. currently, it imports it using require which doesn't make much sense since I wrote the export using ES syntax. how do I get VS Code to behave this way?
// example.js
export default function reader() {}
this is how the import works currently
I use this plugin for auto import. Maybe this plugin will help you too.

Use import inside a nodejs usually using require

I have a NodeJS app with a lot of routes, functions, etc. I have a lot of files with const xxx = require('yyy').
I now want to use this: https://www.npmjs.com/package/simple-ldap-search
I thought my journey would be as peaceful as it has been for now, but I can't use it, let me explain: instead of the usual require, I have to use import, as described in the documentation:
import SimpleLDAP from 'simple-ldap-search';
I searched on StackOverflow, and I saw I could put "type": "module" in the top package.json file, but then if I do I can't use require... It seems unsolvable.
Does it mean I have to choose between using ES6 or CommonJS? I read this question: Using Node.js require vs. ES6 import/export
I have the impression the import is more efficient, am I right?
Is it possible to use simple-ldap-search in my current node app without making big changes?
Is it possible to "convert" (I know it's not the precise term but I think you'll understand) the use of require to import and vice-versa?
Thanks for your answers.
As this project issue states, it isn't possible to use require in a project that switched to type: "module" in its package.json. For these use cases one would need to use a loader like Babel, etc.
It is worth considering switching over to ES modules, i.e to add type: "module" to your top-level package.json. It is still possible to import a CommonJS module with import but it does not work the other way around.
Alternatively, you can switch back to a 2.x version of that package, from what I see they made the switch to ES modules from 3.0.0 onwards.
While you can't use import in a commonjs file, you can use import()
This will make it more annoying when having to deal with the async loading, but is probably easier than converting your entire project to ESModules.
const simpleLDAPLoader = import('simple-ldap-search');
// later
async function () {
const {default: SimpleLDAP} = await simpleLDAPLoader;
const simpleLDAP = new SimpleLDAP();
}
Adn this will use the cached version every time you await the exisiting promise
Source: https://nodejs.org/api/esm.html#esm_interoperability_with_commonjs
Using require to load an ES module is not supported because ES modules have asynchronous execution. Instead, use import() to load an ES module from a CommonJS module.
I am not sure if it is going to help you in your case but I have found this solution for myself and I can use ES6 import and "require" at the same time.
In package.json:
"type": "module"
And in your file:
import { createRequire } from "module";
const require = createRequire(import.meta.url);
Works for my projects!

Importing distant modules in javascript from unpkg

I’m trying to import whole modules in a javascript file.
This file pertains to the Home Assistant environment where the frontend is written in javascript, usually using LitElements and modules (cf. documentation).
For instance, the doc uses a fancy wired-card by writing:
import "https://unpkg.com/wired-card#0.8.1/wired-card.js?module";.
I've read a lot about the import call but resources are usually about local elements and it seems that I need them to be distant.
In fact, I know the plain old JS quite well but I am a bit clueless regarding importing modules (and LitElements for that matter).
For instance, I'm looking for an accordion (expansion panel), like the one of JQueryUI. I found several resources (e.g. here, here, or here) but I couldn't find how to import them easily.
What makes a module importable? Are those not or am I doing it wrong?
In standard ECMAscript, a JS file is importable if it defines a module in the new system. Kinda circular.
Basically, it should export some resources from the module file. For example, if I have a test-module.js, I can export some class using the export keyword:
class Fubar {}
export { Fubar }
// or, more concisely
export class Fubar {}
The export keyword tells the module system that the resource defined should be made available to importers.
On the flip side, if you want to import a module, you must also do so from a module! This is because module imports are async and processed before the execution (excluding the dynamic import() function).
So, if I want to import my Fubar class from another module, I can do this:
import { Fubar } from './test-module.js`
However, if I load this script as a non-module, I will get an error. Instead, I must tell the browser that the script is a module:
<script type="module" src="test-module.js"></script>
So, in short, something is "importable" if it is itself a module.
More reading:
Mozilla Dev Network article on the modules system: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
MDN article on the import keyword: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
EDIT: something I missed - to make web resources a little nicer, the import URLs can be any URL, not just a relative path. This allows importing 3rd party scripts as modules. But, those 3rd party scripts need to be modules themselves.

import * as React from 'react'; vs import React from 'react';

I've noticed that React can be imported like this:
import * as React from 'react';
...or like this:
import React from 'react';
The first imports everything in the react module (see: Import an entire module's contents)
The second imports only the default module export (see: Importing defaults)
It seems like the two approaches are different and fundamentally incompatible.
Why do they both work?
Please reference the source code and explain the mechanism...I'm interested in understanding how this works.
Update
This is not a duplicate of What is the difference between import * as react from 'react' vs import react from 'react'
That question was answered with general ES6 module information.
I am asking about the mechanism that makes the react module work like this. It seems to be related to "hacky" export mechanism in the source here but it's not clear how that enables both importing the entire module and just the default export into React and having both of those approaches work with transpiling JSX, etc.
TL;DR
Indeed ES import statements import default and import * are not the same thing, the fact that they behave the same in this case is a combination of how React authors chose to publish the library and compatibility layers in TypeScript (using esModuleInterop) or Babel and your bundler to make them "just work". It probably shouldn't work according to ES6 spec, but today we are still working in an era where JS modules are a mess, so tools like Babel, TypeScript, Webpack, etc try to normalize behavior.
More details:
React is not an ES6 library. If you look at the source code you see this in index.js:
const React = require('./src/React');
// TODO: decide on the top-level export form.
// This is hacky but makes it work with both Rollup and Jest.
module.exports = React.default || React;
(Note the comment, even in React source code they struggle with ES6 default export compatibility.)
The module.exports = syntax is CommonJS (NodeJS). A browser would not understand this. This is why we use bundlers like Webpack, Rollup, or Parcel. They understand all kinds of module syntax and produce bundles that should work in the browser.
But even though React is not an ES library, both TypeScript and Babel let you import it as if it is (using import syntax, rather than require(), etc), but there are differences between CJS and ES that have to be resolved. One of them is the fact that export = can give you things that ES has no spec-compliant way to import, like a function or a class as the module. To work around these incompatibilities Babel has for awhile allowed you to import CJS modules as if they were exporting something by default, or import as a namespace. TypeScript for awhile didn't do this, but more recently added that as an option under esModuleInterop. So now both Babel and TypeScript can pretty consistently allow a CJS module to be imported using default or namespace ES imports.
With TypeScript it also depends on how the type-definitions for the library are actually defined. I won't get into that, but you can imagine situations where thanks to transpilers and bundlers a particular import works at runtime, but TypeScript doesn't compile without errors.
Another thing worth mentioning is that if you look at the built code for React there is a UMD module version as well as the CJS version. The UMD version includes some gnarly runtime code to try to make it work in any module environment, including the browser. It's mainly for use if you want to just include React at runtime (ie you don't use a bundler). Example.
Confusing? Yeah, I think so. :)
You most likely have "allowSyntheticDefaultImports": true, set in your tsconfig.json, which essentially shuts the compiler up about default imports it thinks are invalid. Typescript added esModuleInterop which does essentially what babel does for module loading.
This allows you to use ES6 default imports even when the source code you're importing doesn't export anything as default
Typescript is strict (follows the rules) when it comes to this, which is why they require you to import * as React from 'react'. Or requires you to tell it to allow synthetic default imports in its base config.
More On That Here

Categories

Resources