Skip to content

Commit 9388cce

Browse files
zrosenbauerclaude
andauthored
fix(packages/core): remove default asset seeding that blocked generated banners (#62)
The hardcoded default SVGs in packages/core/public/ lacked the `<!-- zpress-generated -->` marker, so they were treated as user-customized files and could never be overwritten by generateAssets(). This meant users always got the generic ZPRESS branding instead of banners derived from their config title. Remove the seeding step entirely — generateAssets() now always runs with a fallback title of "Documentation" and the existing shouldGenerate() check still preserves genuinely user-customized files. Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0ae702f commit 9388cce

6 files changed

Lines changed: 11 additions & 260 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@zpress/core': patch
3+
---
4+
5+
Remove default asset seeding in favor of always generating banner, logo, and icon SVGs. Previously, hardcoded default SVGs without the `<!-- zpress-generated -->` marker were seeded into `.zpress/public/`, which prevented the generated assets from overwriting them. Now `generateAssets` always runs with a fallback title of "Documentation", and user-customized files (without the marker) are still preserved.

packages/core/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
},
1919
"files": [
2020
"dist",
21-
"public",
2221
"templates"
2322
],
2423
"type": "module",

packages/core/public/banner.svg

Lines changed: 0 additions & 135 deletions
This file was deleted.

packages/core/public/icon.svg

Lines changed: 0 additions & 21 deletions
This file was deleted.

packages/core/public/logo.svg

Lines changed: 0 additions & 18 deletions
This file was deleted.

packages/core/src/sync/index.ts

Lines changed: 6 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { log } from '@clack/prompts'
55
import { match, P } from 'ts-pattern'
66

77
import { generateAssets } from '../banner/index.ts'
8-
import { GENERATED_MARKER } from '../banner/svg-shared.ts'
98
import type { AssetConfig } from '../banner/types.ts'
109
import type { Paths } from '../paths.ts'
1110
import type { Section, ZpressConfig } from '../types.ts'
@@ -62,15 +61,9 @@ export async function sync(config: ZpressConfig, options: SyncOptions): Promise<
6261
await fs.mkdir(outDir, { recursive: true })
6362
await fs.mkdir(path.resolve(outDir, '.generated'), { recursive: true })
6463

65-
// Seed .zpress/public/ with default assets from the package (skip files that already exist)
66-
await seedDefaultAssets(options.paths.publicDir)
67-
68-
// Fallback: generate banner/logo if not already present in .zpress/public/
69-
// (Primary generation path is `zpress generate` or `zpress setup`)
64+
// Generate banner/logo/icon SVGs (skips user-customized files automatically)
7065
const assetConfig = buildAssetConfig(config)
71-
if (assetConfig) {
72-
await generateAssets({ config: assetConfig, publicDir: options.paths.publicDir })
73-
}
66+
await generateAssets({ config: assetConfig, publicDir: options.paths.publicDir })
7467

7568
// Copy public assets into content/public/ so Rspress can resolve them
7669
// (Rspress looks for public/ inside the root directory, which is .zpress/content/)
@@ -260,75 +253,6 @@ zpress build # Build static site
260253
await fs.writeFile(readmePath, content, 'utf8')
261254
}
262255

263-
/**
264-
* Seed .zpress/public/ with default assets from the core package's public/ directory.
265-
* Only copies files that don't already exist — user customizations are preserved.
266-
*
267-
* @private
268-
* @param publicDir - Absolute path to the .zpress/public/ directory
269-
* @returns Promise that resolves when seeding is complete
270-
*/
271-
async function seedDefaultAssets(publicDir: string): Promise<void> {
272-
// Resolve from the bundled dist/ directory up to the package root's public/ dir
273-
const defaultsDir = path.resolve(import.meta.dirname, '..', 'public')
274-
const exists = await fs.stat(defaultsDir).catch(() => null)
275-
if (!exists) {
276-
return
277-
}
278-
279-
await copySeeded(defaultsDir, publicDir)
280-
}
281-
282-
/**
283-
* Recursively copy files from src to dest.
284-
*
285-
* Overwrites a destination file only when it was auto-generated
286-
* (first line matches the zpress-generated marker). User-customized
287-
* files (no marker) are never touched.
288-
*
289-
* @private
290-
* @param src - Source directory path
291-
* @param dest - Destination directory path
292-
* @returns Promise that resolves when all files are copied
293-
*/
294-
async function copySeeded(src: string, dest: string): Promise<void> {
295-
await fs.mkdir(dest, { recursive: true })
296-
const entries = await fs.readdir(src, { withFileTypes: true })
297-
await entries.reduce(async (prevPromise, entry) => {
298-
await prevPromise
299-
const srcPath = path.resolve(src, entry.name)
300-
const destPath = path.resolve(dest, entry.name)
301-
if (entry.isDirectory()) {
302-
await copySeeded(srcPath, destPath)
303-
return
304-
}
305-
const shouldCopy = await isReplaceable(destPath)
306-
if (shouldCopy) {
307-
await fs.copyFile(srcPath, destPath)
308-
}
309-
}, Promise.resolve())
310-
}
311-
312-
/**
313-
* Check whether a destination file can be replaced by a seeded default.
314-
*
315-
* Returns `true` when the file does not exist or was auto-generated
316-
* (has the zpress-generated marker). Returns `false` for user-customized files.
317-
*
318-
* @private
319-
* @param filePath - Absolute path to the destination file
320-
* @returns Whether the file can be safely replaced
321-
*/
322-
async function isReplaceable(filePath: string): Promise<boolean> {
323-
// oxlint-disable-next-line security/detect-non-literal-fs-filename -- path is constructed from trusted publicDir + directory entries
324-
const content = await fs.readFile(filePath, 'utf8').catch(() => null)
325-
if (content === null) {
326-
return true
327-
}
328-
const [firstLine] = content.split('\n')
329-
return firstLine === GENERATED_MARKER
330-
}
331-
332256
/**
333257
* Recursively copy all files from src to dest, overwriting existing files.
334258
*
@@ -387,15 +311,12 @@ function concatPage(pages: readonly PageData[], page: PageData | undefined): Pag
387311

388312
/**
389313
* Extract an `AssetConfig` from the zpress config.
390-
* Returns `null` when no title is set (falls back to default ZPRESS assets).
314+
* Falls back to 'Documentation' when no title is set.
391315
*
392316
* @private
393317
* @param config - Zpress config object
394-
* @returns Asset config or null when no title is configured
318+
* @returns Asset config with title and optional tagline
395319
*/
396-
function buildAssetConfig(config: ZpressConfig): AssetConfig | null {
397-
if (!config.title) {
398-
return null
399-
}
400-
return { title: config.title, tagline: config.tagline }
320+
function buildAssetConfig(config: ZpressConfig): AssetConfig {
321+
return { title: config.title ?? 'Documentation', tagline: config.tagline }
401322
}

0 commit comments

Comments
 (0)