@pega/custom-dx-components
Version:
Utility for building custom UI components
385 lines (354 loc) • 12.1 kB
JavaScript
import path from 'path';
import fs from 'fs';
import chalk from 'chalk';
import inquirer from 'inquirer';
import Ajv from 'ajv';
import {
COMPONENT_SCHEMA } from '../../constants.js';
import {
getComponentDirectoryPath,
getComponents,
getOOTBComponents,
addDebugLog } from '../../util.js';
const ajv = new Ajv({ allErrors: true });
const combinationTypes = ['PAGE', 'CASE'];
const allowedSubtypes = [
...new Set([
...COMPONENT_SCHEMA.subtype.field.map(({ value }) => value),
...COMPONENT_SCHEMA.subtype.template.map(({ value }) => value),
...COMPONENT_SCHEMA.subtype.widget.map(({ value }) => value)
])
];
// additional subtypes are manually added below
const allowedOverrideSubtypes = [
...new Set([
...COMPONENT_SCHEMA.subtype.field.map(({ value }) => value),
...COMPONENT_SCHEMA.subtype.template.map(({ value }) => value),
...COMPONENT_SCHEMA.subtype.widget.map(({ value }) => value)
]), "ACTION", "DATA_CAPTURE", "SUMMARY", "CASEVIEW", "DATAVIEW", "LIST"
];
const schema = {
type: 'object',
properties: {
baseComponentName: { type: 'string' },
componentKey: { type: 'string', pattern: '^[A-Za-z0-9]+_[A-Za-z0-9]+_[A-Za-z0-9]+$' },
name: { type: 'string', pattern: '^[A-Za-z0-9]+_[A-Za-z0-9]+_[A-Za-z0-9]+$' },
label: { type: 'string' },
description: { type: 'string' },
organization: { type: 'string' },
version: { type: 'string' },
library: { type: 'string' },
allowedApplications: { type: 'array' },
type: { type: 'string', enum: COMPONENT_SCHEMA.type.map(({ value }) => value) },
subtype: {
anyOf: [{ type: 'string' }, { type: 'array' }],
enum: allowedSubtypes
},
icon: { type: 'string' },
properties: {
type: 'array',
uniqueItems: true,
items: {
anyOf: [
{
type: 'object',
properties: {
name: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' }
},
required: ['name', 'label', 'format']
},
{
type: 'object',
properties: {
format: { type: 'string' },
source: {
type: 'object',
properties: {
name: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' }
},
required: ['name', 'label', 'format']
},
cascadeElements: {
type: 'array',
uniqueItems: true,
items: {
anyOf: [
{
type: 'object',
properties: {
key: { type: 'string'},
name: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' }
},
required: ['key','name', 'label', 'format']
}
]
}
},
},
required: ['format']
},
{
type: 'object',
properties: {
label: { type: 'string' },
format: { type: 'string' },
properties: {
type: 'array',
uniqueItems: true,
items: {
anyOf: [
{
type: 'object',
properties: {
name: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' }
},
required: ['name', 'label', 'format']
},
{
type: 'object',
properties: {
variant: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' }
},
required: ['variant', 'label', 'format']
},
{
type: 'object',
properties: {
format: { type: 'string' },
elements: {
type: 'array',
uniqueItems: true,
items: {
anyOf: [
{
type: 'object',
properties: {
key: { type: 'string' },
name: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' },
variant: { type: 'string' }
},
required: ['key', 'format', 'label']
}
]
}
}
},
required: ['format', 'elements']
},
]
}
}
},
required: ['label', 'format'],
}
]
}
},
defaultConfig: { type: 'object' }
},
required: ['componentKey', 'name', 'description', 'type', 'subtype', 'properties'],
additionalProperties: true
};
const overrideSchema = {
type: 'object',
properties: {
baseComponentName: { type: 'string' },
componentKey: { type: 'string', pattern: '^[A-Za-z0-9]+$' },
name: { type: 'string', pattern: '^[A-Za-z0-9]+$' },
label: { type: 'string' },
description: { type: 'string' },
organization: { type: 'string' },
version: { type: 'string' },
library: { type: 'string' },
allowedApplications: { type: 'array' },
type: { type: 'string', enum: COMPONENT_SCHEMA.type.map(({ value }) => value) },
subtype: {
anyOf: [{ type: 'string' }, { type: 'array' }],
enum: allowedOverrideSubtypes
},
icon: { type: 'string' },
properties: {
type: 'array',
uniqueItems: true,
items: {
anyOf: [
{
type: 'object',
properties: {
name: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' }
},
required: ['name', 'label', 'format']
},
{
type: 'object',
properties: {
format: { type: 'string' },
source: {
type: 'object',
properties: {
name: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' }
},
required: ['name', 'label', 'format']
},
cascadeElements: {
type: 'array',
uniqueItems: true,
items: {
anyOf: [
{
type: 'object',
properties: {
key: { type: 'string'},
name: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' }
},
required: ['key','name', 'label', 'format']
}
]
}
},
},
required: ['format']
},
{
type: 'object',
properties: {
label: { type: 'string' },
format: { type: 'string' },
properties: {
type: 'array',
uniqueItems: true,
items: {
anyOf: [
{
type: 'object',
properties: {
name: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' }
},
required: ['name', 'label', 'format']
},
{
type: 'object',
properties: {
variant: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' }
},
required: ['variant', 'label', 'format']
},
{
type: 'object',
properties: {
format: { type: 'string' },
elements: {
type: 'array',
uniqueItems: true,
items: {
anyOf: [
{
type: 'object',
properties: {
key: { type: 'string' },
name: { type: 'string' },
label: { type: 'string' },
format: { type: 'string' },
variant: { type: 'string' }
},
required: ['key', 'format', 'label']
}
]
}
}
},
required: ['format', 'elements']
},
]
}
}
},
required: ['label', 'format'],
}
]
}
},
defaultConfig: { type: 'object' }
},
required: ['name', 'type', 'subtype', 'properties', 'componentKey'],
additionalProperties: true
};
const validateSchema = ajv.compile(schema);
const validateOverideSchema = ajv.compile(overrideSchema);
export default async (option) => {
// addDebugLog("validate","", "");
let componentToValidate = null;
if (option.params && option.params[3]) {
componentToValidate = option.params[3];
} else if (option && option.task === 'validateSchema') {
const localComponents = await getComponents();
const answer = await inquirer.prompt({
name: 'component',
type: 'rawlist',
message: 'Select component to validate',
choices: localComponents
});
componentToValidate = answer.component;
} else if (option.name) {
componentToValidate = option.value;
} else {
componentToValidate = option;
}
const componentDirectory = await getComponentDirectoryPath(componentToValidate);
let configFileName = "config.json";
const configFile = path.join(componentDirectory, configFileName);
let configData;
try {
configData = fs.readFileSync(path.resolve(configFile), 'utf8');
}
catch (err) {
// for now, if now file just end
console.log(`${chalk.bold.yellow('Config file not availabe for: ' + componentToValidate)}`);
return;
}
//const data = JSON.parse(fs.readFileSync(path.resolve(configFile), 'utf8'));
let data;
try {
data = JSON.parse(configData);
}
catch (er) {
throw new Error(`Invalid schema json in config.json: ${er}`);
}
const valid = validateSchema(data);
if (valid) {
if (
componentToValidate &&
!/^[A-Za-z0-9]+_[A-Za-z0-9]+_[A-Za-z0-9]+$/.test(componentToValidate)
) {
const ootbComponents = await getOOTBComponents();
if (ootbComponents.includes(componentToValidate)) {
throw new Error(`Invalid: Error occurred in validating ${componentToValidate}`);
}
}
console.log(`${chalk.bold.green(componentToValidate)} schema is valid`);
} else {
throw new Error(`Invalid: ${ajv.errorsText(validateSchema.errors)}`);
}
};