Is import of CommonJS module a sideEffect or not? - javascript

I've written down all files with side effects into package.json
//package.json
"sideEffects": [
"./src/a.js",
"./src/b.js",
]
So, I thought that's all, but then I've got an error produced by our bundle checker:
In the file 'c.js'
ModuleConcatenation bailout: Cannot concat with ./node_modules/json-stable-stringify/index.js: Module is not an ECMAScript module
It tells that we have the import of CommonJS module (there is module.export inside third party package).
So, what should I do? Is import of CommonJS module a side effect or not?
If I would write down c.js into sideEffect it'll help me out?
I cannot just try it out without confidence because we have a long release cycle.

Related

Problem with loading "ES module" when importing in node js [duplicate]

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.

Grunt and ES6 modules in node - incompatible without the use of .mjs?

So, I'm dabbling a bit with Typescript and Grunt at the moment to see if it's worth it for me. The thing is: Typescript does not compile to *.mjs files but only regular *.js files. Node does support ES6 Modules but only if you either mark them as '*.jsm' files or by setting "type": "module". Setting this top-level field in package.json however has global scope for any *.js file in the same directory and any following ones.
This breaks the Gruntfile.js file as it seems since it uses CommonJS modules, see my very basic Gruntfile as example:
module.exports = function (grunt) {
grunt.initConfig({
ts: {
default: {tsconfig: "./tsconfig.json"}
}
})
grunt.loadNpmTasks("grunt-ts");
grunt.registerTask("default", ["ts"]);
}
Without expecting much success I naively changed the export syntax from module.exports = to export default which expectedly did no work since it didn't make much sense.
Questions
Is there any option to use Grunt with ES6 modules enabled in node?
Is there a proper way to tell TypeScript to compile to *.mjs files?
If you set "type": "module" in your package.json, you need to rename Gruntfile.js to Gruntfile.cjs, and run it with grunt --gruntfile Gruntfile.cjs.
The suggested approach with Babel running before Grunt makes Grunt a bit redundant. Since TypeScript does not yet support exporting ES6 modules to *.mjs files (and you have to use the *.mjs suffix in your import when node should still be running with its CommonJS system) and will probably never fully (see Design Meeting Notes 11/22/2019) I have to conclude that ES6 modules still have serious implications and issues. Changing the file extension is not enough since the extension-less imports fail with node. You'd need to go through every compiled file and change the import to specifically load *.mjs files.
However, the TypeScript Compiler can be set up in a way that it does understand ES6 module syntax and to compile to CommonJS (see TS handbook).
{
"compilerOptions": {
"module": "CommonJS",
// [...]
},
}
This way the TypeScript code be written with ES6 module syntax and the output can be CommonJS compatible without braking other code. As a bonus you can skip the Babel approach and grunt can run TS compiler.
You can using babel-node to compile first. Which will resolve ES6 export and import problem.
npm install --save-dev #babel/core #babel/node
npx babel-node server.js

ES6 module, what counts as the first import?

Here's my module:
console.log("module imported");
export function call(){};
In main.ts:
import * as mod from './module';
// other code that doesn't use mod.
I would have expected this to log "module imported" to the console. In fact, the example seems pretty much the same as this one. And they say:
A module code is evaluated only the first time when imported
But there are no console logs. However, after the following edits to main.ts the log message appears:
import * as mod from './module';
if(false){
mod.call();
}
It would make sense if only the first time the module is actually used counted as the first import. But here the log message seems to be based on static analysis alone. The code path that uses the module is never executed.
How does this work? What counts as the first import of an ES6 module?
Also, my gut feeling says that this might be about the bundler. Does it optimize away an unused import like this? I'm running these code snippets in a react app, created with:
npx create-react-app my-app --template typescript
cd my-app
# add the module and import it to index.tsx
npm i
npm run start
# browser opens, check the console
On the other hand, the typescript react app also has imports like './index.css' and they are only there for the bundler to package them. It seems common to import something only for its side-effects.
I have searched for related questions but so far haven't found something with this specific problem:
Run ES6 code only if module is executed directly
`if __name__ == '__main__'` equivalent in javascript es6 modules
In browser JS code that imports from ES6 module is not executed
The last of these looks like a duplicate, but it is about a specific syntax error in the module resolution.
Your guess is correct, it's happening because of bundler. Its a feature of bundler known as Dead code elimination. To know more about it, search for Tree Shaking or Dead code elimination.
If you are not going to use anything from imported module, source code of module will not be included in your build.
I think create-react-app use Webpack for bundling. If you want to disable the feature, starting the app in development mode may solve it. BTW, its good to remove unused code while building.

