From 7a4a0c7cddbf1dcddd0593c84f7be126d4fac7bb Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Mon, 16 Dec 2019 12:49:21 +0100 Subject: [PATCH] Standardized dependency logic implementation (#1998) This adds a standard logic for handling dependencies between Prism components. Right now, the download page, the `loadLanguages` function, and the test suite use the new dependency system. --- components.js | 2 +- components.json | 57 +++-- components/index.js | 107 +++------ dependencies.js | 431 +++++++++++++++++++++++++++++++++++ download.html | 1 + package.json | 3 +- scripts/download.js | 142 ++++++------ tests/dependencies-test.js | 257 +++++++++++++++++++++ tests/helper/prism-loader.js | 57 ++--- 9 files changed, 860 insertions(+), 197 deletions(-) create mode 100644 dependencies.js create mode 100644 tests/dependencies-test.js diff --git a/components.js b/components.js index 39985e8b2e..dd554dcde9 100644 --- a/components.js +++ b/components.js @@ -1,2 +1,2 @@ -var components = {"core":{"meta":{"path":"components/prism-core.js","option":"mandatory"},"core":"Core"},"themes":{"meta":{"path":"themes/{id}.css","link":"index.html?theme={id}","exclusive":true},"prism":{"title":"Default","option":"default"},"prism-dark":"Dark","prism-funky":"Funky","prism-okaidia":{"title":"Okaidia","owner":"ocodia"},"prism-twilight":{"title":"Twilight","owner":"remybach"},"prism-coy":{"title":"Coy","owner":"tshedor"},"prism-solarizedlight":{"title":"Solarized Light","owner":"hectormatos2011 "},"prism-tomorrow":{"title":"Tomorrow Night","owner":"Rosey"}},"languages":{"meta":{"path":"components/prism-{id}","noCSS":true,"examplesPath":"examples/prism-{id}","addCheckAll":true},"markup":{"title":"Markup","alias":["html","xml","svg","mathml"],"aliasTitles":{"html":"HTML","xml":"XML","svg":"SVG","mathml":"MathML"},"option":"default"},"css":{"title":"CSS","option":"default","peerDependencies":"markup"},"clike":{"title":"C-like","option":"default","overrideExampleHeader":true},"javascript":{"title":"JavaScript","require":"clike","peerDependencies":"markup","alias":"js","option":"default"},"abap":{"title":"ABAP","owner":"dellagustin"},"abnf":{"title":"Augmented Backus–Naur form","owner":"RunDevelopment"},"actionscript":{"title":"ActionScript","require":"javascript","peerDependencies":"markup","owner":"Golmote"},"ada":{"title":"Ada","owner":"Lucretia"},"antlr4":{"title":"ANTLR4","alias":"g4","owner":"RunDevelopment"},"apacheconf":{"title":"Apache Configuration","owner":"GuiTeK"},"apl":{"title":"APL","owner":"ngn"},"applescript":{"title":"AppleScript","owner":"Golmote"},"aql":{"title":"AQL","owner":"RunDevelopment"},"arduino":{"title":"Arduino","require":"cpp","owner":"eisbehr-"},"arff":{"title":"ARFF","owner":"Golmote"},"asciidoc":{"alias":"adoc","title":"AsciiDoc","owner":"Golmote"},"asm6502":{"title":"6502 Assembly","owner":"kzurawel"},"aspnet":{"title":"ASP.NET (C#)","require":["markup","csharp"],"owner":"nauzilus"},"autohotkey":{"title":"AutoHotkey","owner":"aviaryan"},"autoit":{"title":"AutoIt","owner":"Golmote"},"bash":{"title":"Bash","alias":"shell","aliasTitles":{"shell":"Shell"},"owner":"zeitgeist87"},"basic":{"title":"BASIC","owner":"Golmote"},"batch":{"title":"Batch","owner":"Golmote"},"bbcode":{"title":"BBcode","owner":"RunDevelopment"},"bison":{"title":"Bison","require":"c","owner":"Golmote"},"bnf":{"title":"Backus–Naur form","alias":"rbnf","aliasTitles":{"rbnf":"Routing Backus–Naur form"},"owner":"RunDevelopment"},"brainfuck":{"title":"Brainfuck","owner":"Golmote"},"brightscript":{"title":"BrightScript","owner":"RunDevelopment"},"bro":{"title":"Bro","owner":"wayward710"},"c":{"title":"C","require":"clike","owner":"zeitgeist87"},"csharp":{"title":"C#","require":"clike","alias":["cs","dotnet"],"owner":"mvalipour"},"cpp":{"title":"C++","require":"c","owner":"zeitgeist87"},"cil":{"title":"CIL","owner":"sbrl"},"coffeescript":{"title":"CoffeeScript","require":"javascript","alias":"coffee","owner":"R-osey"},"cmake":{"title":"CMake","owner":"mjrogozinski"},"clojure":{"title":"Clojure","owner":"troglotit"},"crystal":{"title":"Crystal","require":"ruby","owner":"MakeNowJust"},"csp":{"title":"Content-Security-Policy","owner":"ScottHelme"},"css-extras":{"title":"CSS Extras","require":"css","owner":"milesj"},"d":{"title":"D","require":"clike","owner":"Golmote"},"dart":{"title":"Dart","require":"clike","owner":"Golmote"},"diff":{"title":"Diff","owner":"uranusjr"},"django":{"title":"Django/Jinja2","require":"markup-templating","alias":"jinja2","owner":"romanvm"},"dns-zone-file":{"title":"DNS zone file","owner":"RunDevelopment","alias":"dns-zone"},"docker":{"title":"Docker","alias":"dockerfile","owner":"JustinBeckwith"},"ebnf":{"title":"Extended Backus–Naur form","owner":"RunDevelopment"},"eiffel":{"title":"Eiffel","owner":"Conaclos"},"ejs":{"title":"EJS","require":["javascript","markup-templating"],"owner":"RunDevelopment"},"elixir":{"title":"Elixir","owner":"Golmote"},"elm":{"title":"Elm","owner":"zwilias"},"etlua":{"title":"Embedded Lua templating","require":["lua","markup-templating"],"owner":"RunDevelopment"},"erb":{"title":"ERB","require":["ruby","markup-templating"],"owner":"Golmote"},"erlang":{"title":"Erlang","owner":"Golmote"},"fsharp":{"title":"F#","require":"clike","owner":"simonreynolds7"},"firestore-security-rules":{"title":"Firestore security rules","require":"clike","owner":"RunDevelopment"},"flow":{"title":"Flow","require":"javascript","owner":"Golmote"},"fortran":{"title":"Fortran","owner":"Golmote"},"ftl":{"title":"FreeMarker Template Language","require":"markup-templating","owner":"RunDevelopment"},"gcode":{"title":"G-code","owner":"RunDevelopment"},"gdscript":{"title":"GDScript","owner":"RunDevelopment"},"gedcom":{"title":"GEDCOM","owner":"Golmote"},"gherkin":{"title":"Gherkin","owner":"hason"},"git":{"title":"Git","owner":"lgiraudel"},"glsl":{"title":"GLSL","require":"clike","owner":"Golmote"},"gml":{"title":"GameMaker Language","alias":"gamemakerlanguage","require":"clike","owner":"LiarOnce"},"go":{"title":"Go","require":"clike","owner":"arnehormann"},"graphql":{"title":"GraphQL","owner":"Golmote"},"groovy":{"title":"Groovy","require":"clike","owner":"robfletcher"},"haml":{"title":"Haml","require":"ruby","peerDependencies":["css","css-extras","coffeescript","erb","javascript","less","markdown","ruby","scss","textile"],"owner":"Golmote"},"handlebars":{"title":"Handlebars","require":"markup-templating","owner":"Golmote"},"haskell":{"title":"Haskell","alias":"hs","owner":"bholst"},"haxe":{"title":"Haxe","require":"clike","owner":"Golmote"},"hcl":{"title":"HCL","owner":"outsideris"},"http":{"title":"HTTP","peerDependencies":["javascript","markup"],"owner":"danielgtaylor"},"hpkp":{"title":"HTTP Public-Key-Pins","owner":"ScottHelme"},"hsts":{"title":"HTTP Strict-Transport-Security","owner":"ScottHelme"},"ichigojam":{"title":"IchigoJam","owner":"BlueCocoa"},"icon":{"title":"Icon","owner":"Golmote"},"inform7":{"title":"Inform 7","owner":"Golmote"},"ini":{"title":"Ini","owner":"aviaryan"},"io":{"title":"Io","owner":"AlesTsurko"},"j":{"title":"J","owner":"Golmote"},"java":{"title":"Java","require":"clike","owner":"sherblot"},"javadoc":{"title":"JavaDoc","require":["markup","java","javadoclike"],"peerDependencies":["scala"],"owner":"RunDevelopment"},"javadoclike":{"title":"JavaDoc-like","peerDependencies":["java","javascript","php"],"owner":"RunDevelopment"},"javastacktrace":{"title":"Java stack trace","owner":"RunDevelopment"},"jolie":{"title":"Jolie","require":"clike","owner":"thesave"},"jq":{"title":"JQ","owner":"RunDevelopment"},"jsdoc":{"title":"JSDoc","require":["javascript","javadoclike"],"peerDependencies":["actionscript","coffeescript"],"owner":"RunDevelopment"},"js-extras":{"title":"JS Extras","require":"javascript","peerDependencies":["actionscript","coffeescript","flow","n4js","typescript"],"owner":"RunDevelopment"},"js-templates":{"title":"JS Templates","require":"javascript","peerDependencies":["css","css-extras","graphql","markdown","markup"],"owner":"RunDevelopment"},"json":{"title":"JSON","owner":"CupOfTea696"},"jsonp":{"title":"JSONP","require":"json","owner":"RunDevelopment"},"json5":{"title":"JSON5","require":"json","owner":"RunDevelopment"},"julia":{"title":"Julia","owner":"cdagnino"},"keyman":{"title":"Keyman","owner":"mcdurdin"},"kotlin":{"title":"Kotlin","require":"clike","owner":"Golmote"},"latex":{"title":"LaTeX","alias":["tex","context"],"aliasTitles":{"tex":"TeX","context":"ConTeXt"},"owner":"japborst"},"less":{"title":"Less","require":"css","peerDependencies":"css-extras","owner":"Golmote"},"lilypond":{"title":"LilyPond","require":"scheme","alias":"ly","owner":"RunDevelopment"},"liquid":{"title":"Liquid","owner":"cinhtau"},"lisp":{"title":"Lisp","alias":["emacs","elisp","emacs-lisp"],"owner":"JuanCaicedo"},"livescript":{"title":"LiveScript","owner":"Golmote"},"lolcode":{"title":"LOLCODE","owner":"Golmote"},"lua":{"title":"Lua","owner":"Golmote"},"makefile":{"title":"Makefile","owner":"Golmote"},"markdown":{"title":"Markdown","require":"markup","alias":"md","owner":"Golmote"},"markup-templating":{"title":"Markup templating","require":"markup","owner":"Golmote"},"matlab":{"title":"MATLAB","owner":"Golmote"},"mel":{"title":"MEL","owner":"Golmote"},"mizar":{"title":"Mizar","owner":"Golmote"},"monkey":{"title":"Monkey","owner":"Golmote"},"moonscript":{"title":"MoonScript","alias":"moon","owner":"RunDevelopment"},"n1ql":{"title":"N1QL","owner":"TMWilds"},"n4js":{"title":"N4JS","require":"javascript","peerDependencies":["jsdoc"],"alias":"n4jsd","owner":"bsmith-n4"},"nand2tetris-hdl":{"title":"Nand To Tetris HDL","owner":"stephanmax"},"nasm":{"title":"NASM","owner":"rbmj"},"nginx":{"title":"nginx","owner":"westonganger","require":"clike"},"nim":{"title":"Nim","owner":"Golmote"},"nix":{"title":"Nix","owner":"Golmote"},"nsis":{"title":"NSIS","owner":"idleberg"},"objectivec":{"title":"Objective-C","require":"c","owner":"uranusjr"},"ocaml":{"title":"OCaml","owner":"Golmote"},"opencl":{"title":"OpenCL","require":"cpp","peerDependencies":["c","cpp"],"overrideExampleHeader":true,"owner":"Milania1"},"oz":{"title":"Oz","owner":"Golmote"},"parigp":{"title":"PARI/GP","owner":"Golmote"},"parser":{"title":"Parser","require":"markup","owner":"Golmote"},"pascal":{"title":"Pascal","alias":"objectpascal","aliasTitles":{"objectpascal":"Object Pascal"},"owner":"Golmote"},"pascaligo":{"title":"Pascaligo","owner":"DefinitelyNotAGoat"},"pcaxis":{"title":"PC-Axis","alias":"px","owner":"RunDevelopment"},"perl":{"title":"Perl","owner":"Golmote"},"php":{"title":"PHP","require":["clike","markup-templating"],"owner":"milesj"},"phpdoc":{"title":"PHPDoc","require":["php","javadoclike"],"owner":"RunDevelopment"},"php-extras":{"title":"PHP Extras","require":"php","owner":"milesj"},"plsql":{"title":"PL/SQL","require":"sql","owner":"Golmote"},"powershell":{"title":"PowerShell","owner":"nauzilus"},"processing":{"title":"Processing","require":"clike","owner":"Golmote"},"prolog":{"title":"Prolog","owner":"Golmote"},"properties":{"title":".properties","owner":"Golmote"},"protobuf":{"title":"Protocol Buffers","require":"clike","owner":"just-boris"},"pug":{"title":"Pug","require":["markup","javascript"],"peerDependencies":["coffeescript","ejs","handlebars","less","livescript","markdown","scss","stylus","twig"],"owner":"Golmote"},"puppet":{"title":"Puppet","owner":"Golmote"},"pure":{"title":"Pure","peerDependencies":["c","cpp","fortran"],"owner":"Golmote"},"python":{"title":"Python","alias":"py","owner":"multipetros"},"q":{"title":"Q (kdb+ database)","owner":"Golmote"},"qore":{"title":"Qore","require":"clike","owner":"temnroegg"},"r":{"title":"R","owner":"Golmote"},"jsx":{"title":"React JSX","require":["markup","javascript"],"peerDependencies":["jsdoc","js-extras","js-templates"],"owner":"vkbansal"},"tsx":{"title":"React TSX","require":["jsx","typescript"]},"renpy":{"title":"Ren'py","owner":"HyuchiaDiego"},"reason":{"title":"Reason","require":"clike","owner":"Golmote"},"regex":{"title":"Regex","peerDependencies":["actionscript","coffeescript","flow","javascript","typescript","vala"],"owner":"RunDevelopment"},"rest":{"title":"reST (reStructuredText)","owner":"Golmote"},"rip":{"title":"Rip","owner":"ravinggenius"},"roboconf":{"title":"Roboconf","owner":"Golmote"},"robotframework":{"title":"Robot Framework","alias":"robot","owner":"RunDevelopment"},"ruby":{"title":"Ruby","require":"clike","alias":"rb","owner":"samflores"},"rust":{"title":"Rust","owner":"Golmote"},"sas":{"title":"SAS","peerDependencies":["groovy","lua","sql"],"owner":"Golmote"},"sass":{"title":"Sass (Sass)","require":"css","owner":"Golmote"},"scss":{"title":"Sass (Scss)","require":"css","peerDependencies":"css-extras","owner":"MoOx"},"scala":{"title":"Scala","require":"java","owner":"jozic"},"scheme":{"title":"Scheme","owner":"bacchus123"},"shell-session":{"title":"Shell session","require":"bash","owner":"RunDevelopment"},"smalltalk":{"title":"Smalltalk","owner":"Golmote"},"smarty":{"title":"Smarty","require":"markup-templating","owner":"Golmote"},"solidity":{"title":"Solidity (Ethereum)","require":"clike","owner":"glachaud"},"soy":{"title":"Soy (Closure Template)","require":"markup-templating","owner":"Golmote"},"sparql":{"title":"SPARQL","require":"turtle","owner":"Triply-Dev","alias":"rq"},"splunk-spl":{"title":"Splunk SPL","owner":"RunDevelopment"},"sqf":{"title":"SQF: Status Quo Function (Arma 3)","require":"clike","owner":"RunDevelopment"},"sql":{"title":"SQL","owner":"multipetros"},"stylus":{"title":"Stylus","owner":"vkbansal"},"swift":{"title":"Swift","require":"clike","owner":"chrischares"},"tap":{"title":"TAP","owner":"isaacs","require":"yaml"},"tcl":{"title":"Tcl","owner":"PeterChaplin"},"textile":{"title":"Textile","require":"markup","peerDependencies":"css","owner":"Golmote"},"toml":{"title":"TOML","owner":"RunDevelopment"},"tt2":{"title":"Template Toolkit 2","require":["clike","markup-templating"],"owner":"gflohr"},"turtle":{"title":"Turtle","alias":["trig"],"aliasTitles":{"trig":"TriG"},"owner":"jakubklimek"},"twig":{"title":"Twig","require":"markup","owner":"brandonkelly"},"typescript":{"title":"TypeScript","require":"javascript","peerDependencies":"js-templates","alias":"ts","owner":"vkbansal"},"t4-cs":{"title":"T4 Text Templates (C#)","require":["t4-templating","csharp"],"alias":"t4","owner":"RunDevelopment"},"t4-vb":{"title":"T4 Text Templates (VB)","require":["t4-templating","visual-basic"],"owner":"RunDevelopment"},"t4-templating":{"title":"T4 templating","owner":"RunDevelopment"},"vala":{"title":"Vala","require":"clike","owner":"TemplarVolk"},"vbnet":{"title":"VB.Net","require":"basic","owner":"Bigsby"},"velocity":{"title":"Velocity","require":"markup","owner":"Golmote"},"verilog":{"title":"Verilog","owner":"a-rey"},"vhdl":{"title":"VHDL","owner":"a-rey"},"vim":{"title":"vim","owner":"westonganger"},"visual-basic":{"title":"Visual Basic","alias":"vb","owner":"Golmote"},"wasm":{"title":"WebAssembly","owner":"Golmote"},"wiki":{"title":"Wiki markup","require":"markup","owner":"Golmote"},"xeora":{"title":"Xeora","require":"markup","alias":"xeoracube","aliasTitles":{"xeoracube":"XeoraCube"},"owner":"freakmaxi"},"xojo":{"title":"Xojo (REALbasic)","owner":"Golmote"},"xquery":{"title":"XQuery","require":"markup","owner":"Golmote"},"yaml":{"title":"YAML","alias":"yml","owner":"hason"},"zig":{"title":"Zig","owner":"RunDevelopment"}},"plugins":{"meta":{"path":"plugins/{id}/prism-{id}","link":"plugins/{id}/"},"line-highlight":{"title":"Line Highlight","description":"Highlights specific lines and/or line ranges."},"line-numbers":{"title":"Line Numbers","description":"Line number at the beginning of code lines.","owner":"kuba-kubula"},"show-invisibles":{"title":"Show Invisibles","description":"Show hidden characters such as tabs and line breaks.","after":["autolinker","data-uri-highlight"]},"autolinker":{"title":"Autolinker","description":"Converts URLs and emails in code to clickable links. Parses Markdown links in comments."},"wpd":{"title":"WebPlatform Docs","description":"Makes tokens link to WebPlatform.org documentation. The links open in a new tab."},"custom-class":{"title":"Custom Class","description":"This plugin allows you to prefix Prism's default classes (.comment can become .namespace--comment) or replace them with your defined ones (like .editor__comment). You can even add new classes.","owner":"dvkndn","noCSS":true},"file-highlight":{"title":"File Highlight","description":"Fetch external files and highlight them with Prism. Used on the Prism website itself.","noCSS":true},"show-language":{"title":"Show Language","description":"Display the highlighted language in code blocks (inline code does not show the label).","owner":"nauzilus","noCSS":true,"require":"toolbar"},"jsonp-highlight":{"title":"JSONP Highlight","description":"Fetch content with JSONP and highlight some interesting content (e.g. GitHub/Gists or Bitbucket API).","noCSS":true,"owner":"nauzilus"},"highlight-keywords":{"title":"Highlight Keywords","description":"Adds special CSS classes for each keyword matched in the code. For example, the keyword if will have the class keyword-if as well. You can have fine grained control over the appearance of each keyword by providing your own CSS rules.","owner":"vkbansal","noCSS":true},"remove-initial-line-feed":{"title":"Remove initial line feed","description":"Removes the initial line feed in code blocks.","owner":"Golmote","noCSS":true},"inline-color":{"title":"Inline color","description":"Adds a small inline preview for colors in style sheets.","require":"css-extras","owner":"RunDevelopment"},"previewers":{"title":"Previewers","description":"Previewers for angles, colors, gradients, easing and time.","require":"css-extras","owner":"Golmote"},"autoloader":{"title":"Autoloader","description":"Automatically loads the needed languages to highlight the code blocks.","owner":"Golmote","noCSS":true},"keep-markup":{"title":"Keep Markup","description":"Prevents custom markup from being dropped out during highlighting.","owner":"Golmote","after":"normalize-whitespace","noCSS":true},"command-line":{"title":"Command Line","description":"Display a command line with a prompt and, optionally, the output/response from the commands.","owner":"chriswells0"},"unescaped-markup":{"title":"Unescaped Markup","description":"Write markup without having to escape anything."},"normalize-whitespace":{"title":"Normalize Whitespace","description":"Supports multiple operations to normalize whitespace in code blocks.","owner":"zeitgeist87","after":"unescaped-markup","noCSS":true},"data-uri-highlight":{"title":"Data-URI Highlight","description":"Highlights data-URI contents.","owner":"Golmote","noCSS":true},"toolbar":{"title":"Toolbar","description":"Attach a toolbar for plugins to easily register buttons on the top of a code block.","owner":"mAAdhaTTah"},"copy-to-clipboard":{"title":"Copy to Clipboard Button","description":"Add a button that copies the code block to the clipboard when clicked.","owner":"mAAdhaTTah","require":"toolbar","noCSS":true},"download-button":{"title":"Download Button","description":"A button in the toolbar of a code block adding a convenient way to download a code file.","owner":"Golmote","require":"toolbar","noCSS":true},"match-braces":{"title":"Match braces","description":"Highlights matching braces.","owner":"RunDevelopment"},"diff-highlight":{"title":"Diff Highlight","description":"Highlights the code inside diff blocks.","owner":"RunDevelopment","require":"diff"},"filter-highlight-all":{"title":"Filter highlightAll","description":"Filters the elements the highlightAll and highlightAllUnder methods actually highlight.","owner":"RunDevelopment","noCSS":true}}}; +var components = {"core":{"meta":{"path":"components/prism-core.js","option":"mandatory"},"core":"Core"},"themes":{"meta":{"path":"themes/{id}.css","link":"index.html?theme={id}","exclusive":true},"prism":{"title":"Default","option":"default"},"prism-dark":"Dark","prism-funky":"Funky","prism-okaidia":{"title":"Okaidia","owner":"ocodia"},"prism-twilight":{"title":"Twilight","owner":"remybach"},"prism-coy":{"title":"Coy","owner":"tshedor"},"prism-solarizedlight":{"title":"Solarized Light","owner":"hectormatos2011 "},"prism-tomorrow":{"title":"Tomorrow Night","owner":"Rosey"}},"languages":{"meta":{"path":"components/prism-{id}","noCSS":true,"examplesPath":"examples/prism-{id}","addCheckAll":true},"markup":{"title":"Markup","alias":["html","xml","svg","mathml"],"aliasTitles":{"html":"HTML","xml":"XML","svg":"SVG","mathml":"MathML"},"option":"default"},"css":{"title":"CSS","option":"default","modify":"markup"},"clike":{"title":"C-like","option":"default","overrideExampleHeader":true},"javascript":{"title":"JavaScript","require":"clike","modify":"markup","alias":"js","option":"default"},"abap":{"title":"ABAP","owner":"dellagustin"},"abnf":{"title":"Augmented Backus–Naur form","owner":"RunDevelopment"},"actionscript":{"title":"ActionScript","require":"javascript","modify":"markup","owner":"Golmote"},"ada":{"title":"Ada","owner":"Lucretia"},"antlr4":{"title":"ANTLR4","alias":"g4","owner":"RunDevelopment"},"apacheconf":{"title":"Apache Configuration","owner":"GuiTeK"},"apl":{"title":"APL","owner":"ngn"},"applescript":{"title":"AppleScript","owner":"Golmote"},"aql":{"title":"AQL","owner":"RunDevelopment"},"arduino":{"title":"Arduino","require":"cpp","owner":"eisbehr-"},"arff":{"title":"ARFF","owner":"Golmote"},"asciidoc":{"alias":"adoc","title":"AsciiDoc","owner":"Golmote"},"asm6502":{"title":"6502 Assembly","owner":"kzurawel"},"aspnet":{"title":"ASP.NET (C#)","require":["markup","csharp"],"owner":"nauzilus"},"autohotkey":{"title":"AutoHotkey","owner":"aviaryan"},"autoit":{"title":"AutoIt","owner":"Golmote"},"bash":{"title":"Bash","alias":"shell","aliasTitles":{"shell":"Shell"},"owner":"zeitgeist87"},"basic":{"title":"BASIC","owner":"Golmote"},"batch":{"title":"Batch","owner":"Golmote"},"bbcode":{"title":"BBcode","owner":"RunDevelopment"},"bison":{"title":"Bison","require":"c","owner":"Golmote"},"bnf":{"title":"Backus–Naur form","alias":"rbnf","aliasTitles":{"rbnf":"Routing Backus–Naur form"},"owner":"RunDevelopment"},"brainfuck":{"title":"Brainfuck","owner":"Golmote"},"brightscript":{"title":"BrightScript","owner":"RunDevelopment"},"bro":{"title":"Bro","owner":"wayward710"},"c":{"title":"C","require":"clike","owner":"zeitgeist87"},"csharp":{"title":"C#","require":"clike","alias":["cs","dotnet"],"owner":"mvalipour"},"cpp":{"title":"C++","require":"c","owner":"zeitgeist87"},"cil":{"title":"CIL","owner":"sbrl"},"coffeescript":{"title":"CoffeeScript","require":"javascript","alias":"coffee","owner":"R-osey"},"cmake":{"title":"CMake","owner":"mjrogozinski"},"clojure":{"title":"Clojure","owner":"troglotit"},"crystal":{"title":"Crystal","require":"ruby","owner":"MakeNowJust"},"csp":{"title":"Content-Security-Policy","owner":"ScottHelme"},"css-extras":{"title":"CSS Extras","require":"css","modify":"css","owner":"milesj"},"d":{"title":"D","require":"clike","owner":"Golmote"},"dart":{"title":"Dart","require":"clike","owner":"Golmote"},"diff":{"title":"Diff","owner":"uranusjr"},"django":{"title":"Django/Jinja2","require":"markup-templating","alias":"jinja2","owner":"romanvm"},"dns-zone-file":{"title":"DNS zone file","owner":"RunDevelopment","alias":"dns-zone"},"docker":{"title":"Docker","alias":"dockerfile","owner":"JustinBeckwith"},"ebnf":{"title":"Extended Backus–Naur form","owner":"RunDevelopment"},"eiffel":{"title":"Eiffel","owner":"Conaclos"},"ejs":{"title":"EJS","require":["javascript","markup-templating"],"owner":"RunDevelopment"},"elixir":{"title":"Elixir","owner":"Golmote"},"elm":{"title":"Elm","owner":"zwilias"},"etlua":{"title":"Embedded Lua templating","require":["lua","markup-templating"],"owner":"RunDevelopment"},"erb":{"title":"ERB","require":["ruby","markup-templating"],"owner":"Golmote"},"erlang":{"title":"Erlang","owner":"Golmote"},"fsharp":{"title":"F#","require":"clike","owner":"simonreynolds7"},"firestore-security-rules":{"title":"Firestore security rules","require":"clike","owner":"RunDevelopment"},"flow":{"title":"Flow","require":"javascript","owner":"Golmote"},"fortran":{"title":"Fortran","owner":"Golmote"},"ftl":{"title":"FreeMarker Template Language","require":"markup-templating","owner":"RunDevelopment"},"gcode":{"title":"G-code","owner":"RunDevelopment"},"gdscript":{"title":"GDScript","owner":"RunDevelopment"},"gedcom":{"title":"GEDCOM","owner":"Golmote"},"gherkin":{"title":"Gherkin","owner":"hason"},"git":{"title":"Git","owner":"lgiraudel"},"glsl":{"title":"GLSL","require":"clike","owner":"Golmote"},"gml":{"title":"GameMaker Language","alias":"gamemakerlanguage","require":"clike","owner":"LiarOnce"},"go":{"title":"Go","require":"clike","owner":"arnehormann"},"graphql":{"title":"GraphQL","owner":"Golmote"},"groovy":{"title":"Groovy","require":"clike","owner":"robfletcher"},"haml":{"title":"Haml","require":"ruby","optional":["css","css-extras","coffeescript","erb","javascript","less","markdown","ruby","scss","textile"],"owner":"Golmote"},"handlebars":{"title":"Handlebars","require":"markup-templating","owner":"Golmote"},"haskell":{"title":"Haskell","alias":"hs","owner":"bholst"},"haxe":{"title":"Haxe","require":"clike","owner":"Golmote"},"hcl":{"title":"HCL","owner":"outsideris"},"http":{"title":"HTTP","optional":["css","javascript","json","markup"],"owner":"danielgtaylor"},"hpkp":{"title":"HTTP Public-Key-Pins","owner":"ScottHelme"},"hsts":{"title":"HTTP Strict-Transport-Security","owner":"ScottHelme"},"ichigojam":{"title":"IchigoJam","owner":"BlueCocoa"},"icon":{"title":"Icon","owner":"Golmote"},"inform7":{"title":"Inform 7","owner":"Golmote"},"ini":{"title":"Ini","owner":"aviaryan"},"io":{"title":"Io","owner":"AlesTsurko"},"j":{"title":"J","owner":"Golmote"},"java":{"title":"Java","require":"clike","owner":"sherblot"},"javadoc":{"title":"JavaDoc","require":["markup","java","javadoclike"],"modify":["java"],"optional":["scala"],"owner":"RunDevelopment"},"javadoclike":{"title":"JavaDoc-like","modify":["java","javascript","php"],"owner":"RunDevelopment"},"javastacktrace":{"title":"Java stack trace","owner":"RunDevelopment"},"jolie":{"title":"Jolie","require":"clike","owner":"thesave"},"jq":{"title":"JQ","owner":"RunDevelopment"},"jsdoc":{"title":"JSDoc","require":["javascript","javadoclike"],"modify":"javascript","optional":["actionscript","coffeescript"],"owner":"RunDevelopment"},"js-extras":{"title":"JS Extras","require":"javascript","modify":"javascript","optional":["actionscript","coffeescript","flow","n4js","typescript"],"owner":"RunDevelopment"},"js-templates":{"title":"JS Templates","require":"javascript","modify":"javascript","optional":["css","css-extras","graphql","markdown","markup"],"owner":"RunDevelopment"},"json":{"title":"JSON","owner":"CupOfTea696"},"jsonp":{"title":"JSONP","require":"json","owner":"RunDevelopment"},"json5":{"title":"JSON5","require":"json","owner":"RunDevelopment"},"julia":{"title":"Julia","owner":"cdagnino"},"keyman":{"title":"Keyman","owner":"mcdurdin"},"kotlin":{"title":"Kotlin","require":"clike","owner":"Golmote"},"latex":{"title":"LaTeX","alias":["tex","context"],"aliasTitles":{"tex":"TeX","context":"ConTeXt"},"owner":"japborst"},"less":{"title":"Less","require":"css","optional":"css-extras","owner":"Golmote"},"lilypond":{"title":"LilyPond","require":"scheme","alias":"ly","owner":"RunDevelopment"},"liquid":{"title":"Liquid","owner":"cinhtau"},"lisp":{"title":"Lisp","alias":["emacs","elisp","emacs-lisp"],"owner":"JuanCaicedo"},"livescript":{"title":"LiveScript","owner":"Golmote"},"lolcode":{"title":"LOLCODE","owner":"Golmote"},"lua":{"title":"Lua","owner":"Golmote"},"makefile":{"title":"Makefile","owner":"Golmote"},"markdown":{"title":"Markdown","require":"markup","alias":"md","owner":"Golmote"},"markup-templating":{"title":"Markup templating","require":"markup","owner":"Golmote"},"matlab":{"title":"MATLAB","owner":"Golmote"},"mel":{"title":"MEL","owner":"Golmote"},"mizar":{"title":"Mizar","owner":"Golmote"},"monkey":{"title":"Monkey","owner":"Golmote"},"moonscript":{"title":"MoonScript","alias":"moon","owner":"RunDevelopment"},"n1ql":{"title":"N1QL","owner":"TMWilds"},"n4js":{"title":"N4JS","require":"javascript","optional":["jsdoc"],"alias":"n4jsd","owner":"bsmith-n4"},"nand2tetris-hdl":{"title":"Nand To Tetris HDL","owner":"stephanmax"},"nasm":{"title":"NASM","owner":"rbmj"},"nginx":{"title":"nginx","owner":"westonganger","require":"clike"},"nim":{"title":"Nim","owner":"Golmote"},"nix":{"title":"Nix","owner":"Golmote"},"nsis":{"title":"NSIS","owner":"idleberg"},"objectivec":{"title":"Objective-C","require":"c","owner":"uranusjr"},"ocaml":{"title":"OCaml","owner":"Golmote"},"opencl":{"title":"OpenCL","require":"cpp","modify":["c","cpp"],"overrideExampleHeader":true,"owner":"Milania1"},"oz":{"title":"Oz","owner":"Golmote"},"parigp":{"title":"PARI/GP","owner":"Golmote"},"parser":{"title":"Parser","require":"markup","owner":"Golmote"},"pascal":{"title":"Pascal","alias":"objectpascal","aliasTitles":{"objectpascal":"Object Pascal"},"owner":"Golmote"},"pascaligo":{"title":"Pascaligo","owner":"DefinitelyNotAGoat"},"pcaxis":{"title":"PC-Axis","alias":"px","owner":"RunDevelopment"},"perl":{"title":"Perl","owner":"Golmote"},"php":{"title":"PHP","require":["clike","markup-templating"],"owner":"milesj"},"phpdoc":{"title":"PHPDoc","require":["php","javadoclike"],"modify":"php","owner":"RunDevelopment"},"php-extras":{"title":"PHP Extras","require":"php","modify":"php","owner":"milesj"},"plsql":{"title":"PL/SQL","require":"sql","owner":"Golmote"},"powershell":{"title":"PowerShell","owner":"nauzilus"},"processing":{"title":"Processing","require":"clike","owner":"Golmote"},"prolog":{"title":"Prolog","owner":"Golmote"},"properties":{"title":".properties","owner":"Golmote"},"protobuf":{"title":"Protocol Buffers","require":"clike","owner":"just-boris"},"pug":{"title":"Pug","require":["markup","javascript"],"optional":["coffeescript","ejs","handlebars","less","livescript","markdown","scss","stylus","twig"],"owner":"Golmote"},"puppet":{"title":"Puppet","owner":"Golmote"},"pure":{"title":"Pure","optional":["c","cpp","fortran"],"owner":"Golmote"},"python":{"title":"Python","alias":"py","owner":"multipetros"},"q":{"title":"Q (kdb+ database)","owner":"Golmote"},"qore":{"title":"Qore","require":"clike","owner":"temnroegg"},"r":{"title":"R","owner":"Golmote"},"jsx":{"title":"React JSX","require":["markup","javascript"],"optional":["jsdoc","js-extras","js-templates"],"owner":"vkbansal"},"tsx":{"title":"React TSX","require":["jsx","typescript"]},"renpy":{"title":"Ren'py","owner":"HyuchiaDiego"},"reason":{"title":"Reason","require":"clike","owner":"Golmote"},"regex":{"title":"Regex","modify":["actionscript","coffeescript","flow","javascript","typescript","vala"],"owner":"RunDevelopment"},"rest":{"title":"reST (reStructuredText)","owner":"Golmote"},"rip":{"title":"Rip","owner":"ravinggenius"},"roboconf":{"title":"Roboconf","owner":"Golmote"},"robotframework":{"title":"Robot Framework","alias":"robot","owner":"RunDevelopment"},"ruby":{"title":"Ruby","require":"clike","alias":"rb","owner":"samflores"},"rust":{"title":"Rust","owner":"Golmote"},"sas":{"title":"SAS","peerDependencies":["groovy","lua","sql"],"owner":"Golmote"},"sass":{"title":"Sass (Sass)","require":"css","owner":"Golmote"},"scss":{"title":"Sass (Scss)","require":"css","optional":"css-extras","owner":"MoOx"},"scala":{"title":"Scala","require":"java","owner":"jozic"},"scheme":{"title":"Scheme","owner":"bacchus123"},"shell-session":{"title":"Shell session","require":"bash","owner":"RunDevelopment"},"smalltalk":{"title":"Smalltalk","owner":"Golmote"},"smarty":{"title":"Smarty","require":"markup-templating","owner":"Golmote"},"solidity":{"title":"Solidity (Ethereum)","require":"clike","owner":"glachaud"},"soy":{"title":"Soy (Closure Template)","require":"markup-templating","owner":"Golmote"},"sparql":{"title":"SPARQL","require":"turtle","owner":"Triply-Dev","alias":"rq"},"splunk-spl":{"title":"Splunk SPL","owner":"RunDevelopment"},"sqf":{"title":"SQF: Status Quo Function (Arma 3)","require":"clike","owner":"RunDevelopment"},"sql":{"title":"SQL","owner":"multipetros"},"stylus":{"title":"Stylus","owner":"vkbansal"},"swift":{"title":"Swift","require":"clike","owner":"chrischares"},"tap":{"title":"TAP","owner":"isaacs","require":"yaml"},"tcl":{"title":"Tcl","owner":"PeterChaplin"},"textile":{"title":"Textile","require":"markup","optional":"css","owner":"Golmote"},"toml":{"title":"TOML","owner":"RunDevelopment"},"tt2":{"title":"Template Toolkit 2","require":["clike","markup-templating"],"owner":"gflohr"},"turtle":{"title":"Turtle","alias":["trig"],"aliasTitles":{"trig":"TriG"},"owner":"jakubklimek"},"twig":{"title":"Twig","require":"markup","owner":"brandonkelly"},"typescript":{"title":"TypeScript","require":"javascript","optional":"js-templates","alias":"ts","owner":"vkbansal"},"t4-cs":{"title":"T4 Text Templates (C#)","require":["t4-templating","csharp"],"alias":"t4","owner":"RunDevelopment"},"t4-vb":{"title":"T4 Text Templates (VB)","require":["t4-templating","visual-basic"],"owner":"RunDevelopment"},"t4-templating":{"title":"T4 templating","owner":"RunDevelopment"},"vala":{"title":"Vala","require":"clike","owner":"TemplarVolk"},"vbnet":{"title":"VB.Net","require":"basic","owner":"Bigsby"},"velocity":{"title":"Velocity","require":"markup","owner":"Golmote"},"verilog":{"title":"Verilog","owner":"a-rey"},"vhdl":{"title":"VHDL","owner":"a-rey"},"vim":{"title":"vim","owner":"westonganger"},"visual-basic":{"title":"Visual Basic","alias":"vb","owner":"Golmote"},"wasm":{"title":"WebAssembly","owner":"Golmote"},"wiki":{"title":"Wiki markup","require":"markup","owner":"Golmote"},"xeora":{"title":"Xeora","require":"markup","alias":"xeoracube","aliasTitles":{"xeoracube":"XeoraCube"},"owner":"freakmaxi"},"xojo":{"title":"Xojo (REALbasic)","owner":"Golmote"},"xquery":{"title":"XQuery","require":"markup","owner":"Golmote"},"yaml":{"title":"YAML","alias":"yml","owner":"hason"},"zig":{"title":"Zig","owner":"RunDevelopment"}},"plugins":{"meta":{"path":"plugins/{id}/prism-{id}","link":"plugins/{id}/"},"line-highlight":{"title":"Line Highlight","description":"Highlights specific lines and/or line ranges."},"line-numbers":{"title":"Line Numbers","description":"Line number at the beginning of code lines.","owner":"kuba-kubula"},"show-invisibles":{"title":"Show Invisibles","description":"Show hidden characters such as tabs and line breaks.","optional":["autolinker","data-uri-highlight"]},"autolinker":{"title":"Autolinker","description":"Converts URLs and emails in code to clickable links. Parses Markdown links in comments."},"wpd":{"title":"WebPlatform Docs","description":"Makes tokens link to WebPlatform.org documentation. The links open in a new tab."},"custom-class":{"title":"Custom Class","description":"This plugin allows you to prefix Prism's default classes (.comment can become .namespace--comment) or replace them with your defined ones (like .editor__comment). You can even add new classes.","owner":"dvkndn","noCSS":true},"file-highlight":{"title":"File Highlight","description":"Fetch external files and highlight them with Prism. Used on the Prism website itself.","noCSS":true},"show-language":{"title":"Show Language","description":"Display the highlighted language in code blocks (inline code does not show the label).","owner":"nauzilus","noCSS":true,"require":"toolbar"},"jsonp-highlight":{"title":"JSONP Highlight","description":"Fetch content with JSONP and highlight some interesting content (e.g. GitHub/Gists or Bitbucket API).","noCSS":true,"owner":"nauzilus"},"highlight-keywords":{"title":"Highlight Keywords","description":"Adds special CSS classes for each keyword matched in the code. For example, the keyword if will have the class keyword-if as well. You can have fine grained control over the appearance of each keyword by providing your own CSS rules.","owner":"vkbansal","noCSS":true},"remove-initial-line-feed":{"title":"Remove initial line feed","description":"Removes the initial line feed in code blocks.","owner":"Golmote","noCSS":true},"inline-color":{"title":"Inline color","description":"Adds a small inline preview for colors in style sheets.","require":"css-extras","owner":"RunDevelopment"},"previewers":{"title":"Previewers","description":"Previewers for angles, colors, gradients, easing and time.","require":"css-extras","owner":"Golmote"},"autoloader":{"title":"Autoloader","description":"Automatically loads the needed languages to highlight the code blocks.","owner":"Golmote","noCSS":true},"keep-markup":{"title":"Keep Markup","description":"Prevents custom markup from being dropped out during highlighting.","owner":"Golmote","optional":"normalize-whitespace","noCSS":true},"command-line":{"title":"Command Line","description":"Display a command line with a prompt and, optionally, the output/response from the commands.","owner":"chriswells0"},"unescaped-markup":{"title":"Unescaped Markup","description":"Write markup without having to escape anything."},"normalize-whitespace":{"title":"Normalize Whitespace","description":"Supports multiple operations to normalize whitespace in code blocks.","owner":"zeitgeist87","optional":"unescaped-markup","noCSS":true},"data-uri-highlight":{"title":"Data-URI Highlight","description":"Highlights data-URI contents.","owner":"Golmote","noCSS":true},"toolbar":{"title":"Toolbar","description":"Attach a toolbar for plugins to easily register buttons on the top of a code block.","owner":"mAAdhaTTah"},"copy-to-clipboard":{"title":"Copy to Clipboard Button","description":"Add a button that copies the code block to the clipboard when clicked.","owner":"mAAdhaTTah","require":"toolbar","noCSS":true},"download-button":{"title":"Download Button","description":"A button in the toolbar of a code block adding a convenient way to download a code file.","owner":"Golmote","require":"toolbar","noCSS":true},"match-braces":{"title":"Match braces","description":"Highlights matching braces.","owner":"RunDevelopment"},"diff-highlight":{"title":"Diff Highlight","description":"Highlights the code inside diff blocks.","owner":"RunDevelopment","require":"diff"},"filter-highlight-all":{"title":"Filter highlightAll","description":"Filters the elements the highlightAll and highlightAllUnder methods actually highlight.","owner":"RunDevelopment","noCSS":true}}}; if (typeof module !== 'undefined' && module.exports) { module.exports = components; } \ No newline at end of file diff --git a/components.json b/components.json index bc0dbe33dc..9292842699 100644 --- a/components.json +++ b/components.json @@ -60,7 +60,7 @@ "css": { "title": "CSS", "option": "default", - "peerDependencies": "markup" + "modify": "markup" }, "clike": { "title": "C-like", @@ -70,7 +70,7 @@ "javascript": { "title": "JavaScript", "require": "clike", - "peerDependencies": "markup", + "modify": "markup", "alias": "js", "option": "default" }, @@ -85,7 +85,7 @@ "actionscript": { "title": "ActionScript", "require": "javascript", - "peerDependencies": "markup", + "modify": "markup", "owner": "Golmote" }, "ada": { @@ -235,6 +235,7 @@ "css-extras": { "title": "CSS Extras", "require": "css", + "modify": "css", "owner": "milesj" }, "d": { @@ -374,7 +375,7 @@ "haml": { "title": "Haml", "require": "ruby", - "peerDependencies": [ + "optional": [ "css", "css-extras", "coffeescript", @@ -409,8 +410,10 @@ }, "http": { "title": "HTTP", - "peerDependencies": [ + "optional": [ + "css", "javascript", + "json", "markup" ], "owner": "danielgtaylor" @@ -455,14 +458,17 @@ "javadoc": { "title": "JavaDoc", "require": ["markup", "java", "javadoclike"], - "peerDependencies": [ + "modify": [ + "java" + ], + "optional": [ "scala" ], "owner": "RunDevelopment" }, "javadoclike": { "title": "JavaDoc-like", - "peerDependencies": [ + "modify": [ "java", "javascript", "php" @@ -485,7 +491,8 @@ "jsdoc": { "title": "JSDoc", "require": ["javascript", "javadoclike"], - "peerDependencies": [ + "modify": "javascript", + "optional": [ "actionscript", "coffeescript" ], @@ -494,7 +501,8 @@ "js-extras": { "title": "JS Extras", "require": "javascript", - "peerDependencies": [ + "modify": "javascript", + "optional": [ "actionscript", "coffeescript", "flow", @@ -506,7 +514,8 @@ "js-templates": { "title": "JS Templates", "require": "javascript", - "peerDependencies": [ + "modify": "javascript", + "optional": [ "css", "css-extras", "graphql", @@ -554,7 +563,7 @@ "less": { "title": "Less", "require": "css", - "peerDependencies": "css-extras", + "optional": "css-extras", "owner": "Golmote" }, "lilypond": { @@ -627,7 +636,7 @@ "n4js": { "title": "N4JS", "require": "javascript", - "peerDependencies": [ + "optional": [ "jsdoc" ], "alias": "n4jsd", @@ -670,7 +679,7 @@ "opencl": { "title": "OpenCL", "require": "cpp", - "peerDependencies": [ + "modify": [ "c", "cpp" ], @@ -719,11 +728,13 @@ "phpdoc": { "title": "PHPDoc", "require": ["php", "javadoclike"], + "modify": "php", "owner": "RunDevelopment" }, "php-extras": { "title": "PHP Extras", "require": "php", + "modify": "php", "owner": "milesj" }, "plsql": { @@ -756,7 +767,7 @@ "pug": { "title": "Pug", "require": ["markup", "javascript"], - "peerDependencies": [ + "optional": [ "coffeescript", "ejs", "handlebars", @@ -775,7 +786,7 @@ }, "pure": { "title": "Pure", - "peerDependencies": [ + "optional": [ "c", "cpp", "fortran" @@ -803,7 +814,7 @@ "jsx": { "title": "React JSX", "require": ["markup", "javascript"], - "peerDependencies": [ + "optional": [ "jsdoc", "js-extras", "js-templates" @@ -825,7 +836,7 @@ }, "regex": { "title": "Regex", - "peerDependencies": [ + "modify": [ "actionscript", "coffeescript", "flow", @@ -875,7 +886,7 @@ "scss": { "title": "Sass (Scss)", "require": "css", - "peerDependencies": "css-extras", + "optional": "css-extras", "owner": "MoOx" }, "scala": { @@ -951,7 +962,7 @@ "textile": { "title": "Textile", "require": "markup", - "peerDependencies": "css", + "optional": "css", "owner": "Golmote" }, "toml": { @@ -979,7 +990,7 @@ "typescript": { "title": "TypeScript", "require": "javascript", - "peerDependencies": "js-templates", + "optional": "js-templates", "alias": "ts", "owner": "vkbansal" }, @@ -1084,7 +1095,7 @@ "show-invisibles": { "title": "Show Invisibles", "description": "Show hidden characters such as tabs and line breaks.", - "after": [ + "optional": [ "autolinker", "data-uri-highlight" ] @@ -1155,7 +1166,7 @@ "title": "Keep Markup", "description": "Prevents custom markup from being dropped out during highlighting.", "owner": "Golmote", - "after": "normalize-whitespace", + "optional": "normalize-whitespace", "noCSS": true }, "command-line": { @@ -1171,7 +1182,7 @@ "title": "Normalize Whitespace", "description": "Supports multiple operations to normalize whitespace in code blocks.", "owner": "zeitgeist87", - "after": "unescaped-markup", + "optional": "unescaped-markup", "noCSS": true }, "data-uri-highlight": { diff --git a/components/index.js b/components/index.js index fe5940ae1c..450810a7a2 100644 --- a/components/index.js +++ b/components/index.js @@ -1,82 +1,49 @@ -var components = require('../components.js'); -var peerDependentsMap = null; - -function getPeerDependentsMap() { - var peerDependentsMap = {}; - Object.keys(components.languages).forEach(function (language) { - if (language === 'meta') { - return false; - } - if (components.languages[language].peerDependencies) { - var peerDependencies = components.languages[language].peerDependencies; - if (!Array.isArray(peerDependencies)) { - peerDependencies = [peerDependencies]; - } - peerDependencies.forEach(function (peerDependency) { - if (!peerDependentsMap[peerDependency]) { - peerDependentsMap[peerDependency] = []; - } - peerDependentsMap[peerDependency].push(language); - }); - } - }); - return peerDependentsMap; -} - -function getPeerDependents(mainLanguage) { - if (!peerDependentsMap) { - peerDependentsMap = getPeerDependentsMap(); +const components = require('../components.js'); +const getLoader = require('../dependencies'); + + +/** + * The set of all languages which have been loaded using the below function. + * + * @type {Set} + */ +const loadedLanguages = new Set(); + +/** + * Loads the given languages and adds them to the current Prism instance. + * + * If no languages are provided, __all__ Prism languages will be loaded. + * + * @param {string|string[]} [languages] + * @returns {void} + */ +function loadLanguages(languages) { + if (languages === undefined) { + languages = Object.keys(components.languages).filter(l => l != 'meta'); + } else if (!Array.isArray(languages)) { + languages = [languages]; } - return peerDependentsMap[mainLanguage] || []; -} -function loadLanguages(arr, withoutDependencies) { - // If no argument is passed, load all components - if (!arr) { - arr = Object.keys(components.languages).filter(function (language) { - return language !== 'meta'; - }); - } - if (arr && !arr.length) { - return; - } - - if (!Array.isArray(arr)) { - arr = [arr]; - } + // the user might have loaded languages via some other way or used `prism.js` which already includes some + // we don't need to validate the ids because `getLoader` will ignore invalid ones + const loaded = [...loadedLanguages, ...Object.keys(Prism.languages)]; - arr.forEach(function (language) { - if (!components.languages[language]) { - console.warn('Language does not exist ' + language); + getLoader(components, languages, loaded).load(lang => { + if (!(lang in components.languages)) { + console.warn('Language does not exist: ' + lang); return; } - // Load dependencies first - if (!withoutDependencies && components.languages[language].require) { - loadLanguages(components.languages[language].require); - } - var pathToLanguage = './prism-' + language; + const pathToLanguage = './prism-' + lang; + + // remove from require cache and from Prism delete require.cache[require.resolve(pathToLanguage)]; - delete Prism.languages[language]; + delete Prism.languages[lang]; + require(pathToLanguage); - // Reload dependents - var dependents = getPeerDependents(language).filter(function (dependent) { - // If dependent language was already loaded, - // we want to reload it. - if (Prism.languages[dependent]) { - delete Prism.languages[dependent]; - return true; - } - return false; - }); - if (dependents.length) { - loadLanguages(dependents, true); - } + loadedLanguages.add(lang); }); } -module.exports = function (arr) { - // Don't expose withoutDependencies - loadLanguages(arr); -}; \ No newline at end of file +module.exports = loadLanguages; diff --git a/dependencies.js b/dependencies.js new file mode 100644 index 0000000000..f301cd4d74 --- /dev/null +++ b/dependencies.js @@ -0,0 +1,431 @@ +"use strict"; + +/** + * @typedef {Object} Components + * @typedef {Object} ComponentCategory + * + * @typedef ComponentEntry + * @property {string} [title] The title of the component. + * @property {string} [owner] The GitHub user name of the owner. + * @property {boolean} [noCSS=false] Whether the component doesn't have style sheets which should also be loaded. + * @property {string | string[]} [alias] An optional list of aliases for the id of the component. + * @property {Object} [aliasTitles] An optional map from an alias to its title. + * + * Aliases which are not in this map will the get title of the component. + * @property {string | string[]} [optional] + * @property {string | string[]} [require] + * @property {string | string[]} [modify] + */ + +var getLoader = (function () { + + /** + * A function which does absolutely nothing. + * + * @type {any} + */ + var noop = function () { }; + + /** + * Converts the given value to an array. + * + * If the given value is already an array, the value itself will be returned. + * `null` and `undefined` will return an empty array. + * For every other value a new array with the given value as its only element will be created. + * + * @param {null | undefined | T | T[]} value + * @returns {T[]} + * @template T + */ + function toArray(value) { + if (Array.isArray(value)) { + return value; + } else { + return value == null ? [] : [value]; + } + } + + /** + * Returns a new set for the given string array. + * + * @param {string[]} array + * @returns {StringSet} + * + * @typedef {Object} StringSet + */ + function toSet(array) { + /** @type {StringSet} */ + var set = {}; + for (var i = 0, l = array.length; i < l; i++) { + set[array[i]] = true; + } + return set; + } + + /** + * Creates a map of every components id to its entry. + * + * @param {Components} components + * @returns {EntryMap} + * + * @typedef {{ [id: string]: Readonly | undefined }} EntryMap + */ + function createEntryMap(components) { + /** @type {EntryMap} */ + var map = {}; + + for (var categoryName in components) { + var category = components[categoryName]; + for (var id in category) { + if (id != 'meta') { + /** @type {ComponentEntry | string} */ + var entry = category[id]; + map[id] = typeof entry == 'string' ? { title: entry } : entry; + } + } + } + + return map; + } + + /** + * Creates a full dependencies map which includes all types of dependencies and their transitive dependencies. + * + * @param {EntryMap} entryMap + * @returns {DependencyMap} + * + * @typedef {Object} DependencyMap + */ + function createDependencyMap(entryMap) { + /** @type {DependencyMap} */ + var map = {}; + + /** + * Adds the dependencies of the given component to the dependency map. + * + * @param {string} id + * @param {string[]} stack + */ + function addToMap(id, stack) { + if (id in map) { + return; + } + + stack.push(id); + + // check for circular dependencies + var firstIndex = stack.indexOf(id); + if (firstIndex < stack.length - 1) { + throw new Error('Circular dependency: ' + stack.slice(firstIndex).join(' -> ')); + } + + /** @type {StringSet} */ + var dependencies = {}; + + var entry = entryMap[id]; + if (entry) { + /** @type {string[]} */ + var deps = [].concat(entry.require, entry.modify, entry.optional).filter(Boolean); + deps.forEach(function (depId) { + if (!(depId in entryMap)) { + throw new Error(id + ' depends on an unknown component ' + depId); + } + + addToMap(depId, stack); + dependencies[depId] = true; + for (var transitiveDepId in map[depId]) { + dependencies[transitiveDepId] = true; + } + }); + } + + map[id] = dependencies; + + stack.pop(); + } + + for (var id in entryMap) { + addToMap(id, []); + } + + return map; + } + + /** + * Returns a function which resolves the aliases of its given id of alias. + * + * @param {EntryMap} entryMap + * @returns {(idOrAlias: string) => string} + */ + function createAliasResolver(entryMap) { + /** @type {Object} */ + var map = {}; + + for (var id in entryMap) { + var entry = entryMap[id]; + var aliases = toArray(entry && entry.alias); + aliases.forEach(function (alias) { + if (alias in map) { + throw new Error(alias + ' cannot be alias for both ' + id + ' and ' + map[alias]); + } + if (alias in entryMap) { + throw new Error(alias + ' cannot be alias of ' + id + ' because it is a component.'); + } + map[alias] = id; + }); + } + + return function (idOrAlias) { + return map[idOrAlias] || idOrAlias; + }; + } + + /** + * @typedef LoadChainer + * @property {(before: T, after: () => T) => T} series + * @property {(values: T[]) => T} parallel + * @template T + */ + + /** + * Creates an implicit DAG from the given components and dependencies and call the given `loadComponent` for each + * component in topological order. + * + * @param {DependencyMap} dependencyMap + * @param {StringSet} ids + * @param {(id: string) => T} loadComponent + * @param {LoadChainer} [chainer] + * @returns {T} + * @template T + */ + function loadComponentsInOrder(dependencyMap, ids, loadComponent, chainer) { + const series = chainer ? chainer.series : undefined; + const parallel = chainer ? chainer.parallel : noop; + + /** @type {Object} */ + var cache = {}; + + /** + * A set of ids of nodes which are not depended upon by any other node in the graph. + * @type {StringSet} + */ + var ends = {}; + + /** + * Loads the given component and its dependencies or returns the cached value. + * + * @param {string} id + * @returns {T} + */ + function handleId(id) { + if (id in cache) { + return cache[id]; + } + + // assume that it's an end + // if it isn't, it will be removed later + ends[id] = true; + + // all dependencies of the component in the given ids + var dependsOn = []; + for (var depId in dependencyMap[id]) { + if (depId in ids) { + dependsOn.push(depId); + } + } + + /** + * The value to be returned. + * @type {T} + */ + var value; + + if (dependsOn.length === 0) { + value = loadComponent(id); + } else { + var depsValue = parallel(dependsOn.map(function (depId) { + var value = handleId(depId); + // none of the dependencies can be ends + delete ends[depId]; + return value; + })); + if (series) { + // the chainer will be responsibly for calling the function calling loadComponent + value = series(depsValue, function () { return loadComponent(id); }); + } else { + // we don't have a chainer, so we call loadComponent ourselves + loadComponent(id); + } + } + + // cache and return + return cache[id] = value; + } + + for (var id in ids) { + handleId(id); + } + + /** @type {T[]} */ + var endValues = []; + for (var endId in ends) { + endValues.push(cache[endId]); + } + return parallel(endValues); + } + + /** + * Returns whether the given object has any keys. + * + * @param {object} obj + */ + function hasKeys(obj) { + for (var key in obj) { + return true; + } + return false; + } + + /** + * Returns an object which provides methods to get the ids of the components which have to be loaded (`getIds`) and + * a way to efficiently load them in synchronously and asynchronous contexts (`load`). + * + * The set of ids to be loaded is a superset of `load`. If some of these ids are in `loaded`, the corresponding + * components will have to reloaded. + * + * The ids in `load` and `loaded` may be in any order and can contain duplicates. + * + * @param {Components} components + * @param {string[]} load + * @param {string[]} [loaded=[]] A list of already loaded components. + * + * If a component is in this list, then all of its requirements will also be assumed to be in the list. + * @returns {Loader} + * + * @typedef Loader + * @property {() => string[]} getIds A function to get all ids of the components to load. + * + * The returned ids will be duplicate-free, alias-free and in load order. + * @property {LoadFunction} load A functional interface to load components. + * + * @typedef { (loadComponent: (id: string) => T, chainer?: LoadChainer) => T} LoadFunction + * A functional interface to load components. + * + * The `loadComponent` function will be called for every component in the order in which they have to be loaded. + * + * The `chainer` is useful for asynchronous loading and its `series` and `parallel` functions can be thought of as + * `Promise#then` and `Promise.all`. + * + * @example + * load(id => { loadComponent(id); }); // returns undefined + * + * await load( + * id => loadComponentAsync(id), // returns a Promise for each id + * { + * series: async (before, after) => { + * await before; + * await after(); + * }, + * parallel: async (values) => { + * await Promise.all(values); + * } + * } + * ); + */ + function getLoader(components, load, loaded) { + var entryMap = createEntryMap(components); + var resolveAlias = createAliasResolver(entryMap); + + load = load.map(resolveAlias); + loaded = (loaded || []).map(resolveAlias); + + var loadSet = toSet(load); + var loadedSet = toSet(loaded); + + // add requirements + + load.forEach(addRequirements); + function addRequirements(id) { + var entry = entryMap[id]; + if (entry) { + var require = toArray(entry.require); + require.forEach(function (reqId) { + if (!(reqId in loadedSet)) { + loadSet[reqId] = true; + addRequirements(reqId); + } + }); + } + } + + // add components to reload + + // A component x in `loaded` has to be reloaded if + // 1) a component in `load` modifies x. + // 2) x depends on a component in `load`. + // The above two condition have to be applied until nothing changes anymore. + + var dependencyMap = createDependencyMap(entryMap); + + /** @type {StringSet} */ + var loadAdditions = loadSet; + /** @type {StringSet} */ + var newIds; + while (hasKeys(loadAdditions)) { + newIds = {}; + + // condition 1) + for (var loadId in loadAdditions) { + var entry = entryMap[loadId]; + if (entry) { + var modify = toArray(entry.modify); + modify.forEach(function (modId) { + if (modId in loadedSet) { + newIds[modId] = true; + } + }); + } + } + + // condition 2) + for (var loadedId in loadedSet) { + if (!(loadedId in loadSet)) { + for (var depId in dependencyMap[loadedId]) { + if (depId in loadSet) { + newIds[loadedId] = true; + break; + } + } + } + } + + loadAdditions = newIds; + for (var newId in loadAdditions) { + loadSet[newId] = true; + } + } + + /** @type {Loader} */ + var loader = { + getIds: function () { + var ids = []; + loader.load(function (id) { + ids.push(id); + }); + return ids; + }, + load: function (loadComponent, chainer) { + return loadComponentsInOrder(dependencyMap, loadSet, loadComponent, chainer); + } + }; + + return loader; + } + + return getLoader; + +}()); + +if (typeof module !== 'undefined') { + module.exports = getLoader; +} diff --git a/download.html b/download.html index c8e0bf9af5..71f0e93f19 100644 --- a/download.html +++ b/download.html @@ -175,6 +175,7 @@

Customize your download

+ diff --git a/package.json b/package.json index 9d975990e6..da17b2c706 100755 --- a/package.json +++ b/package.json @@ -7,11 +7,12 @@ "scripts": { "test:aliases": "mocha tests/aliases-test.js", "test:core": "mocha tests/core/**/*.js", + "test:dependencies": "mocha tests/dependencies-test.js", "test:languages": "mocha tests/run.js", "test:patterns": "mocha tests/pattern-tests.js", "test:plugins": "mocha tests/plugins/**/*.js", "test:runner": "mocha tests/testrunner-tests.js", - "test": "npm run test:runner && npm run test:core && npm run test:languages && npm run test:plugins && npm run test:aliases && npm run test:patterns" + "test": "npm run test:runner && npm run test:core && npm run test:dependencies && npm run test:languages && npm run test:plugins && npm run test:aliases && npm run test:patterns" }, "repository": { "type": "git", diff --git a/scripts/download.js b/scripts/download.js index b6ed649cf7..cfd1d16a05 100644 --- a/scripts/download.js +++ b/scripts/download.js @@ -22,6 +22,21 @@ var treePromise = new Promise(function(resolve) { }); }); +/** + * Converts the given value into an array. + * + * @param {T | T[] | null | undefined} value + * @returns {T[]} + * @template T + */ +function toArray(value) { + if (Array.isArray(value)) { + return value; + } else { + return value == null ? [] : [value]; + } +} + var hstr = window.location.hash.match(/(?:languages|plugins)=[-+\w]+|themes=[-\w]+/g); if (hstr) { hstr.forEach(function(str) { @@ -50,13 +65,8 @@ if (hstr) { } components[category][id].option = 'default'; } - if (components[category][id].require) { - var deps = components[category][id].require; - if (!Array.isArray(deps)) { - deps = [deps]; - } - deps.forEach(makeDefault); - } + + toArray(components[category][id].require).forEach(makeDefault); } } }; @@ -142,9 +152,9 @@ for (var category in components) { noCSS: all[id].noCSS || all.meta.noCSS, noJS: all[id].noJS || all.meta.noJS, enabled: checked, - require: $u.type(all[id].require) === 'string' ? [all[id].require] : all[id].require, - after: $u.type(all[id].after) === 'string' ? [all[id].after] : all[id].after, - peerDependencies: $u.type(all[id].peerDependencies) === 'string' ? [all[id].peerDependencies] : all[id].peerDependencies, + require: toArray(all[id].require), + after: toArray(all[id].after), + modify: toArray(all[id].modify), owner: all[id].owner, files: { minified: { @@ -158,11 +168,9 @@ for (var category in components) { } }; - if (info.require) { - info.require.forEach(function (v) { - dependencies[v] = (dependencies[v] || []).concat(id); - }); - } + info.require.forEach(function (v) { + dependencies[v] = (dependencies[v] || []).concat(id); + }); if (!all[id].noJS && !/\.css$/.test(filepath)) { info.files.minified.paths.push(filepath.replace(/(\.js)?$/, '.min.js')); @@ -446,72 +454,31 @@ function delayedGenerateCode(){ timerId = setTimeout(generateCode, 500); } -function getSortedComponents(components, requireName, sorted) { - if (!sorted) { - sorted = []; - for (var component in components) { - sorted.push(component); - } - } - - var i = 0; - while (i < sorted.length) { - var id = sorted[i]; - var indexOfRequirement = i; - var notNow = false; - for (var requirement in components[id][requireName]) { - indexOfRequirement = sorted.indexOf(components[id][requireName][requirement]); - if (indexOfRequirement > i) { - notNow = true; - break; - } - } - if (notNow) { - var tmp = sorted[i]; - sorted[i] = sorted[indexOfRequirement]; - sorted[indexOfRequirement] = tmp; - } - else { - i++; - } - } - return sorted; -} - -function getSortedComponentsByRequirements(components, afterName) { - var sorted = getSortedComponents(components, afterName); - return getSortedComponents(components, "require", sorted); -} - function generateCode(){ + /** @type {CodePromiseInfo[]} */ var promises = []; var redownload = {}; for (var category in components) { - var all = components[category]; - - // In case if one component requires other, required component should go first. - var sorted = getSortedComponentsByRequirements(all, category === 'languages' ? 'peerDependencies' : 'after'); - - for (var i = 0; i < sorted.length; i++) { - var id = sorted[i]; - - if(id === 'meta') { + for (var id in components[category]) { + if (id === 'meta') { continue; } - var info = all[id]; + var info = components[category][id]; if (info.enabled) { if (category !== 'core') { - redownload[category] = redownload[category] || []; + redownload[category] = redownload[category] || []; redownload[category].push(id); } - info.files[minified? 'minified' : 'dev'].paths.forEach(function (path) { + info.files[minified ? 'minified' : 'dev'].paths.forEach(function (path) { if (cache[path]) { var type = path.match(/\.(\w+)$/)[1]; promises.push({ contentsPromise: cache[path].contentsPromise, + id: id, + category: category, path: path, type: type }); @@ -531,7 +498,7 @@ function generateCode(){ var code = res.code; var errors = res.errors; - if(errors.length) { + if (errors.length) { error.style.display = 'block'; error.innerHTML = ''; $u.element.contents(error, errors); @@ -571,7 +538,49 @@ function generateCode(){ }); } +/** + * Returns a promise of the code of the Prism bundle. + * + * @param {CodePromiseInfo[]} promises + * @returns {Promise<{ code: { js: string, css: string }, errors: HTMLElement[] }>} + * + * @typedef CodePromiseInfo + * @property {Promise} contentsPromise + * @property {string} id + * @property {string} category + * @property {string} path + * @property {string} type + */ function buildCode(promises) { + // sort the promises + + /** @type {CodePromiseInfo[]} */ + var finalPromises = []; + /** @type {Object} */ + var toSortMap = {}; + + promises.forEach(function (p) { + if (p.category == "core" || p.category == "themes") { + finalPromises.push(p); + } else { + var infos = toSortMap[p.id]; + if (!infos) { + toSortMap[p.id] = infos = []; + } + infos.push(p); + } + }); + + // this assumes that the ids in `toSortMap` are complete under transitive requirements + getLoader(components, Object.keys(toSortMap)).getIds().forEach(function (id) { + if (!toSortMap[id]) { + console.error(id + " not found."); + } + finalPromises.push.apply(finalPromises, toSortMap[id]); + }); + promises = finalPromises; + + // build var i = 0, l = promises.length; var code = {js: '', css: ''}; @@ -603,6 +612,9 @@ function buildCode(promises) { return new Promise(f); } +/** + * @returns {Promise} + */ function getVersion() { return getFileContents('./package.json').then(function (jsonStr) { return JSON.parse(jsonStr).version; diff --git a/tests/dependencies-test.js b/tests/dependencies-test.js new file mode 100644 index 0000000000..2a9bdb2f3e --- /dev/null +++ b/tests/dependencies-test.js @@ -0,0 +1,257 @@ +const { assert } = require('chai'); +const getLoader = require('../dependencies'); +const components = require('../components'); + + +describe('Dependency logic', function () { + + /** @type {import("../dependencies").Components} */ + const components = { + languages: { + 'a': { + alias: 'a2' + }, + 'b': { + alias: 'b2' + }, + 'c': { + require: 'a', + optional: ['b', 'e'] + }, + 'd': { + require: ['c', 'b'], + alias: 'xyz' + }, + }, + pluginsOrSomething: { + 'e': { + modify: 'a' + }, + } + }; + + /** + * Returns the ids of `getLoader`. + * + * @param {string[]} load + * @param {string[]} [loaded] + * @returns {string[]} + */ + function getIds(load, loaded) { + return getLoader(components, load, loaded).getIds(); + } + + describe('Returned ids', function () { + + it('- should load requirements', function () { + assert.sameMembers(getIds(['d']), ['a', 'b', 'c', 'd']); + }); + + it('- should not load already loaded requirements if not necessary', function () { + assert.sameMembers(getIds(['d'], ['a', 'b']), ['c', 'd']); + }); + + it('- should load already loaded requirements if requested', function () { + assert.sameMembers(getIds(['a', 'd'], ['a', 'b']), ['a', 'c', 'd']); + }); + + it('- should reload modified components', function () { + assert.sameMembers(getIds(['e'], ['a', 'b', 'c', 'd']), ['a', 'c', 'd', 'e']); + }); + + it('- should work with empty load', function () { + assert.sameMembers(getIds([], ['a', 'b', 'c', 'd']), []); + }); + + it('- should return unknown ids as is', function () { + assert.sameMembers(getIds(['c', 'foo'], ['bar']), ['foo', 'c', 'a']); + }); + + it('- should throw on unknown dependencies', function () { + assert.throws(() => { + /** @type {import("../dependencies").Components} */ + const circular = { + languages: { + a: { + require: 'c' + }, + b: 'B' + } + }; + getLoader(circular, ['a']).getIds(); + }); + }); + + }); + + describe('Load order', function () { + + // Note: The order of a and b isn't defined, so don't add any test with both of them being loaded here + + it('- should load components in the correct order (require)', function () { + assert.deepStrictEqual(getIds(['c']), ['a', 'c']); + }); + + it('- should load components in the correct order (modify)', function () { + assert.deepStrictEqual(getIds(['e', 'a']), ['a', 'e']); + }); + + it('- should load components in the correct order (optional)', function () { + assert.deepStrictEqual(getIds(['c', 'b'], ['a']), ['b', 'c']); + }); + + it('- should load components in the correct order (require + optional)', function () { + assert.deepStrictEqual(getIds(['d'], ['a']), ['b', 'c', 'd']); + }); + + it('- should load components in the correct order (require + modify + optional)', function () { + assert.deepStrictEqual(getIds(['d', 'e'], ['b']), ['a', 'e', 'c', 'd']); + }); + + }); + + describe('Aliases', function () { + + it('- should resolve aliases in the list of components to load', function () { + assert.sameMembers(getIds(['xyz']), ['a', 'b', 'c', 'd']); + }); + + it('- should resolve aliases in the list of loaded components', function () { + assert.sameMembers(getIds(['d'], ['a', 'a2', 'b2']), ['c', 'd']); + }); + + it('- should throw on duplicate aliases', function () { + assert.throws(() => { + /** @type {import("../dependencies").Components} */ + const circular = { + languages: { + a: { + alias: 'c' + }, + b: { + alias: 'c' + } + } + }; + getLoader(circular, ['a']).getIds(); + }); + }); + + it('- should throw on aliases which are components', function () { + assert.throws(() => { + /** @type {import("../dependencies").Components} */ + const circular = { + languages: { + a: { + alias: 'b' + }, + b: 'B' + } + }; + getLoader(circular, ['a']).getIds(); + }); + }); + + }); + + describe('Circular dependencies', function () { + + it('- should throw on circular dependencies', function () { + assert.throws(() => { + /** @type {import("../dependencies").Components} */ + const circular = { + languages: { + a: { + require: 'b' + }, + b: { + optional: 'a' + } + } + }; + getLoader(circular, ['a']).getIds(); + }); + }); + + }); + + describe('Async loading', function () { + + it('- should load components in the correct order', async function () { + + /** @type {import("../dependencies").Components} */ + const localComponents = { + languages: { + 'a': {}, + 'b': { + require: 'a' + }, + 'c': { + require: 'b' + } + } + }; + + /** @type {string[]} */ + const actualLoadOrder = []; + /** @type {string[]} */ + const actualResolveOrder = []; + + /** + * + * @param {string} id + * @returns {Promise} + */ + function loadComp(id) { + actualLoadOrder.push(id); + + // the idea is that the components which have to be loaded first, take the longest, so if all were to + // start getting loaded at the same time, their order would be the reverse of the expected order. + let delay; + if (id === 'a') { + delay = 30; + } else if (id === 'b') { + delay = 20; + } else if (id === 'c') { + delay = 10; + } + + return new Promise((resolve) => { + setTimeout(() => { + actualResolveOrder.push(id); + resolve(); + }, delay); + }); + } + + const loader = getLoader(localComponents, ['c']); + + await loader.load(id => loadComp(id), { + series: async (before, after) => { + await before; + await after(); + }, + parallel: async (values) => { + await Promise.all(values); + } + }); + + assert.deepStrictEqual(actualLoadOrder, ['a', 'b', 'c'], 'actualLoadOrder:'); + assert.deepStrictEqual(actualResolveOrder, ['a', 'b', 'c'], 'actualResolveOrder:'); + }); + + }); + +}); + +describe('components.json', function () { + + it('- should be valid', function () { + try { + getLoader(components, Object.keys(components.languages).filter(k => k != 'meta')).getIds(); + } catch (error) { + assert.fail(error.toString()); + } + }); + +}); diff --git a/tests/helper/prism-loader.js b/tests/helper/prism-loader.js index 8ec06e3643..7bc07487c9 100644 --- a/tests/helper/prism-loader.js +++ b/tests/helper/prism-loader.js @@ -4,9 +4,16 @@ const fs = require("fs"); const vm = require("vm"); const { getAllFiles } = require("./test-discovery"); const components = require("../../components"); +const getLoader = require("../../dependencies"); const languagesCatalog = components.languages; +/** + * @typedef PrismLoaderContext + * @property {any} Prism The Prism instance. + * @property {Set} loaded A set of loaded components. + */ + module.exports = { /** @@ -17,7 +24,7 @@ module.exports = { */ createInstance(languages) { let context = { - loadedLanguages: [], + loaded: new Set(), Prism: this.createEmptyPrism() }; @@ -27,53 +34,29 @@ module.exports = { }, /** - * Loads the given languages and appends the config to the given Prism object + * Loads the given languages and appends the config to the given Prism object. * * @private * @param {string|string[]} languages - * @param {{loadedLanguages: string[], Prism: Prism}} context - * @returns {{loadedLanguages: string[], Prism: Prism}} + * @param {PrismLoaderContext} context + * @returns {PrismLoaderContext} */ loadLanguages(languages, context) { if (typeof languages === 'string') { languages = [languages]; } - for (const language of languages) { - context = this.loadLanguage(language, context); - } - - return context; - }, - - /** - * Loads the given language (including recursively loading the dependencies) and - * appends the config to the given Prism object - * - * @private - * @param {string} language - * @param {{loadedLanguages: string[], Prism: Prism}} context - * @returns {{loadedLanguages: string[], Prism: Prism}} - */ - loadLanguage(language, context) { - if (!languagesCatalog[language]) { - throw new Error("Language '" + language + "' not found."); - } - - // the given language was already loaded - if (-1 < context.loadedLanguages.indexOf(language)) { - return context; - } + getLoader(components, languages, [...context.loaded]).load(id => { + if (!languagesCatalog[id]) { + throw new Error(`Language '${id}' not found.`); + } - // if the language has a dependency -> load it first - if (languagesCatalog[language].require) { - context = this.loadLanguages(languagesCatalog[language].require, context); - } + // load the language itself + const languageSource = this.loadComponentSource(id); + context.Prism = this.runFileWithContext(languageSource, { Prism: context.Prism }).Prism; - // load the language itself - const languageSource = this.loadComponentSource(language); - context.Prism = this.runFileWithContext(languageSource, { Prism: context.Prism }).Prism; - context.loadedLanguages.push(language); + context.loaded.add(id); + }); return context; },