What does it mean if an import cannot find the module but the file path and case sensitivity is correct? I’m sensing a NodeJS issue or config issue (babel? eslint?) rather than the JS itself. I’ve tried using Node v19 and v20 with no change. I’ve found this problem happening for two different cases:
- Module not found when file path leads to a directory that should default to index.js
import Inventory from '../../models';
- Module not found when file path excludes the supposedly implied .js extension
import { Inventory } from '../index';
It is fixed when I explicitly import the file. Example:
import { Inventory } from '../index.js';
import Inventory from '../../models/Inventory.model.js';
Here’s an example of the setup where /models directory has the index.js
file and /models/Inventory directory has the Inventory.models.js
but other imports listed above are giving "Module not found" even with the right file path and case sensitivity. All of my tests run and pass when I explicitly import my files, so I know the exports are all correct. Does this look on par to you?
To be sure my imports and exports are correct, here is /models/index.js
import Inventory from './Inventory/Inventory.model.js';
// define model relationships here
export { Inventory };
To be sure my exports are correct, this is the const export from /models/Inventory/Inventory.model.js
export default Inventory;
I also tested renaming the files and import statements to camel case instead of dot case, and the issue still persists, so it’s not the issue either.
Edit: This is the /server/package.json:
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"watch": "nodemon",
"seed": "node config/seeds.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcrypt": "^5.1.1",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.2",
"mysql2": "^3.9.2",
"sequelize": "^6.37.1"
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"nodemon": "^3.1.0",
"sequelize-mock": "^0.10.2",
"uuid": "^9.0.1"
}
}
…And the root package.json:
{
"name": "name",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest",
"start": "node server/index.js",
"dev": "concurrently "cd server && npm run watch" "cd client && npm start"",
"install": "cd server && npm i && cd ../client && npm i",
"build": "cd client && npm run build"
},
"repository": {
"type": "git",
},
"keywords": [],
"jest": {
"testEnvironment": "node",
"transform": {
"^.+\.js$": "babel-jest"
}
},
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.24.0",
"@babel/register": "^7.23.7",
"babel-plugin-inline-dotenv": "^1.7.0",
"concurrently": "^8.2.2",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.33.2",
"jest": "^29.7.0",
"prettier": "^3.2.5",
"supertest": "^6.3.4"
}
}
2
Answers
Per the node.js import documentation,
To import a module by its name without the file extension (i.e. a bare specifier), the module must have an
exports
field in itspackage.json
.More information about import specifier resolutions can be found here.
Basically, when using modern JavaScript (ESM) in Node.js, you gotta stick to some rules. Node doesn’t guess
.js
orindex.js
for you when importing modules. So, if you’re importing stuff, always add.js
at the end of file names, and if you’re pulling from a directory, you gotta sayindex.js
explicitly. Node’s strict like that with ESM. Just remember: no shortcuts with file names or expecting Node to fill in the blanks for you. Stick to this, and you’ll dodge those "module not found" headaches.