mirror of
https://github.com/jackyzha0/quartz.git
synced 2025-05-18 14:34:23 +02:00
Compare commits
4 Commits
7681a86815
...
1bb6f09db1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1bb6f09db1 | ||
![]() |
40a72eba44 | ||
![]() |
bdc15ecb05 | ||
![]() |
5ccb9ddc70 |
@ -221,12 +221,26 @@ export type QuartzEmitterPlugin<Options extends OptionType = undefined> = (
|
|||||||
|
|
||||||
export type QuartzEmitterPluginInstance = {
|
export type QuartzEmitterPluginInstance = {
|
||||||
name: string
|
name: string
|
||||||
emit(ctx: BuildCtx, content: ProcessedContent[], resources: StaticResources): Promise<FilePath[]>
|
emit(
|
||||||
|
ctx: BuildCtx,
|
||||||
|
content: ProcessedContent[],
|
||||||
|
resources: StaticResources,
|
||||||
|
): Promise<FilePath[]> | AsyncGenerator<FilePath>
|
||||||
|
partialEmit?(
|
||||||
|
ctx: BuildCtx,
|
||||||
|
content: ProcessedContent[],
|
||||||
|
resources: StaticResources,
|
||||||
|
changeEvents: ChangeEvent[],
|
||||||
|
): Promise<FilePath[]> | AsyncGenerator<FilePath> | null
|
||||||
getQuartzComponents(ctx: BuildCtx): QuartzComponent[]
|
getQuartzComponents(ctx: BuildCtx): QuartzComponent[]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
An emitter plugin must define a `name` field, an `emit` function, and a `getQuartzComponents` function. `emit` is responsible for looking at all the parsed and filtered content and then appropriately creating files and returning a list of paths to files the plugin created.
|
An emitter plugin must define a `name` field, an `emit` function, and a `getQuartzComponents` function. It can optionally implement a `partialEmit` function for incremental builds.
|
||||||
|
|
||||||
|
- `emit` is responsible for looking at all the parsed and filtered content and then appropriately creating files and returning a list of paths to files the plugin created.
|
||||||
|
- `partialEmit` is an optional function that enables incremental builds. It receives information about which files have changed (`changeEvents`) and can selectively rebuild only the necessary files. This is useful for optimizing build times in development mode. If `partialEmit` is undefined, it will default to the `emit` function.
|
||||||
|
- `getQuartzComponents` declares which Quartz components the emitter uses to construct its pages.
|
||||||
|
|
||||||
Creating new files can be done via regular Node [fs module](https://nodejs.org/api/fs.html) (i.e. `fs.cp` or `fs.writeFile`) or via the `write` function in `quartz/plugins/emitters/helpers.ts` if you are creating files that contain text. `write` has the following signature:
|
Creating new files can be done via regular Node [fs module](https://nodejs.org/api/fs.html) (i.e. `fs.cp` or `fs.writeFile`) or via the `write` function in `quartz/plugins/emitters/helpers.ts` if you are creating files that contain text. `write` has the following signature:
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ If you prefer instructions in a video format you can try following Nicole van de
|
|||||||
## 🔧 Features
|
## 🔧 Features
|
||||||
|
|
||||||
- [[Obsidian compatibility]], [[full-text search]], [[graph view]], note transclusion, [[wikilinks]], [[backlinks]], [[features/Latex|Latex]], [[syntax highlighting]], [[popover previews]], [[Docker Support]], [[i18n|internationalization]], [[comments]] and [many more](./features/) right out of the box
|
- [[Obsidian compatibility]], [[full-text search]], [[graph view]], note transclusion, [[wikilinks]], [[backlinks]], [[features/Latex|Latex]], [[syntax highlighting]], [[popover previews]], [[Docker Support]], [[i18n|internationalization]], [[comments]] and [many more](./features/) right out of the box
|
||||||
- Hot-reload for both configuration and content
|
- Hot-reload on configuration edits and incremental rebuilds for content edits
|
||||||
- Simple JSX layouts and [[creating components|page components]]
|
- Simple JSX layouts and [[creating components|page components]]
|
||||||
- [[SPA Routing|Ridiculously fast page loads]] and tiny bundle sizes
|
- [[SPA Routing|Ridiculously fast page loads]] and tiny bundle sizes
|
||||||
- Fully-customizable parsing, filtering, and page generation through [[making plugins|plugins]]
|
- Fully-customizable parsing, filtering, and page generation through [[making plugins|plugins]]
|
||||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -35,6 +35,7 @@
|
|||||||
"mdast-util-to-hast": "^13.2.0",
|
"mdast-util-to-hast": "^13.2.0",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
"micromorph": "^0.4.5",
|
"micromorph": "^0.4.5",
|
||||||
|
"minimatch": "^10.0.1",
|
||||||
"pixi.js": "^8.8.1",
|
"pixi.js": "^8.8.1",
|
||||||
"preact": "^10.26.4",
|
"preact": "^10.26.4",
|
||||||
"preact-render-to-string": "^6.5.13",
|
"preact-render-to-string": "^6.5.13",
|
||||||
@ -5254,6 +5255,7 @@
|
|||||||
"version": "10.0.1",
|
"version": "10.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
|
||||||
"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
|
"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^2.0.1"
|
"brace-expansion": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"mdast-util-to-hast": "^13.2.0",
|
"mdast-util-to-hast": "^13.2.0",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
"micromorph": "^0.4.5",
|
"micromorph": "^0.4.5",
|
||||||
|
"minimatch": "^10.0.1",
|
||||||
"pixi.js": "^8.8.1",
|
"pixi.js": "^8.8.1",
|
||||||
"preact": "^10.26.4",
|
"preact": "^10.26.4",
|
||||||
"preact-render-to-string": "^6.5.13",
|
"preact-render-to-string": "^6.5.13",
|
||||||
|
@ -20,6 +20,7 @@ import { Mutex } from "async-mutex"
|
|||||||
import { getStaticResourcesFromPlugins } from "./plugins"
|
import { getStaticResourcesFromPlugins } from "./plugins"
|
||||||
import { randomIdNonSecure } from "./util/random"
|
import { randomIdNonSecure } from "./util/random"
|
||||||
import { ChangeEvent } from "./plugins/types"
|
import { ChangeEvent } from "./plugins/types"
|
||||||
|
import { minimatch } from "minimatch"
|
||||||
|
|
||||||
type ContentMap = Map<
|
type ContentMap = Map<
|
||||||
FilePath,
|
FilePath,
|
||||||
@ -117,11 +118,23 @@ async function startWatching(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gitIgnoredMatcher = await isGitIgnored()
|
||||||
const buildData: BuildData = {
|
const buildData: BuildData = {
|
||||||
ctx,
|
ctx,
|
||||||
mut,
|
mut,
|
||||||
contentMap,
|
contentMap,
|
||||||
ignored: await isGitIgnored(),
|
ignored: (path) => {
|
||||||
|
if (gitIgnoredMatcher(path)) return true
|
||||||
|
const pathStr = path.toString()
|
||||||
|
for (const pattern of cfg.configuration.ignorePatterns) {
|
||||||
|
if (minimatch(pathStr, pattern)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
|
||||||
changesSinceLastBuild: {},
|
changesSinceLastBuild: {},
|
||||||
lastBuildMs: 0,
|
lastBuildMs: 0,
|
||||||
}
|
}
|
||||||
@ -137,17 +150,17 @@ async function startWatching(
|
|||||||
.on("add", (fp) => {
|
.on("add", (fp) => {
|
||||||
if (buildData.ignored(fp)) return
|
if (buildData.ignored(fp)) return
|
||||||
changes.push({ path: fp as FilePath, type: "add" })
|
changes.push({ path: fp as FilePath, type: "add" })
|
||||||
rebuild(changes, clientRefresh, buildData)
|
void rebuild(changes, clientRefresh, buildData)
|
||||||
})
|
})
|
||||||
.on("change", (fp) => {
|
.on("change", (fp) => {
|
||||||
if (buildData.ignored(fp)) return
|
if (buildData.ignored(fp)) return
|
||||||
changes.push({ path: fp as FilePath, type: "change" })
|
changes.push({ path: fp as FilePath, type: "change" })
|
||||||
rebuild(changes, clientRefresh, buildData)
|
void rebuild(changes, clientRefresh, buildData)
|
||||||
})
|
})
|
||||||
.on("unlink", (fp) => {
|
.on("unlink", (fp) => {
|
||||||
if (buildData.ignored(fp)) return
|
if (buildData.ignored(fp)) return
|
||||||
changes.push({ path: fp as FilePath, type: "delete" })
|
changes.push({ path: fp as FilePath, type: "delete" })
|
||||||
rebuild(changes, clientRefresh, buildData)
|
void rebuild(changes, clientRefresh, buildData)
|
||||||
})
|
})
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
@ -162,6 +175,7 @@ async function rebuild(changes: ChangeEvent[], clientRefresh: () => void, buildD
|
|||||||
const buildId = randomIdNonSecure()
|
const buildId = randomIdNonSecure()
|
||||||
ctx.buildId = buildId
|
ctx.buildId = buildId
|
||||||
buildData.lastBuildMs = new Date().getTime()
|
buildData.lastBuildMs = new Date().getTime()
|
||||||
|
const numChangesInBuild = changes.length
|
||||||
const release = await mut.acquire()
|
const release = await mut.acquire()
|
||||||
|
|
||||||
// if there's another build after us, release and let them do it
|
// if there's another build after us, release and let them do it
|
||||||
@ -180,16 +194,19 @@ async function rebuild(changes: ChangeEvent[], clientRefresh: () => void, buildD
|
|||||||
}
|
}
|
||||||
|
|
||||||
const staticResources = getStaticResourcesFromPlugins(ctx)
|
const staticResources = getStaticResourcesFromPlugins(ctx)
|
||||||
|
const pathsToParse: FilePath[] = []
|
||||||
for (const [fp, type] of Object.entries(changesSinceLastBuild)) {
|
for (const [fp, type] of Object.entries(changesSinceLastBuild)) {
|
||||||
if (type === "delete" || path.extname(fp) !== ".md") continue
|
if (type === "delete" || path.extname(fp) !== ".md") continue
|
||||||
const fullPath = joinSegments(argv.directory, toPosixPath(fp)) as FilePath
|
const fullPath = joinSegments(argv.directory, toPosixPath(fp)) as FilePath
|
||||||
const parsed = await parseMarkdown(ctx, [fullPath])
|
pathsToParse.push(fullPath)
|
||||||
for (const content of parsed) {
|
}
|
||||||
contentMap.set(content[1].data.relativePath!, {
|
|
||||||
type: "markdown",
|
const parsed = await parseMarkdown(ctx, pathsToParse)
|
||||||
content,
|
for (const content of parsed) {
|
||||||
})
|
contentMap.set(content[1].data.relativePath!, {
|
||||||
}
|
type: "markdown",
|
||||||
|
content,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// update state using changesSinceLastBuild
|
// update state using changesSinceLastBuild
|
||||||
@ -265,7 +282,7 @@ async function rebuild(changes: ChangeEvent[], clientRefresh: () => void, buildD
|
|||||||
|
|
||||||
console.log(`Emitted ${emittedFiles} files to \`${argv.output}\` in ${perf.timeSince("rebuild")}`)
|
console.log(`Emitted ${emittedFiles} files to \`${argv.output}\` in ${perf.timeSince("rebuild")}`)
|
||||||
console.log(chalk.green(`Done rebuilding in ${perf.timeSince()}`))
|
console.log(chalk.green(`Done rebuilding in ${perf.timeSince()}`))
|
||||||
changes.length = 0
|
changes.splice(0, numChangesInBuild)
|
||||||
clientRefresh()
|
clientRefresh()
|
||||||
release()
|
release()
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,9 @@ export class QuartzLogger {
|
|||||||
private readonly spinnerChars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
private readonly spinnerChars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
||||||
|
|
||||||
constructor(verbose: boolean) {
|
constructor(verbose: boolean) {
|
||||||
this.verbose = verbose
|
const isInteractiveTerminal =
|
||||||
|
process.stdout.isTTY && process.env.TERM !== "dumb" && !process.env.CI
|
||||||
|
this.verbose = verbose || !isInteractiveTerminal
|
||||||
}
|
}
|
||||||
|
|
||||||
start(text: string) {
|
start(text: string) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user