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