UNPKG

firebase-tools

Version:
249 lines (248 loc) 9.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.detectFiles = exports.extractAppIdentifiersAndroid = exports.extractAppIdentifierIos = exports.extractAppIdentifiersFlutter = exports.getAllDepsFromPackageJson = exports.detectApps = exports.getPlatformsFromFolder = exports.appDescription = exports.Framework = exports.Platform = void 0; const fs = require("fs-extra"); const path = require("path"); const glob_1 = require("glob"); var Platform; (function (Platform) { Platform["ANDROID"] = "ANDROID"; Platform["WEB"] = "WEB"; Platform["IOS"] = "IOS"; Platform["FLUTTER"] = "FLUTTER"; Platform["ADMIN_NODE"] = "ADMIN_NODE"; })(Platform = exports.Platform || (exports.Platform = {})); var Framework; (function (Framework) { Framework["REACT"] = "react"; Framework["ANGULAR"] = "angular"; })(Framework = exports.Framework || (exports.Framework = {})); function appDescription(a) { return `${a.directory} (${a.platform.toLowerCase()})`; } exports.appDescription = appDescription; async function getPlatformsFromFolder(dirPath) { const apps = await detectApps(dirPath); return [...new Set(apps.map((app) => app.platform))]; } exports.getPlatformsFromFolder = getPlatformsFromFolder; async function detectApps(dirPath) { const packageJsonFiles = await detectFiles(dirPath, "package.json"); const pubSpecYamlFiles = await detectFiles(dirPath, "pubspec.yaml"); const srcMainFolders = await detectFiles(dirPath, "src/main/"); const xCodeProjects = await detectFiles(dirPath, "*.xcodeproj/"); const adminAndWebApps = (await Promise.all(packageJsonFiles.map((p) => packageJsonToAdminOrWebApp(dirPath, p)))).flat(); const flutterAppPromises = await Promise.all(pubSpecYamlFiles.map((f) => processFlutterDir(dirPath, f))); const flutterApps = flutterAppPromises.flat(); const androidAppPromises = await Promise.all(srcMainFolders.map((f) => processAndroidDir(dirPath, f))); const androidApps = androidAppPromises .flat() .filter((a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory))); const iosAppPromises = await Promise.all(xCodeProjects.map((f) => processIosDir(dirPath, f))); const iosApps = iosAppPromises .flat() .filter((a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory))); return [...flutterApps, ...androidApps, ...iosApps, ...adminAndWebApps]; } exports.detectApps = detectApps; async function processIosDir(dirPath, filePath) { const iosDir = path.dirname(filePath); const iosAppIds = await detectAppIdsForPlatform(dirPath, Platform.IOS); if (iosAppIds.length === 0) { return [ { platform: Platform.IOS, directory: iosDir, }, ]; } const iosApps = await Promise.all(iosAppIds.map((app) => ({ platform: Platform.IOS, directory: iosDir, appId: app.appId, bundleId: app.bundleId, }))); return iosApps.flat(); } async function processAndroidDir(dirPath, filePath) { const androidDir = path.dirname(path.dirname(filePath)); const androidAppIds = await detectAppIdsForPlatform(dirPath, Platform.ANDROID); if (androidAppIds.length === 0) { return [ { platform: Platform.ANDROID, directory: androidDir, }, ]; } const androidApps = await Promise.all(androidAppIds.map((app) => ({ platform: Platform.ANDROID, directory: androidDir, appId: app.appId, bundleId: app.bundleId, }))); return androidApps.flat(); } async function processFlutterDir(dirPath, filePath) { const flutterDir = path.dirname(filePath); const flutterAppIds = await detectAppIdsForPlatform(dirPath, Platform.FLUTTER); if (flutterAppIds.length === 0) { return [ { platform: Platform.FLUTTER, directory: flutterDir, }, ]; } const flutterApps = await Promise.all(flutterAppIds.map((app) => { const flutterApp = { platform: Platform.FLUTTER, directory: flutterDir, appId: app.appId, bundleId: app.bundleId, }; return flutterApp; })); return flutterApps.flat(); } function isPathInside(parent, child) { const relativePath = path.relative(parent, child); return !relativePath.startsWith(`..`); } function getAllDepsFromPackageJson(packageJson) { var _a, _b; const devDependencies = Object.keys((_a = packageJson.devDependencies) !== null && _a !== void 0 ? _a : {}); const dependencies = Object.keys((_b = packageJson.dependencies) !== null && _b !== void 0 ? _b : {}); const allDeps = Array.from(new Set([...devDependencies, ...dependencies])); return allDeps; } exports.getAllDepsFromPackageJson = getAllDepsFromPackageJson; async function packageJsonToAdminOrWebApp(dirPath, packageJsonFile) { const fullPath = path.join(dirPath, packageJsonFile); const packageJson = JSON.parse((await fs.readFile(fullPath)).toString()); const allDeps = getAllDepsFromPackageJson(packageJson); const detectedApps = []; if (allDeps.includes("firebase-admin") || allDeps.includes("firebase-functions")) { detectedApps.push({ platform: Platform.ADMIN_NODE, directory: path.dirname(packageJsonFile), }); } if (allDeps.includes("firebase") || detectedApps.length === 0) { detectedApps.push({ platform: Platform.WEB, directory: path.dirname(packageJsonFile), frameworks: getFrameworksFromPackageJson(packageJson), }); } return detectedApps; } const WEB_FRAMEWORKS = Object.values(Framework); const WEB_FRAMEWORKS_SIGNALS = { react: ["react", "next"], angular: ["@angular/core"], }; async function detectAppIdsForPlatform(dirPath, platform) { let appIdFiles; let extractFunc; switch (platform) { case Platform.ANDROID: appIdFiles = await detectFiles(dirPath, "google-services*.json*"); extractFunc = extractAppIdentifiersAndroid; break; case Platform.IOS: appIdFiles = await detectFiles(dirPath, "GoogleService-*.plist*"); extractFunc = extractAppIdentifierIos; break; case Platform.FLUTTER: appIdFiles = await detectFiles(dirPath, "firebase_options.dart"); extractFunc = extractAppIdentifiersFlutter; break; default: return []; } const allAppIds = await Promise.all(appIdFiles.map(async (file) => { const fileContent = (await fs.readFile(path.join(dirPath, file))).toString(); return extractFunc(fileContent); })); return allAppIds.flat(); } function getFrameworksFromPackageJson(packageJson) { const allDeps = getAllDepsFromPackageJson(packageJson); return WEB_FRAMEWORKS.filter((framework) => WEB_FRAMEWORKS_SIGNALS[framework].find((dep) => allDeps.includes(dep))); } function extractAppIdentifiersFlutter(fileContent) { const optionsRegex = /FirebaseOptions\(([^)]*)\)/g; const appIdRegex = /appId: '([^']*)'/; const bundleIdRegex = /iosBundleId: '([^']*)'/; const matches = fileContent.matchAll(optionsRegex); const identifiers = []; for (const match of matches) { const optionsContent = match[1]; const appIdMatch = appIdRegex.exec(optionsContent); const bundleIdMatch = bundleIdRegex.exec(optionsContent); if (appIdMatch === null || appIdMatch === void 0 ? void 0 : appIdMatch[1]) { identifiers.push({ appId: appIdMatch[1], bundleId: bundleIdMatch === null || bundleIdMatch === void 0 ? void 0 : bundleIdMatch[1], }); } } return identifiers; } exports.extractAppIdentifiersFlutter = extractAppIdentifiersFlutter; function extractAppIdentifierIos(fileContent) { const appIdRegex = /<key>GOOGLE_APP_ID<\/key>\s*<string>([^<]*)<\/string>/; const bundleIdRegex = /<key>BUNDLE_ID<\/key>\s*<string>([^<]*)<\/string>/; const appIdMatch = fileContent.match(appIdRegex); const bundleIdMatch = fileContent.match(bundleIdRegex); if (appIdMatch === null || appIdMatch === void 0 ? void 0 : appIdMatch[1]) { return [ { appId: appIdMatch[1], bundleId: bundleIdMatch === null || bundleIdMatch === void 0 ? void 0 : bundleIdMatch[1], }, ]; } return []; } exports.extractAppIdentifierIos = extractAppIdentifierIos; function extractAppIdentifiersAndroid(fileContent) { var _a, _b; const identifiers = []; try { const config = JSON.parse(fileContent); if (config.client && Array.isArray(config.client)) { for (const client of config.client) { if ((_a = client.client_info) === null || _a === void 0 ? void 0 : _a.mobilesdk_app_id) { identifiers.push({ appId: client.client_info.mobilesdk_app_id, bundleId: (_b = client.client_info.android_client_info) === null || _b === void 0 ? void 0 : _b.package_name, }); } } } } catch (e) { console.error("Error parsing google-services.json:", e); } return identifiers; } exports.extractAppIdentifiersAndroid = extractAppIdentifiersAndroid; async function detectFiles(dirPath, filePattern) { const options = { cwd: dirPath, ignore: [ "**/dataconnect*/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/out/**", "**/.next/**", "**/coverage/**", ], absolute: false, }; return (0, glob_1.glob)(`**/${filePattern}`, options); } exports.detectFiles = detectFiles;