Windows环境下使用readdirSync与Sequelize加载模块报错求助
Windows下ESM环境中readdirSync+Sequelize加载模块报错的解决办法
问题情况
在Windows机器上使用readdirSync和Sequelize加载同目录模块时出现异常,项目在Mac/Unix系统可正常运行。Node版本为v16.16.0,切换其他Node版本问题依旧,package.json已配置"type": "module"。
错误日志
[nodemon] restarting due to changes... [nodemon] starting `node --experimental-specifier-resolution=node --inspect ./index.js` Debugger listening on ws://127.0.0.1:9229/05b29298-95ff-4a2d-8445-64d74fad50b2 For help, see: https://nodejs.org/en/docs/inspector (node:34452) ExperimentalWarning: The Node.js specifier resolution flag is experimental. It could change or be removed at any time. (Use `node --trace-warnings ...` to show where the warning was created) node:internal/errors:465 ErrorCaptureStackTrace(err); ^ Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:' at new NodeError (node:internal/errors:372:5) at throwIfUnsupportedURLScheme (node:internal/modules/esm/resolve:1120:11) at defaultResolve (node:internal/modules/esm/resolve:1200:3) at ESMLoader.resolve (node:internal/modules/esm/loader:580:30) at ESMLoader.getModuleJob (node:internal/modules/esm/loader:294:18) at ESMLoader.import (node:internal/modules/esm/loader:380:22) at importModuleDynamically (node:internal/modules/esm/translators:106:35) at importModuleDynamicallyCallback (node:internal/process/esm_loader:35:14) at loadModel (file:///D:/Storage/backend/models/index.js:32:31) at file:///D:/Storage/backend/models/index.js:37:20 { code: 'ERR_UNSUPPORTED_ESM_URL_SCHEME' } [nodemon] app crashed - waiting for file changes before starting...
目录结构
backend: - package.json - models - index.js - users.js - services.js
相关代码
package.json
{ "type": "module", "description": "Project", "main": "./index.js", "license": "MIT", "scripts": { "develop": "nodemon --experimental-specifier-resolution=node --inspect ./index.js", "start": "node --experimental-specifier-resolution=node ./index.js" } }
models/index.js
import { Sequelize, DataTypes } from 'sequelize' import fs from 'fs' import path from 'path' import config from '../config.cjs' const { DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, DB_PORT } = config const db = new Sequelize( DB_NAME, DB_USER, DB_PASSWORD, { port: DB_PORT, host: DB_HOST, dialect: 'postgres', underscored: true, underscoredAll: true, timestamps: false, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone } ) db.loadModels = async () => { const models = await Promise.all( fs.readdirSync(path.resolve('models')) .reduce((acc, file) => { if (file !== 'index.js') { const modelPath = path.resolve('models', file) const loadModel = async () => { const modelFile = await import(modelPath) const model = await modelFile.default(db, DataTypes) return model } acc.push(loadModel()) } return acc }, [])) await Promise.all(models.map(({ associate }) => associate(db.models))) return models } export default db
解决办法
问题根源是ESM的动态import在Windows下要求传入file://协议的URL,而path.resolve返回的是Windows本地路径(如D:\xxx\yyy.js),不符合ESM加载器的要求。
修改步骤:
- 导入
url模块的pathToFileURL方法,将本地路径转换为符合要求的file URL - 对生成的模型路径进行URL转换后再传入
import
修改后的models/index.js代码:
import { Sequelize, DataTypes } from 'sequelize' import fs from 'fs' import path from 'path' import { pathToFileURL } from 'url' // 新增导入 import config from '../config.cjs' const { DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, DB_PORT } = config const db = new Sequelize( DB_NAME, DB_USER, DB_PASSWORD, { port: DB_PORT, host: DB_HOST, dialect: 'postgres', underscored: true, underscoredAll: true, timestamps: false, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone } ) db.loadModels = async () => { const models = await Promise.all( fs.readdirSync(path.resolve('models')) .reduce((acc, file) => { if (file !== 'index.js') { const modelPath = path.resolve('models', file) const modelUrl = pathToFileURL(modelPath).href // 转换为file URL const loadModel = async () => { const modelFile = await import(modelUrl) // 使用URL导入 const model = await modelFile.default(db, DataTypes) return model } acc.push(loadModel()) } return acc }, [])) await Promise.all(models.map(({ associate }) => associate(db.models))) return models } export default db
这样修改后,Windows下的路径会被转换为file:///D:/Storage/backend/models/users.js这类合法的file URL,ESM加载器就能正常处理了,同时Mac/Unix系统的兼容性也不受影响。
内容的提问来源于stack exchange,提问作者Rocky
相关产品推荐
相关产品推荐

