libs/nx/src/executors/journal/utils/create-journal-record.ts
Transform Markdown Frontmatter Data & Decorators into individual objects for further processing.
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 { readMarkdownAttributes } from '../../shared/metadata-utils/read-markdown'; import { JournalLogger } from '../journal.impl'; 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: JournalLogger ): 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 }; }