drizzle-team / drizzle-kit-mirror

Docs and issues repository for drizzle-kit
290 stars 17 forks source link

Typescript and ESM not working together #55

Open Marsup opened 1 year ago

Marsup commented 1 year ago

Hi,

On a project using typescript and ESM mode, it seems that drizzle-kit generate doesn't resolve imports properly. For example, with such a layout:

| - schemas
\-- table1.ts
\-- table2.ts

If table1 imports table2, it needs to do it with import { table2 } from './table2.js'; as extensions are mandatory with ESM. But this will throw a Cannot find module ./table2.js when running drizzle-kit.

A temporary workaround is to remove all extensions for the migration generation, and then revert to make it work for runtime.

Btw, isn't drizzle-kit open-source?

StephenODea54 commented 1 year ago

Same thing for applying migrations:

import { migrate } from 'drizzle-orm/node-postgres/migrator';
import { db } from './db.js'; // MODULE_NOT_FOUND

migrate(db, { migrationsFolder: './src/db/migrations' })
    .then(() => {
        console.log('Migrations complete!');
        process.exit(0);
    })
    .catch(err => {
        console.error('Migrations failed!', err);
        process.exit(1);
    });
bestickley commented 1 year ago

@Marsup and @StephenODea54, I ran into this issue, but was able to get around it with: "moduleResolution": "bundler", in tsconfig.json. Thought I'd share as it might help.

AndriiSherman commented 1 year ago

Should be fixed in 0.19.0

arguiot commented 1 year ago

Error is still here:

drizzle-kit: v0.19.1
drizzle-orm: v0.27.0

