forked from csstools/mdcss
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
185 lines (148 loc) · 4.98 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
var fs = require('fs');
var fsp = require('./lib/fs-promise');
var marked = require('marked');
var path = require('path');
var isDoc = /^\s*-{3,}\n((?:[ \t]*[A-z][\w-]*[ \t]*:[ \t]*[\w-][^\n]*\n*)*)(?:[ \t]*-{3,})?/;
var isMeta = /([A-z][\w-]*)[ \t]*:[ \t]*([\w-][^\n]*)/g;
module.exports = require('postcss').plugin('mdcss', function (opts) {
// set options object
opts = Object(opts);
// set theme
opts.theme = opts.theme || require('mdcss-theme-github');
// set index
opts.index = opts.index || 'index.html';
// throw if theme is not a function
if (typeof opts.theme !== 'function') throw Error('The theme failed to load');
// conditionally set theme as executed theme
if (opts.theme.type === 'mdcss-theme') opts.theme = opts.theme(opts);
// set destination path
opts.destination = path.join(process.cwd(), opts.destination || 'styleguide');
// set additional assets path
opts.assets = (opts.assets || []).map(function (src) {
return path.join(process.cwd(), src);
});
// return plugin
return function (css, result) {
// set current css directory or current directory
var dir = css.source.input.file ? path.dirname(css.source.input.file) : process.cwd();
// set documentation list, hash, and unique identifier
var list = [];
var hash = {};
var uniq = 0;
// walk comments
css.walkComments(function (comment) {
// if comment is documentation
if (isDoc.test(comment.text)) {
// set documentation
var doc = {};
// filter documentation meta
doc.content = comment.text.replace(isDoc, function (isDoc0, metas) {
// push meta to documentation
if (metas) metas.replace(isMeta, function (isMeta0, name, value) {
doc[name] = value.trim();
});
// remove meta from documentation content
return '';
}, opts.marked).trim();
// conditionally set the closest documentation name
if (doc.title && !doc.name) doc.name = titleToName(doc.title);
// conditionally import external content
if (!doc.content) {
// get comment source path
var src = comment.source.input.file;
// if the comment source path exists
if (src) {
// get the closest matching directory for this comment
var localdir = src ? path.dirname(src) : dir;
var mdbase = doc.import;
var mdspec;
// conditionally use a sibling md files if no import exists
if (!mdbase) {
mdbase = mdspec = path.basename(src, path.extname(src));
if (doc.name) {
mdspec += '.' + doc.name;
}
mdbase += '.md';
mdspec += '.md';
}
// try to read the closest matching documentation
try {
if (mdspec) {
doc.content = fs.readFileSync(path.join(localdir, mdspec), 'utf8');
} else throw new Error();
} catch (error1) {
try {
doc.content = fs.readFileSync(path.join(localdir, mdbase), 'utf8');
} catch (error2) {
doc.content = '';
comment.warn(result, 'Documentation import "' + mdbase + '" could not be read.');
}
}
}
}
doc.content = marked(doc.content);
// set documentation context
doc.context = comment;
// insure documentation has unique name
var name = doc.name || 'section' + --uniq;
var uniqname = name;
while (uniqname in hash) uniqname = name + --uniq;
// push documentation to hash
hash[uniqname] = doc;
}
});
// walk hashes
Object.keys(hash).forEach(function (name) {
// set documentation
var doc = hash[name];
// if documentation has a parent section
if ('section' in doc) {
// get parent section
var title = doc.section;
var sname = titleToName(title);
var parent = hash[sname];
// if parent section does not exist
if (!parent) {
// create parent section
parent = hash[sname] = {
title: title,
name: sname
};
// add parent section to list
list.push(parent);
}
if (!parent.children) parent.children = [];
// make documentation a child of the parent section
parent.children.push(doc);
doc.parent = parent;
}
// otherwise make documentation a child of list
else list.push(doc);
});
// return theme executed with parsed list, destination
return opts.theme({
list: list,
opts: opts
}).then(function (docs) {
// empty the destination directory
return fsp.emptyDir(opts.destination)
// then copy the theme assets into the destination
.then(function () {
return fsp.copy(docs.assets, opts.destination);
})
// then copy the compiled template into the destination
.then(function () {
return fsp.outputFile(path.join(opts.destination, opts.index), docs.template);
})
// then copy any of the additional assets into the destination
.then(function () {
return Promise.all(opts.assets.map(function (src) {
return fsp.copy(src, path.join(opts.destination, path.basename(src)));
}));
});
});
};
});
function titleToName(title) {
return title.replace(/\s+/g, '-').replace(/[^A-z0-9_-]/g, '').toLowerCase();
}