mirror of
https://github.com/flynx/features.js.git
synced 2025-10-29 10:20:09 +00:00
moved to new feature inertization algorithm, still testing...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
2754cf5457
commit
6f553d2408
355
features.js
355
features.js
@ -170,6 +170,8 @@ module.FeatureProto = {
|
|||||||
// Feature(tag, obj)
|
// Feature(tag, obj)
|
||||||
// -> feature
|
// -> feature
|
||||||
//
|
//
|
||||||
|
// Feature(tag, suggested)
|
||||||
|
// -> feature
|
||||||
//
|
//
|
||||||
// Feature(tag, actions)
|
// Feature(tag, actions)
|
||||||
// -> feature
|
// -> feature
|
||||||
@ -337,6 +339,7 @@ var FeatureSetProto = {
|
|||||||
// XXX add dependency loops to .conflicts...
|
// XXX add dependency loops to .conflicts...
|
||||||
// XXX might be a good idea to check dependency loops on feature
|
// XXX might be a good idea to check dependency loops on feature
|
||||||
// construction, too... (???)
|
// construction, too... (???)
|
||||||
|
/*
|
||||||
buildFeatureList: function(obj, lst){
|
buildFeatureList: function(obj, lst){
|
||||||
var that = this
|
var that = this
|
||||||
obj = obj || {}
|
obj = obj || {}
|
||||||
@ -666,8 +669,139 @@ var FeatureSetProto = {
|
|||||||
conflicts: conflicts,
|
conflicts: conflicts,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setup: function(obj, lst){
|
||||||
|
// if no explicit object is given, just the list...
|
||||||
|
if(lst == null){
|
||||||
|
lst = obj
|
||||||
|
obj = null
|
||||||
|
}
|
||||||
|
|
||||||
// Build list of features in an appropriate order to load...
|
obj = obj || (this.__actions__ || actions.Actions)()
|
||||||
|
|
||||||
|
lst = lst.constructor !== Array ? [lst] : lst
|
||||||
|
var features = this.buildFeatureList(obj, lst)
|
||||||
|
lst = features.features
|
||||||
|
|
||||||
|
// check for conflicts...
|
||||||
|
if(Object.keys(features.conflicts).length != 0
|
||||||
|
|| Object.keys(features.missing).length != 0){
|
||||||
|
var m = features.missing
|
||||||
|
var c = features.conflicts
|
||||||
|
|
||||||
|
// build a report...
|
||||||
|
var report = []
|
||||||
|
|
||||||
|
// missing deps...
|
||||||
|
Object.keys(m).forEach(function(k){
|
||||||
|
report.push(k + ': requires following missing features:\n'
|
||||||
|
+' ' + m[k].join(', '))
|
||||||
|
})
|
||||||
|
report.push('\n')
|
||||||
|
|
||||||
|
// conflicts...
|
||||||
|
Object.keys(c).forEach(function(k){
|
||||||
|
report.push(k + ': must setup after:\n ' + c[k].join(', '))
|
||||||
|
})
|
||||||
|
|
||||||
|
// break...
|
||||||
|
throw 'Feature dependency error:\n ' + report.join('\n ')
|
||||||
|
}
|
||||||
|
|
||||||
|
// report excluded features...
|
||||||
|
if(this.__verbose__ && features.excluded.length > 0){
|
||||||
|
console.warn('Excluded features due to exclusivity conflict:',
|
||||||
|
features.excluded.join(', '))
|
||||||
|
}
|
||||||
|
|
||||||
|
// report unapplicable features...
|
||||||
|
if(this.__verbose__ && features.unapplicable.length > 0){
|
||||||
|
console.log('Features not applicable in current context:',
|
||||||
|
features.unapplicable.join(', '))
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the setup...
|
||||||
|
var that = this
|
||||||
|
var setup = FeatureProto.setup
|
||||||
|
lst.forEach(function(n){
|
||||||
|
// setup...
|
||||||
|
if(that[n] != null){
|
||||||
|
this.__verbose__ && console.log('Setting up feature:', n)
|
||||||
|
setup.call(that[n], obj)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// XXX should we extend this if it already was in the object???
|
||||||
|
obj.features = features
|
||||||
|
|
||||||
|
return obj
|
||||||
|
},
|
||||||
|
//*/
|
||||||
|
|
||||||
|
|
||||||
|
// Build list of features in load order...
|
||||||
|
//
|
||||||
|
// .buildFeatureList()
|
||||||
|
// .buildFeatureList('*')
|
||||||
|
// -> data
|
||||||
|
//
|
||||||
|
// .buildFeatureList(feature-tag)
|
||||||
|
// -> data
|
||||||
|
//
|
||||||
|
// .buildFeatureList([feature-tag, .. ])
|
||||||
|
// -> data
|
||||||
|
//
|
||||||
|
// .buildFeatureList(.., filter)
|
||||||
|
// -> data
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// return format:
|
||||||
|
// {
|
||||||
|
// // input feature feature tags...
|
||||||
|
// input: [ .. ],
|
||||||
|
//
|
||||||
|
// // output list of feature tags...
|
||||||
|
// features: [ .. ],
|
||||||
|
//
|
||||||
|
// // disabled features...
|
||||||
|
// disabled: [ .. ],
|
||||||
|
// // exclusive features that got excluded...
|
||||||
|
// excluded: [ .. ],
|
||||||
|
//
|
||||||
|
// // Errors...
|
||||||
|
// error: null | {
|
||||||
|
// // fatal/recoverable error indicator...
|
||||||
|
// fatal: bool,
|
||||||
|
//
|
||||||
|
// // missing dependencies...
|
||||||
|
// // NOTE: this includes tags only included by .depends and
|
||||||
|
// // ignores tags from .suggested...
|
||||||
|
// missing: [ .. ],
|
||||||
|
//
|
||||||
|
// // XXX
|
||||||
|
// conflicts: conflicts,
|
||||||
|
//
|
||||||
|
// // detected dependency loops (if .length > 0 sets fatal)...
|
||||||
|
// loops: [ .. ],
|
||||||
|
//
|
||||||
|
// // sorting loop overflow error (if true sets fatal)...
|
||||||
|
// sort_loop_overflow: bool,
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// // Introspection...
|
||||||
|
// // index of features and their list of dependencies...
|
||||||
|
// depends: {
|
||||||
|
// feature-tag: [ feature-tag, .. ],
|
||||||
|
// ..
|
||||||
|
// },
|
||||||
|
// // index of features and list of features depending on them...
|
||||||
|
// // XXX should this include suggestions or should we do them
|
||||||
|
// // in a separate list...
|
||||||
|
// depended: {
|
||||||
|
// feature-tag: [ feature-tag, .. ],
|
||||||
|
// ..
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//
|
||||||
//
|
//
|
||||||
// Algorithm:
|
// Algorithm:
|
||||||
// - expand features:
|
// - expand features:
|
||||||
@ -679,11 +813,12 @@ var FeatureSetProto = {
|
|||||||
// - by priority
|
// - by priority
|
||||||
// - by dependency (detect loops/errors)
|
// - by dependency (detect loops/errors)
|
||||||
//
|
//
|
||||||
|
//
|
||||||
// XXX differences to .buildFeatureList(..):
|
// XXX differences to .buildFeatureList(..):
|
||||||
// - order is slightly different -- within expectations...
|
// - order is slightly different -- within expectations...
|
||||||
// - this includes meta-features...
|
// - this includes meta-features...
|
||||||
// this seems to be more logical and more flexible...
|
// this seems to be more logical and more flexible...
|
||||||
_buildFeatureList: function(lst, filter){
|
buildFeatureList: function(lst, filter){
|
||||||
var all = this.features
|
var all = this.features
|
||||||
lst = (lst == null || lst == '*') ? all : lst
|
lst = (lst == null || lst == '*') ? all : lst
|
||||||
lst = lst.constructor !== Array ? [lst] : lst
|
lst = lst.constructor !== Array ? [lst] : lst
|
||||||
@ -1085,42 +1220,32 @@ var FeatureSetProto = {
|
|||||||
|
|
||||||
|
|
||||||
//-------------------------------------------------------------
|
//-------------------------------------------------------------
|
||||||
|
|
||||||
// XXX should we report stuff here???
|
|
||||||
// report dependency loops...
|
|
||||||
//
|
|
||||||
// NOTE: a loop error should be raised only when one of the loop elements
|
|
||||||
// is encountered during the linearisation process...
|
|
||||||
// XXX should looping features get disabled or be loaded in random-ish order???
|
|
||||||
// ...#2 case will need to be handled at the sorting stage...
|
|
||||||
loops.length > 0
|
|
||||||
&& loops
|
|
||||||
.forEach(function(loop){
|
|
||||||
console.warn('feature loop detected:\n\t' + loop.join('\n\t\t-> ')) })
|
|
||||||
// report conflicts...
|
|
||||||
Object.keys(conflicts)
|
|
||||||
.forEach(function(group){
|
|
||||||
console.error('Exclusive "'+ group +'" conflict at:', conflicts[group]) })
|
|
||||||
// report loop limit...
|
|
||||||
// XXX store this to data...
|
|
||||||
loop_limit <= 0
|
|
||||||
&& console.error('Hit loop limit while sorting dependencies!')
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
input: lst,
|
input: lst,
|
||||||
|
|
||||||
list: list,
|
features: list,
|
||||||
|
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
excluded: excluded,
|
excluded: excluded,
|
||||||
|
|
||||||
// XXX should these be in a error block???
|
// errors and conflicts...
|
||||||
missing: missing,
|
error: (loops.length > 0
|
||||||
loops: loops,
|
|| Object.keys(conflicts).length > 0
|
||||||
conflicts: conflicts,
|
|| loop_limit <= 0
|
||||||
sort_loop_error: loop_limit <= 0,
|
|| missing.length > 0) ?
|
||||||
|
{
|
||||||
|
missing: missing,
|
||||||
|
conflicts: conflicts,
|
||||||
|
|
||||||
|
// fatal stuff...
|
||||||
|
fatal: loops.length > 0 || loop_limit <= 0,
|
||||||
|
loops: loops,
|
||||||
|
sort_loop_overflow: loop_limit <= 0,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
|
||||||
|
// introspection...
|
||||||
depends: features,
|
depends: features,
|
||||||
depended: rev_features,
|
depended: rev_features,
|
||||||
//suggests: suggested,
|
//suggests: suggested,
|
||||||
@ -1128,18 +1253,34 @@ var FeatureSetProto = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_setup: function(obj, lst){
|
// Setup features...
|
||||||
// if no explicit object is given, just the list...
|
//
|
||||||
|
// Setup features on existing actions object...
|
||||||
|
// .setup(actions, [feature-tag, ...])
|
||||||
|
// -> actions
|
||||||
|
//
|
||||||
|
// Setup features on a new actions object...
|
||||||
|
// .setup(feature-tag)
|
||||||
|
// .setup([feature-tag, ...])
|
||||||
|
// -> actions
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// This will add .unapplicable to the output of .buildFeatureList(..)
|
||||||
|
// and to .features of the resulting object...
|
||||||
|
//
|
||||||
|
// NOTE: this will store the build result in .features of the output
|
||||||
|
// actions object.
|
||||||
|
setup: function(obj, lst){
|
||||||
|
// no explicit object is given...
|
||||||
if(lst == null){
|
if(lst == null){
|
||||||
lst = obj
|
lst = obj
|
||||||
obj = null
|
obj = null
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = obj || (this.__actions__ || actions.Actions)()
|
obj = obj || (this.__actions__ || actions.Actions)()
|
||||||
|
lst = lst instanceof Array ? lst : [lst]
|
||||||
|
|
||||||
lst = lst.constructor !== Array ? [lst] : lst
|
|
||||||
var unapplicable = []
|
var unapplicable = []
|
||||||
var features = this._buildFeatureList(lst,
|
var features = this.buildFeatureList(lst,
|
||||||
function(n){
|
function(n){
|
||||||
var f = this[n]
|
var f = this[n]
|
||||||
// check applicability if possible...
|
// check applicability if possible...
|
||||||
@ -1149,55 +1290,44 @@ var FeatureSetProto = {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
lst = features.list
|
|
||||||
|
|
||||||
// XXX STUB: adapter code, remove when done...
|
|
||||||
features.unapplicable = unapplicable
|
features.unapplicable = unapplicable
|
||||||
features.features = features.list
|
// cleanup disabled...
|
||||||
|
features.disabled = features.disabled
|
||||||
|
.filter(function(n){ return unapplicable.indexOf(n) < 0 })
|
||||||
|
|
||||||
// check for conflicts...
|
// if we have critical errors and set verbose...
|
||||||
/*/ XXX need to update this section...
|
var fatal = features.error
|
||||||
if(Object.keys(features.conflicts).length != 0
|
&& (features.error.loops.length > 0 || features.error.sort_loop_overflow)
|
||||||
|| Object.keys(features.missing).length != 0){
|
var verbose = this.__verbose__ || fatal
|
||||||
var m = features.missing
|
|
||||||
var c = features.conflicts
|
|
||||||
|
|
||||||
// build a report...
|
// report stuff...
|
||||||
var report = []
|
if(verbose){
|
||||||
|
// report dependency loops...
|
||||||
// missing deps...
|
features.error.loops.length > 0
|
||||||
Object.keys(m).forEach(function(k){
|
&& loops
|
||||||
report.push(k + ': requires following missing features:\n'
|
.forEach(function(loop){
|
||||||
+' ' + m[k].join(', '))
|
console.warn('feature loop detected:\n\t' + loop.join('\n\t\t-> ')) })
|
||||||
})
|
// report conflicts...
|
||||||
report.push('\n')
|
Object.keys(features.error.conflicts)
|
||||||
|
.forEach(function(group){
|
||||||
// conflicts...
|
console.error('Exclusive "'+ group +'" conflict at:', conflicts[group]) })
|
||||||
Object.keys(c).forEach(function(k){
|
// report loop limit...
|
||||||
report.push(k + ': must setup after:\n ' + c[k].join(', '))
|
features.error.sort_loop_overflow
|
||||||
})
|
&& console.error('Hit loop limit while sorting dependencies!')
|
||||||
|
|
||||||
// break...
|
|
||||||
throw 'Feature dependency error:\n ' + report.join('\n ')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// report excluded features...
|
obj.features = features
|
||||||
if(this.__verbose__ && features.excluded.length > 0){
|
|
||||||
console.warn('Excluded features due to exclusivity conflict:',
|
|
||||||
features.excluded.join(', '))
|
|
||||||
}
|
|
||||||
|
|
||||||
// report unapplicable features...
|
// fatal error -- can't load...
|
||||||
if(this.__verbose__ && features.unapplicable.length > 0){
|
// XXX should we throw an error here???
|
||||||
console.log('Features not applicable in current context:',
|
if(fatal){
|
||||||
features.unapplicable.join(', '))
|
return
|
||||||
}
|
}
|
||||||
//*/
|
|
||||||
|
|
||||||
// do the setup...
|
// do the setup...
|
||||||
var that = this
|
var that = this
|
||||||
var setup = FeatureProto.setup
|
var setup = FeatureProto.setup
|
||||||
lst.forEach(function(n){
|
features.features.forEach(function(n){
|
||||||
// setup...
|
// setup...
|
||||||
if(that[n] != null){
|
if(that[n] != null){
|
||||||
this.__verbose__ && console.log('Setting up feature:', n)
|
this.__verbose__ && console.log('Setting up feature:', n)
|
||||||
@ -1205,83 +1335,6 @@ var FeatureSetProto = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// XXX should we extend this if it already was in the object???
|
|
||||||
obj.features = features
|
|
||||||
|
|
||||||
return obj
|
|
||||||
},
|
|
||||||
|
|
||||||
//
|
|
||||||
// .setup(<actions>, [<feature>, ...])
|
|
||||||
// -> <actions>
|
|
||||||
//
|
|
||||||
// .setup([<feature>, ...])
|
|
||||||
// -> <actions>
|
|
||||||
//
|
|
||||||
setup: function(obj, lst){
|
|
||||||
// if no explicit object is given, just the list...
|
|
||||||
if(lst == null){
|
|
||||||
lst = obj
|
|
||||||
obj = null
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = obj || (this.__actions__ || actions.Actions)()
|
|
||||||
|
|
||||||
lst = lst.constructor !== Array ? [lst] : lst
|
|
||||||
var features = this.buildFeatureList(obj, lst)
|
|
||||||
lst = features.features
|
|
||||||
|
|
||||||
// check for conflicts...
|
|
||||||
if(Object.keys(features.conflicts).length != 0
|
|
||||||
|| Object.keys(features.missing).length != 0){
|
|
||||||
var m = features.missing
|
|
||||||
var c = features.conflicts
|
|
||||||
|
|
||||||
// build a report...
|
|
||||||
var report = []
|
|
||||||
|
|
||||||
// missing deps...
|
|
||||||
Object.keys(m).forEach(function(k){
|
|
||||||
report.push(k + ': requires following missing features:\n'
|
|
||||||
+' ' + m[k].join(', '))
|
|
||||||
})
|
|
||||||
report.push('\n')
|
|
||||||
|
|
||||||
// conflicts...
|
|
||||||
Object.keys(c).forEach(function(k){
|
|
||||||
report.push(k + ': must setup after:\n ' + c[k].join(', '))
|
|
||||||
})
|
|
||||||
|
|
||||||
// break...
|
|
||||||
throw 'Feature dependency error:\n ' + report.join('\n ')
|
|
||||||
}
|
|
||||||
|
|
||||||
// report excluded features...
|
|
||||||
if(this.__verbose__ && features.excluded.length > 0){
|
|
||||||
console.warn('Excluded features due to exclusivity conflict:',
|
|
||||||
features.excluded.join(', '))
|
|
||||||
}
|
|
||||||
|
|
||||||
// report unapplicable features...
|
|
||||||
if(this.__verbose__ && features.unapplicable.length > 0){
|
|
||||||
console.log('Features not applicable in current context:',
|
|
||||||
features.unapplicable.join(', '))
|
|
||||||
}
|
|
||||||
|
|
||||||
// do the setup...
|
|
||||||
var that = this
|
|
||||||
var setup = FeatureProto.setup
|
|
||||||
lst.forEach(function(n){
|
|
||||||
// setup...
|
|
||||||
if(that[n] != null){
|
|
||||||
this.__verbose__ && console.log('Setting up feature:', n)
|
|
||||||
setup.call(that[n], obj)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// XXX should we extend this if it already was in the object???
|
|
||||||
obj.features = features
|
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
},
|
},
|
||||||
remove: function(obj, lst){
|
remove: function(obj, lst){
|
||||||
@ -1296,7 +1349,6 @@ var FeatureSetProto = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// shorthand for: Feature(<feature-set>, ...)
|
// shorthand for: Feature(<feature-set>, ...)
|
||||||
// XXX should this return this?
|
|
||||||
Feature: function(){
|
Feature: function(){
|
||||||
return this.__feature__.apply(null, [this].concat(args2array(arguments))) },
|
return this.__feature__.apply(null, [this].concat(args2array(arguments))) },
|
||||||
}
|
}
|
||||||
@ -1306,7 +1358,6 @@ var FeatureSet =
|
|||||||
module.FeatureSet = object.makeConstructor('FeatureSet', FeatureSetProto)
|
module.FeatureSet = object.makeConstructor('FeatureSet', FeatureSetProto)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
var Features =
|
var Features =
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ig-features",
|
"name": "ig-features",
|
||||||
"version": "2.2.6",
|
"version": "3.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "features.js",
|
"main": "features.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user