how can I setup #types for a third party react javascript module so I can access it from typescript and package it with webpack?

There is a 3rd party all-javascript npm scoped package, let's call it #foo, with a module inside called bar. I wanted to use the react component #foo/bar/X from within my typescript .tsx file. I immediately ran into "module not found" when I tried to import X from '#foo/bar/X'. How can I resolve this using #types typescript to resolve the module X and get webpack to run without errors?
My starting point was this introduction on how to how to use react and webpack with typescript: https://www.typescriptlang.org/docs/handbook/react-&-webpack.html. However, said instructions do not tackle the problem that the react component I want to use has no #types. So my first step is I needed to add #types.
There was one important nuance about the scoped module #foo/bar, which was that it broke all its subcomponents into individual sub modules like #foo/bar/X, #foo/bar/Y, and #foo/bar/Z with #foo/bar itself having no functionality. As we will see that was an important nuance which made solving this a bit trickier than if I had not had to import a scoped module into my typescript.
There is a nice blog post here about how to import a vanilla javascript into typescript. Unfortunately there is a shortcoming to the proposed approach. Namely, the idea of adding "index.d.ts' to a src/#typings has two problems:
It works only for running the typescript compiler (tsc) locally. That is to say it will make tsc happy, by allowing tsc to resolve the module from your your local ".d.ts" file, but webpack will still fail with module not found.
The proposed approach didn't teach me how to deal with scoped modules which have different rules for how folders under #types are named
Whatever approach I tried, I knew I needed to have this import statement in my typescript file:
import X from '#foo/bar/X';
ReactDOM.render(
<X/>,
document.getElementById("example")
);
Somewhere, I needed to have the following in a ".d.ts" declarations file like index.d.ts, that much was obvious:
declare module '#foo/bar';
declare module '#foo/bar/X';
But running 'webpack' yielding Module not found: Error: Can't resolve '#foo/bar/X'
I decided to run the typescript compiler (tsc) with this flag to see the possible places where tsc would try to resolve the module.
tsc --traceresolution
Using the traceresolution flag I was able to see something very interesting: that the location under node_modules/#types where tsc searches was quite unexepected (and thereby where webpack searches, since webpack follows the same rules as tsc). It was expecting to find my index.d.ts file under node_modules/#types/foo__bar NOTE THE DOUBLE UNDERSCORE. That's right, for scope packages, you cannot have node_modules/#types/#foo/bar/index.d.ts. Instead you must use a single folder under #types named foo__bar with DOUBLE UNDERSCORE.
With this knowledge I created #types/foo__bar. I copied package.json from the #foo/bar module into #types/foo__bar. I cleaned out all the scripts and other useless stuff that the #types would not need, and stripped it down to this:
{
"name": "#types/#sfoo/bar",
"version": "2.6.0",
"peerDependencies": {
"react": "^16.3",
"react-dom": "^16",
"styled-components": "^3"
},
"dependencies": {
...
},
"engines": {
"node": ">=6"
},
"gitHead": "ac0288aaa47a4f15e56db3a5eff4424fb7905419",
"main": "",
"types": "index"
}
But yet there was one more problem! Even after tsc could resolve my #types, webpack still yielded module not found: can't resolve #foo/bar. So why the heck can't webpack find it? Again, I found a flag that could show me more:
webpack --verbose
But here I had less luck since the webpack rules were just like the typescript rules for module resolution. However from the --verbose output I did notice that one file webpack was interested in was node_modules/#foo/bar/index.js. However, the bar module lacked any index.js since it was really just an empty enclosing module around submodules like #foo/bar/X. Whether this is a bug or a feature of webpack, I don't know, but ADDING AN EMPTY index.js to node_modules/#foo/bar pacified webpack, and my simple example now worked with component X displayed in the browser when I loaded the HTML page.

Using node modules with Rollup to build web client

I'm trying to build a react application using rollup instead of browserify and babel. I realize I need to use the rollup-plugin-babel to transpile jsx, but when I tell rollup the format is iife, the final page loads with an error:
Uncaught ReferenceError: React is not defined
What do I need to add to the rollup.config.js to include the node modules I've installed in package.json in my final build?
Two options:
Include React as a separate <script> tag before your app bundle
Include rollup-plugin-node-resolve in your config file, to pull in dependencies from your node_modules folder.
If you take the second route you'll also need rollup-plugin-commonjs (to convert the CommonJS module into an ES module). I think you would also need to add import * as React from 'react' to each module that contained JSX, otherwise you'll continue to get the ReferenceError.
Note: you might be able to use rollup-plugin-buble to transpile JSX. It's similar to the Babel plugin but much faster (though it doesn't transpile every ES2015 feature)

Categories

Resources