hyperformula
Version:
HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas
113 lines • 6.25 kB
JavaScript
/**
* @license
* Copyright (c) 2025 Handsoncode. All rights reserved.
*/
import { ArraySizePredictor } from "./ArraySize.mjs";
import { CellContentParser } from "./CellContentParser.mjs";
import { ClipboardOperations } from "./ClipboardOperations.mjs";
import { Config } from "./Config.mjs";
import { CrudOperations } from "./CrudOperations.mjs";
import { DateTimeHelper } from "./DateTimeHelper.mjs";
import { DependencyGraph } from "./DependencyGraph/index.mjs";
import { SheetSizeLimitExceededError } from "./errors.mjs";
import { Evaluator } from "./Evaluator.mjs";
import { Exporter } from "./Exporter.mjs";
import { GraphBuilder } from "./GraphBuilder.mjs";
import { UIElement } from "./i18n/index.mjs";
import { ArithmeticHelper } from "./interpreter/ArithmeticHelper.mjs";
import { FunctionRegistry } from "./interpreter/FunctionRegistry.mjs";
import { Interpreter } from "./interpreter/Interpreter.mjs";
import { LazilyTransformingAstService } from "./LazilyTransformingAstService.mjs";
import { buildColumnSearchStrategy } from "./Lookup/SearchStrategy.mjs";
import { NamedExpressions } from "./NamedExpressions.mjs";
import { NumberLiteralHelper } from "./NumberLiteralHelper.mjs";
import { Operations } from "./Operations.mjs";
import { buildLexerConfig, ParserWithCaching, Unparser } from "./parser/index.mjs";
import { Serialization } from "./Serialization.mjs";
import { findBoundaries, validateAsSheet } from "./Sheet.mjs";
import { EmptyStatistics, Statistics, StatType } from "./statistics/index.mjs";
import { UndoRedo } from "./UndoRedo.mjs";
export class BuildEngineFactory {
static buildFromSheets(sheets, configInput = {}, namedExpressions = []) {
const config = new Config(configInput);
return this.buildEngine(config, sheets, namedExpressions);
}
static buildFromSheet(sheet, configInput = {}, namedExpressions = []) {
const config = new Config(configInput);
const newsheetprefix = config.translationPackage.getUITranslation(UIElement.NEW_SHEET_PREFIX) + '1';
return this.buildEngine(config, {
[newsheetprefix]: sheet
}, namedExpressions);
}
static buildEmpty(configInput = {}, namedExpressions = []) {
return this.buildEngine(new Config(configInput), {}, namedExpressions);
}
static rebuildWithConfig(config, sheets, namedExpressions, stats) {
return this.buildEngine(config, sheets, namedExpressions, stats);
}
static buildEngine(config, sheets = {}, inputNamedExpressions = [], stats = config.useStats ? new Statistics() : new EmptyStatistics()) {
stats.start(StatType.BUILD_ENGINE_TOTAL);
const namedExpressions = new NamedExpressions();
const functionRegistry = new FunctionRegistry(config);
const lazilyTransformingAstService = new LazilyTransformingAstService(stats);
const dependencyGraph = DependencyGraph.buildEmpty(lazilyTransformingAstService, config, functionRegistry, namedExpressions, stats);
const columnSearch = buildColumnSearchStrategy(dependencyGraph, config, stats);
const sheetMapping = dependencyGraph.sheetMapping;
const addressMapping = dependencyGraph.addressMapping;
for (const sheetName in sheets) {
if (Object.prototype.hasOwnProperty.call(sheets, sheetName)) {
const sheet = sheets[sheetName];
validateAsSheet(sheet);
const boundaries = findBoundaries(sheet);
if (boundaries.height > config.maxRows || boundaries.width > config.maxColumns) {
throw new SheetSizeLimitExceededError();
}
const sheetId = sheetMapping.addSheet(sheetName);
addressMapping.autoAddSheet(sheetId, boundaries);
}
}
const parser = new ParserWithCaching(config, functionRegistry, sheetMapping.get);
lazilyTransformingAstService.parser = parser;
const unparser = new Unparser(config, buildLexerConfig(config), sheetMapping.fetchDisplayName, namedExpressions);
const dateTimeHelper = new DateTimeHelper(config);
const numberLiteralHelper = new NumberLiteralHelper(config);
const arithmeticHelper = new ArithmeticHelper(config, dateTimeHelper, numberLiteralHelper);
const cellContentParser = new CellContentParser(config, dateTimeHelper, numberLiteralHelper);
const arraySizePredictor = new ArraySizePredictor(config, functionRegistry);
const operations = new Operations(config, dependencyGraph, columnSearch, cellContentParser, parser, stats, lazilyTransformingAstService, namedExpressions, arraySizePredictor);
const undoRedo = new UndoRedo(config, operations);
lazilyTransformingAstService.undoRedo = undoRedo;
const clipboardOperations = new ClipboardOperations(config, dependencyGraph, operations);
const crudOperations = new CrudOperations(config, operations, undoRedo, clipboardOperations, dependencyGraph, columnSearch, parser, cellContentParser, lazilyTransformingAstService, namedExpressions);
const exporter = new Exporter(config, namedExpressions, sheetMapping.fetchDisplayName, lazilyTransformingAstService);
const serialization = new Serialization(dependencyGraph, unparser, exporter);
const interpreter = new Interpreter(config, dependencyGraph, columnSearch, stats, arithmeticHelper, functionRegistry, namedExpressions, serialization, arraySizePredictor, dateTimeHelper);
stats.measure(StatType.GRAPH_BUILD, () => {
const graphBuilder = new GraphBuilder(dependencyGraph, columnSearch, parser, cellContentParser, stats, arraySizePredictor);
graphBuilder.buildGraph(sheets, stats);
});
inputNamedExpressions.forEach(entry => {
crudOperations.ensureItIsPossibleToAddNamedExpression(entry.name, entry.expression, entry.scope);
crudOperations.operations.addNamedExpression(entry.name, entry.expression, entry.scope, entry.options);
});
const evaluator = new Evaluator(config, stats, interpreter, lazilyTransformingAstService, dependencyGraph, columnSearch);
evaluator.run();
stats.end(StatType.BUILD_ENGINE_TOTAL);
return {
config,
stats,
dependencyGraph,
columnSearch,
parser,
unparser,
cellContentParser,
evaluator,
lazilyTransformingAstService,
crudOperations,
exporter,
namedExpressions,
serialization,
functionRegistry
};
}
}