Skip to content

Commit

Permalink
Added support for recursively packaged packages. Reworked the system …
Browse files Browse the repository at this point in the history
…variable/module so that they are object identical, in preparation for presumed specified removal of the free variable.
  • Loading branch information
kriskowal committed Apr 18, 2009
1 parent 4a6e4cb commit ed3e8bd
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 77 deletions.
215 changes: 152 additions & 63 deletions lib/packages.js
Expand Up @@ -3,23 +3,124 @@ var json = require('./json');
var fs = require('./file');
var system = require('./system');

function main () {
var packagesPlatform;
try {
packagesPlatform = require('./packages-platform');
} catch (exception) {
}
var packagesData = exports.readPackages(system.prefix);
var sortedPackages = exports.sortedPackages(packagesData);
var analysis = {};
exports.analyze(analysis, sortedPackages);
if (packagesPlatform)
packagesPlatform.analyze(analysis, sortedPackages);
exports.synthesize(analysis);
if (packagesPlatform)
packagesPlatform.synthesize(analysis);
}

/*** readPackages
recursively loads all package data from package.json files
and packages/ directories, starting with the given directory,
usually the system.prefix.
*/
exports.readPackages = function (packageDirectory) {
exports.readPackages = function (packageDirectory, packagesData, visitedPackages) {
/* construct an object graph from package json files */
var packagesData = {};
packagesDirectory.list().forEach(function (packageName) {
var packageJson = packagesDirectory.join(packageName, 'package.json');
if (packageJson.exists()) {
var packageData = json.parse(packageJson.read().toString());
packageData.dir = packageJson.resolve('.');
packagesData[packageName] = packageData;

if (!packagesData)
packagesData = {};
if (!visitedPackages)
visitedPackages = {};

packageDirectory = fs.path(packageDirectory).canonical();
var name = packageDirectory.basename();

/* check for cyclic symbolic linkage */
if (Object.prototype.hasOwnProperty.call(visitedPackages, packageDirectory))
return packagesData;
visitedPackages[packageDirectory] = true;

/* check for duplicate package names */
if (Object.prototype.hasOwnProperty.call(packagesData, name)) {
system.log.warn("Package name collision ignored: " + packageDirectory);
return packagesData;
}

if (!packageDirectory.join('package.json').isFile()) {
system.log.warn('No package.json in ' + packageDirectory);
return packagesData;
}

try {
var packageDatum =
packagesData[name] =
json.parse(packageDirectory.join('package.json').read().toString());
packageDatum.directory = packageDirectory.join('');

/* sub packages */
var packagesDirectory = fs.path(packageDatum.packages || 'packages');
if (packagesDirectory.isDirectory()) {
packagesDirectory.list().forEach(function (packageName) {
var packageDirectory = packagesDirectory.join(packageName);
if (packagesDirectory.join(packageName).isDirectory())
exports.readPackages(
packagesDirectory.join(packageName),
packagesData,
visitedPackages
);
});
}
});
return packagesData;

} catch (exception) {
system.log.error("Could not load package '" + packageName + "'. " + exception);
} finally {
return packagesData;
}

};

/*** verify
scans a package object for missing dependencies and throws away
any package that has unmet dependencies.
*/
exports.verify = function (packageData) {
for (var name in packageData) {
if (Object.prototype.hasOwnProperty.call(packageData, name)) {
try {
scan(packageData, name);
} catch (exception) {
}
}
}
};

var scan = function (packageData, name) {
var packageDatum = packageData[name];
if (!packageDatum)
throw name;
try {
if (packageDatum.dependencies) {
packageDatum.dependencies.forEach(function (dependency) {
scan(packageData, dependency);
});
}
} catch (exception) {
if (typeof exception == "string")
system.log.error(
'Threw away package ' + name +
' because it depends on ' + exception +
'.'
);
delete packageData[name];
throw name;
}
};

/*** sortedPackages
returns an array of packages in order from the most
dependent to least dependent, sorted based on
their transitive dependencies.
*/
exports.sortedPackages = function (packageData) {
var order = topo(packageData);
Expand All @@ -29,7 +130,41 @@ exports.sortedPackages = function (packageData) {
return sorted;
};

var topo = function (graph) {
var sorted = [],
visited = {};
for (var name in graph) {
if (
Object.prototype.hasOwnProperty.call(graph, name) &&
!Object.prototype.hasOwnProperty.call(visited, name)
) {
sorted.push.apply(sorted, _topo(graph, name, visited));
}
}
return sorted;
};

var _topo = function (graph, name, visited) {
var node = graph[name];
if (Object.prototype.hasOwnProperty.call(visited, node))
return [];
visited[name] = true;
var sorted = [];
var dependencies = graph[name].dependencies || [];
for (var i = 0; i < dependencies.length; i++) {
var dependency = dependencies[i];
if (Object.prototype.hasOwnProperty.call(visited, dependency))
continue;
visited[dependency] = true;
sorted.push.apply(sorted, _topo(graph, dependency, visited));
}
sorted.push(name);
return sorted;
};

/*** analyze
constructs prioritized top-level module paths
based on the given sorted package array.
*/
exports.analyze = function (analysis, packagesData) {
var jsPaths = analysis.jsPaths = []
Expand All @@ -40,7 +175,7 @@ exports.analyze = function (analysis, packagesData) {
if (packageData.platforms)
platforms = packageData.platforms;
system.platforms.forEach(function (platform) {
var platformDir = packageData.dir.join('platforms', platform);
var platformDir = packageData.directory.join('platforms', platform);
if (platformDir.isDirectory())
jsPaths.push(platformDir);
});
Expand All @@ -50,14 +185,17 @@ exports.analyze = function (analysis, packagesData) {
packageData.js = [packageData.js];
if (!packageData.js)
packageData.js = ['lib'];
for (var i = 0; i < packageData.js.length; i++)
packageData.js[i] = packageData.dir.resolve(packageData.js[i]);
for (var i = 0; i < packageData.js.length; i++) {
packageData.js[i] = packageData.directory.resolve(packageData.js[i]);
}

jsPaths.unshift.apply(jsPaths, packageData.js);
});
};

/*** synthesize
applies the results of the analysis on the current
execution environment.
*/
exports.synthesize = function (analysis) {
exports.addJsPaths(analysis.jsPaths);
Expand All @@ -70,54 +208,5 @@ exports.addJsPaths = function (jsPaths) {
require.loader.setPaths(jsPaths.concat(require.loader.getPaths()));
};

var packagesPlatform;
try {
packagesPlatform = require('./packages-platform');
} catch (exception) {
}
var packagesDirectory = fs.path(system.prefix).join('packages');
var packageData = exports.readPackages(packagesDirectory);
var sortedPackages = exports.sortedPackages(packageData);
var analysis = {};
exports.analyze(analysis, sortedPackages);
if (packagesPlatform)
packagesPlatform.analyze(analysis, sortedPackages);
exports.synthesize(analysis);
if (packagesPlatform)
packagesPlatform.synthesize(analysis);


/* functions */

function topo(graph) {
var sorted = [],
visited = {};
for (var name in graph) {
if (
Object.prototype.hasOwnProperty.call(graph, name) &&
!Object.prototype.hasOwnProperty.call(visited, name)
) {
sorted.push.apply(sorted, _topo(graph, name, visited));
}
}
return sorted;
}

function _topo(graph, name, visited) {
var node = graph[name];
if (Object.prototype.hasOwnProperty.call(visited, node))
return [];
visited[name] = true;
var sorted = [];
var dependencies = graph[name].dependencies || [];
for (var i = 0; i < dependencies.length; i++) {
var dependency = dependencies[i];
if (Object.prototype.hasOwnProperty.call(visited, dependency))
continue;
visited[dependency] = true;
sorted.push.apply(sorted, _topo(graph, dependency, visited));
}
sorted.push(name);
return sorted;
}
main();

31 changes: 18 additions & 13 deletions narwhal.js
Expand Up @@ -79,11 +79,15 @@ var Loader = function (options) {

loader.load = function (canonical) {
if (!Object.prototype.hasOwnProperty.call(factories, canonical)) {
factories[canonical] = loader.evaluate(loader.fetch(canonical), canonical);
loader.reload(canonical);
}
return factories[canonical];
};

loader.reload = function (canonical) {
factories[canonical] = loader.evaluate(loader.fetch(canonical), canonical);
};

loader.isLoaded = function (canonical) {
return Object.prototype.hasOwnProperty.call(factories, canonical);
};
Expand Down Expand Up @@ -117,7 +121,7 @@ var Sandbox = function (options) {
var debugDepth = 0;
var mainId;

var sandbox = function (id, baseId) {
var sandbox = function (id, baseId, force, reload) {
id = loader.resolve(id, baseId);

system.log.debug("require: " + id + " (parent="+baseId+")");
Expand All @@ -126,7 +130,7 @@ var Sandbox = function (options) {
mainId = id;

/* populate memo with module instance */
if (!Object.prototype.hasOwnProperty.call(modules, id)) {
if (!Object.prototype.hasOwnProperty.call(modules, id) || force) {

if (system.debug) {
debugDepth++;
Expand All @@ -142,7 +146,11 @@ var Sandbox = function (options) {
globals[name] = true;
}

var exports = modules[id] = {};
if (!Object.prototype.hasOwnProperty.call(modules, id) || reload)
modules[id] = {};
var exports = modules[id];
if (reload)
loader.reload(id);
var factory = loader.load(id);
var require = Require(id);
factory(require, exports, sandboxSystem);
Expand Down Expand Up @@ -224,14 +232,18 @@ var Sandbox = function (options) {
return require;
};

sandbox.force = function (id) {
return sandbox(id, '', true);
};

sandbox.loader = loader;
sandbox.system = system;

return sandbox;
};

var loader = Loader({});
global.require = Sandbox({loader: loader});
global.require = Sandbox({loader: loader, modules: {system: system}});

////////////////////////////////////////////////
// Ugh, these are duplicated from the File object, since they're required for
Expand Down Expand Up @@ -282,14 +294,7 @@ try {
system.log.error("Couldn't load global/primordial patches ("+e+")");
}

/* synchronize the system module and system free variable */
var systemModule = require('system');
for (var name in system) {
if (Object.prototype.hasOwnProperty.call(system, name)) {
systemModule[name] = system[name];
}
}
global.system = systemModule;
require.force("system");

// load packages
try {
Expand Down
4 changes: 4 additions & 0 deletions package.json
@@ -0,0 +1,4 @@
{
"author": "Tom Robinson",
"js": "lib"
}
2 changes: 1 addition & 1 deletion platforms/rhino/packages-platform.js
Expand Up @@ -16,7 +16,7 @@ exports.analyze = function (analysis, sortedPackages) {
if (!packageData.java)
packageData.java = [];
for (var i = 0; i < packageData.java.length; i++)
packageData.java[i] = packageData.dir.resolve(packageData.java[i]);
packageData.java[i] = packageData.directory.resolve(packageData.java[i]);
javaPaths.unshift.apply(javaPaths, packageData.java);
});
};
Expand Down

0 comments on commit ed3e8bd

Please sign in to comment.