diff --git a/Makefile b/Makefile index c5a355b..7ef3132 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,9 @@ -.PHONY: help prod dev clean update-npmjs-readme release publish +.PHONY: help build clean update-npmjs-readme release publish help: @echo "Available targets:" @echo " - help: Show this help message" - @echo " - prod: Build the project in production mode" - @echo " - dev: Build the project in development mode" + @echo " - build: Build the project" @echo " - clean: Remove build artifacts" @echo " - release: Create a new release version" @echo " - publish: Publish the new version created with the release target" @@ -12,17 +11,13 @@ help: node_modules: npm install -prod: node_modules - npm run prod - -dev: node_modules - npm run dev - chmod +x dist/wtc-dev.mjs +build: node_modules + npm run build clean: rm -r dist node_modules -release: prod +release: build @read -p "Enter version bump (patch, minor, major): " bump && \ version=$$(npm version $$bump | grep -oP "(?<=v)[^']+") && \ echo "Version $$version created. Run 'make publish' to push the changes and publish the package." diff --git a/README.adoc b/README.adoc index d012c0f..fc849c0 100644 --- a/README.adoc +++ b/README.adoc @@ -2,8 +2,6 @@ An interactive CLI tool to calculate work time. -image::img/demo.png[] - == Install You can run this in your terminal @@ -35,35 +33,15 @@ After installation, you should be able to run the program with wtc ---- -== Update - -To update, just run the install command again - == Rationale -Don't know if it's just me but calculating my working hours sometimes -can get difficult. Especially if you have flexible hours and you end up -starting at a weird time, f.ex 08:15. This combined with the fact that -I have to log my hours to many different tasks, at the end of the day -calculating all this can get very confusing. +This is a highly opinionated CLI tool I built for my specific needs. +In time, I will probable make it more generic and configurable. +For now, the following assumptions are made. -To alleviate my pains, I included the following features - -* Asks wether you already had lunch or not and accommodates this in the calculation -* Asks the hours that you already logged and calculates unlogged hours -* Calculates how much under/overtime you worked - -This is a highly opinionated tool I built for my specific needs. -There probably exists other tools to do the same task -(maybe even better) but I wanted something simple that fits for my -needs specifically. - -== Configuration file - -See the https://git.korhonen.cc/FunctionalHacker/work-time-calculator/src/branch/main/config/config.toml[default configuration file] -for more information on how to override configurations. +* You have an unpaid 30 minute lunch break == TODO -* [x] Configuration file for default settings and altering behaviour in interactive mode +* [ ] Configuration file for default settings and altering behaviour in interactive mode * [ ] Non-interactive mode with CLI arguments parsing diff --git a/bin/wtc b/bin/wtc index 408a2ea..525bc83 100755 --- a/bin/wtc +++ b/bin/wtc @@ -1,4 +1,2 @@ -#!/bin/sh - -DIR="$(dirname "$(readlink -f "$0")")" -node "$DIR/../dist/wtc.mjs" "$@" +#!/usr/bin/env node +import '../dist/main.js'; diff --git a/config/config-schema.json b/config/config-schema.json deleted file mode 100644 index 281af39..0000000 --- a/config/config-schema.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "unpaidLunchBreakDuration": { - "type": "string", - "description": "Comment out or remove if you don't have an unpaid lunch break or if you normally log your lunch break hours" - }, - "language": { - "type": "string", - "enum": ["en", "fi"], - "description": "The language of the application. Currently supported languages are English (en) and Finnish (fi)" - }, - "timestampFormat": { - "type": "string", - "description": "Time format used to display timestamps (started, stopped, etc.). Refer to the dayjs documentation on how to set this https://day.js.org/docs/en/display/format" - }, - "defaults": { - "type": "object", - "properties": { - "workDayDuration": { "type": "string", "description": "Your work day duration" }, - "startTime": { "type": "string", "description": "The time you start working" }, - "stopTime": { - "type": ["string", "null"], - "description": "The time you stop working. Can either be 'now' or a time" - }, - "hadLunch": { "type": "boolean", "description": "Wether you had lunch already or not" } - }, - "additionalProperties": false, - "description": "Default values for inputs" - }, - "askInput": { - "type": "object", - "properties": { - "workDayDuration": { - "type": "boolean", - "description": "Disable prompt for work day duration if set to false" - }, - "startTime": { "type": "boolean", "description": "Disable prompt for start time if set to false" }, - "stopTime": { "type": "boolean", "description": "Disable prompt for stop time if set to false" }, - "logged": { "type": "boolean", "description": "Disable prompt for logged time if set to false" } - }, - "additionalProperties": false, - "description": "Settings to disable prompts" - } - }, - "additionalProperties": false, - "description": "Work Time Calculator configuration file. Configuration file location: $XDG_CONFIG_HOME/wtc/config.toml, usually ~/.config/wtc/config.toml" -} diff --git a/config/config.toml b/config/config.toml deleted file mode 100644 index cc7ead1..0000000 --- a/config/config.toml +++ /dev/null @@ -1,43 +0,0 @@ -#:schema https://git.korhonen.cc/FunctionalHacker/work-time-calculator/raw/branch/main/config/config-schema.json - -# Work Time Calculator configuration file -# This is the default configuration. -# You can only partially override the config, -# any missing values will use the defaults described here. -# On Unix/Linux you can place your configuration file in $XDG_CONFIG_HOME/wtc/config.toml, -# usually ~/.config/wtc/config.toml -# For windows, I don't know. - -# Comment out or remove if you don't have an unpaid lunch break -# or if you normally log your lunch break hours -unpaidLunchBreakDuration = "00:30" - -# The language of the application. -# Currently supported languages are "en", "fi" -language = "en" - -# Time format used to display timestamps (started, stopped etc.) -# Refer to the dayjs documentation on how to set this https://day.js.org/docs/en/display/format -# For example, the finnish format would be "MM.DD.YYYY [kello] HH.mm" -timestampFormat = "YYYY-MM-DD HH:mm" - -# This section is for default values for inputs -[defaults] - -# Your work day duration -workDayDuration = "07:30" - -# The time you start working -startTime = "08:00" - -# The time you stop working. Can either be "now" or a time -stopTime = "now" - -# This section can be used to disable prompts for each -# of the questions. The default value will be used automatically -# if the setting is set to false -[askInput] -workDayDuration = true -startTime = true -stopTime = true -logged = true diff --git a/img/demo.png b/img/demo.png deleted file mode 100644 index 068b615..0000000 Binary files a/img/demo.png and /dev/null differ diff --git a/package-lock.json b/package-lock.json index a0226f1..3484b54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,34 +1,25 @@ { "name": "work-time-calculator", - "version": "1.0.2", + "version": "0.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "work-time-calculator", - "version": "1.0.2", + "version": "0.0.5", "license": "MIT", "dependencies": { "chalk": "^5.3.0", - "dayjs": "^1.11.10", - "iarna-toml-esm": "^3.0.5", - "xdg-basedir": "^5.1.0", - "yargs": "^17.7.2" + "dayjs": "^1.11.10" }, "bin": { "wtc": "bin/wtc" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^11.1.5", "@types/node": "^20.9.0", - "@types/yargs": "^17.0.31", "@typescript-eslint/eslint-plugin": "^6.10.0", "eslint-config-prettier": "^9.0.0", - "rollup": "^4.5.1", - "rollup-plugin-add-shebang": "^0.3.1", - "tslib": "^2.6.2" + "typescript": "^5.2.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -135,64 +126,6 @@ "dev": true, "peer": true }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -228,263 +161,6 @@ "node": ">= 8" } }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", - "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-builtin-module": "^3.2.1", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-terser": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", - "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", - "dev": true, - "dependencies": { - "serialize-javascript": "^6.0.1", - "smob": "^1.0.0", - "terser": "^5.17.4" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-typescript": { - "version": "11.1.5", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.5.tgz", - "integrity": "sha512-rnMHrGBB0IUEv69Q8/JGRD/n4/n6b3nfpufUu26axhUcboUzv/twfZU8fIBbTOphRAe0v8EyxzeDpKXqGHfyDA==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.14.0||^3.0.0||^4.0.0", - "tslib": "*", - "typescript": ">=3.7.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - }, - "tslib": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.5.tgz", - "integrity": "sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.5.1.tgz", - "integrity": "sha512-YaN43wTyEBaMqLDYeze+gQ4ZrW5RbTEGtT5o1GVDkhpdNcsLTnLRcLccvwy3E9wiDKWg9RIhuoy3JQKDRBfaZA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.5.1.tgz", - "integrity": "sha512-n1bX+LCGlQVuPlCofO0zOKe1b2XkFozAVRoczT+yxWZPGnkEAKTTYVOGZz8N4sKuBnKMxDbfhUsB1uwYdup/sw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.5.1.tgz", - "integrity": "sha512-QqJBumdvfBqBBmyGHlKxje+iowZwrHna7pokj/Go3dV1PJekSKfmjKrjKQ/e6ESTGhkfPNLq3VXdYLAc+UtAQw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.5.1.tgz", - "integrity": "sha512-RrkDNkR/P5AEQSPkxQPmd2ri8WTjSl0RYmuFOiEABkEY/FSg0a4riihWQGKDJ4LnV9gigWZlTMx2DtFGzUrYQw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.5.1.tgz", - "integrity": "sha512-ZFPxvUZmE+fkB/8D9y/SWl/XaDzNSaxd1TJUSE27XAKlRpQ2VNce/86bGd9mEUgL3qrvjJ9XTGwoX0BrJkYK/A==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.5.1.tgz", - "integrity": "sha512-FEuAjzVIld5WVhu+M2OewLmjmbXWd3q7Zcx+Rwy4QObQCqfblriDMMS7p7+pwgjZoo9BLkP3wa9uglQXzsB9ww==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.5.1.tgz", - "integrity": "sha512-f5Gs8WQixqGRtI0Iq/cMqvFYmgFzMinuJO24KRfnv7Ohi/HQclwrBCYkzQu1XfLEEt3DZyvveq9HWo4bLJf1Lw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.5.1.tgz", - "integrity": "sha512-CWPkPGrFfN2vj3mw+S7A/4ZaU3rTV7AkXUr08W9lNP+UzOvKLVf34tWCqrKrfwQ0NTk5GFqUr2XGpeR2p6R4gw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.5.1.tgz", - "integrity": "sha512-ZRETMFA0uVukUC9u31Ed1nx++29073goCxZtmZARwk5aF/ltuENaeTtRVsSQzFlzdd4J6L3qUm+EW8cbGt0CKQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.5.1.tgz", - "integrity": "sha512-ihqfNJNb2XtoZMSCPeoo0cYMgU04ksyFIoOw5S0JUVbOhafLot+KD82vpKXOurE2+9o/awrqIxku9MRR9hozHQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.5.1.tgz", - "integrity": "sha512-zK9MRpC8946lQ9ypFn4gLpdwr5a01aQ/odiIJeL9EbgZDMgbZjjT/XzTqJvDfTmnE1kHdbG20sAeNlpc91/wbg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.5.1.tgz", - "integrity": "sha512-5I3Nz4Sb9TYOtkRwlH0ow+BhMH2vnh38tZ4J4mggE48M/YyJyp/0sPSxhw1UeS1+oBgQ8q7maFtSeKpeRJu41Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -500,33 +176,12 @@ "undici-types": "~5.26.4" } }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", - "dev": true - }, "node_modules/@types/semver": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", "dev": true }, - "node_modules/@types/yargs": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.31.tgz", - "integrity": "sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", @@ -729,6 +384,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -767,6 +423,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "peer": true, "engines": { "node": ">=8" } @@ -775,6 +433,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -831,24 +491,6 @@ "node": ">=8" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -870,23 +512,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -897,13 +528,9 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true }, "node_modules/concat-map": { "version": "0.0.1", @@ -956,15 +583,6 @@ "dev": true, "peer": true }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -990,27 +608,6 @@ "node": ">=6.0.0" } }, - "node_modules/emitter-component": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", - "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1212,12 +809,6 @@ "node": ">=4.0" } }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -1357,37 +948,6 @@ "dev": true, "peer": true }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1474,26 +1034,6 @@ "node": ">=8" } }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/iarna-toml-esm": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/iarna-toml-esm/-/iarna-toml-esm-3.0.5.tgz", - "integrity": "sha512-CgeDbPohnFG827UoRaCqKxJ8idiIDZDWlcHf5hUReQnZ8jHnNnhN4QJFiY12fKvr0LvuDuKAimqQfrmQnacbtw==", - "dependencies": { - "stream": "^0.0.2" - } - }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -1548,33 +1088,6 @@ "dev": true, "peer": true }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1584,14 +1097,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1604,12 +1109,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1729,15 +1228,6 @@ "node": ">=10" } }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1888,12 +1378,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -1955,40 +1439,6 @@ } ] }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2025,59 +1475,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rollup": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.5.1.tgz", - "integrity": "sha512-0EQribZoPKpb5z1NW/QYm3XSR//Xr8BeEXU49Lc/mQmpmVVG5jPUVrpc2iptup/0WMrY9mzas0fxH+TjYvG2CA==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.5.1", - "@rollup/rollup-android-arm64": "4.5.1", - "@rollup/rollup-darwin-arm64": "4.5.1", - "@rollup/rollup-darwin-x64": "4.5.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.5.1", - "@rollup/rollup-linux-arm64-gnu": "4.5.1", - "@rollup/rollup-linux-arm64-musl": "4.5.1", - "@rollup/rollup-linux-x64-gnu": "4.5.1", - "@rollup/rollup-linux-x64-musl": "4.5.1", - "@rollup/rollup-win32-arm64-msvc": "4.5.1", - "@rollup/rollup-win32-ia32-msvc": "4.5.1", - "@rollup/rollup-win32-x64-msvc": "4.5.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-add-shebang": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-add-shebang/-/rollup-plugin-add-shebang-0.3.1.tgz", - "integrity": "sha512-tKONSgKoVw9Om1cp1CnAlPQ9nsHBzu8fInKObX3zT5KZVoAJtslD1aBL84lJuKLeh+L28dB26CBBeYT+doTMLg==", - "dev": true, - "dependencies": { - "magic-string": "^0.25.3", - "rollup-pluginutils": "^2.8.1" - } - }, - "node_modules/rollup-pluginutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", - "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", - "dev": true, - "dependencies": { - "estree-walker": "^0.6.1" - } - }, - "node_modules/rollup-pluginutils/node_modules/estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2101,26 +1498,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -2136,15 +1513,6 @@ "node": ">=10" } }, - "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2177,63 +1545,12 @@ "node": ">=8" } }, - "node_modules/smob": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/smob/-/smob-1.4.1.tgz", - "integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==", - "dev": true - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true - }, - "node_modules/stream": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", - "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", - "dependencies": { - "emitter-component": "^1.1.1" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -2267,36 +1584,6 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/terser": { - "version": "5.24.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", - "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", - "dev": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2328,12 +1615,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2365,7 +1646,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2406,22 +1686,6 @@ "node": ">= 8" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2429,56 +1693,12 @@ "dev": true, "peer": true }, - "node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 5a651e7..c9291cf 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "work-time-calculator", - "version": "1.0.2", + "version": "0.0.5", "description": "An interactive CLI tool to calculate work time", "license": "MIT", "repository": { @@ -13,13 +13,12 @@ "email": "wtc@functionalhacker.korhonen.cc" }, "type": "module", + "main": "src/main.ts", "bin": { "wtc": "bin/wtc" }, - "main": "dist/wtc.mjs", "scripts": { - "prod": "rollup -c ./rollup.prod.config.js", - "dev": "rollup -c ./rollup.dev.config.js" + "build": "tsc" }, "keywords": [ "work", @@ -28,22 +27,13 @@ ], "author": "Marko Korhonen ", "devDependencies": { - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^11.1.5", "@types/node": "^20.9.0", - "@types/yargs": "^17.0.31", "@typescript-eslint/eslint-plugin": "^6.10.0", "eslint-config-prettier": "^9.0.0", - "rollup": "^4.5.1", - "rollup-plugin-add-shebang": "^0.3.1", - "tslib": "^2.6.2" + "typescript": "^5.2.2" }, "dependencies": { "chalk": "^5.3.0", - "dayjs": "^1.11.10", - "iarna-toml-esm": "^3.0.5", - "xdg-basedir": "^5.1.0", - "yargs": "^17.7.2" + "dayjs": "^1.11.10" } } diff --git a/rollup.dev.config.js b/rollup.dev.config.js deleted file mode 100644 index 7d2e1fb..0000000 --- a/rollup.dev.config.js +++ /dev/null @@ -1,15 +0,0 @@ -import typescript from '@rollup/plugin-typescript'; -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import shebang from 'rollup-plugin-add-shebang'; - -/** @type {import('rollup').RollupOptions} */ -const config = { - input: 'src/main.ts', - output: { - format: 'esm', - file: 'dist/wtc-dev.mjs', - }, - plugins: [typescript(), nodeResolve({ exportConditions: ['node'] }), shebang({ include: 'dist/wtc-dev.mjs' })], -}; - -export default config; diff --git a/rollup.prod.config.js b/rollup.prod.config.js deleted file mode 100644 index 258f57b..0000000 --- a/rollup.prod.config.js +++ /dev/null @@ -1,15 +0,0 @@ -import typescript from '@rollup/plugin-typescript'; -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import terser from '@rollup/plugin-terser'; - -/** @type {import('rollup').RollupOptions} */ -const config = { - input: 'src/main.ts', - output: { - format: 'esm', - file: 'dist/wtc.mjs', - }, - plugins: [typescript(), nodeResolve({ exportConditions: ['node'] }), terser()], -}; - -export default config; diff --git a/src/config.ts b/src/config.ts deleted file mode 100644 index 02f288b..0000000 --- a/src/config.ts +++ /dev/null @@ -1,71 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { xdgConfig } from 'xdg-basedir'; -import {parse} from 'iarna-toml-esm'; -import { parseDuration, parseTimestamp } from './parse.js'; -import WtcConfig from './types/WtcConfig.js'; -import Language from './types/Language.js'; - -interface RawConfig extends Omit { - unpaidLunchBreakDuration: string; - defaults: { - workDayDuration: string; - startTime: string; - stopTime: string; - hadLunch: boolean; - }; -} - -const defaultConfig: RawConfig = { - language: Language.en, - timestampFormat: 'YYYY-MM-DD HH:mm', - unpaidLunchBreakDuration: '00:30', - defaults: { - workDayDuration: '07:30', - startTime: '08:00', - stopTime: 'now', - hadLunch: true, - }, - askInput: { - workDayDuration: true, - startTime: true, - stopTime: true, - logged: true, - }, -}; - -const getConfig = (): WtcConfig => { - const configDir = xdgConfig || path.join(process.env.HOME ?? './', '.config'); - const configFilePath = path.join(configDir, 'wtc', 'config.toml'); - - let configData: Partial; - if (fs.existsSync(configFilePath)) { - configData = parse(fs.readFileSync(configFilePath, 'utf8')) as unknown as RawConfig; - } else { - configData = defaultConfig; - } - - const { language, timestampFormat, unpaidLunchBreakDuration, defaults, askInput } = configData; - - return { - language: language ?? defaultConfig.language, - timestampFormat: timestampFormat ?? defaultConfig.timestampFormat, - unpaidLunchBreakDuration: !unpaidLunchBreakDuration - ? undefined - : parseDuration(unpaidLunchBreakDuration), - defaults: { - workDayDuration: parseDuration(defaults?.workDayDuration ?? defaultConfig.defaults.workDayDuration), - startTime: parseTimestamp(defaults?.startTime ?? defaultConfig.defaults.startTime), - stopTime: parseTimestamp(defaults?.stopTime ?? defaultConfig.defaults.stopTime), - hadLunch: defaults?.hadLunch ?? defaultConfig.defaults.hadLunch, - }, - askInput: { - workDayDuration: askInput?.workDayDuration ?? defaultConfig.askInput.workDayDuration, - startTime: askInput?.startTime ?? defaultConfig.askInput.startTime, - stopTime: askInput?.stopTime ?? defaultConfig.askInput.stopTime, - logged: askInput?.logged ?? defaultConfig.askInput.logged, - }, - }; -}; - -export default getConfig; diff --git a/src/dayjs.ts b/src/dayjs.ts deleted file mode 100644 index 7626024..0000000 --- a/src/dayjs.ts +++ /dev/null @@ -1,10 +0,0 @@ -import dayjs, {Dayjs} from 'dayjs/esm'; -import duration, {Duration} from 'dayjs/esm/plugin/duration'; -import customParseFormat from 'dayjs/esm/plugin/customParseFormat'; - -dayjs.extend(duration); -dayjs.extend(customParseFormat); - -export default dayjs; - -export type {Dayjs, Duration}; diff --git a/src/format.ts b/src/format.ts index b097907..3be9bb4 100644 --- a/src/format.ts +++ b/src/format.ts @@ -1,43 +1,28 @@ -import dayjs, { Dayjs } from './dayjs'; +import dayjs, { Dayjs } from 'dayjs'; import { Duration } from 'dayjs/plugin/duration.js'; -import Language from './types/Language'; -import { MessageKey, message } from './i18n'; + +export const formatTimestamp = (timestamp: Dayjs): string => timestamp.format('YYYY-MM-DD HH:mm'); export const formatTime = (time: Dayjs): string => time.format('HH:mm'); -export const formatDuration = - (language: Language) => - (duration?: Duration, short?: boolean): string => { - duration = duration ?? dayjs.duration(0, 'minutes'); - if (duration.hours() === 0 && duration.minutes() === 0) { - return 'none'; - } +export const formatDuration = (duration: Duration, short?: boolean): string => { + if (duration.hours() === 0 && duration.minutes() === 0) { + return 'none'; + } - let formatString; + const formatString = short + ? 'HH:mm' + : duration.hours() > 0 && duration.minutes() > 0 + ? `H [hour${duration.hours() > 1 ? 's' : ''} and] m [minute${duration.minutes() > 1 ? 's' : ''}]` + : duration.hours() > 0 + ? `H [hour${duration.hours() > 1 ? 's' : ''}]` + : `m [minute${duration.minutes() > 1 ? 's' : ''}]`; - if (short) { - formatString = 'HH:mm'; - } else if (language === Language.fi) { - formatString = - duration.hours() > 0 && duration.minutes() > 0 - ? `H [tunti${duration.hours() > 1 ? 'a' : ''} ja] m [minuuttia]` - : duration.hours() > 0 - ? `H [tunti${duration.hours() > 1 ? 'a' : ''}]` - : 'm [minuuttia]'; - } else { - formatString = - duration.hours() > 0 && duration.minutes() > 0 - ? `H [hour${duration.hours() > 1 ? 's' : ''} and] m [minute${duration.minutes() > 1 ? 's' : ''}]` - : duration.hours() > 0 - ? `H [hour${duration.hours() > 1 ? 's' : ''}]` - : `m [minute${duration.minutes() > 1 ? 's' : ''}]`; - } + return duration.format(formatString); +}; - return duration.format(formatString); - }; - -export const getHoursRoundedStr = (language: Language) => (duration: Duration) => - `(${getHoursRounded(duration)} ${message(language)(MessageKey.hoursRounded)})`; +export const getHoursRoundedStr = (duration: Duration) => + `(${getHoursRounded(duration)} as hours rounded to next even 15 minutes)`; const getHoursRounded = (duration: Duration) => { // Round up to the next multiple of 15 diff --git a/src/i18n.ts b/src/i18n.ts deleted file mode 100644 index 074e43b..0000000 --- a/src/i18n.ts +++ /dev/null @@ -1,150 +0,0 @@ -import Language from './types/Language'; - -export enum MessageKey { - cliHelp, - cliVersion, - promptWorkDayDuration, - excludingLunch, - promptStartTime, - promptStopTime, - parseTimeFailed, - startTimeBeforeStopTimeError, - promptLunchBreak, - promptYesNoYes, - promptYesNoNo, - unpaidLunch, - promptLogged, - none, - startedWorking, - stoppedWorking, - workedToday, - loggedOver, - workedOvertime, - workLeft, - hoursCalculated, - klo, - unloggedToday, - hoursRounded, -} - -const messages: Record> = { - [MessageKey.cliHelp]: { - [Language.en]: 'Show this help', - [Language.fi]: 'Näytä tämä ohje', - }, - [MessageKey.cliVersion]: { - [Language.en]: 'Show program version', - [Language.fi]: 'Näytä ohjelman versio', - }, - [MessageKey.promptWorkDayDuration]: { - [Language.en]: 'How long is your work day today{0}? [{1}]: ', - [Language.fi]: 'Kuinka pitkä työpäiväsi on tänään{0}? [{1}]: ', - }, - [MessageKey.excludingLunch]: { - [Language.en]: ', excluding the lunch break', - [Language.fi]: ', poisluettuna lounastauko', - }, - [MessageKey.promptStartTime]: { - [Language.en]: 'What time did you start work today? [{0}]: ', - [Language.fi]: 'Mihin aikaan aloitit työskentelyn tänään? [{0}]: ', - }, - [MessageKey.promptStopTime]: { - [Language.en]: "What time did you stop working? If you didn't stop yet, leave this empty: ", - [Language.fi]: 'Mihin aikaan lopetit työskentelyn? Jos et lopettanut vielä, jätä tämä tyhjäksi: ', - }, - [MessageKey.parseTimeFailed]: { - [Language.en]: 'Failed to parse time "{0}", using default value "{1}"', - [Language.fi]: 'Ajan "{0}" parsiminen epäonnistui, käytetään oletusasetusta "{1}"', - }, - [MessageKey.startTimeBeforeStopTimeError]: { - [Language.en]: 'Start time ({0}) needs to be before stop time ({1}). Exiting', - [Language.fi]: 'Aloitusaika ({0}) pitää olla ennen lopetusaikaa ({1}). Ohjelma sammuu', - }, - [MessageKey.promptLunchBreak]: { - [Language.en]: 'Did you have a lunch break? [{0}]: ', - [Language.fi]: 'Piditkö jo lounastauon? [{0}]: ', - }, - [MessageKey.promptYesNoYes]: { - [Language.en]: 'Y/n', - [Language.fi]: 'K/e', - }, - [MessageKey.promptYesNoNo]: { - [Language.en]: 'y/N', - [Language.fi]: 'k/E', - }, - [MessageKey.unpaidLunch]: { - [Language.en]: 'Unpaid lunch:', - [Language.fi]: 'Palkaton lounas:', - }, - [MessageKey.promptLogged]: { - [Language.en]: 'How many hours did you log already? [00:00] ', - [Language.fi]: 'Kuinka monta tuntia kirjasit jo? [00:00] ', - }, - [MessageKey.none]: { - [Language.en]: 'None', - [Language.fi]: 'Ei yhtään', - }, - [MessageKey.startedWorking]: { - [Language.en]: 'Started working:', - [Language.fi]: 'Aloitit työskentelyn:', - }, - [MessageKey.stoppedWorking]: { - [Language.en]: 'Stopped working', - [Language.fi]: 'Lopetit työskentelyn', - }, - [MessageKey.hoursCalculated]: { - [Language.en]: 'Hours calculated', - [Language.fi]: 'Tunnit laskettu', - }, - [MessageKey.workedToday]: { - [Language.en]: 'Worked today:', - [Language.fi]: 'Tänään työskennelty:', - }, - [MessageKey.workedOvertime]: { - [Language.en]: 'You worked {0} overtime today', - [Language.fi]: 'Olet tehnyt {0} ylitöitä tänään', - }, - [MessageKey.loggedOver]: { - [Language.en]: 'You have logged {0} more than you worked today!', - [Language.fi]: 'Olet kirjannut {0} enemmän kuin olet työskennellyt!', - }, - [MessageKey.workLeft]: { - [Language.en]: 'You still have to work {0} more today', - [Language.fi]: 'Sinun pitää työskennellä tänään vielä {0} lisää', - }, - [MessageKey.klo]: { - [Language.en]: 'at', - [Language.fi]: 'klo', - }, - [MessageKey.unloggedToday]: { - [Language.en]: 'Unlogged today:', - [Language.fi]: 'Kirjaamattomia tänään:', - }, - [MessageKey.hoursRounded]: { - [Language.en]: 'as hours rounded to next even 15 minutes', - [Language.fi]: 'tunteina pyöristettynä seuraavaan 15 minuuttiin', - }, -}; - -/** - * Get a function to fetch messages for a given language - * @param language The language to get the messages for - */ -export const message = - (language: Language) => - /** - * Get a message for a fiven key - * @param key The key of the message - */ - (key: keyof typeof messages, ...params: string[]) => { - let result = messages[key][language]; - if (!result) { - throw `Unknown language: ${language}`; - } - - // Replace parameters in the template - for (let i = 0; i < params.length; i++) { - result = result.replace(new RegExp(`\\{${i}\\}`, 'g'), params[i]); - } - return result; - }; diff --git a/src/input.ts b/src/input.ts deleted file mode 100644 index 386060f..0000000 --- a/src/input.ts +++ /dev/null @@ -1,146 +0,0 @@ -import chalk from 'chalk'; -import { parseDuration, parseTimestamp } from './parse'; -import * as readline from 'readline/promises'; -import { formatDuration, formatTime } from './format'; -import { Dayjs } from 'dayjs'; -import { WtcPromptResult } from './types/WtcPromptResult'; -import { WtcRuntimeConfig } from './types/WtcConfig'; -import { MessageKey } from './i18n'; -import dayjs, { Duration } from './dayjs'; - -const { error } = console; - -const input = async (runtimeCfg: WtcRuntimeConfig): Promise => { - const { config, msg } = runtimeCfg; - const fmtDuration = formatDuration(config.language); - const { defaults, askInput, unpaidLunchBreakDuration: lunchBreakDuration } = config; - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - let startedAt: Dayjs | undefined = undefined; - let stoppedAt: Dayjs | undefined = undefined; - let stoppedWorking = false; - - try { - // Get work day duration - let workDayDuration: Duration | undefined = undefined; - - if (askInput.workDayDuration) { - const durationAnswer = await rl.question( - msg( - MessageKey.promptWorkDayDuration, - config.unpaidLunchBreakDuration ? msg(MessageKey.excludingLunch) : '', - fmtDuration(defaults.workDayDuration, true), - ), - ); - if (durationAnswer !== '') { - workDayDuration = parseDuration(durationAnswer); - if (workDayDuration.asMinutes() <= 0) { - error( - chalk.red( - msg(MessageKey.parseTimeFailed, durationAnswer, fmtDuration(defaults.workDayDuration)), - ), - ); - workDayDuration = undefined; - } - } - } - - if (!workDayDuration) { - workDayDuration = defaults.workDayDuration; - } - - if (askInput.startTime) { - const startTimeAnswer = await rl.question(msg(MessageKey.promptStartTime, formatTime(defaults.startTime))); - if (startTimeAnswer !== '') { - startedAt = parseTimestamp(startTimeAnswer); - if (!startedAt.isValid()) { - error(chalk.red(msg(MessageKey.parseTimeFailed, startTimeAnswer, formatTime(defaults.startTime)))); - } - } - } - - if (!startedAt?.isValid()) { - startedAt = defaults.startTime; - } - - if (askInput.stopTime) { - const stoppedAnswer = await rl.question(msg(MessageKey.promptStopTime, formatTime(defaults.stopTime))); - - if (stoppedAnswer !== '') { - stoppedWorking = true; - stoppedAt = parseTimestamp(stoppedAnswer); - if (!stoppedAt.isValid()) { - error(chalk.red(msg(MessageKey.parseTimeFailed, stoppedAnswer, formatTime(defaults.stopTime)))); - } - } - } - - if (!stoppedAt) { - stoppedAt = defaults.stopTime; - } - - if (stoppedAt.isSame(startedAt) || stoppedAt.isBefore(startedAt)) { - error( - chalk.red(msg(MessageKey.startTimeBeforeStopTimeError, formatTime(startedAt), formatTime(stoppedAt))), - ); - process.exit(1); - } - - let worked = dayjs.duration(stoppedAt.diff(startedAt)); - - let hadLunch = false; - if (lunchBreakDuration) { - const lunchAnswer = ( - await rl.question( - msg( - MessageKey.promptLunchBreak, - msg(config.defaults.hadLunch ? MessageKey.promptYesNoYes : MessageKey.promptYesNoNo), - ), - ) - ).toLowerCase(); - - if ( - lunchAnswer === 'y' || - lunchAnswer === 'k' || - (config.defaults.hadLunch && lunchAnswer !== 'n' && lunchAnswer !== 'e') - ) { - hadLunch = true; - worked = worked.subtract(lunchBreakDuration); - } - } - - // Calculate unlogged time - let loggedAnswer = await rl.question(msg(MessageKey.promptLogged)); - if (loggedAnswer === '') { - loggedAnswer = '00:00'; - } - const logged = parseDuration(loggedAnswer); - const unLogged = worked.subtract(logged); - const workLeft = workDayDuration.subtract(worked); - const workLeftMinutes = workLeft.asMinutes(); - let workedOvertime: Duration | undefined; - - if (workLeftMinutes < 0) { - workedOvertime = dayjs.duration(Math.round(workLeftMinutes * -1), 'minutes'); - } - - return { - logged, - unLogged, - startedAt, - stoppedAt, - stoppedWorking, - hadLunch, - worked, - workLeft, - workedOvertime, - }; - } finally { - rl.close(); - } -}; - -export default input; diff --git a/src/main.ts b/src/main.ts index 6e3b054..563d872 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,38 +1,150 @@ -import yargs from 'yargs'; -import { hideBin } from 'yargs/helpers'; -import ui from './ui.js'; -import update from './update.js'; -import getConfig from './config.js'; -import { MessageKey, message } from './i18n.js'; -import { WtcRuntimeConfig } from './types/WtcConfig.js'; +import chalk from 'chalk'; +import dayjs, { Dayjs } from 'dayjs'; +import * as readline from 'readline/promises'; +import { formatDuration, formatTime, formatTimestamp, getHoursRoundedStr } from './format.js'; +import duration, { Duration } from 'dayjs/plugin/duration.js'; +import { parseDuration, parseTimestamp } from './parse.js'; -// Build runtime config -const config = getConfig(); -const msg = message(config.language); -const runtimeConfig: WtcRuntimeConfig = { - config, - msg, +dayjs.extend(duration); + +const { log, error } = console; +const defaultStartTime = '08:00'; +const lunchBreakDuration = dayjs.duration({ minutes: 30 }); +const defaultWorkDayDuration = dayjs.duration({ hours: 7, minutes: 30 }); + +const main = async () => { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + let startedAt: Dayjs | undefined = undefined; + const now = dayjs(); + + try { + // Get work day duration + let workDayDuration: Duration | undefined = undefined; + + const durationAnswer = await rl.question( + `How long is your work day today, excluding the lunch break? [${formatDuration( + defaultWorkDayDuration, + true, + )}] `, + ); + if (durationAnswer !== '') { + workDayDuration = parseDuration(durationAnswer); + if (workDayDuration.asMinutes() <= 0) { + error( + chalk.red( + `Failed to parse ${durationAnswer} to duration, using default work day duration ${defaultWorkDayDuration}`, + ), + ); + workDayDuration = undefined; + } + } + + if (!workDayDuration) { + workDayDuration = defaultWorkDayDuration; + } + + // Calculate worked time + const startTimeAnswer = await rl.question(`What time did you start work today? [${defaultStartTime}] `); + if (startTimeAnswer !== '') { + startedAt = parseTimestamp(startTimeAnswer); + if (!startedAt.isValid()) { + error( + chalk.red( + `Failed to parse ${startTimeAnswer} to time, using default start time ${defaultStartTime}`, + ), + ); + } + } + + if (!startedAt?.isValid()) { + startedAt = parseTimestamp(defaultStartTime); + } + + let stoppedWorking = false; + let stoppedAt: Dayjs | undefined = undefined; + const stoppedAnswer = await rl.question( + `What time did you stop working (default is current time if you didn't stop yet)? [${formatTime(now)}] `, + ); + + if (stoppedAnswer === '') { + stoppedAt = now; + } else { + stoppedWorking = true; + stoppedAt = parseTimestamp(stoppedAnswer); + if (!stoppedAt.isValid()) { + error(`Failed to parse ${stoppedAnswer} to time, using current time`); + stoppedAt = dayjs(); + } + } + + if (stoppedAt.isSame(startedAt) || stoppedAt.isBefore(startedAt)) { + error( + chalk.red( + `Start time (${formatTime(startedAt)}) needs to be before stop time (${formatTime( + stoppedAt, + )}). Exiting`, + ), + ); + process.exit(1); + } + + let worked = dayjs.duration(stoppedAt.diff(startedAt)); + + if ((await rl.question('Did you have a lunch break? [Y/n] ')).toLowerCase() !== 'n') { + worked = worked.subtract(lunchBreakDuration); + } + + // Calculate unlogged time + let loggedAnswer = await rl.question('How many hours did you log already? [00:00] '); + if (loggedAnswer === '') { + loggedAnswer = '00:00'; + } + const logged = parseDuration(loggedAnswer); + let unLogged: Duration | undefined = undefined; + + if (logged.asMinutes() === worked.asMinutes()) { + unLogged = worked.subtract(logged); + } else { + unLogged = worked.subtract(logged); + } + + // Log result + log(); + log('Started working at:', formatTimestamp(startedAt)); + log((stoppedWorking ? 'Stopped working' : 'Hours calculated') + ' at:', formatTimestamp(stoppedAt)); + log('Worked today:', chalk.green(formatDuration(worked)), chalk.yellow(getHoursRoundedStr(worked))); + + if (unLogged.asMinutes() == 0) { + log('Unlogged today:', chalk.green('none')); + } else if (unLogged.asMinutes() > 0) { + log('Unlogged today:', chalk.red(formatDuration(unLogged)), chalk.yellow(getHoursRoundedStr(unLogged))); + } else if (unLogged.asMinutes() < 0) { + log( + chalk.red(`You have logged ${formatDuration(unLogged)} more than you worked today!`), + chalk.yellow(getHoursRoundedStr(unLogged)), + ); + } + + const workLeft = workDayDuration.subtract(worked); + const workLeftMinutes = workLeft.asMinutes(); + if (workLeftMinutes > 0) { + log('You still have to work', chalk.green(formatDuration(workLeft)), 'more today'); + } else if (workLeft.asMinutes() < 0) { + log( + 'You worked', + chalk.green( + formatDuration(dayjs.duration({ minutes: Math.round(workLeftMinutes * -1) })), + 'overtime today', + ), + ); + } + } finally { + rl.close(); + } }; -// Process args. Yargs will exit if it detects help or version -const args = await yargs(hideBin(process.argv)) - .usage('Work time calculator') - .alias('help', 'h') - .alias('version', 'v') - .options({ - help: { - description: msg(MessageKey.cliHelp), - }, - version: { - description: msg(MessageKey.cliVersion), - }, - }).argv; - -// Run updater if requested -if (args.update) { - update(); - process.exit(0); -} - -// Run UI if no arguments -ui(runtimeConfig); +main(); diff --git a/src/output.ts b/src/output.ts deleted file mode 100644 index 86e0619..0000000 --- a/src/output.ts +++ /dev/null @@ -1,49 +0,0 @@ -import chalk from 'chalk'; -import { formatDuration, getHoursRoundedStr } from './format'; -import { WtcPromptResult } from './types/WtcPromptResult'; -import { MessageKey } from './i18n.js'; -import { WtcRuntimeConfig } from './types/WtcConfig'; -import dayjs from './dayjs'; - -const { log } = console; - -const output = (result: WtcPromptResult, runtimeCfg: WtcRuntimeConfig) => { - const {config, msg} = runtimeCfg; - const { language, timestampFormat } = config; - const fmtDuration = formatDuration(language); - const hoursRounded = getHoursRoundedStr(language); - const { startedAt, stoppedAt, stoppedWorking, worked, unLogged, workLeft, workedOvertime, hadLunch } = result; - - log(); - log(msg(MessageKey.startedWorking), startedAt.format(timestampFormat)); - log( - (stoppedWorking ? msg(MessageKey.stoppedWorking) : msg(MessageKey.hoursCalculated)) + - ` ${msg(MessageKey.klo)}:`, - stoppedAt.format(timestampFormat), - ); - log(msg(MessageKey.workedToday), chalk.green(fmtDuration(worked)), chalk.yellow(hoursRounded(worked))); - - if (hadLunch) { - log(msg(MessageKey.unpaidLunch), chalk.green(fmtDuration(config.unpaidLunchBreakDuration))); - } - - const unLoggedMinutes = unLogged.asMinutes(); - if (unLoggedMinutes >= 0) { - log( - msg(MessageKey.unloggedToday), - unLoggedMinutes === 0 ? chalk.green(msg(MessageKey.none)) : chalk.red(fmtDuration(unLogged)), - unLoggedMinutes === 0 ? '' : chalk.yellow(hoursRounded(unLogged)), - ); - } else if (unLoggedMinutes < 0) { - const overLogged = dayjs.duration(Math.abs(unLogged.asMilliseconds()), 'milliseconds'); - log(chalk.red(msg(MessageKey.loggedOver, fmtDuration(overLogged)))); - } - - if (workLeft.asMinutes() > 0) { - log(msg(MessageKey.workLeft, chalk.green(fmtDuration(workLeft)))); - } else if (workedOvertime) { - log(msg(MessageKey.workedOvertime, chalk.green(fmtDuration(workedOvertime)))); - } -}; - -export default output; diff --git a/src/parse.ts b/src/parse.ts index 7d723fb..a3cab22 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -1,6 +1,11 @@ -import dayjs, { Dayjs, Duration } from './dayjs'; +import dayjs, { Dayjs } from 'dayjs'; +import customParseFormat from 'dayjs/plugin/customParseFormat.js'; +import duration, { Duration } from 'dayjs/plugin/duration.js'; -export const parseTimestamp = (time: string): Dayjs => (time === 'now' ? dayjs() : dayjs(time, 'HH:mm', true)); +dayjs.extend(customParseFormat); +dayjs.extend(duration); + +export const parseTimestamp = (time: string): Dayjs => dayjs(time, 'HH:mm', true); export const parseDuration = (time: string): Duration => { const [hours, minutes] = time.split(':').map(Number); diff --git a/src/types/Language.ts b/src/types/Language.ts deleted file mode 100644 index 3dcd6d4..0000000 --- a/src/types/Language.ts +++ /dev/null @@ -1,6 +0,0 @@ -enum Language { - en = 'en', - fi = 'fi', -} - -export default Language; diff --git a/src/types/WtcConfig.ts b/src/types/WtcConfig.ts deleted file mode 100644 index 7d822b8..0000000 --- a/src/types/WtcConfig.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Dayjs } from 'dayjs'; -import { Duration } from 'dayjs/plugin/duration.js'; -import Language from './Language.js'; -import { message } from '../i18n.js'; - -export default interface WtcConfig { - language: Language, - timestampFormat: string, - unpaidLunchBreakDuration?: Duration; - defaults: { - workDayDuration: Duration; - startTime: Dayjs; - stopTime: Dayjs; - hadLunch: boolean; - }; - askInput: { - workDayDuration: boolean; - startTime: boolean; - stopTime: boolean; - logged: boolean; - }; -} - -/** Config and current language msg function together */ -export interface WtcRuntimeConfig { - config: WtcConfig; - msg: ReturnType; -} - diff --git a/src/types/WtcPromptResult.ts b/src/types/WtcPromptResult.ts deleted file mode 100644 index 9e0f269..0000000 --- a/src/types/WtcPromptResult.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Dayjs } from 'dayjs'; -import { Duration } from 'dayjs/plugin/duration'; - -export interface WtcPromptResult { - startedAt: Dayjs; - stoppedAt: Dayjs; - stoppedWorking: boolean; - logged: Duration; - unLogged: Duration; - hadLunch: boolean; - worked: Duration; - workLeft: Duration; - workedOvertime?: Duration; -} diff --git a/src/ui.ts b/src/ui.ts deleted file mode 100644 index 9d792fe..0000000 --- a/src/ui.ts +++ /dev/null @@ -1,7 +0,0 @@ -import input from './input.js'; -import output from './output.js'; -import { WtcRuntimeConfig } from './types/WtcConfig.js'; - -const ui = async (config: WtcRuntimeConfig) => output(await input(config), config); - -export default ui; diff --git a/src/update.ts b/src/update.ts deleted file mode 100644 index 92ad114..0000000 --- a/src/update.ts +++ /dev/null @@ -1,7 +0,0 @@ -const { log } = console; - -const update = () => { - log('update'); -}; - -export default update;