@silexlabs/grapesjs-data-source
Version:
Grapesjs Data Source
333 lines • 13 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFilterFromToken = getFilterFromToken;
exports.fromStored = fromStored;
exports.tokenToField = tokenToField;
exports.propertyToField = propertyToField;
exports.expressionToFields = expressionToFields;
exports.getExpressionResultType = getExpressionResultType;
exports.getTokenOptions = getTokenOptions;
exports.optionsToOptionsForm = optionsToOptionsForm;
exports.getOptionObject = getOptionObject;
exports.buildArgs = buildArgs;
const dataSourceManager_1 = require("./dataSourceManager");
const utils_1 = require("../utils");
const state_1 = require("./state");
const lit_1 = require("lit");
/**
* Add missing methonds to the filter
* When filters are stored they lose their methods
* @throws Error if the filter is not found
*/
function getFilterFromToken(token, filters) {
if (token.type !== 'filter')
throw new Error('Token is not a filter');
const filter = filters.find(filter => filter.id === token.id);
if (!filter) {
console.error('Filter not found', token);
throw new Error(`Filter ${token.id} not found`);
}
return Object.assign(Object.assign(Object.assign({}, token), filter), {
// Keep the options as they are stored
options: token.options });
}
/**
* Get the token from its stored form
* @throws Error if the token type is not found
*/
function fromStored(token, componentId) {
var _a;
// Handle invalid tokens - return null or throw a descriptive error
if (!token || typeof token !== 'object') {
console.error('Invalid token: not an object', token);
throw new Error('Invalid token: expected an object');
}
if (!token.type) {
console.error('Invalid token: missing type property', token);
throw new Error('Invalid token: missing type property');
}
switch (token.type) {
case 'filter': {
if (token.optionsForm)
return token;
const original = (0, dataSourceManager_1.getFilters)().find(filter => filter.id === token.id);
if (!original) {
console.error('Filter not found', token);
throw new Error(`Filter ${token.id} not found`);
}
return Object.assign(Object.assign(Object.assign({}, original), token), { type: 'filter' });
}
case 'property': {
if (token.optionsForm)
return token;
const field = propertyToField(token, componentId);
if (!field) {
console.error('Field not found for token', token);
throw new Error(`Field ${token.fieldId} not found`);
}
return Object.assign(Object.assign(Object.assign({}, (_a = getTokenOptions(field)) !== null && _a !== void 0 ? _a : {}), token), { type: 'property', propType: 'field' });
}
case 'state':
return token;
default:
console.error('Unknown token type (reading type)', token);
throw new Error('Unknown token type');
}
}
/**
* Get the type corresponding to a token
*/
function tokenToField(token, prev, component) {
var _a, _b, _c;
switch (token.type) {
case 'filter': {
try {
const filter = getFilterFromToken(token, (0, dataSourceManager_1.getFilters)());
try {
if (filter.validate(prev)) {
return filter.output(prev, (_a = filter.options) !== null && _a !== void 0 ? _a : {});
}
}
catch (e) {
console.warn('Filter validate error:', e, { token, prev });
return null;
}
return null;
}
catch (_d) {
// FIXME: notify user
console.error('Error while getting filter result type', { token, prev, component });
return null;
}
}
case 'property':
try {
return propertyToField(token, component.getId());
}
catch (_e) {
// FIXME: notify user
console.error('Error while getting property result type', { token, component });
return null;
}
case 'state': {
const parent = (0, state_1.getParentByPersistentId)(token.componentId, component);
if (!parent) {
console.warn('Component not found for state', token);
const manager = (0, dataSourceManager_1.getManager)();
manager.editor.runCommand('notifications:add', {
type: 'error',
group: utils_1.NOTIFICATION_GROUP,
message: `Component not found for state: ${token.storedStateId}`,
componentId: component.getId(),
});
return null;
}
const expression = (_b = (0, state_1.getState)(parent, token.storedStateId, token.exposed)) === null || _b === void 0 ? void 0 : _b.expression;
if (!expression) {
console.warn('State is not defined on component', { component: parent, token });
const manager = (0, dataSourceManager_1.getManager)();
manager.editor.runCommand('notifications:add', {
type: 'error',
group: utils_1.NOTIFICATION_GROUP,
message: `State '${token.storedStateId}' is not defined on component '${parent.getName() || parent.get('id')}'`,
componentId: parent.getId(),
});
return null;
}
try {
const field = getExpressionResultType(expression, parent);
return field ? Object.assign(Object.assign({}, field), { kind: (_c = token.forceKind) !== null && _c !== void 0 ? _c : field.kind }) : null;
}
catch (_f) {
// FIXME: notify user
console.error('Error while getting expression result type in tokenToField', { expression, parent, component, token, prev });
return null;
}
}
default:
console.error('Unknown token type (reading type)', token);
throw new Error('Unknown token type');
}
}
/**
* Get the type corresponding to a property
* @throws Error if the type is not found
*/
function propertyToField(property, componentId) {
var _a, _b;
const manager = (0, dataSourceManager_1.getManager)();
const allTypes = manager.cachedTypes.length > 0 ? manager.cachedTypes : [];
const typeNames = [];
// Check each typeId and handle missing types
for (const typeId of property.typeIds) {
const type = allTypes.find(type => type.id === typeId && (!property.dataSourceId || type.dataSourceId === property.dataSourceId));
if (!type) {
// Show notification for missing type, similar to main branch DataTree.getType
manager.editor.runCommand('notifications:add', {
type: 'error',
group: utils_1.NOTIFICATION_GROUP,
message: `Type not found ${(_a = property.dataSourceId) !== null && _a !== void 0 ? _a : ''}.${typeId}`,
componentId,
});
console.error(`Type not found ${(_b = property.dataSourceId) !== null && _b !== void 0 ? _b : ''}.${typeId}`);
// Continue processing other types instead of throwing
}
else {
typeNames.push(type.label);
}
}
const args = property.options ? Object.entries(property.options).map(([name, value]) => ({
typeId: 'JSON', // FIXME: Why is this hardcoded?
name,
defaultValue: value, // FIXME: Why is this value, it should keep the initial default
})) : undefined;
return {
id: property.fieldId,
label: typeNames.length > 0 ? typeNames.join(', ') : property.label,
typeIds: property.typeIds,
kind: property.kind,
dataSourceId: property.dataSourceId,
arguments: args,
previewIndex: property.previewIndex,
};
}
/**
* Evaluate the types of each token in an expression
*/
function expressionToFields(expression, component) {
// Resolve type of the expression 1 step at a time
let prev = null;
const unknownField = {
id: 'unknown',
label: 'unknown',
typeIds: [],
kind: 'scalar',
};
return expression.map((token) => {
try {
const field = tokenToField(fromStored(token, component.getId()), prev, component);
if (!field) {
console.warn('Type not found for token in expressionToFields', { token, expression });
return unknownField;
}
prev = field;
return field;
}
catch (_a) {
// FIXME: notify user
console.error('Error while getting expression result type in expressionToFields', { expression, component, token, prev });
return unknownField;
}
});
}
/**
* Evaluate an expression to a type
* This is used to validate expressions and for autocompletion
* @throws Error if the token type is not found
*/
function getExpressionResultType(expression, component) {
// Resolve type of the expression 1 step at a time
return expression.reduce((prev, token) => {
return tokenToField(fromStored(token, component.getId()), prev, component);
}, null);
}
/**
* Get the options of a token
*/
function getTokenOptions(field) {
if (field.arguments && field.arguments.length > 0) {
return {
optionsForm: optionsToOptionsForm(field.arguments.map((arg) => ({ name: arg.name, value: arg.defaultValue }))),
options: field.arguments.reduce((options, arg) => {
options[arg.name] = arg.defaultValue;
return options;
}, {}),
};
}
return null;
}
/**
* Get the options of a token or a field
*/
function optionsToOptionsForm(arr) {
return (selected, input, options) => {
return (0, lit_1.html) `
${arr.map((obj) => {
var _a, _b;
const value = (_b = (_a = options[obj.name]) !== null && _a !== void 0 ? _a : obj.value) !== null && _b !== void 0 ? _b : '';
return (0, lit_1.html) `<label>${obj.name}</label><input type="text" name=${obj.name} .value=${value}>`;
})}
`;
};
}
/**
* Utility function to shallow compare two objects
* Used to compare options of tree items
*/
function getOptionObject(option1, option2) {
// Handle the case where one or both are undefined or empty
if (!option1 && !option2)
return { error: false, result: undefined };
if (isEmpty(option1) && isEmpty(option2))
return { error: false, result: undefined };
// Handle the case where one is undefined or empty and the other is not
if (!option1 || !option2)
return { error: true, result: undefined };
if (isEmpty(option1) || isEmpty(option2))
return { error: true, result: undefined };
const keys1 = Object.keys(option1);
const keys2 = Object.keys(option2);
if (keys1.length !== keys2.length) {
return { error: true, result: undefined };
}
for (const key of keys1) {
if (option1[key] !== option2[key]) {
return { error: true, result: undefined };
}
}
return { error: false, result: option1 };
}
function isJson(str) {
if (typeof str !== 'string')
return false;
if (str.length === 0)
return false;
try {
JSON.parse(str);
}
catch (_a) {
return false;
}
return true;
}
function isEmpty(value) {
if (value === null || typeof value === 'undefined')
return true;
const isString = typeof value === 'string';
const isJsonString = isString && isJson(value);
if (isString && !isJsonString)
return value === '';
const json = isJsonString ? JSON.parse(value) : value;
if (json === null)
return true;
if (Array.isArray(json))
return json.length === 0;
if (typeof json === 'object')
return Object.values(json).filter(v => !!v).length === 0;
return false;
}
function buildArgs(options) {
const args = options ? `(${Object
.keys(options)
.map(key => ({ key, value: options[key] }))
.filter(({ value }) => !isEmpty(value))
.map(({ key, value }) => {
//return typeof value === 'string' && !isJson(value) ? `${key}: "${value}"` : `${key}: ${value}`
return `${key}: ${value}`;
})
.join(', ')})` : '';
// Valid args for GraphQL canot be just ()
const validArgs = args === '()' ? '' : args;
return validArgs;
}
//# sourceMappingURL=token.js.map