2023-05-30 08:02:20 -07:00
import fs from "fs"
import { Repository } from "@napi-rs/simple-git"
import { QuartzTransformerPlugin } from "../types"
2025-03-19 05:47:35 +01:00
import path from "path"
2025-05-28 10:40:51 +02:00
import { styleText } from "util"
2023-05-30 08:02:20 -07:00
export interface Options {
2023-07-22 17:27:41 -07:00
priority : ( "frontmatter" | "git" | "filesystem" ) [ ]
2023-05-30 08:02:20 -07:00
}
const defaultOptions : Options = {
2023-07-22 17:27:41 -07:00
priority : [ "frontmatter" , "git" , "filesystem" ] ,
2023-05-30 08:02:20 -07:00
}
2025-06-01 22:42:37 -07:00
// YYYY-MM-DD
const iso8601DateOnlyRegex = /^\d{4}-\d{2}-\d{2}$/
2024-01-28 21:27:16 -08:00
function coerceDate ( fp : string , d : any ) : Date {
2025-06-01 22:42:37 -07:00
// check ISO8601 date-only format
// we treat this one as local midnight as the normal
// js date ctor treats YYYY-MM-DD as UTC midnight
if ( typeof d === "string" && iso8601DateOnlyRegex . test ( d ) ) {
d = ` ${ d } T00:00:00 `
}
2024-01-28 21:27:16 -08:00
const dt = new Date ( d )
const invalidDate = isNaN ( dt . getTime ( ) ) || dt . getTime ( ) === 0
if ( invalidDate && d !== undefined ) {
console . log (
2025-05-28 10:40:51 +02:00
styleText (
"yellow" ,
2024-01-28 21:27:16 -08:00
` \ nWarning: found invalid date " ${ d } " in \` ${ fp } \` . Supported formats: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format ` ,
) ,
)
}
return invalidDate ? new Date ( ) : dt
}
type MaybeDate = undefined | string | number
2024-08-08 18:28:13 -07:00
export const CreatedModifiedDate : QuartzTransformerPlugin < Partial < Options > > = ( userOpts ) = > {
2023-06-11 23:26:43 -07:00
const opts = { . . . defaultOptions , . . . userOpts }
return {
name : "CreatedModifiedDate" ,
2025-03-16 14:17:31 -07:00
markdownPlugins ( ctx ) {
2023-06-11 23:26:43 -07:00
return [
( ) = > {
let repo : Repository | undefined = undefined
2025-03-19 05:47:35 +01:00
let repositoryWorkdir : string
2025-03-18 08:56:06 -07:00
if ( opts . priority . includes ( "git" ) ) {
try {
repo = Repository . discover ( ctx . argv . directory )
2025-03-18 21:48:24 -07:00
repositoryWorkdir = repo . workdir ( ) ? ? ctx . argv . directory
2025-03-18 08:56:06 -07:00
} catch ( e ) {
console . log (
2025-05-28 10:40:51 +02:00
styleText (
"yellow" ,
` \ nWarning: couldn't find git repository for ${ ctx . argv . directory } ` ,
) ,
2025-03-18 08:56:06 -07:00
)
}
}
2023-06-11 23:26:43 -07:00
return async ( _tree , file ) = > {
2024-01-28 21:27:16 -08:00
let created : MaybeDate = undefined
let modified : MaybeDate = undefined
let published : MaybeDate = undefined
2023-05-30 08:02:20 -07:00
2025-03-16 14:17:31 -07:00
const fp = file . data . relativePath !
2025-03-18 08:56:06 -07:00
const fullFp = file . data . filePath !
2023-06-11 23:26:43 -07:00
for ( const source of opts . priority ) {
if ( source === "filesystem" ) {
2023-09-22 10:04:37 -07:00
const st = await fs . promises . stat ( fullFp )
2024-01-28 21:27:16 -08:00
created || = st . birthtimeMs
modified || = st . mtimeMs
2023-06-11 23:26:43 -07:00
} else if ( source === "frontmatter" && file . data . frontmatter ) {
2024-12-23 15:00:26 -05:00
created || = file . data . frontmatter . created as MaybeDate
modified || = file . data . frontmatter . modified as MaybeDate
published || = file . data . frontmatter . published as MaybeDate
2025-03-18 08:56:06 -07:00
} else if ( source === "git" && repo ) {
2024-01-02 18:23:28 +01:00
try {
2025-03-19 05:47:35 +01:00
const relativePath = path . relative ( repositoryWorkdir , fullFp )
modified || = await repo . getFileLatestModifiedDateAsync ( relativePath )
2024-01-02 18:23:28 +01:00
} catch {
console . log (
2025-05-28 10:40:51 +02:00
styleText (
"yellow" ,
2025-03-18 08:56:06 -07:00
` \ nWarning: ${ file . data . filePath ! } isn't yet tracked by git, dates will be inaccurate ` ,
2024-01-02 18:23:28 +01:00
) ,
)
}
2023-05-30 08:02:20 -07:00
}
}
2023-06-11 23:26:43 -07:00
file . data . dates = {
2024-01-28 21:27:16 -08:00
created : coerceDate ( fp , created ) ,
modified : coerceDate ( fp , modified ) ,
published : coerceDate ( fp , published ) ,
2023-06-11 23:26:43 -07:00
}
2023-05-30 08:02:20 -07:00
}
2023-07-22 17:27:41 -07:00
} ,
2023-06-11 23:26:43 -07:00
]
} ,
2023-05-30 08:02:20 -07:00
}
}
2023-07-22 17:27:41 -07:00
declare module "vfile" {
2023-05-30 08:02:20 -07:00
interface DataMap {
dates : {
created : Date
modified : Date
published : Date
}
}
}