@knowcode/doc-builder 1.9.6 → 1.9.8

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.
@@ -1,16 +1,41 @@
1
1
  module.exports = {
2
- "siteName": "@knowcode/doc-builder",
3
- "siteDescription": "Beautiful documentation with the least effort possible",
2
+ "configVersion": "1.9.7",
4
3
  "docsDir": "docs",
5
4
  "outputDir": "html",
6
- "productionUrl": "https://doc-builder-delta.vercel.app",
5
+ "siteName": "@knowcode/doc-builder",
6
+ "siteDescription": "Beautiful documentation with the least effort possible",
7
+ "favicon": "✨",
7
8
  "features": {
8
9
  "authentication": false,
9
10
  "changelog": true,
10
11
  "mermaid": true,
12
+ "tooltips": true,
13
+ "search": false,
11
14
  "darkMode": true,
12
15
  "phosphorIcons": true,
13
- "menuDefaultOpen": false
16
+ "phosphorWeight": "regular",
17
+ "phosphorSize": "1.2em",
18
+ "normalizeTitle": true,
19
+ "showPdfDownload": true,
20
+ "menuDefaultOpen": false,
21
+ "attachments": true,
22
+ "dynamicNavIcons": true,
23
+ "subtleColors": true
24
+ },
25
+ "auth": {
26
+ "supabaseUrl": "https://xcihhnfcitjrwbynxmka.supabase.co",
27
+ "supabaseAnonKey": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhjaWhobmZjaXRqcndieW54bWthIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTM0Mzc2MzcsImV4cCI6MjA2OTAxMzYzN30.zvWp3JFIR8fBIiwuFF5gqOR_Kxb42baZS5fsBz60XOY"
28
+ },
29
+ "changelog": {
30
+ "daysBack": 14,
31
+ "enabled": true
32
+ },
33
+ "folderOrder": [],
34
+ "folderDescriptions": {},
35
+ "folderIcons": {},
36
+ "deployment": {
37
+ "platform": "vercel",
38
+ "outputDirectory": "html"
14
39
  },
15
40
  "seo": {
16
41
  "enabled": true,
@@ -25,13 +50,18 @@ module.exports = {
25
50
  "vercel",
26
51
  "notion-style"
27
52
  ],
28
- "generateSitemap": true,
29
- "generateRobotsTxt": true,
30
- "ogImage": "/og-default.png",
53
+ "titleTemplate": "{pageTitle} | {siteName}",
54
+ "autoKeywords": true,
55
+ "keywordLimit": 7,
56
+ "descriptionFallback": "smart",
31
57
  "organization": {
32
58
  "name": "Knowcode Ltd",
33
- "url": "https://knowcode.tech"
59
+ "url": "https://knowcode.tech",
60
+ "logo": ""
34
61
  },
62
+ "ogImage": "/og-default.png",
63
+ "generateSitemap": true,
64
+ "generateRobotsTxt": true,
35
65
  "customMetaTags": [
36
66
  {
37
67
  "name": "google-site-verification",
@@ -42,5 +72,43 @@ module.exports = {
42
72
  "content": "B2D8C4C12C530D47AA962B24CAA09630"
43
73
  }
44
74
  ]
45
- }
75
+ },
76
+ "attachmentTypes": [
77
+ ".pdf",
78
+ ".doc",
79
+ ".docx",
80
+ ".xls",
81
+ ".xlsx",
82
+ ".csv",
83
+ ".ppt",
84
+ ".pptx",
85
+ ".txt",
86
+ ".rtf",
87
+ ".html",
88
+ ".htm",
89
+ ".zip",
90
+ ".tar",
91
+ ".gz",
92
+ ".7z",
93
+ ".rar",
94
+ ".png",
95
+ ".jpg",
96
+ ".jpeg",
97
+ ".gif",
98
+ ".svg",
99
+ ".webp",
100
+ ".ico",
101
+ ".bmp",
102
+ ".json",
103
+ ".xml",
104
+ ".yaml",
105
+ ".yml",
106
+ ".toml",
107
+ ".mp4",
108
+ ".mp3",
109
+ ".wav",
110
+ ".avi",
111
+ ".mov"
112
+ ],
113
+ "productionUrl": "https://doc-builder-delta.vercel.app"
46
114
  };
