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

electrode-io / electrode-native / 7528

24 Oct 2025 12:58AM UTC coverage: 56.546% (-3.5%) from 60.065%
7528

push

Azure Pipelines

r0h0gg6
Merge pull request #1918 from electrode-io/cuid2-fix-2

Update yarn.lock

3566 of 7725 branches covered (46.16%)

Branch coverage included in aggregate %.

2 of 5 new or added lines in 1 file covered. (40.0%)

568 existing lines in 37 files now uncovered.

9396 of 15198 relevant lines covered (61.82%)

524.78 hits per line

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

9.41
/ern-api-impl-gen/src/generators/android/ApiImplAndroidGenerator.ts
1
import {
2✔
2
  android,
3
  fileUtils,
4
  injectReactNativeVersionKeysInObject,
5
  log,
6
  manifest,
7
  mustacheUtils,
8
  PackagePath,
9
  Platform,
10
  PluginConfig,
11
  shell,
12
} from 'ern-core';
13
import fs from 'fs-extra';
2✔
14
import path from 'path';
2✔
15
import readDir from 'fs-readdir-recursive';
2✔
16
import { ApiImplGeneratable } from '../../ApiImplGeneratable';
17

18
export const ROOT_DIR = shell.pwd();
2✔
19
const SRC_MAIN_JAVA_DIR = path.normalize('src/main/java');
2✔
20
const API_IMPL_PACKAGE = path.normalize('com/ern/api/impl');
2✔
21

