UNPKG

portfolio-generator

Version:

This package generates a bootstrap based portfolio website for you.

292 lines (269 loc) 8.55 kB
const { showWarning, showError, showMultipleProgress, } = require("cybersaksham-npm-logs"); const validateProjectName = require("validate-npm-package-name"); const { checkNodeVersion } = require("./versions"); const path = require("path"); const fs = require("fs-extra"); const chalk = require("chalk"); const prettier = require("prettier"); // Code Imports const structure = require("./code/structure.json"); const { aboutQuestions, contactQuestions, counterQuestions, portfolioQuestions, skillsQuestions, resumeQuestions, manifestQuestions, fileQuestions, addFileConfirmation, } = require("./code/questions"); const datafiles = require("./code/datafiles.json"); module.exports.createApp = async (name, version, dummy = false) => { if (!checkNodeVersion()) { showWarning({ warnings: [ `You are using Node ${process.version} so the project will be bootstrapped with an old unsupported version of tools.`, `Please update to Node 14 or higher for a better, fully supported experience.`, ], summary: ["Falling to react scripts version react-scripts@0.9.x"], }); // Fall back to latest supported react-scripts on Node 4 version = "react-scripts@0.9.x"; } const root = path.resolve(name); const appName = path.basename(root); checkAppName(appName); // Checking directory fs.ensureDirSync(name); isSafeToCreateProjectIn(root, name); // Start creating the project console.log( `${chalk.cyan("[INFO]")} Creating a new Portfolio project in ${chalk.green( root )}.` ); console.log(); await downloadFiles(root); console.log(); console.log(); await addData(root, dummy); }; const checkAppName = (appName) => { const validationResult = validateProjectName(appName); if (!validationResult.validForNewPackages) { // Show Errors let errors = [ `Cannot create a project named ${chalk.green( `"${appName}"` )} because of npm naming restrictions:`, ]; let validationErrors = [ ...(validationResult.errors || []), ...(validationResult.warnings || []), ]; if (validationErrors.length > 0) { errors.push("Found errors are:"); validationErrors.forEach((err) => { errors.push(chalk.red(` *${err}`)); }); } showError({ code: 400, errors, summary: ["Please choose a different project name."], }); process.exit(1); } // TODO: fetch dependencies from package.json file to be added const dependencies = ["react", "react-dom", "react-scripts"].sort(); if (dependencies.includes(appName)) { let errors = [ `Cannot create a project named ${chalk.green( `"${appName}"` )} because a dependency with the same name exists.`, `Due to the way npm works, the following names are not allowed:`, ]; dependencies.map((dep) => { errors.push(chalk.cyan(` ${dep}`)); }); showError({ code: 400, errors, summary: ["Please choose a different project name."], }); process.exit(1); } }; // If project only contains files generated by GH, it’s safe. // Also, if project contains remnant error logs from a previous // installation, lets remove them now. // We also special case IJ-based products .idea because it integrates with CRA: // https://github.com/facebook/create-react-app/pull/368#issuecomment-243446094 const isSafeToCreateProjectIn = (root, name) => { const validFiles = [ ".DS_Store", ".git", ".gitattributes", ".gitignore", ".gitlab-ci.yml", ".hg", ".hgcheck", ".hgignore", ".idea", ".npmignore", ".travis.yml", "docs", "LICENSE", "README.md", "mkdocs.yml", "Thumbs.db", ]; // These files should be allowed to remain on a failed install, but then // silently removed during the next create. const errorLogFilePatterns = [ "npm-debug.log", "yarn-error.log", "yarn-debug.log", ]; const isErrorLog = (file) => { return errorLogFilePatterns.some((pattern) => file.startsWith(pattern)); }; const conflicts = fs .readdirSync(root) .filter((file) => !validFiles.includes(file)) // IntelliJ IDEA creates module files before CRA is launched .filter((file) => !/\.iml$/.test(file)) // Don't treat log files from previous installation as conflicts .filter((file) => !isErrorLog(file)); if (conflicts.length > 0) { let errors = [ `The directory ${chalk.green(name)} contains files that could conflict:`, "", ]; for (const file of conflicts) { try { const stats = fs.lstatSync(path.join(root, file)); if (stats.isDirectory()) { errors.push(` ${chalk.blue(`${file}/`)}`); } else { errors.push(` ${chalk.green(`${file}/`)}`); } } catch (e) { // Ignore } } showError({ code: 400, errors, summary: [ "Either try using a new directory name, or remove the files listed above.", ], }); process.exit(1); } // Remove any log files from a previous installation. fs.readdirSync(root).forEach((file) => { if (isErrorLog(file)) { fs.removeSync(path.join(root, file)); } }); }; // Download files from the github repository // https://github.com/cybersaksham/Portfolio-Generator/tree/master/Required%20Code const downloadFiles = async (root) => { structure.folders.forEach((dir) => { fs.ensureDirSync(path.join(root, dir)); }); const fileList = []; structure.files.forEach(({ name, source }) => { fileList.push({ source, destination: path.join(root, name), }); }); await showMultipleProgress(fileList); }; // Add Data to files // Ask data from user and add to files const addData = async (root, dummy = false) => { insertPackageJson(root); await insertData(root, datafiles.about, aboutQuestions, dummy); await insertData(root, datafiles.contact, contactQuestions, dummy); await insertData(root, datafiles.counter, counterQuestions, dummy); await insertData(root, datafiles.portfolio, portfolioQuestions, dummy); await insertData(root, datafiles.resume, resumeQuestions, dummy); await insertData(root, datafiles.skills, skillsQuestions, dummy); await insertData(root, datafiles.manifest, manifestQuestions, dummy); if (!dummy) console.log(chalk.cyan("Image") + " Data:\n"); await insertFiles(root, datafiles.favicon, dummy, "Favicon Image."); await insertFiles(root, datafiles["404"], dummy, "Error Image."); await insertFiles(root, datafiles.bg, dummy, "Background Image."); await insertFiles( root, datafiles.pic, dummy, "Upload a picture of yourself." ); if (!dummy) { let addResume = await addFileConfirmation( "Do you want to add pdf file for resume?" ); if (addResume) { await insertFiles( root, datafiles.resumePdf, dummy, "Upload pdf of your resume." ); } else { fs.unlinkSync(path.join(root, datafiles.resumePdf)); } } }; // Insert Data in file // Change variables in data file to given data const insertData = async (root, fileLocation, questions, dummy) => { let filepath = path.join(root, fileLocation); if (!dummy) console.log(chalk.cyan(path.basename(filepath)) + " Data:\n"); const data = await questions(root, dummy); let file = fs.readFileSync(filepath).toString(); for (const key in data) { let replacableData = data[key]; if (typeof replacableData === "object") { replacableData = JSON.stringify(replacableData); } file = file.replaceAll(`[[${key}]]`, replacableData); } file = prettier.format(file, { filepath }); fs.writeFileSync(filepath, file); if (!dummy) console.log(); if (!dummy) console.log(); }; // Insert files // Insert favicon.ico, webp image, Resume.pdf const insertFiles = async (root, fileLocation, dummy, message = "") => { if (dummy) { } else { let filepath = path.join(root, fileLocation); let filedata = await fileQuestions( path.basename(filepath), path.extname(filepath), message ); let file = fs.readFileSync(filedata); fs.writeFileSync(filepath, file); } }; // Insert package.json // Change package name in package.json file const insertPackageJson = (root) => { let filepath = path.join(root, "package.json"); let file = fs.readFileSync(filepath).toString(); file = file.replaceAll("[[name]]", path.basename(root).toString()); fs.writeFileSync(filepath, file); };