Giter VIP home page Giter VIP logo

joi-to-typescript's People

Contributors

adam-charlton avatar andys8 avatar aron123 avatar averagehelper avatar cmaster11 avatar dependabot[bot] avatar gaurav5430 avatar harrynowl avatar hzhou0 avatar imjared avatar infinitysamurai avatar jrmurr avatar kalinkrustev avatar kammerjaeger avatar kjb085 avatar konstantinzelinsky avatar lixw1994 avatar loganrupe avatar lukeshay avatar mkubliniak avatar mrjono1 avatar stockson avatar toddtarsi avatar vamcs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

joi-to-typescript's Issues

valid( Joi.override, "string" )

Ran into an issue using Joi.override with AnySchema..valid()

demoSchema.ts

import Joi from "joi";

export default Joi.object({

	// OK:
	// foo: Joi.string().valid("bar").valid("baz")

	// OK:
	// foo: Joi.string().valid("bar").valid("baz", "qux")

	// NOT OK:
	foo: Joi.string().valid("bar").valid(Joi.override, "baz", "qux")

	// TypeError: value.replace is not a function
	// at toStringLiteral (\node_modules\joi-to-typescript\dist\main\utils.js:26:22)

})
.meta({ className: "demo" })

Test code used to confirm that the joi is valid:
testDemo.ts

import demo from "./demoSchema"

// confirm that joi is valid:
// yarn ts-node ./test/sandbox/testDemo.ts
{
	// should err
	const { value, warning, error } = demo.validate({ foo: "bar" })
	if (!error) throw new Error("should have errored")
} {
	// should not err
	const { value, warning, error } = demo.validate({ foo: "baz" })
	if (error) throw new Error("should not have errored")
}
console.log("done")

Generating data type based on custom type's base type?

We have some custom types we're using (Mainly to control the value format in a consolidated place)

An example:

export const Joi = Joi.extend(joi => {
    return {
      type: 'time',
      base: joi.string().pattern(/^\d{2}:\d{2}:\d{2}-\d{4}$/),
    };
  })
const UserSchema = Joi.object({
  name: Joi.string().required(),
  timeSubmitted: Joi.time().required()

})

But it's getting generated as this:

interface UserSchema {
  name: string;
  time: unknown;
}

Is there a way to generate time as a string since that's its base type?

Error exporting primitive types from schema file root

When exporting a labeled Joi schema that results in a primitive TypeScript type, joi-to-typescript generates an invalid output. This pattern is very useful when you want to reuse Joi schemas (e.g. an EmailSchema can be used by both UserSchema and CompanySchema).

To reproduce

export const EmailSchema = Joi.string()
  .email({ tlds: { allow: false } })
  .label('Email')

export const CompanySchema = Joi.object({
  email: EmailSchema
})

export const UserSchema = Joi.object({
  email: EmailSchema.required()
})

Expected behaviour

/**
 * This file was automatically generated by joi-to-typescript
 * Do not modify this file manually
 */

export type Email = string

export type Company = {
  email?: Email
}

export type User = {
  email: Email
}

Actual behaviour

/**
 * This file was automatically generated by joi-to-typescript
 * Do not modify this file manually
 */

string // Here is the issue, should be (type Email = string)

export type Company = {
  email?: Email
}

export type User = {
  email: Email
}

Labels with spaces produce invalid type names

With this schema:

export const RegisterAccountParams = Joi.object({
  name: Joi.string()
    .label("Company Name")
    .required(),
  phoneNumber: Joi.string()
    .label("Company Phone Number")
    .required(),
  customerEmail: Joi.string()
    .label("Customer Email")
    .required(),
  website: Joi.string()
    .label("Website")
});

this output is generated:

/**
 * This file was automatically generated by joi-to-typescript
 * Do not modify this file manually
 */

import { Customer Email, Company Name, Company Phone Number, Website } from '.';

export interface RegisterAccountParams {
  customerEmail: Customer Email;
  name: Company Name;
  phoneNumber: Company Phone Number;
  website?: Website;
}

The import statement is clearly incorrect. It seems to me that joi-to-typescript takes label to mean that we're referencing another named exported schema, which is not our intention.

