UNPKG

@o3r/rules-engine

Version:

This module provides a rule engine that can be executed on your Otter application to customize your application (translations, placeholders and configs) based on a json file generated by your CMS.

134 lines • 8.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ngGenerateRulesEngineToComponent = void 0; const tslib_1 = require("tslib"); const schematics_1 = require("@angular-devkit/schematics"); const schematics_2 = require("@o3r/schematics"); const ast_utils_1 = require("@schematics/angular/utility/ast-utils"); const change_1 = require("@schematics/angular/utility/change"); const ts = tslib_1.__importStar(require("typescript")); const rulesEngineProperties = ['rulesEngineService']; const checkRulesEngine = (componentPath) => (tree) => { if (!componentPath || !tree.exists(componentPath)) { throw new schematics_2.O3rCliError(`Unable to add rules-engine: component "${componentPath || ''}" does not exist`); } const componentSourceFile = ts.createSourceFile(componentPath, tree.readText(componentPath), ts.ScriptTarget.ES2020, true); const o3rClassDeclaration = componentSourceFile.statements.find((statement) => ts.isClassDeclaration(statement)); const constructorDeclaration = o3rClassDeclaration.members.find((classElement) => ts.isConstructorDeclaration(classElement)); const constructorPropertiesModifiers = [ts.SyntaxKind.PrivateKeyword, ts.SyntaxKind.ProtectedKeyword, ts.SyntaxKind.PublicKeyword]; if (o3rClassDeclaration.members.some((classElement) => ts.isPropertyDeclaration(classElement) && ts.isIdentifier(classElement.name) && rulesEngineProperties.includes(classElement.name.escapedText.toString())) || constructorDeclaration?.parameters?.some((parameterElement) => parameterElement.modifiers?.some((mod) => constructorPropertiesModifiers.includes(mod.kind)) && rulesEngineProperties.includes(parameterElement.name.escapedText.toString()))) { throw new schematics_2.O3rCliError(`Unable to add rules-engine: component "${componentPath}" already has at least one of these properties: ${rulesEngineProperties.join(', ')}.`); } }; /** * Generate the code to enable rules-engine on a component * @param options */ function ngGenerateRulesEngineToComponentFn(options) { const componentPath = options.path; return (0, schematics_1.chain)([ checkRulesEngine(componentPath), (tree) => { const { standalone } = (0, schematics_2.getO3rComponentInfoOrThrowIfNotFound)(tree, componentPath); const generateFiles = () => { const projectName = options.projectName || (0, schematics_2.getLibraryNameFromPath)(componentPath) || ''; const imports = [ { from: '@angular/core', importNames: ['inject', 'OnInit', 'OnDestroy'] }, { from: '@o3r/core', importNames: ['computeItemIdentifier'] }, { from: '@o3r/rules-engine', importNames: ['RulesEngineRunnerService', ...(standalone ? ['RulesEngineRunnerModule'] : []) ] } ]; let sourceFile = ts.createSourceFile(componentPath, tree.readText(componentPath), ts.ScriptTarget.ES2020, true); const recorder = tree.beginUpdate(componentPath); const changes = imports.reduce((acc, { importNames, from }) => acc.concat(importNames.map((importName) => (0, ast_utils_1.insertImport)(sourceFile, componentPath, importName, from))), []); (0, change_1.applyToUpdateRecorder)(recorder, changes); tree.commitUpdate(recorder); sourceFile = ts.createSourceFile(componentPath, tree.readText(componentPath), ts.ScriptTarget.ES2020, true); const result = ts.transform(sourceFile, [ ...(standalone ? [(0, schematics_2.addImportsIntoComponentDecoratorTransformerFactory)(['RulesEngineRunnerModule'])] : []), (ctx) => (rootNode) => { const { factory } = ctx; const visit = (node) => { if (ts.isClassDeclaration(node)) { const implementsClauses = node.heritageClauses?.find((heritageClause) => heritageClause.token === ts.SyntaxKind.ImplementsKeyword); const interfaceToImplements = (0, schematics_2.generateImplementsExpressionWithTypeArguments)('OnInit, OnDestroy'); const deduplicateHeritageClauses = (clauses) => clauses.filter((h, i) => !clauses.slice(i + 1).some((h2) => h2.kind === h.kind && h2.expression.escapedText === h.expression.escapedText)); const newImplementsClauses = implementsClauses ? factory.updateHeritageClause(implementsClauses, deduplicateHeritageClauses([...implementsClauses.types, ...interfaceToImplements])) : factory.createHeritageClause(ts.SyntaxKind.ImplementsKeyword, [...interfaceToImplements]); const heritageClauses = Array.from(node.heritageClauses ?? []) .filter((h) => h.token !== ts.SyntaxKind.ImplementsKeyword) .concat(newImplementsClauses); const newModifiers = [] .concat(ts.getDecorators(node) || []) .concat(ts.getModifiers(node) || []); const propertiesToAdd = (0, schematics_2.generateClassElementsFromString)(` private readonly componentName = computeItemIdentifier('${node.name?.escapedText}', '${projectName}'); private readonly rulesEngineService = inject(RulesEngineRunnerService, {optional: true}); `); const newNgOnInit = (0, schematics_2.getSimpleUpdatedMethod)(node, factory, 'ngOnInit', (0, schematics_2.generateBlockStatementsFromString)(` if (this.rulesEngineService) { this.rulesEngineService.enableRuleSetFor(this.componentName); } `)); const newNgOnDestroy = (0, schematics_2.getSimpleUpdatedMethod)(node, factory, 'ngOnDestroy', (0, schematics_2.generateBlockStatementsFromString)(` if (this.rulesEngineService) { this.rulesEngineService.disableRuleSetFor(this.componentName); } `)); const newMembers = node.members .filter((classElement) => !((0, schematics_2.findMethodByName)('ngOnInit')(classElement) || (0, schematics_2.findMethodByName)('ngOnDestroy')(classElement))) .concat(propertiesToAdd, newNgOnInit, newNgOnDestroy) .sort(schematics_2.sortClassElement); return factory.updateClassDeclaration(node, newModifiers, node.name, node.typeParameters, heritageClauses, newMembers); } return ts.visitEachChild(node, visit, ctx); }; return ts.visitNode(rootNode, visit); }, schematics_2.fixStringLiterals ]); const printer = ts.createPrinter({ removeComments: false, newLine: ts.NewLineKind.LineFeed }); tree.overwrite(componentPath, printer.printFile(result.transformed[0])); return tree; }; const updateModuleRule = () => { const moduleFilePath = options.path.replace(/component.ts$/, 'module.ts'); const moduleSourceFile = ts.createSourceFile(moduleFilePath, tree.readText(moduleFilePath), ts.ScriptTarget.ES2020, true); const recorder = tree.beginUpdate(moduleFilePath); const changes = (0, ast_utils_1.addImportToModule)(moduleSourceFile, moduleFilePath, 'RulesEngineRunnerModule', '@o3r/rules-engine'); (0, change_1.applyToUpdateRecorder)(recorder, changes); tree.commitUpdate(recorder); }; return (0, schematics_1.chain)([ standalone ? schematics_1.noop : updateModuleRule, generateFiles, options.skipLinter ? (0, schematics_1.noop)() : (0, schematics_2.applyEsLintFix)() ]); } ]); } /** * Generate the code to enable rules-engine on a component * @param options */ exports.ngGenerateRulesEngineToComponent = (0, schematics_2.createOtterSchematic)(ngGenerateRulesEngineToComponentFn); //# sourceMappingURL=index.js.map