UNPKG

budgie-cli

Version:
204 lines (170 loc) 6.72 kB
import { Language, LanguagesBag } from "budgie"; import chalk from "chalk"; import { ExitCode } from "./codes"; import { convertFiles } from "./conversions/convertFiles"; import { ConversionStatus } from "./converters/converter"; import { createConvertersBag } from "./converters/convertersBag"; import { IFileSystem } from "./fileSystem"; import { ILogger } from "./logger"; import { IBudgieProjectMetadata } from "./postprocessing/metadata"; import { postprocess } from "./postprocessing/postprocess"; import { preprocessFiles } from "./preprocessing/preprocessFiles"; import { queueAsyncActions } from "./utils/asyncQueue"; /** * Dependencies to set up and run a runner. */ export interface IMainDependencies { /** * Base or root directory to ignore from the beginning of file paths, such as "src/", if not "". */ baseDirectory?: string; /** * Unique file paths to convert. */ filePaths: ReadonlySet<string>; /** * Reads and writes files. */ fileSystem: IFileSystem; /** * Names of output language(s) to convert to. */ languageNames?: ReadonlyArray<string>; /** * Logs information on significant events. */ logger: ILogger; /** * Namespace before path names, such as "Budgie", if not "". */ namespace?: string; /** * Budgie configuration project, if provided. */ project?: string; /** * TypeScript configuration project, if provided. */ typescriptConfig?: string; } /** * Reads a set of file paths into memory. * * @param filePaths Unique file paths to read in. * @param fileSystem Reads and writes files. * @returns File contents of the files, keyed by file path. */ const readFilesFromSystem = async (filePaths: ReadonlySet<string>, fileSystem: IFileSystem) => { const map = new Map<string, string>(); await queueAsyncActions( Array.from(filePaths).map((filePath: string) => async () => { map.set(filePath, await fileSystem.readFile(filePath)); }), ); return map; }; const getProjectMetadata = async (project: string | undefined, fileSystem: IFileSystem) => { if (project === undefined) { return undefined; } try { return JSON.parse(await fileSystem.readFile(project)) as IBudgieProjectMetadata; } catch (error) { return error; } }; /** * Validates Budgie settings, sets up a conversion runner, and runs it. * * @param dependencies Dependencies to set up and run a runner. */ export const main = async (dependencies: IMainDependencies): Promise<ExitCode> => { const printAvailableLanguages = (languageNames: ReadonlyArray<string>) => { dependencies.logger.log("Available languages:"); for (const languageName of languageNames) { dependencies.logger.log(` ${languageName}`); } }; const getLanguagesFromNames = (languageNames: ReadonlyArray<string> | undefined): Language[] | undefined => { const languagesBag = new LanguagesBag(); const supportedLanguageNames = languagesBag.getLanguageNames(); if (languageNames === undefined || languageNames.length === 0) { dependencies.logger.error("You must provide a -l/--language."); printAvailableLanguages(supportedLanguageNames); return undefined; } const languages = []; for (const languageName of languageNames) { if (languageNames.indexOf(languageName) === -1) { dependencies.logger.error(`Unknown language name: '${chalk.bold(languageName)}'.`); printAvailableLanguages(languageNames); return undefined; } languages.push(languagesBag.getLanguageByName(languageName)); } return languages; }; const run = async (): Promise<ExitCode> => { // 0a: Retrieve corresponding Budgie languages for -l/--language const languages = getLanguagesFromNames(dependencies.languageNames); if (languages === undefined) { return ExitCode.Error; } // 0b: Read project metadata if a Budgie project file is provided const metadata = await getProjectMetadata(dependencies.project, dependencies.fileSystem); if (metadata instanceof Error) { return ExitCode.Error; } // 0c: Create language preprocessor converters per known language type const existingFileContents = await readFilesFromSystem(dependencies.filePaths, dependencies.fileSystem); const convertersBag = createConvertersBag({ baseDirectory: dependencies.baseDirectory, existingFileContents, fileSystem: dependencies.fileSystem, logger: dependencies.logger, metadata, outputNamespace: dependencies.namespace, typescriptConfig: dependencies.typescriptConfig, }); // 1: Preprocess any known language types, such as .ts, to .bg files const preprocessResult = await preprocessFiles({ convertersBag, filePaths: dependencies.filePaths, fileSystem: dependencies.fileSystem, languages, logger: dependencies.logger, }); if (preprocessResult.status === ConversionStatus.Failed) { return ExitCode.Error; } // 2: Convert all .bg files to the output language const conversionResults = await convertFiles({ baseDirectory: dependencies.baseDirectory, budgieFilePaths: preprocessResult.budgieFilePaths, existingFileContents, fileSystem: dependencies.fileSystem, languages, logger: dependencies.logger, outputNamespace: dependencies.namespace, typescriptConfig: dependencies.typescriptConfig, }); if (conversionResults.status === ConversionStatus.Failed) { return ExitCode.Error; } // 3: Create project metadata files (and soon exports) await postprocess({ fileSystem: dependencies.fileSystem, languages, logger: dependencies.logger, project: dependencies.project, }); return ExitCode.Ok; }; try { return await run(); } catch (error) { dependencies.logger.error(error.stack); return ExitCode.Error; } }; export type IMain = typeof main;