Генератор документации на Node.js

от
Прочие языки   node.js, cloud9, c9, javascript, js

В какой-то момент, при создании очередного модуля к OwnLang, возникла сложность в поддержании документации. Сначала она писалась вручную в Markdown и выглядела подобным образом:
  1. ## Модуль-types
  2.  
  3. Содержит функции для проверки и преобразования типов.
  4.  
  5. ### Функции
  6.  
  7. `typeof(value)` - возвращает тип переданного в функцию значения.
  8.  
  9. ```own
  10. print typeof(1) // 1 (NUMBER)
  11. print typeof("text") // 2 (STRING)
  12. print typeof([]) // 3 (ARRAY)
  13. ```
  14.  
  15. `string(value)` - преобразует значение в строку.
  16.  
  17. ```own
  18. print typeof(string(1)) // 2 (STRING)
  19. ```

Мне же нужно было вести список модулей на русском и английском языке, сохранять в HTML в двух вариантах (все модули и модули только для Android), в Markdown в один файл и в несколько файлов для GitBook (на каждый модуль один файл, причём язык подсветки нужно было менять с own на scala).

Вырисовывалась такая структура
  1. Модули:
  2.  - Модуль 1
  3.    - название
  4.    - поддержка (все платформы / ПК / Android)
  5.    - описание
  6.    - описание на русском
  7.    - функции
  8.      - функция 1
  9.        - название
  10.        - список аргументов
  11.        - описание
  12.        - описание на русском
  13.        - пример
  14.    - константы
  15.    - типы данных
  16.  - Модуль 2

Поочерёдно загрузив каждый модуль, можно программно получить список все модулей и его функций, после чего сгенерировать файл с приведённой структурой.

Так как описания и примеры нужно заполнять вручную, вариант сохранять в JSON не подходит - экранировать символы и заменять переносы строк на \n не очень удобно. Поэтому был взят формат YAML.
  1. ---
  2.  -
  3.     name: "types"
  4.     scope: "both"
  5.     desc: "Contains functions for type checking and conversion"
  6.     desc_ru: "Содержит функции для проверки и преобразования типов"
  7.     functions:
  8.       -
  9.         name: "string"
  10.         args: "value"
  11.         desc: "converts value to string"
  12.         desc_ru: "преобразует значение в строку"
  13.         example: |-
  14.           print typeof(string(1)) // 2 (STRING)
  15.       -
  16.         name: "typeof"
  17.         args: "value"
  18.         desc: "returns the type of value"
  19.         desc_ru: "возвращает тип переданного значения"
  20.         example: |-
  21.           print typeof(1) // 1 (NUMBER)
  22.           print typeof("text") // 2 (STRING)
  23.           print typeof([]) // 3 (ARRAY)

Для сравнения, вот так пришлось бы писать в случае с JSON.

Из такого представления можно легко сконвертировать описания в Markdown:
  1. {name}({args}) - {desc}
  2.  
  3. ```own
  4. {example}
  5. ```

Для этого нужен парсер markdown, парсер yaml и библиотека для подсветки синтаксиса (для подсветки кода в html). Всё это, а также многое другое, уже реализовано для Node.js, на котором и напишем генератор.

Так как Node.js нужен лишь для простой задачи, то устанавливать его и качать редакторы будет излишним, можно обойтись облачными платформами, например Cloud 9

При создании нового проекта, выбираем Node.js и получаем настроенный проект с онлайн-редактором. Можно сразу же писать код.

shot-20160504T193422.png

Первым делом устанавливаем необходимые модули:
npm install marked - парсер Markdown
npm install yamljs - парсер Yaml
npm install highlight.js - известная библиотека для подсветки синтаксиса
npm install underscore - underscore.js незаменимая вещь для коллекций
npm install string-template - шаблонизатор, чтоб не конкатенировать строки

После этого можно приступать к разработке.

Парсим YAML
  1. const yaml = require('yamljs');
  2. let modules = yaml.load('modules.yml');

Теперь в modules у нас полная иерархия описания модулей. Пройдёмся по массиву и сгенерируем md для двух языков.
  1. const fs = require('fs'),
  2.       underscore = require('underscore');
  3. const langs = ["en", "ru"];
  4. underscore.each(langs, function(lang) {
  5.     const path = "/modules_" + lang;
  6.     const file = fs.createWriteStream(path + '.md');
  7.     underscore.each(modules, function(module) {
  8.         file.write('\n\n');
  9.         processMarkdownModule(file, lang, module);
  10.         file.write('\n');
  11.     });
  12.     file.end();
  13.     console.log(path + '.md generated.');
  14. });
underscore.each перебирает все элементы массива, аналог for-in, только в функциональном стиле.
fs.createWriteStream создаёт файл для записи.

Далее записываем в файл нужные данные при помощи шаблонизатора.
  1. file.write(format('## {name} {#{anchorname}}\n\n', {
  2.     name: module.name,
  3.     anchorname: module.name
  4. }));
  5. file.write(format('{desc}\n', {desc: getValue(module, 'desc', lang)}));

Теперь у нас генерируется полноценный текстовый файл, мы вольны фильтровать модули, сортировать функции, чтобы на выходе получить желаемый результат.

shot-20160504T211011.png

Теперь сгенерируем html из Markdown-файла.
  1. const hljs = require('highlight.js');
  2. const marked = require('marked');
  3. marked.setOptions({
  4.     highlight: code => hljs.highlight('own', code).value
  5. });
  6. underscore.each(langs, function(lang) {
  7.     const path = "/modules_" + lang;
  8.     fs.readFile(path + '.md', 'utf8', function (err, data) {
  9.         if (err) return console.log(err);
  10.         const file = fs.createWriteStream(path + '.html');
  11.         file.write(marked(data, {renderer: markedRenderer}));
  12.         file.end();
  13.         console.log(path + '.html generated.');
  14.     });
  15. });

Можно реализовать свою подсветку синтаксиса в highlight.js на основе уже существующих языков. Они находятся тут /node_modules/highlight.js/lib/languages.

shot-20160504T211938.png

Вот и всё, генератор документации готов. Можно добавить побольше настроек, возможность запуска из командной строки, подключение хедера и футера к каждому файлу, что и было сделано в конечном результате.

Полный исходник (245 строк)

Файлы проекта: docbuilder.zip
+8   8   0
1676