From 536aaa01b0b6085b12c842a6d1f314c3cb0468e8 Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Thu, 23 Nov 2023 19:57:46 +0200 Subject: [PATCH 01/13] Add update instructions --- README.adoc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.adoc b/README.adoc index 8b37bc7..326a621 100644 --- a/README.adoc +++ b/README.adoc @@ -35,6 +35,16 @@ After installation, you should be able to run the program with wtc ---- +== Update + +The easiest way to update is to first remove the program and then install again + +[,shell] +---- +npm r -g work-time-calculator +npm i -g work-time-calculator +---- + == Rationale Don't know if it's just me but calculating my working hours sometimes From 6bbfd257454de8d2066c2c9c3c8bd99239cd358d Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Thu, 23 Nov 2023 20:24:35 +0200 Subject: [PATCH 02/13] Fix running on older nodejs versions The shebang does not work without extension --- Makefile | 1 - README.adoc | 8 +------- bin/wtc | 4 ++++ package.json | 3 +-- rollup.config.js | 5 ++--- 5 files changed, 8 insertions(+), 13 deletions(-) create mode 100755 bin/wtc diff --git a/Makefile b/Makefile index a1f0c68..7ef3132 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,6 @@ node_modules: build: node_modules npm run build - chmod +x dist/wtc clean: rm -r dist node_modules diff --git a/README.adoc b/README.adoc index 326a621..d012c0f 100644 --- a/README.adoc +++ b/README.adoc @@ -37,13 +37,7 @@ wtc == Update -The easiest way to update is to first remove the program and then install again - -[,shell] ----- -npm r -g work-time-calculator -npm i -g work-time-calculator ----- +To update, just run the install command again == Rationale diff --git a/bin/wtc b/bin/wtc new file mode 100755 index 0000000..bf01370 --- /dev/null +++ b/bin/wtc @@ -0,0 +1,4 @@ +#!/bin/sh + +DIR="$(dirname "$(readlink -f "$0")")" +node "$DIR/../dist/wtc.js" "$@" diff --git a/package.json b/package.json index 0171f14..a7461f9 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "main": "src/main.ts", "type": "module", "bin": { - "wtc": "dist/wtc" + "wtc": "bin/wtc" }, "scripts": { "build": "rollup -c" @@ -34,7 +34,6 @@ "@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" }, "dependencies": { diff --git a/rollup.config.js b/rollup.config.js index 0aef700..c109771 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,15 +1,14 @@ import typescript from '@rollup/plugin-typescript'; import terser from '@rollup/plugin-terser'; -import shebang from 'rollup-plugin-add-shebang'; /** @type {import('rollup').RollupOptions} */ const config = { input: 'src/main.ts', output: { format: 'esm', - file: 'dist/wtc', + file: 'dist/wtc.js', }, - plugins: [typescript(), terser(), shebang({ include: 'dist/wtc' })], + plugins: [typescript(), terser()], external: [ '@iarna/toml', 'chalk', From a2e53e32a616cc028f564b48e329bf772790a77d Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Thu, 23 Nov 2023 20:25:46 +0200 Subject: [PATCH 03/13] 1.0.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3bbb2b4..534df9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "work-time-calculator", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "work-time-calculator", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/package.json b/package.json index a7461f9..94dbbb5 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.0", + "version": "1.0.1", "description": "An interactive CLI tool to calculate work time", "license": "MIT", "repository": { From acb07d0cdc647d7a3cd615420c71fc6ae44627bc Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Thu, 23 Nov 2023 20:31:05 +0200 Subject: [PATCH 04/13] Fix config schema --- config/config-schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config-schema.json b/config/config-schema.json index 165e5f9..281af39 100644 --- a/config/config-schema.json +++ b/config/config-schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { - "lunchBreakDuration": { + "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" }, From f8cf9bad7d02e26628d28b63f14bc2b6010df037 Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Thu, 23 Nov 2023 20:46:58 +0200 Subject: [PATCH 05/13] Fix crash on missing config sections --- src/config.ts | 32 +++++++++++++++++--------------- src/input.ts | 2 +- src/types/WtcConfig.ts | 2 +- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/config.ts b/src/config.ts index 6055013..fe6f0f8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -27,7 +27,7 @@ const defaultConfig: RawConfig = { hadLunch: true, }, askInput: { - workDayLength: true, + workDayDuration: true, startTime: true, stopTime: true, logged: true, @@ -38,30 +38,32 @@ const getConfig = (): WtcConfig => { const configDir = xdgConfig || path.join(process.env.HOME ?? './', '.config'); let configFilePath = path.join(configDir, 'wtc', 'config.toml'); - let configData: RawConfig; + let configData: Partial; if (fs.existsSync(configFilePath)) { configData = toml.parse(fs.readFileSync(configFilePath, 'utf8')) as unknown as RawConfig; } else { configData = defaultConfig; } + const { language, timestampFormat, unpaidLunchBreakDuration, defaults, askInput } = configData; + return { - language: configData.language ?? defaultConfig.language, - timestampFormat: configData.timestampFormat ?? defaultConfig.timestampFormat, - unpaidLunchBreakDuration: !configData.unpaidLunchBreakDuration ? undefined : parseDuration(configData.unpaidLunchBreakDuration), + language: language ?? defaultConfig.language, + timestampFormat: timestampFormat ?? defaultConfig.timestampFormat, + unpaidLunchBreakDuration: !unpaidLunchBreakDuration + ? undefined + : parseDuration(unpaidLunchBreakDuration), defaults: { - workDayDuration: parseDuration( - configData.defaults.workDayDuration ?? defaultConfig.defaults.workDayDuration, - ), - startTime: parseTimestamp(configData.defaults.startTime ?? defaultConfig.defaults.startTime), - stopTime: parseTimestamp(configData.defaults.stopTime ?? defaultConfig.defaults.stopTime), - hadLunch: configData.defaults.hadLunch ?? defaultConfig.defaults.hadLunch, + 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: { - workDayLength: configData.askInput.workDayLength ?? defaultConfig.askInput.workDayLength, - startTime: configData.askInput.startTime ?? defaultConfig.askInput.startTime, - stopTime: configData.askInput.stopTime ?? defaultConfig.askInput.stopTime, - logged: configData.askInput.logged ?? defaultConfig.askInput.logged, + workDayDuration: askInput?.workDayDuration ?? defaultConfig.askInput.workDayDuration, + startTime: askInput?.startTime ?? defaultConfig.askInput.startTime, + stopTime: askInput?.stopTime ?? defaultConfig.askInput.stopTime, + logged: askInput?.logged ?? defaultConfig.askInput.logged, }, }; }; diff --git a/src/input.ts b/src/input.ts index 5a2303c..035ed48 100644 --- a/src/input.ts +++ b/src/input.ts @@ -30,7 +30,7 @@ const input = async (config: WtcConfig): Promise => { // Get work day duration let workDayDuration: Duration | undefined = undefined; - if (askInput.workDayLength) { + if (askInput.workDayDuration) { const durationAnswer = await rl.question( msg( MessageKey.promptWorkDayDuration, diff --git a/src/types/WtcConfig.ts b/src/types/WtcConfig.ts index c0b9c62..15d6382 100644 --- a/src/types/WtcConfig.ts +++ b/src/types/WtcConfig.ts @@ -13,7 +13,7 @@ export default interface WtcConfig { hadLunch: boolean; }; askInput: { - workDayLength: boolean; + workDayDuration: boolean; startTime: boolean; stopTime: boolean; logged: boolean; From e7a67ebfa0ac3db6636fc3fe94f5459456378796 Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Thu, 23 Nov 2023 20:53:37 +0200 Subject: [PATCH 06/13] Fix displaying overlogged time as negative --- src/output.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/output.ts b/src/output.ts index 49eaa33..b300000 100644 --- a/src/output.ts +++ b/src/output.ts @@ -3,8 +3,11 @@ import { formatDuration, getHoursRoundedStr } from './format'; import { WtcPromptResult } from './types/WtcPromptResult'; import { MessageKey, message } from './i18n.js'; import WtcConfig from './types/WtcConfig'; +import duration from 'dayjs/plugin/duration.js'; +import dayjs from 'dayjs'; const { log } = console; +dayjs.extend(duration); const output = (result: WtcPromptResult, config: WtcConfig) => { const { language, timestampFormat } = config; @@ -34,7 +37,8 @@ const output = (result: WtcPromptResult, config: WtcConfig) => { unLoggedMinutes === 0 ? '' : chalk.yellow(hoursRounded(unLogged)), ); } else if (unLoggedMinutes < 0) { - log(chalk.red(msg(MessageKey.loggedOver, fmtDuration(unLogged))), chalk.yellow(hoursRounded(unLogged))); + const overLogged = dayjs.duration(Math.abs(unLogged.asMilliseconds()), 'milliseconds'); + log(chalk.red(msg(MessageKey.loggedOver, fmtDuration(overLogged)))); } if (workLeft.asMinutes() > 0) { From 13b4c3a6402d13d32baa89fd1b07170bec214228 Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Thu, 23 Nov 2023 20:56:11 +0200 Subject: [PATCH 07/13] Fix typo --- src/format.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/format.ts b/src/format.ts index 38c6c69..a242f7b 100644 --- a/src/format.ts +++ b/src/format.ts @@ -23,7 +23,7 @@ export const formatDuration = ? `H [tunti${duration.hours() > 1 ? 'a' : ''} ja] m [minuuttia]` : duration.hours() > 0 ? `H [tunti${duration.hours() > 1 ? 'a' : ''}]` - : 'm [minuttia]'; + : 'm [minuuttia]'; } else { formatString = duration.hours() > 0 && duration.minutes() > 0 From 0ce99d923536bbbec9c4ce59e113c65e5228eae6 Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Thu, 23 Nov 2023 20:56:40 +0200 Subject: [PATCH 08/13] 1.0.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 534df9f..56b462e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "work-time-calculator", - "version": "1.0.1", + "version": "1.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "work-time-calculator", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/package.json b/package.json index 94dbbb5..21d7b11 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.1", + "version": "1.0.2", "description": "An interactive CLI tool to calculate work time", "license": "MIT", "repository": { From bb375e62ea5922f1ec271ae3262bb22e4be442be Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Fri, 24 Nov 2023 16:03:29 +0200 Subject: [PATCH 09/13] Update package-lock.json --- package-lock.json | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 56b462e..971f2c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,6 @@ "@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" } }, @@ -1644,15 +1643,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", @@ -1968,31 +1958,6 @@ "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", @@ -2117,13 +2082,6 @@ "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/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", From e44fc052f20c908474205773d5503d0eb20db23b Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Fri, 24 Nov 2023 16:03:44 +0200 Subject: [PATCH 10/13] Add dayjs configurator --- src/dayjs.ts | 10 ++++++++++ src/format.ts | 2 +- src/input.ts | 9 +++------ src/output.ts | 4 +--- src/parse.ts | 7 +------ 5 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 src/dayjs.ts diff --git a/src/dayjs.ts b/src/dayjs.ts new file mode 100644 index 0000000..526f1a2 --- /dev/null +++ b/src/dayjs.ts @@ -0,0 +1,10 @@ +import dayjs, {Dayjs} from 'dayjs'; +import duration, {Duration} from 'dayjs/plugin/duration.js'; +import customParseFormat from 'dayjs/plugin/customParseFormat.js'; + +dayjs.extend(duration); +dayjs.extend(customParseFormat); + +export default dayjs; + +export type {Dayjs, Duration}; diff --git a/src/format.ts b/src/format.ts index a242f7b..b097907 100644 --- a/src/format.ts +++ b/src/format.ts @@ -1,4 +1,4 @@ -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'; diff --git a/src/input.ts b/src/input.ts index 035ed48..afc847d 100644 --- a/src/input.ts +++ b/src/input.ts @@ -1,15 +1,12 @@ import chalk from 'chalk'; -import { Duration } from 'dayjs/plugin/duration'; import { parseDuration, parseTimestamp } from './parse'; import * as readline from 'readline/promises'; import { formatDuration, formatTime } from './format'; -import dayjs, { Dayjs } from 'dayjs'; +import { Dayjs } from 'dayjs'; import { WtcPromptResult } from './types/WtcPromptResult'; -import duration from 'dayjs/plugin/duration.js'; import WtcConfig from './types/WtcConfig'; import { MessageKey, message } from './i18n'; - -dayjs.extend(duration); +import dayjs, { Duration } from './dayjs'; const { error } = console; @@ -123,7 +120,7 @@ const input = async (config: WtcConfig): Promise => { const logged = parseDuration(loggedAnswer); const unLogged = worked.subtract(logged); const workLeft = workDayDuration.subtract(worked); - let workLeftMinutes = workLeft.asMinutes(); + const workLeftMinutes = workLeft.asMinutes(); let workedOvertime: Duration | undefined; if (workLeftMinutes < 0) { diff --git a/src/output.ts b/src/output.ts index b300000..9d8de7b 100644 --- a/src/output.ts +++ b/src/output.ts @@ -3,11 +3,9 @@ import { formatDuration, getHoursRoundedStr } from './format'; import { WtcPromptResult } from './types/WtcPromptResult'; import { MessageKey, message } from './i18n.js'; import WtcConfig from './types/WtcConfig'; -import duration from 'dayjs/plugin/duration.js'; -import dayjs from 'dayjs'; +import dayjs from './dayjs'; const { log } = console; -dayjs.extend(duration); const output = (result: WtcPromptResult, config: WtcConfig) => { const { language, timestampFormat } = config; diff --git a/src/parse.ts b/src/parse.ts index 4abe2fd..7d723fb 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -1,9 +1,4 @@ -import dayjs, { Dayjs } from 'dayjs'; -import customParseFormat from 'dayjs/plugin/customParseFormat.js'; -import duration, { Duration } from 'dayjs/plugin/duration.js'; - -dayjs.extend(customParseFormat); -dayjs.extend(duration); +import dayjs, { Dayjs, Duration } from './dayjs'; export const parseTimestamp = (time: string): Dayjs => (time === 'now' ? dayjs() : dayjs(time, 'HH:mm', true)); From 820a49efd822363d3123e43c00d7fd8c6413ab44 Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Fri, 24 Nov 2023 16:06:57 +0200 Subject: [PATCH 11/13] Fix main directive in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21d7b11..5a1e825 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,11 @@ "url": "https://git.korhonen.cc/FunctionalHacker/work-time-calculator/issues", "email": "wtc@functionalhacker.korhonen.cc" }, - "main": "src/main.ts", "type": "module", "bin": { "wtc": "bin/wtc" }, + "main": "dist/wtc.js", "scripts": { "build": "rollup -c" }, From 3239a7c611cde618ef9ab866b28dcace181c474f Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Fri, 24 Nov 2023 16:44:32 +0200 Subject: [PATCH 12/13] Bundle all files to single JavaScript file, add development mode --- Makefile | 15 +++-- bin/wtc | 2 +- package-lock.json | 147 ++++++++++++++++++++++++++++++++++++++++-- package.json | 9 ++- rollup.config.js | 27 -------- rollup.dev.config.js | 15 +++++ rollup.prod.config.js | 15 +++++ src/config.ts | 6 +- src/dayjs.ts | 6 +- 9 files changed, 194 insertions(+), 48 deletions(-) delete mode 100644 rollup.config.js create mode 100644 rollup.dev.config.js create mode 100644 rollup.prod.config.js diff --git a/Makefile b/Makefile index 7ef3132..c5a355b 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ -.PHONY: help build clean update-npmjs-readme release publish +.PHONY: help prod dev clean update-npmjs-readme release publish help: @echo "Available targets:" @echo " - help: Show this help message" - @echo " - build: Build the project" + @echo " - prod: Build the project in production mode" + @echo " - dev: Build the project in development mode" @echo " - clean: Remove build artifacts" @echo " - release: Create a new release version" @echo " - publish: Publish the new version created with the release target" @@ -11,13 +12,17 @@ help: node_modules: npm install -build: node_modules - npm run build +prod: node_modules + npm run prod + +dev: node_modules + npm run dev + chmod +x dist/wtc-dev.mjs clean: rm -r dist node_modules -release: build +release: prod @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/bin/wtc b/bin/wtc index bf01370..408a2ea 100755 --- a/bin/wtc +++ b/bin/wtc @@ -1,4 +1,4 @@ #!/bin/sh DIR="$(dirname "$(readlink -f "$0")")" -node "$DIR/../dist/wtc.js" "$@" +node "$DIR/../dist/wtc.mjs" "$@" diff --git a/package-lock.json b/package-lock.json index 971f2c0..a0226f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,9 @@ "version": "1.0.2", "license": "MIT", "dependencies": { - "@iarna/toml": "^2.2.5", "chalk": "^5.3.0", "dayjs": "^1.11.10", + "iarna-toml-esm": "^3.0.5", "xdg-basedir": "^5.1.0", "yargs": "^17.7.2" }, @@ -19,6 +19,7 @@ "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", @@ -26,6 +27,7 @@ "@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" } }, @@ -133,11 +135,6 @@ "dev": true, "peer": true }, - "node_modules/@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -231,6 +228,31 @@ "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", @@ -478,6 +500,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", @@ -809,6 +837,18 @@ "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", @@ -916,6 +956,15 @@ "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", @@ -941,6 +990,14 @@ "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", @@ -1429,6 +1486,14 @@ "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", @@ -1483,6 +1548,21 @@ "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", @@ -1524,6 +1604,12 @@ "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", @@ -1643,6 +1729,15 @@ "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", @@ -1958,6 +2053,31 @@ "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", @@ -2082,6 +2202,21 @@ "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", diff --git a/package.json b/package.json index 5a1e825..5a651e7 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,10 @@ "bin": { "wtc": "bin/wtc" }, - "main": "dist/wtc.js", + "main": "dist/wtc.mjs", "scripts": { - "build": "rollup -c" + "prod": "rollup -c ./rollup.prod.config.js", + "dev": "rollup -c ./rollup.dev.config.js" }, "keywords": [ "work", @@ -27,6 +28,7 @@ ], "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", @@ -34,12 +36,13 @@ "@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" }, "dependencies": { - "@iarna/toml": "^2.2.5", "chalk": "^5.3.0", "dayjs": "^1.11.10", + "iarna-toml-esm": "^3.0.5", "xdg-basedir": "^5.1.0", "yargs": "^17.7.2" } diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index c109771..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,27 +0,0 @@ -import typescript from '@rollup/plugin-typescript'; -import terser from '@rollup/plugin-terser'; - -/** @type {import('rollup').RollupOptions} */ -const config = { - input: 'src/main.ts', - output: { - format: 'esm', - file: 'dist/wtc.js', - }, - plugins: [typescript(), terser()], - external: [ - '@iarna/toml', - 'chalk', - 'dayjs', - 'dayjs/plugin/customParseFormat.js', - 'dayjs/plugin/duration.js', - 'fs', - 'path', - 'readline/promises', - 'xdg-basedir', - 'yargs', - 'yargs/helpers', - ], -}; - -export default config; diff --git a/rollup.dev.config.js b/rollup.dev.config.js new file mode 100644 index 0000000..7d2e1fb --- /dev/null +++ b/rollup.dev.config.js @@ -0,0 +1,15 @@ +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 new file mode 100644 index 0000000..258f57b --- /dev/null +++ b/rollup.prod.config.js @@ -0,0 +1,15 @@ +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 index fe6f0f8..02f288b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,7 +1,7 @@ import fs from 'fs'; import path from 'path'; import { xdgConfig } from 'xdg-basedir'; -import toml from '@iarna/toml'; +import {parse} from 'iarna-toml-esm'; import { parseDuration, parseTimestamp } from './parse.js'; import WtcConfig from './types/WtcConfig.js'; import Language from './types/Language.js'; @@ -36,11 +36,11 @@ const defaultConfig: RawConfig = { const getConfig = (): WtcConfig => { const configDir = xdgConfig || path.join(process.env.HOME ?? './', '.config'); - let configFilePath = path.join(configDir, 'wtc', 'config.toml'); + const configFilePath = path.join(configDir, 'wtc', 'config.toml'); let configData: Partial; if (fs.existsSync(configFilePath)) { - configData = toml.parse(fs.readFileSync(configFilePath, 'utf8')) as unknown as RawConfig; + configData = parse(fs.readFileSync(configFilePath, 'utf8')) as unknown as RawConfig; } else { configData = defaultConfig; } diff --git a/src/dayjs.ts b/src/dayjs.ts index 526f1a2..7626024 100644 --- a/src/dayjs.ts +++ b/src/dayjs.ts @@ -1,6 +1,6 @@ -import dayjs, {Dayjs} from 'dayjs'; -import duration, {Duration} from 'dayjs/plugin/duration.js'; -import customParseFormat from 'dayjs/plugin/customParseFormat.js'; +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); From c97472f6f5aa1f1085f118cc5ca659bb64723d2f Mon Sep 17 00:00:00 2001 From: Marko Korhonen Date: Mon, 27 Nov 2023 17:57:13 +0200 Subject: [PATCH 13/13] i18n CLI options --- src/i18n.ts | 11 ++++++++++- src/input.ts | 8 ++++---- src/main.ts | 35 ++++++++++++++++++++++++++++++++--- src/output.ts | 8 ++++---- src/types/WtcConfig.ts | 8 ++++++++ src/ui.ts | 8 ++------ src/update.ts | 7 +++++++ 7 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 src/update.ts diff --git a/src/i18n.ts b/src/i18n.ts index 329206a..074e43b 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -1,7 +1,8 @@ import Language from './types/Language'; - export enum MessageKey { + cliHelp, + cliVersion, promptWorkDayDuration, excludingLunch, promptStartTime, @@ -27,6 +28,14 @@ export enum MessageKey { } 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}]: ', diff --git a/src/input.ts b/src/input.ts index afc847d..386060f 100644 --- a/src/input.ts +++ b/src/input.ts @@ -4,14 +4,14 @@ import * as readline from 'readline/promises'; import { formatDuration, formatTime } from './format'; import { Dayjs } from 'dayjs'; import { WtcPromptResult } from './types/WtcPromptResult'; -import WtcConfig from './types/WtcConfig'; -import { MessageKey, message } from './i18n'; +import { WtcRuntimeConfig } from './types/WtcConfig'; +import { MessageKey } from './i18n'; import dayjs, { Duration } from './dayjs'; const { error } = console; -const input = async (config: WtcConfig): Promise => { - const msg = message(config.language); +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({ diff --git a/src/main.ts b/src/main.ts index f244ada..6e3b054 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,38 @@ 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'; + +// Build runtime config +const config = getConfig(); +const msg = message(config.language); +const runtimeConfig: WtcRuntimeConfig = { + config, + msg, +}; // Process args. Yargs will exit if it detects help or version -yargs(hideBin(process.argv)).usage('Work time calculator').alias('help', 'h').alias('version', 'v').argv; +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 UI if help or version is not prompted -ui(); +// Run updater if requested +if (args.update) { + update(); + process.exit(0); +} + +// Run UI if no arguments +ui(runtimeConfig); diff --git a/src/output.ts b/src/output.ts index 9d8de7b..86e0619 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,15 +1,15 @@ import chalk from 'chalk'; import { formatDuration, getHoursRoundedStr } from './format'; import { WtcPromptResult } from './types/WtcPromptResult'; -import { MessageKey, message } from './i18n.js'; -import WtcConfig from './types/WtcConfig'; +import { MessageKey } from './i18n.js'; +import { WtcRuntimeConfig } from './types/WtcConfig'; import dayjs from './dayjs'; const { log } = console; -const output = (result: WtcPromptResult, config: WtcConfig) => { +const output = (result: WtcPromptResult, runtimeCfg: WtcRuntimeConfig) => { + const {config, msg} = runtimeCfg; const { language, timestampFormat } = config; - const msg = message(language); const fmtDuration = formatDuration(language); const hoursRounded = getHoursRoundedStr(language); const { startedAt, stoppedAt, stoppedWorking, worked, unLogged, workLeft, workedOvertime, hadLunch } = result; diff --git a/src/types/WtcConfig.ts b/src/types/WtcConfig.ts index 15d6382..7d822b8 100644 --- a/src/types/WtcConfig.ts +++ b/src/types/WtcConfig.ts @@ -1,6 +1,7 @@ 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, @@ -19,3 +20,10 @@ export default interface WtcConfig { logged: boolean; }; } + +/** Config and current language msg function together */ +export interface WtcRuntimeConfig { + config: WtcConfig; + msg: ReturnType; +} + diff --git a/src/ui.ts b/src/ui.ts index 45d0285..9d792fe 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -1,11 +1,7 @@ -import getConfig from './config.js'; import input from './input.js'; import output from './output.js'; +import { WtcRuntimeConfig } from './types/WtcConfig.js'; -const ui = async () => { - const config = getConfig(); - const result = await input(config); - output(result, config); -}; +const ui = async (config: WtcRuntimeConfig) => output(await input(config), config); export default ui; diff --git a/src/update.ts b/src/update.ts new file mode 100644 index 0000000..92ad114 --- /dev/null +++ b/src/update.ts @@ -0,0 +1,7 @@ +const { log } = console; + +const update = () => { + log('update'); +}; + +export default update;