markshell 1.7.0 → 1.8.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/bin/markshell.js +45 -0
- package/docs/usage.md +22 -4
- package/lib/index.js +50 -9
- package/lib/syntaxhighlighter/index.d.ts +31 -1
- package/lib/syntaxhighlighter/index.js +180 -33
- package/package.json +1 -1
package/bin/markshell.js
CHANGED
|
@@ -36,6 +36,51 @@ if (args.length === 0 || args.indexOf('--help') !== -1) {
|
|
|
36
36
|
printHelp();
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// Handle --example flag to run interactive demos
|
|
40
|
+
const exampleIdx = args.indexOf('--example');
|
|
41
|
+
if (exampleIdx !== -1) {
|
|
42
|
+
const path = require('path');
|
|
43
|
+
const exampleName = args[exampleIdx + 1];
|
|
44
|
+
|
|
45
|
+
const examples = {
|
|
46
|
+
'dynamic-loading': path.join(__dirname, '../examples/dynamic-loading-demo.js'),
|
|
47
|
+
'syntax-colors': path.join(__dirname, '../examples/syntax-colors-demo.js'),
|
|
48
|
+
'demo': path.join(__dirname, '../DEMO.md')
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (!exampleName || !examples[exampleName]) {
|
|
52
|
+
console.log(`Markshell - Version ${pkg.version}
|
|
53
|
+
|
|
54
|
+
${chalk.yellow.bold('Available examples:')}
|
|
55
|
+
|
|
56
|
+
${chalk.cyan('markshell --example dynamic-loading')}
|
|
57
|
+
Interactive demo showing dynamic language loading with statistics
|
|
58
|
+
|
|
59
|
+
${chalk.cyan('markshell --example syntax-colors')}
|
|
60
|
+
Visual demonstration of syntax highlighting colors
|
|
61
|
+
|
|
62
|
+
${chalk.cyan('markshell --example demo')}
|
|
63
|
+
Comprehensive demo showcasing all markdown features and 25+ languages
|
|
64
|
+
`);
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const examplePath = examples[exampleName];
|
|
69
|
+
|
|
70
|
+
if (exampleName === 'demo') {
|
|
71
|
+
// Render the DEMO.md file
|
|
72
|
+
try {
|
|
73
|
+
markshell.toConsole(examplePath);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
printError(e);
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
// Execute the example script
|
|
79
|
+
require(examplePath);
|
|
80
|
+
}
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
|
|
39
84
|
const theme = args.indexOf('--theme');
|
|
40
85
|
|
|
41
86
|
if (theme !== 1) {
|
package/docs/usage.md
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
# Usage
|
|
2
2
|
|
|
3
3
|
```bash
|
|
4
|
-
markshell [
|
|
4
|
+
markshell [filepath | -f [filepath] | --filepath [filepath]]
|
|
5
|
+
markshell --example [example-name]
|
|
6
|
+
markshell --help
|
|
5
7
|
```
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
## Options
|
|
10
|
+
|
|
11
|
+
**filepath or -f**
|
|
8
12
|
: path to markdown file
|
|
13
|
+
|
|
14
|
+
**--example [name]**
|
|
15
|
+
: run interactive examples and demos
|
|
16
|
+
- `dynamic-loading` - Shows dynamic language loading with statistics
|
|
17
|
+
- `syntax-colors` - Visual demonstration of syntax highlighting colors
|
|
18
|
+
- `demo` - Comprehensive showcase of all markdown features
|
|
19
|
+
|
|
9
20
|
**--help**
|
|
10
|
-
:
|
|
21
|
+
: display usage information
|
|
11
22
|
|
|
12
23
|
## Examples
|
|
13
24
|
|
|
14
25
|
```sh
|
|
26
|
+
# Render a markdown file
|
|
27
|
+
markshell ./my/markdownfile.md
|
|
15
28
|
markshell --filepath './my/markdownfile.md'
|
|
16
|
-
|
|
29
|
+
|
|
30
|
+
# Run interactive examples
|
|
31
|
+
markshell --example dynamic-loading
|
|
32
|
+
markshell --example syntax-colors
|
|
33
|
+
markshell --example demo
|
|
34
|
+
```
|
package/lib/index.js
CHANGED
|
@@ -230,6 +230,9 @@ const _codeBlock = (content) => {
|
|
|
230
230
|
* Formats source code blocks using PrismJS
|
|
231
231
|
* @param {string} content of MarkDown file
|
|
232
232
|
*/
|
|
233
|
+
// Module-level storage for code block placeholders
|
|
234
|
+
let _codeBlockPlaceholders = [];
|
|
235
|
+
|
|
233
236
|
const _highlightedCodeBlock = (content) => {
|
|
234
237
|
|
|
235
238
|
let codeRegex = new RegExp(/(\`\`\`)(.*?)(\`\`\`)/igs);
|
|
@@ -239,6 +242,10 @@ const _highlightedCodeBlock = (content) => {
|
|
|
239
242
|
return content;
|
|
240
243
|
}
|
|
241
244
|
|
|
245
|
+
// Reset and store highlighted code blocks with placeholders to protect from inline code processing
|
|
246
|
+
_codeBlockPlaceholders = [];
|
|
247
|
+
let placeholderIndex = 0;
|
|
248
|
+
|
|
242
249
|
newContent.forEach((element) => {
|
|
243
250
|
|
|
244
251
|
let langRegex = new RegExp(/(\`\`\`)(.*?)(\r?\n)/igs);
|
|
@@ -259,7 +266,12 @@ const _highlightedCodeBlock = (content) => {
|
|
|
259
266
|
let hlSource = syntaxHighlighter.highlight(source, lang, this._theme.sourceCodeTheme);
|
|
260
267
|
this._theme.sourceCodeTheme;
|
|
261
268
|
|
|
262
|
-
|
|
269
|
+
// Use a placeholder that won't be matched by inline code regex
|
|
270
|
+
const placeholder = `__CODEBLOCK_${placeholderIndex}__`;
|
|
271
|
+
_codeBlockPlaceholders.push({ placeholder, code: hlSource });
|
|
272
|
+
placeholderIndex++;
|
|
273
|
+
|
|
274
|
+
content = content.replace(element, placeholder)
|
|
263
275
|
|
|
264
276
|
} catch (e) {
|
|
265
277
|
|
|
@@ -275,6 +287,14 @@ const _highlightedCodeBlock = (content) => {
|
|
|
275
287
|
|
|
276
288
|
}
|
|
277
289
|
|
|
290
|
+
const _restoreCodeBlocks = (content) => {
|
|
291
|
+
// Restore code blocks from placeholders
|
|
292
|
+
_codeBlockPlaceholders.forEach(({ placeholder, code }) => {
|
|
293
|
+
content = content.replace(placeholder, code);
|
|
294
|
+
});
|
|
295
|
+
return content;
|
|
296
|
+
}
|
|
297
|
+
|
|
278
298
|
/**
|
|
279
299
|
* Formats Blockquote
|
|
280
300
|
* @param {string} content of markdown file
|
|
@@ -457,13 +477,23 @@ const _addBold = (content) => {
|
|
|
457
477
|
|
|
458
478
|
/**
|
|
459
479
|
* Formats italic elements in MarkDown
|
|
480
|
+
* Supports both underscore (_text_) and asterisk (*text*) emphasis markers
|
|
460
481
|
* @param {string} content of markdown file
|
|
461
482
|
*/
|
|
462
483
|
const _addItalic = (content) => {
|
|
463
484
|
|
|
464
|
-
|
|
485
|
+
// First pass: Handle underscore emphasis
|
|
486
|
+
// Pattern: _text_ where underscores are not adjacent to word characters
|
|
487
|
+
let underscoreRegex = new RegExp(/(?<!\w)_([^_\n]+?)_(?!\w)/g);
|
|
488
|
+
content = _highlightText(content, underscoreRegex, this._theme.italic);
|
|
465
489
|
|
|
466
|
-
|
|
490
|
+
// Second pass: Handle asterisk emphasis (but not bold **)
|
|
491
|
+
// Pattern: *text* where asterisks are not adjacent to other asterisks (to avoid matching **bold**)
|
|
492
|
+
// Also not adjacent to word characters on the outside to avoid matching mid-word asterisks
|
|
493
|
+
let asteriskRegex = new RegExp(/(?<!\*)(?<!\w)\*([^*\n]+?)\*(?!\*)(?!\w)/g);
|
|
494
|
+
content = _highlightText(content, asteriskRegex, this._theme.italic);
|
|
495
|
+
|
|
496
|
+
return content;
|
|
467
497
|
|
|
468
498
|
}
|
|
469
499
|
|
|
@@ -493,8 +523,10 @@ const _addCode = (content) => {
|
|
|
493
523
|
*/
|
|
494
524
|
const _addInlineCode = (content) => {
|
|
495
525
|
|
|
496
|
-
//
|
|
497
|
-
|
|
526
|
+
// Use negative lookbehind/lookahead to avoid matching backticks that are part of triple-backtick code blocks
|
|
527
|
+
// (?<!`) = not preceded by backtick, (?!`) = not followed by backtick
|
|
528
|
+
// This ensures we only match single backticks for inline code, not triple backticks for code blocks
|
|
529
|
+
return _highlightText(content, /(?<!`)`([^`]+)`(?!`)/g, this._theme.inlineCode);
|
|
498
530
|
|
|
499
531
|
}
|
|
500
532
|
|
|
@@ -682,17 +714,26 @@ const _toRawContent = (filepath) => {
|
|
|
682
714
|
|
|
683
715
|
|
|
684
716
|
try {
|
|
685
|
-
//
|
|
717
|
+
// Process code blocks FIRST - replaces them with placeholders
|
|
718
|
+
// This prevents inline code regex from matching backticks inside code blocks
|
|
719
|
+
content = _highlightedCodeBlock(content);
|
|
720
|
+
} catch (e) {
|
|
721
|
+
throw `Error in Highlighted Code: ${e}`;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
try {
|
|
725
|
+
// Process inline code AFTER code blocks are replaced with placeholders
|
|
726
|
+
// At this point, code blocks are hidden as __CODEBLOCK_N__ so inline code won't match them
|
|
686
727
|
content = _addInlineCode(content);
|
|
687
728
|
} catch (e) {
|
|
688
729
|
throw `Error in addInlineCode: ${e}`;
|
|
689
730
|
}
|
|
690
731
|
|
|
691
732
|
try {
|
|
692
|
-
//
|
|
693
|
-
content =
|
|
733
|
+
// Restore code blocks from placeholders
|
|
734
|
+
content = _restoreCodeBlocks(content);
|
|
694
735
|
} catch (e) {
|
|
695
|
-
throw `Error in
|
|
736
|
+
throw `Error in restoring code blocks: ${e}`;
|
|
696
737
|
}
|
|
697
738
|
|
|
698
739
|
try {
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
export var theme: any;
|
|
2
2
|
export var themeTokenKeys: string[];
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Information about loaded languages
|
|
6
|
+
*/
|
|
7
|
+
export interface LoadedLanguagesInfo {
|
|
8
|
+
count: number;
|
|
9
|
+
languages: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
3
12
|
/**
|
|
4
13
|
*
|
|
5
14
|
* @param {string} source
|
|
@@ -7,6 +16,20 @@ export var themeTokenKeys: string[];
|
|
|
7
16
|
* @param {sourceTheme} outTheme
|
|
8
17
|
*/
|
|
9
18
|
declare function _highlight(source: string, language: string, outTheme: sourceTheme): string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolves language aliases to canonical names
|
|
22
|
+
* @param language - Language identifier (may be an alias)
|
|
23
|
+
* @returns Canonical language name or null
|
|
24
|
+
*/
|
|
25
|
+
declare function _resolveLanguageAlias(language: string): string | null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get information about currently loaded languages
|
|
29
|
+
* @returns Statistics about loaded languages
|
|
30
|
+
*/
|
|
31
|
+
declare function _getLoadedLanguagesInfo(): LoadedLanguagesInfo;
|
|
32
|
+
|
|
10
33
|
type sourceTheme = string;
|
|
11
34
|
declare namespace sourceTheme {
|
|
12
35
|
const COY: string;
|
|
@@ -18,4 +41,11 @@ declare namespace sourceTheme {
|
|
|
18
41
|
const TOMORROW: string;
|
|
19
42
|
const TWILIGHT: string;
|
|
20
43
|
}
|
|
21
|
-
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
_highlight as highlight,
|
|
47
|
+
sourceTheme as themes,
|
|
48
|
+
sourceTheme as availableThemes,
|
|
49
|
+
_getLoadedLanguagesInfo as getLoadedLanguagesInfo,
|
|
50
|
+
_resolveLanguageAlias as resolveLanguageAlias
|
|
51
|
+
};
|
|
@@ -17,7 +17,75 @@ const prismjs = require('prismjs/prism');
|
|
|
17
17
|
// load all supported languages
|
|
18
18
|
// @ts-ignore
|
|
19
19
|
const loadLanguages = require('prismjs/components/');
|
|
20
|
-
|
|
20
|
+
// Languages will be loaded on-demand for better performance
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Cache of loaded languages to avoid redundant loading
|
|
24
|
+
* @type {Set<string>}
|
|
25
|
+
*/
|
|
26
|
+
const loadedLanguagesCache = new Set();
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Comprehensive language alias mapping based on PrismJS components
|
|
30
|
+
* Maps common aliases to their canonical language names
|
|
31
|
+
* @type {Object<string, string>}
|
|
32
|
+
*/
|
|
33
|
+
const languageAliases = {
|
|
34
|
+
// JavaScript ecosystem
|
|
35
|
+
'js': 'javascript',
|
|
36
|
+
'mjs': 'javascript',
|
|
37
|
+
'cjs': 'javascript',
|
|
38
|
+
'jsx': 'javascript',
|
|
39
|
+
'ts': 'typescript',
|
|
40
|
+
'tsx': 'typescript',
|
|
41
|
+
|
|
42
|
+
// Shell/Bash variants
|
|
43
|
+
'sh': 'bash',
|
|
44
|
+
'shell': 'bash',
|
|
45
|
+
'console': 'bash',
|
|
46
|
+
'command': 'bash',
|
|
47
|
+
'bash session': 'bash',
|
|
48
|
+
'zsh': 'bash',
|
|
49
|
+
|
|
50
|
+
// Python
|
|
51
|
+
'py': 'python',
|
|
52
|
+
'py3': 'python',
|
|
53
|
+
'python3': 'python',
|
|
54
|
+
|
|
55
|
+
// Ruby
|
|
56
|
+
'rb': 'ruby',
|
|
57
|
+
|
|
58
|
+
// Markup languages
|
|
59
|
+
'html': 'markup',
|
|
60
|
+
'xml': 'markup',
|
|
61
|
+
'svg': 'markup',
|
|
62
|
+
'mathml': 'markup',
|
|
63
|
+
'ssml': 'markup',
|
|
64
|
+
|
|
65
|
+
// C family
|
|
66
|
+
'c++': 'cpp',
|
|
67
|
+
'c#': 'csharp',
|
|
68
|
+
'cs': 'csharp',
|
|
69
|
+
|
|
70
|
+
// Other common aliases
|
|
71
|
+
'yml': 'yaml',
|
|
72
|
+
'md': 'markdown',
|
|
73
|
+
'dockerfile': 'docker',
|
|
74
|
+
'objc': 'objectivec',
|
|
75
|
+
'hs': 'haskell',
|
|
76
|
+
'rs': 'rust',
|
|
77
|
+
'kt': 'kotlin',
|
|
78
|
+
'proto': 'protobuf',
|
|
79
|
+
'coffee': 'coffeescript',
|
|
80
|
+
'gawk': 'awk',
|
|
81
|
+
'hbs': 'handlebars',
|
|
82
|
+
'mustache': 'handlebars',
|
|
83
|
+
'g4': 'antlr4',
|
|
84
|
+
'ino': 'arduino',
|
|
85
|
+
'adoc': 'asciidoc',
|
|
86
|
+
'idr': 'idris',
|
|
87
|
+
'eta': 'ejs',
|
|
88
|
+
};
|
|
21
89
|
|
|
22
90
|
/**
|
|
23
91
|
* @readonly
|
|
@@ -50,9 +118,9 @@ const getHighlightToken = (tokens) => {
|
|
|
50
118
|
|
|
51
119
|
for (let i = 0; i < tokens.length; i++) {
|
|
52
120
|
|
|
53
|
-
if (
|
|
121
|
+
if (themeTokenKeys.indexOf(tokens[i]) !== -1) {
|
|
54
122
|
|
|
55
|
-
tokenFound =
|
|
123
|
+
tokenFound = theme.token[tokens[i]];
|
|
56
124
|
break;
|
|
57
125
|
}
|
|
58
126
|
|
|
@@ -132,7 +200,7 @@ const _addBackground = (source, originalSource) => {
|
|
|
132
200
|
}
|
|
133
201
|
|
|
134
202
|
bgAddedSource.push(
|
|
135
|
-
|
|
203
|
+
theme.background(sourceLines[i]) + theme.toEOL((" ").repeat(fill2end))
|
|
136
204
|
);
|
|
137
205
|
|
|
138
206
|
}
|
|
@@ -142,14 +210,88 @@ const _addBackground = (source, originalSource) => {
|
|
|
142
210
|
}
|
|
143
211
|
|
|
144
212
|
/**
|
|
145
|
-
*
|
|
146
|
-
* @param {string}
|
|
147
|
-
* @
|
|
148
|
-
|
|
213
|
+
* Resolves language aliases to canonical names
|
|
214
|
+
* @param {string} language - Language identifier (may be an alias)
|
|
215
|
+
* @returns {string|null} - Canonical language name or null if no language provided
|
|
216
|
+
*/
|
|
217
|
+
const resolveLanguageAlias = (language) => {
|
|
218
|
+
if (!language) return null;
|
|
219
|
+
|
|
220
|
+
const normalized = language.toLowerCase().trim();
|
|
221
|
+
return languageAliases[normalized] || normalized;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Dynamically loads a language component if not already loaded
|
|
226
|
+
* @param {string} language - Language to load (canonical name or alias)
|
|
227
|
+
* @returns {boolean} - True if language is available, false otherwise
|
|
228
|
+
*/
|
|
229
|
+
const ensureLanguageLoaded = (language) => {
|
|
230
|
+
if (!language) return false;
|
|
231
|
+
|
|
232
|
+
// Resolve alias to canonical name
|
|
233
|
+
const canonicalLang = resolveLanguageAlias(language);
|
|
234
|
+
if (!canonicalLang) return false;
|
|
235
|
+
|
|
236
|
+
// Check if already loaded in our cache
|
|
237
|
+
if (loadedLanguagesCache.has(canonicalLang)) {
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Check if language exists in Prism.languages (might be pre-loaded)
|
|
242
|
+
// @ts-ignore
|
|
243
|
+
if (Prism.languages[canonicalLang] !== undefined) {
|
|
244
|
+
loadedLanguagesCache.add(canonicalLang);
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Attempt to load the language
|
|
249
|
+
try {
|
|
250
|
+
// Suppress PrismJS warnings for this operation
|
|
251
|
+
const originalSilent = loadLanguages.silent;
|
|
252
|
+
loadLanguages.silent = true;
|
|
253
|
+
|
|
254
|
+
// Load the language (PrismJS handles dependencies automatically)
|
|
255
|
+
loadLanguages([canonicalLang]);
|
|
256
|
+
|
|
257
|
+
// Restore original silent setting
|
|
258
|
+
loadLanguages.silent = originalSilent;
|
|
259
|
+
|
|
260
|
+
// Verify it loaded successfully
|
|
261
|
+
// @ts-ignore
|
|
262
|
+
if (Prism.languages[canonicalLang] !== undefined) {
|
|
263
|
+
loadedLanguagesCache.add(canonicalLang);
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return false;
|
|
268
|
+
|
|
269
|
+
} catch (error) {
|
|
270
|
+
// Language loading failed
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get information about loaded languages (for debugging/monitoring)
|
|
277
|
+
* @returns {{count: number, languages: string[]}} - Statistics about loaded languages
|
|
278
|
+
*/
|
|
279
|
+
const getLoadedLanguagesInfo = () => {
|
|
280
|
+
return {
|
|
281
|
+
count: loadedLanguagesCache.size,
|
|
282
|
+
languages: Array.from(loadedLanguagesCache).sort()
|
|
283
|
+
};
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
*
|
|
288
|
+
* @param {string} source
|
|
289
|
+
* @param {string} language
|
|
290
|
+
* @param {sourceTheme} outTheme
|
|
149
291
|
*/
|
|
150
292
|
const _highlight = (source, language, outTheme) => {
|
|
151
293
|
|
|
152
|
-
// Detect if theme value is supported - otherwise just use default
|
|
294
|
+
// Detect if theme value is supported - otherwise just use default Okaidia theme
|
|
153
295
|
if (outTheme !== undefined) {
|
|
154
296
|
|
|
155
297
|
let themePath = path.join(__dirname, './themes/', outTheme + '.theme'),
|
|
@@ -157,48 +299,51 @@ const _highlight = (source, language, outTheme) => {
|
|
|
157
299
|
|
|
158
300
|
if (fs.existsSync(filePath)) {
|
|
159
301
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
302
|
+
theme = require(filePath);
|
|
303
|
+
themeTokenKeys = Object.keys(theme.token);
|
|
163
304
|
|
|
164
305
|
} else {
|
|
165
306
|
|
|
166
|
-
throw `Theme '${outTheme}' do not
|
|
307
|
+
throw `Theme '${outTheme}' do not exist`
|
|
167
308
|
|
|
168
309
|
}
|
|
169
310
|
|
|
170
311
|
}
|
|
171
312
|
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
// @ts-ignore
|
|
177
|
-
Prism.languages['console'] = Prism.languages['shell'];
|
|
178
|
-
// @ts-ignore
|
|
179
|
-
Prism.languages['command'] = Prism.languages['shell'];
|
|
180
|
-
// @ts-ignore
|
|
181
|
-
Prism.languages['bash session'] = Prism.languages['shell'];
|
|
313
|
+
// If no language specified, render as plain text
|
|
314
|
+
if (!language) {
|
|
315
|
+
return _addBackground(source, source);
|
|
316
|
+
}
|
|
182
317
|
|
|
183
|
-
//
|
|
184
|
-
|
|
318
|
+
// Dynamically load the language if needed
|
|
319
|
+
const languageLoaded = ensureLanguageLoaded(language);
|
|
320
|
+
|
|
321
|
+
if (!languageLoaded) {
|
|
322
|
+
// Language doesn't exist or failed to load - fallback to plain text
|
|
323
|
+
// Optionally log a warning (can be configured via environment variable)
|
|
324
|
+
if (process.env.MARKSHELL_WARN_UNKNOWN_LANG === 'true') {
|
|
325
|
+
console.warn(`[markshell] Unknown language '${language}' - rendering as plain text`);
|
|
326
|
+
}
|
|
327
|
+
return _addBackground(source, source);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Get the canonical language name for highlighting
|
|
331
|
+
const canonicalLang = resolveLanguageAlias(language);
|
|
332
|
+
|
|
333
|
+
// @ts-ignore - Prism is loaded globally
|
|
334
|
+
if (Prism.languages[canonicalLang] !== undefined) {
|
|
185
335
|
// @ts-ignore
|
|
186
|
-
const prismCode = prismjs.highlight(source, Prism.languages[
|
|
336
|
+
const prismCode = prismjs.highlight(source, Prism.languages[canonicalLang], canonicalLang);
|
|
187
337
|
// load HTML fragment
|
|
188
338
|
const dom = JSDOM.fragment(prismCode);
|
|
189
339
|
|
|
190
|
-
// console.log('Original Source:\n', source);
|
|
191
|
-
|
|
192
340
|
var highlightedSource = parseFormatedContent(dom.childNodes, 0);
|
|
193
341
|
|
|
194
342
|
return _addBackground(highlightedSource, source);
|
|
195
343
|
|
|
196
344
|
} else {
|
|
197
|
-
|
|
345
|
+
// Shouldn't reach here if ensureLanguageLoaded worked correctly
|
|
198
346
|
return _addBackground(source, source);
|
|
199
|
-
|
|
200
|
-
// throw `Language '${language}' couldn't be found`;
|
|
201
|
-
|
|
202
347
|
}
|
|
203
348
|
|
|
204
349
|
}
|
|
@@ -206,5 +351,7 @@ const _highlight = (source, language, outTheme) => {
|
|
|
206
351
|
module.exports = {
|
|
207
352
|
highlight: _highlight,
|
|
208
353
|
themes: sourceTheme,
|
|
209
|
-
availableThemes: sourceTheme
|
|
354
|
+
availableThemes: sourceTheme,
|
|
355
|
+
getLoadedLanguagesInfo: getLoadedLanguagesInfo,
|
|
356
|
+
resolveLanguageAlias: resolveLanguageAlias
|
|
210
357
|
}
|