@@ -0,0 +1,47 @@
1
+ module.exports = {
2
+ "configVersion": "1.9.6",
3
+ "siteName": "@knowcode/doc-builder",
4
+ "siteDescription": "Beautiful documentation with the least effort possible",
5
+ "docsDir": "docs",
6
+ "outputDir": "html",
7
+ "productionUrl": "https://doc-builder-delta.vercel.app",
8
+ "features": {
9
+ "authentication": false,
10
+ "changelog": true,
11
+ "mermaid": true,
12
+ "darkMode": true,
13
+ "phosphorIcons": true,
14
+ "menuDefaultOpen": false
15
+ },
16
+ "seo": {
17
+ "enabled": true,
18
+ "siteUrl": "https://doc-builder-delta.vercel.app",
19
+ "author": "Lindsay Smith",
20
+ "twitterHandle": "@planbbackups",
21
+ "language": "en-US",
22
+ "keywords": [
23
+ "documentation",
24
+ "markdown",
25
+ "static site generator",
26
+ "vercel",
27
+ "notion-style"
28
+ ],
29
+ "generateSitemap": true,
30
+ "generateRobotsTxt": true,
31
+ "ogImage": "/og-default.png",
32
+ "organization": {
33
+ "name": "Knowcode Ltd",
34
+ "url": "https://knowcode.tech"
35
+ },
36
+ "customMetaTags": [
37
+ {
38
+ "name": "google-site-verification",
39
+ "content": "FtzcDTf5BQ9K5EfnGazQkgU2U4FiN3ITzM7gHwqUAqQ"
40
+ },
41
+ {
42
+ "name": "msvalidate.01",
43
+ "content": "B2D8C4C12C530D47AA962B24CAA09630"
44
+ }
45
+ ]
46
+ }
47
+ };
package/lib/config.js CHANGED
@@ -1,12 +1,16 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const chalk = require('chalk');
4
+ const semver = require('semver');
4
5
  const sharedAuth = require('./shared-auth-config');
5
6
 
6
7
  /**
7
8
  * Default configuration
8
9
  */
9
10
  const defaultConfig = {
11
+ // Configuration version - updated when new config options are added
12
+ configVersion: require('../package.json').version,
13
+
10
14
  // Source and output directories
11
15
  docsDir: 'docs',
12
16
  outputDir: 'html',
@@ -214,6 +218,112 @@ const notionInspiredPreset = {
214
218
  }
215
219
  };
216
220
 
