@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
JavaScript
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();