Jacky Zhao
2024-08-09 9acaa1c8ac8c8afd3fa08d3b1f58a60006fcfc6f
quartz/build.ts
@@ -60,7 +60,7 @@
  const release = await mut.acquire()
  perf.addEvent("clean")
  await rimraf(output)
  await rimraf(path.join(output, "*"), { glob: true })
  console.log(`Cleaned output directory \`${output}\` in ${perf.timeSince("clean")}`)
  perf.addEvent("glob")
@@ -185,9 +185,14 @@
        const emitterGraph =
          (await emitter.getDependencyGraph?.(ctx, processedFiles, staticResources)) ?? null
        // emmiter may not define a dependency graph. nothing to update if so
        if (emitterGraph) {
          dependencies[emitter.name]?.updateIncomingEdgesForNode(emitterGraph, fp)
          const existingGraph = dependencies[emitter.name]
          if (existingGraph !== null) {
            existingGraph.mergeGraph(emitterGraph)
          } else {
            // might be the first time we're adding a mardown file
            dependencies[emitter.name] = emitterGraph
          }
        }
      }
      break
@@ -203,8 +208,9 @@
          const emitterGraph =
            (await emitter.getDependencyGraph?.(ctx, processedFiles, staticResources)) ?? null
          // emmiter may not define a dependency graph. nothing to update if so
          if (emitterGraph) {
          // only update the graph if the emitter plugin uses the changed file
          // eg. Assets plugin ignores md files, so we skip updating the graph
          if (emitterGraph?.hasNode(fp)) {
            // merge the new dependencies into the dep graph
            dependencies[emitter.name]?.updateIncomingEdgesForNode(emitterGraph, fp)
          }
@@ -223,7 +229,6 @@
  // EMIT
  perf.addEvent("rebuild")
  let emittedFiles = 0
  const destinationsToDelete = new Set<FilePath>()
  for (const emitter of cfg.plugins.emitters) {
    const depGraph = dependencies[emitter.name]
@@ -263,11 +268,6 @@
      // and supply [a.md, b.md] to the emitter
      const upstreams = [...depGraph.getLeafNodeAncestors(fp)] as FilePath[]
      if (action === "delete" && upstreams.length === 1) {
        // if there's only one upstream, the destination is solely dependent on this file
        destinationsToDelete.add(upstreams[0])
      }
      const upstreamContent = upstreams
        // filter out non-markdown files
        .filter((file) => contentMap.has(file))
@@ -290,14 +290,26 @@
  console.log(`Emitted ${emittedFiles} files to \`${argv.output}\` in ${perf.timeSince("rebuild")}`)
  // CLEANUP
  // delete files that are solely dependent on this file
  await rimraf([...destinationsToDelete])
  const destinationsToDelete = new Set<FilePath>()
  for (const file of toRemove) {
    // remove from cache
    contentMap.delete(file)
    // remove the node from dependency graphs
    Object.values(dependencies).forEach((depGraph) => depGraph?.removeNode(file))
    Object.values(dependencies).forEach((depGraph) => {
      // remove the node from dependency graphs
      depGraph?.removeNode(file)
      // remove any orphan nodes. eg if a.md is deleted, a.html is orphaned and should be removed
      const orphanNodes = depGraph?.removeOrphanNodes()
      orphanNodes?.forEach((node) => {
        // only delete files that are in the output directory
        if (node.startsWith(argv.output)) {
          destinationsToDelete.add(node)
        }
      })
    })
  }
  await rimraf([...destinationsToDelete])
  console.log(chalk.green(`Done rebuilding in ${perf.timeSince()}`))
  toRemove.clear()
  release()
@@ -374,7 +386,7 @@
    // TODO: we can probably traverse the link graph to figure out what's safe to delete here
    // instead of just deleting everything
    await rimraf(argv.output)
    await rimraf(path.join(argv.output, ".*"), { glob: true })
    await emitContent(ctx, filteredContent)
    console.log(chalk.green(`Done rebuilding in ${perf.timeSince()}`))
  } catch (err) {