UNPKG

5.93 kBJavaScriptView Raw
1
2/**
3 * This is the __module__ description for the `YUIDoc` module.
4 *
5 * @module firedoc
6 * @main firedoc
7 * @submodule helpers
8 * @submodule utils
9 * @submodule tags
10 * @submodule digesters
11 */
12
13const _ = require('underscore');
14const path = require('path');
15const fs = require('graceful-fs');
16const readdirp = require('readdirp');
17const debug = require('debug')('firedoc:firedoc');
18
19/**
20 * Firedoc main class
21 *
22 * @class Firedoc
23 * @constructor
24 * @param config The config object
25 */
26function Firedoc (config) {
27 if (!(this instanceof Firedoc)) {
28 return new Firedoc(config);
29 }
30
31 /**
32 * Holds the number of files that we are processing.
33 * @property filecount
34 * @type Boolean
35 * @private
36 */
37 this.filecount = 0;
38 /**
39 * Holder for the list of files we are processing.
40 * @property filemap
41 * @type Object
42 * @private
43 */
44 this.filemap = {};
45 /**
46 * Holder for the list of directories we are processing.
47 * @property dirmap
48 * @type Object
49 * @private
50 */
51 this.dirmap = {};
52
53 /**
54 * Internal holder for configuration options.
55 * @property options
56 * @type Object
57 * @private
58 */
59 this.options = {
60 writeJSON: true,
61 extensions: '.js',
62 excludes: [
63 '.git',
64 '.svn',
65 'CVS',
66 'build_rollup_tmp',
67 'build_tmp',
68 'node_modules'
69 ],
70 norecurse: false,
71 paths: ['./'],
72 cwd: process.cwd(),
73 http: false,
74 dest: path.join(process.cwd(), 'out'),
75 theme: path.join(__dirname, '../themes/default'),
76 syntaxtype: 'js'
77 };
78
79 // setup options
80 var cwd = config.cwd || this.options.cwd;
81 var pkg;
82 var firedocOptions;
83 if (fs.existsSync(cwd + '/yuidoc.json')) {
84 pkg = require(cwd + '/yuidoc.json');
85 firedocOptions = pkg.options;
86 delete pkg.options;
87 } else if (fs.existsSync(cwd + '/package.json')) {
88 pkg = require(cwd + '/package.json');
89 firedocOptions = pkg.firedoc;
90 delete pkg.firedoc;
91 //compatible with firedoc.options
92 if (pkg.yuidoc && pkg.yuidoc.options) {
93 firedocOptions = _.extend(firedocOptions, pkg.yuidoc.options);
94 delete pkg.yuidoc;
95 }
96 // handle with `envOption`
97 var envOption = pkg['firedoc.' + process.env.NODE_ENV];
98 if (envOption) {
99 firedocOptions = _.extend(firedocOptions, envOption);
100 delete pkg['firedoc.' + process.env.NODE_ENV];
101 }
102 }
103
104 // extends default options with firedocOptions
105 if (firedocOptions) {
106 this.options = _.extend(this.options, firedocOptions);
107 }
108 this.options.project = pkg;
109
110 // setup options from config
111 for (var key in config) {
112 if (!_.isUndefined(config[key])) this.options[key] = config[key];
113 }
114
115 if (_.isString(this.options.paths)) {
116 this.options.paths = [this.options.paths];
117 }
118 if (this.options.markdown === true) {
119 this.options.theme = path.join(__dirname, '../themes/markdown');
120 }
121 if (this.options.outdir) {
122 this.options.dest = this.options.outdir;
123 }
124 if (!path.isAbsolute(this.options.dest)) {
125 this.options.dest = path.join(process.cwd(), this.options.dest);
126 }
127};
128exports.Firedoc = Firedoc;
129
130Firedoc.prototype = {
131
132 /**
133 * Walks the paths and parses the directory contents
134 *
135 * @method walk
136 * @param {Function} next
137 */
138 walk: function (next) {
139 var self = this;
140 var paths = _.clone(self.options.paths);
141 var curr = paths.shift() || './';
142 self.walkOne(curr, onfinish);
143
144 function onfinish (err) {
145 if (err) throw err;
146 curr = paths.shift();
147 if (_.isString(curr))
148 return self.walkOne(curr, onfinish);
149 else
150 return next();
151 }
152 },
153
154 /**
155 * Walk one path
156 *
157 * @method walkOne
158 * @param {String} path - The path to walk
159 * @param {Function} next
160 */
161 walkOne: function (root, next) {
162 debug('walking path: ' + root);
163 if (!path.isAbsolute(root)) {
164 root = path.join(process.cwd(), root);
165 }
166
167 var self = this;
168 readdirp(
169 {
170 root: root,
171 fileFilter: '*.@(js|rs|ts|coffee)',
172 directoryFilter: self.options.excludes.map(
173 function (ex) {
174 return '!' + ex;
175 }
176 )
177 }
178 )
179 .on('data', function (entry) {
180 var text = fs.readFileSync(entry.fullPath, 'utf8');
181 self.filecount += 1;
182 self.filemap[entry.fullPath] = text.replace(/\r?\n|\r/g, '\n');
183 self.dirmap[entry.fullPath] = entry.fullParentDir;
184 })
185 .on('end', next)
186 .on('error', next);
187 },
188
189 lint: function (warnings) {
190 var code = 0, count = 0;
191 if (warnings && warnings.length) {
192 code = 1;
193 console.log('FireDoc found', warnings.length, 'lint errors in your docs');
194 warnings.forEach(function (item) {
195 count++;
196 console.log('#' + count, item.message, item.line + '\n');
197 });
198 }
199 },
200
201 /**
202 * Process the config, walk the file tree and write out the JSON data.
203 * @method build
204 * @param {Function} callback
205 */
206 build: function (callback) {
207 debug('Starting from: ' + this.options.path);
208 var self = this;
209 this.walk(function () {
210 var parser = require('./docparser');
211 var builder = require('./builder');
212 var ast = parser.parse(
213 self.options.syntaxtype,
214 self.filemap,
215 self.dirmap);
216
217 debug('Parsing completed');
218 if (self.options.lint) {
219 debug('lint the warnings from ast');
220 self.lint(ast.warnings);
221 if (_.isFunction(callback)) return callback(ast.warnings);
222 }
223 if (self.options.parseOnly) {
224 debug('skip the build because parse only');
225 if (_.isFunction(callback)) callback(null, ast, self.options);
226 return;
227 }
228 builder.compile(ast, self.options, callback);
229 });
230 }
231};