No config path provided, using default 'drizzle.config.ts'
Reading config file '/Users/arguiot/Developer/Elva-Project/elva-service/drizzle.config.ts'
Reading schema files:
/Users/arguiot/Developer/Elva-Project/elva-service/src/db/schema.ts

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/arguiot/Developer/Elva-Project/elva-service/src/db/schema/messages' imported from /Users/arguiot/Developer/Elva-Project/elva-service/src/db/schema.ts
    at new NodeError (node:internal/errors:405:5)
    at finalizeResolution (node:internal/modules/esm/resolve:224:11)
    at moduleResolve (node:internal/modules/esm/resolve:837:10)
    at defaultResolve (node:internal/modules/esm/resolve:1035:11)
    at nextResolve (node:internal/modules/esm/hooks:726:28)
    at resolve (file:///Users/arguiot/Developer/Elva-Project/elva-service/node_modules/drizzle-kit/loader.mjs:24:10)
    at nextResolve (node:internal/modules/esm/hooks:726:28)
    at Hooks.resolve (node:internal/modules/esm/hooks:235:30)
    at MessagePort.handleMessage (node:internal/modules/esm/worker:168:24)
    at [nodejs.internal.kHybridDispatch] (node:internal/event_target:762:20) {
  code: 'ERR_MODULE_NOT_FOUND'
}
b0o commented 1 year ago

We're having an issue that I think is related to this. In 0.18.1, drizzle-kit push:mysql was working as expected, but after upgrading to 0.19.1, we're getting this error:

$ drizzle-kit push:mysql

drizzle-kit: v0.19.1
drizzle-orm: v0.27.0

No config path provided, using default 'drizzle.config.ts'
Reading config file '/path/to/my/proj/drizzle.config.ts'
Reading schema files:
/path/to/my/proj/lib/db/schema/foo.ts
/path/to/my/proj/lib/db/schema/bar.ts
/path/to/my/proj/lib/db/schema/qux.ts

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/path/to/my/proj/lib/db/types' imported from /path/to/my/proj/lib/db/schema/foo.ts
    at new NodeError (node:internal/errors:372:5)
    at finalizeResolution (node:internal/modules/esm/resolve:437:11)
    at moduleResolve (node:internal/modules/esm/resolve:1009:10)
    at defaultResolve (node:internal/modules/esm/resolve:1218:11)
    at resolve (file:///path/to/my/proj/node_modules/drizzle-kit/loader.mjs:24:10)
    at ESMLoader.resolve (node:internal/modules/esm/loader:580:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:294:18)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:80:40)
    at link (node:internal/modules/esm/module_job:78:36)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Schema file foo.ts (redacted):

import { InferModel } from 'drizzle-orm'
import { mysqlTable } from 'drizzle-orm/mysql-core'

import { primaryKey, email, now } from '../types'

export const foos = mysqlTable(
  'foos',
  {
    id: primaryKey('id'),
    email: email('email').notNull(),
    createdAt: now('created_at'),
  },
)

export type Foos = InferModel<typeof foos>
export type NewFoo = InferModel<typeof foos, 'insert'>

The ../types.ts file contains some helpers/custom types we use across our schemas.

rphlmr commented 1 year ago

Try the @win tag for drzzle-kit.

> drizzle-kit generate:pg

drizzle-kit: v0.19.2
drizzle-orm: v0.27.0

No config path provided, using default 'drizzle.config.ts'
Reading config file '/Users/rphlmr/workspaces/test/drizzle.config.ts'
TypeError: r.includes is not a function
    at u (file:///Users/rphlmr/workspaces/test/node_modules/@esbuild-kit/esm-loader/dist/index.js:1:2109)
    at nextResolve (node:internal/modules/esm/loader:163:28)
    at resolve (file:///Users/rphlmr/workspaces/test/node_modules/drizzle-kit/loader.mjs:35:10)
    at nextResolve (node:internal/modules/esm/loader:163:28)
    at ESMLoader.resolve (node:internal/modules/esm/loader:842:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:77:40)
    at link (node:internal/modules/esm/module_job:76:36)

My ts config:

{
  "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
  "compilerOptions": {
    "lib": ["DOM", "DOM.Iterable", "ES2022"],
    "types": ["vitest/globals"],
    "isolatedModules": true,
    "esModuleInterop": true,
    "jsx": "react-jsx",
    "module": "CommonJS",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "target": "ES2022",
    "strict": true,
    "allowJs": true,
    "forceConsistentCasingInFileNames": true,
    "baseUrl": ".",
    "paths": {
      "~/*": ["./app/*"]
    },
    "skipLibCheck": true,
    // Remix takes care of building everything in `remix build`.
    "noEmit": true
  }
}
timfee commented 1 year ago

Try the @win tag for drzzle-kit.

@win produces a different error in esbuild:

node:internal/process/esm_loader:46
      internalBinding('errors').triggerUncaughtException(
                                ^
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@esbuild-kit/esm-loader' imported from /Users/timfee/Sites/timepage/
Did you mean to import @esbuild-kit+esm-loader@2.5.5/node_modules/@esbuild-kit/esm-loader/dist/index.js?

@latest [0.19.1] fails where it has been previously:

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/timfee/Sites/timepage/db/auth/users' imported from /Users/timfee/Sites/timepage/db/auth/keys.ts
steve-taylor commented 1 year ago

@Marsup and @StephenODea54, I ran into this issue, but was able to get around it with: "moduleResolution": "bundler", in tsconfig.json. Thought I'd share as it might help.

It's not always an acceptable solution. I have a separate package just for my Drizzle schema that compiles to JavaScript using tsc, which doesn't do any transformation on filenames being imported. This is IMHO the right way to compile TypeScript when targeting Node.js (i.e. not using Babel and/or a bundler).

If this was open source, I'd be more than happy to create a pull request to fix this.

steve-taylor commented 12 months ago

After a bit of rubber-ducking on the Drizzle Discord server, I found a workaround. I changed my drizzle.config.json from this

{
  "schema": ["./src/core/tables.ts", "./src/cms/tables.ts"],
  "out": "./migrations"
}

to this

{
  "schema": ["./dist/core/tables.js", "./dist/cms/tables.js"],
  "out": "./migrations"
}

which means I have to ensure my schema source is transpiled before running drizzle-kit.

This is a reasonable workaround for me. Note: My schema is in a library package, not an app, because it's shared between multiple apps.

a7ul commented 10 months ago

After stuggling a bit, found a solution that works with the ts files as is.

I run drizzle-kit like so using tsx

bunx tsx ./node_modules/.bin/drizzle-kit generate:pg

or

npx tsx ./node_modules/.bin/drizzle-kit generate:pg

Now its able to read ts files that have esm style imports

Tronikelis commented 10 months ago

After stuggling a bit, found a solution that works with the ts files as is.

This does not work for me as tsx requires JS files and .bin/* files are scripts, so currently I am migrating like this:

npx tsx ./node_modules/drizzle-kit/bin.cjs generate:pg

ashishpandey001 commented 10 months ago

this is also a problem if I need to use top-level await within drizzle.config.ts. Some use cases could be getting db credentials asynchronously via an async method call.

StepanMynarik commented 10 months ago

Same issue here. Ended up using @steve-taylor workaround.

xeho91 commented 9 months ago

I'm going to add the reference to track answer to the question why drizzle-kit isn't open-sourced yet: https://github.com/drizzle-team/drizzle-orm/issues/580

So, we can only use CJS for now. Any ESM features not supported by ESM (such as top-level await, importing ESM packages in config) will not work yet, and we can't contribute by ourselves to resolve this issue.

Hebilicious commented 8 months ago

A workaround that worked flawlessly for me is to use bun to run any drizzle kit related script.

k00k commented 7 months ago

I just ran into this issue because I'm trying to get Drizzle working in an AdonisJS application. AdonisJS has its own env module and when importing it (ESM) and trying to use drizzle-kit for either migrations or studio, I get: Error: Cannot find module '/home/steve/Development/tpg-web/start/env.js'

Here's my drizzle.config.ts:

import type { Config } from 'drizzle-kit'
import env from '#start/env'
const url = env.get('DATABASE_URL')
const authToken = env.get('DATABASE_AUTH_TOKEN')

export default {
  schema: './database/schema/users.ts',
  out: './database/migrations',
  driver: 'turso',
  dbCredentials: {
    url,
    authToken,
  },
} satisfies Config

That env file that I'm importing on line 2 is a .ts file like so:

import { Env } from '@adonisjs/core/env'

export default await Env.create(new URL('../', import.meta.url), {
  NODE_ENV: Env.schema.enum(['development', 'production', 'test'] as const),
  PORT: Env.schema.number(),
  DATABASE_URL: Env.schema.string(),
  DATABASE_AUTH_TOKEN: Env.schema.string(),

.......
MC-Mihai commented 6 months ago

My solution is to have a schema file which imports each schema defined throughout the application using commonJs and export it again.

drizzle/schema.ts

export * from 'src/path/to/model/schema-1.model';
export * from 'src/path/to/model/schema-2.model';

Then in the configuration for the schema I use this file instead.

drizzle/config.ts

export default {
  schema: './drizzle/schema.ts',
  out: './drizzle/migration',
  ...
} satisfies Config

It is annoying that I get errors while modifying the file because it doesn't match the project configuration, but I don't compile it in the project and it's just one file. So far it works even when using ESM. You also need to keep the imports up to date in the schema file, which in my case is a "replicated" file with lazily modified imports to not explicitly import everything again for the sake of simplicity...

Charioteer commented 6 months ago

Hey @AndriiSherman (and maybe @AlexBlokh?),

I know there are countless other things you are probably working on right now but nevertheless this issue is open for more than 9 months without a true fix. The workarounds listed here work more or less but as soon as you have a project structure like, for example, this:

db/
├─ enums/
│  ├─ myEnum.ts
├─ schemas/
│  ├─ mySchema.ts

And import a Postgres enum defined in myEnum.ts in mySchema.ts like so:

import {
  bigserial,
  pgTable
} from 'drizzle-orm/pg-core'

import myEnum from '../enums/myEnum.js' // ESM import -> file ending ".js" although having a ".ts" file

export const mySchema = pgTable('my_schema', {
  id: bigserial('id', { mode: 'bigint' }),
  enum: myEnum('enum')
})

It breaks the enum discovery during e.g. drizzle-kit push:pg leading to missing enum generations in the generated SQL. This still happens when using tsx as suggested by @a7ul and @Tronikelis.

Since, unfortunately, drizzle-kit is (IMHO) still not open source, it's hard to contribute or help with this issue which is why I reached back to you.

Thanks in advance and thanks in general for the time you invest in the open source commmunity!

mpellegrini commented 5 months ago

@Charioteer Is it possible that you don't have ./db/enums/myEnum.ts enumerated in your drizzle.config.js file?

https://orm.drizzle.team/kit-docs/config-reference#schem

I have a similar situation and getting the enum to recognize it. But only when using the workaround by @Tronikelis.

Vorelli commented 1 month ago

After a bit of rubber-ducking on the Drizzle Discord server, I found a workaround. I changed my drizzle.config.json from this

{
  "schema": ["./src/core/tables.ts", "./src/cms/tables.ts"],
  "out": "./migrations"
}

to this

{
  "schema": ["./dist/core/tables.js", "./dist/cms/tables.js"],
  "out": "./migrations"
}

which means I have to ensure my schema source is transpiled before running drizzle-kit.

This is a reasonable workaround for me. Note: My schema is in a library package, not an app, because it's shared between multiple apps.

@steve-taylor Thanks for the fix!! Doing this, but targeting my dist folder and globbing on .js files:

import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  dialect: 'postgresql',
  schema: './dist/schema/**/*.js',
  out: '../drizzle',
});