libs/nx/src/executors/journal/utils/create-journal-record.ts
Transform Markdown Frontmatter Data & Decorators into individual objects for further processing.
Properties |
description | |
Type |
string
|
id | |
Type |
string
|
import { BuildingBlockJournalRecord } from '@allianz/taly-core/schemas';
import { tsquery } from '@phenomnomnominal/tsquery';
import { existsSync } from 'fs';
import { readFile } from 'fs/promises';
import path, { basename } from 'path';
import ts from 'typescript';
import { ExecutorLogger } from '../../shared/logger';
import { readMarkdownAttributes } from '../../shared/metadata-utils/read-markdown';
import { BbMarkdownAttributes, validateMarkdownAttributes } from './bb-markdown-validators';
import { readAclResourceDescriptor } from './read-acl-resource-descriptor';
import { transformLobValues } from './transform-lob-values';
/**
* Transform Markdown Frontmatter Data & Decorators
* into individual objects for further processing.
*/
export interface BusinessEventData {
id: string;
description: string;
}
export async function createJournalRecord(
dir: string,
packageName: string,
logger: ExecutorLogger
): Promise<BuildingBlockJournalRecord> {
// from the given folder path use the folder name to guess the markdown & component file names.
const buildingBlockBaseName = basename(dir);
const markdownFile = path.join(dir, buildingBlockBaseName + '.md');
const componentFile = path.join(dir, buildingBlockBaseName + '.component.ts');
const moduleFile = path.join(dir, buildingBlockBaseName + '.module.ts');
const aclDeclarations = path.join(dir, buildingBlockBaseName + '.acl.yml');
// prepare all data we are interested in
const mdAttributes: BbMarkdownAttributes = await readMarkdownAttributes(markdownFile);
const mdValidationErrors = validateMarkdownAttributes(mdAttributes);
if (mdValidationErrors.length) {
logger.error(' - Markdown attribute validation errors:');
logger.error(
' ' +
mdValidationErrors
.map((error) => `${error.error}${error.details ? ` (${error.details})` : ''}`)
.join(', ')
);
}
let selector: string | undefined;
if (!existsSync(componentFile)) {
logger.error(` - Could not find component file for "${buildingBlockBaseName}"`);
} else {
const componentContent = (await readFile(componentFile)).toString();
const selectorNodes = tsquery(
componentContent,
'Decorator:has(Identifier[name="Component"]) PropertyAssignment:has(Identifier[name="selector"]) StringLiteral'
) as ts.StringLiteral[];
if (selectorNodes.length === 0) {
logger.error(
` - Could not determine component selector in file "${basename(componentFile)}"`
);
}
selector = selectorNodes[0]?.text as string | undefined;
}
let moduleName: string | undefined;
if (!existsSync(moduleFile)) {
logger.error(` - Could not find module file for "${buildingBlockBaseName}"`);
} else {
const moduleContent = (await readFile(moduleFile)).toString();
const moduleNodes = tsquery(
moduleContent,
'ClassDeclaration:has(Decorator Identifier[name="NgModule"])'
) as ts.ClassDeclaration[];
if (moduleNodes.length === 0) {
logger.error(` - Could not determine module name in file "${basename(moduleFile)}"`);
}
moduleName = moduleNodes[0]?.name?.escapedText as string | undefined;
}
const aclDescriptor = readAclResourceDescriptor(aclDeclarations, { root: dir, logger });
// save & return it in an object
const id = buildingBlockBaseName;
const isDeprecated = mdAttributes.deprecated !== undefined;
const isDesignApproved = mdAttributes['design-approved']?.toString().toLowerCase() === 'true';
return {
id: id,
title: mdAttributes.title,
lob: transformLobValues(mdAttributes.lob),
channel: mdAttributes.channel,
aclDescriptor,
selector: selector,
validations: mdAttributes.validations,
businessEvents: mdAttributes['business-events'],
module: moduleName,
package: packageName,
designApproved: isDesignApproved,
deprecated: isDeprecated,
deprecationMessage: isDeprecated ? mdAttributes.deprecated?.trim() : undefined
};
}