UNPKG

@ventum-digital/iiq-plugin-project-generator

Version:

A npm tool to set-up the project structure for developing an IIQ Plugin.

287 lines (216 loc) 8.41 kB
const fs = require('fs'); const path = require('path'); const minimist = require('minimist'); const globalDirectory = require("./borrowed/global-directory").default; const {spawn} = require('child_process'); const rl = require('readline-sync'); const {getTemplateResourceBasePath, getAvailableSubTemplates, BASE_TEMPLATE_NAME} = require("./TemplateHelper"); function queryTemplate(baseTemplateName) { function printItem(i, name) { console.log(`\t[${i}] ${name}`); } function parseAnswer(selection) { if (selection === "") { return 0; } else { return parseInt(selection); } } function isInvalidSelection(selection, max) { if (isNaN(selection) || selection < 0 || selection > max) { console.log(`Invalid selection. Please select a number between 0 and ${max}`); return true; } return false; } console.log("'template' option was not specified. Please select a template from the list:"); let extensions = getAvailableSubTemplates(baseTemplateName); for (let i = 0; i < extensions.length; i++) { printItem(i, extensions[i]); } let selection; do { selection = parseAnswer(rl.question("> Select template [default: 0]: ")); } while (isInvalidSelection(selection, extensions.length)); return extensions[selection]; } module.exports = { generateProject: ( templateName, cmdOptionsRequired, cmdOptionsOptional = {}, additionalReplaceValuesGetter = null, argValidator = null ) => { if (cmdOptionsRequired.template !== undefined) { throw new Error("'template' option is reserved"); } if (Object.values(cmdOptionsRequired).includes("t")) { throw new Error("'t' short option is reserved"); } cmdOptionsRequired = { ...cmdOptionsRequired, template: "t", }; const cmdOptions = {...cmdOptionsRequired, ...cmdOptionsOptional}; let commandLineArgs = process.argv.slice(2); const args = minimist(commandLineArgs, { alias : cmdOptions, string: Object.keys(cmdOptions) }); // Validate required arguments const missingArgs = Object.keys(cmdOptionsRequired).filter(arg => !args[arg]); for (const missingArg of missingArgs) { let arg; if (missingArg === "template") { arg = queryTemplate(templateName); } else { do { arg = rl.question(`> Provide value for "${missingArg}": `); } while (!arg); } args[missingArg] = arg; } const replaceValues = { ...((() => { const result = {}; for (const key of Object.keys(cmdOptions)) { result[key] = args[key]; } return result; })()), ...(additionalReplaceValuesGetter ? additionalReplaceValuesGetter(args) : {}) } // Validate arguments if (argValidator) { try { argValidator(replaceValues); } catch (error) { console.error(`Error: ${error.message}`); process.exit(1); } } ////////////////////////////// // Function to create a directory if it doesn't exist function createDir(dirPath) { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, {recursive: true}); } } // Function to create a file with content function createFile(filePath, content, overwrite) { if (overwrite || !fs.existsSync(filePath)) { fs.writeFileSync(filePath, content, 'utf8'); console.log(`Created: ${filePath}`); } else { console.log(`Skipped (already exists): ${filePath}`); } } // Generates a project by copying files from the "resources" folder, // replacing tokens (e.g., %%projectName%% and %%version%%), and recreating function replaceTokens(content, delimiter = "__") { for (const key of Object.keys(replaceValues)) { const token = `${delimiter}${key}${delimiter}`; content = content.replace(new RegExp(token, 'g'), replaceValues[key] || ''); } return content; } function shouldNotBeTokenized(fileName) { const extensions = [".png", ".jpg", ".gif", ".jar"]; return fileName && extensions.some(ext => fileName.endsWith(ext)); } // Helper function to copy files and replace tokens function prepareFiles(srcDir, destDir, overwrite = true) { const entries = fs.readdirSync(srcDir, {withFileTypes: true}); entries.forEach(entry => { let srcPath = path.join(srcDir, entry.name); if (entry.name.startsWith("__delete__")) { // Remove the file or folder const name = entry.name.substring("__delete__".length); const pathToDelete = path.join(destDir, name); if (fs.existsSync(pathToDelete)) { fs.rmSync(pathToDelete, {recursive: true, force: true}); console.log(`Deleted: ${pathToDelete}`); } return; } let replacedName = replaceTokens(entry.name); // Replace tokens in the file or folder name if (replacedName === "__gitignore__") { replacedName = ".gitignore"; } if (replacedName !== entry.name) { console.log(`Renaming: ${entry.name} -> ${replacedName}`); } let destPath = path.join(destDir, replacedName); if (entry.isDirectory()) { createDir(destPath); prepareFiles(srcPath, destPath); } else if (entry.isFile()) { // If is a text file, replace tokens, otherwise just copy if (shouldNotBeTokenized(replacedName)) { fs.copyFileSync(srcPath, destPath); console.log(`Copied: ${replacedName}`); } else { let content = fs.readFileSync(srcPath, 'utf8'); content = replaceTokens(content); createFile(destPath, content, overwrite); } } }); } ////////////////////////////// const projectName = args.packageName; const projectDir = path.resolve(projectName); // Use require.resolve to locate the "templates/plugin" directory in the package let resourcesDir; try { // Resolve the templates directory within the package resourcesDir = getTemplateResourceBasePath(templateName); } catch (error) { console.error(`Error: Could not locate '${resourcesDir}' directory. Make sure the package is installed globally.`); process.exit(1); } if (!fs.existsSync(resourcesDir)) { console.error(`Resources folder not found at: ${resourcesDir}`); process.exit(1); } if (fs.existsSync(projectDir)) { console.error(`Error: Project directory already exists: ${projectDir}`); process.exit(1); } // Create the main project directory createDir(projectDir); // Process and copy files from the resources folder prepareFiles(path.join(resourcesDir, "base"), projectDir); if (args.template !== BASE_TEMPLATE_NAME) { const templateDir = path.join(resourcesDir, "extensions", args.template); if (!fs.existsSync(templateDir)) { console.error(`Error: Template directory not found: ${templateDir}`); process.exit(1); } console.log('-------------------------------------------------------------'); console.log(`Using template: ${args.template}` ); prepareFiles(templateDir, projectDir); } console.log('-------------------------------------------------------------'); console.log(`Project '${projectName}' created successfully!`); return { launchIntelliJ: () => { // Define the command and arguments const command = 'idea'; const args = [`"${projectDir}"`]; console.log(`Running: ${command} ${args.join(' ')}`); // Spawn the process with detached mode const child = spawn(command, args, { detached: true, // Detach the process to disown it stdio : 'ignore', // Ignore standard I/O streams shell : true // Run the command in a shell }); // Prevent the child process from being killed when the parent exits child.unref(); console.log(`IntelliJ IDEA started: ${projectName}`); } } }, }