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