diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 97bd59d88d..c447963b21 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -71,6 +71,7 @@ module.exports = { 'unicorn/no-useless-switch-case': 'error', 'unicorn/prefer-switch': 'error', 'unicorn/switch-case-braces': 'error', + 'unicorn/prefer-spread': 'error', 'import/newline-after-import': ['error', { count: 1 }], 'import/first': 'error', 'import/order': [ diff --git a/dev/ts/shared/main.ts b/dev/ts/shared/main.ts index 44962474e3..e65e46a039 100644 --- a/dev/ts/shared/main.ts +++ b/dev/ts/shared/main.ts @@ -264,7 +264,7 @@ function configureStyleFromParameters(parameters: URLSearchParams): void { // Collect style properties to update them later with the bpmn-visualization API logStartup(`Configuring the "Update Style" API from query parameters`); // Only create the StyleUpdate object if some parameters are set - if (Array.from(parameters.keys()).some(key => key.startsWith('style.api.'))) { + if ([...parameters.keys()].some(key => key.startsWith('style.api.'))) { style = { stroke: {}, font: {}, fill: {} }; parameters.get('style.api.stroke.color') && (style.stroke.color = parameters.get('style.api.stroke.color')); diff --git a/src/component/helpers/array-utils.ts b/src/component/helpers/array-utils.ts index ae5a2e4b67..de25e939dc 100644 --- a/src/component/helpers/array-utils.ts +++ b/src/component/helpers/array-utils.ts @@ -53,13 +53,14 @@ export function ensureIsArray(elements: (T | string)[] | T | string, acceptEm * @internal */ export function filter(arrayToFilter: T[], suffix: string, options?: FilterParameter): T[] { - let pattern = ''; + const patterns: string[] = []; if (options?.startingWith) { - pattern = pattern.concat(`^(${options.startingWith}).*`); + patterns.push(`^(${options.startingWith}).*`); } else if (options?.notStartingWith) { - pattern = pattern.concat(`^(?!(${options.notStartingWith})).*`); + patterns.push(`^(?!(${options.notStartingWith})).*`); } - pattern = pattern.concat(`${suffix}$`); + patterns.push(`${suffix}$`); + const pattern = patterns.join(''); return arrayToFilter.filter(element => (options?.ignoreCase ? new RegExp(pattern, 'i').test(element) : new RegExp(pattern).test(element))); } diff --git a/src/component/mxgraph/config/ShapeConfigurator.ts b/src/component/mxgraph/config/ShapeConfigurator.ts index 62f9c766ef..f6b0ad6388 100644 --- a/src/component/mxgraph/config/ShapeConfigurator.ts +++ b/src/component/mxgraph/config/ShapeConfigurator.ts @@ -147,10 +147,10 @@ export default class ShapeConfigurator { // 'this.state.cell.style' = the style applied to the cell: 1st element: style name = bpmn shape name const cell = this.state.cell; // dialect = strictHtml is set means that current node holds an HTML label - let allBpmnClassNames = computeAllBpmnClassNamesOfCell(cell, this.dialect === mxConstants.DIALECT_STRICTHTML); + const allBpmnClassNames = computeAllBpmnClassNamesOfCell(cell, this.dialect === mxConstants.DIALECT_STRICTHTML); const extraCssClasses = this.state.style[BpmnStyleIdentifier.EXTRA_CSS_CLASSES]; if (typeof extraCssClasses == 'string') { - allBpmnClassNames = allBpmnClassNames.concat(extraCssClasses.split(',')); + allBpmnClassNames.push(...extraCssClasses.split(',')); } this.node.setAttribute('class', allBpmnClassNames.join(' ')); diff --git a/src/component/mxgraph/renderer/StyleComputer.ts b/src/component/mxgraph/renderer/StyleComputer.ts index b5bb82fedd..fb96dc3c78 100644 --- a/src/component/mxgraph/renderer/StyleComputer.ts +++ b/src/component/mxgraph/renderer/StyleComputer.ts @@ -58,9 +58,8 @@ export default class StyleComputer { const fontStyleValues = this.computeFontStyleValues(bpmnCell); const labelStyleValues = StyleComputer.computeLabelStyleValues(bpmnCell, labelBounds); - return styles // - .concat(toArrayOfMxGraphStyleEntries([...mainStyleValues, ...fontStyleValues, ...labelStyleValues])) - .join(';'); + styles.push(...toArrayOfMxGraphStyleEntries([...mainStyleValues, ...fontStyleValues, ...labelStyleValues])); + return styles.join(';'); } private computeShapeStyleValues(shape: Shape): Map { diff --git a/src/component/mxgraph/style/style-updater.ts b/src/component/mxgraph/style/style-updater.ts index 18f19a7baa..af6988b44d 100644 --- a/src/component/mxgraph/style/style-updater.ts +++ b/src/component/mxgraph/style/style-updater.ts @@ -34,7 +34,8 @@ export function createNewStyleUpdater(graph: BpmnGraph): StyleUpdater { // The message flow icon is stored in a dedicated Cell, so it must be kept in sync const withCellIdsOfMessageFlowIcons = (bpmnElementIds: string | string[]): string[] => { const cellIds = ensureIsArray(bpmnElementIds); - return cellIds.concat(cellIds.map(id => messageFlowIconId(id))); + cellIds.push(...cellIds.map(id => messageFlowIconId(id))); + return cellIds; }; export class StyleUpdater { diff --git a/src/component/parser/json/converter/ProcessConverter.ts b/src/component/parser/json/converter/ProcessConverter.ts index 001b49358d..4b4996a3d3 100644 --- a/src/component/parser/json/converter/ProcessConverter.ts +++ b/src/component/parser/json/converter/ProcessConverter.ts @@ -75,10 +75,14 @@ const computeSubProcessKind = (processedSemanticType: BpmnSemanticType, bpmnElem } }; -const orderedFlowNodeBpmnTypes: BpmnSemanticType[] = (['adHocSubProcess', 'transaction'] as BpmnSemanticType[]) // specific management for adhoc and transaction sub-processes which are handled with a dedicated ShapeBpmnSubProcessKind +const orderedFlowNodeBpmnTypes: BpmnSemanticType[] = [ + // specific management for adhoc and transaction sub-processes which are handled with a dedicated ShapeBpmnSubProcessKind + 'adHocSubProcess', + 'transaction', // process boundary events afterward as we need its parent activity to be available when building it - .concat(ShapeUtil.flowNodeKinds().filter(kind => kind != ShapeBpmnElementKind.EVENT_BOUNDARY) as BpmnSemanticType[]) - .concat([ShapeBpmnElementKind.EVENT_BOUNDARY]); + ...(ShapeUtil.flowNodeKinds().filter(kind => kind !== ShapeBpmnElementKind.EVENT_BOUNDARY) as BpmnSemanticType[]), + ShapeBpmnElementKind.EVENT_BOUNDARY, +]; function getShapeBpmnElementKind(bpmnSemanticType: BpmnSemanticType): ShapeBpmnElementKind { return ['adHocSubProcess', 'transaction'].includes(bpmnSemanticType as string) ? ShapeBpmnElementKind.SUB_PROCESS : (bpmnSemanticType as ShapeBpmnElementKind); @@ -290,9 +294,7 @@ export default class ProcessConverter { eventDefinitions.set(kind, eventDefinitions.get(kind) + 1); } - return Array.from(eventDefinitions.keys()) - .map(kind => ({ kind, counter: eventDefinitions.get(kind) })) - .filter(eventDefinition => eventDefinition.counter > 0); + return [...eventDefinitions.entries()].filter(([, counter]) => counter > 0).map(([kind, counter]) => ({ kind, counter })); } private buildShapeBpmnSubProcess(bpmnElement: TSubProcess, parentId: string, subProcessKind: ShapeBpmnSubProcessKind, markers: ShapeBpmnMarkerKind[]): ShapeBpmnSubProcess { diff --git a/src/component/registry/bpmn-model-registry.ts b/src/component/registry/bpmn-model-registry.ts index 9fe4e66e46..401f24d5cb 100644 --- a/src/component/registry/bpmn-model-registry.ts +++ b/src/component/registry/bpmn-model-registry.ts @@ -118,8 +118,9 @@ class SearchableModel { private elements = new Map(); constructor(bpmnModel: BpmnModel) { - for (const shapeOrEdge of ([] as (Edge | Shape)[]).concat(bpmnModel.pools, bpmnModel.lanes, bpmnModel.flowNodes, bpmnModel.edges)) + for (const shapeOrEdge of [...bpmnModel.pools, ...bpmnModel.lanes, ...bpmnModel.flowNodes, ...bpmnModel.edges]) { this.elements.set(shapeOrEdge.bpmnElement.id, shapeOrEdge); + } } elementById(id: string): Shape | Edge | undefined { diff --git a/src/component/registry/css-registry.ts b/src/component/registry/css-registry.ts index e6bc0435d6..d0756fbbe9 100644 --- a/src/component/registry/css-registry.ts +++ b/src/component/registry/css-registry.ts @@ -92,7 +92,7 @@ export class CssClassesCache { * @return the registered CSS class names */ getClassNames(bpmnElementId: string): string[] { - return Array.from(this.classNamesByBpmnId.get(bpmnElementId) ?? []); + return this.classNamesByBpmnId.has(bpmnElementId) ? [...this.classNamesByBpmnId.get(bpmnElementId)] : []; } /** @@ -101,7 +101,7 @@ export class CssClassesCache { * @return an array representing the BPMN element IDs. */ getBpmnIds(): string[] { - return Array.from(this.classNamesByBpmnId.keys()); + return [...this.classNamesByBpmnId.keys()]; } /** diff --git a/test/e2e/bpmn.theme.test.ts b/test/e2e/bpmn.theme.test.ts index 6fd6ee15ff..da888f2ca3 100644 --- a/test/e2e/bpmn.theme.test.ts +++ b/test/e2e/bpmn.theme.test.ts @@ -57,7 +57,7 @@ describe('BPMN theme', () => { ); const pageTester = new PageTester({ targetedPage: AvailableTestPages.BPMN_RENDERING, diagramSubfolder: 'theme' }, page); - const useCases = Array.from(styleOptionsPerUseCase.keys()); + const useCases = [...styleOptionsPerUseCase.keys()]; it.each(useCases)(`Use case %s`, async (useCase: string) => { await pageTester.gotoPageAndLoadBpmnDiagram('01.most.bpmn.types.without.label', { diff --git a/test/integration/helpers/html-utils.ts b/test/integration/helpers/html-utils.ts index 349beb20a1..58434088c3 100644 --- a/test/integration/helpers/html-utils.ts +++ b/test/integration/helpers/html-utils.ts @@ -174,8 +174,8 @@ export class HtmlElementLookup { } } -function computeClassValue(bpmnClasses: string[], additionalClasses?: string[]): string { - return bpmnClasses.concat(additionalClasses).filter(Boolean).join(' '); +function computeClassValue(bpmnClasses: string[], additionalClasses: string[] = []): string { + return [...bpmnClasses, ...additionalClasses].filter(Boolean).join(' '); } export function expectSvgEvent(svgGroupElement: HTMLElement): void { diff --git a/test/performance/bpmn.load.performance.test.ts b/test/performance/bpmn.load.performance.test.ts index 450908fd7c..ca661a5c20 100644 --- a/test/performance/bpmn.load.performance.test.ts +++ b/test/performance/bpmn.load.performance.test.ts @@ -54,7 +54,7 @@ afterAll(async () => { const oldData = JSON.parse(oldDataString.substring('const data = '.length, oldDataString.length)) as ChartData; const data = { zoom: oldData.zoom, - load: oldData.load.concat(metricsArray), + load: [...oldData.load, ...metricsArray], }; fs.writeFileSync(performanceDataFilePath, 'const data = ' + JSON.stringify(data)); } catch (error) { diff --git a/test/performance/bpmn.navigation.performance.test.ts b/test/performance/bpmn.navigation.performance.test.ts index 7d4e2e90f5..2f273d8c37 100644 --- a/test/performance/bpmn.navigation.performance.test.ts +++ b/test/performance/bpmn.navigation.performance.test.ts @@ -78,7 +78,7 @@ afterAll(async () => { const oldDataString = fs.readFileSync(performanceDataFilePath, 'utf8'); const oldData = JSON.parse(oldDataString.substring('const data = '.length, oldDataString.length)) as ChartData; const data = { - zoom: oldData.zoom.concat(metricsArray), + zoom: [...oldData.zoom, ...metricsArray], load: oldData.load, }; fs.writeFileSync(performanceDataFilePath, 'const data = ' + JSON.stringify(data)); diff --git a/test/unit/component/mxgraph/shape/render/utils.test.ts b/test/unit/component/mxgraph/shape/render/utils.test.ts index f3ec1f23af..bc39649277 100644 --- a/test/unit/component/mxgraph/shape/render/utils.test.ts +++ b/test/unit/component/mxgraph/shape/render/utils.test.ts @@ -23,7 +23,7 @@ function computeAllPermutations(array: string[]): string[][][] { const permutation = [...array]; const length = permutation.length, - result = [[permutation.slice()]], + result = [[[...permutation]]], c = new Array(length).fill(0); let index = 1, k, @@ -37,7 +37,7 @@ function computeAllPermutations(array: string[]): string[][][] { permutation[k] = p; ++c[index]; index = 1; - result.push([permutation.slice()]); + result.push([[...permutation]]); } else { c[index] = 0; ++index;