Permalink
Browse files

working NodeJS loader

  • Loading branch information...
1 parent 3d94bc8 commit a45ed05d64e10c42e389860002429ab142839763 @cadorn cadorn committed Feb 13, 2012
Showing with 299 additions and 21 deletions.
  1. +1 −1 .gitignore
  2. +0 −2 index.js
  3. +143 −0 lib/bundler.js
  4. +23 −8 lib/loader.js
  5. +21 −0 lib/util/error.js
  6. +3 −2 package.json
  7. +3 −3 tests/adapter/vows.js
  8. +0 −5 tests/all.js
  9. +105 −0 tests/examples.js
View
@@ -1,2 +1,2 @@
-/node_modules/
+node_modules/
/npm-debug.log
View
@@ -1,2 +0,0 @@
-
-exports.LOADER = require("./lib/loader");
View
@@ -0,0 +1,143 @@
+/**
+ * Copyright: 2012 Christoph Dorn <christoph@christophdorn.com>
+ * License: MIT
+ */
+
+var FS = require("fs"),
+ PATH = require("path"),
+ Q = require("q");
+
+
+// TODO: Use helper function from bundler where possible.
+
+exports.parseModule = function(path)
+{
+ return Q.call(function()
+ {
+ var report = {};
+
+ var code = FS.readFileSync(path).toString();
+
+ report.staticLinks = {};
+ report.dynamicLinks = {};
+
+ scrapeRequires(code).forEach(function(name)
+ {
+ if (/^["']{1}.*["']{1}/.test(name))
+ {
+ // String literal require.
+ name = name.substring(1, name.length-1);
+ report.staticLinks[name] = name;
+ }
+ else
+ {
+ // Variable require.
+ // TODO: Specify line number once we detect via AST.
+ throw new Error("Found variable argument to require statement 'require(" + name + ")' in module '" + path + "'. 'require()' only accepts a string literal. Use 'require.async(" + name + ", function(EXPORTS) {})' instead.");
+ }
+ });
+
+ scrapeAsyncRequires(code).forEach(function(name)
+ {
+ report.dynamicLinks[name] = name;
+ });
+
+ return report;
+ });
+}
+
+/**
+ * Scrape dependencies from a Modules/1.1 module. Mostly borrowed from FlyScript.
+ * Instead of just looking for string literals we also look for dynamic requires.
+ * @source http://code.google.com/p/bravojs/source/browse/bravo.js
+ * @todo Use AST analysis here instead of regular expression.
+ */
+function scrapeRequires(txt)
+{
+ var dep = [],
+ m,
+ requireRE = /\/\/.*|\/\*[\s\S]*?\*\/|"(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|[;=(,:!^]\s*\/(?:\\.|[^\/\\])+\/|(?:^|[^A-Za-z0-9_\.])\s*require\s*\(\s*("(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^\\])*'|(?:\\[\s\S]|[^\)\\])*)\s*\)/g;
+ for (requireRE.lastIndex = 0; m = requireRE.exec(txt);)
+ if (m[1]) dep.push(m[1]);
+ return dep;
+}
+function scrapeAsyncRequires(txt)
+{
+ var dep = [],
+ m,
+ requireRE = /\/\/.*|\/\*[\s\S]*?\*\/|"(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|[;=(,:!^]\s*\/(?:\\.|[^\/\\])+\/|(?:^|[^A-Za-z0-9_\.])\s*require\.async\s*\(\s*("(?:\\[\s\S]|[^"\\,])*"|'(?:\\[\s\S]|[^\\,])*'|(?:\\[\s\S]|[^\)\\,])*)\s*,/g;
+ for (requireRE.lastIndex = 0; m = requireRE.exec(txt);)
+ if (m[1]) dep.push(m[1]);
+ return dep;
+}
+
+exports.resolveUri = function(uri)
+{
+ return Q.call(function()
+ {
+ if (PATH.existsSync(uri))
+ {
+ return FS.realpathSync(uri);
+ }
+ else
+ if (PATH.existsSync(uri + ".js"))
+ {
+ return FS.realpathSync(uri + ".js");
+ }
+ else
+ {
+ throw new Error("Could not find file at path: " + uri);
+ }
+ });
+}
+
+
+exports.remapSources = function(report)
+{
+ return Q.call(function()
+ {
+ // We do nothing by default.
+
+ return report;
+ });
+}
+
+
+exports.getBundleHeader = function()
+{
+ return "";
+}
+
+
+exports.encodeModule = function(path, canonicalId, staticLinks)
+{
+ return Q.call(function()
+ {
+ var code = FS.readFileSync(path).toString();
+
+ // Check if module is an ASCII resource file.
+ if ([
+ "txt",
+ "text"
+ ].indexOf(PATH.basename(path).replace(/^.*?\.([^\.]*)$/, "$1")) >= 0)
+ {
+ code = '"' + encodeURIComponent(code) + '"';
+ }
+ else
+ // Check if code is simply a JSON structure.
+ if (/^[\s\n]*\{[\s\S]*\}[\s\n]*$/.test(code))
+ {
+ // We don't have to do anything.
+ }
+ else
+ {
+ code = [
+ 'function(require, exports, module)\n{',
+ ' ' + code.split('\n').join('\n '),
+ '}'
+ ].join('\n');
+ }
+
+ return code;
+ });
+}
View
@@ -3,9 +3,7 @@
* License: MIT
*/
-var
- // TODO: Use NPM module here.
- LOADER = require("sourcemint-loader-js/loader"),
+var LOADER = require("sourcemint-loader-js/loader"),
VM = require("vm");
FS = require("fs"),
HTTP = require("http");
@@ -34,9 +32,9 @@ exports.sandbox = function(sandboxIdentifier, loadedCallback, sandboxOptions)
// Set our own loader for the sandbox.
options.load = function(uri, loadedCallback)
{
- resolveURI(uri).then(function(uri)
+ exports.resolveURI(uri).then(function(uri)
{
- loadBundleCode(uri).then(function(code)
+ exports.loadBundleCode(uri).then(function(code)
{
try
{
@@ -48,7 +46,9 @@ exports.sandbox = function(sandboxIdentifier, loadedCallback, sandboxOptions)
// TODO: Capture errors by watching this processe's stdout file log from
// another process.
VM.runInNewContext(code, {
- require: LOADER.require
+ require: LOADER.require,
+ // TODO: Map `console` to `console` object provided by `sandboxOptions`.
+ console: console
}, uri, true);
loadedCallback();
@@ -71,6 +71,16 @@ exports.sandbox = function(sandboxIdentifier, loadedCallback, sandboxOptions)
});
}
+ options.onInitModule = function(moduleInterface, moduleObj)
+ {
+ sandboxOptions.onInitModule(moduleInterface, moduleObj);
+
+ moduleObj.require.resolve = function()
+ {
+ return moduleObj.require.id.apply(null, arguments);
+ }
+ };
+
LOADER.require.sandbox(sandboxIdentifier, function(sandbox)
{
loadedCallback(sandbox);
@@ -79,7 +89,7 @@ exports.sandbox = function(sandboxIdentifier, loadedCallback, sandboxOptions)
// TODO: Relocate this to github.com/pinf/core-js/lib/resolver.js and return PINF URI info object.
-function resolveURI(uri)
+exports.resolveURI = function(uri)
{
var deferred = Q.defer(),
m;
@@ -101,6 +111,11 @@ function resolveURI(uri)
deferred.resolve(uri);
}
else
+ if (m = uri.match(/^(\/.*)$/))
+ {
+ deferred.resolve(uri);
+ }
+ else
{
throw new Error("Unable to resolve URI: " + uri);
deferred.reject();
@@ -110,7 +125,7 @@ function resolveURI(uri)
}
//TODO: Relocate this to github.com/sourcemint/downloader-js/lib/bundle.js#loadBundleCode
-function loadBundleCode(uri)
+exports.loadBundleCode = function(uri)
{
var deferred = Q.defer(),
m;
View
@@ -0,0 +1,21 @@
+
+
+exports.logError = function(err)
+{
+ console.error(exports.formatError(err));
+}
+
+
+exports.formatError = function(err)
+{
+ // TODO: Use generic error formatter here.
+ if (typeof err === "object" && typeof err.stack !== "undefined")
+ {
+ return err.stack;
+ }
+ else
+ {
+ return err;
+ }
+}
+
View
@@ -1,12 +1,13 @@
{
"name": "sourcemint-platform-nodejs",
- "version": "0.1.2",
+ "version": "0.1.3",
"dependencies": {
"q": "0.x",
"sourcemint-loader-js": "0.x"
},
"devDependencies": {
- "vows": "0.x"
+ "vows": "0.x",
+ "sourcemint-bundler-js": "0.x"
},
"scripts": {
"test": "vows tests/adapter/vows"
View
@@ -5,19 +5,19 @@ var VOWS = require("vows"),
VOWS.describe("Tests").addBatch(
{
- "All Tests":
+ "All Examples":
{
topic: function()
{
var self = this;
- require("../all").main().then(function() {
+ require("../examples").main().then(function() {
self.callback(true);
}, function(err) {
// NOTE: If this fires and `err` instanceof Error `vows` will fail this test.
self.callback(err);
});
},
- "all tests passed": function(status)
+ "all examples passed": function(status)
{
ASSERT.equal(status, true);
}
View
@@ -1,5 +0,0 @@
-
-exports.main = function()
-{
- return require("../examples/01-PortableLoaderTests").main();
-}
Oops, something went wrong.

0 comments on commit a45ed05

Please sign in to comment.