ce4c83ff
wxy
初始提交
|
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
|
var path = require('path');
var fs = require('fs');
var Evaluator = require('stylus/lib/visitor/evaluator');
var loaderUtils = require('loader-utils');
var nodes = require('stylus/lib/nodes');
var utils = require('stylus/lib/utils');
var when = require('when');
var whenNodefn = require('when/node/function');
var listImports = require('./listimports');
module.exports = PathCache;
var readFile = whenNodefn.lift(fs.readFile);
// A cache of import paths of a stylus file resolved to their location on disk
// before the stylus file is rendered. With a special evaluator this lets us
// webpack's resolver.
function PathCache(contexts, sources, imports) {
this.contexts = contexts;
this.sources = sources;
this.imports = imports;
// Non relative paths are simpler and looked up in this as a fallback
// to this.context.
this.simpleContext = {};
for (var dirname in this.contexts) {
for (var path in this.contexts[dirname]) {
this.simpleContext[path] = this.contexts[dirname][path];
}
}
}
// Return a promise for a PathCache.
PathCache.create = function(contexts, sources, imports) {
return when(new PathCache(contexts, sources, imports));
};
PathCache.createFromFile = resolveFileDeep;
// Create a list of ways to resolve paths.
PathCache.resolvers = resolvers;
PathCache.resolvers.reduce = reduceResolvers;
// Lookup the path in this cache.
PathCache.prototype.find = function(path, dirname) {
if (this.contexts[dirname] && this.contexts[dirname][path]) {
return this.contexts[dirname][path].path;
} else if (this.simpleContext[path]) {
return this.simpleContext[path].path;
} else if (/.styl$/.test(path)) {
// A user can specify @import 'something.styl' but if they specify
// @import 'something' stylus adds .styl, we drop that here to see if we
// looked for it without .styl.
return this.find(path.replace(/.styl$/, ''), dirname);
} else {
return undefined;
}
};
// Return if the path in this cache is an index file.
PathCache.prototype.isIndex = function(path, dirname) {
if (this.contexts[dirname] && this.contexts[dirname][path]) {
return this.contexts[dirname][path].index;
} else {
return undefined;
}
};
// Return an array of all imports the original file depends on.
PathCache.prototype.allDeps = function() {
var deps = [];
for (var dirname in this.contexts) {
for (var path in this.contexts[dirname]) {
if (this.contexts[dirname][path]) {
deps = deps.concat(this.contexts[dirname][path].path);
}
}
}
return deps;
};
// Create an array of ways to resolve a path.
//
// The resolved paths may be a path or an object specifying path and index
// members. The index member is used later by stylus, we store it at this point.
function resolvers(options, webpackResolver) {
var evaluator = new Evaluator(nodes.null, options);
var whenWebpackResolver = whenNodefn.lift(webpackResolver);
// Stylus's normal resolver for single files.
var stylusFile = function(context, path) {
// Stylus adds .styl to paths for normal "paths" lookup if it isn't there.
if (!/.styl$/.test(path)) {
path += '.styl';
}
var paths = options.paths.concat(context);
var found = utils.find(path, paths, options.filename)
if (found) {
return normalizePaths(found);
}
};
// Stylus's normal resolver for node_modules packages. Cannot locate paths
// inside a package.
var stylusIndex = function(context, path) {
// Stylus calls the argument name. If it exists it should match the name
// of a module in node_modules.
if (!path) {
return null;
}
var paths = options.paths.concat(context);
var found = utils.lookupIndex(path, paths, options.filename);
if (found) {
return {path: normalizePaths(found), index: true};
}
};
// Fallback to resolving with webpack's configured resovler.
var webpackResolve = function(context, path) {
// Follow the webpack stylesheet idiom of '~path' meaning a path in a
// modules folder and a unprefixed 'path' meaning a relative path like
// './path'.
path = loaderUtils.urlToRequest(path, options.root);
// First try with a '.styl' extension.
return whenWebpackResolver(context, path + '.styl')
// If the user adds ".styl" to resolve.extensions, webpack can find
// index files like stylus but it uses all of webpack's configuration,
// by default for example the module could be web_modules.
.catch(function() { return whenWebpackResolver(context, path); })
.catch(function() { return null; })
.then(function(result) {
return Array.isArray(result) && result[1] && result[1].path || result
});
};
if (options.preferPathResolver === 'webpack') {
return [
webpackResolve,
stylusFile,
stylusIndex
];
}
else {
return [
stylusFile,
stylusIndex,
webpackResolve
];
}
}
function reduceResolvers(resolvers, context, path) {
return when
.reduce(resolvers, function(result, resolver) {
return result ? result : resolver(context, path);
}, undefined);
}
// Run resolvers on one path and return an object with the found path under a
// key of the original path.
//
// Example:
// resolving the path
// 'a/file'
// returns an object
// {'a/file': {path: ['node_modules/a/file'], index: true}}
function resolveOne(resolvers, context, path) {
return reduceResolvers(resolvers, context, path)
.then(function(result) {
result = typeof result === 'string' ? [result] : result;
result = Array.isArray(result) ? {path: result, index: false} : result;
var map = {};
map[path] = result;
return map;
});
}
// Run the resolvers on an array of paths and return an object like resolveOne.
function resolveMany(resolvers, context, paths) {
return when
.map(paths, resolveOne.bind(null, resolvers, context))
.then(function(maps) {
return maps.reduce(function(map, resolvedPaths) {
Object.keys(resolvedPaths).forEach(function(path) {
map[path] = resolvedPaths[path];
});
return map;
}, {});
});
}
// Load a file at fullPath, resolve all of it's imports and report for those.
function resolveFileDeep(helpers, parentCache, source, fullPath) {
var resolvers = helpers.resolvers;
var readFile = helpers.readFile;
var contexts = parentCache.contexts;
var sources = parentCache.sources;
contexts = contexts || {};
var nestResolve = resolveFileDeep.bind(null, helpers, parentCache, null);
var context = path.dirname(fullPath);
readFile = whenNodefn.lift(readFile);
return when
.resolve(source || sources[fullPath] || readFile(fullPath))
// Cast the buffer from the cached input file system to a string.
.then(String)
// Store the source so that the evaluator doesn't need to touch the
// file system.
.then(function(_source) {
sources[fullPath] = _source;
return _source;
})
// Make sure the stylus functions/index.styl source is stored.
.then(partial(ensureFunctionsSource, sources))
// List imports and use its cache. The source file is translated into a
// list of imports. Where the source file came from isn't important for the
// list. The where is added by resolveMany with the context and resolvers.
.then(partialRight(listImports, { cache: parentCache.imports }))
.then(resolveMany.bind(null, resolvers, context))
.then(function(newPaths) {
// Contexts are the full path since multiple could be in the same folder
// but different deps.
contexts[context] = merge(contexts[context] || {}, newPaths);
return when.map(Object.keys(newPaths), function(key) {
var found = newPaths[key] && newPaths[key].path;
if (found) {
return when.map(found, nestResolve);
}
});
})
.then(function() {
return PathCache.create(contexts, sources, parentCache.imports);
});
}
// Resolve functions in a promise wrapper to catch any errors from resolving.
var functionsPath =
new when.Promise(function(resolve) {
resolve(require.resolve('stylus/lib/functions/index.styl'));
})
.catch(function() { return ''; });
var functionsSource = functionsPath
.then(readFile)
.catch(function(error) {
// Ignore error if functions/index.styl doesn't exist.
if (error.code !== 'ENOENT') {
throw error;
}
return '';
})
.then(String);
function ensureFunctionsSource(sources, source) {
if (!sources[functionsPath]) {
return functionsSource
.then(function(functionsSource) {
if (functionsSource) {
sources[functionsPath] = functionsSource;
}
})
// Pass through the source given to this function.
.yield(source);
}
// Pass through the source given to this function.
return source;
}
var slice = Array.prototype.slice.call.bind(Array.prototype.slice);
function merge(a, b) {
var key;
for (key in b) {
a[key] = b[key];
}
return a;
}
function partial(fn) {
var args = slice(arguments, 1);
return function() {
return fn.apply(this, args.concat(slice(arguments)));
};
}
function partialRight(fn) {
var args = slice(arguments, 1);
return function() {
return fn.apply(this, slice(arguments).concat(args));
};
}
function normalizePaths(paths) {
for(var i in paths) {
paths[i] = path.normalize(paths[i]);
}
return paths;
}
|