Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to use file path for loading entities and Entity Load Order #7516

Closed
thxmike opened this issue Mar 30, 2021 · 7 comments · Fixed by #8536
Closed

Unable to use file path for loading entities and Entity Load Order #7516

thxmike opened this issue Mar 30, 2021 · 7 comments · Fixed by #8536

Comments

@thxmike
Copy link

thxmike commented Mar 30, 2021

Issue Description

Error when using TypeORM, and Typescript with folder path

Expected Behavior

As reported here
https://stackoverflow.com/questions/66735359/error-when-using-typeorm-and-typescript-with-folder-path
Entities should load without error.
Currently when using the following tsconfig

{
  "compilerOptions": {
    "target": "ES2020",
    "module":"ES2020",
    "esModuleInterop": true,
    "moduleResolution": "node",
    "strict": true,
    "declaration": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "outDir": "bin",
    "types": ["node"],
    "typeRoots": ["../node_modules/@types"],
    "sourceMap": true
  },
  "include": ["**/*.ts"],
  "exclude": ["tests", "node_modules", "lib", "bin", "dist"]
}

and package.json

{
  "name": "test-api",
  "version": "0.0.12",
  "description": "...",
  "license": "ISC",
  "author": "Me",
  "main": "server.js",
  "type": "module",
  "scripts": {
    "start": "node bin/server.js",
    "build": "npm i && tsc",
    "lint": "./node_modules/.bin/eslint --ignore-path .gitignore .",
    "test": "echo \"Error: currently no test specified\""
  },
  "dependencies": {
    "express": "^4.17.1",
    "mssql": "^6.3.1",
    "reflect-metadata": "^0.1.13",
    "syswide-cas": "^5.3.0",
    "typeorm": "^0.2.31",
    "uuid-mongodb": "^2.4.2"
  },
  "devDependencies": {
    "@types/node": "^14.14.35"
  }
}

