@feedinbox/cli
Version:
CLI tool for installing FeedInbox components into your project
335 lines (317 loc) • 11.8 kB
JavaScript
// src/commands/add.ts
import path3 from "path";
import fs3 from "fs-extra";
import chalk2 from "chalk";
import inquirer from "inquirer";
// src/utils/registry.ts
async function getTemplateRegistry() {
return {
"react-vanilla": {
name: "React + Vanilla CSS",
description: "Clean React components with vanilla CSS styling",
dependencies: {
"@feedinbox/sdk": "^1.0.0"
},
devDependencies: {
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0"
},
files: [
"FeedbackWidget.tsx",
"NewsletterWidget.tsx",
"ContactWidget.tsx",
"index.ts",
"styles/index.css",
"styles/feedback-widget.css",
"styles/newsletter-widget.css",
"styles/contact-widget.css"
]
},
"react-tailwind": {
name: "React + Tailwind CSS",
description: "React components styled with Tailwind CSS utilities",
dependencies: {
"@feedinbox/sdk": "^1.0.0"
},
devDependencies: {
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"tailwindcss": "^3.4.0"
},
files: [
"FeedbackWidget.tsx",
"NewsletterWidget.tsx",
"ContactWidget.tsx",
"index.ts"
]
},
"react-shadcn": {
name: "React + shadcn/ui",
description: "React components using shadcn/ui component library",
dependencies: {
"@feedinbox/sdk": "^1.0.0"
},
devDependencies: {
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0"
},
files: [
"FeedbackWidget.tsx",
"NewsletterWidget.tsx",
"ContactWidget.tsx",
"index.ts",
"ui/button.tsx",
"ui/input.tsx",
"ui/textarea.tsx",
"ui/dialog.tsx",
"ui/checkbox.tsx",
"ui/radio-group.tsx"
]
}
};
}
// src/utils/template.ts
import path from "path";
import fs from "fs-extra";
import { fileURLToPath } from "url";
var __filename = fileURLToPath(import.meta.url);
var __dirname = path.dirname(__filename);
async function copyTemplate(templateType, targetDir) {
const templateDir = path.join(__dirname, "../../templates", templateType);
if (!await fs.pathExists(templateDir)) {
throw new Error(`Template directory not found: ${templateType}`);
}
await fs.copy(templateDir, targetDir, {
overwrite: true,
filter: (src) => {
return !src.includes(".git") && !src.includes("node_modules");
}
});
console.log(`\u{1F4C1} Template files copied from ${templateType}`);
}
// src/utils/package.ts
import path2 from "path";
import fs2 from "fs-extra";
import chalk from "chalk";
async function updatePackageJson(dependencies, devDependencies) {
const packageJsonPath = path2.join(process.cwd(), "package.json");
if (!await fs2.pathExists(packageJsonPath)) {
console.log(chalk.yellow("\u26A0\uFE0F No package.json found. You may need to run npm init first."));
return;
}
const packageJson = await fs2.readJson(packageJsonPath);
if (Object.keys(dependencies).length > 0) {
packageJson.dependencies = packageJson.dependencies || {};
Object.assign(packageJson.dependencies, dependencies);
console.log(chalk.blue("\u{1F4E6} Added dependencies:"), Object.keys(dependencies).join(", "));
}
if (Object.keys(devDependencies).length > 0) {
packageJson.devDependencies = packageJson.devDependencies || {};
Object.assign(packageJson.devDependencies, devDependencies);
console.log(chalk.blue("\u{1F527} Added dev dependencies:"), Object.keys(devDependencies).join(", "));
}
await fs2.writeJson(packageJsonPath, packageJson, { spaces: 2 });
console.log(chalk.green("\u2705 package.json updated"));
console.log(chalk.yellow("\u{1F4A1} Run npm install to install new dependencies"));
}
// src/commands/add.ts
async function addComponents(type, options) {
const validTypes = ["react-vanilla", "react-tailwind", "react-shadcn"];
if (!validTypes.includes(type)) {
throw new Error(`Invalid component type: ${type}. Valid types: ${validTypes.join(", ")}`);
}
const targetDir = path3.resolve(process.cwd(), options.dir);
if (await fs3.pathExists(targetDir)) {
const files = await fs3.readdir(targetDir);
if (files.length > 0 && !options.force) {
if (!options.yes) {
const { confirm } = await inquirer.prompt([
{
type: "confirm",
name: "confirm",
message: `Directory ${options.dir} already exists and contains files. Continue?`,
default: false
}
]);
if (!confirm) {
console.log(chalk2.yellow("Installation cancelled."));
return;
}
}
}
}
console.log(chalk2.blue(`\u{1F4E6} Installing ${type} components...`));
const registry = await getTemplateRegistry();
const template = registry[type];
if (!template) {
throw new Error(`Template not found for type: ${type}`);
}
await fs3.ensureDir(targetDir);
console.log(chalk2.blue("\u{1F4C4} Copying component files..."));
await copyTemplate(type, targetDir);
console.log(chalk2.blue("\u{1F4CB} Updating dependencies..."));
await updatePackageJson(template.dependencies, template.devDependencies);
await createComponentReadme(type, targetDir);
console.log(chalk2.green(`\u2705 ${type} components installed to ${options.dir}`));
showNextSteps(type);
}
async function createComponentReadme(type, targetDir) {
const readmeContent = `# FeedInbox Components
This directory contains FeedInbox ${type} components installed via the CLI.
## Usage
\`\`\`typescript
import { FeedbackWidget, NewsletterWidget, ContactWidget } from './'
import { FeedInboxSDK } from '@feedinbox/sdk'
// Initialize SDK
const feedinbox = new FeedInboxSDK({
apiKey: 'fb_your_api_key'
})
// Use components
<FeedbackWidget
apiKey="fb_your_api_key"
onSuccess={(response) => console.log(response)}
onError={(error) => console.error(error)}
/>
\`\`\`
## Customization
These components are copied into your project, so you can:
- Modify styling and behavior
- Add your own props and features
- Customize the UI to match your design system
- Update dependencies as needed
## Components Included
- **FeedbackWidget**: Collect user feedback with priority levels
- **NewsletterWidget**: Newsletter subscription with consent management
- **ContactWidget**: Contact form with validation
## Documentation
Visit https://feedinbox.com/docs for complete documentation.
`;
await fs3.writeFile(path3.join(targetDir, "README.md"), readmeContent);
}
function showNextSteps(type) {
console.log(chalk2.yellow("\n\u{1F4D6} Next steps:"));
console.log("1. Install the SDK if not already installed:");
console.log(chalk2.cyan(" npm install @feedinbox/sdk"));
if (type === "react-tailwind") {
console.log("2. Make sure Tailwind CSS is configured in your project");
console.log("3. Import and use the components");
} else if (type === "react-shadcn") {
console.log("2. Make sure shadcn/ui is configured in your project");
console.log("3. Install required shadcn components if not already installed");
console.log("4. Import and use the components");
} else {
console.log("2. Import the CSS file in your app:");
console.log(chalk2.cyan(' import "./components/feedinbox/styles.css"'));
console.log("3. Import and use the components");
}
console.log("4. Configure your API key and start collecting feedback!");
}
// src/commands/init.ts
import path4 from "path";
import fs4 from "fs-extra";
import chalk3 from "chalk";
import inquirer2 from "inquirer";
async function initConfig() {
const configPath = path4.join(process.cwd(), "feedinbox.config.json");
if (await fs4.pathExists(configPath)) {
const { overwrite } = await inquirer2.prompt([
{
type: "confirm",
name: "overwrite",
message: "FeedInbox config already exists. Overwrite?",
default: false
}
]);
if (!overwrite) {
console.log(chalk3.yellow("Configuration unchanged."));
return;
}
}
const answers = await inquirer2.prompt([
{
type: "input",
name: "apiKey",
message: "Enter your FeedInbox API key:",
validate: (input) => {
if (!input) return "API key is required";
if (!input.startsWith("fb_")) return 'API key must start with "fb_"';
return true;
}
},
{
type: "input",
name: "apiUrl",
message: "Enter your API URL (optional):",
default: "https://api.feedinbox.com"
},
{
type: "input",
name: "workspaceId",
message: "Enter your workspace ID (optional):"
}
]);
const config = {
apiKey: answers.apiKey,
apiUrl: answers.apiUrl,
workspaceId: answers.workspaceId || void 0,
version: "1.0.0",
createdAt: (/* @__PURE__ */ new Date()).toISOString()
};
await fs4.writeJson(configPath, config, { spaces: 2 });
console.log(chalk3.green("\u2705 Configuration saved to feedinbox.config.json"));
console.log(chalk3.yellow("\u26A0\uFE0F Make sure to add feedinbox.config.json to your .gitignore"));
}
// src/commands/list.ts
import chalk4 from "chalk";
function listTemplates() {
console.log(chalk4.blue("\n\u{1F4CB} Available FeedInbox component types:\n"));
console.log(chalk4.green("react-vanilla"));
console.log(" React components with vanilla CSS");
console.log(" \u2022 Clean, customizable styling");
console.log(" \u2022 No external CSS framework required");
console.log(" \u2022 Perfect for custom design systems\n");
console.log(chalk4.green("react-tailwind"));
console.log(" React components with Tailwind CSS");
console.log(" \u2022 Utility-first CSS approach");
console.log(" \u2022 Requires Tailwind CSS in your project");
console.log(" \u2022 Highly customizable with utilities\n");
console.log(chalk4.green("react-shadcn"));
console.log(" React components with shadcn/ui");
console.log(" \u2022 Modern component library");
console.log(" \u2022 Requires shadcn/ui setup");
console.log(" \u2022 Beautiful, accessible components\n");
console.log(chalk4.yellow("Usage:"));
console.log(" npx @feedinbox/cli add react-vanilla");
console.log(" npx @feedinbox/cli add react-tailwind");
console.log(" npx @feedinbox/cli add react-shadcn\n");
}
// src/utils/project.ts
import path5 from "path";
import fs5 from "fs-extra";
import chalk5 from "chalk";
async function validateProject() {
var _a, _b, _c, _d, _e;
const cwd = process.cwd();
const packageJsonPath = path5.join(cwd, "package.json");
if (!await fs5.pathExists(packageJsonPath)) {
throw new Error("No package.json found. Please run this command in a Node.js project directory.");
}
const packageJson = await fs5.readJson(packageJsonPath);
const hasReact = ((_a = packageJson.dependencies) == null ? void 0 : _a.react) || ((_b = packageJson.devDependencies) == null ? void 0 : _b.react) || ((_c = packageJson.peerDependencies) == null ? void 0 : _c.react);
if (!hasReact) {
console.log(chalk5.yellow("\u26A0\uFE0F React not detected in dependencies. Make sure this is a React project."));
}
const hasTypeScript = ((_d = packageJson.dependencies) == null ? void 0 : _d.typescript) || ((_e = packageJson.devDependencies) == null ? void 0 : _e.typescript) || await fs5.pathExists(path5.join(cwd, "tsconfig.json"));
if (!hasTypeScript) {
console.log(chalk5.yellow("\u26A0\uFE0F TypeScript not detected. FeedInbox components are written in TypeScript."));
}
console.log(chalk5.green("\u2705 Project validation passed"));
}
export {
addComponents,
getTemplateRegistry,
initConfig,
listTemplates,
validateProject
};
//# sourceMappingURL=index.mjs.map