@docmd/core 0.4.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 +21 -0
- package/README.md +166 -0
- package/bin/docmd.js +52 -0
- package/package.json +23 -0
- package/src/commands/build.js +262 -0
- package/src/commands/dev.js +353 -0
- package/src/commands/init.js +277 -0
- package/src/commands/live.js +15 -0
- package/src/utils/config-loader.js +38 -0
- package/src/utils/fs-utils.js +40 -0
- package/src/utils/logger.js +21 -0
- package/src/utils/plugin-loader.js +90 -0
- package/src/utils/plugin-runner.js +31 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Source file from the docmd project — https://github.com/docmd-io/docmd
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
async function ensureDir(dirPath) {
|
|
7
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async function remove(dirPath) {
|
|
11
|
+
await fs.rm(dirPath, { recursive: true, force: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function copy(src, dest) {
|
|
15
|
+
await fs.cp(src, dest, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function exists(filePath) {
|
|
19
|
+
try {
|
|
20
|
+
await fs.access(filePath);
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function writeJson(file, object, options = {}) {
|
|
28
|
+
const content = JSON.stringify(object, null, options.spaces || 2);
|
|
29
|
+
await fs.writeFile(file, content, 'utf8');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
...fs,
|
|
34
|
+
ensureDir,
|
|
35
|
+
remove,
|
|
36
|
+
copy,
|
|
37
|
+
pathExists: exists,
|
|
38
|
+
exists,
|
|
39
|
+
writeJson
|
|
40
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Source file from the docmd project — https://github.com/docmd-io/docmd
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
const { version } = require('../../package.json');
|
|
6
|
+
|
|
7
|
+
const printBanner = () => {
|
|
8
|
+
const logo = `
|
|
9
|
+
|
|
10
|
+
${chalk.blue(' _ _ ')}
|
|
11
|
+
${chalk.blue(' _| |___ ___ _____ _| |')}
|
|
12
|
+
${chalk.blue(' | . | . | _| | . |')}
|
|
13
|
+
${chalk.blue(' |___|___|___|_|_|_|___|')}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
console.log(logo);
|
|
17
|
+
console.log(` ${chalk.dim(`v${version}`)}`);
|
|
18
|
+
console.log(`\n`);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
module.exports = { printBanner };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
const hooks = {
|
|
4
|
+
markdownSetup: [],
|
|
5
|
+
injectHead: [],
|
|
6
|
+
injectBody: [],
|
|
7
|
+
onPostBuild: [],
|
|
8
|
+
assets: [],
|
|
9
|
+
getClientAssets: [] // Legacy support
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// Map short names to package names
|
|
13
|
+
const ALIASES = {
|
|
14
|
+
'search': '@docmd/plugin-search',
|
|
15
|
+
'seo': '@docmd/plugin-seo',
|
|
16
|
+
'sitemap': '@docmd/plugin-sitemap',
|
|
17
|
+
'analytics': '@docmd/plugin-analytics',
|
|
18
|
+
'mermaid': '@docmd/plugin-mermaid'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function loadPlugins(config) {
|
|
22
|
+
// 1. Reset hooks
|
|
23
|
+
Object.keys(hooks).forEach(key => hooks[key] = []);
|
|
24
|
+
|
|
25
|
+
// 2. Initialize Plugin Map (Name -> Options)
|
|
26
|
+
// This ensures unique plugins (last write wins)
|
|
27
|
+
const pluginMap = new Map();
|
|
28
|
+
|
|
29
|
+
// A. Add Defaults
|
|
30
|
+
pluginMap.set('@docmd/plugin-search', config.search !== false ? {} : false);
|
|
31
|
+
pluginMap.set('@docmd/plugin-seo', config.plugins?.seo || {});
|
|
32
|
+
pluginMap.set('@docmd/plugin-sitemap', config.plugins?.sitemap || {});
|
|
33
|
+
pluginMap.set('@docmd/plugin-analytics', config.plugins?.analytics || {});
|
|
34
|
+
|
|
35
|
+
// B. Add/Override from Config
|
|
36
|
+
if (config.plugins) {
|
|
37
|
+
Object.keys(config.plugins).forEach(key => {
|
|
38
|
+
// Resolve Alias (e.g., 'mermaid' -> '@docmd/plugin-mermaid')
|
|
39
|
+
const resolvedName = ALIASES[key] || key;
|
|
40
|
+
const options = config.plugins[key];
|
|
41
|
+
|
|
42
|
+
// Update map (Override default if exists)
|
|
43
|
+
pluginMap.set(resolvedName, options);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 3. Load and Register
|
|
48
|
+
for (const [name, options] of pluginMap) {
|
|
49
|
+
if (options === false) continue; // Skip disabled
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// Try resolving standard package
|
|
53
|
+
let pluginModule;
|
|
54
|
+
try {
|
|
55
|
+
pluginModule = require(name);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
// Fallback for local development or misnamed packages
|
|
58
|
+
console.warn(chalk.dim(` > Debug: Could not require '${name}', checking alternatives...`));
|
|
59
|
+
throw e;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
registerPlugin(name, pluginModule, options);
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.warn(chalk.yellow(`⚠️ Could not load plugin: ${name}`));
|
|
65
|
+
// Only log full error in verbose/debug mode to reduce noise
|
|
66
|
+
// console.error(e.message);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return hooks;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function registerPlugin(name, plugin, options) {
|
|
74
|
+
if (typeof plugin.markdownSetup === 'function') hooks.markdownSetup.push((md) => plugin.markdownSetup(md, options));
|
|
75
|
+
|
|
76
|
+
if (typeof plugin.generateMetaTags === 'function') {
|
|
77
|
+
hooks.injectHead.push((config, page, root) => plugin.generateMetaTags(config, page, root));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (typeof plugin.generateScripts === 'function') {
|
|
81
|
+
hooks.injectHead.push((c) => plugin.generateScripts(c, options).headScriptsHtml || '');
|
|
82
|
+
hooks.injectBody.push((c) => plugin.generateScripts(c, options).bodyScriptsHtml || '');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (typeof plugin.onPostBuild === 'function') hooks.onPostBuild.push((ctx) => plugin.onPostBuild({ ...ctx, options }));
|
|
86
|
+
|
|
87
|
+
if (typeof plugin.getAssets === 'function') hooks.assets.push(() => plugin.getAssets(options));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = { loadPlugins, hooks };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const { onPostBuild: searchPostBuild, getClientAssets: getSearchAssets } = require('@docmd/plugin-search');
|
|
2
|
+
const { onPostBuild: sitemapPostBuild } = require('@docmd/plugin-sitemap');
|
|
3
|
+
// We will implement SEO/Analytics later, but they are typically "hooks" inside the HTML generation
|
|
4
|
+
// or post-build actions.
|
|
5
|
+
|
|
6
|
+
// Map of standard plugins that run post-build
|
|
7
|
+
const POST_BUILD_PLUGINS = [
|
|
8
|
+
{ name: 'search', fn: searchPostBuild },
|
|
9
|
+
{ name: 'sitemap', fn: sitemapPostBuild }
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
async function runPostBuildHooks(context) {
|
|
13
|
+
for (const plugin of POST_BUILD_PLUGINS) {
|
|
14
|
+
try {
|
|
15
|
+
if (plugin.fn) {
|
|
16
|
+
await plugin.fn(context);
|
|
17
|
+
}
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.error(`❌ Plugin '${plugin.name}' failed:`, e.message);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getPluginAssets() {
|
|
25
|
+
// Aggregate assets from plugins that have client-side scripts (like Search)
|
|
26
|
+
return [
|
|
27
|
+
{ src: getSearchAssets(), dest: 'plugins/search' }
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { runPostBuildHooks, getPluginAssets };
|