How to run a file with imported ES6 modules in Node - javascript

I have a react app set up with webpack, and would like to also have a script that runs in node to write stuff into output file, such as:
script.mjs
import fs from 'fs';
import {SomeClass} from './index.js';
const outputFile= fs.createWriteStream(__dirname + '/output.png');
// Writes into output file
and have the following script command in package.json:
"scripts": {
...
"runFile": "node --experimental-modules test/script.mjs",
...
}
Whenever I run npm run runFile it complains with:
import {SomeClass} from './index.js';
^^^^^^^^^
SyntaxError: The requested module './index.js' does not provide an export named 'SomeClass'
even though it exists in that file:
./index.js
export SomeClass from './SomeClass';
I even used node -r esm test/script.js to run in node, but it keeps complaining about ES6 exports all the time. Could anybody point me to how to run a js file that has ES6 stuff with node command?
NODE v10

There is a small inconsistency in your export/import statements.
Oh, and you are exporting from an entirely different file. Is there are reason you need to do so?
You may export an containing your class
export { someClass
Then you can import it with the present import statement.
More direct would be to export from your ./someClass file with a default export
export default someClass
then import it without destructuring the object:
import some class from './someClass';
The latter would fit well with the file name and is a style expected by most.
Note that when you run old node versions you need some extra CLI flags to run modules. With present node (^13) it is enough to set type: module. See Hao Wu's answer.

Related

How to get Typescript to compile CommonJS imports?

I have written a TS file, that loads in a 3rd party package, using import XXX { YYY, ABC, 123 }from 'XXX';
It will compile to CommonJS no issue, and thats OK. But I'd like to compile it to an ESModule. I changed the target and module settings to esnext in my TS config file, and it does compile, however, when I run the file, it errors out saying:
SyntaxError: Named export 'ABC' not found. The requested module 'XXX' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'XXX';
const { ABC } = pkg;
Is there any settings I can use to tell Typescript to convert the imports to the import tye shown in the error message?
You may want to try nodenext instead of esnext for the module setting. This is currently experimental but it seems to address your need with CommonJS interop.
I haven't tried it myself so I cannot promise it will work.

node.js 13 - import is not working properly

$ node --version
v13.8.0
Let's create three files:
// package.json
{
"type": "module"
}
// foobar.js
function foo() { console.log('foo') }
export default {foo}
// index.js
import Foo from './foobar.js';
Foo.foo();
Run index.js
$ node index.js
(node:22768) ExperimentalWarning: The ESM module loader is experimental.
foo
All working.
And now changing './foobar.js'; to './foobar';
// index.js
import Foo from './foobar';
Foo.foo();
And we get an error!
(node:22946) ExperimentalWarning: The ESM module loader is experimental.
internal/modules/esm/resolve.js:58
let url = moduleWrapResolve(specifier, parentURL);
^
Error: Cannot find module /foobar imported from /index.js
There is no other files in directory.
Why does it happens?
Why import without extension doesn't work?
UPDATE:
https://nodejs.org/api/esm.html
package.json "type" field
Files ending with .js or lacking any extension will be loaded as ES modules when the nearest parent package.json file contains a top-level field "type" with a value of "module".
So './foobar' must work.
UPDATE 2:
I believe what the documentation call "extensionless files" are literaly files without extensions, not files imported without extensions.
For example, if you import your file with import Foo from './foobar';, and you file is called foobar without the .js extension, it will work fine.
Thanks to #Seblor
Looking at the documentation, in the category Differences Between ES Modules and CommonJS, and more precisely at the "Mandatory file extensions" section, it says that the .js file extension must be present to make the import work :
A file extension must be provided when using the import keyword. Directory indexes (e.g. './startup/index.js') must also be fully specified.
This behavior matches how import behaves in browser environments, assuming a typically configured server.
I am not sure why your last question is about the import without extention not working anymore. I guess you have been working at some point with a transpiler like babel that will resolve the files without the extensions.
Edit :
I believe what the documentation call "extensionless files" are literaly files without extensions, not files imported without extensions.
For example, if you import your file with import Foo from './foobar';, and you file is called foobar without the .js extension, it will work fine.

How to use import inside eslintrc file?

I'm trying to use imported object to setup some restrictions for globals inside .eslintrc.js file, but import doesnt work. How can i make dynamic eslint config?
import {loadedGlobals} from '#/util/globals'
module.exports = {
'globals': Object.keys(loadedGlobals).reduce((acum, key) => {
acum[key] = false
return acum
}, acum),
// ...
}
How to use import inside eslintrc file?
ESLint currently doesn't support a configuration file by the name of eslintrc so I'm going to assume you mean .eslintrc.js.
ESLint currently does not support ES Modules as you can see from the JavaScript (ESM) bullet item on their configuration file formats documentation.
If you are willing to install another dependency here is how you can use import inside of .eslintrc.js:
Install the esm module, npm i esm -D (Here I'm choosing as a devDependency).
Create a new file as a sibling to .eslintrc.js called .eslintrc.esm.js.
Inside of .eslintrc.esm.js include your ESLint configuration. Here you can use import and you should export your configuration as export default { // Your config }.
Inside .eslintrc.js include the following code:
const _require = require('esm')(module)
module.exports = _require('./.eslintrc.esm').default
Now you should be able to run eslint as usual. A bit clunky with the extra file, but you can organize them in a directory if you like and use the --config option of eslint to point to the new location.
You might notice that you are using the old syntax when exporting your object. You could try using require() instead of import.
Alternatively, you could look into Shareable Configs.

What does import Module from 'module' import when no default export is defined and why is it different from import * as Module?

I am pretty new to JavaScript and have been struggling with imports recently. There has been one thing I cannot wrap my head around.
In older node modules (mostly those which came to see the light prior to ES6), which may be installed using the npm, such as express, usually no default export is defined.
My IDE (WebStorm) marks the following line with the Default export is not declared in the imported module notification.
import express from 'express';
This message may be circumvented by trying to import the whole module as an alias using
import * as express from 'express';
implicitly telling my IDE to just import everything and name it express, however doing so then leads to an express is not a function error when trying to instantiate the application on the following line.
const app = express();
Peculiarly the original import (without the alias) works.
What exactly is imported using the import statement without the alias, when no default export is defined? I would think it is the whole module, but it does not seems so.
What does import Module from 'module' import when no default export is defined?
Nothing. In fact, instantiating the module will throw a SyntaxError when something is imported that is not exported or exported multiple times from the imported module.
Why is it different from import * as Module?
Because import * just imports a module namespace object that has the exports as properties. If you don't export anything, it'll be an empty object.
In older, pre-ES6 node modules usually no default export is defined.
Which means that you cannot import them as an ES6 module. Your IDE seems to expect that though and puts up the warning.
So what happens if you refer to them in an import declaration? Your module loader might do anything with it, and HostResolveImportedModule will return a module record that is not an source text "ES6 module" record - i.e. it might do anything implementation-dependent with CommonJS modules.

Can't find module 'hbs' with ES6 style import

I'm doing this in TypeScript, but tried it in vanilla JS as well with the same error. I've pulled down two modules: express and hbs. I'm trying to use the ES6 import syntax like this:
import * as http from 'http';
import * as express from 'express';
import hbs from 'hbs';
The last line gives me an error saying it can't find module hbs. I'm looking right at it... I can see it just fine. However when I replace the line with the older CommonJS syntax:
var hbs = require('hbs');
It works fine... what gives? Still on the learning curve with ES6...
Observation 1... as you have in your other exports, you should either import the whole module with an alias:
import * as hbs from 'hbs';
Or you can choose to import specific exports:
import {thing} from 'hbs';
Observation 2... is hbs a TypeScript module, or a JavaScript one? If it is a JavaScript one (as I believe it may be) you will need to pair it with a definition file, for example hbs.d.ts that describes the JavaScript file. TypeScript won't recognise a plain JavaScript module without the definition.
I was experiecing a similar problem. The syntax is correct ES6 indeed.
Good news is that the problem seems to have been fixed already in the development version of the typescript compiler 0.8: try 'npm install typescript#next -g' and then running the compiler again.
you should use default as the imported module name .
import {default as hbs} from "hbs";
this works same as
var hbs = require('hbs');
because require("hbs") imports default module exported by hbs.

Categories