Skip to content

Commit

Permalink
Update Care Apps Setup; API URL on window (#9883)
Browse files Browse the repository at this point in the history
  • Loading branch information
gigincg authored Jan 11, 2025
1 parent 544ee22 commit a293183
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 93 deletions.
6 changes: 5 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ ESLINT_NO_DEV_ERRORS=true
CARE_CDN_URL="https://egov-s3-facility-10bedicu.s3.amazonaws.com https://egov-s3-patient-data-10bedicu.s3.amazonaws.com http://localhost:4566"
REACT_ALLOWED_LOCALES="en,hi,ta,ml,mr,kn"

REACT_ENABLED_APPS="https://care-scribe-fe.pages.dev|ohcnetwork/care_scribe_fe"
# Remote apps
# Localhost : ohcnetwork/care_scribe_fe@localhost:4173
# Remote URL : ohcnetwork/care_scribe_fe@https://care-scribe-fe.pages.dev
# Repo/Github Pages : ohcnetwork/care_scribe_fe
REACT_ENABLED_APPS=""
82 changes: 62 additions & 20 deletions scripts/setup-care-apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,70 @@ interface Plugin {
camelCaseName: string;
}

interface ParsedRemoteConfig {
url: string;
org: string;
repo: string;
}

/**
* Parses a remote app configuration string into its components
* Supports two formats:
* 1. GitHub Pages: "organization/repository"
* Example: "coronasafe/care_fe"
*
* 2. Custom URL: "organization/repository@url"
* Example: "coronasafe/care_fe@localhost:5173"
* Example: "coronasafe/[email protected]"
* Note: Protocol (http/https) is automatically added in the vite config:
* - localhost URLs use http://
* - all other URLs use https://
*
* @param appConfig - Configuration string for a remote app
* @returns Parsed configuration object
*/
function parseRemoteConfig(appConfig: string): ParsedRemoteConfig {
// Handle custom URLs (both localhost and custom hosted)
if (appConfig.includes("@")) {
const [package_] = appConfig.split("@");
const [org, repo] = package_.split("/");
return {
url: "", // URL not needed for plugin setup
org,
repo,
};
}

// Handle GitHub Pages URLs
const [org, repo] = appConfig.split("/");
return {
url: "", // URL not needed for plugin setup
org,
repo,
};
}

// Function to read enabled apps from env
function readAppsConfig(): Plugin[] {
const appsConfig = process.env.REACT_ENABLED_APPS
? process.env.REACT_ENABLED_APPS.split(",").map((app) => {
const package_ = app.includes("|")
? app.split("|")[1].split("@")[0]
: app.split("@")[0];
console.log(package_);
const [, repo] = package_.split("/");
return {
repo,
// Convert repo name to camelCase for import
camelCaseName: repo
.replace(/[-_]/g, "")
.replace(/\b\w/g, (char, index) =>
index === 0 ? char.toLowerCase() : char.toUpperCase(),
),
};
})
: [];
console.log("Found plugins: ", appsConfig);
return appsConfig;
if (!process.env.REACT_ENABLED_APPS) {
return [];
}

const plugins = process.env.REACT_ENABLED_APPS.split(",").map((app) => {
const { repo } = parseRemoteConfig(app);
return {
repo,
// Convert repo name to camelCase for import
camelCaseName: repo
.replace(/[-_]/g, "")
.replace(/\b\w/g, (char, index) =>
index === 0 ? char.toLowerCase() : char.toUpperCase(),
),
};
});

console.log("Found plugins:", plugins);
return plugins;
}

const plugins = readAppsConfig();
Expand Down
10 changes: 10 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ import App from "@/App";
import "@/i18n";
import "@/style/index.css";

// Extend Window interface to include CARE_API_URL
declare global {
interface Window {
CARE_API_URL: string;
}
}

// Set API URL from environment variable
window.CARE_API_URL = import.meta.env.REACT_CARE_API_URL;

if ("serviceWorker" in navigator) {
registerSW({ immediate: false });
}
Expand Down
126 changes: 54 additions & 72 deletions vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -60,66 +60,70 @@ function getPluginAliases() {
return aliases;
}

function getPluginDependencies(): string[] {
const pluginsDir = path.resolve(__dirname, "apps");
// Make sure the `apps` folder exists
if (!fs.existsSync(pluginsDir)) {
return [];
}
const pluginFolders = fs.readdirSync(pluginsDir);

const dependencies = new Set<string>();

pluginFolders.forEach((pluginFolder) => {
const packageJsonPath = path.join(pluginsDir, pluginFolder, "package.json");
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
const pluginDependencies = packageJson.dependencies
? Object.keys(packageJson.dependencies)
: [];
pluginDependencies.forEach((dep) => dependencies.add(dep));
}
});

return Array.from(dependencies);
/**
* Parses a remote app configuration string into its components
* @param appConfig - Configuration string for a remote app
* @returns Parsed configuration object
*/
interface ParsedRemoteConfig {
url: string;
org: string;
repo: string;
}

// Recursive function to check if the module is statically imported by an entry point
function isStaticallyImportedByEntry(
getModuleInfo: (moduleId: string) => any,
moduleId: string,
visited = new Set(),
) {
if (visited.has(moduleId)) return false;
visited.add(moduleId);

const modInfo = getModuleInfo(moduleId);
if (!modInfo) return false;

// Check if the module is an entry point
if (modInfo.isEntry) {
return true;
function parseRemoteConfig(appConfig: string): ParsedRemoteConfig {
if (!appConfig.includes("/")) {
throw new Error(
`Invalid app configuration format: ${appConfig}. Expected 'org/repo' or 'org/repo@url'.`,
);
}

// Check all static importers
for (const importerId of modInfo.importers) {
if (isStaticallyImportedByEntry(getModuleInfo, importerId, visited)) {
return true;
// Handle custom URLs (both localhost and custom hosted)
if (appConfig.includes("@")) {
const [package_, url] = appConfig.split("@");
const [org, repo] = package_.split("/");
if (!org || !repo || !url) {
throw new Error(
`Invalid custom URL configuration: ${appConfig}. Expected 'org/repo@url'.`,
);
}
// Add appropriate protocol based on whether it's localhost
const protocol = url.includes("localhost") ? "http://" : "https://";
const fullUrl = url.startsWith("http") ? url : `${protocol}${url}`;

return {
url: `${fullUrl}/assets/remoteEntry.js`,
org,
repo,
};
}

return false;
// Handle GitHub Pages URLs
const [org, repo] = appConfig.split("/");
if (!org || !repo) {
throw new Error(
`Invalid GitHub Pages configuration: ${appConfig}. Expected 'org/repo'.`,
);
}
return {
url: `https://${org}.github.io/${repo}/assets/remoteEntry.js`,
org,
repo,
};
}

/**
* Generates remote module configurations for Module Federation
*
* Supports two formats for REACT_ENABLED_APPS:
* 1. GitHub Pages: "organization/repository[@branch]"
* Example: "coronasafe/care_fe@main"
* 1. GitHub Pages: "organization/repository"
* Example: "coronasafe/care_fe"
*
* 2. Custom URL: "prot://localhost:port|organization/repository[@branch]"
* Example: "http://localhost:5173|coronasafe/care_fe@main"
* 2. Custom URL: "organization/repository@url"
* Example: "coronasafe/care_fe@localhost:5173"
* Example: "coronasafe/[email protected]"
* Note: Protocol (http/https) is automatically added based on the URL:
* - localhost URLs use http://
* - all other URLs use https://
*
* @param enabledApps - Comma-separated list of enabled apps
* @returns Remote module configuration object for Module Federation
Expand All @@ -128,35 +132,13 @@ function getRemotes(enabledApps: string) {
if (!enabledApps) return {};

return enabledApps.split(",").reduce((acc, app) => {
const [package_, branch = "main"] = app.split("@");
const { repo, url } = parseRemoteConfig(app);
console.log(`Configuring Remote Module for ${repo}:`, url);

// Handle custom URLs
if ((package_.includes("|"))) {
const [host, pathParts] = package_.split("|");
const [org, repo] = pathParts.split("/");
const remoteUrl = `"${host}/assets/remoteEntry.js"`;
console.log(`Using Local Remote Module for ${org}/${repo}:`, remoteUrl);
return {
...acc,
[repo]: {
external: `Promise.resolve(${remoteUrl})`,
from: "vite",
externalType: "promise",
},
};
}

// Handle GitHub Pages URLs
const [org, repo] = package_.split("/");
const remoteUrl = `"https://${org}.github.io/${repo}/assets/remoteEntry.js"`;
console.log(
`Using GitHub Pages Remote Module for ${org}/${repo}:`,
remoteUrl,
);
return {
...acc,
[repo]: {
external: `Promise.resolve(${remoteUrl})`,
external: `Promise.resolve("${url}")`,
from: "vite",
externalType: "promise",
},
Expand Down

0 comments on commit a293183

Please sign in to comment.