{"version":3,"file":"Xcodeproj.js","sourceRoot":"","sources":["../../../src/ios/utils/Xcodeproj.ts"],"names":[],"mappings":"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","sourcesContent":["import { ExpoConfig } from '@expo/config-types';\nimport * as path from 'path';\nimport xcode, {\n  PBXFile,\n  PBXGroup,\n  PBXNativeTarget,\n  PBXProject,\n  UUID,\n  XCBuildConfiguration,\n  XCConfigurationList,\n  XcodeProject,\n} from 'xcode';\nimport pbxFile from 'xcode/lib/pbxFile';\n\nimport { assert } from '../../utils/errors';\nimport { addWarningIOS } from '../../utils/warnings';\nimport * as Paths from '../Paths';\n\nexport type ProjectSectionEntry = [string, PBXProject];\n\nexport type NativeTargetSection = Record<string, PBXNativeTarget>;\n\nexport type NativeTargetSectionEntry = [string, PBXNativeTarget];\n\nexport type ConfigurationLists = Record<string, XCConfigurationList>;\n\nexport type ConfigurationListEntry = [string, XCConfigurationList];\n\nexport type ConfigurationSectionEntry = [string, XCBuildConfiguration];\n\nexport function getProjectName(projectRoot: string) {\n  const sourceRoot = Paths.getSourceRoot(projectRoot);\n  return path.basename(sourceRoot);\n}\n\n// TODO: come up with a better solution for using app.json expo.name in various places\nfunction sanitizedName(name: string) {\n  return name\n    .replace(/[\\W_]+/g, '')\n    .normalize('NFD')\n    .replace(/[\\u0300-\\u036f]/g, '');\n}\n\n// TODO: it's silly and kind of fragile that we look at app config to determine\n// the ios project paths. Overall this function needs to be revamped, just a\n// placeholder for now! Make this more robust when we support applying config\n// at any time (currently it's only applied on eject).\nexport function getHackyProjectName(projectRoot: string, config: ExpoConfig): string {\n  // Attempt to get the current ios folder name (apply).\n  try {\n    return getProjectName(projectRoot);\n  } catch {\n    // If no iOS project exists then create a new one (eject).\n    const projectName = config.name;\n    assert(projectName, 'Your project needs a name in app.json/app.config.js.');\n    return sanitizedName(projectName);\n  }\n}\n\nfunction createProjectFileForGroup({ filepath, group }: { filepath: string; group: PBXGroup }) {\n  const file = new pbxFile(filepath);\n\n  const conflictingFile = group.children.find(child => child.comment === file.basename);\n  if (conflictingFile) {\n    // This can happen when a file like the GoogleService-Info.plist needs to be added and the eject command is run twice.\n    // Not much we can do here since it might be a conflicting file.\n    return null;\n  }\n  return file;\n}\n\n/**\n * Add a resource file (ex: `SplashScreen.storyboard`, `Images.xcassets`) to an Xcode project.\n * This is akin to creating a new code file in Xcode with `⌘+n`.\n */\nexport function addResourceFileToGroup({\n  filepath,\n  groupName,\n  // Should add to `PBXBuildFile Section`\n  isBuildFile,\n  project,\n}: {\n  filepath: string;\n  groupName: string;\n  isBuildFile?: boolean;\n  project: XcodeProject;\n}): XcodeProject {\n  return addFileToGroupAndLink({\n    filepath,\n    groupName,\n    project,\n    addFileToProject({ project, file }) {\n      project.addToPbxFileReferenceSection(file);\n      if (isBuildFile) {\n        project.addToPbxBuildFileSection(file);\n      }\n      project.addToPbxResourcesBuildPhase(file);\n    },\n  });\n}\n\n/**\n * Add a build source file (ex: `AppDelegate.m`, `ViewController.swift`) to an Xcode project.\n * This is akin to creating a new code file in Xcode with `⌘+n`.\n */\nexport function addBuildSourceFileToGroup({\n  filepath,\n  groupName,\n  project,\n}: {\n  filepath: string;\n  groupName: string;\n  project: XcodeProject;\n}): XcodeProject {\n  return addFileToGroupAndLink({\n    filepath,\n    groupName,\n    project,\n    addFileToProject({ project, file }) {\n      project.addToPbxFileReferenceSection(file);\n      project.addToPbxBuildFileSection(file);\n      project.addToPbxSourcesBuildPhase(file);\n    },\n  });\n}\n\n// TODO(brentvatne): I couldn't figure out how to do this with an existing\n// higher level function exposed by the xcode library, but we should find out how to do\n// that and replace this with it\nexport function addFileToGroupAndLink({\n  filepath,\n  groupName,\n  project,\n  addFileToProject,\n}: {\n  filepath: string;\n  groupName: string;\n  project: XcodeProject;\n  addFileToProject: (props: { file: PBXFile; project: XcodeProject }) => void;\n}): XcodeProject {\n  const group = pbxGroupByPathOrAssert(project, groupName);\n\n  const file = createProjectFileForGroup({ filepath, group });\n\n  if (!file) {\n    // This can happen when a file like the GoogleService-Info.plist needs to be added and the eject command is run twice.\n    // Not much we can do here since it might be a conflicting file.\n    addWarningIOS(\n      'ios-xcode-project',\n      `Skipped adding duplicate file \"${filepath}\" to PBXGroup named \"${groupName}\"`\n    );\n    return project;\n  }\n\n  file.uuid = project.generateUuid();\n  file.fileRef = project.generateUuid();\n\n  addFileToProject({ project, file });\n\n  group.children.push({\n    value: file.fileRef,\n    comment: file.basename,\n  });\n  return project;\n}\n\nexport function getApplicationNativeTarget({\n  project,\n  projectName,\n}: {\n  project: XcodeProject;\n  projectName: string;\n}) {\n  const applicationNativeTarget = project.getTarget('com.apple.product-type.application');\n  assert(\n    applicationNativeTarget,\n    `Couldn't locate application PBXNativeTarget in '.xcodeproj' file.`\n  );\n  assert(\n    String(applicationNativeTarget.target.name) === projectName,\n    `Application native target name mismatch. Expected ${projectName}, but found ${applicationNativeTarget.target.name}.`\n  );\n  return applicationNativeTarget;\n}\n\n/**\n * Add a framework to the default app native target.\n *\n * @param projectName Name of the PBX project.\n * @param framework String ending in `.framework`, i.e. `StoreKit.framework`\n */\nexport function addFramework({\n  project,\n  projectName,\n  framework,\n}: {\n  project: XcodeProject;\n  projectName: string;\n  framework: string;\n}) {\n  const target = getApplicationNativeTarget({ project, projectName });\n  return project.addFramework(framework, { target: target.uuid });\n}\n\nfunction splitPath(path: string): string[] {\n  // TODO: Should we account for other platforms that may not use `/`\n  return path.split('/');\n}\n\nconst findGroup = (\n  group: PBXGroup | undefined,\n  name: string\n):\n  | {\n      value: UUID;\n      comment?: string;\n    }\n  | undefined => {\n  if (!group) {\n    return undefined;\n  }\n\n  return group.children.find(group => group.comment === name);\n};\n\nfunction findGroupInsideGroup(\n  project: XcodeProject,\n  group: PBXGroup | undefined,\n  name: string\n): null | PBXGroup {\n  const foundGroup = findGroup(group, name);\n  if (foundGroup) {\n    return project.getPBXGroupByKey(foundGroup.value) ?? null;\n  }\n  return null;\n}\n\nfunction pbxGroupByPathOrAssert(project: XcodeProject, path: string): PBXGroup {\n  const { firstProject } = project.getFirstProject();\n\n  let group = project.getPBXGroupByKey(firstProject.mainGroup);\n\n  const components = splitPath(path);\n  for (const name of components) {\n    const nextGroup = findGroupInsideGroup(project, group, name);\n    if (nextGroup) {\n      group = nextGroup;\n    } else {\n      break;\n    }\n  }\n\n  if (!group) {\n    throw Error(`Xcode PBXGroup with name \"${path}\" could not be found in the Xcode project.`);\n  }\n\n  return group;\n}\n\nexport function ensureGroupRecursively(project: XcodeProject, filepath: string): PBXGroup | null {\n  const components = splitPath(filepath);\n  const hasChild = (group: PBXGroup, name: string) =>\n    group.children.find(({ comment }) => comment === name);\n  const { firstProject } = project.getFirstProject();\n\n  let topMostGroup = project.getPBXGroupByKey(firstProject.mainGroup);\n\n  for (const pathComponent of components) {\n    if (topMostGroup && !hasChild(topMostGroup, pathComponent)) {\n      topMostGroup.children.push({\n        comment: pathComponent,\n        value: project.pbxCreateGroup(pathComponent, '\"\"'),\n      });\n    }\n    topMostGroup = project.pbxGroupByName(pathComponent);\n  }\n  return topMostGroup ?? null;\n}\n\n/**\n * Get the pbxproj for the given path\n */\nexport function getPbxproj(projectRoot: string): XcodeProject {\n  const projectPath = Paths.getPBXProjectPath(projectRoot);\n  const project = xcode.project(projectPath);\n  project.parseSync();\n  return project;\n}\n\n/**\n * Get the productName for a project, if the name is using a variable `$(TARGET_NAME)`, then attempt to get the value of that variable.\n *\n * @param project\n */\nexport function getProductName(project: XcodeProject): string {\n  let productName = project.productName;\n\n  if (productName === '$(TARGET_NAME)') {\n    const targetName = project.getFirstTarget()?.firstTarget?.productName;\n    productName = targetName ?? project.productName;\n  }\n\n  return productName;\n}\n\nexport function getProjectSection(project: XcodeProject) {\n  return project.pbxProjectSection();\n}\n\nexport function getNativeTargets(project: XcodeProject): NativeTargetSectionEntry[] {\n  const section = project.pbxNativeTargetSection();\n  return Object.entries(section).filter(isNotComment);\n}\n\nexport function findFirstNativeTarget(project: XcodeProject): NativeTargetSectionEntry {\n  const { targets } = Object.values(getProjectSection(project))[0];\n  const target = targets[0].value;\n  const nativeTargets = getNativeTargets(project);\n  return nativeTargets.find(([key]) => key === target) as NativeTargetSectionEntry;\n}\n\nexport function findNativeTargetByName(\n  project: XcodeProject,\n  targetName: string\n): NativeTargetSectionEntry {\n  const nativeTargets = getNativeTargets(project);\n  return nativeTargets.find(\n    ([, i]) => i.name === targetName || i.name === `\"${targetName}\"`\n  ) as NativeTargetSectionEntry;\n}\n\nexport function getXCConfigurationListEntries(project: XcodeProject): ConfigurationListEntry[] {\n  const lists = project.pbxXCConfigurationList();\n  return Object.entries(lists).filter(isNotComment);\n}\n\nexport function getBuildConfigurationForId(\n  project: XcodeProject,\n  configurationListId: string\n): ConfigurationSectionEntry[] {\n  const configurationListEntries = getXCConfigurationListEntries(project);\n  const [, configurationList] = configurationListEntries.find(\n    ([key]) => key === configurationListId\n  ) as ConfigurationListEntry;\n\n  const buildConfigurations = configurationList.buildConfigurations.map(i => i.value);\n\n  return Object.entries(project.pbxXCBuildConfigurationSection())\n    .filter(isNotComment)\n    .filter(isBuildConfig)\n    .filter(isNotTestHost)\n    .filter(([key]: ConfigurationSectionEntry) => buildConfigurations.includes(key));\n}\n\nexport function isBuildConfig([, sectionItem]: ConfigurationSectionEntry): boolean {\n  return sectionItem.isa === 'XCBuildConfiguration';\n}\n\nexport function isNotTestHost([, sectionItem]: ConfigurationSectionEntry): boolean {\n  return !sectionItem.buildSettings.TEST_HOST;\n}\n\nexport function isNotComment([key]:\n  | ConfigurationSectionEntry\n  | ProjectSectionEntry\n  | ConfigurationListEntry\n  | NativeTargetSectionEntry): boolean {\n  return !key.endsWith(`_comment`);\n}\n"]}