You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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加载器的要求。

修改步骤:

  1. 导入url模块的pathToFileURL方法,将本地路径转换为符合要求的file URL
  2. 对生成的模型路径进行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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 11:54:14