Skip to content

Commit

Permalink
feat: init project
Browse files Browse the repository at this point in the history
  • Loading branch information
dnldsht committed Sep 13, 2024
0 parents commit 0425b26
Show file tree
Hide file tree
Showing 13 changed files with 4,346 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
old
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"eslint.format.enable": true,
"eslint.quiet": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
5 changes: 5 additions & 0 deletions build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
externals: ['vue', 'luxon', 'defu'],
})
9 changes: 9 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import antfu from '@antfu/eslint-config'

export default antfu(
{
rules: {
curly: ['error', 'all'],
},
},
)
54 changes: 54 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "vue-luxon-next",
"type": "module",
"version": "1.0.0",
"private": false,
"description": "Easy DateTime formatting & parsing in Vue using Luxon",
"author": "Donald Shtjefni <[email protected]>",
"license": "MIT",
"repository": "[email protected]:dnldsht/v-luxon.git",
"keywords": [
"vue",
"vuejs",
"luxon",
"date",
"datetime",
"time",
"format",
"parse"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "unbuild",
"test": "vitest",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"dependencies": {
"defu": "^6.1.2",
"luxon": "^3.3.0"
},
"devDependencies": {
"@antfu/eslint-config": "^3.6.0",
"@types/luxon": "^3.3.0",
"@vitest/ui": "^2.1.0",
"eslint": "^9.10.0",
"ts-loader": "^9.4.4",
"typescript": "^5.1.3",
"unbuild": "^1.2.1",
"vitest": "^2.1.0",
"vue": "^3.3.4"
}
}
48 changes: 48 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { LuxonOptions } from './types'
import { DateTime } from 'luxon'

const localeFormats = {
full: { format: DateTime.DATETIME_FULL },
fulls: { format: DateTime.DATETIME_FULL_WITH_SECONDS },
huge: { format: DateTime.DATETIME_HUGE },
huges: { format: DateTime.DATETIME_HUGE_WITH_SECONDS },
med: { format: DateTime.DATETIME_MED },
meds: { format: DateTime.DATETIME_MED_WITH_SECONDS },
short: { format: DateTime.DATETIME_SHORT },
shorts: { format: DateTime.DATETIME_SHORT_WITH_SECONDS },
date_full: { format: DateTime.DATE_FULL },
date_huge: { format: DateTime.DATE_HUGE },
date_med: { format: DateTime.DATE_MED },
date_medd: { format: DateTime.DATE_MED_WITH_WEEKDAY },
date_short: { format: DateTime.DATE_SHORT },
time24: { format: DateTime.TIME_24_SIMPLE },
time24longoffset: { format: DateTime.TIME_24_WITH_LONG_OFFSET },
time24s: { format: DateTime.TIME_24_WITH_SECONDS },
time: { format: DateTime.TIME_SIMPLE },
times: { format: DateTime.TIME_WITH_SECONDS },
}

export const DEFAULT_SETTINGS: LuxonOptions = {
input: {
zone: 'utc',
format: 'iso',
},
output: {
format: 'short',
},
templates: {
server: {
zone: 'utc',
format: 'iso',
},
client: {
zone: 'local',
format: 'med',
},
inputdate: {
zone: 'client',
format: 'yyyy-MM-dd',
},
...localeFormats,
},
} as const
39 changes: 39 additions & 0 deletions src/format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { DateTime } from 'luxon'
import type { OutputOptions } from './types'

export default function format(dt: DateTime, options: OutputOptions) {
dt = dt.setZone(options.zone)
if (options.locale) {
dt = dt.setLocale(options.locale)
}

if (typeof options.format === 'object') {
return dt.toLocaleString(options.format)
}

switch (options.format) {
case 'relative':
return dt.toRelative(options.relative)
case 'sql':
return dt.toSQL(options.sql)
case 'iso':
return dt.toISO(options.iso)
case 'http':
return dt.toHTTP()
case 'jsdate':
return dt.toJSDate()
case 'rfc':
case 'rfc2822':
return dt.toRFC2822()
case 'millis':
return dt.toMillis()
case 'unix':
case 'seconds':
return dt.toSeconds()
default:
if (typeof options.format !== 'string') {
throw new TypeError(`Invalid format argument: ${options.format} must be a string`)
}
return dt.toFormat(options.format)
}
}
79 changes: 79 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { App } from 'vue'

import type { ParseOptions } from './parse'
import type { InputOptions, LuxonOptions, OutputOptions } from './types'
import { defu } from 'defu'
import { DEFAULT_SETTINGS } from './constants'
import formatDt from './format'
import parse from './parse'

type ParseInput = string | number | Date
type FormatInputOptions = string | Partial<InputOptions>
type FormatOutputOptions = string | Partial<OutputOptions>

