@interopio/cli
Version:
Interop.io CLI - a command line for creating Interop.io applications
144 lines (143 loc) • 8.82 kB
JavaScript
/* eslint-disable import/default */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ux } from "@oclif/core";
import decompress from "decompress";
import fs from "fs-extra/esm";
import log4js from "log4js";
import { existsSync } from "node:fs";
import { copyFile, mkdir, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { PLATFORM_TEMPLATE, TEMP_ZIP_NAME } from "../../shared/constants.js";
import { obfuscateLicense } from "../../utils/license-obfuscate.js";
import locatorService from "../locator.service.js";
export class BrowserPlatformService {
currentDir = process.cwd();
logger = log4js.getLogger("NewBrowserPlatformService");
async build(config) {
const { appName, template, version } = config;
this.logger.info(`Creating new ${appName} app with io.CB version ${version} and template ${template}`);
ux.info(`Creating new ${appName} app with io.CB version ${version} and template ${template}`);
const cliConfig = await locatorService.configurationService.getCliConfig();
const appDir = join(this.currentDir, appName);
this.logger.info(`Making new app directory at ${appDir}`);
await mkdir(appDir);
this.logger.info(`New app directory created at ${appDir}, proceeding with fetching ${template} template`);
const resolvedMode = await this.getZipTemplate(config, cliConfig);
this.logger.info(`Fetched ${template} template with mode ${resolvedMode}, proceeding with unpacking`);
ux.info(`Proceeding with ${resolvedMode} template`);
await this.unpackZipTemplate(appDir, template, join(appDir, TEMP_ZIP_NAME));
this.logger.info(`Unpacked ${template} template, proceeding with ${template} configuration`);
if (template === "vanilla-js") {
await this.configureVanillaJsTemplate(config, cliConfig);
}
if (template === "home-react-wsp") {
await this.configureHomeWspReactTemplate(config, cliConfig);
}
if (template === "dev-react-seed") {
await this.configureDevReactSeedTemplate(config, cliConfig);
}
if (template === "wsp-frame") {
await this.configureWspFrameTemplate(config, cliConfig);
}
this.logger.info(`New ${appName} app creation complete.`);
ux.info(`New Browser Platform Creation Complete. Please run "cd ./${appName}" and "npm install" to install dependencies${template === PLATFORM_TEMPLATE.DEV_REACT_SEED ? `, "npm run bootstrap" to install all dependencies for all apps` : ""} and "npm start" to start the app.`);
}
async clean(config) {
const appDir = join(this.currentDir, config.appName);
this.logger.info(`Cleaning up the app directory at ${appDir}`);
await rm(appDir, { force: true, recursive: true });
ux.action.stop("New Browser Platform Creation Failed. Cleaned up the app directory.");
this.logger.info(`Cleaned up the app directory at ${appDir}`);
}
async configureDevReactSeedTemplate(config, cliConfig) {
const appDir = join(this.currentDir, config.appName);
const configExampleLocation = join(appDir, "workspace-platform", "src", "config.example.json");
const configLocation = join(appDir, "workspace-platform", "src", "config.json");
await this.setPlatformConfig(cliConfig, configExampleLocation, configLocation, "dev-react-seed");
}
async configureHomeWspReactTemplate(config, cliConfig) {
const appDir = join(this.currentDir, config.appName);
const configExampleLocation = join(appDir, "src", "config.example.json");
const configLocation = join(appDir, "src", "config.json");
await this.setPlatformConfig(cliConfig, configExampleLocation, configLocation, "home-react-wsp");
}
async configureVanillaJsTemplate(config, cliConfig) {
const appDir = join(this.currentDir, config.appName);
const configExampleLocation = join(appDir, "public", "config.example.json");
const configLocation = join(appDir, "public", "config.json");
await this.setPlatformConfig(cliConfig, configExampleLocation, configLocation, "vanilla-js");
}
async configureWspFrameTemplate(config, cliConfig) {
const appDir = join(this.currentDir, config.appName);
const configExampleLocation = join(appDir, "src", "config.example.json");
const configLocation = join(appDir, "src", "config.json");
await this.setPlatformConfig(cliConfig, configExampleLocation, configLocation, "wsp-frame");
}
async getZipTemplate(config, cliConfig) {
const { appName, version } = config;
const appDir = join(this.currentDir, appName);
const zipLocation = join(appDir, TEMP_ZIP_NAME);
if (cliConfig.mode === "remote") {
this.logger.info(`Fetching remote template ${version}, to zip location ${zipLocation}`);
await this.resolveRemoteZip(version, zipLocation);
this.logger.info(`Completed remote template fetch`);
return "remote";
}
if (cliConfig.mode === "offline") {
this.logger.info(`Fetching offline template ${version}, to zip location ${zipLocation}`);
await this.resolveLocalZip(version, zipLocation);
this.logger.info(`Completed local template fetch`);
return "offline";
}
try {
this.logger.info(`Fetching remote template ${version}, to zip location ${zipLocation}, with auto mode`);
await this.resolveRemoteZip(version, zipLocation);
this.logger.info(`Completed remote template fetch with auto mode`);
return "remote";
}
catch (error) {
this.logger.warn(`Could not get the remote template, falling back to the offline template. Reason: ${error.message}`);
ux.warn(`Could not get the remote template, falling back to the offline template. Reason: ${error.message}`);
await this.resolveLocalZip(version, zipLocation, `Unable to get the remote template, resolved to the offline template ${version}`);
this.logger.info(`Completed local fallback template fetch with auto mode`);
return "offline";
}
}
async resolveLocalZip(version, zipLocation, stopMessage) {
ux.action.start(`Fetching offline template ${version}`);
const verifiedVersion = await locatorService.validationService.transformVersion(version, "ioCb", "offline");
const { root } = locatorService.configurationService.directoryConfig;
const templateLocation = join(root, "templates", verifiedVersion ? `${verifiedVersion}.zip` : "latest.zip");
existsSync(templateLocation) || ux.error(`Could not find the offline template ${version}`);
await copyFile(templateLocation, zipLocation);
ux.action.stop(stopMessage);
}
async resolveRemoteZip(version, zipLocation, stopMessage) {
ux.action.start(`Downloading remote template ${version}`);
const verifiedVersion = await locatorService.validationService.transformVersion(version, "ioCb", "remote");
await locatorService.downloaderService.downloadRemoteZip(verifiedVersion, zipLocation);
ux.action.stop(stopMessage);
}
async setPlatformConfig(cliConfig, configExampleLocation, configLocation, template) {
this.logger.info(`Setting the config.json file with license key ${cliConfig.licenseKey ? obfuscateLicense(cliConfig.licenseKey) : "no license key"}, using example file at ${configExampleLocation}`);
existsSync(configExampleLocation) || ux.error(`Could not find the config.example.json file at ${configExampleLocation}`);
const configFile = await fs.readJSON(configExampleLocation, { throws: false });
if (template === "dev-react-seed" || template === "wsp-frame") {
configFile.browserPlatform.licenseKey = cliConfig.licenseKey;
}
else {
configFile.licenseKey = cliConfig.licenseKey;
}
await writeFile(configLocation, JSON.stringify(configFile, null, 4));
this.logger.info(`Set the config.json file at location ${configLocation}`);
}
async unpackZipTemplate(appDir, template, zipLocation) {
await mkdir(join(appDir, "temp"));
await decompress(zipLocation, join(appDir, "temp"), { strip: 1 });
const source = join(appDir, "temp", `browser-platform-${template}`);
existsSync(source) || ux.error(`Could not find the template ${join(appDir, "temp", `browser-platform-${template}`)}`);
fs.copySync(source, appDir, { overwrite: true });
await rm(zipLocation, { force: true, recursive: true });
await rm(join(appDir, "temp"), { force: true, recursive: true });
}
}