UNPKG

mzreact-cli

Version:
199 lines (171 loc) 7.17 kB
import chalk from "chalk"; import { getContentsOfTemplateFiles } from "./templateAnalyser"; import { CliProgram, CommandMatch, DefinitionCandidate } from "./types"; import { checkFolderTree, findAncestorFile, findDirBackNavigation, isFileExists, readContentOfFile, writefile } from "./utils/files"; import { capitalize, isAName, isBem, isIdentifier } from "./utils/strings"; import { COMPONENT_PATH_DEFINITION_PATTERN, COMPONENT_PATH_HAVING_MULTIPLE_DEFINITIONS, PARSED_TO_NAME } from "./utils/regexes"; const cli: CliProgram = { options: undefined, instance: undefined } type Definitions = { name: string; prefix: string; }; const verifyNames = (m: RegExpExecArray, folder: string) => { const { custFolder, custPrefix } = m.groups as CommandMatch if ((custFolder && !isAName(custFolder)) || ( !custFolder && !isIdentifier(folder))) { //cli.instance?.error("error") throw new Error("The component name is incorrect ! By default, the CLI applies the folder name as the component name. Consider reading the customization (https://www.npmjs.com/package/mzreact-cli#customization) to maybe tacle this issue ") } else if (custPrefix && !isBem(custPrefix)) { throw new Error("The prefix is invalid ! It should follow the BEM convention for blocks") } } const exists = (x: string | undefined) => x !== "" && x !== undefined; const hyphenToCamelCase = (s: string) => s.replace(/-([a-zA-Z])/g, (g) => g[1].toUpperCase()); // export const getTransformed = (basePath: string, x: string): string => { let m; if ((m = PARSED_TO_NAME.exec(x))) { const groups = m.groups!; const path = groups.same || groups.nested; if (path || groups.nested) { return getTransformed(basePath, path === "." ? basePath : path); // } if (groups.hyphened) { return hyphenToCamelCase(groups.hyphened); } return Object.entries(groups) .map(([key, value]) => { return key !== "qualify" ? value : undefined; }) .find(exists)!; } return x; }; const getComponentName = (m: RegExpExecArray, folder: string): Definitions => { const { custFolder, custPrefix } = m.groups as CommandMatch if (custPrefix) { return { name: capitalize(custFolder), prefix: custPrefix, }; } if (custFolder) { return { name: capitalize(custFolder), prefix: custFolder, }; } return { name: capitalize(folder), prefix: folder.toLowerCase().replace(" ", "-"), } } const writeComponentFiles = (folderTree: string, replacements: Map<string, string>) => { let configFilePath = findAncestorFile('.mzr.md'); let distTemplateFile = __dirname + "/template/.mzr-custom.md"; if (!isFileExists(distTemplateFile)) { distTemplateFile = __dirname + "/template/.mzr.md"; } getContentsOfTemplateFiles(replacements, configFilePath ? configFilePath : distTemplateFile) .forEach(element => { writefile(`${folderTree}/${element.file}`, element.template); }); }; function onReadFile(fileName: string) { if (!cli.options?.force) { throw ` ... Aborting because: Files in target directory !!! - Make sure to create the component in a directory containing no files! - OR you can use the '--force' option or use 'gfc <ComponentName>' - gcf is equivalent to "--generate [--force] --component <ComponentName> [--force]" `; } } const createReplacementsMapping = (folderTree: string, componentNames: Definitions) => { return new Map([ ["#__TO_ROOT__#", findDirBackNavigation(folderTree, "src")!], ["#__COMPONENT__#", componentNames.name], ["#__PREFIX__#", componentNames.prefix], ]); } export const pathArgsAsList = (pathArg: string) => { const m = COMPONENT_PATH_HAVING_MULTIPLE_DEFINITIONS.exec(pathArg); if (m) { let { base, subPathDefinitions } = m.groups as any; const definitions = candidatesFromPressetFile(base, subPathDefinitions); if (definitions.length) { return definitions; } } return [pathArg]; } function pushCandidate({ definitions, definition, base }: DefinitionCandidate) { definition .split(",") .map((sub: string) => (base + "/" + sub).replace("//", "/")).forEach(x => { definitions.push(x) }) } const createComponent = (pathArg: string) => { const messages: { success: string[], fail: { [x: string]: string } } = { success: [], fail: {} }; // TODO: change here const m = COMPONENT_PATH_HAVING_MULTIPLE_DEFINITIONS.exec(pathArg) const base = m ? m[1] : pathArg; pathArgsAsList(pathArg).forEach((definition: string) => { const m = COMPONENT_PATH_DEFINITION_PATTERN.exec(definition); if (m) { const filepath = m[1]; const folder = capitalize( filepath.substring(filepath.lastIndexOf("/") + 1) ); try { const transformed = getTransformed(base, folder); verifyNames(m, transformed); const folderTree = checkFolderTree(filepath, onReadFile); let componentNames = getComponentName(m, transformed); writeComponentFiles(folderTree, createReplacementsMapping(folderTree, componentNames)); messages.success.push(`${componentNames.name}`); } catch (err: any) { messages.fail[folder] = chalk.red(err.message || err); } } }); return commandConclusion(messages); } export { createComponent, cli } function commandConclusion(messages: { success: string[]; fail: { [x: string]: string; }; }) { let result = ["Command executed ..."]; const failures = Object.keys(messages.fail); if (messages.success.length > 0) { result.push(`\x1b[37m[\x1b[32m✔\x1b[37m] \x1b[32m ${messages.success.length} components sucessfuly created :\n${messages.success.map(n => `\x1b[37m${n}\x1b[32m`).join(", ") }`); } if (messages.success.length && failures.length) { result.push('\x1b[37m\n----------------------------\n') } if (failures.length) { result.push(`\x1b[37m[\x1b[31mx\x1b[37m] \x1b[31m ${failures.length} component(s) failed to be created\n${Object.entries(messages.fail).map(x => { const [name, message] = x return `\x1b[31m x \x1b[41m\x1b[37m"${name}"\n\x1b[31m\x1b[49m ${message}\n` }).join("\n")}`) } return result.join("\n") } function candidatesFromPressetFile(base: any, subPathDefinitions: any) { const definitions: string[] = [] subPathDefinitions.split(",") .forEach((definition: string) => { if (isFileExists(definition)) { definition = readContentOfFile(definition) .split("\n").join(",").replace(",,", ','); } pushCandidate({ definitions, definition, base }) }); return definitions; }