hot content reload
This commit is contained in:
parent
a7603fcca8
commit
7f571846bb
@ -30,4 +30,3 @@ For a comprehensive list of features, visit the [features page](/features). You
|
||||
|
||||
### 🚧 Troubleshooting
|
||||
Having trouble with Quartz? Try searching for your issue using the search feature. If you're still having trouble, feel free to [submit an issue](https://github.com/jackyzha0/quartz/issues) if you feel you found a bug or ask for help in our [Discord Community](https://discord.gg/cRFFHYye7t).
|
||||
|
||||
|
16
package-lock.json
generated
16
package-lock.json
generated
@ -1,18 +1,19 @@
|
||||
{
|
||||
"name": "@jackyzha0/quartz",
|
||||
"version": "4.0.4",
|
||||
"version": "4.0.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@jackyzha0/quartz",
|
||||
"version": "4.0.4",
|
||||
"version": "4.0.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^0.6.3",
|
||||
"@floating-ui/dom": "^1.4.0",
|
||||
"@napi-rs/simple-git": "^0.1.8",
|
||||
"chalk": "^4.1.2",
|
||||
"chokidar": "^3.5.3",
|
||||
"cli-spinner": "^0.2.10",
|
||||
"d3": "^7.8.5",
|
||||
"esbuild-sass-plugin": "^2.9.0",
|
||||
@ -54,6 +55,7 @@
|
||||
"unist-util-visit": "^4.1.2",
|
||||
"vfile": "^5.3.7",
|
||||
"workerpool": "^6.4.0",
|
||||
"ws": "^8.13.0",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"bin": {
|
||||
@ -69,6 +71,7 @@
|
||||
"@types/pretty-time": "^1.1.2",
|
||||
"@types/serve-handler": "^6.1.1",
|
||||
"@types/workerpool": "^6.4.0",
|
||||
"@types/ws": "^8.5.5",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"esbuild": "^0.18.11",
|
||||
"tsx": "^3.12.7",
|
||||
@ -1498,6 +1501,15 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz",
|
||||
"integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/yargs": {
|
||||
"version": "17.0.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
|
||||
|
@ -31,6 +31,7 @@
|
||||
"@floating-ui/dom": "^1.4.0",
|
||||
"@napi-rs/simple-git": "^0.1.8",
|
||||
"chalk": "^4.1.2",
|
||||
"chokidar": "^3.5.3",
|
||||
"cli-spinner": "^0.2.10",
|
||||
"d3": "^7.8.5",
|
||||
"esbuild-sass-plugin": "^2.9.0",
|
||||
@ -72,6 +73,7 @@
|
||||
"unist-util-visit": "^4.1.2",
|
||||
"vfile": "^5.3.7",
|
||||
"workerpool": "^6.4.0",
|
||||
"ws": "^8.13.0",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -84,6 +86,7 @@
|
||||
"@types/pretty-time": "^1.1.2",
|
||||
"@types/serve-handler": "^6.1.1",
|
||||
"@types/workerpool": "^6.4.0",
|
||||
"@types/ws": "^8.5.5",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"esbuild": "^0.18.11",
|
||||
"tsx": "^3.12.7",
|
||||
|
@ -72,7 +72,7 @@ const BuildArgv = {
|
||||
serve: {
|
||||
boolean: true,
|
||||
default: false,
|
||||
describe: 'run a local server to preview your Quartz'
|
||||
describe: 'run a local server to live-preview your Quartz'
|
||||
},
|
||||
port: {
|
||||
number: true,
|
||||
@ -255,6 +255,7 @@ See the [documentation](https://quartz.jzhao.xyz) for how to get started.
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /\.inline\.(ts|js)$/ }, async (args) => {
|
||||
let text = await promises.readFile(args.path, 'utf8')
|
||||
|
||||
// remove default exports that we manually inserted
|
||||
text = text.replace('export default', '')
|
||||
text = text.replace('export', '')
|
||||
|
@ -2,7 +2,7 @@ import 'source-map-support/register.js'
|
||||
import path from "path"
|
||||
import { PerfTimer } from "./perf"
|
||||
import { rimraf } from "rimraf"
|
||||
import { globby } from "globby"
|
||||
import { globby, isGitIgnored } from "globby"
|
||||
import chalk from "chalk"
|
||||
import http from "http"
|
||||
import serveHandler from "serve-handler"
|
||||
@ -11,6 +11,9 @@ import { filterContent } from "./processors/filter"
|
||||
import { emitContent } from "./processors/emit"
|
||||
import cfg from "../quartz.config"
|
||||
import { FilePath } from "./path"
|
||||
import chokidar from "chokidar"
|
||||
import { ProcessedContent } from './plugins/vfile'
|
||||
import WebSocket, { WebSocketServer } from 'ws'
|
||||
|
||||
interface Argv {
|
||||
directory: string
|
||||
@ -51,10 +54,53 @@ export default async function buildQuartz(argv: Argv, version: string) {
|
||||
const filePaths = fps.map(fp => `${argv.directory}${path.sep}${fp}` as FilePath)
|
||||
const parsedFiles = await parseMarkdown(cfg.plugins.transformers, argv.directory, filePaths, argv.verbose)
|
||||
const filteredContent = filterContent(cfg.plugins.filters, parsedFiles, argv.verbose)
|
||||
await emitContent(argv.directory, output, cfg, filteredContent, argv.verbose)
|
||||
await emitContent(argv.directory, output, cfg, filteredContent, argv.serve, argv.verbose)
|
||||
console.log(chalk.green(`Done processing ${fps.length} files in ${perf.timeSince()}`))
|
||||
|
||||
if (argv.serve) {
|
||||
const wss = new WebSocketServer({ port: 3001 })
|
||||
const connections: WebSocket[] = []
|
||||
wss.on('connection', ws => connections.push(ws))
|
||||
|
||||
const ignored = await isGitIgnored()
|
||||
const contentMap = new Map<FilePath, ProcessedContent>()
|
||||
for (const content of parsedFiles) {
|
||||
const [_tree, vfile] = content
|
||||
contentMap.set(vfile.data.filePath!, content)
|
||||
}
|
||||
|
||||
async function rebuild(fp: string, action: 'add' | 'change' | 'unlink') {
|
||||
perf.addEvent('rebuild')
|
||||
if (!ignored(fp)) {
|
||||
console.log(chalk.yellow(`Detected change in ${fp}, rebuilding...`))
|
||||
const fullPath = `${argv.directory}${path.sep}${fp}` as FilePath
|
||||
if (action === 'add' || action === 'change') {
|
||||
const [parsedContent] = await parseMarkdown(cfg.plugins.transformers, argv.directory, [fullPath], argv.verbose)
|
||||
contentMap.set(fullPath, parsedContent)
|
||||
} else if (action === 'unlink') {
|
||||
contentMap.delete(fullPath)
|
||||
}
|
||||
|
||||
await rimraf(output)
|
||||
const parsedFiles = [...contentMap.values()]
|
||||
const filteredContent = filterContent(cfg.plugins.filters, parsedFiles, argv.verbose)
|
||||
await emitContent(argv.directory, output, cfg, filteredContent, argv.serve, argv.verbose)
|
||||
console.log(chalk.green(`Done rebuilding in ${perf.timeSince('rebuild')}`))
|
||||
connections.forEach(conn => conn.send('rebuild'))
|
||||
}
|
||||
}
|
||||
|
||||
const watcher = chokidar.watch('.', {
|
||||
persistent: true,
|
||||
cwd: argv.directory,
|
||||
ignoreInitial: true,
|
||||
})
|
||||
|
||||
watcher
|
||||
.on('add', fp => rebuild(fp, 'add'))
|
||||
.on('change', fp => rebuild(fp, 'change'))
|
||||
.on('unlink', fp => rebuild(fp, 'unlink'))
|
||||
|
||||
const server = http.createServer(async (req, res) => {
|
||||
await serveHandler(req, res, {
|
||||
public: output,
|
||||
@ -62,10 +108,10 @@ export default async function buildQuartz(argv: Argv, version: string) {
|
||||
})
|
||||
const status = res.statusCode
|
||||
const statusString = (status >= 200 && status < 300) ?
|
||||
chalk.green(`[${status}]`) :
|
||||
chalk.green(`[${status}]`) :
|
||||
(status >= 300 && status < 400) ?
|
||||
chalk.yellow(`[${status}]`) :
|
||||
chalk.red(`[${status}]`)
|
||||
chalk.yellow(`[${status}]`) :
|
||||
chalk.red(`[${status}]`)
|
||||
console.log(statusString + chalk.grey(` ${req.url}`))
|
||||
})
|
||||
server.listen(argv.port)
|
||||
|
@ -7,7 +7,6 @@ import { EmitCallback } from "../plugins/types"
|
||||
import { ProcessedContent } from "../plugins/vfile"
|
||||
import { FilePath, QUARTZ, slugifyFilePath } from "../path"
|
||||
import { globbyStream } from "globby"
|
||||
import chalk from "chalk"
|
||||
|
||||
// @ts-ignore
|
||||
import spaRouterScript from '../components/scripts/spa.inline'
|
||||
@ -21,7 +20,7 @@ import { QuartzLogger } from "../log"
|
||||
import { googleFontHref } from "../theme"
|
||||
import { trace } from "../trace"
|
||||
|
||||
function addGlobalPageResources(cfg: GlobalConfiguration, staticResources: StaticResources, componentResources: ComponentResources) {
|
||||
function addGlobalPageResources(cfg: GlobalConfiguration, reloadScript: boolean, staticResources: StaticResources, componentResources: ComponentResources) {
|
||||
staticResources.css.push(googleFontHref(cfg.theme))
|
||||
|
||||
// popovers
|
||||
@ -64,9 +63,20 @@ function addGlobalPageResources(cfg: GlobalConfiguration, staticResources: Stati
|
||||
document.dispatchEvent(event)`
|
||||
)
|
||||
}
|
||||
|
||||
if (reloadScript) {
|
||||
staticResources.js.push({
|
||||
loadTime: "afterDOMReady",
|
||||
contentType: "inline",
|
||||
script: `
|
||||
const socket = new WebSocket('ws://localhost:3001')
|
||||
socket.addEventListener('message', () => document.location.reload())
|
||||
`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function emitContent(contentFolder: string, output: string, cfg: QuartzConfig, content: ProcessedContent[], verbose: boolean) {
|
||||
export async function emitContent(contentFolder: string, output: string, cfg: QuartzConfig, content: ProcessedContent[], reloadScript: boolean, verbose: boolean) {
|
||||
const perf = new PerfTimer()
|
||||
const log = new QuartzLogger(verbose)
|
||||
|
||||
@ -88,18 +98,18 @@ export async function emitContent(contentFolder: string, output: string, cfg: Qu
|
||||
// important that this goes *after* component scripts
|
||||
// as the "nav" event gets triggered here and we should make sure
|
||||
// that everyone else had the chance to register a listener for it
|
||||
addGlobalPageResources(cfg.configuration, staticResources, componentResources)
|
||||
addGlobalPageResources(cfg.configuration, reloadScript, staticResources, componentResources)
|
||||
|
||||
// emit in one go
|
||||
let emittedFiles = 0
|
||||
const emittedResources = await emitComponentResources(cfg.configuration, componentResources, emit)
|
||||
if (verbose) {
|
||||
for (const file of emittedResources) {
|
||||
emittedFiles += 1
|
||||
console.log(`[emit:Resources] ${file}`)
|
||||
}
|
||||
}
|
||||
|
||||
// emitter plugins
|
||||
let emittedFiles = 0
|
||||
for (const emitter of cfg.plugins.emitters) {
|
||||
try {
|
||||
const emitted = await emitter.emit(contentFolder, cfg.configuration, content, staticResources, emit)
|
||||
|
Loading…
Reference in New Issue
Block a user