This repository has been archived on 2024-07-27. You can view files and clone it, but cannot push or open issues or pull requests.
keksAccountGUI/node_modulesOLD/stylus-loader/lib/pathcache.js
2019-08-11 20:48:02 +02:00

304 lines
No EOL
9.3 KiB
JavaScript

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;
}