@life-and-dev/mdsite 0.6.0 → 0.7.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.
Files changed (63) hide show
  1. package/README.md +16 -17
  2. package/dist/commands/clean.js +28 -10
  3. package/dist/commands/clean.js.map +1 -1
  4. package/dist/commands/commands.test.js +49 -21
  5. package/dist/commands/commands.test.js.map +1 -1
  6. package/dist/commands/generate.js +5 -4
  7. package/dist/commands/generate.js.map +1 -1
  8. package/dist/commands/init.js +2 -2
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/prepare.js +2 -2
  11. package/dist/commands/prepare.js.map +1 -1
  12. package/dist/commands/prepare.test.js +9 -10
  13. package/dist/commands/prepare.test.js.map +1 -1
  14. package/dist/commands/preview.js +21 -21
  15. package/dist/commands/preview.js.map +1 -1
  16. package/dist/commands/start.js +13 -11
  17. package/dist/commands/start.js.map +1 -1
  18. package/dist/commands/stop.js +7 -4
  19. package/dist/commands/stop.js.map +1 -1
  20. package/dist/commands/workflows.test.js +25 -24
  21. package/dist/commands/workflows.test.js.map +1 -1
  22. package/dist/config/default-mdsite-config.js +7 -8
  23. package/dist/config/default-mdsite-config.js.map +1 -1
  24. package/dist/config/default-mdsite-config.test.js +7 -8
  25. package/dist/config/default-mdsite-config.test.js.map +1 -1
  26. package/dist/config/mdsite-config.d.ts +46 -10
  27. package/dist/config/mdsite-config.js +46 -24
  28. package/dist/config/mdsite-config.js.map +1 -1
  29. package/dist/config/mdsite-config.test.js +55 -50
  30. package/dist/config/mdsite-config.test.js.map +1 -1
  31. package/dist/process/child-process.d.ts +4 -0
  32. package/dist/process/child-process.js +33 -1
  33. package/dist/process/child-process.js.map +1 -1
  34. package/dist/process/child-process.test.js +39 -3
  35. package/dist/process/child-process.test.js.map +1 -1
  36. package/dist/process/runtime-state.d.ts +13 -5
  37. package/dist/process/runtime-state.js +21 -13
  38. package/dist/process/runtime-state.js.map +1 -1
  39. package/dist/process/runtime-state.test.js +3 -5
  40. package/dist/process/runtime-state.test.js.map +1 -1
  41. package/dist/renderer/mdsite-nuxt.d.ts +28 -3
  42. package/dist/renderer/mdsite-nuxt.js +29 -12
  43. package/dist/renderer/mdsite-nuxt.js.map +1 -1
  44. package/dist/renderer/mdsite-nuxt.test.js +34 -12
  45. package/dist/renderer/mdsite-nuxt.test.js.map +1 -1
  46. package/mdsite-nuxt/app/components/AppFooter.vue +84 -22
  47. package/mdsite-nuxt/app/composables/useFooter.test.ts +54 -0
  48. package/mdsite-nuxt/app/composables/useFooter.ts +48 -31
  49. package/mdsite-nuxt/app/composables/useSiteConfig.test.ts +13 -87
  50. package/mdsite-nuxt/app/composables/useSiteConfig.ts +7 -26
  51. package/mdsite-nuxt/app/composables/useSourceEdit.test.ts +103 -0
  52. package/mdsite-nuxt/app/composables/useSourceEdit.ts +39 -51
  53. package/mdsite-nuxt/app/layouts/default.vue +10 -3
  54. package/mdsite-nuxt/nuxt.config.ts +21 -14
  55. package/mdsite-nuxt/scripts/generate-favicons.test.ts +3 -3
  56. package/mdsite-nuxt/scripts/generate-favicons.ts +4 -4
  57. package/mdsite-nuxt/scripts/generate-indices.test.ts +71 -10
  58. package/mdsite-nuxt/scripts/generate-indices.ts +161 -27
  59. package/mdsite-nuxt/scripts/renderer-hooks.test.ts +0 -86
  60. package/mdsite-nuxt/scripts/renderer-hooks.ts +1 -48
  61. package/mdsite-nuxt/utils/mdsite-config.ts +86 -41
  62. package/package.json +1 -1
  63. package/mdsite-nuxt/example.config.yml +0 -67
