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

electrode-io / electrode-native / 7581

24 Apr 2026 09:20AM UTC coverage: 56.608% (-11.2%) from 67.856%
7581

push

Azure Pipelines

web-flow
Merge pull request #1924 from electrode-io/npm-security-audit-fix

3600 of 7762 branches covered (46.38%)

Branch coverage included in aggregate %.

0 of 2 new or added lines in 1 file covered. (0.0%)

1659 existing lines in 112 files now uncovered.

9425 of 15247 relevant lines covered (61.82%)

523.13 hits per line

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

16.67
/ern-local-cli/src/commands/create-api-impl.ts
1
import {
1✔
2
  log,
3
  ModuleTypes,
4
  PackagePath,
5
  Platform,
6
  utils as coreUtils,
7
  validateModuleName,
8
} from 'ern-core';
9
import { generateApiImpl } from 'ern-api-impl-gen';
1✔
10
import {
1✔
11
  askUserToSelectAnEnvironment,
12
  epilog,
13
  logErrorAndExitIfNotSatisfied,
14
  performPkgNameConflictCheck,
15
  promptUserToUseSuggestedModuleName,
16
  tryCatchWrap,
17
} from '../lib';
18
import path from 'path';
1✔
19
import { Argv } from 'yargs';
20
import untildify from 'untildify';
1✔
21

22
export const command = 'create-api-impl <apiName> [apiImplName]';
1✔
23
export const desc = 'Commands to generate API implementation skeleton.';
1✔
24

25
export const builder = (argv: Argv) => {
1✔
UNCOV
26
  return argv
×
27
    .option('force', {
28
      alias: 'f',
29
      describe:
30
        'Forces a project creation even if an implementation already present inside the output location',
31
      type: 'boolean',
32
    })
33
    .option('hasConfig', {
34
      describe:
35
        'Indicates if this api implementation requires some config during initialization.\nThis option will be stored and reused during container generation to enforce config initialization',
36
      type: 'boolean',
37
    })
38
    .option('jsOnly', {
39
      alias: 'j',
40
      describe:
41
        'Generate js project with proper dependencies (Implementation of the API has to be written in js)',
42
      type: 'boolean',
43
    })
44
    .option('manifestId', {
45
      describe: 'Id of the Manifest entry to use',
46
      type: 'string',
47
    })
48
    .option('nativeOnly', {
49
      alias: 'n',
50
      describe:
51
        'Generate native projects with proper dependencies (Implementation of the API has to be written in native)',
52
      type: 'boolean',
53
    })
54
    .option('packageName', {
55
      alias: 'p',
56
      describe: 'Name to use for the apiImpl npm package',
57
    })
58
    .option('scope', {
59
      alias: 's',
60
      describe: 'Scope to use for the apiImpl npm package',
61
    })
62
    .option('skipNpmCheck', {
63
      describe:
64
        'Skip the check ensuring package does not already exists in npm registry',
65
      type: 'boolean',
66
    })
67
    .option('outputDirectory', {
68
      alias: 'o',
69
      describe: 'Path to output directory',
70
    })
UNCOV
71
    .coerce('outputDirectory', (p) => untildify(p))
×
72
    .epilog(epilog(exports));
73
};
74

75
const WORKING_DIRECTORY = path.join(Platform.rootDirectory, 'api-impl-gen');
1✔
76
const PLUGIN_DIRECTORY = path.join(WORKING_DIRECTORY, 'plugins');
1✔
77

78
export const commandHandler = async ({
1✔
79
  apiName,
80
  apiImplName,
81
  force,
82
  hasConfig,
83
  jsOnly,
84
  manifestId,
85
  nativeOnly,
86
  packageName,
87
  scope,
88
  skipNpmCheck,
89
  outputDirectory,
90
}: {
91
  apiName: string;
92
  apiImplName?: string;
93
  force: boolean;
94
  hasConfig: boolean;
95
  jsOnly: boolean;
96
  manifestId?: string;
97
  nativeOnly: boolean;
98
  packageName?: string;
99
  scope?: string;
100
  skipNpmCheck?: boolean;
101
  outputDirectory: string;
102
}) => {
UNCOV
103
  const apiDep = PackagePath.fromString(apiName);
×
104
  // pre conditions
UNCOV
105
  await logErrorAndExitIfNotSatisfied({
×
106
    noGitOrFilesystemPath: {
107
      obj: apiName,
108
    },
109
    publishedToNpm: {
110
      extraErrorMessage: `Couldn't find package ${apiName} to generate the api implementation`,
111
      obj: apiName,
112
    },
113
  });
114

UNCOV
115
  if (apiImplName) {
×
UNCOV
116
    await logErrorAndExitIfNotSatisfied({
×
117
      isValidElectrodeNativeModuleName: {
118
        name: apiImplName,
119
      },
120
    });
121
  }
122

UNCOV
123
  if (manifestId) {
×
124
    await logErrorAndExitIfNotSatisfied({
×
125
      manifestIdExists: {
126
        id: manifestId,
127
      },
128
    });
129
  }
130

UNCOV
131
  const reactNativeVersion = await coreUtils.reactNativeManifestVersion({
×
132
    manifestId,
133
  });
UNCOV
134
  if (!reactNativeVersion) {
×
135
    throw new Error(
×
136
      'React Native version is not defined in Manifest. This should not happen!',
137
    );
138
  }
139

UNCOV
140
  if (jsOnly && nativeOnly) {
×
141
    log.warn('Looks like both js and native are selected, should be only one');
×
142
    nativeOnly = (await askUserToSelectAnEnvironment()) !== 'js';
×
143
    jsOnly = !nativeOnly;
×
144
  }
145

UNCOV
146
  if (!jsOnly && !nativeOnly) {
×
147
    nativeOnly = (await askUserToSelectAnEnvironment()) !== 'js';
×
148
    jsOnly = !nativeOnly;
×
149
  }
150

UNCOV
151
  const moduleType = nativeOnly
×
152
    ? ModuleTypes.NATIVE_API_IMPL
×
153
    : ModuleTypes.JS_API_IMPL;
154

UNCOV
155
  if (apiImplName && !validateModuleName(apiImplName, moduleType)) {
×
156
    apiImplName = await promptUserToUseSuggestedModuleName(
×
157
      apiImplName,
158
      moduleType,
159
    );
160
  }
161

162
  // Must conform to definition of ElectrodeNativeModuleName
UNCOV
163
  if (!apiImplName) {
×
UNCOV
164
    const simpleName = apiName.replace(/^@(.+)\/|-api$/g, '');
×
UNCOV
165
    apiImplName = `${simpleName}-${coreUtils.getModuleSuffix(moduleType)}`;
×
166
  }
167

168
  // If no package name is specified get default name from apiImplName
UNCOV
169
  if (!packageName) {
×
170
    packageName = coreUtils.getDefaultPackageNameForModule(
×
171
      apiImplName,
172
      moduleType,
173
    );
174
  }
175

176
  // Check if packageName is valid
UNCOV
177
  await logErrorAndExitIfNotSatisfied({
×
178
    isValidNpmPackageName: {
179
      name: packageName,
180
    },
181
  });
182

183
  // Skip npm check
UNCOV
184
  if (!skipNpmCheck && !(await performPkgNameConflictCheck(packageName))) {
×
185
    throw new Error(`Aborting command `);
×
186
  }
187

UNCOV
188
  log.info(`Generating API implementation for ${apiName}`);
×
189

UNCOV
190
  await generateApiImpl({
×
191
    apiDependency: apiDep,
192
    apiImplName,
193
    forceGenerate: force,
194
    hasConfig,
195
    nativeOnly,
196
    outputDirectory,
197
    packageName,
198
    paths: {
199
      apiImplHull: path.join(
200
        Platform.currentPlatformVersionPath,
201
        'ern-api-impl-gen',
202
        'hull',
203
      ),
204
      outDirectory: '',
205
    },
206
    reactNativeVersion,
207
    scope,
208
  });
UNCOV
209
  log.info('Success');
×
210
};
211

212
export const handler = tryCatchWrap(commandHandler);
1✔
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