Skip to content

Commit

Permalink
Fix events definition
Browse files Browse the repository at this point in the history
  • Loading branch information
vividviolet committed Jan 15, 2025
1 parent 8aa5787 commit 2b5a7f8
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 58 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"build-consumer": "loom build && ./scripts/build-consumer.sh",
"build-consumer-spin": "loom build && ./scripts/build-consumer-spin.sh",
"generate-definition": "node ./scripts/generator.js",
"generate-definition:admin": "yarn generate-definition packages/ui-extensions/src/surfaces/admin/components.d.ts ../web/areas/clients/admin-web/app/shared/domains/extensibility/ui-extensions/components ../web/areas/clients/admin-web/app/shared/domains/extensibility/ui-extensions/definitionTemplate.txt",
"clean": "git clean -xdf ./packages; rm -rf ./build",
"predeploy": "yarn build",
"deploy": "changeset publish",
Expand Down
3 changes: 3 additions & 0 deletions packages/ui-extensions/src/surfaces/admin/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2794,6 +2794,9 @@ export type ButtonBaseProps = Required<
| 'target'
| 'href'
| 'download'
| 'onBlur'
| 'onClick'
| 'onFocus'
>
>;
export interface ButtonProps extends ButtonBaseProps {
Expand Down
139 changes: 81 additions & 58 deletions scripts/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const allSymbolNodes = new Map();
const REQUIRED_TYPE = 'Required';
const EXTRACT_TYPE = 'Extract';
const PICK_TYPE = 'Pick';
const FUNCTION_REGEX = /^on([A-Z][a-zA-Z]+$)/;

function generate({ componentName, checker, outputRootFolder, templatePath }) {
const definition = constructFullDefinitionFromSymbol({
Expand All @@ -30,12 +31,18 @@ function generate({ componentName, checker, outputRootFolder, templatePath }) {
: path.join(componentName);

if (!definition) {
console.warn(`Failed to generate definition for ${componentName}`);
console.warn(
`Cannot generate definition for ${componentName}. This might not be a real component`,
);
return;
}

// TEMPORARY - Fix for missing id until we add it to the interface for components
definition.properties.id = { type: "'string'" };
// Strip out empty events
if (!Object.keys(definition.events).length) {
delete definition.events;
}

fs.mkdir(outputPath, { recursive: true }, (err) => {
if (err) throw err;
Expand Down Expand Up @@ -76,7 +83,7 @@ function parseComplexType({ type, checker }) {
symbolName: type.expression.escapedText,
checker,
});
return fullDefininition?.properties;
return fullDefininition;
}
} else if (type.kind === ts.SyntaxKind.TypeReference) {
if (type.typeName.escapedText === REQUIRED_TYPE) {
Expand All @@ -93,7 +100,7 @@ function parseComplexType({ type, checker }) {
symbolName: referenceType.typeName.escapedText,
checker,
});
return definition.properties;
return definition;
}
} else if (type.typeName.escapedText === EXTRACT_TYPE) {
const parsedExpression = getParsedExpression({
Expand All @@ -119,10 +126,10 @@ function parseDeclarations({ declarations, checker }) {
checker,
});

combinedDeclarations = {
...combinedDeclarations,
...(heritageDeclations || {}),
};
combinedDeclarations = deepMergeDefinition(
combinedDeclarations,
heritageDeclations,
);
}

const hasComplexExtend =
Expand All @@ -131,17 +138,14 @@ function parseDeclarations({ declarations, checker }) {
if (hasComplexExtend) {
const parsedTypes = declaration.types.reduce((acc2, type) => {
// Parse expression
const parsedExpression = parseComplexType({
const parsedDefinition = parseComplexType({
type,
checker,
});
return { ...acc2, ...(parsedExpression || {}) };
}, {});

return {
...combinedDeclarations,
...(parsedTypes || {}),
};
return deepMergeDefinition(acc2, parsedDefinition);
}, {});
return deepMergeDefinition(combinedDeclarations, parsedTypes);
}

// Handle other complex type
Expand All @@ -153,10 +157,7 @@ function parseDeclarations({ declarations, checker }) {
type: declaration.type,
checker,
});
return {
...combinedDeclarations,
...(parsedExpression || {}),
};
return deepMergeDefinition(combinedDeclarations, parsedExpression);
}

return combinedDeclarations;
Expand All @@ -181,14 +182,15 @@ function constructFullDefinitionFromSymbol({ symbolName, checker }) {
}

const nodeType = checker.getDeclaredTypeOfSymbol(symbol);

let all =
let events = {};
const symbolProperties =
getChildDefinition({
symbol,
nodeType,
checker,
}) || {};

let all = symbolProperties;
const declarations = symbol.getDeclarations();

if (declarations) {
Expand All @@ -197,48 +199,51 @@ function constructFullDefinitionFromSymbol({ symbolName, checker }) {
checker,
});

all = { ...all, ...(declarationsDefinitions || {}) };
all = {
...all,
...(declarationsDefinitions.properties || {}),
};
events = declarationsDefinitions.events || {};
}

const definition = Object.keys(all).reduce((acc, key) => {
if (!all[key]) {
return acc;
}

if (all[key].generic) {
acc.generic = true;
return acc;
}
const definition = Object.keys(all).reduce(
(acc, key) => {
if (!all[key]) {
return acc;
}

if (all[key].slot) {
if (!Object.prototype.hasOwnProperty.call(acc, 'slots')) {
acc.slots = [];
if (all[key].generic) {
acc.generic = true;
return acc;
}

acc.slots.push(`'${key}'`);
return acc;
}
if (all[key].slot) {
if (!Object.prototype.hasOwnProperty.call(acc, 'slots')) {
acc.slots = [];
}

// If this is an event, push to the events array and skip adding it to prop
if (typeof all[key].event === 'string') {
if (!Object.prototype.hasOwnProperty.call(acc, 'events')) {
acc.events = [];
acc.slots.push(`'${key}'`);
return acc;
}
acc.events.push(`'${all[key].event}'`);
return acc;
}

if (!Object.prototype.hasOwnProperty.call(acc, 'properties')) {
acc.properties = {};
}
// If this is an event, push to the events array and skip adding it to prop
if (typeof all[key].event === 'string') {
// This is just set to an empty placeholder because by defaults most events handlers don't need special logic
acc.events[key] = {};
return acc;
}

acc.properties[key] = all[key];
acc.properties[key] = all[key];

return acc;
}, {});
return acc;
},
{ properties: {}, events },
);

// Save definition for reuse
allSymbolNodes.set(symbolName, { node, definition });
// console.log('JSON -->', JSON.stringify(definition));
// console.log('END constructFullDefinitionFromSymbol -->', symbolName);
return definition;
}

Expand Down Expand Up @@ -381,7 +386,7 @@ function getDefinition({
checker,
});
if (parsedExpression) {
return parsedExpression[name];
return parsedExpression.properties[name] || parsedExpression.events[name];
}
}

Expand Down Expand Up @@ -409,9 +414,9 @@ function getDefinition({
}

if (isFunction) {
const matchHandler = name.match(/^on([A-Z][a-zA-Z]+$)/);
const matchHandler = name.match(FUNCTION_REGEX);
if (matchHandler && typeof matchHandler[1] === 'string') {
return { event: matchHandler[1].toLowerCase() };
return { event: name };
}
}

Expand Down Expand Up @@ -570,7 +575,8 @@ function getParsedExpression({ type, checker }) {

switch (type.typeName.escapedText) {
case PICK_TYPE: {
const props = {};
const properties = {};
const events = {};
const referenceDefinition = constructFullDefinitionFromSymbol({
symbolName: typeReference.typeName.escapedText,
checker,
Expand All @@ -584,10 +590,15 @@ function getParsedExpression({ type, checker }) {
}

expressionType.types.forEach((t) => {
props[t.literal.text] = referenceDefinition.properties[t.literal.text];
if (referenceDefinition.properties[t.literal.text]) {
properties[t.literal.text] =
referenceDefinition.properties[t.literal.text];
} else if (referenceDefinition.events[t.literal.text]) {
events[t.literal.text] = referenceDefinition.events[t.literal.text];
}
});

return props;
return { properties, events };
}
case EXTRACT_TYPE: {
if (
Expand All @@ -606,27 +617,39 @@ function getParsedExpression({ type, checker }) {
return;
}

const props = {
const properties = {
[propIndex]: {
type: referenceDefinition.properties[propIndex].type,
default: referenceDefinition.properties[propIndex].default,
values: expressionType.types.map((t) => `'${t.literal.text}'`),
},
};
return props;
return { properties, events: {} };
} else if (expressionType.kind === ts.SyntaxKind.TypeLiteral) {
// Handles inline declarations like `Extract<T, {type?: string}>`
// In this case the typeReference we passed in already has the correct interface
const referenceDefinition = constructFullDefinitionFromSymbol({
symbolName: typeReference.typeName.escapedText,
checker,
});
return referenceDefinition.properties;
return referenceDefinition;
}
}
}
}

function deepMergeDefinition(org = {}, addition = {}) {
if (!addition) {
return org;
}
const { properties = {}, events = {} } = org;
const merged = {
properties: { ...properties, ...(addition.properties || {}) },
events: { ...events, ...(addition.events || {}) },
};
return merged;
}

const filePath = process.argv[2];
const outputRootFolder = process.argv[3];
const templatePath = process.argv[4];
Expand Down

0 comments on commit 2b5a7f8

Please sign in to comment.