@@ -26,25 +26,52 @@ export type MdsiteMenuItem = string | null | MdsiteMenuGroup
26
26
  export type MdsiteMenuGroup = { [key: string]: MdsiteMenuValue }
27
27
  export type MdsiteMenuValue = string | null | MdsiteMenuItem[]
28
28
 
29
+ /**
30
+ * Footer items mirror the `menu` shape but with no nested sub-menus. Each item
31
+ * is one of:
32
+ * - a bare markdown file name (string) — title is read from the file's H1
33
+ * - `null` — rendered as a vertical separator
34
+ * - a single-key object — the key is the link text, the value is either an
35
+ * internal markdown path or an external URL (http/https)
36
+ *
37
+ * Lives under `features.footer` in `mdsite.yml`. Keep this type in sync with
38
+ * the CLI's `FooterItem` in `src/config/mdsite-config.ts`.
39
+ */
40
+ export type MdsiteFooterItem = string | null | { [key: string]: string | null }
41
+
29
42
  export interface MdsiteConfig {
30
- content?: {
31
- path?: string
32
- }
33
- favicon: string
34
43
  features: {
35
44
  bibleTooltips: boolean
36
- sourceEdit: boolean
45
+ /**
46
+ * URL prefix used to build the "Edit on …" link for a content page.
47
+ * The renderer appends `<contentPath>.md` to this prefix, so it must
48
+ * already include any path segment between the host and the content
49
+ * file (e.g. `https://github.com/org/repo/edit/main/`). An empty
50
+ * string disables the link.
51
+ */
52
+ sourceEdit: string
53
+ /**
54
+ * Footer items mirror the `menu` shape but with no nested sub-menus. Each
55
+ * item is one of:
56
+ * - a bare markdown file name (string) — title is read from the file's H1
57
+ * - `null` — rendered as a vertical separator
58
+ * - a single-key object — the key is the link text, the value is either
59
+ * an internal markdown path or an external URL (http/https)
60
+ *
61
+ * Lives under `features.footer` in `mdsite.yml`. Keep this in sync with
62
+ * the CLI's `FooterItem` in `src/config/mdsite-config.ts`.
63
+ */
64
+ footer: MdsiteFooterItem[]
37
65
  }
38
66
  menu: MdsiteMenuItem[]
39
- footer: string[]
40
- server: {
67
+ paths: {
68
+ input: string
69
+ build: string
41
70
  output: string
42
- path: string
43
- repo: string
44
- gitBranch: string
45
71
  }
46
72
  site: {
47
73
  canonical: string
74
+ favicon: string
48
75
  name: string
49
76
  }
50
77
  themes: {
@@ -190,8 +217,8 @@ export function resolveContentDir(options: {
190
217
 
191
218
  if (resolvedConfigPath) {
192
219
  const configDir = path.dirname(resolvedConfigPath);
193
- const contentPath = resolveContentConfigPath(rawConfig.content);
194
- return contentPath ? path.resolve(configDir, contentPath) : configDir;
220
+ const inputPath = resolveInputConfigPath(rawConfig.paths?.input);
221
+ return inputPath ? path.resolve(configDir, inputPath) : configDir;
195
222
  }
196
223
 
197
224
  if (options.searchFrom) {
@@ -202,40 +229,58 @@ export function resolveContentDir(options: {
202
229
  }
203
230
 
204
231
  /**
205
- * Extract the content path from either the shorthand string form
206
- * (`content: docs`) or the explicit object form (`content:\n path: docs`).
207
- * Returns undefined when no usable path is configured.
232
+ * Extract the content path from `paths.input`. Returns undefined when no
233
+ * usable path is configured.
208
234
  */
209
- function resolveContentConfigPath(rawContent: unknown): string | undefined {
210
- if (typeof rawContent === 'string') {
211
- const trimmed = rawContent.trim();
235
+ function resolveInputConfigPath(rawInput: unknown): string | undefined {
236
+ if (typeof rawInput === 'string') {
237
+ const trimmed = rawInput.trim();
212
238
  return trimmed.length > 0 ? trimmed : undefined;
213
239
  }
214
240
 
215
- if (rawContent && typeof rawContent === 'object' && typeof (rawContent as { path?: unknown }).path === 'string') {
216
- return (rawContent as { path: string }).path;
241
+ if (rawInput && typeof rawInput === 'object' && typeof (rawInput as { path?: unknown }).path === 'string') {
242
+ return (rawInput as { path: string }).path;
217
243
  }
218
244
 
219
245
  return undefined;
220
246
  }
221
247
 
248
+ /**
249
+ * Runtime type-guard for entries inside the `footer:` YAML list. Accepts:
250
+ * - non-empty strings (file names / external URLs)
251
+ * - `null` (separator)
252
+ * - single-key objects whose value is a string or `null`
253
+ * Drops anything else silently to stay backwards-compatible with malformed
254
+ * user input; the CLI mirrors this filter.
255
+ */
256
+ function isValidFooterItem(item: unknown): item is MdsiteFooterItem {
257
+ if (item === null) return true
258
+ if (typeof item === 'string') return item.trim().length > 0
259
+ if (typeof item === 'object') {
260
+ const keys = Object.keys(item as Record<string, unknown>)
261
+ if (keys.length !== 1) return false
262
+ const value = (item as Record<string, unknown>)[keys[0]]
263
+ return value === null || typeof value === 'string'
264
+ }
265
+ return false
266
+ }
267
+
222
268
  function createDefaultMdsiteConfig(siteName: string): MdsiteConfig {
223
269
  return {
224
- favicon: '',
225
270
  features: {
226
271
  bibleTooltips: true,
227
- sourceEdit: true
272
+ sourceEdit: '',
273
+ footer: []
228
274
  },
229
275
  menu: [],
230
- footer: [],
231
- server: {
232
- output: '.output',
233
- path: '.mdsite',
234
- repo: 'https://github.com/life-and-dev/mdsite',
235
- gitBranch: 'main'
276
+ paths: {
277
+ input: '',
278
+ build: '.mdsite',
279
+ output: '.output'
236
280
  },
237
281
  site: {
238
282
  canonical: '',
283
+ favicon: '',
239
284
  name: siteName
240
285
  },
241
286
  themes: {
@@ -251,27 +296,27 @@ function createDefaultMdsiteConfig(siteName: string): MdsiteConfig {
251
296
 
252
297
  function normalizeMdsiteConfig(rawConfig: Record<string, any>, contentDir: string): MdsiteConfig {
253
298
  const fallbackConfig = createDefaultMdsiteConfig(path.basename(contentDir) || 'Site');
254
- const contentPath = resolveContentConfigPath(rawConfig.content);
299
+ const inputPath = resolveInputConfigPath(rawConfig.paths?.input);
255
300
 
256
301
  return {
257
- favicon: typeof rawConfig.favicon === 'string' ? rawConfig.favicon : fallbackConfig.favicon,
258
302
  features: {
259
- bibleTooltips: rawConfig.features?.bibleTooltips ?? fallbackConfig.features.bibleTooltips,
260
- sourceEdit: rawConfig.features?.sourceEdit ?? fallbackConfig.features.sourceEdit
303
+ bibleTooltips: rawConfig.features?.['bible-tooltips'] ?? fallbackConfig.features.bibleTooltips,
304
+ sourceEdit: typeof rawConfig.features?.['source-edit'] === 'string'
305
+ ? rawConfig.features['source-edit']
306
+ : fallbackConfig.features.sourceEdit,
307
+ footer: Array.isArray(rawConfig.features?.footer)
308
+ ? rawConfig.features.footer.filter(isValidFooterItem)
309
+ : fallbackConfig.features.footer
261
310
  },
262
- content: contentPath ? { path: contentPath } : fallbackConfig.content,
263
311
  menu: Array.isArray(rawConfig.menu) ? rawConfig.menu : fallbackConfig.menu,
264
- footer: Array.isArray(rawConfig.footer) ? rawConfig.footer.filter((item): item is string => typeof item === 'string') : [],
265
- server: {
266
- output: typeof rawConfig.server?.output === 'string' ? rawConfig.server.output : fallbackConfig.server.output,
267
- path: typeof rawConfig.server?.path === 'string' ? rawConfig.server.path : fallbackConfig.server.path,
268
- repo: typeof rawConfig.server?.repo === 'string' ? rawConfig.server.repo : fallbackConfig.server.repo,
269
- gitBranch: typeof rawConfig.server?.['git-branch'] === 'string' && rawConfig.server['git-branch'].trim()
270
- ? rawConfig.server['git-branch']
271
- : fallbackConfig.server.gitBranch
312
+ paths: {
313
+ input: inputPath ?? fallbackConfig.paths.input,
314
+ build: typeof rawConfig.paths?.build === 'string' ? rawConfig.paths.build : fallbackConfig.paths.build,
315
+ output: typeof rawConfig.paths?.output === 'string' ? rawConfig.paths.output : fallbackConfig.paths.output
272
316
  },
273
317
  site: {
274
318
  canonical: typeof rawConfig.site?.canonical === 'string' ? rawConfig.site.canonical : fallbackConfig.site.canonical,
319
+ favicon: typeof rawConfig.site?.favicon === 'string' ? rawConfig.site.favicon : fallbackConfig.site.favicon,
275
320
  name: typeof rawConfig.site?.name === 'string' && rawConfig.site.name.trim() ? rawConfig.site.name : fallbackConfig.site.name
276
321
  },
277
322
  themes: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@life-and-dev/mdsite",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "Local-first CLI that orchestrates mdsite-nuxt",
@@ -1,67 +0,0 @@
1
- content:
2
- git:
3
- branch: main
4
- path: ../example # Path where git content is cloned/checked out relative to this file
5
- repo: https://github.com/life-and-dev/md-site # If missing, assume content is part of this git repo
6
- path: ../example/docs # Path where md files are located relative to this file
7
- features:
8
- bibleTooltips: true
9
- sourceEdit: false
10
- site:
11
- canonical: 'https://life-and-dev.github.io/mdsite/'
12
- name: 'Example Site'
13
- themes:
14
- light:
15
- colors:
16
- primary: '#802020'
17
- secondary: '#ffffff'
18
- selected: '#802020'
19
- error: '#d64d5b'
20
- warning: '#c29e4a'
21
- info: '#548af7'
22
- success: '#6aab73'
23
- background: '#f9f9f9'
24
- surface: '#ffffff'
25
- surface-rail: '#f5f5f5'
26
- surface-appbar: '#f0f0f0'
27
- on-surface-rail: '#2a2a2a'
28
- on-surface-appbar: '#000000'
29
- on-background: '#292929'
30
- on-surface: '#292929'
31
- on-primary: '#ffffff'
32
- on-secondary: '#ffffff'
33
- on-selectable: '#292929'
34
- on-selected: '#ffffff'
35
- on-error: '#ffffff'
36
- on-warning: '#ffffff'
37
- on-info: '#ffffff'
38
- on-success: '#ffffff'
39
- outline: '#d7d7d7'
40
- outline-bars: '#f4f4f4'
41
- dark:
42
- colors:
43
- primary: '#af5f5f'
44
- secondary: '#302d2b'
45
- selected: '#af5f5f'
46
- error: '#d64d5b'
47
- warning: '#c29e4a'
48
- info: '#548af7'
49
- success: '#6aab73'
50
- background: '#161616'
51
- surface: '#0d0d0d'
52
- surface-rail: '#252525'
53
- surface-appbar: '#282828'
54
- on-surface-rail: '#d0d0d0'
55
- on-surface-appbar: '#ffffff'
56
- on-background: '#d1d1d1'
57
- on-surface: '#d1d1d1'
58
- on-primary: '#111111'
59
- on-secondary: '#111111'
60
- on-selectable: '#d1d1d1'
61
- on-selected: '#ffffff'
62
- on-error: '#ffffff'
63
- on-warning: '#ffffff'
64
- on-info: '#ffffff'
65
- on-success: '#ffffff'
66
- outline: '#303030'
67
- outline-bars: '#161616'