Comparing version 0.3.8 to 0.4.0
@@ -1,8 +0,5 @@ | ||
/*! | ||
* configure | ||
* | ||
* Date: 2017/10/19 | ||
* | ||
* This is licensed under the MIT License (MIT). | ||
* For details, see: https://github.com/nuintun/fengine/blob/master/LICENSE | ||
/** | ||
* @module configure | ||
* @license MIT | ||
* @version 2017/11/14 | ||
*/ | ||
@@ -13,21 +10,20 @@ | ||
// Import lib | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var yaml = require('js-yaml'); | ||
var utils = require('./utils'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const yaml = require('js-yaml'); | ||
const utils = require('./utils'); | ||
// Variable declaration | ||
var CWD = process.cwd(); | ||
const CWD = process.cwd(); | ||
/** | ||
* Format watch | ||
* | ||
* @param watch | ||
* @function formatWatch | ||
* @param {Array} watch | ||
* @returns {Array} | ||
*/ | ||
function formatWatch(watch) { | ||
var unique = {}; | ||
var result = []; | ||
const unique = {}; | ||
const result = []; | ||
watch.forEach(function(value) { | ||
watch.forEach((value) => { | ||
value = value.toLowerCase(); | ||
@@ -46,8 +42,9 @@ | ||
/** | ||
* Run | ||
* | ||
* @param port | ||
* @function configure | ||
* @param {number} port | ||
* @returns {Object} | ||
*/ | ||
module.exports = function(port) { | ||
var yml = path.resolve(CWD, 'fengine.yml'); | ||
const DEFAULT_WATCH = ['.htm', '.html']; | ||
let yml = path.resolve(CWD, 'fengine.yml'); | ||
@@ -57,3 +54,3 @@ // File config | ||
// Parse yaml | ||
var source = fs.readFileSync(yml); | ||
const source = fs.readFileSync(yml); | ||
@@ -72,5 +69,5 @@ yml = yaml.safeLoad(source, { filename: yml }); | ||
yml.port = port || port === 0 ? port : (utils.isLegalPort(+yml.port) ? +yml.port : 0); | ||
yml.watch = Array.isArray(yml.watch) ? formatWatch(yml.watch.concat(['.htm', '.html'])) : ['.htm', '.html']; | ||
yml.watch = Array.isArray(yml.watch) ? formatWatch(yml.watch.concat(DEFAULT_WATCH)) : DEFAULT_WATCH; | ||
return yml; | ||
}; |
@@ -1,10 +0,5 @@ | ||
/*! | ||
* events | ||
* | ||
* Date: 2016/8/1 | ||
* | ||
* Original Author: https://github.com/aralejs/events | ||
* | ||
* This is licensed under the MIT License (MIT). | ||
* For details, see: https://github.com/nuintun/fengine/blob/master/LICENSE | ||
/** | ||
* @module events | ||
* @license MIT | ||
* @version 2017/11/14 | ||
*/ | ||
@@ -15,13 +10,12 @@ | ||
// Array slice | ||
var slice = Array.prototype.slice; | ||
const slice = Array.prototype.slice; | ||
/** | ||
* Faster apply | ||
* Call is faster than apply, optimize less than 6 args | ||
* | ||
* @function apply | ||
* @description Faster apply, call is faster than apply, optimize less than 6 args | ||
* @param {Function} fn | ||
* @param {any} context | ||
* @param {Array} args | ||
* https://github.com/micro-js/apply | ||
* http://blog.csdn.net/zhengyinhui100/article/details/7837127 | ||
* @see https://github.com/micro-js/apply | ||
* @see http://blog.csdn.net/zhengyinhui100/article/details/7837127 | ||
*/ | ||
@@ -46,15 +40,9 @@ function apply(fn, context, args) { | ||
/** | ||
* Events | ||
* | ||
* @constructor | ||
* @class Events | ||
*/ | ||
function Events() { | ||
// Keep this empty so it's easier to inherit from | ||
} | ||
Events.prototype = { | ||
class Events { | ||
/** | ||
* Bind event | ||
* | ||
* @param {String} name | ||
* @method on | ||
* @description Bind event | ||
* @param {string} name | ||
* @param {Function} listener | ||
@@ -64,7 +52,6 @@ * @param {any} context | ||
*/ | ||
on: function(name, listener, context) { | ||
var self = this; | ||
var events = self._events || (self._events = {}); | ||
on(name, listener, context) { | ||
const events = this._events || (this._events = {}); | ||
context = arguments.length < 3 ? self : context; | ||
context = arguments.length < 3 ? this : context; | ||
@@ -74,8 +61,9 @@ // [...[listener, context]] | ||
return self; | ||
}, | ||
return this; | ||
} | ||
/** | ||
* Bind event only emit once | ||
* | ||
* @param {String} name | ||
* @method once | ||
* @description Bind event only emit once | ||
* @param {string} name | ||
* @param {Function} listener | ||
@@ -85,47 +73,42 @@ * @param {any} context | ||
*/ | ||
once: function(name, listener, context) { | ||
var self = this; | ||
once(name, listener, context) { | ||
context = arguments.length < 3 ? this : context; | ||
context = arguments.length < 3 ? self : context; | ||
function feedback() { | ||
self.off(name, feedback, this); | ||
apply(listener, this, arguments); | ||
const feedback = () => { | ||
this.off(name, feedback, context); | ||
apply(listener, context, arguments); | ||
}; | ||
return self.on(name, feedback, context); | ||
}, | ||
return this.on(name, feedback, context); | ||
} | ||
/** | ||
* Emit event | ||
* | ||
* @param {String} name | ||
* @method emit | ||
* @description Emit event | ||
* @param {string} name | ||
* @param {any} [...param] | ||
* @returns {Events} | ||
*/ | ||
emit: function(name) { | ||
var context = this; | ||
var data = slice.call(arguments, 1); | ||
var events = context._events || (context._events = {}); | ||
var listeners = events[name] || []; | ||
emit(name) { | ||
let pass = true; | ||
const data = slice.call(arguments, 1); | ||
const events = this._events || (this._events = {}); | ||
const listeners = events[name] || []; | ||
var result; | ||
var listener; | ||
var returned; | ||
// Emit events | ||
for (var i = 0, length = listeners.length; i < length; i++) { | ||
listener = listeners[i]; | ||
result = apply(listener[0], listener[1], data); | ||
for (let item of listeners) { | ||
let listener = item[0]; | ||
let context = item[1]; | ||
if (returned !== false) { | ||
returned = result; | ||
} | ||
pass = apply(listener, context, data) !== false && pass; | ||
} | ||
return returned; | ||
}, | ||
// Emit will return false if one of the callbacks return false | ||
return pass; | ||
} | ||
/** | ||
* Remove event | ||
* | ||
* @param {String} name | ||
* @method off | ||
* @description Remove event | ||
* @param {string} name | ||
* @param {Function} listener | ||
@@ -135,10 +118,9 @@ * @param {any} context | ||
*/ | ||
off: function(name, listener, context) { | ||
var self = this; | ||
var length = arguments.length; | ||
var events = self._events || (self._events = {}); | ||
off(name, listener, context) { | ||
const length = arguments.length; | ||
const events = this._events || (this._events = {}); | ||
switch (length) { | ||
case 0: | ||
self._events = {}; | ||
this._events = {}; | ||
break; | ||
@@ -149,24 +131,19 @@ case 1: | ||
default: | ||
if (listener) { | ||
var listeners = events[name]; | ||
const listeners = events[name]; | ||
if (listeners) { | ||
context = length < 3 ? self : context; | ||
length = listeners.length; | ||
if (listeners) { | ||
context = length < 3 ? this : context; | ||
var monitor; | ||
for (let i = 0, len = listeners.length; i < length; i++) { | ||
let monitor = listeners[i]; | ||
for (var i = 0; i < length; i++) { | ||
monitor = listeners[i]; | ||
if (monitor[0] === listener && monitor[1] === context) { | ||
listeners.splice(i, 1); | ||
break; | ||
} | ||
if (monitor[0] === listener && monitor[1] === context) { | ||
listeners.splice(i, 1); | ||
break; | ||
} | ||
} | ||
// Remove event from queue to prevent memory leak | ||
if (!listeners.length) { | ||
delete events[name]; | ||
} | ||
// Remove event from queue to prevent memory leak | ||
if (!listeners.length) { | ||
delete events[name]; | ||
} | ||
@@ -177,6 +154,6 @@ } | ||
return self; | ||
return this; | ||
} | ||
}; | ||
} | ||
module.exports = Events; |
@@ -1,8 +0,5 @@ | ||
/*! | ||
* fengine | ||
* | ||
* Date: 2016/8/2 | ||
* | ||
* This is licensed under the MIT License (MIT). | ||
* For details, see: https://github.com/nuintun/fengine/blob/master/LICENSE | ||
/** | ||
* @module fengine | ||
* @license MIT | ||
* @version 2017/11/14 | ||
*/ | ||
@@ -13,48 +10,47 @@ | ||
// Import lib | ||
var fs = require('fs'); | ||
var url = require('url'); | ||
var path = require('path'); | ||
var http = require('http'); | ||
var utils = require('./utils'); | ||
var cluster = require('cluster'); | ||
var mime = require('mime-types'); | ||
var FileSend = require('file-send'); | ||
var pkg = require('../package.json'); | ||
var Transform = require('./transform'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const http = require('http'); | ||
const utils = require('./utils'); | ||
const cluster = require('cluster'); | ||
const FileSend = require('file-send'); | ||
const pkg = require('../package.json'); | ||
const Transform = require('./transform'); | ||
// Variable declaration | ||
var CWD = process.cwd(); | ||
var LOG_LEVELS = utils.LOG_LEVELS; | ||
var WORKER_ID = cluster.worker.id; | ||
var FAVICON = path.join(__dirname, '../favicon.ico'); | ||
var FAVICON_SIZE = fs.lstatSync(FAVICON).size; | ||
const join = path.join; | ||
const mime = FileSend.mime; | ||
const relative = path.relative; | ||
const LOG_LEVELS = utils.LOG_LEVELS; | ||
const WORKER_ID = cluster.worker.id; | ||
const FAVICON = join(__dirname, '../favicon.ico'); | ||
const FAVICON_SIZE = fs.lstatSync(FAVICON).size; | ||
/** | ||
* Fengine | ||
* | ||
* @param options | ||
* @constructor | ||
* @class Fengine | ||
*/ | ||
function Fengine(options) { | ||
this.options = options; | ||
class Fengine { | ||
/** | ||
* @constructor | ||
* @param {Object} options | ||
* @returns {Fengine} | ||
*/ | ||
constructor(options) { | ||
this.options = options; | ||
// Run server | ||
this.run(); | ||
} | ||
// Run server | ||
this.run(); | ||
} | ||
/** | ||
* prototype | ||
* | ||
* @type {{ | ||
* dir: Fengine.dir, | ||
* error: Fengine.error, | ||
* run: Fengine.run | ||
* }} | ||
*/ | ||
Fengine.prototype = { | ||
dir: function(files, dir) { | ||
var parent = utils.normalize(path.join(dir, '../')); | ||
var up = dir === '/' ? '' : ' <span>-</span>\n' | ||
+ ' <a href="' + parent + '" title="Back to parent directory."><h1>up</h1></a>\n'; | ||
var html = '<!DOCTYPE html>\n' | ||
/** | ||
* @method dir | ||
* @param {Array} files | ||
* @param {string} dir | ||
* @returns {string} | ||
*/ | ||
dir(files, dir) { | ||
const parent = utils.normalize(join(dir, '../')); | ||
const up = dir === '/' ? '' : ' <span>-</span>\n' | ||
+ ` <a href="${ parent }" title="Back to parent directory."><h1>up</h1></a>\n`; | ||
let html = '<!DOCTYPE html>\n' | ||
+ '<html>\n' | ||
@@ -65,3 +61,3 @@ + ' <head>\n' | ||
+ ' <meta content="text/html; charset=utf-8" http-equiv="content-type" />\n' | ||
+ ' <title>' + dir + '</title>\n' | ||
+ ` <title>${ dir }</title>\n` | ||
+ ' <style>\n' | ||
@@ -88,9 +84,9 @@ + ' html, body, p {\n' | ||
+ ' <div class="ui-dir">\n' | ||
+ ' <a href="' + dir + '" title="' + dir + '"><h1>' + dir + '</h1></a>\n' + up | ||
+ ` <a href="${ dir }" title="${ dir }"><h1>${ dir }</h1></a>\n${ up }` | ||
+ ' </div>\n'; | ||
var width = String(files.length).length; | ||
const width = String(files.length).length; | ||
files.forEach(function(file, index) { | ||
var href = dir + file; | ||
files.forEach((file, index) => { | ||
const href = dir + file; | ||
@@ -107,58 +103,24 @@ html += ' <div class="ui-file">\n' | ||
return html; | ||
}, | ||
error: function(response, statusCode, next) { | ||
var html = '<!DOCTYPE html>\n' | ||
+ '<html>\n' | ||
+ ' <head>\n' | ||
+ ' <meta name="renderer" content="webkit" />\n' | ||
+ ' <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />\n' | ||
+ ' <meta content="text/html; charset=utf-8" http-equiv="content-type" />\n' | ||
+ ' <title>' + statusCode + '</title>\n' | ||
+ ' <style>\n' | ||
+ ' html, body, div, p {\n' | ||
+ ' margin: 0; padding: 0;\n' | ||
+ ' text-align: center;\n' | ||
+ ' font-family: Calibri, "Lucida Console", Consolas, "Liberation Mono", Menlo, Courier, monospace;\n' | ||
+ ' }\n' | ||
+ ' p { color: #0e90d2; line-height: 100%; }\n' | ||
+ ' .ui-code { font-size: 200px; font-weight: bold; margin-top: 66px; }\n' | ||
+ ' .ui-message { font-size: 80px; }\n' | ||
+ ' </style>\n' | ||
+ ' </head>\n' | ||
+ ' <body>\n' | ||
+ ' <p class="ui-code">' + statusCode + '</p>\n' | ||
+ ' <p class="ui-message">' + http.STATUS_CODES[statusCode] + '</p>\n' | ||
+ ' </body>\n' | ||
+ '</html>\n'; | ||
} | ||
if (utils.fn(next)) { | ||
next(html); | ||
} else { | ||
response.statusCode = statusCode; | ||
response.setHeader('Content-Type', 'text/html; charset=UTF-8'); | ||
response.end(html); | ||
} | ||
}, | ||
send: function(requset, response) { | ||
var context = this; | ||
var options = context.options; | ||
/** | ||
* @method send | ||
* @param {Request} request | ||
* @param {string} path | ||
* @param {Response} response | ||
* @returns {FileSend} | ||
*/ | ||
send(request, path, response) { | ||
// File send | ||
var send = new FileSend(requset, { | ||
root: options.root | ||
}).on('error', function(error, next) { | ||
context.error(response, error.statusCode, next); | ||
}).on('dir', function(realpath, stats, next) { | ||
// Set Content-Type | ||
send.setHeader('Content-Type', 'text/html; charset=UTF-8'); | ||
const send = new FileSend(request, path, { | ||
root: this.options.root | ||
}).on('dir', (realpath, next) => { | ||
// Read dir | ||
fs.readdir(realpath, (error, files) => { | ||
if (error) return this.send(request, path, response); | ||
// Read dir | ||
fs.readdir(realpath, function(error, files) { | ||
if (error) { | ||
send.statError(response, error); | ||
} else { | ||
// Response | ||
next(context.dir(files, send.url)); | ||
} | ||
// Set Content-Type | ||
send.setHeader('Content-Type', 'text/html; charset=UTF-8'); | ||
// Response | ||
next(this.dir(files, path)); | ||
}); | ||
@@ -169,7 +131,12 @@ }).pipe(response); | ||
return send; | ||
}, | ||
transform: function(pathname, source, response) { | ||
var context = this; | ||
var options = context.options; | ||
} | ||
/** | ||
* @method transform | ||
* @param {string} path | ||
* @param {string|Buffer} source | ||
* @param {Response} response | ||
* @returns {Transform} | ||
*/ | ||
transform(path, source, response) { | ||
// Set status code | ||
@@ -179,6 +146,8 @@ response.statusCode = 200; | ||
// Set Content-Type | ||
response.setHeader('Content-Type', mime.lookup(pathname)); | ||
response.setHeader('Content-Type', mime.lookup(path)); | ||
const options = this.options; | ||
// Transform | ||
var transform = new Transform(pathname, source, { | ||
const transform = new Transform(path, source, { | ||
root: options.base, | ||
@@ -191,3 +160,3 @@ data: options.data, | ||
// Event data | ||
transform.on('data', function(data) { | ||
transform.on('data', (data) => { | ||
response.write(data); | ||
@@ -197,5 +166,5 @@ }); | ||
// Event error | ||
transform.on('error', function(event, file, command) { | ||
var type; | ||
var message; | ||
transform.on('error', (event, file, command) => { | ||
let type; | ||
let message; | ||
@@ -205,5 +174,4 @@ switch (event) { | ||
type = LOG_LEVELS.WARN; | ||
message = 'Found cyclic command \'' | ||
+ command + '\'\n at file: /' | ||
+ utils.normalize(path.relative(options.root, file.src)); | ||
message = `Found cyclic command '${ command }'\n` | ||
+ ` at file: /${ utils.normalize(relative(options.root, file.src)) }`; | ||
break; | ||
@@ -214,5 +182,4 @@ case 'io': | ||
if (command) { | ||
message = 'File of command \'' | ||
+ command + '\' does not exist\n at file: /' | ||
+ utils.normalize(path.relative(options.root, file.src)); | ||
message = `File of command '${ command }' does not exist\n` | ||
+ ` at file: /${ utils.normalize(relative(options.root, file.src)) }`; | ||
} else { | ||
@@ -232,3 +199,3 @@ message = 'The default layout file does not exist'; | ||
// Event end | ||
transform.on('end', function() { | ||
transform.on('end', () => { | ||
response.end(); | ||
@@ -239,55 +206,45 @@ }); | ||
return transform; | ||
}, | ||
run: function() { | ||
var context = this; | ||
var options = context.options; | ||
} | ||
/** | ||
* @method run | ||
*/ | ||
run() { | ||
const options = this.options; | ||
// Create server | ||
var server = http.createServer(function(requset, response) { | ||
const server = http.createServer((request, response) => { | ||
response.setHeader('Server', 'Fengine/' + pkg.version); | ||
response.setHeader('X-Powered-By', 'Node/' + process.version); | ||
var parsed = url.parse(requset.url); | ||
var pathname = utils.decodeURI(parsed.pathname); | ||
const pathname = utils.decodeURI(utils.pathname(request.url)); | ||
if (pathname === -1 || pathname.indexOf('\0') !== -1) { | ||
return context.error(response, 400); | ||
return this.send(request, pathname, response); | ||
} | ||
var extname = path.extname(pathname).toLowerCase(); | ||
const extname = path.extname(pathname).toLowerCase(); | ||
if (options.watch.indexOf(extname) !== -1) { | ||
pathname = path.join(options.root, pathname); | ||
const realpath = join(options.root, pathname); | ||
if (utils.isOutBound(pathname, CWD)) { | ||
return context.error(response, 403); | ||
if (utils.isOutBound(realpath, options.root)) { | ||
return this.send(request, pathname, response); | ||
} | ||
if (!utils.isOutBound(pathname, options.base)) { | ||
fs.stat(pathname, function(error, stats) { | ||
if (error) { | ||
return context.error(response, 404); | ||
} | ||
if (utils.isOutBound(realpath, options.base)) { | ||
return this.send(request, pathname, response); | ||
} | ||
// Is file | ||
if (stats.isFile()) { | ||
fs.readFile(pathname, function(error, source) { | ||
if (error) { | ||
return context.error(response, 404); | ||
} | ||
fs.readFile(realpath, (error, source) => { | ||
if (error) { | ||
return this.send(request, pathname, response); | ||
} | ||
context.transform(pathname, source, response); | ||
}); | ||
} else { | ||
context.send(requset, response); | ||
} | ||
}); | ||
} else { | ||
context.send(requset, response); | ||
} | ||
this.transform(realpath, source, response); | ||
}); | ||
} else { | ||
if (pathname.toLowerCase() === '/favicon.ico') { | ||
pathname = path.join(options.root, pathname); | ||
if (pathname === '/favicon.ico') { | ||
const realpath = join(options.root, pathname); | ||
fs.stat(pathname, function(error) { | ||
fs.stat(realpath, (error) => { | ||
if (error) { | ||
@@ -302,6 +259,6 @@ response.statusCode = 200; | ||
context.send(requset, response); | ||
this.send(request, pathname, response); | ||
}); | ||
} else { | ||
context.send(requset, response); | ||
this.send(request, pathname, response); | ||
} | ||
@@ -313,6 +270,5 @@ } | ||
server.on('listening', function() { | ||
var server = this.address(); | ||
var hostname = options.hostname; | ||
const server = this.address(); | ||
const hostname = options.hostname ? options.hostname : '127.0.0.1'; | ||
hostname = hostname ? hostname : '127.0.0.1'; | ||
options.data.server = hostname + ':' + server.port; | ||
@@ -328,9 +284,6 @@ | ||
// Event error | ||
server.on('error', function(error) { | ||
var hostname = options.hostname; | ||
server.on('error', (error) => { | ||
const hostname = options.hostname ? options.hostname : '127.0.0.1'; | ||
const message = error.syscall + ' ' + error.code + ' ' + hostname + ':' + error.port; | ||
hostname = hostname ? hostname : '127.0.0.1'; | ||
var message = error.syscall + ' ' + error.code + ' ' + hostname + ':' + error.port; | ||
// Message | ||
@@ -347,3 +300,3 @@ process.send({ | ||
// Event close | ||
server.on('close', function() { | ||
server.on('close', () => { | ||
// Message | ||
@@ -366,5 +319,5 @@ process.send({ | ||
} | ||
}; | ||
} | ||
// Exports | ||
module.exports = Fengine; |
1130
lib/transform.js
@@ -1,8 +0,5 @@ | ||
/*! | ||
* transform | ||
* | ||
* Date: 2016/8/1 | ||
* | ||
* This is licensed under the MIT License (MIT). | ||
* For details, see: https://github.com/nuintun/fengine/blob/master/LICENSE | ||
/** | ||
* @module transform | ||
* @license MIT | ||
* @version 2017/11/14 | ||
*/ | ||
@@ -13,9 +10,9 @@ | ||
// Import lib | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var utils = require('./utils'); | ||
var Events = require('./events'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const utils = require('./utils'); | ||
const Events = require('./events'); | ||
// Variable declaration | ||
var DIRECTIVE = { | ||
const DIRECTIVE = { | ||
SKIP: 'skip', | ||
@@ -27,3 +24,3 @@ SLOT: 'slot', | ||
}; | ||
var EVENTS = { | ||
const EVENTS = { | ||
END: 'end', | ||
@@ -33,5 +30,5 @@ DATA: 'data', | ||
}; | ||
var CWD = process.cwd(); | ||
const CWD = process.cwd(); | ||
// Default options | ||
var DEFAULTOPTIONS = { | ||
const DEFAULTOPTIONS = { | ||
root: CWD, | ||
@@ -44,12 +41,11 @@ layout: null, | ||
data: { | ||
dirname: function() { | ||
var context = this; | ||
var dirname = utils.normalize(path.relative(context.root, context.dirname)); | ||
dirname() { | ||
const dirname = utils.normalize(path.relative(this.root, this.dirname)); | ||
return dirname ? dirname + '/' : dirname; | ||
}, | ||
filename: function() { | ||
filename() { | ||
return this.filename; | ||
}, | ||
extname: function() { | ||
extname() { | ||
return this.extname; | ||
@@ -61,26 +57,18 @@ } | ||
/** | ||
* assert | ||
* | ||
* @type {{ | ||
* tags: assert.tags, | ||
* data: assert.data, | ||
* layout: assert.layout | ||
* }} | ||
* @namespace assert | ||
*/ | ||
var assert = { | ||
const assert = { | ||
/** | ||
* tags | ||
* | ||
* @method tags | ||
* @param {Transform} context | ||
* @returns {void} | ||
*/ | ||
tags: function(context) { | ||
var options = context.options; | ||
tags(context) { | ||
const options = context.options; | ||
function assert(key) { | ||
var def = DEFAULTOPTIONS.tags[key]; | ||
var tags = options.tags[key]; | ||
const assert = (key) => { | ||
const def = DEFAULTOPTIONS.tags[key]; | ||
const tags = options.tags[key]; | ||
if (!Array.isArray(tags)) { | ||
options.tags[key] = def.map(function(tag) { | ||
options.tags[key] = def.map((tag) => { | ||
return utils.str4regex(tag); | ||
@@ -107,14 +95,12 @@ }); | ||
/** | ||
* data | ||
* | ||
* @method data | ||
* @param {Transform} context | ||
* @returns {void} | ||
*/ | ||
data: function(context) { | ||
var options = context.options; | ||
data(context) { | ||
const options = context.options; | ||
if (utils.typeOf(options.data) === 'object') { | ||
Object.keys(options.data).forEach(function(key) { | ||
var def = DEFAULTOPTIONS.data[key]; | ||
var data = options.data[key]; | ||
Object.keys(options.data).forEach((key) => { | ||
let data = options.data[key]; | ||
const def = DEFAULTOPTIONS.data[key]; | ||
@@ -140,9 +126,7 @@ if (utils.fn(data)) { | ||
/** | ||
* layout | ||
* | ||
* @method layout | ||
* @param {Transform} context | ||
* @returns {void} | ||
*/ | ||
layout: function(context) { | ||
var options = context.options; | ||
layout(context) { | ||
const options = context.options; | ||
@@ -160,677 +144,631 @@ options.layout = utils.fn(options.layout) | ||
/** | ||
* Transform | ||
* | ||
* @param {String} src | ||
* @param {Buffer|String} source | ||
* @param {Object} options | ||
* @constructor | ||
* @class Transform | ||
* @extends Events | ||
*/ | ||
function Transform(src, source, options) { | ||
// Buffer | ||
if (Buffer.isBuffer(source)) { | ||
source = source.toString(); | ||
} | ||
class Transform extends Events { | ||
/** | ||
* @constructor | ||
* @param {string} src | ||
* @param {Buffer|string} source | ||
* @param {Object} options | ||
* @returns {Transform} | ||
*/ | ||
constructor(src, source, options) { | ||
// Buffer | ||
if (Buffer.isBuffer(source)) { | ||
source = source.toString(); | ||
} | ||
// Src must be a string | ||
if (!utils.string(src)) { | ||
throw new TypeError('src must be a file path.'); | ||
} | ||
// Src must be a string | ||
if (!utils.string(src)) { | ||
throw new TypeError('src must be a file path.'); | ||
} | ||
// Source must be a string | ||
if (!utils.string(source)) { | ||
throw new TypeError('source must be a string or buffer.'); | ||
} | ||
// Source must be a string | ||
if (!utils.string(source)) { | ||
throw new TypeError('source must be a string or buffer.'); | ||
} | ||
// Context | ||
var context = this; | ||
super(); | ||
// Property | ||
context.index = 0; | ||
context.slot = null; | ||
context.slotted = ''; | ||
context.parent = null; | ||
context.layout = null; | ||
context.isMaster = true; | ||
context.source = source; | ||
context.finished = false; | ||
context.options = options = utils.extend(true, {}, DEFAULTOPTIONS, options); | ||
// Property | ||
this.index = 0; | ||
this.slot = null; | ||
this.slotted = ''; | ||
this.parent = null; | ||
this.layout = null; | ||
this.isMaster = true; | ||
this.source = source; | ||
this.finished = false; | ||
this.options = options = utils.extend(true, {}, DEFAULTOPTIONS, options); | ||
// Path | ||
context.src = path.resolve(CWD, src); | ||
context.dirname = path.dirname(context.src); | ||
context.extname = path.extname(context.src); | ||
context.filename = path.basename(context.src, context.extname); | ||
context.root = path.resolve(CWD, utils.string(options.root) ? options.root : CWD); | ||
// Path | ||
this.src = path.resolve(CWD, src); | ||
this.dirname = path.dirname(this.src); | ||
this.extname = path.extname(this.src); | ||
this.filename = path.basename(this.src, this.extname); | ||
this.root = path.resolve(CWD, utils.string(options.root) ? options.root : CWD); | ||
// Transform start | ||
context.transform(); | ||
} | ||
// Transform start | ||
this.transform(); | ||
} | ||
/** | ||
* extend | ||
* | ||
* @type {Events} | ||
*/ | ||
Transform.prototype = Object.create(Events.prototype, { | ||
constructor: { value: Transform } | ||
}); | ||
/** | ||
* @method isSameTags | ||
* @description Is same tags | ||
* @returns {boolean} | ||
*/ | ||
isSameTags() { | ||
const tags = this.options.tags; | ||
const data = tags.data; | ||
const directive = tags.directive; | ||
/** | ||
* Is same tags | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
Transform.prototype.isSameTags = function() { | ||
var tags = this.options.tags; | ||
var data = tags.data; | ||
var directive = tags.directive; | ||
return data[0] === directive[0] && data[1] === directive[1]; | ||
} | ||
return data[0] === directive[0] && data[1] === directive[1]; | ||
}; | ||
/** | ||
* @method createSeparator | ||
* @description Create separator | ||
*/ | ||
createSeparator() { | ||
const options = this.options; | ||
/** | ||
* Create separator | ||
*/ | ||
Transform.prototype.createSeparator = function() { | ||
var context = this; | ||
var options = context.options; | ||
// Main file init tags and data | ||
if (this.isMaster) { | ||
assert.tags(this); | ||
assert.data(this); | ||
} | ||
// Main file init tags and data | ||
if (context.isMaster) { | ||
assert.tags(context); | ||
assert.data(context); | ||
} | ||
// The tags and directive | ||
const unique = {}; | ||
const dataDirective = []; | ||
const dataTags = options.tags.data; | ||
const directiveTags = options.tags.directive; | ||
const isSameTags = this.isSameTags(); | ||
// The tags and directive | ||
var unique = {}; | ||
var dataDirective = []; | ||
var dataTags = options.tags.data; | ||
var directiveTags = options.tags.directive; | ||
var isSameTags = context.isSameTags(); | ||
// Create data directive | ||
for (let data in options.data) { | ||
if (options.data.hasOwnProperty(data)) { | ||
data = data.toLowerCase(); | ||
// Create data directive | ||
for (var data in options.data) { | ||
if (options.data.hasOwnProperty(data)) { | ||
data = data.toLowerCase(); | ||
if (!unique[data]) { | ||
unique[data] = true; | ||
if (!unique[data]) { | ||
unique[data] = true; | ||
// Trim data | ||
const trimmed = data.trim(); | ||
// Trim data | ||
var trimmed = data.trim(); | ||
if (isSameTags | ||
&& (trimmed === DIRECTIVE.SKIP | ||
|| trimmed === DIRECTIVE.SLOT | ||
|| trimmed === DIRECTIVE.LAYOUT | ||
|| trimmed === DIRECTIVE.INCLUDE | ||
|| trimmed === DIRECTIVE.NONLAYOUT)) { | ||
continue; | ||
} | ||
if (isSameTags | ||
&& (trimmed === DIRECTIVE.SKIP | ||
|| trimmed === DIRECTIVE.SLOT | ||
|| trimmed === DIRECTIVE.LAYOUT | ||
|| trimmed === DIRECTIVE.INCLUDE | ||
|| trimmed === DIRECTIVE.NONLAYOUT)) { | ||
continue; | ||
dataDirective.push(utils.str4regex(data)); | ||
} | ||
dataDirective.push(utils.str4regex(data)); | ||
} | ||
} | ||
} | ||
// Separator regexp | ||
context.separator = { | ||
transform: new RegExp( | ||
directiveTags[0] | ||
+ '\\s*(' | ||
+ utils.str4regex(DIRECTIVE.INCLUDE) | ||
+ '\\s*\\(\\s*(.+?)\\s*\\)|' | ||
+ utils.str4regex(DIRECTIVE.SLOT) | ||
+ ')\\s*' | ||
+ directiveTags[1] | ||
+ '|' | ||
+ dataTags[0] | ||
+ '\\s*(' | ||
+ dataDirective.join('|') | ||
+ ')\\s*' | ||
+ dataTags[1], | ||
'gim' | ||
), | ||
layout: new RegExp( | ||
directiveTags[0] | ||
+ '\\s*(?:' | ||
+ utils.str4regex(DIRECTIVE.LAYOUT) | ||
+ '\\s*\\(\\s*(.+?)\\s*\\)|' | ||
+ utils.str4regex(DIRECTIVE.NONLAYOUT) | ||
+ ')\\s*' | ||
+ directiveTags[1], | ||
'gim' | ||
), | ||
skip: new RegExp( | ||
directiveTags[0] | ||
+ '\\s*' | ||
+ utils.str4regex(DIRECTIVE.SKIP) | ||
+ '\\s*' | ||
+ directiveTags[1], | ||
'gim' | ||
) | ||
}; | ||
}; | ||
/** | ||
* resolve | ||
* | ||
* @param {String} url | ||
* @returns {String} | ||
*/ | ||
Transform.prototype.resolve = function(url) { | ||
var context = this; | ||
if (/^[\\/]/.test(url)) { | ||
return path.join(context.root, url); | ||
} else { | ||
return path.join(context.dirname, url); | ||
// Separator regexp | ||
this.separator = { | ||
transform: new RegExp( | ||
directiveTags[0] | ||
+ '\\s*(' | ||
+ utils.str4regex(DIRECTIVE.INCLUDE) | ||
+ '\\s*\\(\\s*(.+?)\\s*\\)|' | ||
+ utils.str4regex(DIRECTIVE.SLOT) | ||
+ ')\\s*' | ||
+ directiveTags[1] | ||
+ '|' | ||
+ dataTags[0] | ||
+ '\\s*(' | ||
+ dataDirective.join('|') | ||
+ ')\\s*' | ||
+ dataTags[1], | ||
'gim' | ||
), | ||
layout: new RegExp( | ||
directiveTags[0] | ||
+ '\\s*(?:' | ||
+ utils.str4regex(DIRECTIVE.LAYOUT) | ||
+ '\\s*\\(\\s*(.+?)\\s*\\)|' | ||
+ utils.str4regex(DIRECTIVE.NONLAYOUT) | ||
+ ')\\s*' | ||
+ directiveTags[1], | ||
'gim' | ||
), | ||
skip: new RegExp( | ||
directiveTags[0] | ||
+ '\\s*' | ||
+ utils.str4regex(DIRECTIVE.SKIP) | ||
+ '\\s*' | ||
+ directiveTags[1], | ||
'gim' | ||
) | ||
}; | ||
} | ||
}; | ||
/** | ||
* write | ||
* | ||
* @param {String} data | ||
* @param {String} [type] | ||
* @returns {String} | ||
*/ | ||
Transform.prototype.write = function(data, type) { | ||
var context = this; | ||
// Data type | ||
type = type || 'context'; | ||
// Cache slotted | ||
if (context.layout && type !== 'layout') { | ||
context.layout.slotted += data; | ||
/** | ||
* @method resolve | ||
* @param {string} url | ||
* @returns {string} | ||
*/ | ||
resolve(url) { | ||
if (/^[\\/]/.test(url)) { | ||
return path.join(this.root, url); | ||
} else { | ||
return path.join(this.dirname, url); | ||
} | ||
} | ||
// Emit data event | ||
context.emit(EVENTS.DATA, data, type); | ||
/** | ||
* @method write | ||
* @param {string} data | ||
* @param {string} [type] | ||
* @returns {string} | ||
*/ | ||
write(data, type) { | ||
// Data type | ||
type = type || 'context'; | ||
return data; | ||
}; | ||
// Cache slotted | ||
if (this.layout && type !== 'layout') { | ||
this.layout.slotted += data; | ||
} | ||
/** | ||
* end | ||
* | ||
* @param {String} data | ||
* @param {String} [type] | ||
* @returns {String} | ||
*/ | ||
Transform.prototype.end = function(data, type) { | ||
var context = this; | ||
// Emit data event | ||
this.emit(EVENTS.DATA, data, type); | ||
// Write data | ||
if (arguments.length) { | ||
context.write(data, type); | ||
return data; | ||
} | ||
// Delete prop | ||
context.slot = null; | ||
context.slotted = ''; | ||
context.layout = null; | ||
context.parent = null; | ||
/** | ||
* @method end | ||
* @param {string} data | ||
* @param {string} [type] | ||
* @returns {string} | ||
*/ | ||
end(data, type) { | ||
// Write data | ||
if (arguments.length) { | ||
this.write(data, type); | ||
} | ||
// Emit end event | ||
context.emit(EVENTS.END); | ||
// Delete prop | ||
this.slot = null; | ||
this.slotted = ''; | ||
this.layout = null; | ||
this.parent = null; | ||
return data; | ||
}; | ||
// Emit end event | ||
this.emit(EVENTS.END); | ||
/** | ||
* next | ||
* | ||
* @param {String} data | ||
* @param {String} [type] | ||
* @returns {void} | ||
*/ | ||
Transform.prototype.next = function(data, type) { | ||
var context = this; | ||
// Write data | ||
if (arguments.length) { | ||
context.write(data, type); | ||
return data; | ||
} | ||
// Last match | ||
if (!context.exec()) { | ||
// Cache slotted | ||
if (context.isLayout() && context.layout) { | ||
context.layout.slotted += context.slotted; | ||
/** | ||
* @method next | ||
* @param {string} data | ||
* @param {string} [type] | ||
*/ | ||
next(data, type) { | ||
// Write data | ||
if (arguments.length) { | ||
this.write(data, type); | ||
} | ||
// Write end data | ||
context.write(context.source.substring(context.index)); | ||
// Last match | ||
if (!this.exec()) { | ||
// Cache slotted | ||
if (this.isLayout() && this.layout) { | ||
this.layout.slotted += this.slotted; | ||
} | ||
// Set index to end | ||
context.index = context.source.length; | ||
// Write end data | ||
this.write(this.source.substring(this.index)); | ||
// Finished | ||
context.finished = true; | ||
// Set index to end | ||
this.index = this.source.length; | ||
// Call layout next or context end | ||
if (context.layout) { | ||
context.layout.next(); | ||
} else { | ||
context.end(); | ||
// Finished | ||
this.finished = true; | ||
// Call layout next or context end | ||
if (this.layout) { | ||
this.layout.next(); | ||
} else { | ||
this.end(); | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* thread | ||
* | ||
* @param {String} src | ||
* @param {Buffer|String} source | ||
* @returns {Transform} | ||
*/ | ||
Transform.prototype.thread = function(src, source) { | ||
var context = this; | ||
var options = utils.extend(true, {}, context.options); | ||
/** | ||
* @method thread | ||
* @param {string} src | ||
* @param {Buffer|string} source | ||
* @returns {Transform} | ||
*/ | ||
thread(src, source) { | ||
const options = utils.extend(true, {}, this.options); | ||
// Reset layout | ||
options.layout = null; | ||
// Reset layout | ||
options.layout = null; | ||
var thread = new Transform(src, source, options); | ||
const thread = new Transform(src, source, options); | ||
// Not main file | ||
thread.isMaster = false; | ||
// Not main file | ||
thread.isMaster = false; | ||
return thread; | ||
}; | ||
return thread; | ||
} | ||
/** | ||
* error | ||
* | ||
* @param {String} type | ||
* @param {Transform} context | ||
* @param {String} message | ||
*/ | ||
Transform.prototype.error = function(type, context, message) { | ||
this.emit(EVENTS.ERROR, type, context, message); | ||
}; | ||
/** | ||
* @method error | ||
* @param {string} type | ||
* @param {Transform} context | ||
* @param {string} message | ||
*/ | ||
error(type, context, message) { | ||
this.emit(EVENTS.ERROR, type, context, message); | ||
} | ||
/** | ||
* io error | ||
* | ||
* @param {Transform} context | ||
* @param {String} message | ||
*/ | ||
Transform.prototype.io = function(context, message) { | ||
this.error('io', context, message); | ||
}; | ||
/** | ||
* @method io | ||
* @description io error | ||
* @param {Transform} context | ||
* @param {string} message | ||
*/ | ||
io(context, message) { | ||
this.error('io', context, message); | ||
} | ||
/** | ||
* circle error | ||
* | ||
* @param {Transform} context | ||
* @param {String} message | ||
*/ | ||
Transform.prototype.circle = function(context, message) { | ||
this.error('circle', context, message); | ||
}; | ||
/** | ||
* @method circle | ||
* @description circle error | ||
* @param {Transform} context | ||
* @param {string} message | ||
*/ | ||
circle(context, message) { | ||
this.error('circle', context, message); | ||
} | ||
/** | ||
* Is skip | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
Transform.prototype.isSkip = function() { | ||
var context = this; | ||
var separator = context.separator; | ||
/** | ||
* @method isSkip | ||
* @returns {boolean} | ||
*/ | ||
isSkip() { | ||
const separator = this.separator; | ||
return !!context.source.match(separator.skip); | ||
}; | ||
return !!this.source.match(separator.skip); | ||
} | ||
/** | ||
* Is layout | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
Transform.prototype.isLayout = function() { | ||
return !!this.slot; | ||
}; | ||
/** | ||
* Is cyclic layout | ||
* | ||
* @param {String|null} layout | ||
* @returns {Boolean} | ||
*/ | ||
Transform.prototype.isCyclicLayout = function(layout) { | ||
var context = this; | ||
// Layout is null | ||
if (!layout) return false; | ||
if (layout === context.src) { | ||
return true; | ||
/** | ||
* @method isLayout | ||
* @returns {boolean} | ||
*/ | ||
isLayout() { | ||
return !!this.slot; | ||
} | ||
var slot = context.slot; | ||
/** | ||
* @method isCyclicLayout | ||
* @param {string|null} layout | ||
* @returns {boolean} | ||
*/ | ||
isCyclicLayout(layout) { | ||
// Layout is null | ||
if (!layout) return false; | ||
// Loop | ||
while (slot) { | ||
if (layout === slot.src) { | ||
if (layout === this.src) { | ||
return true; | ||
} | ||
slot = slot.slot; | ||
} | ||
let slot = this.slot; | ||
return false; | ||
}; | ||
// Loop | ||
while (slot) { | ||
if (layout === slot.src) { | ||
return true; | ||
} | ||
/** | ||
* Is cyclic include | ||
* | ||
* @param {String} src | ||
* @returns {Boolean} | ||
*/ | ||
Transform.prototype.isCyclicInclude = function(src) { | ||
var context = this; | ||
slot = slot.slot; | ||
} | ||
// Src is null | ||
if (!src) return false; | ||
if (src === context.src) { | ||
return true; | ||
return false; | ||
} | ||
var parent = context.parent; | ||
/** | ||
* @method isCyclicInclude | ||
* @param {string} src | ||
* @returns {boolean} | ||
*/ | ||
isCyclicInclude(src) { | ||
// Src is null | ||
if (!src) return false; | ||
// Loop | ||
while (parent) { | ||
if (src === parent.src) { | ||
if (src === this.src) { | ||
return true; | ||
} | ||
parent = parent.parent; | ||
} | ||
let parent = this.parent; | ||
return false; | ||
}; | ||
/** | ||
* Match layout | ||
* | ||
* @returns {{ | ||
* src: {String}, | ||
* command: {String} | ||
* }} | ||
*/ | ||
Transform.prototype.matchLayout = function() { | ||
var src = null; | ||
var command = null; | ||
var context = this; | ||
var separator = context.separator; | ||
var match = separator.layout.exec(context.source); | ||
if (match) { | ||
while (match) { | ||
if (match) { | ||
src = match[1]; | ||
command = match[0]; | ||
if (src) { | ||
src = context.resolve(src); | ||
} | ||
// Loop | ||
while (parent) { | ||
if (src === parent.src) { | ||
return true; | ||
} | ||
match = separator.layout.exec(context.source); | ||
parent = parent.parent; | ||
} | ||
} else { | ||
src = context.options.layout; | ||
return false; | ||
} | ||
return { | ||
src: src, | ||
command: command | ||
}; | ||
}; | ||
/** | ||
* @method matchLayout | ||
* @returns {{ | ||
* src: {string}, | ||
* command: {string} | ||
* }} | ||
*/ | ||
matchLayout() { | ||
let src = null; | ||
let command = null; | ||
const separator = this.separator; | ||
let match = separator.layout.exec(this.source); | ||
/** | ||
* Set layout | ||
* | ||
* @param {String} command | ||
* @param {String} src | ||
* @returns {String} | ||
*/ | ||
Transform.prototype.setLayout = function(command, src) { | ||
// Read layout | ||
fs.readFile(src, function(error, source) { | ||
var context = this; | ||
if (match) { | ||
while (match) { | ||
if (match) { | ||
src = match[1]; | ||
command = match[0]; | ||
if (error) { | ||
context.io(context, command); | ||
if (src) { | ||
src = this.resolve(src); | ||
} | ||
} | ||
return context.skipLayout(); | ||
match = separator.layout.exec(this.source); | ||
} | ||
} else { | ||
src = this.options.layout; | ||
} | ||
return { | ||
src: src, | ||
command: command | ||
}; | ||
} | ||
/** | ||
* @method setLayout | ||
* @param {string} command | ||
* @param {string} src | ||
* @returns {string} | ||
*/ | ||
setLayout(command, src) { | ||
// Read layout | ||
var layout = context.thread(src, source); | ||
fs.readFile(src, (error, source) => { | ||
if (error) { | ||
this.io(this, command); | ||
// Set context layout | ||
context.layout = layout; | ||
return this.skipLayout(); | ||
} | ||
// Set layout slot | ||
layout.slot = context; | ||
// Read layout | ||
const layout = this.thread(src, source); | ||
// Data event | ||
layout.on(EVENTS.DATA, function(data) { | ||
context.write(data, 'layout'); | ||
}); | ||
// Set context layout | ||
this.layout = layout; | ||
// Circle event | ||
layout.on(EVENTS.ERROR, function(type, file, message) { | ||
context.error(type, file, message); | ||
}); | ||
// Set layout slot | ||
layout.slot = this; | ||
// End event | ||
layout.once(EVENTS.END, function() { | ||
context.end(); | ||
}); | ||
}.bind(this)); | ||
// Data event | ||
layout.on(EVENTS.DATA, (data) => { | ||
this.write(data, 'layout'); | ||
}); | ||
return src; | ||
}; | ||
// Circle event | ||
layout.on(EVENTS.ERROR, (type, file, message) => { | ||
this.error(type, file, message); | ||
}); | ||
/** | ||
* Skip layout | ||
*/ | ||
Transform.prototype.skipLayout = function() { | ||
var context = this; | ||
// End event | ||
layout.once(EVENTS.END, () => { | ||
this.end(); | ||
}); | ||
}); | ||
context.layout = null; | ||
return src; | ||
} | ||
context.next(); | ||
}; | ||
/** | ||
* @method skipLayout | ||
*/ | ||
skipLayout() { | ||
this.layout = null; | ||
/** | ||
* Include file | ||
* | ||
* @param {String} command | ||
* @param {String} src | ||
* @returns {String} | ||
*/ | ||
Transform.prototype.include = function(command, src) { | ||
// Read include | ||
fs.readFile(src, function(error, source) { | ||
var context = this; | ||
this.next(); | ||
} | ||
if (error) { | ||
context.io(context, command); | ||
context.write(command); | ||
/** | ||
* @method include | ||
* @param {string} command | ||
* @param {string} src | ||
* @returns {string} | ||
*/ | ||
include(command, src) { | ||
// Read include | ||
fs.readFile(src, (error, source) => { | ||
if (error) { | ||
this.io(this, command); | ||
this.write(command); | ||
return context.next(); | ||
} | ||
return this.next(); | ||
} | ||
// Read include | ||
var include = context.thread(src, source); | ||
// Read include | ||
const include = this.thread(src, source); | ||
// Set parent | ||
include.parent = context; | ||
// Set parent | ||
include.parent = this; | ||
// Data event | ||
include.on(EVENTS.DATA, function(data) { | ||
context.write(data, 'include'); | ||
}); | ||
// Data event | ||
include.on(EVENTS.DATA, (data) => { | ||
this.write(data, 'include'); | ||
}); | ||
// Circle event | ||
include.on(EVENTS.ERROR, function(type, file, message) { | ||
context.error(type, file, message); | ||
}); | ||
// Circle event | ||
include.on(EVENTS.ERROR, (type, file, message) => { | ||
this.error(type, file, message); | ||
}); | ||
// End event | ||
include.once(EVENTS.END, function() { | ||
context.next(); | ||
// End event | ||
include.once(EVENTS.END, () => { | ||
this.next(); | ||
}); | ||
}); | ||
}.bind(this)); | ||
return src; | ||
}; | ||
return src; | ||
} | ||
/** | ||
* Print data | ||
* | ||
* @param {String} command | ||
* @param {String} data | ||
* @returns {String} | ||
*/ | ||
Transform.prototype.print = function(command, data) { | ||
var context = this; | ||
var options = context.options; | ||
/** | ||
* @method print | ||
* @param {string} command | ||
* @param {string} data | ||
* @returns {string} | ||
*/ | ||
print(command, data) { | ||
const options = this.options; | ||
data = options.data.hasOwnProperty(data) | ||
? options.data[data] | ||
: command; | ||
data = options.data.hasOwnProperty(data) | ||
? options.data[data] | ||
: command; | ||
context.next(data); | ||
this.next(data); | ||
return data; | ||
}; | ||
return data; | ||
} | ||
/** | ||
* exec | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
Transform.prototype.exec = function() { | ||
var context = this; | ||
var separator = context.separator; | ||
var match = separator.transform.exec(context.source); | ||
/** | ||
* @method exec | ||
* @returns {boolean} | ||
*/ | ||
exec() { | ||
const separator = this.separator; | ||
let match = separator.transform.exec(this.source); | ||
if (!match) return false; | ||
if (!match) return false; | ||
var index = match.index; | ||
var type = match[1] ? 'directive' : 'data'; | ||
var command = type === 'directive' ? match[1] : match[3]; | ||
var data = type === 'directive' ? match[2] : match[3]; | ||
const index = match.index; | ||
const type = match[1] ? 'directive' : 'data'; | ||
const command = type === 'directive' ? match[1] : match[3]; | ||
const data = type === 'directive' ? match[2] : match[3]; | ||
// Matched string | ||
match = match[0]; | ||
// Matched string | ||
match = match[0]; | ||
// Write source before match | ||
context.write(context.source.substring(context.index, index)); | ||
// Write source before match | ||
this.write(this.source.substring(this.index, index)); | ||
// Set index | ||
context.index = index + match.length; | ||
// Set index | ||
this.index = index + match.length; | ||
// Switch type | ||
switch (type) { | ||
case 'data': | ||
context.print(match, data); | ||
break; | ||
case 'directive': | ||
// Ignore case | ||
var commandIgnoreCase = command.toLowerCase(); | ||
// Switch type | ||
switch (type) { | ||
case 'data': | ||
this.print(match, data); | ||
break; | ||
case 'directive': | ||
// Ignore case | ||
const commandIgnoreCase = command.toLowerCase(); | ||
// Command switch | ||
switch (commandIgnoreCase) { | ||
case DIRECTIVE.SLOT: | ||
if (context.isLayout()) { | ||
if (context.slot.finished) { | ||
context.next(context.slotted); | ||
// Command switch | ||
switch (commandIgnoreCase) { | ||
case DIRECTIVE.SLOT: | ||
if (this.isLayout()) { | ||
if (this.slot.finished) { | ||
this.next(this.slotted); | ||
} else { | ||
this.slot.next(); | ||
} | ||
} else { | ||
context.slot.next(); | ||
this.next(match); | ||
} | ||
} else { | ||
context.next(match); | ||
} | ||
break; | ||
default: | ||
if (data && commandIgnoreCase.indexOf(DIRECTIVE.INCLUDE) === 0) { | ||
var src = context.resolve(data); | ||
break; | ||
default: | ||
if (data && commandIgnoreCase.indexOf(DIRECTIVE.INCLUDE) === 0) { | ||
const src = this.resolve(data); | ||
// Cyclic include | ||
if (context.isCyclicInclude(src)) { | ||
context.circle(context, match); | ||
context.next(match); | ||
// Cyclic include | ||
if (this.isCyclicInclude(src)) { | ||
this.circle(this, match); | ||
this.next(match); | ||
return true; | ||
return true; | ||
} | ||
// Include | ||
this.include(match, src); | ||
} else { | ||
this.next(match); | ||
} | ||
break; | ||
} | ||
break; | ||
default: | ||
this.next(match); | ||
break; | ||
} | ||
// Include | ||
context.include(match, src); | ||
} else { | ||
context.next(match); | ||
} | ||
break; | ||
} | ||
break; | ||
default: | ||
context.next(match); | ||
break; | ||
return true; | ||
} | ||
return true; | ||
}; | ||
/** | ||
* @method transform | ||
*/ | ||
transform() { | ||
// Start transform in next tick | ||
process.nextTick(() => { | ||
// Create separator | ||
this.createSeparator(); | ||
/** | ||
* transform | ||
*/ | ||
Transform.prototype.transform = function() { | ||
// Start transform in next tick | ||
process.nextTick(function() { | ||
var context = this; | ||
// Has skip command | ||
if (this.isSkip()) { | ||
this.write(this.source); | ||
// Create separator | ||
context.createSeparator(); | ||
// Set index to end | ||
this.index = this.source.length; | ||
// Has skip command | ||
if (context.isSkip()) { | ||
context.write(context.source); | ||
return this.end(); | ||
} | ||
// Set index to end | ||
context.index = context.source.length; | ||
// Main file init layout | ||
if (this.isMaster) { | ||
assert.layout(this); | ||
} | ||
return context.end(); | ||
} | ||
// Match layout | ||
const layout = this.matchLayout(); | ||
const src = layout.src; | ||
const command = layout.command; | ||
// Main file init layout | ||
if (context.isMaster) { | ||
assert.layout(context); | ||
} | ||
if (src && !this.isCyclicLayout(src)) { | ||
this.setLayout(command, src); | ||
} else { | ||
if (command && src) { | ||
this.circle(this, command); | ||
} | ||
// Match layout | ||
var layout = context.matchLayout(); | ||
var src = layout.src; | ||
var command = layout.command; | ||
if (src && !context.isCyclicLayout(src)) { | ||
context.setLayout(command, src); | ||
} else { | ||
if (command && src) { | ||
context.circle(context, command); | ||
// Skip layout | ||
this.skipLayout(); | ||
} | ||
}); | ||
} | ||
} | ||
// Skip layout | ||
context.skipLayout(); | ||
} | ||
}.bind(this)); | ||
}; | ||
// Exports | ||
module.exports = Transform; |
424
lib/utils.js
@@ -1,8 +0,5 @@ | ||
/*! | ||
* utils | ||
* | ||
* Date: 2016/7/29 | ||
* | ||
* This is licensed under the MIT License (MIT). | ||
* For details, see: https://github.com/nuintun/fengine/blob/master/LICENSE | ||
/** | ||
* @module utils | ||
* @license MIT | ||
* @version 2017/11/14 | ||
*/ | ||
@@ -12,23 +9,27 @@ | ||
var fs = require('fs'); | ||
var chalk = require('chalk'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const chalk = require('chalk'); | ||
const parseURL = require('url').parse; | ||
const relative = path.relative; | ||
// Prototype method | ||
var toString = Object.prototype.toString; | ||
var getPrototypeOf = Object.getPrototypeOf; | ||
var hasOwnProperty = Object.prototype.hasOwnProperty; | ||
var fnToString = hasOwnProperty.toString; | ||
var objectFunctionString = fnToString.call(Object); | ||
const toString = Object.prototype.toString; | ||
const getPrototypeOf = Object.getPrototypeOf; | ||
const hasOwnProperty = Object.prototype.hasOwnProperty; | ||
const fnToString = hasOwnProperty.toString; | ||
const objectFunctionString = fnToString.call(Object); | ||
// Variable declaration | ||
var EXTRACTTYPE_RE = /\[object (.+)\]/; | ||
var REGEXSYMBOL_RE = /[\[\]\\.^|()*+$:?!-]/g; | ||
var LOG_LEVELS = { INFO: 0, WARN: 1, ERROR: 2 }; | ||
const EXTRACTTYPE_RE = /\[object (.+)\]/; | ||
const REGEXSYMBOL_RE = /[\[\]\\.^|()*+$:?!-]/g; | ||
const LOG_LEVELS = { INFO: 0, WARN: 1, ERROR: 2 }; | ||
/** | ||
* Simple date format | ||
* | ||
* @param date | ||
* @param format | ||
* @returns {String} | ||
* @function dateFormat | ||
* @description Simple date format | ||
* @param {Date} date | ||
* @param {string} format | ||
* @returns {string} | ||
*/ | ||
@@ -42,3 +43,3 @@ function dateFormat(date, format) { | ||
var map = { | ||
const map = { | ||
'Y': date.getFullYear(), // Year | ||
@@ -52,4 +53,4 @@ 'M': date.getMonth() + 1, // Month | ||
format = format.replace(/([YMDhms])+/g, function(matched, key) { | ||
var value = map[key]; | ||
format = format.replace(/([YMDhms])+/g, (matched, key) => { | ||
let value = map[key]; | ||
@@ -72,10 +73,9 @@ if (key === 'Y') { | ||
/** | ||
* typeOf | ||
* | ||
* @param value | ||
* @returns {String} | ||
* @function typeOf | ||
* @param {any} value | ||
* @returns {string} | ||
*/ | ||
function typeOf(value) { | ||
// Get real type | ||
var type = toString.call(value).toLowerCase(); | ||
let type = toString.call(value).toLowerCase(); | ||
@@ -102,6 +102,5 @@ type = type.replace(EXTRACTTYPE_RE, '$1').toLowerCase(); | ||
/** | ||
* Is function | ||
* | ||
* @param value | ||
* @returns {Boolean} | ||
* @function isFunction | ||
* @param {any} value | ||
* @returns {boolean} | ||
*/ | ||
@@ -113,6 +112,5 @@ function isFunction(value) { | ||
/** | ||
* Is string | ||
* | ||
* @param value | ||
* @returns {Boolean} | ||
* @function isString | ||
* @param {any} value | ||
* @returns {boolean} | ||
*/ | ||
@@ -124,19 +122,8 @@ function isString(value) { | ||
/** | ||
* Is number | ||
* | ||
* @param value | ||
* @returns {Boolean} | ||
* @function isPlainObject | ||
* @param {any} value | ||
* @returns {boolean} | ||
*/ | ||
function isNumber(value) { | ||
return typeOf(value) === 'number'; | ||
} | ||
/** | ||
* Is plain object | ||
* | ||
* @param value | ||
* @returns {Boolean} | ||
*/ | ||
function isPlainObject(value) { | ||
var proto, ctor; | ||
let proto, ctor; | ||
@@ -163,12 +150,11 @@ // Detect obvious negatives | ||
/** | ||
* extend | ||
* | ||
* @function extend | ||
* @returns {Object} | ||
*/ | ||
function extend() { | ||
var i = 1; | ||
var deep = false; | ||
var length = arguments.length; | ||
var target = arguments[0] || {}; | ||
var options, name, src, copy, copyIsArray, clone; | ||
let i = 1; | ||
let deep = false; | ||
let target = arguments[0] || {}; | ||
const length = arguments.length; | ||
let options, name, src, copy, copyIsArray, clone; | ||
@@ -229,5 +215,5 @@ // Handle a deep copy situation | ||
/** | ||
* String for regex | ||
* | ||
* @param string | ||
* @function str4regex | ||
* @description String for regex | ||
* @param {string} string | ||
* @returns {void|XML} | ||
@@ -241,164 +227,190 @@ */ | ||
// Exports | ||
module.exports = { | ||
typeOf: typeOf, | ||
fn: isFunction, | ||
extend: extend, | ||
string: isString, | ||
number: isNumber, | ||
str4regex: str4regex, | ||
/** | ||
* Normalize path | ||
* | ||
* @param path | ||
* @returns {String} | ||
*/ | ||
normalize: function(path) { | ||
// \a\b\.\c\.\d ==> /a/b/./c/./d | ||
path = path.replace(/\\/g, '/'); | ||
/** | ||
* @function normalize | ||
* @description Normalize path | ||
* @param {string} path | ||
* @returns {string} | ||
*/ | ||
function normalize(path) { | ||
// \a\b\.\c\.\d ==> /a/b/./c/./d | ||
path = path.replace(/\\/g, '/'); | ||
// :///a/b/c ==> ://a/b/c | ||
path = path.replace(/(:)?\/{2,}/, '$1//'); | ||
// :///a/b/c ==> ://a/b/c | ||
path = path.replace(/:\/{3,}/, '://'); | ||
// /a/b/./c/./d ==> /a/b/c/d | ||
path = path.replace(/\/\.\//g, '/'); | ||
// /a/b/./c/./d ==> /a/b/c/d | ||
path = path.replace(/\/\.\//g, '/'); | ||
// @author wh1100717 | ||
// a//b/c ==> a/b/c | ||
// a///b/////c ==> a/b/c | ||
path = path.replace(/([^:/])\/+\//g, '$1/'); | ||
// a//b/c ==> a/b/c | ||
// //a//b/c ==> a/b/c | ||
// a///b/////c ==> a/b/c | ||
path = path.replace(/\/{2,}/g, '/'); | ||
// Transfer path | ||
var src = path; | ||
// DOUBLE_DOT_RE matches a/b/c//../d path correctly only if replace // with / first | ||
var DOUBLE_DOT_RE = /([^/]+)\/\.\.(?:\/|$)/g; | ||
// Transfer path | ||
let src = path; | ||
// DOUBLE_DOT_RE matches a/b/c//../d path correctly only if replace // with / first | ||
const DOUBLE_DOT_RE = /([^/]+)\/\.\.(?:\/|$)/g; | ||
// a/b/c/../../d ==> a/b/../d ==> a/d | ||
do { | ||
src = src.replace(DOUBLE_DOT_RE, function(matched, dirname) { | ||
return dirname === '..' ? matched : ''; | ||
}); | ||
// a/b/c/../../d ==> a/b/../d ==> a/d | ||
do { | ||
src = src.replace(DOUBLE_DOT_RE, (matched, dirname) => { | ||
return dirname === '..' ? matched : ''; | ||
}); | ||
// Break | ||
if (path === src) { | ||
break; | ||
} else { | ||
path = src; | ||
} | ||
} while (true); | ||
// Get path | ||
return path; | ||
}, | ||
/** | ||
* Is out bound | ||
* | ||
* @param path | ||
* @param root | ||
* @returns {Boolean} | ||
*/ | ||
isOutBound: function(path, root) { | ||
if (process.platform === 'win32') { | ||
path = path.toLowerCase(); | ||
root = root.toLowerCase(); | ||
// Break | ||
if (path === src) { | ||
break; | ||
} else { | ||
path = src; | ||
} | ||
} while (true); | ||
if (path.length < root.length) { | ||
return true; | ||
} | ||
// Get path | ||
return path; | ||
} | ||
return path.indexOf(root) !== 0; | ||
}, | ||
/** | ||
* Decode uri | ||
* | ||
* @param uri | ||
* @returns {String|Number} | ||
*/ | ||
decodeURI: function(uri) { | ||
try { | ||
return decodeURIComponent(uri); | ||
} catch (err) { | ||
return -1; | ||
} | ||
}, | ||
/** | ||
* Converts a number to a string with | ||
* a given amount of leading characters. | ||
* | ||
* @param {Number} number Number to convert. | ||
* @param {Number} width Amount of leading characters to prepend. | ||
* @param {String} [padding = '0'] leading character. | ||
* @throws Error | ||
* @returns {String} | ||
*/ | ||
pad: function(number, width, padding) { | ||
// Convert number to string. | ||
var string = number.toString(); | ||
/** | ||
* @function isOutBound | ||
* @description Test path is out of bound of base | ||
* @param {string} path | ||
* @param {string} root | ||
* @returns {boolean} | ||
*/ | ||
function isOutBound(path, root) { | ||
path = relative(root, path); | ||
// Return either the original number as string, | ||
// or the number with leading padding characters. | ||
if (!width || string.length >= width) { | ||
return string; | ||
} | ||
if (/\.\.(?:[\\/]|$)/.test(path)) return true; | ||
var leadingCharacters = new Array(width - string.length + 1).join(padding || '0'); | ||
return false; | ||
} | ||
return leadingCharacters + string; | ||
}, | ||
// Check that the port number is not NaN when coerced to a number, | ||
// is an integer and that it falls within the legal range of port numbers. | ||
isLegalPort: function(port) { | ||
return port === port >>> 0 && port <= 0xFFFF; | ||
}, | ||
/** | ||
* File exists sync | ||
* | ||
* @param src | ||
* @returns {boolean} | ||
*/ | ||
existsSync: function(src) { | ||
if (!src) return false; | ||
/** | ||
* @function decodeURI | ||
* @description Decode URI component. | ||
* @param {string} uri | ||
* @returns {string|-1} | ||
*/ | ||
function decodeURI(uri) { | ||
try { | ||
return decodeURIComponent(uri); | ||
} catch (err) { | ||
return -1; | ||
} | ||
} | ||
try { | ||
return fs.statSync(src).isFile(); | ||
} catch (error) { | ||
// check exception. if ENOENT - no such file or directory ok, file doesn't exist. | ||
// otherwise something else went wrong, we don't have rights to access the file, ... | ||
if (error.code !== 'ENOENT') { | ||
throw error; | ||
} | ||
/** | ||
* @function pad | ||
* @description Converts a number to a string with a given amount of leading characters. | ||
* @param {number} number Number to convert. | ||
* @param {number} width Amount of leading characters to prepend. | ||
* @param {string} [padding = '0'] leading character. | ||
* @throws Error | ||
* @returns {string} | ||
*/ | ||
function pad(number, width, padding) { | ||
// Convert number to string. | ||
const string = number.toString(); | ||
return false; | ||
} | ||
}, | ||
LOG_LEVELS: LOG_LEVELS, | ||
log: function(message) { | ||
var type = message.type; | ||
var data = message.data; | ||
var bookmark = dateFormat(new Date()); | ||
var output = chalk.reset.green.bold('[' + bookmark + '] '); | ||
// Return either the original number as string, | ||
// or the number with leading padding characters. | ||
if (!width || string.length >= width) { | ||
return string; | ||
} | ||
switch (type) { | ||
case LOG_LEVELS.INFO: | ||
data = chalk.reset.cyan(data); | ||
break; | ||
case LOG_LEVELS.WARN: | ||
data = chalk.reset.yellow(data); | ||
break; | ||
case LOG_LEVELS.ERROR: | ||
data = chalk.reset.red(data); | ||
break; | ||
} | ||
const leadingCharacters = new Array(width - string.length + 1).join(padding || '0'); | ||
// Break line | ||
output += data + '\n'; | ||
return leadingCharacters + string; | ||
} | ||
// Output message | ||
if (type === LOG_LEVELS.INFO) { | ||
process.stdout.write(output); | ||
} else { | ||
process.stderr.write(output); | ||
/** | ||
* @function isLegalPort | ||
* @description Check that the port number is not NaN when coerced to a number, | ||
* is an integer and that it falls within the legal range of port numbers. | ||
* @param {any} port | ||
* @returns {boolean} | ||
*/ | ||
function isLegalPort(port) { | ||
return port === port >>> 0 && port <= 0xFFFF; | ||
} | ||
/** | ||
* @function existsSync | ||
* @description File exists sync | ||
* @param {string} src | ||
* @returns {boolean} | ||
*/ | ||
function existsSync(src) { | ||
if (!src) return false; | ||
try { | ||
return fs.statSync(src).isFile(); | ||
} catch (error) { | ||
// check exception. if ENOENT - no such file or directory ok, file doesn't exist. | ||
// otherwise something else went wrong, we don't have rights to access the file, ... | ||
if (error.code !== 'ENOENT') { | ||
throw error; | ||
} | ||
return false; | ||
} | ||
} | ||
/** | ||
* @function log | ||
* @param {Object} message | ||
*/ | ||
function log(message) { | ||
const type = message.type; | ||
let data = message.data; | ||
const bookmark = dateFormat(new Date()); | ||
let output = chalk.reset.green.bold('[' + bookmark + '] '); | ||
switch (type) { | ||
case LOG_LEVELS.INFO: | ||
data = chalk.reset.cyan(data); | ||
break; | ||
case LOG_LEVELS.WARN: | ||
data = chalk.reset.yellow(data); | ||
break; | ||
case LOG_LEVELS.ERROR: | ||
data = chalk.reset.red(data); | ||
break; | ||
} | ||
// Break line | ||
output += data + '\n'; | ||
// Output message | ||
if (type === LOG_LEVELS.INFO) { | ||
process.stdout.write(output); | ||
} else { | ||
process.stderr.write(output); | ||
} | ||
} | ||
/** | ||
* @function pathname | ||
* @param {string} url | ||
* @returns {string} | ||
*/ | ||
function pathname(url) { | ||
return parseURL(url).pathname; | ||
} | ||
// Exports | ||
module.exports = { | ||
pad, | ||
log, | ||
typeOf, | ||
extend, | ||
pathname, | ||
str4regex, | ||
normalize, | ||
decodeURI, | ||
isOutBound, | ||
existsSync, | ||
LOG_LEVELS, | ||
isLegalPort, | ||
fn: isFunction, | ||
string: isString | ||
}; |
@@ -1,8 +0,5 @@ | ||
/*! | ||
* worker | ||
* | ||
* Date: 2017/10/19 | ||
* | ||
* This is licensed under the MIT License (MIT). | ||
* For details, see: https://github.com/nuintun/fengine/blob/master/LICENSE | ||
/** | ||
* @module worker | ||
* @license MIT | ||
* @version 2017/11/14 | ||
*/ | ||
@@ -12,7 +9,7 @@ | ||
var Fengine = require('./fengine'); | ||
const Fengine = require('./fengine'); | ||
// Bootstrap | ||
process.once('message', function(options) { | ||
process.once('message', (options) => { | ||
new Fengine(options); | ||
}); |
{ | ||
"name": "fengine", | ||
"version": "0.3.8", | ||
"description": "A development tool for f2e", | ||
"version": "0.4.0", | ||
"description": "A development tool for f2e.", | ||
"author": { | ||
@@ -20,3 +20,3 @@ "name": "nuintun", | ||
"engines": { | ||
"node": ">=0.10.0" | ||
"node": ">=4.0.0" | ||
}, | ||
@@ -36,10 +36,9 @@ "bin": { | ||
"dependencies": { | ||
"chalk": "^2.2.0", | ||
"inquirer": "^3.3.0", | ||
"chalk": "^2.3.0", | ||
"inquirer": "^4.0.0", | ||
"commander": "^2.11.0", | ||
"js-yaml": "^3.10.0", | ||
"file-send": "^2.2.0", | ||
"mime-types": "^2.1.17" | ||
"file-send": "^3.0.0" | ||
}, | ||
"readmeFilename": "README.md" | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5
115426
1445
+ Addedfile-send@3.2.1(transitive)
+ Addedinquirer@4.0.2(transitive)
- Removedmime-types@^2.1.17
- Removedcore-util-is@1.0.3(transitive)
- Removedee-first@1.1.1(transitive)
- Removedfile-send@2.2.2(transitive)
- Removedinherits@2.0.4(transitive)
- Removedinquirer@3.3.0(transitive)
- Removedon-finished@2.4.1(transitive)
- Removedprocess-nextick-args@2.0.1(transitive)
- Removedreadable-stream@2.3.8(transitive)
- Removedsafe-buffer@5.1.2(transitive)
- Removedstring_decoder@1.1.1(transitive)
- Removedutil-deprecate@1.0.2(transitive)
Updatedchalk@^2.3.0
Updatedfile-send@^3.0.0
Updatedinquirer@^4.0.0