1 |
|
---|
2 | /**
|
---|
3 | * Module dependencies.
|
---|
4 | */
|
---|
5 |
|
---|
6 | var Visitor = require('./')
|
---|
7 | , Parser = require('../parser')
|
---|
8 | , nodes = require('../nodes')
|
---|
9 | , utils = require('../utils')
|
---|
10 | , dirname = require('path').dirname
|
---|
11 | , fs = require('fs');
|
---|
12 |
|
---|
13 | /**
|
---|
14 | * Initialize a new `DepsResolver` with the given `root` Node
|
---|
15 | * and the `options`.
|
---|
16 | *
|
---|
17 | * @param {Node} root
|
---|
18 | * @param {Object} options
|
---|
19 | * @api private
|
---|
20 | */
|
---|
21 |
|
---|
22 | var DepsResolver = module.exports = function DepsResolver(root, options) {
|
---|
23 | this.root = root;
|
---|
24 | this.filename = options.filename;
|
---|
25 | this.paths = options.paths || [];
|
---|
26 | this.paths.push(dirname(options.filename || '.'));
|
---|
27 | this.options = options;
|
---|
28 | this.functions = {};
|
---|
29 | this.deps = [];
|
---|
30 | };
|
---|
31 |
|
---|
32 | /**
|
---|
33 | * Inherit from `Visitor.prototype`.
|
---|
34 | */
|
---|
35 |
|
---|
36 | DepsResolver.prototype.__proto__ = Visitor.prototype;
|
---|
37 |
|
---|
38 | var visit = DepsResolver.prototype.visit;
|
---|
39 |
|
---|
40 | DepsResolver.prototype.visit = function(node) {
|
---|
41 | switch (node.nodeName) {
|
---|
42 | case 'root':
|
---|
43 | case 'block':
|
---|
44 | case 'expression':
|
---|
45 | this.visitRoot(node);
|
---|
46 | break;
|
---|
47 | case 'group':
|
---|
48 | case 'media':
|
---|
49 | case 'atblock':
|
---|
50 | case 'atrule':
|
---|
51 | case 'keyframes':
|
---|
52 | case 'each':
|
---|
53 | case 'supports':
|
---|
54 | this.visit(node.block);
|
---|
55 | break;
|
---|
56 | default:
|
---|
57 | visit.call(this, node);
|
---|
58 | }
|
---|
59 | };
|
---|
60 |
|
---|
61 | /**
|
---|
62 | * Visit Root.
|
---|
63 | */
|
---|
64 |
|
---|
65 | DepsResolver.prototype.visitRoot = function(block) {
|
---|
66 | for (var i = 0, len = block.nodes.length; i < len; ++i) {
|
---|
67 | this.visit(block.nodes[i]);
|
---|
68 | }
|
---|
69 | };
|
---|
70 |
|
---|
71 | /**
|
---|
72 | * Visit Ident.
|
---|
73 | */
|
---|
74 |
|
---|
75 | DepsResolver.prototype.visitIdent = function(ident) {
|
---|
76 | this.visit(ident.val);
|
---|
77 | };
|
---|
78 |
|
---|
79 | /**
|
---|
80 | * Visit If.
|
---|
81 | */
|
---|
82 |
|
---|
83 | DepsResolver.prototype.visitIf = function(node) {
|
---|
84 | this.visit(node.block);
|
---|
85 | this.visit(node.cond);
|
---|
86 | for (var i = 0, len = node.elses.length; i < len; ++i) {
|
---|
87 | this.visit(node.elses[i]);
|
---|
88 | }
|
---|
89 | };
|
---|
90 |
|
---|
91 | /**
|
---|
92 | * Visit Function.
|
---|
93 | */
|
---|
94 |
|
---|
95 | DepsResolver.prototype.visitFunction = function(fn) {
|
---|
96 | this.functions[fn.name] = fn.block;
|
---|
97 | };
|
---|
98 |
|
---|
99 | /**
|
---|
100 | * Visit Call.
|
---|
101 | */
|
---|
102 |
|
---|
103 | DepsResolver.prototype.visitCall = function(call) {
|
---|
104 | if (call.name in this.functions) this.visit(this.functions[call.name]);
|
---|
105 | if (call.block) this.visit(call.block);
|
---|
106 | };
|
---|
107 |
|
---|
108 | /**
|
---|
109 | * Visit Import.
|
---|
110 | */
|
---|
111 |
|
---|
112 | DepsResolver.prototype.visitImport = function(node) {
|
---|
113 | var path = !node.path.first.val.isNull && node.path.first.val || node.path.first.name
|
---|
114 | , literal, found, oldPath;
|
---|
115 |
|
---|
116 | if (!path) return;
|
---|
117 |
|
---|
118 | literal = /\.css(?:"|$)/.test(path);
|
---|
119 |
|
---|
120 | // support optional .styl
|
---|
121 | if (!literal && !/\.styl$/i.test(path)) {
|
---|
122 | oldPath = path;
|
---|
123 | path += '.styl';
|
---|
124 | }
|
---|
125 |
|
---|
126 | // Lookup
|
---|
127 | found = utils.find(path, this.paths, this.filename);
|
---|
128 |
|
---|
129 | // support optional index
|
---|
130 | if (!found && oldPath) found = utils.lookupIndex(oldPath, this.paths, this.filename);
|
---|
131 |
|
---|
132 | if (!found) return;
|
---|
133 |
|
---|
134 | this.deps = this.deps.concat(found);
|
---|
135 |
|
---|
136 | if (literal) return;
|
---|
137 |
|
---|
138 | // nested imports
|
---|
139 | for (var i = 0, len = found.length; i < len; ++i) {
|
---|
140 | var file = found[i]
|
---|
141 | , dir = dirname(file)
|
---|
142 | , str = fs.readFileSync(file, 'utf-8')
|
---|
143 | , block = new nodes.Block
|
---|
144 | , parser = new Parser(str, utils.merge({ root: block }, this.options));
|
---|
145 |
|
---|
146 | if (!~this.paths.indexOf(dir)) this.paths.push(dir);
|
---|
147 |
|
---|
148 | try {
|
---|
149 | block = parser.parse();
|
---|
150 | } catch (err) {
|
---|
151 | err.filename = file;
|
---|
152 | err.lineno = parser.lexer.lineno;
|
---|
153 | err.column = parser.lexer.column;
|
---|
154 | err.input = str;
|
---|
155 | throw err;
|
---|
156 | }
|
---|
157 |
|
---|
158 | this.visit(block);
|
---|
159 | }
|
---|
160 | };
|
---|
161 |
|
---|
162 | /**
|
---|
163 | * Get dependencies.
|
---|
164 | */
|
---|
165 |
|
---|
166 | DepsResolver.prototype.resolve = function() {
|
---|
167 | this.visit(this.root);
|
---|
168 | return utils.uniq(this.deps);
|
---|
169 | };
|
---|