Skip to content

Commit 87e43f1

Browse files
authored
feat: support prism langs dependencies import validation (#2489)
As per the Prism for highlight requires the strict dependencies import order for languages. When user add multi highlight support for much langs, it may put in wrong order. A validation to make user aware the order for each langs' dependencies is in demand.
1 parent 5f80683 commit 87e43f1

File tree

2 files changed

+301
-0
lines changed

2 files changed

+301
-0
lines changed

src/core/render/compiler/code.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import Prism from 'prismjs';
22
// See https://github.com/PrismJS/prism/pull/1367
33
import 'prismjs/components/prism-markup-templating.js';
4+
import checkLangDependenciesAllLoaded from '../../util/prism.js';
45

56
export const highlightCodeCompiler = ({ renderer }) =>
67
(renderer.code = function ({ text, lang = 'markup' }) {
8+
checkLangDependenciesAllLoaded(lang);
79
const langOrMarkup = Prism.languages[lang] || Prism.languages.markup;
810
const code = Prism.highlight(
911
text.replace(/@DOCSIFY_QM@/g, '`'),

src/core/util/prism.js

+299
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
import Prism from 'prismjs';
2+
/**
3+
*
4+
* The dependencies map which syncs from
5+
* https://github.com/PrismJS/prism/blob/master/plugins/autoloader/prism-autoloader.js
6+
*
7+
*/
8+
const lang_dependencies = {
9+
javascript: 'clike',
10+
actionscript: 'javascript',
11+
apex: ['clike', 'sql'],
12+
arduino: 'cpp',
13+
aspnet: ['markup', 'csharp'],
14+
birb: 'clike',
15+
bison: 'c',
16+
c: 'clike',
17+
csharp: 'clike',
18+
cpp: 'c',
19+
cfscript: 'clike',
20+
chaiscript: ['clike', 'cpp'],
21+
cilkc: 'c',
22+
cilkcpp: 'cpp',
23+
coffeescript: 'javascript',
24+
crystal: 'ruby',
25+
'css-extras': 'css',
26+
d: 'clike',
27+
dart: 'clike',
28+
django: 'markup-templating',
29+
ejs: ['javascript', 'markup-templating'],
30+
etlua: ['lua', 'markup-templating'],
31+
erb: ['ruby', 'markup-templating'],
32+
fsharp: 'clike',
33+
'firestore-security-rules': 'clike',
34+
flow: 'javascript',
35+
ftl: 'markup-templating',
36+
gml: 'clike',
37+
glsl: 'c',
38+
go: 'clike',
39+
gradle: 'clike',
40+
groovy: 'clike',
41+
haml: 'ruby',
42+
handlebars: 'markup-templating',
43+
haxe: 'clike',
44+
hlsl: 'c',
45+
idris: 'haskell',
46+
java: 'clike',
47+
javadoc: ['markup', 'java', 'javadoclike'],
48+
jolie: 'clike',
49+
jsdoc: ['javascript', 'javadoclike', 'typescript'],
50+
'js-extras': 'javascript',
51+
json5: 'json',
52+
jsonp: 'json',
53+
'js-templates': 'javascript',
54+
kotlin: 'clike',
55+
latte: ['clike', 'markup-templating', 'php'],
56+
less: 'css',
57+
lilypond: 'scheme',
58+
liquid: 'markup-templating',
59+
markdown: 'markup',
60+
'markup-templating': 'markup',
61+
mongodb: 'javascript',
62+
n4js: 'javascript',
63+
objectivec: 'c',
64+
opencl: 'c',
65+
parser: 'markup',
66+
php: 'markup-templating',
67+
phpdoc: ['php', 'javadoclike'],
68+
'php-extras': 'php',
69+
plsql: 'sql',
70+
processing: 'clike',
71+
protobuf: 'clike',
72+
pug: ['markup', 'javascript'],
73+
purebasic: 'clike',
74+
purescript: 'haskell',
75+
qsharp: 'clike',
76+
qml: 'javascript',
77+
qore: 'clike',
78+
racket: 'scheme',
79+
cshtml: ['markup', 'csharp'],
80+
jsx: ['markup', 'javascript'],
81+
tsx: ['jsx', 'typescript'],
82+
reason: 'clike',
83+
ruby: 'clike',
84+
sass: 'css',
85+
scss: 'css',
86+
scala: 'java',
87+
'shell-session': 'bash',
88+
smarty: 'markup-templating',
89+
solidity: 'clike',
90+
soy: 'markup-templating',
91+
sparql: 'turtle',
92+
sqf: 'clike',
93+
squirrel: 'clike',
94+
stata: ['mata', 'java', 'python'],
95+
't4-cs': ['t4-templating', 'csharp'],
96+
't4-vb': ['t4-templating', 'vbnet'],
97+
tap: 'yaml',
98+
tt2: ['clike', 'markup-templating'],
99+
textile: 'markup',
100+
twig: 'markup-templating',
101+
typescript: 'javascript',
102+
v: 'clike',
103+
vala: 'clike',
104+
vbnet: 'basic',
105+
velocity: 'markup',
106+
wiki: 'markup',
107+
xeora: 'markup',
108+
'xml-doc': 'markup',
109+
xquery: 'markup',
110+
};
111+
112+
const lang_aliases = {
113+
html: 'markup',
114+
xml: 'markup',
115+
svg: 'markup',
116+
mathml: 'markup',
117+
ssml: 'markup',
118+
atom: 'markup',
119+
rss: 'markup',
120+
js: 'javascript',
121+
g4: 'antlr4',
122+
ino: 'arduino',
123+
'arm-asm': 'armasm',
124+
art: 'arturo',
125+
adoc: 'asciidoc',
126+
avs: 'avisynth',
127+
avdl: 'avro-idl',
128+
gawk: 'awk',
129+
sh: 'bash',
130+
shell: 'bash',
131+
shortcode: 'bbcode',
132+
rbnf: 'bnf',
133+
oscript: 'bsl',
134+
cs: 'csharp',
135+
dotnet: 'csharp',
136+
cfc: 'cfscript',
137+
'cilk-c': 'cilkc',
138+
'cilk-cpp': 'cilkcpp',
139+
cilk: 'cilkcpp',
140+
coffee: 'coffeescript',
141+
conc: 'concurnas',
142+
jinja2: 'django',
143+
'dns-zone': 'dns-zone-file',
144+
dockerfile: 'docker',
145+
gv: 'dot',
146+
eta: 'ejs',
147+
xlsx: 'excel-formula',
148+
xls: 'excel-formula',
149+
gamemakerlanguage: 'gml',
150+
po: 'gettext',
151+
gni: 'gn',
152+
ld: 'linker-script',
153+
'go-mod': 'go-module',
154+
hbs: 'handlebars',
155+
mustache: 'handlebars',
156+
hs: 'haskell',
157+
idr: 'idris',
158+
gitignore: 'ignore',
159+
hgignore: 'ignore',
160+
npmignore: 'ignore',
161+
webmanifest: 'json',
162+
kt: 'kotlin',
163+
kts: 'kotlin',
164+
kum: 'kumir',
165+
tex: 'latex',
166+
context: 'latex',
167+
ly: 'lilypond',
168+
emacs: 'lisp',
169+
elisp: 'lisp',
170+
'emacs-lisp': 'lisp',
171+
md: 'markdown',
172+
moon: 'moonscript',
173+
n4jsd: 'n4js',
174+
nani: 'naniscript',
175+
objc: 'objectivec',
176+
qasm: 'openqasm',
177+
objectpascal: 'pascal',
178+
px: 'pcaxis',
179+
pcode: 'peoplecode',
180+
plantuml: 'plant-uml',
181+
pq: 'powerquery',
182+
mscript: 'powerquery',
183+
pbfasm: 'purebasic',
184+
purs: 'purescript',
185+
py: 'python',
186+
qs: 'qsharp',
187+
rkt: 'racket',
188+
razor: 'cshtml',
189+
rpy: 'renpy',
190+
res: 'rescript',
191+
robot: 'robotframework',
192+
rb: 'ruby',
193+
'sh-session': 'shell-session',
194+
shellsession: 'shell-session',
195+
smlnj: 'sml',
196+
sol: 'solidity',
197+
sln: 'solution-file',
198+
rq: 'sparql',
199+
sclang: 'supercollider',
200+
t4: 't4-cs',
201+
trickle: 'tremor',
202+
troy: 'tremor',
203+
trig: 'turtle',
204+
ts: 'typescript',
205+
tsconfig: 'typoscript',
206+
uscript: 'unrealscript',
207+
uc: 'unrealscript',
208+
url: 'uri',
209+
vb: 'visual-basic',
210+
vba: 'visual-basic',
211+
webidl: 'web-idl',
212+
mathematica: 'wolfram',
213+
nb: 'wolfram',
214+
wl: 'wolfram',
215+
xeoracube: 'xeora',
216+
yml: 'yaml',
217+
};
218+
219+
// The `depTreeCache` is used to cache the dependency tree for each language,
220+
// preventing duplicate calculations and avoiding repeated warning messages.
221+
const depTreeCache = {};
222+
223+
/**
224+
* PrismJs language dependencies required a specific order to load.
225+
* Try to check and print a warning message if some dependencies missing or in wrong order.
226+
* @param {*} lang current lang to check dependencies
227+
*/
228+
export default function checkLangDependenciesAllLoaded(lang) {
229+
if (!lang) {
230+
return;
231+
}
232+
233+
lang = lang_aliases[lang] || lang;
234+
235+
const validLang = lang_dependencies[lang];
236+
if (!validLang) {
237+
return;
238+
}
239+
240+
if (!depTreeCache[lang]) {
241+
/**
242+
* The dummy node constructs the dependency tree as the root
243+
* and maintains the final global loading status (dummy.loaded) for current lang.
244+
*/
245+
const dummy = {
246+
cur: '',
247+
loaded: true,
248+
dependencies: [],
249+
};
250+
251+
buildAndCheckDepTree(lang, dummy, dummy);
252+
253+
const depTree = dummy.dependencies[0];
254+
depTreeCache[lang] = depTree;
255+
256+
if (!dummy.loaded) {
257+
const prettyOutput = prettryPrint(depTree, 1);
258+
// eslint-disable-next-line no-console
259+
console.warn(
260+
`The language '${lang}' required dependencies for code block highlighting are not satisfied.`,
261+
`Priority dependencies from low to high, consider to place all the necessary dependencie by priority (higher first): \n`,
262+
prettyOutput,
263+
);
264+
}
265+
}
266+
}
267+
268+
const buildAndCheckDepTree = (lang, parent, dummy) => {
269+
if (!lang) {
270+
return;
271+
}
272+
const cur = { cur: lang, loaded: true, dependencies: [] };
273+
let deps = lang_dependencies[lang] || [];
274+
275+
if (!(lang in Prism.languages)) {
276+
dummy.loaded = false;
277+
cur.loaded = false;
278+
}
279+
280+
if (typeof deps === 'string') {
281+
deps = [deps];
282+
}
283+
284+
deps.forEach(dep => {
285+
buildAndCheckDepTree(dep, cur, dummy);
286+
});
287+
288+
parent.dependencies.push(cur);
289+
};
290+
291+
const prettryPrint = (depTree, level) => {
292+
let cur = `${' '.repeat(level * 3)} ${depTree.cur} ${depTree.loaded ? '(+)' : '(-)'}`;
293+
if (depTree.dependencies.length) {
294+
depTree.dependencies.forEach(dep => {
295+
cur += prettryPrint(dep, level + 1, cur);
296+
});
297+
}
298+
return '\n' + cur;
299+
};

0 commit comments

Comments
 (0)