1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4  value: true
5});
6exports.getApplicationIdAsync = getApplicationIdAsync;
7exports.getPackage = getPackage;
8exports.renameJniOnDiskForType = renameJniOnDiskForType;
9exports.renamePackageOnDisk = renamePackageOnDisk;
10exports.renamePackageOnDiskForType = renamePackageOnDiskForType;
11exports.setPackageInAndroidManifest = setPackageInAndroidManifest;
12exports.setPackageInBuildGradle = setPackageInBuildGradle;
13exports.withPackageRefactor = exports.withPackageManifest = exports.withPackageGradle = void 0;
14
15function _debug() {
16  const data = _interopRequireDefault(require("debug"));
17
18  _debug = function () {
19    return data;
20  };
21
22  return data;
23}
24
25function _fs() {
26  const data = _interopRequireDefault(require("fs"));
27
28  _fs = function () {
29    return data;
30  };
31
32  return data;
33}
34
35function _glob() {
36  const data = require("glob");
37
38  _glob = function () {
39    return data;
40  };
41
42  return data;
43}
44
45function _path() {
46  const data = _interopRequireDefault(require("path"));
47
48  _path = function () {
49    return data;
50  };
51
52  return data;
53}
54
55function _androidPlugins() {
56  const data = require("../plugins/android-plugins");
57
58  _androidPlugins = function () {
59    return data;
60  };
61
62  return data;
63}
64
65function _withDangerousMod() {
66  const data = require("../plugins/withDangerousMod");
67
68  _withDangerousMod = function () {
69    return data;
70  };
71
72  return data;
73}
74
75function _modules() {
76  const data = require("../utils/modules");
77
78  _modules = function () {
79    return data;
80  };
81
82  return data;
83}
84
85function _warnings() {
86  const data = require("../utils/warnings");
87
88  _warnings = function () {
89    return data;
90  };
91
92  return data;
93}
94
95function _Paths() {
96  const data = require("./Paths");
97
98  _Paths = function () {
99    return data;
100  };
101
102  return data;
103}
104
105function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
106
107const debug = (0, _debug().default)('expo:config-plugins:android:package');
108const withPackageManifest = (0, _androidPlugins().createAndroidManifestPlugin)(setPackageInAndroidManifest, 'withPackageManifest');
109exports.withPackageManifest = withPackageManifest;
110
111const withPackageGradle = config => {
112  return (0, _androidPlugins().withAppBuildGradle)(config, config => {
113    if (config.modResults.language === 'groovy') {
114      config.modResults.contents = setPackageInBuildGradle(config, config.modResults.contents);
115    } else {
116      (0, _warnings().addWarningAndroid)('android.package', `Cannot automatically configure app build.gradle if it's not groovy`);
117    }
118
119    return config;
120  });
121};
122
123exports.withPackageGradle = withPackageGradle;
124
125const withPackageRefactor = config => {
126  return (0, _withDangerousMod().withDangerousMod)(config, ['android', async config => {
127    await renamePackageOnDisk(config, config.modRequest.projectRoot);
128    return config;
129  }]);
130};
131
132exports.withPackageRefactor = withPackageRefactor;
133
134function getPackage(config) {
135  var _config$android$packa, _config$android;
136
137  return (_config$android$packa = (_config$android = config.android) === null || _config$android === void 0 ? void 0 : _config$android.package) !== null && _config$android$packa !== void 0 ? _config$android$packa : null;
138}
139
140function getPackageRoot(projectRoot, type) {
141  return _path().default.join(projectRoot, 'android', 'app', 'src', type, 'java');
142}
143
144function getCurrentPackageName(projectRoot, packageRoot) {
145  const mainApplication = (0, _Paths().getProjectFilePath)(projectRoot, 'MainApplication');
146
147  const packagePath = _path().default.dirname(mainApplication);
148
149  const packagePathParts = _path().default.relative(packageRoot, packagePath).split(_path().default.sep).filter(Boolean);
150
151  return packagePathParts.join('.');
152}
153
154function getCurrentPackageForProjectFile(projectRoot, packageRoot, fileName, type) {
155  const filePath = (0, _glob().sync)(_path().default.join(projectRoot, `android/app/src/${type}/java/**/${fileName}.@(java|kt)`))[0];
156
157  if (!filePath) {
158    return null;
159  }
160
161  const packagePath = _path().default.dirname(filePath);
162
163  const packagePathParts = _path().default.relative(packageRoot, packagePath).split(_path().default.sep).filter(Boolean);
164
165  return packagePathParts.join('.');
166}
167
168function getCurrentPackageNameForType(projectRoot, type) {
169  const packageRoot = getPackageRoot(projectRoot, type);
170
171  if (type === 'main') {
172    return getCurrentPackageName(projectRoot, packageRoot);
173  } // debug, etc..
174
175
176  return getCurrentPackageForProjectFile(projectRoot, packageRoot, '*', type);
177} // NOTE(brentvatne): this assumes that our MainApplication.java file is in the root of the package
178// this makes sense for standard react-native projects but may not apply in customized projects, so if
179// we want this to be runnable in any app we need to handle other possibilities
180
181
182async function renamePackageOnDisk(config, projectRoot) {
183  const newPackageName = getPackage(config);
184
185  if (newPackageName === null) {
186    return;
187  }
188
189  for (const type of ['main', 'debug']) {
190    await renameJniOnDiskForType({
191      projectRoot,
192      type,
193      packageName: newPackageName
194    });
195    await renamePackageOnDiskForType({
196      projectRoot,
197      type,
198      packageName: newPackageName
199    });
200  }
201}
202
203async function renameJniOnDiskForType({
204  projectRoot,
205  type,
206  packageName
207}) {
208  if (!packageName) {
209    return;
210  }
211
212  const currentPackageName = getCurrentPackageNameForType(projectRoot, type);
213
214  if (!currentPackageName || !packageName || currentPackageName === packageName) {
215    return;
216  }
217
218  const jniRoot = _path().default.join(projectRoot, 'android', 'app', 'src', type, 'jni');
219
220  const filesToUpdate = [...(0, _glob().sync)('**/*', {
221    cwd: jniRoot,
222    absolute: true
223  })]; // Replace all occurrences of the path in the project
224
225  filesToUpdate.forEach(filepath => {
226    try {
227      if (_fs().default.lstatSync(filepath).isFile() && ['.h', '.cpp'].includes(_path().default.extname(filepath))) {
228        let contents = _fs().default.readFileSync(filepath).toString();
229
230        contents = contents.replace(new RegExp(transformJavaClassDescriptor(currentPackageName).replace(/\//g, '\\/'), 'g'), transformJavaClassDescriptor(packageName));
231
232        _fs().default.writeFileSync(filepath, contents);
233      }
234    } catch {
235      debug(`Error updating "${filepath}" for type "${type}"`);
236    }
237  });
238}
239
240async function renamePackageOnDiskForType({
241  projectRoot,
242  type,
243  packageName
244}) {
245  if (!packageName) {
246    return;
247  }
248
249  const currentPackageName = getCurrentPackageNameForType(projectRoot, type);
250  debug(`Found package "${currentPackageName}" for type "${type}"`);
251
252  if (!currentPackageName || currentPackageName === packageName) {
253    return;
254  }
255
256  debug(`Refactor "${currentPackageName}" to "${packageName}" for type "${type}"`);
257  const packageRoot = getPackageRoot(projectRoot, type); // Set up our paths
258
259  if (!(await (0, _modules().directoryExistsAsync)(packageRoot))) {
260    debug(`- skipping refactor of missing directory: ${packageRoot}`);
261    return;
262  }
263
264  const currentPackagePath = _path().default.join(packageRoot, ...currentPackageName.split('.'));
265
266  const newPackagePath = _path().default.join(packageRoot, ...packageName.split('.')); // Create the new directory
267
268
269  _fs().default.mkdirSync(newPackagePath, {
270    recursive: true
271  }); // Move everything from the old directory over
272
273
274  (0, _glob().sync)('**/*', {
275    cwd: currentPackagePath
276  }).forEach(relativePath => {
277    const filepath = _path().default.join(currentPackagePath, relativePath);
278
279    if (_fs().default.lstatSync(filepath).isFile()) {
280      moveFileSync(filepath, _path().default.join(newPackagePath, relativePath));
281    } else {
282      _fs().default.mkdirSync(filepath, {
283        recursive: true
284      });
285    }
286  }); // Remove the old directory recursively from com/old/package to com/old and com,
287  // as long as the directories are empty
288
289  const oldPathParts = currentPackageName.split('.');
290
291  while (oldPathParts.length) {
292    const pathToCheck = _path().default.join(packageRoot, ...oldPathParts);
293
294    try {
295      const files = _fs().default.readdirSync(pathToCheck);
296
297      if (files.length === 0) {
298        _fs().default.rmdirSync(pathToCheck);
299      }
300    } finally {
301      oldPathParts.pop();
302    }
303  }
304
305  const filesToUpdate = [...(0, _glob().sync)('**/*', {
306    cwd: newPackagePath,
307    absolute: true
308  })]; // Only update the BUCK file to match the main package name
309
310  if (type === 'main') {
311    filesToUpdate.push(_path().default.join(projectRoot, 'android', 'app', 'BUCK'));
312  } // Replace all occurrences of the path in the project
313
314
315  filesToUpdate.forEach(filepath => {
316    try {
317      if (_fs().default.lstatSync(filepath).isFile()) {
318        let contents = _fs().default.readFileSync(filepath).toString();
319
320        contents = contents.replace(new RegExp(currentPackageName, 'g'), packageName);
321
322        if (['.h', '.cpp'].includes(_path().default.extname(filepath))) {
323          contents = contents.replace(new RegExp(transformJavaClassDescriptor(currentPackageName).replace(/\//g, '\\'), 'g'), transformJavaClassDescriptor(packageName));
324        }
325
326        _fs().default.writeFileSync(filepath, contents);
327      }
328    } catch {
329      debug(`Error updating "${filepath}" for type "${type}"`);
330    }
331  });
332}
333
334function moveFileSync(src, dest) {
335  _fs().default.mkdirSync(_path().default.dirname(dest), {
336    recursive: true
337  });
338
339  _fs().default.renameSync(src, dest);
340}
341
342function setPackageInBuildGradle(config, buildGradle) {
343  const packageName = getPackage(config);
344
345  if (packageName === null) {
346    return buildGradle;
347  }
348
349  const pattern = new RegExp(`applicationId ['"].*['"]`);
350  return buildGradle.replace(pattern, `applicationId '${packageName}'`);
351}
352
353function setPackageInAndroidManifest(config, androidManifest) {
354  const packageName = getPackage(config);
355
356  if (packageName) {
357    androidManifest.manifest.$.package = packageName;
358  } else {
359    delete androidManifest.manifest.$.package;
360  }
361
362  return androidManifest;
363}
364
365async function getApplicationIdAsync(projectRoot) {
366  var _matchResult$;
367
368  const buildGradlePath = (0, _Paths().getAppBuildGradleFilePath)(projectRoot);
369
370  if (!_fs().default.existsSync(buildGradlePath)) {
371    return null;
372  }
373
374  const buildGradle = await _fs().default.promises.readFile(buildGradlePath, 'utf8');
375  const matchResult = buildGradle.match(/applicationId ['"](.*)['"]/); // TODO add fallback for legacy cases to read from AndroidManifest.xml
376
377  return (_matchResult$ = matchResult === null || matchResult === void 0 ? void 0 : matchResult[1]) !== null && _matchResult$ !== void 0 ? _matchResult$ : null;
378}
379/**
380 * Transform a java package name to java class descriptor,
381 * e.g. `com.helloworld` -> `Lcom/helloworld`.
382 */
383
384
385function transformJavaClassDescriptor(packageName) {
386  return `L${packageName.replace(/\./g, '/')}`;
387}
388//# sourceMappingURL=Package.js.map