UNPKG

5.92 kBJavaScriptView Raw
1'use strict';
2
3/*
4 * Dependencies.
5 */
6
7var Ware = require('ware');
8var extend = require('extend.js');
9var parser = require('./lib/parse.js');
10var stringifier = require('./lib/stringify.js');
11var File = require('./lib/file.js');
12var utilities = require('./lib/utilities.js');
13
14/*
15 * Methods.
16 */
17
18var Parser = parser.Parser;
19var parseProto = Parser.prototype;
20var Compiler = stringifier.Compiler;
21var compileProto = Compiler.prototype;
22
23/**
24 * Throws if passed an exception.
25 *
26 * Here until the following PR is merged into
27 * segmentio/ware:
28 *
29 * https://github.com/segmentio/ware/pull/21
30 *
31 * @param {Error?} exception
32 */
33function fail(exception) {
34 if (exception) {
35 throw exception;
36 }
37}
38
39/**
40 * Create a custom, cloned, Parser.
41 *
42 * @return {Function}
43 */
44function constructParser() {
45 var customProto;
46 var expressions;
47 var key;
48
49 /**
50 * Extensible prototype.
51 */
52 function CustomProto() {}
53
54 CustomProto.prototype = parseProto;
55
56 customProto = new CustomProto();
57
58 /**
59 * Extensible constructor.
60 */
61 function CustomParser() {
62 Parser.apply(this, arguments);
63 }
64
65 CustomParser.prototype = customProto;
66
67 /*
68 * Construct new objects for things that plugin's
69 * might modify.
70 */
71
72 customProto.blockTokenizers = extend({}, parseProto.blockTokenizers);
73 customProto.blockMethods = extend([], parseProto.blockMethods);
74 customProto.inlineTokenizers = extend({}, parseProto.inlineTokenizers);
75 customProto.inlineMethods = extend([], parseProto.inlineMethods);
76
77 expressions = parseProto.expressions;
78 customProto.expressions = {};
79
80 for (key in expressions) {
81 customProto.expressions[key] = extend({}, expressions[key]);
82 }
83
84 return CustomParser;
85}
86
87/**
88 * Create a custom, cloned, Compiler.
89 *
90 * @return {Function}
91 */
92function constructCompiler() {
93 var customProto;
94
95 /**
96 * Extensible prototype.
97 */
98 function CustomProto() {}
99
100 CustomProto.prototype = compileProto;
101
102 customProto = new CustomProto();
103
104 /**
105 * Extensible constructor.
106 */
107 function CustomCompiler() {
108 Compiler.apply(this, arguments);
109 }
110
111 CustomCompiler.prototype = customProto;
112
113 return CustomCompiler;
114}
115
116/**
117 * Construct an MDAST instance.
118 *
119 * @constructor {MDAST}
120 */
121function MDAST() {
122 var self = this;
123
124 if (!(self instanceof MDAST)) {
125 return new MDAST();
126 }
127
128 self.ware = new Ware();
129 self.attachers = [];
130
131 self.Parser = constructParser();
132 self.Compiler = constructCompiler();
133}
134
135/**
136 * Attach a plugin.
137 *
138 * @param {Function|Array.<Function>} attach
139 * @param {Object?} [options]
140 * @param {FileSet?} [fileSet] - Optional file-set,
141 * passed by the CLI.
142 * @return {MDAST}
143 */
144function use(attach, options, fileSet) {
145 var self = this;
146 var index;
147 var transformer;
148
149 if (!(self instanceof MDAST)) {
150 self = new MDAST();
151 }
152
153 /*
154 * Multiple attachers.
155 */
156
157 if ('length' in attach && typeof attach !== 'function') {
158 index = -1;
159
160 while (attach[++index]) {
161 self.use(attach[index], options, fileSet);
162 }
163
164 return self;
165 }
166
167 /*
168 * Single plugin.
169 */
170
171 if (self.attachers.indexOf(attach) === -1) {
172 transformer = attach(self, options, fileSet);
173
174 self.attachers.push(attach);
175
176 if (transformer) {
177 self.ware.use(transformer);
178 }
179 }
180
181 return self;
182}
183
184/**
185 * Apply transformers to `node`.
186 *
187 * @param {Node} ast
188 * @param {File?} [file]
189 * @param {Function?} [done]
190 * @return {Node} - `ast`.
191 */
192function run(ast, file, done) {
193 var self = this;
194
195 if (typeof file === 'function') {
196 done = file;
197 file = null;
198 }
199
200 file = new File(file);
201
202 done = typeof done === 'function' ? done : fail;
203
204 if (typeof ast !== 'object' && typeof ast.type !== 'string') {
205 utilities.raise(ast, 'ast');
206 }
207
208 /*
209 * Only run when this is an instance of MDAST.
210 */
211
212 if (self.ware) {
213 self.ware.run(ast, file, done);
214 } else {
215 done(null, ast, file);
216 }
217
218 return ast;
219}
220
221/**
222 * Wrapper to pass a file to `parser`.
223 */
224function parse(value, options) {
225 return parser.call(this, new File(value), options);
226}
227
228/**
229 * Wrapper to pass a file to `stringifier`.
230 */
231function stringify(ast, file, options) {
232 if (options === null || options === undefined) {
233 options = file;
234 file = null;
235 }
236
237 return stringifier.call(this, ast, new File(file), options);
238}
239
240/**
241 * Parse a value and apply transformers.
242 *
243 * @param {string|File} value
244 * @param {Object?} [options]
245 * @param {Function?} [done]
246 * @return {string?}
247 */
248function process(value, options, done) {
249 var file = new File(value);
250 var self = this instanceof MDAST ? this : new MDAST();
251 var result = null;
252 var ast;
253
254 if (typeof options === 'function') {
255 done = options;
256 options = null;
257 }
258
259 if (!options) {
260 options = {};
261 }
262
263 /**
264 * Invoked when `run` completes. Hoists `result` into
265 * the upper scope to return something for sync
266 * operations.
267 */
268 function callback(exception) {
269 if (exception) {
270 (done || fail)(exception);
271 } else {
272 result = self.stringify(ast, file, options);
273
274 if (done) {
275 done(null, result, file);
276 }
277 }
278 }
279
280 ast = self.parse(file, options);
281 file.ast = ast;
282 self.run(ast, file, callback);
283
284 return result;
285}
286
287/*
288 * Methods.
289 */
290
291var proto = MDAST.prototype;
292
293proto.use = use;
294proto.parse = parse;
295proto.run = run;
296proto.stringify = stringify;
297proto.process = process;
298
299/*
300 * Functions.
301 */
302
303MDAST.use = use;
304MDAST.parse = parse;
305MDAST.run = run;
306MDAST.stringify = stringify;
307MDAST.process = process;
308
309/*
310 * Expose `mdast`.
311 */
312
313module.exports = MDAST;