From a270ee4ae5c1e9a050e73d460ee3b0ce16a24b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Fri, 17 Jan 2025 16:10:04 +0100 Subject: [PATCH] Create CLI working scaffolding --- package-lock.json | 35 +++++++++++++++++++++++----- package.json | 4 +++- package.nix | 2 +- src/cli/index.ts | 47 ++++++++++++++++++++++++++++++++++++++ src/config/index.ts | 8 +++++++ src/database/serializer.ts | 19 +++++++++------ src/index.ts | 5 ++++ src/runner/index.ts | 15 ++++++++++++ src/types/cli.ts | 6 +++++ src/types/config.ts | 15 ++++++++---- 10 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 src/cli/index.ts create mode 100644 src/config/index.ts create mode 100644 src/runner/index.ts create mode 100644 src/types/cli.ts diff --git a/package-lock.json b/package-lock.json index eb565df..a99ccc7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,10 @@ "version": "0.0.1", "license": "ISC", "dependencies": { + "commander": "^13.0.0", "dayjs": "^1.11.13", - "peggy": "^4.2.0" + "peggy": "^4.2.0", + "yaml": "^2.7.0" }, "bin": { "obsidian-tasks-reminder": "dist/index.js" @@ -566,13 +568,12 @@ } }, "node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.0.0.tgz", + "integrity": "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==", "license": "MIT", "engines": { - "node": "^12.20.0 || >=14" + "node": ">=18" } }, "node_modules/dayjs": { @@ -1053,6 +1054,16 @@ "tsc-alias": "dist/bin/index.js" } }, + "node_modules/tsc-alias/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/tsx": { "version": "4.19.2", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", @@ -1093,6 +1104,18 @@ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true, "license": "MIT" + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } } } } diff --git a/package.json b/package.json index 538ac12..461da2e 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,9 @@ "typescript": "^5.6.3" }, "dependencies": { + "commander": "^13.0.0", "dayjs": "^1.11.13", - "peggy": "^4.2.0" + "peggy": "^4.2.0", + "yaml": "^2.7.0" } } diff --git a/package.nix b/package.nix index 8f7cec5..cb6a4a0 100644 --- a/package.nix +++ b/package.nix @@ -7,5 +7,5 @@ buildNpmPackage { pname = "obsidian-tasks-reminder"; version = "0.0.1"; src = ./.; - npmDepsHash = "sha256-d8uZWYmroWoju976WXnCaYX+0uTLK/tc6hS/WgEHv/o="; + npmDepsHash = "sha256-ofPAFnHbW+M0uQN/OXDLK+PQ3ls6AtD58/AOmSxn754="; } diff --git a/src/cli/index.ts b/src/cli/index.ts new file mode 100644 index 0000000..8ace330 --- /dev/null +++ b/src/cli/index.ts @@ -0,0 +1,47 @@ +import { program } from "commander"; +import { CLIOptions } from "../types/cli"; + +import { loadConfig } from "../config"; +import { notify, scan } from "../runner"; + +const getOptions = () => program + .name("obsidian-tasks-reminder") + .version("0.0.1") + .requiredOption("-c, --config ", "sets the path to the YAML file with configuration") + .option("-x, --set ", "overrides the config option for this specific run (arg: =, i.e. backend.ntfy.enable=false", (v: string, prev: string[]) => prev.concat([v]), []) + .option("-s, --scan", "scans new tasks for future notifications and generates the database") + .option("-n, --notify", "reads the generated database and triggers notifications if any") + .parse() + .opts(); + + export const run = async () => { + const options = getOptions(); + const config = loadConfig(options.config); + + for (const override of options.set) { + const [path, value] = override.split("="); + + const segments = path.trim().split(".") + + let current: any = config; + segments.map(s => s.trim()).forEach((segment: string, idx) => { + if(!current[segment]) { + current[segment] = {}; + } + + if(idx === segments.length - 1) { + current[segment] = JSON.parse(value.trim()); + } + + current = current[segment]; + }); + } + + if (options.scan) { + scan(config); + } + + if (options.notify) { + notify(config); + } + } diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..88aad1d --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,8 @@ +import fs from "fs"; +import yaml from "yaml"; +import { Config } from "../types/config"; + +export function loadConfig(file: string): Config { + const text = fs.readFileSync(file, 'utf-8'); + return yaml.parse(text) as Config; +} \ No newline at end of file diff --git a/src/database/serializer.ts b/src/database/serializer.ts index 3775fb1..9d9ef45 100644 --- a/src/database/serializer.ts +++ b/src/database/serializer.ts @@ -5,7 +5,7 @@ import { Notification, NotificationDatabase } from "../types/notification"; /** * Applies the mapper for each task from list, groups them by time and dumps the data into JSON formatted file. */ -export function dumpDatabase(file: string, tasks: Task[], mapper: (task: Task) => Notification[]) { +export function dumpDatabase(file: string, tasks: Task[], mapper: string) { const data = serializeDatabase(tasks, mapper); fs.writeFileSync(file, data); } @@ -13,8 +13,10 @@ export function dumpDatabase(file: string, tasks: Task[], mapper: (task: Task) = /** * Applies the mapper for each task from list, groups them by time and serializes into JSON format. */ -export function serializeDatabase(tasks: Task[], mapper: (task: Task) => Notification[]): string { - const output = tasks.flatMap(wrapWithTimeFiller(mapper)).reduce((acc, n) => { +export function serializeDatabase(tasks: Task[], mapper: string): string { + const transformer = new Function("$", `return ${mapper}`) as (task: Task) => Notification; + + const output = tasks.map(wrapWithTimeFiller(transformer)).reduce((acc, n) => { if (n.time) { (acc[n.time] = (acc[n.time] || [])).push(n); }; @@ -25,10 +27,13 @@ export function serializeDatabase(tasks: Task[], mapper: (task: Task) => Notific return JSON.stringify(output); } -function wrapWithTimeFiller(mapper: (task: Task) => Notification[]): (task: Task) => Notification[] { - return (task: Task) => mapper(task) - .map(notification => ({ +function wrapWithTimeFiller(mapper: (task: Task) => Notification): (task: Task) => Notification { + return (task: Task) => { + const notification = mapper(task); + + return { ...notification, time: task.reminder ?? notification.time, - })); + } + }; } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index e69de29..5f78216 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +import { run } from "./cli"; + +run(); \ No newline at end of file diff --git a/src/runner/index.ts b/src/runner/index.ts new file mode 100644 index 0000000..059c998 --- /dev/null +++ b/src/runner/index.ts @@ -0,0 +1,15 @@ +import { loadTasks } from "../loader"; +import { dumpDatabase } from "../database/serializer"; +import { loadDatabase } from "../database/deserializer"; +import { remind } from "../backend"; +import { Config } from "../types/config"; + +export async function scan(config: Config) { + const tasks = await loadTasks(config.sources, config.query); + dumpDatabase(config.databaseFile, tasks, config.mapper); +} + +export async function notify(config: Config) { + const db = loadDatabase(config.databaseFile); + remind(config, db); +} \ No newline at end of file diff --git a/src/types/cli.ts b/src/types/cli.ts new file mode 100644 index 0000000..eb1544c --- /dev/null +++ b/src/types/cli.ts @@ -0,0 +1,6 @@ +export type CLIOptions = { + config: string; + scan: boolean; + notify: boolean; + set: string[]; +}; \ No newline at end of file diff --git a/src/types/config.ts b/src/types/config.ts index 1ffc95e..fb08a65 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,12 +1,17 @@ +export type Config = { + sources: string[]; + query: string; + mapper: string; + databaseFile: string; + backend: Record; +}; + export type BackendSettings = { enable?: boolean; }; + export type SupportedBackends = 'ntfy.sh'; export type BackendConfig = { backend: SupportedBackends; settings: BackendSettings; -} - -export type Config = { - backend: Record; -}; \ No newline at end of file +} \ No newline at end of file