Skip to content

Commit 4587d93

Browse files
author
Andrei
committed
wip: parser
1 parent 3f7bedc commit 4587d93

File tree

5 files changed

+151
-87
lines changed

5 files changed

+151
-87
lines changed

bun.lockb

7.75 KB
Binary file not shown.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"dependencies": {
1616
"chalk": "^5.3.0",
17+
"cheerio": "^1.0.0",
1718
"commander": "^12.1.0",
1819
"fs-extra": "^11.2.0",
1920
"inquirer": "^12.0.0",

src/index.ts

Lines changed: 9 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@ import inquirer from "inquirer";
55
import { Command } from "commander";
66
import logger from "./utils/logger"; // Assuming you have a logger utility
77
import open from "open"
8+
import Invoice from "./utils/invoice";
89

910
const program = new Command();
10-
const DEFAULT_PATH = path.join(os.homedir(), ".config", "icli/icli.json");
11-
const INVOICE_PATH = path.join(path.dirname(DEFAULT_PATH), "invoices.json");
1211

13-
// Utility function to check if config exists
12+
export const DEFAULT_DIR = path.join(os.homedir(), ".config", "icli/");
13+
export const DEFAULT_PATH = path.join(os.homedir(), ".config", "icli/icli.json");
14+
export const INVOICE_PATH = path.join(path.dirname(DEFAULT_PATH), "invoices.json");
15+
1416
export function configSetup(configPath: string) {
1517
return fs.existsSync(configPath);
1618
}
1719

18-
// Initialize configuration setup if not already present
1920
async function initConfig() {
2021
if (!configSetup(DEFAULT_PATH)) {
2122
const config = {
@@ -35,16 +36,6 @@ async function initConfig() {
3536
}
3637
}
3738

38-
// Function to handle viewing invoice history
39-
function viewInvoiceHistory() {
40-
if (!fs.existsSync(INVOICE_PATH)) {
41-
logger.info("No invoice history found.");
42-
} else {
43-
const invoiceHistory = fs.readJsonSync(INVOICE_PATH);
44-
logger.info("Invoice History:");
45-
console.log(invoiceHistory); // Display the history to the user
46-
}
47-
}
4839

4940
async function openConfigDirectory() {
5041
const configDir = path.dirname(DEFAULT_PATH);
@@ -58,73 +49,7 @@ async function openConfigDirectory() {
5849
}
5950

6051

61-
async function createInvoice() {
62-
const templatePath = path.dirname(DEFAULT_PATH);
63-
const templates = fs.readdirSync(templatePath).filter(file => file.endsWith(".html"));
64-
65-
// Check if templates are available
66-
if (templates.length === 0) {
67-
logger.error("No invoice templates found. Please ensure there are .html files in the templates directory.");
68-
return; // Exit the function early
69-
}
70-
71-
const answers = await inquirer.prompt([
72-
{
73-
type: "list",
74-
name: "template",
75-
message: "Choose an invoice template:",
76-
choices: templates,
77-
},
78-
{
79-
type: "input",
80-
name: "companyName",
81-
message: "Enter your company name:",
82-
},
83-
{
84-
type: "number",
85-
name: "numItems",
86-
message: "How many items do you want to add?",
87-
validate: value => value && value > 0 || "Must be at least 1 item",
88-
},
89-
]);
90-
91-
const items = [];
92-
for (let i = 0; i < answers.numItems; i++) {
93-
const itemDetails = await inquirer.prompt([
94-
{
95-
type: "input",
96-
name: "itemName",
97-
message: `Enter the name for item ${i + 1}:`,
98-
},
99-
{
100-
type: "number",
101-
name: "price",
102-
message: `Enter the price for item ${i + 1}:`,
103-
validate: value => value && value > 0 || "Price must be greater than zero",
104-
},
105-
]);
106-
items.push(itemDetails);
107-
}
108-
109-
const invoice = {
110-
template: answers.template,
111-
companyName: answers.companyName,
112-
items,
113-
createdAt: new Date().toISOString(),
114-
};
115-
116-
let invoiceHistory = [];
117-
if (fs.existsSync(INVOICE_PATH)) {
118-
invoiceHistory = fs.readJsonSync(INVOICE_PATH);
119-
}
120-
121-
invoiceHistory.push(invoice);
122-
fs.writeJsonSync(INVOICE_PATH, invoiceHistory, { spaces: 2 });
123-
124-
logger.info("Invoice successfully created!");
125-
}
12652

127-
// CLI setup with commander
12853
program
12954
.version("1.0.0")
13055
.description("Invoice CLI Tool");
@@ -141,15 +66,15 @@ program
14166
.command("history")
14267
.description("View invoice history")
14368
.action(() => {
144-
viewInvoiceHistory();
69+
new Invoice().viewHistory();
14570
mainMenu();
14671
});
14772

14873
program
14974
.command("create")
15075
.description("Create a new invoice")
15176
.action(async () => {
152-
await createInvoice();
77+
await new Invoice().createInvoice();
15378
mainMenu();
15479
});
15580

@@ -179,19 +104,17 @@ async function mainMenu() {
179104

180105
switch (choices.action) {
181106
case "View Invoice History":
182-
viewInvoiceHistory();
107+
new Invoice().viewHistory();
183108
break;
184109
case "Create a New Invoice":
185-
await createInvoice();
110+
await new Invoice().createInvoice()
186111
break;
187112
case "Exit CLI":
188113
logger.info("Exiting CLI...");
189114
process.exit(0);
190115
}
191116

192-
// Loop back to the main menu after completing an action
193117
mainMenu();
194118
}
195119

196-
// Initialize the CLI program
197120
program.parse(process.argv);

src/utils/html.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import logger from "./logger"
2+
import path from "path"
3+
import fs from "fs-extra"
4+
import { load } from "cheerio"
5+
6+
class HTML {
7+
private requiredIds: string[] = ["sefu"]
8+
9+
create(path: string) {
10+
return this.parser(path)
11+
}
12+
13+
async parser(filePath: string): Promise<boolean> {
14+
try {
15+
if (path.extname(filePath) !== '.html')
16+
throw new Error('file type')
17+
18+
const fileExists = await fs.pathExists(filePath);
19+
if (!fileExists)
20+
throw new Error('file non-existant')
21+
22+
const htmlContent = await fs.readFile(filePath, 'utf-8');
23+
24+
const $ = load(htmlContent);
25+
26+
const idsFound = $('[id]').map((_, element) => $(element).attr('id')).get();
27+
28+
const missingIds = this.requiredIds.filter(id => !idsFound.includes(id));
29+
30+
if (missingIds.length > 0 || !idsFound.length) {
31+
logger.error(`Missing IDs: ${missingIds.join(', ')}`);
32+
} else {
33+
logger.success('All required IDs are present in the HTML.');
34+
}
35+
36+
return true
37+
} catch (error) {
38+
logger.error(`An error occured`)
39+
40+
return false
41+
}
42+
}
43+
44+
}
45+
46+
export default HTML

src/utils/invoice.ts

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,99 @@
1+
import inquirer from "inquirer";
2+
import { INVOICE_PATH, DEFAULT_PATH, DEFAULT_DIR } from "..";
3+
import logger from "./logger";
4+
import fs from "fs-extra";
5+
import path from "path"
6+
import HTML from "./html";
7+
18
class Invoice {
2-
constructor() {}
9+
async createInvoice() {
10+
try {
11+
12+
const templatePath = path.dirname(DEFAULT_PATH);
13+
const templates = fs.readdirSync(templatePath).filter(file => file.endsWith(".html")).map(f => `${DEFAULT_DIR}${f}`)
14+
15+
logger.info(JSON.stringify(templates))
16+
17+
if (templates.length === 0) {
18+
logger.error("No invoice templates found. Please ensure there are .html files in the templates directory.");
19+
throw new Error("No templates.")
20+
}
21+
22+
let template = templates.length > 1 ? await inquirer.prompt([{
23+
type: "list",
24+
name: "template",
25+
message: "Choose an invoice template:",
26+
choices: templates,
27+
}]).then(r => r.template) : templates[0]
28+
29+
const html = await new HTML().create(template)
30+
if (!html) throw new Error('Invalid template')
31+
32+
const answers = await inquirer.prompt([
33+
{
34+
type: "input",
35+
name: "companyName",
36+
message: "Enter your company name:",
37+
},
38+
{
39+
type: "number",
40+
name: "numItems",
41+
message: "How many items do you want to add?",
42+
validate: value => value && value > 0 || "Must be at least 1 item",
43+
},
44+
]);
45+
46+
const items = [];
47+
for (let i = 0; i < answers.numItems; i++) {
48+
const itemDetails = await inquirer.prompt([
49+
{
50+
type: "input",
51+
name: "itemName",
52+
message: `Enter the name for item ${i + 1}:`,
53+
},
54+
{
55+
type: "number",
56+
name: "price",
57+
message: `Enter the price for item ${i + 1}:`,
58+
validate: value => value && value > 0 || "Price must be greater than zero",
59+
},
60+
]);
61+
items.push(itemDetails);
62+
}
63+
64+
const invoice = {
65+
template: template,
66+
companyName: answers.companyName,
67+
items,
68+
createdAt: new Date().toISOString(),
69+
};
70+
71+
let invoiceHistory = [];
72+
if (fs.existsSync(INVOICE_PATH)) {
73+
invoiceHistory = fs.readJsonSync(INVOICE_PATH);
74+
}
75+
76+
invoiceHistory.push(invoice);
77+
fs.writeJsonSync(INVOICE_PATH, invoiceHistory, { spaces: 2 });
78+
79+
logger.info("Invoice successfully created!");
80+
} catch (e) {
81+
82+
logger.error('Got an error')
83+
84+
}
85+
}
86+
87+
88+
viewHistory() {
89+
if (!fs.existsSync(INVOICE_PATH)) {
90+
logger.info("No invoice history found.");
91+
} else {
92+
const invoiceHistory = fs.readJsonSync(INVOICE_PATH);
93+
logger.info("Invoice History:");
94+
console.log(invoiceHistory);
95+
}
96+
}
397
}
498

599
export default Invoice;

0 commit comments

Comments
 (0)