mirror of
https://github.com/liabru/matter-js.git
synced 2025-01-21 17:14:38 -05:00
refactor plugin system
This commit is contained in:
parent
e84c537d29
commit
8da170f382
4 changed files with 135 additions and 207 deletions
|
@ -1,7 +1,6 @@
|
|||
(function() {
|
||||
|
||||
var chain = Matter.Common.chain,
|
||||
last = Matter.Common.last;
|
||||
var Common = Matter.Common;
|
||||
|
||||
var MatterPlugin = {
|
||||
name: 'matter-plugin',
|
||||
|
@ -11,12 +10,7 @@
|
|||
for: 'matter-js@^0.10.0',
|
||||
|
||||
uses: [
|
||||
{
|
||||
plugin: 'matter-plugin-2@^0.0.1',
|
||||
options: {
|
||||
message: 'hello'
|
||||
}
|
||||
},
|
||||
'matter-plugin-2@^0.1.1',
|
||||
'matter-plugin-3@^0.10.0'
|
||||
],
|
||||
|
||||
|
@ -25,25 +19,25 @@
|
|||
},
|
||||
|
||||
install: function(base) {
|
||||
base.Engine.create = chain(
|
||||
base.Engine.create = Common.chain(
|
||||
Matter.Engine.create,
|
||||
MatterPlugin.engineCreate
|
||||
MatterPlugin._engineCreate
|
||||
);
|
||||
|
||||
base.Body.create = chain(
|
||||
MatterPlugin.bodyCreate,
|
||||
base.Body.create = Common.chain(
|
||||
MatterPlugin._bodyCreate,
|
||||
Matter.Body.create
|
||||
);
|
||||
},
|
||||
|
||||
engineCreate: function(element, options, engine) {
|
||||
engine = last(arguments);
|
||||
_engineCreate: function(element, options) {
|
||||
var engine = this;
|
||||
|
||||
console.log('2nd patched engine create!', engine);
|
||||
},
|
||||
|
||||
bodyCreate: function(options) {
|
||||
console.log('patched body create!', arguments);
|
||||
_bodyCreate: function(options) {
|
||||
console.log('patched body create!', options);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
(function() {
|
||||
|
||||
var chain = Matter.Common.chain;
|
||||
var Common = Matter.Common;
|
||||
|
||||
var MatterPlugin2 = {
|
||||
name: 'matter-plugin-2',
|
||||
|
@ -9,21 +9,23 @@
|
|||
|
||||
for: 'matter-js@^0.10.0',
|
||||
|
||||
uses: ['matter-plugin'],
|
||||
uses: ['matter-plugin-fake'],
|
||||
|
||||
options: {
|
||||
thing: 1
|
||||
},
|
||||
|
||||
install: function(matter) {
|
||||
matter.Engine.create = chain(
|
||||
matter.Engine.create = Common.chain(
|
||||
matter.Engine.create,
|
||||
MatterPlugin2.engineCreate
|
||||
MatterPlugin2._engineCreate
|
||||
);
|
||||
},
|
||||
|
||||
engineCreate: function(element, options, engine) {
|
||||
console.log('patched engine create!', arguments);
|
||||
_engineCreate: function(element, options) {
|
||||
var engine = this;
|
||||
|
||||
console.log('patched engine create!', engine);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -202,6 +202,16 @@ module.exports = Common;
|
|||
Common.isPlainObject = function(obj) {
|
||||
return typeof obj === 'object' && obj.constructor === Object;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the object is a string.
|
||||
* @method isString
|
||||
* @param {object} obj
|
||||
* @return {boolean} True if the object is a string, otherwise false
|
||||
*/
|
||||
Common.isString = function(obj) {
|
||||
return toString.call(obj) === '[object String]';
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the given value clamped between a minimum and maximum value.
|
||||
|
@ -361,16 +371,6 @@ module.exports = Common;
|
|||
return mapped;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the last value in an array.
|
||||
* @method last
|
||||
* @param {array} list
|
||||
* @return {} The last value in list.
|
||||
*/
|
||||
Common.last = function(list) {
|
||||
return list[list.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a directed graph and returns the partially ordered set of vertices in topological order.
|
||||
* Circular dependencies are allowed.
|
||||
|
@ -418,10 +418,10 @@ module.exports = Common;
|
|||
|
||||
/**
|
||||
* Takes _n_ functions as arguments and returns a new function that calls them in order.
|
||||
* The arguments and `this` value applied when calling the new function will be applied to every function passed.
|
||||
* An additional final argument is passed to provide access to the last value returned (that was not `undefined`).
|
||||
* Therefore if a passed function does not return a value, the previously returned value is passed as the final argument.
|
||||
* After all passed functions have been called the new function returns the final value (if any).
|
||||
* The arguments applied when calling the new function will also be applied to every function passed.
|
||||
* The value of `this` refers to the last value returned in the chain that was not `undefined`.
|
||||
* Therefore if a passed function does not return a value, the previously returned value is maintained.
|
||||
* After all passed functions have been called the new function returns the last returned value (if any).
|
||||
* @method chain
|
||||
* @param ...funcs {function} The functions to chain.
|
||||
* @return {function} A new function that calls the passed functions in order.
|
||||
|
@ -430,11 +430,10 @@ module.exports = Common;
|
|||
var funcs = Array.prototype.slice.call(arguments);
|
||||
|
||||
return function() {
|
||||
var args = Array.prototype.slice.call(arguments),
|
||||
lastResult;
|
||||
var lastResult;
|
||||
|
||||
for (var i = 0; i < funcs.length; i += 1) {
|
||||
var result = funcs[i].apply(this, lastResult ? args.concat(lastResult) : args);
|
||||
var result = funcs[i].apply(lastResult, arguments);
|
||||
|
||||
if (typeof result !== 'undefined') {
|
||||
lastResult = result;
|
||||
|
|
|
@ -12,27 +12,21 @@ var Common = require('./Common');
|
|||
|
||||
(function() {
|
||||
|
||||
//Plugin._anonymousName = 0;
|
||||
Plugin._registry = {};
|
||||
|
||||
Plugin.exports = function(options) {
|
||||
var plugin = options;
|
||||
|
||||
/*plugin.uses = plugin.uses || [];
|
||||
plugin.for = plugin.for || 'matter-js@*';
|
||||
plugin.name = plugin.name || Plugin.anonymousName();
|
||||
plugin.version = plugin.version || '0.0.0';*/
|
||||
//plugin.id = plugin.name + '@' + plugin.version;
|
||||
|
||||
if (!Plugin.isPlugin(plugin)) {
|
||||
Common.log('Plugin.exports: ' + plugin.name + ' does not implement all required fields.', 'warn');
|
||||
Plugin.exports = function(plugin) {
|
||||
if (!Plugin.isValid(plugin)) {
|
||||
Common.log('Plugin.exports: ' + Plugin.toString(plugin) + ' does not implement all required fields.', 'warn');
|
||||
}
|
||||
|
||||
if (plugin.name in Plugin._registry) {
|
||||
var registered = Plugin._registry[plugin.name];
|
||||
|
||||
if (Plugin.versionGte(plugin.version, registered.version)) {
|
||||
if (Plugin.versionParse(plugin.version).number >= Plugin.versionParse(registered.version).number) {
|
||||
Common.log('Plugin.exports: ' + Plugin.toString(registered) + ' was upgraded to ' + Plugin.toString(plugin), 'warn');
|
||||
Plugin._registry[plugin.name] = plugin;
|
||||
} else {
|
||||
Common.log('Plugin.exports: ' + Plugin.toString(registered) + ' can not be downgraded to ' + Plugin.toString(plugin), 'warn');
|
||||
}
|
||||
} else {
|
||||
Plugin._registry[plugin.name] = plugin;
|
||||
|
@ -41,27 +35,25 @@ var Common = require('./Common');
|
|||
return plugin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a unique identifier for anonymous plugins.
|
||||
* @method anonymousName
|
||||
* @return {Number} Unique identifier name
|
||||
*/
|
||||
/*Plugin.anonymousName = function() {
|
||||
return 'anonymous-' + Plugin._nextId++;
|
||||
};*/
|
||||
Plugin.resolve = function(dependency) {
|
||||
return Plugin._registry[Plugin.dependencyParse(dependency).name];
|
||||
};
|
||||
|
||||
Plugin.isPlugin = function(obj) {
|
||||
return obj && obj.name && Common.isFunction(obj.install);
|
||||
Plugin.toString = function(plugin) {
|
||||
return (plugin.name || 'anonymous') + '@' + (plugin.version || plugin.range || '0.0.0');
|
||||
};
|
||||
|
||||
Plugin.isValid = function(obj) {
|
||||
return obj && obj.name && obj.version;
|
||||
};
|
||||
|
||||
Plugin.isUsed = function(base, name) {
|
||||
return base.used.indexOf(name) > -1;
|
||||
//return (',' + base.used.join(',')).indexOf(',' + name + '@') > -1;
|
||||
};
|
||||
|
||||
Plugin.isFor = function(plugin, base) {
|
||||
var parsed = Plugin.versionParse(plugin.for);
|
||||
return base.name === parsed.name && Plugin.versionSatisfies(base.version, parsed.version);
|
||||
var parsed = plugin.for && Plugin.dependencyParse(plugin.for);
|
||||
return !plugin.for || (base.name === parsed.name && Plugin.versionSatisfies(base.version, parsed.range));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -73,208 +65,149 @@ var Common = require('./Common');
|
|||
*/
|
||||
Plugin.installDependencies = function(base) {
|
||||
if (!base.uses || base.uses.length === 0) {
|
||||
Common.log('Plugin.installDependencies: ' + base.name + ' does not specify any dependencies to install.', 'warn');
|
||||
Common.log('Plugin.installDependencies: ' + Plugin.toString(base) + ' does not specify any dependencies to install.', 'warn');
|
||||
return;
|
||||
}
|
||||
|
||||
if (base.used && base.used.length > 0) {
|
||||
Common.log('Plugin.installDependencies: ' + base.name + ' has already installed its dependencies.', 'warn');
|
||||
Common.log('Plugin.installDependencies: ' + Plugin.toString(base) + ' has already installed its dependencies.', 'warn');
|
||||
return;
|
||||
}
|
||||
|
||||
var dependencies = Plugin.dependencies(base),
|
||||
sortedDependencies = Common.topologicalSort(dependencies),
|
||||
warnings = 0;
|
||||
var dependencies = Plugin.trackDependencies(base),
|
||||
sortedDependencies = Common.topologicalSort(dependencies);
|
||||
|
||||
console.log(dependencies, sortedDependencies);
|
||||
|
||||
for (var i = 0; i < sortedDependencies.length; i += 1) {
|
||||
var plugin = Plugin.resolve(sortedDependencies[i]);
|
||||
|
||||
if (sortedDependencies[i] === base.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!plugin) {
|
||||
Common.log('Plugin.installDependencies: ' + sortedDependencies[i] + ' could not be resolved.', 'warn');
|
||||
warnings += 1;
|
||||
continue;
|
||||
}
|
||||
var plugin = Plugin.resolve(sortedDependencies[i]);
|
||||
|
||||
if (Plugin.isUsed(base, plugin.name)) {
|
||||
if (!plugin || Plugin.isUsed(base, plugin.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Plugin.isFor(plugin, base)) {
|
||||
Common.log('Plugin.installDependencies: ' + plugin.name + '@' + plugin.version + ' is for ' + plugin.for + ' but used on ' + base.name + '@' + base.version + '.', 'warn');
|
||||
warnings += 1;
|
||||
Common.log('Plugin.installDependencies: ' + Plugin.toString(plugin) + ' is for ' + plugin.for + ' but installed on ' + Plugin.toString(base) + '.', 'warn');
|
||||
}
|
||||
|
||||
var options = Common.isPlainObject(sortedDependencies[i]) ? sortedDependencies[i].options : null;
|
||||
|
||||
if (plugin.install) {
|
||||
plugin.install(base, options);
|
||||
plugin.install(base);
|
||||
}
|
||||
|
||||
base.used.push(plugin.name);
|
||||
}
|
||||
|
||||
if (warnings > 0) {
|
||||
Common.log('Plugin.installDependencies: Some dependencies may not function as expected, see above warnings.', 'warn');
|
||||
}
|
||||
console.log(base.used);
|
||||
};
|
||||
|
||||
Plugin.dependencies = function(base, _dependencies) {
|
||||
base = Plugin.resolve(base) || base;
|
||||
_dependencies = _dependencies || {};
|
||||
Plugin.trackDependencies = function(base, tracked) {
|
||||
var parsedBase = Plugin.dependencyParse(base),
|
||||
name = parsedBase.name;
|
||||
|
||||
var name = Plugin.versionParse(Plugin.dependencyName(base)).name;
|
||||
tracked = tracked || {};
|
||||
|
||||
if (name in _dependencies) {
|
||||
if (name in tracked) {
|
||||
return;
|
||||
}
|
||||
|
||||
_dependencies[name] = Common.map(base.uses || [], function(dependency) {
|
||||
return Plugin.versionParse(Plugin.dependencyName(dependency)).name;
|
||||
base = Plugin.resolve(base) || base;
|
||||
|
||||
tracked[name] = Common.map(base.uses || [], function(dependency) {
|
||||
var parsed = Plugin.dependencyParse(dependency),
|
||||
resolved = Plugin.resolve(dependency);
|
||||
|
||||
if (resolved && !Plugin.versionSatisfies(resolved.version, parsed.range)) {
|
||||
Common.log(
|
||||
'Plugin.trackDependencies: ' + Plugin.toString(resolved) + ' does not satisfy '
|
||||
+ Plugin.toString(parsed) + ' used by ' + Plugin.toString(parsedBase) + '.',
|
||||
'warn'
|
||||
);
|
||||
} else if (!resolved) {
|
||||
Common.log(
|
||||
'Plugin.trackDependencies: ' + dependency + ' used by '
|
||||
+ Plugin.toString(parsedBase) + ' could not be resolved.',
|
||||
'warn'
|
||||
);
|
||||
}
|
||||
|
||||
return parsed.name;
|
||||
});
|
||||
|
||||
for (var i = 0; i < _dependencies[name].length; i += 1) {
|
||||
Plugin.dependencies(_dependencies[name][i], _dependencies);
|
||||
for (var i = 0; i < tracked[name].length; i += 1) {
|
||||
Plugin.trackDependencies(tracked[name][i], tracked);
|
||||
}
|
||||
|
||||
return _dependencies;
|
||||
return tracked;
|
||||
};
|
||||
|
||||
Plugin.dependencyName = function(dependency) {
|
||||
return (dependency.plugin && (dependency.plugin.name || dependency.plugin)) || dependency.name || dependency;
|
||||
};
|
||||
|
||||
Plugin.resolve = function(dependency) {
|
||||
if (Plugin.isPlugin(dependency)) {
|
||||
return dependency;
|
||||
Plugin.dependencyParse = function(dependency) {
|
||||
if (Common.isString(dependency)) {
|
||||
return {
|
||||
name: dependency.split('@')[0],
|
||||
range: dependency.split('@')[1] || '*'
|
||||
};
|
||||
}
|
||||
|
||||
var plugin = dependency.plugin && Plugin.resolve(dependency.plugin);
|
||||
|
||||
if (plugin) {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
var parsed = Plugin.versionParse(Plugin.dependencyName(dependency));
|
||||
|
||||
plugin = Plugin._registry[parsed.name];
|
||||
|
||||
if (!plugin) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Plugin.versionSatisfies(plugin.version, parsed.version)) {
|
||||
return plugin;
|
||||
}
|
||||
};
|
||||
|
||||
Plugin.versionParse = function(name) {
|
||||
return {
|
||||
name: name.split('@')[0],
|
||||
pattern: name.split('@')[1] || '*'
|
||||
name: dependency.name,
|
||||
range: dependency.range || dependency.version
|
||||
};
|
||||
};
|
||||
|
||||
Plugin.semverParse = function(pattern) {
|
||||
var parsed = {};
|
||||
Plugin.versionParse = function(range) {
|
||||
var isRange = isNaN(Number(range[0])),
|
||||
version = isRange ? range.substr(1) : range,
|
||||
parts = Common.map(version.split('.'), function(part) {
|
||||
return Number(part);
|
||||
});
|
||||
|
||||
parsed.version = pattern;
|
||||
|
||||
if (+parsed.version[0] === NaN) {
|
||||
parsed.operator = parsed.version[0];
|
||||
parsed.version = parsed.version.substr(1);
|
||||
}
|
||||
|
||||
parsed.parts = Common.map(parsed.version.split('.'), function(part) {
|
||||
return +part;
|
||||
});
|
||||
|
||||
return parsed;
|
||||
return {
|
||||
isRange: isRange,
|
||||
version: version,
|
||||
range: range,
|
||||
operator: isRange ? range[0] : '',
|
||||
parts: parts,
|
||||
number: parts[0] * 1e8 + parts[1] * 1e4 + parts[2]
|
||||
};
|
||||
};
|
||||
|
||||
Plugin.versionNumber = function(version) {
|
||||
var parts = Plugin.semverParse(version).parts;
|
||||
return parts[0] * 1e8 + parts[1] * 1e4 + parts[2];
|
||||
};
|
||||
|
||||
Plugin.versionLt = function(versionA, versionB) {
|
||||
return Plugin.versionNumber(versionA) < Plugin.versionNumber(versionB);
|
||||
};
|
||||
|
||||
Plugin.versionLte = function(versionA, versionB) {
|
||||
return Plugin.versionNumber(versionA) <= Plugin.versionNumber(versionB);
|
||||
};
|
||||
|
||||
Plugin.versionGt = function(versionA, versionB) {
|
||||
return Plugin.versionNumber(versionA) > Plugin.versionNumber(versionB);
|
||||
};
|
||||
|
||||
Plugin.versionGte = function(versionA, versionB) {
|
||||
return Plugin.versionNumber(versionA) >= Plugin.versionNumber(versionB);
|
||||
};
|
||||
|
||||
Plugin.versionSatisfies = function(version, pattern) {
|
||||
Plugin.versionSatisfies = function(version, range) {
|
||||
// https://docs.npmjs.com/misc/semver#advanced-range-syntax
|
||||
var operator;
|
||||
pattern = pattern || '*';
|
||||
|
||||
range = range || '*';
|
||||
|
||||
if (isNaN(+pattern[0])) {
|
||||
operator = pattern[0];
|
||||
pattern = pattern.substr(1);
|
||||
}
|
||||
var rangeParsed = Plugin.versionParse(range),
|
||||
rangeParts = rangeParsed.parts,
|
||||
versionParsed = Plugin.versionParse(version),
|
||||
versionParts = versionParsed.parts;
|
||||
|
||||
var parts = Common.map(version.split('.'), function(part) {
|
||||
return +part;
|
||||
});
|
||||
|
||||
var patternParts = Common.map(pattern.split('.'), function(part) {
|
||||
return +part;
|
||||
});
|
||||
|
||||
/*var parsed = Plugin.semverParse(pattern);
|
||||
|
||||
if (parsed.operator === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parsed.operator === '~') {
|
||||
return Plugin.versionGte(version, parsed.pattern) +
|
||||
Plugin.versionLte(version, parts[0] + '.' + (+parts[1] + 1) + '.0');
|
||||
}
|
||||
|
||||
if (parsed.operator === '^') {
|
||||
return Plugin.versionGte(version, pattern) +
|
||||
Plugin.versionLte(version, parts[0] + '.' + (+parts[1] + 1) + '.0');
|
||||
}*/
|
||||
|
||||
if (operator === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (operator === '~') {
|
||||
return parts[0] === patternParts[0] && parts[1] === patternParts[1] && parts[2] >= patternParts[2];
|
||||
}
|
||||
|
||||
if (operator === '^') {
|
||||
if (patternParts[0] > 0) {
|
||||
return parts[0] === patternParts[0] && Plugin.versionGte(version, pattern);
|
||||
if (rangeParsed.isRange) {
|
||||
if (rangeParsed.operator === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (patternParts[1] > 0) {
|
||||
return parts[2] >= patternParts[2];
|
||||
if (rangeParsed.operator === '~') {
|
||||
return versionParts[0] === rangeParts[0] && versionParts[1] === rangeParts[1] && versionParts[2] >= rangeParts[2];
|
||||
}
|
||||
|
||||
//return parts[0] === patternParts[0] && (parts[1] >= patternParts[1] || parts[2] >= patternParts[2]);
|
||||
if (rangeParsed.operator === '^') {
|
||||
if (rangeParts[0] > 0) {
|
||||
return versionParts[0] === rangeParts[0] && versionParsed.number >= rangeParsed.number;
|
||||
}
|
||||
|
||||
//return '^' + parts[0] === patternParts[0] && +parts[1] >= +patternParts[1] || +parts[2] >= +patternParts[2];
|
||||
if (rangeParts[1] > 0) {
|
||||
return versionParts[1] === rangeParts[1] && versionParts[2] >= rangeParts[2];
|
||||
}
|
||||
|
||||
return versionParts[2] === rangeParts[2];
|
||||
}
|
||||
}
|
||||
|
||||
return version === pattern;
|
||||
return version === range;
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
Loading…
Add table
Reference in a new issue