perf(editor): Memoize locale translate calls during actions generation (#6773)

performance(editor): Memoize locale translate calls during actions generation

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
OlegIvaniv 2023-07-28 10:22:30 +02:00 committed by GitHub
parent dd6a4c956a
commit 2d47e8dc4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,11 +1,10 @@
import { startCase } from 'lodash-es'; import { memoize, startCase } from 'lodash-es';
import type { import type {
INodePropertyCollection, INodePropertyCollection,
INodePropertyOptions, INodePropertyOptions,
INodeProperties, INodeProperties,
INodeTypeDescription, INodeTypeDescription,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { deepCopy } from 'n8n-workflow';
import { CUSTOM_API_CALL_KEY } from '@/constants'; import { CUSTOM_API_CALL_KEY } from '@/constants';
import type { ActionTypeDescription, SimplifiedNodeType, ActionsRecord } from '@/Interface'; import type { ActionTypeDescription, SimplifiedNodeType, ActionsRecord } from '@/Interface';
@ -13,6 +12,13 @@ import { i18n } from '@/plugins/i18n';
const PLACEHOLDER_RECOMMENDED_ACTION_KEY = 'placeholder_recommended'; const PLACEHOLDER_RECOMMENDED_ACTION_KEY = 'placeholder_recommended';
function translate(...args: Parameters<typeof i18n.baseText>) {
return i18n.baseText(...args);
}
// Memoize the translation function so we don't have to re-translate the same string
// multiple times when generating the actions
const cachedBaseText = memoize(translate, (...args) => JSON.stringify(args));
const customNodeActionsParsers: { const customNodeActionsParsers: {
[key: string]: ( [key: string]: (
matchedProperty: INodeProperties, matchedProperty: INodeProperties,
@ -26,7 +32,7 @@ const customNodeActionsParsers: {
(categoryItem): ActionTypeDescription => ({ (categoryItem): ActionTypeDescription => ({
...getNodeTypeBase(nodeTypeDescription), ...getNodeTypeBase(nodeTypeDescription),
actionKey: categoryItem.value as string, actionKey: categoryItem.value as string,
displayName: i18n.baseText('nodeCreator.actionsCategory.onEvent', { displayName: cachedBaseText('nodeCreator.actionsCategory.onEvent', {
interpolate: { event: startCase(categoryItem.name) }, interpolate: { event: startCase(categoryItem.name) },
}), }),
description: categoryItem.description || '', description: categoryItem.description || '',
@ -40,8 +46,8 @@ const customNodeActionsParsers: {
function getNodeTypeBase(nodeTypeDescription: INodeTypeDescription, label?: string) { function getNodeTypeBase(nodeTypeDescription: INodeTypeDescription, label?: string) {
const isTrigger = nodeTypeDescription.group.includes('trigger'); const isTrigger = nodeTypeDescription.group.includes('trigger');
const category = isTrigger const category = isTrigger
? i18n.baseText('nodeCreator.actionsCategory.triggers') ? cachedBaseText('nodeCreator.actionsCategory.triggers')
: i18n.baseText('nodeCreator.actionsCategory.actions'); : cachedBaseText('nodeCreator.actionsCategory.actions');
return { return {
name: nodeTypeDescription.name, name: nodeTypeDescription.name,
group: nodeTypeDescription.group, group: nodeTypeDescription.group,
@ -101,7 +107,7 @@ function triggersCategory(nodeTypeDescription: INodeTypeDescription): ActionType
{ {
...getNodeTypeBase(nodeTypeDescription), ...getNodeTypeBase(nodeTypeDescription),
actionKey: PLACEHOLDER_RECOMMENDED_ACTION_KEY, actionKey: PLACEHOLDER_RECOMMENDED_ACTION_KEY,
displayName: i18n.baseText('nodeCreator.actionsCategory.onNewEvent', { displayName: cachedBaseText('nodeCreator.actionsCategory.onNewEvent', {
interpolate: { event: nodeTypeDescription.displayName.replace('Trigger', '').trimEnd() }, interpolate: { event: nodeTypeDescription.displayName.replace('Trigger', '').trimEnd() },
}), }),
description: '', description: '',
@ -125,7 +131,7 @@ function triggersCategory(nodeTypeDescription: INodeTypeDescription): ActionType
actionKey: categoryItem.value as string, actionKey: categoryItem.value as string,
displayName: displayName:
categoryItem.action ?? categoryItem.action ??
i18n.baseText('nodeCreator.actionsCategory.onEvent', { cachedBaseText('nodeCreator.actionsCategory.onEvent', {
interpolate: { event: startCase(categoryItem.name) }, interpolate: { event: startCase(categoryItem.name) },
}), }),
description: categoryItem.description || '', description: categoryItem.description || '',
@ -175,7 +181,7 @@ function resourceCategories(nodeTypeDescription: INodeTypeDescription): ActionTy
return { return {
...getNodeTypeBase( ...getNodeTypeBase(
nodeTypeDescription, nodeTypeDescription,
`${resourceOption.name} ${i18n.baseText('nodeCreator.actionsCategory.actions')}`, `${resourceOption.name} ${cachedBaseText('nodeCreator.actionsCategory.actions')}`,
), ),
actionKey: operationOption.value as string, actionKey: operationOption.value as string,
description: operationOption?.description ?? '', description: operationOption?.description ?? '',
@ -234,7 +240,7 @@ export function useActionsGenerator() {
} }
function generateMergedNodesAndActions(nodeTypes: INodeTypeDescription[]) { function generateMergedNodesAndActions(nodeTypes: INodeTypeDescription[]) {
const visibleNodeTypes = deepCopy(nodeTypes); const visibleNodeTypes = [...nodeTypes];
const actions: ActionsRecord<typeof mergedNodes> = {}; const actions: ActionsRecord<typeof mergedNodes> = {};
const mergedNodes: SimplifiedNodeType[] = []; const mergedNodes: SimplifiedNodeType[] = [];