firebase-tools
Version:
Command-Line Interface for Firebase
249 lines (248 loc) • 9.98 kB
JavaScript
;
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;