22
export default class ApiImplAndroidGenerator implements ApiImplGeneratable {
2✔
23
  public static getMustacheFileNamesMap(resourceDir: string, apiName: string) {
UNCOV
24
    const files = readDir(resourceDir, (f) => f.endsWith('.mustache'));
×
UNCOV
25
    const classNames: { [k: string]: string } = {
×
26
      'RequestHandlerConfig.java.mustache': 'RequestHandlerConfig.java',
27
      'RequestHandlerProvider.java.mustache': 'RequestHandlerProvider.java',
28
      'apiController.mustache': `${apiName}ApiController.java`,
29
      'requestHandlerProvider.mustache': `${apiName}ApiRequestHandlerProvider.java`,
30
      'requestHandlers.mustache': `${apiName}ApiRequestHandler.java`,
31
    };
UNCOV
32
    return { files, classNames };
×
33
  }
34

35
  private regenerateApiImpl: boolean;
36

37
  get name(): string {
38
    return 'ApiImplAndroidGenerator';
×
39
  }
40

41
  get platform(): string {
UNCOV
42
    return 'android';
×
43
  }
44

45
  public async generate(
46
    apiDependency: PackagePath,
47
    paths: any,
48
    reactNativeVersion: string,
49
    plugins: PackagePath[],
50
    apis: any[],
51
    regen: boolean,
52
  ) {
UNCOV
53
    log.debug(`Starting project generation for ${this.platform}`);
×
UNCOV
54
    this.regenerateApiImpl = regen;
×
UNCOV
55
    await this.fillHull(
×
56
      apiDependency,
57
      paths,
58
      reactNativeVersion,
59
      plugins,
60
      apis,
61
    );
62
  }
63

64
  public async fillHull(
65
    apiDependency: PackagePath,
66
    paths: any,
67
    reactNativeVersion: string,
68
    pluginsPaths: PackagePath[],
69
    apis: any[],
70
  ) {
UNCOV
71
    shell.pushd(ROOT_DIR);
×
72

UNCOV
73
    try {
×
UNCOV
74
      log.debug(
×
75
        `[=== Starting hull filling for api impl gen for ${this.platform} ===]`,
76
      );
77

UNCOV
78
      const outputDirectory = path.join(paths.outDirectory, 'android');
×
UNCOV
79
      log.debug(
×
80
        `Creating out directory(${outputDirectory}) for android and copying container hull to it.`,
81
      );
82

UNCOV
83
      fs.ensureDirSync(outputDirectory);
×
84

UNCOV
85
      fileUtils.chmodr('755', outputDirectory);
×
UNCOV
86
      shell.cp(
×
87
        '-Rf',
88
        path.join(paths.apiImplHull, 'android/{.*,*}'),
89
        outputDirectory,
90
      );
91

UNCOV
92
      const srcOutputDirectory = path.join(
×
93
        outputDirectory,
94
        'lib',
95
        SRC_MAIN_JAVA_DIR,
96
      );
97
      let pluginPath: PackagePath;
UNCOV
98
      for (pluginPath of pluginsPaths) {
×
UNCOV
99
        const pluginConfig = await manifest.getPluginConfig(
×
100
          pluginPath,
101
          'android',
102
        );
UNCOV
103
        if (pluginConfig) {
×
UNCOV
104
          log.debug(`Copying ${pluginPath.name} to ${outputDirectory}`);
×
UNCOV
105
          this.copyPluginToOutput(
×
106
            paths,
107
            srcOutputDirectory,
108
            pluginPath,
109
            pluginConfig,
110
          );
111
        }
112
      }
UNCOV
113
      const editableFiles = await this.generateRequestHandlerClasses(
×
114
        apiDependency,
115
        paths,
116
        apis,
117
      );
UNCOV
118
      await this.updateGradleProperties(
×
119
        paths,
120
        reactNativeVersion,
121
        outputDirectory,
122
      );
UNCOV
123
      await this.updateBuildGradle(
×
124
        apiDependency,
125
        paths,
126
        reactNativeVersion,
127
        outputDirectory,
128
      );
129
    } finally {
UNCOV
130
      shell.popd();
×
131
    }
132
  }
133

134
  public copyPluginToOutput(
135
    paths: any,
136
    pluginOutputDirectory: string,
137
    pluginPath: PackagePath,
138
    pluginConfig: PluginConfig<'android'>,
139
  ) {
UNCOV
140
    if (pluginPath.name === 'react-native') {
×
141
      return;
×
142
    }
UNCOV
143
    log.debug(`injecting ${pluginPath.name} code.`);
×
UNCOV
144
    const pluginSrcDirectory = path.join(
×
145
      paths.outDirectory,
146
      'node_modules',
147
      pluginPath.name!,
148
      'android',
149
      pluginConfig.moduleName,
150
      SRC_MAIN_JAVA_DIR,
151
      '*',
152
    );
153

UNCOV
154
    fs.ensureDirSync(pluginOutputDirectory);
×
155

UNCOV
156
    log.debug(
×
157
      `Copying code from ${pluginSrcDirectory} to ${pluginOutputDirectory}`,
158
    );
UNCOV
159
    shell.cp('-Rf', pluginSrcDirectory, pluginOutputDirectory);
×
160
  }
161

162
  public updateBuildGradle(
163
    apiDependency: PackagePath,
164
    paths: any,
165
    reactNativeVersion: string,
166
    outputDirectory: string,
167
  ): Promise<any> {
UNCOV
168
    let mustacheView: any = {};
×
UNCOV
169
    const versions = android.resolveAndroidVersions({
×
170
      androidGradlePlugin: '3.2.1',
171
      reactNativeVersion,
172
    });
UNCOV
173
    mustacheView.reactNativeVersion = reactNativeVersion;
×
UNCOV
174
    mustacheView = Object.assign(mustacheView, versions);
×
UNCOV
175
    injectReactNativeVersionKeysInObject(mustacheView, reactNativeVersion);
×
UNCOV
176
    mustacheUtils.mustacheRenderToOutputFileUsingTemplateFile(
×
177
      path.join(paths.apiImplHull, 'android/build.gradle'),
178
      mustacheView,
179
      path.join(outputDirectory, 'build.gradle'),
180
    );
UNCOV
181
    return mustacheUtils.mustacheRenderToOutputFileUsingTemplateFile(
×
182
      path.join(paths.apiImplHull, 'android/lib/build.gradle'),
183
      mustacheView,
184
      path.join(outputDirectory, 'lib/build.gradle'),
185
    );
186
  }
187

188
  public updateGradleProperties(
189
    paths: any,
190
    reactNativeVersion: string,
191
    outputDirectory: string,
192
  ): Promise<any> {
UNCOV
193
    let mustacheView: any = {};
×
UNCOV
194
    const versions = android.resolveAndroidVersions({ reactNativeVersion });
×
UNCOV
195
    mustacheView = Object.assign(mustacheView, versions);
×
UNCOV
196
    injectReactNativeVersionKeysInObject(mustacheView, reactNativeVersion);
×
UNCOV
197
    return mustacheUtils.mustacheRenderToOutputFileUsingTemplateFile(
×
198
      path.join(
199
        paths.apiImplHull,
200
        'android/gradle/wrapper/gradle-wrapper.properties',
201
      ),
202
      mustacheView,
203
      path.join(outputDirectory, 'gradle/wrapper/gradle-wrapper.properties'),
204
    );
205
  }
206

207
  public async generateRequestHandlerClasses(
208
    apiDependency: PackagePath,
209
    paths: any,
210
    apis: any[],
211
  ) {
UNCOV
212
    log.debug('=== updating request handler implementation class ===');
×
UNCOV
213
    try {
×
UNCOV
214
      const editableFiles: string[] = [];
×
UNCOV
215
      const resourceDir = path.join(
×
216
        Platform.currentPlatformVersionPath,
217
        'ern-api-impl-gen/resources/android',
218
      );
UNCOV
219
      const outputDir = path.join(
×
220
        paths.outDirectory,
221
        'android/lib',
222
        SRC_MAIN_JAVA_DIR,
223
        API_IMPL_PACKAGE,
224
      );
UNCOV
225
      fs.ensureDirSync(outputDir);
×
226

UNCOV
227
      for (const api of apis) {
×
228
        const { files, classNames } =
UNCOV
229
          ApiImplAndroidGenerator.getMustacheFileNamesMap(
×
230
            resourceDir,
231
            api.apiName,
232
          );
UNCOV
233
        for (const file of files) {
×
UNCOV
234
          if (file === 'requestHandlerProvider.mustache') {
×
UNCOV
235
            editableFiles.push(path.join(outputDir, classNames[file]));
×
UNCOV
236
            if (this.regenerateApiImpl) {
×
UNCOV
237
              log.debug(`Skipping regeneration of ${classNames[file]}`);
×
UNCOV
238
              continue;
×
239
            }
240
          }
UNCOV
241
          if (classNames[file]) {
×
UNCOV
242
            const partialProxy = (name: string) => {
×
UNCOV
243
              return fs.readFileSync(
×
244
                path.join(resourceDir, `${name}.mustache`),
245
                'utf8',
246
              );
247
            };
UNCOV
248
            await mustacheUtils.mustacheRenderToOutputFileUsingTemplateFile(
×
249
              path.join(resourceDir, file),
250
              api,
251
              path.join(outputDir, classNames[file]),
252
              partialProxy,
253
            );
254
          } else {
UNCOV
255
            log.info(`No mapping for for ${file}. Mustaching skipped.`);
×
256
          }
257
        }
UNCOV
258
        log.debug(
×
259
          `Api implementation files successfully generated for ${api.apiName}Api`,
260
        );
261
      }
UNCOV
262
      return editableFiles;
×
263
    } catch (e) {
264
      throw new Error(`Failed to update RequestHandlerClass: ${e}`);
×
265
    }
266
  }
267
}
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

© 2026 Coveralls, Inc