Labels are handy for customizing the error messages that Joi throws on validation failures. Is there a way that this behaviour might be made configurable, or perhaps default to ignoring label modifiers on object properties?

To be clear, we expect an output similar to this one:

export interface RegisterAccountParams {
  customerEmail: string;
  name: string;
  phoneNumber: string;
  website?: string;
}

Converting schema marked with `className` meta property ignores schema extension

I noticed an unfortunate behavior when extending renamed schemas. Here is a small example.

With meta

Source schemas:

export const BaseSchemaBar = Joi.object({
    bar: Joi.number().required()
}).meta({ className: 'BaseSchemaDTO' });

export const BaseExtensionSchema = Joi.object({
    baz: BaseSchemaBar.keys({
        foo: Joi.boolean().required()
    })
}).meta({ className: 'BaseExtensionDTO' });

Resulted interfaces:

export interface BaseSchemaDTO {
    bar: number;
}

export interface BaseExtensionDTO {
    baz: BaseSchemaDTO;
}

As you can see, foo property is missing.

Without meta

Source schemas:

export const BaseSchemaBar = Joi.object({
    bar: Joi.number().required()
});

export const BaseExtensionSchema = Joi.object({
    baz: BaseSchemaBar.keys({
        foo: Joi.boolean().required()
    })
});

Resulted interfaces:

export interface BaseSchemaBar {
  bar: number;
}

export interface BaseExtensionSchema {
  baz: {
    bar: number;
    foo: boolean;
  };
}

Here all fields are fine.

feat: add ignoreFiles patterns (and ignore tests files by default)

Unit test files should be excluded from interfaces generation (ideally, by default in a future major version).

To avoid listing all of them, what about adding file patterns support to excludeFiles option (e.g. ['**/*.test.ts', '**/*.spec.ts']) or adding a separate excludeFilesPatterns` option to support patterns or regexp ?

Also, a similar includeFiles option (matching patterns) would be useful!

Handling of function type-

Nice module, though I was facing an issue while using the function type.
So while converting joi schema push: j.function().required() to typescript type using this module leads to
push?: (...args: any[]) => any;
whereas the correct output should be push: (...args: any[]) => any;

Basic use of .allow('') is not supported

According to Joi documentation using Joi.string().allow('') is valid and a type of string | '' would be expected.

Instead a type of '' is generated which is incorrect and does not allow both valid strings and empty strings to be used in the type

Add CLI interface

It would be nice if this tool had a simple CLI interface such as

$ npx joi-to-typescript --schema-dir ./schemas --output-dir ./types --sort-by-name

I use this great tool in many projects, and I usually end up wrapping it in a simple file which simply calls convertFromDirectory() with my directories as parameters, and adding that wrapper file seems a bit redundant really.

Would a PR for this be of interest? If so, any preference on CLI helpers - meow? Any thoughts on syntax? To me, the most relevant usage is to convert a directory of schema files and store the generated files in another directory, as per the example above. And options should be fairly straight-forward. Would this be sufficient or is there a need for e.g. converting a single schema file etc?

updated to 4.0.0 - logging LOTS of json to the console

Picked up the latest, and the output to console is tons of JSON. I made sure debug: false, but still all the output is in JSON.

{"type":"string","flags":{"description":"end segment of url for this page","presence":"required"},"rules":[{"name":"min","args":{"limit":1}},{"name":"max","args":{"limit":100}},{"name":"case","args":{"direction":"lower"}},{"name":"pattern","args":{"regex":"/^[a-z0-9]+(?:-[a-z0-9]+)*$/"}}],"metas":[{"className":"SlugValue"}]}
{"type":"string","rules":[{"name":"uri"}]}
{"type":"string","rules":[{"name":"uri"}],"metas":[{"className":"ImageValue"}]}
{"type":"string","rules":[{"name":"uri"}],"metas":[{"className":"ImageValue"}]}
{"type":"date","flags":{"format":"javascript"}}
{"type":"date","flags":{"format":"javascript"},"metas":[{"className":"DateValue"}]}
{"type":"date","flags":{"format":"javascript"},"metas":[{"className":"DateValue"}]}
{"type":"string"}
{"type":"string","metas":[{"className":"NoValueRecord"}]}

Expected Joi.allow(null) to only allow null values

Consder the following code:

export const ExampleSchema = Joi.object({
  a: Joi.allow(1).required(),
}).meta({ className: 'Example' });`

// This generates what I would expect on the TS interface side:

export interface Example {
  a: 1;
}

However when I try for only a null value it works differently:

export const ExampleSchema = Joi.object({
  a: Joi.allow(null).required(),
}).meta({ className: 'Example' });`

// generates

export interface Example {
  a: any | null;
}

Is this a bug or intended behavior? If it is intended is there a way I can define a property on a schema to always expect to be null?

Joi.Forbidden() should prevent property from being output in typescript

When Forbidden and/or Strip is used, the resulting typescript model should not have the entry defined in its model.

export const examplePOSTBodySchema = baseSchema
  .fork(['id'], (x) => x.forbidden().strip(true))
  .meta({className: 'examplePOSTBody'});

Here I am forking a base schema and trying to remove the id parameter. Unfortunately Forbidden is not supported so id is still output in the resulting model.

Option to generate readonly types from Joi schemas

Hey @mrjono1, really love the work you do on this project!

I'm just asking is there a way to add a option in the command to make the generated interfaces have readonly attributes? I'd say this is a good thing because enforcing immutability is a good thing in some cases. Probably an option like generateReadonlyAttributes: true ?

Example of this could be:

const userSchema = joi.object({
  name: joi.string().required(),
}).meta({ className: 'User' });

// Generated interface
interface User {
  readonly name: string;
}

Allow null doesn't work on array and object

Hi,

I'm not sure if you guys are aware of this issue/bug.

Joi:
name: Joi.string().allow(null).required

TypeScript:
name: string | null

This result is correct. However, when I use it with the array type, it doesn't generate | null

Joi:
imageUrls: Joi.array().items(Joi.string()).allow(null).required()

TypeScript:
imageUrls: string[]

It's supposed to be imageUrls: string[] | null
The same issue with object.

Bug when files in different directories have the same name.

When 2 schema files have same name in different directories, only the first one generates an interface file.

example :

- schemas
   - folder1
      -  mySchema.ts
      -  mySchema2.ts
   - folder2
      -  mySchema.ts
      -  mySchema3.ts

generates the following interfaces :

- interfaces
   - folder1
      -  mySchema.ts
      -  mySchema2.ts
   - folder2
      -  mySchema3.ts

=> interfaces/folder2/mySchema.ts is missing.

More flexible file system API

Hi there, first of all thanks for a great job so far!

The library works fine with a one directory that have only one directory with joi schemas
But it is not really comfortable otherwise, especially if you have dozen of joi schemas and complex structure in your project

What about breaking major changes in API? why not to use glob in search joi schema files?

const defaultOutputFile = (srcPath: string, fileName: string) =>  path.join(
     srcPath, 
    `${path.basename(fileName, '.ts')}.type.ts`
)

export function convertFromDirectory({
    schemaFile,
    outputFile = defaultOutputFile,
    // ...
}{
    schemaFile: string | string[];
    outputFile?: (scrPath: string, fileName: string) => string
    // ...
}) {
    // ... implementation
} 

Benefits
The usage is more flexible and fits more use cases.
Below old options can be dropped from API because it could be done by this new realisation

  1. schemaFileSuffix
 convertFromDirectory({
    schemaFile: './src/**/*.schema.ts',
    outputFile: (srcPath: string, fileName: string) => path.join(srcPath, fileName.replace('.schema', '')),
}})
  1. flattenTree
convertFromDirectory({
    schemaFile: './src/**/*.schema.ts',
    outputFile: (srcPath: string, fileName: string) => path.join('./src/types', fileName),
}})
  1. rootDirectoryOnly
convertFromDirectory({
    schemaFile: './src/*.schema.ts',
}})
  1. ignoreFiles can be be done by glob in a variety of ways out of box

Caveats
I am not sure about indexAllToRoot โ€” it could be hard to implement

What do you think? Will you accept such PR?

Meta className for Schemas using .concat() does not update

For version 2.0.1 when using a Joi schema that is created via .concat() and then adding a new className via .meta() the resulting type that is generated does not use the new className.

As an example:

Schema:

import Joi from 'joi';

export const A = Joi.object({
  a: Joi.string()
}).meta({ className: 'A' });

export const B = Joi.object({
  b: Joi.string()
}).meta({ className: 'B' });

export const AB = A
  .concat(B)
  .meta({ className: 'AB' });

What is generated:

/**
 * This file was automatically generated by joi-to-typescript
 * Do not modify this file manually
 */

export interface A {
  a?: string;
  b?: string;
}

export interface A {
  a?: string;
}

export interface B {
  b?: string;
}

What is expected:

/**
 * This file was automatically generated by joi-to-typescript
 * Do not modify this file manually
 */

export interface A {
  a?: string;
}

export interface B {
  b?: string;
}

export interface AB {
  a?: string;
  b?: string;
}

Redundant typing

I found an issue, which I think is related to #53

Starting Schema - schema/Base.ts

export const InteractionTypeSchema = Joi.string().valid("CREATE", "DELETE", "UPDATE")
    .meta({ className: 'InteractionType' });

export const InteractionSchema = Joi.object({
    companyId: Joi.string().required(),
    userId: Joi.string().required(),
    appVersion: Joi.string().required(),
    interactionType: InteractionTypeSchema,
})
    .meta({ className: 'Interaction' });

Generates - interfaces/Base.ts

export interface Interaction {
  appVersion: string;
  companyId: string;
  interactionType?: InteractionType | 'CREATE' | 'DELETE' | 'UPDATE';
  userId: string;
}

export type InteractionType = 'CREATE' | 'DELETE' | 'UPDATE';

I think interactionType should only reference InteractionType and not CREATE, DELETE, UPDATE

.rename() and .xor() schema methods are ignored

OS: Linux pop-os 5.19.0-76051900-generic
Node: v16.17.0
Joi: 17.6.0
Joi-to-typescript: 4.0.5

Hi there,

I have the following schema which is intended to require only one of the keys described in .xor(), throwing an error if neither one or both are present in the object. Additionally, if the key provided was the one suffixed with "_FILE", it should be renamed to remove that suffix.

const ConfigSchema = Joi.object({
  JWT_SECRET: Joi.string(),
  JWT_SECRET_FILE: Joi.string(),
  MONGODB_NAME: Joi.string(),
  MONGODB_NAME_FILE: Joi.string()
})
  .xor('JWT_SECRET_FILE', 'JWT_SECRET')
  .xor('MONGODB_NAME_FILE', 'MONGODB_NAME')
  .rename('JWT_SECRET_FILE', 'JWT_SECRET')
  .rename('MONGODB_NAME_FILE', 'MONGODB_NAME')

This works fine at runtime, and I can write a TypeScript interface to work with the validated schema exactly as I intend to through the rest of my code:

export interface IConfig {
  JWT_SECRET: string;
  MONGODB_NAME: string;
}

However, when I use joi-to-typescript the resulting interface contains the properties that I need to remove as well:

export interface IConfig {
  JWT_SECRET: string;
  JWT_SECRET_FILE: string;
  MONGODB_NAME: string;
  MONGODB_NAME_FILE: string;
}

Is a missing feature of joi-to-typescript? Or perhaps I'm not using Joi's .xor() and .rename() methods properly (although they seem to work fine)? For reference, I have setup an npm script to run joi-to-typescript as follows (as I do not have ts-node installed globally):

"scripts": {
  "types": "node_modules/.bin/ts-node src/schemas/types.ts"
}
// src/schemas/types.ts

import { convertFromDirectory } from 'joi-to-typescript';

convertFromDirectory({
  schemaDirectory: './src/schemas',
  typeOutputDirectory: './src/interfaces',
  defaultToRequired: true,
  ignoreFiles: ['types.ts'],
  debug: true
});

Schema directories containing index.ts causes issues in generated index.ts

When using convertFromDirectory, if the schema directory contains an index.ts file this results in the corresponding generated index.ts containing the TypeScript types defined directly inline rather than exporting via their generated files.

For example, without an index.ts in the schema directory, the generated index.ts correctly looks something like:

export * from './Artist';
export * from './ArtistList';
export * from './NewArtist';

However, if an index.ts exists in the schema directory, the generated index.ts becomes:

export interface Artist {
  ...
}
export interface ArtistList {
  ...
}
export interface NewArtist {
  ...
}

The generation of index.ts should be the same regardless of the presence of a schema index.ts

Using variables for schemas

Hello, wondering what's happening here with the [object Object] output:

demoSchema.ts

import Joi from "joi";

const foo = Joi.object({ bar: "baz" })

export const demoSchema = Joi.object({ foo })
	.meta({ className: "demo" })

emitted demo.ts

export interface demo {
  foo?: {
    bar?: [object Object] | 'baz';
  };
}

Do not convert index.ts in schemaDirectory [Question]

I would like to have an index.ts in the schemaDirectory that exports all Joi schemas without joi-to-typescript trying to convert it.

The reason for this is because an index.ts allows me to easily import the Joi schemas elsewhere in the code base without the need for one import statement per schema.

Error: Cannot find module 'src/schemas/index.ts'

Describe the bug
After updating from version 1.10.0 to 1.10.1 you get the following error :

`Running joi-to-typescript...
(node:3844) UnhandledPromiseRejectionWarning: Error: Cannot find module 'src/schemas/index.ts'
Require stack:

  • /Users/thomaskuhlmann/Projects/app/functions/node_modules/joi-to-typescript/dist/analyseSchemaFile.js
  • /Users/thomaskuhlmann/Projects/app/functions/node_modules/joi-to-typescript/dist/convertFilesInDirectory.js
  • /Users/thomaskuhlmann/Projects/app/functions/node_modules/joi-to-typescript/dist/index.js
  • /Users/thomaskuhlmann/Projects/app/functions/scripts/types.ts
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:815:15)
    at Function.Module._resolveFilename (/Users/thomaskuhlmann/Projects/app/functions/node_modules/tsconfig-paths/lib/register.js:75:40)
    at Function.Module._load (internal/modules/cjs/loader.js:667:27)
    at Module.require (internal/modules/cjs/loader.js:887:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.analyseSchemaFile (/Users/thomaskuhlmann/Projects/app/functions/node_modules/joi-to-typescript/dist/analyseSchemaFile.js:20:30)
    at Object.convertFilesInDirectory (/Users/thomaskuhlmann/Projects/app/functions/node_modules/joi-to-typescript/dist/convertFilesInDirectory.js:57:58)
    at Object.convertFromDirectory (/Users/thomaskuhlmann/Projects/app/functions/node_modules/joi-to-typescript/dist/index.js:95:62)
    at types (/Users/thomaskuhlmann/Projects/app/functions/scripts/types.ts:7:24)
    at Object. (/Users/thomaskuhlmann/Projects/app/functions/scripts/types.ts:23:1)
    (node:3844) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
    (node:3844) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.`

To Reproduce
Run:

import { convertFromDirectory } from "joi-to-typescript";

const types = async (): Promise<void> => {
  console.log("Running joi-to-typescript...");

  // Configure your settings here
  const result = await convertFromDirectory({
    schemaDirectory: "./src/schemas",
    typeOutputDirectory: "./src/interfaces",
    debug: true,
    defaultToRequired: true,
    indexAllToRoot: true,
  });

  if (result) {
    console.log("Completed joi-to-typescript");
  } else {
    console.log("Failed to run joi-to-typescript");
  }
};

types();

with version 1.10.1

Reverting back to the previous version solves the problem

Handle basic uses of `.when()`

I think we could support simple use cases of when like

Joi.object({
      type: Joi.boolean().valid(true, false),
      trueField: Joi.string().when('type', {
        is: true,
        then: Joi.required(),
        otherwise: Joi.forbidden()
      }),
      falseField: Joi.string().when('type', {
        is: false,
        then: Joi.required(),
        otherwise: Joi.forbidden()
      })
    })

this would support an object like { type: false, falseField: 'aStr' } and { type: true, trueField: 'aStr' } and would error on something like { type: false, trueField: 'shouldNotWork' }

Right now the lib would make a type like

{type: true | false; trueField: string; falseField: string} which is correct but could be better

Under the hood joi uses references to figure out what field to look at and generate a good validator. There can be a lot of runtime stuff references can use but i think simple sibling key references could be supported. Handling a reference to a grand parent object is probably too much at the moment.

I think if we support a simple case like this then fall back to what we do now if the reference/is condition is too complicated would be nice.

Idea: Joi should be in the peerDependencies section

I'm pretty sure that Joi should be a peer dependency instead of a normal dependency.

I haven't had a use case like this before so I will investigate if this is the case I may do a major version bump for this change.

Stop mutating Settings

A low priority task is to stop the mutation of appSettings/Settings and instead pass the mutated values as parameters

Nested objects that aren't arrays do not insert correctly

When generating types from the following schemas the nested object is not correctly referenced.

import Joi from 'joi';

export const BarSchema = Joi.object({
  id: Joi.number().required().description('Id').example(1)
}).label('Bar');

export const FooSchema = Joi.object({
  id: Joi.number().required().description('Id').example(1),
  bar: BarSchema.required().description('Bar')
}).label('Foo');

Results in the below, with object being used instead of Bar.

/**
 * This file was automatically generated by joi-to-typescript
 * Do not modify this file manually
 */

import { object } from '.';

/**
 * Bar
 */
export interface Bar {
  /**
   * id
   * Id
   */
  id: number;
}

/**
 * Foo
 */
export interface Foo {
  /**
   * bar
   * Bar
   */
  bar: object;
  /**
   * id
   * Id
   */
  id: number;
}

Using valid or allow on Joi.number() does not produce a number typed field

When generating a schema with a Joi.number() that has valid() or allow() the resulting type is not a number but assumed the valid values as the types of the field.

To Reproduce

export const TestSchema = Joi.object({
  'clean': Joi.number(),
  'valid': Joi.number().valid(0, 1),
  'allow': Joi.number().allow(2, 3)
}).label('Test');

Expected behavior

export interface Test {
  allow?: number;
  clean?: number;
  valid?: number;
}

Actual behavior

export interface Test {
  allow?: 2 | 3;
  clean?: number;
  valid?: 0 | 1;
}

Better handling of enums

Hey,
overall a very nice package!

I noticed an issue when using enums:

import Joi from "joi";

export const roleSchema = Joi.string()
	.valid("Admin", "User")
	.label("Role");

export const userSchema = Joi.object({
	firstName: Joi.string().required(),
	lastName: Joi.string().required(),
	role: roleSchema.required(),
}).label("User");

This will generate the following output:

export type Role = 'Admin' | 'User';

export interface User {
	firstName: string;
	lastName: string;
	role: Role | 'Admin' | 'User';
}

However role: Role would be enough and and more readable ;)
Regards

Extend interfaces when extending schemas

Hi!

I just realised that if you extend a schema, e.g.

const WorkspaceSchema = Joi.object({
 statistics:Joi.object({
     views: Joi.number()
     })
}).label('IWorkspace')

const ProjectSchema = WorkspaceSchema.keys({
  statistics:Joi.object({
    users:Joi.number()
    })
}).label('IProject')

You end up with two interfaces:

export interface IWorkspace {
  statistics: {
    views: number;
  };
}

and

export interface IProject {
  statistics: {
    users: number
   }
}

Wouldn't it make more sense to extend IProject with IWorkspace?

export interface IProject extends IWorkspace {
  statistics: {
    users: number
   }
}

Example script

Create an example script to run this package from the cli

Tried to parse a readme.md file

I have a Readme.md file in a directory with schemas and it attempted to parse the then crashed.

expected result is only parsing *.ts files

If label() was not supplied still chop off the word Schema

I haven't used .label() to defined the interface name. I would like this package to use the variable name and chop the word schema off it like it does for the file names

To Reproduce

export const UserSchema = Joi.object({});

Expected behavior

export interface User {}

Actual behavior

export interface UserSchema {}

Joi.ref is breaking

Schema:

const schema = Joi.object()
  .label('SignUp')
  .keys({
    password: Joi.string()
      .required()
      .description('The password of the authenticating user')
      .example('test-PASSWORD123'),
    repeatPassword: Joi.string()
      .required()
      .allow(Joi.ref('password'))
      .description('Repeat the password to ensure no typos')
      .example('test-PASSWORD123')
  });

Result:

joi-to-typescript/dist/main/utils.js:26
    return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
                     ^

TypeError: value.replace is not a function
    at toStringLiteral (joi-to-typescript/dist/main/utils.js:26:22)
    at joi-to-typescript/dist/main/parse.js:341:92
    at Array.map (<anonymous>)
    at parseStringSchema (joi-to-typescript/dist/main/parse.js:339:42)
    at parseHelper (joi-to-typescript/dist/main/parse.js:189:20)
    at parseSchema (joi-to-typescript/dist/main/parse.js:265:26)
    at joi-to-typescript/dist/main/parse.js:423:30
    at joi-to-typescript/dist/main/utils.js:13:27
    at Array.reduce (<anonymous>)
    at filterMap (joi-to-typescript/dist/main/utils.js:12:17)

Shape returned at this line:

{ ref: { path: [ 'password' ] } }

Is it possible for me to just skip all processing on this field using a meta flag or something?

Error when sourcing from a directory from another ES module package

I want to be able to build from schemas in another package (separate from the package where the destination interfaces are built and written to). Currently this doesn't seem to be possible as the other package has "type": "module" in its' package.json file.

The resulting error is thus:

(node:39837) UnhandledPromiseRejectionWarning: Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/some-other-package/src/schemas/OneSchema.ts
require() of ES modules is not supported.
require() of /Users/some-other-package/src/schemas/OneSchema.ts from /Users/my-types-package/node_modules/joi-to-typescript/dist/analyseSchemaFile.js is an ES module file as it is a .ts file whose nearest parent package.json contains "type": "module" which defines all .ts files in that package scope as ES modules.
Instead change the requiring code to use import(), or remove "type": "module" from /Users/some-other-package/package.json.

Removing "type": "module" from said package does indeed squash the error, but that is sub-optimal as this package is required by others and should be an ES module package.

Is there any way of resolving this?

My build code is basically the same as your example, except I've plonked your source schemas into the "other" package:

const result = await convertFromDirectory({
    schemaDirectory: '../some-other-package/src/schemas',
    typeOutputDirectory: './lib/built',
    debug: true
  })

Thanks in advance for any help.

Incorrect generated types for allow modifier

The joi schema:

const ChainInfo = Joi.object({
  id: Joi.string().optional(),
  host: Joi.string()
    .uri({ scheme: ['http', 'https'] })
    .required()
    .allow('none'),
})
  .options(options)
  .meta({ unknownType: 'string', className: 'ChainInfoType' });

The generated type:

export interface ChainInfoType {
  host: 'none';
  id?: string;
}

Expected type:

export interface ChainInfoType {
  host: 'string';
  id?: string;
}

Contributors documentation guide

Hello @mrjono1, Thanks again for contributing joi-to-typescript to the community.

Do you have documentation about the generation flows and data structures that contributors can learn from when they work on a contribution? I saw in the readme the contribution section but it doesn't deal with documentations.

Thanks
Eran

How do I add complex types to `unknownType`?

When creating a dictionary that has a more complex type than primitives, for example another schema, how can I correctly add that type reference?

For example, my simplified case below:

export const Item = Joi.object({ id: Joi.string().required() })
export const Items = Joi.object().unknown(true).meta({ unknownType: 'Array<Item>' })

Items is a dictionary where each entry is list of Item. This works fine for the generated file for these types, since Item is after all defined there. But if I use Items in another schema, Array<Item> will be wrong since the import will be missing (or a copy of the type itself).

Can't disable the `index.ts` file

I'm trying to set this up for a project I'm working on, and it's working great, minus that it seems like the indexAllToRoot option is non-functional, no matter what I set it to, it outputs an index.ts file in the interfaces route.

Project Structure

makefile
generateInterfaces.ts
src:
    schemas:
        config.ts
    types:

What I want to be generated:

makefile
generateInterfaces.ts
src:
    schemas:
        config.ts
    types:
        config.ts

Versus what actually gets generated:

makefile
generateInterfaces.ts
src:
    schemas:
        config.ts
    types:
        config.ts
        index.ts

This isn't really blocking my project because I just added an additional command to my makefile to delete the index.ts for now, however it would be nice if the option did what it was claiming to do? (or maybe had a better description if I'm misunderstanding what it does)

Files:

// generateInterfaces.ts
import { convertFromDirectory } from "joi-to-typescript";

convertFromDirectory({
	indentationChacters: `\t`,
	schemaDirectory: "./src/schemas",
	typeOutputDirectory: "./src/types",
	treatDefaultedOptionalAsRequired: true,
	indexAllToRoot: false,
	fileHeader: `/*
This file was automatically generated by joi-to-typescript
Do not modify this file manually
*/`
});
// src/schemas/config.ts
import Joi from "joi";

export const serverOptionsSchema = Joi.object({
	port: Joi.number().port().description(`The port the server will run on`),
}).description(`The options specific to the web server`);

export const configSchema = Joi.object({
	server: serverOptionsSchema,
}).description(`The configuration format for the server`);
# makefile
NODE = node
PACKAGE_MANAGER = pnpm
OUT_DIR = dist

BIN := $(shell $(PACKAGE_MANAGER) bin)

.PHONY: $(OUT_DIR) dev prod run


$(OUT_DIR): interfaces
	tsc --outDir=$(OUT_DIR)

interfaces:
	$(BIN)/ts-node generateInterfaces.ts
	rm src/types/index.ts

Environment information:

  • Node v16.16.0
  • Package manager: PNPM (version 7.9.0)
  • Typescript 4.7.4
  • Operating System: Ubuntu 20.04 LTS

If there's any extra information required let me know and I'll happily provide it.

Package.json -> type: "module" -- not supported

I recently needed to add type:"module" to my package.json and setup my modules: "ESNext". This has caused joi-to-typescript to break with the following error.

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /scripts/makeTypes.ts
    at new NodeError (node:internal/errors:372:5)
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:56:11)
    at defaultGetFormat (node:internal/modules/esm/get_format:83:38)
    at defaultLoad (node:internal/modules/esm/load:21:20)
    at ESMLoader.load (node:internal/modules/esm/loader:407:26)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:326:22)
    at new ModuleJob (node:internal/modules/esm/module_job:66:26)
    at ESMLoader.#createModuleJob (node:internal/modules/esm/loader:345:17)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:304:34)
    at async Promise.all (index 0) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}

Now, if I add this to my tsconfig.json:

    "ts-node": {
        "compilerOptions": {
            "module": "CommonJS"
        }
    },

and remove type: "module" from package.json

While I can clearly remove / readd the type module to my package.json, I was wondering if there is a build target fix that would resolve this... e.g. not commonjs module.

Any thoughts?

Getting error while running example folder

Here is the issue that appears I run the script:

npm run types
Running joi-to-typescript...
TypeError: parsedSchema.children.flatMap is not a function
    at Object.getAllCustomTypes (/Users/josephkhan/htdocs/new-preparations/joi-to-typescript/example/node_modules/joi-to-typescript/dist/parse.js:27:38)
    at Object.convertSchema (/Users/josephkhan/htdocs/new-preparations/joi-to-typescript/example/node_modules/joi-to-typescript/dist/index.js:54:37)
    at Object.analyseSchemaFile (/Users/josephkhan/htdocs/new-preparations/joi-to-typescript/example/node_modules/joi-to-typescript/dist/analyseSchemaFile.js:29:39)
    at process._tickCallback (internal/process/next_tick.js:68:7)
    at Function.Module.runMain (internal/modules/cjs/loader.js:832:11)
    at main (/Users/josephkhan/htdocs/new-preparations/joi-to-typescript/example/node_modules/ts-node/src/bin.ts:198:14)
    at Object.<anonymous> (/Users/josephkhan/htdocs/new-preparations/joi-to-typescript/example/node_modules/ts-node/src/bin.ts:288:3)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)

Is there something I am missing?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.