@zpress/core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +63 -0
- package/dist/index.d.ts +967 -0
- package/dist/index.mjs +15 -83
- package/package.json +13 -3
package/LICENSE
CHANGED
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/joggrdocs/zpress/main/assets/banner.svg" alt="zpress" width="90%" />
|
|
3
|
+
<p><strong>Config loading, sync engine, and asset utilities for zpress.</strong></p>
|
|
4
|
+
|
|
5
|
+
<a href="https://github.com/joggrdocs/zpress/actions/workflows/ci.yml"><img src="https://github.com/joggrdocs/zpress/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI" /></a>
|
|
6
|
+
<a href="https://www.npmjs.com/package/@zpress/core"><img src="https://img.shields.io/npm/v/@zpress/core" alt="npm version" /></a>
|
|
7
|
+
<a href="https://github.com/joggrdocs/zpress/blob/main/LICENSE"><img src="https://img.shields.io/github/license/joggrdocs/zpress" alt="License" /></a>
|
|
8
|
+
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @zpress/core
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## API
|
|
18
|
+
|
|
19
|
+
### Config
|
|
20
|
+
|
|
21
|
+
| Export | Description |
|
|
22
|
+
| -------------- | ----------------------------------- |
|
|
23
|
+
| `defineConfig` | Type-safe config factory |
|
|
24
|
+
| `loadConfig` | Load and validate `zpress.config.*` |
|
|
25
|
+
|
|
26
|
+
### Sync Engine
|
|
27
|
+
|
|
28
|
+
| Export | Description |
|
|
29
|
+
| ---------------- | ------------------------------------- |
|
|
30
|
+
| `sync` | Run the full sync pipeline |
|
|
31
|
+
| `resolveEntries` | Resolve glob patterns to page entries |
|
|
32
|
+
| `loadManifest` | Load a previously written manifest |
|
|
33
|
+
|
|
34
|
+
### Assets
|
|
35
|
+
|
|
36
|
+
| Export | Description |
|
|
37
|
+
| ------------------- | ------------------------ |
|
|
38
|
+
| `generateAssets` | Generate all asset files |
|
|
39
|
+
| `generateBannerSvg` | Generate banner SVG |
|
|
40
|
+
| `generateIconSvg` | Generate icon SVG |
|
|
41
|
+
| `generateLogoSvg` | Generate logo SVG |
|
|
42
|
+
|
|
43
|
+
### Utilities
|
|
44
|
+
|
|
45
|
+
| Export | Description |
|
|
46
|
+
| -------------- | --------------------------- |
|
|
47
|
+
| `createPaths` | Build resolved path helpers |
|
|
48
|
+
| `hasGlobChars` | Check if a string has globs |
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { defineConfig, loadConfig, sync } from '@zpress/core'
|
|
54
|
+
|
|
55
|
+
const config = defineConfig({
|
|
56
|
+
title: 'my-project',
|
|
57
|
+
sections: [{ text: 'Guide', from: 'docs/*.md' }],
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
[MIT](https://github.com/joggrdocs/zpress/blob/main/LICENSE) - Joggr, Inc.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,967 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input configuration for asset generation, extracted from ZpressConfig.
|
|
3
|
+
*/
|
|
4
|
+
export declare interface AssetConfig {
|
|
5
|
+
readonly title: string;
|
|
6
|
+
readonly tagline: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Error produced during asset generation or file writing.
|
|
11
|
+
*/
|
|
12
|
+
export declare interface AssetError {
|
|
13
|
+
readonly _tag: 'AssetError';
|
|
14
|
+
readonly type: 'empty_title' | 'write_failed';
|
|
15
|
+
readonly message: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Convenience alias for asset operation results.
|
|
20
|
+
*/
|
|
21
|
+
export declare type AssetResult<T> = Result<T, AssetError>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Controls how an entry appears as a card on its parent section's
|
|
25
|
+
* auto-generated landing page.
|
|
26
|
+
*
|
|
27
|
+
* When present, the landing page uses workspace-style cards
|
|
28
|
+
* (icon + scope + name + description + tags + optional badge).
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* card: {
|
|
33
|
+
* icon: 'devicon:hono',
|
|
34
|
+
* iconColor: 'api',
|
|
35
|
+
* scope: 'apps/',
|
|
36
|
+
* description: 'Hono REST API with RPC-typed routes',
|
|
37
|
+
* tags: ['Hono', 'REST', 'Serverless'],
|
|
38
|
+
* badge: { src: '/logos/vercel.svg', alt: 'Vercel' },
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare interface CardConfig {
|
|
43
|
+
/**
|
|
44
|
+
* Iconify identifier for the card icon (e.g. 'devicon:hono').
|
|
45
|
+
*/
|
|
46
|
+
icon?: string;
|
|
47
|
+
/**
|
|
48
|
+
* CSS class suffix for the icon color (maps to `.workspace-icon--{color}`).
|
|
49
|
+
*/
|
|
50
|
+
iconColor?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Scope label shown above the name (e.g. `"apps/"`).
|
|
53
|
+
*/
|
|
54
|
+
scope?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Short description shown on the card. Overrides auto-extracted description.
|
|
57
|
+
*/
|
|
58
|
+
description?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Technology tags shown at the bottom of the card.
|
|
61
|
+
*/
|
|
62
|
+
tags?: string[];
|
|
63
|
+
/**
|
|
64
|
+
* Deploy badge image shown in the card header.
|
|
65
|
+
*/
|
|
66
|
+
badge?: {
|
|
67
|
+
src: string;
|
|
68
|
+
alt: string;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Error produced during config validation in `defineConfig`.
|
|
74
|
+
*/
|
|
75
|
+
export declare interface ConfigError {
|
|
76
|
+
readonly _tag: 'ConfigError';
|
|
77
|
+
readonly type: 'empty_sections' | 'missing_field' | 'duplicate_prefix' | 'invalid_icon' | 'invalid_entry';
|
|
78
|
+
readonly message: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Create a `ConfigError` value.
|
|
83
|
+
*
|
|
84
|
+
* @param type - Error classification
|
|
85
|
+
* @param message - Human-readable description
|
|
86
|
+
* @returns A frozen `ConfigError` object
|
|
87
|
+
*/
|
|
88
|
+
export declare function configError(type: ConfigError['type'], message: string): ConfigError;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Convenience alias for config validation results.
|
|
92
|
+
*/
|
|
93
|
+
export declare type ConfigResult<T> = readonly [ConfigError, null] | readonly [null, T];
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create all derived project paths from a resolved directory.
|
|
97
|
+
*/
|
|
98
|
+
export declare function createPaths(dir: string): Paths;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Type-safe config helper with validation.
|
|
102
|
+
*
|
|
103
|
+
* Validates the config structure and exits with a clear error message
|
|
104
|
+
* if any issues are found. This is the primary entry point for user-
|
|
105
|
+
* provided config — validation happens at the boundary.
|
|
106
|
+
*
|
|
107
|
+
* `defineConfig` is called in user config files (e.g. `zpress.config.ts`)
|
|
108
|
+
* where the return value is consumed by the c12 config loader, which
|
|
109
|
+
* expects a plain `ZpressConfig` object — not a `Result` tuple.
|
|
110
|
+
* Because this is the outermost user-facing boundary (not a library
|
|
111
|
+
* function), `process.exit(1)` is acceptable here: the user must fix
|
|
112
|
+
* their config before any downstream code can run.
|
|
113
|
+
*
|
|
114
|
+
* @param config - Raw zpress config object
|
|
115
|
+
* @returns The validated config (unchanged)
|
|
116
|
+
*/
|
|
117
|
+
export declare function defineConfig(config: ZpressConfig): ZpressConfig;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* A single node in the information architecture.
|
|
121
|
+
*
|
|
122
|
+
* What you provide determines what it is:
|
|
123
|
+
*
|
|
124
|
+
* **Page — explicit file**
|
|
125
|
+
* ```ts
|
|
126
|
+
* { text: 'Architecture', link: '/architecture', from: 'docs/architecture.md' }
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* **Page — inline/generated content**
|
|
130
|
+
* ```ts
|
|
131
|
+
* { text: 'Overview', link: '/api/overview', content: '# API Overview\n...' }
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* **Section — explicit children**
|
|
135
|
+
* ```ts
|
|
136
|
+
* { text: 'Guides', items: [ ... ] }
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* **Section — auto-discovered from glob**
|
|
140
|
+
* ```ts
|
|
141
|
+
* { text: 'Guides', prefix: '/guides', from: 'docs/guides/*.md' }
|
|
142
|
+
* ```
|
|
143
|
+
*
|
|
144
|
+
* **Section — mix of explicit + auto-discovered**
|
|
145
|
+
* ```ts
|
|
146
|
+
* {
|
|
147
|
+
* text: 'Guides',
|
|
148
|
+
* prefix: '/guides',
|
|
149
|
+
* from: 'docs/guides/*.md',
|
|
150
|
+
* items: [
|
|
151
|
+
* { text: 'Getting Started', link: '/guides/start', from: 'docs/intro.md' },
|
|
152
|
+
* ],
|
|
153
|
+
* }
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export declare interface Entry {
|
|
157
|
+
/**
|
|
158
|
+
* Display text in sidebar and nav.
|
|
159
|
+
*/
|
|
160
|
+
readonly text: string;
|
|
161
|
+
/**
|
|
162
|
+
* Output URL path.
|
|
163
|
+
* - Pages: exact URL (e.g. `"/guides/add-api-route"`)
|
|
164
|
+
* - Sections: optional — makes the section header clickable
|
|
165
|
+
*/
|
|
166
|
+
readonly link?: UrlPath;
|
|
167
|
+
/**
|
|
168
|
+
* Content source — file path or glob, relative to repo root.
|
|
169
|
+
*
|
|
170
|
+
* - **No wildcards** → single file (e.g. `"docs/architecture.md"`)
|
|
171
|
+
* - **With wildcards** → auto-discover children (e.g. `"docs/guides/*.md"`)
|
|
172
|
+
*/
|
|
173
|
+
readonly from?: FilePath | GlobPattern;
|
|
174
|
+
/**
|
|
175
|
+
* URL prefix for auto-discovered children.
|
|
176
|
+
* Used with glob `from` — each discovered file gets `prefix + "/" + slug`.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* `prefix: "/guides"` + file `add-api-route.md` → `/guides/add-api-route`
|
|
180
|
+
*/
|
|
181
|
+
readonly prefix?: UrlPath;
|
|
182
|
+
/**
|
|
183
|
+
* Inline markdown or async content generator.
|
|
184
|
+
* For virtual pages that have no source `.md` file.
|
|
185
|
+
* Mutually exclusive with `from`.
|
|
186
|
+
*/
|
|
187
|
+
readonly content?: string | (() => string | Promise<string>);
|
|
188
|
+
/**
|
|
189
|
+
* Child entries — pages and/or sub-sections.
|
|
190
|
+
*/
|
|
191
|
+
readonly items?: readonly Entry[];
|
|
192
|
+
/**
|
|
193
|
+
* Make this section collapsible in the sidebar.
|
|
194
|
+
* Sections at depth > 1 are collapsible by default.
|
|
195
|
+
* Set to `false` to keep a deep section always-open.
|
|
196
|
+
*/
|
|
197
|
+
readonly collapsible?: boolean;
|
|
198
|
+
/**
|
|
199
|
+
* Exclude globs, scoped to this entry's `from` glob.
|
|
200
|
+
*/
|
|
201
|
+
readonly exclude?: readonly GlobPattern[];
|
|
202
|
+
/**
|
|
203
|
+
* Hide from sidebar. Page is still built and routable.
|
|
204
|
+
* Useful for pages that should exist but not clutter navigation.
|
|
205
|
+
*/
|
|
206
|
+
readonly hidden?: boolean;
|
|
207
|
+
/**
|
|
208
|
+
* Frontmatter injected at build time.
|
|
209
|
+
* - On a page: applied to that page.
|
|
210
|
+
* - On a section: applied to all pages within.
|
|
211
|
+
*/
|
|
212
|
+
readonly frontmatter?: Frontmatter;
|
|
213
|
+
/**
|
|
214
|
+
* How to derive `text` for auto-discovered children.
|
|
215
|
+
* - `"filename"` — kebab-to-title from filename (default)
|
|
216
|
+
* - `"heading"` — first `# heading` in the file
|
|
217
|
+
* - `"frontmatter"` — `title` field from YAML frontmatter, falls back to heading
|
|
218
|
+
*/
|
|
219
|
+
readonly textFrom?: 'filename' | 'heading' | 'frontmatter';
|
|
220
|
+
/**
|
|
221
|
+
* Transform function applied to auto-derived text (from `textFrom`).
|
|
222
|
+
* Called after text derivation for glob-discovered and recursive children.
|
|
223
|
+
* Does NOT apply to entries with explicit `text` (those are already user-controlled).
|
|
224
|
+
*
|
|
225
|
+
* @param text - The derived text (from heading or filename)
|
|
226
|
+
* @param slug - The filename slug (without extension)
|
|
227
|
+
* @returns Transformed text for sidebar display
|
|
228
|
+
*/
|
|
229
|
+
readonly textTransform?: (text: string, slug: string) => string;
|
|
230
|
+
/**
|
|
231
|
+
* Sort order for auto-discovered children.
|
|
232
|
+
* - `"alpha"` — alphabetical by derived text (default)
|
|
233
|
+
* - `"filename"` — alphabetical by filename
|
|
234
|
+
* - Custom comparator function
|
|
235
|
+
*/
|
|
236
|
+
readonly sort?: 'alpha' | 'filename' | ((a: ResolvedPage, b: ResolvedPage) => number);
|
|
237
|
+
/**
|
|
238
|
+
* Enable recursive directory-based nesting for glob patterns.
|
|
239
|
+
* When true, directory structure under the glob base drives sidebar nesting:
|
|
240
|
+
* - The `indexFile` (default `"overview"`) in a directory becomes the section header page
|
|
241
|
+
* - Other `.md` files become children
|
|
242
|
+
* - Sub-directories become nested collapsible sections
|
|
243
|
+
*
|
|
244
|
+
* Requires `from` with a recursive glob (e.g. `"docs/**\/*.md"`) and `prefix`.
|
|
245
|
+
* @default false
|
|
246
|
+
*/
|
|
247
|
+
readonly recursive?: boolean;
|
|
248
|
+
/**
|
|
249
|
+
* Filename (without extension) used as the section header page in each directory.
|
|
250
|
+
* Only meaningful when `recursive` is true.
|
|
251
|
+
* @default "overview"
|
|
252
|
+
*/
|
|
253
|
+
readonly indexFile?: string;
|
|
254
|
+
/**
|
|
255
|
+
* Card display metadata for the parent section's auto-generated landing page.
|
|
256
|
+
*
|
|
257
|
+
* When present on child entries, the parent's landing page uses
|
|
258
|
+
* workspace-style cards instead of the default simple cards.
|
|
259
|
+
*/
|
|
260
|
+
readonly card?: CardConfig;
|
|
261
|
+
/**
|
|
262
|
+
* Isolate this section into its own Rspress sidebar namespace.
|
|
263
|
+
*
|
|
264
|
+
* When `true`, the section's children appear under a dedicated sidebar
|
|
265
|
+
* keyed by `link` (e.g. `"/apps/"`) instead of the root `"/"` sidebar.
|
|
266
|
+
* This mirrors how the OpenAPI reference already works.
|
|
267
|
+
*
|
|
268
|
+
* Requires `link` to be set.
|
|
269
|
+
* @default false
|
|
270
|
+
*/
|
|
271
|
+
readonly isolated?: boolean;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Explicit feature card for the home page.
|
|
276
|
+
*
|
|
277
|
+
* When `features` is provided on the config, these replace the
|
|
278
|
+
* auto-generated feature cards that are normally derived from
|
|
279
|
+
* top-level sections.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```ts
|
|
283
|
+
* {
|
|
284
|
+
* text: 'Getting Started',
|
|
285
|
+
* description: 'Everything you need to set up and start building.',
|
|
286
|
+
* link: '/getting-started',
|
|
287
|
+
* icon: 'pixelarticons:speed-fast',
|
|
288
|
+
* }
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
export declare interface Feature {
|
|
292
|
+
/**
|
|
293
|
+
* Display title for the feature card.
|
|
294
|
+
*/
|
|
295
|
+
readonly text: string;
|
|
296
|
+
/**
|
|
297
|
+
* Short description shown below the title.
|
|
298
|
+
*/
|
|
299
|
+
readonly description: string;
|
|
300
|
+
/**
|
|
301
|
+
* Link target when the card is clicked.
|
|
302
|
+
*/
|
|
303
|
+
readonly link?: string;
|
|
304
|
+
/**
|
|
305
|
+
* Iconify icon identifier (e.g. `"pixelarticons:speed-fast"`).
|
|
306
|
+
*/
|
|
307
|
+
readonly icon?: string;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Relative file path from repo root (e.g. `"docs/guides/add-api-route.md"`)
|
|
312
|
+
*/
|
|
313
|
+
declare type FilePath = string;
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Rspress frontmatter fields injectable at build time.
|
|
317
|
+
*/
|
|
318
|
+
export declare interface Frontmatter {
|
|
319
|
+
readonly title?: string;
|
|
320
|
+
readonly titleTemplate?: string | boolean;
|
|
321
|
+
readonly description?: string;
|
|
322
|
+
readonly layout?: 'doc' | 'page' | 'home' | (string & {});
|
|
323
|
+
readonly sidebar?: boolean;
|
|
324
|
+
readonly aside?: boolean | 'left';
|
|
325
|
+
readonly outline?: false | number | [number, number] | 'deep';
|
|
326
|
+
readonly navbar?: boolean;
|
|
327
|
+
readonly editLink?: boolean;
|
|
328
|
+
readonly lastUpdated?: boolean;
|
|
329
|
+
readonly footer?: boolean;
|
|
330
|
+
readonly pageClass?: string;
|
|
331
|
+
readonly head?: readonly [string, Record<string, string>][];
|
|
332
|
+
/**
|
|
333
|
+
* Arbitrary extra fields merged into frontmatter.
|
|
334
|
+
*/
|
|
335
|
+
readonly [key: string]: unknown;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Generate banner, logo, and icon SVGs, writing them to the public directory.
|
|
340
|
+
*
|
|
341
|
+
* For each asset:
|
|
342
|
+
* 1. If the file is missing or has the zpress-generated marker → write it
|
|
343
|
+
* 2. If the file exists without the marker → skip (user-customized)
|
|
344
|
+
*
|
|
345
|
+
* @param params - Config and target directory
|
|
346
|
+
* @returns Result containing the list of filenames written, or an error
|
|
347
|
+
*/
|
|
348
|
+
export declare function generateAssets(params: GenerateAssetsParams): Promise<AssetResult<readonly string[]>>;
|
|
349
|
+
|
|
350
|
+
declare interface GenerateAssetsParams {
|
|
351
|
+
readonly config: AssetConfig;
|
|
352
|
+
readonly publicDir: string;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Generate a banner SVG from the project config.
|
|
357
|
+
*
|
|
358
|
+
* @param config - Title and optional tagline
|
|
359
|
+
* @returns Result containing the generated asset or an error
|
|
360
|
+
*/
|
|
361
|
+
export declare function generateBannerSvg(config: AssetConfig): AssetResult<GeneratedAsset>;
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* A generated SVG asset ready to be written to disk.
|
|
365
|
+
*/
|
|
366
|
+
export declare interface GeneratedAsset {
|
|
367
|
+
readonly filename: string;
|
|
368
|
+
readonly content: string;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Generate an icon (favicon) SVG from the project config.
|
|
373
|
+
*
|
|
374
|
+
* @param config - Title (first character is used for the icon glyph)
|
|
375
|
+
* @returns Result containing the generated asset or an error
|
|
376
|
+
*/
|
|
377
|
+
export declare function generateIconSvg(config: AssetConfig): AssetResult<GeneratedAsset>;
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Generate a logo SVG from the project config.
|
|
381
|
+
*
|
|
382
|
+
* @param config - Title (tagline is ignored for logos)
|
|
383
|
+
* @returns Result containing the generated asset or an error
|
|
384
|
+
*/
|
|
385
|
+
export declare function generateLogoSvg(config: AssetConfig): AssetResult<GeneratedAsset>;
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Glob pattern (e.g. `"docs/guides/*.md"`)
|
|
389
|
+
*/
|
|
390
|
+
declare type GlobPattern = string;
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Returns true if the string contains glob metacharacters.
|
|
394
|
+
*/
|
|
395
|
+
export declare function hasGlobChars(s: string): boolean;
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Load zpress config at runtime via c12.
|
|
399
|
+
*
|
|
400
|
+
* Returns a `ConfigResult` tuple instead of calling `process.exit` — the
|
|
401
|
+
* CLI boundary is responsible for surfacing the error and exiting.
|
|
402
|
+
*/
|
|
403
|
+
export declare function loadConfig(dir: string): Promise<ConfigResult<ZpressConfig>>;
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Load the previous sync manifest from disk.
|
|
407
|
+
*
|
|
408
|
+
* @param outDir - Absolute path to the output directory
|
|
409
|
+
* @returns Parsed manifest, or `null` if no manifest exists
|
|
410
|
+
*/
|
|
411
|
+
export declare function loadManifest(outDir: string): Promise<Manifest | null>;
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Tracks output files for incremental sync and stale file cleanup.
|
|
415
|
+
*/
|
|
416
|
+
export declare interface Manifest {
|
|
417
|
+
/**
|
|
418
|
+
* Map of output relative path to entry metadata.
|
|
419
|
+
*
|
|
420
|
+
* @remarks Mutable — entries are accumulated during the sync pass as pages
|
|
421
|
+
* are copied. The manifest is built incrementally and then persisted to disk.
|
|
422
|
+
* Will be refactored to an immutable accumulator pattern alongside `SyncContext.manifest`.
|
|
423
|
+
*/
|
|
424
|
+
files: Record<string, ManifestEntry>;
|
|
425
|
+
/**
|
|
426
|
+
* Timestamp of last sync.
|
|
427
|
+
*/
|
|
428
|
+
readonly timestamp: number;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Metadata for a single output file tracked in the manifest.
|
|
433
|
+
*/
|
|
434
|
+
export declare interface ManifestEntry {
|
|
435
|
+
/**
|
|
436
|
+
* Repo-relative path to source file (undefined for virtual pages).
|
|
437
|
+
*/
|
|
438
|
+
readonly source?: string;
|
|
439
|
+
/**
|
|
440
|
+
* Source file mtime in ms (for quick-check).
|
|
441
|
+
*/
|
|
442
|
+
readonly sourceMtime?: number;
|
|
443
|
+
/**
|
|
444
|
+
* SHA-256 hex of the written output.
|
|
445
|
+
*/
|
|
446
|
+
readonly contentHash: string;
|
|
447
|
+
/**
|
|
448
|
+
* Output path relative to .content/.
|
|
449
|
+
*/
|
|
450
|
+
readonly outputPath: string;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export declare interface NavItem {
|
|
454
|
+
readonly text: string;
|
|
455
|
+
readonly link?: UrlPath;
|
|
456
|
+
readonly items?: readonly NavItem[];
|
|
457
|
+
readonly activeMatch?: string;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Configuration for OpenAPI spec integration.
|
|
462
|
+
*/
|
|
463
|
+
export declare interface OpenAPIConfig {
|
|
464
|
+
/**
|
|
465
|
+
* Path to openapi.json relative to repo root.
|
|
466
|
+
*/
|
|
467
|
+
spec: FilePath;
|
|
468
|
+
/**
|
|
469
|
+
* URL prefix for API operation pages (e.g., '/api').
|
|
470
|
+
*/
|
|
471
|
+
prefix: UrlPath;
|
|
472
|
+
/**
|
|
473
|
+
* Sidebar group title.
|
|
474
|
+
* @default 'API Reference'
|
|
475
|
+
*/
|
|
476
|
+
title?: string;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Data for a single page to be written to the output directory.
|
|
481
|
+
*/
|
|
482
|
+
export declare interface PageData {
|
|
483
|
+
/**
|
|
484
|
+
* Absolute path to source .md (undefined for virtual pages).
|
|
485
|
+
*/
|
|
486
|
+
readonly source?: string;
|
|
487
|
+
/**
|
|
488
|
+
* Inline content for virtual pages.
|
|
489
|
+
*/
|
|
490
|
+
readonly content?: string | (() => string | Promise<string>);
|
|
491
|
+
/**
|
|
492
|
+
* Relative path inside .content/ (e.g. "guides/add-api-route.md").
|
|
493
|
+
*/
|
|
494
|
+
readonly outputPath: string;
|
|
495
|
+
/**
|
|
496
|
+
* Merged frontmatter to inject.
|
|
497
|
+
*/
|
|
498
|
+
readonly frontmatter: Frontmatter;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* All user-project paths derived from a single root directory.
|
|
503
|
+
*/
|
|
504
|
+
export declare interface Paths {
|
|
505
|
+
readonly repoRoot: string;
|
|
506
|
+
readonly outputRoot: string;
|
|
507
|
+
readonly contentDir: string;
|
|
508
|
+
readonly publicDir: string;
|
|
509
|
+
readonly distDir: string;
|
|
510
|
+
readonly cacheDir: string;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Internal resolved node — produced by the resolver, consumed by copy + sidebar/nav generators.
|
|
515
|
+
*/
|
|
516
|
+
export declare interface ResolvedEntry {
|
|
517
|
+
readonly text: string;
|
|
518
|
+
readonly link?: string;
|
|
519
|
+
readonly collapsible?: boolean;
|
|
520
|
+
readonly hidden?: boolean;
|
|
521
|
+
/**
|
|
522
|
+
* @remarks Mutable — `injectLandingPages` may reassign items when
|
|
523
|
+
* promoting an overview child to section page. Will be refactored
|
|
524
|
+
* to immutable tree rebuild.
|
|
525
|
+
*/
|
|
526
|
+
items?: readonly ResolvedEntry[];
|
|
527
|
+
/**
|
|
528
|
+
* Present on leaf pages (and section headers that are also pages).
|
|
529
|
+
*
|
|
530
|
+
* @remarks Mutable — `injectLandingPages` assigns virtual pages to sections
|
|
531
|
+
* that have a link but no page. Will be refactored to immutable tree rebuild
|
|
532
|
+
* when landing pages move to Vue components.
|
|
533
|
+
*/
|
|
534
|
+
page?: PageData;
|
|
535
|
+
readonly card?: CardConfig;
|
|
536
|
+
/**
|
|
537
|
+
* When true, this section gets its own sidebar namespace keyed by `link`.
|
|
538
|
+
*/
|
|
539
|
+
readonly isolated?: boolean;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* A fully resolved page after the sync engine processes the config.
|
|
544
|
+
*/
|
|
545
|
+
export declare interface ResolvedPage {
|
|
546
|
+
/**
|
|
547
|
+
* Display text.
|
|
548
|
+
*/
|
|
549
|
+
readonly text: string;
|
|
550
|
+
/**
|
|
551
|
+
* Output URL path.
|
|
552
|
+
*/
|
|
553
|
+
readonly link: UrlPath;
|
|
554
|
+
/**
|
|
555
|
+
* Source file path (undefined for virtual pages).
|
|
556
|
+
*/
|
|
557
|
+
readonly source?: FilePath;
|
|
558
|
+
/**
|
|
559
|
+
* Merged frontmatter.
|
|
560
|
+
*/
|
|
561
|
+
readonly frontmatter: Frontmatter;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* A fully resolved section.
|
|
566
|
+
*/
|
|
567
|
+
export declare interface ResolvedSection {
|
|
568
|
+
readonly text: string;
|
|
569
|
+
readonly link?: UrlPath;
|
|
570
|
+
readonly collapsible?: boolean;
|
|
571
|
+
readonly items: readonly (ResolvedPage | ResolvedSection)[];
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Walk the Entry tree and produce a ResolvedEntry tree.
|
|
576
|
+
*
|
|
577
|
+
* Resolves globs, derives text, merges frontmatter, deduplicates.
|
|
578
|
+
* Returns a `SyncOutcome` tuple — the caller is responsible for
|
|
579
|
+
* surfacing errors and exiting.
|
|
580
|
+
*
|
|
581
|
+
* @param entries - Config entry tree to resolve
|
|
582
|
+
* @param ctx - Sync context (provides repo root, config, quiet flag)
|
|
583
|
+
* @param inheritedFrontmatter - Frontmatter inherited from parent entries
|
|
584
|
+
* @param depth - Current nesting depth (0 = top-level)
|
|
585
|
+
* @returns Result tuple containing resolved entry tree or the first sync error
|
|
586
|
+
*/
|
|
587
|
+
export declare function resolveEntries(entries: readonly Entry[], ctx: SyncContext, inheritedFrontmatter?: Frontmatter, depth?: number): Promise<readonly [SyncError, null] | readonly [null, ResolvedEntry[]]>;
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* zpress — unified information architecture config.
|
|
591
|
+
*
|
|
592
|
+
* The IA tree IS the config. Each node defines what it is, where its
|
|
593
|
+
* content comes from, and where it sits in the sidebar — all in one place.
|
|
594
|
+
* Source `.md` files are never edited.
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
* ```ts
|
|
598
|
+
* import { defineConfig } from '@zpress/core'
|
|
599
|
+
*
|
|
600
|
+
* export default defineConfig({
|
|
601
|
+
* title: 'My Docs',
|
|
602
|
+
* sections: [
|
|
603
|
+
* {
|
|
604
|
+
* text: 'Introduction',
|
|
605
|
+
* items: [
|
|
606
|
+
* { text: 'Architecture', link: '/architecture', from: 'docs/architecture.md' },
|
|
607
|
+
* { text: 'Structure', link: '/structure', from: 'docs/structure.md' },
|
|
608
|
+
* ],
|
|
609
|
+
* },
|
|
610
|
+
* {
|
|
611
|
+
* text: 'Guides',
|
|
612
|
+
* prefix: '/guides',
|
|
613
|
+
* from: 'docs/guides/*.md',
|
|
614
|
+
* },
|
|
615
|
+
* {
|
|
616
|
+
* text: 'API Reference',
|
|
617
|
+
* items: [
|
|
618
|
+
* { text: 'Overview', link: '/api/overview', content: '# API\n...' },
|
|
619
|
+
* { text: 'Routes', link: '/api/routes', from: 'apps/api/docs/routes.md' },
|
|
620
|
+
* ],
|
|
621
|
+
* },
|
|
622
|
+
* ],
|
|
623
|
+
* })
|
|
624
|
+
* ```
|
|
625
|
+
*/
|
|
626
|
+
/**
|
|
627
|
+
* Result type for error handling without exceptions.
|
|
628
|
+
*
|
|
629
|
+
* Success: `[null, value]`
|
|
630
|
+
* Failure: `[error, null]`
|
|
631
|
+
*
|
|
632
|
+
* @example
|
|
633
|
+
* ```ts
|
|
634
|
+
* const [error, value] = loadConfig(path)
|
|
635
|
+
* if (error) return [error, null]
|
|
636
|
+
* ```
|
|
637
|
+
*/
|
|
638
|
+
export declare type Result<T, E = Error> = readonly [E, null] | readonly [null, T];
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Rspress sidebar item shape.
|
|
642
|
+
*
|
|
643
|
+
* Constructed immutably by sidebar generators, then serialized to JSON.
|
|
644
|
+
*/
|
|
645
|
+
export declare interface SidebarItem {
|
|
646
|
+
readonly text: string;
|
|
647
|
+
readonly link?: string;
|
|
648
|
+
/**
|
|
649
|
+
* Rspress `collapsed` — set by sidebar generator from `collapsible`.
|
|
650
|
+
*/
|
|
651
|
+
readonly collapsed?: boolean;
|
|
652
|
+
readonly items?: readonly SidebarItem[];
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Mapping from repo-relative source path to content-relative output path.
|
|
657
|
+
*/
|
|
658
|
+
declare type SourceMap = ReadonlyMap<string, string>;
|
|
659
|
+
|
|
660
|
+
export declare function sync(config: ZpressConfig, options: SyncOptions): Promise<SyncResult>;
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Context threaded through all sync operations.
|
|
664
|
+
*/
|
|
665
|
+
export declare interface SyncContext {
|
|
666
|
+
/**
|
|
667
|
+
* Absolute path to repo root.
|
|
668
|
+
*/
|
|
669
|
+
readonly repoRoot: string;
|
|
670
|
+
/**
|
|
671
|
+
* Absolute path to .content/ output directory.
|
|
672
|
+
*/
|
|
673
|
+
readonly outDir: string;
|
|
674
|
+
/**
|
|
675
|
+
* Resolved config.
|
|
676
|
+
*/
|
|
677
|
+
readonly config: ZpressConfig;
|
|
678
|
+
/**
|
|
679
|
+
* Previous manifest (for incremental sync).
|
|
680
|
+
*/
|
|
681
|
+
readonly previousManifest: Manifest | null;
|
|
682
|
+
/**
|
|
683
|
+
* Current manifest being built.
|
|
684
|
+
*
|
|
685
|
+
* @remarks Mutable during the sync pass — entries are added as pages are copied.
|
|
686
|
+
* Will be refactored to an immutable accumulator pattern in a future pass.
|
|
687
|
+
*/
|
|
688
|
+
manifest: Manifest;
|
|
689
|
+
/**
|
|
690
|
+
* When true, suppress all log output during sync.
|
|
691
|
+
*/
|
|
692
|
+
readonly quiet: boolean;
|
|
693
|
+
/**
|
|
694
|
+
* Mapping from repo-relative source paths to content-relative output paths.
|
|
695
|
+
* Used by the copy step to rewrite relative markdown links.
|
|
696
|
+
*/
|
|
697
|
+
readonly sourceMap?: SourceMap;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Domain-specific error types for the sync engine.
|
|
702
|
+
*
|
|
703
|
+
* All sync operations return `Result<T, SyncError>` instead of throwing.
|
|
704
|
+
* Config validation returns `Result<T, ConfigError>`.
|
|
705
|
+
*/
|
|
706
|
+
/**
|
|
707
|
+
* Error produced by the sync engine during entry resolution, page copy, or sidebar generation.
|
|
708
|
+
*/
|
|
709
|
+
export declare interface SyncError {
|
|
710
|
+
readonly _tag: 'SyncError';
|
|
711
|
+
readonly type: 'missing_from' | 'missing_link' | 'file_not_found' | 'missing_content' | 'internal';
|
|
712
|
+
readonly message: string;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Create a `SyncError` value.
|
|
717
|
+
*
|
|
718
|
+
* @param type - Error classification
|
|
719
|
+
* @param message - Human-readable description
|
|
720
|
+
* @returns A frozen `SyncError` object
|
|
721
|
+
*/
|
|
722
|
+
export declare function syncError(type: SyncError['type'], message: string): SyncError;
|
|
723
|
+
|
|
724
|
+
export declare interface SyncOptions {
|
|
725
|
+
/**
|
|
726
|
+
* Resolved project paths.
|
|
727
|
+
*/
|
|
728
|
+
readonly paths: Paths;
|
|
729
|
+
/**
|
|
730
|
+
* When true, suppress all log output during sync.
|
|
731
|
+
*/
|
|
732
|
+
readonly quiet?: boolean;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Convenience alias for sync operation results.
|
|
737
|
+
*
|
|
738
|
+
* Named `SyncOutcome` to avoid collision with the `SyncResult` interface
|
|
739
|
+
* in `sync/index.ts` (which represents the aggregate return value of a full sync pass).
|
|
740
|
+
*/
|
|
741
|
+
export declare type SyncOutcome<T> = readonly [SyncError, null] | readonly [null, T];
|
|
742
|
+
|
|
743
|
+
export declare interface SyncResult {
|
|
744
|
+
readonly pagesWritten: number;
|
|
745
|
+
readonly pagesSkipped: number;
|
|
746
|
+
readonly pagesRemoved: number;
|
|
747
|
+
readonly elapsed: number;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* URL path (e.g. `"/guides/add-api-route"`)
|
|
752
|
+
*/
|
|
753
|
+
declare type UrlPath = string;
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* A named group of workspace items for custom workspace categories.
|
|
757
|
+
*
|
|
758
|
+
* Lets users define arbitrary groups beyond the built-in `apps` and `packages`
|
|
759
|
+
* (e.g. "Services", "Tools", "Integrations") that receive the same
|
|
760
|
+
* card/landing-page treatment.
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* ```ts
|
|
764
|
+
* {
|
|
765
|
+
* name: 'Integrations',
|
|
766
|
+
* description: 'Third-party service connectors',
|
|
767
|
+
* icon: 'pixelarticons:integration',
|
|
768
|
+
* items: [
|
|
769
|
+
* { text: 'Stripe', description: 'Payment processing', docsPrefix: '/integrations/stripe' },
|
|
770
|
+
* ],
|
|
771
|
+
* }
|
|
772
|
+
* ```
|
|
773
|
+
*/
|
|
774
|
+
export declare interface WorkspaceGroup {
|
|
775
|
+
readonly name: string;
|
|
776
|
+
readonly description: string;
|
|
777
|
+
readonly icon: string;
|
|
778
|
+
readonly items: readonly WorkspaceItem[];
|
|
779
|
+
/**
|
|
780
|
+
* URL prefix override for the group's landing page.
|
|
781
|
+
* Defaults to `/${slugify(name)}` when omitted.
|
|
782
|
+
*/
|
|
783
|
+
readonly link?: string;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* A workspace item representing an app or package in the monorepo.
|
|
788
|
+
*
|
|
789
|
+
* Used as the single source of truth for workspace metadata — home page cards,
|
|
790
|
+
* landing page cards, and introduction bullets all derive from these arrays.
|
|
791
|
+
*
|
|
792
|
+
* @example
|
|
793
|
+
* ```ts
|
|
794
|
+
* {
|
|
795
|
+
* text: 'API',
|
|
796
|
+
* icon: 'devicon:hono',
|
|
797
|
+
* iconColor: 'api',
|
|
798
|
+
* description: 'Hono REST API serving all client applications with RPC-typed routes',
|
|
799
|
+
* tags: ['hono', 'react', 'vercel'],
|
|
800
|
+
* badge: { src: '/logos/vercel.svg', alt: 'Vercel' },
|
|
801
|
+
* docsPrefix: '/apps/api',
|
|
802
|
+
* }
|
|
803
|
+
* ```
|
|
804
|
+
*/
|
|
805
|
+
export declare interface WorkspaceItem {
|
|
806
|
+
/**
|
|
807
|
+
* Display name (e.g. "API", "Console", "AI").
|
|
808
|
+
*/
|
|
809
|
+
readonly text: string;
|
|
810
|
+
/**
|
|
811
|
+
* Main icon — Iconify identifier (e.g. "devicon:hono").
|
|
812
|
+
* Falls back to a default app or package icon when omitted.
|
|
813
|
+
*/
|
|
814
|
+
readonly icon?: string;
|
|
815
|
+
/**
|
|
816
|
+
* CSS class suffix for icon color (maps to `.workspace-icon--{color}`).
|
|
817
|
+
*/
|
|
818
|
+
readonly iconColor?: string;
|
|
819
|
+
/**
|
|
820
|
+
* Short description for cards and bullet lists.
|
|
821
|
+
*/
|
|
822
|
+
readonly description: string;
|
|
823
|
+
/**
|
|
824
|
+
* Technology tags — kebab-case keys resolved by the UI TechTag component.
|
|
825
|
+
* Each tag maps to an Iconify icon and display label.
|
|
826
|
+
*/
|
|
827
|
+
readonly tags?: readonly string[];
|
|
828
|
+
/**
|
|
829
|
+
* Deploy badge image for the card header.
|
|
830
|
+
*/
|
|
831
|
+
readonly badge?: {
|
|
832
|
+
readonly src: string;
|
|
833
|
+
readonly alt: string;
|
|
834
|
+
};
|
|
835
|
+
/**
|
|
836
|
+
* Docs path prefix (e.g. "/apps/api"). Matches section entries and derives card links.
|
|
837
|
+
* Also used as the URL prefix for glob-discovered children.
|
|
838
|
+
*/
|
|
839
|
+
readonly docsPrefix: string;
|
|
840
|
+
/**
|
|
841
|
+
* Content source — file path or glob, **relative to the workspace item's
|
|
842
|
+
* base path** (derived from `docsPrefix`).
|
|
843
|
+
*
|
|
844
|
+
* - `docsPrefix: "/apps/api"` + `from: "docs/*.md"` → resolves to `apps/api/docs/*.md`
|
|
845
|
+
* - **No wildcards** → single file (e.g. `"docs/overview.md"`)
|
|
846
|
+
* - **With wildcards** → auto-discover children (e.g. `"docs/*.md"`)
|
|
847
|
+
*
|
|
848
|
+
* @default "docs/*.md"
|
|
849
|
+
*/
|
|
850
|
+
readonly from?: string;
|
|
851
|
+
/**
|
|
852
|
+
* Explicit child entries for this workspace item.
|
|
853
|
+
* Can be combined with `from` — explicit children override glob-discovered pages.
|
|
854
|
+
*/
|
|
855
|
+
readonly items?: readonly Entry[];
|
|
856
|
+
/**
|
|
857
|
+
* Sort order for auto-discovered children.
|
|
858
|
+
* - `"alpha"` — alphabetical by derived text (default)
|
|
859
|
+
* - `"filename"` — alphabetical by filename
|
|
860
|
+
* - Custom comparator function
|
|
861
|
+
*/
|
|
862
|
+
readonly sort?: Entry['sort'];
|
|
863
|
+
/**
|
|
864
|
+
* How to derive `text` for auto-discovered children.
|
|
865
|
+
* - `"filename"` — kebab-to-title from filename (default)
|
|
866
|
+
* - `"heading"` — first `# heading` in the file
|
|
867
|
+
* - `"frontmatter"` — `title` field from YAML frontmatter, falls back to heading
|
|
868
|
+
*/
|
|
869
|
+
readonly textFrom?: Entry['textFrom'];
|
|
870
|
+
/**
|
|
871
|
+
* Transform function applied to auto-derived text (from `textFrom`).
|
|
872
|
+
*/
|
|
873
|
+
readonly textTransform?: Entry['textTransform'];
|
|
874
|
+
/**
|
|
875
|
+
* Enable recursive directory-based nesting for glob patterns.
|
|
876
|
+
* Requires `from` with a recursive glob (e.g. `"apps/api/docs/**\/*.md"`).
|
|
877
|
+
* @default false
|
|
878
|
+
*/
|
|
879
|
+
readonly recursive?: boolean;
|
|
880
|
+
/**
|
|
881
|
+
* Filename (without extension) used as the section header page in each directory.
|
|
882
|
+
* Only meaningful when `recursive` is true.
|
|
883
|
+
* @default "overview"
|
|
884
|
+
*/
|
|
885
|
+
readonly indexFile?: string;
|
|
886
|
+
/**
|
|
887
|
+
* Exclude globs, scoped to this item's `from` glob.
|
|
888
|
+
*/
|
|
889
|
+
readonly exclude?: readonly string[];
|
|
890
|
+
/**
|
|
891
|
+
* Make this item's section collapsible in the sidebar.
|
|
892
|
+
*/
|
|
893
|
+
readonly collapsible?: boolean;
|
|
894
|
+
/**
|
|
895
|
+
* Frontmatter injected at build time for all pages under this workspace item.
|
|
896
|
+
*/
|
|
897
|
+
readonly frontmatter?: Frontmatter;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
export declare interface ZpressConfig {
|
|
901
|
+
/**
|
|
902
|
+
* Site title.
|
|
903
|
+
*/
|
|
904
|
+
readonly title?: string;
|
|
905
|
+
/**
|
|
906
|
+
* Site meta description. Used as the hero headline on the home page.
|
|
907
|
+
*/
|
|
908
|
+
readonly description?: string;
|
|
909
|
+
/**
|
|
910
|
+
* Path to a custom favicon file served from `.zpress/public/`.
|
|
911
|
+
* When omitted, defaults to the auto-generated `/icon.svg`.
|
|
912
|
+
*/
|
|
913
|
+
readonly icon?: string;
|
|
914
|
+
/**
|
|
915
|
+
* Hero tagline displayed below the headline on the home page.
|
|
916
|
+
* When omitted, the tagline is not rendered.
|
|
917
|
+
*/
|
|
918
|
+
readonly tagline?: string;
|
|
919
|
+
/**
|
|
920
|
+
* Workspace apps — deployable services that make up the platform.
|
|
921
|
+
* Single source of truth for app metadata used on the home page,
|
|
922
|
+
* landing pages, and introduction page.
|
|
923
|
+
*/
|
|
924
|
+
readonly apps?: readonly WorkspaceItem[];
|
|
925
|
+
/**
|
|
926
|
+
* Workspace packages — shared libraries consumed by apps.
|
|
927
|
+
* Single source of truth for package metadata used on the home page,
|
|
928
|
+
* landing pages, and introduction page.
|
|
929
|
+
*/
|
|
930
|
+
readonly packages?: readonly WorkspaceItem[];
|
|
931
|
+
/**
|
|
932
|
+
* Custom workspace groups — arbitrary named groups of workspace items.
|
|
933
|
+
* Each group receives the same card/landing-page treatment as apps and packages.
|
|
934
|
+
* Rendered after apps and packages, in array order.
|
|
935
|
+
*/
|
|
936
|
+
readonly workspaces?: readonly WorkspaceGroup[];
|
|
937
|
+
/**
|
|
938
|
+
* Explicit feature cards for the home page.
|
|
939
|
+
*
|
|
940
|
+
* When provided, these replace the auto-generated feature cards
|
|
941
|
+
* that are normally derived from top-level sections.
|
|
942
|
+
* When omitted, features are auto-generated from sections with icons.
|
|
943
|
+
*/
|
|
944
|
+
readonly features?: readonly Feature[];
|
|
945
|
+
/**
|
|
946
|
+
* The information architecture.
|
|
947
|
+
* Defines content sources, sidebar structure, and routing in a single tree.
|
|
948
|
+
*/
|
|
949
|
+
readonly sections: readonly Entry[];
|
|
950
|
+
/**
|
|
951
|
+
* Top navigation bar.
|
|
952
|
+
* - `"auto"` — one nav item per top-level section
|
|
953
|
+
* - Array — explicit nav items
|
|
954
|
+
* @default "auto"
|
|
955
|
+
*/
|
|
956
|
+
readonly nav?: 'auto' | readonly NavItem[];
|
|
957
|
+
/**
|
|
958
|
+
* Globs to exclude globally across all sources.
|
|
959
|
+
*/
|
|
960
|
+
readonly exclude?: readonly GlobPattern[];
|
|
961
|
+
/**
|
|
962
|
+
* OpenAPI spec integration for interactive API docs.
|
|
963
|
+
*/
|
|
964
|
+
readonly openapi?: OpenAPIConfig;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
export { }
|
package/dist/index.mjs
CHANGED
|
@@ -90,11 +90,6 @@ function validateConfig(config) {
|
|
|
90
90
|
featErr,
|
|
91
91
|
null
|
|
92
92
|
];
|
|
93
|
-
const [navErr] = validateNav(config.nav);
|
|
94
|
-
if (navErr) return [
|
|
95
|
-
navErr,
|
|
96
|
-
null
|
|
97
|
-
];
|
|
98
93
|
return [
|
|
99
94
|
null,
|
|
100
95
|
config
|
|
@@ -227,29 +222,6 @@ function validateFeature(feature) {
|
|
|
227
222
|
if (feature.icon && !feature.icon.includes(':')) return configError('invalid_icon', `Feature "${feature.text}": icon must be an Iconify identifier (e.g. "pixelarticons:speed-fast")`);
|
|
228
223
|
return null;
|
|
229
224
|
}
|
|
230
|
-
function validateNav(nav) {
|
|
231
|
-
if ('auto' === nav || void 0 === nav) return [
|
|
232
|
-
null,
|
|
233
|
-
true
|
|
234
|
-
];
|
|
235
|
-
const navError = nav.reduce((acc, item)=>{
|
|
236
|
-
if (acc) return acc;
|
|
237
|
-
return validateNavItem(item);
|
|
238
|
-
}, null);
|
|
239
|
-
if (navError) return [
|
|
240
|
-
navError,
|
|
241
|
-
null
|
|
242
|
-
];
|
|
243
|
-
return [
|
|
244
|
-
null,
|
|
245
|
-
true
|
|
246
|
-
];
|
|
247
|
-
}
|
|
248
|
-
function validateNavItem(item) {
|
|
249
|
-
if (!item.icon) return configError('missing_nav_icon', `NavItem "${item.text}": top-level nav items require an "icon" (Iconify identifier)`);
|
|
250
|
-
if (!item.icon.includes(':')) return configError('invalid_icon', `NavItem "${item.text}": icon must be an Iconify identifier (e.g. "pixelarticons:folder")`);
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
225
|
async function config_loadConfig(dir) {
|
|
254
226
|
const { config } = await loadConfig({
|
|
255
227
|
cwd: dir,
|
|
@@ -1457,7 +1429,7 @@ function buildFeatures(sections, repoRoot) {
|
|
|
1457
1429
|
return Promise.all(sections.slice(0, 3).map(async (section, index)=>{
|
|
1458
1430
|
const link = section.link ?? findFirstChildLink(section);
|
|
1459
1431
|
const details = await extractSectionDescription(section, repoRoot);
|
|
1460
|
-
const iconId =
|
|
1432
|
+
const iconId = null;
|
|
1461
1433
|
const iconColor = ICON_COLORS[index % ICON_COLORS.length];
|
|
1462
1434
|
return {
|
|
1463
1435
|
title: section.text,
|
|
@@ -2168,13 +2140,12 @@ function deduplicateByLink(entries) {
|
|
|
2168
2140
|
});
|
|
2169
2141
|
return result;
|
|
2170
2142
|
}
|
|
2171
|
-
function buildSidebarEntry(entry
|
|
2143
|
+
function buildSidebarEntry(entry) {
|
|
2172
2144
|
if (entry.items && entry.items.length > 0) return {
|
|
2173
2145
|
text: entry.text,
|
|
2174
2146
|
items: generateSidebar(entry.items),
|
|
2175
2147
|
...maybeCollapsed(entry.collapsible),
|
|
2176
|
-
...maybeLink(entry.link)
|
|
2177
|
-
...maybeIcon(icon)
|
|
2148
|
+
...maybeLink(entry.link)
|
|
2178
2149
|
};
|
|
2179
2150
|
if (null === entry.link || void 0 === entry.link) {
|
|
2180
2151
|
log.error(`[zpress] Leaf entry "${entry.text}" has no link — skipping`);
|
|
@@ -2184,33 +2155,28 @@ function buildSidebarEntry(entry, icon) {
|
|
|
2184
2155
|
}
|
|
2185
2156
|
return {
|
|
2186
2157
|
text: entry.text,
|
|
2187
|
-
link: entry.link
|
|
2188
|
-
...maybeIcon(icon)
|
|
2158
|
+
link: entry.link
|
|
2189
2159
|
};
|
|
2190
2160
|
}
|
|
2191
|
-
function generateSidebar(entries
|
|
2161
|
+
function generateSidebar(entries) {
|
|
2192
2162
|
const visible = entries.filter((e)=>!e.hidden);
|
|
2193
2163
|
const pages = visible.filter((e)=>!e.items || 0 === e.items.length);
|
|
2194
2164
|
const sections = visible.filter((e)=>e.items && e.items.length > 0);
|
|
2195
2165
|
return [
|
|
2196
2166
|
...pages,
|
|
2197
2167
|
...sections
|
|
2198
|
-
].map(
|
|
2199
|
-
const icon = resolveIcon(icons, entry.text);
|
|
2200
|
-
return buildSidebarEntry(entry, icon);
|
|
2201
|
-
});
|
|
2168
|
+
].map(buildSidebarEntry);
|
|
2202
2169
|
}
|
|
2203
|
-
function buildNavEntry(entry
|
|
2170
|
+
function buildNavEntry(entry) {
|
|
2204
2171
|
const link = sidebar_resolveLink(entry);
|
|
2205
2172
|
const children = resolveChildren(entry);
|
|
2206
2173
|
return {
|
|
2207
2174
|
text: entry.text,
|
|
2208
2175
|
link,
|
|
2209
|
-
...maybeIcon(icon),
|
|
2210
2176
|
...maybeChildren(children)
|
|
2211
2177
|
};
|
|
2212
2178
|
}
|
|
2213
|
-
function generateNav(config, resolved
|
|
2179
|
+
function generateNav(config, resolved) {
|
|
2214
2180
|
if ('auto' !== config.nav && void 0 !== config.nav) return [
|
|
2215
2181
|
...config.nav
|
|
2216
2182
|
];
|
|
@@ -2220,7 +2186,7 @@ function generateNav(config, resolved, icons) {
|
|
|
2220
2186
|
return [
|
|
2221
2187
|
...nonIsolated,
|
|
2222
2188
|
...isolated
|
|
2223
|
-
].map(
|
|
2189
|
+
].map(buildNavEntry).filter((item)=>void 0 !== item.link);
|
|
2224
2190
|
}
|
|
2225
2191
|
function sidebar_findFirstLink(entry) {
|
|
2226
2192
|
if (entry.link) return entry.link;
|
|
@@ -2241,15 +2207,6 @@ function maybeLink(link) {
|
|
|
2241
2207
|
};
|
|
2242
2208
|
return {};
|
|
2243
2209
|
}
|
|
2244
|
-
function maybeIcon(icon) {
|
|
2245
|
-
if (icon) return {
|
|
2246
|
-
icon
|
|
2247
|
-
};
|
|
2248
|
-
return {};
|
|
2249
|
-
}
|
|
2250
|
-
function resolveIcon(icons, text) {
|
|
2251
|
-
if (icons) return icons.get(text);
|
|
2252
|
-
}
|
|
2253
2210
|
function sidebar_resolveLink(entry) {
|
|
2254
2211
|
if (entry.link) return entry.link;
|
|
2255
2212
|
return sidebar_findFirstLink(entry);
|
|
@@ -2379,10 +2336,10 @@ function resolveTags(tags) {
|
|
|
2379
2336
|
...tags
|
|
2380
2337
|
];
|
|
2381
2338
|
}
|
|
2382
|
-
function buildMultiSidebar(resolved, openapiSidebar
|
|
2339
|
+
function buildMultiSidebar(resolved, openapiSidebar) {
|
|
2383
2340
|
const rootEntries = resolved.filter((e)=>!e.isolated);
|
|
2384
2341
|
const isolatedEntries = resolved.filter((e)=>e.isolated && e.link);
|
|
2385
|
-
const docsSidebar = generateSidebar(rootEntries
|
|
2342
|
+
const docsSidebar = generateSidebar(rootEntries);
|
|
2386
2343
|
const childrenByLink = new Map(isolatedEntries.map((entry)=>{
|
|
2387
2344
|
const link = entry.link;
|
|
2388
2345
|
const items = resolveEntryItems(entry.items);
|
|
@@ -2394,23 +2351,19 @@ function buildMultiSidebar(resolved, openapiSidebar, icons) {
|
|
|
2394
2351
|
const isolatedSidebar = Object.fromEntries(isolatedEntries.flatMap((entry)=>{
|
|
2395
2352
|
const entryLink = entry.link;
|
|
2396
2353
|
const children = resolveChildrenByLink(childrenByLink, entryLink);
|
|
2397
|
-
const icon = icons.get(entry.text);
|
|
2398
2354
|
const parentLink = resolveParentLink(entryLink);
|
|
2399
2355
|
const parentEntry = match(parentLink).with(P.nonNullable, (pl)=>isolatedEntries.find((e)=>e.link === pl)).otherwise(()=>{});
|
|
2400
2356
|
const isChild = null != parentEntry && parentEntry !== entry;
|
|
2401
2357
|
const landing = {
|
|
2402
2358
|
text: entry.text,
|
|
2403
|
-
link: entryLink
|
|
2404
|
-
...multi_maybeIcon(icon)
|
|
2359
|
+
link: entryLink
|
|
2405
2360
|
};
|
|
2406
2361
|
const sidebarItems = match(isChild).with(true, ()=>{
|
|
2407
2362
|
const pe = parentEntry;
|
|
2408
2363
|
const peLink = pe.link;
|
|
2409
|
-
const parentIcon = icons.get(pe.text);
|
|
2410
2364
|
const parentLanding = {
|
|
2411
2365
|
text: pe.text,
|
|
2412
|
-
link: peLink
|
|
2413
|
-
...multi_maybeIcon(parentIcon)
|
|
2366
|
+
link: peLink
|
|
2414
2367
|
};
|
|
2415
2368
|
const siblings = isolatedEntries.filter((sib)=>{
|
|
2416
2369
|
const sibLink = sib.link;
|
|
@@ -2480,12 +2433,6 @@ function resolveParentLink(entryLink) {
|
|
|
2480
2433
|
if (segments) return segments;
|
|
2481
2434
|
return null;
|
|
2482
2435
|
}
|
|
2483
|
-
function multi_maybeIcon(icon) {
|
|
2484
|
-
if (icon) return {
|
|
2485
|
-
icon
|
|
2486
|
-
};
|
|
2487
|
-
return {};
|
|
2488
|
-
}
|
|
2489
2436
|
function buildSidebarGroup(text, link, children, collapsed) {
|
|
2490
2437
|
if (children.length > 0) return {
|
|
2491
2438
|
text,
|
|
@@ -2522,7 +2469,6 @@ function synthesizeWorkspaceSections(config) {
|
|
|
2522
2469
|
text: 'Apps',
|
|
2523
2470
|
link: '/apps',
|
|
2524
2471
|
isolated: true,
|
|
2525
|
-
icon: 'pixelarticons:device-laptop',
|
|
2526
2472
|
frontmatter: {
|
|
2527
2473
|
description: 'Deployable applications that make up the platform.'
|
|
2528
2474
|
},
|
|
@@ -2532,7 +2478,6 @@ function synthesizeWorkspaceSections(config) {
|
|
|
2532
2478
|
text: 'Packages',
|
|
2533
2479
|
link: '/packages',
|
|
2534
2480
|
isolated: true,
|
|
2535
|
-
icon: 'pixelarticons:archive',
|
|
2536
2481
|
frontmatter: {
|
|
2537
2482
|
description: 'Shared libraries and utilities consumed across apps and services.'
|
|
2538
2483
|
},
|
|
@@ -2545,7 +2490,6 @@ function synthesizeWorkspaceSections(config) {
|
|
|
2545
2490
|
text: group.name,
|
|
2546
2491
|
link,
|
|
2547
2492
|
isolated: true,
|
|
2548
|
-
icon: group.icon,
|
|
2549
2493
|
frontmatter: {
|
|
2550
2494
|
description: group.description
|
|
2551
2495
|
},
|
|
@@ -2766,9 +2710,8 @@ async function sync(config, options) {
|
|
|
2766
2710
|
skipped: 0
|
|
2767
2711
|
}));
|
|
2768
2712
|
const removed = await match(previousManifest).with(P.nonNullable, async (m)=>await cleanStaleFiles(outDir, m, ctx.manifest)).otherwise(()=>Promise.resolve(0));
|
|
2769
|
-
const
|
|
2770
|
-
const
|
|
2771
|
-
const nav = generateNav(config, resolved, icons);
|
|
2713
|
+
const sortedSidebar = buildMultiSidebar(resolved, openapiSidebar);
|
|
2714
|
+
const nav = generateNav(config, resolved);
|
|
2772
2715
|
await promises.writeFile(node_path.resolve(outDir, '.generated/sidebar.json'), JSON.stringify(sortedSidebar, null, 2), 'utf8');
|
|
2773
2716
|
await promises.writeFile(node_path.resolve(outDir, '.generated/nav.json'), JSON.stringify(nav, null, 2), 'utf8');
|
|
2774
2717
|
await saveManifest(outDir, ctx.manifest);
|
|
@@ -2864,17 +2807,6 @@ async function copyAll(src, dest) {
|
|
|
2864
2807
|
else await promises.copyFile(srcPath, destPath);
|
|
2865
2808
|
}, Promise.resolve());
|
|
2866
2809
|
}
|
|
2867
|
-
function buildIconMap(sections) {
|
|
2868
|
-
return new Map(sections.flatMap((section)=>{
|
|
2869
|
-
if (section.icon) return [
|
|
2870
|
-
[
|
|
2871
|
-
section.text,
|
|
2872
|
-
section.icon
|
|
2873
|
-
]
|
|
2874
|
-
];
|
|
2875
|
-
return [];
|
|
2876
|
-
}));
|
|
2877
|
-
}
|
|
2878
2810
|
function resolveQuiet(quiet) {
|
|
2879
2811
|
if (null != quiet) return quiet;
|
|
2880
2812
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zpress/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Config loading, sync engine, and asset utilities for zpress",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"config",
|
|
7
|
+
"core",
|
|
8
|
+
"docs",
|
|
9
|
+
"zpress"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/joggrdocs/zpress/tree/main/packages/core#readme",
|
|
12
|
+
"bugs": "https://github.com/joggrdocs/zpress/issues",
|
|
4
13
|
"license": "MIT",
|
|
5
14
|
"repository": {
|
|
6
15
|
"type": "git",
|
|
@@ -13,12 +22,13 @@
|
|
|
13
22
|
"type": "module",
|
|
14
23
|
"exports": {
|
|
15
24
|
".": {
|
|
16
|
-
"types": "./
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
17
26
|
"import": "./dist/index.mjs"
|
|
18
27
|
}
|
|
19
28
|
},
|
|
20
29
|
"publishConfig": {
|
|
21
|
-
"access": "public"
|
|
30
|
+
"access": "public",
|
|
31
|
+
"provenance": true
|
|
22
32
|
},
|
|
23
33
|
"dependencies": {
|
|
24
34
|
"@clack/prompts": "^1.1.0",
|