ts-morph#ClassDeclarationStructure TypeScript Examples
The following examples show how to use
ts-morph#ClassDeclarationStructure.
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: ModelsGenerator.ts From gengen with MIT License | 6 votes |
private getIdentities(identities: IIdentityModel[], interfaces: IInterfaceModel[]): StatementStructures[] {
return identities.map(
(z): ClassDeclarationStructure => ({
kind: StructureKind.Class,
isExported: true,
name: z.name,
ctors: [
{
parameters: [
{
name: z.property.name,
hasQuestionToken: true,
type: TypeSerializer.fromTypeName(`${z.property.type} | ${z.property.dtoType}`).toString()
}
] as OptionalKind<ParameterDeclarationStructure>[],
statements: `this.${z.property.name} = new ${z.property.type}(${z.property.name});`
}
] as OptionalKind<ConstructorDeclarationStructure>[],
properties: [{ scope: Scope.Public, name: z.property.name, type: z.property.type }, this.getGuardProperty(z.name)],
methods: [
{
scope: Scope.Public,
isStatic: true,
name: TO_DTO_METHOD,
parameters: [{ name: z.property.name, type: z.property.type }],
returnType: interfaces.find(
(i) =>
i.properties.length === 1 &&
i.properties.every((x) => x.dtoType === z.property.dtoType && x.name === z.property.name)
)?.name,
statements: `return { ${z.property.name}: ${z.property.name}.toString() };`
}
]
})
);
}
Example #2
Source File: ModelsGenerator.ts From gengen with MIT License | 6 votes |
private getObjects(objects: IObjectModel[]): ClassDeclarationStructure[] {
return objects.map((z) => ({
kind: StructureKind.Class,
isExported: true,
name: z.name,
properties: this.getObjectProperties(z),
methods: [
{
scope: Scope.Public,
isStatic: true,
name: TO_DTO_METHOD,
parameters: [{ name: 'model', type: `Partial<${z.name}>` }],
returnType: z.dtoType,
statements: (x) => {
x.writeLine('return {');
z.properties.forEach((p) =>
x.withIndentationLevel(3, () => x.writeLine(`${p.name}: ${this.getToDtoPropertyInitializer(p)},`))
);
x.writeLine('};');
}
},
{
scope: Scope.Public,
isStatic: true,
name: FROM_DTO_METHOD,
parameters: [{ name: 'dto', type: z.dtoType }],
returnType: z.name,
statements: (x) => {
x.writeLine(`const model = new ${z.name}();`);
z.properties.forEach((p) =>
x.withIndentationLevel(2, () => x.writeLine(`model.${p.name} = ${this.getFromDtoPropertyInitializer(p)};`))
);
x.writeLine('return model;');
}
}
]
}));
}
Example #3
Source File: AngularServicesGenerator.ts From gengen with MIT License | 6 votes |
protected getServices(services: IServiceModel[]): ClassDeclarationStructure[] {
return services.map(
(service): ClassDeclarationStructure => ({
kind: StructureKind.Class,
isExported: true,
name: `${service.name}Service`,
extends: service.methods.some((x) => x.kind === MethodKind.Download) ? DOWNLOAD_SERVICE : BASE_SERVICE,
decorators: [
{
kind: StructureKind.Decorator,
name: 'Injectable',
arguments: Writers.object({ providedIn: "'root'" })
}
],
ctors: [this.getConstructorStatement(service)],
methods: this.methodGenerator.getMethodsCodeStructures(service.methods)
})
);
}
Example #4
Source File: generate-files.ts From prisma-nestjs-graphql with MIT License | 4 votes |
export async function generateFiles(args: EventArguments) {
const { project, config, output, eventEmitter } = args;
if (config.emitSingle) {
const rootDirectory =
project.getDirectory(output) || project.createDirectory(output);
const sourceFile =
rootDirectory.getSourceFile('index.ts') ||
rootDirectory.createSourceFile('index.ts', undefined, { overwrite: true });
const statements = project.getSourceFiles().flatMap(s => {
if (s === sourceFile) {
return [];
}
const classDeclaration = s.getClass(() => true);
const statements = s.getStructure().statements;
// Reget decorator full name
// TODO: Check possible bug of ts-morph
if (Array.isArray(statements)) {
for (const statement of statements) {
if (
!(typeof statement === 'object' && statement.kind === StructureKind.Class)
) {
continue;
}
for (const property of statement.properties || []) {
for (const decorator of property.decorators || []) {
const fullName = classDeclaration
?.getProperty(property.name)
?.getDecorator(decorator.name)
?.getFullName();
ok(
fullName,
`Cannot get full name of decorator of class ${statement.name!}`,
);
decorator.name = fullName;
}
}
}
}
project.removeSourceFile(s);
return statements;
});
const imports = new ImportDeclarationMap();
const enums: (StatementStructures | string)[] = [];
const classes: ClassDeclarationStructure[] = [];
for (const statement of statements as (StatementStructures | string)[]) {
if (typeof statement === 'string') {
if (statement.startsWith('registerEnumType')) {
enums.push(statement);
}
continue;
}
switch (statement.kind) {
case StructureKind.ImportDeclaration:
if (
statement.moduleSpecifier.startsWith('./') ||
statement.moduleSpecifier.startsWith('..')
) {
continue;
}
for (const namedImport of statement.namedImports as ImportSpecifierStructure[]) {
const name = namedImport.alias || namedImport.name;
imports.add(name, statement.moduleSpecifier);
}
if (statement.defaultImport) {
imports.create({
from: statement.moduleSpecifier,
name: statement.defaultImport,
defaultImport: statement.defaultImport,
});
}
if (statement.namespaceImport) {
imports.create({
from: statement.moduleSpecifier,
name: statement.namespaceImport,
namespaceImport: statement.namespaceImport,
});
}
break;
case StructureKind.Enum:
enums.unshift(statement);
break;
case StructureKind.Class:
classes.push(statement);
break;
}
}
sourceFile.set({
kind: StructureKind.SourceFile,
statements: [...imports.toStatements(), ...enums, ...classes],
});
}
if (config.emitCompiled) {
project.compilerOptions.set({
declaration: true,
declarationDir: output,
rootDir: output,
outDir: output,
emitDecoratorMetadata: false,
skipLibCheck: true,
});
const emitResult = await project.emit();
const errors = emitResult.getDiagnostics().map(d => String(d.getMessageText()));
if (errors.length > 0) {
eventEmitter.emitSync('Warning', errors);
}
} else {
await project.save();
}
}
Example #5
Source File: input-type.ts From prisma-nestjs-graphql with MIT License | 4 votes |
export function inputType(
args: EventArguments & {
inputType: InputType;
fileType: string;
classDecoratorName: string;
},
) {
const {
classDecoratorName,
classTransformerTypeModels,
config,
eventEmitter,
fieldSettings,
fileType,
getModelName,
getSourceFile,
inputType,
models,
removeTypes,
typeNames,
} = args;
typeNames.add(inputType.name);
const importDeclarations = new ImportDeclarationMap();
const sourceFile = getSourceFile({
name: inputType.name,
type: fileType,
});
const classStructure: ClassDeclarationStructure = {
kind: StructureKind.Class,
isExported: true,
name: inputType.name,
decorators: [
{
name: classDecoratorName,
arguments: [],
},
],
properties: [],
};
const modelName = getModelName(inputType.name) || '';
const model = models.get(modelName);
const modelFieldSettings = model && fieldSettings.get(model.name);
const moduleSpecifier = '@nestjs/graphql';
// console.log('sourceFile.getBaseName()', sourceFile.getBaseName());
importDeclarations
.set('Field', {
namedImports: [{ name: 'Field' }],
moduleSpecifier,
})
.set(classDecoratorName, {
namedImports: [{ name: classDecoratorName }],
moduleSpecifier,
});
const useInputType = config.useInputType.find(x =>
inputType.name.includes(x.typeName),
);
for (const field of inputType.fields) {
field.inputTypes = field.inputTypes.filter(t => !removeTypes.has(String(t.type)));
eventEmitter.emitSync('BeforeGenerateField', field, args);
const { inputTypes, isRequired, name } = field;
if (inputTypes.length === 0) {
// No types
continue;
}
const usePattern = useInputType?.ALL || useInputType?.[name];
const graphqlInputType = getGraphqlInputType(inputTypes, usePattern);
const { isList, location, type } = graphqlInputType;
const typeName = String(type);
const settings = modelFieldSettings?.get(name);
const propertySettings = settings?.getPropertyType({
name: inputType.name,
input: true,
});
const modelField = model?.fields.find(f => f.name === name);
const isCustomsApplicable = typeName === modelField?.type;
const propertyType = castArray(
propertySettings?.name ||
getPropertyType({
location,
type: typeName,
}),
);
const property = propertyStructure({
name,
isNullable: !isRequired,
propertyType,
isList,
});
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
classStructure.properties!.push(property);
if (propertySettings) {
importDeclarations.create({ ...propertySettings });
} else if (propertyType.includes('Decimal')) {
importDeclarations.add('Decimal', '@prisma/client/runtime');
}
// Get graphql type
let graphqlType: string;
const shouldHideField =
settings?.shouldHideField({
name: inputType.name,
input: true,
}) ||
config.decorate.some(
d =>
d.name === 'HideField' &&
d.from === '@nestjs/graphql' &&
d.isMatchField(name) &&
d.isMatchType(inputType.name),
);
const fieldType = settings?.getFieldType({
name: inputType.name,
input: true,
});
if (fieldType && isCustomsApplicable && !shouldHideField) {
graphqlType = fieldType.name;
importDeclarations.create({ ...fieldType });
} else {
// Import property type class
const graphqlImport = getGraphqlImport({
config,
sourceFile,
location,
typeName,
getSourceFile,
});
graphqlType = graphqlImport.name;
let referenceName = propertyType[0];
if (location === 'enumTypes') {
referenceName = last(referenceName.split(' ')) as string;
}
if (
graphqlImport.specifier &&
!importDeclarations.has(graphqlImport.name) &&
graphqlImport.name !== inputType.name
// ((graphqlImport.name !== inputType.name && !shouldHideField) ||
// (shouldHideField && referenceName === graphqlImport.name))
) {
importDeclarations.set(graphqlImport.name, {
namedImports: [{ name: graphqlImport.name }],
moduleSpecifier: graphqlImport.specifier,
});
}
}
ok(property.decorators, 'property.decorators is undefined');
if (shouldHideField) {
importDeclarations.add('HideField', '@nestjs/graphql');
property.decorators.push({ name: 'HideField', arguments: [] });
} else {
// Generate `@Field()` decorator
property.decorators.push({
name: 'Field',
arguments: [
isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`,
JSON5.stringify({
...settings?.fieldArguments(),
nullable: !isRequired,
}),
],
});
// Debug
// if (classStructure.name === 'XCreateInput') {
// console.log({
// field,
// property,
// modelField,
// graphqlInputType,
// 'args.inputType': args.inputType,
// 'classStructure.name': classStructure.name,
// classTransformerTypeModels,
// modelName,
// graphqlType,
// });
// }
if (graphqlType === 'GraphQLDecimal') {
importDeclarations.add('transformToDecimal', 'prisma-graphql-type-decimal');
importDeclarations.add('Transform', 'class-transformer');
importDeclarations.add('Type', 'class-transformer');
property.decorators.push(
{
name: 'Type',
arguments: ['() => Object'],
},
{
name: 'Transform',
arguments: ['transformToDecimal'],
},
);
} else if (
location === 'inputObjectTypes' &&
(modelField?.type === 'Decimal' ||
[
'connect',
'connectOrCreate',
'create',
'createMany',
'data',
'delete',
'deleteMany',
'disconnect',
'set',
'update',
'updateMany',
'upsert',
'where',
].includes(name) ||
classTransformerTypeModels.has(getModelName(graphqlType) || ''))
) {
importDeclarations.add('Type', 'class-transformer');
property.decorators.push({ name: 'Type', arguments: [`() => ${graphqlType}`] });
}
if (isCustomsApplicable) {
for (const options of settings || []) {
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);
}
}
}
for (const decorate of config.decorate) {
if (decorate.isMatchField(name) && decorate.isMatchType(inputType.name)) {
property.decorators.push({
name: decorate.name,
arguments: decorate.arguments?.map(x => pupa(x, { propertyType })),
});
importDeclarations.create(decorate);
}
}
}
eventEmitter.emitSync('ClassProperty', property, {
location,
isList,
propertyType,
});
}
sourceFile.set({
statements: [...importDeclarations.toStatements(), classStructure],
});
}
Example #6
Source File: model-output-type.ts From prisma-nestjs-graphql with MIT License | 4 votes |
export function modelOutputType(outputType: OutputType, args: EventArguments) {
const { getSourceFile, models, config, modelFields, fieldSettings, eventEmitter } =
args;
const model = models.get(outputType.name);
ok(model, `Cannot find model by name ${outputType.name}`);
const sourceFile = getSourceFile({
name: outputType.name,
type: 'model',
});
const sourceFileStructure = sourceFile.getStructure();
const exportDeclaration = getExportDeclaration(
model.name,
sourceFileStructure.statements as StatementStructures[],
);
const importDeclarations = new ImportDeclarationMap();
const classStructure: ClassDeclarationStructure = {
kind: StructureKind.Class,
isExported: true,
name: outputType.name,
decorators: [
{
name: 'ObjectType',
arguments: [],
},
],
properties: [],
};
(sourceFileStructure.statements as StatementStructures[]).push(classStructure);
ok(classStructure.decorators, 'classStructure.decorators is undefined');
const decorator = classStructure.decorators.find(d => d.name === 'ObjectType');
ok(decorator, 'ObjectType decorator not found');
let modelSettings: ObjectSettings | undefined;
// Get model settings from documentation
if (model.documentation) {
const objectTypeOptions: PlainObject = {};
const { documentation, settings } = createObjectSettings({
text: model.documentation,
config,
});
if (documentation) {
if (!classStructure.leadingTrivia) {
classStructure.leadingTrivia = createComment(documentation);
}
objectTypeOptions.description = documentation;
}
decorator.arguments = settings.getObjectTypeArguments(objectTypeOptions);
modelSettings = settings;
}
importDeclarations.add('Field', nestjsGraphql);
importDeclarations.add('ObjectType', nestjsGraphql);
for (const field of outputType.fields) {
let fileType = 'model';
const { location, isList, type, namespace } = field.outputType;
let outputTypeName = String(type);
if (namespace !== 'model') {
fileType = 'output';
outputTypeName = getOutputTypeName(outputTypeName);
}
const modelField = modelFields.get(model.name)?.get(field.name);
const settings = fieldSettings.get(model.name)?.get(field.name);
const fieldType = settings?.getFieldType({
name: outputType.name,
output: true,
});
const propertySettings = settings?.getPropertyType({
name: outputType.name,
output: true,
});
const propertyType = castArray(
propertySettings?.name ||
getPropertyType({
location,
type: outputTypeName,
}),
);
// For model we keep only one type
propertyType.splice(1, propertyType.length);
if (field.isNullable && !isList) {
propertyType.push('null');
}
let graphqlType: string;
if (fieldType) {
graphqlType = fieldType.name;
importDeclarations.create({ ...fieldType });
} else {
const graphqlImport = getGraphqlImport({
config,
sourceFile,
fileType,
location,
isId: modelField?.isId,
noTypeId: config.noTypeId,
typeName: outputTypeName,
getSourceFile,
});
graphqlType = graphqlImport.name;
if (graphqlImport.name !== outputType.name && graphqlImport.specifier) {
importDeclarations.add(graphqlImport.name, graphqlImport.specifier);
}
}
const property = propertyStructure({
name: field.name,
isNullable: field.isNullable,
hasExclamationToken: true,
hasQuestionToken: location === 'outputObjectTypes',
propertyType,
isList,
});
if (typeof property.leadingTrivia === 'string' && modelField?.documentation) {
property.leadingTrivia += createComment(modelField.documentation, settings);
}
classStructure.properties?.push(property);
if (propertySettings) {
importDeclarations.create({ ...propertySettings });
} else if (propertyType.includes('Decimal')) {
importDeclarations.add('Decimal', '@prisma/client/runtime');
}
ok(property.decorators, 'property.decorators is undefined');
if (settings?.shouldHideField({ name: outputType.name, output: true })) {
importDeclarations.add('HideField', nestjsGraphql);
property.decorators.push({ name: 'HideField', arguments: [] });
} else {
// Generate `@Field()` decorator
property.decorators.push({
name: 'Field',
arguments: [
isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`,
JSON5.stringify({
...settings?.fieldArguments(),
nullable: Boolean(field.isNullable),
defaultValue: ['number', 'string', 'boolean'].includes(
typeof modelField?.default,
)
? modelField?.default
: undefined,
description: modelField?.documentation,
}),
],
});
for (const setting of settings || []) {
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);
}
}
for (const decorate of config.decorate) {
if (decorate.isMatchField(field.name) && decorate.isMatchType(outputTypeName)) {
property.decorators.push({
name: decorate.name,
arguments: decorate.arguments?.map(x => pupa(x, { propertyType })),
});
importDeclarations.create(decorate);
}
}
}
eventEmitter.emitSync('ClassProperty', property, {
location,
isList,
propertyType,
});
}
// Generate class decorators from model settings
for (const setting of modelSettings || []) {
// 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);
}
}
if (exportDeclaration) {
sourceFile.set({
statements: [exportDeclaration, '\n', classStructure],
});
const classDeclaration = sourceFile.getClassOrThrow(model.name);
const commentedText = classDeclaration
.getText()
.split('\n')
.map(x => `// ${x}`);
classDeclaration.remove();
sourceFile.addStatements(['\n', ...commentedText]);
} else {
sourceFile.set({
statements: [...importDeclarations.toStatements(), classStructure],
});
}
}
Example #7
Source File: output-type.ts From prisma-nestjs-graphql with MIT License | 4 votes |
export function outputType(outputType: OutputType, args: EventArguments) {
const { getSourceFile, models, eventEmitter, fieldSettings, getModelName, config } =
args;
const importDeclarations = new ImportDeclarationMap();
const fileType = 'output';
const modelName = getModelName(outputType.name) || '';
const model = models.get(modelName);
const isAggregateOutput =
model &&
/(?:Count|Avg|Sum|Min|Max)AggregateOutputType$/.test(outputType.name) &&
String(outputType.name).startsWith(model.name);
const isCountOutput =
model?.name && outputType.name === `${model.name}CountOutputType`;
// Get rid of bogus suffixes
outputType.name = getOutputTypeName(outputType.name);
if (isAggregateOutput) {
eventEmitter.emitSync('AggregateOutput', { ...args, outputType });
}
const sourceFile = getSourceFile({
name: outputType.name,
type: fileType,
});
const classStructure: ClassDeclarationStructure = {
kind: StructureKind.Class,
isExported: true,
name: outputType.name,
decorators: [
{
name: 'ObjectType',
arguments: [],
},
],
properties: [],
};
importDeclarations.add('Field', nestjsGraphql);
importDeclarations.add('ObjectType', nestjsGraphql);
for (const field of outputType.fields) {
const { location, isList, type } = field.outputType;
const outputTypeName = getOutputTypeName(String(type));
const settings = isCountOutput
? undefined
: model && fieldSettings.get(model.name)?.get(field.name);
const propertySettings = settings?.getPropertyType({
name: outputType.name,
output: true,
});
const isCustomsApplicable =
outputTypeName === model?.fields.find(f => f.name === field.name)?.type;
field.outputType.type = outputTypeName;
const propertyType = castArray(
propertySettings?.name ||
getPropertyType({
location,
type: outputTypeName,
}),
);
const property = propertyStructure({
name: field.name,
isNullable: field.isNullable,
hasQuestionToken: isCountOutput ? true : undefined,
propertyType,
isList,
});
classStructure.properties?.push(property);
if (propertySettings) {
importDeclarations.create({ ...propertySettings });
} else if (propertyType.includes('Decimal')) {
importDeclarations.add('Decimal', '@prisma/client/runtime');
}
// Get graphql type
let graphqlType: string;
const shouldHideField =
settings?.shouldHideField({
name: outputType.name,
output: true,
}) ||
config.decorate.some(
d =>
d.name === 'HideField' &&
d.from === '@nestjs/graphql' &&
d.isMatchField(field.name) &&
d.isMatchType(outputTypeName),
);
const fieldType = settings?.getFieldType({
name: outputType.name,
output: true,
});
if (fieldType && isCustomsApplicable && !shouldHideField) {
graphqlType = fieldType.name;
importDeclarations.create({ ...fieldType });
} else {
const graphqlImport = getGraphqlImport({
config,
sourceFile,
fileType,
location,
isId: false,
typeName: outputTypeName,
getSourceFile,
});
graphqlType = graphqlImport.name;
let referenceName = propertyType[0];
if (location === 'enumTypes') {
referenceName = last(referenceName.split(' ')) as string;
}
if (
graphqlImport.specifier &&
!importDeclarations.has(graphqlImport.name) &&
((graphqlImport.name !== outputType.name && !shouldHideField) ||
(shouldHideField && referenceName === graphqlImport.name))
) {
importDeclarations.set(graphqlImport.name, {
namedImports: [{ name: graphqlImport.name }],
moduleSpecifier: graphqlImport.specifier,
});
}
}
ok(property.decorators, 'property.decorators is undefined');
if (shouldHideField) {
importDeclarations.add('HideField', nestjsGraphql);
property.decorators.push({ name: 'HideField', arguments: [] });
} else {
// Generate `@Field()` decorator
property.decorators.push({
name: 'Field',
arguments: [
isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`,
JSON5.stringify({
...settings?.fieldArguments(),
nullable: Boolean(field.isNullable),
}),
],
});
if (isCustomsApplicable) {
for (const options of settings || []) {
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);
}
}
}
}
eventEmitter.emitSync('ClassProperty', property, {
location,
isList,
propertyType,
});
}
sourceFile.set({
statements: [...importDeclarations.toStatements(), classStructure],
});
}