mirror of
https://github.com/jacobtread/obsidian-timekeep.git
synced 2026-07-22 05:12:14 +00:00
refactor: replace zod for valibot
For tree shaking because currently zod has a bunch of extra stuff that shows up negatively on the scorecard
This commit is contained in:
parent
ee3dfc7101
commit
fa1fedbb01
5 changed files with 79 additions and 50 deletions
|
|
@ -23,7 +23,7 @@
|
|||
"p-limit": "^7.3.0",
|
||||
"pdfmake": "^0.3.7",
|
||||
"uuid": "^11.0.5",
|
||||
"zod": "^4.3.6"
|
||||
"valibot": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.19.37",
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ importers:
|
|||
uuid:
|
||||
specifier: ^11.0.5
|
||||
version: 11.1.1
|
||||
zod:
|
||||
specifier: ^4.3.6
|
||||
version: 4.4.3
|
||||
valibot:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0(typescript@6.0.2)
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^20.19.37
|
||||
|
|
@ -931,6 +931,14 @@ packages:
|
|||
resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==}
|
||||
hasBin: true
|
||||
|
||||
valibot@1.4.0:
|
||||
resolution: {integrity: sha512-iC/x7fVcSyOwlm/VSt7RlHnzNGLGvR9GnxdifUeWoCJo0q4ZZvrVkIHC6faTlkxG47I2Y4UrFquPuVHCrOnrLg==}
|
||||
peerDependencies:
|
||||
typescript: '>=5'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
vite@8.0.13:
|
||||
resolution: {integrity: sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
|
|
@ -1047,9 +1055,6 @@ packages:
|
|||
resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==}
|
||||
engines: {node: '>=12.20'}
|
||||
|
||||
zod@4.4.3:
|
||||
resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@babel/helper-string-parser@7.27.1': {}
|
||||
|
|
@ -1753,6 +1758,10 @@ snapshots:
|
|||
|
||||
uuid@11.1.1: {}
|
||||
|
||||
valibot@1.4.0(typescript@6.0.2):
|
||||
optionalDependencies:
|
||||
typescript: 6.0.2
|
||||
|
||||
vite@8.0.13(@types/node@20.19.41):
|
||||
dependencies:
|
||||
lightningcss: 1.32.0
|
||||
|
|
@ -1809,5 +1818,3 @@ snapshots:
|
|||
sax: 1.6.0
|
||||
|
||||
yocto-queue@1.2.2: {}
|
||||
|
||||
zod@4.4.3: {}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { safeParse } from "valibot";
|
||||
|
||||
import { isEmptyString } from "@/utils/text";
|
||||
|
||||
import { TIMEKEEP, Timekeep, stripTimekeepRuntimeData } from "@/timekeep/schema";
|
||||
|
|
@ -33,15 +35,22 @@ export function load(value: string): LoadResult {
|
|||
}
|
||||
|
||||
// Parse the data against the schema
|
||||
const timekeepResult = TIMEKEEP.safeParse(parsedValue);
|
||||
const timekeepResult = safeParse(TIMEKEEP, parsedValue);
|
||||
if (!timekeepResult.success) {
|
||||
const error = timekeepResult.issues
|
||||
.map((issue) => {
|
||||
const path = issue.path?.map((p) => p.key).join(".") ?? "root";
|
||||
return `${path}: ${issue.message}`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: timekeepResult.error.toString(),
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
const timekeep = timekeepResult.data;
|
||||
const timekeep = timekeepResult.output;
|
||||
return { success: true, timekeep };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import moment from "moment";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { parse } from "valibot";
|
||||
import { expect, it, describe, vi } from "vitest";
|
||||
|
||||
import { TIMEKEEP } from "@/timekeep/schema";
|
||||
|
|
@ -36,7 +37,7 @@ describe("schema transform", () => {
|
|||
},
|
||||
],
|
||||
};
|
||||
const result = TIMEKEEP.parse(input);
|
||||
const result = parse(TIMEKEEP, input);
|
||||
|
||||
expect(result).toEqual({
|
||||
entries: [
|
||||
|
|
|
|||
|
|
@ -1,75 +1,87 @@
|
|||
import moment from "moment";
|
||||
import moment, { Moment } from "moment";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { z } from "zod";
|
||||
import * as v from "valibot";
|
||||
|
||||
/*
|
||||
* This file contains the strict schema for parsing timekeep data
|
||||
* it also contains the types for each timekeep structure.
|
||||
*/
|
||||
|
||||
type TimeEntryGroupBase = z.output<typeof TIME_ENTRY_GROUP_BASE>;
|
||||
type RawTimeEntrySingle = v.InferOutput<typeof TIME_ENTRY_SINGLE>;
|
||||
type RawTimeEntryGroupBase = v.InferOutput<typeof TIME_ENTRY_GROUP_BASE>;
|
||||
|
||||
// Type aliases from inferred zod types
|
||||
export type TimeEntrySingle = z.output<typeof TIME_ENTRY_SINGLE>;
|
||||
export type TimeEntryGroup = TimeEntryGroupBase & {
|
||||
export type TimeEntrySingle = RawTimeEntrySingle;
|
||||
export type TimeEntryGroup = RawTimeEntryGroupBase & {
|
||||
id: string;
|
||||
subEntries: TimeEntry[];
|
||||
};
|
||||
export type TimeEntry = TimeEntrySingle | TimeEntryGroup;
|
||||
export type Timekeep = z.output<typeof TIMEKEEP>;
|
||||
|
||||
export type Timekeep = v.InferOutput<typeof TIMEKEEP>;
|
||||
|
||||
const strToMoment = (value: string | null): Moment | null =>
|
||||
value === null ? null : moment(value);
|
||||
|
||||
// Schema for a time entry with no children
|
||||
const TIME_ENTRY_SINGLE = z
|
||||
.object({
|
||||
const TIME_ENTRY_SINGLE = v.pipe(
|
||||
v.object({
|
||||
// Name of the entry
|
||||
name: z.string(),
|
||||
name: v.string(),
|
||||
|
||||
// Start time for this entry
|
||||
startTime: z
|
||||
.string()
|
||||
.nullable()
|
||||
.transform((value) => (value === null ? null : moment(value))),
|
||||
startTime: v.pipe(v.nullable(v.string()), v.transform(strToMoment)),
|
||||
|
||||
// End time for this entry, null when this entry is not finished
|
||||
endTime: z
|
||||
.string()
|
||||
.nullable()
|
||||
.transform((value) => (value === null ? null : moment(value))),
|
||||
endTime: v.pipe(v.nullable(v.string()), v.transform(strToMoment)),
|
||||
|
||||
// Single entries have no children
|
||||
subEntries: z.null(),
|
||||
})
|
||||
subEntries: v.null(),
|
||||
}),
|
||||
// At runtime a unique ID is inserted
|
||||
.transform((entry) => ({
|
||||
v.transform((entry) => ({
|
||||
...entry,
|
||||
id: uuid(),
|
||||
}));
|
||||
}))
|
||||
);
|
||||
|
||||
// Schema for a time entry with children (Base portion, separate portion is required for recursion)
|
||||
const TIME_ENTRY_GROUP_BASE = z.object({
|
||||
name: z.string(),
|
||||
startTime: z.null(),
|
||||
endTime: z.null(),
|
||||
const TIME_ENTRY_GROUP_BASE = v.object({
|
||||
name: v.string(),
|
||||
startTime: v.null(),
|
||||
endTime: v.null(),
|
||||
// Optional field to indicate the entry is collapsed
|
||||
collapsed: z.boolean().optional(),
|
||||
collapsed: v.optional(v.boolean()),
|
||||
// Optional field to indicate the entry should stay as a group when non-started and should only create
|
||||
// sub entries when starting
|
||||
folder: z.boolean().optional(),
|
||||
folder: v.optional(v.boolean()),
|
||||
});
|
||||
|
||||
// Schema for a time entry group
|
||||
const TIME_ENTRY_GROUP: z.ZodType<TimeEntryGroup> = TIME_ENTRY_GROUP_BASE.extend({
|
||||
subEntries: z.lazy(() => z.array(z.union([TIME_ENTRY_SINGLE, TIME_ENTRY_GROUP]))),
|
||||
})
|
||||
const TIME_ENTRY_GROUP = v.pipe(
|
||||
v.object({
|
||||
...TIME_ENTRY_GROUP_BASE.entries,
|
||||
subEntries: v.lazy(() => TIME_ENTRY_ARRAY),
|
||||
}),
|
||||
// At runtime a unique ID is inserted
|
||||
/* istanbul ignore next */
|
||||
.transform((entry) => ({
|
||||
v.transform((entry) => ({
|
||||
...entry,
|
||||
id: uuid(),
|
||||
}));
|
||||
}))
|
||||
);
|
||||
|
||||
// Schema for time entries
|
||||
const TIME_ENTRY = z.union([TIME_ENTRY_SINGLE, TIME_ENTRY_GROUP]);
|
||||
const TIME_ENTRY: v.GenericSchema<TimeEntry> = v.union([
|
||||
TIME_ENTRY_SINGLE,
|
||||
TIME_ENTRY_GROUP,
|
||||
// Forcefully cast to the type as the type inference is unable to
|
||||
// properly type this recursive structure
|
||||
]) as unknown as v.GenericSchema<TimeEntry>;
|
||||
|
||||
const TIME_ENTRY_ARRAY = v.array(TIME_ENTRY);
|
||||
|
||||
// Schema for an entire timekeep
|
||||
export const TIMEKEEP = z.object({
|
||||
entries: z.array(TIME_ENTRY),
|
||||
export const TIMEKEEP = v.object({
|
||||
entries: TIME_ENTRY_ARRAY,
|
||||
});
|
||||
|
||||
export function defaultTimekeep(): Timekeep {
|
||||
|
|
|
|||
Loading…
Reference in a new issue