@mgks/docmd 0.3.11 → 0.4.1
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/README.md +15 -160
- package/bin/docmd.js +6 -69
- package/package.json +17 -57
- package/bin/postinstall.js +0 -14
- package/src/assets/css/docmd-highlight-dark.css +0 -86
- package/src/assets/css/docmd-highlight-light.css +0 -86
- package/src/assets/css/docmd-main.css +0 -1757
- package/src/assets/css/docmd-theme-retro.css +0 -867
- package/src/assets/css/docmd-theme-ruby.css +0 -629
- package/src/assets/css/docmd-theme-sky.css +0 -617
- package/src/assets/favicon.ico +0 -0
- package/src/assets/images/docmd-logo-dark.png +0 -0
- package/src/assets/images/docmd-logo-light.png +0 -0
- package/src/assets/js/docmd-image-lightbox.js +0 -74
- package/src/assets/js/docmd-main.js +0 -260
- package/src/assets/js/docmd-mermaid.js +0 -205
- package/src/assets/js/docmd-search.js +0 -218
- package/src/commands/build.js +0 -237
- package/src/commands/dev.js +0 -352
- package/src/commands/init.js +0 -277
- package/src/commands/live.js +0 -145
- package/src/core/asset-manager.js +0 -72
- package/src/core/config-loader.js +0 -58
- package/src/core/config-validator.js +0 -80
- package/src/core/file-processor.js +0 -103
- package/src/core/fs-utils.js +0 -40
- package/src/core/html-generator.js +0 -185
- package/src/core/icon-renderer.js +0 -106
- package/src/core/logger.js +0 -21
- package/src/core/markdown/containers.js +0 -94
- package/src/core/markdown/renderers.js +0 -90
- package/src/core/markdown/rules.js +0 -402
- package/src/core/markdown/setup.js +0 -113
- package/src/core/navigation-helper.js +0 -74
- package/src/index.js +0 -12
- package/src/live/core.js +0 -67
- package/src/live/index.html +0 -216
- package/src/live/live.css +0 -256
- package/src/live/shims.js +0 -1
- package/src/plugins/analytics.js +0 -48
- package/src/plugins/seo.js +0 -107
- package/src/plugins/sitemap.js +0 -127
- package/src/templates/layout.ejs +0 -187
- package/src/templates/navigation.ejs +0 -97
- package/src/templates/no-style.ejs +0 -166
- package/src/templates/partials/theme-init.js +0 -36
- package/src/templates/toc.ejs +0 -38
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
// Source file from the docmd project — https://github.com/docmd-io/docmd
|
|
2
|
-
|
|
3
|
-
const fs = require('./fs-utils');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const matter = require('gray-matter');
|
|
6
|
-
const { createMarkdownItInstance } = require('./markdown/setup');
|
|
7
|
-
|
|
8
|
-
function decodeHtmlEntities(html) {
|
|
9
|
-
return html.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, ' ');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function stripHtmlTags(str) {
|
|
13
|
-
if (!str) return '';
|
|
14
|
-
return str.replace(/<[^>]*>?/gm, '');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function extractHeadingsFromHtml(htmlContent) {
|
|
18
|
-
const headings = [];
|
|
19
|
-
const headingRegex = /<h([1-6])[^>]*?id="([^"]*)"[^>]*?>([\s\S]*?)<\/h\1>/g;
|
|
20
|
-
let match;
|
|
21
|
-
while ((match = headingRegex.exec(htmlContent)) !== null) {
|
|
22
|
-
const level = parseInt(match[1], 10);
|
|
23
|
-
const id = match[2];
|
|
24
|
-
const text = decodeHtmlEntities(match[3].replace(/<\/?[^>]+(>|$)/g, ''));
|
|
25
|
-
headings.push({ id, level, text });
|
|
26
|
-
}
|
|
27
|
-
return headings;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function formatPathForDisplay(absolutePath) {
|
|
31
|
-
const CWD = process.cwd();
|
|
32
|
-
const relativePath = path.relative(CWD, absolutePath);
|
|
33
|
-
if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) {
|
|
34
|
-
return `./${relativePath}`;
|
|
35
|
-
}
|
|
36
|
-
return relativePath;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function processMarkdownFile(filePath, md, config) {
|
|
40
|
-
const rawContent = await fs.readFile(filePath, 'utf8');
|
|
41
|
-
return processMarkdownContent(rawContent, md, config, filePath);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Pure logic, no file reading (Used by Live Editor)
|
|
45
|
-
function processMarkdownContent(rawContent, md, config, filePath = 'memory') {
|
|
46
|
-
let frontmatter, markdownContent;
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
({ data: frontmatter, content: markdownContent } = matter(rawContent));
|
|
50
|
-
} catch (e) {
|
|
51
|
-
console.error(`❌ Error parsing frontmatter in ${filePath === 'memory' ? 'content' : formatPathForDisplay(filePath)}:`);
|
|
52
|
-
console.error(` ${e.message}`);
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!frontmatter.title && config.autoTitleFromH1 !== false) {
|
|
57
|
-
const h1Match = markdownContent.match(/^#\s+(.*)/m);
|
|
58
|
-
if (h1Match) frontmatter.title = h1Match[1].trim();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let htmlContent, headings;
|
|
62
|
-
if (frontmatter.noStyle === true) {
|
|
63
|
-
htmlContent = markdownContent;
|
|
64
|
-
headings = [];
|
|
65
|
-
} else {
|
|
66
|
-
htmlContent = md.render(markdownContent);
|
|
67
|
-
headings = extractHeadingsFromHtml(htmlContent);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
let searchData = null;
|
|
71
|
-
if (!frontmatter.noindex) {
|
|
72
|
-
const rawText = decodeHtmlEntities(stripHtmlTags(htmlContent));
|
|
73
|
-
searchData = {
|
|
74
|
-
title: frontmatter.title || 'Untitled',
|
|
75
|
-
content: rawText.slice(0, 5000), // Safety cap to prevent massive JSON
|
|
76
|
-
headings: headings.map(h => h.text)
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return { frontmatter, htmlContent, headings, searchData };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function findMarkdownFiles(dir) {
|
|
84
|
-
let files = [];
|
|
85
|
-
const items = await fs.readdir(dir, { withFileTypes: true });
|
|
86
|
-
for (const item of items) {
|
|
87
|
-
const fullPath = path.join(dir, item.name);
|
|
88
|
-
if (item.isDirectory()) {
|
|
89
|
-
files = files.concat(await findMarkdownFiles(fullPath));
|
|
90
|
-
} else if (item.isFile() && (item.name.endsWith('.md') || item.name.endsWith('.markdown'))) {
|
|
91
|
-
files.push(fullPath);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return files;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
module.exports = {
|
|
98
|
-
processMarkdownFile,
|
|
99
|
-
processMarkdownContent,
|
|
100
|
-
createMarkdownItInstance,
|
|
101
|
-
extractHeadingsFromHtml,
|
|
102
|
-
findMarkdownFiles
|
|
103
|
-
};
|
package/src/core/fs-utils.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
// Source file from the docmd project — https://github.com/docmd-io/docmd
|
|
2
|
-
|
|
3
|
-
const ejs = require('ejs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const fs = require('../core/fs-utils');
|
|
6
|
-
const { createMarkdownItInstance } = require('./file-processor');
|
|
7
|
-
const { generateSeoMetaTags } = require('../plugins/seo');
|
|
8
|
-
const { generateAnalyticsScripts } = require('../plugins/analytics');
|
|
9
|
-
const { renderIcon } = require('./icon-renderer');
|
|
10
|
-
|
|
11
|
-
let mdInstance = null;
|
|
12
|
-
let themeInitScript = '';
|
|
13
|
-
|
|
14
|
-
(async () => {
|
|
15
|
-
try {
|
|
16
|
-
const themeInitPath = path.join(__dirname, '..', 'templates', 'partials', 'theme-init.js');
|
|
17
|
-
if (await fs.exists(themeInitPath)) {
|
|
18
|
-
const scriptContent = await fs.readFile(themeInitPath, 'utf8');
|
|
19
|
-
themeInitScript = `<script>\n${scriptContent}\n</script>`;
|
|
20
|
-
}
|
|
21
|
-
} catch (e) { /* ignore */ }
|
|
22
|
-
})();
|
|
23
|
-
|
|
24
|
-
// Basic whitespace cleanup (keep this simple version)
|
|
25
|
-
function cleanupHtml(html) {
|
|
26
|
-
if (!html) return '';
|
|
27
|
-
// return html.replace(/^\s*[\r\n]/gm, '').trim(); // Remove leading/trailing blank lines from each line
|
|
28
|
-
return html.trim(); // Only trim the start/end of the whole document
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function fixHtmlLinks(htmlContent, relativePathToRoot, isOfflineMode, configBase = '/') {
|
|
32
|
-
if (!htmlContent) return '';
|
|
33
|
-
const root = relativePathToRoot || './';
|
|
34
|
-
const baseUrl = configBase.endsWith('/') ? configBase : configBase + '/';
|
|
35
|
-
|
|
36
|
-
return htmlContent.replace(/(href|src)=["']([^"']+)["']/g, (match, attr, url) => {
|
|
37
|
-
if (url.startsWith('#') || url.startsWith('http') || url.startsWith('mailto:') || url === '') {
|
|
38
|
-
return match;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
let finalPath = url;
|
|
42
|
-
|
|
43
|
-
// 1. Handle Base URL removal
|
|
44
|
-
if (baseUrl !== '/' && url.startsWith(baseUrl)) {
|
|
45
|
-
finalPath = '/' + url.substring(baseUrl.length);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// 2. Handle Absolute Paths
|
|
49
|
-
if (finalPath.startsWith('/')) {
|
|
50
|
-
// Simple logic: if root relative, prepend relative path
|
|
51
|
-
finalPath = root + finalPath.substring(1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// 3. Offline Mode Logic
|
|
55
|
-
if (isOfflineMode) {
|
|
56
|
-
const [pathOnly] = finalPath.split(/[?#]/);
|
|
57
|
-
const ext = path.extname(pathOnly);
|
|
58
|
-
const isAsset = ['.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico'].includes(ext.toLowerCase());
|
|
59
|
-
|
|
60
|
-
if (!isAsset && !ext) {
|
|
61
|
-
if (finalPath.endsWith('/')) {
|
|
62
|
-
finalPath += 'index.html';
|
|
63
|
-
} else if (!finalPath.includes('#')) {
|
|
64
|
-
finalPath += '/index.html';
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
if (finalPath.endsWith('/index.html')) {
|
|
69
|
-
finalPath = finalPath.substring(0, finalPath.length - 10);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return `${attr}="${finalPath}"`;
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function processPluginHooks(config, pageData, relativePathToRoot) {
|
|
78
|
-
let metaTagsHtml = '';
|
|
79
|
-
let faviconLinkHtml = '';
|
|
80
|
-
let themeCssLinkHtml = '';
|
|
81
|
-
let pluginStylesHtml = '';
|
|
82
|
-
let pluginHeadScriptsHtml = '';
|
|
83
|
-
let pluginBodyScriptsHtml = '';
|
|
84
|
-
|
|
85
|
-
const safeRoot = relativePathToRoot || './';
|
|
86
|
-
|
|
87
|
-
if (config.favicon) {
|
|
88
|
-
const cleanFaviconPath = config.favicon.startsWith('/') ? config.favicon.substring(1) : config.favicon;
|
|
89
|
-
const finalFaviconHref = `${safeRoot}${cleanFaviconPath}`;
|
|
90
|
-
faviconLinkHtml = `<link rel="icon" href="${finalFaviconHref}" type="image/x-icon" sizes="any">\n<link rel="shortcut icon" href="${finalFaviconHref}" type="image/x-icon">`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (config.theme && config.theme.name && config.theme.name !== 'default') {
|
|
94
|
-
const themeCssPath = `assets/css/docmd-theme-${config.theme.name}.css`;
|
|
95
|
-
themeCssLinkHtml = `<link rel="stylesheet" href="${safeRoot}${themeCssPath}">`;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (config.plugins?.seo) {
|
|
99
|
-
metaTagsHtml += generateSeoMetaTags(config, pageData, safeRoot);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (config.plugins?.analytics) {
|
|
103
|
-
const analyticsScripts = generateAnalyticsScripts(config, pageData);
|
|
104
|
-
pluginHeadScriptsHtml += analyticsScripts.headScriptsHtml;
|
|
105
|
-
pluginBodyScriptsHtml += analyticsScripts.bodyScriptsHtml;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return { metaTagsHtml, faviconLinkHtml, themeCssLinkHtml, pluginStylesHtml, pluginHeadScriptsHtml, pluginBodyScriptsHtml };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function generateHtmlPage(templateData, isOfflineMode = false) {
|
|
112
|
-
let { content, frontmatter, outputPath, headings, config } = templateData;
|
|
113
|
-
const { currentPagePath, prevPage, nextPage, relativePathToRoot, navigationHtml, siteTitle } = templateData;
|
|
114
|
-
const pageTitle = frontmatter.title;
|
|
115
|
-
|
|
116
|
-
if (!relativePathToRoot) templateData.relativePathToRoot = './';
|
|
117
|
-
|
|
118
|
-
content = fixHtmlLinks(content, templateData.relativePathToRoot, isOfflineMode, config.base);
|
|
119
|
-
const pluginOutputs = await processPluginHooks(config, { frontmatter, outputPath }, templateData.relativePathToRoot);
|
|
120
|
-
|
|
121
|
-
let footerHtml = '';
|
|
122
|
-
if (config.footer) {
|
|
123
|
-
if (!mdInstance) mdInstance = createMarkdownItInstance(config);
|
|
124
|
-
footerHtml = mdInstance.renderInline(config.footer);
|
|
125
|
-
footerHtml = fixHtmlLinks(footerHtml, templateData.relativePathToRoot, isOfflineMode, config.base);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
let templateName = frontmatter.noStyle === true ? 'no-style.ejs' : 'layout.ejs';
|
|
129
|
-
const layoutTemplatePath = path.join(__dirname, '..', 'templates', templateName);
|
|
130
|
-
if (!await fs.exists(layoutTemplatePath)) throw new Error(`Template not found: ${layoutTemplatePath}`);
|
|
131
|
-
const layoutTemplate = await fs.readFile(layoutTemplatePath, 'utf8');
|
|
132
|
-
|
|
133
|
-
const isActivePage = currentPagePath && content && content.trim().length > 0;
|
|
134
|
-
|
|
135
|
-
let editUrl = null;
|
|
136
|
-
let editLinkText = 'Edit this page';
|
|
137
|
-
if (config.editLink && config.editLink.enabled && config.editLink.baseUrl) {
|
|
138
|
-
editUrl = `${config.editLink.baseUrl.replace(/\/$/, '')}/${outputPath.replace(/\/index\.html$/, '.md')}`;
|
|
139
|
-
if (outputPath.endsWith('index.html') && outputPath !== 'index.html') editUrl = editUrl.replace('.md', '/index.md');
|
|
140
|
-
if (outputPath === 'index.html') editUrl = `${config.editLink.baseUrl.replace(/\/$/, '')}/index.md`;
|
|
141
|
-
editLinkText = config.editLink.text || editLinkText;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const ejsData = {
|
|
145
|
-
...templateData,
|
|
146
|
-
description: frontmatter.description || '',
|
|
147
|
-
footerHtml, editUrl, editLinkText, isActivePage,
|
|
148
|
-
defaultMode: config.theme?.defaultMode || 'light',
|
|
149
|
-
logo: config.logo, sidebarConfig: config.sidebar || {}, theme: config.theme,
|
|
150
|
-
customCssFiles: config.theme?.customCss || [], customJsFiles: config.customJs || [],
|
|
151
|
-
sponsor: config.sponsor, footer: config.footer, renderIcon, themeInitScript,
|
|
152
|
-
headings: frontmatter.toc !== false ? (headings || []) : [],
|
|
153
|
-
...pluginOutputs,
|
|
154
|
-
isOfflineMode
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const rawHtml = renderHtmlPage(layoutTemplate, ejsData, layoutTemplatePath);
|
|
158
|
-
const pkgVersion = require('../../package.json').version;
|
|
159
|
-
const brandingComment = `<!-- Generated by docmd (v${pkgVersion}) - https://docmd.io -->\n`;
|
|
160
|
-
|
|
161
|
-
// REMOVED: formatHtml(rawHtml)
|
|
162
|
-
return brandingComment + cleanupHtml(rawHtml);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function renderHtmlPage(templateContent, ejsData, filename = 'template.ejs', options = {}) {
|
|
166
|
-
try {
|
|
167
|
-
return ejs.render(templateContent, ejsData, { filename: filename, ...options });
|
|
168
|
-
} catch (e) {
|
|
169
|
-
console.error(`❌ Error rendering EJS template: ${e.message}`);
|
|
170
|
-
throw e;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function generateNavigationHtml(navItems, currentPagePath, relativePathToRoot, config, isOfflineMode = false) {
|
|
175
|
-
const navTemplatePath = path.join(__dirname, '..', 'templates', 'navigation.ejs');
|
|
176
|
-
if (!await fs.exists(navTemplatePath)) throw new Error(`Navigation template not found: ${navTemplatePath}`);
|
|
177
|
-
const navTemplate = await fs.readFile(navTemplatePath, 'utf8');
|
|
178
|
-
const safeRoot = relativePathToRoot || './';
|
|
179
|
-
|
|
180
|
-
return ejs.render(navTemplate, {
|
|
181
|
-
navItems, currentPagePath, relativePathToRoot: safeRoot, config, isOfflineMode, renderIcon
|
|
182
|
-
}, { filename: navTemplatePath });
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
module.exports = { generateHtmlPage, generateNavigationHtml, renderHtmlPage };
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
// Source file from the docmd project — https://github.com/docmd-io/docmd
|
|
2
|
-
|
|
3
|
-
const lucideStatic = require('lucide-static');
|
|
4
|
-
|
|
5
|
-
// On first load, log debug information about a specific icon to understand its structure
|
|
6
|
-
let debugRun = false;
|
|
7
|
-
if (debugRun) {
|
|
8
|
-
console.log(`[docmd] Lucide static icons loaded - type: ${typeof lucideStatic}`);
|
|
9
|
-
if (typeof lucideStatic === 'object') {
|
|
10
|
-
console.log(`[docmd] Available icon keys (first 10): ${Object.keys(lucideStatic).slice(0, 10).join(', ')}...`);
|
|
11
|
-
console.log(`[docmd] Total icons available: ${Object.keys(lucideStatic).length}`);
|
|
12
|
-
|
|
13
|
-
// Inspect a sample icon to understand its structure
|
|
14
|
-
const sampleIcon = lucideStatic['Home'];
|
|
15
|
-
if (sampleIcon) {
|
|
16
|
-
console.log(`[docmd] Sample icon (Home) structure:`,
|
|
17
|
-
JSON.stringify(sampleIcon).substring(0, 150) + '...');
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
debugRun = false;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Convert kebab-case to PascalCase for icon names (lucide-static uses PascalCase)
|
|
24
|
-
function kebabToPascal(str) {
|
|
25
|
-
return str
|
|
26
|
-
.split('-')
|
|
27
|
-
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
28
|
-
.join('');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Map of special case icon mappings that can't be handled by the kebabToPascal conversion
|
|
32
|
-
// Only keep truly necessary mappings that can't be derived from kebab-case
|
|
33
|
-
const iconSpecialCases = {
|
|
34
|
-
'arrow-up-right-square': 'ExternalLink', // Different name entirely
|
|
35
|
-
'cloud-upload': 'UploadCloud', // Different word order
|
|
36
|
-
'file-cog': 'Settings', // Completely different icon
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const warnedMissingIcons = new Set();
|
|
40
|
-
|
|
41
|
-
function renderIcon(iconName, options = {}) {
|
|
42
|
-
// Try different ways to get the icon data
|
|
43
|
-
let iconData;
|
|
44
|
-
|
|
45
|
-
// 1. Check special cases mapping for exceptions
|
|
46
|
-
if (iconSpecialCases[iconName]) {
|
|
47
|
-
iconData = lucideStatic[iconSpecialCases[iconName]];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 2. If not found, try standard PascalCase conversion
|
|
51
|
-
if (!iconData) {
|
|
52
|
-
const pascalCaseName = kebabToPascal(iconName);
|
|
53
|
-
iconData = lucideStatic[pascalCaseName];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!iconData) {
|
|
57
|
-
if (!warnedMissingIcons.has(iconName)) { // Check if already warned
|
|
58
|
-
console.warn(`[docmd] Lucide icon not found: ${iconName}. Falling back to empty string.`);
|
|
59
|
-
warnedMissingIcons.add(iconName); // Add to set so it doesn't warn again
|
|
60
|
-
}
|
|
61
|
-
return '';
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
// The iconData is a string containing a complete SVG
|
|
66
|
-
// We need to extract the contents and apply our own attributes
|
|
67
|
-
const svgString = iconData.trim();
|
|
68
|
-
|
|
69
|
-
// Extract the SVG content between the opening and closing tags
|
|
70
|
-
const contentMatch = svgString.match(/<svg[^>]*>([\s\S]*)<\/svg>/);
|
|
71
|
-
if (!contentMatch) {
|
|
72
|
-
return ''; // Not a valid SVG
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const svgContent = contentMatch[1];
|
|
76
|
-
|
|
77
|
-
// Create our custom attributes for the SVG
|
|
78
|
-
const attributes = {
|
|
79
|
-
class: `lucide-icon icon-${iconName} ${options.class || ''}`.trim(),
|
|
80
|
-
width: options.width || '1em',
|
|
81
|
-
height: options.height || '1em',
|
|
82
|
-
viewBox: '0 0 24 24',
|
|
83
|
-
fill: 'none',
|
|
84
|
-
stroke: options.stroke || 'currentColor',
|
|
85
|
-
'stroke-width': options.strokeWidth || '2',
|
|
86
|
-
'stroke-linecap': 'round',
|
|
87
|
-
'stroke-linejoin': 'round',
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const attributesString = Object.entries(attributes)
|
|
91
|
-
.map(([key, value]) => `${key}="${value}"`)
|
|
92
|
-
.join(' ');
|
|
93
|
-
|
|
94
|
-
// Return the new SVG with our attributes and the original content
|
|
95
|
-
return `<svg ${attributesString}>${svgContent}</svg>`;
|
|
96
|
-
} catch (err) {
|
|
97
|
-
console.error(`[docmd] Error rendering icon ${iconName}:`, err);
|
|
98
|
-
return '';
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function clearWarnedIcons() {
|
|
103
|
-
warnedMissingIcons.clear();
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
module.exports = { renderIcon, clearWarnedIcons };
|
package/src/core/logger.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
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 };
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
// Source file from the docmd project — https://github.com/docmd-io/docmd
|
|
2
|
-
|
|
3
|
-
const containers = {
|
|
4
|
-
card: {
|
|
5
|
-
name: 'card',
|
|
6
|
-
render: (tokens, idx) => {
|
|
7
|
-
if (tokens[idx].nesting === 1) {
|
|
8
|
-
const title = tokens[idx].info ? tokens[idx].info.trim() : '';
|
|
9
|
-
return `<div class="docmd-container card">${title ? `<div class="card-title">${title}</div>` : ''}<div class="card-content">`;
|
|
10
|
-
}
|
|
11
|
-
return '</div></div>';
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
callout: {
|
|
15
|
-
name: 'callout',
|
|
16
|
-
render: (tokens, idx) => {
|
|
17
|
-
if (tokens[idx].nesting === 1) {
|
|
18
|
-
const [type, ...titleParts] = tokens[idx].info.split(' ');
|
|
19
|
-
const title = titleParts.join(' ');
|
|
20
|
-
return `<div class="docmd-container callout callout-${type}">${title ? `<div class="callout-title">${title}</div>` : ''}<div class="callout-content">`;
|
|
21
|
-
}
|
|
22
|
-
return '</div></div>';
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
button: {
|
|
26
|
-
name: 'button',
|
|
27
|
-
selfClosing: true,
|
|
28
|
-
render: (tokens, idx) => {
|
|
29
|
-
if (tokens[idx].nesting === 1) {
|
|
30
|
-
const parts = tokens[idx].info.split(' ');
|
|
31
|
-
const text = parts[0];
|
|
32
|
-
const url = parts[1];
|
|
33
|
-
const color = parts[2];
|
|
34
|
-
const colorStyle = color && color.startsWith('color:') ? ` style="background-color: ${color.split(':')[1]}"` : '';
|
|
35
|
-
|
|
36
|
-
let finalUrl = url;
|
|
37
|
-
let targetAttr = '';
|
|
38
|
-
if (url && url.startsWith('external:')) {
|
|
39
|
-
finalUrl = url.substring(9);
|
|
40
|
-
targetAttr = ' target="_blank" rel="noopener noreferrer"';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return `<a href="${finalUrl}" class="docmd-button"${colorStyle}${targetAttr}>${text.replace(/_/g, ' ')}</a>`;
|
|
44
|
-
}
|
|
45
|
-
return '';
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
steps: {
|
|
49
|
-
name: 'steps',
|
|
50
|
-
render: (tokens, idx) => {
|
|
51
|
-
if (tokens[idx].nesting === 1) {
|
|
52
|
-
return '<div class="docmd-container steps steps-reset steps-numbering">';
|
|
53
|
-
}
|
|
54
|
-
return '</div>';
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
collapsible: {
|
|
58
|
-
name: 'collapsible',
|
|
59
|
-
render: (tokens, idx) => {
|
|
60
|
-
if (tokens[idx].nesting === 1) {
|
|
61
|
-
const info = tokens[idx].info.trim();
|
|
62
|
-
let isOpen = false;
|
|
63
|
-
let displayTitle = info;
|
|
64
|
-
|
|
65
|
-
if (info.startsWith('open ')) {
|
|
66
|
-
isOpen = true;
|
|
67
|
-
displayTitle = info.substring(5);
|
|
68
|
-
}
|
|
69
|
-
if (!displayTitle) displayTitle = 'Click to expand';
|
|
70
|
-
|
|
71
|
-
return `<details class="docmd-container collapsible" ${isOpen ? 'open' : ''}>
|
|
72
|
-
<summary class="collapsible-summary">
|
|
73
|
-
<span class="collapsible-title">${displayTitle}</span>
|
|
74
|
-
<span class="collapsible-arrow">
|
|
75
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>
|
|
76
|
-
</span>
|
|
77
|
-
</summary>
|
|
78
|
-
<div class="collapsible-content">`;
|
|
79
|
-
}
|
|
80
|
-
return '</div></details>\n';
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
changelog: {
|
|
84
|
-
name: 'changelog',
|
|
85
|
-
render: (tokens, idx) => {
|
|
86
|
-
if (tokens[idx].nesting === 1) {
|
|
87
|
-
return '<div class="docmd-container changelog-timeline">';
|
|
88
|
-
}
|
|
89
|
-
return '</div>';
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
module.exports = { containers };
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
// Source file from the docmd project — https://github.com/docmd-io/docmd
|
|
2
|
-
|
|
3
|
-
const customOrderedListOpenRenderer = function(tokens, idx, options, env, self) {
|
|
4
|
-
const token = tokens[idx];
|
|
5
|
-
let isInSteps = false;
|
|
6
|
-
for (let i = idx - 1; i >= 0; i--) {
|
|
7
|
-
if (tokens[i].type === 'container_steps_open') {
|
|
8
|
-
isInSteps = true;
|
|
9
|
-
break;
|
|
10
|
-
}
|
|
11
|
-
if (tokens[i].type === 'container_steps_close') {
|
|
12
|
-
break;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
if (isInSteps) {
|
|
16
|
-
const start = token.attrGet('start');
|
|
17
|
-
return start ? `<ol class="steps-list" start="${start}">` : '<ol class="steps-list">';
|
|
18
|
-
}
|
|
19
|
-
const start = token.attrGet('start');
|
|
20
|
-
return start ? `<ol start="${start}">` : '<ol>';
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const customListItemOpenRenderer = function(tokens, idx, options, env, self) {
|
|
24
|
-
const token = tokens[idx];
|
|
25
|
-
let isInStepsList = false;
|
|
26
|
-
for (let i = idx - 1; i >= 0; i--) {
|
|
27
|
-
if (tokens[i].type === 'ordered_list_open' && tokens[i].markup && tokens[i].level < token.level) {
|
|
28
|
-
let j = i - 1;
|
|
29
|
-
while (j >= 0) {
|
|
30
|
-
if (tokens[j].type === 'container_steps_open') {
|
|
31
|
-
isInStepsList = true;
|
|
32
|
-
break;
|
|
33
|
-
}
|
|
34
|
-
if (tokens[j].type === 'container_steps_close') {
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
j--;
|
|
38
|
-
}
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (isInStepsList) {
|
|
43
|
-
return '<li class="step-item">';
|
|
44
|
-
}
|
|
45
|
-
return '<li>';
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const customImageRenderer = function(tokens, idx, options, env, self) {
|
|
49
|
-
const defaultImageRenderer = function(tokens, idx, options, env, self) { return self.renderToken(tokens, idx, options); };
|
|
50
|
-
const renderedImage = defaultImageRenderer(tokens, idx, options, env, self);
|
|
51
|
-
const nextToken = tokens[idx + 1];
|
|
52
|
-
if (nextToken && nextToken.type === 'attrs_block') {
|
|
53
|
-
const attrs = nextToken.attrs || [];
|
|
54
|
-
const attrsStr = attrs.map(([name, value]) => `${name}="${value}"`).join(' ');
|
|
55
|
-
return renderedImage.replace('<img ', `<img ${attrsStr} `);
|
|
56
|
-
}
|
|
57
|
-
return renderedImage;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
// Table Wrapper for horizontal scrolling
|
|
61
|
-
const tableOpenRenderer = (tokens, idx, options, env, self) => '<div class="table-wrapper">' + self.renderToken(tokens, idx, options);
|
|
62
|
-
const tableCloseRenderer = (tokens, idx, options, env, self) => self.renderToken(tokens, idx, options) + '</div>';
|
|
63
|
-
|
|
64
|
-
// Tabs Renderers
|
|
65
|
-
const tabsOpenRenderer = (tokens, idx) => `<div class="${tokens[idx].attrs.map(attr => attr[1]).join(' ')}">`;
|
|
66
|
-
const tabsNavOpenRenderer = () => '<div class="docmd-tabs-nav">';
|
|
67
|
-
const tabsNavCloseRenderer = () => '</div>';
|
|
68
|
-
const tabsNavItemRenderer = (tokens, idx) => `<div class="${tokens[idx].attrs[0][1]}">${tokens[idx].content}</div>`;
|
|
69
|
-
const tabsContentOpenRenderer = () => '<div class="docmd-tabs-content">';
|
|
70
|
-
const tabsContentCloseRenderer = () => '</div>';
|
|
71
|
-
const tabPaneOpenRenderer = (tokens, idx) => `<div class="${tokens[idx].attrs[0][1]}">`;
|
|
72
|
-
const tabPaneCloseRenderer = () => '</div>';
|
|
73
|
-
const tabsCloseRenderer = () => '</div>';
|
|
74
|
-
|
|
75
|
-
module.exports = {
|
|
76
|
-
customOrderedListOpenRenderer,
|
|
77
|
-
customListItemOpenRenderer,
|
|
78
|
-
customImageRenderer,
|
|
79
|
-
tableOpenRenderer,
|
|
80
|
-
tableCloseRenderer,
|
|
81
|
-
tabsOpenRenderer,
|
|
82
|
-
tabsNavOpenRenderer,
|
|
83
|
-
tabsNavCloseRenderer,
|
|
84
|
-
tabsNavItemRenderer,
|
|
85
|
-
tabsContentOpenRenderer,
|
|
86
|
-
tabsContentCloseRenderer,
|
|
87
|
-
tabPaneOpenRenderer,
|
|
88
|
-
tabPaneCloseRenderer,
|
|
89
|
-
tabsCloseRenderer
|
|
90
|
-
};
|