Skip to content

Commit 9744d0c

Browse files
committed
Improve params UX and fix broken unlink on Android (#63)
* Some suggestions on how to make it async * Make promiseWaterfall resolve with an array of all values at the end, just like * Fix tests * Remove old files * Bring back command * Fix naming * Add tests * Further tweaks * Use instead * Do not check inside registerNativeModule * Remove space * Fixes and remove , can be added in a sep. pr * Move error handler to the end * Make it nicer * Naming * Fix info msg on ios * Use the same isInstalled check - @Kureev note that these checks were different, you were once checking in settings.gradle, 2nd time in import * Fix unlink - settings.gradle patch * Fix unlink - do the same for build.gradle just in case * Unit test isInstalledIOS * Make it config.params back * Test missing library group as well * Revert "Fix unlink - do the same for build.gradle just in case" This reverts commit 046336d. * Revert "Fix unlink - settings.gradle patch" This reverts commit ac99169. * Make sure isInstalled is used properly * Unit test isInstalledAndroid & make sure it returns bool * Add iOS docs * Update to `params`
1 parent ac758de commit 9744d0c

File tree

9 files changed

+201
-57
lines changed

9 files changed

+201
-57
lines changed

src/android/isInstalled.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const compose = require('lodash').flowRight;
2+
const readFile = require('./fs').readFile;
3+
4+
module.exports = function isInstalled(projectConfig, name) {
5+
return compose(
6+
(content) => content.indexOf(`:${name}`) >= 0,
7+
readFile(projectConfig.buildGradlePath)
8+
)();
9+
};

src/android/registerNativeModule.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,9 @@ module.exports = function registerNativeAndroidModule(name, androidConfig, param
2828
makeMainActivityPatch(androidConfig, params)
2929
);
3030

31-
/**
32-
* Check if module has been installed already
33-
*/
34-
const isInstalled = compose(
35-
(content) => ~content.indexOf(`:${name}`),
36-
readFile(projectConfig.buildGradlePath)
37-
);
38-
39-
if (isInstalled(name)) {
40-
return false;
41-
}
42-
4331
compose(
4432
performSettingsGradlePatch,
4533
performBuildGradlePatch,
4634
performMainActivityPatch
4735
)();
48-
49-
return true;
5036
};

src/android/unregisterNativeModule.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const path = require('path');
44
const compose = require('lodash').flowRight;
55
const getReactVersion = require('../getReactNativeVersion');
66
const getPrefix = require('./getPrefix');
7+
const isInstalled = require('./isInstalled');
78

89
const cut = (scope, pattern) =>
910
scope.replace(pattern, '');
@@ -60,15 +61,7 @@ module.exports = function unregisterNativeAndroidModule(name, dependencyConfig,
6061
readFile(projectConfig.mainActivityPath)
6162
);
6263

63-
/**
64-
* Check if module has been installed already
65-
*/
66-
const isInstalled = compose(
67-
(content) => ~content.indexOf(getAddPackagePatch(dependencyConfig)),
68-
readFile(projectConfig.mainActivityPath)
69-
);
70-
71-
if (!isInstalled(name)) {
64+
if (!isInstalled(projectConfig, name)) {
7265
return false;
7366
}
7467

src/ios/isInstalled.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const xcode = require('xcode');
2+
const getGroup = require('./getGroup');
3+
const hasLibraryImported = require('./hasLibraryImported');
4+
5+
/**
6+
* Returns true if `xcodeproj` specified by dependencyConfig is present
7+
* in a top level `libraryFolder`
8+
*/
9+
module.exports = function isInstalled(projectConfig, dependencyConfig) {
10+
const project = xcode.project(projectConfig.pbxprojPath).parseSync();
11+
const libraries = getGroup(project, projectConfig.libraryFolder);
12+
13+
if (!libraries) {
14+
return false;
15+
}
16+
17+
return hasLibraryImported(libraries, dependencyConfig.projectName);
18+
};

src/ios/registerNativeModule.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@ module.exports = function registerNativeModuleIOS(dependencyConfig, projectConfi
3737
);
3838
}
3939

40-
if (hasLibraryImported(libraries, dependencyConfig.projectName)) {
41-
return false;
42-
}
43-
4440
const file = addFileToProject(
4541
project,
4642
path.relative(projectConfig.sourceDir, dependencyConfig.projectPath)
@@ -68,6 +64,4 @@ module.exports = function registerNativeModuleIOS(dependencyConfig, projectConfi
6864
projectConfig.pbxprojPath,
6965
project.writeSync()
7066
);
71-
72-
return true;
7367
};

