Make `react-native link` play nicely with CocoaPods-based iOS projects.
Summary: The core React Native codebase already has full support for CocoaPods. However, `react-native link` doesn’t play nicely with CocoaPods, so installing third-party libs from the RN ecosystem is really hard. This change will allow to link projects that contains its own `.podspec` file to CocoaPods-based projects. In case `link` detect `Podfile` in `iOS` directory, it will look for related `.podspec` file in linked project directory, and add it to `Podfile`. If `Podfile` and `.podspec` files are not present, it will fall back to previous implementation. **Test Plan** 1. Build a React Native project where the iOS part uses CocoaPods to manage its dependencies. The most common scenario here is to have React Native be a Pod dependency, among others. 2. Install a RN-related library, that contains `.podspec` file, with `react-native link` (as an example it could be: [react-native-maps](https://github.com/airbnb/react-native-maps) 3. Building the resulting iOS workspace should succeed (and there should be new entry in `Podfile`) Closes https://github.com/facebook/react-native/pull/15460 Differential Revision: D6078649 Pulled By: hramos fbshipit-source-id: 9651085875892fd66299563ca0e42fb2bcc00825
This commit is contained in:
parent
ce937e5b3f
commit
74146cb315
|
@ -35,8 +35,8 @@ Install a library with native dependencies:
|
|||
$ npm install <library-with-native-dependencies> --save
|
||||
```
|
||||
|
||||
**Note:** _`--save` or `--save-dev` flag is very important for this step. React Native will link
|
||||
your libs based on `dependencies` and `devDependencies` in your `package.json` file._
|
||||
> ***Note:*** `--save` or `--save-dev` flag is very important for this step. React Native will link
|
||||
your libs based on `dependencies` and `devDependencies` in your `package.json` file.
|
||||
|
||||
#### Step 2
|
||||
|
||||
|
@ -47,6 +47,10 @@ $ react-native link
|
|||
|
||||
Done! All libraries with native dependencies should be successfully linked to your iOS/Android project.
|
||||
|
||||
> ***Note:*** If your iOS project is using CocoaPods (contains `Podfile`) and linked library has `podspec` file,
|
||||
then `react-native link` will link library using Podfile. To support non-trivial Podfiles
|
||||
add `# Add new pods below this line` comment to places where you expect pods to be added.
|
||||
|
||||
### Manual linking
|
||||
|
||||
#### Step 1
|
||||
|
|
|
@ -5,6 +5,7 @@ exports.valid = {
|
|||
'demoProject.xcodeproj': {
|
||||
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
|
||||
},
|
||||
'TestPod.podspec': 'empty'
|
||||
};
|
||||
|
||||
exports.validTestName = {
|
||||
|
@ -12,3 +13,7 @@ exports.validTestName = {
|
|||
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
|
||||
},
|
||||
};
|
||||
|
||||
exports.pod = {
|
||||
'TestPod.podspec': 'empty'
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ const ios = require('./ios');
|
|||
const flat = {
|
||||
android: android.valid,
|
||||
ios: ios.valid,
|
||||
Podfile: 'empty'
|
||||
};
|
||||
|
||||
const nested = {
|
||||
|
@ -19,4 +20,9 @@ const withExamples = {
|
|||
android: android.valid,
|
||||
};
|
||||
|
||||
module.exports = { flat, nested, withExamples };
|
||||
const withPods = {
|
||||
Podfile: 'content',
|
||||
ios: ios.pod
|
||||
};
|
||||
|
||||
module.exports = { flat, nested, withExamples, withPods };
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
const findPodfilePath = require('../../ios/findPodfilePath');
|
||||
const fs = require('fs');
|
||||
const projects = require('../../__fixtures__/projects');
|
||||
const ios = require('../../__fixtures__/ios');
|
||||
|
||||
describe('ios::findPodfilePath', () => {
|
||||
it('returns null if there is no Podfile', () => {
|
||||
fs.__setMockFilesystem(ios.valid);
|
||||
expect(findPodfilePath('')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns Podfile path if it exists', () => {
|
||||
fs.__setMockFilesystem(projects.withPods);
|
||||
expect(findPodfilePath('/ios')).toContain('Podfile');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
'use strict';
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
const findPodspecName = require('../../ios/findPodspecName');
|
||||
const fs = require('fs');
|
||||
const projects = require('../../__fixtures__/projects');
|
||||
const ios = require('../../__fixtures__/ios');
|
||||
|
||||
describe('ios::findPodspecName', () => {
|
||||
it('returns null if there is not podspec file', () => {
|
||||
fs.__setMockFilesystem(projects.flat);
|
||||
expect(findPodspecName('')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns podspec name if only one exists', () => {
|
||||
fs.__setMockFilesystem(ios.pod);
|
||||
expect(findPodspecName('/')).toBe('TestPod');
|
||||
});
|
||||
|
||||
it('returns podspec name that match packet directory', () => {
|
||||
fs.__setMockFilesystem({
|
||||
user: {
|
||||
PacketName: {
|
||||
'Another.podspec': 'empty',
|
||||
'PacketName.podspec': 'empty'
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(findPodspecName('/user/PacketName')).toBe('PacketName');
|
||||
});
|
||||
|
||||
it('returns first podspec name if not match in directory', () => {
|
||||
fs.__setMockFilesystem({
|
||||
user: {
|
||||
packet: {
|
||||
'Another.podspec': 'empty',
|
||||
'PacketName.podspec': 'empty'
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(findPodspecName('/user/packet')).toBe('Another');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function findPodfilePath(projectFolder) {
|
||||
const podFilePath = path.join(projectFolder, '..', 'Podfile');
|
||||
const podFileExists = fs.existsSync(podFilePath);
|
||||
|
||||
return podFileExists ? podFilePath : null;
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
const glob = require('glob');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function findPodspecName(folder) {
|
||||
const podspecs = glob.sync('*.podspec', { cwd: folder });
|
||||
let podspecFile = null;
|
||||
if (podspecs.length === 0) {
|
||||
return null;
|
||||
}
|
||||
else if (podspecs.length === 1) {
|
||||
podspecFile = podspecs[0];
|
||||
}
|
||||
else {
|
||||
const folderParts = folder.split(path.sep);
|
||||
const currentFolder = folderParts[folderParts.length - 1];
|
||||
const toSelect = podspecs.indexOf(currentFolder + '.podspec');
|
||||
if (toSelect === -1) {
|
||||
podspecFile = podspecs[0];
|
||||
}
|
||||
else {
|
||||
podspecFile = podspecs[toSelect];
|
||||
}
|
||||
}
|
||||
|
||||
return podspecFile.replace('.podspec', '');
|
||||
};
|
|
@ -9,6 +9,8 @@
|
|||
'use strict';
|
||||
|
||||
const findProject = require('./findProject');
|
||||
const findPodfilePath = require('./findPodfilePath');
|
||||
const findPodspecName = require('./findPodspecName');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
|
@ -44,6 +46,8 @@ exports.projectConfig = function projectConfigIOS(folder, userConfig) {
|
|||
sourceDir: path.dirname(projectPath),
|
||||
folder: folder,
|
||||
pbxprojPath: path.join(projectPath, 'project.pbxproj'),
|
||||
podfile: findPodfilePath(projectPath),
|
||||
podspec: findPodspecName(folder),
|
||||
projectPath: projectPath,
|
||||
projectName: path.basename(projectPath),
|
||||
libraryFolder: userConfig.libraryFolder || 'Libraries',
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
platform :ios, '9.0'
|
||||
|
||||
target 'Testing' do
|
||||
pod 'TestPod', '~> 3.1'
|
||||
|
||||
# test should point to this line
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
platform :ios, '9.0'
|
||||
|
||||
target 'none' do
|
||||
pod 'React',
|
||||
:path => "../node_modules/react-native",
|
||||
:subspecs => [
|
||||
"Core",
|
||||
"ART",
|
||||
"RCTActionSheet",
|
||||
"RCTAnimation",
|
||||
"RCTCameraRoll",
|
||||
"RCTGeolocation",
|
||||
"RCTImage",
|
||||
"RCTNetwork",
|
||||
"RCTText",
|
||||
"RCTVibration",
|
||||
"RCTWebSocket",
|
||||
"DevSupport",
|
||||
"BatchedBridge"
|
||||
]
|
||||
|
||||
pod 'Yoga',
|
||||
:path => "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
# test should point to this line
|
||||
post_install do |installer|
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
# platform :ios, '9.0'
|
||||
|
||||
target 'None' do
|
||||
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
|
||||
# use_frameworks!
|
||||
# Your 'node_modules' directory is probably in the root of your project, # but if not, adjust the `:path` accordingly
|
||||
pod 'React', :path => '../node_modules/react-native', :subspecs => [
|
||||
'Core',
|
||||
'RCTText',
|
||||
'RCTNetwork',
|
||||
'BatchedBridge',
|
||||
'RCTImage',
|
||||
'RCTWebSocket', # needed for debugging
|
||||
# Add any other subspecs you want to use in your project
|
||||
]
|
||||
|
||||
# Add new pods below this line
|
||||
|
||||
# test should point to this line
|
||||
target 'NoneTests' do
|
||||
inherit! :search_paths
|
||||
# Pods for testing
|
||||
end
|
||||
end
|
||||
|
||||
target 'Second' do
|
||||
|
||||
target 'NoneUITests' do
|
||||
inherit! :search_paths
|
||||
# Add new pods below this line
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
# platform :ios, '9.0'
|
||||
|
||||
target 'None' do
|
||||
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
|
||||
# use_frameworks!
|
||||
# Your 'node_modules' directory is probably in the root of your project, # but if not, adjust the `:path` accordingly
|
||||
pod 'React', :path => '../node_modules/react-native', :subspecs => [
|
||||
'Core',
|
||||
'RCTText',
|
||||
'RCTNetwork',
|
||||
'BatchedBridge',
|
||||
'RCTImage',
|
||||
'RCTWebSocket', # needed for debugging
|
||||
# Add any other subspecs you want to use in your project
|
||||
]
|
||||
|
||||
# Explicitly include Yoga if you are using RN >= 0.42.0
|
||||
pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
# test should point to this line
|
||||
target 'NoneTests' do
|
||||
inherit! :search_paths
|
||||
# Pods for testing
|
||||
end
|
||||
|
||||
target 'NoneUITests' do
|
||||
inherit! :search_paths
|
||||
# Pods for testing
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const findLineToAddPod = require('../../pods/findLineToAddPod');
|
||||
const readPodfile = require('../../pods/readPodfile');
|
||||
|
||||
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
|
||||
const LINE_AFTER_TARGET_IN_TEST_PODFILE = 4;
|
||||
|
||||
describe('pods::findLineToAddPod', () => {
|
||||
it('returns null if file is not Podfile', () => {
|
||||
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
|
||||
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns correct line number for Simple Podfile', () => {
|
||||
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
|
||||
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 7, indentation: 2 });
|
||||
});
|
||||
|
||||
it('returns correct line number for Podfile with target', () => {
|
||||
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithTarget'));
|
||||
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 21, indentation: 2 });
|
||||
});
|
||||
|
||||
it('returns correct line number for Podfile with function', () => {
|
||||
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithFunction'));
|
||||
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 26, indentation: 2 });
|
||||
});
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const readPodfile = require('../../pods/readPodfile');
|
||||
const findMarkedLinesInPodfile = require('../../pods/findMarkedLinesInPodfile');
|
||||
|
||||
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
|
||||
const LINE_AFTER_TARGET_IN_TEST_PODFILE = 4;
|
||||
|
||||
describe('pods::findMarkedLinesInPodfile', () => {
|
||||
it('returns empty array if file is not Podfile', () => {
|
||||
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
|
||||
expect(findMarkedLinesInPodfile(podfile)).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns empty array for Simple Podfile', () => {
|
||||
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
|
||||
expect(findMarkedLinesInPodfile(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns correct line numbers for Podfile with marker', () => {
|
||||
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithMarkers'));
|
||||
const expectedObject = [{ line: 18, indentation: 2 }, { line: 31, indentation: 4 }];
|
||||
expect(findMarkedLinesInPodfile(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual(expectedObject);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const findPodTargetLine = require('../../pods/findPodTargetLine');
|
||||
const readPodfile = require('../../pods/readPodfile');
|
||||
|
||||
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
|
||||
|
||||
describe('pods::findPodTargetLine', () => {
|
||||
it('returns null if file is not Podfile', () => {
|
||||
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
|
||||
expect(findPodTargetLine(podfile, 'name')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null if there is not matching project name', () => {
|
||||
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
|
||||
expect(findPodTargetLine(podfile, 'invalidName')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null if there is not matching project name', () => {
|
||||
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
|
||||
expect(findPodTargetLine(podfile, 'Testing')).toBe(4);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const isInstalled = require('../../pods/isInstalled');
|
||||
|
||||
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
|
||||
|
||||
describe('pods::isInstalled', () => {
|
||||
it('returns false if pod is missing', () => {
|
||||
const project = { podfile: path.join(PODFILES_PATH, 'PodfileSimple') };
|
||||
const podspecName = { podspec: 'NotExisting' };
|
||||
expect(isInstalled(project, podspecName)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for existing pod with version number', () => {
|
||||
const project = { podfile: path.join(PODFILES_PATH, 'PodfileSimple') };
|
||||
const podspecName = { podspec: 'TestPod' };
|
||||
expect(isInstalled(project, podspecName)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for existing pod with path', () => {
|
||||
const project = { podfile: path.join(PODFILES_PATH, 'PodfileWithTarget') };
|
||||
const podspecName = { podspec: 'Yoga' };
|
||||
expect(isInstalled(project, podspecName)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for existing pod with multiline definition', () => {
|
||||
const project = { podfile: path.join(PODFILES_PATH, 'PodfileWithFunction') };
|
||||
const podspecName = { podspec: 'React' };
|
||||
expect(isInstalled(project, podspecName)).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const removePodEntry = require('../../pods/removePodEntry');
|
||||
const readPodfile = require('../../pods/readPodfile');
|
||||
|
||||
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
|
||||
|
||||
describe('pods::removePodEntry', () => {
|
||||
it('should remove one line from Podfile with TestPod', () => {
|
||||
const { podfileContent, podLinesCount } = readTestPodFile('PodfileSimple');
|
||||
const podFileWithRemoved = removePodEntry(podfileContent, 'TestPod');
|
||||
const newLineCount = podFileWithRemoved.split('\n').length;
|
||||
expect(newLineCount).toBe(podLinesCount - 1);
|
||||
});
|
||||
|
||||
it('should remove one line from Podfile with Yoga', () => {
|
||||
const { podfileContent, podLinesCount } = readTestPodFile('PodfileWithTarget');
|
||||
const podFileWithRemoved = removePodEntry(podfileContent, 'Yoga');
|
||||
const newLineCount = podFileWithRemoved.split('\n').length;
|
||||
expect(newLineCount).toBe(podLinesCount - 1);
|
||||
});
|
||||
|
||||
it('should remove whole reference to React pod from Podfile', () => {
|
||||
const { podfileContent, podLinesCount } = readTestPodFile('PodfileWithTarget');
|
||||
const podFileWithRemoved = removePodEntry(podfileContent, 'React');
|
||||
const newLineCount = podFileWithRemoved.split('\n').length;
|
||||
expect(newLineCount).toBe(podLinesCount - 9);
|
||||
});
|
||||
});
|
||||
|
||||
function readTestPodFile(fileName) {
|
||||
const podfileLines = readPodfile(path.join(PODFILES_PATH, fileName));
|
||||
return { podfileContent: podfileLines.join('\n'), podLinesCount: podfileLines.length };
|
||||
}
|
|
@ -29,9 +29,11 @@ const promiseWaterfall = require('./promiseWaterfall');
|
|||
const registerDependencyAndroid = require('./android/registerNativeModule');
|
||||
const registerDependencyWindows = require('./windows/registerNativeModule');
|
||||
const registerDependencyIOS = require('./ios/registerNativeModule');
|
||||
const registerDependencyPods = require('./pods/registerNativeModule');
|
||||
const isInstalledAndroid = require('./android/isInstalled');
|
||||
const isInstalledWindows = require('./windows/isInstalled');
|
||||
const isInstalledIOS = require('./ios/isInstalled');
|
||||
const isInstalledPods = require('./pods/isInstalled');
|
||||
const copyAssetsAndroid = require('./android/copyAssets');
|
||||
const copyAssetsIOS = require('./ios/copyAssets');
|
||||
const getProjectDependencies = require('./getProjectDependencies');
|
||||
|
@ -106,17 +108,19 @@ const linkDependencyIOS = (iOSProject, dependency) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const isInstalled = isInstalledIOS(iOSProject, dependency.config.ios);
|
||||
|
||||
const isInstalled = isInstalledIOS(iOSProject, dependency.config.ios) || isInstalledPods(iOSProject, dependency.config.ios);
|
||||
if (isInstalled) {
|
||||
log.info(chalk.grey(`iOS module ${dependency.name} is already linked`));
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(`Linking ${dependency.name} ios dependency`);
|
||||
|
||||
registerDependencyIOS(dependency.config.ios, iOSProject);
|
||||
|
||||
if (iOSProject.podfile && dependency.config.ios.podspec) {
|
||||
registerDependencyPods(dependency, iOSProject);
|
||||
}
|
||||
else {
|
||||
registerDependencyIOS(dependency.config.ios, iOSProject);
|
||||
}
|
||||
log.info(`iOS module ${dependency.name} has been successfully linked`);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function addPodEntry(podLines, linesToAddEntry, podName, nodePath) {
|
||||
const newEntry = `pod '${podName}', :path => '../node_modules/${nodePath}'\n`;
|
||||
|
||||
if (!linesToAddEntry) {
|
||||
return;
|
||||
} else if (Array.isArray(linesToAddEntry)) {
|
||||
linesToAddEntry.map(({ line, indentation }, idx) =>
|
||||
podLines.splice(line + idx, 0, getLineToAdd(newEntry, indentation))
|
||||
);
|
||||
} else {
|
||||
const { line, indentation } = linesToAddEntry;
|
||||
podLines.splice(line, 0, getLineToAdd(newEntry, indentation));
|
||||
}
|
||||
};
|
||||
|
||||
function getLineToAdd(newEntry, indentation) {
|
||||
const spaces = Array(indentation + 1).join(' ');
|
||||
return spaces + newEntry;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function findLineToAddPod(podLines, firstTargetLine) {
|
||||
// match line with new target: target 'project_name' do (most likely target inside podfile main target)
|
||||
const nextTarget = /target (\'|\")\w+(\'|\") do/g;
|
||||
// match line that has only 'end' (if we don't catch new target or function, this would mean this is end of current target)
|
||||
const endOfCurrentTarget = /^\s*end\s*$/g;
|
||||
// match function definition, like: post_install do |installer| (some Podfiles have function defined inside main target
|
||||
const functionDefinition = /^\s*[a-z_]+\s+do(\s+\|[a-z]+\|)?/g;
|
||||
|
||||
for (let i = firstTargetLine, len = podLines.length; i < len; i++) {
|
||||
const matchNextConstruct = podLines[i].match(nextTarget) || podLines[i].match(functionDefinition);
|
||||
const matchEnd = podLines[i].match(endOfCurrentTarget);
|
||||
|
||||
if (matchNextConstruct || matchEnd) {
|
||||
const firstNonSpaceCharacter = podLines[i].search(/\S/);
|
||||
return {
|
||||
indentation: firstNonSpaceCharacter + (matchEnd ? 2 : 0),
|
||||
line: i
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
const MARKER_TEXT = '# Add new pods below this line';
|
||||
|
||||
module.exports = function findMarkedLinesInPodfile(podLines) {
|
||||
const result = [];
|
||||
for (let i = 0, len = podLines.length; i < len; i++) {
|
||||
if (podLines[i].includes(MARKER_TEXT)) {
|
||||
result.push({ line: i + 1, indentation: podLines[i].indexOf('#') });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function findPodTargetLine(podLines, projectName) {
|
||||
const targetName = projectName.replace('.xcodeproj', '');
|
||||
//match first target definition in file: target 'target_name' do
|
||||
const targetRegex = new RegExp('target (\'|\")' + targetName + '(\'|\") do', 'g');
|
||||
for (let i = 0, len = podLines.length; i < len; i++) {
|
||||
const match = podLines[i].match(targetRegex);
|
||||
if (match) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
const readPodfile = require('./readPodfile');
|
||||
|
||||
module.exports = function isInstalled(iOSProject, dependencyConfig) {
|
||||
if (!iOSProject.podfile) {
|
||||
return false;
|
||||
}
|
||||
// match line with pod declaration: pod 'dependencyPodName' (other possible parameters of pod are ignored)
|
||||
const dependencyRegExp = new RegExp('pod\\s+(\'|\")' + dependencyConfig.podspec + '(\'|\")', 'g');
|
||||
const podLines = readPodfile(iOSProject.podfile);
|
||||
for (let i = 0, len = podLines.length; i < len; i++) {
|
||||
const match = podLines[i].match(dependencyRegExp);
|
||||
if (match) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
module.exports = function readPodfile(podfilePath) {
|
||||
const podContent = fs.readFileSync(podfilePath, 'utf8');
|
||||
return podContent.split(/\r?\n/g);
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
const readPodfile = require('./readPodfile');
|
||||
const findPodTargetLine = require('./findPodTargetLine');
|
||||
const findLineToAddPod = require('./findLineToAddPod');
|
||||
const findMarkedLinesInPodfile = require('./findMarkedLinesInPodfile');
|
||||
const addPodEntry = require('./addPodEntry');
|
||||
const savePodFile = require('./savePodFile');
|
||||
|
||||
module.exports = function registerNativeModulePods(dependency, iOSProject) {
|
||||
const podLines = readPodfile(iOSProject.podfile);
|
||||
const linesToAddEntry = getLinesToAddEntry(podLines, iOSProject);
|
||||
addPodEntry(podLines, linesToAddEntry, dependency.config.ios.podspec, dependency.name);
|
||||
savePodFile(iOSProject.podfile, podLines);
|
||||
};
|
||||
|
||||
function getLinesToAddEntry(podLines, { projectName }) {
|
||||
const linesToAddPodWithMarker = findMarkedLinesInPodfile(podLines);
|
||||
if (linesToAddPodWithMarker.length > 0) {
|
||||
return linesToAddPodWithMarker;
|
||||
} else {
|
||||
const firstTargetLined = findPodTargetLine(podLines, projectName);
|
||||
return findLineToAddPod(podLines, firstTargetLined);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function removePodEntry(podfileContent, podName) {
|
||||
// this regex should catch line(s) with full pod definition, like: pod 'podname', :path => '../node_modules/podname', :subspecs => ['Sub2', 'Sub1']
|
||||
const podRegex = new RegExp("\\n( |\\t)*pod\\s+(\"|')" + podName + "(\"|')(,\\s*(:[a-z]+\\s*=>)?\\s*((\"|').*?(\"|')|\\[[\\s\\S]*?\\]))*\\n", 'g');
|
||||
return podfileContent.replace(podRegex, '\n');
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
module.exports = function savePodFile(podfilePath, podLines) {
|
||||
const newPodfile = podLines.join('\n');
|
||||
fs.writeFileSync(podfilePath, newPodfile);
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const removePodEntry = require('./removePodEntry');
|
||||
|
||||
/**
|
||||
* Unregister native module IOS with CocoaPods
|
||||
*/
|
||||
module.exports = function unregisterNativeModule(dependencyConfig, iOSProject) {
|
||||
const podContent = fs.readFileSync(iOSProject.podfile, 'utf8');
|
||||
const removed = removePodEntry(podContent, dependencyConfig.podspec);
|
||||
fs.writeFileSync(iOSProject.podfile, removed);
|
||||
};
|
|
@ -4,16 +4,17 @@ const getProjectDependencies = require('./getProjectDependencies');
|
|||
const unregisterDependencyAndroid = require('./android/unregisterNativeModule');
|
||||
const unregisterDependencyWindows = require('./windows/unregisterNativeModule');
|
||||
const unregisterDependencyIOS = require('./ios/unregisterNativeModule');
|
||||
const unregisterDependencyPods = require('./pods/unregisterNativeModule');
|
||||
const isInstalledAndroid = require('./android/isInstalled');
|
||||
const isInstalledWindows = require('./windows/isInstalled');
|
||||
const isInstalledIOS = require('./ios/isInstalled');
|
||||
const isInstalledPods = require('./pods/isInstalled');
|
||||
const unlinkAssetsAndroid = require('./android/unlinkAssets');
|
||||
const unlinkAssetsIOS = require('./ios/unlinkAssets');
|
||||
const getDependencyConfig = require('./getDependencyConfig');
|
||||
const compact = require('lodash').compact;
|
||||
const difference = require('lodash').difference;
|
||||
const filter = require('lodash').filter;
|
||||
const find = require('lodash').find;
|
||||
const flatten = require('lodash').flatten;
|
||||
const isEmpty = require('lodash').isEmpty;
|
||||
const promiseWaterfall = require('./promiseWaterfall');
|
||||
|
@ -65,16 +66,21 @@ const unlinkDependencyIOS = (iOSProject, dependency, packageName, iOSDependencie
|
|||
return;
|
||||
}
|
||||
|
||||
const isInstalled = isInstalledIOS(iOSProject, dependency.ios);
|
||||
|
||||
if (!isInstalled) {
|
||||
const isIosInstalled = isInstalledIOS(iOSProject, dependency.ios);
|
||||
const isPodInstalled = isInstalledPods(iOSProject, dependency.ios);
|
||||
if (!isIosInstalled && !isPodInstalled) {
|
||||
log.info(`iOS module ${packageName} is not installed`);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(`Unlinking ${packageName} ios dependency`);
|
||||
|
||||
unregisterDependencyIOS(dependency.ios, iOSProject, iOSDependencies);
|
||||
if (isIosInstalled) {
|
||||
unregisterDependencyIOS(dependency.ios, iOSProject, iOSDependencies);
|
||||
}
|
||||
else if (isPodInstalled) {
|
||||
unregisterDependencyPods(dependency.ios, iOSProject);
|
||||
}
|
||||
|
||||
log.info(`iOS module ${packageName} has been successfully unlinked`);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue