UNPKG

2.84 kBJavaScriptView Raw
1/*!
2 * Copyright (c) 2015-2017 Cisco Systems, Inc. See LICENSE file.
3 */
4
5import parse from './parse';
6import fs from 'fs';
7import traverse from 'babel-traverse';
8import doctrine from 'doctrine';
9import {isProgram} from 'babel-types';
10/**
11 * transform function which operates on each discovered example block
12 * @callback transformCallback
13 * @param {Object} options
14 * @param {ast} options.comment
15 * @param {string} options.name
16 * @param {string} options.filename
17 * @param {string} options.type
18 */
19
20/**
21 * Extracts comment blocks from the source code in the specified file
22 * @param {transformCallback} transform
23 * @param {string} filename
24 * @returns {Array<ast>}
25 */
26export default function extract(transform, filename) {
27 // eslint-disable-next-line no-sync
28 const code = fs.readFileSync(filename, {encoding: 'utf8'});
29
30 const ast = parse(code, {sourceFilename: filename});
31
32 const results = [];
33
34 let done = false;
35 traverse(ast, {
36 enter(path) {
37 if (path.node.leadingComments) {
38 path.node.leadingComments
39 .filter(isJSDocComment)
40 .forEach((comment) => {
41 const result = doctrine.parse(comment.value, {
42 unwrap: true,
43 sloppy: true,
44 recoverable: true,
45 lineNumbers: true
46 });
47
48 if (result.tags) {
49 result.tags.forEach((tag) => {
50 if (tag.title === 'example') {
51 results.push(transform({
52 comment: tag.description,
53 name: getNodeName(path.node),
54 filename: path.node.loc.filename,
55 type: path.node.type
56 }));
57 }
58 });
59 }
60 });
61 }
62 },
63 Program: {
64 exit(path) {
65 if (isProgram(path)) {
66 if (done) {
67 return;
68 }
69 path.pushContainer('body', results);
70 done = true;
71 }
72 }
73 }
74 });
75
76 return ast;
77}
78
79/**
80 * Extracts the name from the specified node
81 * @param {Node} node
82 * @returns {string}
83 */
84function getNodeName(node) {
85 if (node.id) {
86 return node.id.name;
87 }
88
89 if (node.key) {
90 return node.key.name;
91 }
92
93 throw new Error('Could not find name for node');
94}
95/**
96 * Indicates if the specified comment block is a doc block
97 * @param {CommentBlock} comment
98 * @returns {Boolean}
99 */
100function isJSDocComment(comment) {
101 const asterisks = comment.value.match(/^(\*+)/);
102 if (comment.value.startsWith('/*') && comment.value.endsWith('*/')) {
103 return false;
104 }
105
106 // eslint-disable-next-line
107 return (comment.type === `CommentBlock` || // estree
108 // eslint-disable-next-line
109 comment.type === `Block`) // get-comments / traditional
110 // eslint-disable-next-line
111 && asterisks && asterisks[ 1 ].length === 1;
112}