@@ -3,6 +3,31 @@ import matchAll from 'match-all';
33import { customHeadingRenderer } from './markdown_renderers' ;
44
55
6+ /**
7+ * Map from the language of a fenced code block to the title of corresponding tab.
8+ * The language is a string provided by the default marked tokenizer.
9+ * Note that not all of the languages (such as python2) might be possible at the moment
10+ * in the default marked tokenizer. We anyway include them here for
11+ * robustness to potential future improvements of marked.
12+ * In case tab title can't be resolved from language using this mapping, the language itself is used as a tab title.
13+ */
14+ const LANGUAGE_TO_TAB_TITLE = {
15+ js : 'Node.JS' ,
16+ javascript : 'Node.js' ,
17+ nodejs : 'Node.js' ,
18+ bash : 'Bash' ,
19+ curl : 'cURL' ,
20+ dockerfile : 'Dockerfile' ,
21+ php : 'PHP' ,
22+ json : 'JSON' ,
23+ xml : 'XML' ,
24+ python : 'Python' ,
25+ python2 : 'Python 2' ,
26+ python3 : 'Python 3' ,
27+ yml : 'YAML' ,
28+ yaml : 'YAML' ,
29+ } ;
30+
631const APIFY_CODE_TABS = 'apify-code-tabs' ;
732const DEFAULT_MARKED_RENDERER = new marked . Renderer ( ) ;
833
@@ -29,39 +54,106 @@ const codeTabObjectFromCodeTabMarkdown = (markdown) => {
2954
3055
3156/**
57+ * This custom function is used in the same context as default `marked` function.
58+ *
59+ * It parses the given markdown and treats some headings and code blocks in a custom way
60+ * -----------------------------------------------------------------------------------------------
61+ * 0. Heading with {custom-id} in text will have id="custom-id" property on reasulting <h...> tag.
62+ * E.g.
63+ * # Welcome to Apify {welcome-title-id}
64+ * is turned to
65+ * <h1 id="welcome-title-id">Welcome to Apify</h1>
66+ * -----------------------------------------------------------------------------------------------
67+ * 1. Fenced code block with explicit language which is in the mapping LANGUAGE_TO_TAB_TITLE
68+ * ```my-lang
69+ * my-code
70+ * ```
71+ * This block is turned into [apify-code-tabs]$INDEX[/apify-code-tabs] in returned HTML
72+ * and returned codeTabsObjectPerIndex contains key $INDEX with value
73+ * {
74+ * LANGUAGE_TO_TAB_TITLE[my-lang]: { lang: 'my-lang', code: 'my-code' }
75+ * }
76+ * -----------------------------------------------------------------------------------------------
77+ * 2. Fenced code block with explicit language which is NOT in the mapping LANGUAGE_TO_TAB_TITLE
78+ * ```my-lang-not-in-mapping
79+ * my-code
80+ * ```
81+ * This block is turned into [apify-code-tabs]$INDEX[/apify-code-tabs] in returned HTML
82+ * and returned codeTabsObjectPerIndex contains key $INDEX with value
83+ * {
84+ * my-lang-not-in-mapping: { lang: 'my-lang-not-in-mapping', code: 'my-code' }
85+ * }
86+ * -----------------------------------------------------------------------------------------------
87+ * 3. Fenced code block with no language
88+ * ```
89+ * my-code
90+ * ```
91+ *
92+ * is handled by default marked package and returned in HTML already parsed to <code> block.
93+ * -----------------------------------------------------------------------------------------------
94+ * 4. Indented code block
95+ * my-code
96+ *
97+ * is handled by default marked package and returned in HTML already parsed to <code> block.
98+ * -----------------------------------------------------------------------------------------------
99+ * 5. Special marked-tabs code fence
100+ * Each code block of following form
101+ * ```marked-tabs
102+ * <marked-tab header="Node.js" lang="javascript">
103+ * js-code
104+ * </marked-tab>
105+ *
106+ * <marked-tab header="Python" lang="python">
107+ * python-code
108+ * </marked-tab>
109+ * ```
110+ * is replaced by [apify-code-tabs]$INDEX[/apify-code-tabs] in the returned HTML where $INDEX is
111+ * an unique integer, to allow multiple marked-tabs components on the same page.
112+ *
113+ * For the example above codeTabsObjectPerIndex would contain key $INDEX with the following value
114+ * {
115+ * 'Node.js': {lang: 'javascript', code: 'js-code'},
116+ * 'Python': {lang: 'python', code: 'python-code'}
117+ * }
118+ *
119+ * i.e. each <marked-tab header="HEADER" lang="LANG">CODE</marked-tab> is turned into
120+ * HEADER: {lang: LANG, code: CODE} entry.
121+ *
122+ * Note that you have to use double quotation marks around HEADER and LANG, otherwise, the expression will not be matched
123+ * which results in unexpected and hard to debug errors.
124+ *
125+ * Each [apify-code-tabs]$INDEX[/apify-code-tabs] is meant to be later replaced be a react component
126+ * rendering the appropriate codeTabBlockObject returned by this function.
32127 * @param {string } markdown
33128 * @return {{ html: string, codeTabsObjectPerIndex: Object.<number, Object.<string, {language: string, code: string}>> } }
34129 */
35130export const apifyMarked = ( markdown ) => {
36131 const renderer = new marked . Renderer ( ) ;
37132 renderer . heading = customHeadingRenderer ;
38133 renderer . code = ( code , language ) => {
39- if ( language === 'marked-tabs' ) {
134+ if ( language ) {
40135 return code ;
41136 }
42137 return DEFAULT_MARKED_RENDERER . code ( code , language ) ;
43138 } ;
44139 const tokens = marked . lexer ( markdown ) ;
45140
46- /**
47- * Each code block of following form
48- * ```marked-tabs
49- * ... some code
50- * ```
51- * is replaced by [apify-code-tabs]INDEX[/apify-code-tabs] where index is
52- * an increasing integer starting at 0, to allow multiple marked-tabs components
53- * on the same page.
54- *
55- * [apify-code-tabs]INDEX[/apify-code-tabs] is meant to be later replaced be a react component
56- * rendering the appropriate codeTabBlockObject returned by this function.
57- */
58141 let markedTabTokenIndex = 0 ;
59142 const codeTabsObjectPerIndex = { } ;
60143 tokens . forEach ( ( token ) => {
61- if ( token . type === 'code' && token . lang === 'marked-tabs' ) {
62- codeTabsObjectPerIndex [ markedTabTokenIndex ] = codeTabObjectFromCodeTabMarkdown ( token . text ) ;
144+ if ( token . type === 'code' && token . lang ) {
145+ if ( token . lang === 'marked-tabs' ) {
146+ codeTabsObjectPerIndex [ markedTabTokenIndex ] = codeTabObjectFromCodeTabMarkdown ( token . text ) ;
147+ } else {
148+ const tabTitle = LANGUAGE_TO_TAB_TITLE [ token . lang ] || token . lang ;
149+ codeTabsObjectPerIndex [ markedTabTokenIndex ] = {
150+ [ tabTitle ] : {
151+ language : token . lang ,
152+ code : token . text ,
153+ } ,
154+ } ;
155+ }
63156 token . text = `[${ APIFY_CODE_TABS } ]${ markedTabTokenIndex } [/${ APIFY_CODE_TABS } ]` ;
64-
65157 markedTabTokenIndex ++ ;
66158 }
67159 } ) ;
0 commit comments