codeplay-common 3.0.6 → 3.0.8

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,1739 +1,1925 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const plist = require('plist');
4
-
5
- const { readFileSync } = require("fs");
6
-
7
- const ENABLE_AUTO_UPDATE = true;
8
-
9
- const configPath = path.join(process.cwd(), 'capacitor.config.json');
10
-
11
- // Expected plugin list with minimum versions
12
- const requiredPlugins = [
13
-
14
- { pattern: /backbutton-(\d+\.\d+)\.js$/, minVersion: '2.0', required: true, baseDir: 'js',mandatoryUpdate: true},
15
-
16
- /*/common-(\d+\.\d+)\.js$/*/
17
- { pattern: /common-(\d+\.\d+)(?:-beta-(\d+))?\.js$/, minVersion: '5.9', required: true, baseDir: 'js',mandatoryUpdate: true },
18
-
19
- { pattern: /localization_settings-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
20
- { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: true },
21
- { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js',mandatoryUpdate: false },
22
- { pattern: /localNotification_AppSettings-(\d+\.\d+)\.js$/, minVersion: '1.0', required: true, baseDir: 'js',mandatoryUpdate: false },
23
- { pattern: /onesignal-(\d+\.\d+)\.js$/, minVersion: '2.3', required: true, baseDir: 'js',mandatoryUpdate: false },
24
- { pattern: /saveToGalleryAndSaveAnyFile-(\d+\.\d+)(-ios)?\.js$/, minVersion: '3.1', required: true, baseDir: 'js',mandatoryUpdate: true },
25
- { pattern: /Ads[\/\\]admob-emi-(\d+\.\d+)\.js$/, minVersion: '3.7', required: true, baseDir: 'js',mandatoryUpdate: true },
26
-
27
- // New added plugins
28
- { pattern: /video-player-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: false },
29
- { pattern: /image-cropper-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
30
- { pattern: /common-(\d+\.\d+)\.less$/, minVersion: '1.6', required: true, baseDir: 'assets/css',mandatoryUpdate: false },
31
-
32
-
33
- // New folders
34
- { pattern: /IAP-(\d+\.\d+)$/, minVersion: '2.8', isFolder: true , required: true, baseDir: 'js/Ads',mandatoryUpdate: true },
35
- { pattern: /editor-(\d+\.\d+)$/, minVersion: '1.9', isFolder: true, required: true, baseDir: 'js',mandatoryUpdate: true },
36
- { pattern: /ffmpeg-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true, required: true, baseDir: 'js',mandatoryUpdate: true },
37
- { pattern: /theme-(\d+\.\d+)$/, minVersion: '3.2', isFolder: true , required: true, baseDir: 'theme',mandatoryUpdate: true },
38
-
39
-
40
- { pattern: /certificatejs-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true , required: true, baseDir: 'certificate',mandatoryUpdate: true }
41
-
42
- ];
43
-
44
-
45
-
46
-
47
-
48
-
49
- //Check codeplay-common latest version installed or not Start
50
- const { execSync } = require('child_process');
51
-
52
- function getInstalledVersion(packageName) {
53
- try {
54
- const packageJsonPath = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
55
- if (fs.existsSync(packageJsonPath)) {
56
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
57
- return packageJson.version;
58
- }
59
- } catch (error) {
60
- return null;
61
- }
62
- return null;
63
- }
64
-
65
- function getLatestVersion(packageName) {
66
- try {
67
- return execSync(`npm view ${packageName} version`).toString().trim();
68
- } catch (error) {
69
- console.error(`Failed to fetch latest version for ${packageName}`);
70
- return null;
71
- }
72
- }
73
-
74
- function checkPackageVersion() {
75
- const packageName = 'codeplay-common';
76
- const installedVersion = getInstalledVersion(packageName);
77
- const latestVersion = getLatestVersion(packageName);
78
-
79
- if (!installedVersion) {
80
- console.error(`${packageName} is not installed. Please install it using "npm install ${packageName}".`);
81
- process.exit(1);
82
- }
83
-
84
- if (installedVersion !== latestVersion) {
85
- 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`);
86
- process.exit(1);
87
- }
88
-
89
- console.log(`${packageName} is up to date (version ${installedVersion}).`);
90
- }
91
-
92
- // Run package version check before executing the main script
93
- try {
94
- checkPackageVersion();
95
- } catch (error) {
96
- console.error(error.message);
97
- process.exit(1);
98
- }
99
-
100
- //Check codeplay-common latest version installed or not END
101
-
102
-
103
-
104
- function compareWithBeta(installedVersion, minVersion, isBeta) {
105
- const baseCompare = compareVersions(installedVersion, minVersion);
106
-
107
- if (!isBeta) {
108
- // Stable version → normal compare
109
- return baseCompare;
110
- }
111
-
112
- // Beta version logic
113
- if (baseCompare > 0) return 1; // 5.3-beta > 5.2
114
- if (baseCompare < 0) return -1; // 5.1-beta < 5.2
115
-
116
- // Same version but beta → LOWER than stable
117
- return -1; // 5.2-beta < 5.2
118
- }
119
-
120
-
121
-
122
-
123
- const checkAppUniqueId=()=>{
124
-
125
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
126
-
127
- const appUniqueId = config.android?.APP_UNIQUE_ID;
128
- const RESIZEABLE_ACTIVITY = config.android?.RESIZEABLE_ACTIVITY;
129
- const orientation = config.android?.ORIENTATION;
130
-
131
-
132
- let logErrorMessage="";
133
-
134
- // 1️⃣ Check if it’s missing
135
- if (RESIZEABLE_ACTIVITY === undefined) {
136
- logErrorMessage+='❌ Missing android.RESIZEABLE_ACTIVITY option in capacitor.config.json.\n';
137
- }
138
-
139
- // 2️⃣ Check if it’s not boolean (true/false only)
140
- else if (typeof RESIZEABLE_ACTIVITY !== 'boolean') {
141
- logErrorMessage+='❌ Invalid android.RESIZEABLE_ACTIVITY value. Please use only true or false (without quotes).\n';
142
- }
143
-
144
-
145
-
146
- if (!orientation) {
147
- logErrorMessage+='❌ Missing android.ORIENTATION option in capacitor.config.json.\n';
148
- }
149
-
150
- else if(orientation!="portrait" && orientation!="landscape" && orientation!="auto")
151
- {
152
- logErrorMessage+='❌ Spelling mistake in android.ORIENTATION option in capacitor.config.json. Please use only ["portrait" "landscape" "auto"]\n';
153
- }
154
-
155
-
156
- if (!appUniqueId) {
157
- logErrorMessage+='❌ APP_UNIQUE_ID is missing in capacitor.config.json.';
158
- }
159
-
160
- else if (!Number.isInteger(appUniqueId)) {
161
- logErrorMessage+='❌ APP_UNIQUE_ID must be an integer. Example: 1, 2, 3, etc.';
162
- }
163
-
164
-
165
-
166
- if(logErrorMessage!="")
167
- {
168
- console.error(logErrorMessage);
169
- process.exit(1)
170
- }
171
-
172
-
173
- console.log(`✅ APP_UNIQUE_ID is valid: ${appUniqueId}`);
174
-
175
- }
176
-
177
- checkAppUniqueId();
178
-
179
-
180
-
181
-
182
-
183
-
184
-
185
- //@Codemirror check and install/uninstall the packages START
186
- //const fs = require("fs");
187
- //const path = require("path");
188
- //const { execSync } = require("child_process");
189
-
190
- const baseDir = path.join(__dirname, "..", "src", "js");
191
-
192
- // Step 1: Find highest versioned folder like `editor-1.6`
193
- const editorDirs = fs.readdirSync(baseDir)
194
- .filter(name => /^editor-\d+\.\d+$/.test(name))
195
- .sort((a, b) => {
196
- const getVersion = str => str.match(/(\d+)\.(\d+)/).slice(1).map(Number);
197
- const [aMajor, aMinor] = getVersion(a);
198
- const [bMajor, bMinor] = getVersion(b);
199
- return bMajor - aMajor || bMinor - aMinor;
200
- });
201
-
202
- if (editorDirs.length === 0) {
203
-
204
- console.log("@Codemirror used editor(s) are not found")
205
- //console.error("❌ No editor-x.x folders found in src/js.");
206
- //process.exit(1);
207
- }
208
- else
209
- {
210
-
211
- const latestEditorDir = editorDirs.sort((a, b) => {
212
- const versionA = parseFloat(a.split('-')[1]);
213
- const versionB = parseFloat(b.split('-')[1]);
214
- return versionB - versionA;
215
- })[0];
216
-
217
- //const latestEditorDir = editorDirs[editorDirs.length - 1];
218
- const runJsPath = path.join(baseDir, latestEditorDir, "run.js");
219
-
220
- if (!fs.existsSync(runJsPath)) {
221
- console.error(`❌ run.js not found in ${latestEditorDir}`);
222
- process.exit(1);
223
- }
224
-
225
- // Step 2: Execute the run.js file
226
- console.log(`🚀 Executing ${runJsPath}...`);
227
- execSync(`node "${runJsPath}"`, { stdio: "inherit" });
228
- }
229
-
230
- //@Codemirror check and install/uninstall the packages END
231
-
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
- // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists START
244
-
245
- const os = require('os');
246
-
247
- const saveToGalleryAndSaveFileCheck_iOS = () => {
248
-
249
- // List of paths to scan
250
- const SCAN_PATHS = [
251
- path.resolve(__dirname, '../src/certificate'),
252
- path.resolve(__dirname, '../src/pages'),
253
- path.resolve(__dirname, '../src/js'),
254
- path.resolve(__dirname, '../src/app.f7')
255
- ];
256
-
257
- // Directory to exclude
258
- const EXCLUDED_DIR = path.resolve(__dirname, '../src/js/Ads');
259
-
260
- const ANDROID_MANIFEST_PATH = path.resolve(__dirname, '../android/app/src/main/AndroidManifest.xml');
261
-
262
-
263
- // Match iOS-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5-ios.js) not in comments
264
- const IOS_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*-ios\.js['"]/m;
265
-
266
- // Match Android-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5.js) not in comments
267
- const ANDROID_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*\.js['"]/m;
268
-
269
-
270
-
271
-
272
-
273
- const ALLOWED_EXTENSIONS = ['.js', '.f7'];
274
- const isMac = os.platform() === 'darwin';
275
-
276
- let iosImportFound = false;
277
- let androidImportFound = false;
278
-
279
- // Files to skip completely (full or partial match)
280
- const SKIP_FILES = [
281
- 'pdf-3.11.174.min.js',
282
- 'pdf.worker-3.11.174.min.js'
283
- ,'index.browser.js'
284
- ];
285
-
286
-
287
- function scanDirectory(dir) {
288
-
289
- /*
290
- //######################### DO NOT DELETE THIS - START [Appid base validation] #####################################
291
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
292
- const appUniqueId = config.android?.APP_UNIQUE_ID;
293
- if (appUniqueId == "206") return;
294
- //######################### DO NOT DELETE THIS - END [Appid base validation] #####################################
295
- */
296
-
297
- const stat = fs.statSync(dir);
298
-
299
- if (stat.isFile()) {
300
-
301
- // 🔥 Skip files in SKIP_FILES array
302
- const baseName = path.basename(dir);
303
- if (SKIP_FILES.includes(baseName)) {
304
- // Just skip silently
305
- return;
306
- }
307
-
308
- // Only scan allowed file extensions
309
- if (ALLOWED_EXTENSIONS.some(ext => dir.endsWith(ext))) {
310
- process.stdout.write(`\r🔍 Scanning: ${dir} `);
311
-
312
- const content = fs.readFileSync(dir, 'utf8');
313
-
314
- if (IOS_FILE_REGEX.test(content)) {
315
- iosImportFound = true;
316
- if (!isMac) {
317
- console.error(`\n❌ ERROR: iOS-specific import detected in: ${dir}`);
318
- console.error(`🚫 STOPPED: This file should not be imported in Android/Windows/Linux builds.\n`);
319
- process.exit(1);
320
- }
321
- }
322
- else if (ANDROID_FILE_REGEX.test(content) && !content.includes('-ios.js')) {
323
- androidImportFound = true;
324
- }
325
- }
326
- }
327
- else if (stat.isDirectory()) {
328
- if (dir === EXCLUDED_DIR || path.basename(dir) === 'node_modules') return;
329
-
330
- const entries = fs.readdirSync(dir, { withFileTypes: true });
331
- for (let entry of entries) {
332
- scanDirectory(path.join(dir, entry.name));
333
- }
334
- }
335
- }
336
-
337
-
338
- // Run scan on all specified paths
339
- for (let scanPath of SCAN_PATHS) {
340
- if (fs.existsSync(scanPath)) {
341
- scanDirectory(scanPath);
342
- }
343
- }
344
-
345
-
346
-
347
- /* // Check src folder
348
- if (!fs.existsSync(ROOT_DIR)) {
349
- console.warn(`⚠️ Warning: 'src' directory not found at: ${ROOT_DIR}`);
350
- return;
351
- } */
352
-
353
- //scanDirectory(ROOT_DIR);
354
-
355
- // iOS Checks
356
- if (isMac && !iosImportFound) {
357
- console.warn(`⚠️ WARNING: You're on macOS but no iOS version (saveToGalleryAndSaveAnyFile-x.x-ios.js) found.`);
358
- process.exit(1);
359
- } else if (isMac && iosImportFound) {
360
- console.log('✅ iOS version detected for macOS build.');
361
- } else if (!iosImportFound) {
362
- console.log('✅ No iOS-specific imports detected for non-macOS.');
363
- }
364
-
365
- // Android Checks
366
- if (androidImportFound) {
367
- console.log("📱 Android version of saveToGalleryAndSaveAnyFile detected. Checking AndroidManifest.xml...");
368
-
369
- if (!fs.existsSync(ANDROID_MANIFEST_PATH)) {
370
- console.error("❌ AndroidManifest.xml not found. Cannot add requestLegacyExternalStorage attribute.");
371
- return;
372
- }
373
-
374
- let manifestContent = fs.readFileSync(ANDROID_MANIFEST_PATH, 'utf8');
375
-
376
- if (!manifestContent.includes('android:requestLegacyExternalStorage="true"')) {
377
- console.log("Adding android:requestLegacyExternalStorage=\"true\" to <application> tag...");
378
-
379
- manifestContent = manifestContent.replace(
380
- /<application([^>]*)>/,
381
- (match, attrs) => {
382
- if (attrs.includes('android:requestLegacyExternalStorage')) return match;
383
- return `<application${attrs} android:requestLegacyExternalStorage="true">`;
384
- }
385
- );
386
-
387
- fs.writeFileSync(ANDROID_MANIFEST_PATH, manifestContent, 'utf8');
388
- console.log("✅ android:requestLegacyExternalStorage=\"true\" added successfully.");
389
- } else {
390
- console.log("ℹ️ android:requestLegacyExternalStorage already exists in AndroidManifest.xml.");
391
- }
392
- } else {
393
- console.log("✅ No Android saveToGalleryAndSaveAnyFile imports detected.");
394
- }
395
- };
396
-
397
- saveToGalleryAndSaveFileCheck_iOS();
398
- // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists END
399
-
400
-
401
-
402
-
403
-
404
-
405
-
406
-
407
-
408
-
409
-
410
-
411
-
412
- /*
413
- // Clean up AppleDouble files (._*) created by macOS START
414
- if (process.platform === 'darwin') {
415
- try {
416
- console.log('🧹 Cleaning up AppleDouble files (._*)...');
417
- execSync(`find . -name '._*' -delete`);
418
- console.log('✅ AppleDouble files removed.');
419
- } catch (err) {
420
- console.warn('⚠️ Failed to remove AppleDouble files:', err.message);
421
- }
422
- } else {
423
- console.log('ℹ️ Skipping AppleDouble cleanup not a macOS machine.');
424
- }
425
-
426
- // Clean up AppleDouble files (._*) created by macOS END
427
- */
428
-
429
-
430
-
431
-
432
-
433
-
434
- //In routes.js file check static import START
435
-
436
- const routesPath = path.join(process.cwd(), 'src', 'js', 'routes.js');
437
- const routesContent = fs.readFileSync(routesPath, 'utf-8');
438
-
439
- let inBlockComment = false;
440
- const lines = routesContent.split('\n');
441
-
442
- const allowedImport = `import HomePage from '../pages/home.f7';`;
443
- const badImportRegex = /^[ \t]*import\s+[\w{}*,\s]*\s+from\s+['"].+\.f7['"]\s*;/;
444
- const badImports = [];
445
-
446
- lines.forEach((line, index) => {
447
- const trimmed = line.trim();
448
-
449
- // Handle block comment start and end
450
- if (trimmed.startsWith('/*')) inBlockComment = true;
451
- if (inBlockComment && trimmed.endsWith('*/')) {
452
- inBlockComment = false;
453
- return;
454
- }
455
-
456
- // Skip if inside block comment or line comment
457
- if (inBlockComment || trimmed.startsWith('//')) return;
458
-
459
- // Match static .f7 import
460
- if (badImportRegex.test(trimmed) && trimmed !== allowedImport) {
461
- badImports.push({ line: trimmed, number: index + 1 });
462
- }
463
- });
464
-
465
- if (badImports.length > 0) {
466
- console.error('\n❌ ERROR: Detected disallowed static imports of .f7 files in routes.js\n');
467
- console.error(`⚠️ Only this static import is allowed:\n ${allowedImport}\n`);
468
- console.error(`🔧 Please convert other imports to async dynamic imports like this:\n`);
469
- console.error(`
470
-
471
- import HomePage from '../pages/home.f7';
472
-
473
- const routes = [
474
- {
475
- path: '/',
476
- component:HomePage,
477
- },
478
- {
479
- path: '/ProfilePage/',
480
- async async({ resolve }) {
481
- const page = await import('../pages/profile.f7');
482
- resolve({ component: page.default });
483
- },
484
- }]
485
- `);
486
-
487
- badImports.forEach(({ line, number }) => {
488
- console.error(`${number}: ${line}`);
489
- });
490
-
491
- process.exit(1);
492
- } else {
493
- console.log('✅ routes.js passed the .f7 import check.');
494
- }
495
-
496
- //In routes.js file check static import END
497
-
498
-
499
-
500
-
501
-
502
-
503
-
504
-
505
-
506
-
507
-
508
-
509
-
510
- // Check and change the "BridgeWebViewClient.java" file START
511
- /*
512
- For crash issue due to low memory problem, we need to modify the onRenderProcessGone method in BridgeWebViewClient.java.
513
- */
514
-
515
-
516
- const bridgeWebViewClientFilePath = path.join(process.cwd(), 'node_modules', '@capacitor/android/capacitor/src/main/java/com/getcapacitor', 'BridgeWebViewClient.java');
517
-
518
- // Read the file
519
- if (!fs.existsSync(bridgeWebViewClientFilePath)) {
520
- console.error('❌ Error: BridgeWebViewClient.java not found.');
521
- process.exit(1);
522
- }
523
-
524
- let fileContent = fs.readFileSync(bridgeWebViewClientFilePath, 'utf8');
525
-
526
- // Define old and new code
527
- const oldCodeStart = `@Override
528
- public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
529
- super.onRenderProcessGone(view, detail);
530
- boolean result = false;
531
-
532
- List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
533
- if (webViewListeners != null) {
534
- for (WebViewListener listener : bridge.getWebViewListeners()) {
535
- result = listener.onRenderProcessGone(view, detail) || result;
536
- }
537
- }
538
-
539
- return result;
540
- }`;
541
-
542
- const newCode = `@Override
543
- public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
544
- super.onRenderProcessGone(view, detail);
545
-
546
- boolean result = false;
547
-
548
- List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
549
- if (webViewListeners != null) {
550
- for (WebViewListener listener : bridge.getWebViewListeners()) {
551
- result = listener.onRenderProcessGone(view, detail) || result;
552
- }
553
- }
554
-
555
- if (!result) {
556
- // If no one handled it, handle it ourselves!
557
-
558
- /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
559
- if (detail.didCrash()) {
560
- //Log.e("CapacitorWebView", "WebView crashed internally!");
561
- } else {
562
- //Log.w("CapacitorWebView", "WebView was killed by system (low memory) internally!");
563
- }
564
- }*/
565
-
566
- view.post(() -> {
567
- Toast.makeText(view.getContext(), "Reloading due to low memory issue", Toast.LENGTH_SHORT).show();
568
- });
569
-
570
- view.reload(); // Safely reload WebView
571
-
572
- return true; // We handled it
573
- }
574
-
575
- return result;
576
- }`;
577
-
578
- // Step 1: Update method if needed
579
- let updated = false;
580
-
581
- if (fileContent.includes(oldCodeStart)) {
582
- console.log('✅ Found old onRenderProcessGone method. Replacing it...');
583
- fileContent = fileContent.replace(oldCodeStart, newCode);
584
- updated = true;
585
- } else if (fileContent.includes(newCode)) {
586
- console.log('ℹ️ Method already updated. No changes needed in "BridgeWebViewClient.java".');
587
- } else {
588
- console.error('❌ Error: Neither old nor new code found. Unexpected content.');
589
- process.exit(1);
590
- }
591
-
592
- // Step 2: Check and add import if missing
593
- const importToast = 'import android.widget.Toast;';
594
- if (!fileContent.includes(importToast)) {
595
- console.log('✅ Adding missing import for Toast...');
596
- const importRegex = /import\s+[^;]+;/g;
597
- const matches = [...fileContent.matchAll(importRegex)];
598
-
599
- if (matches.length > 0) {
600
- const lastImport = matches[matches.length - 1];
601
- const insertPosition = lastImport.index + lastImport[0].length;
602
- fileContent = fileContent.slice(0, insertPosition) + `\n${importToast}` + fileContent.slice(insertPosition);
603
- updated = true;
604
- } else {
605
- console.error('❌ Error: No import section found in file.');
606
- process.exit(1);
607
- }
608
- } else {
609
- console.log('ℹ️ Import for Toast already exists. No changes needed.');
610
- }
611
-
612
- // Step 3: Save if updated
613
- if (updated) {
614
- fs.writeFileSync(bridgeWebViewClientFilePath, fileContent, 'utf8');
615
- console.log('✅ File updated successfully.');
616
- } else {
617
- console.log('ℹ️ No changes needed.');
618
- }
619
-
620
-
621
-
622
-
623
- // Check and change the "BridgeWebViewClient.java" file END
624
-
625
-
626
-
627
-
628
-
629
-
630
-
631
-
632
- /*
633
- // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file START
634
-
635
- // Build the path dynamically like you requested
636
- const gradlePath = path.join(
637
- process.cwd(),
638
- 'android',
639
- 'build.gradle'
640
- );
641
-
642
- // Read the existing build.gradle
643
- let gradleContent = fs.readFileSync(gradlePath, 'utf8');
644
-
645
- // Add `ext.kotlin_version` if it's not already there
646
- if (!gradleContent.includes('ext.kotlin_version')) {
647
- gradleContent = gradleContent.replace(
648
- /buildscript\s*{/,
649
- `buildscript {\n ext.kotlin_version = '2.1.0'`
650
- );
651
- }
652
-
653
- // Add Kotlin classpath if it's not already there
654
- if (!gradleContent.includes('org.jetbrains.kotlin:kotlin-gradle-plugin')) {
655
- gradleContent = gradleContent.replace(
656
- /dependencies\s*{([\s\S]*?)classpath 'com.android.tools.build:gradle:8.7.2'/,
657
- `dependencies {\n classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")\n$1classpath 'com.android.tools.build:gradle:8.7.2'`
658
- );
659
- }
660
-
661
- // Write back the modified content
662
- fs.writeFileSync(gradlePath, gradleContent, 'utf8');
663
-
664
- console.log('✅ Kotlin version updated in build.gradle.');
665
-
666
- // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file END
667
- */
668
-
669
-
670
-
671
-
672
-
673
-
674
-
675
-
676
- let _admobConfig;
677
-
678
-
679
-
680
- const androidPlatformPath = path.join(process.cwd(), 'android');
681
- const iosPlatformPath = path.join(process.cwd(), 'ios');
682
- const pluginPath = path.join(process.cwd(), 'node_modules', 'emi-indo-cordova-plugin-admob', 'plugin.xml');
683
- const infoPlistPath = path.join(process.cwd(), 'ios', 'App', 'App', 'Info.plist');
684
- const resourcesPath = path.join(process.cwd(), 'resources', 'res');
685
- const androidResPath = path.join(process.cwd(), 'android', 'app', 'src', 'main', 'res');
686
- const localNotificationsPluginPath = path.join(process.cwd(), 'node_modules', '@capacitor', 'local-notifications');
687
-
688
- function fileExists(filePath) {
689
- return fs.existsSync(filePath);
690
- }
691
-
692
- function copyFolderSync(source, target) {
693
- if (!fs.existsSync(target)) {
694
- fs.mkdirSync(target, { recursive: true });
695
- }
696
-
697
- fs.readdirSync(source).forEach(file => {
698
- const sourceFile = path.join(source, file);
699
- const targetFile = path.join(target, file);
700
-
701
- if (fs.lstatSync(sourceFile).isDirectory()) {
702
- copyFolderSync(sourceFile, targetFile);
703
- } else {
704
- fs.copyFileSync(sourceFile, targetFile);
705
- }
706
- });
707
- }
708
-
709
- function checkAndCopyResources() {
710
- if (fileExists(resourcesPath)) {
711
- copyFolderSync(resourcesPath, androidResPath);
712
- console.log('✅ Successfully copied resources/res to android/app/src/main/res.');
713
- } else {
714
- console.log('resources/res folder not found.');
715
-
716
- if (fileExists(localNotificationsPluginPath)) {
717
- throw new Error('❌ resources/res is required for @capacitor/local-notifications. Stopping execution.');
718
- }
719
- }
720
- }
721
-
722
-
723
-
724
-
725
-
726
-
727
-
728
-
729
- function getAdMobConfig() {
730
- if (!fileExists(configPath)) {
731
- throw new Error('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
732
- }
733
-
734
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
735
- const admobConfig = config.plugins?.AdMob;
736
-
737
- if (!admobConfig) {
738
- throw new Error(' AdMob configuration is missing in capacitor.config.json.');
739
- }
740
-
741
- // Default to true if ADMOB_ENABLED is not specified
742
- const isEnabled = admobConfig.ADMOB_ENABLED !== false;
743
-
744
- if (!isEnabled) {
745
- return { ADMOB_ENABLED: false }; // Skip further validation
746
- }
747
-
748
- if (!admobConfig.APP_ID_ANDROID || !admobConfig.APP_ID_IOS) {
749
- throw new Error(' ❌ AdMob configuration is incomplete. Ensure APP_ID_ANDROID and APP_ID_IOS are defined.');
750
- }
751
-
752
- return {
753
- ADMOB_ENABLED: true,
754
- APP_ID_ANDROID: admobConfig.APP_ID_ANDROID,
755
- APP_ID_IOS: admobConfig.APP_ID_IOS,
756
- USE_LITE_ADS: admobConfig.USE_LITE_ADS === "lite",
757
- };
758
- }
759
-
760
- function validateAndroidBuildOptions() {
761
-
762
-
763
- if (!fileExists(configPath)) {
764
- console.log('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
765
- process.exit(1);
766
- }
767
-
768
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
769
-
770
- const targetAppId=config.appId
771
-
772
- const buildOptions = config.android?.buildOptions;
773
-
774
- if (!buildOptions) {
775
- console.log('❌ Missing android.buildOptions in capacitor.config.json.');
776
- process.exit(1);
777
- }
778
-
779
- const requiredProps = [
780
- 'keystorePath',
781
- 'keystorePassword',
782
- 'keystoreAlias',
783
- 'keystoreAliasPassword',
784
- 'releaseType',
785
- 'signingType'
786
- ];
787
-
788
- const missing = requiredProps.filter(prop => !buildOptions[prop]);
789
-
790
- if (missing.length > 0) {
791
- console.log('❌ Missing properties android.buildOptions in capacitor.config.json.');
792
- process.exit(1);
793
- }
794
-
795
-
796
- const keystorePath=buildOptions.keystorePath
797
- const keyFileName = path.basename(keystorePath);
798
-
799
-
800
-
801
- const keystoreMap = {
802
- "gameskey.jks": [
803
- "com.cube.blaster",
804
- ],
805
- "htmleditorkeystoke.jks": [
806
- "com.HTML.AngularJS.Codeplay",
807
- "com.html.codeplay.pro",
808
- "com.bootstrap.code.play",
809
- "com.kids.learning.master",
810
- "com.Simple.Barcode.Scanner"
811
- ]
812
- };
813
-
814
- // find which keystore is required for the given targetAppId
815
- let requiredKey = "newappskey.jks"; // default
816
- for (const [keyFile, appIds] of Object.entries(keystoreMap)) {
817
- if (appIds.includes(targetAppId)) {
818
- requiredKey = keyFile;
819
- break;
820
- }
821
- }
822
-
823
- // validate
824
- if (keyFileName !== requiredKey) {
825
- console.log(`❌ The keystore path is mismatched. Expected ${requiredKey} for ${targetAppId}, but got ${keyFileName}`);
826
- process.exit(1);
827
- }
828
-
829
-
830
-
831
-
832
-
833
- // optionally return them
834
- //return buildOptions;
835
- }
836
-
837
- function updatePluginXml(admobConfig) {
838
- if (!fileExists(pluginPath)) {
839
- console.error(' ❌ plugin.xml not found. Ensure the plugin is installed.');
840
- return;
841
- }
842
-
843
- let pluginContent = fs.readFileSync(pluginPath, 'utf8');
844
-
845
- pluginContent = pluginContent
846
- .replace(/<preference name="APP_ID_ANDROID" default=".*?" \/>/, `<preference name="APP_ID_ANDROID" default="${admobConfig.APP_ID_ANDROID}" />`)
847
- .replace(/<preference name="APP_ID_IOS" default=".*?" \/>/, `<preference name="APP_ID_IOS" default="${admobConfig.APP_ID_IOS}" />`);
848
-
849
- fs.writeFileSync(pluginPath, pluginContent, 'utf8');
850
- console.log('✅ AdMob IDs successfully updated in plugin.xml');
851
- }
852
-
853
- function updateInfoPlist(admobConfig) {
854
- if (!fileExists(infoPlistPath)) {
855
- console.error(' ❌ Info.plist not found. Ensure you have built the iOS project.');
856
- return;
857
- }
858
-
859
- const plistContent = fs.readFileSync(infoPlistPath, 'utf8');
860
- const plistData = plist.parse(plistContent);
861
-
862
- plistData.GADApplicationIdentifier = admobConfig.APP_ID_IOS;
863
- plistData.NSUserTrackingUsageDescription = 'This identifier will be used to deliver personalized ads to you.';
864
- plistData.GADDelayAppMeasurementInit = true;
865
-
866
- const updatedPlistContent = plist.build(plistData);
867
- fs.writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
868
- console.log('AdMob IDs and additional configurations successfully updated in Info.plist');
869
- }
870
-
871
-
872
- try {
873
- if (!fileExists(configPath)) {
874
- throw new Error(' ❌ capacitor.config.json not found. Skipping setup.');
875
- }
876
-
877
- if (!fileExists(androidPlatformPath) && !fileExists(iosPlatformPath)) {
878
- throw new Error('Neither Android nor iOS platforms are found. Ensure platforms are added to your Capacitor project.');
879
- }
880
-
881
- checkAndCopyResources();
882
-
883
-
884
-
885
- _admobConfig = getAdMobConfig();
886
-
887
-
888
-
889
-
890
-
891
- // Proceed only if ADMOB_ENABLED is true
892
- if (_admobConfig.ADMOB_ENABLED) {
893
- if (fileExists(androidPlatformPath)) {
894
- updatePluginXml(_admobConfig);
895
- }
896
-
897
- if (fileExists(iosPlatformPath)) {
898
- updateInfoPlist(_admobConfig);
899
- }
900
- }
901
-
902
-
903
- } catch (error) {
904
- console.error(error.message);
905
- process.exit(1); // Stop execution if there's a critical error
906
- }
907
-
908
-
909
-
910
- validateAndroidBuildOptions();
911
-
912
-
913
-
914
-
915
-
916
-
917
- // Check all the codeplays plugins version START
918
-
919
-
920
- const readline = require('readline');
921
-
922
-
923
- //const srcDir = path.join(__dirname, 'src');
924
- const srcDir = path.join(process.cwd(), 'src');
925
- let outdatedPlugins = [];
926
-
927
- function parseVersion(ver) {
928
- return ver.split('.').map(n => parseInt(n, 10));
929
- }
930
-
931
- function compareVersions(v1, v2) {
932
- const [a1, b1] = parseVersion(v1);
933
- const [a2, b2] = parseVersion(v2);
934
- if (a1 !== a2) return a1 - a2;
935
- return b1 - b2;
936
- }
937
-
938
- function walkSync(dir, filelist = []) {
939
- fs.readdirSync(dir).forEach(file => {
940
- const fullPath = path.join(dir, file);
941
- const stat = fs.statSync(fullPath);
942
- if (stat.isDirectory()) {
943
- walkSync(fullPath, filelist);
944
- } else {
945
- filelist.push(fullPath);
946
- }
947
- });
948
- return filelist;
949
- }
950
-
951
-
952
-
953
- function getSearchRoot(plugin) {
954
- return path.join(srcDir, plugin.baseDir || 'js');
955
- }
956
-
957
-
958
-
959
-
960
-
961
-
962
-
963
-
964
-
965
-
966
-
967
-
968
-
969
-
970
-
971
-
972
-
973
-
974
-
975
-
976
- /*############################################## AUTO DOWNLOAD FROM SERVER START #####################################*/
977
-
978
- // ============================================================
979
- // 🔥 AUTO PLUGIN UPDATE SYSTEM (MANDATORY UPDATES)
980
- // ============================================================
981
-
982
- const https = require("https");
983
-
984
- /**
985
- * Check if file exists on server using HEAD request
986
- */
987
- function urlExists(url) {
988
- return new Promise(resolve => {
989
- const req = https.request(url, { method: "HEAD" }, res => {
990
- resolve(res.statusCode === 200);
991
- });
992
-
993
- req.on("error", () => resolve(false));
994
- req.end();
995
- });
996
- }
997
-
998
- /**
999
- * Download file from server
1000
- */
1001
- function downloadFile(url, dest) {
1002
- return new Promise((resolve, reject) => {
1003
- const file = fs.createWriteStream(dest);
1004
-
1005
- https.get(url, response => {
1006
- if (response.statusCode !== 200) {
1007
- reject("Download failed");
1008
- return;
1009
- }
1010
-
1011
- response.pipe(file);
1012
-
1013
- file.on("finish", () => {
1014
- file.close(resolve);
1015
- });
1016
- }).on("error", err => {
1017
- fs.unlink(dest, () => {});
1018
- reject(err);
1019
- });
1020
- });
1021
- }
1022
-
1023
- /**
1024
- * Update imports across src folder
1025
- * Replaces old filename → new filename
1026
- */
1027
- /**
1028
- * Update imports across project
1029
- * Replaces old filename → new filename
1030
- */
1031
-
1032
- const VITE_ALIAS_ONLY = [
1033
- "common",
1034
- "admob-emi",
1035
- "localization",
1036
- "theme",
1037
- "certificatejs",
1038
- "ffmpeg"
1039
- ];
1040
- function updateImports(oldName, newName) {
1041
-
1042
- const projectRoot = process.cwd();
1043
-
1044
- const viteFiles = [
1045
- path.join(projectRoot, "vite.config.js"),
1046
- path.join(projectRoot, "vite.config.mjs")
1047
- ];
1048
-
1049
- // Detect if alias-only plugin
1050
- const isAliasOnly = VITE_ALIAS_ONLY.some(p =>
1051
- oldName.includes(p)
1052
- );
1053
-
1054
- //--------------------------------------------------
1055
- // 1️⃣ ALWAYS update vite config
1056
- //--------------------------------------------------
1057
- viteFiles.forEach(file => {
1058
- if (!fs.existsSync(file)) return;
1059
-
1060
- let content = fs.readFileSync(file, "utf8");
1061
-
1062
- if (content.includes(oldName)) {
1063
- content = content.replaceAll(oldName, newName);
1064
- fs.writeFileSync(file, content);
1065
-
1066
- console.log(`✏️ Updated alias in ${path.basename(file)}`);
1067
- }
1068
- });
1069
-
1070
- //--------------------------------------------------
1071
- // 2️⃣ If alias-only → STOP here
1072
- //--------------------------------------------------
1073
- if (isAliasOnly) {
1074
- console.log(`⚡ Alias-only plugin → skipped full scan`);
1075
- return;
1076
- }
1077
-
1078
- //--------------------------------------------------
1079
- // 3️⃣ Otherwise scan src
1080
- //--------------------------------------------------
1081
- const srcDir = path.join(projectRoot, "src");
1082
-
1083
- function walk(dir) {
1084
- fs.readdirSync(dir).forEach(file => {
1085
-
1086
- if (["node_modules","android","ios","dist",".git"].includes(file))
1087
- return;
1088
-
1089
- const full = path.join(dir, file);
1090
- const stat = fs.statSync(full);
1091
-
1092
- if (stat.isDirectory()) {
1093
- walk(full);
1094
- }
1095
- else if (
1096
- (full.endsWith(".js") ||
1097
- full.endsWith(".f7") ||
1098
- full.endsWith(".mjs")) &&
1099
- !full.endsWith(".min.js")
1100
- ) {
1101
- let content = fs.readFileSync(full, "utf8");
1102
-
1103
- if (content.includes(oldName)) {
1104
- content = content.replaceAll(oldName, newName);
1105
- fs.writeFileSync(full, content);
1106
-
1107
- console.log(`✏️ Updated import in ${path.relative(projectRoot, full)}`);
1108
- }
1109
- }
1110
- });
1111
- }
1112
-
1113
- if (fs.existsSync(srcDir)) {
1114
- walk(srcDir);
1115
- }
1116
- }
1117
-
1118
-
1119
-
1120
- /**
1121
- * Auto-update a plugin file
1122
- * Returns TRUE if success
1123
- * Returns FALSE if fallback to manual needed
1124
- */
1125
-
1126
-
1127
-
1128
- function fetchVersions() {
1129
- return new Promise((resolve, reject) => {
1130
- https.get(
1131
- "https://htmlcodeplay.com/code-play-plugin/versions.json",
1132
- res => {
1133
- let data = "";
1134
-
1135
- res.on("data", chunk => data += chunk);
1136
- res.on("end", () => {
1137
- try {
1138
- resolve(JSON.parse(data));
1139
- } catch {
1140
- resolve(null);
1141
- }
1142
- });
1143
- }
1144
- ).on("error", () => resolve(null));
1145
- });
1146
- }
1147
-
1148
-
1149
-
1150
- async function autoUpdatePlugin(pluginDef, pluginInfo) {
1151
-
1152
- if (pluginDef.isFolder) return false;
1153
-
1154
- const versions = await fetchVersions();
1155
-
1156
- if (!versions) {
1157
- console.log("⚠️ versions.json not reachable");
1158
- return false;
1159
- }
1160
-
1161
- const oldFullPath = path.join(srcDir, pluginInfo.name);
1162
- const oldFileName = path.basename(oldFullPath);
1163
-
1164
- // Extract base name
1165
- const baseName = oldFileName.replace(/-\d+\.\d+.*$/, "");
1166
-
1167
- const latestVersion = versions[baseName];
1168
-
1169
- if (!latestVersion) {
1170
- console.log(`❌ No version entry for ${baseName}`);
1171
- return false;
1172
- }
1173
-
1174
- const newFileName = `${baseName}-${latestVersion}.js`;
1175
-
1176
- const url =
1177
- `https://htmlcodeplay.com/code-play-plugin/${newFileName}`;
1178
-
1179
- console.log(`🔍 Checking latest: ${newFileName}`);
1180
-
1181
- if (!(await urlExists(url))) {
1182
- console.log(`❌ File missing on server`);
1183
- return false;
1184
- }
1185
-
1186
- const destPath =
1187
- path.join(path.dirname(oldFullPath), newFileName);
1188
-
1189
- await downloadFile(url, destPath);
1190
-
1191
- fs.unlinkSync(oldFullPath);
1192
-
1193
- updateImports(oldFileName, newFileName);
1194
-
1195
- console.log(`✅ Updated ${newFileName}`);
1196
-
1197
- return true;
1198
- }
1199
-
1200
-
1201
-
1202
-
1203
- /*############################################## AUTO DOWNLOAD FROM SERVER END #####################################*/
1204
-
1205
-
1206
-
1207
-
1208
-
1209
-
1210
-
1211
-
1212
-
1213
-
1214
-
1215
-
1216
-
1217
-
1218
-
1219
-
1220
-
1221
-
1222
-
1223
-
1224
-
1225
-
1226
- let hasMandatoryUpdate = false;
1227
- function checkPlugins() {
1228
- return new Promise(async (resolve, reject) => {
1229
- const files = walkSync(srcDir);
1230
- const outdatedPlugins = [];
1231
- let hasMandatoryUpdate = false;
1232
-
1233
- for (const plugin of requiredPlugins) {
1234
- const searchRoot = getSearchRoot(plugin);
1235
-
1236
- // ---------- Folder plugins ----------
1237
- if (plugin.isFolder) {
1238
- if (!fs.existsSync(searchRoot)) continue;
1239
-
1240
- const subDirs = fs.readdirSync(searchRoot)
1241
- .map(name => path.join(searchRoot, name))
1242
- .filter(p => fs.statSync(p).isDirectory());
1243
-
1244
- for (const dir of subDirs) {
1245
- const relativePath = path.relative(searchRoot, dir).replace(/\\/g, '/');
1246
- const match = plugin.pattern.exec(relativePath);
1247
-
1248
- if (match) {
1249
- const currentVersion = match[1];
1250
-
1251
- if (compareVersions(currentVersion, plugin.minVersion) < 0) {
1252
- outdatedPlugins.push({
1253
- name: relativePath,
1254
- currentVersion,
1255
- requiredVersion: plugin.minVersion,
1256
- mandatoryUpdate: plugin.mandatoryUpdate === true
1257
- });
1258
-
1259
- if (plugin.mandatoryUpdate) {
1260
- hasMandatoryUpdate = true;
1261
- }
1262
- }
1263
- }
1264
- }
1265
- continue;
1266
- }
1267
-
1268
- // ---------- File plugins ----------
1269
- const matchedFile = files.find(file =>
1270
- file.startsWith(searchRoot) && plugin.pattern.test(file)
1271
- );
1272
-
1273
- if (matchedFile) {
1274
- const match = plugin.pattern.exec(matchedFile);
1275
- if (match) {
1276
- const currentVersion = match[1];
1277
- const isBeta = !!match[2];
1278
-
1279
- const cmp = plugin.pattern.source.includes('beta')
1280
- ? compareWithBeta(currentVersion, plugin.minVersion, isBeta)
1281
- : compareVersions(currentVersion, plugin.minVersion);
1282
-
1283
- if (cmp < 0) {
1284
- outdatedPlugins.push({
1285
- name: path.relative(srcDir, matchedFile),
1286
- currentVersion: isBeta ? `${currentVersion}-beta` : currentVersion,
1287
- requiredVersion: plugin.minVersion,
1288
- mandatoryUpdate: plugin.mandatoryUpdate === true
1289
- });
1290
-
1291
- if (plugin.mandatoryUpdate) {
1292
- hasMandatoryUpdate = true;
1293
- }
1294
- }
1295
- }
1296
- }
1297
- }
1298
-
1299
- // ---------- Result handling ----------
1300
- if (outdatedPlugins.length > 0) {
1301
- console.log('\n❗ The following plugins are outdated:\n');
1302
-
1303
- outdatedPlugins.forEach( p => {
1304
- const tag = p.mandatoryUpdate ? '🔥 MANDATORY' : '';
1305
- console.log(
1306
- ` ❌ - ${p.name} (Current: ${p.currentVersion}, Required: ${p.requiredVersion}) ${tag}`
1307
- );
1308
- });
1309
-
1310
- // 🚨 Mandatory update → stop build
1311
- /* if (hasMandatoryUpdate) {
1312
- console.log('\n🚫 One or more plugins require a mandatory update.');
1313
- console.log('❌ Build cancelled. Please update mandatory plugins and try again.');
1314
- process.exit(1);
1315
- } */
1316
-
1317
-
1318
-
1319
-
1320
-
1321
- if (hasMandatoryUpdate) {
1322
-
1323
- //--------------------------------------------------
1324
- // 🚫 AUTO UPDATE DISABLED
1325
- //--------------------------------------------------
1326
- if (!ENABLE_AUTO_UPDATE) {
1327
- console.log("\n🚫 Auto-update disabled.");
1328
- console.log("❌ Manual update required.");
1329
- process.exit(1);
1330
- }
1331
-
1332
- //--------------------------------------------------
1333
- // 🔥 AUTO UPDATE ENABLED
1334
- //--------------------------------------------------
1335
- console.log("\n🔥 Mandatory plugins outdated. Trying auto-update...\n");
1336
-
1337
-
1338
-
1339
- let autoFailed = false;
1340
-
1341
- for (const p of outdatedPlugins.filter(x => x.mandatoryUpdate)) {
1342
-
1343
- const pluginDef = requiredPlugins.find(def =>
1344
- def.pattern.test(p.name)
1345
- );
1346
-
1347
- if (!pluginDef) continue;
1348
-
1349
- const success = await autoUpdatePlugin(
1350
- pluginDef,
1351
- p
1352
- );
1353
-
1354
- if (!success) {
1355
- autoFailed = true;
1356
-
1357
- const pluginDef = requiredPlugins.find(def =>
1358
- def.pattern.test(p.name)
1359
- );
1360
-
1361
- console.log(`❌ Manual update required for ${p.name}`);
1362
-
1363
- if (pluginDef) {
1364
- console.log(`👉 Required minimum version: ${pluginDef.minVersion}`);
1365
- }
1366
- }
1367
- }
1368
-
1369
- // 🚨 Fallback to manual if any failed
1370
- if (autoFailed) {
1371
- console.log('\n🚫 One or more plugins require manual update.');
1372
- console.log('❌ Build cancelled. Please update mandatory plugins.');
1373
- process.exit(1);
1374
- }
1375
-
1376
- console.log('\n🎉 All mandatory plugins auto-updated!');
1377
- }
1378
-
1379
-
1380
-
1381
-
1382
-
1383
-
1384
- // Optional updates ask user
1385
- const rl = readline.createInterface({
1386
- input: process.stdin,
1387
- output: process.stdout
1388
- });
1389
-
1390
- rl.question(
1391
- '\nAre you sure you want to continue without updating these plugins? (y/n): ',
1392
- answer => {
1393
- rl.close();
1394
-
1395
- if (answer.toLowerCase() !== 'y') {
1396
- console.log('\n❌ Build cancelled due to outdated plugins.');
1397
- process.exit(1);
1398
- } else {
1399
- console.log('\n✅ Continuing build...');
1400
- resolve();
1401
- }
1402
- }
1403
- );
1404
- } else {
1405
- console.log('✅ All plugin versions are up to date.');
1406
- resolve();
1407
- }
1408
- });
1409
- }
1410
-
1411
-
1412
-
1413
-
1414
-
1415
-
1416
-
1417
-
1418
- // Check all the codeplays plugins version START
1419
-
1420
-
1421
-
1422
-
1423
- // ====================================================================
1424
- // AUTO-ADD esbuild.drop: ['console','debugger'] to vite.config.js / mjs
1425
- // ====================================================================
1426
-
1427
-
1428
-
1429
- const checkAndupdateDropInViteConfig = () => {
1430
-
1431
- const possibleFiles = [
1432
- "vite.config.js",
1433
- "vite.config.mjs"
1434
- ];
1435
-
1436
- // Detect existing config file
1437
- const viteConfigPath = possibleFiles
1438
- .map(file => path.join(process.cwd(), file))
1439
- .find(filePath => fs.existsSync(filePath));
1440
-
1441
- if (!viteConfigPath) {
1442
- console.warn("⚠️ No vite config found. Skipping.");
1443
- return;
1444
- }
1445
-
1446
- //console.log("📄 Using:", viteConfigPath.split("/").pop());
1447
-
1448
- let viteContent = fs.readFileSync(viteConfigPath, "utf8");
1449
-
1450
- // Skip if already exists
1451
- if (/drop\s*:\s*\[.*['"]console['"].*\]/.test(viteContent)) {
1452
- console.log("ℹ️ vite.config.(m)js already Updated. Skipping...");
1453
- return;
1454
- }
1455
-
1456
- console.log("🔧 Adding esbuild.drop ...");
1457
-
1458
- // If esbuild block exists
1459
- if (/esbuild\s*:\s*{/.test(viteContent)) {
1460
- viteContent = viteContent.replace(
1461
- /esbuild\s*:\s*{([\s\S]*?)(^ {0,8})}/m,
1462
- (full, inner, indent) => {
1463
-
1464
- let lines = inner
1465
- .split("\n")
1466
- .map(l => l.trim())
1467
- .filter(Boolean);
1468
-
1469
- // Fix last comma
1470
- if (lines.length > 0) {
1471
- lines[lines.length - 1] =
1472
- lines[lines.length - 1].replace(/,+$/, "") + ",";
1473
- }
1474
-
1475
- // Re-indent
1476
- lines = lines.map(l => indent + " " + l);
1477
-
1478
- // Add drop
1479
- lines.push(`${indent} drop: ['console','debugger'],`);
1480
-
1481
- return `esbuild: {\n${lines.join("\n")}\n${indent}}`;
1482
- }
1483
- );
1484
- }
1485
-
1486
- // If esbuild missing
1487
- else {
1488
- viteContent = viteContent.replace(
1489
- /export default defineConfig\s*\(\s*{/,
1490
- m => `${m}\n esbuild: {\n drop: ['console','debugger'],\n },`
1491
- );
1492
- }
1493
-
1494
- fs.writeFileSync(viteConfigPath, viteContent, "utf8");
1495
- console.log("✅ vite.config.(m)js Updated successfully.");
1496
- };
1497
-
1498
-
1499
-
1500
-
1501
-
1502
-
1503
-
1504
-
1505
-
1506
-
1507
- const compareVersion = (v1, v2) => {
1508
- const a = v1.split(".").map(Number);
1509
- const b = v2.split(".").map(Number);
1510
-
1511
- for (let i = 0; i < Math.max(a.length, b.length); i++) {
1512
- const num1 = a[i] || 0;
1513
- const num2 = b[i] || 0;
1514
- if (num1 > num2) return 1;
1515
- if (num1 < num2) return -1;
1516
- }
1517
- return 0;
1518
- };
1519
-
1520
-
1521
-
1522
-
1523
- const admobConfigPath = path.join('src', 'js','Ads', 'admob-ad-configuration.json');
1524
-
1525
- const checkAdmobConfigurationProperty=()=>{
1526
-
1527
-
1528
- if (!_admobConfig.ADMOB_ENABLED)
1529
- {
1530
- console.log("ℹ️ Admob is not enabled so 'admob-ad-configuration.json' checking is skipping...");
1531
- return;
1532
- }
1533
-
1534
-
1535
- const REQUIRED_CONFIG_KEYS = [
1536
- "isKidsApp",
1537
- "isTesting",
1538
- "isConsoleLogEnabled",
1539
- "bannerEnabled",
1540
- "interstitialEnabled",
1541
- "appOpenEnabled",
1542
- "rewardVideoEnabled",
1543
- "rewardInterstitialEnabled",
1544
- "collapsibleEnabled",
1545
- "isLandScape",
1546
- "isOverlappingEnable",
1547
- "bannerTypeAndroid",
1548
- "bannerTypeiOS",
1549
- "bannerTopSpaceColor",
1550
- "interstitialLoadScreenTextColor",
1551
- "interstitialLoadScreenBackgroundColor",
1552
- "beforeBannerSpace",
1553
- "whenShow",
1554
- "minimumClick",
1555
- "interstitialTimeOut",
1556
- "interstitialFirstTimeOut",
1557
- "appOpenAdsTimeOut",
1558
- "maxRetryCount",
1559
- "retrySecondsAr",
1560
- "appOpenPerSession",
1561
- "interstitialPerSession",
1562
- "appOpenFirstTimeOut"
1563
- ];
1564
-
1565
-
1566
-
1567
-
1568
-
1569
- let admobConfigInJson;
1570
-
1571
- try {
1572
- admobConfigInJson = JSON.parse(readFileSync(admobConfigPath, "utf8"));
1573
- } catch (err) {
1574
- console.error("❌ Failed to read admob-ad-configuration.json", err);
1575
- process.exit(1);
1576
- }
1577
-
1578
- // Validate config object exists
1579
- if (!admobConfigInJson.config) {
1580
- console.error('❌ "config" object is missing in admob-ad-configuration.json');
1581
- process.exit(1);
1582
- }
1583
-
1584
-
1585
- const admobConfigMinVersion="1.5"
1586
-
1587
- if (compareVersion(admobConfigInJson.VERSION, admobConfigMinVersion) < 0) {
1588
- console.error(`❌ Please use at-least version ${admobConfigMinVersion} in "src/js/Ads/admob-ad-configuration.json"`);
1589
- process.exit(1);
1590
- }
1591
-
1592
-
1593
- const config = admobConfigInJson.config;
1594
-
1595
- // ✅ Find missing properties
1596
- const missingKeys = REQUIRED_CONFIG_KEYS.filter(
1597
- key => !(key in config)
1598
- );
1599
-
1600
-
1601
-
1602
- if (missingKeys.length > 0) {
1603
- console.error("❌ Missing required configuration keys. Please check it in 'src/js/Ads/admob-ad-configuration.json'");
1604
-
1605
- missingKeys.forEach(k => console.error(" - " + k));
1606
- process.exit(1);
1607
- }
1608
-
1609
-
1610
- console.log('✅ All keys exist. in "admob-ad-configuration.json file" Configuration looks good.');
1611
- }
1612
-
1613
-
1614
-
1615
- function ensureGitignoreEntry(entry) {
1616
- const gitignorePath = path.join(process.cwd(), '.gitignore');
1617
-
1618
- // If .gitignore doesn't exist, create it
1619
- if (!fs.existsSync(gitignorePath)) {
1620
- fs.writeFileSync(gitignorePath, `${entry}\n`, 'utf8');
1621
- console.log(`✅ .gitignore created and added: ${entry}`);
1622
- return;
1623
- }
1624
-
1625
- const content = fs.readFileSync(gitignorePath, 'utf8');
1626
-
1627
- // Normalize lines (trim + remove trailing slashes for comparison)
1628
- const lines = content
1629
- .split(/\r?\n/)
1630
- .map(l => l.trim());
1631
-
1632
- const normalizedEntry = entry.replace(/\/$/, '');
1633
-
1634
- const exists = lines.some(
1635
- line => line.replace(/\/$/, '') === normalizedEntry
1636
- );
1637
-
1638
- if (exists) {
1639
- console.log(`ℹ️ .gitignore already contains: ${entry}`);
1640
- return;
1641
- }
1642
-
1643
- // Ensure file ends with newline
1644
- const separator = content.endsWith('\n') ? '' : '\n';
1645
-
1646
- fs.appendFileSync(gitignorePath, `${separator}${entry}\n`, 'utf8');
1647
- console.log(`✅ Added to .gitignore: ${entry}`);
1648
- }
1649
-
1650
-
1651
- ensureGitignoreEntry('buildCodeplay/');
1652
-
1653
-
1654
- // Run the validation
1655
- (async () => {
1656
- await checkPlugins();
1657
- checkAndupdateDropInViteConfig();
1658
- checkAdmobConfigurationProperty()
1659
- })();
1660
-
1661
-
1662
- // ======================================================
1663
- // Validate theme folder location (src/js/theme is NOT allowed)
1664
- // ======================================================
1665
-
1666
- function validateThemeFolderLocation() {
1667
- const oldThemePath = path.join(process.cwd(), 'src', 'js', 'theme');
1668
- const newThemePath = path.join(process.cwd(), 'src', 'theme');
1669
-
1670
- // ❌ Block old structure
1671
- if (fs.existsSync(oldThemePath)) {
1672
- console.error(
1673
- '\n INVALID PROJECT STRUCTURE DETECTED\n' +
1674
- '--------------------------------------------------\n' +
1675
- 'The "theme" folder must NOT be inside:\n' +
1676
- ' src/js/theme\n\n' +
1677
- '✅ Correct location is:\n' +
1678
- ' src/theme\n\n' +
1679
- '🛑 Please move the folder and re-run the build.\n'
1680
- );
1681
- process.exit(1);
1682
- }
1683
-
1684
- // ⚠️ Optional warning if new theme folder is missing
1685
- if (!fs.existsSync(newThemePath)) {
1686
- console.warn(
1687
- '\n⚠️ WARNING: "src/theme" folder not found.\n' +
1688
- 'If your app uses themes, please ensure it exists.\n'
1689
- );
1690
- } else {
1691
- console.log('✅ Theme folder structure validated (src/theme).');
1692
- }
1693
- }
1694
- validateThemeFolderLocation()
1695
-
1696
-
1697
-
1698
- const validateAndRestoreSignDetails=()=>{
1699
-
1700
- // Read config file
1701
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
1702
-
1703
- // Ensure android and buildOptions exist
1704
- if (!config.android) config.android = {};
1705
- if (!config.android.buildOptions) config.android.buildOptions = {};
1706
-
1707
- // Update only if changed
1708
- let updated = false;
1709
-
1710
- if (config.android.buildOptions.releaseType !== 'AAB') {
1711
- config.android.buildOptions.releaseType = 'AAB';
1712
- updated = true;
1713
- }
1714
-
1715
- if (config.android.buildOptions.signingType !== 'jarsigner') {
1716
- config.android.buildOptions.signingType = 'jarsigner';
1717
- updated = true;
1718
- }
1719
-
1720
- // Write back only if modified
1721
- if (updated) {
1722
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
1723
- console.log('capacitor.config.json updated successfully.');
1724
- } else {
1725
- console.log('No changes needed.');
1726
- }
1727
-
1728
- }
1729
-
1730
- validateAndRestoreSignDetails()
1731
-
1732
-
1733
- /*
1734
- Release Notes
1735
-
1736
- 5.1
1737
- Kotlin version update is commented. Previously admob is not worked if not update the kotlin version to higher version
1738
-
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
+
19
+ // Expected plugin list with minimum versions
20
+ /* const requiredPlugins = [
21
+
22
+ { pattern: /backbutton-(\d+\.\d+)\.js$/, minVersion: '2.0', required: true, baseDir: 'js',mandatoryUpdate: true},
23
+
24
+
25
+ { pattern: /common-(\d+\.\d+)(?:-beta-(\d+))?\.js$/, minVersion: '6.0', required: true, baseDir: 'js',mandatoryUpdate: true },
26
+
27
+ { pattern: /localization_settings-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
28
+ { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: true },
29
+ { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js',mandatoryUpdate: false },
30
+ { pattern: /localNotification_AppSettings-(\d+\.\d+)\.js$/, minVersion: '1.0', required: true, baseDir: 'js',mandatoryUpdate: false },
31
+ { pattern: /onesignal-(\d+\.\d+)\.js$/, minVersion: '2.3', required: true, baseDir: 'js',mandatoryUpdate: false },
32
+ { pattern: /saveToGalleryAndSaveAnyFile-(\d+\.\d+)(-ios)?\.js$/, minVersion: '3.1', required: true, baseDir: 'js',mandatoryUpdate: true },
33
+ { pattern: /Ads[\/\\]admob-emi-(\d+\.\d+)\.js$/, minVersion: '3.7', required: true, baseDir: 'js',mandatoryUpdate: true },
34
+
35
+ // New added plugins
36
+ { pattern: /video-player-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: false },
37
+ { pattern: /image-cropper-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
38
+ { pattern: /common-(\d+\.\d+)\.less$/, minVersion: '1.6', required: true, baseDir: 'assets/css',mandatoryUpdate: false },
39
+
40
+
41
+ // New folders
42
+ { pattern: /IAP-(\d+\.\d+)$/, minVersion: '2.8', isFolder: true , required: true, baseDir: 'js/Ads',mandatoryUpdate: true },
43
+ { pattern: /editor-(\d+\.\d+)$/, minVersion: '1.9', isFolder: true, required: true, baseDir: 'js',mandatoryUpdate: true },
44
+ { pattern: /ffmpeg-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true, required: true, baseDir: 'js',mandatoryUpdate: true },
45
+ { pattern: /theme-(\d+\.\d+)$/, minVersion: '3.3', isFolder: true , required: true, baseDir: 'theme',mandatoryUpdate: true },
46
+
47
+
48
+ { pattern: /certificatejs-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true , required: true, baseDir: 'certificate',mandatoryUpdate: true }
49
+
50
+ ]; */
51
+
52
+
53
+ function requireOrInstall(packageName) {
54
+ try {
55
+ return require(packageName);
56
+ } catch (err) {
57
+
58
+ console.log(`📦 "${packageName}" not found. Installing automatically...`);
59
+
60
+ try {
61
+ execSync(`npm install ${packageName}`, { stdio: "inherit" });
62
+ console.log(`✅ "${packageName}" installed successfully.`);
63
+ } catch (installErr) {
64
+ console.error(`❌ Failed to install "${packageName}".`);
65
+ process.exit(1);
66
+ }
67
+
68
+ // Try loading again
69
+ return require(packageName);
70
+ }
71
+ }
72
+
73
+ const AdmZip = requireOrInstall("adm-zip");
74
+
75
+
76
+
77
+
78
+ const versionsFile = path.join(__dirname, "versions.json");
79
+
80
+ function loadRequiredPlugins() {
81
+
82
+ if (!fs.existsSync(versionsFile)) {
83
+ console.error("❌ versions.json not found");
84
+ process.exit(1);
85
+ }
86
+
87
+ const json = JSON.parse(fs.readFileSync(versionsFile, "utf8"));
88
+
89
+ return json.plugins.map(p => ({
90
+ ...p,
91
+ pattern: new RegExp(p.pattern)
92
+ }));
93
+
94
+ }
95
+
96
+ let requiredPlugins = loadRequiredPlugins();
97
+
98
+
99
+
100
+
101
+
102
+
103
+ async function downloadAndExtractZip(url, destFolder) {
104
+
105
+ const zipPath = destFolder + ".zip";
106
+
107
+ await downloadFile(url, zipPath);
108
+
109
+ const zip = new AdmZip(zipPath);
110
+ zip.extractAllTo(destFolder, true);
111
+
112
+ fs.unlinkSync(zipPath);
113
+
114
+ }
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+ //Check codeplay-common latest version installed or not Start
123
+ //const { execSync } = require('child_process');
124
+
125
+
126
+ function getInstalledVersion(packageName) {
127
+ try {
128
+ const packageJsonPath = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
129
+ if (fs.existsSync(packageJsonPath)) {
130
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
131
+ return packageJson.version;
132
+ }
133
+ } catch (error) {
134
+ return null;
135
+ }
136
+ return null;
137
+ }
138
+
139
+ function getLatestVersion(packageName) {
140
+ try {
141
+ return execSync(`npm view ${packageName} version`).toString().trim();
142
+ } catch (error) {
143
+ console.error(`Failed to fetch latest version for ${packageName}`);
144
+ return null;
145
+ }
146
+ }
147
+
148
+ function checkPackageVersion() {
149
+ const packageName = 'codeplay-common';
150
+ const installedVersion = getInstalledVersion(packageName);
151
+ const latestVersion = getLatestVersion(packageName);
152
+
153
+ if (!installedVersion) {
154
+ console.error(`${packageName} is not installed. Please install it using "npm install ${packageName}".`);
155
+ process.exit(1);
156
+ }
157
+
158
+ if (installedVersion !== latestVersion) {
159
+ 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`);
160
+ process.exit(1);
161
+ }
162
+
163
+ console.log(`${packageName} is up to date (version ${installedVersion}).`);
164
+ }
165
+
166
+ // Run package version check before executing the main script
167
+ try {
168
+ checkPackageVersion();
169
+ } catch (error) {
170
+ console.error(error.message);
171
+ process.exit(1);
172
+ }
173
+
174
+ //Check codeplay-common latest version installed or not END
175
+
176
+
177
+
178
+ function compareWithBeta(installedVersion, minVersion, isBeta) {
179
+ const baseCompare = compareVersions(installedVersion, minVersion);
180
+
181
+ if (!isBeta) {
182
+ // Stable version → normal compare
183
+ return baseCompare;
184
+ }
185
+
186
+ // Beta version logic
187
+ if (baseCompare > 0) return 1; // 5.3-beta > 5.2
188
+ if (baseCompare < 0) return -1; // 5.1-beta < 5.2
189
+
190
+ // Same version but beta LOWER than stable
191
+ return -1; // 5.2-beta < 5.2
192
+ }
193
+
194
+
195
+
196
+
197
+ const checkAppUniqueId=()=>{
198
+
199
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
200
+
201
+ const appUniqueId = config.android?.APP_UNIQUE_ID;
202
+ const RESIZEABLE_ACTIVITY = config.android?.RESIZEABLE_ACTIVITY;
203
+ const orientation = config.android?.ORIENTATION;
204
+
205
+
206
+ let logErrorMessage="";
207
+
208
+ // 1️⃣ Check if it’s missing
209
+ if (RESIZEABLE_ACTIVITY === undefined) {
210
+ logErrorMessage+='❌ Missing android.RESIZEABLE_ACTIVITY option in capacitor.config.json.\n';
211
+ }
212
+
213
+ // 2️⃣ Check if it’s not boolean (true/false only)
214
+ else if (typeof RESIZEABLE_ACTIVITY !== 'boolean') {
215
+ logErrorMessage+='❌ Invalid android.RESIZEABLE_ACTIVITY value. Please use only true or false (without quotes).\n';
216
+ }
217
+
218
+
219
+
220
+ if (!orientation) {
221
+ logErrorMessage+='❌ Missing android.ORIENTATION option in capacitor.config.json.\n';
222
+ }
223
+
224
+ else if(orientation!="portrait" && orientation!="landscape" && orientation!="auto")
225
+ {
226
+ logErrorMessage+='❌ Spelling mistake in android.ORIENTATION option in capacitor.config.json. Please use only ["portrait" "landscape" "auto"]\n';
227
+ }
228
+
229
+
230
+ if (!appUniqueId) {
231
+ logErrorMessage+='❌ APP_UNIQUE_ID is missing in capacitor.config.json.';
232
+ }
233
+
234
+ else if (!Number.isInteger(appUniqueId)) {
235
+ logErrorMessage+='❌ APP_UNIQUE_ID must be an integer. Example: 1, 2, 3, etc.';
236
+ }
237
+
238
+
239
+
240
+ if(logErrorMessage!="")
241
+ {
242
+ console.error(logErrorMessage);
243
+ process.exit(1)
244
+ }
245
+
246
+
247
+ console.log(`✅ APP_UNIQUE_ID is valid: ${appUniqueId}`);
248
+
249
+ }
250
+
251
+ checkAppUniqueId();
252
+
253
+
254
+
255
+
256
+
257
+
258
+
259
+ //@Codemirror check and install/uninstall the packages START
260
+ //const fs = require("fs");
261
+ //const path = require("path");
262
+ //const { execSync } = require("child_process");
263
+
264
+ const baseDir = path.join(__dirname, "..", "src", "js");
265
+
266
+ // Step 1: Find highest versioned folder like `editor-1.6`
267
+ const editorDirs = fs.readdirSync(baseDir)
268
+ .filter(name => /^editor-\d+\.\d+$/.test(name))
269
+ .sort((a, b) => {
270
+ const getVersion = str => str.match(/(\d+)\.(\d+)/).slice(1).map(Number);
271
+ const [aMajor, aMinor] = getVersion(a);
272
+ const [bMajor, bMinor] = getVersion(b);
273
+ return bMajor - aMajor || bMinor - aMinor;
274
+ });
275
+
276
+ if (editorDirs.length === 0) {
277
+
278
+ console.log("@Codemirror used editor(s) are not found")
279
+ //console.error("❌ No editor-x.x folders found in src/js.");
280
+ //process.exit(1);
281
+ }
282
+ else
283
+ {
284
+
285
+ const latestEditorDir = editorDirs.sort((a, b) => {
286
+ const versionA = parseFloat(a.split('-')[1]);
287
+ const versionB = parseFloat(b.split('-')[1]);
288
+ return versionB - versionA;
289
+ })[0];
290
+
291
+ //const latestEditorDir = editorDirs[editorDirs.length - 1];
292
+ const runJsPath = path.join(baseDir, latestEditorDir, "run.js");
293
+
294
+ if (!fs.existsSync(runJsPath)) {
295
+ console.error(`❌ run.js not found in ${latestEditorDir}`);
296
+ process.exit(1);
297
+ }
298
+
299
+ // Step 2: Execute the run.js file
300
+ console.log(`🚀 Executing ${runJsPath}...`);
301
+ execSync(`node "${runJsPath}"`, { stdio: "inherit" });
302
+ }
303
+
304
+ //@Codemirror check and install/uninstall the packages END
305
+
306
+
307
+
308
+
309
+
310
+
311
+
312
+
313
+
314
+
315
+
316
+
317
+ // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists START
318
+
319
+ const os = require('os');
320
+
321
+ const saveToGalleryAndSaveFileCheck_iOS = () => {
322
+
323
+ // List of paths to scan
324
+ const SCAN_PATHS = [
325
+ path.resolve(__dirname, '../src/certificate'),
326
+ path.resolve(__dirname, '../src/pages'),
327
+ path.resolve(__dirname, '../src/js'),
328
+ path.resolve(__dirname, '../src/app.f7')
329
+ ];
330
+
331
+ // Directory to exclude
332
+ const EXCLUDED_DIR = path.resolve(__dirname, '../src/js/Ads');
333
+
334
+ const ANDROID_MANIFEST_PATH = path.resolve(__dirname, '../android/app/src/main/AndroidManifest.xml');
335
+
336
+
337
+ // Match iOS-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5-ios.js) not in comments
338
+ const IOS_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*-ios\.js['"]/m;
339
+
340
+ // Match Android-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5.js) not in comments
341
+ const ANDROID_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*\.js['"]/m;
342
+
343
+
344
+
345
+
346
+
347
+ const ALLOWED_EXTENSIONS = ['.js', '.f7'];
348
+ const isMac = os.platform() === 'darwin';
349
+
350
+ let iosImportFound = false;
351
+ let androidImportFound = false;
352
+
353
+ // Files to skip completely (full or partial match)
354
+ const SKIP_FILES = [
355
+ 'pdf-3.11.174.min.js',
356
+ 'pdf.worker-3.11.174.min.js'
357
+ ,'index.browser.js'
358
+ ];
359
+
360
+
361
+ function scanDirectory(dir) {
362
+
363
+ /*
364
+ //######################### DO NOT DELETE THIS - START [Appid base validation] #####################################
365
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
366
+ const appUniqueId = config.android?.APP_UNIQUE_ID;
367
+ if (appUniqueId == "206") return;
368
+ //######################### DO NOT DELETE THIS - END [Appid base validation] #####################################
369
+ */
370
+
371
+ const stat = fs.statSync(dir);
372
+
373
+ if (stat.isFile()) {
374
+
375
+ // 🔥 Skip files in SKIP_FILES array
376
+ const baseName = path.basename(dir);
377
+ if (SKIP_FILES.includes(baseName)) {
378
+ // Just skip silently
379
+ return;
380
+ }
381
+
382
+ // Only scan allowed file extensions
383
+ if (ALLOWED_EXTENSIONS.some(ext => dir.endsWith(ext))) {
384
+ process.stdout.write(`\r🔍 Scanning: ${dir} `);
385
+
386
+ const content = fs.readFileSync(dir, 'utf8');
387
+
388
+ if (IOS_FILE_REGEX.test(content)) {
389
+ iosImportFound = true;
390
+ if (!isMac) {
391
+ console.error(`\n❌ ERROR: iOS-specific import detected in: ${dir}`);
392
+ console.error(`🚫 STOPPED: This file should not be imported in Android/Windows/Linux builds.\n`);
393
+ process.exit(1);
394
+ }
395
+ }
396
+ else if (ANDROID_FILE_REGEX.test(content) && !content.includes('-ios.js')) {
397
+ androidImportFound = true;
398
+ }
399
+ }
400
+ }
401
+ else if (stat.isDirectory()) {
402
+ if (dir === EXCLUDED_DIR || path.basename(dir) === 'node_modules') return;
403
+
404
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
405
+ for (let entry of entries) {
406
+ scanDirectory(path.join(dir, entry.name));
407
+ }
408
+ }
409
+ }
410
+
411
+
412
+ // Run scan on all specified paths
413
+ for (let scanPath of SCAN_PATHS) {
414
+ if (fs.existsSync(scanPath)) {
415
+ scanDirectory(scanPath);
416
+ }
417
+ }
418
+
419
+
420
+
421
+ /* // Check src folder
422
+ if (!fs.existsSync(ROOT_DIR)) {
423
+ console.warn(`⚠️ Warning: 'src' directory not found at: ${ROOT_DIR}`);
424
+ return;
425
+ } */
426
+
427
+ //scanDirectory(ROOT_DIR);
428
+
429
+ // iOS Checks
430
+ if (isMac && !iosImportFound) {
431
+ console.warn(`⚠️ WARNING: You're on macOS but no iOS version (saveToGalleryAndSaveAnyFile-x.x-ios.js) found.`);
432
+ process.exit(1);
433
+ } else if (isMac && iosImportFound) {
434
+ console.log('✅ iOS version detected for macOS build.');
435
+ } else if (!iosImportFound) {
436
+ console.log('✅ No iOS-specific imports detected for non-macOS.');
437
+ }
438
+
439
+ // Android Checks
440
+ if (androidImportFound) {
441
+ console.log("📱 Android version of saveToGalleryAndSaveAnyFile detected. Checking AndroidManifest.xml...");
442
+
443
+ if (!fs.existsSync(ANDROID_MANIFEST_PATH)) {
444
+ console.error("❌ AndroidManifest.xml not found. Cannot add requestLegacyExternalStorage attribute.");
445
+ return;
446
+ }
447
+
448
+ let manifestContent = fs.readFileSync(ANDROID_MANIFEST_PATH, 'utf8');
449
+
450
+ if (!manifestContent.includes('android:requestLegacyExternalStorage="true"')) {
451
+ console.log("Adding android:requestLegacyExternalStorage=\"true\" to <application> tag...");
452
+
453
+ manifestContent = manifestContent.replace(
454
+ /<application([^>]*)>/,
455
+ (match, attrs) => {
456
+ if (attrs.includes('android:requestLegacyExternalStorage')) return match;
457
+ return `<application${attrs} android:requestLegacyExternalStorage="true">`;
458
+ }
459
+ );
460
+
461
+ fs.writeFileSync(ANDROID_MANIFEST_PATH, manifestContent, 'utf8');
462
+ console.log("✅ android:requestLegacyExternalStorage=\"true\" added successfully.");
463
+ } else {
464
+ console.log("ℹ️ android:requestLegacyExternalStorage already exists in AndroidManifest.xml.");
465
+ }
466
+ } else {
467
+ console.log("✅ No Android saveToGalleryAndSaveAnyFile imports detected.");
468
+ }
469
+ };
470
+
471
+ saveToGalleryAndSaveFileCheck_iOS();
472
+ // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists END
473
+
474
+
475
+
476
+
477
+
478
+
479
+
480
+
481
+
482
+
483
+
484
+
485
+
486
+ /*
487
+ // Clean up AppleDouble files (._*) created by macOS START
488
+ if (process.platform === 'darwin') {
489
+ try {
490
+ console.log('🧹 Cleaning up AppleDouble files (._*)...');
491
+ execSync(`find . -name '._*' -delete`);
492
+ console.log('✅ AppleDouble files removed.');
493
+ } catch (err) {
494
+ console.warn('⚠️ Failed to remove AppleDouble files:', err.message);
495
+ }
496
+ } else {
497
+ console.log('ℹ️ Skipping AppleDouble cleanup — not a macOS machine.');
498
+ }
499
+
500
+ // Clean up AppleDouble files (._*) created by macOS END
501
+ */
502
+
503
+
504
+
505
+
506
+
507
+
508
+ //In routes.js file check static import START
509
+
510
+ const routesPath = path.join(process.cwd(), 'src', 'js', 'routes.js');
511
+ const routesContent = fs.readFileSync(routesPath, 'utf-8');
512
+
513
+ let inBlockComment = false;
514
+ const lines = routesContent.split('\n');
515
+
516
+ const allowedImport = `import HomePage from '../pages/home.f7';`;
517
+ const badImportRegex = /^[ \t]*import\s+[\w{}*,\s]*\s+from\s+['"].+\.f7['"]\s*;/;
518
+ const badImports = [];
519
+
520
+ lines.forEach((line, index) => {
521
+ const trimmed = line.trim();
522
+
523
+ // Handle block comment start and end
524
+ if (trimmed.startsWith('/*')) inBlockComment = true;
525
+ if (inBlockComment && trimmed.endsWith('*/')) {
526
+ inBlockComment = false;
527
+ return;
528
+ }
529
+
530
+ // Skip if inside block comment or line comment
531
+ if (inBlockComment || trimmed.startsWith('//')) return;
532
+
533
+ // Match static .f7 import
534
+ if (badImportRegex.test(trimmed) && trimmed !== allowedImport) {
535
+ badImports.push({ line: trimmed, number: index + 1 });
536
+ }
537
+ });
538
+
539
+ if (badImports.length > 0) {
540
+ console.error('\n❌ ERROR: Detected disallowed static imports of .f7 files in routes.js\n');
541
+ console.error(`⚠️ Only this static import is allowed:\n ${allowedImport}\n`);
542
+ console.error(`🔧 Please convert other imports to async dynamic imports like this:\n`);
543
+ console.error(`
544
+
545
+ import HomePage from '../pages/home.f7';
546
+
547
+ const routes = [
548
+ {
549
+ path: '/',
550
+ component:HomePage,
551
+ },
552
+ {
553
+ path: '/ProfilePage/',
554
+ async async({ resolve }) {
555
+ const page = await import('../pages/profile.f7');
556
+ resolve({ component: page.default });
557
+ },
558
+ }]
559
+ `);
560
+
561
+ badImports.forEach(({ line, number }) => {
562
+ console.error(`${number}: ${line}`);
563
+ });
564
+
565
+ process.exit(1);
566
+ } else {
567
+ console.log('✅ routes.js passed the .f7 import check.');
568
+ }
569
+
570
+ //In routes.js file check static import END
571
+
572
+
573
+
574
+
575
+
576
+
577
+
578
+
579
+
580
+
581
+
582
+
583
+
584
+ // Check and change the "BridgeWebViewClient.java" file START
585
+ /*
586
+ For crash issue due to low memory problem, we need to modify the onRenderProcessGone method in BridgeWebViewClient.java.
587
+ */
588
+
589
+
590
+ const bridgeWebViewClientFilePath = path.join(process.cwd(), 'node_modules', '@capacitor/android/capacitor/src/main/java/com/getcapacitor', 'BridgeWebViewClient.java');
591
+
592
+ // Read the file
593
+ if (!fs.existsSync(bridgeWebViewClientFilePath)) {
594
+ console.error('❌ Error: BridgeWebViewClient.java not found.');
595
+ process.exit(1);
596
+ }
597
+
598
+ let fileContent = fs.readFileSync(bridgeWebViewClientFilePath, 'utf8');
599
+
600
+ // Define old and new code
601
+ const oldCodeStart = `@Override
602
+ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
603
+ super.onRenderProcessGone(view, detail);
604
+ boolean result = false;
605
+
606
+ List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
607
+ if (webViewListeners != null) {
608
+ for (WebViewListener listener : bridge.getWebViewListeners()) {
609
+ result = listener.onRenderProcessGone(view, detail) || result;
610
+ }
611
+ }
612
+
613
+ return result;
614
+ }`;
615
+
616
+ const newCode = `@Override
617
+ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
618
+ super.onRenderProcessGone(view, detail);
619
+
620
+ boolean result = false;
621
+
622
+ List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
623
+ if (webViewListeners != null) {
624
+ for (WebViewListener listener : bridge.getWebViewListeners()) {
625
+ result = listener.onRenderProcessGone(view, detail) || result;
626
+ }
627
+ }
628
+
629
+ if (!result) {
630
+ // If no one handled it, handle it ourselves!
631
+
632
+ /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
633
+ if (detail.didCrash()) {
634
+ //Log.e("CapacitorWebView", "WebView crashed internally!");
635
+ } else {
636
+ //Log.w("CapacitorWebView", "WebView was killed by system (low memory) internally!");
637
+ }
638
+ }*/
639
+
640
+ view.post(() -> {
641
+ Toast.makeText(view.getContext(), "Reloading due to low memory issue", Toast.LENGTH_SHORT).show();
642
+ });
643
+
644
+ view.reload(); // Safely reload WebView
645
+
646
+ return true; // We handled it
647
+ }
648
+
649
+ return result;
650
+ }`;
651
+
652
+ // Step 1: Update method if needed
653
+ let updated = false;
654
+
655
+ if (fileContent.includes(oldCodeStart)) {
656
+ console.log('✅ Found old onRenderProcessGone method. Replacing it...');
657
+ fileContent = fileContent.replace(oldCodeStart, newCode);
658
+ updated = true;
659
+ } else if (fileContent.includes(newCode)) {
660
+ console.log('ℹ️ Method already updated. No changes needed in "BridgeWebViewClient.java".');
661
+ } else {
662
+ console.error('❌ Error: Neither old nor new code found. Unexpected content.');
663
+ process.exit(1);
664
+ }
665
+
666
+ // Step 2: Check and add import if missing
667
+ const importToast = 'import android.widget.Toast;';
668
+ if (!fileContent.includes(importToast)) {
669
+ console.log('✅ Adding missing import for Toast...');
670
+ const importRegex = /import\s+[^;]+;/g;
671
+ const matches = [...fileContent.matchAll(importRegex)];
672
+
673
+ if (matches.length > 0) {
674
+ const lastImport = matches[matches.length - 1];
675
+ const insertPosition = lastImport.index + lastImport[0].length;
676
+ fileContent = fileContent.slice(0, insertPosition) + `\n${importToast}` + fileContent.slice(insertPosition);
677
+ updated = true;
678
+ } else {
679
+ console.error('❌ Error: No import section found in file.');
680
+ process.exit(1);
681
+ }
682
+ } else {
683
+ console.log('ℹ️ Import for Toast already exists. No changes needed.');
684
+ }
685
+
686
+ // Step 3: Save if updated
687
+ if (updated) {
688
+ fs.writeFileSync(bridgeWebViewClientFilePath, fileContent, 'utf8');
689
+ console.log('✅ File updated successfully.');
690
+ } else {
691
+ console.log('ℹ️ No changes needed.');
692
+ }
693
+
694
+
695
+
696
+
697
+ // Check and change the "BridgeWebViewClient.java" file END
698
+
699
+
700
+
701
+
702
+
703
+
704
+
705
+
706
+ /*
707
+ // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file START
708
+
709
+ // Build the path dynamically like you requested
710
+ const gradlePath = path.join(
711
+ process.cwd(),
712
+ 'android',
713
+ 'build.gradle'
714
+ );
715
+
716
+ // Read the existing build.gradle
717
+ let gradleContent = fs.readFileSync(gradlePath, 'utf8');
718
+
719
+ // Add `ext.kotlin_version` if it's not already there
720
+ if (!gradleContent.includes('ext.kotlin_version')) {
721
+ gradleContent = gradleContent.replace(
722
+ /buildscript\s*{/,
723
+ `buildscript {\n ext.kotlin_version = '2.1.0'`
724
+ );
725
+ }
726
+
727
+ // Add Kotlin classpath if it's not already there
728
+ if (!gradleContent.includes('org.jetbrains.kotlin:kotlin-gradle-plugin')) {
729
+ gradleContent = gradleContent.replace(
730
+ /dependencies\s*{([\s\S]*?)classpath 'com.android.tools.build:gradle:8.7.2'/,
731
+ `dependencies {\n classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")\n$1classpath 'com.android.tools.build:gradle:8.7.2'`
732
+ );
733
+ }
734
+
735
+ // Write back the modified content
736
+ fs.writeFileSync(gradlePath, gradleContent, 'utf8');
737
+
738
+ console.log(' Kotlin version updated in build.gradle.');
739
+
740
+ // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file END
741
+ */
742
+
743
+
744
+
745
+
746
+
747
+
748
+
749
+
750
+ let _admobConfig;
751
+
752
+
753
+
754
+ const androidPlatformPath = path.join(process.cwd(), 'android');
755
+ const iosPlatformPath = path.join(process.cwd(), 'ios');
756
+ const pluginPath = path.join(process.cwd(), 'node_modules', 'emi-indo-cordova-plugin-admob', 'plugin.xml');
757
+ const infoPlistPath = path.join(process.cwd(), 'ios', 'App', 'App', 'Info.plist');
758
+ const resourcesPath = path.join(process.cwd(), 'resources', 'res');
759
+ const androidResPath = path.join(process.cwd(), 'android', 'app', 'src', 'main', 'res');
760
+ const localNotificationsPluginPath = path.join(process.cwd(), 'node_modules', '@capacitor', 'local-notifications');
761
+
762
+ function fileExists(filePath) {
763
+ return fs.existsSync(filePath);
764
+ }
765
+
766
+ function copyFolderSync(source, target) {
767
+ if (!fs.existsSync(target)) {
768
+ fs.mkdirSync(target, { recursive: true });
769
+ }
770
+
771
+ fs.readdirSync(source).forEach(file => {
772
+ const sourceFile = path.join(source, file);
773
+ const targetFile = path.join(target, file);
774
+
775
+ if (fs.lstatSync(sourceFile).isDirectory()) {
776
+ copyFolderSync(sourceFile, targetFile);
777
+ } else {
778
+ fs.copyFileSync(sourceFile, targetFile);
779
+ }
780
+ });
781
+ }
782
+
783
+ function checkAndCopyResources() {
784
+ if (fileExists(resourcesPath)) {
785
+ copyFolderSync(resourcesPath, androidResPath);
786
+ console.log('✅ Successfully copied resources/res to android/app/src/main/res.');
787
+ } else {
788
+ console.log('resources/res folder not found.');
789
+
790
+ if (fileExists(localNotificationsPluginPath)) {
791
+ throw new Error('❌ resources/res is required for @capacitor/local-notifications. Stopping execution.');
792
+ }
793
+ }
794
+ }
795
+
796
+
797
+
798
+
799
+
800
+
801
+
802
+
803
+ function getAdMobConfig() {
804
+ if (!fileExists(configPath)) {
805
+ throw new Error('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
806
+ }
807
+
808
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
809
+ const admobConfig = config.plugins?.AdMob;
810
+
811
+ if (!admobConfig) {
812
+ throw new Error('❌ AdMob configuration is missing in capacitor.config.json.');
813
+ }
814
+
815
+ // Default to true if ADMOB_ENABLED is not specified
816
+ const isEnabled = admobConfig.ADMOB_ENABLED !== false;
817
+
818
+ if (!isEnabled) {
819
+ return { ADMOB_ENABLED: false }; // Skip further validation
820
+ }
821
+
822
+ if (!admobConfig.APP_ID_ANDROID || !admobConfig.APP_ID_IOS) {
823
+ throw new Error(' ❌ AdMob configuration is incomplete. Ensure APP_ID_ANDROID and APP_ID_IOS are defined.');
824
+ }
825
+
826
+ return {
827
+ ADMOB_ENABLED: true,
828
+ APP_ID_ANDROID: admobConfig.APP_ID_ANDROID,
829
+ APP_ID_IOS: admobConfig.APP_ID_IOS,
830
+ USE_LITE_ADS: admobConfig.USE_LITE_ADS === "lite",
831
+ };
832
+ }
833
+
834
+ function validateAndroidBuildOptions() {
835
+
836
+
837
+ if (!fileExists(configPath)) {
838
+ console.log('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
839
+ process.exit(1);
840
+ }
841
+
842
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
843
+
844
+ const targetAppId=config.appId
845
+
846
+ const buildOptions = config.android?.buildOptions;
847
+
848
+ if (!buildOptions) {
849
+ console.log('❌ Missing android.buildOptions in capacitor.config.json.');
850
+ process.exit(1);
851
+ }
852
+
853
+ const requiredProps = [
854
+ 'keystorePath',
855
+ 'keystorePassword',
856
+ 'keystoreAlias',
857
+ 'keystoreAliasPassword',
858
+ 'releaseType',
859
+ 'signingType'
860
+ ];
861
+
862
+ const missing = requiredProps.filter(prop => !buildOptions[prop]);
863
+
864
+ if (missing.length > 0) {
865
+ console.log('❌ Missing properties android.buildOptions in capacitor.config.json.');
866
+ process.exit(1);
867
+ }
868
+
869
+
870
+ const keystorePath=buildOptions.keystorePath
871
+ const keyFileName = path.basename(keystorePath);
872
+
873
+
874
+
875
+ const keystoreMap = {
876
+ "gameskey.jks": [
877
+ "com.cube.blaster",
878
+ ],
879
+ "htmleditorkeystoke.jks": [
880
+ "com.HTML.AngularJS.Codeplay",
881
+ "com.html.codeplay.pro",
882
+ "com.bootstrap.code.play",
883
+ "com.kids.learning.master",
884
+ "com.Simple.Barcode.Scanner"
885
+ ]
886
+ };
887
+
888
+ // find which keystore is required for the given targetAppId
889
+ let requiredKey = "newappskey.jks"; // default
890
+ for (const [keyFile, appIds] of Object.entries(keystoreMap)) {
891
+ if (appIds.includes(targetAppId)) {
892
+ requiredKey = keyFile;
893
+ break;
894
+ }
895
+ }
896
+
897
+ // validate
898
+ if (keyFileName !== requiredKey) {
899
+ console.log(`❌ The keystore path is mismatched. Expected ${requiredKey} for ${targetAppId}, but got ${keyFileName}`);
900
+ process.exit(1);
901
+ }
902
+
903
+
904
+
905
+
906
+
907
+ // optionally return them
908
+ //return buildOptions;
909
+ }
910
+
911
+ function updatePluginXml(admobConfig) {
912
+ if (!fileExists(pluginPath)) {
913
+ console.error(' ❌ plugin.xml not found. Ensure the plugin is installed.');
914
+ return;
915
+ }
916
+
917
+ let pluginContent = fs.readFileSync(pluginPath, 'utf8');
918
+
919
+ pluginContent = pluginContent
920
+ .replace(/<preference name="APP_ID_ANDROID" default=".*?" \/>/, `<preference name="APP_ID_ANDROID" default="${admobConfig.APP_ID_ANDROID}" />`)
921
+ .replace(/<preference name="APP_ID_IOS" default=".*?" \/>/, `<preference name="APP_ID_IOS" default="${admobConfig.APP_ID_IOS}" />`);
922
+
923
+ fs.writeFileSync(pluginPath, pluginContent, 'utf8');
924
+ console.log('✅ AdMob IDs successfully updated in plugin.xml');
925
+ }
926
+
927
+ function updateInfoPlist(admobConfig) {
928
+ if (!fileExists(infoPlistPath)) {
929
+ console.error(' ❌ Info.plist not found. Ensure you have built the iOS project.');
930
+ return;
931
+ }
932
+
933
+ const plistContent = fs.readFileSync(infoPlistPath, 'utf8');
934
+ const plistData = plist.parse(plistContent);
935
+
936
+ plistData.GADApplicationIdentifier = admobConfig.APP_ID_IOS;
937
+ plistData.NSUserTrackingUsageDescription = 'This identifier will be used to deliver personalized ads to you.';
938
+ plistData.GADDelayAppMeasurementInit = true;
939
+
940
+ const updatedPlistContent = plist.build(plistData);
941
+ fs.writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
942
+ console.log('AdMob IDs and additional configurations successfully updated in Info.plist');
943
+ }
944
+
945
+
946
+ try {
947
+ if (!fileExists(configPath)) {
948
+ throw new Error(' ❌ capacitor.config.json not found. Skipping setup.');
949
+ }
950
+
951
+ if (!fileExists(androidPlatformPath) && !fileExists(iosPlatformPath)) {
952
+ throw new Error('Neither Android nor iOS platforms are found. Ensure platforms are added to your Capacitor project.');
953
+ }
954
+
955
+ checkAndCopyResources();
956
+
957
+
958
+
959
+ _admobConfig = getAdMobConfig();
960
+
961
+
962
+
963
+
964
+
965
+ // Proceed only if ADMOB_ENABLED is true
966
+ if (_admobConfig.ADMOB_ENABLED) {
967
+ if (fileExists(androidPlatformPath)) {
968
+ updatePluginXml(_admobConfig);
969
+ }
970
+
971
+ if (fileExists(iosPlatformPath)) {
972
+ updateInfoPlist(_admobConfig);
973
+ }
974
+ }
975
+
976
+
977
+ } catch (error) {
978
+ console.error(error.message);
979
+ process.exit(1); // Stop execution if there's a critical error
980
+ }
981
+
982
+
983
+
984
+ validateAndroidBuildOptions();
985
+
986
+
987
+
988
+
989
+
990
+
991
+ // Check all the codeplays plugins version START
992
+
993
+
994
+ const readline = require('readline');
995
+
996
+
997
+ //const srcDir = path.join(__dirname, 'src');
998
+ const srcDir = path.join(process.cwd(), 'src');
999
+ let outdatedPlugins = [];
1000
+
1001
+ function parseVersion(ver) {
1002
+ return ver.split('.').map(n => parseInt(n, 10));
1003
+ }
1004
+
1005
+ function compareVersions(v1, v2) {
1006
+ const [a1, b1] = parseVersion(v1);
1007
+ const [a2, b2] = parseVersion(v2);
1008
+ if (a1 !== a2) return a1 - a2;
1009
+ return b1 - b2;
1010
+ }
1011
+
1012
+ function walkSync(dir, filelist = []) {
1013
+ fs.readdirSync(dir).forEach(file => {
1014
+ const fullPath = path.join(dir, file);
1015
+ const stat = fs.statSync(fullPath);
1016
+ if (stat.isDirectory()) {
1017
+ walkSync(fullPath, filelist);
1018
+ } else {
1019
+ filelist.push(fullPath);
1020
+ }
1021
+ });
1022
+ return filelist;
1023
+ }
1024
+
1025
+
1026
+
1027
+ function getSearchRoot(plugin) {
1028
+ return path.join(srcDir, plugin.baseDir || 'js');
1029
+ }
1030
+
1031
+
1032
+
1033
+
1034
+
1035
+
1036
+
1037
+
1038
+
1039
+
1040
+
1041
+
1042
+
1043
+
1044
+
1045
+
1046
+
1047
+
1048
+
1049
+
1050
+ /*############################################## AUTO DOWNLOAD FROM SERVER START #####################################*/
1051
+
1052
+ // ============================================================
1053
+ // 🔥 AUTO PLUGIN UPDATE SYSTEM (MANDATORY UPDATES)
1054
+ // ============================================================
1055
+
1056
+ const https = require("https");
1057
+
1058
+ /**
1059
+ * Check if file exists on server using HEAD request
1060
+ */
1061
+ function urlExists(url) {
1062
+ return new Promise(resolve => {
1063
+ const req = https.request(url, { method: "HEAD" }, res => {
1064
+ resolve(res.statusCode === 200);
1065
+ });
1066
+
1067
+ req.on("error", () => resolve(false));
1068
+ req.end();
1069
+ });
1070
+ }
1071
+
1072
+ /**
1073
+ * Download file from server
1074
+ */
1075
+ function downloadFile(url, dest) {
1076
+ return new Promise((resolve, reject) => {
1077
+ const file = fs.createWriteStream(dest);
1078
+
1079
+ https.get(url, response => {
1080
+ if (response.statusCode !== 200) {
1081
+ reject("Download failed");
1082
+ return;
1083
+ }
1084
+
1085
+ response.pipe(file);
1086
+
1087
+ file.on("finish", () => {
1088
+ file.close(resolve);
1089
+ });
1090
+ }).on("error", err => {
1091
+ fs.unlink(dest, () => {});
1092
+ reject(err);
1093
+ });
1094
+ });
1095
+ }
1096
+
1097
+ /**
1098
+ * Update imports across src folder
1099
+ * Replaces old filename → new filename
1100
+ */
1101
+ /**
1102
+ * Update imports across project
1103
+ * Replaces old filename → new filename
1104
+ */
1105
+
1106
+ const VITE_ALIAS_ONLY = [
1107
+ "common",
1108
+ "admob-emi",
1109
+ "localization",
1110
+ "theme",
1111
+ "certificatejs",
1112
+ "ffmpeg"
1113
+ ];
1114
+ function updateImports(oldName, newName) {
1115
+
1116
+ const projectRoot = process.cwd();
1117
+
1118
+ const viteFiles = [
1119
+ path.join(projectRoot, "vite.config.js"),
1120
+ path.join(projectRoot, "vite.config.mjs")
1121
+ ];
1122
+
1123
+ // Detect if alias-only plugin
1124
+ const isAliasOnly = VITE_ALIAS_ONLY.some(p =>
1125
+ oldName.includes(p)
1126
+ );
1127
+
1128
+ //--------------------------------------------------
1129
+ // 1️⃣ ALWAYS update vite config
1130
+ //--------------------------------------------------
1131
+ viteFiles.forEach(file => {
1132
+ if (!fs.existsSync(file)) return;
1133
+
1134
+ let content = fs.readFileSync(file, "utf8");
1135
+
1136
+ if (content.includes(oldName)) {
1137
+ content = content.replaceAll(oldName, newName);
1138
+ fs.writeFileSync(file, content);
1139
+
1140
+ console.log(`✏️ Updated alias in ${path.basename(file)}`);
1141
+ }
1142
+ });
1143
+
1144
+ //--------------------------------------------------
1145
+ // 2️⃣ If alias-only → STOP here
1146
+ //--------------------------------------------------
1147
+ if (isAliasOnly) {
1148
+ console.log(`⚡ Alias-only plugin → skipped full scan`);
1149
+ return;
1150
+ }
1151
+
1152
+ //--------------------------------------------------
1153
+ // 3️⃣ Otherwise scan src
1154
+ //--------------------------------------------------
1155
+ const srcDir = path.join(projectRoot, "src");
1156
+
1157
+ function walk(dir) {
1158
+ fs.readdirSync(dir).forEach(file => {
1159
+
1160
+ if (["node_modules","android","ios","dist",".git"].includes(file))
1161
+ return;
1162
+
1163
+ const full = path.join(dir, file);
1164
+ const stat = fs.statSync(full);
1165
+
1166
+ if (stat.isDirectory()) {
1167
+ walk(full);
1168
+ }
1169
+ else if (
1170
+ (full.endsWith(".js") ||
1171
+ full.endsWith(".f7") ||
1172
+ full.endsWith(".mjs")) &&
1173
+ !full.endsWith(".min.js")
1174
+ ) {
1175
+ let content = fs.readFileSync(full, "utf8");
1176
+
1177
+ if (content.includes(oldName)) {
1178
+ content = content.replaceAll(oldName, newName);
1179
+ fs.writeFileSync(full, content);
1180
+
1181
+ console.log(`✏️ Updated import in ${path.relative(projectRoot, full)}`);
1182
+ }
1183
+ }
1184
+ });
1185
+ }
1186
+
1187
+ if (fs.existsSync(srcDir)) {
1188
+ walk(srcDir);
1189
+ }
1190
+ }
1191
+
1192
+
1193
+
1194
+ /**
1195
+ * Auto-update a plugin file
1196
+ * Returns TRUE if success
1197
+ * Returns FALSE if fallback to manual needed
1198
+ */
1199
+
1200
+
1201
+ let _serverVersions = null;
1202
+ async function fetchVersions() {
1203
+
1204
+ if (_serverVersions) return _serverVersions;
1205
+
1206
+ return new Promise((resolve) => {
1207
+
1208
+ https.get(
1209
+ "https://htmlcodeplay.com/code-play-plugin/versions.json",
1210
+ { timeout: 5000 },
1211
+ res => {
1212
+
1213
+ let data = "";
1214
+
1215
+ res.on("data", chunk => data += chunk);
1216
+
1217
+ res.on("end", () => {
1218
+
1219
+ try {
1220
+
1221
+ _serverVersions = JSON.parse(data);
1222
+
1223
+ resolve(_serverVersions);
1224
+
1225
+ } catch {
1226
+
1227
+ resolve(null);
1228
+
1229
+ }
1230
+
1231
+ });
1232
+
1233
+ }
1234
+
1235
+ ).on("error", () => resolve(null));
1236
+
1237
+ });
1238
+
1239
+ }
1240
+
1241
+
1242
+
1243
+ async function autoUpdatePlugin(pluginDef, pluginInfo) {
1244
+
1245
+ const versions = await fetchVersions();
1246
+
1247
+ if (!versions) {
1248
+ console.log("⚠️ versions.json not reachable");
1249
+ return false;
1250
+ }
1251
+
1252
+ const oldFullPath = path.join(srcDir, pluginInfo.name);
1253
+ const oldFileName = path.basename(oldFullPath);
1254
+
1255
+ // Extract base name
1256
+ const baseName = oldFileName.replace(/-\d+\.\d+.*$/, "");
1257
+
1258
+ const latestVersion = versions[baseName];
1259
+
1260
+ if (!latestVersion) {
1261
+ console.log(`❌ No version entry for ${baseName}`);
1262
+ return false;
1263
+ }
1264
+
1265
+ // ===============================
1266
+ // FOLDER PLUGIN UPDATE
1267
+ // ===============================
1268
+ if (pluginDef.isFolder) {
1269
+
1270
+ const zipName = `${baseName}-${latestVersion}.zip`;
1271
+ const url = `https://htmlcodeplay.com/code-play-plugin/${zipName}`;
1272
+
1273
+ const destRoot = path.join(srcDir, pluginDef.destDir || pluginDef.baseDir || '');
1274
+ const oldPath = path.join(destRoot, pluginInfo.name);
1275
+ const newPath = path.join(destRoot, `${baseName}-${latestVersion}`);
1276
+
1277
+ if (!(await urlExists(url))) return false;
1278
+
1279
+ fs.rmSync(oldPath, { recursive: true, force: true });
1280
+
1281
+ await downloadAndExtractZip(url, newPath);
1282
+
1283
+ console.log(`✅ Folder updated → ${baseName}-${latestVersion}`);
1284
+
1285
+ return true;
1286
+ }
1287
+
1288
+ // ===============================
1289
+ // FILE PLUGIN UPDATE
1290
+ // ===============================
1291
+
1292
+ const pluginDir = path.dirname(oldFullPath);
1293
+
1294
+ const variants = [
1295
+ `${baseName}-${latestVersion}.js`,
1296
+ `${baseName}-${latestVersion}-ios.js`
1297
+ ];
1298
+
1299
+ let downloaded = [];
1300
+
1301
+ // Download new files first
1302
+ for (const fileName of variants) {
1303
+
1304
+ const url = `https://htmlcodeplay.com/code-play-plugin/${fileName}`;
1305
+
1306
+ console.log(`🔍 Checking latest: ${fileName}`);
1307
+
1308
+ if (await urlExists(url)) {
1309
+
1310
+ const destPath = path.join(pluginDir, fileName);
1311
+
1312
+ await downloadFile(url, destPath);
1313
+
1314
+ downloaded.push(fileName);
1315
+
1316
+ console.log(`⬇ Downloaded → ${fileName}`);
1317
+ }
1318
+ }
1319
+
1320
+ // If nothing downloaded → skip
1321
+ if (downloaded.length === 0) {
1322
+ console.log(`❌ No files downloaded for ${baseName}`);
1323
+ return false;
1324
+ }
1325
+
1326
+ // Remove old versions AFTER download
1327
+ const existingFiles = fs.readdirSync(pluginDir);
1328
+
1329
+ existingFiles.forEach(file => {
1330
+
1331
+ if (
1332
+ file.startsWith(baseName + "-") &&
1333
+ file.endsWith(".js") &&
1334
+ !downloaded.includes(file)
1335
+ ) {
1336
+
1337
+ const oldPath = path.join(pluginDir, file);
1338
+
1339
+ fs.unlinkSync(oldPath);
1340
+
1341
+ console.log(`🗑 Removed old file ${file}`);
1342
+ }
1343
+
1344
+ });
1345
+
1346
+ const newFileName = `${baseName}-${latestVersion}.js`;
1347
+
1348
+ updateImports(oldFileName, newFileName);
1349
+
1350
+ console.log(`✅ Updated → ${newFileName}`);
1351
+
1352
+ return true;
1353
+ }
1354
+
1355
+
1356
+
1357
+
1358
+ /*############################################## AUTO DOWNLOAD FROM SERVER END #####################################*/
1359
+
1360
+
1361
+
1362
+
1363
+
1364
+
1365
+
1366
+
1367
+
1368
+
1369
+
1370
+
1371
+
1372
+
1373
+
1374
+
1375
+
1376
+
1377
+ async function loadPluginVersions() {
1378
+
1379
+ if (!USE_LIVE_SERVER_VERSION) {
1380
+ console.log("ℹ️ Using local plugin versions (offline mode).");
1381
+ return;
1382
+ }
1383
+
1384
+ console.log("🌐 Fetching plugin versions from server...");
1385
+
1386
+ const versions = await fetchVersions();
1387
+
1388
+ if (!versions || typeof versions !== "object") {
1389
+ console.log("⚠️ Server unavailable or invalid versions.json. Falling back to local versions.");
1390
+ return;
1391
+ }
1392
+
1393
+ requiredPlugins.forEach(plugin => {
1394
+
1395
+ if (!plugin.name) return;
1396
+
1397
+ if (versions[plugin.name]) {
1398
+ plugin.minVersion = versions[plugin.name];
1399
+ }
1400
+
1401
+ });
1402
+
1403
+ console.log("✅ Plugin versions loaded from server.");
1404
+
1405
+ }
1406
+
1407
+
1408
+
1409
+ let hasMandatoryUpdate = false;
1410
+ function checkPlugins() {
1411
+ return new Promise(async (resolve, reject) => {
1412
+ const files = walkSync(srcDir);
1413
+ const outdatedPlugins = [];
1414
+ let hasMandatoryUpdate = false;
1415
+
1416
+ for (const plugin of requiredPlugins) {
1417
+ const searchRoot = getSearchRoot(plugin);
1418
+
1419
+ // ---------- Folder plugins ----------
1420
+ if (plugin.isFolder) {
1421
+ if (!fs.existsSync(searchRoot)) continue;
1422
+
1423
+ const subDirs = fs.readdirSync(searchRoot)
1424
+ .map(name => path.join(searchRoot, name))
1425
+ .filter(p => fs.statSync(p).isDirectory());
1426
+
1427
+ for (const dir of subDirs) {
1428
+ const relativePath = path.relative(searchRoot, dir).replace(/\\/g, '/');
1429
+ const match = plugin.pattern.exec(relativePath);
1430
+
1431
+ if (match) {
1432
+ const currentVersion = match[1];
1433
+
1434
+ if (compareVersions(currentVersion, plugin.minVersion) < 0) {
1435
+ outdatedPlugins.push({
1436
+ name: relativePath,
1437
+ currentVersion,
1438
+ requiredVersion: plugin.minVersion,
1439
+ mandatoryUpdate: plugin.mandatoryUpdate === true
1440
+ });
1441
+
1442
+ if (plugin.mandatoryUpdate) {
1443
+ hasMandatoryUpdate = true;
1444
+ }
1445
+ }
1446
+ }
1447
+ }
1448
+ continue;
1449
+ }
1450
+
1451
+ // ---------- File plugins ----------
1452
+ const matchedFile = files.find(file =>
1453
+ file.startsWith(searchRoot) && plugin.pattern.test(file)
1454
+ );
1455
+
1456
+ if (matchedFile) {
1457
+ const match = plugin.pattern.exec(matchedFile);
1458
+ if (match) {
1459
+ const currentVersion = match[1];
1460
+ const isBeta = !!match[2];
1461
+
1462
+ const cmp = plugin.pattern.source.includes('beta')
1463
+ ? compareWithBeta(currentVersion, plugin.minVersion, isBeta)
1464
+ : compareVersions(currentVersion, plugin.minVersion);
1465
+
1466
+ if (cmp < 0) {
1467
+ outdatedPlugins.push({
1468
+ name: path.relative(srcDir, matchedFile),
1469
+ currentVersion: isBeta ? `${currentVersion}-beta` : currentVersion,
1470
+ requiredVersion: plugin.minVersion,
1471
+ mandatoryUpdate: plugin.mandatoryUpdate === true
1472
+ });
1473
+
1474
+ if (plugin.mandatoryUpdate) {
1475
+ hasMandatoryUpdate = true;
1476
+ }
1477
+ }
1478
+ }
1479
+ }
1480
+ }
1481
+
1482
+ // ---------- Result handling ----------
1483
+ if (outdatedPlugins.length > 0) {
1484
+ console.log('\n❗ The following plugins are outdated:\n');
1485
+
1486
+ outdatedPlugins.forEach( p => {
1487
+ const tag = p.mandatoryUpdate ? '🔥 MANDATORY' : '';
1488
+ console.log(
1489
+ ` ⚠️ - ${p.name} (Current: ${p.currentVersion}, Required: ${p.requiredVersion}) ${tag}`
1490
+ );
1491
+ });
1492
+
1493
+ // 🚨 Mandatory update → stop build
1494
+ /* if (hasMandatoryUpdate) {
1495
+ console.log('\n🚫 One or more plugins require a mandatory update.');
1496
+ console.log('❌ Build cancelled. Please update mandatory plugins and try again.');
1497
+ process.exit(1);
1498
+ } */
1499
+
1500
+
1501
+
1502
+
1503
+
1504
+ if (hasMandatoryUpdate) {
1505
+
1506
+ //--------------------------------------------------
1507
+ // 🚫 AUTO UPDATE DISABLED
1508
+ //--------------------------------------------------
1509
+ if (!ENABLE_AUTO_UPDATE) {
1510
+ console.log("\n🚫 Auto-update disabled.");
1511
+ console.log("❌ Manual update required.");
1512
+ process.exit(1);
1513
+ }
1514
+
1515
+ //--------------------------------------------------
1516
+ // 🔥 AUTO UPDATE ENABLED
1517
+ //--------------------------------------------------
1518
+ console.log("\n🔥 Mandatory plugins outdated. Trying auto-update...\n");
1519
+
1520
+
1521
+
1522
+ let autoFailed = false;
1523
+
1524
+ for (const p of outdatedPlugins.filter(x => x.mandatoryUpdate)) {
1525
+
1526
+ const pluginDef = requiredPlugins.find(def =>
1527
+ def.pattern.test(p.name)
1528
+ );
1529
+
1530
+ if (!pluginDef) continue;
1531
+
1532
+ const success = await autoUpdatePlugin(
1533
+ pluginDef,
1534
+ p
1535
+ );
1536
+
1537
+ if (!success) {
1538
+ autoFailed = true;
1539
+
1540
+ const pluginDef = requiredPlugins.find(def =>
1541
+ def.pattern.test(p.name)
1542
+ );
1543
+
1544
+ console.log(`❌ Manual update required for ${p.name}`);
1545
+
1546
+ if (pluginDef) {
1547
+ console.log(`👉 Required minimum version: ${pluginDef.minVersion}`);
1548
+ }
1549
+ }
1550
+ }
1551
+
1552
+ // 🚨 Fallback to manual if any failed
1553
+ if (autoFailed) {
1554
+ console.log('\n🚫 One or more plugins require manual update.');
1555
+ console.log('❌ Build cancelled. Please update mandatory plugins.');
1556
+ process.exit(1);
1557
+ }
1558
+
1559
+ console.log('\n🎉 All mandatory plugins auto-updated!');
1560
+ }
1561
+
1562
+
1563
+
1564
+
1565
+
1566
+
1567
+ // Optional updates → ask user
1568
+ const rl = readline.createInterface({
1569
+ input: process.stdin,
1570
+ output: process.stdout
1571
+ });
1572
+
1573
+ rl.question(
1574
+ '\nAre you sure you want to continue without updating these plugins? (y/n): ',
1575
+ answer => {
1576
+ rl.close();
1577
+
1578
+ if (answer.toLowerCase() !== 'y') {
1579
+ console.log('\n❌ Build cancelled due to outdated plugins.');
1580
+ process.exit(1);
1581
+ } else {
1582
+ console.log('\n✅ Continuing build...');
1583
+ resolve();
1584
+ }
1585
+ }
1586
+ );
1587
+ } else {
1588
+ console.log('✅ All plugin versions are up to date.');
1589
+ resolve();
1590
+ }
1591
+ });
1592
+ }
1593
+
1594
+
1595
+
1596
+
1597
+
1598
+
1599
+
1600
+
1601
+ // Check all the codeplays plugins version START
1602
+
1603
+
1604
+
1605
+
1606
+ // ====================================================================
1607
+ // AUTO-ADD esbuild.drop: ['console','debugger'] to vite.config.js / mjs
1608
+ // ====================================================================
1609
+
1610
+
1611
+
1612
+ const checkAndupdateDropInViteConfig = () => {
1613
+
1614
+ const possibleFiles = [
1615
+ "vite.config.js",
1616
+ "vite.config.mjs"
1617
+ ];
1618
+
1619
+ // Detect existing config file
1620
+ const viteConfigPath = possibleFiles
1621
+ .map(file => path.join(process.cwd(), file))
1622
+ .find(filePath => fs.existsSync(filePath));
1623
+
1624
+ if (!viteConfigPath) {
1625
+ console.warn("⚠️ No vite config found. Skipping.");
1626
+ return;
1627
+ }
1628
+
1629
+ //console.log("📄 Using:", viteConfigPath.split("/").pop());
1630
+
1631
+ let viteContent = fs.readFileSync(viteConfigPath, "utf8");
1632
+
1633
+ // Skip if already exists
1634
+ if (/drop\s*:\s*\[.*['"]console['"].*\]/.test(viteContent)) {
1635
+ console.log("ℹ️ vite.config.(m)js already Updated. Skipping...");
1636
+ return;
1637
+ }
1638
+
1639
+ console.log("🔧 Adding esbuild.drop ...");
1640
+
1641
+ // If esbuild block exists
1642
+ if (/esbuild\s*:\s*{/.test(viteContent)) {
1643
+ viteContent = viteContent.replace(
1644
+ /esbuild\s*:\s*{([\s\S]*?)(^ {0,8})}/m,
1645
+ (full, inner, indent) => {
1646
+
1647
+ let lines = inner
1648
+ .split("\n")
1649
+ .map(l => l.trim())
1650
+ .filter(Boolean);
1651
+
1652
+ // Fix last comma
1653
+ if (lines.length > 0) {
1654
+ lines[lines.length - 1] =
1655
+ lines[lines.length - 1].replace(/,+$/, "") + ",";
1656
+ }
1657
+
1658
+ // Re-indent
1659
+ lines = lines.map(l => indent + " " + l);
1660
+
1661
+ // Add drop
1662
+ lines.push(`${indent} drop: ['console','debugger'],`);
1663
+
1664
+ return `esbuild: {\n${lines.join("\n")}\n${indent}}`;
1665
+ }
1666
+ );
1667
+ }
1668
+
1669
+ // If esbuild missing
1670
+ else {
1671
+ viteContent = viteContent.replace(
1672
+ /export default defineConfig\s*\(\s*{/,
1673
+ m => `${m}\n esbuild: {\n drop: ['console','debugger'],\n },`
1674
+ );
1675
+ }
1676
+
1677
+ fs.writeFileSync(viteConfigPath, viteContent, "utf8");
1678
+ console.log("✅ vite.config.(m)js Updated successfully.");
1679
+ };
1680
+
1681
+
1682
+
1683
+
1684
+
1685
+
1686
+
1687
+
1688
+
1689
+
1690
+ const compareVersion = (v1, v2) => {
1691
+ const a = v1.split(".").map(Number);
1692
+ const b = v2.split(".").map(Number);
1693
+
1694
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
1695
+ const num1 = a[i] || 0;
1696
+ const num2 = b[i] || 0;
1697
+ if (num1 > num2) return 1;
1698
+ if (num1 < num2) return -1;
1699
+ }
1700
+ return 0;
1701
+ };
1702
+
1703
+
1704
+
1705
+
1706
+ const admobConfigPath = path.join('src', 'js','Ads', 'admob-ad-configuration.json');
1707
+
1708
+ const checkAdmobConfigurationProperty=()=>{
1709
+
1710
+
1711
+ if (!_admobConfig.ADMOB_ENABLED)
1712
+ {
1713
+ console.log("ℹ️ Admob is not enabled so 'admob-ad-configuration.json' checking is skipping...");
1714
+ return;
1715
+ }
1716
+
1717
+
1718
+ const REQUIRED_CONFIG_KEYS = [
1719
+ "isKidsApp",
1720
+ "isTesting",
1721
+ "isConsoleLogEnabled",
1722
+ "bannerEnabled",
1723
+ "interstitialEnabled",
1724
+ "appOpenEnabled",
1725
+ "rewardVideoEnabled",
1726
+ "rewardInterstitialEnabled",
1727
+ "collapsibleEnabled",
1728
+ "isLandScape",
1729
+ "isOverlappingEnable",
1730
+ "bannerTypeAndroid",
1731
+ "bannerTypeiOS",
1732
+ "bannerTopSpaceColor",
1733
+ "interstitialLoadScreenTextColor",
1734
+ "interstitialLoadScreenBackgroundColor",
1735
+ "beforeBannerSpace",
1736
+ "whenShow",
1737
+ "minimumClick",
1738
+ "interstitialTimeOut",
1739
+ "interstitialFirstTimeOut",
1740
+ "appOpenAdsTimeOut",
1741
+ "maxRetryCount",
1742
+ "retrySecondsAr",
1743
+ "appOpenPerSession",
1744
+ "interstitialPerSession",
1745
+ "appOpenFirstTimeOut"
1746
+ ];
1747
+
1748
+
1749
+
1750
+
1751
+
1752
+ let admobConfigInJson;
1753
+
1754
+ try {
1755
+ admobConfigInJson = JSON.parse(readFileSync(admobConfigPath, "utf8"));
1756
+ } catch (err) {
1757
+ console.error("❌ Failed to read admob-ad-configuration.json", err);
1758
+ process.exit(1);
1759
+ }
1760
+
1761
+ // ✅ Validate config object exists
1762
+ if (!admobConfigInJson.config) {
1763
+ console.error('❌ "config" object is missing in admob-ad-configuration.json');
1764
+ process.exit(1);
1765
+ }
1766
+
1767
+
1768
+ const admobConfigMinVersion="1.5"
1769
+
1770
+ if (compareVersion(admobConfigInJson.VERSION, admobConfigMinVersion) < 0) {
1771
+ console.error(`❌ Please use at-least version ${admobConfigMinVersion} in "src/js/Ads/admob-ad-configuration.json"`);
1772
+ process.exit(1);
1773
+ }
1774
+
1775
+
1776
+ const config = admobConfigInJson.config;
1777
+
1778
+ // ✅ Find missing properties
1779
+ const missingKeys = REQUIRED_CONFIG_KEYS.filter(
1780
+ key => !(key in config)
1781
+ );
1782
+
1783
+
1784
+
1785
+ if (missingKeys.length > 0) {
1786
+ console.error("❌ Missing required configuration keys. Please check it in 'src/js/Ads/admob-ad-configuration.json'");
1787
+
1788
+ missingKeys.forEach(k => console.error(" - " + k));
1789
+ process.exit(1);
1790
+ }
1791
+
1792
+
1793
+ console.log('✅ All keys exist. in "admob-ad-configuration.json file" Configuration looks good.');
1794
+ }
1795
+
1796
+
1797
+
1798
+ function ensureGitignoreEntry(entry) {
1799
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
1800
+
1801
+ // If .gitignore doesn't exist, create it
1802
+ if (!fs.existsSync(gitignorePath)) {
1803
+ fs.writeFileSync(gitignorePath, `${entry}\n`, 'utf8');
1804
+ console.log(`✅ .gitignore created and added: ${entry}`);
1805
+ return;
1806
+ }
1807
+
1808
+ const content = fs.readFileSync(gitignorePath, 'utf8');
1809
+
1810
+ // Normalize lines (trim + remove trailing slashes for comparison)
1811
+ const lines = content
1812
+ .split(/\r?\n/)
1813
+ .map(l => l.trim());
1814
+
1815
+ const normalizedEntry = entry.replace(/\/$/, '');
1816
+
1817
+ const exists = lines.some(
1818
+ line => line.replace(/\/$/, '') === normalizedEntry
1819
+ );
1820
+
1821
+ if (exists) {
1822
+ console.log(`ℹ️ .gitignore already contains: ${entry}`);
1823
+ return;
1824
+ }
1825
+
1826
+ // Ensure file ends with newline
1827
+ const separator = content.endsWith('\n') ? '' : '\n';
1828
+
1829
+ fs.appendFileSync(gitignorePath, `${separator}${entry}\n`, 'utf8');
1830
+ console.log(`✅ Added to .gitignore: ${entry}`);
1831
+ }
1832
+
1833
+
1834
+ ensureGitignoreEntry('buildCodeplay/');
1835
+
1836
+
1837
+ // Run the validation
1838
+ (async () => {
1839
+
1840
+ await loadPluginVersions(); // 🔥 NEW
1841
+
1842
+ await checkPlugins();
1843
+ checkAndupdateDropInViteConfig();
1844
+ checkAdmobConfigurationProperty()
1845
+ })();
1846
+
1847
+
1848
+ // ======================================================
1849
+ // Validate theme folder location (src/js/theme is NOT allowed)
1850
+ // ======================================================
1851
+
1852
+ function validateThemeFolderLocation() {
1853
+ const oldThemePath = path.join(process.cwd(), 'src', 'js', 'theme');
1854
+ const newThemePath = path.join(process.cwd(), 'src', 'theme');
1855
+
1856
+ // ❌ Block old structure
1857
+ if (fs.existsSync(oldThemePath)) {
1858
+ console.error(
1859
+ '\n❌ INVALID PROJECT STRUCTURE DETECTED\n' +
1860
+ '--------------------------------------------------\n' +
1861
+ 'The "theme" folder must NOT be inside:\n' +
1862
+ ' src/js/theme\n\n' +
1863
+ '✅ Correct location is:\n' +
1864
+ ' src/theme\n\n' +
1865
+ '🛑 Please move the folder and re-run the build.\n'
1866
+ );
1867
+ process.exit(1);
1868
+ }
1869
+
1870
+ // ⚠️ Optional warning if new theme folder is missing
1871
+ if (!fs.existsSync(newThemePath)) {
1872
+ console.warn(
1873
+ '\n⚠️ WARNING: "src/theme" folder not found.\n' +
1874
+ 'If your app uses themes, please ensure it exists.\n'
1875
+ );
1876
+ } else {
1877
+ console.log('✅ Theme folder structure validated (src/theme).');
1878
+ }
1879
+ }
1880
+ validateThemeFolderLocation()
1881
+
1882
+
1883
+
1884
+ const validateAndRestoreSignDetails=()=>{
1885
+
1886
+ // Read config file
1887
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
1888
+
1889
+ // Ensure android and buildOptions exist
1890
+ if (!config.android) config.android = {};
1891
+ if (!config.android.buildOptions) config.android.buildOptions = {};
1892
+
1893
+ // Update only if changed
1894
+ let updated = false;
1895
+
1896
+ if (config.android.buildOptions.releaseType !== 'AAB') {
1897
+ config.android.buildOptions.releaseType = 'AAB';
1898
+ updated = true;
1899
+ }
1900
+
1901
+ if (config.android.buildOptions.signingType !== 'jarsigner') {
1902
+ config.android.buildOptions.signingType = 'jarsigner';
1903
+ updated = true;
1904
+ }
1905
+
1906
+ // Write back only if modified
1907
+ if (updated) {
1908
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
1909
+ console.log('capacitor.config.json updated successfully.');
1910
+ } else {
1911
+ console.log('No changes needed.');
1912
+ }
1913
+
1914
+ }
1915
+
1916
+ validateAndRestoreSignDetails()
1917
+
1918
+
1919
+ /*
1920
+ Release Notes
1921
+
1922
+ 5.1
1923
+ Kotlin version update is commented. Previously admob is not worked if not update the kotlin version to higher version
1924
+
1739
1925
  */