221
+ /**
222
+ * Check if config migration is needed
223
+ * @param {object} userConfig - Current user configuration
224
+ * @param {string} packageVersion - Current package version
225
+ * @returns {boolean} - Whether migration is needed
226
+ */
227
+ function needsMigration(userConfig, packageVersion) {
228
+ // If no configVersion in user config, it needs migration
229
+ if (!userConfig.configVersion) {
230
+ return true;
231
+ }
232
+
233
+ try {
234
+ // Use semver to compare versions
235
+ return semver.lt(userConfig.configVersion, packageVersion);
236
+ } catch (error) {
237
+ console.warn(chalk.yellow(`Warning: Invalid version format in config: ${userConfig.configVersion}`));
238
+ return true; // Migrate on version parse errors
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Deep merge objects, preserving user customizations
244
+ * @param {object} target - Target object (defaults)
245
+ * @param {object} source - Source object (user config)
246
+ * @returns {object} - Merged object
247
+ */
248
+ function deepMerge(target, source) {
249
+ const result = { ...target };
250
+
251
+ for (const key in source) {
252
+ if (source.hasOwnProperty(key)) {
253
+ if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
254
+ // Recursively merge objects
255
+ result[key] = deepMerge(target[key] || {}, source[key]);
256
+ } else {
257
+ // Use source value (user customization takes precedence)
258
+ result[key] = source[key];
259
+ }
260
+ }
261
+ }
262
+
263
+ return result;
264
+ }
265
+
266
+ /**
267
+ * Migrate user configuration to current version
268
+ * @param {object} userConfig - Current user configuration
269
+ * @param {string} targetVersion - Target package version
270
+ * @returns {object} - Migrated configuration
271
+ */
272
+ function migrateConfig(userConfig, targetVersion) {
273
+ console.log(chalk.blue(`🔄 Migrating config from ${userConfig.configVersion || 'unknown'} to ${targetVersion}`));
274
+
275
+ // Start with current defaults
276
+ let migratedConfig = { ...defaultConfig };
277
+
278
+ // Deep merge user customizations
279
+ migratedConfig = deepMerge(migratedConfig, userConfig);
280
+
281
+ // Update the config version
282
+ migratedConfig.configVersion = targetVersion;
283
+
284
+ // Log new features added
285
+ const newFeatures = [];
286
+ if (!userConfig.features?.dynamicNavIcons) {
287
+ newFeatures.push('Dynamic navigation icons');
288
+ }
289
+ if (!userConfig.features?.subtleColors) {
290
+ newFeatures.push('Subtle status colors');
291
+ }
292
+ if (!userConfig.features?.phosphorWeight) {
293
+ newFeatures.push('Phosphor icon weight options');
294
+ }
295
+ if (!userConfig.features?.phosphorSize) {
296
+ newFeatures.push('Phosphor icon sizing');
297
+ }
298
+
299
+ if (newFeatures.length > 0) {
300
+ console.log(chalk.green(`✨ Added new features: ${newFeatures.join(', ')}`));
301
+ }
302
+
303
+ return migratedConfig;
304
+ }
305
+
306
+ /**
307
+ * Save configuration to file
308
+ * @param {string} configPath - Path to config file
309
+ * @param {object} config - Configuration object to save
310
+ */
311
+ async function saveConfig(configPath, config) {
312
+ // Create backup of existing config
313
+ if (fs.existsSync(configPath)) {
314
+ const backupPath = `${configPath}.backup.${Date.now()}`;
315
+ await fs.copy(configPath, backupPath);
316
+ console.log(chalk.gray(`💾 Backed up existing config to ${path.basename(backupPath)}`));
317
+ }
318
+
319
+ // Format config as module.exports
320
+ const configContent = `module.exports = ${JSON.stringify(config, null, 2)};
321
+ `;
322
+
323
+ await fs.writeFile(configPath, configContent);
324
+ console.log(chalk.green(`✅ Updated config file: ${path.basename(configPath)}`));
325
+ }
326
+
217
327
  /**
218
328
  * Load configuration
219
329
  */
@@ -230,7 +340,15 @@ async function loadConfig(configPath, options = {}) {
230
340
  if (fs.existsSync(customConfigPath)) {
231
341
  try {
232
342
  const customConfig = require(customConfigPath);
233
- config = { ...config, ...customConfig };
343
+ const packageJson = require('../package.json');
344
+
345
+ // Check if migration is needed
346
+ if (needsMigration(customConfig, packageJson.version)) {
347
+ config = migrateConfig(customConfig, packageJson.version);
348
+ await saveConfig(customConfigPath, config);
349
+ } else {
350
+ config = { ...config, ...customConfig };
351
+ }
234
352
 
235
353
  // Handle alternative config formats
236
354
  if (customConfig.site) {
@@ -257,7 +375,10 @@ async function loadConfig(configPath, options = {}) {
257
375
  console.warn(chalk.yellow(`Warning: Failed to load config file: ${error.message}`));
258
376
  }
259
377
  } else if (!options.preset && !options.legacy) {
260
- console.log(chalk.gray('No config file found, using defaults'));
378
+ console.log(chalk.gray('No config file found, creating default config...'));
379
+ // Create default config file with all settings
380
+ await saveConfig(customConfigPath, config);
381
+ console.log(chalk.green(`✅ Created ${configPath} with default settings`));
261
382
  }
262
383
 
263
384
  // Apply CLI options (these override config file)
@@ -358,5 +479,8 @@ module.exports = {
358
479
  defaultConfig,
359
480
  notionInspiredPreset,
360
481
  loadConfig,
361
- createDefaultConfig
482
+ createDefaultConfig,
483
+ needsMigration,
484
+ migrateConfig,
485
+ saveConfig
362
486
  };
@@ -1326,7 +1326,7 @@ async function generateSupabaseAuthFiles(outputDir, config) {
1326
1326
  const supabaseAuth = new SupabaseAuth(authConfig);
1327
1327
 
1328
1328
  // Generate auth script and save to js/auth.js
1329
- const authScript = supabaseAuth.generateAuthScript();
1329
+ const authScript = supabaseAuth.generateAuthScript(config);
1330
1330
  await fs.writeFile(path.join(outputDir, 'js', 'auth.js'), authScript);
1331
1331
 
1332
1332
  // Also copy to root for backward compatibility
@@ -81,6 +81,7 @@ const emojiToPhosphor = {
81
81
  '🧭': '<i class="ph ph-compass" aria-label="navigation"></i>',
82
82
  '🗺️': '<i class="ph ph-map-trifold" aria-label="map"></i>',
83
83
  '🌐': '<i class="ph ph-globe" aria-label="global"></i>',
84
+ '🌍': '<i class="ph ph-globe-hemisphere-west" aria-label="world"></i>',
84
85
 
85
86
  // Communication
86
87
  '📧': '<i class="ph ph-envelope" aria-label="email"></i>',
@@ -250,6 +251,20 @@ const emojiToPhosphor = {
250
251
  '📑': '<i class="ph ph-file-text" aria-label="document"></i>',
251
252
  '💀': '<i class="ph ph-skull" aria-label="skull"></i>',
252
253
  '🔤': '<i class="ph ph-textbox" aria-label="letters"></i>',
254
+ '🧱': '<i class="ph ph-wall" aria-label="brick"></i>',
255
+ '🖼️': '<i class="ph ph-image" aria-label="picture"></i>',
256
+ '🎂': '<i class="ph ph-cake" aria-label="birthday"></i>',
257
+ '↩️': '<i class="ph ph-arrow-u-left" aria-label="return"></i>',
258
+ '📜': '<i class="ph ph-scroll" aria-label="scroll"></i>',
259
+ '🕰️': '<i class="ph ph-clock" aria-label="clock"></i>',
260
+ '🧮': '<i class="ph ph-calculator" aria-label="calculator"></i>',
261
+ '🎫': '<i class="ph ph-ticket" aria-label="ticket"></i>',
262
+ '🌿': '<i class="ph ph-leaf" aria-label="leaf"></i>',
263
+ '🥊': '<i class="ph ph-boxing-glove" aria-label="boxing"></i>',
264
+ '🎛️': '<i class="ph ph-faders" aria-label="controls"></i>',
265
+ '🗣️': '<i class="ph ph-megaphone-simple" aria-label="speaking"></i>',
266
+ '💝': '<i class="ph ph-gift" aria-label="gift with bow"></i>',
267
+ '🐌': '<i class="ph ph-spiral" aria-label="snail"></i>',
253
268
  };
254
269
 
255
270
  /**
@@ -26,7 +26,7 @@ class SupabaseAuth {
26
26
  /**
27
27
  * Generate client-side auth script for inclusion in HTML pages
28
28
  */
29
- generateAuthScript() {
29
+ generateAuthScript(fullConfig) {
30
30
  return `
31
31
  /**
32
32
  * Supabase Authentication for Documentation Site
@@ -56,6 +56,9 @@ class SupabaseAuth {
56
56
  // Check authentication and site access
57
57
  async function checkAuth() {
58
58
  try {
59
+ // Check if global authentication is enabled
60
+ const isGlobalAuthEnabled = ${fullConfig.features?.authentication === 'supabase'};
61
+
59
62
  // Check if current page is in private directory
60
63
  const currentPath = window.location.pathname;
61
64
  const isPrivatePage = currentPath.includes('/private/');
@@ -64,11 +67,11 @@ class SupabaseAuth {
64
67
  const { data: { user }, error: userError } = await supabaseClient.auth.getUser();
65
68
 
66
69
  if (userError || !user) {
67
- // Only redirect if we're on a private page
68
- if (isPrivatePage) {
70
+ // Redirect if global auth is enabled OR if we're on a private page
71
+ if (isGlobalAuthEnabled || isPrivatePage) {
69
72
  redirectToLogin();
70
73
  } else {
71
- // Public page, just show it
74
+ // Truly public page (no global auth and not private)
72
75
  document.body.classList.add('authenticated'); // Use same class to show body
73
76
  updateAuthButton(false);
74
77
  }
@@ -84,7 +87,7 @@ class SupabaseAuth {
84
87
  .single();
85
88
 
86
89
  if (accessError || !access) {
87
- if (isPrivatePage) {
90
+ if (isGlobalAuthEnabled || isPrivatePage) {
88
91
  showAccessDenied();
89
92
  } else {
90
93
  // Public page, show it but don't grant private navigation access
@@ -102,7 +105,8 @@ class SupabaseAuth {
102
105
 
103
106
  } catch (error) {
104
107
  console.error('Auth check failed:', error);
105
- if (window.location.pathname.includes('/private/')) {
108
+ const isGlobalAuthEnabled = ${fullConfig.features?.authentication === 'supabase'};
109
+ if (isGlobalAuthEnabled || window.location.pathname.includes('/private/')) {
106
110
  redirectToLogin();
107
111
  } else {
108
112
  // Public page, show it anyway
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowcode/doc-builder",
3
- "version": "1.9.6",
3
+ "version": "1.9.8",
4
4
  "description": "Reusable documentation builder for markdown-based sites with Vercel deployment support",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -37,7 +37,8 @@
37
37
  "gray-matter": "^4.0.3",
38
38
  "marked": "^15.0.12",
39
39
  "ora": "5.4.1",
40
- "prompts": "^2.4.2"
40
+ "prompts": "^2.4.2",
41
+ "semver": "^7.7.2"
41
42
  },
42
43
  "engines": {
43
44
  "node": ">=14.0.0"