francescov1 / mongoose-tsgen

A plug-n-play Typescript generator for Mongoose.
103 stars 24 forks source link

Problem generating types for .js file #46

Closed ErnestBrandi closed 3 years ago

ErnestBrandi commented 3 years ago

Hi, when I try to generate the types from my models using npx mtgen ./lib/models/ --js, I get the following error SyntaxError: Cannot use import statement outside a module. Any idea what could cause this ? I can provide any information requested about my setup.

Here is a model sample

import mongoose from "mongoose";
const Schema = mongoose.Schema;

/* Subdocument */
const Feedback = new Schema({
  /* Misc fields */
});

export default mongoose.models.Feedback || mongoose.model("Feedback", Feedback);
francescov1 commented 3 years ago

Hi @ErnestBrandi,

More info about the setup would be great (general folder structure, mongoose version, etc.). Can provide more assistance then!

Note that we are starting to remove support for JS since most of the generator now depends on using TS compiler libraries, so I would recommend moving off of the --js option (if possible of course) option. This will also improve your generated models with better types for things like Mongoose methods & statics.

ErnestBrandi commented 3 years ago

Hi, switching to .ts files does indeed get rid of that error. A few things though :

francescov1 commented 3 years ago

Hi, switching to .ts files does indeed get rid of that error. A few things though :

  • if there is a mistake in the mongoose model, it will just print RangeError: Maximum call stack size exceeded; it would be great to know where it failed,

What sort of mistake? If you could provide a few examples I'd love to improve this!

  • I have a subdocument isolated in a separated file which also is also in my models folder, it triggers the following message A module was found at {path}.ts, but no new exported models were found. If this file contains a Mongoose schema, ensure it is exported and its name does not conflict with others. and as a result, no associated types are generated. This seems to limit code splitting.

When this warning is printed out, it indicates that the file did not have any Mongoose schemas exported, therefore there is no way for the generator to parse the Mongoose schema. The generator works by loading each file into memory using a require statement, so if the Mongoose schema is not exported from that file there is no way to generate a type for it.

To fix this, you simply need to export any schemas that you want typed.

Note that in earlier versions, the generator would throw an error in this situation rather than simply printing a warning to the console. If you are experiencing this, ensure to update to the latest version of mongoose-tsgen

shahidcodes commented 3 years ago

I am also facing this problem, my model looks like this

// user.ts
import mongoose from 'mongoose';

const addressSchema = new mongoose.Schema(
  {
    street: String,
    city: String,
    zip: String,
  },
  { _id: false }
);

const schema = new mongoose.Schema({
  customerId: Number,
  billingType: String,
  login: String,
  password: {
    type: String,
    select: false,
  },
  category: String,
  name: String,
  email: String,
  billingEmail: String,
  phone: String,
  address: addressSchema,
  createdAt: Date,
  lastOnlineAt: Date,
});

export const Users = mongoose.model('User', schema);

the error I get is


Generating mongoose typescript definitions... !
    SyntaxError: Cannot use import statement outside a module
 ERROR  Command failed with exit code 2.```
shahidcodes commented 3 years ago

Found the problem - my tsconfig.json had "module": "esnext", since I am using next.js, Change from

"module": "esnext",

to

 "module": "commonjs",

worked.

ErnestBrandi commented 3 years ago

@shahidcodes just want to point out that esnext has nothing to do with Next.js.

@francescov1 so the generator does not work for "external nested path" ? For instance I cannot have

const nestedPath = {
    strField: { type: String; }
    dateField : { type: Date; } 
}
export { nestedPath };

in a separate file, that I would include in several schemas as a nested path (https://mongoosejs.com/docs/subdocs.html).

About the mistake in mongoose schema, it could be

const new Schema({
  field: {
    type: String,
    default: ["foo", "bar"], // this is wrong
  }
}); 

can you detect there is something wrong with the schema and print a more descriptive error message ?

Also (different topic), would you consider adding an option to make every field with a default option exported as required properties ? Indeed, I have omitted the required option for most of the fields having a default option as the value ends up being added anyway.

francescov1 commented 3 years ago

Hey @ErnestBrandi

Thanks for the info. Having an external nested path like that should actually work, since the generator will load each schema into memory to read its data (thus it uses the computed mongoose javascript object). In your case, if nestedPath is a subdocument that you use in other schemas, you should define it as a schema so that the generator can properly parse it.

If you just want the ParentNestedPathDocument type generated (type for the schema below, but specifically as a child doc of parentSchema) then you can just export the schema directly.

nestedPath.ts


const NestedPathSchema = new Schema({
strField: { type: String; },
dateField : { type: Date; } 
})

// notice we dont initialize a model here export const NestedPathSchema;


and re-use here
> parentSchema.ts
```typescript
import { NestedPathSchema } from "./nestedPath.ts"

const ParentSchema = new Schema({
  nestedPathSubdocArray: [NestedPathSchema]
}); 

export const Parent = mongoose.model("Parent", ParentSchema);

if you also want to generate an explicit NestedPathDocument type, then initialize a model with the schema:

nestedPath.ts


const NestedPathSchema = new Schema({
strField: { type: String; },
dateField : { type: Date; } 
})

export const NestedPath = mongoose.model("NestedPath", NestedPathSchema);


and reference the schema like this:
> parentSchema.ts
```typescript
import { NestedPath } from "./nestedPath.ts"

const ParentSchema = new Schema({
  nestedPathSubdocArray: [NestedPath.schema]
}); 

export const Parent = mongoose.model("Parent", ParentSchema);

can you detect there is something wrong with the schema and print a more descriptive error message ?

I'll look into this, would definitely be useful.

Also (different topic), would you consider adding an option to make every field with a default option exported as required properties ? Indeed, I have omitted the required option for most of the fields having a default option has a value ends up being added anyway.

Yes, this is a great idea and makes much more sense. Will add in the next version 😊

ErnestBrandi commented 3 years ago

Ok, thanks for the replies. I'll close the issue and post in separate threads from now if I have anymore feedback :)

francescov1 commented 3 years ago

Also (different topic), would you consider adding an option to make every field with a default option exported as required properties ? Indeed, I have omitted the required option for most of the fields having a default option as the value ends up being added anyway.

Hey @ErnestBrandi,

I started to implement this, but ultimately decided against it after doing some testing. Properties on Mongoose schemas with default set are technically still optional; a user can later unset them (ie user.firstName = undefined) which makes the typing incorrect. For this reason, it wouldn't make sense to type it as required.

The correct solution is to include both default and required options in your case, this way you can ensure that both Typescript and Mongoose enforce the required option.