src/link.js

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
const log = require('npmlog');
22
const path = require('path');
33
const uniq = require('lodash').uniq;
4+
const flatten = require('lodash').flatten;
5+
const pkg = require('../package.json');
46

57
const isEmpty = require('lodash').isEmpty;
68
const registerDependencyAndroid = require('./android/registerNativeModule');
79
const registerDependencyIOS = require('./ios/registerNativeModule');
10+
const isInstalledAndroid = require('./android/isInstalled');
11+
const isInstalledIOS = require('./ios/isInstalled');
812
const copyAssetsAndroid = require('./android/copyAssets');
913
const copyAssetsIOS = require('./ios/copyAssets');
1014
const getProjectDependencies = require('./getProjectDependencies');
@@ -27,35 +31,49 @@ function promiseWaterfall(tasks) {
2731
);
2832
}
2933

30-
const linkDependency = (project, dependency) => {
31-
if (project.android && dependency.config.android) {
34+
const linkDependencyAndroid = (androidProject, dependency) => {
35+
if (!androidProject || !dependency.config.android) {
36+
return null;
37+
}
38+
39+
const isInstalled = isInstalledAndroid(androidProject, dependency.name);
40+
41+
if (isInstalled) {
42+
log.info(`Android module ${dependency.name} is already linked`);
43+
return null;
44+
}
45+
46+
return pollParams(dependency.config.params).then(params => {
3247
log.info(`Linking ${dependency.name} android dependency`);
3348

34-
const didLinkAndroid = registerDependencyAndroid(
49+
registerDependencyAndroid(
3550
dependency.name,
3651
dependency.config.android,
37-
dependency.config.params,
38-
project.android
52+
params,
53+
androidProject
3954
);
4055

41-
if (didLinkAndroid) {
42-
log.info(`Android module ${dependency.name} has been successfully linked`);
43-
} else {
44-
log.info(`Android module ${dependency.name} is already linked`);
45-
}
46-
}
56+
log.info(`Android module ${dependency.name} has been successfully linked`);
57+
});
58+
};
4759

48-
if (project.ios && dependency.config.ios) {
49-
log.info(`Linking ${dependency.name} ios dependency`);
60+
const linkDependencyIOS = (iOSProject, dependency) => {
61+
if (!iOSProject || !dependency.config.ios) {
62+
return;
63+
}
5064

51-
const didLinkIOS = registerDependencyIOS(dependency.config.ios, project.ios);
65+
const isInstalled = isInstalledIOS(iOSProject, dependency.config.ios);
5266

53-
if (didLinkIOS) {
54-
log.info(`iOS module ${dependency.name} has been successfully linked`);
55-
} else {
56-
log.info(`iOS module ${dependency.name} is already linked`);
57-
}
67+
if (isInstalled) {
68+
log.info(`iOS module ${dependency.name} is already linked`);
69+
return;
5870
}
71+
72+
log.info(`Linking ${dependency.name} ios dependency`);
73+
74+
registerDependencyIOS(dependency.config.ios, iOSProject);
75+
76+
log.info(`iOS module ${dependency.name} has been successfully linked`);
5977
};
6078

6179
const linkAssets = (project, assets) => {
@@ -101,13 +119,20 @@ module.exports = function link(config, args) {
101119
project.assets
102120
));
103121

104-
return Promise.all(
105-
dependencies.map(dependency => promiseWaterfall([
106-
() => pollParams(dependency.config.params),
107-
(params) => dependency.config.params = params,
108-
() => promisify(dependency.config.commands.prelink || commandStub),
109-
() => linkDependency(project, dependency),
110-
() => promisify(dependency.config.commands.postlink || commandStub),
111-
]))
112-
).then(() => linkAssets(project, assets));
122+
const tasks = flatten(dependencies.map(dependency => [
123+
() => promisify(dependency.config.commands.prelink || commandStub),
124+
() => linkDependencyAndroid(project.android, dependency),
125+
() => linkDependencyIOS(project.ios, dependency),
126+
() => promisify(dependency.config.commands.postlink || commandStub),
127+
]));
128+
129+
tasks.push(() => linkAssets(project, assets));
130+
131+
return promiseWaterfall(tasks).catch(err => {
132+
log.error(
133+
`It seems something went wrong while linking. Error: ${err.message} \n`
134+
+ `Please file an issue here: ${pkg.bugs.url}`
135+
);
136+
throw err;
137+
});
113138
};

test/android/isInstalled.spec.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const chai = require('chai');
2+
const expect = chai.expect;
3+
const mock = require('mock-fs');
4+
const fs = require('fs');
5+
const path = require('path');
6+
const isInstalled = require('../../src/android/isInstalled');
7+
8+
const projectConfig = {
9+
buildGradlePath: 'build.gradle',
10+
};
11+
12+
describe('android::isInstalled', () => {
13+
14+
before(() => {
15+
mock({
16+
'build.gradle': fs.readFileSync(path.join(__dirname, '../fixtures/android/patchedBuild.gradle')),
17+
});
18+
});
19+
20+
it('should return true when project is already in build.gradle', () => {
21+
expect(isInstalled(projectConfig, 'test')).to.be.true;
22+
});
23+
24+
it('should return false when project is not in build.gradle', () => {
25+
expect(isInstalled(projectConfig, 'test2')).to.be.false;
26+
});
27+
28+
after(mock.restore);
29+
30+
});

test/ios/isInstalled.spec.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const chai = require('chai');
2+
const expect = chai.expect;
3+
const mock = require('mock-fs');
4+
const fs = require('fs');
5+
const path = require('path');
6+
const isInstalled = require('../../src/ios/isInstalled');
7+
8+
const baseProjectConfig = {
9+
pbxprojPath: 'project.pbxproj',
10+
libraryFolder: 'Libraries',
11+
};
12+
13+
describe('ios::isInstalled', () => {
14+
15+
before(() => {
16+
mock({
17+
'project.pbxproj': fs.readFileSync(path.join(__dirname, '../fixtures/project.pbxproj')),
18+
});
19+
});
20+
21+
it('should return true when .xcodeproj in Libraries', () => {
22+
const dependencyConfig = { projectName: 'React.xcodeproj' };
23+
expect(isInstalled(baseProjectConfig, dependencyConfig)).to.be.true;
24+
});
25+
26+
it('should return false when .xcodeproj not in Libraries', () => {
27+
const dependencyConfig = { projectName: 'Missing.xcodeproj' };
28+
expect(isInstalled(baseProjectConfig, dependencyConfig)).to.be.false;
29+
});
30+
31+
it('should return false when `LibraryFolder` is missing', () => {
32+
const dependencyConfig = { projectName: 'React.xcodeproj' };
33+
const projectConfig = Object.assign({}, baseProjectConfig, { libraryFolder: 'Missing' });
34+
expect(isInstalled(projectConfig, dependencyConfig)).to.be.false;
35+
});
36+
37+
after(mock.restore);
38+
39+
});

test/link.spec.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,21 @@ describe('link', () => {
6969
getDependencyConfig: sinon.stub().returns({ android: {}, ios: {}, assets: [], commands: {} }),
7070
};
7171

72+
mock(
73+
'../src/android/isInstalled.js',
74+
sinon.stub().returns(false)
75+
);
76+
7277
mock(
7378
'../src/android/registerNativeModule.js',
7479
registerNativeModule
7580
);
7681

82+
mock(
83+
'../src/ios/isInstalled.js',
84+
sinon.stub().returns(false)
85+
);
86+
7787
mock(
7888
'../src/ios/registerNativeModule.js',
7989
registerNativeModule
@@ -87,6 +97,41 @@ describe('link', () => {
8797
});
8898
});
8999

100+
it('should not register modules when they are already installed', (done) => {
101+
const registerNativeModule = sinon.stub();
102+
const config = {
103+
getProjectConfig: () => ({ ios: {}, android: {}, assets: [] }),
104+
getDependencyConfig: sinon.stub().returns({ ios: {}, android: {}, assets: [], commands: {} }),
105+
};
106+
107+
mock(
108+
'../src/ios/isInstalled.js',
109+
sinon.stub().returns(true)
110+
);
111+
112+
mock(
113+
'../src/android/isInstalled.js',
114+
sinon.stub().returns(true)
115+
);
116+
117+
mock(
118+
'../src/ios/registerNativeModule.js',
119+
registerNativeModule
120+
);
121+
122+
mock(
123+
'../src/android/registerNativeModule.js',
124+
registerNativeModule
125+
);
126+
127+
const link = require('../src/link');
128+
129+
link(config, ['react-native-blur']).then(() => {
130+
expect(registerNativeModule.callCount).to.equal(0);
131+
done();
132+
});
133+
});
134+
90135
it('should run prelink and postlink commands at the appropriate times', (done) => {
91136
const registerNativeModule = sinon.stub();
92137
const prelink = sinon.stub().yieldsAsync();
@@ -97,6 +142,11 @@ describe('link', () => {
97142
registerNativeModule
98143
);
99144

145+
mock(
146+
'../src/ios/isInstalled.js',
147+
sinon.stub().returns(false)
148+
);
149+
100150
const config = {
101151
getProjectConfig: () => ({ ios: {}, assets: [] }),
102152
getDependencyConfig: sinon.stub().returns({

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy