2024-02-18 00:45:01 +07:00
import path from "path"
import { visit } from "unist-util-visit"
import { Root } from "hast"
import { VFile } from "vfile"
2023-06-17 09:41:59 +07:00
import { QuartzEmitterPlugin } from "../types"
2023-06-08 12:27:32 +07:00
import { QuartzComponentProps } from "../../components/types"
2023-07-01 14:03:01 +07:00
import HeaderConstructor from "../../components/Header"
2023-06-12 13:46:38 +07:00
import BodyConstructor from "../../components/Body"
2023-07-01 14:03:01 +07:00
import { pageResources , renderPage } from "../../components/renderPage"
import { FullPageLayout } from "../../cfg"
2024-02-18 00:45:01 +07:00
import { Argv } from "../../util/ctx"
import { FilePath , isRelativeURL , joinSegments , pathToRoot } from "../../util/path"
2023-07-26 13:37:24 +07:00
import { defaultContentPageLayout , sharedPageComponents } from "../../../quartz.layout"
import { Content } from "../../components"
2023-09-22 23:43:34 +07:00
import chalk from "chalk"
2024-01-19 01:56:14 +07:00
import { write } from "./helpers"
2024-02-09 22:07:32 +07:00
import DepGraph from "../../depgraph"
2023-06-02 04:35:31 +07:00
2024-02-18 00:45:01 +07:00
// get all the dependencies for the markdown file
// eg. images, scripts, stylesheets, transclusions
const parseDependencies = ( argv : Argv , hast : Root , file : VFile ) : string [ ] = > {
const dependencies : string [ ] = [ ]
visit ( hast , "element" , ( elem ) : void = > {
let ref : string | null = null
if (
[ "script" , "img" , "audio" , "video" , "source" , "iframe" ] . includes ( elem . tagName ) &&
elem ? . properties ? . src
) {
ref = elem . properties . src . toString ( )
} else if ( [ "a" , "link" ] . includes ( elem . tagName ) && elem ? . properties ? . href ) {
// transclusions will create a tags with relative hrefs
ref = elem . properties . href . toString ( )
}
// if it is a relative url, its a local file and we need to add
// it to the dependency graph. otherwise, ignore
if ( ref === null || ! isRelativeURL ( ref ) ) {
return
}
let fp = path . join ( file . data . filePath ! , path . relative ( argv . directory , ref ) ) . replace ( /\\/g , "/" )
// markdown files have the .md extension stripped in hrefs, add it back here
if ( ! fp . split ( "/" ) . pop ( ) ? . includes ( "." ) ) {
fp += ".md"
}
dependencies . push ( fp )
} )
return dependencies
}
2023-07-26 13:37:24 +07:00
export const ContentPage : QuartzEmitterPlugin < Partial < FullPageLayout > > = ( userOpts ) = > {
const opts : FullPageLayout = {
. . . sharedPageComponents ,
. . . defaultContentPageLayout ,
pageBody : Content ( ) ,
. . . userOpts ,
2023-06-04 02:07:19 +07:00
}
2023-07-26 13:37:24 +07:00
const { head : Head , header , beforeBody , pageBody , left , right , footer : Footer } = opts
2023-06-12 13:46:38 +07:00
const Header = HeaderConstructor ( )
const Body = BodyConstructor ( )
2023-06-12 13:26:43 +07:00
return {
name : "ContentPage" ,
getQuartzComponents() {
2023-07-26 13:37:24 +07:00
return [ Head , Header , Body , . . . header , . . . beforeBody , pageBody , . . . left , . . . right , Footer ]
2023-06-12 13:26:43 +07:00
} ,
2024-02-09 22:07:32 +07:00
async getDependencyGraph ( ctx , content , _resources ) {
const graph = new DepGraph < FilePath > ( )
2024-02-18 00:45:01 +07:00
for ( const [ tree , file ] of content ) {
2024-02-09 22:07:32 +07:00
const sourcePath = file . data . filePath !
const slug = file . data . slug !
graph . addEdge ( sourcePath , joinSegments ( ctx . argv . output , slug + ".html" ) as FilePath )
2024-02-18 00:45:01 +07:00
parseDependencies ( ctx . argv , tree as Root , file ) . forEach ( ( dep ) = > {
graph . addEdge ( dep as FilePath , sourcePath )
} )
2024-02-09 22:07:32 +07:00
}
return graph
} ,
2024-01-19 01:56:14 +07:00
async emit ( ctx , content , resources ) : Promise < FilePath [ ] > {
2023-07-24 07:07:19 +07:00
const cfg = ctx . cfg . configuration
2023-07-13 14:19:35 +07:00
const fps : FilePath [ ] = [ ]
2023-07-23 07:27:41 +07:00
const allFiles = content . map ( ( c ) = > c [ 1 ] . data )
2023-09-22 23:43:34 +07:00
let containsIndex = false
2023-06-12 13:26:43 +07:00
for ( const [ tree , file ] of content ) {
2023-08-20 05:52:25 +07:00
const slug = file . data . slug !
2023-09-22 23:43:34 +07:00
if ( slug === "index" ) {
containsIndex = true
}
2023-09-13 11:29:57 +07:00
const externalResources = pageResources ( pathToRoot ( slug ) , resources )
2023-06-12 13:26:43 +07:00
const componentData : QuartzComponentProps = {
fileData : file.data ,
2023-07-01 14:03:01 +07:00
externalResources ,
2023-06-12 13:26:43 +07:00
cfg ,
children : [ ] ,
2023-06-20 10:37:45 +07:00
tree ,
2023-07-23 07:27:41 +07:00
allFiles ,
2023-06-12 13:26:43 +07:00
}
2023-06-08 12:27:32 +07:00
2024-02-05 11:57:10 +07:00
const content = renderPage ( cfg , slug , componentData , opts , externalResources )
2024-01-19 01:56:14 +07:00
const fp = await write ( {
ctx ,
2023-07-01 14:03:01 +07:00
content ,
2023-08-20 05:52:25 +07:00
slug ,
2023-06-12 13:26:43 +07:00
ext : ".html" ,
} )
2023-06-01 04:01:23 +07:00
2023-06-12 13:26:43 +07:00
fps . push ( fp )
}
2023-09-22 23:43:34 +07:00
2024-02-09 22:07:32 +07:00
if ( ! containsIndex && ! ctx . argv . fastRebuild ) {
2023-09-22 23:43:34 +07:00
console . log (
chalk . yellow (
` \ nWarning: you seem to be missing an \` index.md \` home page file at the root of your \` ${ ctx . argv . directory } \` folder. This may cause errors when deploying. ` ,
) ,
)
}
2023-06-12 13:26:43 +07:00
return fps
2023-07-23 07:27:41 +07:00
} ,
2023-06-01 04:01:23 +07:00
}
}