UNPKG

vueless

Version:

Vue Styleless UI Component Library, powered by Tailwind CSS.

154 lines (118 loc) 5.96 kB
/* eslint-disable no-console */ import path from "node:path"; import { cwd } from "node:process"; import { existsSync } from "node:fs"; import { styleText } from "node:util"; import { cp, readFile, writeFile, rename } from "node:fs/promises"; import { getDirFiles } from "../../utils/node/helper.js"; import { replaceRelativeImports } from "../utils/format.js"; import { getStorybookId, getStoryMetaKeyIndex } from "../utils/data.js"; import { COMPONENTS, VUELESS_PACKAGE_DIR, VUELESS_USER_COMPONENTS_DIR } from "../../constants.js"; /** * Copies an existing Vueless component to a new location with a new name. * This includes duplicating the source files and updating the component's internal references as needed. * If the target name is omitted or matches the source name, the component will override the original. * * @param {Array<string>} options - An array containing one or two elements: * – The name of the component to be copied. * – (Optional) The desired name for the copied component. If omitted, uses the source component name. * @return {Promise<void>} A promise that resolves when the component has been successfully copied. * If an error occurs, no value is returned, and the operation exits early with a logged message. */ export async function copyVuelessComponent(options) { const [componentName, newComponentName = ""] = options; if (!componentName) { console.log(styleText("red", "Component name is required.")); return; } if (!(componentName in COMPONENTS)) { console.log(styleText("red", "There is no such component.")); return; } if (newComponentName && !newComponentName.startsWith("U")) { console.log(styleText("red", `Component should have 'U' prefix (ex. 'UButtonCustom').`)); return; } const absoluteSourcePath = path.join(cwd(), VUELESS_PACKAGE_DIR, COMPONENTS[componentName]); const absoluteDestPath = path.join(cwd(), VUELESS_USER_COMPONENTS_DIR, newComponentName); if (existsSync(absoluteDestPath)) { console.log(styleText("red", `Component with name '${newComponentName}' already exists.`)); return; } await cp(absoluteSourcePath, absoluteDestPath, { recursive: true }); await modifyCreatedComponent(absoluteDestPath, componentName, newComponentName); const isOverridingOriginal = componentName === newComponentName; const destDir = `${VUELESS_USER_COMPONENTS_DIR}/${newComponentName}`; const successMessage = isOverridingOriginal ? `The '${componentName}' was successfully copied and will override the original component.` : `The '${componentName}' was successfully copied into the '${destDir}' directory.`; console.log(styleText("green", successMessage)); } /** * Modifies the files related to a specified component by renaming the component and updating its references * across various files in the specified directory. * * @param {string} destPath - The destination path where the component and its related files are located. * @param {string} componentName - The current name of the component to be modified. * @param {string} newComponentName - The new name to assign to the component and its references. * @return {Promise<void>} A promise that resolves when the modification process is completed. */ async function modifyCreatedComponent(destPath, componentName, newComponentName) { const destFiles = await getDirFiles(destPath, ""); const storybookId = await getStorybookId(); for await (const filePath of destFiles) { const fileContent = await readFile(filePath, "utf-8"); let updatedContent = replaceRelativeImports(newComponentName, filePath, fileContent); /* Renaming component name in constants */ if (filePath.endsWith("constants.ts")) { updatedContent = updatedContent.replaceAll(componentName, newComponentName); } /* Renaming component name in tests */ if (filePath.endsWith("test.ts")) { updatedContent = updatedContent.replaceAll(componentName, newComponentName); } /* Renaming component name in types */ if (filePath.endsWith("types.ts")) { updatedContent = updatedContent.replaceAll(componentName, newComponentName); } /* Renaming component name in components */ if (filePath.endsWith(".vue")) { let lines = updatedContent.split("\n"); for (const [index, line] of lines.entries()) { // Add some condition here in future if some edge cases appear if (line.includes(componentName)) { lines[index] = line.replaceAll(componentName, newComponentName); } } updatedContent = lines.join("\n"); } /* Renaming component name in stories */ if (filePath.endsWith("stories.ts")) { let lines = updatedContent.split("\n"); // saving indexes const storyIdIndex = getStoryMetaKeyIndex(fileContent, "id"); const storyTitleIndex = getStoryMetaKeyIndex(fileContent, "title"); const componentImportIndex = lines.findIndex( (line) => line.includes(componentName) && line.includes("import"), ); updatedContent = lines.join("\n").replaceAll(componentName, newComponentName); lines = updatedContent.split("\n"); // replacing lines by indexes lines[storyIdIndex] = ` id: "${storybookId}",`; lines[storyTitleIndex] = ` title: "Custom / ${newComponentName}",`; lines[componentImportIndex] = `import ${newComponentName} from "../${newComponentName}.vue";`; updatedContent = lines.join("\n"); } /* Renaming file */ let targetPath = filePath; const [fileName] = filePath.split("/").reverse(); if (fileName.includes(componentName)) { const [targetDir] = filePath.split(fileName); const targetFileName = fileName.replace(componentName, newComponentName); targetPath = path.join(targetDir, targetFileName); await rename(filePath, targetPath); } /* Update file */ await writeFile(targetPath, updatedContent); } }