UNPKG

5.76 kBJavaScriptView Raw
1'use strict';
2
3/*
4 * Dependencies.
5 */
6
7var Ware = require('ware');
8var parser = require('./lib/parse.js');
9var stringifier = require('./lib/stringify.js');
10var File = require('./lib/file.js');
11var utilities = require('./lib/utilities.js');
12
13/*
14 * Methods.
15 */
16
17var clone = utilities.clone;
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 = clone(parseProto.blockTokenizers);
73 customProto.blockMethods = clone(parseProto.blockMethods);
74 customProto.inlineTokenizers = clone(parseProto.inlineTokenizers);
75 customProto.inlineMethods = clone(parseProto.inlineMethods);
76
77 expressions = parseProto.expressions;
78 customProto.expressions = {};
79
80 for (key in expressions) {
81 customProto.expressions[key] = clone(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 * @return {MDAST}
141 */
142function use(attach, options) {
143 var self = this;
144 var index;
145 var transformer;
146
147 if (!(self instanceof MDAST)) {
148 self = new MDAST();
149 }
150
151 /*
152 * Multiple attachers.
153 */
154
155 if ('length' in attach && typeof attach !== 'function') {
156 index = attach.length;
157
158 while (attach[--index]) {
159 self.use(attach[index]);
160 }
161
162 return self;
163 }
164
165 /*
166 * Single plugin.
167 */
168
169 if (self.attachers.indexOf(attach) === -1) {
170 transformer = attach(self, options);
171
172 self.attachers.push(attach);
173
174 if (transformer) {
175 self.ware.use(transformer);
176 }
177 }
178
179 return self;
180}
181
182/**
183 * Apply transformers to `node`.
184 *
185 * @param {Node} ast
186 * @param {File?} [file]
187 * @param {Function?} [done]
188 * @return {Node} - `ast`.
189 */
190function run(ast, file, done) {
191 var self = this;
192
193 if (typeof file === 'function') {
194 done = file;
195 file = null;
196 }
197
198 file = new File(file);
199
200 done = typeof done === 'function' ? done : fail;
201
202 if (typeof ast !== 'object' && typeof ast.type !== 'string') {
203 utilities.raise(ast, 'ast');
204 }
205
206 /*
207 * Only run when this is an instance of MDAST.
208 */
209
210 if (self.ware) {
211 self.ware.run(ast, file, done);
212 } else {
213 done(null, ast, file);
214 }
215
216 return ast;
217}
218
219/**
220 * Wrapper to pass a file to `parser`.
221 */
222function parse(value, options) {
223 return parser.call(this, new File(value), options);
224}
225
226/**
227 * Wrapper to pass a file to `stringifier`.
228 */
229function stringify(ast, file, options) {
230 if (options === null || options === undefined) {
231 options = file;
232 file = null;
233 }
234
235 return stringifier.call(this, ast, new File(file), options);
236}
237
238/**
239 * Parse a value and apply transformers.
240 *
241 * @param {string|File} value
242 * @param {Object?} [options]
243 * @param {Function?} [done]
244 * @return {string?}
245 */
246function process(value, options, done) {
247 var file = new File(value);
248 var self = this instanceof MDAST ? this : new MDAST();
249 var result = null;
250 var ast;
251
252 if (typeof options === 'function') {
253 done = options;
254 options = null;
255 }
256
257 if (!options) {
258 options = {};
259 }
260
261 /**
262 * Invoked when `run` completes. Hoists `result` into
263 * the upper scope to return something for sync
264 * operations.
265 */
266 function callback(exception) {
267 if (exception) {
268 (done || fail)(exception);
269 } else {
270 result = self.stringify(ast, file, options);
271
272 if (done) {
273 done(null, result, file);
274 }
275 }
276 }
277
278 ast = self.parse(file, options);
279 self.run(ast, file, callback);
280
281 return result;
282}
283
284/*
285 * Methods.
286 */
287
288var proto = MDAST.prototype;
289
290proto.use = use;
291proto.parse = parse;
292proto.run = run;
293proto.stringify = stringify;
294proto.process = process;
295
296/*
297 * Functions.
298 */
299
300MDAST.use = use;
301MDAST.parse = parse;
302MDAST.run = run;
303MDAST.stringify = stringify;
304MDAST.process = process;
305
306/*
307 * Expose `mdast`.
308 */
309
310module.exports = MDAST;