I get the following error when loading the entities with a string file path - ${process.cwd()}/bin/models/entities/*.js

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: ...\Projects\orders-api\bin\models\entities\Address.js
require() of ES modules is not supported.
require() of ...\Projects\orders-api\bin\models\entities\Address.js from ...\Projects\orders-api\node_modules\typeorm\util\DirectoryExportedClassesLoader.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename Address.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from ...
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1080:13)
at Module.load (internal/modules/cjs/loader.js:928:32)
at Function.Module._load (internal/modules/cjs/loader.js:769:14)
at Module.require (internal/modules/cjs/loader.js:952:19)
at require (internal/modules/cjs/helpers.js:88:18)
Instead rename Address.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from C:\Users\mrivera\Projects\orders-api\package.json.

I have tried different combinations of the tsconfig.json but no change. 

I load my connection options as follows

return createConnection({
name: "default",
type: "mssql",
host: this._server,
username: this._user_name,
password: this._password,
database: this._database,
extra: true,
options: {
isolation: "READ_UNCOMMITTED",
},
entities: [
${this._entity_path}/entities/*.js
],
})

Here is an example of one of the entities I use

import { Column, Entity, Index, OneToMany, PrimaryGeneratedColumn } from 'typeorm';

import { Patient } from './Patient.js';
import { PatientNextOfKin } from './PatientNextOfKin.js';

@Index("PK__Address__3213E83F7240D5FD", ["id"], { unique: true })
@entity("Address", { schema: "Orders" })
export class Address {
@PrimaryGeneratedColumn({ type: "int", name: "id" })
id!: number;

@column("nvarchar", { name: "line", length: 500 })
line!: string;

@column("nvarchar", { name: "line2", length: 100 })
line2!: string;

@column("nvarchar", { name: "city", length: 100 })
city!: string;

@column("nvarchar", { name: "state", length: 2 })
state!: string;

@column("nvarchar", { name: "zip", length: 10 })
zip!: string;

@OneToMany(() => Patient, (patient) => patient.address)
patients!: Patient[];

@OneToMany(
() => PatientNextOfKin,
(patientNextOfKin) => patientNextOfKin.address
)
patientNextOfKins!: PatientNextOfKin[];

}


I do not use a ormconfig.json since I cannot persist this data in my repo as these configuration values are load during runtime.

### Steps to Reproduce
tsc
node ..\bin\server.js


### Additional Context

As a work around, I collect each entity in an array and pass it into the connection options. However, I run into another issue which the runtime is unable to load the entities in the correct order which results in another runtime error

> file:///C:/Users/.../Projects/my-api/bin/models/entities/entity1.js:29 __metadata("design:type", Entity2) ^
> 
> ReferenceError: Cannot access 'Entity2' before initialization at file:///C:/Users/.../Projects/my-api/bin/models/entities/entity1.js:29:31 at ModuleJob.run (internal/modules/esm/module_job.js:152:23) at async Loader.import (internal/modules/esm/loader.js:166:24) at async Object.loadESM (internal/process/esm_loader.js:68:5)
>  and is documented here. 

https://stackoverflow.com/questions/66715166/referenceerror-with-typeorm-entity-and-onetomany

### Relevant Database Driver(s)
mssql
### Are you willing to resolve this issue by submitting a Pull Request?

Yes if I knew where to look.

<!--
  Remember that first-time contributors are welcome! 🙌
-->

- [ ] Yes, I have the time, and I know how to start.
- [ ] Yes, I have the time, but I don't know how to start. I would need guidance.
- [ ] No, I don't have the time, although I believe I could do it if I had the time...
- [ ] No, I don't have the time and I wouldn't even know how to start.


<!--
  👋 Have a great day and thank you for the bug report!
-->
@kevin-lindsay-1
Copy link
Contributor

kevin-lindsay-1 commented Apr 8, 2021

I transpile my ts to a ./build/ folder, and i keep my entities in ./src/db/entities/.

Here's my config for entities which works fine:

createConnection({
  ...,
  entities: ['build/db/entities/*.js']
})

@thxmike
Copy link
Author

thxmike commented Apr 14, 2021

Yes, in my post, I transpile to the bin directory, the "this._entity_path" variable includes bin in the path. i.e. bin/models/entities. This can be see in the error messages

@bartdorsey
Copy link

So this is happening because you are trying to output ES modules from the typescript compilation step, and down inside of DirectoryExportedClassesLoader.js It does uses require() to dynamically load the modules.

I'm not sure if there's an easy fix for TypeORM here. If they switch to await import() instead of require() for the dynamic loading of the entities, then it'll break for everyone NOT using ES modules in Node.

Unless it could do some introspection and check if require exists, and if it does, use the require function, else use the await import function.

@chetrit
Copy link

chetrit commented Sep 19, 2021

Same problem here, with an hybrid codebase (js/ts), this problem seems inevitable when updating ts-node to versions 10.x, I don't have any problem with ts-node 9.1.1 though.

@ChibiBlasphem
Copy link

ChibiBlasphem commented Sep 28, 2021

This is pretty much problematic for any app which is on node 14 and higher and for which the runtime is fully modular.

EDIT: by the way import() syntax is supported for commonjs and ESModule and should not break anything as stated here (https://nodejs.org/api/esm.html#esm_import_expressions)

@cx690
Copy link

cx690 commented Nov 8, 2021

I meet the same problem,and I regist classes by this way.

import path from 'path';
import { fileURLToPath } from 'url';
import { createConnection } from 'typeorm';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const js_files = fs.readdirSync(path.resolve(__dirname, './entities')).filter((f) => {
    return f.endsWith('.js') || f.endsWith('.ts');
});//find all js or ts in entities dir

function* importSth() {
    for (const f of js_files) {
        yield import('./entities/' + f).then((res) => Object.values(res));//import all of them classes
    }
}
const entities = (await Promise.all([...importSth()])).flat();
createConnection({
        type: 'mysql',
        database: 'index',
        username: 'root', 
        password: '123456', 
        host: 'localhost', 
        entities, //regist
})

@justin-echternach
Copy link

try ... "allowSyntheticDefaultImports": true, in tsconfig.json

giladgd added a commit to giladgd/typeorm that referenced this issue Jan 18, 2022
When TypeORM tries to load an entity file or a connection config file, it will determine what is the appropriate module system to use for the file and then `import` or `require` it as it sees fit.

Closes: typeorm#7516
Closes: typeorm#7159
@giladgd giladgd mentioned this issue Jan 18, 2022
7 tasks
pleerock pushed a commit that referenced this issue Jan 31, 2022
* feat: support importing TypeORM in esm projects

Closes: #6974
Closes: #6941

* bugfix: generate index.mjs directly out of commonjs exports

The new implementation generates ESM exports directly out of the commonjs exports, and provides a default export to maintain compatability with existing `import`s of the commonjs implementation

* feat: support loading ESM entity and connection config files

When TypeORM tries to load an entity file or a connection config file, it will determine what is the appropriate module system to use for the file and then `import` or `require` it as it sees fit.

Closes: #7516
Closes: #7159

* fix: adapt ImportUtils.importOrRequireFile tests to older version of nodejs

* fix: improved importOrRequireFile implementation

* feat: add solution to circular dependency issue in ESM projects

* docs: added FAQ regarding ESM projects

* chore: add `"type": "commonjs"` to package.json

* style

* docs: improve `ts-node` usage examples for CLI commands in ESM projects

* feat: add support for generating an ESM base project

* refactor: renamed `Related` type to `Relation`

* docs: added a section in the Getting Started guide regarding the `Relation` wrapper type in ESM projects

* docs: improved documentation of the `Relation` type

* docs: improved documentation of the `Relation` type

* docs: added ESM support to the list of TypeORM features
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants