diff --git a/src/handlers/model-output-type.ts b/src/handlers/model-output-type.ts index 71a24e02..05e93740 100644 --- a/src/handlers/model-output-type.ts +++ b/src/handlers/model-output-type.ts @@ -174,6 +174,21 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) { }), ], }); + + for (const options of settings || []) { + if (!options.output || options.isFieldType) { + continue; + } + property.decorators?.push({ + name: options.name, + arguments: options.arguments, + }); + assert( + options.from, + "Missed 'from' part in configuration or field setting", + ); + importDeclarations.create(options); + } } eventEmitter.emitSync('ClassProperty', property, { location, isList }); diff --git a/src/helpers/field-settings.ts b/src/helpers/field-settings.ts index 152a87f0..0dd27c5b 100644 --- a/src/helpers/field-settings.ts +++ b/src/helpers/field-settings.ts @@ -35,16 +35,17 @@ export function createFieldSettings(args: { text: string; config: GeneratorConfiguration; }) { - let { text } = args; - const { config } = args; + const { config, text } = args; const result: FieldSettings = new FieldSettings(); - const matches = text.matchAll(/@(?\w+(\.(\w+))?)\((?.*?)\)/g); - for (const match of matches) { - text = text.slice(0, match.index); - const name = match.groups?.name; - if (!name) { + const textLines = text.split('\\n'); + const documentationLines: string[] = []; + for (const line of textLines) { + const match = /^@(?\w+(\.(\w+))?)\((?.*?)\)/.exec(line); + if (!match) { + documentationLines.push(line); continue; } + const name = match.groups?.name; const decorator: FieldSetting = { name: '', arguments: [], @@ -99,7 +100,7 @@ export function createFieldSettings(args: { return { result, - documentation: text.split('\\n').filter(Boolean).join('\\n') || undefined, + documentation: documentationLines.filter(Boolean).join('\\n') || undefined, }; } diff --git a/src/test/custom-decorators.spec.ts b/src/test/custom-decorators.spec.ts index 6fe3ee62..455e82ee 100644 --- a/src/test/custom-decorators.spec.ts +++ b/src/test/custom-decorators.spec.ts @@ -112,6 +112,46 @@ describe('custom decorators namespace both input and output', () => { }); }); +describe('custom decorators and description', () => { + let importDeclarations: any[]; + before(async () => { + ({ project, sourceFiles } = await testGenerate({ + schema: ` + model User { + /// user id really + id Int @id + /// User name really + /// @Validator.Length(5, 15, "check length") + name String + }`, + options: [ + `outputFilePattern = "{name}.{type}.ts"`, + `fields_Validator_from = "class-validator"`, + `fields_Validator_output = true`, + `fields_Validator_input = true`, + ], + })); + }); + + describe('user model output', () => { + before(() => setSourceFile('user.model.ts')); + + it('has description', () => { + const data = d('name')?.arguments?.[1]; + expect(data).toContain("description:'User name really'"); + }); + + it('has decorator length', () => { + const decorators = p('name')?.decorators; + expect(decorators).toHaveLength(2); + expect(decorators).toContainEqual( + expect.objectContaining({ name: 'Length' }), + ); + expect(sourceText).toContain('@Validator.Length(5, 15, "check length")'); + }); + }); +}); + describe('custom decorators default import', () => { let importDeclarations: any[]; @@ -219,7 +259,7 @@ describe('custom decorators field custom type namespace', () => { setSourceFile('user-create.input.ts'); }); - it('field type', () => { + it('email field type', () => { const decorator = p('email')?.decorators?.find(d => d.name === 'Field'); const typeArgument = decorator?.arguments?.[0]; expect(typeArgument).toEqual('() => Scalars.EmailAddress');