react-native-notify-kit 10.3.3 → 10.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -12
- package/app.plugin.js +3 -0
- package/cli/dist/commands/initNse.js +8 -9
- package/cli/dist/commands/initNse.js.map +1 -1
- package/cli/dist/lib/detectProject.d.ts +1 -4
- package/cli/dist/lib/detectProject.js +3 -32
- package/cli/dist/lib/detectProject.js.map +1 -1
- package/cli/dist/lib/initNseCore.d.ts +8 -0
- package/cli/dist/lib/initNseCore.js +197 -0
- package/cli/dist/lib/initNseCore.js.map +1 -0
- package/cli/dist/lib/patchPodfile.d.ts +9 -0
- package/cli/dist/lib/patchPodfile.js +22 -15
- package/cli/dist/lib/patchPodfile.js.map +1 -1
- package/cli/dist/lib/patchXcodeProject.d.ts +16 -0
- package/cli/dist/lib/patchXcodeProject.js +54 -26
- package/cli/dist/lib/patchXcodeProject.js.map +1 -1
- package/cli/dist/lib/writeTemplates.js +5 -14
- package/cli/dist/lib/writeTemplates.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/ios/RNNotifee/NotifeeExtensionHelper.h +2 -2
- package/ios/RNNotifee/NotifeeExtensionHelper.m +1 -0
- package/package.json +15 -2
- package/plugin/build/android/withNotifyKitAndroidManifest.js +236 -0
- package/plugin/build/index.js +62 -0
- package/plugin/build/ios/withNotifyKitIosNseAppExtension.js +142 -0
- package/plugin/build/ios/withNotifyKitIosNseFiles.js +79 -0
- package/plugin/build/ios/withNotifyKitIosNsePodfile.js +130 -0
- package/plugin/build/ios/withNotifyKitIosNseXcodeProject.js +49 -0
- package/plugin/build/options.js +224 -0
- package/plugin/build/shared/nse/index.js +7 -0
- package/plugin/build/shared/nse/initNseCore.js +220 -0
- package/plugin/build/shared/nse/patchPodfile.js +261 -0
- package/plugin/build/shared/nse/patchXcodeProject.js +199 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const NOTIFY_KIT_FOREGROUND_SERVICE_NAME = 'app.notifee.core.ForegroundService';
|
|
4
|
+
const ANDROID_SPECIAL_USE_FGS_SUBTYPE_PROPERTY =
|
|
5
|
+
'android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE';
|
|
6
|
+
|
|
7
|
+
const ANDROID_FOREGROUND_SERVICE_PERMISSION = 'android.permission.FOREGROUND_SERVICE';
|
|
8
|
+
|
|
9
|
+
const ANDROID_FOREGROUND_SERVICE_TYPE_PERMISSIONS = {
|
|
10
|
+
camera: 'android.permission.FOREGROUND_SERVICE_CAMERA',
|
|
11
|
+
connectedDevice: 'android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE',
|
|
12
|
+
dataSync: 'android.permission.FOREGROUND_SERVICE_DATA_SYNC',
|
|
13
|
+
health: 'android.permission.FOREGROUND_SERVICE_HEALTH',
|
|
14
|
+
location: 'android.permission.FOREGROUND_SERVICE_LOCATION',
|
|
15
|
+
mediaPlayback: 'android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK',
|
|
16
|
+
mediaProjection: 'android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION',
|
|
17
|
+
microphone: 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
|
|
18
|
+
phoneCall: 'android.permission.FOREGROUND_SERVICE_PHONE_CALL',
|
|
19
|
+
remoteMessaging: 'android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING',
|
|
20
|
+
specialUse: 'android.permission.FOREGROUND_SERVICE_SPECIAL_USE',
|
|
21
|
+
systemExempted: 'android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function withNotifyKitAndroidManifest(config, foregroundServiceOptions) {
|
|
25
|
+
if (!foregroundServiceOptions.enabled) {
|
|
26
|
+
return config;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { withAndroidManifest } = requireExpoConfigPlugins();
|
|
30
|
+
|
|
31
|
+
return withAndroidManifest(config, modConfig => {
|
|
32
|
+
applyNotifyKitAndroidForegroundServiceManifest(
|
|
33
|
+
modConfig.modResults,
|
|
34
|
+
foregroundServiceOptions,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return modConfig;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function applyNotifyKitAndroidForegroundServiceManifest(
|
|
42
|
+
androidManifest,
|
|
43
|
+
foregroundServiceOptions,
|
|
44
|
+
) {
|
|
45
|
+
ensurePermission(androidManifest, ANDROID_FOREGROUND_SERVICE_PERMISSION);
|
|
46
|
+
|
|
47
|
+
for (const permission of resolveTypeSpecificPermissions(foregroundServiceOptions.types)) {
|
|
48
|
+
ensurePermission(androidManifest, permission);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const application = ensureMainApplication(androidManifest);
|
|
52
|
+
const service = ensureNotifyKitForegroundService(application);
|
|
53
|
+
service.$ = {
|
|
54
|
+
...service.$,
|
|
55
|
+
'android:name': NOTIFY_KIT_FOREGROUND_SERVICE_NAME,
|
|
56
|
+
'android:exported': 'false',
|
|
57
|
+
'android:foregroundServiceType': foregroundServiceOptions.types.join('|'),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (foregroundServiceOptions.types.includes('specialUse')) {
|
|
61
|
+
if (foregroundServiceOptions.specialUseSubtype === undefined) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
'[react-native-notify-kit] android.foregroundService.specialUseSubtype is required when types includes specialUse.',
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
upsertSpecialUseProperty(service, foregroundServiceOptions.specialUseSubtype);
|
|
68
|
+
} else {
|
|
69
|
+
removeSpecialUseProperty(service);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return androidManifest;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveTypeSpecificPermissions(types) {
|
|
76
|
+
const permissions = [];
|
|
77
|
+
|
|
78
|
+
for (const type of types) {
|
|
79
|
+
const permission = ANDROID_FOREGROUND_SERVICE_TYPE_PERMISSIONS[type];
|
|
80
|
+
if (permission !== undefined && !permissions.includes(permission)) {
|
|
81
|
+
permissions.push(permission);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return permissions;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function ensurePermission(androidManifest, permissionName) {
|
|
89
|
+
const manifest = ensureManifestRoot(androidManifest);
|
|
90
|
+
const permissions = ensureArrayProperty(
|
|
91
|
+
manifest,
|
|
92
|
+
'uses-permission',
|
|
93
|
+
'[react-native-notify-kit] AndroidManifest.xml uses-permission must be an array.',
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const hasPermission = permissions.some(
|
|
97
|
+
permission => permission.$ && permission.$['android:name'] === permissionName,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (!hasPermission) {
|
|
101
|
+
permissions.push({
|
|
102
|
+
$: {
|
|
103
|
+
'android:name': permissionName,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function ensureMainApplication(androidManifest) {
|
|
110
|
+
const manifest = ensureManifestRoot(androidManifest);
|
|
111
|
+
const applications = ensureArrayProperty(
|
|
112
|
+
manifest,
|
|
113
|
+
'application',
|
|
114
|
+
'[react-native-notify-kit] AndroidManifest.xml application must be an array.',
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (applications.length === 0) {
|
|
118
|
+
applications.push({});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return applications[0];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function ensureNotifyKitForegroundService(application) {
|
|
125
|
+
const services = ensureArrayProperty(
|
|
126
|
+
application,
|
|
127
|
+
'service',
|
|
128
|
+
'[react-native-notify-kit] AndroidManifest.xml application.service must be an array.',
|
|
129
|
+
);
|
|
130
|
+
const existingService = services.find(
|
|
131
|
+
service =>
|
|
132
|
+
service.$ && service.$['android:name'] === NOTIFY_KIT_FOREGROUND_SERVICE_NAME,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (existingService !== undefined) {
|
|
136
|
+
return existingService;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const service = {
|
|
140
|
+
$: {
|
|
141
|
+
'android:name': NOTIFY_KIT_FOREGROUND_SERVICE_NAME,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
services.push(service);
|
|
145
|
+
|
|
146
|
+
return service;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function upsertSpecialUseProperty(service, subtype) {
|
|
150
|
+
const properties = ensureArrayProperty(
|
|
151
|
+
service,
|
|
152
|
+
'property',
|
|
153
|
+
'[react-native-notify-kit] AndroidManifest.xml service.property must be an array.',
|
|
154
|
+
);
|
|
155
|
+
const existingProperty = properties.find(
|
|
156
|
+
property =>
|
|
157
|
+
property.$ &&
|
|
158
|
+
property.$['android:name'] === ANDROID_SPECIAL_USE_FGS_SUBTYPE_PROPERTY,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (existingProperty !== undefined) {
|
|
162
|
+
existingProperty.$ = {
|
|
163
|
+
...existingProperty.$,
|
|
164
|
+
'android:name': ANDROID_SPECIAL_USE_FGS_SUBTYPE_PROPERTY,
|
|
165
|
+
'android:value': subtype,
|
|
166
|
+
};
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
properties.push({
|
|
171
|
+
$: {
|
|
172
|
+
'android:name': ANDROID_SPECIAL_USE_FGS_SUBTYPE_PROPERTY,
|
|
173
|
+
'android:value': subtype,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function removeSpecialUseProperty(service) {
|
|
179
|
+
if (service.property === undefined) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
service.property = service.property.filter(
|
|
184
|
+
property =>
|
|
185
|
+
property.$ &&
|
|
186
|
+
property.$['android:name'] !== ANDROID_SPECIAL_USE_FGS_SUBTYPE_PROPERTY,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (service.property.length === 0) {
|
|
190
|
+
delete service.property;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function ensureManifestRoot(androidManifest) {
|
|
195
|
+
if (androidManifest.manifest === undefined) {
|
|
196
|
+
androidManifest.manifest = {};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return androidManifest.manifest;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function ensureArrayProperty(object, key, errorMessage) {
|
|
203
|
+
const value = object[key];
|
|
204
|
+
|
|
205
|
+
if (value === undefined) {
|
|
206
|
+
const nextValue = [];
|
|
207
|
+
object[key] = nextValue;
|
|
208
|
+
return nextValue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!Array.isArray(value)) {
|
|
212
|
+
throw new Error(errorMessage);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return value;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function requireExpoConfigPlugins() {
|
|
219
|
+
try {
|
|
220
|
+
return require('expo/config-plugins');
|
|
221
|
+
} catch (error) {
|
|
222
|
+
try {
|
|
223
|
+
return require(require.resolve('expo/config-plugins', { paths: [process.cwd()] }));
|
|
224
|
+
} catch {
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
module.exports = {
|
|
231
|
+
NOTIFY_KIT_FOREGROUND_SERVICE_NAME,
|
|
232
|
+
ANDROID_SPECIAL_USE_FGS_SUBTYPE_PROPERTY,
|
|
233
|
+
withNotifyKitAndroidManifest,
|
|
234
|
+
applyNotifyKitAndroidForegroundServiceManifest,
|
|
235
|
+
resolveTypeSpecificPermissions,
|
|
236
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { createRunOncePlugin } = requireExpoConfigPlugins();
|
|
4
|
+
const { normalizeNotifyKitPluginOptions } = require('./options');
|
|
5
|
+
const {
|
|
6
|
+
withNotifyKitAndroidManifest,
|
|
7
|
+
} = require('./android/withNotifyKitAndroidManifest');
|
|
8
|
+
const {
|
|
9
|
+
withNotifyKitIosNseAppExtension,
|
|
10
|
+
} = require('./ios/withNotifyKitIosNseAppExtension');
|
|
11
|
+
const {
|
|
12
|
+
withNotifyKitIosNseFiles,
|
|
13
|
+
} = require('./ios/withNotifyKitIosNseFiles');
|
|
14
|
+
const {
|
|
15
|
+
withNotifyKitIosNsePodfile,
|
|
16
|
+
} = require('./ios/withNotifyKitIosNsePodfile');
|
|
17
|
+
const {
|
|
18
|
+
withNotifyKitIosNseXcodeProject,
|
|
19
|
+
} = require('./ios/withNotifyKitIosNseXcodeProject');
|
|
20
|
+
const pkg = require('../../package.json');
|
|
21
|
+
|
|
22
|
+
function withNotifyKit(config, props = {}) {
|
|
23
|
+
const options = normalizeNotifyKitPluginOptions(props);
|
|
24
|
+
const foregroundServiceOptions = options.android.foregroundService;
|
|
25
|
+
const nseOptions = options.ios.notificationServiceExtension;
|
|
26
|
+
const configWithAndroidManifest = withNotifyKitAndroidManifest(
|
|
27
|
+
config,
|
|
28
|
+
foregroundServiceOptions,
|
|
29
|
+
);
|
|
30
|
+
const configWithAppExtension = withNotifyKitIosNseAppExtension(
|
|
31
|
+
configWithAndroidManifest,
|
|
32
|
+
nseOptions,
|
|
33
|
+
);
|
|
34
|
+
const configWithFiles = withNotifyKitIosNseFiles(configWithAppExtension, nseOptions);
|
|
35
|
+
const configWithXcodeProject = withNotifyKitIosNseXcodeProject(configWithFiles, nseOptions);
|
|
36
|
+
|
|
37
|
+
return withNotifyKitIosNsePodfile(configWithXcodeProject, nseOptions);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const plugin = createRunOncePlugin(withNotifyKit, pkg.name, pkg.version);
|
|
41
|
+
|
|
42
|
+
module.exports = plugin;
|
|
43
|
+
module.exports.default = plugin;
|
|
44
|
+
module.exports.withNotifyKit = withNotifyKit;
|
|
45
|
+
module.exports.normalizeNotifyKitPluginOptions = normalizeNotifyKitPluginOptions;
|
|
46
|
+
module.exports.withNotifyKitAndroidManifest = withNotifyKitAndroidManifest;
|
|
47
|
+
module.exports.withNotifyKitIosNseAppExtension = withNotifyKitIosNseAppExtension;
|
|
48
|
+
module.exports.withNotifyKitIosNseFiles = withNotifyKitIosNseFiles;
|
|
49
|
+
module.exports.withNotifyKitIosNseXcodeProject = withNotifyKitIosNseXcodeProject;
|
|
50
|
+
module.exports.withNotifyKitIosNsePodfile = withNotifyKitIosNsePodfile;
|
|
51
|
+
|
|
52
|
+
function requireExpoConfigPlugins() {
|
|
53
|
+
try {
|
|
54
|
+
return require('expo/config-plugins');
|
|
55
|
+
} catch (error) {
|
|
56
|
+
try {
|
|
57
|
+
return require(require.resolve('expo/config-plugins', { paths: [process.cwd()] }));
|
|
58
|
+
} catch {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function withNotifyKitIosNseAppExtension(config, nseOptions) {
|
|
4
|
+
if (!nseOptions.enabled) {
|
|
5
|
+
return config;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const bundleIdentifier = resolveNotifyKitIosNseBundleIdentifier(config, nseOptions);
|
|
9
|
+
const currentAppExtensions = getCurrentAppExtensions(config);
|
|
10
|
+
const nextAppExtensions = upsertNotifyKitIosNseAppExtension(currentAppExtensions, {
|
|
11
|
+
targetName: nseOptions.targetName,
|
|
12
|
+
bundleIdentifier,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
...config,
|
|
17
|
+
extra: setNestedAppExtensions(config.extra, nextAppExtensions),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function resolveNotifyKitIosNseBundleIdentifier(config, nseOptions) {
|
|
22
|
+
const hostBundleIdentifier = config.ios && config.ios.bundleIdentifier;
|
|
23
|
+
if (!hostBundleIdentifier) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'[react-native-notify-kit] ios.bundleIdentifier is required when ios.notificationServiceExtension.enabled is true.',
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return `${hostBundleIdentifier}${nseOptions.bundleSuffix}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function upsertNotifyKitIosNseAppExtension(appExtensions, nextExtension) {
|
|
33
|
+
const nextAppExtensions = [];
|
|
34
|
+
let didUpsert = false;
|
|
35
|
+
|
|
36
|
+
for (const appExtension of appExtensions) {
|
|
37
|
+
if (!isPlainObject(appExtension)) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
'[react-native-notify-kit] extra.eas.build.experimental.ios.appExtensions must contain objects.',
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (appExtension.targetName === nextExtension.targetName) {
|
|
44
|
+
if (
|
|
45
|
+
appExtension.bundleIdentifier !== undefined &&
|
|
46
|
+
appExtension.bundleIdentifier !== nextExtension.bundleIdentifier
|
|
47
|
+
) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`[react-native-notify-kit] EAS app extension targetName '${nextExtension.targetName}' already uses bundleIdentifier '${appExtension.bundleIdentifier}'. ` +
|
|
50
|
+
`Expected '${nextExtension.bundleIdentifier}'.`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!didUpsert) {
|
|
55
|
+
nextAppExtensions.push({
|
|
56
|
+
...appExtension,
|
|
57
|
+
targetName: nextExtension.targetName,
|
|
58
|
+
bundleIdentifier: nextExtension.bundleIdentifier,
|
|
59
|
+
});
|
|
60
|
+
didUpsert = true;
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (appExtension.bundleIdentifier === nextExtension.bundleIdentifier) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`[react-native-notify-kit] EAS app extension bundleIdentifier '${nextExtension.bundleIdentifier}' already belongs to targetName '${appExtension.targetName}'. ` +
|
|
68
|
+
`Expected '${nextExtension.targetName}'.`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
nextAppExtensions.push(appExtension);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!didUpsert) {
|
|
76
|
+
nextAppExtensions.push({
|
|
77
|
+
targetName: nextExtension.targetName,
|
|
78
|
+
bundleIdentifier: nextExtension.bundleIdentifier,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return nextAppExtensions;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getCurrentAppExtensions(config) {
|
|
86
|
+
const appExtensions = getObject(config.extra)?.eas;
|
|
87
|
+
const build = getObject(appExtensions)?.build;
|
|
88
|
+
const experimental = getObject(build)?.experimental;
|
|
89
|
+
const ios = getObject(experimental)?.ios;
|
|
90
|
+
const currentAppExtensions = getObject(ios)?.appExtensions;
|
|
91
|
+
|
|
92
|
+
if (currentAppExtensions === undefined) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!Array.isArray(currentAppExtensions)) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
'[react-native-notify-kit] extra.eas.build.experimental.ios.appExtensions must be an array.',
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return currentAppExtensions;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function setNestedAppExtensions(currentExtra, appExtensions) {
|
|
106
|
+
const extra = getObject(currentExtra) ?? {};
|
|
107
|
+
const eas = getObject(extra.eas) ?? {};
|
|
108
|
+
const build = getObject(eas.build) ?? {};
|
|
109
|
+
const experimental = getObject(build.experimental) ?? {};
|
|
110
|
+
const ios = getObject(experimental.ios) ?? {};
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
...extra,
|
|
114
|
+
eas: {
|
|
115
|
+
...eas,
|
|
116
|
+
build: {
|
|
117
|
+
...build,
|
|
118
|
+
experimental: {
|
|
119
|
+
...experimental,
|
|
120
|
+
ios: {
|
|
121
|
+
...ios,
|
|
122
|
+
appExtensions,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function getObject(value) {
|
|
131
|
+
return isPlainObject(value) ? value : null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function isPlainObject(value) {
|
|
135
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = {
|
|
139
|
+
withNotifyKitIosNseAppExtension,
|
|
140
|
+
resolveNotifyKitIosNseBundleIdentifier,
|
|
141
|
+
upsertNotifyKitIosNseAppExtension,
|
|
142
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
renderNotificationServiceSwift,
|
|
8
|
+
renderNseEntitlementsPlist,
|
|
9
|
+
renderNseInfoPlist,
|
|
10
|
+
} = require('../shared/nse/initNseCore');
|
|
11
|
+
|
|
12
|
+
function withNotifyKitIosNseFiles(config, nseOptions) {
|
|
13
|
+
if (!nseOptions.enabled) {
|
|
14
|
+
return config;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { withDangerousMod } = requireExpoConfigPlugins();
|
|
18
|
+
|
|
19
|
+
return withDangerousMod(config, [
|
|
20
|
+
'ios',
|
|
21
|
+
modConfig => {
|
|
22
|
+
writeNotifyKitIosNseFiles(
|
|
23
|
+
modConfig.modRequest.platformProjectRoot,
|
|
24
|
+
nseOptions.targetName,
|
|
25
|
+
);
|
|
26
|
+
return modConfig;
|
|
27
|
+
},
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function writeNotifyKitIosNseFiles(platformProjectRoot, targetName) {
|
|
32
|
+
const targetDir = path.join(platformProjectRoot, targetName);
|
|
33
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
34
|
+
|
|
35
|
+
writeFileIfMissingOrIdentical(
|
|
36
|
+
path.join(targetDir, 'NotificationService.swift'),
|
|
37
|
+
renderNotificationServiceSwift(),
|
|
38
|
+
);
|
|
39
|
+
writeFileIfMissingOrIdentical(
|
|
40
|
+
path.join(targetDir, 'Info.plist'),
|
|
41
|
+
renderNseInfoPlist({ targetName }),
|
|
42
|
+
);
|
|
43
|
+
writeFileIfMissingOrIdentical(
|
|
44
|
+
path.join(targetDir, `${targetName}.entitlements`),
|
|
45
|
+
renderNseEntitlementsPlist(),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function writeFileIfMissingOrIdentical(filePath, contents) {
|
|
50
|
+
if (fs.existsSync(filePath)) {
|
|
51
|
+
const currentContents = fs.readFileSync(filePath, 'utf8');
|
|
52
|
+
if (currentContents !== contents) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`[react-native-notify-kit] Refusing to overwrite existing ${filePath}. ` +
|
|
55
|
+
'Delete it or make it match the generated NotifyKit NSE template.',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fs.writeFileSync(filePath, contents, 'utf8');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function requireExpoConfigPlugins() {
|
|
65
|
+
try {
|
|
66
|
+
return require('expo/config-plugins');
|
|
67
|
+
} catch (error) {
|
|
68
|
+
try {
|
|
69
|
+
return require(require.resolve('expo/config-plugins', { paths: [process.cwd()] }));
|
|
70
|
+
} catch {
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
module.exports = {
|
|
77
|
+
withNotifyKitIosNseFiles,
|
|
78
|
+
writeNotifyKitIosNseFiles,
|
|
79
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const { patchPodfileForNotifyKitNse } = require('../shared/nse/patchPodfile');
|
|
6
|
+
|
|
7
|
+
function withNotifyKitIosNsePodfile(config, nseOptions) {
|
|
8
|
+
if (!nseOptions.enabled) {
|
|
9
|
+
return config;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { withPodfile } = requireExpoConfigPlugins();
|
|
13
|
+
const configuredUseFrameworks = detectConfiguredExpoBuildPropertiesUseFrameworks(config);
|
|
14
|
+
|
|
15
|
+
return withPodfile(config, modConfig => {
|
|
16
|
+
const { projectRoot, platformProjectRoot } = modConfig.modRequest;
|
|
17
|
+
const packagePathFromIos = resolveNotifyKitPackagePathFromIos(
|
|
18
|
+
projectRoot,
|
|
19
|
+
platformProjectRoot,
|
|
20
|
+
);
|
|
21
|
+
const hostUseFrameworks = detectPodfileUseFrameworks(modConfig.modResults.contents);
|
|
22
|
+
const useFrameworks = resolveNseUseFrameworks(hostUseFrameworks, configuredUseFrameworks);
|
|
23
|
+
const result = patchPodfileForNotifyKitNse(modConfig.modResults.contents, {
|
|
24
|
+
targetName: nseOptions.targetName,
|
|
25
|
+
packagePathFromIos,
|
|
26
|
+
placement: 'topLevel',
|
|
27
|
+
useFrameworks,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
modConfig.modResults.contents = result.contents;
|
|
31
|
+
return modConfig;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolveNotifyKitPackagePathFromIos(projectRoot, platformProjectRoot) {
|
|
36
|
+
const packageJsonPath = require.resolve('react-native-notify-kit/package.json', {
|
|
37
|
+
paths: [projectRoot],
|
|
38
|
+
});
|
|
39
|
+
const packageDir = path.dirname(packageJsonPath);
|
|
40
|
+
|
|
41
|
+
return normalizePodfilePath(path.relative(platformProjectRoot, packageDir));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizePodfilePath(filePath) {
|
|
45
|
+
return filePath.replace(/\\/g, '/');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function detectPodfileUseFrameworks(contents) {
|
|
49
|
+
let detected = false;
|
|
50
|
+
|
|
51
|
+
for (const line of contents.split('\n')) {
|
|
52
|
+
const stripped = line.replace(/#.*$/, '').trim();
|
|
53
|
+
if (!/^use_frameworks!(?:\s|$)/.test(stripped)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (/:linkage\s*=>\s*:static\b/.test(stripped) || /\blinkage:\s*:static\b/.test(stripped)) {
|
|
58
|
+
return 'static';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (
|
|
62
|
+
/:linkage\s*=>\s*:dynamic\b/.test(stripped) ||
|
|
63
|
+
/\blinkage:\s*:dynamic\b/.test(stripped)
|
|
64
|
+
) {
|
|
65
|
+
return 'dynamic';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
detected = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return detected;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function resolveNseUseFrameworks(hostUseFrameworks, configuredUseFrameworks) {
|
|
75
|
+
if (hostUseFrameworks === false) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (hostUseFrameworks === true && configuredUseFrameworks !== false) {
|
|
80
|
+
return configuredUseFrameworks;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return hostUseFrameworks;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function detectConfiguredExpoBuildPropertiesUseFrameworks(config) {
|
|
87
|
+
const plugins = Array.isArray(config.plugins) ? config.plugins : [];
|
|
88
|
+
let detected = false;
|
|
89
|
+
|
|
90
|
+
for (const plugin of plugins) {
|
|
91
|
+
if (!Array.isArray(plugin) || plugin[0] !== 'expo-build-properties') {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const options = isPlainObject(plugin[1]) ? plugin[1] : null;
|
|
96
|
+
const ios = isPlainObject(options?.ios) ? options.ios : null;
|
|
97
|
+
const useFrameworks = ios?.useFrameworks;
|
|
98
|
+
|
|
99
|
+
if (useFrameworks === 'static' || useFrameworks === 'dynamic') {
|
|
100
|
+
detected = useFrameworks;
|
|
101
|
+
} else if (useFrameworks === true || useFrameworks === false) {
|
|
102
|
+
detected = useFrameworks;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return detected;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function isPlainObject(value) {
|
|
110
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function requireExpoConfigPlugins() {
|
|
114
|
+
try {
|
|
115
|
+
return require('expo/config-plugins');
|
|
116
|
+
} catch (error) {
|
|
117
|
+
try {
|
|
118
|
+
return require(require.resolve('expo/config-plugins', { paths: [process.cwd()] }));
|
|
119
|
+
} catch {
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
withNotifyKitIosNsePodfile,
|
|
127
|
+
resolveNotifyKitPackagePathFromIos,
|
|
128
|
+
normalizePodfilePath,
|
|
129
|
+
detectPodfileUseFrameworks,
|
|
130
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
patchXcodeProjectForNotifyKitNse,
|
|
5
|
+
} = require('../shared/nse/patchXcodeProject');
|
|
6
|
+
const {
|
|
7
|
+
resolveNotifyKitIosNseBundleIdentifier,
|
|
8
|
+
} = require('./withNotifyKitIosNseAppExtension');
|
|
9
|
+
|
|
10
|
+
function withNotifyKitIosNseXcodeProject(config, nseOptions) {
|
|
11
|
+
if (!nseOptions.enabled) {
|
|
12
|
+
return config;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { withXcodeProject } = requireExpoConfigPlugins();
|
|
16
|
+
const bundleIdentifier = resolveNotifyKitIosNseBundleIdentifier(config, nseOptions);
|
|
17
|
+
|
|
18
|
+
return withXcodeProject(config, modConfig => {
|
|
19
|
+
const result = patchXcodeProjectForNotifyKitNse(modConfig.modResults, {
|
|
20
|
+
targetName: nseOptions.targetName,
|
|
21
|
+
bundleIdentifier,
|
|
22
|
+
parentTargetName: modConfig.modRequest.projectName,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (result.didChange && !result.hostTargetUuid) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
'[react-native-notify-kit] Failed to link NotifyKit NSE target to the host app target.',
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return modConfig;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function requireExpoConfigPlugins() {
|
|
36
|
+
try {
|
|
37
|
+
return require('expo/config-plugins');
|
|
38
|
+
} catch (error) {
|
|
39
|
+
try {
|
|
40
|
+
return require(require.resolve('expo/config-plugins', { paths: [process.cwd()] }));
|
|
41
|
+
} catch {
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
withNotifyKitIosNseXcodeProject,
|
|
49
|
+
};
|