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

rokucommunity / ropm / #874

23 Jan 2024 06:13PM UTC coverage: 89.291%. Remained the same
#874

push

GitHub
Merge 741ad53e2 into 803e66631

517 of 626 branches covered (0.0%)

Branch coverage included in aggregate %.

642 of 672 relevant lines covered (95.54%)

66.73 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[];
106✔
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[];
106✔
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[];
106✔
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[];
106✔
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(
21✔
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(
86✔
51
            this.modules.map(x => x.init())
108✔
52
        );
53

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

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

62
        //have each module apply transforms (rename functions, components, file paths, etc...)
63
        await Promise.all(
86✔
64
            this.modules.map(x => x.transform(this.noprefixRopmAliases))
101✔
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);
112✔
75

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

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

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

86
            //remove if not an approved module
87
            if (!dep) {
109✔
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);
105✔
93

94
        //give each module the approved list of dependencies
95
        await Promise.all(
88✔
96
            this.modules.map(x => x.createPrefixMap(reducedDependencies))
105✔
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 {
95✔
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) {
95✔
118
            const npmModuleNameLower = module.npmModuleName.toLowerCase();
120✔
119
            //make the bucket if not exist
120
            if (!moduleVersions[npmModuleNameLower]) {
120✔
121
                moduleVersions[npmModuleNameLower] = {};
109✔
122
            }
123
            const dominantVersion = util.getDominantVersion(module.version);
120✔
124

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

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

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

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

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

155
                let version: string;
156
                if (semver.prerelease(dominantVersion)) {
118✔
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;
115✔
162
                }
163

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

176
        return result;
95✔
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