UNPKG

libmodulor

Version:

A TypeScript library to create platform-agnostic applications

254 lines (253 loc) 10.4 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; import { inject, injectable } from 'inversify'; import { APP_SRC_DIR_NAME, APP_SRC_UCDS_DIR_NAME, UC_DEF_FILE_NAME_SUFFIX, UC_DEF_SUFFIX, UC_INPUT_FIELD_PATTERN, UC_INPUT_SUFFIX, UC_OPI_SUFFIX, UC_POLICY_SUFFIX_FULL, } from '../../../convention.js'; import { optsAllSet } from '../../opts.js'; import { initOutputItem, } from '../../UCDefASTParser.js'; const ERR_UCD_CONST_NAME = (name) => `The UCD const name '${name}' must follow the convention`; const ERR_UCD_FILE_SUFFIX = (fileName) => `The file '${fileName}' must end with ${UC_DEF_FILE_NAME_SUFFIX}`; const ERR_UCD_IMPORTS_EXTERNAL_ALLOWED = (aliasPrefix, allowed, text) => `External imports must be an alias '${aliasPrefix}*' or be one of ${allowed} (Got ${text})`; const ERR_UCD_IMPORTS_INTERNAL_MAX_DEPTH = (maxDepth) => `Internal imports must not be deeper than ${maxDepth}`; const ERR_UCD_INPUT_FIELD_PATTERN = () => `The input field def must match ${UC_INPUT_FIELD_PATTERN}`; const ERR_UCD_INPUT_TYPE_NAME = (ucName) => `The input type must be named ${ucName}${UC_INPUT_SUFFIX}`; const ERR_UCD_OPI_TYPE_NAME = (ucName, idx) => `The OPI must be named ${ucName}${UC_OPI_SUFFIX}${idx}`; // TODO : Improve the check* methods that look a little bit hacky let UCDefSourcesChecker = class UCDefSourcesChecker { fsManager; logger; ucDefASTParser; opts; output; constructor(fsManager, logger, ucDefASTParser) { this.fsManager = fsManager; this.logger = logger; this.ucDefASTParser = ucDefASTParser; this.output = { items: [], }; } async exec({ ctx }) { const { appPath, opts } = ctx; // TODO : Consider removing this and using profiling when needed const startTime = Date.now(); this.logger.debug('UCDefSourceChecker starting', { appPath, startTime, }); this.opts = optsAllSet(opts); const ucdsPaths = this.ucdsAbsolutePaths(ctx); this.logger.debug('UCDefSourceChecker initializing'); await this.ucDefASTParser.init(this.opts, ucdsPaths); this.logger.debug('UCDefSourceChecker transpiling'); await this.ucDefASTParser.transpile(); this.logger.debug('UCDefSourceChecker processing files'); this.output.items = await this.processFiles(appPath, ucdsPaths); const duration = Date.now() - startTime; this.logger.debug('UCDefSourceChecker done', { appPath, duration: `${duration} ms`, }); return this.output; } checkConstName(name, item) { item.constName = { err: name.endsWith(UC_DEF_SUFFIX) ? null : ERR_UCD_CONST_NAME(name), value: name, }; } checkImport(name, item) { const { external, internal } = this.opts.source.imports; const isInternal = name.startsWith(internal.startChar); if (isInternal) { const { maxDepth } = internal; item.internalImports?.push({ err: !name.startsWith(maxDepth) ? null : ERR_UCD_IMPORTS_INTERNAL_MAX_DEPTH(maxDepth), value: name, }); return; } const { aliasPrefix, allowed } = external; item.externalImports?.push({ err: name.startsWith(aliasPrefix) || allowed.includes(name) ? null : ERR_UCD_IMPORTS_EXTERNAL_ALLOWED(aliasPrefix, allowed, name), value: name, }); } checkInputType(name, fields, item) { item.ioI = { err: null, value: name, }; item.ioIFields = fields; for (const f of item.ioIFields) { const parts = f.value.split(':'); if (parts.length !== 2) { continue; } const [_name, type] = parts; // trim because we process `name: Type` if the code is linted correctly f.err = type?.trim().match(new RegExp(UC_INPUT_FIELD_PATTERN)) !== null ? null : ERR_UCD_INPUT_FIELD_PATTERN(); } } checkMainStep(lifecycle, step, item) { switch (lifecycle) { case 'client': if (!item.lifecycleClientSteps) { item.lifecycleClientSteps = []; } item.lifecycleClientSteps.push({ err: null, value: step }); break; case 'server': if (!item.lifecycleServerSteps) { item.lifecycleServerSteps = []; } item.lifecycleServerSteps.push({ err: null, value: step }); break; default: ((_) => { })(lifecycle); } } checkMetadata(metadata, item) { item.metadataAction = { err: null, value: metadata.action, }; item.metadataBeta = { err: null, value: metadata.beta?.toString() ?? '', }; item.metadataIcon = { err: null, value: metadata.icon, }; item.metadataName = { err: null, value: metadata.name, }; item.metadataNew = { err: null, value: metadata.new?.toString() ?? '', }; item.metadataSensitive = { err: null, value: metadata.sensitive?.toString() ?? '', }; } checkOPIType(name, fields, idx, item) { const key = `ioOPI${idx}`; item[key] = { err: null, value: name, }; const fieldsKey = `${key}Fields`; item[fieldsKey] = fields; for (const f of item[fieldsKey]) { // There are no specific validation rules for OPIx for now f.err = null; } } checkPolicy(lifecycle, name, item) { switch (lifecycle) { case 'client': item.lifecycleClientPolicy = { err: null, value: name.replaceAll(UC_POLICY_SUFFIX_FULL, ''), }; break; case 'server': item.lifecycleServerPolicy = { err: null, value: name.replaceAll(UC_POLICY_SUFFIX_FULL, ''), }; break; default: ((_) => { })(lifecycle); } } async processFiles(appPath, paths) { const res = await Promise.all(paths.map(async (path) => { const item = initOutputItem(); this.logger.trace('UCDefSourceChecker processing file', { path, }); await this.ucDefASTParser.processFile(path, (name) => this.checkImport(name, item), (name) => this.checkConstName(name, item), (name, fields) => this.checkInputType(name, fields, item), (name, fields, idx) => this.checkOPIType(name, fields, idx, item), (lifecycle, step) => this.checkMainStep(lifecycle, step, item), (lifecycle, name) => this.checkPolicy(lifecycle, name, item), (value) => this.checkMetadata(value, item)); const relativePath = path.replaceAll(appPath, ''); item.filePath = { err: relativePath.endsWith(UC_DEF_FILE_NAME_SUFFIX) ? null : ERR_UCD_FILE_SUFFIX(relativePath), value: relativePath, }; if (item.ioI) { item.ioI.err = item.ioI.value === `${item.metadataName?.value}${UC_INPUT_SUFFIX}` ? null : ERR_UCD_INPUT_TYPE_NAME(item.metadataName?.value ?? '-'); } if (item.ioOPI0) { item.ioOPI0.err = item.ioOPI0.value === `${item.metadataName?.value}${UC_OPI_SUFFIX}0` ? null : ERR_UCD_OPI_TYPE_NAME(item.metadataName?.value ?? '-', 0); } if (item.ioOPI1) { item.ioOPI1.err = item.ioOPI1.value === `${item.metadataName?.value}${UC_OPI_SUFFIX}1` ? null : ERR_UCD_OPI_TYPE_NAME(item.metadataName?.value ?? '-', 1); } return item; })); const errors = []; for (const analysis of res) { for (const [_k, analysisVal] of Object.entries(analysis)) { if (!analysisVal) { continue; } const asArr = Array.isArray(analysisVal) ? analysisVal : [analysisVal]; for (const val of asArr) { const { err } = val; if (err) { errors.push(err); } } } } if (errors.length > 0) { throw new Error(errors[0]); } return res; } ucdsAbsolutePaths(ctx) { return ctx.ucdRefs.map(({ fileName }) => this.fsManager.path(ctx.appPath, APP_SRC_DIR_NAME, APP_SRC_UCDS_DIR_NAME, fileName)); } }; UCDefSourcesChecker = __decorate([ injectable(), __param(0, inject('FSManager')), __param(1, inject('Logger')), __param(2, inject('UCDefASTParser')), __metadata("design:paramtypes", [Object, Object, Object]) ], UCDefSourcesChecker); export { UCDefSourcesChecker };