• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / ropm / #901

17 May 2024 06:41PM UTC coverage: 90.608%. Remained the same
#901

push

TwitchBronBron
0.10.24

515 of 611 branches covered (84.29%)

Branch coverage included in aggregate %.

633 of 656 relevant lines covered (96.49%)

67.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.82
/src/prefixer/ModuleManager.ts
1
/* eslint-disable @typescript-eslint/consistent-indexed-object-style */
2
import { RopmModule } from './RopmModule';
1✔
3
import * as semver from 'semver';
1✔
4
import type { ModuleDependency } from '../util';
5
import { util } from '../util';
1✔
6

7
export class ModuleManager {
1✔
8
    public modules = [] as RopmModule[];
104✔
9

10
    /**
11
     * A list of all direct dependencies of the host application.
12
     * This is used to pick which version prettier prefixes whenever there's multiple versions required
13
     */
14
    public hostDependencies = [] as ModuleDependency[];
104✔
15

16
    /**
17
     * The absolute path to the rootDir of the host application
18
     */
19
    public hostRootDir!: string;
20

21
    /**
22
     * A list of npm aliases of modules that should not have their source code prefixed during ropm install.
23
     * This is the npm alias name.
24
     */
25
    public noprefixNpmAliases = [] as string[];
104✔
26

27
    /**
28
     * A list of ropm aliases of modules that should not have their source code prefixed during ropm install.
29
     * This is the ropm alias name, and is only resolved later in the process so don't use this unless you know for sure it is populated
30
     */
31
    private noprefixRopmAliases = [] as string[];
104✔
32

33
    /**
34
     * Add a new project to the prefixer
35
     * @param filePaths the list of absolute file paths for this project
36
     * @param prefix the prefix to give all of this module's own functions and components (and their internal usage)
37
     * @param prefixMap if this module has its own dependencies, then this prefix map allows us to rename those prefixes
38
     */
39
    public addModule(modulePath: string) {
40
        this.modules.push(
20✔
41
            new RopmModule(this.hostRootDir, modulePath)
42
        );
43
    }
44

45
    /**
46
     * Initialize all modules
47
     */
48
    public async process() {
49
        //init all modules
50
        await Promise.all(
85✔
51
            this.modules.map(x => x.init())
107✔
52
        );
53

54
        //remove duplicate/unnecessary modules
55
        await this.reduceModulesAndCreatePrefixMaps();
85✔
56

57
        //copy every file from every module to its target location
58
        await Promise.all(
85✔
59
            this.modules.map(x => x.copyFiles())
100✔
60
        );
61

62
        //have each module apply transforms (rename functions, components, file paths, etc...)
63
        await Promise.all(
85✔
64
            this.modules.map(x => x.transform(this.noprefixRopmAliases))
100✔
65
        );
66
    }
67

68
    /**
69
     * Reduce the number of dependencies to only one version for each major.
70
     * Then, remove unnecessary dependencies
71
     */
72
    public async reduceModulesAndCreatePrefixMaps() {
73
        //remove non-valid dependencies
74
        this.modules = this.modules.filter(x => x.isValid);
111✔
75

76
        const reducedDependencies = this.getReducedDependencies();
87✔
77

78
        //discard any modules that are not in the list
79
        for (let i = this.modules.length - 1; i >= 0; i--) {
87✔
80
            const module = this.modules[i];
108✔
81

82
            const dep = reducedDependencies.find((dep) => {
108✔
83
                return dep.version === module.version && dep.npmModuleName === module.npmModuleName;
135✔
84
            });
85

86
            //remove if not an approved module
87
            if (!dep) {
108✔
88
                this.modules.splice(i, 1);
4✔
89
            }
90
        }
91

92
        this.noprefixRopmAliases = this.modules.filter(x => this.noprefixNpmAliases.includes(x.npmAliasName)).map(x => x.ropmModuleName);
104✔
93

94
        //give each module the approved list of dependencies
95
        await Promise.all(
87✔
96
            this.modules.map(x => x.createPrefixMap(reducedDependencies))
104✔
97
        );
98
    }
99

100
    /**
101
     * Gather the entire list of dependencies, and then reduce them all to the highest minor/patch version within each major.
102
     * Also derive the optimal ropm alias for each dependency based on the host app aliases and the aliases from all dependencies
103
     */
104
    public getReducedDependencies() {
105
        //versions[moduleName][dominantVersion] = highestVersionNumber
106
        const moduleVersions = {} as {
94✔
107
            [moduleName: string]: {
108
                //the dominantVersion is a single int, or the full version number for pre-release versions
109
                [dominantVersion: string]: {
110
                    highestVersion: string;
111
                    aliases: string[];
112
                };
113
            };
114
        };
115

116
        //for each package of the same name, compute the highest version within each major version range
117
        for (const module of this.modules) {
94✔
118
            const npmModuleNameLower = module.npmModuleName.toLowerCase();
119✔
119
            //make the bucket if not exist
120
            if (!moduleVersions[npmModuleNameLower]) {
119✔
121
                moduleVersions[npmModuleNameLower] = {};
108✔
122
            }
123
            const dominantVersion = util.getDominantVersion(module.version);
119✔
124

125
            if (!moduleVersions[npmModuleNameLower][dominantVersion]) {
119✔
126
                moduleVersions[npmModuleNameLower][dominantVersion] = {
117✔
127
                    highestVersion: module.version,
128
                    aliases: []
129
                };
130
            }
131
            const previousVersion = moduleVersions[npmModuleNameLower][dominantVersion].highestVersion ?? module.version;
119!
132
            //if this new version is higher, keep it
133
            if (semver.compare(module.version, previousVersion) > 0) {
119✔
134
                moduleVersions[npmModuleNameLower][dominantVersion].highestVersion = module.version;
2✔
135
            }
136
            moduleVersions[npmModuleNameLower][dominantVersion].aliases.push(module.ropmModuleName);
119✔
137
        }
138

139
        const result = [] as Dependency[];
94✔
140

141
        //compute the list of unique aliases
142
        for (const moduleName in moduleVersions) {
94✔
143
            const dominantVersions = Object.keys(moduleVersions[moduleName]).sort();
108✔
144
            for (let i = 0; i < dominantVersions.length; i++) {
108✔
145
                const dominantVersion = dominantVersions[i];
117✔
146

147
                const hostDependency = this.hostDependencies.find(
117✔
148
                    dep => dep.npmModuleName === moduleName && util.getDominantVersion(dep.version) === dominantVersion
128✔
149
                );
150

151
                const obj = moduleVersions[moduleName][dominantVersion];
117✔
152
                //convert the version number into a valid roku identifier
153
                const dominantVersionIdentifier = util.prereleaseToRokuIdentifier(dominantVersion);
117✔
154

155
                let version: string;
156
                if (semver.prerelease(dominantVersion)) {
117✔
157
                    //use exactly this prerelease version
158
                    version = dominantVersion;
3✔
159
                } else {
160
                    //use the highest version within this major range
161
                    version = semver.maxSatisfying([obj.highestVersion, hostDependency?.version ?? '0.0.0'], '*') as string;
114✔
162
                }
163

164
                result.push({
117✔
165
                    dominantVersion: dominantVersion.toString(),
166
                    npmModuleName: moduleName,
167
                    //use the hosts's alias, or default to the module name
168
                    ropmModuleName: hostDependency?.npmAlias
351✔
169
                        ? util.getRopmNameFromModuleName(hostDependency.npmAlias)
117✔
170
                        : `${util.getRopmNameFromModuleName(moduleName)}_v${dominantVersionIdentifier}`,
171
                    version: version
172
                });
173
            }
174
        }
175

176
        return result;
94✔
177
    }
178
}
179

180
export interface Dependency {
181
    npmModuleName: string;
182
    /**
183
     * The dominant version of the dependency. This will be the major version in most cases, will be the full version string for pre-release versions
184
     */
185
    dominantVersion: string;
186
    version: string;
187
    ropmModuleName: string;
188
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc