1"use strict"; 2 3Object.defineProperty(exports, "__esModule", { 4 value: true 5}); 6var _exportNames = { 7 getConfig: true, 8 getPackageJson: true, 9 getConfigFilePaths: true, 10 modifyConfigAsync: true, 11 getWebOutputPath: true, 12 getNameFromConfig: true, 13 getDefaultTarget: true, 14 getProjectConfigDescription: true, 15 getProjectConfigDescriptionWithPaths: true 16}; 17exports.getConfig = getConfig; 18exports.getConfigFilePaths = getConfigFilePaths; 19exports.getDefaultTarget = getDefaultTarget; 20exports.getNameFromConfig = getNameFromConfig; 21exports.getPackageJson = getPackageJson; 22exports.getProjectConfigDescription = getProjectConfigDescription; 23exports.getProjectConfigDescriptionWithPaths = getProjectConfigDescriptionWithPaths; 24exports.getWebOutputPath = getWebOutputPath; 25exports.modifyConfigAsync = modifyConfigAsync; 26function _jsonFile() { 27 const data = _interopRequireDefault(require("@expo/json-file")); 28 _jsonFile = function () { 29 return data; 30 }; 31 return data; 32} 33function _fs() { 34 const data = _interopRequireDefault(require("fs")); 35 _fs = function () { 36 return data; 37 }; 38 return data; 39} 40function _glob() { 41 const data = require("glob"); 42 _glob = function () { 43 return data; 44 }; 45 return data; 46} 47function _path() { 48 const data = _interopRequireDefault(require("path")); 49 _path = function () { 50 return data; 51 }; 52 return data; 53} 54function _resolveFrom() { 55 const data = _interopRequireDefault(require("resolve-from")); 56 _resolveFrom = function () { 57 return data; 58 }; 59 return data; 60} 61function _semver() { 62 const data = _interopRequireDefault(require("semver")); 63 _semver = function () { 64 return data; 65 }; 66 return data; 67} 68function _slugify() { 69 const data = _interopRequireDefault(require("slugify")); 70 _slugify = function () { 71 return data; 72 }; 73 return data; 74} 75function _getConfig() { 76 const data = require("./getConfig"); 77 _getConfig = function () { 78 return data; 79 }; 80 return data; 81} 82function _getExpoSDKVersion() { 83 const data = require("./getExpoSDKVersion"); 84 _getExpoSDKVersion = function () { 85 return data; 86 }; 87 return data; 88} 89function _getFullName() { 90 const data = require("./getFullName"); 91 _getFullName = function () { 92 return data; 93 }; 94 return data; 95} 96function _withConfigPlugins() { 97 const data = require("./plugins/withConfigPlugins"); 98 _withConfigPlugins = function () { 99 return data; 100 }; 101 return data; 102} 103function _withInternal() { 104 const data = require("./plugins/withInternal"); 105 _withInternal = function () { 106 return data; 107 }; 108 return data; 109} 110function _resolvePackageJson() { 111 const data = require("./resolvePackageJson"); 112 _resolvePackageJson = function () { 113 return data; 114 }; 115 return data; 116} 117var _Config = require("./Config.types"); 118Object.keys(_Config).forEach(function (key) { 119 if (key === "default" || key === "__esModule") return; 120 if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; 121 if (key in exports && exports[key] === _Config[key]) return; 122 Object.defineProperty(exports, key, { 123 enumerable: true, 124 get: function () { 125 return _Config[key]; 126 } 127 }); 128}); 129function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 130/** 131 * If a config has an `expo` object then that will be used as the config. 132 * This method reduces out other top level values if an `expo` object exists. 133 * 134 * @param config Input config object to reduce 135 */ 136function reduceExpoObject(config) { 137 var _config$expo; 138 if (!config) return config === undefined ? null : config; 139 const { 140 mods, 141 ...expo 142 } = (_config$expo = config.expo) !== null && _config$expo !== void 0 ? _config$expo : config; 143 return { 144 expo, 145 mods 146 }; 147} 148 149/** 150 * Get all platforms that a project is currently capable of running. 151 * 152 * @param projectRoot 153 * @param exp 154 */ 155function getSupportedPlatforms(projectRoot) { 156 const platforms = []; 157 if (_resolveFrom().default.silent(projectRoot, 'react-native')) { 158 platforms.push('ios', 'android'); 159 } 160 if (_resolveFrom().default.silent(projectRoot, 'react-native-web')) { 161 platforms.push('web'); 162 } 163 return platforms; 164} 165 166/** 167 * Evaluate the config for an Expo project. 168 * If a function is exported from the `app.config.js` then a partial config will be passed as an argument. 169 * The partial config is composed from any existing app.json, and certain fields from the `package.json` like name and description. 170 * 171 * If options.isPublicConfig is true, the Expo config will include only public-facing options (omitting private keys). 172 * The resulting config should be suitable for hosting or embedding in a publicly readable location. 173 * 174 * **Example** 175 * ```js 176 * module.exports = function({ config }) { 177 * // mutate the config before returning it. 178 * config.slug = 'new slug' 179 * return { expo: config }; 180 * } 181 * ``` 182 * 183 * **Supports** 184 * - `app.config.ts` 185 * - `app.config.js` 186 * - `app.config.json` 187 * - `app.json` 188 * 189 * @param projectRoot the root folder containing all of your application code 190 * @param options enforce criteria for a project config 191 */ 192function getConfig(projectRoot, options = {}) { 193 const paths = getConfigFilePaths(projectRoot); 194 const rawStaticConfig = paths.staticConfigPath ? (0, _getConfig().getStaticConfig)(paths.staticConfigPath) : null; 195 // For legacy reasons, always return an object. 196 const rootConfig = rawStaticConfig || {}; 197 const staticConfig = reduceExpoObject(rawStaticConfig) || {}; 198 199 // Can only change the package.json location if an app.json or app.config.json exists 200 const [packageJson, packageJsonPath] = getPackageJsonAndPath(projectRoot); 201 function fillAndReturnConfig(config, dynamicConfigObjectType) { 202 const configWithDefaultValues = { 203 ...ensureConfigHasDefaultValues({ 204 projectRoot, 205 exp: config.expo, 206 pkg: packageJson, 207 skipSDKVersionRequirement: options.skipSDKVersionRequirement, 208 paths, 209 packageJsonPath 210 }), 211 mods: config.mods, 212 dynamicConfigObjectType, 213 rootConfig, 214 dynamicConfigPath: paths.dynamicConfigPath, 215 staticConfigPath: paths.staticConfigPath 216 }; 217 if (options.isModdedConfig) { 218 var _config$mods; 219 // @ts-ignore: Add the mods back to the object. 220 configWithDefaultValues.exp.mods = (_config$mods = config.mods) !== null && _config$mods !== void 0 ? _config$mods : null; 221 } 222 223 // Apply static json plugins, should be done after _internal 224 configWithDefaultValues.exp = (0, _withConfigPlugins().withConfigPlugins)(configWithDefaultValues.exp, !!options.skipPlugins); 225 if (!options.isModdedConfig) { 226 // @ts-ignore: Delete mods added by static plugins when they won't have a chance to be evaluated 227 delete configWithDefaultValues.exp.mods; 228 } 229 if (options.isPublicConfig) { 230 var _configWithDefaultVal, _configWithDefaultVal2, _configWithDefaultVal3, _configWithDefaultVal4; 231 // TODD(EvanBacon): Drop plugins array after it's been resolved. 232 233 // Remove internal values with references to user's file paths from the public config. 234 delete configWithDefaultValues.exp._internal; 235 if (configWithDefaultValues.exp.hooks) { 236 delete configWithDefaultValues.exp.hooks; 237 } 238 if ((_configWithDefaultVal = configWithDefaultValues.exp.ios) !== null && _configWithDefaultVal !== void 0 && _configWithDefaultVal.config) { 239 delete configWithDefaultValues.exp.ios.config; 240 } 241 if ((_configWithDefaultVal2 = configWithDefaultValues.exp.android) !== null && _configWithDefaultVal2 !== void 0 && _configWithDefaultVal2.config) { 242 delete configWithDefaultValues.exp.android.config; 243 } 244 245 // These value will be overwritten when the manifest is being served from the host (i.e. not completely accurate). 246 // @ts-ignore: currentFullName not on type yet. 247 configWithDefaultValues.exp.currentFullName = (0, _getFullName().getFullName)(configWithDefaultValues.exp); 248 // @ts-ignore: originalFullName not on type yet. 249 configWithDefaultValues.exp.originalFullName = (0, _getFullName().getFullName)(configWithDefaultValues.exp); 250 (_configWithDefaultVal3 = configWithDefaultValues.exp.updates) === null || _configWithDefaultVal3 === void 0 ? true : delete _configWithDefaultVal3.codeSigningCertificate; 251 (_configWithDefaultVal4 = configWithDefaultValues.exp.updates) === null || _configWithDefaultVal4 === void 0 ? true : delete _configWithDefaultVal4.codeSigningMetadata; 252 } 253 return configWithDefaultValues; 254 } 255 256 // Fill in the static config 257 function getContextConfig(config) { 258 return ensureConfigHasDefaultValues({ 259 projectRoot, 260 exp: config.expo, 261 pkg: packageJson, 262 skipSDKVersionRequirement: true, 263 paths, 264 packageJsonPath 265 }).exp; 266 } 267 if (paths.dynamicConfigPath) { 268 // No app.config.json or app.json but app.config.js 269 const { 270 exportedObjectType, 271 config: rawDynamicConfig 272 } = (0, _getConfig().getDynamicConfig)(paths.dynamicConfigPath, { 273 projectRoot, 274 staticConfigPath: paths.staticConfigPath, 275 packageJsonPath, 276 config: getContextConfig(staticConfig) 277 }); 278 // Allow for the app.config.js to `export default null;` 279 // Use `dynamicConfigPath` to detect if a dynamic config exists. 280 const dynamicConfig = reduceExpoObject(rawDynamicConfig) || {}; 281 return fillAndReturnConfig(dynamicConfig, exportedObjectType); 282 } 283 284 // No app.config.js but json or no config 285 return fillAndReturnConfig(staticConfig || {}, null); 286} 287function getPackageJson(projectRoot) { 288 const [pkg] = getPackageJsonAndPath(projectRoot); 289 return pkg; 290} 291function getPackageJsonAndPath(projectRoot) { 292 const packageJsonPath = (0, _resolvePackageJson().getRootPackageJsonPath)(projectRoot); 293 return [_jsonFile().default.read(packageJsonPath), packageJsonPath]; 294} 295 296/** 297 * Get the static and dynamic config paths for a project. Also accounts for custom paths. 298 * 299 * @param projectRoot 300 */ 301function getConfigFilePaths(projectRoot) { 302 return { 303 dynamicConfigPath: getDynamicConfigFilePath(projectRoot), 304 staticConfigPath: getStaticConfigFilePath(projectRoot) 305 }; 306} 307function getDynamicConfigFilePath(projectRoot) { 308 for (const fileName of ['app.config.ts', 'app.config.js']) { 309 const configPath = _path().default.join(projectRoot, fileName); 310 if (_fs().default.existsSync(configPath)) { 311 return configPath; 312 } 313 } 314 return null; 315} 316function getStaticConfigFilePath(projectRoot) { 317 for (const fileName of ['app.config.json', 'app.json']) { 318 const configPath = _path().default.join(projectRoot, fileName); 319 if (_fs().default.existsSync(configPath)) { 320 return configPath; 321 } 322 } 323 return null; 324} 325 326/** 327 * Attempt to modify an Expo project config. 328 * This will only fully work if the project is using static configs only. 329 * Otherwise 'warn' | 'fail' will return with a message about why the config couldn't be updated. 330 * The potentially modified config object will be returned for testing purposes. 331 * 332 * @param projectRoot 333 * @param modifications modifications to make to an existing config 334 * @param readOptions options for reading the current config file 335 * @param writeOptions If true, the static config file will not be rewritten 336 */ 337async function modifyConfigAsync(projectRoot, modifications, readOptions = {}, writeOptions = {}) { 338 const config = getConfig(projectRoot, readOptions); 339 if (config.dynamicConfigPath) { 340 // We cannot automatically write to a dynamic config. 341 /* Currently we should just use the safest approach possible, informing the user that they'll need to manually modify their dynamic config. 342 if (config.staticConfigPath) { 343 // Both a dynamic and a static config exist. 344 if (config.dynamicConfigObjectType === 'function') { 345 // The dynamic config exports a function, this means it possibly extends the static config. 346 } else { 347 // Dynamic config ignores the static config, there isn't a reason to automatically write to it. 348 // Instead we should warn the user to add values to their dynamic config. 349 } 350 } 351 */ 352 return { 353 type: 'warn', 354 message: `Cannot automatically write to dynamic config at: ${_path().default.relative(projectRoot, config.dynamicConfigPath)}`, 355 config: null 356 }; 357 } else if (config.staticConfigPath) { 358 // Static with no dynamic config, this means we can append to the config automatically. 359 let outputConfig; 360 // If the config has an expo object (app.json) then append the options to that object. 361 if (config.rootConfig.expo) { 362 outputConfig = { 363 ...config.rootConfig, 364 expo: { 365 ...config.rootConfig.expo, 366 ...modifications 367 } 368 }; 369 } else { 370 // Otherwise (app.config.json) just add the config modification to the top most level. 371 outputConfig = { 372 ...config.rootConfig, 373 ...modifications 374 }; 375 } 376 if (!writeOptions.dryRun) { 377 await _jsonFile().default.writeAsync(config.staticConfigPath, outputConfig, { 378 json5: false 379 }); 380 } 381 return { 382 type: 'success', 383 config: outputConfig 384 }; 385 } 386 return { 387 type: 'fail', 388 message: 'No config exists', 389 config: null 390 }; 391} 392function ensureConfigHasDefaultValues({ 393 projectRoot, 394 exp, 395 pkg, 396 paths, 397 packageJsonPath, 398 skipSDKVersionRequirement = false 399}) { 400 var _exp$name, _exp$slug, _exp$version; 401 if (!exp) { 402 exp = {}; 403 } 404 exp = (0, _withInternal().withInternal)(exp, { 405 projectRoot, 406 ...(paths !== null && paths !== void 0 ? paths : {}), 407 packageJsonPath 408 }); 409 // Defaults for package.json fields 410 const pkgName = typeof pkg.name === 'string' ? pkg.name : _path().default.basename(projectRoot); 411 const pkgVersion = typeof pkg.version === 'string' ? pkg.version : '1.0.0'; 412 const pkgWithDefaults = { 413 ...pkg, 414 name: pkgName, 415 version: pkgVersion 416 }; 417 418 // Defaults for app.json/app.config.js fields 419 const name = (_exp$name = exp.name) !== null && _exp$name !== void 0 ? _exp$name : pkgName; 420 const slug = (_exp$slug = exp.slug) !== null && _exp$slug !== void 0 ? _exp$slug : (0, _slugify().default)(name.toLowerCase()); 421 const version = (_exp$version = exp.version) !== null && _exp$version !== void 0 ? _exp$version : pkgVersion; 422 let description = exp.description; 423 if (!description && typeof pkg.description === 'string') { 424 description = pkg.description; 425 } 426 const expWithDefaults = { 427 ...exp, 428 name, 429 slug, 430 version, 431 description 432 }; 433 let sdkVersion; 434 try { 435 sdkVersion = (0, _getExpoSDKVersion().getExpoSDKVersion)(projectRoot, expWithDefaults); 436 } catch (error) { 437 if (!skipSDKVersionRequirement) throw error; 438 } 439 let platforms = exp.platforms; 440 if (!platforms) { 441 platforms = getSupportedPlatforms(projectRoot); 442 } 443 return { 444 exp: { 445 ...expWithDefaults, 446 sdkVersion, 447 platforms 448 }, 449 pkg: pkgWithDefaults 450 }; 451} 452const DEFAULT_BUILD_PATH = `web-build`; 453function getWebOutputPath(config = {}) { 454 var _expo$web, _expo$web$build; 455 if (process.env.WEBPACK_BUILD_OUTPUT_PATH) { 456 return process.env.WEBPACK_BUILD_OUTPUT_PATH; 457 } 458 const expo = config.expo || config || {}; 459 return (expo === null || expo === void 0 ? void 0 : (_expo$web = expo.web) === null || _expo$web === void 0 ? void 0 : (_expo$web$build = _expo$web.build) === null || _expo$web$build === void 0 ? void 0 : _expo$web$build.output) || DEFAULT_BUILD_PATH; 460} 461function getNameFromConfig(exp = {}) { 462 // For RN CLI support 463 const appManifest = exp.expo || exp; 464 const { 465 web = {} 466 } = appManifest; 467 468 // rn-cli apps use a displayName value as well. 469 const appName = exp.displayName || appManifest.displayName || appManifest.name; 470 const webName = web.name || appName; 471 return { 472 appName, 473 webName 474 }; 475} 476function getDefaultTarget(projectRoot, exp) { 477 var _exp; 478 (_exp = exp) !== null && _exp !== void 0 ? _exp : exp = getConfig(projectRoot, { 479 skipSDKVersionRequirement: true 480 }).exp; 481 482 // before SDK 37, always default to managed to preserve previous behavior 483 if (exp.sdkVersion && exp.sdkVersion !== 'UNVERSIONED' && _semver().default.lt(exp.sdkVersion, '37.0.0')) { 484 return 'managed'; 485 } 486 return isBareWorkflowProject(projectRoot) ? 'bare' : 'managed'; 487} 488function isBareWorkflowProject(projectRoot) { 489 const [pkg] = getPackageJsonAndPath(projectRoot); 490 491 // TODO: Drop this 492 if (pkg.dependencies && pkg.dependencies.expokit) { 493 return false; 494 } 495 const xcodeprojFiles = (0, _glob().sync)('ios/**/*.xcodeproj', { 496 absolute: true, 497 cwd: projectRoot 498 }); 499 if (xcodeprojFiles.length) { 500 return true; 501 } 502 const gradleFiles = (0, _glob().sync)('android/**/*.gradle', { 503 absolute: true, 504 cwd: projectRoot 505 }); 506 if (gradleFiles.length) { 507 return true; 508 } 509 return false; 510} 511 512/** 513 * Return a useful name describing the project config. 514 * - dynamic: app.config.js 515 * - static: app.json 516 * - custom path app config relative to root folder 517 * - both: app.config.js or app.json 518 */ 519function getProjectConfigDescription(projectRoot) { 520 const paths = getConfigFilePaths(projectRoot); 521 return getProjectConfigDescriptionWithPaths(projectRoot, paths); 522} 523 524/** 525 * Returns a string describing the configurations used for the given project root. 526 * Will return null if no config is found. 527 * 528 * @param projectRoot 529 * @param projectConfig 530 */ 531function getProjectConfigDescriptionWithPaths(projectRoot, projectConfig) { 532 if (projectConfig.dynamicConfigPath) { 533 const relativeDynamicConfigPath = _path().default.relative(projectRoot, projectConfig.dynamicConfigPath); 534 if (projectConfig.staticConfigPath) { 535 return `${relativeDynamicConfigPath} or ${_path().default.relative(projectRoot, projectConfig.staticConfigPath)}`; 536 } 537 return relativeDynamicConfigPath; 538 } else if (projectConfig.staticConfigPath) { 539 return _path().default.relative(projectRoot, projectConfig.staticConfigPath); 540 } 541 // If a config doesn't exist, our tooling will generate a static app.json 542 return 'app.json'; 543} 544//# sourceMappingURL=Config.js.map