Expo+Drizzle ORM/Convex报错:Cannot use import statement outside a module
问题背景
在Expo项目中搭配Expo SQLite使用Drizzle ORM时,执行npx drizzle-kit generate出现报错,提示react-native模块中的import语句无法在CommonJS环境中运行。后续使用Convex DB执行npx convex dev时也出现完全相同的错误,且Convex命令最初可正常运行,后续突发问题。
Drizzle配置文件drizzle.config.ts内容:
import { Config } from "drizzle-kit" export default { schema: "./lib/db/schema", out: "./lib/db/migrations", dialect: "sqlite", driver: "expo", } satisfies Config
报错日志:
No config path provided, using default 'drizzle.config.ts' Reading config file '/Users/jay/Documents/personal/Code/Expo/pretty-budgeter/drizzle.config.ts' /Users/jay/Documents/personal/Code/Expo/pretty-budgeter/node_modules/react-native/index.js:14 import typeof ActionSheetIOS from './Libraries/ActionSheetIOS/ActionSheetIOS'; ^^^^^^ SyntaxError: Cannot use import statement outside a module at wrapSafe (node:internal/modules/cjs/loader:1281:20) at Module._compile (node:internal/modules/cjs/loader:1321:27) at Module._extensions..js (node:internal/modules/cjs/loader:1416:10) at newLoader (/Users/jay/Documents/personal/Code/Expo/pretty-budgeter/node_modules/drizzle-kit/bin.cjs:14724:13) at Object.newLoader (/Users/jay/Documents/personal/Code/Expo/pretty-budgeter/node_modules/drizzle-kit/bin.cjs:14724:13) at extensions..js (/Users/jay/Documents/personal/Code/Expo/pretty-budgeter/node_modules/drizzle-kit/bin.cjs:17252:28) at Module.load (node:internal/modules/cjs/loader:1208:32) at Module._load (node:internal/modules/cjs/loader:1024:12) at Module.require (node:internal/modules/cjs/loader:1233:19) at require (node:internal/modules/helpers:179:18
已知信息:配置符合Drizzle官方文档要求,在项目外生成迁移文件再导入可正常使用。
错误原因
核心问题是CLI工具(Drizzle Kit/Convex)以CommonJS模式运行,但加载到了项目中的ES模块代码。当CLI解析你的schema文件时,间接引入了react-native模块,而react-native使用ES模块的import语法,CommonJS加载器无法识别,因此触发语法错误。
解决办法
1. 彻底隔离schema与Expo依赖
这是最稳妥的方案:将数据库schema做成纯结构定义文件,完全不引入任何Expo/React Native相关代码。需要使用Expo SQLite的逻辑,单独拆分到运行时文件中。
示例:
lib/db/schema.ts(纯数据库结构,无Expo依赖):
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; export const expenses = sqliteTable("expenses", { id: integer("id").primaryKey({ autoIncrement: true }), amount: integer("amount").notNull(), description: text("description"), createdAt: text("created_at").default("CURRENT_TIMESTAMP"), });
lib/db/db.ts(Expo运行时专用,负责连接数据库):
import { drizzle } from "drizzle-orm/expo-sqlite"; import { openDatabaseSync } from "expo-sqlite"; import * as schema from "./schema"; const dbInstance = openDatabaseSync("budget.db"); export const db = drizzle(dbInstance, { schema });
这样Drizzle Kit生成迁移时只会加载纯schema文件,不会触发react-native的模块加载逻辑。
2. 调整项目的模块类型
将项目切换为ES模块模式,让Node.js使用ES模块加载器处理所有文件:
- 在项目根目录的
package.json中添加"type": "module":
{ "name": "pretty-budgeter", "type": "module", // 其他项目配置... }
- 将
drizzle.config.ts重命名为drizzle.config.mjs,确保语法符合ES模块规范:
import { Config } from "drizzle-kit"; export default { schema: "./lib/db/schema", out: "./lib/db/migrations", dialect: "sqlite", driver: "expo", } satisfies Config;
3. 用环境变量强制CLI启用ES模块
执行CLI命令时,通过NODE_OPTIONS参数指定启用ES模块支持:
# Drizzle生成迁移命令 NODE_OPTIONS="--experimental-modules --es-module-specifier-resolution=node" npx drizzle-kit generate # Convex启动开发服务命令 NODE_OPTIONS="--experimental-modules --es-module-specifier-resolution=node" npx convex dev
4. 清理依赖缓存(针对Convex突发错误)
如果Convex之前可正常运行,突然报错,大概率是依赖缓存异常导致:
# 删除node_modules和包管理器锁文件 rm -rf node_modules rm package-lock.json # 使用yarn则删除yarn.lock,pnpm则删除pnpm-lock.yaml # 重新安装依赖 npm install # 或 yarn install / pnpm install
内容的提问来源于stack exchange,提问作者Jay Sloan

