English documentation: README.en.md · 默认中文文档:README.md
BeautifulBlog is a static website generator. Like Hexo and similar tools, it produces files that can be deployed to an ordinary static file server. Its key difference is that a theme is not a page template applied by the generator. A theme is an independent website program controlled by its author.
The generator provides article transformation results, indexes, and lifecycle notifications. The theme decides how to read these data, organize pages, implement navigation, choose its presentation, and add browser-side interactions. The generator does not require a particular home page, article page, or URL layout; it only requires compatibility with the agreed data and hook interfaces.
| Template-based generators | BeautifulBlog |
|---|---|
| The generator reads a theme template and inserts articles into it | The theme is independent website code with its own structure and runtime logic |
| A theme mainly describes layout, styling, and template syntax | A theme can implement its own home page, routing, search, catalog, recommendations, and interactions |
| The generator controls how articles enter pages | The generator provides article data and change notifications; the theme decides how to use them |
| A theme works inside the generator's template lifecycle | A theme can contain a browser application; Node hooks coordinate with the generator |
The published result is still a static site. A theme may use JavaScript in the browser to load articles or provide interactions, but the production site does not need to run BeautifulBlog's Node server.
articles/ article source files
│
├── format transformers read articles and front matter
▼
content/ normalized data, HTML, attachments, and files.json
│
├── the generator notifies the active theme hooks
▼
sites/<theme>/ theme source: browser website + Node lifecycle hooks
│
└── copied to nowSite/ active theme runtime copy
│
└── built-in static server or external hosting
The article transformers convert Markdown, TXT, PDF, and other inputs into the agreed intermediate result. A theme can use the generated HTML and metadata, or combine them in its own way. The generator never inserts articles into a theme home page or template automatically.
A theme contains two kinds of code:
- Browser website: usually
index.htmlplus scripts, styles, and static assets. This is the website itself and decides how to presentcontent/. - Node hooks:
hooks.jsis loaded by the generator to receive theme changes, full generation, and article change events. It is not a browser script or page template.
The site currently integrates with the generator through three main conventions:
- Content files: each article produces a JSON metadata file and an HTML file at the same path, and may have an attachment directory. Metadata includes the title, date, front matter fields, lengths, modification time, and source article identifier.
- Article index:
content/files.jsoncontainsfileList, which records metadata URLs, titles, source identifiers, and attachment directory URLs. A theme can use it to build lists or maintain its own index. - Lifecycle hooks: a theme exports an object implementing
ISiteHooks. The current implementation callsbeforeUnloadandloadedwhen switching themes,generatedafterblog generate ycompletes, andarticleChanged(context, type, dest)after watched article additions, changes, or deletions are regenerated. Some other regeneration paths do not currently callgenerated.
A theme can also declare the raw article extensions it supports with metainfo.articleExtensions exported from its hooks and optionally implement interceptArticle(context). The generator passes sourcePath, destinationPath, extension, the current config, and the global config. Returning a TransformResult gives the theme full control of that article; the generator only writes the result as standard HTML, JSON, and attachments. Returning undefined falls back to the default transformer. This lets a theme handle formats unknown to the generator and choose its own parsing and metadata logic.
Theme switching and forced refresh reload hooks and re-evaluate format support. Refreshing is non-destructive: if the new theme does not support an existing format, or an interceptor fails, existing HTML, JSON, attachments, and files.json remain available. Outputs for deleted articles are removed only after the complete generation round succeeds.
init, install, and uninstall exist in the interface definitions, but the generator does not yet invoke all of them through a complete lifecycle. A theme should not depend on these events. Content files also do not yet have an independent schema version; new or changed fields should remain backwards-readable until versioning is formalized.
base_url is the deployment path prefix. Theme requests for articles, scripts, styles, and attachments must preserve this prefix, must work when the site is not mounted at the domain root, and must never contain development-machine absolute paths.
A standalone blog directory normally contains:
articles/ article source files
content/ generated content, metadata, attachments, and index
config/ base_url, active theme, and other configuration
sites/ optional theme sources
nowSite/ active theme copy, overwritten by synchronization
assets/ blog-level shared assets
See the default theme build guide for the default theme's responsibilities, build process, data contract, and evolution plan.
The development environment requires Node.js 24.12 or later and pnpm 10:
pnpm install
pnpm build
pnpm test
pnpm blog -hCreate a standalone blog directory from the CLI:
pnpm blog init ./my-blogInside the blog directory, start the synchronization server with the installed blog command:
blog sync 8080The command regenerates content/ according to the configuration, synchronizes the selected theme to nowSite/, starts the static server, and watches article and theme changes. The startup log prints the address. The path prefix comes from base_url in config/<name>.json. Use blog -h for the complete CLI help.
Theme authors own the website structure and behavior. You can start from sites/default or create a completely new theme directory. Typical responsibilities include:
- Designing the home page, reading view, navigation, and URL structure.
- Choosing how to present article lists, categories, tags, search, archives, and recommendations.
- Using the agreed content index and metadata, or building a theme-specific data layer.
- Responding to theme loading, full generation, and individual article changes in Node hooks.
- Building the theme into browser-runnable files and ensuring the
nowSite/copy contains all resources required by the site.
See the default theme build guide for theme TypeScript, hook signatures, and the default build process. Do not edit nowSite/ manually; the tool generates it as a runtime copy.
blog init <directory>: create a standalone blog and the default theme.blog generate [y|n] [verbose]: generate article content;yforces regeneration.blog changesite [theme]: switch the active theme.blog sync [port]: start the static server and watch article and theme changes.blog refresh: regenerate content and synchronize the active theme.blog new <a|c> <relative-directory> <name>: create an article or subdirectory.blog config <add|del|use> <parameter>: manage configuration.
pnpm build
pnpm test
pnpm packpnpm build compiles the generator, checks the default theme, and bundles its browser entry. pnpm test builds first and then runs the repository tests. pnpm pack creates an npm package. Standalone builds are available through pnpm pkgwin, pnpm pkglinux, and pnpm pkgall.
- A theme is a website program, not a general-purpose template; the default theme is an example and does not define the generator's page shape.
- The built-in transformers provide general HTML and metadata, while the transformer plugin system is still evolving.
- Raw article interception is supported. Theme switching and forced refresh use non-destructive commits, preserving existing output when a format is unsupported or an interceptor fails.
- Some
ISiteHooksinstallation lifecycle events are not yet wired into the full call flow. - Content indexes and metadata need schema versions and an explicit backwards-compatibility policy.
- EJS/Hexo template compatibility was previously considered as an optional adapter direction; it must not change the core model in which the theme controls its own website.