ts-morph#PropertyDeclarationStructure TypeScript Examples

The following examples show how to use ts-morph#PropertyDeclarationStructure. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ConfigGenerator.ts    From gengen with MIT License 6 votes vote down vote up
private getProperties(controllers: Record<string, Record<string, string>>): OptionalKind<PropertyDeclarationStructure>[] {
        return Object.entries(controllers).map(([controller, actions]) => {
            const propertyValue = Object.entries(actions).reduce<Record<string, string>>((store, [name, path]) => {
                store[name] = `'${path}'`;
                return store;
            }, {});

            return {
                name: `${controller}Service`,
                scope: Scope.Public,
                isStatic: true,
                initializer: Writers.object(propertyValue)
            };
        });
    }
Example #2
Source File: ModelsGenerator.ts    From gengen with MIT License 6 votes vote down vote up
private getObjectProperties(objectModel: IObjectModel): PropertyDeclarationStructure[] {
        return [
            ...objectModel.properties.map(
                (objectProperty): PropertyDeclarationStructure => ({
                    kind: StructureKind.Property,
                    scope: Scope.Public,
                    name: objectProperty.name,
                    type: new TypeSerializer(objectProperty).toString(),
                    initializer: UNDEFINED_STRING
                })
            ),
            this.getGuardProperty(objectModel.name)
        ];
    }
Example #3
Source File: ModelsGenerator.ts    From gengen with MIT License 6 votes vote down vote up
private getGuardProperty(name: string): PropertyDeclarationStructure {
        return {
            kind: StructureKind.Property,
            scope: Scope.Private,
            name: `__${lowerFirst(name)}`,
            type: 'string',
            hasExclamationToken: true
        };
    }
Example #4
Source File: emit-single.ts    From prisma-nestjs-graphql with MIT License 6 votes vote down vote up
function classProperty(
  property: PropertyDeclarationStructure,
  eventArguments: {
    location: DMMF.FieldLocation;
    isList: boolean;
    propertyType: string[];
  },
) {
  const { location, isList, propertyType } = eventArguments;
  if (['inputObjectTypes', 'outputObjectTypes'].includes(location) && !isList) {
    const types = propertyType.filter(t => t !== 'null');
    property.type = types.map(t => `InstanceType<typeof ${t}>`).join(' | ');
    if (types.length !== propertyType.length) {
      // If null was removed
      property.type += ' | null';
    }
  }
}
Example #5
Source File: property-structure.ts    From prisma-nestjs-graphql with MIT License 6 votes vote down vote up
/**
 * Get property structure (field) for class.
 */
