1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | var TreeWalker = require("./tree-walker");
|
7 | var UglifyJS = require("uglify-js");
|
8 | var Markdown = require("./tfw-md");
|
9 | var JSON = require("./tolojson");
|
10 |
|
11 | var declarations = {};
|
12 | var tw = new TreeWalker();
|
13 | var lastComments = undefined;
|
14 | var rxTag = /^[ \t]*@([a-zA-Z]+)[ \t]*/;
|
15 | var rxParam = /^(\{[^\}]*\})?[ \t]*([\w$_][\w$_0-9\.]*)[ \t-]*/;
|
16 |
|
17 | function comments(node) {
|
18 | var com = node.start.comments_before;
|
19 | if (!Array.isArray(com) || com.length == 0) return undefined;
|
20 | com = stripComments( com[com.length - 1].value.trim() );
|
21 | var lines = com.split("\n");
|
22 | var tags = {$summary: "", $full: ""};
|
23 | var tag = "$summary";
|
24 | lines.forEach(
|
25 | function(line) {
|
26 | var m = line.match(rxTag);
|
27 | var item;
|
28 | if (m) {
|
29 | line = line.substr(m[0].length);
|
30 | tag = "$" + m[1].toLowerCase();
|
31 | if (typeof tags[tag] === 'undefined') {
|
32 | tags[tag] = [];
|
33 | }
|
34 | if (tag == '$param') {
|
35 | m = line.match(rxParam);
|
36 | item = {content: ""};
|
37 | if (m) {
|
38 | if (typeof m[1] === 'string') {
|
39 | item.type = m[1].substr(1, m[1].length - 2).trim();
|
40 | }
|
41 | item.name = m[2].trim();
|
42 | line = line.substr(m[0].length);
|
43 | }
|
44 | tags[tag].push(item);
|
45 | } else {
|
46 | tags[tag].push("");
|
47 | }
|
48 | }
|
49 | if (tag == '$summary' && line.trim() == '') {
|
50 |
|
51 | tags.$full = tags.$summary;
|
52 | tag = '$full';
|
53 | } else {
|
54 | line += "\n";
|
55 | item = tags[tag];
|
56 | if (typeof item === 'string') {
|
57 | tags[tag] += line;
|
58 | }
|
59 | else if (Array.isArray(item)) {
|
60 | var arr = item;
|
61 | item = item[arr.length - 1];
|
62 | if (typeof item === 'string') {
|
63 | arr[arr.length - 1] += line;
|
64 | }
|
65 | else if (typeof item.content === 'string') {
|
66 | item.content += line;
|
67 | }
|
68 | }
|
69 | }
|
70 | }
|
71 | );
|
72 | if (Array.isArray(tags.$example)) {
|
73 |
|
74 | var i, example;
|
75 | for (i = 0 ; i < tags.$example.length ; i++) {
|
76 | example = tags.$example[i].trim();
|
77 | tags.$example[i] = "```js\n" + example + "\n```";
|
78 | }
|
79 | }
|
80 | var key, val;
|
81 | for (key in tags) {
|
82 | val = tags[key];
|
83 | if (typeof val === 'string') {
|
84 | tags[key] = Markdown.toHTML(val.trim());
|
85 | }
|
86 | else if (Array.isArray(val)) {
|
87 | val.forEach(
|
88 | function(itm, idx) {
|
89 | if (typeof itm === 'string') {
|
90 | val[idx] = Markdown.toHTML(itm.trim());
|
91 | }
|
92 | else if (typeof itm.content === 'string') {
|
93 | itm.content = Markdown.toHTML(itm.content.trim());
|
94 | }
|
95 | }
|
96 | );
|
97 | }
|
98 | }
|
99 | return tags;
|
100 | }
|
101 |
|
102 | function getArgs(node) {
|
103 | var args = [];
|
104 | var argnames = node.argnames;
|
105 | if (Array.isArray(argnames)) {
|
106 | argnames.forEach(
|
107 | function(arg) {
|
108 | args.push(arg.name);
|
109 | }
|
110 | );
|
111 | }
|
112 | return args;
|
113 | }
|
114 |
|
115 | function getFunction(node) {
|
116 | var name = node.name;
|
117 | var com = comments(node);
|
118 | if (typeof com === 'undefined' || com.length == 0) {
|
119 | com = lastComments;
|
120 | }
|
121 | return {
|
122 | TYPE: "Function",
|
123 | name: name,
|
124 | comments: com,
|
125 | args: getArgs(node)
|
126 | };
|
127 | }
|
128 |
|
129 | function parseFunction(node) {
|
130 | var obj = getFunction(node.value);
|
131 | declarations[node.name.name] = obj;
|
132 | }
|
133 |
|
134 | function parseMethod(node) {
|
135 | var name = node.body.left.expression.expression.name;
|
136 | var dec = declarations[name];
|
137 | if (dec) {
|
138 | dec.TYPE = "Class";
|
139 | if (typeof dec.methods !== 'object') {
|
140 | dec.methods = {};
|
141 | }
|
142 | var method = node.body.left.property;
|
143 | dec.methods[method] = {
|
144 | comments: comments(node),
|
145 | args: getArgs(node.body.right)
|
146 | };
|
147 | }
|
148 | }
|
149 |
|
150 | function parseVar(tree) {
|
151 | var actions = {
|
152 | "[VarDef]value/[Function]": parseFunction
|
153 | };
|
154 | tree.definitions.forEach(
|
155 | function(node) {
|
156 | tw.action(node, actions);
|
157 | }
|
158 | );
|
159 | }
|
160 |
|
161 | function parseModuleExports(node) {
|
162 | var exports = {
|
163 | comments: comments(node) || lastComments
|
164 | };
|
165 | declarations.exports = exports;
|
166 | var right = node.body.right;
|
167 | if (right.TYPE == "SymbolRef") {
|
168 | exports.value = declarations[right.name];
|
169 | return;
|
170 | }
|
171 | }
|
172 |
|
173 | function parseExportsAtt(node) {
|
174 | if (typeof declarations.exports !== 'object'
|
175 | || declarations.exports.TYPE != 'Object')
|
176 | {
|
177 | declarations.exports = {
|
178 | TYPE: "Object",
|
179 | attributes: {}
|
180 | };
|
181 | }
|
182 | var name = node.body.left.property;
|
183 | var exports = {
|
184 | comments: comments(node),
|
185 | value: undefined
|
186 | };
|
187 | declarations.exports.attributes[name] = exports;
|
188 | var right = node.body.right;
|
189 | if (right.TYPE == "SymbolRef") {
|
190 | exports.value = declarations[right.name];
|
191 | return;
|
192 | }
|
193 | if (right.TYPE == "Function") {
|
194 | exports.value = {
|
195 | TYPE: "Function",
|
196 | args: getArgs(right)
|
197 | };
|
198 | }
|
199 | }
|
200 |
|
201 | function parseAttribute(node) {
|
202 | var objName = node.body.left.expression.name;
|
203 | var attName = node.body.left.property;
|
204 | var f = node.body.right;
|
205 | if (f.TYPE == 'Function') {
|
206 | var dec = declarations[objName];
|
207 | if (typeof dec.statics !== 'object') {
|
208 | dec.statics = {};
|
209 | }
|
210 | dec.statics[attName] = getFunction(f);
|
211 | if (typeof dec.statics[attName].comments !== 'object') {
|
212 | var com = comments(node) || lastComments;
|
213 | dec.statics[attName].comments = com;
|
214 | }
|
215 | }
|
216 | }
|
217 |
|
218 |
|
219 |
|
220 |
|
221 | exports.stripComments = stripComments;
|
222 |
|
223 | function stripComments(code) {
|
224 |
|
225 | code = code.substr( 2 );
|
226 |
|
227 | if( code.charAt(0) == '*' ) {
|
228 | code = ' ' + code;
|
229 | }
|
230 |
|
231 | var match = /^[ \t]*\*+[ \t]*\n/.exec( code );
|
232 | if( match ) {
|
233 | code = code.substr( match[0].length );
|
234 | }
|
235 |
|
236 | var pos = code.lastIndexOf( '*/' );
|
237 | if( pos > -1 ) {
|
238 | code = code.substr( 0, pos );
|
239 | }
|
240 |
|
241 | var lines = code.split( '\n' );
|
242 |
|
243 | var rxLine = /^ \*[ ]+[^ \n]/;
|
244 |
|
245 | var prefixLength = 100;
|
246 | lines.forEach(function (line) {
|
247 | var m = rxLine.exec( line );
|
248 | if( m ) {
|
249 | prefixLength = Math.min( prefixLength, m[0].length - 1 );
|
250 | }
|
251 | });
|
252 |
|
253 | var result = [];
|
254 | lines.forEach(function (line) {
|
255 | var m = rxLine.exec( line );
|
256 | if( m ) {
|
257 | result.push( line.substr( prefixLength ) );
|
258 | } else {
|
259 | result.push( line );
|
260 | }
|
261 | });
|
262 | return result.join( '\n' ).trimRight();
|
263 | };
|
264 |
|
265 | exports.parseDoc = function(code) {
|
266 | declarations = {};
|
267 | var tree = UglifyJS.parse(code);
|
268 | var items = tree.body;
|
269 | var actions = {
|
270 | "[Var]definitions": parseVar,
|
271 | "[SimpleStatement]body/": {
|
272 | "[Assign]left/[Dot]expression/[Dot][property=prototype]": parseMethod,
|
273 | "[Assign][operator==]left/": {
|
274 | "[Dot][property=exports]expression/[SymbolRef][name=module]": parseModuleExports,
|
275 | "[Dot]expression/[name=exports]": parseExportsAtt,
|
276 | "[Dot]expression/[SymbolRef]": parseAttribute
|
277 | }
|
278 | }
|
279 | };
|
280 | items.forEach( function(node) {
|
281 | lastComments = comments(node);
|
282 | tw.action(node, actions);
|
283 | } );
|
284 | return {
|
285 | exports: declarations.exports
|
286 | };
|
287 | };
|