|
1 | 1 | import { escapeHtml } from './strings'; |
| 2 | +import { docGlobal } from './doc-tokens/doc-globals'; |
| 3 | +import { docName, docClass, docMethod } from './doc-tokens/doc-names'; |
| 4 | +import { docType } from './doc-tokens/doc-types'; |
2 | 5 |
|
3 | | -export function methodDoc(code, language) { |
| 6 | +export function methodDoc(code, language, render) { |
4 | 7 | try { |
5 | | - let json = JSON.parse(code); |
| 8 | + const json = JSON.parse(code); |
6 | 9 |
|
7 | 10 | // support language specific overrides |
8 | 11 | if (json.languages && json.languages[language]) { |
9 | 12 | Object.assign(json, json.languages[language]); |
10 | 13 | } |
11 | 14 |
|
12 | | - const md = []; |
| 15 | + const html = []; |
13 | 16 |
|
14 | 17 | if (!json.examplesOnly) { |
15 | 18 | if (json.method) { |
16 | | - md.push(methodHeader(json)); |
| 19 | + html.push(methodHeader(json, language)); |
17 | 20 | } |
18 | 21 | if (json.desc) { |
19 | | - md.push(json.desc); |
| 22 | + html.push(render(json.desc)); |
20 | 23 | } |
21 | 24 |
|
22 | | - md.push('```%doc'); |
23 | | - |
| 25 | + html.push(`<div class="block block--doc">`); |
| 26 | + html.push(`<dl>`); |
24 | 27 | if (json.args) { |
25 | | - md.push('Parameters:'); |
26 | | - md.push(parameters(json)); |
| 28 | + html.push(`<dt>Parameters</dt>`); |
| 29 | + html.push(parameters(json, language).map(markdownDefD(render)).join('\n')); |
27 | 30 | } |
28 | | - md.push('Return Value:'); |
29 | | - md.push(returnType(json)); |
| 31 | + |
| 32 | + html.push(`<dt>Return Value</dt>`); |
| 33 | + html.push(`<dd>`); |
| 34 | + html.push(render(returnType(json, language))); |
| 35 | + html.push(`</dd>`); |
| 36 | + |
30 | 37 | if (json.constraints && json.constraints.length) { |
31 | | - md.push('Constraints:'); |
32 | | - md.push(json.constraints.join('\n')); |
| 38 | + html.push(`<dt>Constraints</dt>`); |
| 39 | + html.push(json.constraints.map(markdownDefD(render)).join('\n')); |
33 | 40 | } |
34 | 41 | if (json.errors && json.errors.length) { |
35 | | - md.push('Errors:'); |
36 | | - md.push(json.errors.join('\n')); |
| 42 | + html.push(`<dt>Errors</dt>`); |
| 43 | + html.push(json.errors.map(markdownDefD(render)).join('\n')); |
37 | 44 | } |
38 | | - md.push('```'); |
| 45 | + |
| 46 | + html.push(`</dl>`); |
| 47 | + html.push(`</div>`); |
39 | 48 | } |
40 | 49 |
|
41 | 50 | if (json.examples && json.examples.length) { |
42 | | - md.push('```%doc-block'); |
43 | | - md.push('#### Examples'); |
44 | | - md.push(exampleHeader(json)); |
45 | | - md.push(exampleRows(json)); |
46 | | - md.push('```'); |
| 51 | + html.push(`<div class="block block--doc-block">`); |
| 52 | + html.push(`<h4>Examples</h4>`); |
| 53 | + html.push(`<table>`); |
| 54 | + html.push(`<thead>`); |
| 55 | + html.push(exampleHeader(json)); |
| 56 | + html.push(`</thead>`); |
| 57 | + html.push(`<tbody>`); |
| 58 | + html.push(exampleRows(json)); |
| 59 | + html.push(`</tbody>`); |
| 60 | + html.push(`</table>`); |
| 61 | + html.push(`</div>`); |
47 | 62 | } |
48 | 63 |
|
49 | | - return md.join('\n'); |
| 64 | + return html.join('\n'); |
50 | 65 | } catch (ex) { |
51 | | - return '`Failed to render %jsonblock: ' + ex.message + '`'; |
| 66 | + return `<code>Failed to render %method-doc: ${escapeHtml(ex.message)}</code>`; |
52 | 67 | } |
53 | 68 | } |
54 | 69 |
|
| 70 | +const markdownDefD = (render) => (dd) => `<dd>${render(dd)}</dd>`; |
| 71 | + |
55 | 72 | function hasExampleNames(json) { |
56 | 73 | return json.examples && json.examples.filter((e) => !!e.name).length > 0; |
57 | 74 | } |
| 75 | + |
58 | 76 | function exampleRows(json) { |
59 | 77 | const hasExamples = hasExampleNames(json); |
60 | | - return json.examples.map((v) => exampleRow(json, v, hasExamples)).join('\n'); |
| 78 | + return json.examples.map((v) => `<tr>${exampleRow(v, hasExamples)}</tr>`).join('\n'); |
61 | 79 | } |
62 | 80 |
|
63 | | -function exampleRow(json, example, hasExamples) { |
64 | | - let md = ''; |
| 81 | +function exampleRow(example, hasExamples) { |
| 82 | + const tds = []; |
65 | 83 | if (hasExamples) { |
66 | | - const name = example.name; |
67 | | - md = `*${name || 'Example'}*|`; |
68 | | - } |
69 | | - |
70 | | - if (example.args) { |
71 | | - md += example.args.map((arg) => formatExampleValue(arg)).join('|'); |
| 84 | + tds.push(`<em>${example.name || 'Example'}</em>`); |
72 | 85 | } |
73 | | - md += `|${formatExampleValue(example.returns) || ''}`; |
74 | | - return md; |
| 86 | + if (example.args) tds.push(...example.args.map(formatExampleValue)); |
| 87 | + tds.push(formatExampleValue(example.returns) || ''); |
| 88 | + return tds.map((t) => `<td>${t}</td>`).join(''); |
75 | 89 | } |
76 | 90 |
|
77 | 91 | function formatExampleValue(value) { |
78 | 92 | return `<code>${JSON.stringify(value)}</code>`; |
79 | 93 | } |
80 | 94 |
|
81 | 95 | function exampleHeader(json) { |
82 | | - const line1 = []; |
83 | | - const line2 = []; |
84 | | - if (hasExampleNames(json)) { |
85 | | - line1.push('|'); |
86 | | - line2.push(''); |
87 | | - } |
88 | | - |
89 | | - Object.keys(getArgs(json)).forEach((key) => { |
90 | | - line1.push(key); |
91 | | - line2.push(''); |
92 | | - }); |
93 | | - line1.push('Return Value'); |
94 | | - return `${line1.join('|')}\n-${line2.join('|-')}|-`; |
| 96 | + const keys = hasExampleNames(json) ? [''] : []; |
| 97 | + keys.push(...Object.keys(getArgs(json))); |
| 98 | + keys.push('Return Value'); |
| 99 | + return `<tr>${keys.map((k) => `<th>${k}</th>`).join('')}</tr>`; |
95 | 100 | } |
96 | 101 |
|
97 | 102 | function getArgs(json) { |
98 | 103 | return json.args || json.params || json.parameters || {}; |
99 | 104 | } |
100 | 105 |
|
101 | | -function methodName(json) { |
102 | | - let globalName = json.global !== false ? `@@docGlobal:${json.global || 'Challenge'}.` : ''; |
| 106 | +function methodName(json, language) { |
103 | 107 | // if a class is provided, it will always be shown and overrides global |
104 | | - if (json.class) { |
105 | | - globalName = `@@docClass:${json.class}.`; |
106 | | - } |
107 | | - |
108 | | - return `${globalName}@@docMethod:${json.method}`; |
| 108 | + const prefix = json.class |
| 109 | + ? docClass(json.class, language) |
| 110 | + : json.global !== false |
| 111 | + ? docGlobal(json.global || 'Challenge', language) |
| 112 | + : ''; |
| 113 | + return `${prefix}${prefix ? '.' : ''}${docMethod(json.method, language)}`; |
109 | 114 | } |
110 | 115 |
|
111 | | -function methodHeader(json) { |
112 | | - const args = Object.keys(getArgs(json)).map((key) => `\`@@docName:${key}\``); |
113 | | - return `### \`${methodName(json)}\`(${args.join(', ')})`; |
| 116 | +function methodHeader(json, language) { |
| 117 | + const args = Object.keys(getArgs(json)).map((key) => `<code>${docName(key, language)}</code>`); |
| 118 | + return `<h3><code>${methodName(json, language)}</code>(${args.join(', ')})</h3>`; |
114 | 119 | } |
115 | 120 |
|
116 | | -function parameters(json) { |
| 121 | +function parameters(json, language) { |
117 | 122 | const args = getArgs(json); |
118 | | - const params = Object.keys(args).map((key) => { |
| 123 | + return Object.keys(args).map((key) => { |
119 | 124 | const arg = args[key]; |
120 | 125 | const type = typeof arg === 'string' ? arg : arg.type; |
121 | | - let md = `@@docName:${key}: ${formatDocType(json, type, 'String')}`; |
122 | | - if (arg.desc) { |
123 | | - md += ` - ${arg.desc}`; |
124 | | - } |
125 | | - |
126 | | - return md; |
| 126 | + const md = `${docName(key, language)}: ${formatDocType(json, type, 'String', language)}`; |
| 127 | + return md + (arg.desc ? ` - ${arg.desc}` : ''); |
127 | 128 | }); |
128 | | - |
129 | | - return params.join('\n'); |
130 | 129 | } |
131 | 130 |
|
132 | | -function returnType(json) { |
| 131 | +function returnType(json, language) { |
133 | 132 | if (json.returns) { |
134 | 133 | const type = typeof json.returns === 'string' ? json.returns : json.returns.type; |
135 | | - let md = formatDocType(json, type, 'void'); |
136 | | - if (json.returns.desc) { |
137 | | - md += ` - ${json.returns.desc}`; |
138 | | - } |
139 | | - |
140 | | - return md; |
| 134 | + const md = formatDocType(json, type, 'void', language); |
| 135 | + return md + (json.returns.desc ? ` - ${json.returns.desc}` : ''); |
141 | 136 | } |
142 | | - return '@@docType:void'; |
| 137 | + return docType('void', language); |
143 | 138 | } |
144 | 139 |
|
145 | | -function formatDocType(json, type, defaultValue) { |
| 140 | +function formatDocType(json, type, defaultValue, language) { |
146 | 141 | if (json.formatTypes === false) { |
147 | 142 | return `<dfn class="doc-type">${escapeHtml(type)}</dfn>`; |
148 | 143 | } |
149 | | - |
150 | | - return `@@docType:${type || defaultValue || 'null'}`; |
| 144 | + return docType(type || defaultValue || 'null', language); |
151 | 145 | } |
0 commit comments