codeplay-common 1.8.7 → 1.8.9

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,890 +1,979 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const plist = require('plist');
4
-
5
-
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.4', required: true },
14
- { pattern: /common-(\d+\.\d+)\.js$/, minVersion: '4.0', required: true },
15
- { pattern: /localization_settings-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true },
16
- { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.3', required: true },
17
- { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true },
18
- { pattern: /localNotification_AppSettings-(\d+\.\d+)\.js$/, minVersion: '1.0', required: true },
19
- { pattern: /onesignal-(\d+\.\d+)\.js$/, minVersion: '2.1', required: true },
20
- { pattern: /saveToGalleryAndSaveAnyFile-(\d+\.\d+)(-ios)?\.js$/, minVersion: '2.5', required: true },
21
- { pattern: /Ads[\/\\]IAP-(\d+\.\d+)$/, minVersion: '2.5', isFolder: true , required: true},
22
- { pattern: /Ads[\/\\]admob-emi-(\d+\.\d+)\.js$/, minVersion: '2.8', required: true }
23
- ];
24
-
25
-
26
-
27
-
28
-
29
-
30
- //Check codeplay-common latest version installed or not Start
31
- const { execSync } = require('child_process');
32
-
33
- function getInstalledVersion(packageName) {
34
- try {
35
- const packageJsonPath = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
36
- if (fs.existsSync(packageJsonPath)) {
37
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
38
- return packageJson.version;
39
- }
40
- } catch (error) {
41
- return null;
42
- }
43
- return null;
44
- }
45
-
46
- function getLatestVersion(packageName) {
47
- try {
48
- return execSync(`npm view ${packageName} version`).toString().trim();
49
- } catch (error) {
50
- console.error(`Failed to fetch latest version for ${packageName}`);
51
- return null;
52
- }
53
- }
54
-
55
- function checkPackageVersion() {
56
- const packageName = 'codeplay-common';
57
- const installedVersion = getInstalledVersion(packageName);
58
- const latestVersion = getLatestVersion(packageName);
59
-
60
- if (!installedVersion) {
61
- console.error(`${packageName} is not installed. Please install it using "npm install ${packageName}".`);
62
- process.exit(1);
63
- }
64
-
65
- if (installedVersion !== latestVersion) {
66
- 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`);
67
- process.exit(1);
68
- }
69
-
70
- console.log(`${packageName} is up to date (version ${installedVersion}).`);
71
- }
72
-
73
- // Run package version check before executing the main script
74
- try {
75
- checkPackageVersion();
76
- } catch (error) {
77
- console.error(error.message);
78
- process.exit(1);
79
- }
80
-
81
- //Check codeplay-common latest version installed or not END
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
- //@Codemirror check and install/uninstall the packages START
92
- //const fs = require("fs");
93
- //const path = require("path");
94
- //const { execSync } = require("child_process");
95
-
96
- const baseDir = path.join(__dirname, "..", "src", "js");
97
-
98
- // Step 1: Find highest versioned folder like `editor-1.6`
99
- const editorDirs = fs.readdirSync(baseDir)
100
- .filter(name => /^editor-\d+\.\d+$/.test(name))
101
- .sort((a, b) => {
102
- const getVersion = str => str.match(/(\d+)\.(\d+)/).slice(1).map(Number);
103
- const [aMajor, aMinor] = getVersion(a);
104
- const [bMajor, bMinor] = getVersion(b);
105
- return bMajor - aMajor || bMinor - aMinor;
106
- });
107
-
108
- if (editorDirs.length === 0) {
109
-
110
- console.log("@Codemirror used editor(s) are not found")
111
- //console.error("❌ No editor-x.x folders found in src/js.");
112
- //process.exit(1);
113
- }
114
- else
115
- {
116
-
117
- const latestEditorDir = editorDirs.sort((a, b) => {
118
- const versionA = parseFloat(a.split('-')[1]);
119
- const versionB = parseFloat(b.split('-')[1]);
120
- return versionB - versionA;
121
- })[0];
122
-
123
- //const latestEditorDir = editorDirs[editorDirs.length - 1];
124
- const runJsPath = path.join(baseDir, latestEditorDir, "run.js");
125
-
126
- if (!fs.existsSync(runJsPath)) {
127
- console.error(`❌ run.js not found in ${latestEditorDir}`);
128
- process.exit(1);
129
- }
130
-
131
- // Step 2: Execute the run.js file
132
- console.log(`🚀 Executing ${runJsPath}...`);
133
- execSync(`node "${runJsPath}"`, { stdio: "inherit" });
134
- }
135
-
136
- //@Codemirror check and install/uninstall the packages END
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
- // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists START
150
-
151
- const os = require('os');
152
-
153
- const saveToGalleryAndSaveFileCheck_iOS = () => {
154
- const ROOT_DIR = path.resolve(__dirname, '../src');
155
- const ANDROID_MANIFEST_PATH = path.resolve(__dirname, '../android/app/src/main/AndroidManifest.xml');
156
-
157
-
158
- // Match iOS-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5-ios.js) not in comments
159
- const IOS_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*-ios\.js['"]/m;
160
-
161
- // Match Android-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5.js) not in comments
162
- const ANDROID_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*\.js['"]/m;
163
-
164
-
165
-
166
-
167
-
168
- const ALLOWED_EXTENSIONS = ['.js', '.f7'];
169
- const isMac = os.platform() === 'darwin';
170
-
171
- let iosImportFound = false;
172
- let androidImportFound = false;
173
-
174
- function scanDirectory(dir) {
175
- const entries = fs.readdirSync(dir, { withFileTypes: true });
176
-
177
- for (let entry of entries) {
178
- const fullPath = path.join(dir, entry.name);
179
-
180
- if (entry.isDirectory()) {
181
- if (entry.name === 'node_modules') continue;
182
- scanDirectory(fullPath);
183
- } else if (entry.isFile() && ALLOWED_EXTENSIONS.some(ext => fullPath.endsWith(ext))) {
184
- const content = fs.readFileSync(fullPath, 'utf8');
185
-
186
- // Detect iOS version file imports
187
- if (IOS_FILE_REGEX.test(content)) {
188
- iosImportFound = true;
189
- if (!isMac) {
190
- console.error(`\n❌ ERROR: iOS-specific import detected in: ${fullPath}`);
191
- console.error(`🚫 STOPPED: This file should not be imported in Android/Windows/Linux builds.\n`);
192
- process.exit(1);
193
- }
194
- }
195
-
196
- // Detect Android version file imports (but ignore iOS ones)
197
- else if (ANDROID_FILE_REGEX.test(content) && !content.includes('-ios.js')) {
198
- androidImportFound = true;
199
- }
200
- }
201
- }
202
- }
203
-
204
- // Check src folder
205
- if (!fs.existsSync(ROOT_DIR)) {
206
- console.warn(`⚠️ Warning: 'src' directory not found at: ${ROOT_DIR}`);
207
- return;
208
- }
209
-
210
- scanDirectory(ROOT_DIR);
211
-
212
- // iOS Checks
213
- if (isMac && !iosImportFound) {
214
- console.warn(`⚠️ WARNING: You're on macOS but no iOS version (saveToGalleryAndSaveAnyFile-x.x-ios.js) found.`);
215
- process.exit(1);
216
- } else if (isMac && iosImportFound) {
217
- console.log('✅ iOS version detected for macOS build.');
218
- } else if (!iosImportFound) {
219
- console.log('✅ No iOS-specific imports detected for non-macOS.');
220
- }
221
-
222
- // Android Checks
223
- if (androidImportFound) {
224
- console.log("📱 Android version of saveToGalleryAndSaveAnyFile detected. Checking AndroidManifest.xml...");
225
-
226
- if (!fs.existsSync(ANDROID_MANIFEST_PATH)) {
227
- console.error("❌ AndroidManifest.xml not found. Cannot add requestLegacyExternalStorage attribute.");
228
- return;
229
- }
230
-
231
- let manifestContent = fs.readFileSync(ANDROID_MANIFEST_PATH, 'utf8');
232
-
233
- if (!manifestContent.includes('android:requestLegacyExternalStorage="true"')) {
234
- console.log("Adding android:requestLegacyExternalStorage=\"true\" to <application> tag...");
235
-
236
- manifestContent = manifestContent.replace(
237
- /<application([^>]*)>/,
238
- (match, attrs) => {
239
- if (attrs.includes('android:requestLegacyExternalStorage')) return match;
240
- return `<application${attrs} android:requestLegacyExternalStorage="true">`;
241
- }
242
- );
243
-
244
- fs.writeFileSync(ANDROID_MANIFEST_PATH, manifestContent, 'utf8');
245
- console.log("✅ android:requestLegacyExternalStorage=\"true\" added successfully.");
246
- } else {
247
- console.log("ℹ️ android:requestLegacyExternalStorage already exists in AndroidManifest.xml.");
248
- }
249
- } else {
250
- console.log("✅ No Android saveToGalleryAndSaveAnyFile imports detected.");
251
- }
252
- };
253
-
254
- saveToGalleryAndSaveFileCheck_iOS();
255
- // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists END
256
-
257
-
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
-
266
-
267
-
268
-
269
- /*
270
- // Clean up AppleDouble files (._*) created by macOS START
271
- if (process.platform === 'darwin') {
272
- try {
273
- console.log('🧹 Cleaning up AppleDouble files (._*)...');
274
- execSync(`find . -name '._*' -delete`);
275
- console.log('✅ AppleDouble files removed.');
276
- } catch (err) {
277
- console.warn('⚠️ Failed to remove AppleDouble files:', err.message);
278
- }
279
- } else {
280
- console.log('ℹ️ Skipping AppleDouble cleanup — not a macOS machine.');
281
- }
282
-
283
- // Clean up AppleDouble files (._*) created by macOS END
284
- */
285
-
286
-
287
-
288
-
289
-
290
-
291
- //In routes.js file check static import START
292
-
293
- const routesPath = path.join(process.cwd(), 'src', 'js', 'routes.js');
294
- const routesContent = fs.readFileSync(routesPath, 'utf-8');
295
-
296
- let inBlockComment = false;
297
- const lines = routesContent.split('\n');
298
-
299
- const allowedImport = `import HomePage from '../pages/home.f7';`;
300
- const badImportRegex = /^[ \t]*import\s+[\w{}*,\s]*\s+from\s+['"].+\.f7['"]\s*;/;
301
- const badImports = [];
302
-
303
- lines.forEach((line, index) => {
304
- const trimmed = line.trim();
305
-
306
- // Handle block comment start and end
307
- if (trimmed.startsWith('/*')) inBlockComment = true;
308
- if (inBlockComment && trimmed.endsWith('*/')) {
309
- inBlockComment = false;
310
- return;
311
- }
312
-
313
- // Skip if inside block comment or line comment
314
- if (inBlockComment || trimmed.startsWith('//')) return;
315
-
316
- // Match static .f7 import
317
- if (badImportRegex.test(trimmed) && trimmed !== allowedImport) {
318
- badImports.push({ line: trimmed, number: index + 1 });
319
- }
320
- });
321
-
322
- if (badImports.length > 0) {
323
- console.error('\n❌ ERROR: Detected disallowed static imports of .f7 files in routes.js\n');
324
- console.error(`⚠️ Only this static import is allowed:\n ${allowedImport}\n`);
325
- console.error(`🔧 Please convert other imports to async dynamic imports like this:\n`);
326
- console.error(`
327
-
328
- import HomePage from '../pages/home.f7';
329
-
330
- const routes = [
331
- {
332
- path: '/',
333
- component:HomePage,
334
- },
335
- {
336
- path: '/ProfilePage/',
337
- async async({ resolve }) {
338
- const page = await import('../pages/profile.f7');
339
- resolve({ component: page.default });
340
- },
341
- }]
342
- `);
343
-
344
- badImports.forEach(({ line, number }) => {
345
- console.error(`${number}: ${line}`);
346
- });
347
-
348
- process.exit(1);
349
- } else {
350
- console.log('✅ routes.js passed the .f7 import check.');
351
- }
352
-
353
- //In routes.js file check static import END
354
-
355
-
356
-
357
-
358
-
359
-
360
-
361
-
362
-
363
-
364
-
365
-
366
-
367
- // Check and change the "BridgeWebViewClient.java" file START
368
- /*
369
- For crash issue due to low memory problem, we need to modify the onRenderProcessGone method in BridgeWebViewClient.java.
370
- */
371
-
372
-
373
- const bridgeWebViewClientFilePath = path.join(process.cwd(), 'node_modules', '@capacitor/android/capacitor/src/main/java/com/getcapacitor', 'BridgeWebViewClient.java');
374
-
375
- // Read the file
376
- if (!fs.existsSync(bridgeWebViewClientFilePath)) {
377
- console.error('❌ Error: BridgeWebViewClient.java not found.');
378
- process.exit(1);
379
- }
380
-
381
- let fileContent = fs.readFileSync(bridgeWebViewClientFilePath, 'utf8');
382
-
383
- // Define old and new code
384
- const oldCodeStart = `@Override
385
- public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
386
- super.onRenderProcessGone(view, detail);
387
- boolean result = false;
388
-
389
- List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
390
- if (webViewListeners != null) {
391
- for (WebViewListener listener : bridge.getWebViewListeners()) {
392
- result = listener.onRenderProcessGone(view, detail) || result;
393
- }
394
- }
395
-
396
- return result;
397
- }`;
398
-
399
- const newCode = `@Override
400
- public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
401
- super.onRenderProcessGone(view, detail);
402
-
403
- boolean result = false;
404
-
405
- List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
406
- if (webViewListeners != null) {
407
- for (WebViewListener listener : bridge.getWebViewListeners()) {
408
- result = listener.onRenderProcessGone(view, detail) || result;
409
- }
410
- }
411
-
412
- if (!result) {
413
- // If no one handled it, handle it ourselves!
414
-
415
- /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
416
- if (detail.didCrash()) {
417
- //Log.e("CapacitorWebView", "WebView crashed internally!");
418
- } else {
419
- //Log.w("CapacitorWebView", "WebView was killed by system (low memory) internally!");
420
- }
421
- }*/
422
-
423
- view.post(() -> {
424
- Toast.makeText(view.getContext(), "Reloading due to low memory issue", Toast.LENGTH_SHORT).show();
425
- });
426
-
427
- view.reload(); // Safely reload WebView
428
-
429
- return true; // We handled it
430
- }
431
-
432
- return result;
433
- }`;
434
-
435
- // Step 1: Update method if needed
436
- let updated = false;
437
-
438
- if (fileContent.includes(oldCodeStart)) {
439
- console.log('✅ Found old onRenderProcessGone method. Replacing it...');
440
- fileContent = fileContent.replace(oldCodeStart, newCode);
441
- updated = true;
442
- } else if (fileContent.includes(newCode)) {
443
- console.log('ℹ️ Method already updated. No changes needed in "BridgeWebViewClient.java".');
444
- } else {
445
- console.error('❌ Error: Neither old nor new code found. Unexpected content.');
446
- process.exit(1);
447
- }
448
-
449
- // Step 2: Check and add import if missing
450
- const importToast = 'import android.widget.Toast;';
451
- if (!fileContent.includes(importToast)) {
452
- console.log('✅ Adding missing import for Toast...');
453
- const importRegex = /import\s+[^;]+;/g;
454
- const matches = [...fileContent.matchAll(importRegex)];
455
-
456
- if (matches.length > 0) {
457
- const lastImport = matches[matches.length - 1];
458
- const insertPosition = lastImport.index + lastImport[0].length;
459
- fileContent = fileContent.slice(0, insertPosition) + `\n${importToast}` + fileContent.slice(insertPosition);
460
- updated = true;
461
- } else {
462
- console.error('❌ Error: No import section found in file.');
463
- process.exit(1);
464
- }
465
- } else {
466
- console.log('ℹ️ Import for Toast already exists. No changes needed.');
467
- }
468
-
469
- // Step 3: Save if updated
470
- if (updated) {
471
- fs.writeFileSync(bridgeWebViewClientFilePath, fileContent, 'utf8');
472
- console.log('✅ File updated successfully.');
473
- } else {
474
- console.log('ℹ️ No changes needed.');
475
- }
476
-
477
-
478
-
479
-
480
- // Check and change the "BridgeWebViewClient.java" file END
481
-
482
-
483
-
484
-
485
-
486
-
487
-
488
-
489
-
490
- // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file START
491
-
492
- // Build the path dynamically like you requested
493
- const gradlePath = path.join(
494
- process.cwd(),
495
- 'android',
496
- 'build.gradle'
497
- );
498
-
499
- // Read the existing build.gradle
500
- let gradleContent = fs.readFileSync(gradlePath, 'utf8');
501
-
502
- // Add `ext.kotlin_version` if it's not already there
503
- if (!gradleContent.includes('ext.kotlin_version')) {
504
- gradleContent = gradleContent.replace(
505
- /buildscript\s*{/,
506
- `buildscript {\n ext.kotlin_version = '2.1.0'`
507
- );
508
- }
509
-
510
- // Add Kotlin classpath if it's not already there
511
- if (!gradleContent.includes('org.jetbrains.kotlin:kotlin-gradle-plugin')) {
512
- gradleContent = gradleContent.replace(
513
- /dependencies\s*{([\s\S]*?)classpath 'com.android.tools.build:gradle:8.7.2'/,
514
- `dependencies {\n classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")\n$1classpath 'com.android.tools.build:gradle:8.7.2'`
515
- );
516
- }
517
-
518
- // Write back the modified content
519
- fs.writeFileSync(gradlePath, gradleContent, 'utf8');
520
-
521
- console.log('✅ Kotlin version updated in build.gradle.');
522
-
523
- // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file END
524
-
525
-
526
-
527
-
528
-
529
-
530
-
531
-
532
-
533
-
534
-
535
-
536
-
537
- const androidPlatformPath = path.join(process.cwd(), 'android');
538
- const iosPlatformPath = path.join(process.cwd(), 'ios');
539
- const pluginPath = path.join(process.cwd(), 'node_modules', 'emi-indo-cordova-plugin-admob', 'plugin.xml');
540
- const infoPlistPath = path.join(process.cwd(), 'ios', 'App', 'App', 'Info.plist');
541
- const resourcesPath = path.join(process.cwd(), 'resources', 'res');
542
- const androidResPath = path.join(process.cwd(), 'android', 'app', 'src', 'main', 'res');
543
- const localNotificationsPluginPath = path.join(process.cwd(), 'node_modules', '@capacitor', 'local-notifications');
544
-
545
- function fileExists(filePath) {
546
- return fs.existsSync(filePath);
547
- }
548
-
549
- function copyFolderSync(source, target) {
550
- if (!fs.existsSync(target)) {
551
- fs.mkdirSync(target, { recursive: true });
552
- }
553
-
554
- fs.readdirSync(source).forEach(file => {
555
- const sourceFile = path.join(source, file);
556
- const targetFile = path.join(target, file);
557
-
558
- if (fs.lstatSync(sourceFile).isDirectory()) {
559
- copyFolderSync(sourceFile, targetFile);
560
- } else {
561
- fs.copyFileSync(sourceFile, targetFile);
562
- }
563
- });
564
- }
565
-
566
- function checkAndCopyResources() {
567
- if (fileExists(resourcesPath)) {
568
- copyFolderSync(resourcesPath, androidResPath);
569
- console.log('✅ Successfully copied resources/res to android/app/src/main/res.');
570
- } else {
571
- console.log('resources/res folder not found.');
572
-
573
- if (fileExists(localNotificationsPluginPath)) {
574
- throw new Error('❌ resources/res is required for @capacitor/local-notifications. Stopping execution.');
575
- }
576
- }
577
- }
578
-
579
-
580
-
581
- function getAdMobConfig() {
582
- if (!fileExists(configPath)) {
583
- throw new Error('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
584
- }
585
-
586
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
587
- const admobConfig = config.plugins?.AdMob;
588
-
589
- if (!admobConfig) {
590
- throw new Error('❌ AdMob configuration is missing in capacitor.config.json.');
591
- }
592
-
593
- // Default to true if ADMOB_ENABLED is not specified
594
- const isEnabled = admobConfig.ADMOB_ENABLED !== false;
595
-
596
- if (!isEnabled) {
597
- return { ADMOB_ENABLED: false }; // Skip further validation
598
- }
599
-
600
- if (!admobConfig.APP_ID_ANDROID || !admobConfig.APP_ID_IOS) {
601
- throw new Error(' ❌ AdMob configuration is incomplete. Ensure APP_ID_ANDROID and APP_ID_IOS are defined.');
602
- }
603
-
604
- return {
605
- ADMOB_ENABLED: true,
606
- APP_ID_ANDROID: admobConfig.APP_ID_ANDROID,
607
- APP_ID_IOS: admobConfig.APP_ID_IOS,
608
- USE_LITE_ADS: admobConfig.USE_LITE_ADS === "lite",
609
- };
610
- }
611
-
612
- function validateAndroidBuildOptions() {
613
-
614
-
615
- if (!fileExists(configPath)) {
616
- console.log('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
617
- process.exit(1);
618
- }
619
-
620
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
621
-
622
- const targetAppId=config.appId
623
-
624
- const buildOptions = config.android?.buildOptions;
625
-
626
- if (!buildOptions) {
627
- console.log('❌ Missing android.buildOptions in capacitor.config.json.');
628
- process.exit(1);
629
- }
630
-
631
- const requiredProps = [
632
- 'keystorePath',
633
- 'keystorePassword',
634
- 'keystoreAlias',
635
- 'keystoreAliasPassword',
636
- 'releaseType',
637
- 'signingType'
638
- ];
639
-
640
- const missing = requiredProps.filter(prop => !buildOptions[prop]);
641
-
642
- if (missing.length > 0) {
643
- console.log('❌ Missing properties android.buildOptions in capacitor.config.json.');
644
- process.exit(1);
645
- }
646
-
647
-
648
- const keystorePath=buildOptions.keystorePath
649
- const keyFileName = path.basename(keystorePath);
650
-
651
-
652
-
653
- const keystoreMap = {
654
- "gameskey.jks": [
655
- "com.cube.blaster",
656
- ],
657
- "htmleditorkeystoke.jks": [
658
- "com.HTML.AngularJS.Codeplay",
659
- "com.html.codeplay.pro",
660
- "com.bootstrap.code.play",
661
- "com.kids.learning.master",
662
- "com.Simple.Barcode.Scanner"
663
- ]
664
- };
665
-
666
- // find which keystore is required for the given targetAppId
667
- let requiredKey = "newappskey.jks"; // default
668
- for (const [keyFile, appIds] of Object.entries(keystoreMap)) {
669
- if (appIds.includes(targetAppId)) {
670
- requiredKey = keyFile;
671
- break;
672
- }
673
- }
674
-
675
- // validate
676
- if (keyFileName !== requiredKey) {
677
- console.log(`❌ The keystore path is mismatched. Expected ${requiredKey} for ${targetAppId}, but got ${keyFileName}`);
678
- process.exit(1);
679
- }
680
-
681
-
682
-
683
-
684
-
685
- // optionally return them
686
- //return buildOptions;
687
- }
688
-
689
- function updatePluginXml(admobConfig) {
690
- if (!fileExists(pluginPath)) {
691
- console.error(' ❌ plugin.xml not found. Ensure the plugin is installed.');
692
- return;
693
- }
694
-
695
- let pluginContent = fs.readFileSync(pluginPath, 'utf8');
696
-
697
- pluginContent = pluginContent
698
- .replace(/<preference name="APP_ID_ANDROID" default=".*?" \/>/, `<preference name="APP_ID_ANDROID" default="${admobConfig.APP_ID_ANDROID}" />`)
699
- .replace(/<preference name="APP_ID_IOS" default=".*?" \/>/, `<preference name="APP_ID_IOS" default="${admobConfig.APP_ID_IOS}" />`);
700
-
701
- fs.writeFileSync(pluginPath, pluginContent, 'utf8');
702
- console.log(' AdMob IDs successfully updated in plugin.xml');
703
- }
704
-
705
- function updateInfoPlist(admobConfig) {
706
- if (!fileExists(infoPlistPath)) {
707
- console.error(' ❌ Info.plist not found. Ensure you have built the iOS project.');
708
- return;
709
- }
710
-
711
- const plistContent = fs.readFileSync(infoPlistPath, 'utf8');
712
- const plistData = plist.parse(plistContent);
713
-
714
- plistData.GADApplicationIdentifier = admobConfig.APP_ID_IOS;
715
- plistData.NSUserTrackingUsageDescription = 'This identifier will be used to deliver personalized ads to you.';
716
- plistData.GADDelayAppMeasurementInit = true;
717
-
718
- const updatedPlistContent = plist.build(plistData);
719
- fs.writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
720
- console.log('AdMob IDs and additional configurations successfully updated in Info.plist');
721
- }
722
-
723
- try {
724
- if (!fileExists(configPath)) {
725
- throw new Error(' ❌ capacitor.config.json not found. Skipping setup.');
726
- }
727
-
728
- if (!fileExists(androidPlatformPath) && !fileExists(iosPlatformPath)) {
729
- throw new Error('Neither Android nor iOS platforms are found. Ensure platforms are added to your Capacitor project.');
730
- }
731
-
732
- checkAndCopyResources();
733
-
734
- const admobConfig = getAdMobConfig();
735
-
736
-
737
-
738
-
739
- // Proceed only if ADMOB_ENABLED is true
740
- if (admobConfig.ADMOB_ENABLED) {
741
- if (fileExists(androidPlatformPath)) {
742
- updatePluginXml(admobConfig);
743
- }
744
-
745
- if (fileExists(iosPlatformPath)) {
746
- updateInfoPlist(admobConfig);
747
- }
748
- }
749
-
750
-
751
- } catch (error) {
752
- console.error(error.message);
753
- process.exit(1); // Stop execution if there's a critical error
754
- }
755
-
756
-
757
-
758
- validateAndroidBuildOptions();
759
-
760
-
761
-
762
-
763
-
764
-
765
- // Check all the codeplays plugins version START
766
-
767
-
768
- const readline = require('readline');
769
-
770
-
771
- //const srcDir = path.join(__dirname, 'src');
772
- const srcDir = path.join(process.cwd(), 'src');
773
- let outdatedPlugins = [];
774
-
775
- function parseVersion(ver) {
776
- return ver.split('.').map(n => parseInt(n, 10));
777
- }
778
-
779
- function compareVersions(v1, v2) {
780
- const [a1, b1] = parseVersion(v1);
781
- const [a2, b2] = parseVersion(v2);
782
- if (a1 !== a2) return a1 - a2;
783
- return b1 - b2;
784
- }
785
-
786
- function walkSync(dir, filelist = []) {
787
- fs.readdirSync(dir).forEach(file => {
788
- const fullPath = path.join(dir, file);
789
- const stat = fs.statSync(fullPath);
790
- if (stat.isDirectory()) {
791
- walkSync(fullPath, filelist);
792
- } else {
793
- filelist.push(fullPath);
794
- }
795
- });
796
- return filelist;
797
- }
798
-
799
- function checkPlugins() {
800
- const files = walkSync(srcDir);
801
-
802
- for (const plugin of requiredPlugins) {
803
- if (plugin.isFolder) {
804
-
805
- let baseFolder = path.join(srcDir,'js', 'Ads'); // <- use known folder name
806
-
807
- if (fs.existsSync(baseFolder)) {
808
- const subDirs = fs.readdirSync(baseFolder)
809
- .map(name => path.join(baseFolder, name))
810
- .filter(p => fs.statSync(p).isDirectory());
811
-
812
- for (const dir of subDirs) {
813
- const relativePath = path.relative(srcDir, dir).replace(/\\/g, '/'); // e.g. Ads/IAP-2.0
814
- const match = plugin.pattern.exec(relativePath);
815
- if (match) {
816
- const currentVersion = match[1];
817
- if (compareVersions(currentVersion, plugin.minVersion) < 0) {
818
- outdatedPlugins.push({
819
- name: relativePath,
820
- currentVersion,
821
- requiredVersion: plugin.minVersion
822
- });
823
- }
824
- }
825
- }
826
- }
827
- continue;
828
- }
829
-
830
- const matchedFile = files.find(file => plugin.pattern.test(file));
831
- if (matchedFile) {
832
- const match = plugin.pattern.exec(matchedFile);
833
- if (match) {
834
- const currentVersion = match[1];
835
- if (compareVersions(currentVersion, plugin.minVersion) < 0) {
836
- outdatedPlugins.push({
837
- name: path.relative(__dirname, matchedFile),
838
- currentVersion,
839
- requiredVersion: plugin.minVersion
840
- });
841
- }
842
- }
843
- }
844
- }
845
-
846
- if (outdatedPlugins.length > 0) {
847
- console.log('\n❗ The following plugins are outdated:');
848
- outdatedPlugins.forEach(p => {
849
- console.log(` ❌ - ${p.name} (Current: ${p.currentVersion}, Required: ${p.requiredVersion})`);
850
- });
851
-
852
- const rl = readline.createInterface({
853
- input: process.stdin,
854
- output: process.stdout
855
- });
856
-
857
- rl.question('\nAre you sure you want to continue without updating these plugins? (y/n): ', answer => {
858
- if (answer.toLowerCase() !== 'y') {
859
- console.log('\n❌ Build cancelled due to outdated plugins.');
860
- process.exit(1);
861
- } else {
862
- console.log('\n✅ Continuing build...');
863
- rl.close();
864
- }
865
- });
866
- } else {
867
- console.log('✅ All plugin versions are up to date.');
868
- }
869
- }
870
-
871
- // Run the validation
872
- checkPlugins();
873
-
874
-
875
-
876
-
877
- // Check all the codeplays plugins version START
878
-
879
-
880
-
881
-
882
-
883
-
884
-
885
-
886
-
887
-
888
-
889
-
890
-
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const plist = require('plist');
4
+
5
+
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.4', required: true },
14
+ { pattern: /common-(\d+\.\d+)\.js$/, minVersion: '4.0', required: true },
15
+ { pattern: /localization_settings-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true },
16
+ { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.3', required: true },
17
+ { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true },
18
+ { pattern: /localNotification_AppSettings-(\d+\.\d+)\.js$/, minVersion: '1.0', required: true },
19
+ { pattern: /onesignal-(\d+\.\d+)\.js$/, minVersion: '2.1', required: true },
20
+ { pattern: /saveToGalleryAndSaveAnyFile-(\d+\.\d+)(-ios)?\.js$/, minVersion: '2.5', required: true },
21
+ { pattern: /Ads[\/\\]IAP-(\d+\.\d+)$/, minVersion: '2.5', isFolder: true , required: true},
22
+ { pattern: /Ads[\/\\]admob-emi-(\d+\.\d+)\.js$/, minVersion: '2.8', required: true }
23
+ ];
24
+
25
+
26
+
27
+
28
+
29
+
30
+ //Check codeplay-common latest version installed or not Start
31
+ const { execSync } = require('child_process');
32
+
33
+ function getInstalledVersion(packageName) {
34
+ try {
35
+ const packageJsonPath = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
36
+ if (fs.existsSync(packageJsonPath)) {
37
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
38
+ return packageJson.version;
39
+ }
40
+ } catch (error) {
41
+ return null;
42
+ }
43
+ return null;
44
+ }
45
+
46
+ function getLatestVersion(packageName) {
47
+ try {
48
+ return execSync(`npm view ${packageName} version`).toString().trim();
49
+ } catch (error) {
50
+ console.error(`Failed to fetch latest version for ${packageName}`);
51
+ return null;
52
+ }
53
+ }
54
+
55
+ function checkPackageVersion() {
56
+ const packageName = 'codeplay-common';
57
+ const installedVersion = getInstalledVersion(packageName);
58
+ const latestVersion = getLatestVersion(packageName);
59
+
60
+ if (!installedVersion) {
61
+ console.error(`${packageName} is not installed. Please install it using "npm install ${packageName}".`);
62
+ process.exit(1);
63
+ }
64
+
65
+ if (installedVersion !== latestVersion) {
66
+ 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`);
67
+ process.exit(1);
68
+ }
69
+
70
+ console.log(`${packageName} is up to date (version ${installedVersion}).`);
71
+ }
72
+
73
+ // Run package version check before executing the main script
74
+ try {
75
+ checkPackageVersion();
76
+ } catch (error) {
77
+ console.error(error.message);
78
+ process.exit(1);
79
+ }
80
+
81
+ //Check codeplay-common latest version installed or not END
82
+
83
+
84
+
85
+
86
+
87
+
88
+ const checkAppUniqueId=()=>{
89
+
90
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
91
+
92
+ const appUniqueId = config.android?.APP_UNIQUE_ID;
93
+ const RESIZEABLE_ACTIVITY = config.android?.RESIZEABLE_ACTIVITY;
94
+ const orientation = config.android?.ORIENTATION;
95
+
96
+
97
+ let logErrorMessage="";
98
+
99
+ // 1️⃣ Check if it’s missing
100
+ if (RESIZEABLE_ACTIVITY === undefined) {
101
+ logErrorMessage+='❌ Missing android.RESIZEABLE_ACTIVITY option in capacitor.config.json.\n';
102
+ }
103
+
104
+ // 2️⃣ Check if it’s not boolean (true/false only)
105
+ else if (typeof RESIZEABLE_ACTIVITY !== 'boolean') {
106
+ logErrorMessage+='❌ Invalid android.RESIZEABLE_ACTIVITY value. Please use only true or false (without quotes).\n';
107
+ }
108
+
109
+
110
+
111
+ if (!orientation) {
112
+ logErrorMessage+='❌ Missing android.ORIENTATION option in capacitor.config.json.\n';
113
+ }
114
+
115
+ else if(orientation!="portrait" && orientation!="landscape" && orientation!="auto")
116
+ {
117
+ logErrorMessage+='❌ Spelling mistake in android.ORIENTATION option in capacitor.config.json. Please use only ["portrait" "landscape" "auto"]\n';
118
+ }
119
+
120
+
121
+ if (!appUniqueId) {
122
+ logErrorMessage+='❌ APP_UNIQUE_ID is missing in capacitor.config.json.';
123
+ }
124
+
125
+ else if (!Number.isInteger(appUniqueId)) {
126
+ logErrorMessage+='❌ APP_UNIQUE_ID must be an integer. Example: 1, 2, 3, etc.';
127
+ }
128
+
129
+
130
+
131
+ if(logErrorMessage!="")
132
+ {
133
+ console.error(logErrorMessage);
134
+ process.exit(1)
135
+ }
136
+
137
+
138
+ console.log(`✅ APP_UNIQUE_ID is valid: ${appUniqueId}`);
139
+
140
+ }
141
+
142
+ checkAppUniqueId();
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+ //@Codemirror check and install/uninstall the packages START
151
+ //const fs = require("fs");
152
+ //const path = require("path");
153
+ //const { execSync } = require("child_process");
154
+
155
+ const baseDir = path.join(__dirname, "..", "src", "js");
156
+
157
+ // Step 1: Find highest versioned folder like `editor-1.6`
158
+ const editorDirs = fs.readdirSync(baseDir)
159
+ .filter(name => /^editor-\d+\.\d+$/.test(name))
160
+ .sort((a, b) => {
161
+ const getVersion = str => str.match(/(\d+)\.(\d+)/).slice(1).map(Number);
162
+ const [aMajor, aMinor] = getVersion(a);
163
+ const [bMajor, bMinor] = getVersion(b);
164
+ return bMajor - aMajor || bMinor - aMinor;
165
+ });
166
+
167
+ if (editorDirs.length === 0) {
168
+
169
+ console.log("@Codemirror used editor(s) are not found")
170
+ //console.error("❌ No editor-x.x folders found in src/js.");
171
+ //process.exit(1);
172
+ }
173
+ else
174
+ {
175
+
176
+ const latestEditorDir = editorDirs.sort((a, b) => {
177
+ const versionA = parseFloat(a.split('-')[1]);
178
+ const versionB = parseFloat(b.split('-')[1]);
179
+ return versionB - versionA;
180
+ })[0];
181
+
182
+ //const latestEditorDir = editorDirs[editorDirs.length - 1];
183
+ const runJsPath = path.join(baseDir, latestEditorDir, "run.js");
184
+
185
+ if (!fs.existsSync(runJsPath)) {
186
+ console.error(`❌ run.js not found in ${latestEditorDir}`);
187
+ process.exit(1);
188
+ }
189
+
190
+ // Step 2: Execute the run.js file
191
+ console.log(`🚀 Executing ${runJsPath}...`);
192
+ execSync(`node "${runJsPath}"`, { stdio: "inherit" });
193
+ }
194
+
195
+ //@Codemirror check and install/uninstall the packages END
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+ // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists START
209
+
210
+ const os = require('os');
211
+
212
+ const saveToGalleryAndSaveFileCheck_iOS = () => {
213
+
214
+ // List of paths to scan
215
+ const SCAN_PATHS = [
216
+ path.resolve(__dirname, '../src/certificate'),
217
+ path.resolve(__dirname, '../src/pages'),
218
+ path.resolve(__dirname, '../src/js'),
219
+ path.resolve(__dirname, '../src/app.f7')
220
+ ];
221
+
222
+ // Directory to exclude
223
+ const EXCLUDED_DIR = path.resolve(__dirname, '../src/js/Ads');
224
+
225
+ const ANDROID_MANIFEST_PATH = path.resolve(__dirname, '../android/app/src/main/AndroidManifest.xml');
226
+
227
+
228
+ // Match iOS-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5-ios.js) not in comments
229
+ const IOS_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*-ios\.js['"]/m;
230
+
231
+ // Match Android-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5.js) not in comments
232
+ const ANDROID_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*\.js['"]/m;
233
+
234
+
235
+
236
+
237
+
238
+ const ALLOWED_EXTENSIONS = ['.js', '.f7'];
239
+ const isMac = os.platform() === 'darwin';
240
+
241
+ let iosImportFound = false;
242
+ let androidImportFound = false;
243
+
244
+ function scanDirectory(dir) {
245
+ const stat = fs.statSync(dir);
246
+
247
+ if (stat.isFile()) {
248
+ // Only scan allowed file extensions
249
+ if (ALLOWED_EXTENSIONS.some(ext => dir.endsWith(ext))) {
250
+ process.stdout.write(`\r🔍 Scanning: ${dir} `);
251
+
252
+ const content = fs.readFileSync(dir, 'utf8');
253
+
254
+ if (IOS_FILE_REGEX.test(content)) {
255
+ iosImportFound = true;
256
+ if (!isMac) {
257
+ console.error(`\n❌ ERROR: iOS-specific import detected in: ${dir}`);
258
+ console.error(`🚫 STOPPED: This file should not be imported in Android/Windows/Linux builds.\n`);
259
+ process.exit(1);
260
+ }
261
+ } else if (ANDROID_FILE_REGEX.test(content) && !content.includes('-ios.js')) {
262
+ androidImportFound = true;
263
+ }
264
+ }
265
+ }
266
+ else if (stat.isDirectory()) {
267
+ if (dir === EXCLUDED_DIR || path.basename(dir) === 'node_modules') return;
268
+
269
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
270
+ for (let entry of entries) {
271
+ scanDirectory(path.join(dir, entry.name));
272
+ }
273
+ }
274
+ }
275
+
276
+ // Run scan on all specified paths
277
+ for (let scanPath of SCAN_PATHS) {
278
+ if (fs.existsSync(scanPath)) {
279
+ scanDirectory(scanPath);
280
+ }
281
+ }
282
+
283
+
284
+
285
+ /* // Check src folder
286
+ if (!fs.existsSync(ROOT_DIR)) {
287
+ console.warn(`⚠️ Warning: 'src' directory not found at: ${ROOT_DIR}`);
288
+ return;
289
+ } */
290
+
291
+ //scanDirectory(ROOT_DIR);
292
+
293
+ // iOS Checks
294
+ if (isMac && !iosImportFound) {
295
+ console.warn(`⚠️ WARNING: You're on macOS but no iOS version (saveToGalleryAndSaveAnyFile-x.x-ios.js) found.`);
296
+ process.exit(1);
297
+ } else if (isMac && iosImportFound) {
298
+ console.log('✅ iOS version detected for macOS build.');
299
+ } else if (!iosImportFound) {
300
+ console.log('✅ No iOS-specific imports detected for non-macOS.');
301
+ }
302
+
303
+ // Android Checks
304
+ if (androidImportFound) {
305
+ console.log("📱 Android version of saveToGalleryAndSaveAnyFile detected. Checking AndroidManifest.xml...");
306
+
307
+ if (!fs.existsSync(ANDROID_MANIFEST_PATH)) {
308
+ console.error("❌ AndroidManifest.xml not found. Cannot add requestLegacyExternalStorage attribute.");
309
+ return;
310
+ }
311
+
312
+ let manifestContent = fs.readFileSync(ANDROID_MANIFEST_PATH, 'utf8');
313
+
314
+ if (!manifestContent.includes('android:requestLegacyExternalStorage="true"')) {
315
+ console.log("Adding android:requestLegacyExternalStorage=\"true\" to <application> tag...");
316
+
317
+ manifestContent = manifestContent.replace(
318
+ /<application([^>]*)>/,
319
+ (match, attrs) => {
320
+ if (attrs.includes('android:requestLegacyExternalStorage')) return match;
321
+ return `<application${attrs} android:requestLegacyExternalStorage="true">`;
322
+ }
323
+ );
324
+
325
+ fs.writeFileSync(ANDROID_MANIFEST_PATH, manifestContent, 'utf8');
326
+ console.log("✅ android:requestLegacyExternalStorage=\"true\" added successfully.");
327
+ } else {
328
+ console.log("ℹ️ android:requestLegacyExternalStorage already exists in AndroidManifest.xml.");
329
+ }
330
+ } else {
331
+ console.log("✅ No Android saveToGalleryAndSaveAnyFile imports detected.");
332
+ }
333
+ };
334
+
335
+ saveToGalleryAndSaveFileCheck_iOS();
336
+ // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists END
337
+
338
+
339
+
340
+
341
+
342
+
343
+
344
+
345
+
346
+
347
+
348
+
349
+
350
+ /*
351
+ // Clean up AppleDouble files (._*) created by macOS START
352
+ if (process.platform === 'darwin') {
353
+ try {
354
+ console.log('🧹 Cleaning up AppleDouble files (._*)...');
355
+ execSync(`find . -name '._*' -delete`);
356
+ console.log('✅ AppleDouble files removed.');
357
+ } catch (err) {
358
+ console.warn('⚠️ Failed to remove AppleDouble files:', err.message);
359
+ }
360
+ } else {
361
+ console.log('ℹ️ Skipping AppleDouble cleanup — not a macOS machine.');
362
+ }
363
+
364
+ // Clean up AppleDouble files (._*) created by macOS END
365
+ */
366
+
367
+
368
+
369
+
370
+
371
+
372
+ //In routes.js file check static import START
373
+
374
+ const routesPath = path.join(process.cwd(), 'src', 'js', 'routes.js');
375
+ const routesContent = fs.readFileSync(routesPath, 'utf-8');
376
+
377
+ let inBlockComment = false;
378
+ const lines = routesContent.split('\n');
379
+
380
+ const allowedImport = `import HomePage from '../pages/home.f7';`;
381
+ const badImportRegex = /^[ \t]*import\s+[\w{}*,\s]*\s+from\s+['"].+\.f7['"]\s*;/;
382
+ const badImports = [];
383
+
384
+ lines.forEach((line, index) => {
385
+ const trimmed = line.trim();
386
+
387
+ // Handle block comment start and end
388
+ if (trimmed.startsWith('/*')) inBlockComment = true;
389
+ if (inBlockComment && trimmed.endsWith('*/')) {
390
+ inBlockComment = false;
391
+ return;
392
+ }
393
+
394
+ // Skip if inside block comment or line comment
395
+ if (inBlockComment || trimmed.startsWith('//')) return;
396
+
397
+ // Match static .f7 import
398
+ if (badImportRegex.test(trimmed) && trimmed !== allowedImport) {
399
+ badImports.push({ line: trimmed, number: index + 1 });
400
+ }
401
+ });
402
+
403
+ if (badImports.length > 0) {
404
+ console.error('\n❌ ERROR: Detected disallowed static imports of .f7 files in routes.js\n');
405
+ console.error(`⚠️ Only this static import is allowed:\n ${allowedImport}\n`);
406
+ console.error(`🔧 Please convert other imports to async dynamic imports like this:\n`);
407
+ console.error(`
408
+
409
+ import HomePage from '../pages/home.f7';
410
+
411
+ const routes = [
412
+ {
413
+ path: '/',
414
+ component:HomePage,
415
+ },
416
+ {
417
+ path: '/ProfilePage/',
418
+ async async({ resolve }) {
419
+ const page = await import('../pages/profile.f7');
420
+ resolve({ component: page.default });
421
+ },
422
+ }]
423
+ `);
424
+
425
+ badImports.forEach(({ line, number }) => {
426
+ console.error(`${number}: ${line}`);
427
+ });
428
+
429
+ process.exit(1);
430
+ } else {
431
+ console.log('✅ routes.js passed the .f7 import check.');
432
+ }
433
+
434
+ //In routes.js file check static import END
435
+
436
+
437
+
438
+
439
+
440
+
441
+
442
+
443
+
444
+
445
+
446
+
447
+
448
+ // Check and change the "BridgeWebViewClient.java" file START
449
+ /*
450
+ For crash issue due to low memory problem, we need to modify the onRenderProcessGone method in BridgeWebViewClient.java.
451
+ */
452
+
453
+
454
+ const bridgeWebViewClientFilePath = path.join(process.cwd(), 'node_modules', '@capacitor/android/capacitor/src/main/java/com/getcapacitor', 'BridgeWebViewClient.java');
455
+
456
+ // Read the file
457
+ if (!fs.existsSync(bridgeWebViewClientFilePath)) {
458
+ console.error('❌ Error: BridgeWebViewClient.java not found.');
459
+ process.exit(1);
460
+ }
461
+
462
+ let fileContent = fs.readFileSync(bridgeWebViewClientFilePath, 'utf8');
463
+
464
+ // Define old and new code
465
+ const oldCodeStart = `@Override
466
+ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
467
+ super.onRenderProcessGone(view, detail);
468
+ boolean result = false;
469
+
470
+ List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
471
+ if (webViewListeners != null) {
472
+ for (WebViewListener listener : bridge.getWebViewListeners()) {
473
+ result = listener.onRenderProcessGone(view, detail) || result;
474
+ }
475
+ }
476
+
477
+ return result;
478
+ }`;
479
+
480
+ const newCode = `@Override
481
+ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
482
+ super.onRenderProcessGone(view, detail);
483
+
484
+ boolean result = false;
485
+
486
+ List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
487
+ if (webViewListeners != null) {
488
+ for (WebViewListener listener : bridge.getWebViewListeners()) {
489
+ result = listener.onRenderProcessGone(view, detail) || result;
490
+ }
491
+ }
492
+
493
+ if (!result) {
494
+ // If no one handled it, handle it ourselves!
495
+
496
+ /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
497
+ if (detail.didCrash()) {
498
+ //Log.e("CapacitorWebView", "WebView crashed internally!");
499
+ } else {
500
+ //Log.w("CapacitorWebView", "WebView was killed by system (low memory) internally!");
501
+ }
502
+ }*/
503
+
504
+ view.post(() -> {
505
+ Toast.makeText(view.getContext(), "Reloading due to low memory issue", Toast.LENGTH_SHORT).show();
506
+ });
507
+
508
+ view.reload(); // Safely reload WebView
509
+
510
+ return true; // We handled it
511
+ }
512
+
513
+ return result;
514
+ }`;
515
+
516
+ // Step 1: Update method if needed
517
+ let updated = false;
518
+
519
+ if (fileContent.includes(oldCodeStart)) {
520
+ console.log('✅ Found old onRenderProcessGone method. Replacing it...');
521
+ fileContent = fileContent.replace(oldCodeStart, newCode);
522
+ updated = true;
523
+ } else if (fileContent.includes(newCode)) {
524
+ console.log('ℹ️ Method already updated. No changes needed in "BridgeWebViewClient.java".');
525
+ } else {
526
+ console.error('❌ Error: Neither old nor new code found. Unexpected content.');
527
+ process.exit(1);
528
+ }
529
+
530
+ // Step 2: Check and add import if missing
531
+ const importToast = 'import android.widget.Toast;';
532
+ if (!fileContent.includes(importToast)) {
533
+ console.log('✅ Adding missing import for Toast...');
534
+ const importRegex = /import\s+[^;]+;/g;
535
+ const matches = [...fileContent.matchAll(importRegex)];
536
+
537
+ if (matches.length > 0) {
538
+ const lastImport = matches[matches.length - 1];
539
+ const insertPosition = lastImport.index + lastImport[0].length;
540
+ fileContent = fileContent.slice(0, insertPosition) + `\n${importToast}` + fileContent.slice(insertPosition);
541
+ updated = true;
542
+ } else {
543
+ console.error('❌ Error: No import section found in file.');
544
+ process.exit(1);
545
+ }
546
+ } else {
547
+ console.log('ℹ️ Import for Toast already exists. No changes needed.');
548
+ }
549
+
550
+ // Step 3: Save if updated
551
+ if (updated) {
552
+ fs.writeFileSync(bridgeWebViewClientFilePath, fileContent, 'utf8');
553
+ console.log('✅ File updated successfully.');
554
+ } else {
555
+ console.log('ℹ️ No changes needed.');
556
+ }
557
+
558
+
559
+
560
+
561
+ // Check and change the "BridgeWebViewClient.java" file END
562
+
563
+
564
+
565
+
566
+
567
+
568
+
569
+
570
+
571
+ // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file START
572
+
573
+ // Build the path dynamically like you requested
574
+ const gradlePath = path.join(
575
+ process.cwd(),
576
+ 'android',
577
+ 'build.gradle'
578
+ );
579
+
580
+ // Read the existing build.gradle
581
+ let gradleContent = fs.readFileSync(gradlePath, 'utf8');
582
+
583
+ // Add `ext.kotlin_version` if it's not already there
584
+ if (!gradleContent.includes('ext.kotlin_version')) {
585
+ gradleContent = gradleContent.replace(
586
+ /buildscript\s*{/,
587
+ `buildscript {\n ext.kotlin_version = '2.1.0'`
588
+ );
589
+ }
590
+
591
+ // Add Kotlin classpath if it's not already there
592
+ if (!gradleContent.includes('org.jetbrains.kotlin:kotlin-gradle-plugin')) {
593
+ gradleContent = gradleContent.replace(
594
+ /dependencies\s*{([\s\S]*?)classpath 'com.android.tools.build:gradle:8.7.2'/,
595
+ `dependencies {\n classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")\n$1classpath 'com.android.tools.build:gradle:8.7.2'`
596
+ );
597
+ }
598
+
599
+ // Write back the modified content
600
+ fs.writeFileSync(gradlePath, gradleContent, 'utf8');
601
+
602
+ console.log('✅ Kotlin version updated in build.gradle.');
603
+
604
+ // To resolve the kotlin version issue, we need to update the kotlin version in the build.gradle file END
605
+
606
+
607
+
608
+
609
+
610
+
611
+
612
+
613
+
614
+
615
+
616
+
617
+
618
+ const androidPlatformPath = path.join(process.cwd(), 'android');
619
+ const iosPlatformPath = path.join(process.cwd(), 'ios');
620
+ const pluginPath = path.join(process.cwd(), 'node_modules', 'emi-indo-cordova-plugin-admob', 'plugin.xml');
621
+ const infoPlistPath = path.join(process.cwd(), 'ios', 'App', 'App', 'Info.plist');
622
+ const resourcesPath = path.join(process.cwd(), 'resources', 'res');
623
+ const androidResPath = path.join(process.cwd(), 'android', 'app', 'src', 'main', 'res');
624
+ const localNotificationsPluginPath = path.join(process.cwd(), 'node_modules', '@capacitor', 'local-notifications');
625
+
626
+ function fileExists(filePath) {
627
+ return fs.existsSync(filePath);
628
+ }
629
+
630
+ function copyFolderSync(source, target) {
631
+ if (!fs.existsSync(target)) {
632
+ fs.mkdirSync(target, { recursive: true });
633
+ }
634
+
635
+ fs.readdirSync(source).forEach(file => {
636
+ const sourceFile = path.join(source, file);
637
+ const targetFile = path.join(target, file);
638
+
639
+ if (fs.lstatSync(sourceFile).isDirectory()) {
640
+ copyFolderSync(sourceFile, targetFile);
641
+ } else {
642
+ fs.copyFileSync(sourceFile, targetFile);
643
+ }
644
+ });
645
+ }
646
+
647
+ function checkAndCopyResources() {
648
+ if (fileExists(resourcesPath)) {
649
+ copyFolderSync(resourcesPath, androidResPath);
650
+ console.log('✅ Successfully copied resources/res to android/app/src/main/res.');
651
+ } else {
652
+ console.log('resources/res folder not found.');
653
+
654
+ if (fileExists(localNotificationsPluginPath)) {
655
+ throw new Error('❌ resources/res is required for @capacitor/local-notifications. Stopping execution.');
656
+ }
657
+ }
658
+ }
659
+
660
+
661
+
662
+
663
+
664
+
665
+
666
+
667
+ function getAdMobConfig() {
668
+ if (!fileExists(configPath)) {
669
+ throw new Error('❌ capacitor.config.json not found. Ensure this is a Capacitor project.');
670
+ }
671
+
672
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
673
+ const admobConfig = config.plugins?.AdMob;
674
+
675
+ if (!admobConfig) {
676
+ throw new Error('❌ AdMob configuration is missing in capacitor.config.json.');
677
+ }
678
+
679
+ // Default to true if ADMOB_ENABLED is not specified
680
+ const isEnabled = admobConfig.ADMOB_ENABLED !== false;
681
+
682
+ if (!isEnabled) {
683
+ return { ADMOB_ENABLED: false }; // Skip further validation
684
+ }
685
+
686
+ if (!admobConfig.APP_ID_ANDROID || !admobConfig.APP_ID_IOS) {
687
+ throw new Error(' ❌ AdMob configuration is incomplete. Ensure APP_ID_ANDROID and APP_ID_IOS are defined.');
688
+ }
689
+
690
+ return {
691
+ ADMOB_ENABLED: true,
692
+ APP_ID_ANDROID: admobConfig.APP_ID_ANDROID,
693
+ APP_ID_IOS: admobConfig.APP_ID_IOS,
694
+ USE_LITE_ADS: admobConfig.USE_LITE_ADS === "lite",
695
+ };
696
+ }
697
+
698
+ function validateAndroidBuildOptions() {
699
+
700
+
701
+ if (!fileExists(configPath)) {
702
+ console.log(' capacitor.config.json not found. Ensure this is a Capacitor project.');
703
+ process.exit(1);
704
+ }
705
+
706
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
707
+
708
+ const targetAppId=config.appId
709
+
710
+ const buildOptions = config.android?.buildOptions;
711
+
712
+ if (!buildOptions) {
713
+ console.log('❌ Missing android.buildOptions in capacitor.config.json.');
714
+ process.exit(1);
715
+ }
716
+
717
+ const requiredProps = [
718
+ 'keystorePath',
719
+ 'keystorePassword',
720
+ 'keystoreAlias',
721
+ 'keystoreAliasPassword',
722
+ 'releaseType',
723
+ 'signingType'
724
+ ];
725
+
726
+ const missing = requiredProps.filter(prop => !buildOptions[prop]);
727
+
728
+ if (missing.length > 0) {
729
+ console.log(' Missing properties android.buildOptions in capacitor.config.json.');
730
+ process.exit(1);
731
+ }
732
+
733
+
734
+ const keystorePath=buildOptions.keystorePath
735
+ const keyFileName = path.basename(keystorePath);
736
+
737
+
738
+
739
+ const keystoreMap = {
740
+ "gameskey.jks": [
741
+ "com.cube.blaster",
742
+ ],
743
+ "htmleditorkeystoke.jks": [
744
+ "com.HTML.AngularJS.Codeplay",
745
+ "com.html.codeplay.pro",
746
+ "com.bootstrap.code.play",
747
+ "com.kids.learning.master",
748
+ "com.Simple.Barcode.Scanner"
749
+ ]
750
+ };
751
+
752
+ // find which keystore is required for the given targetAppId
753
+ let requiredKey = "newappskey.jks"; // default
754
+ for (const [keyFile, appIds] of Object.entries(keystoreMap)) {
755
+ if (appIds.includes(targetAppId)) {
756
+ requiredKey = keyFile;
757
+ break;
758
+ }
759
+ }
760
+
761
+ // validate
762
+ if (keyFileName !== requiredKey) {
763
+ console.log(`❌ The keystore path is mismatched. Expected ${requiredKey} for ${targetAppId}, but got ${keyFileName}`);
764
+ process.exit(1);
765
+ }
766
+
767
+
768
+
769
+
770
+
771
+ // optionally return them
772
+ //return buildOptions;
773
+ }
774
+
775
+ function updatePluginXml(admobConfig) {
776
+ if (!fileExists(pluginPath)) {
777
+ console.error(' ❌ plugin.xml not found. Ensure the plugin is installed.');
778
+ return;
779
+ }
780
+
781
+ let pluginContent = fs.readFileSync(pluginPath, 'utf8');
782
+
783
+ pluginContent = pluginContent
784
+ .replace(/<preference name="APP_ID_ANDROID" default=".*?" \/>/, `<preference name="APP_ID_ANDROID" default="${admobConfig.APP_ID_ANDROID}" />`)
785
+ .replace(/<preference name="APP_ID_IOS" default=".*?" \/>/, `<preference name="APP_ID_IOS" default="${admobConfig.APP_ID_IOS}" />`);
786
+
787
+ fs.writeFileSync(pluginPath, pluginContent, 'utf8');
788
+ console.log('✅ AdMob IDs successfully updated in plugin.xml');
789
+ }
790
+
791
+ function updateInfoPlist(admobConfig) {
792
+ if (!fileExists(infoPlistPath)) {
793
+ console.error(' ❌ Info.plist not found. Ensure you have built the iOS project.');
794
+ return;
795
+ }
796
+
797
+ const plistContent = fs.readFileSync(infoPlistPath, 'utf8');
798
+ const plistData = plist.parse(plistContent);
799
+
800
+ plistData.GADApplicationIdentifier = admobConfig.APP_ID_IOS;
801
+ plistData.NSUserTrackingUsageDescription = 'This identifier will be used to deliver personalized ads to you.';
802
+ plistData.GADDelayAppMeasurementInit = true;
803
+
804
+ const updatedPlistContent = plist.build(plistData);
805
+ fs.writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
806
+ console.log('AdMob IDs and additional configurations successfully updated in Info.plist');
807
+ }
808
+
809
+ try {
810
+ if (!fileExists(configPath)) {
811
+ throw new Error(' ❌ capacitor.config.json not found. Skipping setup.');
812
+ }
813
+
814
+ if (!fileExists(androidPlatformPath) && !fileExists(iosPlatformPath)) {
815
+ throw new Error('Neither Android nor iOS platforms are found. Ensure platforms are added to your Capacitor project.');
816
+ }
817
+
818
+ checkAndCopyResources();
819
+
820
+
821
+
822
+ const admobConfig = getAdMobConfig();
823
+
824
+
825
+
826
+
827
+
828
+ // Proceed only if ADMOB_ENABLED is true
829
+ if (admobConfig.ADMOB_ENABLED) {
830
+ if (fileExists(androidPlatformPath)) {
831
+ updatePluginXml(admobConfig);
832
+ }
833
+
834
+ if (fileExists(iosPlatformPath)) {
835
+ updateInfoPlist(admobConfig);
836
+ }
837
+ }
838
+
839
+
840
+ } catch (error) {
841
+ console.error(error.message);
842
+ process.exit(1); // Stop execution if there's a critical error
843
+ }
844
+
845
+
846
+
847
+ validateAndroidBuildOptions();
848
+
849
+
850
+
851
+
852
+
853
+
854
+ // Check all the codeplays plugins version START
855
+
856
+
857
+ const readline = require('readline');
858
+
859
+
860
+ //const srcDir = path.join(__dirname, 'src');
861
+ const srcDir = path.join(process.cwd(), 'src');
862
+ let outdatedPlugins = [];
863
+
864
+ function parseVersion(ver) {
865
+ return ver.split('.').map(n => parseInt(n, 10));
866
+ }
867
+
868
+ function compareVersions(v1, v2) {
869
+ const [a1, b1] = parseVersion(v1);
870
+ const [a2, b2] = parseVersion(v2);
871
+ if (a1 !== a2) return a1 - a2;
872
+ return b1 - b2;
873
+ }
874
+
875
+ function walkSync(dir, filelist = []) {
876
+ fs.readdirSync(dir).forEach(file => {
877
+ const fullPath = path.join(dir, file);
878
+ const stat = fs.statSync(fullPath);
879
+ if (stat.isDirectory()) {
880
+ walkSync(fullPath, filelist);
881
+ } else {
882
+ filelist.push(fullPath);
883
+ }
884
+ });
885
+ return filelist;
886
+ }
887
+
888
+ function checkPlugins() {
889
+ const files = walkSync(srcDir);
890
+
891
+ for (const plugin of requiredPlugins) {
892
+ if (plugin.isFolder) {
893
+
894
+ let baseFolder = path.join(srcDir,'js', 'Ads'); // <- use known folder name
895
+
896
+ if (fs.existsSync(baseFolder)) {
897
+ const subDirs = fs.readdirSync(baseFolder)
898
+ .map(name => path.join(baseFolder, name))
899
+ .filter(p => fs.statSync(p).isDirectory());
900
+
901
+ for (const dir of subDirs) {
902
+ const relativePath = path.relative(srcDir, dir).replace(/\\/g, '/'); // e.g. Ads/IAP-2.0
903
+ const match = plugin.pattern.exec(relativePath);
904
+ if (match) {
905
+ const currentVersion = match[1];
906
+ if (compareVersions(currentVersion, plugin.minVersion) < 0) {
907
+ outdatedPlugins.push({
908
+ name: relativePath,
909
+ currentVersion,
910
+ requiredVersion: plugin.minVersion
911
+ });
912
+ }
913
+ }
914
+ }
915
+ }
916
+ continue;
917
+ }
918
+
919
+ const matchedFile = files.find(file => plugin.pattern.test(file));
920
+ if (matchedFile) {
921
+ const match = plugin.pattern.exec(matchedFile);
922
+ if (match) {
923
+ const currentVersion = match[1];
924
+ if (compareVersions(currentVersion, plugin.minVersion) < 0) {
925
+ outdatedPlugins.push({
926
+ name: path.relative(__dirname, matchedFile),
927
+ currentVersion,
928
+ requiredVersion: plugin.minVersion
929
+ });
930
+ }
931
+ }
932
+ }
933
+ }
934
+
935
+ if (outdatedPlugins.length > 0) {
936
+ console.log('\n❗ The following plugins are outdated:');
937
+ outdatedPlugins.forEach(p => {
938
+ console.log(` ❌ - ${p.name} (Current: ${p.currentVersion}, Required: ${p.requiredVersion})`);
939
+ });
940
+
941
+ const rl = readline.createInterface({
942
+ input: process.stdin,
943
+ output: process.stdout
944
+ });
945
+
946
+ rl.question('\nAre you sure you want to continue without updating these plugins? (y/n): ', answer => {
947
+ if (answer.toLowerCase() !== 'y') {
948
+ console.log('\n❌ Build cancelled due to outdated plugins.');
949
+ process.exit(1);
950
+ } else {
951
+ console.log('\n✅ Continuing build...');
952
+ rl.close();
953
+ }
954
+ });
955
+ } else {
956
+ console.log('✅ All plugin versions are up to date.');
957
+ }
958
+ }
959
+
960
+ // Run the validation
961
+ checkPlugins();
962
+
963
+
964
+
965
+
966
+ // Check all the codeplays plugins version START
967
+
968
+
969
+
970
+
971
+
972
+
973
+
974
+
975
+
976
+
977
+
978
+
979
+