@aws-amplify/appsync-modelgen-plugin
Version:
239 lines • 12.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppSyncModelTypeScriptVisitor = void 0;
const visitor_plugin_common_1 = require("@graphql-codegen/visitor-plugin-common");
const typescript_declaration_block_1 = require("../languages/typescript-declaration-block");
const appsync_visitor_1 = require("./appsync-visitor");
const change_case_1 = require("change-case");
class AppSyncModelTypeScriptVisitor extends appsync_visitor_1.AppSyncModelVisitor {
constructor() {
super(...arguments);
this.SCALAR_TYPE_MAP = {
String: 'string',
Int: 'number',
Float: 'number',
Boolean: 'boolean',
ID: 'string',
};
this.IMPORT_STATEMENTS = [
'import { ModelInit, MutableModel, PersistentModelConstructor } from "@aws-amplify/datastore";',
'import { initSchema } from "@aws-amplify/datastore";',
'',
'import { schema } from "./schema";',
];
this.BASE_DATASTORE_IMPORT = new Set(['ModelInit', 'MutableModel']);
this.TS_IGNORE_DATASTORE_IMPORT = new Set(['LazyLoading', 'LazyLoadingDisabled']);
this.MODEL_META_FIELD_NAME = '__modelMeta__';
}
generate() {
const shouldUseModelNameFieldInHasManyAndBelongsTo = false;
const shouldImputeKeyForUniDirectionalHasMany = true;
const shouldUseFieldsInAssociatedWithInHasOne = true;
this.processDirectives(shouldUseModelNameFieldInHasManyAndBelongsTo, shouldImputeKeyForUniDirectionalHasMany, shouldUseFieldsInAssociatedWithInHasOne);
const imports = this.generateImports();
const enumDeclarations = Object.values(this.enumMap)
.map(enumObj => this.generateEnumDeclarations(enumObj))
.join('\n\n');
const modelDeclarations = Object.values(this.modelMap)
.map(typeObj => this.generateModelDeclaration(typeObj))
.join('\n\n');
const nonModelDeclarations = Object.values(this.nonModelMap)
.map(typeObj => this.generateModelDeclaration(typeObj, true, false))
.join('\n\n');
const modelInitialization = this.generateModelInitialization([...Object.values(this.modelMap), ...Object.values(this.nonModelMap)]);
const modelExports = this.generateExports(Object.values(this.modelMap));
return [imports, enumDeclarations, modelDeclarations, nonModelDeclarations, modelInitialization, modelExports].join('\n\n');
}
generateImports() {
return this.IMPORT_STATEMENTS.join('\n');
}
generateEnumDeclarations(enumObj, exportEnum = false) {
const enumDeclarations = new typescript_declaration_block_1.TypeScriptDeclarationBlock()
.asKind('enum')
.withName((0, change_case_1.pascalCase)(this.getEnumName(enumObj)))
.withEnumValues(enumObj.values)
.export(exportEnum);
return enumDeclarations.string;
}
generateModelMetaData(modelObj) {
const modelName = this.generateModelTypeDeclarationName(modelObj);
const modelDeclarations = new typescript_declaration_block_1.TypeScriptDeclarationBlock()
.asKind('type')
.withName(`${modelName}MetaData`)
.export(false);
let readOnlyFieldNames = [];
modelObj.fields.forEach((field) => {
if (field.isReadOnly) {
readOnlyFieldNames.push(`'${field.name}'`);
}
});
if (readOnlyFieldNames.length) {
modelDeclarations.addProperty('readOnlyFields', readOnlyFieldNames.join(' | '));
return modelDeclarations.string;
}
return '';
}
generateModelMetaDataType(modelObj) {
let readOnlyFieldNames = [];
modelObj.fields.forEach((field) => {
if (field.isReadOnly) {
readOnlyFieldNames.push(`'${field.name}'`);
}
});
let modelMetaFields = [];
modelMetaFields.push(`identifier: ${this.constructIdentifier(modelObj)};`);
if (readOnlyFieldNames.length) {
modelMetaFields.push(`readOnlyFields: ${readOnlyFieldNames.join(' | ')};`);
}
const result = ['{', (0, visitor_plugin_common_1.indentMultiline)(modelMetaFields.join('\n')), '}'];
return result.join('\n');
}
constructIdentifier(modelObj) {
const primaryKeyField = modelObj.fields.find(f => f.primaryKeyInfo);
const { primaryKeyType, sortKeyFields } = primaryKeyField.primaryKeyInfo;
switch (primaryKeyType) {
case appsync_visitor_1.CodeGenPrimaryKeyType.ManagedId:
this.BASE_DATASTORE_IMPORT.add('ManagedIdentifier');
return `ManagedIdentifier<${modelObj.name}, '${primaryKeyField.name}'>`;
case appsync_visitor_1.CodeGenPrimaryKeyType.OptionallyManagedId:
this.BASE_DATASTORE_IMPORT.add('OptionallyManagedIdentifier');
return `OptionallyManagedIdentifier<${modelObj.name}, '${primaryKeyField.name}'>`;
case appsync_visitor_1.CodeGenPrimaryKeyType.CustomId:
const identifierFields = [primaryKeyField.name, ...sortKeyFields.map(f => f.name)].filter(f => f);
const identifierFieldsStr = identifierFields.length === 1 ? `'${identifierFields[0]}'` : `[${identifierFields.map(fieldStr => `'${fieldStr}'`).join(', ')}]`;
if (identifierFields.length > 1) {
this.BASE_DATASTORE_IMPORT.add('CompositeIdentifier');
return `CompositeIdentifier<${modelObj.name}, ${identifierFieldsStr}>`;
}
this.BASE_DATASTORE_IMPORT.add('CustomIdentifier');
return `CustomIdentifier<${modelObj.name}, ${identifierFieldsStr}>`;
}
}
generateModelDeclaration(modelObj, isDeclaration = true, isModelType = true) {
const modelName = this.generateModelTypeDeclarationName(modelObj);
const eagerModelDeclaration = new typescript_declaration_block_1.TypeScriptDeclarationBlock().asKind('type').withName(`Eager${modelName}`);
const lazyModelDeclaration = new typescript_declaration_block_1.TypeScriptDeclarationBlock().asKind('type').withName(`Lazy${modelName}`);
let readOnlyFieldNames = [];
let modelMetaDataFormatted;
let modelMetaDataDeclaration = '';
if (isModelType && this.isCustomPKEnabled()) {
this.BASE_DATASTORE_IMPORT.add(this.MODEL_META_FIELD_NAME);
const modelMetaDataType = this.generateModelMetaDataType(modelObj);
eagerModelDeclaration.addProperty(`[${this.MODEL_META_FIELD_NAME}]`, modelMetaDataType, undefined, 'DEFAULT', {
readonly: true,
optional: false,
});
lazyModelDeclaration.addProperty(`[${this.MODEL_META_FIELD_NAME}]`, modelMetaDataType, undefined, 'DEFAULT', {
readonly: true,
optional: false,
});
}
modelObj.fields.forEach((field) => {
const fieldName = this.getFieldName(field);
eagerModelDeclaration.addProperty(fieldName, this.getNativeType(field), undefined, 'DEFAULT', {
readonly: true,
optional: field.isList ? field.isListNullable : field.isNullable,
});
lazyModelDeclaration.addProperty(fieldName, this.getNativeType(field, { lazy: true }), undefined, 'DEFAULT', {
readonly: true,
optional: !this.isModelType(field) && (field.isList ? field.isListNullable : field.isNullable),
});
if (field.isReadOnly) {
readOnlyFieldNames.push(`'${field.name}'`);
}
});
if (isModelType && !this.isCustomPKEnabled()) {
modelMetaDataFormatted = `, ${modelName}MetaData`;
modelMetaDataDeclaration = readOnlyFieldNames.length > 0 ? modelMetaDataFormatted : '';
}
const conditionalType = `export declare type ${modelName} = LazyLoading extends LazyLoadingDisabled ? Eager${modelName} : Lazy${modelName}`;
const modelVariableBase = `export declare const ${modelName}: (new (init: ModelInit<${modelName}${modelMetaDataDeclaration}>) => ${modelName})`;
const modelVariable = modelVariableBase +
(Object.values(this.modelMap).includes(modelObj)
? ` & {\n copyOf(source: ${modelName}, mutator: (draft: MutableModel<${modelName}${modelMetaDataDeclaration}>) => MutableModel<${modelName}${modelMetaDataDeclaration}> | void): ${modelName};\n}`
: '');
return [eagerModelDeclaration.string, lazyModelDeclaration.string, conditionalType, modelVariable].join('\n\n');
}
generateModelInitialization(models, includeTypeInfo = true) {
if (models.length === 0) {
return '';
}
const modelClasses = models
.map(model => [this.generateModelImportName(model), this.generateModelImportAlias(model)])
.map(([importName, aliasName]) => {
return importName === aliasName ? importName : `${importName}: ${aliasName}`;
});
const initializationResult = ['const', '{', modelClasses.join(', '), '}', '=', 'initSchema(schema)'];
if (includeTypeInfo) {
const typeInfo = models
.map(model => {
return [this.generateModelImportName(model), this.generateModelTypeDeclarationName(model)];
})
.map(([importName, modelDeclarationName]) => `${importName}: PersistentModelConstructor<${modelDeclarationName}>;`);
const typeInfoStr = ['{', (0, visitor_plugin_common_1.indentMultiline)(typeInfo.join('\n')), '}'].join('\n');
initializationResult.push('as', typeInfoStr);
}
return `${initializationResult.join(' ')};`;
}
generateExports(modelsOrEnum) {
const exportStr = modelsOrEnum
.map(model => {
if (model.type === 'model') {
const modelClassName = this.generateModelImportAlias(model);
const exportClassName = this.getModelName(model);
return modelClassName !== exportClassName ? `${modelClassName} as ${exportClassName}` : modelClassName;
}
return model.name;
})
.join(',\n');
return ['export {', (0, visitor_plugin_common_1.indentMultiline)(exportStr), '};'].join('\n');
}
generateModelTypeDeclarationName(model) {
return `${this.getModelName(model)}Model`;
}
generateModelImportAlias(model) {
return this.getModelName(model);
}
generateModelImportName(model) {
return this.getModelName(model);
}
generateModelExportName(model) {
return this.getModelName(model);
}
getListType(typeStr, field) {
let type = typeStr;
if (field.isNullable) {
type = `(${type} | null)`;
}
return `${type}[]`;
}
getNativeType(field, options) {
const typeName = field.type;
const isNullable = field.isList ? field.isListNullable : field.isNullable;
const nullableTypeUnion = isNullable ? ' | null' : '';
if (this.isModelType(field)) {
const modelType = this.modelMap[typeName];
const typeNameStr = this.generateModelTypeDeclarationName(modelType);
if (options === null || options === void 0 ? void 0 : options.lazy) {
if (field.isList) {
this.TS_IGNORE_DATASTORE_IMPORT.add('AsyncCollection');
}
else {
this.TS_IGNORE_DATASTORE_IMPORT.add('AsyncItem');
}
return `${field.isList ? 'AsyncCollection' : 'AsyncItem'}<${typeNameStr}${!field.isList && isNullable ? ' | undefined' : ''}>`;
}
return (field.isList ? this.getListType(typeNameStr, field) : typeNameStr) + nullableTypeUnion;
}
let nativeType = super.getNativeType(field);
if (this.isEnumType(field)) {
const baseEnumType = `keyof typeof ${this.getEnumName(this.enumMap[typeName])}`;
const enumType = field.isList ? `Array<${baseEnumType}>` : baseEnumType;
nativeType = `${nativeType} | ${enumType}`;
}
nativeType = nativeType + nullableTypeUnion;
return nativeType;
}
}
exports.AppSyncModelTypeScriptVisitor = AppSyncModelTypeScriptVisitor;
//# sourceMappingURL=appsync-typescript-visitor.js.map