expo-app-blocker 0.1.52 → 0.1.53
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/package.json +1 -1
- package/plugin/src/index.js +146 -159
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-app-blocker",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.53",
|
|
4
4
|
"description": "Expo module for cross-platform app blocking. Android: UsageStatsManager + Overlay. iOS: Screen Time API (FamilyControls + ManagedSettings + DeviceActivity).",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
package/plugin/src/index.js
CHANGED
|
@@ -232,13 +232,19 @@ function withAppBlockerIOS(config, pluginConfig) {
|
|
|
232
232
|
// Populate `targets/` synchronously at config-eval time so the
|
|
233
233
|
// `@bacons/apple-targets` plugin (registered just below) can glob the
|
|
234
234
|
// directory and register the Shield/DeviceActivityMonitor/ShieldConfiguration
|
|
235
|
-
// extensions on the first prebuild.
|
|
236
|
-
//
|
|
237
|
-
//
|
|
235
|
+
// extensions on the first prebuild. We also run placeholder substitution and
|
|
236
|
+
// copy the shield icon here, not in a later withDangerousMod, so the on-disk
|
|
237
|
+
// state matches the plugin config after every config evaluation — not just
|
|
238
|
+
// during `expo prebuild`. Otherwise non-prebuild config loads (EAS env probe,
|
|
239
|
+
// autolinking, doctor) would re-copy the placeholder templates over the
|
|
240
|
+
// substituted Swift files and leave the tree in a broken state.
|
|
238
241
|
const projectRoot = config._internal?.projectRoot;
|
|
239
242
|
if (projectRoot) {
|
|
240
243
|
const targetsDir = path.join(projectRoot, "targets");
|
|
241
244
|
const packageTargetsDir = path.resolve(__dirname, "..", "..", "targets");
|
|
245
|
+
|
|
246
|
+
// 1. Copy template Swift files + expo-target.config.js from this package
|
|
247
|
+
// into the consumer's `targets/`. Preserves any user-managed assets.
|
|
242
248
|
if (fs.existsSync(packageTargetsDir)) {
|
|
243
249
|
for (const dir of fs.readdirSync(packageTargetsDir)) {
|
|
244
250
|
const srcDir = path.join(packageTargetsDir, dir);
|
|
@@ -252,6 +258,140 @@ function withAppBlockerIOS(config, pluginConfig) {
|
|
|
252
258
|
}
|
|
253
259
|
}
|
|
254
260
|
}
|
|
261
|
+
|
|
262
|
+
// 2. Substitute placeholders in the freshly-copied Swift files. The
|
|
263
|
+
// substitution map mirrors the original withDangerousMod block — kept
|
|
264
|
+
// here so config-eval produces final, build-ready Swift in one pass.
|
|
265
|
+
function hexToRgb(hex) {
|
|
266
|
+
const h = hex.replace("#", "");
|
|
267
|
+
return {
|
|
268
|
+
r: (parseInt(h.substring(0, 2), 16) / 255).toFixed(3),
|
|
269
|
+
g: (parseInt(h.substring(2, 4), 16) / 255).toFixed(3),
|
|
270
|
+
b: (parseInt(h.substring(4, 6), 16) / 255).toFixed(3),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const shield = pluginConfig?.ios?.shield || {};
|
|
275
|
+
const primaryColor = hexToRgb(shield.primaryButtonColor || "#fb6107");
|
|
276
|
+
const titleColor = hexToRgb(shield.titleColor || "#111111");
|
|
277
|
+
const subtitleColor = hexToRgb(shield.subtitleColor || "#737373");
|
|
278
|
+
const bgColorHex = shield.backgroundColor || null;
|
|
279
|
+
const bgColor = bgColorHex ? hexToRgb(bgColorHex) : null;
|
|
280
|
+
|
|
281
|
+
const blurStyleMap = {
|
|
282
|
+
"systemUltraThinMaterial": ".systemUltraThinMaterial",
|
|
283
|
+
"systemThinMaterial": ".systemThinMaterial",
|
|
284
|
+
"systemMaterial": ".systemMaterial",
|
|
285
|
+
"systemThickMaterial": ".systemThickMaterial",
|
|
286
|
+
"systemChromeMaterial": ".systemChromeMaterial",
|
|
287
|
+
"systemUltraThinMaterialLight": ".systemUltraThinMaterialLight",
|
|
288
|
+
"systemThinMaterialLight": ".systemThinMaterialLight",
|
|
289
|
+
"systemMaterialLight": ".systemMaterialLight",
|
|
290
|
+
"systemThickMaterialLight": ".systemThickMaterialLight",
|
|
291
|
+
"systemChromeMaterialLight": ".systemChromeMaterialLight",
|
|
292
|
+
"systemUltraThinMaterialDark": ".systemUltraThinMaterialDark",
|
|
293
|
+
"systemThinMaterialDark": ".systemThinMaterialDark",
|
|
294
|
+
"systemMaterialDark": ".systemMaterialDark",
|
|
295
|
+
"systemThickMaterialDark": ".systemThickMaterialDark",
|
|
296
|
+
"systemChromeMaterialDark": ".systemChromeMaterialDark",
|
|
297
|
+
"regular": ".regular",
|
|
298
|
+
"prominent": ".prominent",
|
|
299
|
+
"light": ".light",
|
|
300
|
+
"dark": ".dark",
|
|
301
|
+
"extraLight": ".extraLight",
|
|
302
|
+
};
|
|
303
|
+
const blurRaw = shield.backgroundBlurStyle || (bgColorHex ? null : "systemThickMaterial");
|
|
304
|
+
const blurSwift = blurRaw && blurStyleMap[blurRaw] ? blurStyleMap[blurRaw] : null;
|
|
305
|
+
|
|
306
|
+
const notification = pluginConfig?.ios?.notification || {};
|
|
307
|
+
const notificationTitle = notification.title || "App Blocker";
|
|
308
|
+
const notificationBody = notification.body || "Tap to return to the app and complete the unlock challenge.";
|
|
309
|
+
const notificationAttachIcon = notification.attachIcon === false ? "false" : "true";
|
|
310
|
+
|
|
311
|
+
const tempUnlockTitle = shield.tempUnlockTitle || "Almost there!";
|
|
312
|
+
const tempUnlockSubtitle = shield.tempUnlockSubtitle || "Your free time is loading. Try again in a moment.";
|
|
313
|
+
const tempUnlockButtonLabel = shield.tempUnlockButtonLabel || "OK";
|
|
314
|
+
|
|
315
|
+
const countSuffixTemplate = shield.countSuffix !== undefined
|
|
316
|
+
? shield.countSuffix
|
|
317
|
+
: " You have {count} apps blocked.";
|
|
318
|
+
|
|
319
|
+
// Swift string-literal escaping for plugin substitutions that land inside
|
|
320
|
+
// `"..."` literals. `\(` is the Swift interpolation escape; rendering it
|
|
321
|
+
// unescaped here is intentional — see renderCountSuffixSwift.
|
|
322
|
+
function escapeSwiftString(s) {
|
|
323
|
+
return String(s)
|
|
324
|
+
.replace(/\\/g, "\\\\")
|
|
325
|
+
.replace(/"/g, '\\"')
|
|
326
|
+
.replace(/\n/g, "\\n")
|
|
327
|
+
.replace(/\r/g, "\\r");
|
|
328
|
+
}
|
|
329
|
+
function renderCountSuffixSwift(template) {
|
|
330
|
+
if (!template) return '""';
|
|
331
|
+
const escaped = escapeSwiftString(template);
|
|
332
|
+
return `"${escaped.replace(/\{count\}/g, "\\(count)")}"`;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const replacements = {
|
|
336
|
+
"APP_GROUP_PLACEHOLDER": appGroup,
|
|
337
|
+
"SHIELD_TITLE_PLACEHOLDER": shield.title || "Hold on!",
|
|
338
|
+
"SHIELD_SUBTITLE_PLACEHOLDER": shield.subtitle || "{appName} is blocked.",
|
|
339
|
+
"SHIELD_PRIMARY_BUTTON_PLACEHOLDER": shield.primaryButtonLabel || "Earn Free Time",
|
|
340
|
+
"SHIELD_SECONDARY_BUTTON_PLACEHOLDER": shield.secondaryButtonLabel === null ? "none" : (shield.secondaryButtonLabel || "Not now"),
|
|
341
|
+
"SHIELD_TEMP_UNLOCK_TITLE_PLACEHOLDER": tempUnlockTitle,
|
|
342
|
+
"SHIELD_TEMP_UNLOCK_SUBTITLE_PLACEHOLDER": tempUnlockSubtitle,
|
|
343
|
+
"SHIELD_TEMP_UNLOCK_BUTTON_PLACEHOLDER": tempUnlockButtonLabel,
|
|
344
|
+
"SHIELD_COUNT_SUFFIX_SWIFT_PLACEHOLDER": renderCountSuffixSwift(countSuffixTemplate),
|
|
345
|
+
"NOTIFICATION_TITLE_PLACEHOLDER": notificationTitle,
|
|
346
|
+
"NOTIFICATION_BODY_PLACEHOLDER": notificationBody,
|
|
347
|
+
"NOTIFICATION_ATTACH_ICON_PLACEHOLDER": notificationAttachIcon,
|
|
348
|
+
"SHIELD_PRIMARY_R_PLACEHOLDER": primaryColor.r,
|
|
349
|
+
"SHIELD_PRIMARY_G_PLACEHOLDER": primaryColor.g,
|
|
350
|
+
"SHIELD_PRIMARY_B_PLACEHOLDER": primaryColor.b,
|
|
351
|
+
"SHIELD_TITLE_R_PLACEHOLDER": titleColor.r,
|
|
352
|
+
"SHIELD_TITLE_G_PLACEHOLDER": titleColor.g,
|
|
353
|
+
"SHIELD_TITLE_B_PLACEHOLDER": titleColor.b,
|
|
354
|
+
"SHIELD_SUBTITLE_R_PLACEHOLDER": subtitleColor.r,
|
|
355
|
+
"SHIELD_SUBTITLE_G_PLACEHOLDER": subtitleColor.g,
|
|
356
|
+
"SHIELD_SUBTITLE_B_PLACEHOLDER": subtitleColor.b,
|
|
357
|
+
"SHIELD_BG_COLOR_PLACEHOLDER": bgColor
|
|
358
|
+
? `UIColor(red: ${bgColor.r}, green: ${bgColor.g}, blue: ${bgColor.b}, alpha: 1.0)`
|
|
359
|
+
: "nil",
|
|
360
|
+
"SHIELD_BLUR_STYLE_PLACEHOLDER": blurSwift || "nil",
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
if (fs.existsSync(targetsDir)) {
|
|
364
|
+
for (const dir of fs.readdirSync(targetsDir)) {
|
|
365
|
+
const dirPath = path.join(targetsDir, dir);
|
|
366
|
+
if (!fs.statSync(dirPath).isDirectory()) continue;
|
|
367
|
+
for (const file of fs.readdirSync(dirPath)) {
|
|
368
|
+
if (!file.endsWith(".swift")) continue;
|
|
369
|
+
const filePath = path.join(dirPath, file);
|
|
370
|
+
let content = fs.readFileSync(filePath, "utf-8");
|
|
371
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
372
|
+
content = content.replace(new RegExp(key, "g"), value);
|
|
373
|
+
}
|
|
374
|
+
fs.writeFileSync(filePath, content);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// 3. Copy shield icon into ShieldConfiguration + ShieldAction target assets.
|
|
380
|
+
const shieldIcon = pluginConfig?.ios?.shield?.icon;
|
|
381
|
+
if (shieldIcon) {
|
|
382
|
+
const iconSrc = path.isAbsolute(shieldIcon)
|
|
383
|
+
? shieldIcon
|
|
384
|
+
: path.resolve(projectRoot, shieldIcon);
|
|
385
|
+
if (fs.existsSync(iconSrc)) {
|
|
386
|
+
for (const target of ["ShieldConfiguration", "ShieldAction"]) {
|
|
387
|
+
const assetsDir = path.join(targetsDir, target, "assets");
|
|
388
|
+
if (!fs.existsSync(assetsDir)) {
|
|
389
|
+
fs.mkdirSync(assetsDir, { recursive: true });
|
|
390
|
+
}
|
|
391
|
+
fs.copyFileSync(iconSrc, path.join(assetsDir, "shield-icon.png"));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
255
395
|
}
|
|
256
396
|
|
|
257
397
|
// Auto-register `@bacons/apple-targets` so users don't have to add it to
|
|
@@ -311,162 +451,9 @@ function withAppBlockerIOS(config, pluginConfig) {
|
|
|
311
451
|
}
|
|
312
452
|
}
|
|
313
453
|
|
|
314
|
-
//
|
|
315
|
-
// withAppBlockerIOS).
|
|
316
|
-
//
|
|
317
|
-
const targetsDir = path.join(path.dirname(platformRoot), "targets");
|
|
318
|
-
|
|
319
|
-
// Helper: hex to RGB floats
|
|
320
|
-
function hexToRgb(hex) {
|
|
321
|
-
const h = hex.replace("#", "");
|
|
322
|
-
return {
|
|
323
|
-
r: (parseInt(h.substring(0, 2), 16) / 255).toFixed(3),
|
|
324
|
-
g: (parseInt(h.substring(2, 4), 16) / 255).toFixed(3),
|
|
325
|
-
b: (parseInt(h.substring(4, 6), 16) / 255).toFixed(3),
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Shield config defaults
|
|
330
|
-
const shield = pluginConfig?.ios?.shield || {};
|
|
331
|
-
const primaryColor = hexToRgb(shield.primaryButtonColor || "#fb6107");
|
|
332
|
-
const titleColor = hexToRgb(shield.titleColor || "#111111");
|
|
333
|
-
const subtitleColor = hexToRgb(shield.subtitleColor || "#737373");
|
|
334
|
-
// Background color (solid) - separate from blur
|
|
335
|
-
const bgColorHex = shield.backgroundColor || null;
|
|
336
|
-
const bgColor = bgColorHex ? hexToRgb(bgColorHex) : null;
|
|
337
|
-
|
|
338
|
-
// Blur style mapping
|
|
339
|
-
const blurStyleMap = {
|
|
340
|
-
"systemUltraThinMaterial": ".systemUltraThinMaterial",
|
|
341
|
-
"systemThinMaterial": ".systemThinMaterial",
|
|
342
|
-
"systemMaterial": ".systemMaterial",
|
|
343
|
-
"systemThickMaterial": ".systemThickMaterial",
|
|
344
|
-
"systemChromeMaterial": ".systemChromeMaterial",
|
|
345
|
-
"systemUltraThinMaterialLight": ".systemUltraThinMaterialLight",
|
|
346
|
-
"systemThinMaterialLight": ".systemThinMaterialLight",
|
|
347
|
-
"systemMaterialLight": ".systemMaterialLight",
|
|
348
|
-
"systemThickMaterialLight": ".systemThickMaterialLight",
|
|
349
|
-
"systemChromeMaterialLight": ".systemChromeMaterialLight",
|
|
350
|
-
"systemUltraThinMaterialDark": ".systemUltraThinMaterialDark",
|
|
351
|
-
"systemThinMaterialDark": ".systemThinMaterialDark",
|
|
352
|
-
"systemMaterialDark": ".systemMaterialDark",
|
|
353
|
-
"systemThickMaterialDark": ".systemThickMaterialDark",
|
|
354
|
-
"systemChromeMaterialDark": ".systemChromeMaterialDark",
|
|
355
|
-
"regular": ".regular",
|
|
356
|
-
"prominent": ".prominent",
|
|
357
|
-
"light": ".light",
|
|
358
|
-
"dark": ".dark",
|
|
359
|
-
"extraLight": ".extraLight",
|
|
360
|
-
};
|
|
361
|
-
const blurRaw = shield.backgroundBlurStyle || (bgColorHex ? null : "systemThickMaterial");
|
|
362
|
-
const blurSwift = blurRaw && blurStyleMap[blurRaw] ? blurStyleMap[blurRaw] : null;
|
|
363
|
-
|
|
364
|
-
// Notification config (shown when the user taps the Shield primary button).
|
|
365
|
-
// All copy is configurable so non-English apps can localize without forking.
|
|
366
|
-
const notification = pluginConfig?.ios?.notification || {};
|
|
367
|
-
const notificationTitle = notification.title || "App Blocker";
|
|
368
|
-
const notificationBody = notification.body || "Tap to return to the app and complete the unlock challenge.";
|
|
369
|
-
// attachIcon defaults to true to preserve current behavior; set to false
|
|
370
|
-
// to drop the duplicate icon attachment so only the system app icon shows.
|
|
371
|
-
const notificationAttachIcon = notification.attachIcon === false ? "false" : "true";
|
|
372
|
-
|
|
373
|
-
// Temporary-unlock state copy (shown when the user has just earned time
|
|
374
|
-
// and the Shield is briefly visible while ManagedSettings clears).
|
|
375
|
-
const tempUnlockTitle = shield.tempUnlockTitle || "Almost there!";
|
|
376
|
-
const tempUnlockSubtitle = shield.tempUnlockSubtitle || "Your free time is loading. Try again in a moment.";
|
|
377
|
-
const tempUnlockButtonLabel = shield.tempUnlockButtonLabel || "OK";
|
|
378
|
-
|
|
379
|
-
// "You have N apps blocked" suffix appended to the subtitle when more
|
|
380
|
-
// than one app is blocked. Set countSuffix to "" to drop it entirely,
|
|
381
|
-
// or to a localized template like " יש לך {count} אפליקציות חסומות.".
|
|
382
|
-
// Defaults preserve the legacy English suffix.
|
|
383
|
-
const countSuffixTemplate = shield.countSuffix !== undefined
|
|
384
|
-
? shield.countSuffix
|
|
385
|
-
: " You have {count} apps blocked.";
|
|
386
|
-
|
|
387
|
-
// Swift string-literal escaping. Plugin substitutions land inside `"..."`
|
|
388
|
-
// literals so backslashes, quotes, and the Swift interpolation escape
|
|
389
|
-
// `\(` MUST all be escaped or the extension fails to compile.
|
|
390
|
-
function escapeSwiftString(s) {
|
|
391
|
-
return String(s)
|
|
392
|
-
.replace(/\\/g, "\\\\")
|
|
393
|
-
.replace(/"/g, '\\"')
|
|
394
|
-
.replace(/\n/g, "\\n")
|
|
395
|
-
.replace(/\r/g, "\\r");
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Render the count suffix template into a Swift expression. We use
|
|
399
|
-
// `\(count)` interpolation when the template includes `{count}` so the
|
|
400
|
-
// runtime value is substituted. Empty template → empty literal.
|
|
401
|
-
function renderCountSuffixSwift(template) {
|
|
402
|
-
if (!template) return '""';
|
|
403
|
-
const escaped = escapeSwiftString(template);
|
|
404
|
-
return `"${escaped.replace(/\{count\}/g, "\\(count)")}"`;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// All placeholder replacements
|
|
408
|
-
const replacements = {
|
|
409
|
-
"APP_GROUP_PLACEHOLDER": appGroup,
|
|
410
|
-
"SHIELD_TITLE_PLACEHOLDER": shield.title || "Hold on!",
|
|
411
|
-
"SHIELD_SUBTITLE_PLACEHOLDER": shield.subtitle || "{appName} is blocked.",
|
|
412
|
-
"SHIELD_PRIMARY_BUTTON_PLACEHOLDER": shield.primaryButtonLabel || "Earn Free Time",
|
|
413
|
-
"SHIELD_SECONDARY_BUTTON_PLACEHOLDER": shield.secondaryButtonLabel === null ? "none" : (shield.secondaryButtonLabel || "Not now"),
|
|
414
|
-
"SHIELD_TEMP_UNLOCK_TITLE_PLACEHOLDER": tempUnlockTitle,
|
|
415
|
-
"SHIELD_TEMP_UNLOCK_SUBTITLE_PLACEHOLDER": tempUnlockSubtitle,
|
|
416
|
-
"SHIELD_TEMP_UNLOCK_BUTTON_PLACEHOLDER": tempUnlockButtonLabel,
|
|
417
|
-
"SHIELD_COUNT_SUFFIX_SWIFT_PLACEHOLDER": renderCountSuffixSwift(countSuffixTemplate),
|
|
418
|
-
"NOTIFICATION_TITLE_PLACEHOLDER": notificationTitle,
|
|
419
|
-
"NOTIFICATION_BODY_PLACEHOLDER": notificationBody,
|
|
420
|
-
"NOTIFICATION_ATTACH_ICON_PLACEHOLDER": notificationAttachIcon,
|
|
421
|
-
"SHIELD_PRIMARY_R_PLACEHOLDER": primaryColor.r,
|
|
422
|
-
"SHIELD_PRIMARY_G_PLACEHOLDER": primaryColor.g,
|
|
423
|
-
"SHIELD_PRIMARY_B_PLACEHOLDER": primaryColor.b,
|
|
424
|
-
"SHIELD_TITLE_R_PLACEHOLDER": titleColor.r,
|
|
425
|
-
"SHIELD_TITLE_G_PLACEHOLDER": titleColor.g,
|
|
426
|
-
"SHIELD_TITLE_B_PLACEHOLDER": titleColor.b,
|
|
427
|
-
"SHIELD_SUBTITLE_R_PLACEHOLDER": subtitleColor.r,
|
|
428
|
-
"SHIELD_SUBTITLE_G_PLACEHOLDER": subtitleColor.g,
|
|
429
|
-
"SHIELD_SUBTITLE_B_PLACEHOLDER": subtitleColor.b,
|
|
430
|
-
"SHIELD_BG_COLOR_PLACEHOLDER": bgColor
|
|
431
|
-
? `UIColor(red: ${bgColor.r}, green: ${bgColor.g}, blue: ${bgColor.b}, alpha: 1.0)`
|
|
432
|
-
: "nil",
|
|
433
|
-
"SHIELD_BLUR_STYLE_PLACEHOLDER": blurSwift || "nil",
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
// Inject all placeholders into extension Swift files
|
|
437
|
-
if (fs.existsSync(targetsDir)) {
|
|
438
|
-
const dirs = fs.readdirSync(targetsDir);
|
|
439
|
-
for (const dir of dirs) {
|
|
440
|
-
const dirPath = path.join(targetsDir, dir);
|
|
441
|
-
if (!fs.statSync(dirPath).isDirectory()) continue;
|
|
442
|
-
const files = fs.readdirSync(dirPath);
|
|
443
|
-
for (const file of files) {
|
|
444
|
-
if (!file.endsWith(".swift")) continue;
|
|
445
|
-
const filePath = path.join(dirPath, file);
|
|
446
|
-
let content = fs.readFileSync(filePath, "utf-8");
|
|
447
|
-
for (const [key, value] of Object.entries(replacements)) {
|
|
448
|
-
content = content.replace(new RegExp(key, "g"), value);
|
|
449
|
-
}
|
|
450
|
-
fs.writeFileSync(filePath, content);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// Copy shield icon to ShieldConfiguration and ShieldAction target assets
|
|
456
|
-
const shieldIcon = pluginConfig?.ios?.shield?.icon;
|
|
457
|
-
if (shieldIcon) {
|
|
458
|
-
const projectRoot = path.dirname(platformRoot);
|
|
459
|
-
const iconSrc = path.resolve(projectRoot, shieldIcon);
|
|
460
|
-
if (fs.existsSync(iconSrc)) {
|
|
461
|
-
for (const target of ["ShieldConfiguration", "ShieldAction"]) {
|
|
462
|
-
const assetsDir = path.join(targetsDir, target, "assets");
|
|
463
|
-
if (!fs.existsSync(assetsDir)) {
|
|
464
|
-
fs.mkdirSync(assetsDir, { recursive: true });
|
|
465
|
-
}
|
|
466
|
-
fs.copyFileSync(iconSrc, path.join(assetsDir, "shield-icon.png"));
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
454
|
+
// Target Swift files + shield icon are produced at config-eval time
|
|
455
|
+
// (see withAppBlockerIOS). The block below only handles `ios/` patches
|
|
456
|
+
// that depend on the prebuilt platform tree.
|
|
470
457
|
|
|
471
458
|
return config;
|
|
472
459
|
},
|