167 lines
4.4 KiB
JavaScript
167 lines
4.4 KiB
JavaScript
|
// Except for a block in #visitImport, everything here is an exact copy of the
|
||
|
// source in stylus this currently depends on. If stylus is updated, update
|
||
|
// this appropriately.
|
||
|
|
||
|
var Evaluator = require('stylus/lib/visitor/evaluator')
|
||
|
, nodes = require('stylus/lib/nodes')
|
||
|
, Stack = require('stylus/lib/stack')
|
||
|
, Frame = require('stylus/lib/stack/frame')
|
||
|
, Scope = require('stylus/lib/stack/scope')
|
||
|
, utils = require('stylus/lib/utils')
|
||
|
, bifs = require('stylus/lib/functions')
|
||
|
, basename = require('path').basename
|
||
|
, dirname = require('path').dirname
|
||
|
, relative = require('path').relative
|
||
|
, join = require('path').join
|
||
|
, colors = require('stylus/lib/colors')
|
||
|
// , debug = require('debug')('stylus:evaluator')
|
||
|
, fs = require('fs');
|
||
|
|
||
|
module.exports = CachedPathEvaluator;
|
||
|
|
||
|
/**
|
||
|
* Import `file` and return Block node.
|
||
|
*
|
||
|
* @api private
|
||
|
*/
|
||
|
function importFile(node, file, literal, index) {
|
||
|
var importStack = this.importStack
|
||
|
, Parser = require('stylus/lib/parser')
|
||
|
, stat;
|
||
|
|
||
|
// Handling the `require`
|
||
|
if (node.once) {
|
||
|
if (this.requireHistory[file]) return nodes.null;
|
||
|
this.requireHistory[file] = true;
|
||
|
|
||
|
if (literal && !this.includeCSS) {
|
||
|
return node;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Expose imports
|
||
|
node.path = file;
|
||
|
node.dirname = dirname(file);
|
||
|
// Store the modified time
|
||
|
stat = fs.statSync(file);
|
||
|
node.mtime = stat.mtime;
|
||
|
this.paths.push(node.dirname);
|
||
|
|
||
|
// Avoid overflows from importing the same file over again
|
||
|
if (file === importStack[importStack.length - 1]) return nodes.null;
|
||
|
|
||
|
if (this.options._imports) this.options._imports.push(node.clone());
|
||
|
|
||
|
// Parse the file
|
||
|
importStack.push(file);
|
||
|
nodes.filename = file;
|
||
|
|
||
|
var str;
|
||
|
if (this.cache.sources && this.cache.sources[file]) {
|
||
|
str = this.cache.sources[file];
|
||
|
} else {
|
||
|
str = fs.readFileSync(file, 'utf8');
|
||
|
}
|
||
|
|
||
|
if (literal && !this.resolveURL) return new nodes.Literal(str.replace(/\r\n?/g, '\n'));
|
||
|
|
||
|
// parse
|
||
|
var block = new nodes.Block
|
||
|
, parser = new Parser(str, utils.merge({ root: block }, this.options));
|
||
|
|
||
|
try {
|
||
|
block = parser.parse();
|
||
|
} catch (err) {
|
||
|
err.filename = file;
|
||
|
err.lineno = parser.lexer.lineno;
|
||
|
err.input = str;
|
||
|
throw err;
|
||
|
}
|
||
|
|
||
|
// Evaluate imported "root"
|
||
|
block.parent = this.root;
|
||
|
block.scope = false;
|
||
|
var ret = this.visit(block);
|
||
|
importStack.pop();
|
||
|
if (importStack.length || index) this.paths.pop();
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
function CachedPathEvaluator(root, options) {
|
||
|
Evaluator.apply(this, arguments);
|
||
|
|
||
|
this.cache = options.cache;
|
||
|
}
|
||
|
|
||
|
CachedPathEvaluator.prototype = Object.create(Evaluator.prototype);
|
||
|
CachedPathEvaluator.prototype.constructor = CachedPathEvaluator;
|
||
|
|
||
|
CachedPathEvaluator.prototype.visitImport = function(imported) {
|
||
|
this.return++;
|
||
|
|
||
|
var path = this.visit(imported.path).first
|
||
|
, nodeName = imported.once ? 'require' : 'import'
|
||
|
, found
|
||
|
, literal
|
||
|
, index;
|
||
|
|
||
|
this.return--;
|
||
|
// debug('import %s', path);
|
||
|
|
||
|
// url() passed
|
||
|
if ('url' == path.name) {
|
||
|
if (imported.once) throw new Error('You cannot @require a url');
|
||
|
|
||
|
return imported;
|
||
|
}
|
||
|
|
||
|
// Ensure string
|
||
|
if (!path.string) throw new Error('@' + nodeName + ' string expected');
|
||
|
|
||
|
var name = path = path.string;
|
||
|
|
||
|
// Absolute URL
|
||
|
if (/url\s*\(\s*['"]?(?:https?:)?\/\//i.test(path)) {
|
||
|
if (imported.once) throw new Error('You cannot @require a url');
|
||
|
return imported;
|
||
|
}
|
||
|
|
||
|
// Literal
|
||
|
if (/\.css(?:"|$)/.test(path)) {
|
||
|
literal = true;
|
||
|
if (!imported.once && !this.includeCSS) {
|
||
|
return imported;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// support optional .styl
|
||
|
if (!literal && !/\.styl$/i.test(path)) path += '.styl';
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* THIS IS THE ONLY BLOCK THAT DIFFERS FROM THE ACTUAL STYLUS IMPLEMENTATION. *
|
||
|
*****************************************************************************/
|
||
|
// Lookup
|
||
|
var dirname = this.paths[this.paths.length - 1];
|
||
|
found = this.cache.find(path, dirname);
|
||
|
index = this.cache.isIndex(path, dirname);
|
||
|
if (!found) {
|
||
|
found = utils.find(path, this.paths, this.filename);
|
||
|
if (!found) {
|
||
|
found = utils.lookupIndex(name, this.paths, this.filename);
|
||
|
index = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Throw if import failed
|
||
|
if (!found) throw new Error('failed to locate @' + nodeName + ' file ' + path);
|
||
|
|
||
|
var block = new nodes.Block;
|
||
|
|
||
|
for (var i = 0, len = found.length; i < len; ++i) {
|
||
|
block.push(importFile.call(this, imported, found[i], literal, index));
|
||
|
}
|
||
|
|
||
|
return block;
|
||
|
}
|