/*______________
| ______ | U I Z E J A V A S C R I P T F R A M E W O R K
| / / | ---------------------------------------------------
| / O / | MODULE : Uize.Services.FileBuilderAdapter Class
| / / / |
| / / / /| | ONLINE : http://uize.com
| /____/ /__/_| | COPYRIGHT : (c)2012-2014 UIZE
| /___ | LICENSE : Available under MIT License or GNU General Public License
|_______________| http://uize.com/license.html
*/
/* Module Meta Data
type: Class
importance: 5
codeCompleteness: 0
docCompleteness: 2
*/
/*?
Introduction
The =Uize.Services.FileBuilderAdapter= module defines an abstract base class for adapters for the file builder service (=Uize.Services.FileBuilder=).
*DEVELOPERS:* `Chris van Rensburg`
*/
/*
- how handlers are used...
- handler is picked by going through all the handlers in sequence, until a handler matches the URL path
- handlers are recursive, so for every handler that maps a requested path to a source path, the remaining handlers are evaluated to see if any apply to the source path
- so, for example, a handler for scrunched JavaScript modules can rely on a handler for compiled JST modules, so that if a .jst source file is modified, requesting the scrunched compiled JST module will result in the .jst source file first being compiled to a JST source module, and then being scrunched to a scrunched JST module
- handlers can also register build needs
- handlers can have multiple inputs
- a handler for a SimpleDoc guide HTML URL will have at least two direct inputs...
- the .simple source file
- the .jst JavaScript template used to build the HTML file
- some handlers may need to check multiple files to determine if the current built result is current
- for example, a handler for a JavaScript module doc HTML page will need to check that none of the modules along the modules inheritance chain (if it is a class module) have a more recent modified date, since the documentation reflects inherited features for classes
- needed items can be objects, in memory
- as objects in memory, needed objects can have a last modified date
- a JST template can have template modules as dependencies
- template module dependencies are dependencies for the process of using of such a JST template
- if one of the template module dependencies is modified since the last build using the JST template, then the last built product of the JST template would need to be rebuilt
- with a request driven model for build process, for the purpose of performance, any file can have an internal / parsed representation as an object
- so, for example, a .json file that is built as part of a build process can also be stored in memory as a JavaScript object, so that processes that repeatedly use the .json file as an input can not have to repeatedly parse the
- all files can be modified through their string or object interfaces
- if modified through object interface...
- buffered serialization, buffered writing to file
- immediate serialization when requested in text form or requested through file interface
- writing to file can be decoupled from serialization to text, as a consequence of file system service
- if modified through file interface...
- immediate parsing when requested in object form
- to aid in performance, files can be cached in a memory cache system (such as memcache)
*/
Uize.module ({
name:'Uize.Services.FileBuilderAdapter',
superclass:'Uize.Service.Adapter',
required:[
'Uize.Url',
'Uize.Str.Has',
'Uize.Array.Util',
'Uize.Services.FileSystem',
'Uize.Json',
'Uize.Build.Util'
],
builder:function (_superclass) {
'use strict';
/*** General Variables ***/
var
_undefined,
_hasPrefix = Uize.Str.Has.hasPrefix,
_sacredEmptyObject = {},
_trueFlag = {}
;
/*** Utility Functions ***/
function _makeUrlTesterMethod (_pathType) {
_pathType += 'Path';
return function (_url) {return _hasPrefix (_url,this.params [_pathType] + '/')};
}
function _makeUrlTransformerMethod (_pathTypeToRemove,_pathTypeToPrepend) {
var _pathTypeToPrependIsSource = _pathTypeToPrepend == 'source';
_pathTypeToRemove += 'Path';
_pathTypeToPrepend += 'Path';
return (
_pathTypeToPrependIsSource
? function (_url) {
var
_params = this.params,
_urlMinusOldPath = _url.slice (_params [_pathTypeToRemove].length)
;
return (
(
_hasPrefix (_urlMinusOldPath,'/' + _params.modulesFolder + '/Uize')
? _params.uizePath
: _params [_pathTypeToPrepend]
) +
_urlMinusOldPath
);
}
: function (_url) {
var _params = this.params;
return _params [_pathTypeToPrepend] + _url.slice (_params [_pathTypeToRemove].length);
}
);
}
function _makeUrlGeneratorMethod (_pathType) {
_pathType += 'Path';
return function (_path) {
return this.params [_pathType] + (_path && _path.charCodeAt (0) != 47 ? '/' : '') + _path;
};
};
function _makeSubPathExtractorMethod (_pathType) {
_pathType += 'Path';
return function (_url) {return _url.slice (this.params [_pathType].length + 1)};
}
return _superclass.subclass ({
alphastructor:function () {
var m = this;
/*** Private Instance Properties ***/
m._filesConsideredCurrentLookup = {};
m._objectCache = {};
/*** Public Instance Properties ***/
m.fileSystem = Uize.Services.FileSystem.singleton ();
m.urlHandlers = [];
},
instanceMethods:{
registerFileBuilders:function (_urlHandler) {
Uize.push (this.urlHandlers,Uize.Array.Util.flatten (arguments,Infinity,true));
},
/*** URL tester methods ***/
isBuiltUrl:_makeUrlTesterMethod ('built'),
isTempUrl:_makeUrlTesterMethod ('temp'),
isMemoryUrl:_makeUrlTesterMethod ('memory'),
isSourceUrl:_makeUrlTesterMethod ('source'),
/*** URL transformer methods ***/
sourceUrlFromBuiltUrl:_makeUrlTransformerMethod ('built','source'),
sourceUrlFromTempUrl:_makeUrlTransformerMethod ('temp','source'),
sourceUrlFromMemoryUrl:_makeUrlTransformerMethod ('memory','source'),
tempUrlFromBuiltUrl:_makeUrlTransformerMethod ('built','temp'),
tempUrlFromMemoryUrl:_makeUrlTransformerMethod ('memory','temp'),
tempUrlFromSourceUrl:_makeUrlTransformerMethod ('source','temp'),
memoryUrlFromBuiltUrl:_makeUrlTransformerMethod ('built','memory'),
memoryUrlFromTempUrl:_makeUrlTransformerMethod ('temp','memory'),
memoryUrlFromSourceUrl:_makeUrlTransformerMethod ('source','memory'),
builtUrlFromTempUrl:_makeUrlTransformerMethod ('temp','built'),
builtUrlFromMemoryUrl:_makeUrlTransformerMethod ('memory','built'),
builtUrlFromSourceUrl:_makeUrlTransformerMethod ('source','built'),
/*** URL generator methods ***/
builtUrl:_makeUrlGeneratorMethod ('built'),
tempUrl:_makeUrlGeneratorMethod ('temp'),
memoryUrl:_makeUrlGeneratorMethod ('memory'),
sourceUrl:_makeUrlGeneratorMethod ('source'),
/*** sub-path extractor methods ***/
pathUnderBuiltUrl:_makeSubPathExtractorMethod ('built'),
pathUnderTempUrl:_makeSubPathExtractorMethod ('temp'),
pathUnderMemoryUrl:_makeSubPathExtractorMethod ('memory'),
pathUnderSourceUrl:_makeSubPathExtractorMethod ('source'),
/*** abstractions of various methods of the file system service to support object storage ***/
writeFile:function (_params) {
var
m = this,
_path = _params.path
;
m.isMemoryUrl (_path)
? (
m._objectCache [_path] = {
contents:_params.contents,
modifiedDate:new Date
}
)
: m.fileSystem.writeFile (_params)
;
},
readFile:function (_params) {
var
m = this,
_path = _params.path
;
return (
m.isMemoryUrl (_path)
? (m._objectCache [_path] || _sacredEmptyObject).contents
: m.fileSystem.readFile (_params)
);
},
getModifiedDate:function (_params) {
var
m = this,
_path = _params.path
;
return (
m.isMemoryUrl (_path)
? (m._objectCache [_path] || _sacredEmptyObject).modifiedDate
: m.fileSystem.getModifiedDate (_params)
);
},
fileExists:function (_params) {
var
m = this,
_path = _params.path
;
return (
m.isMemoryUrl (_path)
? !!m._objectCache [_path]
: m.fileSystem.fileExists (_params)
);
},
folderExists:function (_params) {
var
m = this,
_path = _params.path
;
return (
m.isMemoryUrl (_path)
? !!m._objectCache [_path]
: m.fileSystem.folderExists (_params)
);
},
/*** General Utility Methods ***/
processSimpleDoc:function (_title,_simpleDocBuildResult,_simpleDocTemplatePath,_extraTemplateInputs) {
var
_contentsTreeItems = _simpleDocBuildResult.contentsTreeItems,
_contentsTreeItem0 = _contentsTreeItems [0]
;
return this.readFile ({path:_simpleDocTemplatePath}) (
Uize.copyInto (
{
title:_title,
description:
(
_contentsTreeItem0 &&
(_contentsTreeItem0.description || (_contentsTreeItem0.items [0] || {}).description)
) || '',
body:_simpleDocBuildResult.html
},
_extraTemplateInputs
)
);
},
moduleNameFromPath:function (_path,_pathType) {
var _modulesPath = this [_pathType + 'Url'] (this.params.modulesFolder + '/');
return (
_hasPrefix (_path,_modulesPath)
? Uize.Build.Util.moduleNameFromModulePath (_path.slice (_modulesPath.length),true)
: ''
);
},
moduleNameFromTempPath:function (_path) {
return this.moduleNameFromPath (_path,'temp');
},
getModuleUrl:function (_moduleName,_includeExtension) {
return (
this.params.modulesFolder + '/' + Uize.modulePathResolver (_moduleName) +
(_includeExtension !== false ? '.js' : '')
);
},
buildFile:function (_params,_callback) {
var m = this;
if (_params.filesModified)
m._filesConsideredCurrentLookup = {}
;
var
_console = _params.console,
_filesConsideredCurrentLookup = m._filesConsideredCurrentLookup,
_staleBefore = _params.staleBefore = Uize.toNumber (_params.staleBefore,-Infinity)
;
_params.isDev = _params.isDev == 'true';
m.params = _params;
function _ensureFileCurrent (_url) {
var
_startTime = Uize.now (),
_log
;
if (_filesConsideredCurrentLookup [_url] == _trueFlag) {
_log = {
url:_url,
built:false,
duration:Uize.since (_startTime)
};
} else {
var
_urlParts = Uize.Url.from (_url),
_matchingHandler
;
for (
var
_urlHandlerNo = -1,
_urlHandlers = m.urlHandlers,
_urlHandlersLength = _urlHandlers.length,
_urlHandler
;
++_urlHandlerNo < _urlHandlersLength;
) {
if ((_urlHandler = _urlHandlers [_urlHandlerNo]).urlMatcher.call (m,_urlParts)) {
_matchingHandler = _urlHandler;
break;
}
}
if (_matchingHandler) {
var
_builderInputs = (_matchingHandler.builderInputs || Uize.nop).call (m,_urlParts),
_builder = _matchingHandler.builder
;
if (_builderInputs || _builder) {
var
_path = _urlParts.pathname,
_subLogs = [],
_subLog,
_builderInputModifiedDate,
_maxBuilderInputModifiedDate = _staleBefore,
_processBuilderInput = function (_builderInput) {
if (typeof _builderInput == 'string') {
if (
(_subLog = _ensureFileCurrent (_builderInput)) &&
(_subLog.built || _subLog.buildError)
)
_subLogs.push (_subLog)
;
if (
(_builderInputModifiedDate = m.getModifiedDate ({path:_builderInput})) >
_maxBuilderInputModifiedDate
)
_maxBuilderInputModifiedDate = _builderInputModifiedDate
;
} else {
Uize.forEach (_builderInput,_processBuilderInput);
}
}
;
_processBuilderInput (_builderInputs);
if (
!m.fileExists ({path:_path}) ||
m.getModifiedDate ({path:_path}) < _maxBuilderInputModifiedDate
) {
var _buildError;
try {
if (_builder) {
m.writeFile ({
path:_url,
contents:_builder.call (m,_builderInputs,_urlParts)
});
if (m.isMemoryUrl (_url))
m._objectCache [_url].modifiedDate = _maxBuilderInputModifiedDate
;
} else {
m.fileSystem.copyFile ({
path:Uize.values (_builderInputs) [0],targetPath:_url
});
}
_filesConsideredCurrentLookup [_url] = _trueFlag;
} catch (_error) {
_buildError = _error;
}
_log = {
url:_url,
built:!_buildError,
fileBuilder:_matchingHandler.moduleName || _matchingHandler.description,
duration:Uize.since (_startTime),
builderInputs:_builderInputs
};
if (_subLogs.length)
_log.subLogs = _subLogs
;
if (_buildError)
_log.buildError = _buildError
;
if (_buildError) {
console.log (_buildError);
typeof console != 'undefined' && typeof console.trace == 'function' &&
console.trace ()
;
throw _buildError;
}
} else {
_log = {
url:_url,
built:false,
fileBuilder:_matchingHandler.moduleName || _matchingHandler.description,
duration:Uize.since (_startTime)
};
_filesConsideredCurrentLookup [_url] = _trueFlag;
}
}
}
}
return _log;
}
var _pathPrefix = m.params.pathPrefix;
if (_pathPrefix == _undefined)
_pathPrefix = m.params.builtPath + '/'
;
var
_url = _params.url,
_log = []
;
Uize.isArray (_url)
? Uize.forEach (_url,function (_url) {_log.push (_ensureFileCurrent (_pathPrefix + _url))})
: _log.push (_ensureFileCurrent (_pathPrefix + _url))
;
var _logAsJson = Uize.Json.to (_log,{keyDelimiter:': ',indentChars:' '});
if (_console == 'verbose')
console.log (_logAsJson)
;
_callback && _callback (_logAsJson);
}
}
});
}
});