@docmd/core 0.4.3 → 0.4.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 docmd (docmd.io)
3
+ Copyright (c) 2025 docmd.io
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -157,6 +157,26 @@ module.exports = {
157
157
  - **Contributing**: We welcome PRs! See [CONTRIBUTING.md](.github/CONTRIBUTING.md).
158
158
  - **Support**: If you find `docmd` useful, please consider [sponsoring the project](https://github.com/sponsors/mgks) or giving it a star ⭐.
159
159
 
160
+ ## The docmd Ecosystem
161
+
162
+ `docmd` is a modular system. Here are the official packages:
163
+
164
+ **The Engine**
165
+ * [**@docmd/core**](https://www.npmjs.com/package/@docmd/core) - The CLI runner and build orchestrator.
166
+ * [**@docmd/parser**](https://www.npmjs.com/package/@docmd/parser) - The pure Markdown-to-HTML logic.
167
+ * [**@docmd/live**](https://www.npmjs.com/package/@docmd/live) - The browser-based Live Editor bundle.
168
+
169
+ **Interface & Design**
170
+ * [**@docmd/ui**](https://www.npmjs.com/package/@docmd/ui) - Base EJS templates and assets.
171
+ * [**@docmd/themes**](https://www.npmjs.com/package/@docmd/themes) - Official themes (Sky, Ruby, Retro).
172
+
173
+ **Plugins**
174
+ * [**@docmd/plugin-search**](https://www.npmjs.com/package/@docmd/plugin-search) - Offline full-text search.
175
+ * [**@docmd/plugin-mermaid**](https://www.npmjs.com/package/@docmd/plugin-mermaid) - Diagrams and flowcharts.
176
+ * [**@docmd/plugin-seo**](https://www.npmjs.com/package/@docmd/plugin-seo) - Meta tags and Open Graph data.
177
+ * [**@docmd/plugin-sitemap**](https://www.npmjs.com/package/@docmd/plugin-sitemap) - Automatic sitemap generation.
178
+ * [**@docmd/plugin-analytics**](https://www.npmjs.com/package/@docmd/plugin-analytics) - Google Analytics integration.
179
+
160
180
  ## License
161
181
 
162
182
  Distributed under the MIT License. See `LICENSE` for more information.
package/bin/docmd.js CHANGED
@@ -1,5 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ /**
4
+ * --------------------------------------------------------------------
5
+ * docmd : the minimalist, zero-config documentation generator.
6
+ *
7
+ * @package @docmd/core (and ecosystem)
8
+ * @website https://docmd.io
9
+ * @repository https://github.com/docmd-io/docmd
10
+ * @license MIT
11
+ * @copyright Copyright (c) 2025 docmd.io
12
+ *
13
+ * [docmd-source] - Please do not remove this header.
14
+ * --------------------------------------------------------------------
15
+ */
16
+
3
17
  const { program } = require('commander');
4
18
  const { version } = require('../package.json');
5
19
  const { initProject } = require('../src/commands/init');
@@ -39,10 +53,11 @@ program
39
53
 
40
54
  program
41
55
  .command('live')
42
- .action(async () => {
56
+ .description('Launch the Live Editor')
57
+ .option('--build-only', 'Generate the dist/ folder without starting the server')
58
+ .action(async (opts) => {
43
59
  try {
44
- await buildLive();
45
-
60
+ await buildLive({ serve: !opts.buildOnly });
46
61
  } catch (e) {
47
62
  console.error(e);
48
63
  process.exit(1);
package/package.json CHANGED
@@ -1,20 +1,21 @@
1
1
  {
2
2
  "name": "@docmd/core",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "The minimalist, zero-config documentation generator.",
5
5
  "bin": {
6
6
  "docmd": "bin/docmd.js"
7
7
  },
8
8
  "dependencies": {
9
- "@docmd/parser": "^0.4.3",
10
- "@docmd/ui": "^0.4.3",
11
- "@docmd/themes": "^0.4.3",
12
- "@docmd/live": "^0.4.3",
13
- "@docmd/plugin-search": "^0.4.3",
14
- "@docmd/plugin-seo": "^0.4.3",
15
- "@docmd/plugin-sitemap": "^0.4.3",
16
- "@docmd/plugin-analytics": "^0.4.3",
17
- "@docmd/plugin-mermaid": "^0.4.3",
9
+ "@docmd/parser": "^0.4.5",
10
+ "@docmd/ui": "^0.4.5",
11
+ "@docmd/themes": "^0.4.5",
12
+ "@docmd/live": "^0.4.5",
13
+ "@docmd/plugin-search": "^0.4.5",
14
+ "@docmd/plugin-seo": "^0.4.5",
15
+ "@docmd/plugin-sitemap": "^0.4.5",
16
+ "@docmd/plugin-analytics": "^0.4.5",
17
+ "@docmd/plugin-mermaid": "^0.4.5",
18
+ "esbuild": "^0.25.0",
18
19
  "chalk": "^4.1.2",
19
20
  "chokidar": "^3.5.3",
20
21
  "commander": "^11.0.0",
@@ -1,3 +1,17 @@
1
+ /**
2
+ * --------------------------------------------------------------------
3
+ * docmd : the minimalist, zero-config documentation generator.
4
+ *
5
+ * @package @docmd/core (and ecosystem)
6
+ * @website https://docmd.io
7
+ * @repository https://github.com/docmd-io/docmd
8
+ * @license MIT
9
+ * @copyright Copyright (c) 2025 docmd.io
10
+ *
11
+ * [docmd-source] - Please do not remove this header.
12
+ * --------------------------------------------------------------------
13
+ */
14
+
1
15
  const path = require('path');
2
16
  const fs = require('../utils/fs-utils');
3
17
  const { loadConfig } = require('../utils/config-loader');
@@ -6,22 +20,68 @@ const parser = require('@docmd/parser');
6
20
  const ui = require('@docmd/ui');
7
21
  const themes = require('@docmd/themes');
8
22
  const { findPageNeighbors } = require('@docmd/parser/src/utils/navigation-helper');
23
+ const esbuild = require('esbuild');
24
+
25
+ const COPYRIGHT_BANNER = `/*!
26
+ * --------------------------------------------------------------------
27
+ * docmd : the minimalist, zero-config documentation generator.
28
+ *
29
+ * @package @docmd/core (and ecosystem)
30
+ * @website https://docmd.io
31
+ * @repository https://github.com/docmd-io/docmd
32
+ * @license MIT
33
+ * @copyright Copyright (c) 2025-present docmd.io
34
+ *
35
+ * [docmd-source] - Please do not remove this header.
36
+ * --------------------------------------------------------------------
37
+ */`;
9
38
 
10
- async function findMarkdownFilesRecursive(dir) {
39
+ // Recursively find files with specific extensions
40
+ async function findFilesRecursive(dir, extensions) {
11
41
  let files = [];
12
42
  if (!await fs.exists(dir)) return [];
13
43
  const items = await fs.readdir(dir, { withFileTypes: true });
14
44
  for (const item of items) {
15
45
  const fullPath = path.join(dir, item.name);
16
46
  if (item.isDirectory()) {
17
- files = files.concat(await findMarkdownFilesRecursive(fullPath));
18
- } else if (item.isFile() && (item.name.endsWith('.md') || item.name.endsWith('.markdown'))) {
19
- files.push(fullPath);
47
+ files = files.concat(await findFilesRecursive(fullPath, extensions));
48
+ } else if (item.isFile()) {
49
+ // If extensions is null, get all files. Otherwise check list.
50
+ if (!extensions || extensions.includes(path.extname(item.name))) {
51
+ files.push(fullPath);
52
+ }
20
53
  }
21
54
  }
22
55
  return files;
23
56
  }
24
57
 
58
+ // Minify CSS and JS assets in the output directory
59
+ async function minifyAssets(outputDir) {
60
+ const assets = await findFilesRecursive(path.join(outputDir, 'assets'), ['.css', '.js']);
61
+
62
+ for (const file of assets) {
63
+ if (file.endsWith('.min.js') || file.endsWith('.min.css')) continue;
64
+
65
+ try {
66
+ const ext = path.extname(file);
67
+ const content = await fs.readFile(file, 'utf8');
68
+
69
+ const result = await esbuild.transform(content, {
70
+ loader: ext.slice(1),
71
+ minify: true,
72
+ legalComments: 'none'
73
+ });
74
+
75
+ const finalContent = COPYRIGHT_BANNER + '\n' + result.code;
76
+
77
+ await fs.writeFile(file, finalContent);
78
+ } catch (e) {
79
+ console.warn(`⚠️ Minification failed for ${path.basename(file)}: ${e.message}`);
80
+ }
81
+ }
82
+ }
83
+
84
+ // Generate HTML tag for asset
25
85
  function generateTag(pathOrUrl, type, attributes = {}) {
26
86
  const attrs = Object.entries(attributes).map(([k,v]) => v === true ? k : `${k}="${v}"`).join(' ');
27
87
  if (type === 'css') return `<link rel="stylesheet" href="${pathOrUrl}" ${attrs}>`;
@@ -29,6 +89,7 @@ function generateTag(pathOrUrl, type, attributes = {}) {
29
89
  return '';
30
90
  }
31
91
 
92
+ // Main Build Function
32
93
  async function buildSite(configPath, options = { isDev: false, offline: false }) {
33
94
  const CWD = process.cwd();
34
95
  const config = await loadConfig(configPath);
@@ -41,28 +102,20 @@ async function buildSite(configPath, options = { isDev: false, offline: false })
41
102
  if (!await fs.exists(srcDir)) throw new Error(`Source directory not found: ${srcDir}`);
42
103
  await fs.ensureDir(outputDir);
43
104
 
44
- // --- 1. ASSET COPYING (Simplified) ---
45
-
46
- // A. Copy ALL UI Assets (Deep copy)
47
- // This handles css, js, images, AND favicon.ico in root of assets
105
+ // --- 1. ASSET COPYING (Core & Themes) ---
48
106
  const uiAssets = ui.getAssetsDir();
49
- if (await fs.exists(uiAssets)) {
50
- await fs.copy(uiAssets, path.join(outputDir, 'assets'));
51
- }
107
+ if (await fs.exists(uiAssets)) await fs.copy(uiAssets, path.join(outputDir, 'assets'));
52
108
 
53
- // B. Copy Themes
54
109
  const themesDir = themes.getThemesDir();
55
- if (await fs.exists(themesDir)) {
56
- await fs.copy(themesDir, path.join(outputDir, 'assets/css'));
57
- }
110
+ if (await fs.exists(themesDir)) await fs.copy(themesDir, path.join(outputDir, 'assets/css'));
58
111
 
59
- // C. Copy User Assets (Override)
60
112
  const userAssets = path.resolve(CWD, 'assets');
61
113
  if (await fs.exists(userAssets)) await fs.copy(userAssets, path.join(outputDir, 'assets'));
62
114
 
63
- // --- 2. GENERATE TAGS ---
115
+ // --- 2. GENERATE TAGS & COPY PLUGIN ASSETS ---
64
116
  const assetTags = { head: [], body: [] };
65
117
 
118
+ // Theme CSS Tag
66
119
  if (config.theme && config.theme.name && config.theme.name !== 'default') {
67
120
  const themeFileName = `docmd-theme-${config.theme.name}.css`;
68
121
  if (await fs.exists(path.join(themes.getThemesDir(), themeFileName))) {
@@ -72,15 +125,12 @@ async function buildSite(configPath, options = { isDev: false, offline: false })
72
125
  }
73
126
  }
74
127
 
75
- // Core JS (Keep this, or move to EJS if you want full control, but keeping here is fine)
76
- assetTags.body.push(rel => generateTag(`${rel}assets/js/docmd-main.js?v=${buildHash}`, 'js'));
77
-
78
- // Lightbox
128
+ // Lightbox Tag
79
129
  if(await fs.exists(path.join(uiAssets, 'js/docmd-image-lightbox.js'))) {
80
130
  assetTags.body.push(rel => generateTag(`${rel}assets/js/docmd-image-lightbox.js?v=${buildHash}`, 'js'));
81
131
  }
82
132
 
83
- // Plugin Assets
133
+ // Plugin Assets Loop
84
134
  if (hooks.assets) {
85
135
  for (const getAssetsFn of hooks.assets) {
86
136
  const assets = getAssetsFn();
@@ -88,11 +138,13 @@ async function buildSite(configPath, options = { isDev: false, offline: false })
88
138
  for (const asset of assets) {
89
139
  let tagGen;
90
140
  if (asset.src && asset.dest) {
141
+ // Copy the file to output
91
142
  const destPath = path.join(outputDir, asset.dest);
92
143
  if (await fs.exists(asset.src)) {
93
144
  await fs.ensureDir(path.dirname(destPath));
94
145
  await fs.copy(asset.src, destPath);
95
146
  }
147
+ // Generate the HTML tag
96
148
  tagGen = (rel) => generateTag(`${rel}${asset.dest}?v=${buildHash}`, asset.type, asset.attributes);
97
149
  } else if (asset.url) {
98
150
  tagGen = () => generateTag(asset.url, asset.type, asset.attributes);
@@ -103,14 +155,19 @@ async function buildSite(configPath, options = { isDev: false, offline: false })
103
155
  }
104
156
  }
105
157
 
106
- // --- 3. PROCESSING ---
158
+ // --- 3. ASSET MINIFICATION ---
159
+ if (config.minify !== false && !options.isDev) {
160
+ await minifyAssets(outputDir);
161
+ }
162
+
163
+ // --- 4. PROCESSING ---
107
164
  const mdProcessor = parser.createMarkdownProcessor(config, (md) => hooks.markdownSetup.forEach(hook => hook(md)));
108
165
  const themeInitPath = path.join(ui.getTemplatesDir(), 'partials', 'theme-init.js');
109
166
  let themeInitScript = '';
110
167
  if (await fs.exists(themeInitPath)) themeInitScript = `<script>${await fs.readFile(themeInitPath, 'utf8')}</script>`;
111
168
  let footerHtml = config.footer ? mdProcessor.renderInline(config.footer) : '';
112
169
 
113
- const mdFiles = await findMarkdownFilesRecursive(srcDir);
170
+ const mdFiles = await findFilesRecursive(srcDir, ['.md', '.markdown']);
114
171
  const pages = [];
115
172
 
116
173
  for (const filePath of mdFiles) {
@@ -124,12 +181,12 @@ async function buildSite(configPath, options = { isDev: false, offline: false })
124
181
  pages.push({ ...processed, sourcePath: filePath, outputPath: htmlOutputPath });
125
182
  }
126
183
 
127
- // --- 4. RENDER LOOP ---
184
+ // --- 5. RENDER LOOP ---
128
185
  for (const page of pages) {
129
186
  // 1. Determine Output Location
130
187
  const finalPath = path.join(outputDir, page.outputPath);
131
188
 
132
- // 2. Calculate Relative Path to Root (CRITICAL FIX)
189
+ // 2. Calculate Relative Path to Root
133
190
  // "content/nested/index.html" -> dir "content/nested" -> relative "../.."
134
191
  const fileDir = path.dirname(page.outputPath);
135
192
  let relativePathToRoot = path.relative(fileDir, '.');
@@ -1,4 +1,16 @@
1
- // Source file from the docmd project — https://github.com/docmd-io/docmd
1
+ /**
2
+ * --------------------------------------------------------------------
3
+ * docmd : the minimalist, zero-config documentation generator.
4
+ *
5
+ * @package @docmd/core (and ecosystem)
6
+ * @website https://docmd.io
7
+ * @repository https://github.com/docmd-io/docmd
8
+ * @license MIT
9
+ * @copyright Copyright (c) 2025 docmd.io
10
+ *
11
+ * [docmd-source] - Please do not remove this header.
12
+ * --------------------------------------------------------------------
13
+ */
2
14
 
3
15
  const http = require('http');
4
16
  const WebSocket = require('ws');
@@ -1,4 +1,16 @@
1
- // Source file from the docmd project — https://github.com/docmd-io/docmd
1
+ /**
2
+ * --------------------------------------------------------------------
3
+ * docmd : the minimalist, zero-config documentation generator.
4
+ *
5
+ * @package @docmd/core (and ecosystem)
6
+ * @website https://docmd.io
7
+ * @repository https://github.com/docmd-io/docmd
8
+ * @license MIT
9
+ * @copyright Copyright (c) 2025 docmd.io
10
+ *
11
+ * [docmd-source] - Please do not remove this header.
12
+ * --------------------------------------------------------------------
13
+ */
2
14
 
3
15
  const fs = require('../utils/fs-utils');
4
16
  const path = require('path');
@@ -1,3 +1,17 @@
1
+ /**
2
+ * --------------------------------------------------------------------
3
+ * docmd : the minimalist, zero-config documentation generator.
4
+ *
5
+ * @package @docmd/core (and ecosystem)
6
+ * @website https://docmd.io
7
+ * @repository https://github.com/docmd-io/docmd
8
+ * @license MIT
9
+ * @copyright Copyright (c) 2025 docmd.io
10
+ *
11
+ * [docmd-source] - Please do not remove this header.
12
+ * --------------------------------------------------------------------
13
+ */
14
+
1
15
  async function buildLive(options = {}) {
2
16
  // Delegate to the standalone package
3
17
  const livePkg = require('@docmd/live');
@@ -1,3 +1,17 @@
1
+ /**
2
+ * --------------------------------------------------------------------
3
+ * docmd : the minimalist, zero-config documentation generator.
4
+ *
5
+ * @package @docmd/core (and ecosystem)
6
+ * @website https://docmd.io
7
+ * @repository https://github.com/docmd-io/docmd
8
+ * @license MIT
9
+ * @copyright Copyright (c) 2025 docmd.io
10
+ *
11
+ * [docmd-source] - Please do not remove this header.
12
+ * --------------------------------------------------------------------
13
+ */
14
+
1
15
  const path = require('path');
2
16
  const fs = require('fs');
3
17
  const { validateConfig } = require('@docmd/parser');
@@ -1,4 +1,16 @@
1
- // Source file from the docmd project — https://github.com/docmd-io/docmd
1
+ /**
2
+ * --------------------------------------------------------------------
3
+ * docmd : the minimalist, zero-config documentation generator.
4
+ *
5
+ * @package @docmd/core (and ecosystem)
6
+ * @website https://docmd.io
7
+ * @repository https://github.com/docmd-io/docmd
8
+ * @license MIT
9
+ * @copyright Copyright (c) 2025 docmd.io
10
+ *
11
+ * [docmd-source] - Please do not remove this header.
12
+ * --------------------------------------------------------------------
13
+ */
2
14
 
3
15
  const fs = require('node:fs/promises');
4
16
  const path = require('node:path');
@@ -1,4 +1,16 @@
1
- // Source file from the docmd project — https://github.com/docmd-io/docmd
1
+ /**
2
+ * --------------------------------------------------------------------
3
+ * docmd : the minimalist, zero-config documentation generator.
4
+ *
5
+ * @package @docmd/core (and ecosystem)
6
+ * @website https://docmd.io
7
+ * @repository https://github.com/docmd-io/docmd
8
+ * @license MIT
9
+ * @copyright Copyright (c) 2025 docmd.io
10
+ *
11
+ * [docmd-source] - Please do not remove this header.
12
+ * --------------------------------------------------------------------
13
+ */
2
14
 
3
15
  const chalk = require('chalk');
4
16
 
@@ -1,3 +1,17 @@
1
+ /**
2
+ * --------------------------------------------------------------------
3
+ * docmd : the minimalist, zero-config documentation generator.
4
+ *
5
+ * @package @docmd/core (and ecosystem)
6
+ * @website https://docmd.io
7
+ * @repository https://github.com/docmd-io/docmd
8
+ * @license MIT
9
+ * @copyright Copyright (c) 2025 docmd.io
10
+ *
11
+ * [docmd-source] - Please do not remove this header.
12
+ * --------------------------------------------------------------------
13
+ */
14
+
1
15
  const chalk = require('chalk');
2
16
 
3
17
  const hooks = {
@@ -1,3 +1,17 @@
1
+ /**
2
+ * --------------------------------------------------------------------
3
+ * docmd : the minimalist, zero-config documentation generator.
4
+ *
5
+ * @package @docmd/core (and ecosystem)
6
+ * @website https://docmd.io
7
+ * @repository https://github.com/docmd-io/docmd
8
+ * @license MIT
9
+ * @copyright Copyright (c) 2025 docmd.io
10
+ *
11
+ * [docmd-source] - Please do not remove this header.
12
+ * --------------------------------------------------------------------
13
+ */
14
+
1
15
  const { onPostBuild: searchPostBuild, getClientAssets: getSearchAssets } = require('@docmd/plugin-search');
2
16
  const { onPostBuild: sitemapPostBuild } = require('@docmd/plugin-sitemap');
3
17
  // We will implement SEO/Analytics later, but they are typically "hooks" inside the HTML generation