UNPKG

@hoover-institution/hubspot-lib

Version:

A toolkit for deep integration with HubSpot's Marketing Events API with a plugin-based architecture.

231 lines (202 loc) • 7.54 kB
#!/usr/bin/env node import fs from "fs-extra"; import path from "path"; import readline from "readline"; import chalk from "chalk"; import { fileURLToPath } from "url"; import inquirer from "inquirer"; import fileTree from "inquirer-file-tree-selection-prompt"; import { wait } from "../utils/time.js"; inquirer.registerPrompt("file-tree-selection", fileTree); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const ROOT = process.cwd(); async function run() { console.log("\n" + chalk.bold.green("šŸš€ HubSpot Plugin Generator") + "\n"); const { selectionMode } = await inquirer.prompt([ { type: "list", name: "selectionMode", message: "šŸ“‚ How do you want to select the plugin directory?", choices: ["Browse folders", "Manually enter path"], }, ]); let pluginDir; if (selectionMode === "Browse folders") { const { pluginDir: chosenDir } = await inquirer.prompt([ { type: "file-tree-selection", name: "pluginDir", message: "\nšŸ”Œ Select your plugin directory:", onlyShowDir: true, root: ROOT, // Set root to process.cwd() to avoid showing C://... }, ]); const { createNew } = await inquirer.prompt([ { type: "confirm", name: "createNew", message: "\nšŸ“ Do you want to create a new subfolder inside this path?", default: false, }, ]); if (createNew) { const { newName } = await inquirer.prompt([ { type: "input", name: "newName", message: "šŸ“ New folder name:", validate: (val) => !!val.trim() || "Folder name cannot be empty", }, ]); pluginDir = path.join(chosenDir, newName.trim()); } else { pluginDir = chosenDir; } } else { const { manualPath } = await inquirer.prompt([ { type: "input", name: "manualPath", message: "\nāœļø Enter your desired plugin directory (relative or absolute):", }, ]); pluginDir = path.isAbsolute(manualPath.trim()) ? manualPath.trim() : path.join(ROOT, manualPath.trim()); } console.log(); const { pluginNames } = await inquirer.prompt([ { type: "input", name: "pluginNames", message: "šŸŖ Plugin names (comma-separated, use ALL_CAPS).\nšŸ“Œ Usage: PLUGIN_1, PLUGIN_2, PLUGIN_3\n", validate: (val) => !!val.trim() || "Must enter at least one plugin name", }, ]); const parsedPluginNames = pluginNames .split(",") .map((p) => p.trim()) .filter(Boolean); console.log("\nšŸ“¦ Updating package.json...\n"); const pkgPath = path.join(ROOT, "package.json"); if (!(await fs.pathExists(pkgPath))) { console.error(chalk.red(`āŒ Could not find package.json at ${pkgPath}`)); process.exit(1); } const pkgRaw = await fs.readFile(pkgPath, "utf8"); const pkg = JSON.parse(pkgRaw); const oldPath = pkg["hubspot-lib"]?.pluginDir; const relativePluginDir = path.relative(ROOT, pluginDir).replace(/\\/g, "/"); pkg["hubspot-lib"] = pkg["hubspot-lib"] || {}; pkg["hubspot-lib"].pluginDir = relativePluginDir; await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2)); if (oldPath && oldPath !== relativePluginDir) { console.log( chalk.yellow( ` šŸ” pluginDir changed from '${oldPath}' to '${relativePluginDir}'` ) ); } else if (!oldPath) { console.log(chalk.blue(` āž• pluginDir set to '${relativePluginDir}'`)); } else { console.log(chalk.gray(` šŸ”„ pluginDir remains '${relativePluginDir}'`)); } console.log(); await fs.ensureDir(pluginDir); console.log(chalk.bold("šŸ“ Creating plugin files:\n")); for (const plugin of parsedPluginNames) { const filePath = path.join(pluginDir, `${plugin}.js`); const displayPath = path.relative(ROOT, filePath).replace(/\\/g, "/"); if (await fs.pathExists(filePath)) { console.warn(chalk.yellow(`āš ļø Skipped existing: ${displayPath}`)); } else { const content = generatePluginTemplate(plugin); await fs.writeFile(filePath, content); console.log(chalk.green(`āœ… Created: ${displayPath}`)); } } console.log(); console.log("āœ… Scaffolding complete"); console.log(); } function generatePluginTemplate(name) { return [ 'import { createPlugin, EVENTS } from "@hoover-institution/hubspot-lib";', "", `// ${name} plugin: all MarketingEvent actions`, `// There are more variables passed to each hook, there are so many that they aren't documented here`, "", `export default createPlugin("${name}", {`, ` [EVENTS.CREATE_EVENT]: ({ eventName, externalEventId, status }) => {`, ` console.log(\`[${name}][CreateEvent] \${eventName} (ID: \${externalEventId}) → \${status}\`);`, ` return 200;`, ` },`, "", ` [EVENTS.GET_EVENT]: ({ externalEventId, found }) => {`, ` console.log(\`[${name}][GetEvent] ID: \${externalEventId} → \${found ? "Found" : "Not Found"}\`);`, ` return 200;`, ` },`, "", ` [EVENTS.GET_EVENTS]: ({ count }) => {`, ` console.log(\`[${name}][GetEvents] Total events: \${count}\`);`, ` return 200;`, ` },`, "", ` [EVENTS.DELETE_EVENT]: ({ externalEventId, success }) => {`, ` console.log(\`[${name}][DeleteEvent] ID: \${externalEventId} → \${success ? "Deleted" : "Failed"}\`);`, ` return success ? 200 : 404;`, ` },`, "", ` [EVENTS.REGISTER_EMAIL]: ({ email, externalEventId, subscriberState, timestamp }) => {`, ` console.log(\`[${name}][Register] updateRegistration called with:\`, {`, ` email,`, ` externalEventId,`, ` subscriberState,`, ` timestamp,`, ` });`, ` return 200;`, ` },`, "", ` [EVENTS.GET_CONTACTS_BY_STATE]: ({ externalEventId, subscriberState, count }) => {`, ` console.log(\`[${name}][GetContactsByState] Event ID: \${externalEventId}, State: \${subscriberState} → \${count} contacts\`);`, ` return 200;`, ` },`, "", ` [EVENTS.CREATE_OR_FIND_CONTACT_LIST]: ({ listName, listId, created }) => {`, ` console.log(\`[${name}][CreateOrFindContactList] \${listName} (ID: \${listId}) → \${created ? "Created" : "Found"}\`);`, ` return 200;`, ` },`, "", ` [EVENTS.GET_CONTACT_EVENT_STATE]: ({ email, externalEventId, state }) => {`, ` console.log(\`[${name}][GetContactEventState] \${email} (Event ID: \${externalEventId}) → \${state}\`);`, ` return 200;`, ` },`, "", ` [EVENTS.ADD_CONTACT_TO_LIST]: ({ listId, contactId }) => {`, ` console.log(\`[${name}][AddContactToList] Contact ID: \${contactId} → List ID: \${listId}\`);`, ` return 200;`, ` },`, "", ` [EVENTS.REMOVE_CONTACT_FROM_LIST]: ({ listId, contactId }) => {`, ` console.log(\`[${name}][RemoveContactFromList] Contact ID: \${contactId} ← List ID: \${listId}\`);`, ` return 200;`, ` },`, "", ` [EVENTS.ASSOCIATE_LIST_WITH_EVENT]: ({ eventId, listId }) => {`, ` console.log(\`[${name}][AssociateListWithEvent] List ID: \${listId} ↔ Event ID: \${eventId}\`);`, ` return 200;`, ` },`, "", ` [EVENTS.MARKETING_EVENT_ERROR]: ({ action, error }) => {`, ` console.error(\`[${name}][Error] \${action}:\`, error);`, ` return 500;`, ` },`, "});", "", ].join("\n"); } run();