Coverage

100%
147
147
0

lib/parser.js

100%
88
88
0
LineHitsSource
1/*
2 * @author: Hsiaoming Yang
3 */
4
5
6function parse(code) {
713 code = code + '\n';
813 var lines = code.split(/\r\n|\r|\n/);
913 var sections = [];
10
1113 var comments = [];
1213 var inComment = false, section;
1313 lines.forEach(function(line) {
14 // comment block
15195 if (/^\/\*/.test(line)) {
169 inComment = true;
17 }
18195 if (/\*\/$/.test(line)) {
199 inComment = false;
209 line = line.replace(/\*\/$/, '');
219 comments.push(cleanLine(line));
229 return;
23 }
24
25186 if (/^\/\//.test(line)) {
2633 comments.push(cleanLine(line));
2733 return;
28 }
29
30153 if (inComment) {
3180 comments.push(cleanLine(line));
3280 return;
33 }
34
35 // it is not comment now
3673 if (comments.length) {
3713 section = parseSection(comments);
3813 if (section) {
3910 sections.push(section);
40 }
41 // reset storage
4213 comments = [];
43 }
44 });
4513 return sections;
46}
471module.exports = parse;
48
49
50function parseSection(lines) {
5113 var name, title, description = [], modifiers = [], examples = [];
52
5313 var parsed, exampleStart, exampleFile;
5413 lines.forEach(function(line) {
55 // example file is the last line of comment
56130 if (exampleFile) return;
57
58114 if (!name && line.trim()) {
5913 parsed = parseTitle(line);
6013 if (parsed) {
6110 name = parsed.name;
6210 title = parsed.title;
6310 return;
64 }
65 }
66107 if (!name) return;
67
68 // parse modifier
69 // :hover - Button when hover
70101 if (/^((?:\:|\.)[a-zA-Z0-9\:]+)\s+\-\s+(.*)$/.exec(line)) {
7127 return modifiers.push({name: RegExp.$1, description: RegExp.$2});
72 }
73
7474 if (/^Examples\:/.test(line.trim())) {
759 if (line.trim() === 'Examples:') {
766 exampleStart = true;
77 } else {
783 exampleFile = line.trim().replace(/^Examples\:/, '').trim()
79 }
809 return;
81 }
82
8365 if (!exampleStart && /^\s{2,}/.test(line)) {
841 exampleStart = true;
85 }
8665 if (exampleStart) {
8716 examples.push(line);
8816 return;
89 }
90
9149 description.push(line);
92 });
9316 if (!name) return null;
94
9510 var ret = {name: name, title: title, modifiers: modifiers};
96
9710 ret.description = description.join('\n').replace(/^\n*\s*/, '');
98
9910 if (exampleFile) {
1003 ret.exampleFile = exampleFile;
101 } else {
1027 ret.html = formatCode(examples);
1037 ret.examples = parseExamples(ret.html, modifiers);
104 }
10510 return ret;
106}
107
108
109function cleanLine(line) {
110 // clean comment line
111122 line = line.replace(/^\/\//, '');
112122 line = line.replace(/^\s*\*/, '');
113122 line = line.replace(/^\/\*/, '');
114122 line = line.replace(/\*\/$/, '');
115122 line = line.replace(/^\s{0,1}/, '');
116122 return line.trimRight();
117}
118
119
120function parseTitle(line) {
121 // the first line can be title
12213 if (/^(\d+\.\d+(?:\.\d+)?)\s+(.*)$/.exec(line)) {
12310 var name = RegExp.$1;
12410 var title = RegExp.$2;
12510 return {name: name, title: title};
126 }
1273 return null;
128}
129
130function parseExamples(code, modifiers) {
131 // <button class="classy {{modifier}}">Button</button>
1327 var examples = [];
133
134 // we should have an example without modifier
1357 examples.push({
136 name: '',
137 code: code.replace(/\{\{modifier\}\}/g, '')
138 });
139
1407 if (!modifiers) return examples;
141
1427 var name, value;
1437 modifiers.forEach(function(modifier) {
14421 name = modifier.name;
14521 value = code.replace(
146 /\{\{modifier\}\}/g,
147 name.replace(':', ' pseudo-class-').replace('.', ' ')
148 );
14921 examples.push({name: name, code: value});
150 });
1517 return examples;
152}
153
154function formatCode(lines) {
1557 var indent;
1567 var codes = [];
157
1587 lines.forEach(function(line) {
15916 if (indent === undefined && line.trim()) {
1607 var match = line.match(/\S/);
1617 if (match) {
1627 indent = new RegExp('^\\s{' + match.index + '}');
163 }
164 }
16516 if (!indent) return;
16616 codes.push(line.replace(indent, ''));
167 });
1687 return codes.join('\n');
169}
170

lib/style.js

100%
59
59
0
LineHitsSource
11var fs = require('fs');
21var path = require('path');
31var parser = require('./parser');
4
5
6function styleFile(filepath, options, callback) {
74 var encoding = 'utf8';
84 if (!callback) {
92 callback = options;
10 } else {
112 encoding = options.encoding || 'utf8';
12 }
134 fs.readFile(filepath, encoding, function(err, data) {
144 if (err) throw err;
154 if (callback) {
164 var sections = parser(data);
174 if (!sections.length) {
181 callback(null);
191 return;
20 }
213 sections = getSections(sections, filepath, encoding);
22
233 var order = sections[0].name.match(/^(\d+)\./);
243 order = parseInt(order[1], 10);
25
263 var css = getCSS(data, filepath, options);
273 callback({order: order, filepath: filepath, css: css, sections: sections});
28 }
29 });
30}
311exports.styleFile = styleFile;
32
33function styleFileSync(filepath, options) {
344 var encoding;
354 if (!options) {
363 encoding = 'utf8';
37 } else {
381 encoding = options.encoding || 'utf8';
39 }
404 var data = fs.readFileSync(filepath, encoding);
41
424 var sections = parser(data);
434 if (!sections.length) {
441 return null;
45 }
46
473 sections = getSections(sections, filepath, encoding);
483 var order = sections[0].name.match(/^(\d+)\./);
493 order = parseInt(order[1], 10);
50
513 var css = getCSS(data, filepath, options);
523 return {order: order, filepath: filepath, css: css, sections: sections};
53}
541exports.styleFileSync = styleFileSync;
55
56
57function getSections(sections, filepath, encoding) {
586 var ret = [], data;
596 var basepath = path.dirname(filepath);
606 sections.forEach(function(section) {
616 if (section.exampleFile) {
622 data = fs.readFileSync(path.join(basepath, section.exampleFile), encoding)
632 section.examples = [{name: '', code: data}];
642 section.html = section.exampleFile;
65 }
666 ret.push(section);
67 });
686 return ret;
69}
70
71function getCSS(data, filepath, options) {
726 options = options || {};
736 var ext = path.extname(filepath);
746 if (ext === '.styl') {
752 var stylus = require('stylus'), css;
762 var ret = stylus(data);
772 ret.set('filename', path.basename(filepath));
782 Object.keys(options).forEach(function(key) {
791 ret.set(key, options[key]);
80 });
812 ret.render(function(err, out) {
822 if (err) throw err;
832 css = out;
84 });
85 } else {
864 css = data;
87 }
886 var pseudos = /(\:hover|\:disabled|\:active|\:visited)/g;
896 css += css.replace(pseudos, function(matched) {
9018 return '.pseudo-class-' + matched.replace(':', '');
91 });
926 return css;
93}
94