codeplay-common 3.2.1 → 3.2.2

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.
@@ -1,2318 +1,2561 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const plist = require('plist');
4
-
5
-
6
-
7
- const { execSync } = require("child_process");
8
-
9
-
10
-
11
-
12
- const { readFileSync } = require("fs");
13
-
14
- const ENABLE_AUTO_UPDATE = true;
15
- const USE_LIVE_SERVER_VERSION = true;
16
-
17
- const configPath = path.join(process.cwd(), 'capacitor.config.json');
18
- const updateLogFile = path.join(process.cwd(), "", "plugin-update-log.txt");
19
-
20
- // Expected plugin list with minimum versions
21
- /* const requiredPlugins = [
22
-
23
- { pattern: /backbutton-(\d+\.\d+)\.js$/, minVersion: '2.0', required: true, baseDir: 'js',mandatoryUpdate: true},
24
-
25
-
26
- { pattern: /common-(\d+\.\d+)(?:-beta-(\d+))?\.js$/, minVersion: '6.0', required: true, baseDir: 'js',mandatoryUpdate: true },
27
-
28
- { pattern: /localization_settings-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
29
- { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: true },
30
- { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js',mandatoryUpdate: false },
31
- { pattern: /localNotification_AppSettings-(\d+\.\d+)\.js$/, minVersion: '1.0', required: true, baseDir: 'js',mandatoryUpdate: false },
32
- { pattern: /onesignal-(\d+\.\d+)\.js$/, minVersion: '2.3', required: true, baseDir: 'js',mandatoryUpdate: false },
33
- { pattern: /saveToGalleryAndSaveAnyFile-(\d+\.\d+)(-ios)?\.js$/, minVersion: '3.1', required: true, baseDir: 'js',mandatoryUpdate: true },
34
- { pattern: /Ads[\/\\]admob-emi-(\d+\.\d+)\.js$/, minVersion: '3.7', required: true, baseDir: 'js',mandatoryUpdate: true },
35
-
36
- // New added plugins
37
- { pattern: /video-player-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: false },
38
- { pattern: /image-cropper-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
39
- { pattern: /common-(\d+\.\d+)\.less$/, minVersion: '1.6', required: true, baseDir: 'assets/css',mandatoryUpdate: false },
40
-
41
-
42
- // New folders
43
- { pattern: /IAP-(\d+\.\d+)$/, minVersion: '2.8', isFolder: true , required: true, baseDir: 'js/Ads',mandatoryUpdate: true },
44
- { pattern: /editor-(\d+\.\d+)$/, minVersion: '1.9', isFolder: true, required: true, baseDir: 'js',mandatoryUpdate: true },
45
- { pattern: /ffmpeg-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true, required: true, baseDir: 'js',mandatoryUpdate: true },
46
- { pattern: /theme-(\d+\.\d+)$/, minVersion: '3.3', isFolder: true , required: true, baseDir: 'theme',mandatoryUpdate: true },
47
-
48
-
49
- { pattern: /certificatejs-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true , required: true, baseDir: 'certificate',mandatoryUpdate: true }
50
-
51
- ]; */
52
-
53
-
54
- function requireOrInstall(packageName) {
55
- try {
56
- return require(packageName);
57
- } catch (err) {
58
-
59
- console.log(`📦 "${packageName}" not found. Installing automatically...`);
60
-
61
- try {
62
- execSync(`npm install ${packageName}`, { stdio: "inherit" });
63
- console.log(`✅ "${packageName}" installed successfully.`);
64
- } catch (installErr) {
65
- console.error(`❌ Failed to install "${packageName}".`);
66
- process.exit(1);
67
- }
68
-
69
- // Try loading again
70
- return require(packageName);
71
- }
72
- }
73
-
74
- const AdmZip = requireOrInstall("adm-zip");
75
-
76
-
77
-
78
- const pkg = require(path.join(process.cwd(), 'node_modules', 'codeplay-common', 'package.json'));
79
-
80
- const pluginName = pkg.name;
81
- const pluginVersion = pkg.version;
82
-
83
- let updateLogs = [];
84
-
85
- function writeUpdateLine(message) {
86
- updateLogs.push(message);
87
- }
88
-
89
- const MAX_LOG_BLOCKS = 50;
90
-
91
- function saveUpdateLogs() {
92
-
93
- if (updateLogs.length === 0) return;
94
-
95
- const logDir = path.dirname(updateLogFile);
96
-
97
- if (!fs.existsSync(logDir)) {
98
- fs.mkdirSync(logDir, { recursive: true });
99
- }
100
-
101
- const now = new Date();
102
-
103
- const formattedTime = now.toLocaleString('en-GB', {
104
- day: '2-digit',
105
- month: '2-digit',
106
- year: 'numeric',
107
- hour: '2-digit',
108
- minute: '2-digit',
109
- hour12: true
110
- }).replace(',', '').replace(/\//g, '-');
111
-
112
- let newBlock = `${pluginName}: ${pluginVersion}\n[${formattedTime}]\n`;
113
-
114
- updateLogs.forEach(line => {
115
- newBlock += `${line}\n`;
116
- });
117
-
118
- newBlock += "\n";
119
-
120
- let existingLog = "";
121
-
122
- if (fs.existsSync(updateLogFile)) {
123
- existingLog = fs.readFileSync(updateLogFile, "utf8");
124
- }
125
-
126
- let combinedLog = newBlock + existingLog;
127
-
128
- // Split blocks by plugin header
129
- const blocks = combinedLog.split(/\n(?=codeplay-common:)/);
130
-
131
- // Keep only latest 50
132
- const trimmed = blocks.slice(0, MAX_LOG_BLOCKS).join("\n");
133
-
134
- fs.writeFileSync(updateLogFile, trimmed);
135
-
136
- }
137
-
138
-
139
-
140
-
141
-
142
- const versionsFile = path.join(__dirname, "versions.json");
143
-
144
- function loadRequiredPlugins() {
145
-
146
- if (!fs.existsSync(versionsFile)) {
147
- console.error("❌ versions.json not found");
148
- process.exit(1);
149
- }
150
-
151
- const json = JSON.parse(fs.readFileSync(versionsFile, "utf8"));
152
-
153
- return json.plugins.map(p => ({
154
- ...p,
155
- pattern: new RegExp(p.pattern)
156
- }));
157
-
158
- }
159
-
160
- let requiredPlugins = loadRequiredPlugins();
161
-
162
-
163
-
164
-
165
-
166
-
167
- async function downloadAndExtractZip(url, destFolder) {
168
-
169
- const zipPath = destFolder + ".zip";
170
-
171
- await downloadFile(url, zipPath);
172
-
173
- const zip = new AdmZip(zipPath);
174
- zip.extractAllTo(destFolder, true);
175
-
176
- fs.unlinkSync(zipPath);
177
-
178
- }
179
-
180
-
181
-
182
-
183
-
184
-
185
-
186
- //Check codeplay-common latest version installed or not Start
187
- //const { execSync } = require('child_process');
188
-
189
-
190
- function getInstalledVersion(packageName) {
191
- try {
192
- const packageJsonPath = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
193
- if (fs.existsSync(packageJsonPath)) {
194
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
195
- return packageJson.version;
196
- }
197
- } catch (error) {
198
- return null;
199
- }
200
- return null;
201
- }
202
-
203
- function getLatestVersion(packageName) {
204
- try {
205
- return execSync(`npm view ${packageName} version`).toString().trim();
206
- } catch (error) {
207
- console.error(`Failed to fetch latest version for ${packageName}`);
208
- return null;
209
- }
210
- }
211
-
212
- function checkPackageVersion() {
213
- const packageName = 'codeplay-common';
214
- const installedVersion = getInstalledVersion(packageName);
215
- const latestVersion = getLatestVersion(packageName);
216
-
217
- if (!installedVersion) {
218
- console.error(`${packageName} is not installed. Please install it using "npm install ${packageName}".`);
219
- process.exit(1);
220
- }
221
-
222
- if (installedVersion !== latestVersion) {
223
- console.error(`\x1b[31m${packageName} is outdated (installed: ${installedVersion}, latest: ${latestVersion}). Please update it.\x1b[0m\n\x1b[33mUse 'npm uninstall codeplay-common ; npm i codeplay-common'\x1b[0m`);
224
- process.exit(1);
225
- }
226
-
227
- console.log(`${packageName} is up to date (version ${installedVersion}).`);
228
- }
229
-
230
- // Run package version check before executing the main script
231
- try {
232
- checkPackageVersion();
233
- } catch (error) {
234
- console.error(error.message);
235
- process.exit(1);
236
- }
237
-
238
- //Check codeplay-common latest version installed or not END
239
-
240
-
241
-
242
- function compareWithBeta(installedVersion, minVersion, isBeta) {
243
- const baseCompare = compareVersions(installedVersion, minVersion);
244
-
245
- if (!isBeta) {
246
- // Stable version → normal compare
247
- return baseCompare;
248
- }
249
-
250
- // Beta version logic
251
- if (baseCompare > 0) return 1; // 5.3-beta > 5.2
252
- if (baseCompare < 0) return -1; // 5.1-beta < 5.2
253
-
254
- // Same version but beta LOWER than stable
255
- return -1; // 5.2-beta < 5.2
256
- }
257
-
258
-
259
-
260
-
261
- const checkAppUniqueId=()=>{
262
-
263
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
264
-
265
- const appUniqueId = config.android?.APP_UNIQUE_ID;
266
- const RESIZEABLE_ACTIVITY = config.android?.RESIZEABLE_ACTIVITY;
267
- const orientation = config.android?.ORIENTATION;
268
-
269
-
270
- let logErrorMessage="";
271
-
272
- // 1️⃣ Check if it’s missing
273
- if (RESIZEABLE_ACTIVITY === undefined) {
274
- logErrorMessage+='❌ Missing android.RESIZEABLE_ACTIVITY option in capacitor.config.json.\n';
275
- }
276
-
277
- // 2️⃣ Check if it’s not boolean (true/false only)
278
- else if (typeof RESIZEABLE_ACTIVITY !== 'boolean') {
279
- logErrorMessage+='❌ Invalid android.RESIZEABLE_ACTIVITY value. Please use only true or false (without quotes).\n';
280
- }
281
-
282
-
283
-
284
- if (!orientation) {
285
- logErrorMessage+='❌ Missing android.ORIENTATION option in capacitor.config.json.\n';
286
- }
287
-
288
- else if(orientation!="portrait" && orientation!="landscape" && orientation!="auto")
289
- {
290
- logErrorMessage+='❌ Spelling mistake in android.ORIENTATION option in capacitor.config.json. Please use only ["portrait" "landscape" "auto"]\n';
291
- }
292
-
293
-
294
- if (!appUniqueId) {
295
- logErrorMessage+='❌ APP_UNIQUE_ID is missing in capacitor.config.json.';
296
- }
297
-
298
- else if (!Number.isInteger(appUniqueId)) {
299
- logErrorMessage+='❌ APP_UNIQUE_ID must be an integer. Example: 1, 2, 3, etc.';
300
- }
301
-
302
-
303
-
304
- if(logErrorMessage!="")
305
- {
306
- console.error(logErrorMessage);
307
- process.exit(1)
308
- }
309
-
310
-
311
- console.log(`✅ APP_UNIQUE_ID is valid: ${appUniqueId}`);
312
-
313
- }
314
-
315
- checkAppUniqueId();
316
-
317
-
318
-
319
-
320
-
321
-
322
-
323
- //@Codemirror check and install/uninstall the packages START
324
- //const fs = require("fs");
325
- //const path = require("path");
326
- //const { execSync } = require("child_process");
327
-
328
- const jsDir = path.join(__dirname, "..", "src", "js");
329
-
330
- // 🔍 Detect OLD structure
331
- const oldEditorDirs = fs.readdirSync(jsDir)
332
- .filter(name => /^editor-\d+\.\d+$/.test(name));
333
-
334
- // 📁 New structure path (optional, not mandatory)
335
- const newEditorBaseDir = path.join(jsDir, "editor");
336
-
337
- // ======================================================
338
- // CASE 1: OLD STRUCTURE FOUND → STOP
339
- // ======================================================
340
- if (oldEditorDirs.length > 0) {
341
-
342
- console.error(`
343
- ❌ OLD EDITOR STRUCTURE DETECTED
344
-
345
- You are using outdated folder structure:
346
- src/js/editor-x.x/
347
-
348
- 🚨 This is no longer supported.
349
-
350
- 📦 Found folders:
351
- ${oldEditorDirs.map(d => " - " + d).join("\n")}
352
-
353
- 👉 Please move them manually:
354
-
355
- src/js/editor-1.6
356
-
357
- src/js/editor/editor-1.6
358
-
359
- ⚠️ Also ensure:
360
- src/js/editor/configuration.json exists
361
-
362
- ❌ Build stopped.
363
- `);
364
-
365
- process.exit(1);
366
- }
367
-
368
- // ======================================================
369
- // ✅ CASE 2: NEW STRUCTURE EXISTS → VALIDATE
370
- // ======================================================
371
- if (fs.existsSync(newEditorBaseDir)) {
372
-
373
- // 🔍 Find editor-x.x inside new folder
374
- const editorDirs = fs.readdirSync(newEditorBaseDir)
375
- .filter(name => /^editor-\d+\.\d+$/.test(name));
376
-
377
- // 👉 If editor folder exists but no versions → skip safely
378
- if (editorDirs.length === 0) {
379
- console.log("ℹ️ No editor-x.x folders found inside src/js/editor/");
380
- return;
381
- }
382
-
383
- // ======================================================
384
- // Validate configuration.json (NEW RULE)
385
- // ======================================================
386
-
387
- const editorConfigPath = path.join(newEditorBaseDir, "configuration.json");
388
-
389
- // ❌ Missing config
390
- if (!fs.existsSync(editorConfigPath)) {
391
- console.error(`
392
- ❌ MISSING EDITOR CONFIGURATION FILE
393
-
394
- Required:
395
- src/js/editor/configuration.json
396
-
397
- ❌ Build stopped.
398
- `);
399
- process.exit(1);
400
- }
401
-
402
- // Check wrong placement
403
- const invalidConfigs = [];
404
-
405
- editorDirs.forEach(dir => {
406
- const wrongPath = path.join(newEditorBaseDir, dir, "configuration.json");
407
- if (fs.existsSync(wrongPath)) {
408
- invalidConfigs.push(`src/js/editor/${dir}/configuration.json`);
409
- }
410
- });
411
-
412
- if (invalidConfigs.length > 0) {
413
- console.error(`
414
- INVALID CONFIGURATION LOCATION
415
-
416
- 🚫 configuration.json must NOT be inside version folders.
417
-
418
- Found:
419
- ${invalidConfigs.map(p => " - " + p).join("\n")}
420
-
421
- ✅ Correct:
422
- src/js/editor/configuration.json
423
-
424
- ❌ Build stopped.
425
- `);
426
- process.exit(1);
427
- }
428
-
429
- console.log("✅ Editor structure validated.");
430
-
431
- // ======================================================
432
- // 🚀 Continue execution (run.js)
433
- // ======================================================
434
-
435
- const latestEditorDir = editorDirs.sort((a, b) => {
436
- const vA = parseFloat(a.split('-')[1]);
437
- const vB = parseFloat(b.split('-')[1]);
438
- return vB - vA;
439
- })[0];
440
-
441
- const runJsPath = path.join(newEditorBaseDir, latestEditorDir, "run.js");
442
-
443
- if (!fs.existsSync(runJsPath)) {
444
- console.error(`❌ run.js not found in ${latestEditorDir}`);
445
- process.exit(1);
446
- }
447
-
448
- console.log(`🚀 Executing ${runJsPath}...`);
449
- execSync(`node "${runJsPath}"`, { stdio: "inherit" });
450
- }
451
-
452
- // ======================================================
453
- // CASE 3: NOTHING EXISTS → DO NOTHING
454
- // ======================================================
455
- else {
456
- console.log("ℹ️ Editor not used in this project. Skipping...");
457
- }
458
-
459
- //@Codemirror check and install/uninstall the packages END
460
-
461
-
462
-
463
-
464
-
465
-
466
-
467
-
468
- //editor-x.x import old style check and stop execution START
469
-
470
-
471
-
472
-
473
-
474
-
475
-
476
-
477
- const ROOT_DIR = path.join(__dirname, "..", "src");
478
-
479
- // Match: editor/editor-2.3, editor/editor-2.3.1, etc.
480
- const FORBIDDEN_REGEX = /editor\/editor-\d+(\.\d+)+/;
481
-
482
- let hasError = false;
483
-
484
- const ERROR_MESSAGE = `const ERROR_MESSAGE = ❌ Invalid import detected!
485
-
486
- You are using a direct version-based path like: editor/editor-x.x/editor.js
487
-
488
- 🚫 This is NOT allowed.
489
-
490
- 👉 Please use the proper alias or updated import method.
491
- Example: import { ... } from '@editor'
492
-
493
- ⚠️ Do not use version-based paths in imports.
494
- 👉 Please add this manually in vite.config.js:
495
-
496
- alias: {
497
- '@editor': path.resolve(__dirname, './src/js/editor/editor-x.x')
498
- }`
499
-
500
- function scanDir(dir) {
501
- const files = fs.readdirSync(dir);
502
-
503
- for (const file of files) {
504
- const fullPath = path.join(dir, file);
505
- const stat = fs.statSync(fullPath);
506
-
507
- if (stat.isDirectory()) {
508
- scanDir(fullPath);
509
- } else if (file.endsWith(".js") || file.endsWith(".ts") || file.endsWith(".f7")) {
510
- const content = fs.readFileSync(fullPath, "utf-8");
511
-
512
- const lines = content.split("\n");
513
-
514
- lines.forEach((line, index) => {
515
- if (FORBIDDEN_REGEX.test(line)) {
516
- console.error(
517
- `❌ Forbidden import found:\nFile: ${fullPath}\nLine: ${index + 1}\nCode: ${line.trim()}\n`,
518
- ERROR_MESSAGE
519
- );
520
- hasError = true;
521
- }
522
- });
523
- }
524
- }
525
- }
526
-
527
- // Run scan
528
- scanDir(ROOT_DIR);
529
-
530
- // Throw error (exit process)
531
- if (hasError) {
532
- console.error("🚫 Build failed due to forbidden editor imports.");
533
- process.exit(1);
534
- } else {
535
- console.log("✅ No forbidden imports found.");
536
- }
537
-
538
-
539
-
540
-
541
-
542
-
543
- //editor-x.x import old style check and stop execution START
544
-
545
-
546
-
547
-
548
-
549
-
550
-
551
-
552
-
553
-
554
- // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists START
555
-
556
- const os = require('os');
557
-
558
- const saveToGalleryAndSaveFileCheck_iOS = () => {
559
-
560
- // List of paths to scan
561
- const SCAN_PATHS = [
562
- path.resolve(__dirname, '../src/certificate'),
563
- path.resolve(__dirname, '../src/pages'),
564
- path.resolve(__dirname, '../src/js'),
565
- path.resolve(__dirname, '../src/app.f7')
566
- ];
567
-
568
- // Directory to exclude
569
- const EXCLUDED_DIR = path.resolve(__dirname, '../src/js/Ads');
570
-
571
- const ANDROID_MANIFEST_PATH = path.resolve(__dirname, '../android/app/src/main/AndroidManifest.xml');
572
-
573
-
574
- // Match iOS-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5-ios.js) not in comments
575
- const IOS_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*-ios\.js['"]/m;
576
-
577
- // Match Android-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5.js) not in comments
578
- const ANDROID_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*\.js['"]/m;
579
-
580
-
581
-
582
-
583
-
584
- const ALLOWED_EXTENSIONS = ['.js', '.f7'];
585
- const isMac = os.platform() === 'darwin';
586
-
587
- let iosImportFound = false;
588
- let androidImportFound = false;
589
-
590
- // Files to skip completely (full or partial match)
591
- const SKIP_FILES = [
592
- 'pdf-3.11.174.min.js',
593
- 'pdf.worker-3.11.174.min.js'
594
- ,'index.browser.js'
595
- ];
596
-
597
-
598
- function scanDirectory(dir) {
599
-
600
- /*
601
- //######################### DO NOT DELETE THIS - START [Appid base validation] #####################################
602
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
603
- const appUniqueId = config.android?.APP_UNIQUE_ID;
604
- if (appUniqueId == "206") return;
605
- //######################### DO NOT DELETE THIS - END [Appid base validation] #####################################
606
- */
607
-
608
- const stat = fs.statSync(dir);
609
-
610
- if (stat.isFile()) {
611
-
612
- // 🔥 Skip files in SKIP_FILES array
613
- const baseName = path.basename(dir);
614
- if (SKIP_FILES.includes(baseName)) {
615
- // Just skip silently
616
- return;
617
- }
618
-
619
- // Only scan allowed file extensions
620
- if (ALLOWED_EXTENSIONS.some(ext => dir.endsWith(ext))) {
621
- process.stdout.write(`\r🔍 Scanning: ${dir} `);
622
-
623
- const content = fs.readFileSync(dir, 'utf8');
624
-
625
- if (IOS_FILE_REGEX.test(content)) {
626
- iosImportFound = true;
627
- if (!isMac) {
628
- console.error(`\n❌ ERROR: iOS-specific import detected in: ${dir}`);
629
- console.error(`🚫 STOPPED: This file should not be imported in Android/Windows/Linux builds.\n`);
630
- process.exit(1);
631
- }
632
- }
633
- else if (ANDROID_FILE_REGEX.test(content) && !content.includes('-ios.js')) {
634
- androidImportFound = true;
635
- }
636
- }
637
- }
638
- else if (stat.isDirectory()) {
639
- if (dir === EXCLUDED_DIR || path.basename(dir) === 'node_modules') return;
640
-
641
- const entries = fs.readdirSync(dir, { withFileTypes: true });
642
- for (let entry of entries) {
643
- scanDirectory(path.join(dir, entry.name));
644
- }
645
- }
646
- }
647
-
648
-
649
- // Run scan on all specified paths
650
- for (let scanPath of SCAN_PATHS) {
651
- if (fs.existsSync(scanPath)) {
652
- scanDirectory(scanPath);
653
- }
654
- }
655
-
656
-
657
-
658
- /* // Check src folder
659
- if (!fs.existsSync(ROOT_DIR)) {
660
- console.warn(`⚠️ Warning: 'src' directory not found at: ${ROOT_DIR}`);
661
- return;
662
- } */
663
-
664
- //scanDirectory(ROOT_DIR);
665
-
666
- // iOS Checks
667
- if (isMac && !iosImportFound) {
668
- console.warn(`⚠️ WARNING: You're on macOS but no iOS version (saveToGalleryAndSaveAnyFile-x.x-ios.js) found.`);
669
- process.exit(1);
670
- } else if (isMac && iosImportFound) {
671
- console.log('✅ iOS version detected for macOS build.');
672
- } else if (!iosImportFound) {
673
- console.log('✅ No iOS-specific imports detected for non-macOS.');
674
- }
675
-
676
- // Android Checks
677
- if (androidImportFound) {
678
- console.log("📱 Android version of saveToGalleryAndSaveAnyFile detected. Checking AndroidManifest.xml...");
679
-
680
- if (!fs.existsSync(ANDROID_MANIFEST_PATH)) {
681
- console.error("❌ AndroidManifest.xml not found. Cannot add requestLegacyExternalStorage attribute.");
682
- return;
683
- }
684
-
685
- let manifestContent = fs.readFileSync(ANDROID_MANIFEST_PATH, 'utf8');
686
-
687
- if (!manifestContent.includes('android:requestLegacyExternalStorage="true"')) {
688
- console.log("Adding android:requestLegacyExternalStorage=\"true\" to <application> tag...");
689
-
690
- manifestContent = manifestContent.replace(
691
- /<application([^>]*)>/,
692
- (match, attrs) => {
693
- if (attrs.includes('android:requestLegacyExternalStorage')) return match;
694
- return `<application${attrs} android:requestLegacyExternalStorage="true">`;
695
- }
696
- );
697
-
698
- fs.writeFileSync(ANDROID_MANIFEST_PATH, manifestContent, 'utf8');
699
- console.log("✅ android:requestLegacyExternalStorage=\"true\" added successfully.");
700
- } else {
701
- console.log("ℹ️ android:requestLegacyExternalStorage already exists in AndroidManifest.xml.");
702
- }
703
- } else {
704
- console.log("✅ No Android saveToGalleryAndSaveAnyFile imports detected.");
705
- }
706
- };
707
-
708
- saveToGalleryAndSaveFileCheck_iOS();
709
- // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists END
710
-
711
-
712
-
713
-
714
-
715
-
716
-
717
-
718
-
719
-
720
-
721
-
722
-
723
- /*
724
- // Clean up AppleDouble files (._*) created by macOS START
725
- if (process.platform === 'darwin') {
726
- try {
727
- console.log('🧹 Cleaning up AppleDouble files (._*)...');
728
- execSync(`find . -name '._*' -delete`);
729
- console.log('✅ AppleDouble files removed.');
730
- } catch (err) {
731
- console.warn('⚠️ Failed to remove AppleDouble files:', err.message);
732
- }
733
- } else {
734
- console.log('ℹ️ Skipping AppleDouble cleanup not a macOS machine.');
735
- }
736
-
737
- // Clean up AppleDouble files (._*) created by macOS END
738
- */
739
-
740
-
741
-
742
-
743
-
744
-
745
- //In routes.js file check static import START
746
-
747
- const routesPath = path.join(process.cwd(), 'src', 'js', 'routes.js');
748
- const routesContent = fs.readFileSync(routesPath, 'utf-8');
749
-
750
- let inBlockComment = false;
751
- const lines = routesContent.split('\n');
752
-
753
- const allowedImport = `import HomePage from '../pages/home.f7';`;
754
- const badImportRegex = /^[ \t]*import\s+[\w{}*,\s]*\s+from\s+['"].+\.f7['"]\s*;/;
755
- const badImports = [];
756
-
757
- lines.forEach((line, index) => {
758
- const trimmed = line.trim();
759
-
760
- // Handle block comment start and end
761
- if (trimmed.startsWith('/*')) inBlockComment = true;
762
- if (inBlockComment && trimmed.endsWith('*/')) {
763
- inBlockComment = false;
764
- return;
765
- }
766
-
767
- // Skip if inside block comment or line comment
768
- if (inBlockComment || trimmed.startsWith('//')) return;
769
-
770
- // Match static .f7 import
771
- if (badImportRegex.test(trimmed) && trimmed !== allowedImport) {
772
- badImports.push({ line: trimmed, number: index + 1 });
773
- }
774
- });
775
-
776
- if (badImports.length > 0) {
777
- console.error('\n❌ ERROR: Detected disallowed static imports of .f7 files in routes.js\n');
778
- console.error(`⚠️ Only this static import is allowed:\n ${allowedImport}\n`);
779
- console.error(`🔧 Please convert other imports to async dynamic imports like this:\n`);
780
- console.error(`
781
-
782
- import HomePage from '../pages/home.f7';
783
-
784
- const routes = [
785
- {
786
- path: '/',
787
- component:HomePage,
788
- },
789
- {
790
- path: '/ProfilePage/',
791
- async async({ resolve }) {
792
- const page = await import('../pages/profile.f7');
793
- resolve({ component: page.default });
794
- },
795
- }]
796
- `);
797
-
798
- badImports.forEach(({ line, number }) => {
799
- console.error(`${number}: ${line}`);
800
- });
801
-
802
- process.exit(1);
803
- } else {
804
- console.log('✅ routes.js passed the .f7 import check.');
805
- }
806
-
807
- //In routes.js file check static import END
808
-
809
-
810
-
811
-
812
-
813
-
814
-
815
-
816
-
817
-
818
-
819
-
820
-
821
- // Check and change the "BridgeWebViewClient.java" file START
822
- /*
823
- For crash issue due to low memory problem, we need to modify the onRenderProcessGone method in BridgeWebViewClient.java.
824
- */
825
-
826
-
827
- const bridgeWebViewClientFilePath = path.join(process.cwd(), 'node_modules', '@capacitor/android/capacitor/src/main/java/com/getcapacitor', 'BridgeWebViewClient.java');
828
-
829
- // Read the file
830
- if (!fs.existsSync(bridgeWebViewClientFilePath)) {
831
- console.error('❌ Error: BridgeWebViewClient.java not found.');
832
- process.exit(1);
833
- }
834
-
835
- let fileContent = fs.readFileSync(bridgeWebViewClientFilePath, 'utf8');
836
-
837
- // Define old and new code
838
- const oldCodeStart = `@Override
839
- public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
840
- super.onRenderProcessGone(view, detail);
841
- boolean result = false;
842
-
843
- List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
844
- if (webViewListeners != null) {
845
- for (WebViewListener listener : bridge.getWebViewListeners()) {
846
- result = listener.onRenderProcessGone(view, detail) || result;
847
- }
848
- }
849
-
850
- return result;
851
- }`;
852
-
853
- const newCode = `@Override
854
- public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
855
- super.onRenderProcessGone(view, detail);
856
-
857
- boolean result = false;
858
-
859
- List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
860
- if (webViewListeners != null) {
861
- for (WebViewListener listener : bridge.getWebViewListeners()) {
862
- result = listener.onRenderProcessGone(view, detail) || result;
863
- }
864
- }
865
-
866
- if (!result) {
867
- // If no one handled it, handle it ourselves!
868
-
869
- /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
870
- if (detail.didCrash()) {
871
- //Log.e("CapacitorWebView", "WebView crashed internally!");
872
- } else {
873
- //Log.w("CapacitorWebView", "WebView was killed by system (low memory) internally!");
874
- }
875
- }*/
876
-
877
- view.post(() -> {
878
- Toast.makeText(view.getContext(), "Reloading due to low memory issue", Toast.LENGTH_SHORT).show();
879
- });
880
-
881
- view.reload(); // Safely reload WebView
882
-
883
- return true; // We handled it
884
- }
885
-
886
- return result;
887
- }`;
888
-
889
- // Step 1: Update method if needed
890
- let updated = false;
891
-
892
- if (fileContent.includes(oldCodeStart)) {
893
- console.log('✅ Found old onRenderProcessGone method. Replacing it...');
894
- fileContent = fileContent.replace(oldCodeStart, newCode);
895
- updated = true;
896
- } else if (fileContent.includes(newCode)) {
897
- console.log('ℹ️ Method already updated. No changes needed in "BridgeWebViewClient.java".');
898
- } else {
899
- console.error('❌ Error: Neither old nor new code found. Unexpected content.');
900
- process.exit(1);
901
- }
902
-
903
- // Step 2: Check and add import if missing
904
- const importToast = 'import android.widget.Toast;';
905
- if (!fileContent.includes(importToast)) {
906
- console.log('✅ Adding missing import for Toast...');
907
- const importRegex = /import\s+[^;]+;/g;
908
- const matches = [...fileContent.matchAll(importRegex)];
909
-
910
- if (matches.length > 0) {
911
- const lastImport = matches[matches.length - 1];
912
- const insertPosition = lastImport.index + lastImport[0].length;
913
- fileContent = fileContent.slice(0, insertPosition) + `\n${importToast}` + fileContent.slice(insertPosition);
914
- updated = true;
915
- } else {
916
- console.error(' Error: No import section found in file.');
917
- process.exit(1);
918
- }
919
- } else {
920
- console.log('ℹ️ Import for Toast already exists. No changes needed.');
921
- }
922
-
923
- // Step 3: Save if updated
924
- if (updated) {
925
- fs.writeFileSync(bridgeWebViewClientFilePath, fileContent, 'utf8');
926
- console.log('✅ File updated successfully.');
927
- } else {
928
- console.log('ℹ️ No changes needed.');
929
- }
930
-
931
-
932
-
933
-
934
- // Check and change the "BridgeWebViewClient.java" file END
935
-
936
-
937
-
938
-
939
-
940
-
941
-
942
-
943
- /*
944
- // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file START
945
-
946
- // Build the path dynamically like you requested
947
- const gradlePath = path.join(
948
- process.cwd(),
949
- 'android',
950
- 'build.gradle'
951
- );
952
-
953
- // Read the existing build.gradle
954
- let gradleContent = fs.readFileSync(gradlePath, 'utf8');
955
-
956
- // Add `ext.kotlin_version` if it's not already there
957
- if (!gradleContent.includes('ext.kotlin_version')) {
958
- gradleContent = gradleContent.replace(
959
- /buildscript\s*{/,
960
- `buildscript {\n ext.kotlin_version = '2.1.0'`
961
- );
962
- }
963
-
964
- // Add Kotlin classpath if it's not already there
965
- if (!gradleContent.includes('org.jetbrains.kotlin:kotlin-gradle-plugin')) {
966
- gradleContent = gradleContent.replace(
967
- /dependencies\s*{([\s\S]*?)classpath 'com.android.tools.build:gradle:8.7.2'/,
968
- `dependencies {\n classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")\n$1classpath 'com.android.tools.build:gradle:8.7.2'`
969
- );
970
- }
971
-
972
- // Write back the modified content
973
- fs.writeFileSync(gradlePath, gradleContent, 'utf8');
974
-
975
- console.log('✅ Kotlin version updated in build.gradle.');
976
-
977
- // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file END
978
- */
979
-
980
-
981
-
982
-
983
-
984
-
985
-
986
-
987
- let _admobConfig;
988
-
989
-
990
-
991
- const androidPlatformPath = path.join(process.cwd(), 'android');
992
- const iosPlatformPath = path.join(process.cwd(), 'ios');
993
- const pluginPath = path.join(process.cwd(), 'node_modules', 'emi-indo-cordova-plugin-admob', 'plugin.xml');
994
- const infoPlistPath = path.join(process.cwd(), 'ios', 'App', 'App', 'Info.plist');
995
- const resourcesPath = path.join(process.cwd(), 'resources', 'res');
996
- const androidResPath = path.join(process.cwd(), 'android', 'app', 'src', 'main', 'res');
997
- const localNotificationsPluginPath = path.join(process.cwd(), 'node_modules', '@capacitor', 'local-notifications');
998
-
999
- function fileExists(filePath) {
1000
- return fs.existsSync(filePath);
1001
- }
1002
-
1003
- function copyFolderSync(source, target) {
1004
- if (!fs.existsSync(target)) {
1005
- fs.mkdirSync(target, { recursive: true });
1006
- }
1007
-
1008
- fs.readdirSync(source).forEach(file => {
1009
- const sourceFile = path.join(source, file);
1010
- const targetFile = path.join(target, file);
1011
-
1012
- if (fs.lstatSync(sourceFile).isDirectory()) {
1013
- copyFolderSync(sourceFile, targetFile);
1014
- } else {
1015
- fs.copyFileSync(sourceFile, targetFile);
1016
- }
1017
- });
1018
- }
1019
-
1020
- function checkAndCopyResources() {
1021
- if (fileExists(resourcesPath)) {
1022
- copyFolderSync(resourcesPath, androidResPath);
1023
- console.log('✅ Successfully copied resources/res to android/app/src/main/res.');
1024
- } else {
1025
- console.log('resources/res folder not found.');
1026
-
1027
- if (fileExists(localNotificationsPluginPath)) {
1028
- throw new Error('❌ resources/res is required for @capacitor/local-notifications. Stopping execution.');
1029
- }
1030
- }
1031
- }
1032
-
1033
-
1034
-
1035
-
1036
-
1037
-
1038
-
1039
-
1040
- function getAdMobConfig() {
1041
- if (!fileExists(configPath)) {
1042
- throw new Error('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
1043
- }
1044
-
1045
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
1046
- const admobConfig = config.plugins?.AdMob;
1047
-
1048
- if (!admobConfig) {
1049
- throw new Error('❌ AdMob configuration is missing in capacitor.config.json.');
1050
- }
1051
-
1052
- // Default to true if ADMOB_ENABLED is not specified
1053
- const isEnabled = admobConfig.ADMOB_ENABLED !== false;
1054
-
1055
- if (!isEnabled) {
1056
- return { ADMOB_ENABLED: false }; // Skip further validation
1057
- }
1058
-
1059
- if (!admobConfig.APP_ID_ANDROID || !admobConfig.APP_ID_IOS) {
1060
- throw new Error(' ❌ AdMob configuration is incomplete. Ensure APP_ID_ANDROID and APP_ID_IOS are defined.');
1061
- }
1062
-
1063
- return {
1064
- ADMOB_ENABLED: true,
1065
- APP_ID_ANDROID: admobConfig.APP_ID_ANDROID,
1066
- APP_ID_IOS: admobConfig.APP_ID_IOS,
1067
- USE_LITE_ADS: admobConfig.USE_LITE_ADS === "lite",
1068
- };
1069
- }
1070
-
1071
- function validateAndroidBuildOptions() {
1072
-
1073
-
1074
- if (!fileExists(configPath)) {
1075
- console.log('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
1076
- process.exit(1);
1077
- }
1078
-
1079
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
1080
-
1081
- const targetAppId=config.appId
1082
-
1083
- const buildOptions = config.android?.buildOptions;
1084
-
1085
- if (!buildOptions) {
1086
- console.log('❌ Missing android.buildOptions in capacitor.config.json.');
1087
- process.exit(1);
1088
- }
1089
-
1090
- const requiredProps = [
1091
- 'keystorePath',
1092
- 'keystorePassword',
1093
- 'keystoreAlias',
1094
- 'keystoreAliasPassword',
1095
- 'releaseType',
1096
- 'signingType'
1097
- ];
1098
-
1099
- const missing = requiredProps.filter(prop => !buildOptions[prop]);
1100
-
1101
- if (missing.length > 0) {
1102
- console.log('❌ Missing properties android.buildOptions in capacitor.config.json.');
1103
- process.exit(1);
1104
- }
1105
-
1106
-
1107
- const keystorePath=buildOptions.keystorePath
1108
- const keyFileName = path.basename(keystorePath);
1109
-
1110
-
1111
-
1112
- const keystoreMap = {
1113
- "gameskey.jks": [
1114
- "com.cube.blaster",
1115
- ],
1116
- "htmleditorkeystoke.jks": [
1117
- "com.HTML.AngularJS.Codeplay",
1118
- "com.html.codeplay.pro",
1119
- "com.bootstrap.code.play",
1120
- "com.kids.learning.master",
1121
- "com.Simple.Barcode.Scanner",
1122
- "com.FAShtmlcssjs.editor"
1123
- ]
1124
- };
1125
-
1126
- // find which keystore is required for the given targetAppId
1127
- let requiredKey = "newappskey.jks"; // default
1128
- for (const [keyFile, appIds] of Object.entries(keystoreMap)) {
1129
- if (appIds.includes(targetAppId)) {
1130
- requiredKey = keyFile;
1131
- break;
1132
- }
1133
- }
1134
-
1135
- // validate
1136
- if (keyFileName !== requiredKey) {
1137
- console.log(`❌ The keystore path is mismatched. Expected ${requiredKey} for ${targetAppId}, but got ${keyFileName}`);
1138
- process.exit(1);
1139
- }
1140
-
1141
-
1142
-
1143
-
1144
-
1145
- // optionally return them
1146
- //return buildOptions;
1147
- }
1148
-
1149
- function updatePluginXml(admobConfig) {
1150
- if (!fileExists(pluginPath)) {
1151
- console.error(' plugin.xml not found. Ensure the plugin is installed.');
1152
- return;
1153
- }
1154
-
1155
- let pluginContent = fs.readFileSync(pluginPath, 'utf8');
1156
-
1157
- pluginContent = pluginContent
1158
- .replace(/<preference name="APP_ID_ANDROID" default=".*?" \/>/, `<preference name="APP_ID_ANDROID" default="${admobConfig.APP_ID_ANDROID}" />`)
1159
- .replace(/<preference name="APP_ID_IOS" default=".*?" \/>/, `<preference name="APP_ID_IOS" default="${admobConfig.APP_ID_IOS}" />`);
1160
-
1161
- fs.writeFileSync(pluginPath, pluginContent, 'utf8');
1162
- console.log('✅ AdMob IDs successfully updated in plugin.xml');
1163
- }
1164
-
1165
- function updateInfoPlist(admobConfig) {
1166
- if (!fileExists(infoPlistPath)) {
1167
- console.error(' ❌ Info.plist not found. Ensure you have built the iOS project.');
1168
- return;
1169
- }
1170
-
1171
- const plistContent = fs.readFileSync(infoPlistPath, 'utf8');
1172
- const plistData = plist.parse(plistContent);
1173
-
1174
- plistData.GADApplicationIdentifier = admobConfig.APP_ID_IOS;
1175
- plistData.NSUserTrackingUsageDescription = 'This identifier will be used to deliver personalized ads to you.';
1176
- plistData.GADDelayAppMeasurementInit = true;
1177
-
1178
- const updatedPlistContent = plist.build(plistData);
1179
- fs.writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
1180
- console.log('AdMob IDs and additional configurations successfully updated in Info.plist');
1181
- }
1182
-
1183
-
1184
- try {
1185
- if (!fileExists(configPath)) {
1186
- throw new Error(' ❌ capacitor.config.json not found. Skipping setup.');
1187
- }
1188
-
1189
- if (!fileExists(androidPlatformPath) && !fileExists(iosPlatformPath)) {
1190
- throw new Error('Neither Android nor iOS platforms are found. Ensure platforms are added to your Capacitor project.');
1191
- }
1192
-
1193
- checkAndCopyResources();
1194
-
1195
-
1196
-
1197
- _admobConfig = getAdMobConfig();
1198
-
1199
-
1200
-
1201
-
1202
-
1203
- // Proceed only if ADMOB_ENABLED is true
1204
- if (_admobConfig.ADMOB_ENABLED) {
1205
- if (fileExists(androidPlatformPath)) {
1206
- updatePluginXml(_admobConfig);
1207
- }
1208
-
1209
- if (fileExists(iosPlatformPath)) {
1210
- updateInfoPlist(_admobConfig);
1211
- }
1212
- }
1213
-
1214
-
1215
- } catch (error) {
1216
- console.error(error.message);
1217
- process.exit(1); // Stop execution if there's a critical error
1218
- }
1219
-
1220
-
1221
-
1222
- validateAndroidBuildOptions();
1223
-
1224
-
1225
-
1226
-
1227
-
1228
-
1229
- // Check all the codeplays plugins version START
1230
-
1231
-
1232
- const readline = require('readline');
1233
-
1234
-
1235
- //const srcDir = path.join(__dirname, 'src');
1236
- const srcDir = path.join(process.cwd(), 'src');
1237
- let outdatedPlugins = [];
1238
-
1239
- function parseVersion(ver) {
1240
- return ver.split('.').map(n => parseInt(n, 10));
1241
- }
1242
-
1243
- function compareVersions(v1, v2) {
1244
- const [a1, b1] = parseVersion(v1);
1245
- const [a2, b2] = parseVersion(v2);
1246
- if (a1 !== a2) return a1 - a2;
1247
- return b1 - b2;
1248
- }
1249
-
1250
- function walkSync(dir, filelist = []) {
1251
- fs.readdirSync(dir).forEach(file => {
1252
- const fullPath = path.join(dir, file);
1253
- const stat = fs.statSync(fullPath);
1254
- if (stat.isDirectory()) {
1255
- walkSync(fullPath, filelist);
1256
- } else {
1257
- filelist.push(fullPath);
1258
- }
1259
- });
1260
- return filelist;
1261
- }
1262
-
1263
-
1264
-
1265
- function getSearchRoot(plugin) {
1266
- return path.join(srcDir, plugin.baseDir || 'js');
1267
- }
1268
-
1269
-
1270
-
1271
-
1272
-
1273
-
1274
-
1275
-
1276
-
1277
-
1278
-
1279
-
1280
-
1281
-
1282
-
1283
-
1284
-
1285
-
1286
-
1287
-
1288
- /*############################################## AUTO DOWNLOAD FROM SERVER START #####################################*/
1289
-
1290
- // ============================================================
1291
- // 🔥 AUTO PLUGIN UPDATE SYSTEM (MANDATORY UPDATES)
1292
- // ============================================================
1293
-
1294
- const https = require("https");
1295
-
1296
- /**
1297
- * Check if file exists on server using HEAD request
1298
- */
1299
- function urlExists(url) {
1300
- return new Promise(resolve => {
1301
- const req = https.request(url, { method: "HEAD" }, res => {
1302
- resolve(res.statusCode === 200);
1303
- });
1304
-
1305
- req.on("error", () => resolve(false));
1306
- req.end();
1307
- });
1308
- }
1309
-
1310
- /**
1311
- * Download file from server
1312
- */
1313
- function downloadFile(url, dest) {
1314
- return new Promise((resolve, reject) => {
1315
- const file = fs.createWriteStream(dest);
1316
-
1317
- https.get(url, response => {
1318
- if (response.statusCode !== 200) {
1319
- reject("Download failed");
1320
- return;
1321
- }
1322
-
1323
- response.pipe(file);
1324
-
1325
- file.on("finish", () => {
1326
- file.close(resolve);
1327
- });
1328
- }).on("error", err => {
1329
- fs.unlink(dest, () => {});
1330
- reject(err);
1331
- });
1332
- });
1333
- }
1334
-
1335
- /**
1336
- * Update imports across src folder
1337
- * Replaces old filename → new filename
1338
- */
1339
- /**
1340
- * Update imports across project
1341
- * Replaces old filename → new filename
1342
- */
1343
-
1344
- const VITE_ALIAS_ONLY = [
1345
- "common",
1346
- "admob-emi",
1347
- "localization",
1348
- "theme",
1349
- "certificatejs",
1350
- "ffmpeg"
1351
- ];
1352
-
1353
- function updateImports(oldName, newName) {
1354
-
1355
- const projectRoot = process.cwd();
1356
-
1357
- const filesToScan = [
1358
- path.join(projectRoot, "vite.config.js"),
1359
- path.join(projectRoot, "vite.config.mjs")
1360
- ];
1361
-
1362
- const srcDir = path.join(projectRoot, "src");
1363
-
1364
- // scan vite config
1365
- filesToScan.forEach(file => {
1366
-
1367
- if (!fs.existsSync(file)) return;
1368
-
1369
- let content = fs.readFileSync(file, "utf8");
1370
-
1371
- if (content.includes(oldName)) {
1372
-
1373
- content = content.split(oldName).join(newName);
1374
-
1375
- fs.writeFileSync(file, content);
1376
-
1377
- console.log(`✏️ Updated alias in ${path.basename(file)}`);
1378
- }
1379
-
1380
- });
1381
-
1382
- // scan src files
1383
- function walk(dir) {
1384
-
1385
- fs.readdirSync(dir).forEach(file => {
1386
-
1387
- if (["node_modules","android","ios","dist",".git"].includes(file))
1388
- return;
1389
-
1390
- const full = path.join(dir, file);
1391
- const stat = fs.statSync(full);
1392
-
1393
- if (stat.isDirectory()) {
1394
- walk(full);
1395
- }
1396
-
1397
- else if (
1398
- (full.endsWith(".js") ||
1399
- full.endsWith(".f7") ||
1400
- full.endsWith(".mjs")) &&
1401
- !full.endsWith(".min.js")
1402
- ) {
1403
-
1404
- let content = fs.readFileSync(full, "utf8");
1405
-
1406
- if (content.includes(oldName)) {
1407
-
1408
- content = content.split(oldName).join(newName);
1409
-
1410
- fs.writeFileSync(full, content);
1411
-
1412
- console.log(`✏️ Updated import in ${path.relative(projectRoot, full)}`);
1413
- }
1414
-
1415
- }
1416
-
1417
- });
1418
-
1419
- }
1420
-
1421
- if (fs.existsSync(srcDir)) {
1422
- walk(srcDir);
1423
- }
1424
-
1425
- }
1426
-
1427
-
1428
- /**
1429
- * Auto-update a plugin file
1430
- * Returns TRUE if success
1431
- * Returns FALSE if fallback to manual needed
1432
- */
1433
-
1434
-
1435
- let _serverVersions = null;
1436
- async function fetchVersions() {
1437
-
1438
- if (_serverVersions) return _serverVersions;
1439
-
1440
- return new Promise((resolve) => {
1441
-
1442
- https.get(
1443
- "https://htmlcodeplay.com/code-play-plugin/versions.json",
1444
- { timeout: 5000 },
1445
- res => {
1446
-
1447
- let data = "";
1448
-
1449
- res.on("data", chunk => data += chunk);
1450
-
1451
- res.on("end", () => {
1452
-
1453
- try {
1454
-
1455
- _serverVersions = JSON.parse(data);
1456
-
1457
- resolve(_serverVersions);
1458
-
1459
- } catch {
1460
-
1461
- resolve(null);
1462
-
1463
- }
1464
-
1465
- });
1466
-
1467
- }
1468
-
1469
- ).on("error", () => resolve(null));
1470
-
1471
- });
1472
-
1473
- }
1474
-
1475
-
1476
-
1477
- async function autoUpdatePlugin(pluginDef, pluginInfo) {
1478
-
1479
- const versions = await fetchVersions();
1480
-
1481
- if (!versions) {
1482
- console.log("⚠️ versions.json not reachable");
1483
- return false;
1484
- }
1485
-
1486
- const oldFullPath = path.join(srcDir, pluginInfo.name);
1487
- const oldFileName = path.basename(oldFullPath);
1488
-
1489
- //const oldFileName = path.basename(oldFullPath);
1490
- const oldVersionFile = oldFileName;
1491
-
1492
-
1493
- const ext = path.extname(oldFileName); // .js or .less
1494
- //const baseName = oldFileName.replace(/-\d+\.\d+.*$/, "");
1495
- const baseName = oldFileName.replace(/-\d+\.\d+.*$/, '').replace(/\.(js|less)$/, '');
1496
-
1497
- // version lookup key
1498
- let pluginKey = baseName;
1499
-
1500
-
1501
- // Only common plugin has js and less variants
1502
- if (baseName === "common") {
1503
- if (ext === ".js") pluginKey = "common-js";
1504
- if (ext === ".less") pluginKey = "common-less";
1505
- }
1506
-
1507
- const latestVersion = versions[pluginKey];
1508
-
1509
- if (!latestVersion) {
1510
- console.log(`❌ No version entry for ${baseName}`);
1511
- return false;
1512
- }
1513
-
1514
- // ===============================
1515
- // FOLDER PLUGIN UPDATE
1516
- // ===============================
1517
- if (pluginDef.isFolder) {
1518
-
1519
- const zipName = `${baseName}-${latestVersion}.zip`;
1520
- const url = `https://htmlcodeplay.com/code-play-plugin/${zipName}`;
1521
-
1522
- const destRoot = path.join(srcDir, pluginDef.destDir || pluginDef.baseDir || '');
1523
- const oldPath = path.join(destRoot, pluginInfo.name);
1524
- const newPath = path.join(destRoot, `${baseName}-${latestVersion}`);
1525
-
1526
- if (!(await urlExists(url))) return false;
1527
-
1528
- fs.rmSync(oldPath, { recursive: true, force: true });
1529
-
1530
- await downloadAndExtractZip(url, newPath);
1531
-
1532
- updateImports(pluginInfo.name, `${baseName}-${latestVersion}`);
1533
-
1534
- // ADD THIS
1535
- writeUpdateLine(`${pluginInfo.name} -> ${baseName}-${latestVersion}`);
1536
-
1537
- console.log(`✅ Folder updated → ${baseName}-${latestVersion}`);
1538
-
1539
- return true;
1540
- }
1541
- // ===============================
1542
- // FILE PLUGIN UPDATE
1543
- // ===============================
1544
-
1545
- const pluginDir = path.dirname(oldFullPath);
1546
-
1547
- // Only this plugin has ios variant
1548
- const IOS_VARIANT_PLUGINS = [
1549
- "saveToGalleryAndSaveAnyFile"
1550
- ];
1551
-
1552
- /* let variants = [
1553
- `${baseName}-${latestVersion}.js`
1554
- ]; */
1555
-
1556
- //const ext = path.extname(oldFileName);
1557
- const variants = [`${baseName.replace(ext,'')}-${latestVersion}${ext}`];
1558
-
1559
-
1560
- if (IOS_VARIANT_PLUGINS.includes(baseName)) {
1561
- variants.push(`${baseName}-${latestVersion}-ios.js`);
1562
- }
1563
-
1564
- let downloaded = [];
1565
-
1566
- // Download files
1567
- for (const fileName of variants) {
1568
-
1569
- const url = `https://htmlcodeplay.com/code-play-plugin/${fileName}`;
1570
-
1571
- console.log(`🔍 Checking latest: ${fileName}`);
1572
-
1573
- if (await urlExists(url)) {
1574
-
1575
- const destPath = path.join(pluginDir, fileName);
1576
-
1577
- await downloadFile(url, destPath);
1578
-
1579
- downloaded.push(fileName);
1580
-
1581
- console.log(`⬇ Downloaded → ${fileName}`);
1582
-
1583
- //writeUpdateLine(`Downloaded: ${fileName}`);
1584
-
1585
- }
1586
- }
1587
-
1588
- if (downloaded.length === 0) {
1589
- console.log(`❌ No files downloaded for ${baseName}`);
1590
- return false;
1591
- }
1592
-
1593
- // Remove ONLY versioned files (safe)
1594
- //const versionPattern = new RegExp(`^${baseName}-\\d+\\.\\d+(-ios)?\\.js$`);
1595
- const versionPattern = new RegExp(`^${baseName}-\\d+\\.\\d+(-ios)?\\${ext}$`);
1596
-
1597
- const existingFiles = fs.readdirSync(pluginDir);
1598
-
1599
- existingFiles.forEach(file => {
1600
-
1601
- if (
1602
- versionPattern.test(file) &&
1603
- !downloaded.includes(file)
1604
- ) {
1605
-
1606
- const oldPath = path.join(pluginDir, file);
1607
-
1608
- fs.unlinkSync(oldPath);
1609
-
1610
- console.log(`🗑 Removed old file → ${file}`);
1611
- //writeUpdateLine(`Removed old file: ${file}`);
1612
- }
1613
-
1614
- });
1615
-
1616
- //const newFileName = `${baseName}-${latestVersion}.js`;
1617
- const newFileName = `${baseName}-${latestVersion}${ext}`;
1618
-
1619
- writeUpdateLine(`${oldVersionFile} -> ${newFileName}`);
1620
-
1621
- updateImports(oldFileName, newFileName);
1622
- //updateImports(baseName, `${baseName}-${latestVersion}.js`);
1623
- //updateImports(pluginInfo.name, `${baseName}-${latestVersion}`);
1624
-
1625
- //console.log(`✅ Updated ${newFileName}`);
1626
- //console.log(`✅ Updated → ${baseName}-${latestVersion}`);
1627
-
1628
- return true;
1629
- }
1630
-
1631
-
1632
-
1633
-
1634
- /*############################################## AUTO DOWNLOAD FROM SERVER END #####################################*/
1635
-
1636
-
1637
-
1638
-
1639
-
1640
-
1641
-
1642
-
1643
-
1644
-
1645
-
1646
-
1647
-
1648
-
1649
-
1650
-
1651
-
1652
-
1653
- async function loadPluginVersions() {
1654
-
1655
- if (!USE_LIVE_SERVER_VERSION) {
1656
- console.log("ℹ️ Using local plugin versions (offline mode).");
1657
- return;
1658
- }
1659
-
1660
- console.log("🌐 Fetching plugin versions from server...");
1661
-
1662
- const versions = await fetchVersions();
1663
-
1664
- if (!versions || typeof versions !== "object") {
1665
- console.log("⚠️ Server unavailable or invalid versions.json. Falling back to local versions.");
1666
- return;
1667
- }
1668
-
1669
- requiredPlugins.forEach(plugin => {
1670
-
1671
- if (!plugin.name) return;
1672
-
1673
- if (versions[plugin.name]) {
1674
- plugin.minVersion = versions[plugin.name];
1675
- }
1676
-
1677
- });
1678
-
1679
- console.log("✅ Plugin versions loaded from server.");
1680
-
1681
- }
1682
-
1683
-
1684
-
1685
- let hasMandatoryUpdate = false;
1686
- function checkPlugins() {
1687
- return new Promise(async (resolve, reject) => {
1688
- const files = walkSync(srcDir);
1689
- const outdatedPlugins = [];
1690
- let hasMandatoryUpdate = false;
1691
-
1692
- for (const plugin of requiredPlugins) {
1693
- const searchRoot = getSearchRoot(plugin);
1694
-
1695
- // ---------- Folder plugins ----------
1696
- if (plugin.isFolder) {
1697
- if (!fs.existsSync(searchRoot)) continue;
1698
-
1699
- const subDirs = fs.readdirSync(searchRoot)
1700
- .map(name => path.join(searchRoot, name))
1701
- .filter(p => fs.statSync(p).isDirectory());
1702
-
1703
- for (const dir of subDirs) {
1704
- const relativePath = path.relative(searchRoot, dir).replace(/\\/g, '/');
1705
- const match = plugin.pattern.exec(relativePath);
1706
-
1707
- if (match) {
1708
- const currentVersion = match[1];
1709
-
1710
- if (compareVersions(currentVersion, plugin.minVersion) < 0) {
1711
- outdatedPlugins.push({
1712
- name: relativePath,
1713
- currentVersion,
1714
- requiredVersion: plugin.minVersion,
1715
- mandatoryUpdate: plugin.mandatoryUpdate === true
1716
- });
1717
-
1718
- if (plugin.mandatoryUpdate) {
1719
- hasMandatoryUpdate = true;
1720
- }
1721
- }
1722
- }
1723
- }
1724
- continue;
1725
- }
1726
-
1727
- // ---------- File plugins ----------
1728
- const matchedFile = files.find(file =>
1729
- file.startsWith(searchRoot) && plugin.pattern.test(file)
1730
- );
1731
-
1732
- if (matchedFile) {
1733
- const match = plugin.pattern.exec(matchedFile);
1734
- if (match) {
1735
- const currentVersion = match[1];
1736
- const isBeta = !!match[2];
1737
-
1738
- const cmp = plugin.pattern.source.includes('beta')
1739
- ? compareWithBeta(currentVersion, plugin.minVersion, isBeta)
1740
- : compareVersions(currentVersion, plugin.minVersion);
1741
-
1742
- if (cmp < 0) {
1743
- outdatedPlugins.push({
1744
- name: path.relative(srcDir, matchedFile),
1745
- currentVersion: isBeta ? `${currentVersion}-beta` : currentVersion,
1746
- requiredVersion: plugin.minVersion,
1747
- mandatoryUpdate: plugin.mandatoryUpdate === true
1748
- });
1749
-
1750
- if (plugin.mandatoryUpdate) {
1751
- hasMandatoryUpdate = true;
1752
- }
1753
- }
1754
- }
1755
- }
1756
- }
1757
-
1758
- // ---------- Result handling ----------
1759
- if (outdatedPlugins.length > 0) {
1760
- console.log('\n❗ The following plugins are outdated:\n');
1761
-
1762
- outdatedPlugins.forEach( p => {
1763
- const tag = p.mandatoryUpdate ? '🔥 MANDATORY' : '';
1764
- console.log(
1765
- ` ⚠️ - ${p.name} (Current: ${p.currentVersion}, Required: ${p.requiredVersion}) ${tag}`
1766
- );
1767
- });
1768
-
1769
- // 🚨 Mandatory update → stop build
1770
- /* if (hasMandatoryUpdate) {
1771
- console.log('\n🚫 One or more plugins require a mandatory update.');
1772
- console.log('❌ Build cancelled. Please update mandatory plugins and try again.');
1773
- process.exit(1);
1774
- } */
1775
-
1776
-
1777
-
1778
-
1779
-
1780
- if (hasMandatoryUpdate) {
1781
-
1782
- //--------------------------------------------------
1783
- // 🚫 AUTO UPDATE DISABLED
1784
- //--------------------------------------------------
1785
- if (!ENABLE_AUTO_UPDATE) {
1786
- console.log("\n🚫 Auto-update disabled.");
1787
- console.log("❌ Manual update required.");
1788
- process.exit(1);
1789
- }
1790
-
1791
- //--------------------------------------------------
1792
- // 🔥 AUTO UPDATE ENABLED
1793
- //--------------------------------------------------
1794
- console.log("\n🔥 Mandatory plugins outdated. Trying auto-update...\n");
1795
-
1796
-
1797
-
1798
- let autoFailed = false;
1799
-
1800
- for (const p of outdatedPlugins.filter(x => x.mandatoryUpdate)) {
1801
-
1802
- const pluginDef = requiredPlugins.find(def =>
1803
- def.pattern.test(p.name)
1804
- );
1805
-
1806
- if (!pluginDef) continue;
1807
-
1808
- const success = await autoUpdatePlugin(
1809
- pluginDef,
1810
- p
1811
- );
1812
-
1813
- if (!success) {
1814
- autoFailed = true;
1815
-
1816
- const pluginDef = requiredPlugins.find(def =>
1817
- def.pattern.test(p.name)
1818
- );
1819
-
1820
- console.log(`❌ Manual update required for ${p.name}`);
1821
-
1822
- if (pluginDef) {
1823
- console.log(`👉 Required minimum version: ${pluginDef.minVersion}`);
1824
- }
1825
- }
1826
- }
1827
-
1828
- // 🚨 Fallback to manual if any failed
1829
- if (autoFailed) {
1830
- console.log('\n🚫 One or more plugins require manual update.');
1831
- console.log('❌ Build cancelled. Please update mandatory plugins.');
1832
- process.exit(1);
1833
- }
1834
-
1835
- console.log('\n🎉 All mandatory plugins auto-updated! Rechecking plugins...\n');
1836
-
1837
- // Re-run plugin check so outdated list becomes empty
1838
- await checkPlugins();
1839
- return;
1840
- }
1841
-
1842
-
1843
-
1844
-
1845
-
1846
-
1847
- // Optional updates → ask user
1848
- const rl = readline.createInterface({
1849
- input: process.stdin,
1850
- output: process.stdout
1851
- });
1852
-
1853
- rl.question(
1854
- '\nAre you sure you want to continue without updating these plugins? (y/n): ',
1855
- answer => {
1856
- rl.close();
1857
-
1858
- if (answer.toLowerCase() !== 'y') {
1859
- console.log('\n❌ Build cancelled due to outdated plugins.');
1860
- process.exit(1);
1861
- } else {
1862
- console.log('\n✅ Continuing build...');
1863
- resolve();
1864
- }
1865
- }
1866
- );
1867
- } else {
1868
- console.log('✅ All plugin versions are up to date.');
1869
- saveUpdateLogs();
1870
- resolve();
1871
- }
1872
- });
1873
- }
1874
-
1875
-
1876
-
1877
-
1878
-
1879
-
1880
-
1881
-
1882
- // Check all the codeplays plugins version START
1883
-
1884
-
1885
-
1886
-
1887
- // ====================================================================
1888
- // AUTO-ADD esbuild.drop: ['console','debugger'] to vite.config.js / mjs
1889
- // ====================================================================
1890
-
1891
-
1892
-
1893
- const checkAndupdateDropInViteConfig = () => {
1894
-
1895
- const possibleFiles = [
1896
- "vite.config.js",
1897
- "vite.config.mjs"
1898
- ];
1899
-
1900
- // Detect existing config file
1901
- const viteConfigPath = possibleFiles
1902
- .map(file => path.join(process.cwd(), file))
1903
- .find(filePath => fs.existsSync(filePath));
1904
-
1905
- if (!viteConfigPath) {
1906
- console.warn("⚠️ No vite config found. Skipping.");
1907
- return;
1908
- }
1909
-
1910
- //console.log("📄 Using:", viteConfigPath.split("/").pop());
1911
-
1912
- let viteContent = fs.readFileSync(viteConfigPath, "utf8");
1913
-
1914
- // Skip if already exists
1915
- if (/drop\s*:\s*\[.*['"]console['"].*\]/.test(viteContent)) {
1916
- console.log("ℹ️ vite.config.(m)js already Updated. Skipping...");
1917
- return;
1918
- }
1919
-
1920
- console.log("🔧 Adding esbuild.drop ...");
1921
-
1922
- // If esbuild block exists
1923
- if (/esbuild\s*:\s*{/.test(viteContent)) {
1924
- viteContent = viteContent.replace(
1925
- /esbuild\s*:\s*{([\s\S]*?)(^ {0,8})}/m,
1926
- (full, inner, indent) => {
1927
-
1928
- let lines = inner
1929
- .split("\n")
1930
- .map(l => l.trim())
1931
- .filter(Boolean);
1932
-
1933
- // Fix last comma
1934
- if (lines.length > 0) {
1935
- lines[lines.length - 1] =
1936
- lines[lines.length - 1].replace(/,+$/, "") + ",";
1937
- }
1938
-
1939
- // Re-indent
1940
- lines = lines.map(l => indent + " " + l);
1941
-
1942
- // Add drop
1943
- lines.push(`${indent} drop: ['console','debugger'],`);
1944
-
1945
- return `esbuild: {\n${lines.join("\n")}\n${indent}}`;
1946
- }
1947
- );
1948
- }
1949
-
1950
- // If esbuild missing
1951
- else {
1952
- viteContent = viteContent.replace(
1953
- /export default defineConfig\s*\(\s*{/,
1954
- m => `${m}\n esbuild: {\n drop: ['console','debugger'],\n },`
1955
- );
1956
- }
1957
-
1958
- fs.writeFileSync(viteConfigPath, viteContent, "utf8");
1959
- console.log("✅ vite.config.(m)js Updated successfully.");
1960
- };
1961
-
1962
-
1963
-
1964
-
1965
-
1966
-
1967
-
1968
-
1969
-
1970
-
1971
- const compareVersion = (v1, v2) => {
1972
- const a = v1.split(".").map(Number);
1973
- const b = v2.split(".").map(Number);
1974
-
1975
- for (let i = 0; i < Math.max(a.length, b.length); i++) {
1976
- const num1 = a[i] || 0;
1977
- const num2 = b[i] || 0;
1978
- if (num1 > num2) return 1;
1979
- if (num1 < num2) return -1;
1980
- }
1981
- return 0;
1982
- };
1983
-
1984
-
1985
-
1986
-
1987
- const admobConfigPath = path.join('src', 'js','Ads', 'admob-ad-configuration.json');
1988
-
1989
- const checkAdmobConfigurationProperty=()=>{
1990
-
1991
-
1992
- if (!_admobConfig.ADMOB_ENABLED)
1993
- {
1994
- console.log("ℹ️ Admob is not enabled so 'admob-ad-configuration.json' checking is skipping...");
1995
- return;
1996
- }
1997
-
1998
-
1999
- const REQUIRED_CONFIG_KEYS = [
2000
- "isKidsApp",
2001
- "isTesting",
2002
- "isConsoleLogEnabled",
2003
- "bannerEnabled",
2004
- "interstitialEnabled",
2005
- "appOpenEnabled",
2006
- "rewardVideoEnabled",
2007
- "rewardInterstitialEnabled",
2008
- "collapsibleEnabled",
2009
- "isLandScape",
2010
- "isOverlappingEnable",
2011
- "bannerTypeAndroid",
2012
- "bannerTypeiOS",
2013
- "bannerTopSpaceColor",
2014
- "interstitialLoadScreenTextColor",
2015
- "interstitialLoadScreenBackgroundColor",
2016
- "beforeBannerSpace",
2017
- "whenShow",
2018
- "minimumClick",
2019
- "interstitialTimeOut",
2020
- "interstitialFirstTimeOut",
2021
- "appOpenAdsTimeOut",
2022
- "maxRetryCount",
2023
- "retrySecondsAr",
2024
- "appOpenPerSession",
2025
- "interstitialPerSession",
2026
- "appOpenFirstTimeOut"
2027
- ];
2028
-
2029
-
2030
-
2031
-
2032
-
2033
- let admobConfigInJson;
2034
-
2035
- try {
2036
- admobConfigInJson = JSON.parse(readFileSync(admobConfigPath, "utf8"));
2037
- } catch (err) {
2038
- console.error("❌ Failed to read admob-ad-configuration.json", err);
2039
- process.exit(1);
2040
- }
2041
-
2042
- // ✅ Validate config object exists
2043
- if (!admobConfigInJson.config) {
2044
- console.error('❌ "config" object is missing in admob-ad-configuration.json');
2045
- process.exit(1);
2046
- }
2047
-
2048
-
2049
- const admobConfigMinVersion="1.5"
2050
-
2051
- if (compareVersion(admobConfigInJson.VERSION, admobConfigMinVersion) < 0) {
2052
- console.error(`❌ Please use at-least version ${admobConfigMinVersion} in "src/js/Ads/admob-ad-configuration.json"`);
2053
- process.exit(1);
2054
- }
2055
-
2056
-
2057
- const config = admobConfigInJson.config;
2058
-
2059
- // Find missing properties
2060
- const missingKeys = REQUIRED_CONFIG_KEYS.filter(
2061
- key => !(key in config)
2062
- );
2063
-
2064
-
2065
-
2066
- if (missingKeys.length > 0) {
2067
- console.error("❌ Missing required configuration keys. Please check it in 'src/js/Ads/admob-ad-configuration.json'");
2068
-
2069
- missingKeys.forEach(k => console.error(" - " + k));
2070
- process.exit(1);
2071
- }
2072
-
2073
-
2074
- console.log(' All keys exist. in "admob-ad-configuration.json file" Configuration looks good.');
2075
- }
2076
-
2077
-
2078
-
2079
- function ensureGitignoreEntry(entry) {
2080
- const gitignorePath = path.join(process.cwd(), '.gitignore');
2081
-
2082
- // If .gitignore doesn't exist, create it
2083
- if (!fs.existsSync(gitignorePath)) {
2084
- fs.writeFileSync(gitignorePath, `${entry}\n`, 'utf8');
2085
- console.log(`✅ .gitignore created and added: ${entry}`);
2086
- return;
2087
- }
2088
-
2089
- const content = fs.readFileSync(gitignorePath, 'utf8');
2090
-
2091
- // Normalize lines (trim + remove trailing slashes for comparison)
2092
- const lines = content
2093
- .split(/\r?\n/)
2094
- .map(l => l.trim());
2095
-
2096
- const normalizedEntry = entry.replace(/\/$/, '');
2097
-
2098
- const exists = lines.some(
2099
- line => line.replace(/\/$/, '') === normalizedEntry
2100
- );
2101
-
2102
- if (exists) {
2103
- console.log(`ℹ️ .gitignore already contains: ${entry}`);
2104
- return;
2105
- }
2106
-
2107
- // Ensure file ends with newline
2108
- const separator = content.endsWith('\n') ? '' : '\n';
2109
-
2110
- fs.appendFileSync(gitignorePath, `${separator}${entry}\n`, 'utf8');
2111
- console.log(`✅ Added to .gitignore: ${entry}`);
2112
- }
2113
-
2114
-
2115
- ensureGitignoreEntry('buildCodeplay/');
2116
-
2117
-
2118
- // Run the validation
2119
- (async () => {
2120
-
2121
- await loadPluginVersions(); // 🔥 NEW
2122
-
2123
- await checkPlugins();
2124
- checkAndupdateDropInViteConfig();
2125
- checkAdmobConfigurationProperty()
2126
- })();
2127
-
2128
-
2129
- // ======================================================
2130
- // Validate theme folder location (src/js/theme is NOT allowed)
2131
- // ======================================================
2132
-
2133
- function validateThemeFolderLocation() {
2134
- const oldThemePath = path.join(process.cwd(), 'src', 'js', 'theme');
2135
- const newThemePath = path.join(process.cwd(), 'src', 'theme');
2136
-
2137
- // ❌ Block old structure
2138
- if (fs.existsSync(oldThemePath)) {
2139
- console.error(
2140
- '\n❌ INVALID PROJECT STRUCTURE DETECTED\n' +
2141
- '--------------------------------------------------\n' +
2142
- 'The "theme" folder must NOT be inside:\n' +
2143
- ' src/js/theme\n\n' +
2144
- '✅ Correct location is:\n' +
2145
- ' src/theme\n\n' +
2146
- '🛑 Please move the folder and re-run the build.\n'
2147
- );
2148
- process.exit(1);
2149
- }
2150
-
2151
- // ⚠️ Optional warning if new theme folder is missing
2152
- if (!fs.existsSync(newThemePath)) {
2153
- console.warn(
2154
- '\n⚠️ WARNING: "src/theme" folder not found.\n' +
2155
- 'If your app uses themes, please ensure it exists.\n'
2156
- );
2157
- } else {
2158
- console.log('✅ Theme folder structure validated (src/theme).');
2159
- }
2160
- }
2161
- validateThemeFolderLocation()
2162
-
2163
-
2164
-
2165
- const validateAndRestoreSignDetails=()=>{
2166
-
2167
- // Read config file
2168
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
2169
-
2170
- // Ensure android and buildOptions exist
2171
- if (!config.android) config.android = {};
2172
- if (!config.android.buildOptions) config.android.buildOptions = {};
2173
-
2174
- // Update only if changed
2175
- let updated = false;
2176
-
2177
- if (config.android.buildOptions.releaseType !== 'AAB') {
2178
- config.android.buildOptions.releaseType = 'AAB';
2179
- updated = true;
2180
- }
2181
-
2182
- if (config.android.buildOptions.signingType !== 'jarsigner') {
2183
- config.android.buildOptions.signingType = 'jarsigner';
2184
- updated = true;
2185
- }
2186
-
2187
- // Write back only if modified
2188
- if (updated) {
2189
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
2190
- console.log('capacitor.config.json updated successfully.');
2191
- } else {
2192
- console.log('No changes needed.');
2193
- }
2194
-
2195
- }
2196
-
2197
- validateAndRestoreSignDetails()
2198
-
2199
-
2200
- execSync('node buildCodeplay/fix-onesignal-plugin.js', { stdio: 'inherit' });
2201
-
2202
-
2203
-
2204
-
2205
-
2206
-
2207
-
2208
- //################################## SystemBars.java update for "@capacitor/android": "^8.3.0" plugin START ###############################
2209
-
2210
-
2211
- const filePath = path.join(
2212
- __dirname,
2213
- "../node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java"
2214
- );
2215
-
2216
- // 🔍 OLD BLOCK (anchor)
2217
- const OLD_BLOCK = `if (shouldPassthroughInsets) {
2218
- // We need to correct for a possible shown IME
2219
- v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
2220
-
2221
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {
2222
- Insets safeAreaInsets = calcSafeAreaInsets(insets);
2223
- injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
2224
- }
2225
-
2226
- return new WindowInsetsCompat.Builder(insets)
2227
- .setInsets(
2228
- WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(),
2229
- Insets.of(
2230
- systemBarsInsets.left,
2231
- systemBarsInsets.top,
2232
- systemBarsInsets.right,
2233
- getBottomInset(systemBarsInsets, keyboardVisible)
2234
- )
2235
- )
2236
- .build();
2237
- }`;
2238
-
2239
- // ✅ NEW BLOCK
2240
- const NEW_BLOCK = `if (shouldPassthroughInsets) {
2241
- /* 🔴 ORIGINAL CODE (COMMENTED FOR SAFETY)
2242
- ${OLD_BLOCK.split("\n").map(line => " " + line).join("\n")}
2243
- */
2244
-
2245
- // ✅ NEW LOGIC (CUSTOM FIX)
2246
- v.setPadding(0, 0, 0, 0);
2247
-
2248
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {
2249
- Insets safeAreaInsets = calcSafeAreaInsets(insets);
2250
- injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
2251
- }
2252
-
2253
- return insets; // WebView handles everything
2254
- }`;
2255
-
2256
- // 🚨 ERROR MESSAGE
2257
- const ERROR_MSG = `
2258
- ❌ Capacitor SystemBars.java structure changed!
2259
-
2260
- Plugin: @capacitor/android
2261
- Path : node_modules\@capacitor\android\capacitor\src\main\java\com\getcapacitor\plugin\SystemBars.java
2262
-
2263
- Check version of "@capacitor/android": in package.json
2264
-
2265
- 👉 Expected code block not found.
2266
-
2267
- This usually means Capacitor updated internally.
2268
-
2269
- Please:
2270
- 1. Open SystemBars.java
2271
- 2. Update patch script
2272
- 3. Re-run build
2273
-
2274
- ⛔ Build stopped.
2275
- `;
2276
-
2277
- function patchFile() {
2278
- if (!fs.existsSync(filePath)) {
2279
- console.error("❌ SystemBars.java not found!");
2280
- process.exit(1);
2281
- }
2282
-
2283
- let content = fs.readFileSync(filePath, "utf8");
2284
-
2285
- // ✅ Already patched?
2286
- if (content.includes("🔴 ORIGINAL CODE (COMMENTED FOR SAFETY)")) {
2287
- console.log(" Already SystemBars.java patched. Skipping...");
2288
- return;
2289
- }
2290
-
2291
- // 🔍 Check old block exists
2292
- if (!content.includes(OLD_BLOCK)) {
2293
- console.error(ERROR_MSG);
2294
- process.exit(1);
2295
- }
2296
-
2297
- // 🔁 Replace
2298
- const updated = content.replace(OLD_BLOCK, NEW_BLOCK);
2299
-
2300
- fs.writeFileSync(filePath, updated, "utf8");
2301
-
2302
- console.log("SystemBars.java patched (comment + new logic)!");
2303
- }
2304
-
2305
- patchFile();
2306
-
2307
- //################################## SystemBars.java update for "@capacitor/android": "^8.3.0" plugin END ###############################
2308
-
2309
-
2310
-
2311
-
2312
- /*
2313
- Release Notes
2314
-
2315
- 5.1
2316
- Kotlin version update is commented. Previously admob is not worked if not update the kotlin version to higher version
2317
-
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const plist = require('plist');
4
+
5
+
6
+
7
+ const { execSync } = require("child_process");
8
+
9
+
10
+
11
+
12
+ const { readFileSync } = require("fs");
13
+
14
+ const ENABLE_AUTO_UPDATE = true;
15
+ const USE_LIVE_SERVER_VERSION = true;
16
+
17
+ const configPath = path.join(process.cwd(), 'capacitor.config.json');
18
+ const updateLogFile = path.join(process.cwd(), "", "plugin-update-log.txt");
19
+
20
+ // Expected plugin list with minimum versions
21
+ /* const requiredPlugins = [
22
+
23
+ { pattern: /backbutton-(\d+\.\d+)\.js$/, minVersion: '2.0', required: true, baseDir: 'js',mandatoryUpdate: true},
24
+
25
+
26
+ { pattern: /common-(\d+\.\d+)(?:-beta-(\d+))?\.js$/, minVersion: '6.0', required: true, baseDir: 'js',mandatoryUpdate: true },
27
+
28
+ { pattern: /localization_settings-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
29
+ { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: true },
30
+ { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js',mandatoryUpdate: false },
31
+ { pattern: /localNotification_AppSettings-(\d+\.\d+)\.js$/, minVersion: '1.0', required: true, baseDir: 'js',mandatoryUpdate: false },
32
+ { pattern: /onesignal-(\d+\.\d+)\.js$/, minVersion: '2.3', required: true, baseDir: 'js',mandatoryUpdate: false },
33
+ { pattern: /saveToGalleryAndSaveAnyFile-(\d+\.\d+)(-ios)?\.js$/, minVersion: '3.1', required: true, baseDir: 'js',mandatoryUpdate: true },
34
+ { pattern: /Ads[\/\\]admob-emi-(\d+\.\d+)\.js$/, minVersion: '3.7', required: true, baseDir: 'js',mandatoryUpdate: true },
35
+
36
+ // New added plugins
37
+ { pattern: /video-player-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: false },
38
+ { pattern: /image-cropper-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
39
+ { pattern: /common-(\d+\.\d+)\.less$/, minVersion: '1.6', required: true, baseDir: 'assets/css',mandatoryUpdate: false },
40
+
41
+
42
+ // New folders
43
+ { pattern: /IAP-(\d+\.\d+)$/, minVersion: '2.8', isFolder: true , required: true, baseDir: 'js/Ads',mandatoryUpdate: true },
44
+ { pattern: /editor-(\d+\.\d+)$/, minVersion: '1.9', isFolder: true, required: true, baseDir: 'js',mandatoryUpdate: true },
45
+ { pattern: /ffmpeg-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true, required: true, baseDir: 'js',mandatoryUpdate: true },
46
+ { pattern: /theme-(\d+\.\d+)$/, minVersion: '3.3', isFolder: true , required: true, baseDir: 'theme',mandatoryUpdate: true },
47
+
48
+
49
+ { pattern: /certificatejs-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true , required: true, baseDir: 'certificate',mandatoryUpdate: true }
50
+
51
+ ]; */
52
+
53
+
54
+ const ROOT_DIR = path.join(__dirname, "..", "src");
55
+
56
+ function requireOrInstall(packageName) {
57
+ try {
58
+ return require(packageName);
59
+ } catch (err) {
60
+
61
+ console.log(`📦 "${packageName}" not found. Installing automatically...`);
62
+
63
+ try {
64
+ execSync(`npm install ${packageName}`, { stdio: "inherit" });
65
+ console.log(`✅ "${packageName}" installed successfully.`);
66
+ } catch (installErr) {
67
+ console.error(`❌ Failed to install "${packageName}".`);
68
+ process.exit(1);
69
+ }
70
+
71
+ // Try loading again
72
+ return require(packageName);
73
+ }
74
+ }
75
+
76
+ const AdmZip = requireOrInstall("adm-zip");
77
+
78
+
79
+
80
+ const pkg = require(path.join(process.cwd(), 'node_modules', 'codeplay-common', 'package.json'));
81
+
82
+ const pluginName = pkg.name;
83
+ const pluginVersion = pkg.version;
84
+
85
+ let updateLogs = [];
86
+
87
+ function writeUpdateLine(message) {
88
+ updateLogs.push(message);
89
+ }
90
+
91
+ const MAX_LOG_BLOCKS = 50;
92
+
93
+ function saveUpdateLogs() {
94
+
95
+ if (updateLogs.length === 0) return;
96
+
97
+ const logDir = path.dirname(updateLogFile);
98
+
99
+ if (!fs.existsSync(logDir)) {
100
+ fs.mkdirSync(logDir, { recursive: true });
101
+ }
102
+
103
+ const now = new Date();
104
+
105
+ const formattedTime = now.toLocaleString('en-GB', {
106
+ day: '2-digit',
107
+ month: '2-digit',
108
+ year: 'numeric',
109
+ hour: '2-digit',
110
+ minute: '2-digit',
111
+ hour12: true
112
+ }).replace(',', '').replace(/\//g, '-');
113
+
114
+ let newBlock = `${pluginName}: ${pluginVersion}\n[${formattedTime}]\n`;
115
+
116
+ updateLogs.forEach(line => {
117
+ newBlock += `${line}\n`;
118
+ });
119
+
120
+ newBlock += "\n";
121
+
122
+ let existingLog = "";
123
+
124
+ if (fs.existsSync(updateLogFile)) {
125
+ existingLog = fs.readFileSync(updateLogFile, "utf8");
126
+ }
127
+
128
+ let combinedLog = newBlock + existingLog;
129
+
130
+ // Split blocks by plugin header
131
+ const blocks = combinedLog.split(/\n(?=codeplay-common:)/);
132
+
133
+ // Keep only latest 50
134
+ const trimmed = blocks.slice(0, MAX_LOG_BLOCKS).join("\n");
135
+
136
+ fs.writeFileSync(updateLogFile, trimmed);
137
+
138
+ }
139
+
140
+
141
+
142
+
143
+
144
+ const versionsFile = path.join(__dirname, "versions.json");
145
+
146
+ function loadRequiredPlugins() {
147
+
148
+ if (!fs.existsSync(versionsFile)) {
149
+ console.error("❌ versions.json not found");
150
+ process.exit(1);
151
+ }
152
+
153
+ const json = JSON.parse(fs.readFileSync(versionsFile, "utf8"));
154
+
155
+ return json.plugins.map(p => ({
156
+ ...p,
157
+ pattern: new RegExp(p.pattern)
158
+ }));
159
+
160
+ }
161
+
162
+ let requiredPlugins = loadRequiredPlugins();
163
+
164
+
165
+
166
+
167
+
168
+
169
+ async function downloadAndExtractZip(url, destFolder) {
170
+
171
+ const zipPath = destFolder + ".zip";
172
+
173
+ await downloadFile(url, zipPath);
174
+
175
+ const zip = new AdmZip(zipPath);
176
+ zip.extractAllTo(destFolder, true);
177
+
178
+ fs.unlinkSync(zipPath);
179
+
180
+ }
181
+
182
+
183
+
184
+
185
+
186
+
187
+
188
+ //Check codeplay-common latest version installed or not Start
189
+ //const { execSync } = require('child_process');
190
+
191
+
192
+ function getInstalledVersion(packageName) {
193
+ try {
194
+ const packageJsonPath = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
195
+ if (fs.existsSync(packageJsonPath)) {
196
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
197
+ return packageJson.version;
198
+ }
199
+ } catch (error) {
200
+ return null;
201
+ }
202
+ return null;
203
+ }
204
+
205
+ function getLatestVersion(packageName) {
206
+ try {
207
+ return execSync(`npm view ${packageName} version`).toString().trim();
208
+ } catch (error) {
209
+ console.error(`Failed to fetch latest version for ${packageName}`);
210
+ return null;
211
+ }
212
+ }
213
+
214
+ function checkPackageVersion() {
215
+ const packageName = 'codeplay-common';
216
+ const installedVersion = getInstalledVersion(packageName);
217
+ const latestVersion = getLatestVersion(packageName);
218
+
219
+ if (!installedVersion) {
220
+ console.error(`${packageName} is not installed. Please install it using "npm install ${packageName}".`);
221
+ process.exit(1);
222
+ }
223
+
224
+ if (installedVersion !== latestVersion) {
225
+ console.error(`\x1b[31m${packageName} is outdated (installed: ${installedVersion}, latest: ${latestVersion}). Please update it.\x1b[0m\n\x1b[33mUse 'npm uninstall codeplay-common ; npm i codeplay-common'\x1b[0m`);
226
+ process.exit(1);
227
+ }
228
+
229
+ console.log(`${packageName} is up to date (version ${installedVersion}).`);
230
+ }
231
+
232
+ // Run package version check before executing the main script
233
+ try {
234
+ checkPackageVersion();
235
+ } catch (error) {
236
+ console.error(error.message);
237
+ process.exit(1);
238
+ }
239
+
240
+ //Check codeplay-common latest version installed or not END
241
+
242
+
243
+
244
+ function compareWithBeta(installedVersion, minVersion, isBeta) {
245
+ const baseCompare = compareVersions(installedVersion, minVersion);
246
+
247
+ if (!isBeta) {
248
+ // Stable version → normal compare
249
+ return baseCompare;
250
+ }
251
+
252
+ // Beta version logic
253
+ if (baseCompare > 0) return 1; // 5.3-beta > 5.2
254
+ if (baseCompare < 0) return -1; // 5.1-beta < 5.2
255
+
256
+ // Same version but beta → LOWER than stable
257
+ return -1; // 5.2-beta < 5.2
258
+ }
259
+
260
+
261
+
262
+
263
+ const checkAppUniqueId=()=>{
264
+
265
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
266
+
267
+ const appUniqueId = config.android?.APP_UNIQUE_ID;
268
+ const RESIZEABLE_ACTIVITY = config.android?.RESIZEABLE_ACTIVITY;
269
+ const orientation = config.android?.ORIENTATION;
270
+
271
+
272
+ let logErrorMessage="";
273
+
274
+ // 1️⃣ Check if it’s missing
275
+ if (RESIZEABLE_ACTIVITY === undefined) {
276
+ logErrorMessage+='❌ Missing android.RESIZEABLE_ACTIVITY option in capacitor.config.json.\n';
277
+ }
278
+
279
+ // 2️⃣ Check if it’s not boolean (true/false only)
280
+ else if (typeof RESIZEABLE_ACTIVITY !== 'boolean') {
281
+ logErrorMessage+='❌ Invalid android.RESIZEABLE_ACTIVITY value. Please use only true or false (without quotes).\n';
282
+ }
283
+
284
+
285
+
286
+ if (!orientation) {
287
+ logErrorMessage+='❌ Missing android.ORIENTATION option in capacitor.config.json.\n';
288
+ }
289
+
290
+ else if(orientation!="portrait" && orientation!="landscape" && orientation!="auto")
291
+ {
292
+ logErrorMessage+='❌ Spelling mistake in android.ORIENTATION option in capacitor.config.json. Please use only ["portrait" "landscape" "auto"]\n';
293
+ }
294
+
295
+
296
+ if (!appUniqueId) {
297
+ logErrorMessage+='❌ APP_UNIQUE_ID is missing in capacitor.config.json.';
298
+ }
299
+
300
+ else if (!Number.isInteger(appUniqueId)) {
301
+ logErrorMessage+='❌ APP_UNIQUE_ID must be an integer. Example: 1, 2, 3, etc.';
302
+ }
303
+
304
+
305
+
306
+ if(logErrorMessage!="")
307
+ {
308
+ console.error(logErrorMessage);
309
+ process.exit(1)
310
+ }
311
+
312
+
313
+ console.log(`✅ APP_UNIQUE_ID is valid: ${appUniqueId}`);
314
+
315
+ }
316
+
317
+ checkAppUniqueId();
318
+
319
+
320
+
321
+
322
+
323
+
324
+ // ======================================================
325
+ // 🚫 BLOCK STATIC IMPORT OF showSubscribePopup (ANY VERSION)
326
+ // ======================================================
327
+
328
+ const STATIC_SUBSCRIBE_REGEX = /import\s*{\s*showSubscribePopup\s*}\s*from\s*['"].*\/js\/Ads\/IAP-\d+(\.\d+)*\/IAP-check-And-LoadAd\.js['"]/;
329
+
330
+ // Detect dynamic import (valid)
331
+ const DYNAMIC_SUBSCRIBE_REGEX = /await\s+import\s*\(\s*['"].*\/js\/Ads\/IAP-\d+(\.\d+)*\/IAP-check-And-LoadAd\.js['"]\s*\)/;
332
+
333
+ let subscribeImportError = false;
334
+
335
+ function scanSubscribeImport(dir) {
336
+ const files = fs.readdirSync(dir);
337
+
338
+ for (const file of files) {
339
+ const fullPath = path.join(dir, file);
340
+ const stat = fs.statSync(fullPath);
341
+
342
+ if (stat.isDirectory()) {
343
+ scanSubscribeImport(fullPath);
344
+ }
345
+ else if (file.endsWith(".js") || file.endsWith(".ts") || file.endsWith(".f7")) {
346
+
347
+ const content = fs.readFileSync(fullPath, "utf-8");
348
+ const lines = content.split("\n");
349
+
350
+ lines.forEach((line, index) => {
351
+
352
+ // ❌ STATIC import → ERROR
353
+ if (STATIC_SUBSCRIBE_REGEX.test(line)) {
354
+ console.error(`
355
+ ❌ Forbidden static import detected!
356
+
357
+ File: ${fullPath}
358
+ Line: ${index + 1}
359
+ Code: ${line.trim()}
360
+
361
+ 🚫 DO NOT use static import for showSubscribePopup
362
+
363
+ 👉 Remove:
364
+ import { showSubscribePopup } from './../js/Ads/IAP-x.x/IAP-check-And-LoadAd.js'
365
+
366
+ ✅ Use dynamic import (ANY version allowed):
367
+
368
+ const { showSubscribePopup } = await import('./../js/Ads/IAP-x.x/IAP-check-And-LoadAd.js');
369
+ showSubscribePopup();
370
+ `);
371
+ subscribeImportError = true;
372
+ }
373
+
374
+ // Optional: detect wrong dynamic usage (no await)
375
+ if (
376
+ line.includes("import(") &&
377
+ line.includes("IAP-") &&
378
+ !line.includes("await")
379
+ ) {
380
+ console.warn(`
381
+ ⚠️ Warning: Dynamic import without await
382
+
383
+ File: ${fullPath}
384
+ Line: ${index + 1}
385
+ Code: ${line.trim()}
386
+
387
+ 👉 Always use:
388
+ const { showSubscribePopup } = await import(...)
389
+ `);
390
+ }
391
+
392
+ });
393
+ }
394
+ }
395
+ }
396
+
397
+
398
+ // Run scan
399
+ scanSubscribeImport(ROOT_DIR);
400
+
401
+ // Stop build
402
+ if (subscribeImportError) {
403
+ console.error("🚫 Build failed due to forbidden showSubscribePopup static import.");
404
+ process.exit(1);
405
+ } else {
406
+ console.log("✅ showSubscribePopup import usage is valid.");
407
+ }
408
+
409
+
410
+
411
+
412
+ // ======================================================
413
+ // 🚫 BLOCK STATIC IMPORT OF showSubscribePopup (ANY VERSION) END
414
+ // ======================================================
415
+
416
+
417
+
418
+
419
+
420
+
421
+
422
+
423
+
424
+
425
+
426
+ //@Codemirror check and install/uninstall the packages START
427
+ //const fs = require("fs");
428
+ //const path = require("path");
429
+ //const { execSync } = require("child_process");
430
+
431
+ const jsDir = path.join(__dirname, "..", "src", "js");
432
+
433
+ // 🔍 Detect OLD structure
434
+ const oldEditorDirs = fs.readdirSync(jsDir)
435
+ .filter(name => /^editor-\d+\.\d+$/.test(name));
436
+
437
+ // 📁 New structure path (optional, not mandatory)
438
+ const newEditorBaseDir = path.join(jsDir, "editor");
439
+
440
+ // ======================================================
441
+ // CASE 1: OLD STRUCTURE FOUND → STOP
442
+ // ======================================================
443
+ if (oldEditorDirs.length > 0) {
444
+
445
+ console.error(`
446
+ ❌ OLD EDITOR STRUCTURE DETECTED
447
+
448
+ You are using outdated folder structure:
449
+ src/js/editor-x.x/
450
+
451
+ 🚨 This is no longer supported.
452
+
453
+ 📦 Found folders:
454
+ ${oldEditorDirs.map(d => " - " + d).join("\n")}
455
+
456
+ 👉 Please move them manually:
457
+
458
+ src/js/editor-1.6
459
+
460
+ src/js/editor/editor-1.6
461
+
462
+ ⚠️ Also ensure:
463
+ src/js/editor/configuration.json exists
464
+
465
+ ❌ Build stopped.
466
+ `);
467
+
468
+ process.exit(1);
469
+ }
470
+
471
+ // ======================================================
472
+ // ✅ CASE 2: NEW STRUCTURE EXISTS → VALIDATE
473
+ // ======================================================
474
+ if (fs.existsSync(newEditorBaseDir)) {
475
+
476
+ // 🔍 Find editor-x.x inside new folder
477
+ const editorDirs = fs.readdirSync(newEditorBaseDir)
478
+ .filter(name => /^editor-\d+\.\d+$/.test(name));
479
+
480
+ // 👉 If editor folder exists but no versions → skip safely
481
+ if (editorDirs.length === 0) {
482
+ console.log("ℹ️ No editor-x.x folders found inside src/js/editor/");
483
+ return;
484
+ }
485
+
486
+ // ======================================================
487
+ // ✅ Validate configuration.json (NEW RULE)
488
+ // ======================================================
489
+
490
+ const editorConfigPath = path.join(newEditorBaseDir, "configuration.json");
491
+
492
+ // ❌ Missing config
493
+ if (!fs.existsSync(editorConfigPath)) {
494
+ console.error(`
495
+ ❌ MISSING EDITOR CONFIGURATION FILE
496
+
497
+ Required:
498
+ src/js/editor/configuration.json
499
+
500
+ Build stopped.
501
+ `);
502
+ process.exit(1);
503
+ }
504
+
505
+ // Check wrong placement
506
+ const invalidConfigs = [];
507
+
508
+ editorDirs.forEach(dir => {
509
+ const wrongPath = path.join(newEditorBaseDir, dir, "configuration.json");
510
+ if (fs.existsSync(wrongPath)) {
511
+ invalidConfigs.push(`src/js/editor/${dir}/configuration.json`);
512
+ }
513
+ });
514
+
515
+ if (invalidConfigs.length > 0) {
516
+ console.error(`
517
+ INVALID CONFIGURATION LOCATION
518
+
519
+ 🚫 configuration.json must NOT be inside version folders.
520
+
521
+ Found:
522
+ ${invalidConfigs.map(p => " - " + p).join("\n")}
523
+
524
+ ✅ Correct:
525
+ src/js/editor/configuration.json
526
+
527
+ Build stopped.
528
+ `);
529
+ process.exit(1);
530
+ }
531
+
532
+ console.log(" Editor structure validated.");
533
+
534
+ // ======================================================
535
+ // 🚀 Continue execution (run.js)
536
+ // ======================================================
537
+
538
+ const latestEditorDir = editorDirs.sort((a, b) => {
539
+ const vA = parseFloat(a.split('-')[1]);
540
+ const vB = parseFloat(b.split('-')[1]);
541
+ return vB - vA;
542
+ })[0];
543
+
544
+ const runJsPath = path.join(newEditorBaseDir, latestEditorDir, "run.js");
545
+
546
+ if (!fs.existsSync(runJsPath)) {
547
+ console.error(`❌ run.js not found in ${latestEditorDir}`);
548
+ process.exit(1);
549
+ }
550
+
551
+ console.log(`🚀 Executing ${runJsPath}...`);
552
+ execSync(`node "${runJsPath}"`, { stdio: "inherit" });
553
+ }
554
+
555
+ // ======================================================
556
+ // CASE 3: NOTHING EXISTS → DO NOTHING
557
+ // ======================================================
558
+ else {
559
+ console.log("ℹ️ Editor not used in this project. Skipping...");
560
+ }
561
+
562
+ //@Codemirror check and install/uninstall the packages END
563
+
564
+
565
+
566
+
567
+
568
+
569
+
570
+
571
+ const localizationBaseDir = path.join(__dirname, "..", "src", "js", "localization");
572
+
573
+ // ======================================================
574
+ // 🌐 LOCALIZATION CHECK (FULLY DYNAMIC)
575
+ // ======================================================
576
+
577
+
578
+ const jsRootDir = path.join(__dirname, "..", "src", "js");
579
+
580
+ // 🔍 Detect OLD localization files in root js/
581
+ const oldLocalizationFiles = fs.readdirSync(jsRootDir)
582
+ .filter(name =>
583
+ /^localization-\d+(\.\d+)*\.js$/.test(name) ||
584
+ /^localization_settings-\d+(\.\d+)*\.js$/.test(name)
585
+ );
586
+
587
+ // If old structure found → STOP
588
+ if (oldLocalizationFiles.length > 0) {
589
+
590
+ console.error(`
591
+ OLD LOCALIZATION STRUCTURE DETECTED
592
+
593
+ You are using outdated file structure:
594
+ src/js/localization-x.x.js
595
+ src/js/localization_settings-x.x.js
596
+
597
+ 🚨 This is no longer supported.
598
+
599
+ 📦 Found files:
600
+ ${oldLocalizationFiles.map(f => " - " + f).join("\n")}
601
+
602
+ 👉 Please move them to new structure:
603
+
604
+ src/js/localization/localization_settings-x.x.js
605
+ src/js/localization/localization-x.x/
606
+
607
+ ⚠️ Example:
608
+
609
+ OLD:
610
+ src/js/localization-x.x.js
611
+ src/js/localization_settings-x.x.js
612
+
613
+ NEW:
614
+ src/js/localization/localization_settings-1.1.js
615
+ src/js/localization/localization-x.x/localization-x.x.js
616
+
617
+ ❌ Build stopped.
618
+ `);
619
+
620
+ process.exit(1);
621
+ }
622
+
623
+
624
+ if (fs.existsSync(localizationBaseDir)) {
625
+
626
+ // 🔍 Find localization-x.x folders
627
+ const localizationVersions = fs.readdirSync(localizationBaseDir)
628
+ .filter(name => /^localization-\d+(\.\d+)+$/.test(name));
629
+
630
+ if (localizationVersions.length === 0) {
631
+ console.log("ℹ️ No localization-x.x folder found. Skipping...");
632
+ } else {
633
+
634
+ // 🔥 Get latest version (supports 1.6, 1.6.1, etc.)
635
+ const latestLocalizationDir = localizationVersions.sort((a, b) => {
636
+ const vA = a.split('-')[1].split('.').map(Number);
637
+ const vB = b.split('-')[1].split('.').map(Number);
638
+
639
+ for (let i = 0; i < Math.max(vA.length, vB.length); i++) {
640
+ const diff = (vB[i] || 0) - (vA[i] || 0);
641
+ if (diff !== 0) return diff;
642
+ }
643
+ return 0;
644
+ })[0];
645
+
646
+ const localizationPath = path.join(localizationBaseDir, latestLocalizationDir);
647
+
648
+ // ======================================================
649
+ // 🔍 Find settings file dynamically
650
+ // ======================================================
651
+
652
+ const settingsFile = fs.readdirSync(localizationBaseDir)
653
+ .find(name => /^localization_settings-.*\.js$/.test(name));
654
+
655
+ if (!settingsFile) {
656
+ console.error(`
657
+ ❌ MISSING LOCALIZATION SETTINGS FILE
658
+
659
+ Expected:
660
+ src/js/localization/localization_settings-x.x.js
661
+
662
+ Build stopped.
663
+ `);
664
+ process.exit(1);
665
+ }
666
+
667
+ // ======================================================
668
+ // 🔍 Find run.js INSIDE version folder
669
+ // ======================================================
670
+
671
+ const runJsPath = path.join(localizationPath, "run.js");
672
+
673
+ if (!fs.existsSync(runJsPath)) {
674
+ console.error(`
675
+ ❌ LOCALIZATION run.js NOT FOUND
676
+
677
+ Expected:
678
+ ${runJsPath}
679
+
680
+ Build stopped.
681
+ `);
682
+ process.exit(1);
683
+ }
684
+
685
+ console.log(`🌐 Localization detected: ${latestLocalizationDir}`);
686
+ console.log(`⚙️ Using settings: ${settingsFile}`);
687
+ console.log(`🚀 Executing: ${runJsPath}`);
688
+
689
+ // ======================================================
690
+ // 🚀 Execute run.js
691
+ // ======================================================
692
+
693
+ execSync(`node "${runJsPath}"`, { stdio: "inherit" });
694
+ }
695
+
696
+ } else {
697
+ console.log("ℹ️ Localization not used in this project. Skipping...");
698
+ }
699
+
700
+
701
+
702
+
703
+
704
+
705
+
706
+
707
+
708
+
709
+
710
+
711
+
712
+
713
+
714
+
715
+
716
+
717
+
718
+ //editor-x.x import old style check and stop execution START
719
+
720
+
721
+
722
+ // Match: editor/editor-2.3, editor/editor-2.3.1, etc.
723
+ const FORBIDDEN_REGEX = /editor\/editor-\d+(\.\d+)+/;
724
+
725
+ let hasError = false;
726
+
727
+ const ERROR_MESSAGE = `const ERROR_MESSAGE = ❌ Invalid import detected!
728
+
729
+ You are using a direct version-based path like: editor/editor-x.x/editor.js
730
+
731
+ 🚫 This is NOT allowed.
732
+
733
+ 👉 Please use the proper alias or updated import method.
734
+ Example: import { ... } from '@editor'
735
+
736
+ ⚠️ Do not use version-based paths in imports.
737
+ 👉 Please add this manually in vite.config.js:
738
+
739
+ alias: {
740
+ '@editor': path.resolve(__dirname, './src/js/editor/editor-x.x')
741
+ }`
742
+
743
+ function scanDir(dir) {
744
+ const files = fs.readdirSync(dir);
745
+
746
+ for (const file of files) {
747
+ const fullPath = path.join(dir, file);
748
+ const stat = fs.statSync(fullPath);
749
+
750
+ if (stat.isDirectory()) {
751
+ scanDir(fullPath);
752
+ } else if (file.endsWith(".js") || file.endsWith(".ts") || file.endsWith(".f7")) {
753
+ const content = fs.readFileSync(fullPath, "utf-8");
754
+
755
+ const lines = content.split("\n");
756
+
757
+ lines.forEach((line, index) => {
758
+ if (FORBIDDEN_REGEX.test(line)) {
759
+ console.error(
760
+ `❌ Forbidden import found:\nFile: ${fullPath}\nLine: ${index + 1}\nCode: ${line.trim()}\n`,
761
+ ERROR_MESSAGE
762
+ );
763
+ hasError = true;
764
+ }
765
+ });
766
+ }
767
+ }
768
+ }
769
+
770
+ // Run scan
771
+ scanDir(ROOT_DIR);
772
+
773
+ // Throw error (exit process)
774
+ if (hasError) {
775
+ console.error("🚫 Build failed due to forbidden editor imports.");
776
+ process.exit(1);
777
+ } else {
778
+ console.log("✅ No forbidden imports found.");
779
+ }
780
+
781
+
782
+
783
+
784
+
785
+
786
+ //editor-x.x import old style check and stop execution START
787
+
788
+
789
+
790
+
791
+
792
+
793
+
794
+
795
+
796
+
797
+ // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists START
798
+
799
+ const os = require('os');
800
+
801
+ const saveToGalleryAndSaveFileCheck_iOS = () => {
802
+
803
+ // List of paths to scan
804
+ const SCAN_PATHS = [
805
+ path.resolve(__dirname, '../src/certificate'),
806
+ path.resolve(__dirname, '../src/pages'),
807
+ path.resolve(__dirname, '../src/js'),
808
+ path.resolve(__dirname, '../src/app.f7')
809
+ ];
810
+
811
+ // Directory to exclude
812
+ const EXCLUDED_DIR = path.resolve(__dirname, '../src/js/Ads');
813
+
814
+ const ANDROID_MANIFEST_PATH = path.resolve(__dirname, '../android/app/src/main/AndroidManifest.xml');
815
+
816
+
817
+ // Match iOS-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5-ios.js) not in comments
818
+ const IOS_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*-ios\.js['"]/m;
819
+
820
+ // Match Android-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5.js) not in comments
821
+ const ANDROID_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*\.js['"]/m;
822
+
823
+
824
+
825
+
826
+
827
+ const ALLOWED_EXTENSIONS = ['.js', '.f7'];
828
+ const isMac = os.platform() === 'darwin';
829
+
830
+ let iosImportFound = false;
831
+ let androidImportFound = false;
832
+
833
+ // Files to skip completely (full or partial match)
834
+ const SKIP_FILES = [
835
+ 'pdf-3.11.174.min.js',
836
+ 'pdf.worker-3.11.174.min.js'
837
+ ,'index.browser.js'
838
+ ];
839
+
840
+
841
+ function scanDirectory(dir) {
842
+
843
+ /*
844
+ //######################### DO NOT DELETE THIS - START [Appid base validation] #####################################
845
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
846
+ const appUniqueId = config.android?.APP_UNIQUE_ID;
847
+ if (appUniqueId == "206") return;
848
+ //######################### DO NOT DELETE THIS - END [Appid base validation] #####################################
849
+ */
850
+
851
+ const stat = fs.statSync(dir);
852
+
853
+ if (stat.isFile()) {
854
+
855
+ // 🔥 Skip files in SKIP_FILES array
856
+ const baseName = path.basename(dir);
857
+ if (SKIP_FILES.includes(baseName)) {
858
+ // Just skip silently
859
+ return;
860
+ }
861
+
862
+ // Only scan allowed file extensions
863
+ if (ALLOWED_EXTENSIONS.some(ext => dir.endsWith(ext))) {
864
+ process.stdout.write(`\r🔍 Scanning: ${dir} `);
865
+
866
+ const content = fs.readFileSync(dir, 'utf8');
867
+
868
+ if (IOS_FILE_REGEX.test(content)) {
869
+ iosImportFound = true;
870
+ if (!isMac) {
871
+ console.error(`\n❌ ERROR: iOS-specific import detected in: ${dir}`);
872
+ console.error(`🚫 STOPPED: This file should not be imported in Android/Windows/Linux builds.\n`);
873
+ process.exit(1);
874
+ }
875
+ }
876
+ else if (ANDROID_FILE_REGEX.test(content) && !content.includes('-ios.js')) {
877
+ androidImportFound = true;
878
+ }
879
+ }
880
+ }
881
+ else if (stat.isDirectory()) {
882
+ if (dir === EXCLUDED_DIR || path.basename(dir) === 'node_modules') return;
883
+
884
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
885
+ for (let entry of entries) {
886
+ scanDirectory(path.join(dir, entry.name));
887
+ }
888
+ }
889
+ }
890
+
891
+
892
+ // Run scan on all specified paths
893
+ for (let scanPath of SCAN_PATHS) {
894
+ if (fs.existsSync(scanPath)) {
895
+ scanDirectory(scanPath);
896
+ }
897
+ }
898
+
899
+
900
+
901
+ /* // Check src folder
902
+ if (!fs.existsSync(ROOT_DIR)) {
903
+ console.warn(`⚠️ Warning: 'src' directory not found at: ${ROOT_DIR}`);
904
+ return;
905
+ } */
906
+
907
+ //scanDirectory(ROOT_DIR);
908
+
909
+ // iOS Checks
910
+ if (isMac && !iosImportFound) {
911
+ console.warn(`⚠️ WARNING: You're on macOS but no iOS version (saveToGalleryAndSaveAnyFile-x.x-ios.js) found.`);
912
+ process.exit(1);
913
+ } else if (isMac && iosImportFound) {
914
+ console.log('✅ iOS version detected for macOS build.');
915
+ } else if (!iosImportFound) {
916
+ console.log(' No iOS-specific imports detected for non-macOS.');
917
+ }
918
+
919
+ // Android Checks
920
+ if (androidImportFound) {
921
+ console.log("📱 Android version of saveToGalleryAndSaveAnyFile detected. Checking AndroidManifest.xml...");
922
+
923
+ if (!fs.existsSync(ANDROID_MANIFEST_PATH)) {
924
+ console.error("❌ AndroidManifest.xml not found. Cannot add requestLegacyExternalStorage attribute.");
925
+ return;
926
+ }
927
+
928
+ let manifestContent = fs.readFileSync(ANDROID_MANIFEST_PATH, 'utf8');
929
+
930
+ if (!manifestContent.includes('android:requestLegacyExternalStorage="true"')) {
931
+ console.log("Adding android:requestLegacyExternalStorage=\"true\" to <application> tag...");
932
+
933
+ manifestContent = manifestContent.replace(
934
+ /<application([^>]*)>/,
935
+ (match, attrs) => {
936
+ if (attrs.includes('android:requestLegacyExternalStorage')) return match;
937
+ return `<application${attrs} android:requestLegacyExternalStorage="true">`;
938
+ }
939
+ );
940
+
941
+ fs.writeFileSync(ANDROID_MANIFEST_PATH, manifestContent, 'utf8');
942
+ console.log("✅ android:requestLegacyExternalStorage=\"true\" added successfully.");
943
+ } else {
944
+ console.log("ℹ️ android:requestLegacyExternalStorage already exists in AndroidManifest.xml.");
945
+ }
946
+ } else {
947
+ console.log("✅ No Android saveToGalleryAndSaveAnyFile imports detected.");
948
+ }
949
+ };
950
+
951
+ saveToGalleryAndSaveFileCheck_iOS();
952
+ // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists END
953
+
954
+
955
+
956
+
957
+
958
+
959
+
960
+
961
+
962
+
963
+
964
+
965
+
966
+ /*
967
+ // Clean up AppleDouble files (._*) created by macOS START
968
+ if (process.platform === 'darwin') {
969
+ try {
970
+ console.log('🧹 Cleaning up AppleDouble files (._*)...');
971
+ execSync(`find . -name '._*' -delete`);
972
+ console.log('✅ AppleDouble files removed.');
973
+ } catch (err) {
974
+ console.warn('⚠️ Failed to remove AppleDouble files:', err.message);
975
+ }
976
+ } else {
977
+ console.log('ℹ️ Skipping AppleDouble cleanup not a macOS machine.');
978
+ }
979
+
980
+ // Clean up AppleDouble files (._*) created by macOS END
981
+ */
982
+
983
+
984
+
985
+
986
+
987
+
988
+ //In routes.js file check static import START
989
+
990
+ const routesPath = path.join(process.cwd(), 'src', 'js', 'routes.js');
991
+ const routesContent = fs.readFileSync(routesPath, 'utf-8');
992
+
993
+ let inBlockComment = false;
994
+ const lines = routesContent.split('\n');
995
+
996
+ const allowedImport = `import HomePage from '../pages/home.f7';`;
997
+ const badImportRegex = /^[ \t]*import\s+[\w{}*,\s]*\s+from\s+['"].+\.f7['"]\s*;/;
998
+ const badImports = [];
999
+
1000
+ lines.forEach((line, index) => {
1001
+ const trimmed = line.trim();
1002
+
1003
+ // Handle block comment start and end
1004
+ if (trimmed.startsWith('/*')) inBlockComment = true;
1005
+ if (inBlockComment && trimmed.endsWith('*/')) {
1006
+ inBlockComment = false;
1007
+ return;
1008
+ }
1009
+
1010
+ // Skip if inside block comment or line comment
1011
+ if (inBlockComment || trimmed.startsWith('//')) return;
1012
+
1013
+ // Match static .f7 import
1014
+ if (badImportRegex.test(trimmed) && trimmed !== allowedImport) {
1015
+ badImports.push({ line: trimmed, number: index + 1 });
1016
+ }
1017
+ });
1018
+
1019
+ if (badImports.length > 0) {
1020
+ console.error('\n❌ ERROR: Detected disallowed static imports of .f7 files in routes.js\n');
1021
+ console.error(`⚠️ Only this static import is allowed:\n ${allowedImport}\n`);
1022
+ console.error(`🔧 Please convert other imports to async dynamic imports like this:\n`);
1023
+ console.error(`
1024
+
1025
+ import HomePage from '../pages/home.f7';
1026
+
1027
+ const routes = [
1028
+ {
1029
+ path: '/',
1030
+ component:HomePage,
1031
+ },
1032
+ {
1033
+ path: '/ProfilePage/',
1034
+ async async({ resolve }) {
1035
+ const page = await import('../pages/profile.f7');
1036
+ resolve({ component: page.default });
1037
+ },
1038
+ }]
1039
+ `);
1040
+
1041
+ badImports.forEach(({ line, number }) => {
1042
+ console.error(`${number}: ${line}`);
1043
+ });
1044
+
1045
+ process.exit(1);
1046
+ } else {
1047
+ console.log('✅ routes.js passed the .f7 import check.');
1048
+ }
1049
+
1050
+ //In routes.js file check static import END
1051
+
1052
+
1053
+
1054
+
1055
+
1056
+
1057
+
1058
+
1059
+
1060
+
1061
+
1062
+
1063
+
1064
+ // Check and change the "BridgeWebViewClient.java" file START
1065
+ /*
1066
+ For crash issue due to low memory problem, we need to modify the onRenderProcessGone method in BridgeWebViewClient.java.
1067
+ */
1068
+
1069
+
1070
+ const bridgeWebViewClientFilePath = path.join(process.cwd(), 'node_modules', '@capacitor/android/capacitor/src/main/java/com/getcapacitor', 'BridgeWebViewClient.java');
1071
+
1072
+ // Read the file
1073
+ if (!fs.existsSync(bridgeWebViewClientFilePath)) {
1074
+ console.error('❌ Error: BridgeWebViewClient.java not found.');
1075
+ process.exit(1);
1076
+ }
1077
+
1078
+ let fileContent = fs.readFileSync(bridgeWebViewClientFilePath, 'utf8');
1079
+
1080
+ // Define old and new code
1081
+ const oldCodeStart = `@Override
1082
+ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
1083
+ super.onRenderProcessGone(view, detail);
1084
+ boolean result = false;
1085
+
1086
+ List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
1087
+ if (webViewListeners != null) {
1088
+ for (WebViewListener listener : bridge.getWebViewListeners()) {
1089
+ result = listener.onRenderProcessGone(view, detail) || result;
1090
+ }
1091
+ }
1092
+
1093
+ return result;
1094
+ }`;
1095
+
1096
+ const newCode = `@Override
1097
+ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
1098
+ super.onRenderProcessGone(view, detail);
1099
+
1100
+ boolean result = false;
1101
+
1102
+ List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
1103
+ if (webViewListeners != null) {
1104
+ for (WebViewListener listener : bridge.getWebViewListeners()) {
1105
+ result = listener.onRenderProcessGone(view, detail) || result;
1106
+ }
1107
+ }
1108
+
1109
+ if (!result) {
1110
+ // If no one handled it, handle it ourselves!
1111
+
1112
+ /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1113
+ if (detail.didCrash()) {
1114
+ //Log.e("CapacitorWebView", "WebView crashed internally!");
1115
+ } else {
1116
+ //Log.w("CapacitorWebView", "WebView was killed by system (low memory) internally!");
1117
+ }
1118
+ }*/
1119
+
1120
+ view.post(() -> {
1121
+ Toast.makeText(view.getContext(), "Reloading due to low memory issue", Toast.LENGTH_SHORT).show();
1122
+ });
1123
+
1124
+ view.reload(); // Safely reload WebView
1125
+
1126
+ return true; // We handled it
1127
+ }
1128
+
1129
+ return result;
1130
+ }`;
1131
+
1132
+ // Step 1: Update method if needed
1133
+ let updated = false;
1134
+
1135
+ if (fileContent.includes(oldCodeStart)) {
1136
+ console.log('✅ Found old onRenderProcessGone method. Replacing it...');
1137
+ fileContent = fileContent.replace(oldCodeStart, newCode);
1138
+ updated = true;
1139
+ } else if (fileContent.includes(newCode)) {
1140
+ console.log('ℹ️ Method already updated. No changes needed in "BridgeWebViewClient.java".');
1141
+ } else {
1142
+ console.error('❌ Error: Neither old nor new code found. Unexpected content.');
1143
+ process.exit(1);
1144
+ }
1145
+
1146
+ // Step 2: Check and add import if missing
1147
+ const importToast = 'import android.widget.Toast;';
1148
+ if (!fileContent.includes(importToast)) {
1149
+ console.log('✅ Adding missing import for Toast...');
1150
+ const importRegex = /import\s+[^;]+;/g;
1151
+ const matches = [...fileContent.matchAll(importRegex)];
1152
+
1153
+ if (matches.length > 0) {
1154
+ const lastImport = matches[matches.length - 1];
1155
+ const insertPosition = lastImport.index + lastImport[0].length;
1156
+ fileContent = fileContent.slice(0, insertPosition) + `\n${importToast}` + fileContent.slice(insertPosition);
1157
+ updated = true;
1158
+ } else {
1159
+ console.error('❌ Error: No import section found in file.');
1160
+ process.exit(1);
1161
+ }
1162
+ } else {
1163
+ console.log('ℹ️ Import for Toast already exists. No changes needed.');
1164
+ }
1165
+
1166
+ // Step 3: Save if updated
1167
+ if (updated) {
1168
+ fs.writeFileSync(bridgeWebViewClientFilePath, fileContent, 'utf8');
1169
+ console.log('✅ File updated successfully.');
1170
+ } else {
1171
+ console.log('ℹ️ No changes needed.');
1172
+ }
1173
+
1174
+
1175
+
1176
+
1177
+ // Check and change the "BridgeWebViewClient.java" file END
1178
+
1179
+
1180
+
1181
+
1182
+
1183
+
1184
+
1185
+
1186
+ /*
1187
+ // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file START
1188
+
1189
+ // Build the path dynamically like you requested
1190
+ const gradlePath = path.join(
1191
+ process.cwd(),
1192
+ 'android',
1193
+ 'build.gradle'
1194
+ );
1195
+
1196
+ // Read the existing build.gradle
1197
+ let gradleContent = fs.readFileSync(gradlePath, 'utf8');
1198
+
1199
+ // Add `ext.kotlin_version` if it's not already there
1200
+ if (!gradleContent.includes('ext.kotlin_version')) {
1201
+ gradleContent = gradleContent.replace(
1202
+ /buildscript\s*{/,
1203
+ `buildscript {\n ext.kotlin_version = '2.1.0'`
1204
+ );
1205
+ }
1206
+
1207
+ // Add Kotlin classpath if it's not already there
1208
+ if (!gradleContent.includes('org.jetbrains.kotlin:kotlin-gradle-plugin')) {
1209
+ gradleContent = gradleContent.replace(
1210
+ /dependencies\s*{([\s\S]*?)classpath 'com.android.tools.build:gradle:8.7.2'/,
1211
+ `dependencies {\n classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")\n$1classpath 'com.android.tools.build:gradle:8.7.2'`
1212
+ );
1213
+ }
1214
+
1215
+ // Write back the modified content
1216
+ fs.writeFileSync(gradlePath, gradleContent, 'utf8');
1217
+
1218
+ console.log('✅ Kotlin version updated in build.gradle.');
1219
+
1220
+ // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file END
1221
+ */
1222
+
1223
+
1224
+
1225
+
1226
+
1227
+
1228
+
1229
+
1230
+ let _admobConfig;
1231
+
1232
+
1233
+
1234
+ const androidPlatformPath = path.join(process.cwd(), 'android');
1235
+ const iosPlatformPath = path.join(process.cwd(), 'ios');
1236
+ const pluginPath = path.join(process.cwd(), 'node_modules', 'emi-indo-cordova-plugin-admob', 'plugin.xml');
1237
+ const infoPlistPath = path.join(process.cwd(), 'ios', 'App', 'App', 'Info.plist');
1238
+ const resourcesPath = path.join(process.cwd(), 'resources', 'res');
1239
+ const androidResPath = path.join(process.cwd(), 'android', 'app', 'src', 'main', 'res');
1240
+ const localNotificationsPluginPath = path.join(process.cwd(), 'node_modules', '@capacitor', 'local-notifications');
1241
+
1242
+ function fileExists(filePath) {
1243
+ return fs.existsSync(filePath);
1244
+ }
1245
+
1246
+ function copyFolderSync(source, target) {
1247
+ if (!fs.existsSync(target)) {
1248
+ fs.mkdirSync(target, { recursive: true });
1249
+ }
1250
+
1251
+ fs.readdirSync(source).forEach(file => {
1252
+ const sourceFile = path.join(source, file);
1253
+ const targetFile = path.join(target, file);
1254
+
1255
+ if (fs.lstatSync(sourceFile).isDirectory()) {
1256
+ copyFolderSync(sourceFile, targetFile);
1257
+ } else {
1258
+ fs.copyFileSync(sourceFile, targetFile);
1259
+ }
1260
+ });
1261
+ }
1262
+
1263
+ function checkAndCopyResources() {
1264
+ if (fileExists(resourcesPath)) {
1265
+ copyFolderSync(resourcesPath, androidResPath);
1266
+ console.log('✅ Successfully copied resources/res to android/app/src/main/res.');
1267
+ } else {
1268
+ console.log('resources/res folder not found.');
1269
+
1270
+ if (fileExists(localNotificationsPluginPath)) {
1271
+ throw new Error('❌ resources/res is required for @capacitor/local-notifications. Stopping execution.');
1272
+ }
1273
+ }
1274
+ }
1275
+
1276
+
1277
+
1278
+
1279
+
1280
+
1281
+
1282
+
1283
+ function getAdMobConfig() {
1284
+ if (!fileExists(configPath)) {
1285
+ throw new Error('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
1286
+ }
1287
+
1288
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
1289
+ const admobConfig = config.plugins?.AdMob;
1290
+
1291
+ if (!admobConfig) {
1292
+ throw new Error('❌ AdMob configuration is missing in capacitor.config.json.');
1293
+ }
1294
+
1295
+ // Default to true if ADMOB_ENABLED is not specified
1296
+ const isEnabled = admobConfig.ADMOB_ENABLED !== false;
1297
+
1298
+ if (!isEnabled) {
1299
+ return { ADMOB_ENABLED: false }; // Skip further validation
1300
+ }
1301
+
1302
+ if (!admobConfig.APP_ID_ANDROID || !admobConfig.APP_ID_IOS) {
1303
+ throw new Error(' ❌ AdMob configuration is incomplete. Ensure APP_ID_ANDROID and APP_ID_IOS are defined.');
1304
+ }
1305
+
1306
+ return {
1307
+ ADMOB_ENABLED: true,
1308
+ APP_ID_ANDROID: admobConfig.APP_ID_ANDROID,
1309
+ APP_ID_IOS: admobConfig.APP_ID_IOS,
1310
+ USE_LITE_ADS: admobConfig.USE_LITE_ADS === "lite",
1311
+ };
1312
+ }
1313
+
1314
+ function validateAndroidBuildOptions() {
1315
+
1316
+
1317
+ if (!fileExists(configPath)) {
1318
+ console.log('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
1319
+ process.exit(1);
1320
+ }
1321
+
1322
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
1323
+
1324
+ const targetAppId=config.appId
1325
+
1326
+ const buildOptions = config.android?.buildOptions;
1327
+
1328
+ if (!buildOptions) {
1329
+ console.log('❌ Missing android.buildOptions in capacitor.config.json.');
1330
+ process.exit(1);
1331
+ }
1332
+
1333
+ const requiredProps = [
1334
+ 'keystorePath',
1335
+ 'keystorePassword',
1336
+ 'keystoreAlias',
1337
+ 'keystoreAliasPassword',
1338
+ 'releaseType',
1339
+ 'signingType'
1340
+ ];
1341
+
1342
+ const missing = requiredProps.filter(prop => !buildOptions[prop]);
1343
+
1344
+ if (missing.length > 0) {
1345
+ console.log('❌ Missing properties android.buildOptions in capacitor.config.json.');
1346
+ process.exit(1);
1347
+ }
1348
+
1349
+
1350
+ const keystorePath=buildOptions.keystorePath
1351
+ const keyFileName = path.basename(keystorePath);
1352
+
1353
+
1354
+
1355
+ const keystoreMap = {
1356
+ "gameskey.jks": [
1357
+ "com.cube.blaster",
1358
+ ],
1359
+ "htmleditorkeystoke.jks": [
1360
+ "com.HTML.AngularJS.Codeplay",
1361
+ "com.html.codeplay.pro",
1362
+ "com.bootstrap.code.play",
1363
+ "com.kids.learning.master",
1364
+ "com.Simple.Barcode.Scanner",
1365
+ "com.FAShtmlcssjs.editor"
1366
+ ]
1367
+ };
1368
+
1369
+ // find which keystore is required for the given targetAppId
1370
+ let requiredKey = "newappskey.jks"; // default
1371
+ for (const [keyFile, appIds] of Object.entries(keystoreMap)) {
1372
+ if (appIds.includes(targetAppId)) {
1373
+ requiredKey = keyFile;
1374
+ break;
1375
+ }
1376
+ }
1377
+
1378
+ // validate
1379
+ if (keyFileName !== requiredKey) {
1380
+ console.log(`❌ The keystore path is mismatched. Expected ${requiredKey} for ${targetAppId}, but got ${keyFileName}`);
1381
+ process.exit(1);
1382
+ }
1383
+
1384
+
1385
+
1386
+
1387
+
1388
+ // optionally return them
1389
+ //return buildOptions;
1390
+ }
1391
+
1392
+ function updatePluginXml(admobConfig) {
1393
+ if (!fileExists(pluginPath)) {
1394
+ console.error(' ❌ plugin.xml not found. Ensure the plugin is installed.');
1395
+ return;
1396
+ }
1397
+
1398
+ let pluginContent = fs.readFileSync(pluginPath, 'utf8');
1399
+
1400
+ pluginContent = pluginContent
1401
+ .replace(/<preference name="APP_ID_ANDROID" default=".*?" \/>/, `<preference name="APP_ID_ANDROID" default="${admobConfig.APP_ID_ANDROID}" />`)
1402
+ .replace(/<preference name="APP_ID_IOS" default=".*?" \/>/, `<preference name="APP_ID_IOS" default="${admobConfig.APP_ID_IOS}" />`);
1403
+
1404
+ fs.writeFileSync(pluginPath, pluginContent, 'utf8');
1405
+ console.log('✅ AdMob IDs successfully updated in plugin.xml');
1406
+ }
1407
+
1408
+ function updateInfoPlist(admobConfig) {
1409
+ if (!fileExists(infoPlistPath)) {
1410
+ console.error(' ❌ Info.plist not found. Ensure you have built the iOS project.');
1411
+ return;
1412
+ }
1413
+
1414
+ const plistContent = fs.readFileSync(infoPlistPath, 'utf8');
1415
+ const plistData = plist.parse(plistContent);
1416
+
1417
+ plistData.GADApplicationIdentifier = admobConfig.APP_ID_IOS;
1418
+ plistData.NSUserTrackingUsageDescription = 'This identifier will be used to deliver personalized ads to you.';
1419
+ plistData.GADDelayAppMeasurementInit = true;
1420
+
1421
+ const updatedPlistContent = plist.build(plistData);
1422
+ fs.writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
1423
+ console.log('AdMob IDs and additional configurations successfully updated in Info.plist');
1424
+ }
1425
+
1426
+
1427
+ try {
1428
+ if (!fileExists(configPath)) {
1429
+ throw new Error(' capacitor.config.json not found. Skipping setup.');
1430
+ }
1431
+
1432
+ if (!fileExists(androidPlatformPath) && !fileExists(iosPlatformPath)) {
1433
+ throw new Error('Neither Android nor iOS platforms are found. Ensure platforms are added to your Capacitor project.');
1434
+ }
1435
+
1436
+ checkAndCopyResources();
1437
+
1438
+
1439
+
1440
+ _admobConfig = getAdMobConfig();
1441
+
1442
+
1443
+
1444
+
1445
+
1446
+ // Proceed only if ADMOB_ENABLED is true
1447
+ if (_admobConfig.ADMOB_ENABLED) {
1448
+ if (fileExists(androidPlatformPath)) {
1449
+ updatePluginXml(_admobConfig);
1450
+ }
1451
+
1452
+ if (fileExists(iosPlatformPath)) {
1453
+ updateInfoPlist(_admobConfig);
1454
+ }
1455
+ }
1456
+
1457
+
1458
+ } catch (error) {
1459
+ console.error(error.message);
1460
+ process.exit(1); // Stop execution if there's a critical error
1461
+ }
1462
+
1463
+
1464
+
1465
+ validateAndroidBuildOptions();
1466
+
1467
+
1468
+
1469
+
1470
+
1471
+
1472
+ // Check all the codeplays plugins version START
1473
+
1474
+
1475
+ const readline = require('readline');
1476
+
1477
+
1478
+ //const srcDir = path.join(__dirname, 'src');
1479
+ const srcDir = path.join(process.cwd(), 'src');
1480
+ let outdatedPlugins = [];
1481
+
1482
+ function parseVersion(ver) {
1483
+ return ver.split('.').map(n => parseInt(n, 10));
1484
+ }
1485
+
1486
+ function compareVersions(v1, v2) {
1487
+ const [a1, b1] = parseVersion(v1);
1488
+ const [a2, b2] = parseVersion(v2);
1489
+ if (a1 !== a2) return a1 - a2;
1490
+ return b1 - b2;
1491
+ }
1492
+
1493
+ function walkSync(dir, filelist = []) {
1494
+ fs.readdirSync(dir).forEach(file => {
1495
+ const fullPath = path.join(dir, file);
1496
+ const stat = fs.statSync(fullPath);
1497
+ if (stat.isDirectory()) {
1498
+ walkSync(fullPath, filelist);
1499
+ } else {
1500
+ filelist.push(fullPath);
1501
+ }
1502
+ });
1503
+ return filelist;
1504
+ }
1505
+
1506
+
1507
+
1508
+ function getSearchRoot(plugin) {
1509
+ return path.join(srcDir, plugin.baseDir || 'js');
1510
+ }
1511
+
1512
+
1513
+
1514
+
1515
+
1516
+
1517
+
1518
+
1519
+
1520
+
1521
+
1522
+
1523
+
1524
+
1525
+
1526
+
1527
+
1528
+
1529
+
1530
+
1531
+ /*############################################## AUTO DOWNLOAD FROM SERVER START #####################################*/
1532
+
1533
+ // ============================================================
1534
+ // 🔥 AUTO PLUGIN UPDATE SYSTEM (MANDATORY UPDATES)
1535
+ // ============================================================
1536
+
1537
+ const https = require("https");
1538
+
1539
+ /**
1540
+ * Check if file exists on server using HEAD request
1541
+ */
1542
+ function urlExists(url) {
1543
+ return new Promise(resolve => {
1544
+ const req = https.request(url, { method: "HEAD" }, res => {
1545
+ resolve(res.statusCode === 200);
1546
+ });
1547
+
1548
+ req.on("error", () => resolve(false));
1549
+ req.end();
1550
+ });
1551
+ }
1552
+
1553
+ /**
1554
+ * Download file from server
1555
+ */
1556
+ function downloadFile(url, dest) {
1557
+ return new Promise((resolve, reject) => {
1558
+ const file = fs.createWriteStream(dest);
1559
+
1560
+ https.get(url, response => {
1561
+ if (response.statusCode !== 200) {
1562
+ reject("Download failed");
1563
+ return;
1564
+ }
1565
+
1566
+ response.pipe(file);
1567
+
1568
+ file.on("finish", () => {
1569
+ file.close(resolve);
1570
+ });
1571
+ }).on("error", err => {
1572
+ fs.unlink(dest, () => {});
1573
+ reject(err);
1574
+ });
1575
+ });
1576
+ }
1577
+
1578
+ /**
1579
+ * Update imports across src folder
1580
+ * Replaces old filename → new filename
1581
+ */
1582
+ /**
1583
+ * Update imports across project
1584
+ * Replaces old filename → new filename
1585
+ */
1586
+
1587
+ const VITE_ALIAS_ONLY = [
1588
+ "common",
1589
+ "admob-emi",
1590
+ "localization",
1591
+ "theme",
1592
+ "certificatejs",
1593
+ "ffmpeg"
1594
+ ];
1595
+
1596
+ function updateImports(oldName, newName) {
1597
+
1598
+ const projectRoot = process.cwd();
1599
+
1600
+ const filesToScan = [
1601
+ path.join(projectRoot, "vite.config.js"),
1602
+ path.join(projectRoot, "vite.config.mjs")
1603
+ ];
1604
+
1605
+ const srcDir = path.join(projectRoot, "src");
1606
+
1607
+ // scan vite config
1608
+ filesToScan.forEach(file => {
1609
+
1610
+ if (!fs.existsSync(file)) return;
1611
+
1612
+ let content = fs.readFileSync(file, "utf8");
1613
+
1614
+ if (content.includes(oldName)) {
1615
+
1616
+ content = content.split(oldName).join(newName);
1617
+
1618
+ fs.writeFileSync(file, content);
1619
+
1620
+ console.log(`✏️ Updated alias in ${path.basename(file)}`);
1621
+ }
1622
+
1623
+ });
1624
+
1625
+ // scan src files
1626
+ function walk(dir) {
1627
+
1628
+ fs.readdirSync(dir).forEach(file => {
1629
+
1630
+ if (["node_modules","android","ios","dist",".git"].includes(file))
1631
+ return;
1632
+
1633
+ const full = path.join(dir, file);
1634
+ const stat = fs.statSync(full);
1635
+
1636
+ if (stat.isDirectory()) {
1637
+ walk(full);
1638
+ }
1639
+
1640
+ else if (
1641
+ (full.endsWith(".js") ||
1642
+ full.endsWith(".f7") ||
1643
+ full.endsWith(".mjs")) &&
1644
+ !full.endsWith(".min.js")
1645
+ ) {
1646
+
1647
+ let content = fs.readFileSync(full, "utf8");
1648
+
1649
+ if (content.includes(oldName)) {
1650
+
1651
+ content = content.split(oldName).join(newName);
1652
+
1653
+ fs.writeFileSync(full, content);
1654
+
1655
+ console.log(`✏️ Updated import in ${path.relative(projectRoot, full)}`);
1656
+ }
1657
+
1658
+ }
1659
+
1660
+ });
1661
+
1662
+ }
1663
+
1664
+ if (fs.existsSync(srcDir)) {
1665
+ walk(srcDir);
1666
+ }
1667
+
1668
+ }
1669
+
1670
+
1671
+ /**
1672
+ * Auto-update a plugin file
1673
+ * Returns TRUE if success
1674
+ * Returns FALSE if fallback to manual needed
1675
+ */
1676
+
1677
+
1678
+ let _serverVersions = null;
1679
+ async function fetchVersions() {
1680
+
1681
+ if (_serverVersions) return _serverVersions;
1682
+
1683
+ return new Promise((resolve) => {
1684
+
1685
+ https.get(
1686
+ "https://htmlcodeplay.com/code-play-plugin/versions.json",
1687
+ { timeout: 5000 },
1688
+ res => {
1689
+
1690
+ let data = "";
1691
+
1692
+ res.on("data", chunk => data += chunk);
1693
+
1694
+ res.on("end", () => {
1695
+
1696
+ try {
1697
+
1698
+ _serverVersions = JSON.parse(data);
1699
+
1700
+ resolve(_serverVersions);
1701
+
1702
+ } catch {
1703
+
1704
+ resolve(null);
1705
+
1706
+ }
1707
+
1708
+ });
1709
+
1710
+ }
1711
+
1712
+ ).on("error", () => resolve(null));
1713
+
1714
+ });
1715
+
1716
+ }
1717
+
1718
+
1719
+
1720
+ async function autoUpdatePlugin(pluginDef, pluginInfo) {
1721
+
1722
+ const versions = await fetchVersions();
1723
+
1724
+ if (!versions) {
1725
+ console.log("⚠️ versions.json not reachable");
1726
+ return false;
1727
+ }
1728
+
1729
+ const oldFullPath = path.join(srcDir, pluginInfo.name);
1730
+ const oldFileName = path.basename(oldFullPath);
1731
+
1732
+ //const oldFileName = path.basename(oldFullPath);
1733
+ const oldVersionFile = oldFileName;
1734
+
1735
+
1736
+ const ext = path.extname(oldFileName); // .js or .less
1737
+ //const baseName = oldFileName.replace(/-\d+\.\d+.*$/, "");
1738
+ const baseName = oldFileName.replace(/-\d+\.\d+.*$/, '').replace(/\.(js|less)$/, '');
1739
+
1740
+ // version lookup key
1741
+ let pluginKey = baseName;
1742
+
1743
+
1744
+ // Only common plugin has js and less variants
1745
+ if (baseName === "common") {
1746
+ if (ext === ".js") pluginKey = "common-js";
1747
+ if (ext === ".less") pluginKey = "common-less";
1748
+ }
1749
+
1750
+ const latestVersion = versions[pluginKey];
1751
+
1752
+ if (!latestVersion) {
1753
+ console.log(`❌ No version entry for ${baseName}`);
1754
+ return false;
1755
+ }
1756
+
1757
+ // ===============================
1758
+ // FOLDER PLUGIN UPDATE
1759
+ // ===============================
1760
+ if (pluginDef.isFolder) {
1761
+
1762
+ const zipName = `${baseName}-${latestVersion}.zip`;
1763
+ const url = `https://htmlcodeplay.com/code-play-plugin/${zipName}`;
1764
+
1765
+ const destRoot = path.join(srcDir, pluginDef.destDir || pluginDef.baseDir || '');
1766
+ const oldPath = path.join(destRoot, pluginInfo.name);
1767
+ const newPath = path.join(destRoot, `${baseName}-${latestVersion}`);
1768
+
1769
+ if (!(await urlExists(url))) return false;
1770
+
1771
+ fs.rmSync(oldPath, { recursive: true, force: true });
1772
+
1773
+ await downloadAndExtractZip(url, newPath);
1774
+
1775
+ updateImports(pluginInfo.name, `${baseName}-${latestVersion}`);
1776
+
1777
+ // ✅ ADD THIS
1778
+ writeUpdateLine(`${pluginInfo.name} -> ${baseName}-${latestVersion}`);
1779
+
1780
+ console.log(`✅ Folder updated → ${baseName}-${latestVersion}`);
1781
+
1782
+ return true;
1783
+ }
1784
+ // ===============================
1785
+ // FILE PLUGIN UPDATE
1786
+ // ===============================
1787
+
1788
+ const pluginDir = path.dirname(oldFullPath);
1789
+
1790
+ // Only this plugin has ios variant
1791
+ const IOS_VARIANT_PLUGINS = [
1792
+ "saveToGalleryAndSaveAnyFile"
1793
+ ];
1794
+
1795
+ /* let variants = [
1796
+ `${baseName}-${latestVersion}.js`
1797
+ ]; */
1798
+
1799
+ //const ext = path.extname(oldFileName);
1800
+ const variants = [`${baseName.replace(ext,'')}-${latestVersion}${ext}`];
1801
+
1802
+
1803
+ if (IOS_VARIANT_PLUGINS.includes(baseName)) {
1804
+ variants.push(`${baseName}-${latestVersion}-ios.js`);
1805
+ }
1806
+
1807
+ let downloaded = [];
1808
+
1809
+ // Download files
1810
+ for (const fileName of variants) {
1811
+
1812
+ const url = `https://htmlcodeplay.com/code-play-plugin/${fileName}`;
1813
+
1814
+ console.log(`🔍 Checking latest: ${fileName}`);
1815
+
1816
+ if (await urlExists(url)) {
1817
+
1818
+ const destPath = path.join(pluginDir, fileName);
1819
+
1820
+ await downloadFile(url, destPath);
1821
+
1822
+ downloaded.push(fileName);
1823
+
1824
+ console.log(`⬇ Downloaded → ${fileName}`);
1825
+
1826
+ //writeUpdateLine(`Downloaded: ${fileName}`);
1827
+
1828
+ }
1829
+ }
1830
+
1831
+ if (downloaded.length === 0) {
1832
+ console.log(`❌ No files downloaded for ${baseName}`);
1833
+ return false;
1834
+ }
1835
+
1836
+ // Remove ONLY versioned files (safe)
1837
+ //const versionPattern = new RegExp(`^${baseName}-\\d+\\.\\d+(-ios)?\\.js$`);
1838
+ const versionPattern = new RegExp(`^${baseName}-\\d+\\.\\d+(-ios)?\\${ext}$`);
1839
+
1840
+ const existingFiles = fs.readdirSync(pluginDir);
1841
+
1842
+ existingFiles.forEach(file => {
1843
+
1844
+ if (
1845
+ versionPattern.test(file) &&
1846
+ !downloaded.includes(file)
1847
+ ) {
1848
+
1849
+ const oldPath = path.join(pluginDir, file);
1850
+
1851
+ fs.unlinkSync(oldPath);
1852
+
1853
+ console.log(`🗑 Removed old file → ${file}`);
1854
+ //writeUpdateLine(`Removed old file: ${file}`);
1855
+ }
1856
+
1857
+ });
1858
+
1859
+ //const newFileName = `${baseName}-${latestVersion}.js`;
1860
+ const newFileName = `${baseName}-${latestVersion}${ext}`;
1861
+
1862
+ writeUpdateLine(`${oldVersionFile} -> ${newFileName}`);
1863
+
1864
+ updateImports(oldFileName, newFileName);
1865
+ //updateImports(baseName, `${baseName}-${latestVersion}.js`);
1866
+ //updateImports(pluginInfo.name, `${baseName}-${latestVersion}`);
1867
+
1868
+ //console.log(`✅ Updated ${newFileName}`);
1869
+ //console.log(`✅ Updated → ${baseName}-${latestVersion}`);
1870
+
1871
+ return true;
1872
+ }
1873
+
1874
+
1875
+
1876
+
1877
+ /*############################################## AUTO DOWNLOAD FROM SERVER END #####################################*/
1878
+
1879
+
1880
+
1881
+
1882
+
1883
+
1884
+
1885
+
1886
+
1887
+
1888
+
1889
+
1890
+
1891
+
1892
+
1893
+
1894
+
1895
+
1896
+ async function loadPluginVersions() {
1897
+
1898
+ if (!USE_LIVE_SERVER_VERSION) {
1899
+ console.log("ℹ️ Using local plugin versions (offline mode).");
1900
+ return;
1901
+ }
1902
+
1903
+ console.log("🌐 Fetching plugin versions from server...");
1904
+
1905
+ const versions = await fetchVersions();
1906
+
1907
+ if (!versions || typeof versions !== "object") {
1908
+ console.log("⚠️ Server unavailable or invalid versions.json. Falling back to local versions.");
1909
+ return;
1910
+ }
1911
+
1912
+ requiredPlugins.forEach(plugin => {
1913
+
1914
+ if (!plugin.name) return;
1915
+
1916
+ if (versions[plugin.name]) {
1917
+ plugin.minVersion = versions[plugin.name];
1918
+ }
1919
+
1920
+ });
1921
+
1922
+ console.log("✅ Plugin versions loaded from server.");
1923
+
1924
+ }
1925
+
1926
+
1927
+
1928
+ let hasMandatoryUpdate = false;
1929
+ function checkPlugins() {
1930
+ return new Promise(async (resolve, reject) => {
1931
+ const files = walkSync(srcDir);
1932
+ const outdatedPlugins = [];
1933
+ let hasMandatoryUpdate = false;
1934
+
1935
+ for (const plugin of requiredPlugins) {
1936
+ const searchRoot = getSearchRoot(plugin);
1937
+
1938
+ // ---------- Folder plugins ----------
1939
+ if (plugin.isFolder) {
1940
+ if (!fs.existsSync(searchRoot)) continue;
1941
+
1942
+ const subDirs = fs.readdirSync(searchRoot)
1943
+ .map(name => path.join(searchRoot, name))
1944
+ .filter(p => fs.statSync(p).isDirectory());
1945
+
1946
+ for (const dir of subDirs) {
1947
+ const relativePath = path.relative(searchRoot, dir).replace(/\\/g, '/');
1948
+ const match = plugin.pattern.exec(relativePath);
1949
+
1950
+ if (match) {
1951
+ const currentVersion = match[1];
1952
+
1953
+ if (compareVersions(currentVersion, plugin.minVersion) < 0) {
1954
+ outdatedPlugins.push({
1955
+ name: relativePath,
1956
+ currentVersion,
1957
+ requiredVersion: plugin.minVersion,
1958
+ mandatoryUpdate: plugin.mandatoryUpdate === true
1959
+ });
1960
+
1961
+ if (plugin.mandatoryUpdate) {
1962
+ hasMandatoryUpdate = true;
1963
+ }
1964
+ }
1965
+ }
1966
+ }
1967
+ continue;
1968
+ }
1969
+
1970
+ // ---------- File plugins ----------
1971
+ const matchedFile = files.find(file =>
1972
+ file.startsWith(searchRoot) && plugin.pattern.test(file)
1973
+ );
1974
+
1975
+ if (matchedFile) {
1976
+ const match = plugin.pattern.exec(matchedFile);
1977
+ if (match) {
1978
+ const currentVersion = match[1];
1979
+ const isBeta = !!match[2];
1980
+
1981
+ const cmp = plugin.pattern.source.includes('beta')
1982
+ ? compareWithBeta(currentVersion, plugin.minVersion, isBeta)
1983
+ : compareVersions(currentVersion, plugin.minVersion);
1984
+
1985
+ if (cmp < 0) {
1986
+ outdatedPlugins.push({
1987
+ name: path.relative(srcDir, matchedFile),
1988
+ currentVersion: isBeta ? `${currentVersion}-beta` : currentVersion,
1989
+ requiredVersion: plugin.minVersion,
1990
+ mandatoryUpdate: plugin.mandatoryUpdate === true
1991
+ });
1992
+
1993
+ if (plugin.mandatoryUpdate) {
1994
+ hasMandatoryUpdate = true;
1995
+ }
1996
+ }
1997
+ }
1998
+ }
1999
+ }
2000
+
2001
+ // ---------- Result handling ----------
2002
+ if (outdatedPlugins.length > 0) {
2003
+ console.log('\n❗ The following plugins are outdated:\n');
2004
+
2005
+ outdatedPlugins.forEach( p => {
2006
+ const tag = p.mandatoryUpdate ? '🔥 MANDATORY' : '';
2007
+ console.log(
2008
+ ` ⚠️ - ${p.name} (Current: ${p.currentVersion}, Required: ${p.requiredVersion}) ${tag}`
2009
+ );
2010
+ });
2011
+
2012
+ // 🚨 Mandatory update → stop build
2013
+ /* if (hasMandatoryUpdate) {
2014
+ console.log('\n🚫 One or more plugins require a mandatory update.');
2015
+ console.log('❌ Build cancelled. Please update mandatory plugins and try again.');
2016
+ process.exit(1);
2017
+ } */
2018
+
2019
+
2020
+
2021
+
2022
+
2023
+ if (hasMandatoryUpdate) {
2024
+
2025
+ //--------------------------------------------------
2026
+ // 🚫 AUTO UPDATE DISABLED
2027
+ //--------------------------------------------------
2028
+ if (!ENABLE_AUTO_UPDATE) {
2029
+ console.log("\n🚫 Auto-update disabled.");
2030
+ console.log("❌ Manual update required.");
2031
+ process.exit(1);
2032
+ }
2033
+
2034
+ //--------------------------------------------------
2035
+ // 🔥 AUTO UPDATE ENABLED
2036
+ //--------------------------------------------------
2037
+ console.log("\n🔥 Mandatory plugins outdated. Trying auto-update...\n");
2038
+
2039
+
2040
+
2041
+ let autoFailed = false;
2042
+
2043
+ for (const p of outdatedPlugins.filter(x => x.mandatoryUpdate)) {
2044
+
2045
+ const pluginDef = requiredPlugins.find(def =>
2046
+ def.pattern.test(p.name)
2047
+ );
2048
+
2049
+ if (!pluginDef) continue;
2050
+
2051
+ const success = await autoUpdatePlugin(
2052
+ pluginDef,
2053
+ p
2054
+ );
2055
+
2056
+ if (!success) {
2057
+ autoFailed = true;
2058
+
2059
+ const pluginDef = requiredPlugins.find(def =>
2060
+ def.pattern.test(p.name)
2061
+ );
2062
+
2063
+ console.log(`❌ Manual update required for ${p.name}`);
2064
+
2065
+ if (pluginDef) {
2066
+ console.log(`👉 Required minimum version: ${pluginDef.minVersion}`);
2067
+ }
2068
+ }
2069
+ }
2070
+
2071
+ // 🚨 Fallback to manual if any failed
2072
+ if (autoFailed) {
2073
+ console.log('\n🚫 One or more plugins require manual update.');
2074
+ console.log(' Build cancelled. Please update mandatory plugins.');
2075
+ process.exit(1);
2076
+ }
2077
+
2078
+ console.log('\n🎉 All mandatory plugins auto-updated! Rechecking plugins...\n');
2079
+
2080
+ // Re-run plugin check so outdated list becomes empty
2081
+ await checkPlugins();
2082
+ return;
2083
+ }
2084
+
2085
+
2086
+
2087
+
2088
+
2089
+
2090
+ // Optional updates → ask user
2091
+ const rl = readline.createInterface({
2092
+ input: process.stdin,
2093
+ output: process.stdout
2094
+ });
2095
+
2096
+ rl.question(
2097
+ '\nAre you sure you want to continue without updating these plugins? (y/n): ',
2098
+ answer => {
2099
+ rl.close();
2100
+
2101
+ if (answer.toLowerCase() !== 'y') {
2102
+ console.log('\n❌ Build cancelled due to outdated plugins.');
2103
+ process.exit(1);
2104
+ } else {
2105
+ console.log('\n✅ Continuing build...');
2106
+ resolve();
2107
+ }
2108
+ }
2109
+ );
2110
+ } else {
2111
+ console.log('✅ All plugin versions are up to date.');
2112
+ saveUpdateLogs();
2113
+ resolve();
2114
+ }
2115
+ });
2116
+ }
2117
+
2118
+
2119
+
2120
+
2121
+
2122
+
2123
+
2124
+
2125
+ // Check all the codeplays plugins version START
2126
+
2127
+
2128
+
2129
+
2130
+ // ====================================================================
2131
+ // AUTO-ADD esbuild.drop: ['console','debugger'] to vite.config.js / mjs
2132
+ // ====================================================================
2133
+
2134
+
2135
+
2136
+ const checkAndupdateDropInViteConfig = () => {
2137
+
2138
+ const possibleFiles = [
2139
+ "vite.config.js",
2140
+ "vite.config.mjs"
2141
+ ];
2142
+
2143
+ // Detect existing config file
2144
+ const viteConfigPath = possibleFiles
2145
+ .map(file => path.join(process.cwd(), file))
2146
+ .find(filePath => fs.existsSync(filePath));
2147
+
2148
+ if (!viteConfigPath) {
2149
+ console.warn("⚠️ No vite config found. Skipping.");
2150
+ return;
2151
+ }
2152
+
2153
+ //console.log("📄 Using:", viteConfigPath.split("/").pop());
2154
+
2155
+ let viteContent = fs.readFileSync(viteConfigPath, "utf8");
2156
+
2157
+ // Skip if already exists
2158
+ if (/drop\s*:\s*\[.*['"]console['"].*\]/.test(viteContent)) {
2159
+ console.log("ℹ️ vite.config.(m)js already Updated. Skipping...");
2160
+ return;
2161
+ }
2162
+
2163
+ console.log("🔧 Adding esbuild.drop ...");
2164
+
2165
+ // If esbuild block exists
2166
+ if (/esbuild\s*:\s*{/.test(viteContent)) {
2167
+ viteContent = viteContent.replace(
2168
+ /esbuild\s*:\s*{([\s\S]*?)(^ {0,8})}/m,
2169
+ (full, inner, indent) => {
2170
+
2171
+ let lines = inner
2172
+ .split("\n")
2173
+ .map(l => l.trim())
2174
+ .filter(Boolean);
2175
+
2176
+ // Fix last comma
2177
+ if (lines.length > 0) {
2178
+ lines[lines.length - 1] =
2179
+ lines[lines.length - 1].replace(/,+$/, "") + ",";
2180
+ }
2181
+
2182
+ // Re-indent
2183
+ lines = lines.map(l => indent + " " + l);
2184
+
2185
+ // Add drop
2186
+ lines.push(`${indent} drop: ['console','debugger'],`);
2187
+
2188
+ return `esbuild: {\n${lines.join("\n")}\n${indent}}`;
2189
+ }
2190
+ );
2191
+ }
2192
+
2193
+ // If esbuild missing
2194
+ else {
2195
+ viteContent = viteContent.replace(
2196
+ /export default defineConfig\s*\(\s*{/,
2197
+ m => `${m}\n esbuild: {\n drop: ['console','debugger'],\n },`
2198
+ );
2199
+ }
2200
+
2201
+ fs.writeFileSync(viteConfigPath, viteContent, "utf8");
2202
+ console.log("✅ vite.config.(m)js Updated successfully.");
2203
+ };
2204
+
2205
+
2206
+
2207
+
2208
+
2209
+
2210
+
2211
+
2212
+
2213
+
2214
+ const compareVersion = (v1, v2) => {
2215
+ const a = v1.split(".").map(Number);
2216
+ const b = v2.split(".").map(Number);
2217
+
2218
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
2219
+ const num1 = a[i] || 0;
2220
+ const num2 = b[i] || 0;
2221
+ if (num1 > num2) return 1;
2222
+ if (num1 < num2) return -1;
2223
+ }
2224
+ return 0;
2225
+ };
2226
+
2227
+
2228
+
2229
+
2230
+ const admobConfigPath = path.join('src', 'js','Ads', 'admob-ad-configuration.json');
2231
+
2232
+ const checkAdmobConfigurationProperty=()=>{
2233
+
2234
+
2235
+ if (!_admobConfig.ADMOB_ENABLED)
2236
+ {
2237
+ console.log("ℹ️ Admob is not enabled so 'admob-ad-configuration.json' checking is skipping...");
2238
+ return;
2239
+ }
2240
+
2241
+
2242
+ const REQUIRED_CONFIG_KEYS = [
2243
+ "isKidsApp",
2244
+ "isTesting",
2245
+ "isConsoleLogEnabled",
2246
+ "bannerEnabled",
2247
+ "interstitialEnabled",
2248
+ "appOpenEnabled",
2249
+ "rewardVideoEnabled",
2250
+ "rewardInterstitialEnabled",
2251
+ "collapsibleEnabled",
2252
+ "isLandScape",
2253
+ "isOverlappingEnable",
2254
+ "bannerTypeAndroid",
2255
+ "bannerTypeiOS",
2256
+ "bannerTopSpaceColor",
2257
+ "interstitialLoadScreenTextColor",
2258
+ "interstitialLoadScreenBackgroundColor",
2259
+ "beforeBannerSpace",
2260
+ "whenShow",
2261
+ "minimumClick",
2262
+ "interstitialTimeOut",
2263
+ "interstitialFirstTimeOut",
2264
+ "appOpenAdsTimeOut",
2265
+ "maxRetryCount",
2266
+ "retrySecondsAr",
2267
+ "appOpenPerSession",
2268
+ "interstitialPerSession",
2269
+ "appOpenFirstTimeOut"
2270
+ ];
2271
+
2272
+
2273
+
2274
+
2275
+
2276
+ let admobConfigInJson;
2277
+
2278
+ try {
2279
+ admobConfigInJson = JSON.parse(readFileSync(admobConfigPath, "utf8"));
2280
+ } catch (err) {
2281
+ console.error("❌ Failed to read admob-ad-configuration.json", err);
2282
+ process.exit(1);
2283
+ }
2284
+
2285
+ // ✅ Validate config object exists
2286
+ if (!admobConfigInJson.config) {
2287
+ console.error('❌ "config" object is missing in admob-ad-configuration.json');
2288
+ process.exit(1);
2289
+ }
2290
+
2291
+
2292
+ const admobConfigMinVersion="1.5"
2293
+
2294
+ if (compareVersion(admobConfigInJson.VERSION, admobConfigMinVersion) < 0) {
2295
+ console.error(`❌ Please use at-least version ${admobConfigMinVersion} in "src/js/Ads/admob-ad-configuration.json"`);
2296
+ process.exit(1);
2297
+ }
2298
+
2299
+
2300
+ const config = admobConfigInJson.config;
2301
+
2302
+ // Find missing properties
2303
+ const missingKeys = REQUIRED_CONFIG_KEYS.filter(
2304
+ key => !(key in config)
2305
+ );
2306
+
2307
+
2308
+
2309
+ if (missingKeys.length > 0) {
2310
+ console.error("❌ Missing required configuration keys. Please check it in 'src/js/Ads/admob-ad-configuration.json'");
2311
+
2312
+ missingKeys.forEach(k => console.error(" - " + k));
2313
+ process.exit(1);
2314
+ }
2315
+
2316
+
2317
+ console.log('✅ All keys exist. in "admob-ad-configuration.json file" Configuration looks good.');
2318
+ }
2319
+
2320
+
2321
+
2322
+ function ensureGitignoreEntry(entry) {
2323
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
2324
+
2325
+ // If .gitignore doesn't exist, create it
2326
+ if (!fs.existsSync(gitignorePath)) {
2327
+ fs.writeFileSync(gitignorePath, `${entry}\n`, 'utf8');
2328
+ console.log(`✅ .gitignore created and added: ${entry}`);
2329
+ return;
2330
+ }
2331
+
2332
+ const content = fs.readFileSync(gitignorePath, 'utf8');
2333
+
2334
+ // Normalize lines (trim + remove trailing slashes for comparison)
2335
+ const lines = content
2336
+ .split(/\r?\n/)
2337
+ .map(l => l.trim());
2338
+
2339
+ const normalizedEntry = entry.replace(/\/$/, '');
2340
+
2341
+ const exists = lines.some(
2342
+ line => line.replace(/\/$/, '') === normalizedEntry
2343
+ );
2344
+
2345
+ if (exists) {
2346
+ console.log(`ℹ️ .gitignore already contains: ${entry}`);
2347
+ return;
2348
+ }
2349
+
2350
+ // Ensure file ends with newline
2351
+ const separator = content.endsWith('\n') ? '' : '\n';
2352
+
2353
+ fs.appendFileSync(gitignorePath, `${separator}${entry}\n`, 'utf8');
2354
+ console.log(`✅ Added to .gitignore: ${entry}`);
2355
+ }
2356
+
2357
+
2358
+ ensureGitignoreEntry('buildCodeplay/');
2359
+
2360
+
2361
+ // Run the validation
2362
+ (async () => {
2363
+
2364
+ await loadPluginVersions(); // 🔥 NEW
2365
+
2366
+ await checkPlugins();
2367
+ checkAndupdateDropInViteConfig();
2368
+ checkAdmobConfigurationProperty()
2369
+ })();
2370
+
2371
+
2372
+ // ======================================================
2373
+ // Validate theme folder location (src/js/theme is NOT allowed)
2374
+ // ======================================================
2375
+
2376
+ function validateThemeFolderLocation() {
2377
+ const oldThemePath = path.join(process.cwd(), 'src', 'js', 'theme');
2378
+ const newThemePath = path.join(process.cwd(), 'src', 'theme');
2379
+
2380
+ // ❌ Block old structure
2381
+ if (fs.existsSync(oldThemePath)) {
2382
+ console.error(
2383
+ '\n❌ INVALID PROJECT STRUCTURE DETECTED\n' +
2384
+ '--------------------------------------------------\n' +
2385
+ 'The "theme" folder must NOT be inside:\n' +
2386
+ ' src/js/theme\n\n' +
2387
+ '✅ Correct location is:\n' +
2388
+ ' src/theme\n\n' +
2389
+ '🛑 Please move the folder and re-run the build.\n'
2390
+ );
2391
+ process.exit(1);
2392
+ }
2393
+
2394
+ // ⚠️ Optional warning if new theme folder is missing
2395
+ if (!fs.existsSync(newThemePath)) {
2396
+ console.warn(
2397
+ '\n⚠️ WARNING: "src/theme" folder not found.\n' +
2398
+ 'If your app uses themes, please ensure it exists.\n'
2399
+ );
2400
+ } else {
2401
+ console.log('✅ Theme folder structure validated (src/theme).');
2402
+ }
2403
+ }
2404
+ validateThemeFolderLocation()
2405
+
2406
+
2407
+
2408
+ const validateAndRestoreSignDetails=()=>{
2409
+
2410
+ // Read config file
2411
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
2412
+
2413
+ // Ensure android and buildOptions exist
2414
+ if (!config.android) config.android = {};
2415
+ if (!config.android.buildOptions) config.android.buildOptions = {};
2416
+
2417
+ // Update only if changed
2418
+ let updated = false;
2419
+
2420
+ if (config.android.buildOptions.releaseType !== 'AAB') {
2421
+ config.android.buildOptions.releaseType = 'AAB';
2422
+ updated = true;
2423
+ }
2424
+
2425
+ if (config.android.buildOptions.signingType !== 'jarsigner') {
2426
+ config.android.buildOptions.signingType = 'jarsigner';
2427
+ updated = true;
2428
+ }
2429
+
2430
+ // Write back only if modified
2431
+ if (updated) {
2432
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
2433
+ console.log('capacitor.config.json updated successfully.');
2434
+ } else {
2435
+ console.log('No changes needed.');
2436
+ }
2437
+
2438
+ }
2439
+
2440
+ validateAndRestoreSignDetails()
2441
+
2442
+
2443
+ execSync('node buildCodeplay/fix-onesignal-plugin.js', { stdio: 'inherit' });
2444
+
2445
+
2446
+
2447
+
2448
+
2449
+
2450
+
2451
+ //################################## SystemBars.java update for "@capacitor/android": "^8.3.0" plugin START ###############################
2452
+
2453
+
2454
+ const filePath = path.join(
2455
+ __dirname,
2456
+ "../node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java"
2457
+ );
2458
+
2459
+ // 🔍 OLD BLOCK (anchor)
2460
+ const OLD_BLOCK = `if (shouldPassthroughInsets) {
2461
+ // We need to correct for a possible shown IME
2462
+ v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
2463
+
2464
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {
2465
+ Insets safeAreaInsets = calcSafeAreaInsets(insets);
2466
+ injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
2467
+ }
2468
+
2469
+ return new WindowInsetsCompat.Builder(insets)
2470
+ .setInsets(
2471
+ WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(),
2472
+ Insets.of(
2473
+ systemBarsInsets.left,
2474
+ systemBarsInsets.top,
2475
+ systemBarsInsets.right,
2476
+ getBottomInset(systemBarsInsets, keyboardVisible)
2477
+ )
2478
+ )
2479
+ .build();
2480
+ }`;
2481
+
2482
+ // ✅ NEW BLOCK
2483
+ const NEW_BLOCK = `if (shouldPassthroughInsets) {
2484
+ /* 🔴 ORIGINAL CODE (COMMENTED FOR SAFETY)
2485
+ ${OLD_BLOCK.split("\n").map(line => " " + line).join("\n")}
2486
+ */
2487
+
2488
+ // ✅ NEW LOGIC (CUSTOM FIX)
2489
+ v.setPadding(0, 0, 0, 0);
2490
+
2491
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {
2492
+ Insets safeAreaInsets = calcSafeAreaInsets(insets);
2493
+ injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
2494
+ }
2495
+
2496
+ return insets; // WebView handles everything
2497
+ }`;
2498
+
2499
+ // 🚨 ERROR MESSAGE
2500
+ const ERROR_MSG = `
2501
+ ❌ Capacitor SystemBars.java structure changed!
2502
+
2503
+ Plugin: @capacitor/android
2504
+ Path : node_modules\@capacitor\android\capacitor\src\main\java\com\getcapacitor\plugin\SystemBars.java
2505
+
2506
+ Check version of "@capacitor/android": in package.json
2507
+
2508
+ 👉 Expected code block not found.
2509
+
2510
+ This usually means Capacitor updated internally.
2511
+
2512
+ Please:
2513
+ 1. Open SystemBars.java
2514
+ 2. Update patch script
2515
+ 3. Re-run build
2516
+
2517
+ ⛔ Build stopped.
2518
+ `;
2519
+
2520
+ function patchFile() {
2521
+ if (!fs.existsSync(filePath)) {
2522
+ console.error("❌ SystemBars.java not found!");
2523
+ process.exit(1);
2524
+ }
2525
+
2526
+ let content = fs.readFileSync(filePath, "utf8");
2527
+
2528
+ // ✅ Already patched?
2529
+ if (content.includes("🔴 ORIGINAL CODE (COMMENTED FOR SAFETY)")) {
2530
+ console.log("✅ Already SystemBars.java patched. Skipping...");
2531
+ return;
2532
+ }
2533
+
2534
+ // 🔍 Check old block exists
2535
+ if (!content.includes(OLD_BLOCK)) {
2536
+ console.error(ERROR_MSG);
2537
+ process.exit(1);
2538
+ }
2539
+
2540
+ // 🔁 Replace
2541
+ const updated = content.replace(OLD_BLOCK, NEW_BLOCK);
2542
+
2543
+ fs.writeFileSync(filePath, updated, "utf8");
2544
+
2545
+ console.log("✅ SystemBars.java patched (comment + new logic)!");
2546
+ }
2547
+
2548
+ patchFile();
2549
+
2550
+ //################################## SystemBars.java update for "@capacitor/android": "^8.3.0" plugin END ###############################
2551
+
2552
+
2553
+
2554
+
2555
+ /*
2556
+ Release Notes
2557
+
2558
+ 5.1
2559
+ Kotlin version update is commented. Previously admob is not worked if not update the kotlin version to higher version
2560
+
2318
2561
  */