mirror of
https://github.com/sstent/node.git
synced 2026-01-27 15:41:43 +00:00
updated app
This commit is contained in:
460
first-project/node_modules/derby/lib/files.js
generated
vendored
Normal file
460
first-project/node_modules/derby/lib/files.js
generated
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
var pathUtil = require('path')
|
||||
, dirname = pathUtil.dirname
|
||||
, basename = pathUtil.basename
|
||||
, join = pathUtil.join
|
||||
, exists = pathUtil.exists
|
||||
, relative = pathUtil.relative
|
||||
, fs = require('fs')
|
||||
, crypto = require('crypto')
|
||||
, stylus = require('stylus')
|
||||
, nib = require('nib')
|
||||
, less = require('less')
|
||||
, racer = require('racer')
|
||||
, Promise = racer.util.Promise
|
||||
, finishAfter = racer.util.async.finishAfter
|
||||
, asyncForEach = racer.util.async.forEach
|
||||
, htmlUtil = require('html-util')
|
||||
, parseHtml = htmlUtil.parse
|
||||
, minifyHtml = htmlUtil.minify
|
||||
, styleCompilers = {
|
||||
stylus: stylusCompiler
|
||||
, less: lessCompiler
|
||||
}
|
||||
, onlyWhitespace = /^[\s\n]*$/;
|
||||
|
||||
exports.css = css;
|
||||
exports.templates = templates;
|
||||
exports.js = js;
|
||||
exports.library = library;
|
||||
exports.parseName = parseName;
|
||||
exports.hashFile = hashFile;
|
||||
exports.writeJs = writeJs;
|
||||
exports.watch = watch;
|
||||
|
||||
function css(root, clientName, compress, callback) {
|
||||
// TODO: Set default configuration options in a single place
|
||||
var styles = require('./derby').settings.styles || ['less', 'stylus']
|
||||
, compiled = []
|
||||
, finish;
|
||||
|
||||
if (!Array.isArray(styles)) styles = [styles];
|
||||
|
||||
finish = finishAfter(styles.length, function(err) {
|
||||
callback(err, compiled.join(''));
|
||||
});
|
||||
|
||||
styles.forEach(function(style, i) {
|
||||
var compiler = styleCompilers[style];
|
||||
if (!compiler) finish(new Error('Unable to find compiler for: ' + style));
|
||||
|
||||
compiler(root, clientName, compress, function(err, value) {
|
||||
compiled[i] = value || '';
|
||||
finish(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function stylusCompiler(root, clientName, compress, callback) {
|
||||
findPath(root + '/styles', clientName, '.styl', function(path) {
|
||||
if (!path) return callback('');
|
||||
fs.readFile(path, 'utf8', function(err, styl) {
|
||||
if (err) return callback(err);
|
||||
stylus(styl)
|
||||
.use(nib())
|
||||
.set('filename', path)
|
||||
.set('compress', compress)
|
||||
.render(callback);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function lessCompiler(root, clientName, compress, callback) {
|
||||
findPath(root + '/styles', clientName, '.less', function(path) {
|
||||
if (!path) return callback('');
|
||||
|
||||
fs.readFile(path, 'utf8', function(err, lessFile) {
|
||||
if (err) return callback(err);
|
||||
var parser = new less.Parser({
|
||||
paths: [root + '/styles']
|
||||
, filename: path
|
||||
});
|
||||
parser.parse(lessFile, function(err, tree) {
|
||||
var compiled;
|
||||
if (err) return callback(err);
|
||||
try {
|
||||
compiled = tree.toCSS({compress: compress});
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, compiled);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function templates(root, clientName, callback) {
|
||||
loadTemplates(root + '/views', clientName, callback);
|
||||
}
|
||||
|
||||
function js(parentFilename, options, callback) {
|
||||
var finish, inline, inlineFile, js;
|
||||
|
||||
// Needed for tests
|
||||
if (!parentFilename) return;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
// TODO: Move this to config:
|
||||
// Express will try to include mime, which won't work in the browser
|
||||
// It doesn't actually need this for routing, so we just ignore it
|
||||
if (options.ignore) {
|
||||
options.ignore.push('mime');
|
||||
} else {
|
||||
options.ignore = ['mime'];
|
||||
}
|
||||
if (options.require) {
|
||||
options.require.push(parentFilename);
|
||||
} else {
|
||||
options.require = [parentFilename];
|
||||
}
|
||||
inlineFile = join(dirname(parentFilename), 'inline.js');
|
||||
finish = finishAfter(2, function(err) {
|
||||
callback(err, js, inline);
|
||||
});
|
||||
racer.js(options, function(err, value) {
|
||||
js = value;
|
||||
finish(err);
|
||||
});
|
||||
fs.readFile(inlineFile, 'utf8', function(err, value) {
|
||||
inline = value;
|
||||
// Ignore file not found error
|
||||
if (err && err.code === 'ENOENT') err = null;
|
||||
finish(err);
|
||||
});
|
||||
}
|
||||
|
||||
function library(root, callback) {
|
||||
var components = {};
|
||||
|
||||
fs.readdir(root, function(err, files) {
|
||||
if (err) return callback(err);
|
||||
asyncForEach(files, libraryFile, function(err) {
|
||||
if (err) return callback(err);
|
||||
callback(null, components);
|
||||
});
|
||||
});
|
||||
|
||||
function libraryFile(file, callback) {
|
||||
var path = root + '/' + file
|
||||
fs.stat(path, function(err, stats) {
|
||||
if (err) return callback(err);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return addComponent(root, file, callback);
|
||||
}
|
||||
if (extensions['html'].test(file)) {
|
||||
file = file.replace(extensions['html'], '');
|
||||
return addComponent(root, file, callback);
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function addComponent(root, name, callback) {
|
||||
loadTemplates(root, name, function(err, templates, instances) {
|
||||
components[name] = {
|
||||
templates: templates
|
||||
, instances: instances
|
||||
};
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function parseName(parentFilename, options) {
|
||||
var parentDir = dirname(parentFilename)
|
||||
, root = parentDir
|
||||
, base = basename(parentFilename, '.js');
|
||||
if (base === 'index') {
|
||||
base = basename(parentDir);
|
||||
root = dirname(dirname(parentDir));
|
||||
} else if (basename(parentDir) === 'lib') {
|
||||
root = dirname(parentDir);
|
||||
}
|
||||
return {
|
||||
root: root
|
||||
, clientName: options.name || base
|
||||
, require: './' + basename(parentFilename)
|
||||
};
|
||||
}
|
||||
|
||||
function hashFile(file) {
|
||||
var hash = crypto.createHash('md5').update(file).digest('base64');
|
||||
// Base64 uses characters reserved in URLs and adds extra padding charcters.
|
||||
// Replace "/" and "+" with the unreserved "-" and "_" and remove "=" padding
|
||||
return hash.replace(/[\/\+=]/g, function(match) {
|
||||
switch (match) {
|
||||
case '/': return '-';
|
||||
case '+': return '_';
|
||||
case '=': return '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeJs(root, js, options, callback) {
|
||||
var staticRoot = options.staticRoot || join(root, 'public')
|
||||
, staticDir = options.staticDir || 'gen'
|
||||
, staticPath = join(staticRoot, staticDir)
|
||||
, hash = hashFile(js)
|
||||
, filename = hash + '.js'
|
||||
, jsFile = join('/', staticDir, filename)
|
||||
, filePath = join(staticPath, filename);
|
||||
|
||||
function finish() {
|
||||
fs.writeFile(filePath, js, function(err) {
|
||||
callback(err, jsFile, hash);
|
||||
});
|
||||
}
|
||||
exists(staticPath, function(value) {
|
||||
if (value) return finish();
|
||||
|
||||
exists(staticRoot, function(value) {
|
||||
if (value) {
|
||||
fs.mkdir(staticPath, '0777', function(err) {
|
||||
finish();
|
||||
})
|
||||
return;
|
||||
}
|
||||
fs.mkdir(staticRoot, '0777', function(err) {
|
||||
fs.mkdir(staticPath, '0777', function(err) {
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function watch(dir, type, onChange) {
|
||||
var extension = extensions[type]
|
||||
, watchFn = fs.watch ? systemWatch : pollWatch;
|
||||
|
||||
files(dir, extension).forEach(watchFn);
|
||||
|
||||
function systemWatch(file) {
|
||||
fs.watch(file, function() {
|
||||
onChange(file);
|
||||
});
|
||||
}
|
||||
function pollWatch(file) {
|
||||
fs.watchFile(file, {interval: 100}, function(curr, prev) {
|
||||
if (prev.mtime < curr.mtime) {
|
||||
onChange(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function findPath(root, name, extension, callback) {
|
||||
if (name.charAt(0) !== '/') {
|
||||
name = join(root, name);
|
||||
}
|
||||
var path = name + extension;
|
||||
exists(path, function(value) {
|
||||
if (value) return callback(path);
|
||||
path = join(name, 'index' + extension);
|
||||
exists(path, function(value) {
|
||||
callback(value ? path : null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadTemplates(root, fileName, callback) {
|
||||
var count = 0
|
||||
, calls = {incr: incr, finish: finish};
|
||||
function incr() {
|
||||
count++;
|
||||
}
|
||||
function finish(err, templates, instances) {
|
||||
if (err) {
|
||||
calls.finish = function() {};
|
||||
return callback(err);
|
||||
}
|
||||
--count || callback(null, templates, instances);
|
||||
}
|
||||
forTemplate(root, fileName, 'import', calls);
|
||||
}
|
||||
|
||||
function forTemplate(root, fileName, get, calls, files, templates, instances, alias, currentNs) {
|
||||
if (currentNs == null) currentNs = '';
|
||||
calls.incr();
|
||||
findPath(root, fileName, '.html', function(path) {
|
||||
var getCount, got, matchesGet, promise;
|
||||
if (path === null) {
|
||||
if (!files) {
|
||||
// Return without doing anything if the path isn't found, and this is the
|
||||
// initial automatic lookup based on the clientName
|
||||
return calls.finish(null, {}, {});
|
||||
} else {
|
||||
return calls.finish(new Error("Can't find file " + fileName));
|
||||
}
|
||||
}
|
||||
files || (files = {});
|
||||
templates || (templates = {});
|
||||
instances || (instances = {});
|
||||
|
||||
got = false;
|
||||
if (get === 'import') {
|
||||
matchesGet = function() {
|
||||
return got = true;
|
||||
}
|
||||
} else if (Array.isArray(get)) {
|
||||
getCount = get.length;
|
||||
matchesGet = function(name) {
|
||||
--getCount || (got = true);
|
||||
return ~get.indexOf(name);
|
||||
}
|
||||
} else {
|
||||
matchesGet = function(name) {
|
||||
got = true;
|
||||
return get === name;
|
||||
}
|
||||
}
|
||||
|
||||
promise = files[path];
|
||||
if (!promise) {
|
||||
promise = files[path] = new Promise;
|
||||
fs.readFile(path, 'utf8', function(err, file) {
|
||||
promise.resolve(err, file);
|
||||
});
|
||||
}
|
||||
promise.on(function(err, file) {
|
||||
if (err) calls.finish(err);
|
||||
parseTemplateFile(root, dirname(path), path, calls, files, templates, instances, alias, currentNs, matchesGet, file);
|
||||
if (!got) {
|
||||
calls.finish(new Error("Can't find template '" + get + "' in " + path));
|
||||
}
|
||||
calls.finish(null, templates, instances);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function parseTemplateFile(root, dir, path, calls, files, templates, instances, alias, currentNs, matchesGet, file) {
|
||||
var relativePath = relative(root, path)
|
||||
, as, importTemplates, name, ns, src, templateOptions;
|
||||
|
||||
parseHtml(file, {
|
||||
// Force template tags to be treated as raw tags,
|
||||
// meaning their contents are not parsed as HTML
|
||||
rawTags: /^(?:[^\s=\/>]+:|style|script)$/i
|
||||
, matchEnd: matchEnd
|
||||
, start: start
|
||||
, text: text
|
||||
});
|
||||
|
||||
function matchEnd(tagName) {
|
||||
if (tagName.slice(-1) === ':') {
|
||||
return /<\/?[^\s=\/>]+:[\s>]/;
|
||||
}
|
||||
return new RegExp('</' + tagName, 'i');
|
||||
}
|
||||
|
||||
function start(tag, tagName, attrs) {
|
||||
var i = tagName.length - 1
|
||||
, srcNs, template;
|
||||
|
||||
as, importTemplates, ns, src = null;
|
||||
name = (tagName.charAt(i) === ':' ? tagName.slice(0, i) : '').toLowerCase();
|
||||
if (name === 'import') {
|
||||
src = attrs.src, ns = attrs.ns, as = attrs.as, template = attrs.template;
|
||||
if (!src) {
|
||||
calls.finish(new Error("Template import in " + path +
|
||||
" must have a 'src' attribute"));
|
||||
}
|
||||
if (template) {
|
||||
importTemplates = template.toLowerCase().split(' ');
|
||||
if (importTemplates.length > 1 && (as != null)) {
|
||||
calls.finish(new Error("Template import of '" + src + "' in " +
|
||||
path + " can't specify multiple 'template' values with 'as'"));
|
||||
}
|
||||
}
|
||||
if ('ns' in attrs) {
|
||||
if (as) calls.finish(new Error("Template import of '" + src +
|
||||
"' in " + path + " can't specifiy both 'ns' and 'as' attributes"));
|
||||
// Import into the namespace specified via 'ns' underneath
|
||||
// the current namespace
|
||||
ns = ns
|
||||
? currentNs ? currentNs + ':' + ns : ns
|
||||
: currentNs;
|
||||
} else if (as) {
|
||||
// If 'as' is specified, import into the current namespace
|
||||
ns = currentNs;
|
||||
} else {
|
||||
// If no namespace is specified, use the src as a namespace.
|
||||
// Remove leading '.' and '/' characters
|
||||
srcNs = src.replace(/^[.\/]*/, '');
|
||||
ns = currentNs ? currentNs + ':' + srcNs : srcNs;
|
||||
}
|
||||
ns = ns.toLowerCase();
|
||||
} else {
|
||||
templateOptions = attrs;
|
||||
}
|
||||
}
|
||||
|
||||
function text(text, isRawText) {
|
||||
var instanceName, templateName, toGet;
|
||||
if (!matchesGet(name)) return;
|
||||
if (src) {
|
||||
if (!onlyWhitespace.test(text)) {
|
||||
calls.finish(new Error("Template import of '" + src + "' in " +
|
||||
path + " can't contain content: " + text));
|
||||
}
|
||||
toGet = importTemplates || 'import';
|
||||
return forTemplate(root, join(dir, src), toGet, calls, files, templates, instances, as, ns);
|
||||
}
|
||||
templateName = relativePath + ':' + name;
|
||||
instanceName = alias || name;
|
||||
if (currentNs) {
|
||||
instanceName = currentNs + ':' + instanceName;
|
||||
}
|
||||
instances[instanceName] = [templateName, templateOptions];
|
||||
if (templates[templateName]) return;
|
||||
if (!(name && isRawText)) {
|
||||
if (onlyWhitespace.test(text)) return;
|
||||
calls.finish(new Error("Can't read template in " + path +
|
||||
" near the text: " + text));
|
||||
}
|
||||
templates[templateName] = minifyHtml(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: These should be set as configuration options
|
||||
var extensions = {
|
||||
html: /\.html$/i
|
||||
, css: /\.styl$|\.css|\.less$/i
|
||||
, js: /\.js$/i
|
||||
};
|
||||
|
||||
var ignoreDirectories = ['node_modules', '.git', 'gen'];
|
||||
|
||||
function ignored(path) {
|
||||
return ignoreDirectories.indexOf(path) === -1;
|
||||
}
|
||||
|
||||
function files(dir, extension, out) {
|
||||
if (out == null) out = [];
|
||||
fs.readdirSync(dir).filter(ignored).forEach(function(p) {
|
||||
p = join(dir, p);
|
||||
if (fs.statSync(p).isDirectory()) {
|
||||
files(p, extension, out);
|
||||
} else if (extension.test(p)) {
|
||||
out.push(p);
|
||||
}
|
||||
});
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user