8

When I look at the documentation for Node.JS for the "path" module it explicitly states you must import the module by doing this: const path = require('path');

When I look at the documentation for Node.JS for the file system "fs" module it doesn't make any mention of doing the const fs = require('fs); instead it talks about using the import method import { mkdir } from 'fs'; but when I try to use the import method I get a syntax error: SyntaxError: Cannot use import statement outside a module

Using const fs = require('fs'); works just fine but I want to make sure my code is consistent/correct so which one do I use and why? If it's important for me not to require certain modules I want to figure out how to make it work correctly.

Mickey
  • 2,285
  • 6
  • 26
  • 37
  • 2
    Does this answer your question? [The difference between "require(x)" and "import x"](https://stackoverflow.com/questions/46677752/the-difference-between-requirex-and-import-x) – esqew Dec 14 '21 at 20:58

2 Answers2

18

Summary

import is the future of the Javascript language in both nodejs and the browser and is used in ECMAScript modules (ESM modules) for loading other modules, either statically or dynamically.

require() is the original way that nodejs loaded modules and is used in CommonJS modules. require() is natively supported in nodejs, but not in browsers (though there are some 3rd party libraries that have require-like module loaders for the browser).

Pick One

You will generally want to pick one mechanism and use it for your whole project as it complicates things to mix/match. import had some growing pains for awhile in nodejs, but the implementation is fairly complete now. Either way, you will probably run into some modules that are not of the type you chose for your project and will have to learn how to deal with that.

If I was starting a new project that I expected to last awhile and be working on it for years, I would pick ESM modules with import as that is the present/future architecture for modules in Javascript.

If I was writing a quick "get it done" kind of script that relied on a bunch of scripts that are only available in CommonJS, I would probably use CommonJS require().

If I was writing modules I wanted to share between nodejs and browsers, I'd pick ESM because it is supported in both.

Your Questions

When I look at the documentation for Node.JS for the file system "fs" module it doesn't make any mention of doing the const fs = require('fs); instead it talks about using the import method import { mkdir } from 'fs'

The current version of the fs documentation lets you pick whether you want the code examples to show CJS or ESM module syntax. You were apparently looking at the doc when it was showing ESM syntax. You can switch it. Either will work.

When you see this image enter image description here in the documentation, that lets you toggle the sample code in the doc from CJS to ESM or back.

SyntaxError: Cannot use import statement outside a module

In nodejs, you have to tell nodejs in some way whether your top level module is CJS or ESM. By default, a .js file is treated as CJS and an .mjs file is treated as ESM. If nodejs thinks you have a CJS file, you cannot use import (that is only for ESM modules).

You can also modify the package.json file that controls your particular script (by adding "type": "module" and force your file to an ESM module if you want (which allows you to use .js files as ESM modules).

Using const fs = require('fs'); works just fine

Yes, if nodejs thinks you are using a CJS module.

I want to make sure my code is consistent/correct so which one do I use and why?

See my previous explanation for when I'd choose each module type. If you are just learning, I would suggest you learn how to tell nodejs you're using ESM modules and use import since that is the future direction of the language. require() isn't going to go away anytime soon, but I would not be surprised if we start to see new NPM packages (shareable modules) that only support the newer ESM syntax and can only be loaded from ESM modules. It is possible (with some careful work) to make a shareable module that can be loaded by either an ESM module or a CJS module.

Also, ESM modules are getting some features (such as top-level await) that CJS modules will never get.

More Details

For more details, I suggest reading this article:

Node Modules at War - Why CommonJS and ESM Modules Can't Get Along

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 1
    adding `"type": "module"` to package.json allows for import feature but gets rid of node wrapper entirely `exports, require, module, __filename, __dirname` - I don't have the CJS and ESM toggle on my Node.JS docs. The link you posted is very helpful. I'm new and trying to wrap my mind around this stuff. It's difficult. Haven't coded anything since PHP/MySQL 16 years ago. – Mickey Dec 15 '21 at 00:54
  • @Mickey - Yes, ESM modules do not have `module`, `require`, `__filename`, `__dirname`. You use `import` instead of `require`, `export` instead of `module.exports`, and `__filename` and `__dirname` can be synthesized from `import.meta.url`. ESM modules are a different type of module. You play by their rules when using them. – jfriend00 Dec 15 '21 at 01:36
  • @Mickey - Look here: https://nodejs.org/api/fs.html#file-system. You should see that toggle for changing the sample code. It's by some methods, not all. – jfriend00 Dec 15 '21 at 01:37
  • I'm learning Node.JS right now. I hate that there are so many instances of "do what you want and it will still work". I like programming languages where there is only one way so this is confusing. Can I use 100% CJS or 100% ESM or do I have to use both? – Mickey Dec 15 '21 at 01:54
  • @jfriend00 I have a rather simple reason to mix `import` with `require` in my nodejs code, I need to use [debugjs](https://github.com/debug-js/debug) which is cjs only, so in my esm code I always add `import { createRequire } from "module"; const require = createRequire(import.meta.url);` to use debugjs, so far I haven't seen any problem. – Qiulang May 26 '23 at 04:40
  • @jfriend00 even in my ts code I mix `import` with `require` to use [debugjs](https://github.com/debug-js/debug), as I asked a question here https://stackoverflow.com/questions/74690087/what-is-the-problem-of-mixing-require-and-import-in-the-same-typescript-file can you take a look ? – Qiulang May 26 '23 at 04:42
1

As @jfriend00 mentioned, even though you don't want to mix require and imports, there are some use cases where some packages you need can't be imported as a module. In this case, you can use the code below before the require statement.

import { createRequire } from "module";
const require = createRequire(import.meta.url);