metalsmith-prism 5.0.1 → 5.0.4
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 +80 -22
- package/lib/index.cjs +209 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.js +160 -90
- package/lib/index.js.map +1 -0
- package/lib/index.modern.js +186 -0
- package/lib/index.modern.js.map +1 -0
- package/package.json +41 -14
- package/src/index.js +214 -0
package/README.md
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
# metalsmith-prism
|
|
2
2
|
|
|
3
|
-
A Metalsmith plugin that **adds Prism specific HTML markup** to code sections for syntax coloring.
|
|
3
|
+
A Metalsmith plugin that **adds Prism specific HTML markup** to code sections for syntax coloring. Now with full dual module support for both **ESM** and **CommonJS** environments.
|
|
4
4
|
|
|
5
|
-
[![
|
|
6
|
-
[![
|
|
7
|
-
[![
|
|
8
|
-
[![
|
|
5
|
+
[![metalsmith:plugin][metalsmith-badge]][metalsmith-url]
|
|
6
|
+
[![npm: version][npm-badge]][npm-url]
|
|
7
|
+
[![license: MIT][license-badge]][license-url]
|
|
8
|
+
[![coverage][coverage-badge]][coverage-url]
|
|
9
|
+
[![ESM/CommonJS][modules-badge]][npm-url]
|
|
10
|
+
|
|
11
|
+
## Dual Module Support (ESM and CommonJS)
|
|
12
|
+
|
|
13
|
+
This plugin supports both ESM and CommonJS environments with no configuration needed:
|
|
14
|
+
|
|
15
|
+
- ESM: `import prism from 'metalsmith-prism'`
|
|
16
|
+
- CommonJS: `const prism = require('metalsmith-prism')`
|
|
17
|
+
|
|
18
|
+
The package detects your environment automatically and provides the appropriate module format. This makes it compatible with both modern ESM projects and legacy CommonJS codebases.
|
|
9
19
|
|
|
10
20
|
While this plugin adds all the required Prism HTML markup, **prism.css** must be included on the page to provide the syntax coloring. The plugin:
|
|
11
21
|
|
|
22
|
+
- Supports both ESM and CommonJS environments
|
|
12
23
|
- Automatically handles language dependencies
|
|
13
24
|
- Supports HTML entity decoding
|
|
14
25
|
- Can add line numbers
|
|
15
26
|
- Works seamlessly with Markdown code blocks
|
|
16
|
-
- Supports all Prism.js
|
|
27
|
+
- Supports all Prism.js language
|
|
17
28
|
|
|
18
29
|
## Requirements
|
|
19
30
|
|
|
20
|
-
- Node `>= 18.
|
|
21
|
-
-
|
|
22
|
-
- Metalsmith `>= v2.6.x`
|
|
31
|
+
- Node `>= 18.0.0`
|
|
32
|
+
- Metalsmith `>= v2.6.0`
|
|
23
33
|
|
|
24
34
|
## Quick Start
|
|
25
35
|
|
|
@@ -35,7 +45,7 @@ metalsmith(__dirname).use(
|
|
|
35
45
|
prism({
|
|
36
46
|
decode: true, // Decode HTML entities
|
|
37
47
|
lineNumbers: true, // Show line numbers
|
|
38
|
-
preLoad: ['java']
|
|
48
|
+
preLoad: ['java'] // Pre-load language dependencies
|
|
39
49
|
})
|
|
40
50
|
);
|
|
41
51
|
```
|
|
@@ -74,21 +84,44 @@ The css files can be downloaded from the [Prism website](https://prismjs.com/dow
|
|
|
74
84
|
|
|
75
85
|
### Add `metalsmith-prism` plugin to metalsmith
|
|
76
86
|
|
|
87
|
+
**ESM:**
|
|
88
|
+
|
|
77
89
|
```js
|
|
78
|
-
|
|
90
|
+
import Metalsmith from 'metalsmith';
|
|
91
|
+
import prism from 'metalsmith-prism';
|
|
92
|
+
|
|
93
|
+
Metalsmith(__dirname).use(prism()).build();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**CommonJS:**
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
const Metalsmith = require('metalsmith');
|
|
79
100
|
const prism = require('metalsmith-prism');
|
|
80
101
|
|
|
81
|
-
|
|
102
|
+
Metalsmith(__dirname).use(prism()).build();
|
|
82
103
|
```
|
|
83
104
|
|
|
84
105
|
### To use with Markdown code blocks rendered by [@metalsmith/markdown](https://github.com/metalsmith/markdown)
|
|
85
106
|
|
|
107
|
+
**ESM:**
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
import Metalsmith from 'metalsmith';
|
|
111
|
+
import markdown from '@metalsmith/markdown';
|
|
112
|
+
import prism from 'metalsmith-prism';
|
|
113
|
+
|
|
114
|
+
Metalsmith(__dirname).use(markdown()).use(prism()).build();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**CommonJS:**
|
|
118
|
+
|
|
86
119
|
```js
|
|
87
|
-
const
|
|
120
|
+
const Metalsmith = require('metalsmith');
|
|
88
121
|
const markdown = require('@metalsmith/markdown');
|
|
89
122
|
const prism = require('metalsmith-prism');
|
|
90
123
|
|
|
91
|
-
|
|
124
|
+
Metalsmith(__dirname).use(markdown()).use(prism()).build();
|
|
92
125
|
```
|
|
93
126
|
|
|
94
127
|
## Language support
|
|
@@ -106,7 +139,7 @@ Always decode the html entities when processing language of type `markup`
|
|
|
106
139
|
```js
|
|
107
140
|
Metalsmith(__dirname).use(
|
|
108
141
|
prism({
|
|
109
|
-
decode: true
|
|
142
|
+
decode: true
|
|
110
143
|
})
|
|
111
144
|
);
|
|
112
145
|
```
|
|
@@ -117,8 +150,8 @@ Adds the additional HTML markup so line numbers can be added via the line-number
|
|
|
117
150
|
|
|
118
151
|
```javascript
|
|
119
152
|
Metalsmith(__dirname).use(
|
|
120
|
-
|
|
121
|
-
lineNumbers: true
|
|
153
|
+
prism({
|
|
154
|
+
lineNumbers: true
|
|
122
155
|
})
|
|
123
156
|
);
|
|
124
157
|
```
|
|
@@ -132,7 +165,7 @@ Useful for loading syntax that extends other language components that are not au
|
|
|
132
165
|
```javascript
|
|
133
166
|
Metalsmith(__dirname).use(
|
|
134
167
|
prism({
|
|
135
|
-
preLoad: ['java', 'scala']
|
|
168
|
+
preLoad: ['java', 'scala']
|
|
136
169
|
})
|
|
137
170
|
);
|
|
138
171
|
```
|
|
@@ -143,14 +176,20 @@ To enable debug logs, set the `DEBUG` environment variable to `metalsmith-prism`
|
|
|
143
176
|
|
|
144
177
|
Linux/Mac:
|
|
145
178
|
|
|
146
|
-
```
|
|
147
|
-
DEBUG=metalsmith-prism
|
|
179
|
+
```bash
|
|
180
|
+
DEBUG=metalsmith-prism npm test
|
|
148
181
|
```
|
|
149
182
|
|
|
150
183
|
Windows:
|
|
151
184
|
|
|
185
|
+
```bash
|
|
186
|
+
set "DEBUG=metalsmith-prism" && npm test
|
|
152
187
|
```
|
|
153
|
-
|
|
188
|
+
|
|
189
|
+
Or use the test:debug script:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
npm run test:debug
|
|
154
193
|
```
|
|
155
194
|
|
|
156
195
|
## CLI Usage
|
|
@@ -168,10 +207,29 @@ Add `metalsmith-prism` key to your `metalsmith.json` plugins key
|
|
|
168
207
|
}
|
|
169
208
|
```
|
|
170
209
|
|
|
210
|
+
## Test Coverage
|
|
211
|
+
|
|
212
|
+
This project maintains high statement and line coverage for the source code. Coverage is verified during the release process using the c8 coverage tool.
|
|
213
|
+
|
|
171
214
|
## Credits
|
|
172
215
|
|
|
173
|
-
[Robert McGuinness](https://github.com/robmcguinness) - for the initial implementation of the plugin.
|
|
216
|
+
- [Robert McGuinness](https://github.com/robmcguinness) - for the initial implementation of the plugin.
|
|
217
|
+
- [Werner Glinka](https://github.com/wernerglinka) - current maintainer.
|
|
174
218
|
|
|
175
219
|
## License
|
|
176
220
|
|
|
177
221
|
Code released under [the MIT license](https://github.com/wernerglinka/metalsmith-prism/blob/main/LICENSE).
|
|
222
|
+
|
|
223
|
+
[coverage-badge]: https://img.shields.io/badge/coverage-94%25-brightgreen
|
|
224
|
+
[coverage-url]: #test-coverage
|
|
225
|
+
[modules-badge]: https://img.shields.io/badge/modules-ESM%2FCJS-blue
|
|
226
|
+
[npm-url]: https://npmjs.org/package/metalsmith-prism
|
|
227
|
+
[npm-badge]: https://img.shields.io/npm/v/metalsmith-prism.svg
|
|
228
|
+
[npm-url]: https://www.npmjs.com/package/metalsmith-prism
|
|
229
|
+
[metalsmith-badge]: https://img.shields.io/badge/metalsmith-plugin-green.svg?longCache=true
|
|
230
|
+
[metalsmith-url]: https://metalsmith.io
|
|
231
|
+
[license-badge]: https://img.shields.io/github/license/wernerglinka/metalsmith-prism
|
|
232
|
+
[license-url]: LICENSE
|
|
233
|
+
[coverage-badge]: https://img.shields.io/badge/test%20coverage-98%25-brightgreen
|
|
234
|
+
[coverage-url]: #test-coverage
|
|
235
|
+
[modules-badge]: https://img.shields.io/badge/modules-ESM%2FCJS-blue
|
package/lib/index.cjs
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var cheerio = require('cheerio');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var Prism = require('prismjs');
|
|
6
|
+
var loadLanguages = require('prismjs/components/index.js');
|
|
7
|
+
var he = require('he');
|
|
8
|
+
var debugLib = require('debug');
|
|
9
|
+
|
|
10
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
11
|
+
|
|
12
|
+
var Prism__default = /*#__PURE__*/_interopDefaultLegacy(Prism);
|
|
13
|
+
var loadLanguages__default = /*#__PURE__*/_interopDefaultLegacy(loadLanguages);
|
|
14
|
+
var he__default = /*#__PURE__*/_interopDefaultLegacy(he);
|
|
15
|
+
var debugLib__default = /*#__PURE__*/_interopDefaultLegacy(debugLib);
|
|
16
|
+
|
|
17
|
+
const debug = debugLib__default["default"]('metalsmith-prism');
|
|
18
|
+
|
|
19
|
+
// Import languages from Prism's default export
|
|
20
|
+
const {
|
|
21
|
+
languages
|
|
22
|
+
} = Prism__default["default"];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check if a file is HTML based on its extension
|
|
26
|
+
* @param {string} filePath - Path to the file
|
|
27
|
+
* @returns {boolean} - True if the file has an HTML extension
|
|
28
|
+
*/
|
|
29
|
+
const isHTMLFile = filePath => {
|
|
30
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
31
|
+
return ['.html', '.htm'].includes(extension);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef Options
|
|
36
|
+
* @property {boolean} [decode=false] - Whether to decode HTML entities
|
|
37
|
+
* @property {boolean} [lineNumbers=false] - Whether to add line numbers
|
|
38
|
+
* @property {string[]} [preLoad=[]] - Languages to preload
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Metalsmith plugin to highlight code syntax with PrismJS
|
|
43
|
+
*
|
|
44
|
+
* This plugin finds all code blocks in HTML files that have language-* classes
|
|
45
|
+
* and applies Prism.js syntax highlighting to them. It can also add line numbers
|
|
46
|
+
* and handle HTML entity decoding.
|
|
47
|
+
*
|
|
48
|
+
* @param {Options} [options] - Configuration options
|
|
49
|
+
* @param {boolean} [options.decode=false] - Whether to decode HTML entities in code blocks
|
|
50
|
+
* @param {boolean} [options.lineNumbers=false] - Whether to add line numbers to code blocks
|
|
51
|
+
* @param {string[]} [options.preLoad=[]] - Languages to preload before processing
|
|
52
|
+
* @returns {import('metalsmith').Plugin} - A metalsmith plugin function
|
|
53
|
+
* @example
|
|
54
|
+
* // Basic usage
|
|
55
|
+
* metalsmith.use(prism());
|
|
56
|
+
*
|
|
57
|
+
* // With options
|
|
58
|
+
* metalsmith.use(prism({
|
|
59
|
+
* decode: true,
|
|
60
|
+
* lineNumbers: true,
|
|
61
|
+
* preLoad: ['java', 'scala']
|
|
62
|
+
* }));
|
|
63
|
+
*/
|
|
64
|
+
const metalsmithPrism = (options = {}) => {
|
|
65
|
+
// Create a new options object with defaults
|
|
66
|
+
const opts = {
|
|
67
|
+
decode: false,
|
|
68
|
+
lineNumbers: false,
|
|
69
|
+
preLoad: [],
|
|
70
|
+
...options
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Track loaded languages to avoid duplicate loading
|
|
74
|
+
const loadedLanguages = new Set();
|
|
75
|
+
|
|
76
|
+
// Always load PHP by default
|
|
77
|
+
debug('Loading PHP by default');
|
|
78
|
+
try {
|
|
79
|
+
loadLanguages__default["default"](['php']);
|
|
80
|
+
loadedLanguages.add('php');
|
|
81
|
+
} catch (e) {
|
|
82
|
+
debug('Failed to load PHP:', e);
|
|
83
|
+
}
|
|
84
|
+
if (opts.preLoad && opts.preLoad.length) {
|
|
85
|
+
debug('Preloading languages:', opts.preLoad);
|
|
86
|
+
opts.preLoad.forEach(language => {
|
|
87
|
+
if (!loadedLanguages.has(language)) {
|
|
88
|
+
try {
|
|
89
|
+
loadLanguages__default["default"]([language]);
|
|
90
|
+
loadedLanguages.add(language);
|
|
91
|
+
debug(`Successfully preloaded language: ${language}`);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.warn(`Failed to preload prism syntax: ${language}!`, e);
|
|
94
|
+
debug(`Error preloading language ${language}:`, e);
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
debug(`Language ${language} already loaded, skipping`);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Require optional language package
|
|
104
|
+
* @param {string} language
|
|
105
|
+
* @param {Set} loadedLanguages
|
|
106
|
+
*/
|
|
107
|
+
const requireLanguage = (language, loadedLanguages) => {
|
|
108
|
+
if (loadedLanguages.has(language) || languages[language]) {
|
|
109
|
+
debug(`Language ${language} already available, skipping load`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
debug(`Loading language on-demand: ${language}`);
|
|
113
|
+
try {
|
|
114
|
+
loadLanguages__default["default"]([language]);
|
|
115
|
+
loadedLanguages.add(language);
|
|
116
|
+
debug(`Successfully loaded language: ${language}`);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.warn(`Failed to load prism syntax: ${language}!`, e);
|
|
119
|
+
debug(`Error loading language ${language}:`, e);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Set up line numbers functionality
|
|
124
|
+
const NEW_LINE_EXP = /\n(?!$)/g;
|
|
125
|
+
let lineNumbersWrapper;
|
|
126
|
+
|
|
127
|
+
// Only set up the hook if line numbers are requested
|
|
128
|
+
if (opts.lineNumbers) {
|
|
129
|
+
debug('Setting up line numbers hook');
|
|
130
|
+
Prism__default["default"].hooks.add('after-tokenize', env => {
|
|
131
|
+
const match = env.code.match(NEW_LINE_EXP);
|
|
132
|
+
const linesNum = match ? match.length + 1 : 1;
|
|
133
|
+
debug(`Counted ${linesNum} lines for line numbers`);
|
|
134
|
+
const lines = new Array(linesNum + 1).join('<span></span>');
|
|
135
|
+
lineNumbersWrapper = `<span aria-hidden="true" class="line-numbers-rows">${lines}</span>`;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return (files, metalsmith, done) => {
|
|
139
|
+
debug('Starting metalsmith-prism plugin');
|
|
140
|
+
debug('Options:', opts);
|
|
141
|
+
|
|
142
|
+
// Call done asynchronously to avoid blocking
|
|
143
|
+
setImmediate(done);
|
|
144
|
+
try {
|
|
145
|
+
Object.keys(files).forEach(file => {
|
|
146
|
+
if (!isHTMLFile(file)) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
debug(`Processing HTML file: ${file}`);
|
|
150
|
+
const contents = files[file].contents.toString();
|
|
151
|
+
const $ = cheerio.load(contents, {
|
|
152
|
+
decodeEntities: false
|
|
153
|
+
});
|
|
154
|
+
let highlighted = false;
|
|
155
|
+
const code = $('code');
|
|
156
|
+
if (!code.length) {
|
|
157
|
+
debug(`No code blocks found in ${file}`);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
debug(`Found ${code.length} code blocks in ${file}`);
|
|
161
|
+
code.each(function () {
|
|
162
|
+
const $this = $(this);
|
|
163
|
+
const className = $this.attr('class') || '';
|
|
164
|
+
const targets = className.split('language-');
|
|
165
|
+
let addLineNumbers = false;
|
|
166
|
+
if (targets.length > 1) {
|
|
167
|
+
const $pre = $this.parent('pre');
|
|
168
|
+
if ($pre) {
|
|
169
|
+
// Copy className to <pre> container
|
|
170
|
+
$pre.addClass(className);
|
|
171
|
+
if (opts.lineNumbers) {
|
|
172
|
+
$pre.addClass('line-numbers');
|
|
173
|
+
addLineNumbers = true;
|
|
174
|
+
debug('Adding line numbers');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
highlighted = true;
|
|
178
|
+
let language = targets[1];
|
|
179
|
+
debug(`Detected language: ${language}`);
|
|
180
|
+
requireLanguage(language, loadedLanguages);
|
|
181
|
+
if (!languages[language]) {
|
|
182
|
+
debug(`Language ${language} not available, falling back to markup`);
|
|
183
|
+
language = 'markup';
|
|
184
|
+
}
|
|
185
|
+
const html = language === 'markup' && !opts.decode ? $this.html() : he__default["default"].decode($this.html());
|
|
186
|
+
debug(`HTML decoding ${opts.decode ? 'applied' : 'not applied'} for language ${language}`);
|
|
187
|
+
debug(`Highlighting code with language: ${language}`);
|
|
188
|
+
const highlightedCode = Prism__default["default"].highlight(html, languages[language]);
|
|
189
|
+
$this.html(addLineNumbers ? highlightedCode + lineNumbersWrapper : highlightedCode);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
if (highlighted) {
|
|
193
|
+
debug(`Updating contents of ${file} with highlighted code`);
|
|
194
|
+
files[file].contents = Buffer.from($.html());
|
|
195
|
+
} else {
|
|
196
|
+
debug(`No code was highlighted in ${file}`);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
debug('Completed metalsmith-prism plugin');
|
|
200
|
+
} catch (error) {
|
|
201
|
+
debug('Error in metalsmith-prism plugin:', error);
|
|
202
|
+
// We can't call done(error) here because done has already been called
|
|
203
|
+
console.error('Error processing files:', error);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
module.exports = metalsmithPrism;
|
|
209
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/index.js"],"sourcesContent":["import { load } from 'cheerio';\nimport { extname } from 'path';\nimport Prism from 'prismjs';\nimport loadLanguages from 'prismjs/components/index.js';\nimport he from 'he';\nimport debugLib from 'debug';\n\nconst debug = debugLib( 'metalsmith-prism' );\n\n// Import languages from Prism's default export\nconst { languages } = Prism;\n\n/**\n * Check if a file is HTML based on its extension\n * @param {string} filePath - Path to the file\n * @returns {boolean} - True if the file has an HTML extension\n */\nconst isHTMLFile = ( filePath ) => {\n const extension = extname( filePath ).toLowerCase();\n return [ '.html', '.htm' ].includes( extension );\n};\n\n/**\n * @typedef Options\n * @property {boolean} [decode=false] - Whether to decode HTML entities\n * @property {boolean} [lineNumbers=false] - Whether to add line numbers\n * @property {string[]} [preLoad=[]] - Languages to preload\n */\n\n/**\n * Metalsmith plugin to highlight code syntax with PrismJS\n *\n * This plugin finds all code blocks in HTML files that have language-* classes\n * and applies Prism.js syntax highlighting to them. It can also add line numbers\n * and handle HTML entity decoding.\n *\n * @param {Options} [options] - Configuration options\n * @param {boolean} [options.decode=false] - Whether to decode HTML entities in code blocks\n * @param {boolean} [options.lineNumbers=false] - Whether to add line numbers to code blocks\n * @param {string[]} [options.preLoad=[]] - Languages to preload before processing\n * @returns {import('metalsmith').Plugin} - A metalsmith plugin function\n * @example\n * // Basic usage\n * metalsmith.use(prism());\n *\n * // With options\n * metalsmith.use(prism({\n * decode: true,\n * lineNumbers: true,\n * preLoad: ['java', 'scala']\n * }));\n */\nconst metalsmithPrism = ( options = {} ) => {\n // Create a new options object with defaults\n const opts = {\n decode: false,\n lineNumbers: false,\n preLoad: [],\n ...options\n };\n\n // Track loaded languages to avoid duplicate loading\n const loadedLanguages = new Set();\n\n // Always load PHP by default\n debug( 'Loading PHP by default' );\n try {\n loadLanguages( [ 'php' ] );\n loadedLanguages.add( 'php' );\n } catch ( e ) {\n debug( 'Failed to load PHP:', e );\n }\n\n if ( opts.preLoad && opts.preLoad.length ) {\n debug( 'Preloading languages:', opts.preLoad );\n opts.preLoad.forEach( ( language ) => {\n if ( !loadedLanguages.has( language ) ) {\n try {\n loadLanguages( [ language ] );\n loadedLanguages.add( language );\n debug( `Successfully preloaded language: ${ language }` );\n } catch ( e ) {\n console.warn( `Failed to preload prism syntax: ${ language }!`, e );\n debug( `Error preloading language ${ language }:`, e );\n }\n } else {\n debug( `Language ${ language } already loaded, skipping` );\n }\n } );\n }\n\n /**\n * Require optional language package\n * @param {string} language\n * @param {Set} loadedLanguages\n */\n const requireLanguage = ( language, loadedLanguages ) => {\n if ( loadedLanguages.has( language ) || languages[ language ] ) {\n debug( `Language ${ language } already available, skipping load` );\n return;\n }\n\n debug( `Loading language on-demand: ${ language }` );\n try {\n loadLanguages( [ language ] );\n loadedLanguages.add( language );\n debug( `Successfully loaded language: ${ language }` );\n } catch ( e ) {\n console.warn( `Failed to load prism syntax: ${ language }!`, e );\n debug( `Error loading language ${ language }:`, e );\n }\n };\n\n // Set up line numbers functionality\n const NEW_LINE_EXP = /\\n(?!$)/g;\n let lineNumbersWrapper;\n\n // Only set up the hook if line numbers are requested\n if ( opts.lineNumbers ) {\n debug( 'Setting up line numbers hook' );\n Prism.hooks.add( 'after-tokenize', ( env ) => {\n const match = env.code.match( NEW_LINE_EXP );\n const linesNum = match ? match.length + 1 : 1;\n debug( `Counted ${ linesNum } lines for line numbers` );\n const lines = new Array( linesNum + 1 ).join( '<span></span>' );\n lineNumbersWrapper = `<span aria-hidden=\"true\" class=\"line-numbers-rows\">${ lines }</span>`;\n } );\n }\n\n return ( files, metalsmith, done ) => {\n debug( 'Starting metalsmith-prism plugin' );\n debug( 'Options:', opts );\n\n // Call done asynchronously to avoid blocking\n setImmediate( done );\n\n try {\n Object.keys( files ).forEach( ( file ) => {\n if ( !isHTMLFile( file ) ) {\n return;\n }\n\n debug( `Processing HTML file: ${ file }` );\n const contents = files[ file ].contents.toString();\n const $ = load( contents, { decodeEntities: false } );\n let highlighted = false;\n const code = $( 'code' );\n\n if ( !code.length ) {\n debug( `No code blocks found in ${ file }` );\n return;\n }\n\n debug( `Found ${ code.length } code blocks in ${ file }` );\n\n code.each( function() {\n const $this = $( this );\n\n const className = $this.attr( 'class' ) || '';\n const targets = className.split( 'language-' );\n let addLineNumbers = false;\n\n if ( targets.length > 1 ) {\n const $pre = $this.parent( 'pre' );\n\n if ( $pre ) {\n // Copy className to <pre> container\n $pre.addClass( className );\n\n if ( opts.lineNumbers ) {\n $pre.addClass( 'line-numbers' );\n addLineNumbers = true;\n debug( 'Adding line numbers' );\n }\n }\n\n highlighted = true;\n let language = targets[ 1 ];\n debug( `Detected language: ${ language }` );\n requireLanguage( language, loadedLanguages );\n\n if ( !languages[ language ] ) {\n debug( `Language ${ language } not available, falling back to markup` );\n language = 'markup';\n }\n\n const html = language === 'markup' && !opts.decode ? $this.html() : he.decode( $this.html() );\n debug( `HTML decoding ${ opts.decode ? 'applied' : 'not applied' } for language ${ language }` );\n\n debug( `Highlighting code with language: ${ language }` );\n const highlightedCode = Prism.highlight( html, languages[ language ] );\n $this.html( addLineNumbers ? highlightedCode + lineNumbersWrapper : highlightedCode );\n }\n } );\n\n if ( highlighted ) {\n debug( `Updating contents of ${ file } with highlighted code` );\n files[ file ].contents = Buffer.from( $.html() );\n } else {\n debug( `No code was highlighted in ${ file }` );\n }\n } );\n\n debug( 'Completed metalsmith-prism plugin' );\n } catch ( error ) {\n debug( 'Error in metalsmith-prism plugin:', error );\n // We can't call done(error) here because done has already been called\n console.error( 'Error processing files:', error );\n }\n };\n};\n\n// ESM export\nexport default metalsmithPrism;\n"],"names":["debug","debugLib","languages","Prism","isHTMLFile","filePath","extension","extname","toLowerCase","includes","metalsmithPrism","options","opts","decode","lineNumbers","preLoad","loadedLanguages","Set","loadLanguages","add","e","length","forEach","language","has","console","warn","requireLanguage","NEW_LINE_EXP","lineNumbersWrapper","hooks","env","match","code","linesNum","lines","Array","join","files","metalsmith","done","setImmediate","Object","keys","file","contents","toString","$","load","decodeEntities","highlighted","each","$this","className","attr","targets","split","addLineNumbers","$pre","parent","addClass","html","he","highlightedCode","highlight","Buffer","from","error"],"mappings":"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"}
|
package/lib/index.js
CHANGED
|
@@ -3,20 +3,23 @@ import { extname } from 'path';
|
|
|
3
3
|
import Prism from 'prismjs';
|
|
4
4
|
import loadLanguages from 'prismjs/components/index.js';
|
|
5
5
|
import he from 'he';
|
|
6
|
+
import debugLib from 'debug';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
const { languages } = Prism;
|
|
8
|
+
const debug = debugLib('metalsmith-prism');
|
|
9
9
|
|
|
10
|
-
//
|
|
11
|
-
|
|
10
|
+
// Import languages from Prism's default export
|
|
11
|
+
const {
|
|
12
|
+
languages
|
|
13
|
+
} = Prism;
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
|
-
* Check if a file is HTML
|
|
15
|
-
* @param {string} filePath
|
|
16
|
-
* @returns {boolean}
|
|
16
|
+
* Check if a file is HTML based on its extension
|
|
17
|
+
* @param {string} filePath - Path to the file
|
|
18
|
+
* @returns {boolean} - True if the file has an HTML extension
|
|
17
19
|
*/
|
|
18
|
-
const isHTMLFile =
|
|
19
|
-
|
|
20
|
+
const isHTMLFile = filePath => {
|
|
21
|
+
const extension = extname(filePath).toLowerCase();
|
|
22
|
+
return ['.html', '.htm'].includes(extension);
|
|
20
23
|
};
|
|
21
24
|
|
|
22
25
|
/**
|
|
@@ -29,102 +32,169 @@ const isHTMLFile = ( filePath ) => {
|
|
|
29
32
|
/**
|
|
30
33
|
* Metalsmith plugin to highlight code syntax with PrismJS
|
|
31
34
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
35
|
+
* This plugin finds all code blocks in HTML files that have language-* classes
|
|
36
|
+
* and applies Prism.js syntax highlighting to them. It can also add line numbers
|
|
37
|
+
* and handle HTML entity decoding.
|
|
38
|
+
*
|
|
39
|
+
* @param {Options} [options] - Configuration options
|
|
40
|
+
* @param {boolean} [options.decode=false] - Whether to decode HTML entities in code blocks
|
|
41
|
+
* @param {boolean} [options.lineNumbers=false] - Whether to add line numbers to code blocks
|
|
42
|
+
* @param {string[]} [options.preLoad=[]] - Languages to preload before processing
|
|
43
|
+
* @returns {import('metalsmith').Plugin} - A metalsmith plugin function
|
|
44
|
+
* @example
|
|
45
|
+
* // Basic usage
|
|
46
|
+
* metalsmith.use(prism());
|
|
47
|
+
*
|
|
48
|
+
* // With options
|
|
49
|
+
* metalsmith.use(prism({
|
|
50
|
+
* decode: true,
|
|
51
|
+
* lineNumbers: true,
|
|
52
|
+
* preLoad: ['java', 'scala']
|
|
53
|
+
* }));
|
|
34
54
|
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
55
|
+
const metalsmithPrism = (options = {}) => {
|
|
56
|
+
// Create a new options object with defaults
|
|
57
|
+
const opts = {
|
|
58
|
+
decode: false,
|
|
59
|
+
lineNumbers: false,
|
|
60
|
+
preLoad: [],
|
|
61
|
+
...options
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Track loaded languages to avoid duplicate loading
|
|
65
|
+
const loadedLanguages = new Set();
|
|
66
|
+
|
|
67
|
+
// Always load PHP by default
|
|
68
|
+
debug('Loading PHP by default');
|
|
69
|
+
try {
|
|
70
|
+
loadLanguages(['php']);
|
|
71
|
+
loadedLanguages.add('php');
|
|
72
|
+
} catch (e) {
|
|
73
|
+
debug('Failed to load PHP:', e);
|
|
74
|
+
}
|
|
75
|
+
if (opts.preLoad && opts.preLoad.length) {
|
|
76
|
+
debug('Preloading languages:', opts.preLoad);
|
|
77
|
+
opts.preLoad.forEach(language => {
|
|
78
|
+
if (!loadedLanguages.has(language)) {
|
|
79
|
+
try {
|
|
80
|
+
loadLanguages([language]);
|
|
81
|
+
loadedLanguages.add(language);
|
|
82
|
+
debug(`Successfully preloaded language: ${language}`);
|
|
83
|
+
} catch (e) {
|
|
84
|
+
console.warn(`Failed to preload prism syntax: ${language}!`, e);
|
|
85
|
+
debug(`Error preloading language ${language}:`, e);
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
debug(`Language ${language} already loaded, skipping`);
|
|
42
89
|
}
|
|
43
|
-
}
|
|
90
|
+
});
|
|
44
91
|
}
|
|
45
92
|
|
|
46
93
|
/**
|
|
47
94
|
* Require optional language package
|
|
48
95
|
* @param {string} language
|
|
96
|
+
* @param {Set} loadedLanguages
|
|
49
97
|
*/
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
} catch ( e ) {
|
|
55
|
-
console.warn( `Failed to load prism syntax: ${ language }!` );
|
|
56
|
-
}
|
|
98
|
+
const requireLanguage = (language, loadedLanguages) => {
|
|
99
|
+
if (loadedLanguages.has(language) || languages[language]) {
|
|
100
|
+
debug(`Language ${language} already available, skipping load`);
|
|
101
|
+
return;
|
|
57
102
|
}
|
|
58
|
-
|
|
103
|
+
debug(`Loading language on-demand: ${language}`);
|
|
104
|
+
try {
|
|
105
|
+
loadLanguages([language]);
|
|
106
|
+
loadedLanguages.add(language);
|
|
107
|
+
debug(`Successfully loaded language: ${language}`);
|
|
108
|
+
} catch (e) {
|
|
109
|
+
console.warn(`Failed to load prism syntax: ${language}!`, e);
|
|
110
|
+
debug(`Error loading language ${language}:`, e);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
59
113
|
|
|
60
|
-
// Set up line numbers
|
|
114
|
+
// Set up line numbers functionality
|
|
61
115
|
const NEW_LINE_EXP = /\n(?!$)/g;
|
|
62
116
|
let lineNumbersWrapper;
|
|
63
117
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
let
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
118
|
+
// Only set up the hook if line numbers are requested
|
|
119
|
+
if (opts.lineNumbers) {
|
|
120
|
+
debug('Setting up line numbers hook');
|
|
121
|
+
Prism.hooks.add('after-tokenize', env => {
|
|
122
|
+
const match = env.code.match(NEW_LINE_EXP);
|
|
123
|
+
const linesNum = match ? match.length + 1 : 1;
|
|
124
|
+
debug(`Counted ${linesNum} lines for line numbers`);
|
|
125
|
+
const lines = new Array(linesNum + 1).join('<span></span>');
|
|
126
|
+
lineNumbersWrapper = `<span aria-hidden="true" class="line-numbers-rows">${lines}</span>`;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return (files, metalsmith, done) => {
|
|
130
|
+
debug('Starting metalsmith-prism plugin');
|
|
131
|
+
debug('Options:', opts);
|
|
132
|
+
|
|
133
|
+
// Call done asynchronously to avoid blocking
|
|
134
|
+
setImmediate(done);
|
|
135
|
+
try {
|
|
136
|
+
Object.keys(files).forEach(file => {
|
|
137
|
+
if (!isHTMLFile(file)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
debug(`Processing HTML file: ${file}`);
|
|
141
|
+
const contents = files[file].contents.toString();
|
|
142
|
+
const $ = load(contents, {
|
|
143
|
+
decodeEntities: false
|
|
144
|
+
});
|
|
145
|
+
let highlighted = false;
|
|
146
|
+
const code = $('code');
|
|
147
|
+
if (!code.length) {
|
|
148
|
+
debug(`No code blocks found in ${file}`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
debug(`Found ${code.length} code blocks in ${file}`);
|
|
152
|
+
code.each(function () {
|
|
153
|
+
const $this = $(this);
|
|
154
|
+
const className = $this.attr('class') || '';
|
|
155
|
+
const targets = className.split('language-');
|
|
156
|
+
let addLineNumbers = false;
|
|
157
|
+
if (targets.length > 1) {
|
|
158
|
+
const $pre = $this.parent('pre');
|
|
159
|
+
if ($pre) {
|
|
160
|
+
// Copy className to <pre> container
|
|
161
|
+
$pre.addClass(className);
|
|
162
|
+
if (opts.lineNumbers) {
|
|
163
|
+
$pre.addClass('line-numbers');
|
|
164
|
+
addLineNumbers = true;
|
|
165
|
+
debug('Adding line numbers');
|
|
166
|
+
}
|
|
103
167
|
}
|
|
168
|
+
highlighted = true;
|
|
169
|
+
let language = targets[1];
|
|
170
|
+
debug(`Detected language: ${language}`);
|
|
171
|
+
requireLanguage(language, loadedLanguages);
|
|
172
|
+
if (!languages[language]) {
|
|
173
|
+
debug(`Language ${language} not available, falling back to markup`);
|
|
174
|
+
language = 'markup';
|
|
175
|
+
}
|
|
176
|
+
const html = language === 'markup' && !opts.decode ? $this.html() : he.decode($this.html());
|
|
177
|
+
debug(`HTML decoding ${opts.decode ? 'applied' : 'not applied'} for language ${language}`);
|
|
178
|
+
debug(`Highlighting code with language: ${language}`);
|
|
179
|
+
const highlightedCode = Prism.highlight(html, languages[language]);
|
|
180
|
+
$this.html(addLineNumbers ? highlightedCode + lineNumbersWrapper : highlightedCode);
|
|
104
181
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
language = 'markup';
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const html = ( language === 'markup' && !options.decode )
|
|
115
|
-
? $this.html()
|
|
116
|
-
: he.decode( $this.html() );
|
|
117
|
-
|
|
118
|
-
const highlightedCode = Prism.highlight( html, languages[ language ] );
|
|
119
|
-
$this.html( addLineNumbers ? highlightedCode + lineNumbersWrapper : highlightedCode );
|
|
182
|
+
});
|
|
183
|
+
if (highlighted) {
|
|
184
|
+
debug(`Updating contents of ${file} with highlighted code`);
|
|
185
|
+
files[file].contents = Buffer.from($.html());
|
|
186
|
+
} else {
|
|
187
|
+
debug(`No code was highlighted in ${file}`);
|
|
120
188
|
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
189
|
+
});
|
|
190
|
+
debug('Completed metalsmith-prism plugin');
|
|
191
|
+
} catch (error) {
|
|
192
|
+
debug('Error in metalsmith-prism plugin:', error);
|
|
193
|
+
// We can't call done(error) here because done has already been called
|
|
194
|
+
console.error('Error processing files:', error);
|
|
195
|
+
}
|
|
127
196
|
};
|
|
128
|
-
}
|
|
197
|
+
};
|
|
129
198
|
|
|
130
|
-
export default
|
|
199
|
+
export { metalsmithPrism as default };
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.js"],"sourcesContent":["import { load } from 'cheerio';\nimport { extname } from 'path';\nimport Prism from 'prismjs';\nimport loadLanguages from 'prismjs/components/index.js';\nimport he from 'he';\nimport debugLib from 'debug';\n\nconst debug = debugLib( 'metalsmith-prism' );\n\n// Import languages from Prism's default export\nconst { languages } = Prism;\n\n/**\n * Check if a file is HTML based on its extension\n * @param {string} filePath - Path to the file\n * @returns {boolean} - True if the file has an HTML extension\n */\nconst isHTMLFile = ( filePath ) => {\n const extension = extname( filePath ).toLowerCase();\n return [ '.html', '.htm' ].includes( extension );\n};\n\n/**\n * @typedef Options\n * @property {boolean} [decode=false] - Whether to decode HTML entities\n * @property {boolean} [lineNumbers=false] - Whether to add line numbers\n * @property {string[]} [preLoad=[]] - Languages to preload\n */\n\n/**\n * Metalsmith plugin to highlight code syntax with PrismJS\n *\n * This plugin finds all code blocks in HTML files that have language-* classes\n * and applies Prism.js syntax highlighting to them. It can also add line numbers\n * and handle HTML entity decoding.\n *\n * @param {Options} [options] - Configuration options\n * @param {boolean} [options.decode=false] - Whether to decode HTML entities in code blocks\n * @param {boolean} [options.lineNumbers=false] - Whether to add line numbers to code blocks\n * @param {string[]} [options.preLoad=[]] - Languages to preload before processing\n * @returns {import('metalsmith').Plugin} - A metalsmith plugin function\n * @example\n * // Basic usage\n * metalsmith.use(prism());\n *\n * // With options\n * metalsmith.use(prism({\n * decode: true,\n * lineNumbers: true,\n * preLoad: ['java', 'scala']\n * }));\n */\nconst metalsmithPrism = ( options = {} ) => {\n // Create a new options object with defaults\n const opts = {\n decode: false,\n lineNumbers: false,\n preLoad: [],\n ...options\n };\n\n // Track loaded languages to avoid duplicate loading\n const loadedLanguages = new Set();\n\n // Always load PHP by default\n debug( 'Loading PHP by default' );\n try {\n loadLanguages( [ 'php' ] );\n loadedLanguages.add( 'php' );\n } catch ( e ) {\n debug( 'Failed to load PHP:', e );\n }\n\n if ( opts.preLoad && opts.preLoad.length ) {\n debug( 'Preloading languages:', opts.preLoad );\n opts.preLoad.forEach( ( language ) => {\n if ( !loadedLanguages.has( language ) ) {\n try {\n loadLanguages( [ language ] );\n loadedLanguages.add( language );\n debug( `Successfully preloaded language: ${ language }` );\n } catch ( e ) {\n console.warn( `Failed to preload prism syntax: ${ language }!`, e );\n debug( `Error preloading language ${ language }:`, e );\n }\n } else {\n debug( `Language ${ language } already loaded, skipping` );\n }\n } );\n }\n\n /**\n * Require optional language package\n * @param {string} language\n * @param {Set} loadedLanguages\n */\n const requireLanguage = ( language, loadedLanguages ) => {\n if ( loadedLanguages.has( language ) || languages[ language ] ) {\n debug( `Language ${ language } already available, skipping load` );\n return;\n }\n\n debug( `Loading language on-demand: ${ language }` );\n try {\n loadLanguages( [ language ] );\n loadedLanguages.add( language );\n debug( `Successfully loaded language: ${ language }` );\n } catch ( e ) {\n console.warn( `Failed to load prism syntax: ${ language }!`, e );\n debug( `Error loading language ${ language }:`, e );\n }\n };\n\n // Set up line numbers functionality\n const NEW_LINE_EXP = /\\n(?!$)/g;\n let lineNumbersWrapper;\n\n // Only set up the hook if line numbers are requested\n if ( opts.lineNumbers ) {\n debug( 'Setting up line numbers hook' );\n Prism.hooks.add( 'after-tokenize', ( env ) => {\n const match = env.code.match( NEW_LINE_EXP );\n const linesNum = match ? match.length + 1 : 1;\n debug( `Counted ${ linesNum } lines for line numbers` );\n const lines = new Array( linesNum + 1 ).join( '<span></span>' );\n lineNumbersWrapper = `<span aria-hidden=\"true\" class=\"line-numbers-rows\">${ lines }</span>`;\n } );\n }\n\n return ( files, metalsmith, done ) => {\n debug( 'Starting metalsmith-prism plugin' );\n debug( 'Options:', opts );\n\n // Call done asynchronously to avoid blocking\n setImmediate( done );\n\n try {\n Object.keys( files ).forEach( ( file ) => {\n if ( !isHTMLFile( file ) ) {\n return;\n }\n\n debug( `Processing HTML file: ${ file }` );\n const contents = files[ file ].contents.toString();\n const $ = load( contents, { decodeEntities: false } );\n let highlighted = false;\n const code = $( 'code' );\n\n if ( !code.length ) {\n debug( `No code blocks found in ${ file }` );\n return;\n }\n\n debug( `Found ${ code.length } code blocks in ${ file }` );\n\n code.each( function() {\n const $this = $( this );\n\n const className = $this.attr( 'class' ) || '';\n const targets = className.split( 'language-' );\n let addLineNumbers = false;\n\n if ( targets.length > 1 ) {\n const $pre = $this.parent( 'pre' );\n\n if ( $pre ) {\n // Copy className to <pre> container\n $pre.addClass( className );\n\n if ( opts.lineNumbers ) {\n $pre.addClass( 'line-numbers' );\n addLineNumbers = true;\n debug( 'Adding line numbers' );\n }\n }\n\n highlighted = true;\n let language = targets[ 1 ];\n debug( `Detected language: ${ language }` );\n requireLanguage( language, loadedLanguages );\n\n if ( !languages[ language ] ) {\n debug( `Language ${ language } not available, falling back to markup` );\n language = 'markup';\n }\n\n const html = language === 'markup' && !opts.decode ? $this.html() : he.decode( $this.html() );\n debug( `HTML decoding ${ opts.decode ? 'applied' : 'not applied' } for language ${ language }` );\n\n debug( `Highlighting code with language: ${ language }` );\n const highlightedCode = Prism.highlight( html, languages[ language ] );\n $this.html( addLineNumbers ? highlightedCode + lineNumbersWrapper : highlightedCode );\n }\n } );\n\n if ( highlighted ) {\n debug( `Updating contents of ${ file } with highlighted code` );\n files[ file ].contents = Buffer.from( $.html() );\n } else {\n debug( `No code was highlighted in ${ file }` );\n }\n } );\n\n debug( 'Completed metalsmith-prism plugin' );\n } catch ( error ) {\n debug( 'Error in metalsmith-prism plugin:', error );\n // We can't call done(error) here because done has already been called\n console.error( 'Error processing files:', error );\n }\n };\n};\n\n// ESM export\nexport default metalsmithPrism;\n"],"names":["debug","debugLib","languages","Prism","isHTMLFile","filePath","extension","extname","toLowerCase","includes","metalsmithPrism","options","opts","decode","lineNumbers","preLoad","loadedLanguages","Set","loadLanguages","add","e","length","forEach","language","has","console","warn","requireLanguage","NEW_LINE_EXP","lineNumbersWrapper","hooks","env","match","code","linesNum","lines","Array","join","files","metalsmith","done","setImmediate","Object","keys","file","contents","toString","$","load","decodeEntities","highlighted","each","$this","className","attr","targets","split","addLineNumbers","$pre","parent","addClass","html","he","highlightedCode","highlight","Buffer","from","error"],"mappings":"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"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { load } from 'cheerio';
|
|
2
|
+
import { extname } from 'path';
|
|
3
|
+
import Prism from 'prismjs';
|
|
4
|
+
import loadLanguages from 'prismjs/components/index.js';
|
|
5
|
+
import he from 'he';
|
|
6
|
+
import debugLib from 'debug';
|
|
7
|
+
|
|
8
|
+
const debug = debugLib('metalsmith-prism');
|
|
9
|
+
|
|
10
|
+
// Import languages from Prism's default export
|
|
11
|
+
const {
|
|
12
|
+
languages
|
|
13
|
+
} = Prism;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if a file is HTML based on its extension
|
|
17
|
+
* @param {string} filePath - Path to the file
|
|
18
|
+
* @returns {boolean} - True if the file has an HTML extension
|
|
19
|
+
*/
|
|
20
|
+
const isHTMLFile = filePath => {
|
|
21
|
+
const extension = extname(filePath).toLowerCase();
|
|
22
|
+
return extension === '.html' || extension === '.htm';
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef Options
|
|
27
|
+
* @property {boolean} [decode=false] - Whether to decode HTML entities
|
|
28
|
+
* @property {boolean} [lineNumbers=false] - Whether to add line numbers
|
|
29
|
+
* @property {string[]} [preLoad=[]] - Languages to preload
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Metalsmith plugin to highlight code syntax with PrismJS
|
|
34
|
+
*
|
|
35
|
+
* This plugin finds all code blocks in HTML files that have language-* classes
|
|
36
|
+
* and applies Prism.js syntax highlighting to them. It can also add line numbers
|
|
37
|
+
* and handle HTML entity decoding.
|
|
38
|
+
*
|
|
39
|
+
* @param {Options} [options] - Configuration options
|
|
40
|
+
* @param {boolean} [options.decode=false] - Whether to decode HTML entities in code blocks
|
|
41
|
+
* @param {boolean} [options.lineNumbers=false] - Whether to add line numbers to code blocks
|
|
42
|
+
* @param {string[]} [options.preLoad=[]] - Languages to preload before processing
|
|
43
|
+
* @returns {import('metalsmith').Plugin} - A metalsmith plugin function
|
|
44
|
+
* @example
|
|
45
|
+
* // Basic usage
|
|
46
|
+
* metalsmith.use(prism());
|
|
47
|
+
*
|
|
48
|
+
* // With options
|
|
49
|
+
* metalsmith.use(prism({
|
|
50
|
+
* decode: true,
|
|
51
|
+
* lineNumbers: true,
|
|
52
|
+
* preLoad: ['java', 'scala']
|
|
53
|
+
* }));
|
|
54
|
+
*/
|
|
55
|
+
function metalsmithPrism(options = {}) {
|
|
56
|
+
// Track loaded languages to avoid duplicate loading
|
|
57
|
+
const loadedLanguages = new Set();
|
|
58
|
+
|
|
59
|
+
// Always load PHP by default
|
|
60
|
+
debug('Loading PHP by default');
|
|
61
|
+
try {
|
|
62
|
+
loadLanguages(['php']);
|
|
63
|
+
loadedLanguages.add('php');
|
|
64
|
+
} catch (e) {
|
|
65
|
+
debug('Failed to load PHP:', e);
|
|
66
|
+
}
|
|
67
|
+
if (options.preLoad) {
|
|
68
|
+
debug('Preloading languages:', options.preLoad);
|
|
69
|
+
options.preLoad.forEach(language => {
|
|
70
|
+
if (!loadedLanguages.has(language)) {
|
|
71
|
+
try {
|
|
72
|
+
loadLanguages([language]);
|
|
73
|
+
loadedLanguages.add(language);
|
|
74
|
+
debug(`Successfully preloaded language: ${language}`);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
console.warn(`Failed to preload prism syntax: ${language}!`, e);
|
|
77
|
+
debug(`Error preloading language ${language}:`, e);
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
debug(`Language ${language} already loaded, skipping`);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Require optional language package
|
|
87
|
+
* @param {string} language
|
|
88
|
+
*/
|
|
89
|
+
function requireLanguage(language) {
|
|
90
|
+
if (!loadedLanguages.has(language) && !languages[language]) {
|
|
91
|
+
debug(`Loading language on-demand: ${language}`);
|
|
92
|
+
try {
|
|
93
|
+
loadLanguages([language]);
|
|
94
|
+
loadedLanguages.add(language);
|
|
95
|
+
debug(`Successfully loaded language: ${language}`);
|
|
96
|
+
} catch (e) {
|
|
97
|
+
console.warn(`Failed to load prism syntax: ${language}!`, e);
|
|
98
|
+
debug(`Error loading language ${language}:`, e);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Set up line numbers functionality
|
|
104
|
+
const NEW_LINE_EXP = /\n(?!$)/g;
|
|
105
|
+
let lineNumbersWrapper;
|
|
106
|
+
|
|
107
|
+
// Only set up the hook if line numbers are requested
|
|
108
|
+
if (options.lineNumbers) {
|
|
109
|
+
debug('Setting up line numbers hook');
|
|
110
|
+
Prism.hooks.add('after-tokenize', env => {
|
|
111
|
+
const match = env.code.match(NEW_LINE_EXP);
|
|
112
|
+
const linesNum = match ? match.length + 1 : 1;
|
|
113
|
+
debug(`Counted ${linesNum} lines for line numbers`);
|
|
114
|
+
const lines = new Array(linesNum + 1).join('<span></span>');
|
|
115
|
+
lineNumbersWrapper = `<span aria-hidden="true" class="line-numbers-rows">${lines}</span>`;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return function (files, metalsmith, done) {
|
|
119
|
+
debug('Starting metalsmith-prism plugin');
|
|
120
|
+
debug('Options:', options);
|
|
121
|
+
setImmediate(done);
|
|
122
|
+
Object.keys(files).forEach(file => {
|
|
123
|
+
if (!isHTMLFile(file)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
debug(`Processing HTML file: ${file}`);
|
|
127
|
+
const contents = files[file].contents.toString();
|
|
128
|
+
const $ = load(contents, {
|
|
129
|
+
decodeEntities: false
|
|
130
|
+
});
|
|
131
|
+
let highlighted = false;
|
|
132
|
+
const code = $('code');
|
|
133
|
+
if (!code.length) {
|
|
134
|
+
debug(`No code blocks found in ${file}`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
debug(`Found ${code.length} code blocks in ${file}`);
|
|
138
|
+
code.each(function () {
|
|
139
|
+
const $this = $(this);
|
|
140
|
+
const className = $this.attr('class') || '';
|
|
141
|
+
const targets = className.split('language-');
|
|
142
|
+
let addLineNumbers = false;
|
|
143
|
+
if (targets.length > 1) {
|
|
144
|
+
const $pre = $this.parent('pre');
|
|
145
|
+
if ($pre) {
|
|
146
|
+
// Copy className to <pre> container
|
|
147
|
+
$pre.addClass(className);
|
|
148
|
+
if (options.lineNumbers) {
|
|
149
|
+
$pre.addClass('line-numbers');
|
|
150
|
+
addLineNumbers = true;
|
|
151
|
+
debug('Adding line numbers');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
highlighted = true;
|
|
155
|
+
let language = targets[1];
|
|
156
|
+
debug(`Detected language: ${language}`);
|
|
157
|
+
requireLanguage(language);
|
|
158
|
+
if (!languages[language]) {
|
|
159
|
+
debug(`Language ${language} not available, falling back to markup`);
|
|
160
|
+
language = 'markup';
|
|
161
|
+
}
|
|
162
|
+
const html = language === 'markup' && !options.decode ? $this.html() : he.decode($this.html());
|
|
163
|
+
debug(`HTML decoding ${options.decode ? 'applied' : 'not applied'} for language ${language}`);
|
|
164
|
+
debug(`Highlighting code with language: ${language}`);
|
|
165
|
+
const highlightedCode = Prism.highlight(html, languages[language]);
|
|
166
|
+
$this.html(addLineNumbers ? highlightedCode + lineNumbersWrapper : highlightedCode);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
if (highlighted) {
|
|
170
|
+
debug(`Updating contents of ${file} with highlighted code`);
|
|
171
|
+
files[file].contents = Buffer.from($.html());
|
|
172
|
+
} else {
|
|
173
|
+
debug(`No code was highlighted in ${file}`);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
debug('Completed metalsmith-prism plugin');
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// CommonJS export compatibility
|
|
181
|
+
if (typeof module !== 'undefined') {
|
|
182
|
+
module.exports = metalsmithPrism;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export { metalsmithPrism as default };
|
|
186
|
+
//# sourceMappingURL=index.modern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.modern.js","sources":["../src/index.js"],"sourcesContent":["import { load } from 'cheerio';\nimport { extname } from 'path';\nimport Prism from 'prismjs';\nimport loadLanguages from 'prismjs/components/index.js';\nimport he from 'he';\nimport debugLib from 'debug';\n\nconst debug = debugLib('metalsmith-prism');\n\n// Import languages from Prism's default export\nconst { languages } = Prism;\n\n/**\n * Check if a file is HTML based on its extension\n * @param {string} filePath - Path to the file\n * @returns {boolean} - True if the file has an HTML extension\n */\nconst isHTMLFile = (filePath) => {\n const extension = extname(filePath).toLowerCase();\n return extension === '.html' || extension === '.htm';\n};\n\n/**\n * @typedef Options\n * @property {boolean} [decode=false] - Whether to decode HTML entities\n * @property {boolean} [lineNumbers=false] - Whether to add line numbers\n * @property {string[]} [preLoad=[]] - Languages to preload\n */\n\n/**\n * Metalsmith plugin to highlight code syntax with PrismJS\n *\n * This plugin finds all code blocks in HTML files that have language-* classes\n * and applies Prism.js syntax highlighting to them. It can also add line numbers\n * and handle HTML entity decoding.\n *\n * @param {Options} [options] - Configuration options\n * @param {boolean} [options.decode=false] - Whether to decode HTML entities in code blocks\n * @param {boolean} [options.lineNumbers=false] - Whether to add line numbers to code blocks\n * @param {string[]} [options.preLoad=[]] - Languages to preload before processing\n * @returns {import('metalsmith').Plugin} - A metalsmith plugin function\n * @example\n * // Basic usage\n * metalsmith.use(prism());\n *\n * // With options\n * metalsmith.use(prism({\n * decode: true,\n * lineNumbers: true,\n * preLoad: ['java', 'scala']\n * }));\n */\nfunction metalsmithPrism(options = {}) {\n // Track loaded languages to avoid duplicate loading\n const loadedLanguages = new Set();\n\n // Always load PHP by default\n debug('Loading PHP by default');\n try {\n loadLanguages(['php']);\n loadedLanguages.add('php');\n } catch (e) {\n debug('Failed to load PHP:', e);\n }\n\n if (options.preLoad) {\n debug('Preloading languages:', options.preLoad);\n options.preLoad.forEach((language) => {\n if (!loadedLanguages.has(language)) {\n try {\n loadLanguages([language]);\n loadedLanguages.add(language);\n debug(`Successfully preloaded language: ${language}`);\n } catch (e) {\n console.warn(`Failed to preload prism syntax: ${language}!`, e);\n debug(`Error preloading language ${language}:`, e);\n }\n } else {\n debug(`Language ${language} already loaded, skipping`);\n }\n });\n }\n\n /**\n * Require optional language package\n * @param {string} language\n */\n function requireLanguage(language) {\n if (!loadedLanguages.has(language) && !languages[language]) {\n debug(`Loading language on-demand: ${language}`);\n try {\n loadLanguages([language]);\n loadedLanguages.add(language);\n debug(`Successfully loaded language: ${language}`);\n } catch (e) {\n console.warn(`Failed to load prism syntax: ${language}!`, e);\n debug(`Error loading language ${language}:`, e);\n }\n }\n }\n\n // Set up line numbers functionality\n const NEW_LINE_EXP = /\\n(?!$)/g;\n let lineNumbersWrapper;\n\n // Only set up the hook if line numbers are requested\n if (options.lineNumbers) {\n debug('Setting up line numbers hook');\n Prism.hooks.add('after-tokenize', (env) => {\n const match = env.code.match(NEW_LINE_EXP);\n const linesNum = match ? match.length + 1 : 1;\n debug(`Counted ${linesNum} lines for line numbers`);\n const lines = new Array(linesNum + 1).join('<span></span>');\n lineNumbersWrapper = `<span aria-hidden=\"true\" class=\"line-numbers-rows\">${lines}</span>`;\n });\n }\n\n return function (files, metalsmith, done) {\n debug('Starting metalsmith-prism plugin');\n debug('Options:', options);\n\n setImmediate(done);\n\n Object.keys(files).forEach((file) => {\n if (!isHTMLFile(file)) {\n return;\n }\n\n debug(`Processing HTML file: ${file}`);\n const contents = files[file].contents.toString();\n const $ = load(contents, { decodeEntities: false });\n let highlighted = false;\n const code = $('code');\n\n if (!code.length) {\n debug(`No code blocks found in ${file}`);\n return;\n }\n\n debug(`Found ${code.length} code blocks in ${file}`);\n\n code.each(function () {\n const $this = $(this);\n\n const className = $this.attr('class') || '';\n const targets = className.split('language-');\n let addLineNumbers = false;\n\n if (targets.length > 1) {\n const $pre = $this.parent('pre');\n\n if ($pre) {\n // Copy className to <pre> container\n $pre.addClass(className);\n\n if (options.lineNumbers) {\n $pre.addClass('line-numbers');\n addLineNumbers = true;\n debug('Adding line numbers');\n }\n }\n\n highlighted = true;\n let language = targets[1];\n debug(`Detected language: ${language}`);\n requireLanguage(language);\n\n if (!languages[language]) {\n debug(`Language ${language} not available, falling back to markup`);\n language = 'markup';\n }\n\n const html = language === 'markup' && !options.decode ? $this.html() : he.decode($this.html());\n debug(`HTML decoding ${options.decode ? 'applied' : 'not applied'} for language ${language}`);\n\n debug(`Highlighting code with language: ${language}`);\n const highlightedCode = Prism.highlight(html, languages[language]);\n $this.html(addLineNumbers ? highlightedCode + lineNumbersWrapper : highlightedCode);\n }\n });\n\n if (highlighted) {\n debug(`Updating contents of ${file} with highlighted code`);\n files[file].contents = Buffer.from($.html());\n } else {\n debug(`No code was highlighted in ${file}`);\n }\n });\n\n debug('Completed metalsmith-prism plugin');\n };\n}\n\n// ESM export\nexport { metalsmithPrism as default };\n\n// CommonJS export compatibility\nif (typeof module !== 'undefined') {\n module.exports = metalsmithPrism;\n}\n"],"names":["debug","debugLib","languages","Prism","isHTMLFile","filePath","extension","extname","toLowerCase","metalsmithPrism","options","loadedLanguages","Set","loadLanguages","add","e","preLoad","forEach","language","has","console","warn","requireLanguage","NEW_LINE_EXP","lineNumbersWrapper","lineNumbers","hooks","env","match","code","linesNum","length","lines","Array","join","files","metalsmith","done","setImmediate","Object","keys","file","contents","toString","$","load","decodeEntities","highlighted","each","$this","className","attr","targets","split","addLineNumbers","$pre","parent","addClass","html","decode","he","highlightedCode","highlight","Buffer","from","module","exports"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "metalsmith-prism",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.4",
|
|
4
4
|
"description": "Syntax highlighting for Metalsmith HTML templates using Prism.js",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "lib/index.
|
|
7
|
-
"
|
|
6
|
+
"main": "./lib/index.cjs",
|
|
7
|
+
"module": "./lib/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
"import": "./lib/index.js",
|
|
10
|
+
"require": "./lib/index.cjs",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
8
13
|
"engines": {
|
|
9
14
|
"node": ">= 18.0.0"
|
|
10
15
|
},
|
|
11
16
|
"scripts": {
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
17
|
+
"build": "microbundle --entry src/index.js --output lib/index.js --target node -f esm,cjs --strict --generateTypes=false",
|
|
18
|
+
"changelog": "auto-changelog -u --commit-limit false --ignore-commit-pattern '^((dev|chore|ci):|Release)'",
|
|
19
|
+
"coverage": "npm test && c8 report --reporter=text-lcov > ./coverage.info",
|
|
20
|
+
"format": "prettier --write \"**/*.{yml,md,json}\"",
|
|
21
|
+
"format:check": "prettier --list-different \"**/*.{yml,md,js,json}\"",
|
|
22
|
+
"lint": "eslint --fix .",
|
|
23
|
+
"lint:check": "eslint --fix-dry-run .",
|
|
24
|
+
"format-and-lint": "npm run format && npm run lint",
|
|
25
|
+
"prepublishOnly": "npm run build",
|
|
26
|
+
"update-coverage": "node scripts/update-coverage-badge.js",
|
|
27
|
+
"prerelease": "npm run update-coverage && git add README.md && git commit -m \"Update coverage badge in README\" || true",
|
|
28
|
+
"release": "npm run build && GITHUB_TOKEN=$(grep GITHUB_TOKEN .env | cut -d '=' -f2) ./node_modules/.bin/release-it . ",
|
|
29
|
+
"release:check": "npm run lint:check && npm run build && GITHUB_TOKEN=$(grep GITHUB_TOKEN .env | cut -d '=' -f2) ./node_modules/.bin/release-it . --dry-run",
|
|
30
|
+
"test": "c8 --include=src/**/*.js mocha 'test/index.js' 'test/cjs.test.cjs' 'test/comprehensive.mjs' 'test/additional.mjs' -t 15000",
|
|
31
|
+
"test:esm": "c8 --include=src/**/*.js mocha 'test/index.js' 'test/comprehensive.mjs' 'test/additional.mjs' -t 15000",
|
|
32
|
+
"test:cjs": "c8 --include=src/**/*.js mocha test/cjs.test.cjs -t 15000",
|
|
33
|
+
"test:e2e": "serve -l 3000 test/fixtures",
|
|
34
|
+
"depcheck": "depcheck"
|
|
15
35
|
},
|
|
16
36
|
"repository": {
|
|
17
37
|
"type": "git",
|
|
@@ -24,6 +44,7 @@
|
|
|
24
44
|
"highlighting"
|
|
25
45
|
],
|
|
26
46
|
"files": [
|
|
47
|
+
"src",
|
|
27
48
|
"lib",
|
|
28
49
|
"README.md"
|
|
29
50
|
],
|
|
@@ -35,17 +56,23 @@
|
|
|
35
56
|
"homepage": "https://github.com/wernerglinka/metalsmith-prism",
|
|
36
57
|
"dependencies": {
|
|
37
58
|
"cheerio": "^1.0.0",
|
|
38
|
-
"debug": "^4.
|
|
59
|
+
"debug": "^4.4.0",
|
|
60
|
+
"dotenv": "^16.5.0",
|
|
39
61
|
"he": "^1.2.0",
|
|
40
62
|
"metalsmith": "^2.6.3",
|
|
41
|
-
"prismjs": "^1.
|
|
63
|
+
"prismjs": "^1.30.0"
|
|
42
64
|
},
|
|
43
65
|
"devDependencies": {
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"eslint
|
|
48
|
-
"
|
|
49
|
-
"prettier": "^
|
|
66
|
+
"auto-changelog": "^2.5.0",
|
|
67
|
+
"c8": "^10.1.3",
|
|
68
|
+
"chai": "^5.2.0",
|
|
69
|
+
"eslint": "^9.25.1",
|
|
70
|
+
"eslint-config-prettier": "^10.1.2",
|
|
71
|
+
"eslint-plugin-prettier": "^5.2.6",
|
|
72
|
+
"globals": "^16.0.0",
|
|
73
|
+
"microbundle": "^0.15.1",
|
|
74
|
+
"mocha": "^11.1.0",
|
|
75
|
+
"prettier": "^3.5.3",
|
|
76
|
+
"release-it": "^19.0.1"
|
|
50
77
|
}
|
|
51
78
|
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { load } from 'cheerio';
|
|
2
|
+
import { extname } from 'path';
|
|
3
|
+
import Prism from 'prismjs';
|
|
4
|
+
import loadLanguages from 'prismjs/components/index.js';
|
|
5
|
+
import he from 'he';
|
|
6
|
+
import debugLib from 'debug';
|
|
7
|
+
|
|
8
|
+
const debug = debugLib( 'metalsmith-prism' );
|
|
9
|
+
|
|
10
|
+
// Import languages from Prism's default export
|
|
11
|
+
const { languages } = Prism;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if a file is HTML based on its extension
|
|
15
|
+
* @param {string} filePath - Path to the file
|
|
16
|
+
* @returns {boolean} - True if the file has an HTML extension
|
|
17
|
+
*/
|
|
18
|
+
const isHTMLFile = ( filePath ) => {
|
|
19
|
+
const extension = extname( filePath ).toLowerCase();
|
|
20
|
+
return [ '.html', '.htm' ].includes( extension );
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef Options
|
|
25
|
+
* @property {boolean} [decode=false] - Whether to decode HTML entities
|
|
26
|
+
* @property {boolean} [lineNumbers=false] - Whether to add line numbers
|
|
27
|
+
* @property {string[]} [preLoad=[]] - Languages to preload
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Metalsmith plugin to highlight code syntax with PrismJS
|
|
32
|
+
*
|
|
33
|
+
* This plugin finds all code blocks in HTML files that have language-* classes
|
|
34
|
+
* and applies Prism.js syntax highlighting to them. It can also add line numbers
|
|
35
|
+
* and handle HTML entity decoding.
|
|
36
|
+
*
|
|
37
|
+
* @param {Options} [options] - Configuration options
|
|
38
|
+
* @param {boolean} [options.decode=false] - Whether to decode HTML entities in code blocks
|
|
39
|
+
* @param {boolean} [options.lineNumbers=false] - Whether to add line numbers to code blocks
|
|
40
|
+
* @param {string[]} [options.preLoad=[]] - Languages to preload before processing
|
|
41
|
+
* @returns {import('metalsmith').Plugin} - A metalsmith plugin function
|
|
42
|
+
* @example
|
|
43
|
+
* // Basic usage
|
|
44
|
+
* metalsmith.use(prism());
|
|
45
|
+
*
|
|
46
|
+
* // With options
|
|
47
|
+
* metalsmith.use(prism({
|
|
48
|
+
* decode: true,
|
|
49
|
+
* lineNumbers: true,
|
|
50
|
+
* preLoad: ['java', 'scala']
|
|
51
|
+
* }));
|
|
52
|
+
*/
|
|
53
|
+
const metalsmithPrism = ( options = {} ) => {
|
|
54
|
+
// Create a new options object with defaults
|
|
55
|
+
const opts = {
|
|
56
|
+
decode: false,
|
|
57
|
+
lineNumbers: false,
|
|
58
|
+
preLoad: [],
|
|
59
|
+
...options
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Track loaded languages to avoid duplicate loading
|
|
63
|
+
const loadedLanguages = new Set();
|
|
64
|
+
|
|
65
|
+
// Always load PHP by default
|
|
66
|
+
debug( 'Loading PHP by default' );
|
|
67
|
+
try {
|
|
68
|
+
loadLanguages( [ 'php' ] );
|
|
69
|
+
loadedLanguages.add( 'php' );
|
|
70
|
+
} catch ( e ) {
|
|
71
|
+
debug( 'Failed to load PHP:', e );
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if ( opts.preLoad && opts.preLoad.length ) {
|
|
75
|
+
debug( 'Preloading languages:', opts.preLoad );
|
|
76
|
+
opts.preLoad.forEach( ( language ) => {
|
|
77
|
+
if ( !loadedLanguages.has( language ) ) {
|
|
78
|
+
try {
|
|
79
|
+
loadLanguages( [ language ] );
|
|
80
|
+
loadedLanguages.add( language );
|
|
81
|
+
debug( `Successfully preloaded language: ${ language }` );
|
|
82
|
+
} catch ( e ) {
|
|
83
|
+
console.warn( `Failed to preload prism syntax: ${ language }!`, e );
|
|
84
|
+
debug( `Error preloading language ${ language }:`, e );
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
debug( `Language ${ language } already loaded, skipping` );
|
|
88
|
+
}
|
|
89
|
+
} );
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Require optional language package
|
|
94
|
+
* @param {string} language
|
|
95
|
+
* @param {Set} loadedLanguages
|
|
96
|
+
*/
|
|
97
|
+
const requireLanguage = ( language, loadedLanguages ) => {
|
|
98
|
+
if ( loadedLanguages.has( language ) || languages[ language ] ) {
|
|
99
|
+
debug( `Language ${ language } already available, skipping load` );
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
debug( `Loading language on-demand: ${ language }` );
|
|
104
|
+
try {
|
|
105
|
+
loadLanguages( [ language ] );
|
|
106
|
+
loadedLanguages.add( language );
|
|
107
|
+
debug( `Successfully loaded language: ${ language }` );
|
|
108
|
+
} catch ( e ) {
|
|
109
|
+
console.warn( `Failed to load prism syntax: ${ language }!`, e );
|
|
110
|
+
debug( `Error loading language ${ language }:`, e );
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Set up line numbers functionality
|
|
115
|
+
const NEW_LINE_EXP = /\n(?!$)/g;
|
|
116
|
+
let lineNumbersWrapper;
|
|
117
|
+
|
|
118
|
+
// Only set up the hook if line numbers are requested
|
|
119
|
+
if ( opts.lineNumbers ) {
|
|
120
|
+
debug( 'Setting up line numbers hook' );
|
|
121
|
+
Prism.hooks.add( 'after-tokenize', ( env ) => {
|
|
122
|
+
const match = env.code.match( NEW_LINE_EXP );
|
|
123
|
+
const linesNum = match ? match.length + 1 : 1;
|
|
124
|
+
debug( `Counted ${ linesNum } lines for line numbers` );
|
|
125
|
+
const lines = new Array( linesNum + 1 ).join( '<span></span>' );
|
|
126
|
+
lineNumbersWrapper = `<span aria-hidden="true" class="line-numbers-rows">${ lines }</span>`;
|
|
127
|
+
} );
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return ( files, metalsmith, done ) => {
|
|
131
|
+
debug( 'Starting metalsmith-prism plugin' );
|
|
132
|
+
debug( 'Options:', opts );
|
|
133
|
+
|
|
134
|
+
// Call done asynchronously to avoid blocking
|
|
135
|
+
setImmediate( done );
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
Object.keys( files ).forEach( ( file ) => {
|
|
139
|
+
if ( !isHTMLFile( file ) ) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
debug( `Processing HTML file: ${ file }` );
|
|
144
|
+
const contents = files[ file ].contents.toString();
|
|
145
|
+
const $ = load( contents, { decodeEntities: false } );
|
|
146
|
+
let highlighted = false;
|
|
147
|
+
const code = $( 'code' );
|
|
148
|
+
|
|
149
|
+
if ( !code.length ) {
|
|
150
|
+
debug( `No code blocks found in ${ file }` );
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
debug( `Found ${ code.length } code blocks in ${ file }` );
|
|
155
|
+
|
|
156
|
+
code.each( function() {
|
|
157
|
+
const $this = $( this );
|
|
158
|
+
|
|
159
|
+
const className = $this.attr( 'class' ) || '';
|
|
160
|
+
const targets = className.split( 'language-' );
|
|
161
|
+
let addLineNumbers = false;
|
|
162
|
+
|
|
163
|
+
if ( targets.length > 1 ) {
|
|
164
|
+
const $pre = $this.parent( 'pre' );
|
|
165
|
+
|
|
166
|
+
if ( $pre ) {
|
|
167
|
+
// Copy className to <pre> container
|
|
168
|
+
$pre.addClass( className );
|
|
169
|
+
|
|
170
|
+
if ( opts.lineNumbers ) {
|
|
171
|
+
$pre.addClass( 'line-numbers' );
|
|
172
|
+
addLineNumbers = true;
|
|
173
|
+
debug( 'Adding line numbers' );
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
highlighted = true;
|
|
178
|
+
let language = targets[ 1 ];
|
|
179
|
+
debug( `Detected language: ${ language }` );
|
|
180
|
+
requireLanguage( language, loadedLanguages );
|
|
181
|
+
|
|
182
|
+
if ( !languages[ language ] ) {
|
|
183
|
+
debug( `Language ${ language } not available, falling back to markup` );
|
|
184
|
+
language = 'markup';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const html = language === 'markup' && !opts.decode ? $this.html() : he.decode( $this.html() );
|
|
188
|
+
debug( `HTML decoding ${ opts.decode ? 'applied' : 'not applied' } for language ${ language }` );
|
|
189
|
+
|
|
190
|
+
debug( `Highlighting code with language: ${ language }` );
|
|
191
|
+
const highlightedCode = Prism.highlight( html, languages[ language ] );
|
|
192
|
+
$this.html( addLineNumbers ? highlightedCode + lineNumbersWrapper : highlightedCode );
|
|
193
|
+
}
|
|
194
|
+
} );
|
|
195
|
+
|
|
196
|
+
if ( highlighted ) {
|
|
197
|
+
debug( `Updating contents of ${ file } with highlighted code` );
|
|
198
|
+
files[ file ].contents = Buffer.from( $.html() );
|
|
199
|
+
} else {
|
|
200
|
+
debug( `No code was highlighted in ${ file }` );
|
|
201
|
+
}
|
|
202
|
+
} );
|
|
203
|
+
|
|
204
|
+
debug( 'Completed metalsmith-prism plugin' );
|
|
205
|
+
} catch ( error ) {
|
|
206
|
+
debug( 'Error in metalsmith-prism plugin:', error );
|
|
207
|
+
// We can't call done(error) here because done has already been called
|
|
208
|
+
console.error( 'Error processing files:', error );
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// ESM export
|
|
214
|
+
export default metalsmithPrism;
|