1 | var parse = require('esprima').parse;
|
2 |
|
3 | module.exports = function (src, opts, fn) {
|
4 | if (typeof opts === 'function') {
|
5 | fn = opts;
|
6 | opts = {};
|
7 | }
|
8 | if (typeof src === 'object') {
|
9 | opts = src;
|
10 | src = opts.source;
|
11 | delete opts.source;
|
12 | }
|
13 | src = src || opts.source;
|
14 | opts.range = true;
|
15 | if (typeof src !== 'string') src = String(src);
|
16 |
|
17 | var ast = parse(src, opts);
|
18 |
|
19 | var result = {
|
20 | chunks : src.split(''),
|
21 | toString : function () { return result.chunks.join('') },
|
22 | inspect : function () { return result.toString() }
|
23 | };
|
24 | var index = 0;
|
25 |
|
26 | (function walk (node, parent) {
|
27 | insertHelpers(node, parent, result.chunks);
|
28 |
|
29 | Object.keys(node).forEach(function (key) {
|
30 | if (key === 'parent') return;
|
31 |
|
32 | var child = node[key];
|
33 | if (Array.isArray(child)) {
|
34 | child.forEach(function (c) {
|
35 | if (c && typeof c.type === 'string') {
|
36 | walk(c, node);
|
37 | }
|
38 | });
|
39 | }
|
40 | else if (child && typeof child.type === 'string') {
|
41 | insertHelpers(child, node, result.chunks);
|
42 | walk(child, node);
|
43 | }
|
44 | });
|
45 | fn(node);
|
46 | })(ast, undefined);
|
47 |
|
48 | return result;
|
49 | };
|
50 |
|
51 | function insertHelpers (node, parent, chunks) {
|
52 | if (!node.range) return;
|
53 |
|
54 | node.parent = parent;
|
55 |
|
56 | node.source = function () {
|
57 | return chunks.slice(
|
58 | node.range[0], node.range[1] + 1
|
59 | ).join('');
|
60 | };
|
61 |
|
62 | if (node.update && typeof node.update === 'object') {
|
63 | var prev = node.update;
|
64 | Object.keys(prev).forEach(function (key) {
|
65 | update[key] = prev[key];
|
66 | });
|
67 | node.update = update;
|
68 | }
|
69 | else {
|
70 | node.update = update;
|
71 | }
|
72 |
|
73 | function update (s) {
|
74 | chunks[node.range[0]] = s;
|
75 | for (var i = node.range[0] + 1; i < node.range[1] + 1; i++) {
|
76 | chunks[i] = '';
|
77 | }
|
78 | };
|
79 | }
|