export function propertyStructure(args: {
  propertyType: string[];
  isList: boolean;
  name: string;
  isNullable?: boolean;
  hasQuestionToken?: boolean;
  hasExclamationToken?: boolean;
}): PropertyDeclarationStructure {
  const {
    isNullable,
    propertyType,
    name,
    isList,
    hasQuestionToken,
    hasExclamationToken,
  } = args;
  const type = propertyType.map(type => (isList ? `Array<${type}>` : type)).join(' | ');

  return {
    kind: StructureKind.Property,
    name,
    type,
    hasQuestionToken: hasQuestionToken ?? isNullable,
    hasExclamationToken: hasExclamationToken ?? !isNullable,
    decorators: [],
    leadingTrivia: '\n',
  };
}
Example #6
Source File: helpers.ts    From prisma-nestjs-graphql with MIT License 5 votes vote down vote up
export function testSourceFile(args: {
  project: Project;
  file?: string;
  class?: string;
  property?: string;
}) {
  const { project, file, property, class: className } = args;
  let sourceFile: SourceFile;
  if (file) {
    sourceFile = project.getSourceFileOrThrow(s => s.getFilePath().endsWith(file));
  } else if (className) {
    sourceFile = project.getSourceFileOrThrow(s => Boolean(s.getClass(className)));
  } else {
    throw new TypeError('file or class must be provided');
  }

  const importDeclarations = sourceFile
    .getImportDeclarations()
    .map(d => d.getStructure());
  const classFile = sourceFile.getClass(() => true)!;
  const propertyStructure =
    property && classFile.getProperty(p => p.getName() === property)?.getStructure();
  const propertyDecorators = (
    propertyStructure as PropertyDeclarationStructure | undefined
  )?.decorators;
  const fieldDecorator = propertyDecorators?.find(d => d.name === 'Field');

  type ImportElement = { name: string; specifier: string };
  const namedImports: ImportElement[] = [];
  const namespaceImports: ImportElement[] = [];

  for (const d of importDeclarations) {
    if (d.namespaceImport) {
      namespaceImports.push({
        name: d.namespaceImport,
        specifier: d.moduleSpecifier,
      });
    }
    for (const s of (d.namedImports || []) as ImportSpecifierStructure[]) {
      namedImports.push({
        name: s.name,
        specifier: d.moduleSpecifier,
      });
    }
  }

  return {
    sourceFile,
    classFile,
    sourceText: sourceFile.getText(),
    namedImports,
    namespaceImports,
    property: propertyStructure as PropertyDeclarationStructure | undefined,
    propertyDecorators,
    fieldDecorator,
    fieldDecoratorType: fieldDecorator?.arguments?.[0],
    fieldDecoratorOptions: fieldDecorator?.arguments?.[1],
  };
}
Example #7
Source File: test.spec.ts    From prisma-nestjs-graphql with MIT License 5 votes vote down vote up
propertyStructure: PropertyDeclarationStructure
Example #8
Source File: generate.spec.ts    From prisma-nestjs-graphql with MIT License 4 votes vote down vote up
describe('model with one id int', () => {
  before(async () => {
    ({ project, sourceFiles } = await testGenerate({
      schema: `
            /// User really
            model User {
                  /// user id really
                  id Int @id @default(1)
                }`,
    }));
  });

  describe('model', () => {
    let s: ReturnType<typeof testSourceFile>;
    before(() => {
      s = testSourceFile({ project, file: 'user.model.ts' });
    });

    it('class should be exported', () => {
      expect(s.classFile.isExported()).toBe(true);
    });

    it('argument decorated id', () => {
      const { fieldDecoratorType } = testSourceFile({
        project,
        file: 'user.model.ts',
        property: 'id',
      });
      expect(fieldDecoratorType).toEqual('() => ID');
    });

    it('should have import graphql type id', () => {
      expect(s.namedImports).toContainEqual({
        name: 'ID',
        specifier: '@nestjs/graphql',
      });
    });

    it('should have import field decorator', () => {
      expect(s.namedImports).toContainEqual({
        name: 'Field',
        specifier: '@nestjs/graphql',
      });
    });

    it('should have exclamation mark for non null id field', () => {
      const s = testSourceFile({
        project,
        file: 'user.model.ts',
        property: 'id',
      });
      expect(s.property?.hasExclamationToken).toEqual(true);
    });

    it('default value', () => {
      const s = testSourceFile({
        project,
        class: 'User',
        property: 'id',
      });
      expect(s.fieldDecoratorOptions).toMatch(/nullable:\s*false/);
      expect(s.fieldDecoratorOptions).toMatch(/defaultValue:\s*1/);
      expect(s.fieldDecoratorOptions).not.toMatch(/description:\s*undefined/);
    });

    it('property description', () => {
      const s = testSourceFile({
        project,
        file: 'user.model.ts',
        property: 'id',
      });
      expect(s.fieldDecoratorOptions).toMatch(/nullable:\s*false/);
      expect(s.fieldDecoratorOptions).toMatch(/description:\s*["']user id really["']/);
    });

    it('property description in jsdoc', () => {
      const description = s.classFile
        .getProperty('id')
        ?.getJsDocs()[0]
        .getDescription();
      expect(description).toEqual('\nuser id really');
    });

    it('object type description', () => {
      const decoratorArgument = s.classFile.getDecorators()[0].getStructure()
        .arguments?.[0] as string | undefined;
      expect(decoratorArgument).toMatch(/description:\s*["']User really["']/);
    });

    it('has js comment', () => {
      expect(s.classFile.getJsDocs()[0].getDescription()).toEqual('\nUser really');
    });

    it('has import objecttype', () => {
      expect(s.namedImports).toContainEqual({
        name: 'ObjectType',
        specifier: '@nestjs/graphql',
      });
    });
  });

  it('aggregate user output class should be exported', () => {
    const s = testSourceFile({ project, file: 'aggregate-user.output.ts' });
    expect(s.classFile.isExported()).toBe(true);
  });

  it('aggregate user output contains decorator ObjectType', () => {
    const s = testSourceFile({ project, file: 'aggregate-user.output.ts' });
    expect(s.namedImports).toContainEqual({
      name: 'ObjectType',
      specifier: '@nestjs/graphql',
    });
  });

  it('aggregate user output count', () => {
    const s = testSourceFile({
      project,
      file: 'aggregate-user.output.ts',
      property: '_count',
    });
    expect(s.property?.type).toEqual('UserCountAggregate');
    expect(s.property?.hasQuestionToken).toEqual(true);
  });

  it('user count aggregate (usercountaggregate) id property', () => {
    const s = testSourceFile({
      project,
      file: 'user-count-aggregate.output.ts',
      property: 'id',
    });
    expect(s.property?.type).toEqual('number');
    expect(s.fieldDecoratorType).toEqual('() => Int');
    expect(s.property?.hasQuestionToken).toEqual(false);
    expect(s.namedImports).toContainEqual({
      name: 'Int',
      specifier: '@nestjs/graphql',
    });
  });

  describe('where input', () => {
    it('should have id property', () => {
      const { property } = testSourceFile({
        project,
        file: 'user-where.input.ts',
        property: 'id',
      });
      expect(property?.name).toEqual('id');
    });

    it('should have type IntFilter', () => {
      const { property } = testSourceFile({
        project,
        file: 'user-where.input.ts',
        property: 'id',
      });
      expect(property?.type).toEqual('IntFilter');
    });

    it('field decorator returns IntFilter', () => {
      const { fieldDecoratorType } = testSourceFile({
        project,
        file: 'user-where.input.ts',
        property: 'id',
      });
      expect(fieldDecoratorType).toEqual('() => IntFilter');
    });

    it('field decorator IntFilter nullable', () => {
      const { fieldDecoratorOptions } = testSourceFile({
        project,
        file: 'user-where.input.ts',
        property: 'id',
      });
      expect(fieldDecoratorOptions).toMatch(/nullable:\s*true/);
    });

    it('property AND has one type', () => {
      const { property } = testSourceFile({
        project,
        file: 'user-where.input.ts',
        property: 'AND',
      });
      expect(property?.type).toEqual('Array<UserWhereInput>');
    });
  });

  describe('aggregate user args', () => {
    before(() => {
      setSourceFile('user-aggregate.args.ts');
      classFile = sourceFile.getClass(() => true)!;
    });

    it('decorator name args', () => {
      const { classFile } = testSourceFile({
        project,
        file: 'user-aggregate.args.ts',
      });
      const decorator = classFile.getDecorator('ArgsType');
      expect(decorator?.getText()).toEqual('@ArgsType()');
    });

    it('no duplicated properties', () => {
      const names = sourceFile
        .getClass(() => true)
        ?.getProperties()
        .map(p => p.getName())!;
      expect([...new Set(names).values()]).toHaveLength(names.length);
    });

    it('count', () => {
      expect(p('_count')?.type).toEqual('UserCountAggregateInput');
    });

    it('sum', () => {
      expect(p('_sum')?.type).toEqual('UserSumAggregateInput');
    });

    it('min', () => {
      expect(p('_min')?.type).toEqual('UserMinAggregateInput');
    });

    it('max', () => {
      expect(p('_max')?.type).toEqual('UserMaxAggregateInput');
    });
  });

  describe('user count aggregate input', () => {
    let id: PropertyDeclarationStructure;
    before(() => {
      setSourceFile('user-count-aggregate.input.ts');
      id = getPropertyStructure(sourceFile, 'id')!;
    });

    it('property id should have true type', () => {
      const s = testSourceFile({
        project,
        file: 'user-count-aggregate.input.ts',
        property: 'id',
      });

      expect(s.property?.type).toEqual('true');
    });

    it('nullable', () => {
      expect(id.hasQuestionToken).toEqual(true);
    });

    it('decorated field type should be boolean', () => {
      const argument = t('id');
      expect(argument).toEqual('() => Boolean');
    });
  });

  it('rename to user-group-by args', () => {
    const s = testSourceFile({
      project,
      file: 'user-group-by.args.ts',
    });

    expect(s.classFile.getName()).toEqual('UserGroupByArgs');
  });

  it('rename to user aggregateargs', () => {
    const s = testSourceFile({
      project,
      file: 'user-aggregate.args.ts',
    });

    expect(s.classFile.getName()).toEqual('UserAggregateArgs');
  });
});