react-native-notify-kit 10.10.0 → 10.10.1
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 +9 -1
- package/android/src/main/kotlin/io/invertase/notifee/NotifeeApiModule.kt +17 -12
- package/android/src/test/java/io/invertase/notifee/RequestPermissionThreadTest.java +102 -0
- package/cli/dist/commands/initNse.js +11 -1
- package/cli/dist/lib/notifeeExtensionFlag.js +152 -0
- package/cli/dist/lib/patchPodfile.js +3 -2
- package/cli/package.json +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/ios/NotifeeCore/NotifeeCore+UNUserNotificationCenter.m +128 -72
- package/package.json +4 -3
- package/plugin/build/ios/withNotifyKitIosNseFiles.js +43 -7
- package/plugin/build/ios/withNotifyKitIosNsePodfile.js +19 -77
- package/plugin/build/shared/nse/notifeeExtensionFlag.js +155 -0
- package/plugin/build/shared/nse/patchPodfile.js +6 -64
- package/src/version.ts +1 -1
|
@@ -1,52 +1,54 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
|
+
exports.withNotifyKitIosNsePodfile = withNotifyKitIosNsePodfile;
|
|
4
|
+
exports.resolveNotifyKitPackagePathFromIos = resolveNotifyKitPackagePathFromIos;
|
|
5
|
+
exports.normalizePodfilePath = normalizePodfilePath;
|
|
6
|
+
exports.detectPodfileUseFrameworks = detectPodfileUseFrameworks;
|
|
3
7
|
const fs = require('fs');
|
|
4
8
|
const path = require('path');
|
|
5
|
-
|
|
6
|
-
const { patchPodfileForNotifyKitNse } = require('../shared/nse/patchPodfile');
|
|
7
|
-
|
|
9
|
+
const patchPodfile_1 = require('../shared/nse/patchPodfile');
|
|
8
10
|
function withNotifyKitIosNsePodfile(config, nseOptions) {
|
|
9
11
|
if (!nseOptions.enabled) {
|
|
10
12
|
return config;
|
|
11
13
|
}
|
|
12
|
-
|
|
13
14
|
const { withPodfile } = requireExpoConfigPlugins();
|
|
14
15
|
const configuredUseFrameworks = detectConfiguredExpoBuildPropertiesUseFrameworks(config);
|
|
15
|
-
|
|
16
16
|
return withPodfile(config, modConfig => {
|
|
17
17
|
const { projectRoot, platformProjectRoot } = modConfig.modRequest;
|
|
18
18
|
const packagePathFromIos = resolveNotifyKitPackagePathFromIos(projectRoot, platformProjectRoot);
|
|
19
|
+
// Podfile mods can run before expo-build-properties writes its properties
|
|
20
|
+
// mod. Read the configuration that the host will consume at pod install,
|
|
21
|
+
// rather than a missing/stale file left by a clean/incremental prebuild.
|
|
22
|
+
const podfileProperties = readExpoPodfileProperties(platformProjectRoot);
|
|
23
|
+
if (configuredUseFrameworks === 'static' || configuredUseFrameworks === 'dynamic') {
|
|
24
|
+
podfileProperties['ios.useFrameworks'] = configuredUseFrameworks;
|
|
25
|
+
}
|
|
19
26
|
const hostUseFrameworks = detectPodfileUseFrameworks(modConfig.modResults.contents, {
|
|
20
27
|
ignoredTargetName: nseOptions.targetName,
|
|
21
|
-
podfileProperties
|
|
28
|
+
podfileProperties,
|
|
22
29
|
env: process.env,
|
|
23
30
|
});
|
|
24
31
|
const useFrameworks = resolveNseUseFrameworks(hostUseFrameworks, configuredUseFrameworks);
|
|
25
|
-
const result = patchPodfileForNotifyKitNse(modConfig.modResults.contents, {
|
|
32
|
+
const result = (0, patchPodfile_1.patchPodfileForNotifyKitNse)(modConfig.modResults.contents, {
|
|
26
33
|
targetName: nseOptions.targetName,
|
|
27
34
|
packagePathFromIos,
|
|
28
35
|
placement: 'topLevel',
|
|
29
36
|
useFrameworks,
|
|
30
37
|
});
|
|
31
|
-
|
|
32
38
|
modConfig.modResults.contents = result.contents;
|
|
33
39
|
return modConfig;
|
|
34
40
|
});
|
|
35
41
|
}
|
|
36
|
-
|
|
37
42
|
function resolveNotifyKitPackagePathFromIos(projectRoot, platformProjectRoot) {
|
|
38
43
|
const packageJsonPath = require.resolve('react-native-notify-kit/package.json', {
|
|
39
44
|
paths: [projectRoot],
|
|
40
45
|
});
|
|
41
46
|
const packageDir = path.dirname(packageJsonPath);
|
|
42
|
-
|
|
43
47
|
return normalizePodfilePath(path.relative(platformProjectRoot, packageDir));
|
|
44
48
|
}
|
|
45
|
-
|
|
46
49
|
function normalizePodfilePath(filePath) {
|
|
47
50
|
return filePath.replace(/\\/g, '/');
|
|
48
51
|
}
|
|
49
|
-
|
|
50
52
|
function detectPodfileUseFrameworks(contents, optionsOrIgnoredTargetName) {
|
|
51
53
|
const options =
|
|
52
54
|
typeof optionsOrIgnoredTargetName === 'string'
|
|
@@ -59,30 +61,24 @@ function detectPodfileUseFrameworks(contents, optionsOrIgnoredTargetName) {
|
|
|
59
61
|
let globalUseFrameworks = false;
|
|
60
62
|
let hostUseFrameworks = false;
|
|
61
63
|
let charIndex = 0;
|
|
62
|
-
|
|
63
64
|
for (const line of contents.split('\n')) {
|
|
64
65
|
const detectedUseFrameworks = parseUseFrameworksLine(line, options);
|
|
65
66
|
if (detectedUseFrameworks === null) {
|
|
66
67
|
charIndex += line.length + 1;
|
|
67
68
|
continue;
|
|
68
69
|
}
|
|
69
|
-
|
|
70
70
|
const targetBlock = findMostSpecificTargetBlock(targetBlocks, charIndex);
|
|
71
71
|
if (!targetBlock) {
|
|
72
72
|
globalUseFrameworks = mergeUseFrameworksDetection(globalUseFrameworks, detectedUseFrameworks);
|
|
73
73
|
} else if (targetBlock === firstHostTarget) {
|
|
74
74
|
hostUseFrameworks = mergeUseFrameworksDetection(hostUseFrameworks, detectedUseFrameworks);
|
|
75
75
|
}
|
|
76
|
-
|
|
77
76
|
charIndex += line.length + 1;
|
|
78
77
|
}
|
|
79
|
-
|
|
80
78
|
return hostUseFrameworks !== false ? hostUseFrameworks : globalUseFrameworks;
|
|
81
79
|
}
|
|
82
|
-
|
|
83
80
|
function readExpoPodfileProperties(platformProjectRoot) {
|
|
84
81
|
const propertiesPath = path.join(platformProjectRoot, 'Podfile.properties.json');
|
|
85
|
-
|
|
86
82
|
try {
|
|
87
83
|
const parsed = JSON.parse(fs.readFileSync(propertiesPath, 'utf8'));
|
|
88
84
|
return isPlainObject(parsed) ? parsed : {};
|
|
@@ -90,240 +86,186 @@ function readExpoPodfileProperties(platformProjectRoot) {
|
|
|
90
86
|
if (isFileNotFoundError(error)) {
|
|
91
87
|
return {};
|
|
92
88
|
}
|
|
93
|
-
|
|
94
89
|
throw error;
|
|
95
90
|
}
|
|
96
91
|
}
|
|
97
|
-
|
|
98
92
|
function isFileNotFoundError(error) {
|
|
99
93
|
return isPlainObject(error) && error.code === 'ENOENT';
|
|
100
94
|
}
|
|
101
|
-
|
|
102
95
|
function resolveNseUseFrameworks(hostUseFrameworks, configuredUseFrameworks) {
|
|
103
96
|
if (hostUseFrameworks === false) {
|
|
104
97
|
return false;
|
|
105
98
|
}
|
|
106
|
-
|
|
107
99
|
if (hostUseFrameworks === true && configuredUseFrameworks !== false) {
|
|
108
100
|
return configuredUseFrameworks;
|
|
109
101
|
}
|
|
110
|
-
|
|
111
102
|
return hostUseFrameworks;
|
|
112
103
|
}
|
|
113
|
-
|
|
114
104
|
function detectConfiguredExpoBuildPropertiesUseFrameworks(config) {
|
|
115
105
|
const plugins = Array.isArray(config.plugins) ? config.plugins : [];
|
|
116
106
|
let detected = false;
|
|
117
|
-
|
|
118
107
|
for (const plugin of plugins) {
|
|
119
108
|
if (!Array.isArray(plugin) || plugin[0] !== 'expo-build-properties') {
|
|
120
109
|
continue;
|
|
121
110
|
}
|
|
122
|
-
|
|
123
111
|
const options = isPlainObject(plugin[1]) ? plugin[1] : null;
|
|
124
112
|
const ios = isPlainObject(options?.ios) ? options.ios : null;
|
|
125
113
|
const useFrameworks = ios?.useFrameworks;
|
|
126
|
-
|
|
127
114
|
if (useFrameworks === 'static' || useFrameworks === 'dynamic') {
|
|
128
115
|
detected = useFrameworks;
|
|
129
116
|
} else if (useFrameworks === true || useFrameworks === false) {
|
|
130
117
|
detected = useFrameworks;
|
|
131
118
|
}
|
|
132
119
|
}
|
|
133
|
-
|
|
134
120
|
return detected;
|
|
135
121
|
}
|
|
136
|
-
|
|
137
122
|
function isPlainObject(value) {
|
|
138
123
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
139
124
|
}
|
|
140
|
-
|
|
141
125
|
function findTargetBlocks(contents) {
|
|
142
126
|
const targetPattern = /^[ \t]*target\s+['"]([^'"]+)['"]\s+do\b.*$/gm;
|
|
143
127
|
const blocks = [];
|
|
144
128
|
let match;
|
|
145
|
-
|
|
146
129
|
while ((match = targetPattern.exec(contents)) !== null) {
|
|
147
130
|
const endLineStart = findMatchingRubyBlockEnd(contents, match.index);
|
|
148
131
|
if (endLineStart === -1) {
|
|
149
132
|
continue;
|
|
150
133
|
}
|
|
151
|
-
|
|
152
134
|
blocks.push({
|
|
153
135
|
targetName: match[1],
|
|
154
136
|
startIndex: match.index,
|
|
155
137
|
endIndex: findLineEnd(contents, endLineStart),
|
|
156
138
|
});
|
|
157
139
|
}
|
|
158
|
-
|
|
159
140
|
return blocks;
|
|
160
141
|
}
|
|
161
|
-
|
|
162
142
|
function findMostSpecificTargetBlock(targetBlocks, charIndex) {
|
|
163
143
|
return targetBlocks
|
|
164
144
|
.filter(block => charIndex >= block.startIndex && charIndex < block.endIndex)
|
|
165
145
|
.sort((a, b) => b.startIndex - a.startIndex)[0];
|
|
166
146
|
}
|
|
167
|
-
|
|
168
147
|
function parseUseFrameworksLine(line, options) {
|
|
169
148
|
const stripped = stripRubyLineComment(line).trim();
|
|
170
149
|
if (!/^use_frameworks!(?:\s|$)/.test(stripped)) {
|
|
171
150
|
return null;
|
|
172
151
|
}
|
|
173
|
-
|
|
174
152
|
if (!isUseFrameworksConditionActive(stripped, options)) {
|
|
175
153
|
return null;
|
|
176
154
|
}
|
|
177
|
-
|
|
178
155
|
const podfilePropertyLinkage = getPodfilePropertyUseFrameworksLinkage(stripped, options);
|
|
179
156
|
if (podfilePropertyLinkage !== null) {
|
|
180
157
|
return podfilePropertyLinkage;
|
|
181
158
|
}
|
|
182
|
-
|
|
183
159
|
const envLinkage = getEnvUseFrameworksLinkage(stripped, options);
|
|
184
160
|
if (envLinkage !== null) {
|
|
185
161
|
return envLinkage;
|
|
186
162
|
}
|
|
187
|
-
|
|
188
163
|
if (/:linkage\s*=>\s*:static\b/.test(stripped) || /\blinkage:\s*:static\b/.test(stripped)) {
|
|
189
164
|
return 'static';
|
|
190
165
|
}
|
|
191
|
-
|
|
192
166
|
if (/:linkage\s*=>\s*:dynamic\b/.test(stripped) || /\blinkage:\s*:dynamic\b/.test(stripped)) {
|
|
193
167
|
return 'dynamic';
|
|
194
168
|
}
|
|
195
|
-
|
|
196
169
|
return true;
|
|
197
170
|
}
|
|
198
|
-
|
|
199
171
|
function mergeUseFrameworksDetection(current, detected) {
|
|
200
172
|
return detected;
|
|
201
173
|
}
|
|
202
|
-
|
|
203
174
|
function isUseFrameworksConditionActive(strippedLine, options) {
|
|
204
175
|
const condition = strippedLine.match(/\s+if\s+(.+)$/)?.[1]?.trim();
|
|
205
176
|
if (!condition) {
|
|
206
177
|
return true;
|
|
207
178
|
}
|
|
208
|
-
|
|
209
179
|
const podfilePropertyName = getPodfilePropertyName(condition);
|
|
210
180
|
if (podfilePropertyName !== null) {
|
|
211
181
|
return isRubyTruthy(options.podfileProperties?.[podfilePropertyName]);
|
|
212
182
|
}
|
|
213
|
-
|
|
214
183
|
const envName = getEnvName(condition);
|
|
215
184
|
if (envName !== null) {
|
|
216
185
|
return isRubyTruthy(options.env?.[envName]);
|
|
217
186
|
}
|
|
218
|
-
|
|
219
187
|
return true;
|
|
220
188
|
}
|
|
221
|
-
|
|
222
189
|
function getPodfilePropertyUseFrameworksLinkage(strippedLine, options) {
|
|
223
190
|
const propertyName = strippedLine.match(
|
|
224
191
|
/(?:\:linkage\s*=>|linkage:)\s*podfile_properties\[['"]([^'"]+)['"]\](?:\.to_sym)?/,
|
|
225
192
|
)?.[1];
|
|
226
|
-
|
|
227
193
|
return propertyName
|
|
228
194
|
? normalizeUseFrameworksValue(options.podfileProperties?.[propertyName])
|
|
229
195
|
: null;
|
|
230
196
|
}
|
|
231
|
-
|
|
232
197
|
function getEnvUseFrameworksLinkage(strippedLine, options) {
|
|
233
198
|
const envName = strippedLine.match(
|
|
234
199
|
/(?:\:linkage\s*=>|linkage:)\s*ENV\[['"]([^'"]+)['"]\](?:\.to_sym)?/,
|
|
235
200
|
)?.[1];
|
|
236
|
-
|
|
237
201
|
return envName ? normalizeUseFrameworksValue(options.env?.[envName]) : null;
|
|
238
202
|
}
|
|
239
|
-
|
|
240
203
|
function normalizeUseFrameworksValue(value) {
|
|
241
204
|
if (value === 'static' || value === 'dynamic' || value === true) {
|
|
242
205
|
return value;
|
|
243
206
|
}
|
|
244
|
-
|
|
245
207
|
return null;
|
|
246
208
|
}
|
|
247
|
-
|
|
248
209
|
function getPodfilePropertyName(expression) {
|
|
249
210
|
return expression.match(/^podfile_properties\[['"]([^'"]+)['"]\]$/)?.[1] ?? null;
|
|
250
211
|
}
|
|
251
|
-
|
|
252
212
|
function getEnvName(expression) {
|
|
253
213
|
return expression.match(/^ENV\[['"]([^'"]+)['"]\]$/)?.[1] ?? null;
|
|
254
214
|
}
|
|
255
|
-
|
|
256
215
|
function isRubyTruthy(value) {
|
|
257
216
|
return value !== undefined && value !== null && value !== false;
|
|
258
217
|
}
|
|
259
|
-
|
|
260
218
|
function findLineEnd(content, lineStartIndex) {
|
|
261
219
|
const newlineIndex = content.indexOf('\n', lineStartIndex);
|
|
262
220
|
return newlineIndex === -1 ? content.length : newlineIndex + 1;
|
|
263
221
|
}
|
|
264
|
-
|
|
265
222
|
function findMatchingRubyBlockEnd(content, startIndex) {
|
|
266
223
|
const afterStart = content.slice(startIndex);
|
|
267
224
|
const lines = afterStart.split('\n');
|
|
268
225
|
let depth = 0;
|
|
269
226
|
let charIndex = startIndex;
|
|
270
|
-
|
|
271
227
|
for (const line of lines) {
|
|
272
228
|
const trimmed = stripRubyLineComment(line).trim();
|
|
273
|
-
|
|
274
229
|
depth += countRubyBlockOpeners(trimmed);
|
|
275
|
-
|
|
276
230
|
if (trimmed === 'end') {
|
|
277
231
|
depth--;
|
|
278
232
|
if (depth === 0) {
|
|
279
233
|
return charIndex;
|
|
280
234
|
}
|
|
281
235
|
}
|
|
282
|
-
|
|
283
236
|
charIndex += line.length + 1;
|
|
284
237
|
}
|
|
285
|
-
|
|
286
238
|
return -1;
|
|
287
239
|
}
|
|
288
|
-
|
|
289
240
|
function countRubyBlockOpeners(line) {
|
|
290
241
|
if (line.length === 0) {
|
|
291
242
|
return 0;
|
|
292
243
|
}
|
|
293
|
-
|
|
294
244
|
const startsWithKeywordBlock = /^(if|unless|case|begin|while|until|for|def|class|module)\b/.test(
|
|
295
245
|
line,
|
|
296
246
|
);
|
|
297
247
|
if (startsWithKeywordBlock) {
|
|
298
248
|
return 1;
|
|
299
249
|
}
|
|
300
|
-
|
|
301
250
|
if (/\bdo\b(\s*\|[^|]*\|)?\s*$/.test(line)) {
|
|
302
251
|
return 1;
|
|
303
252
|
}
|
|
304
|
-
|
|
305
253
|
return 0;
|
|
306
254
|
}
|
|
307
|
-
|
|
308
255
|
function stripRubyLineComment(line) {
|
|
309
256
|
return line.replace(/#.*$/, '');
|
|
310
257
|
}
|
|
311
|
-
|
|
312
258
|
function requireExpoConfigPlugins() {
|
|
313
259
|
try {
|
|
314
260
|
return require('expo/config-plugins');
|
|
315
261
|
} catch (error) {
|
|
316
262
|
try {
|
|
317
|
-
|
|
263
|
+
const expoConfigPluginsPath = require.resolve('expo/config-plugins', {
|
|
264
|
+
paths: [process.cwd()],
|
|
265
|
+
});
|
|
266
|
+
return require(expoConfigPluginsPath);
|
|
318
267
|
} catch {
|
|
319
268
|
throw error;
|
|
320
269
|
}
|
|
321
270
|
}
|
|
322
271
|
}
|
|
323
|
-
|
|
324
|
-
module.exports = {
|
|
325
|
-
withNotifyKitIosNsePodfile,
|
|
326
|
-
resolveNotifyKitPackagePathFromIos,
|
|
327
|
-
normalizePodfilePath,
|
|
328
|
-
detectPodfileUseFrameworks,
|
|
329
|
-
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
|
+
exports.ensureNotifeeExtensionFlag = ensureNotifeeExtensionFlag;
|
|
4
|
+
const child_process_1 = require('child_process');
|
|
5
|
+
// Keep the Expo/CLI copies identical: each is shipped through a separate build.
|
|
6
|
+
// Parse, never execute, consumer Ruby. A line scanner cannot distinguish a
|
|
7
|
+
// top-level assignment from continuations, interpolation, heredocs or blocks.
|
|
8
|
+
const FLAG_PREFLIGHT = String.raw`
|
|
9
|
+
require 'ripper'
|
|
10
|
+
require 'json'
|
|
11
|
+
source = STDIN.read
|
|
12
|
+
ast = Ripper.sexp(source)
|
|
13
|
+
def walk(node, &block)
|
|
14
|
+
return unless node.is_a?(Array)
|
|
15
|
+
yield node
|
|
16
|
+
node.each { |child| walk(child, &block) if child.is_a?(Array) }
|
|
17
|
+
end
|
|
18
|
+
def flag_tokens(node)
|
|
19
|
+
tokens = []
|
|
20
|
+
walk(node) { |part| tokens << part if part[0] == :@gvar && part[1] == '$NotifeeExtension' }
|
|
21
|
+
tokens
|
|
22
|
+
end
|
|
23
|
+
def evaluation?(node)
|
|
24
|
+
found = false
|
|
25
|
+
walk(node) do |part|
|
|
26
|
+
if [:@ident, :@const].include?(part[0]) &&
|
|
27
|
+
%w[target abstract_target pod podspec use_native_modules! use_expo_modules! use_react_native! Specification eval instance_eval class_eval module_eval from_file from_string].include?(part[1])
|
|
28
|
+
found = true
|
|
29
|
+
end
|
|
30
|
+
found = true if part[0] == :@tstring_content && part[1].include?('.podspec')
|
|
31
|
+
end
|
|
32
|
+
found
|
|
33
|
+
end
|
|
34
|
+
# Only a bounded prelude is provably safe before an existing true flag. Unknown
|
|
35
|
+
# helpers and dynamic dispatch can evaluate podspecs even without a named call.
|
|
36
|
+
def literal?(node)
|
|
37
|
+
return true if node.nil? || node == false
|
|
38
|
+
return false unless node.is_a?(Array)
|
|
39
|
+
return node.all? { |part| literal?(part) } unless node[0].is_a?(Symbol)
|
|
40
|
+
case node[0]
|
|
41
|
+
when :@int, :@float, :@rational, :@imaginary, :@tstring_content, :@label, :@ident
|
|
42
|
+
true
|
|
43
|
+
when :@kw
|
|
44
|
+
%w[true false nil].include?(node[1])
|
|
45
|
+
when :var_ref, :string_literal, :string_content, :symbol_literal, :symbol,
|
|
46
|
+
:array, :hash, :assoclist_from_args, :assoc_new, :args_add_block, :arg_paren
|
|
47
|
+
node.drop(1).all? { |part| literal?(part) }
|
|
48
|
+
else
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
def safe_prelude?(node)
|
|
53
|
+
return true if node[0] == :void_stmt
|
|
54
|
+
if node[0] == :assign
|
|
55
|
+
lhs = node[1]
|
|
56
|
+
return lhs && lhs[0] == :var_field &&
|
|
57
|
+
[:@ident, :@gvar, :@ivar, :@cvar, :@const].include?(lhs[1][0]) && literal?(node[2])
|
|
58
|
+
end
|
|
59
|
+
allowed = %w[require require_relative platform source project workspace install! use_frameworks!]
|
|
60
|
+
if node[0] == :command
|
|
61
|
+
return allowed.include?(node[1][1]) && literal?(node[2])
|
|
62
|
+
end
|
|
63
|
+
if node[0] == :method_add_arg && node[1][0] == :fcall
|
|
64
|
+
return allowed.include?(node[1][1][1]) && literal?(node[2])
|
|
65
|
+
end
|
|
66
|
+
false
|
|
67
|
+
end
|
|
68
|
+
unless ast
|
|
69
|
+
puts JSON.generate({ error: 'Ruby syntax could not be parsed', line: 1 })
|
|
70
|
+
exit
|
|
71
|
+
end
|
|
72
|
+
has_eval = false
|
|
73
|
+
flag_text = nil
|
|
74
|
+
walk(ast) do |part|
|
|
75
|
+
has_eval = true if part[0] == :@ident && %w[eval instance_eval class_eval module_eval].include?(part[1])
|
|
76
|
+
flag_text ||= part if part[0] == :@tstring_content && part[1].include?('$NotifeeExtension')
|
|
77
|
+
end
|
|
78
|
+
if has_eval && flag_text
|
|
79
|
+
puts JSON.generate({ error: 'dynamic Ruby evaluation mentions the flag', line: flag_text[2][0] })
|
|
80
|
+
exit
|
|
81
|
+
end
|
|
82
|
+
all = flag_tokens(ast)
|
|
83
|
+
accepted = []
|
|
84
|
+
evaluating = false
|
|
85
|
+
error = nil
|
|
86
|
+
(ast[1] || []).each do |statement|
|
|
87
|
+
tokens = flag_tokens(statement)
|
|
88
|
+
unless tokens.empty?
|
|
89
|
+
lhs = statement[1] if statement[0] == :assign
|
|
90
|
+
rhs = statement[2] if statement[0] == :assign
|
|
91
|
+
literal_true = lhs && lhs[0] == :var_field && lhs[1][0] == :@gvar &&
|
|
92
|
+
lhs[1][1] == '$NotifeeExtension' && rhs && rhs[0] == :var_ref && rhs[1][0] == :@kw && rhs[1][1] == 'true'
|
|
93
|
+
if !literal_true || tokens.length != 1
|
|
94
|
+
error = { error: 'conflicting or ambiguous configuration; only unconditional literal true is supported', line: tokens[0][2][0] }
|
|
95
|
+
break
|
|
96
|
+
end
|
|
97
|
+
if evaluating && accepted.empty?
|
|
98
|
+
error = { error: 'assignment follows possible target/autolinking or podspec evaluation', line: tokens[0][2][0] }
|
|
99
|
+
break
|
|
100
|
+
end
|
|
101
|
+
accepted.concat(tokens)
|
|
102
|
+
end
|
|
103
|
+
evaluating ||= evaluation?(statement) || !safe_prelude?(statement)
|
|
104
|
+
end
|
|
105
|
+
puts JSON.generate(error || { assigned: !accepted.empty?, references: all.length })
|
|
106
|
+
`;
|
|
107
|
+
function ensureNotifeeExtensionFlag(contents) {
|
|
108
|
+
if (contents.includes('$NotifeeExtension')) {
|
|
109
|
+
let result;
|
|
110
|
+
try {
|
|
111
|
+
result = JSON.parse(
|
|
112
|
+
(0, child_process_1.execFileSync)('ruby', ['-e', FLAG_PREFLIGHT], {
|
|
113
|
+
input: contents,
|
|
114
|
+
encoding: 'utf8',
|
|
115
|
+
timeout: 10_000,
|
|
116
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
} catch {
|
|
120
|
+
throw new Error(
|
|
121
|
+
'NotifyKit NSE: $NotifeeExtension at line 1: Ruby (Ripper) preflight unavailable. ' +
|
|
122
|
+
'Use the Ruby toolchain required by CocoaPods; no changes were written.',
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
if (result.error) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`NotifyKit NSE: $NotifeeExtension at line ${result.line}: ${result.error}. ` +
|
|
128
|
+
'Set $NotifeeExtension = true in the Podfile header before autolinking; no changes were written.',
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (result.assigned) return contents;
|
|
132
|
+
}
|
|
133
|
+
// Insert after Ruby's shebang, magic comments and other leading comments.
|
|
134
|
+
// Keep every original byte, including a BOM and a header without final LF.
|
|
135
|
+
const bom = contents.startsWith('\uFEFF') ? '\uFEFF' : '';
|
|
136
|
+
const text = contents.slice(bom.length);
|
|
137
|
+
const lines = text.split('\n');
|
|
138
|
+
let headerEnd = 0;
|
|
139
|
+
let blockComment = false;
|
|
140
|
+
for (let index = 0; index < lines.length; index++) {
|
|
141
|
+
const line = lines[index];
|
|
142
|
+
if (/^=begin(?:\s|$)/.test(line)) blockComment = true;
|
|
143
|
+
if (!blockComment && !/^\s*(?:#.*)?$/.test(line)) break;
|
|
144
|
+
headerEnd += line.length + (index < lines.length - 1 ? 1 : 0);
|
|
145
|
+
if (/^=end(?:\s|$)/.test(line)) blockComment = false;
|
|
146
|
+
}
|
|
147
|
+
const separator = headerEnd > 0 && text[headerEnd - 1] !== '\n' ? '\n' : '';
|
|
148
|
+
return (
|
|
149
|
+
bom +
|
|
150
|
+
text.slice(0, headerEnd) +
|
|
151
|
+
separator +
|
|
152
|
+
'$NotifeeExtension = true\n' +
|
|
153
|
+
text.slice(headerEnd)
|
|
154
|
+
);
|
|
155
|
+
}
|