The Node documentation on ECMAScript Modules makes recommendations for "dual packages" that provide both CommonJS scripts and ES module scripts to users.
Specifically, they suggest transpiling ES module scripts to CommonJS, then using an "ES module wrapper", an ES module script that imports from CJS and re-exports its CJS named exports as ESM named exports.
// ./node_modules/pkg/wrapper.mjs
import cjsModule from './index.cjs';
export const name = cjsModule.name;
My question is: is it possible to do this the opposite way, providing my scripts in ES module format, but adding a simple wrapper file in CommonJS allowing CJS users to require it?
For my part, I can't see a way to do this in synchronous code. It appears to me that CommonJS scripts can only import ES modules via asynchronous dynamic import().
(async () => {
const {foo} = await import('./foo.mjs');
})();
But that means my CJS script can't export anything that depends on foo.
There is no way to synchronously import ESM modules. CJS exports are synchronous, but ESM imports are inherently asynchronous.
In CommonJS, require() is synchronous; it doesn't return a promise or
call a callback. require() reads from the disk (or perhaps even from
the network), and then immediately runs the script, which may itself
do I/O or other side effects, and then returns whatever values were
set on module.exports.
In ESM, the module loader runs in asynchronous phases. In the first
phase, it parses the script to detect calls to import and export
without running the imported script. In the parsing phase, the ESM
loader can immediately detect a typo in named imports and throw an
exception without ever actually running the dependency code.
The ESM module loader then asynchronously downloads and parses any
scripts that you imported, and then scripts that your scripts
imported, building out a “module graph” of dependencies, until
eventually it finds a script that doesn’t import anything. Finally,
that script is allowed to execute, and then scripts that depend on
that are allowed to run, and so on.
It isn't even necessarily possible to transpile ESM to CJS in all cases. For example, ESM can use top-level await, and CJS modules can't. As a result, there's no way to translate this ESM code into CJS:
export const foo = await fetch('./data.json');
CJS and ESM are completely different animals.
Related
This question already has answers here:
SyntaxError: Cannot use import statement outside a module
(34 answers)
Closed last year.
So I am currently practicing with a trading bot on hardhat. My problem is, when I want to run my script this error shows up:
import './ABIConstants.js';
^^^^^^
SyntaxError: Cannot use import statement outside a module
with this suggestion:
(node:1284) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
However, when I do as it tells me and set "type":"module" I get this error:
hardhat.config.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename hardhat.config.js to end in .cjs, change the requiring code to use dynamic import() which is avakage.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).
And the errors continue as I do the above....etc....
How do I fix this?
Heres the command I am using to run the script if it helps
npx hardhat run scripts/ArbitrageBot.js
There are two kinds of modules in nodejs - the older CommonJS which uses require() to load other CommonJS modules and the newer ESM which uses import to load other ESM modules? There are a few ways to mix and match module types, but that requires some additional knowledge and it is always easier if all the modules in your project are of the same type. So, for us to offer specific advice on your project, we need to know everything that you're trying to use in your project and what module type they all are.
The specific error you first report in your question is because you are trying to use import to load other modules from a file that nodejs is assuming is a CommonJS module and it will not allow that. If everything you are programming with is CommonJS modules, then switch to use require() to load your module instead of import. But, if everything isn't CommonJS modules, then it may be a bit more complicated.
The file extension (.mjs or .cjs) can force a module type or the "type": xxx in package.json can force a type. By default, with neither of those in place nodejs assumes your top level module with a .js extension is a CommonJS module where you would use require() to load other CommonJS modules.
The second error you get when you tried to force your top level module to be an ESM module makes it sounds like the module you are trying to import is a CommonJS module.
So, if I had to guess, I'd say the file you are trying to import is a CommonJS file and therefore, life would be easiest if you made your top level file CommonJS. To do that, remove the "type": "module" from package.json and change your import someModule to require(someModule) instead. This will attempt to just let everything be CommonJS modules.
i want to use this code in run in html, how could i use require.js to do this
const monerojs = require("monero-javascript");
the following doesnt work i get "Script error for "monero-javascript""
requirejs(["monero-javascript"], function(monerojs) {
main();
async function main() {
let walletKeys = await monerojs.createWalletKeys({networkType: "stagenet", language: "English"});
}
});
Require.JS is for loading AMD modules.
Node.js modules are either ECMAScript modules (which use import and export) or CommonJS modules (which use require and module.exports).
Even though both AMD and CommonJS modules use a function named require they are, in general, not compatible. (It is possible to write a module so that it meets the requirements of both AMD and CommonJS but this isn't too common).
If you want to use Node.js modules in the browser then use a bundler like Webpack or Parcel (but note that they cannot provide APIs that are specific to Node.js, such as the fs module for reading and writing to and from the file system).
Is there is a performance or behavioural difference between importing from an index file or importing individual modules?
For example, with an index file (#/modules/user) of...
import routes from './routes'
import controller from './controller'
const user = {
routes,
controller
}
export default user
If I import just the routes from that file...
import user from '#/modules/user'
const routes = Router()
routes.use('/user', user.routes)
Is this any different to just importing the routes individually from their own file (#/modules/user/routes)? Does the controller get imported as it's in the user object?
import userRoutes from '#/modules/user/routes'
const routes = Router()
routes.use('/user', userRoutes)
There are currently no native ES modules in Node.js. The actual difference depends on the toolchain. When the application is built with Webpack/Rollup (a minifier is potentially needed too) and configured to use ES modules internally, tree-shaking can be applied. This is best case scenario.
This would be a case for tree-shaking if there were a reexporting module:
import routes from './routes'
import controller from './controller'
export {
routes,
controller
}
And it was imported like
import { routes } from '#/modules/user'
However, the cases in the original post are different. In one case, once user constant is imported, it's impossible to remove unused controllers property from it with tree-shaking. In another case, #/modules/user/controller module remains unused and doesn't need tree-shaking. It will be ignored even if the application is configured to use CommonJS modules.
So yes, it's possible for ES modules to import only modules that are in use, and this heavily depends on actual code and project configuration.
Client-side applications primarily benefit from tree-shaking because it affects bundle size. This concern shouldn't be taken into account in server-side Node.js application - unless unused module imports massive amount of third-party modules that aren't used anywhere else.
Currently you can only use the import syntax using transpilers(converters from one syntax to another) as it is not yet natively supported by engines. Lets take babel for example.
Babel converts the import to CommonJS style code that can work within Node.js. While the syntax is conformant to ES6, the implementation is not.
Babel converts the syntax into CommonJS, evaluating the imported code before determining what is being imported. With ES6, the imports and exports are determined before the code is evaluated.
With future support of es6 imports inside node, importing a specific function inside the code will only return that exported function, without evaluating the whole script in the targeted file.
Currently they work the same since transpilers convert them to traditional node require syntax.
However, you can use webpack Treeshaking to remove unused code from the output file, this way they are almost identical in behavior.
The # syntax
The # syntax depends on the module loader or module bundler. The module loader is not part of the ECMAScript spec. You most likely have something like babel-plugin-root-import in your webpack/babel config.
Basically it means from the root of the project.. it avoids having to write things like import Component from '../../../../components/component'. in this case, it has nothing to do with partial imports.
Unfortunately my knowledge of JavaScript module loaders is still growing and I'm trying to understand their relationship to the new ES6 Modules. As far as I can tell using a module loader like CommonJS or RequireJS using ES5 compliant JavaScript really needed the use of an asynchronous module loader to increase performance and load only as needed using the respective module loader's syntax.
However looking at the ES6 module documentation and reading other information, it appears to me that module loading is natively supported via the import and export keywords. If this is the case am I correct that ES6 JS modules natively support asynchronous module loading and therefore I do not need to use an additional tool like CommonJS or RequireJS?
it appears to me that module loading is natively supported via the import and export keywords.
Not exactly. The import and export declarations only define the dependencies and the interface of each module. They allow to statically extract strings that name the required modules, nothing else.
If this is the case, do I not need to use an additional tool like CommonJS or RequireJS?
No. You still need to use a loader for ES6 modules, which resolves the names or paths or whatever from the imports to actual module files and loads them with an implementation-dependent method.
There are many tools or toolchains available, examples for the different solutions are
webpack: bundles everything into one large script
System.js: loads single modules dynamically and asynchronously (similar to what require.js does)
native: node.js and web browsers are still figuring out how to support module loading without additional libraries
babel transpilation: you can convert ES6 modules to AMD or CommonJS format and use the known tools like require.js for these
As far as my understanding goes, ES6 supports the syntax for defining and importing modules. The actual act of importing the modules that are required are a job of the infrastructure.
In modern browsers (as of 2016 that is) do not have built in functionality to support module loading and as such you will still need something like SystemJS to do the actual loading.
ES6 JavaScript Files are inherently treated as a module. if you define anything in a .js file, it will only be visible within that file (local scope ). what export does is, it exposes the classes / variables defined as export, visible to outside. then you can import it to a another module. There are other ways to define modules such as using Commonjs or AMD etc.. . Module loaders are required if you want to dynamically lazy load modules. ex. Systemjs is a such a Dynamic Module loader. it will fetch the physical module file from server dynamically when it is requested, and will prevent having multiple loads the same file. in SPA application in past had to load everything at the beginning to it to work. with dynamic module loaders now we can have only the files we need to do the intended job. hope this will help you.
https://github.com/systemjs/systemjs
Module loader is responsible for loading modules.
What I know is module loader loads modules in browser asynchronously whereas in Node.js it loads synchronously.
I wanted to confirm whether this information is correct or not.
ES6 module loaders will be asynchronous while node.js module loaders are not.
Here are some key aspects of module loaders:
Module code automatically runs in strict mode and there’s no way to opt-out of strict mode.
Variables created in the top level of a module are not automatically added to the shared global scope. They exist only within
the top-level scope of the module.
The value of this in the top level of a module is undefined. Does not allow HTML-style comments within the code (a leftover feature from
the early browser days).
Modules must export anything that should be available to code outside of the module.
https://leanpub.com/understandinges6/read#leanpub-auto-modules
Modules, in general, solve several problems for developers. First,
they allow the developer to separate code into smaller pieces, called
modules. Second, they make it easy for developers to load (inject)
those modules into other sections of code. Having modules injected
like this helps keep project code uncoupled from the module (read:
improved testability). And third, modules can load scripts
asynchronously. This means that apps can begin loading faster, as
they don’t require all scripts to be loaded prior to executing code.
http://chimera.labs.oreilly.com/books/1234000001623/ch03.html#_default_values
On the other hand because node.js is based on require which is synchronous this means node.js does not provide an asynchronous variant out of the box.
Of course there are async module loaders for node (async-require), but natively (with require) is not supported.
ESM is intentionally async (to accomodate loading over networks) and the rationale behind import also aims at knowing during code interpretation what dependencies exist (e.g. allowing bundlers to do treeshaking etc). This is also the reason to have imports at the beginning of your files and they can't be conditional (unless you use the await import() syntax).
You can also not eval(readFileSync(moduleFile)) if you use ESM syntax in the loaded file.