export default {
install: (app: App, options: LuxonOptions = {}) => {
const luxonOptions = defu(options, DEFAULT_SETTINGS) as Required<LuxonOptions>

function extendInput(value: ParseInput, format?: FormatInputOptions): ParseOptions {
let options = typeof format === 'string' ? { format } : format
if (options === undefined) {
options = {}
}

let template: Partial<InputOptions> | undefined

if (typeof options.format === 'string' && options.format in luxonOptions.templates) {
const { format: templateFormat, ...rest } = luxonOptions.templates[options.format as keyof typeof luxonOptions.templates]
template = {
...rest,
...(templateFormat && typeof templateFormat === 'string' && { format: templateFormat }),
}
}

if (!options.format) {
if (value instanceof Date) {
options.format = 'jsdate'
}
else if (typeof value === 'number') {
options.format = 'millis'
}
else {
options.format = template?.format || luxonOptions.input.format
}
}

options.zone = options.zone || template?.zone || luxonOptions.input.zone
return { value, ...options } as ParseOptions
}

function extendOutput(format?: FormatOutputOptions): OutputOptions {
if (!format) {
format = luxonOptions.output
}
const base = typeof format === 'string' ? { format } : format

let template: Partial<OutputOptions> | undefined

if (typeof base.format === 'string' && base.format in luxonOptions.templates) {
template = luxonOptions.templates[base.format as keyof typeof luxonOptions.templates] as Partial<OutputOptions>
}

const m = defu(template, base, luxonOptions.output)
return m as OutputOptions
}

function luxonParse(value: ParseInput, format?: FormatInputOptions) {
return parse(extendInput(value, format))
}

function luxonFormat(value: ParseInput, format?: FormatOutputOptions, inputFormat?: FormatInputOptions) {
const dt = luxonParse(value, inputFormat)
return formatDt(dt, extendOutput(format))
}

app.config.globalProperties.$lp = luxonParse
app.config.globalProperties.$lf = luxonFormat
app.config.globalProperties.$luxon = luxonFormat
},
}
61 changes: 61 additions & 0 deletions src/parse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { Zone } from 'luxon'
import { DateTime } from 'luxon'

interface JsDateParseOptions {
value: Date
format: 'jsdate'
zone?: string | Zone
}

interface NumberParseOptions {
value: number
format: 'millis' | 'seconds' | 'unix'
zone?: string | Zone
}

interface StringParseOptions {
value: string
format: 'sql' | 'iso' | 'http' | 'rfc2822' | string
zone?: string | Zone
}

export type ParseOptions = StringParseOptions | NumberParseOptions | JsDateParseOptions

export default function parse(options: ParseOptions): DateTime {
const { value, format, zone } = options

switch (format) {
case 'sql':
return DateTime.fromSQL(value, { zone })
case 'iso':
return DateTime.fromISO(value, { zone })
case 'http':
return DateTime.fromHTTP(value, { zone })
case 'jsdate':
if (!(value instanceof Date)) {
throw new TypeError(`Value must be an instance of Date, received ${value === null ? 'null' : typeof value}`)
}
return DateTime.fromJSDate(value, { zone })
case 'rfc2822':
return DateTime.fromRFC2822(value, { zone })
case 'millis':
if (typeof value !== 'number') {
throw new TypeError(`Value must be an integer, received ${value === null ? 'null' : typeof value}`)
}
return DateTime.fromMillis(value, { zone })
case 'seconds':
case 'unix':
if (typeof value !== 'number') {
throw new TypeError(`Value must be an integer, received ${value === null ? 'null' : typeof value}`)
}
return DateTime.fromSeconds(value, { zone })
default:
if (typeof format !== 'string') {
throw new TypeError(`Invalid format argument: ${format} must be a string`)
}
if (typeof value !== 'string') {
throw new TypeError(`Value must be a string, received ${value === null ? 'null' : typeof value}`)
}
return DateTime.fromFormat(value, format, { zone })
}
}
22 changes: 22 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { DateTimeFormatOptions, ToISOTimeOptions, ToRelativeOptions, ToSQLOptions, Zone } from 'luxon'

export interface InputOptions {
zone?: string | Zone
format: string
}

export interface OutputOptions {
zone?: string | Zone
locale?: string
format?: string | DateTimeFormatOptions | Intl.DateTimeFormatOptions
relative?: ToRelativeOptions
sql?: ToSQLOptions
iso?: ToISOTimeOptions

}

export interface LuxonOptions {
templates?: Record<string, OutputOptions>
input?: InputOptions
output?: OutputOptions
}
10 changes: 10 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"esModuleInterop": true
},
"include": ["src", "vitest.config.ts"]
}
8 changes: 8 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
},
})
Loading

0 comments on commit 0425b26

Please sign in to comment.