codeplay-common 4.7.6 → 4.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,13 +10,37 @@ const { execSync } = require("child_process");
10
10
 
11
11
 
12
12
  const { readFileSync } = require("fs");
13
+ const readline = require('readline');
14
+
15
+ // Await import confirmation before continuing the remaining build steps.
16
+ (async () => {
17
+
18
+ // Standalone builds default to Android on every host OS.
19
+ // npm run build:ios explicitly selects iOS through CODEPLAY_BUILD_PLATFORM.
20
+ const buildPlatform = String(process.env.CODEPLAY_BUILD_PLATFORM || '').trim().toLowerCase() || 'android';
21
+
22
+ if (!['android', 'ios'].includes(buildPlatform)) {
23
+ console.error(`❌ Unsupported build target: ${buildPlatform}. Expected android or ios.`);
24
+ process.exit(1);
25
+ }
13
26
 
14
27
  const ENABLE_AUTO_UPDATE = true;
15
28
  const USE_LIVE_SERVER_VERSION = true;
16
29
 
30
+ // Reuse directory traversal; refresh after generators or plugin downloads alter src.
31
+ let sourceFileInventory = null;
32
+ function getSourceFiles() {
33
+ if (!sourceFileInventory) {
34
+ const sourceRoot = path.join(process.cwd(), 'src');
35
+ sourceFileInventory = fs.existsSync(sourceRoot) ? walkSync(sourceRoot) : [];
36
+ }
37
+ return sourceFileInventory;
38
+ }
39
+
17
40
  const configPath = path.join(process.cwd(), 'capacitor.config.json');
18
41
  const packageJsonPath = path.join(process.cwd(), 'package.json');
19
42
  const updateLogFile = path.join(process.cwd(), "", "plugin-update-log.txt");
43
+ const ONESIGNAL_PLUGIN_NAME = "onesignal-cordova-plugin";
20
44
  const ADMOB_NEXTGEN_PLUGIN_NAME = "admob-emi-nextgen";
21
45
  const ADMOB_NEXTGEN_FILE_PATTERN = new RegExp(`^${ADMOB_NEXTGEN_PLUGIN_NAME}-\\d+(?:\\.\\d+)*\\.js$`);
22
46
  const ADMOB_NEXTGEN_CONFIG = "admob-ad-configuration-nextgen.json";
@@ -25,7 +49,10 @@ const AGENT_INSTRUCTIONS_VERSION_KEY = "agents";
25
49
  const AGENT_INSTRUCTIONS_ZIP_PREFIX = "agents";
26
50
  const AGENT_INSTRUCTIONS_FILE = "AGENTS.md";
27
51
  const AGENT_INSTRUCTIONS_DIR = "AGENTS";
28
- const AGENT_INSTRUCTIONS_VERSION_FILE = ".codeplay-version";
52
+ const AGENT_INSTRUCTIONS_VERSION_FILE = ".codeplay-version";
53
+
54
+ // Remove the retired plugin before any validation, patching or package updates.
55
+ removeOneSignalPlugin();
29
56
 
30
57
  const parseVersionParts = (version) => String(version || "0")
31
58
  .trim()
@@ -77,7 +104,6 @@ const getLatestAdmobNextgenFileName = (adsDir) =>
77
104
  { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: true },
78
105
  { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js',mandatoryUpdate: false },
79
106
  { pattern: /localNotification_AppSettings-(\d+\.\d+)\.js$/, minVersion: '1.0', required: true, baseDir: 'js',mandatoryUpdate: false },
80
- { pattern: /onesignal-(\d+\.\d+)\.js$/, minVersion: '2.3', required: true, baseDir: 'js',mandatoryUpdate: false },
81
107
  { pattern: /saveToGalleryAndSaveAnyFile-(\d+\.\d+)(-ios)?\.js$/, minVersion: '3.1', required: true, baseDir: 'js',mandatoryUpdate: true },
82
108
  { pattern: /Ads[\/\\]admob-emi-(\d+\.\d+)\.js$/, minVersion: '3.7', required: true, baseDir: 'js',mandatoryUpdate: true },
83
109
 
@@ -200,7 +226,10 @@ function loadRequiredPlugins() {
200
226
 
201
227
  const json = JSON.parse(fs.readFileSync(versionsFile, "utf8"));
202
228
 
203
- return json.plugins.map(p => ({
229
+ // OneSignal is retired; do not download its legacy helpers again.
230
+ return json.plugins.filter(p =>
231
+ !/^onesignal(?:-|$)/i.test(String(p.name || '')) && !/onesignal/i.test(String(p.pattern || ''))
232
+ ).map(p => ({
204
233
  ...p,
205
234
  pattern: new RegExp(p.pattern)
206
235
  }));
@@ -760,38 +789,43 @@ else {
760
789
 
761
790
 
762
791
 
763
- // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists START
764
-
765
- const os = require('os');
766
-
767
- const saveToGalleryAndSaveFileCheck_iOS = () => {
768
-
769
- // List of paths to scan
770
- const SCAN_PATHS = [
771
- path.resolve(__dirname, '../src/certificate'),
772
- path.resolve(__dirname, '../src/pages'),
773
- path.resolve(__dirname, '../src/js'),
774
- path.resolve(__dirname, '../src/app.f7')
775
- ];
792
+ // Validate saveToGalleryAndSaveAnyFile imports for the build platform START
776
793
 
777
- // Directory to exclude
778
- const EXCLUDED_DIR = path.resolve(__dirname, '../src/js/Ads');
794
+ const saveToGalleryAndSaveFileCheck_iOS = async () => {
779
795
 
796
+ const SOURCE_ROOT = path.resolve(__dirname, '../src');
797
+ const EXCLUDED_DIR = path.join(SOURCE_ROOT, 'js', 'Ads');
780
798
  const ANDROID_MANIFEST_PATH = path.resolve(__dirname, '../android/app/src/main/AndroidManifest.xml');
781
799
 
782
800
 
783
- // Match iOS-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5-ios.js) not in comments
784
- const IOS_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*-ios\.js['"]/m;
801
+ // Skip comments and unrelated strings; match static/dynamic imports, re-exports and require.
802
+ const COMMENT_OR_STRING_REGEX = /\/\/[^\r\n]*|\/\*[\s\S]*?\*\/|(['"`])(?:\\[\s\S]|(?!\1)[^\\])*\1/g;
803
+ const IMPORT_REGEX = /(['"`])(?:\\[\s\S]|(?!\1)[^\\])*\1|\b(?:import\s+(?:[^;'"`]*?\s+from\s*)?|export\s+[^;'"`]*?\s+from\s*|(?:import|require)\s*\(\s*)(['"])([^'"\r\n]+)\2/g;
804
+ const SAVE_FILE_REGEX = /^(.*[\/\\])?(saveToGalleryAndSaveAnyFile-\d+(?:\.\d+)*)(-ios)?(\.js)?([?#].*)?$/;
805
+ const pendingChanges = [];
785
806
 
786
- // Match Android-specific imports (e.g., saveToGalleryAndSaveAnyFile-2.5.js) not in comments
787
- const ANDROID_FILE_REGEX = /^(?!\s*\/\/).*['"](?:.*\/)?saveToGalleryAndSaveAnyFile-\d+(\.\d+)*\.js['"]/m;
807
+ const ALLOWED_EXTENSIONS = ['.js', '.mjs', '.ts', '.f7'];
808
+ const referencedVersions = new Set();
809
+ let aliasImportFound = false;
810
+ let hasForbiddenEditorImport = false;
811
+ const FORBIDDEN_REGEX = /editor\/editor-\d+(\.\d+)+/;
812
+ const ERROR_MESSAGE = `const ERROR_MESSAGE = ❌ Invalid import detected!
788
813
 
814
+ You are using a direct version-based path like: editor/editor-x.x/editor.js
789
815
 
816
+ 🚫 This is NOT allowed.
790
817
 
818
+ 👉 Please use the proper alias or updated import method.
819
+ Example: import { ... } from '@editor'
820
+
821
+ ⚠️ Do not use version-based paths in imports.
822
+ 👉 Please add this manually in vite.config.js:
791
823
 
824
+ alias: {
825
+ '@editor': path.resolve(__dirname, './src/js/editor/editor-x.x')
826
+ }`
792
827
 
793
- const ALLOWED_EXTENSIONS = ['.js', '.f7'];
794
- const isMac = os.platform() === 'darwin';
828
+ const isIOSBuild = buildPlatform === 'ios';
795
829
 
796
830
  let iosImportFound = false;
797
831
  let androidImportFound = false;
@@ -809,13 +843,7 @@ const STATUSBAR_FILE = path.resolve(__dirname, '../src/js/common-configuration.j
809
843
  const STATUSBAR_REGEX = /^(?!\s*\/\/).*Capacitor\.Plugins\.StatusBar\.setStyle/m;
810
844
 
811
845
 
812
- const ONESIGNAL_OLD_FILES = [
813
- path.resolve(__dirname, '../src/js/onesignal-settings.js')
814
- ];
815
- const ONESIGNAL_VERSION_REGEX = /^onesignal-\d+(\.\d+)*\.js$/;
816
-
817
- function scanDirectory(dir) {
818
-
846
+ function scanSourceFile(dir) {
819
847
  /*
820
848
  //######################### DO NOT DELETE THIS - START [Appid base validation] #####################################
821
849
  const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
@@ -824,132 +852,187 @@ function scanDirectory(dir) {
824
852
  //######################### DO NOT DELETE THIS - END [Appid base validation] #####################################
825
853
  */
826
854
 
827
- const stat = fs.statSync(dir);
828
-
829
- if (stat.isFile()) {
830
-
831
-
832
-
833
855
  const baseName = path.basename(dir);
834
-
835
- const JS_ROOT = path.resolve(__dirname, '../src/js');
836
- const ONESIGNAL_DIR = path.join(JS_ROOT, 'onesignal');
837
-
838
- // 🚨 Block old OneSignal file locations (ONLY in src/js root)
839
- if (path.dirname(dir) === JS_ROOT) {
840
-
841
- // ❌ onesignal-settings.js
842
- if (ONESIGNAL_OLD_FILES.includes(dir)) {
843
- console.error(`\n❌ ERROR: onesignal-settings.js should not be in src/js/`);
844
- console.error(`📁 Move it to: src/js/onesignal/`);
845
- console.error(`📄 File: ${dir}\n`);
846
- process.exit(1);
847
- }
848
-
849
- // ❌ onesignal-x.x.js
850
- if (ONESIGNAL_VERSION_REGEX.test(baseName)) {
851
- console.error(`\n❌ ERROR: ${baseName} should not be in src/js/`);
852
- console.error(`📁 Move it to: src/js/onesignal/`);
853
- console.error(`📄 File: ${dir}\n`);
854
- process.exit(1);
855
- }
856
- }
857
-
858
- // 🚨 Special check ONLY for common-configuration.js
856
+ const isGalleryScope = ['certificate', 'pages', 'js'].some(folder =>
857
+ dir.startsWith(path.join(SOURCE_ROOT, folder) + path.sep)
858
+ ) || dir === path.join(SOURCE_ROOT, 'app.f7');
859
+ const shouldCheckGallery = isGalleryScope && !dir.startsWith(EXCLUDED_DIR + path.sep)
860
+ && !dir.split(path.sep).includes('node_modules') && !SKIP_FILES.includes(baseName)
861
+ && ALLOWED_EXTENSIONS.some(ext => dir.endsWith(ext));
862
+ const shouldCheckEditor = /\.(js|ts|f7)$/.test(dir);
863
+ const shouldCheckOneSignal = /\.(js|mjs|cjs|ts|tsx|jsx|f7)$/.test(dir);
864
+ if (!shouldCheckGallery && !shouldCheckEditor && !shouldCheckOneSignal) return;
865
+
866
+ const content = fs.readFileSync(dir, 'utf8');
867
+ if (shouldCheckEditor) {
868
+ content.split('\n').forEach((line, index) => {
869
+ if (!FORBIDDEN_REGEX.test(line)) return;
870
+ console.error(`❌ Forbidden import found:\nFile: ${dir}\nLine: ${index + 1}\nCode: ${line.trim()}\n`, ERROR_MESSAGE);
871
+ hasForbiddenEditorImport = true;
872
+ });
873
+ }
859
874
  if (dir === STATUSBAR_FILE) {
860
- let content = fs.readFileSync(dir, 'utf8');
861
-
862
- // 🧹 Remove ALL comments (single-line + multi-line)
863
- const contentWithoutComments = content
864
- .replace(/\/\/.*$/gm, '') // remove // comments
865
- .replace(/\/\*[\s\S]*?\*\//g, ''); // remove /* */ comments
866
-
867
- // ✅ Now check only real code
875
+ const contentWithoutComments = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
868
876
  if (contentWithoutComments.includes('Capacitor.Plugins.StatusBar.setStyle')) {
869
- console.error(`\n❌ ERROR: StatusBar.setStyle should NOT be used in common-configuration.js`);
870
- console.error(`🚫 iOS status bar is controlled via other methods. Remove this line.`);
871
- console.error(`📄 File: ${dir}\n`);
872
- process.exit(1);
877
+ throw new Error(`❌ StatusBar.setStyle should NOT be used in common-configuration.js. File: ${dir}`);
873
878
  }
874
879
  }
875
880
 
876
- // 🔥 Skip files in SKIP_FILES array
877
-
878
- if (SKIP_FILES.includes(baseName)) {
879
- // Just skip silently
880
- return;
881
+ const code = content.replace(COMMENT_OR_STRING_REGEX, (match, quote) =>
882
+ quote ? match : match.replace(/[^\r\n]/g, ' ')
883
+ );
884
+ const replacements = [];
885
+ IMPORT_REGEX.lastIndex = 0;
886
+ let imported;
887
+ while ((imported = IMPORT_REGEX.exec(code)) !== null) {
888
+ if (!imported[3]) continue;
889
+ const importPath = imported[3];
890
+ if (importPath === ONESIGNAL_PLUGIN_NAME || importPath.startsWith(ONESIGNAL_PLUGIN_NAME + '/')) {
891
+ throw new Error(`❌ OneSignal was removed, but ${dir} still imports ${importPath}. Remove that import and its initialization before building.`);
892
+ }
893
+ if (!shouldCheckGallery) continue;
894
+ if (/^@saveGallery[?#]/.test(importPath)) {
895
+ throw new Error(`❌ ${importPath} in ${dir} requires a dedicated Vite alias for its query/hash suffix.`);
896
+ }
897
+ if (importPath === '@saveGallery') {
898
+ aliasImportFound = true;
899
+ continue;
900
+ }
901
+ const fileMatch = importPath.match(SAVE_FILE_REGEX);
902
+ if (!fileMatch) continue;
903
+ if (fileMatch[5]) {
904
+ throw new Error(`❌ Cannot migrate ${importPath} in ${dir} to a plain @saveGallery alias: its query/hash suffix requires a dedicated Vite alias.`);
905
+ }
906
+ referencedVersions.add(fileMatch[2]);
907
+ const nextImportPath = '@saveGallery';
908
+ const offset = imported.index + imported[0].lastIndexOf(imported[2] + importPath + imported[2]) + 1;
909
+ replacements.push({ offset, importPath, nextImportPath });
881
910
  }
882
911
 
883
- // Only scan allowed file extensions
884
- if (ALLOWED_EXTENSIONS.some(ext => dir.endsWith(ext))) {
885
- process.stdout.write(`\r🔍 Scanning: ${dir} `);
912
+ if (replacements.length) {
913
+ let nextContent = content;
914
+ for (const replacement of [...replacements].reverse()) {
915
+ nextContent = nextContent.slice(0, replacement.offset) + replacement.nextImportPath
916
+ + nextContent.slice(replacement.offset + replacement.importPath.length);
917
+ }
918
+ pendingChanges.push({ filePath: dir, content, nextContent, replacements });
919
+ }
920
+ }
886
921
 
887
- const content = fs.readFileSync(dir, 'utf8');
922
+ // One source content pass handles gallery migration, retired OneSignal imports, StatusBar and editor validation.
923
+ for (const sourceFile of getSourceFiles()) scanSourceFile(sourceFile);
924
+ if (hasForbiddenEditorImport) throw new Error('🚫 Build failed due to forbidden editor imports.');
925
+ console.log('✅ No forbidden editor imports found.');
888
926
 
889
- if (IOS_FILE_REGEX.test(content)) {
890
- iosImportFound = true;
891
- if (!isMac) {
892
- console.error(`\n❌ ERROR: iOS-specific import detected in: ${dir}`);
893
- console.error(`🚫 STOPPED: This file should not be imported in Android/Windows/Linux builds.\n`);
894
- process.exit(1);
895
- }
896
- }
897
- else if (ANDROID_FILE_REGEX.test(content) && !content.includes('-ios.js')) {
898
- androidImportFound = true;
899
- }
927
+ if (referencedVersions.size || aliasImportFound) {
928
+ const viteConfigPath = getViteConfigPath();
929
+ if (!viteConfigPath) throw new Error('❌ Cannot configure @saveGallery: vite.config.js or vite.config.mjs is missing.');
930
+ const viteContent = fs.readFileSync(viteConfigPath, 'utf8');
931
+ const viteCode = viteContent.replace(COMMENT_OR_STRING_REGEX, (match, quote) =>
932
+ quote ? match : match.replace(/[^\r\n]/g, ' ')
933
+ );
934
+ const aliasRegex = /(['"])@saveGallery\1\s*:\s*path\.resolve\(\s*__dirname\s*,\s*(['"])([^'"]+)\2\s*\)/g;
935
+ const aliasMatches = [...viteCode.matchAll(aliasRegex)];
936
+ if (aliasMatches.length > 1) throw new Error('❌ Multiple active @saveGallery alias entries found. Keep one entry.');
937
+ const aliasMatch = aliasMatches[0];
938
+ if (!aliasMatch && /['"]@saveGallery['"]\s*:/.test(viteCode)) {
939
+ throw new Error('❌ Unsupported @saveGallery alias format. Use path.resolve(__dirname, a literal file path).');
940
+ }
941
+ if (aliasImportFound && aliasMatch) {
942
+ const existingName = path.basename(aliasMatch[3]).match(SAVE_FILE_REGEX);
943
+ if (!existingName) throw new Error('❌ @saveGallery must point to a versioned saveToGalleryAndSaveAnyFile helper.');
944
+ referencedVersions.add(existingName[2]);
945
+ }
946
+ if (!referencedVersions.size) {
947
+ const jsDir = path.join(SOURCE_ROOT, 'js');
948
+ const pattern = isIOSBuild ? /^saveToGalleryAndSaveAnyFile-\d+(?:\.\d+)*-ios\.js$/ : /^saveToGalleryAndSaveAnyFile-\d+(?:\.\d+)*\.js$/;
949
+ const latestFile = getLatestVersionedFileName(jsDir, pattern);
950
+ if (!latestFile) throw new Error(`❌ No ${buildPlatform} save-gallery helper found in src/js.`);
951
+ referencedVersions.add(latestFile.match(SAVE_FILE_REGEX)[2]);
952
+ }
953
+ if (referencedVersions.size !== 1) {
954
+ throw new Error(`❌ @saveGallery cannot represent multiple helper versions: ${[...referencedVersions].join(', ')}. Align them before building.`);
955
+ }
956
+ const baseName = [...referencedVersions][0];
957
+ const targetRelativePath = `./src/js/${baseName}${isIOSBuild ? '-ios' : ''}.js`;
958
+ const targetPath = path.resolve(path.dirname(viteConfigPath), targetRelativePath);
959
+ if (!fs.existsSync(targetPath)) throw new Error(`❌ Cannot configure @saveGallery: target file does not exist: ${targetPath}`);
960
+ const aliasEntry = `'@saveGallery': path.resolve(__dirname, '${targetRelativePath}')`;
961
+ let nextViteContent = viteContent;
962
+ if (aliasMatch) {
963
+ if (path.resolve(path.dirname(viteConfigPath), aliasMatch[3]) !== targetPath) {
964
+ nextViteContent = viteContent.slice(0, aliasMatch.index) + aliasEntry + viteContent.slice(aliasMatch.index + aliasMatch[0].length);
900
965
  }
901
- }
902
- else if (stat.isDirectory()) {
903
- if (dir === EXCLUDED_DIR || path.basename(dir) === 'node_modules') return;
966
+ } else {
967
+ const aliasHeader = /\balias\s*:\s*\{/g.exec(viteCode);
968
+ const resolveHeader = /\bresolve\s*:\s*\{/g.exec(viteCode);
969
+ const returnHeader = /\breturn\s*\{/g.exec(viteCode);
970
+ const newline = viteContent.includes('\r\n') ? '\r\n' : '\n';
971
+ const header = aliasHeader || resolveHeader || returnHeader;
972
+ if (!header || (!aliasHeader && /\balias\s*:/.test(viteCode))) {
973
+ throw new Error('❌ Cannot safely insert @saveGallery in this Vite config. Add a resolve.alias object first.');
974
+ }
975
+ const insert = aliasHeader ? `${newline} ${aliasEntry},`
976
+ : resolveHeader ? `${newline} alias: { ${aliasEntry} },`
977
+ : `${newline} resolve: { alias: { ${aliasEntry} } },`;
978
+ const offset = header.index + header[0].length;
979
+ nextViteContent = viteContent.slice(0, offset) + insert + viteContent.slice(offset);
980
+ }
981
+ nextViteContent = ensureImportOrRequire(nextViteContent, 'path', "import path from 'path';", "const path = require('path');");
982
+ if (nextViteContent !== viteContent) {
983
+ pendingChanges.unshift({ filePath: viteConfigPath, content: viteContent, nextContent: nextViteContent,
984
+ replacements: [{ importPath: aliasMatch ? aliasMatch[3] : '(missing @saveGallery alias)', nextImportPath: targetRelativePath }] });
985
+ }
986
+ if (isIOSBuild) iosImportFound = true;
987
+ else androidImportFound = true;
988
+ }
904
989
 
905
- const entries = fs.readdirSync(dir, { withFileTypes: true });
906
- for (let entry of entries) {
907
- scanDirectory(path.join(dir, entry.name));
990
+ if (pendingChanges.length) {
991
+ console.warn(`\n⚠️ WARNING: This build is considered ${isIOSBuild ? 'iOS' : 'Android'}.`);
992
+ console.warn('The following source imports and Vite alias targets will be changed:');
993
+ for (const change of pendingChanges) {
994
+ console.warn(`\n📄 ${change.filePath}`);
995
+ for (const replacement of change.replacements) {
996
+ console.warn(` ${replacement.importPath} → ${replacement.nextImportPath}`);
908
997
  }
909
998
  }
910
- }
911
999
 
1000
+ const confirmed = await new Promise(resolve => {
1001
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1002
+ rl.on('close', () => resolve(false));
1003
+ rl.on('SIGINT', () => { resolve(false); rl.close(); });
1004
+ rl.question('\nPress Enter or type y to apply these changes and continue; any other answer cancels: ', answer => {
1005
+ const normalizedAnswer = answer.trim().toLowerCase();
1006
+ resolve(normalizedAnswer === '' || normalizedAnswer === 'y');
1007
+ rl.close();
1008
+ });
1009
+ });
1010
+ if (!confirmed) throw new Error('❌ Build cancelled. No import filenames were changed.');
912
1011
 
913
- // Run scan on all specified paths
914
- for (let scanPath of SCAN_PATHS) {
915
- if (fs.existsSync(scanPath)) {
916
- scanDirectory(scanPath);
1012
+ // Check every source again before writing so edits made while awaiting confirmation are preserved.
1013
+ for (const change of pendingChanges) {
1014
+ if (fs.readFileSync(change.filePath, 'utf8') !== change.content) {
1015
+ throw new Error(`❌ Source changed while awaiting confirmation: ${change.filePath}. Build cancelled; run it again.`);
1016
+ }
1017
+ }
1018
+ for (const change of pendingChanges) {
1019
+ fs.writeFileSync(change.filePath, change.nextContent, 'utf8');
1020
+ console.log(`✅ Updated platform imports in: ${change.filePath}`);
917
1021
  }
918
1022
  }
919
1023
 
920
-
921
-
922
- /* // Check src folder
923
- if (!fs.existsSync(ROOT_DIR)) {
924
- console.warn(`⚠️ Warning: 'src' directory not found at: ${ROOT_DIR}`);
925
- return;
926
- } */
927
-
928
- //scanDirectory(ROOT_DIR);
929
-
930
- // iOS Checks
931
- /* if (isMac && !iosImportFound) {
932
- console.warn(`⚠️ WARNING: You're on macOS but no iOS version (saveToGalleryAndSaveAnyFile-x.x-ios.js) found.`);
933
- process.exit(1);
934
- } else if (isMac && iosImportFound) {
935
- console.log('✅ iOS version detected for macOS build.');
936
- } else if (!iosImportFound) {
937
- console.log('✅ No iOS-specific imports detected for non-macOS.');
938
- } */
939
-
940
- // ✅ Only care if iOS import is found in NON-mac systems
941
- if (iosImportFound && !isMac) {
942
- console.error(`\n❌ ERROR: iOS-specific file is imported in non-macOS environment.`);
943
- process.exit(1);
944
- } else if (iosImportFound && isMac) {
945
- console.log('✅ iOS import detected (valid for macOS build).');
1024
+ // The alias selects exactly one helper for the target platform.
1025
+ if (iosImportFound) {
1026
+ console.log('✅ iOS import detected (valid for iOS build).');
1027
+ } else if (androidImportFound) {
1028
+ console.log('✅ Android import detected (valid for Android build).');
946
1029
  } else {
947
- console.log('✅ No iOS-specific imports found (this is totally fine).');
1030
+ console.log('✅ No platform-specific saveToGalleryAndSaveAnyFile imports found.');
948
1031
  }
949
1032
 
950
1033
 
951
1034
  // Android Checks
952
- if (androidImportFound) {
1035
+ if (buildPlatform === 'android' && androidImportFound) {
953
1036
  console.log("📱 Android version of saveToGalleryAndSaveAnyFile detected. Checking AndroidManifest.xml...");
954
1037
 
955
1038
  if (!fs.existsSync(ANDROID_MANIFEST_PATH)) {
@@ -980,8 +1063,8 @@ if (iosImportFound && !isMac) {
980
1063
  }
981
1064
  };
982
1065
 
983
- saveToGalleryAndSaveFileCheck_iOS();
984
- // saveToGalleryAndSaveAnyFile-x.x-ios.js file check for android and return error if exists END
1066
+ await saveToGalleryAndSaveFileCheck_iOS();
1067
+ // Validate saveToGalleryAndSaveAnyFile imports for the build platform END
985
1068
 
986
1069
 
987
1070
 
@@ -1701,7 +1784,6 @@ validateAndroidBuildOptions();
1701
1784
  // Check all the codeplays plugins version START
1702
1785
 
1703
1786
 
1704
- const readline = require('readline');
1705
1787
 
1706
1788
 
1707
1789
  //const srcDir = path.join(__dirname, 'src');
@@ -1720,20 +1802,15 @@ function compareVersions(v1, v2) {
1720
1802
  }
1721
1803
 
1722
1804
  function walkSync(dir, filelist = []) {
1723
- fs.readdirSync(dir).forEach(file => {
1724
- const fullPath = path.join(dir, file);
1725
- const stat = fs.statSync(fullPath);
1726
- if (stat.isDirectory()) {
1727
- walkSync(fullPath, filelist);
1728
- } else {
1729
- filelist.push(fullPath);
1730
- }
1731
- });
1805
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
1806
+ const fullPath = path.join(dir, entry.name);
1807
+ const isDirectory = entry.isDirectory() || (entry.isSymbolicLink() && fs.statSync(fullPath).isDirectory());
1808
+ if (isDirectory) walkSync(fullPath, filelist);
1809
+ else filelist.push(fullPath);
1810
+ }
1732
1811
  return filelist;
1733
1812
  }
1734
1813
 
1735
-
1736
-
1737
1814
  function getSearchRoot(plugin) {
1738
1815
  return path.join(srcDir, plugin.baseDir || 'js');
1739
1816
  }
@@ -1788,6 +1865,7 @@ function urlExists(url) {
1788
1865
  * Download file from server
1789
1866
  */
1790
1867
  function downloadFile(url, dest) {
1868
+ if (path.resolve(dest).startsWith(path.join(process.cwd(), 'src') + path.sep)) sourceFileInventory = null;
1791
1869
  return new Promise((resolve, reject) => {
1792
1870
  const file = fs.createWriteStream(dest);
1793
1871
 
@@ -1839,6 +1917,8 @@ const VITE_ALIAS_ONLY = [
1839
1917
  function updateImports(oldName, newName) {
1840
1918
 
1841
1919
  const projectRoot = process.cwd();
1920
+ const oldSaveHelper = oldName.match(/^(saveToGalleryAndSaveAnyFile-\d+(?:\.\d+)*)(?:-ios)?\.js$/);
1921
+ const newSaveHelper = newName.match(/^(saveToGalleryAndSaveAnyFile-\d+(?:\.\d+)*)(?:-ios)?\.js$/);
1842
1922
 
1843
1923
  const filesToScan = [
1844
1924
  path.join(projectRoot, "vite.config.js"),
@@ -1854,9 +1934,23 @@ function updateImports(oldName, newName) {
1854
1934
 
1855
1935
  let content = fs.readFileSync(file, "utf8");
1856
1936
 
1857
- if (content.includes(oldName)) {
1937
+ if (content.includes(oldName) || (oldSaveHelper && newSaveHelper && content.includes('@saveGallery'))) {
1858
1938
 
1859
1939
  content = content.split(oldName).join(newName);
1940
+ // Plugin updates can replace an iOS filename with the generic new filename.
1941
+ // Keep the centralized save-gallery alias aligned with the selected build.
1942
+ content = content.replace(/(['"]@saveGallery['"]\s*:\s*path\.resolve\(\s*__dirname\s*,\s*['"])([^'"]+)(['"]\s*\))/g,
1943
+ (match, prefix, target, suffix) => {
1944
+ if (!/saveToGalleryAndSaveAnyFile-\d+(?:\.\d+)*(?:-ios)?\.js$/.test(target)) return match;
1945
+ if (oldSaveHelper && newSaveHelper && target.includes(oldSaveHelper[1])) {
1946
+ target = target.replace(oldSaveHelper[1], newSaveHelper[1]);
1947
+ }
1948
+ const nextTarget = target.replace(/(?:-ios)?\.js$/, buildPlatform === 'ios' ? '-ios.js' : '.js');
1949
+ if (!fs.existsSync(path.resolve(path.dirname(file), nextTarget))) {
1950
+ throw new Error(`❌ Updated @saveGallery target is missing: ${nextTarget}`);
1951
+ }
1952
+ return prefix + nextTarget + suffix;
1953
+ });
1860
1954
 
1861
1955
  fs.writeFileSync(file, content);
1862
1956
 
@@ -1865,47 +1959,16 @@ function updateImports(oldName, newName) {
1865
1959
 
1866
1960
  });
1867
1961
 
1868
- // scan src files
1869
- function walk(dir) {
1870
-
1871
- fs.readdirSync(dir).forEach(file => {
1872
-
1873
- if (["node_modules","android","ios","dist",".git"].includes(file))
1874
- return;
1875
-
1876
- const full = path.join(dir, file);
1877
- const stat = fs.statSync(full);
1878
-
1879
- if (stat.isDirectory()) {
1880
- walk(full);
1881
- }
1882
-
1883
- else if (
1884
- (full.endsWith(".js") ||
1885
- full.endsWith(".f7") ||
1886
- full.endsWith(".mjs")) &&
1887
- !full.endsWith(".min.js")
1888
- ) {
1889
-
1890
- let content = fs.readFileSync(full, "utf8");
1891
-
1892
- if (content.includes(oldName)) {
1893
-
1894
- content = content.split(oldName).join(newName);
1895
-
1896
- fs.writeFileSync(full, content);
1897
-
1898
- console.log(`✏️ Updated import in ${path.relative(projectRoot, full)}`);
1899
- }
1900
-
1901
- }
1902
-
1903
- });
1904
-
1905
- }
1906
-
1907
- if (fs.existsSync(srcDir)) {
1908
- walk(srcDir);
1962
+ // Downloads invalidate the inventory; content-only edits reuse it.
1963
+ for (const full of getSourceFiles()) {
1964
+ const relativePath = path.relative(srcDir, full);
1965
+ if (relativePath.split(path.sep).some(part => ['node_modules', 'android', 'ios', 'dist', '.git'].includes(part))) continue;
1966
+ if (!/\.(js|f7|mjs)$/.test(full) || full.endsWith('.min.js')) continue;
1967
+ let content = fs.readFileSync(full, 'utf8');
1968
+ if (!content.includes(oldName)) continue;
1969
+ content = content.split(oldName).join(newName);
1970
+ fs.writeFileSync(full, content);
1971
+ console.log(`✏️ Updated import in ${path.relative(projectRoot, full)}`);
1909
1972
  }
1910
1973
 
1911
1974
  }
@@ -2299,7 +2362,7 @@ async function loadPluginVersions() {
2299
2362
  let hasMandatoryUpdate = false;
2300
2363
  function checkPlugins() {
2301
2364
  return new Promise(async (resolve, reject) => {
2302
- const files = walkSync(srcDir);
2365
+ const files = getSourceFiles();
2303
2366
  const outdatedPlugins = [];
2304
2367
  let hasMandatoryUpdate = false;
2305
2368
 
@@ -2635,6 +2698,7 @@ Expected:
2635
2698
  console.log(`🚀 Executing ${runJsPath}...`);
2636
2699
 
2637
2700
  execSync(`node "${runJsPath}"`, { stdio: "inherit" });
2701
+ sourceFileInventory = null;
2638
2702
  }
2639
2703
 
2640
2704
  } else {
@@ -2659,96 +2723,6 @@ Expected:
2659
2723
 
2660
2724
 
2661
2725
 
2662
- //editor-x.x import old style check and stop execution START
2663
-
2664
-
2665
-
2666
- // Match: editor/editor-2.3, editor/editor-2.3.1, etc.
2667
- const FORBIDDEN_REGEX = /editor\/editor-\d+(\.\d+)+/;
2668
-
2669
- let hasError = false;
2670
-
2671
- const ERROR_MESSAGE = `const ERROR_MESSAGE = ❌ Invalid import detected!
2672
-
2673
- You are using a direct version-based path like: editor/editor-x.x/editor.js
2674
-
2675
- 🚫 This is NOT allowed.
2676
-
2677
- 👉 Please use the proper alias or updated import method.
2678
- Example: import { ... } from '@editor'
2679
-
2680
- ⚠️ Do not use version-based paths in imports.
2681
- 👉 Please add this manually in vite.config.js:
2682
-
2683
- alias: {
2684
- '@editor': path.resolve(__dirname, './src/js/editor/editor-x.x')
2685
- }`
2686
-
2687
- function scanDir(dir) {
2688
- const files = fs.readdirSync(dir);
2689
-
2690
- for (const file of files) {
2691
- const fullPath = path.join(dir, file);
2692
- const stat = fs.statSync(fullPath);
2693
-
2694
- if (stat.isDirectory()) {
2695
- scanDir(fullPath);
2696
- } else if (file.endsWith(".js") || file.endsWith(".ts") || file.endsWith(".f7")) {
2697
- const content = fs.readFileSync(fullPath, "utf-8");
2698
-
2699
- const lines = content.split("\n");
2700
-
2701
- lines.forEach((line, index) => {
2702
- if (FORBIDDEN_REGEX.test(line)) {
2703
- console.error(
2704
- `❌ Forbidden import found:\nFile: ${fullPath}\nLine: ${index + 1}\nCode: ${line.trim()}\n`,
2705
- ERROR_MESSAGE
2706
- );
2707
- hasError = true;
2708
- }
2709
- });
2710
- }
2711
- }
2712
- }
2713
-
2714
- // Run scan
2715
- scanDir(ROOT_DIR);
2716
-
2717
- // Throw error (exit process)
2718
- if (hasError) {
2719
- console.error("🚫 Build failed due to forbidden editor imports.");
2720
- process.exit(1);
2721
- } else {
2722
- console.log("✅ No forbidden imports found.");
2723
- }
2724
-
2725
-
2726
-
2727
-
2728
-
2729
-
2730
- //editor-x.x import old style check and stop execution START
2731
-
2732
-
2733
-
2734
-
2735
-
2736
-
2737
-
2738
-
2739
-
2740
-
2741
- // Check all the codeplays plugins version START
2742
-
2743
-
2744
-
2745
-
2746
- // ====================================================================
2747
- // AUTO-ADD esbuild.drop: ['console','debugger'] to vite.config.js / mjs
2748
- // ====================================================================
2749
-
2750
-
2751
-
2752
2726
  const checkAndupdateDropInViteConfig = () => {
2753
2727
 
2754
2728
  const possibleFiles = [
@@ -2869,7 +2843,7 @@ const syncAdmobAliasInViteConfig = () => {
2869
2843
  };
2870
2844
 
2871
2845
 
2872
- const getViteConfigPath = () => {
2846
+ function getViteConfigPath() {
2873
2847
  const possibleFiles = [
2874
2848
  "vite.config.mjs",
2875
2849
  "vite.config.js"
@@ -2919,7 +2893,7 @@ const getAnalyticsConfig = () => {
2919
2893
  };
2920
2894
  };
2921
2895
 
2922
- const isPackageAvailable = (packageName) => {
2896
+ function isPackageAvailable(packageName) {
2923
2897
  if (!fs.existsSync(packageJsonPath)) return false;
2924
2898
 
2925
2899
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
@@ -2927,49 +2901,62 @@ const isPackageAvailable = (packageName) => {
2927
2901
  return Boolean(
2928
2902
  packageJson.dependencies?.[packageName] ||
2929
2903
  packageJson.devDependencies?.[packageName] ||
2904
+ packageJson.optionalDependencies?.[packageName] ||
2905
+ packageJson.peerDependencies?.[packageName] ||
2930
2906
  fs.existsSync(path.join(process.cwd(), "node_modules", packageName, "package.json"))
2931
2907
  );
2932
2908
  };
2933
2909
 
2934
2910
  const sourceImportsPackage = (packageName, ignoredFilePath) => {
2935
- const sourceDir = path.join(process.cwd(), "src");
2936
- if (!fs.existsSync(sourceDir)) return false;
2937
-
2938
2911
  const escapedPackage = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2939
2912
  const importRegex = new RegExp(`(?:from\\s*['"]${escapedPackage}(?:/[^'"]*)?['"]|import\\s*['"]${escapedPackage}(?:/[^'"]*)?['"]|import\\s*\\(\\s*['"]${escapedPackage}(?:/[^'"]*)?['"]\\s*\\)|require\\s*\\(\\s*['"]${escapedPackage}(?:/[^'"]*)?['"]\\s*\\))`);
2940
2913
 
2941
- const scan = (dir) => {
2942
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
2943
- const fullPath = path.join(dir, entry.name);
2944
-
2945
- if (ignoredFilePath && path.resolve(fullPath) === path.resolve(ignoredFilePath)) {
2946
- continue;
2947
- }
2948
-
2949
- if (entry.isDirectory()) {
2950
- if (scan(fullPath)) return true;
2951
- continue;
2952
- }
2953
-
2954
- if (!/\.(js|mjs|cjs|ts|tsx|jsx|f7)$/.test(entry.name)) continue;
2955
-
2956
- const content = fs.readFileSync(fullPath, "utf8");
2957
- if (importRegex.test(content)) return true;
2958
- }
2959
-
2960
- return false;
2961
- };
2962
-
2963
- return scan(sourceDir);
2914
+ return getSourceFiles().some(fullPath => {
2915
+ if (ignoredFilePath && path.resolve(fullPath) === path.resolve(ignoredFilePath)) return false;
2916
+ if (!/\.(js|mjs|cjs|ts|tsx|jsx|f7)$/.test(fullPath)) return false;
2917
+ return importRegex.test(fs.readFileSync(fullPath, 'utf8'));
2918
+ });
2964
2919
  };
2965
2920
 
2966
- const runNpmCommand = (command) => {
2921
+ function runNpmCommand(command) {
2967
2922
  console.log(`🚀 ${command}`);
2968
2923
  execSync(command, { stdio: "inherit" });
2969
2924
  };
2970
2925
 
2926
+ function removeOneSignalPlugin() {
2927
+ if (!isPackageAvailable(ONESIGNAL_PLUGIN_NAME)) {
2928
+ console.log('ℹ️ OneSignal is not installed. Skipping removal.');
2929
+ return;
2930
+ }
2931
+
2932
+ console.warn(`⚠️ WARNING: ${ONESIGNAL_PLUGIN_NAME} was found. OneSignal is retired and will be uninstalled automatically.`);
2933
+ runNpmCommand(`npm uninstall ${ONESIGNAL_PLUGIN_NAME}`);
2934
+ if (isPackageAvailable(ONESIGNAL_PLUGIN_NAME)) {
2935
+ throw new Error(`❌ ${ONESIGNAL_PLUGIN_NAME} is still declared or installed after npm uninstall. Build stopped; check dependent packages.`);
2936
+ }
2937
+ sourceFileInventory = null;
2938
+ console.log(`✅ Uninstalled ${ONESIGNAL_PLUGIN_NAME}.`);
2939
+ }
2940
+
2971
2941
  const CODEPLAY_SYSTEM_BARS_PLUGIN = "capacitor-codeplay-system-bars";
2972
2942
 
2943
+ function ensureMandatoryCapacitorPlugins() {
2944
+ const requiredPackages = [
2945
+ "@capacitor/device",
2946
+ "@capacitor-community/in-app-review"
2947
+ ];
2948
+
2949
+ for (const packageName of requiredPackages) {
2950
+ const installedPackagePath = path.join(process.cwd(), "node_modules", packageName, "package.json");
2951
+ if (isPackageAvailable(packageName) && fs.existsSync(installedPackagePath)) {
2952
+ console.log(`ℹ️ ${packageName} is already installed.`);
2953
+ continue;
2954
+ }
2955
+
2956
+ runNpmCommand(`npm install ${packageName}`);
2957
+ }
2958
+ }
2959
+
2973
2960
  const syncCodeplaySystemBarsPackage = () => {
2974
2961
  if (!fs.existsSync(packageJsonPath)) {
2975
2962
  console.warn(`⚠️ package.json not found. Skipping ${CODEPLAY_SYSTEM_BARS_PLUGIN} package sync.`);
@@ -3180,27 +3167,17 @@ const syncAdmobConfigImports = () => {
3180
3167
  const adsDir = path.join(process.cwd(), "src", "js", "Ads");
3181
3168
  if (!fs.existsSync(adsDir)) return;
3182
3169
 
3183
- const scan = (dir) => {
3184
- fs.readdirSync(dir, { withFileTypes: true }).forEach(entry => {
3185
- const fullPath = path.join(dir, entry.name);
3186
-
3187
- if (entry.isDirectory()) {
3188
- scan(fullPath);
3189
- return;
3190
- }
3191
-
3192
- if (!/\.(js|mjs|ts|f7)$/.test(entry.name) || entry.name.endsWith(".min.js")) return;
3193
-
3194
- let content = fs.readFileSync(fullPath, "utf8");
3195
- if (!content.includes(ADMOB_OLD_CONFIG)) return;
3196
-
3197
- content = content.split(ADMOB_OLD_CONFIG).join(ADMOB_NEXTGEN_CONFIG);
3198
- fs.writeFileSync(fullPath, content, "utf8");
3199
- console.log(`✏️ Updated AdMob config import in ${path.relative(process.cwd(), fullPath)}`);
3200
- });
3201
- };
3170
+ // Downloads invalidate the inventory; content-only migrations reuse it.
3171
+ for (const fullPath of getSourceFiles()) {
3172
+ if (!fullPath.startsWith(adsDir + path.sep)) continue;
3173
+ if (!/\.(js|mjs|ts|f7)$/.test(fullPath) || fullPath.endsWith('.min.js')) continue;
3174
+ let content = fs.readFileSync(fullPath, 'utf8');
3175
+ if (!content.includes(ADMOB_OLD_CONFIG)) continue;
3176
+ content = content.split(ADMOB_OLD_CONFIG).join(ADMOB_NEXTGEN_CONFIG);
3177
+ fs.writeFileSync(fullPath, content, 'utf8');
3178
+ console.log(`✏️ Updated AdMob config import in ${path.relative(process.cwd(), fullPath)}`);
3179
+ }
3202
3180
 
3203
- scan(adsDir);
3204
3181
  };
3205
3182
 
3206
3183
  const ensureAdmobNextgenFiles = async () => {
@@ -3273,6 +3250,7 @@ const ensureAdmobNextgenFiles = async () => {
3273
3250
  mergedConfig.iosAppId = iosAppId;
3274
3251
  }
3275
3252
 
3253
+ if (!fs.existsSync(nextgenConfigPath)) sourceFileInventory = null;
3276
3254
  fs.writeFileSync(nextgenConfigPath, JSON.stringify(mergedConfig, null, 2) + "\n", "utf8");
3277
3255
  syncAdmobConfigImports();
3278
3256
  console.log("✅ AdMob nextgen files and configuration are ready.");
@@ -3448,7 +3426,9 @@ export const trackConsumablePurchased = trackEvent;
3448
3426
  `;
3449
3427
 
3450
3428
  const nextContent = analyticsEnabled ? activeWrapper : noopWrapper;
3451
- const currentContent = fs.existsSync(analyticsFile) ? fs.readFileSync(analyticsFile, "utf8") : "";
3429
+ const analyticsFileExists = fs.existsSync(analyticsFile);
3430
+ const currentContent = analyticsFileExists ? fs.readFileSync(analyticsFile, "utf8") : "";
3431
+ if (!analyticsFileExists) sourceFileInventory = null;
3452
3432
 
3453
3433
  if (currentContent !== nextContent) {
3454
3434
  fs.writeFileSync(analyticsFile, nextContent, "utf8");
@@ -3459,6 +3439,7 @@ export const trackConsumablePurchased = trackEvent;
3459
3439
 
3460
3440
  if (fs.existsSync(legacyAnalyticsFile)) {
3461
3441
  fs.unlinkSync(legacyAnalyticsFile);
3442
+ sourceFileInventory = null;
3462
3443
  console.log("✅ Removed old Firebase analytics wrapper: src/js/analytics/firebase-analytics.js");
3463
3444
  }
3464
3445
 
@@ -3540,7 +3521,7 @@ const syncFirebaseAnalyticsPodfile = () => {
3540
3521
  }
3541
3522
  };
3542
3523
 
3543
- const ensureImportOrRequire = (content, moduleName, esmLine, cjsLine) => {
3524
+ function ensureImportOrRequire(content, moduleName, esmLine, cjsLine) {
3544
3525
  const escaped = moduleName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3545
3526
  if (new RegExp(`from\\s*['"]${escaped}['"]|require\\s*\\(\\s*['"]${escaped}['"]\\s*\\)`).test(content)) {
3546
3527
  return content;
@@ -4033,11 +4014,12 @@ if (compareVersion(admobConfigInJson.VERSION, admobConfigMinVersion) < 0) {
4033
4014
 
4034
4015
 
4035
4016
 
4036
- function ensureGitignoreEntry(entry) {
4017
+ function ensureGitignoreEntry(entry, shouldIgnore = true) {
4037
4018
  const gitignorePath = path.join(process.cwd(), '.gitignore');
4038
4019
 
4039
4020
  // If .gitignore doesn't exist, create it
4040
4021
  if (!fs.existsSync(gitignorePath)) {
4022
+ if (!shouldIgnore) return;
4041
4023
  fs.writeFileSync(gitignorePath, `${entry}\n`, 'utf8');
4042
4024
  console.log(`✅ .gitignore created and added: ${entry}`);
4043
4025
  return;
@@ -4045,6 +4027,18 @@ function ensureGitignoreEntry(entry) {
4045
4027
 
4046
4028
  const content = fs.readFileSync(gitignorePath, 'utf8');
4047
4029
 
4030
+ if (!shouldIgnore) {
4031
+ const normalizedEntry = entry.replace(/^\/|\/$/g, '');
4032
+ const nextContent = content.split(/(?<=\n)/).filter(line =>
4033
+ line.trim().replace(/^\/|\/$/g, '') !== normalizedEntry
4034
+ ).join('');
4035
+ if (nextContent !== content) {
4036
+ fs.writeFileSync(gitignorePath, nextContent, 'utf8');
4037
+ console.log(`✅ Removed from .gitignore: ${entry}`);
4038
+ }
4039
+ return;
4040
+ }
4041
+
4048
4042
  // Normalize lines (trim + remove trailing slashes for comparison)
4049
4043
  const lines = content
4050
4044
  .split(/\r?\n/)
@@ -4069,7 +4063,8 @@ function ensureGitignoreEntry(entry) {
4069
4063
  }
4070
4064
 
4071
4065
 
4072
- ensureGitignoreEntry('buildCodeplay/');
4066
+ ensureGitignoreEntry('buildCodeplay/', false);
4067
+ ensureGitignoreEntry('agent-temp/');
4073
4068
 
4074
4069
 
4075
4070
  // ======================================================
@@ -4249,7 +4244,7 @@ function patchFile() {
4249
4244
  async function main() {
4250
4245
  validateThemeFolderLocation();
4251
4246
  validateAndRestoreSignDetails();
4252
- execSync('node buildCodeplay/fix-onesignal-plugin.js', { stdio: 'inherit' });
4247
+ ensureMandatoryCapacitorPlugins();
4253
4248
 
4254
4249
  await loadPluginVersions(); // 🔥 NEW
4255
4250
  await syncAgentInstructions();
@@ -4265,7 +4260,8 @@ async function main() {
4265
4260
  checkAdmobConfigurationProperty();
4266
4261
  }
4267
4262
 
4268
- main().catch(err => {
4263
+ await main();
4264
+ })().catch(err => {
4269
4265
  console.error(err?.message || err);
4270
4266
  process.exit(1);
4271
4267
  });