From 6b5f6ce9b2724c25a2f13755e124238db4e61cc4 Mon Sep 17 00:00:00 2001 From: antoniopresto Date: Sun, 25 Feb 2024 18:25:08 -0300 Subject: [PATCH] add dateFormat + minor improvements --- .run/Template Jest.run.xml | 13 - package.json | 4 +- packages/accounts/package.json | 2 +- packages/babel-plugins/package.json | 2 +- packages/boilerplate/package.json | 3 +- packages/deepstate/package.json | 2 +- packages/entity/package.json | 2 +- packages/helpers/package.json | 2 +- packages/logstorm/package.json | 5 +- packages/mongo/package.json | 2 +- packages/plugin-engine/package.json | 2 +- packages/powership/package.json | 2 +- packages/runmate/package.json | 2 +- .../__tests__/findWorkspacePackages.spec.ts | 18 +- packages/runmate/src/commands/main.ts | 20 +- .../runmate/src/commands/set-json-value.ts | 3 +- packages/runmate/src/findWorkspacePackages.ts | 48 +- packages/runmate/src/packageRunner.ts | 24 +- packages/schema/package.json | 2 +- packages/schema/src/tsfy/tsfyWriter.ts | 4 +- packages/schema/src/writeTypes.ts | 4 +- packages/server/package.json | 2 +- packages/transporter/package.json | 2 +- packages/utils/package.json | 40 +- packages/utils/src/dateFormat.ts | 414 ++++++++++++++++++ packages/utils/src/index.ts | 4 +- pnpm-lock.yaml | 30 +- 27 files changed, 571 insertions(+), 87 deletions(-) delete mode 100644 .run/Template Jest.run.xml create mode 100644 packages/utils/src/dateFormat.ts diff --git a/.run/Template Jest.run.xml b/.run/Template Jest.run.xml deleted file mode 100644 index df84b882..00000000 --- a/.run/Template Jest.run.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/package.json b/package.json index 239ea597..c8ee5953 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "powership", - "version": "3.2.20", + "version": "3.2.24", "private": true, "scripts": { "pack": "run-s pack:*", @@ -45,7 +45,7 @@ "runmate": "latest", "typedoc-plugin-markdown": "3.14.0", "typescript": "4.8.2", - "bun-safe": "1.0.5", + "bun-safe": "1.0.7", "semver": "7.5.2", "typedoc": "0.23.24", "zx": "7.2.3" diff --git a/packages/accounts/package.json b/packages/accounts/package.json index 87748c19..d05d64db 100644 --- a/packages/accounts/package.json +++ b/packages/accounts/package.json @@ -1,6 +1,6 @@ { "name": "@powership/accounts", - "version": "3.2.22", + "version": "3.2.24", "description": "Powership accounts", "type": "module", "main": "./out/index.cjs", diff --git a/packages/babel-plugins/package.json b/packages/babel-plugins/package.json index 3135a744..c2ac5d93 100644 --- a/packages/babel-plugins/package.json +++ b/packages/babel-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@powership/babel-plugins", - "version": "3.2.22", + "version": "3.2.24", "main": "./out/index.js", "sideEffects": false, "typings": "./out/index.d.ts", diff --git a/packages/boilerplate/package.json b/packages/boilerplate/package.json index aa4e58c0..5a93fcd0 100644 --- a/packages/boilerplate/package.json +++ b/packages/boilerplate/package.json @@ -1,6 +1,6 @@ { "name": "@powership/boilerplate", - "version": "3.2.22", + "version": "3.2.24", "author": "antoniopresto ", "sideEffects": false, "type": "module", @@ -34,6 +34,7 @@ "out/*", "tsconfig/*", "eslintrc.cjs", + "babel-config.cjs", "README.md" ] } diff --git a/packages/deepstate/package.json b/packages/deepstate/package.json index 690b076d..f1e7f68f 100644 --- a/packages/deepstate/package.json +++ b/packages/deepstate/package.json @@ -1,6 +1,6 @@ { "name": "@powership/deepstate", - "version": "3.2.22", + "version": "3.2.24", "main": "out/index.cjs", "module": "out/module/index.mjs", "sideEffects": false, diff --git a/packages/entity/package.json b/packages/entity/package.json index f02078b8..b4bec08b 100644 --- a/packages/entity/package.json +++ b/packages/entity/package.json @@ -1,6 +1,6 @@ { "name": "@powership/entity", - "version": "3.2.22", + "version": "3.2.24", "type": "module", "main": "./out/index.cjs", "module": "./out/module/index.mjs", diff --git a/packages/helpers/package.json b/packages/helpers/package.json index b7f5e0b2..d27d5953 100644 --- a/packages/helpers/package.json +++ b/packages/helpers/package.json @@ -1,6 +1,6 @@ { "name": "@powership/helpers", - "version": "3.2.22", + "version": "3.2.24", "type": "module", "main": "./out/index.cjs", "module": "./out/module/index.mjs", diff --git a/packages/logstorm/package.json b/packages/logstorm/package.json index 5fa401fe..6436a78c 100644 --- a/packages/logstorm/package.json +++ b/packages/logstorm/package.json @@ -1,6 +1,6 @@ { "name": "logstorm", - "version": "3.2.22", + "version": "3.2.24", "typings": "out", "author": "antoniopresto ", "type": "module", @@ -39,7 +39,8 @@ "description": "run command in each package", "keywords": [], "dependencies": { - "plugin-hooks": "2.0.0" + "plugin-hooks": "2.0.0", + "@powership/utils": "workspace:*" }, "devDependencies": { "@powership/boilerplate": "workspace:*", diff --git a/packages/mongo/package.json b/packages/mongo/package.json index ee26b512..7e108eeb 100644 --- a/packages/mongo/package.json +++ b/packages/mongo/package.json @@ -1,6 +1,6 @@ { "name": "@powership/mongo", - "version": "3.2.22", + "version": "3.2.24", "type": "module", "main": "./out/index.cjs", "module": "./out/module/index.mjs", diff --git a/packages/plugin-engine/package.json b/packages/plugin-engine/package.json index 05901d87..61f8a320 100644 --- a/packages/plugin-engine/package.json +++ b/packages/plugin-engine/package.json @@ -1,6 +1,6 @@ { "name": "plugin-engine", - "version": "3.2.22", + "version": "3.2.24", "type": "module", "main": "./out/index.cjs", "module": "./out/module/index.mjs", diff --git a/packages/powership/package.json b/packages/powership/package.json index 7f15d43c..bc77da0c 100644 --- a/packages/powership/package.json +++ b/packages/powership/package.json @@ -1,6 +1,6 @@ { "name": "powership", - "version": "3.2.22", + "version": "3.2.24", "author": "antoniopresto ", "type": "module", "main": "./out/index.cjs", diff --git a/packages/runmate/package.json b/packages/runmate/package.json index 04280bdd..788d329e 100644 --- a/packages/runmate/package.json +++ b/packages/runmate/package.json @@ -1,6 +1,6 @@ { "name": "runmate", - "version": "3.2.22", + "version": "3.2.24", "typings": "out", "author": "antoniopresto ", "license": "MIT", diff --git a/packages/runmate/src/__tests__/findWorkspacePackages.spec.ts b/packages/runmate/src/__tests__/findWorkspacePackages.spec.ts index 1b91dc2a..259cbc3a 100644 --- a/packages/runmate/src/__tests__/findWorkspacePackages.spec.ts +++ b/packages/runmate/src/__tests__/findWorkspacePackages.spec.ts @@ -7,14 +7,20 @@ describe('findWorkspacePackages', () => { cwd: nodePath.resolve(__dirname), }); + expect(sut).toEqual([]); + }); + + test('catch top package', () => { + const sut = findWorkspacePackages({ + cwd: nodePath.resolve(__dirname, '../../'), + }); + expect(sut).toEqual([ { - found: [], - pattern: 'apps/*', - }, - { - found: [], - pattern: 'packages/*', + path: expect.stringMatching(/\/packages\/runmate\/package.json$/), + json: expect.objectContaining({ name: 'runmate' }), + relative: './', + pattern: './', }, ]); }); diff --git a/packages/runmate/src/commands/main.ts b/packages/runmate/src/commands/main.ts index 342b8458..427f087d 100644 --- a/packages/runmate/src/commands/main.ts +++ b/packages/runmate/src/commands/main.ts @@ -11,6 +11,11 @@ export function main(program: Command) { 'Run command in each package in ./packages folder or in `--cwd` option folder' ) .option('-d, --cwd ', 'Source directory or glob pattern') + .option( + '--include-root ', + 'Include root project in execution', + 'true' + ) .option( '-c, --chunk-size ', 'Chunk size of parallel executions', @@ -33,6 +38,7 @@ export function main(program: Command) { .action(async function run( commands: string[], options?: { + includeRoot?: unknown; cwd?: string; chunkSize: string; failFast: boolean; @@ -42,7 +48,7 @@ export function main(program: Command) { packageNameCommand: boolean; } ): Promise { - const { + let { // cwd: src, chunkSize = 1, @@ -51,6 +57,7 @@ export function main(program: Command) { packages, packageNameCommand, from, + includeRoot, } = options || {}; const ignoreList = ignore?.split(/, ?/); @@ -60,6 +67,17 @@ export function main(program: Command) { const runner = await packageRunner({ cwd: src, failFast, + includeRoot: (() => { + if (typeof includeRoot === 'boolean') return includeRoot; + + if (typeof includeRoot !== 'string') { + throw new Error( + `includeRoot received invalid value: ${includeRoot} ` + ); + } + + return includeRoot === 'true'; + })(), }); if (packageNameCommand !== false) { diff --git a/packages/runmate/src/commands/set-json-value.ts b/packages/runmate/src/commands/set-json-value.ts index 5eb4f63a..cae8291d 100644 --- a/packages/runmate/src/commands/set-json-value.ts +++ b/packages/runmate/src/commands/set-json-value.ts @@ -1,4 +1,5 @@ -import { chalk, jsonParse, nodePath, setByPath } from '@powership/utils'; +import { jsonParse, setByPath } from '@powership/utils'; +import { chalk, nodePath } from '@powership/utils/out/node-utils'; import { Command } from 'commander'; import { writePackageJSON } from '../handleJSON'; diff --git a/packages/runmate/src/findWorkspacePackages.ts b/packages/runmate/src/findWorkspacePackages.ts index ae1d15ad..7e2599a6 100644 --- a/packages/runmate/src/findWorkspacePackages.ts +++ b/packages/runmate/src/findWorkspacePackages.ts @@ -1,15 +1,16 @@ import nodePath from 'path'; -import * as path from 'path'; import process from 'process'; import fs from 'fs-extra'; import * as glob from 'glob'; import { PackageJson } from './ICommons'; +import { readPackageJSON } from './handleJSON'; export type FindWorkspacePackagesInit = { cwd?: string; globCache?: any; + includeRoot?: boolean; }; export type WorkspacePackagesFoundResult = ReturnType< @@ -17,38 +18,51 @@ export type WorkspacePackagesFoundResult = ReturnType< >; export function findWorkspacePackages(init?: FindWorkspacePackagesInit) { - const { cwd: rootDir = process.cwd(), globCache } = init || {}; + const { + cwd: rootDir = process.cwd(), + globCache, + includeRoot = true, + } = init || {}; const pnpmWorkspaces = listPnpmWorkspaces(rootDir); const packageJsonWorkspaces = listPackageJSONWorkspaces(rootDir); - const foundPatterns = pnpmWorkspaces?.length + let foundPatterns = pnpmWorkspaces?.length ? pnpmWorkspaces : packageJsonWorkspaces?.length ? packageJsonWorkspaces : null; if (!foundPatterns) { - throw new Error('No workspaces config found.'); + console.warn('⚠️ No workspaces config found.'); + foundPatterns = []; } - return foundPatterns.flatMap((pattern) => { - const found = glob.sync(nodePath.join(pattern, 'package.json'), { - cwd: rootDir, - absolute: true, - cache: globCache, - ignore: ['**/node_modules/**'], - }); + if (includeRoot) { + foundPatterns.unshift('./'); + } - return { - found, - pattern, - }; + return foundPatterns.flatMap((pattern) => { + return glob + .sync(nodePath.join(pattern, 'package.json'), { + cwd: rootDir, + absolute: true, + cache: globCache, + ignore: ['**/node_modules/**'], + }) + .flatMap((path) => { + return { + pattern, + path: path, + json: readPackageJSON(path), + relative: nodePath.relative(rootDir, nodePath.dirname(path)) || `./`, + }; + }); }); } function listPnpmWorkspaces(rootDir: string) { - const pnpmWorkspacePath = path.join(rootDir, 'pnpm-workspace.yaml'); + const pnpmWorkspacePath = nodePath.join(rootDir, 'pnpm-workspace.yaml'); if (fs.existsSync(pnpmWorkspacePath)) { const workspaceFileContent = fs.readFileSync(pnpmWorkspacePath, 'utf-8'); @@ -72,7 +86,7 @@ function listPnpmWorkspaces(rootDir: string) { } function listPackageJSONWorkspaces(rootDir: string) { - const packageJsonPath = path.join(rootDir, 'package.json'); + const packageJsonPath = nodePath.join(rootDir, 'package.json'); if (fs.existsSync(packageJsonPath)) { const packageJson = fs.readJSONSync(packageJsonPath) as PackageJson; diff --git a/packages/runmate/src/packageRunner.ts b/packages/runmate/src/packageRunner.ts index 7824f526..920f9882 100644 --- a/packages/runmate/src/packageRunner.ts +++ b/packages/runmate/src/packageRunner.ts @@ -2,6 +2,7 @@ import nodePath from 'path'; import { inspect } from 'util'; import { chunk } from 'lodash'; +import { logstorm } from 'logstorm'; import { DepTree, PackageItem } from './depTree'; import { findWorkspacePackages } from './findWorkspacePackages'; @@ -69,6 +70,7 @@ export interface PackageRunnerOptions { cwd?: string; cache?: boolean | Record; failFast?: boolean; + includeRoot?: boolean; } export interface PackageRunOptions { @@ -103,6 +105,7 @@ export async function packageRunner( cwd = process.cwd(), cache: cacheOption = true, failFast: failFastRoot = true, + includeRoot, } = options; const cache = (() => { @@ -112,11 +115,24 @@ export async function packageRunner( return undefined; })(); - const files = findWorkspacePackages({ globCache: cache, cwd }).flatMap( - (el) => el.found - ); + const files = findWorkspacePackages({ globCache: cache, cwd, includeRoot }); + + (() => { + // force log + const level = logstorm.level; + const prefix = logstorm.prefix; + logstorm.level = 'info'; + logstorm.prefix = false; + logstorm.info( + `Running command in:\n${(() => { + return files.map((el) => ` ‣ ${el.relative}`).join('\n'); + })()}` + ); + logstorm.level = level; + logstorm.prefix = prefix; + })(); - const utils = files.map((file) => getPackageRunnerUtils(file)); + const utils = files.map((file) => getPackageRunnerUtils(file.path)); const packages = reduceObject(utils, (item) => ({ [item.name]: item })); const depTree = new DepTree(utils.map((el) => el.json)).find(); diff --git a/packages/schema/package.json b/packages/schema/package.json index 2b454c21..09af5893 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -1,6 +1,6 @@ { "name": "@powership/schema", - "version": "3.2.22", + "version": "3.2.24", "type": "module", "main": "./out/index.cjs", "module": "./out/module/index.mjs", diff --git a/packages/schema/src/tsfy/tsfyWriter.ts b/packages/schema/src/tsfy/tsfyWriter.ts index 472a34f2..fcf7a8b7 100644 --- a/packages/schema/src/tsfy/tsfyWriter.ts +++ b/packages/schema/src/tsfy/tsfyWriter.ts @@ -6,13 +6,15 @@ import { simpleObjectHash, Store, } from '@powership/utils'; -import { ensureFileSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; import { GraphType } from '../GraphType/GraphType'; import { ObjectType } from '../ObjectType'; import { createTSFYContext, tsfy, TSFYConfig } from './tsfy'; +const { ensureFileSync } = fsExtra; + export const defaultTypesDest = path.resolve( Process.cwd(), 'generated/powership.d.ts' diff --git a/packages/schema/src/writeTypes.ts b/packages/schema/src/writeTypes.ts index 88325dd6..3fab4502 100644 --- a/packages/schema/src/writeTypes.ts +++ b/packages/schema/src/writeTypes.ts @@ -2,7 +2,9 @@ import path from 'path'; import { Process } from '@powership/utils'; import { Emitter, mitt } from '@powership/utils'; -import { ensureFileSync, writeFileSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; + +const { ensureFileSync, writeFileSync } = fsExtra; import { AnyResolver } from './Resolver'; import { GraphTypeLike } from './fields/IObjectLike'; diff --git a/packages/server/package.json b/packages/server/package.json index 1bf2143b..b0f3380d 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@powership/server", - "version": "3.2.22", + "version": "3.2.24", "type": "module", "main": "./out/index.cjs", "module": "./out/module/index.mjs", diff --git a/packages/transporter/package.json b/packages/transporter/package.json index c65512de..01232331 100644 --- a/packages/transporter/package.json +++ b/packages/transporter/package.json @@ -1,6 +1,6 @@ { "name": "@powership/transporter", - "version": "3.2.22", + "version": "3.2.24", "type": "module", "main": "./out/index.cjs", "module": "./out/module/index.mjs", diff --git a/packages/utils/package.json b/packages/utils/package.json index 209b501b..f15214a1 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@powership/utils", - "version": "3.2.22", + "version": "3.2.24", "typings": "out", "author": "antoniopresto ", "license": "MIT", @@ -15,7 +15,11 @@ "import": "./out/module/index.mjs", "require": "./out/index.cjs" }, - "./node-utils": "./out/module/node-utils" + "./out/node-utils": { + "types": "./out/node-utils.d.ts", + "import": "./out/module/node-utils.mjs", + "require": "./out/node-utils.cjs" + } }, "browser": { "out/module/index.mjs": "./out/browser/module/index.mjs", @@ -40,9 +44,19 @@ "description": "powership utils and helper functions", "keywords": [], "dependencies": { + "@types/dateformat": "5.0.2", + "@types/fs-extra": "9.0.13", + "@types/lodash": "4.14.191", + "@types/lodash-es": "4.17.12", + "@types/semver": "7.5.8", "aggio": "0.2.0", "awesome-phonenumber": "6.2.0", "big.js": "6.2.1", + "bun-safe": "1.0.7", + "bun-types": "1.0.29", + "chalk": "4.1.2", + "commander": "10.0.0", + "dateformat": "5.0.3", "dayjs": "1.11.6", "deep-diff": "1.0.2", "deep-object-diff": "1.1.9", @@ -50,9 +64,7 @@ "fast-copy": "3.0.0", "fast-deep-equal": "3.1.3", "fs-extra": "10.1.0", - "chalk": "4.1.2", - "bun-safe": "1.0.5", - "bun-types": "1.0.29", + "glob": "10.3.10", "graphql": "16.6.0", "graphql-parse-resolve-info": "4.13.0", "hoper": "1.0.8", @@ -65,29 +77,22 @@ "object-hash": "3.0.0", "plugin-hooks": "2.0.0", "prettier": "2.8.8", + "qs": "6.11.2", + "semver": "7.6.0", "slugify": "1.6.5", "ts-toolbelt": "9.6.0", + "tsx": "4.7.1", "ulid": "2.3.0", - "qs": "6.11.2", - "url-pattern": "1.0.3", - "commander": "10.0.0", - "glob": "10.3.10", - "semver": "7.6.0", - "@types/semver": "7.5.8", - "@types/lodash": "4.14.191", - "@types/lodash-es": "4.17.12", - "@types/fs-extra": "9.0.13", - "tsx": "4.7.1" + "url-pattern": "1.0.3" }, "devDependencies": { - "@powership/boilerplate": "workspace:*", "@babel/cli": "7.19.3", "@babel/plugin-transform-typescript": "7.19.3", "@babel/preset-env": "7.19.3", "@babel/preset-typescript": "7.18.6", "@powership/babel-plugins": "workspace:*", + "@powership/boilerplate": "workspace:*", "@types/big.js": "6.1.6", - "@types/qs": "6.9.10", "@types/deep-diff": "^1.0.2", "@types/ejson": "2.2.0", "@types/jest": "29.5.3", @@ -95,6 +100,7 @@ "@types/node": "16.18.3", "@types/object-hash": "2.2.1", "@types/prettier": "2.7.1", + "@types/qs": "6.9.10", "@typescript-eslint/eslint-plugin": "5.39.0", "@typescript-eslint/parser": "5.39.0", "babel-preset-minify": "0.5.2", diff --git a/packages/utils/src/dateFormat.ts b/packages/utils/src/dateFormat.ts new file mode 100644 index 00000000..0f2cb2ab --- /dev/null +++ b/packages/utils/src/dateFormat.ts @@ -0,0 +1,414 @@ +/* + * Date Format 1.2.3 + * (c) 2007-2009 Steven Levithan + * MIT license + * + * Includes enhancements by Scott Trenda + * and Kris Kowal + * + * Accepts a date, a mask, or a date and a mask. + * Returns a formatted version of the given date. + * The date defaults to the current date/time. + * The mask defaults to masks.default. + */ + +/** + * @param date Defaults to the current date/time. + * @param mask Defaults to `masks.default`. + * @param utc + * @param gmt + * @returns A formatted version of the given date. + */ +export function dateFormat( + date?: Date | string | number, + mask?: string, + utc?: boolean, + gmt?: boolean +): string; +export function dateFormat(mask?: string, utc?: boolean, gmt?: boolean): string; +export function dateFormat(...args: any[]) { + let [date, mask, utc, gmt] = args; + + // You can't provide utc if you skip other args (use the 'UTC:' mask prefix) + if (arguments.length === 1 && typeof date === 'string' && !/\d/.test(date)) { + mask = date; + date = undefined; + } + + date = date || date === 0 ? date : new Date(); + + if (!(date instanceof Date)) { + date = new Date(date); + } + + if (isNaN(date)) { + throw TypeError('Invalid date'); + } + + mask = String(masks[mask] || mask || masks['default']); + + // Allow setting the utc/gmt argument via the mask + const maskSlice = mask.slice(0, 4); + if (maskSlice === 'UTC:' || maskSlice === 'GMT:') { + mask = mask.slice(4); + utc = true; + if (maskSlice === 'GMT:') { + gmt = true; + } + } + + const _ = () => (utc ? 'getUTC' : 'get'); + const d = () => date[_() + 'Date'](); + const D = () => date[_() + 'Day'](); + const m = () => date[_() + 'Month'](); + const y = () => date[_() + 'FullYear'](); + const H = () => date[_() + 'Hours'](); + const M = () => date[_() + 'Minutes'](); + const s = () => date[_() + 'Seconds'](); + const L = () => date[_() + 'Milliseconds'](); + const o = () => (utc ? 0 : date.getTimezoneOffset()); + const W = () => getWeek(date); + const N = () => getDayOfWeek(date); + + const flags = { + d: () => d(), + dd: () => pad(d()), + ddd: () => i18n.dayNames[D()], + DDD: () => + getDayName({ + y: y(), + m: m(), + d: d(), + _: _(), + dayName: i18n.dayNames[D()], + short: true, + }), + dddd: () => i18n.dayNames[D() + 7], + DDDD: () => + getDayName({ + y: y(), + m: m(), + d: d(), + _: _(), + dayName: i18n.dayNames[D() + 7], + }), + m: () => m() + 1, + mm: () => pad(m() + 1), + mmm: () => i18n.monthNames[m()], + mmmm: () => i18n.monthNames[m() + 12], + yy: () => String(y()).slice(2), + yyyy: () => pad(y(), 4), + h: () => H() % 12 || 12, + hh: () => pad(H() % 12 || 12), + H: () => H(), + HH: () => pad(H()), + M: () => M(), + MM: () => pad(M()), + s: () => s(), + ss: () => pad(s()), + l: () => pad(L(), 3), + L: () => pad(Math.floor(L() / 10)), + t: () => (H() < 12 ? i18n.timeNames[0] : i18n.timeNames[1]), + tt: () => (H() < 12 ? i18n.timeNames[2] : i18n.timeNames[3]), + T: () => (H() < 12 ? i18n.timeNames[4] : i18n.timeNames[5]), + TT: () => (H() < 12 ? i18n.timeNames[6] : i18n.timeNames[7]), + Z: () => (gmt ? 'GMT' : utc ? 'UTC' : formatTimezone(date)), + o: () => + (o() > 0 ? '-' : '+') + + pad(Math.floor(Math.abs(o()) / 60) * 100 + (Math.abs(o()) % 60), 4), + p: () => + (o() > 0 ? '-' : '+') + + pad(Math.floor(Math.abs(o()) / 60), 2) + + ':' + + pad(Math.floor(Math.abs(o()) % 60), 2), + S: () => + ['th', 'st', 'nd', 'rd'][ + // @ts-ignore + d() % 10 > 3 ? 0 : (((d() % 100) - (d() % 10) != 10) * d()) % 10 + ], + W: () => W(), + WW: () => pad(W()), + N: () => N(), + }; + + return mask.replace(token, (match) => { + if (match in flags) { + return flags[match](); + } + return match.slice(1, match.length - 1); + }); +} + +// /** +// * Get proper timezone abbreviation or timezone offset. +// * +// * This will fall back to `GMT+xxxx` if it does not recognize the +// * timezone within the `timezone` RegEx above. Currently only common +// * American and Australian timezone abbreviations are supported. +// */ + +export interface DateFormatMasks { + /** + * @default "ddd mmm dd yyyy HH:MM:ss" + */ + default: string; + /** + * @default "m/d/yy" + */ + shortDate: string; + /** + * @default "mm/dd/yyyy" + */ + paddedShortDate: string; + /** + * @default "mmm d, yyyy" + */ + mediumDate: string; + /** + * @default "mmmm d, yyyy" + */ + longDate: string; + /** + * @default "dddd, mmmm d, yyyy" + */ + fullDate: string; + /** + * @default "h:MM TT" + */ + shortTime: string; + /** + * @default "h:MM:ss TT" + */ + mediumTime: string; + /** + * @default "h:MM:ss TT Z" + */ + longTime: string; + /** + * @default "yyyy-mm-dd" + */ + isoDate: string; + /** + * @default "HH:MM:ss" + */ + isoTime: string; + /** + * @default "yyyy-mm-dd'T'HH:MM:sso" + */ + isoDateTime: string; + /** + * @default "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" + */ + isoUtcDateTime: string; + /** + * @default "ddd, dd mmm yyyy HH:MM:ss Z" + */ + expiresHeaderFormat: string; + [key: string]: string; +} + +export interface DateFormatI18n { + dayNames: string[]; + monthNames: string[]; + timeNames: string[]; +} + +// Regexes and supporting functions are cached through closure +const token = + /d{1,4}|D{3,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|W{1,2}|[LlopSZN]|"[^"]*"|'[^']*'/g; +const timezone = + /\b(?:[A-Z]{1,3}[A-Z][TC])(?:[-+]\d{4})?|((?:Australian )?(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time)\b/g; +const timezoneClip = /[^-+\dA-Z]/g; + +export let masks: DateFormatMasks = { + default: 'ddd mmm dd yyyy HH:MM:ss', + shortDate: 'm/d/yy', + paddedShortDate: 'mm/dd/yyyy', + mediumDate: 'mmm d, yyyy', + longDate: 'mmmm d, yyyy', + fullDate: 'dddd, mmmm d, yyyy', + shortTime: 'h:MM TT', + mediumTime: 'h:MM:ss TT', + longTime: 'h:MM:ss TT Z', + isoDate: 'yyyy-mm-dd', + isoTime: 'HH:MM:ss', + isoDateTime: "yyyy-mm-dd'T'HH:MM:sso", + isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'", + expiresHeaderFormat: 'ddd, dd mmm yyyy HH:MM:ss Z', +}; + +/** + * Internationalization strings + * + * @example + * import { i18n } from 'dateformat'; + * + * i18n.dayNames = [ + * 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', + * 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' + * ]; + * i18n.monthNames = [ + * 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', + * 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' + * ]; + * i18n.timeNames = [ + * 'a', 'p', 'am', 'pm', 'A', 'P', 'AM', 'PM' + * ]; + **/ + +export let i18n: DateFormatI18n = { + dayNames: [ + 'Sun', + 'Mon', + 'Tue', + 'Wed', + 'Thu', + 'Fri', + 'Sat', + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + ], + monthNames: [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', + ], + timeNames: ['a', 'p', 'am', 'pm', 'A', 'P', 'AM', 'PM'], +}; + +const pad = (val, len = 2) => String(val).padStart(len, '0'); + +/** + * Get day name + * Yesterday, Today, Tomorrow if the date lies within, else fallback to Monday - Sunday + * @param {Object} + * @return {String} + */ +const getDayName = ({ y, m, d, _, dayName, short = false }) => { + const today = new Date(); + const yesterday = new Date(); + yesterday.setDate(yesterday[_ + 'Date']() - 1); + const tomorrow = new Date(); + tomorrow.setDate(tomorrow[_ + 'Date']() + 1); + const today_d = () => today[_ + 'Date'](); + const today_m = () => today[_ + 'Month'](); + const today_y = () => today[_ + 'FullYear'](); + const yesterday_d = () => yesterday[_ + 'Date'](); + const yesterday_m = () => yesterday[_ + 'Month'](); + const yesterday_y = () => yesterday[_ + 'FullYear'](); + const tomorrow_d = () => tomorrow[_ + 'Date'](); + const tomorrow_m = () => tomorrow[_ + 'Month'](); + const tomorrow_y = () => tomorrow[_ + 'FullYear'](); + + if (today_y() === y && today_m() === m && today_d() === d) { + return short ? 'Tdy' : 'Today'; + } else if ( + yesterday_y() === y && + yesterday_m() === m && + yesterday_d() === d + ) { + return short ? 'Ysd' : 'Yesterday'; + } else if (tomorrow_y() === y && tomorrow_m() === m && tomorrow_d() === d) { + return short ? 'Tmw' : 'Tomorrow'; + } + return dayName; +}; + +/** + * Get the ISO 8601 week number + * Based on comments from + * http://techblog.procurios.nl/k/n618/news/view/33796/14863/Calculate-ISO-8601-week-and-year-in-javascript.html + * + * @param {Date} `date` + * @return {Number} + */ +const getWeek = (date: Date): number => { + // Remove time components of date + const targetThursday = new Date( + date.getFullYear(), + date.getMonth(), + date.getDate() + ); + + // Change date to Thursday same week + targetThursday.setDate( + targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3 + ); + + // Take January 4th as it is always in week 1 (see ISO 8601) + const firstThursday = new Date(targetThursday.getFullYear(), 0, 4); + + // Change date to Thursday same week + firstThursday.setDate( + firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3 + ); + + // Check if daylight-saving-time-switch occurred and correct for it + const ds = + targetThursday.getTimezoneOffset() - firstThursday.getTimezoneOffset(); + targetThursday.setHours(targetThursday.getHours() - ds); + + // Number of weeks between target Thursday and first Thursday + // @ts-ignore + const weekDiff = (targetThursday - firstThursday) / (86400000 * 7); + return 1 + Math.floor(weekDiff); +}; + +/** + * Get ISO-8601 numeric representation of the day of week + * 1 (for Monday) through 7 (for Sunday) + * + * @param {Date} `date` + * @return {Number} + */ +const getDayOfWeek = (date) => { + let dow = date.getDay(); + if (dow === 0) { + dow = 7; + } + return dow; +}; + +/** + * Get proper timezone abbreviation or timezone offset. + * + * This will fall back to `GMT+xxxx` if it does not recognize the + * timezone within the `timezone` RegEx above. Currently only common + * American and Australian timezone abbreviations are supported. + * + * @param {String | Date} date + * @return {String} + */ +export const formatTimezone = (date) => { + // @ts-ignore + return (String(date).match(timezone) || ['']) + .pop() + .replace(timezoneClip, '') + .replace(/GMT\+0000/g, 'UTC'); +}; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index c1315f53..436d966a 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -80,13 +80,11 @@ export * from './state/miniState'; export * from './ReactLike'; export * from './jsonClone'; export * from './tryCatch'; +export * from './dateFormat'; // @only-server export * from './logLevels'; -// @only-server -export * from './node-utils'; - // @only-server export * from './nodeLogger'; // @only-server diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f2a07412..42f1b974 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,8 +30,8 @@ importers: specifier: 4.15.2 version: 4.15.2(eslint@7.20.0)(typescript@4.8.2) bun-safe: - specifier: 1.0.5 - version: 1.0.5 + specifier: 1.0.7 + version: 1.0.7 bun-types: specifier: 1.0.26 version: 1.0.26 @@ -669,6 +669,9 @@ importers: packages/logstorm: dependencies: + '@powership/utils': + specifier: workspace:* + version: link:../utils plugin-hooks: specifier: 2.0.0 version: 2.0.0 @@ -1586,6 +1589,9 @@ importers: packages/utils: dependencies: + '@types/dateformat': + specifier: 5.0.2 + version: 5.0.2 '@types/fs-extra': specifier: 9.0.13 version: 9.0.13 @@ -1608,8 +1614,8 @@ importers: specifier: 6.2.1 version: 6.2.1 bun-safe: - specifier: 1.0.5 - version: 1.0.5 + specifier: 1.0.7 + version: 1.0.7 bun-types: specifier: 1.0.29 version: 1.0.29 @@ -1619,6 +1625,9 @@ importers: commander: specifier: 10.0.0 version: 10.0.0 + dateformat: + specifier: 5.0.3 + version: 5.0.3 dayjs: specifier: 1.11.6 version: 1.11.6 @@ -4794,6 +4803,10 @@ packages: resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} dev: true + /@types/dateformat@5.0.2: + resolution: {integrity: sha512-M95hNBMa/hnwErH+a+VOD/sYgTmo15OTYTM2Hr52/e0OdOuY+Crag+kd3/ioZrhg0WGbl9Sm3hR7UU+MH6rfOw==} + dev: false + /@types/deep-diff@1.0.5: resolution: {integrity: sha512-PQyNSy1YMZU1hgZA5tTYfHPpUAo9Dorn1PZho2/budQLfqLu3JIP37JAavnwYpR1S2yFZTXa3hxaE4ifGW5jaA==} dev: true @@ -6035,8 +6048,8 @@ packages: ieee754: 1.2.1 dev: true - /bun-safe@1.0.5: - resolution: {integrity: sha512-4Kuyxb/v1RAw9PU739WlKtFzvKH8EAFIFYqI6fRPc0z95nIGp/30O60aFIOb6DcOVw1lm+Q9M42Kf8OLmH0kbA==} + /bun-safe@1.0.7: + resolution: {integrity: sha512-vKAtLM2NlBfLGqe8H54SxRP8Obz/yZNiFBLZmE4s/iz2lqgocHe9ok5UXIp8M8+VXmClVOccEk4XGW479dHyGg==} hasBin: true dependencies: bun-types: 1.0.29 @@ -6309,6 +6322,11 @@ packages: resolution: {integrity: sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ==} dev: false + /dateformat@5.0.3: + resolution: {integrity: sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==} + engines: {node: '>=12.20'} + dev: false + /dayjs@1.11.6: resolution: {integrity: sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ==} dev: false