Skip to content
This repository was archived by the owner on Jun 10, 2024. It is now read-only.
This repository was archived by the owner on Jun 10, 2024. It is now read-only.

Perf: Pre-merge global elements at normalization time #99

@matwilko

Description

@matwilko

Since global elements will always end up being applied to every file (if they are going to be processed at all), then all the global elements { !files } are going to need to be merged again and again for every file config fetched (that's not cached).

Rather than repeat this for potentially every file, I'd propose that during the normalization process, all global elements be pre-merged backwards through the array and eliminated.

This obviously has the benefit of each individual file having to process fewer array elements when its config is fetched, and the number of merge operations therefore required. And depending on the merge function of individual schemas, it could significantly reduce the work they have to do overall if the global elements apply things that reduce the amount of processing required.

It does have some upfront cost, but we're using a O(n) operation once at normalisation time to reduce the n in a O(n * fileCount) at getConfig time.

This process could also be used to gather all the global ignores and eliminate those elements as well, doing the work that this.ignores usually has to do for "free".

So, to be more concrete, here's some very quick and dirty example code of what this might look like:

function preMergeGlobals(configs) {
    const [...preMergedConfigs, globalIgnores] = [...preMergeGlobalsInner(configs.reverse())];
    this.length = 0;
    this.push(...preMergedConfigs.reverse());
    
    // TODO: Figure out where to put the global ignores so that `this.ignores` doesn't have to traverse the array
}

function* preMergeGlobalsInner(reversedConfigs) {
    const globalIgnores = [];
    let currentGlobalToMerge = undefined;
    for (const config in reversedConfigs) {

        if (!config.files && !config.ignores) {
            currentGlobalToMerge = currentGlobalToMerge
                ? this[ConfigArraySymbol.schema].merge(config, currentGlobalToMerge) // We're iterating backwards, so apply the earlier config on top of this one
                : config;
        } else if (config.ignores && Object.keys(config).length === 1) {
            globalIgnores.push(config.ignores);
        } else {
            yield currentGlobalToMerge
                ? this[ConfigArraySymbol.schema].merge(config, currentGlobalToMerge) // We're iterating backwards, so apply the earlier config on top of this one
                : config;
        }
    }

    yield { ignores: globalIgnores.reverse().flat() };
}

Happy to put together a PR with properly thought out code for this if you think it's worth implementing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions