@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
JavaScript
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
;