Skip to content

Commit

Permalink
feat(custom decorators): Allow attach @Directive()
Browse files Browse the repository at this point in the history
  • Loading branch information
unlight committed Oct 16, 2021
1 parent a2a8d46 commit d6faef0
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 130 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ module.exports = {
'sonarjs/no-identical-functions': 0,
'consistent-return': 0,
'max-lines': 0,
'regexp/strict': 0,
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/no-unsafe-member-access': 0,
'@typescript-eslint/no-floating-promises': 0,
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,34 @@ export class User {
}
```
### @Directive()
Allow attach `@Directive` decorator from `@nestjs/graphql`
GraphQL federation example:
```
/// @Directive({ arguments: ['@extends'] })
/// @Directive({ arguments: ['@key(fields: "id")'] })
model User {
/// @Directive({ arguments: ['@external'] })
id String @id
}
```
May generate:
```ts
@ObjectType()
@Directive('@extends')
@Directive('@key(fields: "id")')
export class User {
@Field(() => ID, { nullable: false })
@Directive('@external')
id!: string;
}
```
#### @ObjectType()
Allow rename type in schema and mark as abstract.
Expand Down
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,23 @@
"devDependencies": {
"@commitlint/cli": "^13.2.1",
"@commitlint/config-conventional": "^13.2.0",
"@nestjs/common": "^8.0.11",
"@nestjs/core": "^8.0.11",
"@nestjs/common": "^8.1.1",
"@nestjs/core": "^8.1.1",
"@nestjs/graphql": "^9.0.6",
"@nestjs/platform-express": "^8.0.11",
"@nestjs/platform-express": "^8.1.1",
"@paljs/plugins": "^4.0.8",
"@prisma/client": "^3.2.1",
"@semantic-release/changelog": "^6.0.0",
"@semantic-release/git": "^10.0.0",
"@types/flat": "^5.0.2",
"@types/lodash": "^4.14.175",
"@types/mocha": "^9.0.0",
"@types/node": "^16.10.4",
"@types/node": "^16.11.0",
"@types/pluralize": "^0.0.29",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"apollo-server-express": "^3.3.0",
"c8": "^7.9.0",
"apollo-server-express": "^3.4.0",
"c8": "^7.10.0",
"class-transformer": "^0.4.0",
"class-validator": "^0.13.1",
"commitizen": "^4.2.4",
Expand All @@ -92,7 +92,7 @@
"eslint": "^7.32.0",
"eslint-import-resolver-node": "^0.3.6",
"eslint-plugin-etc": "^1.5.4",
"eslint-plugin-import": "^2.25.1",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-only-warn": "^1.0.3",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-promise": "^5.1.0",
Expand All @@ -107,9 +107,9 @@
"ghooks": "^2.0.4",
"git-branch-is": "^4.0.0",
"graphql": "^15.6.1",
"graphql-scalars": "^1.11.1",
"graphql-scalars": "^1.12.0",
"graphql-type-json": "^0.3.2",
"mocha": "^9.1.2",
"mocha": "^9.1.3",
"ololog": "^1.1.175",
"precise-commits": "^1.0.2",
"prettier": "^2.4.1",
Expand Down
26 changes: 15 additions & 11 deletions src/handlers/input-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,22 @@ export function inputType(

if (isCustomsApplicable) {
for (const options of settings || []) {
if (!options.input || options.kind !== 'Decorator') {
continue;
if (
(options.kind === 'Decorator' &&
options.input &&
options.match?.(name)) ??
true
) {
property.decorators.push({
name: options.name,
arguments: options.arguments as string[],
});
ok(
options.from,
"Missed 'from' part in configuration or field setting",
);
importDeclarations.create(options);
}
property.decorators.push({
name: options.name,
arguments: options.arguments as string[],
});
ok(
options.from,
"Missed 'from' part in configuration or field setting",
);
importDeclarations.create(options);
}
}

Expand Down
38 changes: 20 additions & 18 deletions src/handlers/model-output-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,18 +178,20 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {
});

for (const setting of settings || []) {
if (!shouldBeDecorated(setting)) {
continue;
if (
shouldBeDecorated(setting) &&
(setting.match?.(field.name) ?? true)
) {
property.decorators.push({
name: setting.name,
arguments: setting.arguments as string[],
});
ok(
setting.from,
"Missed 'from' part in configuration or field setting",
);
importDeclarations.create(setting);
}
property.decorators.push({
name: setting.name,
arguments: setting.arguments as string[],
});
ok(
setting.from,
"Missed 'from' part in configuration or field setting",
);
importDeclarations.create(setting);
}

for (const decorate of config.decorate) {
Expand Down Expand Up @@ -217,14 +219,14 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {

// Generate class decorators from model settings
for (const setting of modelSettings || []) {
if (!shouldBeDecorated(setting)) {
continue;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
if (shouldBeDecorated(setting)) {
classStructure.decorators.push({
name: setting.name,
arguments: setting.arguments as string[],
});
importDeclarations.create(setting);
}
classStructure.decorators.push({
name: setting.name,
arguments: setting.arguments as string[],
});
importDeclarations.create(setting);
}

if (exportDeclaration) {
Expand Down
26 changes: 15 additions & 11 deletions src/handlers/output-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,22 @@ export function outputType(outputType: OutputType, args: EventArguments) {

if (isCustomsApplicable) {
for (const options of settings || []) {
if (!options.output || options.kind !== 'Decorator') {
continue;
if (
(options.kind === 'Decorator' &&
options.output &&
options.match?.(field.name)) ??
true
) {
property.decorators.push({
name: options.name,
arguments: options.arguments as string[],
});
ok(
options.from,
"Missed 'from' part in configuration or field setting",
);
importDeclarations.create(options);
}
property.decorators.push({
name: options.name,
arguments: options.arguments as string[],
});
ok(
options.from,
"Missed 'from' part in configuration or field setting",
);
importDeclarations.create(options);
}
}
}
Expand Down
18 changes: 16 additions & 2 deletions src/helpers/object-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,16 @@ export function createObjectSettings(args: {
name: options.name,
isAbstract: options.isAbstract,
};
} else if (name === 'Directive' && match.groups?.args) {
const options = customType(match.groups.args);
merge(element, { model: true, from: '@nestjs/graphql' }, options, {
name,
namespace: false,
kind: 'Decorator',
arguments: Array.isArray(options.arguments)
? options.arguments.map(s => JSON5.stringify(s))
: options.arguments,
});
} else {
const namespace = getNamespace(name);
element.namespaceImport = namespace;
Expand All @@ -158,7 +168,7 @@ export function createObjectSettings(args: {
.map(s => trim(s))
.filter(Boolean),
};
merge(element, config.fields[namespace], options);
merge(element, namespace && config.fields[namespace], options);
}
result.push(element);
}
Expand Down Expand Up @@ -234,10 +244,14 @@ function parseArgs(string: string): Record<string, unknown> | string {
}
}

function getNamespace(name: unknown) {
function getNamespace(name: unknown): string | undefined {
if (name === undefined) {
return undefined;
}
let result = String(name);
if (result.includes('.')) {
[result] = result.split('.');
}
// eslint-disable-next-line consistent-return
return result;
}
72 changes: 62 additions & 10 deletions src/test/custom-decorators.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import { Project, SourceFile } from 'ts-morph';
import { testSourceFile } from './helpers';
import { testGenerate } from './test-generate';

let sourceFile: SourceFile;
let project: Project;
let sourceFiles: SourceFile[];

describe('custom decorators namespace both input and output', () => {
before(async () => {
({ project, sourceFiles } = await testGenerate({
({ project } = await testGenerate({
schema: `
model User {
id Int @id
Expand Down Expand Up @@ -153,7 +151,7 @@ describe('custom decorators namespace both input and output', () => {

describe('fieldtype disable output', () => {
before(async () => {
({ project, sourceFiles } = await testGenerate({
({ project } = await testGenerate({
schema: `
model User {
id String @id @default(cuid())
Expand Down Expand Up @@ -182,7 +180,7 @@ describe('fieldtype disable output', () => {

describe('custom decorators and description', () => {
before(async () => {
({ project, sourceFiles } = await testGenerate({
({ project } = await testGenerate({
schema: `
model User {
/// user id really
Expand Down Expand Up @@ -225,7 +223,7 @@ describe('custom decorators and description', () => {

describe('custom decorators default import', () => {
before(async () => {
({ project, sourceFiles } = await testGenerate({
({ project } = await testGenerate({
schema: `
model User {
id Int @id
Expand Down Expand Up @@ -265,7 +263,7 @@ describe('custom decorators default import', () => {

describe('default import alternative syntax', () => {
before(async () => {
({ project, sourceFiles } = await testGenerate({
({ project } = await testGenerate({
schema: `
model User {
id Int @id
Expand Down Expand Up @@ -303,7 +301,7 @@ describe('default import alternative syntax', () => {

describe('custom decorators field custom type namespace', () => {
before(async () => {
({ project, sourceFiles } = await testGenerate({
({ project } = await testGenerate({
schema: `
model User {
id Int @id
Expand Down Expand Up @@ -368,7 +366,7 @@ describe('custom decorators field custom type namespace', () => {

describe('decorate option', () => {
before(async () => {
({ project, sourceFiles } = await testGenerate({
({ project } = await testGenerate({
schema: `
model User {
id Int @id @default(autoincrement())
Expand Down Expand Up @@ -460,7 +458,7 @@ describe('decorate option', () => {

describe('model decorate', () => {
before(async () => {
({ project, sourceFiles } = await testGenerate({
({ project } = await testGenerate({
schema: `
/// @NG.Directive('@extends')
/// @NG.Directive('@key(fields: "id")')
Expand Down Expand Up @@ -511,3 +509,57 @@ describe('model decorate', () => {
expect(s.propertyDecorators?.find(d => d.name === 'Directive')).toBeFalsy();
});
});

describe('model directive', () => {
before(async () => {
({ project } = await testGenerate({
schema: `
/// @Directive({ arguments: ['@extends'] })
/// @Directive({ arguments: ['@key(fields: "id")'] })
model User {
/// @Directive({ arguments: ['@external'] })
id String @id
}`,
options: [`outputFilePattern = "{name}.{type}.ts"`],
}));
});

it('user model id property', () => {
const s = testSourceFile({
project,
file: 'user.model.ts',
property: 'id',
});
console.log('s.sourceText', s.sourceText);
expect(s.propertyDecorators?.find(d => d.name === 'Directive')).toBeTruthy();
expect(
s.propertyDecorators?.find(d => d.name === 'Directive')?.arguments?.[0],
).toBe("'@external'");
});

it('user model class', () => {
const s = testSourceFile({
project,
file: 'user.model.ts',
});
expect(s.namedImports).toContainEqual({
name: 'Directive',
specifier: '@nestjs/graphql',
});
expect(s.classFile.getDecorator('Directive')).toBeTruthy();
});

it('usergroupby should not have ng.directive', () => {
const s = testSourceFile({
project,
file: 'user-group-by.output.ts',
property: 'id',
});
expect(s.propertyDecorators).toHaveLength(1);
expect(s.propertyDecorators?.find(d => d.name === 'Directive')).toBeFalsy();
expect(s.namedImports).not.toContainEqual({
name: 'Directive',
specifier: '@nestjs/graphql',
});
});
});
Loading

0 comments on commit d6faef0

Please sign in to comment.