codeplay-common 2.1.13 → 2.1.15

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,1231 +1,1259 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const plist = require('plist');
4
-
5
- const { readFileSync } = require("fs");
6
-
7
-
8
-
9
- const configPath = path.join(process.cwd(), 'capacitor.config.json');
10
-
11
- // Expected plugin list with minimum versions
12
- const requiredPlugins = [
13
- { pattern: /backbutton-(\d+\.\d+)\.js$/, minVersion: '1.6', required: true, baseDir: 'js' },
14
- { pattern: /common-(\d+\.\d+)\.js$/, minVersion: '5.2', required: true, baseDir: 'js' },
15
- { pattern: /localization_settings-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js' },
16
- { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.3', required: true, baseDir: 'js' },
17
- { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js' },
18
- { pattern: /localNotification_AppSettings-(\d+\.\d+)\.js$/, minVersion: '1.0', required: true, baseDir: 'js' },
19
- { pattern: /onesignal-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js' },
20
- { pattern: /saveToGalleryAndSaveAnyFile-(\d+\.\d+)(-ios)?\.js$/, minVersion: '3.0', required: true, baseDir: 'js' },
21
- { pattern: /Ads[\/\\]IAP-(\d+\.\d+)$/, minVersion: '2.5', isFolder: true , required: true, baseDir: 'js' },
22
- { pattern: /Ads[\/\\]admob-emi-(\d+\.\d+)\.js$/, minVersion: '3.3', required: true, baseDir: 'js' },
23
-
24
- // New added plugins
25
- { pattern: /video-player-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js' },
26
- { pattern: /image-cropper-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js' },
27
-
28
- { pattern: /common-(\d+\.\d+)\.less$/, minVersion: '1.4', required: true, baseDir: 'assets/css' },
29
-
30
-
31
- // New folders
32
- { pattern: /editor-(\d+\.\d+)$/, minVersion: '1.8', isFolder: true, required: true, baseDir: 'js' },
33
- { pattern: /ffmpeg-(\d+\.\d+)$/, minVersion: '1.3', isFolder: true, required: true, baseDir: 'js' },
34
- { pattern: /theme-(\d+\.\d+)$/, minVersion: '1.4', isFolder: true , required: true, baseDir: 'js' }
35
- ];
36
-
37
-
38
-
39
-
40
-
41
-
42
- //Check codeplay-common latest version installed or not Start
43
- const { execSync } = require('child_process');
44
-
45
- function getInstalledVersion(packageName) {
46
- try {
47
- const packageJsonPath = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
48
- if (fs.existsSync(packageJsonPath)) {
49
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
50
- return packageJson.version;
51
- }
52
- } catch (error) {
53
- return null;
54
- }
55
- return null;
56
- }
57
-
58
- function getLatestVersion(packageName) {
59
- try {
60
- return execSync(`npm view ${packageName} version`).toString().trim();
61
- } catch (error) {
62
- console.error(`Failed to fetch latest version for ${packageName}`);
63
- return null;
64
- }
65
- }
66
-
67
- function checkPackageVersion() {
68
- const packageName = 'codeplay-common';
69
- const installedVersion = getInstalledVersion(packageName);
70
- const latestVersion = getLatestVersion(packageName);
71
-
72
- if (!installedVersion) {
73
- console.error(`${packageName} is not installed. Please install it using "npm install ${packageName}".`);
74
- process.exit(1);
75
- }
76
-
77
- if (installedVersion !== latestVersion) {
78
- 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`);
79
- process.exit(1);
80
- }
81
-
82
- console.log(`${packageName} is up to date (version ${installedVersion}).`);
83
- }
84
-
85
- // Run package version check before executing the main script
86
- try {
87
- checkPackageVersion();
88
- } catch (error) {
89
- console.error(error.message);
90
- process.exit(1);
91
- }
92
-
93
- //Check codeplay-common latest version installed or not END
94
-
95
-
96
-
97
-
98
-
99
-
100
- const checkAppUniqueId=()=>{
101
-
102
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
103
-
104
- const appUniqueId = config.android?.APP_UNIQUE_ID;
105
- const RESIZEABLE_ACTIVITY = config.android?.RESIZEABLE_ACTIVITY;
106
- const orientation = config.android?.ORIENTATION;
107
-
108
-
109
- let logErrorMessage="";
110
-
111
- // 1️⃣ Check if it’s missing
112
- if (RESIZEABLE_ACTIVITY === undefined) {
113
- logErrorMessage+='❌ Missing android.RESIZEABLE_ACTIVITY option in capacitor.config.json.\n';
114
- }
115
-
116
- // 2️⃣ Check if it’s not boolean (true/false only)
117
- else if (typeof RESIZEABLE_ACTIVITY !== 'boolean') {
118
- logErrorMessage+='❌ Invalid android.RESIZEABLE_ACTIVITY value. Please use only true or false (without quotes).\n';
119
- }
120
-
121
-
122
-
123
- if (!orientation) {
124
- logErrorMessage+='❌ Missing android.ORIENTATION option in capacitor.config.json.\n';
125
- }
126
-
127
- else if(orientation!="portrait" && orientation!="landscape" && orientation!="auto")
128
- {
129
- logErrorMessage+='❌ Spelling mistake in android.ORIENTATION option in capacitor.config.json. Please use only ["portrait" "landscape" "auto"]\n';
130
- }
131
-
132
-
133
- if (!appUniqueId) {
134
- logErrorMessage+='❌ APP_UNIQUE_ID is missing in capacitor.config.json.';
135
- }
136
-
137
- else if (!Number.isInteger(appUniqueId)) {
138
- logErrorMessage+='❌ APP_UNIQUE_ID must be an integer. Example: 1, 2, 3, etc.';
139
- }
140
-
141
-
142
-
143
- if(logErrorMessage!="")
144
- {
145
- console.error(logErrorMessage);
146
- process.exit(1)
147
- }
148
-
149
-
150
- console.log(`✅ APP_UNIQUE_ID is valid: ${appUniqueId}`);
151
-
152
- }
153
-
154
- checkAppUniqueId();
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
- //@Codemirror check and install/uninstall the packages START
163
- //const fs = require("fs");
164
- //const path = require("path");
165
- //const { execSync } = require("child_process");
166
-
167
- const baseDir = path.join(__dirname, "..", "src", "js");
168
-
169
- // Step 1: Find highest versioned folder like `editor-1.6`
170
- const editorDirs = fs.readdirSync(baseDir)
171
- .filter(name => /^editor-\d+\.\d+$/.test(name))
172
- .sort((a, b) => {
173
- const getVersion = str => str.match(/(\d+)\.(\d+)/).slice(1).map(Number);
174
- const [aMajor, aMinor] = getVersion(a);
175
- const [bMajor, bMinor] = getVersion(b);
176
- return bMajor - aMajor || bMinor - aMinor;
177
- });
178
-
179
- if (editorDirs.length === 0) {
180
-
181
- console.log("@Codemirror used editor(s) are not found")
182
- //console.error("❌ No editor-x.x folders found in src/js.");
183
- //process.exit(1);
184
- }
185
- else
186
- {
187
-
188
- const latestEditorDir = editorDirs.sort((a, b) => {
189
- const versionA = parseFloat(a.split('-')[1]);
190
- const versionB = parseFloat(b.split('-')[1]);
191
- return versionB - versionA;
192
- })[0];
193
-
194
- //const latestEditorDir = editorDirs[editorDirs.length - 1];
195
- const runJsPath = path.join(baseDir, latestEditorDir, "run.js");
196
-
197
- if (!fs.existsSync(runJsPath)) {
198
- console.error(`❌ run.js not found in ${latestEditorDir}`);
199
- process.exit(1);
200
- }
201
-
202
- // Step 2: Execute the run.js file
203
- console.log(`🚀 Executing ${runJsPath}...`);
204
- execSync(`node "${runJsPath}"`, { stdio: "inherit" });
205
- }
206
-
207
- //@Codemirror check and install/uninstall the packages END
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
-
218
-
219
-
220
- // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists START
221
-
222
- const os = require('os');
223
-
224
- const saveToGalleryAndSaveFileCheck_iOS = () => {
225
-
226
- // List of paths to scan
227
- const SCAN_PATHS = [
228
- path.resolve(__dirname, '../src/certificate'),
229
- path.resolve(__dirname, '../src/pages'),
230
- path.resolve(__dirname, '../src/js'),
231
- path.resolve(__dirname, '../src/app.f7')
232
- ];
233
-
234
- // Directory to exclude
235
- const EXCLUDED_DIR = path.resolve(__dirname, '../src/js/Ads');
236
-
237
- const ANDROID_MANIFEST_PATH = path.resolve(__dirname, '../android/app/src/main/AndroidManifest.xml');
238
-
239
-
240
- // Match iOS-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5-ios.js) not in comments
241
- const IOS_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*-ios\.js['"]/m;
242
-
243
- // Match Android-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5.js) not in comments
244
- const ANDROID_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*\.js['"]/m;
245
-
246
-
247
-
248
-
249
-
250
- const ALLOWED_EXTENSIONS = ['.js', '.f7'];
251
- const isMac = os.platform() === 'darwin';
252
-
253
- let iosImportFound = false;
254
- let androidImportFound = false;
255
-
256
- // Files to skip completely (full or partial match)
257
- const SKIP_FILES = [
258
- 'pdf-3.11.174.min.js',
259
- 'pdf.worker-3.11.174.min.js'
260
- ,'index.browser.js'
261
- ];
262
-
263
-
264
- function scanDirectory(dir) {
265
-
266
- /*
267
- //######################### DO NOT DELETE THIS - START [Appid base validation] #####################################
268
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
269
- const appUniqueId = config.android?.APP_UNIQUE_ID;
270
- if (appUniqueId == "206") return;
271
- //######################### DO NOT DELETE THIS - END [Appid base validation] #####################################
272
- */
273
-
274
- const stat = fs.statSync(dir);
275
-
276
- if (stat.isFile()) {
277
-
278
- // 🔥 Skip files in SKIP_FILES array
279
- const baseName = path.basename(dir);
280
- if (SKIP_FILES.includes(baseName)) {
281
- // Just skip silently
282
- return;
283
- }
284
-
285
- // Only scan allowed file extensions
286
- if (ALLOWED_EXTENSIONS.some(ext => dir.endsWith(ext))) {
287
- process.stdout.write(`\r🔍 Scanning: ${dir} `);
288
-
289
- const content = fs.readFileSync(dir, 'utf8');
290
-
291
- if (IOS_FILE_REGEX.test(content)) {
292
- iosImportFound = true;
293
- if (!isMac) {
294
- console.error(`\n❌ ERROR: iOS-specific import detected in: ${dir}`);
295
- console.error(`🚫 STOPPED: This file should not be imported in Android/Windows/Linux builds.\n`);
296
- process.exit(1);
297
- }
298
- }
299
- else if (ANDROID_FILE_REGEX.test(content) && !content.includes('-ios.js')) {
300
- androidImportFound = true;
301
- }
302
- }
303
- }
304
- else if (stat.isDirectory()) {
305
- if (dir === EXCLUDED_DIR || path.basename(dir) === 'node_modules') return;
306
-
307
- const entries = fs.readdirSync(dir, { withFileTypes: true });
308
- for (let entry of entries) {
309
- scanDirectory(path.join(dir, entry.name));
310
- }
311
- }
312
- }
313
-
314
-
315
- // Run scan on all specified paths
316
- for (let scanPath of SCAN_PATHS) {
317
- if (fs.existsSync(scanPath)) {
318
- scanDirectory(scanPath);
319
- }
320
- }
321
-
322
-
323
-
324
- /* // Check src folder
325
- if (!fs.existsSync(ROOT_DIR)) {
326
- console.warn(`⚠️ Warning: 'src' directory not found at: ${ROOT_DIR}`);
327
- return;
328
- } */
329
-
330
- //scanDirectory(ROOT_DIR);
331
-
332
- // iOS Checks
333
- if (isMac && !iosImportFound) {
334
- console.warn(`⚠️ WARNING: You're on macOS but no iOS version (saveToGalleryAndSaveAnyFile-x.x-ios.js) found.`);
335
- process.exit(1);
336
- } else if (isMac && iosImportFound) {
337
- console.log('✅ iOS version detected for macOS build.');
338
- } else if (!iosImportFound) {
339
- console.log('✅ No iOS-specific imports detected for non-macOS.');
340
- }
341
-
342
- // Android Checks
343
- if (androidImportFound) {
344
- console.log("📱 Android version of saveToGalleryAndSaveAnyFile detected. Checking AndroidManifest.xml...");
345
-
346
- if (!fs.existsSync(ANDROID_MANIFEST_PATH)) {
347
- console.error("❌ AndroidManifest.xml not found. Cannot add requestLegacyExternalStorage attribute.");
348
- return;
349
- }
350
-
351
- let manifestContent = fs.readFileSync(ANDROID_MANIFEST_PATH, 'utf8');
352
-
353
- if (!manifestContent.includes('android:requestLegacyExternalStorage="true"')) {
354
- console.log("Adding android:requestLegacyExternalStorage=\"true\" to <application> tag...");
355
-
356
- manifestContent = manifestContent.replace(
357
- /<application([^>]*)>/,
358
- (match, attrs) => {
359
- if (attrs.includes('android:requestLegacyExternalStorage')) return match;
360
- return `<application${attrs} android:requestLegacyExternalStorage="true">`;
361
- }
362
- );
363
-
364
- fs.writeFileSync(ANDROID_MANIFEST_PATH, manifestContent, 'utf8');
365
- console.log("✅ android:requestLegacyExternalStorage=\"true\" added successfully.");
366
- } else {
367
- console.log("ℹ️ android:requestLegacyExternalStorage already exists in AndroidManifest.xml.");
368
- }
369
- } else {
370
- console.log("✅ No Android saveToGalleryAndSaveAnyFile imports detected.");
371
- }
372
- };
373
-
374
- saveToGalleryAndSaveFileCheck_iOS();
375
- // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists END
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
384
-
385
-
386
-
387
-
388
-
389
- /*
390
- // Clean up AppleDouble files (._*) created by macOS START
391
- if (process.platform === 'darwin') {
392
- try {
393
- console.log('🧹 Cleaning up AppleDouble files (._*)...');
394
- execSync(`find . -name '._*' -delete`);
395
- console.log('✅ AppleDouble files removed.');
396
- } catch (err) {
397
- console.warn('⚠️ Failed to remove AppleDouble files:', err.message);
398
- }
399
- } else {
400
- console.log('ℹ️ Skipping AppleDouble cleanup — not a macOS machine.');
401
- }
402
-
403
- // Clean up AppleDouble files (._*) created by macOS END
404
- */
405
-
406
-
407
-
408
-
409
-
410
-
411
- //In routes.js file check static import START
412
-
413
- const routesPath = path.join(process.cwd(), 'src', 'js', 'routes.js');
414
- const routesContent = fs.readFileSync(routesPath, 'utf-8');
415
-
416
- let inBlockComment = false;
417
- const lines = routesContent.split('\n');
418
-
419
- const allowedImport = `import HomePage from '../pages/home.f7';`;
420
- const badImportRegex = /^[ \t]*import\s+[\w{}*,\s]*\s+from\s+['"].+\.f7['"]\s*;/;
421
- const badImports = [];
422
-
423
- lines.forEach((line, index) => {
424
- const trimmed = line.trim();
425
-
426
- // Handle block comment start and end
427
- if (trimmed.startsWith('/*')) inBlockComment = true;
428
- if (inBlockComment && trimmed.endsWith('*/')) {
429
- inBlockComment = false;
430
- return;
431
- }
432
-
433
- // Skip if inside block comment or line comment
434
- if (inBlockComment || trimmed.startsWith('//')) return;
435
-
436
- // Match static .f7 import
437
- if (badImportRegex.test(trimmed) && trimmed !== allowedImport) {
438
- badImports.push({ line: trimmed, number: index + 1 });
439
- }
440
- });
441
-
442
- if (badImports.length > 0) {
443
- console.error('\n❌ ERROR: Detected disallowed static imports of .f7 files in routes.js\n');
444
- console.error(`⚠️ Only this static import is allowed:\n ${allowedImport}\n`);
445
- console.error(`🔧 Please convert other imports to async dynamic imports like this:\n`);
446
- console.error(`
447
-
448
- import HomePage from '../pages/home.f7';
449
-
450
- const routes = [
451
- {
452
- path: '/',
453
- component:HomePage,
454
- },
455
- {
456
- path: '/ProfilePage/',
457
- async async({ resolve }) {
458
- const page = await import('../pages/profile.f7');
459
- resolve({ component: page.default });
460
- },
461
- }]
462
- `);
463
-
464
- badImports.forEach(({ line, number }) => {
465
- console.error(`${number}: ${line}`);
466
- });
467
-
468
- process.exit(1);
469
- } else {
470
- console.log('✅ routes.js passed the .f7 import check.');
471
- }
472
-
473
- //In routes.js file check static import END
474
-
475
-
476
-
477
-
478
-
479
-
480
-
481
-
482
-
483
-
484
-
485
-
486
-
487
- // Check and change the "BridgeWebViewClient.java" file START
488
- /*
489
- For crash issue due to low memory problem, we need to modify the onRenderProcessGone method in BridgeWebViewClient.java.
490
- */
491
-
492
-
493
- const bridgeWebViewClientFilePath = path.join(process.cwd(), 'node_modules', '@capacitor/android/capacitor/src/main/java/com/getcapacitor', 'BridgeWebViewClient.java');
494
-
495
- // Read the file
496
- if (!fs.existsSync(bridgeWebViewClientFilePath)) {
497
- console.error('❌ Error: BridgeWebViewClient.java not found.');
498
- process.exit(1);
499
- }
500
-
501
- let fileContent = fs.readFileSync(bridgeWebViewClientFilePath, 'utf8');
502
-
503
- // Define old and new code
504
- const oldCodeStart = `@Override
505
- public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
506
- super.onRenderProcessGone(view, detail);
507
- boolean result = false;
508
-
509
- List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
510
- if (webViewListeners != null) {
511
- for (WebViewListener listener : bridge.getWebViewListeners()) {
512
- result = listener.onRenderProcessGone(view, detail) || result;
513
- }
514
- }
515
-
516
- return result;
517
- }`;
518
-
519
- const newCode = `@Override
520
- public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
521
- super.onRenderProcessGone(view, detail);
522
-
523
- boolean result = false;
524
-
525
- List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
526
- if (webViewListeners != null) {
527
- for (WebViewListener listener : bridge.getWebViewListeners()) {
528
- result = listener.onRenderProcessGone(view, detail) || result;
529
- }
530
- }
531
-
532
- if (!result) {
533
- // If no one handled it, handle it ourselves!
534
-
535
- /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
536
- if (detail.didCrash()) {
537
- //Log.e("CapacitorWebView", "WebView crashed internally!");
538
- } else {
539
- //Log.w("CapacitorWebView", "WebView was killed by system (low memory) internally!");
540
- }
541
- }*/
542
-
543
- view.post(() -> {
544
- Toast.makeText(view.getContext(), "Reloading due to low memory issue", Toast.LENGTH_SHORT).show();
545
- });
546
-
547
- view.reload(); // Safely reload WebView
548
-
549
- return true; // We handled it
550
- }
551
-
552
- return result;
553
- }`;
554
-
555
- // Step 1: Update method if needed
556
- let updated = false;
557
-
558
- if (fileContent.includes(oldCodeStart)) {
559
- console.log('✅ Found old onRenderProcessGone method. Replacing it...');
560
- fileContent = fileContent.replace(oldCodeStart, newCode);
561
- updated = true;
562
- } else if (fileContent.includes(newCode)) {
563
- console.log('ℹ️ Method already updated. No changes needed in "BridgeWebViewClient.java".');
564
- } else {
565
- console.error('❌ Error: Neither old nor new code found. Unexpected content.');
566
- process.exit(1);
567
- }
568
-
569
- // Step 2: Check and add import if missing
570
- const importToast = 'import android.widget.Toast;';
571
- if (!fileContent.includes(importToast)) {
572
- console.log('✅ Adding missing import for Toast...');
573
- const importRegex = /import\s+[^;]+;/g;
574
- const matches = [...fileContent.matchAll(importRegex)];
575
-
576
- if (matches.length > 0) {
577
- const lastImport = matches[matches.length - 1];
578
- const insertPosition = lastImport.index + lastImport[0].length;
579
- fileContent = fileContent.slice(0, insertPosition) + `\n${importToast}` + fileContent.slice(insertPosition);
580
- updated = true;
581
- } else {
582
- console.error(' Error: No import section found in file.');
583
- process.exit(1);
584
- }
585
- } else {
586
- console.log('ℹ️ Import for Toast already exists. No changes needed.');
587
- }
588
-
589
- // Step 3: Save if updated
590
- if (updated) {
591
- fs.writeFileSync(bridgeWebViewClientFilePath, fileContent, 'utf8');
592
- console.log('✅ File updated successfully.');
593
- } else {
594
- console.log('ℹ️ No changes needed.');
595
- }
596
-
597
-
598
-
599
-
600
- // Check and change the "BridgeWebViewClient.java" file END
601
-
602
-
603
-
604
-
605
-
606
-
607
-
608
-
609
- /*
610
- // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file START
611
-
612
- // Build the path dynamically like you requested
613
- const gradlePath = path.join(
614
- process.cwd(),
615
- 'android',
616
- 'build.gradle'
617
- );
618
-
619
- // Read the existing build.gradle
620
- let gradleContent = fs.readFileSync(gradlePath, 'utf8');
621
-
622
- // Add `ext.kotlin_version` if it's not already there
623
- if (!gradleContent.includes('ext.kotlin_version')) {
624
- gradleContent = gradleContent.replace(
625
- /buildscript\s*{/,
626
- `buildscript {\n ext.kotlin_version = '2.1.0'`
627
- );
628
- }
629
-
630
- // Add Kotlin classpath if it's not already there
631
- if (!gradleContent.includes('org.jetbrains.kotlin:kotlin-gradle-plugin')) {
632
- gradleContent = gradleContent.replace(
633
- /dependencies\s*{([\s\S]*?)classpath 'com.android.tools.build:gradle:8.7.2'/,
634
- `dependencies {\n classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")\n$1classpath 'com.android.tools.build:gradle:8.7.2'`
635
- );
636
- }
637
-
638
- // Write back the modified content
639
- fs.writeFileSync(gradlePath, gradleContent, 'utf8');
640
-
641
- console.log('✅ Kotlin version updated in build.gradle.');
642
-
643
- // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file END
644
- */
645
-
646
-
647
-
648
-
649
-
650
-
651
-
652
-
653
- let _admobConfig;
654
-
655
-
656
-
657
- const androidPlatformPath = path.join(process.cwd(), 'android');
658
- const iosPlatformPath = path.join(process.cwd(), 'ios');
659
- const pluginPath = path.join(process.cwd(), 'node_modules', 'emi-indo-cordova-plugin-admob', 'plugin.xml');
660
- const infoPlistPath = path.join(process.cwd(), 'ios', 'App', 'App', 'Info.plist');
661
- const resourcesPath = path.join(process.cwd(), 'resources', 'res');
662
- const androidResPath = path.join(process.cwd(), 'android', 'app', 'src', 'main', 'res');
663
- const localNotificationsPluginPath = path.join(process.cwd(), 'node_modules', '@capacitor', 'local-notifications');
664
-
665
- function fileExists(filePath) {
666
- return fs.existsSync(filePath);
667
- }
668
-
669
- function copyFolderSync(source, target) {
670
- if (!fs.existsSync(target)) {
671
- fs.mkdirSync(target, { recursive: true });
672
- }
673
-
674
- fs.readdirSync(source).forEach(file => {
675
- const sourceFile = path.join(source, file);
676
- const targetFile = path.join(target, file);
677
-
678
- if (fs.lstatSync(sourceFile).isDirectory()) {
679
- copyFolderSync(sourceFile, targetFile);
680
- } else {
681
- fs.copyFileSync(sourceFile, targetFile);
682
- }
683
- });
684
- }
685
-
686
- function checkAndCopyResources() {
687
- if (fileExists(resourcesPath)) {
688
- copyFolderSync(resourcesPath, androidResPath);
689
- console.log('✅ Successfully copied resources/res to android/app/src/main/res.');
690
- } else {
691
- console.log('resources/res folder not found.');
692
-
693
- if (fileExists(localNotificationsPluginPath)) {
694
- throw new Error('❌ resources/res is required for @capacitor/local-notifications. Stopping execution.');
695
- }
696
- }
697
- }
698
-
699
-
700
-
701
-
702
-
703
-
704
-
705
-
706
- function getAdMobConfig() {
707
- if (!fileExists(configPath)) {
708
- throw new Error(' capacitor.config.json not found. Ensure this is a Capacitor project.');
709
- }
710
-
711
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
712
- const admobConfig = config.plugins?.AdMob;
713
-
714
- if (!admobConfig) {
715
- throw new Error('❌ AdMob configuration is missing in capacitor.config.json.');
716
- }
717
-
718
- // Default to true if ADMOB_ENABLED is not specified
719
- const isEnabled = admobConfig.ADMOB_ENABLED !== false;
720
-
721
- if (!isEnabled) {
722
- return { ADMOB_ENABLED: false }; // Skip further validation
723
- }
724
-
725
- if (!admobConfig.APP_ID_ANDROID || !admobConfig.APP_ID_IOS) {
726
- throw new Error(' ❌ AdMob configuration is incomplete. Ensure APP_ID_ANDROID and APP_ID_IOS are defined.');
727
- }
728
-
729
- return {
730
- ADMOB_ENABLED: true,
731
- APP_ID_ANDROID: admobConfig.APP_ID_ANDROID,
732
- APP_ID_IOS: admobConfig.APP_ID_IOS,
733
- USE_LITE_ADS: admobConfig.USE_LITE_ADS === "lite",
734
- };
735
- }
736
-
737
- function validateAndroidBuildOptions() {
738
-
739
-
740
- if (!fileExists(configPath)) {
741
- console.log('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
742
- process.exit(1);
743
- }
744
-
745
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
746
-
747
- const targetAppId=config.appId
748
-
749
- const buildOptions = config.android?.buildOptions;
750
-
751
- if (!buildOptions) {
752
- console.log('❌ Missing android.buildOptions in capacitor.config.json.');
753
- process.exit(1);
754
- }
755
-
756
- const requiredProps = [
757
- 'keystorePath',
758
- 'keystorePassword',
759
- 'keystoreAlias',
760
- 'keystoreAliasPassword',
761
- 'releaseType',
762
- 'signingType'
763
- ];
764
-
765
- const missing = requiredProps.filter(prop => !buildOptions[prop]);
766
-
767
- if (missing.length > 0) {
768
- console.log('❌ Missing properties android.buildOptions in capacitor.config.json.');
769
- process.exit(1);
770
- }
771
-
772
-
773
- const keystorePath=buildOptions.keystorePath
774
- const keyFileName = path.basename(keystorePath);
775
-
776
-
777
-
778
- const keystoreMap = {
779
- "gameskey.jks": [
780
- "com.cube.blaster",
781
- ],
782
- "htmleditorkeystoke.jks": [
783
- "com.HTML.AngularJS.Codeplay",
784
- "com.html.codeplay.pro",
785
- "com.bootstrap.code.play",
786
- "com.kids.learning.master",
787
- "com.Simple.Barcode.Scanner"
788
- ]
789
- };
790
-
791
- // find which keystore is required for the given targetAppId
792
- let requiredKey = "newappskey.jks"; // default
793
- for (const [keyFile, appIds] of Object.entries(keystoreMap)) {
794
- if (appIds.includes(targetAppId)) {
795
- requiredKey = keyFile;
796
- break;
797
- }
798
- }
799
-
800
- // validate
801
- if (keyFileName !== requiredKey) {
802
- console.log(`❌ The keystore path is mismatched. Expected ${requiredKey} for ${targetAppId}, but got ${keyFileName}`);
803
- process.exit(1);
804
- }
805
-
806
-
807
-
808
-
809
-
810
- // optionally return them
811
- //return buildOptions;
812
- }
813
-
814
- function updatePluginXml(admobConfig) {
815
- if (!fileExists(pluginPath)) {
816
- console.error(' ❌ plugin.xml not found. Ensure the plugin is installed.');
817
- return;
818
- }
819
-
820
- let pluginContent = fs.readFileSync(pluginPath, 'utf8');
821
-
822
- pluginContent = pluginContent
823
- .replace(/<preference name="APP_ID_ANDROID" default=".*?" \/>/, `<preference name="APP_ID_ANDROID" default="${admobConfig.APP_ID_ANDROID}" />`)
824
- .replace(/<preference name="APP_ID_IOS" default=".*?" \/>/, `<preference name="APP_ID_IOS" default="${admobConfig.APP_ID_IOS}" />`);
825
-
826
- fs.writeFileSync(pluginPath, pluginContent, 'utf8');
827
- console.log('✅ AdMob IDs successfully updated in plugin.xml');
828
- }
829
-
830
- function updateInfoPlist(admobConfig) {
831
- if (!fileExists(infoPlistPath)) {
832
- console.error(' ❌ Info.plist not found. Ensure you have built the iOS project.');
833
- return;
834
- }
835
-
836
- const plistContent = fs.readFileSync(infoPlistPath, 'utf8');
837
- const plistData = plist.parse(plistContent);
838
-
839
- plistData.GADApplicationIdentifier = admobConfig.APP_ID_IOS;
840
- plistData.NSUserTrackingUsageDescription = 'This identifier will be used to deliver personalized ads to you.';
841
- plistData.GADDelayAppMeasurementInit = true;
842
-
843
- const updatedPlistContent = plist.build(plistData);
844
- fs.writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
845
- console.log('AdMob IDs and additional configurations successfully updated in Info.plist');
846
- }
847
-
848
-
849
- try {
850
- if (!fileExists(configPath)) {
851
- throw new Error(' ❌ capacitor.config.json not found. Skipping setup.');
852
- }
853
-
854
- if (!fileExists(androidPlatformPath) && !fileExists(iosPlatformPath)) {
855
- throw new Error('Neither Android nor iOS platforms are found. Ensure platforms are added to your Capacitor project.');
856
- }
857
-
858
- checkAndCopyResources();
859
-
860
-
861
-
862
- _admobConfig = getAdMobConfig();
863
-
864
-
865
-
866
-
867
-
868
- // Proceed only if ADMOB_ENABLED is true
869
- if (_admobConfig.ADMOB_ENABLED) {
870
- if (fileExists(androidPlatformPath)) {
871
- updatePluginXml(_admobConfig);
872
- }
873
-
874
- if (fileExists(iosPlatformPath)) {
875
- updateInfoPlist(_admobConfig);
876
- }
877
- }
878
-
879
-
880
- } catch (error) {
881
- console.error(error.message);
882
- process.exit(1); // Stop execution if there's a critical error
883
- }
884
-
885
-
886
-
887
- validateAndroidBuildOptions();
888
-
889
-
890
-
891
-
892
-
893
-
894
- // Check all the codeplays plugins version START
895
-
896
-
897
- const readline = require('readline');
898
-
899
-
900
- //const srcDir = path.join(__dirname, 'src');
901
- const srcDir = path.join(process.cwd(), 'src');
902
- let outdatedPlugins = [];
903
-
904
- function parseVersion(ver) {
905
- return ver.split('.').map(n => parseInt(n, 10));
906
- }
907
-
908
- function compareVersions(v1, v2) {
909
- const [a1, b1] = parseVersion(v1);
910
- const [a2, b2] = parseVersion(v2);
911
- if (a1 !== a2) return a1 - a2;
912
- return b1 - b2;
913
- }
914
-
915
- function walkSync(dir, filelist = []) {
916
- fs.readdirSync(dir).forEach(file => {
917
- const fullPath = path.join(dir, file);
918
- const stat = fs.statSync(fullPath);
919
- if (stat.isDirectory()) {
920
- walkSync(fullPath, filelist);
921
- } else {
922
- filelist.push(fullPath);
923
- }
924
- });
925
- return filelist;
926
- }
927
-
928
-
929
-
930
- function getSearchRoot(plugin) {
931
- return path.join(srcDir, plugin.baseDir || 'js');
932
- }
933
-
934
- function checkPlugins() {
935
- const files = walkSync(srcDir);
936
-
937
- for (const plugin of requiredPlugins) {
938
-
939
- const searchRoot = getSearchRoot(plugin);
940
-
941
- // ---------- FOLDER PLUGINS ----------
942
- if (plugin.isFolder) {
943
-
944
- if (!fs.existsSync(searchRoot)) continue;
945
-
946
- const subDirs = fs.readdirSync(searchRoot)
947
- .map(name => path.join(searchRoot, name))
948
- .filter(p => fs.statSync(p).isDirectory());
949
-
950
- for (const dir of subDirs) {
951
- const relativePath = path
952
- .relative(searchRoot, dir)
953
- .replace(/\\/g, '/');
954
-
955
- const match = plugin.pattern.exec(relativePath);
956
- if (match) {
957
- const currentVersion = match[1];
958
- if (compareVersions(currentVersion, plugin.minVersion) < 0) {
959
- outdatedPlugins.push({
960
- name: relativePath,
961
- currentVersion,
962
- requiredVersion: plugin.minVersion
963
- });
964
- }
965
- }
966
- }
967
- continue;
968
- }
969
-
970
- // ---------- FILE PLUGINS ----------
971
- const matchedFile = files.find(file =>
972
- file.startsWith(searchRoot) && plugin.pattern.test(file)
973
- );
974
-
975
- if (matchedFile) {
976
- const match = plugin.pattern.exec(matchedFile);
977
- if (match) {
978
- const currentVersion = match[1];
979
- if (compareVersions(currentVersion, plugin.minVersion) < 0) {
980
- outdatedPlugins.push({
981
- name: path.relative(srcDir, matchedFile),
982
- currentVersion,
983
- requiredVersion: plugin.minVersion
984
- });
985
- }
986
- }
987
- }
988
- }
989
-
990
-
991
- if (outdatedPlugins.length > 0) {
992
- console.log('\n❗ The following plugins are outdated:');
993
- outdatedPlugins.forEach(p => {
994
- console.log(` ❌ - ${p.name} (Current: ${p.currentVersion}, Required: ${p.requiredVersion})`);
995
- });
996
-
997
- const rl = readline.createInterface({
998
- input: process.stdin,
999
- output: process.stdout
1000
- });
1001
-
1002
- rl.question('\nAre you sure you want to continue without updating these plugins? (y/n): ', answer => {
1003
- if (answer.toLowerCase() !== 'y') {
1004
- console.log('\n❌ Build cancelled due to outdated plugins.');
1005
- process.exit(1);
1006
- } else {
1007
- console.log('\n✅ Continuing build...');
1008
- rl.close();
1009
- }
1010
- });
1011
- } else {
1012
- console.log('✅ All plugin versions are up to date.');
1013
- }
1014
- }
1015
-
1016
-
1017
- // Run the validation
1018
- checkPlugins();
1019
-
1020
-
1021
-
1022
-
1023
- // Check all the codeplays plugins version START
1024
-
1025
-
1026
-
1027
-
1028
- // ====================================================================
1029
- // AUTO-ADD esbuild.drop: ['console','debugger'] to vite.config.js / mjs
1030
- // ====================================================================
1031
-
1032
-
1033
-
1034
- const checkAndupdateDropInViteConfig = () => {
1035
-
1036
- const possibleFiles = [
1037
- "vite.config.js",
1038
- "vite.config.mjs"
1039
- ];
1040
-
1041
- // Detect existing config file
1042
- const viteConfigPath = possibleFiles
1043
- .map(file => path.join(process.cwd(), file))
1044
- .find(filePath => fs.existsSync(filePath));
1045
-
1046
- if (!viteConfigPath) {
1047
- console.warn("⚠️ No vite config found. Skipping.");
1048
- return;
1049
- }
1050
-
1051
- //console.log("📄 Using:", viteConfigPath.split("/").pop());
1052
-
1053
- let viteContent = fs.readFileSync(viteConfigPath, "utf8");
1054
-
1055
- // Skip if already exists
1056
- if (/drop\s*:\s*\[.*['"]console['"].*\]/.test(viteContent)) {
1057
- console.log("ℹ️ vite.config.(m)js already Updated. Skipping...");
1058
- return;
1059
- }
1060
-
1061
- console.log("🔧 Adding esbuild.drop ...");
1062
-
1063
- // If esbuild block exists
1064
- if (/esbuild\s*:\s*{/.test(viteContent)) {
1065
- viteContent = viteContent.replace(
1066
- /esbuild\s*:\s*{([\s\S]*?)(^ {0,8})}/m,
1067
- (full, inner, indent) => {
1068
-
1069
- let lines = inner
1070
- .split("\n")
1071
- .map(l => l.trim())
1072
- .filter(Boolean);
1073
-
1074
- // Fix last comma
1075
- if (lines.length > 0) {
1076
- lines[lines.length - 1] =
1077
- lines[lines.length - 1].replace(/,+$/, "") + ",";
1078
- }
1079
-
1080
- // Re-indent
1081
- lines = lines.map(l => indent + " " + l);
1082
-
1083
- // Add drop
1084
- lines.push(`${indent} drop: ['console','debugger'],`);
1085
-
1086
- return `esbuild: {\n${lines.join("\n")}\n${indent}}`;
1087
- }
1088
- );
1089
- }
1090
-
1091
- // If esbuild missing
1092
- else {
1093
- viteContent = viteContent.replace(
1094
- /export default defineConfig\s*\(\s*{/,
1095
- m => `${m}\n esbuild: {\n drop: ['console','debugger'],\n },`
1096
- );
1097
- }
1098
-
1099
- fs.writeFileSync(viteConfigPath, viteContent, "utf8");
1100
- console.log("✅ vite.config.(m)js Updated successfully.");
1101
- };
1102
-
1103
- checkAndupdateDropInViteConfig();
1104
-
1105
-
1106
-
1107
-
1108
-
1109
-
1110
-
1111
-
1112
- const compareVersion = (v1, v2) => {
1113
- const a = v1.split(".").map(Number);
1114
- const b = v2.split(".").map(Number);
1115
-
1116
- for (let i = 0; i < Math.max(a.length, b.length); i++) {
1117
- const num1 = a[i] || 0;
1118
- const num2 = b[i] || 0;
1119
- if (num1 > num2) return 1;
1120
- if (num1 < num2) return -1;
1121
- }
1122
- return 0;
1123
- };
1124
-
1125
-
1126
-
1127
-
1128
- const admobConfigPath = path.join('src', 'js','Ads', 'admob-ad-configuration.json');
1129
-
1130
- const checkAdmobConfigurationProperty=()=>{
1131
-
1132
-
1133
- if (!_admobConfig.ADMOB_ENABLED)
1134
- {
1135
- console.log("ℹ️ Admob is not enabled so 'admob-ad-configuration.json' checking is skipping...");
1136
- return;
1137
- }
1138
-
1139
-
1140
- const REQUIRED_CONFIG_KEYS = [
1141
- "isKidsApp",
1142
- "isTesting",
1143
- "isConsoleLogEnabled",
1144
- "bannerEnabled",
1145
- "interstitialEnabled",
1146
- "appOpenEnabled",
1147
- "rewardVideoEnabled",
1148
- "rewardInterstitialEnabled",
1149
- "collapsibleEnabled",
1150
- "isLandScape",
1151
- "overlappingHeight",
1152
- "isOverlappingEnable",
1153
- "bannerTypeAndroid",
1154
- "bannerTypeiOS",
1155
- "bannerTopSpaceColor",
1156
- "interstitialLoadScreenTextColor",
1157
- "interstitialLoadScreenBackgroundColor",
1158
- "beforeBannerSpace",
1159
- "whenShow",
1160
- "minimumClick",
1161
- "interstitialTimeOut",
1162
- "interstitialFirstTimeOut",
1163
- "appOpenAdsTimeOut",
1164
- "maxRetryCount",
1165
- "retrySecondsAr",
1166
- "appOpenPerSession",
1167
- "interstitialPerSession",
1168
- "appOpenFirstTimeOut"
1169
- ];
1170
-
1171
-
1172
-
1173
-
1174
-
1175
- let admobConfigInJson;
1176
-
1177
- try {
1178
- admobConfigInJson = JSON.parse(readFileSync(admobConfigPath, "utf8"));
1179
- } catch (err) {
1180
- console.error("❌ Failed to read admob-ad-configuration.json", err);
1181
- process.exit(1);
1182
- }
1183
-
1184
- // ✅ Validate config object exists
1185
- if (!admobConfigInJson.config) {
1186
- console.error('❌ "config" object is missing in admob-ad-configuration.json');
1187
- process.exit(1);
1188
- }
1189
-
1190
-
1191
- const admobConfigMinVersion="1.4"
1192
-
1193
- if (compareVersion(admobConfigInJson.VERSION, admobConfigMinVersion) < 0) {
1194
- console.error(`❌ Please use at-least version ${admobConfigMinVersion} in "src/js/Ads/admob-ad-configuration.json"`);
1195
- process.exit(1);
1196
- }
1197
-
1198
-
1199
- const config = admobConfigInJson.config;
1200
-
1201
- // ✅ Find missing properties
1202
- const missingKeys = REQUIRED_CONFIG_KEYS.filter(
1203
- key => !(key in config)
1204
- );
1205
-
1206
-
1207
-
1208
- if (missingKeys.length > 0) {
1209
- console.error("❌ Missing required configuration keys. Please check it in 'src/js/Ads/admob-ad-configuration.json'");
1210
-
1211
- missingKeys.forEach(k => console.error(" - " + k));
1212
- process.exit(1);
1213
- }
1214
-
1215
-
1216
- console.log('✅ All keys exist. in "admob-ad-configuration.json file" Configuration looks good.');
1217
- }
1218
-
1219
-
1220
- checkAdmobConfigurationProperty()
1221
-
1222
-
1223
-
1224
-
1225
- /*
1226
- Release Notes
1227
-
1228
- 5.1
1229
- Kotlin version update is commented. Previously admob is not worked if not update the kotlin version to higher version
1230
-
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const plist = require('plist');
4
+
5
+ const { readFileSync } = require("fs");
6
+
7
+
8
+
9
+ const configPath = path.join(process.cwd(), 'capacitor.config.json');
10
+
11
+ // Expected plugin list with minimum versions
12
+ const requiredPlugins = [
13
+ { pattern: /backbutton-(\d+\.\d+)\.js$/, minVersion: '1.6', required: true, baseDir: 'js' },
14
+
15
+ /*/common-(\d+\.\d+)\.js$/*/
16
+ { pattern: /common-(\d+\.\d+)(?:-beta-(\d+))?\.js$/, minVersion: '5.2', required: true, baseDir: 'js' },
17
+
18
+ { pattern: /localization_settings-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js' },
19
+ { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.3', required: true, baseDir: 'js' },
20
+ { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js' },
21
+ { pattern: /localNotification_AppSettings-(\d+\.\d+)\.js$/, minVersion: '1.0', required: true, baseDir: 'js' },
22
+ { pattern: /onesignal-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js' },
23
+ { pattern: /saveToGalleryAndSaveAnyFile-(\d+\.\d+)(-ios)?\.js$/, minVersion: '3.0', required: true, baseDir: 'js' },
24
+ { pattern: /Ads[\/\\]IAP-(\d+\.\d+)$/, minVersion: '2.5', isFolder: true , required: true, baseDir: 'js' },
25
+ { pattern: /Ads[\/\\]admob-emi-(\d+\.\d+)\.js$/, minVersion: '3.3', required: true, baseDir: 'js' },
26
+
27
+ // New added plugins
28
+ { pattern: /video-player-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js' },
29
+ { pattern: /image-cropper-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js' },
30
+
31
+ { pattern: /common-(\d+\.\d+)\.less$/, minVersion: '1.4', required: true, baseDir: 'assets/css' },
32
+
33
+
34
+ // New folders
35
+ { pattern: /editor-(\d+\.\d+)$/, minVersion: '1.8', isFolder: true, required: true, baseDir: 'js' },
36
+ { pattern: /ffmpeg-(\d+\.\d+)$/, minVersion: '1.3', isFolder: true, required: true, baseDir: 'js' },
37
+ { pattern: /theme-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true , required: true, baseDir: 'js' }
38
+ ];
39
+
40
+
41
+
42
+
43
+
44
+
45
+ //Check codeplay-common latest version installed or not Start
46
+ const { execSync } = require('child_process');
47
+
48
+ function getInstalledVersion(packageName) {
49
+ try {
50
+ const packageJsonPath = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
51
+ if (fs.existsSync(packageJsonPath)) {
52
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
53
+ return packageJson.version;
54
+ }
55
+ } catch (error) {
56
+ return null;
57
+ }
58
+ return null;
59
+ }
60
+
61
+ function getLatestVersion(packageName) {
62
+ try {
63
+ return execSync(`npm view ${packageName} version`).toString().trim();
64
+ } catch (error) {
65
+ console.error(`Failed to fetch latest version for ${packageName}`);
66
+ return null;
67
+ }
68
+ }
69
+
70
+ function checkPackageVersion() {
71
+ const packageName = 'codeplay-common';
72
+ const installedVersion = getInstalledVersion(packageName);
73
+ const latestVersion = getLatestVersion(packageName);
74
+
75
+ if (!installedVersion) {
76
+ console.error(`${packageName} is not installed. Please install it using "npm install ${packageName}".`);
77
+ process.exit(1);
78
+ }
79
+
80
+ if (installedVersion !== latestVersion) {
81
+ 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`);
82
+ process.exit(1);
83
+ }
84
+
85
+ console.log(`${packageName} is up to date (version ${installedVersion}).`);
86
+ }
87
+
88
+ // Run package version check before executing the main script
89
+ try {
90
+ checkPackageVersion();
91
+ } catch (error) {
92
+ console.error(error.message);
93
+ process.exit(1);
94
+ }
95
+
96
+ //Check codeplay-common latest version installed or not END
97
+
98
+
99
+
100
+ function compareWithBeta(installedVersion, minVersion, isBeta) {
101
+ const baseCompare = compareVersions(installedVersion, minVersion);
102
+
103
+ if (!isBeta) {
104
+ // Stable version → normal compare
105
+ return baseCompare;
106
+ }
107
+
108
+ // Beta version logic
109
+ if (baseCompare > 0) return 1; // 5.3-beta > 5.2
110
+ if (baseCompare < 0) return -1; // 5.1-beta < 5.2
111
+
112
+ // Same version but beta → LOWER than stable
113
+ return -1; // 5.2-beta < 5.2
114
+ }
115
+
116
+
117
+
118
+
119
+ const checkAppUniqueId=()=>{
120
+
121
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
122
+
123
+ const appUniqueId = config.android?.APP_UNIQUE_ID;
124
+ const RESIZEABLE_ACTIVITY = config.android?.RESIZEABLE_ACTIVITY;
125
+ const orientation = config.android?.ORIENTATION;
126
+
127
+
128
+ let logErrorMessage="";
129
+
130
+ // 1️⃣ Check if it’s missing
131
+ if (RESIZEABLE_ACTIVITY === undefined) {
132
+ logErrorMessage+='❌ Missing android.RESIZEABLE_ACTIVITY option in capacitor.config.json.\n';
133
+ }
134
+
135
+ // 2️⃣ Check if it’s not boolean (true/false only)
136
+ else if (typeof RESIZEABLE_ACTIVITY !== 'boolean') {
137
+ logErrorMessage+='❌ Invalid android.RESIZEABLE_ACTIVITY value. Please use only true or false (without quotes).\n';
138
+ }
139
+
140
+
141
+
142
+ if (!orientation) {
143
+ logErrorMessage+='❌ Missing android.ORIENTATION option in capacitor.config.json.\n';
144
+ }
145
+
146
+ else if(orientation!="portrait" && orientation!="landscape" && orientation!="auto")
147
+ {
148
+ logErrorMessage+='❌ Spelling mistake in android.ORIENTATION option in capacitor.config.json. Please use only ["portrait" "landscape" "auto"]\n';
149
+ }
150
+
151
+
152
+ if (!appUniqueId) {
153
+ logErrorMessage+='❌ APP_UNIQUE_ID is missing in capacitor.config.json.';
154
+ }
155
+
156
+ else if (!Number.isInteger(appUniqueId)) {
157
+ logErrorMessage+='❌ APP_UNIQUE_ID must be an integer. Example: 1, 2, 3, etc.';
158
+ }
159
+
160
+
161
+
162
+ if(logErrorMessage!="")
163
+ {
164
+ console.error(logErrorMessage);
165
+ process.exit(1)
166
+ }
167
+
168
+
169
+ console.log(`✅ APP_UNIQUE_ID is valid: ${appUniqueId}`);
170
+
171
+ }
172
+
173
+ checkAppUniqueId();
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+ //@Codemirror check and install/uninstall the packages START
182
+ //const fs = require("fs");
183
+ //const path = require("path");
184
+ //const { execSync } = require("child_process");
185
+
186
+ const baseDir = path.join(__dirname, "..", "src", "js");
187
+
188
+ // Step 1: Find highest versioned folder like `editor-1.6`
189
+ const editorDirs = fs.readdirSync(baseDir)
190
+ .filter(name => /^editor-\d+\.\d+$/.test(name))
191
+ .sort((a, b) => {
192
+ const getVersion = str => str.match(/(\d+)\.(\d+)/).slice(1).map(Number);
193
+ const [aMajor, aMinor] = getVersion(a);
194
+ const [bMajor, bMinor] = getVersion(b);
195
+ return bMajor - aMajor || bMinor - aMinor;
196
+ });
197
+
198
+ if (editorDirs.length === 0) {
199
+
200
+ console.log("@Codemirror used editor(s) are not found")
201
+ //console.error("❌ No editor-x.x folders found in src/js.");
202
+ //process.exit(1);
203
+ }
204
+ else
205
+ {
206
+
207
+ const latestEditorDir = editorDirs.sort((a, b) => {
208
+ const versionA = parseFloat(a.split('-')[1]);
209
+ const versionB = parseFloat(b.split('-')[1]);
210
+ return versionB - versionA;
211
+ })[0];
212
+
213
+ //const latestEditorDir = editorDirs[editorDirs.length - 1];
214
+ const runJsPath = path.join(baseDir, latestEditorDir, "run.js");
215
+
216
+ if (!fs.existsSync(runJsPath)) {
217
+ console.error(`❌ run.js not found in ${latestEditorDir}`);
218
+ process.exit(1);
219
+ }
220
+
221
+ // Step 2: Execute the run.js file
222
+ console.log(`🚀 Executing ${runJsPath}...`);
223
+ execSync(`node "${runJsPath}"`, { stdio: "inherit" });
224
+ }
225
+
226
+ //@Codemirror check and install/uninstall the packages END
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+ // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists START
240
+
241
+ const os = require('os');
242
+
243
+ const saveToGalleryAndSaveFileCheck_iOS = () => {
244
+
245
+ // List of paths to scan
246
+ const SCAN_PATHS = [
247
+ path.resolve(__dirname, '../src/certificate'),
248
+ path.resolve(__dirname, '../src/pages'),
249
+ path.resolve(__dirname, '../src/js'),
250
+ path.resolve(__dirname, '../src/app.f7')
251
+ ];
252
+
253
+ // Directory to exclude
254
+ const EXCLUDED_DIR = path.resolve(__dirname, '../src/js/Ads');
255
+
256
+ const ANDROID_MANIFEST_PATH = path.resolve(__dirname, '../android/app/src/main/AndroidManifest.xml');
257
+
258
+
259
+ // Match iOS-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5-ios.js) not in comments
260
+ const IOS_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*-ios\.js['"]/m;
261
+
262
+ // Match Android-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5.js) not in comments
263
+ const ANDROID_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*\.js['"]/m;
264
+
265
+
266
+
267
+
268
+
269
+ const ALLOWED_EXTENSIONS = ['.js', '.f7'];
270
+ const isMac = os.platform() === 'darwin';
271
+
272
+ let iosImportFound = false;
273
+ let androidImportFound = false;
274
+
275
+ // Files to skip completely (full or partial match)
276
+ const SKIP_FILES = [
277
+ 'pdf-3.11.174.min.js',
278
+ 'pdf.worker-3.11.174.min.js'
279
+ ,'index.browser.js'
280
+ ];
281
+
282
+
283
+ function scanDirectory(dir) {
284
+
285
+ /*
286
+ //######################### DO NOT DELETE THIS - START [Appid base validation] #####################################
287
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
288
+ const appUniqueId = config.android?.APP_UNIQUE_ID;
289
+ if (appUniqueId == "206") return;
290
+ //######################### DO NOT DELETE THIS - END [Appid base validation] #####################################
291
+ */
292
+
293
+ const stat = fs.statSync(dir);
294
+
295
+ if (stat.isFile()) {
296
+
297
+ // 🔥 Skip files in SKIP_FILES array
298
+ const baseName = path.basename(dir);
299
+ if (SKIP_FILES.includes(baseName)) {
300
+ // Just skip silently
301
+ return;
302
+ }
303
+
304
+ // Only scan allowed file extensions
305
+ if (ALLOWED_EXTENSIONS.some(ext => dir.endsWith(ext))) {
306
+ process.stdout.write(`\r🔍 Scanning: ${dir} `);
307
+
308
+ const content = fs.readFileSync(dir, 'utf8');
309
+
310
+ if (IOS_FILE_REGEX.test(content)) {
311
+ iosImportFound = true;
312
+ if (!isMac) {
313
+ console.error(`\n❌ ERROR: iOS-specific import detected in: ${dir}`);
314
+ console.error(`🚫 STOPPED: This file should not be imported in Android/Windows/Linux builds.\n`);
315
+ process.exit(1);
316
+ }
317
+ }
318
+ else if (ANDROID_FILE_REGEX.test(content) && !content.includes('-ios.js')) {
319
+ androidImportFound = true;
320
+ }
321
+ }
322
+ }
323
+ else if (stat.isDirectory()) {
324
+ if (dir === EXCLUDED_DIR || path.basename(dir) === 'node_modules') return;
325
+
326
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
327
+ for (let entry of entries) {
328
+ scanDirectory(path.join(dir, entry.name));
329
+ }
330
+ }
331
+ }
332
+
333
+
334
+ // Run scan on all specified paths
335
+ for (let scanPath of SCAN_PATHS) {
336
+ if (fs.existsSync(scanPath)) {
337
+ scanDirectory(scanPath);
338
+ }
339
+ }
340
+
341
+
342
+
343
+ /* // Check src folder
344
+ if (!fs.existsSync(ROOT_DIR)) {
345
+ console.warn(`⚠️ Warning: 'src' directory not found at: ${ROOT_DIR}`);
346
+ return;
347
+ } */
348
+
349
+ //scanDirectory(ROOT_DIR);
350
+
351
+ // iOS Checks
352
+ if (isMac && !iosImportFound) {
353
+ console.warn(`⚠️ WARNING: You're on macOS but no iOS version (saveToGalleryAndSaveAnyFile-x.x-ios.js) found.`);
354
+ process.exit(1);
355
+ } else if (isMac && iosImportFound) {
356
+ console.log('✅ iOS version detected for macOS build.');
357
+ } else if (!iosImportFound) {
358
+ console.log('✅ No iOS-specific imports detected for non-macOS.');
359
+ }
360
+
361
+ // Android Checks
362
+ if (androidImportFound) {
363
+ console.log("📱 Android version of saveToGalleryAndSaveAnyFile detected. Checking AndroidManifest.xml...");
364
+
365
+ if (!fs.existsSync(ANDROID_MANIFEST_PATH)) {
366
+ console.error("❌ AndroidManifest.xml not found. Cannot add requestLegacyExternalStorage attribute.");
367
+ return;
368
+ }
369
+
370
+ let manifestContent = fs.readFileSync(ANDROID_MANIFEST_PATH, 'utf8');
371
+
372
+ if (!manifestContent.includes('android:requestLegacyExternalStorage="true"')) {
373
+ console.log("Adding android:requestLegacyExternalStorage=\"true\" to <application> tag...");
374
+
375
+ manifestContent = manifestContent.replace(
376
+ /<application([^>]*)>/,
377
+ (match, attrs) => {
378
+ if (attrs.includes('android:requestLegacyExternalStorage')) return match;
379
+ return `<application${attrs} android:requestLegacyExternalStorage="true">`;
380
+ }
381
+ );
382
+
383
+ fs.writeFileSync(ANDROID_MANIFEST_PATH, manifestContent, 'utf8');
384
+ console.log("✅ android:requestLegacyExternalStorage=\"true\" added successfully.");
385
+ } else {
386
+ console.log("ℹ️ android:requestLegacyExternalStorage already exists in AndroidManifest.xml.");
387
+ }
388
+ } else {
389
+ console.log("✅ No Android saveToGalleryAndSaveAnyFile imports detected.");
390
+ }
391
+ };
392
+
393
+ saveToGalleryAndSaveFileCheck_iOS();
394
+ // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists END
395
+
396
+
397
+
398
+
399
+
400
+
401
+
402
+
403
+
404
+
405
+
406
+
407
+
408
+ /*
409
+ // Clean up AppleDouble files (._*) created by macOS START
410
+ if (process.platform === 'darwin') {
411
+ try {
412
+ console.log('🧹 Cleaning up AppleDouble files (._*)...');
413
+ execSync(`find . -name '._*' -delete`);
414
+ console.log('✅ AppleDouble files removed.');
415
+ } catch (err) {
416
+ console.warn('⚠️ Failed to remove AppleDouble files:', err.message);
417
+ }
418
+ } else {
419
+ console.log('ℹ️ Skipping AppleDouble cleanup not a macOS machine.');
420
+ }
421
+
422
+ // Clean up AppleDouble files (._*) created by macOS END
423
+ */
424
+
425
+
426
+
427
+
428
+
429
+
430
+ //In routes.js file check static import START
431
+
432
+ const routesPath = path.join(process.cwd(), 'src', 'js', 'routes.js');
433
+ const routesContent = fs.readFileSync(routesPath, 'utf-8');
434
+
435
+ let inBlockComment = false;
436
+ const lines = routesContent.split('\n');
437
+
438
+ const allowedImport = `import HomePage from '../pages/home.f7';`;
439
+ const badImportRegex = /^[ \t]*import\s+[\w{}*,\s]*\s+from\s+['"].+\.f7['"]\s*;/;
440
+ const badImports = [];
441
+
442
+ lines.forEach((line, index) => {
443
+ const trimmed = line.trim();
444
+
445
+ // Handle block comment start and end
446
+ if (trimmed.startsWith('/*')) inBlockComment = true;
447
+ if (inBlockComment && trimmed.endsWith('*/')) {
448
+ inBlockComment = false;
449
+ return;
450
+ }
451
+
452
+ // Skip if inside block comment or line comment
453
+ if (inBlockComment || trimmed.startsWith('//')) return;
454
+
455
+ // Match static .f7 import
456
+ if (badImportRegex.test(trimmed) && trimmed !== allowedImport) {
457
+ badImports.push({ line: trimmed, number: index + 1 });
458
+ }
459
+ });
460
+
461
+ if (badImports.length > 0) {
462
+ console.error('\n❌ ERROR: Detected disallowed static imports of .f7 files in routes.js\n');
463
+ console.error(`⚠️ Only this static import is allowed:\n ${allowedImport}\n`);
464
+ console.error(`🔧 Please convert other imports to async dynamic imports like this:\n`);
465
+ console.error(`
466
+
467
+ import HomePage from '../pages/home.f7';
468
+
469
+ const routes = [
470
+ {
471
+ path: '/',
472
+ component:HomePage,
473
+ },
474
+ {
475
+ path: '/ProfilePage/',
476
+ async async({ resolve }) {
477
+ const page = await import('../pages/profile.f7');
478
+ resolve({ component: page.default });
479
+ },
480
+ }]
481
+ `);
482
+
483
+ badImports.forEach(({ line, number }) => {
484
+ console.error(`${number}: ${line}`);
485
+ });
486
+
487
+ process.exit(1);
488
+ } else {
489
+ console.log('✅ routes.js passed the .f7 import check.');
490
+ }
491
+
492
+ //In routes.js file check static import END
493
+
494
+
495
+
496
+
497
+
498
+
499
+
500
+
501
+
502
+
503
+
504
+
505
+
506
+ // Check and change the "BridgeWebViewClient.java" file START
507
+ /*
508
+ For crash issue due to low memory problem, we need to modify the onRenderProcessGone method in BridgeWebViewClient.java.
509
+ */
510
+
511
+
512
+ const bridgeWebViewClientFilePath = path.join(process.cwd(), 'node_modules', '@capacitor/android/capacitor/src/main/java/com/getcapacitor', 'BridgeWebViewClient.java');
513
+
514
+ // Read the file
515
+ if (!fs.existsSync(bridgeWebViewClientFilePath)) {
516
+ console.error('❌ Error: BridgeWebViewClient.java not found.');
517
+ process.exit(1);
518
+ }
519
+
520
+ let fileContent = fs.readFileSync(bridgeWebViewClientFilePath, 'utf8');
521
+
522
+ // Define old and new code
523
+ const oldCodeStart = `@Override
524
+ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
525
+ super.onRenderProcessGone(view, detail);
526
+ boolean result = false;
527
+
528
+ List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
529
+ if (webViewListeners != null) {
530
+ for (WebViewListener listener : bridge.getWebViewListeners()) {
531
+ result = listener.onRenderProcessGone(view, detail) || result;
532
+ }
533
+ }
534
+
535
+ return result;
536
+ }`;
537
+
538
+ const newCode = `@Override
539
+ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
540
+ super.onRenderProcessGone(view, detail);
541
+
542
+ boolean result = false;
543
+
544
+ List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
545
+ if (webViewListeners != null) {
546
+ for (WebViewListener listener : bridge.getWebViewListeners()) {
547
+ result = listener.onRenderProcessGone(view, detail) || result;
548
+ }
549
+ }
550
+
551
+ if (!result) {
552
+ // If no one handled it, handle it ourselves!
553
+
554
+ /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
555
+ if (detail.didCrash()) {
556
+ //Log.e("CapacitorWebView", "WebView crashed internally!");
557
+ } else {
558
+ //Log.w("CapacitorWebView", "WebView was killed by system (low memory) internally!");
559
+ }
560
+ }*/
561
+
562
+ view.post(() -> {
563
+ Toast.makeText(view.getContext(), "Reloading due to low memory issue", Toast.LENGTH_SHORT).show();
564
+ });
565
+
566
+ view.reload(); // Safely reload WebView
567
+
568
+ return true; // We handled it
569
+ }
570
+
571
+ return result;
572
+ }`;
573
+
574
+ // Step 1: Update method if needed
575
+ let updated = false;
576
+
577
+ if (fileContent.includes(oldCodeStart)) {
578
+ console.log('✅ Found old onRenderProcessGone method. Replacing it...');
579
+ fileContent = fileContent.replace(oldCodeStart, newCode);
580
+ updated = true;
581
+ } else if (fileContent.includes(newCode)) {
582
+ console.log('ℹ️ Method already updated. No changes needed in "BridgeWebViewClient.java".');
583
+ } else {
584
+ console.error('❌ Error: Neither old nor new code found. Unexpected content.');
585
+ process.exit(1);
586
+ }
587
+
588
+ // Step 2: Check and add import if missing
589
+ const importToast = 'import android.widget.Toast;';
590
+ if (!fileContent.includes(importToast)) {
591
+ console.log('✅ Adding missing import for Toast...');
592
+ const importRegex = /import\s+[^;]+;/g;
593
+ const matches = [...fileContent.matchAll(importRegex)];
594
+
595
+ if (matches.length > 0) {
596
+ const lastImport = matches[matches.length - 1];
597
+ const insertPosition = lastImport.index + lastImport[0].length;
598
+ fileContent = fileContent.slice(0, insertPosition) + `\n${importToast}` + fileContent.slice(insertPosition);
599
+ updated = true;
600
+ } else {
601
+ console.error('❌ Error: No import section found in file.');
602
+ process.exit(1);
603
+ }
604
+ } else {
605
+ console.log('ℹ️ Import for Toast already exists. No changes needed.');
606
+ }
607
+
608
+ // Step 3: Save if updated
609
+ if (updated) {
610
+ fs.writeFileSync(bridgeWebViewClientFilePath, fileContent, 'utf8');
611
+ console.log('✅ File updated successfully.');
612
+ } else {
613
+ console.log('ℹ️ No changes needed.');
614
+ }
615
+
616
+
617
+
618
+
619
+ // Check and change the "BridgeWebViewClient.java" file END
620
+
621
+
622
+
623
+
624
+
625
+
626
+
627
+
628
+ /*
629
+ // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file START
630
+
631
+ // Build the path dynamically like you requested
632
+ const gradlePath = path.join(
633
+ process.cwd(),
634
+ 'android',
635
+ 'build.gradle'
636
+ );
637
+
638
+ // Read the existing build.gradle
639
+ let gradleContent = fs.readFileSync(gradlePath, 'utf8');
640
+
641
+ // Add `ext.kotlin_version` if it's not already there
642
+ if (!gradleContent.includes('ext.kotlin_version')) {
643
+ gradleContent = gradleContent.replace(
644
+ /buildscript\s*{/,
645
+ `buildscript {\n ext.kotlin_version = '2.1.0'`
646
+ );
647
+ }
648
+
649
+ // Add Kotlin classpath if it's not already there
650
+ if (!gradleContent.includes('org.jetbrains.kotlin:kotlin-gradle-plugin')) {
651
+ gradleContent = gradleContent.replace(
652
+ /dependencies\s*{([\s\S]*?)classpath 'com.android.tools.build:gradle:8.7.2'/,
653
+ `dependencies {\n classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")\n$1classpath 'com.android.tools.build:gradle:8.7.2'`
654
+ );
655
+ }
656
+
657
+ // Write back the modified content
658
+ fs.writeFileSync(gradlePath, gradleContent, 'utf8');
659
+
660
+ console.log(' Kotlin version updated in build.gradle.');
661
+
662
+ // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file END
663
+ */
664
+
665
+
666
+
667
+
668
+
669
+
670
+
671
+
672
+ let _admobConfig;
673
+
674
+
675
+
676
+ const androidPlatformPath = path.join(process.cwd(), 'android');
677
+ const iosPlatformPath = path.join(process.cwd(), 'ios');
678
+ const pluginPath = path.join(process.cwd(), 'node_modules', 'emi-indo-cordova-plugin-admob', 'plugin.xml');
679
+ const infoPlistPath = path.join(process.cwd(), 'ios', 'App', 'App', 'Info.plist');
680
+ const resourcesPath = path.join(process.cwd(), 'resources', 'res');
681
+ const androidResPath = path.join(process.cwd(), 'android', 'app', 'src', 'main', 'res');
682
+ const localNotificationsPluginPath = path.join(process.cwd(), 'node_modules', '@capacitor', 'local-notifications');
683
+
684
+ function fileExists(filePath) {
685
+ return fs.existsSync(filePath);
686
+ }
687
+
688
+ function copyFolderSync(source, target) {
689
+ if (!fs.existsSync(target)) {
690
+ fs.mkdirSync(target, { recursive: true });
691
+ }
692
+
693
+ fs.readdirSync(source).forEach(file => {
694
+ const sourceFile = path.join(source, file);
695
+ const targetFile = path.join(target, file);
696
+
697
+ if (fs.lstatSync(sourceFile).isDirectory()) {
698
+ copyFolderSync(sourceFile, targetFile);
699
+ } else {
700
+ fs.copyFileSync(sourceFile, targetFile);
701
+ }
702
+ });
703
+ }
704
+
705
+ function checkAndCopyResources() {
706
+ if (fileExists(resourcesPath)) {
707
+ copyFolderSync(resourcesPath, androidResPath);
708
+ console.log(' Successfully copied resources/res to android/app/src/main/res.');
709
+ } else {
710
+ console.log('resources/res folder not found.');
711
+
712
+ if (fileExists(localNotificationsPluginPath)) {
713
+ throw new Error('❌ resources/res is required for @capacitor/local-notifications. Stopping execution.');
714
+ }
715
+ }
716
+ }
717
+
718
+
719
+
720
+
721
+
722
+
723
+
724
+
725
+ function getAdMobConfig() {
726
+ if (!fileExists(configPath)) {
727
+ throw new Error('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
728
+ }
729
+
730
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
731
+ const admobConfig = config.plugins?.AdMob;
732
+
733
+ if (!admobConfig) {
734
+ throw new Error('❌ AdMob configuration is missing in capacitor.config.json.');
735
+ }
736
+
737
+ // Default to true if ADMOB_ENABLED is not specified
738
+ const isEnabled = admobConfig.ADMOB_ENABLED !== false;
739
+
740
+ if (!isEnabled) {
741
+ return { ADMOB_ENABLED: false }; // Skip further validation
742
+ }
743
+
744
+ if (!admobConfig.APP_ID_ANDROID || !admobConfig.APP_ID_IOS) {
745
+ throw new Error(' ❌ AdMob configuration is incomplete. Ensure APP_ID_ANDROID and APP_ID_IOS are defined.');
746
+ }
747
+
748
+ return {
749
+ ADMOB_ENABLED: true,
750
+ APP_ID_ANDROID: admobConfig.APP_ID_ANDROID,
751
+ APP_ID_IOS: admobConfig.APP_ID_IOS,
752
+ USE_LITE_ADS: admobConfig.USE_LITE_ADS === "lite",
753
+ };
754
+ }
755
+
756
+ function validateAndroidBuildOptions() {
757
+
758
+
759
+ if (!fileExists(configPath)) {
760
+ console.log('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
761
+ process.exit(1);
762
+ }
763
+
764
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
765
+
766
+ const targetAppId=config.appId
767
+
768
+ const buildOptions = config.android?.buildOptions;
769
+
770
+ if (!buildOptions) {
771
+ console.log('❌ Missing android.buildOptions in capacitor.config.json.');
772
+ process.exit(1);
773
+ }
774
+
775
+ const requiredProps = [
776
+ 'keystorePath',
777
+ 'keystorePassword',
778
+ 'keystoreAlias',
779
+ 'keystoreAliasPassword',
780
+ 'releaseType',
781
+ 'signingType'
782
+ ];
783
+
784
+ const missing = requiredProps.filter(prop => !buildOptions[prop]);
785
+
786
+ if (missing.length > 0) {
787
+ console.log('❌ Missing properties android.buildOptions in capacitor.config.json.');
788
+ process.exit(1);
789
+ }
790
+
791
+
792
+ const keystorePath=buildOptions.keystorePath
793
+ const keyFileName = path.basename(keystorePath);
794
+
795
+
796
+
797
+ const keystoreMap = {
798
+ "gameskey.jks": [
799
+ "com.cube.blaster",
800
+ ],
801
+ "htmleditorkeystoke.jks": [
802
+ "com.HTML.AngularJS.Codeplay",
803
+ "com.html.codeplay.pro",
804
+ "com.bootstrap.code.play",
805
+ "com.kids.learning.master",
806
+ "com.Simple.Barcode.Scanner"
807
+ ]
808
+ };
809
+
810
+ // find which keystore is required for the given targetAppId
811
+ let requiredKey = "newappskey.jks"; // default
812
+ for (const [keyFile, appIds] of Object.entries(keystoreMap)) {
813
+ if (appIds.includes(targetAppId)) {
814
+ requiredKey = keyFile;
815
+ break;
816
+ }
817
+ }
818
+
819
+ // validate
820
+ if (keyFileName !== requiredKey) {
821
+ console.log(`❌ The keystore path is mismatched. Expected ${requiredKey} for ${targetAppId}, but got ${keyFileName}`);
822
+ process.exit(1);
823
+ }
824
+
825
+
826
+
827
+
828
+
829
+ // optionally return them
830
+ //return buildOptions;
831
+ }
832
+
833
+ function updatePluginXml(admobConfig) {
834
+ if (!fileExists(pluginPath)) {
835
+ console.error(' ❌ plugin.xml not found. Ensure the plugin is installed.');
836
+ return;
837
+ }
838
+
839
+ let pluginContent = fs.readFileSync(pluginPath, 'utf8');
840
+
841
+ pluginContent = pluginContent
842
+ .replace(/<preference name="APP_ID_ANDROID" default=".*?" \/>/, `<preference name="APP_ID_ANDROID" default="${admobConfig.APP_ID_ANDROID}" />`)
843
+ .replace(/<preference name="APP_ID_IOS" default=".*?" \/>/, `<preference name="APP_ID_IOS" default="${admobConfig.APP_ID_IOS}" />`);
844
+
845
+ fs.writeFileSync(pluginPath, pluginContent, 'utf8');
846
+ console.log('✅ AdMob IDs successfully updated in plugin.xml');
847
+ }
848
+
849
+ function updateInfoPlist(admobConfig) {
850
+ if (!fileExists(infoPlistPath)) {
851
+ console.error(' ❌ Info.plist not found. Ensure you have built the iOS project.');
852
+ return;
853
+ }
854
+
855
+ const plistContent = fs.readFileSync(infoPlistPath, 'utf8');
856
+ const plistData = plist.parse(plistContent);
857
+
858
+ plistData.GADApplicationIdentifier = admobConfig.APP_ID_IOS;
859
+ plistData.NSUserTrackingUsageDescription = 'This identifier will be used to deliver personalized ads to you.';
860
+ plistData.GADDelayAppMeasurementInit = true;
861
+
862
+ const updatedPlistContent = plist.build(plistData);
863
+ fs.writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
864
+ console.log('AdMob IDs and additional configurations successfully updated in Info.plist');
865
+ }
866
+
867
+
868
+ try {
869
+ if (!fileExists(configPath)) {
870
+ throw new Error(' ❌ capacitor.config.json not found. Skipping setup.');
871
+ }
872
+
873
+ if (!fileExists(androidPlatformPath) && !fileExists(iosPlatformPath)) {
874
+ throw new Error('Neither Android nor iOS platforms are found. Ensure platforms are added to your Capacitor project.');
875
+ }
876
+
877
+ checkAndCopyResources();
878
+
879
+
880
+
881
+ _admobConfig = getAdMobConfig();
882
+
883
+
884
+
885
+
886
+
887
+ // Proceed only if ADMOB_ENABLED is true
888
+ if (_admobConfig.ADMOB_ENABLED) {
889
+ if (fileExists(androidPlatformPath)) {
890
+ updatePluginXml(_admobConfig);
891
+ }
892
+
893
+ if (fileExists(iosPlatformPath)) {
894
+ updateInfoPlist(_admobConfig);
895
+ }
896
+ }
897
+
898
+
899
+ } catch (error) {
900
+ console.error(error.message);
901
+ process.exit(1); // Stop execution if there's a critical error
902
+ }
903
+
904
+
905
+
906
+ validateAndroidBuildOptions();
907
+
908
+
909
+
910
+
911
+
912
+
913
+ // Check all the codeplays plugins version START
914
+
915
+
916
+ const readline = require('readline');
917
+
918
+
919
+ //const srcDir = path.join(__dirname, 'src');
920
+ const srcDir = path.join(process.cwd(), 'src');
921
+ let outdatedPlugins = [];
922
+
923
+ function parseVersion(ver) {
924
+ return ver.split('.').map(n => parseInt(n, 10));
925
+ }
926
+
927
+ function compareVersions(v1, v2) {
928
+ const [a1, b1] = parseVersion(v1);
929
+ const [a2, b2] = parseVersion(v2);
930
+ if (a1 !== a2) return a1 - a2;
931
+ return b1 - b2;
932
+ }
933
+
934
+ function walkSync(dir, filelist = []) {
935
+ fs.readdirSync(dir).forEach(file => {
936
+ const fullPath = path.join(dir, file);
937
+ const stat = fs.statSync(fullPath);
938
+ if (stat.isDirectory()) {
939
+ walkSync(fullPath, filelist);
940
+ } else {
941
+ filelist.push(fullPath);
942
+ }
943
+ });
944
+ return filelist;
945
+ }
946
+
947
+
948
+
949
+ function getSearchRoot(plugin) {
950
+ return path.join(srcDir, plugin.baseDir || 'js');
951
+ }
952
+
953
+ function checkPlugins() {
954
+ const files = walkSync(srcDir);
955
+
956
+ for (const plugin of requiredPlugins) {
957
+
958
+ const searchRoot = getSearchRoot(plugin);
959
+
960
+ // ---------- FOLDER PLUGINS ----------
961
+ if (plugin.isFolder) {
962
+
963
+ if (!fs.existsSync(searchRoot)) continue;
964
+
965
+ const subDirs = fs.readdirSync(searchRoot)
966
+ .map(name => path.join(searchRoot, name))
967
+ .filter(p => fs.statSync(p).isDirectory());
968
+
969
+ for (const dir of subDirs) {
970
+ const relativePath = path
971
+ .relative(searchRoot, dir)
972
+ .replace(/\\/g, '/');
973
+
974
+ const match = plugin.pattern.exec(relativePath);
975
+ if (match) {
976
+ const currentVersion = match[1];
977
+ if (compareVersions(currentVersion, plugin.minVersion) < 0) {
978
+ outdatedPlugins.push({
979
+ name: relativePath,
980
+ currentVersion,
981
+ requiredVersion: plugin.minVersion
982
+ });
983
+ }
984
+ }
985
+ }
986
+ continue;
987
+ }
988
+
989
+ // ---------- FILE PLUGINS ----------
990
+ const matchedFile = files.find(file =>
991
+ file.startsWith(searchRoot) && plugin.pattern.test(file)
992
+ );
993
+
994
+ if (matchedFile) {
995
+ const match = plugin.pattern.exec(matchedFile);
996
+ if (match) {
997
+ const currentVersion = match[1];
998
+ const isBeta = !!match[2]; // <-- only common has this
999
+
1000
+ const cmp = plugin.pattern.source.includes('beta')
1001
+ ? compareWithBeta(currentVersion, plugin.minVersion, isBeta)
1002
+ : compareVersions(currentVersion, plugin.minVersion);
1003
+
1004
+ if (cmp < 0) {
1005
+ outdatedPlugins.push({
1006
+ name: path.relative(srcDir, matchedFile),
1007
+ currentVersion: isBeta
1008
+ ? `${currentVersion}-beta`
1009
+ : currentVersion,
1010
+ requiredVersion: plugin.minVersion
1011
+ });
1012
+ }
1013
+ }
1014
+ }
1015
+
1016
+ }
1017
+
1018
+
1019
+ if (outdatedPlugins.length > 0) {
1020
+ console.log('\n❗ The following plugins are outdated:');
1021
+ outdatedPlugins.forEach(p => {
1022
+ console.log(` ❌ - ${p.name} (Current: ${p.currentVersion}, Required: ${p.requiredVersion})`);
1023
+ });
1024
+
1025
+ const rl = readline.createInterface({
1026
+ input: process.stdin,
1027
+ output: process.stdout
1028
+ });
1029
+
1030
+ rl.question('\nAre you sure you want to continue without updating these plugins? (y/n): ', answer => {
1031
+ if (answer.toLowerCase() !== 'y') {
1032
+ console.log('\n❌ Build cancelled due to outdated plugins.');
1033
+ process.exit(1);
1034
+ } else {
1035
+ console.log('\n✅ Continuing build...');
1036
+ rl.close();
1037
+ }
1038
+ });
1039
+ } else {
1040
+ console.log('✅ All plugin versions are up to date.');
1041
+ }
1042
+ }
1043
+
1044
+
1045
+ // Run the validation
1046
+ checkPlugins();
1047
+
1048
+
1049
+
1050
+
1051
+ // Check all the codeplays plugins version START
1052
+
1053
+
1054
+
1055
+
1056
+ // ====================================================================
1057
+ // AUTO-ADD esbuild.drop: ['console','debugger'] to vite.config.js / mjs
1058
+ // ====================================================================
1059
+
1060
+
1061
+
1062
+ const checkAndupdateDropInViteConfig = () => {
1063
+
1064
+ const possibleFiles = [
1065
+ "vite.config.js",
1066
+ "vite.config.mjs"
1067
+ ];
1068
+
1069
+ // Detect existing config file
1070
+ const viteConfigPath = possibleFiles
1071
+ .map(file => path.join(process.cwd(), file))
1072
+ .find(filePath => fs.existsSync(filePath));
1073
+
1074
+ if (!viteConfigPath) {
1075
+ console.warn("⚠️ No vite config found. Skipping.");
1076
+ return;
1077
+ }
1078
+
1079
+ //console.log("📄 Using:", viteConfigPath.split("/").pop());
1080
+
1081
+ let viteContent = fs.readFileSync(viteConfigPath, "utf8");
1082
+
1083
+ // Skip if already exists
1084
+ if (/drop\s*:\s*\[.*['"]console['"].*\]/.test(viteContent)) {
1085
+ console.log("ℹ️ vite.config.(m)js already Updated. Skipping...");
1086
+ return;
1087
+ }
1088
+
1089
+ console.log("🔧 Adding esbuild.drop ...");
1090
+
1091
+ // If esbuild block exists
1092
+ if (/esbuild\s*:\s*{/.test(viteContent)) {
1093
+ viteContent = viteContent.replace(
1094
+ /esbuild\s*:\s*{([\s\S]*?)(^ {0,8})}/m,
1095
+ (full, inner, indent) => {
1096
+
1097
+ let lines = inner
1098
+ .split("\n")
1099
+ .map(l => l.trim())
1100
+ .filter(Boolean);
1101
+
1102
+ // Fix last comma
1103
+ if (lines.length > 0) {
1104
+ lines[lines.length - 1] =
1105
+ lines[lines.length - 1].replace(/,+$/, "") + ",";
1106
+ }
1107
+
1108
+ // Re-indent
1109
+ lines = lines.map(l => indent + " " + l);
1110
+
1111
+ // Add drop
1112
+ lines.push(`${indent} drop: ['console','debugger'],`);
1113
+
1114
+ return `esbuild: {\n${lines.join("\n")}\n${indent}}`;
1115
+ }
1116
+ );
1117
+ }
1118
+
1119
+ // If esbuild missing
1120
+ else {
1121
+ viteContent = viteContent.replace(
1122
+ /export default defineConfig\s*\(\s*{/,
1123
+ m => `${m}\n esbuild: {\n drop: ['console','debugger'],\n },`
1124
+ );
1125
+ }
1126
+
1127
+ fs.writeFileSync(viteConfigPath, viteContent, "utf8");
1128
+ console.log("✅ vite.config.(m)js Updated successfully.");
1129
+ };
1130
+
1131
+ checkAndupdateDropInViteConfig();
1132
+
1133
+
1134
+
1135
+
1136
+
1137
+
1138
+
1139
+
1140
+ const compareVersion = (v1, v2) => {
1141
+ const a = v1.split(".").map(Number);
1142
+ const b = v2.split(".").map(Number);
1143
+
1144
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
1145
+ const num1 = a[i] || 0;
1146
+ const num2 = b[i] || 0;
1147
+ if (num1 > num2) return 1;
1148
+ if (num1 < num2) return -1;
1149
+ }
1150
+ return 0;
1151
+ };
1152
+
1153
+
1154
+
1155
+
1156
+ const admobConfigPath = path.join('src', 'js','Ads', 'admob-ad-configuration.json');
1157
+
1158
+ const checkAdmobConfigurationProperty=()=>{
1159
+
1160
+
1161
+ if (!_admobConfig.ADMOB_ENABLED)
1162
+ {
1163
+ console.log("ℹ️ Admob is not enabled so 'admob-ad-configuration.json' checking is skipping...");
1164
+ return;
1165
+ }
1166
+
1167
+
1168
+ const REQUIRED_CONFIG_KEYS = [
1169
+ "isKidsApp",
1170
+ "isTesting",
1171
+ "isConsoleLogEnabled",
1172
+ "bannerEnabled",
1173
+ "interstitialEnabled",
1174
+ "appOpenEnabled",
1175
+ "rewardVideoEnabled",
1176
+ "rewardInterstitialEnabled",
1177
+ "collapsibleEnabled",
1178
+ "isLandScape",
1179
+ "overlappingHeight",
1180
+ "isOverlappingEnable",
1181
+ "bannerTypeAndroid",
1182
+ "bannerTypeiOS",
1183
+ "bannerTopSpaceColor",
1184
+ "interstitialLoadScreenTextColor",
1185
+ "interstitialLoadScreenBackgroundColor",
1186
+ "beforeBannerSpace",
1187
+ "whenShow",
1188
+ "minimumClick",
1189
+ "interstitialTimeOut",
1190
+ "interstitialFirstTimeOut",
1191
+ "appOpenAdsTimeOut",
1192
+ "maxRetryCount",
1193
+ "retrySecondsAr",
1194
+ "appOpenPerSession",
1195
+ "interstitialPerSession",
1196
+ "appOpenFirstTimeOut"
1197
+ ];
1198
+
1199
+
1200
+
1201
+
1202
+
1203
+ let admobConfigInJson;
1204
+
1205
+ try {
1206
+ admobConfigInJson = JSON.parse(readFileSync(admobConfigPath, "utf8"));
1207
+ } catch (err) {
1208
+ console.error("❌ Failed to read admob-ad-configuration.json", err);
1209
+ process.exit(1);
1210
+ }
1211
+
1212
+ // ✅ Validate config object exists
1213
+ if (!admobConfigInJson.config) {
1214
+ console.error('❌ "config" object is missing in admob-ad-configuration.json');
1215
+ process.exit(1);
1216
+ }
1217
+
1218
+
1219
+ const admobConfigMinVersion="1.4"
1220
+
1221
+ if (compareVersion(admobConfigInJson.VERSION, admobConfigMinVersion) < 0) {
1222
+ console.error(`❌ Please use at-least version ${admobConfigMinVersion} in "src/js/Ads/admob-ad-configuration.json"`);
1223
+ process.exit(1);
1224
+ }
1225
+
1226
+
1227
+ const config = admobConfigInJson.config;
1228
+
1229
+ // Find missing properties
1230
+ const missingKeys = REQUIRED_CONFIG_KEYS.filter(
1231
+ key => !(key in config)
1232
+ );
1233
+
1234
+
1235
+
1236
+ if (missingKeys.length > 0) {
1237
+ console.error("❌ Missing required configuration keys. Please check it in 'src/js/Ads/admob-ad-configuration.json'");
1238
+
1239
+ missingKeys.forEach(k => console.error(" - " + k));
1240
+ process.exit(1);
1241
+ }
1242
+
1243
+
1244
+ console.log('✅ All keys exist. in "admob-ad-configuration.json file" Configuration looks good.');
1245
+ }
1246
+
1247
+
1248
+ checkAdmobConfigurationProperty()
1249
+
1250
+
1251
+
1252
+
1253
+ /*
1254
+ Release Notes
1255
+
1256
+ 5.1
1257
+ Kotlin version update is commented. Previously admob is not worked if not update the kotlin version to higher version
1258
+
1231
1259
  */